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