Repository: swiftlang/swift-format
Pull request #943: Remove severity
Author: login: bkolb
Created: 2025-02-21T21:57:06Z | Merged: 2025-03-20T13:31:49Z

Instead of making the severity configurable,
this patch removes severity all together and treats every finding as an error.

Issue: #879


Diff:
diff --git a/.gitignore b/.gitignore
index 7bfa76f4a..63325b54d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@
 swift-format.xcodeproj/
 Package.resolved
 /.vscode
+.index-build
diff --git a/Documentation/RuleDocumentation.md b/Documentation/RuleDocumentation.md
index 0b626d5cf..a6d2d0bac 100644
--- a/Documentation/RuleDocumentation.md
+++ b/Documentation/RuleDocumentation.md
@@ -4,7 +4,7 @@
 
 Use the rules below in the `rules` block of your `.swift-format`
 configuration file, as described in
-[Configuration](Configuration.md). All of these rules can be
+[Configuration](Documentation/Configuration.md). All of these rules can be
 applied in the linter, but only some of them can format your source code
 automatically.
 
diff --git a/Sources/SwiftFormat/API/Finding.swift b/Sources/SwiftFormat/API/Finding.swift
index c4fe886ab..7a9399cb9 100644
--- a/Sources/SwiftFormat/API/Finding.swift
+++ b/Sources/SwiftFormat/API/Finding.swift
@@ -12,13 +12,6 @@
 
 /// A problem with the style or syntax of the source code discovered during linting or formatting.
 public struct Finding {
-  /// The severity of a finding.
-  public enum Severity {
-    case warning
-    case error
-    case refactoring
-    case convention
-  }
 
   /// The file path and location in that file where a finding was encountered.
   public struct Location {
@@ -83,27 +76,22 @@ public struct Finding {
   /// The finding's message.
   public let message: Message
 
-  /// The severity of the finding.
-  public let severity: Severity
-
   /// The optional location of the finding.
   public let location: Location?
 
   /// Notes that provide additional detail about the finding.
   public let notes: [Note]
 
-  /// Creates a new finding with the given category, message, severity, optional location, and
+  /// Creates a new finding with the given category, message, optional location, and
   /// notes.
   init(
     category: FindingCategorizing,
     message: Message,
-    severity: Finding.Severity,
     location: Location? = nil,
     notes: [Note] = []
   ) {
     self.category = category
     self.message = message
-    self.severity = severity
     self.location = location
     self.notes = notes
   }
diff --git a/Sources/SwiftFormat/API/FindingCategorizing.swift b/Sources/SwiftFormat/API/FindingCategorizing.swift
index 46ad563ff..08fc7859c 100644
--- a/Sources/SwiftFormat/API/FindingCategorizing.swift
+++ b/Sources/SwiftFormat/API/FindingCategorizing.swift
@@ -17,13 +17,5 @@
 /// to be displayed as part of the diagnostic message when the finding is presented to the user.
 /// For example, the category `Indentation` in the message `[Indentation] Indent by 2 spaces`.
 public protocol FindingCategorizing: CustomStringConvertible {
-  /// The default severity of findings emitted in this category.
-  ///
-  /// By default, all findings are warnings. Individual categories may choose to override this to
-  /// make the findings in those categories more severe.
-  var defaultSeverity: Finding.Severity { get }
-}
 
-extension FindingCategorizing {
-  public var defaultSeverity: Finding.Severity { .warning }
 }
diff --git a/Sources/SwiftFormat/Core/FindingEmitter.swift b/Sources/SwiftFormat/Core/FindingEmitter.swift
index b42e101f1..71dd8f83d 100644
--- a/Sources/SwiftFormat/Core/FindingEmitter.swift
+++ b/Sources/SwiftFormat/Core/FindingEmitter.swift
@@ -48,13 +48,10 @@ final class FindingEmitter {
   ) {
     guard let consumer = self.consumer else { return }
 
-    // TODO: Provide a way via the formatter configuration for users to customize the severity of
-    // findings based on their category, falling back to the default if it isn't overridden.
     consumer(
       Finding(
         category: category,
         message: message,
-        severity: category.defaultSeverity,
         location: location,
         notes: notes
       )
diff --git a/Sources/SwiftFormat/Core/Parsing.swift b/Sources/SwiftFormat/Core/Parsing.swift
index 09b20ef0a..36a8ad9fc 100644
--- a/Sources/SwiftFormat/Core/Parsing.swift
+++ b/Sources/SwiftFormat/Core/Parsing.swift
@@ -63,11 +63,9 @@ func parseAndEmitDiagnostics(
     for diagnostic in diagnostics {
       let location = diagnostic.location(converter: expectedConverter)
 
-      // Downgrade editor placeholders to warnings, because it is useful to support formatting
+      // Ignore editor placeholders, because it is useful to support formatting
       // in-progress files that contain those.
-      if diagnostic.diagnosticID == StaticTokenError.editorPlaceholder.diagnosticID {
-        parsingDiagnosticHandler(downgradedToWarning(diagnostic), location)
-      } else {
+      if diagnostic.diagnosticID != StaticTokenError.editorPlaceholder.diagnosticID {
         parsingDiagnosticHandler(diagnostic, location)
         hasErrors = true
       }
@@ -79,29 +77,3 @@ func parseAndEmitDiagnostics(
   }
   return sourceFile
 }
-
-// Wraps a `DiagnosticMessage` but forces its severity to be that of a warning instead of an error.
-struct DowngradedDiagnosticMessage: DiagnosticMessage {
-  var originalDiagnostic: DiagnosticMessage
-
-  var message: String { originalDiagnostic.message }
-
-  var diagnosticID: SwiftDiagnostics.MessageID { originalDiagnostic.diagnosticID }
-
-  var severity: DiagnosticSeverity { .warning }
-}
-
-/// Returns a new `Diagnostic` that is identical to the given diagnostic, except that its severity
-/// has been downgraded to a warning.
-func downgradedToWarning(_ diagnostic: Diagnostic) -> Diagnostic {
-  // `Diagnostic` is immutable, so create a new one with the same values except for the
-  // severity-downgraded message.
-  return Diagnostic(
-    node: diagnostic.node,
-    position: diagnostic.position,
-    message: DowngradedDiagnosticMessage(originalDiagnostic: diagnostic.diagMessage),
-    highlights: diagnostic.highlights,
-    notes: diagnostic.notes,
-    fixIts: diagnostic.fixIts
-  )
-}
diff --git a/Sources/SwiftFormat/Core/Rule.swift b/Sources/SwiftFormat/Core/Rule.swift
index 368c7087e..764aa45e3 100644
--- a/Sources/SwiftFormat/Core/Rule.swift
+++ b/Sources/SwiftFormat/Core/Rule.swift
@@ -62,7 +62,6 @@ extension Rule {
   public func diagnose<SyntaxType: SyntaxProtocol>(
     _ message: Finding.Message,
     on node: SyntaxType?,
-    severity: Finding.Severity? = nil,
     anchor: FindingAnchor = .start,
     notes: [Finding.Note] = []
   ) {
@@ -86,7 +85,7 @@ extension Rule {
       syntaxLocation = nil
     }
 
-    let category = RuleBasedFindingCategory(ruleType: type(of: self), severity: severity)
+    let category = RuleBasedFindingCategory(ruleType: type(of: self))
     context.findingEmitter.emit(
       message,
       category: category,
diff --git a/Sources/SwiftFormat/Core/RuleBasedFindingCategory.swift b/Sources/SwiftFormat/Core/RuleBasedFindingCategory.swift
index a43dade37..03a2f4541 100644
--- a/Sources/SwiftFormat/Core/RuleBasedFindingCategory.swift
+++ b/Sources/SwiftFormat/Core/RuleBasedFindingCategory.swift
@@ -22,11 +22,8 @@ struct RuleBasedFindingCategory: FindingCategorizing {
 
   var description: String { ruleType.ruleName }
 
-  var severity: Finding.Severity?
-
   /// Creates a finding category that wraps the given rule type.
-  init(ruleType: Rule.Type, severity: Finding.Severity? = nil) {
+  init(ruleType: Rule.Type) {
     self.ruleType = ruleType
-    self.severity = severity
   }
 }
diff --git a/Sources/SwiftFormat/Rules/OmitExplicitReturns.swift b/Sources/SwiftFormat/Rules/OmitExplicitReturns.swift
index d9808732c..032ef736d 100644
--- a/Sources/SwiftFormat/Rules/OmitExplicitReturns.swift
+++ b/Sources/SwiftFormat/Rules/OmitExplicitReturns.swift
@@ -34,7 +34,7 @@ public final class OmitExplicitReturns: SyntaxFormatRule {
     }
 
     funcDecl.body?.statements = rewrapReturnedExpression(returnStmt)
-    diagnose(.omitReturnStatement, on: returnStmt, severity: .refactoring)
+    diagnose(.omitReturnStatement, on: returnStmt)
     return DeclSyntax(funcDecl)
   }
 
@@ -78,7 +78,7 @@ public final class OmitExplicitReturns: SyntaxFormatRule {
     }
 
     closureExpr.statements = rewrapReturnedExpression(returnStmt)
-    diagnose(.omitReturnStatement, on: returnStmt, severity: .refactoring)
+    diagnose(.omitReturnStatement, on: returnStmt)
     return ExprSyntax(closureExpr)
   }
 
@@ -111,7 +111,7 @@ public final class OmitExplicitReturns: SyntaxFormatRule {
 
       getter.body?.statements = rewrapReturnedExpression(returnStmt)
 
-      diagnose(.omitReturnStatement, on: returnStmt, severity: .refactoring)
+      diagnose(.omitReturnStatement, on: returnStmt)
 
       accessors[getterAt] = getter
       var newBlock = accessorBlock
@@ -123,7 +123,7 @@ public final class OmitExplicitReturns: SyntaxFormatRule {
         return nil
       }
 
-      diagnose(.omitReturnStatement, on: returnStmt, severity: .refactoring)
+      diagnose(.omitReturnStatement, on: returnStmt)
 
       var newBlock = accessorBlock
       newBlock.accessors = .getter(rewrapReturnedExpression(returnStmt))
diff --git a/Sources/SwiftFormat/Rules/TypeNamesShouldBeCapitalized.swift b/Sources/SwiftFormat/Rules/TypeNamesShouldBeCapitalized.swift
index 44912bad7..8a610ed0c 100644
--- a/Sources/SwiftFormat/Rules/TypeNamesShouldBeCapitalized.swift
+++ b/Sources/SwiftFormat/Rules/TypeNamesShouldBeCapitalized.swift
@@ -61,7 +61,7 @@ public final class TypeNamesShouldBeCapitalized: SyntaxLintRule {
     if let firstChar = name.text[leadingUnderscores.endIndex...].first,
       firstChar.uppercased() != String(firstChar)
     {
-      diagnose(.capitalizeTypeName(name: name.text, kind: kind), on: name, severity: .convention)
+      diagnose(.capitalizeTypeName(name: name.text, kind: kind), on: name)
     }
   }
 }
diff --git a/Sources/_SwiftFormatTestSupport/Configuration+Testing.swift b/Sources/_SwiftFormatTestSupport/Configuration+Testing.swift
index a3c593726..0c8974609 100644
--- a/Sources/_SwiftFormatTestSupport/Configuration+Testing.swift
+++ b/Sources/_SwiftFormatTestSupport/Configuration+Testing.swift
@@ -44,4 +44,11 @@ extension Configuration {
     config.indentBlankLines = false
     return config
   }
+
+  public static func forTesting(enabledRule: String) -> Configuration {
+    var config = Configuration.forTesting
+    config.rules = config.rules.mapValues({ _ in false })
+    config.rules[enabledRule] = true
+    return config
+  }
 }
diff --git a/Sources/swift-format/Subcommands/Lint.swift b/Sources/swift-format/Subcommands/Lint.swift
index cee4cee41..0f6c9bfaf 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."
+      help: "Fail on warnings. Deprecated: All findings are treated as errors now."
     )
     var strict: Bool = false
 
@@ -38,9 +38,17 @@ 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.
+            """
+          )
+        }
         frontend.run()
 
-        if frontend.diagnosticsEngine.hasErrors || strict && frontend.diagnosticsEngine.hasWarnings {
+        if frontend.diagnosticsEngine.hasErrors {
           throw ExitCode.failure
         }
       }
diff --git a/Sources/swift-format/Utilities/DiagnosticsEngine.swift b/Sources/swift-format/Utilities/DiagnosticsEngine.swift
index 220a2a23c..d014c8129 100644
--- a/Sources/swift-format/Utilities/DiagnosticsEngine.swift
+++ b/Sources/swift-format/Utilities/DiagnosticsEngine.swift
@@ -134,15 +134,8 @@ final class DiagnosticsEngine {
   /// Converts a lint finding into a diagnostic message that can be used by the `TSCBasic`
   /// diagnostics engine and returns it.
   private func diagnosticMessage(for finding: Finding) -> Diagnostic {
-    let severity: Diagnostic.Severity
-    switch finding.severity {
-    case .error: severity = .error
-    case .warning: severity = .warning
-    case .refactoring: severity = .warning
-    case .convention: severity = .warning
-    }
     return Diagnostic(
-      severity: severity,
+      severity: .error,
       location: finding.location.map(Diagnostic.Location.init),
       category: "\(finding.category)",
       message: "\(finding.message.text)"
diff --git a/Tests/SwiftFormatTests/Rules/FileScopedDeclarationPrivacyTests.swift b/Tests/SwiftFormatTests/Rules/FileScopedDeclarationPrivacyTests.swift
index 82acf48dc..c0a317f52 100644
--- a/Tests/SwiftFormatTests/Rules/FileScopedDeclarationPrivacyTests.swift
+++ b/Tests/SwiftFormatTests/Rules/FileScopedDeclarationPrivacyTests.swift
@@ -179,7 +179,7 @@ final class FileScopedDeclarationPrivacyTests: LintOrFormatRuleTestCase {
     findingsProvider: (String, String) -> [FindingSpec]
   ) {
     for testConfig in testConfigurations {
-      var configuration = Configuration.forTesting
+      var configuration = Configuration.forTesting(enabledRule: FileScopedDeclarationPrivacy.self.ruleName)
       configuration.fileScopedDeclarationPrivacy.accessLevel = testConfig.desired
 
       let substitutedInput = source.replacingOccurrences(of: "$access$", with: testConfig.original)
diff --git a/Tests/SwiftFormatTests/Rules/LintOrFormatRuleTestCase.swift b/Tests/SwiftFormatTests/Rules/LintOrFormatRuleTestCase.swift
index 0e5756fb2..938d956d8 100644
--- a/Tests/SwiftFormatTests/Rules/LintOrFormatRuleTestCase.swift
+++ b/Tests/SwiftFormatTests/Rules/LintOrFormatRuleTestCase.swift
@@ -49,8 +49,7 @@ class LintOrFormatRuleTestCase: DiagnosingTestCase {
     var emittedFindings = [Finding]()
 
     // Force the rule to be enabled while we test it.
-    var configuration = Configuration.forTesting
-    configuration.rules[type.ruleName] = true
+    let configuration = Configuration.forTesting(enabledRule: type.ruleName)
     let context = makeContext(
       sourceFileSyntax: sourceFileSyntax,
       configuration: configuration,
@@ -59,8 +58,6 @@ class LintOrFormatRuleTestCase: DiagnosingTestCase {
     )
 
     var emittedPipelineFindings = [Finding]()
-    // Disable default rules, so only select rule runs in pipeline
-    configuration.rules = [type.ruleName: true]
     let pipeline = SwiftLinter(
       configuration: configuration,
       findingConsumer: { emittedPipelineFindings.append($0) }
@@ -118,8 +115,8 @@ class LintOrFormatRuleTestCase: DiagnosingTestCase {
     var emittedFindings = [Finding]()
 
     // Force the rule to be enabled while we test it.
-    var configuration = configuration ?? Configuration.forTesting
-    configuration.rules[formatType.ruleName] = true
+    let configuration = configuration ?? Configuration.forTesting(enabledRule: formatType.ruleName)
+
     let context = makeContext(
       sourceFileSyntax: sourceFileSyntax,
       configuration: configuration,
@@ -162,8 +159,6 @@ class LintOrFormatRuleTestCase: DiagnosingTestCase {
     )
 
     var emittedPipelineFindings = [Finding]()
-    // Disable default rules, so only select rule runs in pipeline
-    configuration.rules = [formatType.ruleName: true]
     let pipeline = SwiftFormatter(
       configuration: configuration,
       findingConsumer: { emittedPipelineFindings.append($0) }
diff --git a/api-breakages.txt b/api-breakages.txt
index ec9be6cbc..0b04b73c8 100644
--- a/api-breakages.txt
+++ b/api-breakages.txt
@@ -7,3 +7,10 @@ API breakage: enumelement SwiftFormatError.unsupportedConfigurationVersion has b
 API breakage: class UseLetInEveryBoundCaseVariable has changed its super class from SwiftFormat.SyntaxLintRule to SwiftFormat.SyntaxFormatRule
 API breakage: func UseLetInEveryBoundCaseVariable.visit(_:) has return type change from SwiftSyntax.SyntaxVisitorContinueKind to SwiftSyntax.MatchingPatternConditionSyntax
 API breakage: func UseLetInEveryBoundCaseVariable.visit(_:) has parameter 0 type change from SwiftSyntax.ValueBindingPatternSyntax to SwiftSyntax.MatchingPatternConditionSyntax
+API breakage: func Rule.diagnose(_:on:severity:anchor:notes:) has parameter 2 type change from SwiftFormat.Finding.Severity? to SwiftFormat.FindingAnchor
+API breakage: func Rule.diagnose(_:on:severity:anchor:notes:) has parameter 3 type change from SwiftFormat.FindingAnchor to [SwiftFormat.Finding.Note]
+API breakage: enum Finding.Severity has been removed
+API breakage: var Finding.severity has been removed
+API breakage: var FindingCategorizing.defaultSeverity has been removed
+API breakage: var FindingCategorizing.defaultSeverity has been removed
+API breakage: func Rule.diagnose(_:on:severity:anchor:notes:) has been renamed to func diagnose(_:on:anchor:notes:)
\ No newline at end of file


Reviews:
user: login: ahoppen
body: Thank you, Bernd. Looks good to me overall, just have a few thoughts inline. I would also like to hear @allevato’s opinion before merging this. 
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-24T20:02:39Z
user: login: allevato
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-24T20:27:41Z
user: login: allevato
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-25T13:52:08Z
user: login: allevato
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-25T13:55:46Z
user: login: allevato
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-25T13:56:21Z
user: login: allevato
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-25T14:20:15Z
user: login: bkolb
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-09T14:01:13Z
user: login: ahoppen
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-10T18:47:38Z
user: login: bkolb
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T21:28:33Z
user: login: bkolb
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T21:31:16Z
user: login: bkolb
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T21:34:41Z
user: login: allevato
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-13T12:08:29Z
user: login: bkolb
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T11:08:58Z
user: login: bkolb
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T14:18:43Z
user: login: allevato
body: Thanks! This is a lot cleaner.
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-18T15:47:39Z
user: login: allevato
body: Looks like these need to be added to `api-breakages.txt`:

```
7 breaking changes detected in SwiftFormat:
  💔 API breakage: func Rule.diagnose(_:on:severity:anchor:notes:) has parameter 2 type change from SwiftFormat.Finding.Severity? to SwiftFormat.FindingAnchor
  💔 API breakage: func Rule.diagnose(_:on:severity:anchor:notes:) has parameter 3 type change from SwiftFormat.FindingAnchor to [SwiftFormat.Finding.Note]
  💔 API breakage: enum Finding.Severity has been removed
  💔 API breakage: var Finding.severity has been removed
  💔 API breakage: var FindingCategorizing.defaultSeverity has been removed
  💔 API breakage: var FindingCategorizing.defaultSeverity has been removed
  💔 API breakage: func Rule.diagnose(_:on:severity:anchor:notes:) has been renamed to func diagnose(_:on:anchor:notes:)
```
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-18T15:54:13Z

Review comments:
diff_hunk: @@ -113,6 +113,7 @@ var targets: [Target] = [
     dependencies: [
       "SwiftFormat",
       "_SwiftFormatTestSupport",
+      "swift-format",
path: Package.swift
user: login: ahoppen
body: I think depending on executable targets form tests doesn’t work on Windows (see https://github.com/swiftlang/swift-format/pull/827). Could you move the necessary types to the `SwiftFormat` module so we can test them?
created_at: 2025-02-24T19:41:45Z
author_association: MEMBER
diff_hunk: @@ -44,4 +44,24 @@ extension Configuration {
     config.indentBlankLines = false
     return config
   }
+
+  public static func forTesting(enabledRule: String) -> Configuration {
+    var config = Configuration.forTesting.disableAllRules()
+    config.rules[enabledRule] = true
+    return config
+  }
+}
+
+extension Configuration {
+  public func disableAllRules() -> Self {
path: Sources/_SwiftFormatTestSupport/Configuration+Testing.swift
user: login: ahoppen
body: According to the [Swift API Design Guidelines](https://www.swift.org/documentation/api-design-guidelines/#name-according-to-side-effects), this should be `withAllRulesDisabled` or something like that. The current name would suggest that the method mutates `self`.

Same for `enable`.
created_at: 2025-02-24T19:44:15Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,102 @@
+//===----------------------------------------------------------------------===//
+//
+// This source file is part of the Swift.org open source project
+//
+// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See https://swift.org/LICENSE.txt for license information
+// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
+//
+//===----------------------------------------------------------------------===//
+
+import Foundation
+@_spi(Rules) import SwiftFormat
+import SwiftParser
+import SwiftSyntax
+
+/// Collects information about rules in the formatter code base.
+final class PrettyPrintCollector {
+
+  /// A list of all the format-only pretty-print categories found in the code base.
+  var allPrettyPrinterCategories = Set<String>()
+
+  /// Populates the internal collections with rules in the given directory.
+  ///
+  /// - Parameter url: The file system URL that should be scanned for rules.
+  func collect(from url: URL) throws {
+    // For each file in the Rules directory, find types that either conform to SyntaxLintRule or
+    // inherit from SyntaxFormatRule.
+    let fm = FileManager.default
+    guard let rulesEnumerator = fm.enumerator(atPath: url.path) else {
+      fatalError("Could not list the directory \(url.path)")
+    }
+
+    for baseName in rulesEnumerator {
+      // Ignore files that aren't Swift source files.
+      guard let baseName = baseName as? String, baseName.hasSuffix(".swift") else { continue }
+
+      let fileURL = url.appendingPathComponent(baseName)
+      let fileInput = try String(contentsOf: fileURL)
+      let sourceFile = Parser.parse(source: fileInput)
+
+      for statement in sourceFile.statements {
+        let pp = self.detectPrettyPrintCategories(at: statement)
+        allPrettyPrinterCategories.formUnion(pp)
+      }
+    }
+  }
+
+  private func detectPrettyPrintCategories(at statement: CodeBlockItemSyntax) -> [String] {
+    guard let enumDecl = statement.item.as(EnumDeclSyntax.self) else {
+      return []
+    }
+
+    if enumDecl.name.text == "PrettyPrintFindingCategory" {
+      print("HIT")
path: Sources/generate-swift-format/PrettyPrintCollector.swift
user: login: ahoppen
body: Leftover from development?
created_at: 2025-02-24T19:45:17Z
author_association: MEMBER
path: Sources/generate-swift-format/PrettyPrintCollector.swift
user: login: ahoppen
body: Is this an unrelated change to the removal of `severity`? If so, could you open a separate PR for it?
created_at: 2025-02-24T19:45:52Z
author_association: MEMBER
diff_hunk: @@ -28,7 +28,7 @@ extension SwiftFormatCommand {
 
     @Flag(
       name: .shortAndLong,
-      help: "Fail on warnings."
+      help: "Fail on warnings. Deprecated: All findings are treated as errors now."
     )
     var strict: Bool = false
path: Sources/swift-format/Subcommands/Lint.swift
user: login: ahoppen
body: Should we print a message to stderr to notify the user that `--strict` is deprecated, similar to what we did in https://github.com/swiftlang/swift-format/pull/914?
created_at: 2025-02-24T19:48:11Z
author_association: MEMBER
diff_hunk: @@ -216,7 +216,7 @@ class Frontend {
     if lintFormatOptions.paths == ["-"] {
       processStandardInput()
     } else if lintFormatOptions.paths.isEmpty {
-      diagnosticsEngine.emitWarning(
+      diagnosticsEngine.emitError(
path: Sources/swift-format/Frontend/Frontend.swift
user: login: ahoppen
body: This should continue to be a warning, otherwise we don’t have a soft deprecation of invoking `swift-format` without an input path. Maybe this just needs to become a print to stderr instead of using the diagnostic engine.
created_at: 2025-02-24T19:50:36Z
author_association: MEMBER
diff_hunk: @@ -40,7 +40,7 @@ class Frontend {
       // That way they will be printed out, but we'll continue execution on the valid rules.
       let invalidRules = configuration.rules.filter { !RuleRegistry.rules.keys.contains($0.key) }
       for rule in invalidRules {
-        diagnosticsEngine.emitWarning("Configuration contains an unrecognized rule: \(rule.key)", location: nil)
+        diagnosticsEngine.emitError("Configuration contains an unrecognized rule: \(rule.key)", location: nil)
path: Sources/swift-format/Frontend/Frontend.swift
user: login: ahoppen
body: I think this could break clients in the following scenario: You have a Swift 6.1 rule added to your `.swift-format` file but locally you are running a 6.0 `swift-format` to format your code (not to lint). That should mostly work but with this change, formatting would exit with 1 and thus fail (IIUC Xcode would show an error message in that case). So, I think this would need to become a print to stderr instead of going through the diagnostic engine.

CC @allevato Do you have thoughts on this?
created_at: 2025-02-24T20:01:39Z
author_association: MEMBER
diff_hunk: @@ -40,7 +40,7 @@ class Frontend {
       // That way they will be printed out, but we'll continue execution on the valid rules.
       let invalidRules = configuration.rules.filter { !RuleRegistry.rules.keys.contains($0.key) }
       for rule in invalidRules {
-        diagnosticsEngine.emitWarning("Configuration contains an unrecognized rule: \(rule.key)", location: nil)
+        diagnosticsEngine.emitError("Configuration contains an unrecognized rule: \(rule.key)", location: nil)
path: Sources/swift-format/Frontend/Frontend.swift
user: login: allevato
body: I'll do a more thorough soon, but for this issue specifically: I'd like to keep all messages from the formatter going through the diagnostics engine instead of having some there and some through just `stderr`.

I think the answer here is that we should only remove the severity from `Finding`s but not `Diagnostic`/`DiagnosticEngine`. That removes the complexity from the rules themselves (and forces all findings to become errors, which is the change we're asking for), but still lets the formatter frontend issue other diagnostics that aren't as severe.
created_at: 2025-02-24T20:27:41Z
author_association: MEMBER
diff_hunk: @@ -16,7 +16,10 @@ final class OmitReturnsTests: LintOrFormatRuleTestCase {
           }
         """,
       findings: [
-        FindingSpec("1️⃣", message: "'return' can be omitted because body consists of a single expression")
+        FindingSpec(
path: Tests/SwiftFormatTests/Rules/OmitReturnsTests.swift
user: login: allevato
body: Here and below, are these reformattings relevant to this PR? If not, can you revert them to reduce the number of unrelated changes?
created_at: 2025-02-25T13:52:08Z
author_association: MEMBER
diff_hunk: @@ -44,4 +44,24 @@ extension Configuration {
     config.indentBlankLines = false
     return config
   }
+
+  public static func forTesting(enabledRule: String) -> Configuration {
+    var config = Configuration.forTesting.disableAllRules()
+    config.rules[enabledRule] = true
+    return config
+  }
+}
+
+extension Configuration {
+  public func disableAllRules() -> Self {
+    var config = self
+    config.rules = config.rules.mapValues({ _ in false })
+    return config
+  }
+
+  public func enable(_ rule: String, enabled: Bool) -> Self {
path: Sources/_SwiftFormatTestSupport/Configuration+Testing.swift
user: login: allevato
body: This doesn't look used; can it be removed?
created_at: 2025-02-25T13:55:46Z
author_association: MEMBER
diff_hunk: @@ -44,4 +44,24 @@ extension Configuration {
     config.indentBlankLines = false
     return config
   }
+
+  public static func forTesting(enabledRule: String) -> Configuration {
+    var config = Configuration.forTesting.disableAllRules()
+    config.rules[enabledRule] = true
+    return config
+  }
+}
+
+extension Configuration {
+  public func disableAllRules() -> Self {
path: Sources/_SwiftFormatTestSupport/Configuration+Testing.swift
user: login: allevato
body: It looks like this is unused outside of the call in `forTesting(...)` above; I would consider inlining it there until we have a separate need for it.
created_at: 2025-02-25T13:56:20Z
author_association: MEMBER
diff_hunk: @@ -65,9 +65,7 @@ func parseAndEmitDiagnostics(
 
       // Downgrade editor placeholders to warnings, because it is useful to support formatting
path: Sources/SwiftFormat/Core/Parsing.swift
user: login: allevato
body: Please update this comment to indicate that editor placeholder errors are being ignored instead of downgraded to warnings.

(Which I think is fine. Editor placeholders are common in source being interactively edited and formatted that I think it's fine to not report them at all.)
created_at: 2025-02-25T14:20:15Z
author_association: MEMBER
diff_hunk: @@ -44,4 +44,24 @@ extension Configuration {
     config.indentBlankLines = false
     return config
   }
+
+  public static func forTesting(enabledRule: String) -> Configuration {
+    var config = Configuration.forTesting.disableAllRules()
+    config.rules[enabledRule] = true
+    return config
+  }
+}
+
+extension Configuration {
+  public func disableAllRules() -> Self {
path: Sources/_SwiftFormatTestSupport/Configuration+Testing.swift
user: login: bkolb
body: done
created_at: 2025-03-09T14:01:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -44,4 +44,24 @@ extension Configuration {
     config.indentBlankLines = false
     return config
   }
+
+  public static func forTesting(enabledRule: String) -> Configuration {
+    var config = Configuration.forTesting.disableAllRules()
+    config.rules[enabledRule] = true
+    return config
+  }
+}
+
+extension Configuration {
+  public func disableAllRules() -> Self {
path: Sources/_SwiftFormatTestSupport/Configuration+Testing.swift
user: login: ahoppen
body: Looks like you didn’t push your recent changes 😉 
created_at: 2025-03-10T18:47:37Z
author_association: MEMBER
diff_hunk: @@ -16,7 +16,10 @@ final class OmitReturnsTests: LintOrFormatRuleTestCase {
           }
         """,
       findings: [
-        FindingSpec("1️⃣", message: "'return' can be omitted because body consists of a single expression")
+        FindingSpec(
path: Tests/SwiftFormatTests/Rules/OmitReturnsTests.swift
user: login: bkolb
body: I get them when running 
`xcrun swift-format --recursive --in-place --configuration .swift-format .`
I am not sure why this changed.
created_at: 2025-03-12T21:28:33Z
author_association: CONTRIBUTOR
path: Sources/generate-swift-format/PrettyPrintCollector.swift
user: login: bkolb
body: yes and no. This was to be able to allow setting a severity also for pretty print issues. Is there a way to disable those otherwise?
created_at: 2025-03-12T21:31:16Z
author_association: CONTRIBUTOR
path: Sources/generate-swift-format/PrettyPrintCollector.swift
user: login: bkolb
body: It is related to this: 
Sources/SwiftFormat/Core/RuleRegistry+Generated.swift
created_at: 2025-03-12T21:34:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -92,7 +91,7 @@ final class DiagnosticsEngine {
     for note in finding.notes {
       emit(
         Diagnostic(
-          severity: .note,
+          severity: .warning,
path: Sources/swift-format/Utilities/DiagnosticsEngine.swift
user: login: allevato
body: Should this change be reverted? We're only removing severity from findings, but diagnostics should still support a `note` level severity.
created_at: 2025-03-13T12:03:44Z
author_association: MEMBER
diff_hunk: @@ -16,7 +16,10 @@ final class OmitReturnsTests: LintOrFormatRuleTestCase {
           }
         """,
       findings: [
-        FindingSpec("1️⃣", message: "'return' can be omitted because body consists of a single expression")
+        FindingSpec(
path: Tests/SwiftFormatTests/Rules/OmitReturnsTests.swift
user: login: allevato
body: Did you have any temporary changes to the swift-format configuration in your directory when you ran it? These lines don't exceed the 120 column limit so they shouldn't have been modified, as far as I can tell.
created_at: 2025-03-13T12:05:49Z
author_association: MEMBER
path: Sources/generate-swift-format/PrettyPrintCollector.swift
user: login: allevato
body: IMO, adding a way to disable pretty-print findings is a significant enough feature that it should be factored out into its own PR, not included in a PR about removing finding severity. Can you please do that?

I think we _do_ want that feature, it'll just be easier to track things as a separate PR.
created_at: 2025-03-13T12:07:31Z
author_association: MEMBER
diff_hunk: @@ -16,7 +16,10 @@ final class OmitReturnsTests: LintOrFormatRuleTestCase {
           }
         """,
       findings: [
-        FindingSpec("1️⃣", message: "'return' can be omitted because body consists of a single expression")
+        FindingSpec(
path: Tests/SwiftFormatTests/Rules/OmitReturnsTests.swift
user: login: bkolb
body: no, not that I am aware of. I will manually revert it
created_at: 2025-03-18T11:08:57Z
author_association: CONTRIBUTOR
path: Sources/generate-swift-format/PrettyPrintCollector.swift
user: login: bkolb
body: done
created_at: 2025-03-18T14:18:42Z
author_association: CONTRIBUTOR

PR comments:
user: login: bkolb
created_at: 2025-03-18T14:55:28Z
body: I think I addressed all your comments. Can you please check again?

author_association: CONTRIBUTOR
user: login: bkolb
created_at: 2025-03-20T12:15:46Z
body: I updated the api-breakages.txt file. Can you please approve the workflow again?
author_association: CONTRIBUTOR