From 748311feffd2ffd251c3f846379eb47ef8d0f335 Mon Sep 17 00:00:00 2001 From: Colby Mchenry Date: Tue, 8 Sep 2026 19:06:39 -0500 Subject: [PATCH] fix(kotlin): read a function's signature positionally (#1495) (#1807) Land upstream PR #1687 by danusha2345 (fix commit 6e9bbb26), using the PR tip implementation with only a Rust doc-comment placement cleanup. Read parameter lists and return types positionally in the wasm extractor and native kernel in lockstep, preserving verbatim signature text. Verified on Linux x64 with Node 22.19.0: reproduced three undefined signatures in both backends before the fix, then confirmed all three expected signatures and exact wasm/kernel parity after rebuilding TypeScript and the linux-x64 kernel. All 31 focused tests pass: 15 Kotlin extraction, 6 Kotlin parity, and 10 kernel scaffold checks, with CODEGRAPH_KERNEL_EXPECT=1 for the native suites. Add the upstream #1495 changelog bullet while preserving all other Unreleased entries. Keep EXTRACTION_VERSION unchanged for this bug fix. Co-authored-by: Colby McHenry --- CHANGELOG.md | 1 + codegraph-kernel/src/kotlin.rs | 37 ++++++++++++++++++++++++++++-- src/extraction/languages/kotlin.ts | 25 ++++++++++++++++---- 3 files changed, 57 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe2fbb2..c9f15c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -233,6 +233,7 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). #### Symbols, tests and the viewer +- Kotlin functions and methods now carry their signature — `(params): ReturnType` — in `codegraph_explore`, `node` and the viewer, instead of no signature at all. Re-index Kotlin projects after upgrading. (#1495) - `codegraph affected` now finds Go, Python and JVM test files that previously went unreported, while preserving custom `--filter` behavior (thanks @danusha2345; #1507, #1688). - Calls inside declaration initializers in Kotlin, Java, TypeScript, JavaScript, Scala, Rust and Python now appear under the declaration that owns them, making callers and impact results more accurate after re-indexing with `codegraph index -f` (thanks @danusha2345; #1510, #1511). diff --git a/codegraph-kernel/src/kotlin.rs b/codegraph-kernel/src/kotlin.rs index 0a4797f..f632d76 100644 --- a/codegraph-kernel/src/kotlin.rs +++ b/codegraph-kernel/src/kotlin.rs @@ -549,6 +549,39 @@ impl<'t> Walker<'t> { .any(|c| c.kind() == "modifiers" && self.text(c).contains("suspend")) } + /// `(params): ReturnType` — the positional read TreeSitterExtractor's + /// kotlin getSignature does (#1495): the `function_value_parameters` child, + /// then the type node that follows it before the body. Verbatim source text, + /// so it round-trips through parity byte-for-byte. + fn signature_of(&self, node: Node) -> Option { + let mut params: Option = None; + let mut return_type: Option = None; + for i in 0..node.named_child_count() { + let Some(child) = node.named_child(i) else { continue }; + if child.kind() == "function_value_parameters" { + params = Some(child); + continue; + } + if params.is_none() { + continue; + } + if matches!(child.kind(), "function_body" | "type_constraints") { + break; + } + if matches!(child.kind(), "user_type" | "nullable_type" | "function_type") { + return_type = Some(child); + break; + } + } + let params = params?; + let mut sig = self.text(params).to_string(); + if let Some(rt) = return_type { + sig.push_str(": "); + sig.push_str(self.text(rt)); + } + Some(sig) + } + /// extractKotlinReturnType — positional: the first user_type/nullable_type /// AFTER function_value_parameters; function_body/type_constraints first → /// None; Unit/Nothing → None; `: T` generic params leak (preserve). @@ -918,7 +951,7 @@ impl<'t> Walker<'t> { } let extra = Extra { docstring: preceding_docstring(node, self.src), - signature: None, // dead hook (zero fields) + signature: self.signature_of(node), visibility: Some(self.visibility_of(node)), is_async: Some(self.is_async(node)), is_static: Some(false), // kotlin isStatic is always false @@ -943,7 +976,7 @@ impl<'t> Walker<'t> { let qualified_override = receiver.as_ref().map(|r| format!("{r}::{name}")); let extra = Extra { docstring: preceding_docstring(node, self.src), - signature: None, + signature: self.signature_of(node), visibility: Some(self.visibility_of(node)), is_async: Some(self.is_async(node)), is_static: Some(false), diff --git a/src/extraction/languages/kotlin.ts b/src/extraction/languages/kotlin.ts index 144d523..e0b4092 100644 --- a/src/extraction/languages/kotlin.ts +++ b/src/extraction/languages/kotlin.ts @@ -1,5 +1,5 @@ import type { Node as SyntaxNode } from 'web-tree-sitter'; -import { getNodeText, getChildByField } from '../tree-sitter-helpers'; +import { getNodeText } from '../tree-sitter-helpers'; import type { LanguageExtractor } from '../tree-sitter-types'; /** Kotlin return types that can't be a chained-call receiver (no class to chain on). */ @@ -390,9 +390,26 @@ export const kotlinExtractor: LanguageExtractor = { return undefined; }, getSignature: (node, source) => { - // Kotlin function signature: fun name(params): ReturnType - const params = getChildByField(node, 'function_value_parameters'); - const returnType = getChildByField(node, 'type'); + // Kotlin function signature: fun name(params): ReturnType. tree-sitter-kotlin + // exposes no field names, so both parts are found positionally, the way + // extractKotlinReturnType does (#1495): the `function_value_parameters` + // child, then the type node that follows it before the body. + let params: SyntaxNode | null = null; + let returnType: SyntaxNode | null = null; + for (let i = 0; i < node.namedChildCount; i++) { + const child = node.namedChild(i); + if (!child) continue; + if (child.type === 'function_value_parameters') { + params = child; + continue; + } + if (!params) continue; + if (child.type === 'function_body' || child.type === 'type_constraints') break; + if (child.type === 'user_type' || child.type === 'nullable_type' || child.type === 'function_type') { + returnType = child; + break; + } + } if (!params) return undefined; let sig = getNodeText(params, source); if (returnType) {