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