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