Skip to content

Commit c7e8eb9

Browse files
committed
implement handling of extensions to external types as enabled by swiftlang/swift#59047
- introduce transformation generating internal Extended Types Symbol Graph Format from the Extension Block Symbol Graph Format emmitted by the compiler - register symbols and relationships used by Extended Types Symbol Graph Format - adapt SymbolGraphLoader to automatically detect if input Symbol Graph Files use the Extension Block Symbol Graph Format and apply the ExtendedTypesFormatTransformation in case - improve Swift title token parsing to correctly identify Symbol titles of nested types, which contain "." infixes added tests: - test detection of the Extension Block Symbol Graph Format and application of the ExtendedTypesFormatTransformation in SymbolGraphLoader - test the ExtendedTypesFormatTransformation - extend tests for Swift title token parsing with test-cases containing "." infixes
1 parent 3099317 commit c7e8eb9

File tree

36 files changed

+1726
-275
lines changed

36 files changed

+1726
-275
lines changed

Package.resolved

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
"repositoryURL": "https://github.com/apple/swift-docc-symbolkit",
4343
"state": {
4444
"branch": "main",
45-
"revision": "da6cedd103e0e08a2bc7b14869ec37fba4db72d9",
45+
"revision": "b45d1f2ed151d057b54504d653e0da5552844e34",
4646
"version": null
4747
}
4848
},

Package.swift

+3-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ let package = Package(
7979
// Test utility library
8080
.target(
8181
name: "SwiftDocCTestUtilities",
82-
dependencies: []),
82+
dependencies: [
83+
"SymbolKit"
84+
]),
8385

