clang-tools 6.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 /// \brief 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
48 static TypeMatcher constRefType() {
49 return lValueReferenceType(pointee(isConstQualified()));
50 }
51
52 static TypeMatcher nonConstValueType() {
53 return qualType(unless(anyOf(referenceType(), isConstQualified())));
54 }
55
56 /// \brief Whether or not \p ParamDecl is used exactly one time in \p Ctor.
57 ///
58 /// Checks both in the init-list and the body of the constructor.
59 static bool paramReferredExactlyOnce(const CXXConstructorDecl *Ctor,
60 const ParmVarDecl *ParamDecl) {
61 /// \brief \c clang::RecursiveASTVisitor that checks that the given
62 /// \c ParmVarDecl is used exactly one time.
63 ///
64 /// \see ExactlyOneUsageVisitor::hasExactlyOneUsageIn()
65 class ExactlyOneUsageVisitor
66 : public RecursiveASTVisitor<ExactlyOneUsageVisitor> {
67 friend class RecursiveASTVisitor<ExactlyOneUsageVisitor>;
68
69 public:
70 ExactlyOneUsageVisitor(const ParmVarDecl *ParamDecl)
71 : ParamDecl(ParamDecl) {}
72
73 /// \brief Whether or not the parameter variable is referred only once in
74 /// the
75 /// given constructor.
76 bool hasExactlyOneUsageIn(const CXXConstructorDecl *Ctor) {
77 Count = 0;
78 TraverseDecl(const_cast<CXXConstructorDecl *>(Ctor));
79 return Count == 1;
80 }
81
82 private:
83 /// \brief Counts the number of references to a variable.
84 ///
85 /// Stops the AST traversal if more than one usage is found.
86 bool VisitDeclRefExpr(DeclRefExpr *D) {
87 if (const ParmVarDecl *To = dyn_cast<ParmVarDecl>(D->getDecl())) {
88 if (To == ParamDecl) {
89 ++Count;
90 if (Count > 1) {
91 // No need to look further, used more than once.
92 return false;
93 }
94 }
95 }
96 return true;
97 }
98
99 const ParmVarDecl *ParamDecl;
100 unsigned Count;
101 };
102
103 return ExactlyOneUsageVisitor(ParamDecl).hasExactlyOneUsageIn(Ctor);
104 }
105
106 /// \brief Find all references to \p ParamDecl across all of the
107 /// redeclarations of \p Ctor.
108 static SmallVector<const ParmVarDecl *, 2>
109 collectParamDecls(const CXXConstructorDecl *Ctor,
110 const ParmVarDecl *ParamDecl) {
111 SmallVector<const ParmVarDecl *, 2> Results;
112 unsigned ParamIdx = ParamDecl->getFunctionScopeIndex();
113
114 for (const FunctionDecl *Redecl : Ctor->redecls())
115 Results.push_back(Redecl->getParamDecl(ParamIdx));
116 return Results;
117 }
118
119 PassByValueCheck::PassByValueCheck(StringRef Name, ClangTidyContext *Context)
120 : ClangTidyCheck(Name, Context),
121 IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
122 Options.getLocalOrGlobal("IncludeStyle", "llvm"))),
123 ValuesOnly(Options.get("ValuesOnly", 0) != 0) {}
124
125 void PassByValueCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
126 Options.store(Opts, "IncludeStyle",
127 utils::IncludeSorter::toString(IncludeStyle));
128 Options.store(Opts, "ValuesOnly", ValuesOnly);
129 }
130
131 void PassByValueCheck::registerMatchers(MatchFinder *Finder) {
132 // Only register the matchers for C++; the functionality currently does not
133 // provide any benefit to other languages, despite being benign.
134 if (!getLangOpts().CPlusPlus)
135 return;
136
137 Finder->addMatcher(
138 cxxConstructorDecl(
139 forEachConstructorInitializer(
140 cxxCtorInitializer(
141 unless(isBaseInitializer()),
142 // Clang builds a CXXConstructExpr only when it knows which
143 // constructor will be called. In dependent contexts a
144 // ParenListExpr is generated instead of a CXXConstructExpr,
145 // filtering out templates automatically for us.
146 withInitializer(cxxConstructExpr(
147 has(ignoringParenImpCasts(declRefExpr(to(
148 parmVarDecl(
149 hasType(qualType(
150 // Match only const-ref or a non-const value
151 // parameters. Rvalues and const-values
152 // shouldn't be modified.
153 ValuesOnly ? nonConstValueType()
154 : anyOf(constRefType(),
155 nonConstValueType()))))
156 .bind("Param"))))),
157 hasDeclaration(cxxConstructorDecl(
158 isCopyConstructor(), unless(isDeleted()),
159 hasDeclContext(
160 cxxRecordDecl(isMoveConstructible())))))))
161 .bind("Initializer")))
162 .bind("Ctor"),
163 this);
164 }
165
166 void PassByValueCheck::registerPPCallbacks(CompilerInstance &Compiler) {
167 // Only register the preprocessor callbacks for C++; the functionality
168 // currently does not provide any benefit to other languages, despite being
169 // benign.
170 if (getLangOpts().CPlusPlus) {
171 Inserter.reset(new utils::IncludeInserter(
172 Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle));
173 Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
174 }
175 }
176
177 void PassByValueCheck::check(const MatchFinder::MatchResult &Result) {
178 const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("Ctor");
179 const auto *ParamDecl = Result.Nodes.getNodeAs<ParmVarDecl>("Param");
180 const auto *Initializer =
181 Result.Nodes.getNodeAs<CXXCtorInitializer>("Initializer");
182 SourceManager &SM = *Result.SourceManager;
183
184 // If the parameter is used or anything other than the copy, do not apply
185 // the changes.
186 if (!paramReferredExactlyOnce(Ctor, ParamDecl))
187 return;
188
189 // If the parameter is trivial to copy, don't move it. Moving a trivivally
190 // copyable type will cause a problem with performance-move-const-arg
191 if (ParamDecl->getType().getNonReferenceType().isTriviallyCopyableType(
192 *Result.Context))
193 return;
194
195 auto Diag = diag(ParamDecl->getLocStart(), "pass by value and use std::move");
196
197 // Iterate over all declarations of the constructor.
198 for (const ParmVarDecl *ParmDecl : collectParamDecls(Ctor, ParamDecl)) {
199 auto ParamTL = ParmDecl->getTypeSourceInfo()->getTypeLoc();
200 auto RefTL = ParamTL.getAs<ReferenceTypeLoc>();
201
202 // Do not replace if it is already a value, skip.
203 if (RefTL.isNull())
204 continue;
205
206 TypeLoc ValueTL = RefTL.getPointeeLoc();
207 auto TypeRange = CharSourceRange::getTokenRange(ParmDecl->getLocStart(),
208 ParamTL.getLocEnd());
209 std::string ValueStr = Lexer::getSourceText(CharSourceRange::getTokenRange(
210 ValueTL.getSourceRange()),
211 SM, getLangOpts())
212 .str();
213 ValueStr += ' ';
214 Diag << FixItHint::CreateReplacement(TypeRange, ValueStr);
215 }
216
217 // Use std::move in the initialization list.
218 Diag << FixItHint::CreateInsertion(Initializer->getRParenLoc(), ")")
219 << FixItHint::CreateInsertion(
220 Initializer->getLParenLoc().getLocWithOffset(1), "std::move(");
221
222 if (auto IncludeFixit = Inserter->CreateIncludeInsertion(
223 Result.SourceManager->getFileID(Initializer->getSourceLocation()),
224 "utility",
225 /*IsAngled=*/true)) {
226 Diag << *IncludeFixit;
227 }
228 }
229
230 } // namespace modernize
231 } // namespace tidy
232 } // namespace clang
clang::ast_matchers
Definition: ClangTidyDiagnosticConsumer.h:27
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:59
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:449
llvm
Definition: ClangTidyOptions.cpp:31
clang::tidy::modernize::constRefType
static TypeMatcher constRefType()
Definition: PassByValueCheck.cpp:48
Name
StringHandle Name
Definition: PreprocessorTracker.cpp:525
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:52
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:166
clang::tidy::ClangTidyOptions::OptionMap
std::map< std::string, std::string > OptionMap
Definition: ClangTidyOptions.h:99
clang::tidy::modernize::PassByValueCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: PassByValueCheck.cpp:131
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:109
clang
Definition: AndroidTidyModule.cpp:28
clang::tidy::utils::IncludeInserter
Produces fixes to insert specified includes to source files, if not yet present.
Definition: IncludeInserter.h:55
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:104
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:177
clang::ast_matchers::AST_MATCHER
AST_MATCHER(VarDecl, isAsm)
Definition: NoAssemblerCheck.cpp:18
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:125
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:416