fix(kotlin): extract typealias declarations so references don't mint sourceless stubs

A Kotlin `typealias` is a named type, but `type_alias` was in neither
`class_types` nor `function_types`, so the declaration was dropped from the
graph. A property or parameter typed as the alias (`var onClick: ClickHandler?`,
`fun handler(h: ClickHandler)`) still resolved the name `ClickHandler` through
`ensure_named_node`, which minted a *sourceless* stub and pointed the
`references` edge at it — even though the alias is declared in the same file.

That violates the provenance invariant: a real source-backed declaration must
not lose to a sourceless god node (and a bare-id stub becomes a cross-file hub
shared by every same-named reference in the corpus). Scala and TypeScript
already model type aliases as nodes, so Kotlin was the inconsistent outlier.

`type_alias` has an `identifier` name (covered by `name_fallback_child_types`)
and no body, so adding it to the Kotlin `class_types` emits a clean empty type
node (no phantom members) that the in-file references now bind to.
This commit is contained in:
rajatnagda45 authored and safishamsi committed 2026-10-20 10:35:23 +01:00
1 parent 7a32f3324e
commit d2ac22c621
2 files changed
+59 -1

No files matched your search

+8 -1
View File
@@ -1535,8 +1535,15 @@ _CSHARP_CONFIG = LanguageConfig(
)
_KOTLIN_CONFIG = LanguageConfig(
# `type_alias` is `typealias ClickHandler = (Int) -> Unit`. Without it the
# declaration was dropped, yet an in-file field/parameter typed as the alias
# still resolved its name through `ensure_named_node` and minted a
# *sourceless* stub — a real in-file type losing to a sourceless god node
# (provenance). Scala and TypeScript already model type aliases as nodes; the
# alias has an `identifier` name (covered by `name_fallback_child_types`) and
# no body, so it emits a clean empty type node those references bind to.
ts_module="tree_sitter_kotlin",
class_types=frozenset({"class_declaration", "object_declaration"}),
class_types=frozenset({"class_declaration", "object_declaration", "type_alias"}),
function_types=frozenset({"function_declaration"}),
# Grammar 1.1.0 (PyPI tree_sitter_kotlin) names the import node `import`;
# older forks use `import_header`. Accept both (#2526).
@@ -0,0 +1,51 @@
"""A Kotlin `typealias` is a named type and must be extracted as a sourced node.
`typealias ClickHandler = (Int) -> Unit` parses as `type_alias`, which was in
neither `class_types` nor `function_types`, so the declaration was dropped. But
a property or parameter typed as the alias (`var onClick: ClickHandler?`,
`fun handler(h: ClickHandler)`) still ran the name `ClickHandler` through
`ensure_named_node`, which minted a *sourceless* stub and pointed the
`references` edge at it — even though the alias is declared in the same file.
That is a provenance violation (a real source-backed declaration losing to a
sourceless god node). Scala and TypeScript already model type aliases as nodes;
`type_alias` has an `identifier` name and no body, so adding it to the Kotlin
`class_types` emits a clean empty type node those references bind to.
"""
from pathlib import Path
from graphify.extract import extract
def _extract(tmp_path, body: str):
(tmp_path / "m.kt").write_text(body)
return extract([tmp_path / "m.kt"], cache_root=tmp_path, parallel=False)
def _node(r, label):
return next((n for n in r["nodes"] if n.get("label") == label), None)
def test_typealias_is_a_sourced_node_not_a_sourceless_stub(tmp_path):
r = _extract(
tmp_path,
"typealias ClickHandler = (Int) -> Unit\n"
"class Button {\n"
" var onClick: ClickHandler? = null\n"
" fun handler(h: ClickHandler) { onClick = h }\n"
"}\n",
)
alias = _node(r, "ClickHandler")
assert alias is not None, "typealias declaration was dropped"
assert alias.get("source_file"), (
f"typealias ClickHandler is a sourceless god node: {alias}"
)
assert not any(
(not n.get("source_file")) and n.get("label") == "ClickHandler"
for n in r["nodes"]
)
refs_to_alias = {
e["source"]
for e in r["edges"]
if e["relation"] == "references" and e["target"] == alias["id"]
}
assert refs_to_alias, "references to the typealias did not bind to it"