1 //===--- RvalueReferenceParamNotMovedCheck.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 "RvalueReferenceParamNotMovedCheck.h" 10 #include "clang/AST/ASTContext.h" 11 #include "clang/ASTMatchers/ASTMatchFinder.h" 12 13 using namespace clang::ast_matchers; 14 15 namespace clang::tidy::cppcoreguidelines { 16 17 namespace { 18 AST_MATCHER_P(LambdaExpr, valueCapturesVar, DeclarationMatcher, VarMatcher) { 19 return std::find_if(Node.capture_begin(), Node.capture_end(), 20 [&](const LambdaCapture &Capture) { 21 return Capture.capturesVariable() && 22 VarMatcher.matches(*Capture.getCapturedVar(), 23 Finder, Builder) && 24 Capture.getCaptureKind() == LCK_ByCopy; 25 }) != Node.capture_end(); 26 } 27 AST_MATCHER_P2(Stmt, argumentOf, bool, AllowPartialMove, StatementMatcher, 28 Ref) { 29 if (AllowPartialMove) { 30 return stmt(anyOf(Ref, hasDescendant(Ref))).matches(Node, Finder, Builder); 31 } else { 32 return Ref.matches(Node, Finder, Builder); 33 } 34 } 35 } // namespace 36 37 void RvalueReferenceParamNotMovedCheck::registerMatchers(MatchFinder *Finder) { 38 auto ToParam = hasAnyParameter(parmVarDecl(equalsBoundNode("param"))); 39 40 StatementMatcher MoveCallMatcher = 41 callExpr( 42 anyOf(callee(functionDecl(hasName("::std::move"))), 43 callee(unresolvedLookupExpr(hasAnyDeclaration( 44 namedDecl(hasUnderlyingDecl(hasName("::std::move"))))))), 45 argumentCountIs(1), 46 hasArgument( 47 0, argumentOf( 48 AllowPartialMove, 49 declRefExpr(to(equalsBoundNode("param"))).bind("ref"))), 50 unless(hasAncestor( 51 lambdaExpr(valueCapturesVar(equalsBoundNode("param")))))) 52 .bind("move-call"); 53 54 Finder->addMatcher( 55 parmVarDecl( 56 hasType(type(rValueReferenceType())), parmVarDecl().bind("param"), 57 unless(hasType(references(qualType( 58 anyOf(isConstQualified(), substTemplateTypeParmType()))))), 59 optionally(hasType(qualType(references(templateTypeParmType( 60 hasDeclaration(templateTypeParmDecl().bind("template-type"))))))), 61 anyOf(hasAncestor(cxxConstructorDecl( 62 ToParam, isDefinition(), unless(isMoveConstructor()), 63 optionally(hasDescendant(MoveCallMatcher)))), 64 hasAncestor(functionDecl( 65 unless(cxxConstructorDecl()), ToParam, 66 unless(cxxMethodDecl(isMoveAssignmentOperator())), 67 hasBody(optionally(hasDescendant(MoveCallMatcher))))))), 68 this); 69 } 70 71 void RvalueReferenceParamNotMovedCheck::check( 72 const MatchFinder::MatchResult &Result) { 73 const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param"); 74 const auto *TemplateType = 75 Result.Nodes.getNodeAs<TemplateTypeParmDecl>("template-type"); 76 77 if (!Param) 78 return; 79 80 if (IgnoreUnnamedParams && Param->getName().empty()) 81 return; 82 83 const auto *Function = dyn_cast<FunctionDecl>(Param->getDeclContext()); 84 if (!Function) 85 return; 86 87 if (IgnoreNonDeducedTemplateTypes && TemplateType) 88 return; 89 90 if (TemplateType) { 91 if (const FunctionTemplateDecl *FuncTemplate = 92 Function->getDescribedFunctionTemplate()) { 93 const TemplateParameterList *Params = 94 FuncTemplate->getTemplateParameters(); 95 if (llvm::is_contained(*Params, TemplateType)) { 96 // Ignore forwarding reference 97 return; 98 } 99 } 100 } 101 102 const auto *MoveCall = Result.Nodes.getNodeAs<CallExpr>("move-call"); 103 if (!MoveCall) { 104 diag(Param->getLocation(), 105 "rvalue reference parameter %0 is never moved from " 106 "inside the function body") 107 << Param; 108 } 109 } 110 111 RvalueReferenceParamNotMovedCheck::RvalueReferenceParamNotMovedCheck( 112 StringRef Name, ClangTidyContext *Context) 113 : ClangTidyCheck(Name, Context), 114 AllowPartialMove(Options.getLocalOrGlobal("AllowPartialMove", false)), 115 IgnoreUnnamedParams( 116 Options.getLocalOrGlobal("IgnoreUnnamedParams", false)), 117 IgnoreNonDeducedTemplateTypes( 118 Options.getLocalOrGlobal("IgnoreNonDeducedTemplateTypes", false)) {} 119 120 void RvalueReferenceParamNotMovedCheck::storeOptions( 121 ClangTidyOptions::OptionMap &Opts) { 122 Options.store(Opts, "AllowPartialMove", AllowPartialMove); 123 Options.store(Opts, "IgnoreUnnamedParams", IgnoreUnnamedParams); 124 Options.store(Opts, "IgnoreNonDeducedTemplateTypes", 125 IgnoreNonDeducedTemplateTypes); 126 } 127 128 } // namespace clang::tidy::cppcoreguidelines 129