clang-tools  8.0.0
UnnecessaryCopyInitialization.cpp
Go to the documentation of this file.
1 //===--- UnnecessaryCopyInitialization.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 
11 
12 #include "../utils/DeclRefExprUtils.h"
13 #include "../utils/FixItHintUtils.h"
14 #include "../utils/Matchers.h"
15 #include "../utils/OptionsUtils.h"
16 
17 namespace clang {
18 namespace tidy {
19 namespace performance {
20 namespace {
21 
22 void recordFixes(const VarDecl &Var, ASTContext &Context,
23  DiagnosticBuilder &Diagnostic) {
24  Diagnostic << utils::fixit::changeVarDeclToReference(Var, Context);
25  if (!Var.getType().isLocalConstQualified())
26  Diagnostic << utils::fixit::changeVarDeclToConst(Var);
27 }
28 
29 } // namespace
30 
31 using namespace ::clang::ast_matchers;
33 
35  StringRef Name, ClangTidyContext *Context)
36  : ClangTidyCheck(Name, Context),
37  AllowedTypes(
38  utils::options::parseStringList(Options.get("AllowedTypes", ""))) {}
39 
41  auto ConstReference = referenceType(pointee(qualType(isConstQualified())));
42 
43  // Match method call expressions where the `this` argument is only used as
44  // const, this will be checked in `check()` part. This returned const
45  // reference is highly likely to outlive the local const reference of the
46  // variable being declared. The assumption is that the const reference being
47  // returned either points to a global static variable or to a member of the
48  // called object.
49  auto ConstRefReturningMethodCall =
50  cxxMemberCallExpr(callee(cxxMethodDecl(returns(ConstReference))),
51  on(declRefExpr(to(varDecl().bind("objectArg")))));
52  auto ConstRefReturningFunctionCall =
53  callExpr(callee(functionDecl(returns(ConstReference))),
54  unless(callee(cxxMethodDecl())));
55 
56  auto localVarCopiedFrom = [this](const internal::Matcher<Expr> &CopyCtorArg) {
57  return compoundStmt(
58  forEachDescendant(
59  declStmt(
60  has(varDecl(hasLocalStorage(),
61  hasType(qualType(
62  hasCanonicalType(
64  unless(hasDeclaration(namedDecl(
65  matchers::matchesAnyListedName(
66  AllowedTypes)))))),
67  unless(isImplicit()),
68  hasInitializer(
69  cxxConstructExpr(
70  hasDeclaration(cxxConstructorDecl(
71  isCopyConstructor())),
72  hasArgument(0, CopyCtorArg))
73  .bind("ctorCall")))
74  .bind("newVarDecl")))
75  .bind("declStmt")))
76  .bind("blockStmt");
77  };
78 
79  Finder->addMatcher(localVarCopiedFrom(anyOf(ConstRefReturningFunctionCall,
80  ConstRefReturningMethodCall)),
81  this);
82 
83  Finder->addMatcher(localVarCopiedFrom(declRefExpr(
84  to(varDecl(hasLocalStorage()).bind("oldVarDecl")))),
85  this);
86 }
87 
89  const MatchFinder::MatchResult &Result) {
90  const auto *NewVar = Result.Nodes.getNodeAs<VarDecl>("newVarDecl");
91  const auto *OldVar = Result.Nodes.getNodeAs<VarDecl>("oldVarDecl");
92  const auto *ObjectArg = Result.Nodes.getNodeAs<VarDecl>("objectArg");
93  const auto *BlockStmt = Result.Nodes.getNodeAs<Stmt>("blockStmt");
94  const auto *CtorCall = Result.Nodes.getNodeAs<CXXConstructExpr>("ctorCall");
95 
96  // Do not propose fixes if the DeclStmt has multiple VarDecls or in macros
97  // since we cannot place them correctly.
98  bool IssueFix =
99  Result.Nodes.getNodeAs<DeclStmt>("declStmt")->isSingleDecl() &&
100  !NewVar->getLocation().isMacroID();
101 
102  // A constructor that looks like T(const T& t, bool arg = false) counts as a
103  // copy only when it is called with default arguments for the arguments after
104  // the first.
105  for (unsigned int i = 1; i < CtorCall->getNumArgs(); ++i)
106  if (!CtorCall->getArg(i)->isDefaultArgument())
107  return;
108 
109  if (OldVar == nullptr) {
110  handleCopyFromMethodReturn(*NewVar, *BlockStmt, IssueFix, ObjectArg,
111  *Result.Context);
112  } else {
113  handleCopyFromLocalVar(*NewVar, *OldVar, *BlockStmt, IssueFix,
114  *Result.Context);
115  }
116 }
117 
118 void UnnecessaryCopyInitialization::handleCopyFromMethodReturn(
119  const VarDecl &Var, const Stmt &BlockStmt, bool IssueFix,
120  const VarDecl *ObjectArg, ASTContext &Context) {
121  bool IsConstQualified = Var.getType().isConstQualified();
122  if (!IsConstQualified && !isOnlyUsedAsConst(Var, BlockStmt, Context))
123  return;
124  if (ObjectArg != nullptr &&
125  !isOnlyUsedAsConst(*ObjectArg, BlockStmt, Context))
126  return;
127 
128  auto Diagnostic =
129  diag(Var.getLocation(),
130  IsConstQualified ? "the const qualified variable %0 is "
131  "copy-constructed from a const reference; "
132  "consider making it a const reference"
133  : "the variable %0 is copy-constructed from a "
134  "const reference but is only used as const "
135  "reference; consider making it a const reference")
136  << &Var;
137  if (IssueFix)
138  recordFixes(Var, Context, Diagnostic);
139 }
140 
141 void UnnecessaryCopyInitialization::handleCopyFromLocalVar(
142  const VarDecl &NewVar, const VarDecl &OldVar, const Stmt &BlockStmt,
143  bool IssueFix, ASTContext &Context) {
144  if (!isOnlyUsedAsConst(NewVar, BlockStmt, Context) ||
145  !isOnlyUsedAsConst(OldVar, BlockStmt, Context))
146  return;
147 
148  auto Diagnostic = diag(NewVar.getLocation(),
149  "local copy %0 of the variable %1 is never modified; "
150  "consider avoiding the copy")
151  << &NewVar << &OldVar;
152  if (IssueFix)
153  recordFixes(NewVar, Context, Diagnostic);
154 }
155 
158  Options.store(Opts, "AllowedTypes",
160 }
161 
162 } // namespace performance
163 } // namespace tidy
164 } // namespace clang
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
std::string serializeStringList(ArrayRef< std::string > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
bool isOnlyUsedAsConst(const VarDecl &Var, const Stmt &Stmt, ASTContext &Context)
Returns true if all DeclRefExpr to the variable within Stmt do not modify it.
Base class for all clang-tidy checks.
Definition: ClangTidy.h:127
std::vector< std::string > parseStringList(StringRef Option)
Parse a semicolon separated list of strings.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
llvm::Optional< bool > isExpensiveToCopy(QualType Type, const ASTContext &Context)
Returns true if Type is expensive to copy.
Definition: TypeTraits.cpp:42
static constexpr llvm::StringLiteral Name
std::map< std::string, std::string > OptionMap
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
UnnecessaryCopyInitialization(StringRef Name, ClangTidyContext *Context)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
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.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidy.cpp:438
FixItHint changeVarDeclToConst(const VarDecl &Var)
Creates fix to make VarDecl const qualified.
FixItHint changeVarDeclToReference(const VarDecl &Var, ASTContext &Context)
Creates fix to make VarDecl a reference by adding &.