blob: 678eeebab2debe6b15b069698978c230421998bb [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,
Shuai Wangcb98b702018-09-14 20:07:18 +000082 &ExprMutationAnalyzer::findReferenceMutation,
83 &ExprMutationAnalyzer::findFunctionArgMutation}) {
Shuai Wange9192f82018-09-11 21:13:20 +000084 if (const Stmt *S = (this->*Finder)(Exp))
85 return Results[Exp] = S;
86 }
87
88 return Results[Exp] = nullptr;
89}
90
91bool ExprMutationAnalyzer::isUnevaluated(const Expr *Exp) {
92 return selectFirst<Expr>(
93 "expr",
94 match(
95 findAll(
96 expr(equalsNode(Exp),
97 anyOf(
98 // `Exp` is part of the underlying expression of
99 // decltype/typeof if it has an ancestor of
100 // typeLoc.
101 hasAncestor(typeLoc(unless(
102 hasAncestor(unaryExprOrTypeTraitExpr())))),
103 hasAncestor(expr(anyOf(
104 // `UnaryExprOrTypeTraitExpr` is unevaluated
105 // unless it's sizeof on VLA.
106 unaryExprOrTypeTraitExpr(unless(sizeOfExpr(
107 hasArgumentOfType(variableArrayType())))),
108 // `CXXTypeidExpr` is unevaluated unless it's
109 // applied to an expression of glvalue of
110 // polymorphic class type.
111 cxxTypeidExpr(
112 unless(isPotentiallyEvaluated())),
113 // The controlling expression of
114 // `GenericSelectionExpr` is unevaluated.
115 genericSelectionExpr(hasControllingExpr(
116 hasDescendant(equalsNode(Exp)))),
117 cxxNoexceptExpr())))))
118 .bind("expr")),
119 Stm, Context)) != nullptr;
120}
121
122const Stmt *
123ExprMutationAnalyzer::findExprMutation(ArrayRef<BoundNodes> Matches) {
124 for (const auto &Nodes : Matches) {
125 if (const Stmt *S = findMutation(Nodes.getNodeAs<Expr>("expr")))
126 return S;
127 }
128 return nullptr;
129}
130
131const Stmt *
132ExprMutationAnalyzer::findDeclMutation(ArrayRef<BoundNodes> Matches) {
133 for (const auto &DeclNodes : Matches) {
134 if (const Stmt *S = findDeclMutation(DeclNodes.getNodeAs<Decl>("decl")))
135 return S;
136 }
137 return nullptr;
138}
139
140const Stmt *ExprMutationAnalyzer::findDeclMutation(const Decl *Dec) {
141 const auto Refs = match(
142 findAll(declRefExpr(to(equalsNode(Dec))).bind("expr")), Stm, Context);
143 for (const auto &RefNodes : Refs) {
144 const auto *E = RefNodes.getNodeAs<Expr>("expr");
145 if (findMutation(E))
146 return E;
147 }
148 return nullptr;
149}
150
151const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) {
152 // LHS of any assignment operators.
153 const auto AsAssignmentLhs =
154 binaryOperator(isAssignmentOperator(), hasLHS(equalsNode(Exp)));
155
156 // Operand of increment/decrement operators.
157 const auto AsIncDecOperand =
158 unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")),
159 hasUnaryOperand(equalsNode(Exp)));
160
161 // Invoking non-const member function.
162 // A member function is assumed to be non-const when it is unresolved.
163 const auto NonConstMethod = cxxMethodDecl(unless(isConst()));
164 const auto AsNonConstThis =
165 expr(anyOf(cxxMemberCallExpr(callee(NonConstMethod), on(equalsNode(Exp))),
166 cxxOperatorCallExpr(callee(NonConstMethod),
167 hasArgument(0, equalsNode(Exp))),
168 callExpr(callee(expr(anyOf(
169 unresolvedMemberExpr(hasObjectExpression(equalsNode(Exp))),
170 cxxDependentScopeMemberExpr(
171 hasObjectExpression(equalsNode(Exp)))))))));
172
173 // Taking address of 'Exp'.
174 // We're assuming 'Exp' is mutated as soon as its address is taken, though in
175 // theory we can follow the pointer and see whether it escaped `Stm` or is
176 // dereferenced and then mutated. This is left for future improvements.
177 const auto AsAmpersandOperand =
178 unaryOperator(hasOperatorName("&"),
179 // A NoOp implicit cast is adding const.
180 unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))),
181 hasUnaryOperand(equalsNode(Exp)));
182 const auto AsPointerFromArrayDecay =
183 castExpr(hasCastKind(CK_ArrayToPointerDecay),
184 unless(hasParent(arraySubscriptExpr())), has(equalsNode(Exp)));
185 // Treat calling `operator->()` of move-only classes as taking address.
186 // These are typically smart pointers with unique ownership so we treat
187 // mutation of pointee as mutation of the smart pointer itself.
188 const auto AsOperatorArrowThis =
189 cxxOperatorCallExpr(hasOverloadedOperatorName("->"),
190 callee(cxxMethodDecl(ofClass(isMoveOnly()),
191 returns(nonConstPointerType()))),
192 argumentCountIs(1), hasArgument(0, equalsNode(Exp)));
193
194 // Used as non-const-ref argument when calling a function.
195 // An argument is assumed to be non-const-ref when the function is unresolved.
Shuai Wangcb98b702018-09-14 20:07:18 +0000196 // Instantiated template functions are not handled here but in
197 // findFunctionArgMutation which has additional smarts for handling forwarding
198 // references.
Shuai Wange9192f82018-09-11 21:13:20 +0000199 const auto NonConstRefParam = forEachArgumentWithParam(
200 equalsNode(Exp), parmVarDecl(hasType(nonConstReferenceType())));
Shuai Wangcb98b702018-09-14 20:07:18 +0000201 const auto NotInstantiated = unless(hasDeclaration(isInstantiated()));
Shuai Wange9192f82018-09-11 21:13:20 +0000202 const auto AsNonConstRefArg = anyOf(
Shuai Wangcb98b702018-09-14 20:07:18 +0000203 callExpr(NonConstRefParam, NotInstantiated),
204 cxxConstructExpr(NonConstRefParam, NotInstantiated),
Shuai Wange9192f82018-09-11 21:13:20 +0000205 callExpr(callee(expr(anyOf(unresolvedLookupExpr(), unresolvedMemberExpr(),
206 cxxDependentScopeMemberExpr(),
207 hasType(templateTypeParmType())))),
208 hasAnyArgument(equalsNode(Exp))),
209 cxxUnresolvedConstructExpr(hasAnyArgument(equalsNode(Exp))));
210
211 // Captured by a lambda by reference.
212 // If we're initializing a capture with 'Exp' directly then we're initializing
213 // a reference capture.
214 // For value captures there will be an ImplicitCastExpr <LValueToRValue>.
215 const auto AsLambdaRefCaptureInit = lambdaExpr(hasCaptureInit(Exp));
216
217 // Returned as non-const-ref.
218 // If we're returning 'Exp' directly then it's returned as non-const-ref.
219 // For returning by value there will be an ImplicitCastExpr <LValueToRValue>.
220 // For returning by const-ref there will be an ImplicitCastExpr <NoOp> (for
221 // adding const.)
222 const auto AsNonConstRefReturn = returnStmt(hasReturnValue(equalsNode(Exp)));
223
224 const auto Matches =
225 match(findAll(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand, AsNonConstThis,
226 AsAmpersandOperand, AsPointerFromArrayDecay,
227 AsOperatorArrowThis, AsNonConstRefArg,
228 AsLambdaRefCaptureInit, AsNonConstRefReturn))
229 .bind("stmt")),
230 Stm, Context);
231 return selectFirst<Stmt>("stmt", Matches);
232}
233
234const Stmt *ExprMutationAnalyzer::findMemberMutation(const Expr *Exp) {
235 // Check whether any member of 'Exp' is mutated.
236 const auto MemberExprs =
237 match(findAll(expr(anyOf(memberExpr(hasObjectExpression(equalsNode(Exp))),
238 cxxDependentScopeMemberExpr(
239 hasObjectExpression(equalsNode(Exp)))))
240 .bind("expr")),
241 Stm, Context);
242 return findExprMutation(MemberExprs);
243}
244
245const Stmt *ExprMutationAnalyzer::findArrayElementMutation(const Expr *Exp) {
246 // Check whether any element of an array is mutated.
247 const auto SubscriptExprs = match(
248 findAll(arraySubscriptExpr(hasBase(ignoringImpCasts(equalsNode(Exp))))
249 .bind("expr")),
250 Stm, Context);
251 return findExprMutation(SubscriptExprs);
252}
253
254const Stmt *ExprMutationAnalyzer::findCastMutation(const Expr *Exp) {
255 // If 'Exp' is casted to any non-const reference type, check the castExpr.
256 const auto Casts =
257 match(findAll(castExpr(hasSourceExpression(equalsNode(Exp)),
258 anyOf(explicitCastExpr(hasDestinationType(
259 nonConstReferenceType())),
260 implicitCastExpr(hasImplicitDestinationType(
261 nonConstReferenceType()))))
262 .bind("expr")),
263 Stm, Context);
264 return findExprMutation(Casts);
265}
266
267const Stmt *ExprMutationAnalyzer::findRangeLoopMutation(const Expr *Exp) {
268 // If range for looping over 'Exp' with a non-const reference loop variable,
269 // check all declRefExpr of the loop variable.
270 const auto LoopVars =
271 match(findAll(cxxForRangeStmt(
272 hasLoopVariable(
273 varDecl(hasType(nonConstReferenceType())).bind("decl")),
274 hasRangeInit(equalsNode(Exp)))),
275 Stm, Context);
276 return findDeclMutation(LoopVars);
277}
278
279const Stmt *ExprMutationAnalyzer::findReferenceMutation(const Expr *Exp) {
280 // Follow non-const reference returned by `operator*()` of move-only classes.
281 // These are typically smart pointers with unique ownership so we treat
282 // mutation of pointee as mutation of the smart pointer itself.
283 const auto Ref = match(
284 findAll(cxxOperatorCallExpr(
285 hasOverloadedOperatorName("*"),
286 callee(cxxMethodDecl(ofClass(isMoveOnly()),
287 returns(hasUnqualifiedDesugaredType(
288 nonConstReferenceType())))),
289 argumentCountIs(1), hasArgument(0, equalsNode(Exp)))
290 .bind("expr")),
291 Stm, Context);
292 if (const Stmt *S = findExprMutation(Ref))
293 return S;
294
295 // If 'Exp' is bound to a non-const reference, check all declRefExpr to that.
296 const auto Refs = match(
297 stmt(forEachDescendant(
298 varDecl(
299 hasType(nonConstReferenceType()),
300 hasInitializer(anyOf(equalsNode(Exp),
301 conditionalOperator(anyOf(
302 hasTrueExpression(equalsNode(Exp)),
303 hasFalseExpression(equalsNode(Exp)))))),
304 hasParent(declStmt().bind("stmt")),
305 // Don't follow the reference in range statement, we've handled
306 // that separately.
307 unless(hasParent(declStmt(hasParent(
308 cxxForRangeStmt(hasRangeStmt(equalsBoundNode("stmt"))))))))
309 .bind("decl"))),
310 Stm, Context);
311 return findDeclMutation(Refs);
312}
313
Shuai Wangcb98b702018-09-14 20:07:18 +0000314const Stmt *ExprMutationAnalyzer::findFunctionArgMutation(const Expr *Exp) {
315 const auto NonConstRefParam = forEachArgumentWithParam(
316 equalsNode(Exp),
317 parmVarDecl(hasType(nonConstReferenceType())).bind("parm"));
318 const auto IsInstantiated = hasDeclaration(isInstantiated());
319 const auto FuncDecl = hasDeclaration(functionDecl().bind("func"));
320 const auto Matches = match(
321 findAll(expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl),
322 cxxConstructExpr(NonConstRefParam, IsInstantiated,
323 FuncDecl)))
324 .bind("expr")),
325 Stm, Context);
326 for (const auto &Nodes : Matches) {
327 const auto *Exp = Nodes.getNodeAs<Expr>("expr");
328 const auto *Func = Nodes.getNodeAs<FunctionDecl>("func");
329 if (!Func->getBody())
330 return Exp;
331
332 const auto *Parm = Nodes.getNodeAs<ParmVarDecl>("parm");
333 const ArrayRef<ParmVarDecl *> AllParams =
334 Func->getPrimaryTemplate()->getTemplatedDecl()->parameters();
335 QualType ParmType =
336 AllParams[std::min<size_t>(Parm->getFunctionScopeIndex(),
337 AllParams.size() - 1)]
338 ->getType();
339 if (const auto *T = ParmType->getAs<PackExpansionType>())
340 ParmType = T->getPattern();
341
342 // If param type is forwarding reference, follow into the function
343 // definition and see whether the param is mutated inside.
344 if (const auto *RefType = ParmType->getAs<RValueReferenceType>()) {
345 if (!RefType->getPointeeType().getQualifiers() &&
346 RefType->getPointeeType()->getAs<TemplateTypeParmType>()) {
347 std::unique_ptr<FunctionParmMutationAnalyzer> &Analyzer =
348 FuncParmAnalyzer[Func];
349 if (!Analyzer)
350 Analyzer.reset(new FunctionParmMutationAnalyzer(*Func, Context));
351 if (Analyzer->findMutation(Parm))
352 return Exp;
353 continue;
354 }
355 }
356 // Not forwarding reference.
357 return Exp;
358 }
359 return nullptr;
360}
361
362FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer(
363 const FunctionDecl &Func, ASTContext &Context)
364 : BodyAnalyzer(*Func.getBody(), Context) {
365 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(&Func)) {
366 // CXXCtorInitializer might also mutate Param but they're not part of
367 // function body, check them eagerly here since they're typically trivial.
368 for (const CXXCtorInitializer *Init : Ctor->inits()) {
369 ExprMutationAnalyzer InitAnalyzer(*Init->getInit(), Context);
370 for (const ParmVarDecl *Parm : Ctor->parameters()) {
371 if (Results.find(Parm) != Results.end())
372 continue;
373 if (const Stmt *S = InitAnalyzer.findDeclMutation(Parm))
374 Results[Parm] = S;
375 }
376 }
377 }
378}
379
380const Stmt *
381FunctionParmMutationAnalyzer::findMutation(const ParmVarDecl *Parm) {
382 const auto Memoized = Results.find(Parm);
383 if (Memoized != Results.end())
384 return Memoized->second;
385
386 if (const Stmt *S = BodyAnalyzer.findDeclMutation(Parm))
387 return Results[Parm] = S;
388
389 return Results[Parm] = nullptr;
390}
391
Shuai Wange9192f82018-09-11 21:13:20 +0000392} // namespace clang