From b19234bd169a134749b3d893e8e867019044c02d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20R=C3=B6nnqvist?= Date: Thu, 13 Nov 2025 10:21:24 +0100 Subject: [PATCH 1/2] Add flags to control severity of specific diagnostics rdar://107343046 --- .../Diagnostics/DiagnosticEngine.swift | 35 +++++++++-- .../Actions/Convert/ConvertAction.swift | 7 +++ .../ConvertAction+CommandInitialization.swift | 2 + .../ArgumentParsing/Subcommands/Convert.swift | 58 ++++++++++++++++- .../Diagnostics/DiagnosticEngineTests.swift | 63 ++++++++++++++++++- .../ConvertSubcommandTests.swift | 22 +++++++ features.json | 3 + 7 files changed, 180 insertions(+), 10 deletions(-) diff --git a/Sources/SwiftDocC/Infrastructure/Diagnostics/DiagnosticEngine.swift b/Sources/SwiftDocC/Infrastructure/Diagnostics/DiagnosticEngine.swift index ee014a95e6..5cc65fb5fc 100644 --- a/Sources/SwiftDocC/Infrastructure/Diagnostics/DiagnosticEngine.swift +++ b/Sources/SwiftDocC/Infrastructure/Diagnostics/DiagnosticEngine.swift @@ -40,7 +40,11 @@ public final class DiagnosticEngine { /// Determines whether warnings will be treated as errors. private let treatWarningsAsErrors: Bool - + /// A list of diagnostic identifiers that are explicitly lowered to a "warning" severity. + package var diagnosticIDsWithWarningSeverity: Set + /// A list of diagnostic identifiers that are explicitly raised to an "error" severity. + package var diagnosticIDsWithErrorSeverity: Set + /// Determines which problems should be emitted. private func shouldEmit(_ problem: Problem) -> Bool { problem.diagnostic.severity.rawValue <= filterLevel.rawValue @@ -52,9 +56,21 @@ public final class DiagnosticEngine { } /// Creates a new diagnostic engine instance with no consumers. - public init(filterLevel: DiagnosticSeverity = .warning, treatWarningsAsErrors: Bool = false) { + /// - Parameters: + /// - filterLevel: The lowers severity (inclusive) that the engine emits to its consumers. + /// - treatWarningsAsErrors: A Boolean value indicating whether the engine raises the severity of warnings to "error" (unless `warningGroupsWithWarningSeverity` explicitly lowers the severity of that diagnostic to a warning) + /// - diagnosticIDsWithWarningSeverity: A list of diagnostic identifiers that are explicitly lowered to a "warning" severity. + /// - diagnosticIDsWithErrorSeverity: A list of diagnostic identifiers that are explicitly raised to an "error" severity. + public init( + filterLevel: DiagnosticSeverity = .warning, + treatWarningsAsErrors: Bool = false, + diagnosticIDsWithWarningSeverity: Set = [], + diagnosticIDsWithErrorSeverity: Set = [] + ) { self.filterLevel = filterLevel self.treatWarningsAsErrors = treatWarningsAsErrors + self.diagnosticIDsWithWarningSeverity = diagnosticIDsWithWarningSeverity + self.diagnosticIDsWithErrorSeverity = diagnosticIDsWithErrorSeverity } /// Removes all of the encountered diagnostics from this engine. @@ -77,9 +93,7 @@ public final class DiagnosticEngine { public func emit(_ problems: [Problem]) { let mappedProblems = problems.map { problem -> Problem in var problem = problem - if treatWarningsAsErrors, problem.diagnostic.severity == .warning { - problem.diagnostic.severity = .error - } + updateDiagnosticSeverity(&problem.diagnostic) return problem } let filteredProblems = mappedProblems.filter(shouldEmit) @@ -125,4 +139,15 @@ public final class DiagnosticEngine { $0.removeValue(forKey: ObjectIdentifier(consumer)) } } + + private func updateDiagnosticSeverity(_ diagnostic: inout Diagnostic) { + let identifier = diagnostic.identifier + if diagnosticIDsWithErrorSeverity.contains(identifier) { + diagnostic.severity = .error + } else if diagnosticIDsWithWarningSeverity.contains(identifier) { + diagnostic.severity = .warning + } else if treatWarningsAsErrors, diagnostic.severity == .warning { + diagnostic.severity = .error + } + } } diff --git a/Sources/SwiftDocCUtilities/Action/Actions/Convert/ConvertAction.swift b/Sources/SwiftDocCUtilities/Action/Actions/Convert/ConvertAction.swift index b6c98ecedd..26441710a2 100644 --- a/Sources/SwiftDocCUtilities/Action/Actions/Convert/ConvertAction.swift +++ b/Sources/SwiftDocCUtilities/Action/Actions/Convert/ConvertAction.swift @@ -61,6 +61,8 @@ public struct ConvertAction: AsyncAction { /// - formatConsoleOutputForTools: `true` if the convert action should write diagnostics to the console in a format suitable for parsing by an IDE or other tool, otherwise `false`. /// - inheritDocs: `true` if the convert action should retain the original documentation content for inherited symbols, otherwise `false`. /// - treatWarningsAsErrors: `true` if the convert action should treat warnings as errors, otherwise `false`. + /// - diagnosticIDsWithWarningSeverity: A list of diagnostic identifiers that are explicitly lowered to a "warning" severity. + /// - diagnosticIDsWithErrorSeverity: A list of diagnostic identifiers that are explicitly raised to an "error" severity. /// - experimentalEnableCustomTemplates: `true` if the convert action should enable support for custom "header.html" and "footer.html" template files, otherwise `false`. /// - experimentalModifyCatalogWithGeneratedCuration: `true` if the convert action should write documentation extension files containing markdown representations of DocC's automatic curation into the `documentationBundleURL`, otherwise `false`. /// - transformForStaticHosting: `true` if the convert action should process the build documentation archive so that it supports a static hosting environment, otherwise `false`. @@ -88,6 +90,8 @@ public struct ConvertAction: AsyncAction { formatConsoleOutputForTools: Bool = false, inheritDocs: Bool = false, treatWarningsAsErrors: Bool = false, + diagnosticIDsWithWarningSeverity: Set = [], + diagnosticIDsWithErrorSeverity: Set = [], experimentalEnableCustomTemplates: Bool = false, experimentalModifyCatalogWithGeneratedCuration: Bool = false, transformForStaticHosting: Bool = false, @@ -132,7 +136,10 @@ public struct ConvertAction: AsyncAction { self.experimentalModifyCatalogWithGeneratedCuration = experimentalModifyCatalogWithGeneratedCuration let engine = diagnosticEngine ?? DiagnosticEngine(treatWarningsAsErrors: treatWarningsAsErrors) + // Set these properties even if the caller passed a base diagnostic engine engine.filterLevel = filterLevel + engine.diagnosticIDsWithWarningSeverity = diagnosticIDsWithWarningSeverity + engine.diagnosticIDsWithErrorSeverity = diagnosticIDsWithErrorSeverity if let diagnosticFilePath { engine.add(DiagnosticFileWriter(outputPath: diagnosticFilePath)) } diff --git a/Sources/SwiftDocCUtilities/ArgumentParsing/ActionExtensions/ConvertAction+CommandInitialization.swift b/Sources/SwiftDocCUtilities/ArgumentParsing/ActionExtensions/ConvertAction+CommandInitialization.swift index 4d7272d3a2..6cebbfd699 100644 --- a/Sources/SwiftDocCUtilities/ArgumentParsing/ActionExtensions/ConvertAction+CommandInitialization.swift +++ b/Sources/SwiftDocCUtilities/ArgumentParsing/ActionExtensions/ConvertAction+CommandInitialization.swift @@ -75,6 +75,8 @@ extension ConvertAction { formatConsoleOutputForTools: convert.formatConsoleOutputForTools, inheritDocs: convert.enableInheritedDocs, treatWarningsAsErrors: convert.warningsAsErrors, + diagnosticIDsWithWarningSeverity: Set(convert.diagnosticIDsWithWarningSeverity), + diagnosticIDsWithErrorSeverity: Set(convert.diagnosticIDsWithErrorSeverity), experimentalEnableCustomTemplates: convert.experimentalEnableCustomTemplates, experimentalModifyCatalogWithGeneratedCuration: convert.experimentalModifyCatalogWithGeneratedCuration, transformForStaticHosting: convert.transformForStaticHosting, diff --git a/Sources/SwiftDocCUtilities/ArgumentParsing/Subcommands/Convert.swift b/Sources/SwiftDocCUtilities/ArgumentParsing/Subcommands/Convert.swift index a33715a625..bb431ab13a 100644 --- a/Sources/SwiftDocCUtilities/ArgumentParsing/Subcommands/Convert.swift +++ b/Sources/SwiftDocCUtilities/ArgumentParsing/Subcommands/Convert.swift @@ -238,10 +238,30 @@ extension Docc { help: "Format output to the console intended for an IDE or other tool to parse.") var formatConsoleOutputForTools = false - @Flag(help: "Treat warnings as errors") + @Flag(help: "Treat all warnings as errors") var warningsAsErrors = false + + @Option( + name: [.customLong("Werror")], // This matches Swift's spellings + parsing: ArrayParsingStrategy.singleValue, + help: ArgumentHelp("Treat this diagnostic group as an error", valueName: "diagnostic-id") + ) + var warningGroupsWithErrorSeverity: [String] = [] + + @Option( + name: [.customLong("Wwarning")], // This matches Swift's spellings + parsing: ArrayParsingStrategy.singleValue, + help: ArgumentHelp( + "Treat this diagnostic group as a warning", + discussion: """ + If you pass '--warnings-as-errors' you can use this flag to lower one or more specific groups of diagnostics to a warnings severity. + """, + valueName: "diagnostic-id" + ) + ) + var warningGroupsWithWarningSeverity: [String] = [] - func validate() throws { + mutating func validate() throws { if analyze && diagnosticLevel != nil { warnAboutDiagnostic(.init( severity: .information, @@ -260,6 +280,26 @@ extension Docc { """ )) } + + if !warningGroupsWithErrorSeverity.isEmpty, + !warningGroupsWithWarningSeverity.isEmpty + { + // Check if there's overlap between the two diagnostic levels + let diagnosticIDsWithConflictingSeverities = Set(warningGroupsWithErrorSeverity).intersection(warningGroupsWithWarningSeverity) + if !diagnosticIDsWithConflictingSeverities.isEmpty { + for diagnosticID in diagnosticIDsWithConflictingSeverities.sorted() { + warnAboutDiagnostic(.init( + severity: .information, + identifier: "org.swift.docc.ConflictingDiagnosticSeverity", + summary: "Conflicting severity (both '--Wwarning' and '--Werror') for diagnostic group '\(diagnosticID)'." + )) + } + + // Because we don't know which severity was specified last, remove the diagnostic ID from both groups + warningGroupsWithErrorSeverity.removeAll(where: { diagnosticIDsWithConflictingSeverities.contains($0) }) + warningGroupsWithWarningSeverity.removeAll(where: { diagnosticIDsWithConflictingSeverities.contains($0) }) + } + } } private static let supportedDiagnosticLevelsMessage = """ @@ -276,7 +316,19 @@ extension Docc { get { diagnosticOptions.warningsAsErrors } set { diagnosticOptions.warningsAsErrors = newValue } } - + + /// A list of diagnostic identifiers that are explicitly lowered to a "warning" severity. + public var diagnosticIDsWithWarningSeverity: [String] { + get { diagnosticOptions.warningGroupsWithWarningSeverity } + set { diagnosticOptions.warningGroupsWithWarningSeverity = newValue } + } + + /// A list of diagnostic identifiers that are explicitly raised to an "error" severity. + public var diagnosticIDsWithErrorSeverity: [String] { + get { diagnosticOptions.warningGroupsWithErrorSeverity } + set { diagnosticOptions.warningGroupsWithErrorSeverity = newValue } + } + /// A user-provided value that is true if output to the console should be formatted for an IDE or other tool to parse. public var formatConsoleOutputForTools: Bool { get { diagnosticOptions.formatConsoleOutputForTools } diff --git a/Tests/SwiftDocCTests/Diagnostics/DiagnosticEngineTests.swift b/Tests/SwiftDocCTests/Diagnostics/DiagnosticEngineTests.swift index cfcb1b79eb..b067452a44 100644 --- a/Tests/SwiftDocCTests/Diagnostics/DiagnosticEngineTests.swift +++ b/Tests/SwiftDocCTests/Diagnostics/DiagnosticEngineTests.swift @@ -28,7 +28,7 @@ class DiagnosticEngineTests: XCTestCase { let diagnostic = Diagnostic(source: nil, severity: .error, range: nil, identifier: "org.swift.docc.test", summary: "Test diagnostic") let problem = Problem(diagnostic: diagnostic, possibleSolutions: []) let engine = DiagnosticEngine() - let exp = expectation(description: "Recieved diagnostic") + let exp = expectation(description: "Received diagnostic") let consumer = TestConsumer(exp) XCTAssertEqual(engine.problems.count, 0) @@ -52,7 +52,7 @@ class DiagnosticEngineTests: XCTestCase { let diagnostic = Diagnostic(source: nil, severity: .error, range: nil, identifier: "org.swift.docc.test", summary: "Test diagnostic") let problem = Problem(diagnostic: diagnostic, possibleSolutions: []) let engine = DiagnosticEngine() - let exp = expectation(description: "Recieved diagnostic") + let exp = expectation(description: "Received diagnostic") exp.expectedFulfillmentCount = 2 let consumerA = TestConsumer(exp) let consumerB = TestConsumer(exp) @@ -164,4 +164,63 @@ class DiagnosticEngineTests: XCTestCase { error: Test warning """) } + + func testRaiseSeverityOfSpecificDiagnostics() { + let warnings = ["One", "Two", "Three"].map { id in + Problem(diagnostic: Diagnostic(source: nil, severity: .warning, range: nil, identifier: id, summary: "Test diagnostic \(id.lowercased())"), possibleSolutions: []) + } + + let defaultEngine = DiagnosticEngine() + defaultEngine.emit(warnings) + + XCTAssertEqual(DiagnosticConsoleWriter.formattedDescription(for: defaultEngine.problems, options: .formatConsoleOutputForTools), """ + warning: Test diagnostic one + warning: Test diagnostic two + warning: Test diagnostic three + """) + + let engineWithSpecificDiagnosticsRaised = DiagnosticEngine(diagnosticIDsWithErrorSeverity: ["Two", "Unknown"]) + engineWithSpecificDiagnosticsRaised.emit(warnings) + XCTAssertEqual(DiagnosticConsoleWriter.formattedDescription(for: engineWithSpecificDiagnosticsRaised.problems, options: .formatConsoleOutputForTools), """ + warning: Test diagnostic one + error: Test diagnostic two + warning: Test diagnostic three + """) + + let engineWithFilterAndSpecificDiagnosticsRaised = DiagnosticEngine(filterLevel: .error, diagnosticIDsWithErrorSeverity: ["Two", "Unknown"]) + engineWithFilterAndSpecificDiagnosticsRaised.emit(warnings) + XCTAssertEqual(DiagnosticConsoleWriter.formattedDescription(for: engineWithFilterAndSpecificDiagnosticsRaised.problems, options: .formatConsoleOutputForTools), """ + error: Test diagnostic two + """) + } + + func testLowerSeverityOfSpecificDiagnostics() { + let warnings = ["One", "Two", "Three"].map { id in + Problem(diagnostic: Diagnostic(source: nil, severity: .warning, range: nil, identifier: id, summary: "Test diagnostic \(id.lowercased())"), possibleSolutions: []) + } + + let engineWithRaisedSeverity = DiagnosticEngine(treatWarningsAsErrors: true) + engineWithRaisedSeverity.emit(warnings) + + XCTAssertEqual(DiagnosticConsoleWriter.formattedDescription(for: engineWithRaisedSeverity.problems, options: .formatConsoleOutputForTools), """ + error: Test diagnostic one + error: Test diagnostic two + error: Test diagnostic three + """) + + let engineWithSpecificDiagnosticsLowered = DiagnosticEngine(treatWarningsAsErrors: true, diagnosticIDsWithWarningSeverity: ["Two", "Unknown"]) + engineWithSpecificDiagnosticsLowered.emit(warnings) + XCTAssertEqual(DiagnosticConsoleWriter.formattedDescription(for: engineWithSpecificDiagnosticsLowered.problems, options: .formatConsoleOutputForTools), """ + error: Test diagnostic one + warning: Test diagnostic two + error: Test diagnostic three + """) + + let engineWithFilterAndSpecificDiagnosticsLowered = DiagnosticEngine(filterLevel: .error, treatWarningsAsErrors: true, diagnosticIDsWithWarningSeverity: ["Two", "Unknown"]) + engineWithFilterAndSpecificDiagnosticsLowered.emit(warnings) + XCTAssertEqual(DiagnosticConsoleWriter.formattedDescription(for: engineWithFilterAndSpecificDiagnosticsLowered.problems, options: .formatConsoleOutputForTools), """ + error: Test diagnostic one + error: Test diagnostic three + """) + } } diff --git a/Tests/SwiftDocCUtilitiesTests/ArgumentParsing/ConvertSubcommandTests.swift b/Tests/SwiftDocCUtilitiesTests/ArgumentParsing/ConvertSubcommandTests.swift index 41027fbba1..c971e318d4 100644 --- a/Tests/SwiftDocCUtilitiesTests/ArgumentParsing/ConvertSubcommandTests.swift +++ b/Tests/SwiftDocCUtilitiesTests/ArgumentParsing/ConvertSubcommandTests.swift @@ -596,6 +596,28 @@ class ConvertSubcommandTests: XCTestCase { let disabledFlagConvert = try Docc.Convert.parse(["--disable-mentioned-in"]) XCTAssertEqual(disabledFlagConvert.enableMentionedIn, false) } + + func testExplicitDiagnosticSeverities() throws { + // The feature is enabled when no flag is passed. + let noFlagConvert = try Docc.Convert.parse([]) + XCTAssertEqual(noFlagConvert.diagnosticIDsWithErrorSeverity, []) + XCTAssertEqual(noFlagConvert.diagnosticIDsWithWarningSeverity, []) + + // The flags can be used to specify explicit diagnostic severities + let explicitlySetSeverities = try Docc.Convert.parse(["--Wwarning", "First", "--Wwarning", "Second", "--Werror", "Third"]) + XCTAssertEqual(explicitlySetSeverities.diagnosticIDsWithErrorSeverity, ["Third"]) + XCTAssertEqual(explicitlySetSeverities.diagnosticIDsWithWarningSeverity, ["First", "Second"]) + + // It's allowed (but redundant) to repeat the same configuration + let repeatedSeverity = try Docc.Convert.parse(["--Wwarning", "Something", "--Wwarning", "Something"]) + XCTAssertEqual(repeatedSeverity.diagnosticIDsWithErrorSeverity, []) + XCTAssertEqual(repeatedSeverity.diagnosticIDsWithWarningSeverity, ["Something", "Something"], "The duplication doesn't matter. Later stages turn this into a Set") + + // Specifying both an explicit warning severity and error severity for the same diagnostic raises a warning. + let conflictingSeverity = try Docc.Convert.parse(["--Wwarning", "First", "--Werror", "First", "--Werror", "Second"]) + XCTAssertEqual(conflictingSeverity.diagnosticIDsWithErrorSeverity, ["Second"], "'First' is excluded from both lists") + XCTAssertEqual(conflictingSeverity.diagnosticIDsWithWarningSeverity, [], "'First' is excluded from both lists") + } // This test calls ``ConvertOptions.infoPlistFallbacks._unusedVersionForBackwardsCompatibility`` which is deprecated. // Deprecating the test silences the deprecation warning when running the tests. It doesn't skip the test. diff --git a/features.json b/features.json index 014b9bf65c..4fe1ee313e 100644 --- a/features.json +++ b/features.json @@ -12,6 +12,9 @@ { "name": "overloads" }, + { + "name": "precise-diagnostic-severity-control" + }, { "name": "synthesized-landing-page-name" } From 63b42021e43154458af2193e9aed1fbf881ae4d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20R=C3=B6nnqvist?= Date: Thu, 13 Nov 2025 16:46:40 +0100 Subject: [PATCH 2/2] Support severity control for groups of related diagnostics --- .../Diagnostics/Diagnostic.swift | 5 +++ .../Diagnostics/DiagnosticEngine.swift | 13 +++--- .../Diagnostics/DiagnosticEngineTests.swift | 45 +++++++++++++++++++ 3 files changed, 58 insertions(+), 5 deletions(-) diff --git a/Sources/SwiftDocC/Infrastructure/Diagnostics/Diagnostic.swift b/Sources/SwiftDocC/Infrastructure/Diagnostics/Diagnostic.swift index e769f1bf1b..a553bc22b5 100644 --- a/Sources/SwiftDocC/Infrastructure/Diagnostics/Diagnostic.swift +++ b/Sources/SwiftDocC/Infrastructure/Diagnostics/Diagnostic.swift @@ -29,6 +29,9 @@ public struct Diagnostic { /// /// `org.swift.docc.SummaryContainsLink` public var identifier: String + + /// A unique string that identifies a group of diagnostics whose severity can be controlled by passing `--Werror` and `--Wwarning` flags to `docc`. + public var groupIdentifier: String? /// A brief summary that describe the problem or issue. public var summary: String @@ -47,6 +50,7 @@ public struct Diagnostic { severity: DiagnosticSeverity, range: SourceRange? = nil, identifier: String, + groupIdentifier: String? = nil, summary: String, explanation: String? = nil, notes: [DiagnosticNote] = [] @@ -55,6 +59,7 @@ public struct Diagnostic { self.severity = severity self.range = range self.identifier = identifier + self.groupIdentifier = groupIdentifier self.summary = summary self.explanation = explanation self.notes = notes diff --git a/Sources/SwiftDocC/Infrastructure/Diagnostics/DiagnosticEngine.swift b/Sources/SwiftDocC/Infrastructure/Diagnostics/DiagnosticEngine.swift index 5cc65fb5fc..47660f4e5d 100644 --- a/Sources/SwiftDocC/Infrastructure/Diagnostics/DiagnosticEngine.swift +++ b/Sources/SwiftDocC/Infrastructure/Diagnostics/DiagnosticEngine.swift @@ -141,11 +141,14 @@ public final class DiagnosticEngine { } private func updateDiagnosticSeverity(_ diagnostic: inout Diagnostic) { - let identifier = diagnostic.identifier - if diagnosticIDsWithErrorSeverity.contains(identifier) { - diagnostic.severity = .error - } else if diagnosticIDsWithWarningSeverity.contains(identifier) { - diagnostic.severity = .warning + func _severity(identifier: String) -> DiagnosticSeverity? { + if diagnosticIDsWithErrorSeverity.contains(identifier) { .error } + else if diagnosticIDsWithWarningSeverity.contains(identifier) { .warning } + else { nil } + } + + if let severity = diagnostic.groupIdentifier.flatMap(_severity) ?? _severity(identifier: diagnostic.identifier) { + diagnostic.severity = severity } else if treatWarningsAsErrors, diagnostic.severity == .warning { diagnostic.severity = .error } diff --git a/Tests/SwiftDocCTests/Diagnostics/DiagnosticEngineTests.swift b/Tests/SwiftDocCTests/Diagnostics/DiagnosticEngineTests.swift index b067452a44..ac8cb93b48 100644 --- a/Tests/SwiftDocCTests/Diagnostics/DiagnosticEngineTests.swift +++ b/Tests/SwiftDocCTests/Diagnostics/DiagnosticEngineTests.swift @@ -223,4 +223,49 @@ class DiagnosticEngineTests: XCTestCase { error: Test diagnostic three """) } + + func testRaiseSeverityOfDiagnosticGroups() { + let letterWarnings = ["A", "B", "C"].map { id in + Problem(diagnostic: Diagnostic(source: nil, severity: .warning, range: nil, identifier: id, groupIdentifier: "Letter", summary: "Test diagnostic \(id)"), possibleSolutions: []) + } + let numberWarnings = ["1", "2", "3"].map { id in + Problem(diagnostic: Diagnostic(source: nil, severity: .warning, range: nil, identifier: id, groupIdentifier: "Number", summary: "Test diagnostic \(id)"), possibleSolutions: []) + } + + let engineWithRaisedLetterSeverity = DiagnosticEngine(diagnosticIDsWithErrorSeverity: ["Letter"]) + engineWithRaisedLetterSeverity.emit(letterWarnings) + engineWithRaisedLetterSeverity.emit(numberWarnings) + XCTAssertEqual(DiagnosticConsoleWriter.formattedDescription(for: engineWithRaisedLetterSeverity.problems, options: .formatConsoleOutputForTools), """ + error: Test diagnostic A + error: Test diagnostic B + error: Test diagnostic C + warning: Test diagnostic 1 + warning: Test diagnostic 2 + warning: Test diagnostic 3 + """) + + let engineWithRaisedNumberSeverity = DiagnosticEngine(diagnosticIDsWithErrorSeverity: ["Number"]) + engineWithRaisedNumberSeverity.emit(letterWarnings) + engineWithRaisedNumberSeverity.emit(numberWarnings) + XCTAssertEqual(DiagnosticConsoleWriter.formattedDescription(for: engineWithRaisedNumberSeverity.problems, options: .formatConsoleOutputForTools), """ + warning: Test diagnostic A + warning: Test diagnostic B + warning: Test diagnostic C + error: Test diagnostic 1 + error: Test diagnostic 2 + error: Test diagnostic 3 + """) + + let engineWithRaisedNumberSeverityAndOneLetter = DiagnosticEngine(diagnosticIDsWithErrorSeverity: ["Number", "B"]) + engineWithRaisedNumberSeverityAndOneLetter.emit(letterWarnings) + engineWithRaisedNumberSeverityAndOneLetter.emit(numberWarnings) + XCTAssertEqual(DiagnosticConsoleWriter.formattedDescription(for: engineWithRaisedNumberSeverityAndOneLetter.problems, options: .formatConsoleOutputForTools), """ + warning: Test diagnostic A + error: Test diagnostic B + warning: Test diagnostic C + error: Test diagnostic 1 + error: Test diagnostic 2 + error: Test diagnostic 3 + """) + } }