@@ -4919,6 +4919,229 @@ let expected = """
49194919 " Unknown feature flag in Info.plist: 'ExperimenalOverloadedSymbolPresentation'. Possible suggestions: 'ExperimentalOverloadedSymbolPresentation' " )
49204920 }
49214921
4922+ func testContextGeneratesUnifiedOverloadGroupsAcrossPlatforms( ) throws {
4923+ enableFeatureFlag ( \. isExperimentalOverloadedSymbolPresentationEnabled)
4924+
4925+ let symbolKind = try XCTUnwrap ( SymbolGraph . Symbol. KindIdentifier. allCases. filter ( { $0. isOverloadableKind } ) . first)
4926+
4927+ let tempURL = try createTempFolder ( content: [
4928+ Folder ( name: " unit-test.docc " , content: [
4929+ JSONFile ( name: " ModuleName-macos.symbols.json " , content: makeSymbolGraph (
4930+ moduleName: " ModuleName " ,
4931+ platform: . init( operatingSystem: . init( name: " macosx " ) ) ,
4932+ symbols: [
4933+ makeSymbol ( identifier: " symbol-1 " , kind: symbolKind) ,
4934+ makeSymbol ( identifier: " symbol-2 " , kind: symbolKind) ,
4935+ ] ) ) ,
4936+ JSONFile ( name: " ModuleName-ios.symbols.json " , content: makeSymbolGraph (
4937+ moduleName: " ModuleName " ,
4938+ platform: . init( operatingSystem: . init( name: " ios " ) ) ,
4939+ symbols: [
4940+ makeSymbol ( identifier: " symbol-2 " , kind: symbolKind) ,
4941+ makeSymbol ( identifier: " symbol-3 " , kind: symbolKind) ,
4942+ ] ) ) ,
4943+ ] )
4944+ ] )
4945+ let ( _, bundle, context) = try loadBundle ( from: tempURL)
4946+ let moduleReference = ResolvedTopicReference ( bundleIdentifier: bundle. identifier, path: " /documentation/ModuleName " , sourceLanguage: . swift)
4947+
4948+ let overloadGroupNode : DocumentationNode
4949+ let overloadGroupSymbol : Symbol
4950+ let overloadGroupReferences : Symbol . Overloads
4951+
4952+ // There should only be one overload group for `SymbolName` - the one from the iOS symbol
4953+ // graph should have been removed during graph collection.
4954+ switch context. resolve ( . unresolved( . init( topicURL: . init( symbolPath: " SymbolName " ) ) ) , in: moduleReference, fromSymbolLink: true ) {
4955+ case let . failure( _, errorMessage) :
4956+ XCTFail ( " Could not resolve overload group page. Error message: \( errorMessage) " )
4957+ return
4958+ case let . success( overloadGroupReference) :
4959+ overloadGroupNode = try context. entity ( with: overloadGroupReference)
4960+ overloadGroupSymbol = try XCTUnwrap ( overloadGroupNode. semantic as? Symbol )
4961+ overloadGroupReferences = try XCTUnwrap ( overloadGroupSymbol. overloadsVariants. firstValue)
4962+
4963+ XCTAssertEqual ( overloadGroupReferences. displayIndex, 0 )
4964+
4965+ let unifiedSymbol = try XCTUnwrap ( overloadGroupNode. unifiedSymbol)
4966+ XCTAssertEqual ( unifiedSymbol. uniqueIdentifier, " symbol-1 " + SymbolGraph. Symbol. overloadGroupIdentifierSuffix)
4967+ }
4968+
4969+ let overloadedReferences = try [ " symbol-1 " , " symbol-2 " , " symbol-3 " ]
4970+ . map { try XCTUnwrap ( context. documentationCache. reference ( symbolID: $0) ) }
4971+
4972+ for (index, reference) in overloadedReferences. indexed ( ) {
4973+ let overloadedDocumentationNode = try XCTUnwrap ( context. documentationCache [ reference] )
4974+ let overloadedSymbol = try XCTUnwrap ( overloadedDocumentationNode. semantic as? Symbol )
4975+
4976+ let overloads = try XCTUnwrap ( overloadedSymbol. overloadsVariants. firstValue)
4977+
4978+ // Make sure that each symbol contains all of its sibling overloads.
4979+ XCTAssertEqual ( overloads. references. count, overloadedReferences. count - 1 )
4980+ for (otherIndex, otherReference) in overloadedReferences. indexed ( ) where otherIndex != index {
4981+ XCTAssert ( overloads. references. contains ( otherReference) )
4982+ }
4983+
4984+ if overloads. displayIndex == 0 {
4985+ // The first declaration in the display list should be the same declaration as
4986+ // the overload group page
4987+ XCTAssertEqual ( overloadedSymbol. declaration. first? . value. declarationFragments, overloadGroupSymbol. declaration. first? . value. declarationFragments)
4988+ } else {
4989+ // Otherwise, this reference should also be referenced by the overload group
4990+ XCTAssert ( overloadGroupReferences. references. contains ( reference) )
4991+ }
4992+ }
4993+ }
4994+
4995+ func testContextGeneratesOverloadGroupsWhenOnePlatformHasNoOverloads( ) throws {
4996+ enableFeatureFlag ( \. isExperimentalOverloadedSymbolPresentationEnabled)
4997+
4998+ let symbolKind = try XCTUnwrap ( SymbolGraph . Symbol. KindIdentifier. allCases. filter ( { $0. isOverloadableKind } ) . first)
4999+
5000+ // This situation used to crash. The macOS symbol graph only has one symbol in the overload
5001+ // group, whereas the iOS graph has two. Due to the way that Symbol loaded the overload
5002+ // mixins, `symbol-1` wouldn't save its overload data, which would trip an assertion in
5003+ // DocumentationContext. We need to ensure that an overload group is properly created, and
5004+ // that both symbols are correctly grouped underneath it.
5005+ let tempURL = try createTempFolder ( content: [
5006+ Folder ( name: " unit-test.docc " , content: [
5007+ JSONFile ( name: " ModuleName-macos.symbols.json " , content: makeSymbolGraph (
5008+ moduleName: " ModuleName " ,
5009+ platform: . init( operatingSystem: . init( name: " macosx " ) ) ,
5010+ symbols: [
5011+ makeSymbol ( identifier: " symbol-1 " , kind: symbolKind) ,
5012+ ] ) ) ,
5013+ JSONFile ( name: " ModuleName-ios.symbols.json " , content: makeSymbolGraph (
5014+ moduleName: " ModuleName " ,
5015+ platform: . init( operatingSystem: . init( name: " ios " ) ) ,
5016+ symbols: [
5017+ makeSymbol ( identifier: " symbol-1 " , kind: symbolKind) ,
5018+ makeSymbol ( identifier: " symbol-2 " , kind: symbolKind) ,
5019+ ] ) ) ,
5020+ ] )
5021+ ] )
5022+ let ( _, bundle, context) = try loadBundle ( from: tempURL)
5023+ let moduleReference = ResolvedTopicReference ( bundleIdentifier: bundle. identifier, path: " /documentation/ModuleName " , sourceLanguage: . swift)
5024+
5025+ let overloadGroupNode : DocumentationNode
5026+ let overloadGroupSymbol : Symbol
5027+ let overloadGroupReferences : Symbol . Overloads
5028+
5029+ // Even though the macOS symbol graph doesn't contain an overload group, one should still
5030+ // have been created from the iOS symbol graph, and that overload group should reference
5031+ // both symbols.
5032+ switch context. resolve ( . unresolved( . init( topicURL: . init( symbolPath: " SymbolName " ) ) ) , in: moduleReference, fromSymbolLink: true ) {
5033+ case let . failure( _, errorMessage) :
5034+ XCTFail ( " Could not resolve overload group page. Error message: \( errorMessage) " )
5035+ return
5036+ case let . success( overloadGroupReference) :
5037+ overloadGroupNode = try context. entity ( with: overloadGroupReference)
5038+ overloadGroupSymbol = try XCTUnwrap ( overloadGroupNode. semantic as? Symbol )
5039+ overloadGroupReferences = try XCTUnwrap ( overloadGroupSymbol. overloadsVariants. firstValue)
5040+
5041+ XCTAssertEqual ( overloadGroupReferences. displayIndex, 0 )
5042+
5043+ let unifiedSymbol = try XCTUnwrap ( overloadGroupNode. unifiedSymbol)
5044+ XCTAssertEqual ( unifiedSymbol. uniqueIdentifier, " symbol-1 " + SymbolGraph. Symbol. overloadGroupIdentifierSuffix)
5045+ }
5046+
5047+ let overloadedReferences = try [ " symbol-1 " , " symbol-2 " ]
5048+ . map { try XCTUnwrap ( context. documentationCache. reference ( symbolID: $0) ) }
5049+
5050+ for (index, reference) in overloadedReferences. indexed ( ) {
5051+ let overloadedDocumentationNode = try XCTUnwrap ( context. documentationCache [ reference] )
5052+ let overloadedSymbol = try XCTUnwrap ( overloadedDocumentationNode. semantic as? Symbol )
5053+
5054+ let overloads = try XCTUnwrap ( overloadedSymbol. overloadsVariants. firstValue)
5055+
5056+ // Make sure that each symbol contains all of its sibling overloads.
5057+ XCTAssertEqual ( overloads. references. count, overloadedReferences. count - 1 )
5058+ for (otherIndex, otherReference) in overloadedReferences. indexed ( ) where otherIndex != index {
5059+ XCTAssert ( overloads. references. contains ( otherReference) )
5060+ }
5061+
5062+ if overloads. displayIndex == 0 {
5063+ // The first declaration in the display list should be the same declaration as
5064+ // the overload group page
5065+ XCTAssertEqual ( overloadedSymbol. declaration. first? . value. declarationFragments, overloadGroupSymbol. declaration. first? . value. declarationFragments)
5066+ } else {
5067+ // Otherwise, this reference should also be referenced by the overload group
5068+ XCTAssert ( overloadGroupReferences. references. contains ( reference) )
5069+ }
5070+ }
5071+ }
5072+
5073+ /// Ensure that overload groups are correctly loaded into the path hierarchy and create nodes,
5074+ /// even when they came from an extension symbol graph.
5075+ func testContextGeneratesOverloadGroupsForExtensionGraphOverloads( ) throws {
5076+ enableFeatureFlag ( \. isExperimentalOverloadedSymbolPresentationEnabled)
5077+
5078+ let symbolKind = try XCTUnwrap ( SymbolGraph . Symbol. KindIdentifier. allCases. filter ( { $0. isOverloadableKind } ) . first)
5079+
5080+ let tempURL = try createTempFolder ( content: [
5081+ Folder ( name: " unit-test.docc " , content: [
5082+ JSONFile ( name: " ModuleName.symbols.json " , content: makeSymbolGraph (
5083+ moduleName: " ModuleName " ,
5084+ platform: . init( operatingSystem: . init( name: " macosx " ) ) ,
5085+ symbols: [
5086+ makeSymbol ( name: " RegularSymbol " , identifier: " RegularSymbol " , kind: . class) ,
5087+ ] ) ) ,
5088+ JSONFile ( name
: " [email protected] " , content
: makeSymbolGraph ( 5089+ moduleName: " OtherModule " ,
5090+ platform: . init( operatingSystem: . init( name: " macosx " ) ) ,
5091+ symbols: [
5092+ makeSymbol ( identifier: " symbol-1 " , kind: symbolKind) ,
5093+ makeSymbol ( identifier: " symbol-2 " , kind: symbolKind) ,
5094+ ] ) ) ,
5095+ ] )
5096+ ] )
5097+ let ( _, bundle, context) = try loadBundle ( from: tempURL)
5098+ let moduleReference = ResolvedTopicReference ( bundleIdentifier: bundle. identifier, path: " /documentation/ModuleName " , sourceLanguage: . swift)
5099+
5100+ let overloadGroupNode : DocumentationNode
5101+ let overloadGroupSymbol : Symbol
5102+ let overloadGroupReferences : Symbol . Overloads
5103+
5104+ switch context. resolve ( . unresolved( . init( topicURL: . init( symbolPath: " SymbolName " ) ) ) , in: moduleReference, fromSymbolLink: true ) {
5105+ case let . failure( _, errorMessage) :
5106+ XCTFail ( " Could not resolve overload group page. Error message: \( errorMessage) " )
5107+ return
5108+ case let . success( overloadGroupReference) :
5109+ overloadGroupNode = try context. entity ( with: overloadGroupReference)
5110+ overloadGroupSymbol = try XCTUnwrap ( overloadGroupNode. semantic as? Symbol )
5111+ overloadGroupReferences = try XCTUnwrap ( overloadGroupSymbol. overloadsVariants. firstValue)
5112+
5113+ XCTAssertEqual ( overloadGroupReferences. displayIndex, 0 )
5114+
5115+ let unifiedSymbol = try XCTUnwrap ( overloadGroupNode. unifiedSymbol)
5116+ XCTAssertEqual ( unifiedSymbol. uniqueIdentifier, " symbol-1 " + SymbolGraph. Symbol. overloadGroupIdentifierSuffix)
5117+ }
5118+
5119+ let overloadedReferences = try [ " symbol-1 " , " symbol-2 " ]
5120+ . map { try XCTUnwrap ( context. documentationCache. reference ( symbolID: $0) ) }
5121+
5122+ for (index, reference) in overloadedReferences. indexed ( ) {
5123+ let overloadedDocumentationNode = try XCTUnwrap ( context. documentationCache [ reference] )
5124+ let overloadedSymbol = try XCTUnwrap ( overloadedDocumentationNode. semantic as? Symbol )
5125+
5126+ let overloads = try XCTUnwrap ( overloadedSymbol. overloadsVariants. firstValue)
5127+
5128+ // Make sure that each symbol contains all of its sibling overloads.
5129+ XCTAssertEqual ( overloads. references. count, overloadedReferences. count - 1 )
5130+ for (otherIndex, otherReference) in overloadedReferences. indexed ( ) where otherIndex != index {
5131+ XCTAssert ( overloads. references. contains ( otherReference) )
5132+ }
5133+
5134+ if overloads. displayIndex == 0 {
5135+ // The first declaration in the display list should be the same declaration as
5136+ // the overload group page
5137+ XCTAssertEqual ( overloadedSymbol. declaration. first? . value. declarationFragments, overloadGroupSymbol. declaration. first? . value. declarationFragments)
5138+ } else {
5139+ // Otherwise, this reference should also be referenced by the overload group
5140+ XCTAssert ( overloadGroupReferences. references. contains ( reference) )
5141+ }
5142+ }
5143+ }
5144+
49225145 // A test helper that creates a symbol with a given identifier and kind.
49235146 private func makeSymbol(
49245147 name: String = " SymbolName " ,
0 commit comments