xref: /freebsd/contrib/llvm-project/clang/lib/Analysis/ExprMutationAnalyzer.cpp (revision 7a6dacaca14b62ca4b74406814becb87a3fefac0)
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 
18297eecfbSDimitry Andric // Check if result of Source expression could be a Target expression.
19297eecfbSDimitry Andric // Checks:
20297eecfbSDimitry Andric //  - Implicit Casts
21297eecfbSDimitry Andric //  - Binary Operators
22297eecfbSDimitry Andric //  - ConditionalOperator
23297eecfbSDimitry Andric //  - BinaryConditionalOperator
24297eecfbSDimitry Andric static bool canExprResolveTo(const Expr *Source, const Expr *Target) {
25297eecfbSDimitry Andric 
26297eecfbSDimitry Andric   const auto IgnoreDerivedToBase = [](const Expr *E, auto Matcher) {
27297eecfbSDimitry Andric     if (Matcher(E))
28297eecfbSDimitry Andric       return true;
29297eecfbSDimitry Andric     if (const auto *Cast = dyn_cast<ImplicitCastExpr>(E)) {
30297eecfbSDimitry Andric       if ((Cast->getCastKind() == CK_DerivedToBase ||
31297eecfbSDimitry Andric            Cast->getCastKind() == CK_UncheckedDerivedToBase) &&
32297eecfbSDimitry Andric           Matcher(Cast->getSubExpr()))
33297eecfbSDimitry Andric         return true;
34297eecfbSDimitry Andric     }
35297eecfbSDimitry Andric     return false;
36297eecfbSDimitry Andric   };
37297eecfbSDimitry Andric 
38297eecfbSDimitry Andric   const auto EvalCommaExpr = [](const Expr *E, auto Matcher) {
39297eecfbSDimitry Andric     const Expr *Result = E;
40297eecfbSDimitry Andric     while (const auto *BOComma =
41297eecfbSDimitry Andric                dyn_cast_or_null<BinaryOperator>(Result->IgnoreParens())) {
42297eecfbSDimitry Andric       if (!BOComma->isCommaOp())
43297eecfbSDimitry Andric         break;
44297eecfbSDimitry Andric       Result = BOComma->getRHS();
45297eecfbSDimitry Andric     }
46297eecfbSDimitry Andric 
47297eecfbSDimitry Andric     return Result != E && Matcher(Result);
48297eecfbSDimitry Andric   };
49297eecfbSDimitry Andric 
50297eecfbSDimitry Andric   // The 'ConditionalOperatorM' matches on `<anything> ? <expr> : <expr>`.
51297eecfbSDimitry Andric   // This matching must be recursive because `<expr>` can be anything resolving
52297eecfbSDimitry Andric   // to the `InnerMatcher`, for example another conditional operator.
53297eecfbSDimitry Andric   // The edge-case `BaseClass &b = <cond> ? DerivedVar1 : DerivedVar2;`
54297eecfbSDimitry Andric   // is handled, too. The implicit cast happens outside of the conditional.
55297eecfbSDimitry Andric   // This is matched by `IgnoreDerivedToBase(canResolveToExpr(InnerMatcher))`
56297eecfbSDimitry Andric   // below.
57297eecfbSDimitry Andric   const auto ConditionalOperatorM = [Target](const Expr *E) {
58297eecfbSDimitry Andric     if (const auto *OP = dyn_cast<ConditionalOperator>(E)) {
59297eecfbSDimitry Andric       if (const auto *TE = OP->getTrueExpr()->IgnoreParens())
60297eecfbSDimitry Andric         if (canExprResolveTo(TE, Target))
61297eecfbSDimitry Andric           return true;
62297eecfbSDimitry Andric       if (const auto *FE = OP->getFalseExpr()->IgnoreParens())
63297eecfbSDimitry Andric         if (canExprResolveTo(FE, Target))
64297eecfbSDimitry Andric           return true;
65297eecfbSDimitry Andric     }
66297eecfbSDimitry Andric     return false;
67297eecfbSDimitry Andric   };
68297eecfbSDimitry Andric 
69297eecfbSDimitry Andric   const auto ElvisOperator = [Target](const Expr *E) {
70297eecfbSDimitry Andric     if (const auto *OP = dyn_cast<BinaryConditionalOperator>(E)) {
71297eecfbSDimitry Andric       if (const auto *TE = OP->getTrueExpr()->IgnoreParens())
72297eecfbSDimitry Andric         if (canExprResolveTo(TE, Target))
73297eecfbSDimitry Andric           return true;
74297eecfbSDimitry Andric       if (const auto *FE = OP->getFalseExpr()->IgnoreParens())
75297eecfbSDimitry Andric         if (canExprResolveTo(FE, Target))
76297eecfbSDimitry Andric           return true;
77297eecfbSDimitry Andric     }
78297eecfbSDimitry Andric     return false;
79297eecfbSDimitry Andric   };
80297eecfbSDimitry Andric 
81297eecfbSDimitry Andric   const Expr *SourceExprP = Source->IgnoreParens();
82297eecfbSDimitry Andric   return IgnoreDerivedToBase(SourceExprP,
83297eecfbSDimitry Andric                              [&](const Expr *E) {
84297eecfbSDimitry Andric                                return E == Target || ConditionalOperatorM(E) ||
85297eecfbSDimitry Andric                                       ElvisOperator(E);
86297eecfbSDimitry Andric                              }) ||
87297eecfbSDimitry Andric          EvalCommaExpr(SourceExprP, [&](const Expr *E) {
88297eecfbSDimitry Andric            return IgnoreDerivedToBase(
89297eecfbSDimitry Andric                E->IgnoreParens(), [&](const Expr *EE) { return EE == Target; });
90297eecfbSDimitry Andric          });
91297eecfbSDimitry Andric }
92297eecfbSDimitry 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 
105297eecfbSDimitry Andric AST_MATCHER_P(Stmt, canResolveToExpr, const Stmt *, Inner) {
10681ad6265SDimitry Andric   auto *Exp = dyn_cast<Expr>(&Node);
107297eecfbSDimitry Andric   if (!Exp)
108297eecfbSDimitry Andric     return true;
109297eecfbSDimitry Andric   auto *Target = dyn_cast<Expr>(Inner);
110297eecfbSDimitry Andric   if (!Target)
111297eecfbSDimitry Andric     return false;
112297eecfbSDimitry 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 
157297eecfbSDimitry Andric template <typename T>
158297eecfbSDimitry Andric ast_matchers::internal::Matcher<T>
159297eecfbSDimitry Andric findFirst(const ast_matchers::internal::Matcher<T> &Matcher) {
160297eecfbSDimitry Andric   return anyOf(Matcher, hasDescendant(Matcher));
161297eecfbSDimitry Andric }
162297eecfbSDimitry 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) {
246*7a6dacacSDimitry Andric   const auto Refs = match(
247*7a6dacacSDimitry Andric       findAll(
248*7a6dacacSDimitry Andric           declRefExpr(to(
249*7a6dacacSDimitry Andric                           // `Dec` or a binding if `Dec` is a decomposition.
250*7a6dacacSDimitry Andric                           anyOf(equalsNode(Dec),
251*7a6dacacSDimitry Andric                                 bindingDecl(forDecomposition(equalsNode(Dec))))
252*7a6dacacSDimitry Andric                           //
253*7a6dacacSDimitry Andric                           ))
254*7a6dacacSDimitry Andric               .bind(NodeID<Expr>::value)),
2550b57cec5SDimitry Andric       Stm, Context);
2560b57cec5SDimitry Andric   for (const auto &RefNodes : Refs) {
2570b57cec5SDimitry Andric     const auto *E = RefNodes.getNodeAs<Expr>(NodeID<Expr>::value);
2580b57cec5SDimitry Andric     if ((this->*Finder)(E))
2590b57cec5SDimitry Andric       return E;
2600b57cec5SDimitry Andric   }
2610b57cec5SDimitry Andric   return nullptr;
2620b57cec5SDimitry Andric }
2630b57cec5SDimitry Andric 
26481ad6265SDimitry Andric bool ExprMutationAnalyzer::isUnevaluated(const Stmt *Exp, const Stmt &Stm,
26581ad6265SDimitry Andric                                          ASTContext &Context) {
26681ad6265SDimitry Andric   return selectFirst<Stmt>(
2670b57cec5SDimitry Andric              NodeID<Expr>::value,
2680b57cec5SDimitry Andric              match(
269297eecfbSDimitry Andric                  findFirst(
270297eecfbSDimitry Andric                      stmt(canResolveToExpr(Exp),
2710b57cec5SDimitry Andric                           anyOf(
2720b57cec5SDimitry Andric                               // `Exp` is part of the underlying expression of
2730b57cec5SDimitry Andric                               // decltype/typeof if it has an ancestor of
2740b57cec5SDimitry Andric                               // typeLoc.
2750b57cec5SDimitry Andric                               hasAncestor(typeLoc(unless(
2760b57cec5SDimitry Andric                                   hasAncestor(unaryExprOrTypeTraitExpr())))),
2770b57cec5SDimitry Andric                               hasAncestor(expr(anyOf(
2780b57cec5SDimitry Andric                                   // `UnaryExprOrTypeTraitExpr` is unevaluated
2790b57cec5SDimitry Andric                                   // unless it's sizeof on VLA.
2800b57cec5SDimitry Andric                                   unaryExprOrTypeTraitExpr(unless(sizeOfExpr(
2810b57cec5SDimitry Andric                                       hasArgumentOfType(variableArrayType())))),
2820b57cec5SDimitry Andric                                   // `CXXTypeidExpr` is unevaluated unless it's
2830b57cec5SDimitry Andric                                   // applied to an expression of glvalue of
2840b57cec5SDimitry Andric                                   // polymorphic class type.
2850b57cec5SDimitry Andric                                   cxxTypeidExpr(
2860b57cec5SDimitry Andric                                       unless(isPotentiallyEvaluated())),
2870b57cec5SDimitry Andric                                   // The controlling expression of
2880b57cec5SDimitry Andric                                   // `GenericSelectionExpr` is unevaluated.
2890b57cec5SDimitry Andric                                   genericSelectionExpr(hasControllingExpr(
2900b57cec5SDimitry Andric                                       hasDescendant(equalsNode(Exp)))),
2910b57cec5SDimitry Andric                                   cxxNoexceptExpr())))))
2920b57cec5SDimitry Andric                          .bind(NodeID<Expr>::value)),
2930b57cec5SDimitry Andric                  Stm, Context)) != nullptr;
2940b57cec5SDimitry Andric }
2950b57cec5SDimitry Andric 
29681ad6265SDimitry Andric bool ExprMutationAnalyzer::isUnevaluated(const Expr *Exp) {
29781ad6265SDimitry Andric   return isUnevaluated(Exp, Stm, Context);
29881ad6265SDimitry Andric }
29981ad6265SDimitry Andric 
3000b57cec5SDimitry Andric const Stmt *
3010b57cec5SDimitry Andric ExprMutationAnalyzer::findExprMutation(ArrayRef<BoundNodes> Matches) {
3020b57cec5SDimitry Andric   return tryEachMatch<Expr>(Matches, this, &ExprMutationAnalyzer::findMutation);
3030b57cec5SDimitry Andric }
3040b57cec5SDimitry Andric 
3050b57cec5SDimitry Andric const Stmt *
3060b57cec5SDimitry Andric ExprMutationAnalyzer::findDeclMutation(ArrayRef<BoundNodes> Matches) {
3070b57cec5SDimitry Andric   return tryEachMatch<Decl>(Matches, this, &ExprMutationAnalyzer::findMutation);
3080b57cec5SDimitry Andric }
3090b57cec5SDimitry Andric 
3100b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findExprPointeeMutation(
3110b57cec5SDimitry Andric     ArrayRef<ast_matchers::BoundNodes> Matches) {
3120b57cec5SDimitry Andric   return tryEachMatch<Expr>(Matches, this,
3130b57cec5SDimitry Andric                             &ExprMutationAnalyzer::findPointeeMutation);
3140b57cec5SDimitry Andric }
3150b57cec5SDimitry Andric 
3160b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findDeclPointeeMutation(
3170b57cec5SDimitry Andric     ArrayRef<ast_matchers::BoundNodes> Matches) {
3180b57cec5SDimitry Andric   return tryEachMatch<Decl>(Matches, this,
3190b57cec5SDimitry Andric                             &ExprMutationAnalyzer::findPointeeMutation);
3200b57cec5SDimitry Andric }
3210b57cec5SDimitry Andric 
3220b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) {
3230b57cec5SDimitry Andric   // LHS of any assignment operators.
324297eecfbSDimitry Andric   const auto AsAssignmentLhs =
325297eecfbSDimitry Andric       binaryOperator(isAssignmentOperator(), hasLHS(canResolveToExpr(Exp)));
3260b57cec5SDimitry Andric 
3270b57cec5SDimitry Andric   // Operand of increment/decrement operators.
3280b57cec5SDimitry Andric   const auto AsIncDecOperand =
3290b57cec5SDimitry Andric       unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")),
330297eecfbSDimitry Andric                     hasUnaryOperand(canResolveToExpr(Exp)));
3310b57cec5SDimitry Andric 
3320b57cec5SDimitry Andric   // Invoking non-const member function.
3330b57cec5SDimitry Andric   // A member function is assumed to be non-const when it is unresolved.
3340b57cec5SDimitry Andric   const auto NonConstMethod = cxxMethodDecl(unless(isConst()));
335e8d8bef9SDimitry Andric 
336e8d8bef9SDimitry Andric   const auto AsNonConstThis = expr(anyOf(
337297eecfbSDimitry Andric       cxxMemberCallExpr(on(canResolveToExpr(Exp)), unless(isConstCallee())),
3380b57cec5SDimitry Andric       cxxOperatorCallExpr(callee(NonConstMethod),
339297eecfbSDimitry Andric                           hasArgument(0, canResolveToExpr(Exp))),
340e8d8bef9SDimitry Andric       // In case of a templated type, calling overloaded operators is not
341e8d8bef9SDimitry Andric       // resolved and modelled as `binaryOperator` on a dependent type.
342e8d8bef9SDimitry Andric       // Such instances are considered a modification, because they can modify
343e8d8bef9SDimitry Andric       // in different instantiations of the template.
344297eecfbSDimitry Andric       binaryOperator(isTypeDependent(),
345297eecfbSDimitry Andric                      hasEitherOperand(ignoringImpCasts(canResolveToExpr(Exp)))),
346*7a6dacacSDimitry Andric       // A fold expression may contain `Exp` as it's initializer.
347*7a6dacacSDimitry Andric       // We don't know if the operator modifies `Exp` because the
348*7a6dacacSDimitry Andric       // operator is type dependent due to the parameter pack.
349*7a6dacacSDimitry Andric       cxxFoldExpr(hasFoldInit(ignoringImpCasts(canResolveToExpr(Exp)))),
350e8d8bef9SDimitry Andric       // Within class templates and member functions the member expression might
351e8d8bef9SDimitry Andric       // not be resolved. In that case, the `callExpr` is considered to be a
352e8d8bef9SDimitry Andric       // modification.
353297eecfbSDimitry Andric       callExpr(callee(expr(anyOf(
354297eecfbSDimitry Andric           unresolvedMemberExpr(hasObjectExpression(canResolveToExpr(Exp))),
355297eecfbSDimitry Andric           cxxDependentScopeMemberExpr(
356297eecfbSDimitry Andric               hasObjectExpression(canResolveToExpr(Exp))))))),
357e8d8bef9SDimitry Andric       // Match on a call to a known method, but the call itself is type
358e8d8bef9SDimitry Andric       // dependent (e.g. `vector<T> v; v.push(T{});` in a templated function).
359297eecfbSDimitry Andric       callExpr(allOf(
360297eecfbSDimitry Andric           isTypeDependent(),
361e8d8bef9SDimitry Andric           callee(memberExpr(hasDeclaration(NonConstMethod),
362297eecfbSDimitry Andric                             hasObjectExpression(canResolveToExpr(Exp))))))));
3630b57cec5SDimitry Andric 
3640b57cec5SDimitry Andric   // Taking address of 'Exp'.
3650b57cec5SDimitry Andric   // We're assuming 'Exp' is mutated as soon as its address is taken, though in
3660b57cec5SDimitry Andric   // theory we can follow the pointer and see whether it escaped `Stm` or is
3670b57cec5SDimitry Andric   // dereferenced and then mutated. This is left for future improvements.
3680b57cec5SDimitry Andric   const auto AsAmpersandOperand =
3690b57cec5SDimitry Andric       unaryOperator(hasOperatorName("&"),
3700b57cec5SDimitry Andric                     // A NoOp implicit cast is adding const.
3710b57cec5SDimitry Andric                     unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))),
372297eecfbSDimitry Andric                     hasUnaryOperand(canResolveToExpr(Exp)));
373297eecfbSDimitry Andric   const auto AsPointerFromArrayDecay = castExpr(
374297eecfbSDimitry Andric       hasCastKind(CK_ArrayToPointerDecay),
375297eecfbSDimitry Andric       unless(hasParent(arraySubscriptExpr())), has(canResolveToExpr(Exp)));
3760b57cec5SDimitry Andric   // Treat calling `operator->()` of move-only classes as taking address.
3770b57cec5SDimitry Andric   // These are typically smart pointers with unique ownership so we treat
3780b57cec5SDimitry Andric   // mutation of pointee as mutation of the smart pointer itself.
379e8d8bef9SDimitry Andric   const auto AsOperatorArrowThis = cxxOperatorCallExpr(
380e8d8bef9SDimitry Andric       hasOverloadedOperatorName("->"),
381e8d8bef9SDimitry Andric       callee(
382e8d8bef9SDimitry Andric           cxxMethodDecl(ofClass(isMoveOnly()), returns(nonConstPointerType()))),
383297eecfbSDimitry Andric       argumentCountIs(1), hasArgument(0, canResolveToExpr(Exp)));
3840b57cec5SDimitry Andric 
3850b57cec5SDimitry Andric   // Used as non-const-ref argument when calling a function.
3860b57cec5SDimitry Andric   // An argument is assumed to be non-const-ref when the function is unresolved.
3870b57cec5SDimitry Andric   // Instantiated template functions are not handled here but in
3880b57cec5SDimitry Andric   // findFunctionArgMutation which has additional smarts for handling forwarding
3890b57cec5SDimitry Andric   // references.
390e8d8bef9SDimitry Andric   const auto NonConstRefParam = forEachArgumentWithParamType(
391297eecfbSDimitry Andric       anyOf(canResolveToExpr(Exp),
392297eecfbSDimitry Andric             memberExpr(hasObjectExpression(canResolveToExpr(Exp)))),
393e8d8bef9SDimitry Andric       nonConstReferenceType());
3940b57cec5SDimitry Andric   const auto NotInstantiated = unless(hasDeclaration(isInstantiated()));
395e8d8bef9SDimitry Andric   const auto TypeDependentCallee =
396e8d8bef9SDimitry Andric       callee(expr(anyOf(unresolvedLookupExpr(), unresolvedMemberExpr(),
397e8d8bef9SDimitry Andric                         cxxDependentScopeMemberExpr(),
398e8d8bef9SDimitry Andric                         hasType(templateTypeParmType()), isTypeDependent())));
399e8d8bef9SDimitry Andric 
4000b57cec5SDimitry Andric   const auto AsNonConstRefArg = anyOf(
4010b57cec5SDimitry Andric       callExpr(NonConstRefParam, NotInstantiated),
4020b57cec5SDimitry Andric       cxxConstructExpr(NonConstRefParam, NotInstantiated),
403297eecfbSDimitry Andric       callExpr(TypeDependentCallee, hasAnyArgument(canResolveToExpr(Exp))),
404297eecfbSDimitry Andric       cxxUnresolvedConstructExpr(hasAnyArgument(canResolveToExpr(Exp))),
405e8d8bef9SDimitry Andric       // Previous False Positive in the following Code:
406e8d8bef9SDimitry Andric       // `template <typename T> void f() { int i = 42; new Type<T>(i); }`
407e8d8bef9SDimitry Andric       // Where the constructor of `Type` takes its argument as reference.
408e8d8bef9SDimitry Andric       // The AST does not resolve in a `cxxConstructExpr` because it is
409e8d8bef9SDimitry Andric       // type-dependent.
410297eecfbSDimitry Andric       parenListExpr(hasDescendant(expr(canResolveToExpr(Exp)))),
411e8d8bef9SDimitry Andric       // If the initializer is for a reference type, there is no cast for
412e8d8bef9SDimitry Andric       // the variable. Values are cast to RValue first.
413297eecfbSDimitry Andric       initListExpr(hasAnyInit(expr(canResolveToExpr(Exp)))));
4140b57cec5SDimitry Andric 
4150b57cec5SDimitry Andric   // Captured by a lambda by reference.
4160b57cec5SDimitry Andric   // If we're initializing a capture with 'Exp' directly then we're initializing
4170b57cec5SDimitry Andric   // a reference capture.
4180b57cec5SDimitry Andric   // For value captures there will be an ImplicitCastExpr <LValueToRValue>.
4190b57cec5SDimitry Andric   const auto AsLambdaRefCaptureInit = lambdaExpr(hasCaptureInit(Exp));
4200b57cec5SDimitry Andric 
4210b57cec5SDimitry Andric   // Returned as non-const-ref.
4220b57cec5SDimitry Andric   // If we're returning 'Exp' directly then it's returned as non-const-ref.
4230b57cec5SDimitry Andric   // For returning by value there will be an ImplicitCastExpr <LValueToRValue>.
4240b57cec5SDimitry Andric   // For returning by const-ref there will be an ImplicitCastExpr <NoOp> (for
4250b57cec5SDimitry Andric   // adding const.)
426e8d8bef9SDimitry Andric   const auto AsNonConstRefReturn =
427297eecfbSDimitry Andric       returnStmt(hasReturnValue(canResolveToExpr(Exp)));
428e8d8bef9SDimitry Andric 
429e8d8bef9SDimitry Andric   // It is used as a non-const-reference for initalizing a range-for loop.
430297eecfbSDimitry Andric   const auto AsNonConstRefRangeInit = cxxForRangeStmt(hasRangeInit(declRefExpr(
431297eecfbSDimitry Andric       allOf(canResolveToExpr(Exp), hasType(nonConstReferenceType())))));
4320b57cec5SDimitry Andric 
4335ffd83dbSDimitry Andric   const auto Matches = match(
434297eecfbSDimitry Andric       traverse(
435297eecfbSDimitry Andric           TK_AsIs,
436297eecfbSDimitry Andric           findFirst(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand, AsNonConstThis,
437297eecfbSDimitry Andric                                AsAmpersandOperand, AsPointerFromArrayDecay,
438297eecfbSDimitry Andric                                AsOperatorArrowThis, AsNonConstRefArg,
439297eecfbSDimitry Andric                                AsLambdaRefCaptureInit, AsNonConstRefReturn,
440297eecfbSDimitry Andric                                AsNonConstRefRangeInit))
4415ffd83dbSDimitry Andric                         .bind("stmt"))),
4420b57cec5SDimitry Andric       Stm, Context);
4430b57cec5SDimitry Andric   return selectFirst<Stmt>("stmt", Matches);
4440b57cec5SDimitry Andric }
4450b57cec5SDimitry Andric 
4460b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findMemberMutation(const Expr *Exp) {
4470b57cec5SDimitry Andric   // Check whether any member of 'Exp' is mutated.
448297eecfbSDimitry Andric   const auto MemberExprs = match(
449297eecfbSDimitry Andric       findAll(expr(anyOf(memberExpr(hasObjectExpression(canResolveToExpr(Exp))),
450297eecfbSDimitry Andric                          cxxDependentScopeMemberExpr(
451297eecfbSDimitry Andric                              hasObjectExpression(canResolveToExpr(Exp))),
4525f757f3fSDimitry Andric                          binaryOperator(hasOperatorName(".*"),
4535f757f3fSDimitry Andric                                         hasLHS(equalsNode(Exp)))))
4540b57cec5SDimitry Andric                   .bind(NodeID<Expr>::value)),
4550b57cec5SDimitry Andric       Stm, Context);
4560b57cec5SDimitry Andric   return findExprMutation(MemberExprs);
4570b57cec5SDimitry Andric }
4580b57cec5SDimitry Andric 
4590b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findArrayElementMutation(const Expr *Exp) {
4600b57cec5SDimitry Andric   // Check whether any element of an array is mutated.
461297eecfbSDimitry Andric   const auto SubscriptExprs = match(
462297eecfbSDimitry Andric       findAll(arraySubscriptExpr(
463297eecfbSDimitry Andric                   anyOf(hasBase(canResolveToExpr(Exp)),
464297eecfbSDimitry Andric                         hasBase(implicitCastExpr(allOf(
465297eecfbSDimitry Andric                             hasCastKind(CK_ArrayToPointerDecay),
466297eecfbSDimitry Andric                             hasSourceExpression(canResolveToExpr(Exp)))))))
4670b57cec5SDimitry Andric                   .bind(NodeID<Expr>::value)),
4680b57cec5SDimitry Andric       Stm, Context);
4690b57cec5SDimitry Andric   return findExprMutation(SubscriptExprs);
4700b57cec5SDimitry Andric }
4710b57cec5SDimitry Andric 
4720b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findCastMutation(const Expr *Exp) {
473e8d8bef9SDimitry Andric   // If the 'Exp' is explicitly casted to a non-const reference type the
474e8d8bef9SDimitry Andric   // 'Exp' is considered to be modified.
475297eecfbSDimitry Andric   const auto ExplicitCast =
476297eecfbSDimitry Andric       match(findFirst(stmt(castExpr(hasSourceExpression(canResolveToExpr(Exp)),
477297eecfbSDimitry Andric                                     explicitCastExpr(hasDestinationType(
478297eecfbSDimitry Andric                                         nonConstReferenceType()))))
479e8d8bef9SDimitry Andric                           .bind("stmt")),
480e8d8bef9SDimitry Andric             Stm, Context);
481e8d8bef9SDimitry Andric 
482e8d8bef9SDimitry Andric   if (const auto *CastStmt = selectFirst<Stmt>("stmt", ExplicitCast))
483e8d8bef9SDimitry Andric     return CastStmt;
484e8d8bef9SDimitry Andric 
4850b57cec5SDimitry Andric   // If 'Exp' is casted to any non-const reference type, check the castExpr.
486e8d8bef9SDimitry Andric   const auto Casts = match(
487297eecfbSDimitry Andric       findAll(expr(castExpr(hasSourceExpression(canResolveToExpr(Exp)),
488297eecfbSDimitry Andric                             anyOf(explicitCastExpr(hasDestinationType(
489297eecfbSDimitry Andric                                       nonConstReferenceType())),
4900b57cec5SDimitry Andric                                   implicitCastExpr(hasImplicitDestinationType(
491e8d8bef9SDimitry Andric                                       nonConstReferenceType())))))
4920b57cec5SDimitry Andric                   .bind(NodeID<Expr>::value)),
4930b57cec5SDimitry Andric       Stm, Context);
494e8d8bef9SDimitry Andric 
4950b57cec5SDimitry Andric   if (const Stmt *S = findExprMutation(Casts))
4960b57cec5SDimitry Andric     return S;
4970b57cec5SDimitry Andric   // Treat std::{move,forward} as cast.
4980b57cec5SDimitry Andric   const auto Calls =
4990b57cec5SDimitry Andric       match(findAll(callExpr(callee(namedDecl(
5000b57cec5SDimitry Andric                                  hasAnyName("::std::move", "::std::forward"))),
501297eecfbSDimitry Andric                              hasArgument(0, canResolveToExpr(Exp)))
5020b57cec5SDimitry Andric                         .bind("expr")),
5030b57cec5SDimitry Andric             Stm, Context);
5040b57cec5SDimitry Andric   return findExprMutation(Calls);
5050b57cec5SDimitry Andric }
5060b57cec5SDimitry Andric 
5070b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findRangeLoopMutation(const Expr *Exp) {
508e8d8bef9SDimitry Andric   // Keep the ordering for the specific initialization matches to happen first,
509e8d8bef9SDimitry Andric   // because it is cheaper to match all potential modifications of the loop
510e8d8bef9SDimitry Andric   // variable.
511e8d8bef9SDimitry Andric 
512e8d8bef9SDimitry Andric   // The range variable is a reference to a builtin array. In that case the
513e8d8bef9SDimitry Andric   // array is considered modified if the loop-variable is a non-const reference.
514e8d8bef9SDimitry Andric   const auto DeclStmtToNonRefToArray = declStmt(hasSingleDecl(varDecl(hasType(
515e8d8bef9SDimitry Andric       hasUnqualifiedDesugaredType(referenceType(pointee(arrayType())))))));
516297eecfbSDimitry Andric   const auto RefToArrayRefToElements = match(
517297eecfbSDimitry Andric       findFirst(stmt(cxxForRangeStmt(
518972a253aSDimitry Andric                          hasLoopVariable(
519972a253aSDimitry Andric                              varDecl(anyOf(hasType(nonConstReferenceType()),
520972a253aSDimitry Andric                                            hasType(nonConstPointerType())))
521e8d8bef9SDimitry Andric                                  .bind(NodeID<Decl>::value)),
522e8d8bef9SDimitry Andric                          hasRangeStmt(DeclStmtToNonRefToArray),
523297eecfbSDimitry Andric                          hasRangeInit(canResolveToExpr(Exp))))
524e8d8bef9SDimitry Andric                     .bind("stmt")),
525e8d8bef9SDimitry Andric       Stm, Context);
526e8d8bef9SDimitry Andric 
527e8d8bef9SDimitry Andric   if (const auto *BadRangeInitFromArray =
528e8d8bef9SDimitry Andric           selectFirst<Stmt>("stmt", RefToArrayRefToElements))
529e8d8bef9SDimitry Andric     return BadRangeInitFromArray;
530e8d8bef9SDimitry Andric 
531e8d8bef9SDimitry Andric   // Small helper to match special cases in range-for loops.
532e8d8bef9SDimitry Andric   //
533e8d8bef9SDimitry Andric   // It is possible that containers do not provide a const-overload for their
534e8d8bef9SDimitry Andric   // iterator accessors. If this is the case, the variable is used non-const
535e8d8bef9SDimitry Andric   // no matter what happens in the loop. This requires special detection as it
536e8d8bef9SDimitry Andric   // is then faster to find all mutations of the loop variable.
537e8d8bef9SDimitry Andric   // It aims at a different modification as well.
538e8d8bef9SDimitry Andric   const auto HasAnyNonConstIterator =
539e8d8bef9SDimitry Andric       anyOf(allOf(hasMethod(allOf(hasName("begin"), unless(isConst()))),
540e8d8bef9SDimitry Andric                   unless(hasMethod(allOf(hasName("begin"), isConst())))),
541e8d8bef9SDimitry Andric             allOf(hasMethod(allOf(hasName("end"), unless(isConst()))),
542e8d8bef9SDimitry Andric                   unless(hasMethod(allOf(hasName("end"), isConst())))));
543e8d8bef9SDimitry Andric 
544e8d8bef9SDimitry Andric   const auto DeclStmtToNonConstIteratorContainer = declStmt(
545e8d8bef9SDimitry Andric       hasSingleDecl(varDecl(hasType(hasUnqualifiedDesugaredType(referenceType(
546e8d8bef9SDimitry Andric           pointee(hasDeclaration(cxxRecordDecl(HasAnyNonConstIterator)))))))));
547e8d8bef9SDimitry Andric 
548297eecfbSDimitry Andric   const auto RefToContainerBadIterators = match(
549297eecfbSDimitry Andric       findFirst(stmt(cxxForRangeStmt(allOf(
550e8d8bef9SDimitry Andric                          hasRangeStmt(DeclStmtToNonConstIteratorContainer),
551297eecfbSDimitry Andric                          hasRangeInit(canResolveToExpr(Exp)))))
552e8d8bef9SDimitry Andric                     .bind("stmt")),
553e8d8bef9SDimitry Andric       Stm, Context);
554e8d8bef9SDimitry Andric 
555e8d8bef9SDimitry Andric   if (const auto *BadIteratorsContainer =
556e8d8bef9SDimitry Andric           selectFirst<Stmt>("stmt", RefToContainerBadIterators))
557e8d8bef9SDimitry Andric     return BadIteratorsContainer;
558e8d8bef9SDimitry Andric 
5590b57cec5SDimitry Andric   // If range for looping over 'Exp' with a non-const reference loop variable,
5600b57cec5SDimitry Andric   // check all declRefExpr of the loop variable.
5610b57cec5SDimitry Andric   const auto LoopVars =
5620b57cec5SDimitry Andric       match(findAll(cxxForRangeStmt(
5630b57cec5SDimitry Andric                 hasLoopVariable(varDecl(hasType(nonConstReferenceType()))
5640b57cec5SDimitry Andric                                     .bind(NodeID<Decl>::value)),
565297eecfbSDimitry Andric                 hasRangeInit(canResolveToExpr(Exp)))),
5660b57cec5SDimitry Andric             Stm, Context);
5670b57cec5SDimitry Andric   return findDeclMutation(LoopVars);
5680b57cec5SDimitry Andric }
5690b57cec5SDimitry Andric 
5700b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findReferenceMutation(const Expr *Exp) {
5710b57cec5SDimitry Andric   // Follow non-const reference returned by `operator*()` of move-only classes.
5720b57cec5SDimitry Andric   // These are typically smart pointers with unique ownership so we treat
5730b57cec5SDimitry Andric   // mutation of pointee as mutation of the smart pointer itself.
574297eecfbSDimitry Andric   const auto Ref = match(
575297eecfbSDimitry Andric       findAll(cxxOperatorCallExpr(
5760b57cec5SDimitry Andric                   hasOverloadedOperatorName("*"),
5770b57cec5SDimitry Andric                   callee(cxxMethodDecl(ofClass(isMoveOnly()),
5780b57cec5SDimitry Andric                                        returns(nonConstReferenceType()))),
579297eecfbSDimitry Andric                   argumentCountIs(1), hasArgument(0, canResolveToExpr(Exp)))
5800b57cec5SDimitry Andric                   .bind(NodeID<Expr>::value)),
5810b57cec5SDimitry Andric       Stm, Context);
5820b57cec5SDimitry Andric   if (const Stmt *S = findExprMutation(Ref))
5830b57cec5SDimitry Andric     return S;
5840b57cec5SDimitry Andric 
5850b57cec5SDimitry Andric   // If 'Exp' is bound to a non-const reference, check all declRefExpr to that.
5860b57cec5SDimitry Andric   const auto Refs = match(
5870b57cec5SDimitry Andric       stmt(forEachDescendant(
588297eecfbSDimitry Andric           varDecl(hasType(nonConstReferenceType()),
589297eecfbSDimitry Andric                   hasInitializer(anyOf(
590297eecfbSDimitry Andric                       canResolveToExpr(Exp),
591297eecfbSDimitry Andric                       memberExpr(hasObjectExpression(canResolveToExpr(Exp))))),
5920b57cec5SDimitry Andric                   hasParent(declStmt().bind("stmt")),
593e8d8bef9SDimitry Andric                   // Don't follow the reference in range statement, we've
594e8d8bef9SDimitry Andric                   // handled that separately.
595297eecfbSDimitry Andric                   unless(hasParent(declStmt(hasParent(cxxForRangeStmt(
596297eecfbSDimitry Andric                       hasRangeStmt(equalsBoundNode("stmt"))))))))
5970b57cec5SDimitry Andric               .bind(NodeID<Decl>::value))),
5980b57cec5SDimitry Andric       Stm, Context);
5990b57cec5SDimitry Andric   return findDeclMutation(Refs);
6000b57cec5SDimitry Andric }
6010b57cec5SDimitry Andric 
6020b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findFunctionArgMutation(const Expr *Exp) {
6030b57cec5SDimitry Andric   const auto NonConstRefParam = forEachArgumentWithParam(
604297eecfbSDimitry Andric       canResolveToExpr(Exp),
6050b57cec5SDimitry Andric       parmVarDecl(hasType(nonConstReferenceType())).bind("parm"));
6060b57cec5SDimitry Andric   const auto IsInstantiated = hasDeclaration(isInstantiated());
6070b57cec5SDimitry Andric   const auto FuncDecl = hasDeclaration(functionDecl().bind("func"));
6080b57cec5SDimitry Andric   const auto Matches = match(
6095ffd83dbSDimitry Andric       traverse(
610e8d8bef9SDimitry Andric           TK_AsIs,
6115ffd83dbSDimitry Andric           findAll(
6125ffd83dbSDimitry Andric               expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl,
6130b57cec5SDimitry Andric                                   unless(callee(namedDecl(hasAnyName(
6140b57cec5SDimitry Andric                                       "::std::move", "::std::forward"))))),
6150b57cec5SDimitry Andric                          cxxConstructExpr(NonConstRefParam, IsInstantiated,
6160b57cec5SDimitry Andric                                           FuncDecl)))
6175ffd83dbSDimitry Andric                   .bind(NodeID<Expr>::value))),
6180b57cec5SDimitry Andric       Stm, Context);
6190b57cec5SDimitry Andric   for (const auto &Nodes : Matches) {
6200b57cec5SDimitry Andric     const auto *Exp = Nodes.getNodeAs<Expr>(NodeID<Expr>::value);
6210b57cec5SDimitry Andric     const auto *Func = Nodes.getNodeAs<FunctionDecl>("func");
6220b57cec5SDimitry Andric     if (!Func->getBody() || !Func->getPrimaryTemplate())
6230b57cec5SDimitry Andric       return Exp;
6240b57cec5SDimitry Andric 
6250b57cec5SDimitry Andric     const auto *Parm = Nodes.getNodeAs<ParmVarDecl>("parm");
6260b57cec5SDimitry Andric     const ArrayRef<ParmVarDecl *> AllParams =
6270b57cec5SDimitry Andric         Func->getPrimaryTemplate()->getTemplatedDecl()->parameters();
6280b57cec5SDimitry Andric     QualType ParmType =
6290b57cec5SDimitry Andric         AllParams[std::min<size_t>(Parm->getFunctionScopeIndex(),
6300b57cec5SDimitry Andric                                    AllParams.size() - 1)]
6310b57cec5SDimitry Andric             ->getType();
6320b57cec5SDimitry Andric     if (const auto *T = ParmType->getAs<PackExpansionType>())
6330b57cec5SDimitry Andric       ParmType = T->getPattern();
6340b57cec5SDimitry Andric 
6350b57cec5SDimitry Andric     // If param type is forwarding reference, follow into the function
6360b57cec5SDimitry Andric     // definition and see whether the param is mutated inside.
6370b57cec5SDimitry Andric     if (const auto *RefType = ParmType->getAs<RValueReferenceType>()) {
6380b57cec5SDimitry Andric       if (!RefType->getPointeeType().getQualifiers() &&
6390b57cec5SDimitry Andric           RefType->getPointeeType()->getAs<TemplateTypeParmType>()) {
6400b57cec5SDimitry Andric         std::unique_ptr<FunctionParmMutationAnalyzer> &Analyzer =
6410b57cec5SDimitry Andric             FuncParmAnalyzer[Func];
6420b57cec5SDimitry Andric         if (!Analyzer)
6430b57cec5SDimitry Andric           Analyzer.reset(new FunctionParmMutationAnalyzer(*Func, Context));
6440b57cec5SDimitry Andric         if (Analyzer->findMutation(Parm))
6450b57cec5SDimitry Andric           return Exp;
6460b57cec5SDimitry Andric         continue;
6470b57cec5SDimitry Andric       }
6480b57cec5SDimitry Andric     }
6490b57cec5SDimitry Andric     // Not forwarding reference.
6500b57cec5SDimitry Andric     return Exp;
6510b57cec5SDimitry Andric   }
6520b57cec5SDimitry Andric   return nullptr;
6530b57cec5SDimitry Andric }
6540b57cec5SDimitry Andric 
6550b57cec5SDimitry Andric FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer(
6560b57cec5SDimitry Andric     const FunctionDecl &Func, ASTContext &Context)
6570b57cec5SDimitry Andric     : BodyAnalyzer(*Func.getBody(), Context) {
6580b57cec5SDimitry Andric   if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(&Func)) {
6590b57cec5SDimitry Andric     // CXXCtorInitializer might also mutate Param but they're not part of
6600b57cec5SDimitry Andric     // function body, check them eagerly here since they're typically trivial.
6610b57cec5SDimitry Andric     for (const CXXCtorInitializer *Init : Ctor->inits()) {
6620b57cec5SDimitry Andric       ExprMutationAnalyzer InitAnalyzer(*Init->getInit(), Context);
6630b57cec5SDimitry Andric       for (const ParmVarDecl *Parm : Ctor->parameters()) {
66406c3fb27SDimitry Andric         if (Results.contains(Parm))
6650b57cec5SDimitry Andric           continue;
6660b57cec5SDimitry Andric         if (const Stmt *S = InitAnalyzer.findMutation(Parm))
6670b57cec5SDimitry Andric           Results[Parm] = S;
6680b57cec5SDimitry Andric       }
6690b57cec5SDimitry Andric     }
6700b57cec5SDimitry Andric   }
6710b57cec5SDimitry Andric }
6720b57cec5SDimitry Andric 
6730b57cec5SDimitry Andric const Stmt *
6740b57cec5SDimitry Andric FunctionParmMutationAnalyzer::findMutation(const ParmVarDecl *Parm) {
6750b57cec5SDimitry Andric   const auto Memoized = Results.find(Parm);
6760b57cec5SDimitry Andric   if (Memoized != Results.end())
6770b57cec5SDimitry Andric     return Memoized->second;
6780b57cec5SDimitry Andric 
6790b57cec5SDimitry Andric   if (const Stmt *S = BodyAnalyzer.findMutation(Parm))
6800b57cec5SDimitry Andric     return Results[Parm] = S;
6810b57cec5SDimitry Andric 
6820b57cec5SDimitry Andric   return Results[Parm] = nullptr;
6830b57cec5SDimitry Andric }
6840b57cec5SDimitry Andric 
6850b57cec5SDimitry Andric } // namespace clang
686