xref: /freebsd/contrib/llvm-project/clang/lib/Analysis/ExprMutationAnalyzer.cpp (revision 297eecfb02bb25902531dbb5c3b9a88caf8adf29)
10b57cec5SDimitry Andric //===---------- ExprMutationAnalyzer.cpp ----------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric #include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
9e8d8bef9SDimitry Andric #include "clang/AST/Expr.h"
10e8d8bef9SDimitry Andric #include "clang/AST/OperationKinds.h"
110b57cec5SDimitry Andric #include "clang/ASTMatchers/ASTMatchFinder.h"
12e8d8bef9SDimitry Andric #include "clang/ASTMatchers/ASTMatchers.h"
130b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h"
140b57cec5SDimitry Andric 
150b57cec5SDimitry Andric namespace clang {
160b57cec5SDimitry Andric using namespace ast_matchers;
170b57cec5SDimitry Andric 
18*297eecfbSDimitry Andric // Check if result of Source expression could be a Target expression.
19*297eecfbSDimitry Andric // Checks:
20*297eecfbSDimitry Andric //  - Implicit Casts
21*297eecfbSDimitry Andric //  - Binary Operators
22*297eecfbSDimitry Andric //  - ConditionalOperator
23*297eecfbSDimitry Andric //  - BinaryConditionalOperator
24*297eecfbSDimitry Andric static bool canExprResolveTo(const Expr *Source, const Expr *Target) {
25*297eecfbSDimitry Andric 
26*297eecfbSDimitry Andric   const auto IgnoreDerivedToBase = [](const Expr *E, auto Matcher) {
27*297eecfbSDimitry Andric     if (Matcher(E))
28*297eecfbSDimitry Andric       return true;
29*297eecfbSDimitry Andric     if (const auto *Cast = dyn_cast<ImplicitCastExpr>(E)) {
30*297eecfbSDimitry Andric       if ((Cast->getCastKind() == CK_DerivedToBase ||
31*297eecfbSDimitry Andric            Cast->getCastKind() == CK_UncheckedDerivedToBase) &&
32*297eecfbSDimitry Andric           Matcher(Cast->getSubExpr()))
33*297eecfbSDimitry Andric         return true;
34*297eecfbSDimitry Andric     }
35*297eecfbSDimitry Andric     return false;
36*297eecfbSDimitry Andric   };
37*297eecfbSDimitry Andric 
38*297eecfbSDimitry Andric   const auto EvalCommaExpr = [](const Expr *E, auto Matcher) {
39*297eecfbSDimitry Andric     const Expr *Result = E;
40*297eecfbSDimitry Andric     while (const auto *BOComma =
41*297eecfbSDimitry Andric                dyn_cast_or_null<BinaryOperator>(Result->IgnoreParens())) {
42*297eecfbSDimitry Andric       if (!BOComma->isCommaOp())
43*297eecfbSDimitry Andric         break;
44*297eecfbSDimitry Andric       Result = BOComma->getRHS();
45*297eecfbSDimitry Andric     }
46*297eecfbSDimitry Andric 
47*297eecfbSDimitry Andric     return Result != E && Matcher(Result);
48*297eecfbSDimitry Andric   };
49*297eecfbSDimitry Andric 
50*297eecfbSDimitry Andric   // The 'ConditionalOperatorM' matches on `<anything> ? <expr> : <expr>`.
51*297eecfbSDimitry Andric   // This matching must be recursive because `<expr>` can be anything resolving
52*297eecfbSDimitry Andric   // to the `InnerMatcher`, for example another conditional operator.
53*297eecfbSDimitry Andric   // The edge-case `BaseClass &b = <cond> ? DerivedVar1 : DerivedVar2;`
54*297eecfbSDimitry Andric   // is handled, too. The implicit cast happens outside of the conditional.
55*297eecfbSDimitry Andric   // This is matched by `IgnoreDerivedToBase(canResolveToExpr(InnerMatcher))`
56*297eecfbSDimitry Andric   // below.
57*297eecfbSDimitry Andric   const auto ConditionalOperatorM = [Target](const Expr *E) {
58*297eecfbSDimitry Andric     if (const auto *OP = dyn_cast<ConditionalOperator>(E)) {
59*297eecfbSDimitry Andric       if (const auto *TE = OP->getTrueExpr()->IgnoreParens())
60*297eecfbSDimitry Andric         if (canExprResolveTo(TE, Target))
61*297eecfbSDimitry Andric           return true;
62*297eecfbSDimitry Andric       if (const auto *FE = OP->getFalseExpr()->IgnoreParens())
63*297eecfbSDimitry Andric         if (canExprResolveTo(FE, Target))
64*297eecfbSDimitry Andric           return true;
65*297eecfbSDimitry Andric     }
66*297eecfbSDimitry Andric     return false;
67*297eecfbSDimitry Andric   };
68*297eecfbSDimitry Andric 
69*297eecfbSDimitry Andric   const auto ElvisOperator = [Target](const Expr *E) {
70*297eecfbSDimitry Andric     if (const auto *OP = dyn_cast<BinaryConditionalOperator>(E)) {
71*297eecfbSDimitry Andric       if (const auto *TE = OP->getTrueExpr()->IgnoreParens())
72*297eecfbSDimitry Andric         if (canExprResolveTo(TE, Target))
73*297eecfbSDimitry Andric           return true;
74*297eecfbSDimitry Andric       if (const auto *FE = OP->getFalseExpr()->IgnoreParens())
75*297eecfbSDimitry Andric         if (canExprResolveTo(FE, Target))
76*297eecfbSDimitry Andric           return true;
77*297eecfbSDimitry Andric     }
78*297eecfbSDimitry Andric     return false;
79*297eecfbSDimitry Andric   };
80*297eecfbSDimitry Andric 
81*297eecfbSDimitry Andric   const Expr *SourceExprP = Source->IgnoreParens();
82*297eecfbSDimitry Andric   return IgnoreDerivedToBase(SourceExprP,
83*297eecfbSDimitry Andric                              [&](const Expr *E) {
84*297eecfbSDimitry Andric                                return E == Target || ConditionalOperatorM(E) ||
85*297eecfbSDimitry Andric                                       ElvisOperator(E);
86*297eecfbSDimitry Andric                              }) ||
87*297eecfbSDimitry Andric          EvalCommaExpr(SourceExprP, [&](const Expr *E) {
88*297eecfbSDimitry Andric            return IgnoreDerivedToBase(
89*297eecfbSDimitry Andric                E->IgnoreParens(), [&](const Expr *EE) { return EE == Target; });
90*297eecfbSDimitry Andric          });
91*297eecfbSDimitry Andric }
92*297eecfbSDimitry Andric 
930b57cec5SDimitry Andric namespace {
940b57cec5SDimitry Andric 
950b57cec5SDimitry Andric AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) {
960b57cec5SDimitry Andric   return llvm::is_contained(Node.capture_inits(), E);
970b57cec5SDimitry Andric }
980b57cec5SDimitry Andric 
990b57cec5SDimitry Andric AST_MATCHER_P(CXXForRangeStmt, hasRangeStmt,
1000b57cec5SDimitry Andric               ast_matchers::internal::Matcher<DeclStmt>, InnerMatcher) {
1010b57cec5SDimitry Andric   const DeclStmt *const Range = Node.getRangeStmt();
1020b57cec5SDimitry Andric   return InnerMatcher.matches(*Range, Finder, Builder);
1030b57cec5SDimitry Andric }
1040b57cec5SDimitry Andric 
105*297eecfbSDimitry Andric AST_MATCHER_P(Stmt, canResolveToExpr, const Stmt *, Inner) {
10681ad6265SDimitry Andric   auto *Exp = dyn_cast<Expr>(&Node);
107*297eecfbSDimitry Andric   if (!Exp)
108*297eecfbSDimitry Andric     return true;
109*297eecfbSDimitry Andric   auto *Target = dyn_cast<Expr>(Inner);
110*297eecfbSDimitry Andric   if (!Target)
111*297eecfbSDimitry Andric     return false;
112*297eecfbSDimitry Andric   return canExprResolveTo(Exp, Target);
113e8d8bef9SDimitry Andric }
114e8d8bef9SDimitry Andric 
115e8d8bef9SDimitry Andric // Similar to 'hasAnyArgument', but does not work because 'InitListExpr' does
116e8d8bef9SDimitry Andric // not have the 'arguments()' method.
117e8d8bef9SDimitry Andric AST_MATCHER_P(InitListExpr, hasAnyInit, ast_matchers::internal::Matcher<Expr>,
118e8d8bef9SDimitry Andric               InnerMatcher) {
119e8d8bef9SDimitry Andric   for (const Expr *Arg : Node.inits()) {
120e8d8bef9SDimitry Andric     ast_matchers::internal::BoundNodesTreeBuilder Result(*Builder);
121e8d8bef9SDimitry Andric     if (InnerMatcher.matches(*Arg, Finder, &Result)) {
122e8d8bef9SDimitry Andric       *Builder = std::move(Result);
123e8d8bef9SDimitry Andric       return true;
124e8d8bef9SDimitry Andric     }
125e8d8bef9SDimitry Andric   }
126e8d8bef9SDimitry Andric   return false;
127e8d8bef9SDimitry Andric }
128e8d8bef9SDimitry Andric 
1290b57cec5SDimitry Andric const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXTypeidExpr>
1300b57cec5SDimitry Andric     cxxTypeidExpr;
1310b57cec5SDimitry Andric 
1320b57cec5SDimitry Andric AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) {
1330b57cec5SDimitry Andric   return Node.isPotentiallyEvaluated();
1340b57cec5SDimitry Andric }
1350b57cec5SDimitry Andric 
1365f757f3fSDimitry Andric AST_MATCHER(CXXMemberCallExpr, isConstCallee) {
1375f757f3fSDimitry Andric   const Decl *CalleeDecl = Node.getCalleeDecl();
1385f757f3fSDimitry Andric   const auto *VD = dyn_cast_or_null<ValueDecl>(CalleeDecl);
1395f757f3fSDimitry Andric   if (!VD)
1405f757f3fSDimitry Andric     return false;
1415f757f3fSDimitry Andric   const QualType T = VD->getType().getCanonicalType();
1425f757f3fSDimitry Andric   const auto *MPT = dyn_cast<MemberPointerType>(T);
1435f757f3fSDimitry Andric   const auto *FPT = MPT ? cast<FunctionProtoType>(MPT->getPointeeType())
1445f757f3fSDimitry Andric                         : dyn_cast<FunctionProtoType>(T);
1455f757f3fSDimitry Andric   if (!FPT)
1465f757f3fSDimitry Andric     return false;
1475f757f3fSDimitry Andric   return FPT->isConst();
1485f757f3fSDimitry Andric }
1495f757f3fSDimitry Andric 
1500b57cec5SDimitry Andric AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr,
1510b57cec5SDimitry Andric               ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
15206c3fb27SDimitry Andric   if (Node.isTypePredicate())
15306c3fb27SDimitry Andric     return false;
1540b57cec5SDimitry Andric   return InnerMatcher.matches(*Node.getControllingExpr(), Finder, Builder);
1550b57cec5SDimitry Andric }
1560b57cec5SDimitry Andric 
157*297eecfbSDimitry Andric template <typename T>
158*297eecfbSDimitry Andric ast_matchers::internal::Matcher<T>
159*297eecfbSDimitry Andric findFirst(const ast_matchers::internal::Matcher<T> &Matcher) {
160*297eecfbSDimitry Andric   return anyOf(Matcher, hasDescendant(Matcher));
161*297eecfbSDimitry Andric }
162*297eecfbSDimitry Andric 
1630b57cec5SDimitry Andric const auto nonConstReferenceType = [] {
1640b57cec5SDimitry Andric   return hasUnqualifiedDesugaredType(
1650b57cec5SDimitry Andric       referenceType(pointee(unless(isConstQualified()))));
1660b57cec5SDimitry Andric };
1670b57cec5SDimitry Andric 
1680b57cec5SDimitry Andric const auto nonConstPointerType = [] {
1690b57cec5SDimitry Andric   return hasUnqualifiedDesugaredType(
1700b57cec5SDimitry Andric       pointerType(pointee(unless(isConstQualified()))));
1710b57cec5SDimitry Andric };
1720b57cec5SDimitry Andric 
1730b57cec5SDimitry Andric const auto isMoveOnly = [] {
1740b57cec5SDimitry Andric   return cxxRecordDecl(
1750b57cec5SDimitry Andric       hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted()))),
1760b57cec5SDimitry Andric       hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted()))),
1770b57cec5SDimitry Andric       unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(),
1780b57cec5SDimitry Andric                                                 unless(isDeleted()))),
1790b57cec5SDimitry Andric                    hasMethod(cxxMethodDecl(isCopyAssignmentOperator(),
1800b57cec5SDimitry Andric                                            unless(isDeleted()))))));
1810b57cec5SDimitry Andric };
1820b57cec5SDimitry Andric 
1830b57cec5SDimitry Andric template <class T> struct NodeID;
1845ffd83dbSDimitry Andric template <> struct NodeID<Expr> { static constexpr StringRef value = "expr"; };
1855ffd83dbSDimitry Andric template <> struct NodeID<Decl> { static constexpr StringRef value = "decl"; };
1865ffd83dbSDimitry Andric constexpr StringRef NodeID<Expr>::value;
1875ffd83dbSDimitry Andric constexpr StringRef NodeID<Decl>::value;
1880b57cec5SDimitry Andric 
1890b57cec5SDimitry Andric template <class T, class F = const Stmt *(ExprMutationAnalyzer::*)(const T *)>
1900b57cec5SDimitry Andric const Stmt *tryEachMatch(ArrayRef<ast_matchers::BoundNodes> Matches,
1910b57cec5SDimitry Andric                          ExprMutationAnalyzer *Analyzer, F Finder) {
1920b57cec5SDimitry Andric   const StringRef ID = NodeID<T>::value;
1930b57cec5SDimitry Andric   for (const auto &Nodes : Matches) {
1940b57cec5SDimitry Andric     if (const Stmt *S = (Analyzer->*Finder)(Nodes.getNodeAs<T>(ID)))
1950b57cec5SDimitry Andric       return S;
1960b57cec5SDimitry Andric   }
1970b57cec5SDimitry Andric   return nullptr;
1980b57cec5SDimitry Andric }
1990b57cec5SDimitry Andric 
2000b57cec5SDimitry Andric } // namespace
2010b57cec5SDimitry Andric 
2020b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findMutation(const Expr *Exp) {
2030b57cec5SDimitry Andric   return findMutationMemoized(Exp,
2040b57cec5SDimitry Andric                               {&ExprMutationAnalyzer::findDirectMutation,
2050b57cec5SDimitry Andric                                &ExprMutationAnalyzer::findMemberMutation,
2060b57cec5SDimitry Andric                                &ExprMutationAnalyzer::findArrayElementMutation,
2070b57cec5SDimitry Andric                                &ExprMutationAnalyzer::findCastMutation,
2080b57cec5SDimitry Andric                                &ExprMutationAnalyzer::findRangeLoopMutation,
2090b57cec5SDimitry Andric                                &ExprMutationAnalyzer::findReferenceMutation,
2100b57cec5SDimitry Andric                                &ExprMutationAnalyzer::findFunctionArgMutation},
2110b57cec5SDimitry Andric                               Results);
2120b57cec5SDimitry Andric }
2130b57cec5SDimitry Andric 
2140b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findMutation(const Decl *Dec) {
2150b57cec5SDimitry Andric   return tryEachDeclRef(Dec, &ExprMutationAnalyzer::findMutation);
2160b57cec5SDimitry Andric }
2170b57cec5SDimitry Andric 
2180b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findPointeeMutation(const Expr *Exp) {
2190b57cec5SDimitry Andric   return findMutationMemoized(Exp, {/*TODO*/}, PointeeResults);
2200b57cec5SDimitry Andric }
2210b57cec5SDimitry Andric 
2220b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findPointeeMutation(const Decl *Dec) {
2230b57cec5SDimitry Andric   return tryEachDeclRef(Dec, &ExprMutationAnalyzer::findPointeeMutation);
2240b57cec5SDimitry Andric }
2250b57cec5SDimitry Andric 
2260b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findMutationMemoized(
2270b57cec5SDimitry Andric     const Expr *Exp, llvm::ArrayRef<MutationFinder> Finders,
2280b57cec5SDimitry Andric     ResultMap &MemoizedResults) {
2290b57cec5SDimitry Andric   const auto Memoized = MemoizedResults.find(Exp);
2300b57cec5SDimitry Andric   if (Memoized != MemoizedResults.end())
2310b57cec5SDimitry Andric     return Memoized->second;
2320b57cec5SDimitry Andric 
2330b57cec5SDimitry Andric   if (isUnevaluated(Exp))
2340b57cec5SDimitry Andric     return MemoizedResults[Exp] = nullptr;
2350b57cec5SDimitry Andric 
2360b57cec5SDimitry Andric   for (const auto &Finder : Finders) {
2370b57cec5SDimitry Andric     if (const Stmt *S = (this->*Finder)(Exp))
2380b57cec5SDimitry Andric       return MemoizedResults[Exp] = S;
2390b57cec5SDimitry Andric   }
2400b57cec5SDimitry Andric 
2410b57cec5SDimitry Andric   return MemoizedResults[Exp] = nullptr;
2420b57cec5SDimitry Andric }
2430b57cec5SDimitry Andric 
2440b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::tryEachDeclRef(const Decl *Dec,
2450b57cec5SDimitry Andric                                                  MutationFinder Finder) {
2460b57cec5SDimitry Andric   const auto Refs =
2470b57cec5SDimitry Andric       match(findAll(declRefExpr(to(equalsNode(Dec))).bind(NodeID<Expr>::value)),
2480b57cec5SDimitry Andric             Stm, Context);
2490b57cec5SDimitry Andric   for (const auto &RefNodes : Refs) {
2500b57cec5SDimitry Andric     const auto *E = RefNodes.getNodeAs<Expr>(NodeID<Expr>::value);
2510b57cec5SDimitry Andric     if ((this->*Finder)(E))
2520b57cec5SDimitry Andric       return E;
2530b57cec5SDimitry Andric   }
2540b57cec5SDimitry Andric   return nullptr;
2550b57cec5SDimitry Andric }
2560b57cec5SDimitry Andric 
25781ad6265SDimitry Andric bool ExprMutationAnalyzer::isUnevaluated(const Stmt *Exp, const Stmt &Stm,
25881ad6265SDimitry Andric                                          ASTContext &Context) {
25981ad6265SDimitry Andric   return selectFirst<Stmt>(
2600b57cec5SDimitry Andric              NodeID<Expr>::value,
2610b57cec5SDimitry Andric              match(
262*297eecfbSDimitry Andric                  findFirst(
263*297eecfbSDimitry Andric                      stmt(canResolveToExpr(Exp),
2640b57cec5SDimitry Andric                           anyOf(
2650b57cec5SDimitry Andric                               // `Exp` is part of the underlying expression of
2660b57cec5SDimitry Andric                               // decltype/typeof if it has an ancestor of
2670b57cec5SDimitry Andric                               // typeLoc.
2680b57cec5SDimitry Andric                               hasAncestor(typeLoc(unless(
2690b57cec5SDimitry Andric                                   hasAncestor(unaryExprOrTypeTraitExpr())))),
2700b57cec5SDimitry Andric                               hasAncestor(expr(anyOf(
2710b57cec5SDimitry Andric                                   // `UnaryExprOrTypeTraitExpr` is unevaluated
2720b57cec5SDimitry Andric                                   // unless it's sizeof on VLA.
2730b57cec5SDimitry Andric                                   unaryExprOrTypeTraitExpr(unless(sizeOfExpr(
2740b57cec5SDimitry Andric                                       hasArgumentOfType(variableArrayType())))),
2750b57cec5SDimitry Andric                                   // `CXXTypeidExpr` is unevaluated unless it's
2760b57cec5SDimitry Andric                                   // applied to an expression of glvalue of
2770b57cec5SDimitry Andric                                   // polymorphic class type.
2780b57cec5SDimitry Andric                                   cxxTypeidExpr(
2790b57cec5SDimitry Andric                                       unless(isPotentiallyEvaluated())),
2800b57cec5SDimitry Andric                                   // The controlling expression of
2810b57cec5SDimitry Andric                                   // `GenericSelectionExpr` is unevaluated.
2820b57cec5SDimitry Andric                                   genericSelectionExpr(hasControllingExpr(
2830b57cec5SDimitry Andric                                       hasDescendant(equalsNode(Exp)))),
2840b57cec5SDimitry Andric                                   cxxNoexceptExpr())))))
2850b57cec5SDimitry Andric                          .bind(NodeID<Expr>::value)),
2860b57cec5SDimitry Andric                  Stm, Context)) != nullptr;
2870b57cec5SDimitry Andric }
2880b57cec5SDimitry Andric 
28981ad6265SDimitry Andric bool ExprMutationAnalyzer::isUnevaluated(const Expr *Exp) {
29081ad6265SDimitry Andric   return isUnevaluated(Exp, Stm, Context);
29181ad6265SDimitry Andric }
29281ad6265SDimitry Andric 
2930b57cec5SDimitry Andric const Stmt *
2940b57cec5SDimitry Andric ExprMutationAnalyzer::findExprMutation(ArrayRef<BoundNodes> Matches) {
2950b57cec5SDimitry Andric   return tryEachMatch<Expr>(Matches, this, &ExprMutationAnalyzer::findMutation);
2960b57cec5SDimitry Andric }
2970b57cec5SDimitry Andric 
2980b57cec5SDimitry Andric const Stmt *
2990b57cec5SDimitry Andric ExprMutationAnalyzer::findDeclMutation(ArrayRef<BoundNodes> Matches) {
3000b57cec5SDimitry Andric   return tryEachMatch<Decl>(Matches, this, &ExprMutationAnalyzer::findMutation);
3010b57cec5SDimitry Andric }
3020b57cec5SDimitry Andric 
3030b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findExprPointeeMutation(
3040b57cec5SDimitry Andric     ArrayRef<ast_matchers::BoundNodes> Matches) {
3050b57cec5SDimitry Andric   return tryEachMatch<Expr>(Matches, this,
3060b57cec5SDimitry Andric                             &ExprMutationAnalyzer::findPointeeMutation);
3070b57cec5SDimitry Andric }
3080b57cec5SDimitry Andric 
3090b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findDeclPointeeMutation(
3100b57cec5SDimitry Andric     ArrayRef<ast_matchers::BoundNodes> Matches) {
3110b57cec5SDimitry Andric   return tryEachMatch<Decl>(Matches, this,
3120b57cec5SDimitry Andric                             &ExprMutationAnalyzer::findPointeeMutation);
3130b57cec5SDimitry Andric }
3140b57cec5SDimitry Andric 
3150b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) {
3160b57cec5SDimitry Andric   // LHS of any assignment operators.
317*297eecfbSDimitry Andric   const auto AsAssignmentLhs =
318*297eecfbSDimitry Andric       binaryOperator(isAssignmentOperator(), hasLHS(canResolveToExpr(Exp)));
3190b57cec5SDimitry Andric 
3200b57cec5SDimitry Andric   // Operand of increment/decrement operators.
3210b57cec5SDimitry Andric   const auto AsIncDecOperand =
3220b57cec5SDimitry Andric       unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")),
323*297eecfbSDimitry Andric                     hasUnaryOperand(canResolveToExpr(Exp)));
3240b57cec5SDimitry Andric 
3250b57cec5SDimitry Andric   // Invoking non-const member function.
3260b57cec5SDimitry Andric   // A member function is assumed to be non-const when it is unresolved.
3270b57cec5SDimitry Andric   const auto NonConstMethod = cxxMethodDecl(unless(isConst()));
328e8d8bef9SDimitry Andric 
329e8d8bef9SDimitry Andric   const auto AsNonConstThis = expr(anyOf(
330*297eecfbSDimitry Andric       cxxMemberCallExpr(on(canResolveToExpr(Exp)), unless(isConstCallee())),
3310b57cec5SDimitry Andric       cxxOperatorCallExpr(callee(NonConstMethod),
332*297eecfbSDimitry Andric                           hasArgument(0, canResolveToExpr(Exp))),
333e8d8bef9SDimitry Andric       // In case of a templated type, calling overloaded operators is not
334e8d8bef9SDimitry Andric       // resolved and modelled as `binaryOperator` on a dependent type.
335e8d8bef9SDimitry Andric       // Such instances are considered a modification, because they can modify
336e8d8bef9SDimitry Andric       // in different instantiations of the template.
337*297eecfbSDimitry Andric       binaryOperator(isTypeDependent(),
338*297eecfbSDimitry Andric                      hasEitherOperand(ignoringImpCasts(canResolveToExpr(Exp)))),
339e8d8bef9SDimitry Andric       // Within class templates and member functions the member expression might
340e8d8bef9SDimitry Andric       // not be resolved. In that case, the `callExpr` is considered to be a
341e8d8bef9SDimitry Andric       // modification.
342*297eecfbSDimitry Andric       callExpr(callee(expr(anyOf(
343*297eecfbSDimitry Andric           unresolvedMemberExpr(hasObjectExpression(canResolveToExpr(Exp))),
344*297eecfbSDimitry Andric           cxxDependentScopeMemberExpr(
345*297eecfbSDimitry Andric               hasObjectExpression(canResolveToExpr(Exp))))))),
346e8d8bef9SDimitry Andric       // Match on a call to a known method, but the call itself is type
347e8d8bef9SDimitry Andric       // dependent (e.g. `vector<T> v; v.push(T{});` in a templated function).
348*297eecfbSDimitry Andric       callExpr(allOf(
349*297eecfbSDimitry Andric           isTypeDependent(),
350e8d8bef9SDimitry Andric           callee(memberExpr(hasDeclaration(NonConstMethod),
351*297eecfbSDimitry Andric                             hasObjectExpression(canResolveToExpr(Exp))))))));
3520b57cec5SDimitry Andric 
3530b57cec5SDimitry Andric   // Taking address of 'Exp'.
3540b57cec5SDimitry Andric   // We're assuming 'Exp' is mutated as soon as its address is taken, though in
3550b57cec5SDimitry Andric   // theory we can follow the pointer and see whether it escaped `Stm` or is
3560b57cec5SDimitry Andric   // dereferenced and then mutated. This is left for future improvements.
3570b57cec5SDimitry Andric   const auto AsAmpersandOperand =
3580b57cec5SDimitry Andric       unaryOperator(hasOperatorName("&"),
3590b57cec5SDimitry Andric                     // A NoOp implicit cast is adding const.
3600b57cec5SDimitry Andric                     unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))),
361*297eecfbSDimitry Andric                     hasUnaryOperand(canResolveToExpr(Exp)));
362*297eecfbSDimitry Andric   const auto AsPointerFromArrayDecay = castExpr(
363*297eecfbSDimitry Andric       hasCastKind(CK_ArrayToPointerDecay),
364*297eecfbSDimitry Andric       unless(hasParent(arraySubscriptExpr())), has(canResolveToExpr(Exp)));
3650b57cec5SDimitry Andric   // Treat calling `operator->()` of move-only classes as taking address.
3660b57cec5SDimitry Andric   // These are typically smart pointers with unique ownership so we treat
3670b57cec5SDimitry Andric   // mutation of pointee as mutation of the smart pointer itself.
368e8d8bef9SDimitry Andric   const auto AsOperatorArrowThis = cxxOperatorCallExpr(
369e8d8bef9SDimitry Andric       hasOverloadedOperatorName("->"),
370e8d8bef9SDimitry Andric       callee(
371e8d8bef9SDimitry Andric           cxxMethodDecl(ofClass(isMoveOnly()), returns(nonConstPointerType()))),
372*297eecfbSDimitry Andric       argumentCountIs(1), hasArgument(0, canResolveToExpr(Exp)));
3730b57cec5SDimitry Andric 
3740b57cec5SDimitry Andric   // Used as non-const-ref argument when calling a function.
3750b57cec5SDimitry Andric   // An argument is assumed to be non-const-ref when the function is unresolved.
3760b57cec5SDimitry Andric   // Instantiated template functions are not handled here but in
3770b57cec5SDimitry Andric   // findFunctionArgMutation which has additional smarts for handling forwarding
3780b57cec5SDimitry Andric   // references.
379e8d8bef9SDimitry Andric   const auto NonConstRefParam = forEachArgumentWithParamType(
380*297eecfbSDimitry Andric       anyOf(canResolveToExpr(Exp),
381*297eecfbSDimitry Andric             memberExpr(hasObjectExpression(canResolveToExpr(Exp)))),
382e8d8bef9SDimitry Andric       nonConstReferenceType());
3830b57cec5SDimitry Andric   const auto NotInstantiated = unless(hasDeclaration(isInstantiated()));
384e8d8bef9SDimitry Andric   const auto TypeDependentCallee =
385e8d8bef9SDimitry Andric       callee(expr(anyOf(unresolvedLookupExpr(), unresolvedMemberExpr(),
386e8d8bef9SDimitry Andric                         cxxDependentScopeMemberExpr(),
387e8d8bef9SDimitry Andric                         hasType(templateTypeParmType()), isTypeDependent())));
388e8d8bef9SDimitry Andric 
3890b57cec5SDimitry Andric   const auto AsNonConstRefArg = anyOf(
3900b57cec5SDimitry Andric       callExpr(NonConstRefParam, NotInstantiated),
3910b57cec5SDimitry Andric       cxxConstructExpr(NonConstRefParam, NotInstantiated),
392*297eecfbSDimitry Andric       callExpr(TypeDependentCallee, hasAnyArgument(canResolveToExpr(Exp))),
393*297eecfbSDimitry Andric       cxxUnresolvedConstructExpr(hasAnyArgument(canResolveToExpr(Exp))),
394e8d8bef9SDimitry Andric       // Previous False Positive in the following Code:
395e8d8bef9SDimitry Andric       // `template <typename T> void f() { int i = 42; new Type<T>(i); }`
396e8d8bef9SDimitry Andric       // Where the constructor of `Type` takes its argument as reference.
397e8d8bef9SDimitry Andric       // The AST does not resolve in a `cxxConstructExpr` because it is
398e8d8bef9SDimitry Andric       // type-dependent.
399*297eecfbSDimitry Andric       parenListExpr(hasDescendant(expr(canResolveToExpr(Exp)))),
400e8d8bef9SDimitry Andric       // If the initializer is for a reference type, there is no cast for
401e8d8bef9SDimitry Andric       // the variable. Values are cast to RValue first.
402*297eecfbSDimitry Andric       initListExpr(hasAnyInit(expr(canResolveToExpr(Exp)))));
4030b57cec5SDimitry Andric 
4040b57cec5SDimitry Andric   // Captured by a lambda by reference.
4050b57cec5SDimitry Andric   // If we're initializing a capture with 'Exp' directly then we're initializing
4060b57cec5SDimitry Andric   // a reference capture.
4070b57cec5SDimitry Andric   // For value captures there will be an ImplicitCastExpr <LValueToRValue>.
4080b57cec5SDimitry Andric   const auto AsLambdaRefCaptureInit = lambdaExpr(hasCaptureInit(Exp));
4090b57cec5SDimitry Andric 
4100b57cec5SDimitry Andric   // Returned as non-const-ref.
4110b57cec5SDimitry Andric   // If we're returning 'Exp' directly then it's returned as non-const-ref.
4120b57cec5SDimitry Andric   // For returning by value there will be an ImplicitCastExpr <LValueToRValue>.
4130b57cec5SDimitry Andric   // For returning by const-ref there will be an ImplicitCastExpr <NoOp> (for
4140b57cec5SDimitry Andric   // adding const.)
415e8d8bef9SDimitry Andric   const auto AsNonConstRefReturn =
416*297eecfbSDimitry Andric       returnStmt(hasReturnValue(canResolveToExpr(Exp)));
417e8d8bef9SDimitry Andric 
418e8d8bef9SDimitry Andric   // It is used as a non-const-reference for initalizing a range-for loop.
419*297eecfbSDimitry Andric   const auto AsNonConstRefRangeInit = cxxForRangeStmt(hasRangeInit(declRefExpr(
420*297eecfbSDimitry Andric       allOf(canResolveToExpr(Exp), hasType(nonConstReferenceType())))));
4210b57cec5SDimitry Andric 
4225ffd83dbSDimitry Andric   const auto Matches = match(
423*297eecfbSDimitry Andric       traverse(
424*297eecfbSDimitry Andric           TK_AsIs,
425*297eecfbSDimitry Andric           findFirst(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand, AsNonConstThis,
426*297eecfbSDimitry Andric                                AsAmpersandOperand, AsPointerFromArrayDecay,
427*297eecfbSDimitry Andric                                AsOperatorArrowThis, AsNonConstRefArg,
428*297eecfbSDimitry Andric                                AsLambdaRefCaptureInit, AsNonConstRefReturn,
429*297eecfbSDimitry Andric                                AsNonConstRefRangeInit))
4305ffd83dbSDimitry Andric                         .bind("stmt"))),
4310b57cec5SDimitry Andric       Stm, Context);
4320b57cec5SDimitry Andric   return selectFirst<Stmt>("stmt", Matches);
4330b57cec5SDimitry Andric }
4340b57cec5SDimitry Andric 
4350b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findMemberMutation(const Expr *Exp) {
4360b57cec5SDimitry Andric   // Check whether any member of 'Exp' is mutated.
437*297eecfbSDimitry Andric   const auto MemberExprs = match(
438*297eecfbSDimitry Andric       findAll(expr(anyOf(memberExpr(hasObjectExpression(canResolveToExpr(Exp))),
439*297eecfbSDimitry Andric                          cxxDependentScopeMemberExpr(
440*297eecfbSDimitry Andric                              hasObjectExpression(canResolveToExpr(Exp))),
4415f757f3fSDimitry Andric                          binaryOperator(hasOperatorName(".*"),
4425f757f3fSDimitry Andric                                         hasLHS(equalsNode(Exp)))))
4430b57cec5SDimitry Andric                   .bind(NodeID<Expr>::value)),
4440b57cec5SDimitry Andric       Stm, Context);
4450b57cec5SDimitry Andric   return findExprMutation(MemberExprs);
4460b57cec5SDimitry Andric }
4470b57cec5SDimitry Andric 
4480b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findArrayElementMutation(const Expr *Exp) {
4490b57cec5SDimitry Andric   // Check whether any element of an array is mutated.
450*297eecfbSDimitry Andric   const auto SubscriptExprs = match(
451*297eecfbSDimitry Andric       findAll(arraySubscriptExpr(
452*297eecfbSDimitry Andric                   anyOf(hasBase(canResolveToExpr(Exp)),
453*297eecfbSDimitry Andric                         hasBase(implicitCastExpr(allOf(
454*297eecfbSDimitry Andric                             hasCastKind(CK_ArrayToPointerDecay),
455*297eecfbSDimitry Andric                             hasSourceExpression(canResolveToExpr(Exp)))))))
4560b57cec5SDimitry Andric                   .bind(NodeID<Expr>::value)),
4570b57cec5SDimitry Andric       Stm, Context);
4580b57cec5SDimitry Andric   return findExprMutation(SubscriptExprs);
4590b57cec5SDimitry Andric }
4600b57cec5SDimitry Andric 
4610b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findCastMutation(const Expr *Exp) {
462e8d8bef9SDimitry Andric   // If the 'Exp' is explicitly casted to a non-const reference type the
463e8d8bef9SDimitry Andric   // 'Exp' is considered to be modified.
464*297eecfbSDimitry Andric   const auto ExplicitCast =
465*297eecfbSDimitry Andric       match(findFirst(stmt(castExpr(hasSourceExpression(canResolveToExpr(Exp)),
466*297eecfbSDimitry Andric                                     explicitCastExpr(hasDestinationType(
467*297eecfbSDimitry Andric                                         nonConstReferenceType()))))
468e8d8bef9SDimitry Andric                           .bind("stmt")),
469e8d8bef9SDimitry Andric             Stm, Context);
470e8d8bef9SDimitry Andric 
471e8d8bef9SDimitry Andric   if (const auto *CastStmt = selectFirst<Stmt>("stmt", ExplicitCast))
472e8d8bef9SDimitry Andric     return CastStmt;
473e8d8bef9SDimitry Andric 
4740b57cec5SDimitry Andric   // If 'Exp' is casted to any non-const reference type, check the castExpr.
475e8d8bef9SDimitry Andric   const auto Casts = match(
476*297eecfbSDimitry Andric       findAll(expr(castExpr(hasSourceExpression(canResolveToExpr(Exp)),
477*297eecfbSDimitry Andric                             anyOf(explicitCastExpr(hasDestinationType(
478*297eecfbSDimitry Andric                                       nonConstReferenceType())),
4790b57cec5SDimitry Andric                                   implicitCastExpr(hasImplicitDestinationType(
480e8d8bef9SDimitry Andric                                       nonConstReferenceType())))))
4810b57cec5SDimitry Andric                   .bind(NodeID<Expr>::value)),
4820b57cec5SDimitry Andric       Stm, Context);
483e8d8bef9SDimitry Andric 
4840b57cec5SDimitry Andric   if (const Stmt *S = findExprMutation(Casts))
4850b57cec5SDimitry Andric     return S;
4860b57cec5SDimitry Andric   // Treat std::{move,forward} as cast.
4870b57cec5SDimitry Andric   const auto Calls =
4880b57cec5SDimitry Andric       match(findAll(callExpr(callee(namedDecl(
4890b57cec5SDimitry Andric                                  hasAnyName("::std::move", "::std::forward"))),
490*297eecfbSDimitry Andric                              hasArgument(0, canResolveToExpr(Exp)))
4910b57cec5SDimitry Andric                         .bind("expr")),
4920b57cec5SDimitry Andric             Stm, Context);
4930b57cec5SDimitry Andric   return findExprMutation(Calls);
4940b57cec5SDimitry Andric }
4950b57cec5SDimitry Andric 
4960b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findRangeLoopMutation(const Expr *Exp) {
497e8d8bef9SDimitry Andric   // Keep the ordering for the specific initialization matches to happen first,
498e8d8bef9SDimitry Andric   // because it is cheaper to match all potential modifications of the loop
499e8d8bef9SDimitry Andric   // variable.
500e8d8bef9SDimitry Andric 
501e8d8bef9SDimitry Andric   // The range variable is a reference to a builtin array. In that case the
502e8d8bef9SDimitry Andric   // array is considered modified if the loop-variable is a non-const reference.
503e8d8bef9SDimitry Andric   const auto DeclStmtToNonRefToArray = declStmt(hasSingleDecl(varDecl(hasType(
504e8d8bef9SDimitry Andric       hasUnqualifiedDesugaredType(referenceType(pointee(arrayType())))))));
505*297eecfbSDimitry Andric   const auto RefToArrayRefToElements = match(
506*297eecfbSDimitry Andric       findFirst(stmt(cxxForRangeStmt(
507972a253aSDimitry Andric                          hasLoopVariable(
508972a253aSDimitry Andric                              varDecl(anyOf(hasType(nonConstReferenceType()),
509972a253aSDimitry Andric                                            hasType(nonConstPointerType())))
510e8d8bef9SDimitry Andric                                  .bind(NodeID<Decl>::value)),
511e8d8bef9SDimitry Andric                          hasRangeStmt(DeclStmtToNonRefToArray),
512*297eecfbSDimitry Andric                          hasRangeInit(canResolveToExpr(Exp))))
513e8d8bef9SDimitry Andric                     .bind("stmt")),
514e8d8bef9SDimitry Andric       Stm, Context);
515e8d8bef9SDimitry Andric 
516e8d8bef9SDimitry Andric   if (const auto *BadRangeInitFromArray =
517e8d8bef9SDimitry Andric           selectFirst<Stmt>("stmt", RefToArrayRefToElements))
518e8d8bef9SDimitry Andric     return BadRangeInitFromArray;
519e8d8bef9SDimitry Andric 
520e8d8bef9SDimitry Andric   // Small helper to match special cases in range-for loops.
521e8d8bef9SDimitry Andric   //
522e8d8bef9SDimitry Andric   // It is possible that containers do not provide a const-overload for their
523e8d8bef9SDimitry Andric   // iterator accessors. If this is the case, the variable is used non-const
524e8d8bef9SDimitry Andric   // no matter what happens in the loop. This requires special detection as it
525e8d8bef9SDimitry Andric   // is then faster to find all mutations of the loop variable.
526e8d8bef9SDimitry Andric   // It aims at a different modification as well.
527e8d8bef9SDimitry Andric   const auto HasAnyNonConstIterator =
528e8d8bef9SDimitry Andric       anyOf(allOf(hasMethod(allOf(hasName("begin"), unless(isConst()))),
529e8d8bef9SDimitry Andric                   unless(hasMethod(allOf(hasName("begin"), isConst())))),
530e8d8bef9SDimitry Andric             allOf(hasMethod(allOf(hasName("end"), unless(isConst()))),
531e8d8bef9SDimitry Andric                   unless(hasMethod(allOf(hasName("end"), isConst())))));
532e8d8bef9SDimitry Andric 
533e8d8bef9SDimitry Andric   const auto DeclStmtToNonConstIteratorContainer = declStmt(
534e8d8bef9SDimitry Andric       hasSingleDecl(varDecl(hasType(hasUnqualifiedDesugaredType(referenceType(
535e8d8bef9SDimitry Andric           pointee(hasDeclaration(cxxRecordDecl(HasAnyNonConstIterator)))))))));
536e8d8bef9SDimitry Andric 
537*297eecfbSDimitry Andric   const auto RefToContainerBadIterators = match(
538*297eecfbSDimitry Andric       findFirst(stmt(cxxForRangeStmt(allOf(
539e8d8bef9SDimitry Andric                          hasRangeStmt(DeclStmtToNonConstIteratorContainer),
540*297eecfbSDimitry Andric                          hasRangeInit(canResolveToExpr(Exp)))))
541e8d8bef9SDimitry Andric                     .bind("stmt")),
542e8d8bef9SDimitry Andric       Stm, Context);
543e8d8bef9SDimitry Andric 
544e8d8bef9SDimitry Andric   if (const auto *BadIteratorsContainer =
545e8d8bef9SDimitry Andric           selectFirst<Stmt>("stmt", RefToContainerBadIterators))
546e8d8bef9SDimitry Andric     return BadIteratorsContainer;
547e8d8bef9SDimitry Andric 
5480b57cec5SDimitry Andric   // If range for looping over 'Exp' with a non-const reference loop variable,
5490b57cec5SDimitry Andric   // check all declRefExpr of the loop variable.
5500b57cec5SDimitry Andric   const auto LoopVars =
5510b57cec5SDimitry Andric       match(findAll(cxxForRangeStmt(
5520b57cec5SDimitry Andric                 hasLoopVariable(varDecl(hasType(nonConstReferenceType()))
5530b57cec5SDimitry Andric                                     .bind(NodeID<Decl>::value)),
554*297eecfbSDimitry Andric                 hasRangeInit(canResolveToExpr(Exp)))),
5550b57cec5SDimitry Andric             Stm, Context);
5560b57cec5SDimitry Andric   return findDeclMutation(LoopVars);
5570b57cec5SDimitry Andric }
5580b57cec5SDimitry Andric 
5590b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findReferenceMutation(const Expr *Exp) {
5600b57cec5SDimitry Andric   // Follow non-const reference returned by `operator*()` of move-only classes.
5610b57cec5SDimitry Andric   // These are typically smart pointers with unique ownership so we treat
5620b57cec5SDimitry Andric   // mutation of pointee as mutation of the smart pointer itself.
563*297eecfbSDimitry Andric   const auto Ref = match(
564*297eecfbSDimitry Andric       findAll(cxxOperatorCallExpr(
5650b57cec5SDimitry Andric                   hasOverloadedOperatorName("*"),
5660b57cec5SDimitry Andric                   callee(cxxMethodDecl(ofClass(isMoveOnly()),
5670b57cec5SDimitry Andric                                        returns(nonConstReferenceType()))),
568*297eecfbSDimitry Andric                   argumentCountIs(1), hasArgument(0, canResolveToExpr(Exp)))
5690b57cec5SDimitry Andric                   .bind(NodeID<Expr>::value)),
5700b57cec5SDimitry Andric       Stm, Context);
5710b57cec5SDimitry Andric   if (const Stmt *S = findExprMutation(Ref))
5720b57cec5SDimitry Andric     return S;
5730b57cec5SDimitry Andric 
5740b57cec5SDimitry Andric   // If 'Exp' is bound to a non-const reference, check all declRefExpr to that.
5750b57cec5SDimitry Andric   const auto Refs = match(
5760b57cec5SDimitry Andric       stmt(forEachDescendant(
577*297eecfbSDimitry Andric           varDecl(hasType(nonConstReferenceType()),
578*297eecfbSDimitry Andric                   hasInitializer(anyOf(
579*297eecfbSDimitry Andric                       canResolveToExpr(Exp),
580*297eecfbSDimitry Andric                       memberExpr(hasObjectExpression(canResolveToExpr(Exp))))),
5810b57cec5SDimitry Andric                   hasParent(declStmt().bind("stmt")),
582e8d8bef9SDimitry Andric                   // Don't follow the reference in range statement, we've
583e8d8bef9SDimitry Andric                   // handled that separately.
584*297eecfbSDimitry Andric                   unless(hasParent(declStmt(hasParent(cxxForRangeStmt(
585*297eecfbSDimitry Andric                       hasRangeStmt(equalsBoundNode("stmt"))))))))
5860b57cec5SDimitry Andric               .bind(NodeID<Decl>::value))),
5870b57cec5SDimitry Andric       Stm, Context);
5880b57cec5SDimitry Andric   return findDeclMutation(Refs);
5890b57cec5SDimitry Andric }
5900b57cec5SDimitry Andric 
5910b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findFunctionArgMutation(const Expr *Exp) {
5920b57cec5SDimitry Andric   const auto NonConstRefParam = forEachArgumentWithParam(
593*297eecfbSDimitry Andric       canResolveToExpr(Exp),
5940b57cec5SDimitry Andric       parmVarDecl(hasType(nonConstReferenceType())).bind("parm"));
5950b57cec5SDimitry Andric   const auto IsInstantiated = hasDeclaration(isInstantiated());
5960b57cec5SDimitry Andric   const auto FuncDecl = hasDeclaration(functionDecl().bind("func"));
5970b57cec5SDimitry Andric   const auto Matches = match(
5985ffd83dbSDimitry Andric       traverse(
599e8d8bef9SDimitry Andric           TK_AsIs,
6005ffd83dbSDimitry Andric           findAll(
6015ffd83dbSDimitry Andric               expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl,
6020b57cec5SDimitry Andric                                   unless(callee(namedDecl(hasAnyName(
6030b57cec5SDimitry Andric                                       "::std::move", "::std::forward"))))),
6040b57cec5SDimitry Andric                          cxxConstructExpr(NonConstRefParam, IsInstantiated,
6050b57cec5SDimitry Andric                                           FuncDecl)))
6065ffd83dbSDimitry Andric                   .bind(NodeID<Expr>::value))),
6070b57cec5SDimitry Andric       Stm, Context);
6080b57cec5SDimitry Andric   for (const auto &Nodes : Matches) {
6090b57cec5SDimitry Andric     const auto *Exp = Nodes.getNodeAs<Expr>(NodeID<Expr>::value);
6100b57cec5SDimitry Andric     const auto *Func = Nodes.getNodeAs<FunctionDecl>("func");
6110b57cec5SDimitry Andric     if (!Func->getBody() || !Func->getPrimaryTemplate())
6120b57cec5SDimitry Andric       return Exp;
6130b57cec5SDimitry Andric 
6140b57cec5SDimitry Andric     const auto *Parm = Nodes.getNodeAs<ParmVarDecl>("parm");
6150b57cec5SDimitry Andric     const ArrayRef<ParmVarDecl *> AllParams =
6160b57cec5SDimitry Andric         Func->getPrimaryTemplate()->getTemplatedDecl()->parameters();
6170b57cec5SDimitry Andric     QualType ParmType =
6180b57cec5SDimitry Andric         AllParams[std::min<size_t>(Parm->getFunctionScopeIndex(),
6190b57cec5SDimitry Andric                                    AllParams.size() - 1)]
6200b57cec5SDimitry Andric             ->getType();
6210b57cec5SDimitry Andric     if (const auto *T = ParmType->getAs<PackExpansionType>())
6220b57cec5SDimitry Andric       ParmType = T->getPattern();
6230b57cec5SDimitry Andric 
6240b57cec5SDimitry Andric     // If param type is forwarding reference, follow into the function
6250b57cec5SDimitry Andric     // definition and see whether the param is mutated inside.
6260b57cec5SDimitry Andric     if (const auto *RefType = ParmType->getAs<RValueReferenceType>()) {
6270b57cec5SDimitry Andric       if (!RefType->getPointeeType().getQualifiers() &&
6280b57cec5SDimitry Andric           RefType->getPointeeType()->getAs<TemplateTypeParmType>()) {
6290b57cec5SDimitry Andric         std::unique_ptr<FunctionParmMutationAnalyzer> &Analyzer =
6300b57cec5SDimitry Andric             FuncParmAnalyzer[Func];
6310b57cec5SDimitry Andric         if (!Analyzer)
6320b57cec5SDimitry Andric           Analyzer.reset(new FunctionParmMutationAnalyzer(*Func, Context));
6330b57cec5SDimitry Andric         if (Analyzer->findMutation(Parm))
6340b57cec5SDimitry Andric           return Exp;
6350b57cec5SDimitry Andric         continue;
6360b57cec5SDimitry Andric       }
6370b57cec5SDimitry Andric     }
6380b57cec5SDimitry Andric     // Not forwarding reference.
6390b57cec5SDimitry Andric     return Exp;
6400b57cec5SDimitry Andric   }
6410b57cec5SDimitry Andric   return nullptr;
6420b57cec5SDimitry Andric }
6430b57cec5SDimitry Andric 
6440b57cec5SDimitry Andric FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer(
6450b57cec5SDimitry Andric     const FunctionDecl &Func, ASTContext &Context)
6460b57cec5SDimitry Andric     : BodyAnalyzer(*Func.getBody(), Context) {
6470b57cec5SDimitry Andric   if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(&Func)) {
6480b57cec5SDimitry Andric     // CXXCtorInitializer might also mutate Param but they're not part of
6490b57cec5SDimitry Andric     // function body, check them eagerly here since they're typically trivial.
6500b57cec5SDimitry Andric     for (const CXXCtorInitializer *Init : Ctor->inits()) {
6510b57cec5SDimitry Andric       ExprMutationAnalyzer InitAnalyzer(*Init->getInit(), Context);
6520b57cec5SDimitry Andric       for (const ParmVarDecl *Parm : Ctor->parameters()) {
65306c3fb27SDimitry Andric         if (Results.contains(Parm))
6540b57cec5SDimitry Andric           continue;
6550b57cec5SDimitry Andric         if (const Stmt *S = InitAnalyzer.findMutation(Parm))
6560b57cec5SDimitry Andric           Results[Parm] = S;
6570b57cec5SDimitry Andric       }
6580b57cec5SDimitry Andric     }
6590b57cec5SDimitry Andric   }
6600b57cec5SDimitry Andric }
6610b57cec5SDimitry Andric 
6620b57cec5SDimitry Andric const Stmt *
6630b57cec5SDimitry Andric FunctionParmMutationAnalyzer::findMutation(const ParmVarDecl *Parm) {
6640b57cec5SDimitry Andric   const auto Memoized = Results.find(Parm);
6650b57cec5SDimitry Andric   if (Memoized != Results.end())
6660b57cec5SDimitry Andric     return Memoized->second;
6670b57cec5SDimitry Andric 
6680b57cec5SDimitry Andric   if (const Stmt *S = BodyAnalyzer.findMutation(Parm))
6690b57cec5SDimitry Andric     return Results[Parm] = S;
6700b57cec5SDimitry Andric 
6710b57cec5SDimitry Andric   return Results[Parm] = nullptr;
6720b57cec5SDimitry Andric }
6730b57cec5SDimitry Andric 
6740b57cec5SDimitry Andric } // namespace clang
675