clang-tools 6.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
10 #include "UnnecessaryCopyInitialization.h"
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 using namespace ::clang::ast_matchers;
31 using utils::decl_ref_expr::isOnlyUsedAsConst;
32
33 void UnnecessaryCopyInitialization::registerMatchers(MatchFinder *Finder) {
34 auto ConstReference = referenceType(pointee(qualType(isConstQualified())));
35 auto ConstOrConstReference =
36 allOf(anyOf(ConstReference, isConstQualified()),
37 unless(allOf(pointerType(), unless(pointerType(pointee(
38 qualType(isConstQualified())))))));
39
40 // Match method call expressions where the `this` argument is only used as
41 // const, this will be checked in `check()` part. This returned const
42 // reference is highly likely to outlive the local const reference of the
43 // variable being declared. The assumption is that the const reference being
44 // returned either points to a global static variable or to a member of the
45 // called object.
46 auto ConstRefReturningMethodCall =
47 cxxMemberCallExpr(callee(cxxMethodDecl(returns(ConstReference))),
48 on(declRefExpr(to(varDecl().bind("objectArg")))));
49 auto ConstRefReturningFunctionCall =
50 callExpr(callee(functionDecl(returns(ConstReference))),
51 unless(callee(cxxMethodDecl())));
52
53 auto localVarCopiedFrom = [](const internal::Matcher<Expr> &CopyCtorArg) {
54 return compoundStmt(
55 forEachDescendant(
56 declStmt(
57 has(varDecl(hasLocalStorage(),
58 hasType(matchers::isExpensiveToCopy()),
59 unless(isImplicit()),
60 hasInitializer(
61 cxxConstructExpr(
62 hasDeclaration(cxxConstructorDecl(
63 isCopyConstructor())),
64 hasArgument(0, CopyCtorArg))
65 .bind("ctorCall")))
66 .bind("newVarDecl")))
67 .bind("declStmt")))
68 .bind("blockStmt");
69 };
70
71 Finder->addMatcher(localVarCopiedFrom(anyOf(ConstRefReturningFunctionCall,
72 ConstRefReturningMethodCall)),
73 this);
74
75 Finder->addMatcher(localVarCopiedFrom(declRefExpr(
76 to(varDecl(hasLocalStorage()).bind("oldVarDecl")))),
77 this);
78 }
79
80 void UnnecessaryCopyInitialization::check(
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
clang::ast_matchers
Definition: ClangTidyDiagnosticConsumer.h:27
clang::tidy::performance::UnnecessaryCopyInitialization::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: UnnecessaryCopyInitialization.cpp:33
clang::tidy::utils::decl_ref_expr::isOnlyUsedAsConst
bool isOnlyUsedAsConst(const VarDecl &Var, const Stmt &Stmt, ASTContext &Context)
Returns true if all DeclRefExpr to the variable within Stmt do not modify it.
Definition: DeclRefExprUtils.cpp:108
clang::tidy::utils::type_traits::isExpensiveToCopy
llvm::Optional< bool > isExpensiveToCopy(QualType Type, const ASTContext &Context)
Returns true if Type is expensive to copy.
Definition: TypeTraits.cpp:42
clang
Definition: AndroidTidyModule.cpp:28
clang::tidy::performance::UnnecessaryCopyInitialization::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: UnnecessaryCopyInitialization.cpp:80
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidy.cpp:416
clang::tidy::utils::fixit::changeVarDeclToConst
FixItHint changeVarDeclToConst(const VarDecl &Var)
Creates fix to make VarDecl const qualified.
Definition: FixItHintUtils.cpp:29
UnnecessaryCopyInitialization.h
clang::tidy::utils::fixit::changeVarDeclToReference
FixItHint changeVarDeclToReference(const VarDecl &Var, ASTContext &Context)
Creates fix to make VarDecl a reference by adding &.
Definition: FixItHintUtils.cpp:19