Skip to content

Commit bd619ff

Browse files
authored
Merge pull request #509 from allevato/remove-tsc
Remove the `swift-tools-support-core` dependency from swift-format.
2 parents b5b62af + f3e5cc5 commit bd619ff

8 files changed

+254
-173
lines changed

Package.swift

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,6 @@ let package = Package(
143143
.product(name: "ArgumentParser", package: "swift-argument-parser"),
144144
.product(name: "SwiftSyntax", package: "swift-syntax"),
145145
.product(name: "SwiftParser", package: "swift-syntax"),
146-
.product(name: "TSCBasic", package: "swift-tools-support-core"),
147146
]
148147
),
149148

@@ -223,15 +222,10 @@ if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil {
223222
url: "https://github.com/apple/swift-syntax.git",
224223
branch: "main"
225224
),
226-
.package(
227-
url: "https://github.com/apple/swift-tools-support-core.git",
228-
exact: Version("0.4.0")
229-
),
230225
]
231226
} else {
232227
package.dependencies += [
233228
.package(path: "../swift-argument-parser"),
234229
.package(path: "../swift-syntax"),
235-
.package(path: "../swift-tools-support-core"),
236230
]
237231
}

Sources/swift-format/Frontend/FormatFrontend.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ class FormatFrontend: Frontend {
4040
return
4141
}
4242

