1 //===---------- ExprMutationAnalyzer.cpp ----------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 #include "clang/Analysis/Analyses/ExprMutationAnalyzer.h" 9 #include "clang/AST/Expr.h" 10 #include "clang/AST/OperationKinds.h" 11 #include "clang/ASTMatchers/ASTMatchFinder.h" 12 #include "clang/ASTMatchers/ASTMatchers.h" 13 #include "llvm/ADT/STLExtras.h" 14 15 namespace clang { 16 using namespace ast_matchers; 17 18 // Check if result of Source expression could be a Target expression. 19 // Checks: 20 // - Implicit Casts 21 // - Binary Operators 22 // - ConditionalOperator 23 // - BinaryConditionalOperator 24 static bool canExprResolveTo(const Expr *Source, const Expr *Target) { 25 26 const auto IgnoreDerivedToBase = [](const Expr *E, auto Matcher) { 27 if (Matcher(E)) 28 return true; 29 if (const auto *Cast = dyn_cast<ImplicitCastExpr>(E)) { 30 if ((Cast->getCastKind() == CK_DerivedToBase || 31 Cast->getCastKind() == CK_UncheckedDerivedToBase) && 32 Matcher(Cast->getSubExpr())) 33 return true; 34 } 35 return false; 36 }; 37 38 const auto EvalCommaExpr = [](const Expr *E, auto Matcher) { 39 const Expr *Result = E; 40 while (const auto *BOComma = 41 dyn_cast_or_null<BinaryOperator>(Result->IgnoreParens())) { 42 if (!BOComma->isCommaOp()) 43 break; 44 Result = BOComma->getRHS(); 45 } 46 47 return Result != E && Matcher(Result); 48 }; 49 50 // The 'ConditionalOperatorM' matches on `<anything> ? <expr> : <expr>`. 51 // This matching must be recursive because `<expr>` can be anything resolving 52 // to the `InnerMatcher`, for example another conditional operator. 53 // The edge-case `BaseClass &b = <cond> ? DerivedVar1 : DerivedVar2;` 54 // is handled, too. The implicit cast happens outside of the conditional. 55 // This is matched by `IgnoreDerivedToBase(canResolveToExpr(InnerMatcher))` 56 // below. 57 const auto ConditionalOperatorM = [Target](const Expr *E) { 58 if (const auto *OP = dyn_cast<ConditionalOperator>(E)) { 59 if (const auto *TE = OP->getTrueExpr()->IgnoreParens()) 60 if (canExprResolveTo(TE, Target)) 61 return true; 62 if (const auto *FE = OP->getFalseExpr()->IgnoreParens()) 63 if (canExprResolveTo(FE, Target)) 64 return true; 65 } 66 return false; 67 }; 68 69 const auto ElvisOperator = [Target](const Expr *E) { 70 if (const auto *OP = dyn_cast<BinaryConditionalOperator>(E)) { 71 if (const auto *TE = OP->getTrueExpr()->IgnoreParens()) 72 if (canExprResolveTo(TE, Target)) 73 return true; 74 if (const auto *FE = OP->getFalseExpr()->IgnoreParens()) 75 if (canExprResolveTo(FE, Target)) 76 return true; 77 } 78 return false; 79 }; 80 81 const Expr *SourceExprP = Source->IgnoreParens(); 82 return IgnoreDerivedToBase(SourceExprP, 83 [&](const Expr *E) { 84 return E == Target || ConditionalOperatorM(E) || 85 ElvisOperator(E); 86 }) || 87 EvalCommaExpr(SourceExprP, [&](const Expr *E) { 88 return IgnoreDerivedToBase( 89 E->IgnoreParens(), [&](const Expr *EE) { return EE == Target; }); 90 }); 91 } 92 93 namespace { 94 95 AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) { 96 return llvm::is_contained(Node.capture_inits(), E); 97 } 98 99 AST_MATCHER_P(CXXForRangeStmt, hasRangeStmt, 100 ast_matchers::internal::Matcher<DeclStmt>, InnerMatcher) { 101 const DeclStmt *const Range = Node.getRangeStmt(); 102 return InnerMatcher.matches(*Range, Finder, Builder); 103 } 104 105 AST_MATCHER_P(Stmt, canResolveToExpr, const Stmt *, Inner) { 106 auto *Exp = dyn_cast<Expr>(&Node); 107 if (!Exp) 108 return true; 109 auto *Target = dyn_cast<Expr>(Inner); 110 if (!Target) 111 return false; 112 return canExprResolveTo(Exp, Target); 113 } 114 115 // Similar to 'hasAnyArgument', but does not work because 'InitListExpr' does 116 // not have the 'arguments()' method. 117 AST_MATCHER_P(InitListExpr, hasAnyInit, ast_matchers::internal::Matcher<Expr>, 118 InnerMatcher) { 119 for (const Expr *Arg : Node.inits()) { 120 ast_matchers::internal::BoundNodesTreeBuilder Result(*Builder); 121 if (InnerMatcher.matches(*Arg, Finder, &Result)) { 122 *Builder = std::move(Result); 123 return true; 124 } 125 } 126 return false; 127 } 128 129 const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXTypeidExpr> 130 cxxTypeidExpr; 131 132 AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) { 133 return Node.isPotentiallyEvaluated(); 134 } 135 136 AST_MATCHER(CXXMemberCallExpr, isConstCallee) { 137 const Decl *CalleeDecl = Node.getCalleeDecl(); 138 const auto *VD = dyn_cast_or_null<ValueDecl>(CalleeDecl); 139 if (!VD) 140 return false; 141 const QualType T = VD->getType().getCanonicalType(); 142 const auto *MPT = dyn_cast<MemberPointerType>(T); 143 const auto *FPT = MPT ? cast<FunctionProtoType>(MPT->getPointeeType()) 144 : dyn_cast<FunctionProtoType>(T); 145 if (!FPT) 146 return false; 147 return FPT->isConst(); 148 } 149 150 AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr, 151 ast_matchers::internal::Matcher<Expr>, InnerMatcher) { 152 if (Node.isTypePredicate()) 153 return false; 154 return InnerMatcher.matches(*Node.getControllingExpr(), Finder, Builder); 155 } 156 157 template <typename T> 158 ast_matchers::internal::Matcher<T> 159 findFirst(const ast_matchers::internal::Matcher<T> &Matcher) { 160 return anyOf(Matcher, hasDescendant(Matcher)); 161 } 162 163 const auto nonConstReferenceType = [] { 164 return hasUnqualifiedDesugaredType( 165 referenceType(pointee(unless(isConstQualified())))); 166 }; 167 168 const auto nonConstPointerType = [] { 169 return hasUnqualifiedDesugaredType( 170 pointerType(pointee(unless(isConstQualified())))); 171 }; 172 173 const auto isMoveOnly = [] { 174 return cxxRecordDecl( 175 hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted()))), 176 hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted()))), 177 unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(), 178 unless(isDeleted()))), 179 hasMethod(cxxMethodDecl(isCopyAssignmentOperator(), 180 unless(isDeleted())))))); 181 }; 182 183 template <class T> struct NodeID; 184 template <> struct NodeID<Expr> { static constexpr StringRef value = "expr"; }; 185 template <> struct NodeID<Decl> { static constexpr StringRef value = "decl"; }; 186 constexpr StringRef NodeID<Expr>::value; 187 constexpr StringRef NodeID<Decl>::value; 188 189 template <class T, 190 class F = const Stmt *(ExprMutationAnalyzer::Analyzer::*)(const T *)> 191 const Stmt *tryEachMatch(ArrayRef<ast_matchers::BoundNodes> Matches, 192 ExprMutationAnalyzer::Analyzer *Analyzer, F Finder) { 193 const StringRef ID = NodeID<T>::value; 194 for (const auto &Nodes : Matches) { 195 if (const Stmt *S = (Analyzer->*Finder)(Nodes.getNodeAs<T>(ID))) 196 return S; 197 } 198 return nullptr; 199 } 200 201 } // namespace 202 203 const Stmt *ExprMutationAnalyzer::Analyzer::findMutation(const Expr *Exp) { 204 return findMutationMemoized( 205 Exp, 206 {&ExprMutationAnalyzer::Analyzer::findDirectMutation, 207 &ExprMutationAnalyzer::Analyzer::findMemberMutation, 208 &ExprMutationAnalyzer::Analyzer::findArrayElementMutation, 209 &ExprMutationAnalyzer::Analyzer::findCastMutation, 210 &ExprMutationAnalyzer::Analyzer::findRangeLoopMutation, 211 &ExprMutationAnalyzer::Analyzer::findReferenceMutation, 212 &ExprMutationAnalyzer::Analyzer::findFunctionArgMutation}, 213 Memorized.Results); 214 } 215 216 const Stmt *ExprMutationAnalyzer::Analyzer::findMutation(const Decl *Dec) { 217 return tryEachDeclRef(Dec, &ExprMutationAnalyzer::Analyzer::findMutation); 218 } 219 220 const Stmt * 221 ExprMutationAnalyzer::Analyzer::findPointeeMutation(const Expr *Exp) { 222 return findMutationMemoized(Exp, {/*TODO*/}, Memorized.PointeeResults); 223 } 224 225 const Stmt * 226 ExprMutationAnalyzer::Analyzer::findPointeeMutation(const Decl *Dec) { 227 return tryEachDeclRef(Dec, 228 &ExprMutationAnalyzer::Analyzer::findPointeeMutation); 229 } 230 231 const Stmt *ExprMutationAnalyzer::Analyzer::findMutationMemoized( 232 const Expr *Exp, llvm::ArrayRef<MutationFinder> Finders, 233 Memoized::ResultMap &MemoizedResults) { 234 const auto Memoized = MemoizedResults.find(Exp); 235 if (Memoized != MemoizedResults.end()) 236 return Memoized->second; 237 238 // Assume Exp is not mutated before analyzing Exp. 239 MemoizedResults[Exp] = nullptr; 240 if (isUnevaluated(Exp)) 241 return nullptr; 242 243 for (const auto &Finder : Finders) { 244 if (const Stmt *S = (this->*Finder)(Exp)) 245 return MemoizedResults[Exp] = S; 246 } 247 248 return nullptr; 249 } 250 251 const Stmt * 252 ExprMutationAnalyzer::Analyzer::tryEachDeclRef(const Decl *Dec, 253 MutationFinder Finder) { 254 const auto Refs = match( 255 findAll( 256 declRefExpr(to( 257 // `Dec` or a binding if `Dec` is a decomposition. 258 anyOf(equalsNode(Dec), 259 bindingDecl(forDecomposition(equalsNode(Dec)))) 260 // 261 )) 262 .bind(NodeID<Expr>::value)), 263 Stm, Context); 264 for (const auto &RefNodes : Refs) { 265 const auto *E = RefNodes.getNodeAs<Expr>(NodeID<Expr>::value); 266 if ((this->*Finder)(E)) 267 return E; 268 } 269 return nullptr; 270 } 271 272 bool ExprMutationAnalyzer::Analyzer::isUnevaluated(const Stmt *Exp, 273 const Stmt &Stm, 274 ASTContext &Context) { 275 return selectFirst<Stmt>( 276 NodeID<Expr>::value, 277 match( 278 findFirst( 279 stmt(canResolveToExpr(Exp), 280 anyOf( 281 // `Exp` is part of the underlying expression of 282 // decltype/typeof if it has an ancestor of 283 // typeLoc. 284 hasAncestor(typeLoc(unless( 285 hasAncestor(unaryExprOrTypeTraitExpr())))), 286 hasAncestor(expr(anyOf( 287 // `UnaryExprOrTypeTraitExpr` is unevaluated 288 // unless it's sizeof on VLA. 289 unaryExprOrTypeTraitExpr(unless(sizeOfExpr( 290 hasArgumentOfType(variableArrayType())))), 291 // `CXXTypeidExpr` is unevaluated unless it's 292 // applied to an expression of glvalue of 293 // polymorphic class type. 294 cxxTypeidExpr( 295 unless(isPotentiallyEvaluated())), 296 // The controlling expression of 297 // `GenericSelectionExpr` is unevaluated. 298 genericSelectionExpr(hasControllingExpr( 299 hasDescendant(equalsNode(Exp)))), 300 cxxNoexceptExpr()))))) 301 .bind(NodeID<Expr>::value)), 302 Stm, Context)) != nullptr; 303 } 304 305 bool ExprMutationAnalyzer::Analyzer::isUnevaluated(const Expr *Exp) { 306 return isUnevaluated(Exp, Stm, Context); 307 } 308 309 const Stmt * 310 ExprMutationAnalyzer::Analyzer::findExprMutation(ArrayRef<BoundNodes> Matches) { 311 return tryEachMatch<Expr>(Matches, this, 312 &ExprMutationAnalyzer::Analyzer::findMutation); 313 } 314 315 const Stmt * 316 ExprMutationAnalyzer::Analyzer::findDeclMutation(ArrayRef<BoundNodes> Matches) { 317 return tryEachMatch<Decl>(Matches, this, 318 &ExprMutationAnalyzer::Analyzer::findMutation); 319 } 320 321 const Stmt *ExprMutationAnalyzer::Analyzer::findExprPointeeMutation( 322 ArrayRef<ast_matchers::BoundNodes> Matches) { 323 return tryEachMatch<Expr>( 324 Matches, this, &ExprMutationAnalyzer::Analyzer::findPointeeMutation); 325 } 326 327 const Stmt *ExprMutationAnalyzer::Analyzer::findDeclPointeeMutation( 328 ArrayRef<ast_matchers::BoundNodes> Matches) { 329 return tryEachMatch<Decl>( 330 Matches, this, &ExprMutationAnalyzer::Analyzer::findPointeeMutation); 331 } 332 333 const Stmt * 334 ExprMutationAnalyzer::Analyzer::findDirectMutation(const Expr *Exp) { 335 // LHS of any assignment operators. 336 const auto AsAssignmentLhs = 337 binaryOperator(isAssignmentOperator(), hasLHS(canResolveToExpr(Exp))); 338 339 // Operand of increment/decrement operators. 340 const auto AsIncDecOperand = 341 unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")), 342 hasUnaryOperand(canResolveToExpr(Exp))); 343 344 // Invoking non-const member function. 345 // A member function is assumed to be non-const when it is unresolved. 346 const auto NonConstMethod = cxxMethodDecl(unless(isConst())); 347 348 const auto AsNonConstThis = expr(anyOf( 349 cxxMemberCallExpr(on(canResolveToExpr(Exp)), unless(isConstCallee())), 350 cxxOperatorCallExpr(callee(NonConstMethod), 351 hasArgument(0, canResolveToExpr(Exp))), 352 // In case of a templated type, calling overloaded operators is not 353 // resolved and modelled as `binaryOperator` on a dependent type. 354 // Such instances are considered a modification, because they can modify 355 // in different instantiations of the template. 356 binaryOperator(isTypeDependent(), 357 hasEitherOperand(ignoringImpCasts(canResolveToExpr(Exp)))), 358 // A fold expression may contain `Exp` as it's initializer. 359 // We don't know if the operator modifies `Exp` because the 360 // operator is type dependent due to the parameter pack. 361 cxxFoldExpr(hasFoldInit(ignoringImpCasts(canResolveToExpr(Exp)))), 362 // Within class templates and member functions the member expression might 363 // not be resolved. In that case, the `callExpr` is considered to be a 364 // modification. 365 callExpr(callee(expr(anyOf( 366 unresolvedMemberExpr(hasObjectExpression(canResolveToExpr(Exp))), 367 cxxDependentScopeMemberExpr( 368 hasObjectExpression(canResolveToExpr(Exp))))))), 369 // Match on a call to a known method, but the call itself is type 370 // dependent (e.g. `vector<T> v; v.push(T{});` in a templated function). 371 callExpr(allOf( 372 isTypeDependent(), 373 callee(memberExpr(hasDeclaration(NonConstMethod), 374 hasObjectExpression(canResolveToExpr(Exp)))))))); 375 376 // Taking address of 'Exp'. 377 // We're assuming 'Exp' is mutated as soon as its address is taken, though in 378 // theory we can follow the pointer and see whether it escaped `Stm` or is 379 // dereferenced and then mutated. This is left for future improvements. 380 const auto AsAmpersandOperand = 381 unaryOperator(hasOperatorName("&"), 382 // A NoOp implicit cast is adding const. 383 unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))), 384 hasUnaryOperand(canResolveToExpr(Exp))); 385 const auto AsPointerFromArrayDecay = castExpr( 386 hasCastKind(CK_ArrayToPointerDecay), 387 unless(hasParent(arraySubscriptExpr())), has(canResolveToExpr(Exp))); 388 // Treat calling `operator->()` of move-only classes as taking address. 389 // These are typically smart pointers with unique ownership so we treat 390 // mutation of pointee as mutation of the smart pointer itself. 391 const auto AsOperatorArrowThis = cxxOperatorCallExpr( 392 hasOverloadedOperatorName("->"), 393 callee( 394 cxxMethodDecl(ofClass(isMoveOnly()), returns(nonConstPointerType()))), 395 argumentCountIs(1), hasArgument(0, canResolveToExpr(Exp))); 396 397 // Used as non-const-ref argument when calling a function. 398 // An argument is assumed to be non-const-ref when the function is unresolved. 399 // Instantiated template functions are not handled here but in 400 // findFunctionArgMutation which has additional smarts for handling forwarding 401 // references. 402 const auto NonConstRefParam = forEachArgumentWithParamType( 403 anyOf(canResolveToExpr(Exp), 404 memberExpr(hasObjectExpression(canResolveToExpr(Exp)))), 405 nonConstReferenceType()); 406 const auto NotInstantiated = unless(hasDeclaration(isInstantiated())); 407 408 const auto AsNonConstRefArg = 409 anyOf(callExpr(NonConstRefParam, NotInstantiated), 410 cxxConstructExpr(NonConstRefParam, NotInstantiated), 411 // If the call is type-dependent, we can't properly process any 412 // argument because required type conversions and implicit casts 413 // will be inserted only after specialization. 414 callExpr(isTypeDependent(), hasAnyArgument(canResolveToExpr(Exp))), 415 cxxUnresolvedConstructExpr(hasAnyArgument(canResolveToExpr(Exp))), 416 // Previous False Positive in the following Code: 417 // `template <typename T> void f() { int i = 42; new Type<T>(i); }` 418 // Where the constructor of `Type` takes its argument as reference. 419 // The AST does not resolve in a `cxxConstructExpr` because it is 420 // type-dependent. 421 parenListExpr(hasDescendant(expr(canResolveToExpr(Exp)))), 422 // If the initializer is for a reference type, there is no cast for 423 // the variable. Values are cast to RValue first. 424 initListExpr(hasAnyInit(expr(canResolveToExpr(Exp))))); 425 426 // Captured by a lambda by reference. 427 // If we're initializing a capture with 'Exp' directly then we're initializing 428 // a reference capture. 429 // For value captures there will be an ImplicitCastExpr <LValueToRValue>. 430 const auto AsLambdaRefCaptureInit = lambdaExpr(hasCaptureInit(Exp)); 431 432 // Returned as non-const-ref. 433 // If we're returning 'Exp' directly then it's returned as non-const-ref. 434 // For returning by value there will be an ImplicitCastExpr <LValueToRValue>. 435 // For returning by const-ref there will be an ImplicitCastExpr <NoOp> (for 436 // adding const.) 437 const auto AsNonConstRefReturn = 438 returnStmt(hasReturnValue(canResolveToExpr(Exp))); 439 440 // It is used as a non-const-reference for initializing a range-for loop. 441 const auto AsNonConstRefRangeInit = cxxForRangeStmt(hasRangeInit(declRefExpr( 442 allOf(canResolveToExpr(Exp), hasType(nonConstReferenceType()))))); 443 444 const auto Matches = match( 445 traverse( 446 TK_AsIs, 447 findFirst(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand, AsNonConstThis, 448 AsAmpersandOperand, AsPointerFromArrayDecay, 449 AsOperatorArrowThis, AsNonConstRefArg, 450 AsLambdaRefCaptureInit, AsNonConstRefReturn, 451 AsNonConstRefRangeInit)) 452 .bind("stmt"))), 453 Stm, Context); 454 return selectFirst<Stmt>("stmt", Matches); 455 } 456 457 const Stmt * 458 ExprMutationAnalyzer::Analyzer::findMemberMutation(const Expr *Exp) { 459 // Check whether any member of 'Exp' is mutated. 460 const auto MemberExprs = match( 461 findAll(expr(anyOf(memberExpr(hasObjectExpression(canResolveToExpr(Exp))), 462 cxxDependentScopeMemberExpr( 463 hasObjectExpression(canResolveToExpr(Exp))), 464 binaryOperator(hasOperatorName(".*"), 465 hasLHS(equalsNode(Exp))))) 466 .bind(NodeID<Expr>::value)), 467 Stm, Context); 468 return findExprMutation(MemberExprs); 469 } 470 471 const Stmt * 472 ExprMutationAnalyzer::Analyzer::findArrayElementMutation(const Expr *Exp) { 473 // Check whether any element of an array is mutated. 474 const auto SubscriptExprs = match( 475 findAll(arraySubscriptExpr( 476 anyOf(hasBase(canResolveToExpr(Exp)), 477 hasBase(implicitCastExpr(allOf( 478 hasCastKind(CK_ArrayToPointerDecay), 479 hasSourceExpression(canResolveToExpr(Exp))))))) 480 .bind(NodeID<Expr>::value)), 481 Stm, Context); 482 return findExprMutation(SubscriptExprs); 483 } 484 485 const Stmt *ExprMutationAnalyzer::Analyzer::findCastMutation(const Expr *Exp) { 486 // If the 'Exp' is explicitly casted to a non-const reference type the 487 // 'Exp' is considered to be modified. 488 const auto ExplicitCast = 489 match(findFirst(stmt(castExpr(hasSourceExpression(canResolveToExpr(Exp)), 490 explicitCastExpr(hasDestinationType( 491 nonConstReferenceType())))) 492 .bind("stmt")), 493 Stm, Context); 494 495 if (const auto *CastStmt = selectFirst<Stmt>("stmt", ExplicitCast)) 496 return CastStmt; 497 498 // If 'Exp' is casted to any non-const reference type, check the castExpr. 499 const auto Casts = match( 500 findAll(expr(castExpr(hasSourceExpression(canResolveToExpr(Exp)), 501 anyOf(explicitCastExpr(hasDestinationType( 502 nonConstReferenceType())), 503 implicitCastExpr(hasImplicitDestinationType( 504 nonConstReferenceType()))))) 505 .bind(NodeID<Expr>::value)), 506 Stm, Context); 507 508 if (const Stmt *S = findExprMutation(Casts)) 509 return S; 510 // Treat std::{move,forward} as cast. 511 const auto Calls = 512 match(findAll(callExpr(callee(namedDecl( 513 hasAnyName("::std::move", "::std::forward"))), 514 hasArgument(0, canResolveToExpr(Exp))) 515 .bind("expr")), 516 Stm, Context); 517 return findExprMutation(Calls); 518 } 519 520 const Stmt * 521 ExprMutationAnalyzer::Analyzer::findRangeLoopMutation(const Expr *Exp) { 522 // Keep the ordering for the specific initialization matches to happen first, 523 // because it is cheaper to match all potential modifications of the loop 524 // variable. 525 526 // The range variable is a reference to a builtin array. In that case the 527 // array is considered modified if the loop-variable is a non-const reference. 528 const auto DeclStmtToNonRefToArray = declStmt(hasSingleDecl(varDecl(hasType( 529 hasUnqualifiedDesugaredType(referenceType(pointee(arrayType()))))))); 530 const auto RefToArrayRefToElements = match( 531 findFirst(stmt(cxxForRangeStmt( 532 hasLoopVariable( 533 varDecl(anyOf(hasType(nonConstReferenceType()), 534 hasType(nonConstPointerType()))) 535 .bind(NodeID<Decl>::value)), 536 hasRangeStmt(DeclStmtToNonRefToArray), 537 hasRangeInit(canResolveToExpr(Exp)))) 538 .bind("stmt")), 539 Stm, Context); 540 541 if (const auto *BadRangeInitFromArray = 542 selectFirst<Stmt>("stmt", RefToArrayRefToElements)) 543 return BadRangeInitFromArray; 544 545 // Small helper to match special cases in range-for loops. 546 // 547 // It is possible that containers do not provide a const-overload for their 548 // iterator accessors. If this is the case, the variable is used non-const 549 // no matter what happens in the loop. This requires special detection as it 550 // is then faster to find all mutations of the loop variable. 551 // It aims at a different modification as well. 552 const auto HasAnyNonConstIterator = 553 anyOf(allOf(hasMethod(allOf(hasName("begin"), unless(isConst()))), 554 unless(hasMethod(allOf(hasName("begin"), isConst())))), 555 allOf(hasMethod(allOf(hasName("end"), unless(isConst()))), 556 unless(hasMethod(allOf(hasName("end"), isConst()))))); 557 558 const auto DeclStmtToNonConstIteratorContainer = declStmt( 559 hasSingleDecl(varDecl(hasType(hasUnqualifiedDesugaredType(referenceType( 560 pointee(hasDeclaration(cxxRecordDecl(HasAnyNonConstIterator))))))))); 561 562 const auto RefToContainerBadIterators = match( 563 findFirst(stmt(cxxForRangeStmt(allOf( 564 hasRangeStmt(DeclStmtToNonConstIteratorContainer), 565 hasRangeInit(canResolveToExpr(Exp))))) 566 .bind("stmt")), 567 Stm, Context); 568 569 if (const auto *BadIteratorsContainer = 570 selectFirst<Stmt>("stmt", RefToContainerBadIterators)) 571 return BadIteratorsContainer; 572 573 // If range for looping over 'Exp' with a non-const reference loop variable, 574 // check all declRefExpr of the loop variable. 575 const auto LoopVars = 576 match(findAll(cxxForRangeStmt( 577 hasLoopVariable(varDecl(hasType(nonConstReferenceType())) 578 .bind(NodeID<Decl>::value)), 579 hasRangeInit(canResolveToExpr(Exp)))), 580 Stm, Context); 581 return findDeclMutation(LoopVars); 582 } 583 584 const Stmt * 585 ExprMutationAnalyzer::Analyzer::findReferenceMutation(const Expr *Exp) { 586 // Follow non-const reference returned by `operator*()` of move-only classes. 587 // These are typically smart pointers with unique ownership so we treat 588 // mutation of pointee as mutation of the smart pointer itself. 589 const auto Ref = match( 590 findAll(cxxOperatorCallExpr( 591 hasOverloadedOperatorName("*"), 592 callee(cxxMethodDecl(ofClass(isMoveOnly()), 593 returns(nonConstReferenceType()))), 594 argumentCountIs(1), hasArgument(0, canResolveToExpr(Exp))) 595 .bind(NodeID<Expr>::value)), 596 Stm, Context); 597 if (const Stmt *S = findExprMutation(Ref)) 598 return S; 599 600 // If 'Exp' is bound to a non-const reference, check all declRefExpr to that. 601 const auto Refs = match( 602 stmt(forEachDescendant( 603 varDecl(hasType(nonConstReferenceType()), 604 hasInitializer(anyOf( 605 canResolveToExpr(Exp), 606 memberExpr(hasObjectExpression(canResolveToExpr(Exp))))), 607 hasParent(declStmt().bind("stmt")), 608 // Don't follow the reference in range statement, we've 609 // handled that separately. 610 unless(hasParent(declStmt(hasParent(cxxForRangeStmt( 611 hasRangeStmt(equalsBoundNode("stmt")))))))) 612 .bind(NodeID<Decl>::value))), 613 Stm, Context); 614 return findDeclMutation(Refs); 615 } 616 617 const Stmt * 618 ExprMutationAnalyzer::Analyzer::findFunctionArgMutation(const Expr *Exp) { 619 const auto NonConstRefParam = forEachArgumentWithParam( 620 canResolveToExpr(Exp), 621 parmVarDecl(hasType(nonConstReferenceType())).bind("parm")); 622 const auto IsInstantiated = hasDeclaration(isInstantiated()); 623 const auto FuncDecl = hasDeclaration(functionDecl().bind("func")); 624 const auto Matches = match( 625 traverse( 626 TK_AsIs, 627 findAll( 628 expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl, 629 unless(callee(namedDecl(hasAnyName( 630 "::std::move", "::std::forward"))))), 631 cxxConstructExpr(NonConstRefParam, IsInstantiated, 632 FuncDecl))) 633 .bind(NodeID<Expr>::value))), 634 Stm, Context); 635 for (const auto &Nodes : Matches) { 636 const auto *Exp = Nodes.getNodeAs<Expr>(NodeID<Expr>::value); 637 const auto *Func = Nodes.getNodeAs<FunctionDecl>("func"); 638 if (!Func->getBody() || !Func->getPrimaryTemplate()) 639 return Exp; 640 641 const auto *Parm = Nodes.getNodeAs<ParmVarDecl>("parm"); 642 const ArrayRef<ParmVarDecl *> AllParams = 643 Func->getPrimaryTemplate()->getTemplatedDecl()->parameters(); 644 QualType ParmType = 645 AllParams[std::min<size_t>(Parm->getFunctionScopeIndex(), 646 AllParams.size() - 1)] 647 ->getType(); 648 if (const auto *T = ParmType->getAs<PackExpansionType>()) 649 ParmType = T->getPattern(); 650 651 // If param type is forwarding reference, follow into the function 652 // definition and see whether the param is mutated inside. 653 if (const auto *RefType = ParmType->getAs<RValueReferenceType>()) { 654 if (!RefType->getPointeeType().getQualifiers() && 655 RefType->getPointeeType()->getAs<TemplateTypeParmType>()) { 656 FunctionParmMutationAnalyzer *Analyzer = 657 FunctionParmMutationAnalyzer::getFunctionParmMutationAnalyzer( 658 *Func, Context, Memorized); 659 if (Analyzer->findMutation(Parm)) 660 return Exp; 661 continue; 662 } 663 } 664 // Not forwarding reference. 665 return Exp; 666 } 667 return nullptr; 668 } 669 670 FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer( 671 const FunctionDecl &Func, ASTContext &Context, 672 ExprMutationAnalyzer::Memoized &Memorized) 673 : BodyAnalyzer(*Func.getBody(), Context, Memorized) { 674 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(&Func)) { 675 // CXXCtorInitializer might also mutate Param but they're not part of 676 // function body, check them eagerly here since they're typically trivial. 677 for (const CXXCtorInitializer *Init : Ctor->inits()) { 678 ExprMutationAnalyzer::Analyzer InitAnalyzer(*Init->getInit(), Context, 679 Memorized); 680 for (const ParmVarDecl *Parm : Ctor->parameters()) { 681 if (Results.contains(Parm)) 682 continue; 683 if (const Stmt *S = InitAnalyzer.findMutation(Parm)) 684 Results[Parm] = S; 685 } 686 } 687 } 688 } 689 690 const Stmt * 691 FunctionParmMutationAnalyzer::findMutation(const ParmVarDecl *Parm) { 692 const auto Memoized = Results.find(Parm); 693 if (Memoized != Results.end()) 694 return Memoized->second; 695 // To handle call A -> call B -> call A. Assume parameters of A is not mutated 696 // before analyzing parameters of A. Then when analyzing the second "call A", 697 // FunctionParmMutationAnalyzer can use this memoized value to avoid infinite 698 // recursion. 699 Results[Parm] = nullptr; 700 if (const Stmt *S = BodyAnalyzer.findMutation(Parm)) 701 return Results[Parm] = S; 702 return Results[Parm]; 703 } 704 705 } // namespace clang 706