@@ -4919,6 +4919,229 @@ let expected = """
4919
4919
" Unknown feature flag in Info.plist: 'ExperimenalOverloadedSymbolPresentation'. Possible suggestions: 'ExperimentalOverloadedSymbolPresentation' " )
4920
4920
}
4921
4921
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
+
4922
5145
// A test helper that creates a symbol with a given identifier and kind.
4923
5146
private func makeSymbol(
4924
5147
name: String = " SymbolName " ,
0 commit comments