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" 90b57cec5SDimitry Andric #include "clang/ASTMatchers/ASTMatchFinder.h" 100b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h" 110b57cec5SDimitry Andric 120b57cec5SDimitry Andric namespace clang { 130b57cec5SDimitry Andric using namespace ast_matchers; 140b57cec5SDimitry Andric 150b57cec5SDimitry Andric namespace { 160b57cec5SDimitry Andric 170b57cec5SDimitry Andric AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) { 180b57cec5SDimitry Andric return llvm::is_contained(Node.capture_inits(), E); 190b57cec5SDimitry Andric } 200b57cec5SDimitry Andric 210b57cec5SDimitry Andric AST_MATCHER_P(CXXForRangeStmt, hasRangeStmt, 220b57cec5SDimitry Andric ast_matchers::internal::Matcher<DeclStmt>, InnerMatcher) { 230b57cec5SDimitry Andric const DeclStmt *const Range = Node.getRangeStmt(); 240b57cec5SDimitry Andric return InnerMatcher.matches(*Range, Finder, Builder); 250b57cec5SDimitry Andric } 260b57cec5SDimitry Andric 270b57cec5SDimitry Andric AST_MATCHER_P(Expr, maybeEvalCommaExpr, 280b57cec5SDimitry Andric ast_matchers::internal::Matcher<Expr>, InnerMatcher) { 290b57cec5SDimitry Andric const Expr* Result = &Node; 300b57cec5SDimitry Andric while (const auto *BOComma = 310b57cec5SDimitry Andric dyn_cast_or_null<BinaryOperator>(Result->IgnoreParens())) { 320b57cec5SDimitry Andric if (!BOComma->isCommaOp()) 330b57cec5SDimitry Andric break; 340b57cec5SDimitry Andric Result = BOComma->getRHS(); 350b57cec5SDimitry Andric } 360b57cec5SDimitry Andric return InnerMatcher.matches(*Result, Finder, Builder); 370b57cec5SDimitry Andric } 380b57cec5SDimitry Andric 390b57cec5SDimitry Andric const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXTypeidExpr> 400b57cec5SDimitry Andric cxxTypeidExpr; 410b57cec5SDimitry Andric 420b57cec5SDimitry Andric AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) { 430b57cec5SDimitry Andric return Node.isPotentiallyEvaluated(); 440b57cec5SDimitry Andric } 450b57cec5SDimitry Andric 460b57cec5SDimitry Andric const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, 470b57cec5SDimitry Andric GenericSelectionExpr> 480b57cec5SDimitry Andric genericSelectionExpr; 490b57cec5SDimitry Andric 500b57cec5SDimitry Andric AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr, 510b57cec5SDimitry Andric ast_matchers::internal::Matcher<Expr>, InnerMatcher) { 520b57cec5SDimitry Andric return InnerMatcher.matches(*Node.getControllingExpr(), Finder, Builder); 530b57cec5SDimitry Andric } 540b57cec5SDimitry Andric 550b57cec5SDimitry Andric const auto nonConstReferenceType = [] { 560b57cec5SDimitry Andric return hasUnqualifiedDesugaredType( 570b57cec5SDimitry Andric referenceType(pointee(unless(isConstQualified())))); 580b57cec5SDimitry Andric }; 590b57cec5SDimitry Andric 600b57cec5SDimitry Andric const auto nonConstPointerType = [] { 610b57cec5SDimitry Andric return hasUnqualifiedDesugaredType( 620b57cec5SDimitry Andric pointerType(pointee(unless(isConstQualified())))); 630b57cec5SDimitry Andric }; 640b57cec5SDimitry Andric 650b57cec5SDimitry Andric const auto isMoveOnly = [] { 660b57cec5SDimitry Andric return cxxRecordDecl( 670b57cec5SDimitry Andric hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted()))), 680b57cec5SDimitry Andric hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted()))), 690b57cec5SDimitry Andric unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(), 700b57cec5SDimitry Andric unless(isDeleted()))), 710b57cec5SDimitry Andric hasMethod(cxxMethodDecl(isCopyAssignmentOperator(), 720b57cec5SDimitry Andric unless(isDeleted())))))); 730b57cec5SDimitry Andric }; 740b57cec5SDimitry Andric 750b57cec5SDimitry Andric template <class T> struct NodeID; 76*5ffd83dbSDimitry Andric template <> struct NodeID<Expr> { static constexpr StringRef value = "expr"; }; 77*5ffd83dbSDimitry Andric template <> struct NodeID<Decl> { static constexpr StringRef value = "decl"; }; 78*5ffd83dbSDimitry Andric constexpr StringRef NodeID<Expr>::value; 79*5ffd83dbSDimitry Andric constexpr StringRef NodeID<Decl>::value; 800b57cec5SDimitry Andric 810b57cec5SDimitry Andric template <class T, class F = const Stmt *(ExprMutationAnalyzer::*)(const T *)> 820b57cec5SDimitry Andric const Stmt *tryEachMatch(ArrayRef<ast_matchers::BoundNodes> Matches, 830b57cec5SDimitry Andric ExprMutationAnalyzer *Analyzer, F Finder) { 840b57cec5SDimitry Andric const StringRef ID = NodeID<T>::value; 850b57cec5SDimitry Andric for (const auto &Nodes : Matches) { 860b57cec5SDimitry Andric if (const Stmt *S = (Analyzer->*Finder)(Nodes.getNodeAs<T>(ID))) 870b57cec5SDimitry Andric return S; 880b57cec5SDimitry Andric } 890b57cec5SDimitry Andric return nullptr; 900b57cec5SDimitry Andric } 910b57cec5SDimitry Andric 920b57cec5SDimitry Andric } // namespace 930b57cec5SDimitry Andric 940b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findMutation(const Expr *Exp) { 950b57cec5SDimitry Andric return findMutationMemoized(Exp, 960b57cec5SDimitry Andric {&ExprMutationAnalyzer::findDirectMutation, 970b57cec5SDimitry Andric &ExprMutationAnalyzer::findMemberMutation, 980b57cec5SDimitry Andric &ExprMutationAnalyzer::findArrayElementMutation, 990b57cec5SDimitry Andric &ExprMutationAnalyzer::findCastMutation, 1000b57cec5SDimitry Andric &ExprMutationAnalyzer::findRangeLoopMutation, 1010b57cec5SDimitry Andric &ExprMutationAnalyzer::findReferenceMutation, 1020b57cec5SDimitry Andric &ExprMutationAnalyzer::findFunctionArgMutation}, 1030b57cec5SDimitry Andric Results); 1040b57cec5SDimitry Andric } 1050b57cec5SDimitry Andric 1060b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findMutation(const Decl *Dec) { 1070b57cec5SDimitry Andric return tryEachDeclRef(Dec, &ExprMutationAnalyzer::findMutation); 1080b57cec5SDimitry Andric } 1090b57cec5SDimitry Andric 1100b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findPointeeMutation(const Expr *Exp) { 1110b57cec5SDimitry Andric return findMutationMemoized(Exp, {/*TODO*/}, PointeeResults); 1120b57cec5SDimitry Andric } 1130b57cec5SDimitry Andric 1140b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findPointeeMutation(const Decl *Dec) { 1150b57cec5SDimitry Andric return tryEachDeclRef(Dec, &ExprMutationAnalyzer::findPointeeMutation); 1160b57cec5SDimitry Andric } 1170b57cec5SDimitry Andric 1180b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findMutationMemoized( 1190b57cec5SDimitry Andric const Expr *Exp, llvm::ArrayRef<MutationFinder> Finders, 1200b57cec5SDimitry Andric ResultMap &MemoizedResults) { 1210b57cec5SDimitry Andric const auto Memoized = MemoizedResults.find(Exp); 1220b57cec5SDimitry Andric if (Memoized != MemoizedResults.end()) 1230b57cec5SDimitry Andric return Memoized->second; 1240b57cec5SDimitry Andric 1250b57cec5SDimitry Andric if (isUnevaluated(Exp)) 1260b57cec5SDimitry Andric return MemoizedResults[Exp] = nullptr; 1270b57cec5SDimitry Andric 1280b57cec5SDimitry Andric for (const auto &Finder : Finders) { 1290b57cec5SDimitry Andric if (const Stmt *S = (this->*Finder)(Exp)) 1300b57cec5SDimitry Andric return MemoizedResults[Exp] = S; 1310b57cec5SDimitry Andric } 1320b57cec5SDimitry Andric 1330b57cec5SDimitry Andric return MemoizedResults[Exp] = nullptr; 1340b57cec5SDimitry Andric } 1350b57cec5SDimitry Andric 1360b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::tryEachDeclRef(const Decl *Dec, 1370b57cec5SDimitry Andric MutationFinder Finder) { 1380b57cec5SDimitry Andric const auto Refs = 1390b57cec5SDimitry Andric match(findAll(declRefExpr(to(equalsNode(Dec))).bind(NodeID<Expr>::value)), 1400b57cec5SDimitry Andric Stm, Context); 1410b57cec5SDimitry Andric for (const auto &RefNodes : Refs) { 1420b57cec5SDimitry Andric const auto *E = RefNodes.getNodeAs<Expr>(NodeID<Expr>::value); 1430b57cec5SDimitry Andric if ((this->*Finder)(E)) 1440b57cec5SDimitry Andric return E; 1450b57cec5SDimitry Andric } 1460b57cec5SDimitry Andric return nullptr; 1470b57cec5SDimitry Andric } 1480b57cec5SDimitry Andric 1490b57cec5SDimitry Andric bool ExprMutationAnalyzer::isUnevaluated(const Expr *Exp) { 1500b57cec5SDimitry Andric return selectFirst<Expr>( 1510b57cec5SDimitry Andric NodeID<Expr>::value, 1520b57cec5SDimitry Andric match( 1530b57cec5SDimitry Andric findAll( 1540b57cec5SDimitry Andric expr(equalsNode(Exp), 1550b57cec5SDimitry Andric anyOf( 1560b57cec5SDimitry Andric // `Exp` is part of the underlying expression of 1570b57cec5SDimitry Andric // decltype/typeof if it has an ancestor of 1580b57cec5SDimitry Andric // typeLoc. 1590b57cec5SDimitry Andric hasAncestor(typeLoc(unless( 1600b57cec5SDimitry Andric hasAncestor(unaryExprOrTypeTraitExpr())))), 1610b57cec5SDimitry Andric hasAncestor(expr(anyOf( 1620b57cec5SDimitry Andric // `UnaryExprOrTypeTraitExpr` is unevaluated 1630b57cec5SDimitry Andric // unless it's sizeof on VLA. 1640b57cec5SDimitry Andric unaryExprOrTypeTraitExpr(unless(sizeOfExpr( 1650b57cec5SDimitry Andric hasArgumentOfType(variableArrayType())))), 1660b57cec5SDimitry Andric // `CXXTypeidExpr` is unevaluated unless it's 1670b57cec5SDimitry Andric // applied to an expression of glvalue of 1680b57cec5SDimitry Andric // polymorphic class type. 1690b57cec5SDimitry Andric cxxTypeidExpr( 1700b57cec5SDimitry Andric unless(isPotentiallyEvaluated())), 1710b57cec5SDimitry Andric // The controlling expression of 1720b57cec5SDimitry Andric // `GenericSelectionExpr` is unevaluated. 1730b57cec5SDimitry Andric genericSelectionExpr(hasControllingExpr( 1740b57cec5SDimitry Andric hasDescendant(equalsNode(Exp)))), 1750b57cec5SDimitry Andric cxxNoexceptExpr()))))) 1760b57cec5SDimitry Andric .bind(NodeID<Expr>::value)), 1770b57cec5SDimitry Andric Stm, Context)) != nullptr; 1780b57cec5SDimitry Andric } 1790b57cec5SDimitry Andric 1800b57cec5SDimitry Andric const Stmt * 1810b57cec5SDimitry Andric ExprMutationAnalyzer::findExprMutation(ArrayRef<BoundNodes> Matches) { 1820b57cec5SDimitry Andric return tryEachMatch<Expr>(Matches, this, &ExprMutationAnalyzer::findMutation); 1830b57cec5SDimitry Andric } 1840b57cec5SDimitry Andric 1850b57cec5SDimitry Andric const Stmt * 1860b57cec5SDimitry Andric ExprMutationAnalyzer::findDeclMutation(ArrayRef<BoundNodes> Matches) { 1870b57cec5SDimitry Andric return tryEachMatch<Decl>(Matches, this, &ExprMutationAnalyzer::findMutation); 1880b57cec5SDimitry Andric } 1890b57cec5SDimitry Andric 1900b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findExprPointeeMutation( 1910b57cec5SDimitry Andric ArrayRef<ast_matchers::BoundNodes> Matches) { 1920b57cec5SDimitry Andric return tryEachMatch<Expr>(Matches, this, 1930b57cec5SDimitry Andric &ExprMutationAnalyzer::findPointeeMutation); 1940b57cec5SDimitry Andric } 1950b57cec5SDimitry Andric 1960b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findDeclPointeeMutation( 1970b57cec5SDimitry Andric ArrayRef<ast_matchers::BoundNodes> Matches) { 1980b57cec5SDimitry Andric return tryEachMatch<Decl>(Matches, this, 1990b57cec5SDimitry Andric &ExprMutationAnalyzer::findPointeeMutation); 2000b57cec5SDimitry Andric } 2010b57cec5SDimitry Andric 2020b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) { 2030b57cec5SDimitry Andric // LHS of any assignment operators. 204*5ffd83dbSDimitry Andric const auto AsAssignmentLhs = binaryOperator( 205*5ffd83dbSDimitry Andric isAssignmentOperator(), 206*5ffd83dbSDimitry Andric hasLHS(maybeEvalCommaExpr(ignoringParenImpCasts(equalsNode(Exp))))); 2070b57cec5SDimitry Andric 2080b57cec5SDimitry Andric // Operand of increment/decrement operators. 2090b57cec5SDimitry Andric const auto AsIncDecOperand = 2100b57cec5SDimitry Andric unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")), 211*5ffd83dbSDimitry Andric hasUnaryOperand(maybeEvalCommaExpr( 212*5ffd83dbSDimitry Andric ignoringParenImpCasts(equalsNode(Exp))))); 2130b57cec5SDimitry Andric 2140b57cec5SDimitry Andric // Invoking non-const member function. 2150b57cec5SDimitry Andric // A member function is assumed to be non-const when it is unresolved. 2160b57cec5SDimitry Andric const auto NonConstMethod = cxxMethodDecl(unless(isConst())); 2170b57cec5SDimitry Andric const auto AsNonConstThis = 2180b57cec5SDimitry Andric expr(anyOf(cxxMemberCallExpr(callee(NonConstMethod), 2190b57cec5SDimitry Andric on(maybeEvalCommaExpr(equalsNode(Exp)))), 2200b57cec5SDimitry Andric cxxOperatorCallExpr(callee(NonConstMethod), 2210b57cec5SDimitry Andric hasArgument(0, 2220b57cec5SDimitry Andric maybeEvalCommaExpr(equalsNode(Exp)))), 2230b57cec5SDimitry Andric callExpr(callee(expr(anyOf( 2240b57cec5SDimitry Andric unresolvedMemberExpr( 2250b57cec5SDimitry Andric hasObjectExpression(maybeEvalCommaExpr(equalsNode(Exp)))), 2260b57cec5SDimitry Andric cxxDependentScopeMemberExpr( 2270b57cec5SDimitry Andric hasObjectExpression(maybeEvalCommaExpr(equalsNode(Exp)))))))))); 2280b57cec5SDimitry Andric 2290b57cec5SDimitry Andric // Taking address of 'Exp'. 2300b57cec5SDimitry Andric // We're assuming 'Exp' is mutated as soon as its address is taken, though in 2310b57cec5SDimitry Andric // theory we can follow the pointer and see whether it escaped `Stm` or is 2320b57cec5SDimitry Andric // dereferenced and then mutated. This is left for future improvements. 2330b57cec5SDimitry Andric const auto AsAmpersandOperand = 2340b57cec5SDimitry Andric unaryOperator(hasOperatorName("&"), 2350b57cec5SDimitry Andric // A NoOp implicit cast is adding const. 2360b57cec5SDimitry Andric unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))), 2370b57cec5SDimitry Andric hasUnaryOperand(maybeEvalCommaExpr(equalsNode(Exp)))); 2380b57cec5SDimitry Andric const auto AsPointerFromArrayDecay = 2390b57cec5SDimitry Andric castExpr(hasCastKind(CK_ArrayToPointerDecay), 2400b57cec5SDimitry Andric unless(hasParent(arraySubscriptExpr())), 2410b57cec5SDimitry Andric has(maybeEvalCommaExpr(equalsNode(Exp)))); 2420b57cec5SDimitry Andric // Treat calling `operator->()` of move-only classes as taking address. 2430b57cec5SDimitry Andric // These are typically smart pointers with unique ownership so we treat 2440b57cec5SDimitry Andric // mutation of pointee as mutation of the smart pointer itself. 2450b57cec5SDimitry Andric const auto AsOperatorArrowThis = 2460b57cec5SDimitry Andric cxxOperatorCallExpr(hasOverloadedOperatorName("->"), 2470b57cec5SDimitry Andric callee(cxxMethodDecl(ofClass(isMoveOnly()), 2480b57cec5SDimitry Andric returns(nonConstPointerType()))), 2490b57cec5SDimitry Andric argumentCountIs(1), 2500b57cec5SDimitry Andric hasArgument(0, maybeEvalCommaExpr(equalsNode(Exp)))); 2510b57cec5SDimitry Andric 2520b57cec5SDimitry Andric // Used as non-const-ref argument when calling a function. 2530b57cec5SDimitry Andric // An argument is assumed to be non-const-ref when the function is unresolved. 2540b57cec5SDimitry Andric // Instantiated template functions are not handled here but in 2550b57cec5SDimitry Andric // findFunctionArgMutation which has additional smarts for handling forwarding 2560b57cec5SDimitry Andric // references. 2570b57cec5SDimitry Andric const auto NonConstRefParam = forEachArgumentWithParam( 2580b57cec5SDimitry Andric maybeEvalCommaExpr(equalsNode(Exp)), 2590b57cec5SDimitry Andric parmVarDecl(hasType(nonConstReferenceType()))); 2600b57cec5SDimitry Andric const auto NotInstantiated = unless(hasDeclaration(isInstantiated())); 2610b57cec5SDimitry Andric const auto AsNonConstRefArg = anyOf( 2620b57cec5SDimitry Andric callExpr(NonConstRefParam, NotInstantiated), 2630b57cec5SDimitry Andric cxxConstructExpr(NonConstRefParam, NotInstantiated), 2640b57cec5SDimitry Andric callExpr(callee(expr(anyOf(unresolvedLookupExpr(), unresolvedMemberExpr(), 2650b57cec5SDimitry Andric cxxDependentScopeMemberExpr(), 2660b57cec5SDimitry Andric hasType(templateTypeParmType())))), 2670b57cec5SDimitry Andric hasAnyArgument(maybeEvalCommaExpr(equalsNode(Exp)))), 2680b57cec5SDimitry Andric cxxUnresolvedConstructExpr(hasAnyArgument(maybeEvalCommaExpr(equalsNode(Exp))))); 2690b57cec5SDimitry Andric 2700b57cec5SDimitry Andric // Captured by a lambda by reference. 2710b57cec5SDimitry Andric // If we're initializing a capture with 'Exp' directly then we're initializing 2720b57cec5SDimitry Andric // a reference capture. 2730b57cec5SDimitry Andric // For value captures there will be an ImplicitCastExpr <LValueToRValue>. 2740b57cec5SDimitry Andric const auto AsLambdaRefCaptureInit = lambdaExpr(hasCaptureInit(Exp)); 2750b57cec5SDimitry Andric 2760b57cec5SDimitry Andric // Returned as non-const-ref. 2770b57cec5SDimitry Andric // If we're returning 'Exp' directly then it's returned as non-const-ref. 2780b57cec5SDimitry Andric // For returning by value there will be an ImplicitCastExpr <LValueToRValue>. 2790b57cec5SDimitry Andric // For returning by const-ref there will be an ImplicitCastExpr <NoOp> (for 2800b57cec5SDimitry Andric // adding const.) 2810b57cec5SDimitry Andric const auto AsNonConstRefReturn = returnStmt(hasReturnValue( 2820b57cec5SDimitry Andric maybeEvalCommaExpr(equalsNode(Exp)))); 2830b57cec5SDimitry Andric 284*5ffd83dbSDimitry Andric const auto Matches = match( 285*5ffd83dbSDimitry Andric traverse( 286*5ffd83dbSDimitry Andric ast_type_traits::TK_AsIs, 287*5ffd83dbSDimitry Andric findAll(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand, AsNonConstThis, 2880b57cec5SDimitry Andric AsAmpersandOperand, AsPointerFromArrayDecay, 2890b57cec5SDimitry Andric AsOperatorArrowThis, AsNonConstRefArg, 2900b57cec5SDimitry Andric AsLambdaRefCaptureInit, AsNonConstRefReturn)) 291*5ffd83dbSDimitry Andric .bind("stmt"))), 2920b57cec5SDimitry Andric Stm, Context); 2930b57cec5SDimitry Andric return selectFirst<Stmt>("stmt", Matches); 2940b57cec5SDimitry Andric } 2950b57cec5SDimitry Andric 2960b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findMemberMutation(const Expr *Exp) { 2970b57cec5SDimitry Andric // Check whether any member of 'Exp' is mutated. 2980b57cec5SDimitry Andric const auto MemberExprs = 2990b57cec5SDimitry Andric match(findAll(expr(anyOf(memberExpr(hasObjectExpression(equalsNode(Exp))), 3000b57cec5SDimitry Andric cxxDependentScopeMemberExpr( 3010b57cec5SDimitry Andric hasObjectExpression(equalsNode(Exp))))) 3020b57cec5SDimitry Andric .bind(NodeID<Expr>::value)), 3030b57cec5SDimitry Andric Stm, Context); 3040b57cec5SDimitry Andric return findExprMutation(MemberExprs); 3050b57cec5SDimitry Andric } 3060b57cec5SDimitry Andric 3070b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findArrayElementMutation(const Expr *Exp) { 3080b57cec5SDimitry Andric // Check whether any element of an array is mutated. 3090b57cec5SDimitry Andric const auto SubscriptExprs = match( 3100b57cec5SDimitry Andric findAll(arraySubscriptExpr(hasBase(ignoringImpCasts(equalsNode(Exp)))) 3110b57cec5SDimitry Andric .bind(NodeID<Expr>::value)), 3120b57cec5SDimitry Andric Stm, Context); 3130b57cec5SDimitry Andric return findExprMutation(SubscriptExprs); 3140b57cec5SDimitry Andric } 3150b57cec5SDimitry Andric 3160b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findCastMutation(const Expr *Exp) { 3170b57cec5SDimitry Andric // If 'Exp' is casted to any non-const reference type, check the castExpr. 3180b57cec5SDimitry Andric const auto Casts = 3190b57cec5SDimitry Andric match(findAll(castExpr(hasSourceExpression(equalsNode(Exp)), 3200b57cec5SDimitry Andric anyOf(explicitCastExpr(hasDestinationType( 3210b57cec5SDimitry Andric nonConstReferenceType())), 3220b57cec5SDimitry Andric implicitCastExpr(hasImplicitDestinationType( 3230b57cec5SDimitry Andric nonConstReferenceType())))) 3240b57cec5SDimitry Andric .bind(NodeID<Expr>::value)), 3250b57cec5SDimitry Andric Stm, Context); 3260b57cec5SDimitry Andric if (const Stmt *S = findExprMutation(Casts)) 3270b57cec5SDimitry Andric return S; 3280b57cec5SDimitry Andric // Treat std::{move,forward} as cast. 3290b57cec5SDimitry Andric const auto Calls = 3300b57cec5SDimitry Andric match(findAll(callExpr(callee(namedDecl( 3310b57cec5SDimitry Andric hasAnyName("::std::move", "::std::forward"))), 3320b57cec5SDimitry Andric hasArgument(0, equalsNode(Exp))) 3330b57cec5SDimitry Andric .bind("expr")), 3340b57cec5SDimitry Andric Stm, Context); 3350b57cec5SDimitry Andric return findExprMutation(Calls); 3360b57cec5SDimitry Andric } 3370b57cec5SDimitry Andric 3380b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findRangeLoopMutation(const Expr *Exp) { 3390b57cec5SDimitry Andric // If range for looping over 'Exp' with a non-const reference loop variable, 3400b57cec5SDimitry Andric // check all declRefExpr of the loop variable. 3410b57cec5SDimitry Andric const auto LoopVars = 3420b57cec5SDimitry Andric match(findAll(cxxForRangeStmt( 3430b57cec5SDimitry Andric hasLoopVariable(varDecl(hasType(nonConstReferenceType())) 3440b57cec5SDimitry Andric .bind(NodeID<Decl>::value)), 3450b57cec5SDimitry Andric hasRangeInit(equalsNode(Exp)))), 3460b57cec5SDimitry Andric Stm, Context); 3470b57cec5SDimitry Andric return findDeclMutation(LoopVars); 3480b57cec5SDimitry Andric } 3490b57cec5SDimitry Andric 3500b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findReferenceMutation(const Expr *Exp) { 3510b57cec5SDimitry Andric // Follow non-const reference returned by `operator*()` of move-only classes. 3520b57cec5SDimitry Andric // These are typically smart pointers with unique ownership so we treat 3530b57cec5SDimitry Andric // mutation of pointee as mutation of the smart pointer itself. 3540b57cec5SDimitry Andric const auto Ref = 3550b57cec5SDimitry Andric match(findAll(cxxOperatorCallExpr( 3560b57cec5SDimitry Andric hasOverloadedOperatorName("*"), 3570b57cec5SDimitry Andric callee(cxxMethodDecl(ofClass(isMoveOnly()), 3580b57cec5SDimitry Andric returns(nonConstReferenceType()))), 3590b57cec5SDimitry Andric argumentCountIs(1), hasArgument(0, equalsNode(Exp))) 3600b57cec5SDimitry Andric .bind(NodeID<Expr>::value)), 3610b57cec5SDimitry Andric Stm, Context); 3620b57cec5SDimitry Andric if (const Stmt *S = findExprMutation(Ref)) 3630b57cec5SDimitry Andric return S; 3640b57cec5SDimitry Andric 3650b57cec5SDimitry Andric // If 'Exp' is bound to a non-const reference, check all declRefExpr to that. 3660b57cec5SDimitry Andric const auto Refs = match( 3670b57cec5SDimitry Andric stmt(forEachDescendant( 3680b57cec5SDimitry Andric varDecl( 3690b57cec5SDimitry Andric hasType(nonConstReferenceType()), 3700b57cec5SDimitry Andric hasInitializer(anyOf(equalsNode(Exp), 3710b57cec5SDimitry Andric conditionalOperator(anyOf( 3720b57cec5SDimitry Andric hasTrueExpression(equalsNode(Exp)), 3730b57cec5SDimitry Andric hasFalseExpression(equalsNode(Exp)))))), 3740b57cec5SDimitry Andric hasParent(declStmt().bind("stmt")), 3750b57cec5SDimitry Andric // Don't follow the reference in range statement, we've handled 3760b57cec5SDimitry Andric // that separately. 3770b57cec5SDimitry Andric unless(hasParent(declStmt(hasParent( 3780b57cec5SDimitry Andric cxxForRangeStmt(hasRangeStmt(equalsBoundNode("stmt")))))))) 3790b57cec5SDimitry Andric .bind(NodeID<Decl>::value))), 3800b57cec5SDimitry Andric Stm, Context); 3810b57cec5SDimitry Andric return findDeclMutation(Refs); 3820b57cec5SDimitry Andric } 3830b57cec5SDimitry Andric 3840b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findFunctionArgMutation(const Expr *Exp) { 3850b57cec5SDimitry Andric const auto NonConstRefParam = forEachArgumentWithParam( 3860b57cec5SDimitry Andric equalsNode(Exp), 3870b57cec5SDimitry Andric parmVarDecl(hasType(nonConstReferenceType())).bind("parm")); 3880b57cec5SDimitry Andric const auto IsInstantiated = hasDeclaration(isInstantiated()); 3890b57cec5SDimitry Andric const auto FuncDecl = hasDeclaration(functionDecl().bind("func")); 3900b57cec5SDimitry Andric const auto Matches = match( 391*5ffd83dbSDimitry Andric traverse( 392*5ffd83dbSDimitry Andric ast_type_traits::TK_AsIs, 393*5ffd83dbSDimitry Andric findAll( 394*5ffd83dbSDimitry Andric expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl, 3950b57cec5SDimitry Andric unless(callee(namedDecl(hasAnyName( 3960b57cec5SDimitry Andric "::std::move", "::std::forward"))))), 3970b57cec5SDimitry Andric cxxConstructExpr(NonConstRefParam, IsInstantiated, 3980b57cec5SDimitry Andric FuncDecl))) 399*5ffd83dbSDimitry Andric .bind(NodeID<Expr>::value))), 4000b57cec5SDimitry Andric Stm, Context); 4010b57cec5SDimitry Andric for (const auto &Nodes : Matches) { 4020b57cec5SDimitry Andric const auto *Exp = Nodes.getNodeAs<Expr>(NodeID<Expr>::value); 4030b57cec5SDimitry Andric const auto *Func = Nodes.getNodeAs<FunctionDecl>("func"); 4040b57cec5SDimitry Andric if (!Func->getBody() || !Func->getPrimaryTemplate()) 4050b57cec5SDimitry Andric return Exp; 4060b57cec5SDimitry Andric 4070b57cec5SDimitry Andric const auto *Parm = Nodes.getNodeAs<ParmVarDecl>("parm"); 4080b57cec5SDimitry Andric const ArrayRef<ParmVarDecl *> AllParams = 4090b57cec5SDimitry Andric Func->getPrimaryTemplate()->getTemplatedDecl()->parameters(); 4100b57cec5SDimitry Andric QualType ParmType = 4110b57cec5SDimitry Andric AllParams[std::min<size_t>(Parm->getFunctionScopeIndex(), 4120b57cec5SDimitry Andric AllParams.size() - 1)] 4130b57cec5SDimitry Andric ->getType(); 4140b57cec5SDimitry Andric if (const auto *T = ParmType->getAs<PackExpansionType>()) 4150b57cec5SDimitry Andric ParmType = T->getPattern(); 4160b57cec5SDimitry Andric 4170b57cec5SDimitry Andric // If param type is forwarding reference, follow into the function 4180b57cec5SDimitry Andric // definition and see whether the param is mutated inside. 4190b57cec5SDimitry Andric if (const auto *RefType = ParmType->getAs<RValueReferenceType>()) { 4200b57cec5SDimitry Andric if (!RefType->getPointeeType().getQualifiers() && 4210b57cec5SDimitry Andric RefType->getPointeeType()->getAs<TemplateTypeParmType>()) { 4220b57cec5SDimitry Andric std::unique_ptr<FunctionParmMutationAnalyzer> &Analyzer = 4230b57cec5SDimitry Andric FuncParmAnalyzer[Func]; 4240b57cec5SDimitry Andric if (!Analyzer) 4250b57cec5SDimitry Andric Analyzer.reset(new FunctionParmMutationAnalyzer(*Func, Context)); 4260b57cec5SDimitry Andric if (Analyzer->findMutation(Parm)) 4270b57cec5SDimitry Andric return Exp; 4280b57cec5SDimitry Andric continue; 4290b57cec5SDimitry Andric } 4300b57cec5SDimitry Andric } 4310b57cec5SDimitry Andric // Not forwarding reference. 4320b57cec5SDimitry Andric return Exp; 4330b57cec5SDimitry Andric } 4340b57cec5SDimitry Andric return nullptr; 4350b57cec5SDimitry Andric } 4360b57cec5SDimitry Andric 4370b57cec5SDimitry Andric FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer( 4380b57cec5SDimitry Andric const FunctionDecl &Func, ASTContext &Context) 4390b57cec5SDimitry Andric : BodyAnalyzer(*Func.getBody(), Context) { 4400b57cec5SDimitry Andric if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(&Func)) { 4410b57cec5SDimitry Andric // CXXCtorInitializer might also mutate Param but they're not part of 4420b57cec5SDimitry Andric // function body, check them eagerly here since they're typically trivial. 4430b57cec5SDimitry Andric for (const CXXCtorInitializer *Init : Ctor->inits()) { 4440b57cec5SDimitry Andric ExprMutationAnalyzer InitAnalyzer(*Init->getInit(), Context); 4450b57cec5SDimitry Andric for (const ParmVarDecl *Parm : Ctor->parameters()) { 4460b57cec5SDimitry Andric if (Results.find(Parm) != Results.end()) 4470b57cec5SDimitry Andric continue; 4480b57cec5SDimitry Andric if (const Stmt *S = InitAnalyzer.findMutation(Parm)) 4490b57cec5SDimitry Andric Results[Parm] = S; 4500b57cec5SDimitry Andric } 4510b57cec5SDimitry Andric } 4520b57cec5SDimitry Andric } 4530b57cec5SDimitry Andric } 4540b57cec5SDimitry Andric 4550b57cec5SDimitry Andric const Stmt * 4560b57cec5SDimitry Andric FunctionParmMutationAnalyzer::findMutation(const ParmVarDecl *Parm) { 4570b57cec5SDimitry Andric const auto Memoized = Results.find(Parm); 4580b57cec5SDimitry Andric if (Memoized != Results.end()) 4590b57cec5SDimitry Andric return Memoized->second; 4600b57cec5SDimitry Andric 4610b57cec5SDimitry Andric if (const Stmt *S = BodyAnalyzer.findMutation(Parm)) 4620b57cec5SDimitry Andric return Results[Parm] = S; 4630b57cec5SDimitry Andric 4640b57cec5SDimitry Andric return Results[Parm] = nullptr; 4650b57cec5SDimitry Andric } 4660b57cec5SDimitry Andric 4670b57cec5SDimitry Andric } // namespace clang 468