1e9192f83SShuai Wang //===---------- ExprMutationAnalyzer.cpp ----------------------------------===// 2e9192f83SShuai Wang // 32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information. 52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6e9192f83SShuai Wang // 7e9192f83SShuai Wang //===----------------------------------------------------------------------===// 8e9192f83SShuai Wang #include "clang/Analysis/Analyses/ExprMutationAnalyzer.h" 9e517e5cfSJonas Toth #include "clang/AST/Expr.h" 10e517e5cfSJonas Toth #include "clang/AST/OperationKinds.h" 11*53ea5ffcSCongcong Cai #include "clang/AST/Stmt.h" 12e9192f83SShuai Wang #include "clang/ASTMatchers/ASTMatchFinder.h" 13e517e5cfSJonas Toth #include "clang/ASTMatchers/ASTMatchers.h" 14*53ea5ffcSCongcong Cai #include "clang/ASTMatchers/ASTMatchersMacros.h" 15e9192f83SShuai Wang #include "llvm/ADT/STLExtras.h" 16e9192f83SShuai Wang 17e9192f83SShuai Wang namespace clang { 18e9192f83SShuai Wang using namespace ast_matchers; 19e9192f83SShuai Wang 206eb372e4SPiotr Zegar // Check if result of Source expression could be a Target expression. 216eb372e4SPiotr Zegar // Checks: 226eb372e4SPiotr Zegar // - Implicit Casts 236eb372e4SPiotr Zegar // - Binary Operators 246eb372e4SPiotr Zegar // - ConditionalOperator 256eb372e4SPiotr Zegar // - BinaryConditionalOperator 266eb372e4SPiotr Zegar static bool canExprResolveTo(const Expr *Source, const Expr *Target) { 276eb372e4SPiotr Zegar const auto IgnoreDerivedToBase = [](const Expr *E, auto Matcher) { 286eb372e4SPiotr Zegar if (Matcher(E)) 296eb372e4SPiotr Zegar return true; 306eb372e4SPiotr Zegar if (const auto *Cast = dyn_cast<ImplicitCastExpr>(E)) { 316eb372e4SPiotr Zegar if ((Cast->getCastKind() == CK_DerivedToBase || 326eb372e4SPiotr Zegar Cast->getCastKind() == CK_UncheckedDerivedToBase) && 336eb372e4SPiotr Zegar Matcher(Cast->getSubExpr())) 346eb372e4SPiotr Zegar return true; 356eb372e4SPiotr Zegar } 366eb372e4SPiotr Zegar return false; 376eb372e4SPiotr Zegar }; 386eb372e4SPiotr Zegar 396eb372e4SPiotr Zegar const auto EvalCommaExpr = [](const Expr *E, auto Matcher) { 406eb372e4SPiotr Zegar const Expr *Result = E; 416eb372e4SPiotr Zegar while (const auto *BOComma = 426eb372e4SPiotr Zegar dyn_cast_or_null<BinaryOperator>(Result->IgnoreParens())) { 436eb372e4SPiotr Zegar if (!BOComma->isCommaOp()) 446eb372e4SPiotr Zegar break; 456eb372e4SPiotr Zegar Result = BOComma->getRHS(); 466eb372e4SPiotr Zegar } 476eb372e4SPiotr Zegar 486eb372e4SPiotr Zegar return Result != E && Matcher(Result); 496eb372e4SPiotr Zegar }; 506eb372e4SPiotr Zegar 516eb372e4SPiotr Zegar // The 'ConditionalOperatorM' matches on `<anything> ? <expr> : <expr>`. 526eb372e4SPiotr Zegar // This matching must be recursive because `<expr>` can be anything resolving 536eb372e4SPiotr Zegar // to the `InnerMatcher`, for example another conditional operator. 546eb372e4SPiotr Zegar // The edge-case `BaseClass &b = <cond> ? DerivedVar1 : DerivedVar2;` 556eb372e4SPiotr Zegar // is handled, too. The implicit cast happens outside of the conditional. 566eb372e4SPiotr Zegar // This is matched by `IgnoreDerivedToBase(canResolveToExpr(InnerMatcher))` 576eb372e4SPiotr Zegar // below. 586eb372e4SPiotr Zegar const auto ConditionalOperatorM = [Target](const Expr *E) { 59f98c9a9bSCongcong Cai if (const auto *CO = dyn_cast<AbstractConditionalOperator>(E)) { 60f98c9a9bSCongcong Cai const auto *TE = CO->getTrueExpr()->IgnoreParens(); 61f98c9a9bSCongcong Cai if (TE && canExprResolveTo(TE, Target)) 626eb372e4SPiotr Zegar return true; 63f98c9a9bSCongcong Cai const auto *FE = CO->getFalseExpr()->IgnoreParens(); 64f98c9a9bSCongcong Cai if (FE && canExprResolveTo(FE, Target)) 656eb372e4SPiotr Zegar return true; 666eb372e4SPiotr Zegar } 676eb372e4SPiotr Zegar return false; 686eb372e4SPiotr Zegar }; 696eb372e4SPiotr Zegar 706eb372e4SPiotr Zegar const Expr *SourceExprP = Source->IgnoreParens(); 716eb372e4SPiotr Zegar return IgnoreDerivedToBase(SourceExprP, 726eb372e4SPiotr Zegar [&](const Expr *E) { 73f98c9a9bSCongcong Cai return E == Target || ConditionalOperatorM(E); 746eb372e4SPiotr Zegar }) || 756eb372e4SPiotr Zegar EvalCommaExpr(SourceExprP, [&](const Expr *E) { 766eb372e4SPiotr Zegar return IgnoreDerivedToBase( 776eb372e4SPiotr Zegar E->IgnoreParens(), [&](const Expr *EE) { return EE == Target; }); 786eb372e4SPiotr Zegar }); 796eb372e4SPiotr Zegar } 806eb372e4SPiotr Zegar 81e9192f83SShuai Wang namespace { 82e9192f83SShuai Wang 83*53ea5ffcSCongcong Cai AST_MATCHER(Type, isDependentType) { return Node.isDependentType(); } 84*53ea5ffcSCongcong Cai 85e9192f83SShuai Wang AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) { 86e9192f83SShuai Wang return llvm::is_contained(Node.capture_inits(), E); 87e9192f83SShuai Wang } 88e9192f83SShuai Wang 89e9192f83SShuai Wang AST_MATCHER_P(CXXForRangeStmt, hasRangeStmt, 90e9192f83SShuai Wang ast_matchers::internal::Matcher<DeclStmt>, InnerMatcher) { 91e9192f83SShuai Wang const DeclStmt *const Range = Node.getRangeStmt(); 92e9192f83SShuai Wang return InnerMatcher.matches(*Range, Finder, Builder); 93e9192f83SShuai Wang } 94e9192f83SShuai Wang 956eb372e4SPiotr Zegar AST_MATCHER_P(Stmt, canResolveToExpr, const Stmt *, Inner) { 96ca81abcfSusama hameed auto *Exp = dyn_cast<Expr>(&Node); 976eb372e4SPiotr Zegar if (!Exp) 986eb372e4SPiotr Zegar return true; 996eb372e4SPiotr Zegar auto *Target = dyn_cast<Expr>(Inner); 1006eb372e4SPiotr Zegar if (!Target) 1016eb372e4SPiotr Zegar return false; 1026eb372e4SPiotr Zegar return canExprResolveTo(Exp, Target); 103e517e5cfSJonas Toth } 104e517e5cfSJonas Toth 105*53ea5ffcSCongcong Cai // use class member to store data can reduce stack usage to avoid stack overflow 106*53ea5ffcSCongcong Cai // when recursive call. 107*53ea5ffcSCongcong Cai class ExprPointeeResolve { 108*53ea5ffcSCongcong Cai const Expr *T; 109*53ea5ffcSCongcong Cai 110*53ea5ffcSCongcong Cai bool resolveExpr(const Expr *E) { 111*53ea5ffcSCongcong Cai if (E == nullptr) 112*53ea5ffcSCongcong Cai return false; 113*53ea5ffcSCongcong Cai if (E == T) 114*53ea5ffcSCongcong Cai return true; 115*53ea5ffcSCongcong Cai 116*53ea5ffcSCongcong Cai if (const auto *BO = dyn_cast<BinaryOperator>(E)) { 117*53ea5ffcSCongcong Cai if (BO->isAdditiveOp()) 118*53ea5ffcSCongcong Cai return (resolveExpr(BO->getLHS()) || resolveExpr(BO->getRHS())); 119*53ea5ffcSCongcong Cai if (BO->isCommaOp()) 120*53ea5ffcSCongcong Cai return resolveExpr(BO->getRHS()); 121*53ea5ffcSCongcong Cai return false; 122*53ea5ffcSCongcong Cai } 123*53ea5ffcSCongcong Cai 124*53ea5ffcSCongcong Cai if (const auto *PE = dyn_cast<ParenExpr>(E)) 125*53ea5ffcSCongcong Cai return resolveExpr(PE->getSubExpr()); 126*53ea5ffcSCongcong Cai 127*53ea5ffcSCongcong Cai if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E)) { 128*53ea5ffcSCongcong Cai // only implicit cast needs to be treated as resolvable. 129*53ea5ffcSCongcong Cai // explicit cast will be checked in `findPointeeToNonConst` 130*53ea5ffcSCongcong Cai const CastKind kind = ICE->getCastKind(); 131*53ea5ffcSCongcong Cai if (kind == CK_LValueToRValue || kind == CK_DerivedToBase || 132*53ea5ffcSCongcong Cai kind == CK_UncheckedDerivedToBase) 133*53ea5ffcSCongcong Cai return resolveExpr(ICE->getSubExpr()); 134*53ea5ffcSCongcong Cai return false; 135*53ea5ffcSCongcong Cai } 136*53ea5ffcSCongcong Cai 137*53ea5ffcSCongcong Cai if (const auto *ACE = dyn_cast<AbstractConditionalOperator>(E)) 138*53ea5ffcSCongcong Cai return resolve(ACE->getTrueExpr()) || resolve(ACE->getFalseExpr()); 139*53ea5ffcSCongcong Cai 140*53ea5ffcSCongcong Cai return false; 141*53ea5ffcSCongcong Cai } 142*53ea5ffcSCongcong Cai 143*53ea5ffcSCongcong Cai public: 144*53ea5ffcSCongcong Cai ExprPointeeResolve(const Expr *T) : T(T) {} 145*53ea5ffcSCongcong Cai bool resolve(const Expr *S) { return resolveExpr(S); } 146*53ea5ffcSCongcong Cai }; 147*53ea5ffcSCongcong Cai 148*53ea5ffcSCongcong Cai AST_MATCHER_P(Stmt, canResolveToExprPointee, const Stmt *, T) { 149*53ea5ffcSCongcong Cai auto *Exp = dyn_cast<Expr>(&Node); 150*53ea5ffcSCongcong Cai if (!Exp) 151*53ea5ffcSCongcong Cai return true; 152*53ea5ffcSCongcong Cai auto *Target = dyn_cast<Expr>(T); 153*53ea5ffcSCongcong Cai if (!Target) 154*53ea5ffcSCongcong Cai return false; 155*53ea5ffcSCongcong Cai return ExprPointeeResolve{Target}.resolve(Exp); 156*53ea5ffcSCongcong Cai } 157*53ea5ffcSCongcong Cai 158e517e5cfSJonas Toth // Similar to 'hasAnyArgument', but does not work because 'InitListExpr' does 159e517e5cfSJonas Toth // not have the 'arguments()' method. 160e517e5cfSJonas Toth AST_MATCHER_P(InitListExpr, hasAnyInit, ast_matchers::internal::Matcher<Expr>, 161e517e5cfSJonas Toth InnerMatcher) { 162e517e5cfSJonas Toth for (const Expr *Arg : Node.inits()) { 163eb7dea8bSCongcong Cai if (Arg == nullptr) 164eb7dea8bSCongcong Cai continue; 165e517e5cfSJonas Toth ast_matchers::internal::BoundNodesTreeBuilder Result(*Builder); 166e517e5cfSJonas Toth if (InnerMatcher.matches(*Arg, Finder, &Result)) { 167e517e5cfSJonas Toth *Builder = std::move(Result); 168e517e5cfSJonas Toth return true; 169e517e5cfSJonas Toth } 170e517e5cfSJonas Toth } 171e517e5cfSJonas Toth return false; 172e517e5cfSJonas Toth } 173e517e5cfSJonas Toth 174e9192f83SShuai Wang const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXTypeidExpr> 175e9192f83SShuai Wang cxxTypeidExpr; 176e9192f83SShuai Wang 177e9192f83SShuai Wang AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) { 178e9192f83SShuai Wang return Node.isPotentiallyEvaluated(); 179e9192f83SShuai Wang } 180e9192f83SShuai Wang 181feb7b191SCongcong Cai AST_MATCHER(CXXMemberCallExpr, isConstCallee) { 182feb7b191SCongcong Cai const Decl *CalleeDecl = Node.getCalleeDecl(); 183feb7b191SCongcong Cai const auto *VD = dyn_cast_or_null<ValueDecl>(CalleeDecl); 184feb7b191SCongcong Cai if (!VD) 185feb7b191SCongcong Cai return false; 186feb7b191SCongcong Cai const QualType T = VD->getType().getCanonicalType(); 187feb7b191SCongcong Cai const auto *MPT = dyn_cast<MemberPointerType>(T); 188feb7b191SCongcong Cai const auto *FPT = MPT ? cast<FunctionProtoType>(MPT->getPointeeType()) 189feb7b191SCongcong Cai : dyn_cast<FunctionProtoType>(T); 190feb7b191SCongcong Cai if (!FPT) 191feb7b191SCongcong Cai return false; 192feb7b191SCongcong Cai return FPT->isConst(); 193feb7b191SCongcong Cai } 194feb7b191SCongcong Cai 195e9192f83SShuai Wang AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr, 196e9192f83SShuai Wang ast_matchers::internal::Matcher<Expr>, InnerMatcher) { 19712728e14SAaron Ballman if (Node.isTypePredicate()) 19812728e14SAaron Ballman return false; 199e9192f83SShuai Wang return InnerMatcher.matches(*Node.getControllingExpr(), Finder, Builder); 200e9192f83SShuai Wang } 201e9192f83SShuai Wang 2026eb372e4SPiotr Zegar template <typename T> 2036eb372e4SPiotr Zegar ast_matchers::internal::Matcher<T> 2046eb372e4SPiotr Zegar findFirst(const ast_matchers::internal::Matcher<T> &Matcher) { 2056eb372e4SPiotr Zegar return anyOf(Matcher, hasDescendant(Matcher)); 2066eb372e4SPiotr Zegar } 2076eb372e4SPiotr Zegar 208e9192f83SShuai Wang const auto nonConstReferenceType = [] { 209e9192f83SShuai Wang return hasUnqualifiedDesugaredType( 210e9192f83SShuai Wang referenceType(pointee(unless(isConstQualified())))); 211e9192f83SShuai Wang }; 212e9192f83SShuai Wang 213e9192f83SShuai Wang const auto nonConstPointerType = [] { 214e9192f83SShuai Wang return hasUnqualifiedDesugaredType( 215e9192f83SShuai Wang pointerType(pointee(unless(isConstQualified())))); 216e9192f83SShuai Wang }; 217e9192f83SShuai Wang 218e9192f83SShuai Wang const auto isMoveOnly = [] { 219e9192f83SShuai Wang return cxxRecordDecl( 220e9192f83SShuai Wang hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted()))), 221e9192f83SShuai Wang hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted()))), 222e9192f83SShuai Wang unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(), 223e9192f83SShuai Wang unless(isDeleted()))), 224e9192f83SShuai Wang hasMethod(cxxMethodDecl(isCopyAssignmentOperator(), 225e9192f83SShuai Wang unless(isDeleted())))))); 226e9192f83SShuai Wang }; 227e9192f83SShuai Wang 228aaaa310dSShuai Wang template <class T> struct NodeID; 2290c3df70fSBenjamin Kramer template <> struct NodeID<Expr> { static constexpr StringRef value = "expr"; }; 2300c3df70fSBenjamin Kramer template <> struct NodeID<Decl> { static constexpr StringRef value = "decl"; }; 231c0c6a127SBenjamin Kramer constexpr StringRef NodeID<Expr>::value; 232c0c6a127SBenjamin Kramer constexpr StringRef NodeID<Decl>::value; 233aaaa310dSShuai Wang 234f40f4fceSCongcong Cai template <class T, 235f40f4fceSCongcong Cai class F = const Stmt *(ExprMutationAnalyzer::Analyzer::*)(const T *)> 236aaaa310dSShuai Wang const Stmt *tryEachMatch(ArrayRef<ast_matchers::BoundNodes> Matches, 237f40f4fceSCongcong Cai ExprMutationAnalyzer::Analyzer *Analyzer, F Finder) { 238aaaa310dSShuai Wang const StringRef ID = NodeID<T>::value; 239aaaa310dSShuai Wang for (const auto &Nodes : Matches) { 240aaaa310dSShuai Wang if (const Stmt *S = (Analyzer->*Finder)(Nodes.getNodeAs<T>(ID))) 241aaaa310dSShuai Wang return S; 242aaaa310dSShuai Wang } 243aaaa310dSShuai Wang return nullptr; 244aaaa310dSShuai Wang } 245aaaa310dSShuai Wang 246e9192f83SShuai Wang } // namespace 247e9192f83SShuai Wang 248f40f4fceSCongcong Cai const Stmt *ExprMutationAnalyzer::Analyzer::findMutation(const Expr *Exp) { 249f40f4fceSCongcong Cai return findMutationMemoized( 250f40f4fceSCongcong Cai Exp, 251f40f4fceSCongcong Cai {&ExprMutationAnalyzer::Analyzer::findDirectMutation, 252f40f4fceSCongcong Cai &ExprMutationAnalyzer::Analyzer::findMemberMutation, 253f40f4fceSCongcong Cai &ExprMutationAnalyzer::Analyzer::findArrayElementMutation, 254f40f4fceSCongcong Cai &ExprMutationAnalyzer::Analyzer::findCastMutation, 255f40f4fceSCongcong Cai &ExprMutationAnalyzer::Analyzer::findRangeLoopMutation, 256f40f4fceSCongcong Cai &ExprMutationAnalyzer::Analyzer::findReferenceMutation, 257f40f4fceSCongcong Cai &ExprMutationAnalyzer::Analyzer::findFunctionArgMutation}, 258f40f4fceSCongcong Cai Memorized.Results); 259e9192f83SShuai Wang } 260e9192f83SShuai Wang 261f40f4fceSCongcong Cai const Stmt *ExprMutationAnalyzer::Analyzer::findMutation(const Decl *Dec) { 262f40f4fceSCongcong Cai return tryEachDeclRef(Dec, &ExprMutationAnalyzer::Analyzer::findMutation); 263aaaa310dSShuai Wang } 264aaaa310dSShuai Wang 265f40f4fceSCongcong Cai const Stmt * 266f40f4fceSCongcong Cai ExprMutationAnalyzer::Analyzer::findPointeeMutation(const Expr *Exp) { 267*53ea5ffcSCongcong Cai return findMutationMemoized( 268*53ea5ffcSCongcong Cai Exp, 269*53ea5ffcSCongcong Cai { 270*53ea5ffcSCongcong Cai &ExprMutationAnalyzer::Analyzer::findPointeeValueMutation, 271*53ea5ffcSCongcong Cai &ExprMutationAnalyzer::Analyzer::findPointeeMemberMutation, 272*53ea5ffcSCongcong Cai &ExprMutationAnalyzer::Analyzer::findPointeeToNonConst, 273*53ea5ffcSCongcong Cai }, 274*53ea5ffcSCongcong Cai Memorized.PointeeResults); 275aaaa310dSShuai Wang } 276aaaa310dSShuai Wang 277f40f4fceSCongcong Cai const Stmt * 278f40f4fceSCongcong Cai ExprMutationAnalyzer::Analyzer::findPointeeMutation(const Decl *Dec) { 279f40f4fceSCongcong Cai return tryEachDeclRef(Dec, 280f40f4fceSCongcong Cai &ExprMutationAnalyzer::Analyzer::findPointeeMutation); 281aaaa310dSShuai Wang } 282aaaa310dSShuai Wang 283f40f4fceSCongcong Cai const Stmt *ExprMutationAnalyzer::Analyzer::findMutationMemoized( 284aaaa310dSShuai Wang const Expr *Exp, llvm::ArrayRef<MutationFinder> Finders, 285f40f4fceSCongcong Cai Memoized::ResultMap &MemoizedResults) { 28668a48ec9SCongcong Cai // Assume Exp is not mutated before analyzing Exp. 28728733ed6SKazu Hirata auto [Memoized, Inserted] = MemoizedResults.try_emplace(Exp); 28828733ed6SKazu Hirata if (!Inserted) 289aaaa310dSShuai Wang return Memoized->second; 290aaaa310dSShuai Wang 291ae20dbddSCongcong Cai if (ExprMutationAnalyzer::isUnevaluated(Exp, Context)) 2926e31714dSCongcong Cai return nullptr; 293aaaa310dSShuai Wang 294aaaa310dSShuai Wang for (const auto &Finder : Finders) { 295aaaa310dSShuai Wang if (const Stmt *S = (this->*Finder)(Exp)) 296aaaa310dSShuai Wang return MemoizedResults[Exp] = S; 297aaaa310dSShuai Wang } 298aaaa310dSShuai Wang 2996e31714dSCongcong Cai return nullptr; 300aaaa310dSShuai Wang } 301aaaa310dSShuai Wang 302f40f4fceSCongcong Cai const Stmt * 303f40f4fceSCongcong Cai ExprMutationAnalyzer::Analyzer::tryEachDeclRef(const Decl *Dec, 304aaaa310dSShuai Wang MutationFinder Finder) { 3058fd32b96SClement Courbet const auto Refs = match( 3068fd32b96SClement Courbet findAll( 3078fd32b96SClement Courbet declRefExpr(to( 3088fd32b96SClement Courbet // `Dec` or a binding if `Dec` is a decomposition. 3098fd32b96SClement Courbet anyOf(equalsNode(Dec), 3108fd32b96SClement Courbet bindingDecl(forDecomposition(equalsNode(Dec)))) 3118fd32b96SClement Courbet // 3128fd32b96SClement Courbet )) 3138fd32b96SClement Courbet .bind(NodeID<Expr>::value)), 314aaaa310dSShuai Wang Stm, Context); 315aaaa310dSShuai Wang for (const auto &RefNodes : Refs) { 316aaaa310dSShuai Wang const auto *E = RefNodes.getNodeAs<Expr>(NodeID<Expr>::value); 317aaaa310dSShuai Wang if ((this->*Finder)(E)) 318aaaa310dSShuai Wang return E; 319aaaa310dSShuai Wang } 320aaaa310dSShuai Wang return nullptr; 321e9192f83SShuai Wang } 322e9192f83SShuai Wang 323ae20dbddSCongcong Cai bool ExprMutationAnalyzer::isUnevaluated(const Stmt *Stm, ASTContext &Context) { 324ae20dbddSCongcong Cai return !match(stmt(anyOf( 325ca81abcfSusama hameed // `Exp` is part of the underlying expression of 326ca81abcfSusama hameed // decltype/typeof if it has an ancestor of 327ca81abcfSusama hameed // typeLoc. 328ae20dbddSCongcong Cai hasAncestor(typeLoc( 329ae20dbddSCongcong Cai unless(hasAncestor(unaryExprOrTypeTraitExpr())))), 330ca81abcfSusama hameed hasAncestor(expr(anyOf( 331ca81abcfSusama hameed // `UnaryExprOrTypeTraitExpr` is unevaluated 332ca81abcfSusama hameed // unless it's sizeof on VLA. 333ca81abcfSusama hameed unaryExprOrTypeTraitExpr(unless(sizeOfExpr( 334ca81abcfSusama hameed hasArgumentOfType(variableArrayType())))), 335ca81abcfSusama hameed // `CXXTypeidExpr` is unevaluated unless it's 336ca81abcfSusama hameed // applied to an expression of glvalue of 337ca81abcfSusama hameed // polymorphic class type. 338ae20dbddSCongcong Cai cxxTypeidExpr(unless(isPotentiallyEvaluated())), 339ca81abcfSusama hameed // The controlling expression of 340ca81abcfSusama hameed // `GenericSelectionExpr` is unevaluated. 341ae20dbddSCongcong Cai genericSelectionExpr( 342ae20dbddSCongcong Cai hasControllingExpr(hasDescendant(equalsNode(Stm)))), 343ae20dbddSCongcong Cai cxxNoexceptExpr()))))), 344ae20dbddSCongcong Cai *Stm, Context) 345ae20dbddSCongcong Cai .empty(); 34660268222Susama hameed } 34760268222Susama hameed 348e9192f83SShuai Wang const Stmt * 349f40f4fceSCongcong Cai ExprMutationAnalyzer::Analyzer::findExprMutation(ArrayRef<BoundNodes> Matches) { 350f40f4fceSCongcong Cai return tryEachMatch<Expr>(Matches, this, 351f40f4fceSCongcong Cai &ExprMutationAnalyzer::Analyzer::findMutation); 352e9192f83SShuai Wang } 353e9192f83SShuai Wang 354e9192f83SShuai Wang const Stmt * 355f40f4fceSCongcong Cai ExprMutationAnalyzer::Analyzer::findDeclMutation(ArrayRef<BoundNodes> Matches) { 356aaaa310dSShuai Wang return tryEachMatch<Decl>(Matches, this, 357f40f4fceSCongcong Cai &ExprMutationAnalyzer::Analyzer::findMutation); 358e9192f83SShuai Wang } 359e9192f83SShuai Wang 360f40f4fceSCongcong Cai const Stmt *ExprMutationAnalyzer::Analyzer::findExprPointeeMutation( 361f40f4fceSCongcong Cai ArrayRef<ast_matchers::BoundNodes> Matches) { 362f40f4fceSCongcong Cai return tryEachMatch<Expr>( 363f40f4fceSCongcong Cai Matches, this, &ExprMutationAnalyzer::Analyzer::findPointeeMutation); 364f40f4fceSCongcong Cai } 365f40f4fceSCongcong Cai 366f40f4fceSCongcong Cai const Stmt *ExprMutationAnalyzer::Analyzer::findDeclPointeeMutation( 367f40f4fceSCongcong Cai ArrayRef<ast_matchers::BoundNodes> Matches) { 368f40f4fceSCongcong Cai return tryEachMatch<Decl>( 369f40f4fceSCongcong Cai Matches, this, &ExprMutationAnalyzer::Analyzer::findPointeeMutation); 370f40f4fceSCongcong Cai } 371f40f4fceSCongcong Cai 372f40f4fceSCongcong Cai const Stmt * 373f40f4fceSCongcong Cai ExprMutationAnalyzer::Analyzer::findDirectMutation(const Expr *Exp) { 374e9192f83SShuai Wang // LHS of any assignment operators. 3756eb372e4SPiotr Zegar const auto AsAssignmentLhs = 3766eb372e4SPiotr Zegar binaryOperator(isAssignmentOperator(), hasLHS(canResolveToExpr(Exp))); 377e9192f83SShuai Wang 378e9192f83SShuai Wang // Operand of increment/decrement operators. 379e9192f83SShuai Wang const auto AsIncDecOperand = 380e9192f83SShuai Wang unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")), 3816eb372e4SPiotr Zegar hasUnaryOperand(canResolveToExpr(Exp))); 382e9192f83SShuai Wang 383e9192f83SShuai Wang // Invoking non-const member function. 384e9192f83SShuai Wang // A member function is assumed to be non-const when it is unresolved. 385e9192f83SShuai Wang const auto NonConstMethod = cxxMethodDecl(unless(isConst())); 386e517e5cfSJonas Toth 387e517e5cfSJonas Toth const auto AsNonConstThis = expr(anyOf( 3886eb372e4SPiotr Zegar cxxMemberCallExpr(on(canResolveToExpr(Exp)), unless(isConstCallee())), 389e9192f83SShuai Wang cxxOperatorCallExpr(callee(NonConstMethod), 3906eb372e4SPiotr Zegar hasArgument(0, canResolveToExpr(Exp))), 391e517e5cfSJonas Toth // In case of a templated type, calling overloaded operators is not 392e517e5cfSJonas Toth // resolved and modelled as `binaryOperator` on a dependent type. 393e517e5cfSJonas Toth // Such instances are considered a modification, because they can modify 394e517e5cfSJonas Toth // in different instantiations of the template. 3956eb372e4SPiotr Zegar binaryOperator(isTypeDependent(), 3966eb372e4SPiotr Zegar hasEitherOperand(ignoringImpCasts(canResolveToExpr(Exp)))), 3979f8ccf50SJulian Schmidt // A fold expression may contain `Exp` as it's initializer. 3989f8ccf50SJulian Schmidt // We don't know if the operator modifies `Exp` because the 3999f8ccf50SJulian Schmidt // operator is type dependent due to the parameter pack. 4009f8ccf50SJulian Schmidt cxxFoldExpr(hasFoldInit(ignoringImpCasts(canResolveToExpr(Exp)))), 401e517e5cfSJonas Toth // Within class templates and member functions the member expression might 402e517e5cfSJonas Toth // not be resolved. In that case, the `callExpr` is considered to be a 403e517e5cfSJonas Toth // modification. 4046eb372e4SPiotr Zegar callExpr(callee(expr(anyOf( 4056eb372e4SPiotr Zegar unresolvedMemberExpr(hasObjectExpression(canResolveToExpr(Exp))), 4066eb372e4SPiotr Zegar cxxDependentScopeMemberExpr( 4076eb372e4SPiotr Zegar hasObjectExpression(canResolveToExpr(Exp))))))), 408e517e5cfSJonas Toth // Match on a call to a known method, but the call itself is type 409e517e5cfSJonas Toth // dependent (e.g. `vector<T> v; v.push(T{});` in a templated function). 4106eb372e4SPiotr Zegar callExpr(allOf( 4116eb372e4SPiotr Zegar isTypeDependent(), 412e517e5cfSJonas Toth callee(memberExpr(hasDeclaration(NonConstMethod), 4136eb372e4SPiotr Zegar hasObjectExpression(canResolveToExpr(Exp)))))))); 414e9192f83SShuai Wang 415e9192f83SShuai Wang // Taking address of 'Exp'. 416e9192f83SShuai Wang // We're assuming 'Exp' is mutated as soon as its address is taken, though in 417e9192f83SShuai Wang // theory we can follow the pointer and see whether it escaped `Stm` or is 418e9192f83SShuai Wang // dereferenced and then mutated. This is left for future improvements. 419e9192f83SShuai Wang const auto AsAmpersandOperand = 420e9192f83SShuai Wang unaryOperator(hasOperatorName("&"), 421e9192f83SShuai Wang // A NoOp implicit cast is adding const. 422e9192f83SShuai Wang unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))), 4236eb372e4SPiotr Zegar hasUnaryOperand(canResolveToExpr(Exp))); 4246eb372e4SPiotr Zegar const auto AsPointerFromArrayDecay = castExpr( 4256eb372e4SPiotr Zegar hasCastKind(CK_ArrayToPointerDecay), 4266eb372e4SPiotr Zegar unless(hasParent(arraySubscriptExpr())), has(canResolveToExpr(Exp))); 427e9192f83SShuai Wang // Treat calling `operator->()` of move-only classes as taking address. 428e9192f83SShuai Wang // These are typically smart pointers with unique ownership so we treat 429e9192f83SShuai Wang // mutation of pointee as mutation of the smart pointer itself. 430e517e5cfSJonas Toth const auto AsOperatorArrowThis = cxxOperatorCallExpr( 431e517e5cfSJonas Toth hasOverloadedOperatorName("->"), 432e517e5cfSJonas Toth callee( 433e517e5cfSJonas Toth cxxMethodDecl(ofClass(isMoveOnly()), returns(nonConstPointerType()))), 4346eb372e4SPiotr Zegar argumentCountIs(1), hasArgument(0, canResolveToExpr(Exp))); 435e9192f83SShuai Wang 436e9192f83SShuai Wang // Used as non-const-ref argument when calling a function. 437e9192f83SShuai Wang // An argument is assumed to be non-const-ref when the function is unresolved. 438cb98b707SShuai Wang // Instantiated template functions are not handled here but in 439cb98b707SShuai Wang // findFunctionArgMutation which has additional smarts for handling forwarding 440cb98b707SShuai Wang // references. 441e517e5cfSJonas Toth const auto NonConstRefParam = forEachArgumentWithParamType( 4426eb372e4SPiotr Zegar anyOf(canResolveToExpr(Exp), 443*53ea5ffcSCongcong Cai memberExpr( 444*53ea5ffcSCongcong Cai hasObjectExpression(ignoringImpCasts(canResolveToExpr(Exp))))), 445e517e5cfSJonas Toth nonConstReferenceType()); 446cb98b707SShuai Wang const auto NotInstantiated = unless(hasDeclaration(isInstantiated())); 447e517e5cfSJonas Toth 4481ce89899SDmitry Polukhin const auto AsNonConstRefArg = 4491ce89899SDmitry Polukhin anyOf(callExpr(NonConstRefParam, NotInstantiated), 450cb98b707SShuai Wang cxxConstructExpr(NonConstRefParam, NotInstantiated), 4511ce89899SDmitry Polukhin // If the call is type-dependent, we can't properly process any 4521ce89899SDmitry Polukhin // argument because required type conversions and implicit casts 4531ce89899SDmitry Polukhin // will be inserted only after specialization. 4541ce89899SDmitry Polukhin callExpr(isTypeDependent(), hasAnyArgument(canResolveToExpr(Exp))), 4556eb372e4SPiotr Zegar cxxUnresolvedConstructExpr(hasAnyArgument(canResolveToExpr(Exp))), 456e517e5cfSJonas Toth // Previous False Positive in the following Code: 457e517e5cfSJonas Toth // `template <typename T> void f() { int i = 42; new Type<T>(i); }` 458e517e5cfSJonas Toth // Where the constructor of `Type` takes its argument as reference. 459e517e5cfSJonas Toth // The AST does not resolve in a `cxxConstructExpr` because it is 460e517e5cfSJonas Toth // type-dependent. 4616eb372e4SPiotr Zegar parenListExpr(hasDescendant(expr(canResolveToExpr(Exp)))), 462e517e5cfSJonas Toth // If the initializer is for a reference type, there is no cast for 463e517e5cfSJonas Toth // the variable. Values are cast to RValue first. 4646eb372e4SPiotr Zegar initListExpr(hasAnyInit(expr(canResolveToExpr(Exp))))); 465e9192f83SShuai Wang 466e9192f83SShuai Wang // Captured by a lambda by reference. 467e9192f83SShuai Wang // If we're initializing a capture with 'Exp' directly then we're initializing 468e9192f83SShuai Wang // a reference capture. 469e9192f83SShuai Wang // For value captures there will be an ImplicitCastExpr <LValueToRValue>. 470e9192f83SShuai Wang const auto AsLambdaRefCaptureInit = lambdaExpr(hasCaptureInit(Exp)); 471e9192f83SShuai Wang 472e9192f83SShuai Wang // Returned as non-const-ref. 473e9192f83SShuai Wang // If we're returning 'Exp' directly then it's returned as non-const-ref. 474e9192f83SShuai Wang // For returning by value there will be an ImplicitCastExpr <LValueToRValue>. 475e9192f83SShuai Wang // For returning by const-ref there will be an ImplicitCastExpr <NoOp> (for 476e9192f83SShuai Wang // adding const.) 477e517e5cfSJonas Toth const auto AsNonConstRefReturn = 4786eb372e4SPiotr Zegar returnStmt(hasReturnValue(canResolveToExpr(Exp))); 479e517e5cfSJonas Toth 480f40f4fceSCongcong Cai // It is used as a non-const-reference for initializing a range-for loop. 4816eb372e4SPiotr Zegar const auto AsNonConstRefRangeInit = cxxForRangeStmt(hasRangeInit(declRefExpr( 4826eb372e4SPiotr Zegar allOf(canResolveToExpr(Exp), hasType(nonConstReferenceType()))))); 483e9192f83SShuai Wang 484f85aedc1SStephen Kelly const auto Matches = match( 4856eb372e4SPiotr Zegar traverse( 4866eb372e4SPiotr Zegar TK_AsIs, 4876eb372e4SPiotr Zegar findFirst(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand, AsNonConstThis, 4886eb372e4SPiotr Zegar AsAmpersandOperand, AsPointerFromArrayDecay, 4896eb372e4SPiotr Zegar AsOperatorArrowThis, AsNonConstRefArg, 4906eb372e4SPiotr Zegar AsLambdaRefCaptureInit, AsNonConstRefReturn, 4916eb372e4SPiotr Zegar AsNonConstRefRangeInit)) 492f85aedc1SStephen Kelly .bind("stmt"))), 493e9192f83SShuai Wang Stm, Context); 494e9192f83SShuai Wang return selectFirst<Stmt>("stmt", Matches); 495e9192f83SShuai Wang } 496e9192f83SShuai Wang 497f40f4fceSCongcong Cai const Stmt * 498f40f4fceSCongcong Cai ExprMutationAnalyzer::Analyzer::findMemberMutation(const Expr *Exp) { 499e9192f83SShuai Wang // Check whether any member of 'Exp' is mutated. 5006eb372e4SPiotr Zegar const auto MemberExprs = match( 5016eb372e4SPiotr Zegar findAll(expr(anyOf(memberExpr(hasObjectExpression(canResolveToExpr(Exp))), 5026eb372e4SPiotr Zegar cxxDependentScopeMemberExpr( 5036eb372e4SPiotr Zegar hasObjectExpression(canResolveToExpr(Exp))), 504feb7b191SCongcong Cai binaryOperator(hasOperatorName(".*"), 505feb7b191SCongcong Cai hasLHS(equalsNode(Exp))))) 506aaaa310dSShuai Wang .bind(NodeID<Expr>::value)), 507e9192f83SShuai Wang Stm, Context); 508e9192f83SShuai Wang return findExprMutation(MemberExprs); 509e9192f83SShuai Wang } 510e9192f83SShuai Wang 511f40f4fceSCongcong Cai const Stmt * 512f40f4fceSCongcong Cai ExprMutationAnalyzer::Analyzer::findArrayElementMutation(const Expr *Exp) { 513e9192f83SShuai Wang // Check whether any element of an array is mutated. 5146eb372e4SPiotr Zegar const auto SubscriptExprs = match( 5156eb372e4SPiotr Zegar findAll(arraySubscriptExpr( 5166eb372e4SPiotr Zegar anyOf(hasBase(canResolveToExpr(Exp)), 5176eb372e4SPiotr Zegar hasBase(implicitCastExpr(allOf( 5186eb372e4SPiotr Zegar hasCastKind(CK_ArrayToPointerDecay), 5196eb372e4SPiotr Zegar hasSourceExpression(canResolveToExpr(Exp))))))) 520aaaa310dSShuai Wang .bind(NodeID<Expr>::value)), 521e9192f83SShuai Wang Stm, Context); 522e9192f83SShuai Wang return findExprMutation(SubscriptExprs); 523e9192f83SShuai Wang } 524e9192f83SShuai Wang 525f40f4fceSCongcong Cai const Stmt *ExprMutationAnalyzer::Analyzer::findCastMutation(const Expr *Exp) { 526e517e5cfSJonas Toth // If the 'Exp' is explicitly casted to a non-const reference type the 527e517e5cfSJonas Toth // 'Exp' is considered to be modified. 5286eb372e4SPiotr Zegar const auto ExplicitCast = 5296eb372e4SPiotr Zegar match(findFirst(stmt(castExpr(hasSourceExpression(canResolveToExpr(Exp)), 5306eb372e4SPiotr Zegar explicitCastExpr(hasDestinationType( 5316eb372e4SPiotr Zegar nonConstReferenceType())))) 532e517e5cfSJonas Toth .bind("stmt")), 533e517e5cfSJonas Toth Stm, Context); 534e517e5cfSJonas Toth 535e517e5cfSJonas Toth if (const auto *CastStmt = selectFirst<Stmt>("stmt", ExplicitCast)) 536e517e5cfSJonas Toth return CastStmt; 537e517e5cfSJonas Toth 538e9192f83SShuai Wang // If 'Exp' is casted to any non-const reference type, check the castExpr. 539e517e5cfSJonas Toth const auto Casts = match( 5406eb372e4SPiotr Zegar findAll(expr(castExpr(hasSourceExpression(canResolveToExpr(Exp)), 5416eb372e4SPiotr Zegar anyOf(explicitCastExpr(hasDestinationType( 5426eb372e4SPiotr Zegar nonConstReferenceType())), 543e9192f83SShuai Wang implicitCastExpr(hasImplicitDestinationType( 544e517e5cfSJonas Toth nonConstReferenceType()))))) 545aaaa310dSShuai Wang .bind(NodeID<Expr>::value)), 546e9192f83SShuai Wang Stm, Context); 547e517e5cfSJonas Toth 5484305993cSShuai Wang if (const Stmt *S = findExprMutation(Casts)) 5494305993cSShuai Wang return S; 5504305993cSShuai Wang // Treat std::{move,forward} as cast. 5514305993cSShuai Wang const auto Calls = 5524305993cSShuai Wang match(findAll(callExpr(callee(namedDecl( 5534305993cSShuai Wang hasAnyName("::std::move", "::std::forward"))), 5546eb372e4SPiotr Zegar hasArgument(0, canResolveToExpr(Exp))) 5554305993cSShuai Wang .bind("expr")), 5564305993cSShuai Wang Stm, Context); 5574305993cSShuai Wang return findExprMutation(Calls); 558e9192f83SShuai Wang } 559e9192f83SShuai Wang 560f40f4fceSCongcong Cai const Stmt * 561f40f4fceSCongcong Cai ExprMutationAnalyzer::Analyzer::findRangeLoopMutation(const Expr *Exp) { 562e517e5cfSJonas Toth // Keep the ordering for the specific initialization matches to happen first, 563e517e5cfSJonas Toth // because it is cheaper to match all potential modifications of the loop 564e517e5cfSJonas Toth // variable. 565e517e5cfSJonas Toth 566e517e5cfSJonas Toth // The range variable is a reference to a builtin array. In that case the 567e517e5cfSJonas Toth // array is considered modified if the loop-variable is a non-const reference. 568e517e5cfSJonas Toth const auto DeclStmtToNonRefToArray = declStmt(hasSingleDecl(varDecl(hasType( 569e517e5cfSJonas Toth hasUnqualifiedDesugaredType(referenceType(pointee(arrayType()))))))); 5706eb372e4SPiotr Zegar const auto RefToArrayRefToElements = match( 5716eb372e4SPiotr Zegar findFirst(stmt(cxxForRangeStmt( 57246ae26e7SJonas Toth hasLoopVariable( 57346ae26e7SJonas Toth varDecl(anyOf(hasType(nonConstReferenceType()), 57446ae26e7SJonas Toth hasType(nonConstPointerType()))) 575e517e5cfSJonas Toth .bind(NodeID<Decl>::value)), 576e517e5cfSJonas Toth hasRangeStmt(DeclStmtToNonRefToArray), 5776eb372e4SPiotr Zegar hasRangeInit(canResolveToExpr(Exp)))) 578e517e5cfSJonas Toth .bind("stmt")), 579e517e5cfSJonas Toth Stm, Context); 580e517e5cfSJonas Toth 581e517e5cfSJonas Toth if (const auto *BadRangeInitFromArray = 582e517e5cfSJonas Toth selectFirst<Stmt>("stmt", RefToArrayRefToElements)) 583e517e5cfSJonas Toth return BadRangeInitFromArray; 584e517e5cfSJonas Toth 585e517e5cfSJonas Toth // Small helper to match special cases in range-for loops. 586e517e5cfSJonas Toth // 587e517e5cfSJonas Toth // It is possible that containers do not provide a const-overload for their 588e517e5cfSJonas Toth // iterator accessors. If this is the case, the variable is used non-const 589e517e5cfSJonas Toth // no matter what happens in the loop. This requires special detection as it 590e517e5cfSJonas Toth // is then faster to find all mutations of the loop variable. 591e517e5cfSJonas Toth // It aims at a different modification as well. 592e517e5cfSJonas Toth const auto HasAnyNonConstIterator = 593e517e5cfSJonas Toth anyOf(allOf(hasMethod(allOf(hasName("begin"), unless(isConst()))), 594e517e5cfSJonas Toth unless(hasMethod(allOf(hasName("begin"), isConst())))), 595e517e5cfSJonas Toth allOf(hasMethod(allOf(hasName("end"), unless(isConst()))), 596e517e5cfSJonas Toth unless(hasMethod(allOf(hasName("end"), isConst()))))); 597e517e5cfSJonas Toth 598e517e5cfSJonas Toth const auto DeclStmtToNonConstIteratorContainer = declStmt( 599e517e5cfSJonas Toth hasSingleDecl(varDecl(hasType(hasUnqualifiedDesugaredType(referenceType( 600e517e5cfSJonas Toth pointee(hasDeclaration(cxxRecordDecl(HasAnyNonConstIterator))))))))); 601e517e5cfSJonas Toth 6026eb372e4SPiotr Zegar const auto RefToContainerBadIterators = match( 6036eb372e4SPiotr Zegar findFirst(stmt(cxxForRangeStmt(allOf( 604e517e5cfSJonas Toth hasRangeStmt(DeclStmtToNonConstIteratorContainer), 6056eb372e4SPiotr Zegar hasRangeInit(canResolveToExpr(Exp))))) 606e517e5cfSJonas Toth .bind("stmt")), 607e517e5cfSJonas Toth Stm, Context); 608e517e5cfSJonas Toth 609e517e5cfSJonas Toth if (const auto *BadIteratorsContainer = 610e517e5cfSJonas Toth selectFirst<Stmt>("stmt", RefToContainerBadIterators)) 611e517e5cfSJonas Toth return BadIteratorsContainer; 612e517e5cfSJonas Toth 613e9192f83SShuai Wang // If range for looping over 'Exp' with a non-const reference loop variable, 614e9192f83SShuai Wang // check all declRefExpr of the loop variable. 615e9192f83SShuai Wang const auto LoopVars = 616e9192f83SShuai Wang match(findAll(cxxForRangeStmt( 617aaaa310dSShuai Wang hasLoopVariable(varDecl(hasType(nonConstReferenceType())) 618aaaa310dSShuai Wang .bind(NodeID<Decl>::value)), 6196eb372e4SPiotr Zegar hasRangeInit(canResolveToExpr(Exp)))), 620e9192f83SShuai Wang Stm, Context); 621e9192f83SShuai Wang return findDeclMutation(LoopVars); 622e9192f83SShuai Wang } 623e9192f83SShuai Wang 624f40f4fceSCongcong Cai const Stmt * 625f40f4fceSCongcong Cai ExprMutationAnalyzer::Analyzer::findReferenceMutation(const Expr *Exp) { 626e9192f83SShuai Wang // Follow non-const reference returned by `operator*()` of move-only classes. 627e9192f83SShuai Wang // These are typically smart pointers with unique ownership so we treat 628e9192f83SShuai Wang // mutation of pointee as mutation of the smart pointer itself. 6296eb372e4SPiotr Zegar const auto Ref = match( 6306eb372e4SPiotr Zegar findAll(cxxOperatorCallExpr( 631e9192f83SShuai Wang hasOverloadedOperatorName("*"), 632e9192f83SShuai Wang callee(cxxMethodDecl(ofClass(isMoveOnly()), 633cef621d0SShuai Wang returns(nonConstReferenceType()))), 6346eb372e4SPiotr Zegar argumentCountIs(1), hasArgument(0, canResolveToExpr(Exp))) 635aaaa310dSShuai Wang .bind(NodeID<Expr>::value)), 636e9192f83SShuai Wang Stm, Context); 637e9192f83SShuai Wang if (const Stmt *S = findExprMutation(Ref)) 638e9192f83SShuai Wang return S; 639e9192f83SShuai Wang 640e9192f83SShuai Wang // If 'Exp' is bound to a non-const reference, check all declRefExpr to that. 641e9192f83SShuai Wang const auto Refs = match( 642e9192f83SShuai Wang stmt(forEachDescendant( 6436eb372e4SPiotr Zegar varDecl(hasType(nonConstReferenceType()), 6446eb372e4SPiotr Zegar hasInitializer(anyOf( 6456eb372e4SPiotr Zegar canResolveToExpr(Exp), 6466eb372e4SPiotr Zegar memberExpr(hasObjectExpression(canResolveToExpr(Exp))))), 647e9192f83SShuai Wang hasParent(declStmt().bind("stmt")), 648e517e5cfSJonas Toth // Don't follow the reference in range statement, we've 649e517e5cfSJonas Toth // handled that separately. 6506eb372e4SPiotr Zegar unless(hasParent(declStmt(hasParent(cxxForRangeStmt( 6516eb372e4SPiotr Zegar hasRangeStmt(equalsBoundNode("stmt")))))))) 652aaaa310dSShuai Wang .bind(NodeID<Decl>::value))), 653e9192f83SShuai Wang Stm, Context); 654e9192f83SShuai Wang return findDeclMutation(Refs); 655e9192f83SShuai Wang } 656e9192f83SShuai Wang 657f40f4fceSCongcong Cai const Stmt * 658f40f4fceSCongcong Cai ExprMutationAnalyzer::Analyzer::findFunctionArgMutation(const Expr *Exp) { 659cb98b707SShuai Wang const auto NonConstRefParam = forEachArgumentWithParam( 6606eb372e4SPiotr Zegar canResolveToExpr(Exp), 661cb98b707SShuai Wang parmVarDecl(hasType(nonConstReferenceType())).bind("parm")); 662cb98b707SShuai Wang const auto IsInstantiated = hasDeclaration(isInstantiated()); 663cb98b707SShuai Wang const auto FuncDecl = hasDeclaration(functionDecl().bind("func")); 664cb98b707SShuai Wang const auto Matches = match( 665f85aedc1SStephen Kelly traverse( 666027899daSAlexander Kornienko TK_AsIs, 667f85aedc1SStephen Kelly findAll( 668f85aedc1SStephen Kelly expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl, 6694305993cSShuai Wang unless(callee(namedDecl(hasAnyName( 6704305993cSShuai Wang "::std::move", "::std::forward"))))), 671cb98b707SShuai Wang cxxConstructExpr(NonConstRefParam, IsInstantiated, 672cb98b707SShuai Wang FuncDecl))) 673f85aedc1SStephen Kelly .bind(NodeID<Expr>::value))), 674cb98b707SShuai Wang Stm, Context); 675cb98b707SShuai Wang for (const auto &Nodes : Matches) { 676aaaa310dSShuai Wang const auto *Exp = Nodes.getNodeAs<Expr>(NodeID<Expr>::value); 677cb98b707SShuai Wang const auto *Func = Nodes.getNodeAs<FunctionDecl>("func"); 67886e5cb0eSShuai Wang if (!Func->getBody() || !Func->getPrimaryTemplate()) 679cb98b707SShuai Wang return Exp; 680cb98b707SShuai Wang 681cb98b707SShuai Wang const auto *Parm = Nodes.getNodeAs<ParmVarDecl>("parm"); 682cb98b707SShuai Wang const ArrayRef<ParmVarDecl *> AllParams = 683cb98b707SShuai Wang Func->getPrimaryTemplate()->getTemplatedDecl()->parameters(); 684cb98b707SShuai Wang QualType ParmType = 685cb98b707SShuai Wang AllParams[std::min<size_t>(Parm->getFunctionScopeIndex(), 686cb98b707SShuai Wang AllParams.size() - 1)] 687cb98b707SShuai Wang ->getType(); 688cb98b707SShuai Wang if (const auto *T = ParmType->getAs<PackExpansionType>()) 689cb98b707SShuai Wang ParmType = T->getPattern(); 690cb98b707SShuai Wang 691cb98b707SShuai Wang // If param type is forwarding reference, follow into the function 692cb98b707SShuai Wang // definition and see whether the param is mutated inside. 693cb98b707SShuai Wang if (const auto *RefType = ParmType->getAs<RValueReferenceType>()) { 694cb98b707SShuai Wang if (!RefType->getPointeeType().getQualifiers() && 695cb98b707SShuai Wang RefType->getPointeeType()->getAs<TemplateTypeParmType>()) { 696f40f4fceSCongcong Cai FunctionParmMutationAnalyzer *Analyzer = 697f40f4fceSCongcong Cai FunctionParmMutationAnalyzer::getFunctionParmMutationAnalyzer( 698f40f4fceSCongcong Cai *Func, Context, Memorized); 699cb98b707SShuai Wang if (Analyzer->findMutation(Parm)) 700cb98b707SShuai Wang return Exp; 701cb98b707SShuai Wang continue; 702cb98b707SShuai Wang } 703cb98b707SShuai Wang } 704cb98b707SShuai Wang // Not forwarding reference. 705cb98b707SShuai Wang return Exp; 706cb98b707SShuai Wang } 707cb98b707SShuai Wang return nullptr; 708cb98b707SShuai Wang } 709cb98b707SShuai Wang 710*53ea5ffcSCongcong Cai const Stmt * 711*53ea5ffcSCongcong Cai ExprMutationAnalyzer::Analyzer::findPointeeValueMutation(const Expr *Exp) { 712*53ea5ffcSCongcong Cai const auto Matches = match( 713*53ea5ffcSCongcong Cai stmt(forEachDescendant( 714*53ea5ffcSCongcong Cai expr(anyOf( 715*53ea5ffcSCongcong Cai // deref by * 716*53ea5ffcSCongcong Cai unaryOperator(hasOperatorName("*"), 717*53ea5ffcSCongcong Cai hasUnaryOperand(canResolveToExprPointee(Exp))), 718*53ea5ffcSCongcong Cai // deref by [] 719*53ea5ffcSCongcong Cai arraySubscriptExpr(hasBase(canResolveToExprPointee(Exp))))) 720*53ea5ffcSCongcong Cai .bind(NodeID<Expr>::value))), 721*53ea5ffcSCongcong Cai Stm, Context); 722*53ea5ffcSCongcong Cai return findExprMutation(Matches); 723*53ea5ffcSCongcong Cai } 724*53ea5ffcSCongcong Cai 725*53ea5ffcSCongcong Cai const Stmt * 726*53ea5ffcSCongcong Cai ExprMutationAnalyzer::Analyzer::findPointeeMemberMutation(const Expr *Exp) { 727*53ea5ffcSCongcong Cai const Stmt *MemberCallExpr = selectFirst<Stmt>( 728*53ea5ffcSCongcong Cai "stmt", match(stmt(forEachDescendant( 729*53ea5ffcSCongcong Cai cxxMemberCallExpr(on(canResolveToExprPointee(Exp)), 730*53ea5ffcSCongcong Cai unless(isConstCallee())) 731*53ea5ffcSCongcong Cai .bind("stmt"))), 732*53ea5ffcSCongcong Cai Stm, Context)); 733*53ea5ffcSCongcong Cai if (MemberCallExpr) 734*53ea5ffcSCongcong Cai return MemberCallExpr; 735*53ea5ffcSCongcong Cai const auto Matches = 736*53ea5ffcSCongcong Cai match(stmt(forEachDescendant( 737*53ea5ffcSCongcong Cai memberExpr(hasObjectExpression(canResolveToExprPointee(Exp))) 738*53ea5ffcSCongcong Cai .bind(NodeID<Expr>::value))), 739*53ea5ffcSCongcong Cai Stm, Context); 740*53ea5ffcSCongcong Cai return findExprMutation(Matches); 741*53ea5ffcSCongcong Cai } 742*53ea5ffcSCongcong Cai 743*53ea5ffcSCongcong Cai const Stmt * 744*53ea5ffcSCongcong Cai ExprMutationAnalyzer::Analyzer::findPointeeToNonConst(const Expr *Exp) { 745*53ea5ffcSCongcong Cai const auto NonConstPointerOrDependentType = 746*53ea5ffcSCongcong Cai type(anyOf(nonConstPointerType(), isDependentType())); 747*53ea5ffcSCongcong Cai 748*53ea5ffcSCongcong Cai // assign 749*53ea5ffcSCongcong Cai const auto InitToNonConst = 750*53ea5ffcSCongcong Cai varDecl(hasType(NonConstPointerOrDependentType), 751*53ea5ffcSCongcong Cai hasInitializer(expr(canResolveToExprPointee(Exp)).bind("stmt"))); 752*53ea5ffcSCongcong Cai const auto AssignToNonConst = 753*53ea5ffcSCongcong Cai binaryOperation(hasOperatorName("="), 754*53ea5ffcSCongcong Cai hasLHS(expr(hasType(NonConstPointerOrDependentType))), 755*53ea5ffcSCongcong Cai hasRHS(canResolveToExprPointee(Exp))); 756*53ea5ffcSCongcong Cai // arguments like 757*53ea5ffcSCongcong Cai const auto ArgOfInstantiationDependent = allOf( 758*53ea5ffcSCongcong Cai hasAnyArgument(canResolveToExprPointee(Exp)), isInstantiationDependent()); 759*53ea5ffcSCongcong Cai const auto ArgOfNonConstParameter = forEachArgumentWithParamType( 760*53ea5ffcSCongcong Cai canResolveToExprPointee(Exp), NonConstPointerOrDependentType); 761*53ea5ffcSCongcong Cai const auto CallLikeMatcher = 762*53ea5ffcSCongcong Cai anyOf(ArgOfNonConstParameter, ArgOfInstantiationDependent); 763*53ea5ffcSCongcong Cai const auto PassAsNonConstArg = 764*53ea5ffcSCongcong Cai expr(anyOf(cxxUnresolvedConstructExpr(ArgOfInstantiationDependent), 765*53ea5ffcSCongcong Cai cxxConstructExpr(CallLikeMatcher), callExpr(CallLikeMatcher), 766*53ea5ffcSCongcong Cai parenListExpr(has(canResolveToExprPointee(Exp))), 767*53ea5ffcSCongcong Cai initListExpr(hasAnyInit(canResolveToExprPointee(Exp))))); 768*53ea5ffcSCongcong Cai // cast 769*53ea5ffcSCongcong Cai const auto CastToNonConst = 770*53ea5ffcSCongcong Cai explicitCastExpr(hasSourceExpression(canResolveToExprPointee(Exp)), 771*53ea5ffcSCongcong Cai hasDestinationType(NonConstPointerOrDependentType)); 772*53ea5ffcSCongcong Cai 773*53ea5ffcSCongcong Cai // capture 774*53ea5ffcSCongcong Cai // FIXME: false positive if the pointee does not change in lambda 775*53ea5ffcSCongcong Cai const auto CaptureNoConst = lambdaExpr(hasCaptureInit(Exp)); 776*53ea5ffcSCongcong Cai 777*53ea5ffcSCongcong Cai const auto Matches = 778*53ea5ffcSCongcong Cai match(stmt(anyOf(forEachDescendant( 779*53ea5ffcSCongcong Cai stmt(anyOf(AssignToNonConst, PassAsNonConstArg, 780*53ea5ffcSCongcong Cai CastToNonConst, CaptureNoConst)) 781*53ea5ffcSCongcong Cai .bind("stmt")), 782*53ea5ffcSCongcong Cai forEachDescendant(InitToNonConst))), 783*53ea5ffcSCongcong Cai Stm, Context); 784*53ea5ffcSCongcong Cai return selectFirst<Stmt>("stmt", Matches); 785*53ea5ffcSCongcong Cai } 786*53ea5ffcSCongcong Cai 787cb98b707SShuai Wang FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer( 788f40f4fceSCongcong Cai const FunctionDecl &Func, ASTContext &Context, 789f40f4fceSCongcong Cai ExprMutationAnalyzer::Memoized &Memorized) 790f40f4fceSCongcong Cai : BodyAnalyzer(*Func.getBody(), Context, Memorized) { 791cb98b707SShuai Wang if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(&Func)) { 792cb98b707SShuai Wang // CXXCtorInitializer might also mutate Param but they're not part of 793cb98b707SShuai Wang // function body, check them eagerly here since they're typically trivial. 794cb98b707SShuai Wang for (const CXXCtorInitializer *Init : Ctor->inits()) { 795f40f4fceSCongcong Cai ExprMutationAnalyzer::Analyzer InitAnalyzer(*Init->getInit(), Context, 796f40f4fceSCongcong Cai Memorized); 797cb98b707SShuai Wang for (const ParmVarDecl *Parm : Ctor->parameters()) { 798ea9d4040SKazu Hirata if (Results.contains(Parm)) 799cb98b707SShuai Wang continue; 800cef621d0SShuai Wang if (const Stmt *S = InitAnalyzer.findMutation(Parm)) 801cb98b707SShuai Wang Results[Parm] = S; 802cb98b707SShuai Wang } 803cb98b707SShuai Wang } 804cb98b707SShuai Wang } 805cb98b707SShuai Wang } 806cb98b707SShuai Wang 807cb98b707SShuai Wang const Stmt * 808cb98b707SShuai Wang FunctionParmMutationAnalyzer::findMutation(const ParmVarDecl *Parm) { 809cb98b707SShuai Wang const auto Memoized = Results.find(Parm); 810cb98b707SShuai Wang if (Memoized != Results.end()) 811cb98b707SShuai Wang return Memoized->second; 812f40f4fceSCongcong Cai // To handle call A -> call B -> call A. Assume parameters of A is not mutated 813f40f4fceSCongcong Cai // before analyzing parameters of A. Then when analyzing the second "call A", 814f40f4fceSCongcong Cai // FunctionParmMutationAnalyzer can use this memoized value to avoid infinite 815f40f4fceSCongcong Cai // recursion. 816f40f4fceSCongcong Cai Results[Parm] = nullptr; 817cef621d0SShuai Wang if (const Stmt *S = BodyAnalyzer.findMutation(Parm)) 818cb98b707SShuai Wang return Results[Parm] = S; 819f40f4fceSCongcong Cai return Results[Parm]; 820cb98b707SShuai Wang } 821cb98b707SShuai Wang 822e9192f83SShuai Wang } // namespace clang 823