1 //===--- DeclRefExprUtils.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 "DeclRefExprUtils.h"
11 #include "Matchers.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/AST/DeclCXX.h"
14 #include "clang/ASTMatchers/ASTMatchFinder.h"
15
16 namespace clang {
17 namespace tidy {
18 namespace utils {
19 namespace decl_ref_expr {
20
21 using namespace ::clang::ast_matchers;
22 using llvm::SmallPtrSet;
23
24 namespace {
25
isSetDifferenceEmpty(const S & S1,const S & S2)26 template <typename S> bool isSetDifferenceEmpty(const S &S1, const S &S2) {
27 for (const auto &E : S1)
28 if (S2.count(E) == 0)
29 return false;
30 return true;
31 }
32
33 // Extracts all Nodes keyed by ID from Matches and inserts them into Nodes.
34 template <typename Node>
extractNodesByIdTo(ArrayRef<BoundNodes> Matches,StringRef ID,SmallPtrSet<const Node *,16> & Nodes)35 void extractNodesByIdTo(ArrayRef<BoundNodes> Matches, StringRef ID,
36 SmallPtrSet<const Node *, 16> &Nodes) {
37 for (const auto &Match : Matches)
38 Nodes.insert(Match.getNodeAs<Node>(ID));
39 }
40
41 } // namespace
42
43 // Finds all DeclRefExprs where a const method is called on VarDecl or VarDecl
44 // is the a const reference or value argument to a CallExpr or CXXConstructExpr.
45 SmallPtrSet<const DeclRefExpr *, 16>
constReferenceDeclRefExprs(const VarDecl & VarDecl,const Stmt & Stmt,ASTContext & Context)46 constReferenceDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt,
47 ASTContext &Context) {
48 auto DeclRefToVar =
49 declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind("declRef");
50 auto ConstMethodCallee = callee(cxxMethodDecl(isConst()));
51 // Match method call expressions where the variable is referenced as the this
52 // implicit object argument and opertor call expression for member operators
53 // where the variable is the 0-th argument.
54 auto Matches = match(
55 findAll(expr(anyOf(cxxMemberCallExpr(ConstMethodCallee, on(DeclRefToVar)),
56 cxxOperatorCallExpr(ConstMethodCallee,
57 hasArgument(0, DeclRefToVar))))),
58 Stmt, Context);
59 SmallPtrSet<const DeclRefExpr *, 16> DeclRefs;
60 extractNodesByIdTo(Matches, "declRef", DeclRefs);
61 auto ConstReferenceOrValue =
62 qualType(anyOf(referenceType(pointee(qualType(isConstQualified()))),
63 unless(anyOf(referenceType(), pointerType()))));
64 auto UsedAsConstRefOrValueArg = forEachArgumentWithParam(
65 DeclRefToVar, parmVarDecl(hasType(ConstReferenceOrValue)));
66 Matches = match(findAll(callExpr(UsedAsConstRefOrValueArg)), Stmt, Context);
67 extractNodesByIdTo(Matches, "declRef", DeclRefs);
68 Matches =
69 match(findAll(cxxConstructExpr(UsedAsConstRefOrValueArg)), Stmt, Context);
70 extractNodesByIdTo(Matches, "declRef", DeclRefs);
71 return DeclRefs;
72 }
73
74 // Finds all DeclRefExprs where a const method is called on VarDecl or VarDecl
75 // is the a const reference or value argument to a CallExpr or CXXConstructExpr.
76 SmallPtrSet<const DeclRefExpr *, 16>
constReferenceDeclRefExprs(const VarDecl & VarDecl,const Decl & Decl,ASTContext & Context)77 constReferenceDeclRefExprs(const VarDecl &VarDecl, const Decl &Decl,
78 ASTContext &Context) {
79 auto DeclRefToVar =
80 declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind("declRef");
81 auto ConstMethodCallee = callee(cxxMethodDecl(isConst()));
82 // Match method call expressions where the variable is referenced as the this
83 // implicit object argument and opertor call expression for member operators
84 // where the variable is the 0-th argument.
85 auto Matches =
86 match(decl(forEachDescendant(expr(
87 anyOf(cxxMemberCallExpr(ConstMethodCallee, on(DeclRefToVar)),
88 cxxOperatorCallExpr(ConstMethodCallee,
89 hasArgument(0, DeclRefToVar)))))),
90 Decl, Context);
91 SmallPtrSet<const DeclRefExpr *, 16> DeclRefs;
92 extractNodesByIdTo(Matches, "declRef", DeclRefs);
93 auto ConstReferenceOrValue =
94 qualType(anyOf(referenceType(pointee(qualType(isConstQualified()))),
95 unless(anyOf(referenceType(), pointerType()))));
96 auto UsedAsConstRefOrValueArg = forEachArgumentWithParam(
97 DeclRefToVar, parmVarDecl(hasType(ConstReferenceOrValue)));
98 Matches = match(decl(forEachDescendant(callExpr(UsedAsConstRefOrValueArg))),
99 Decl, Context);
100 extractNodesByIdTo(Matches, "declRef", DeclRefs);
101 Matches =
102 match(decl(forEachDescendant(cxxConstructExpr(UsedAsConstRefOrValueArg))),
103 Decl, Context);
104 extractNodesByIdTo(Matches, "declRef", DeclRefs);
105 return DeclRefs;
106 }
107
isOnlyUsedAsConst(const VarDecl & Var,const Stmt & Stmt,ASTContext & Context)108 bool isOnlyUsedAsConst(const VarDecl &Var, const Stmt &Stmt,
109 ASTContext &Context) {
110 // Collect all DeclRefExprs to the loop variable and all CallExprs and
111 // CXXConstructExprs where the loop variable is used as argument to a const
112 // reference parameter.
113 // If the difference is empty it is safe for the loop variable to be a const
114 // reference.
115 auto AllDeclRefs = allDeclRefExprs(Var, Stmt, Context);
116 auto ConstReferenceDeclRefs = constReferenceDeclRefExprs(Var, Stmt, Context);
117 return isSetDifferenceEmpty(AllDeclRefs, ConstReferenceDeclRefs);
118 }
119
120 SmallPtrSet<const DeclRefExpr *, 16>
allDeclRefExprs(const VarDecl & VarDecl,const Stmt & Stmt,ASTContext & Context)121 allDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt, ASTContext &Context) {
122 auto Matches = match(
123 findAll(declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind("declRef")),
124 Stmt, Context);
125 SmallPtrSet<const DeclRefExpr *, 16> DeclRefs;
126 extractNodesByIdTo(Matches, "declRef", DeclRefs);
127 return DeclRefs;
128 }
129
130 SmallPtrSet<const DeclRefExpr *, 16>
allDeclRefExprs(const VarDecl & VarDecl,const Decl & Decl,ASTContext & Context)131 allDeclRefExprs(const VarDecl &VarDecl, const Decl &Decl, ASTContext &Context) {
132 auto Matches = match(
133 decl(forEachDescendant(
134 declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind("declRef"))),
135 Decl, Context);
136 SmallPtrSet<const DeclRefExpr *, 16> DeclRefs;
137 extractNodesByIdTo(Matches, "declRef", DeclRefs);
138 return DeclRefs;
139 }
140
isCopyConstructorArgument(const DeclRefExpr & DeclRef,const Decl & Decl,ASTContext & Context)141 bool isCopyConstructorArgument(const DeclRefExpr &DeclRef, const Decl &Decl,
142 ASTContext &Context) {
143 auto UsedAsConstRefArg = forEachArgumentWithParam(
144 declRefExpr(equalsNode(&DeclRef)),
145 parmVarDecl(hasType(matchers::isReferenceToConst())));
146 auto Matches = match(
147 decl(hasDescendant(
148 cxxConstructExpr(UsedAsConstRefArg, hasDeclaration(cxxConstructorDecl(
149 isCopyConstructor())))
150 .bind("constructExpr"))),
151 Decl, Context);
152 return !Matches.empty();
153 }
154
isCopyAssignmentArgument(const DeclRefExpr & DeclRef,const Decl & Decl,ASTContext & Context)155 bool isCopyAssignmentArgument(const DeclRefExpr &DeclRef, const Decl &Decl,
156 ASTContext &Context) {
157 auto UsedAsConstRefArg = forEachArgumentWithParam(
158 declRefExpr(equalsNode(&DeclRef)),
159 parmVarDecl(hasType(matchers::isReferenceToConst())));
160 auto Matches = match(
161 decl(hasDescendant(
162 cxxOperatorCallExpr(UsedAsConstRefArg, hasOverloadedOperatorName("="),
163 callee(cxxMethodDecl(isCopyAssignmentOperator())))
164 .bind("operatorCallExpr"))),
165 Decl, Context);
166 return !Matches.empty();
167 }
168
169 } // namespace decl_ref_expr
170 } // namespace utils
171 } // namespace tidy
172 } // namespace clang
173