@@ -34,6 +34,9 @@ struct ExpectedFixIt {
3434} // end namespace swift
3535
3636namespace {
37+
38+ static constexpr StringLiteral fixitExpectationNoneString (" none" );
39+
3740struct ExpectedDiagnosticInfo {
3841 // This specifies the full range of the "expected-foo {{}}" specifier.
3942 const char *ExpectedStart, *ExpectedEnd = nullptr ;
@@ -46,7 +49,7 @@ struct ExpectedDiagnosticInfo {
4649
4750 // This is true if a '{{none}}' is present to mark that there should be no
4851 // extra fixits.
49- bool noExtraFixitsMayAppear = false ;
52+ bool noExtraFixitsMayAppear () const { return noneMarkerStartLoc != nullptr ; } ;
5053
5154 // This is the raw input buffer for the message text, the part in the
5255 // {{...}}
@@ -59,6 +62,9 @@ struct ExpectedDiagnosticInfo {
5962
6063 std::vector<ExpectedFixIt> Fixits;
6164
65+ // Loc of {{none}}
66+ const char *noneMarkerStartLoc = nullptr ;
67+
6268 ExpectedDiagnosticInfo (const char *ExpectedStart,
6369 DiagnosticKind Classification)
6470 : ExpectedStart(ExpectedStart), Classification(Classification) {}
@@ -288,16 +294,15 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
288294 unsigned PrevExpectedContinuationLine = 0 ;
289295
290296 std::vector<ExpectedDiagnosticInfo> ExpectedDiagnostics;
291-
292- auto addError = [&](const char *Loc, std::string message,
297+
298+ auto addError = [&](const char *Loc, const Twine & message,
293299 ArrayRef<llvm::SMFixIt> FixIts = {}) {
294300 auto loc = SourceLoc (SMLoc::getFromPointer (Loc));
295301 auto diag = SM.GetMessage (loc, llvm::SourceMgr::DK_Error, message,
296302 {}, FixIts);
297303 Errors.push_back (diag);
298304 };
299-
300-
305+
301306 // Scan the memory buffer looking for expected-note/warning/error.
302307 for (size_t Match = InputFile.find (" expected-" );
303308 Match != StringRef::npos; Match = InputFile.find (" expected-" , Match+1 )) {
@@ -457,11 +462,25 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
457462 ExtraChecks = ExtraChecks.substr (EndLoc+2 ).ltrim ();
458463
459464 // 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 ;
462474 continue ;
463475 }
464-
476+
477+ if (Expected.noneMarkerStartLoc ) {
478+ addError (Expected.noneMarkerStartLoc , Twine (" {{" ) +
479+ fixitExpectationNoneString +
480+ " }} must be at the end." );
481+ break ;
482+ }
483+
465484 // Parse the pieces of the fix-it.
466485 size_t MinusLoc = FixItStr.find (' -' );
467486 if (MinusLoc == StringRef::npos) {
@@ -547,46 +566,121 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
547566
548567 auto &FoundDiagnostic = *FoundDiagnosticIter;
549568
550- const char *IncorrectFixit = nullptr ;
569+ const char *missedFixitLoc = nullptr ;
551570 // Verify that any expected fix-its are present in the diagnostic.
552571 for (auto fixit : expected.Fixits ) {
553572 // 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+ }
556577 }
557578
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+ };
560606
561607 // If we have any expected fixits that didn't get matched, then they are
562608 // 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+
564623 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+ }
566636 } 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 ;
576640 }
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);
588682 }
589-
683+
590684 // Actually remove the diagnostic from the list, so we don't match it
591685 // again. We do have to do this after checking fix-its, though, because
592686 // the diagnostic owns its fix-its.
0 commit comments