From c2503e2bee5f2a0c880ff536a33736ca31709ecc Mon Sep 17 00:00:00 2001 From: Colby McHenry Date: Thu, 16 Jul 2026 23:50:02 -0500 Subject: [PATCH] =?UTF-8?q?feat(kernel):=20R5=20=E2=80=94=20Python=20and?= =?UTF-8?q?=20Go=20ports,=20gates=20passed,=20default-on?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Python (codegraph-kernel/src/python.rs) and Go (src/go.rs) join the native kernel, mirroring the wasm extractors bug-for-bug. Python: decorated_definition docstrings/decorators (decorates refs only for bare-identifier decorators — the call-kind quirk), function-in-class → method, module-level assignments always extract as variable, from-import per-name binding refs, self.x fn-ref candidates as bare names. Go: receiver methods with Recv::name qualified names + contains edges to the first earlier struct of that name, type_spec struct/interface classification with embedding→extends and interface method nodes, composite-literal instantiates keeping the package qualifier, top-level var/const initializer walks attributed to the declared symbol (#693), 2-hop field chains (#1276), New().Method() re-encode (#645/#608), and the GO_SPEC fn-ref layers. Grammars: tree-sitter-python 0.23.6 + tree-sitter-go 0.23.4 crates, with wasm vendored from the same tags (parser.c sha-matched) — both were 2023-era in tree-sitter-wasms. Gates: extraction sweeps 100% (flask 83/83, django 3,035/3,038 +3 error-file deferrals, gin 99/99, prometheus 978/979 +1); full-init dump-diffs byte-identical on flask (10,833 rows), gin (17,540), django (360,794), and prometheus (213,758); torture fixtures enforced in npm test. DEFAULT_ROUTED now covers typescript/tsx/javascript/jsx/java/ python/go. Full suite: 2,471 tests pass. Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 2 +- __tests__/fixtures/kernel-parity/torture.go | 61 + __tests__/fixtures/kernel-parity/torture.py | 49 + __tests__/kernel-grammar-parity.test.ts | 2 +- __tests__/kernel-scaffold.test.ts | 8 +- __tests__/kernel-tsjs-parity.test.ts | 12 +- codegraph-kernel/Cargo.lock | 22 + codegraph-kernel/Cargo.toml | 2 + codegraph-kernel/src/go.rs | 1223 +++++++++++++++++++ codegraph-kernel/src/langs.rs | 5 +- codegraph-kernel/src/lib.rs | 4 + codegraph-kernel/src/python.rs | 978 +++++++++++++++ docs/design/rust-kernel-migration-plan.md | 29 +- scripts/kernel-parity.mjs | 4 +- src/extraction/grammars.ts | 4 +- src/extraction/kernel/index.ts | 2 + src/extraction/wasm/tree-sitter-go.wasm | Bin 0 -> 210013 bytes src/extraction/wasm/tree-sitter-python.wasm | Bin 0 -> 456131 bytes 18 files changed, 2393 insertions(+), 14 deletions(-) create mode 100644 __tests__/fixtures/kernel-parity/torture.go create mode 100644 __tests__/fixtures/kernel-parity/torture.py create mode 100644 codegraph-kernel/src/go.rs create mode 100644 codegraph-kernel/src/python.rs create mode 100755 src/extraction/wasm/tree-sitter-go.wasm create mode 100755 src/extraction/wasm/tree-sitter-python.wasm diff --git a/CHANGELOG.md b/CHANGELOG.md index 818c851..062a461 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### New Features -- Indexing TypeScript, TSX, JavaScript, JSX, and Java projects is faster: parsing and symbol extraction now run in a native engine when a prebuilt binary is available for your platform (release bundles include one), producing exactly the same graph — verified byte-for-byte against the previous engine on real repositories, from small libraries up to vscode- and dubbo-scale codebases (Lombok-generated members included). The speedup is largest on resource-constrained machines like CI runners. No setup needed: platforms without the native binary, and individual files with syntax errors, automatically use the previous engine, and `CODEGRAPH_KERNEL=0` turns the native path off entirely. +- Indexing TypeScript, TSX, JavaScript, JSX, Java, Python, and Go projects is faster: parsing and symbol extraction now run in a native engine when a prebuilt binary is available for your platform (release bundles include one), producing exactly the same graph — verified byte-for-byte against the previous engine on real repositories, from small libraries up to vscode-, dubbo-, and django-scale codebases (Lombok-generated members included). The speedup is largest on resource-constrained machines like CI runners. No setup needed: platforms without the native binary, and individual files with syntax errors, automatically use the previous engine, and `CODEGRAPH_KERNEL=0` turns the native path off entirely. - Reference resolution now runs in parallel on large projects. When a project has enough pending references to make it worthwhile (roughly 150k+, typical for big Java/Kotlin/Spring codebases), resolution fans out across worker threads while results are applied in the exact order the single-threaded path would have used — the graph comes out byte-for-byte identical, about twice as fast end-to-end on a 4,000-file Java project in our testing. Small projects keep the single-threaded path automatically (the fan-out costs more than it saves there). Set `CODEGRAPH_NO_PARALLEL_RESOLVE=1` to disable, or `CODEGRAPH_PARALLEL_RESOLVE_MIN=` to tune when it engages. - Indexing large projects got another sizeable speedup — about a quarter less wall-clock on the same 4,000-file Java project, with the graph still byte-for-byte identical. Two changes: the database no longer interleaves expensive checkpoint housekeeping into the middle of resolution on a fresh index (it's folded once at the end instead), and while one batch's results are being written out, the worker threads are already resolving the next batch instead of sitting idle. - The dynamic-dispatch analysis that runs at the end of indexing (callback, event, and framework wiring) now runs its passes in parallel on large projects, cutting that stage roughly in half there — and a pass that crashes now retries safely instead of failing the whole index, which also makes very large codebases that previously died in this stage more likely to index to completion. Graphs remain byte-for-byte identical. diff --git a/__tests__/fixtures/kernel-parity/torture.go b/__tests__/fixtures/kernel-parity/torture.go new file mode 100644 index 0000000..f89b8d4 --- /dev/null +++ b/__tests__/fixtures/kernel-parity/torture.go @@ -0,0 +1,61 @@ +// Go torture fixture — receivers, embedding, interfaces, composite literals. +package torture + +import ( + "fmt" + pkga "example.com/other/pkga" +) + +const MAX_ITEMS = 128 + +var DefaultRegistry = NewRegistry() + +var handlerTable = map[string]func(int){ + "recv": TargetCb, +} + +type Widget struct { + *Base + Queryable + name string +} + +type Stack[T any] struct { + items []T +} + +type Core interface { + Reader + Marshal(v any) ([]byte, error) + Unmarshal(data []byte) error +} + +type Dur int + +func NewRegistry() *Registry { + w := Widget{name: "w"} + q := pkga.Widget{} + fmt.Println(w, q, MAX_ITEMS) + cfg := loadConfig() + cfg.conn.Exec("x") + return New().Init() +} + +func (s *Stack[T]) Push(item T) { + s.items = append(s.items, item) +} + +func (w Widget) Render() string { + return w.name +} + +func TargetCb(n int) {} + +func shadowed() { + MAX_ITEMS := 5 + fmt.Println(MAX_ITEMS) +} + +func reads() int { + return MAX_ITEMS +} diff --git a/__tests__/fixtures/kernel-parity/torture.py b/__tests__/fixtures/kernel-parity/torture.py new file mode 100644 index 0000000..900fc74 --- /dev/null +++ b/__tests__/fixtures/kernel-parity/torture.py @@ -0,0 +1,49 @@ +"""Python torture fixture — decorators, self fn-refs, imports, shadowing.""" +import os, sys +import os.path as osp +from collections import OrderedDict, defaultdict +from .relative import thing +from mypkg.handlers import target_cb + +RETRY_LIMITS = {"a": 1} +API_BASE = "https://example.test" +x = compute(RETRY_LIMITS) + + +class Service(BaseService, mixins.LoggerMixin): + """Class docs.""" + + def __init__(self, registry): + self.registry = registry + register(self.on_event) + queue(target_cb) + + @staticmethod + def helper(arg): + return transform(arg) + + async def run(self): + cfg = self.registry.lookup("x") + limit = RETRY_LIMITS + obj.method_chain().deep(cfg) + ", ".join(cfg) + return await fetch(API_BASE) + + def on_event(self): + pass + + +@app.route("/x") +def view(): + def inner(): + return API_BASE + return inner() + + +def shadowed(): + API_BASE = "local" + return API_BASE + + +handlers = {"recv": target_cb} +callbacks = [target_cb, view] diff --git a/__tests__/kernel-grammar-parity.test.ts b/__tests__/kernel-grammar-parity.test.ts index 6287a15..fd39270 100644 --- a/__tests__/kernel-grammar-parity.test.ts +++ b/__tests__/kernel-grammar-parity.test.ts @@ -36,7 +36,7 @@ const kernelBuilt = fs.existsSync(KERNEL_PATH); // Every kernel-capable language. `jsx` shares the javascript grammar on BOTH // paths (langs.rs mirrors WASM_GRAMMAR_FILES), so the distinct grammars are: -const GRAMMAR_LANGUAGES: Language[] = ['typescript', 'tsx', 'javascript', 'java']; +const GRAMMAR_LANGUAGES: Language[] = ['typescript', 'tsx', 'javascript', 'java', 'python', 'go']; describe.skipIf(!kernelBuilt)('kernel↔wasm grammar parity', () => { beforeAll(async () => { diff --git a/__tests__/kernel-scaffold.test.ts b/__tests__/kernel-scaffold.test.ts index 372e505..6eec5ac 100644 --- a/__tests__/kernel-scaffold.test.ts +++ b/__tests__/kernel-scaffold.test.ts @@ -72,12 +72,12 @@ describe.skipIf(!kernelBuilt)('kernel scaffold', () => { expect(info.languages).toContain('javascript'); }); - it('TS/JS family + Java route to the kernel by default; others stay wasm', () => { - for (const lang of ['typescript', 'tsx', 'javascript', 'jsx', 'java'] as const) { + it('TS/JS family + Java + Python + Go route to the kernel by default; others stay wasm', () => { + for (const lang of ['typescript', 'tsx', 'javascript', 'jsx', 'java', 'python', 'go'] as const) { expect(kernelRoutes(lang), lang).toBe(true); } - expect(kernelRoutes('python')).toBe(false); - expect(tryKernelExtract('src/a.py', 'def f():\n pass\n', 'python')).toBeNull(); + expect(kernelRoutes('ruby')).toBe(false); + expect(tryKernelExtract('src/a.rb', 'def f\nend\n', 'ruby')).toBeNull(); // CODEGRAPH_KERNEL_LANGS REPLACES the default set when present. process.env.CODEGRAPH_KERNEL_LANGS = 'tsx'; expect(kernelRoutes('typescript')).toBe(false); diff --git a/__tests__/kernel-tsjs-parity.test.ts b/__tests__/kernel-tsjs-parity.test.ts index 8543aba..cdf0289 100644 --- a/__tests__/kernel-tsjs-parity.test.ts +++ b/__tests__/kernel-tsjs-parity.test.ts @@ -60,7 +60,7 @@ let savedEnv: Record; describe.skipIf(!kernelBuilt)('kernel TS/JS extraction parity', () => { beforeAll(async () => { await initGrammars(); - await loadGrammarsForLanguages(['typescript', 'tsx', 'javascript', 'jsx', 'java']); + await loadGrammarsForLanguages(['typescript', 'tsx', 'javascript', 'jsx', 'java', 'python', 'go']); }); beforeEach(() => { @@ -110,6 +110,16 @@ describe.skipIf(!kernelBuilt)('kernel TS/JS extraction parity', () => { assertParity('fixtures/Torture.java', fs.readFileSync(file, 'utf8'), 'java'); }); + it('torture fixture (python): decorators, self fn-refs, imports, shadowing', () => { + const file = path.join(FIXTURE_DIR, 'torture.py'); + assertParity('fixtures/torture.py', fs.readFileSync(file, 'utf8'), 'python'); + }); + + it('torture fixture (go): receivers, embedding, interfaces, composite literals', () => { + const file = path.join(FIXTURE_DIR, 'torture.go'); + assertParity('fixtures/torture.go', fs.readFileSync(file, 'utf8'), 'go'); + }); + it.each(REAL_SOURCES)('real source parity: %s', (rel) => { const file = path.join(__dirname, '..', rel); assertParity(rel, fs.readFileSync(file, 'utf8'), 'typescript'); diff --git a/codegraph-kernel/Cargo.lock b/codegraph-kernel/Cargo.lock index ec0c756..9ad427c 100644 --- a/codegraph-kernel/Cargo.lock +++ b/codegraph-kernel/Cargo.lock @@ -52,8 +52,10 @@ dependencies = [ "regex", "sha2", "tree-sitter", + "tree-sitter-go", "tree-sitter-java", "tree-sitter-javascript", + "tree-sitter-python", "tree-sitter-typescript", ] @@ -480,6 +482,16 @@ dependencies = [ "tree-sitter-language", ] +[[package]] +name = "tree-sitter-go" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b13d476345220dbe600147dd444165c5791bf85ef53e28acbedd46112ee18431" +dependencies = [ + "cc", + "tree-sitter-language", +] + [[package]] name = "tree-sitter-java" version = "0.23.5" @@ -506,6 +518,16 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "009994f150cc0cd50ff54917d5bc8bffe8cad10ca10d81c34da2ec421ae61782" +[[package]] +name = "tree-sitter-python" +version = "0.23.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d065aaa27f3aaceaf60c1f0e0ac09e1cb9eb8ed28e7bcdaa52129cffc7f4b04" +dependencies = [ + "cc", + "tree-sitter-language", +] + [[package]] name = "tree-sitter-typescript" version = "0.23.2" diff --git a/codegraph-kernel/Cargo.toml b/codegraph-kernel/Cargo.toml index 243aa5d..eed746d 100644 --- a/codegraph-kernel/Cargo.toml +++ b/codegraph-kernel/Cargo.toml @@ -23,6 +23,8 @@ regex = "1" tree-sitter-typescript = "0.23" tree-sitter-javascript = "0.25" tree-sitter-java = "0.23" +tree-sitter-python = "0.23" +tree-sitter-go = "0.23" [build-dependencies] napi-build = "2" diff --git a/codegraph-kernel/src/go.rs b/codegraph-kernel/src/go.rs new file mode 100644 index 0000000..71081cb --- /dev/null +++ b/codegraph-kernel/src/go.rs @@ -0,0 +1,1223 @@ +//! Go extraction — a faithful Rust port of `TreeSitterExtractor`'s Go paths +//! (src/extraction/tree-sitter.ts) plus languages/go.ts. +//! +//! Go's shape quirks, mirrored exactly: methods are top-level with a receiver +//! (qualifiedName override `Recv::name` + a contains edge to the FIRST +//! earlier-in-file struct of that name), structs/interfaces arrive as +//! `type_spec` and classify via the inner type node (struct embedding → +//! extends; interface method_elems become method nodes), composite literals +//! (`pkga.Widget{}`) keep their package qualifier as `instantiates` refs, +//! top-level var/const specs walk their initializers ATTRIBUTED to the +//! declared symbol (#693), 2-hop field chains (`t.conn.Exec`) keep the chain +//! (#1276), and `New().Method()` re-encodes as `New().Method` (#645/#608) +//! only for bare-identifier factories. Files with parse errors defer to wasm. + +use crate::buffers::{ + build_meta, edge_kind_index, node_kind_index, Arena, BoolFlags, EdgeRow, EmitOut, NodeRow, + RefRow, StrRef, Tables, FLAG_IS_EXPORTED, FUNCTION_REF_CODE, NONE, NONE_STR, +}; +use crate::docstring::preceding_docstring; +use crate::ids; +use crate::textutil as util; +use regex::Regex; +use std::collections::{HashMap, HashSet}; +use std::sync::OnceLock; +use tree_sitter::{Node, Parser}; + +const MAX_VALUE_REF_NODES: usize = 20_000; + +fn receiver_re() -> &'static Regex { + static RE: OnceLock = OnceLock::new(); + RE.get_or_init(|| Regex::new(r"\(\s*(?:[A-Za-z_]\w*\s+)?\*?\s*([A-Za-z_]\w*)").unwrap()) +} +fn simple_ident_re() -> &'static Regex { + static RE: OnceLock = OnceLock::new(); + RE.get_or_init(|| Regex::new(r"^[A-Za-z_]\w*$").unwrap()) +} +fn go_two_hop_re() -> &'static Regex { + static RE: OnceLock = OnceLock::new(); + RE.get_or_init(|| Regex::new(r"^[A-Za-z_]\w*\.[A-Za-z_]\w*$").unwrap()) +} +fn generic_angle_re() -> &'static Regex { + static RE: OnceLock = OnceLock::new(); + RE.get_or_init(|| Regex::new(r"<[^>]*>").unwrap()) +} +fn bracket_args_re() -> &'static Regex { + static RE: OnceLock = OnceLock::new(); + RE.get_or_init(|| Regex::new(r"\[[^\]]*\]").unwrap()) +} + +struct Scope { + row: u32, + kind: &'static str, + name: String, +} + +#[derive(Default)] +struct Extra { + docstring: Option, + signature: Option, + is_exported: Option, + return_type: Option, + qualified_name: Option, +} + +struct ValueScope<'t> { + row: u32, + node: Node<'t>, + name: String, +} + +struct Cand { + from: u32, + name: String, + line: u32, + column_byte: usize, + row: usize, +} + +/// Per-node metadata for the receiver-method owner lookup (mirrors the TS +/// side's scan over `this.nodes` — FIRST match wins, earlier-in-file only). +struct NodeMeta { + kind: &'static str, + name: String, +} + +pub struct Walker<'t> { + src: &'t str, + file_path: &'t str, + line_starts: Vec, + arena: Arena, + tables: Tables, + stack: Vec, + nodes_meta: Vec, + node_ids: Vec, + defined_fn_names: HashSet, + imported_names: HashSet, + fn_ref_cands: Vec, + fs_values: HashMap, + fs_value_counts: HashMap, + value_scopes: Vec>, +} + +pub fn extract(file_path: &str, source: &str) -> Result { + let grammar = crate::langs::grammar_for("go").ok_or("no go grammar")?; + let t0 = std::time::Instant::now(); + let mut parser = Parser::new(); + parser + .set_language(&grammar) + .map_err(|e| format!("set_language(go) failed: {e}"))?; + let tree = parser + .parse(source, None) + .ok_or_else(|| "parser returned null tree".to_string())?; + if tree.root_node().has_error() { + return Err("defer: parse tree contains errors — wasm recovery is canonical".to_string()); + } + + let mut w = Walker { + src: source, + file_path, + line_starts: util::line_starts(source), + arena: Arena::default(), + tables: Tables::default(), + stack: Vec::new(), + nodes_meta: Vec::new(), + node_ids: Vec::new(), + defined_fn_names: HashSet::new(), + imported_names: HashSet::new(), + fn_ref_cands: Vec::new(), + fs_values: HashMap::new(), + fs_value_counts: HashMap::new(), + value_scopes: Vec::new(), + }; + + let line_count = source.bytes().filter(|b| *b == b'\n').count() as u32 + 1; + let base_name = file_path.rsplit(['/', '\\']).next().unwrap_or(file_path); + let mut flags = BoolFlags::default(); + flags.set(FLAG_IS_EXPORTED, false); + let file_id = w.arena.put(&ids::file_node_id(file_path)); + let name_ref = w.arena.put(base_name); + let qn_ref = w.arena.put(file_path); + w.tables.push_node(&NodeRow { + kind: node_kind_index("file").unwrap(), + visibility: 0, + flags, + start_line: 1, + end_line: line_count, + start_column: 0, + end_column: 0, + name: name_ref, + qualified_name: qn_ref, + id: file_id, + docstring: NONE_STR, + signature: NONE_STR, + decorators: NONE_STR, + type_parameters: NONE_STR, + return_type: NONE_STR, + extra_json: NONE_STR, + }); + w.nodes_meta.push(NodeMeta { kind: "file", name: base_name.to_string() }); + w.node_ids.push(ids::file_node_id(file_path)); + w.stack.push(Scope { row: 0, kind: "file", name: base_name.to_string() }); + + w.visit_node(tree.root_node()); + w.flush_fn_ref_candidates(); + w.flush_value_refs(tree.root_node()); + w.stack.pop(); + + let duration_ms = t0.elapsed().as_secs_f64() * 1000.0; + let meta = build_meta(&w.tables, w.arena.len(), NONE_STR, duration_ms); + Ok(EmitOut { + meta, + nodes: w.tables.nodes, + edges: w.tables.edges, + refs: w.tables.refs, + arena: w.arena.into_vec(), + }) +} + +impl<'t> Walker<'t> { + fn text(&self, node: Node) -> &'t str { + &self.src[node.byte_range()] + } + fn line_of(&self, node: Node) -> u32 { + node.start_position().row as u32 + 1 + } + fn col_of(&self, node: Node) -> u32 { + util::col16(self.src, &self.line_starts, node.start_position().row, node.start_byte()) + } + fn end_col_of(&self, node: Node) -> u32 { + util::col16(self.src, &self.line_starts, node.end_position().row, node.end_byte()) + } + fn top_row(&self) -> u32 { + self.stack.last().map(|s| s.row).unwrap_or(0) + } + fn inside_class_like(&self) -> bool { + self.stack + .last() + .map(|s| matches!(s.kind, "class" | "struct" | "interface" | "trait" | "enum" | "module")) + .unwrap_or(false) + } + + fn push_ref_at(&mut self, from_row: u32, name: &str, kind_code: u8, node: Node) { + let name_ref = self.arena.put(name); + self.tables.push_ref(&RefRow { + from_idx: from_row, + kind: kind_code, + line: self.line_of(node), + column: self.col_of(node), + reference_name: name_ref, + candidates: NONE_STR, + from_id_str: NONE_STR, + }); + if kind_code == edge_kind_index("imports").unwrap() { + if util::simple_name().is_match(name) { + self.imported_names.insert(name.to_string()); + } else if let Some(c) = util::qualified_import().captures(name) { + self.imported_names.insert(c[1].to_string()); + } + } + } + + fn create_node(&mut self, kind: &'static str, name: &str, node: Node<'t>, extra: Extra) -> Option { + if name.is_empty() { + return None; + } + let start_line = self.line_of(node); + let id = ids::node_id(self.file_path, kind, name, start_line); + let end_line = node.end_position().row as u32 + 1; + + let qualified = extra.qualified_name.unwrap_or_else(|| { + let mut parts: Vec<&str> = Vec::new(); + for s in &self.stack { + if s.kind != "file" { + parts.push(&s.name); + } + } + let mut qn = parts.join("::"); + if !qn.is_empty() { + qn.push_str("::"); + } + qn.push_str(name); + qn + }); + + let mut flags = BoolFlags::default(); + if let Some(v) = extra.is_exported { + flags.set(FLAG_IS_EXPORTED, v); + } + let name_ref = self.arena.put(name); + let qn_ref = self.arena.put(&qualified); + let id_ref = self.arena.put(&id); + let doc_ref = opt_str(&mut self.arena, extra.docstring.as_deref()); + let sig_ref = opt_str(&mut self.arena, extra.signature.as_deref()); + let ret_ref = opt_str(&mut self.arena, extra.return_type.as_deref()); + let row = self.tables.push_node(&NodeRow { + kind: node_kind_index(kind).unwrap(), + visibility: 0, + flags, + start_line, + end_line, + start_column: self.col_of(node), + end_column: self.end_col_of(node), + name: name_ref, + qualified_name: qn_ref, + id: id_ref, + docstring: doc_ref, + signature: sig_ref, + decorators: NONE_STR, + type_parameters: NONE_STR, + return_type: ret_ref, + extra_json: NONE_STR, + }); + self.nodes_meta.push(NodeMeta { kind, name: name.to_string() }); + self.node_ids.push(id); + + let parent_row = self.top_row(); + self.tables.push_edge(&EdgeRow { + source_idx: parent_row, + target_idx: row, + kind: edge_kind_index("contains").unwrap(), + provenance: 0, + line: NONE, + column: NONE, + metadata_json: NONE_STR, + source_id_str: NONE_STR, + target_id_str: NONE_STR, + }); + + if kind == "function" || kind == "method" { + self.defined_fn_names.insert(name.to_string()); + } + let target_kind_ok = kind == "constant" || kind == "variable"; + if target_kind_ok + && util::utf16_len(name) >= 3 + && util::has_upper_or_underscore().is_match(name) + { + let parent_ok = self + .stack + .last() + .map(|s| matches!(s.kind, "file" | "class" | "module" | "struct" | "enum")) + .unwrap_or(false); + if parent_ok { + self.fs_values.insert(name.to_string(), row); + *self.fs_value_counts.entry(name.to_string()).or_insert(0) += 1; + } + } + if matches!(kind, "function" | "method" | "constant" | "variable") { + self.value_scopes.push(ValueScope { row, node, name: name.to_string() }); + } + Some(row) + } + + fn extract_name(&self, node: Node) -> String { + if let Some(name_node) = node.child_by_field_name("name") { + return self.text(name_node).to_string(); + } + for i in 0..node.named_child_count() { + if let Some(c) = node.named_child(i) { + if matches!(c.kind(), "identifier" | "type_identifier" | "simple_identifier" | "constant") { + return self.text(c).to_string(); + } + } + } + "".to_string() + } + + /// goExtractor.getSignature: params + ' ' + result. + fn signature_of(&self, node: Node) -> Option { + let params = node.child_by_field_name("parameters")?; + let mut sig = self.text(params).to_string(); + if let Some(result) = node.child_by_field_name("result") { + sig.push(' '); + sig.push_str(self.text(result)); + } + Some(sig) + } + + /// goExtractor.isExported: uppercase first letter of the name field. + fn is_exported(&self, node: Node) -> bool { + if let Some(name_node) = node.child_by_field_name("name") { + let text = self.text(name_node); + return text.as_bytes().first().map(|b| b.is_ascii_uppercase()).unwrap_or(false); + } + false + } + + /// extractGoReturnType (languages/go.ts). + fn return_type_of(&self, node: Node) -> Option { + let mut result = node.child_by_field_name("result")?; + if result.kind() == "parameter_list" { + let first = (0..result.named_child_count()) + .filter_map(|i| result.named_child(i)) + .find(|c| c.kind() == "parameter_declaration")?; + result = first.child_by_field_name("type").unwrap_or(first); + } + if result.kind() == "pointer_type" { + result = (0..result.named_child_count()) + .filter_map(|i| result.named_child(i)) + .find(|c| matches!(c.kind(), "type_identifier" | "qualified_type" | "generic_type")) + .unwrap_or(result); + } + let text = self.text(result).trim(); + let text = text.strip_prefix('*').unwrap_or(text); + let text = generic_angle_re().replace_all(text, ""); + let text = bracket_args_re().replace_all(&text, ""); + let last = text.rsplit('.').next().unwrap_or("").trim().to_string(); + if last.is_empty() || !simple_ident_re().is_match(&last) { + return None; + } + Some(last) + } + + /// goExtractor.getReceiverType: the regex over the receiver's text. + fn receiver_type_of(&self, node: Node) -> Option { + let receiver = node.child_by_field_name("receiver")?; + let text = self.text(receiver); + receiver_re().captures(text).map(|c| c[1].to_string()) + } + + // --- visitNode ------------------------------------------------------------ + + fn visit_node(&mut self, node: Node<'t>) { + let kind = node.kind(); + let mut skip_children = false; + + self.maybe_capture_fn_refs(node); + + if kind == "function_declaration" { + self.extract_function(node); + skip_children = true; + } else if kind == "method_declaration" { + self.extract_method(node); + skip_children = true; + } else if kind == "type_spec" { + skip_children = self.extract_type_alias(node); + } else if matches!(kind, "var_declaration" | "short_var_declaration" | "const_declaration") + && !self.inside_class_like() + { + self.extract_variable(node); + self.scan_fn_ref_subtree(node, 0); + skip_children = true; + } else if kind == "import_declaration" { + self.extract_import(node); + } else if kind == "call_expression" { + self.extract_call(node); + } else if kind == "composite_literal" { + self.extract_instantiation(node); + } + + if !skip_children { + for i in 0..node.named_child_count() { + if let Some(c) = node.named_child(i) { + self.visit_node(c); + } + } + } + } + + fn visit_function_body(&mut self, body: Node<'t>) { + self.visit_for_calls_and_structure(body); + } + + fn visit_for_calls_and_structure(&mut self, node: Node<'t>) { + let kind = node.kind(); + self.maybe_capture_fn_refs(node); + + if kind == "call_expression" { + self.extract_call(node); + } else if kind == "composite_literal" { + self.extract_instantiation(node); + } + + if kind == "function_declaration" { + let name = self.extract_name(node); + if name != "" { + self.extract_function(node); + return; + } + } + + for i in 0..node.named_child_count() { + if let Some(c) = node.named_child(i) { + self.visit_for_calls_and_structure(c); + } + } + } + + // --- extractors -------------------------------------------------------------- + + fn extract_function(&mut self, node: Node<'t>) { + // (getReceiverType only matches method_declaration's receiver field — + // function_declaration has none, so no reroute happens here) + let name = self.extract_name(node); + if name == "" { + if let Some(body) = node.child_by_field_name("body") { + self.visit_function_body(body); + } + return; + } + let extra = Extra { + docstring: preceding_docstring(node, self.src), + signature: self.signature_of(node), + is_exported: Some(self.is_exported(node)), + return_type: self.return_type_of(node), + ..Extra::default() + }; + let Some(row) = self.create_node("function", &name, node, extra) else { return }; + self.extract_type_annotations(node, row); + self.stack.push(Scope { row, kind: "function", name }); + if let Some(body) = node.child_by_field_name("body") { + self.visit_function_body(body); + } + self.stack.pop(); + } + + fn extract_method(&mut self, node: Node<'t>) { + // methodsAreTopLevel: always a method. Receiver-qualified name + + // a contains edge from the FIRST earlier struct/class/enum/trait + // node of the receiver's name (mirrors the this.nodes.find scan). + let receiver_type = self.receiver_type_of(node); + let name = self.extract_name(node); + let extra = Extra { + docstring: preceding_docstring(node, self.src), + signature: self.signature_of(node), + return_type: self.return_type_of(node), + qualified_name: receiver_type.as_ref().map(|r| format!("{r}::{name}")), + ..Extra::default() // extractMethod passes no isExported + }; + let Some(row) = self.create_node("method", &name, node, extra) else { return }; + + if let Some(receiver_type) = &receiver_type { + if !self.inside_class_like() { + let owner_row = self + .nodes_meta + .iter() + .position(|m| { + m.name == *receiver_type + && matches!(m.kind, "struct" | "class" | "enum" | "trait") + }) + .map(|i| i as u32); + if let Some(owner_row) = owner_row { + self.tables.push_edge(&EdgeRow { + source_idx: owner_row, + target_idx: row, + kind: edge_kind_index("contains").unwrap(), + provenance: 0, + line: NONE, + column: NONE, + metadata_json: NONE_STR, + source_id_str: NONE_STR, + target_id_str: NONE_STR, + }); + } + } + } + + self.extract_type_annotations(node, row); + self.stack.push(Scope { row, kind: "method", name }); + if let Some(body) = node.child_by_field_name("body") { + self.visit_function_body(body); + } + self.stack.pop(); + } + + /// extractTypeAlias for Go: type_spec → struct / interface / plain alias. + fn extract_type_alias(&mut self, node: Node<'t>) -> bool { + let name = self.extract_name(node); + if name == "" { + return false; + } + let docstring = preceding_docstring(node, self.src); + let is_exported = Some(self.is_exported(node)); + let type_child = node.child_by_field_name("type"); + let resolved = type_child.map(|t| t.kind()); + + if resolved == Some("struct_type") { + let Some(row) = self.create_node( + "struct", + &name, + node, + Extra { docstring, is_exported, ..Extra::default() }, + ) else { + return true; + }; + self.stack.push(Scope { row, kind: "struct", name }); + if let Some(type_child) = type_child { + // Struct embedding → extends (field_declaration without a + // field_identifier), reached via the inheritance recursion. + self.extract_inheritance(type_child, row); + let body = type_child.child_by_field_name("body").unwrap_or(type_child); + for i in 0..body.named_child_count() { + if let Some(c) = body.named_child(i) { + self.visit_node(c); + } + } + } + self.stack.pop(); + return true; + } + + if resolved == Some("interface_type") { + let Some(row) = self.create_node( + "interface", + &name, + node, + Extra { docstring, is_exported, ..Extra::default() }, + ) else { + return true; + }; + if let Some(type_child) = type_child { + self.extract_inheritance(type_child, row); + self.extract_go_interface_methods(type_child, row, &name); + } + return true; + } + + self.create_node( + "type_alias", + &name, + node, + Extra { docstring, is_exported, ..Extra::default() }, + ); + // (go type_spec has no `value` field — no type-ref walk; TS/tsx member + // extraction is TS-family-only) + false + } + + /// extractGoInterfaceMethods: method_elem/method_spec → method nodes. + fn extract_go_interface_methods(&mut self, interface_type: Node<'t>, iface_row: u32, iface_name: &str) { + self.stack.push(Scope { row: iface_row, kind: "interface", name: iface_name.to_string() }); + for i in 0..interface_type.named_child_count() { + let Some(m) = interface_type.named_child(i) else { continue }; + if !matches!(m.kind(), "method_elem" | "method_spec") { + continue; + } + let name_node = m.child_by_field_name("name").or_else(|| m.named_child(0)); + let Some(name_node) = name_node else { continue }; + let mname = self.text(name_node).to_string(); + if !mname.is_empty() { + let signature = self.signature_of(m); + self.create_node("method", &mname, m, Extra { signature, ..Extra::default() }); + } + } + self.stack.pop(); + } + + /// extractVariable's Go branch: var/const specs + short_var_declaration. + fn extract_variable(&mut self, node: Node<'t>) { + let docstring = preceding_docstring(node, self.src); + let is_const_decl = node.kind() == "const_declaration"; + + for i in 0..node.named_child_count() { + let Some(spec) = node.named_child(i) else { continue }; + if !matches!(spec.kind(), "var_spec" | "const_spec") { + continue; + } + let mut var_row: Option = None; + if let Some(name_node) = spec.named_child(0) { + if name_node.kind() == "identifier" { + let name = self.text(name_node).to_string(); + let value_node = if spec.named_child_count() > 1 { + spec.named_child(spec.named_child_count() - 1) + } else { + None + }; + let signature = value_node.map(|v| util::init_signature(self.text(v))); + var_row = self.create_node( + if is_const_decl { "constant" } else { "variable" }, + &name, + spec, + Extra { docstring: docstring.clone(), signature, ..Extra::default() }, + ); + } + } + // Walk the initializer ATTRIBUTED to the declared symbol (#693). + if let Some(value_field) = spec.child_by_field_name("value") { + if let Some(row) = var_row { + let name = self.nodes_meta[row as usize].name.clone(); + self.stack.push(Scope { row, kind: "variable", name }); + self.visit_function_body(value_field); + self.stack.pop(); + } else { + self.visit_function_body(value_field); + } + } + } + + if node.kind() == "short_var_declaration" { + let left = node.child_by_field_name("left"); + let right = node.child_by_field_name("right"); + if let Some(left) = left { + let identifiers: Vec = if left.kind() == "expression_list" { + (0..left.named_child_count()) + .filter_map(|i| left.named_child(i)) + .filter(|c| c.kind() == "identifier") + .collect() + } else { + vec![left] + }; + for id in identifiers { + let name = self.text(id).to_string(); + let signature = right.map(|r| util::init_signature(self.text(r))); + self.create_node( + "variable", + &name, + node, + Extra { docstring: docstring.clone(), signature, ..Extra::default() }, + ); + } + } + } + } + + /// extractImport's Go branch: one import node + ref per import_spec. + fn extract_import(&mut self, node: Node<'t>) { + let parent = self.top_row(); + let imports_kind = edge_kind_index("imports").unwrap(); + let mut handle_spec = |w: &mut Self, spec: Node<'t>| { + let lit = (0..spec.named_child_count()) + .filter_map(|i| spec.named_child(i)) + .find(|c| c.kind() == "interpreted_string_literal"); + let Some(lit) = lit else { return }; + let import_path: String = w + .text(lit) + .chars() + .filter(|c| *c != '\'' && *c != '"') + .collect(); + if import_path.is_empty() { + return; + } + let signature = w.text(spec).trim().to_string(); + w.create_node( + "import", + &import_path, + spec, + Extra { signature: Some(signature), ..Extra::default() }, + ); + w.push_ref_at(parent, &import_path, imports_kind, spec); + }; + + let spec_list = (0..node.named_child_count()) + .filter_map(|i| node.named_child(i)) + .find(|c| c.kind() == "import_spec_list"); + if let Some(list) = spec_list { + for i in 0..list.named_child_count() { + if let Some(spec) = list.named_child(i) { + if spec.kind() == "import_spec" { + handle_spec(self, spec); + } + } + } + } else { + let spec = (0..node.named_child_count()) + .filter_map(|i| node.named_child(i)) + .find(|c| c.kind() == "import_spec"); + if let Some(spec) = spec { + handle_spec(self, spec); + } + } + } + + /// extractCall — Go's generic-tail paths (selector_expression callees). + fn extract_call(&mut self, node: Node<'t>) { + if self.stack.is_empty() { + return; + } + let func = node + .child_by_field_name("function") + .or_else(|| node.named_child(0)); + let mut callee_name = String::new(); + + if let Some(func) = func { + if func.kind() == "selector_expression" { + let property = func + .child_by_field_name("property") + .or_else(|| func.child_by_field_name("field")); + if let Some(property) = property { + let method_name = self.text(property); + let receiver = func + .child_by_field_name("object") + .or_else(|| func.child_by_field_name("operand")) + .or_else(|| func.child_by_field_name("argument")) + .or_else(|| func.named_child(0)); + if let Some(r) = receiver { + if is_literal_receiver(r.kind()) { + return; + } + } + if let Some(r) = receiver { + match r.kind() { + "identifier" | "simple_identifier" | "field_identifier" => { + let receiver_name = self.text(r); + if !matches!(receiver_name, "self" | "this" | "cls" | "super") { + callee_name = format!("{receiver_name}.{method_name}"); + } else { + callee_name = method_name.to_string(); + } + } + "call_expression" => { + // Bare package-level factory chain `New().Method()` + // re-encodes; instance chains keep the bare name. + let inner_fn = r.child_by_field_name("function"); + let reencode = + inner_fn.map(|f| f.kind() == "identifier").unwrap_or(false); + if reencode { + let inner: String = self + .text(inner_fn.unwrap()) + .replace("->", ".") + .chars() + .filter(|c| !c.is_whitespace()) + .collect(); + callee_name = format!("{inner}().{method_name}"); + } else { + callee_name = method_name.to_string(); + } + } + "selector_expression" => { + // 2-hop field chain `t.conn.Exec` (#1276). + let chain: String = self + .text(r) + .chars() + .filter(|c| !c.is_whitespace()) + .collect(); + if go_two_hop_re().is_match(&chain) { + callee_name = format!("{chain}.{method_name}"); + } else { + callee_name = method_name.to_string(); + } + } + _ => { + callee_name = method_name.to_string(); + } + } + } else { + callee_name = method_name.to_string(); + } + } + } else { + callee_name = self.text(func).to_string(); + } + } + + if !callee_name.is_empty() { + // `(*T)(x)` conversions normalize to `T`. + if let Some(c) = util::paren_conversion().captures(&callee_name) { + callee_name = c[1].to_string(); + } + let from = self.top_row(); + self.push_ref_at(from, &callee_name.clone(), edge_kind_index("calls").unwrap(), node); + } + } + + /// extractInstantiation's composite_literal branch: named struct types + /// only; the package qualifier is KEPT. + fn extract_instantiation(&mut self, node: Node<'t>) { + if self.stack.is_empty() { + return; + } + let ctor = node + .child_by_field_name("constructor") + .or_else(|| node.child_by_field_name("type")) + .or_else(|| node.child_by_field_name("name")) + .or_else(|| node.named_child(0)); + let Some(ctor) = ctor else { return }; + if !matches!(ctor.kind(), "type_identifier" | "qualified_type") { + return; + } + let mut go_type = self.text(ctor).trim().to_string(); + if let Some(br) = go_type.find('[') { + if br > 0 { + go_type.truncate(br); + go_type = go_type.trim().to_string(); + } + } + if !go_type.is_empty() { + let from = self.top_row(); + self.push_ref_at(from, &go_type, edge_kind_index("instantiates").unwrap(), node); + } + } + + /// extractInheritance — the Go branches: interface embedding + /// (constraint_elem) and struct embedding (field_declaration without a + /// field_identifier), plus the field_declaration_list recursion. + fn extract_inheritance(&mut self, node: Node<'t>, class_row: u32) { + let extends_kind = edge_kind_index("extends").unwrap(); + for i in 0..node.named_child_count() { + let Some(child) = node.named_child(i) else { continue }; + match child.kind() { + "constraint_elem" => { + let type_id = (0..child.named_child_count()) + .filter_map(|j| child.named_child(j)) + .find(|c| c.kind() == "type_identifier"); + if let Some(type_id) = type_id { + let name = self.text(type_id).to_string(); + self.push_ref_at(class_row, &name, extends_kind, type_id); + } + } + "field_declaration" => { + let has_field_identifier = (0..child.named_child_count()) + .filter_map(|j| child.named_child(j)) + .any(|c| c.kind() == "field_identifier"); + if !has_field_identifier { + let type_id = (0..child.named_child_count()) + .filter_map(|j| child.named_child(j)) + .find(|c| c.kind() == "type_identifier"); + if let Some(type_id) = type_id { + let name = self.text(type_id).to_string(); + self.push_ref_at(class_row, &name, extends_kind, type_id); + } + } + } + "field_declaration_list" | "class_heritage" => { + self.extract_inheritance(child, class_row); + } + _ => {} + } + } + } + + /// extractTypeAnnotations — Go's returnField is `result`. + fn extract_type_annotations(&mut self, node: Node<'t>, from_row: u32) { + if let Some(params) = node.child_by_field_name("parameters") { + self.extract_type_refs_from_subtree(params, from_row); + } + if let Some(ret) = node.child_by_field_name("result") { + self.extract_type_refs_from_subtree(ret, from_row); + } + let type_annotation = (0..node.named_child_count()) + .filter_map(|i| node.named_child(i)) + .find(|c| c.kind() == "type_annotation"); + if let Some(ta) = type_annotation { + self.extract_type_refs_from_subtree(ta, from_row); + } + } + + fn extract_type_refs_from_subtree(&mut self, node: Node<'t>, from_row: u32) { + if node.kind() == "type_identifier" { + let type_name = self.text(node).to_string(); + if !type_name.is_empty() && !is_builtin_type(&type_name) { + self.push_ref_at(from_row, &type_name, edge_kind_index("references").unwrap(), node); + } + return; + } + for i in 0..node.named_child_count() { + if let Some(c) = node.named_child(i) { + self.extract_type_refs_from_subtree(c, from_row); + } + } + } + + // --- fn refs (GO_SPEC, with the literal_element/expression_list layers) -------- + + fn maybe_capture_fn_refs(&mut self, node: Node<'t>) { + let (mode, field): (&str, &str) = match node.kind() { + "argument_list" => ("args", ""), + "assignment_statement" => ("rhs", "right"), + "short_var_declaration" => ("rhs", "right"), + "var_spec" => ("varinit", "value"), + "keyed_element" => ("value", ""), // value = LAST named child + "literal_value" => ("list", ""), + _ => return, + }; + if self.stack.is_empty() { + return; + } + let from = self.top_row(); + + let mut values: Vec = Vec::new(); + match mode { + "args" | "list" => { + for i in 0..node.named_child_count() { + if let Some(c) = node.named_child(i) { + values.push(c); + } + } + } + "rhs" => { + if let Some(rhs) = node.child_by_field_name(field) { + let lhs_text = node + .child_by_field_name("left") + .map(|l| self.text(l)) + .unwrap_or(""); + let lhs_last = util::lhs_last_name() + .captures(lhs_text) + .and_then(|c| c.get(1)) + .map(|m| m.as_str()); + if !(lhs_last.is_some() && lhs_last == Some(self.text(rhs).trim())) { + values.push(rhs); + } + } + } + "value" => { + let v = node + .child_by_field_name("value") + .or_else(|| { + if node.named_child_count() > 0 { + node.named_child(node.named_child_count() - 1) + } else { + None + } + }); + if let Some(v) = v { + values.push(v); + } + } + _ => { + // varinit — Go var_spec names are plain identifiers (no + // destructuring patterns to skip). + if let Some(v) = node.child_by_field_name(field) { + values.push(v); + } + } + } + + for v in values { + self.normalize_fn_ref_value(v, from, 0); + } + } + + /// normalizeValue with GO_SPEC's transparent layers (literal_element, + /// expression_list — both fan out to named children). + fn normalize_fn_ref_value(&mut self, v: Node<'t>, from: u32, depth: u32) { + if depth > 4 { + return; + } + match v.kind() { + "identifier" => { + let name = self.text(v).to_string(); + if name.is_empty() || is_stoplisted(&name) { + return; + } + let p = v.start_position(); + self.fn_ref_cands.push(Cand { + from, + name, + line: p.row as u32 + 1, + column_byte: v.start_byte(), + row: p.row, + }); + } + "literal_element" | "expression_list" => { + for i in 0..v.named_child_count() { + if let Some(c) = v.named_child(i) { + self.normalize_fn_ref_value(c, from, depth + 1); + } + } + } + _ => {} + } + } + + fn scan_fn_ref_subtree(&mut self, node: Node<'t>, depth: u32) { + if depth > 12 { + return; + } + if depth > 0 + && matches!( + node.kind(), + "function_declaration" | "arrow_function" | "function_expression" + | "lambda_literal" | "lambda_expression" + ) + { + return; + } + self.maybe_capture_fn_refs(node); + for i in 0..node.named_child_count() { + if let Some(c) = node.named_child(i) { + self.scan_fn_ref_subtree(c, depth + 1); + } + } + } + + fn flush_fn_ref_candidates(&mut self) { + let cands = std::mem::take(&mut self.fn_ref_cands); + if cands.is_empty() || util::is_generated_file(self.file_path) { + return; + } + let mut seen: HashSet<(String, String)> = HashSet::new(); + for c in cands { + if !c.name.starts_with("this.") + && !c.name.contains("::") + && !self.defined_fn_names.contains(&c.name) + && !self.imported_names.contains(&c.name) + { + continue; + } + if !seen.insert((self.node_ids[c.from as usize].clone(), c.name.clone())) { + continue; + } + let column = util::col16(self.src, &self.line_starts, c.row, c.column_byte); + let name_ref = self.arena.put(&c.name); + self.tables.push_ref(&RefRow { + from_idx: c.from, + kind: FUNCTION_REF_CODE, + line: c.line, + column, + reference_name: name_ref, + candidates: NONE_STR, + from_id_str: NONE_STR, + }); + } + } + + // --- value refs ------------------------------------------------------------------- + + fn flush_value_refs(&mut self, root: Node<'t>) { + let scopes = std::mem::take(&mut self.value_scopes); + let mut targets = std::mem::take(&mut self.fs_values); + let counts = std::mem::take(&mut self.fs_value_counts); + if std::env::var("CODEGRAPH_VALUE_REFS").as_deref() == Ok("0") { + return; + } + if targets.is_empty() || scopes.is_empty() || util::is_generated_file(self.file_path) { + return; + } + + // Shadow prune — Go declarator shapes: const_spec/var_spec (name = + // first child) and short_var_declaration (left / expression_list). + let mut decl_counts: HashMap<&str, u32> = HashMap::new(); + let mut bump = |decl_counts: &mut HashMap<&'t str, u32>, name_node: Option>, src: &'t str, targets: &HashMap| { + if let Some(n) = name_node { + if matches!(n.kind(), "identifier" | "simple_identifier") { + let nm = &src[n.byte_range()]; + if targets.contains_key(nm) { + *decl_counts.entry(nm).or_insert(0) += 1; + } + } + } + }; + let mut dstack: Vec = vec![root]; + let mut dvisited = 0usize; + while let Some(n) = dstack.pop() { + if dvisited >= MAX_VALUE_REF_NODES { + break; + } + dvisited += 1; + match n.kind() { + "const_spec" | "var_spec" => bump(&mut decl_counts, n.named_child(0), self.src, &targets), + "short_var_declaration" => { + let left = n + .child_by_field_name("left") + .or_else(|| n.child_by_field_name("pattern")) + .or_else(|| n.named_child(0)); + if let Some(left) = left { + if left.kind() == "identifier" { + bump(&mut decl_counts, Some(left), self.src, &targets); + } else { + for i in 0..left.named_child_count() { + bump(&mut decl_counts, left.named_child(i), self.src, &targets); + } + } + } + } + _ => {} + } + for i in 0..n.named_child_count() { + if let Some(c) = n.named_child(i) { + dstack.push(c); + } + } + } + let shadowed: Vec = decl_counts + .iter() + .filter(|(nm, c)| **c > counts.get(**nm).copied().unwrap_or(1)) + .map(|(nm, _)| nm.to_string()) + .collect(); + for nm in shadowed { + targets.remove(&nm); + } + if targets.is_empty() { + return; + } + + let refs_kind = edge_kind_index("references").unwrap(); + for scope in &scopes { + let mut seen: HashSet<&str> = HashSet::new(); + let mut stack: Vec = vec![scope.node]; + let mut visited = 0usize; + while let Some(n) = stack.pop() { + if visited >= MAX_VALUE_REF_NODES { + break; + } + visited += 1; + if matches!(n.kind(), "identifier" | "constant" | "name" | "simple_identifier") { + let ref_name = self.text(n); + if let Some(&target_row) = targets.get(ref_name) { + let target_id = self.node_ids[target_row as usize].as_str(); + if target_id != self.node_ids[scope.row as usize] + && ref_name != scope.name + && !seen.contains(&target_id) + { + seen.insert(target_id); + let meta = self.arena.put(r#"{"valueRef":true}"#); + self.tables.push_edge(&EdgeRow { + source_idx: scope.row, + target_idx: target_row, + kind: refs_kind, + provenance: 0, + line: NONE, + column: NONE, + metadata_json: meta, + source_id_str: NONE_STR, + target_id_str: NONE_STR, + }); + } + } + } + for i in 0..n.named_child_count() { + if let Some(c) = n.named_child(i) { + stack.push(c); + } + } + } + } + } +} + +fn is_stoplisted(name: &str) -> bool { + matches!( + name, + "this" | "self" | "super" | "null" | "nil" | "true" | "false" | "undefined" | "new" + | "NULL" | "nullptr" | "None" + ) +} + +fn is_literal_receiver(kind: &str) -> bool { + matches!( + kind, + "string" | "string_literal" | "interpreted_string_literal" | "raw_string_literal" + | "template_string" | "concatenated_string" | "formatted_string" | "f_string" + | "line_string_literal" | "string_content" | "heredoc_body" + | "number" | "number_literal" | "integer" | "integer_literal" | "float" + | "float_literal" | "int_literal" | "decimal_integer_literal" | "real_literal" + | "char_literal" | "character_literal" | "rune_literal" | "regex" | "regex_literal" + | "true" | "false" | "boolean_literal" | "bool_literal" | "none" | "null" | "nil" + | "null_literal" | "undefined" + | "list" | "list_literal" | "array" | "array_literal" | "array_creation_expression" + | "dictionary" | "dict_literal" | "object" | "tuple" | "set" + ) +} + +/// BUILTIN_TYPES (shared table). +fn is_builtin_type(name: &str) -> bool { + matches!( + name, + "string" | "number" | "boolean" | "void" | "null" | "undefined" | "never" | "any" + | "unknown" | "object" | "symbol" | "bigint" | "true" | "false" + | "str" | "bool" | "i8" | "i16" | "i32" | "i64" | "i128" | "isize" + | "u8" | "u16" | "u32" | "u64" | "u128" | "usize" | "f32" | "f64" | "char" + | "int" | "long" | "short" | "byte" | "float" | "double" + | "int8" | "int16" | "int32" | "int64" | "uint8" | "uint16" | "uint32" | "uint64" + | "float32" | "float64" | "complex64" | "complex128" | "rune" | "error" + | "Int" | "Long" | "Short" | "Byte" | "Float" | "Double" | "Boolean" | "Char" + | "Unit" | "String" | "Any" | "AnyRef" | "AnyVal" | "Nothing" | "Null" + ) +} + +fn opt_str(arena: &mut Arena, s: Option<&str>) -> StrRef { + match s { + Some(s) => arena.put(s), + None => NONE_STR, + } +} diff --git a/codegraph-kernel/src/langs.rs b/codegraph-kernel/src/langs.rs index 8473b55..29c746f 100644 --- a/codegraph-kernel/src/langs.rs +++ b/codegraph-kernel/src/langs.rs @@ -15,7 +15,8 @@ use tree_sitter::Language; /// Languages this kernel binary can extract (reported by contractInfo; /// TS-side routing policy decides what actually routes). -pub const LANGUAGES: [&str; 5] = ["typescript", "tsx", "javascript", "jsx", "java"]; +pub const LANGUAGES: [&str; 7] = + ["typescript", "tsx", "javascript", "jsx", "java", "python", "go"]; pub fn grammar_for(language: &str) -> Option { match language { @@ -23,6 +24,8 @@ pub fn grammar_for(language: &str) -> Option { "tsx" => Some(tree_sitter_typescript::LANGUAGE_TSX.into()), "javascript" | "jsx" => Some(tree_sitter_javascript::LANGUAGE.into()), "java" => Some(tree_sitter_java::LANGUAGE.into()), + "python" => Some(tree_sitter_python::LANGUAGE.into()), + "go" => Some(tree_sitter_go::LANGUAGE.into()), _ => None, } } diff --git a/codegraph-kernel/src/lib.rs b/codegraph-kernel/src/lib.rs index 9bc2382..6ef491d 100644 --- a/codegraph-kernel/src/lib.rs +++ b/codegraph-kernel/src/lib.rs @@ -19,9 +19,11 @@ mod buffers; mod docstring; mod ids; +mod go; mod java; mod langs; mod textutil; +mod python; mod tsjs; use napi::bindgen_prelude::*; @@ -99,6 +101,8 @@ pub fn grammar_info(language: String) -> Option { pub fn extract_file(file_path: String, content: String, language: String) -> Result { let out = match language.as_str() { "java" => java::extract(&file_path, &content).map_err(Error::from_reason)?, + "python" => python::extract(&file_path, &content).map_err(Error::from_reason)?, + "go" => go::extract(&file_path, &content).map_err(Error::from_reason)?, _ => tsjs::extract(&file_path, &content, &language).map_err(Error::from_reason)?, }; Ok(ExtractBuffers { diff --git a/codegraph-kernel/src/python.rs b/codegraph-kernel/src/python.rs new file mode 100644 index 0000000..0bffcfd --- /dev/null +++ b/codegraph-kernel/src/python.rs @@ -0,0 +1,978 @@ +//! Python extraction — a faithful Rust port of `TreeSitterExtractor`'s Python +//! paths (src/extraction/tree-sitter.ts) plus languages/python.ts. +//! +//! Same porting contract as tsjs/java: behavior parity, bug-for-bug — +//! including the quirks: decorates refs only fire for bare-identifier +//! decorators (`@staticmethod` yes, `@app.route(...)` no — python's `call` +//! kind isn't `call_expression`), module-level assignments always extract as +//! `variable` (no isConst hook), and `self.method` fn-ref candidates carry the +//! BARE attribute name. Python is not a TYPE_ANNOTATION language — no type +//! refs anywhere. Files with parse errors defer to wasm. + +use crate::buffers::{ + build_meta, edge_kind_index, node_kind_index, Arena, BoolFlags, EdgeRow, EmitOut, NodeRow, + RefRow, StrRef, Tables, FLAG_IS_ASYNC, FLAG_IS_STATIC, FUNCTION_REF_CODE, NONE, NONE_STR, +}; +use crate::docstring::preceding_docstring; +use crate::ids; +use crate::textutil as util; +use std::collections::{HashMap, HashSet}; +use tree_sitter::{Node, Parser}; + +const MAX_VALUE_REF_NODES: usize = 20_000; + +struct Scope { + row: u32, + kind: &'static str, + name: String, +} + +#[derive(Default)] +struct Extra { + docstring: Option, + signature: Option, + is_async: Option, + is_static: Option, +} + +struct ValueScope<'t> { + row: u32, + node: Node<'t>, + name: String, +} + +struct Cand { + from: u32, + name: String, + line: u32, + column_byte: usize, + row: usize, +} + +pub struct Walker<'t> { + src: &'t str, + file_path: &'t str, + line_starts: Vec, + arena: Arena, + tables: Tables, + stack: Vec, + node_ids: Vec, + defined_fn_names: HashSet, + imported_names: HashSet, + fn_ref_cands: Vec, + fs_values: HashMap, + fs_value_counts: HashMap, + value_scopes: Vec>, +} + +pub fn extract(file_path: &str, source: &str) -> Result { + let grammar = crate::langs::grammar_for("python").ok_or("no python grammar")?; + let t0 = std::time::Instant::now(); + let mut parser = Parser::new(); + parser + .set_language(&grammar) + .map_err(|e| format!("set_language(python) failed: {e}"))?; + let tree = parser + .parse(source, None) + .ok_or_else(|| "parser returned null tree".to_string())?; + if tree.root_node().has_error() { + return Err("defer: parse tree contains errors — wasm recovery is canonical".to_string()); + } + + let mut w = Walker { + src: source, + file_path, + line_starts: util::line_starts(source), + arena: Arena::default(), + tables: Tables::default(), + stack: Vec::new(), + node_ids: Vec::new(), + defined_fn_names: HashSet::new(), + imported_names: HashSet::new(), + fn_ref_cands: Vec::new(), + fs_values: HashMap::new(), + fs_value_counts: HashMap::new(), + value_scopes: Vec::new(), + }; + + let line_count = source.bytes().filter(|b| *b == b'\n').count() as u32 + 1; + let base_name = file_path.rsplit(['/', '\\']).next().unwrap_or(file_path); + let mut flags = BoolFlags::default(); + flags.set(crate::buffers::FLAG_IS_EXPORTED, false); + let file_id = w.arena.put(&ids::file_node_id(file_path)); + let name_ref = w.arena.put(base_name); + let qn_ref = w.arena.put(file_path); + w.tables.push_node(&NodeRow { + kind: node_kind_index("file").unwrap(), + visibility: 0, + flags, + start_line: 1, + end_line: line_count, + start_column: 0, + end_column: 0, + name: name_ref, + qualified_name: qn_ref, + id: file_id, + docstring: NONE_STR, + signature: NONE_STR, + decorators: NONE_STR, + type_parameters: NONE_STR, + return_type: NONE_STR, + extra_json: NONE_STR, + }); + w.node_ids.push(ids::file_node_id(file_path)); + w.stack.push(Scope { row: 0, kind: "file", name: base_name.to_string() }); + + w.visit_node(tree.root_node()); + w.flush_fn_ref_candidates(); + w.flush_value_refs(tree.root_node()); + w.stack.pop(); + + let duration_ms = t0.elapsed().as_secs_f64() * 1000.0; + let meta = build_meta(&w.tables, w.arena.len(), NONE_STR, duration_ms); + Ok(EmitOut { + meta, + nodes: w.tables.nodes, + edges: w.tables.edges, + refs: w.tables.refs, + arena: w.arena.into_vec(), + }) +} + +impl<'t> Walker<'t> { + fn text(&self, node: Node) -> &'t str { + &self.src[node.byte_range()] + } + fn line_of(&self, node: Node) -> u32 { + node.start_position().row as u32 + 1 + } + fn col_of(&self, node: Node) -> u32 { + util::col16(self.src, &self.line_starts, node.start_position().row, node.start_byte()) + } + fn end_col_of(&self, node: Node) -> u32 { + util::col16(self.src, &self.line_starts, node.end_position().row, node.end_byte()) + } + fn top_row(&self) -> u32 { + self.stack.last().map(|s| s.row).unwrap_or(0) + } + fn inside_class_like(&self) -> bool { + self.stack + .last() + .map(|s| matches!(s.kind, "class" | "struct" | "interface" | "trait" | "enum" | "module")) + .unwrap_or(false) + } + + fn push_ref_at(&mut self, from_row: u32, name: &str, kind_code: u8, node: Node) { + let name_ref = self.arena.put(name); + self.tables.push_ref(&RefRow { + from_idx: from_row, + kind: kind_code, + line: self.line_of(node), + column: self.col_of(node), + reference_name: name_ref, + candidates: NONE_STR, + from_id_str: NONE_STR, + }); + if kind_code == edge_kind_index("imports").unwrap() { + if util::simple_name().is_match(name) { + self.imported_names.insert(name.to_string()); + } else if let Some(c) = util::qualified_import().captures(name) { + self.imported_names.insert(c[1].to_string()); + } + } + } + + fn create_node(&mut self, kind: &'static str, name: &str, node: Node<'t>, extra: Extra) -> Option { + if name.is_empty() { + return None; + } + let start_line = self.line_of(node); + let id = ids::node_id(self.file_path, kind, name, start_line); + let end_line = node.end_position().row as u32 + 1; + + let qualified = { + let mut parts: Vec<&str> = Vec::new(); + for s in &self.stack { + if s.kind != "file" { + parts.push(&s.name); + } + } + let mut qn = parts.join("::"); + if !qn.is_empty() { + qn.push_str("::"); + } + qn.push_str(name); + qn + }; + + let mut flags = BoolFlags::default(); + if let Some(v) = extra.is_async { + flags.set(FLAG_IS_ASYNC, v); + } + if let Some(v) = extra.is_static { + flags.set(FLAG_IS_STATIC, v); + } + let name_ref = self.arena.put(name); + let qn_ref = self.arena.put(&qualified); + let id_ref = self.arena.put(&id); + let doc_ref = opt_str(&mut self.arena, extra.docstring.as_deref()); + let sig_ref = opt_str(&mut self.arena, extra.signature.as_deref()); + let row = self.tables.push_node(&NodeRow { + kind: node_kind_index(kind).unwrap(), + visibility: 0, + flags, + start_line, + end_line, + start_column: self.col_of(node), + end_column: self.end_col_of(node), + name: name_ref, + qualified_name: qn_ref, + id: id_ref, + docstring: doc_ref, + signature: sig_ref, + decorators: NONE_STR, + type_parameters: NONE_STR, + return_type: NONE_STR, + extra_json: NONE_STR, + }); + self.node_ids.push(id); + + let parent_row = self.top_row(); + self.tables.push_edge(&EdgeRow { + source_idx: parent_row, + target_idx: row, + kind: edge_kind_index("contains").unwrap(), + provenance: 0, + line: NONE, + column: NONE, + metadata_json: NONE_STR, + source_id_str: NONE_STR, + target_id_str: NONE_STR, + }); + + if kind == "function" || kind == "method" { + self.defined_fn_names.insert(name.to_string()); + } + // captureValueRefScope + let target_kind_ok = kind == "constant" || kind == "variable"; + if target_kind_ok + && util::utf16_len(name) >= 3 + && util::has_upper_or_underscore().is_match(name) + { + let parent_ok = self + .stack + .last() + .map(|s| matches!(s.kind, "file" | "class" | "module" | "struct" | "enum")) + .unwrap_or(false); + if parent_ok { + self.fs_values.insert(name.to_string(), row); + *self.fs_value_counts.entry(name.to_string()).or_insert(0) += 1; + } + } + if matches!(kind, "function" | "method" | "constant" | "variable") { + self.value_scopes.push(ValueScope { row, node, name: name.to_string() }); + } + Some(row) + } + + fn extract_name(&self, node: Node) -> String { + if let Some(name_node) = node.child_by_field_name("name") { + return self.text(name_node).to_string(); + } + for i in 0..node.named_child_count() { + if let Some(c) = node.named_child(i) { + if matches!(c.kind(), "identifier" | "type_identifier" | "simple_identifier" | "constant") { + return self.text(c).to_string(); + } + } + } + "".to_string() + } + + /// pythonExtractor.getSignature: params + ` -> returnType`. + fn signature_of(&self, node: Node) -> Option { + let params = node.child_by_field_name("parameters")?; + let mut sig = self.text(params).to_string(); + if let Some(ret) = node.child_by_field_name("return_type") { + sig.push_str(" -> "); + sig.push_str(self.text(ret)); + } + Some(sig) + } + + /// pythonExtractor.isAsync: the PREVIOUS SIBLING token is `async`. + fn is_async(&self, node: Node) -> bool { + node.prev_sibling().map(|p| p.kind() == "async").unwrap_or(false) + } + + /// pythonExtractor.isStatic: preceding decorator mentioning `staticmethod`. + fn is_static(&self, node: Node) -> bool { + if let Some(prev) = node.prev_named_sibling() { + if prev.kind() == "decorator" { + return self.text(prev).contains("staticmethod"); + } + } + false + } + + // --- visitNode ------------------------------------------------------------ + + fn visit_node(&mut self, node: Node<'t>) { + let kind = node.kind(); + let mut skip_children = false; + + self.maybe_capture_fn_refs(node); + + if kind == "function_definition" { + // functionTypes ∩ methodTypes: inside a class-like ⇒ method. + if self.inside_class_like() { + self.extract_method(node); + } else { + self.extract_function(node); + } + skip_children = true; + } else if kind == "class_definition" { + self.extract_class(node); + skip_children = true; + } else if kind == "assignment" && !self.inside_class_like() { + self.extract_variable(node); + self.scan_fn_ref_subtree(node, 0); + skip_children = true; + } else if kind == "import_statement" || kind == "import_from_statement" { + self.extract_import(node); + } else if kind == "call" { + self.extract_call(node); + } + + if !skip_children { + for i in 0..node.named_child_count() { + if let Some(c) = node.named_child(i) { + self.visit_node(c); + } + } + } + } + + fn visit_function_body(&mut self, body: Node<'t>) { + self.visit_for_calls_and_structure(body); + } + + fn visit_for_calls_and_structure(&mut self, node: Node<'t>) { + let kind = node.kind(); + self.maybe_capture_fn_refs(node); + + if kind == "call" { + self.extract_call(node); + } + + // Nested NAMED functions become their own nodes. + if kind == "function_definition" { + let name = self.extract_name(node); + if name != "" { + self.extract_function(node); + return; + } + } + if kind == "class_definition" { + self.extract_class(node); + return; + } + + for i in 0..node.named_child_count() { + if let Some(c) = node.named_child(i) { + self.visit_for_calls_and_structure(c); + } + } + } + + // --- extractors -------------------------------------------------------------- + + fn extract_function(&mut self, node: Node<'t>) { + let name = self.extract_name(node); + if name == "" { + if let Some(body) = node.child_by_field_name("body") { + self.visit_function_body(body); + } + return; + } + let extra = Extra { + docstring: preceding_docstring(node, self.src), + signature: self.signature_of(node), + is_async: Some(self.is_async(node)), + is_static: Some(self.is_static(node)), + }; + let Some(row) = self.create_node("function", &name, node, extra) else { return }; + // (python is not a TYPE_ANNOTATION language — no type refs) + self.extract_decorators_for(node, row); + self.stack.push(Scope { row, kind: "function", name }); + if let Some(body) = node.child_by_field_name("body") { + self.visit_function_body(body); + } + self.stack.pop(); + } + + fn extract_method(&mut self, node: Node<'t>) { + let name = self.extract_name(node); + let extra = Extra { + docstring: preceding_docstring(node, self.src), + signature: self.signature_of(node), + is_async: Some(self.is_async(node)), + is_static: Some(self.is_static(node)), + }; + let Some(row) = self.create_node("method", &name, node, extra) else { return }; + self.extract_decorators_for(node, row); + self.stack.push(Scope { row, kind: "method", name }); + if let Some(body) = node.child_by_field_name("body") { + self.visit_function_body(body); + } + self.stack.pop(); + } + + fn extract_class(&mut self, node: Node<'t>) { + let name = self.extract_name(node); + let extra = Extra { + docstring: preceding_docstring(node, self.src), + ..Extra::default() + }; + let Some(row) = self.create_node("class", &name, node, extra) else { return }; + + // Inheritance: `class Flask(Scaffold, Mixin):` — argument_list children. + let extends_kind = edge_kind_index("extends").unwrap(); + for i in 0..node.named_child_count() { + let Some(child) = node.named_child(i) else { continue }; + if child.kind() == "argument_list" { + for j in 0..child.named_child_count() { + let Some(arg) = child.named_child(j) else { continue }; + if matches!(arg.kind(), "identifier" | "attribute") { + let name = self.text(arg).to_string(); + self.push_ref_at(row, &name, extends_kind, arg); + } + } + } + } + self.extract_decorators_for(node, row); + + self.stack.push(Scope { row, kind: "class", name }); + let body = node.child_by_field_name("body").unwrap_or(node); + for i in 0..body.named_child_count() { + if let Some(c) = body.named_child(i) { + self.visit_node(c); + } + } + self.stack.pop(); + } + + /// extractVariable's python branch: `left = right` at module scope. + fn extract_variable(&mut self, node: Node<'t>) { + let docstring = preceding_docstring(node, self.src); + let left = node.child_by_field_name("left").or_else(|| node.named_child(0)); + let right = node.child_by_field_name("right").or_else(|| node.named_child(1)); + let Some(left) = left else { return }; + if !matches!(left.kind(), "identifier" | "constant") { + return; + } + let name = self.text(left).to_string(); + let signature = right.map(|r| util::init_signature(self.text(r))); + // No isConst hook ⇒ always `variable` (UPPER_CASE constants included). + self.create_node("variable", &name, node, Extra { docstring, signature, ..Extra::default() }); + } + + fn extract_import(&mut self, node: Node<'t>) { + let import_text = self.text(node).trim().to_string(); + let imports_kind = edge_kind_index("imports").unwrap(); + + if node.kind() == "import_from_statement" { + // Hook path: module_name field → import node + module ref, then + // per-name binding refs (emitPyFromImportRefs). + let Some(module_node) = node.child_by_field_name("module_name") else { return }; + let module_name = self.text(module_node).to_string(); + if module_name.is_empty() { + return; + } + self.create_node( + "import", + &module_name, + node, + Extra { signature: Some(import_text), ..Extra::default() }, + ); + let parent = self.top_row(); + self.push_ref_at(parent, &module_name.clone(), imports_kind, node); + + // emitPyFromImportRefs: one `imports` ref per imported name. + for i in 0..node.named_child_count() { + let Some(child) = node.named_child(i) else { continue }; + if child.start_byte() == module_node.start_byte() + && child.end_byte() == module_node.end_byte() + { + continue; + } + if child.kind() == "wildcard_import" { + continue; + } + let name_node = match child.kind() { + "aliased_import" => child + .child_by_field_name("alias") + .or_else(|| child.child_by_field_name("name")) + .or_else(|| child.named_child(0)), + "dotted_name" => Some(child), + _ => None, + }; + let Some(name_node) = name_node else { continue }; + let raw = self.text(name_node); + let local = raw.rsplit('.').next().unwrap_or(""); + if local.is_empty() { + continue; + } + self.push_ref_at(parent, &local.to_string(), imports_kind, name_node); + } + return; + } + + // import_statement: `import a.b, x as y` — one import node + module ref + // per dotted name (the python multi-import branch). + let parent = self.top_row(); + for i in 0..node.named_child_count() { + let Some(child) = node.named_child(i) else { continue }; + if child.kind() == "dotted_name" { + let name = self.text(child).to_string(); + self.create_node( + "import", + &name, + node, + Extra { signature: Some(import_text.clone()), ..Extra::default() }, + ); + self.push_ref_at(parent, &name, imports_kind, child); + } else if child.kind() == "aliased_import" { + let dotted = (0..child.named_child_count()) + .filter_map(|j| child.named_child(j)) + .find(|c| c.kind() == "dotted_name"); + if let Some(dotted) = dotted { + let name = self.text(dotted).to_string(); + self.create_node( + "import", + &name, + node, + Extra { signature: Some(import_text.clone()), ..Extra::default() }, + ); + self.push_ref_at(parent, &name, imports_kind, dotted); + } + } + } + } + + /// extractCall — python `call` through the generic tail (attribute callees). + fn extract_call(&mut self, node: Node<'t>) { + if self.stack.is_empty() { + return; + } + let func = node + .child_by_field_name("function") + .or_else(|| node.named_child(0)); + let mut callee_name = String::new(); + + if let Some(func) = func { + if func.kind() == "attribute" { + // `property` and `field` fields don't exist on attribute — + // the generic path falls back to namedChild(1) (the attr name). + let property = func + .child_by_field_name("property") + .or_else(|| func.child_by_field_name("field")) + .or_else(|| func.named_child(1)); + if let Some(property) = property { + let method_name = self.text(property); + let receiver = func + .child_by_field_name("object") + .or_else(|| func.child_by_field_name("operand")) + .or_else(|| func.child_by_field_name("argument")) + .or_else(|| func.named_child(0)); + if let Some(r) = receiver { + if is_literal_receiver(r.kind()) { + return; + } + } + let recv_ident = receiver.filter(|r| { + matches!(r.kind(), "identifier" | "simple_identifier" | "field_identifier") + }); + if let Some(r) = recv_ident { + let receiver_name = self.text(r); + if !matches!(receiver_name, "self" | "this" | "cls" | "super") { + callee_name = format!("{receiver_name}.{method_name}"); + } else { + callee_name = method_name.to_string(); + } + } else { + callee_name = method_name.to_string(); + } + } + } else { + callee_name = self.text(func).to_string(); + } + } + + if !callee_name.is_empty() { + if let Some(c) = util::paren_conversion().captures(&callee_name) { + callee_name = c[1].to_string(); + } + let from = self.top_row(); + self.push_ref_at(from, &callee_name.clone(), edge_kind_index("calls").unwrap(), node); + } + } + + /// extractDecoratorsFor — python decorators are PRECEDING SIBLINGS inside + /// decorated_definition. Only bare-identifier decorators yield a target + /// (python's `call` kind isn't `call_expression`, and `attribute` isn't in + /// the target-kind list — mirrored exactly). + fn extract_decorators_for(&mut self, decl: Node<'t>, decorated_row: u32) { + for i in 0..decl.named_child_count() { + if let Some(child) = decl.named_child(i) { + self.consider_decorator(child, decorated_row); + } + } + let Some(parent) = decl.parent() else { return }; + let decl_start = decl.start_byte(); + let mut decl_idx: isize = -1; + for i in 0..parent.named_child_count() { + if let Some(sib) = parent.named_child(i) { + if sib.start_byte() == decl_start { + decl_idx = i as isize; + break; + } + } + } + if decl_idx > 0 { + let mut j = decl_idx - 1; + while j >= 0 { + let Some(sib) = parent.named_child(j as usize) else { + j -= 1; + continue; + }; + if !matches!(sib.kind(), "decorator" | "annotation" | "marker_annotation") { + break; + } + self.consider_decorator(sib, decorated_row); + j -= 1; + } + } + } + + fn consider_decorator(&mut self, n: Node<'t>, decorated_row: u32) { + if !matches!(n.kind(), "decorator" | "annotation" | "marker_annotation" | "attribute") { + return; + } + let mut target: Option = None; + for i in 0..n.named_child_count() { + let Some(child) = n.named_child(i) else { continue }; + if child.kind() == "call_expression" { + target = child.child_by_field_name("function").or_else(|| child.named_child(0)); + if target.is_some() { + break; + } + } + if matches!( + child.kind(), + "identifier" | "member_expression" | "scoped_identifier" | "navigation_expression" + | "user_type" | "type_identifier" + ) { + target = Some(child); + break; + } + } + let Some(target) = target else { return }; + let mut name = self.text(target).to_string(); + if let Some(lt) = name.find('<') { + if lt > 0 { + name.truncate(lt); + } + } + let last_dot = name + .rfind('.') + .map(|i| i as isize) + .unwrap_or(-1) + .max(name.rfind("::").map(|i| i as isize).unwrap_or(-1)); + if last_dot >= 0 { + name = name[(last_dot as usize + 1)..].to_string(); + if name.starts_with(':') || name.starts_with('.') { + name.remove(0); + } + } + let name = name.trim().to_string(); + if name.is_empty() { + return; + } + self.push_ref_at(decorated_row, &name, edge_kind_index("decorates").unwrap(), n); + } + + // --- fn refs (PYTHON_SPEC) ------------------------------------------------------ + + fn maybe_capture_fn_refs(&mut self, node: Node<'t>) { + let (mode, field): (&str, &str) = match node.kind() { + "argument_list" => ("args", ""), + "assignment" => ("rhs", "right"), + "keyword_argument" => ("value", "value"), + "pair" => ("value", "value"), + "list" => ("list", ""), + _ => return, + }; + if self.stack.is_empty() { + return; + } + let from = self.top_row(); + + let mut values: Vec = Vec::new(); + match mode { + "args" | "list" => { + for i in 0..node.named_child_count() { + if let Some(c) = node.named_child(i) { + values.push(c); + } + } + } + "rhs" => { + if let Some(rhs) = node.child_by_field_name(field) { + let lhs_text = node + .child_by_field_name("left") + .map(|l| self.text(l)) + .unwrap_or(""); + let lhs_last = util::lhs_last_name() + .captures(lhs_text) + .and_then(|c| c.get(1)) + .map(|m| m.as_str()); + if !(lhs_last.is_some() && lhs_last == Some(self.text(rhs).trim())) { + values.push(rhs); + } + } + } + _ => { + if let Some(v) = node.child_by_field_name(field) { + values.push(v); + } + } + } + + for v in values { + let (name, anchor) = match v.kind() { + "identifier" => (self.text(v).to_string(), v), + // `self.handle_click` — object EXACTLY `self`; BARE attr name. + "attribute" => { + let obj = v.child_by_field_name("object"); + let attr = v.child_by_field_name("attribute"); + match (obj, attr) { + (Some(o), Some(a)) + if o.kind() == "identifier" && self.text(o) == "self" => + { + (self.text(a).to_string(), a) + } + _ => continue, + } + } + _ => continue, + }; + if name.is_empty() || is_stoplisted(&name) { + continue; + } + let p = anchor.start_position(); + self.fn_ref_cands.push(Cand { + from, + name, + line: p.row as u32 + 1, + column_byte: anchor.start_byte(), + row: p.row, + }); + } + } + + fn scan_fn_ref_subtree(&mut self, node: Node<'t>, depth: u32) { + if depth > 12 { + return; + } + // Halts at functionTypes ∪ the fixed arrow/lambda list — python's + // `lambda` kind is NOT in that list (mirrored). + if depth > 0 + && matches!( + node.kind(), + "function_definition" | "arrow_function" | "function_expression" | "lambda_literal" + | "lambda_expression" + ) + { + return; + } + self.maybe_capture_fn_refs(node); + for i in 0..node.named_child_count() { + if let Some(c) = node.named_child(i) { + self.scan_fn_ref_subtree(c, depth + 1); + } + } + } + + fn flush_fn_ref_candidates(&mut self) { + let cands = std::mem::take(&mut self.fn_ref_cands); + if cands.is_empty() || util::is_generated_file(self.file_path) { + return; + } + let mut seen: HashSet<(String, String)> = HashSet::new(); + for c in cands { + if !c.name.starts_with("this.") + && !c.name.contains("::") + && !self.defined_fn_names.contains(&c.name) + && !self.imported_names.contains(&c.name) + { + continue; + } + if !seen.insert((self.node_ids[c.from as usize].clone(), c.name.clone())) { + continue; + } + let column = util::col16(self.src, &self.line_starts, c.row, c.column_byte); + let name_ref = self.arena.put(&c.name); + self.tables.push_ref(&RefRow { + from_idx: c.from, + kind: FUNCTION_REF_CODE, + line: c.line, + column, + reference_name: name_ref, + candidates: NONE_STR, + from_id_str: NONE_STR, + }); + } + } + + // --- value refs ------------------------------------------------------------------- + + fn flush_value_refs(&mut self, root: Node<'t>) { + let scopes = std::mem::take(&mut self.value_scopes); + let mut targets = std::mem::take(&mut self.fs_values); + let counts = std::mem::take(&mut self.fs_value_counts); + if std::env::var("CODEGRAPH_VALUE_REFS").as_deref() == Ok("0") { + return; + } + if targets.is_empty() || scopes.is_empty() || util::is_generated_file(self.file_path) { + return; + } + + // Shadow prune — python's declarator shape is `assignment`. + let mut decl_counts: HashMap<&str, u32> = HashMap::new(); + let mut dstack: Vec = vec![root]; + let mut dvisited = 0usize; + while let Some(n) = dstack.pop() { + if dvisited >= MAX_VALUE_REF_NODES { + break; + } + dvisited += 1; + if n.kind() == "assignment" { + let left = n + .child_by_field_name("left") + .or_else(|| n.child_by_field_name("pattern")) + .or_else(|| n.named_child(0)); + if let Some(left) = left { + if left.kind() == "identifier" { + let nm = self.text(left); + if targets.contains_key(nm) { + *decl_counts.entry(nm).or_insert(0) += 1; + } + } else { + for i in 0..left.named_child_count() { + if let Some(c) = left.named_child(i) { + if c.kind() == "identifier" { + let nm = self.text(c); + if targets.contains_key(nm) { + *decl_counts.entry(nm).or_insert(0) += 1; + } + } + } + } + } + } + } + for i in 0..n.named_child_count() { + if let Some(c) = n.named_child(i) { + dstack.push(c); + } + } + } + let shadowed: Vec = decl_counts + .iter() + .filter(|(nm, c)| **c > counts.get(**nm).copied().unwrap_or(1)) + .map(|(nm, _)| nm.to_string()) + .collect(); + for nm in shadowed { + targets.remove(&nm); + } + if targets.is_empty() { + return; + } + + let refs_kind = edge_kind_index("references").unwrap(); + for scope in &scopes { + let mut seen: HashSet<&str> = HashSet::new(); + let mut stack: Vec = vec![scope.node]; + let mut visited = 0usize; + while let Some(n) = stack.pop() { + if visited >= MAX_VALUE_REF_NODES { + break; + } + visited += 1; + if matches!(n.kind(), "identifier" | "constant" | "name" | "simple_identifier") { + let ref_name = self.text(n); + if let Some(&target_row) = targets.get(ref_name) { + let target_id = self.node_ids[target_row as usize].as_str(); + if target_id != self.node_ids[scope.row as usize] + && ref_name != scope.name + && !seen.contains(&target_id) + { + seen.insert(target_id); + let meta = self.arena.put(r#"{"valueRef":true}"#); + self.tables.push_edge(&EdgeRow { + source_idx: scope.row, + target_idx: target_row, + kind: refs_kind, + provenance: 0, + line: NONE, + column: NONE, + metadata_json: meta, + source_id_str: NONE_STR, + target_id_str: NONE_STR, + }); + } + } + } + for i in 0..n.named_child_count() { + if let Some(c) = n.named_child(i) { + stack.push(c); + } + } + } + } + } +} + +/// NAME_STOPLIST (function-ref.ts). +fn is_stoplisted(name: &str) -> bool { + matches!( + name, + "this" | "self" | "super" | "null" | "nil" | "true" | "false" | "undefined" | "new" + | "NULL" | "nullptr" | "None" + ) +} + +/// LITERAL_RECEIVER_TYPES membership (shared table; python names among them). +fn is_literal_receiver(kind: &str) -> bool { + matches!( + kind, + "string" | "string_literal" | "interpreted_string_literal" | "raw_string_literal" + | "template_string" | "concatenated_string" | "formatted_string" | "f_string" + | "line_string_literal" | "string_content" | "heredoc_body" + | "number" | "number_literal" | "integer" | "integer_literal" | "float" + | "float_literal" | "int_literal" | "decimal_integer_literal" | "real_literal" + | "char_literal" | "character_literal" | "rune_literal" | "regex" | "regex_literal" + | "true" | "false" | "boolean_literal" | "bool_literal" | "none" | "null" | "nil" + | "null_literal" | "undefined" + | "list" | "list_literal" | "array" | "array_literal" | "array_creation_expression" + | "dictionary" | "dict_literal" | "object" | "tuple" | "set" + ) +} + +fn opt_str(arena: &mut Arena, s: Option<&str>) -> StrRef { + match s { + Some(s) => arena.put(s), + None => NONE_STR, + } +} diff --git a/docs/design/rust-kernel-migration-plan.md b/docs/design/rust-kernel-migration-plan.md index 68806ed..a2d0a03 100644 --- a/docs/design/rust-kernel-migration-plan.md +++ b/docs/design/rust-kernel-migration-plan.md @@ -30,7 +30,7 @@ Work top to bottom; each step has a section below with the detail. dispatch), so the Mac headline barely moves (~11.3→11.1s; parse-loop 5.0→4.4s); the win shows where worker CPU binds (dubbo on 2-CPU: 28→22.5s, ~1.25×). The identified follow-up lever for the Mac number is decode-direct-to-store (§4c). -- [ ] **R5. Port Python, Go.** (§4) +- [x] **R5. Port Python, Go.** (§4) — **ported + gates passed + DEFAULT-ON 2026-07-16, §4e.** - [ ] **R6. Kernel-scale re-validation** in the cg1212 container (expect parse 6m → ~2m). (§6) - [ ] **R7. Long-tail languages opportunistically** per the tracker; T3 may stay TS forever. (§4) - [ ] **P1. Kernel-scale resolution speed** — the 19.5-min sequential wall at 2M nodes. (§7a) @@ -264,6 +264,29 @@ Default routing: `DEFAULT_ROUTED = {typescript, tsx, javascript, jsx}` in Windows VM still deferred (same fallback rationale as §4b). - Default routing now includes `java`. +### 4e. R5 — Python + Go PORTED + gates PASSED + DEFAULT-ON (2026-07-16) + +- **Walkers:** `codegraph-kernel/src/python.rs` + `src/go.rs` (the java.rs pattern). + Python: decorated_definition docstring/decorator handling (decorates only for + bare-identifier decorators — the `call`-kind quirk mirrored), fn-in-class → method, + module assignments always `variable` (no isConst hook), from-import binding refs, + `self.x` fn-ref candidates as BARE names, attribute callees via the namedChild(1) + fallback. Go: receiver methods with `Recv::name` QNs + first-earlier-struct + contains edges, type_spec → struct/interface classification (embedding → extends; + interface method_elems → method nodes), composite-literal instantiates keeping the + package qualifier, top-level var/const initializer walks attributed to the symbol + (#693), 2-hop field chains (#1276), `New().Method()` re-encode (#645/#608), + GO_SPEC fn-ref layers (literal_element/expression_list fan-out). +- **Grammars:** crates tree-sitter-python 0.23.6 (bffb65a) + tree-sitter-go 0.23.4 + (3c3775f); wasm vendored from the same tags, parser.c sha-matched (both were + 2023-era in tree-sitter-wasms). +- **Parity:** extraction sweeps 100% — flask 83/83, django 3,035/3,038 (+3 error-file + deferrals), gin 99/99, prometheus 978/979 (+1). Full-init dumps byte-identical: + flask (10,833 rows), gin (17,540), **django (360,794)**, **prometheus (213,758)**. + Torture fixtures in `npm test`. Even Mac-side init already moves where extraction + matters: prometheus 5.7→4.5s, django 9.0→8.7s. +- Default routing now: typescript, tsx, javascript, jsx, java, python, go. + ### 4d. Direct-to-store decode (2026-07-16) — and where the wall ACTUALLY is Kernel-routed files now ship their flat buffers from the parse worker all the way @@ -308,8 +331,8 @@ parity before porting the language. |---|---|---|---|---|---| | typescript, tsx, javascript, jsx | `languages/typescript.ts`, `javascript.ts` + shared branches | T1 | crates.io | First target. Value-reference edges (#895/#897) and component recognition (#841 forwardRef/memo/styled) must survive — they're extraction-side. Largest test surface; gate is strictest here. **PORTED + GATE PASSED + DEFAULT-ON (§4a/§4b); erroring files defer to wasm per-file.** | ✅ | | java | `languages/java.ts` | T1 | crates.io | Second target; unlocks the dubbo-parity claim. Lombok member synthesis (#912) is a NODE synthesizer hook in extraction (`synthesizeMembers`) — port or keep as TS post-pass. **PORTED incl. Lombok + gate passed + DEFAULT-ON (§4c).** | ✅ | -| python | `languages/python.ts` | T1 | crates.io | Third. Decorator extraction feeds framework route detection — parity required. | ☐ | -| go | `languages/go.ts` | T1 | crates.io | Third (tie). Value-reference edges ship here too (#897). | ☐ | +| python | `languages/python.ts` | T1 | crates.io | Third. Decorator extraction feeds framework route detection — parity required. **PORTED + DEFAULT-ON (§4e).** | ✅ | +| go | `languages/go.ts` | T1 | crates.io | Third (tie). Value-reference edges ship here too (#897). **PORTED + DEFAULT-ON (§4e).** | ✅ | | ruby, php | dedicated files | T1 | crates.io | Straightforward; PHP property-receiver shapes (#1220/#1251) are RESOLUTION-side, unaffected. | ☐ | | csharp | `languages/csharp.ts` | T1 | crates.io | Plain. | ☐ | | rust, dart, scala, lua, luau, r | dedicated files | T1 | crates.io (luau/r/scala: verify crate freshness vs our wasm) | Long-tail T1; port opportunistically after the big five. | ☐ | diff --git a/scripts/kernel-parity.mjs b/scripts/kernel-parity.mjs index dbcc7b4..b38ed06 100644 --- a/scripts/kernel-parity.mjs +++ b/scripts/kernel-parity.mjs @@ -39,11 +39,11 @@ if (paths.length === 0) { process.exit(2); } -const KERNEL_LANGS = new Set(['typescript', 'tsx', 'javascript', 'jsx', 'java']); +const KERNEL_LANGS = new Set(['typescript', 'tsx', 'javascript', 'jsx', 'java', 'python', 'go']); const EXTS = new Map([ ['.ts', 'typescript'], ['.mts', 'typescript'], ['.cts', 'typescript'], ['.tsx', 'tsx'], ['.js', 'javascript'], ['.mjs', 'javascript'], - ['.cjs', 'javascript'], ['.jsx', 'jsx'], ['.java', 'java'], + ['.cjs', 'javascript'], ['.jsx', 'jsx'], ['.java', 'java'], ['.py', 'python'], ['.pyw', 'python'], ['.go', 'go'], ]); /** Collect candidate files. */ diff --git a/src/extraction/grammars.ts b/src/extraction/grammars.ts index 7289c9a..48de13e 100644 --- a/src/extraction/grammars.ts +++ b/src/extraction/grammars.ts @@ -280,6 +280,8 @@ export async function initGrammars(): Promise { * - tree-sitter/tree-sitter-typescript v0.23.2 (f975a62) → typescript + tsx * - tree-sitter/tree-sitter-javascript v0.25.0 (44c892e) → javascript + jsx * - tree-sitter/tree-sitter-java v0.23.5 (94703d5) → java + * - tree-sitter/tree-sitter-python v0.23.6 (bffb65a) → python + * - tree-sitter/tree-sitter-go v0.23.4 (3c3775f) → go * Built from each repo's CHECKED-IN parser.c (no `generate`) with * tree-sitter-cli 0.25.10 `build --wasm` — the same tables crates.io compiles * (parser.c sha-matched against the crates.io tarball). @@ -289,7 +291,7 @@ export async function initGrammars(): Promise { const VENDORED_WASM_LANGS: ReadonlySet = new Set([ 'pascal', 'scala', 'lua', 'luau', 'csharp', 'r', 'cfml', 'cfscript', 'cfquery', 'cobol', 'vbnet', 'erlang', 'terraform', 'arkts', 'nix', - 'typescript', 'tsx', 'javascript', 'jsx', 'java', + 'typescript', 'tsx', 'javascript', 'jsx', 'java', 'python', 'go', ]); /** Absolute path of a language's grammar WASM (vendored or tree-sitter-wasms). */ diff --git a/src/extraction/kernel/index.ts b/src/extraction/kernel/index.ts index 96df323..2f433a0 100644 --- a/src/extraction/kernel/index.ts +++ b/src/extraction/kernel/index.ts @@ -39,6 +39,8 @@ const DEFAULT_ROUTED: ReadonlySet = new Set([ 'javascript', 'jsx', 'java', + 'python', + 'go', ]); /** diff --git a/src/extraction/wasm/tree-sitter-go.wasm b/src/extraction/wasm/tree-sitter-go.wasm new file mode 100755 index 0000000000000000000000000000000000000000..2fcc32f8f05b49e212932743a8e3e12676855a3c GIT binary patch literal 210013 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

e z4aB?R+LC$BDuEL0+csv~eujk^^gGfv20L@{4jV5{|I*&yU^lb$7euxyJ1O;%ge{HDo3(FXP#=H=)Qv~nW- zi8Pu1LYhjw(W)#SCf_6ZB32vmE~gaC`7HWk9a)f1#sun5eI!cupugvG@h&3Pw^p9( z*7cm;XXW!JcC;7*a*|P%o z@nslEW$_+O7EcsC`ZqLF=pUpd=s!r4>Ay%*>3>MeQG4_;dq=43YrB0c-^nN5z-P8w zKWe5$hc0zbX!ewtSbbbCw4d0MSuCi`_%LO%c4u~)=bvzWGZA{F(MxEx9L-Nh zsdLhcnBPUTIwQa=B)_9>)+6{lDCRylWFy9mG?7XmO`;T}*wZ3SrqW1DQV-0V@{PN4 zj?7YHoz^p>d^2kX>MK+pX(DAIEkPBLCR1gksZ<4NDXNCFG}S$E<>64WF9zO9n-Vawwh^q*T?vdz&hZR$o-DR*V%VuVqrgvvxc*a zx94wlAuF(t*9+6%qTQyq@HTCrwrRtBZF($dn?_rg_FR|7sxD3Q>GDTVUF7`@^OM%i zz%`9J8qRFy`za~R+u>uu?gGiT8H@%mulhbhcJeJxH$X-_HAk9AEs=Vt71CsCgEWKM zAPNZZg|NZa!FNiQaLPizw%0A&<)gtcrh+Zms1FVH?#?(I6o`k%mV4R7)9ZDspE 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 X|7-&?2PFJ&{wJRQ;qK%QnfLjBaR|+F literal 0 HcmV?d00001 diff --git a/src/extraction/wasm/tree-sitter-python.wasm b/src/extraction/wasm/tree-sitter-python.wasm new file mode 100755 index 0000000000000000000000000000000000000000..8b3bc8a78c93dc96052de58a00c2b174eb5b3c94 GIT binary patch literal 456131 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 literal 0 HcmV?d00001