Index: clang-tidy/modernize/LoopConvertCheck.h =================================================================== --- clang-tidy/modernize/LoopConvertCheck.h +++ clang-tidy/modernize/LoopConvertCheck.h @@ -27,9 +27,9 @@ private: struct RangeDescriptor { bool ContainerNeedsDereference; + bool DerefByConstRef; bool DerefByValue; bool IsTriviallyCopyable; - bool DerefByConstRef; }; void doConversion(ASTContext *Context, const VarDecl *IndexVar, Index: clang-tidy/modernize/LoopConvertCheck.cpp =================================================================== --- clang-tidy/modernize/LoopConvertCheck.cpp +++ clang-tidy/modernize/LoopConvertCheck.cpp @@ -381,6 +381,20 @@ return true; } +/// \brief Returns true if the container is const-qualified. +static bool containerIsConst(const Expr *ContainerExpr, bool Dereference) { + if (const auto *VDec = getReferencedVariable(ContainerExpr)) { + QualType CType = VDec->getType(); + if (Dereference) { + if (!CType->isPointerType()) + return false; + CType = CType->getPointeeType(); + } + return CType.isConstQualified(); + } + return false; +} + LoopConvertCheck::LoopConvertCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), TUInfo(new TUTrackingInfo), MinConfidence(StringSwitch( @@ -401,6 +415,11 @@ StringRef ContainerString, const UsageResult &Usages, const DeclStmt *AliasDecl, bool AliasUseRequired, bool AliasFromForInit, const ForStmt *TheLoop, RangeDescriptor Descriptor) { + // If there aren't any usages, converting the loop would generate an unused + // variable warning. + if (Usages.size() == 0) + return; + auto Diag = diag(TheLoop->getForLoc(), "use range-based for loop instead"); std::string VarName; @@ -436,11 +455,23 @@ VarName = Namer.createIndexName(); // First, replace all usages of the array subscript expression with our new // variable. - for (const auto &I : Usages) { - std::string ReplaceText = I.IsArrow ? VarName + "." : VarName; + for (const auto &Usage : Usages) { + std::string ReplaceText; + if (Usage.Expression) { + // If this is an access to a member through the arrow operator, after + // the replacement it must be accessed through the '.' operator. + ReplaceText = Usage.Kind == Usage::UK_MemberThroughArrow ? VarName + "." + : VarName; + } else { + // The Usage expression is only null in case of lambda captures (which + // are VarDecl). If the index is captured by value, add '&' to capture + // by reference instead. + ReplaceText = + Usage.Kind == Usage::UK_CaptureByCopy ? "&" + VarName : VarName; + } TUInfo->getReplacedVars().insert(std::make_pair(TheLoop, IndexVar)); Diag << FixItHint::CreateReplacement( - CharSourceRange::getTokenRange(I.Range), ReplaceText); + CharSourceRange::getTokenRange(Usage.Range), ReplaceText); } } @@ -537,16 +568,24 @@ if (FixerKind == LFK_Array) { // The array being indexed by IndexVar was discovered during traversal. ContainerExpr = Finder.getContainerIndexed()->IgnoreParenImpCasts(); + // Very few loops are over expressions that generate arrays rather than // array variables. Consider loops over arrays that aren't just represented // by a variable to be risky conversions. if (!getReferencedVariable(ContainerExpr) && !isDirectMemberExpr(ContainerExpr)) ConfidenceLevel.lowerTo(Confidence::CL_Risky); + + // Use 'const' if the array is const. + if (containerIsConst(ContainerExpr, Descriptor.ContainerNeedsDereference)) + Descriptor.DerefByConstRef = true; + } else if (FixerKind == LFK_PseudoArray) { if (!Descriptor.DerefByValue && !Descriptor.DerefByConstRef) { const UsageResult &Usages = Finder.getUsages(); - if (usagesAreConst(Usages)) { + if (usagesAreConst(Usages) || + containerIsConst(ContainerExpr, + Descriptor.ContainerNeedsDereference)) { Descriptor.DerefByConstRef = true; } else if (usagesReturnRValues(Usages)) { // If the index usages (dereference, subscript, at) return RValues, @@ -558,7 +597,7 @@ if (!U.Expression || U.Expression->getType().isNull()) continue; QualType Type = U.Expression->getType().getCanonicalType(); - if (U.IsArrow) { + if (U.Kind == Usage::UK_MemberThroughArrow) { if (!Type->isPointerType()) continue; Type = Type->getPointeeType(); @@ -625,6 +664,7 @@ // expression the loop variable is being tested against instead. const auto *EndCall = Nodes.getStmtAs(EndCallName); const auto *BoundExpr = Nodes.getStmtAs(ConditionBoundName); + // If the loop calls end()/size() after each iteration, lower our confidence // level. if (FixerKind != LFK_Array && !EndVar) Index: clang-tidy/modernize/LoopConvertUtils.h =================================================================== --- clang-tidy/modernize/LoopConvertUtils.h +++ clang-tidy/modernize/LoopConvertUtils.h @@ -197,14 +197,30 @@ /// \brief The information needed to describe a valid convertible usage /// of an array index or iterator. struct Usage { + enum UsageKind { + // Regular usages of the loop index (the ones not specified below). + UK_Default, + // Indicates whether this is an access to a member through the arrow + // operator on pointers or iterators. + UK_MemberThroughArrow, + // If the variable is being captured by a lambda, indicates whether the + // capture was done by value or by reference. + UK_CaptureByCopy, + UK_CaptureByRef + }; + // The expression that is going to be converted. Null in case of lambda + // captures. const Expr *Expression; - bool IsArrow; + + UsageKind Kind; + + // Range that covers this usage. SourceRange Range; explicit Usage(const Expr *E) - : Expression(E), IsArrow(false), Range(Expression->getSourceRange()) {} - Usage(const Expr *E, bool IsArrow, SourceRange Range) - : Expression(E), IsArrow(IsArrow), Range(std::move(Range)) {} + : Expression(E), Kind(UK_Default), Range(Expression->getSourceRange()) {} + Usage(const Expr *E, UsageKind Kind, SourceRange Range) + : Expression(E), Kind(Kind), Range(std::move(Range)) {} }; /// \brief A class to encapsulate lowering of the tool's confidence level. Index: clang-tidy/modernize/LoopConvertUtils.cpp =================================================================== --- clang-tidy/modernize/LoopConvertUtils.cpp +++ clang-tidy/modernize/LoopConvertUtils.cpp @@ -560,7 +560,7 @@ // If something complicated is happening (i.e. the next token isn't an // arrow), give up on making this work. if (!ArrowLoc.isInvalid()) { - addUsage(Usage(ResultExpr, /*IsArrow=*/true, + addUsage(Usage(ResultExpr, Usage::UK_MemberThroughArrow, SourceRange(Base->getExprLoc(), ArrowLoc))); return true; } @@ -762,7 +762,10 @@ // FIXME: if the index is captured, it will count as an usage and the // alias (if any) won't work, because it is only used in case of having // exactly one usage. - addUsage(Usage(nullptr, false, C->getLocation())); + addUsage(Usage(nullptr, + C->getCaptureKind() == LCK_ByCopy ? Usage::UK_CaptureByCopy + : Usage::UK_CaptureByRef, + C->getLocation())); } } return VisitorBase::TraverseLambdaCapture(LE, C); Index: test/clang-tidy/Inputs/modernize-loop-convert/structures.h =================================================================== --- test/clang-tidy/Inputs/modernize-loop-convert/structures.h +++ test/clang-tidy/Inputs/modernize-loop-convert/structures.h @@ -59,8 +59,9 @@ }; template -class dependent{ +class dependent { public: + dependent(); struct iterator_base { const ElemType& operator*()const; iterator_base& operator ++(); Index: test/clang-tidy/modernize-loop-convert-basic.cpp =================================================================== --- test/clang-tidy/modernize-loop-convert-basic.cpp +++ test/clang-tidy/modernize-loop-convert-basic.cpp @@ -7,6 +7,7 @@ const int N = 6; const int NMinusOne = N - 1; int arr[N] = {1, 2, 3, 4, 5, 6}; +const int constArr[N] = {1, 2, 3, 4, 5, 6}; int (*pArr)[N] = &arr; void f() { @@ -17,10 +18,9 @@ int k; } // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead [modernize-loop-convert] - // CHECK-FIXES: for (auto & elem : arr) { + // CHECK-FIXES: for (auto & elem : arr) // CHECK-FIXES-NEXT: sum += elem; // CHECK-FIXES-NEXT: int k; - // CHECK-FIXES-NEXT: } for (int i = 0; i < N; ++i) { printf("Fibonacci number is %d\n", arr[i]); @@ -53,9 +53,8 @@ arr[i] += 1; } // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead - // CHECK-FIXES: for (auto & elem : arr) { + // CHECK-FIXES: for (auto & elem : arr) // CHECK-FIXES-NEXT: elem += 1; - // CHECK-FIXES-NEXT: } for (int i = 0; i < N; ++i) { int x = arr[i] + 2; @@ -77,9 +76,8 @@ sum += arr[i]; } // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead - // CHECK-FIXES: for (auto & elem : arr) { + // CHECK-FIXES: for (auto & elem : arr) // CHECK-FIXES-NEXT: sum += elem; - // CHECK-FIXES-NEXT: } for (int i = 0; i < N; ++i) { printf("Fibonacci number %d has address %p\n", arr[i], &arr[i]); @@ -95,9 +93,17 @@ teas[i].g(); } // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead - // CHECK-FIXES: for (auto & tea : teas) { + // CHECK-FIXES: for (auto & tea : teas) // CHECK-FIXES-NEXT: tea.g(); - // CHECK-FIXES-NEXT: } +} + +void constArray() { + for (int i = 0; i < N; ++i) { + printf("2 * %d = %d\n", constArr[i], constArr[i] + constArr[i]); + } + // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead + // CHECK-FIXES: for (const auto & elem : constArr) + // CHECK-FIXES-NEXT: printf("2 * %d = %d\n", elem, elem + elem); } struct HasArr { @@ -108,17 +114,15 @@ printf("%d", Arr[i]); } // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use range-based for loop instead - // CHECK-FIXES: for (auto & elem : Arr) { + // CHECK-FIXES: for (auto & elem : Arr) // CHECK-FIXES-NEXT: printf("%d", elem); - // CHECK-FIXES-NEXT: } for (int i = 0; i < N; ++i) { printf("%d", ValArr[i].x); } // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use range-based for loop instead - // CHECK-FIXES: for (auto & elem : ValArr) { + // CHECK-FIXES: for (auto & elem : ValArr) // CHECK-FIXES-NEXT: printf("%d", elem.x); - // CHECK-FIXES-NEXT: } } void explicitThis() { @@ -126,21 +130,19 @@ printf("%d", this->Arr[i]); } // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use range-based for loop instead - // CHECK-FIXES: for (auto & elem : this->Arr) { + // CHECK-FIXES: for (auto & elem : this->Arr) // CHECK-FIXES-NEXT: printf("%d", elem); - // CHECK-FIXES-NEXT: } for (int i = 0; i < N; ++i) { printf("%d", this->ValArr[i].x); } // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use range-based for loop instead - // CHECK-FIXES: for (auto & elem : this->ValArr) { + // CHECK-FIXES: for (auto & elem : this->ValArr) // CHECK-FIXES-NEXT: printf("%d", elem.x); - // CHECK-FIXES-NEXT: } } }; -// Loops whose bounds are value-dependent shold not be converted. +// Loops whose bounds are value-dependent should not be converted. template void dependentExprBound() { for (int i = 0; i < N; ++i) @@ -263,7 +265,7 @@ printf("Fibonacci number is %d\n", *it); } // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead - // CHECK-FIXES: for (auto & elem : v) { + // CHECK-FIXES: for (auto & elem : v) // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", elem); for (dependent::iterator it(v.begin()), e = v.end(); @@ -271,7 +273,7 @@ printf("Fibonacci number is %d\n", *it); } // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead - // CHECK-FIXES: for (auto & elem : v) { + // CHECK-FIXES: for (auto & elem : v) // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", elem); doublyDependent intmap; @@ -285,13 +287,14 @@ // PtrSet's iterator dereferences by value so auto & can't be used. { - PtrSet int_ptrs; - for (PtrSet::iterator I = int_ptrs.begin(), - E = int_ptrs.end(); + PtrSet val_int_ptrs; + for (PtrSet::iterator I = val_int_ptrs.begin(), + E = val_int_ptrs.end(); I != E; ++I) { + (void) *I; } - // CHECK-MESSAGES: :[[@LINE-4]]:5: warning: use range-based for loop instead - // CHECK-FIXES: for (auto int_ptr : int_ptrs) { + // CHECK-MESSAGES: :[[@LINE-5]]:5: warning: use range-based for loop instead + // CHECK-FIXES: for (auto val_int_ptr : val_int_ptrs) } // This container uses an iterator where the derefence type is a typedef of @@ -302,9 +305,10 @@ for (TypedefDerefContainer::iterator I = int_ptrs.begin(), E = int_ptrs.end(); I != E; ++I) { + (void) *I; } - // CHECK-MESSAGES: :[[@LINE-4]]:5: warning: use range-based for loop instead - // CHECK-FIXES: for (auto & int_ptr : int_ptrs) { + // CHECK-MESSAGES: :[[@LINE-5]]:5: warning: use range-based for loop instead + // CHECK-FIXES: for (auto & int_ptr : int_ptrs) } { @@ -314,10 +318,8 @@ for (RValueDerefContainer::iterator I = container.begin(), E = container.end(); I != E; ++I) { + (void) *I; } - // CHECK-FIXES: for (RValueDerefContainer::iterator I = container.begin(), - // CHECK-FIXES-NEXT: E = container.end(); - // CHECK-FIXES-NEXT: I != E; ++I) { } } @@ -350,17 +352,11 @@ it != e; ++it) { printf("Fibonacci number is %d\n", *it); } - // CHECK-FIXES: for (dependent::const_iterator it = v.begin(), e = v.end(); - // CHECK-FIXES-NEXT: it != e; ++it) { - // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", *it); for (dependent::const_iterator it(v.begin()), e = v.end(); it != e; ++it) { printf("Fibonacci number is %d\n", *it); } - // CHECK-FIXES: for (dependent::const_iterator it(v.begin()), e = v.end(); - // CHECK-FIXES-NEXT: it != e; ++it) { - // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", *it); } // Tests to ensure that an implicit 'this' is picked up as the container. @@ -380,42 +376,45 @@ void doSomething() const; void doLoop() { - for (iterator I = begin(), E = end(); I != E; ++I) { - } + for (iterator I = begin(), E = end(); I != E; ++I) + (void) *I; // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use range-based for loop instead - // CHECK-FIXES: for (auto & elem : *this) { + // CHECK-FIXES: for (auto & elem : *this) - for (iterator I = C::begin(), E = C::end(); I != E; ++I) { - } + for (iterator I = C::begin(), E = C::end(); I != E; ++I) + (void) *I; // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use range-based for loop instead - // CHECK-FIXES: for (auto & elem : *this) { + // CHECK-FIXES: for (auto & elem : *this) for (iterator I = begin(), E = end(); I != E; ++I) { + (void) *I; doSomething(); } - for (iterator I = begin(); I != end(); ++I) { - } + for (iterator I = begin(); I != end(); ++I) + (void) *I; // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use range-based for loop instead - // CHECK-FIXES: for (auto & elem : *this) { + // CHECK-FIXES: for (auto & elem : *this) for (iterator I = begin(); I != end(); ++I) { + (void) *I; doSomething(); } } void doLoop() const { - for (const_iterator I = begin(), E = end(); I != E; ++I) { - } + for (const_iterator I = begin(), E = end(); I != E; ++I) + (void) *I; // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use range-based for loop instead - // CHECK-FIXES: for (auto & elem : *this) { + // CHECK-FIXES: for (auto & elem : *this) - for (const_iterator I = C::begin(), E = C::end(); I != E; ++I) { - } + for (const_iterator I = C::begin(), E = C::end(); I != E; ++I) + (void) *I; // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use range-based for loop instead - // CHECK-FIXES: for (auto & elem : *this) { + // CHECK-FIXES: for (auto & elem : *this) for (const_iterator I = begin(), E = end(); I != E; ++I) { + (void) *I; doSomething(); } } @@ -431,10 +430,10 @@ void doLoop() { // The implicit 'this' will have an Implicit cast to const C2* wrapped // around it. Make sure the replacement still happens. - for (iterator I = begin(), E = end(); I != E; ++I) { - } + for (iterator I = begin(), E = end(); I != E; ++I) + (void) *I; // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use range-based for loop instead - // CHECK-FIXES: for (auto & elem : *this) { + // CHECK-FIXES: for (auto & elem : *this) } }; @@ -445,6 +444,8 @@ const int N = 6; dependent v; dependent *pv; +const dependent constv; +const dependent *pconstv; transparent> cv; @@ -500,21 +501,62 @@ // CHECK-FIXES-NEXT: sum += elem + 2; } +void constness() { + int sum = 0; + for (int i = 0, e = constv.size(); i < e; ++i) { + printf("Fibonacci number is %d\n", constv[i]); + sum += constv[i] + 2; + } + // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead + // CHECK-FIXES: for (const auto & elem : constv) + // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", elem); + // CHECK-FIXES-NEXT: sum += elem + 2; + + for (int i = 0, e = constv.size(); i < e; ++i) { + printf("Fibonacci number is %d\n", constv.at(i)); + sum += constv.at(i) + 2; + } + // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead + // CHECK-FIXES: for (const auto & elem : constv) + // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", elem); + // CHECK-FIXES-NEXT: sum += elem + 2; + + for (int i = 0, e = pconstv->size(); i < e; ++i) { + printf("Fibonacci number is %d\n", pconstv->at(i)); + sum += pconstv->at(i) + 2; + } + // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead + // CHECK-FIXES: for (const auto & elem : *pconstv) + // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", elem); + // CHECK-FIXES-NEXT: sum += elem + 2; + + // This test will fail if size() isn't called repeatedly, since it + // returns unsigned int, and 0 is deduced to be signed int. + // FIXME: Insert the necessary explicit conversion, or write out the types + // explicitly. + for (int i = 0; i < pconstv->size(); ++i) { + printf("Fibonacci number is %d\n", (*pconstv).at(i)); + sum += (*pconstv)[i] + 2; + } + // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead + // CHECK-FIXES: for (const auto & elem : *pconstv) + // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", elem); + // CHECK-FIXES-NEXT: sum += elem + 2; +} + // Check for loops that don't mention containers. void noContainer() { for (auto i = 0; i < v.size(); ++i) { } - // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead - // CHECK-FIXES: for (const auto & elem : v) { for (auto i = 0; i < v.size(); ++i) ; - // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead - // CHECK-FIXES: for (const auto & elem : v) } struct NoBeginEnd { unsigned size() const; + unsigned& operator[](int); + const unsigned& operator[](int) const; }; struct NoConstBeginEnd { @@ -522,6 +564,8 @@ unsigned size() const; unsigned* begin(); unsigned* end(); + unsigned& operator[](int); + const unsigned& operator[](int) const; }; struct ConstBeginEnd { @@ -529,30 +573,34 @@ unsigned size() const; unsigned* begin() const; unsigned* end() const; + unsigned& operator[](int); + const unsigned& operator[](int) const; }; // Shouldn't transform pseudo-array uses if the container doesn't provide // begin() and end() of the right const-ness. void NoBeginEndTest() { NoBeginEnd NBE; - for (unsigned i = 0, e = NBE.size(); i < e; ++i) { - } + for (unsigned i = 0, e = NBE.size(); i < e; ++i) + printf("%d\n", NBE[i]); const NoConstBeginEnd const_NCBE; - for (unsigned i = 0, e = const_NCBE.size(); i < e; ++i) { - } + for (unsigned i = 0, e = const_NCBE.size(); i < e; ++i) + printf("%d\n", const_NCBE[i]); ConstBeginEnd CBE; - for (unsigned i = 0, e = CBE.size(); i < e; ++i) { - } + for (unsigned i = 0, e = CBE.size(); i < e; ++i) + printf("%d\n", CBE[i]); // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead - // CHECK-FIXES: for (const auto & elem : CBE) { + // CHECK-FIXES: for (auto & elem : CBE) + // CHECK-FIXES-NEXT: printf("%d\n", elem); const ConstBeginEnd const_CBE; - for (unsigned i = 0, e = const_CBE.size(); i < e; ++i) { - } + for (unsigned i = 0, e = const_CBE.size(); i < e; ++i) + printf("%d\n", const_CBE[i]); // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead - // CHECK-FIXES: for (const auto & elem : const_CBE) { + // CHECK-FIXES: for (const auto & elem : const_CBE) + // CHECK-FIXES-NEXT: printf("%d\n", elem); } struct DerefByValue { @@ -570,7 +618,7 @@ printf("%d\n", DBV[i]); } // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead - // CHECK-FIXES: for (auto elem : DBV) { + // CHECK-FIXES: for (auto elem : DBV) // CHECK-FIXES-NEXT: printf("%d\n", elem); for (unsigned i = 0, e = DBV.size(); i < e; ++i) { @@ -578,8 +626,8 @@ printf("%d\n", DBV[i]); } // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead - // CHECK-FIXES: for (auto elem : DBV) { - // CHECK-FIXES-NEXT: auto f = [DBV, elem]() {}; + // CHECK-FIXES: for (auto elem : DBV) + // CHECK-FIXES-NEXT: auto f = [DBV, &elem]() {}; // CHECK-FIXES-NEXT: printf("%d\n", elem); } Index: test/clang-tidy/modernize-loop-convert-extra.cpp =================================================================== --- test/clang-tidy/modernize-loop-convert-extra.cpp +++ test/clang-tidy/modernize-loop-convert-extra.cpp @@ -14,10 +14,9 @@ int b = arr[i][a]; } // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead - // CHECK-FIXES: for (auto & elem : arr) { + // CHECK-FIXES: for (auto & elem : arr) // CHECK-FIXES-NEXT: int a = 0; // CHECK-FIXES-NEXT: int b = elem[a]; - // CHECK-FIXES-NEXT: } for (int j = 0; j < M; ++j) { int a = 0; @@ -121,7 +120,7 @@ } // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead // CHECK-FIXES: for (auto alias : IntArr) - // CHECK-FIXES-NEXT: if (alias) { + // CHECK-FIXES-NEXT: if (alias) for (unsigned i = 0; i < N; ++i) { while (int alias = IntArr[i]) { @@ -130,7 +129,7 @@ } // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead // CHECK-FIXES: for (auto alias : IntArr) - // CHECK-FIXES-NEXT: while (alias) { + // CHECK-FIXES-NEXT: while (alias) for (unsigned i = 0; i < N; ++i) { switch (int alias = IntArr[i]) { @@ -140,7 +139,7 @@ } // CHECK-MESSAGES: :[[@LINE-6]]:3: warning: use range-based for loop instead // CHECK-FIXES: for (auto alias : IntArr) - // CHECK-FIXES-NEXT: switch (alias) { + // CHECK-FIXES-NEXT: switch (alias) for (unsigned i = 0; i < N; ++i) { for (int alias = IntArr[i]; alias < N; ++alias) { @@ -149,7 +148,7 @@ } // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead // CHECK-FIXES: for (auto alias : IntArr) - // CHECK-FIXES-NEXT: for (; alias < N; ++alias) { + // CHECK-FIXES-NEXT: for (; alias < N; ++alias) for (unsigned i = 0; i < N; ++i) { for (unsigned j = 0; int alias = IntArr[i]; ++j) { @@ -158,14 +157,14 @@ } // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead // CHECK-FIXES: for (auto alias : IntArr) - // CHECK-FIXES-NEXT: for (unsigned j = 0; alias; ++j) { + // CHECK-FIXES-NEXT: for (unsigned j = 0; alias; ++j) struct IntRef { IntRef(const int& i); }; for (int i = 0; i < N; ++i) { IntRef Int(IntArr[i]); } // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead - // CHECK-FIXES: for (auto & elem : IntArr) { + // CHECK-FIXES: for (auto & elem : IntArr) // CHECK-FIXES-NEXT: IntRef Int(elem); } @@ -288,7 +287,7 @@ } // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead // CHECK-FIXES: for (auto & DEFs_it : DEFs) - // CHECK-FIXES-NEXT: if (DEFs_it == DEF) { + // CHECK-FIXES-NEXT: if (DEFs_it == DEF) // CHECK-FIXES-NEXT: printf("I found %d\n", DEFs_it); } @@ -315,8 +314,8 @@ T Vals; // Using the name "Val", although it is the name of an existing struct, is // safe in this loop since it will only exist within this scope. - for (T::iterator it = Vals.begin(), e = Vals.end(); it != e; ++it) { - } + for (T::iterator it = Vals.begin(), e = Vals.end(); it != e; ++it) + (void) *it; // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead // CHECK-FIXES: for (auto & Val : Vals) @@ -333,8 +332,8 @@ U TDs; // Naming the variable "TD" within this loop is safe because the typedef // was never used within the loop. - for (U::iterator it = TDs.begin(), e = TDs.end(); it != e; ++it) { - } + for (U::iterator it = TDs.begin(), e = TDs.end(); it != e; ++it) + (void) *it; // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead // CHECK-FIXES: for (auto & TD : TDs) @@ -342,8 +341,9 @@ for (U::iterator it = TDs.begin(), e = TDs.end(); it != e; ++it) { TD V; V.x = 5; + (void) *it; } - // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead + // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead // CHECK-FIXES: for (auto & TDs_it : TDs) // CHECK-FIXES-NEXT: TD V; // CHECK-FIXES-NEXT: V.x = 5; @@ -456,8 +456,8 @@ } } // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead - // CHECK-FIXES: for (auto & elem : NestT) { - // CHECK-FIXES-NEXT: for (T::iterator TI = (elem).begin(), TE = (elem).end(); TI != TE; ++TI) { + // CHECK-FIXES: for (auto & elem : NestT) + // CHECK-FIXES-NEXT: for (T::iterator TI = (elem).begin(), TE = (elem).end(); TI != TE; ++TI) // CHECK-FIXES-NEXT: printf("%d", *TI); // The inner loop is also convertible. @@ -468,8 +468,8 @@ } } // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead - // CHECK-FIXES: for (const auto & elem : NestS) { - // CHECK-FIXES-NEXT: for (S::const_iterator SI = (elem).begin(), SE = (elem).end(); SI != SE; ++SI) { + // CHECK-FIXES: for (const auto & elem : NestS) + // CHECK-FIXES-NEXT: for (S::const_iterator SI = (elem).begin(), SE = (elem).end(); SI != SE; ++SI) // CHECK-FIXES-NEXT: printf("%d", *SI); for (Nested::const_iterator I = NestS.begin(), E = NestS.end(); I != E; ++I) { @@ -480,7 +480,7 @@ } } // CHECK-MESSAGES: :[[@LINE-7]]:3: warning: use range-based for loop instead - // CHECK-FIXES: for (const auto & s : NestS) { + // CHECK-FIXES: for (const auto & s : NestS) for (Nested::iterator I = NestS.begin(), E = NestS.end(); I != E; ++I) { S &s = *I; @@ -490,7 +490,7 @@ } } // CHECK-MESSAGES: :[[@LINE-7]]:3: warning: use range-based for loop instead - // CHECK-FIXES: for (auto & s : NestS) { + // CHECK-FIXES: for (auto & s : NestS) Foo foo; for (Nested::const_iterator I = NestS.begin(), E = NestS.end(); I != E; ++I) { @@ -501,7 +501,7 @@ } } // CHECK-MESSAGES: :[[@LINE-7]]:3: warning: use range-based for loop instead - // CHECK-FIXES: for (const auto & s : NestS) { + // CHECK-FIXES: for (const auto & s : NestS) for (Nested::iterator I = NestS.begin(), E = NestS.end(); I != E; ++I) { S &s = *I; @@ -511,7 +511,7 @@ } } // CHECK-MESSAGES: :[[@LINE-7]]:3: warning: use range-based for loop instead - // CHECK-FIXES: for (auto & s : NestS) { + // CHECK-FIXES: for (auto & s : NestS) } @@ -623,7 +623,7 @@ printf("Fibonacci number is %d\n", *it); } // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead - // CHECK-FIXES: for (auto & elem : v) { + // CHECK-FIXES: for (auto & elem : v) // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", elem); for (dependent::iterator it(v.begin()); @@ -631,7 +631,7 @@ printf("Fibonacci number is %d\n", *it); } // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead - // CHECK-FIXES: for (auto & elem : v) { + // CHECK-FIXES: for (auto & elem : v) // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", elem); doublyDependent intmap; @@ -684,6 +684,9 @@ namespace Macros { +#define TWO_PARAM(x, y) if (x == y) {} +#define THREE_PARAM(x, y, z) if (x == y) {z;} + const int N = 10; int arr[N]; @@ -692,12 +695,28 @@ printf("Value: %d\n", arr[i]); } // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead - // CHECK-FIXES: for (auto & elem : arr) { + // CHECK-FIXES: for (auto & elem : arr) // CHECK-FIXES-NEXT: printf("Value: %d\n", elem); for (int i = 0; i < N; ++i) { printf("Value: %d\n", CONT arr[i]); } + + // FIXME: Right now, clang-tidy does not allow to make insertions in several + // arguments of the same macro call. The following code: + // \code + // for (int i = 0; i < N; ++i) { + // TWO_PARAM(arr[i], arr[i]); + // THREE_PARAM(arr[i], arr[i], arr[i]); + // } + // \endcode + // Should be converted to this: + // \code + // for (auto & elem : arr) { + // TWO_PARAM(elem, elem); + // THREE_PARAM(elem, elem, elem); + // } + // \endcode } } // namespace Macros @@ -708,12 +727,14 @@ void set_union(Container &container) { for (typename Container::const_iterator SI = container.begin(), SE = container.end(); SI != SE; ++SI) { + (void) *SI; } + S s; - for (S::iterator SI = s.begin(), SE = s.end(); SI != SE; ++SI) { - } + for (S::iterator SI = s.begin(), SE = s.end(); SI != SE; ++SI) + (void) *SI; // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead - // CHECK-FIXES: for (auto & elem : s) { + // CHECK-FIXES: for (auto & elem : s) } void template_instantiation() { @@ -735,7 +756,7 @@ auto F1 = [Arr, I]() { int R1 = Arr[I] + 1; }; // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead // CHECK-FIXES: for (auto & elem : Arr) - // CHECK-FIXES-NEXT: auto F1 = [Arr, elem]() { int R1 = elem + 1; }; + // CHECK-FIXES-NEXT: auto F1 = [Arr, &elem]() { int R1 = elem + 1; }; for (int I = 0; I < N; ++I) auto F2 = [Arr, &I]() { int R2 = Arr[I] + 3; }; @@ -749,8 +770,7 @@ auto F3 = [&Arr, I]() { int R3 = Arr[I]; }; // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead // CHECK-FIXES: for (auto & elem : Arr) - // CHECK-FIXES-NEXT: auto F3 = [&Arr, elem]() { int R3 = elem; }; - // FIXME: this does two copies instead of one. Capture elem by ref? + // CHECK-FIXES-NEXT: auto F3 = [&Arr, &elem]() { int R3 = elem; }; for (int I = 0; I < N; ++I) @@ -764,8 +784,7 @@ auto F5 = [&Arr, I]() { int &R5 = Arr[I]; }; // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead // CHECK-FIXES: for (auto & elem : Arr) - // CHECK-FIXES-NEXT: auto F5 = [&Arr, elem]() { int &R5 = elem; }; - // FIXME: this does one copy instead of none. Capture elem by ref? + // CHECK-FIXES-NEXT: auto F5 = [&Arr, &elem]() { int &R5 = elem; }; for (int I = 0; I < N; ++I) @@ -782,10 +801,9 @@ } // CHECK-MESSAGES: :[[@LINE-6]]:3: warning: use range-based for loop instead // CHECK-FIXES: for (auto & elem : Arr) - // CHECK-FIXES-NEXT: auto F = [Arr, elem](int k) { + // CHECK-FIXES-NEXT: auto F = [Arr, &elem](int k) // CHECK-FIXES-NEXT: printf("%d\n", elem + k); - // CHECK-FIXES-NEXT: }; - // CHECK-FIXES-NEXT: F(elem); + // CHECK-FIXES: F(elem); } void implicitCapture() { @@ -815,7 +833,7 @@ } // CHECK-MESSAGES: :[[@LINE-6]]:3: warning: use range-based for loop instead // CHECK-FIXES: for (auto & elem : Arr) - // CHECK-FIXES-NEXT: auto G3 = [&]() { + // CHECK-FIXES-NEXT: auto G3 = [&]() // CHECK-FIXES-NEXT: int R3 = elem; // CHECK-FIXES-NEXT: int J3 = elem + R3; @@ -826,7 +844,7 @@ } // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead // CHECK-FIXES: for (auto & elem : Arr) - // CHECK-FIXES-NEXT: auto G4 = [=]() { + // CHECK-FIXES-NEXT: auto G4 = [=]() // CHECK-FIXES-NEXT: int R4 = elem + 5; // Alias by value. @@ -838,7 +856,7 @@ } // CHECK-MESSAGES: :[[@LINE-6]]:3: warning: use range-based for loop instead // CHECK-FIXES: for (auto R5 : Arr) - // CHECK-FIXES-NEXT: auto G5 = [&]() { + // CHECK-FIXES-NEXT: auto G5 = [&]() // CHECK-FIXES: int J5 = 8 + R5; // Alias by reference. @@ -850,7 +868,7 @@ } // CHECK-MESSAGES: :[[@LINE-6]]:3: warning: use range-based for loop instead // CHECK-FIXES: for (auto & R6 : Arr) - // CHECK-FIXES-NEXT: auto G6 = [&]() { + // CHECK-FIXES-NEXT: auto G6 = [&]() // CHECK-FIXES: int J6 = -1 + R6; } @@ -883,6 +901,30 @@ auto H5 = [=]() { int R = *I; }; } +void captureByValue() { + // When the index is captured by value, we should replace this by a capture + // by reference. This avoids extra copies. + // FIXME: this could change semantics on array or pseudoarray loops if the + // container is captured by copy. + const int N = 10; + int Arr[N]; + dependent Dep; + + for (int I = 0; I < N; ++I) { + auto C1 = [&Arr, I]() { if (Arr[I] == 1); }; + } + // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead + // CHECK-FIXES: for (auto & elem : Arr) + // CHECK-FIXES-NEXT: auto C1 = [&Arr, &elem]() { if (elem == 1); }; + + for (unsigned I = 0; I < Dep.size(); ++I) { + auto C2 = [&Dep, I]() { if (Dep[I] == 2); }; + } + // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead + // CHECK-FIXES: for (auto & elem : Dep) + // CHECK-FIXES-NEXT: auto C2 = [&Dep, &elem]() { if (elem == 2); }; +} + } // namespace Lambdas namespace InitLists { Index: test/clang-tidy/modernize-loop-convert-negative.cpp =================================================================== --- test/clang-tidy/modernize-loop-convert-negative.cpp +++ test/clang-tidy/modernize-loop-convert-negative.cpp @@ -290,7 +290,6 @@ dependent v; dependent *pv; -transparent> cv; int Sum = 0; // Checks for the Index start and end: @@ -473,3 +472,41 @@ } } // namespace NegativeMultiEndCall + +namespace NoUsages { + +const int N = 6; +int arr[N] = {1, 2, 3, 4, 5, 6}; +S s; +dependent v; +int Count = 0; + +void foo(); + +void f() { + for (int I = 0; I < N; ++I) {} + for (int I = 0; I < N; ++I) + printf("Hello world\n"); + for (int I = 0; I < N; ++I) + ++Count; + for (int I = 0; I < N; ++I) + foo(); + + for (S::iterator I = s.begin(), E = s.end(); I != E; ++I) {} + for (S::iterator I = s.begin(), E = s.end(); I != E; ++I) + printf("Hello world\n"); + for (S::iterator I = s.begin(), E = s.end(); I != E; ++I) + ++Count; + for (S::iterator I = s.begin(), E = s.end(); I != E; ++I) + foo(); + + for (int I = 0; I < v.size(); ++I) {} + for (int I = 0; I < v.size(); ++I) + printf("Hello world\n"); + for (int I = 0; I < v.size(); ++I) + ++Count; + for (int I = 0; I < v.size(); ++I) + foo(); +} + +} // namespace NoUsages