8486
// Command-line tool
8587
.executableTarget(

Sources/SwiftDocC/Coverage/DocumentationCoverageOptions.swift

+5-5
Original file line numberDiff line numberDiff line change
@@ -185,15 +185,15 @@ extension DocumentationCoverageOptions.KindFilterOptions {
185185
/// Converts given ``DocumentationNode.Kind`` to corresponding `BitFlagRepresentation` if possible. Returns `nil` if the given Kind is not representable.
186186
fileprivate init?(kind: DocumentationNode.Kind) {
187187
switch kind {
188-
case .module: // 1
188+
case .module, .extendedModule: // 1
189189
self = .module
190-
case .class: // 2
190+
case .class, .extendedClass: // 2
191191
self = .class
192-
case .structure: // 3
192+
case .structure, .extendedStructure: // 3
193193
self = .structure
194-
case .enumeration: // 4
194+
case .enumeration, .extendedEnumeration: // 4
195195
self = .enumeration
196-
case .protocol: // 5
196+
case .protocol, .extendedProtocol: // 5
197197
self = .protocol
198198
case .typeAlias: // 6
199199
self = .typeAlias

Sources/SwiftDocC/Indexing/Navigator/NavigatorIndex+Ext.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public class FileSystemRenderNodeProvider: RenderNodeProvider {
7676

7777
extension RenderNode {
7878
private static let typesThatShouldNotUseNavigatorTitle: Set<NavigatorIndex.PageType> = [
79-
.framework, .class, .structure, .enumeration, .protocol, .typeAlias, .associatedType
79+
.framework, .class, .structure, .enumeration, .protocol, .typeAlias, .associatedType, .extension
8080
]
8181

8282
/// Returns a navigator title preferring the fragments inside the metadata, if applicable.

Sources/SwiftDocC/Infrastructure/CoverageDataEntry.swift

+16-11
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,12 @@ extension CoverageDataEntry {
243243
.protocol,
244244
.typeAlias,
245245
.associatedType,
246-
.typeDef:
246+
.typeDef,
247+
.extendedClass,
248+
.extendedStructure,
249+
.extendedEnumeration,
250+
.extendedProtocol,
251+
.unknownExtendedType:
247252
self = .types
248253
case .localVariable,
249254
.instanceProperty,
@@ -256,7 +261,7 @@ extension CoverageDataEntry {
256261
.typeSubscript,
257262
.instanceSubscript:
258263
self = .members
259-
case .function, .module, .globalVariable, .operator:
264+
case .function, .module, .globalVariable, .operator, .extendedModule:
260265
self = .globals
261266
case let kind where SummaryCategory.allKnownNonSymbolKindNames.contains(kind.name):
262267
self = .nonSymbol
@@ -297,46 +302,46 @@ extension CoverageDataEntry {
297302
context: DocumentationContext
298303
) throws {
299304
switch documentationNode.kind {
300-
case DocumentationNode.Kind.class:
305+
case .class, .extendedClass:
301306
self = try .class(
302307
memberStats: KindSpecificData.extractChildStats(
303308
documentationNode: documentationNode,
304309
context: context))
305-
case DocumentationNode.Kind.enumeration:
310+
case .enumeration, .extendedEnumeration:
306311
self = try .enumeration(
307312
memberStats: KindSpecificData.extractChildStats(
308313
documentationNode: documentationNode,
309314
context: context))
310-
case DocumentationNode.Kind.structure:
315+
case .structure, .extendedStructure:
311316
self = try .structure(
312317
memberStats: KindSpecificData.extractChildStats(
313318
documentationNode: documentationNode,
314319
context: context))
315-
case DocumentationNode.Kind.protocol:
316-
self = try .enumeration(
320+
case .protocol, .extendedProtocol:
321+
self = try .protocol(
317322
memberStats: KindSpecificData.extractChildStats(
318323
documentationNode: documentationNode,
319324
context: context))
320325

321-
case DocumentationNode.Kind.instanceMethod:
326+
case .instanceMethod:
322327
self = try .instanceMethod(
323328
parameterStats: CoverageDataEntry.KindSpecificData.extractFunctionSignatureStats(
324329
documentationNode: documentationNode,
325330
context: context
326331
, fieldName: "method parameters"))
327-
case DocumentationNode.Kind.operator:
332+
case .operator:
328333
self = try .`operator`(
329334
parameterStats: CoverageDataEntry.KindSpecificData.extractFunctionSignatureStats(
330335
documentationNode: documentationNode,
331336
context: context,
332337
fieldName: "operator parameters"))
333-
case DocumentationNode.Kind.function:
338+
case .function:
334339
self = try .`operator`(
335340
parameterStats: CoverageDataEntry.KindSpecificData.extractFunctionSignatureStats(
336341
documentationNode: documentationNode,
337342
context: context,
338343
fieldName: "function parameters"))
339-
case DocumentationNode.Kind.initializer:
344+
case .initializer:
340345
self = try .`operator`(
341346
parameterStats: CoverageDataEntry.KindSpecificData.extractFunctionSignatureStats(
342347
documentationNode: documentationNode,

Sources/SwiftDocC/Infrastructure/DocumentationContext.swift

+5-2
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,9 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
277277
public var externalMetadata = ExternalMetadata()
278278

279279

280+
/// The decoder used in the `SymbolGraphLoader`
281+
var decoder: JSONDecoder = JSONDecoder()
282+
280283
/// Initializes a documentation context with a given `dataProvider` and registers all the documentation bundles that it provides.
281284
///
282285
/// - Parameter dataProvider: The data provider to register bundles from.
@@ -1025,7 +1028,7 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
10251028
private func parentChildRelationship(from edge: SymbolGraph.Relationship) -> (ResolvedTopicReference, ResolvedTopicReference)? {
10261029
// Filter only parent <-> child edges
10271030
switch edge.kind {
1028-
case .memberOf, .requirementOf:
1031+
case .memberOf, .requirementOf, .declaredIn, .inContextOf:
10291032
guard let parentRef = symbolIndex[edge.target]?.reference, let childRef = symbolIndex[edge.source]?.reference else {
10301033
return nil
10311034
}
@@ -1923,7 +1926,7 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
19231926
discoveryGroup.async(queue: discoveryQueue) { [unowned self] in
19241927
symbolGraphLoader = SymbolGraphLoader(bundle: bundle, dataProvider: self.dataProvider)
19251928
do {
1926-
try symbolGraphLoader.loadAll()
1929+
try symbolGraphLoader.loadAll(using: decoder)
19271930
if LinkResolutionMigrationConfiguration.shouldSetUpHierarchyBasedLinkResolver {
19281931
let pathHierarchy = PathHierarchy(symbolGraphLoader: symbolGraphLoader, bundleName: urlReadablePath(bundle.displayName), knownDisambiguatedPathComponents: knownDisambiguatedSymbolPathComponents)
19291932
hierarchyBasedResolver = PathHierarchyBasedLinkResolver(pathHierarchy: pathHierarchy)

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

+12
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,18 @@ extension ExternalSymbolResolver {
6666
symbolKind = .var
6767
case .module:
6868
symbolKind = .module
69+
case .extendedModule:
70+
symbolKind = .extendedModule
71+
case .extendedStructure:
72+
symbolKind = .extendedStructure
73+
case .extendedClass:
74+
symbolKind = .extendedClass
75+
case .extendedEnumeration:
76+
symbolKind = .extendedEnumeration
77+
case .extendedProtocol:
78+
symbolKind = .extendedProtocol
79+
case .unknownExtendedType:
80+
symbolKind = .unknownExtendedType
6981

7082
// There shouldn't be any reason for a symbol graph file to reference one of these kinds outside of the symbol graph itself.
7183
// Return `.class` as the symbol kind (acting as "any symbol") so that the render reference gets a "symbol" role.

Sources/SwiftDocC/Infrastructure/Link Resolution/DocumentationCacheBasedLinkResolver.swift

+7-4
Original file line numberDiff line numberDiff line change
@@ -495,10 +495,13 @@ final class DocumentationCacheBasedLinkResolver {
495495
// therefore for the currently processed symbol to be a child of a re-written symbol it needs to have
496496
// at least 3 components. It's a fair optimization to make since graphs will include a lot of root level symbols.
497497
guard reference.pathComponents.count > 3,
498-
// Fetch the symbol's parent
499-
let parentReference = try symbolsURLHierarchy.parent(of: reference),
500-
// If the parent path matches the current reference path, bail out
501-
parentReference.pathComponents != reference.pathComponents.dropLast()
498+
// Fetch the symbol's parent
499+
let parentReference = try symbolsURLHierarchy.parent(of: reference),
500+
// If the parent path matches the current reference path, bail out
501+
parentReference.pathComponents != reference.pathComponents.dropLast(),
502+
// If the parent is not from the same module (because we're dealing with a
503+
// default implementation of an external protocol), bail out
504+
parentReference.pathComponents[..<3] == reference.pathComponents[..<3]
502505
else { return reference }
503506

504507
// Build an up to date reference path for the current node based on the parent path
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright (c) 2021 Apple Inc. and the Swift project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See https://swift.org/LICENSE.txt for license information
8+
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
*/
10+
11+
import SymbolKit
12+
13+
extension SymbolGraph.Symbol.AccessControl: Comparable {
14+
private var level: Int? {
15+
switch self {
16+
case .private:
17+
return 0
18+
case .filePrivate:
19+
return 1
20+
case .internal:
21+
return 2
22+
case .public:
23+
return 3
24+
case .open:
25+
return 4
26+
default:
27+
assertionFailure("Unknown AccessControl case was used in comparison.")
28+
return nil
29+
}
30+
}
31+
32+
public static func < (lhs: SymbolGraph.Symbol.AccessControl, rhs: SymbolGraph.Symbol.AccessControl) -> Bool {
33+
guard let lhs = lhs.level,
34+
let rhs = rhs.level else {
35+
return false
36+
}
37+
38+
return lhs < rhs
39+
}
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright (c) 2021 Apple Inc. and the Swift project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See https://swift.org/LICENSE.txt for license information
8+
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
*/
10+
11+
import SymbolKit
12+
13+
// MARK: Custom Relationship Kind Identifiers
14+
15+
extension SymbolGraph.Relationship.Kind {
16+
/// This relationship connects top-level extended type symbols the
17+
/// respective extended module symbol.
18+
static let declaredIn = Self(rawValue: "declaredIn")
19+
20+
/// This relationship markes a parent-child hierarchy between a nested
21+
/// extended type symbol and its parent extended type symbol. It mirrors the
22+
/// `memberOf` relationship between the two respective original type symbols.
23+
static let inContextOf = Self(rawValue: "inContextOf")
24+
}
25+
26+
// MARK: Custom Symbol Kind Identifiers
27+
28+
extension SymbolGraph.Symbol.KindIdentifier {
29+
static let extendedProtocol = Self(rawValue: "protocol.extension")
30+
31+
static let extendedStructure = Self(rawValue: "struct.extension")
32+
33+
static let extendedClass = Self(rawValue: "class.extension")
34+
35+
static let extendedEnumeration = Self(rawValue: "enum.extension")
36+
37+
static let unknownExtendedType = Self(rawValue: "unknown.extension")
38+
39+
static let extendedModule = Self(rawValue: "module.extension")
40+
41+
init?(extending other: Self) {
42+
switch other {
43+
case .struct:
44+
self = .extendedStructure
45+
case .protocol:
46+
self = .extendedProtocol
47+
case .class:
48+
self = .extendedClass
49+
case .enum:
50+
self = .extendedEnumeration
51+
case .module:
52+
self = .extendedModule
53+
default:
54+
return nil
55+
}
56+
}
57+
58+
static func extendedType(for extensionBlock: SymbolGraph.Symbol) -> Self? {
59+
guard let extensionMixin = extensionBlock.mixins[SymbolGraph.Symbol.Swift.Extension.mixinKey] as? SymbolGraph.Symbol.Swift.Extension else {
60+
return nil
61+
}
62+
63+
guard let typeKind = extensionMixin.typeKind else {
64+
return nil
65+
}
66+
67+
return Self(extending: typeKind)
68+
}
69+
}
70+
71+
extension SymbolGraph.Symbol.Kind {
72+
static func extendedType(for extensionBlock: SymbolGraph.Symbol) -> Self {
73+
let id = SymbolGraph.Symbol.KindIdentifier.extendedType(for: extensionBlock)
74+
switch id {
75+
case .some(.extendedProtocol):
76+
return Self(parsedIdentifier: .extendedProtocol, displayName: "Extended Protocol")
77+
case .some(.extendedStructure):
78+
return Self(parsedIdentifier: .extendedStructure, displayName: "Extended Structure")
79+
case .some(.extendedClass):
80+
return Self(parsedIdentifier: .extendedClass, displayName: "Extended Class")
81+
case .some(.extendedEnumeration):
82+
return Self(parsedIdentifier: .extendedEnumeration, displayName: "Extended Enumeration")
83+
default:
84+
return unknownExtendedType
85+
}
86+
}
87+
88+
static let unknownExtendedType = Self(parsedIdentifier: .unknownExtendedType, displayName: "Extended Type")
89+
}
90+
91+
92+
// MARK: Swift AccessControl Levels
93+
94+
extension SymbolGraph.Symbol.AccessControl {
95+
static let `private` = Self(rawValue: "private")
96+
97+
static let filePrivate = Self(rawValue: "fileprivate")
98+
99+
static let `internal` = Self(rawValue: "internal")
100+
101+
static let `public` = Self(rawValue: "public")
102+
103+
static let open = Self(rawValue: "open")
104+
}

0 commit comments

Comments
 (0)