clang-tools 10.0.0

PassByValueCheck.cpp
Go to the documentation of this file.
1 //===--- PassByValueCheck.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
9 #include "PassByValueCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/AST/RecursiveASTVisitor.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/ASTMatchers/ASTMatchers.h"
14 #include "clang/Frontend/CompilerInstance.h"
15 #include "clang/Lex/Lexer.h"
16 #include "clang/Lex/Preprocessor.h"
17
18 using namespace clang::ast_matchers;
19 using namespace llvm;
20
21 namespace clang {
22 namespace tidy {
23 namespace modernize {
24
25 namespace {
26 /// Matches move-constructible classes.
27 ///
28 /// Given
29 /// \code
30 /// // POD types are trivially move constructible.
31 /// struct Foo { int a; };
32 ///
33 /// struct Bar {
34 /// Bar(Bar &&) = deleted;
35 /// int a;
36 /// };
37 /// \endcode
38 /// recordDecl(isMoveConstructible())
39 /// matches "Foo".
40 AST_MATCHER(CXXRecordDecl, isMoveConstructible) {
41 for (const CXXConstructorDecl *Ctor : Node.ctors()) {
42 if (Ctor->isMoveConstructor() && !Ctor->isDeleted())
43 return true;
44 }
45 return false;
46 }
47 } // namespace
48
49 static TypeMatcher constRefType() {
50 return lValueReferenceType(pointee(isConstQualified()));
51 }
52
53 static TypeMatcher nonConstValueType() {
54 return qualType(unless(anyOf(referenceType(), isConstQualified())));
55 }
56
57 /// Whether or not \p ParamDecl is used exactly one time in \p Ctor.
58 ///
59 /// Checks both in the init-list and the body of the constructor.
60 static bool paramReferredExactlyOnce(const CXXConstructorDecl *Ctor,
61 const ParmVarDecl *ParamDecl) {
62 /// \c clang::RecursiveASTVisitor that checks that the given
63 /// \c ParmVarDecl is used exactly one time.
64 ///
65 /// \see ExactlyOneUsageVisitor::hasExactlyOneUsageIn()
66 class ExactlyOneUsageVisitor
67 : public RecursiveASTVisitor<ExactlyOneUsageVisitor> {
68 friend class RecursiveASTVisitor<ExactlyOneUsageVisitor>;
69
70 public:
71 ExactlyOneUsageVisitor(const ParmVarDecl *ParamDecl)
72 : ParamDecl(ParamDecl) {}
73
74 /// Whether or not the parameter variable is referred only once in
75 /// the
76 /// given constructor.
77 bool hasExactlyOneUsageIn(const CXXConstructorDecl *Ctor) {
78 Count = 0;
79 TraverseDecl(const_cast<CXXConstructorDecl *>(Ctor));
80 return Count == 1;
81 }
82
83 private:
84 /// Counts the number of references to a variable.
85 ///
86 /// Stops the AST traversal if more than one usage is found.
87 bool VisitDeclRefExpr(DeclRefExpr *D) {
88 if (const ParmVarDecl *To = dyn_cast<ParmVarDecl>(D->getDecl())) {
89 if (To == ParamDecl) {
90 ++Count;
91 if (Count > 1) {
92 // No need to look further, used more than once.
93 return false;
94 }
95 }
96 }
97 return true;
98 }
99
100 const ParmVarDecl *ParamDecl;
101 unsigned Count;
102 };
103
104 return ExactlyOneUsageVisitor(ParamDecl).hasExactlyOneUsageIn(Ctor);
105 }
106
107 /// Find all references to \p ParamDecl across all of the
108 /// redeclarations of \p Ctor.
109 static SmallVector<const ParmVarDecl *, 2>
110 collectParamDecls(const CXXConstructorDecl *Ctor,
111 const ParmVarDecl *ParamDecl) {
112 SmallVector<const ParmVarDecl *, 2> Results;
113 unsigned ParamIdx = ParamDecl->getFunctionScopeIndex();
114
115 for (const FunctionDecl *Redecl : Ctor->redecls())
116 Results.push_back(Redecl->getParamDecl(ParamIdx));
117 return Results;
118 }
119
120 PassByValueCheck::PassByValueCheck(StringRef Name, ClangTidyContext *Context)
121 : ClangTidyCheck(Name, Context),
122 IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
123 Options.getLocalOrGlobal("IncludeStyle", "llvm"))),
124 ValuesOnly(Options.get("ValuesOnly", 0) != 0) {}
125
126 void PassByValueCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
127 Options.store(Opts, "IncludeStyle",
128 utils::IncludeSorter::toString(IncludeStyle));
129 Options.store(Opts, "ValuesOnly", ValuesOnly);
130 }
131
132 void PassByValueCheck::registerMatchers(MatchFinder *Finder) {
133 // Only register the matchers for C++; the functionality currently does not
134 // provide any benefit to other languages, despite being benign.
135 if (!getLangOpts().CPlusPlus)
136 return;
137
138 Finder->addMatcher(
139 cxxConstructorDecl(
140 forEachConstructorInitializer(
141 cxxCtorInitializer(
142 unless(isBaseInitializer()),
143 // Clang builds a CXXConstructExpr only when it knows which
144 // constructor will be called. In dependent contexts a
145 // ParenListExpr is generated instead of a CXXConstructExpr,
146 // filtering out templates automatically for us.
147 withInitializer(cxxConstructExpr(
148 has(ignoringParenImpCasts(declRefExpr(to(
149 parmVarDecl(
150 hasType(qualType(
151 // Match only const-ref or a non-const value
152 // parameters. Rvalues and const-values
153 // shouldn't be modified.
154 ValuesOnly ? nonConstValueType()
155 : anyOf(constRefType(),
156 nonConstValueType()))))
157 .bind("Param"))))),
158 hasDeclaration(cxxConstructorDecl(
159 isCopyConstructor(), unless(isDeleted()),
160 hasDeclContext(
161 cxxRecordDecl(isMoveConstructible())))))))
162 .bind("Initializer")))
163 .bind("Ctor"),
164 this);
165 }
166
167 void PassByValueCheck::registerPPCallbacks(const SourceManager &SM,
168 Preprocessor *PP,
169 Preprocessor *ModuleExpanderPP) {
170 // Only register the preprocessor callbacks for C++; the functionality
171 // currently does not provide any benefit to other languages, despite being
172 // benign.
173 if (getLangOpts().CPlusPlus) {
174 Inserter = std::make_unique<utils::IncludeInserter>(SM, getLangOpts(),
175 IncludeStyle);
176 PP->addPPCallbacks(Inserter->CreatePPCallbacks());
177 }
178 }
179
180 void PassByValueCheck::check(const MatchFinder::MatchResult &Result) {
181 const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("Ctor");
182 const auto *ParamDecl = Result.Nodes.getNodeAs<ParmVarDecl>("Param");
183 const auto *Initializer =
184 Result.Nodes.getNodeAs<CXXCtorInitializer>("Initializer");
185 SourceManager &SM = *Result.SourceManager;
186
187 // If the parameter is used or anything other than the copy, do not apply
188 // the changes.
189 if (!paramReferredExactlyOnce(Ctor, ParamDecl))
190 return;
191
192 // If the parameter is trivial to copy, don't move it. Moving a trivivally
193 // copyable type will cause a problem with performance-move-const-arg
194 if (ParamDecl->getType().getNonReferenceType().isTriviallyCopyableType(
195 *Result.Context))
196 return;
197
198 auto Diag = diag(ParamDecl->getBeginLoc(), "pass by value and use std::move");
199
200 // Iterate over all declarations of the constructor.
201 for (const ParmVarDecl *ParmDecl : collectParamDecls(Ctor, ParamDecl)) {
202 auto ParamTL = ParmDecl->getTypeSourceInfo()->getTypeLoc();
203 auto RefTL = ParamTL.getAs<ReferenceTypeLoc>();
204
205 // Do not replace if it is already a value, skip.
206 if (RefTL.isNull())
207 continue;
208
209 TypeLoc ValueTL = RefTL.getPointeeLoc();
210 auto TypeRange = CharSourceRange::getTokenRange(ParmDecl->getBeginLoc(),
211 ParamTL.getEndLoc());
212 std::string ValueStr = Lexer::getSourceText(CharSourceRange::getTokenRange(
213 ValueTL.getSourceRange()),
214 SM, getLangOpts())
215 .str();
216 ValueStr += ' ';
217 Diag << FixItHint::CreateReplacement(TypeRange, ValueStr);
218 }
219
220 // Use std::move in the initialization list.
221 Diag << FixItHint::CreateInsertion(Initializer->getRParenLoc(), ")")
222 << FixItHint::CreateInsertion(
223 Initializer->getLParenLoc().getLocWithOffset(1), "std::move(");
224
225 if (auto IncludeFixit = Inserter->CreateIncludeInsertion(
226 Result.SourceManager->getFileID(Initializer->getSourceLocation()),
227 "utility",
228 /*IsAngled=*/true)) {
229 Diag << *IncludeFixit;
230 }
231 }
232
233 } // namespace modernize
234 } // namespace tidy
235 } // namespace clang
clang::ast_matchers
Definition: AbseilMatcher.h:14
clang::tidy::modernize::paramReferredExactlyOnce
static bool paramReferredExactlyOnce(const CXXConstructorDecl *Ctor, const ParmVarDecl *ParamDecl)
Whether or not ParamDecl is used exactly one time in Ctor.
Definition: PassByValueCheck.cpp:60
llvm
Some operations such as code completion produce a set of candidates.
Definition: YAMLGenerator.cpp:28
clang::tidy::modernize::constRefType
static TypeMatcher constRefType()
Definition: PassByValueCheck.cpp:49
clang::tidy::modernize::PassByValueCheck::registerPPCallbacks
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
Override this to register PPCallbacks in the preprocessor.
Definition: PassByValueCheck.cpp:167
clang::tidy::utils::IncludeSorter::toString
static StringRef toString(IncludeStyle Style)
Converts IncludeStyle to string representation.
Definition: IncludeSorter.cpp:283
clang::ast_matchers::AST_MATCHER
AST_MATCHER(Expr, isMacroID)
Definition: PreferIsaOrDynCastInConditionalsCheck.cpp:18
clang::tidy::modernize::nonConstValueType
static TypeMatcher nonConstValueType()
Definition: PassByValueCheck.cpp:53
Results
std::vector< CodeCompletionResult > Results
Definition: CodeComplete.cpp:689
PassByValueCheck.h
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:47
clang::tidy::ClangTidyCheck::getLangOpts
const LangOptions & getLangOpts() const
Returns the language options from the context.
Definition: ClangTidyCheck.h:188
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:184
clang::tidy::ClangTidyCheck::OptionsView::store
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.
Definition: ClangTidyCheck.cpp:58
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:27
clang::tidy::ClangTidyOptions::OptionMap
std::map< std::string, std::string > OptionMap
Definition: ClangTidyOptions.h:97
clang::clangd::getTokenRange
llvm::Optional< Range > getTokenRange(const SourceManager &SM, const LangOptions &LangOpts, SourceLocation TokLoc)
Returns the taken range at TokLoc.
Definition: SourceCode.cpp:227
clang::tidy::modernize::PassByValueCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: PassByValueCheck.cpp:132
clang::tidy::modernize::collectParamDecls
static SmallVector< const ParmVarDecl *, 2 > collectParamDecls(const CXXConstructorDecl *Ctor, const ParmVarDecl *ParamDecl)
Find all references to ParamDecl across all of the redeclarations of Ctor.
Definition: PassByValueCheck.cpp:110
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::bugprone::PP
static Preprocessor * PP
Definition: BadSignalToKillThreadCheck.cpp:28
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:77
clang::tidy::modernize::PassByValueCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: PassByValueCheck.cpp:180
clang::tidy::modernize::PassByValueCheck::storeOptions
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
Definition: PassByValueCheck.cpp:126
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidyCheck.cpp:21