xref: /llvm-project/clang/lib/Analysis/ExprMutationAnalyzer.cpp (revision 53ea5ffcb38d428e446d357f310e9c28957eaec7)
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"
11*53ea5ffcSCongcong Cai #include "clang/AST/Stmt.h"
12e9192f83SShuai Wang #include "clang/ASTMatchers/ASTMatchFinder.h"
13e517e5cfSJonas Toth #include "clang/ASTMatchers/ASTMatchers.h"
14*53ea5ffcSCongcong Cai #include "clang/ASTMatchers/ASTMatchersMacros.h"
15e9192f83SShuai Wang #include "llvm/ADT/STLExtras.h"
16e9192f83SShuai Wang 
17e9192f83SShuai Wang namespace clang {
18e9192f83SShuai Wang using namespace ast_matchers;
19e9192f83SShuai Wang 
206eb372e4SPiotr Zegar // Check if result of Source expression could be a Target expression.
216eb372e4SPiotr Zegar // Checks:
226eb372e4SPiotr Zegar //  - Implicit Casts
236eb372e4SPiotr Zegar //  - Binary Operators
246eb372e4SPiotr Zegar //  - ConditionalOperator
256eb372e4SPiotr Zegar //  - BinaryConditionalOperator
266eb372e4SPiotr Zegar static bool canExprResolveTo(const Expr *Source, const Expr *Target) {
276eb372e4SPiotr Zegar   const auto IgnoreDerivedToBase = [](const Expr *E, auto Matcher) {
286eb372e4SPiotr Zegar     if (Matcher(E))
296eb372e4SPiotr Zegar       return true;
306eb372e4SPiotr Zegar     if (const auto *Cast = dyn_cast<ImplicitCastExpr>(E)) {
316eb372e4SPiotr Zegar       if ((Cast->getCastKind() == CK_DerivedToBase ||
326eb372e4SPiotr Zegar            Cast->getCastKind() == CK_UncheckedDerivedToBase) &&
336eb372e4SPiotr Zegar           Matcher(Cast->getSubExpr()))
346eb372e4SPiotr Zegar         return true;
356eb372e4SPiotr Zegar     }
366eb372e4SPiotr Zegar     return false;
376eb372e4SPiotr Zegar   };
386eb372e4SPiotr Zegar 
396eb372e4SPiotr Zegar   const auto EvalCommaExpr = [](const Expr *E, auto Matcher) {
406eb372e4SPiotr Zegar     const Expr *Result = E;
416eb372e4SPiotr Zegar     while (const auto *BOComma =
426eb372e4SPiotr Zegar                dyn_cast_or_null<BinaryOperator>(Result->IgnoreParens())) {
436eb372e4SPiotr Zegar       if (!BOComma->isCommaOp())
446eb372e4SPiotr Zegar         break;
456eb372e4SPiotr Zegar       Result = BOComma->getRHS();
466eb372e4SPiotr Zegar     }
476eb372e4SPiotr Zegar 
486eb372e4SPiotr Zegar     return Result != E && Matcher(Result);
496eb372e4SPiotr Zegar   };
506eb372e4SPiotr Zegar 
516eb372e4SPiotr Zegar   // The 'ConditionalOperatorM' matches on `<anything> ? <expr> : <expr>`.
526eb372e4SPiotr Zegar   // This matching must be recursive because `<expr>` can be anything resolving
536eb372e4SPiotr Zegar   // to the `InnerMatcher`, for example another conditional operator.
546eb372e4SPiotr Zegar   // The edge-case `BaseClass &b = <cond> ? DerivedVar1 : DerivedVar2;`
556eb372e4SPiotr Zegar   // is handled, too. The implicit cast happens outside of the conditional.
566eb372e4SPiotr Zegar   // This is matched by `IgnoreDerivedToBase(canResolveToExpr(InnerMatcher))`
576eb372e4SPiotr Zegar   // below.
586eb372e4SPiotr Zegar   const auto ConditionalOperatorM = [Target](const Expr *E) {
59f98c9a9bSCongcong Cai     if (const auto *CO = dyn_cast<AbstractConditionalOperator>(E)) {
60f98c9a9bSCongcong Cai       const auto *TE = CO->getTrueExpr()->IgnoreParens();
61f98c9a9bSCongcong Cai       if (TE && canExprResolveTo(TE, Target))
626eb372e4SPiotr Zegar         return true;
63f98c9a9bSCongcong Cai       const auto *FE = CO->getFalseExpr()->IgnoreParens();
64f98c9a9bSCongcong Cai       if (FE && canExprResolveTo(FE, Target))
656eb372e4SPiotr Zegar         return true;
666eb372e4SPiotr Zegar     }
676eb372e4SPiotr Zegar     return false;
686eb372e4SPiotr Zegar   };
696eb372e4SPiotr Zegar 
706eb372e4SPiotr Zegar   const Expr *SourceExprP = Source->IgnoreParens();
716eb372e4SPiotr Zegar   return IgnoreDerivedToBase(SourceExprP,
726eb372e4SPiotr Zegar                              [&](const Expr *E) {
73f98c9a9bSCongcong Cai                                return E == Target || ConditionalOperatorM(E);
746eb372e4SPiotr Zegar                              }) ||
756eb372e4SPiotr Zegar          EvalCommaExpr(SourceExprP, [&](const Expr *E) {
766eb372e4SPiotr Zegar            return IgnoreDerivedToBase(
776eb372e4SPiotr Zegar                E->IgnoreParens(), [&](const Expr *EE) { return EE == Target; });
786eb372e4SPiotr Zegar          });
796eb372e4SPiotr Zegar }
806eb372e4SPiotr Zegar 
81e9192f83SShuai Wang namespace {
82e9192f83SShuai Wang 
83*53ea5ffcSCongcong Cai AST_MATCHER(Type, isDependentType) { return Node.isDependentType(); }
84*53ea5ffcSCongcong Cai 
85e9192f83SShuai Wang AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) {
86e9192f83SShuai Wang   return llvm::is_contained(Node.capture_inits(), E);
87e9192f83SShuai Wang }
88e9192f83SShuai Wang 
89e9192f83SShuai Wang AST_MATCHER_P(CXXForRangeStmt, hasRangeStmt,
90e9192f83SShuai Wang               ast_matchers::internal::Matcher<DeclStmt>, InnerMatcher) {
91e9192f83SShuai Wang   const DeclStmt *const Range = Node.getRangeStmt();
92e9192f83SShuai Wang   return InnerMatcher.matches(*Range, Finder, Builder);
93e9192f83SShuai Wang }
94e9192f83SShuai Wang 
956eb372e4SPiotr Zegar AST_MATCHER_P(Stmt, canResolveToExpr, const Stmt *, Inner) {
96ca81abcfSusama hameed   auto *Exp = dyn_cast<Expr>(&Node);
976eb372e4SPiotr Zegar   if (!Exp)
986eb372e4SPiotr Zegar     return true;
996eb372e4SPiotr Zegar   auto *Target = dyn_cast<Expr>(Inner);
1006eb372e4SPiotr Zegar   if (!Target)
1016eb372e4SPiotr Zegar     return false;
1026eb372e4SPiotr Zegar   return canExprResolveTo(Exp, Target);
103e517e5cfSJonas Toth }
104e517e5cfSJonas Toth 
105*53ea5ffcSCongcong Cai // use class member to store data can reduce stack usage to avoid stack overflow
106*53ea5ffcSCongcong Cai // when recursive call.
107*53ea5ffcSCongcong Cai class ExprPointeeResolve {
108*53ea5ffcSCongcong Cai   const Expr *T;
109*53ea5ffcSCongcong Cai 
110*53ea5ffcSCongcong Cai   bool resolveExpr(const Expr *E) {
111*53ea5ffcSCongcong Cai     if (E == nullptr)
112*53ea5ffcSCongcong Cai       return false;
113*53ea5ffcSCongcong Cai     if (E == T)
114*53ea5ffcSCongcong Cai       return true;
115*53ea5ffcSCongcong Cai 
116*53ea5ffcSCongcong Cai     if (const auto *BO = dyn_cast<BinaryOperator>(E)) {
117*53ea5ffcSCongcong Cai       if (BO->isAdditiveOp())
118*53ea5ffcSCongcong Cai         return (resolveExpr(BO->getLHS()) || resolveExpr(BO->getRHS()));
119*53ea5ffcSCongcong Cai       if (BO->isCommaOp())
120*53ea5ffcSCongcong Cai         return resolveExpr(BO->getRHS());
121*53ea5ffcSCongcong Cai       return false;
122*53ea5ffcSCongcong Cai     }
123*53ea5ffcSCongcong Cai 
124*53ea5ffcSCongcong Cai     if (const auto *PE = dyn_cast<ParenExpr>(E))
125*53ea5ffcSCongcong Cai       return resolveExpr(PE->getSubExpr());
126*53ea5ffcSCongcong Cai 
127*53ea5ffcSCongcong Cai     if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E)) {
128*53ea5ffcSCongcong Cai       // only implicit cast needs to be treated as resolvable.
129*53ea5ffcSCongcong Cai       // explicit cast will be checked in `findPointeeToNonConst`
130*53ea5ffcSCongcong Cai       const CastKind kind = ICE->getCastKind();
131*53ea5ffcSCongcong Cai       if (kind == CK_LValueToRValue || kind == CK_DerivedToBase ||
132*53ea5ffcSCongcong Cai           kind == CK_UncheckedDerivedToBase)
133*53ea5ffcSCongcong Cai         return resolveExpr(ICE->getSubExpr());
134*53ea5ffcSCongcong Cai       return false;
135*53ea5ffcSCongcong Cai     }
136*53ea5ffcSCongcong Cai 
137*53ea5ffcSCongcong Cai     if (const auto *ACE = dyn_cast<AbstractConditionalOperator>(E))
138*53ea5ffcSCongcong Cai       return resolve(ACE->getTrueExpr()) || resolve(ACE->getFalseExpr());
139*53ea5ffcSCongcong Cai 
140*53ea5ffcSCongcong Cai     return false;
141*53ea5ffcSCongcong Cai   }
142*53ea5ffcSCongcong Cai 
143*53ea5ffcSCongcong Cai public:
144*53ea5ffcSCongcong Cai   ExprPointeeResolve(const Expr *T) : T(T) {}
145*53ea5ffcSCongcong Cai   bool resolve(const Expr *S) { return resolveExpr(S); }
146*53ea5ffcSCongcong Cai };
147*53ea5ffcSCongcong Cai 
148*53ea5ffcSCongcong Cai AST_MATCHER_P(Stmt, canResolveToExprPointee, const Stmt *, T) {
149*53ea5ffcSCongcong Cai   auto *Exp = dyn_cast<Expr>(&Node);
150*53ea5ffcSCongcong Cai   if (!Exp)
151*53ea5ffcSCongcong Cai     return true;
152*53ea5ffcSCongcong Cai   auto *Target = dyn_cast<Expr>(T);
153*53ea5ffcSCongcong Cai   if (!Target)
154*53ea5ffcSCongcong Cai     return false;
155*53ea5ffcSCongcong Cai   return ExprPointeeResolve{Target}.resolve(Exp);
156*53ea5ffcSCongcong Cai }
157*53ea5ffcSCongcong Cai 
158e517e5cfSJonas Toth // Similar to 'hasAnyArgument', but does not work because 'InitListExpr' does
159e517e5cfSJonas Toth // not have the 'arguments()' method.
160e517e5cfSJonas Toth AST_MATCHER_P(InitListExpr, hasAnyInit, ast_matchers::internal::Matcher<Expr>,
161e517e5cfSJonas Toth               InnerMatcher) {
162e517e5cfSJonas Toth   for (const Expr *Arg : Node.inits()) {
163eb7dea8bSCongcong Cai     if (Arg == nullptr)
164eb7dea8bSCongcong Cai       continue;
165e517e5cfSJonas Toth     ast_matchers::internal::BoundNodesTreeBuilder Result(*Builder);
166e517e5cfSJonas Toth     if (InnerMatcher.matches(*Arg, Finder, &Result)) {
167e517e5cfSJonas Toth       *Builder = std::move(Result);
168e517e5cfSJonas Toth       return true;
169e517e5cfSJonas Toth     }
170e517e5cfSJonas Toth   }
171e517e5cfSJonas Toth   return false;
172e517e5cfSJonas Toth }
173e517e5cfSJonas Toth 
174e9192f83SShuai Wang const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXTypeidExpr>
175e9192f83SShuai Wang     cxxTypeidExpr;
176e9192f83SShuai Wang 
177e9192f83SShuai Wang AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) {
178e9192f83SShuai Wang   return Node.isPotentiallyEvaluated();
179e9192f83SShuai Wang }
180e9192f83SShuai Wang 
181feb7b191SCongcong Cai AST_MATCHER(CXXMemberCallExpr, isConstCallee) {
182feb7b191SCongcong Cai   const Decl *CalleeDecl = Node.getCalleeDecl();
183feb7b191SCongcong Cai   const auto *VD = dyn_cast_or_null<ValueDecl>(CalleeDecl);
184feb7b191SCongcong Cai   if (!VD)
185feb7b191SCongcong Cai     return false;
186feb7b191SCongcong Cai   const QualType T = VD->getType().getCanonicalType();
187feb7b191SCongcong Cai   const auto *MPT = dyn_cast<MemberPointerType>(T);
188feb7b191SCongcong Cai   const auto *FPT = MPT ? cast<FunctionProtoType>(MPT->getPointeeType())
189feb7b191SCongcong Cai                         : dyn_cast<FunctionProtoType>(T);
190feb7b191SCongcong Cai   if (!FPT)
191feb7b191SCongcong Cai     return false;
192feb7b191SCongcong Cai   return FPT->isConst();
193feb7b191SCongcong Cai }
194feb7b191SCongcong Cai 
195e9192f83SShuai Wang AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr,
196e9192f83SShuai Wang               ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
19712728e14SAaron Ballman   if (Node.isTypePredicate())
19812728e14SAaron Ballman     return false;
199e9192f83SShuai Wang   return InnerMatcher.matches(*Node.getControllingExpr(), Finder, Builder);
200e9192f83SShuai Wang }
201e9192f83SShuai Wang 
2026eb372e4SPiotr Zegar template <typename T>
2036eb372e4SPiotr Zegar ast_matchers::internal::Matcher<T>
2046eb372e4SPiotr Zegar findFirst(const ast_matchers::internal::Matcher<T> &Matcher) {
2056eb372e4SPiotr Zegar   return anyOf(Matcher, hasDescendant(Matcher));
2066eb372e4SPiotr Zegar }
2076eb372e4SPiotr Zegar 
208e9192f83SShuai Wang const auto nonConstReferenceType = [] {
209e9192f83SShuai Wang   return hasUnqualifiedDesugaredType(
210e9192f83SShuai Wang       referenceType(pointee(unless(isConstQualified()))));
211e9192f83SShuai Wang };
212e9192f83SShuai Wang 
213e9192f83SShuai Wang const auto nonConstPointerType = [] {
214e9192f83SShuai Wang   return hasUnqualifiedDesugaredType(
215e9192f83SShuai Wang       pointerType(pointee(unless(isConstQualified()))));
216e9192f83SShuai Wang };
217e9192f83SShuai Wang 
218e9192f83SShuai Wang const auto isMoveOnly = [] {
219e9192f83SShuai Wang   return cxxRecordDecl(
220e9192f83SShuai Wang       hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted()))),
221e9192f83SShuai Wang       hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted()))),
222e9192f83SShuai Wang       unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(),
223e9192f83SShuai Wang                                                 unless(isDeleted()))),
224e9192f83SShuai Wang                    hasMethod(cxxMethodDecl(isCopyAssignmentOperator(),
225e9192f83SShuai Wang                                            unless(isDeleted()))))));
226e9192f83SShuai Wang };
227e9192f83SShuai Wang 
228aaaa310dSShuai Wang template <class T> struct NodeID;
2290c3df70fSBenjamin Kramer template <> struct NodeID<Expr> { static constexpr StringRef value = "expr"; };
2300c3df70fSBenjamin Kramer template <> struct NodeID<Decl> { static constexpr StringRef value = "decl"; };
231c0c6a127SBenjamin Kramer constexpr StringRef NodeID<Expr>::value;
232c0c6a127SBenjamin Kramer constexpr StringRef NodeID<Decl>::value;
233aaaa310dSShuai Wang 
234f40f4fceSCongcong Cai template <class T,
235f40f4fceSCongcong Cai           class F = const Stmt *(ExprMutationAnalyzer::Analyzer::*)(const T *)>
236aaaa310dSShuai Wang const Stmt *tryEachMatch(ArrayRef<ast_matchers::BoundNodes> Matches,
237f40f4fceSCongcong Cai                          ExprMutationAnalyzer::Analyzer *Analyzer, F Finder) {
238aaaa310dSShuai Wang   const StringRef ID = NodeID<T>::value;
239aaaa310dSShuai Wang   for (const auto &Nodes : Matches) {
240aaaa310dSShuai Wang     if (const Stmt *S = (Analyzer->*Finder)(Nodes.getNodeAs<T>(ID)))
241aaaa310dSShuai Wang       return S;
242aaaa310dSShuai Wang   }
243aaaa310dSShuai Wang   return nullptr;
244aaaa310dSShuai Wang }
245aaaa310dSShuai Wang 
246e9192f83SShuai Wang } // namespace
247e9192f83SShuai Wang 
248f40f4fceSCongcong Cai const Stmt *ExprMutationAnalyzer::Analyzer::findMutation(const Expr *Exp) {
249f40f4fceSCongcong Cai   return findMutationMemoized(
250f40f4fceSCongcong Cai       Exp,
251f40f4fceSCongcong Cai       {&ExprMutationAnalyzer::Analyzer::findDirectMutation,
252f40f4fceSCongcong Cai        &ExprMutationAnalyzer::Analyzer::findMemberMutation,
253f40f4fceSCongcong Cai        &ExprMutationAnalyzer::Analyzer::findArrayElementMutation,
254f40f4fceSCongcong Cai        &ExprMutationAnalyzer::Analyzer::findCastMutation,
255f40f4fceSCongcong Cai        &ExprMutationAnalyzer::Analyzer::findRangeLoopMutation,
256f40f4fceSCongcong Cai        &ExprMutationAnalyzer::Analyzer::findReferenceMutation,
257f40f4fceSCongcong Cai        &ExprMutationAnalyzer::Analyzer::findFunctionArgMutation},
258f40f4fceSCongcong Cai       Memorized.Results);
259e9192f83SShuai Wang }
260e9192f83SShuai Wang 
261f40f4fceSCongcong Cai const Stmt *ExprMutationAnalyzer::Analyzer::findMutation(const Decl *Dec) {
262f40f4fceSCongcong Cai   return tryEachDeclRef(Dec, &ExprMutationAnalyzer::Analyzer::findMutation);
263aaaa310dSShuai Wang }
264aaaa310dSShuai Wang 
265f40f4fceSCongcong Cai const Stmt *
266f40f4fceSCongcong Cai ExprMutationAnalyzer::Analyzer::findPointeeMutation(const Expr *Exp) {
267*53ea5ffcSCongcong Cai   return findMutationMemoized(
268*53ea5ffcSCongcong Cai       Exp,
269*53ea5ffcSCongcong Cai       {
270*53ea5ffcSCongcong Cai           &ExprMutationAnalyzer::Analyzer::findPointeeValueMutation,
271*53ea5ffcSCongcong Cai           &ExprMutationAnalyzer::Analyzer::findPointeeMemberMutation,
272*53ea5ffcSCongcong Cai           &ExprMutationAnalyzer::Analyzer::findPointeeToNonConst,
273*53ea5ffcSCongcong Cai       },
274*53ea5ffcSCongcong Cai       Memorized.PointeeResults);
275aaaa310dSShuai Wang }
276aaaa310dSShuai Wang 
277f40f4fceSCongcong Cai const Stmt *
278f40f4fceSCongcong Cai ExprMutationAnalyzer::Analyzer::findPointeeMutation(const Decl *Dec) {
279f40f4fceSCongcong Cai   return tryEachDeclRef(Dec,
280f40f4fceSCongcong Cai                         &ExprMutationAnalyzer::Analyzer::findPointeeMutation);
281aaaa310dSShuai Wang }
282aaaa310dSShuai Wang 
283f40f4fceSCongcong Cai const Stmt *ExprMutationAnalyzer::Analyzer::findMutationMemoized(
284aaaa310dSShuai Wang     const Expr *Exp, llvm::ArrayRef<MutationFinder> Finders,
285f40f4fceSCongcong Cai     Memoized::ResultMap &MemoizedResults) {
28668a48ec9SCongcong Cai   // Assume Exp is not mutated before analyzing Exp.
28728733ed6SKazu Hirata   auto [Memoized, Inserted] = MemoizedResults.try_emplace(Exp);
28828733ed6SKazu Hirata   if (!Inserted)
289aaaa310dSShuai Wang     return Memoized->second;
290aaaa310dSShuai Wang 
291ae20dbddSCongcong Cai   if (ExprMutationAnalyzer::isUnevaluated(Exp, Context))
2926e31714dSCongcong Cai     return nullptr;
293aaaa310dSShuai Wang 
294aaaa310dSShuai Wang   for (const auto &Finder : Finders) {
295aaaa310dSShuai Wang     if (const Stmt *S = (this->*Finder)(Exp))
296aaaa310dSShuai Wang       return MemoizedResults[Exp] = S;
297aaaa310dSShuai Wang   }
298aaaa310dSShuai Wang 
2996e31714dSCongcong Cai   return nullptr;
300aaaa310dSShuai Wang }
301aaaa310dSShuai Wang 
302f40f4fceSCongcong Cai const Stmt *
303f40f4fceSCongcong Cai ExprMutationAnalyzer::Analyzer::tryEachDeclRef(const Decl *Dec,
304aaaa310dSShuai Wang                                                MutationFinder Finder) {
3058fd32b96SClement Courbet   const auto Refs = match(
3068fd32b96SClement Courbet       findAll(
3078fd32b96SClement Courbet           declRefExpr(to(
3088fd32b96SClement Courbet                           // `Dec` or a binding if `Dec` is a decomposition.
3098fd32b96SClement Courbet                           anyOf(equalsNode(Dec),
3108fd32b96SClement Courbet                                 bindingDecl(forDecomposition(equalsNode(Dec))))
3118fd32b96SClement Courbet                           //
3128fd32b96SClement Courbet                           ))
3138fd32b96SClement Courbet               .bind(NodeID<Expr>::value)),
314aaaa310dSShuai Wang       Stm, Context);
315aaaa310dSShuai Wang   for (const auto &RefNodes : Refs) {
316aaaa310dSShuai Wang     const auto *E = RefNodes.getNodeAs<Expr>(NodeID<Expr>::value);
317aaaa310dSShuai Wang     if ((this->*Finder)(E))
318aaaa310dSShuai Wang       return E;
319aaaa310dSShuai Wang   }
320aaaa310dSShuai Wang   return nullptr;
321e9192f83SShuai Wang }
322e9192f83SShuai Wang 
323ae20dbddSCongcong Cai bool ExprMutationAnalyzer::isUnevaluated(const Stmt *Stm, ASTContext &Context) {
324ae20dbddSCongcong Cai   return !match(stmt(anyOf(
325ca81abcfSusama hameed                     // `Exp` is part of the underlying expression of
326ca81abcfSusama hameed                     // decltype/typeof if it has an ancestor of
327ca81abcfSusama hameed                     // typeLoc.
328ae20dbddSCongcong Cai                     hasAncestor(typeLoc(
329ae20dbddSCongcong Cai                         unless(hasAncestor(unaryExprOrTypeTraitExpr())))),
330ca81abcfSusama hameed                     hasAncestor(expr(anyOf(
331ca81abcfSusama hameed                         // `UnaryExprOrTypeTraitExpr` is unevaluated
332ca81abcfSusama hameed                         // unless it's sizeof on VLA.
333ca81abcfSusama hameed                         unaryExprOrTypeTraitExpr(unless(sizeOfExpr(
334ca81abcfSusama hameed                             hasArgumentOfType(variableArrayType())))),
335ca81abcfSusama hameed                         // `CXXTypeidExpr` is unevaluated unless it's
336ca81abcfSusama hameed                         // applied to an expression of glvalue of
337ca81abcfSusama hameed                         // polymorphic class type.
338ae20dbddSCongcong Cai                         cxxTypeidExpr(unless(isPotentiallyEvaluated())),
339ca81abcfSusama hameed                         // The controlling expression of
340ca81abcfSusama hameed                         // `GenericSelectionExpr` is unevaluated.
341ae20dbddSCongcong Cai                         genericSelectionExpr(
342ae20dbddSCongcong Cai                             hasControllingExpr(hasDescendant(equalsNode(Stm)))),
343ae20dbddSCongcong Cai                         cxxNoexceptExpr()))))),
344ae20dbddSCongcong Cai                 *Stm, Context)
345ae20dbddSCongcong Cai               .empty();
34660268222Susama hameed }
34760268222Susama hameed 
348e9192f83SShuai Wang const Stmt *
349f40f4fceSCongcong Cai ExprMutationAnalyzer::Analyzer::findExprMutation(ArrayRef<BoundNodes> Matches) {
350f40f4fceSCongcong Cai   return tryEachMatch<Expr>(Matches, this,
351f40f4fceSCongcong Cai                             &ExprMutationAnalyzer::Analyzer::findMutation);
352e9192f83SShuai Wang }
353e9192f83SShuai Wang 
354e9192f83SShuai Wang const Stmt *
355f40f4fceSCongcong Cai ExprMutationAnalyzer::Analyzer::findDeclMutation(ArrayRef<BoundNodes> Matches) {
356aaaa310dSShuai Wang   return tryEachMatch<Decl>(Matches, this,
357f40f4fceSCongcong Cai                             &ExprMutationAnalyzer::Analyzer::findMutation);
358e9192f83SShuai Wang }
359e9192f83SShuai Wang 
360f40f4fceSCongcong Cai const Stmt *ExprMutationAnalyzer::Analyzer::findExprPointeeMutation(
361f40f4fceSCongcong Cai     ArrayRef<ast_matchers::BoundNodes> Matches) {
362f40f4fceSCongcong Cai   return tryEachMatch<Expr>(
363f40f4fceSCongcong Cai       Matches, this, &ExprMutationAnalyzer::Analyzer::findPointeeMutation);
364f40f4fceSCongcong Cai }
365f40f4fceSCongcong Cai 
366f40f4fceSCongcong Cai const Stmt *ExprMutationAnalyzer::Analyzer::findDeclPointeeMutation(
367f40f4fceSCongcong Cai     ArrayRef<ast_matchers::BoundNodes> Matches) {
368f40f4fceSCongcong Cai   return tryEachMatch<Decl>(
369f40f4fceSCongcong Cai       Matches, this, &ExprMutationAnalyzer::Analyzer::findPointeeMutation);
370f40f4fceSCongcong Cai }
371f40f4fceSCongcong Cai 
372f40f4fceSCongcong Cai const Stmt *
373f40f4fceSCongcong Cai ExprMutationAnalyzer::Analyzer::findDirectMutation(const Expr *Exp) {
374e9192f83SShuai Wang   // LHS of any assignment operators.
3756eb372e4SPiotr Zegar   const auto AsAssignmentLhs =
3766eb372e4SPiotr Zegar       binaryOperator(isAssignmentOperator(), hasLHS(canResolveToExpr(Exp)));
377e9192f83SShuai Wang 
378e9192f83SShuai Wang   // Operand of increment/decrement operators.
379e9192f83SShuai Wang   const auto AsIncDecOperand =
380e9192f83SShuai Wang       unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")),
3816eb372e4SPiotr Zegar                     hasUnaryOperand(canResolveToExpr(Exp)));
382e9192f83SShuai Wang 
383e9192f83SShuai Wang   // Invoking non-const member function.
384e9192f83SShuai Wang   // A member function is assumed to be non-const when it is unresolved.
385e9192f83SShuai Wang   const auto NonConstMethod = cxxMethodDecl(unless(isConst()));
386e517e5cfSJonas Toth 
387e517e5cfSJonas Toth   const auto AsNonConstThis = expr(anyOf(
3886eb372e4SPiotr Zegar       cxxMemberCallExpr(on(canResolveToExpr(Exp)), unless(isConstCallee())),
389e9192f83SShuai Wang       cxxOperatorCallExpr(callee(NonConstMethod),
3906eb372e4SPiotr Zegar                           hasArgument(0, canResolveToExpr(Exp))),
391e517e5cfSJonas Toth       // In case of a templated type, calling overloaded operators is not
392e517e5cfSJonas Toth       // resolved and modelled as `binaryOperator` on a dependent type.
393e517e5cfSJonas Toth       // Such instances are considered a modification, because they can modify
394e517e5cfSJonas Toth       // in different instantiations of the template.
3956eb372e4SPiotr Zegar       binaryOperator(isTypeDependent(),
3966eb372e4SPiotr Zegar                      hasEitherOperand(ignoringImpCasts(canResolveToExpr(Exp)))),
3979f8ccf50SJulian Schmidt       // A fold expression may contain `Exp` as it's initializer.
3989f8ccf50SJulian Schmidt       // We don't know if the operator modifies `Exp` because the
3999f8ccf50SJulian Schmidt       // operator is type dependent due to the parameter pack.
4009f8ccf50SJulian Schmidt       cxxFoldExpr(hasFoldInit(ignoringImpCasts(canResolveToExpr(Exp)))),
401e517e5cfSJonas Toth       // Within class templates and member functions the member expression might
402e517e5cfSJonas Toth       // not be resolved. In that case, the `callExpr` is considered to be a
403e517e5cfSJonas Toth       // modification.
4046eb372e4SPiotr Zegar       callExpr(callee(expr(anyOf(
4056eb372e4SPiotr Zegar           unresolvedMemberExpr(hasObjectExpression(canResolveToExpr(Exp))),
4066eb372e4SPiotr Zegar           cxxDependentScopeMemberExpr(
4076eb372e4SPiotr Zegar               hasObjectExpression(canResolveToExpr(Exp))))))),
408e517e5cfSJonas Toth       // Match on a call to a known method, but the call itself is type
409e517e5cfSJonas Toth       // dependent (e.g. `vector<T> v; v.push(T{});` in a templated function).
4106eb372e4SPiotr Zegar       callExpr(allOf(
4116eb372e4SPiotr Zegar           isTypeDependent(),
412e517e5cfSJonas Toth           callee(memberExpr(hasDeclaration(NonConstMethod),
4136eb372e4SPiotr Zegar                             hasObjectExpression(canResolveToExpr(Exp))))))));
414e9192f83SShuai Wang 
415e9192f83SShuai Wang   // Taking address of 'Exp'.
416e9192f83SShuai Wang   // We're assuming 'Exp' is mutated as soon as its address is taken, though in
417e9192f83SShuai Wang   // theory we can follow the pointer and see whether it escaped `Stm` or is
418e9192f83SShuai Wang   // dereferenced and then mutated. This is left for future improvements.
419e9192f83SShuai Wang   const auto AsAmpersandOperand =
420e9192f83SShuai Wang       unaryOperator(hasOperatorName("&"),
421e9192f83SShuai Wang                     // A NoOp implicit cast is adding const.
422e9192f83SShuai Wang                     unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))),
4236eb372e4SPiotr Zegar                     hasUnaryOperand(canResolveToExpr(Exp)));
4246eb372e4SPiotr Zegar   const auto AsPointerFromArrayDecay = castExpr(
4256eb372e4SPiotr Zegar       hasCastKind(CK_ArrayToPointerDecay),
4266eb372e4SPiotr Zegar       unless(hasParent(arraySubscriptExpr())), has(canResolveToExpr(Exp)));
427e9192f83SShuai Wang   // Treat calling `operator->()` of move-only classes as taking address.
428e9192f83SShuai Wang   // These are typically smart pointers with unique ownership so we treat
429e9192f83SShuai Wang   // mutation of pointee as mutation of the smart pointer itself.
430e517e5cfSJonas Toth   const auto AsOperatorArrowThis = cxxOperatorCallExpr(
431e517e5cfSJonas Toth       hasOverloadedOperatorName("->"),
432e517e5cfSJonas Toth       callee(
433e517e5cfSJonas Toth           cxxMethodDecl(ofClass(isMoveOnly()), returns(nonConstPointerType()))),
4346eb372e4SPiotr Zegar       argumentCountIs(1), hasArgument(0, canResolveToExpr(Exp)));
435e9192f83SShuai Wang 
436e9192f83SShuai Wang   // Used as non-const-ref argument when calling a function.
437e9192f83SShuai Wang   // An argument is assumed to be non-const-ref when the function is unresolved.
438cb98b707SShuai Wang   // Instantiated template functions are not handled here but in
439cb98b707SShuai Wang   // findFunctionArgMutation which has additional smarts for handling forwarding
440cb98b707SShuai Wang   // references.
441e517e5cfSJonas Toth   const auto NonConstRefParam = forEachArgumentWithParamType(
4426eb372e4SPiotr Zegar       anyOf(canResolveToExpr(Exp),
443*53ea5ffcSCongcong Cai             memberExpr(
444*53ea5ffcSCongcong Cai                 hasObjectExpression(ignoringImpCasts(canResolveToExpr(Exp))))),
445e517e5cfSJonas Toth       nonConstReferenceType());
446cb98b707SShuai Wang   const auto NotInstantiated = unless(hasDeclaration(isInstantiated()));
447e517e5cfSJonas Toth 
4481ce89899SDmitry Polukhin   const auto AsNonConstRefArg =
4491ce89899SDmitry Polukhin       anyOf(callExpr(NonConstRefParam, NotInstantiated),
450cb98b707SShuai Wang             cxxConstructExpr(NonConstRefParam, NotInstantiated),
4511ce89899SDmitry Polukhin             // If the call is type-dependent, we can't properly process any
4521ce89899SDmitry Polukhin             // argument because required type conversions and implicit casts
4531ce89899SDmitry Polukhin             // will be inserted only after specialization.
4541ce89899SDmitry Polukhin             callExpr(isTypeDependent(), hasAnyArgument(canResolveToExpr(Exp))),
4556eb372e4SPiotr Zegar             cxxUnresolvedConstructExpr(hasAnyArgument(canResolveToExpr(Exp))),
456e517e5cfSJonas Toth             // Previous False Positive in the following Code:
457e517e5cfSJonas Toth             // `template <typename T> void f() { int i = 42; new Type<T>(i); }`
458e517e5cfSJonas Toth             // Where the constructor of `Type` takes its argument as reference.
459e517e5cfSJonas Toth             // The AST does not resolve in a `cxxConstructExpr` because it is
460e517e5cfSJonas Toth             // type-dependent.
4616eb372e4SPiotr Zegar             parenListExpr(hasDescendant(expr(canResolveToExpr(Exp)))),
462e517e5cfSJonas Toth             // If the initializer is for a reference type, there is no cast for
463e517e5cfSJonas Toth             // the variable. Values are cast to RValue first.
4646eb372e4SPiotr Zegar             initListExpr(hasAnyInit(expr(canResolveToExpr(Exp)))));
465e9192f83SShuai Wang 
466e9192f83SShuai Wang   // Captured by a lambda by reference.
467e9192f83SShuai Wang   // If we're initializing a capture with 'Exp' directly then we're initializing
468e9192f83SShuai Wang   // a reference capture.
469e9192f83SShuai Wang   // For value captures there will be an ImplicitCastExpr <LValueToRValue>.
470e9192f83SShuai Wang   const auto AsLambdaRefCaptureInit = lambdaExpr(hasCaptureInit(Exp));
471e9192f83SShuai Wang 
472e9192f83SShuai Wang   // Returned as non-const-ref.
473e9192f83SShuai Wang   // If we're returning 'Exp' directly then it's returned as non-const-ref.
474e9192f83SShuai Wang   // For returning by value there will be an ImplicitCastExpr <LValueToRValue>.
475e9192f83SShuai Wang   // For returning by const-ref there will be an ImplicitCastExpr <NoOp> (for
476e9192f83SShuai Wang   // adding const.)
477e517e5cfSJonas Toth   const auto AsNonConstRefReturn =
4786eb372e4SPiotr Zegar       returnStmt(hasReturnValue(canResolveToExpr(Exp)));
479e517e5cfSJonas Toth 
480f40f4fceSCongcong Cai   // It is used as a non-const-reference for initializing a range-for loop.
4816eb372e4SPiotr Zegar   const auto AsNonConstRefRangeInit = cxxForRangeStmt(hasRangeInit(declRefExpr(
4826eb372e4SPiotr Zegar       allOf(canResolveToExpr(Exp), hasType(nonConstReferenceType())))));
483e9192f83SShuai Wang 
484f85aedc1SStephen Kelly   const auto Matches = match(
4856eb372e4SPiotr Zegar       traverse(
4866eb372e4SPiotr Zegar           TK_AsIs,
4876eb372e4SPiotr Zegar           findFirst(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand, AsNonConstThis,
4886eb372e4SPiotr Zegar                                AsAmpersandOperand, AsPointerFromArrayDecay,
4896eb372e4SPiotr Zegar                                AsOperatorArrowThis, AsNonConstRefArg,
4906eb372e4SPiotr Zegar                                AsLambdaRefCaptureInit, AsNonConstRefReturn,
4916eb372e4SPiotr Zegar                                AsNonConstRefRangeInit))
492f85aedc1SStephen Kelly                         .bind("stmt"))),
493e9192f83SShuai Wang       Stm, Context);
494e9192f83SShuai Wang   return selectFirst<Stmt>("stmt", Matches);
495e9192f83SShuai Wang }
496e9192f83SShuai Wang 
497f40f4fceSCongcong Cai const Stmt *
498f40f4fceSCongcong Cai ExprMutationAnalyzer::Analyzer::findMemberMutation(const Expr *Exp) {
499e9192f83SShuai Wang   // Check whether any member of 'Exp' is mutated.
5006eb372e4SPiotr Zegar   const auto MemberExprs = match(
5016eb372e4SPiotr Zegar       findAll(expr(anyOf(memberExpr(hasObjectExpression(canResolveToExpr(Exp))),
5026eb372e4SPiotr Zegar                          cxxDependentScopeMemberExpr(
5036eb372e4SPiotr Zegar                              hasObjectExpression(canResolveToExpr(Exp))),
504feb7b191SCongcong Cai                          binaryOperator(hasOperatorName(".*"),
505feb7b191SCongcong Cai                                         hasLHS(equalsNode(Exp)))))
506aaaa310dSShuai Wang                   .bind(NodeID<Expr>::value)),
507e9192f83SShuai Wang       Stm, Context);
508e9192f83SShuai Wang   return findExprMutation(MemberExprs);
509e9192f83SShuai Wang }
510e9192f83SShuai Wang 
511f40f4fceSCongcong Cai const Stmt *
512f40f4fceSCongcong Cai ExprMutationAnalyzer::Analyzer::findArrayElementMutation(const Expr *Exp) {
513e9192f83SShuai Wang   // Check whether any element of an array is mutated.
5146eb372e4SPiotr Zegar   const auto SubscriptExprs = match(
5156eb372e4SPiotr Zegar       findAll(arraySubscriptExpr(
5166eb372e4SPiotr Zegar                   anyOf(hasBase(canResolveToExpr(Exp)),
5176eb372e4SPiotr Zegar                         hasBase(implicitCastExpr(allOf(
5186eb372e4SPiotr Zegar                             hasCastKind(CK_ArrayToPointerDecay),
5196eb372e4SPiotr Zegar                             hasSourceExpression(canResolveToExpr(Exp)))))))
520aaaa310dSShuai Wang                   .bind(NodeID<Expr>::value)),
521e9192f83SShuai Wang       Stm, Context);
522e9192f83SShuai Wang   return findExprMutation(SubscriptExprs);
523e9192f83SShuai Wang }
524e9192f83SShuai Wang 
525f40f4fceSCongcong Cai const Stmt *ExprMutationAnalyzer::Analyzer::findCastMutation(const Expr *Exp) {
526e517e5cfSJonas Toth   // If the 'Exp' is explicitly casted to a non-const reference type the
527e517e5cfSJonas Toth   // 'Exp' is considered to be modified.
5286eb372e4SPiotr Zegar   const auto ExplicitCast =
5296eb372e4SPiotr Zegar       match(findFirst(stmt(castExpr(hasSourceExpression(canResolveToExpr(Exp)),
5306eb372e4SPiotr Zegar                                     explicitCastExpr(hasDestinationType(
5316eb372e4SPiotr Zegar                                         nonConstReferenceType()))))
532e517e5cfSJonas Toth                           .bind("stmt")),
533e517e5cfSJonas Toth             Stm, Context);
534e517e5cfSJonas Toth 
535e517e5cfSJonas Toth   if (const auto *CastStmt = selectFirst<Stmt>("stmt", ExplicitCast))
536e517e5cfSJonas Toth     return CastStmt;
537e517e5cfSJonas Toth 
538e9192f83SShuai Wang   // If 'Exp' is casted to any non-const reference type, check the castExpr.
539e517e5cfSJonas Toth   const auto Casts = match(
5406eb372e4SPiotr Zegar       findAll(expr(castExpr(hasSourceExpression(canResolveToExpr(Exp)),
5416eb372e4SPiotr Zegar                             anyOf(explicitCastExpr(hasDestinationType(
5426eb372e4SPiotr Zegar                                       nonConstReferenceType())),
543e9192f83SShuai Wang                                   implicitCastExpr(hasImplicitDestinationType(
544e517e5cfSJonas Toth                                       nonConstReferenceType())))))
545aaaa310dSShuai Wang                   .bind(NodeID<Expr>::value)),
546e9192f83SShuai Wang       Stm, Context);
547e517e5cfSJonas Toth 
5484305993cSShuai Wang   if (const Stmt *S = findExprMutation(Casts))
5494305993cSShuai Wang     return S;
5504305993cSShuai Wang   // Treat std::{move,forward} as cast.
5514305993cSShuai Wang   const auto Calls =
5524305993cSShuai Wang       match(findAll(callExpr(callee(namedDecl(
5534305993cSShuai Wang                                  hasAnyName("::std::move", "::std::forward"))),
5546eb372e4SPiotr Zegar                              hasArgument(0, canResolveToExpr(Exp)))
5554305993cSShuai Wang                         .bind("expr")),
5564305993cSShuai Wang             Stm, Context);
5574305993cSShuai Wang   return findExprMutation(Calls);
558e9192f83SShuai Wang }
559e9192f83SShuai Wang 
560f40f4fceSCongcong Cai const Stmt *
561f40f4fceSCongcong Cai ExprMutationAnalyzer::Analyzer::findRangeLoopMutation(const Expr *Exp) {
562e517e5cfSJonas Toth   // Keep the ordering for the specific initialization matches to happen first,
563e517e5cfSJonas Toth   // because it is cheaper to match all potential modifications of the loop
564e517e5cfSJonas Toth   // variable.
565e517e5cfSJonas Toth 
566e517e5cfSJonas Toth   // The range variable is a reference to a builtin array. In that case the
567e517e5cfSJonas Toth   // array is considered modified if the loop-variable is a non-const reference.
568e517e5cfSJonas Toth   const auto DeclStmtToNonRefToArray = declStmt(hasSingleDecl(varDecl(hasType(
569e517e5cfSJonas Toth       hasUnqualifiedDesugaredType(referenceType(pointee(arrayType())))))));
5706eb372e4SPiotr Zegar   const auto RefToArrayRefToElements = match(
5716eb372e4SPiotr Zegar       findFirst(stmt(cxxForRangeStmt(
57246ae26e7SJonas Toth                          hasLoopVariable(
57346ae26e7SJonas Toth                              varDecl(anyOf(hasType(nonConstReferenceType()),
57446ae26e7SJonas Toth                                            hasType(nonConstPointerType())))
575e517e5cfSJonas Toth                                  .bind(NodeID<Decl>::value)),
576e517e5cfSJonas Toth                          hasRangeStmt(DeclStmtToNonRefToArray),
5776eb372e4SPiotr Zegar                          hasRangeInit(canResolveToExpr(Exp))))
578e517e5cfSJonas Toth                     .bind("stmt")),
579e517e5cfSJonas Toth       Stm, Context);
580e517e5cfSJonas Toth 
581e517e5cfSJonas Toth   if (const auto *BadRangeInitFromArray =
582e517e5cfSJonas Toth           selectFirst<Stmt>("stmt", RefToArrayRefToElements))
583e517e5cfSJonas Toth     return BadRangeInitFromArray;
584e517e5cfSJonas Toth 
585e517e5cfSJonas Toth   // Small helper to match special cases in range-for loops.
586e517e5cfSJonas Toth   //
587e517e5cfSJonas Toth   // It is possible that containers do not provide a const-overload for their
588e517e5cfSJonas Toth   // iterator accessors. If this is the case, the variable is used non-const
589e517e5cfSJonas Toth   // no matter what happens in the loop. This requires special detection as it
590e517e5cfSJonas Toth   // is then faster to find all mutations of the loop variable.
591e517e5cfSJonas Toth   // It aims at a different modification as well.
592e517e5cfSJonas Toth   const auto HasAnyNonConstIterator =
593e517e5cfSJonas Toth       anyOf(allOf(hasMethod(allOf(hasName("begin"), unless(isConst()))),
594e517e5cfSJonas Toth                   unless(hasMethod(allOf(hasName("begin"), isConst())))),
595e517e5cfSJonas Toth             allOf(hasMethod(allOf(hasName("end"), unless(isConst()))),
596e517e5cfSJonas Toth                   unless(hasMethod(allOf(hasName("end"), isConst())))));
597e517e5cfSJonas Toth 
598e517e5cfSJonas Toth   const auto DeclStmtToNonConstIteratorContainer = declStmt(
599e517e5cfSJonas Toth       hasSingleDecl(varDecl(hasType(hasUnqualifiedDesugaredType(referenceType(
600e517e5cfSJonas Toth           pointee(hasDeclaration(cxxRecordDecl(HasAnyNonConstIterator)))))))));
601e517e5cfSJonas Toth 
6026eb372e4SPiotr Zegar   const auto RefToContainerBadIterators = match(
6036eb372e4SPiotr Zegar       findFirst(stmt(cxxForRangeStmt(allOf(
604e517e5cfSJonas Toth                          hasRangeStmt(DeclStmtToNonConstIteratorContainer),
6056eb372e4SPiotr Zegar                          hasRangeInit(canResolveToExpr(Exp)))))
606e517e5cfSJonas Toth                     .bind("stmt")),
607e517e5cfSJonas Toth       Stm, Context);
608e517e5cfSJonas Toth 
609e517e5cfSJonas Toth   if (const auto *BadIteratorsContainer =
610e517e5cfSJonas Toth           selectFirst<Stmt>("stmt", RefToContainerBadIterators))
611e517e5cfSJonas Toth     return BadIteratorsContainer;
612e517e5cfSJonas Toth 
613e9192f83SShuai Wang   // If range for looping over 'Exp' with a non-const reference loop variable,
614e9192f83SShuai Wang   // check all declRefExpr of the loop variable.
615e9192f83SShuai Wang   const auto LoopVars =
616e9192f83SShuai Wang       match(findAll(cxxForRangeStmt(
617aaaa310dSShuai Wang                 hasLoopVariable(varDecl(hasType(nonConstReferenceType()))
618aaaa310dSShuai Wang                                     .bind(NodeID<Decl>::value)),
6196eb372e4SPiotr Zegar                 hasRangeInit(canResolveToExpr(Exp)))),
620e9192f83SShuai Wang             Stm, Context);
621e9192f83SShuai Wang   return findDeclMutation(LoopVars);
622e9192f83SShuai Wang }
623e9192f83SShuai Wang 
624f40f4fceSCongcong Cai const Stmt *
625f40f4fceSCongcong Cai ExprMutationAnalyzer::Analyzer::findReferenceMutation(const Expr *Exp) {
626e9192f83SShuai Wang   // Follow non-const reference returned by `operator*()` of move-only classes.
627e9192f83SShuai Wang   // These are typically smart pointers with unique ownership so we treat
628e9192f83SShuai Wang   // mutation of pointee as mutation of the smart pointer itself.
6296eb372e4SPiotr Zegar   const auto Ref = match(
6306eb372e4SPiotr Zegar       findAll(cxxOperatorCallExpr(
631e9192f83SShuai Wang                   hasOverloadedOperatorName("*"),
632e9192f83SShuai Wang                   callee(cxxMethodDecl(ofClass(isMoveOnly()),
633cef621d0SShuai Wang                                        returns(nonConstReferenceType()))),
6346eb372e4SPiotr Zegar                   argumentCountIs(1), hasArgument(0, canResolveToExpr(Exp)))
635aaaa310dSShuai Wang                   .bind(NodeID<Expr>::value)),
636e9192f83SShuai Wang       Stm, Context);
637e9192f83SShuai Wang   if (const Stmt *S = findExprMutation(Ref))
638e9192f83SShuai Wang     return S;
639e9192f83SShuai Wang 
640e9192f83SShuai Wang   // If 'Exp' is bound to a non-const reference, check all declRefExpr to that.
641e9192f83SShuai Wang   const auto Refs = match(
642e9192f83SShuai Wang       stmt(forEachDescendant(
6436eb372e4SPiotr Zegar           varDecl(hasType(nonConstReferenceType()),
6446eb372e4SPiotr Zegar                   hasInitializer(anyOf(
6456eb372e4SPiotr Zegar                       canResolveToExpr(Exp),
6466eb372e4SPiotr Zegar                       memberExpr(hasObjectExpression(canResolveToExpr(Exp))))),
647e9192f83SShuai Wang                   hasParent(declStmt().bind("stmt")),
648e517e5cfSJonas Toth                   // Don't follow the reference in range statement, we've
649e517e5cfSJonas Toth                   // handled that separately.
6506eb372e4SPiotr Zegar                   unless(hasParent(declStmt(hasParent(cxxForRangeStmt(
6516eb372e4SPiotr Zegar                       hasRangeStmt(equalsBoundNode("stmt"))))))))
652aaaa310dSShuai Wang               .bind(NodeID<Decl>::value))),
653e9192f83SShuai Wang       Stm, Context);
654e9192f83SShuai Wang   return findDeclMutation(Refs);
655e9192f83SShuai Wang }
656e9192f83SShuai Wang 
657f40f4fceSCongcong Cai const Stmt *
658f40f4fceSCongcong Cai ExprMutationAnalyzer::Analyzer::findFunctionArgMutation(const Expr *Exp) {
659cb98b707SShuai Wang   const auto NonConstRefParam = forEachArgumentWithParam(
6606eb372e4SPiotr Zegar       canResolveToExpr(Exp),
661cb98b707SShuai Wang       parmVarDecl(hasType(nonConstReferenceType())).bind("parm"));
662cb98b707SShuai Wang   const auto IsInstantiated = hasDeclaration(isInstantiated());
663cb98b707SShuai Wang   const auto FuncDecl = hasDeclaration(functionDecl().bind("func"));
664cb98b707SShuai Wang   const auto Matches = match(
665f85aedc1SStephen Kelly       traverse(
666027899daSAlexander Kornienko           TK_AsIs,
667f85aedc1SStephen Kelly           findAll(
668f85aedc1SStephen Kelly               expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl,
6694305993cSShuai Wang                                   unless(callee(namedDecl(hasAnyName(
6704305993cSShuai Wang                                       "::std::move", "::std::forward"))))),
671cb98b707SShuai Wang                          cxxConstructExpr(NonConstRefParam, IsInstantiated,
672cb98b707SShuai Wang                                           FuncDecl)))
673f85aedc1SStephen Kelly                   .bind(NodeID<Expr>::value))),
674cb98b707SShuai Wang       Stm, Context);
675cb98b707SShuai Wang   for (const auto &Nodes : Matches) {
676aaaa310dSShuai Wang     const auto *Exp = Nodes.getNodeAs<Expr>(NodeID<Expr>::value);
677cb98b707SShuai Wang     const auto *Func = Nodes.getNodeAs<FunctionDecl>("func");
67886e5cb0eSShuai Wang     if (!Func->getBody() || !Func->getPrimaryTemplate())
679cb98b707SShuai Wang       return Exp;
680cb98b707SShuai Wang 
681cb98b707SShuai Wang     const auto *Parm = Nodes.getNodeAs<ParmVarDecl>("parm");
682cb98b707SShuai Wang     const ArrayRef<ParmVarDecl *> AllParams =
683cb98b707SShuai Wang         Func->getPrimaryTemplate()->getTemplatedDecl()->parameters();
684cb98b707SShuai Wang     QualType ParmType =
685cb98b707SShuai Wang         AllParams[std::min<size_t>(Parm->getFunctionScopeIndex(),
686cb98b707SShuai Wang                                    AllParams.size() - 1)]
687cb98b707SShuai Wang             ->getType();
688cb98b707SShuai Wang     if (const auto *T = ParmType->getAs<PackExpansionType>())
689cb98b707SShuai Wang       ParmType = T->getPattern();
690cb98b707SShuai Wang 
691cb98b707SShuai Wang     // If param type is forwarding reference, follow into the function
692cb98b707SShuai Wang     // definition and see whether the param is mutated inside.
693cb98b707SShuai Wang     if (const auto *RefType = ParmType->getAs<RValueReferenceType>()) {
694cb98b707SShuai Wang       if (!RefType->getPointeeType().getQualifiers() &&
695cb98b707SShuai Wang           RefType->getPointeeType()->getAs<TemplateTypeParmType>()) {
696f40f4fceSCongcong Cai         FunctionParmMutationAnalyzer *Analyzer =
697f40f4fceSCongcong Cai             FunctionParmMutationAnalyzer::getFunctionParmMutationAnalyzer(
698f40f4fceSCongcong Cai                 *Func, Context, Memorized);
699cb98b707SShuai Wang         if (Analyzer->findMutation(Parm))
700cb98b707SShuai Wang           return Exp;
701cb98b707SShuai Wang         continue;
702cb98b707SShuai Wang       }
703cb98b707SShuai Wang     }
704cb98b707SShuai Wang     // Not forwarding reference.
705cb98b707SShuai Wang     return Exp;
706cb98b707SShuai Wang   }
707cb98b707SShuai Wang   return nullptr;
708cb98b707SShuai Wang }
709cb98b707SShuai Wang 
710*53ea5ffcSCongcong Cai const Stmt *
711*53ea5ffcSCongcong Cai ExprMutationAnalyzer::Analyzer::findPointeeValueMutation(const Expr *Exp) {
712*53ea5ffcSCongcong Cai   const auto Matches = match(
713*53ea5ffcSCongcong Cai       stmt(forEachDescendant(
714*53ea5ffcSCongcong Cai           expr(anyOf(
715*53ea5ffcSCongcong Cai                    // deref by *
716*53ea5ffcSCongcong Cai                    unaryOperator(hasOperatorName("*"),
717*53ea5ffcSCongcong Cai                                  hasUnaryOperand(canResolveToExprPointee(Exp))),
718*53ea5ffcSCongcong Cai                    // deref by []
719*53ea5ffcSCongcong Cai                    arraySubscriptExpr(hasBase(canResolveToExprPointee(Exp)))))
720*53ea5ffcSCongcong Cai               .bind(NodeID<Expr>::value))),
721*53ea5ffcSCongcong Cai       Stm, Context);
722*53ea5ffcSCongcong Cai   return findExprMutation(Matches);
723*53ea5ffcSCongcong Cai }
724*53ea5ffcSCongcong Cai 
725*53ea5ffcSCongcong Cai const Stmt *
726*53ea5ffcSCongcong Cai ExprMutationAnalyzer::Analyzer::findPointeeMemberMutation(const Expr *Exp) {
727*53ea5ffcSCongcong Cai   const Stmt *MemberCallExpr = selectFirst<Stmt>(
728*53ea5ffcSCongcong Cai       "stmt", match(stmt(forEachDescendant(
729*53ea5ffcSCongcong Cai                         cxxMemberCallExpr(on(canResolveToExprPointee(Exp)),
730*53ea5ffcSCongcong Cai                                           unless(isConstCallee()))
731*53ea5ffcSCongcong Cai                             .bind("stmt"))),
732*53ea5ffcSCongcong Cai                     Stm, Context));
733*53ea5ffcSCongcong Cai   if (MemberCallExpr)
734*53ea5ffcSCongcong Cai     return MemberCallExpr;
735*53ea5ffcSCongcong Cai   const auto Matches =
736*53ea5ffcSCongcong Cai       match(stmt(forEachDescendant(
737*53ea5ffcSCongcong Cai                 memberExpr(hasObjectExpression(canResolveToExprPointee(Exp)))
738*53ea5ffcSCongcong Cai                     .bind(NodeID<Expr>::value))),
739*53ea5ffcSCongcong Cai             Stm, Context);
740*53ea5ffcSCongcong Cai   return findExprMutation(Matches);
741*53ea5ffcSCongcong Cai }
742*53ea5ffcSCongcong Cai 
743*53ea5ffcSCongcong Cai const Stmt *
744*53ea5ffcSCongcong Cai ExprMutationAnalyzer::Analyzer::findPointeeToNonConst(const Expr *Exp) {
745*53ea5ffcSCongcong Cai   const auto NonConstPointerOrDependentType =
746*53ea5ffcSCongcong Cai       type(anyOf(nonConstPointerType(), isDependentType()));
747*53ea5ffcSCongcong Cai 
748*53ea5ffcSCongcong Cai   // assign
749*53ea5ffcSCongcong Cai   const auto InitToNonConst =
750*53ea5ffcSCongcong Cai       varDecl(hasType(NonConstPointerOrDependentType),
751*53ea5ffcSCongcong Cai               hasInitializer(expr(canResolveToExprPointee(Exp)).bind("stmt")));
752*53ea5ffcSCongcong Cai   const auto AssignToNonConst =
753*53ea5ffcSCongcong Cai       binaryOperation(hasOperatorName("="),
754*53ea5ffcSCongcong Cai                       hasLHS(expr(hasType(NonConstPointerOrDependentType))),
755*53ea5ffcSCongcong Cai                       hasRHS(canResolveToExprPointee(Exp)));
756*53ea5ffcSCongcong Cai   // arguments like
757*53ea5ffcSCongcong Cai   const auto ArgOfInstantiationDependent = allOf(
758*53ea5ffcSCongcong Cai       hasAnyArgument(canResolveToExprPointee(Exp)), isInstantiationDependent());
759*53ea5ffcSCongcong Cai   const auto ArgOfNonConstParameter = forEachArgumentWithParamType(
760*53ea5ffcSCongcong Cai       canResolveToExprPointee(Exp), NonConstPointerOrDependentType);
761*53ea5ffcSCongcong Cai   const auto CallLikeMatcher =
762*53ea5ffcSCongcong Cai       anyOf(ArgOfNonConstParameter, ArgOfInstantiationDependent);
763*53ea5ffcSCongcong Cai   const auto PassAsNonConstArg =
764*53ea5ffcSCongcong Cai       expr(anyOf(cxxUnresolvedConstructExpr(ArgOfInstantiationDependent),
765*53ea5ffcSCongcong Cai                  cxxConstructExpr(CallLikeMatcher), callExpr(CallLikeMatcher),
766*53ea5ffcSCongcong Cai                  parenListExpr(has(canResolveToExprPointee(Exp))),
767*53ea5ffcSCongcong Cai                  initListExpr(hasAnyInit(canResolveToExprPointee(Exp)))));
768*53ea5ffcSCongcong Cai   // cast
769*53ea5ffcSCongcong Cai   const auto CastToNonConst =
770*53ea5ffcSCongcong Cai       explicitCastExpr(hasSourceExpression(canResolveToExprPointee(Exp)),
771*53ea5ffcSCongcong Cai                        hasDestinationType(NonConstPointerOrDependentType));
772*53ea5ffcSCongcong Cai 
773*53ea5ffcSCongcong Cai   // capture
774*53ea5ffcSCongcong Cai   // FIXME: false positive if the pointee does not change in lambda
775*53ea5ffcSCongcong Cai   const auto CaptureNoConst = lambdaExpr(hasCaptureInit(Exp));
776*53ea5ffcSCongcong Cai 
777*53ea5ffcSCongcong Cai   const auto Matches =
778*53ea5ffcSCongcong Cai       match(stmt(anyOf(forEachDescendant(
779*53ea5ffcSCongcong Cai                            stmt(anyOf(AssignToNonConst, PassAsNonConstArg,
780*53ea5ffcSCongcong Cai                                       CastToNonConst, CaptureNoConst))
781*53ea5ffcSCongcong Cai                                .bind("stmt")),
782*53ea5ffcSCongcong Cai                        forEachDescendant(InitToNonConst))),
783*53ea5ffcSCongcong Cai             Stm, Context);
784*53ea5ffcSCongcong Cai   return selectFirst<Stmt>("stmt", Matches);
785*53ea5ffcSCongcong Cai }
786*53ea5ffcSCongcong Cai 
787cb98b707SShuai Wang FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer(
788f40f4fceSCongcong Cai     const FunctionDecl &Func, ASTContext &Context,
789f40f4fceSCongcong Cai     ExprMutationAnalyzer::Memoized &Memorized)
790f40f4fceSCongcong Cai     : BodyAnalyzer(*Func.getBody(), Context, Memorized) {
791cb98b707SShuai Wang   if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(&Func)) {
792cb98b707SShuai Wang     // CXXCtorInitializer might also mutate Param but they're not part of
793cb98b707SShuai Wang     // function body, check them eagerly here since they're typically trivial.
794cb98b707SShuai Wang     for (const CXXCtorInitializer *Init : Ctor->inits()) {
795f40f4fceSCongcong Cai       ExprMutationAnalyzer::Analyzer InitAnalyzer(*Init->getInit(), Context,
796f40f4fceSCongcong Cai                                                   Memorized);
797cb98b707SShuai Wang       for (const ParmVarDecl *Parm : Ctor->parameters()) {
798ea9d4040SKazu Hirata         if (Results.contains(Parm))
799cb98b707SShuai Wang           continue;
800cef621d0SShuai Wang         if (const Stmt *S = InitAnalyzer.findMutation(Parm))
801cb98b707SShuai Wang           Results[Parm] = S;
802cb98b707SShuai Wang       }
803cb98b707SShuai Wang     }
804cb98b707SShuai Wang   }
805cb98b707SShuai Wang }
806cb98b707SShuai Wang 
807cb98b707SShuai Wang const Stmt *
808cb98b707SShuai Wang FunctionParmMutationAnalyzer::findMutation(const ParmVarDecl *Parm) {
809cb98b707SShuai Wang   const auto Memoized = Results.find(Parm);
810cb98b707SShuai Wang   if (Memoized != Results.end())
811cb98b707SShuai Wang     return Memoized->second;
812f40f4fceSCongcong Cai   // To handle call A -> call B -> call A. Assume parameters of A is not mutated
813f40f4fceSCongcong Cai   // before analyzing parameters of A. Then when analyzing the second "call A",
814f40f4fceSCongcong Cai   // FunctionParmMutationAnalyzer can use this memoized value to avoid infinite
815f40f4fceSCongcong Cai   // recursion.
816f40f4fceSCongcong Cai   Results[Parm] = nullptr;
817cef621d0SShuai Wang   if (const Stmt *S = BodyAnalyzer.findMutation(Parm))
818cb98b707SShuai Wang     return Results[Parm] = S;
819f40f4fceSCongcong Cai   return Results[Parm];
820cb98b707SShuai Wang }
821cb98b707SShuai Wang 
822e9192f83SShuai Wang } // namespace clang
823