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 @@ -223,13 +223,27 @@ extension PathHierarchy.Error {

case .lookupCollision(partialResult: let partialResult, remaining: let remaining, collisions: let collisions):
let nextPathComponent = remaining.first!
let (pathPrefix, _, solutions) = makeCollisionSolutions(from: collisions, nextPathComponent: nextPathComponent, partialResultPrefix: partialResult.pathPrefix)

let (pathPrefix, foundDisambiguation, solutions) = makeCollisionSolutions(
from: collisions,
nextPathComponent: nextPathComponent,
partialResultPrefix: partialResult.pathPrefix)

let rangeAdjustment: SourceRange
if !foundDisambiguation.isEmpty {
rangeAdjustment = .makeRelativeRange(
startColumn: pathPrefix.count - foundDisambiguation.count,
length: foundDisambiguation.count)
} else {
rangeAdjustment = .makeRelativeRange(
startColumn: pathPrefix.count - nextPathComponent.full.count,
length: nextPathComponent.full.count)
}

return TopicReferenceResolutionErrorInfo("""
\(nextPathComponent.full.singleQuoted) is ambiguous at \(partialResult.node.pathWithoutDisambiguation().singleQuoted)
""",
solutions: solutions,
rangeAdjustment: .makeRelativeRange(startColumn: pathPrefix.count - nextPathComponent.full.count, length: nextPathComponent.full.count)
rangeAdjustment: rangeAdjustment
)
}
}
Expand Down
6 changes: 6 additions & 0 deletions Sources/SwiftDocC/Semantics/ReferenceResolver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ func unresolvedReferenceProblem(source: URL?, range: SourceRange?, severity: Dia
let diagnosticRange: SourceRange?
if var rangeAdjustment = errorInfo.rangeAdjustment, let referenceSourceRange {
rangeAdjustment.offsetWithRange(referenceSourceRange)
assert(rangeAdjustment.lowerBound.column >= 0, """
Unresolved topic reference range adjustment created range with negative column.
Source: \(source?.absoluteString ?? "nil")
Range: \(rangeAdjustment.lowerBound.description):\(rangeAdjustment.upperBound.description)
Summary: \(errorInfo.message)
""")
diagnosticRange = rangeAdjustment
} else {
diagnosticRange = referenceSourceRange
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5266,7 +5266,165 @@ let expected = """
}
}
}


func testContextDiagnosesInsufficientDisambiguationWithCorrectRange() throws {
// This test deliberately does not turn on the overloads feature
// to ensure the symbol link below does not accidentally resolve correctly.
for symbolKindID in SymbolGraph.Symbol.KindIdentifier.allCases where !symbolKindID.isOverloadableKind {
// Generate a 4 symbols with the same name for every non overloadable symbol kind
let symbols: [SymbolGraph.Symbol] = [
makeSymbol(id: "first-\(symbolKindID.identifier)-id", kind: symbolKindID, pathComponents: ["SymbolName"]),
makeSymbol(id: "second-\(symbolKindID.identifier)-id", kind: symbolKindID, pathComponents: ["SymbolName"]),
makeSymbol(id: "third-\(symbolKindID.identifier)-id", kind: symbolKindID, pathComponents: ["SymbolName"]),
makeSymbol(id: "fourth-\(symbolKindID.identifier)-id", kind: symbolKindID, pathComponents: ["SymbolName"]),
]

let catalog =
Folder(name: "unit-test.docc", content: [
JSONFile(name: "ModuleName.symbols.json", content: makeSymbolGraph(
moduleName: "ModuleName",
symbols: symbols
)),

TextFile(name: "ModuleName.md", utf8Content: """
# ``ModuleName``

This is a test file for ModuleName.

## Topics

- ``SymbolName-\(symbolKindID.identifier)``
""")
])

let (_, context) = try loadBundle(catalog: catalog)

let problems = context.problems.sorted(by: \.diagnostic.summary)
XCTAssertEqual(problems.count, 1)

let problem = try XCTUnwrap(problems.first)

XCTAssertEqual(problem.diagnostic.summary, "'SymbolName-\(symbolKindID.identifier)' is ambiguous at '/ModuleName'")

XCTAssertEqual(problem.possibleSolutions.count, 4)

for solution in problem.possibleSolutions {
XCTAssertEqual(solution.replacements.count, 1)
let replacement = try XCTUnwrap(solution.replacements.first)

XCTAssertEqual(replacement.range.lowerBound, .init(line: 7, column: 15, source: nil))
XCTAssertEqual(
replacement.range.upperBound,
.init(line: 7, column: 16 + symbolKindID.identifier.count, source: nil)
)
}
}
}

func testContextDiagnosesIncorrectDisambiguationWithCorrectRange() throws {
// This test deliberately does not turn on the overloads feature
// to ensure the symbol link below does not accidentally resolve correctly.
for symbolKindID in SymbolGraph.Symbol.KindIdentifier.allCases where !symbolKindID.isOverloadableKind {
// Generate a 4 symbols with the same name for every non overloadable symbol kind
let symbols: [SymbolGraph.Symbol] = [
makeSymbol(id: "first-\(symbolKindID.identifier)-id", kind: symbolKindID, pathComponents: ["SymbolName"]),
makeSymbol(id: "second-\(symbolKindID.identifier)-id", kind: symbolKindID, pathComponents: ["SymbolName"]),
makeSymbol(id: "third-\(symbolKindID.identifier)-id", kind: symbolKindID, pathComponents: ["SymbolName"]),
makeSymbol(id: "fourth-\(symbolKindID.identifier)-id", kind: symbolKindID, pathComponents: ["SymbolName"]),
]

let catalog =
Folder(name: "unit-test.docc", content: [
JSONFile(name: "ModuleName.symbols.json", content: makeSymbolGraph(
moduleName: "ModuleName",
symbols: symbols
)),

TextFile(name: "ModuleName.md", utf8Content: """
# ``ModuleName``

This is a test file for ModuleName.

## Topics

- ``SymbolName-abc123``
""")
])

let (_, context) = try loadBundle(catalog: catalog)

let problems = context.problems.sorted(by: \.diagnostic.summary)
XCTAssertEqual(problems.count, 1)

let problem = try XCTUnwrap(problems.first)

XCTAssertEqual(problem.diagnostic.summary, "'abc123' isn't a disambiguation for 'SymbolName' at '/ModuleName'")

XCTAssertEqual(problem.possibleSolutions.count, 4)

for solution in problem.possibleSolutions {
XCTAssertEqual(solution.replacements.count, 1)
let replacement = try XCTUnwrap(solution.replacements.first)

// "Replace '-abc123' with '-(hash)'" where 'abc123' is at 7:15-7:22
XCTAssertEqual(replacement.range.lowerBound, .init(line: 7, column: 15, source: nil))
XCTAssertEqual(replacement.range.upperBound, .init(line: 7, column: 22, source: nil))
}
}
}

func testContextDiagnosesIncorrectSymbolNameWithCorrectRange() throws {
// This test deliberately does not turn on the overloads feature
// to ensure the symbol link below does not accidentally resolve correctly.
for symbolKindID in SymbolGraph.Symbol.KindIdentifier.allCases where !symbolKindID.isOverloadableKind {
// Generate a 4 symbols with the same name for every non overloadable symbol kind
let symbols: [SymbolGraph.Symbol] = [
makeSymbol(id: "first-\(symbolKindID.identifier)-id", kind: symbolKindID, pathComponents: ["SymbolName"]),
makeSymbol(id: "second-\(symbolKindID.identifier)-id", kind: symbolKindID, pathComponents: ["SymbolName"]),
makeSymbol(id: "third-\(symbolKindID.identifier)-id", kind: symbolKindID, pathComponents: ["SymbolName"]),
makeSymbol(id: "fourth-\(symbolKindID.identifier)-id", kind: symbolKindID, pathComponents: ["SymbolName"]),
]

let catalog =
Folder(name: "unit-test.docc", content: [
JSONFile(name: "ModuleName.symbols.json", content: makeSymbolGraph(
moduleName: "ModuleName",
symbols: symbols
)),

TextFile(name: "ModuleName.md", utf8Content: """
# ``ModuleName``

This is a test file for ModuleName.

## Topics

- ``Symbol``
""")
])

let (_, context) = try loadBundle(catalog: catalog)

let problems = context.problems.sorted(by: \.diagnostic.summary)
XCTAssertEqual(problems.count, 1)

let problem = try XCTUnwrap(problems.first)

XCTAssertEqual(problem.diagnostic.summary, "'Symbol' doesn't exist at '/ModuleName'")

XCTAssertEqual(problem.possibleSolutions.count, 1)
let solution = try XCTUnwrap(problem.possibleSolutions.first)

XCTAssertEqual(solution.summary, "Replace 'Symbol' with 'SymbolName'")

XCTAssertEqual(solution.replacements.count, 1)
let replacement = try XCTUnwrap(solution.replacements.first)

XCTAssertEqual(replacement.range.lowerBound, .init(line: 7, column: 5, source: nil))
XCTAssertEqual(replacement.range.upperBound, .init(line: 7, column: 11, source: nil))
}
}

func testResolveExternalLinkFromTechnologyRoot() throws {
enableFeatureFlag(\.isExperimentalLinkHierarchySerializationEnabled)

Expand Down