1 //===--- RvalueReferenceParamNotMovedCheck.cpp - clang-tidy ---------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "RvalueReferenceParamNotMovedCheck.h"
10 #include "../utils/Matchers.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 using namespace clang::ast_matchers
;
16 namespace clang::tidy::cppcoreguidelines
{
18 using matchers::hasUnevaluatedContext
;
21 AST_MATCHER_P(LambdaExpr
, valueCapturesVar
, DeclarationMatcher
, VarMatcher
) {
22 return std::find_if(Node
.capture_begin(), Node
.capture_end(),
23 [&](const LambdaCapture
&Capture
) {
24 return Capture
.capturesVariable() &&
25 VarMatcher
.matches(*Capture
.getCapturedVar(),
27 Capture
.getCaptureKind() == LCK_ByCopy
;
28 }) != Node
.capture_end();
30 AST_MATCHER_P2(Stmt
, argumentOf
, bool, AllowPartialMove
, StatementMatcher
,
32 if (AllowPartialMove
) {
33 return stmt(anyOf(Ref
, hasDescendant(Ref
))).matches(Node
, Finder
, Builder
);
35 return Ref
.matches(Node
, Finder
, Builder
);
40 void RvalueReferenceParamNotMovedCheck::registerMatchers(MatchFinder
*Finder
) {
41 auto ToParam
= hasAnyParameter(parmVarDecl(equalsBoundNode("param")));
43 StatementMatcher MoveCallMatcher
=
46 anyOf(callee(functionDecl(hasName("::std::move"))),
47 callee(unresolvedLookupExpr(hasAnyDeclaration(
48 namedDecl(hasUnderlyingDecl(hasName("::std::move"))))))),
52 declRefExpr(to(equalsBoundNode("param"))).bind("ref"))),
54 lambdaExpr(valueCapturesVar(equalsBoundNode("param"))))),
55 unless(anyOf(hasAncestor(typeLoc()),
56 hasAncestor(expr(hasUnevaluatedContext())))))
61 hasType(type(rValueReferenceType())), parmVarDecl().bind("param"),
62 unless(hasType(references(qualType(
63 anyOf(isConstQualified(), substTemplateTypeParmType()))))),
64 optionally(hasType(qualType(references(templateTypeParmType(
65 hasDeclaration(templateTypeParmDecl().bind("template-type"))))))),
66 anyOf(hasAncestor(cxxConstructorDecl(
67 ToParam
, isDefinition(), unless(isMoveConstructor()),
68 optionally(hasDescendant(MoveCallMatcher
)))),
69 hasAncestor(functionDecl(
70 unless(cxxConstructorDecl()), ToParam
,
71 unless(cxxMethodDecl(isMoveAssignmentOperator())),
72 hasBody(optionally(hasDescendant(MoveCallMatcher
))))))),
76 void RvalueReferenceParamNotMovedCheck::check(
77 const MatchFinder::MatchResult
&Result
) {
78 const auto *Param
= Result
.Nodes
.getNodeAs
<ParmVarDecl
>("param");
79 const auto *TemplateType
=
80 Result
.Nodes
.getNodeAs
<TemplateTypeParmDecl
>("template-type");
85 if (IgnoreUnnamedParams
&& Param
->getName().empty())
88 const auto *Function
= dyn_cast
<FunctionDecl
>(Param
->getDeclContext());
92 if (IgnoreNonDeducedTemplateTypes
&& TemplateType
)
96 if (const FunctionTemplateDecl
*FuncTemplate
=
97 Function
->getDescribedFunctionTemplate()) {
98 const TemplateParameterList
*Params
=
99 FuncTemplate
->getTemplateParameters();
100 if (llvm::is_contained(*Params
, TemplateType
)) {
101 // Ignore forwarding reference
107 const auto *MoveCall
= Result
.Nodes
.getNodeAs
<CallExpr
>("move-call");
109 diag(Param
->getLocation(),
110 "rvalue reference parameter %0 is never moved from "
111 "inside the function body")
116 RvalueReferenceParamNotMovedCheck::RvalueReferenceParamNotMovedCheck(
117 StringRef Name
, ClangTidyContext
*Context
)
118 : ClangTidyCheck(Name
, Context
),
119 AllowPartialMove(Options
.getLocalOrGlobal("AllowPartialMove", false)),
121 Options
.getLocalOrGlobal("IgnoreUnnamedParams", false)),
122 IgnoreNonDeducedTemplateTypes(
123 Options
.getLocalOrGlobal("IgnoreNonDeducedTemplateTypes", false)) {}
125 void RvalueReferenceParamNotMovedCheck::storeOptions(
126 ClangTidyOptions::OptionMap
&Opts
) {
127 Options
.store(Opts
, "AllowPartialMove", AllowPartialMove
);
128 Options
.store(Opts
, "IgnoreUnnamedParams", IgnoreUnnamedParams
);
129 Options
.store(Opts
, "IgnoreNonDeducedTemplateTypes",
130 IgnoreNonDeducedTemplateTypes
);
133 } // namespace clang::tidy::cppcoreguidelines