From f60f4eb479dc1be398777e64993b2d5a961a4f53 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Fri, 18 Apr 2025 13:30:51 -0700 Subject: [PATCH] Make linter emit warnings instead of errors by default When running `swift-format lint` in an Xcode run script phase and it exits with a non-zero exit code, the build will fail. This started happening since we treated all linter findings as errors in https://github.com/swiftlang/swift-format/pull/943. To fix this: - Diagnose all findings as warnings again and exit with code 0 even if there are findings - Resurrect `--strict` to treat all findings as errors and exit with 1 if there were any findings. rdar://148389716 --- Sources/swift-format/Frontend/Frontend.swift | 11 +++++++++-- Sources/swift-format/Subcommands/Lint.swift | 16 ++++++---------- .../Utilities/DiagnosticsEngine.swift | 12 ++++++++++-- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/Sources/swift-format/Frontend/Frontend.swift b/Sources/swift-format/Frontend/Frontend.swift index e1007414b..aaa24ad01 100644 --- a/Sources/swift-format/Frontend/Frontend.swift +++ b/Sources/swift-format/Frontend/Frontend.swift @@ -200,14 +200,21 @@ class Frontend { /// Creates a new frontend with the given options. /// /// - Parameter lintFormatOptions: Options that apply during formatting or linting. - init(configurationOptions: ConfigurationOptions, lintFormatOptions: LintFormatOptions) { + init( + configurationOptions: ConfigurationOptions, + lintFormatOptions: LintFormatOptions, + treatWarningsAsErrors: Bool = false + ) { self.configurationOptions = configurationOptions self.lintFormatOptions = lintFormatOptions self.diagnosticPrinter = StderrDiagnosticPrinter( colorMode: lintFormatOptions.colorDiagnostics.map { $0 ? .on : .off } ?? .auto ) - self.diagnosticsEngine = DiagnosticsEngine(diagnosticsHandlers: [diagnosticPrinter.printDiagnostic]) + self.diagnosticsEngine = DiagnosticsEngine( + diagnosticsHandlers: [diagnosticPrinter.printDiagnostic], + treatWarningsAsErrors: treatWarningsAsErrors + ) self.configurationProvider = ConfigurationProvider(diagnosticsEngine: self.diagnosticsEngine) } diff --git a/Sources/swift-format/Subcommands/Lint.swift b/Sources/swift-format/Subcommands/Lint.swift index 0f6c9bfaf..99d088a39 100644 --- a/Sources/swift-format/Subcommands/Lint.swift +++ b/Sources/swift-format/Subcommands/Lint.swift @@ -28,7 +28,7 @@ extension SwiftFormatCommand { @Flag( name: .shortAndLong, - help: "Fail on warnings. Deprecated: All findings are treated as errors now." + help: "Treat all findings as errors instead of warnings." ) var strict: Bool = false @@ -37,15 +37,11 @@ extension SwiftFormatCommand { func run() throws { try performanceMeasurementOptions.printingInstructionCountIfRequested { - let frontend = LintFrontend(configurationOptions: configurationOptions, lintFormatOptions: lintOptions) - - if strict { - frontend.diagnosticsEngine.emitWarning( - """ - Running swift-format with --strict is deprecated and will be removed in the future. - """ - ) - } + let frontend = LintFrontend( + configurationOptions: configurationOptions, + lintFormatOptions: lintOptions, + treatWarningsAsErrors: strict + ) frontend.run() if frontend.diagnosticsEngine.hasErrors { diff --git a/Sources/swift-format/Utilities/DiagnosticsEngine.swift b/Sources/swift-format/Utilities/DiagnosticsEngine.swift index d014c8129..5d51bef71 100644 --- a/Sources/swift-format/Utilities/DiagnosticsEngine.swift +++ b/Sources/swift-format/Utilities/DiagnosticsEngine.swift @@ -26,20 +26,28 @@ final class DiagnosticsEngine { /// A Boolean value indicating whether any warnings were emitted by the diagnostics engine. private(set) var hasWarnings: Bool + /// Whether to upgrade all warnings to errors. + private let treatWarningsAsErrors: Bool + /// Creates a new diagnostics engine with the given diagnostic handlers. /// /// - Parameter diagnosticsHandlers: An array of functions, each of which takes a `Diagnostic` as /// its sole argument and returns `Void`. The functions are called whenever a diagnostic is /// received by the engine. - init(diagnosticsHandlers: [(Diagnostic) -> Void]) { + init(diagnosticsHandlers: [(Diagnostic) -> Void], treatWarningsAsErrors: Bool = false) { self.handlers = diagnosticsHandlers self.hasErrors = false self.hasWarnings = false + self.treatWarningsAsErrors = treatWarningsAsErrors } /// Emits the diagnostic by passing it to the registered handlers, and tracks whether it was an /// error or warning diagnostic. private func emit(_ diagnostic: Diagnostic) { + var diagnostic = diagnostic + if treatWarningsAsErrors, diagnostic.severity == .warning { + diagnostic.severity = .error + } switch diagnostic.severity { case .error: self.hasErrors = true case .warning: self.hasWarnings = true @@ -135,7 +143,7 @@ final class DiagnosticsEngine { /// diagnostics engine and returns it. private func diagnosticMessage(for finding: Finding) -> Diagnostic { return Diagnostic( - severity: .error, + severity: .warning, location: finding.location.map(Diagnostic.Location.init), category: "\(finding.category)", message: "\(finding.message.text)"