| //===--- RvalueReferenceParamNotMovedCheck.cpp - clang-tidy ---------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "RvalueReferenceParamNotMovedCheck.h" |
| #include "../utils/Matchers.h" |
| #include "clang/AST/ASTContext.h" |
| #include "clang/ASTMatchers/ASTMatchFinder.h" |
| |
| using namespace clang::ast_matchers; |
| |
| namespace clang::tidy::cppcoreguidelines { |
| |
| using matchers::hasUnevaluatedContext; |
| |
| namespace { |
| AST_MATCHER_P(LambdaExpr, valueCapturesVar, DeclarationMatcher, VarMatcher) { |
| return std::find_if(Node.capture_begin(), Node.capture_end(), |
| [&](const LambdaCapture &Capture) { |
| return Capture.capturesVariable() && |
| VarMatcher.matches(*Capture.getCapturedVar(), |
| Finder, Builder) && |
| Capture.getCaptureKind() == LCK_ByCopy; |
| }) != Node.capture_end(); |
| } |
| AST_MATCHER_P2(Stmt, argumentOf, bool, AllowPartialMove, StatementMatcher, |
| Ref) { |
| if (AllowPartialMove) { |
| return stmt(anyOf(Ref, hasDescendant(Ref))).matches(Node, Finder, Builder); |
| } |
| return Ref.matches(Node, Finder, Builder); |
| } |
| } // namespace |
| |
| void RvalueReferenceParamNotMovedCheck::registerMatchers(MatchFinder *Finder) { |
| auto ToParam = hasAnyParameter(parmVarDecl(equalsBoundNode("param"))); |
| |
| StatementMatcher MoveCallMatcher = |
| callExpr( |
| argumentCountIs(1), |
| anyOf(callee(functionDecl(hasName("::std::move"))), |
| callee(unresolvedLookupExpr(hasAnyDeclaration( |
| namedDecl(hasUnderlyingDecl(hasName("::std::move"))))))), |
| hasArgument( |
| 0, argumentOf( |
| AllowPartialMove, |
| declRefExpr(to(equalsBoundNode("param"))).bind("ref"))), |
| unless(hasAncestor( |
| lambdaExpr(valueCapturesVar(equalsBoundNode("param"))))), |
| unless(anyOf(hasAncestor(typeLoc()), |
| hasAncestor(expr(hasUnevaluatedContext()))))) |
| .bind("move-call"); |
| |
| Finder->addMatcher( |
| parmVarDecl( |
| hasType(type(rValueReferenceType())), parmVarDecl().bind("param"), |
| unless(hasType(references(qualType( |
| anyOf(isConstQualified(), substTemplateTypeParmType()))))), |
| optionally(hasType(qualType(references(templateTypeParmType( |
| hasDeclaration(templateTypeParmDecl().bind("template-type"))))))), |
| hasDeclContext( |
| functionDecl( |
| isDefinition(), unless(isDeleted()), unless(isDefaulted()), |
| unless(cxxConstructorDecl(isMoveConstructor())), |
| unless(cxxMethodDecl(isMoveAssignmentOperator())), ToParam, |
| anyOf(cxxConstructorDecl( |
| optionally(hasDescendant(MoveCallMatcher))), |
| functionDecl(unless(cxxConstructorDecl()), |
| optionally(hasBody( |
| hasDescendant(MoveCallMatcher)))))) |
| .bind("func"))), |
| this); |
| } |
| |
| void RvalueReferenceParamNotMovedCheck::check( |
| const MatchFinder::MatchResult &Result) { |
| const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param"); |
| const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("func"); |
| const auto *TemplateType = |
| Result.Nodes.getNodeAs<TemplateTypeParmDecl>("template-type"); |
| |
| if (!Param || !Function) |
| return; |
| |
| if (IgnoreUnnamedParams && Param->getName().empty()) |
| return; |
| |
| if (!Param->isUsed() && Param->hasAttr<UnusedAttr>()) |
| return; |
| |
| if (IgnoreNonDeducedTemplateTypes && TemplateType) |
| return; |
| |
| if (TemplateType) { |
| if (const FunctionTemplateDecl *FuncTemplate = |
| Function->getDescribedFunctionTemplate()) { |
| const TemplateParameterList *Params = |
| FuncTemplate->getTemplateParameters(); |
| if (llvm::is_contained(*Params, TemplateType)) { |
| // Ignore forwarding reference |
| return; |
| } |
| } |
| } |
| |
| const auto *MoveCall = Result.Nodes.getNodeAs<CallExpr>("move-call"); |
| if (!MoveCall) { |
| diag(Param->getLocation(), |
| "rvalue reference parameter %0 is never moved from " |
| "inside the function body") |
| << Param; |
| } |
| } |
| |
| RvalueReferenceParamNotMovedCheck::RvalueReferenceParamNotMovedCheck( |
| StringRef Name, ClangTidyContext *Context) |
| : ClangTidyCheck(Name, Context), |
| AllowPartialMove(Options.getLocalOrGlobal("AllowPartialMove", false)), |
| IgnoreUnnamedParams( |
| Options.getLocalOrGlobal("IgnoreUnnamedParams", false)), |
| IgnoreNonDeducedTemplateTypes( |
| Options.getLocalOrGlobal("IgnoreNonDeducedTemplateTypes", false)) {} |
| |
| void RvalueReferenceParamNotMovedCheck::storeOptions( |
| ClangTidyOptions::OptionMap &Opts) { |
| Options.store(Opts, "AllowPartialMove", AllowPartialMove); |
| Options.store(Opts, "IgnoreUnnamedParams", IgnoreUnnamedParams); |
| Options.store(Opts, "IgnoreNonDeducedTemplateTypes", |
| IgnoreNonDeducedTemplateTypes); |
| } |
| |
| } // namespace clang::tidy::cppcoreguidelines |