brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.6 KiB · 4d26c39 Raw
237 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "MoveConstArgCheck.h"10 11#include "clang/Lex/Lexer.h"12 13using namespace clang::ast_matchers;14 15namespace clang::tidy::performance {16 17static void replaceCallWithArg(const CallExpr *Call, DiagnosticBuilder &Diag,18                               const SourceManager &SM,19                               const LangOptions &LangOpts) {20  const Expr *Arg = Call->getArg(0);21 22  const CharSourceRange BeforeArgumentsRange = Lexer::makeFileCharRange(23      CharSourceRange::getCharRange(Call->getBeginLoc(), Arg->getBeginLoc()),24      SM, LangOpts);25  const CharSourceRange AfterArgumentsRange = Lexer::makeFileCharRange(26      CharSourceRange::getCharRange(Call->getEndLoc(),27                                    Call->getEndLoc().getLocWithOffset(1)),28      SM, LangOpts);29 30  if (BeforeArgumentsRange.isValid() && AfterArgumentsRange.isValid()) {31    Diag << FixItHint::CreateRemoval(BeforeArgumentsRange)32         << FixItHint::CreateRemoval(AfterArgumentsRange);33  }34}35 36void MoveConstArgCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {37  Options.store(Opts, "CheckTriviallyCopyableMove", CheckTriviallyCopyableMove);38  Options.store(Opts, "CheckMoveToConstRef", CheckMoveToConstRef);39}40 41void MoveConstArgCheck::registerMatchers(MatchFinder *Finder) {42  auto MoveCallMatcher =43      callExpr(callee(functionDecl(hasName("::std::move"))), argumentCountIs(1),44               unless(isInTemplateInstantiation()))45          .bind("call-move");46 47  // Match ternary expressions where either branch contains std::move48  auto TernaryWithMoveMatcher =49      conditionalOperator(hasDescendant(MoveCallMatcher));50 51  Finder->addMatcher(52      expr(anyOf(53          castExpr(hasSourceExpression(MoveCallMatcher)),54          cxxConstructExpr(hasDeclaration(cxxConstructorDecl(anyOf(55                               isCopyConstructor(), isMoveConstructor()))),56                           hasArgument(0, MoveCallMatcher)))),57      this);58 59  auto ConstTypeParmMatcher =60      qualType(references(isConstQualified())).bind("invocation-parm-type");61  auto RValueTypeParmMatcher =62      qualType(rValueReferenceType()).bind("invocation-parm-type");63  // Matches respective ParmVarDecl for a CallExpr or CXXConstructExpr.64  auto ArgumentWithParamMatcher = forEachArgumentWithParam(65      anyOf(MoveCallMatcher, TernaryWithMoveMatcher),66      parmVarDecl(67          anyOf(hasType(ConstTypeParmMatcher), hasType(RValueTypeParmMatcher)))68          .bind("invocation-parm"));69  // Matches respective types of arguments for a CallExpr or CXXConstructExpr70  // and it works on calls through function pointers as well.71  auto ArgumentWithParamTypeMatcher = forEachArgumentWithParamType(72      anyOf(MoveCallMatcher, TernaryWithMoveMatcher),73      anyOf(ConstTypeParmMatcher, RValueTypeParmMatcher));74 75  Finder->addMatcher(76      invocation(anyOf(ArgumentWithParamMatcher, ArgumentWithParamTypeMatcher))77          .bind("receiving-expr"),78      this);79}80 81static bool isRValueReferenceParam(const Expr *Invocation,82                                   const QualType *InvocationParmType,83                                   const Expr *Arg) {84  if (Invocation && (*InvocationParmType)->isRValueReferenceType() &&85      Arg->isLValue()) {86    if (!Invocation->getType()->isRecordType())87      return true;88    if (const auto *ConstructCallExpr =89            dyn_cast<CXXConstructExpr>(Invocation)) {90      if (const auto *ConstructorDecl = ConstructCallExpr->getConstructor()) {91        if (!ConstructorDecl->isCopyOrMoveConstructor() &&92            !ConstructorDecl->isDefaultConstructor())93          return true;94      }95    }96  }97  return false;98}99 100void MoveConstArgCheck::check(const MatchFinder::MatchResult &Result) {101  const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>("call-move");102  const auto *ReceivingExpr = Result.Nodes.getNodeAs<Expr>("receiving-expr");103  const auto *InvocationParm =104      Result.Nodes.getNodeAs<ParmVarDecl>("invocation-parm");105  const auto *InvocationParmType =106      Result.Nodes.getNodeAs<QualType>("invocation-parm-type");107 108  // Skipping matchers which have been matched.109  if (!ReceivingExpr && AlreadyCheckedMoves.contains(CallMove))110    return;111 112  if (ReceivingExpr)113    AlreadyCheckedMoves.insert(CallMove);114 115  const Expr *Arg = CallMove->getArg(0);116  const QualType ArgType = Arg->getType().getCanonicalType();117  const SourceManager &SM = Result.Context->getSourceManager();118 119  const CharSourceRange MoveRange =120      CharSourceRange::getCharRange(CallMove->getSourceRange());121  const CharSourceRange FileMoveRange =122      Lexer::makeFileCharRange(MoveRange, SM, getLangOpts());123  if (!FileMoveRange.isValid())124    return;125 126  const bool IsConstArg = ArgType.isConstQualified();127  const bool IsTriviallyCopyable =128      ArgType.isTriviallyCopyableType(*Result.Context);129 130  if (IsConstArg || IsTriviallyCopyable) {131    if (const CXXRecordDecl *R = ArgType->getAsCXXRecordDecl()) {132      // According to [expr.prim.lambda]p3, "whether the closure type is133      // trivially copyable" property can be changed by the implementation of134      // the language, so we shouldn't rely on it when issuing diagnostics.135      if (R->isLambda())136        return;137      // Don't warn when the type is not copyable.138      for (const auto *Ctor : R->ctors()) {139        if (Ctor->isCopyConstructor() && Ctor->isDeleted())140          return;141      }142    }143 144    if (!IsConstArg && IsTriviallyCopyable && !CheckTriviallyCopyableMove)145      return;146 147    const bool IsVariable = isa<DeclRefExpr>(Arg);148    // std::move shouldn't be removed when an lvalue wrapped by std::move is149    // passed to the function with an rvalue reference parameter.150    const bool IsRVRefParam =151        isRValueReferenceParam(ReceivingExpr, InvocationParmType, Arg);152    const auto *Var =153        IsVariable ? dyn_cast<DeclRefExpr>(Arg)->getDecl() : nullptr;154 155    {156      auto Diag = diag(FileMoveRange.getBegin(),157                       "std::move of the %select{|const }0"158                       "%select{expression|variable %5}1 "159                       "%select{|of the trivially-copyable type %6 }2"160                       "has no effect%select{; remove std::move()|}3"161                       "%select{| or make the variable non-const}4")162                  << IsConstArg << IsVariable << IsTriviallyCopyable163                  << IsRVRefParam164                  << (IsConstArg && IsVariable && !IsTriviallyCopyable) << Var165                  << Arg->getType();166      if (!IsRVRefParam)167        replaceCallWithArg(CallMove, Diag, SM, getLangOpts());168    }169    if (IsRVRefParam) {170      // Generate notes for an invocation with an rvalue reference parameter.171      const auto *ReceivingCallExpr = dyn_cast<CallExpr>(ReceivingExpr);172      const auto *ReceivingConstructExpr =173          dyn_cast<CXXConstructExpr>(ReceivingExpr);174      // Skipping the invocation which is a template instantiation.175      if ((!ReceivingCallExpr || !ReceivingCallExpr->getDirectCallee() ||176           ReceivingCallExpr->getDirectCallee()->isTemplateInstantiation()) &&177          (!ReceivingConstructExpr ||178           !ReceivingConstructExpr->getConstructor() ||179           ReceivingConstructExpr->getConstructor()->isTemplateInstantiation()))180        return;181 182      const NamedDecl *FunctionName = nullptr;183      FunctionName =184          ReceivingCallExpr185              ? ReceivingCallExpr->getDirectCallee()->getUnderlyingDecl()186              : ReceivingConstructExpr->getConstructor()->getUnderlyingDecl();187 188      QualType NoRefType = (*InvocationParmType)->getPointeeType();189      PrintingPolicy PolicyWithSuppressedTag(getLangOpts());190      PolicyWithSuppressedTag.SuppressTagKeyword = true;191      PolicyWithSuppressedTag.SuppressUnwrittenScope = true;192      std::string ExpectParmTypeName =193          NoRefType.getAsString(PolicyWithSuppressedTag);194      if (!NoRefType->isPointerType()) {195        NoRefType.addConst();196        ExpectParmTypeName =197            NoRefType.getAsString(PolicyWithSuppressedTag) + " &";198      }199 200      diag(InvocationParm->getLocation(),201           "consider changing the %ordinal0 parameter of %1 from %2 to '%3'",202           DiagnosticIDs::Note)203          << (InvocationParm->getFunctionScopeIndex() + 1) << FunctionName204          << *InvocationParmType << ExpectParmTypeName;205    }206  } else if (ReceivingExpr && CheckMoveToConstRef) {207    if ((*InvocationParmType)->isRValueReferenceType())208      return;209 210    {211      auto Diag = diag(FileMoveRange.getBegin(),212                       "passing result of std::move() as a const reference "213                       "argument; no move will actually happen");214 215      replaceCallWithArg(CallMove, Diag, SM, getLangOpts());216    }217 218    if (const CXXRecordDecl *RecordDecl = ArgType->getAsCXXRecordDecl();219        RecordDecl && RecordDecl->hasDefinition() &&220        !(RecordDecl->hasMoveConstructor() &&221          RecordDecl->hasMoveAssignment())) {222      const bool MissingMoveAssignment = !RecordDecl->hasMoveAssignment();223      const bool MissingMoveConstructor = !RecordDecl->hasMoveConstructor();224      const bool MissingBoth = MissingMoveAssignment && MissingMoveConstructor;225 226      diag(RecordDecl->getLocation(),227           "%0 is not move "228           "%select{|assignable}1%select{|/}2%select{|constructible}3",229           DiagnosticIDs::Note)230          << RecordDecl << MissingMoveAssignment << MissingBoth231          << MissingMoveConstructor;232    }233  }234}235 236} // namespace clang::tidy::performance237