clang-tools  3.8.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(IncludeSorter::parseIncludeStyle(
122  Options.get("IncludeStyle", "llvm"))) {}
123 
125  Options.store(Opts, "IncludeStyle", IncludeSorter::toString(IncludeStyle));
126 }
127 
129  // Only register the matchers for C++; the functionality currently does not
130  // provide any benefit to other languages, despite being benign.
131  if (getLangOpts().CPlusPlus) {
132  Finder->addMatcher(
133  cxxConstructorDecl(
134  forEachConstructorInitializer(
135  cxxCtorInitializer(
136  // Clang builds a CXXConstructExpr only whin it knows which
137  // constructor will be called. In dependent contexts a
138  // ParenListExpr is generated instead of a CXXConstructExpr,
139  // filtering out templates automatically for us.
140  withInitializer(cxxConstructExpr(
141  has(declRefExpr(to(
142  parmVarDecl(
143  hasType(qualType(
144  // Match only const-ref or a non-const value
145  // parameters. Rvalues and const-values
146  // shouldn't be modified.
147  anyOf(constRefType(),
148  nonConstValueType()))))
149  .bind("Param")))),
150  hasDeclaration(cxxConstructorDecl(
151  isCopyConstructor(), unless(isDeleted()),
152  hasDeclContext(
153  cxxRecordDecl(isMoveConstructible())))))))
154  .bind("Initializer")))
155  .bind("Ctor"),
156  this);
157  }
158 }
159 
160 void PassByValueCheck::registerPPCallbacks(CompilerInstance &Compiler) {
161  // Only register the preprocessor callbacks for C++; the functionality
162  // currently does not provide any benefit to other languages, despite being
163  // benign.
164  if (getLangOpts().CPlusPlus) {
165  Inserter.reset(new IncludeInserter(Compiler.getSourceManager(),
166  Compiler.getLangOpts(), IncludeStyle));
167  Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
168  }
169 }
170 
171 void PassByValueCheck::check(const MatchFinder::MatchResult &Result) {
172  const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("Ctor");
173  const auto *ParamDecl = Result.Nodes.getNodeAs<ParmVarDecl>("Param");
174  const auto *Initializer =
175  Result.Nodes.getNodeAs<CXXCtorInitializer>("Initializer");
176  SourceManager &SM = *Result.SourceManager;
177 
178  // If the parameter is used or anything other than the copy, do not apply
179  // the changes.
180  if (!paramReferredExactlyOnce(Ctor, ParamDecl))
181  return;
182 
183  auto Diag = diag(ParamDecl->getLocStart(), "pass by value and use std::move");
184 
185  // Iterate over all declarations of the constructor.
186  for (const ParmVarDecl *ParmDecl : collectParamDecls(Ctor, ParamDecl)) {
187  auto ParamTL = ParmDecl->getTypeSourceInfo()->getTypeLoc();
188  auto RefTL = ParamTL.getAs<ReferenceTypeLoc>();
189 
190  // Do not replace if it is already a value, skip.
191  if (RefTL.isNull())
192  continue;
193 
194  TypeLoc ValueTL = RefTL.getPointeeLoc();
195  auto TypeRange = CharSourceRange::getTokenRange(ParmDecl->getLocStart(),
196  ParamTL.getLocEnd());
197  std::string ValueStr =
198  Lexer::getSourceText(
199  CharSourceRange::getTokenRange(ValueTL.getSourceRange()), SM,
200  Result.Context->getLangOpts())
201  .str();
202  ValueStr += ' ';
203  Diag << FixItHint::CreateReplacement(TypeRange, ValueStr);
204  }
205 
206  // Use std::move in the initialization list.
207  Diag << FixItHint::CreateInsertion(Initializer->getRParenLoc(), ")")
208  << FixItHint::CreateInsertion(
209  Initializer->getLParenLoc().getLocWithOffset(1), "std::move(");
210 
211  auto Insertion =
212  Inserter->CreateIncludeInsertion(SM.getMainFileID(), "utility",
213  /*IsAngled=*/true);
214  if (Insertion.hasValue())
215  Diag << Insertion.getValue();
216 }
217 
218 } // namespace modernize
219 } // namespace tidy
220 } // namespace clang
static bool paramReferredExactlyOnce(const CXXConstructorDecl *Ctor, const ParmVarDecl *ParamDecl)
Whether or not ParamDecl is used exactly one time in Ctor.
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:162
static TypeMatcher constRefType()
StringHandle Name
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:188
static TypeMatcher nonConstValueType()
AST_MATCHER(CXXRecordDecl, isMoveConstructible)
Matches move-constructible classes.
static StringRef toString(IncludeStyle Style)
Base class for all clang-tidy checks.
Definition: ClangTidy.h:102
SourceManager & SM
void registerPPCallbacks(clang::CompilerInstance &Compiler) override
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:344
std::map< std::string, std::string > OptionMap
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register ASTMatchers with Finder.
static SmallVector< const ParmVarDecl *, 2 > collectParamDecls(const CXXConstructorDecl *Ctor, const ParmVarDecl *ParamDecl)
Find all references to ParamDecl across all of the redeclarations of Ctor.
ClangTidyContext & Context
Definition: ClangTidy.cpp:93
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 storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Definition: ClangTidy.cpp:323
const NamedDecl * Result
Definition: USRFinder.cpp:121