10b57cec5SDimitry Andric //===---------- ExprMutationAnalyzer.cpp ----------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric #include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
9e8d8bef9SDimitry Andric #include "clang/AST/Expr.h"
10e8d8bef9SDimitry Andric #include "clang/AST/OperationKinds.h"
110b57cec5SDimitry Andric #include "clang/ASTMatchers/ASTMatchFinder.h"
12e8d8bef9SDimitry Andric #include "clang/ASTMatchers/ASTMatchers.h"
130b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h"
140b57cec5SDimitry Andric
150b57cec5SDimitry Andric namespace clang {
160b57cec5SDimitry Andric using namespace ast_matchers;
170b57cec5SDimitry Andric
18297eecfbSDimitry Andric // Check if result of Source expression could be a Target expression.
19297eecfbSDimitry Andric // Checks:
20297eecfbSDimitry Andric // - Implicit Casts
21297eecfbSDimitry Andric // - Binary Operators
22297eecfbSDimitry Andric // - ConditionalOperator
23297eecfbSDimitry Andric // - BinaryConditionalOperator
canExprResolveTo(const Expr * Source,const Expr * Target)24297eecfbSDimitry Andric static bool canExprResolveTo(const Expr *Source, const Expr *Target) {
25297eecfbSDimitry Andric
26297eecfbSDimitry Andric const auto IgnoreDerivedToBase = [](const Expr *E, auto Matcher) {
27297eecfbSDimitry Andric if (Matcher(E))
28297eecfbSDimitry Andric return true;
29297eecfbSDimitry Andric if (const auto *Cast = dyn_cast<ImplicitCastExpr>(E)) {
30297eecfbSDimitry Andric if ((Cast->getCastKind() == CK_DerivedToBase ||
31297eecfbSDimitry Andric Cast->getCastKind() == CK_UncheckedDerivedToBase) &&
32297eecfbSDimitry Andric Matcher(Cast->getSubExpr()))
33297eecfbSDimitry Andric return true;
34297eecfbSDimitry Andric }
35297eecfbSDimitry Andric return false;
36297eecfbSDimitry Andric };
37297eecfbSDimitry Andric
38297eecfbSDimitry Andric const auto EvalCommaExpr = [](const Expr *E, auto Matcher) {
39297eecfbSDimitry Andric const Expr *Result = E;
40297eecfbSDimitry Andric while (const auto *BOComma =
41297eecfbSDimitry Andric dyn_cast_or_null<BinaryOperator>(Result->IgnoreParens())) {
42297eecfbSDimitry Andric if (!BOComma->isCommaOp())
43297eecfbSDimitry Andric break;
44297eecfbSDimitry Andric Result = BOComma->getRHS();
45297eecfbSDimitry Andric }
46297eecfbSDimitry Andric
47297eecfbSDimitry Andric return Result != E && Matcher(Result);
48297eecfbSDimitry Andric };
49297eecfbSDimitry Andric
50297eecfbSDimitry Andric // The 'ConditionalOperatorM' matches on `<anything> ? <expr> : <expr>`.
51297eecfbSDimitry Andric // This matching must be recursive because `<expr>` can be anything resolving
52297eecfbSDimitry Andric // to the `InnerMatcher`, for example another conditional operator.
53297eecfbSDimitry Andric // The edge-case `BaseClass &b = <cond> ? DerivedVar1 : DerivedVar2;`
54297eecfbSDimitry Andric // is handled, too. The implicit cast happens outside of the conditional.
55297eecfbSDimitry Andric // This is matched by `IgnoreDerivedToBase(canResolveToExpr(InnerMatcher))`
56297eecfbSDimitry Andric // below.
57297eecfbSDimitry Andric const auto ConditionalOperatorM = [Target](const Expr *E) {
58297eecfbSDimitry Andric if (const auto *OP = dyn_cast<ConditionalOperator>(E)) {
59297eecfbSDimitry Andric if (const auto *TE = OP->getTrueExpr()->IgnoreParens())
60297eecfbSDimitry Andric if (canExprResolveTo(TE, Target))
61297eecfbSDimitry Andric return true;
62297eecfbSDimitry Andric if (const auto *FE = OP->getFalseExpr()->IgnoreParens())
63297eecfbSDimitry Andric if (canExprResolveTo(FE, Target))
64297eecfbSDimitry Andric return true;
65297eecfbSDimitry Andric }
66297eecfbSDimitry Andric return false;
67297eecfbSDimitry Andric };
68297eecfbSDimitry Andric
69297eecfbSDimitry Andric const auto ElvisOperator = [Target](const Expr *E) {
70297eecfbSDimitry Andric if (const auto *OP = dyn_cast<BinaryConditionalOperator>(E)) {
71297eecfbSDimitry Andric if (const auto *TE = OP->getTrueExpr()->IgnoreParens())
72297eecfbSDimitry Andric if (canExprResolveTo(TE, Target))
73297eecfbSDimitry Andric return true;
74297eecfbSDimitry Andric if (const auto *FE = OP->getFalseExpr()->IgnoreParens())
75297eecfbSDimitry Andric if (canExprResolveTo(FE, Target))
76297eecfbSDimitry Andric return true;
77297eecfbSDimitry Andric }
78297eecfbSDimitry Andric return false;
79297eecfbSDimitry Andric };
80297eecfbSDimitry Andric
81297eecfbSDimitry Andric const Expr *SourceExprP = Source->IgnoreParens();
82297eecfbSDimitry Andric return IgnoreDerivedToBase(SourceExprP,
83297eecfbSDimitry Andric [&](const Expr *E) {
84297eecfbSDimitry Andric return E == Target || ConditionalOperatorM(E) ||
85297eecfbSDimitry Andric ElvisOperator(E);
86297eecfbSDimitry Andric }) ||
87297eecfbSDimitry Andric EvalCommaExpr(SourceExprP, [&](const Expr *E) {
88297eecfbSDimitry Andric return IgnoreDerivedToBase(
89297eecfbSDimitry Andric E->IgnoreParens(), [&](const Expr *EE) { return EE == Target; });
90297eecfbSDimitry Andric });
91297eecfbSDimitry Andric }
92297eecfbSDimitry Andric
930b57cec5SDimitry Andric namespace {
940b57cec5SDimitry Andric
AST_MATCHER_P(LambdaExpr,hasCaptureInit,const Expr *,E)950b57cec5SDimitry Andric AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) {
960b57cec5SDimitry Andric return llvm::is_contained(Node.capture_inits(), E);
970b57cec5SDimitry Andric }
980b57cec5SDimitry Andric
AST_MATCHER_P(CXXForRangeStmt,hasRangeStmt,ast_matchers::internal::Matcher<DeclStmt>,InnerMatcher)990b57cec5SDimitry Andric AST_MATCHER_P(CXXForRangeStmt, hasRangeStmt,
1000b57cec5SDimitry Andric ast_matchers::internal::Matcher<DeclStmt>, InnerMatcher) {
1010b57cec5SDimitry Andric const DeclStmt *const Range = Node.getRangeStmt();
1020b57cec5SDimitry Andric return InnerMatcher.matches(*Range, Finder, Builder);
1030b57cec5SDimitry Andric }
1040b57cec5SDimitry Andric
AST_MATCHER_P(Stmt,canResolveToExpr,const Stmt *,Inner)105297eecfbSDimitry Andric AST_MATCHER_P(Stmt, canResolveToExpr, const Stmt *, Inner) {
10681ad6265SDimitry Andric auto *Exp = dyn_cast<Expr>(&Node);
107297eecfbSDimitry Andric if (!Exp)
108297eecfbSDimitry Andric return true;
109297eecfbSDimitry Andric auto *Target = dyn_cast<Expr>(Inner);
110297eecfbSDimitry Andric if (!Target)
111297eecfbSDimitry Andric return false;
112297eecfbSDimitry Andric return canExprResolveTo(Exp, Target);
113e8d8bef9SDimitry Andric }
114e8d8bef9SDimitry Andric
115e8d8bef9SDimitry Andric // Similar to 'hasAnyArgument', but does not work because 'InitListExpr' does
116e8d8bef9SDimitry Andric // not have the 'arguments()' method.
AST_MATCHER_P(InitListExpr,hasAnyInit,ast_matchers::internal::Matcher<Expr>,InnerMatcher)117e8d8bef9SDimitry Andric AST_MATCHER_P(InitListExpr, hasAnyInit, ast_matchers::internal::Matcher<Expr>,
118e8d8bef9SDimitry Andric InnerMatcher) {
119e8d8bef9SDimitry Andric for (const Expr *Arg : Node.inits()) {
120e8d8bef9SDimitry Andric ast_matchers::internal::BoundNodesTreeBuilder Result(*Builder);
121e8d8bef9SDimitry Andric if (InnerMatcher.matches(*Arg, Finder, &Result)) {
122e8d8bef9SDimitry Andric *Builder = std::move(Result);
123e8d8bef9SDimitry Andric return true;
124e8d8bef9SDimitry Andric }
125e8d8bef9SDimitry Andric }
126e8d8bef9SDimitry Andric return false;
127e8d8bef9SDimitry Andric }
128e8d8bef9SDimitry Andric
1290b57cec5SDimitry Andric const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXTypeidExpr>
1300b57cec5SDimitry Andric cxxTypeidExpr;
1310b57cec5SDimitry Andric
AST_MATCHER(CXXTypeidExpr,isPotentiallyEvaluated)1320b57cec5SDimitry Andric AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) {
1330b57cec5SDimitry Andric return Node.isPotentiallyEvaluated();
1340b57cec5SDimitry Andric }
1350b57cec5SDimitry Andric
AST_MATCHER(CXXMemberCallExpr,isConstCallee)1365f757f3fSDimitry Andric AST_MATCHER(CXXMemberCallExpr, isConstCallee) {
1375f757f3fSDimitry Andric const Decl *CalleeDecl = Node.getCalleeDecl();
1385f757f3fSDimitry Andric const auto *VD = dyn_cast_or_null<ValueDecl>(CalleeDecl);
1395f757f3fSDimitry Andric if (!VD)
1405f757f3fSDimitry Andric return false;
1415f757f3fSDimitry Andric const QualType T = VD->getType().getCanonicalType();
1425f757f3fSDimitry Andric const auto *MPT = dyn_cast<MemberPointerType>(T);
1435f757f3fSDimitry Andric const auto *FPT = MPT ? cast<FunctionProtoType>(MPT->getPointeeType())
1445f757f3fSDimitry Andric : dyn_cast<FunctionProtoType>(T);
1455f757f3fSDimitry Andric if (!FPT)
1465f757f3fSDimitry Andric return false;
1475f757f3fSDimitry Andric return FPT->isConst();
1485f757f3fSDimitry Andric }
1495f757f3fSDimitry Andric
AST_MATCHER_P(GenericSelectionExpr,hasControllingExpr,ast_matchers::internal::Matcher<Expr>,InnerMatcher)1500b57cec5SDimitry Andric AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr,
1510b57cec5SDimitry Andric ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
15206c3fb27SDimitry Andric if (Node.isTypePredicate())
15306c3fb27SDimitry Andric return false;
1540b57cec5SDimitry Andric return InnerMatcher.matches(*Node.getControllingExpr(), Finder, Builder);
1550b57cec5SDimitry Andric }
1560b57cec5SDimitry Andric
157297eecfbSDimitry Andric template <typename T>
158297eecfbSDimitry Andric ast_matchers::internal::Matcher<T>
findFirst(const ast_matchers::internal::Matcher<T> & Matcher)159297eecfbSDimitry Andric findFirst(const ast_matchers::internal::Matcher<T> &Matcher) {
160297eecfbSDimitry Andric return anyOf(Matcher, hasDescendant(Matcher));
161297eecfbSDimitry Andric }
162297eecfbSDimitry Andric
__anon2875c4430902null1630b57cec5SDimitry Andric const auto nonConstReferenceType = [] {
1640b57cec5SDimitry Andric return hasUnqualifiedDesugaredType(
1650b57cec5SDimitry Andric referenceType(pointee(unless(isConstQualified()))));
1660b57cec5SDimitry Andric };
1670b57cec5SDimitry Andric
__anon2875c4430a02null1680b57cec5SDimitry Andric const auto nonConstPointerType = [] {
1690b57cec5SDimitry Andric return hasUnqualifiedDesugaredType(
1700b57cec5SDimitry Andric pointerType(pointee(unless(isConstQualified()))));
1710b57cec5SDimitry Andric };
1720b57cec5SDimitry Andric
__anon2875c4430b02null1730b57cec5SDimitry Andric const auto isMoveOnly = [] {
1740b57cec5SDimitry Andric return cxxRecordDecl(
1750b57cec5SDimitry Andric hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted()))),
1760b57cec5SDimitry Andric hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted()))),
1770b57cec5SDimitry Andric unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(),
1780b57cec5SDimitry Andric unless(isDeleted()))),
1790b57cec5SDimitry Andric hasMethod(cxxMethodDecl(isCopyAssignmentOperator(),
1800b57cec5SDimitry Andric unless(isDeleted()))))));
1810b57cec5SDimitry Andric };
1820b57cec5SDimitry Andric
1830b57cec5SDimitry Andric template <class T> struct NodeID;
1845ffd83dbSDimitry Andric template <> struct NodeID<Expr> { static constexpr StringRef value = "expr"; };
1855ffd83dbSDimitry Andric template <> struct NodeID<Decl> { static constexpr StringRef value = "decl"; };
1865ffd83dbSDimitry Andric constexpr StringRef NodeID<Expr>::value;
1875ffd83dbSDimitry Andric constexpr StringRef NodeID<Decl>::value;
1880b57cec5SDimitry Andric
189*0fca6ea1SDimitry Andric template <class T,
190*0fca6ea1SDimitry Andric class F = const Stmt *(ExprMutationAnalyzer::Analyzer::*)(const T *)>
tryEachMatch(ArrayRef<ast_matchers::BoundNodes> Matches,ExprMutationAnalyzer::Analyzer * Analyzer,F Finder)1910b57cec5SDimitry Andric const Stmt *tryEachMatch(ArrayRef<ast_matchers::BoundNodes> Matches,
192*0fca6ea1SDimitry Andric ExprMutationAnalyzer::Analyzer *Analyzer, F Finder) {
1930b57cec5SDimitry Andric const StringRef ID = NodeID<T>::value;
1940b57cec5SDimitry Andric for (const auto &Nodes : Matches) {
1950b57cec5SDimitry Andric if (const Stmt *S = (Analyzer->*Finder)(Nodes.getNodeAs<T>(ID)))
1960b57cec5SDimitry Andric return S;
1970b57cec5SDimitry Andric }
1980b57cec5SDimitry Andric return nullptr;
1990b57cec5SDimitry Andric }
2000b57cec5SDimitry Andric
2010b57cec5SDimitry Andric } // namespace
2020b57cec5SDimitry Andric
findMutation(const Expr * Exp)203*0fca6ea1SDimitry Andric const Stmt *ExprMutationAnalyzer::Analyzer::findMutation(const Expr *Exp) {
204*0fca6ea1SDimitry Andric return findMutationMemoized(
205*0fca6ea1SDimitry Andric Exp,
206*0fca6ea1SDimitry Andric {&ExprMutationAnalyzer::Analyzer::findDirectMutation,
207*0fca6ea1SDimitry Andric &ExprMutationAnalyzer::Analyzer::findMemberMutation,
208*0fca6ea1SDimitry Andric &ExprMutationAnalyzer::Analyzer::findArrayElementMutation,
209*0fca6ea1SDimitry Andric &ExprMutationAnalyzer::Analyzer::findCastMutation,
210*0fca6ea1SDimitry Andric &ExprMutationAnalyzer::Analyzer::findRangeLoopMutation,
211*0fca6ea1SDimitry Andric &ExprMutationAnalyzer::Analyzer::findReferenceMutation,
212*0fca6ea1SDimitry Andric &ExprMutationAnalyzer::Analyzer::findFunctionArgMutation},
213*0fca6ea1SDimitry Andric Memorized.Results);
2140b57cec5SDimitry Andric }
2150b57cec5SDimitry Andric
findMutation(const Decl * Dec)216*0fca6ea1SDimitry Andric const Stmt *ExprMutationAnalyzer::Analyzer::findMutation(const Decl *Dec) {
217*0fca6ea1SDimitry Andric return tryEachDeclRef(Dec, &ExprMutationAnalyzer::Analyzer::findMutation);
2180b57cec5SDimitry Andric }
2190b57cec5SDimitry Andric
220*0fca6ea1SDimitry Andric const Stmt *
findPointeeMutation(const Expr * Exp)221*0fca6ea1SDimitry Andric ExprMutationAnalyzer::Analyzer::findPointeeMutation(const Expr *Exp) {
222*0fca6ea1SDimitry Andric return findMutationMemoized(Exp, {/*TODO*/}, Memorized.PointeeResults);
2230b57cec5SDimitry Andric }
2240b57cec5SDimitry Andric
225*0fca6ea1SDimitry Andric const Stmt *
findPointeeMutation(const Decl * Dec)226*0fca6ea1SDimitry Andric ExprMutationAnalyzer::Analyzer::findPointeeMutation(const Decl *Dec) {
227*0fca6ea1SDimitry Andric return tryEachDeclRef(Dec,
228*0fca6ea1SDimitry Andric &ExprMutationAnalyzer::Analyzer::findPointeeMutation);
2290b57cec5SDimitry Andric }
2300b57cec5SDimitry Andric
findMutationMemoized(const Expr * Exp,llvm::ArrayRef<MutationFinder> Finders,Memoized::ResultMap & MemoizedResults)231*0fca6ea1SDimitry Andric const Stmt *ExprMutationAnalyzer::Analyzer::findMutationMemoized(
2320b57cec5SDimitry Andric const Expr *Exp, llvm::ArrayRef<MutationFinder> Finders,
233*0fca6ea1SDimitry Andric Memoized::ResultMap &MemoizedResults) {
2340b57cec5SDimitry Andric const auto Memoized = MemoizedResults.find(Exp);
2350b57cec5SDimitry Andric if (Memoized != MemoizedResults.end())
2360b57cec5SDimitry Andric return Memoized->second;
2370b57cec5SDimitry Andric
238*0fca6ea1SDimitry Andric // Assume Exp is not mutated before analyzing Exp.
239*0fca6ea1SDimitry Andric MemoizedResults[Exp] = nullptr;
2400b57cec5SDimitry Andric if (isUnevaluated(Exp))
241*0fca6ea1SDimitry Andric return nullptr;
2420b57cec5SDimitry Andric
2430b57cec5SDimitry Andric for (const auto &Finder : Finders) {
2440b57cec5SDimitry Andric if (const Stmt *S = (this->*Finder)(Exp))
2450b57cec5SDimitry Andric return MemoizedResults[Exp] = S;
2460b57cec5SDimitry Andric }
2470b57cec5SDimitry Andric
248*0fca6ea1SDimitry Andric return nullptr;
2490b57cec5SDimitry Andric }
2500b57cec5SDimitry Andric
251*0fca6ea1SDimitry Andric const Stmt *
tryEachDeclRef(const Decl * Dec,MutationFinder Finder)252*0fca6ea1SDimitry Andric ExprMutationAnalyzer::Analyzer::tryEachDeclRef(const Decl *Dec,
2530b57cec5SDimitry Andric MutationFinder Finder) {
2547a6dacacSDimitry Andric const auto Refs = match(
2557a6dacacSDimitry Andric findAll(
2567a6dacacSDimitry Andric declRefExpr(to(
2577a6dacacSDimitry Andric // `Dec` or a binding if `Dec` is a decomposition.
2587a6dacacSDimitry Andric anyOf(equalsNode(Dec),
2597a6dacacSDimitry Andric bindingDecl(forDecomposition(equalsNode(Dec))))
2607a6dacacSDimitry Andric //
2617a6dacacSDimitry Andric ))
2627a6dacacSDimitry Andric .bind(NodeID<Expr>::value)),
2630b57cec5SDimitry Andric Stm, Context);
2640b57cec5SDimitry Andric for (const auto &RefNodes : Refs) {
2650b57cec5SDimitry Andric const auto *E = RefNodes.getNodeAs<Expr>(NodeID<Expr>::value);
2660b57cec5SDimitry Andric if ((this->*Finder)(E))
2670b57cec5SDimitry Andric return E;
2680b57cec5SDimitry Andric }
2690b57cec5SDimitry Andric return nullptr;
2700b57cec5SDimitry Andric }
2710b57cec5SDimitry Andric
isUnevaluated(const Stmt * Exp,const Stmt & Stm,ASTContext & Context)272*0fca6ea1SDimitry Andric bool ExprMutationAnalyzer::Analyzer::isUnevaluated(const Stmt *Exp,
273*0fca6ea1SDimitry Andric const Stmt &Stm,
27481ad6265SDimitry Andric ASTContext &Context) {
27581ad6265SDimitry Andric return selectFirst<Stmt>(
2760b57cec5SDimitry Andric NodeID<Expr>::value,
2770b57cec5SDimitry Andric match(
278297eecfbSDimitry Andric findFirst(
279297eecfbSDimitry Andric stmt(canResolveToExpr(Exp),
2800b57cec5SDimitry Andric anyOf(
2810b57cec5SDimitry Andric // `Exp` is part of the underlying expression of
2820b57cec5SDimitry Andric // decltype/typeof if it has an ancestor of
2830b57cec5SDimitry Andric // typeLoc.
2840b57cec5SDimitry Andric hasAncestor(typeLoc(unless(
2850b57cec5SDimitry Andric hasAncestor(unaryExprOrTypeTraitExpr())))),
2860b57cec5SDimitry Andric hasAncestor(expr(anyOf(
2870b57cec5SDimitry Andric // `UnaryExprOrTypeTraitExpr` is unevaluated
2880b57cec5SDimitry Andric // unless it's sizeof on VLA.
2890b57cec5SDimitry Andric unaryExprOrTypeTraitExpr(unless(sizeOfExpr(
2900b57cec5SDimitry Andric hasArgumentOfType(variableArrayType())))),
2910b57cec5SDimitry Andric // `CXXTypeidExpr` is unevaluated unless it's
2920b57cec5SDimitry Andric // applied to an expression of glvalue of
2930b57cec5SDimitry Andric // polymorphic class type.
2940b57cec5SDimitry Andric cxxTypeidExpr(
2950b57cec5SDimitry Andric unless(isPotentiallyEvaluated())),
2960b57cec5SDimitry Andric // The controlling expression of
2970b57cec5SDimitry Andric // `GenericSelectionExpr` is unevaluated.
2980b57cec5SDimitry Andric genericSelectionExpr(hasControllingExpr(
2990b57cec5SDimitry Andric hasDescendant(equalsNode(Exp)))),
3000b57cec5SDimitry Andric cxxNoexceptExpr())))))
3010b57cec5SDimitry Andric .bind(NodeID<Expr>::value)),
3020b57cec5SDimitry Andric Stm, Context)) != nullptr;
3030b57cec5SDimitry Andric }
3040b57cec5SDimitry Andric
isUnevaluated(const Expr * Exp)305*0fca6ea1SDimitry Andric bool ExprMutationAnalyzer::Analyzer::isUnevaluated(const Expr *Exp) {
30681ad6265SDimitry Andric return isUnevaluated(Exp, Stm, Context);
30781ad6265SDimitry Andric }
30881ad6265SDimitry Andric
3090b57cec5SDimitry Andric const Stmt *
findExprMutation(ArrayRef<BoundNodes> Matches)310*0fca6ea1SDimitry Andric ExprMutationAnalyzer::Analyzer::findExprMutation(ArrayRef<BoundNodes> Matches) {
311*0fca6ea1SDimitry Andric return tryEachMatch<Expr>(Matches, this,
312*0fca6ea1SDimitry Andric &ExprMutationAnalyzer::Analyzer::findMutation);
3130b57cec5SDimitry Andric }
3140b57cec5SDimitry Andric
3150b57cec5SDimitry Andric const Stmt *
findDeclMutation(ArrayRef<BoundNodes> Matches)316*0fca6ea1SDimitry Andric ExprMutationAnalyzer::Analyzer::findDeclMutation(ArrayRef<BoundNodes> Matches) {
3170b57cec5SDimitry Andric return tryEachMatch<Decl>(Matches, this,
318*0fca6ea1SDimitry Andric &ExprMutationAnalyzer::Analyzer::findMutation);
3190b57cec5SDimitry Andric }
3200b57cec5SDimitry Andric
findExprPointeeMutation(ArrayRef<ast_matchers::BoundNodes> Matches)321*0fca6ea1SDimitry Andric const Stmt *ExprMutationAnalyzer::Analyzer::findExprPointeeMutation(
322*0fca6ea1SDimitry Andric ArrayRef<ast_matchers::BoundNodes> Matches) {
323*0fca6ea1SDimitry Andric return tryEachMatch<Expr>(
324*0fca6ea1SDimitry Andric Matches, this, &ExprMutationAnalyzer::Analyzer::findPointeeMutation);
325*0fca6ea1SDimitry Andric }
326*0fca6ea1SDimitry Andric
findDeclPointeeMutation(ArrayRef<ast_matchers::BoundNodes> Matches)327*0fca6ea1SDimitry Andric const Stmt *ExprMutationAnalyzer::Analyzer::findDeclPointeeMutation(
328*0fca6ea1SDimitry Andric ArrayRef<ast_matchers::BoundNodes> Matches) {
329*0fca6ea1SDimitry Andric return tryEachMatch<Decl>(
330*0fca6ea1SDimitry Andric Matches, this, &ExprMutationAnalyzer::Analyzer::findPointeeMutation);
331*0fca6ea1SDimitry Andric }
332*0fca6ea1SDimitry Andric
333*0fca6ea1SDimitry Andric const Stmt *
findDirectMutation(const Expr * Exp)334*0fca6ea1SDimitry Andric ExprMutationAnalyzer::Analyzer::findDirectMutation(const Expr *Exp) {
3350b57cec5SDimitry Andric // LHS of any assignment operators.
336297eecfbSDimitry Andric const auto AsAssignmentLhs =
337297eecfbSDimitry Andric binaryOperator(isAssignmentOperator(), hasLHS(canResolveToExpr(Exp)));
3380b57cec5SDimitry Andric
3390b57cec5SDimitry Andric // Operand of increment/decrement operators.
3400b57cec5SDimitry Andric const auto AsIncDecOperand =
3410b57cec5SDimitry Andric unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")),
342297eecfbSDimitry Andric hasUnaryOperand(canResolveToExpr(Exp)));
3430b57cec5SDimitry Andric
3440b57cec5SDimitry Andric // Invoking non-const member function.
3450b57cec5SDimitry Andric // A member function is assumed to be non-const when it is unresolved.
3460b57cec5SDimitry Andric const auto NonConstMethod = cxxMethodDecl(unless(isConst()));
347e8d8bef9SDimitry Andric
348e8d8bef9SDimitry Andric const auto AsNonConstThis = expr(anyOf(
349297eecfbSDimitry Andric cxxMemberCallExpr(on(canResolveToExpr(Exp)), unless(isConstCallee())),
3500b57cec5SDimitry Andric cxxOperatorCallExpr(callee(NonConstMethod),
351297eecfbSDimitry Andric hasArgument(0, canResolveToExpr(Exp))),
352e8d8bef9SDimitry Andric // In case of a templated type, calling overloaded operators is not
353e8d8bef9SDimitry Andric // resolved and modelled as `binaryOperator` on a dependent type.
354e8d8bef9SDimitry Andric // Such instances are considered a modification, because they can modify
355e8d8bef9SDimitry Andric // in different instantiations of the template.
356297eecfbSDimitry Andric binaryOperator(isTypeDependent(),
357297eecfbSDimitry Andric hasEitherOperand(ignoringImpCasts(canResolveToExpr(Exp)))),
3587a6dacacSDimitry Andric // A fold expression may contain `Exp` as it's initializer.
3597a6dacacSDimitry Andric // We don't know if the operator modifies `Exp` because the
3607a6dacacSDimitry Andric // operator is type dependent due to the parameter pack.
3617a6dacacSDimitry Andric cxxFoldExpr(hasFoldInit(ignoringImpCasts(canResolveToExpr(Exp)))),
362e8d8bef9SDimitry Andric // Within class templates and member functions the member expression might
363e8d8bef9SDimitry Andric // not be resolved. In that case, the `callExpr` is considered to be a
364e8d8bef9SDimitry Andric // modification.
365297eecfbSDimitry Andric callExpr(callee(expr(anyOf(
366297eecfbSDimitry Andric unresolvedMemberExpr(hasObjectExpression(canResolveToExpr(Exp))),
367297eecfbSDimitry Andric cxxDependentScopeMemberExpr(
368297eecfbSDimitry Andric hasObjectExpression(canResolveToExpr(Exp))))))),
369e8d8bef9SDimitry Andric // Match on a call to a known method, but the call itself is type
370e8d8bef9SDimitry Andric // dependent (e.g. `vector<T> v; v.push(T{});` in a templated function).
371297eecfbSDimitry Andric callExpr(allOf(
372297eecfbSDimitry Andric isTypeDependent(),
373e8d8bef9SDimitry Andric callee(memberExpr(hasDeclaration(NonConstMethod),
374297eecfbSDimitry Andric hasObjectExpression(canResolveToExpr(Exp))))))));
3750b57cec5SDimitry Andric
3760b57cec5SDimitry Andric // Taking address of 'Exp'.
3770b57cec5SDimitry Andric // We're assuming 'Exp' is mutated as soon as its address is taken, though in
3780b57cec5SDimitry Andric // theory we can follow the pointer and see whether it escaped `Stm` or is
3790b57cec5SDimitry Andric // dereferenced and then mutated. This is left for future improvements.
3800b57cec5SDimitry Andric const auto AsAmpersandOperand =
3810b57cec5SDimitry Andric unaryOperator(hasOperatorName("&"),
3820b57cec5SDimitry Andric // A NoOp implicit cast is adding const.
3830b57cec5SDimitry Andric unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))),
384297eecfbSDimitry Andric hasUnaryOperand(canResolveToExpr(Exp)));
385297eecfbSDimitry Andric const auto AsPointerFromArrayDecay = castExpr(
386297eecfbSDimitry Andric hasCastKind(CK_ArrayToPointerDecay),
387297eecfbSDimitry Andric unless(hasParent(arraySubscriptExpr())), has(canResolveToExpr(Exp)));
3880b57cec5SDimitry Andric // Treat calling `operator->()` of move-only classes as taking address.
3890b57cec5SDimitry Andric // These are typically smart pointers with unique ownership so we treat
3900b57cec5SDimitry Andric // mutation of pointee as mutation of the smart pointer itself.
391e8d8bef9SDimitry Andric const auto AsOperatorArrowThis = cxxOperatorCallExpr(
392e8d8bef9SDimitry Andric hasOverloadedOperatorName("->"),
393e8d8bef9SDimitry Andric callee(
394e8d8bef9SDimitry Andric cxxMethodDecl(ofClass(isMoveOnly()), returns(nonConstPointerType()))),
395297eecfbSDimitry Andric argumentCountIs(1), hasArgument(0, canResolveToExpr(Exp)));
3960b57cec5SDimitry Andric
3970b57cec5SDimitry Andric // Used as non-const-ref argument when calling a function.
3980b57cec5SDimitry Andric // An argument is assumed to be non-const-ref when the function is unresolved.
3990b57cec5SDimitry Andric // Instantiated template functions are not handled here but in
4000b57cec5SDimitry Andric // findFunctionArgMutation which has additional smarts for handling forwarding
4010b57cec5SDimitry Andric // references.
402e8d8bef9SDimitry Andric const auto NonConstRefParam = forEachArgumentWithParamType(
403297eecfbSDimitry Andric anyOf(canResolveToExpr(Exp),
404297eecfbSDimitry Andric memberExpr(hasObjectExpression(canResolveToExpr(Exp)))),
405e8d8bef9SDimitry Andric nonConstReferenceType());
4060b57cec5SDimitry Andric const auto NotInstantiated = unless(hasDeclaration(isInstantiated()));
407e8d8bef9SDimitry Andric
408*0fca6ea1SDimitry Andric const auto AsNonConstRefArg =
409*0fca6ea1SDimitry Andric anyOf(callExpr(NonConstRefParam, NotInstantiated),
4100b57cec5SDimitry Andric cxxConstructExpr(NonConstRefParam, NotInstantiated),
411*0fca6ea1SDimitry Andric // If the call is type-dependent, we can't properly process any
412*0fca6ea1SDimitry Andric // argument because required type conversions and implicit casts
413*0fca6ea1SDimitry Andric // will be inserted only after specialization.
414*0fca6ea1SDimitry Andric callExpr(isTypeDependent(), hasAnyArgument(canResolveToExpr(Exp))),
415297eecfbSDimitry Andric cxxUnresolvedConstructExpr(hasAnyArgument(canResolveToExpr(Exp))),
416e8d8bef9SDimitry Andric // Previous False Positive in the following Code:
417e8d8bef9SDimitry Andric // `template <typename T> void f() { int i = 42; new Type<T>(i); }`
418e8d8bef9SDimitry Andric // Where the constructor of `Type` takes its argument as reference.
419e8d8bef9SDimitry Andric // The AST does not resolve in a `cxxConstructExpr` because it is
420e8d8bef9SDimitry Andric // type-dependent.
421297eecfbSDimitry Andric parenListExpr(hasDescendant(expr(canResolveToExpr(Exp)))),
422e8d8bef9SDimitry Andric // If the initializer is for a reference type, there is no cast for
423e8d8bef9SDimitry Andric // the variable. Values are cast to RValue first.
424297eecfbSDimitry Andric initListExpr(hasAnyInit(expr(canResolveToExpr(Exp)))));
4250b57cec5SDimitry Andric
4260b57cec5SDimitry Andric // Captured by a lambda by reference.
4270b57cec5SDimitry Andric // If we're initializing a capture with 'Exp' directly then we're initializing
4280b57cec5SDimitry Andric // a reference capture.
4290b57cec5SDimitry Andric // For value captures there will be an ImplicitCastExpr <LValueToRValue>.
4300b57cec5SDimitry Andric const auto AsLambdaRefCaptureInit = lambdaExpr(hasCaptureInit(Exp));
4310b57cec5SDimitry Andric
4320b57cec5SDimitry Andric // Returned as non-const-ref.
4330b57cec5SDimitry Andric // If we're returning 'Exp' directly then it's returned as non-const-ref.
4340b57cec5SDimitry Andric // For returning by value there will be an ImplicitCastExpr <LValueToRValue>.
4350b57cec5SDimitry Andric // For returning by const-ref there will be an ImplicitCastExpr <NoOp> (for
4360b57cec5SDimitry Andric // adding const.)
437e8d8bef9SDimitry Andric const auto AsNonConstRefReturn =
438297eecfbSDimitry Andric returnStmt(hasReturnValue(canResolveToExpr(Exp)));
439e8d8bef9SDimitry Andric
440*0fca6ea1SDimitry Andric // It is used as a non-const-reference for initializing a range-for loop.
441297eecfbSDimitry Andric const auto AsNonConstRefRangeInit = cxxForRangeStmt(hasRangeInit(declRefExpr(
442297eecfbSDimitry Andric allOf(canResolveToExpr(Exp), hasType(nonConstReferenceType())))));
4430b57cec5SDimitry Andric
4445ffd83dbSDimitry Andric const auto Matches = match(
445297eecfbSDimitry Andric traverse(
446297eecfbSDimitry Andric TK_AsIs,
447297eecfbSDimitry Andric findFirst(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand, AsNonConstThis,
448297eecfbSDimitry Andric AsAmpersandOperand, AsPointerFromArrayDecay,
449297eecfbSDimitry Andric AsOperatorArrowThis, AsNonConstRefArg,
450297eecfbSDimitry Andric AsLambdaRefCaptureInit, AsNonConstRefReturn,
451297eecfbSDimitry Andric AsNonConstRefRangeInit))
4525ffd83dbSDimitry Andric .bind("stmt"))),
4530b57cec5SDimitry Andric Stm, Context);
4540b57cec5SDimitry Andric return selectFirst<Stmt>("stmt", Matches);
4550b57cec5SDimitry Andric }
4560b57cec5SDimitry Andric
457*0fca6ea1SDimitry Andric const Stmt *
findMemberMutation(const Expr * Exp)458*0fca6ea1SDimitry Andric ExprMutationAnalyzer::Analyzer::findMemberMutation(const Expr *Exp) {
4590b57cec5SDimitry Andric // Check whether any member of 'Exp' is mutated.
460297eecfbSDimitry Andric const auto MemberExprs = match(
461297eecfbSDimitry Andric findAll(expr(anyOf(memberExpr(hasObjectExpression(canResolveToExpr(Exp))),
462297eecfbSDimitry Andric cxxDependentScopeMemberExpr(
463297eecfbSDimitry Andric hasObjectExpression(canResolveToExpr(Exp))),
4645f757f3fSDimitry Andric binaryOperator(hasOperatorName(".*"),
4655f757f3fSDimitry Andric hasLHS(equalsNode(Exp)))))
4660b57cec5SDimitry Andric .bind(NodeID<Expr>::value)),
4670b57cec5SDimitry Andric Stm, Context);
4680b57cec5SDimitry Andric return findExprMutation(MemberExprs);
4690b57cec5SDimitry Andric }
4700b57cec5SDimitry Andric
471*0fca6ea1SDimitry Andric const Stmt *
findArrayElementMutation(const Expr * Exp)472*0fca6ea1SDimitry Andric ExprMutationAnalyzer::Analyzer::findArrayElementMutation(const Expr *Exp) {
4730b57cec5SDimitry Andric // Check whether any element of an array is mutated.
474297eecfbSDimitry Andric const auto SubscriptExprs = match(
475297eecfbSDimitry Andric findAll(arraySubscriptExpr(
476297eecfbSDimitry Andric anyOf(hasBase(canResolveToExpr(Exp)),
477297eecfbSDimitry Andric hasBase(implicitCastExpr(allOf(
478297eecfbSDimitry Andric hasCastKind(CK_ArrayToPointerDecay),
479297eecfbSDimitry Andric hasSourceExpression(canResolveToExpr(Exp)))))))
4800b57cec5SDimitry Andric .bind(NodeID<Expr>::value)),
4810b57cec5SDimitry Andric Stm, Context);
4820b57cec5SDimitry Andric return findExprMutation(SubscriptExprs);
4830b57cec5SDimitry Andric }
4840b57cec5SDimitry Andric
findCastMutation(const Expr * Exp)485*0fca6ea1SDimitry Andric const Stmt *ExprMutationAnalyzer::Analyzer::findCastMutation(const Expr *Exp) {
486e8d8bef9SDimitry Andric // If the 'Exp' is explicitly casted to a non-const reference type the
487e8d8bef9SDimitry Andric // 'Exp' is considered to be modified.
488297eecfbSDimitry Andric const auto ExplicitCast =
489297eecfbSDimitry Andric match(findFirst(stmt(castExpr(hasSourceExpression(canResolveToExpr(Exp)),
490297eecfbSDimitry Andric explicitCastExpr(hasDestinationType(
491297eecfbSDimitry Andric nonConstReferenceType()))))
492e8d8bef9SDimitry Andric .bind("stmt")),
493e8d8bef9SDimitry Andric Stm, Context);
494e8d8bef9SDimitry Andric
495e8d8bef9SDimitry Andric if (const auto *CastStmt = selectFirst<Stmt>("stmt", ExplicitCast))
496e8d8bef9SDimitry Andric return CastStmt;
497e8d8bef9SDimitry Andric
4980b57cec5SDimitry Andric // If 'Exp' is casted to any non-const reference type, check the castExpr.
499e8d8bef9SDimitry Andric const auto Casts = match(
500297eecfbSDimitry Andric findAll(expr(castExpr(hasSourceExpression(canResolveToExpr(Exp)),
501297eecfbSDimitry Andric anyOf(explicitCastExpr(hasDestinationType(
502297eecfbSDimitry Andric nonConstReferenceType())),
5030b57cec5SDimitry Andric implicitCastExpr(hasImplicitDestinationType(
504e8d8bef9SDimitry Andric nonConstReferenceType())))))
5050b57cec5SDimitry Andric .bind(NodeID<Expr>::value)),
5060b57cec5SDimitry Andric Stm, Context);
507e8d8bef9SDimitry Andric
5080b57cec5SDimitry Andric if (const Stmt *S = findExprMutation(Casts))
5090b57cec5SDimitry Andric return S;
5100b57cec5SDimitry Andric // Treat std::{move,forward} as cast.
5110b57cec5SDimitry Andric const auto Calls =
5120b57cec5SDimitry Andric match(findAll(callExpr(callee(namedDecl(
5130b57cec5SDimitry Andric hasAnyName("::std::move", "::std::forward"))),
514297eecfbSDimitry Andric hasArgument(0, canResolveToExpr(Exp)))
5150b57cec5SDimitry Andric .bind("expr")),
5160b57cec5SDimitry Andric Stm, Context);
5170b57cec5SDimitry Andric return findExprMutation(Calls);
5180b57cec5SDimitry Andric }
5190b57cec5SDimitry Andric
520*0fca6ea1SDimitry Andric const Stmt *
findRangeLoopMutation(const Expr * Exp)521*0fca6ea1SDimitry Andric ExprMutationAnalyzer::Analyzer::findRangeLoopMutation(const Expr *Exp) {
522e8d8bef9SDimitry Andric // Keep the ordering for the specific initialization matches to happen first,
523e8d8bef9SDimitry Andric // because it is cheaper to match all potential modifications of the loop
524e8d8bef9SDimitry Andric // variable.
525e8d8bef9SDimitry Andric
526e8d8bef9SDimitry Andric // The range variable is a reference to a builtin array. In that case the
527e8d8bef9SDimitry Andric // array is considered modified if the loop-variable is a non-const reference.
528e8d8bef9SDimitry Andric const auto DeclStmtToNonRefToArray = declStmt(hasSingleDecl(varDecl(hasType(
529e8d8bef9SDimitry Andric hasUnqualifiedDesugaredType(referenceType(pointee(arrayType())))))));
530297eecfbSDimitry Andric const auto RefToArrayRefToElements = match(
531297eecfbSDimitry Andric findFirst(stmt(cxxForRangeStmt(
532972a253aSDimitry Andric hasLoopVariable(
533972a253aSDimitry Andric varDecl(anyOf(hasType(nonConstReferenceType()),
534972a253aSDimitry Andric hasType(nonConstPointerType())))
535e8d8bef9SDimitry Andric .bind(NodeID<Decl>::value)),
536e8d8bef9SDimitry Andric hasRangeStmt(DeclStmtToNonRefToArray),
537297eecfbSDimitry Andric hasRangeInit(canResolveToExpr(Exp))))
538e8d8bef9SDimitry Andric .bind("stmt")),
539e8d8bef9SDimitry Andric Stm, Context);
540e8d8bef9SDimitry Andric
541e8d8bef9SDimitry Andric if (const auto *BadRangeInitFromArray =
542e8d8bef9SDimitry Andric selectFirst<Stmt>("stmt", RefToArrayRefToElements))
543e8d8bef9SDimitry Andric return BadRangeInitFromArray;
544e8d8bef9SDimitry Andric
545e8d8bef9SDimitry Andric // Small helper to match special cases in range-for loops.
546e8d8bef9SDimitry Andric //
547e8d8bef9SDimitry Andric // It is possible that containers do not provide a const-overload for their
548e8d8bef9SDimitry Andric // iterator accessors. If this is the case, the variable is used non-const
549e8d8bef9SDimitry Andric // no matter what happens in the loop. This requires special detection as it
550e8d8bef9SDimitry Andric // is then faster to find all mutations of the loop variable.
551e8d8bef9SDimitry Andric // It aims at a different modification as well.
552e8d8bef9SDimitry Andric const auto HasAnyNonConstIterator =
553e8d8bef9SDimitry Andric anyOf(allOf(hasMethod(allOf(hasName("begin"), unless(isConst()))),
554e8d8bef9SDimitry Andric unless(hasMethod(allOf(hasName("begin"), isConst())))),
555e8d8bef9SDimitry Andric allOf(hasMethod(allOf(hasName("end"), unless(isConst()))),
556e8d8bef9SDimitry Andric unless(hasMethod(allOf(hasName("end"), isConst())))));
557e8d8bef9SDimitry Andric
558e8d8bef9SDimitry Andric const auto DeclStmtToNonConstIteratorContainer = declStmt(
559e8d8bef9SDimitry Andric hasSingleDecl(varDecl(hasType(hasUnqualifiedDesugaredType(referenceType(
560e8d8bef9SDimitry Andric pointee(hasDeclaration(cxxRecordDecl(HasAnyNonConstIterator)))))))));
561e8d8bef9SDimitry Andric
562297eecfbSDimitry Andric const auto RefToContainerBadIterators = match(
563297eecfbSDimitry Andric findFirst(stmt(cxxForRangeStmt(allOf(
564e8d8bef9SDimitry Andric hasRangeStmt(DeclStmtToNonConstIteratorContainer),
565297eecfbSDimitry Andric hasRangeInit(canResolveToExpr(Exp)))))
566e8d8bef9SDimitry Andric .bind("stmt")),
567e8d8bef9SDimitry Andric Stm, Context);
568e8d8bef9SDimitry Andric
569e8d8bef9SDimitry Andric if (const auto *BadIteratorsContainer =
570e8d8bef9SDimitry Andric selectFirst<Stmt>("stmt", RefToContainerBadIterators))
571e8d8bef9SDimitry Andric return BadIteratorsContainer;
572e8d8bef9SDimitry Andric
5730b57cec5SDimitry Andric // If range for looping over 'Exp' with a non-const reference loop variable,
5740b57cec5SDimitry Andric // check all declRefExpr of the loop variable.
5750b57cec5SDimitry Andric const auto LoopVars =
5760b57cec5SDimitry Andric match(findAll(cxxForRangeStmt(
5770b57cec5SDimitry Andric hasLoopVariable(varDecl(hasType(nonConstReferenceType()))
5780b57cec5SDimitry Andric .bind(NodeID<Decl>::value)),
579297eecfbSDimitry Andric hasRangeInit(canResolveToExpr(Exp)))),
5800b57cec5SDimitry Andric Stm, Context);
5810b57cec5SDimitry Andric return findDeclMutation(LoopVars);
5820b57cec5SDimitry Andric }
5830b57cec5SDimitry Andric
584*0fca6ea1SDimitry Andric const Stmt *
findReferenceMutation(const Expr * Exp)585*0fca6ea1SDimitry Andric ExprMutationAnalyzer::Analyzer::findReferenceMutation(const Expr *Exp) {
5860b57cec5SDimitry Andric // Follow non-const reference returned by `operator*()` of move-only classes.
5870b57cec5SDimitry Andric // These are typically smart pointers with unique ownership so we treat
5880b57cec5SDimitry Andric // mutation of pointee as mutation of the smart pointer itself.
589297eecfbSDimitry Andric const auto Ref = match(
590297eecfbSDimitry Andric findAll(cxxOperatorCallExpr(
5910b57cec5SDimitry Andric hasOverloadedOperatorName("*"),
5920b57cec5SDimitry Andric callee(cxxMethodDecl(ofClass(isMoveOnly()),
5930b57cec5SDimitry Andric returns(nonConstReferenceType()))),
594297eecfbSDimitry Andric argumentCountIs(1), hasArgument(0, canResolveToExpr(Exp)))
5950b57cec5SDimitry Andric .bind(NodeID<Expr>::value)),
5960b57cec5SDimitry Andric Stm, Context);
5970b57cec5SDimitry Andric if (const Stmt *S = findExprMutation(Ref))
5980b57cec5SDimitry Andric return S;
5990b57cec5SDimitry Andric
6000b57cec5SDimitry Andric // If 'Exp' is bound to a non-const reference, check all declRefExpr to that.
6010b57cec5SDimitry Andric const auto Refs = match(
6020b57cec5SDimitry Andric stmt(forEachDescendant(
603297eecfbSDimitry Andric varDecl(hasType(nonConstReferenceType()),
604297eecfbSDimitry Andric hasInitializer(anyOf(
605297eecfbSDimitry Andric canResolveToExpr(Exp),
606297eecfbSDimitry Andric memberExpr(hasObjectExpression(canResolveToExpr(Exp))))),
6070b57cec5SDimitry Andric hasParent(declStmt().bind("stmt")),
608e8d8bef9SDimitry Andric // Don't follow the reference in range statement, we've
609e8d8bef9SDimitry Andric // handled that separately.
610297eecfbSDimitry Andric unless(hasParent(declStmt(hasParent(cxxForRangeStmt(
611297eecfbSDimitry Andric hasRangeStmt(equalsBoundNode("stmt"))))))))
6120b57cec5SDimitry Andric .bind(NodeID<Decl>::value))),
6130b57cec5SDimitry Andric Stm, Context);
6140b57cec5SDimitry Andric return findDeclMutation(Refs);
6150b57cec5SDimitry Andric }
6160b57cec5SDimitry Andric
617*0fca6ea1SDimitry Andric const Stmt *
findFunctionArgMutation(const Expr * Exp)618*0fca6ea1SDimitry Andric ExprMutationAnalyzer::Analyzer::findFunctionArgMutation(const Expr *Exp) {
6190b57cec5SDimitry Andric const auto NonConstRefParam = forEachArgumentWithParam(
620297eecfbSDimitry Andric canResolveToExpr(Exp),
6210b57cec5SDimitry Andric parmVarDecl(hasType(nonConstReferenceType())).bind("parm"));
6220b57cec5SDimitry Andric const auto IsInstantiated = hasDeclaration(isInstantiated());
6230b57cec5SDimitry Andric const auto FuncDecl = hasDeclaration(functionDecl().bind("func"));
6240b57cec5SDimitry Andric const auto Matches = match(
6255ffd83dbSDimitry Andric traverse(
626e8d8bef9SDimitry Andric TK_AsIs,
6275ffd83dbSDimitry Andric findAll(
6285ffd83dbSDimitry Andric expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl,
6290b57cec5SDimitry Andric unless(callee(namedDecl(hasAnyName(
6300b57cec5SDimitry Andric "::std::move", "::std::forward"))))),
6310b57cec5SDimitry Andric cxxConstructExpr(NonConstRefParam, IsInstantiated,
6320b57cec5SDimitry Andric FuncDecl)))
6335ffd83dbSDimitry Andric .bind(NodeID<Expr>::value))),
6340b57cec5SDimitry Andric Stm, Context);
6350b57cec5SDimitry Andric for (const auto &Nodes : Matches) {
6360b57cec5SDimitry Andric const auto *Exp = Nodes.getNodeAs<Expr>(NodeID<Expr>::value);
6370b57cec5SDimitry Andric const auto *Func = Nodes.getNodeAs<FunctionDecl>("func");
6380b57cec5SDimitry Andric if (!Func->getBody() || !Func->getPrimaryTemplate())
6390b57cec5SDimitry Andric return Exp;
6400b57cec5SDimitry Andric
6410b57cec5SDimitry Andric const auto *Parm = Nodes.getNodeAs<ParmVarDecl>("parm");
6420b57cec5SDimitry Andric const ArrayRef<ParmVarDecl *> AllParams =
6430b57cec5SDimitry Andric Func->getPrimaryTemplate()->getTemplatedDecl()->parameters();
6440b57cec5SDimitry Andric QualType ParmType =
6450b57cec5SDimitry Andric AllParams[std::min<size_t>(Parm->getFunctionScopeIndex(),
6460b57cec5SDimitry Andric AllParams.size() - 1)]
6470b57cec5SDimitry Andric ->getType();
6480b57cec5SDimitry Andric if (const auto *T = ParmType->getAs<PackExpansionType>())
6490b57cec5SDimitry Andric ParmType = T->getPattern();
6500b57cec5SDimitry Andric
6510b57cec5SDimitry Andric // If param type is forwarding reference, follow into the function
6520b57cec5SDimitry Andric // definition and see whether the param is mutated inside.
6530b57cec5SDimitry Andric if (const auto *RefType = ParmType->getAs<RValueReferenceType>()) {
6540b57cec5SDimitry Andric if (!RefType->getPointeeType().getQualifiers() &&
6550b57cec5SDimitry Andric RefType->getPointeeType()->getAs<TemplateTypeParmType>()) {
656*0fca6ea1SDimitry Andric FunctionParmMutationAnalyzer *Analyzer =
657*0fca6ea1SDimitry Andric FunctionParmMutationAnalyzer::getFunctionParmMutationAnalyzer(
658*0fca6ea1SDimitry Andric *Func, Context, Memorized);
6590b57cec5SDimitry Andric if (Analyzer->findMutation(Parm))
6600b57cec5SDimitry Andric return Exp;
6610b57cec5SDimitry Andric continue;
6620b57cec5SDimitry Andric }
6630b57cec5SDimitry Andric }
6640b57cec5SDimitry Andric // Not forwarding reference.
6650b57cec5SDimitry Andric return Exp;
6660b57cec5SDimitry Andric }
6670b57cec5SDimitry Andric return nullptr;
6680b57cec5SDimitry Andric }
6690b57cec5SDimitry Andric
FunctionParmMutationAnalyzer(const FunctionDecl & Func,ASTContext & Context,ExprMutationAnalyzer::Memoized & Memorized)6700b57cec5SDimitry Andric FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer(
671*0fca6ea1SDimitry Andric const FunctionDecl &Func, ASTContext &Context,
672*0fca6ea1SDimitry Andric ExprMutationAnalyzer::Memoized &Memorized)
673*0fca6ea1SDimitry Andric : BodyAnalyzer(*Func.getBody(), Context, Memorized) {
6740b57cec5SDimitry Andric if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(&Func)) {
6750b57cec5SDimitry Andric // CXXCtorInitializer might also mutate Param but they're not part of
6760b57cec5SDimitry Andric // function body, check them eagerly here since they're typically trivial.
6770b57cec5SDimitry Andric for (const CXXCtorInitializer *Init : Ctor->inits()) {
678*0fca6ea1SDimitry Andric ExprMutationAnalyzer::Analyzer InitAnalyzer(*Init->getInit(), Context,
679*0fca6ea1SDimitry Andric Memorized);
6800b57cec5SDimitry Andric for (const ParmVarDecl *Parm : Ctor->parameters()) {
68106c3fb27SDimitry Andric if (Results.contains(Parm))
6820b57cec5SDimitry Andric continue;
6830b57cec5SDimitry Andric if (const Stmt *S = InitAnalyzer.findMutation(Parm))
6840b57cec5SDimitry Andric Results[Parm] = S;
6850b57cec5SDimitry Andric }
6860b57cec5SDimitry Andric }
6870b57cec5SDimitry Andric }
6880b57cec5SDimitry Andric }
6890b57cec5SDimitry Andric
6900b57cec5SDimitry Andric const Stmt *
findMutation(const ParmVarDecl * Parm)6910b57cec5SDimitry Andric FunctionParmMutationAnalyzer::findMutation(const ParmVarDecl *Parm) {
6920b57cec5SDimitry Andric const auto Memoized = Results.find(Parm);
6930b57cec5SDimitry Andric if (Memoized != Results.end())
6940b57cec5SDimitry Andric return Memoized->second;
695*0fca6ea1SDimitry Andric // To handle call A -> call B -> call A. Assume parameters of A is not mutated
696*0fca6ea1SDimitry Andric // before analyzing parameters of A. Then when analyzing the second "call A",
697*0fca6ea1SDimitry Andric // FunctionParmMutationAnalyzer can use this memoized value to avoid infinite
698*0fca6ea1SDimitry Andric // recursion.
699*0fca6ea1SDimitry Andric Results[Parm] = nullptr;
7000b57cec5SDimitry Andric if (const Stmt *S = BodyAnalyzer.findMutation(Parm))
7010b57cec5SDimitry Andric return Results[Parm] = S;
702*0fca6ea1SDimitry Andric return Results[Parm];
7030b57cec5SDimitry Andric }
7040b57cec5SDimitry Andric
7050b57cec5SDimitry Andric } // namespace clang
706