clang-tools  9.0.0
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 
16 namespace clang {
17 namespace tidy {
18 namespace performance {
19 namespace {
20 
21 void recordFixes(const VarDecl &Var, ASTContext &Context,
22  DiagnosticBuilder &Diagnostic) {
23  Diagnostic << utils::fixit::changeVarDeclToReference(Var, Context);
24  if (!Var.getType().isLocalConstQualified())
25  Diagnostic << utils::fixit::changeVarDeclToConst(Var);
26 }
27 
28 } // namespace
29 
30 using namespace ::clang::ast_matchers;
32 
34  StringRef Name, ClangTidyContext *Context)
35  : ClangTidyCheck(Name, Context),
36  AllowedTypes(
37  utils::options::parseStringList(Options.get("AllowedTypes", ""))) {}
38 
40  auto ConstReference = referenceType(pointee(qualType(isConstQualified())));
41 
42  // Match method call expressions where the `this` argument is only used as
43  // const, this will be checked in `check()` part. This returned const
44  // reference is highly likely to outlive the local const reference of the
45  // variable being declared. The assumption is that the const reference being
46  // returned either points to a global static variable or to a member of the
47  // called object.
48  auto ConstRefReturningMethodCall =
49  cxxMemberCallExpr(callee(cxxMethodDecl(returns(ConstReference))),
50  on(declRefExpr(to(varDecl().bind("objectArg")))));
51  auto ConstRefReturningFunctionCall =
52  callExpr(callee(functionDecl(returns(ConstReference))),
53  unless(callee(cxxMethodDecl())));
54 
55  auto localVarCopiedFrom = [this](const internal::Matcher<Expr> &CopyCtorArg) {
56  return compoundStmt(
57  forEachDescendant(
58  declStmt(
59  has(varDecl(hasLocalStorage(),
60  hasType(qualType(
61  hasCanonicalType(
63  unless(hasDeclaration(namedDecl(
64  matchers::matchesAnyListedName(
65  AllowedTypes)))))),
66  unless(isImplicit()),
67  hasInitializer(
68  cxxConstructExpr(
69  hasDeclaration(cxxConstructorDecl(
70  isCopyConstructor())),
71  hasArgument(0, CopyCtorArg))
72  .bind("ctorCall")))
73  .bind("newVarDecl")))
74  .bind("declStmt")))
75  .bind("blockStmt");
76  };
77 
78  Finder->addMatcher(localVarCopiedFrom(anyOf(ConstRefReturningFunctionCall,
79  ConstRefReturningMethodCall)),
80  this);
81 
82  Finder->addMatcher(localVarCopiedFrom(declRefExpr(
83  to(varDecl(hasLocalStorage()).bind("oldVarDecl")))),
84  this);
85 }
86 
88  const MatchFinder::MatchResult &Result) {
89  const auto *NewVar = Result.Nodes.getNodeAs<VarDecl>("newVarDecl");
90  const auto *OldVar = Result.Nodes.getNodeAs<VarDecl>("oldVarDecl");
91  const auto *ObjectArg = Result.Nodes.getNodeAs<VarDecl>("objectArg");
92  const auto *BlockStmt = Result.Nodes.getNodeAs<Stmt>("blockStmt");
93  const auto *CtorCall = Result.Nodes.getNodeAs<CXXConstructExpr>("ctorCall");
94 
95  // Do not propose fixes if the DeclStmt has multiple VarDecls or in macros
96  // since we cannot place them correctly.
97  bool IssueFix =
98  Result.Nodes.getNodeAs<DeclStmt>("declStmt")->isSingleDecl() &&
99  !NewVar->getLocation().isMacroID();
100 
101  // A constructor that looks like T(const T& t, bool arg = false) counts as a
102  // copy only when it is called with default arguments for the arguments after
103  // the first.
104  for (unsigned int i = 1; i < CtorCall->getNumArgs(); ++i)
105  if (!CtorCall->getArg(i)->isDefaultArgument())
106  return;
107 
108  if (OldVar == nullptr) {
109  handleCopyFromMethodReturn(*NewVar, *BlockStmt, IssueFix, ObjectArg,
110  *Result.Context);
111  } else {
112  handleCopyFromLocalVar(*NewVar, *OldVar, *BlockStmt, IssueFix,
113  *Result.Context);
114  }
115 }
116 
117 void UnnecessaryCopyInitialization::handleCopyFromMethodReturn(
118  const VarDecl &Var, const Stmt &BlockStmt, bool IssueFix,
119  const VarDecl *ObjectArg, ASTContext &Context) {
120  bool IsConstQualified = Var.getType().isConstQualified();
121  if (!IsConstQualified && !isOnlyUsedAsConst(Var, BlockStmt, Context))
122  return;
123  if (ObjectArg != nullptr &&
124  !isOnlyUsedAsConst(*ObjectArg, BlockStmt, Context))
125  return;
126 
127  auto Diagnostic =
128  diag(Var.getLocation(),
129  IsConstQualified ? "the const qualified variable %0 is "
130  "copy-constructed from a const reference; "
131  "consider making it a const reference"
132  : "the variable %0 is copy-constructed from a "
133  "const reference but is only used as const "
134  "reference; consider making it a const reference")
135  << &Var;
136  if (IssueFix)
137  recordFixes(Var, Context, Diagnostic);
138 }
139 
140 void UnnecessaryCopyInitialization::handleCopyFromLocalVar(
141  const VarDecl &NewVar, const VarDecl &OldVar, const Stmt &BlockStmt,
142  bool IssueFix, ASTContext &Context) {
143  if (!isOnlyUsedAsConst(NewVar, BlockStmt, Context) ||
144  !isOnlyUsedAsConst(OldVar, BlockStmt, Context))
145  return;
146 
147  auto Diagnostic = diag(NewVar.getLocation(),
148  "local copy %0 of the variable %1 is never modified; "
149  "consider avoiding the copy")
150  << &NewVar << &OldVar;
151  if (IssueFix)
152  recordFixes(NewVar, Context, Diagnostic);
153 }
154 
157  Options.store(Opts, "AllowedTypes",
159 }
160 
161 } // namespace performance
162 } // namespace tidy
163 } // 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.
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
Definition: Rename.cpp:36
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.
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 &.