1*0b57cec5SDimitry Andric //===---------- ExprMutationAnalyzer.cpp ----------------------------------===// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric #include "clang/Analysis/Analyses/ExprMutationAnalyzer.h" 9*0b57cec5SDimitry Andric #include "clang/ASTMatchers/ASTMatchFinder.h" 10*0b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h" 11*0b57cec5SDimitry Andric 12*0b57cec5SDimitry Andric namespace clang { 13*0b57cec5SDimitry Andric using namespace ast_matchers; 14*0b57cec5SDimitry Andric 15*0b57cec5SDimitry Andric namespace { 16*0b57cec5SDimitry Andric 17*0b57cec5SDimitry Andric AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) { 18*0b57cec5SDimitry Andric return llvm::is_contained(Node.capture_inits(), E); 19*0b57cec5SDimitry Andric } 20*0b57cec5SDimitry Andric 21*0b57cec5SDimitry Andric AST_MATCHER_P(CXXForRangeStmt, hasRangeStmt, 22*0b57cec5SDimitry Andric ast_matchers::internal::Matcher<DeclStmt>, InnerMatcher) { 23*0b57cec5SDimitry Andric const DeclStmt *const Range = Node.getRangeStmt(); 24*0b57cec5SDimitry Andric return InnerMatcher.matches(*Range, Finder, Builder); 25*0b57cec5SDimitry Andric } 26*0b57cec5SDimitry Andric 27*0b57cec5SDimitry Andric AST_MATCHER_P(Expr, maybeEvalCommaExpr, 28*0b57cec5SDimitry Andric ast_matchers::internal::Matcher<Expr>, InnerMatcher) { 29*0b57cec5SDimitry Andric const Expr* Result = &Node; 30*0b57cec5SDimitry Andric while (const auto *BOComma = 31*0b57cec5SDimitry Andric dyn_cast_or_null<BinaryOperator>(Result->IgnoreParens())) { 32*0b57cec5SDimitry Andric if (!BOComma->isCommaOp()) 33*0b57cec5SDimitry Andric break; 34*0b57cec5SDimitry Andric Result = BOComma->getRHS(); 35*0b57cec5SDimitry Andric } 36*0b57cec5SDimitry Andric return InnerMatcher.matches(*Result, Finder, Builder); 37*0b57cec5SDimitry Andric } 38*0b57cec5SDimitry Andric 39*0b57cec5SDimitry Andric const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXTypeidExpr> 40*0b57cec5SDimitry Andric cxxTypeidExpr; 41*0b57cec5SDimitry Andric 42*0b57cec5SDimitry Andric AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) { 43*0b57cec5SDimitry Andric return Node.isPotentiallyEvaluated(); 44*0b57cec5SDimitry Andric } 45*0b57cec5SDimitry Andric 46*0b57cec5SDimitry Andric const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXNoexceptExpr> 47*0b57cec5SDimitry Andric cxxNoexceptExpr; 48*0b57cec5SDimitry Andric 49*0b57cec5SDimitry Andric const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, 50*0b57cec5SDimitry Andric GenericSelectionExpr> 51*0b57cec5SDimitry Andric genericSelectionExpr; 52*0b57cec5SDimitry Andric 53*0b57cec5SDimitry Andric AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr, 54*0b57cec5SDimitry Andric ast_matchers::internal::Matcher<Expr>, InnerMatcher) { 55*0b57cec5SDimitry Andric return InnerMatcher.matches(*Node.getControllingExpr(), Finder, Builder); 56*0b57cec5SDimitry Andric } 57*0b57cec5SDimitry Andric 58*0b57cec5SDimitry Andric const auto nonConstReferenceType = [] { 59*0b57cec5SDimitry Andric return hasUnqualifiedDesugaredType( 60*0b57cec5SDimitry Andric referenceType(pointee(unless(isConstQualified())))); 61*0b57cec5SDimitry Andric }; 62*0b57cec5SDimitry Andric 63*0b57cec5SDimitry Andric const auto nonConstPointerType = [] { 64*0b57cec5SDimitry Andric return hasUnqualifiedDesugaredType( 65*0b57cec5SDimitry Andric pointerType(pointee(unless(isConstQualified())))); 66*0b57cec5SDimitry Andric }; 67*0b57cec5SDimitry Andric 68*0b57cec5SDimitry Andric const auto isMoveOnly = [] { 69*0b57cec5SDimitry Andric return cxxRecordDecl( 70*0b57cec5SDimitry Andric hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted()))), 71*0b57cec5SDimitry Andric hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted()))), 72*0b57cec5SDimitry Andric unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(), 73*0b57cec5SDimitry Andric unless(isDeleted()))), 74*0b57cec5SDimitry Andric hasMethod(cxxMethodDecl(isCopyAssignmentOperator(), 75*0b57cec5SDimitry Andric unless(isDeleted())))))); 76*0b57cec5SDimitry Andric }; 77*0b57cec5SDimitry Andric 78*0b57cec5SDimitry Andric template <class T> struct NodeID; 79*0b57cec5SDimitry Andric template <> struct NodeID<Expr> { static const std::string value; }; 80*0b57cec5SDimitry Andric template <> struct NodeID<Decl> { static const std::string value; }; 81*0b57cec5SDimitry Andric const std::string NodeID<Expr>::value = "expr"; 82*0b57cec5SDimitry Andric const std::string NodeID<Decl>::value = "decl"; 83*0b57cec5SDimitry Andric 84*0b57cec5SDimitry Andric template <class T, class F = const Stmt *(ExprMutationAnalyzer::*)(const T *)> 85*0b57cec5SDimitry Andric const Stmt *tryEachMatch(ArrayRef<ast_matchers::BoundNodes> Matches, 86*0b57cec5SDimitry Andric ExprMutationAnalyzer *Analyzer, F Finder) { 87*0b57cec5SDimitry Andric const StringRef ID = NodeID<T>::value; 88*0b57cec5SDimitry Andric for (const auto &Nodes : Matches) { 89*0b57cec5SDimitry Andric if (const Stmt *S = (Analyzer->*Finder)(Nodes.getNodeAs<T>(ID))) 90*0b57cec5SDimitry Andric return S; 91*0b57cec5SDimitry Andric } 92*0b57cec5SDimitry Andric return nullptr; 93*0b57cec5SDimitry Andric } 94*0b57cec5SDimitry Andric 95*0b57cec5SDimitry Andric } // namespace 96*0b57cec5SDimitry Andric 97*0b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findMutation(const Expr *Exp) { 98*0b57cec5SDimitry Andric return findMutationMemoized(Exp, 99*0b57cec5SDimitry Andric {&ExprMutationAnalyzer::findDirectMutation, 100*0b57cec5SDimitry Andric &ExprMutationAnalyzer::findMemberMutation, 101*0b57cec5SDimitry Andric &ExprMutationAnalyzer::findArrayElementMutation, 102*0b57cec5SDimitry Andric &ExprMutationAnalyzer::findCastMutation, 103*0b57cec5SDimitry Andric &ExprMutationAnalyzer::findRangeLoopMutation, 104*0b57cec5SDimitry Andric &ExprMutationAnalyzer::findReferenceMutation, 105*0b57cec5SDimitry Andric &ExprMutationAnalyzer::findFunctionArgMutation}, 106*0b57cec5SDimitry Andric Results); 107*0b57cec5SDimitry Andric } 108*0b57cec5SDimitry Andric 109*0b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findMutation(const Decl *Dec) { 110*0b57cec5SDimitry Andric return tryEachDeclRef(Dec, &ExprMutationAnalyzer::findMutation); 111*0b57cec5SDimitry Andric } 112*0b57cec5SDimitry Andric 113*0b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findPointeeMutation(const Expr *Exp) { 114*0b57cec5SDimitry Andric return findMutationMemoized(Exp, {/*TODO*/}, PointeeResults); 115*0b57cec5SDimitry Andric } 116*0b57cec5SDimitry Andric 117*0b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findPointeeMutation(const Decl *Dec) { 118*0b57cec5SDimitry Andric return tryEachDeclRef(Dec, &ExprMutationAnalyzer::findPointeeMutation); 119*0b57cec5SDimitry Andric } 120*0b57cec5SDimitry Andric 121*0b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findMutationMemoized( 122*0b57cec5SDimitry Andric const Expr *Exp, llvm::ArrayRef<MutationFinder> Finders, 123*0b57cec5SDimitry Andric ResultMap &MemoizedResults) { 124*0b57cec5SDimitry Andric const auto Memoized = MemoizedResults.find(Exp); 125*0b57cec5SDimitry Andric if (Memoized != MemoizedResults.end()) 126*0b57cec5SDimitry Andric return Memoized->second; 127*0b57cec5SDimitry Andric 128*0b57cec5SDimitry Andric if (isUnevaluated(Exp)) 129*0b57cec5SDimitry Andric return MemoizedResults[Exp] = nullptr; 130*0b57cec5SDimitry Andric 131*0b57cec5SDimitry Andric for (const auto &Finder : Finders) { 132*0b57cec5SDimitry Andric if (const Stmt *S = (this->*Finder)(Exp)) 133*0b57cec5SDimitry Andric return MemoizedResults[Exp] = S; 134*0b57cec5SDimitry Andric } 135*0b57cec5SDimitry Andric 136*0b57cec5SDimitry Andric return MemoizedResults[Exp] = nullptr; 137*0b57cec5SDimitry Andric } 138*0b57cec5SDimitry Andric 139*0b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::tryEachDeclRef(const Decl *Dec, 140*0b57cec5SDimitry Andric MutationFinder Finder) { 141*0b57cec5SDimitry Andric const auto Refs = 142*0b57cec5SDimitry Andric match(findAll(declRefExpr(to(equalsNode(Dec))).bind(NodeID<Expr>::value)), 143*0b57cec5SDimitry Andric Stm, Context); 144*0b57cec5SDimitry Andric for (const auto &RefNodes : Refs) { 145*0b57cec5SDimitry Andric const auto *E = RefNodes.getNodeAs<Expr>(NodeID<Expr>::value); 146*0b57cec5SDimitry Andric if ((this->*Finder)(E)) 147*0b57cec5SDimitry Andric return E; 148*0b57cec5SDimitry Andric } 149*0b57cec5SDimitry Andric return nullptr; 150*0b57cec5SDimitry Andric } 151*0b57cec5SDimitry Andric 152*0b57cec5SDimitry Andric bool ExprMutationAnalyzer::isUnevaluated(const Expr *Exp) { 153*0b57cec5SDimitry Andric return selectFirst<Expr>( 154*0b57cec5SDimitry Andric NodeID<Expr>::value, 155*0b57cec5SDimitry Andric match( 156*0b57cec5SDimitry Andric findAll( 157*0b57cec5SDimitry Andric expr(equalsNode(Exp), 158*0b57cec5SDimitry Andric anyOf( 159*0b57cec5SDimitry Andric // `Exp` is part of the underlying expression of 160*0b57cec5SDimitry Andric // decltype/typeof if it has an ancestor of 161*0b57cec5SDimitry Andric // typeLoc. 162*0b57cec5SDimitry Andric hasAncestor(typeLoc(unless( 163*0b57cec5SDimitry Andric hasAncestor(unaryExprOrTypeTraitExpr())))), 164*0b57cec5SDimitry Andric hasAncestor(expr(anyOf( 165*0b57cec5SDimitry Andric // `UnaryExprOrTypeTraitExpr` is unevaluated 166*0b57cec5SDimitry Andric // unless it's sizeof on VLA. 167*0b57cec5SDimitry Andric unaryExprOrTypeTraitExpr(unless(sizeOfExpr( 168*0b57cec5SDimitry Andric hasArgumentOfType(variableArrayType())))), 169*0b57cec5SDimitry Andric // `CXXTypeidExpr` is unevaluated unless it's 170*0b57cec5SDimitry Andric // applied to an expression of glvalue of 171*0b57cec5SDimitry Andric // polymorphic class type. 172*0b57cec5SDimitry Andric cxxTypeidExpr( 173*0b57cec5SDimitry Andric unless(isPotentiallyEvaluated())), 174*0b57cec5SDimitry Andric // The controlling expression of 175*0b57cec5SDimitry Andric // `GenericSelectionExpr` is unevaluated. 176*0b57cec5SDimitry Andric genericSelectionExpr(hasControllingExpr( 177*0b57cec5SDimitry Andric hasDescendant(equalsNode(Exp)))), 178*0b57cec5SDimitry Andric cxxNoexceptExpr()))))) 179*0b57cec5SDimitry Andric .bind(NodeID<Expr>::value)), 180*0b57cec5SDimitry Andric Stm, Context)) != nullptr; 181*0b57cec5SDimitry Andric } 182*0b57cec5SDimitry Andric 183*0b57cec5SDimitry Andric const Stmt * 184*0b57cec5SDimitry Andric ExprMutationAnalyzer::findExprMutation(ArrayRef<BoundNodes> Matches) { 185*0b57cec5SDimitry Andric return tryEachMatch<Expr>(Matches, this, &ExprMutationAnalyzer::findMutation); 186*0b57cec5SDimitry Andric } 187*0b57cec5SDimitry Andric 188*0b57cec5SDimitry Andric const Stmt * 189*0b57cec5SDimitry Andric ExprMutationAnalyzer::findDeclMutation(ArrayRef<BoundNodes> Matches) { 190*0b57cec5SDimitry Andric return tryEachMatch<Decl>(Matches, this, &ExprMutationAnalyzer::findMutation); 191*0b57cec5SDimitry Andric } 192*0b57cec5SDimitry Andric 193*0b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findExprPointeeMutation( 194*0b57cec5SDimitry Andric ArrayRef<ast_matchers::BoundNodes> Matches) { 195*0b57cec5SDimitry Andric return tryEachMatch<Expr>(Matches, this, 196*0b57cec5SDimitry Andric &ExprMutationAnalyzer::findPointeeMutation); 197*0b57cec5SDimitry Andric } 198*0b57cec5SDimitry Andric 199*0b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findDeclPointeeMutation( 200*0b57cec5SDimitry Andric ArrayRef<ast_matchers::BoundNodes> Matches) { 201*0b57cec5SDimitry Andric return tryEachMatch<Decl>(Matches, this, 202*0b57cec5SDimitry Andric &ExprMutationAnalyzer::findPointeeMutation); 203*0b57cec5SDimitry Andric } 204*0b57cec5SDimitry Andric 205*0b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) { 206*0b57cec5SDimitry Andric // LHS of any assignment operators. 207*0b57cec5SDimitry Andric const auto AsAssignmentLhs = 208*0b57cec5SDimitry Andric binaryOperator(isAssignmentOperator(), 209*0b57cec5SDimitry Andric hasLHS(maybeEvalCommaExpr(equalsNode(Exp)))); 210*0b57cec5SDimitry Andric 211*0b57cec5SDimitry Andric // Operand of increment/decrement operators. 212*0b57cec5SDimitry Andric const auto AsIncDecOperand = 213*0b57cec5SDimitry Andric unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")), 214*0b57cec5SDimitry Andric hasUnaryOperand(maybeEvalCommaExpr(equalsNode(Exp)))); 215*0b57cec5SDimitry Andric 216*0b57cec5SDimitry Andric // Invoking non-const member function. 217*0b57cec5SDimitry Andric // A member function is assumed to be non-const when it is unresolved. 218*0b57cec5SDimitry Andric const auto NonConstMethod = cxxMethodDecl(unless(isConst())); 219*0b57cec5SDimitry Andric const auto AsNonConstThis = 220*0b57cec5SDimitry Andric expr(anyOf(cxxMemberCallExpr(callee(NonConstMethod), 221*0b57cec5SDimitry Andric on(maybeEvalCommaExpr(equalsNode(Exp)))), 222*0b57cec5SDimitry Andric cxxOperatorCallExpr(callee(NonConstMethod), 223*0b57cec5SDimitry Andric hasArgument(0, 224*0b57cec5SDimitry Andric maybeEvalCommaExpr(equalsNode(Exp)))), 225*0b57cec5SDimitry Andric callExpr(callee(expr(anyOf( 226*0b57cec5SDimitry Andric unresolvedMemberExpr( 227*0b57cec5SDimitry Andric hasObjectExpression(maybeEvalCommaExpr(equalsNode(Exp)))), 228*0b57cec5SDimitry Andric cxxDependentScopeMemberExpr( 229*0b57cec5SDimitry Andric hasObjectExpression(maybeEvalCommaExpr(equalsNode(Exp)))))))))); 230*0b57cec5SDimitry Andric 231*0b57cec5SDimitry Andric // Taking address of 'Exp'. 232*0b57cec5SDimitry Andric // We're assuming 'Exp' is mutated as soon as its address is taken, though in 233*0b57cec5SDimitry Andric // theory we can follow the pointer and see whether it escaped `Stm` or is 234*0b57cec5SDimitry Andric // dereferenced and then mutated. This is left for future improvements. 235*0b57cec5SDimitry Andric const auto AsAmpersandOperand = 236*0b57cec5SDimitry Andric unaryOperator(hasOperatorName("&"), 237*0b57cec5SDimitry Andric // A NoOp implicit cast is adding const. 238*0b57cec5SDimitry Andric unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))), 239*0b57cec5SDimitry Andric hasUnaryOperand(maybeEvalCommaExpr(equalsNode(Exp)))); 240*0b57cec5SDimitry Andric const auto AsPointerFromArrayDecay = 241*0b57cec5SDimitry Andric castExpr(hasCastKind(CK_ArrayToPointerDecay), 242*0b57cec5SDimitry Andric unless(hasParent(arraySubscriptExpr())), 243*0b57cec5SDimitry Andric has(maybeEvalCommaExpr(equalsNode(Exp)))); 244*0b57cec5SDimitry Andric // Treat calling `operator->()` of move-only classes as taking address. 245*0b57cec5SDimitry Andric // These are typically smart pointers with unique ownership so we treat 246*0b57cec5SDimitry Andric // mutation of pointee as mutation of the smart pointer itself. 247*0b57cec5SDimitry Andric const auto AsOperatorArrowThis = 248*0b57cec5SDimitry Andric cxxOperatorCallExpr(hasOverloadedOperatorName("->"), 249*0b57cec5SDimitry Andric callee(cxxMethodDecl(ofClass(isMoveOnly()), 250*0b57cec5SDimitry Andric returns(nonConstPointerType()))), 251*0b57cec5SDimitry Andric argumentCountIs(1), 252*0b57cec5SDimitry Andric hasArgument(0, maybeEvalCommaExpr(equalsNode(Exp)))); 253*0b57cec5SDimitry Andric 254*0b57cec5SDimitry Andric // Used as non-const-ref argument when calling a function. 255*0b57cec5SDimitry Andric // An argument is assumed to be non-const-ref when the function is unresolved. 256*0b57cec5SDimitry Andric // Instantiated template functions are not handled here but in 257*0b57cec5SDimitry Andric // findFunctionArgMutation which has additional smarts for handling forwarding 258*0b57cec5SDimitry Andric // references. 259*0b57cec5SDimitry Andric const auto NonConstRefParam = forEachArgumentWithParam( 260*0b57cec5SDimitry Andric maybeEvalCommaExpr(equalsNode(Exp)), 261*0b57cec5SDimitry Andric parmVarDecl(hasType(nonConstReferenceType()))); 262*0b57cec5SDimitry Andric const auto NotInstantiated = unless(hasDeclaration(isInstantiated())); 263*0b57cec5SDimitry Andric const auto AsNonConstRefArg = anyOf( 264*0b57cec5SDimitry Andric callExpr(NonConstRefParam, NotInstantiated), 265*0b57cec5SDimitry Andric cxxConstructExpr(NonConstRefParam, NotInstantiated), 266*0b57cec5SDimitry Andric callExpr(callee(expr(anyOf(unresolvedLookupExpr(), unresolvedMemberExpr(), 267*0b57cec5SDimitry Andric cxxDependentScopeMemberExpr(), 268*0b57cec5SDimitry Andric hasType(templateTypeParmType())))), 269*0b57cec5SDimitry Andric hasAnyArgument(maybeEvalCommaExpr(equalsNode(Exp)))), 270*0b57cec5SDimitry Andric cxxUnresolvedConstructExpr(hasAnyArgument(maybeEvalCommaExpr(equalsNode(Exp))))); 271*0b57cec5SDimitry Andric 272*0b57cec5SDimitry Andric // Captured by a lambda by reference. 273*0b57cec5SDimitry Andric // If we're initializing a capture with 'Exp' directly then we're initializing 274*0b57cec5SDimitry Andric // a reference capture. 275*0b57cec5SDimitry Andric // For value captures there will be an ImplicitCastExpr <LValueToRValue>. 276*0b57cec5SDimitry Andric const auto AsLambdaRefCaptureInit = lambdaExpr(hasCaptureInit(Exp)); 277*0b57cec5SDimitry Andric 278*0b57cec5SDimitry Andric // Returned as non-const-ref. 279*0b57cec5SDimitry Andric // If we're returning 'Exp' directly then it's returned as non-const-ref. 280*0b57cec5SDimitry Andric // For returning by value there will be an ImplicitCastExpr <LValueToRValue>. 281*0b57cec5SDimitry Andric // For returning by const-ref there will be an ImplicitCastExpr <NoOp> (for 282*0b57cec5SDimitry Andric // adding const.) 283*0b57cec5SDimitry Andric const auto AsNonConstRefReturn = returnStmt(hasReturnValue( 284*0b57cec5SDimitry Andric maybeEvalCommaExpr(equalsNode(Exp)))); 285*0b57cec5SDimitry Andric 286*0b57cec5SDimitry Andric const auto Matches = 287*0b57cec5SDimitry Andric match(findAll(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand, AsNonConstThis, 288*0b57cec5SDimitry Andric AsAmpersandOperand, AsPointerFromArrayDecay, 289*0b57cec5SDimitry Andric AsOperatorArrowThis, AsNonConstRefArg, 290*0b57cec5SDimitry Andric AsLambdaRefCaptureInit, AsNonConstRefReturn)) 291*0b57cec5SDimitry Andric .bind("stmt")), 292*0b57cec5SDimitry Andric Stm, Context); 293*0b57cec5SDimitry Andric return selectFirst<Stmt>("stmt", Matches); 294*0b57cec5SDimitry Andric } 295*0b57cec5SDimitry Andric 296*0b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findMemberMutation(const Expr *Exp) { 297*0b57cec5SDimitry Andric // Check whether any member of 'Exp' is mutated. 298*0b57cec5SDimitry Andric const auto MemberExprs = 299*0b57cec5SDimitry Andric match(findAll(expr(anyOf(memberExpr(hasObjectExpression(equalsNode(Exp))), 300*0b57cec5SDimitry Andric cxxDependentScopeMemberExpr( 301*0b57cec5SDimitry Andric hasObjectExpression(equalsNode(Exp))))) 302*0b57cec5SDimitry Andric .bind(NodeID<Expr>::value)), 303*0b57cec5SDimitry Andric Stm, Context); 304*0b57cec5SDimitry Andric return findExprMutation(MemberExprs); 305*0b57cec5SDimitry Andric } 306*0b57cec5SDimitry Andric 307*0b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findArrayElementMutation(const Expr *Exp) { 308*0b57cec5SDimitry Andric // Check whether any element of an array is mutated. 309*0b57cec5SDimitry Andric const auto SubscriptExprs = match( 310*0b57cec5SDimitry Andric findAll(arraySubscriptExpr(hasBase(ignoringImpCasts(equalsNode(Exp)))) 311*0b57cec5SDimitry Andric .bind(NodeID<Expr>::value)), 312*0b57cec5SDimitry Andric Stm, Context); 313*0b57cec5SDimitry Andric return findExprMutation(SubscriptExprs); 314*0b57cec5SDimitry Andric } 315*0b57cec5SDimitry Andric 316*0b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findCastMutation(const Expr *Exp) { 317*0b57cec5SDimitry Andric // If 'Exp' is casted to any non-const reference type, check the castExpr. 318*0b57cec5SDimitry Andric const auto Casts = 319*0b57cec5SDimitry Andric match(findAll(castExpr(hasSourceExpression(equalsNode(Exp)), 320*0b57cec5SDimitry Andric anyOf(explicitCastExpr(hasDestinationType( 321*0b57cec5SDimitry Andric nonConstReferenceType())), 322*0b57cec5SDimitry Andric implicitCastExpr(hasImplicitDestinationType( 323*0b57cec5SDimitry Andric nonConstReferenceType())))) 324*0b57cec5SDimitry Andric .bind(NodeID<Expr>::value)), 325*0b57cec5SDimitry Andric Stm, Context); 326*0b57cec5SDimitry Andric if (const Stmt *S = findExprMutation(Casts)) 327*0b57cec5SDimitry Andric return S; 328*0b57cec5SDimitry Andric // Treat std::{move,forward} as cast. 329*0b57cec5SDimitry Andric const auto Calls = 330*0b57cec5SDimitry Andric match(findAll(callExpr(callee(namedDecl( 331*0b57cec5SDimitry Andric hasAnyName("::std::move", "::std::forward"))), 332*0b57cec5SDimitry Andric hasArgument(0, equalsNode(Exp))) 333*0b57cec5SDimitry Andric .bind("expr")), 334*0b57cec5SDimitry Andric Stm, Context); 335*0b57cec5SDimitry Andric return findExprMutation(Calls); 336*0b57cec5SDimitry Andric } 337*0b57cec5SDimitry Andric 338*0b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findRangeLoopMutation(const Expr *Exp) { 339*0b57cec5SDimitry Andric // If range for looping over 'Exp' with a non-const reference loop variable, 340*0b57cec5SDimitry Andric // check all declRefExpr of the loop variable. 341*0b57cec5SDimitry Andric const auto LoopVars = 342*0b57cec5SDimitry Andric match(findAll(cxxForRangeStmt( 343*0b57cec5SDimitry Andric hasLoopVariable(varDecl(hasType(nonConstReferenceType())) 344*0b57cec5SDimitry Andric .bind(NodeID<Decl>::value)), 345*0b57cec5SDimitry Andric hasRangeInit(equalsNode(Exp)))), 346*0b57cec5SDimitry Andric Stm, Context); 347*0b57cec5SDimitry Andric return findDeclMutation(LoopVars); 348*0b57cec5SDimitry Andric } 349*0b57cec5SDimitry Andric 350*0b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findReferenceMutation(const Expr *Exp) { 351*0b57cec5SDimitry Andric // Follow non-const reference returned by `operator*()` of move-only classes. 352*0b57cec5SDimitry Andric // These are typically smart pointers with unique ownership so we treat 353*0b57cec5SDimitry Andric // mutation of pointee as mutation of the smart pointer itself. 354*0b57cec5SDimitry Andric const auto Ref = 355*0b57cec5SDimitry Andric match(findAll(cxxOperatorCallExpr( 356*0b57cec5SDimitry Andric hasOverloadedOperatorName("*"), 357*0b57cec5SDimitry Andric callee(cxxMethodDecl(ofClass(isMoveOnly()), 358*0b57cec5SDimitry Andric returns(nonConstReferenceType()))), 359*0b57cec5SDimitry Andric argumentCountIs(1), hasArgument(0, equalsNode(Exp))) 360*0b57cec5SDimitry Andric .bind(NodeID<Expr>::value)), 361*0b57cec5SDimitry Andric Stm, Context); 362*0b57cec5SDimitry Andric if (const Stmt *S = findExprMutation(Ref)) 363*0b57cec5SDimitry Andric return S; 364*0b57cec5SDimitry Andric 365*0b57cec5SDimitry Andric // If 'Exp' is bound to a non-const reference, check all declRefExpr to that. 366*0b57cec5SDimitry Andric const auto Refs = match( 367*0b57cec5SDimitry Andric stmt(forEachDescendant( 368*0b57cec5SDimitry Andric varDecl( 369*0b57cec5SDimitry Andric hasType(nonConstReferenceType()), 370*0b57cec5SDimitry Andric hasInitializer(anyOf(equalsNode(Exp), 371*0b57cec5SDimitry Andric conditionalOperator(anyOf( 372*0b57cec5SDimitry Andric hasTrueExpression(equalsNode(Exp)), 373*0b57cec5SDimitry Andric hasFalseExpression(equalsNode(Exp)))))), 374*0b57cec5SDimitry Andric hasParent(declStmt().bind("stmt")), 375*0b57cec5SDimitry Andric // Don't follow the reference in range statement, we've handled 376*0b57cec5SDimitry Andric // that separately. 377*0b57cec5SDimitry Andric unless(hasParent(declStmt(hasParent( 378*0b57cec5SDimitry Andric cxxForRangeStmt(hasRangeStmt(equalsBoundNode("stmt")))))))) 379*0b57cec5SDimitry Andric .bind(NodeID<Decl>::value))), 380*0b57cec5SDimitry Andric Stm, Context); 381*0b57cec5SDimitry Andric return findDeclMutation(Refs); 382*0b57cec5SDimitry Andric } 383*0b57cec5SDimitry Andric 384*0b57cec5SDimitry Andric const Stmt *ExprMutationAnalyzer::findFunctionArgMutation(const Expr *Exp) { 385*0b57cec5SDimitry Andric const auto NonConstRefParam = forEachArgumentWithParam( 386*0b57cec5SDimitry Andric equalsNode(Exp), 387*0b57cec5SDimitry Andric parmVarDecl(hasType(nonConstReferenceType())).bind("parm")); 388*0b57cec5SDimitry Andric const auto IsInstantiated = hasDeclaration(isInstantiated()); 389*0b57cec5SDimitry Andric const auto FuncDecl = hasDeclaration(functionDecl().bind("func")); 390*0b57cec5SDimitry Andric const auto Matches = match( 391*0b57cec5SDimitry Andric findAll(expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl, 392*0b57cec5SDimitry Andric unless(callee(namedDecl(hasAnyName( 393*0b57cec5SDimitry Andric "::std::move", "::std::forward"))))), 394*0b57cec5SDimitry Andric cxxConstructExpr(NonConstRefParam, IsInstantiated, 395*0b57cec5SDimitry Andric FuncDecl))) 396*0b57cec5SDimitry Andric .bind(NodeID<Expr>::value)), 397*0b57cec5SDimitry Andric Stm, Context); 398*0b57cec5SDimitry Andric for (const auto &Nodes : Matches) { 399*0b57cec5SDimitry Andric const auto *Exp = Nodes.getNodeAs<Expr>(NodeID<Expr>::value); 400*0b57cec5SDimitry Andric const auto *Func = Nodes.getNodeAs<FunctionDecl>("func"); 401*0b57cec5SDimitry Andric if (!Func->getBody() || !Func->getPrimaryTemplate()) 402*0b57cec5SDimitry Andric return Exp; 403*0b57cec5SDimitry Andric 404*0b57cec5SDimitry Andric const auto *Parm = Nodes.getNodeAs<ParmVarDecl>("parm"); 405*0b57cec5SDimitry Andric const ArrayRef<ParmVarDecl *> AllParams = 406*0b57cec5SDimitry Andric Func->getPrimaryTemplate()->getTemplatedDecl()->parameters(); 407*0b57cec5SDimitry Andric QualType ParmType = 408*0b57cec5SDimitry Andric AllParams[std::min<size_t>(Parm->getFunctionScopeIndex(), 409*0b57cec5SDimitry Andric AllParams.size() - 1)] 410*0b57cec5SDimitry Andric ->getType(); 411*0b57cec5SDimitry Andric if (const auto *T = ParmType->getAs<PackExpansionType>()) 412*0b57cec5SDimitry Andric ParmType = T->getPattern(); 413*0b57cec5SDimitry Andric 414*0b57cec5SDimitry Andric // If param type is forwarding reference, follow into the function 415*0b57cec5SDimitry Andric // definition and see whether the param is mutated inside. 416*0b57cec5SDimitry Andric if (const auto *RefType = ParmType->getAs<RValueReferenceType>()) { 417*0b57cec5SDimitry Andric if (!RefType->getPointeeType().getQualifiers() && 418*0b57cec5SDimitry Andric RefType->getPointeeType()->getAs<TemplateTypeParmType>()) { 419*0b57cec5SDimitry Andric std::unique_ptr<FunctionParmMutationAnalyzer> &Analyzer = 420*0b57cec5SDimitry Andric FuncParmAnalyzer[Func]; 421*0b57cec5SDimitry Andric if (!Analyzer) 422*0b57cec5SDimitry Andric Analyzer.reset(new FunctionParmMutationAnalyzer(*Func, Context)); 423*0b57cec5SDimitry Andric if (Analyzer->findMutation(Parm)) 424*0b57cec5SDimitry Andric return Exp; 425*0b57cec5SDimitry Andric continue; 426*0b57cec5SDimitry Andric } 427*0b57cec5SDimitry Andric } 428*0b57cec5SDimitry Andric // Not forwarding reference. 429*0b57cec5SDimitry Andric return Exp; 430*0b57cec5SDimitry Andric } 431*0b57cec5SDimitry Andric return nullptr; 432*0b57cec5SDimitry Andric } 433*0b57cec5SDimitry Andric 434*0b57cec5SDimitry Andric FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer( 435*0b57cec5SDimitry Andric const FunctionDecl &Func, ASTContext &Context) 436*0b57cec5SDimitry Andric : BodyAnalyzer(*Func.getBody(), Context) { 437*0b57cec5SDimitry Andric if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(&Func)) { 438*0b57cec5SDimitry Andric // CXXCtorInitializer might also mutate Param but they're not part of 439*0b57cec5SDimitry Andric // function body, check them eagerly here since they're typically trivial. 440*0b57cec5SDimitry Andric for (const CXXCtorInitializer *Init : Ctor->inits()) { 441*0b57cec5SDimitry Andric ExprMutationAnalyzer InitAnalyzer(*Init->getInit(), Context); 442*0b57cec5SDimitry Andric for (const ParmVarDecl *Parm : Ctor->parameters()) { 443*0b57cec5SDimitry Andric if (Results.find(Parm) != Results.end()) 444*0b57cec5SDimitry Andric continue; 445*0b57cec5SDimitry Andric if (const Stmt *S = InitAnalyzer.findMutation(Parm)) 446*0b57cec5SDimitry Andric Results[Parm] = S; 447*0b57cec5SDimitry Andric } 448*0b57cec5SDimitry Andric } 449*0b57cec5SDimitry Andric } 450*0b57cec5SDimitry Andric } 451*0b57cec5SDimitry Andric 452*0b57cec5SDimitry Andric const Stmt * 453*0b57cec5SDimitry Andric FunctionParmMutationAnalyzer::findMutation(const ParmVarDecl *Parm) { 454*0b57cec5SDimitry Andric const auto Memoized = Results.find(Parm); 455*0b57cec5SDimitry Andric if (Memoized != Results.end()) 456*0b57cec5SDimitry Andric return Memoized->second; 457*0b57cec5SDimitry Andric 458*0b57cec5SDimitry Andric if (const Stmt *S = BodyAnalyzer.findMutation(Parm)) 459*0b57cec5SDimitry Andric return Results[Parm] = S; 460*0b57cec5SDimitry Andric 461*0b57cec5SDimitry Andric return Results[Parm] = nullptr; 462*0b57cec5SDimitry Andric } 463*0b57cec5SDimitry Andric 464*0b57cec5SDimitry Andric } // namespace clang 465