1 //===--- MoveConstArgCheck.cpp - clang-tidy -----------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "MoveConstArgCheck.h" 11 12 #include "clang/Lex/Lexer.h" 13 14 using namespace clang::ast_matchers; 15 16 namespace clang { 17 namespace tidy { 18 namespace performance { 19 20 static void ReplaceCallWithArg(const CallExpr *Call, DiagnosticBuilder &Diag, 21 const SourceManager &SM, 22 const LangOptions &LangOpts) { 23 const Expr *Arg = Call->getArg(0); 24 25 CharSourceRange BeforeArgumentsRange = Lexer::makeFileCharRange( 26 CharSourceRange::getCharRange(Call->getLocStart(), Arg->getLocStart()), 27 SM, LangOpts); 28 CharSourceRange AfterArgumentsRange = Lexer::makeFileCharRange( 29 CharSourceRange::getCharRange(Call->getLocEnd(), 30 Call->getLocEnd().getLocWithOffset(1)), 31 SM, LangOpts); 32 33 if (BeforeArgumentsRange.isValid() && AfterArgumentsRange.isValid()) { 34 Diag << FixItHint::CreateRemoval(BeforeArgumentsRange) 35 << FixItHint::CreateRemoval(AfterArgumentsRange); 36 } 37 } 38 39 void MoveConstArgCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { 40 Options.store(Opts, "CheckTriviallyCopyableMove", CheckTriviallyCopyableMove); 41 } 42 43 void MoveConstArgCheck::registerMatchers(MatchFinder *Finder) { 44 if (!getLangOpts().CPlusPlus) 45 return; 46 47 auto MoveCallMatcher = 48 callExpr(callee(functionDecl(hasName("::std::move"))), argumentCountIs(1), 49 unless(isInTemplateInstantiation())) 50 .bind("call-move"); 51 52 Finder->addMatcher(MoveCallMatcher, this); 53 54 auto ConstParamMatcher = forEachArgumentWithParam( 55 MoveCallMatcher, parmVarDecl(hasType(references(isConstQualified())))); 56 57 Finder->addMatcher(callExpr(ConstParamMatcher).bind("receiving-expr"), this); 58 Finder->addMatcher(cxxConstructExpr(ConstParamMatcher).bind("receiving-expr"), 59 this); 60 } 61 62 void MoveConstArgCheck::check(const MatchFinder::MatchResult &Result) { 63 const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>("call-move"); 64 const auto *ReceivingExpr = Result.Nodes.getNodeAs<Expr>("receiving-expr"); 65 const Expr *Arg = CallMove->getArg(0); 66 SourceManager &SM = Result.Context->getSourceManager(); 67 68 CharSourceRange MoveRange = 69 CharSourceRange::getCharRange(CallMove->getSourceRange()); 70 CharSourceRange FileMoveRange = 71 Lexer::makeFileCharRange(MoveRange, SM, getLangOpts()); 72 if (!FileMoveRange.isValid()) 73 return; 74 75 bool IsConstArg = Arg->getType().isConstQualified(); 76 bool IsTriviallyCopyable = 77 Arg->getType().isTriviallyCopyableType(*Result.Context); 78 79 if (IsConstArg || IsTriviallyCopyable) { 80 if (const CXXRecordDecl *R = Arg->getType()->getAsCXXRecordDecl()) { 81 // According to [expr.prim.lambda]p3, "whether the closure type is 82 // trivially copyable" property can be changed by the implementation of 83 // the language, so we shouldn't rely on it when issuing diagnostics. 84 if (R->isLambda()) 85 return; 86 // Don't warn when the type is not copyable. 87 for (const auto *Ctor : R->ctors()) { 88 if (Ctor->isCopyConstructor() && Ctor->isDeleted()) 89 return; 90 } 91 } 92 93 if (!IsConstArg && IsTriviallyCopyable && !CheckTriviallyCopyableMove) 94 return; 95 96 bool IsVariable = isa<DeclRefExpr>(Arg); 97 const auto *Var = 98 IsVariable ? dyn_cast<DeclRefExpr>(Arg)->getDecl() : nullptr; 99 auto Diag = diag(FileMoveRange.getBegin(), 100 "std::move of the %select{|const }0" 101 "%select{expression|variable %4}1 " 102 "%select{|of the trivially-copyable type %5 }2" 103 "has no effect; remove std::move()" 104 "%select{| or make the variable non-const}3") 105 << IsConstArg << IsVariable << IsTriviallyCopyable 106 << (IsConstArg && IsVariable && !IsTriviallyCopyable) << Var 107 << Arg->getType(); 108 109 ReplaceCallWithArg(CallMove, Diag, SM, getLangOpts()); 110 } else if (ReceivingExpr) { 111 auto Diag = diag(FileMoveRange.getBegin(), 112 "passing result of std::move() as a const reference " 113 "argument; no move will actually happen"); 114 115 ReplaceCallWithArg(CallMove, Diag, SM, getLangOpts()); 116 } 117 } 118 119 } // namespace performance 120 } // namespace tidy 121 } // namespace clang 122