19
19
#include " llvm/ADT/StringRef.h"
20
20
#include " llvm/ADT/StringSwitch.h"
21
21
#include " llvm/Support/Casting.h"
22
+ #include " llvm/Support/WithColor.h"
23
+ #include " llvm/Support/raw_ostream.h"
22
24
#include < cassert>
23
25
#include < cstring>
24
26
#include < utility>
@@ -57,6 +59,7 @@ namespace modernize {
57
59
58
60
static const char LoopNameArray[] = " forLoopArray" ;
59
61
static const char LoopNameIterator[] = " forLoopIterator" ;
62
+ static const char LoopNameReverseIterator[] = " forLoopReverseIterator" ;
60
63
static const char LoopNamePseudoArray[] = " forLoopPseudoArray" ;
61
64
static const char ConditionBoundName[] = " conditionBound" ;
62
65
static const char ConditionVarName[] = " conditionVar" ;
@@ -68,7 +71,6 @@ static const char ConditionEndVarName[] = "conditionEndVar";
68
71
static const char EndVarName[] = " endVar" ;
69
72
static const char DerefByValueResultName[] = " derefByValueResult" ;
70
73
static const char DerefByRefResultName[] = " derefByRefResult" ;
71
-
72
74
// shared matchers
73
75
static const TypeMatcher AnyType () { return anything (); }
74
76
@@ -150,10 +152,17 @@ StatementMatcher makeArrayLoopMatcher() {
150
152
// / - The iterator variables 'it', 'f', and 'h' are the same.
151
153
// / - The two containers on which 'begin' and 'end' are called are the same.
152
154
// / - If the end iterator variable 'g' is defined, it is the same as 'f'.
153
- StatementMatcher makeIteratorLoopMatcher () {
155
+ StatementMatcher makeIteratorLoopMatcher (bool IsReverse) {
156
+
157
+ auto BeginNameMatcher = IsReverse ? hasAnyName (" rbegin" , " crbegin" )
158
+ : hasAnyName (" begin" , " cbegin" );
159
+
160
+ auto EndNameMatcher =
161
+ IsReverse ? hasAnyName (" rend" , " crend" ) : hasAnyName (" end" , " cend" );
162
+
154
163
StatementMatcher BeginCallMatcher =
155
164
cxxMemberCallExpr (argumentCountIs (0 ),
156
- callee (cxxMethodDecl (hasAnyName ( " begin " , " cbegin " ) )))
165
+ callee (cxxMethodDecl (BeginNameMatcher )))
157
166
.bind (BeginCallName);
158
167
159
168
DeclarationMatcher InitDeclMatcher =
@@ -167,7 +176,7 @@ StatementMatcher makeIteratorLoopMatcher() {
167
176
varDecl (hasInitializer (anything ())).bind (EndVarName);
168
177
169
178
StatementMatcher EndCallMatcher = cxxMemberCallExpr (
170
- argumentCountIs (0 ), callee (cxxMethodDecl (hasAnyName ( " end " , " cend " ) )));
179
+ argumentCountIs (0 ), callee (cxxMethodDecl (EndNameMatcher )));
171
180
172
181
StatementMatcher IteratorBoundMatcher =
173
182
expr (anyOf (ignoringParenImpCasts (
@@ -225,7 +234,7 @@ StatementMatcher makeIteratorLoopMatcher() {
225
234
hasArgument (
226
235
0 , declRefExpr (to (varDecl (TestDerefReturnsByValue)
227
236
.bind (IncrementVarName))))))))
228
- .bind (LoopNameIterator);
237
+ .bind (IsReverse ? LoopNameReverseIterator : LoopNameIterator);
229
238
}
230
239
231
240
// / The matcher used for array-like containers (pseudoarrays).
@@ -325,7 +334,7 @@ StatementMatcher makePseudoArrayLoopMatcher() {
325
334
// / the output parameter isArrow is set to indicate whether the initialization
326
335
// / is called via . or ->.
327
336
static const Expr *getContainerFromBeginEndCall (const Expr *Init, bool IsBegin,
328
- bool *IsArrow) {
337
+ bool *IsArrow, bool IsReverse ) {
329
338
// FIXME: Maybe allow declaration/initialization outside of the for loop.
330
339
const auto *TheCall =
331
340
dyn_cast_or_null<CXXMemberCallExpr>(digThroughConstructors (Init));
@@ -336,9 +345,11 @@ static const Expr *getContainerFromBeginEndCall(const Expr *Init, bool IsBegin,
336
345
if (!Member)
337
346
return nullptr ;
338
347
StringRef Name = Member->getMemberDecl ()->getName ();
339
- StringRef TargetName = IsBegin ? " begin" : " end" ;
340
- StringRef ConstTargetName = IsBegin ? " cbegin" : " cend" ;
341
- if (Name != TargetName && Name != ConstTargetName)
348
+ if (!Name.consume_back (IsBegin ? " begin" : " end" ))
349
+ return nullptr ;
350
+ if (IsReverse && !Name.consume_back (" r" ))
351
+ return nullptr ;
352
+ if (!Name.empty () && !Name.equals (" c" ))
342
353
return nullptr ;
343
354
344
355
const Expr *SourceExpr = Member->getBase ();
@@ -356,18 +367,19 @@ static const Expr *getContainerFromBeginEndCall(const Expr *Init, bool IsBegin,
356
367
// / must be a member.
357
368
static const Expr *findContainer (ASTContext *Context, const Expr *BeginExpr,
358
369
const Expr *EndExpr,
359
- bool *ContainerNeedsDereference) {
370
+ bool *ContainerNeedsDereference,
371
+ bool IsReverse) {
360
372
// Now that we know the loop variable and test expression, make sure they are
361
373
// valid.
362
374
bool BeginIsArrow = false ;
363
375
bool EndIsArrow = false ;
364
- const Expr *BeginContainerExpr =
365
- getContainerFromBeginEndCall ( BeginExpr, /* IsBegin=*/ true , &BeginIsArrow);
376
+ const Expr *BeginContainerExpr = getContainerFromBeginEndCall (
377
+ BeginExpr, /* IsBegin=*/ true , &BeginIsArrow, IsReverse );
366
378
if (!BeginContainerExpr)
367
379
return nullptr ;
368
380
369
- const Expr *EndContainerExpr =
370
- getContainerFromBeginEndCall ( EndExpr, /* IsBegin=*/ false , &EndIsArrow);
381
+ const Expr *EndContainerExpr = getContainerFromBeginEndCall (
382
+ EndExpr, /* IsBegin=*/ false , &EndIsArrow, IsReverse );
371
383
// Disallow loops that try evil things like this (note the dot and arrow):
372
384
// for (IteratorType It = Obj.begin(), E = Obj->end(); It != E; ++It) { }
373
385
if (!EndContainerExpr || BeginIsArrow != EndIsArrow ||
@@ -475,27 +487,59 @@ static bool containerIsConst(const Expr *ContainerExpr, bool Dereference) {
475
487
476
488
LoopConvertCheck::RangeDescriptor::RangeDescriptor ()
477
489
: ContainerNeedsDereference(false ), DerefByConstRef(false ),
478
- DerefByValue (false ) {}
490
+ DerefByValue (false ), NeedsReverseCall( false ) {}
479
491
480
492
LoopConvertCheck::LoopConvertCheck (StringRef Name, ClangTidyContext *Context)
481
493
: ClangTidyCheck(Name, Context), TUInfo(new TUTrackingInfo),
482
494
MaxCopySize(Options.get(" MaxCopySize" , 16ULL )),
483
495
MinConfidence(Options.get(" MinConfidence" , Confidence::CL_Reasonable)),
484
- NamingStyle(Options.get(" NamingStyle" , VariableNamer::NS_CamelCase)) {}
496
+ NamingStyle(Options.get(" NamingStyle" , VariableNamer::NS_CamelCase)),
497
+ Inserter(Options.getLocalOrGlobal(" IncludeStyle" ,
498
+ utils::IncludeSorter::IS_LLVM)),
499
+ UseCxx20IfAvailable(Options.get(" UseCxx20ReverseRanges" , true )),
500
+ ReverseFunction(Options.get(" MakeReverseRangeFunction" , " " )),
501
+ ReverseHeader(Options.get(" MakeReverseRangeHeader" , " " )) {
502
+
503
+ if (ReverseFunction.empty () && !ReverseHeader.empty ()) {
504
+ llvm::WithColor::warning ()
505
+ << " modernize-loop-convert: 'MakeReverseRangeHeader' is set but "
506
+ " 'MakeReverseRangeFunction' is not, disabling reverse loop "
507
+ " transformation\n " ;
508
+ UseReverseRanges = false ;
509
+ } else if (ReverseFunction.empty ()) {
510
+ UseReverseRanges = UseCxx20IfAvailable && getLangOpts ().CPlusPlus20 ;
511
+ } else {
512
+ UseReverseRanges = true ;
513
+ }
514
+ }
485
515
486
516
void LoopConvertCheck::storeOptions (ClangTidyOptions::OptionMap &Opts) {
487
- Options.store (Opts, " MaxCopySize" , std::to_string ( MaxCopySize) );
517
+ Options.store (Opts, " MaxCopySize" , MaxCopySize);
488
518
Options.store (Opts, " MinConfidence" , MinConfidence);
489
519
Options.store (Opts, " NamingStyle" , NamingStyle);
520
+ Options.store (Opts, " IncludeStyle" , Inserter.getStyle ());
521
+ Options.store (Opts, " UseCxx20ReverseRanges" , UseCxx20IfAvailable);
522
+ Options.store (Opts, " MakeReverseRangeFunction" , ReverseFunction);
523
+ Options.store (Opts, " MakeReverseRangeHeader" , ReverseHeader);
524
+ }
525
+
526
+ void LoopConvertCheck::registerPPCallbacks (const SourceManager &SM,
527
+ Preprocessor *PP,
528
+ Preprocessor *ModuleExpanderPP) {
529
+ Inserter.registerPreprocessor (PP);
490
530
}
491
531
492
532
void LoopConvertCheck::registerMatchers (MatchFinder *Finder) {
493
533
Finder->addMatcher (traverse (ast_type_traits::TK_AsIs, makeArrayLoopMatcher ()),
494
534
this );
495
535
Finder->addMatcher (
496
- traverse (ast_type_traits::TK_AsIs, makeIteratorLoopMatcher ()), this );
536
+ traverse (ast_type_traits::TK_AsIs, makeIteratorLoopMatcher (false )), this );
497
537
Finder->addMatcher (
498
538
traverse (ast_type_traits::TK_AsIs, makePseudoArrayLoopMatcher ()), this );
539
+ if (UseReverseRanges)
540
+ Finder->addMatcher (
541
+ traverse (ast_type_traits::TK_AsIs, makeIteratorLoopMatcher (true )),
542
+ this );
499
543
}
500
544
501
545
// / Given the range of a single declaration, such as:
@@ -646,13 +690,29 @@ void LoopConvertCheck::doConversion(
646
690
}
647
691
}
648
692
649
- StringRef MaybeDereference = Descriptor.ContainerNeedsDereference ? " *" : " " ;
650
- std::string TypeString = Type.getAsString (getLangOpts ());
651
- std::string Range = (" (" + TypeString + " " + VarName + " : " +
652
- MaybeDereference + Descriptor.ContainerString + " )" )
653
- .str ();
693
+ SmallString<128 > Range;
694
+ llvm::raw_svector_ostream Output (Range);
695
+ Output << ' (' ;
696
+ Type.print (Output, getLangOpts ());
697
+ Output << ' ' << VarName << " : " ;
698
+ if (Descriptor.NeedsReverseCall )
699
+ Output << getReverseFunction () << ' (' ;
700
+ if (Descriptor.ContainerNeedsDereference )
701
+ Output << ' *' ;
702
+ Output << Descriptor.ContainerString ;
703
+ if (Descriptor.NeedsReverseCall )
704
+ Output << " ))" ;
705
+ else
706
+ Output << ' )' ;
654
707
FixIts.push_back (FixItHint::CreateReplacement (
655
708
CharSourceRange::getTokenRange (ParenRange), Range));
709
+
710
+ if (Descriptor.NeedsReverseCall && !getReverseHeader ().empty ()) {
711
+ if (Optional<FixItHint> Insertion = Inserter.createIncludeInsertion (
712
+ Context->getSourceManager ().getFileID (Loop->getBeginLoc ()),
713
+ getReverseHeader ()))
714
+ FixIts.push_back (*Insertion);
715
+ }
656
716
diag (Loop->getForLoc (), " use range-based for loop instead" ) << FixIts;
657
717
TUInfo->getGeneratedDecls ().insert (make_pair (Loop, VarName));
658
718
}
@@ -762,8 +822,9 @@ void LoopConvertCheck::determineRangeDescriptor(
762
822
const UsageResult &Usages, RangeDescriptor &Descriptor) {
763
823
Descriptor.ContainerString =
764
824
std::string (getContainerString (Context, Loop, ContainerExpr));
825
+ Descriptor.NeedsReverseCall = (FixerKind == LFK_ReverseIterator);
765
826
766
- if (FixerKind == LFK_Iterator)
827
+ if (FixerKind == LFK_Iterator || FixerKind == LFK_ReverseIterator )
767
828
getIteratorLoopQualifiers (Context, Nodes, Descriptor);
768
829
else
769
830
getArrayLoopQualifiers (Context, Nodes, ContainerExpr, Usages, Descriptor);
@@ -792,7 +853,7 @@ bool LoopConvertCheck::isConvertible(ASTContext *Context,
792
853
return false ;
793
854
794
855
// FIXME: Try to put most of this logic inside a matcher.
795
- if (FixerKind == LFK_Iterator) {
856
+ if (FixerKind == LFK_Iterator || FixerKind == LFK_ReverseIterator ) {
796
857
QualType InitVarType = InitVar->getType ();
797
858
QualType CanonicalInitVarType = InitVarType.getCanonicalType ();
798
859
@@ -831,6 +892,8 @@ void LoopConvertCheck::check(const MatchFinder::MatchResult &Result) {
831
892
FixerKind = LFK_Array;
832
893
} else if ((Loop = Nodes.getNodeAs <ForStmt>(LoopNameIterator))) {
833
894
FixerKind = LFK_Iterator;
895
+ } else if ((Loop = Nodes.getNodeAs <ForStmt>(LoopNameReverseIterator))) {
896
+ FixerKind = LFK_ReverseIterator;
834
897
} else {
835
898
Loop = Nodes.getNodeAs <ForStmt>(LoopNamePseudoArray);
836
899
assert (Loop && " Bad Callback. No for statement" );
@@ -858,10 +921,11 @@ void LoopConvertCheck::check(const MatchFinder::MatchResult &Result) {
858
921
// With array loops, the container is often discovered during the
859
922
// ForLoopIndexUseVisitor traversal.
860
923
const Expr *ContainerExpr = nullptr ;
861
- if (FixerKind == LFK_Iterator) {
862
- ContainerExpr = findContainer (Context, LoopVar->getInit (),
863
- EndVar ? EndVar->getInit () : EndCall,
864
- &Descriptor.ContainerNeedsDereference );
924
+ if (FixerKind == LFK_Iterator || FixerKind == LFK_ReverseIterator) {
925
+ ContainerExpr = findContainer (
926
+ Context, LoopVar->getInit (), EndVar ? EndVar->getInit () : EndCall,
927
+ &Descriptor.ContainerNeedsDereference ,
928
+ /* IsReverse=*/ FixerKind == LFK_ReverseIterator);
865
929
} else if (FixerKind == LFK_PseudoArray) {
866
930
ContainerExpr = EndCall->getImplicitObjectArgument ();
867
931
Descriptor.ContainerNeedsDereference =
@@ -927,6 +991,23 @@ void LoopConvertCheck::check(const MatchFinder::MatchResult &Result) {
927
991
Finder.aliasFromForInit (), Loop, Descriptor);
928
992
}
929
993
994
+ llvm::StringRef LoopConvertCheck::getReverseFunction () const {
995
+ if (!ReverseFunction.empty ())
996
+ return ReverseFunction;
997
+ if (UseReverseRanges)
998
+ return " std::ranges::reverse_view" ;
999
+ return " " ;
1000
+ }
1001
+
1002
+ llvm::StringRef LoopConvertCheck::getReverseHeader () const {
1003
+ if (!ReverseHeader.empty ())
1004
+ return ReverseHeader;
1005
+ if (UseReverseRanges && ReverseFunction.empty ()) {
1006
+ return " <ranges>" ;
1007
+ }
1008
+ return " " ;
1009
+ }
1010
+
930
1011
} // namespace modernize
931
1012
} // namespace tidy
932
1013
} // namespace clang
0 commit comments