@@ -34,6 +34,9 @@ struct ExpectedFixIt {
34
34
} // end namespace swift
35
35
36
36
namespace {
37
+
38
+ static constexpr StringLiteral fixitExpectationNoneString (" none" );
39
+
37
40
struct ExpectedDiagnosticInfo {
38
41
// This specifies the full range of the "expected-foo {{}}" specifier.
39
42
const char *ExpectedStart, *ExpectedEnd = nullptr ;
@@ -46,7 +49,7 @@ struct ExpectedDiagnosticInfo {
46
49
47
50
// This is true if a '{{none}}' is present to mark that there should be no
48
51
// extra fixits.
49
- bool noExtraFixitsMayAppear = false ;
52
+ bool noExtraFixitsMayAppear () const { return noneMarkerStartLoc != nullptr ; } ;
50
53
51
54
// This is the raw input buffer for the message text, the part in the
52
55
// {{...}}
@@ -59,6 +62,9 @@ struct ExpectedDiagnosticInfo {
59
62
60
63
std::vector<ExpectedFixIt> Fixits;
61
64
65
+ // Loc of {{none}}
66
+ const char *noneMarkerStartLoc = nullptr ;
67
+
62
68
ExpectedDiagnosticInfo (const char *ExpectedStart,
63
69
DiagnosticKind Classification)
64
70
: ExpectedStart(ExpectedStart), Classification(Classification) {}
@@ -288,16 +294,15 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
288
294
unsigned PrevExpectedContinuationLine = 0 ;
289
295
290
296
std::vector<ExpectedDiagnosticInfo> ExpectedDiagnostics;
291
-
292
- auto addError = [&](const char *Loc, std::string message,
297
+
298
+ auto addError = [&](const char *Loc, const Twine & message,
293
299
ArrayRef<llvm::SMFixIt> FixIts = {}) {
294
300
auto loc = SourceLoc (SMLoc::getFromPointer (Loc));
295
301
auto diag = SM.GetMessage (loc, llvm::SourceMgr::DK_Error, message,
296
302
{}, FixIts);
297
303
Errors.push_back (diag);
298
304
};
299
-
300
-
305
+
301
306
// Scan the memory buffer looking for expected-note/warning/error.
302
307
for (size_t Match = InputFile.find (" expected-" );
303
308
Match != StringRef::npos; Match = InputFile.find (" expected-" , Match+1 )) {
@@ -457,11 +462,25 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
457
462
ExtraChecks = ExtraChecks.substr (EndLoc+2 ).ltrim ();
458
463
459
464
// Special case for specifying no fixits should appear.
460
- if (FixItStr == " none" ) {
461
- Expected.noExtraFixitsMayAppear = true ;
465
+ if (FixItStr == fixitExpectationNoneString) {
466
+ if (Expected.noneMarkerStartLoc ) {
467
+ addError (FixItStr.data () - 2 ,
468
+ Twine (" A second {{" ) + fixitExpectationNoneString +
469
+ " }} was found. It may only appear once in an expectation." );
470
+ break ;
471
+ }
472
+
473
+ Expected.noneMarkerStartLoc = FixItStr.data () - 2 ;
462
474
continue ;
463
475
}
464
-
476
+
477
+ if (Expected.noneMarkerStartLoc ) {
478
+ addError (Expected.noneMarkerStartLoc , Twine (" {{" ) +
479
+ fixitExpectationNoneString +
480
+ " }} must be at the end." );
481
+ break ;
482
+ }
483
+
465
484
// Parse the pieces of the fix-it.
466
485
size_t MinusLoc = FixItStr.find (' -' );
467
486
if (MinusLoc == StringRef::npos) {
@@ -547,46 +566,121 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
547
566
548
567
auto &FoundDiagnostic = *FoundDiagnosticIter;
549
568
550
- const char *IncorrectFixit = nullptr ;
569
+ const char *missedFixitLoc = nullptr ;
551
570
// Verify that any expected fix-its are present in the diagnostic.
552
571
for (auto fixit : expected.Fixits ) {
553
572
// If we found it, we're ok.
554
- if (!checkForFixIt (fixit, FoundDiagnostic, InputFile))
555
- IncorrectFixit = fixit.StartLoc ;
573
+ if (!checkForFixIt (fixit, FoundDiagnostic, InputFile)) {
574
+ missedFixitLoc = fixit.StartLoc ;
575
+ break ;
576
+ }
556
577
}
557
578
558
- bool matchedAllFixIts =
559
- expected.Fixits .size () == FoundDiagnostic.FixIts .size ();
579
+ const bool isUnexpectedFixitsSeen =
580
+ expected.Fixits .size () < FoundDiagnostic.FixIts .size ();
581
+
582
+ struct ActualFixitsPhrase {
583
+ std::string phrase;
584
+ std::string actualFixits;
585
+ };
586
+
587
+ auto makeActualFixitsPhrase =
588
+ [&](ArrayRef<DiagnosticInfo::FixIt> actualFixits)
589
+ -> ActualFixitsPhrase {
590
+ std::string actualFixitsStr = renderFixits (actualFixits, InputFile);
591
+
592
+ auto phrase = Twine (" actual fix-it" ) +
593
+ (actualFixits.size () >= 2 ? " s" : " " ) +
594
+ " seen: " + actualFixitsStr;
595
+ return ActualFixitsPhrase{phrase.str (), actualFixitsStr};
596
+ };
597
+
598
+ auto emitFixItsError = [&](const char *location, const Twine &message,
599
+ const char *replStartLoc, const char *replEndLoc,
600
+ const std::string &replStr) {
601
+ llvm::SMFixIt fix (llvm::SMRange (SMLoc::getFromPointer (replStartLoc),
602
+ SMLoc::getFromPointer (replEndLoc)),
603
+ replStr);
604
+ addError (location, message, fix);
605
+ };
560
606
561
607
// If we have any expected fixits that didn't get matched, then they are
562
608
// wrong. Replace the failed fixit with what actually happened.
563
- if (IncorrectFixit) {
609
+
610
+ if (missedFixitLoc) {
611
+ // If we had an incorrect expected fixit, render it and produce a fixit
612
+ // of our own.
613
+
614
+ assert (!expected.Fixits .empty () &&
615
+ " some fix-its should be expected here" );
616
+
617
+ const char *replStartLoc = expected.Fixits .front ().StartLoc ;
618
+ const char *replEndLoc = expected.Fixits .back ().EndLoc ;
619
+
620
+ std::string message = " expected fix-it not seen" ;
621
+ std::string actualFixits;
622
+
564
623
if (FoundDiagnostic.FixIts .empty ()) {
565
- addError (IncorrectFixit, " expected fix-it not seen" );
624
+ // / If actual fix-its is empty,
625
+ // / eat a space before first marker.
626
+ // / For example,
627
+ // /
628
+ // / @code
629
+ // / expected-error {{message}} {{1-2=aa}}
630
+ // / ~~~~~~~~~~~
631
+ // / ^ remove
632
+ // / @endcode
633
+ if (replStartLoc[-1 ] == ' ' ) {
634
+ replStartLoc--;
635
+ }
566
636
} else {
567
- // If we had an incorrect expected fixit, render it and produce a fixit
568
- // of our own.
569
- auto actual = renderFixits (FoundDiagnostic.FixIts , InputFile);
570
- auto replStartLoc = SMLoc::getFromPointer (expected.Fixits [0 ].StartLoc );
571
- auto replEndLoc = SMLoc::getFromPointer (expected.Fixits .back ().EndLoc );
572
-
573
- llvm::SMFixIt fix (llvm::SMRange (replStartLoc, replEndLoc), actual);
574
- addError (IncorrectFixit,
575
- " expected fix-it not seen; actual fix-its: " + actual, fix);
637
+ auto phrase = makeActualFixitsPhrase (FoundDiagnostic.FixIts );
638
+ actualFixits = phrase.actualFixits ;
639
+ message += " ; " + phrase.phrase ;
576
640
}
577
- } else if (expected.noExtraFixitsMayAppear &&
578
- !matchedAllFixIts &&
579
- !expected.mayAppear ) {
580
- // If there was no fixit specification, but some were produced, add a
581
- // fixit to add them in.
582
- auto actual = renderFixits (FoundDiagnostic.FixIts , InputFile);
583
- auto replStartLoc = SMLoc::getFromPointer (expected.ExpectedEnd - 8 ); // {{none}} length
584
- auto replEndLoc = SMLoc::getFromPointer (expected.ExpectedEnd );
585
-
586
- llvm::SMFixIt fix (llvm::SMRange (replStartLoc, replEndLoc), actual);
587
- addError (replStartLoc.getPointer (), " expected no fix-its; actual fix-it seen: " + actual, fix);
641
+
642
+ emitFixItsError (missedFixitLoc, message, replStartLoc, replEndLoc,
643
+ actualFixits);
644
+ } else if (expected.noExtraFixitsMayAppear () && isUnexpectedFixitsSeen) {
645
+ // If unexpected fixit were produced, add a fixit to add them in.
646
+
647
+ assert (!FoundDiagnostic.FixIts .empty () &&
648
+ " some fix-its should be produced here" );
649
+ assert (expected.noneMarkerStartLoc && " none marker location is null" );
650
+
651
+ const char *replStartLoc = nullptr , *replEndLoc = nullptr ;
652
+ std::string message;
653
+ if (expected.Fixits .empty ()) {
654
+ message = " expected no fix-its" ;
655
+ replStartLoc = expected.noneMarkerStartLoc ;
656
+ replEndLoc = expected.noneMarkerStartLoc ;
657
+ } else {
658
+ message = " unexpected fix-it seen" ;
659
+ replStartLoc = expected.Fixits .front ().StartLoc ;
660
+ replEndLoc = expected.Fixits .back ().EndLoc ;
661
+ }
662
+
663
+ auto phrase = makeActualFixitsPhrase (FoundDiagnostic.FixIts );
664
+ std::string actualFixits = phrase.actualFixits ;
665
+ message += " ; " + phrase.phrase ;
666
+
667
+ if (replStartLoc == replEndLoc) {
668
+ // / If no fix-its was expected and range of replacement is empty,
669
+ // / insert space after new last marker.
670
+ // / For example:
671
+ // /
672
+ // / @code
673
+ // / expected-error {{message}} {{none}}
674
+ // / ^
675
+ // / insert `{{1-2=aa}} `
676
+ // / @endcode
677
+ actualFixits += " " ;
678
+ }
679
+
680
+ emitFixItsError (expected.noneMarkerStartLoc , message, replStartLoc,
681
+ replEndLoc, actualFixits);
588
682
}
589
-
683
+
590
684
// Actually remove the diagnostic from the list, so we don't match it
591
685
// again. We do have to do this after checking fix-its, though, because
592
686
// the diagnostic owns its fix-its.
0 commit comments