Skip to content

Commit 27bfeca

Browse files
committed
Merge pull request swiftlang#612 from natikgadzhi/feature/configuration/unknown-rules
Output a warning for unknown configuration rules in `.swift-format`
1 parent 8e17245 commit 27bfeca

File tree

4 files changed

+38
-7
lines changed

4 files changed

+38
-7
lines changed

Sources/SwiftFormat/Core/RuleRegistry+Generated.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212

1313
// This file is automatically generated with generate-pipeline. Do Not Edit!
1414

15-
enum RuleRegistry {
16-
static let rules: [String: Bool] = [
15+
@_spi(Internal) public enum RuleRegistry {
16+
public static let rules: [String: Bool] = [
1717
"AllPublicDeclarationsHaveDocumentation": false,
1818
"AlwaysUseLowerCamelCase": true,
1919
"AmbiguousTrailingClosureOverload": true,

Sources/generate-pipeline/RuleRegistryGenerator.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ final class RuleRegistryGenerator: FileGenerator {
4040
4141
// This file is automatically generated with generate-pipeline. Do Not Edit!
4242
43-
enum RuleRegistry {
44-
static let rules: [String: Bool] = [
43+
@_spi(Internal) public enum RuleRegistry {
44+
public static let rules: [String: Bool] = [
4545
4646
"""
4747
)

Sources/swift-format/Frontend/Frontend.swift

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
import Foundation
14-
import SwiftFormat
14+
@_spi(Internal) import SwiftFormat
1515
import SwiftSyntax
1616
import SwiftParser
1717

@@ -161,17 +161,19 @@ class Frontend {
161161
}
162162

163163
/// Returns the configuration that applies to the given `.swift` source file, when an explicit
164-
/// configuration path is also perhaps provided.
164+
/// configuration path is also perhaps provided. Checks for unrecognized rules within the configuration.
165165
///
166166
/// - Parameters:
167167
/// - configurationFilePath: The path to a configuration file that will be loaded, or `nil` to
168168
/// try to infer it from `swiftFilePath`.
169169
/// - swiftFilePath: The path to a `.swift` file, which will be used to infer the path to the
170170
/// configuration file if `configurationFilePath` is nil.
171+
///
171172
/// - Returns: If successful, the returned configuration is the one loaded from
172173
/// `configurationFilePath` if it was provided, or by searching in paths inferred by
173174
/// `swiftFilePath` if one exists, or the default configuration otherwise. If an error occurred
174175
/// when reading the configuration, a diagnostic is emitted and `nil` is returned.
176+
/// if neither `configurationFilePath` nor `swiftFilePath` were provided, a default `Configuration()` will be returned.
175177
private func configuration(
176178
at configurationFileURL: URL?,
177179
orInferredFromSwiftFileAt swiftFileURL: URL?
@@ -180,7 +182,9 @@ class Frontend {
180182
// loaded. (Do not try to fall back to a path inferred from the source file path.)
181183
if let configurationFileURL = configurationFileURL {
182184
do {
183-
return try configurationLoader.configuration(at: configurationFileURL)
185+
let configuration = try configurationLoader.configuration(at: configurationFileURL)
186+
self.checkForUnrecognizedRules(in: configuration)
187+
return configuration
184188
} catch {
185189
diagnosticsEngine.emitError("Unable to read configuration: \(error.localizedDescription)")
186190
return nil
@@ -192,6 +196,7 @@ class Frontend {
192196
if let swiftFileURL = swiftFileURL {
193197
do {
194198
if let configuration = try configurationLoader.configuration(forSwiftFileAt: swiftFileURL) {
199+
self.checkForUnrecognizedRules(in: configuration)
195200
return configuration
196201
}
197202
// Fall through to the default return at the end of the function.
@@ -207,4 +212,16 @@ class Frontend {
207212
// default configuration.
208213
return Configuration()
209214
}
215+
216+
/// Checks if all the rules in the given configuration are supported by the registry.
217+
/// If there are any rules that are not supported, they are emitted as a warning.
218+
private func checkForUnrecognizedRules(in configuration: Configuration) {
219+
// If any rules in the decoded configuration are not supported by the registry,
220+
// emit them into the diagnosticsEngine as warnings.
221+
// That way they will be printed out, but we'll continue execution on the valid rules.
222+
let invalidRules = configuration.rules.filter { !RuleRegistry.rules.keys.contains($0.key) }
223+
for rule in invalidRules {
224+
diagnosticsEngine.emitWarning("Configuration contains an unrecognized rule: \(rule.key)", location: nil)
225+
}
226+
}
210227
}

Sources/swift-format/Utilities/DiagnosticsEngine.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,20 @@ final class DiagnosticsEngine {
6565
message: message))
6666
}
6767

68+
/// Emits a generic warning message.
69+
///
70+
/// - Parameters:
71+
/// - message: The message associated with the error.
72+
/// - location: The location in the source code associated with the error, or nil if there is no
73+
/// location associated with the error.
74+
func emitWarning(_ message: String, location: SourceLocation? = nil) {
75+
emit(
76+
Diagnostic(
77+
severity: .warning,
78+
location: location.map(Diagnostic.Location.init),
79+
message: message))
80+
}
81+
6882
/// Emits a finding from the linter and any of its associated notes as diagnostics.
6983
///
7084
/// - Parameter finding: The finding that should be emitted.

0 commit comments

Comments
 (0)