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