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 if (!getLangOpts().CPlusPlus) 44 return; 45 46 auto MoveCallMatcher = 47 callExpr(callee(functionDecl(hasName("::std::move"))), argumentCountIs(1), 48 unless(isInTemplateInstantiation())) 49 .bind("call-move"); 50 51 Finder->addMatcher(MoveCallMatcher, this); 52 53 auto ConstParamMatcher = forEachArgumentWithParam( 54 MoveCallMatcher, parmVarDecl(hasType(references(isConstQualified())))); 55 56 Finder->addMatcher(callExpr(ConstParamMatcher).bind("receiving-expr"), this); 57 Finder->addMatcher(cxxConstructExpr(ConstParamMatcher).bind("receiving-expr"), 58 this); 59 } 60 61 void MoveConstArgCheck::check(const MatchFinder::MatchResult &Result) { 62 const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>("call-move"); 63 const auto *ReceivingExpr = Result.Nodes.getNodeAs<Expr>("receiving-expr"); 64 const Expr *Arg = CallMove->getArg(0); 65 SourceManager &SM = Result.Context->getSourceManager(); 66 67 CharSourceRange MoveRange = 68 CharSourceRange::getCharRange(CallMove->getSourceRange()); 69 CharSourceRange FileMoveRange = 70 Lexer::makeFileCharRange(MoveRange, SM, getLangOpts()); 71 if (!FileMoveRange.isValid()) 72 return; 73 74 bool IsConstArg = Arg->getType().isConstQualified(); 75 bool IsTriviallyCopyable = 76 Arg->getType().isTriviallyCopyableType(*Result.Context); 77 78 if (IsConstArg || IsTriviallyCopyable) { 79 if (const CXXRecordDecl *R = Arg->getType()->getAsCXXRecordDecl()) { 80 // According to [expr.prim.lambda]p3, "whether the closure type is 81 // trivially copyable" property can be changed by the implementation of 82 // the language, so we shouldn't rely on it when issuing diagnostics. 83 if (R->isLambda()) 84 return; 85 // Don't warn when the type is not copyable. 86 for (const auto *Ctor : R->ctors()) { 87 if (Ctor->isCopyConstructor() && Ctor->isDeleted()) 88 return; 89 } 90 } 91 92 if (!IsConstArg && IsTriviallyCopyable && !CheckTriviallyCopyableMove) 93 return; 94 95 bool IsVariable = isa<DeclRefExpr>(Arg); 96 const auto *Var = 97 IsVariable ? dyn_cast<DeclRefExpr>(Arg)->getDecl() : nullptr; 98 auto Diag = diag(FileMoveRange.getBegin(), 99 "std::move of the %select{|const }0" 100 "%select{expression|variable %4}1 " 101 "%select{|of the trivially-copyable type %5 }2" 102 "has no effect; remove std::move()" 103 "%select{| or make the variable non-const}3") 104 << IsConstArg << IsVariable << IsTriviallyCopyable 105 << (IsConstArg && IsVariable && !IsTriviallyCopyable) << Var 106 << Arg->getType(); 107 108 ReplaceCallWithArg(CallMove, Diag, SM, getLangOpts()); 109 } else if (ReceivingExpr) { 110 auto Diag = diag(FileMoveRange.getBegin(), 111 "passing result of std::move() as a const reference " 112 "argument; no move will actually happen"); 113 114 ReplaceCallWithArg(CallMove, Diag, SM, getLangOpts()); 115 } 116 } 117 118 } // namespace performance 119 } // namespace tidy 120 } // namespace clang 121