Skip to content

Commit 24f415b

Browse files
committed
discriminate empty trailing parentheses
1 parent 46e8700 commit 24f415b

9 files changed

+88
-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 hasEmptyTrailingParentheses:Bool
9+
10+
init(suffix:Selector.Suffix?, hasEmptyTrailingParentheses:Bool)
11+
{
12+
self.suffix = suffix
13+
self.hasEmptyTrailingParentheses = hasEmptyTrailingParentheses
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

+34-5
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,43 @@ 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 predicate.hasEmptyTrailingParentheses
21+
{
22+
switch self.phylum
23+
{
24+
case .actor: return false
25+
case .associatedtype: return false
26+
case .case: return false
27+
case .class: return false
28+
case .deinitializer: return false
29+
case .enum: return false
30+
case .func: break
31+
case .initializer: break
32+
case .macro: break
33+
case .operator: break
34+
case .protocol: return false
35+
case .struct: return false
36+
case .subscript: break
37+
case .typealias: return false
38+
case .var: return false
39+
}
40+
}
41+
42+
guard
43+
let suffix:UCF.Selector.Suffix = predicate.suffix
44+
else
45+
{
46+
return true
47+
}
48+
2049
switch suffix
2150
{
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
51+
case .legacy(let filter, nil): return filter ~= self.phylum
52+
case .legacy(_, let hash?): return hash == self.hash
53+
case .hash(let hash): return hash == self.hash
54+
case .filter(let filter): return filter ~= self.phylum
2655
}
2756
}
2857
}

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

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

4+
extension UCF.Selector
5+
{
6+
var predicate:UCF.Predicate
7+
{
8+
.init(suffix: self.suffix,
9+
hasEmptyTrailingParentheses: self.path.hasEmptyTrailingParentheses)
10+
}
11+
}
412
// TODO: this does not belong in this module
513
extension UCF.Selector
614
{

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

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

1315
@inlinable public
14-
init(components:[String] = [], fold:Int = 0)
16+
init(components:[String] = [], fold:Int = 0, hasEmptyTrailingParentheses:Bool = false)
1517
{
1618
self.components = components
1719
self.fold = fold
20+
self.hasEmptyTrailingParentheses = hasEmptyTrailingParentheses
1821
}
1922
}
2023
}
@@ -114,6 +117,7 @@ extension UCF.Selector.Path
114117
case k?:
115118
// Special case: ignore empty trailing parentheses
116119
self.components.append(String.init(string[i ..< j]))
120+
self.hasEmptyTrailingParentheses = true
117121
return string.index(after: k)
118122

119123
case let k?:

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 self.path.hasEmptyTrailingParentheses
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
{

Sources/UCFTests/Main.ParseCodelink.swift

+1
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ extension Main.ParseCodelink:TestBattery
126126
tests.expect(link.base ==? .relative)
127127
tests.expect(link.path.components ..? ["Real", "init"])
128128
tests.expect(link.path.visible ..? ["Real", "init"])
129+
tests.expect(true: link.path.hasEmptyTrailingParentheses)
129130
tests.expect(nil: link.suffix)
130131
}
131132
if let tests:TestGroup = tests / "EmptyTrailingComponent",

0 commit comments

Comments
 (0)