Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion Sources/SymbolKit/SymbolGraph/Module.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ extension SymbolGraph {
/// Optional bystander module names.
public var bystanders: [String]?

/// Optional extended module name.
///
/// Extended module name is set when the symbol graph contains symbols that are all
/// extensions from a specific module.
public var extended: String?

/// The platform intended for deployment.
public var platform: Platform

Expand All @@ -29,18 +35,20 @@ extension SymbolGraph {
/// but one created implicitly to hold relationships.
public var isVirtual: Bool = false

public init(name: String, platform: Platform, version: SemanticVersion? = nil, bystanders: [String]? = nil, isVirtual: Bool = false) {
public init(name: String, platform: Platform, version: SemanticVersion? = nil, bystanders: [String]? = nil, extended: String? = nil, isVirtual: Bool = false) {
self.name = name
self.platform = platform
self.version = version
self.bystanders = bystanders
self.extended = extended
self.isVirtual = isVirtual
}

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
self.bystanders = try container.decodeIfPresent([String].self, forKey: .bystanders)
self.extended = try container.decodeIfPresent(String.self, forKey: .extended)
self.platform = try container.decode(Platform.self, forKey: .platform)
self.version = try container.decodeIfPresent(SemanticVersion.self, forKey: .version)
self.isVirtual = try container.decodeIfPresent(Bool.self, forKey: .isVirtual) ?? false
Expand Down
15 changes: 13 additions & 2 deletions Sources/SymbolKit/UnifiedSymbolGraph/GraphCollector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,15 @@ extension GraphCollector {
// extension to another modules.
// The snippet extractor generates symbol graph files without an `@` symbol and with the
// the metadata that indicates the module `isVirtual`.
let isMainSymbolGraph = !url.lastPathComponent.contains("@") && !graph.module.isVirtual

let isMainSymbolGraph: Bool
if let extended = graph.module.extended {
// The "extended" field was added in a revision to the symbolgraph format. When that
// field is not set, the symbolgraph data was emitted by a previous compiler version.
isMainSymbolGraph = extended == graph.module.name
} else {
isMainSymbolGraph = !url.lastPathComponent.contains("@") && !graph.module.isVirtual
}

let moduleName: String
if isMainSymbolGraph && graph.module.bystanders == nil {
// When bystander modules are present, the symbol graph is a cross-import overlay, and
Expand All @@ -158,6 +165,10 @@ extension GraphCollector {
if url.lastPathComponent.contains("-snippets.symbols.json") {
return (graph.module.name, isMainSymbolGraph)
}

if let extended = graph.module.extended {
return (extended, isMainSymbolGraph)
}

// For extension symbol graphs, derive the extended module's name from the file name.
//
Expand Down
27 changes: 26 additions & 1 deletion Tests/SymbolKitTests/UnifiedGraph/GraphCollectorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,26 @@ class GraphCollectorTests: XCTestCase {
mixins: [:])
],
relationships: [])

var (extensionName, extensionIsMain) = GraphCollector.moduleNameFor(a_At_B, at: .init(fileURLWithPath: "A@B.symbols.json"))
XCTAssertFalse(extensionIsMain)
XCTAssertEqual("B", extensionName)

// "A@B" style graph, with extension of B in module A, using shortened name scheme.
let a_At_B_Shortened = SymbolGraph(metadata: .init(formatVersion: .init(major: 1, minor: 0, patch: 0), generator: "unit-test"),
module: .init(name: "A", platform: .init(), extended: "B"),
symbols: [
.init(identifier: .init(precise: "s:BBAatB", interfaceLanguage: "swift"),
names: .init(title: "AatB", navigator: nil, subHeading: nil, prose: nil),
pathComponents: ["B", "AatB"],
docComment: nil,
accessLevel: .init(rawValue: "public"),
kind: .init(parsedIdentifier: .class, displayName: "Class"),
mixins: [:])
],
relationships: [])

let (extensionName, extensionIsMain) = GraphCollector.moduleNameFor(a_At_B, at: .init(fileURLWithPath: "A@B.symbols.json"))
(extensionName, extensionIsMain) = GraphCollector.moduleNameFor(a_At_B_Shortened, at: .init(fileURLWithPath: "ABCDEF123456.symbols.json"))
XCTAssertFalse(extensionIsMain)
XCTAssertEqual("B", extensionName)

Expand Down Expand Up @@ -186,5 +204,12 @@ class GraphCollectorTests: XCTestCase {
let (extendedB, extendedBIsMain) = GraphCollector.moduleNameFor(a_At_B, at: .init(fileURLWithPath: "_A_B@B.symbols.json"))
XCTAssertFalse(extendedBIsMain)
XCTAssertEqual("B", extendedB)

// "A@B" style graph, with extension of B in module A, using shortened name scheme.
var a_At_B_ShortenedFileName = a_At_B
a_At_B_ShortenedFileName.module.extended = "B"
let (extendedBShortForm, extendedBIsMainShortForm) = GraphCollector.moduleNameFor(a_At_B, at: .init(fileURLWithPath: "_A_B@B.symbols.json"))
XCTAssertFalse(extendedBIsMainShortForm)
XCTAssertEqual("B", extendedBShortForm)
}
}