xref: /freebsd/contrib/llvm-project/clang/lib/Analysis/ExprMutationAnalyzer.cpp (revision 59c8e88e72633afbc47a4ace0d2170d00d51f7dc)
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/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/ASTMatchers/ASTMatchers.h"
13 #include "llvm/ADT/STLExtras.h"
14 
15 namespace clang {
16 using namespace ast_matchers;
17 
18 namespace {
19 
20 AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) {
21   return llvm::is_contained(Node.capture_inits(), E);
22 }
23 
24 AST_MATCHER_P(CXXForRangeStmt, hasRangeStmt,
25               ast_matchers::internal::Matcher<DeclStmt>, InnerMatcher) {
26   const DeclStmt *const Range = Node.getRangeStmt();
27   return InnerMatcher.matches(*Range, Finder, Builder);
28 }
29 
30 AST_MATCHER_P(Expr, maybeEvalCommaExpr, ast_matchers::internal::Matcher<Expr>,
31               InnerMatcher) {
32   const Expr *Result = &Node;
33   while (const auto *BOComma =
34              dyn_cast_or_null<BinaryOperator>(Result->IgnoreParens())) {
35     if (!BOComma->isCommaOp())
36       break;
37     Result = BOComma->getRHS();
38   }
39   return InnerMatcher.matches(*Result, Finder, Builder);
40 }
41 
42 AST_MATCHER_P(Stmt, canResolveToExpr, ast_matchers::internal::Matcher<Stmt>,
43               InnerMatcher) {
44   auto *Exp = dyn_cast<Expr>(&Node);
45   if (!Exp) {
46     return stmt().matches(Node, Finder, Builder);
47   }
48 
49   auto DerivedToBase = [](const ast_matchers::internal::Matcher<Expr> &Inner) {
50     return implicitCastExpr(anyOf(hasCastKind(CK_DerivedToBase),
51                                   hasCastKind(CK_UncheckedDerivedToBase)),
52                             hasSourceExpression(Inner));
53   };
54   auto IgnoreDerivedToBase =
55       [&DerivedToBase](const ast_matchers::internal::Matcher<Expr> &Inner) {
56         return ignoringParens(expr(anyOf(Inner, DerivedToBase(Inner))));
57       };
58 
59   // The 'ConditionalOperator' matches on `<anything> ? <expr> : <expr>`.
60   // This matching must be recursive because `<expr>` can be anything resolving
61   // to the `InnerMatcher`, for example another conditional operator.
62   // The edge-case `BaseClass &b = <cond> ? DerivedVar1 : DerivedVar2;`
63   // is handled, too. The implicit cast happens outside of the conditional.
64   // This is matched by `IgnoreDerivedToBase(canResolveToExpr(InnerMatcher))`
65   // below.
66   auto const ConditionalOperator = conditionalOperator(anyOf(
67       hasTrueExpression(ignoringParens(canResolveToExpr(InnerMatcher))),
68       hasFalseExpression(ignoringParens(canResolveToExpr(InnerMatcher)))));
69   auto const ElvisOperator = binaryConditionalOperator(anyOf(
70       hasTrueExpression(ignoringParens(canResolveToExpr(InnerMatcher))),
71       hasFalseExpression(ignoringParens(canResolveToExpr(InnerMatcher)))));
72 
73   auto const ComplexMatcher = ignoringParens(
74       expr(anyOf(IgnoreDerivedToBase(InnerMatcher),
75                  maybeEvalCommaExpr(IgnoreDerivedToBase(InnerMatcher)),
76                  IgnoreDerivedToBase(ConditionalOperator),
77                  IgnoreDerivedToBase(ElvisOperator))));
78 
79   return ComplexMatcher.matches(*Exp, Finder, Builder);
80 }
81 
82 // Similar to 'hasAnyArgument', but does not work because 'InitListExpr' does
83 // not have the 'arguments()' method.
84 AST_MATCHER_P(InitListExpr, hasAnyInit, ast_matchers::internal::Matcher<Expr>,
85               InnerMatcher) {
86   for (const Expr *Arg : Node.inits()) {
87     ast_matchers::internal::BoundNodesTreeBuilder Result(*Builder);
88     if (InnerMatcher.matches(*Arg, Finder, &Result)) {
89       *Builder = std::move(Result);
90       return true;
91     }
92   }
93   return false;
94 }
95 
96 const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXTypeidExpr>
97     cxxTypeidExpr;
98 
99 AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) {
100   return Node.isPotentiallyEvaluated();
101 }
102 
103 AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr,
104               ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
105   if (Node.isTypePredicate())
106     return false;
107   return InnerMatcher.matches(*Node.getControllingExpr(), Finder, Builder);
108 }
109 
110 const auto nonConstReferenceType = [] {
111   return hasUnqualifiedDesugaredType(
112       referenceType(pointee(unless(isConstQualified()))));
113 };
114 
115 const auto nonConstPointerType = [] {
116   return hasUnqualifiedDesugaredType(
117       pointerType(pointee(unless(isConstQualified()))));
118 };
119 
120 const auto isMoveOnly = [] {
121   return cxxRecordDecl(
122       hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted()))),
123       hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted()))),
124       unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(),
125                                                 unless(isDeleted()))),
126                    hasMethod(cxxMethodDecl(isCopyAssignmentOperator(),
127                                            unless(isDeleted()))))));
128 };
129 
130 template <class T> struct NodeID;
131 template <> struct NodeID<Expr> { static constexpr StringRef value = "expr"; };
132 template <> struct NodeID<Decl> { static constexpr StringRef value = "decl"; };
133 constexpr StringRef NodeID<Expr>::value;
134 constexpr StringRef NodeID<Decl>::value;
135 
136 template <class T, class F = const Stmt *(ExprMutationAnalyzer::*)(const T *)>
137 const Stmt *tryEachMatch(ArrayRef<ast_matchers::BoundNodes> Matches,
138                          ExprMutationAnalyzer *Analyzer, F Finder) {
139   const StringRef ID = NodeID<T>::value;
140   for (const auto &Nodes : Matches) {
141     if (const Stmt *S = (Analyzer->*Finder)(Nodes.getNodeAs<T>(ID)))
142       return S;
143   }
144   return nullptr;
145 }
146 
147 } // namespace
148 
149 const Stmt *ExprMutationAnalyzer::findMutation(const Expr *Exp) {
150   return findMutationMemoized(Exp,
151                               {&ExprMutationAnalyzer::findDirectMutation,
152                                &ExprMutationAnalyzer::findMemberMutation,
153                                &ExprMutationAnalyzer::findArrayElementMutation,
154                                &ExprMutationAnalyzer::findCastMutation,
155                                &ExprMutationAnalyzer::findRangeLoopMutation,
156                                &ExprMutationAnalyzer::findReferenceMutation,
157                                &ExprMutationAnalyzer::findFunctionArgMutation},
158                               Results);
159 }
160 
161 const Stmt *ExprMutationAnalyzer::findMutation(const Decl *Dec) {
162   return tryEachDeclRef(Dec, &ExprMutationAnalyzer::findMutation);
163 }
164 
165 const Stmt *ExprMutationAnalyzer::findPointeeMutation(const Expr *Exp) {
166   return findMutationMemoized(Exp, {/*TODO*/}, PointeeResults);
167 }
168 
169 const Stmt *ExprMutationAnalyzer::findPointeeMutation(const Decl *Dec) {
170   return tryEachDeclRef(Dec, &ExprMutationAnalyzer::findPointeeMutation);
171 }
172 
173 const Stmt *ExprMutationAnalyzer::findMutationMemoized(
174     const Expr *Exp, llvm::ArrayRef<MutationFinder> Finders,
175     ResultMap &MemoizedResults) {
176   const auto Memoized = MemoizedResults.find(Exp);
177   if (Memoized != MemoizedResults.end())
178     return Memoized->second;
179 
180   if (isUnevaluated(Exp))
181     return MemoizedResults[Exp] = nullptr;
182 
183   for (const auto &Finder : Finders) {
184     if (const Stmt *S = (this->*Finder)(Exp))
185       return MemoizedResults[Exp] = S;
186   }
187 
188   return MemoizedResults[Exp] = nullptr;
189 }
190 
191 const Stmt *ExprMutationAnalyzer::tryEachDeclRef(const Decl *Dec,
192                                                  MutationFinder Finder) {
193   const auto Refs =
194       match(findAll(declRefExpr(to(equalsNode(Dec))).bind(NodeID<Expr>::value)),
195             Stm, Context);
196   for (const auto &RefNodes : Refs) {
197     const auto *E = RefNodes.getNodeAs<Expr>(NodeID<Expr>::value);
198     if ((this->*Finder)(E))
199       return E;
200   }
201   return nullptr;
202 }
203 
204 bool ExprMutationAnalyzer::isUnevaluated(const Stmt *Exp, const Stmt &Stm,
205                                          ASTContext &Context) {
206   return selectFirst<Stmt>(
207              NodeID<Expr>::value,
208              match(
209                  findAll(
210                      stmt(canResolveToExpr(equalsNode(Exp)),
211                           anyOf(
212                               // `Exp` is part of the underlying expression of
213                               // decltype/typeof if it has an ancestor of
214                               // typeLoc.
215                               hasAncestor(typeLoc(unless(
216                                   hasAncestor(unaryExprOrTypeTraitExpr())))),
217                               hasAncestor(expr(anyOf(
218                                   // `UnaryExprOrTypeTraitExpr` is unevaluated
219                                   // unless it's sizeof on VLA.
220                                   unaryExprOrTypeTraitExpr(unless(sizeOfExpr(
221                                       hasArgumentOfType(variableArrayType())))),
222                                   // `CXXTypeidExpr` is unevaluated unless it's
223                                   // applied to an expression of glvalue of
224                                   // polymorphic class type.
225                                   cxxTypeidExpr(
226                                       unless(isPotentiallyEvaluated())),
227                                   // The controlling expression of
228                                   // `GenericSelectionExpr` is unevaluated.
229                                   genericSelectionExpr(hasControllingExpr(
230                                       hasDescendant(equalsNode(Exp)))),
231                                   cxxNoexceptExpr())))))
232                          .bind(NodeID<Expr>::value)),
233                  Stm, Context)) != nullptr;
234 }
235 
236 bool ExprMutationAnalyzer::isUnevaluated(const Expr *Exp) {
237   return isUnevaluated(Exp, Stm, Context);
238 }
239 
240 const Stmt *
241 ExprMutationAnalyzer::findExprMutation(ArrayRef<BoundNodes> Matches) {
242   return tryEachMatch<Expr>(Matches, this, &ExprMutationAnalyzer::findMutation);
243 }
244 
245 const Stmt *
246 ExprMutationAnalyzer::findDeclMutation(ArrayRef<BoundNodes> Matches) {
247   return tryEachMatch<Decl>(Matches, this, &ExprMutationAnalyzer::findMutation);
248 }
249 
250 const Stmt *ExprMutationAnalyzer::findExprPointeeMutation(
251     ArrayRef<ast_matchers::BoundNodes> Matches) {
252   return tryEachMatch<Expr>(Matches, this,
253                             &ExprMutationAnalyzer::findPointeeMutation);
254 }
255 
256 const Stmt *ExprMutationAnalyzer::findDeclPointeeMutation(
257     ArrayRef<ast_matchers::BoundNodes> Matches) {
258   return tryEachMatch<Decl>(Matches, this,
259                             &ExprMutationAnalyzer::findPointeeMutation);
260 }
261 
262 const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) {
263   // LHS of any assignment operators.
264   const auto AsAssignmentLhs = binaryOperator(
265       isAssignmentOperator(), hasLHS(canResolveToExpr(equalsNode(Exp))));
266 
267   // Operand of increment/decrement operators.
268   const auto AsIncDecOperand =
269       unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")),
270                     hasUnaryOperand(canResolveToExpr(equalsNode(Exp))));
271 
272   // Invoking non-const member function.
273   // A member function is assumed to be non-const when it is unresolved.
274   const auto NonConstMethod = cxxMethodDecl(unless(isConst()));
275 
276   const auto AsNonConstThis = expr(anyOf(
277       cxxMemberCallExpr(callee(NonConstMethod),
278                         on(canResolveToExpr(equalsNode(Exp)))),
279       cxxOperatorCallExpr(callee(NonConstMethod),
280                           hasArgument(0, canResolveToExpr(equalsNode(Exp)))),
281       // In case of a templated type, calling overloaded operators is not
282       // resolved and modelled as `binaryOperator` on a dependent type.
283       // Such instances are considered a modification, because they can modify
284       // in different instantiations of the template.
285       binaryOperator(hasEitherOperand(
286           allOf(ignoringImpCasts(canResolveToExpr(equalsNode(Exp))),
287                 isTypeDependent()))),
288       // Within class templates and member functions the member expression might
289       // not be resolved. In that case, the `callExpr` is considered to be a
290       // modification.
291       callExpr(
292           callee(expr(anyOf(unresolvedMemberExpr(hasObjectExpression(
293                                 canResolveToExpr(equalsNode(Exp)))),
294                             cxxDependentScopeMemberExpr(hasObjectExpression(
295                                 canResolveToExpr(equalsNode(Exp)))))))),
296       // Match on a call to a known method, but the call itself is type
297       // dependent (e.g. `vector<T> v; v.push(T{});` in a templated function).
298       callExpr(allOf(isTypeDependent(),
299                      callee(memberExpr(hasDeclaration(NonConstMethod),
300                                        hasObjectExpression(canResolveToExpr(
301                                            equalsNode(Exp)))))))));
302 
303   // Taking address of 'Exp'.
304   // We're assuming 'Exp' is mutated as soon as its address is taken, though in
305   // theory we can follow the pointer and see whether it escaped `Stm` or is
306   // dereferenced and then mutated. This is left for future improvements.
307   const auto AsAmpersandOperand =
308       unaryOperator(hasOperatorName("&"),
309                     // A NoOp implicit cast is adding const.
310                     unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))),
311                     hasUnaryOperand(canResolveToExpr(equalsNode(Exp))));
312   const auto AsPointerFromArrayDecay =
313       castExpr(hasCastKind(CK_ArrayToPointerDecay),
314                unless(hasParent(arraySubscriptExpr())),
315                has(canResolveToExpr(equalsNode(Exp))));
316   // Treat calling `operator->()` of move-only classes as taking address.
317   // These are typically smart pointers with unique ownership so we treat
318   // mutation of pointee as mutation of the smart pointer itself.
319   const auto AsOperatorArrowThis = cxxOperatorCallExpr(
320       hasOverloadedOperatorName("->"),
321       callee(
322           cxxMethodDecl(ofClass(isMoveOnly()), returns(nonConstPointerType()))),
323       argumentCountIs(1), hasArgument(0, canResolveToExpr(equalsNode(Exp))));
324 
325   // Used as non-const-ref argument when calling a function.
326   // An argument is assumed to be non-const-ref when the function is unresolved.
327   // Instantiated template functions are not handled here but in
328   // findFunctionArgMutation which has additional smarts for handling forwarding
329   // references.
330   const auto NonConstRefParam = forEachArgumentWithParamType(
331       anyOf(canResolveToExpr(equalsNode(Exp)),
332             memberExpr(hasObjectExpression(canResolveToExpr(equalsNode(Exp))))),
333       nonConstReferenceType());
334   const auto NotInstantiated = unless(hasDeclaration(isInstantiated()));
335   const auto TypeDependentCallee =
336       callee(expr(anyOf(unresolvedLookupExpr(), unresolvedMemberExpr(),
337                         cxxDependentScopeMemberExpr(),
338                         hasType(templateTypeParmType()), isTypeDependent())));
339 
340   const auto AsNonConstRefArg = anyOf(
341       callExpr(NonConstRefParam, NotInstantiated),
342       cxxConstructExpr(NonConstRefParam, NotInstantiated),
343       callExpr(TypeDependentCallee,
344                hasAnyArgument(canResolveToExpr(equalsNode(Exp)))),
345       cxxUnresolvedConstructExpr(
346           hasAnyArgument(canResolveToExpr(equalsNode(Exp)))),
347       // Previous False Positive in the following Code:
348       // `template <typename T> void f() { int i = 42; new Type<T>(i); }`
349       // Where the constructor of `Type` takes its argument as reference.
350       // The AST does not resolve in a `cxxConstructExpr` because it is
351       // type-dependent.
352       parenListExpr(hasDescendant(expr(canResolveToExpr(equalsNode(Exp))))),
353       // If the initializer is for a reference type, there is no cast for
354       // the variable. Values are cast to RValue first.
355       initListExpr(hasAnyInit(expr(canResolveToExpr(equalsNode(Exp))))));
356 
357   // Captured by a lambda by reference.
358   // If we're initializing a capture with 'Exp' directly then we're initializing
359   // a reference capture.
360   // For value captures there will be an ImplicitCastExpr <LValueToRValue>.
361   const auto AsLambdaRefCaptureInit = lambdaExpr(hasCaptureInit(Exp));
362 
363   // Returned as non-const-ref.
364   // If we're returning 'Exp' directly then it's returned as non-const-ref.
365   // For returning by value there will be an ImplicitCastExpr <LValueToRValue>.
366   // For returning by const-ref there will be an ImplicitCastExpr <NoOp> (for
367   // adding const.)
368   const auto AsNonConstRefReturn =
369       returnStmt(hasReturnValue(canResolveToExpr(equalsNode(Exp))));
370 
371   // It is used as a non-const-reference for initalizing a range-for loop.
372   const auto AsNonConstRefRangeInit = cxxForRangeStmt(
373       hasRangeInit(declRefExpr(allOf(canResolveToExpr(equalsNode(Exp)),
374                                      hasType(nonConstReferenceType())))));
375 
376   const auto Matches = match(
377       traverse(TK_AsIs,
378                findAll(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand,
379                                   AsNonConstThis, AsAmpersandOperand,
380                                   AsPointerFromArrayDecay, AsOperatorArrowThis,
381                                   AsNonConstRefArg, AsLambdaRefCaptureInit,
382                                   AsNonConstRefReturn, AsNonConstRefRangeInit))
383                            .bind("stmt"))),
384       Stm, Context);
385   return selectFirst<Stmt>("stmt", Matches);
386 }
387 
388 const Stmt *ExprMutationAnalyzer::findMemberMutation(const Expr *Exp) {
389   // Check whether any member of 'Exp' is mutated.
390   const auto MemberExprs =
391       match(findAll(expr(anyOf(memberExpr(hasObjectExpression(
392                                    canResolveToExpr(equalsNode(Exp)))),
393                                cxxDependentScopeMemberExpr(hasObjectExpression(
394                                    canResolveToExpr(equalsNode(Exp))))))
395                         .bind(NodeID<Expr>::value)),
396             Stm, Context);
397   return findExprMutation(MemberExprs);
398 }
399 
400 const Stmt *ExprMutationAnalyzer::findArrayElementMutation(const Expr *Exp) {
401   // Check whether any element of an array is mutated.
402   const auto SubscriptExprs =
403       match(findAll(arraySubscriptExpr(
404                         anyOf(hasBase(canResolveToExpr(equalsNode(Exp))),
405                               hasBase(implicitCastExpr(
406                                   allOf(hasCastKind(CK_ArrayToPointerDecay),
407                                         hasSourceExpression(canResolveToExpr(
408                                             equalsNode(Exp))))))))
409                         .bind(NodeID<Expr>::value)),
410             Stm, Context);
411   return findExprMutation(SubscriptExprs);
412 }
413 
414 const Stmt *ExprMutationAnalyzer::findCastMutation(const Expr *Exp) {
415   // If the 'Exp' is explicitly casted to a non-const reference type the
416   // 'Exp' is considered to be modified.
417   const auto ExplicitCast = match(
418       findAll(
419           stmt(castExpr(hasSourceExpression(canResolveToExpr(equalsNode(Exp))),
420                         explicitCastExpr(
421                             hasDestinationType(nonConstReferenceType()))))
422               .bind("stmt")),
423       Stm, Context);
424 
425   if (const auto *CastStmt = selectFirst<Stmt>("stmt", ExplicitCast))
426     return CastStmt;
427 
428   // If 'Exp' is casted to any non-const reference type, check the castExpr.
429   const auto Casts = match(
430       findAll(
431           expr(castExpr(hasSourceExpression(canResolveToExpr(equalsNode(Exp))),
432                         anyOf(explicitCastExpr(
433                                   hasDestinationType(nonConstReferenceType())),
434                               implicitCastExpr(hasImplicitDestinationType(
435                                   nonConstReferenceType())))))
436               .bind(NodeID<Expr>::value)),
437       Stm, Context);
438 
439   if (const Stmt *S = findExprMutation(Casts))
440     return S;
441   // Treat std::{move,forward} as cast.
442   const auto Calls =
443       match(findAll(callExpr(callee(namedDecl(
444                                  hasAnyName("::std::move", "::std::forward"))),
445                              hasArgument(0, canResolveToExpr(equalsNode(Exp))))
446                         .bind("expr")),
447             Stm, Context);
448   return findExprMutation(Calls);
449 }
450 
451 const Stmt *ExprMutationAnalyzer::findRangeLoopMutation(const Expr *Exp) {
452   // Keep the ordering for the specific initialization matches to happen first,
453   // because it is cheaper to match all potential modifications of the loop
454   // variable.
455 
456   // The range variable is a reference to a builtin array. In that case the
457   // array is considered modified if the loop-variable is a non-const reference.
458   const auto DeclStmtToNonRefToArray = declStmt(hasSingleDecl(varDecl(hasType(
459       hasUnqualifiedDesugaredType(referenceType(pointee(arrayType())))))));
460   const auto RefToArrayRefToElements =
461       match(findAll(stmt(cxxForRangeStmt(
462                              hasLoopVariable(
463                                  varDecl(anyOf(hasType(nonConstReferenceType()),
464                                                hasType(nonConstPointerType())))
465                                      .bind(NodeID<Decl>::value)),
466                              hasRangeStmt(DeclStmtToNonRefToArray),
467                              hasRangeInit(canResolveToExpr(equalsNode(Exp)))))
468                         .bind("stmt")),
469             Stm, Context);
470 
471   if (const auto *BadRangeInitFromArray =
472           selectFirst<Stmt>("stmt", RefToArrayRefToElements))
473     return BadRangeInitFromArray;
474 
475   // Small helper to match special cases in range-for loops.
476   //
477   // It is possible that containers do not provide a const-overload for their
478   // iterator accessors. If this is the case, the variable is used non-const
479   // no matter what happens in the loop. This requires special detection as it
480   // is then faster to find all mutations of the loop variable.
481   // It aims at a different modification as well.
482   const auto HasAnyNonConstIterator =
483       anyOf(allOf(hasMethod(allOf(hasName("begin"), unless(isConst()))),
484                   unless(hasMethod(allOf(hasName("begin"), isConst())))),
485             allOf(hasMethod(allOf(hasName("end"), unless(isConst()))),
486                   unless(hasMethod(allOf(hasName("end"), isConst())))));
487 
488   const auto DeclStmtToNonConstIteratorContainer = declStmt(
489       hasSingleDecl(varDecl(hasType(hasUnqualifiedDesugaredType(referenceType(
490           pointee(hasDeclaration(cxxRecordDecl(HasAnyNonConstIterator)))))))));
491 
492   const auto RefToContainerBadIterators =
493       match(findAll(stmt(cxxForRangeStmt(allOf(
494                              hasRangeStmt(DeclStmtToNonConstIteratorContainer),
495                              hasRangeInit(canResolveToExpr(equalsNode(Exp))))))
496                         .bind("stmt")),
497             Stm, Context);
498 
499   if (const auto *BadIteratorsContainer =
500           selectFirst<Stmt>("stmt", RefToContainerBadIterators))
501     return BadIteratorsContainer;
502 
503   // If range for looping over 'Exp' with a non-const reference loop variable,
504   // check all declRefExpr of the loop variable.
505   const auto LoopVars =
506       match(findAll(cxxForRangeStmt(
507                 hasLoopVariable(varDecl(hasType(nonConstReferenceType()))
508                                     .bind(NodeID<Decl>::value)),
509                 hasRangeInit(canResolveToExpr(equalsNode(Exp))))),
510             Stm, Context);
511   return findDeclMutation(LoopVars);
512 }
513 
514 const Stmt *ExprMutationAnalyzer::findReferenceMutation(const Expr *Exp) {
515   // Follow non-const reference returned by `operator*()` of move-only classes.
516   // These are typically smart pointers with unique ownership so we treat
517   // mutation of pointee as mutation of the smart pointer itself.
518   const auto Ref =
519       match(findAll(cxxOperatorCallExpr(
520                         hasOverloadedOperatorName("*"),
521                         callee(cxxMethodDecl(ofClass(isMoveOnly()),
522                                              returns(nonConstReferenceType()))),
523                         argumentCountIs(1),
524                         hasArgument(0, canResolveToExpr(equalsNode(Exp))))
525                         .bind(NodeID<Expr>::value)),
526             Stm, Context);
527   if (const Stmt *S = findExprMutation(Ref))
528     return S;
529 
530   // If 'Exp' is bound to a non-const reference, check all declRefExpr to that.
531   const auto Refs = match(
532       stmt(forEachDescendant(
533           varDecl(
534               hasType(nonConstReferenceType()),
535               hasInitializer(anyOf(canResolveToExpr(equalsNode(Exp)),
536                                    memberExpr(hasObjectExpression(
537                                        canResolveToExpr(equalsNode(Exp)))))),
538               hasParent(declStmt().bind("stmt")),
539               // Don't follow the reference in range statement, we've
540               // handled that separately.
541               unless(hasParent(declStmt(hasParent(
542                   cxxForRangeStmt(hasRangeStmt(equalsBoundNode("stmt"))))))))
543               .bind(NodeID<Decl>::value))),
544       Stm, Context);
545   return findDeclMutation(Refs);
546 }
547 
548 const Stmt *ExprMutationAnalyzer::findFunctionArgMutation(const Expr *Exp) {
549   const auto NonConstRefParam = forEachArgumentWithParam(
550       canResolveToExpr(equalsNode(Exp)),
551       parmVarDecl(hasType(nonConstReferenceType())).bind("parm"));
552   const auto IsInstantiated = hasDeclaration(isInstantiated());
553   const auto FuncDecl = hasDeclaration(functionDecl().bind("func"));
554   const auto Matches = match(
555       traverse(
556           TK_AsIs,
557           findAll(
558               expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl,
559                                   unless(callee(namedDecl(hasAnyName(
560                                       "::std::move", "::std::forward"))))),
561                          cxxConstructExpr(NonConstRefParam, IsInstantiated,
562                                           FuncDecl)))
563                   .bind(NodeID<Expr>::value))),
564       Stm, Context);
565   for (const auto &Nodes : Matches) {
566     const auto *Exp = Nodes.getNodeAs<Expr>(NodeID<Expr>::value);
567     const auto *Func = Nodes.getNodeAs<FunctionDecl>("func");
568     if (!Func->getBody() || !Func->getPrimaryTemplate())
569       return Exp;
570 
571     const auto *Parm = Nodes.getNodeAs<ParmVarDecl>("parm");
572     const ArrayRef<ParmVarDecl *> AllParams =
573         Func->getPrimaryTemplate()->getTemplatedDecl()->parameters();
574     QualType ParmType =
575         AllParams[std::min<size_t>(Parm->getFunctionScopeIndex(),
576                                    AllParams.size() - 1)]
577             ->getType();
578     if (const auto *T = ParmType->getAs<PackExpansionType>())
579       ParmType = T->getPattern();
580 
581     // If param type is forwarding reference, follow into the function
582     // definition and see whether the param is mutated inside.
583     if (const auto *RefType = ParmType->getAs<RValueReferenceType>()) {
584       if (!RefType->getPointeeType().getQualifiers() &&
585           RefType->getPointeeType()->getAs<TemplateTypeParmType>()) {
586         std::unique_ptr<FunctionParmMutationAnalyzer> &Analyzer =
587             FuncParmAnalyzer[Func];
588         if (!Analyzer)
589           Analyzer.reset(new FunctionParmMutationAnalyzer(*Func, Context));
590         if (Analyzer->findMutation(Parm))
591           return Exp;
592         continue;
593       }
594     }
595     // Not forwarding reference.
596     return Exp;
597   }
598   return nullptr;
599 }
600 
601 FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer(
602     const FunctionDecl &Func, ASTContext &Context)
603     : BodyAnalyzer(*Func.getBody(), Context) {
604   if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(&Func)) {
605     // CXXCtorInitializer might also mutate Param but they're not part of
606     // function body, check them eagerly here since they're typically trivial.
607     for (const CXXCtorInitializer *Init : Ctor->inits()) {
608       ExprMutationAnalyzer InitAnalyzer(*Init->getInit(), Context);
609       for (const ParmVarDecl *Parm : Ctor->parameters()) {
610         if (Results.contains(Parm))
611           continue;
612         if (const Stmt *S = InitAnalyzer.findMutation(Parm))
613           Results[Parm] = S;
614       }
615     }
616   }
617 }
618 
619 const Stmt *
620 FunctionParmMutationAnalyzer::findMutation(const ParmVarDecl *Parm) {
621   const auto Memoized = Results.find(Parm);
622   if (Memoized != Results.end())
623     return Memoized->second;
624 
625   if (const Stmt *S = BodyAnalyzer.findMutation(Parm))
626     return Results[Parm] = S;
627 
628   return Results[Parm] = nullptr;
629 }
630 
631 } // namespace clang
632