Skip to content

Commit f1543f0

Browse files
Use SymbolKit's "unified symbol graph" representation when loading symbol graphs (#30)
* use parsed symbol kinds * use unified symbol graphs when loading symbols rdar://69835303 * Add experimental Objective-C feature flag Resolves rdar://84269048. Co-authored-by: Ethan Kusters <[email protected]>
1 parent 2f4391a commit f1543f0

File tree

37 files changed

+4274
-232
lines changed

37 files changed

+4274
-232
lines changed

Package.resolved

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Sources/SwiftDocC/DocumentationService/Convert/ConvertService.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ public struct ConvertService: DocumentationService {
115115
messageIdentifier: String
116116
) -> Result<([RenderNode], RenderReferenceStore?), ConvertServiceError> {
117117
Result {
118+
// Update DocC's current feature flags based on the ones provided
119+
// in the request.
120+
FeatureFlags.current = request.featureFlags
121+
118122
// Set up the documentation context.
119123

120124
let workspace = DocumentationWorkspace()

Sources/SwiftDocC/DocumentationService/Models/Services/Convert/ConvertRequest.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ public struct ConvertRequest: Codable {
1919
/// - ``DocumentationBundle/Info-swift.struct``
2020
public var bundleInfo: DocumentationBundle.Info
2121

22+
/// Feature flags to enable when performing this convert request.
23+
public var featureFlags: FeatureFlags
24+
2225
/// The external IDs of the symbols to convert.
2326
///
2427
/// Use this property to indicate what symbol documentation nodes should be converted. When ``externalIDsToConvert``
@@ -151,6 +154,7 @@ public struct ConvertRequest: Codable {
151154
self.knownDisambiguatedSymbolPathComponents = knownDisambiguatedSymbolPathComponents
152155
self.markupFiles = markupFiles
153156
self.miscResourceURLs = miscResourceURLs
157+
self.featureFlags = FeatureFlags()
154158

155159
self.bundleInfo = DocumentationBundle.Info(
156160
displayName: displayName,
@@ -174,6 +178,7 @@ public struct ConvertRequest: Codable {
174178
/// - miscResourceURLs: The on-disk resources in the documentation bundle to convert.
175179
public init(
176180
bundleInfo: DocumentationBundle.Info,
181+
featureFlags: FeatureFlags = FeatureFlags(),
177182
externalIDsToConvert: [String]?,
178183
documentPathsToConvert: [String]? = nil,
179184
includeRenderReferenceStore: Bool? = nil,
@@ -192,5 +197,6 @@ public struct ConvertRequest: Codable {
192197
self.markupFiles = markupFiles
193198
self.miscResourceURLs = miscResourceURLs
194199
self.bundleInfo = bundleInfo
200+
self.featureFlags = featureFlags
195201
}
196202
}

Sources/SwiftDocC/Infrastructure/DocumentationContext.swift

Lines changed: 118 additions & 75 deletions
Large diffs are not rendered by default.

Sources/SwiftDocC/Infrastructure/External Data/ExternalSymbolResolver+SymbolKind.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ extension ExternalSymbolResolver {
2525
/// everything should work as expected in practice — covering the exceptions with the known values and having "any symbol"
2626
/// value for the rest.
2727
static func symbolKind(forNodeKind kind: DocumentationNode.Kind) -> SymbolGraph.Symbol.Kind {
28-
let symbolKind: SymbolGraph.Symbol.Kind.Swift
28+
let symbolKind: SymbolGraph.Symbol.KindIdentifier
2929

3030
switch kind {
3131
case .associatedType:
@@ -75,6 +75,6 @@ extension ExternalSymbolResolver {
7575
symbolKind = .class
7676
}
7777

78-
return .init(identifier: symbolKind.rawValue, displayName: kind.name)
78+
return .init(parsedIdentifier: symbolKind, displayName: kind.name)
7979
}
8080
}

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import SymbolKit
1717
/// which makes detecting symbol collisions and overloads easier.
1818
struct SymbolGraphLoader {
1919
private(set) var symbolGraphs: [URL: SymbolKit.SymbolGraph] = [:]
20+
private(set) var unifiedGraphs: [String: SymbolKit.UnifiedSymbolGraph] = [:]
21+
private(set) var graphLocations: [String: [SymbolKit.GraphCollector.GraphKind]] = [:]
2022
private var dataProvider: DocumentationContextDataProvider
2123
private var bundle: DocumentationBundle
2224

@@ -48,6 +50,7 @@ struct SymbolGraphLoader {
4850
let decoder = JSONDecoder()
4951

5052
var loadedGraphs = [URL: SymbolKit.SymbolGraph]()
53+
let graphLoader = GraphCollector()
5154
var loadError: Error?
5255
let bundle = self.bundle
5356
let dataProvider = self.dataProvider
@@ -68,14 +71,25 @@ struct SymbolGraphLoader {
6871
case .concurrentlyEachFileInBatches:
6972
symbolGraph = try SymbolGraphConcurrentDecoder.decode(data)
7073
}
74+
75+
if let firstSymbolLanguage = symbolGraph.symbols.first?.value.identifier.interfaceLanguage {
76+
guard FeatureFlags.current.isExperimentalObjectiveCSupportEnabled
77+
|| InterfaceLanguage.from(string: firstSymbolLanguage) == .swift
78+
else {
79+
return
80+
}
81+
}
7182

7283
// `moduleNameFor(_:at:)` is static because it's pure function.
7384
let (moduleName, _) = Self.moduleNameFor(symbolGraph, at: symbolGraphURL)
7485
// If the bundle provides availability defaults add symbol availability data.
7586
self.addDefaultAvailability(to: &symbolGraph, moduleName: moduleName)
7687

7788
// Store the decoded graph in `loadedGraphs`
78-
loadingLock.sync { loadedGraphs[symbolGraphURL] = symbolGraph }
89+
loadingLock.sync {
90+
loadedGraphs[symbolGraphURL] = symbolGraph
91+
graphLoader.mergeSymbolGraph(symbolGraph, at: symbolGraphURL)
92+
}
7993
} catch {
8094
// If the symbol graph was invalid, store the error
8195
loadingLock.sync { loadError = error }
@@ -110,6 +124,7 @@ struct SymbolGraphLoader {
110124
}
111125

112126
self.symbolGraphs = loadedGraphs
127+
(self.unifiedGraphs, self.graphLocations) = graphLoader.finishLoading()
113128
}
114129

115130
// Alias to declutter code

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,14 +142,14 @@ struct SymbolGraphRelationshipsBuilder {
142142
let relationshipConstraints = edge.mixins[SymbolGraph.Relationship.Swift.GenericConstraints.mixinKey] as? SymbolGraph.Relationship.Swift.GenericConstraints
143143

144144
// Add relationships depending whether it's class inheritance or protocol conformance
145-
if conformingSymbol.kind.identifier == SymbolGraph.Symbol.Kind.Swift.protocol.rawValue {
145+
if conformingSymbol.kind.identifier == .protocol {
146146
conformingSymbol.relationships.addRelationship(.inheritsFrom(conformanceNodeReference))
147147
} else {
148148
conformingSymbol.relationships.addRelationship(.conformsTo(conformanceNodeReference, relationshipConstraints?.constraints))
149149
}
150150

151151
if let conformanceSymbol = optionalConformanceNode?.semantic as? Symbol {
152-
if let rawSymbol = conformingNode.symbol, rawSymbol.kind.identifier == SymbolGraph.Symbol.Kind.Swift.protocol.rawValue {
152+
if let rawSymbol = conformingNode.symbol, rawSymbol.kind.identifier == .protocol {
153153
conformanceSymbol.relationships.addRelationship(.inheritedBy(.successfullyResolved(conformingNode.reference)))
154154
} else {
155155
conformanceSymbol.relationships.addRelationship(.conformingType(.successfullyResolved(conformingNode.reference), relationshipConstraints?.constraints))

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

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,7 @@ extension String {
2222
public struct SymbolReference {
2323
/// Returns `true` if the symbol is a known graph leaf symbol.
2424
static func isLeaf(_ symbol: SymbolGraph.Symbol) -> Bool {
25-
guard let swiftKind = SymbolGraph.Symbol.Kind.Swift(rawValue: symbol.kind.identifier) else {
26-
return true
27-
}
28-
29-
return !swiftKind.symbolCouldHaveChildren
25+
return !symbol.kind.identifier.swiftSymbolCouldHaveChildren
3026
}
3127

3228
/// Creates a new reference to a symbol.
@@ -56,15 +52,15 @@ public struct SymbolReference {
5652
}
5753

5854
// A module reference does not have path as it's a root symbol in the topic graph.
59-
if symbol.kind.identifier == SymbolGraph.Symbol.Kind.Swift.module.rawValue {
55+
if symbol.kind.identifier == SymbolGraph.Symbol.KindIdentifier.module {
6056
path = ""
6157
return
6258
}
6359

6460
var name = symbol.pathComponents.joinedSymbolPathComponents
6561

6662
if shouldAddKind {
67-
name = name.appending("-\(symbol.kind.identifier)")
63+
name = name.appending("-\(symbol.identifier.interfaceLanguage).\(symbol.kind.identifier.identifier)")
6864
}
6965
if shouldAddHash {
7066
name = name.appendingHashedIdentifier(identifier)

Sources/SwiftDocC/Infrastructure/Symbol Link Resolution/AbsoluteSymbolLink.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -224,10 +224,10 @@ extension AbsoluteSymbolLink.LinkComponent {
224224
case kindAndPreciseIdentifier(
225225
kindIdentifier: String, preciseIdentifierHash: String
226226
)
227-
228-
private static let knownSymbolKindIdentifiers: Set<String> = {
229-
Set(SymbolGraph.Symbol.Kind.Swift.allCases.map(\.rawValue))
230-
}()
227+
228+
private static func isKnownSymbolKindIdentifier(identifier: String) -> Bool {
229+
return SymbolGraph.Symbol.KindIdentifier.isKnownIdentifier(identifier)
230+
}
231231

232232
/// Creates a disambiguation suffix based on the given kind and precise
233233
/// identifiers.
@@ -261,7 +261,7 @@ extension AbsoluteSymbolLink.LinkComponent {
261261
if splitSuffix.count == 1 && splitSuffix[0] == string {
262262
// The string didn't contain a "-" so now we check
263263
// to see if the hash is a known symbol kind identifier.
264-
if Self.knownSymbolKindIdentifiers.contains(string) {
264+
if Self.isKnownSymbolKindIdentifier(identifier: string) {
265265
self = .kindIdentifier(string)
266266
} else {
267267
// Since we've confirmed that it's not a symbol kind identifier
@@ -274,7 +274,7 @@ extension AbsoluteSymbolLink.LinkComponent {
274274
//
275275
// We expect the symbol kind identifier to come first, followed
276276
// by a hash of the symbol's precise identifier.
277-
if Self.knownSymbolKindIdentifiers.contains(String(splitSuffix[0])) {
277+
if Self.isKnownSymbolKindIdentifier(identifier: String(splitSuffix[0])) {
278278
self = .kindAndPreciseIdentifier(
279279
kindIdentifier: String(splitSuffix[0]),
280280
preciseIdentifierHash: String(splitSuffix[1])

Sources/SwiftDocC/Infrastructure/Symbol Link Resolution/DocCSymbolRepresentable.swift

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,10 +175,44 @@ extension SymbolGraph.Symbol: DocCSymbolRepresentable {
175175
}
176176

177177
public var kindIdentifier: String? {
178-
self.kind.identifier
178+
"\(self.identifier.interfaceLanguage).\(self.kind.identifier.identifier)"
179179
}
180180

181181
public static func == (lhs: SymbolGraph.Symbol, rhs: SymbolGraph.Symbol) -> Bool {
182182
lhs.identifier.precise == rhs.identifier.precise
183183
}
184184
}
185+
186+
extension UnifiedSymbolGraph.Symbol: DocCSymbolRepresentable {
187+
public var preciseIdentifier: String? {
188+
self.uniqueIdentifier
189+
}
190+
191+
public var title: String {
192+
guard let selector = self.defaultSelector else {
193+
fatalError("""
194+
Failed to find a supported default selector. \
195+
Language unsupported or corrupt symbol graph provided.
196+
"""
197+
)
198+
}
199+
200+
return self.names[selector]!.title
201+
}
202+
203+
public var kindIdentifier: String? {
204+
guard let selector = self.defaultSelector else {
205+
fatalError("""
206+
Failed to find a supported default selector. \
207+
Language unsupported or corrupt symbol graph provided.
208+
"""
209+
)
210+
}
211+
212+
return "\(selector.interfaceLanguage).\(self.kind[selector]!.identifier.identifier)"
213+
}
214+
215+
public static func == (lhs: UnifiedSymbolGraph.Symbol, rhs: UnifiedSymbolGraph.Symbol) -> Bool {
216+
lhs.uniqueIdentifier == rhs.uniqueIdentifier
217+
}
218+
}

Sources/SwiftDocC/Infrastructure/Topic Graph/AutomaticCuration.swift

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public struct AutomaticCuration {
2828
}
2929

3030
/// A mapping between a symbol kind and its matching group.
31-
typealias ReferenceGroupIndex = [SymbolGraph.Symbol.Kind.Swift: ReferenceGroup]
31+
typealias ReferenceGroupIndex = [SymbolGraph.Symbol.KindIdentifier: ReferenceGroup]
3232

3333
/// A static list of predefined groups for each supported kind of symbol.
3434
static var groups: ReferenceGroupIndex {
@@ -65,12 +65,9 @@ public struct AutomaticCuration {
6565
}
6666

6767
let childNode = try context.entity(with: child.reference)
68-
guard let childSymbol = childNode.semantic as? Symbol,
69-
let swiftKind = SymbolGraph.Symbol.Kind.Swift(rawValue: childSymbol.kind.identifier) else {
70-
return
68+
if let childSymbol = childNode.semantic as? Symbol {
69+
groupsIndex[childSymbol.kind.identifier]?.references.append(child.reference)
7170
}
72-
73-
groupsIndex[swiftKind]?.references.append(child.reference)
7471
}
7572
.lazy
7673
// Sort the groups in the order intended for rendering
@@ -137,7 +134,7 @@ extension AutomaticCuration {
137134
/// Returns a topics group title for the given symbol kind.
138135
/// - Parameter symbolKind: A symbol kind, such as a protocol or a variable.
139136
/// - Returns: A group title for symbols of the given kind.
140-
static func groupTitle(`for` symbolKind: SymbolGraph.Symbol.Kind.Swift) -> String {
137+
static func groupTitle(`for` symbolKind: SymbolGraph.Symbol.KindIdentifier) -> String {
141138
switch symbolKind {
142139
case .`associatedtype`: return "Associated Types"
143140
case .`class`: return "Classes"
@@ -158,11 +155,12 @@ extension AutomaticCuration {
158155
case .`typealias`: return "Type Aliases"
159156
case .`var`: return "Variables"
160157
case .module: return "Modules"
158+
case .unknown: return "Symbols"
161159
}
162160
}
163161

164162
/// The order of symbol kinds when grouped automatically.
165-
static let groupKindOrder: [SymbolGraph.Symbol.Kind.Swift] = [
163+
static let groupKindOrder: [SymbolGraph.Symbol.KindIdentifier] = [
166164
.module,
167165

168166
.`class`,

Sources/SwiftDocC/Infrastructure/Topic Graph/TopicGraph.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ struct TopicGraph {
3030
class Node: Hashable, CustomDebugStringConvertible {
3131
/// The location of the node's contents.
3232
enum ContentLocation: Hashable {
33-
33+
34+
// TODO: make this take multiple URLs?
3435
/// The node exists as a whole file at some URL.
3536
case file(url: URL)
3637

0 commit comments

Comments
 (0)