clang-tools 22.0.0git
RvalueReferenceParamNotMovedCheck.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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
10#include "../utils/Matchers.h"
11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13
14using namespace clang::ast_matchers;
15
17
18using matchers::hasUnevaluatedContext;
19
20namespace {
21AST_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(),
26 Finder, Builder) &&
27 Capture.getCaptureKind() == LCK_ByCopy;
28 }) != Node.capture_end();
29}
30AST_MATCHER_P2(Stmt, argumentOf, bool, AllowPartialMove, StatementMatcher,
31 Ref) {
32 if (AllowPartialMove)
33 return stmt(anyOf(Ref, hasDescendant(Ref))).matches(Node, Finder, Builder);
34 return Ref.matches(Node, Finder, Builder);
35}
36} // namespace
37
39 auto ToParam = hasAnyParameter(parmVarDecl(equalsBoundNode("param")));
40
41 const StatementMatcher MoveCallMatcher =
42 callExpr(
43 argumentCountIs(1),
44 anyOf(callee(functionDecl(hasName(MoveFunction))),
45 callee(unresolvedLookupExpr(hasAnyDeclaration(
46 namedDecl(hasUnderlyingDecl(hasName(MoveFunction))))))),
47 hasArgument(
48 0, argumentOf(
49 AllowPartialMove,
50 declRefExpr(to(equalsBoundNode("param"))).bind("ref"))),
51 unless(hasAncestor(
52 lambdaExpr(valueCapturesVar(equalsBoundNode("param"))))),
53 unless(anyOf(hasAncestor(typeLoc()),
54 hasAncestor(expr(hasUnevaluatedContext())))))
55 .bind("move-call");
56
57 Finder->addMatcher(
58 parmVarDecl(
59 hasType(type(rValueReferenceType())), parmVarDecl().bind("param"),
60 unless(hasType(references(qualType(
61 anyOf(isConstQualified(), substTemplateTypeParmType()))))),
62 optionally(hasType(qualType(references(templateTypeParmType(
63 hasDeclaration(templateTypeParmDecl().bind("template-type"))))))),
64 hasDeclContext(
65 functionDecl(
66 isDefinition(), unless(isDeleted()), unless(isDefaulted()),
67 unless(cxxConstructorDecl(isMoveConstructor())),
68 unless(cxxMethodDecl(isMoveAssignmentOperator())), ToParam,
69 anyOf(cxxConstructorDecl(
70 optionally(hasDescendant(MoveCallMatcher))),
71 functionDecl(unless(cxxConstructorDecl()),
72 optionally(hasBody(
73 hasDescendant(MoveCallMatcher))))))
74 .bind("func"))),
75 this);
76}
77
79 const MatchFinder::MatchResult &Result) {
80 const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param");
81 const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("func");
82 const auto *TemplateType =
83 Result.Nodes.getNodeAs<TemplateTypeParmDecl>("template-type");
84
85 if (!Param || !Function)
86 return;
87
88 if (IgnoreUnnamedParams && Param->getName().empty())
89 return;
90
91 if (!Param->isUsed() && Param->hasAttr<UnusedAttr>())
92 return;
93
94 if (IgnoreNonDeducedTemplateTypes && TemplateType)
95 return;
96
97 if (TemplateType) {
98 if (const FunctionTemplateDecl *FuncTemplate =
99 Function->getDescribedFunctionTemplate()) {
100 const TemplateParameterList *Params =
101 FuncTemplate->getTemplateParameters();
102 if (llvm::is_contained(*Params, TemplateType)) {
103 // Ignore forwarding reference
104 return;
105 }
106 }
107 }
108
109 const auto *MoveCall = Result.Nodes.getNodeAs<CallExpr>("move-call");
110 if (!MoveCall) {
111 diag(Param->getLocation(),
112 "rvalue reference parameter %0 is never moved from "
113 "inside the function body")
114 << Param;
115 }
116}
117
119 StringRef Name, ClangTidyContext *Context)
120 : ClangTidyCheck(Name, Context),
121 AllowPartialMove(Options.get("AllowPartialMove", false)),
122 IgnoreUnnamedParams(Options.get("IgnoreUnnamedParams", false)),
123 IgnoreNonDeducedTemplateTypes(
124 Options.get("IgnoreNonDeducedTemplateTypes", false)),
125 MoveFunction(Options.get("MoveFunction", "::std::move")) {}
126
129 Options.store(Opts, "AllowPartialMove", AllowPartialMove);
130 Options.store(Opts, "IgnoreUnnamedParams", IgnoreUnnamedParams);
131 Options.store(Opts, "IgnoreNonDeducedTemplateTypes",
132 IgnoreNonDeducedTemplateTypes);
133 Options.store(Opts, "MoveFunction", MoveFunction);
134}
135
136} // namespace clang::tidy::cppcoreguidelines
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
AST_MATCHER_P(Stmt, isStatementIdenticalToBoundNode, std::string, ID)
llvm::StringMap< ClangTidyValue > OptionMap