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