clang-tools  10.0.0git
UnnecessaryCopyInitialization.cpp
Go to the documentation of this file.
1 //===--- UnnecessaryCopyInitialization.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 
10 
11 #include "../utils/DeclRefExprUtils.h"
12 #include "../utils/FixItHintUtils.h"
13 #include "../utils/Matchers.h"
14 #include "../utils/OptionsUtils.h"
15 #include "clang/Basic/Diagnostic.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  if (llvm::Optional<FixItHint> Fix = utils::fixit::addQualifierToVarDecl(
27  Var, Context, DeclSpec::TQ::TQ_const))
28  Diagnostic << *Fix;
29  }
30 }
31 
32 } // namespace
33 
34 using namespace ::clang::ast_matchers;
36 
38  StringRef Name, ClangTidyContext *Context)
39  : ClangTidyCheck(Name, Context),
40  AllowedTypes(
41  utils::options::parseStringList(Options.get("AllowedTypes", ""))) {}
42 
44  auto ConstReference = referenceType(pointee(qualType(isConstQualified())));
45 
46  // Match method call expressions where the `this` argument is only used as
47  // const, this will be checked in `check()` part. This returned const
48  // reference is highly likely to outlive the local const reference of the
49  // variable being declared. The assumption is that the const reference being
50  // returned either points to a global static variable or to a member of the
51  // called object.
52  auto ConstRefReturningMethodCall =
53  cxxMemberCallExpr(callee(cxxMethodDecl(returns(ConstReference))),
54  on(declRefExpr(to(varDecl().bind("objectArg")))));
55  auto ConstRefReturningFunctionCall =
56  callExpr(callee(functionDecl(returns(ConstReference))),
57  unless(callee(cxxMethodDecl())));
58 
59  auto localVarCopiedFrom = [this](const internal::Matcher<Expr> &CopyCtorArg) {
60  return compoundStmt(
61  forEachDescendant(
62  declStmt(
63  has(varDecl(hasLocalStorage(),
64  hasType(qualType(
65  hasCanonicalType(
67  unless(hasDeclaration(namedDecl(
68  matchers::matchesAnyListedName(
69  AllowedTypes)))))),
70  unless(isImplicit()),
71  hasInitializer(
72  cxxConstructExpr(
73  hasDeclaration(cxxConstructorDecl(
74  isCopyConstructor())),
75  hasArgument(0, CopyCtorArg))
76  .bind("ctorCall")))
77  .bind("newVarDecl")))
78  .bind("declStmt")))
79  .bind("blockStmt");
80  };
81 
82  Finder->addMatcher(localVarCopiedFrom(anyOf(ConstRefReturningFunctionCall,
83  ConstRefReturningMethodCall)),
84  this);
85 
86  Finder->addMatcher(localVarCopiedFrom(declRefExpr(
87  to(varDecl(hasLocalStorage()).bind("oldVarDecl")))),
88  this);
89 }
90 
92  const MatchFinder::MatchResult &Result) {
93  const auto *NewVar = Result.Nodes.getNodeAs<VarDecl>("newVarDecl");
94  const auto *OldVar = Result.Nodes.getNodeAs<VarDecl>("oldVarDecl");
95  const auto *ObjectArg = Result.Nodes.getNodeAs<VarDecl>("objectArg");
96  const auto *BlockStmt = Result.Nodes.getNodeAs<Stmt>("blockStmt");
97  const auto *CtorCall = Result.Nodes.getNodeAs<CXXConstructExpr>("ctorCall");
98 
99  // Do not propose fixes if the DeclStmt has multiple VarDecls or in macros
100  // since we cannot place them correctly.
101  bool IssueFix =
102  Result.Nodes.getNodeAs<DeclStmt>("declStmt")->isSingleDecl() &&
103  !NewVar->getLocation().isMacroID();
104 
105  // A constructor that looks like T(const T& t, bool arg = false) counts as a
106  // copy only when it is called with default arguments for the arguments after
107  // the first.
108  for (unsigned int i = 1; i < CtorCall->getNumArgs(); ++i)
109  if (!CtorCall->getArg(i)->isDefaultArgument())
110  return;
111 
112  if (OldVar == nullptr) {
113  handleCopyFromMethodReturn(*NewVar, *BlockStmt, IssueFix, ObjectArg,
114  *Result.Context);
115  } else {
116  handleCopyFromLocalVar(*NewVar, *OldVar, *BlockStmt, IssueFix,
117  *Result.Context);
118  }
119 }
120 
121 void UnnecessaryCopyInitialization::handleCopyFromMethodReturn(
122  const VarDecl &Var, const Stmt &BlockStmt, bool IssueFix,
123  const VarDecl *ObjectArg, ASTContext &Context) {
124  bool IsConstQualified = Var.getType().isConstQualified();
125  if (!IsConstQualified && !isOnlyUsedAsConst(Var, BlockStmt, Context))
126  return;
127  if (ObjectArg != nullptr &&
128  !isOnlyUsedAsConst(*ObjectArg, BlockStmt, Context))
129  return;
130 
131  auto Diagnostic =
132  diag(Var.getLocation(),
133  IsConstQualified ? "the const qualified variable %0 is "
134  "copy-constructed from a const reference; "
135  "consider making it a const reference"
136  : "the variable %0 is copy-constructed from a "
137  "const reference but is only used as const "
138  "reference; consider making it a const reference")
139  << &Var;
140  if (IssueFix)
141  recordFixes(Var, Context, Diagnostic);
142 }
143 
144 void UnnecessaryCopyInitialization::handleCopyFromLocalVar(
145  const VarDecl &NewVar, const VarDecl &OldVar, const Stmt &BlockStmt,
146  bool IssueFix, ASTContext &Context) {
147  if (!isOnlyUsedAsConst(NewVar, BlockStmt, Context) ||
148  !isOnlyUsedAsConst(OldVar, BlockStmt, Context))
149  return;
150 
151  auto Diagnostic = diag(NewVar.getLocation(),
152  "local copy %0 of the variable %1 is never modified; "
153  "consider avoiding the copy")
154  << &NewVar << &OldVar;
155  if (IssueFix)
156  recordFixes(NewVar, Context, Diagnostic);
157 }
158 
161  Options.store(Opts, "AllowedTypes",
163 }
164 
165 } // namespace performance
166 } // namespace tidy
167 } // namespace clang
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.
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:41
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.
static constexpr llvm::StringLiteral Name
std::map< std::string, std::string > OptionMap
UnnecessaryCopyInitialization(StringRef Name, ClangTidyContext *Context)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
static cl::opt< bool > Fix("fix", cl::desc(R"( Apply suggested fixes. Without -fix-errors clang-tidy will bail out if any compilation errors were found. )"), cl::init(false), cl::cat(ClangTidyCategory))
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Optional< FixItHint > addQualifierToVarDecl(const VarDecl &Var, const ASTContext &Context, DeclSpec::TQ Qualifier, QualifierTarget QualTarget, QualifierPolicy QualPolicy)
Creates fix to qualify VarDecl with the specified Qualifier.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
FixItHint changeVarDeclToReference(const VarDecl &Var, ASTContext &Context)
Creates fix to make VarDecl a reference by adding &.