1 //===--- LoopConvertCheck.cpp - clang-tidy---------------------------------===// 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 9 #include "LoopConvertCheck.h" 10 #include "clang/AST/ASTContext.h" 11 #include "clang/ASTMatchers/ASTMatchFinder.h" 12 #include "clang/Basic/LLVM.h" 13 #include "clang/Basic/LangOptions.h" 14 #include "clang/Basic/SourceLocation.h" 15 #include "clang/Basic/SourceManager.h" 16 #include "clang/Lex/Lexer.h" 17 #include "llvm/ADT/ArrayRef.h" 18 #include "llvm/ADT/SmallVector.h" 19 #include "llvm/ADT/StringRef.h" 20 #include "llvm/ADT/StringSet.h" 21 #include "llvm/Support/Casting.h" 22 #include "llvm/Support/raw_ostream.h" 23 #include <cassert> 24 #include <cstring> 25 #include <optional> 26 #include <tuple> 27 #include <utility> 28 29 using namespace clang::ast_matchers; 30 using namespace llvm; 31 32 namespace clang::tidy { 33 34 template <> struct OptionEnumMapping<modernize::Confidence::Level> { 35 static llvm::ArrayRef<std::pair<modernize::Confidence::Level, StringRef>> 36 getEnumMapping() { 37 static constexpr std::pair<modernize::Confidence::Level, StringRef> 38 Mapping[] = {{modernize::Confidence::CL_Reasonable, "reasonable"}, 39 {modernize::Confidence::CL_Safe, "safe"}, 40 {modernize::Confidence::CL_Risky, "risky"}}; 41 return {Mapping}; 42 } 43 }; 44 45 template <> struct OptionEnumMapping<modernize::VariableNamer::NamingStyle> { 46 static llvm::ArrayRef< 47 std::pair<modernize::VariableNamer::NamingStyle, StringRef>> 48 getEnumMapping() { 49 static constexpr std::pair<modernize::VariableNamer::NamingStyle, StringRef> 50 Mapping[] = {{modernize::VariableNamer::NS_CamelCase, "CamelCase"}, 51 {modernize::VariableNamer::NS_CamelBack, "camelBack"}, 52 {modernize::VariableNamer::NS_LowerCase, "lower_case"}, 53 {modernize::VariableNamer::NS_UpperCase, "UPPER_CASE"}}; 54 return {Mapping}; 55 } 56 }; 57 58 namespace modernize { 59 60 static const char LoopNameArray[] = "forLoopArray"; 61 static const char LoopNameIterator[] = "forLoopIterator"; 62 static const char LoopNameReverseIterator[] = "forLoopReverseIterator"; 63 static const char LoopNamePseudoArray[] = "forLoopPseudoArray"; 64 static const char ConditionBoundName[] = "conditionBound"; 65 static const char InitVarName[] = "initVar"; 66 static const char BeginCallName[] = "beginCall"; 67 static const char EndCallName[] = "endCall"; 68 static const char EndVarName[] = "endVar"; 69 static const char DerefByValueResultName[] = "derefByValueResult"; 70 static const char DerefByRefResultName[] = "derefByRefResult"; 71 static const llvm::StringSet<> MemberNames{"begin", "cbegin", "rbegin", 72 "crbegin", "end", "cend", 73 "rend", "crend", "size"}; 74 static const llvm::StringSet<> ADLNames{"begin", "cbegin", "rbegin", 75 "crbegin", "end", "cend", 76 "rend", "crend", "size"}; 77 static const llvm::StringSet<> StdNames{ 78 "std::begin", "std::cbegin", "std::rbegin", "std::crbegin", "std::end", 79 "std::cend", "std::rend", "std::crend", "std::size"}; 80 81 static StatementMatcher integerComparisonMatcher() { 82 return expr(ignoringParenImpCasts( 83 declRefExpr(to(varDecl(equalsBoundNode(InitVarName)))))); 84 } 85 86 static DeclarationMatcher initToZeroMatcher() { 87 return varDecl( 88 hasInitializer(ignoringParenImpCasts(integerLiteral(equals(0))))) 89 .bind(InitVarName); 90 } 91 92 static StatementMatcher incrementVarMatcher() { 93 return declRefExpr(to(varDecl(equalsBoundNode(InitVarName)))); 94 } 95 96 static StatementMatcher 97 arrayConditionMatcher(internal::Matcher<Expr> LimitExpr) { 98 return binaryOperator( 99 anyOf(allOf(hasOperatorName("<"), hasLHS(integerComparisonMatcher()), 100 hasRHS(LimitExpr)), 101 allOf(hasOperatorName(">"), hasLHS(LimitExpr), 102 hasRHS(integerComparisonMatcher())), 103 allOf(hasOperatorName("!="), 104 hasOperands(integerComparisonMatcher(), LimitExpr)))); 105 } 106 107 /// The matcher for loops over arrays. 108 /// \code 109 /// for (int i = 0; i < 3 + 2; ++i) { ... } 110 /// \endcode 111 /// The following string identifiers are bound to these parts of the AST: 112 /// ConditionBoundName: '3 + 2' (as an Expr) 113 /// InitVarName: 'i' (as a VarDecl) 114 /// LoopName: The entire for loop (as a ForStmt) 115 /// 116 /// Client code will need to make sure that: 117 /// - The index variable is only used as an array index. 118 /// - All arrays indexed by the loop are the same. 119 StatementMatcher makeArrayLoopMatcher() { 120 StatementMatcher ArrayBoundMatcher = 121 expr(hasType(isInteger())).bind(ConditionBoundName); 122 123 return forStmt(unless(isInTemplateInstantiation()), 124 hasLoopInit(declStmt(hasSingleDecl(initToZeroMatcher()))), 125 hasCondition(arrayConditionMatcher(ArrayBoundMatcher)), 126 hasIncrement( 127 unaryOperator(hasOperatorName("++"), 128 hasUnaryOperand(incrementVarMatcher())))) 129 .bind(LoopNameArray); 130 } 131 132 /// The matcher used for iterator-based for loops. 133 /// 134 /// This matcher is more flexible than array-based loops. It will match 135 /// catch loops of the following textual forms (regardless of whether the 136 /// iterator type is actually a pointer type or a class type): 137 /// 138 /// \code 139 /// for (containerType::iterator it = container.begin(), 140 /// e = createIterator(); it != e; ++it) { ... } 141 /// for (containerType::iterator it = container.begin(); 142 /// it != anotherContainer.end(); ++it) { ... } 143 /// for (containerType::iterator it = begin(container), 144 /// e = end(container); it != e; ++it) { ... } 145 /// for (containerType::iterator it = std::begin(container), 146 /// e = std::end(container); it != e; ++it) { ... } 147 /// \endcode 148 /// The following string identifiers are bound to the parts of the AST: 149 /// InitVarName: 'it' (as a VarDecl) 150 /// LoopName: The entire for loop (as a ForStmt) 151 /// In the first example only: 152 /// EndVarName: 'e' (as a VarDecl) 153 /// In the second example only: 154 /// EndCallName: 'container.end()' (as a CXXMemberCallExpr) 155 /// In the third/fourth examples: 156 /// 'end(container)' or 'std::end(container)' (as a CallExpr) 157 /// 158 /// Client code will need to make sure that: 159 /// - The two containers on which 'begin' and 'end' are called are the same. 160 StatementMatcher makeIteratorLoopMatcher(bool IsReverse) { 161 162 auto BeginNameMatcher = IsReverse ? hasAnyName("rbegin", "crbegin") 163 : hasAnyName("begin", "cbegin"); 164 auto BeginNameMatcherStd = IsReverse 165 ? hasAnyName("::std::rbegin", "::std::crbegin") 166 : hasAnyName("::std::begin", "::std::cbegin"); 167 168 auto EndNameMatcher = 169 IsReverse ? hasAnyName("rend", "crend") : hasAnyName("end", "cend"); 170 auto EndNameMatcherStd = IsReverse ? hasAnyName("::std::rend", "::std::crend") 171 : hasAnyName("::std::end", "::std::cend"); 172 173 StatementMatcher BeginCallMatcher = 174 expr(anyOf(cxxMemberCallExpr(argumentCountIs(0), 175 callee(cxxMethodDecl(BeginNameMatcher))), 176 callExpr(argumentCountIs(1), 177 callee(functionDecl(BeginNameMatcher)), usesADL()), 178 callExpr(argumentCountIs(1), 179 callee(functionDecl(BeginNameMatcherStd))))) 180 .bind(BeginCallName); 181 182 DeclarationMatcher InitDeclMatcher = 183 varDecl(hasInitializer(anyOf(ignoringParenImpCasts(BeginCallMatcher), 184 materializeTemporaryExpr( 185 ignoringParenImpCasts(BeginCallMatcher)), 186 hasDescendant(BeginCallMatcher)))) 187 .bind(InitVarName); 188 189 DeclarationMatcher EndDeclMatcher = 190 varDecl(hasInitializer(anything())).bind(EndVarName); 191 192 StatementMatcher EndCallMatcher = expr(anyOf( 193 cxxMemberCallExpr(argumentCountIs(0), 194 callee(cxxMethodDecl(EndNameMatcher))), 195 callExpr(argumentCountIs(1), callee(functionDecl(EndNameMatcher)), 196 usesADL()), 197 callExpr(argumentCountIs(1), callee(functionDecl(EndNameMatcherStd))))); 198 199 StatementMatcher IteratorBoundMatcher = 200 expr(anyOf(ignoringParenImpCasts( 201 declRefExpr(to(varDecl(equalsBoundNode(EndVarName))))), 202 ignoringParenImpCasts(expr(EndCallMatcher).bind(EndCallName)), 203 materializeTemporaryExpr(ignoringParenImpCasts( 204 expr(EndCallMatcher).bind(EndCallName))))); 205 206 StatementMatcher IteratorComparisonMatcher = expr(ignoringParenImpCasts( 207 declRefExpr(to(varDecl(equalsBoundNode(InitVarName)))))); 208 209 // This matcher tests that a declaration is a CXXRecordDecl that has an 210 // overloaded operator*(). If the operator*() returns by value instead of by 211 // reference then the return type is tagged with DerefByValueResultName. 212 internal::Matcher<VarDecl> TestDerefReturnsByValue = 213 hasType(hasUnqualifiedDesugaredType( 214 recordType(hasDeclaration(cxxRecordDecl(hasMethod(cxxMethodDecl( 215 hasOverloadedOperatorName("*"), 216 anyOf( 217 // Tag the return type if it's by value. 218 returns(qualType(unless(hasCanonicalType(referenceType()))) 219 .bind(DerefByValueResultName)), 220 returns( 221 // Skip loops where the iterator's operator* returns an 222 // rvalue reference. This is just weird. 223 qualType(unless(hasCanonicalType(rValueReferenceType()))) 224 .bind(DerefByRefResultName)))))))))); 225 226 return forStmt( 227 unless(isInTemplateInstantiation()), 228 hasLoopInit(anyOf(declStmt(declCountIs(2), 229 containsDeclaration(0, InitDeclMatcher), 230 containsDeclaration(1, EndDeclMatcher)), 231 declStmt(hasSingleDecl(InitDeclMatcher)))), 232 hasCondition(ignoringImplicit(binaryOperation( 233 hasOperatorName("!="), hasOperands(IteratorComparisonMatcher, 234 IteratorBoundMatcher)))), 235 hasIncrement(anyOf( 236 unaryOperator(hasOperatorName("++"), 237 hasUnaryOperand(declRefExpr( 238 to(varDecl(equalsBoundNode(InitVarName)))))), 239 cxxOperatorCallExpr( 240 hasOverloadedOperatorName("++"), 241 hasArgument(0, declRefExpr(to( 242 varDecl(equalsBoundNode(InitVarName), 243 TestDerefReturnsByValue)))))))) 244 .bind(IsReverse ? LoopNameReverseIterator : LoopNameIterator); 245 } 246 247 /// The matcher used for array-like containers (pseudoarrays). 248 /// 249 /// This matcher is more flexible than array-based loops. It will match 250 /// loops of the following textual forms (regardless of whether the 251 /// iterator type is actually a pointer type or a class type): 252 /// 253 /// \code 254 /// for (int i = 0, j = container.size(); i < j; ++i) { ... } 255 /// for (int i = 0; i < container.size(); ++i) { ... } 256 /// for (int i = 0; i < size(container); ++i) { ... } 257 /// \endcode 258 /// The following string identifiers are bound to the parts of the AST: 259 /// InitVarName: 'i' (as a VarDecl) 260 /// LoopName: The entire for loop (as a ForStmt) 261 /// In the first example only: 262 /// EndVarName: 'j' (as a VarDecl) 263 /// In the second example only: 264 /// EndCallName: 'container.size()' (as a CXXMemberCallExpr) or 265 /// 'size(contaner)' (as a CallExpr) 266 /// 267 /// Client code will need to make sure that: 268 /// - The containers on which 'size()' is called is the container indexed. 269 /// - The index variable is only used in overloaded operator[] or 270 /// container.at(). 271 /// - The container's iterators would not be invalidated during the loop. 272 StatementMatcher makePseudoArrayLoopMatcher() { 273 // Test that the incoming type has a record declaration that has methods 274 // called 'begin' and 'end'. If the incoming type is const, then make sure 275 // these methods are also marked const. 276 // 277 // FIXME: To be completely thorough this matcher should also ensure the 278 // return type of begin/end is an iterator that dereferences to the same as 279 // what operator[] or at() returns. Such a test isn't likely to fail except 280 // for pathological cases. 281 // 282 // FIXME: Also, a record doesn't necessarily need begin() and end(). Free 283 // functions called begin() and end() taking the container as an argument 284 // are also allowed. 285 TypeMatcher RecordWithBeginEnd = qualType(anyOf( 286 qualType(isConstQualified(), 287 hasUnqualifiedDesugaredType(recordType(hasDeclaration( 288 cxxRecordDecl(isSameOrDerivedFrom(cxxRecordDecl( 289 hasMethod(cxxMethodDecl(hasName("begin"), isConst())), 290 hasMethod(cxxMethodDecl(hasName("end"), 291 isConst())))))) // hasDeclaration 292 ))), // qualType 293 qualType(unless(isConstQualified()), 294 hasUnqualifiedDesugaredType(recordType(hasDeclaration( 295 cxxRecordDecl(isSameOrDerivedFrom(cxxRecordDecl( 296 hasMethod(hasName("begin")), 297 hasMethod(hasName("end"))))))))) // qualType 298 )); 299 300 StatementMatcher SizeCallMatcher = expr(anyOf( 301 cxxMemberCallExpr(argumentCountIs(0), 302 callee(cxxMethodDecl(hasAnyName("size", "length"))), 303 on(anyOf(hasType(pointsTo(RecordWithBeginEnd)), 304 hasType(RecordWithBeginEnd)))), 305 callExpr(argumentCountIs(1), callee(functionDecl(hasName("size"))), 306 usesADL()), 307 callExpr(argumentCountIs(1), 308 callee(functionDecl(hasName("::std::size")))))); 309 310 StatementMatcher EndInitMatcher = 311 expr(anyOf(ignoringParenImpCasts(expr(SizeCallMatcher).bind(EndCallName)), 312 explicitCastExpr(hasSourceExpression(ignoringParenImpCasts( 313 expr(SizeCallMatcher).bind(EndCallName)))))); 314 315 DeclarationMatcher EndDeclMatcher = 316 varDecl(hasInitializer(EndInitMatcher)).bind(EndVarName); 317 318 StatementMatcher IndexBoundMatcher = 319 expr(anyOf(ignoringParenImpCasts( 320 declRefExpr(to(varDecl(equalsBoundNode(EndVarName))))), 321 EndInitMatcher)); 322 323 return forStmt(unless(isInTemplateInstantiation()), 324 hasLoopInit( 325 anyOf(declStmt(declCountIs(2), 326 containsDeclaration(0, initToZeroMatcher()), 327 containsDeclaration(1, EndDeclMatcher)), 328 declStmt(hasSingleDecl(initToZeroMatcher())))), 329 hasCondition(arrayConditionMatcher(IndexBoundMatcher)), 330 hasIncrement( 331 unaryOperator(hasOperatorName("++"), 332 hasUnaryOperand(incrementVarMatcher())))) 333 .bind(LoopNamePseudoArray); 334 } 335 336 enum class IteratorCallKind { 337 ICK_Member, 338 ICK_ADL, 339 ICK_Std, 340 }; 341 342 struct ContainerCall { 343 const Expr *Container; 344 StringRef Name; 345 bool IsArrow; 346 IteratorCallKind CallKind; 347 }; 348 349 // Find the Expr likely initializing an iterator. 350 // 351 // Call is either a CXXMemberCallExpr ('c.begin()') or CallExpr of a free 352 // function with the first argument as a container ('begin(c)'), or nullptr. 353 // Returns at a 3-tuple with the container expr, function name (begin/end/etc), 354 // and whether the call is made through an arrow (->) for CXXMemberCallExprs. 355 // The returned Expr* is nullptr if any of the assumptions are not met. 356 // static std::tuple<const Expr *, StringRef, bool, IteratorCallKind> 357 static std::optional<ContainerCall> getContainerExpr(const Expr *Call) { 358 const Expr *Dug = digThroughConstructorsConversions(Call); 359 360 IteratorCallKind CallKind = IteratorCallKind::ICK_Member; 361 362 if (const auto *TheCall = dyn_cast_or_null<CXXMemberCallExpr>(Dug)) { 363 CallKind = IteratorCallKind::ICK_Member; 364 if (const auto *Member = dyn_cast<MemberExpr>(TheCall->getCallee())) { 365 if (Member->getMemberDecl() == nullptr || 366 !MemberNames.contains(Member->getMemberDecl()->getName())) 367 return std::nullopt; 368 return ContainerCall{TheCall->getImplicitObjectArgument(), 369 Member->getMemberDecl()->getName(), 370 Member->isArrow(), CallKind}; 371 } else { 372 if (TheCall->getDirectCallee() == nullptr || 373 !MemberNames.contains(TheCall->getDirectCallee()->getName())) 374 return std::nullopt; 375 return ContainerCall{TheCall->getArg(0), 376 TheCall->getDirectCallee()->getName(), false, 377 CallKind}; 378 } 379 } else if (const auto *TheCall = dyn_cast_or_null<CallExpr>(Dug)) { 380 if (TheCall->getNumArgs() != 1) 381 return std::nullopt; 382 383 if (TheCall->usesADL()) { 384 if (TheCall->getDirectCallee() == nullptr || 385 !ADLNames.contains(TheCall->getDirectCallee()->getName())) 386 return std::nullopt; 387 CallKind = IteratorCallKind::ICK_ADL; 388 } else { 389 if (!StdNames.contains( 390 TheCall->getDirectCallee()->getQualifiedNameAsString())) 391 return std::nullopt; 392 CallKind = IteratorCallKind::ICK_Std; 393 } 394 395 if (TheCall->getDirectCallee() == nullptr) 396 return std::nullopt; 397 398 return ContainerCall{TheCall->getArg(0), 399 TheCall->getDirectCallee()->getName(), false, 400 CallKind}; 401 } 402 return std::nullopt; 403 } 404 405 /// Determine whether Init appears to be an initializing an iterator. 406 /// 407 /// If it is, returns the object whose begin() or end() method is called, and 408 /// the output parameter isArrow is set to indicate whether the initialization 409 /// is called via . or ->. 410 static std::pair<const Expr *, IteratorCallKind> 411 getContainerFromBeginEndCall(const Expr *Init, bool IsBegin, bool *IsArrow, 412 bool IsReverse) { 413 // FIXME: Maybe allow declaration/initialization outside of the for loop. 414 415 std::optional<ContainerCall> Call = getContainerExpr(Init); 416 if (!Call) 417 return {}; 418 419 *IsArrow = Call->IsArrow; 420 if (!Call->Name.consume_back(IsBegin ? "begin" : "end")) 421 return {}; 422 if (IsReverse && !Call->Name.consume_back("r")) 423 return {}; 424 if (!Call->Name.empty() && !Call->Name.equals("c")) 425 return {}; 426 return std::make_pair(Call->Container, Call->CallKind); 427 } 428 429 /// Determines the container whose begin() and end() functions are called 430 /// for an iterator-based loop. 431 /// 432 /// BeginExpr must be a member call to a function named "begin()", and EndExpr 433 /// must be a member. 434 static const Expr *findContainer(ASTContext *Context, const Expr *BeginExpr, 435 const Expr *EndExpr, 436 bool *ContainerNeedsDereference, 437 bool IsReverse) { 438 // Now that we know the loop variable and test expression, make sure they are 439 // valid. 440 bool BeginIsArrow = false; 441 bool EndIsArrow = false; 442 auto [BeginContainerExpr, BeginCallKind] = getContainerFromBeginEndCall( 443 BeginExpr, /*IsBegin=*/true, &BeginIsArrow, IsReverse); 444 if (!BeginContainerExpr) 445 return nullptr; 446 447 auto [EndContainerExpr, EndCallKind] = getContainerFromBeginEndCall( 448 EndExpr, /*IsBegin=*/false, &EndIsArrow, IsReverse); 449 if (BeginCallKind != EndCallKind) 450 return nullptr; 451 452 // Disallow loops that try evil things like this (note the dot and arrow): 453 // for (IteratorType It = Obj.begin(), E = Obj->end(); It != E; ++It) { } 454 if (!EndContainerExpr || BeginIsArrow != EndIsArrow || 455 !areSameExpr(Context, EndContainerExpr, BeginContainerExpr)) 456 return nullptr; 457 458 *ContainerNeedsDereference = BeginIsArrow; 459 return BeginContainerExpr; 460 } 461 462 /// Obtain the original source code text from a SourceRange. 463 static StringRef getStringFromRange(SourceManager &SourceMgr, 464 const LangOptions &LangOpts, 465 SourceRange Range) { 466 if (SourceMgr.getFileID(Range.getBegin()) != 467 SourceMgr.getFileID(Range.getEnd())) { 468 return {}; // Empty string. 469 } 470 471 return Lexer::getSourceText(CharSourceRange(Range, true), SourceMgr, 472 LangOpts); 473 } 474 475 /// If the given expression is actually a DeclRefExpr or a MemberExpr, 476 /// find and return the underlying ValueDecl; otherwise, return NULL. 477 static const ValueDecl *getReferencedVariable(const Expr *E) { 478 if (const DeclRefExpr *DRE = getDeclRef(E)) 479 return dyn_cast<VarDecl>(DRE->getDecl()); 480 if (const auto *Mem = dyn_cast<MemberExpr>(E->IgnoreParenImpCasts())) 481 return dyn_cast<FieldDecl>(Mem->getMemberDecl()); 482 return nullptr; 483 } 484 485 /// Returns true when the given expression is a member expression 486 /// whose base is `this` (implicitly or not). 487 static bool isDirectMemberExpr(const Expr *E) { 488 if (const auto *Member = dyn_cast<MemberExpr>(E->IgnoreParenImpCasts())) 489 return isa<CXXThisExpr>(Member->getBase()->IgnoreParenImpCasts()); 490 return false; 491 } 492 493 /// Given an expression that represents an usage of an element from the 494 /// containter that we are iterating over, returns false when it can be 495 /// guaranteed this element cannot be modified as a result of this usage. 496 static bool canBeModified(ASTContext *Context, const Expr *E) { 497 if (E->getType().isConstQualified()) 498 return false; 499 auto Parents = Context->getParents(*E); 500 if (Parents.size() != 1) 501 return true; 502 if (const auto *Cast = Parents[0].get<ImplicitCastExpr>()) { 503 if ((Cast->getCastKind() == CK_NoOp && 504 Context->hasSameType(Cast->getType(), E->getType().withConst())) || 505 (Cast->getCastKind() == CK_LValueToRValue && 506 !Cast->getType().isNull() && Cast->getType()->isFundamentalType())) 507 return false; 508 } 509 // FIXME: Make this function more generic. 510 return true; 511 } 512 513 /// Returns true when it can be guaranteed that the elements of the 514 /// container are not being modified. 515 static bool usagesAreConst(ASTContext *Context, const UsageResult &Usages) { 516 for (const Usage &U : Usages) { 517 // Lambda captures are just redeclarations (VarDecl) of the same variable, 518 // not expressions. If we want to know if a variable that is captured by 519 // reference can be modified in an usage inside the lambda's body, we need 520 // to find the expression corresponding to that particular usage, later in 521 // this loop. 522 if (U.Kind != Usage::UK_CaptureByCopy && U.Kind != Usage::UK_CaptureByRef && 523 canBeModified(Context, U.Expression)) 524 return false; 525 } 526 return true; 527 } 528 529 /// Returns true if the elements of the container are never accessed 530 /// by reference. 531 static bool usagesReturnRValues(const UsageResult &Usages) { 532 for (const auto &U : Usages) { 533 if (U.Expression && !U.Expression->isPRValue()) 534 return false; 535 } 536 return true; 537 } 538 539 /// Returns true if the container is const-qualified. 540 static bool containerIsConst(const Expr *ContainerExpr, bool Dereference) { 541 if (const auto *VDec = getReferencedVariable(ContainerExpr)) { 542 QualType CType = VDec->getType(); 543 if (Dereference) { 544 if (!CType->isPointerType()) 545 return false; 546 CType = CType->getPointeeType(); 547 } 548 // If VDec is a reference to a container, Dereference is false, 549 // but we still need to check the const-ness of the underlying container 550 // type. 551 CType = CType.getNonReferenceType(); 552 return CType.isConstQualified(); 553 } 554 return false; 555 } 556 557 LoopConvertCheck::LoopConvertCheck(StringRef Name, ClangTidyContext *Context) 558 : ClangTidyCheck(Name, Context), TUInfo(new TUTrackingInfo), 559 MaxCopySize(Options.get("MaxCopySize", 16ULL)), 560 MinConfidence(Options.get("MinConfidence", Confidence::CL_Reasonable)), 561 NamingStyle(Options.get("NamingStyle", VariableNamer::NS_CamelCase)), 562 Inserter(Options.getLocalOrGlobal("IncludeStyle", 563 utils::IncludeSorter::IS_LLVM), 564 areDiagsSelfContained()), 565 UseCxx20IfAvailable(Options.get("UseCxx20ReverseRanges", true)), 566 ReverseFunction(Options.get("MakeReverseRangeFunction", "")), 567 ReverseHeader(Options.get("MakeReverseRangeHeader", "")) { 568 569 if (ReverseFunction.empty() && !ReverseHeader.empty()) { 570 configurationDiag( 571 "modernize-loop-convert: 'MakeReverseRangeHeader' is set but " 572 "'MakeReverseRangeFunction' is not, disabling reverse loop " 573 "transformation"); 574 UseReverseRanges = false; 575 } else if (ReverseFunction.empty()) { 576 UseReverseRanges = UseCxx20IfAvailable && getLangOpts().CPlusPlus20; 577 } else { 578 UseReverseRanges = true; 579 } 580 } 581 582 void LoopConvertCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { 583 Options.store(Opts, "MaxCopySize", MaxCopySize); 584 Options.store(Opts, "MinConfidence", MinConfidence); 585 Options.store(Opts, "NamingStyle", NamingStyle); 586 Options.store(Opts, "IncludeStyle", Inserter.getStyle()); 587 Options.store(Opts, "UseCxx20ReverseRanges", UseCxx20IfAvailable); 588 Options.store(Opts, "MakeReverseRangeFunction", ReverseFunction); 589 Options.store(Opts, "MakeReverseRangeHeader", ReverseHeader); 590 } 591 592 void LoopConvertCheck::registerPPCallbacks(const SourceManager &SM, 593 Preprocessor *PP, 594 Preprocessor *ModuleExpanderPP) { 595 Inserter.registerPreprocessor(PP); 596 } 597 598 void LoopConvertCheck::registerMatchers(MatchFinder *Finder) { 599 Finder->addMatcher(traverse(TK_AsIs, makeArrayLoopMatcher()), this); 600 Finder->addMatcher(traverse(TK_AsIs, makeIteratorLoopMatcher(false)), this); 601 Finder->addMatcher(traverse(TK_AsIs, makePseudoArrayLoopMatcher()), this); 602 if (UseReverseRanges) 603 Finder->addMatcher(traverse(TK_AsIs, makeIteratorLoopMatcher(true)), this); 604 } 605 606 /// Given the range of a single declaration, such as: 607 /// \code 608 /// unsigned &ThisIsADeclarationThatCanSpanSeveralLinesOfCode = 609 /// InitializationValues[I]; 610 /// next_instruction; 611 /// \endcode 612 /// Finds the range that has to be erased to remove this declaration without 613 /// leaving empty lines, by extending the range until the beginning of the 614 /// next instruction. 615 /// 616 /// We need to delete a potential newline after the deleted alias, as 617 /// clang-format will leave empty lines untouched. For all other formatting we 618 /// rely on clang-format to fix it. 619 void LoopConvertCheck::getAliasRange(SourceManager &SM, SourceRange &Range) { 620 bool Invalid = false; 621 const char *TextAfter = 622 SM.getCharacterData(Range.getEnd().getLocWithOffset(1), &Invalid); 623 if (Invalid) 624 return; 625 unsigned Offset = std::strspn(TextAfter, " \t\r\n"); 626 Range = 627 SourceRange(Range.getBegin(), Range.getEnd().getLocWithOffset(Offset)); 628 } 629 630 /// Computes the changes needed to convert a given for loop, and 631 /// applies them. 632 void LoopConvertCheck::doConversion( 633 ASTContext *Context, const VarDecl *IndexVar, 634 const ValueDecl *MaybeContainer, const UsageResult &Usages, 635 const DeclStmt *AliasDecl, bool AliasUseRequired, bool AliasFromForInit, 636 const ForStmt *Loop, RangeDescriptor Descriptor) { 637 std::string VarNameOrStructuredBinding; 638 bool VarNameFromAlias = (Usages.size() == 1) && AliasDecl; 639 bool AliasVarIsRef = false; 640 bool CanCopy = true; 641 std::vector<FixItHint> FixIts; 642 if (VarNameFromAlias) { 643 const auto *AliasVar = cast<VarDecl>(AliasDecl->getSingleDecl()); 644 645 // Handle structured bindings 646 if (const auto *AliasDecompositionDecl = 647 dyn_cast<DecompositionDecl>(AliasDecl->getSingleDecl())) { 648 VarNameOrStructuredBinding = "["; 649 650 assert(!AliasDecompositionDecl->bindings().empty() && "No bindings"); 651 for (const BindingDecl *Binding : AliasDecompositionDecl->bindings()) { 652 VarNameOrStructuredBinding += Binding->getName().str() + ", "; 653 } 654 655 VarNameOrStructuredBinding.erase(VarNameOrStructuredBinding.size() - 2, 656 2); 657 VarNameOrStructuredBinding += "]"; 658 } else { 659 VarNameOrStructuredBinding = AliasVar->getName().str(); 660 661 // Use the type of the alias if it's not the same 662 QualType AliasVarType = AliasVar->getType(); 663 assert(!AliasVarType.isNull() && "Type in VarDecl is null"); 664 if (AliasVarType->isReferenceType()) { 665 AliasVarType = AliasVarType.getNonReferenceType(); 666 AliasVarIsRef = true; 667 } 668 if (Descriptor.ElemType.isNull() || 669 !Context->hasSameUnqualifiedType(AliasVarType, Descriptor.ElemType)) 670 Descriptor.ElemType = AliasVarType; 671 } 672 673 // We keep along the entire DeclStmt to keep the correct range here. 674 SourceRange ReplaceRange = AliasDecl->getSourceRange(); 675 676 std::string ReplacementText; 677 if (AliasUseRequired) { 678 ReplacementText = VarNameOrStructuredBinding; 679 } else if (AliasFromForInit) { 680 // FIXME: Clang includes the location of the ';' but only for DeclStmt's 681 // in a for loop's init clause. Need to put this ';' back while removing 682 // the declaration of the alias variable. This is probably a bug. 683 ReplacementText = ";"; 684 } else { 685 // Avoid leaving empty lines or trailing whitespaces. 686 getAliasRange(Context->getSourceManager(), ReplaceRange); 687 } 688 689 FixIts.push_back(FixItHint::CreateReplacement( 690 CharSourceRange::getTokenRange(ReplaceRange), ReplacementText)); 691 // No further replacements are made to the loop, since the iterator or index 692 // was used exactly once - in the initialization of AliasVar. 693 } else { 694 VariableNamer Namer(&TUInfo->getGeneratedDecls(), 695 &TUInfo->getParentFinder().getStmtToParentStmtMap(), 696 Loop, IndexVar, MaybeContainer, Context, NamingStyle); 697 VarNameOrStructuredBinding = Namer.createIndexName(); 698 // First, replace all usages of the array subscript expression with our new 699 // variable. 700 for (const auto &Usage : Usages) { 701 std::string ReplaceText; 702 SourceRange Range = Usage.Range; 703 if (Usage.Expression) { 704 // If this is an access to a member through the arrow operator, after 705 // the replacement it must be accessed through the '.' operator. 706 ReplaceText = Usage.Kind == Usage::UK_MemberThroughArrow 707 ? VarNameOrStructuredBinding + "." 708 : VarNameOrStructuredBinding; 709 auto Parents = Context->getParents(*Usage.Expression); 710 if (Parents.size() == 1) { 711 if (const auto *Paren = Parents[0].get<ParenExpr>()) { 712 // Usage.Expression will be replaced with the new index variable, 713 // and parenthesis around a simple DeclRefExpr can always be 714 // removed. 715 Range = Paren->getSourceRange(); 716 } else if (const auto *UOP = Parents[0].get<UnaryOperator>()) { 717 // If we are taking the address of the loop variable, then we must 718 // not use a copy, as it would mean taking the address of the loop's 719 // local index instead. 720 // FIXME: This won't catch cases where the address is taken outside 721 // of the loop's body (for instance, in a function that got the 722 // loop's index as a const reference parameter), or where we take 723 // the address of a member (like "&Arr[i].A.B.C"). 724 if (UOP->getOpcode() == UO_AddrOf) 725 CanCopy = false; 726 } 727 } 728 } else { 729 // The Usage expression is only null in case of lambda captures (which 730 // are VarDecl). If the index is captured by value, add '&' to capture 731 // by reference instead. 732 ReplaceText = Usage.Kind == Usage::UK_CaptureByCopy 733 ? "&" + VarNameOrStructuredBinding 734 : VarNameOrStructuredBinding; 735 } 736 TUInfo->getReplacedVars().insert(std::make_pair(Loop, IndexVar)); 737 FixIts.push_back(FixItHint::CreateReplacement( 738 CharSourceRange::getTokenRange(Range), ReplaceText)); 739 } 740 } 741 742 // Now, we need to construct the new range expression. 743 SourceRange ParenRange(Loop->getLParenLoc(), Loop->getRParenLoc()); 744 745 QualType Type = Context->getAutoDeductType(); 746 if (!Descriptor.ElemType.isNull() && Descriptor.ElemType->isFundamentalType()) 747 Type = Descriptor.ElemType.getUnqualifiedType(); 748 Type = Type.getDesugaredType(*Context); 749 750 // If the new variable name is from the aliased variable, then the reference 751 // type for the new variable should only be used if the aliased variable was 752 // declared as a reference. 753 bool IsCheapToCopy = 754 !Descriptor.ElemType.isNull() && 755 Descriptor.ElemType.isTriviallyCopyableType(*Context) && 756 // TypeInfo::Width is in bits. 757 Context->getTypeInfo(Descriptor.ElemType).Width <= 8 * MaxCopySize; 758 bool UseCopy = CanCopy && ((VarNameFromAlias && !AliasVarIsRef) || 759 (Descriptor.DerefByConstRef && IsCheapToCopy)); 760 761 if (!UseCopy) { 762 if (Descriptor.DerefByConstRef) { 763 Type = Context->getLValueReferenceType(Context->getConstType(Type)); 764 } else if (Descriptor.DerefByValue) { 765 if (!IsCheapToCopy) 766 Type = Context->getRValueReferenceType(Type); 767 } else { 768 Type = Context->getLValueReferenceType(Type); 769 } 770 } 771 772 SmallString<128> Range; 773 llvm::raw_svector_ostream Output(Range); 774 Output << '('; 775 Type.print(Output, getLangOpts()); 776 Output << ' ' << VarNameOrStructuredBinding << " : "; 777 if (Descriptor.NeedsReverseCall) 778 Output << getReverseFunction() << '('; 779 if (Descriptor.ContainerNeedsDereference) 780 Output << '*'; 781 Output << Descriptor.ContainerString; 782 if (Descriptor.NeedsReverseCall) 783 Output << "))"; 784 else 785 Output << ')'; 786 FixIts.push_back(FixItHint::CreateReplacement( 787 CharSourceRange::getTokenRange(ParenRange), Range)); 788 789 if (Descriptor.NeedsReverseCall && !getReverseHeader().empty()) { 790 if (std::optional<FixItHint> Insertion = Inserter.createIncludeInsertion( 791 Context->getSourceManager().getFileID(Loop->getBeginLoc()), 792 getReverseHeader())) 793 FixIts.push_back(*Insertion); 794 } 795 diag(Loop->getForLoc(), "use range-based for loop instead") << FixIts; 796 TUInfo->getGeneratedDecls().insert( 797 make_pair(Loop, VarNameOrStructuredBinding)); 798 } 799 800 /// Returns a string which refers to the container iterated over. 801 StringRef LoopConvertCheck::getContainerString(ASTContext *Context, 802 const ForStmt *Loop, 803 const Expr *ContainerExpr) { 804 StringRef ContainerString; 805 ContainerExpr = ContainerExpr->IgnoreParenImpCasts(); 806 if (isa<CXXThisExpr>(ContainerExpr)) { 807 ContainerString = "this"; 808 } else { 809 // For CXXOperatorCallExpr such as vector_ptr->size() we want the class 810 // object vector_ptr, but for vector[2] we need the whole expression. 811 if (const auto *E = dyn_cast<CXXOperatorCallExpr>(ContainerExpr)) 812 if (E->getOperator() != OO_Subscript) 813 ContainerExpr = E->getArg(0); 814 ContainerString = 815 getStringFromRange(Context->getSourceManager(), Context->getLangOpts(), 816 ContainerExpr->getSourceRange()); 817 } 818 819 return ContainerString; 820 } 821 822 /// Determines what kind of 'auto' must be used after converting a for 823 /// loop that iterates over an array or pseudoarray. 824 void LoopConvertCheck::getArrayLoopQualifiers(ASTContext *Context, 825 const BoundNodes &Nodes, 826 const Expr *ContainerExpr, 827 const UsageResult &Usages, 828 RangeDescriptor &Descriptor) { 829 // On arrays and pseudoarrays, we must figure out the qualifiers from the 830 // usages. 831 if (usagesAreConst(Context, Usages) || 832 containerIsConst(ContainerExpr, Descriptor.ContainerNeedsDereference)) { 833 Descriptor.DerefByConstRef = true; 834 } 835 if (usagesReturnRValues(Usages)) { 836 // If the index usages (dereference, subscript, at, ...) return rvalues, 837 // then we should not use a reference, because we need to keep the code 838 // correct if it mutates the returned objects. 839 Descriptor.DerefByValue = true; 840 } 841 // Try to find the type of the elements on the container, to check if 842 // they are trivially copyable. 843 for (const Usage &U : Usages) { 844 if (!U.Expression || U.Expression->getType().isNull()) 845 continue; 846 QualType Type = U.Expression->getType().getCanonicalType(); 847 if (U.Kind == Usage::UK_MemberThroughArrow) { 848 if (!Type->isPointerType()) { 849 continue; 850 } 851 Type = Type->getPointeeType(); 852 } 853 Descriptor.ElemType = Type; 854 } 855 } 856 857 /// Determines what kind of 'auto' must be used after converting an 858 /// iterator based for loop. 859 void LoopConvertCheck::getIteratorLoopQualifiers(ASTContext *Context, 860 const BoundNodes &Nodes, 861 RangeDescriptor &Descriptor) { 862 // The matchers for iterator loops provide bound nodes to obtain this 863 // information. 864 const auto *InitVar = Nodes.getNodeAs<VarDecl>(InitVarName); 865 QualType CanonicalInitVarType = InitVar->getType().getCanonicalType(); 866 const auto *DerefByValueType = 867 Nodes.getNodeAs<QualType>(DerefByValueResultName); 868 Descriptor.DerefByValue = DerefByValueType; 869 870 if (Descriptor.DerefByValue) { 871 // If the dereference operator returns by value then test for the 872 // canonical const qualification of the init variable type. 873 Descriptor.DerefByConstRef = CanonicalInitVarType.isConstQualified(); 874 Descriptor.ElemType = *DerefByValueType; 875 } else { 876 if (const auto *DerefType = 877 Nodes.getNodeAs<QualType>(DerefByRefResultName)) { 878 // A node will only be bound with DerefByRefResultName if we're dealing 879 // with a user-defined iterator type. Test the const qualification of 880 // the reference type. 881 auto ValueType = DerefType->getNonReferenceType(); 882 883 Descriptor.DerefByConstRef = ValueType.isConstQualified(); 884 Descriptor.ElemType = ValueType; 885 } else { 886 // By nature of the matcher this case is triggered only for built-in 887 // iterator types (i.e. pointers). 888 assert(isa<PointerType>(CanonicalInitVarType) && 889 "Non-class iterator type is not a pointer type"); 890 891 // We test for const qualification of the pointed-at type. 892 Descriptor.DerefByConstRef = 893 CanonicalInitVarType->getPointeeType().isConstQualified(); 894 Descriptor.ElemType = CanonicalInitVarType->getPointeeType(); 895 } 896 } 897 } 898 899 /// Determines the parameters needed to build the range replacement. 900 void LoopConvertCheck::determineRangeDescriptor( 901 ASTContext *Context, const BoundNodes &Nodes, const ForStmt *Loop, 902 LoopFixerKind FixerKind, const Expr *ContainerExpr, 903 const UsageResult &Usages, RangeDescriptor &Descriptor) { 904 Descriptor.ContainerString = 905 std::string(getContainerString(Context, Loop, ContainerExpr)); 906 Descriptor.NeedsReverseCall = (FixerKind == LFK_ReverseIterator); 907 908 if (FixerKind == LFK_Iterator || FixerKind == LFK_ReverseIterator) 909 getIteratorLoopQualifiers(Context, Nodes, Descriptor); 910 else 911 getArrayLoopQualifiers(Context, Nodes, ContainerExpr, Usages, Descriptor); 912 } 913 914 /// Check some of the conditions that must be met for the loop to be 915 /// convertible. 916 bool LoopConvertCheck::isConvertible(ASTContext *Context, 917 const ast_matchers::BoundNodes &Nodes, 918 const ForStmt *Loop, 919 LoopFixerKind FixerKind) { 920 // In self contained diagnosics mode we don't want dependancies on other 921 // loops, otherwise, If we already modified the range of this for loop, don't 922 // do any further updates on this iteration. 923 if (areDiagsSelfContained()) 924 TUInfo = std::make_unique<TUTrackingInfo>(); 925 else if (TUInfo->getReplacedVars().count(Loop)) 926 return false; 927 928 // Check that we have exactly one index variable and at most one end variable. 929 const auto *InitVar = Nodes.getNodeAs<VarDecl>(InitVarName); 930 931 // FIXME: Try to put most of this logic inside a matcher. 932 if (FixerKind == LFK_Iterator || FixerKind == LFK_ReverseIterator) { 933 QualType InitVarType = InitVar->getType(); 934 QualType CanonicalInitVarType = InitVarType.getCanonicalType(); 935 936 const auto *BeginCall = Nodes.getNodeAs<CallExpr>(BeginCallName); 937 assert(BeginCall && "Bad Callback. No begin call expression"); 938 QualType CanonicalBeginType = 939 BeginCall->getDirectCallee()->getReturnType().getCanonicalType(); 940 if (CanonicalBeginType->isPointerType() && 941 CanonicalInitVarType->isPointerType()) { 942 // If the initializer and the variable are both pointers check if the 943 // un-qualified pointee types match, otherwise we don't use auto. 944 if (!Context->hasSameUnqualifiedType( 945 CanonicalBeginType->getPointeeType(), 946 CanonicalInitVarType->getPointeeType())) 947 return false; 948 } 949 } else if (FixerKind == LFK_PseudoArray) { 950 if (const auto *EndCall = Nodes.getNodeAs<CXXMemberCallExpr>(EndCallName)) { 951 // This call is required to obtain the container. 952 if (!isa<MemberExpr>(EndCall->getCallee())) 953 return false; 954 } 955 return Nodes.getNodeAs<CallExpr>(EndCallName) != nullptr; 956 } 957 return true; 958 } 959 960 void LoopConvertCheck::check(const MatchFinder::MatchResult &Result) { 961 const BoundNodes &Nodes = Result.Nodes; 962 Confidence ConfidenceLevel(Confidence::CL_Safe); 963 ASTContext *Context = Result.Context; 964 965 const ForStmt *Loop; 966 LoopFixerKind FixerKind; 967 RangeDescriptor Descriptor; 968 969 if ((Loop = Nodes.getNodeAs<ForStmt>(LoopNameArray))) { 970 FixerKind = LFK_Array; 971 } else if ((Loop = Nodes.getNodeAs<ForStmt>(LoopNameIterator))) { 972 FixerKind = LFK_Iterator; 973 } else if ((Loop = Nodes.getNodeAs<ForStmt>(LoopNameReverseIterator))) { 974 FixerKind = LFK_ReverseIterator; 975 } else { 976 Loop = Nodes.getNodeAs<ForStmt>(LoopNamePseudoArray); 977 assert(Loop && "Bad Callback. No for statement"); 978 FixerKind = LFK_PseudoArray; 979 } 980 981 if (!isConvertible(Context, Nodes, Loop, FixerKind)) 982 return; 983 984 const auto *LoopVar = Nodes.getNodeAs<VarDecl>(InitVarName); 985 const auto *EndVar = Nodes.getNodeAs<VarDecl>(EndVarName); 986 987 // If the loop calls end()/size() after each iteration, lower our confidence 988 // level. 989 if (FixerKind != LFK_Array && !EndVar) 990 ConfidenceLevel.lowerTo(Confidence::CL_Reasonable); 991 992 // If the end comparison isn't a variable, we can try to work with the 993 // expression the loop variable is being tested against instead. 994 const auto *EndCall = Nodes.getNodeAs<Expr>(EndCallName); 995 const auto *BoundExpr = Nodes.getNodeAs<Expr>(ConditionBoundName); 996 997 // Find container expression of iterators and pseudoarrays, and determine if 998 // this expression needs to be dereferenced to obtain the container. 999 // With array loops, the container is often discovered during the 1000 // ForLoopIndexUseVisitor traversal. 1001 const Expr *ContainerExpr = nullptr; 1002 if (FixerKind == LFK_Iterator || FixerKind == LFK_ReverseIterator) { 1003 ContainerExpr = findContainer( 1004 Context, LoopVar->getInit(), EndVar ? EndVar->getInit() : EndCall, 1005 &Descriptor.ContainerNeedsDereference, 1006 /*IsReverse=*/FixerKind == LFK_ReverseIterator); 1007 } else if (FixerKind == LFK_PseudoArray) { 1008 std::optional<ContainerCall> Call = getContainerExpr(EndCall); 1009 if (Call) { 1010 ContainerExpr = Call->Container; 1011 Descriptor.ContainerNeedsDereference = Call->IsArrow; 1012 } 1013 } 1014 1015 // We must know the container or an array length bound. 1016 if (!ContainerExpr && !BoundExpr) 1017 return; 1018 1019 ForLoopIndexUseVisitor Finder(Context, LoopVar, EndVar, ContainerExpr, 1020 BoundExpr, 1021 Descriptor.ContainerNeedsDereference); 1022 1023 // Find expressions and variables on which the container depends. 1024 if (ContainerExpr) { 1025 ComponentFinderASTVisitor ComponentFinder; 1026 ComponentFinder.findExprComponents(ContainerExpr->IgnoreParenImpCasts()); 1027 Finder.addComponents(ComponentFinder.getComponents()); 1028 } 1029 1030 // Find usages of the loop index. If they are not used in a convertible way, 1031 // stop here. 1032 if (!Finder.findAndVerifyUsages(Loop->getBody())) 1033 return; 1034 ConfidenceLevel.lowerTo(Finder.getConfidenceLevel()); 1035 1036 // Obtain the container expression, if we don't have it yet. 1037 if (FixerKind == LFK_Array) { 1038 ContainerExpr = Finder.getContainerIndexed()->IgnoreParenImpCasts(); 1039 1040 // Very few loops are over expressions that generate arrays rather than 1041 // array variables. Consider loops over arrays that aren't just represented 1042 // by a variable to be risky conversions. 1043 if (!getReferencedVariable(ContainerExpr) && 1044 !isDirectMemberExpr(ContainerExpr)) 1045 ConfidenceLevel.lowerTo(Confidence::CL_Risky); 1046 } 1047 1048 // Find out which qualifiers we have to use in the loop range. 1049 TraversalKindScope RAII(*Context, TK_AsIs); 1050 const UsageResult &Usages = Finder.getUsages(); 1051 determineRangeDescriptor(Context, Nodes, Loop, FixerKind, ContainerExpr, 1052 Usages, Descriptor); 1053 1054 // Ensure that we do not try to move an expression dependent on a local 1055 // variable declared inside the loop outside of it. 1056 // FIXME: Determine when the external dependency isn't an expression converted 1057 // by another loop. 1058 TUInfo->getParentFinder().gatherAncestors(*Context); 1059 DependencyFinderASTVisitor DependencyFinder( 1060 &TUInfo->getParentFinder().getStmtToParentStmtMap(), 1061 &TUInfo->getParentFinder().getDeclToParentStmtMap(), 1062 &TUInfo->getReplacedVars(), Loop); 1063 1064 if (DependencyFinder.dependsOnInsideVariable(ContainerExpr) || 1065 Descriptor.ContainerString.empty() || Usages.empty() || 1066 ConfidenceLevel.getLevel() < MinConfidence) 1067 return; 1068 1069 doConversion(Context, LoopVar, getReferencedVariable(ContainerExpr), Usages, 1070 Finder.getAliasDecl(), Finder.aliasUseRequired(), 1071 Finder.aliasFromForInit(), Loop, Descriptor); 1072 } 1073 1074 llvm::StringRef LoopConvertCheck::getReverseFunction() const { 1075 if (!ReverseFunction.empty()) 1076 return ReverseFunction; 1077 if (UseReverseRanges) 1078 return "std::ranges::reverse_view"; 1079 return ""; 1080 } 1081 1082 llvm::StringRef LoopConvertCheck::getReverseHeader() const { 1083 if (!ReverseHeader.empty()) 1084 return ReverseHeader; 1085 if (UseReverseRanges && ReverseFunction.empty()) { 1086 return "<ranges>"; 1087 } 1088 return ""; 1089 } 1090 1091 } // namespace modernize 1092 } // namespace clang::tidy 1093