xref: /freebsd/contrib/llvm-project/clang/lib/Analysis/ExprMutationAnalyzer.cpp (revision e64bea71c21eb42e97aa615188ba91f6cce0d36d)
1 //===---------- ExprMutationAnalyzer.cpp ----------------------------------===//
2 //
3 // 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
6 //
7 //===----------------------------------------------------------------------===//
8 #include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
9 #include "clang/AST/Expr.h"
10 #include "clang/AST/OperationKinds.h"
11 #include "clang/AST/Stmt.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/ASTMatchers/ASTMatchers.h"
14 #include "clang/ASTMatchers/ASTMatchersMacros.h"
15 #include "llvm/ADT/STLExtras.h"
16 
17 namespace clang {
18 using namespace ast_matchers;
19 
20 // Check if result of Source expression could be a Target expression.
21 // Checks:
22 //  - Implicit Casts
23 //  - Binary Operators
24 //  - ConditionalOperator
25 //  - BinaryConditionalOperator
26 static bool canExprResolveTo(const Expr *Source, const Expr *Target) {
27   const auto IgnoreDerivedToBase = [](const Expr *E, auto Matcher) {
28     if (Matcher(E))
29       return true;
30     if (const auto *Cast = dyn_cast<ImplicitCastExpr>(E)) {
31       if ((Cast->getCastKind() == CK_DerivedToBase ||
32            Cast->getCastKind() == CK_UncheckedDerivedToBase) &&
33           Matcher(Cast->getSubExpr()))
34         return true;
35     }
36     return false;
37   };
38 
39   const auto EvalCommaExpr = [](const Expr *E, auto Matcher) {
40     const Expr *Result = E;
41     while (const auto *BOComma =
42                dyn_cast_or_null<BinaryOperator>(Result->IgnoreParens())) {
43       if (!BOComma->isCommaOp())
44         break;
45       Result = BOComma->getRHS();
46     }
47 
48     return Result != E && Matcher(Result);
49   };
50 
51   // The 'ConditionalOperatorM' matches on `<anything> ? <expr> : <expr>`.
52   // This matching must be recursive because `<expr>` can be anything resolving
53   // to the `InnerMatcher`, for example another conditional operator.
54   // The edge-case `BaseClass &b = <cond> ? DerivedVar1 : DerivedVar2;`
55   // is handled, too. The implicit cast happens outside of the conditional.
56   // This is matched by `IgnoreDerivedToBase(canResolveToExpr(InnerMatcher))`
57   // below.
58   const auto ConditionalOperatorM = [Target](const Expr *E) {
59     if (const auto *CO = dyn_cast<AbstractConditionalOperator>(E)) {
60       const auto *TE = CO->getTrueExpr()->IgnoreParens();
61       if (TE && canExprResolveTo(TE, Target))
62         return true;
63       const auto *FE = CO->getFalseExpr()->IgnoreParens();
64       if (FE && canExprResolveTo(FE, Target))
65         return true;
66     }
67     return false;
68   };
69 
70   const Expr *SourceExprP = Source->IgnoreParens();
71   return IgnoreDerivedToBase(SourceExprP,
72                              [&](const Expr *E) {
73                                return E == Target || ConditionalOperatorM(E);
74                              }) ||
75          EvalCommaExpr(SourceExprP, [&](const Expr *E) {
76            return IgnoreDerivedToBase(
77                E->IgnoreParens(), [&](const Expr *EE) { return EE == Target; });
78          });
79 }
80 
81 namespace {
82 
83 // `ArraySubscriptExpr` can switch base and idx, e.g. `a[4]` is the same as
84 // `4[a]`. When type is dependent, we conservatively assume both sides are base.
85 AST_MATCHER_P(ArraySubscriptExpr, hasBaseConservative,
86               ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
87   if (Node.isTypeDependent()) {
88     return InnerMatcher.matches(*Node.getLHS(), Finder, Builder) ||
89            InnerMatcher.matches(*Node.getRHS(), Finder, Builder);
90   }
91   return InnerMatcher.matches(*Node.getBase(), Finder, Builder);
92 }
93 
94 AST_MATCHER(Type, isDependentType) { return Node.isDependentType(); }
95 
96 AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) {
97   return llvm::is_contained(Node.capture_inits(), E);
98 }
99 
100 AST_MATCHER_P(CXXForRangeStmt, hasRangeStmt,
101               ast_matchers::internal::Matcher<DeclStmt>, InnerMatcher) {
102   const DeclStmt *const Range = Node.getRangeStmt();
103   return InnerMatcher.matches(*Range, Finder, Builder);
104 }
105 
106 AST_MATCHER_P(Stmt, canResolveToExpr, const Stmt *, Inner) {
107   auto *Exp = dyn_cast<Expr>(&Node);
108   if (!Exp)
109     return true;
110   auto *Target = dyn_cast<Expr>(Inner);
111   if (!Target)
112     return false;
113   return canExprResolveTo(Exp, Target);
114 }
115 
116 // use class member to store data can reduce stack usage to avoid stack overflow
117 // when recursive call.
118 class ExprPointeeResolve {
119   const Expr *T;
120 
121   bool resolveExpr(const Expr *E) {
122     if (E == nullptr)
123       return false;
124     if (E == T)
125       return true;
126 
127     if (const auto *BO = dyn_cast<BinaryOperator>(E)) {
128       if (BO->isAdditiveOp())
129         return (resolveExpr(BO->getLHS()) || resolveExpr(BO->getRHS()));
130       if (BO->isCommaOp())
131         return resolveExpr(BO->getRHS());
132       return false;
133     }
134 
135     if (const auto *PE = dyn_cast<ParenExpr>(E))
136       return resolveExpr(PE->getSubExpr());
137 
138     if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E)) {
139       // only implicit cast needs to be treated as resolvable.
140       // explicit cast will be checked in `findPointeeToNonConst`
141       const CastKind kind = ICE->getCastKind();
142       if (kind == CK_LValueToRValue || kind == CK_DerivedToBase ||
143           kind == CK_UncheckedDerivedToBase)
144         return resolveExpr(ICE->getSubExpr());
145       return false;
146     }
147 
148     if (const auto *ACE = dyn_cast<AbstractConditionalOperator>(E))
149       return resolve(ACE->getTrueExpr()) || resolve(ACE->getFalseExpr());
150 
151     return false;
152   }
153 
154 public:
155   ExprPointeeResolve(const Expr *T) : T(T) {}
156   bool resolve(const Expr *S) { return resolveExpr(S); }
157 };
158 
159 AST_MATCHER_P(Stmt, canResolveToExprPointee, const Stmt *, T) {
160   auto *Exp = dyn_cast<Expr>(&Node);
161   if (!Exp)
162     return true;
163   auto *Target = dyn_cast<Expr>(T);
164   if (!Target)
165     return false;
166   return ExprPointeeResolve{Target}.resolve(Exp);
167 }
168 
169 // Similar to 'hasAnyArgument', but does not work because 'InitListExpr' does
170 // not have the 'arguments()' method.
171 AST_MATCHER_P(InitListExpr, hasAnyInit, ast_matchers::internal::Matcher<Expr>,
172               InnerMatcher) {
173   for (const Expr *Arg : Node.inits()) {
174     if (Arg == nullptr)
175       continue;
176     ast_matchers::internal::BoundNodesTreeBuilder Result(*Builder);
177     if (InnerMatcher.matches(*Arg, Finder, &Result)) {
178       *Builder = std::move(Result);
179       return true;
180     }
181   }
182   return false;
183 }
184 
185 const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXTypeidExpr>
186     cxxTypeidExpr;
187 
188 AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) {
189   return Node.isPotentiallyEvaluated();
190 }
191 
192 AST_MATCHER(CXXMemberCallExpr, isConstCallee) {
193   const Decl *CalleeDecl = Node.getCalleeDecl();
194   const auto *VD = dyn_cast_or_null<ValueDecl>(CalleeDecl);
195   if (!VD)
196     return false;
197   const QualType T = VD->getType().getCanonicalType();
198   const auto *MPT = dyn_cast<MemberPointerType>(T);
199   const auto *FPT = MPT ? cast<FunctionProtoType>(MPT->getPointeeType())
200                         : dyn_cast<FunctionProtoType>(T);
201   if (!FPT)
202     return false;
203   return FPT->isConst();
204 }
205 
206 AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr,
207               ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
208   if (Node.isTypePredicate())
209     return false;
210   return InnerMatcher.matches(*Node.getControllingExpr(), Finder, Builder);
211 }
212 
213 template <typename T>
214 ast_matchers::internal::Matcher<T>
215 findFirst(const ast_matchers::internal::Matcher<T> &Matcher) {
216   return anyOf(Matcher, hasDescendant(Matcher));
217 }
218 
219 const auto nonConstReferenceType = [] {
220   return hasUnqualifiedDesugaredType(
221       referenceType(pointee(unless(isConstQualified()))));
222 };
223 
224 const auto nonConstPointerType = [] {
225   return hasUnqualifiedDesugaredType(
226       pointerType(pointee(unless(isConstQualified()))));
227 };
228 
229 const auto isMoveOnly = [] {
230   return cxxRecordDecl(
231       hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted()))),
232       hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted()))),
233       unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(),
234                                                 unless(isDeleted()))),
235                    hasMethod(cxxMethodDecl(isCopyAssignmentOperator(),
236                                            unless(isDeleted()))))));
237 };
238 
239 template <class T> struct NodeID;
240 template <> struct NodeID<Expr> { static constexpr StringRef value = "expr"; };
241 template <> struct NodeID<Decl> { static constexpr StringRef value = "decl"; };
242 constexpr StringRef NodeID<Expr>::value;
243 constexpr StringRef NodeID<Decl>::value;
244 
245 template <class T,
246           class F = const Stmt *(ExprMutationAnalyzer::Analyzer::*)(const T *)>
247 const Stmt *tryEachMatch(ArrayRef<ast_matchers::BoundNodes> Matches,
248                          ExprMutationAnalyzer::Analyzer *Analyzer, F Finder) {
249   const StringRef ID = NodeID<T>::value;
250   for (const auto &Nodes : Matches) {
251     if (const Stmt *S = (Analyzer->*Finder)(Nodes.getNodeAs<T>(ID)))
252       return S;
253   }
254   return nullptr;
255 }
256 
257 } // namespace
258 
259 const Stmt *ExprMutationAnalyzer::Analyzer::findMutation(const Expr *Exp) {
260   return findMutationMemoized(
261       Exp,
262       {&ExprMutationAnalyzer::Analyzer::findDirectMutation,
263        &ExprMutationAnalyzer::Analyzer::findMemberMutation,
264        &ExprMutationAnalyzer::Analyzer::findArrayElementMutation,
265        &ExprMutationAnalyzer::Analyzer::findCastMutation,
266        &ExprMutationAnalyzer::Analyzer::findRangeLoopMutation,
267        &ExprMutationAnalyzer::Analyzer::findReferenceMutation,
268        &ExprMutationAnalyzer::Analyzer::findFunctionArgMutation},
269       Memorized.Results);
270 }
271 
272 const Stmt *ExprMutationAnalyzer::Analyzer::findMutation(const Decl *Dec) {
273   return tryEachDeclRef(Dec, &ExprMutationAnalyzer::Analyzer::findMutation);
274 }
275 
276 const Stmt *
277 ExprMutationAnalyzer::Analyzer::findPointeeMutation(const Expr *Exp) {
278   return findMutationMemoized(
279       Exp,
280       {
281           &ExprMutationAnalyzer::Analyzer::findPointeeValueMutation,
282           &ExprMutationAnalyzer::Analyzer::findPointeeMemberMutation,
283           &ExprMutationAnalyzer::Analyzer::findPointeeToNonConst,
284       },
285       Memorized.PointeeResults);
286 }
287 
288 const Stmt *
289 ExprMutationAnalyzer::Analyzer::findPointeeMutation(const Decl *Dec) {
290   return tryEachDeclRef(Dec,
291                         &ExprMutationAnalyzer::Analyzer::findPointeeMutation);
292 }
293 
294 const Stmt *ExprMutationAnalyzer::Analyzer::findMutationMemoized(
295     const Expr *Exp, llvm::ArrayRef<MutationFinder> Finders,
296     Memoized::ResultMap &MemoizedResults) {
297   // Assume Exp is not mutated before analyzing Exp.
298   auto [Memoized, Inserted] = MemoizedResults.try_emplace(Exp);
299   if (!Inserted)
300     return Memoized->second;
301 
302   if (ExprMutationAnalyzer::isUnevaluated(Exp, Context))
303     return nullptr;
304 
305   for (const auto &Finder : Finders) {
306     if (const Stmt *S = (this->*Finder)(Exp))
307       return MemoizedResults[Exp] = S;
308   }
309 
310   return nullptr;
311 }
312 
313 const Stmt *
314 ExprMutationAnalyzer::Analyzer::tryEachDeclRef(const Decl *Dec,
315                                                MutationFinder Finder) {
316   const auto Refs = match(
317       findAll(
318           declRefExpr(to(
319                           // `Dec` or a binding if `Dec` is a decomposition.
320                           anyOf(equalsNode(Dec),
321                                 bindingDecl(forDecomposition(equalsNode(Dec))))
322                           //
323                           ))
324               .bind(NodeID<Expr>::value)),
325       Stm, Context);
326   for (const auto &RefNodes : Refs) {
327     const auto *E = RefNodes.getNodeAs<Expr>(NodeID<Expr>::value);
328     if ((this->*Finder)(E))
329       return E;
330   }
331   return nullptr;
332 }
333 
334 bool ExprMutationAnalyzer::isUnevaluated(const Stmt *Stm, ASTContext &Context) {
335   return !match(stmt(anyOf(
336                     // `Exp` is part of the underlying expression of
337                     // decltype/typeof if it has an ancestor of
338                     // typeLoc.
339                     hasAncestor(typeLoc(
340                         unless(hasAncestor(unaryExprOrTypeTraitExpr())))),
341                     hasAncestor(expr(anyOf(
342                         // `UnaryExprOrTypeTraitExpr` is unevaluated
343                         // unless it's sizeof on VLA.
344                         unaryExprOrTypeTraitExpr(unless(sizeOfExpr(
345                             hasArgumentOfType(variableArrayType())))),
346                         // `CXXTypeidExpr` is unevaluated unless it's
347                         // applied to an expression of glvalue of
348                         // polymorphic class type.
349                         cxxTypeidExpr(unless(isPotentiallyEvaluated())),
350                         // The controlling expression of
351                         // `GenericSelectionExpr` is unevaluated.
352                         genericSelectionExpr(
353                             hasControllingExpr(hasDescendant(equalsNode(Stm)))),
354                         cxxNoexceptExpr()))))),
355                 *Stm, Context)
356               .empty();
357 }
358 
359 const Stmt *
360 ExprMutationAnalyzer::Analyzer::findExprMutation(ArrayRef<BoundNodes> Matches) {
361   return tryEachMatch<Expr>(Matches, this,
362                             &ExprMutationAnalyzer::Analyzer::findMutation);
363 }
364 
365 const Stmt *
366 ExprMutationAnalyzer::Analyzer::findDeclMutation(ArrayRef<BoundNodes> Matches) {
367   return tryEachMatch<Decl>(Matches, this,
368                             &ExprMutationAnalyzer::Analyzer::findMutation);
369 }
370 
371 const Stmt *ExprMutationAnalyzer::Analyzer::findExprPointeeMutation(
372     ArrayRef<ast_matchers::BoundNodes> Matches) {
373   return tryEachMatch<Expr>(
374       Matches, this, &ExprMutationAnalyzer::Analyzer::findPointeeMutation);
375 }
376 
377 const Stmt *ExprMutationAnalyzer::Analyzer::findDeclPointeeMutation(
378     ArrayRef<ast_matchers::BoundNodes> Matches) {
379   return tryEachMatch<Decl>(
380       Matches, this, &ExprMutationAnalyzer::Analyzer::findPointeeMutation);
381 }
382 
383 const Stmt *
384 ExprMutationAnalyzer::Analyzer::findDirectMutation(const Expr *Exp) {
385   // LHS of any assignment operators.
386   const auto AsAssignmentLhs =
387       binaryOperator(isAssignmentOperator(), hasLHS(canResolveToExpr(Exp)));
388 
389   // Operand of increment/decrement operators.
390   const auto AsIncDecOperand =
391       unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")),
392                     hasUnaryOperand(canResolveToExpr(Exp)));
393 
394   // Invoking non-const member function.
395   // A member function is assumed to be non-const when it is unresolved.
396   const auto NonConstMethod = cxxMethodDecl(unless(isConst()));
397 
398   const auto AsNonConstThis = expr(anyOf(
399       cxxMemberCallExpr(on(canResolveToExpr(Exp)), unless(isConstCallee())),
400       cxxOperatorCallExpr(callee(NonConstMethod),
401                           hasArgument(0, canResolveToExpr(Exp))),
402       // In case of a templated type, calling overloaded operators is not
403       // resolved and modelled as `binaryOperator` on a dependent type.
404       // Such instances are considered a modification, because they can modify
405       // in different instantiations of the template.
406       binaryOperator(isTypeDependent(),
407                      hasEitherOperand(ignoringImpCasts(canResolveToExpr(Exp)))),
408       // A fold expression may contain `Exp` as it's initializer.
409       // We don't know if the operator modifies `Exp` because the
410       // operator is type dependent due to the parameter pack.
411       cxxFoldExpr(hasFoldInit(ignoringImpCasts(canResolveToExpr(Exp)))),
412       // Within class templates and member functions the member expression might
413       // not be resolved. In that case, the `callExpr` is considered to be a
414       // modification.
415       callExpr(callee(expr(anyOf(
416           unresolvedMemberExpr(hasObjectExpression(canResolveToExpr(Exp))),
417           cxxDependentScopeMemberExpr(
418               hasObjectExpression(canResolveToExpr(Exp))))))),
419       // Match on a call to a known method, but the call itself is type
420       // dependent (e.g. `vector<T> v; v.push(T{});` in a templated function).
421       callExpr(allOf(
422           isTypeDependent(),
423           callee(memberExpr(hasDeclaration(NonConstMethod),
424                             hasObjectExpression(canResolveToExpr(Exp))))))));
425 
426   // Taking address of 'Exp'.
427   // We're assuming 'Exp' is mutated as soon as its address is taken, though in
428   // theory we can follow the pointer and see whether it escaped `Stm` or is
429   // dereferenced and then mutated. This is left for future improvements.
430   const auto AsAmpersandOperand =
431       unaryOperator(hasOperatorName("&"),
432                     // A NoOp implicit cast is adding const.
433                     unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))),
434                     hasUnaryOperand(canResolveToExpr(Exp)));
435   const auto AsPointerFromArrayDecay = castExpr(
436       hasCastKind(CK_ArrayToPointerDecay),
437       unless(hasParent(arraySubscriptExpr())), has(canResolveToExpr(Exp)));
438   // Treat calling `operator->()` of move-only classes as taking address.
439   // These are typically smart pointers with unique ownership so we treat
440   // mutation of pointee as mutation of the smart pointer itself.
441   const auto AsOperatorArrowThis = cxxOperatorCallExpr(
442       hasOverloadedOperatorName("->"),
443       callee(
444           cxxMethodDecl(ofClass(isMoveOnly()), returns(nonConstPointerType()))),
445       argumentCountIs(1), hasArgument(0, canResolveToExpr(Exp)));
446 
447   // Used as non-const-ref argument when calling a function.
448   // An argument is assumed to be non-const-ref when the function is unresolved.
449   // Instantiated template functions are not handled here but in
450   // findFunctionArgMutation which has additional smarts for handling forwarding
451   // references.
452   const auto NonConstRefParam = forEachArgumentWithParamType(
453       anyOf(canResolveToExpr(Exp),
454             memberExpr(
455                 hasObjectExpression(ignoringImpCasts(canResolveToExpr(Exp))))),
456       nonConstReferenceType());
457   const auto NotInstantiated = unless(hasDeclaration(isInstantiated()));
458 
459   const auto AsNonConstRefArg =
460       anyOf(callExpr(NonConstRefParam, NotInstantiated),
461             cxxConstructExpr(NonConstRefParam, NotInstantiated),
462             // If the call is type-dependent, we can't properly process any
463             // argument because required type conversions and implicit casts
464             // will be inserted only after specialization.
465             callExpr(isTypeDependent(), hasAnyArgument(canResolveToExpr(Exp))),
466             cxxUnresolvedConstructExpr(hasAnyArgument(canResolveToExpr(Exp))),
467             // Previous False Positive in the following Code:
468             // `template <typename T> void f() { int i = 42; new Type<T>(i); }`
469             // Where the constructor of `Type` takes its argument as reference.
470             // The AST does not resolve in a `cxxConstructExpr` because it is
471             // type-dependent.
472             parenListExpr(hasDescendant(expr(canResolveToExpr(Exp)))),
473             // If the initializer is for a reference type, there is no cast for
474             // the variable. Values are cast to RValue first.
475             initListExpr(hasAnyInit(expr(canResolveToExpr(Exp)))));
476 
477   // Captured by a lambda by reference.
478   // If we're initializing a capture with 'Exp' directly then we're initializing
479   // a reference capture.
480   // For value captures there will be an ImplicitCastExpr <LValueToRValue>.
481   const auto AsLambdaRefCaptureInit = lambdaExpr(hasCaptureInit(Exp));
482 
483   // Returned as non-const-ref.
484   // If we're returning 'Exp' directly then it's returned as non-const-ref.
485   // For returning by value there will be an ImplicitCastExpr <LValueToRValue>.
486   // For returning by const-ref there will be an ImplicitCastExpr <NoOp> (for
487   // adding const.)
488   const auto AsNonConstRefReturn =
489       returnStmt(hasReturnValue(canResolveToExpr(Exp)));
490 
491   // It is used as a non-const-reference for initializing a range-for loop.
492   const auto AsNonConstRefRangeInit = cxxForRangeStmt(hasRangeInit(declRefExpr(
493       allOf(canResolveToExpr(Exp), hasType(nonConstReferenceType())))));
494 
495   const auto Matches = match(
496       traverse(
497           TK_AsIs,
498           findFirst(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand, AsNonConstThis,
499                                AsAmpersandOperand, AsPointerFromArrayDecay,
500                                AsOperatorArrowThis, AsNonConstRefArg,
501                                AsLambdaRefCaptureInit, AsNonConstRefReturn,
502                                AsNonConstRefRangeInit))
503                         .bind("stmt"))),
504       Stm, Context);
505   return selectFirst<Stmt>("stmt", Matches);
506 }
507 
508 const Stmt *
509 ExprMutationAnalyzer::Analyzer::findMemberMutation(const Expr *Exp) {
510   // Check whether any member of 'Exp' is mutated.
511   const auto MemberExprs = match(
512       findAll(expr(anyOf(memberExpr(hasObjectExpression(canResolveToExpr(Exp))),
513                          cxxDependentScopeMemberExpr(
514                              hasObjectExpression(canResolveToExpr(Exp))),
515                          binaryOperator(hasOperatorName(".*"),
516                                         hasLHS(equalsNode(Exp)))))
517                   .bind(NodeID<Expr>::value)),
518       Stm, Context);
519   return findExprMutation(MemberExprs);
520 }
521 
522 const Stmt *
523 ExprMutationAnalyzer::Analyzer::findArrayElementMutation(const Expr *Exp) {
524   // Check whether any element of an array is mutated.
525   const auto SubscriptExprs = match(
526       findAll(arraySubscriptExpr(
527                   anyOf(hasBaseConservative(canResolveToExpr(Exp)),
528                         hasBaseConservative(implicitCastExpr(allOf(
529                             hasCastKind(CK_ArrayToPointerDecay),
530                             hasSourceExpression(canResolveToExpr(Exp)))))))
531                   .bind(NodeID<Expr>::value)),
532       Stm, Context);
533   return findExprMutation(SubscriptExprs);
534 }
535 
536 const Stmt *ExprMutationAnalyzer::Analyzer::findCastMutation(const Expr *Exp) {
537   // If the 'Exp' is explicitly casted to a non-const reference type the
538   // 'Exp' is considered to be modified.
539   const auto ExplicitCast =
540       match(findFirst(stmt(castExpr(hasSourceExpression(canResolveToExpr(Exp)),
541                                     explicitCastExpr(hasDestinationType(
542                                         nonConstReferenceType()))))
543                           .bind("stmt")),
544             Stm, Context);
545 
546   if (const auto *CastStmt = selectFirst<Stmt>("stmt", ExplicitCast))
547     return CastStmt;
548 
549   // If 'Exp' is casted to any non-const reference type, check the castExpr.
550   const auto Casts = match(
551       findAll(expr(castExpr(hasSourceExpression(canResolveToExpr(Exp)),
552                             anyOf(explicitCastExpr(hasDestinationType(
553                                       nonConstReferenceType())),
554                                   implicitCastExpr(hasImplicitDestinationType(
555                                       nonConstReferenceType())))))
556                   .bind(NodeID<Expr>::value)),
557       Stm, Context);
558 
559   if (const Stmt *S = findExprMutation(Casts))
560     return S;
561   // Treat std::{move,forward} as cast.
562   const auto Calls =
563       match(findAll(callExpr(callee(namedDecl(
564                                  hasAnyName("::std::move", "::std::forward"))),
565                              hasArgument(0, canResolveToExpr(Exp)))
566                         .bind("expr")),
567             Stm, Context);
568   return findExprMutation(Calls);
569 }
570 
571 const Stmt *
572 ExprMutationAnalyzer::Analyzer::findRangeLoopMutation(const Expr *Exp) {
573   // Keep the ordering for the specific initialization matches to happen first,
574   // because it is cheaper to match all potential modifications of the loop
575   // variable.
576 
577   // The range variable is a reference to a builtin array. In that case the
578   // array is considered modified if the loop-variable is a non-const reference.
579   const auto DeclStmtToNonRefToArray = declStmt(hasSingleDecl(varDecl(hasType(
580       hasUnqualifiedDesugaredType(referenceType(pointee(arrayType())))))));
581   const auto RefToArrayRefToElements = match(
582       findFirst(stmt(cxxForRangeStmt(
583                          hasLoopVariable(
584                              varDecl(anyOf(hasType(nonConstReferenceType()),
585                                            hasType(nonConstPointerType())))
586                                  .bind(NodeID<Decl>::value)),
587                          hasRangeStmt(DeclStmtToNonRefToArray),
588                          hasRangeInit(canResolveToExpr(Exp))))
589                     .bind("stmt")),
590       Stm, Context);
591 
592   if (const auto *BadRangeInitFromArray =
593           selectFirst<Stmt>("stmt", RefToArrayRefToElements))
594     return BadRangeInitFromArray;
595 
596   // Small helper to match special cases in range-for loops.
597   //
598   // It is possible that containers do not provide a const-overload for their
599   // iterator accessors. If this is the case, the variable is used non-const
600   // no matter what happens in the loop. This requires special detection as it
601   // is then faster to find all mutations of the loop variable.
602   // It aims at a different modification as well.
603   const auto HasAnyNonConstIterator =
604       anyOf(allOf(hasMethod(allOf(hasName("begin"), unless(isConst()))),
605                   unless(hasMethod(allOf(hasName("begin"), isConst())))),
606             allOf(hasMethod(allOf(hasName("end"), unless(isConst()))),
607                   unless(hasMethod(allOf(hasName("end"), isConst())))));
608 
609   const auto DeclStmtToNonConstIteratorContainer = declStmt(
610       hasSingleDecl(varDecl(hasType(hasUnqualifiedDesugaredType(referenceType(
611           pointee(hasDeclaration(cxxRecordDecl(HasAnyNonConstIterator)))))))));
612 
613   const auto RefToContainerBadIterators = match(
614       findFirst(stmt(cxxForRangeStmt(allOf(
615                          hasRangeStmt(DeclStmtToNonConstIteratorContainer),
616                          hasRangeInit(canResolveToExpr(Exp)))))
617                     .bind("stmt")),
618       Stm, Context);
619 
620   if (const auto *BadIteratorsContainer =
621           selectFirst<Stmt>("stmt", RefToContainerBadIterators))
622     return BadIteratorsContainer;
623 
624   // If range for looping over 'Exp' with a non-const reference loop variable,
625   // check all declRefExpr of the loop variable.
626   const auto LoopVars =
627       match(findAll(cxxForRangeStmt(
628                 hasLoopVariable(varDecl(hasType(nonConstReferenceType()))
629                                     .bind(NodeID<Decl>::value)),
630                 hasRangeInit(canResolveToExpr(Exp)))),
631             Stm, Context);
632   return findDeclMutation(LoopVars);
633 }
634 
635 const Stmt *
636 ExprMutationAnalyzer::Analyzer::findReferenceMutation(const Expr *Exp) {
637   // Follow non-const reference returned by `operator*()` of move-only classes.
638   // These are typically smart pointers with unique ownership so we treat
639   // mutation of pointee as mutation of the smart pointer itself.
640   const auto Ref = match(
641       findAll(cxxOperatorCallExpr(
642                   hasOverloadedOperatorName("*"),
643                   callee(cxxMethodDecl(ofClass(isMoveOnly()),
644                                        returns(nonConstReferenceType()))),
645                   argumentCountIs(1), hasArgument(0, canResolveToExpr(Exp)))
646                   .bind(NodeID<Expr>::value)),
647       Stm, Context);
648   if (const Stmt *S = findExprMutation(Ref))
649     return S;
650 
651   // If 'Exp' is bound to a non-const reference, check all declRefExpr to that.
652   const auto Refs = match(
653       stmt(forEachDescendant(
654           varDecl(hasType(nonConstReferenceType()),
655                   hasInitializer(anyOf(
656                       canResolveToExpr(Exp),
657                       memberExpr(hasObjectExpression(canResolveToExpr(Exp))))),
658                   hasParent(declStmt().bind("stmt")),
659                   // Don't follow the reference in range statement, we've
660                   // handled that separately.
661                   unless(hasParent(declStmt(hasParent(cxxForRangeStmt(
662                       hasRangeStmt(equalsBoundNode("stmt"))))))))
663               .bind(NodeID<Decl>::value))),
664       Stm, Context);
665   return findDeclMutation(Refs);
666 }
667 
668 const Stmt *
669 ExprMutationAnalyzer::Analyzer::findFunctionArgMutation(const Expr *Exp) {
670   const auto NonConstRefParam = forEachArgumentWithParam(
671       canResolveToExpr(Exp),
672       parmVarDecl(hasType(nonConstReferenceType())).bind("parm"));
673   const auto IsInstantiated = hasDeclaration(isInstantiated());
674   const auto FuncDecl = hasDeclaration(functionDecl().bind("func"));
675   const auto Matches = match(
676       traverse(
677           TK_AsIs,
678           findAll(
679               expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl,
680                                   unless(callee(namedDecl(hasAnyName(
681                                       "::std::move", "::std::forward"))))),
682                          cxxConstructExpr(NonConstRefParam, IsInstantiated,
683                                           FuncDecl)))
684                   .bind(NodeID<Expr>::value))),
685       Stm, Context);
686   for (const auto &Nodes : Matches) {
687     const auto *Exp = Nodes.getNodeAs<Expr>(NodeID<Expr>::value);
688     const auto *Func = Nodes.getNodeAs<FunctionDecl>("func");
689     if (!Func->getBody() || !Func->getPrimaryTemplate())
690       return Exp;
691 
692     const auto *Parm = Nodes.getNodeAs<ParmVarDecl>("parm");
693     const ArrayRef<ParmVarDecl *> AllParams =
694         Func->getPrimaryTemplate()->getTemplatedDecl()->parameters();
695     QualType ParmType =
696         AllParams[std::min<size_t>(Parm->getFunctionScopeIndex(),
697                                    AllParams.size() - 1)]
698             ->getType();
699     if (const auto *T = ParmType->getAs<PackExpansionType>())
700       ParmType = T->getPattern();
701 
702     // If param type is forwarding reference, follow into the function
703     // definition and see whether the param is mutated inside.
704     if (const auto *RefType = ParmType->getAs<RValueReferenceType>()) {
705       if (!RefType->getPointeeType().getQualifiers() &&
706           RefType->getPointeeType()->getAs<TemplateTypeParmType>()) {
707         FunctionParmMutationAnalyzer *Analyzer =
708             FunctionParmMutationAnalyzer::getFunctionParmMutationAnalyzer(
709                 *Func, Context, Memorized);
710         if (Analyzer->findMutation(Parm))
711           return Exp;
712         continue;
713       }
714     }
715     // Not forwarding reference.
716     return Exp;
717   }
718   return nullptr;
719 }
720 
721 const Stmt *
722 ExprMutationAnalyzer::Analyzer::findPointeeValueMutation(const Expr *Exp) {
723   const auto Matches = match(
724       stmt(forEachDescendant(
725           expr(anyOf(
726                    // deref by *
727                    unaryOperator(hasOperatorName("*"),
728                                  hasUnaryOperand(canResolveToExprPointee(Exp))),
729                    // deref by []
730                    arraySubscriptExpr(
731                        hasBaseConservative(canResolveToExprPointee(Exp)))))
732               .bind(NodeID<Expr>::value))),
733       Stm, Context);
734   return findExprMutation(Matches);
735 }
736 
737 const Stmt *
738 ExprMutationAnalyzer::Analyzer::findPointeeMemberMutation(const Expr *Exp) {
739   const Stmt *MemberCallExpr = selectFirst<Stmt>(
740       "stmt", match(stmt(forEachDescendant(
741                         cxxMemberCallExpr(on(canResolveToExprPointee(Exp)),
742                                           unless(isConstCallee()))
743                             .bind("stmt"))),
744                     Stm, Context));
745   if (MemberCallExpr)
746     return MemberCallExpr;
747   const auto Matches =
748       match(stmt(forEachDescendant(
749                 memberExpr(hasObjectExpression(canResolveToExprPointee(Exp)))
750                     .bind(NodeID<Expr>::value))),
751             Stm, Context);
752   return findExprMutation(Matches);
753 }
754 
755 const Stmt *
756 ExprMutationAnalyzer::Analyzer::findPointeeToNonConst(const Expr *Exp) {
757   const auto NonConstPointerOrDependentType =
758       type(anyOf(nonConstPointerType(), isDependentType()));
759 
760   // assign
761   const auto InitToNonConst =
762       varDecl(hasType(NonConstPointerOrDependentType),
763               hasInitializer(expr(canResolveToExprPointee(Exp)).bind("stmt")));
764   const auto AssignToNonConst =
765       binaryOperation(hasOperatorName("="),
766                       hasLHS(expr(hasType(NonConstPointerOrDependentType))),
767                       hasRHS(canResolveToExprPointee(Exp)));
768   // arguments like
769   const auto ArgOfInstantiationDependent = allOf(
770       hasAnyArgument(canResolveToExprPointee(Exp)), isInstantiationDependent());
771   const auto ArgOfNonConstParameter = forEachArgumentWithParamType(
772       canResolveToExprPointee(Exp), NonConstPointerOrDependentType);
773   const auto CallLikeMatcher =
774       anyOf(ArgOfNonConstParameter, ArgOfInstantiationDependent);
775   const auto PassAsNonConstArg =
776       expr(anyOf(cxxUnresolvedConstructExpr(ArgOfInstantiationDependent),
777                  cxxConstructExpr(CallLikeMatcher), callExpr(CallLikeMatcher),
778                  parenListExpr(has(canResolveToExprPointee(Exp))),
779                  initListExpr(hasAnyInit(canResolveToExprPointee(Exp)))));
780   // cast
781   const auto CastToNonConst =
782       explicitCastExpr(hasSourceExpression(canResolveToExprPointee(Exp)),
783                        hasDestinationType(NonConstPointerOrDependentType));
784 
785   // capture
786   // FIXME: false positive if the pointee does not change in lambda
787   const auto CaptureNoConst = lambdaExpr(hasCaptureInit(Exp));
788 
789   const auto Matches =
790       match(stmt(anyOf(forEachDescendant(
791                            stmt(anyOf(AssignToNonConst, PassAsNonConstArg,
792                                       CastToNonConst, CaptureNoConst))
793                                .bind("stmt")),
794                        forEachDescendant(InitToNonConst))),
795             Stm, Context);
796   return selectFirst<Stmt>("stmt", Matches);
797 }
798 
799 FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer(
800     const FunctionDecl &Func, ASTContext &Context,
801     ExprMutationAnalyzer::Memoized &Memorized)
802     : BodyAnalyzer(*Func.getBody(), Context, Memorized) {
803   if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(&Func)) {
804     // CXXCtorInitializer might also mutate Param but they're not part of
805     // function body, check them eagerly here since they're typically trivial.
806     for (const CXXCtorInitializer *Init : Ctor->inits()) {
807       ExprMutationAnalyzer::Analyzer InitAnalyzer(*Init->getInit(), Context,
808                                                   Memorized);
809       for (const ParmVarDecl *Parm : Ctor->parameters()) {
810         if (Results.contains(Parm))
811           continue;
812         if (const Stmt *S = InitAnalyzer.findMutation(Parm))
813           Results[Parm] = S;
814       }
815     }
816   }
817 }
818 
819 const Stmt *
820 FunctionParmMutationAnalyzer::findMutation(const ParmVarDecl *Parm) {
821   auto [Place, Inserted] = Results.try_emplace(Parm);
822   if (!Inserted)
823     return Place->second;
824 
825   // To handle call A -> call B -> call A. Assume parameters of A is not mutated
826   // before analyzing parameters of A. Then when analyzing the second "call A",
827   // FunctionParmMutationAnalyzer can use this memoized value to avoid infinite
828   // recursion.
829   return Place->second = BodyAnalyzer.findMutation(Parm);
830 }
831 
832 } // namespace clang
833