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 180b57cec5SDimitry Andric namespace { 190b57cec5SDimitry Andric 200b57cec5SDimitry Andric AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) { 210b57cec5SDimitry Andric return llvm::is_contained(Node.capture_inits(), E); 220b57cec5SDimitry Andric } 230b57cec5SDimitry Andric 240b57cec5SDimitry Andric AST_MATCHER_P(CXXForRangeStmt, hasRangeStmt, 250b57cec5SDimitry Andric ast_matchers::internal::Matcher<DeclStmt>, InnerMatcher) { 260b57cec5SDimitry Andric const DeclStmt *const Range = Node.getRangeStmt(); 270b57cec5SDimitry Andric return InnerMatcher.matches(*Range, Finder, Builder); 280b57cec5SDimitry Andric } 290b57cec5SDimitry Andric 30e8d8bef9SDimitry Andric AST_MATCHER_P(Expr, maybeEvalCommaExpr, ast_matchers::internal::Matcher<Expr>, 31e8d8bef9SDimitry Andric InnerMatcher) { 320b57cec5SDimitry Andric const Expr *Result = &Node; 330b57cec5SDimitry Andric while (const auto *BOComma = 340b57cec5SDimitry Andric dyn_cast_or_null<BinaryOperator>(Result->IgnoreParens())) { 350b57cec5SDimitry Andric if (!BOComma->isCommaOp()) 360b57cec5SDimitry Andric break; 370b57cec5SDimitry Andric Result = BOComma->getRHS(); 380b57cec5SDimitry Andric } 390b57cec5SDimitry Andric return InnerMatcher.matches(*Result, Finder, Builder); 400b57cec5SDimitry Andric } 410b57cec5SDimitry Andric 42*81ad6265SDimitry Andric AST_MATCHER_P(Stmt, canResolveToExpr, ast_matchers::internal::Matcher<Stmt>, 43e8d8bef9SDimitry Andric InnerMatcher) { 44*81ad6265SDimitry Andric auto *Exp = dyn_cast<Expr>(&Node); 45*81ad6265SDimitry Andric if (!Exp) { 46*81ad6265SDimitry Andric return stmt().matches(Node, Finder, Builder); 47*81ad6265SDimitry Andric } 48*81ad6265SDimitry Andric 49e8d8bef9SDimitry Andric auto DerivedToBase = [](const ast_matchers::internal::Matcher<Expr> &Inner) { 50e8d8bef9SDimitry Andric return implicitCastExpr(anyOf(hasCastKind(CK_DerivedToBase), 51e8d8bef9SDimitry Andric hasCastKind(CK_UncheckedDerivedToBase)), 52e8d8bef9SDimitry Andric hasSourceExpression(Inner)); 53e8d8bef9SDimitry Andric }; 54e8d8bef9SDimitry Andric auto IgnoreDerivedToBase = 55e8d8bef9SDimitry Andric [&DerivedToBase](const ast_matchers::internal::Matcher<Expr> &Inner) { 56e8d8bef9SDimitry Andric return ignoringParens(expr(anyOf(Inner, DerivedToBase(Inner)))); 57e8d8bef9SDimitry Andric }; 58e8d8bef9SDimitry Andric 59e8d8bef9SDimitry Andric // The 'ConditionalOperator' matches on `<anything> ? <expr> : <expr>`. 60e8d8bef9SDimitry Andric // This matching must be recursive because `<expr>` can be anything resolving 61e8d8bef9SDimitry Andric // to the `InnerMatcher`, for example another conditional operator. 62e8d8bef9SDimitry Andric // The edge-case `BaseClass &b = <cond> ? DerivedVar1 : DerivedVar2;` 63e8d8bef9SDimitry Andric // is handled, too. The implicit cast happens outside of the conditional. 64e8d8bef9SDimitry Andric // This is matched by `IgnoreDerivedToBase(canResolveToExpr(InnerMatcher))` 65e8d8bef9SDimitry Andric // below. 66e8d8bef9SDimitry Andric auto const ConditionalOperator = conditionalOperator(anyOf( 67e8d8bef9SDimitry Andric hasTrueExpression(ignoringParens(canResolveToExpr(InnerMatcher))), 68e8d8bef9SDimitry Andric hasFalseExpression(ignoringParens(canResolveToExpr(InnerMatcher))))); 69e8d8bef9SDimitry Andric auto const ElvisOperator = binaryConditionalOperator(anyOf( 70e8d8bef9SDimitry Andric hasTrueExpression(ignoringParens(canResolveToExpr(InnerMatcher))), 71e8d8bef9SDimitry Andric hasFalseExpression(ignoringParens(canResolveToExpr(InnerMatcher))))); 72e8d8bef9SDimitry Andric 73e8d8bef9SDimitry Andric auto const ComplexMatcher = ignoringParens( 74e8d8bef9SDimitry Andric expr(anyOf(IgnoreDerivedToBase(InnerMatcher), 75e8d8bef9SDimitry Andric maybeEvalCommaExpr(IgnoreDerivedToBase(InnerMatcher)), 76e8d8bef9SDimitry Andric IgnoreDerivedToBase(ConditionalOperator), 77e8d8bef9SDimitry Andric IgnoreDerivedToBase(ElvisOperator)))); 78e8d8bef9SDimitry Andric 79*81ad6265SDimitry Andric return ComplexMatcher.matches(*Exp, Finder, Builder); 80e8d8bef9SDimitry Andric } 81e8d8bef9SDimitry Andric 82e8d8bef9SDimitry Andric // Similar to 'hasAnyArgument', but does not work because 'InitListExpr' does 83e8d8bef9SDimitry Andric // not have the 'arguments()' method. 84e8d8bef9SDimitry Andric AST_MATCHER_P(InitListExpr, hasAnyInit, ast_matchers::internal::Matcher<Expr>, 85e8d8bef9SDimitry Andric InnerMatcher) { 86e8d8bef9SDimitry Andric for (const Expr *Arg : Node.inits()) { 87e8d8bef9SDimitry Andric ast_matchers::internal::BoundNodesTreeBuilder Result(*Builder); 88e8d8bef9SDimitry Andric if (InnerMatcher.matches(*Arg, Finder, &Result)) { 89e8d8bef9SDimitry Andric *Builder = std::move(Result); 90e8d8bef9SDimitry Andric return true; 91e8d8bef9SDimitry Andric } 92e8d8bef9SDimitry Andric } 93e8d8bef9SDimitry Andric return false; 94e8d8bef9SDimitry Andric } 95e8d8bef9SDimitry Andric 960b57cec5SDimitry Andric const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXTypeidExpr> 970b57cec5SDimitry Andric cxxTypeidExpr; 980b57cec5SDimitry Andric 990b57cec5SDimitry Andric AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) { 1000b57cec5SDimitry Andric return Node.isPotentiallyEvaluated(); 1010b57cec5SDimitry Andric } 1020b57cec5SDimitry Andric 1030b57cec5SDimitry Andric AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr, 1040b57cec5SDimitry Andric ast_matchers::internal::Matcher<Expr>, InnerMatcher) { 1050b57cec5SDimitry Andric return InnerMatcher.matches(*Node.getControllingExpr(), Finder, Builder); 1060b57cec5SDimitry Andric } 1070b57cec5SDimitry Andric 1080b57cec5SDimitry Andric const auto nonConstReferenceType = [] { 1090b57cec5SDimitry Andric return hasUnqualifiedDesugaredType( 1100b57cec5SDimitry Andric referenceType(pointee(unless(isConstQualified())))); 1110b57cec5SDimitry Andric }; 1120b57cec5SDimitry Andric 1130b57cec5SDimitry Andric const auto nonConstPointerType = [] { 1140b57cec5SDimitry Andric return hasUnqualifiedDesugaredType( 1150b57cec5SDimitry Andric pointerType(pointee(unless(isConstQualified())))); 1160b57cec5SDimitry Andric }; 1170b57cec5SDimitry Andric 1180b57cec5SDimitry Andric const auto isMoveOnly = [] { 1190b57cec5SDimitry Andric return cxxRecordDecl( 1200b57cec5SDimitry Andric hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted()))), 1210b57cec5SDimitry Andric hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted()))), 1220b57cec5SDimitry Andric unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(), 1230b57cec5SDimitry Andric unless(isDeleted()))), 1240b57cec5SDimitry Andric hasMethod(cxxMethodDecl(isCopyAssignmentOperator(), 1250b57cec5SDimitry Andric unless(isDeleted())))))); 1260b57cec5SDimitry Andric }; 1270b57cec5SDimitry Andric 1280b57cec5SDimitry Andric template <class T> struct NodeID; 1295ffd83dbSDimitry Andric template <> struct NodeID<Expr> { static constexpr StringRef value = "expr"; }; 1305ffd83dbSDimitry Andric template <> struct NodeID<Decl> { static constexpr StringRef value = "decl"; }; 1315ffd83dbSDimitry Andric constexpr StringRef NodeID<Expr>::value; 1325ffd83dbSDimitry Andric constexpr StringRef NodeID<Decl>::value; 1330b57cec5SDimitry Andric 1340b57cec5SDimitry Andric template <class T, class F = const Stmt *(ExprMutationAnalyzer::*)(const T *)> 1350b57cec5SDimitry Andric const Stmt *tryEachMatch(ArrayRef<ast_matchers::BoundNodes> Matches, 1360b57cec5SDimitry Andric ExprMutationAnalyzer *Analyzer, F Finder) { 1370b57cec5SDimitry Andric const StringRef ID = NodeID<T>::value; 1380b57cec5SDimitry Andric for (const auto &Nodes : Matches) { 1390b57cec5SDimitry Andric if (const Stmt *S = (Analyzer->*Finder)(Nodes.getNodeAs<T>(ID))) 1400b57cec5SDimitry Andric return S; 1410b57cec5SDimitry Andric } 1420b57cec5SDimitry Andric return nullptr; 1430b57cec5SDimitry Andric } 1440b57cec5SDimitry Andric 1450b57cec5SDimitry Andric } // namespace 1460b57cec5SDimitry Andric 1470b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findMutation(const Expr *Exp) { 1480b57cec5SDimitry Andric return findMutationMemoized(Exp, 1490b57cec5SDimitry Andric {&ExprMutationAnalyzer::findDirectMutation, 1500b57cec5SDimitry Andric &ExprMutationAnalyzer::findMemberMutation, 1510b57cec5SDimitry Andric &ExprMutationAnalyzer::findArrayElementMutation, 1520b57cec5SDimitry Andric &ExprMutationAnalyzer::findCastMutation, 1530b57cec5SDimitry Andric &ExprMutationAnalyzer::findRangeLoopMutation, 1540b57cec5SDimitry Andric &ExprMutationAnalyzer::findReferenceMutation, 1550b57cec5SDimitry Andric &ExprMutationAnalyzer::findFunctionArgMutation}, 1560b57cec5SDimitry Andric Results); 1570b57cec5SDimitry Andric } 1580b57cec5SDimitry Andric 1590b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findMutation(const Decl *Dec) { 1600b57cec5SDimitry Andric return tryEachDeclRef(Dec, &ExprMutationAnalyzer::findMutation); 1610b57cec5SDimitry Andric } 1620b57cec5SDimitry Andric 1630b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findPointeeMutation(const Expr *Exp) { 1640b57cec5SDimitry Andric return findMutationMemoized(Exp, {/*TODO*/}, PointeeResults); 1650b57cec5SDimitry Andric } 1660b57cec5SDimitry Andric 1670b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findPointeeMutation(const Decl *Dec) { 1680b57cec5SDimitry Andric return tryEachDeclRef(Dec, &ExprMutationAnalyzer::findPointeeMutation); 1690b57cec5SDimitry Andric } 1700b57cec5SDimitry Andric 1710b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findMutationMemoized( 1720b57cec5SDimitry Andric const Expr *Exp, llvm::ArrayRef<MutationFinder> Finders, 1730b57cec5SDimitry Andric ResultMap &MemoizedResults) { 1740b57cec5SDimitry Andric const auto Memoized = MemoizedResults.find(Exp); 1750b57cec5SDimitry Andric if (Memoized != MemoizedResults.end()) 1760b57cec5SDimitry Andric return Memoized->second; 1770b57cec5SDimitry Andric 1780b57cec5SDimitry Andric if (isUnevaluated(Exp)) 1790b57cec5SDimitry Andric return MemoizedResults[Exp] = nullptr; 1800b57cec5SDimitry Andric 1810b57cec5SDimitry Andric for (const auto &Finder : Finders) { 1820b57cec5SDimitry Andric if (const Stmt *S = (this->*Finder)(Exp)) 1830b57cec5SDimitry Andric return MemoizedResults[Exp] = S; 1840b57cec5SDimitry Andric } 1850b57cec5SDimitry Andric 1860b57cec5SDimitry Andric return MemoizedResults[Exp] = nullptr; 1870b57cec5SDimitry Andric } 1880b57cec5SDimitry Andric 1890b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::tryEachDeclRef(const Decl *Dec, 1900b57cec5SDimitry Andric MutationFinder Finder) { 1910b57cec5SDimitry Andric const auto Refs = 1920b57cec5SDimitry Andric match(findAll(declRefExpr(to(equalsNode(Dec))).bind(NodeID<Expr>::value)), 1930b57cec5SDimitry Andric Stm, Context); 1940b57cec5SDimitry Andric for (const auto &RefNodes : Refs) { 1950b57cec5SDimitry Andric const auto *E = RefNodes.getNodeAs<Expr>(NodeID<Expr>::value); 1960b57cec5SDimitry Andric if ((this->*Finder)(E)) 1970b57cec5SDimitry Andric return E; 1980b57cec5SDimitry Andric } 1990b57cec5SDimitry Andric return nullptr; 2000b57cec5SDimitry Andric } 2010b57cec5SDimitry Andric 202*81ad6265SDimitry Andric bool ExprMutationAnalyzer::isUnevaluated(const Stmt *Exp, const Stmt &Stm, 203*81ad6265SDimitry Andric ASTContext &Context) { 204*81ad6265SDimitry Andric return selectFirst<Stmt>( 2050b57cec5SDimitry Andric NodeID<Expr>::value, 2060b57cec5SDimitry Andric match( 2070b57cec5SDimitry Andric findAll( 208*81ad6265SDimitry Andric stmt(canResolveToExpr(equalsNode(Exp)), 2090b57cec5SDimitry Andric anyOf( 2100b57cec5SDimitry Andric // `Exp` is part of the underlying expression of 2110b57cec5SDimitry Andric // decltype/typeof if it has an ancestor of 2120b57cec5SDimitry Andric // typeLoc. 2130b57cec5SDimitry Andric hasAncestor(typeLoc(unless( 2140b57cec5SDimitry Andric hasAncestor(unaryExprOrTypeTraitExpr())))), 2150b57cec5SDimitry Andric hasAncestor(expr(anyOf( 2160b57cec5SDimitry Andric // `UnaryExprOrTypeTraitExpr` is unevaluated 2170b57cec5SDimitry Andric // unless it's sizeof on VLA. 2180b57cec5SDimitry Andric unaryExprOrTypeTraitExpr(unless(sizeOfExpr( 2190b57cec5SDimitry Andric hasArgumentOfType(variableArrayType())))), 2200b57cec5SDimitry Andric // `CXXTypeidExpr` is unevaluated unless it's 2210b57cec5SDimitry Andric // applied to an expression of glvalue of 2220b57cec5SDimitry Andric // polymorphic class type. 2230b57cec5SDimitry Andric cxxTypeidExpr( 2240b57cec5SDimitry Andric unless(isPotentiallyEvaluated())), 2250b57cec5SDimitry Andric // The controlling expression of 2260b57cec5SDimitry Andric // `GenericSelectionExpr` is unevaluated. 2270b57cec5SDimitry Andric genericSelectionExpr(hasControllingExpr( 2280b57cec5SDimitry Andric hasDescendant(equalsNode(Exp)))), 2290b57cec5SDimitry Andric cxxNoexceptExpr()))))) 2300b57cec5SDimitry Andric .bind(NodeID<Expr>::value)), 2310b57cec5SDimitry Andric Stm, Context)) != nullptr; 2320b57cec5SDimitry Andric } 2330b57cec5SDimitry Andric 234*81ad6265SDimitry Andric bool ExprMutationAnalyzer::isUnevaluated(const Expr *Exp) { 235*81ad6265SDimitry Andric return isUnevaluated(Exp, Stm, Context); 236*81ad6265SDimitry Andric } 237*81ad6265SDimitry Andric 2380b57cec5SDimitry Andric const Stmt * 2390b57cec5SDimitry Andric ExprMutationAnalyzer::findExprMutation(ArrayRef<BoundNodes> Matches) { 2400b57cec5SDimitry Andric return tryEachMatch<Expr>(Matches, this, &ExprMutationAnalyzer::findMutation); 2410b57cec5SDimitry Andric } 2420b57cec5SDimitry Andric 2430b57cec5SDimitry Andric const Stmt * 2440b57cec5SDimitry Andric ExprMutationAnalyzer::findDeclMutation(ArrayRef<BoundNodes> Matches) { 2450b57cec5SDimitry Andric return tryEachMatch<Decl>(Matches, this, &ExprMutationAnalyzer::findMutation); 2460b57cec5SDimitry Andric } 2470b57cec5SDimitry Andric 2480b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findExprPointeeMutation( 2490b57cec5SDimitry Andric ArrayRef<ast_matchers::BoundNodes> Matches) { 2500b57cec5SDimitry Andric return tryEachMatch<Expr>(Matches, this, 2510b57cec5SDimitry Andric &ExprMutationAnalyzer::findPointeeMutation); 2520b57cec5SDimitry Andric } 2530b57cec5SDimitry Andric 2540b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findDeclPointeeMutation( 2550b57cec5SDimitry Andric ArrayRef<ast_matchers::BoundNodes> Matches) { 2560b57cec5SDimitry Andric return tryEachMatch<Decl>(Matches, this, 2570b57cec5SDimitry Andric &ExprMutationAnalyzer::findPointeeMutation); 2580b57cec5SDimitry Andric } 2590b57cec5SDimitry Andric 2600b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) { 2610b57cec5SDimitry Andric // LHS of any assignment operators. 2625ffd83dbSDimitry Andric const auto AsAssignmentLhs = binaryOperator( 263e8d8bef9SDimitry Andric isAssignmentOperator(), hasLHS(canResolveToExpr(equalsNode(Exp)))); 2640b57cec5SDimitry Andric 2650b57cec5SDimitry Andric // Operand of increment/decrement operators. 2660b57cec5SDimitry Andric const auto AsIncDecOperand = 2670b57cec5SDimitry Andric unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")), 268e8d8bef9SDimitry Andric hasUnaryOperand(canResolveToExpr(equalsNode(Exp)))); 2690b57cec5SDimitry Andric 2700b57cec5SDimitry Andric // Invoking non-const member function. 2710b57cec5SDimitry Andric // A member function is assumed to be non-const when it is unresolved. 2720b57cec5SDimitry Andric const auto NonConstMethod = cxxMethodDecl(unless(isConst())); 273e8d8bef9SDimitry Andric 274e8d8bef9SDimitry Andric const auto AsNonConstThis = expr(anyOf( 275e8d8bef9SDimitry Andric cxxMemberCallExpr(callee(NonConstMethod), 276e8d8bef9SDimitry Andric on(canResolveToExpr(equalsNode(Exp)))), 2770b57cec5SDimitry Andric cxxOperatorCallExpr(callee(NonConstMethod), 278e8d8bef9SDimitry Andric hasArgument(0, canResolveToExpr(equalsNode(Exp)))), 279e8d8bef9SDimitry Andric // In case of a templated type, calling overloaded operators is not 280e8d8bef9SDimitry Andric // resolved and modelled as `binaryOperator` on a dependent type. 281e8d8bef9SDimitry Andric // Such instances are considered a modification, because they can modify 282e8d8bef9SDimitry Andric // in different instantiations of the template. 283e8d8bef9SDimitry Andric binaryOperator(hasEitherOperand( 284e8d8bef9SDimitry Andric allOf(ignoringImpCasts(canResolveToExpr(equalsNode(Exp))), 285e8d8bef9SDimitry Andric isTypeDependent()))), 286e8d8bef9SDimitry Andric // Within class templates and member functions the member expression might 287e8d8bef9SDimitry Andric // not be resolved. In that case, the `callExpr` is considered to be a 288e8d8bef9SDimitry Andric // modification. 289e8d8bef9SDimitry Andric callExpr( 290e8d8bef9SDimitry Andric callee(expr(anyOf(unresolvedMemberExpr(hasObjectExpression( 291e8d8bef9SDimitry Andric canResolveToExpr(equalsNode(Exp)))), 292e8d8bef9SDimitry Andric cxxDependentScopeMemberExpr(hasObjectExpression( 293e8d8bef9SDimitry Andric canResolveToExpr(equalsNode(Exp)))))))), 294e8d8bef9SDimitry Andric // Match on a call to a known method, but the call itself is type 295e8d8bef9SDimitry Andric // dependent (e.g. `vector<T> v; v.push(T{});` in a templated function). 296e8d8bef9SDimitry Andric callExpr(allOf(isTypeDependent(), 297e8d8bef9SDimitry Andric callee(memberExpr(hasDeclaration(NonConstMethod), 298e8d8bef9SDimitry Andric hasObjectExpression(canResolveToExpr( 299e8d8bef9SDimitry Andric equalsNode(Exp))))))))); 3000b57cec5SDimitry Andric 3010b57cec5SDimitry Andric // Taking address of 'Exp'. 3020b57cec5SDimitry Andric // We're assuming 'Exp' is mutated as soon as its address is taken, though in 3030b57cec5SDimitry Andric // theory we can follow the pointer and see whether it escaped `Stm` or is 3040b57cec5SDimitry Andric // dereferenced and then mutated. This is left for future improvements. 3050b57cec5SDimitry Andric const auto AsAmpersandOperand = 3060b57cec5SDimitry Andric unaryOperator(hasOperatorName("&"), 3070b57cec5SDimitry Andric // A NoOp implicit cast is adding const. 3080b57cec5SDimitry Andric unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))), 309e8d8bef9SDimitry Andric hasUnaryOperand(canResolveToExpr(equalsNode(Exp)))); 3100b57cec5SDimitry Andric const auto AsPointerFromArrayDecay = 3110b57cec5SDimitry Andric castExpr(hasCastKind(CK_ArrayToPointerDecay), 3120b57cec5SDimitry Andric unless(hasParent(arraySubscriptExpr())), 313e8d8bef9SDimitry Andric has(canResolveToExpr(equalsNode(Exp)))); 3140b57cec5SDimitry Andric // Treat calling `operator->()` of move-only classes as taking address. 3150b57cec5SDimitry Andric // These are typically smart pointers with unique ownership so we treat 3160b57cec5SDimitry Andric // mutation of pointee as mutation of the smart pointer itself. 317e8d8bef9SDimitry Andric const auto AsOperatorArrowThis = cxxOperatorCallExpr( 318e8d8bef9SDimitry Andric hasOverloadedOperatorName("->"), 319e8d8bef9SDimitry Andric callee( 320e8d8bef9SDimitry Andric cxxMethodDecl(ofClass(isMoveOnly()), returns(nonConstPointerType()))), 321e8d8bef9SDimitry Andric argumentCountIs(1), hasArgument(0, canResolveToExpr(equalsNode(Exp)))); 3220b57cec5SDimitry Andric 3230b57cec5SDimitry Andric // Used as non-const-ref argument when calling a function. 3240b57cec5SDimitry Andric // An argument is assumed to be non-const-ref when the function is unresolved. 3250b57cec5SDimitry Andric // Instantiated template functions are not handled here but in 3260b57cec5SDimitry Andric // findFunctionArgMutation which has additional smarts for handling forwarding 3270b57cec5SDimitry Andric // references. 328e8d8bef9SDimitry Andric const auto NonConstRefParam = forEachArgumentWithParamType( 329e8d8bef9SDimitry Andric anyOf(canResolveToExpr(equalsNode(Exp)), 330e8d8bef9SDimitry Andric memberExpr(hasObjectExpression(canResolveToExpr(equalsNode(Exp))))), 331e8d8bef9SDimitry Andric nonConstReferenceType()); 3320b57cec5SDimitry Andric const auto NotInstantiated = unless(hasDeclaration(isInstantiated())); 333e8d8bef9SDimitry Andric const auto TypeDependentCallee = 334e8d8bef9SDimitry Andric callee(expr(anyOf(unresolvedLookupExpr(), unresolvedMemberExpr(), 335e8d8bef9SDimitry Andric cxxDependentScopeMemberExpr(), 336e8d8bef9SDimitry Andric hasType(templateTypeParmType()), isTypeDependent()))); 337e8d8bef9SDimitry Andric 3380b57cec5SDimitry Andric const auto AsNonConstRefArg = anyOf( 3390b57cec5SDimitry Andric callExpr(NonConstRefParam, NotInstantiated), 3400b57cec5SDimitry Andric cxxConstructExpr(NonConstRefParam, NotInstantiated), 341e8d8bef9SDimitry Andric callExpr(TypeDependentCallee, 342e8d8bef9SDimitry Andric hasAnyArgument(canResolveToExpr(equalsNode(Exp)))), 343e8d8bef9SDimitry Andric cxxUnresolvedConstructExpr( 344e8d8bef9SDimitry Andric hasAnyArgument(canResolveToExpr(equalsNode(Exp)))), 345e8d8bef9SDimitry Andric // Previous False Positive in the following Code: 346e8d8bef9SDimitry Andric // `template <typename T> void f() { int i = 42; new Type<T>(i); }` 347e8d8bef9SDimitry Andric // Where the constructor of `Type` takes its argument as reference. 348e8d8bef9SDimitry Andric // The AST does not resolve in a `cxxConstructExpr` because it is 349e8d8bef9SDimitry Andric // type-dependent. 350e8d8bef9SDimitry Andric parenListExpr(hasDescendant(expr(canResolveToExpr(equalsNode(Exp))))), 351e8d8bef9SDimitry Andric // If the initializer is for a reference type, there is no cast for 352e8d8bef9SDimitry Andric // the variable. Values are cast to RValue first. 353e8d8bef9SDimitry Andric initListExpr(hasAnyInit(expr(canResolveToExpr(equalsNode(Exp)))))); 3540b57cec5SDimitry Andric 3550b57cec5SDimitry Andric // Captured by a lambda by reference. 3560b57cec5SDimitry Andric // If we're initializing a capture with 'Exp' directly then we're initializing 3570b57cec5SDimitry Andric // a reference capture. 3580b57cec5SDimitry Andric // For value captures there will be an ImplicitCastExpr <LValueToRValue>. 3590b57cec5SDimitry Andric const auto AsLambdaRefCaptureInit = lambdaExpr(hasCaptureInit(Exp)); 3600b57cec5SDimitry Andric 3610b57cec5SDimitry Andric // Returned as non-const-ref. 3620b57cec5SDimitry Andric // If we're returning 'Exp' directly then it's returned as non-const-ref. 3630b57cec5SDimitry Andric // For returning by value there will be an ImplicitCastExpr <LValueToRValue>. 3640b57cec5SDimitry Andric // For returning by const-ref there will be an ImplicitCastExpr <NoOp> (for 3650b57cec5SDimitry Andric // adding const.) 366e8d8bef9SDimitry Andric const auto AsNonConstRefReturn = 367e8d8bef9SDimitry Andric returnStmt(hasReturnValue(canResolveToExpr(equalsNode(Exp)))); 368e8d8bef9SDimitry Andric 369e8d8bef9SDimitry Andric // It is used as a non-const-reference for initalizing a range-for loop. 370e8d8bef9SDimitry Andric const auto AsNonConstRefRangeInit = cxxForRangeStmt( 371e8d8bef9SDimitry Andric hasRangeInit(declRefExpr(allOf(canResolveToExpr(equalsNode(Exp)), 372e8d8bef9SDimitry Andric hasType(nonConstReferenceType()))))); 3730b57cec5SDimitry Andric 3745ffd83dbSDimitry Andric const auto Matches = match( 375e8d8bef9SDimitry Andric traverse(TK_AsIs, 376e8d8bef9SDimitry Andric findAll(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand, 377e8d8bef9SDimitry Andric AsNonConstThis, AsAmpersandOperand, 378e8d8bef9SDimitry Andric AsPointerFromArrayDecay, AsOperatorArrowThis, 379e8d8bef9SDimitry Andric AsNonConstRefArg, AsLambdaRefCaptureInit, 380e8d8bef9SDimitry Andric AsNonConstRefReturn, AsNonConstRefRangeInit)) 3815ffd83dbSDimitry Andric .bind("stmt"))), 3820b57cec5SDimitry Andric Stm, Context); 3830b57cec5SDimitry Andric return selectFirst<Stmt>("stmt", Matches); 3840b57cec5SDimitry Andric } 3850b57cec5SDimitry Andric 3860b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findMemberMutation(const Expr *Exp) { 3870b57cec5SDimitry Andric // Check whether any member of 'Exp' is mutated. 3880b57cec5SDimitry Andric const auto MemberExprs = 389e8d8bef9SDimitry Andric match(findAll(expr(anyOf(memberExpr(hasObjectExpression( 390e8d8bef9SDimitry Andric canResolveToExpr(equalsNode(Exp)))), 391e8d8bef9SDimitry Andric cxxDependentScopeMemberExpr(hasObjectExpression( 392e8d8bef9SDimitry Andric canResolveToExpr(equalsNode(Exp)))))) 3930b57cec5SDimitry Andric .bind(NodeID<Expr>::value)), 3940b57cec5SDimitry Andric Stm, Context); 3950b57cec5SDimitry Andric return findExprMutation(MemberExprs); 3960b57cec5SDimitry Andric } 3970b57cec5SDimitry Andric 3980b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findArrayElementMutation(const Expr *Exp) { 3990b57cec5SDimitry Andric // Check whether any element of an array is mutated. 400e8d8bef9SDimitry Andric const auto SubscriptExprs = 401e8d8bef9SDimitry Andric match(findAll(arraySubscriptExpr( 402e8d8bef9SDimitry Andric anyOf(hasBase(canResolveToExpr(equalsNode(Exp))), 403e8d8bef9SDimitry Andric hasBase(implicitCastExpr( 404e8d8bef9SDimitry Andric allOf(hasCastKind(CK_ArrayToPointerDecay), 405e8d8bef9SDimitry Andric hasSourceExpression(canResolveToExpr( 406e8d8bef9SDimitry Andric equalsNode(Exp)))))))) 4070b57cec5SDimitry Andric .bind(NodeID<Expr>::value)), 4080b57cec5SDimitry Andric Stm, Context); 4090b57cec5SDimitry Andric return findExprMutation(SubscriptExprs); 4100b57cec5SDimitry Andric } 4110b57cec5SDimitry Andric 4120b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findCastMutation(const Expr *Exp) { 413e8d8bef9SDimitry Andric // If the 'Exp' is explicitly casted to a non-const reference type the 414e8d8bef9SDimitry Andric // 'Exp' is considered to be modified. 415e8d8bef9SDimitry Andric const auto ExplicitCast = match( 416e8d8bef9SDimitry Andric findAll( 417e8d8bef9SDimitry Andric stmt(castExpr(hasSourceExpression(canResolveToExpr(equalsNode(Exp))), 418e8d8bef9SDimitry Andric explicitCastExpr( 419e8d8bef9SDimitry Andric hasDestinationType(nonConstReferenceType())))) 420e8d8bef9SDimitry Andric .bind("stmt")), 421e8d8bef9SDimitry Andric Stm, Context); 422e8d8bef9SDimitry Andric 423e8d8bef9SDimitry Andric if (const auto *CastStmt = selectFirst<Stmt>("stmt", ExplicitCast)) 424e8d8bef9SDimitry Andric return CastStmt; 425e8d8bef9SDimitry Andric 4260b57cec5SDimitry Andric // If 'Exp' is casted to any non-const reference type, check the castExpr. 427e8d8bef9SDimitry Andric const auto Casts = match( 428e8d8bef9SDimitry Andric findAll( 429e8d8bef9SDimitry Andric expr(castExpr(hasSourceExpression(canResolveToExpr(equalsNode(Exp))), 430e8d8bef9SDimitry Andric anyOf(explicitCastExpr( 431e8d8bef9SDimitry Andric hasDestinationType(nonConstReferenceType())), 4320b57cec5SDimitry Andric implicitCastExpr(hasImplicitDestinationType( 433e8d8bef9SDimitry Andric nonConstReferenceType()))))) 4340b57cec5SDimitry Andric .bind(NodeID<Expr>::value)), 4350b57cec5SDimitry Andric Stm, Context); 436e8d8bef9SDimitry Andric 4370b57cec5SDimitry Andric if (const Stmt *S = findExprMutation(Casts)) 4380b57cec5SDimitry Andric return S; 4390b57cec5SDimitry Andric // Treat std::{move,forward} as cast. 4400b57cec5SDimitry Andric const auto Calls = 4410b57cec5SDimitry Andric match(findAll(callExpr(callee(namedDecl( 4420b57cec5SDimitry Andric hasAnyName("::std::move", "::std::forward"))), 443e8d8bef9SDimitry Andric hasArgument(0, canResolveToExpr(equalsNode(Exp)))) 4440b57cec5SDimitry Andric .bind("expr")), 4450b57cec5SDimitry Andric Stm, Context); 4460b57cec5SDimitry Andric return findExprMutation(Calls); 4470b57cec5SDimitry Andric } 4480b57cec5SDimitry Andric 4490b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findRangeLoopMutation(const Expr *Exp) { 450e8d8bef9SDimitry Andric // Keep the ordering for the specific initialization matches to happen first, 451e8d8bef9SDimitry Andric // because it is cheaper to match all potential modifications of the loop 452e8d8bef9SDimitry Andric // variable. 453e8d8bef9SDimitry Andric 454e8d8bef9SDimitry Andric // The range variable is a reference to a builtin array. In that case the 455e8d8bef9SDimitry Andric // array is considered modified if the loop-variable is a non-const reference. 456e8d8bef9SDimitry Andric const auto DeclStmtToNonRefToArray = declStmt(hasSingleDecl(varDecl(hasType( 457e8d8bef9SDimitry Andric hasUnqualifiedDesugaredType(referenceType(pointee(arrayType()))))))); 458e8d8bef9SDimitry Andric const auto RefToArrayRefToElements = match( 459e8d8bef9SDimitry Andric findAll(stmt(cxxForRangeStmt( 460e8d8bef9SDimitry Andric hasLoopVariable(varDecl(hasType(nonConstReferenceType())) 461e8d8bef9SDimitry Andric .bind(NodeID<Decl>::value)), 462e8d8bef9SDimitry Andric hasRangeStmt(DeclStmtToNonRefToArray), 463e8d8bef9SDimitry Andric hasRangeInit(canResolveToExpr(equalsNode(Exp))))) 464e8d8bef9SDimitry Andric .bind("stmt")), 465e8d8bef9SDimitry Andric Stm, Context); 466e8d8bef9SDimitry Andric 467e8d8bef9SDimitry Andric if (const auto *BadRangeInitFromArray = 468e8d8bef9SDimitry Andric selectFirst<Stmt>("stmt", RefToArrayRefToElements)) 469e8d8bef9SDimitry Andric return BadRangeInitFromArray; 470e8d8bef9SDimitry Andric 471e8d8bef9SDimitry Andric // Small helper to match special cases in range-for loops. 472e8d8bef9SDimitry Andric // 473e8d8bef9SDimitry Andric // It is possible that containers do not provide a const-overload for their 474e8d8bef9SDimitry Andric // iterator accessors. If this is the case, the variable is used non-const 475e8d8bef9SDimitry Andric // no matter what happens in the loop. This requires special detection as it 476e8d8bef9SDimitry Andric // is then faster to find all mutations of the loop variable. 477e8d8bef9SDimitry Andric // It aims at a different modification as well. 478e8d8bef9SDimitry Andric const auto HasAnyNonConstIterator = 479e8d8bef9SDimitry Andric anyOf(allOf(hasMethod(allOf(hasName("begin"), unless(isConst()))), 480e8d8bef9SDimitry Andric unless(hasMethod(allOf(hasName("begin"), isConst())))), 481e8d8bef9SDimitry Andric allOf(hasMethod(allOf(hasName("end"), unless(isConst()))), 482e8d8bef9SDimitry Andric unless(hasMethod(allOf(hasName("end"), isConst()))))); 483e8d8bef9SDimitry Andric 484e8d8bef9SDimitry Andric const auto DeclStmtToNonConstIteratorContainer = declStmt( 485e8d8bef9SDimitry Andric hasSingleDecl(varDecl(hasType(hasUnqualifiedDesugaredType(referenceType( 486e8d8bef9SDimitry Andric pointee(hasDeclaration(cxxRecordDecl(HasAnyNonConstIterator))))))))); 487e8d8bef9SDimitry Andric 488e8d8bef9SDimitry Andric const auto RefToContainerBadIterators = 489e8d8bef9SDimitry Andric match(findAll(stmt(cxxForRangeStmt(allOf( 490e8d8bef9SDimitry Andric hasRangeStmt(DeclStmtToNonConstIteratorContainer), 491e8d8bef9SDimitry Andric hasRangeInit(canResolveToExpr(equalsNode(Exp)))))) 492e8d8bef9SDimitry Andric .bind("stmt")), 493e8d8bef9SDimitry Andric Stm, Context); 494e8d8bef9SDimitry Andric 495e8d8bef9SDimitry Andric if (const auto *BadIteratorsContainer = 496e8d8bef9SDimitry Andric selectFirst<Stmt>("stmt", RefToContainerBadIterators)) 497e8d8bef9SDimitry Andric return BadIteratorsContainer; 498e8d8bef9SDimitry Andric 4990b57cec5SDimitry Andric // If range for looping over 'Exp' with a non-const reference loop variable, 5000b57cec5SDimitry Andric // check all declRefExpr of the loop variable. 5010b57cec5SDimitry Andric const auto LoopVars = 5020b57cec5SDimitry Andric match(findAll(cxxForRangeStmt( 5030b57cec5SDimitry Andric hasLoopVariable(varDecl(hasType(nonConstReferenceType())) 5040b57cec5SDimitry Andric .bind(NodeID<Decl>::value)), 505e8d8bef9SDimitry Andric hasRangeInit(canResolveToExpr(equalsNode(Exp))))), 5060b57cec5SDimitry Andric Stm, Context); 5070b57cec5SDimitry Andric return findDeclMutation(LoopVars); 5080b57cec5SDimitry Andric } 5090b57cec5SDimitry Andric 5100b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findReferenceMutation(const Expr *Exp) { 5110b57cec5SDimitry Andric // Follow non-const reference returned by `operator*()` of move-only classes. 5120b57cec5SDimitry Andric // These are typically smart pointers with unique ownership so we treat 5130b57cec5SDimitry Andric // mutation of pointee as mutation of the smart pointer itself. 5140b57cec5SDimitry Andric const auto Ref = 5150b57cec5SDimitry Andric match(findAll(cxxOperatorCallExpr( 5160b57cec5SDimitry Andric hasOverloadedOperatorName("*"), 5170b57cec5SDimitry Andric callee(cxxMethodDecl(ofClass(isMoveOnly()), 5180b57cec5SDimitry Andric returns(nonConstReferenceType()))), 519e8d8bef9SDimitry Andric argumentCountIs(1), 520e8d8bef9SDimitry Andric hasArgument(0, canResolveToExpr(equalsNode(Exp)))) 5210b57cec5SDimitry Andric .bind(NodeID<Expr>::value)), 5220b57cec5SDimitry Andric Stm, Context); 5230b57cec5SDimitry Andric if (const Stmt *S = findExprMutation(Ref)) 5240b57cec5SDimitry Andric return S; 5250b57cec5SDimitry Andric 5260b57cec5SDimitry Andric // If 'Exp' is bound to a non-const reference, check all declRefExpr to that. 5270b57cec5SDimitry Andric const auto Refs = match( 5280b57cec5SDimitry Andric stmt(forEachDescendant( 5290b57cec5SDimitry Andric varDecl( 5300b57cec5SDimitry Andric hasType(nonConstReferenceType()), 531e8d8bef9SDimitry Andric hasInitializer(anyOf(canResolveToExpr(equalsNode(Exp)), 532e8d8bef9SDimitry Andric memberExpr(hasObjectExpression( 533e8d8bef9SDimitry Andric canResolveToExpr(equalsNode(Exp)))))), 5340b57cec5SDimitry Andric hasParent(declStmt().bind("stmt")), 535e8d8bef9SDimitry Andric // Don't follow the reference in range statement, we've 536e8d8bef9SDimitry Andric // handled that separately. 5370b57cec5SDimitry Andric unless(hasParent(declStmt(hasParent( 5380b57cec5SDimitry Andric cxxForRangeStmt(hasRangeStmt(equalsBoundNode("stmt")))))))) 5390b57cec5SDimitry Andric .bind(NodeID<Decl>::value))), 5400b57cec5SDimitry Andric Stm, Context); 5410b57cec5SDimitry Andric return findDeclMutation(Refs); 5420b57cec5SDimitry Andric } 5430b57cec5SDimitry Andric 5440b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findFunctionArgMutation(const Expr *Exp) { 5450b57cec5SDimitry Andric const auto NonConstRefParam = forEachArgumentWithParam( 546e8d8bef9SDimitry Andric canResolveToExpr(equalsNode(Exp)), 5470b57cec5SDimitry Andric parmVarDecl(hasType(nonConstReferenceType())).bind("parm")); 5480b57cec5SDimitry Andric const auto IsInstantiated = hasDeclaration(isInstantiated()); 5490b57cec5SDimitry Andric const auto FuncDecl = hasDeclaration(functionDecl().bind("func")); 5500b57cec5SDimitry Andric const auto Matches = match( 5515ffd83dbSDimitry Andric traverse( 552e8d8bef9SDimitry Andric TK_AsIs, 5535ffd83dbSDimitry Andric findAll( 5545ffd83dbSDimitry Andric expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl, 5550b57cec5SDimitry Andric unless(callee(namedDecl(hasAnyName( 5560b57cec5SDimitry Andric "::std::move", "::std::forward"))))), 5570b57cec5SDimitry Andric cxxConstructExpr(NonConstRefParam, IsInstantiated, 5580b57cec5SDimitry Andric FuncDecl))) 5595ffd83dbSDimitry Andric .bind(NodeID<Expr>::value))), 5600b57cec5SDimitry Andric Stm, Context); 5610b57cec5SDimitry Andric for (const auto &Nodes : Matches) { 5620b57cec5SDimitry Andric const auto *Exp = Nodes.getNodeAs<Expr>(NodeID<Expr>::value); 5630b57cec5SDimitry Andric const auto *Func = Nodes.getNodeAs<FunctionDecl>("func"); 5640b57cec5SDimitry Andric if (!Func->getBody() || !Func->getPrimaryTemplate()) 5650b57cec5SDimitry Andric return Exp; 5660b57cec5SDimitry Andric 5670b57cec5SDimitry Andric const auto *Parm = Nodes.getNodeAs<ParmVarDecl>("parm"); 5680b57cec5SDimitry Andric const ArrayRef<ParmVarDecl *> AllParams = 5690b57cec5SDimitry Andric Func->getPrimaryTemplate()->getTemplatedDecl()->parameters(); 5700b57cec5SDimitry Andric QualType ParmType = 5710b57cec5SDimitry Andric AllParams[std::min<size_t>(Parm->getFunctionScopeIndex(), 5720b57cec5SDimitry Andric AllParams.size() - 1)] 5730b57cec5SDimitry Andric ->getType(); 5740b57cec5SDimitry Andric if (const auto *T = ParmType->getAs<PackExpansionType>()) 5750b57cec5SDimitry Andric ParmType = T->getPattern(); 5760b57cec5SDimitry Andric 5770b57cec5SDimitry Andric // If param type is forwarding reference, follow into the function 5780b57cec5SDimitry Andric // definition and see whether the param is mutated inside. 5790b57cec5SDimitry Andric if (const auto *RefType = ParmType->getAs<RValueReferenceType>()) { 5800b57cec5SDimitry Andric if (!RefType->getPointeeType().getQualifiers() && 5810b57cec5SDimitry Andric RefType->getPointeeType()->getAs<TemplateTypeParmType>()) { 5820b57cec5SDimitry Andric std::unique_ptr<FunctionParmMutationAnalyzer> &Analyzer = 5830b57cec5SDimitry Andric FuncParmAnalyzer[Func]; 5840b57cec5SDimitry Andric if (!Analyzer) 5850b57cec5SDimitry Andric Analyzer.reset(new FunctionParmMutationAnalyzer(*Func, Context)); 5860b57cec5SDimitry Andric if (Analyzer->findMutation(Parm)) 5870b57cec5SDimitry Andric return Exp; 5880b57cec5SDimitry Andric continue; 5890b57cec5SDimitry Andric } 5900b57cec5SDimitry Andric } 5910b57cec5SDimitry Andric // Not forwarding reference. 5920b57cec5SDimitry Andric return Exp; 5930b57cec5SDimitry Andric } 5940b57cec5SDimitry Andric return nullptr; 5950b57cec5SDimitry Andric } 5960b57cec5SDimitry Andric 5970b57cec5SDimitry Andric FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer( 5980b57cec5SDimitry Andric const FunctionDecl &Func, ASTContext &Context) 5990b57cec5SDimitry Andric : BodyAnalyzer(*Func.getBody(), Context) { 6000b57cec5SDimitry Andric if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(&Func)) { 6010b57cec5SDimitry Andric // CXXCtorInitializer might also mutate Param but they're not part of 6020b57cec5SDimitry Andric // function body, check them eagerly here since they're typically trivial. 6030b57cec5SDimitry Andric for (const CXXCtorInitializer *Init : Ctor->inits()) { 6040b57cec5SDimitry Andric ExprMutationAnalyzer InitAnalyzer(*Init->getInit(), Context); 6050b57cec5SDimitry Andric for (const ParmVarDecl *Parm : Ctor->parameters()) { 6060b57cec5SDimitry Andric if (Results.find(Parm) != Results.end()) 6070b57cec5SDimitry Andric continue; 6080b57cec5SDimitry Andric if (const Stmt *S = InitAnalyzer.findMutation(Parm)) 6090b57cec5SDimitry Andric Results[Parm] = S; 6100b57cec5SDimitry Andric } 6110b57cec5SDimitry Andric } 6120b57cec5SDimitry Andric } 6130b57cec5SDimitry Andric } 6140b57cec5SDimitry Andric 6150b57cec5SDimitry Andric const Stmt * 6160b57cec5SDimitry Andric FunctionParmMutationAnalyzer::findMutation(const ParmVarDecl *Parm) { 6170b57cec5SDimitry Andric const auto Memoized = Results.find(Parm); 6180b57cec5SDimitry Andric if (Memoized != Results.end()) 6190b57cec5SDimitry Andric return Memoized->second; 6200b57cec5SDimitry Andric 6210b57cec5SDimitry Andric if (const Stmt *S = BodyAnalyzer.findMutation(Parm)) 6220b57cec5SDimitry Andric return Results[Parm] = S; 6230b57cec5SDimitry Andric 6240b57cec5SDimitry Andric return Results[Parm] = nullptr; 6250b57cec5SDimitry Andric } 6260b57cec5SDimitry Andric 6270b57cec5SDimitry Andric } // namespace clang 628