Skip to content

Commit c4415d6

Browse files
authored
Merge pull request #349 from tayloraswift/discriminate-empty-trailing-parentheses
Discriminate empty trailing parentheses
2 parents 46e8700 + 9fa6b2b commit c4415d6

10 files changed

+158
-38
lines changed
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import UCF
2+
3+
extension UCF
4+
{
5+
struct Predicate
6+
{
7+
let suffix:Selector.Suffix?
8+
let seal:Selector.Seal?
9+
10+
init(suffix:Selector.Suffix?, seal:Selector.Seal?)
11+
{
12+
self.suffix = suffix
13+
self.seal = seal
14+
}
15+
}
16+
}

Sources/LinkResolution/UCF.ResolutionTable.Search.swift

+9-19
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ extension UCF.ResolutionTable
77
struct Search
88
{
99
private
10-
let predicate:UCF.Selector.Suffix?
10+
let predicate:UCF.Predicate
1111

1212
private
1313
var selected:[Symbol.Decl: Overload]
1414
private
1515
var rejected:[Symbol.Decl: Overload]
1616

17-
init(matching predicate:UCF.Selector.Suffix?)
17+
init(matching predicate:UCF.Predicate)
1818
{
1919
self.predicate = predicate
2020
self.selected = [:]
@@ -29,26 +29,16 @@ extension UCF.ResolutionTable.Search
2929
{
3030
// Because of the way `@_exported` paths are represented in the search tree, it is
3131
// possible to encounter the same overload multiple times, due to namespace inference
32-
if let predicate:UCF.Selector.Suffix = self.predicate
32+
for overload:Overload in candidates
3333
{
34-
for overload:Overload in candidates
34+
guard self.predicate ~= overload
35+
else
3536
{
36-
guard predicate ~= overload
37-
else
38-
{
39-
self.rejected[overload.id] = overload
40-
continue
41-
}
42-
43-
self.selected[overload.id] = overload
44-
}
45-
}
46-
else
47-
{
48-
for overload:Overload in candidates
49-
{
50-
self.selected[overload.id] = overload
37+
self.rejected[overload.id] = overload
38+
continue
5139
}
40+
41+
self.selected[overload.id] = overload
5242
}
5343
}
5444

Sources/LinkResolution/UCF.ResolutionTable.swift

+4-5
Original file line numberDiff line numberDiff line change
@@ -99,17 +99,16 @@ extension UCF.ResolutionTable
9999
extension UCF.ResolutionTable
100100
{
101101
public
102-
func resolve(qualified path:UCF.Selector.Path,
103-
matching suffix:UCF.Selector.Suffix?) -> UCF.Resolution<Overload>
102+
func resolve(qualified:UCF.Selector) -> UCF.Resolution<Overload>
104103
{
105-
var search:Search = .init(matching: suffix)
106-
return self.resolve(qualified: path, with: &search)
104+
var search:Search = .init(matching: qualified.predicate)
105+
return self.resolve(qualified: qualified.path, with: &search)
107106
}
108107

109108
func resolve(_ selector:UCF.Selector,
110109
in scope:UCF.ResolutionScope) -> UCF.Resolution<Overload>
111110
{
112-
var search:Search = .init(matching: selector.suffix)
111+
var search:Search = .init(matching: selector.predicate)
113112

114113
if case .relative = selector.base
115114
{

Sources/LinkResolution/UCF.ResolvableOverload.swift

+57-5
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,66 @@ extension UCF
1515
}
1616
extension UCF.ResolvableOverload
1717
{
18-
static func ~= (suffix:UCF.Selector.Suffix, self:Self) -> Bool
18+
static func ~= (predicate:UCF.Predicate, self:Self) -> Bool
1919
{
20+
if case nil = predicate.seal
21+
{
22+
// Macros are currently the only kind of declaration that *must* be spelled with
23+
// trailing parentheses.
24+
switch self.phylum
25+
{
26+
case .actor: break
27+
case .associatedtype: break
28+
case .case: break
29+
case .class: break
30+
case .deinitializer: break
31+
case .enum: break
32+
case .func: break
33+
case .initializer: break
34+
case .macro: return false
35+
case .operator: break
36+
case .protocol: break
37+
case .struct: break
38+
case .subscript: break
39+
case .typealias: break
40+
case .var: break
41+
}
42+
}
43+
else
44+
{
45+
switch self.phylum
46+
{
47+
case .actor: return false
48+
case .associatedtype: return false
49+
case .case: return false
50+
case .class: return false
51+
case .deinitializer: return false
52+
case .enum: return false
53+
case .func: break
54+
case .initializer: break
55+
case .macro: break
56+
case .operator: break
57+
case .protocol: return false
58+
case .struct: return false
59+
case .subscript: break
60+
case .typealias: return false
61+
case .var: return false
62+
}
63+
}
64+
65+
guard
66+
let suffix:UCF.Selector.Suffix = predicate.suffix
67+
else
68+
{
69+
return true
70+
}
71+
2072
switch suffix
2173
{
22-
case .legacy(let filter, nil): filter ~= self.phylum
23-
case .legacy(_, let hash?): hash == self.hash
24-
case .hash(let hash): hash == self.hash
25-
case .filter(let filter): filter ~= self.phylum
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
2678
}
2779
}
2880
}

Sources/LinkResolution/UCF.Selector (ext).swift

+7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
import FNV1
22
import UCF
33

4+
extension UCF.Selector
5+
{
6+
var predicate:UCF.Predicate
7+
{
8+
.init(suffix: self.suffix, seal: self.path.seal)
9+
}
10+
}
411
// TODO: this does not belong in this module
512
extension UCF.Selector
613
{

Sources/SymbolGraphLinker/Resolution/SSGC.OutlineResolver.swift

+4-6
Original file line numberDiff line numberDiff line change
@@ -144,13 +144,11 @@ extension SSGC.OutlineResolver
144144
else
145145
{
146146
return nil
147-
}
147+
}
148148

149-
switch self.scopes.causalURLs.resolve(
150-
qualified: translatable.path,
151-
matching: translatable.suffix)
152-
{
153-
case .module(let module):
149+
switch self.scopes.causalURLs.resolve(qualified: translatable)
150+
{
151+
case .module(let module):
154152
// Unidoc linker doesn’t currently support `symbol` outlines that are not
155153
// declarations, so for now we just synthesize a normal vertex outline.
156154
let text:SymbolGraph.OutlineText = .init(path: "\(module)", fragment: nil)

Sources/UCF/Codelinks/UCF.Selector.Path.swift

+12-1
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,26 @@ extension UCF.Selector
99
/// The index of the first visible component in this path.
1010
public
1111
var fold:Int
12+
public
13+
var seal:Seal?
1214

1315
@inlinable public
14-
init(components:[String] = [], fold:Int = 0)
16+
init(components:[String] = [], fold:Int = 0, seal:Seal? = nil)
1517
{
1618
self.components = components
1719
self.fold = fold
20+
self.seal = seal
1821
}
1922
}
2023
}
2124
extension UCF.Selector.Path
2225
{
26+
@inlinable public
27+
var hasTrailingParentheses:Bool
28+
{
29+
self.seal != nil
30+
}
31+
2332
@inlinable public
2433
var visible:ArraySlice<String>
2534
{
@@ -114,9 +123,11 @@ extension UCF.Selector.Path
114123
case k?:
115124
// Special case: ignore empty trailing parentheses
116125
self.components.append(String.init(string[i ..< j]))
126+
self.seal = .trailingParentheses
117127
return string.index(after: k)
118128

119129
case let k?:
130+
self.seal = .trailingArguments
120131
j = string.index(after: k)
121132
}
122133
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
extension UCF.Selector
2+
{
3+
@frozen public
4+
enum Seal:Equatable, Hashable, Sendable
5+
{
6+
case trailingParentheses
7+
case trailingArguments
8+
}
9+
}

Sources/UCF/Codelinks/UCF.Selector.swift

+7-2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ extension UCF.Selector:CustomStringConvertible
4545
string += component
4646
}
4747

48+
if case .trailingParentheses? = self.path.seal
49+
{
50+
string += "()"
51+
}
52+
4853
switch self.suffix
4954
{
5055
case nil:
@@ -281,8 +286,8 @@ extension UCF.Selector
281286
}
282287
extension UCF.Selector
283288
{
284-
@inlinable public static
285-
func equivalent(to doclink:Doclink) -> Self?
289+
@inlinable public
290+
static func equivalent(to doclink:Doclink) -> Self?
286291
{
287292
if doclink.absolute
288293
{

0 commit comments

Comments
 (0)