Skip to content

Commit b25bbcb

Browse files
authored
Merge pull request #24 from tayloraswift/fix-symbol-graph-builder-bugs
Fix symbol graph builder bugs
2 parents 833f534 + 40ee5aa commit b25bbcb

File tree

13 files changed

+303
-107
lines changed

13 files changed

+303
-107
lines changed

Package.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,8 +458,9 @@ let package:Package = .init(
458458
.executableTarget(name: "UnidocBuild", dependencies:
459459
[
460460
.target(name: "HTTPClient"),
461-
.target(name: "UnidocAutomation"),
462461
.target(name: "SymbolGraphBuilder"),
462+
.target(name: "UnidocAutomation"),
463+
.target(name: "UnidocLinker"),
463464
]),
464465

465466
.executableTarget(name: "UnidocServer", dependencies:

Sources/CodelinkTests/Main.swift

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -298,41 +298,49 @@ enum Main:SyncTests
298298
tests.expect(codelink.path.components ..? ["Sloth", "update(_:)"])
299299
tests.expect(codelink.hash?.value ==? .init("4ko57", radix: 36))
300300
}
301-
if let tests:TestGroup = tests / "codelinks" / "legacy-docc"
301+
if let tests:TestGroup = tests / "Codelinks" / "LegacyDocC"
302302
{
303-
if let tests:TestGroup = tests / "slashes",
303+
if let tests:TestGroup = tests / "Slashes",
304304
let codelink:Codelink = .parse("Sloth/Color", for: tests)
305305
{
306306
tests.expect(nil: codelink.filter)
307307
tests.expect(nil: codelink.scope)
308308
tests.expect(codelink.path.components ..? ["Sloth", "Color"])
309309
tests.expect(nil: codelink.hash)
310310
}
311-
if let tests:TestGroup = tests / "filter",
311+
if let tests:TestGroup = tests / "TrailingSlashes",
312+
let codelink:Codelink = .parse("Sloth/Color/", for: tests)
313+
{
314+
tests.expect(nil: codelink.filter)
315+
tests.expect(nil: codelink.scope)
316+
tests.expect(codelink.path.components ..? ["Sloth", "Color"])
317+
tests.expect(nil: codelink.hash)
318+
}
319+
if let tests:TestGroup = tests / "Filter",
312320
let codelink:Codelink = .parse("Sloth/Color-swift.enum", for: tests)
313321
{
314322
tests.expect(codelink.filter ==? .enum)
315323
tests.expect(nil: codelink.scope)
316324
tests.expect(codelink.path.components ..? ["Sloth", "Color"])
317325
tests.expect(nil: codelink.hash)
318326
}
319-
if let tests:TestGroup = tests / "hash",
327+
if let tests:TestGroup = tests / "Hash",
320328
let codelink:Codelink = .parse("Sloth/update(_:)-4ko57", for: tests)
321329
{
322330
tests.expect(nil: codelink.filter)
323331
tests.expect(nil: codelink.scope)
324332
tests.expect(codelink.path.components ..? ["Sloth", "update(_:)"])
325333
tests.expect(codelink.hash?.value ==? .init("4KO57", radix: 36))
326334
}
327-
if let tests:TestGroup = tests / "hash" / "minus",
335+
if let tests:TestGroup = tests / "Hash" / "Minus",
328336
let codelink:Codelink = .parse("Sloth/-(_:)-4ko57", for: tests)
329337
{
330338
tests.expect(nil: codelink.filter)
331339
tests.expect(nil: codelink.scope)
332340
tests.expect(codelink.path.components ..? ["Sloth", "-(_:)"])
333341
tests.expect(codelink.hash?.value ==? .init("4KO57", radix: 36))
334342
}
335-
if let tests:TestGroup = tests / "hash" / "slinging" / "slasher",
343+
if let tests:TestGroup = tests / "Hash" / "Slinging" / "Slasher",
336344
let codelink:Codelink = .parse("Sloth//(_:)-4ko57", for: tests)
337345
{
338346
tests.expect(nil: codelink.filter)

Sources/Codelinks/Codelink.Path.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,21 @@ extension Codelink.Path
3737
case (_, "-", nil):
3838
format = .legacy
3939
suffix = .init(.init(codepoints))
40-
// we know we already consumed all remaining input
40+
// We know we already consumed all remaining input.
4141
return
4242

4343
case (.nominal(_, nil), "/", nil):
4444
format = .legacy
45-
fallthrough
45+
46+
// Tolerate trailing slash.
47+
if codepoints.isEmpty
48+
{
49+
return
50+
}
51+
else
52+
{
53+
fallthrough
54+
}
4655

4756
case (.nominal(_, nil), ".", _):
4857
if let next:Component = .init(parsing: &codepoints)

Sources/HTTPClient/ClientInterfaceHandler.swift

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ extension ClientInterfaceHandler:ChannelOutboundHandler
4141
typealias OutboundIn =
4242
(
4343
owner:AsyncThrowingStream<HTTP2Client.Facet, any Error>.Continuation,
44-
batch:[HPACKHeaders]
44+
batch:[HTTP2Client.Request]
4545
)
4646

4747
func errorCaught(context:ChannelHandlerContext, error:any Error)
@@ -53,14 +53,14 @@ extension ClientInterfaceHandler:ChannelOutboundHandler
5353
func write(context:ChannelHandlerContext, data:NIOAny, promise:EventLoopPromise<Void>?)
5454
{
5555
let owner:AsyncThrowingStream<HTTP2Client.Facet, any Error>.Continuation
56-
let batch:[HPACKHeaders]
56+
let batch:[HTTP2Client.Request]
5757

5858
(owner, batch) = self.unwrapOutboundIn(data)
5959

6060
self.owner?.finish()
6161
self.owner = owner
6262

63-
for request:HPACKHeaders in batch
63+
for request:HTTP2Client.Request in batch
6464
{
6565
self.multiplexer.createStreamChannel
6666
{
@@ -71,11 +71,27 @@ extension ClientInterfaceHandler:ChannelOutboundHandler
7171
switch $0
7272
{
7373
case .success(let stream):
74-
stream.writeAndFlush(HTTP2Frame.FramePayload.headers(
75-
HTTP2Frame.FramePayload.Headers.init(
76-
headers: request,
77-
endStream: true)),
78-
promise: nil)
74+
if let body:ByteBuffer = request.body
75+
{
76+
stream.write(HTTP2Frame.FramePayload.headers(
77+
HTTP2Frame.FramePayload.Headers.init(
78+
headers: request.headers,
79+
endStream: false)),
80+
promise: nil)
81+
stream.writeAndFlush(HTTP2Frame.FramePayload.data(
82+
HTTP2Frame.FramePayload.Data.init(
83+
data: .byteBuffer(body),
84+
endStream: true)),
85+
promise: nil)
86+
}
87+
else
88+
{
89+
stream.writeAndFlush(HTTP2Frame.FramePayload.headers(
90+
HTTP2Frame.FramePayload.Headers.init(
91+
headers: request.headers,
92+
endStream: true)),
93+
promise: nil)
94+
}
7995

8096
case .failure(let error):
8197
owner.finish(throwing: error)

Sources/HTTPClient/HTTP2Client.Connection.swift

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,46 @@ extension HTTP2Client.Connection:Sendable
2525
{
2626
}
2727
extension HTTP2Client.Connection
28+
{
29+
@inlinable public
30+
func buffer(string:Substring) -> ByteBuffer
31+
{
32+
self.channel.allocator.buffer(substring: string)
33+
}
34+
35+
@inlinable public
36+
func buffer(string:String) -> ByteBuffer
37+
{
38+
self.channel.allocator.buffer(string: string)
39+
}
40+
41+
@inlinable public
42+
func buffer(bytes:[UInt8]) -> ByteBuffer
43+
{
44+
self.channel.allocator.buffer(bytes: bytes)
45+
}
46+
}
47+
extension HTTP2Client.Connection
2848
{
2949
public
3050
func fetch(_ request:__owned HPACKHeaders) async throws -> HTTP2Client.Facet
51+
{
52+
try await self.fetch(.init(headers: request, body: nil))
53+
}
54+
public
55+
func fetch(_ request:__owned HTTP2Client.Request) async throws -> HTTP2Client.Facet
3156
{
3257
try await self.fetch(reducing: [request], into: .init()) { $0 = $1 }
3358
}
3459

3560
public
36-
func fetch(_ batch:__owned [HPACKHeaders]) async throws -> [HTTP2Client.Facet]
61+
func fetch(_ batch:__owned [HTTP2Client.Request]) async throws -> [HTTP2Client.Facet]
3762
{
3863
try await self.fetch(reducing: batch, into: []) { $0.append($1) }
3964
}
4065

4166
@inlinable public
42-
func fetch<Response>(reducing batch:__owned [HPACKHeaders],
67+
func fetch<Response>(reducing batch:__owned [HTTP2Client.Request],
4368
into initial:__owned Response,
4469
with combine:(inout Response, HTTP2Client.Facet) throws -> ()) async throws -> Response
4570
{

Sources/HTTPClient/HTTP2Client.Facet.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,12 @@ extension HTTP2Client.Facet
5757
self.buffers.append(buffer)
5858
return frame.endStream
5959
}
60-
case _:
60+
61+
case .rstStream, .pushPromise, .goAway:
6162
break
63+
64+
case _:
65+
return false
6266
}
6367

6468
throw HTTP2Client.UnexpectedFrameError.init(payload)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import NIOCore
2+
import NIOHPACK
3+
4+
extension HTTP2Client
5+
{
6+
@frozen public
7+
struct Request:Sendable
8+
{
9+
public
10+
var headers:HPACKHeaders
11+
public
12+
var body:ByteBuffer?
13+
14+
@inlinable public
15+
init(headers:HPACKHeaders = [:], body:ByteBuffer?)
16+
{
17+
self.headers = headers
18+
self.body = body
19+
}
20+
}
21+
}

Sources/HTTPClient/HTTP2Client.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ extension HTTP2Client
7979
{
8080
try await self.connect { try await $0.fetch(request) }
8181
}
82+
public
83+
func fetch(_ request:__owned HTTP2Client.Request) async throws -> Facet
84+
{
85+
try await self.connect { try await $0.fetch(request) }
86+
}
8287
}
8388
extension HTTP2Client
8489
{

Sources/SymbolGraphBuilder/Artifacts/Artifacts.CultureError.swift

Lines changed: 0 additions & 21 deletions
This file was deleted.

Sources/SymbolGraphBuilder/Artifacts/Artifacts.swift

Lines changed: 37 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,14 @@ extension Artifacts
7676
label = "\(language) module"
7777
}
7878

79-
print("Dumping symbols for module '\(sources.module.id)' (\(label))")
79+
switch sources.module.id
80+
{
81+
case "_CertificateInternals": // unbuildable, from swift-certificates 1.0.0
82+
continue
83+
84+
case let name:
85+
print("Dumping symbols for module '\(name)' (\(label))")
86+
}
8087

8188
var arguments:[String] =
8289
[
@@ -103,55 +110,47 @@ extension Artifacts
103110
try await SystemProcess.init(command: "swift", arguments: arguments)()
104111
}
105112

106-
let blacklisted:Set<ModuleIdentifier> =
107-
[
108-
"CDispatch", // too low-level
109-
"CFURLSessionInterface", // too low-level
110-
"CFXMLInterface", // too low-level
111-
"CoreFoundation", // too low-level
112-
"Glibc", // linux-gnu specific
113-
"SwiftGlibc", // linux-gnu specific
114-
"SwiftOnoneSupport", // contains no symbols
115-
"SwiftOverlayShims", // too low-level
116-
"SwiftShims", // contains no symbols
117-
"XCTest", // site policy
118-
"_Builtin_intrinsics", // contains only one symbol, free(_:)
119-
"_Builtin_stddef_max_align_t", // contains only two symbols
120-
"_InternalStaticMirror", // unbuildable
121-
"_InternalSwiftScan", // unbuildable
122-
"_SwiftConcurrencyShims", // contains only two symbols
123-
"std", // unbuildable
124-
]
125-
126113
var parts:[ModuleIdentifier: [SymbolGraphPart.ID]] = [:]
127114
for part:Result<FilePath.Component, any Error> in output.path.directory
128115
{
129116
// We don’t want to *parse* the JSON yet to discover the culture,
130117
// because the JSON can be very large, and parsing JSON is very
131118
// expensive (compared to parsing BSON). So we trust that the file
132119
// name is correct and indicates what is contained within the file.
133-
if let id:SymbolGraphPart.ID = .init("\(try part.get())"),
134-
!blacklisted.contains(id.namespace)
120+
if let id:SymbolGraphPart.ID = .init("\(try part.get())")
135121
{
136-
parts[id.culture, default: []].append(id)
122+
switch id.namespace
123+
{
124+
case "CDispatch", // too low-level
125+
"CFURLSessionInterface", // too low-level
126+
"CFXMLInterface", // too low-level
127+
"CoreFoundation", // too low-level
128+
"Glibc", // linux-gnu specific
129+
"SwiftGlibc", // linux-gnu specific
130+
"SwiftOnoneSupport", // contains no symbols
131+
"SwiftOverlayShims", // too low-level
132+
"SwiftShims", // contains no symbols
133+
"XCTest", // site policy
134+
"_Builtin_intrinsics", // contains only one symbol, free(_:)
135+
"_Builtin_stddef_max_align_t", // contains only two symbols
136+
"_InternalStaticMirror", // unbuildable
137+
"_InternalSwiftScan", // unbuildable
138+
"_SwiftConcurrencyShims", // contains only two symbols
139+
"std": // unbuildable
140+
continue
141+
142+
default:
143+
parts[id.culture, default: []].append(id)
144+
}
137145
}
138146
}
139147

140-
return try modules.map
148+
return modules.map
141149
{
142-
let parts:[SymbolGraphPart.ID]? = parts[$0.module.id]
143-
if case .swift = $0.language,
144-
case nil = parts
145-
{
146-
throw Artifacts.CultureError.empty($0.module.id)
147-
}
148-
else
149-
{
150-
return .init($0.module,
151-
articles: $0.articles,
152-
artifacts: output.path,
153-
parts: parts ?? [])
154-
}
150+
.init($0.module,
151+
articles: $0.articles,
152+
artifacts: output.path,
153+
parts: parts[$0.module.id, default: []])
155154
}
156155
}
157156
}

0 commit comments

Comments
 (0)