Skip to content

Commit b98a915

Browse files
No pages warning (#664)
* Warning when build resulted in no pages (#553) * Emit warning when converting/previewing article-only catalogs with no technology root * Emit diagnostics originated in the Convert Action `perform` process rdar://112462434 * Emit coverage if no errors occurred * Changed diagnostic message * DiagnosticConsumer finalize() deprecated * Marked `finalized()` as deprecated * Protocol extension for default implementation of `flush()` * Message for article-only warning `needs` noun corrected rdar://108011754
1 parent 638779e commit b98a915

File tree

6 files changed

+87
-16
lines changed

6 files changed

+87
-16
lines changed

Sources/SwiftDocC/Infrastructure/Diagnostics/DiagnosticConsumer.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,23 @@ public protocol DiagnosticConsumer: AnyObject {
1717
func receive(_ problems: [Problem])
1818

1919
/// Inform the consumer that the engine has sent all diagnostics for this build.
20+
@available(*, deprecated, renamed: "flush()")
2021
func finalize() throws
22+
23+
/// Inform the consumer that the engine has sent all diagnostics in a given context.
24+
func flush() throws
2125
}
2226

2327
/// A type that can format received diagnostics in way that's suitable for writing to a destination such as a file or `TextOutputStream`.
2428
public protocol DiagnosticFormattingConsumer: DiagnosticConsumer {
2529
/// Options for how problems should be formatted if written to output.
2630
var formattingOptions: DiagnosticFormattingOptions { get set }
2731
}
32+
33+
public extension DiagnosticConsumer {
34+
// Deprecated for supressing the warning emitted when calling `finalize()`
35+
@available(*, deprecated)
36+
func flush() throws {
37+
try finalize()
38+
}
39+
}

Sources/SwiftDocC/Infrastructure/Diagnostics/DiagnosticEngine.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,15 @@ public final class DiagnosticEngine {
9696
}
9797
}
9898

99+
@available(*, deprecated, renamed: "flush()")
99100
public func finalize() {
101+
flush()
102+
}
103+
104+
public func flush() {
100105
workQueue.sync {
101106
for consumer in self.consumers.sync({ $0.values }) {
102-
try? consumer.finalize()
107+
try? consumer.flush()
103108
}
104109
}
105110
}

Sources/SwiftDocC/Infrastructure/DocumentationConverter.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ public struct DocumentationConverter: DocumentationConverterProtocol {
178178
outputConsumer: OutputConsumer
179179
) throws -> (analysisProblems: [Problem], conversionProblems: [Problem]) {
180180
defer {
181-
diagnosticEngine.finalize()
181+
diagnosticEngine.flush()
182182
}
183183

184184
// Unregister the current file data provider and all its bundles

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

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,10 @@ public struct ConvertAction: Action, RecreatingContext {
357357
/// Converts each eligible file from the source documentation bundle,
358358
/// saves the results in the given output alongside the template files.
359359
mutating public func perform(logHandle: LogHandle) throws -> ActionResult {
360+
361+
// The converter has already emitted its problems to the diagnostic engine.
362+
// Track additional problems separately to avoid repeating the converter's problems.
363+
var postConversionProblems: [Problem] = []
360364
let totalTimeMetric = benchmark(begin: Benchmark.Duration(id: "convert-total-time"))
361365

362366
// While running this method keep the `isPerforming` flag up.
@@ -365,6 +369,7 @@ public struct ConvertAction: Action, RecreatingContext {
365369
defer {
366370
didPerformFuture?()
367371
isPerforming.sync({ $0 = false })
372+
diagnosticEngine.flush()
368373
}
369374

370375
if let outOfProcessResolver = outOfProcessResolver {
@@ -451,11 +456,18 @@ public struct ConvertAction: Action, RecreatingContext {
451456
throw error
452457
}
453458

454-
var allProblems = analysisProblems + conversionProblems
455-
456-
if allProblems.containsErrors == false {
457-
let coverageResults = try coverageAction.perform(logHandle: logHandle)
458-
allProblems.append(contentsOf: coverageResults.problems)
459+
var didEncounterError = analysisProblems.containsErrors || conversionProblems.containsErrors
460+
if try context.renderRootModules.isEmpty {
461+
postConversionProblems.append(
462+
Problem(
463+
diagnostic: Diagnostic(
464+
severity: .warning,
465+
identifier: "org.swift.docc.MissingTechnologyRoot",
466+
summary: "No TechnologyRoot to organize article-only documentation.",
467+
explanation: "Article-only documentation needs a TechnologyRoot page (indicated by a `TechnologyRoot` directive within a `Metadata` directive) to define the root of the documentation hierarchy."
468+
)
469+
)
470+
)
459471
}
460472

461473
// If we're building a navigation index, finalize the process and collect encountered problems.
@@ -468,17 +480,27 @@ public struct ConvertAction: Action, RecreatingContext {
468480
// Always emit a JSON representation of the index but only emit the LMDB
469481
// index if the user has explicitly opted in with the `--emit-lmdb-index` flag.
470482
let indexerProblems = indexer.finalize(emitJSON: true, emitLMDB: buildLMDBIndex)
471-
allProblems.append(contentsOf: indexerProblems)
483+
postConversionProblems.append(contentsOf: indexerProblems)
472484
}
485+
486+
// Output to the user the problems encountered during the convert process
487+
diagnosticEngine.emit(postConversionProblems)
473488

474489
// Stop the "total time" metric here. The moveOutput time isn't very interesting to include in the benchmark.
475490
// New tasks and computations should be added above this line so that they're included in the benchmark.
476491
benchmark(end: totalTimeMetric)
477492

493+
if !didEncounterError {
494+
let coverageResults = try coverageAction.perform(logHandle: logHandle)
495+
postConversionProblems.append(contentsOf: coverageResults.problems)
496+
}
497+
498+
didEncounterError = didEncounterError || postConversionProblems.containsErrors
499+
478500
// We should generally only replace the current build output if we didn't encounter errors
479501
// during conversion. However, if the `emitDigest` flag is true,
480502
// we should replace the current output with our digest of problems.
481-
if !allProblems.containsErrors || emitDigest {
503+
if !didEncounterError || emitDigest {
482504
try moveOutput(from: temporaryFolder, to: targetDirectory)
483505
}
484506

@@ -516,12 +538,7 @@ public struct ConvertAction: Action, RecreatingContext {
516538
try outputConsumer.consume(benchmarks: Benchmark.main)
517539
}
518540

519-
// If the user didn't provide the `analyze` flag, filter based on diagnostic level
520-
if !analyze {
521-
allProblems.removeAll(where: { $0.diagnostic.severity.rawValue > diagnosticLevel.rawValue })
522-
}
523-
524-
return ActionResult(didEncounterError: allProblems.containsErrors, outputs: [targetDirectory])
541+
return ActionResult(didEncounterError: didEncounterError, outputs: [targetDirectory])
525542
}
526543

527544
func createTempFolder(with templateURL: URL?) throws -> URL {

Sources/SwiftDocCUtilities/Action/Actions/PreviewAction.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,11 +264,19 @@ extension PreviewAction {
264264
#endif
265265

266266
extension DocumentationContext {
267+
268+
/// A collection of non-implicit root modules
269+
var renderRootModules: [ResolvedTopicReference] {
270+
get throws {
271+
try rootModules.filter({ try !entity(with: $0).isVirtual })
272+
}
273+
}
274+
267275
/// Finds the module and technology pages in the context and returns their paths.
268276
func previewPaths() throws -> [String] {
269277
let urlGenerator = PresentationURLGenerator(context: self, baseURL: URL(string: "/")!)
270278

271-
let rootModules = try rootModules.filter { try !entity(with: $0).isVirtual }
279+
let rootModules = try renderRootModules
272280

273281
return (rootModules + rootTechnologies).map { page in
274282
urlGenerator.presentationURLForReference(page).absoluteString

Tests/SwiftDocCUtilitiesTests/ConvertActionTests.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3026,6 +3026,35 @@ class ConvertActionTests: XCTestCase {
30263026
}) .map { DiagnosticConsoleWriter.formattedDescription(for: $0.diagnostic) }.sorted().joined(separator: "\n")
30273027
}
30283028

3029+
// Tests that when converting a catalog with no technology root a warning is raised (r93371988)
3030+
func testConvertWithNoTechnologyRoot() throws {
3031+
let bundle = Folder(name: "unit-test.docc", content: [
3032+
InfoPlist(displayName: "TestBundle", identifier: "com.test.example"),
3033+
TextFile(name: "Documentation.md", utf8Content: "")
3034+
])
3035+
let testDataProvider = try TestFileSystem(folders: [bundle, Folder.emptyHTMLTemplateDirectory])
3036+
let targetDirectory = URL(fileURLWithPath: testDataProvider.currentDirectoryPath)
3037+
.appendingPathComponent("target", isDirectory: true)
3038+
let engine = DiagnosticEngine()
3039+
var action = try ConvertAction(
3040+
documentationBundleURL: bundle.absoluteURL,
3041+
outOfProcessResolver: nil,
3042+
analyze: true,
3043+
targetDirectory: targetDirectory,
3044+
htmlTemplateDirectory: Folder.emptyHTMLTemplateDirectory.absoluteURL,
3045+
emitDigest: false,
3046+
currentPlatforms: nil,
3047+
dataProvider: testDataProvider,
3048+
fileManager: testDataProvider,
3049+
temporaryDirectory: createTemporaryDirectory(),
3050+
diagnosticEngine: engine
3051+
)
3052+
let _ = try action.perform(logHandle: .standardOutput)
3053+
XCTAssertEqual(engine.problems.count, 1)
3054+
XCTAssertEqual(engine.problems.map { $0.diagnostic.identifier }, ["org.swift.docc.MissingTechnologyRoot"])
3055+
XCTAssert(engine.problems.contains(where: { $0.diagnostic.severity == .warning }))
3056+
}
3057+
30293058
#endif
30303059
}
30313060

0 commit comments

Comments
 (0)