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