clang-tools  11.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 notTemplateSpecConstRefType() {
50  return lValueReferenceType(
51  pointee(unless(templateSpecializationType()), isConstQualified()));
52 }
53 
54 static TypeMatcher nonConstValueType() {
55  return qualType(unless(anyOf(referenceType(), isConstQualified())));
56 }
57 
58 /// 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  /// \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  /// 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  /// 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 /// 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(Options.getLocalOrGlobal("IncludeStyle",
124  utils::IncludeSorter::IS_LLVM)),
125  ValuesOnly(Options.get("ValuesOnly", false)) {}
126 
128  Options.store(Opts, "IncludeStyle", IncludeStyle);
129  Options.store(Opts, "ValuesOnly", ValuesOnly);
130 }
131 
132 void PassByValueCheck::registerMatchers(MatchFinder *Finder) {
133  Finder->addMatcher(
134  traverse(
135  ast_type_traits::TK_AsIs,
136  cxxConstructorDecl(
137  forEachConstructorInitializer(
138  cxxCtorInitializer(
139  unless(isBaseInitializer()),
140  // Clang builds a CXXConstructExpr only when it knows
141  // which constructor will be called. In dependent contexts
142  // a ParenListExpr is generated instead of a
143  // CXXConstructExpr, filtering out templates automatically
144  // for us.
145  withInitializer(cxxConstructExpr(
146  has(ignoringParenImpCasts(declRefExpr(to(
147  parmVarDecl(
148  hasType(qualType(
149  // Match only const-ref or a non-const
150  // value parameters. Rvalues,
151  // TemplateSpecializationValues and
152  // const-values shouldn't be modified.
153  ValuesOnly
155  : anyOf(notTemplateSpecConstRefType(),
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  Inserter = std::make_unique<utils::IncludeInserter>(SM, getLangOpts(),
171  IncludeStyle);
172  PP->addPPCallbacks(Inserter->CreatePPCallbacks());
173 }
174 
175 void PassByValueCheck::check(const MatchFinder::MatchResult &Result) {
176  const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("Ctor");
177  const auto *ParamDecl = Result.Nodes.getNodeAs<ParmVarDecl>("Param");
178  const auto *Initializer =
179  Result.Nodes.getNodeAs<CXXCtorInitializer>("Initializer");
180  SourceManager &SM = *Result.SourceManager;
181 
182  // If the parameter is used or anything other than the copy, do not apply
183  // the changes.
184  if (!paramReferredExactlyOnce(Ctor, ParamDecl))
185  return;
186 
187  // If the parameter is trivial to copy, don't move it. Moving a trivivally
188  // copyable type will cause a problem with performance-move-const-arg
189  if (ParamDecl->getType().getNonReferenceType().isTriviallyCopyableType(
190  *Result.Context))
191  return;
192 
193  auto Diag = diag(ParamDecl->getBeginLoc(), "pass by value and use std::move");
194 
195  // Iterate over all declarations of the constructor.
196  for (const ParmVarDecl *ParmDecl : collectParamDecls(Ctor, ParamDecl)) {
197  auto ParamTL = ParmDecl->getTypeSourceInfo()->getTypeLoc();
198  auto RefTL = ParamTL.getAs<ReferenceTypeLoc>();
199 
200  // Do not replace if it is already a value, skip.
201  if (RefTL.isNull())
202  continue;
203 
204  TypeLoc ValueTL = RefTL.getPointeeLoc();
205  auto TypeRange = CharSourceRange::getTokenRange(ParmDecl->getBeginLoc(),
206  ParamTL.getEndLoc());
207  std::string ValueStr = Lexer::getSourceText(CharSourceRange::getTokenRange(
208  ValueTL.getSourceRange()),
209  SM, getLangOpts())
210  .str();
211  ValueStr += ' ';
212  Diag << FixItHint::CreateReplacement(TypeRange, ValueStr);
213  }
214 
215  // Use std::move in the initialization list.
216  Diag << FixItHint::CreateInsertion(Initializer->getRParenLoc(), ")")
217  << FixItHint::CreateInsertion(
218  Initializer->getLParenLoc().getLocWithOffset(1), "std::move(")
219  << Inserter->CreateIncludeInsertion(
220  Result.SourceManager->getFileID(Initializer->getSourceLocation()),
221  "utility",
222  /*IsAngled=*/true);
223 }
224 
225 } // namespace modernize
226 } // namespace tidy
227 } // namespace clang
llvm
Some operations such as code completion produce a set of candidates.
Definition: YAMLGenerator.cpp:28
clang::tidy::modernize::nonConstValueType
static TypeMatcher nonConstValueType()
Definition: PassByValueCheck.cpp:54
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::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:114
clang::tidy::modernize::notTemplateSpecConstRefType
static TypeMatcher notTemplateSpecConstRefType()
Definition: PassByValueCheck.cpp:49
clang::tidy::ClangTidyCheck::getLangOpts
const LangOptions & getLangOpts() const
Returns the language options from the context.
Definition: ClangTidyCheck.h:475
clang::ast_matchers
Definition: AbseilMatcher.h:14
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:175
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::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::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:471
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:76
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:27
Results
std::vector< CodeCompletionResult > Results
Definition: CodeComplete.cpp:712
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Definition: ClangTidyCheck.cpp:55
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::ast_matchers::AST_MATCHER
AST_MATCHER(Expr, isMacroID)
Definition: PreferIsaOrDynCastInConditionalsCheck.cpp:19
clang::tidy::bugprone::PP
static Preprocessor * PP
Definition: BadSignalToKillThreadCheck.cpp:29
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
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::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:152
PassByValueCheck.h
clang::tidy::ClangTidyOptions::OptionMap
std::map< std::string, ClangTidyValue > OptionMap
Definition: ClangTidyOptions.h:111