blob: f8d75f477872734fea5aa00e4e7f06cde0da27d5 [file] [log] [blame]
Shuai Wange9192f82018-09-11 21:13:20 +00001//===---------- ExprMutationAnalyzer.cpp ----------------------------------===//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
Shuai Wange9192f82018-09-11 21:13:20 +00006//
7//===----------------------------------------------------------------------===//
8#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
9#include "clang/ASTMatchers/ASTMatchFinder.h"
10#include "llvm/ADT/STLExtras.h"
11
12namespace clang {
13using namespace ast_matchers;
14
15namespace {
16
17AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) {
18 return llvm::is_contained(Node.capture_inits(), E);
19}
20
21AST_MATCHER_P(CXXForRangeStmt, hasRangeStmt,
22 ast_matchers::internal::Matcher<DeclStmt>, InnerMatcher) {
23 const DeclStmt *const Range = Node.getRangeStmt();
24 return InnerMatcher.matches(*Range, Finder, Builder);
25}
26
27const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXTypeidExpr>
28 cxxTypeidExpr;
29
30AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) {
31 return Node.isPotentiallyEvaluated();
32}
33
34const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXNoexceptExpr>
35 cxxNoexceptExpr;
36
37const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt,
38 GenericSelectionExpr>
39 genericSelectionExpr;
40
41AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr,
42 ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
43 return InnerMatcher.matches(*Node.getControllingExpr(), Finder, Builder);
44}
45
46const auto nonConstReferenceType = [] {
47 return hasUnqualifiedDesugaredType(
48 referenceType(pointee(unless(isConstQualified()))));
49};
50
51const auto nonConstPointerType = [] {
52 return hasUnqualifiedDesugaredType(
53 pointerType(pointee(unless(isConstQualified()))));
54};
55
56const auto isMoveOnly = [] {
57 return cxxRecordDecl(
58 hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted()))),
59 hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted()))),
60 unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(),
61 unless(isDeleted()))),
62 hasMethod(cxxMethodDecl(isCopyAssignmentOperator(),
63 unless(isDeleted()))))));
64};
65
Shuai Wangaaaa3102018-09-16 21:09:50 +000066template <class T> struct NodeID;
67template <> struct NodeID<Expr> { static const std::string value; };
68template <> struct NodeID<Decl> { static const std::string value; };
69const std::string NodeID<Expr>::value = "expr";
70const std::string NodeID<Decl>::value = "decl";
71
72template <class T, class F = const Stmt *(ExprMutationAnalyzer::*)(const T *)>
73const Stmt *tryEachMatch(ArrayRef<ast_matchers::BoundNodes> Matches,
74 ExprMutationAnalyzer *Analyzer, F Finder) {
75 const StringRef ID = NodeID<T>::value;
76 for (const auto &Nodes : Matches) {
77 if (const Stmt *S = (Analyzer->*Finder)(Nodes.getNodeAs<T>(ID)))
78 return S;
79 }
80 return nullptr;
81}
82
Shuai Wange9192f82018-09-11 21:13:20 +000083} // namespace
84
85const Stmt *ExprMutationAnalyzer::findMutation(const Expr *Exp) {
Shuai Wangaaaa3102018-09-16 21:09:50 +000086 return findMutationMemoized(Exp,
87 {&ExprMutationAnalyzer::findDirectMutation,
88 &ExprMutationAnalyzer::findMemberMutation,
89 &ExprMutationAnalyzer::findArrayElementMutation,
90 &ExprMutationAnalyzer::findCastMutation,
91 &ExprMutationAnalyzer::findRangeLoopMutation,
92 &ExprMutationAnalyzer::findReferenceMutation,
93 &ExprMutationAnalyzer::findFunctionArgMutation},
94 Results);
95}
96
97const Stmt *ExprMutationAnalyzer::findMutation(const Decl *Dec) {
98 return tryEachDeclRef(Dec, &ExprMutationAnalyzer::findMutation);
99}
100
101const Stmt *ExprMutationAnalyzer::findPointeeMutation(const Expr *Exp) {
102 return findMutationMemoized(Exp, {/*TODO*/}, PointeeResults);
103}
104
105const Stmt *ExprMutationAnalyzer::findPointeeMutation(const Decl *Dec) {
106 return tryEachDeclRef(Dec, &ExprMutationAnalyzer::findPointeeMutation);
107}
108
109const Stmt *ExprMutationAnalyzer::findMutationMemoized(
110 const Expr *Exp, llvm::ArrayRef<MutationFinder> Finders,
111 ResultMap &MemoizedResults) {
112 const auto Memoized = MemoizedResults.find(Exp);
113 if (Memoized != MemoizedResults.end())
Shuai Wange9192f82018-09-11 21:13:20 +0000114 return Memoized->second;
115
116 if (isUnevaluated(Exp))
Shuai Wangaaaa3102018-09-16 21:09:50 +0000117 return MemoizedResults[Exp] = nullptr;
Shuai Wange9192f82018-09-11 21:13:20 +0000118
Shuai Wangaaaa3102018-09-16 21:09:50 +0000119 for (const auto &Finder : Finders) {
Shuai Wange9192f82018-09-11 21:13:20 +0000120 if (const Stmt *S = (this->*Finder)(Exp))
Shuai Wangaaaa3102018-09-16 21:09:50 +0000121 return MemoizedResults[Exp] = S;
Shuai Wange9192f82018-09-11 21:13:20 +0000122 }
123
Shuai Wangaaaa3102018-09-16 21:09:50 +0000124 return MemoizedResults[Exp] = nullptr;
125}
126
127const Stmt *ExprMutationAnalyzer::tryEachDeclRef(const Decl *Dec,
128 MutationFinder Finder) {
129 const auto Refs =
130 match(findAll(declRefExpr(to(equalsNode(Dec))).bind(NodeID<Expr>::value)),
131 Stm, Context);
132 for (const auto &RefNodes : Refs) {
133 const auto *E = RefNodes.getNodeAs<Expr>(NodeID<Expr>::value);
134 if ((this->*Finder)(E))
135 return E;
136 }
137 return nullptr;
Shuai Wange9192f82018-09-11 21:13:20 +0000138}
139
140bool ExprMutationAnalyzer::isUnevaluated(const Expr *Exp) {
141 return selectFirst<Expr>(
Shuai Wangaaaa3102018-09-16 21:09:50 +0000142 NodeID<Expr>::value,
Shuai Wange9192f82018-09-11 21:13:20 +0000143 match(
144 findAll(
145 expr(equalsNode(Exp),
146 anyOf(
147 // `Exp` is part of the underlying expression of
148 // decltype/typeof if it has an ancestor of
149 // typeLoc.
150 hasAncestor(typeLoc(unless(
151 hasAncestor(unaryExprOrTypeTraitExpr())))),
152 hasAncestor(expr(anyOf(
153 // `UnaryExprOrTypeTraitExpr` is unevaluated
154 // unless it's sizeof on VLA.
155 unaryExprOrTypeTraitExpr(unless(sizeOfExpr(
156 hasArgumentOfType(variableArrayType())))),
157 // `CXXTypeidExpr` is unevaluated unless it's
158 // applied to an expression of glvalue of
159 // polymorphic class type.
160 cxxTypeidExpr(
161 unless(isPotentiallyEvaluated())),
162 // The controlling expression of
163 // `GenericSelectionExpr` is unevaluated.
164 genericSelectionExpr(hasControllingExpr(
165 hasDescendant(equalsNode(Exp)))),
166 cxxNoexceptExpr())))))
Shuai Wangaaaa3102018-09-16 21:09:50 +0000167 .bind(NodeID<Expr>::value)),
Shuai Wange9192f82018-09-11 21:13:20 +0000168 Stm, Context)) != nullptr;
169}
170
171const Stmt *
172ExprMutationAnalyzer::findExprMutation(ArrayRef<BoundNodes> Matches) {
Shuai Wangaaaa3102018-09-16 21:09:50 +0000173 return tryEachMatch<Expr>(Matches, this, &ExprMutationAnalyzer::findMutation);
Shuai Wange9192f82018-09-11 21:13:20 +0000174}
175
176const Stmt *
177ExprMutationAnalyzer::findDeclMutation(ArrayRef<BoundNodes> Matches) {
Shuai Wangaaaa3102018-09-16 21:09:50 +0000178 return tryEachMatch<Decl>(Matches, this, &ExprMutationAnalyzer::findMutation);
Shuai Wange9192f82018-09-11 21:13:20 +0000179}
180
Shuai Wangaaaa3102018-09-16 21:09:50 +0000181const Stmt *ExprMutationAnalyzer::findExprPointeeMutation(
182 ArrayRef<ast_matchers::BoundNodes> Matches) {
183 return tryEachMatch<Expr>(Matches, this,
184 &ExprMutationAnalyzer::findPointeeMutation);
185}
186
187const Stmt *ExprMutationAnalyzer::findDeclPointeeMutation(
188 ArrayRef<ast_matchers::BoundNodes> Matches) {
189 return tryEachMatch<Decl>(Matches, this,
190 &ExprMutationAnalyzer::findPointeeMutation);
Shuai Wange9192f82018-09-11 21:13:20 +0000191}
192
193const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) {
194 // LHS of any assignment operators.
195 const auto AsAssignmentLhs =
196 binaryOperator(isAssignmentOperator(), hasLHS(equalsNode(Exp)));
197
198 // Operand of increment/decrement operators.
199 const auto AsIncDecOperand =
200 unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")),
201 hasUnaryOperand(equalsNode(Exp)));
202
203 // Invoking non-const member function.
204 // A member function is assumed to be non-const when it is unresolved.
205 const auto NonConstMethod = cxxMethodDecl(unless(isConst()));
206 const auto AsNonConstThis =
207 expr(anyOf(cxxMemberCallExpr(callee(NonConstMethod), on(equalsNode(Exp))),
208 cxxOperatorCallExpr(callee(NonConstMethod),
209 hasArgument(0, equalsNode(Exp))),
210 callExpr(callee(expr(anyOf(
211 unresolvedMemberExpr(hasObjectExpression(equalsNode(Exp))),
212 cxxDependentScopeMemberExpr(
213 hasObjectExpression(equalsNode(Exp)))))))));
214
215 // Taking address of 'Exp'.
216 // We're assuming 'Exp' is mutated as soon as its address is taken, though in
217 // theory we can follow the pointer and see whether it escaped `Stm` or is
218 // dereferenced and then mutated. This is left for future improvements.
219 const auto AsAmpersandOperand =
220 unaryOperator(hasOperatorName("&"),
221 // A NoOp implicit cast is adding const.
222 unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))),
223 hasUnaryOperand(equalsNode(Exp)));
224 const auto AsPointerFromArrayDecay =
225 castExpr(hasCastKind(CK_ArrayToPointerDecay),
226 unless(hasParent(arraySubscriptExpr())), has(equalsNode(Exp)));
227 // Treat calling `operator->()` of move-only classes as taking address.
228 // These are typically smart pointers with unique ownership so we treat
229 // mutation of pointee as mutation of the smart pointer itself.
230 const auto AsOperatorArrowThis =
231 cxxOperatorCallExpr(hasOverloadedOperatorName("->"),
232 callee(cxxMethodDecl(ofClass(isMoveOnly()),
233 returns(nonConstPointerType()))),
234 argumentCountIs(1), hasArgument(0, equalsNode(Exp)));
235
236 // Used as non-const-ref argument when calling a function.
237 // An argument is assumed to be non-const-ref when the function is unresolved.
Shuai Wangcb98b702018-09-14 20:07:18 +0000238 // Instantiated template functions are not handled here but in
239 // findFunctionArgMutation which has additional smarts for handling forwarding
240 // references.
Shuai Wange9192f82018-09-11 21:13:20 +0000241 const auto NonConstRefParam = forEachArgumentWithParam(
242 equalsNode(Exp), parmVarDecl(hasType(nonConstReferenceType())));
Shuai Wangcb98b702018-09-14 20:07:18 +0000243 const auto NotInstantiated = unless(hasDeclaration(isInstantiated()));
Shuai Wange9192f82018-09-11 21:13:20 +0000244 const auto AsNonConstRefArg = anyOf(
Shuai Wangcb98b702018-09-14 20:07:18 +0000245 callExpr(NonConstRefParam, NotInstantiated),
246 cxxConstructExpr(NonConstRefParam, NotInstantiated),
Shuai Wange9192f82018-09-11 21:13:20 +0000247 callExpr(callee(expr(anyOf(unresolvedLookupExpr(), unresolvedMemberExpr(),
248 cxxDependentScopeMemberExpr(),
249 hasType(templateTypeParmType())))),
250 hasAnyArgument(equalsNode(Exp))),
251 cxxUnresolvedConstructExpr(hasAnyArgument(equalsNode(Exp))));
252
253 // Captured by a lambda by reference.
254 // If we're initializing a capture with 'Exp' directly then we're initializing
255 // a reference capture.
256 // For value captures there will be an ImplicitCastExpr <LValueToRValue>.
257 const auto AsLambdaRefCaptureInit = lambdaExpr(hasCaptureInit(Exp));
258
259 // Returned as non-const-ref.
260 // If we're returning 'Exp' directly then it's returned as non-const-ref.
261 // For returning by value there will be an ImplicitCastExpr <LValueToRValue>.
262 // For returning by const-ref there will be an ImplicitCastExpr <NoOp> (for
263 // adding const.)
264 const auto AsNonConstRefReturn = returnStmt(hasReturnValue(equalsNode(Exp)));
265
266 const auto Matches =
267 match(findAll(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand, AsNonConstThis,
268 AsAmpersandOperand, AsPointerFromArrayDecay,
269 AsOperatorArrowThis, AsNonConstRefArg,
270 AsLambdaRefCaptureInit, AsNonConstRefReturn))
271 .bind("stmt")),
272 Stm, Context);
273 return selectFirst<Stmt>("stmt", Matches);
274}
275
276const Stmt *ExprMutationAnalyzer::findMemberMutation(const Expr *Exp) {
277 // Check whether any member of 'Exp' is mutated.
278 const auto MemberExprs =
279 match(findAll(expr(anyOf(memberExpr(hasObjectExpression(equalsNode(Exp))),
280 cxxDependentScopeMemberExpr(
281 hasObjectExpression(equalsNode(Exp)))))
Shuai Wangaaaa3102018-09-16 21:09:50 +0000282 .bind(NodeID<Expr>::value)),
Shuai Wange9192f82018-09-11 21:13:20 +0000283 Stm, Context);
284 return findExprMutation(MemberExprs);
285}
286
287const Stmt *ExprMutationAnalyzer::findArrayElementMutation(const Expr *Exp) {
288 // Check whether any element of an array is mutated.
289 const auto SubscriptExprs = match(
290 findAll(arraySubscriptExpr(hasBase(ignoringImpCasts(equalsNode(Exp))))
Shuai Wangaaaa3102018-09-16 21:09:50 +0000291 .bind(NodeID<Expr>::value)),
Shuai Wange9192f82018-09-11 21:13:20 +0000292 Stm, Context);
293 return findExprMutation(SubscriptExprs);
294}
295
296const Stmt *ExprMutationAnalyzer::findCastMutation(const Expr *Exp) {
297 // If 'Exp' is casted to any non-const reference type, check the castExpr.
298 const auto Casts =
299 match(findAll(castExpr(hasSourceExpression(equalsNode(Exp)),
300 anyOf(explicitCastExpr(hasDestinationType(
301 nonConstReferenceType())),
302 implicitCastExpr(hasImplicitDestinationType(
303 nonConstReferenceType()))))
Shuai Wangaaaa3102018-09-16 21:09:50 +0000304 .bind(NodeID<Expr>::value)),
Shuai Wange9192f82018-09-11 21:13:20 +0000305 Stm, Context);
Shuai Wang43059932018-09-17 20:10:56 +0000306 if (const Stmt *S = findExprMutation(Casts))
307 return S;
308 // Treat std::{move,forward} as cast.
309 const auto Calls =
310 match(findAll(callExpr(callee(namedDecl(
311 hasAnyName("::std::move", "::std::forward"))),
312 hasArgument(0, equalsNode(Exp)))
313 .bind("expr")),
314 Stm, Context);
315 return findExprMutation(Calls);
Shuai Wange9192f82018-09-11 21:13:20 +0000316}
317
318const Stmt *ExprMutationAnalyzer::findRangeLoopMutation(const Expr *Exp) {
319 // If range for looping over 'Exp' with a non-const reference loop variable,
320 // check all declRefExpr of the loop variable.
321 const auto LoopVars =
322 match(findAll(cxxForRangeStmt(
Shuai Wangaaaa3102018-09-16 21:09:50 +0000323 hasLoopVariable(varDecl(hasType(nonConstReferenceType()))
324 .bind(NodeID<Decl>::value)),
Shuai Wange9192f82018-09-11 21:13:20 +0000325 hasRangeInit(equalsNode(Exp)))),
326 Stm, Context);
327 return findDeclMutation(LoopVars);
328}
329
330const Stmt *ExprMutationAnalyzer::findReferenceMutation(const Expr *Exp) {
331 // Follow non-const reference returned by `operator*()` of move-only classes.
332 // These are typically smart pointers with unique ownership so we treat
333 // mutation of pointee as mutation of the smart pointer itself.
Shuai Wangcef621d2018-09-15 21:38:18 +0000334 const auto Ref =
335 match(findAll(cxxOperatorCallExpr(
336 hasOverloadedOperatorName("*"),
337 callee(cxxMethodDecl(ofClass(isMoveOnly()),
338 returns(nonConstReferenceType()))),
339 argumentCountIs(1), hasArgument(0, equalsNode(Exp)))
Shuai Wangaaaa3102018-09-16 21:09:50 +0000340 .bind(NodeID<Expr>::value)),
Shuai Wangcef621d2018-09-15 21:38:18 +0000341 Stm, Context);
Shuai Wange9192f82018-09-11 21:13:20 +0000342 if (const Stmt *S = findExprMutation(Ref))
343 return S;
344
345 // If 'Exp' is bound to a non-const reference, check all declRefExpr to that.
346 const auto Refs = match(
347 stmt(forEachDescendant(
348 varDecl(
349 hasType(nonConstReferenceType()),
350 hasInitializer(anyOf(equalsNode(Exp),
351 conditionalOperator(anyOf(
352 hasTrueExpression(equalsNode(Exp)),
353 hasFalseExpression(equalsNode(Exp)))))),
354 hasParent(declStmt().bind("stmt")),
355 // Don't follow the reference in range statement, we've handled
356 // that separately.
357 unless(hasParent(declStmt(hasParent(
358 cxxForRangeStmt(hasRangeStmt(equalsBoundNode("stmt"))))))))
Shuai Wangaaaa3102018-09-16 21:09:50 +0000359 .bind(NodeID<Decl>::value))),
Shuai Wange9192f82018-09-11 21:13:20 +0000360 Stm, Context);
361 return findDeclMutation(Refs);
362}
363
Shuai Wangcb98b702018-09-14 20:07:18 +0000364const Stmt *ExprMutationAnalyzer::findFunctionArgMutation(const Expr *Exp) {
365 const auto NonConstRefParam = forEachArgumentWithParam(
366 equalsNode(Exp),
367 parmVarDecl(hasType(nonConstReferenceType())).bind("parm"));
368 const auto IsInstantiated = hasDeclaration(isInstantiated());
369 const auto FuncDecl = hasDeclaration(functionDecl().bind("func"));
370 const auto Matches = match(
Shuai Wang43059932018-09-17 20:10:56 +0000371 findAll(expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl,
372 unless(callee(namedDecl(hasAnyName(
373 "::std::move", "::std::forward"))))),
Shuai Wangcb98b702018-09-14 20:07:18 +0000374 cxxConstructExpr(NonConstRefParam, IsInstantiated,
375 FuncDecl)))
Shuai Wangaaaa3102018-09-16 21:09:50 +0000376 .bind(NodeID<Expr>::value)),
Shuai Wangcb98b702018-09-14 20:07:18 +0000377 Stm, Context);
378 for (const auto &Nodes : Matches) {
Shuai Wangaaaa3102018-09-16 21:09:50 +0000379 const auto *Exp = Nodes.getNodeAs<Expr>(NodeID<Expr>::value);
Shuai Wangcb98b702018-09-14 20:07:18 +0000380 const auto *Func = Nodes.getNodeAs<FunctionDecl>("func");
Shuai Wang86e5cb02018-09-19 18:00:55 +0000381 if (!Func->getBody() || !Func->getPrimaryTemplate())
Shuai Wangcb98b702018-09-14 20:07:18 +0000382 return Exp;
383
384 const auto *Parm = Nodes.getNodeAs<ParmVarDecl>("parm");
385 const ArrayRef<ParmVarDecl *> AllParams =
386 Func->getPrimaryTemplate()->getTemplatedDecl()->parameters();
387 QualType ParmType =
388 AllParams[std::min<size_t>(Parm->getFunctionScopeIndex(),
389 AllParams.size() - 1)]
390 ->getType();
391 if (const auto *T = ParmType->getAs<PackExpansionType>())
392 ParmType = T->getPattern();
393
394 // If param type is forwarding reference, follow into the function
395 // definition and see whether the param is mutated inside.
396 if (const auto *RefType = ParmType->getAs<RValueReferenceType>()) {
397 if (!RefType->getPointeeType().getQualifiers() &&
398 RefType->getPointeeType()->getAs<TemplateTypeParmType>()) {
399 std::unique_ptr<FunctionParmMutationAnalyzer> &Analyzer =
400 FuncParmAnalyzer[Func];
401 if (!Analyzer)
402 Analyzer.reset(new FunctionParmMutationAnalyzer(*Func, Context));
403 if (Analyzer->findMutation(Parm))
404 return Exp;
405 continue;
406 }
407 }
408 // Not forwarding reference.
409 return Exp;
410 }
411 return nullptr;
412}
413
414FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer(
415 const FunctionDecl &Func, ASTContext &Context)
416 : BodyAnalyzer(*Func.getBody(), Context) {
417 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(&Func)) {
418 // CXXCtorInitializer might also mutate Param but they're not part of
419 // function body, check them eagerly here since they're typically trivial.
420 for (const CXXCtorInitializer *Init : Ctor->inits()) {
421 ExprMutationAnalyzer InitAnalyzer(*Init->getInit(), Context);
422 for (const ParmVarDecl *Parm : Ctor->parameters()) {
423 if (Results.find(Parm) != Results.end())
424 continue;
Shuai Wangcef621d2018-09-15 21:38:18 +0000425 if (const Stmt *S = InitAnalyzer.findMutation(Parm))
Shuai Wangcb98b702018-09-14 20:07:18 +0000426 Results[Parm] = S;
427 }
428 }
429 }
430}
431
432const Stmt *
433FunctionParmMutationAnalyzer::findMutation(const ParmVarDecl *Parm) {
434 const auto Memoized = Results.find(Parm);
435 if (Memoized != Results.end())
436 return Memoized->second;
437
Shuai Wangcef621d2018-09-15 21:38:18 +0000438 if (const Stmt *S = BodyAnalyzer.findMutation(Parm))
Shuai Wangcb98b702018-09-14 20:07:18 +0000439 return Results[Parm] = S;
440
441 return Results[Parm] = nullptr;
442}
443
Shuai Wange9192f82018-09-11 21:13:20 +0000444} // namespace clang