Skip to content

Commit 537a4a1

Browse files
authored
Merge pull request #378 from tayloraswift/empowered-signatures
introduce function signature based overload disambiguation
2 parents ebb084e + 18cbe7f commit 537a4a1

File tree

71 files changed

+2159
-842
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+2159
-842
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
/Assets/secrets/
1717
/Assets/icons/*.jpg
1818

19+
/TestModules/SymbolGraphs/*.json
1920
/TestModules/*.json
2021
/TestPackages/*.bson
2122
/TypeScript/node_modules

Package.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,7 @@ let package:Package = .init(
415415
.target(name: "SymbolGraphParts",
416416
dependencies: [
417417
.target(name: "LexicalPaths"),
418+
.target(name: "LinkResolution"),
418419
// This is the point where the symbol graph compiler becomes infected with a
419420
// (non-macro) SwiftSyntax dependency.
420421
//
@@ -519,8 +520,8 @@ let package:Package = .init(
519520

520521
.target(name: "UnidocLinker",
521522
dependencies: [
522-
.target(name: "LinkResolution"),
523523
.target(name: "MarkdownRendering"),
524+
.target(name: "SourceDiagnostics"),
524525
.target(name: "UnidocLinking"),
525526
]),
526527

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import UCF
2+
3+
extension UCF
4+
{
5+
@frozen public
6+
struct Autograph:Equatable, Sendable
7+
{
8+
public
9+
let inputs:[String]
10+
public
11+
let output:[String]
12+
13+
@inlinable public
14+
init(inputs:[String], output:[String])
15+
{
16+
self.inputs = inputs
17+
self.output = output
18+
}
19+
}
20+
}
21+
extension UCF.Autograph
22+
{
23+
static func ~= (filter:UCF.SignatureFilter, self:Self) -> Bool
24+
{
25+
if let inputs:[String?] = filter.inputs
26+
{
27+
guard self.inputs.count == inputs.count
28+
else
29+
{
30+
return false
31+
}
32+
for case (let required, let provided?) in zip(self.inputs, inputs)
33+
{
34+
if required != provided
35+
{
36+
return false
37+
}
38+
}
39+
}
40+
41+
if let output:[String?] = filter.output
42+
{
43+
guard self.output.count == output.count
44+
else
45+
{
46+
return false
47+
}
48+
for case (let required, let provided?) in zip(self.output, output)
49+
{
50+
if required != provided
51+
{
52+
return false
53+
}
54+
}
55+
}
56+
57+
return true
58+
}
59+
}

Sources/LinkResolution/UCF.CausalOverload.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,23 @@ extension UCF
1818

1919
public
2020
let documented:Bool
21+
public
22+
let autograph:Autograph?
2123

2224
@inlinable public
2325
init(phylum:Phylum.Decl,
2426
decl:Symbol.Decl,
2527
heir:Symbol.Decl?,
2628
hash:FNV24,
27-
documented:Bool)
29+
documented:Bool,
30+
autograph:Autograph?)
2831
{
2932
self.phylum = phylum
3033
self.decl = decl
3134
self.heir = heir
3235
self.hash = hash
3336
self.documented = documented
37+
self.autograph = autograph
3438
}
3539
}
3640
}

Sources/LinkResolution/UCF.PackageOverload.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ extension UCF
1818

1919
public
2020
let documented:Bool
21+
public
22+
let autograph:Autograph?
2123
/// Used for display purposes. This is not necessarily the symbol from which the
2224
/// ``hash`` was computed.
2325
public
@@ -29,13 +31,15 @@ extension UCF
2931
heir:Int32?,
3032
hash:FNV24,
3133
documented:Bool,
34+
autograph:Autograph?,
3235
id:Symbol.Decl)
3336
{
3437
self.phylum = phylum
3538
self.decl = decl
3639
self.heir = heir
3740
self.hash = hash
3841
self.documented = documented
42+
self.autograph = autograph
3943
self.id = id
4044
}
4145
}

Sources/LinkResolution/UCF.ResolvableOverload.swift

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ extension UCF
77
public
88
protocol ResolvableOverload:Identifiable<Symbol.Decl>, Sendable
99
{
10+
var autograph:Autograph? { get }
1011
var phylum:Phylum.Decl { get }
1112
var hash:FNV24 { get }
1213

@@ -71,12 +72,27 @@ extension UCF.ResolvableOverload
7172

7273
switch suffix
7374
{
74-
case .legacy(let filter, nil): return filter ~= self.phylum
75-
case .legacy(_, let hash?): return hash == self.hash
76-
case .hash(let hash): return hash == self.hash
77-
case .filter(let filter): return filter ~= self.phylum
78-
// TODO: unimplemented
79-
case .pattern: return true
75+
case .legacy(let filter, nil):
76+
return filter ~= self.phylum
77+
78+
case .legacy(_, let hash?):
79+
return hash == self.hash
80+
81+
case .hash(let hash):
82+
return hash == self.hash
83+
84+
case .keywords(let filter):
85+
return filter ~= self.phylum
86+
87+
case .signature(let filter):
88+
if let autograph:UCF.Autograph = self.autograph
89+
{
90+
return filter ~= autograph
91+
}
92+
else
93+
{
94+
return false
95+
}
8096
}
8197
}
8298
}

Sources/MarkdownPluginSwift/Signatures/Signature.Abridged (ext).swift

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,13 @@ extension Signature.Abridged
5858
let text:String = .init(decoding: utf8[range], as: Unicode.UTF8.self)
5959
switch (color, text, depth)
6060
{
61-
case (.keyword, "subscript", .toplevel?),
62-
(.keyword, "deinit", .toplevel?),
63-
(.keyword, "init", .toplevel?),
64-
(.identifier, _, .toplevel?):
65-
$0[.identifier] = text
66-
67-
case (_, _, _):
68-
$0 += text
61+
case (.keyword, "subscript", .toplevel?): $0[.identifier] = text
62+
case (.keyword, "deinit", .toplevel?): $0[.identifier] = text
63+
case (.keyword, "init", .toplevel?): $0[.identifier] = text
64+
case (.identifier, _, .toplevel?): $0[.identifier] = text
65+
// See note in `Signature.Expanded (ext).swift`
66+
case (.type, "`Self`", _): $0[.type] = "Self"
67+
case (_, _, _): $0 += text
6968
}
7069
}
7170
}

Sources/MarkdownPluginSwift/Signatures/Signature.Expanded (ext).swift

Lines changed: 62 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ extension Signature.Expanded
55
{
66
@inlinable public
77
init(_ fragments:__shared some Collection<Signature<Scalar>.Fragment>,
8-
keywords:inout InterestingKeywords)
8+
sugarDictionary:Scalar,
9+
sugarArray:Scalar,
10+
sugarOptional:Scalar,
11+
landmarks:inout SignatureLandmarks)
912
{
1013
var utf8:[UInt8] = []
1114
utf8.reserveCapacity(fragments.reduce(0) { $0 + $1.spelling.utf8.count })
@@ -26,10 +29,22 @@ extension Signature.Expanded
2629
}
2730
}
2831

32+
let sugarMap:SignatureSyntax.SugarMap = linkTargets.reduce(into: .init())
33+
{
34+
switch $1.value
35+
{
36+
case sugarArray: $0.arrays.insert($1.key)
37+
case sugarDictionary: $0.dictionaries.insert($1.key)
38+
case sugarOptional: $0.optionals.insert($1.key)
39+
default: break
40+
}
41+
}
42+
2943
self.init(utf8: utf8,
44+
sugarMap: sugarMap,
3045
linkBoundaries: linkBoundaries,
3146
linkTargets: &linkTargets,
32-
keywords: &keywords)
47+
landmarks: &landmarks)
3348

3449
if !linkTargets.isEmpty
3550
{
@@ -51,29 +66,33 @@ extension Signature.Expanded
5166
init(_ string:String,
5267
linkBoundaries:borrowing [Int] = [])
5368
{
54-
var ignored:InterestingKeywords = .init()
55-
self.init(string, linkBoundaries: linkBoundaries, keywords: &ignored)
69+
var ignored:SignatureLandmarks = .init()
70+
self.init(string, linkBoundaries: linkBoundaries, landmarks: &ignored)
5671
}
5772

5873
@inlinable @_spi(testable) public
5974
init(_ string:String,
6075
linkBoundaries:borrowing [Int] = [],
61-
keywords:inout InterestingKeywords)
76+
landmarks:inout SignatureLandmarks)
6277
{
6378
var empty:[Int: Scalar] = [:]
6479
self.init(utf8: [UInt8].init(string.utf8),
6580
linkBoundaries: linkBoundaries,
6681
linkTargets: &empty,
67-
keywords: &keywords)
82+
landmarks: &landmarks)
6883
}
6984

7085
@inlinable
7186
init(utf8:[UInt8],
87+
sugarMap:SignatureSyntax.SugarMap = .init(),
7288
linkBoundaries:borrowing [Int],
7389
linkTargets:inout [Int: Scalar],
74-
keywords:inout InterestingKeywords)
90+
landmarks:inout SignatureLandmarks)
7591
{
76-
let signature:SignatureSyntax = utf8.withUnsafeBufferPointer { .expanded($0) }
92+
let signature:SignatureSyntax = utf8.withUnsafeBufferPointer
93+
{
94+
.expanded($0, sugaring: sugarMap, landmarks: &landmarks)
95+
}
7796
var references:[Scalar: Int] = [:]
7897
var referents:[Scalar] = []
7998

@@ -97,8 +116,8 @@ extension Signature.Expanded
97116
{
98117
switch String.init(decoding: utf8[range], as: Unicode.UTF8.self)
99118
{
100-
case "@attached": keywords.attached = true
101-
case "@freestanding": keywords.freestanding = true
119+
case "@attached": landmarks.keywords.attached = true
120+
case "@freestanding": landmarks.keywords.freestanding = true
102121
default: break
103122
}
104123
}
@@ -108,20 +127,21 @@ extension Signature.Expanded
108127
// other way to detect them besides inspecting token text!
109128
switch String.init(decoding: utf8[range], as: Unicode.UTF8.self)
110129
{
111-
case "actor": keywords.actor = true
112-
case "class": keywords.class = true
113-
case "final": keywords.final = true
130+
case "actor": landmarks.keywords.actor = true
131+
case "class": landmarks.keywords.class = true
132+
case "final": landmarks.keywords.final = true
114133
default: break
115134
}
116135
}
117136

118137
fallthrough
119138

120139
case .text(let range, let color?, _):
140+
let referent:Scalar? = linkTargets.removeValue(forKey: range.lowerBound)
141+
121142
$0[color]
122143
{
123-
if let referent:Scalar = linkTargets.removeValue(
124-
forKey: range.lowerBound)
144+
if let referent:Scalar
125145
{
126146
$0[.href] =
127147
{
@@ -139,9 +159,34 @@ extension Signature.Expanded
139159
} (&references[referent])
140160
}
141161
}
142-
content:
162+
content:
143163
{
144-
$0 += utf8[range]
164+
// This is an ugly, ugly hack to work around the upstream bug in
165+
// lib/SymbolGraphGen described here:
166+
// https://github.com/swiftlang/swift/issues/78343
167+
//
168+
// This hack is still **correct** if the declaration shadows the `Self`
169+
// type, because lib/SymbolGraphGen will emit such a token without the
170+
// extraneous backticks.
171+
if case .type = color,
172+
case [
173+
0x60, // '`'
174+
0x53, // 'S'
175+
0x65, // 'e'
176+
0x6C, // 'l'
177+
0x66, // 'f'
178+
0x60, // '`'
179+
] = utf8[range]
180+
{
181+
let i:Int = utf8.index(after: range.lowerBound)
182+
let j:Int = utf8.index(before: range.upperBound)
183+
184+
$0 += utf8[i ..< j]
185+
}
186+
else
187+
{
188+
$0 += utf8[range]
189+
}
145190
}
146191
}
147192
}

Sources/MarkdownPluginSwift/Signatures/Signature.Expanded.InterestingKeywords.swift renamed to Sources/MarkdownPluginSwift/Signatures/SignatureLandmarks.InterestingKeywords.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Signatures
22

3-
extension Signature.Expanded
3+
extension SignatureLandmarks
44
{
55
@frozen public
66
struct InterestingKeywords
@@ -16,7 +16,7 @@ extension Signature.Expanded
1616
public
1717
var freestanding:Bool
1818

19-
@inlinable public
19+
@inlinable
2020
init(actor:Bool = false,
2121
attached:Bool = false,
2222
`class`:Bool = false,
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import Signatures
2+
3+
@frozen public
4+
struct SignatureLandmarks
5+
{
6+
public
7+
var keywords:InterestingKeywords
8+
public
9+
var inputs:[String]
10+
public
11+
var output:[String]
12+
13+
@inlinable public
14+
init()
15+
{
16+
self.keywords = .init()
17+
self.inputs = []
18+
self.output = []
19+
}
20+
}

0 commit comments

Comments
 (0)