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(container)' (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 } 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 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 != "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 /// container 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 const DynTypedNodeList 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 except in case of a `sizeof` operator call. 715 const DynTypedNodeList GrandParents = Context->getParents(*Paren); 716 if (GrandParents.size() != 1 || 717 GrandParents[0].get<UnaryExprOrTypeTraitExpr>() == nullptr) { 718 Range = Paren->getSourceRange(); 719 } 720 } else if (const auto *UOP = Parents[0].get<UnaryOperator>()) { 721 // If we are taking the address of the loop variable, then we must 722 // not use a copy, as it would mean taking the address of the loop's 723 // local index instead. 724 // FIXME: This won't catch cases where the address is taken outside 725 // of the loop's body (for instance, in a function that got the 726 // loop's index as a const reference parameter), or where we take 727 // the address of a member (like "&Arr[i].A.B.C"). 728 if (UOP->getOpcode() == UO_AddrOf) 729 CanCopy = false; 730 } 731 } 732 } else { 733 // The Usage expression is only null in case of lambda captures (which 734 // are VarDecl). If the index is captured by value, add '&' to capture 735 // by reference instead. 736 ReplaceText = Usage.Kind == Usage::UK_CaptureByCopy 737 ? "&" + VarNameOrStructuredBinding 738 : VarNameOrStructuredBinding; 739 } 740 TUInfo->getReplacedVars().insert(std::make_pair(Loop, IndexVar)); 741 FixIts.push_back(FixItHint::CreateReplacement( 742 CharSourceRange::getTokenRange(Range), ReplaceText)); 743 } 744 } 745 746 // Now, we need to construct the new range expression. 747 SourceRange ParenRange(Loop->getLParenLoc(), Loop->getRParenLoc()); 748 749 QualType Type = Context->getAutoDeductType(); 750 if (!Descriptor.ElemType.isNull() && Descriptor.ElemType->isFundamentalType()) 751 Type = Descriptor.ElemType.getUnqualifiedType(); 752 Type = Type.getDesugaredType(*Context); 753 754 // If the new variable name is from the aliased variable, then the reference 755 // type for the new variable should only be used if the aliased variable was 756 // declared as a reference. 757 bool IsCheapToCopy = 758 !Descriptor.ElemType.isNull() && 759 Descriptor.ElemType.isTriviallyCopyableType(*Context) && 760 !Descriptor.ElemType->isDependentSizedArrayType() && 761 // TypeInfo::Width is in bits. 762 Context->getTypeInfo(Descriptor.ElemType).Width <= 8 * MaxCopySize; 763 bool UseCopy = CanCopy && ((VarNameFromAlias && !AliasVarIsRef) || 764 (Descriptor.DerefByConstRef && IsCheapToCopy)); 765 766 if (!UseCopy) { 767 if (Descriptor.DerefByConstRef) { 768 Type = Context->getLValueReferenceType(Context->getConstType(Type)); 769 } else if (Descriptor.DerefByValue) { 770 if (!IsCheapToCopy) 771 Type = Context->getRValueReferenceType(Type); 772 } else { 773 Type = Context->getLValueReferenceType(Type); 774 } 775 } 776 777 SmallString<128> Range; 778 llvm::raw_svector_ostream Output(Range); 779 Output << '('; 780 Type.print(Output, getLangOpts()); 781 Output << ' ' << VarNameOrStructuredBinding << " : "; 782 if (Descriptor.NeedsReverseCall) 783 Output << getReverseFunction() << '('; 784 if (Descriptor.ContainerNeedsDereference) 785 Output << '*'; 786 Output << Descriptor.ContainerString; 787 if (Descriptor.NeedsReverseCall) 788 Output << "))"; 789 else 790 Output << ')'; 791 FixIts.push_back(FixItHint::CreateReplacement( 792 CharSourceRange::getTokenRange(ParenRange), Range)); 793 794 if (Descriptor.NeedsReverseCall && !getReverseHeader().empty()) { 795 if (std::optional<FixItHint> Insertion = Inserter.createIncludeInsertion( 796 Context->getSourceManager().getFileID(Loop->getBeginLoc()), 797 getReverseHeader())) 798 FixIts.push_back(*Insertion); 799 } 800 diag(Loop->getForLoc(), "use range-based for loop instead") << FixIts; 801 TUInfo->getGeneratedDecls().insert( 802 make_pair(Loop, VarNameOrStructuredBinding)); 803 } 804 805 /// Returns a string which refers to the container iterated over. 806 StringRef LoopConvertCheck::getContainerString(ASTContext *Context, 807 const ForStmt *Loop, 808 const Expr *ContainerExpr) { 809 StringRef ContainerString; 810 ContainerExpr = ContainerExpr->IgnoreParenImpCasts(); 811 if (isa<CXXThisExpr>(ContainerExpr)) { 812 ContainerString = "this"; 813 } else { 814 // For CXXOperatorCallExpr such as vector_ptr->size() we want the class 815 // object vector_ptr, but for vector[2] we need the whole expression. 816 if (const auto *E = dyn_cast<CXXOperatorCallExpr>(ContainerExpr)) 817 if (E->getOperator() != OO_Subscript) 818 ContainerExpr = E->getArg(0); 819 ContainerString = 820 getStringFromRange(Context->getSourceManager(), Context->getLangOpts(), 821 ContainerExpr->getSourceRange()); 822 } 823 824 return ContainerString; 825 } 826 827 /// Determines what kind of 'auto' must be used after converting a for 828 /// loop that iterates over an array or pseudoarray. 829 void LoopConvertCheck::getArrayLoopQualifiers(ASTContext *Context, 830 const BoundNodes &Nodes, 831 const Expr *ContainerExpr, 832 const UsageResult &Usages, 833 RangeDescriptor &Descriptor) { 834 // On arrays and pseudoarrays, we must figure out the qualifiers from the 835 // usages. 836 if (usagesAreConst(Context, Usages) || 837 containerIsConst(ContainerExpr, Descriptor.ContainerNeedsDereference)) { 838 Descriptor.DerefByConstRef = true; 839 } 840 if (usagesReturnRValues(Usages)) { 841 // If the index usages (dereference, subscript, at, ...) return rvalues, 842 // then we should not use a reference, because we need to keep the code 843 // correct if it mutates the returned objects. 844 Descriptor.DerefByValue = true; 845 } 846 // Try to find the type of the elements on the container, to check if 847 // they are trivially copyable. 848 for (const Usage &U : Usages) { 849 if (!U.Expression || U.Expression->getType().isNull()) 850 continue; 851 QualType Type = U.Expression->getType().getCanonicalType(); 852 if (U.Kind == Usage::UK_MemberThroughArrow) { 853 if (!Type->isPointerType()) { 854 continue; 855 } 856 Type = Type->getPointeeType(); 857 } 858 Descriptor.ElemType = Type; 859 } 860 } 861 862 /// Determines what kind of 'auto' must be used after converting an 863 /// iterator based for loop. 864 void LoopConvertCheck::getIteratorLoopQualifiers(ASTContext *Context, 865 const BoundNodes &Nodes, 866 RangeDescriptor &Descriptor) { 867 // The matchers for iterator loops provide bound nodes to obtain this 868 // information. 869 const auto *InitVar = Nodes.getNodeAs<VarDecl>(InitVarName); 870 QualType CanonicalInitVarType = InitVar->getType().getCanonicalType(); 871 const auto *DerefByValueType = 872 Nodes.getNodeAs<QualType>(DerefByValueResultName); 873 Descriptor.DerefByValue = DerefByValueType; 874 875 if (Descriptor.DerefByValue) { 876 // If the dereference operator returns by value then test for the 877 // canonical const qualification of the init variable type. 878 Descriptor.DerefByConstRef = CanonicalInitVarType.isConstQualified(); 879 Descriptor.ElemType = *DerefByValueType; 880 } else { 881 if (const auto *DerefType = 882 Nodes.getNodeAs<QualType>(DerefByRefResultName)) { 883 // A node will only be bound with DerefByRefResultName if we're dealing 884 // with a user-defined iterator type. Test the const qualification of 885 // the reference type. 886 auto ValueType = DerefType->getNonReferenceType(); 887 888 Descriptor.DerefByConstRef = ValueType.isConstQualified(); 889 Descriptor.ElemType = ValueType; 890 } else { 891 // By nature of the matcher this case is triggered only for built-in 892 // iterator types (i.e. pointers). 893 assert(isa<PointerType>(CanonicalInitVarType) && 894 "Non-class iterator type is not a pointer type"); 895 896 // We test for const qualification of the pointed-at type. 897 Descriptor.DerefByConstRef = 898 CanonicalInitVarType->getPointeeType().isConstQualified(); 899 Descriptor.ElemType = CanonicalInitVarType->getPointeeType(); 900 } 901 } 902 } 903 904 /// Determines the parameters needed to build the range replacement. 905 void LoopConvertCheck::determineRangeDescriptor( 906 ASTContext *Context, const BoundNodes &Nodes, const ForStmt *Loop, 907 LoopFixerKind FixerKind, const Expr *ContainerExpr, 908 const UsageResult &Usages, RangeDescriptor &Descriptor) { 909 Descriptor.ContainerString = 910 std::string(getContainerString(Context, Loop, ContainerExpr)); 911 Descriptor.NeedsReverseCall = (FixerKind == LFK_ReverseIterator); 912 913 if (FixerKind == LFK_Iterator || FixerKind == LFK_ReverseIterator) 914 getIteratorLoopQualifiers(Context, Nodes, Descriptor); 915 else 916 getArrayLoopQualifiers(Context, Nodes, ContainerExpr, Usages, Descriptor); 917 } 918 919 /// Check some of the conditions that must be met for the loop to be 920 /// convertible. 921 bool LoopConvertCheck::isConvertible(ASTContext *Context, 922 const ast_matchers::BoundNodes &Nodes, 923 const ForStmt *Loop, 924 LoopFixerKind FixerKind) { 925 // In self contained diagnostic mode we don't want dependencies on other 926 // loops, otherwise, If we already modified the range of this for loop, don't 927 // do any further updates on this iteration. 928 if (areDiagsSelfContained()) 929 TUInfo = std::make_unique<TUTrackingInfo>(); 930 else if (TUInfo->getReplacedVars().count(Loop)) 931 return false; 932 933 // Check that we have exactly one index variable and at most one end variable. 934 const auto *InitVar = Nodes.getNodeAs<VarDecl>(InitVarName); 935 936 // FIXME: Try to put most of this logic inside a matcher. 937 if (FixerKind == LFK_Iterator || FixerKind == LFK_ReverseIterator) { 938 QualType InitVarType = InitVar->getType(); 939 QualType CanonicalInitVarType = InitVarType.getCanonicalType(); 940 941 const auto *BeginCall = Nodes.getNodeAs<CallExpr>(BeginCallName); 942 assert(BeginCall && "Bad Callback. No begin call expression"); 943 QualType CanonicalBeginType = 944 BeginCall->getDirectCallee()->getReturnType().getCanonicalType(); 945 if (CanonicalBeginType->isPointerType() && 946 CanonicalInitVarType->isPointerType()) { 947 // If the initializer and the variable are both pointers check if the 948 // un-qualified pointee types match, otherwise we don't use auto. 949 return Context->hasSameUnqualifiedType( 950 CanonicalBeginType->getPointeeType(), 951 CanonicalInitVarType->getPointeeType()); 952 } 953 954 if (CanonicalBeginType->isBuiltinType() || 955 CanonicalInitVarType->isBuiltinType()) 956 return false; 957 958 } else if (FixerKind == LFK_PseudoArray) { 959 if (const auto *EndCall = Nodes.getNodeAs<CXXMemberCallExpr>(EndCallName)) { 960 // This call is required to obtain the container. 961 if (!isa<MemberExpr>(EndCall->getCallee())) 962 return false; 963 } 964 return Nodes.getNodeAs<CallExpr>(EndCallName) != nullptr; 965 } 966 return true; 967 } 968 969 void LoopConvertCheck::check(const MatchFinder::MatchResult &Result) { 970 const BoundNodes &Nodes = Result.Nodes; 971 Confidence ConfidenceLevel(Confidence::CL_Safe); 972 ASTContext *Context = Result.Context; 973 974 const ForStmt *Loop = nullptr; 975 LoopFixerKind FixerKind{}; 976 RangeDescriptor Descriptor; 977 978 if ((Loop = Nodes.getNodeAs<ForStmt>(LoopNameArray))) { 979 FixerKind = LFK_Array; 980 } else if ((Loop = Nodes.getNodeAs<ForStmt>(LoopNameIterator))) { 981 FixerKind = LFK_Iterator; 982 } else if ((Loop = Nodes.getNodeAs<ForStmt>(LoopNameReverseIterator))) { 983 FixerKind = LFK_ReverseIterator; 984 } else { 985 Loop = Nodes.getNodeAs<ForStmt>(LoopNamePseudoArray); 986 assert(Loop && "Bad Callback. No for statement"); 987 FixerKind = LFK_PseudoArray; 988 } 989 990 if (!isConvertible(Context, Nodes, Loop, FixerKind)) 991 return; 992 993 const auto *LoopVar = Nodes.getNodeAs<VarDecl>(InitVarName); 994 const auto *EndVar = Nodes.getNodeAs<VarDecl>(EndVarName); 995 996 // If the loop calls end()/size() after each iteration, lower our confidence 997 // level. 998 if (FixerKind != LFK_Array && !EndVar) 999 ConfidenceLevel.lowerTo(Confidence::CL_Reasonable); 1000 1001 // If the end comparison isn't a variable, we can try to work with the 1002 // expression the loop variable is being tested against instead. 1003 const auto *EndCall = Nodes.getNodeAs<Expr>(EndCallName); 1004 const auto *BoundExpr = Nodes.getNodeAs<Expr>(ConditionBoundName); 1005 1006 // Find container expression of iterators and pseudoarrays, and determine if 1007 // this expression needs to be dereferenced to obtain the container. 1008 // With array loops, the container is often discovered during the 1009 // ForLoopIndexUseVisitor traversal. 1010 const Expr *ContainerExpr = nullptr; 1011 if (FixerKind == LFK_Iterator || FixerKind == LFK_ReverseIterator) { 1012 ContainerExpr = findContainer( 1013 Context, LoopVar->getInit(), EndVar ? EndVar->getInit() : EndCall, 1014 &Descriptor.ContainerNeedsDereference, 1015 /*IsReverse=*/FixerKind == LFK_ReverseIterator); 1016 } else if (FixerKind == LFK_PseudoArray) { 1017 std::optional<ContainerCall> Call = getContainerExpr(EndCall); 1018 if (Call) { 1019 ContainerExpr = Call->Container; 1020 Descriptor.ContainerNeedsDereference = Call->IsArrow; 1021 } 1022 } 1023 1024 // We must know the container or an array length bound. 1025 if (!ContainerExpr && !BoundExpr) 1026 return; 1027 1028 ForLoopIndexUseVisitor Finder(Context, LoopVar, EndVar, ContainerExpr, 1029 BoundExpr, 1030 Descriptor.ContainerNeedsDereference); 1031 1032 // Find expressions and variables on which the container depends. 1033 if (ContainerExpr) { 1034 ComponentFinderASTVisitor ComponentFinder; 1035 ComponentFinder.findExprComponents(ContainerExpr->IgnoreParenImpCasts()); 1036 Finder.addComponents(ComponentFinder.getComponents()); 1037 } 1038 1039 // Find usages of the loop index. If they are not used in a convertible way, 1040 // stop here. 1041 if (!Finder.findAndVerifyUsages(Loop->getBody())) 1042 return; 1043 ConfidenceLevel.lowerTo(Finder.getConfidenceLevel()); 1044 1045 // Obtain the container expression, if we don't have it yet. 1046 if (FixerKind == LFK_Array) { 1047 ContainerExpr = Finder.getContainerIndexed()->IgnoreParenImpCasts(); 1048 1049 // Very few loops are over expressions that generate arrays rather than 1050 // array variables. Consider loops over arrays that aren't just represented 1051 // by a variable to be risky conversions. 1052 if (!getReferencedVariable(ContainerExpr) && 1053 !isDirectMemberExpr(ContainerExpr)) 1054 ConfidenceLevel.lowerTo(Confidence::CL_Risky); 1055 } 1056 1057 // Find out which qualifiers we have to use in the loop range. 1058 TraversalKindScope RAII(*Context, TK_AsIs); 1059 const UsageResult &Usages = Finder.getUsages(); 1060 determineRangeDescriptor(Context, Nodes, Loop, FixerKind, ContainerExpr, 1061 Usages, Descriptor); 1062 1063 // Ensure that we do not try to move an expression dependent on a local 1064 // variable declared inside the loop outside of it. 1065 // FIXME: Determine when the external dependency isn't an expression converted 1066 // by another loop. 1067 TUInfo->getParentFinder().gatherAncestors(*Context); 1068 DependencyFinderASTVisitor DependencyFinder( 1069 &TUInfo->getParentFinder().getStmtToParentStmtMap(), 1070 &TUInfo->getParentFinder().getDeclToParentStmtMap(), 1071 &TUInfo->getReplacedVars(), Loop); 1072 1073 if (DependencyFinder.dependsOnInsideVariable(ContainerExpr) || 1074 Descriptor.ContainerString.empty() || Usages.empty() || 1075 ConfidenceLevel.getLevel() < MinConfidence) 1076 return; 1077 1078 doConversion(Context, LoopVar, getReferencedVariable(ContainerExpr), Usages, 1079 Finder.getAliasDecl(), Finder.aliasUseRequired(), 1080 Finder.aliasFromForInit(), Loop, Descriptor); 1081 } 1082 1083 llvm::StringRef LoopConvertCheck::getReverseFunction() const { 1084 if (!ReverseFunction.empty()) 1085 return ReverseFunction; 1086 if (UseReverseRanges) 1087 return "std::ranges::reverse_view"; 1088 return ""; 1089 } 1090 1091 llvm::StringRef LoopConvertCheck::getReverseHeader() const { 1092 if (!ReverseHeader.empty()) 1093 return ReverseHeader; 1094 if (UseReverseRanges && ReverseFunction.empty()) { 1095 return "<ranges>"; 1096 } 1097 return ""; 1098 } 1099 1100 } // namespace modernize 1101 } // namespace clang::tidy 1102