Skip to content

Commit 0466d5c

Browse files
committed
Handle diagnostic verifier locations concretely
This commit makes a number of adjustments to how the diagnostic verifier handles source buffers and source locations. Specifically: • Files named by `-verify-additional-file` are read as late as possible so that if some other component of the compiler has already loaded the file, even in some exotic way (e.g. ClangImporter’s source buffer mirroring), it will use the same buffer. • Expectation source locations now ignore virtual files and other trickery; they are based on the source buffer and physical location in the file. Hopefully this will make `-verify-additional-file` work better on Windows. As an unintended side effect, it also changes how expectations work in tests that use `#sourceLocation()`.
1 parent 12d0458 commit 0466d5c

File tree

6 files changed

+121
-93
lines changed

6 files changed

+121
-93
lines changed

include/swift/Frontend/DiagnosticVerifier.h

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,13 @@ struct LineColumnRange {
4545
};
4646

4747
class CapturedFixItInfo final {
48+
SourceManager *diagSM;
4849
DiagnosticInfo::FixIt FixIt;
4950
mutable LineColumnRange LineColRange;
5051

5152
public:
52-
CapturedFixItInfo(DiagnosticInfo::FixIt FixIt) : FixIt(FixIt) {}
53+
CapturedFixItInfo(SourceManager &diagSM, DiagnosticInfo::FixIt FixIt)
54+
: diagSM(&diagSM), FixIt(FixIt) {}
5355

5456
CharSourceRange &getSourceRange() { return FixIt.getRange(); }
5557
const CharSourceRange &getSourceRange() const { return FixIt.getRange(); }
@@ -58,13 +60,12 @@ class CapturedFixItInfo final {
5860

5961
/// Obtain the line-column range corresponding to the fix-it's
6062
/// replacement range.
61-
const LineColumnRange &getLineColumnRange(const SourceManager &SM,
62-
unsigned BufferID) const;
63+
const LineColumnRange &getLineColumnRange(SourceManager &SM) const;
6364
};
6465

6566
struct CapturedDiagnosticInfo {
6667
llvm::SmallString<128> Message;
67-
llvm::SmallString<32> FileName;
68+
std::optional<unsigned> SourceBufferID;
6869
DiagnosticKind Classification;
6970
SourceLoc Loc;
7071
unsigned Line;
@@ -73,14 +74,14 @@ struct CapturedDiagnosticInfo {
7374
SmallVector<std::string, 1> EducationalNotes;
7475

7576
CapturedDiagnosticInfo(llvm::SmallString<128> Message,
76-
llvm::SmallString<32> FileName,
77+
std::optional<unsigned> SourceBufferID,
7778
DiagnosticKind Classification, SourceLoc Loc,
7879
unsigned Line, unsigned Column,
7980
SmallVector<CapturedFixItInfo, 2> FixIts,
8081
SmallVector<std::string, 1> EducationalNotes)
81-
: Message(Message), FileName(FileName), Classification(Classification),
82-
Loc(Loc), Line(Line), Column(Column), FixIts(FixIts),
83-
EducationalNotes(EducationalNotes) {
82+
: Message(Message), SourceBufferID(SourceBufferID),
83+
Classification(Classification), Loc(Loc), Line(Line), Column(Column),
84+
FixIts(FixIts), EducationalNotes(EducationalNotes) {
8485
std::sort(EducationalNotes.begin(), EducationalNotes.end());
8586
}
8687
};
@@ -91,25 +92,23 @@ class DiagnosticVerifier : public DiagnosticConsumer {
9192
SourceManager &SM;
9293
std::vector<CapturedDiagnosticInfo> CapturedDiagnostics;
9394
ArrayRef<unsigned> BufferIDs;
94-
SmallVector<unsigned, 4> AdditionalBufferIDs;
95+
ArrayRef<std::string> AdditionalFilePaths;
9596
bool AutoApplyFixes;
9697
bool IgnoreUnknown;
9798
bool UseColor;
9899
ArrayRef<std::string> AdditionalExpectedPrefixes;
99100

100101
public:
101102
explicit DiagnosticVerifier(SourceManager &SM, ArrayRef<unsigned> BufferIDs,
103+
ArrayRef<std::string> AdditionalFilePaths,
102104
bool AutoApplyFixes, bool IgnoreUnknown,
103105
bool UseColor,
104106
ArrayRef<std::string> AdditionalExpectedPrefixes)
105-
: SM(SM), BufferIDs(BufferIDs), AutoApplyFixes(AutoApplyFixes),
106-
IgnoreUnknown(IgnoreUnknown), UseColor(UseColor),
107+
: SM(SM), BufferIDs(BufferIDs), AdditionalFilePaths(AdditionalFilePaths),
108+
AutoApplyFixes(AutoApplyFixes), IgnoreUnknown(IgnoreUnknown),
109+
UseColor(UseColor),
107110
AdditionalExpectedPrefixes(AdditionalExpectedPrefixes) {}
108111

109-
void appendAdditionalBufferID(unsigned bufferID) {
110-
AdditionalBufferIDs.push_back(bufferID);
111-
}
112-
113112
virtual void handleDiagnostic(SourceManager &SM,
114113
const DiagnosticInfo &Info) override;
115114

lib/Frontend/DiagnosticVerifier.cpp

Lines changed: 92 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -103,33 +103,71 @@ struct ExpectedFixIt {
103103
std::string Text;
104104
};
105105

106+
struct DiagLoc {
107+
std::optional<unsigned> bufferID;
108+
unsigned line;
109+
unsigned column;
110+
SourceLoc sourceLoc;
111+
112+
DiagLoc(SourceManager &diagSM, SourceManager &verifierSM,
113+
SourceLoc initialSourceLoc, bool wantEnd = false)
114+
: sourceLoc(initialSourceLoc), bufferID(std::nullopt), line(0), column(0)
115+
{
116+
if (sourceLoc.isInvalid())
117+
return;
118+
119+
// Walk out of generated code for macros in default arguments so that we
120+
// register diagnostics emitted in them at the call site instead.
121+
while (true) {
122+
bufferID = diagSM.findBufferContainingLoc(sourceLoc);
123+
ASSERT(bufferID.has_value());
124+
125+
auto generatedInfo = diagSM.getGeneratedSourceInfo(*bufferID);
126+
if (!generatedInfo || generatedInfo->originalSourceRange.isInvalid()
127+
|| generatedInfo->kind != GeneratedSourceInfo::DefaultArgument)
128+
break;
129+
130+
if (wantEnd)
131+
sourceLoc = generatedInfo->originalSourceRange.getEnd();
132+
else
133+
sourceLoc = generatedInfo->originalSourceRange.getStart();
134+
135+
ASSERT(sourceLoc.isValid());
136+
}
137+
138+
// If this diagnostic came from a different SourceManager (as can happen
139+
// while compiling a module interface), translate its SourceLoc to match the
140+
// verifier's SourceManager.
141+
if (&diagSM != &verifierSM) {
142+
sourceLoc = verifierSM.getLocForForeignLoc(sourceLoc, diagSM);
143+
bufferID = verifierSM.findBufferContainingLoc(sourceLoc);
144+
}
145+
146+
// At this point, `bufferID` is filled in and `sourceLoc` is a location in
147+
// that buffer.
148+
if (sourceLoc.isValid())
149+
std::tie(line, column) = verifierSM.getLineAndColumnInBuffer(sourceLoc);
150+
}
151+
};
152+
106153
} // end namespace swift
107154

108155
const LineColumnRange &
109-
CapturedFixItInfo::getLineColumnRange(const SourceManager &SM,
110-
unsigned BufferID) const {
156+
CapturedFixItInfo::getLineColumnRange(SourceManager &SM) const {
111157
if (LineColRange.StartLine != 0) {
112158
// Already computed.
113159
return LineColRange;
114160
}
115161

116162
auto SrcRange = FixIt.getRange();
117163

118-
std::tie(LineColRange.StartLine, LineColRange.StartCol) =
119-
SM.getPresumedLineAndColumnForLoc(SrcRange.getStart(), BufferID);
120-
121-
// We don't have to compute much if the end location is on the same line.
122-
if (SrcRange.getByteLength() == 0) {
123-
LineColRange.EndLine = LineColRange.StartLine;
124-
LineColRange.EndCol = LineColRange.StartCol;
125-
} else if (SM.extractText(SrcRange, BufferID).find_first_of("\n\r") ==
126-
StringRef::npos) {
127-
LineColRange.EndLine = LineColRange.StartLine;
128-
LineColRange.EndCol = LineColRange.StartCol + SrcRange.getByteLength();
129-
} else {
130-
std::tie(LineColRange.EndLine, LineColRange.EndCol) =
131-
SM.getPresumedLineAndColumnForLoc(SrcRange.getEnd(), BufferID);
132-
}
164+
DiagLoc startLoc(*diagSM, SM, SrcRange.getStart());
165+
LineColRange.StartLine = startLoc.line;
166+
LineColRange.StartCol = startLoc.column;
167+
168+
DiagLoc endLoc(*diagSM, SM, SrcRange.getEnd(), /*wantEnd=*/true);
169+
LineColRange.EndLine = endLoc.line;
170+
LineColRange.EndCol = endLoc.column;
133171

134172
return LineColRange;
135173
}
@@ -223,13 +261,13 @@ renderEducationalNotes(llvm::SmallVectorImpl<std::string> &EducationalNotes) {
223261
/// Otherwise return \c CapturedDiagnostics.end() with \c false.
224262
static std::tuple<std::vector<CapturedDiagnosticInfo>::iterator, bool>
225263
findDiagnostic(std::vector<CapturedDiagnosticInfo> &CapturedDiagnostics,
226-
const ExpectedDiagnosticInfo &Expected, StringRef BufferName) {
264+
const ExpectedDiagnosticInfo &Expected, unsigned BufferID) {
227265
auto fallbackI = CapturedDiagnostics.end();
228266

229267
for (auto I = CapturedDiagnostics.begin(), E = CapturedDiagnostics.end();
230268
I != E; ++I) {
231269
// Verify the file and line of the diagnostic.
232-
if (I->Line != Expected.LineNo || I->FileName != BufferName)
270+
if (I->Line != Expected.LineNo || I->SourceBufferID != BufferID)
233271
continue;
234272

235273
// If a specific column was expected, verify it.
@@ -363,7 +401,7 @@ bool DiagnosticVerifier::checkForFixIt(
363401
if (ActualFixIt.getText() != Expected.Text)
364402
continue;
365403

366-
auto &ActualRange = ActualFixIt.getLineColumnRange(SM, BufferID);
404+
auto &ActualRange = ActualFixIt.getLineColumnRange(SM);
367405

368406
if (Expected.Range.StartCol != ActualRange.StartCol ||
369407
Expected.Range.EndCol != ActualRange.EndCol ||
@@ -394,7 +432,7 @@ DiagnosticVerifier::renderFixits(ArrayRef<CapturedFixItInfo> ActualFixIts,
394432
interleave(
395433
ActualFixIts,
396434
[&](const CapturedFixItInfo &ActualFixIt) {
397-
auto &ActualRange = ActualFixIt.getLineColumnRange(SM, BufferID);
435+
auto &ActualRange = ActualFixIt.getLineColumnRange(SM);
398436
OS << "{{";
399437

400438
if (ActualRange.StartLine != DiagnosticLineNo)
@@ -566,7 +604,6 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
566604

567605
const SourceLoc BufferStartLoc = SM.getLocForBufferStart(BufferID);
568606
StringRef InputFile = SM.getEntireTextForBuffer(BufferID);
569-
StringRef BufferName = SM.getIdentifierForBuffer(BufferID);
570607

571608
// Queue up all of the diagnostics, allowing us to sort them and emit them in
572609
// file order.
@@ -709,7 +746,7 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
709746
if (PrevExpectedContinuationLine)
710747
Expected.LineNo = PrevExpectedContinuationLine;
711748
else
712-
Expected.LineNo = SM.getPresumedLineAndColumnForLoc(
749+
Expected.LineNo = SM.getLineAndColumnInBuffer(
713750
BufferStartLoc.getAdvancedLoc(MatchStart.data() -
714751
InputFile.data()),
715752
BufferID)
@@ -888,7 +925,7 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
888925

889926
// Check to see if we had this expected diagnostic.
890927
auto FoundDiagnosticInfo =
891-
findDiagnostic(CapturedDiagnostics, expected, BufferName);
928+
findDiagnostic(CapturedDiagnostics, expected, BufferID);
892929
auto FoundDiagnosticIter = std::get<0>(FoundDiagnosticInfo);
893930
if (FoundDiagnosticIter == CapturedDiagnostics.end()) {
894931
// Diagnostic didn't exist. If this is a 'mayAppear' diagnostic, then
@@ -1078,8 +1115,8 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
10781115
auto I = CapturedDiagnostics.begin();
10791116
for (auto E = CapturedDiagnostics.end(); I != E; ++I) {
10801117
// Verify the file and line of the diagnostic.
1081-
if (I->Line != expectedDiagIter->LineNo || I->FileName != BufferName ||
1082-
I->Classification != expectedDiagIter->Classification)
1118+
if (I->Line != expectedDiagIter->LineNo || I->SourceBufferID != BufferID
1119+
|| I->Classification != expectedDiagIter->Classification)
10831120
continue;
10841121

10851122
// Otherwise, we found it, break out.
@@ -1167,7 +1204,7 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
11671204
bool HadUnexpectedDiag = false;
11681205
auto CapturedDiagIter = CapturedDiagnostics.begin();
11691206
while (CapturedDiagIter != CapturedDiagnostics.end()) {
1170-
if (CapturedDiagIter->FileName != BufferName) {
1207+
if (CapturedDiagIter->SourceBufferID != BufferID) {
11711208
++CapturedDiagIter;
11721209
continue;
11731210
}
@@ -1241,7 +1278,7 @@ void DiagnosticVerifier::handleDiagnostic(SourceManager &SM,
12411278
const DiagnosticInfo &Info) {
12421279
SmallVector<CapturedFixItInfo, 2> fixIts;
12431280
for (const auto &fixIt : Info.FixIts) {
1244-
fixIts.emplace_back(fixIt);
1281+
fixIts.emplace_back(SM, fixIt);
12451282
}
12461283

12471284
llvm::SmallVector<std::string, 1> eduNotes;
@@ -1256,40 +1293,39 @@ void DiagnosticVerifier::handleDiagnostic(SourceManager &SM,
12561293
Info.FormatArgs);
12571294
}
12581295

1259-
if (Info.Loc.isValid()) {
1260-
const auto lineAndColumn = SM.getPresumedLineAndColumnForLoc(Info.Loc);
1261-
const auto fileName = SM.getDisplayNameForLoc(Info.Loc);
1262-
CapturedDiagnostics.emplace_back(message, fileName, Info.Kind, Info.Loc,
1263-
lineAndColumn.first, lineAndColumn.second,
1264-
fixIts, eduNotes);
1265-
} else {
1266-
CapturedDiagnostics.emplace_back(message, StringRef(), Info.Kind, Info.Loc,
1267-
0, 0, fixIts, eduNotes);
1268-
}
1269-
1270-
// If this diagnostic came from a different SourceManager (as can happen
1271-
// while compiling a module interface), translate its SourceLocs to match the
1272-
// verifier's SourceManager.
1273-
if (&SM != &(this->SM)) {
1274-
auto &capturedDiag = CapturedDiagnostics.back();
1275-
auto &correctSM = this->SM;
1276-
1277-
capturedDiag.Loc = correctSM.getLocForForeignLoc(capturedDiag.Loc, SM);
1278-
for (auto &fixIt : capturedDiag.FixIts) {
1279-
auto newStart =
1280-
correctSM.getLocForForeignLoc(fixIt.getSourceRange().getStart(), SM);
1281-
auto &mutableRange = fixIt.getSourceRange();
1282-
mutableRange =
1283-
CharSourceRange(newStart, fixIt.getSourceRange().getByteLength());
1284-
}
1285-
}
1296+
DiagLoc loc(SM, this->SM, Info.Loc);
1297+
CapturedDiagnostics.emplace_back(message, loc.bufferID, Info.Kind,
1298+
loc.sourceLoc, loc.line, loc.column, fixIts,
1299+
eduNotes);
12861300
}
12871301

12881302
/// Once all diagnostics have been captured, perform verification.
12891303
bool DiagnosticVerifier::finishProcessing() {
12901304
DiagnosticVerifier::Result Result = {false, false};
12911305

1292-
ArrayRef<unsigned> BufferIDLists[2] = { BufferIDs, AdditionalBufferIDs };
1306+
SmallVector<unsigned, 4> additionalBufferIDs;
1307+
for (auto path : AdditionalFilePaths) {
1308+
auto bufferID = SM.getIDForBufferIdentifier(path);
1309+
if (!bufferID) {
1310+
// Still need to scan this file for expectations.
1311+
auto result = SM.getFileSystem()->getBufferForFile(path);
1312+
if (!result) {
1313+
auto message = SM.GetMessage(
1314+
SourceLoc(), llvm::SourceMgr::DiagKind::DK_Error,
1315+
llvm::Twine("unable to open file in '-verify-additional-file ") +
1316+
path + "': " + result.getError().message(), {}, {});
1317+
printDiagnostic(message);
1318+
Result.HadError |= true;
1319+
continue;
1320+
}
1321+
bufferID = SM.addNewSourceBuffer(std::move(result.get()));
1322+
}
1323+
if (bufferID) {
1324+
additionalBufferIDs.push_back(*bufferID);
1325+
}
1326+
}
1327+
1328+
ArrayRef<unsigned> BufferIDLists[2] = { BufferIDs, additionalBufferIDs };
12931329
for (ArrayRef<unsigned> BufferIDList : BufferIDLists)
12941330
for (auto &BufferID : BufferIDList) {
12951331
DiagnosticVerifier::Result FileResult = verifyFile(BufferID);

lib/Frontend/Frontend.cpp

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -401,22 +401,10 @@ bool CompilerInstance::setupDiagnosticVerifierIfNeeded() {
401401

402402
if (diagOpts.VerifyMode != DiagnosticOptions::NoVerify) {
403403
DiagVerifier = std::make_unique<DiagnosticVerifier>(
404-
SourceMgr, InputSourceCodeBufferIDs,
404+
SourceMgr, InputSourceCodeBufferIDs, diagOpts.AdditionalVerifierFiles,
405405
diagOpts.VerifyMode == DiagnosticOptions::VerifyAndApplyFixes,
406406
diagOpts.VerifyIgnoreUnknown, diagOpts.UseColor,
407407
diagOpts.AdditionalDiagnosticVerifierPrefixes);
408-
for (const auto &filename : diagOpts.AdditionalVerifierFiles) {
409-
auto result = getFileSystem().getBufferForFile(filename);
410-
if (!result) {
411-
Diagnostics.diagnose(SourceLoc(), diag::error_open_input_file,
412-
filename, result.getError().message());
413-
hadError |= true;
414-
continue;
415-
}
416-
417-
auto bufferID = SourceMgr.addNewSourceBuffer(std::move(result.get()));
418-
DiagVerifier->appendAdditionalBufferID(bufferID);
419-
}
420408

421409
addDiagnosticConsumer(DiagVerifier.get());
422410
}

test/Misc/serialized-diagnostics.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ var z : Int // expected-error {{invalid redeclaration}}
1818
// CHECK-NEXT: Number FIXITs = 0
1919

2020
#sourceLocation(file: "fake-file.swuft", line: 4)
21-
var z : Int // Note: no expected-* here because it's "not in this file".
21+
var z : Int // expected-error {{invalid redeclaration of 'z'}}
2222
// CHECK-NEXT: {{^}}fake-file.swuft:4:5: error: invalid redeclaration of 'z' [] []
2323
// CHECK-NEXT: Number FIXITs = 0
2424
// CHECK-NEXT: +-{{.*[/\\]}}serialized-diagnostics.swift:{{[0-9]+}}:5: note: 'z' previously declared here [] []

test/Parse/ConditionalCompilation/decl_parse_errors.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ var val2: Int = 0
2525
lazy
2626
#line 12 "test.swift"
2727
var val3: Int = 0;
28-
#line
28+
#line // expected-error {{#line directive was renamed to #sourceLocation}}
2929

3030
class C { // expected-note {{to match this opening '{'}}
3131

0 commit comments

Comments
 (0)