ソースを参照

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 <colbymchenry@users.noreply.github.com>
Colby Mchenry 21 時間 前
コミット
748311feff
3 ファイル変更57 行追加6 行削除
  1. 1 0
      CHANGELOG.md
  2. 35 2
      codegraph-kernel/src/kotlin.rs
  3. 21 4
      src/extraction/languages/kotlin.ts

+ 1 - 0
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).

+ 35 - 2
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<String> {
+        let mut params: Option<Node> = None;
+        let mut return_type: Option<Node> = 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),

+ 21 - 4
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) {