1c491c917SChris Cotter //===--- RvalueReferenceParamNotMovedCheck.cpp - clang-tidy ---------------===// 2c491c917SChris Cotter // 3c491c917SChris Cotter // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4c491c917SChris Cotter // See https://llvm.org/LICENSE.txt for license information. 5c491c917SChris Cotter // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6c491c917SChris Cotter // 7c491c917SChris Cotter //===----------------------------------------------------------------------===// 8c491c917SChris Cotter 9c491c917SChris Cotter #include "RvalueReferenceParamNotMovedCheck.h" 10bd762846SChris Cotter #include "../utils/Matchers.h" 11c491c917SChris Cotter #include "clang/AST/ASTContext.h" 12c491c917SChris Cotter #include "clang/ASTMatchers/ASTMatchFinder.h" 13c491c917SChris Cotter 14c491c917SChris Cotter using namespace clang::ast_matchers; 15c491c917SChris Cotter 16c491c917SChris Cotter namespace clang::tidy::cppcoreguidelines { 17c491c917SChris Cotter 18bd762846SChris Cotter using matchers::hasUnevaluatedContext; 19bd762846SChris Cotter 20c491c917SChris Cotter namespace { 21c491c917SChris Cotter AST_MATCHER_P(LambdaExpr, valueCapturesVar, DeclarationMatcher, VarMatcher) { 22c491c917SChris Cotter return std::find_if(Node.capture_begin(), Node.capture_end(), 23c491c917SChris Cotter [&](const LambdaCapture &Capture) { 24c491c917SChris Cotter return Capture.capturesVariable() && 25c491c917SChris Cotter VarMatcher.matches(*Capture.getCapturedVar(), 26c491c917SChris Cotter Finder, Builder) && 27c491c917SChris Cotter Capture.getCaptureKind() == LCK_ByCopy; 28c491c917SChris Cotter }) != Node.capture_end(); 29c491c917SChris Cotter } 30c491c917SChris Cotter AST_MATCHER_P2(Stmt, argumentOf, bool, AllowPartialMove, StatementMatcher, 31c491c917SChris Cotter Ref) { 32c491c917SChris Cotter if (AllowPartialMove) { 33c491c917SChris Cotter return stmt(anyOf(Ref, hasDescendant(Ref))).matches(Node, Finder, Builder); 34c491c917SChris Cotter } 3501c8bf6fSPiotr Zegar return Ref.matches(Node, Finder, Builder); 36c491c917SChris Cotter } 37c491c917SChris Cotter } // namespace 38c491c917SChris Cotter 39c491c917SChris Cotter void RvalueReferenceParamNotMovedCheck::registerMatchers(MatchFinder *Finder) { 40c491c917SChris Cotter auto ToParam = hasAnyParameter(parmVarDecl(equalsBoundNode("param"))); 41c491c917SChris Cotter 42c491c917SChris Cotter StatementMatcher MoveCallMatcher = 43c491c917SChris Cotter callExpr( 44bd762846SChris Cotter argumentCountIs(1), 45c491c917SChris Cotter anyOf(callee(functionDecl(hasName("::std::move"))), 46c491c917SChris Cotter callee(unresolvedLookupExpr(hasAnyDeclaration( 47c491c917SChris Cotter namedDecl(hasUnderlyingDecl(hasName("::std::move"))))))), 48c491c917SChris Cotter hasArgument( 49c491c917SChris Cotter 0, argumentOf( 50c491c917SChris Cotter AllowPartialMove, 51c491c917SChris Cotter declRefExpr(to(equalsBoundNode("param"))).bind("ref"))), 52c491c917SChris Cotter unless(hasAncestor( 53bd762846SChris Cotter lambdaExpr(valueCapturesVar(equalsBoundNode("param"))))), 54bd762846SChris Cotter unless(anyOf(hasAncestor(typeLoc()), 55bd762846SChris Cotter hasAncestor(expr(hasUnevaluatedContext()))))) 56c491c917SChris Cotter .bind("move-call"); 57c491c917SChris Cotter 58c491c917SChris Cotter Finder->addMatcher( 59c491c917SChris Cotter parmVarDecl( 60c491c917SChris Cotter hasType(type(rValueReferenceType())), parmVarDecl().bind("param"), 61c491c917SChris Cotter unless(hasType(references(qualType( 62c491c917SChris Cotter anyOf(isConstQualified(), substTemplateTypeParmType()))))), 63c491c917SChris Cotter optionally(hasType(qualType(references(templateTypeParmType( 64c491c917SChris Cotter hasDeclaration(templateTypeParmDecl().bind("template-type"))))))), 6520d21028SPiotr Zegar hasDeclContext( 6620d21028SPiotr Zegar functionDecl( 6720d21028SPiotr Zegar isDefinition(), unless(isDeleted()), unless(isDefaulted()), 6820d21028SPiotr Zegar unless(cxxConstructorDecl(isMoveConstructor())), 6920d21028SPiotr Zegar unless(cxxMethodDecl(isMoveAssignmentOperator())), ToParam, 7020d21028SPiotr Zegar anyOf(cxxConstructorDecl( 7120d21028SPiotr Zegar optionally(hasDescendant(MoveCallMatcher))), 7220d21028SPiotr Zegar functionDecl(unless(cxxConstructorDecl()), 7320d21028SPiotr Zegar optionally(hasBody( 7420d21028SPiotr Zegar hasDescendant(MoveCallMatcher)))))) 7520d21028SPiotr Zegar .bind("func"))), 76c491c917SChris Cotter this); 77c491c917SChris Cotter } 78c491c917SChris Cotter 79c491c917SChris Cotter void RvalueReferenceParamNotMovedCheck::check( 80c491c917SChris Cotter const MatchFinder::MatchResult &Result) { 81c491c917SChris Cotter const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param"); 8220d21028SPiotr Zegar const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("func"); 83c491c917SChris Cotter const auto *TemplateType = 84c491c917SChris Cotter Result.Nodes.getNodeAs<TemplateTypeParmDecl>("template-type"); 85c491c917SChris Cotter 8620d21028SPiotr Zegar if (!Param || !Function) 87c491c917SChris Cotter return; 88c491c917SChris Cotter 89c491c917SChris Cotter if (IgnoreUnnamedParams && Param->getName().empty()) 90c491c917SChris Cotter return; 91c491c917SChris Cotter 92bb6a98c8SAMS21 if (!Param->isUsed() && Param->hasAttr<UnusedAttr>()) 93bb6a98c8SAMS21 return; 94bb6a98c8SAMS21 95c491c917SChris Cotter if (IgnoreNonDeducedTemplateTypes && TemplateType) 96c491c917SChris Cotter return; 97c491c917SChris Cotter 98c491c917SChris Cotter if (TemplateType) { 99c491c917SChris Cotter if (const FunctionTemplateDecl *FuncTemplate = 100c491c917SChris Cotter Function->getDescribedFunctionTemplate()) { 101c491c917SChris Cotter const TemplateParameterList *Params = 102c491c917SChris Cotter FuncTemplate->getTemplateParameters(); 103c491c917SChris Cotter if (llvm::is_contained(*Params, TemplateType)) { 104c491c917SChris Cotter // Ignore forwarding reference 105c491c917SChris Cotter return; 106c491c917SChris Cotter } 107c491c917SChris Cotter } 108c491c917SChris Cotter } 109c491c917SChris Cotter 110c491c917SChris Cotter const auto *MoveCall = Result.Nodes.getNodeAs<CallExpr>("move-call"); 111c491c917SChris Cotter if (!MoveCall) { 112c491c917SChris Cotter diag(Param->getLocation(), 113c491c917SChris Cotter "rvalue reference parameter %0 is never moved from " 114c491c917SChris Cotter "inside the function body") 115c491c917SChris Cotter << Param; 116c491c917SChris Cotter } 117c491c917SChris Cotter } 118c491c917SChris Cotter 119c491c917SChris Cotter RvalueReferenceParamNotMovedCheck::RvalueReferenceParamNotMovedCheck( 120c491c917SChris Cotter StringRef Name, ClangTidyContext *Context) 121c491c917SChris Cotter : ClangTidyCheck(Name, Context), 122*aaadaee7SCongcong Cai AllowPartialMove(Options.get("AllowPartialMove", false)), 123*aaadaee7SCongcong Cai IgnoreUnnamedParams(Options.get("IgnoreUnnamedParams", false)), 124c491c917SChris Cotter IgnoreNonDeducedTemplateTypes( 125*aaadaee7SCongcong Cai Options.get("IgnoreNonDeducedTemplateTypes", false)) {} 126c491c917SChris Cotter 127c491c917SChris Cotter void RvalueReferenceParamNotMovedCheck::storeOptions( 128c491c917SChris Cotter ClangTidyOptions::OptionMap &Opts) { 129c491c917SChris Cotter Options.store(Opts, "AllowPartialMove", AllowPartialMove); 130c491c917SChris Cotter Options.store(Opts, "IgnoreUnnamedParams", IgnoreUnnamedParams); 131c491c917SChris Cotter Options.store(Opts, "IgnoreNonDeducedTemplateTypes", 132c491c917SChris Cotter IgnoreNonDeducedTemplateTypes); 133c491c917SChris Cotter } 134c491c917SChris Cotter 135c491c917SChris Cotter } // namespace clang::tidy::cppcoreguidelines 136