Skip to content

Commit cef4147

Browse files
committed
colorize redirect routes
1 parent dc43b54 commit cef4147

File tree

13 files changed

+197
-98
lines changed

13 files changed

+197
-98
lines changed

Sources/SymbolGraphBuilder/Sources/SSGC.DocumentationSources.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,11 @@ extension SSGC.DocumentationSources
170170
Linked documentation!
171171
time loading sources : \(profiler.loadingSources)
172172
time linking : \(profiler.linking)
173-
symbols : \(graph.decls.symbols.count)
174-
redirects : \(graph.cultures.reduce(0) { $0 + $1.reexports.count })
173+
symbols : \(graph.decls.symbols.count)
174+
redirects : \
175+
\(graph.cultures.reduce(0) { $0 + $1.reexports.unhashed.count })
176+
redirects (hashed) : \
177+
\(graph.cultures.reduce(0) { $0 + $1.reexports.hashed.count })
175178
""")
176179

177180
return graph
Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import LexicalPaths
12
import Symbols
23

34
extension SSGC.ModuleIndex
@@ -6,22 +7,22 @@ extension SSGC.ModuleIndex
67
@frozen public
78
struct Feature
89
{
9-
public
10-
let lastName:String
1110
public
1211
let phylum:Phylum.Decl
12+
public
13+
let path:UnqualifiedPath
1314

14-
init(lastName:String, phylum:Phylum.Decl)
15+
init(phylum:Phylum.Decl, path:UnqualifiedPath)
1516
{
16-
self.lastName = lastName
1717
self.phylum = phylum
18+
self.path = path
1819
}
1920
}
2021
}
2122
extension SSGC.ModuleIndex.Feature
2223
{
2324
init(from decl:borrowing SSGC.Decl)
2425
{
25-
self.init(lastName: decl.path.last, phylum: decl.phylum)
26+
self.init(phylum: decl.phylum, path: decl.path)
2627
}
2728
}

Sources/SymbolGraphCompiler/SSGC.ModuleIndex.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ extension SSGC
1919
public
2020
let extensions:[SSGC.Extension]
2121
public
22-
let reexports:[Symbol.Decl]
22+
let reexports:[Symbol.Decl: Feature]
2323
public
2424
let features:[Symbol.Decl: Feature]
2525

@@ -36,7 +36,7 @@ extension SSGC
3636
resolvableLinks:UCF.ResolutionTable<UCF.CausalOverload>,
3737
declarations:[(id:Symbol.Module, decls:[Decl])],
3838
extensions:[SSGC.Extension],
39-
reexports:[Symbol.Decl],
39+
reexports:[Symbol.Decl: Feature],
4040
features:[Symbol.Decl: Feature],
4141
language:Phylum.Language? = nil,
4242
markdown:[any SSGC.ResourceFile] = [],

Sources/SymbolGraphCompiler/SSGC.TypeChecker.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,7 @@ extension SSGC.TypeChecker
616616
$0.register($1)
617617
}
618618

619-
var reexported:[Symbol.Decl] = []
619+
var reexported:[Symbol.Decl: SSGC.ModuleIndex.Feature] = [:]
620620
for decl:SSGC.DeclObject in self.declarations.all
621621
{
622622
/// The target may have been re-exported from multiple modules. Swift allows
@@ -630,7 +630,7 @@ extension SSGC.TypeChecker
630630

631631
if decl.culture != culture, decl.namespaces.contains(culture)
632632
{
633-
reexported.append(decl.id)
633+
reexported[decl.id] = .init(from: decl.value)
634634
}
635635
}
636636

Sources/SymbolGraphLinker/Router/SSGC.RouteCollisionError.swift

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,20 @@ import SourceDiagnostics
33

44
extension SSGC
55
{
6-
enum RouteCollisionError:Equatable, Error
6+
struct RouteCollisionError:Equatable, Error
77
{
8-
case hash(FNV24, [Int32])
9-
case path(Route, [Int32])
8+
let participants:[Int32]
9+
let path:Route
10+
let hash:FNV24?
11+
let redirect:Bool
12+
13+
init(participants:[Int32], path:Route, hash:FNV24?, redirect:Bool = false)
14+
{
15+
self.participants = participants
16+
self.path = path
17+
self.hash = hash
18+
self.redirect = redirect
19+
}
1020
}
1121
}
1222
extension SSGC.RouteCollisionError:Diagnostic
@@ -15,27 +25,33 @@ extension SSGC.RouteCollisionError:Diagnostic
1525

1626
func emit(summary output:inout DiagnosticOutput<Symbolicator>)
1727
{
18-
switch self
28+
if let hash:FNV24 = self.hash
1929
{
20-
case .hash(let hash, _):
21-
output[.error] = "hash collision on [\(hash)]"
22-
case .path(.main(let path), _):
23-
output[.error] = "path collision on '\(path)'"
30+
output[.fatal] = """
31+
\(self.participants.count)-way hash collision on '\(self.path)'[\(hash)]
32+
"""
33+
}
34+
else
35+
{
36+
output[.fatal] = """
37+
\(self.participants.count)-way path collision on '\(self.path)'
38+
"""
2439
}
2540
}
2641

2742
func emit(details output:inout DiagnosticOutput<Symbolicator>)
2843
{
29-
switch self
44+
if self.redirect
45+
{
46+
output[.note] = """
47+
collision occurs on @_exported redirect layer
48+
"""
49+
}
50+
for participant:Int32 in self.participants
3051
{
31-
case .hash(_, let collisions), .path(_, let collisions):
32-
for colliding:Int32 in collisions
33-
{
34-
output[.note] = """
35-
symbol (\(output.symbolicator[colliding])) \
36-
does not have a unique URL
37-
"""
38-
}
52+
output[.note] = """
53+
vertex (\(output.symbolicator[participant])) does not have a unique URL
54+
"""
3955
}
4056
}
4157
}

Sources/SymbolGraphLinker/Router/SSGC.Router.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ extension SSGC
99
{
1010
/// A type responsible for detecting URL path collisions between routes in the
1111
/// same symbol graph.
12-
struct Router
12+
struct Router<Hash, Vertex> where Hash:Hashable
1313
{
1414
private(set)
15-
var paths:[Route: InlineDictionary<FNV24?, InlineArray<Int32>>]
15+
var paths:[Route: InlineDictionary<Hash, InlineArray<Vertex>>]
1616

1717
init()
1818
{
@@ -24,7 +24,7 @@ extension SSGC.Router
2424
{
2525
subscript(namespace:Symbol.Module,
2626
path:UnqualifiedPath,
27-
phylum:Phylum.Decl) -> InlineDictionary<FNV24?, InlineArray<Int32>>
27+
phylum:Phylum.Decl) -> InlineDictionary<Hash, InlineArray<Vertex>>
2828
{
2929
_read
3030
{
@@ -35,7 +35,7 @@ extension SSGC.Router
3535
yield &self.paths[.decl(namespace, path, phylum), default: [:]]
3636
}
3737
}
38-
subscript(route:SSGC.Route) -> InlineDictionary<FNV24?, InlineArray<Int32>>
38+
subscript(route:SSGC.Route) -> InlineDictionary<Hash, InlineArray<Vertex>>
3939
{
4040
_read
4141
{

Sources/SymbolGraphLinker/SSGC.Linker.swift

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ extension SSGC
3232
private
3333
var snippets:[String: Markdown.Snippet]
3434
private
35-
var router:Router
35+
var router:Router<FNV24?, Int32>
3636
private
3737
var tables:Tables
3838

@@ -122,34 +122,69 @@ extension SSGC.Linker
122122
as: module.language ?? .swift,
123123
at: offset)
124124
}
125+
125126
// This needs to be done in two passes, because unfurling extensions can intern
126127
// additional symbols, which would otherwise create holes in the address space.
128+
self.allocateNodesFromExtensions(in: indexes)
129+
self.unfurlFeaturesFromExtensions(in: indexes)
130+
self.colorizeReexportedDeclarations(in: indexes)
131+
132+
// We attach snippets first, because they can be referenced by the markdown
133+
// supplements. This works even if the snippet captions contain references to articles,
134+
// because we only eagarly inline snippet captions as markdown AST nodes; codelink
135+
// resolution does not take place until we link the written documentation.
136+
try self.attach(snippets: snippets, projectRoot: projectRoot)
137+
138+
for (offset, module):(Int, SSGC.ModuleIndex) in zip(self.contexts.indices, indexes)
139+
{
140+
try self.attach(markdown: module.markdown, at: offset)
141+
}
142+
}
143+
144+
private mutating
145+
func allocateNodesFromExtensions(in indexes:[SSGC.ModuleIndex])
146+
{
127147
for module:SSGC.ModuleIndex in indexes
128148
{
129149
for node:SSGC.Extension in module.extensions
130150
{
131151
self.tables.allocate(decl: node.extendee.id)
132152
}
133153
}
154+
}
134155

156+
private mutating
157+
func unfurlFeaturesFromExtensions(in indexes:[SSGC.ModuleIndex])
158+
{
135159
for (offset, module):(Int, SSGC.ModuleIndex) in zip(self.contexts.indices, indexes)
136160
{
137-
self.unfurl(reexported: module.reexports, by: offset)
138161
self.unfurl(extensions: module.extensions,
139162
featuresBySymbol: module.features,
140163
at: offset)
141164
}
165+
}
142166

143-
// We attach snippets first, because they can be referenced by the markdown
144-
// supplements. This works even if the snippet captions contain references to articles,
145-
// because we only eagarly inline snippet captions as markdown AST nodes; codelink
146-
// resolution does not take place until we link the written documentation.
147-
try self.attach(snippets: snippets, projectRoot: projectRoot)
148-
167+
private mutating
168+
func colorizeReexportedDeclarations(in indexes:[SSGC.ModuleIndex])
169+
{
170+
var redirect:SSGC.Router<FNV24, (Int32, Int)> = .init()
149171
for (offset, module):(Int, SSGC.ModuleIndex) in zip(self.contexts.indices, indexes)
150172
{
151-
try self.attach(markdown: module.markdown, at: offset)
173+
// @_exported can duplicate a truly staggering number of declarations. To prevent
174+
// this from creating a lot of almost-empty declaration nodes, we only track
175+
// modules that re-export symbols from the same package.
176+
for (id, feature):(Symbol.Decl, SSGC.ModuleIndex.Feature) in module.reexports
177+
{
178+
if let i:Int32 = self.tables.citizen(id)
179+
{
180+
let hash:FNV24 = .decl(id)
181+
redirect[module.id, feature.path, feature.phylum][hash, default: []]
182+
.append((i, offset))
183+
}
184+
}
152185
}
186+
187+
self.tables.graph.colorize(reexports: redirect.paths, with: &self.tables.diagnostics)
153188
}
154189
}
155190
extension SSGC.Linker
@@ -226,21 +261,6 @@ extension SSGC.Linker
226261
}
227262
}
228263

229-
private mutating
230-
func unfurl(reexported:[Symbol.Decl], by offset:Int)
231-
{
232-
// @_exported can duplicate a truly staggering number of declarations. To prevent this
233-
// from creating a lot of almost-empty declaration nodes, we only track modules that
234-
// re-export symbols from the same package.
235-
for id:Symbol.Decl in reexported
236-
{
237-
if let reexported:Int32 = self.tables.citizen(id)
238-
{
239-
self.tables.graph.cultures[offset].reexports.append(reexported)
240-
}
241-
}
242-
}
243-
244264
/// This function also exposes any features manifested by the extensions for
245265
/// codelink resolution.
246266
///
@@ -288,7 +308,7 @@ extension SSGC.Linker
288308
hash: .decl(.init(id, self: $0.extendee.id)),
289309
id: id)
290310

291-
self.tables.packageLinks[namespace, $0.extendee.path, feature.lastName]
311+
self.tables.packageLinks[namespace, $0.extendee.path, feature.path.last]
292312
.append(featureAlias)
293313
}
294314

0 commit comments

Comments
 (0)