Skip to content

Commit 5add2e0

Browse files
authored
Merge pull request #270 from allevato/refactor-diagnostics
Refactor "diagnostics" into "findings".
2 parents 848395f + 7aa5df5 commit 5add2e0

File tree

70 files changed

+984
-475
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+984
-475
lines changed

Package.swift

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ let package = Package(
3333
"SwiftFormatRules",
3434
"SwiftFormatWhitespaceLinter",
3535
"SwiftSyntax",
36+
"SwiftSyntaxParser",
3637
]
3738
),
3839
.target(name: "SwiftFormatConfiguration"),
@@ -66,6 +67,7 @@ let package = Package(
6667
"SwiftFormatCore",
6768
"SwiftFormatRules",
6869
"SwiftSyntax",
70+
"SwiftSyntaxParser",
6971
]
7072
),
7173
.target(
@@ -76,54 +78,61 @@ let package = Package(
7678
"SwiftFormatConfiguration",
7779
"SwiftFormatCore",
7880
"SwiftSyntax",
81+
"TSCBasic",
7982
]
8083
),
8184
.testTarget(
8285
name: "SwiftFormatTests",
8386
dependencies: [
8487
"SwiftFormat",
88+
"SwiftSyntax",
89+
"SwiftSyntaxParser",
8590
]
8691
),
8792
.testTarget(
8893
name: "SwiftFormatConfigurationTests",
8994
dependencies: ["SwiftFormatConfiguration"]
9095
),
9196
.testTarget(
92-
name: "SwiftFormatRulesTests",
97+
name: "SwiftFormatCoreTests",
9398
dependencies: [
9499
"SwiftFormatConfiguration",
95100
"SwiftFormatCore",
96-
"SwiftFormatPrettyPrint",
97-
"SwiftFormatRules",
98-
"SwiftFormatTestSupport",
99101
"SwiftSyntax",
102+
"SwiftSyntaxParser",
100103
]
101104
),
102105
.testTarget(
103-
name: "SwiftFormatCoreTests",
106+
name: "SwiftFormatPerformanceTests",
104107
dependencies: [
105-
"SwiftFormatConfiguration",
106-
"SwiftFormatCore",
108+
"SwiftFormatTestSupport",
109+
"SwiftFormatWhitespaceLinter",
107110
"SwiftSyntax",
111+
"SwiftSyntaxParser",
108112
]
109113
),
110114
.testTarget(
111-
name: "SwiftFormatPerformanceTests",
115+
name: "SwiftFormatPrettyPrintTests",
112116
dependencies: [
117+
"SwiftFormatConfiguration",
118+
"SwiftFormatCore",
119+
"SwiftFormatPrettyPrint",
120+
"SwiftFormatRules",
113121
"SwiftFormatTestSupport",
114-
"SwiftFormatWhitespaceLinter",
115122
"SwiftSyntax",
123+
"SwiftSyntaxParser",
116124
]
117125
),
118126
.testTarget(
119-
name: "SwiftFormatPrettyPrintTests",
127+
name: "SwiftFormatRulesTests",
120128
dependencies: [
121129
"SwiftFormatConfiguration",
122130
"SwiftFormatCore",
123131
"SwiftFormatPrettyPrint",
124132
"SwiftFormatRules",
125133
"SwiftFormatTestSupport",
126134
"SwiftSyntax",
135+
"SwiftSyntaxParser",
127136
]
128137
),
129138
.testTarget(
@@ -134,6 +143,7 @@ let package = Package(
134143
"SwiftFormatTestSupport",
135144
"SwiftFormatWhitespaceLinter",
136145
"SwiftSyntax",
146+
"SwiftSyntaxParser",
137147
]
138148
),
139149
]
@@ -143,12 +153,14 @@ let package = Package(
143153
if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil {
144154
// Building standalone.
145155
package.dependencies += [
146-
.package(url: "https://github.com/apple/swift-syntax", .branch("main")),
147156
.package(url: "https://github.com/apple/swift-argument-parser.git", .branch("main")),
157+
.package(url: "https://github.com/apple/swift-syntax", .branch("main")),
158+
.package(url: "https://github.com/apple/swift-tools-support-core.git", .branch("main")),
148159
]
149160
} else {
150161
package.dependencies += [
151-
.package(path: "../swift-syntax"),
152162
.package(path: "../swift-argument-parser"),
163+
.package(path: "../swift-syntax"),
164+
.package(path: "../swift-tools-support-core"),
153165
]
154166
}

Sources/SwiftFormat/Exports.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SwiftFormatCore
14+
15+
// The `SwiftFormatCore` module isn't meant for public use, but these types need to be since they
16+
// are also part of the public `SwiftFormat` API. Use public typealiases to "re-export" them for
17+
// now.
18+
19+
public typealias Finding = SwiftFormatCore.Finding
20+
public typealias FindingCategorizing = SwiftFormatCore.FindingCategorizing

Sources/SwiftFormat/SwiftFormatter.swift

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,16 @@ import SwiftFormatCore
1616
import SwiftFormatPrettyPrint
1717
import SwiftFormatRules
1818
import SwiftSyntax
19+
import SwiftSyntaxParser
1920

2021
/// Formats Swift source code or syntax trees according to the Swift style guidelines.
2122
public final class SwiftFormatter {
2223

2324
/// The configuration settings that control the formatter's behavior.
2425
public let configuration: Configuration
2526

26-
/// A diagnostic engine to which non-fatal errors will be reported.
27-
public let diagnosticEngine: DiagnosticEngine?
27+
/// An optional callback that will be notified with any findings encountered during formatting.
28+
public let findingConsumer: ((Finding) -> Void)?
2829

2930
/// Advanced options that are useful when debugging the formatter's behavior but are not meant for
3031
/// general use.
@@ -34,11 +35,12 @@ public final class SwiftFormatter {
3435
///
3536
/// - Parameters:
3637
/// - configuration: The configuration settings that control the formatter's behavior.
37-
/// - diagnosticEngine: The diagnostic engine to which non-fatal errors will be reported.
38-
/// Defaults to nil.
39-
public init(configuration: Configuration, diagnosticEngine: DiagnosticEngine? = nil) {
38+
/// - findingConsumer: An optional callback that will be notified with any findings encountered
39+
/// during formatting. Unlike the `Linter` API, this defaults to nil for formatting because
40+
/// findings are typically less useful than the final formatted output.
41+
public init(configuration: Configuration, findingConsumer: ((Finding) -> Void)? = nil) {
4042
self.configuration = configuration
41-
self.diagnosticEngine = diagnosticEngine
43+
self.findingConsumer = findingConsumer
4244
}
4345

4446
/// Formats the Swift code at the given file URL and writes the result to an output stream.
@@ -47,9 +49,13 @@ public final class SwiftFormatter {
4749
/// - url: The URL of the file containing the code to format.
4850
/// - outputStream: A value conforming to `TextOutputStream` to which the formatted output will
4951
/// be written.
52+
/// - parsingDiagnosticHandler: An optional callback that will be notified if there are any
53+
/// errors when parsing the source code.
5054
/// - Throws: If an unrecoverable error occurs when formatting the code.
5155
public func format<Output: TextOutputStream>(
52-
contentsOf url: URL, to outputStream: inout Output
56+
contentsOf url: URL,
57+
to outputStream: inout Output,
58+
parsingDiagnosticHandler: ((Diagnostic) -> Void)? = nil
5359
) throws {
5460
guard FileManager.default.isReadableFile(atPath: url.path) else {
5561
throw SwiftFormatError.fileNotReadable
@@ -58,7 +64,7 @@ public final class SwiftFormatter {
5864
if FileManager.default.fileExists(atPath: url.path, isDirectory: &isDir), isDir.boolValue {
5965
throw SwiftFormatError.isDirectory
6066
}
61-
let sourceFile = try SyntaxParser.parse(url)
67+
let sourceFile = try SyntaxParser.parse(url, diagnosticHandler: parsingDiagnosticHandler)
6268
let source = try String(contentsOf: url, encoding: .utf8)
6369
try format(syntax: sourceFile, assumingFileURL: url, source: source, to: &outputStream)
6470
}
@@ -72,11 +78,17 @@ public final class SwiftFormatter {
7278
/// dummy value will be used.
7379
/// - outputStream: A value conforming to `TextOutputStream` to which the formatted output will
7480
/// be written.
81+
/// - parsingDiagnosticHandler: An optional callback that will be notified if there are any
82+
/// errors when parsing the source code.
7583
/// - Throws: If an unrecoverable error occurs when formatting the code.
7684
public func format<Output: TextOutputStream>(
77-
source: String, assumingFileURL url: URL?, to outputStream: inout Output
85+
source: String,
86+
assumingFileURL url: URL?,
87+
to outputStream: inout Output,
88+
parsingDiagnosticHandler: ((Diagnostic) -> Void)? = nil
7889
) throws {
79-
let sourceFile = try SyntaxParser.parse(source: source)
90+
let sourceFile =
91+
try SyntaxParser.parse(source: source, diagnosticHandler: parsingDiagnosticHandler)
8092
try format(syntax: sourceFile, assumingFileURL: url, source: source, to: &outputStream)
8193
}
8294

@@ -108,7 +120,7 @@ public final class SwiftFormatter {
108120

109121
let assumedURL = url ?? URL(fileURLWithPath: "source")
110122
let context = Context(
111-
configuration: configuration, diagnosticEngine: diagnosticEngine, fileURL: assumedURL,
123+
configuration: configuration, findingConsumer: findingConsumer, fileURL: assumedURL,
112124
sourceFileSyntax: syntax, source: source, ruleNameCache: ruleNameCache)
113125
let pipeline = FormatPipeline(context: context)
114126
let transformedSyntax = pipeline.visit(Syntax(syntax))

Sources/SwiftFormat/SwiftLinter.swift

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import SwiftFormatPrettyPrint
1717
import SwiftFormatRules
1818
import SwiftFormatWhitespaceLinter
1919
import SwiftSyntax
20+
import SwiftSyntaxParser
2021

2122
/// Diagnoses and reports problems in Swift source code or syntax trees according to the Swift style
2223
/// guidelines.
@@ -25,8 +26,8 @@ public final class SwiftLinter {
2526
/// The configuration settings that control the linter's behavior.
2627
public let configuration: Configuration
2728

28-
/// A diagnostic engine to which lint findings will be reported.
29-
public let diagnosticEngine: DiagnosticEngine
29+
/// A callback that will be notified with any findings encountered during linting.
30+
public let findingConsumer: (Finding) -> Void
3031

3132
/// Advanced options that are useful when debugging the linter's behavior but are not meant for
3233
/// general use.
@@ -36,25 +37,32 @@ public final class SwiftLinter {
3637
///
3738
/// - Parameters:
3839
/// - configuration: The configuration settings that control the linter's behavior.
39-
/// - diagnosticEngine: The diagnostic engine to which lint findings will be reported.
40-
public init(configuration: Configuration, diagnosticEngine: DiagnosticEngine) {
40+
/// - findingConsumer: A callback that will be notified with any findings encountered during
41+
/// linting.
42+
public init(configuration: Configuration, findingConsumer: @escaping (Finding) -> Void) {
4143
self.configuration = configuration
42-
self.diagnosticEngine = diagnosticEngine
44+
self.findingConsumer = findingConsumer
4345
}
4446

4547
/// Lints the Swift code at the given file URL.
4648
///
47-
/// - Parameters url: The URL of the file containing the code to format.
49+
/// - Parameters:
50+
/// - url: The URL of the file containing the code to format.
51+
/// - parsingDiagnosticHandler: An optional callback that will be notified if there are any
52+
/// errors when parsing the source code.
4853
/// - Throws: If an unrecoverable error occurs when formatting the code.
49-
public func lint(contentsOf url: URL) throws {
54+
public func lint(
55+
contentsOf url: URL,
56+
parsingDiagnosticHandler: ((Diagnostic) -> Void)? = nil
57+
) throws {
5058
guard FileManager.default.isReadableFile(atPath: url.path) else {
5159
throw SwiftFormatError.fileNotReadable
5260
}
5361
var isDir: ObjCBool = false
5462
if FileManager.default.fileExists(atPath: url.path, isDirectory: &isDir), isDir.boolValue {
5563
throw SwiftFormatError.isDirectory
5664
}
57-
let sourceFile = try SyntaxParser.parse(url)
65+
let sourceFile = try SyntaxParser.parse(url, diagnosticHandler: parsingDiagnosticHandler)
5866
let source = try String(contentsOf: url, encoding: .utf8)
5967
try lint(syntax: sourceFile, assumingFileURL: url, source: source)
6068
}
@@ -64,9 +72,16 @@ public final class SwiftLinter {
6472
/// - Parameters:
6573
/// - source: The Swift source code to be linted.
6674
/// - url: A file URL denoting the filename/path that should be assumed for this source code.
75+
/// - parsingDiagnosticHandler: An optional callback that will be notified if there are any
76+
/// errors when parsing the source code.
6777
/// - Throws: If an unrecoverable error occurs when formatting the code.
68-
public func lint(source: String, assumingFileURL url: URL) throws {
69-
let sourceFile = try SyntaxParser.parse(source: source)
78+
public func lint(
79+
source: String,
80+
assumingFileURL url: URL,
81+
parsingDiagnosticHandler: ((Diagnostic) -> Void)? = nil
82+
) throws {
83+
let sourceFile =
84+
try SyntaxParser.parse(source: source, diagnosticHandler: parsingDiagnosticHandler)
7085
try lint(syntax: sourceFile, assumingFileURL: url, source: source)
7186
}
7287

@@ -88,7 +103,7 @@ public final class SwiftLinter {
88103
}
89104

90105
let context = Context(
91-
configuration: configuration, diagnosticEngine: diagnosticEngine, fileURL: url,
106+
configuration: configuration, findingConsumer: findingConsumer, fileURL: url,
92107
sourceFileSyntax: syntax, source: source, ruleNameCache: ruleNameCache)
93108
let pipeline = LintPipeline(context: context)
94109
pipeline.walk(Syntax(syntax))

Sources/SwiftFormatCore/Context.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ import SwiftSyntax
1616

1717
/// Context contains the bits that each formatter and linter will need access to.
1818
///
19-
/// Specifically, it is the container for the shared configuration, diagnostic engine, and URL of
19+
/// Specifically, it is the container for the shared configuration, diagnostic consumer, and URL of
2020
/// the current file.
21-
public class Context {
21+
public final class Context {
2222

2323
/// Tracks whether `XCTest` has been imported so that certain logic can be modified for files that
2424
/// are known to be tests.
@@ -37,8 +37,8 @@ public class Context {
3737
/// The configuration for this run of the pipeline, provided by a configuration JSON file.
3838
public let configuration: Configuration
3939

40-
/// The engine in which to emit diagnostics, if running in Lint mode.
41-
public let diagnosticEngine: DiagnosticEngine?
40+
/// Emits findings to the finding consumer.
41+
public let findingEmitter: FindingEmitter
4242

4343
/// The URL of the file being linted or formatted.
4444
public let fileURL: URL
@@ -58,14 +58,14 @@ public class Context {
5858
/// Creates a new Context with the provided configuration, diagnostic engine, and file URL.
5959
public init(
6060
configuration: Configuration,
61-
diagnosticEngine: DiagnosticEngine?,
61+
findingConsumer: ((Finding) -> Void)?,
6262
fileURL: URL,
6363
sourceFileSyntax: SourceFileSyntax,
6464
source: String? = nil,
6565
ruleNameCache: [ObjectIdentifier: String]
6666
) {
6767
self.configuration = configuration
68-
self.diagnosticEngine = diagnosticEngine
68+
self.findingEmitter = FindingEmitter(consumer: findingConsumer)
6969
self.fileURL = fileURL
7070
self.importsXCTest = .notDetermined
7171
self.sourceLocationConverter =

Sources/SwiftFormatCore/Diagnostic+Rule.swift

Lines changed: 0 additions & 22 deletions
This file was deleted.

0 commit comments

Comments
 (0)