Skip to content

Commit a0df8a5

Browse files
committed
prefer non-inherited symbols when disambiguating links
1 parent eaf8c27 commit a0df8a5

17 files changed

+260
-181
lines changed

Sources/LinkResolution/UCF.Autograph.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import UCF
33
extension UCF
44
{
55
@frozen public
6-
struct Autograph:Equatable, Sendable
6+
struct Autograph:Hashable, Equatable, Sendable
77
{
88
public
99
let inputs:[String]

Sources/LinkResolution/UCF.CausalOverload.swift

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,37 +8,29 @@ extension UCF
88
struct CausalOverload:ResolvableOverload, Sendable
99
{
1010
public
11-
let phylum:Phylum.Decl
12-
public
13-
let kinks:Phylum.Decl.Kinks
11+
let traits:DisambiguationTraits
1412
public
1513
let decl:Symbol.Decl
1614
public
1715
let heir:Symbol.Decl?
18-
public
19-
let hash:FNV24
2016

2117
public
2218
let documented:Bool
2319
public
24-
let autograph:Autograph?
20+
let inherited:Bool
2521

2622
@inlinable public
27-
init(phylum:Phylum.Decl,
28-
kinks:Phylum.Decl.Kinks,
23+
init(traits:DisambiguationTraits,
2924
decl:Symbol.Decl,
3025
heir:Symbol.Decl?,
31-
hash:FNV24,
3226
documented:Bool,
33-
autograph:Autograph?)
27+
inherited:Bool)
3428
{
35-
self.phylum = phylum
36-
self.kinks = kinks
29+
self.traits = traits
3730
self.decl = decl
3831
self.heir = heir
39-
self.hash = hash
4032
self.documented = documented
41-
self.autograph = autograph
33+
self.inherited = inherited
4234
}
4335
}
4436
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import FNV1
2+
import Symbols
3+
import UCF
4+
5+
extension UCF
6+
{
7+
@frozen public
8+
struct DisambiguationTraits
9+
{
10+
public
11+
let autograph:Autograph?
12+
public
13+
let phylum:Phylum.Decl
14+
public
15+
let kinks:Phylum.Decl.Kinks
16+
public
17+
let hash:FNV24
18+
19+
@inlinable public
20+
init(autograph:Autograph?,
21+
phylum:Phylum.Decl,
22+
kinks:Phylum.Decl.Kinks,
23+
hash:FNV24)
24+
{
25+
self.autograph = autograph
26+
self.phylum = phylum
27+
self.kinks = kinks
28+
self.hash = hash
29+
}
30+
}
31+
}
32+
extension UCF.DisambiguationTraits
33+
{
34+
static func ~= (predicate:UCF.Predicate, self:Self) -> Bool
35+
{
36+
if case nil = predicate.seal
37+
{
38+
// Macros are currently the only kind of declaration that *must* be spelled with
39+
// trailing parentheses.
40+
switch self.phylum
41+
{
42+
case .actor: break
43+
case .associatedtype: break
44+
case .case: break
45+
case .class: break
46+
case .deinitializer: break
47+
case .enum: break
48+
case .func: break
49+
case .initializer: break
50+
case .macro: return false
51+
case .operator: break
52+
case .protocol: break
53+
case .struct: break
54+
case .subscript: break
55+
case .typealias: break
56+
case .var: break
57+
}
58+
}
59+
else
60+
{
61+
switch self.phylum
62+
{
63+
case .actor: return false
64+
case .associatedtype: return false
65+
case .case: break
66+
case .class: return false
67+
case .deinitializer: return false
68+
case .enum: return false
69+
case .func: break
70+
case .initializer: break
71+
case .macro: break
72+
case .operator: break
73+
case .protocol: return false
74+
case .struct: return false
75+
case .subscript: break
76+
case .typealias: return false
77+
case .var: return false
78+
}
79+
}
80+
81+
guard
82+
let suffix:UCF.Selector.Suffix = predicate.suffix
83+
else
84+
{
85+
return true
86+
}
87+
88+
switch suffix
89+
{
90+
case .unidoc(let filter):
91+
if let signature:UCF.SignatureFilter = filter.signature
92+
{
93+
// If a signature filter is present, the declaration must have an autograph.
94+
guard
95+
let autograph:UCF.Autograph = self.autograph, signature ~= autograph
96+
else
97+
{
98+
return false
99+
}
100+
}
101+
102+
let decl:(Phylum.Decl, Phylum.Decl.Kinks) = (self.phylum, self.kinks)
103+
for condition:UCF.ConditionFilter in filter.conditions
104+
{
105+
guard condition ~= decl
106+
else
107+
{
108+
return false
109+
}
110+
}
111+
112+
return true
113+
114+
case .legacy(let filter, nil):
115+
return filter ~= self.phylum
116+
117+
case .legacy(_, let hash?):
118+
return hash == self.hash
119+
120+
case .hash(let hash):
121+
return hash == self.hash
122+
}
123+
}
124+
}

Sources/LinkResolution/UCF.PackageOverload.swift

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,42 +8,34 @@ extension UCF
88
struct PackageOverload:ResolvableOverload, Sendable
99
{
1010
public
11-
let phylum:Phylum.Decl
12-
public
13-
let kinks:Phylum.Decl.Kinks
11+
let traits:DisambiguationTraits
1412
public
1513
let decl:Int32
1614
public
1715
let heir:Int32?
18-
public
19-
let hash:FNV24
2016

2117
public
2218
let documented:Bool
2319
public
24-
let autograph:Autograph?
20+
let inherited:Bool
2521
/// Used for display purposes. This is not necessarily the symbol from which the
2622
/// ``hash`` was computed.
2723
public
2824
let id:Symbol.Decl
2925

3026
@inlinable public
31-
init(phylum:Phylum.Decl,
32-
kinks:Phylum.Decl.Kinks,
27+
init(traits:DisambiguationTraits,
3328
decl:Int32,
3429
heir:Int32?,
35-
hash:FNV24,
3630
documented:Bool,
37-
autograph:Autograph?,
31+
inherited:Bool,
3832
id:Symbol.Decl)
3933
{
40-
self.phylum = phylum
41-
self.kinks = kinks
34+
self.traits = traits
4235
self.decl = decl
4336
self.heir = heir
44-
self.hash = hash
4537
self.documented = documented
46-
self.autograph = autograph
38+
self.inherited = inherited
4739
self.id = id
4840
}
4941
}

Sources/LinkResolution/UCF.ProjectWideResolver.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ extension UCF.ProjectWideResolver
4444
return .overload(overload)
4545

4646
case .ambiguous(let overloads, rejected: let rejections):
47-
rejected = rejections.reduce(into: [:]) { $0[$1.hash] = $1 }
47+
rejected = rejections.reduce(into: [:]) { $0[$1.traits.hash] = $1 }
4848

4949
guard overloads.isEmpty
5050
else
@@ -69,7 +69,7 @@ extension UCF.ProjectWideResolver
6969
case .ambiguous(let overloads, rejected: let rejections):
7070
for overload:any UCF.ResolvableOverload in rejections
7171
{
72-
rejected[overload.hash] = overload
72+
rejected[overload.traits.hash] = overload
7373
}
7474

7575
return .ambiguous(overloads, rejected: [_].init(rejected.values))

Sources/LinkResolution/UCF.Resolution.swift

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,39 @@ extension UCF
1313
}
1414
extension UCF.Resolution where Overload:UCF.ResolvableOverload
1515
{
16-
static func choose(among overloads:[Symbol.Decl: Overload],
16+
static func choose(among overloads:[Symbol.Decl: Overload],
1717
rejected:[Symbol.Decl: Overload]) -> Self
1818
{
19-
var documentedOverload:Overload?
19+
var noninheritedOverload:Overload?
20+
var noninheritedCount:Int = 0
21+
22+
for candidate:Overload in overloads.values
23+
{
24+
if !candidate.inherited
25+
{
26+
noninheritedOverload = candidate
27+
noninheritedCount += 1
28+
}
29+
}
30+
31+
// If this narrowed the selection down to a single overload, we're done.
32+
if let noninheritedOverload:Overload, noninheritedCount == 1
33+
{
34+
return .overload(noninheritedOverload)
35+
}
36+
37+
var documentedOverload:Overload?
2038
var documentedCount:Int = 0
2139

2240
for candidate:Overload in overloads.values
2341
{
42+
// If there are any non-inherited overloads available, we only consider such
43+
// overloads. Otherwise, we consider all overloads including inherited ones.
44+
if noninheritedCount > 0, candidate.inherited
45+
{
46+
continue
47+
}
48+
2449
if candidate.documented
2550
{
2651
documentedOverload = candidate
@@ -32,9 +57,9 @@ extension UCF.Resolution where Overload:UCF.ResolvableOverload
3257
{
3358
return .overload(documentedOverload)
3459
}
35-
else
60+
else
3661
{
37-
return .ambiguous(overloads.values.sorted { $0.id < $1.id },
62+
return .ambiguous(overloads.values.sorted { $0.id < $1.id },
3863
rejected: rejected.values.sorted { $0.id < $1.id })
3964
}
4065
}

Sources/LinkResolution/UCF.ResolutionError.swift

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,36 @@ extension UCF.ResolutionError:Diagnostic
3939
@inlinable public
4040
func emit(details output:inout DiagnosticOutput<Symbolicator>)
4141
{
42+
let collisions:[UCF.Autograph: Int] = [self.overloads, self.rejected].joined().reduce(
43+
into: [:])
44+
{
45+
if let autograph:UCF.Autograph = $1.traits.autograph
46+
{
47+
$0[autograph, default: 0] += 1
48+
}
49+
}
50+
4251
for overload:any UCF.ResolvableOverload in [self.overloads, self.rejected].joined()
4352
{
53+
let traits:UCF.DisambiguationTraits = overload.traits
54+
let suffix:UCF.Selector.Suffix
55+
56+
if let autograph:UCF.Autograph = traits.autograph,
57+
case 1? = collisions[autograph]
58+
{
59+
suffix = .unidoc(.init(
60+
conditions: [],
61+
signature: .function(autograph.inputs, autograph.output)))
62+
}
63+
else
64+
{
65+
suffix = .hash(traits.hash)
66+
}
67+
4468
let suggested:UCF.Selector = .init(
4569
base: self.selector.base,
4670
path: self.selector.path,
47-
suffix: .hash(overload.hash))
71+
suffix: suffix)
4872

4973
output[.note] = """
5074
did you mean '\(suggested)'? (\(output.symbolicator.demangle(overload.id)))

Sources/LinkResolution/UCF.ResolutionTable.Search.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,11 @@ extension UCF.ResolutionTable.Search
2727
mutating
2828
func add(_ candidates:InlineArray<Overload>)
2929
{
30-
// Because of the way `@_exported` paths are represented in the search tree, it is
30+
// 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
3232
for overload:Overload in candidates
3333
{
34-
guard self.predicate ~= overload
34+
guard self.predicate ~= overload.traits
3535
else
3636
{
3737
self.rejected[overload.id] = overload
@@ -44,11 +44,11 @@ extension UCF.ResolutionTable.Search
4444

4545
func any() -> UCF.Resolution<Overload>?
4646
{
47-
guard
47+
guard
4848
let overload:Overload = self.selected.values.first
4949
else
5050
{
51-
return nil
51+
return nil
5252
}
5353

5454
if self.selected.count == 1

0 commit comments

Comments
 (0)