Skip to content

Commit

Permalink
Store only the resolved reference in DocumentationContext.symbolIndex (
Browse files Browse the repository at this point in the history
…#543) (#545)

* Store only the resolved reference in DocumentationContext.symbolIndex

rdar://106654403

* Add test that references in symbol index have nodes in doc cache
  • Loading branch information
d-ronnqvist authored Apr 12, 2023
1 parent 62d06ad commit c467f80
Show file tree
Hide file tree
Showing 22 changed files with 240 additions and 180 deletions.
6 changes: 2 additions & 4 deletions Sources/SwiftDocC/Infrastructure/CoverageDataEntry.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
This source file is part of the Swift.org open source project

Copyright (c) 2021 Apple Inc. and the Swift project authors
Copyright (c) 2021-2023 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception

See https://swift.org/LICENSE.txt for license information
Expand Down Expand Up @@ -371,9 +371,7 @@ extension CoverageDataEntry {
)
let total = children.count
let documented = children.filter {
(context.symbolIndex[$0.reference.description]?.semantic as? Symbol)?
.abstractSection
!= nil
(context.nodeWithSymbolIdentifier($0.reference.description)?.semantic as? Symbol)?.abstractSection != nil
}.count

if total == 0 {
Expand Down
71 changes: 42 additions & 29 deletions Sources/SwiftDocC/Infrastructure/DocumentationContext.swift

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,7 @@ final class DocumentationCacheBasedLinkResolver {
/// Method called when walking the symbol url tree that checks if a parent of a symbol has had its
/// path modified during loading the symbol graph. If that's the case the method replaces
/// `reference` with an updated reference with a correct reference path.
func updateNodeWithReferenceIfCollisionChild(_ reference: ResolvedTopicReference, symbolsURLHierarchy: inout BidirectionalTree<ResolvedTopicReference>, symbolIndex: inout [String: DocumentationNode], context: DocumentationContext) throws {
func updateNodeWithReferenceIfCollisionChild(_ reference: ResolvedTopicReference, symbolsURLHierarchy: inout BidirectionalTree<ResolvedTopicReference>, symbolIndex: inout [String: ResolvedTopicReference], context: DocumentationContext) throws {
let newReference = try currentReferenceFor(reference: reference, symbolsURLHierarchy: &symbolsURLHierarchy)
guard newReference != reference else { return }

Expand All @@ -523,7 +523,7 @@ final class DocumentationCacheBasedLinkResolver {
// Rewrite the symbol index
if let symbolIdentifier = documentationNode?.symbol?.identifier {
symbolIndex.removeValue(forKey: symbolIdentifier.precise)
symbolIndex[symbolIdentifier.precise] = documentationNode
symbolIndex[symbolIdentifier.precise] = newReference
}

// Replace the topic graph node
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1113,7 +1113,7 @@ private extension PathHierarchy.Node {
if let fragments = symbol[mixin: SymbolGraph.Symbol.DeclarationFragments.self]?.declarationFragments {
return fragments.map(\.spelling).joined().split(whereSeparator: { $0.isWhitespace || $0.isNewline }).joined(separator: " ")
}
return context.symbolIndex[symbol.identifier.precise]!.name.description
return context.nodeWithSymbolIdentifier(symbol.identifier.precise)!.name.description
}
// This only gets called for PathHierarchy error messages, so hierarchyBasedLinkResolver is never nil.
let reference = context.hierarchyBasedLinkResolver!.resolvedReferenceMap[identifier]!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,12 @@ final class PathHierarchyBasedLinkResolver {
}

/// Map the resolved identifiers to resolved topic references for all symbols in the given symbol index.
func addMappingForSymbols(symbolIndex: [String: DocumentationNode]) {
func addMappingForSymbols(symbolIndex: [String: ResolvedTopicReference]) {
for (id, node) in pathHierarchy.lookup {
guard let symbol = node.symbol, let node = symbolIndex[symbol.identifier.precise] else {
guard let symbol = node.symbol, let reference = symbolIndex[symbol.identifier.precise] else {
continue
}
resolvedReferenceMap[id] = node.reference
resolvedReferenceMap[id] = reference
}
}

Expand Down Expand Up @@ -164,9 +164,9 @@ final class PathHierarchyBasedLinkResolver {
}

/// Adds the headings for all symbols in the symbol index to the path hierarchy.
func addAnchorForSymbols(symbolIndex: [String: DocumentationNode]) {
func addAnchorForSymbols(symbolIndex: [String: ResolvedTopicReference], documentationCache: [ResolvedTopicReference: DocumentationNode]) {
for (id, node) in pathHierarchy.lookup {
guard let symbol = node.symbol, let node = symbolIndex[symbol.identifier.precise] else { continue }
guard let symbol = node.symbol, let reference = symbolIndex[symbol.identifier.precise], let node = documentationCache[reference] else { continue }
addAnchors(node.anchorSections, to: id)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
This source file is part of the Swift.org open source project

Copyright (c) 2021-2022 Apple Inc. and the Swift project authors
Copyright (c) 2021-2023 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception

See https://swift.org/LICENSE.txt for license information
Expand Down Expand Up @@ -255,14 +255,15 @@ enum GeneratedDocumentationTopics {
// Check that there is origin information (i.e. the symbol is inherited)
let origin = relationship.mixins[SymbolGraph.Relationship.SourceOrigin.mixinKey] as? SymbolGraph.Relationship.SourceOrigin,
// Resolve the containing type
let parent = context.symbolIndex[relationship.target],
let parent = context.nodeWithSymbolIdentifier(relationship.target),
// Resolve the child
let child = context.symbolIndex[relationship.source],
let child = context.nodeWithSymbolIdentifier(relationship.source),
// Get the child symbol
let childSymbol = child.symbol,
// Get the swift extension data
let extends = childSymbol.mixins[SymbolGraph.Symbol.Swift.Extension.mixinKey] as? SymbolGraph.Symbol.Swift.Extension {
let originSymbol = context.symbolIndex[origin.identifier]?.symbol
let extends = childSymbol.mixins[SymbolGraph.Symbol.Swift.Extension.mixinKey] as? SymbolGraph.Symbol.Swift.Extension
{
let originSymbol = context.nodeWithSymbolIdentifier(origin.identifier)?.symbol

// Add the inherited symbol to the index.
try inheritanceIndex.add(child.reference, to: parent.reference, childSymbol: childSymbol, originDisplayName: origin.displayName, originSymbol: originSymbol, extendedModuleName: extends.extendedModule)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
This source file is part of the Swift.org open source project

Copyright (c) 2021-2022 Apple Inc. and the Swift project authors
Copyright (c) 2021-2023 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception

See https://swift.org/LICENSE.txt for license information
Expand Down Expand Up @@ -39,27 +39,29 @@ struct SymbolGraphRelationshipsBuilder {
selector: UnifiedSymbolGraph.Selector,
in bundle: DocumentationBundle,
context: DocumentationContext,
symbolIndex: inout [String: DocumentationNode],
symbolIndex: inout [String: ResolvedTopicReference],
documentationCache: [ResolvedTopicReference: DocumentationNode],
engine: DiagnosticEngine
) {
// Resolve source symbol
guard let implementorNode = symbolIndex[edge.source],
let implementorSymbol = implementorNode.semantic as? Symbol else {
guard let implementorNode = symbolIndex[edge.source].flatMap({ documentationCache[$0] }),
let implementorSymbol = implementorNode.semantic as? Symbol
else {
// The source node for implementation relationship not found.
engine.emit(NodeProblem.notFound(edge.source))
return
}

// Resolve target symbol if possible
let optionalInterfaceNode = symbolIndex[edge.target]
let optionalInterfaceNode = symbolIndex[edge.target].flatMap { documentationCache[$0] }

if optionalInterfaceNode == nil {
// Take the interface language of the target symbol
// or if external - default to the language of the current symbol.
let language = symbolIndex[edge.target]?.reference.sourceLanguage
let language = symbolIndex[edge.target]?.sourceLanguage
?? implementorNode.reference.sourceLanguage

let symbolReference = SymbolReference(edge.target, interfaceLanguage: language, symbol: symbolIndex[edge.target]?.symbol)
let symbolReference = SymbolReference(edge.target, interfaceLanguage: language, symbol: symbolIndex[edge.target].flatMap { documentationCache[$0]?.symbol })
guard let unresolved = UnresolvedTopicReference(symbolReference: symbolReference, bundle: bundle) else {
// The symbol reference format is invalid.
engine.emit(NodeProblem.invalidReference(symbolReference.path))
Expand All @@ -74,9 +76,10 @@ struct SymbolGraphRelationshipsBuilder {
// Find out the parent's title
let parentName: String?

if let reference = symbolIndex[edge.source]?.reference,
let parentNode = try? context.entity(with: reference.removingLastPathComponent()),
let title = (parentNode.semantic as? Symbol)?.title {
if let reference = symbolIndex[edge.source],
let parentNode = try? context.entity(with: reference.removingLastPathComponent()),
let title = (parentNode.semantic as? Symbol)?.title
{
parentName = title
} else {
parentName = nil
Expand All @@ -93,14 +96,14 @@ struct SymbolGraphRelationshipsBuilder {
)

// Make the implementation a child of the requirement
guard let childReference = symbolIndex[edge.source]?.reference else {
guard let childReference = symbolIndex[edge.source] else {
// The child wasn't found, invalid reference in relationship.
engine.emit(SymbolGraphRelationshipsBuilder.NodeProblem.notFound(edge.source))
return
}

if let child = context.topicGraph.nodeWithReference(childReference),
let targetReference = symbolIndex[edge.target]?.reference,
let targetReference = symbolIndex[edge.target],
let parent = context.topicGraph.nodeWithReference(targetReference) {
context.topicGraph.addEdge(from: parent, to: child)
}
Expand All @@ -119,30 +122,32 @@ struct SymbolGraphRelationshipsBuilder {
edge: SymbolGraph.Relationship,
selector: UnifiedSymbolGraph.Selector,
in bundle: DocumentationBundle,
symbolIndex: inout [String: DocumentationNode],
symbolIndex: inout [String: ResolvedTopicReference],
documentationCache: [ResolvedTopicReference: DocumentationNode],
engine: DiagnosticEngine
) {
// Resolve source symbol
guard let conformingNode = symbolIndex[edge.source],
let conformingSymbol = conformingNode.semantic as? Symbol else {
// The source node for coformance relationship not found.
guard let conformingNode = symbolIndex[edge.source].flatMap({ documentationCache[$0] }),
let conformingSymbol = conformingNode.semantic as? Symbol
else {
// The source node for conformance relationship not found.
engine.emit(NodeProblem.notFound(edge.source))
return
}

// Resolve target symbol if possible
let optionalConformanceNode = symbolIndex[edge.target]
let optionalConformanceNode = symbolIndex[edge.target].flatMap { documentationCache[$0] }
let conformanceNodeReference: TopicReference

if let conformanceNode = optionalConformanceNode {
conformanceNodeReference = .successfullyResolved(conformanceNode.reference)
} else {
// Take the interface language of the target symbol
// or if external - default to the language of the current symbol.
let language = symbolIndex[edge.target]?.reference.sourceLanguage
let language = symbolIndex[edge.target]?.sourceLanguage
?? conformingNode.reference.sourceLanguage

let symbolReference = SymbolReference(edge.target, interfaceLanguage: language, symbol: symbolIndex[edge.target]?.symbol)
let symbolReference = SymbolReference(edge.target, interfaceLanguage: language, symbol: symbolIndex[edge.target].flatMap { documentationCache[$0]?.symbol })
guard let unresolved = UnresolvedTopicReference(symbolReference: symbolReference, bundle: bundle) else {
// The symbol reference format is invalid.
engine.emit(NodeProblem.invalidReference(symbolReference.path))
Expand Down Expand Up @@ -203,29 +208,32 @@ struct SymbolGraphRelationshipsBuilder {
edge: SymbolGraph.Relationship,
selector: UnifiedSymbolGraph.Selector,
in bundle: DocumentationBundle,
symbolIndex: inout [String: DocumentationNode],
symbolIndex: inout [String: ResolvedTopicReference],
documentationCache: [ResolvedTopicReference: DocumentationNode],
engine: DiagnosticEngine
) {
// Resolve source symbol
guard let childNode = symbolIndex[edge.source],
let childSymbol = childNode.semantic as? Symbol else {
guard let childNode = symbolIndex[edge.source].flatMap({ documentationCache[$0] }),
let childSymbol = childNode.semantic as? Symbol
else {
// The source node for inheritance relationship not found.
engine.emit(NodeProblem.notFound(edge.source))
return
}

// Resolve target symbol if possible
let optionalParentNode = symbolIndex[edge.target]
let optionalParentNode = symbolIndex[edge.target].flatMap { documentationCache[$0] }
let parentNodeReference: TopicReference

if let parentNode = optionalParentNode {
parentNodeReference = .successfullyResolved(parentNode.reference)
} else {
// Use the target symbol language, if external - fallback on child symbol's langauge
let language = symbolIndex[edge.target]?.symbol.map({ SourceLanguage(id: $0.identifier.interfaceLanguage) })
?? childNode.reference.sourceLanguage
// Use the target symbol language, if external - fallback on child symbol's language
let language: SourceLanguage = symbolIndex[edge.target].flatMap {
documentationCache[$0]?.symbol.map({ SourceLanguage(id: $0.identifier.interfaceLanguage) })
} ?? childNode.reference.sourceLanguage

let symbolReference = SymbolReference(edge.target, interfaceLanguage: language, symbol: symbolIndex[edge.target]?.symbol)
let symbolReference = SymbolReference(edge.target, interfaceLanguage: language, symbol: symbolIndex[edge.target].flatMap { documentationCache[$0]?.symbol })
guard let unresolved = UnresolvedTopicReference(symbolReference: symbolReference, bundle: bundle) else {
// The symbol reference format is invalid.
engine.emit(NodeProblem.invalidReference(symbolReference.path))
Expand Down Expand Up @@ -267,14 +275,16 @@ struct SymbolGraphRelationshipsBuilder {
edge: SymbolGraph.Relationship,
selector: UnifiedSymbolGraph.Selector,
in bundle: DocumentationBundle,
symbolIndex: inout [String: DocumentationNode],
symbolIndex: inout [String: ResolvedTopicReference],
documentationCache: [ResolvedTopicReference: DocumentationNode],
engine: DiagnosticEngine
) {
addProtocolRelationship(
edge: edge,
selector: selector,
in: bundle,
symbolIndex: &symbolIndex,
documentationCache: documentationCache,
engine: engine,
required: true
)
Expand All @@ -291,14 +301,16 @@ struct SymbolGraphRelationshipsBuilder {
edge: SymbolGraph.Relationship,
selector: UnifiedSymbolGraph.Selector,
in bundle: DocumentationBundle,
symbolIndex: inout [String: DocumentationNode],
symbolIndex: inout [String: ResolvedTopicReference],
documentationCache: [ResolvedTopicReference: DocumentationNode],
engine: DiagnosticEngine
) {
addProtocolRelationship(
edge: edge,
selector: selector,
in: bundle,
symbolIndex: &symbolIndex,
documentationCache: documentationCache,
engine: engine,
required: false
)
Expand All @@ -316,12 +328,15 @@ struct SymbolGraphRelationshipsBuilder {
edge: SymbolGraph.Relationship,
selector: UnifiedSymbolGraph.Selector,
in bundle: DocumentationBundle,
symbolIndex: inout [String: DocumentationNode],
symbolIndex: inout [String: ResolvedTopicReference],
documentationCache: [ResolvedTopicReference: DocumentationNode],
engine: DiagnosticEngine, required: Bool
) {
// Resolve source symbol
guard let requiredNode = symbolIndex[edge.source],
let requiredSymbol = requiredNode.semantic as? Symbol else {
guard let requiredNodeRef = symbolIndex[edge.source],
let requiredNode = documentationCache[requiredNodeRef],
let requiredSymbol = requiredNode.semantic as? Symbol
else {
// The source node for requirement relationship not found.
engine.emit(NodeProblem.notFound(edge.source))
return
Expand All @@ -341,8 +356,8 @@ struct SymbolGraphRelationshipsBuilder {
static func addInheritedDefaultImplementation(
edge: SymbolGraph.Relationship,
context: DocumentationContext,
symbolIndex: inout [String: DocumentationNode],
moduleName: String,
symbolIndex: inout [String: ResolvedTopicReference],
moduleName: String,
engine: DiagnosticEngine
) {
func setAsInheritedSymbol(origin: SymbolGraph.Relationship.SourceOrigin, for node: inout DocumentationNode, originNode: DocumentationNode?) {
Expand Down Expand Up @@ -374,13 +389,11 @@ struct SymbolGraphRelationshipsBuilder {
// verify we have the matching data in symbolIndex and documentationCache
// and add the origin data to the symbol.
if let origin = edge.mixins[SymbolGraph.Relationship.SourceOrigin.mixinKey] as? SymbolGraph.Relationship.SourceOrigin,
let node = symbolIndex[edge.source],
node.semantic is Symbol,
context.documentationCache.keys.contains(node.reference) {

let reference = symbolIndex[edge.source],
context.documentationCache[reference]?.semantic is Symbol
{
// OK to unwrap - we've verified the existence of the key above.
setAsInheritedSymbol(origin: origin, for: &symbolIndex[edge.source]!, originNode: symbolIndex[origin.identifier])
setAsInheritedSymbol(origin: origin, for: &context.documentationCache[node.reference]!, originNode: symbolIndex[origin.identifier])
setAsInheritedSymbol(origin: origin, for: &context.documentationCache[reference]!, originNode: symbolIndex[origin.identifier].flatMap { context.documentationCache[$0] })
}
}
}
Expand Down
Loading

0 comments on commit c467f80

Please sign in to comment.