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