clang-tools  12.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(traverse(
72  ast_type_traits::TK_AsIs,
73  cxxConstructExpr(
74  hasDeclaration(cxxConstructorDecl(
75  isCopyConstructor())),
76  hasArgument(0, CopyCtorArg))
77  .bind("ctorCall"))))
78  .bind("newVarDecl")))
79  .bind("declStmt")))
80  .bind("blockStmt");
81  };
82 
83  Finder->addMatcher(localVarCopiedFrom(anyOf(ConstRefReturningFunctionCall,
84  ConstRefReturningMethodCall)),
85  this);
86 
87  Finder->addMatcher(localVarCopiedFrom(declRefExpr(
88  to(varDecl(hasLocalStorage()).bind("oldVarDecl")))),
89  this);
90 }
91 
93  const MatchFinder::MatchResult &Result) {
94  const auto *NewVar = Result.Nodes.getNodeAs<VarDecl>("newVarDecl");
95  const auto *OldVar = Result.Nodes.getNodeAs<VarDecl>("oldVarDecl");
96  const auto *ObjectArg = Result.Nodes.getNodeAs<VarDecl>("objectArg");
97  const auto *BlockStmt = Result.Nodes.getNodeAs<Stmt>("blockStmt");
98  const auto *CtorCall = Result.Nodes.getNodeAs<CXXConstructExpr>("ctorCall");
99 
100  TraversalKindScope RAII(*Result.Context, ast_type_traits::TK_AsIs);
101 
102  // Do not propose fixes if the DeclStmt has multiple VarDecls or in macros
103  // since we cannot place them correctly.
104  bool IssueFix =
105  Result.Nodes.getNodeAs<DeclStmt>("declStmt")->isSingleDecl() &&
106  !NewVar->getLocation().isMacroID();
107 
108  // A constructor that looks like T(const T& t, bool arg = false) counts as a
109  // copy only when it is called with default arguments for the arguments after
110  // the first.
111  for (unsigned int i = 1; i < CtorCall->getNumArgs(); ++i)
112  if (!CtorCall->getArg(i)->isDefaultArgument())
113  return;
114 
115  if (OldVar == nullptr) {
116  handleCopyFromMethodReturn(*NewVar, *BlockStmt, IssueFix, ObjectArg,
117  *Result.Context);
118  } else {
119  handleCopyFromLocalVar(*NewVar, *OldVar, *BlockStmt, IssueFix,
120  *Result.Context);
121  }
122 }
123 
124 void UnnecessaryCopyInitialization::handleCopyFromMethodReturn(
125  const VarDecl &Var, const Stmt &BlockStmt, bool IssueFix,
126  const VarDecl *ObjectArg, ASTContext &Context) {
127  bool IsConstQualified = Var.getType().isConstQualified();
128  if (!IsConstQualified && !isOnlyUsedAsConst(Var, BlockStmt, Context))
129  return;
130  if (ObjectArg != nullptr &&
131  !isOnlyUsedAsConst(*ObjectArg, BlockStmt, Context))
132  return;
133 
134  auto Diagnostic =
135  diag(Var.getLocation(),
136  IsConstQualified ? "the const qualified variable %0 is "
137  "copy-constructed from a const reference; "
138  "consider making it a const reference"
139  : "the variable %0 is copy-constructed from a "
140  "const reference but is only used as const "
141  "reference; consider making it a const reference")
142  << &Var;
143  if (IssueFix)
144  recordFixes(Var, Context, Diagnostic);
145 }
146 
147 void UnnecessaryCopyInitialization::handleCopyFromLocalVar(
148  const VarDecl &NewVar, const VarDecl &OldVar, const Stmt &BlockStmt,
149  bool IssueFix, ASTContext &Context) {
150  if (!isOnlyUsedAsConst(NewVar, BlockStmt, Context) ||
151  !isOnlyUsedAsConst(OldVar, BlockStmt, Context))
152  return;
153 
154  auto Diagnostic = diag(NewVar.getLocation(),
155  "local copy %0 of the variable %1 is never modified; "
156  "consider avoiding the copy")
157  << &NewVar << &OldVar;
158  if (IssueFix)
159  recordFixes(NewVar, Context, Diagnostic);
160 }
161 
164  Options.store(Opts, "AllowedTypes",
166 }
167 
168 } // namespace performance
169 } // namespace tidy
170 } // 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
DiagnosticCallback Diagnostic
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
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.
std::map< std::string, ClangTidyValue > OptionMap
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 &.