clang-tools 18.0.0git
RvalueReferenceParamNotMovedCheck.cpp
Go to the documentation of this file.
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
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 }
35 return Ref.matches(Node, Finder, Builder);
36}
37} // namespace
38
40 auto ToParam = hasAnyParameter(parmVarDecl(equalsBoundNode("param")));
41
42 StatementMatcher MoveCallMatcher =
43 callExpr(
44 argumentCountIs(1),
45 anyOf(callee(functionDecl(hasName("::std::move"))),
46 callee(unresolvedLookupExpr(hasAnyDeclaration(
47 namedDecl(hasUnderlyingDecl(hasName("::std::move"))))))),
48 hasArgument(
49 0, argumentOf(
50 AllowPartialMove,
51 declRefExpr(to(equalsBoundNode("param"))).bind("ref"))),
52 unless(hasAncestor(
53 lambdaExpr(valueCapturesVar(equalsBoundNode("param"))))),
54 unless(anyOf(hasAncestor(typeLoc()),
55 hasAncestor(expr(hasUnevaluatedContext())))))
56 .bind("move-call");
57
58 Finder->addMatcher(
59 parmVarDecl(
60 hasType(type(rValueReferenceType())), parmVarDecl().bind("param"),
61 unless(hasType(references(qualType(
62 anyOf(isConstQualified(), substTemplateTypeParmType()))))),
63 optionally(hasType(qualType(references(templateTypeParmType(
64 hasDeclaration(templateTypeParmDecl().bind("template-type"))))))),
65 anyOf(hasAncestor(cxxConstructorDecl(
66 ToParam, isDefinition(), unless(isMoveConstructor()),
67 optionally(hasDescendant(MoveCallMatcher)))),
68 hasAncestor(functionDecl(
69 unless(cxxConstructorDecl()), ToParam,
70 unless(cxxMethodDecl(isMoveAssignmentOperator())),
71 hasBody(optionally(hasDescendant(MoveCallMatcher))))))),
72 this);
73}
74
76 const MatchFinder::MatchResult &Result) {
77 const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param");
78 const auto *TemplateType =
79 Result.Nodes.getNodeAs<TemplateTypeParmDecl>("template-type");
80
81 if (!Param)
82 return;
83
84 if (IgnoreUnnamedParams && Param->getName().empty())
85 return;
86
87 const auto *Function = dyn_cast<FunctionDecl>(Param->getDeclContext());
88 if (!Function)
89 return;
90
91 if (IgnoreNonDeducedTemplateTypes && TemplateType)
92 return;
93
94 if (TemplateType) {
95 if (const FunctionTemplateDecl *FuncTemplate =
96 Function->getDescribedFunctionTemplate()) {
97 const TemplateParameterList *Params =
98 FuncTemplate->getTemplateParameters();
99 if (llvm::is_contained(*Params, TemplateType)) {
100 // Ignore forwarding reference
101 return;
102 }
103 }
104 }
105
106 const auto *MoveCall = Result.Nodes.getNodeAs<CallExpr>("move-call");
107 if (!MoveCall) {
108 diag(Param->getLocation(),
109 "rvalue reference parameter %0 is never moved from "
110 "inside the function body")
111 << Param;
112 }
113}
114
116 StringRef Name, ClangTidyContext *Context)
117 : ClangTidyCheck(Name, Context),
118 AllowPartialMove(Options.getLocalOrGlobal("AllowPartialMove", false)),
119 IgnoreUnnamedParams(
120 Options.getLocalOrGlobal("IgnoreUnnamedParams", false)),
121 IgnoreNonDeducedTemplateTypes(
122 Options.getLocalOrGlobal("IgnoreNonDeducedTemplateTypes", false)) {}
123
126 Options.store(Opts, "AllowPartialMove", AllowPartialMove);
127 Options.store(Opts, "IgnoreUnnamedParams", IgnoreUnnamedParams);
128 Options.store(Opts, "IgnoreNonDeducedTemplateTypes",
129 IgnoreNonDeducedTemplateTypes);
130}
131
132} // namespace clang::tidy::cppcoreguidelines
CodeCompletionBuilder Builder
llvm::StringRef Name
::clang::DynTypedNode Node
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
AST_MATCHER_P(UserDefinedLiteral, hasLiteral, clang::ast_matchers::internal::Matcher< Expr >, InnerMatcher)
llvm::StringMap< ClangTidyValue > OptionMap