43-
let diagnosticHandler: (Diagnostic, SourceLocation) -> () = { (diagnostic, location) in
43+
let diagnosticHandler: (SwiftDiagnostics.Diagnostic, SourceLocation) -> () = {
44+
(diagnostic, location) in
4445
guard !self.lintFormatOptions.ignoreUnparsableFiles else {
4546
// No diagnostics should be emitted in this mode.
4647
return

Sources/swift-format/Frontend/Frontend.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class Frontend {
5757
final let diagnosticPrinter: StderrDiagnosticPrinter
5858

5959
/// The diagnostic engine to which warnings and errors will be emitted.
60-
final let diagnosticsEngine: UnifiedDiagnosticsEngine
60+
final let diagnosticsEngine: DiagnosticsEngine
6161

6262
/// Options that apply during formatting or linting.
6363
final let lintFormatOptions: LintFormatOptions
@@ -83,7 +83,7 @@ class Frontend {
8383
self.diagnosticPrinter = StderrDiagnosticPrinter(
8484
colorMode: lintFormatOptions.colorDiagnostics.map { $0 ? .on : .off } ?? .auto)
8585
self.diagnosticsEngine =
86-
UnifiedDiagnosticsEngine(diagnosticsHandlers: [diagnosticPrinter.printDiagnostic])
86+
DiagnosticsEngine(diagnosticsHandlers: [diagnosticPrinter.printDiagnostic])
8787
}
8888

8989
/// Runs the linter or formatter over the inputs.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 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+
import SwiftSyntax
15+
16+
/// Diagnostic data that retains the separation of a finding category (if present) from the rest of
17+
/// the message, allowing diagnostic printers that want to print those values separately to do so.
18+
struct Diagnostic {
19+
/// The severity of the diagnostic.
20+
enum Severity {
21+
case note
22+
case warning
23+
case error
24+
}
25+
26+
/// Represents the location of a diagnostic.
27+
struct Location {
28+
/// The file path associated with the diagnostic.
29+
var file: String
30+
31+
/// The 1-based line number where the diagnostic occurred.
32+
var line: Int
33+
34+
/// The 1-based column number where the diagnostic occurred.
35+
var column: Int
36+
37+
/// Creates a new diagnostic location from the given source location.
38+
init(_ sourceLocation: SourceLocation) {
39+
self.file = sourceLocation.file!
40+
self.line = sourceLocation.line!
41+
self.column = sourceLocation.column!
42+
}
43+
44+
/// Creates a new diagnostic location with the given finding location.
45+
init(_ findingLocation: Finding.Location) {
46+
self.file = findingLocation.file
47+
self.line = findingLocation.line
48+
self.column = findingLocation.column
49+
}
50+
}
51+
52+
/// The severity of the diagnostic.
53+
var severity: Severity
54+
55+
/// The location where the diagnostic occurred, if known.
56+
var location: Location?
57+
58+
/// The category of the diagnostic, if any.
59+
var category: String?
60+
61+
/// The message text associated with the diagnostic.
62+
var message: String
63+
64+
var description: String {
65+
if let category = category {
66+
return "[\(category)] \(message)"
67+
} else {
68+
return message
69+
}
70+
}
71+
72+
/// Creates a new diagnostic with the given severity, location, optional category, and
73+
/// message.
74+
init(severity: Severity, location: Location?, category: String? = nil, message: String) {
75+
self.severity = severity
76+
self.location = location
77+
self.category = category
78+
self.message = message
79+
}
80+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 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+
import SwiftSyntax
15+
import SwiftDiagnostics
16+
17+
/// Unifies the handling of findings from the linter, parsing errors from the syntax parser, and
18+
/// generic errors from the frontend so that they are emitted in a uniform fashion.
19+
final class DiagnosticsEngine {
20+
/// The handler functions that will be called to process diagnostics that are emitted.
21+
private let handlers: [(Diagnostic) -> Void]
22+
23+
/// A Boolean value indicating whether any errors were emitted by the diagnostics engine.
24+
private(set) var hasErrors: Bool
25+
26+
/// A Boolean value indicating whether any warnings were emitted by the diagnostics engine.
27+
private(set) var hasWarnings: Bool
28+
29+
/// Creates a new diagnostics engine with the given diagnostic handlers.
30+
///
31+
/// - Parameter diagnosticsHandlers: An array of functions, each of which takes a `Diagnostic` as
32+
/// its sole argument and returns `Void`. The functions are called whenever a diagnostic is
33+
/// received by the engine.
34+
init(diagnosticsHandlers: [(Diagnostic) -> Void]) {
35+
self.handlers = diagnosticsHandlers
36+
self.hasErrors = false
37+
self.hasWarnings = false
38+
}
39+
40+
/// Emits the diagnostic by passing it to the registered handlers, and tracks whether it was an
41+
/// error or warning diagnostic.
42+
private func emit(_ diagnostic: Diagnostic) {
43+
switch diagnostic.severity {
44+
case .error: self.hasErrors = true
45+
case .warning: self.hasWarnings = true
46+
default: break
47+
}
48+
49+
for handler in handlers {
50+
handler(diagnostic)
51+
}
52+
}
53+
54+
/// Emits a generic error message.
55+
///
56+
/// - Parameters:
57+
/// - message: The message associated with the error.
58+
/// - location: The location in the source code associated with the error, or nil if there is no
59+
/// location associated with the error.
60+
func emitError(_ message: String, location: SourceLocation? = nil) {
61+
emit(
62+
Diagnostic(
63+
severity: .error,
64+
location: location.map(Diagnostic.Location.init),
65+
message: message))
66+
}
67+
68+
/// Emits a finding from the linter and any of its associated notes as diagnostics.
69+
///
70+
/// - Parameter finding: The finding that should be emitted.
71+
func consumeFinding(_ finding: Finding) {
72+
emit(diagnosticMessage(for: finding))
73+
74+
for note in finding.notes {
75+
emit(
76+
Diagnostic(
77+
severity: .note,
78+
location: note.location.map(Diagnostic.Location.init),
79+
message: "\(note.message)"))
80+
}
81+
}
82+
83+
/// Emits a diagnostic from the syntax parser and any of its associated notes.
84+
///
85+
/// - Parameter diagnostic: The syntax parser diagnostic that should be emitted.
86+
func consumeParserDiagnostic(
87+
_ diagnostic: SwiftDiagnostics.Diagnostic,
88+
_ location: SourceLocation
89+
) {
90+
emit(diagnosticMessage(for: diagnostic.diagMessage, at: location))
91+
}
92+
93+
/// Converts a diagnostic message from the syntax parser into a diagnostic message that can be
94+
/// used by the `TSCBasic` diagnostics engine and returns it.
95+
private func diagnosticMessage(
96+
for message: SwiftDiagnostics.DiagnosticMessage,
97+
at location: SourceLocation
98+
) -> Diagnostic {
99+
let severity: Diagnostic.Severity
100+
switch message.severity {
101+
case .error: severity = .error
102+
case .warning: severity = .warning
103+
case .note: severity = .note
104+
}
105+
return Diagnostic(
106+
severity: severity,
107+
location: Diagnostic.Location(location),
108+
category: nil,
109+
message: message.message)
110+
}
111+
112+
/// Converts a lint finding into a diagnostic message that can be used by the `TSCBasic`
113+
/// diagnostics engine and returns it.
114+
private func diagnosticMessage(for finding: Finding) -> Diagnostic {
115+
let severity: Diagnostic.Severity
116+
switch finding.severity {
117+
case .error: severity = .error
118+
case .warning: severity = .warning
119+
}
120+
return Diagnostic(
121+
severity: severity,
122+
location: finding.location.map(Diagnostic.Location.init),
123+
category: "\(finding.category)",
124+
message: "\(finding.message.text)")
125+
}
126+
}

Sources/swift-format/Utilities/StderrDiagnosticPrinter.swift

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

1313
import Dispatch
1414
import Foundation
15-
import TSCBasic
1615

1716
/// Manages printing of diagnostics to standard error.
1817
final class StderrDiagnosticPrinter {
@@ -49,11 +48,7 @@ final class StderrDiagnosticPrinter {
4948
init(colorMode: ColorMode) {
5049
switch colorMode {
5150
case .auto:
52-
if let stream = stderrStream.stream as? LocalFileOutputByteStream {
53-
useColors = TerminalController.isTTY(stream)
54-
} else {
55-
useColors = false
56-
}
51+
useColors = isTTY(FileHandle.standardError)
5752
case .off:
5853
useColors = false
5954
case .on:
@@ -62,25 +57,32 @@ final class StderrDiagnosticPrinter {
6257
}
6358

6459
/// Prints a diagnostic to standard error.
65-
func printDiagnostic(_ diagnostic: TSCBasic.Diagnostic) {
60+
func printDiagnostic(_ diagnostic: Diagnostic) {
6661
printQueue.sync {
6762
let stderr = FileHandleTextOutputStream(FileHandle.standardError)
6863

69-
stderr.write("\(ansiSGR(.boldWhite))\(diagnostic.location): ")
64+
stderr.write("\(ansiSGR(.boldWhite))\(description(of: diagnostic.location)): ")
7065

71-
switch diagnostic.behavior {
66+
switch diagnostic.severity {
7267
case .error: stderr.write("\(ansiSGR(.boldRed))error: ")
7368
case .warning: stderr.write("\(ansiSGR(.boldMagenta))warning: ")
7469
case .note: stderr.write("\(ansiSGR(.boldGray))note: ")
75-
case .remark, .ignored: break
7670
}
7771

78-
let data = diagnostic.data as! UnifiedDiagnosticData
79-
if let category = data.category {
72+
if let category = diagnostic.category {
8073
stderr.write("\(ansiSGR(.boldYellow))[\(category)] ")
8174
}
82-
stderr.write("\(ansiSGR(.boldWhite))\(data.message)\(ansiSGR(.reset))\n")
75+
stderr.write("\(ansiSGR(.boldWhite))\(diagnostic.message)\(ansiSGR(.reset))\n")
76+
}
77+
}
78+
79+
/// Returns a string representation of the given diagnostic location, or a fallback string if the
80+
/// location was not known.
81+
private func description(of location: Diagnostic.Location?) -> String {
82+
if let location = location {
83+
return "\(location.file):\(location.line):\(location.column)"
8384
}
85+
return "<unknown>"
8486
}
8587

8688
/// Returns the complete ANSI sequence used to enable the given SGR if colors are enabled in the
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 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 Foundation
14+
15+
/// Returns a value indicating whether or not the stream is a TTY.
16+
func isTTY(_ fileHandle: FileHandle) -> Bool {
17+
// The implementation of this function is adapted from `TerminalController.swift` in
18+
// swift-tools-support-core.
19+
#if os(Windows)
20+
// The TSC implementation of this function only returns `.file` or `.dumb` for Windows,
21+
// neither of which is a TTY.
22+
return false
23+
#else
24+
if ProcessInfo.processInfo.environment["TERM"] == "dumb" {
25+
return false
26+
}
27+
return isatty(fileHandle.fileDescriptor) != 0
28+
#endif
29+
}

0 commit comments

Comments
 (0)