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