Severity: medium — crash paths silently violate the type-vs-term invariant established by Phase 1.
Symptom
TastyIndexer.mkSymbolId:
def mkSymbolId(sym: Symbol): SymbolId =
try {
val isType = isTypeLike(sym)
val name = stripDollar(sym.name)
val (pkg, owners) = collectOwners(sym.maybeOwner)
SymbolId.fromTasty(pkg, owners, name, isType)
} catch {
case NonFatal(_) =>
try SymbolId.fromTasty(Nil, Nil, stripDollar(sym.name), isType = false)
catch { case NonFatal(_) => SymbolId(Nil, Nil, "<unknown>", None) }
}
The fallback hard-codes isType = false. If isTypeLike succeeds (correctly classifying a class as a type) but collectOwners throws (e.g. broken owner chain in best-effort TASTy), the catch promotes the class to a term id.
Concrete failure
A transient TASTy error inside collectOwners while indexing class Foo produces term(Nil, Nil, "Foo") instead of tpe(...). Downstream queries against the correct tpe(...) miss; the orphan term id pollutes getSubtypes / getReferences.
The <unknown> second-fallback is also a collision risk — every fully-broken symbol coalesces under one id.
Fix
Compute isType outside the try so it survives collectOwners failures:
def mkSymbolId(sym: Symbol): SymbolId = {
val isType = try isTypeLike(sym) catch { case NonFatal(_) => false }
try {
val name = stripDollar(sym.name)
val (pkg, owners) = collectOwners(sym.maybeOwner)
SymbolId.fromTasty(pkg, owners, name, isType)
} catch {
case NonFatal(_) =>
try SymbolId.fromTasty(Nil, Nil, stripDollar(sym.name), isType)
catch { case NonFatal(_) => SymbolId(Nil, Nil, "<unknown>", None) }
}
}
Separately, consider whether <unknown> should be replaced with something less collision-prone — e.g. include the source-file URI in the synthetic name, or skip the symbol entirely.
Acceptance criteria
- Unit test: inject a Symbol whose
collectOwners throws but isTypeLike succeeds; verify the produced SymbolId has the right type/term shape
Severity: medium — crash paths silently violate the type-vs-term invariant established by Phase 1.
Symptom
TastyIndexer.mkSymbolId:The fallback hard-codes
isType = false. IfisTypeLikesucceeds (correctly classifying a class as a type) butcollectOwnersthrows (e.g. broken owner chain in best-effort TASTy), the catch promotes the class to a term id.Concrete failure
A transient TASTy error inside
collectOwnerswhile indexing classFooproducesterm(Nil, Nil, "Foo")instead oftpe(...). Downstream queries against the correcttpe(...)miss; the orphan term id pollutesgetSubtypes/getReferences.The
<unknown>second-fallback is also a collision risk — every fully-broken symbol coalesces under one id.Fix
Compute
isTypeoutside the try so it survivescollectOwnersfailures:Separately, consider whether
<unknown>should be replaced with something less collision-prone — e.g. include the source-file URI in the synthetic name, or skip the symbol entirely.Acceptance criteria
collectOwnersthrows butisTypeLikesucceeds; verify the produced SymbolId has the right type/term shape