Skip to content

Commit cdcd726

Browse files
beccadaxtshortli
authored andcommitted
Add fixit alternation to -verify
You can now put `||` between two fix-its to indicate that the test succeeds if either of them is present. This is meant for situations where a fix-it might vary slightly in different subtests or test configurations. Also fixes a bug in the diagnostic verifier where "expected-whatever" would search beyond the same line for its opening "{{", potentially finding one many lines away and giving a bad diagnostic and poor recovery behavior.
1 parent a61595c commit cdcd726

File tree

3 files changed

+80
-39
lines changed

3 files changed

+80
-39
lines changed

include/swift/Frontend/DiagnosticVerifier.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ class DiagnosticVerifier : public DiagnosticConsumer {
132132
/// unexpected ones.
133133
Result verifyFile(unsigned BufferID);
134134

135-
bool checkForFixIt(const ExpectedFixIt &Expected,
135+
bool checkForFixIt(const std::vector<ExpectedFixIt> &ExpectedAlts,
136136
const CapturedDiagnosticInfo &D, unsigned BufferID) const;
137137

138138
// Render the verifier syntax for a given set of fix-its.

lib/Frontend/DiagnosticVerifier.cpp

Lines changed: 64 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ struct ExpectedDiagnosticInfo {
9494
unsigned LineNo = ~0U;
9595
Optional<unsigned> ColumnNo;
9696

97-
std::vector<ExpectedFixIt> Fixits;
97+
using AlternativeExpectedFixIts = std::vector<ExpectedFixIt>;
98+
std::vector<AlternativeExpectedFixIts> Fixits = {};
9899

99100
// Loc of {{none}}
100101
const char *noneMarkerStartLoc = nullptr;
@@ -284,34 +285,36 @@ verifyUnknown(SourceManager &SM,
284285

285286
/// Return true if the given \p ExpectedFixIt is in the fix-its emitted by
286287
/// diagnostic \p D.
287-
bool DiagnosticVerifier::checkForFixIt(const ExpectedFixIt &Expected,
288-
const CapturedDiagnosticInfo &D,
289-
unsigned BufferID) const {
288+
bool DiagnosticVerifier::checkForFixIt(
289+
const ExpectedDiagnosticInfo::AlternativeExpectedFixIts &ExpectedAlts,
290+
const CapturedDiagnosticInfo &D, unsigned BufferID) const {
290291
for (auto &ActualFixIt : D.FixIts) {
291-
if (ActualFixIt.getText() != Expected.Text)
292-
continue;
292+
for (auto &Expected : ExpectedAlts) {
293+
if (ActualFixIt.getText() != Expected.Text)
294+
continue;
293295

294-
LineColumnRange ActualRange = ActualFixIt.getLineColumnRange(
295-
SM, BufferID,
296-
// Don't compute line numbers unless we have to.
297-
/*ComputeStartLocLine=*/Expected.Range.StartLine !=
298-
LineColumnRange::NoValue,
299-
/*ComputeEndLocLine=*/Expected.Range.EndLine !=
300-
LineColumnRange::NoValue);
296+
LineColumnRange ActualRange = ActualFixIt.getLineColumnRange(
297+
SM, BufferID,
298+
// Don't compute line numbers unless we have to.
299+
/*ComputeStartLocLine=*/Expected.Range.StartLine !=
300+
LineColumnRange::NoValue,
301+
/*ComputeEndLocLine=*/Expected.Range.EndLine !=
302+
LineColumnRange::NoValue);
301303

302-
if (Expected.Range.StartCol != ActualRange.StartCol ||
303-
Expected.Range.EndCol != ActualRange.EndCol) {
304-
continue;
305-
}
306-
if (Expected.Range.StartLine != LineColumnRange::NoValue &&
307-
Expected.Range.StartLine != ActualRange.StartLine) {
308-
continue;
309-
}
310-
if (Expected.Range.EndLine != LineColumnRange::NoValue &&
311-
Expected.Range.EndLine != ActualRange.EndLine) {
312-
continue;
304+
if (Expected.Range.StartCol != ActualRange.StartCol ||
305+
Expected.Range.EndCol != ActualRange.EndCol) {
306+
continue;
307+
}
308+
if (Expected.Range.StartLine != LineColumnRange::NoValue &&
309+
Expected.Range.StartLine != ActualRange.StartLine) {
310+
continue;
311+
}
312+
if (Expected.Range.EndLine != LineColumnRange::NoValue &&
313+
Expected.Range.EndLine != ActualRange.EndLine) {
314+
continue;
315+
}
316+
return true;
313317
}
314-
return true;
315318
}
316319

317320
return false;
@@ -521,7 +524,8 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
521524
MatchStart = MatchStart.substr(MatchStart.find_first_not_of(" \t"));
522525

523526
size_t TextStartIdx = MatchStart.find("{{");
524-
if (TextStartIdx == StringRef::npos) {
527+
if (TextStartIdx >=
528+
MatchStart.find("\n")) { // Either not found, or found beyond next \n
525529
addError(MatchStart.data(),
526530
"expected {{ in expected-warning/note/error line");
527531
continue;
@@ -580,7 +584,7 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
580584

581585
unsigned Count = 1;
582586
if (TextStartIdx > 0) {
583-
StringRef CountStr = MatchStart.substr(0, TextStartIdx).trim();
587+
StringRef CountStr = MatchStart.substr(0, TextStartIdx).trim(" \t");
584588
if (CountStr == "*") {
585589
Expected.mayAppear = true;
586590
} else {
@@ -630,6 +634,7 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
630634

631635

632636
// Scan for fix-its: {{10-14=replacement text}}
637+
bool startNewAlternatives = true;
633638
StringRef ExtraChecks = MatchStart.substr(End+2).ltrim(" \t");
634639
while (ExtraChecks.startswith("{{")) {
635640
// First make sure we have a closing "}}".
@@ -658,7 +663,23 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
658663
}
659664

660665
// Prepare for the next round of checks.
661-
ExtraChecks = ExtraChecks.substr(EndIndex + 2).ltrim();
666+
ExtraChecks = ExtraChecks.substr(EndIndex + 2).ltrim(" \t");
667+
668+
// Handle fix-it alternation.
669+
// If two fix-its are separated by `||`, we can match either of the two.
670+
// This is represented by putting them in the same subarray of `Fixits`.
671+
// If they are not separated by `||`, we must match both of them.
672+
// This is represented by putting them in separate subarrays of `Fixits`.
673+
if (startNewAlternatives &&
674+
(Expected.Fixits.empty() || !Expected.Fixits.back().empty()))
675+
Expected.Fixits.push_back({});
676+
677+
if (ExtraChecks.startswith("||")) {
678+
startNewAlternatives = false;
679+
ExtraChecks = ExtraChecks.substr(2).ltrim(" \t");
680+
} else {
681+
startNewAlternatives = true;
682+
}
662683

663684
// If this check starts with 'educational-notes=', check for one or more
664685
// educational notes instead of a fix-it.
@@ -747,10 +768,14 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
747768
FixIt.Text += *current++;
748769
}
749770
}
750-
751-
Expected.Fixits.push_back(FixIt);
771+
772+
Expected.Fixits.back().push_back(FixIt);
752773
}
753774

775+
// If there's a trailing empty alternation, remove it.
776+
if (!Expected.Fixits.empty() && Expected.Fixits.back().empty())
777+
Expected.Fixits.pop_back();
778+
754779
Expected.ExpectedEnd = ExtraChecks.data();
755780

756781
// Don't include trailing whitespace in the expected-foo{{}} range.
@@ -762,7 +787,6 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
762787
ExpectedDiagnostics.push_back(Expected);
763788
}
764789

765-
766790
// Make sure all the expected diagnostics appeared.
767791
std::reverse(ExpectedDiagnostics.begin(), ExpectedDiagnostics.end());
768792

@@ -807,10 +831,12 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
807831

808832
const char *missedFixitLoc = nullptr;
809833
// Verify that any expected fix-its are present in the diagnostic.
810-
for (auto fixit : expected.Fixits) {
834+
for (auto fixitAlternates : expected.Fixits) {
835+
assert(!fixitAlternates.empty() && "an empty alternation survived");
836+
811837
// If we found it, we're ok.
812-
if (!checkForFixIt(fixit, FoundDiagnostic, BufferID)) {
813-
missedFixitLoc = fixit.StartLoc;
838+
if (!checkForFixIt(fixitAlternates, FoundDiagnostic, BufferID)) {
839+
missedFixitLoc = fixitAlternates.front().StartLoc;
814840
break;
815841
}
816842
}
@@ -844,8 +870,8 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
844870
assert(!expected.Fixits.empty() &&
845871
"some fix-its should be expected here");
846872

847-
const char *replStartLoc = expected.Fixits.front().StartLoc;
848-
const char *replEndLoc = expected.Fixits.back().EndLoc;
873+
const char *replStartLoc = expected.Fixits.front().front().StartLoc;
874+
const char *replEndLoc = expected.Fixits.back().back().EndLoc;
849875

850876
std::string message = "expected fix-it not seen";
851877
std::string actualFixits;
@@ -886,8 +912,8 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
886912
replEndLoc = expected.noneMarkerStartLoc;
887913
} else {
888914
message = "unexpected fix-it seen";
889-
replStartLoc = expected.Fixits.front().StartLoc;
890-
replEndLoc = expected.Fixits.back().EndLoc;
915+
replStartLoc = expected.Fixits.front().front().StartLoc;
916+
replEndLoc = expected.Fixits.back().back().EndLoc;
891917
}
892918

893919
auto phrase = makeActualFixitsPhrase(FoundDiagnostic.FixIts);

test/Frontend/verify.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,21 @@ anotherUndefinedFunc()
1515
// CHECK: [[@LINE+1]]:20: error: expected {{{{}} in {{expected}}-{{warning}}
1616
// expected-warning
1717

18+
func fn() {}
19+
fn(()) // expected-error {{argument passed to call that takes no arguments}}
20+
fn(()) // expected-error {{argument passed to call that takes no arguments}} {{4-6=}}
21+
fn(()) // expected-error {{argument passed to call that takes no arguments}} {{4-6=}}||{{3-4=fnord}}
22+
fn(()) // expected-error {{argument passed to call that takes no arguments}} {{3-4=fnord}} || {{4-6=}}
23+
fn(()) // expected-error {{argument passed to call that takes no arguments}} {{4-6=}} {{none}}
24+
fn(()) // expected-error {{argument passed to call that takes no arguments}} {{4-6=}}||{{3-4=fnord}} {{none}}
25+
fn(()) // expected-error {{argument passed to call that takes no arguments}} {{3-4=fnord}}||{{4-6=}} {{none}}
26+
27+
// CHECK: [[@LINE+1]]:81: error: expected fix-it not seen; actual fix-it seen: {{[{][{]4-6=[}][}]}}
28+
fn(()) // expected-error {{argument passed to call that takes no arguments}} {{3-4=fnord}} {{4-6=}}
29+
30+
// CHECK: [[@LINE+1]]:81: error: expected no fix-its; actual fix-it seen: {{[{][{]4-6=[}][}]}}
31+
fn(()) // expected-error {{argument passed to call that takes no arguments}} {{none}}
32+
1833
// CHECK: [[@LINE+2]]:8: error: unexpected error produced: generic type 'Array' specialized with too many type parameters
1934
// CHECK: note: diagnostic produced elsewhere: generic type 'Array' declared here
2035
let x: Array<Int, Int>

0 commit comments

Comments
 (0)