Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ extension PathHierarchy {
if let moduleMatch = modules.first(where: { $0.matches(firstComponent) }) {
return try searchForNode(descendingFrom: moduleMatch, pathComponents: remaining.dropFirst(), onlyFindSymbols: onlyFindSymbols, rawPathForError: rawPath)
}
if modules.count == 1 {
// For absolute links, only use the single-module fallback if the first component doesn't match
// any module name
if modules.count == 1 && !isAbsolute {
do {
return try searchForNode(descendingFrom: modules.first!, pathComponents: remaining, onlyFindSymbols: onlyFindSymbols, rawPathForError: rawPath)
} catch {
Expand Down
86 changes: 85 additions & 1 deletion Tests/SwiftDocCTests/Infrastructure/PathHierarchyTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3198,7 +3198,91 @@ class PathHierarchyTests: XCTestCase {
try assertFindsPath("/MainModule/TopLevelProtocol/extensionMember(_:)", in: tree, asSymbolID: "extensionMember1")
try assertFindsPath("/MainModule/TopLevelProtocol/InnerStruct/extensionMember(_:)", in: tree, asSymbolID: "extensionMember2")
}


func testAbsoluteLinksToOtherModuleWithExtensions() async throws {
enableFeatureFlag(\.isExperimentalLinkHierarchySerializationEnabled)

let extendedTypeID = "extended-type-id"
let extensionID = "extension-id"
let extensionMethodID = "extension-method-id"

let extensionMixin = SymbolGraph.Symbol.Swift.Extension(
extendedModule: "ExtendedModule",
typeKind: .struct,
constraints: []
)

let catalog = Folder(name: "TestCatalog.docc", content: [
JSONFile(name: "MainModule.symbols.json", content: makeSymbolGraph(moduleName: "MainModule", symbols: [])),
JSONFile(name: "MainModule@ExtendedModule.symbols.json", content: makeSymbolGraph(
moduleName: "MainModule",
symbols: [
makeSymbol(
id: extensionID,
kind: .extension,
pathComponents: ["ExtendedType"],
otherMixins: [extensionMixin]
),
makeSymbol(
id: extensionMethodID,
kind: .method,
pathComponents: ["ExtendedType", "extensionMethod()"],
otherMixins: [extensionMixin]
)
],
relationships: [
.init(
source: extensionMethodID,
target: extensionID,
kind: .memberOf,
targetFallback: "ExtendedModule.ExtendedType"
),
.init(
source: extensionID,
target: extendedTypeID,
kind: .extensionTo,
targetFallback: "ExtendedModule.ExtendedType"
)
]
))
])

let (_, context) = try await loadBundle(catalog: catalog)
let tree = context.linkResolver.localResolver.pathHierarchy

try assertFindsPath(
"/MainModule/ExtendedModule/ExtendedType/extensionMethod()",
in: tree,
asSymbolID: extensionMethodID
)

try assertFindsPath(
"ExtendedModule/ExtendedType",
in: tree,
asSymbolID: extensionID
)
try assertFindsPath(
"ExtendedModule/ExtendedType/extensionMethod()",
in: tree,
asSymbolID: extensionMethodID
)

// Verify that a link that resolves relative to the module
// fails to resolve as an absolute link, with a moduleNotFound error.
try assertPathRaisesErrorMessage(
"/ExtendedModule/ExtendedType",
in: tree,
context: context,
expectedErrorMessage: "No module named 'ExtendedModule'"
)
try assertPathRaisesErrorMessage(
"/ExtendedModule/ExtendedType/extensionMethod()",
in: tree,
context: context,
expectedErrorMessage: "No module named 'ExtendedModule'"
)
}

func testMissingRequiredMemberOfSymbolGraphRelationshipInOneLanguageAcrossManyPlatforms() async throws {
// We make a best-effort attempt to create a valid path hierarchy, even if the symbol graph inputs are not valid.

Expand Down