blob: f4be35b3219716519d7f6d57576cb5baa8c8634b [file] [log] [blame]
Shuai Wange9192f82018-09-11 21:13:20 +00001//===---------- ExprMutationAnalyzer.cpp ----------------------------------===//
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#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
10#include "clang/ASTMatchers/ASTMatchFinder.h"
11#include "llvm/ADT/STLExtras.h"
12
13namespace clang {
14using namespace ast_matchers;
15
16namespace {
17
18AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) {
19 return llvm::is_contained(Node.capture_inits(), E);
20}
21
22AST_MATCHER_P(CXXForRangeStmt, hasRangeStmt,
23 ast_matchers::internal::Matcher<DeclStmt>, InnerMatcher) {
24 const DeclStmt *const Range = Node.getRangeStmt();
25 return InnerMatcher.matches(*Range, Finder, Builder);
26}
27
28const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXTypeidExpr>
29 cxxTypeidExpr;
30
31AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) {
32 return Node.isPotentiallyEvaluated();
33}
34
35const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXNoexceptExpr>
36 cxxNoexceptExpr;
37
38const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt,
39 GenericSelectionExpr>
40 genericSelectionExpr;
41
42AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr,
43 ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
44 return InnerMatcher.matches(*Node.getControllingExpr(), Finder, Builder);
45}
46
47const auto nonConstReferenceType = [] {
48 return hasUnqualifiedDesugaredType(
49 referenceType(pointee(unless(isConstQualified()))));
50};
51
52const auto nonConstPointerType = [] {
53 return hasUnqualifiedDesugaredType(
54 pointerType(pointee(unless(isConstQualified()))));
55};
56
57const auto isMoveOnly = [] {
58 return cxxRecordDecl(
59 hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted()))),
60 hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted()))),
61 unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(),
62 unless(isDeleted()))),
63 hasMethod(cxxMethodDecl(isCopyAssignmentOperator(),
64 unless(isDeleted()))))));
65};
66
67} // namespace
68
69const Stmt *ExprMutationAnalyzer::findMutation(const Expr *Exp) {
70 const auto Memoized = Results.find(Exp);
71 if (Memoized != Results.end())
72 return Memoized->second;
73
74 if (isUnevaluated(Exp))
75 return Results[Exp] = nullptr;
76
77 for (const auto &Finder : {&ExprMutationAnalyzer::findDirectMutation,
78 &ExprMutationAnalyzer::findMemberMutation,
79 &ExprMutationAnalyzer::findArrayElementMutation,
80 &ExprMutationAnalyzer::findCastMutation,
81 &ExprMutationAnalyzer::findRangeLoopMutation,
82 &ExprMutationAnalyzer::findReferenceMutation}) {
83 if (const Stmt *S = (this->*Finder)(Exp))
84 return Results[Exp] = S;
85 }
86
87 return Results[Exp] = nullptr;
88}
89
90bool ExprMutationAnalyzer::isUnevaluated(const Expr *Exp) {
91 return selectFirst<Expr>(
92 "expr",
93 match(
94 findAll(
95 expr(equalsNode(Exp),
96 anyOf(
97 // `Exp` is part of the underlying expression of
98 // decltype/typeof if it has an ancestor of
99 // typeLoc.
100 hasAncestor(typeLoc(unless(
101 hasAncestor(unaryExprOrTypeTraitExpr())))),
102 hasAncestor(expr(anyOf(
103 // `UnaryExprOrTypeTraitExpr` is unevaluated
104 // unless it's sizeof on VLA.
105 unaryExprOrTypeTraitExpr(unless(sizeOfExpr(
106 hasArgumentOfType(variableArrayType())))),
107 // `CXXTypeidExpr` is unevaluated unless it's
108 // applied to an expression of glvalue of
109 // polymorphic class type.
110 cxxTypeidExpr(
111 unless(isPotentiallyEvaluated())),
112 // The controlling expression of
113 // `GenericSelectionExpr` is unevaluated.
114 genericSelectionExpr(hasControllingExpr(
115 hasDescendant(equalsNode(Exp)))),
116 cxxNoexceptExpr())))))
117 .bind("expr")),
118 Stm, Context)) != nullptr;
119}
120
121const Stmt *
122ExprMutationAnalyzer::findExprMutation(ArrayRef<BoundNodes> Matches) {
123 for (const auto &Nodes : Matches) {
124 if (const Stmt *S = findMutation(Nodes.getNodeAs<Expr>("expr")))
125 return S;
126 }
127 return nullptr;
128}
129
130const Stmt *
131ExprMutationAnalyzer::findDeclMutation(ArrayRef<BoundNodes> Matches) {
132 for (const auto &DeclNodes : Matches) {
133 if (const Stmt *S = findDeclMutation(DeclNodes.getNodeAs<Decl>("decl")))
134 return S;
135 }
136 return nullptr;
137}
138
139const Stmt *ExprMutationAnalyzer::findDeclMutation(const Decl *Dec) {
140 const auto Refs = match(
141 findAll(declRefExpr(to(equalsNode(Dec))).bind("expr")), Stm, Context);
142 for (const auto &RefNodes : Refs) {
143 const auto *E = RefNodes.getNodeAs<Expr>("expr");
144 if (findMutation(E))
145 return E;
146 }
147 return nullptr;
148}
149
150const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) {
151 // LHS of any assignment operators.
152 const auto AsAssignmentLhs =
153 binaryOperator(isAssignmentOperator(), hasLHS(equalsNode(Exp)));
154
155 // Operand of increment/decrement operators.
156 const auto AsIncDecOperand =
157 unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")),
158 hasUnaryOperand(equalsNode(Exp)));
159
160 // Invoking non-const member function.
161 // A member function is assumed to be non-const when it is unresolved.
162 const auto NonConstMethod = cxxMethodDecl(unless(isConst()));
163 const auto AsNonConstThis =
164 expr(anyOf(cxxMemberCallExpr(callee(NonConstMethod), on(equalsNode(Exp))),
165 cxxOperatorCallExpr(callee(NonConstMethod),
166 hasArgument(0, equalsNode(Exp))),
167 callExpr(callee(expr(anyOf(
168 unresolvedMemberExpr(hasObjectExpression(equalsNode(Exp))),
169 cxxDependentScopeMemberExpr(
170 hasObjectExpression(equalsNode(Exp)))))))));
171
172 // Taking address of 'Exp'.
173 // We're assuming 'Exp' is mutated as soon as its address is taken, though in
174 // theory we can follow the pointer and see whether it escaped `Stm` or is
175 // dereferenced and then mutated. This is left for future improvements.
176 const auto AsAmpersandOperand =
177 unaryOperator(hasOperatorName("&"),
178 // A NoOp implicit cast is adding const.
179 unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))),
180 hasUnaryOperand(equalsNode(Exp)));
181 const auto AsPointerFromArrayDecay =
182 castExpr(hasCastKind(CK_ArrayToPointerDecay),
183 unless(hasParent(arraySubscriptExpr())), has(equalsNode(Exp)));
184 // Treat calling `operator->()` of move-only classes as taking address.
185 // These are typically smart pointers with unique ownership so we treat
186 // mutation of pointee as mutation of the smart pointer itself.
187 const auto AsOperatorArrowThis =
188 cxxOperatorCallExpr(hasOverloadedOperatorName("->"),
189 callee(cxxMethodDecl(ofClass(isMoveOnly()),
190 returns(nonConstPointerType()))),
191 argumentCountIs(1), hasArgument(0, equalsNode(Exp)));
192
193 // Used as non-const-ref argument when calling a function.
194 // An argument is assumed to be non-const-ref when the function is unresolved.
195 const auto NonConstRefParam = forEachArgumentWithParam(
196 equalsNode(Exp), parmVarDecl(hasType(nonConstReferenceType())));
197 const auto AsNonConstRefArg = anyOf(
198 callExpr(NonConstRefParam), cxxConstructExpr(NonConstRefParam),
199 callExpr(callee(expr(anyOf(unresolvedLookupExpr(), unresolvedMemberExpr(),
200 cxxDependentScopeMemberExpr(),
201 hasType(templateTypeParmType())))),
202 hasAnyArgument(equalsNode(Exp))),
203 cxxUnresolvedConstructExpr(hasAnyArgument(equalsNode(Exp))));
204
205 // Captured by a lambda by reference.
206 // If we're initializing a capture with 'Exp' directly then we're initializing
207 // a reference capture.
208 // For value captures there will be an ImplicitCastExpr <LValueToRValue>.
209 const auto AsLambdaRefCaptureInit = lambdaExpr(hasCaptureInit(Exp));
210
211 // Returned as non-const-ref.
212 // If we're returning 'Exp' directly then it's returned as non-const-ref.
213 // For returning by value there will be an ImplicitCastExpr <LValueToRValue>.
214 // For returning by const-ref there will be an ImplicitCastExpr <NoOp> (for
215 // adding const.)
216 const auto AsNonConstRefReturn = returnStmt(hasReturnValue(equalsNode(Exp)));
217
218 const auto Matches =
219 match(findAll(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand, AsNonConstThis,
220 AsAmpersandOperand, AsPointerFromArrayDecay,
221 AsOperatorArrowThis, AsNonConstRefArg,
222 AsLambdaRefCaptureInit, AsNonConstRefReturn))
223 .bind("stmt")),
224 Stm, Context);
225 return selectFirst<Stmt>("stmt", Matches);
226}
227
228const Stmt *ExprMutationAnalyzer::findMemberMutation(const Expr *Exp) {
229 // Check whether any member of 'Exp' is mutated.
230 const auto MemberExprs =
231 match(findAll(expr(anyOf(memberExpr(hasObjectExpression(equalsNode(Exp))),
232 cxxDependentScopeMemberExpr(
233 hasObjectExpression(equalsNode(Exp)))))
234 .bind("expr")),
235 Stm, Context);
236 return findExprMutation(MemberExprs);
237}
238
239const Stmt *ExprMutationAnalyzer::findArrayElementMutation(const Expr *Exp) {
240 // Check whether any element of an array is mutated.
241 const auto SubscriptExprs = match(
242 findAll(arraySubscriptExpr(hasBase(ignoringImpCasts(equalsNode(Exp))))
243 .bind("expr")),
244 Stm, Context);
245 return findExprMutation(SubscriptExprs);
246}
247
248const Stmt *ExprMutationAnalyzer::findCastMutation(const Expr *Exp) {
249 // If 'Exp' is casted to any non-const reference type, check the castExpr.
250 const auto Casts =
251 match(findAll(castExpr(hasSourceExpression(equalsNode(Exp)),
252 anyOf(explicitCastExpr(hasDestinationType(
253 nonConstReferenceType())),
254 implicitCastExpr(hasImplicitDestinationType(
255 nonConstReferenceType()))))
256 .bind("expr")),
257 Stm, Context);
258 return findExprMutation(Casts);
259}
260
261const Stmt *ExprMutationAnalyzer::findRangeLoopMutation(const Expr *Exp) {
262 // If range for looping over 'Exp' with a non-const reference loop variable,
263 // check all declRefExpr of the loop variable.
264 const auto LoopVars =
265 match(findAll(cxxForRangeStmt(
266 hasLoopVariable(
267 varDecl(hasType(nonConstReferenceType())).bind("decl")),
268 hasRangeInit(equalsNode(Exp)))),
269 Stm, Context);
270 return findDeclMutation(LoopVars);
271}
272
273const Stmt *ExprMutationAnalyzer::findReferenceMutation(const Expr *Exp) {
274 // Follow non-const reference returned by `operator*()` of move-only classes.
275 // These are typically smart pointers with unique ownership so we treat
276 // mutation of pointee as mutation of the smart pointer itself.
277 const auto Ref = match(
278 findAll(cxxOperatorCallExpr(
279 hasOverloadedOperatorName("*"),
280 callee(cxxMethodDecl(ofClass(isMoveOnly()),
281 returns(hasUnqualifiedDesugaredType(
282 nonConstReferenceType())))),
283 argumentCountIs(1), hasArgument(0, equalsNode(Exp)))
284 .bind("expr")),
285 Stm, Context);
286 if (const Stmt *S = findExprMutation(Ref))
287 return S;
288
289 // If 'Exp' is bound to a non-const reference, check all declRefExpr to that.
290 const auto Refs = match(
291 stmt(forEachDescendant(
292 varDecl(
293 hasType(nonConstReferenceType()),
294 hasInitializer(anyOf(equalsNode(Exp),
295 conditionalOperator(anyOf(
296 hasTrueExpression(equalsNode(Exp)),
297 hasFalseExpression(equalsNode(Exp)))))),
298 hasParent(declStmt().bind("stmt")),
299 // Don't follow the reference in range statement, we've handled
300 // that separately.
301 unless(hasParent(declStmt(hasParent(
302 cxxForRangeStmt(hasRangeStmt(equalsBoundNode("stmt"))))))))
303 .bind("decl"))),
304 Stm, Context);
305 return findDeclMutation(Refs);
306}
307
308} // namespace clang