blob: f8ede47bda802233575c212892503fd578a3dcde [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) {
Shuai Wangcef621d2018-09-15 21:38:18 +0000134 if (const Stmt *S = findMutation(DeclNodes.getNodeAs<Decl>("decl")))
Shuai Wange9192f82018-09-11 21:13:20 +0000135 return S;
136 }
137 return nullptr;
138}
139
Shuai Wangcef621d2018-09-15 21:38:18 +0000140const Stmt *ExprMutationAnalyzer::findMutation(const Decl *Dec) {
Shuai Wange9192f82018-09-11 21:13:20 +0000141 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.
Shuai Wangcef621d2018-09-15 21:38:18 +0000283 const auto Ref =
284 match(findAll(cxxOperatorCallExpr(
285 hasOverloadedOperatorName("*"),
286 callee(cxxMethodDecl(ofClass(isMoveOnly()),
287 returns(nonConstReferenceType()))),
288 argumentCountIs(1), hasArgument(0, equalsNode(Exp)))
289 .bind("expr")),
290 Stm, Context);
Shuai Wange9192f82018-09-11 21:13:20 +0000291 if (const Stmt *S = findExprMutation(Ref))
292 return S;
293
294 // If 'Exp' is bound to a non-const reference, check all declRefExpr to that.
295 const auto Refs = match(
296 stmt(forEachDescendant(
297 varDecl(
298 hasType(nonConstReferenceType()),
299 hasInitializer(anyOf(equalsNode(Exp),
300 conditionalOperator(anyOf(
301 hasTrueExpression(equalsNode(Exp)),
302 hasFalseExpression(equalsNode(Exp)))))),
303 hasParent(declStmt().bind("stmt")),
304 // Don't follow the reference in range statement, we've handled
305 // that separately.
306 unless(hasParent(declStmt(hasParent(
307 cxxForRangeStmt(hasRangeStmt(equalsBoundNode("stmt"))))))))
308 .bind("decl"))),
309 Stm, Context);
310 return findDeclMutation(Refs);
311}
312
Shuai Wangcb98b702018-09-14 20:07:18 +0000313const Stmt *ExprMutationAnalyzer::findFunctionArgMutation(const Expr *Exp) {
314 const auto NonConstRefParam = forEachArgumentWithParam(
315 equalsNode(Exp),
316 parmVarDecl(hasType(nonConstReferenceType())).bind("parm"));
317 const auto IsInstantiated = hasDeclaration(isInstantiated());
318 const auto FuncDecl = hasDeclaration(functionDecl().bind("func"));
319 const auto Matches = match(
320 findAll(expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl),
321 cxxConstructExpr(NonConstRefParam, IsInstantiated,
322 FuncDecl)))
323 .bind("expr")),
324 Stm, Context);
325 for (const auto &Nodes : Matches) {
326 const auto *Exp = Nodes.getNodeAs<Expr>("expr");
327 const auto *Func = Nodes.getNodeAs<FunctionDecl>("func");
328 if (!Func->getBody())
329 return Exp;
330
331 const auto *Parm = Nodes.getNodeAs<ParmVarDecl>("parm");
332 const ArrayRef<ParmVarDecl *> AllParams =
333 Func->getPrimaryTemplate()->getTemplatedDecl()->parameters();
334 QualType ParmType =
335 AllParams[std::min<size_t>(Parm->getFunctionScopeIndex(),
336 AllParams.size() - 1)]
337 ->getType();
338 if (const auto *T = ParmType->getAs<PackExpansionType>())
339 ParmType = T->getPattern();
340
341 // If param type is forwarding reference, follow into the function
342 // definition and see whether the param is mutated inside.
343 if (const auto *RefType = ParmType->getAs<RValueReferenceType>()) {
344 if (!RefType->getPointeeType().getQualifiers() &&
345 RefType->getPointeeType()->getAs<TemplateTypeParmType>()) {
346 std::unique_ptr<FunctionParmMutationAnalyzer> &Analyzer =
347 FuncParmAnalyzer[Func];
348 if (!Analyzer)
349 Analyzer.reset(new FunctionParmMutationAnalyzer(*Func, Context));
350 if (Analyzer->findMutation(Parm))
351 return Exp;
352 continue;
353 }
354 }
355 // Not forwarding reference.
356 return Exp;
357 }
358 return nullptr;
359}
360
361FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer(
362 const FunctionDecl &Func, ASTContext &Context)
363 : BodyAnalyzer(*Func.getBody(), Context) {
364 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(&Func)) {
365 // CXXCtorInitializer might also mutate Param but they're not part of
366 // function body, check them eagerly here since they're typically trivial.
367 for (const CXXCtorInitializer *Init : Ctor->inits()) {
368 ExprMutationAnalyzer InitAnalyzer(*Init->getInit(), Context);
369 for (const ParmVarDecl *Parm : Ctor->parameters()) {
370 if (Results.find(Parm) != Results.end())
371 continue;
Shuai Wangcef621d2018-09-15 21:38:18 +0000372 if (const Stmt *S = InitAnalyzer.findMutation(Parm))
Shuai Wangcb98b702018-09-14 20:07:18 +0000373 Results[Parm] = S;
374 }
375 }
376 }
377}
378
379const Stmt *
380FunctionParmMutationAnalyzer::findMutation(const ParmVarDecl *Parm) {
381 const auto Memoized = Results.find(Parm);
382 if (Memoized != Results.end())
383 return Memoized->second;
384
Shuai Wangcef621d2018-09-15 21:38:18 +0000385 if (const Stmt *S = BodyAnalyzer.findMutation(Parm))
Shuai Wangcb98b702018-09-14 20:07:18 +0000386 return Results[Parm] = S;
387
388 return Results[Parm] = nullptr;
389}
390
Shuai Wange9192f82018-09-11 21:13:20 +0000391} // namespace clang