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