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