Skip to content

Commit 2be46d9

Browse files
authored
Support documenting return values for Swift initializers (#1015)
rdar://134552183
1 parent 3eca64f commit 2be46d9

File tree

2 files changed

+56
-7
lines changed

2 files changed

+56
-7
lines changed

Sources/SwiftDocC/Model/ParametersAndReturnValidator.swift

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,16 @@ struct ParametersAndReturnValidator {
8484
// If all source languages have empty parameter sections, return `nil` instead of individually empty sections.
8585
parameterVariants = DocumentationDataVariants(defaultVariantValue: nil)
8686
}
87-
var returnVariants = makeReturnsSectionVariants(returns?.first, signatures, symbol.documentedSymbol?.kind, hasDocumentedParameters: parameters != nil)
87+
88+
var returnVariants = makeReturnsSectionVariants(
89+
returns?.first,
90+
signatures,
91+
documentedSymbolKind: symbol.documentedSymbol?.kind,
92+
swiftSymbolKind: symbol.kind.mapFirst { selector, kind in
93+
SourceLanguage(knownLanguageIdentifier: selector.interfaceLanguage) == .swift ? kind.identifier : nil
94+
},
95+
hasDocumentedParameters: parameters != nil
96+
)
8897
if returnVariants.allValues.allSatisfy({ _, section in section.content.isEmpty }) {
8998
// If all source languages have empty return value sections, return `nil` instead of individually empty sections.
9099
returnVariants = DocumentationDataVariants(defaultVariantValue: nil)
@@ -209,25 +218,34 @@ struct ParametersAndReturnValidator {
209218
/// - Parameters:
210219
/// - returns: The symbol's documented return values.
211220
/// - signatures: The symbol's containing only the values that exist in that language representation's function signature.
212-
/// - symbolKind: The documented symbol's kind, for use in diagnostics.
221+
/// - documentedSymbolKind: The documented symbol's kind, for use in diagnostics.
222+
/// - swiftSymbolKind: The symbol's Swift representation's kind, or `nil` if the symbol doesn't have a Swift representation.
213223
/// - hasDocumentedParameters: `true` if the symbol has documented any of its parameters; otherwise `false`.
214224
/// - Returns: A returns section variant containing only the return values that exist in each language representation's function signature.
215225
private func makeReturnsSectionVariants(
216226
_ returns: Return?,
217227
_ signatures: Signatures,
218-
_ symbolKind: SymbolGraph.Symbol.Kind?,
228+
documentedSymbolKind: SymbolGraph.Symbol.Kind?,
229+
swiftSymbolKind: SymbolGraph.Symbol.KindIdentifier?,
219230
hasDocumentedParameters: Bool
220231
) -> DocumentationDataVariants<ReturnsSection> {
221232
let returnsSection = returns.map { ReturnsSection(content: $0.contents) }
222233
var variants = DocumentationDataVariants<ReturnsSection>()
223234

224235
var traitsWithNonVoidReturnValues = Set(signatures.keys)
225236
for (trait, signature) in signatures {
237+
let language = trait.interfaceLanguage.flatMap(SourceLanguage.init(knownLanguageIdentifier:))
238+
239+
// The function signature for Swift initializers indicate a Void return type.
240+
// However, initializers have a _conceptual_ return value that's sometimes worth documenting (rdar://131913065).
241+
if language == .swift, swiftSymbolKind == .`init` {
242+
variants[trait] = returnsSection
243+
continue
244+
}
245+
226246
/// A Boolean value that indicates whether the current signature returns a known "void" value.
227247
var returnsKnownVoidValue: Bool {
228-
guard let language = trait.interfaceLanguage.flatMap(SourceLanguage.init(knownLanguageIdentifier:)),
229-
let voidReturnValues = Self.knownVoidReturnValuesByLanguage[language]
230-
else {
248+
guard let language, let voidReturnValues = Self.knownVoidReturnValuesByLanguage[language] else {
231249
return false
232250
}
233251
return signature.returns.allSatisfy { voidReturnValues.contains($0) }
@@ -251,7 +269,7 @@ struct ParametersAndReturnValidator {
251269

252270
// Diagnose if the symbol had documented its return values but all language representations only return void.
253271
if let returns, traitsWithNonVoidReturnValues.isEmpty {
254-
diagnosticEngine.emit(makeReturnsDocumentedForVoidProblem(returns, symbolKind: symbolKind))
272+
diagnosticEngine.emit(makeReturnsDocumentedForVoidProblem(returns, symbolKind: documentedSymbolKind))
255273
}
256274
return variants
257275
}

Tests/SwiftDocCTests/Model/ParametersAndReturnValidatorTests.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,37 @@ class ParametersAndReturnValidatorTests: XCTestCase {
229229
XCTAssertEqual(objcReturnsContent, "`YES` if the specified circle is empty; otherwise, `NO`.")
230230
}
231231

232+
func testCanDocumentInitializerReturnValue() throws {
233+
let (_, _, context) = try testBundleAndContext(copying: "GeometricalShapes") { url in
234+
try """
235+
# ``Circle/init(center:radius:)``
236+
237+
@Metadata {
238+
@DocumentationExtension(mergeBehavior: override)
239+
}
240+
241+
Override the documentation with a return section that raise warnings.
242+
243+
- Returns: Return value documentation for an initializer.
244+
""".write(to: url.appendingPathComponent("init-extension.md"), atomically: true, encoding: .utf8)
245+
}
246+
XCTAssertEqual(context.problems.map(\.diagnostic.summary), [])
247+
248+
let reference = try XCTUnwrap(context.soleRootModuleReference).appendingPath("Circle/init(center:radius:)")
249+
let node = try context.entity(with: reference)
250+
251+
// Verify that this symbol doesn't have a return value in its signature
252+
XCTAssertEqual(node.symbol?.functionSignature?.returns, [])
253+
254+
let symbolSemantic = try XCTUnwrap(node.semantic as? Symbol)
255+
let swiftReturnsSection = try XCTUnwrap(
256+
symbolSemantic.returnsSectionVariants.allValues.first(where: { trait, _ in trait.interfaceLanguage == "swift" })
257+
).variant
258+
XCTAssertEqual(swiftReturnsSection.content.map { $0.format() }, [
259+
"Return value documentation for an initializer."
260+
])
261+
}
262+
232263
func testNoParameterDiagnosticWithoutFunctionSignature() throws {
233264
var symbolGraph = makeSymbolGraph(docComment: """
234265
Some function description

0 commit comments

Comments
 (0)