Kotlin call-walker: accept both simple_identifier and identifier (#659)
`extract_kotlin` previously emitted zero `calls` edges (and zero `raw_calls` entries) on the current PyPI grammar. The Kotlin branch of `walk_calls` only matched node type `simple_identifier`, but PyPI's `tree_sitter_kotlin` produces `identifier` for the equivalent plain-identifier node. The `simple_identifier` ↔ `identifier` rename is a generation gap between tree-sitter-kotlin grammar versions — older forks (and the JVM `io.github.bonede:tree-sitter-kotlin` binding) still use `simple_identifier`. Accept both names so the extractor works across grammar generations. Also widens `_KOTLIN_CONFIG.name_fallback_child_types` for the same reason (defensive — currently the `name` field path covers class/function name resolution, but if that field is dropped in a future grammar update the fallback would face the same rename). Tested against `tests/fixtures/sample.kt`: edges go from 6 (file-contains + class-method only) to 10 (adds 4 in-file `calls` edges resolved by the walker: - .get() → .buildRequest() @ L8 - .post() → .buildRequest() @ L12 - createClient() → Config @ L21 - createClient() → HttpClient @ L22). A new regression test `test_kotlin_emits_in_file_calls` asserts the four edges so this exact bug can't recur. Found via graphify-kmp (Kotlin Multiplatform port of graphify) — its `PythonParityTest` flagged 4 KMP-only edges that Python missed. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
c3ddace690
commit
71423a1efb
+12
-4
@@ -590,7 +590,11 @@ _KOTLIN_CONFIG = LanguageConfig(
|
||||
call_function_field="",
|
||||
call_accessor_node_types=frozenset({"navigation_expression"}),
|
||||
call_accessor_field="",
|
||||
name_fallback_child_types=("simple_identifier",),
|
||||
# Different tree-sitter-kotlin grammar versions name plain identifier
|
||||
# nodes differently: PyPI's `tree_sitter_kotlin` uses `identifier`,
|
||||
# older forks use `simple_identifier`. Accept both so the extractor
|
||||
# works across grammar generations.
|
||||
name_fallback_child_types=("simple_identifier", "identifier"),
|
||||
body_fallback_child_types=("function_body", "class_body"),
|
||||
function_boundary_types=frozenset({"function_declaration"}),
|
||||
import_handler=_import_kotlin,
|
||||
@@ -1069,15 +1073,19 @@ def _extract_generic(path: Path, config: LanguageConfig) -> dict:
|
||||
if sc.type == "simple_identifier":
|
||||
callee_name = _read_text(sc, source)
|
||||
elif config.ts_module == "tree_sitter_kotlin":
|
||||
# Kotlin: first child may be simple_identifier or navigation_expression
|
||||
# Kotlin: first child may be simple_identifier/identifier or
|
||||
# navigation_expression. PyPI's `tree_sitter_kotlin` produces
|
||||
# `identifier` for plain identifier nodes; older grammar
|
||||
# versions (including the JVM `io.github.bonede:tree-sitter-kotlin`
|
||||
# binding) produce `simple_identifier`. Accept both.
|
||||
first = node.children[0] if node.children else None
|
||||
if first:
|
||||
if first.type == "simple_identifier":
|
||||
if first.type in ("simple_identifier", "identifier"):
|
||||
callee_name = _read_text(first, source)
|
||||
elif first.type == "navigation_expression":
|
||||
is_member_call = True
|
||||
for child in reversed(first.children):
|
||||
if child.type == "simple_identifier":
|
||||
if child.type in ("simple_identifier", "identifier"):
|
||||
callee_name = _read_text(child, source)
|
||||
break
|
||||
elif config.ts_module == "tree_sitter_scala":
|
||||
|
||||
@@ -188,6 +188,18 @@ def test_kotlin_finds_function():
|
||||
r = extract_kotlin(FIXTURES / "sample.kt")
|
||||
assert any("createClient" in l for l in _labels(r))
|
||||
|
||||
def test_kotlin_emits_in_file_calls():
|
||||
"""Regression test for the call-walker `simple_identifier` /
|
||||
`identifier` rename — see graphify-kmp's PythonParityTest."""
|
||||
r = extract_kotlin(FIXTURES / "sample.kt")
|
||||
calls = _calls(r)
|
||||
# In sample.kt: get() and post() both call buildRequest(), and
|
||||
# createClient() invokes Config and HttpClient (constructor calls).
|
||||
assert (".get()", ".buildRequest()") in calls
|
||||
assert (".post()", ".buildRequest()") in calls
|
||||
assert ("createClient()", "Config") in calls
|
||||
assert ("createClient()", "HttpClient") in calls
|
||||
|
||||
|
||||
# ── Scala ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
Reference in New Issue
Block a user