clang-tools 8.0.0

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