clang-tools  3.9.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 
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 
31 using namespace ::clang::ast_matchers;
33 
35  auto ConstReference = referenceType(pointee(qualType(isConstQualified())));
36  auto ConstOrConstReference =
37  allOf(anyOf(ConstReference, isConstQualified()),
38  unless(allOf(pointerType(), unless(pointerType(pointee(
39  qualType(isConstQualified())))))));
40 
41  // Match method call expressions where the `this` argument is only used as
42  // const, this will be checked in `check()` part. This returned const
43  // reference is highly likely to outlive the local const reference of the
44  // variable being declared. The assumption is that the const reference being
45  // returned either points to a global static variable or to a member of the
46  // called object.
47  auto ConstRefReturningMethodCall = cxxMemberCallExpr(
48  callee(cxxMethodDecl(returns(ConstReference))),
49  on(declRefExpr(to(varDecl().bind("objectArg")))));
50  auto ConstRefReturningFunctionCall =
51  callExpr(callee(functionDecl(returns(ConstReference))),
52  unless(callee(cxxMethodDecl())));
53 
54  auto localVarCopiedFrom = [](const internal::Matcher<Expr> &CopyCtorArg) {
55  return compoundStmt(
56  forEachDescendant(
57  declStmt(
58  has(varDecl(hasLocalStorage(),
59  hasType(matchers::isExpensiveToCopy()),
60  hasInitializer(
61  cxxConstructExpr(
62  hasDeclaration(cxxConstructorDecl(
63  isCopyConstructor())),
64  hasArgument(0, CopyCtorArg))
65  .bind("ctorCall")))
66  .bind("newVarDecl"))).bind("declStmt")))
67  .bind("blockStmt");
68  };
69 
70  Finder->addMatcher(
71  localVarCopiedFrom(anyOf(ConstRefReturningFunctionCall,
72  ConstRefReturningMethodCall)),
73  this);
74 
75  Finder->addMatcher(localVarCopiedFrom(declRefExpr(
76  to(varDecl(hasLocalStorage()).bind("oldVarDecl")))),
77  this);
78 }
79 
81  const MatchFinder::MatchResult &Result) {
82  const auto *NewVar = Result.Nodes.getNodeAs<VarDecl>("newVarDecl");
83  const auto *OldVar = Result.Nodes.getNodeAs<VarDecl>("oldVarDecl");
84  const auto *ObjectArg = Result.Nodes.getNodeAs<VarDecl>("objectArg");
85  const auto *BlockStmt = Result.Nodes.getNodeAs<Stmt>("blockStmt");
86  const auto *CtorCall = Result.Nodes.getNodeAs<CXXConstructExpr>("ctorCall");
87  // Do not propose fixes if the DeclStmt has multiple VarDecls or in macros
88  // since we cannot place them correctly.
89  bool IssueFix =
90  Result.Nodes.getNodeAs<DeclStmt>("declStmt")->isSingleDecl() &&
91  !NewVar->getLocation().isMacroID();
92 
93  // A constructor that looks like T(const T& t, bool arg = false) counts as a
94  // copy only when it is called with default arguments for the arguments after
95  // the first.
96  for (unsigned int i = 1; i < CtorCall->getNumArgs(); ++i)
97  if (!CtorCall->getArg(i)->isDefaultArgument())
98  return;
99 
100  if (OldVar == nullptr) {
101  handleCopyFromMethodReturn(*NewVar, *BlockStmt, IssueFix, ObjectArg,
102  *Result.Context);
103  } else {
104  handleCopyFromLocalVar(*NewVar, *OldVar, *BlockStmt, IssueFix,
105  *Result.Context);
106  }
107 }
108 
109 void UnnecessaryCopyInitialization::handleCopyFromMethodReturn(
110  const VarDecl &Var, const Stmt &BlockStmt, bool IssueFix,
111  const VarDecl *ObjectArg, ASTContext &Context) {
112  bool IsConstQualified = Var.getType().isConstQualified();
113  if (!IsConstQualified && !isOnlyUsedAsConst(Var, BlockStmt, Context))
114  return;
115  if (ObjectArg != nullptr &&
116  !isOnlyUsedAsConst(*ObjectArg, BlockStmt, Context))
117  return;
118 
119  auto Diagnostic =
120  diag(Var.getLocation(),
121  IsConstQualified ? "the const qualified variable %0 is "
122  "copy-constructed from a const reference; "
123  "consider making it a const reference"
124  : "the variable %0 is copy-constructed from a "
125  "const reference but is only used as const "
126  "reference; consider making it a const reference")
127  << &Var;
128  if (IssueFix)
129  recordFixes(Var, Context, Diagnostic);
130 }
131 
132 void UnnecessaryCopyInitialization::handleCopyFromLocalVar(
133  const VarDecl &NewVar, const VarDecl &OldVar, const Stmt &BlockStmt,
134  bool IssueFix, ASTContext &Context) {
135  if (!isOnlyUsedAsConst(NewVar, BlockStmt, Context) ||
136  !isOnlyUsedAsConst(OldVar, BlockStmt, Context))
137  return;
138 
139  auto Diagnostic = diag(NewVar.getLocation(),
140  "local copy %0 of the variable %1 is never modified; "
141  "consider avoiding the copy")
142  << &NewVar << &OldVar;
143  if (IssueFix)
144  recordFixes(NewVar, Context, Diagnostic);
145 }
146 
147 } // namespace performance
148 } // namespace tidy
149 } // namespace clang
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:210
bool isOnlyUsedAsConst(const VarDecl &Var, const Stmt &Stmt, ASTContext &Context)
Returns true if all DeclRefExpr to the variable within Stmt do not modify it.
llvm::Optional< bool > isExpensiveToCopy(QualType Type, const ASTContext &Context)
Returns true if Type is expensive to copy.
Definition: TypeTraits.cpp:42
ClangTidyContext & Context
Definition: ClangTidy.cpp:93
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's name.
Definition: ClangTidy.cpp:352
FixItHint changeVarDeclToConst(const VarDecl &Var)
Creates fix to make VarDecl const qualified.
const NamedDecl * Result
Definition: USRFinder.cpp:137
FixItHint changeVarDeclToReference(const VarDecl &Var, ASTContext &Context)
Creates fix to make VarDecl a reference by adding &.