Skip to content

Commit ac8064f

Browse files
committed
Migrate Diagnostics to Explicitly-Scheduled Pass
The new Swift Parser does not emit diagnostics on its own. Schedule the diagnostic pass in all of the places SwiftFormat provides a diagnostic hook.
1 parent a409bd6 commit ac8064f

File tree

3 files changed

+52
-19
lines changed

3 files changed

+52
-19
lines changed

Sources/SwiftFormat/SwiftFormatter.swift

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import SwiftFormatPrettyPrint
1717
import SwiftFormatRules
1818
import SwiftSyntax
1919
import SwiftParser
20+
import SwiftDiagnostics
2021

2122
/// Formats Swift source code or syntax trees according to the Swift style guidelines.
2223
public final class SwiftFormatter {
@@ -55,7 +56,7 @@ public final class SwiftFormatter {
5556
public func format<Output: TextOutputStream>(
5657
contentsOf url: URL,
5758
to outputStream: inout Output,
58-
parsingDiagnosticHandler: ((Diagnostic) -> Void)? = nil
59+
parsingDiagnosticHandler: ((Diagnostic, SourceLocation) -> Void)? = nil
5960
) throws {
6061
guard FileManager.default.isReadableFile(atPath: url.path) else {
6162
throw SwiftFormatError.fileNotReadable
@@ -66,6 +67,14 @@ public final class SwiftFormatter {
6667
}
6768
let source = try String(contentsOf: url, encoding: .utf8)
6869
let sourceFile = try Parser.parse(source: source)
70+
if let parsingDiagnosticHandler = parsingDiagnosticHandler {
71+
let expectedConverter = SourceLocationConverter(file: url.path, tree: sourceFile)
72+
let diagnostics = ParseDiagnosticsGenerator.diagnostics(for: sourceFile)
73+
for diagnostic in diagnostics {
74+
let location = diagnostic.location(converter: expectedConverter)
75+
parsingDiagnosticHandler(diagnostic, location)
76+
}
77+
}
6978
try format(syntax: sourceFile, assumingFileURL: url, source: source, to: &outputStream)
7079
}
7180

@@ -85,9 +94,17 @@ public final class SwiftFormatter {
8594
source: String,
8695
assumingFileURL url: URL?,
8796
to outputStream: inout Output,
88-
parsingDiagnosticHandler: ((Diagnostic) -> Void)? = nil
97+
parsingDiagnosticHandler: ((Diagnostic, SourceLocation) -> Void)? = nil
8998
) throws {
9099
let sourceFile = try Parser.parse(source: source)
100+
if let parsingDiagnosticHandler = parsingDiagnosticHandler {
101+
let expectedConverter = SourceLocationConverter(file: url?.path ?? "<unknown>", tree: sourceFile)
102+
let diagnostics = ParseDiagnosticsGenerator.diagnostics(for: sourceFile)
103+
for diagnostic in diagnostics {
104+
let location = diagnostic.location(converter: expectedConverter)
105+
parsingDiagnosticHandler(diagnostic, location)
106+
}
107+
}
91108
try format(syntax: sourceFile, assumingFileURL: url, source: source, to: &outputStream)
92109
}
93110

Sources/SwiftFormat/SwiftLinter.swift

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import SwiftFormatRules
1818
import SwiftFormatWhitespaceLinter
1919
import SwiftSyntax
2020
import SwiftParser
21+
import SwiftDiagnostics
2122

2223
/// Diagnoses and reports problems in Swift source code or syntax trees according to the Swift style
2324
/// guidelines.
@@ -53,7 +54,7 @@ public final class SwiftLinter {
5354
/// - Throws: If an unrecoverable error occurs when formatting the code.
5455
public func lint(
5556
contentsOf url: URL,
56-
parsingDiagnosticHandler: ((Diagnostic) -> Void)? = nil
57+
parsingDiagnosticHandler: ((Diagnostic, SourceLocation) -> Void)? = nil
5758
) throws {
5859
guard FileManager.default.isReadableFile(atPath: url.path) else {
5960
throw SwiftFormatError.fileNotReadable
@@ -64,7 +65,8 @@ public final class SwiftLinter {
6465
}
6566
let source = try String(contentsOf: url, encoding: .utf8)
6667
let sourceFile = try Parser.parse(source: source)
67-
try lint(syntax: sourceFile, assumingFileURL: url, source: source)
68+
try lint(syntax: sourceFile, assumingFileURL: url,
69+
source: source, parsingDiagnosticHandler: parsingDiagnosticHandler)
6870
}
6971

7072
/// Lints the given Swift source code.
@@ -78,10 +80,11 @@ public final class SwiftLinter {
7880
public func lint(
7981
source: String,
8082
assumingFileURL url: URL,
81-
parsingDiagnosticHandler: ((Diagnostic) -> Void)? = nil
83+
parsingDiagnosticHandler: ((Diagnostic, SourceLocation) -> Void)? = nil
8284
) throws {
8385
let sourceFile = try Parser.parse(source: source)
84-
try lint(syntax: sourceFile, assumingFileURL: url, source: source)
86+
try lint(syntax: sourceFile, assumingFileURL: url,
87+
source: source, parsingDiagnosticHandler: parsingDiagnosticHandler)
8588
}
8689

8790
/// Lints the given Swift syntax tree.
@@ -93,14 +96,29 @@ public final class SwiftLinter {
9396
/// - url: A file URL denoting the filename/path that should be assumed for this syntax tree.
9497
/// - Throws: If an unrecoverable error occurs when formatting the code.
9598
public func lint(syntax: SourceFileSyntax, assumingFileURL url: URL) throws {
96-
try lint(syntax: syntax, assumingFileURL: url, source: nil)
99+
try lint(syntax: syntax, assumingFileURL: url,
100+
source: nil, parsingDiagnosticHandler: nil)
97101
}
98102

99-
private func lint(syntax: SourceFileSyntax, assumingFileURL url: URL, source: String?) throws {
103+
private func lint(
104+
syntax: SourceFileSyntax,
105+
assumingFileURL url: URL,
106+
source: String?,
107+
parsingDiagnosticHandler: ((Diagnostic, SourceLocation) -> Void)?
108+
) throws {
100109
if let position = _firstInvalidSyntaxPosition(in: Syntax(syntax)) {
101110
throw SwiftFormatError.fileContainsInvalidSyntax(position: position)
102111
}
103112

113+
if let parsingDiagnosticHandler = parsingDiagnosticHandler {
114+
let expectedConverter = SourceLocationConverter(file: url.path, tree: syntax)
115+
let diagnostics = ParseDiagnosticsGenerator.diagnostics(for: syntax)
116+
for diagnostic in diagnostics {
117+
let location = diagnostic.location(converter: expectedConverter)
118+
parsingDiagnosticHandler(diagnostic, location)
119+
}
120+
}
121+
104122
let context = Context(
105123
configuration: configuration, findingConsumer: findingConsumer, fileURL: url,
106124
sourceFileSyntax: syntax, source: source, ruleNameCache: ruleNameCache)

Sources/swift-format/Utilities/UnifiedDiagnosticsEngine.swift

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import SwiftFormatCore
1414
import SwiftSyntax
15+
import SwiftDiagnostics
1516
import TSCBasic
1617

1718
/// Diagnostic data that retains the separation of a finding category (if present) from the rest of
@@ -113,24 +114,21 @@ final class UnifiedDiagnosticsEngine {
113114
/// Emits a diagnostic from the syntax parser and any of its associated notes.
114115
///
115116
/// - Parameter diagnostic: The syntax parser diagnostic that should be emitted.
116-
func consumeParserDiagnostic(_ diagnostic: SwiftSyntaxParser.Diagnostic) {
117+
func consumeParserDiagnostic(
118+
_ diagnostic: SwiftDiagnostics.Diagnostic,
119+
_ location: SourceLocation
120+
) {
117121
diagnosticsEngine.emit(
118-
diagnosticMessage(for: diagnostic.message),
119-
location: diagnostic.location.map(UnifiedLocation.parserLocation))
120-
121-
for note in diagnostic.notes {
122-
diagnosticsEngine.emit(
123-
.note(UnifiedDiagnosticData(message: note.message.text)),
124-
location: note.location.map(UnifiedLocation.parserLocation))
125-
}
122+
diagnosticMessage(for: diagnostic.diagMessage),
123+
location: UnifiedLocation.parserLocation(location))
126124
}
127125

128126
/// Converts a diagnostic message from the syntax parser into a diagnostic message that can be
129127
/// used by the `TSCBasic` diagnostics engine and returns it.
130-
private func diagnosticMessage(for message: SwiftSyntaxParser.Diagnostic.Message)
128+
private func diagnosticMessage(for message: SwiftDiagnostics.DiagnosticMessage)
131129
-> TSCBasic.Diagnostic.Message
132130
{
133-
let data = UnifiedDiagnosticData(category: nil, message: message.text)
131+
let data = UnifiedDiagnosticData(category: nil, message: message.message)
134132

135133
switch message.severity {
136134
case .error: return .error(data)

0 commit comments

Comments
 (0)