Skip to content

Commit 566a177

Browse files
authored
Add support for "warnings as errors" (#365)
1 parent be0a6bf commit 566a177

File tree

7 files changed

+163
-7
lines changed

7 files changed

+163
-7
lines changed

Sources/SwiftDocC/Infrastructure/Diagnostics/DiagnosticEngine.swift

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This source file is part of the Swift.org open source project
33

4-
Copyright (c) 2021 Apple Inc. and the Swift project authors
4+
Copyright (c) 2021-2022 Apple Inc. and the Swift project authors
55
Licensed under Apache License v2.0 with Runtime Library Exception
66

77
See https://swift.org/LICENSE.txt for license information
@@ -32,6 +32,9 @@ public final class DiagnosticEngine {
3232
self.filter = { $0.diagnostic.severity.rawValue <= self.filterLevel.rawValue }
3333
}
3434
}
35+
36+
/// Determines whether warnings will be treated as errors.
37+
private let treatWarningsAsErrors: Bool
3538

3639
/// Determines which problems should be emitted.
3740
private var filter: (Problem) -> Bool
@@ -42,8 +45,9 @@ public final class DiagnosticEngine {
4245
}
4346

4447
/// Creates a new diagnostic engine instance with no consumers.
45-
public init(filterLevel: DiagnosticSeverity = .warning) {
48+
public init(filterLevel: DiagnosticSeverity = .warning, treatWarningsAsErrors: Bool = false) {
4649
self.filterLevel = filterLevel
50+
self.treatWarningsAsErrors = treatWarningsAsErrors
4751
self.filter = { $0.diagnostic.severity.rawValue <= filterLevel.rawValue }
4852
}
4953

@@ -65,7 +69,14 @@ public final class DiagnosticEngine {
6569
/// - Parameter problems: The array of diagnostics to dispatch to this engine's currently subscribed consumers.
6670
/// > Note: Diagnostics are dispatched asynchronously.
6771
public func emit(_ problems: [Problem]) {
68-
let filteredProblems = problems.filter(filter)
72+
let mappedProblems = problems.map { problem -> Problem in
73+
var problem = problem
74+
if treatWarningsAsErrors, problem.diagnostic.severity == .warning {
75+
problem.diagnostic.severity = .error
76+
}
77+
return problem
78+
}
79+
let filteredProblems = mappedProblems.filter(filter)
6980
guard !filteredProblems.isEmpty else { return }
7081

7182
if filteredProblems.containsErrors {

Sources/SwiftDocCUtilities/Action/Actions/Convert/ConvertAction.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public struct ConvertAction: Action, RecreatingContext {
3333
let htmlTemplateDirectory: URL?
3434
let emitDigest: Bool
3535
let inheritDocs: Bool
36+
let treatWarningsAsErrors: Bool
3637
let experimentalEnableCustomTemplates: Bool
3738
let buildLMDBIndex: Bool
3839
let documentationCoverageOptions: DocumentationCoverageOptions
@@ -99,6 +100,7 @@ public struct ConvertAction: Action, RecreatingContext {
99100
diagnosticEngine: DiagnosticEngine? = nil,
100101
emitFixits: Bool = false,
101102
inheritDocs: Bool = false,
103+
treatWarningsAsErrors: Bool = false,
102104
experimentalEnableCustomTemplates: Bool = false,
103105
transformForStaticHosting: Bool = false,
104106
hostingBasePath: String? = nil,
@@ -135,10 +137,11 @@ public struct ConvertAction: Action, RecreatingContext {
135137
formattingOptions = []
136138
}
137139
self.inheritDocs = inheritDocs
140+
self.treatWarningsAsErrors = treatWarningsAsErrors
138141

139142
self.experimentalEnableCustomTemplates = experimentalEnableCustomTemplates
140143

141-
let engine = diagnosticEngine ?? DiagnosticEngine()
144+
let engine = diagnosticEngine ?? DiagnosticEngine(treatWarningsAsErrors: treatWarningsAsErrors)
142145
engine.filterLevel = filterLevel
143146
engine.add(DiagnosticConsoleWriter(formattingOptions: formattingOptions))
144147

Sources/SwiftDocCUtilities/ArgumentParsing/ActionExtensions/ConvertAction+CommandInitialization.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ extension ConvertAction {
7878
diagnosticLevel: convert.diagnosticLevel,
7979
emitFixits: convert.emitFixits,
8080
inheritDocs: convert.enableInheritedDocs,
81+
treatWarningsAsErrors: convert.warningsAsErrors,
8182
experimentalEnableCustomTemplates: convert.experimentalEnableCustomTemplates,
8283
transformForStaticHosting: convert.transformForStaticHosting,
8384
hostingBasePath: convert.hostingBasePath,

Sources/SwiftDocCUtilities/ArgumentParsing/Subcommands/Convert.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,10 @@ extension Docc {
138138
@Flag(help: "Inherit documentation for inherited symbols")
139139
public var enableInheritedDocs = false
140140

141+
142+
@Flag(help: "Treat warnings as errors")
143+
public var warningsAsErrors = false
144+
141145
/// Arguments for specifying information about the source code repository that hosts the documented project's code.
142146
@OptionGroup()
143147
public var sourceRepositoryArguments: SourceRepositoryArguments

Tests/SwiftDocCTests/Diagnostics/DiagnosticEngineTests.swift

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This source file is part of the Swift.org open source project
33

4-
Copyright (c) 2021 Apple Inc. and the Swift project authors
4+
Copyright (c) 2021-2022 Apple Inc. and the Swift project authors
55
Licensed under Apache License v2.0 with Runtime Library Exception
66

77
See https://swift.org/LICENSE.txt for license information
@@ -129,4 +129,38 @@ class DiagnosticEngineTests: XCTestCase {
129129
note: Test information
130130
""")
131131
}
132+
133+
func testTreatWarningsAsErrors() {
134+
let error = Problem(diagnostic: Diagnostic(source: nil, severity: .error, range: nil, identifier: "org.swift.docc.tests", summary: "Test error"), possibleSolutions: [])
135+
let warning = Problem(diagnostic: Diagnostic(source: nil, severity: .warning, range: nil, identifier: "org.swift.docc.tests", summary: "Test warning"), possibleSolutions: [])
136+
let information = Problem(diagnostic: Diagnostic(source: nil, severity: .information, range: nil, identifier: "org.swift.docc.tests", summary: "Test information"), possibleSolutions: [])
137+
138+
let defaultEngine = DiagnosticEngine()
139+
defaultEngine.emit(error)
140+
defaultEngine.emit(warning)
141+
defaultEngine.emit(information)
142+
XCTAssertEqual(defaultEngine.problems.localizedDescription, """
143+
error: Test error
144+
warning: Test warning
145+
""")
146+
147+
let engine = DiagnosticEngine(filterLevel: .information, treatWarningsAsErrors: true)
148+
engine.emit(error)
149+
engine.emit(warning)
150+
engine.emit(information)
151+
XCTAssertEqual(engine.problems.localizedDescription, """
152+
error: Test error
153+
error: Test warning
154+
note: Test information
155+
""")
156+
157+
let errorFilterLevelEngine = DiagnosticEngine(filterLevel: .error, treatWarningsAsErrors: true)
158+
errorFilterLevelEngine.emit(error)
159+
errorFilterLevelEngine.emit(warning)
160+
errorFilterLevelEngine.emit(information)
161+
XCTAssertEqual(errorFilterLevelEngine.problems.localizedDescription, """
162+
error: Test error
163+
error: Test warning
164+
""")
165+
}
132166
}

Tests/SwiftDocCUtilitiesTests/ArgumentParsing/ConvertSubcommandTests.swift

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This source file is part of the Swift.org open source project
33

4-
Copyright (c) 2021 Apple Inc. and the Swift project authors
4+
Copyright (c) 2021-2022 Apple Inc. and the Swift project authors
55
Licensed under Apache License v2.0 with Runtime Library Exception
66

77
See https://swift.org/LICENSE.txt for license information
@@ -437,4 +437,26 @@ class ConvertSubcommandTests: XCTestCase {
437437
XCTAssertFalse(convertOptions.transformForStaticHosting)
438438
}
439439
}
440+
441+
func testTreatWarningAsrror() throws {
442+
setenv(TemplateOption.environmentVariableKey, testTemplateURL.path, 1)
443+
do {
444+
// Passing no argument should default to the current working directory.
445+
let convert = try Docc.Convert.parse([])
446+
let convertAction = try ConvertAction(fromConvertCommand: convert)
447+
XCTAssertEqual(convertAction.treatWarningsAsErrors, false)
448+
} catch {
449+
XCTFail("Failed to run docc convert without arguments.")
450+
}
451+
do {
452+
// Passing no argument should default to the current working directory.
453+
let convert = try Docc.Convert.parse([
454+
"--warnings-as-errors"
455+
])
456+
let convertAction = try ConvertAction(fromConvertCommand: convert)
457+
XCTAssertEqual(convertAction.treatWarningsAsErrors, true)
458+
} catch {
459+
XCTFail("Failed to run docc convert without arguments.")
460+
}
461+
}
440462
}

Tests/SwiftDocCUtilitiesTests/ConvertActionTests.swift

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This source file is part of the Swift.org open source project
33

4-
Copyright (c) 2021 Apple Inc. and the Swift project authors
4+
Copyright (c) 2021-2022 Apple Inc. and the Swift project authors
55
Licensed under Apache License v2.0 with Runtime Library Exception
66

77
See https://swift.org/LICENSE.txt for license information
@@ -2557,6 +2557,87 @@ class ConvertActionTests: XCTestCase {
25572557
expectedOutput.assertExist(at: result.outputs[0], fileManager: FileManager.default)
25582558
}
25592559

2560+
func testTreatWarningsAsErrors() throws {
2561+
let bundle = Folder(name: "unit-test.docc", content: [
2562+
InfoPlist(displayName: "TestBundle", identifier: "com.test.example"),
2563+
CopyOfFile(original: symbolGraphFile, newName: "MyKit.symbols.json"),
2564+
TextFile(name: "Article.md", utf8Content: """
2565+
Bad title
2566+
2567+
This article has a malformed title and can't be analyzed, so it
2568+
produces one warning.
2569+
"""),
2570+
])
2571+
2572+
let testDataProvider = try TestFileSystem(folders: [bundle, Folder.emptyHTMLTemplateDirectory])
2573+
let targetDirectory = URL(fileURLWithPath: testDataProvider.currentDirectoryPath)
2574+
.appendingPathComponent("target", isDirectory: true)
2575+
2576+
// Test DiagnosticEngine with "treatWarningsAsErrors" set to false
2577+
do {
2578+
let engine = DiagnosticEngine(treatWarningsAsErrors: false)
2579+
var action = try ConvertAction(
2580+
documentationBundleURL: bundle.absoluteURL,
2581+
outOfProcessResolver: nil,
2582+
analyze: true,
2583+
targetDirectory: targetDirectory,
2584+
htmlTemplateDirectory: Folder.emptyHTMLTemplateDirectory.absoluteURL,
2585+
emitDigest: false,
2586+
currentPlatforms: nil,
2587+
dataProvider: testDataProvider,
2588+
fileManager: testDataProvider,
2589+
temporaryDirectory: createTemporaryDirectory(),
2590+
diagnosticEngine: engine
2591+
)
2592+
let result = try action.perform(logHandle: .none)
2593+
XCTAssertEqual(engine.problems.count, 1)
2594+
XCTAssertTrue(engine.problems.contains(where: { $0.diagnostic.severity == .warning }))
2595+
XCTAssertFalse(result.didEncounterError)
2596+
}
2597+
2598+
// Test DiagnosticEngine with "treatWarningsAsErrors" set to true
2599+
do {
2600+
let engine = DiagnosticEngine(treatWarningsAsErrors: true)
2601+
var action = try ConvertAction(
2602+
documentationBundleURL: bundle.absoluteURL,
2603+
outOfProcessResolver: nil,
2604+
analyze: true,
2605+
targetDirectory: targetDirectory,
2606+
htmlTemplateDirectory: Folder.emptyHTMLTemplateDirectory.absoluteURL,
2607+
emitDigest: false,
2608+
currentPlatforms: nil,
2609+
dataProvider: testDataProvider,
2610+
fileManager: testDataProvider,
2611+
temporaryDirectory: createTemporaryDirectory(),
2612+
diagnosticEngine: engine
2613+
)
2614+
let result = try action.perform(logHandle: .none)
2615+
XCTAssertEqual(engine.problems.count, 1)
2616+
XCTAssertTrue(result.didEncounterError)
2617+
}
2618+
2619+
do {
2620+
var action = try ConvertAction(
2621+
documentationBundleURL: bundle.absoluteURL,
2622+
outOfProcessResolver: nil,
2623+
analyze: true,
2624+
targetDirectory: targetDirectory,
2625+
htmlTemplateDirectory: Folder.emptyHTMLTemplateDirectory.absoluteURL,
2626+
emitDigest: false,
2627+
currentPlatforms: nil,
2628+
dataProvider: testDataProvider,
2629+
fileManager: testDataProvider,
2630+
temporaryDirectory: createTemporaryDirectory(),
2631+
diagnosticEngine: nil,
2632+
treatWarningsAsErrors: true
2633+
)
2634+
let result = try action.perform(logHandle: .none)
2635+
XCTAssertTrue(result.didEncounterError)
2636+
}
2637+
2638+
}
2639+
2640+
25602641
private func uniformlyPrintDiagnosticMessages(_ problems: [Problem]) -> String {
25612642
return problems.sorted(by: { (lhs, rhs) -> Bool in
25622643
guard lhs.diagnostic.identifier != rhs.diagnostic.identifier else {

0 commit comments

Comments
 (0)