Skip to content

Commit

Permalink
Refactor DiagnosticsFormatter and extend its functionality
Browse files Browse the repository at this point in the history
This commit refactors the existing code for `DiagnosticsFormatter` and introduces several new features, complete with documentation and unit tests.

Key Enhancements:

1. Nested Diagnostic Support: Enhanced to include not only top-level diagnostics but also related notes, improving the debugging experience.

2. Custom Decorators: Incorporate the `DiagnosticDecorator` protocol, allowing for custom formatting and styling of diagnostic output.

3. Context Size Control: Added options to control the `ContextSize`, providing more flexibility in how much source code context is displayed around each diagnostic.

Documentation:

- Comprehensive documentation added, detailing the purpose, usage examples, and future developments for `DiagnosticsFormatter`.

Testing:

- Added robust unit tests to validate the new features and ensure reliability.

This refactor and feature addition make `DiagnosticsFormatter` a more versatile and developer-friendly tool for debugging and understanding Swift code.
  • Loading branch information
Matejkob committed Feb 18, 2024
1 parent 2f5dd64 commit 6030474
Show file tree
Hide file tree
Showing 8 changed files with 1,170 additions and 270 deletions.
964 changes: 728 additions & 236 deletions Sources/SwiftDiagnostics/DiagnosticsFormatter.swift

Large diffs are not rendered by default.

29 changes: 20 additions & 9 deletions Sources/SwiftDiagnostics/GroupedDiagnostics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -254,21 +254,24 @@ extension GroupedDiagnostics {
// Render the buffer.
return prefixString
+ formatter.annotatedSource(
tree: sourceFile.tree,
diags: sourceFile.diagnostics,
indentString: diagnosticDecorator.decorateBufferOutline(indentString),
suffixTexts: childSources,
sourceLocationConverter: slc
) + suffixString
inSyntaxTree: sourceFile.tree,
withDiagnostics: sourceFile.diagnostics,
usingIndentString: diagnosticDecorator.decorateBufferOutline(indentString),
appendingSuffixTexts: childSources,
employingSourceLocationConverter: slc
)
+ suffixString
}
}

extension DiagnosticsFormatter {
/// Annotate all of the source files in the given set of grouped diagnostics.
public func annotateSources(in group: GroupedDiagnostics) -> String {
return group.rootSourceFiles.map { rootSourceFileID in
group.annotateSource(rootSourceFileID, formatter: self, indentString: "")
}.joined(separator: "\n")
group.rootSourceFiles
.map { rootSourceFileID in
group.annotateSource(rootSourceFileID, formatter: self, indentString: "")
}
.joined(separator: "\n")
}

public static func annotateSources(
Expand All @@ -279,4 +282,12 @@ extension DiagnosticsFormatter {
let formatter = DiagnosticsFormatter(contextSize: contextSize, colorize: colorize)
return formatter.annotateSources(in: group)
}
// public static func annotateSources(
// in group: GroupedDiagnostics,
// contextSize: ContextSize = .limited(2),
// diagnosticDecorator: DiagnosticDecorator = BasicDiagnosticDecorator()
// ) -> String {
// let formatter = DiagnosticsFormatter(contextSize: contextSize, diagnosticDecorator: diagnosticDecorator)
// return formatter.annotateSources(in: group)
// }
}
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ struct SyntaxStringInterpolationDiagnosticError: Error, CustomStringConvertible
var description: String {
// Start the diagnostic on a new line so it isn't prefixed with the file, which messes up the
// column-aligned message from ``DiagnosticsFormatter``.
// return "\n" + DiagnosticsFormatter.annotatedSource(inSyntaxTree: tree, withDiagnostics: diagnostics)
return "\n" + DiagnosticsFormatter.annotatedSource(tree: tree, diags: diagnostics)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ extension SyntaxParseable {
!suppressStringInterpolationParsingErrors
{
let diagnostics = ParseDiagnosticsGenerator.diagnostics(for: self)
let formattedDiagnostics = DiagnosticsFormatter().annotatedSource(tree: self, diags: diagnostics)
Logger(subsystem: "org.swift.swift-syntax", category: "ParseError").fault(
// let formattedDiagnostics = DiagnosticsFormatter.annotatedSource(inSyntaxTree: self, withDiagnostics: diagnostics)
let formattedDiagnostics = DiagnosticsFormatter.annotatedSource(tree: self, diags: diagnostics)
Logger(subsystem: "SwiftSyntax", category: "ParseError").fault(
"""
Parsing a `\(Self.self)` node from string interpolation produced the following parsing errors.
Set a breakpoint in `SyntaxParseable.logStringInterpolationParsingError()` to debug the failure.
Expand Down
54 changes: 52 additions & 2 deletions Tests/SwiftDiagnosticsTest/DiagnosticTestingUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ struct DiagnosticDescriptor {
/// - fixIts: An array of Fix-It descriptors for quick fixes. Default is an empty array.
init(
locationMarker: LocationMarker,
id: MessageID = MessageID(domain: "test", id: "conjured"),
id: MessageID = .test,
message: String,
severity: DiagnosticSeverity = .error,
highlight: [Syntax] = [],
Expand Down Expand Up @@ -158,6 +158,22 @@ struct NoteDescriptor {

/// The textual content of the note to be displayed.
let message: String

/// Initializes a new `NoteDescriptor`.
///
/// - Parameters:
/// - locationMarker: The marker pointing to location in source code.
/// - id: The ID associated with the note message.
/// - message: The textual content of the note to be displayed.
init(
locationMarker: LocationMarker,
id: MessageID = .test,
message: String
) {
self.locationMarker = locationMarker
self.id = id
self.message = message
}
}

/// A simple implementation of the `NoteMessage` protocol for testing.
Expand All @@ -168,6 +184,19 @@ struct SimpleNoteMessage: NoteMessage {

/// The unique identifier for this note message.
let noteID: MessageID

/// Initializes a new `SimpleNoteMessage`.
///
/// - Parameters:
/// - message: The textual content of the note to be displayed.
/// - noteID: The unique identifier for this note message.
init(
message: String,
noteID: MessageID = .test
) {
self.message = message
self.noteID = noteID
}
}

/// A simple implementation of the `DiagnosticMessage` protocol for testing.
Expand All @@ -181,6 +210,22 @@ struct SimpleDiagnosticMessage: DiagnosticMessage {

/// The severity level of the diagnostic message.
let severity: DiagnosticSeverity

/// Initializes a new `SimpleDiagnosticMessage`.
///
/// - Parameters:
/// - message: The textual content of the diagnostic message to be displayed.
/// - diagnosticID: The ID associated with the diagnostic message for categorization or referencing.
/// - severity: The severity level of the diagnostic message.
init(
message: String,
diagnosticID: MessageID = .test,
severity: DiagnosticSeverity
) {
self.message = message
self.diagnosticID = diagnosticID
self.severity = severity
}
}

/// Asserts that the annotated source generated from diagnostics matches an expected annotated source.
Expand Down Expand Up @@ -211,12 +256,17 @@ func assertAnnotated(
XCTFail(error.localizedDescription, file: file, line: line)
}

// let annotatedSource = DiagnosticsFormatter.annotatedSource(inSyntaxTree: tree, withDiagnostics: diagnostics)
let annotatedSource = DiagnosticsFormatter.annotatedSource(tree: tree, diags: diagnostics)

assertStringsEqualWithDiff(
annotatedSource,
expectedAnnotatedSource,
file: file,
line: line
)
}

extension MessageID {
fileprivate static var test = Self(domain: "test", id: "conjured")
}

0 comments on commit 6030474

Please sign in to comment.