Skip to content

Commit 39182b6

Browse files
committed
Also refer to the original source file when a diagnostic points into an expansion
When the primary diagnostic comes from inside some kind of generated buffer, such as a macro expansion buffer, the file/line/column will point into a source file that might be temporary. In such cases, also add a link to the outermost, original source file, e.g., main.swift:2:1: note: expanded code originates here to help users and tools alike to find that original source file.
1 parent 84ebafb commit 39182b6

File tree

3 files changed

+29
-5
lines changed

3 files changed

+29
-5
lines changed

Sources/SwiftDiagnostics/DiagnosticsFormatter.swift

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -332,12 +332,17 @@ public struct DiagnosticsFormatter {
332332

333333
return prefix + colorizeIfRequested(message.message, annotation: color);
334334
case .note:
335-
let color = ANSIAnnotation(color: .default, trait: .bold)
336-
let prefix = colorizeIfRequested("note: ", annotation: color)
337-
return prefix + message.message
335+
return colorizeNoteIfRequested(message.message)
338336
}
339337
}
340338

339+
/// Annotate a note with an appropriate ANSI color code (if requested).
340+
func colorizeNoteIfRequested(_ message: String) -> String {
341+
let color = ANSIAnnotation(color: .default, trait: .bold)
342+
let prefix = colorizeIfRequested("note: ", annotation: color)
343+
return prefix + message
344+
}
345+
341346
/// Apply the given color and trait to the specified text, when we are
342347
/// supposed to color the output.
343348
private func colorizeIfRequested(

Sources/SwiftDiagnostics/GroupedDiagnostics.swift

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,16 +189,34 @@ extension GroupedDiagnostics {
189189

190190
// If this is a nested source file, draw a box around it.
191191
let isRoot = sourceFile.parent == nil
192-
let prefixString: String
192+
var prefixString: String
193193
let suffixString: String
194194

195195
if isRoot {
196-
// If there's a primary diagnostic,
196+
// If there's a primary diagnostic, print it first.
197197
if let (primaryDiagSourceFile, primaryDiag) = findPrimaryDiagnostic(in: sourceFile) {
198198
let primaryDiagSLC = SourceLocationConverter(fileName: primaryDiagSourceFile.displayName, tree: primaryDiagSourceFile.tree)
199199
let location = primaryDiag.location(converter: primaryDiagSLC)
200200

201+
// Display file/line/column and diagnostic text for the primary diagnostic.
201202
prefixString = "\(location.file):\(location.line):\(location.column): \(formatter.colorizeIfRequested(primaryDiag.diagMessage))\n"
203+
204+
// If the primary diagnostic source file is not the same as the root source file, we're pointing into a generated buffer.
205+
// Provide a link back to the original source file where this generated buffer occurred, so it's easy to find if
206+
// (for example) the generated buffer is no longer available.
207+
if sourceFile.id != primaryDiagSourceFile.id,
208+
var (rootSourceID, rootPosition) = primaryDiagSourceFile.parent {
209+
// Go all the way up to the root to find the absolute position of the outermost generated buffer within the
210+
// root source file.
211+
while let parent = sourceFiles[rootSourceID.id].parent {
212+
(rootSourceID, rootPosition) = parent
213+
}
214+
215+
if rootSourceID == sourceFileID {
216+
let bufferLoc = slc.location(for: rootPosition)
217+
prefixString += "╰─ \(bufferLoc.file):\(bufferLoc.line):\(bufferLoc.column): \(formatter.colorizeNoteIfRequested("expanded code originates here"))\n"
218+
}
219+
}
202220
} else {
203221
let firstLine = sourceFile.diagnostics.first.map { $0.location(converter: slc).line } ?? 0
204222
prefixString = "\(sourceFile.displayName): \(firstLine):"

Tests/SwiftDiagnosticsTest/GroupDiagnosticsFormatterTests.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ final class GroupedDiagnosticsFormatterTests: XCTestCase {
181181
annotated,
182182
"""
183183
#invertedEqualityCheck:1:7: error: no matching operator '==' for types 'Double' and 'Int'
184+
╰─ main.swift:2:1: note: expanded code originates here
184185
1 │ let pi = 3.14159
185186
2 │ #myAssert(pi == 3)
186187
│ ╰─ note: in expansion of macro 'myAssert' here

0 commit comments

Comments
 (0)