Skip to content

Commit 5330653

Browse files
committed
Improve -verify’s wrong diagnostic kind diagnosis
If, for instance, an error is emitted as a warning instead, the verifier now detects this and emits a single diagnostic saying that the warning was found but had the wrong kind, instead of emitting one diagnostic saying the error was missing and another saying the warning was unexpected. In theory there are some edge cases we could handle better by doing two separate passes—one to detect exact expectation matches and remove them, another to detect near-misses and diagnose them—but in practice, I think the text + diagnostic location is likely to be unique enough to keep this from being a problem. (I would hesitate to do wrong-line diagnostics in the same pass like this, though.)
1 parent e04ec5a commit 5330653

File tree

2 files changed

+66
-28
lines changed

2 files changed

+66
-28
lines changed

lib/Frontend/DiagnosticVerifier.cpp

Lines changed: 62 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ struct ExpectedDiagnosticInfo {
7070
// This specifies the full range of the "expected-foo {{}}" specifier.
7171
const char *ExpectedStart, *ExpectedEnd = nullptr;
7272

73+
// This specifies the full range of the classification string.
74+
const char *ClassificationStart, *ClassificationEnd = nullptr;
75+
7376
DiagnosticKind Classification;
7477

7578
// This is true if a '*' constraint is present to say that the diagnostic
@@ -106,8 +109,11 @@ struct ExpectedDiagnosticInfo {
106109
Optional<ExpectedEducationalNotes> EducationalNotes;
107110

108111
ExpectedDiagnosticInfo(const char *ExpectedStart,
112+
const char *ClassificationStart,
113+
const char *ClassificationEnd,
109114
DiagnosticKind Classification)
110-
: ExpectedStart(ExpectedStart), Classification(Classification) {}
115+
: ExpectedStart(ExpectedStart), ClassificationStart(ClassificationStart),
116+
ClassificationEnd(ClassificationEnd), Classification(Classification) {}
111117
};
112118

113119
static std::string getDiagKindString(DiagnosticKind Kind) {
@@ -137,11 +143,15 @@ renderEducationalNotes(llvm::SmallVectorImpl<std::string> &EducationalNotes) {
137143
return OS.str();
138144
}
139145

140-
/// If we find the specified diagnostic in the list, return it.
141-
/// Otherwise return CapturedDiagnostics.end().
142-
static std::vector<CapturedDiagnosticInfo>::iterator
146+
/// If we find the specified diagnostic in the list, return it with \c true .
147+
/// If we find a near-match that varies only in classification, return it with
148+
/// \c false.
149+
/// Otherwise return \c CapturedDiagnostics.end() with \c false.
150+
static std::tuple<std::vector<CapturedDiagnosticInfo>::iterator, bool>
143151
findDiagnostic(std::vector<CapturedDiagnosticInfo> &CapturedDiagnostics,
144152
const ExpectedDiagnosticInfo &Expected, StringRef BufferName) {
153+
auto fallbackI = CapturedDiagnostics.end();
154+
145155
for (auto I = CapturedDiagnostics.begin(), E = CapturedDiagnostics.end();
146156
I != E; ++I) {
147157
// Verify the file and line of the diagnostic.
@@ -153,15 +163,22 @@ findDiagnostic(std::vector<CapturedDiagnosticInfo> &CapturedDiagnostics,
153163
continue;
154164

155165
// Verify the classification and string.
156-
if (I->Classification != Expected.Classification ||
157-
I->Message.find(Expected.MessageStr) == StringRef::npos)
166+
if (I->Message.find(Expected.MessageStr) == StringRef::npos)
158167
continue;
159168

169+
// Verify the classification and, if incorrect, remember as a second choice.
170+
if (I->Classification != Expected.Classification) {
171+
if (fallbackI == E && !Expected.MessageStr.empty())
172+
fallbackI = I;
173+
continue;
174+
}
175+
160176
// Okay, we found a match, hurray!
161-
return I;
177+
return { I, true };
162178
}
163179

164-
return CapturedDiagnostics.end();
180+
// No perfect match; we'll return the fallback or `end()` instead.
181+
return { fallbackI, false };
165182
}
166183

167184
/// If there are any -verify errors (e.g. differences between expectations
@@ -372,20 +389,22 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
372389
// the next match.
373390
StringRef MatchStart = InputFile.substr(Match);
374391
const char *DiagnosticLoc = MatchStart.data();
392+
MatchStart = MatchStart.substr(strlen("expected-"));
393+
const char *ClassificationStartLoc = MatchStart.data();
375394

376395
DiagnosticKind ExpectedClassification;
377-
if (MatchStart.startswith("expected-note")) {
396+
if (MatchStart.startswith("note")) {
378397
ExpectedClassification = DiagnosticKind::Note;
379-
MatchStart = MatchStart.substr(strlen("expected-note"));
380-
} else if (MatchStart.startswith("expected-warning")) {
398+
MatchStart = MatchStart.substr(strlen("note"));
399+
} else if (MatchStart.startswith("warning")) {
381400
ExpectedClassification = DiagnosticKind::Warning;
382-
MatchStart = MatchStart.substr(strlen("expected-warning"));
383-
} else if (MatchStart.startswith("expected-error")) {
401+
MatchStart = MatchStart.substr(strlen("warning"));
402+
} else if (MatchStart.startswith("error")) {
384403
ExpectedClassification = DiagnosticKind::Error;
385-
MatchStart = MatchStart.substr(strlen("expected-error"));
386-
} else if (MatchStart.startswith("expected-remark")) {
404+
MatchStart = MatchStart.substr(strlen("error"));
405+
} else if (MatchStart.startswith("remark")) {
387406
ExpectedClassification = DiagnosticKind::Remark;
388-
MatchStart = MatchStart.substr(strlen("expected-remark"));
407+
MatchStart = MatchStart.substr(strlen("remark"));
389408
} else
390409
continue;
391410

@@ -399,7 +418,9 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
399418
continue;
400419
}
401420

402-
ExpectedDiagnosticInfo Expected(DiagnosticLoc, ExpectedClassification);
421+
ExpectedDiagnosticInfo Expected(DiagnosticLoc, ClassificationStartLoc,
422+
/*ClassificationEndLoc=*/MatchStart.data(),
423+
ExpectedClassification);
403424
int LineOffset = 0;
404425

405426
if (TextStartIdx > 0 && MatchStart[0] == '@') {
@@ -675,18 +696,40 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
675696
auto &expected = ExpectedDiagnostics[i];
676697

677698
// Check to see if we had this expected diagnostic.
678-
auto FoundDiagnosticIter =
699+
auto FoundDiagnosticInfo =
679700
findDiagnostic(CapturedDiagnostics, expected, BufferName);
701+
auto FoundDiagnosticIter = std::get<0>(FoundDiagnosticInfo);
680702
if (FoundDiagnosticIter == CapturedDiagnostics.end()) {
681703
// Diagnostic didn't exist. If this is a 'mayAppear' diagnostic, then
682704
// we're ok. Otherwise, leave it in the list.
683705
if (expected.mayAppear)
684706
ExpectedDiagnostics.erase(ExpectedDiagnostics.begin()+i);
685707
continue;
686708
}
687-
709+
710+
auto emitFixItsError = [&](const char *location, const Twine &message,
711+
const char *replStartLoc, const char *replEndLoc,
712+
const std::string &replStr) {
713+
llvm::SMFixIt fix(llvm::SMRange(SMLoc::getFromPointer(replStartLoc),
714+
SMLoc::getFromPointer(replEndLoc)),
715+
replStr);
716+
addError(location, message, fix);
717+
};
718+
688719
auto &FoundDiagnostic = *FoundDiagnosticIter;
689720

721+
if (!std::get<1>(FoundDiagnosticInfo)) {
722+
// Found a diagnostic with the right location and text but the wrong
723+
// classification. We'll emit an error about the mismatch and
724+
// thereafter pretend that the diagnostic fully matched.
725+
auto expectedKind = getDiagKindString(expected.Classification);
726+
auto actualKind = getDiagKindString(FoundDiagnostic.Classification);
727+
emitFixItsError(expected.ClassificationStart,
728+
llvm::Twine("expected ") + expectedKind + ", not " + actualKind,
729+
expected.ClassificationStart, expected.ClassificationEnd,
730+
actualKind);
731+
}
732+
690733
const char *missedFixitLoc = nullptr;
691734
// Verify that any expected fix-its are present in the diagnostic.
692735
for (auto fixit : expected.Fixits) {
@@ -716,15 +759,6 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
716759
actualFixitsStr};
717760
};
718761

719-
auto emitFixItsError = [&](const char *location, const Twine &message,
720-
const char *replStartLoc, const char *replEndLoc,
721-
const std::string &replStr) {
722-
llvm::SMFixIt fix(llvm::SMRange(SMLoc::getFromPointer(replStartLoc),
723-
SMLoc::getFromPointer(replEndLoc)),
724-
replStr);
725-
addError(location, message, fix);
726-
};
727-
728762
// If we have any expected fixits that didn't get matched, then they are
729763
// wrong. Replace the failed fixit with what actually happened.
730764

test/Frontend/verify.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ undefinedFunc()
88
// CHECK: [[@LINE+1]]:4: error: expected error not produced
99
// expected-error{{This error message never gets output}}
1010

11+
anotherUndefinedFunc()
12+
// CHECK: [[@LINE+1]]:13: error: expected warning, not error
13+
// expected-warning@-2 {{cannot find 'anotherUndefinedFunc' in scope}}
14+
1115
// CHECK: [[@LINE+1]]:20: error: expected {{{{}} in {{expected}}-{{warning}}
1216
// expected-warning
1317

0 commit comments

Comments
 (0)