Skip to content

Commit 1b4a185

Browse files
optionalRequirementOf relationships take precedence over non-optional ones (#1252)
rdar://155967920
1 parent 8902856 commit 1b4a185

File tree

2 files changed

+39
-1
lines changed

2 files changed

+39
-1
lines changed

Sources/SwiftDocC/Infrastructure/Symbol Graph/SymbolGraphRelationshipsBuilder.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,13 @@ struct SymbolGraphRelationshipsBuilder {
346346
assertionFailure(AssertionMessages.sourceNotFound(edge))
347347
return
348348
}
349-
requiredSymbol.isRequired = required
349+
// If both requirementOf and optionalRequirementOf relationships exist
350+
// for the same symbol, let the optional relationship take precedence.
351+
// Optional protocol requirements sometimes appear with both relationships,
352+
// but non-optional requirements do not.
353+
if !required || requiredSymbol.isRequiredVariants.isEmpty {
354+
requiredSymbol.isRequired = required
355+
}
350356
}
351357

352358
/// Sets a node in the context as an inherited symbol.

Tests/SwiftDocCTests/Infrastructure/SymbolGraph/SymbolGraphRelationshipsBuilderTests.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,4 +186,36 @@ class SymbolGraphRelationshipsBuilderTests: XCTestCase {
186186
// Test default implementation was added
187187
XCTAssertFalse((documentationCache["A"]!.semantic as! Symbol).isRequired)
188188
}
189+
190+
func testRequiredAndOptionalRequirementRelationships() throws {
191+
do {
192+
let (bundle, _) = try testBundleAndContext()
193+
var documentationCache = DocumentationContext.ContentCache<DocumentationNode>()
194+
let engine = DiagnosticEngine()
195+
196+
let edge = createSymbols(documentationCache: &documentationCache, bundle: bundle, sourceType: .init(parsedIdentifier: .method, displayName: "Method"), targetType: .init(parsedIdentifier: .protocol, displayName: "Protocol"))
197+
198+
// Adding the "required" relationship before the "optional" one
199+
SymbolGraphRelationshipsBuilder.addRequirementRelationship(edge: edge, localCache: documentationCache, engine: engine)
200+
SymbolGraphRelationshipsBuilder.addOptionalRequirementRelationship(edge: edge, localCache: documentationCache, engine: engine)
201+
202+
// Make sure that the "optional" relationship wins
203+
XCTAssertFalse((documentationCache["A"]!.semantic as! Symbol).isRequired)
204+
}
205+
206+
do {
207+
let (bundle, _) = try testBundleAndContext()
208+
var documentationCache = DocumentationContext.ContentCache<DocumentationNode>()
209+
let engine = DiagnosticEngine()
210+
211+
let edge = createSymbols(documentationCache: &documentationCache, bundle: bundle, sourceType: .init(parsedIdentifier: .method, displayName: "Method"), targetType: .init(parsedIdentifier: .protocol, displayName: "Protocol"))
212+
213+
// Adding the "optional" relationship before the "required" one
214+
SymbolGraphRelationshipsBuilder.addOptionalRequirementRelationship(edge: edge, localCache: documentationCache, engine: engine)
215+
SymbolGraphRelationshipsBuilder.addRequirementRelationship(edge: edge, localCache: documentationCache, engine: engine)
216+
217+
// Make sure that the "optional" relationship still wins
218+
XCTAssertFalse((documentationCache["A"]!.semantic as! Symbol).isRequired)
219+
}
220+
}
189221
}

0 commit comments

Comments
 (0)