From f1ca9919435b401b20d7e9afa4297061997b0f99 Mon Sep 17 00:00:00 2001 From: Colby Mchenry Date: Mon, 20 Jul 2026 00:08:09 -0500 Subject: [PATCH] =?UTF-8?q?feat(kernel):=20R7b=20Rust=20walker=20=E2=80=94?= =?UTF-8?q?=20rustlang=20module,=20tree-sitter-rust=200.24.2=20bump,=20rus?= =?UTF-8?q?t=20default-routed=20(#1371)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First R7b language port. Grammar: tree-sitter-rust pinned =0.24.2 + wasm vendored from tag 77a3747 (parser.c/scanner.c sha-matched against the crates.io tarball), replacing the 2023 ABI-14 tree-sitter-wasms build — the bump alone is precision-positive on the wasm path (receiver-qualified instance-method resolutions replace ambiguous bare-name matches; node sections byte-identical on ripgrep/tokio). Walker mirrors the TS reference bug-for-bug per docs/design/rust-lang-kernel-port-checklist.md (survey artifact): dead-code isAsync, impl-pushes-no-scope, the impl-Trait-for-Generic trait-receiver quirk, phantom const identifiers, use-binding triple emission, wildcard-use-emits-nothing, scoped-supertrait drop, chained-call re-encode gated on scoped_identifier, Rocket route macros body-only. Gates: parity sweeps 0 diffs — ripgrep 101/101, tokio 790/790, rust-analyzer 1217/1488 (271 deferrals are token-macro-table sources that error on BOTH arms — grammar-inherent); full-init dump-diffs byte-identical on all three (3,857 / 13,440 / 39,030 nodes); kernel-rustlang-parity suite (torture + CRLF + defer) in npm test; full suite green x2 with CODEGRAPH_KERNEL_EXPECT=1. Co-authored-by: Claude Fable 5 --- CHANGELOG.md | 3 +- __tests__/fixtures/kernel-parity/torture.rs | 211 +++ __tests__/kernel-grammar-parity.test.ts | 2 +- __tests__/kernel-rustlang-parity.test.ts | 117 ++ codegraph-kernel/Cargo.lock | 11 + codegraph-kernel/Cargo.toml | 1 + codegraph-kernel/src/langs.rs | 6 +- codegraph-kernel/src/lib.rs | 2 + codegraph-kernel/src/rustlang.rs | 1474 +++++++++++++++++ docs/design/rust-kernel-migration-plan.md | 21 +- .../design/rust-lang-kernel-port-checklist.md | 526 ++++++ scripts/kernel-parity.mjs | 3 +- src/extraction/grammars.ts | 4 + src/extraction/kernel/index.ts | 6 + src/extraction/wasm/tree-sitter-rust.wasm | Bin 0 -> 1114303 bytes 15 files changed, 2380 insertions(+), 7 deletions(-) create mode 100644 __tests__/fixtures/kernel-parity/torture.rs create mode 100644 __tests__/kernel-rustlang-parity.test.ts create mode 100644 codegraph-kernel/src/rustlang.rs create mode 100644 docs/design/rust-lang-kernel-port-checklist.md create mode 100755 src/extraction/wasm/tree-sitter-rust.wasm diff --git a/CHANGELOG.md b/CHANGELOG.md index e51d912..3bbbdb9 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, Java, Python, Go, C, and C++ 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-, django-, git-, and protobuf-scale codebases (Lombok-generated members, C function-pointer tables, and Unreal-Engine-style macro-heavy headers included; CUDA and Metal sources ride the C++ path). 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, Go, C, C++, and Rust 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-, django-, git-, protobuf-, tokio-, and rust-analyzer-scale codebases (Lombok-generated members, C function-pointer tables, and Unreal-Engine-style macro-heavy headers included; CUDA and Metal sources ride the C++ path). 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. @@ -28,6 +28,7 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Fixes - TypeScript, TSX, and JavaScript files now parse with up-to-date grammars — modern syntax such as `using` declarations and import attributes no longer trips parse errors that could drop surrounding symbols. (The previously bundled grammars dated from 2023.) +- Rust files also parse with an up-to-date grammar now (the previously bundled build dated from 2023), which additionally sharpens method-call attribution: calls through struct fields resolve with receiver context instead of falling back to ambiguous bare-name matching, removing a class of wrong call edges on common names like `len` and `start`. - Searching or exploring by field names now finds the code that defines them. A query made of object keys or API field names (`profileInfo isTrialEligible quotaInfo billingMethod`) used to return unrelated results while the defining files never appeared, because three retrieval steps each dropped multi-word camelCase terms: an internal case-comparison bug, a match step that only considered classes (never functions or methods), and exploration seeding that required exact symbol-name matches. All three are fixed — `codegraph_explore` with a bag of field names now surfaces the controllers and services that assemble those fields. (#1196) - `codegraph.json`'s `includeIgnored` works again for the "folder of repos" layout: when one `.gitignore` rule covers a parent directory (`/repos/`) holding several embedded git repositories, opting in the individual repos (`"includeIgnored": ["repos/a/"]` — the exact spelling `codegraph init`'s own hint suggests) previously matched nothing and indexed zero files, looping the same suggestion back at you. Both spellings now work — name the parent directory to opt in everything under it, or name individual repos to opt in just those — and the hint no longer re-suggests repos that are already configured. (#1295) - Method calls on literals (`", ".join(...)` in Python, `"x".split(...)` in JavaScript, and the like) no longer produce call edges to unrelated project functions that happen to share the builtin's name — a codebase with a function called `join`, `get`, or `update` could show phantom callers from every string-builtin use. Additionally, a function nested inside another function is now only matched as a call target from inside its container, since it isn't reachable from anywhere else. Blast-radius and affected-test results get cleaner on Python and JavaScript codebases especially. (#1230) diff --git a/__tests__/fixtures/kernel-parity/torture.rs b/__tests__/fixtures/kernel-parity/torture.rs new file mode 100644 index 0000000..0f7e80c --- /dev/null +++ b/__tests__/fixtures/kernel-parity/torture.rs @@ -0,0 +1,211 @@ +//! Torture fixture for the rust kernel walker (R7b) — every quirk in +//! docs/design/rust-lang-kernel-port-checklist.md, parse-clean. + +use std::fmt; +use crate::mod_a::Item; +use crate::mod_b::{A, B as C, sub::D}; +use a::{b::{c, d}}; +use foo_single; +use std::collections::*; + +/// Widget docs. +pub struct Widget { + pub n: u32, + name: String, + field: Deep, +} + +pub struct Unit; + +pub struct Pair(u32, u32); + +/// Doc broken by the attribute below — must yield NO docstring. +#[derive(Debug)] +pub struct Doc { + x: u32, +} + +pub struct Deep { + z: u32, +} + +pub enum Shape { + Circle(f32), + Rect { w: f32, h: f32 }, + Empty, +} + +pub type Alias = Vec; + +/// Render trait docs. +pub trait Render: Base + fmt::Debug { + fn render(&self); + fn hint(&self) -> Size { + Size::default() + } + const CAP: usize = init_cap(); + type Output; +} + +pub trait Base {} + +pub trait Super2: Producer {} + +pub trait Owned: for<'de> Deserialize<'de> {} + +impl Render for Widget { + fn render(&self) { + draw(self); + } +} + +impl fmt::Display for Widget { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "w{}", self.n) + } +} + +impl Widget { + const SCALE: usize = 3; + + fn area(&self) -> u32 { + self.n * mul() + } + + fn clone_self(&self) -> Self { + Self::assoc(); + Widget { + n: self.n, + name: String::new(), + field: Deep { z: 0 }, + } + } + + fn borrow_widget(&self) -> &Widget { + self + } + + fn outer(&self) -> u32 { + fn inner_helper(v: u32) -> u32 { + v + } + inner_helper(self.n) + } +} + +pub struct Container { + item: T, +} + +impl Container { + fn unwrap(self) -> T { + self.item + } +} + +impl Render for Container { + fn render(&self) {} +} + +impl Later { + fn touch(&self) {} +} + +pub struct Later { + z: u32, +} + +/* Block-doc for an async fn — isAsync must stay FALSE (dead-code hook). */ +pub async fn fetch_data(url: &str) -> Result { + let body = get(url).await; + client.request().await.send(); + body +} + +pub fn nested_ret() -> Result, Error> { + make_result() +} + +pub fn vec_ret(w: &Widget) -> Vec { + build_list(w) +} + +pub(crate) fn crate_fn() {} + +fn caller() { + let w = Widget { + n: 1, + name: make_name(), + field: Deep { z: 1 }, + }; + let v = m::Widget { n: 2 }; + let r = Foo::new().bar(); + let x = w.method_a().chain_b(); + let y = w.field.deep_call(); + let s = "lit".len(); + let f = 5.0_f64.floor(); + helper(); + m::helper2(); + let t = helper::(3); + (helper)(1); + takes(Widget { + n: 3, + name: n2(), + field: Deep { z: 2 }, + }); +} + +fn handler() {} +fn handler2() {} +fn cb_a() {} +fn cb_b() {} +fn invoke_all(fns: [fn(); 2]) {} + +fn register(f: fn()) { + f(); +} + +pub struct Holder { + cb: fn(), +} + +fn wiring(mut o: Holder) { + register(handler); + o.cb = handler2; + let h = Holder { cb: cb_a }; + let arr = [cb_a, cb_b]; + let local = handler; + let (t1, t2) = (cb_a, cb_b); + invoke_all(arr); + invoke(foo_single); +} + +static CB: fn() = handler; + +const MAX_LIMIT: u32 = OTHER_LIMIT; +const OTHER_LIMIT: u32 = 99; + +fn reads_limits() -> u32 { + MAX_LIMIT + OTHER_LIMIT +} + +fn shadowed_read() { + let MAX_LIMIT = 5; + let _ = MAX_LIMIT; +} + +mod inner { + pub fn helper_pub() {} + fn hidden() {} + pub struct Item2 { + pub v: u32, + } +} + +fn mount() { + let r = routes![a::b::index_h, health_h]; + let c = catchers![not_found_h]; + let skipped = rocket::routes![a::x]; +} + +routes![top_level_h]; diff --git a/__tests__/kernel-grammar-parity.test.ts b/__tests__/kernel-grammar-parity.test.ts index a3f57cf..682856e 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', 'python', 'go', 'c', 'cpp']; +const GRAMMAR_LANGUAGES: Language[] = ['typescript', 'tsx', 'javascript', 'java', 'python', 'go', 'c', 'cpp', 'rust']; describe.skipIf(!kernelBuilt)('kernel↔wasm grammar parity', () => { beforeAll(async () => { diff --git a/__tests__/kernel-rustlang-parity.test.ts b/__tests__/kernel-rustlang-parity.test.ts new file mode 100644 index 0000000..b6d792e --- /dev/null +++ b/__tests__/kernel-rustlang-parity.test.ts @@ -0,0 +1,117 @@ +/** + * Kernel↔wasm Rust extraction parity (R7b of the kernel migration). + * + * Asserts the native walker (codegraph-kernel/src/rustlang.rs) produces the + * SAME ExtractionResult as the wasm TreeSitterExtractor — nodes, edges, and + * unresolved refs compared as canonicalized multisets — over the checked-in + * torture fixture (torture.rs: impl/trait quirks incl. the + * `impl Trait for Generic` trait-receiver bug, unit-struct skip, phantom + * const identifiers, use-binding refs incl. nested groups + wildcard-emits- + * nothing, chained-call re-encode, turbofish, Rocket route macros body-only, + * fn-ref shapes, value-ref shadowing, attribute-broken docstrings, dead-code + * isAsync) and its CRLF variant (derived in-memory — #1329 docstring + * semantics). + * + * The full-repo sweep lives in scripts/kernel-parity.mjs (ripgrep/tokio/ + * rust-analyzer for the §5 gate); this suite keeps the invariant alive in + * `npm test`. Skips when no kernel binary is staged; CODEGRAPH_KERNEL_EXPECT=1 + * turns that into a failure (kernel-scaffold.test.ts). + */ + +import { describe, it, expect, beforeAll, beforeEach, afterEach } from 'vitest'; +import * as fs from 'fs'; +import * as path from 'path'; +import { extractFromSource } from '../src/extraction'; +import { initGrammars, loadGrammarsForLanguages } from '../src/extraction/grammars'; +import { tryKernelExtract, resetKernelForTests } from '../src/extraction/kernel'; +import type { ExtractionResult } from '../src/types'; + +const KERNEL_PATH = path.join( + __dirname, + '..', + 'codegraph-kernel', + 'prebuilds', + `${process.platform}-${process.arch}`, + 'codegraph-kernel.node' +); +const kernelBuilt = fs.existsSync(KERNEL_PATH); + +const FIXTURE_DIR = path.join(__dirname, 'fixtures', 'kernel-parity'); + +function canon(result: ExtractionResult): { nodes: string[]; edges: string[]; refs: string[] } { + return { + nodes: result.nodes + .map(({ updatedAt: _u, ...n }) => JSON.stringify(n, Object.keys(n).sort())) + .sort(), + edges: result.edges.map((e) => JSON.stringify(e, Object.keys(e).sort())).sort(), + refs: result.unresolvedReferences + .map((r) => JSON.stringify(r, Object.keys(r).sort())) + .sort(), + }; +} + +const ENV_KEYS = ['CODEGRAPH_KERNEL', 'CODEGRAPH_KERNEL_LANGS'] as const; +let savedEnv: Record; + +describe.skipIf(!kernelBuilt)('kernel Rust extraction parity', () => { + beforeAll(async () => { + await initGrammars(); + await loadGrammarsForLanguages(['rust']); + }); + + beforeEach(() => { + savedEnv = Object.fromEntries(ENV_KEYS.map((k) => [k, process.env[k]])); + resetKernelForTests(); + }); + + afterEach(() => { + for (const k of ENV_KEYS) { + if (savedEnv[k] === undefined) delete process.env[k]; + else process.env[k] = savedEnv[k]; + } + resetKernelForTests(); + }); + + function assertParity(filePath: string, source: string, minNodes = 3): void { + process.env.CODEGRAPH_KERNEL_LANGS = 'all'; + delete process.env.CODEGRAPH_KERNEL; + const viaKernel = tryKernelExtract(filePath, source, 'rust'); + expect(viaKernel, `kernel extraction failed for ${filePath}`).not.toBeNull(); + + process.env.CODEGRAPH_KERNEL = '0'; + const viaWasm = extractFromSource(filePath, source, 'rust'); + delete process.env.CODEGRAPH_KERNEL; + + const k = canon(viaKernel!); + const w = canon(viaWasm); + expect(k.nodes, `${filePath}: nodes`).toEqual(w.nodes); + expect(k.edges, `${filePath}: edges`).toEqual(w.edges); + expect(k.refs, `${filePath}: refs`).toEqual(w.refs); + expect(viaWasm.nodes.length).toBeGreaterThanOrEqual(minNodes); + } + + it('torture fixture: impl/trait quirks, use bindings, chains, fn-refs, value-refs, route macros', () => { + const file = path.join(FIXTURE_DIR, 'torture.rs'); + assertParity('fixtures/torture.rs', fs.readFileSync(file, 'utf8'), 20); + }); + + // CRLF variant — the shape every Windows autocrlf checkout has. Derived in + // memory so no platform or editor can silently normalize it away; pins the + // JS-multiline-^ docstring semantics for `///` runs (#1329). + it('torture fixture CRLF parity', () => { + const file = path.join(FIXTURE_DIR, 'torture.rs'); + const crlf = fs.readFileSync(file, 'utf8').replace(/(? { + const broken = 'fn f( {\n return }} 12 (\n'; + process.env.CODEGRAPH_KERNEL_LANGS = 'all'; + delete process.env.CODEGRAPH_KERNEL; + expect(tryKernelExtract('src/broken.rs', broken, 'rust')).toBeNull(); + process.env.CODEGRAPH_KERNEL = '0'; + const viaWasm = extractFromSource('src/broken.rs', broken, 'rust'); + delete process.env.CODEGRAPH_KERNEL; + expect(viaWasm.nodes.some((n) => n.kind === 'file')).toBe(true); + }); +}); diff --git a/codegraph-kernel/Cargo.lock b/codegraph-kernel/Cargo.lock index 95885c2..7386dbd 100644 --- a/codegraph-kernel/Cargo.lock +++ b/codegraph-kernel/Cargo.lock @@ -58,6 +58,7 @@ dependencies = [ "tree-sitter-java", "tree-sitter-javascript", "tree-sitter-python", + "tree-sitter-rust", "tree-sitter-typescript", ] @@ -550,6 +551,16 @@ dependencies = [ "tree-sitter-language", ] +[[package]] +name = "tree-sitter-rust" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439e577dbe07423ec2582ac62c7531120dbfccfa6e5f92406f93dd271a120e45" +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 e2a7c48..24d9aaa 100644 --- a/codegraph-kernel/Cargo.toml +++ b/codegraph-kernel/Cargo.toml @@ -30,6 +30,7 @@ tree-sitter-go = "0.23" # (R7a prep, #1345). A patch bump here without re-vendoring breaks the match. tree-sitter-c = "=0.24.2" tree-sitter-cpp = "=0.23.4" +tree-sitter-rust = "=0.24.2" [build-dependencies] napi-build = "2" diff --git a/codegraph-kernel/src/langs.rs b/codegraph-kernel/src/langs.rs index 9185a9a..cfd9b86 100644 --- a/codegraph-kernel/src/langs.rs +++ b/codegraph-kernel/src/langs.rs @@ -15,8 +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; 9] = - ["typescript", "tsx", "javascript", "jsx", "java", "python", "go", "c", "cpp"]; +pub const LANGUAGES: [&str; 10] = + ["typescript", "tsx", "javascript", "jsx", "java", "python", "go", "c", "cpp", "rust"]; pub fn grammar_for(language: &str) -> Option { match language { @@ -31,6 +31,8 @@ pub fn grammar_for(language: &str) -> Option { // TS-side — the route point applies preParse before the kernel call). "c" => Some(tree_sitter_c::LANGUAGE.into()), "cpp" => Some(tree_sitter_cpp::LANGUAGE.into()), + // R7b: v0.24.2, sha-matched with the vendored wasm (grammars.ts). + "rust" => Some(tree_sitter_rust::LANGUAGE.into()), _ => None, } } diff --git a/codegraph-kernel/src/lib.rs b/codegraph-kernel/src/lib.rs index 9ab3704..e75f988 100644 --- a/codegraph-kernel/src/lib.rs +++ b/codegraph-kernel/src/lib.rs @@ -24,6 +24,7 @@ mod ids; mod go; mod java; mod langs; +mod rustlang; mod textutil; mod python; mod tsjs; @@ -211,6 +212,7 @@ pub fn extract_file(file_path: String, content: String, language: String) -> Res "python" => python::extract(&file_path, &content).map_err(Error::from_reason)?, "go" => go::extract(&file_path, &content).map_err(Error::from_reason)?, "c" | "cpp" => ccpp::extract(&file_path, &content, &language).map_err(Error::from_reason)?, + "rust" => rustlang::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/rustlang.rs b/codegraph-kernel/src/rustlang.rs new file mode 100644 index 0000000..b5b9da3 --- /dev/null +++ b/codegraph-kernel/src/rustlang.rs @@ -0,0 +1,1474 @@ +//! Rust-language extraction — a faithful port of `TreeSitterExtractor`'s rust +//! paths (src/extraction/tree-sitter.ts) plus languages/rust.ts. ("rustlang" +//! because `rust` alone collides with the kernel's own implementation +//! language.) Survey artifact: docs/design/rust-lang-kernel-port-checklist.md. +//! +//! Rust's shape quirks, mirrored exactly (bug-for-bug, all verified against +//! the TS reference): +//! - `isAsync` is dead code upstream: it scans DIRECT children for an `async` +//! token, but the grammar nests it inside `function_modifiers` — every rust +//! fn/method carries isAsync **false** (present-false, never absent). +//! - impl blocks push NO scope: members re-dispatch at file scope, so an impl +//! associated `const` becomes a FILE-level `variable`, and the method↔owner +//! `contains` edge is a source-order name scan (an impl ABOVE its struct +//! gets no edge). `impl Trait for Generic`'s receiver resolves to the +//! TRAIT (the only direct type_identifier), and methods get QN +//! `Trait::method` — preserve, never "fix" via the grammar's trait:/type: +//! fields. +//! - `const_item`/`static_item` ride the generic extractVariable fallback: +//! kind is always `variable`, no signature, and EVERY direct `identifier` +//! child mints a node (`const MAX: u32 = OTHER;` → two nodes, `MAX` + the +//! phantom `OTHER`). Top-level initializer values are never body-walked. +//! - Unit structs (`struct Unit;`, no body field) mint NO node; `mod_item` +//! mints no module node and adds no QN prefix. +//! - Chained-call re-encode is scoped_identifier-gated (`Foo::new().bar()` → +//! `Foo::new().bar`); instance chains, parens, `.await`, 2-hop fields, and +//! `self` receivers all collapse to the bare method name (`self` is node +//! kind `self`, not `identifier`, so it dodges SKIP_RECEIVERS by falling +//! through). Turbofish callees keep the raw `helper::` text. +//! - `use` emits an import node named by the ROOT module (`crate`/`self`/…), +//! one root `imports` ref, then one FULL-path `imports` ref per binding; +//! `use x::*` (use_wildcard) emits nothing at all. +//! - Trait supertraits come only from `trait_bounds`; a scoped supertrait +//! (`fmt::Debug`) matches no case and is silently dropped. +//! - Rocket `routes!`/`catchers!` are extracted ONLY inside function bodies, +//! and only when the macro name is a bare identifier. +//! - A rust type alias emits NO ref to its aliased type (the shared code +//! reads a `value` field; rust's field is `type`). +//! - An `attribute_item` between a doc comment and its item breaks the +//! docstring sibling chain (`#[derive(..)]` kills the docstring). +//! 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_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; + +/// JS `/<[^>]*>/g` — the non-nested generic strip (breaks on nested generics +/// by design: `Result, E>` → `Result, E>` → returnType undefined). +fn generic_angle_re() -> &'static Regex { + static RE: OnceLock = OnceLock::new(); + RE.get_or_init(|| Regex::new(r"<[^>]*>").unwrap()) +} +/// JS `/^[A-Za-z_]\w*$/` (ASCII \w — the regex crate's \w is Unicode). +fn simple_ident_re() -> &'static Regex { + static RE: OnceLock = OnceLock::new(); + RE.get_or_init(|| Regex::new(r"^[A-Za-z_][0-9A-Za-z_]*$").unwrap()) +} + +struct Scope { + row: u32, + kind: &'static str, + name: String, +} + +#[derive(Default)] +struct Extra { + docstring: Option, + signature: Option, + return_type: Option, + qualified_name: Option, + visibility: Option, + is_exported: Option, + is_async: 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 and +/// findNodeByName (mirrors the TS scans 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("rust").ok_or("no rust grammar")?; + let t0 = std::time::Instant::now(); + let mut parser = Parser::new(); + parser + .set_language(&grammar) + .map_err(|e| format!("set_language(rust) 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) + } + /// isInsideClassLikeNode — stack TOP only, file doesn't count. + 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) { + // `::`-separated rust paths match NEITHER regex (separators are + // `.`/`\`), so multi-segment use-imports contribute nothing to + // the fn-ref gate — the rust gate is effectively same-file-only. + 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); + } + if let Some(v) = extra.is_async { + flags.set(FLAG_IS_ASYNC, 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: extra.visibility.unwrap_or(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()); + } + // captureValueRefScope: rust consts are kind `variable` — still targets. + 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) + } + + /// extractName — nameField `name`, else the identifier-like child scan. + 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() + } + + /// rustExtractor.getSignature: raw params text + ` -> ` + raw return type. + 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(rt) = node.child_by_field_name("return_type") { + sig.push_str(" -> "); + sig.push_str(self.text(rt)); + } + Some(sig) + } + + /// rustExtractor.getVisibility: direct `visibility_modifier` child whose + /// text contains `pub` → public, else private; none → private. + fn visibility_of(&self, node: Node) -> u8 { + for i in 0..node.child_count() { + if let Some(c) = node.child(i) { + if c.kind() == "visibility_modifier" { + return if self.text(c).contains("pub") { 1 } else { 2 }; + } + } + } + 2 // private — Rust defaults to private + } + + /// extractRustReturnType (languages/rust.ts:14). + fn return_type_of(&self, node: Node) -> Option { + let mut rt = node.child_by_field_name("return_type")?; + if rt.kind() == "reference_type" { + rt = (0..rt.named_child_count()) + .filter_map(|i| rt.named_child(i)) + .find(|c| matches!(c.kind(), "type_identifier" | "scoped_type_identifier" | "generic_type")) + .unwrap_or(rt); + } + if matches!(rt.kind(), "primitive_type" | "unit_type" | "tuple_type") { + return None; + } + let text = self.text(rt).trim(); + let stripped = generic_angle_re().replace_all(text, ""); + let last = stripped.rsplit("::").next().unwrap_or("").trim(); + if last.is_empty() || !simple_ident_re().is_match(last) { + return None; + } + Some(if last == "Self" { "self".to_string() } else { last.to_string() }) + } + + /// rustExtractor.getReceiverType: parent-walk to the nearest impl_item; + /// LAST direct type_identifier child wins (for `impl Trait for Generic` + /// that's the TRAIT — bug preserved); else the first generic_type's inner + /// type_identifier. + fn receiver_type_of(&self, node: Node) -> Option { + let mut parent = node.parent(); + while let Some(p) = parent { + if p.kind() == "impl_item" { + let type_idents: Vec = (0..p.named_child_count()) + .filter_map(|i| p.named_child(i)) + .filter(|c| c.kind() == "type_identifier") + .collect(); + if let Some(last) = type_idents.last() { + return Some(self.text(*last).to_string()); + } + let generic = (0..p.named_child_count()) + .filter_map(|i| p.named_child(i)) + .find(|c| c.kind() == "generic_type"); + if let Some(g) = generic { + let inner = (0..g.named_child_count()) + .filter_map(|i| g.named_child(i)) + .find(|c| c.kind() == "type_identifier"); + if let Some(inner) = inner { + return Some(self.text(inner).to_string()); + } + } + return None; + } + parent = p.parent(); + } + None + } + + // --- 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 matches!(kind, "function_item" | "function_signature_item") { + self.extract_fn_or_method(node); + skip_children = true; + } else if kind == "trait_item" { + self.extract_interface(node); + skip_children = true; + } else if kind == "struct_item" { + self.extract_struct(node); + skip_children = true; + } else if kind == "enum_item" { + self.extract_enum(node); + skip_children = true; + } else if kind == "type_item" { + self.extract_type_alias(node); + // extractTypeAlias returns false for rust (plain alias) — children + // are visited (nothing in them has a branch). + } else if matches!(kind, "let_declaration" | "const_item" | "static_item") + && !self.inside_class_like() + { + // Inside a class-like scope the gate fails and the else-ladder + // falls through with children VISITED — a trait const's value + // expression emits calls refs from the trait node. + self.extract_variable(node); + self.scan_fn_ref_subtree(node, 0); + skip_children = true; + } else if kind == "use_declaration" { + self.extract_import(node); + // importTypes branch never sets skipChildren. + } else if kind == "call_expression" { + self.extract_call(node); + } else if kind == "struct_expression" { + self.extract_instantiation(node); + } else if kind == "impl_item" { + // Emits the implements back-reference; skipChildren stays false so + // the declaration_list is visited at FILE scope (impl pushes + // nothing on the stack). + self.extract_rust_impl_item(node); + } + + if !skip_children { + for i in 0..node.named_child_count() { + if let Some(c) = node.named_child(i) { + self.visit_node(c); + } + } + } + } + + // --- extractors -------------------------------------------------------------- + + /// extractFunction/extractMethod, decision resolved once: method iff a + /// receiver is found (fn inside an impl — including a NESTED fn inside an + /// impl method's body, whose parent walk passes through the outer fn) or + /// the stack top is class-like (trait members). + fn extract_fn_or_method(&mut self, node: Node<'t>) { + let receiver = self.receiver_type_of(node); + let as_method = receiver.is_some() || self.inside_class_like(); + + 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), + visibility: Some(self.visibility_of(node)), + // isAsync hook exists but never finds a direct `async` child (it + // nests in function_modifiers) — present-false on every node. + is_async: Some(false), + return_type: self.return_type_of(node), + qualified_name: receiver.as_ref().map(|r| format!("{r}::{name}")), + ..Extra::default() // isExported hook absent → flag not set + }; + let kind: &'static str = if as_method { "method" } else { "function" }; + let Some(row) = self.create_node(kind, &name, node, extra) else { return }; + + // Contains edge from the owner: receiver present AND not class-like — + // FIRST earlier-in-file struct/class/enum/trait of the receiver's name. + if as_method && !self.inside_class_like() { + if let Some(receiver) = &receiver { + let owner_row = self + .nodes_meta + .iter() + .position(|m| { + m.name == *receiver + && 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); + // extractDecoratorsFor: rust attribute_items are siblings, not + // decorator/annotation/attribute node types — complete no-op. + self.stack.push(Scope { row, kind, name }); + if let Some(body) = node.child_by_field_name("body") { + self.visit_function_body(body); + } + self.stack.pop(); + } + + /// extractInterface — kind `trait` (interfaceKind), inheritance from + /// trait_bounds, body children visited with the trait pushed. + fn extract_interface(&mut self, node: Node<'t>) { + let name = self.extract_name(node); + let extra = Extra { + docstring: preceding_docstring(node, self.src), + ..Extra::default() // no visibility/isExported on the interface path + }; + let Some(row) = self.create_node("trait", &name, node, extra) else { return }; + self.extract_inheritance(node, row); + + self.stack.push(Scope { row, kind: "trait", 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(); + } + + /// extractStruct — body field REQUIRED (unit structs mint no node; tuple + /// structs' ordered_field_declaration_list is a body). + fn extract_struct(&mut self, node: Node<'t>) { + let Some(body) = node.child_by_field_name("body") else { return }; + let name = self.extract_name(node); + let extra = Extra { + docstring: preceding_docstring(node, self.src), + visibility: Some(self.visibility_of(node)), + ..Extra::default() + }; + let Some(row) = self.create_node("struct", &name, node, extra) else { return }; + self.extract_inheritance(node, row); + + self.stack.push(Scope { row, kind: "struct", name }); + for i in 0..body.named_child_count() { + if let Some(c) = body.named_child(i) { + self.visit_node(c); + } + } + self.stack.pop(); + } + + /// extractEnum — body required; enum_variant children → enum_member nodes + /// (name field only, payloads never walked); other children re-dispatched. + fn extract_enum(&mut self, node: Node<'t>) { + let Some(body) = node.child_by_field_name("body") else { return }; + let name = self.extract_name(node); + let extra = Extra { + docstring: preceding_docstring(node, self.src), + visibility: Some(self.visibility_of(node)), + ..Extra::default() + }; + let Some(row) = self.create_node("enum", &name, node, extra) else { return }; + self.extract_inheritance(node, row); + + self.stack.push(Scope { row, kind: "enum", name }); + for i in 0..body.named_child_count() { + let Some(c) = body.named_child(i) else { continue }; + if c.kind() == "enum_variant" { + if let Some(name_node) = c.child_by_field_name("name") { + let vname = self.text(name_node).to_string(); + self.create_node("enum_member", &vname, c, Extra::default()); + } + } else { + self.visit_node(c); + } + } + self.stack.pop(); + } + + /// extractTypeAlias — plain `type_alias` node. QUIRK: the alias-value ref + /// walk reads a `value` field; rust type_item's field is `type` → no ref + /// to the aliased type. Returns children-visited (false) like the TS. + fn extract_type_alias(&mut self, node: Node<'t>) { + let name = self.extract_name(node); + if name == "" { + return; + } + let extra = Extra { + docstring: preceding_docstring(node, self.src), + ..Extra::default() + }; + self.create_node("type_alias", &name, node, extra); + } + + /// extractVariable's generic fallback: kind is ALWAYS `variable` (no + /// isConst hook), every direct `identifier` child mints a node positioned + /// at the CHILD, docstring shared, isExported present-false, no signature, + /// and the initializer value is never body-walked. + fn extract_variable(&mut self, node: Node<'t>) { + let docstring = preceding_docstring(node, self.src); + for i in 0..node.named_child_count() { + let Some(child) = node.named_child(i) else { continue }; + if child.kind() != "identifier" { + continue; + } + let name = self.text(child).to_string(); + if !name.is_empty() { + self.create_node( + "variable", + &name, + child, + Extra { + docstring: docstring.clone(), + is_exported: Some(false), + ..Extra::default() + }, + ); + } + } + } + + /// extractImport via the rust hook: import node named by the ROOT module + + /// one generic root `imports` ref + per-binding FULL-path refs. + /// `use x::*;` (use_wildcard) → hook returns null → nothing at all. + fn extract_import(&mut self, node: Node<'t>) { + let use_arg = (0..node.named_child_count()) + .filter_map(|i| node.named_child(i)) + .find(|c| matches!(c.kind(), "scoped_use_list" | "scoped_identifier" | "use_list" | "identifier")); + let Some(use_arg) = use_arg else { return }; + + let module_name = self.root_module(use_arg); + let signature = self.text(node).trim().to_string(); + self.create_node( + "import", + &module_name.clone(), + node, + Extra { signature: Some(signature), ..Extra::default() }, + ); + let parent = self.top_row(); + let imports_kind = edge_kind_index("imports").unwrap(); + if !module_name.is_empty() { + self.push_ref_at(parent, &module_name, imports_kind, node); + } + self.emit_use_binding_refs(node, parent); + } + + /// getRootModule (languages/rust.ts:124). + fn root_module(&self, n: Node) -> String { + let Some(first) = n.named_child(0) else { + return self.text(n).to_string(); + }; + match first.kind() { + "identifier" | "crate" | "super" | "self" => self.text(first).to_string(), + "scoped_identifier" => self.root_module(first), + _ => self.text(first).to_string(), + } + } + + /// emitRustUseBindingRefs (tree-sitter.ts:3451) — one FULL-path `imports` + /// ref per binding; `Path as Alias` links the source path; leaves that are + /// only `self`/`super`/`crate`/`*` are skipped. + fn emit_use_binding_refs(&mut self, node: Node<'t>, from_row: u32) { + let mut paths: Vec<(String, Node)> = Vec::new(); + fn join(prefix: &str, seg: &str) -> String { + if prefix.is_empty() { seg.to_string() } else { format!("{prefix}::{seg}") } + } + fn collect<'t>(w: &Walker<'t>, n: Node<'t>, prefix: &str, paths: &mut Vec<(String, Node<'t>)>) { + match n.kind() { + "identifier" => paths.push((join(prefix, w.text(n)), n)), + "scoped_identifier" => { + let full = w.text(n).trim(); + paths.push(( + if prefix.is_empty() { full.to_string() } else { format!("{prefix}::{full}") }, + n, + )); + } + "scoped_use_list" => { + let seg = n + .child_by_field_name("path") + .map(|p| w.text(p).trim().to_string()) + .unwrap_or_default(); + let new_prefix = if seg.is_empty() { prefix.to_string() } else { join(prefix, &seg) }; + let list = n.child_by_field_name("list").or_else(|| { + (0..n.named_child_count()) + .filter_map(|i| n.named_child(i)) + .find(|c| c.kind() == "use_list") + }); + if let Some(list) = list { + collect(w, list, &new_prefix, paths); + } + } + "use_list" => { + for i in 0..n.named_child_count() { + if let Some(c) = n.named_child(i) { + collect(w, c, prefix, paths); + } + } + } + "use_as_clause" => { + let p = n.child_by_field_name("path").or_else(|| n.named_child(0)); + if let Some(p) = p { + collect(w, p, prefix, paths); + } + } + _ => {} // visibility_modifier, use_wildcard, bare crate/self/super + } + } + for i in 0..node.named_child_count() { + if let Some(c) = node.named_child(i) { + collect(self, c, "", &mut paths); + } + } + let imports_kind = edge_kind_index("imports").unwrap(); + for (text, n) in paths { + let leaf = text.rsplit("::").next().unwrap_or(""); + if leaf.is_empty() || matches!(leaf, "self" | "super" | "crate" | "*") { + continue; + } + self.push_ref_at(from_row, &text, imports_kind, n); + } + } + + /// extractCall — the rust paths of the generic else-branch (4312+). + 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() == "field_expression" { + 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; // emit NOTHING (#1230) + } + } + if let Some(r) = receiver { + match r.kind() { + // rust `self` is node kind `self`, NOT `identifier` — + // it dodges this branch and falls to the bare-name + // fallthrough (same net effect as SKIP_RECEIVERS). + "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" => { + // Chained-call re-encode: ONLY an associated- + // function chain (`Foo::new().bar()`, inner + // callee a scoped_identifier). Instance chains + // keep the bare method name. + let inner_fn = r.child_by_field_name("function"); + let reencode = + inner_fn.map(|f| f.kind() == "scoped_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(); + } + } + _ => { + // field_expression 2-hop, parenthesized, + // await_expression, `self` — bare method name. + callee_name = method_name.to_string(); + } + } + } else { + callee_name = method_name.to_string(); + } + } + } else if matches!(func.kind(), "scoped_identifier" | "scoped_call_expression") { + callee_name = self.text(func).to_string(); + } else { + // identifier; generic_function keeps the raw turbofish text + // (`helper::` — unresolvable downstream, preserved). + callee_name = self.text(func).to_string(); + } + } + + if !callee_name.is_empty() { + // Parenthesized-callee normalization — `(f)(x)` → `f`. + 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 — struct_expression via the GENERIC path: strip + /// from the first `<`, keep the trailing `::`/`.` segment (JS slice + /// semantics: slice(lastDot+1) after a `::` leaves one `:`, then ONE + /// leading `[:.]` is stripped). + 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 }; + + let mut class_name = self.text(ctor).to_string(); + if let Some(lt) = class_name.find('<') { + if lt > 0 { + class_name.truncate(lt); + } + } + let last_dot = class_name.rfind('.').map(|i| i as i64).unwrap_or(-1); + let last_colon = class_name.rfind("::").map(|i| i as i64).unwrap_or(-1); + let last = last_dot.max(last_colon); + if last >= 0 { + class_name = class_name[(last + 1) as usize..].to_string(); + if let Some(rest) = class_name.strip_prefix(&[':', '.'][..]) { + class_name = rest.to_string(); + } + } + let class_name = class_name.trim().to_string(); + + if !class_name.is_empty() { + let from = self.top_row(); + self.push_ref_at(from, &class_name, edge_kind_index("instantiates").unwrap(), node); + } + } + + /// extractRustRouteMacro — body-walker-only; bare `routes`/`catchers` + /// identifiers only (`rocket::routes![…]` is skipped); identifier runs in + /// the token tree join with `::`, flushed on `,` and at end. + fn extract_rust_route_macro(&mut self, node: Node<'t>) { + let Some(macro_name) = node.named_child(0) else { return }; + let name = self.text(macro_name); + if name != "routes" && name != "catchers" { + return; + } + let token_tree = (0..node.named_child_count()) + .filter_map(|i| node.named_child(i)) + .find(|c| c.kind() == "token_tree"); + let Some(token_tree) = token_tree else { return }; + if self.stack.is_empty() { + return; + } + let from = self.top_row(); + let refs_kind = edge_kind_index("references").unwrap(); + + let mut parts: Vec<&str> = Vec::new(); + let mut line = 0u32; + let mut column_byte = 0usize; + let mut row = 0usize; + macro_rules! flush { + () => { + if !parts.is_empty() { + let joined = parts.join("::"); + let column = util::col16(self.src, &self.line_starts, row, column_byte); + let name_ref = self.arena.put(&joined); + self.tables.push_ref(&RefRow { + from_idx: from, + kind: refs_kind, + line, + column, + reference_name: name_ref, + candidates: NONE_STR, + from_id_str: NONE_STR, + }); + parts.clear(); + } + }; + } + for i in 0..token_tree.child_count() { + let Some(t) = token_tree.child(i) else { continue }; + if t.kind() == "identifier" { + if parts.is_empty() { + line = t.start_position().row as u32 + 1; + column_byte = t.start_byte(); + row = t.start_position().row; + } + parts.push(self.text(t)); + } else if t.kind() == "," { + flush!(); + } + } + flush!(); + } + + /// extractInheritance — the rust-reachable cases: trait_bounds + /// (supertraits; a scoped `fmt::Debug` bound matches NO case and is + /// dropped), the Go embedding check on field_declaration (inert in rust — + /// every field has a field_identifier), and the field_declaration_list + /// recursion that reaches it. + 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() { + "trait_bounds" => { + for j in 0..child.named_child_count() { + let Some(bound) = child.named_child(j) else { continue }; + let type_node: Option = match bound.kind() { + "type_identifier" => Some(bound), + "generic_type" => (0..bound.named_child_count()) + .filter_map(|k| bound.named_child(k)) + .find(|c| c.kind() == "type_identifier"), + "higher_ranked_trait_bound" => { + let generic = (0..bound.named_child_count()) + .filter_map(|k| bound.named_child(k)) + .find(|c| c.kind() == "generic_type"); + generic + .and_then(|g| { + (0..g.named_child_count()) + .filter_map(|k| g.named_child(k)) + .find(|c| c.kind() == "type_identifier") + }) + .or_else(|| { + (0..bound.named_child_count()) + .filter_map(|k| bound.named_child(k)) + .find(|c| c.kind() == "type_identifier") + }) + } + _ => None, // scoped_type_identifier: dropped (quirk) + }; + if let Some(tn) = type_node { + let name = self.text(tn).to_string(); + self.push_ref_at(class_row, &name, extends_kind, tn); + } + } + } + "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); + } + _ => {} + } + } + } + + /// extractRustImplItem — `impl Trait for Type` back-reference: positional + /// type-node filter (NEVER the grammar's trait:/type: fields), ≥2 needed, + /// target found by FIRST earlier node of kind struct/enum/class (never + /// trait); ref FROM the type's node, named by the trait's full text. + fn extract_rust_impl_item(&mut self, node: Node<'t>) { + let has_for = (0..node.child_count()) + .filter_map(|i| node.child(i)) + .any(|c| c.kind() == "for" && !c.is_named()); + if !has_for { + return; + } + let type_idents: Vec = (0..node.named_child_count()) + .filter_map(|i| node.named_child(i)) + .filter(|c| matches!(c.kind(), "type_identifier" | "generic_type" | "scoped_type_identifier")) + .collect(); + if type_idents.len() < 2 { + return; + } + let trait_node = type_idents[0]; + let type_node = type_idents[type_idents.len() - 1]; + + let trait_name = self.text(trait_node).to_string(); + let type_name = if type_node.kind() == "generic_type" { + (0..type_node.named_child_count()) + .filter_map(|i| type_node.named_child(i)) + .find(|c| c.kind() == "type_identifier") + .map(|c| self.text(c).to_string()) + .unwrap_or_else(|| self.text(type_node).to_string()) + } else { + self.text(type_node).to_string() + }; + + let target_row = self + .nodes_meta + .iter() + .position(|m| m.name == type_name && matches!(m.kind, "struct" | "enum" | "class")) + .map(|i| i as u32); + if let Some(target_row) = target_row { + self.push_ref_at(target_row, &trait_name, edge_kind_index("implements").unwrap(), trait_node); + } + } + + /// extractTypeAnnotations — parameters + return_type subtrees, one + /// `references` ref per type_identifier leaf not in BUILTIN_TYPES. The + /// trailing `type_annotation` child lookup is included for fidelity (the + /// rust grammar has no such node — always a no-op). + 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("return_type") { + 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); + } + } + } + + // --- visitFunctionBody ----------------------------------------------------- + + 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); + + // Rocket route macros: handler paths live in a raw token tree. + if kind == "macro_invocation" { + self.extract_rust_route_macro(node); + } + + if kind == "call_expression" { + self.extract_call(node); + } else if kind == "struct_expression" { + self.extract_instantiation(node); + } + + // Nested NAMED fns become their own nodes (a nested fn inside an impl + // method walks up to the impl and indexes as a METHOD). + if matches!(kind, "function_item" | "function_signature_item") { + let name = self.extract_name(node); + if name != "" { + self.extract_fn_or_method(node); + return; + } + } + + // Structural nodes inside bodies. + if kind == "struct_item" { + self.extract_struct(node); + return; + } + if kind == "enum_item" { + self.extract_enum(node); + return; + } + if kind == "trait_item" { + self.extract_interface(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); + } + } + } + + // --- fn refs (RUST_SPEC) ---------------------------------------------------- + + /// maybeCaptureFnRefs with RUST_SPEC's dispatch: arguments→args, + /// assignment_expression→rhs(right), field_initializer→value(value), + /// array_expression→list, static_item/let_declaration→varinit(value). + /// No layers/unwrap/special — only bare identifiers qualify (`&handler` + /// captures nothing). QUIRK: const_item is NOT in the dispatch. + fn maybe_capture_fn_refs(&mut self, node: Node<'t>) { + enum Mode { + Args, + Rhs, + Value, + List, + Varinit, + } + let mode = match node.kind() { + "arguments" => Mode::Args, + "assignment_expression" => Mode::Rhs, + "field_initializer" => Mode::Value, + "array_expression" => Mode::List, + "static_item" | "let_declaration" => Mode::Varinit, + _ => return, + }; + if self.stack.is_empty() { + return; + } + let from = self.top_row(); + + let mut values: Vec = Vec::new(); + match mode { + Mode::Args | Mode::List => { + for i in 0..node.named_child_count() { + if let Some(c) = node.named_child(i) { + values.push(c); + } + } + } + Mode::Rhs => { + if let Some(rhs) = node.child_by_field_name("right") { + // Param-storage skip: `o.cb = cb`. + 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); + } + } + } + Mode::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); + } + } + Mode::Varinit => { + // Destructuring skip: a tuple/struct pattern LHS extracts data, + // never a function alias (static_item's name is an identifier, + // let_declaration's `pattern` field can be a pattern). + let name_node = node + .child_by_field_name("name") + .or_else(|| node.child_by_field_name("pattern")); + if let Some(nn) = name_node { + if matches!( + nn.kind(), + "object_pattern" | "array_pattern" | "tuple_pattern" | "struct_pattern" + ) { + return; + } + } + if let Some(v) = node.child_by_field_name("value") { + values.push(v); + } + } + } + + for v in values { + // normalizeValue: idTypes = {identifier} only, no layers/unwrap. + if v.kind() == "identifier" { + let name = self.text(v).to_string(); + if name.is_empty() || is_stoplisted(&name) { + continue; + } + 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, + }); + } + } + } + + fn scan_fn_ref_subtree(&mut self, node: Node<'t>, depth: u32) { + if depth > 12 { + return; + } + if depth > 0 + && matches!( + node.kind(), + "function_item" | "function_signature_item" | "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 — rust declarator shapes: const_item/static_item (name + // field) and let_declaration (the shadow source: `pattern` field; a + // tuple pattern bumps every named child). + 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_item" | "static_item" => { + bump(&mut decl_counts, n.child_by_field_name("name"), self.src, &targets) + } + "let_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" + ) +} + +/// LITERAL_RECEIVER_TYPES (shared table). +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 — port the WHOLE set: a rust `String` +/// type_identifier IS suppressed via the Scala row). +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/docs/design/rust-kernel-migration-plan.md b/docs/design/rust-kernel-migration-plan.md index dc45ceb..299f929 100644 --- a/docs/design/rust-kernel-migration-plan.md +++ b/docs/design/rust-kernel-migration-plan.md @@ -127,8 +127,24 @@ them are the ORIGINAL plan and carry expectations that measurement later correct **19.1 min kernel-arm** (parse-loop 560 → 356s; R6 26.4 → P1 17.6 on the old smaller graph → 19.1 on the new richer one: 2,048,295 nodes / 6,406,933 edges, two runs byte-same). -- [ ] **R7b. Remaining long tail** per the tracker (§4) — ruby/php/csharp/rust/… T1s +- [~] **R7b. Remaining long tail** per the tracker (§4) — ruby/php/csharp/… T1s are now ~1-day-each with the walker pattern; T3 may stay TS forever (fine). + **rust DONE 2026-07-20** (first R7b port): grammar bumped to + tree-sitter-rust v0.24.2 (crate `=0.24.2` + vendored wasm from tag + `77a3747`, parser.c/scanner.c sha-matched; replaces the 2023 ABI-14 + tree-sitter-wasms build — wasm-path bump validated standalone: ripgrep/ + tokio node sections IDENTICAL, small precision-positive edge churn only, + full suite green), walker `codegraph-kernel/src/rustlang.rs` (survey + artifact: rust-lang-kernel-port-checklist.md — isAsync dead-code, + impl-pushes-no-scope, trait-receiver bug on `impl Trait for Generic`, + phantom const identifiers, use-binding triple emission, all preserved + bug-for-bug). Gates: parity sweeps **0 diffs** on ripgrep (101/101, + 0 deferred) / tokio (790/790, 0 deferred) / rust-analyzer (1217/1488, + 0 diffs; 271 deferrals are token-macro-table sources — `T![~]`, `[$]` — + that error on BOTH arms, grammar-inherent like fmt's C++ 42%); full-init + dump-diffs **byte-identical** ×3 (3,857 / 13,440 / 39,030 nodes); + DEFAULT_ROUTED += rust; kernel-rustlang-parity suite (torture + CRLF + + defer) in `npm test`. - [ ] **P2. Arc 3, graph richness** (§7b) — product-priority call, standard gates. - [ ] **P3. Parked items** (§7c) — only with explicit maintainer approval. @@ -590,7 +606,8 @@ parity before porting the language. | 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. | ☐ | +| rust | `languages/rust.ts` | T1 | crates.io | **DONE (R7b #1, 2026-07-20)** — `rustlang.rs` walker; grammar bumped to v0.24.2 (crate + vendored wasm together). Parity 0-diff on ripgrep/tokio/rust-analyzer + dump byte-identical ×3; rust-analyzer's parser crates defer 18% (token-macro tables, both-arm parse errors — grammar-inherent). Quirk list: docs/design/rust-lang-kernel-port-checklist.md. | ☑ | +| 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. | ☐ | | kotlin | `languages/kotlin.ts` | T1½ | crates.io | Expect/actual pairing is synthesis-side (fine); extraction is clean but validate against a KMP repo. | ☐ | | swift | shared + dedicated branch | T1½ | crates.io | **Trap:** in-class property extraction lives in `tree-sitter.ts`'s DEDICATED branch, not `swift.ts` (#1020 — Alamofire went 0→348 props). Gate on Alamofire. | ☐ | | c, cpp | `languages/c-cpp.ts` | **T2** | crates.io | **DONE (R7a, 2026-07-17)** — `ccpp/` walker; ALL pre-passes stayed TS-side via the route-point preParse hoist (+6 new blanks added during gating — see the checklist doc); content-based `.h` C-vs-C++ detection stays upstream at detectLanguage. Parity 0-diff + dump byte-identical on redis/git/fmt/protobuf/ALS. | ☑ | diff --git a/docs/design/rust-lang-kernel-port-checklist.md b/docs/design/rust-lang-kernel-port-checklist.md new file mode 100644 index 0000000..d931e7c --- /dev/null +++ b/docs/design/rust-lang-kernel-port-checklist.md @@ -0,0 +1,526 @@ +# Rust-language kernel port (R7b) — the bug-for-bug checklist + +("rust-lang" in the filename to avoid confusion with the kernel's own +implementation language.) + +**Status: PORT COMPLETE (2026-07-20)** — walker `codegraph-kernel/src/rustlang.rs`, +all gates below passed (parity sweeps 0-diff on ripgrep/tokio/rust-analyzer, +dump gates byte-identical ×3, DEFAULT_ROUTED += rust). This doc remains the +quirk reference for the walker. Survey basis: every TS-side branch a +`.rs` file exercises, with file:line anchors as of `ce0ae30` (HEAD at survey +time). Every grammar-shape claim below was **probed against the vendored +tree-sitter-rust v0.24.2 wasm** (probe scripts in the session scratchpad), not +assumed. Read WITH `docs/design/rust-kernel-migration-plan.md` (§0a recipe, §5 +gates) and `docs/design/ccpp-kernel-port-checklist.md` (format precedent). + +**Grammar prep is ALREADY STAGED (uncommitted at survey time):** Cargo.toml +pins `tree-sitter-rust = "=0.24.2"`, `src/extraction/wasm/tree-sitter-rust.wasm` +is vendored from tag `77a3747` (parser.c/scanner.c sha-matched against the +crates.io tarball), and `rust` is in `VENDORED_WASM_LANGS` (grammars.ts:291) — +replacing the 2023-era tree-sitter-wasms build (ABI 14 → 15). Per the recipe: +land the grammar bump FIRST and get the full suite green before the walker +exists. Probing showed the 0.24.2 shapes match the old build on every branch +below (function_modifiers nesting, token trees, impl fields, use shapes), so no +TS-side behavior change is expected from the bump — but the suite run is the +proof. + +## Architecture decisions + +1. **No preParse.** `rustExtractor` has no `preParse` hook, so the route + point's `preParsedSource` (kernel/index.ts:76) is a no-op for rust — both + arms parse raw bytes. Nothing to hoist, nothing to port. +2. **Cargo repos take the DECODED path, not raw buffers.** `rustResolver` + (resolution/frameworks/rust.ts:22, `languages: ['rust']`, detect = + `Cargo.toml` exists) has an `extract()` hook, and parse-worker.ts:93 forces + any language with an applicable framework `extract()` onto the decoded + `extractFromSource` path (framework nodes/refs merge into the decoded + result). So on real Rust repos the kernel win is parse+walk+decode, never + the buffers-to-store transport. Don't chase a raw-path number on + ripgrep/tokio and conclude the port is broken. +3. **The framework extractor itself needs NO port.** It is regex-over-raw-source + TS (see §Frameworks below) and runs identically after either arm inside + `extractFromSource` (tree-sitter.ts:6736-6758). Only the tree-sitter-walk + emissions below move to Rust. +4. **One walker module** (suggest `codegraph-kernel/src/rustlang.rs` — "rust" + alone collides with the crate language), registered in `langs.rs`; per-file + `has_error()` → `defer:` like every walker. +5. **`.rs` → `rust`** at detectLanguage (grammars.ts:78), no content sniffing, + no dialect. MAX_FILE_SIZE (1 MiB, extraction/index.ts:132) and generated-file + skips are orchestrator/TS-side and shared. + +## Extractor config (languages/rust.ts — 151 lines, read it whole) + +Types: functionTypes=[`function_item`, **`function_signature_item`**] (the +latter = a trait method DECLARATION `fn render(&self);` — extracted so a +trait's method set is first-class); classTypes=[] (impl blocks instead); +methodTypes = same two; interfaceTypes=[`trait_item`] with +**interfaceKind:'trait'**; structTypes=[`struct_item`]; enumTypes=[`enum_item`]; +enumMemberTypes=[`enum_variant`]; typeAliasTypes=[`type_item`]; +importTypes=[`use_declaration`]; callTypes=[`call_expression`]; +variableTypes=[`let_declaration`, `const_item`, `static_item`]. +nameField=`name`, bodyField=`body`, paramsField=`parameters`, +returnField=`return_type`. + +Hooks PRESENT (port each exactly): + +- **getReturnType = extractRustReturnType (rust.ts:14)** — reads the + `return_type` field; if `reference_type`, unwrap to the first namedChild of + type `type_identifier`/`scoped_type_identifier`/`generic_type` (`?? rt` — + falls back to the reference_type itself); then if type ∈ + {`primitive_type`,`unit_type`,`tuple_type`} → undefined. Else: + `text.trim().replace(/<[^>]*>/g, '')`, take last `::` segment, trim; must + match `/^[A-Za-z_]\w*$/` else undefined; `'Self'` → the marker **`'self'`** + (resolved to the impl's own type at resolution time). QUIRKS: the + non-greedy-ish `/<[^>]*>/g` strip breaks on NESTED generics — + `Result, E>` → `"Result, E>"` → regex fails → **undefined** (only + single-level generics like `Vec` → `Vec` survive). `-> &Foo` unwraps to + `Foo`; `-> fmt::Result` → `Result`. +- **getSignature (rust.ts:57)** — `undefined` if no `parameters` field; else + raw text of params, plus `' -> ' + ` when present. + Raw `getNodeText` — multi-line params keep their newlines. +- **isAsync (rust.ts:67) — DEAD CODE BUG, PRESERVE:** scans DIRECT children for + `child.type === 'async'`. Probed on v0.24.2: `async` nests inside a + `function_modifiers` child (`pub async fn` children: + `visibility_modifier, function_modifiers, fn, identifier, parameters, ->, + , block`), so **isAsync always returns false** — no rust node ever gets + `isAsync: true`. The walker must reproduce false. +- **getVisibility (rust.ts:74)** — direct child of type `visibility_modifier`: + text `.includes('pub')` → `'public'` else `'private'`; no modifier → + `'private'` (so `pub(crate)`/`pub(super)` are all `'public'`). +- **getReceiverType (rust.ts:83)** — walk PARENT chain to the nearest + `impl_item`; there: filter DIRECT namedChildren of type `type_identifier`; + if ≥1, return the LAST one's source text (`source.substring(startIndex, + endIndex)` — UTF-16 units). If none, find the first `generic_type` child and + return its inner `type_identifier` text; else undefined. Never an impl parent + → undefined. QUIRK/BUG, PRESERVE: for `impl Trait for Generic` the only + direct type_identifier is the TRAIT (probe: `impl Render for Container` → + typeIdents=[`Render`] → receiver = **`Render`**, the trait name — methods get + qualifiedName `Render::render` and a contains edge from the trait node if one + exists in-file). `impl fmt::Display for Fields` is fine + (scoped_type_identifier isn't type_identifier → [Fields]). `impl + Container` → no direct type_identifiers → generic branch → `Container`. + Note `` type_parameters is its own child, its inner T is NOT a direct + impl child. +- **extractImport (rust.ts:120)** — signature = trimmed full `use …;` text. + `useArg` = FIRST namedChild of type `scoped_use_list` | `scoped_identifier` | + `use_list` | `identifier` (a leading `visibility_modifier` on `pub use` is + skipped by the find). moduleName = `getRootModule(useArg)`: recurse into + `namedChild(0)` — if type ∈ {identifier, crate, super, self} return its text; + if `scoped_identifier` recurse; else return the child's text; no child → + whole node text. So `use crate::m::Item` → import node named **`crate`**; + `pub use self::sub::read` → **`self`**; `use foo;` → `foo`. QUIRK: + `use std::fmt::*;` parses as `use_wildcard`, which is NOT in the useArg list + → hook returns null → and because the hook exists, extractImport's + `if (this.extractor.extractImport) return;` (tree-sitter.ts:3350) fires → + **wildcard uses create NO import node and NO refs at all**. `handledRefs` is + not set → the generic path ALSO pushes one `imports` ref for the root module + name (`crate`/`self`/`std`/…) from the file node (tree-sitter.ts:3183-3194). + +Hooks ABSENT (the walker must NOT do these): `preParse`, `resolveName`, +`recoverMangledName`, `isMisparsedFunction`, `isConst`, `isStatic`, +`isExported`, `resolveBody`, `classifyClassNode`, `classifyMethodNode`, +`extractPropertyName`, `propertyTypes`, `fieldTypes`, `extraClassNodeTypes`, +`packageTypes`/`extractPackage`, `extractModifiers`, `synthesizeMembers`, +`extractBareCall`, `visitNode` hook, `skipBodilessClass`, `methodsAreTopLevel`. +Consequences: every function/struct/enum/trait has `isExported` undefined +(file node `false`; extractVariable's `?? false` → `false`); `isStatic` +undefined; **no isConst means `const_item`/`static_item` extract as kind +`'variable'`, never `'constant'`** (see extractVariable below). + +## tree-sitter.ts branches (anchors as of `ce0ae30`) + +### visitNode dispatch — what each top-level rust node hits + +| Node | Branch | Behavior | +|---|---|---| +| `function_item` (top level) | functionTypes, tree-sitter.ts:994 → extractFunction:1517 | not inside class-like at file scope → extractFunction; **first line of extractFunction (1522): if getReceiverType returns a value → extractMethod instead** (this is how impl-block fns become methods — impl_item does NOT push a scope) | +| `function_signature_item` | same | in a trait body (trait pushed, class-like) → extractMethod; no `body` field → no body walk | +| `struct_item` | structTypes:1059 → extractStruct:1869 | `body` field required: **unit structs `struct Unit;` have no body → NO node minted** (1876, `record_declaration` exemption is C#-only). Tuple structs have body `ordered_field_declaration_list` → extracted. `field_declaration` children make NO nodes (rust has no fieldTypes) — visitNode recurses into them and finds nothing | +| `enum_item` | enumTypes:1064 → extractEnum:1914 | body `enum_variant_list`; `enum_variant` children → extractEnumMembers:1958 — **`name` field path: one `enum_member` node from `getChildByField(node,'name')`, then return** (variant payload bodies `B(u32)` / `C { x }` are never walked). Non-variant children (e.g. `attribute_item`) → visitNode (no-op) | +| `trait_item` | interfaceTypes:1054 → extractInterface:1834 | kind `'trait'` (interfaceKind); extractInheritance sees the `trait_bounds` child (see below); body `declaration_list` children visited with the trait pushed → fn items become methods with QN `Trait::name` via nodeStack | +| `impl_item` | dedicated branch:1273-1276 → extractRustImplItem:5690 | emits the implements back-reference (below); **skipChildren stays false** → the `declaration_list` is then visited normally by the loop at 1295 (that's how impl members are reached; impl pushes NOTHING on the nodeStack) | +| `mod_item` | no branch | falls through → children visited. **No `module` node, no qualifiedName prefix** — items inside `mod tests { }` index as if at file scope. (frameworks/rust.ts:329 looks for `kind === 'module'` nodes and finds none from extraction — its `nodes[0]` fallback carries module resolution.) | +| `use_declaration` | importTypes:1209 → extractImport:3170 | import node + root-module ref (hook, above) + `emitRustUseBindingRefs` (3217-3219, rust-only, below) | +| `const_item` / `static_item` (top level) | variableTypes:1098 → extractVariable:2538 | **generic fallback branch (2863-2881)**: kind = `'variable'` ALWAYS (no isConst); iterate DIRECT namedChildren; **every child of type `identifier` mints a node** — for `const MAX: u32 = OTHER;` the children are identifier(MAX), primitive_type, identifier(OTHER) → **TWO `variable` nodes, `MAX` and the phantom `OTHER`** (probed). A non-identifier value (call, literal, array, struct_expression) → one node. Nodes get docstring + isExported:false, NO signature (unlike TS/Go branches). skipChildren=true, then `scanFnRefSubtree` (1110) capture-only. **No instantiates/calls refs from top-level initializers** — the value is never walked as a body | +| `let_declaration` (top level) | variableTypes | only legal inside bodies, so effectively never taken (bodies don't route through extractVariable); it's in variableTypes for the fn-ref dispatch + shadow prune. A body `let` is plain recursion inside visitFunctionBody | +| `type_item` | typeAliasTypes:1071 → extractTypeAlias:2890 | no resolveTypeAliasKind → plain `type_alias` node. QUIRK: the alias-value ref walk (2976) reads `getChildByField(node,'value')` — rust type_item's field is **`type`**, not `value` → null → **a rust type alias emits NO reference to its aliased type** | +| associated `const_item` inside `impl` | variableTypes | impl pushes nothing → `!isInsideClassLikeNode()` is true → extracted as a FILE-level `variable` node (contains edge from the file), e.g. `impl Fields { const CAP … }` → variable `CAP`. PRESERVE | +| associated `const_item` inside `trait` body | variableTypes gate FAILS | trait is pushed (class-like) and `isClassScopeConstantAssignment` needs node.type `assignment` → false → **no node**, but the else-ladder falls through with skipChildren=false → the const's value expression IS visited (a call in it emits a `calls` ref from the trait node) | +| `associated_type` in trait, `macro_definition`, `attribute_item`, `extern_crate_declaration` | no branch | recursed, nothing extracted | +| `macro_invocation` (top level) | no branch in visitNode | recursed into token_tree (raw tokens — nothing matches). **Route macros are only extracted inside function bodies** (visitFunctionBody:5141) — a top-level `routes![…]` emits nothing | +| `struct_expression` | INSTANTIATION_KINDS:359, visitNode:1255 + body walker:5145 | extractInstantiation (below). In practice struct_expressions live in bodies | + +### Node creation, IDs, qualified names + +- `createNode` (1308): id = `generateNodeId(filePath, kind, name, startRow+1)` + = `` `${kind}:${sha256(`${filePath}:${kind}:${name}:${line}`).hex.slice(0,32)}` `` + (tree-sitter-helpers.ts:18). The FILE node id is the literal + `file:${filePath}` (tree-sitter.ts:509), NOT hashed. **Dedupe/self-checks + compare ID STRINGS** (same-(kind,name,line) collisions are routine — `node_ids` + vec pattern in every walker). +- endLine extension via resolveBody (1329) is a no-op for rust (no hook). +- contains edge from nodeStack top for every created node (1363). +- qualifiedName = nodeStack names joined `::` (buildQualifiedName:1447; + namespacePrefix is always empty outside C/C++). Methods with a receiver + override it: `composeReceiverQualifiedName` (1435) = `` `${receiverType}::${name}` `` + verbatim for rust (prefix empty → passes through, per the 1433 comment). +- File node: kind `file`, name basename, qualifiedName = filePath, endLine = + `source.split('\n').length`, isExported false. + +### extractFunction / extractMethod for rust (1517 / 1737) + +- extractFunction: receiverType present → extractMethod (1522). Name via + `extractName` → nameField `name` (identifier). No misparse hook. Node gets + docstring, signature, visibility, isExported:undefined, isAsync:false (bug + above), isStatic:undefined, returnType. Then extractTypeAnnotations, + extractDecoratorsFor (rust `attribute_item`s are SIBLINGS, not children, and + aren't `decorator`/`annotation`/`marker_annotation`/`attribute` types → **no + decorates refs for rust**, and the backward-sibling scan at 5013 stops at the + first attribute_item anyway). Push node, walk `body` field (block), pop. +- extractMethod (reached for impl fns + trait members): receiverType computed + again (1742). Gate at 1747: not class-like AND no methodsAreTopLevel AND no + receiver → back to extractFunction (trait members pass via class-like; impl + fns via receiver). extraProps.qualifiedName = `Type::name` when receiver + (1790). **Contains edge from the owner (1798-1813): only when receiver + present AND not class-like — finds the FIRST node in `this.nodes` with + `name === receiverType && filePath === this.filePath && kind ∈ + {struct,class,enum,trait}`. Source-order dependent: an impl ABOVE its struct + gets no contains edge. `impl Trait for Generic` (receiver=trait bug) links + to the TRAIT node if it's in-file.** Then type annotations, decorators + (no-op), body walk with the method pushed. +- **Nested `fn` inside an impl-method's body**: visitFunctionBody:5245 → + named → extractFunction → getReceiverType walks parents THROUGH the outer fn + to the impl_item → receiver found → extractMethod → a nested helper indexes + as a METHOD with QN `Type::inner` + contains edge from the type. PRESERVE. +- structs/enums/traits declared inside a body are extracted there + (5255-5275), contained by the enclosing function node. + +### extractCall (3684) — the rust paths + +Generic else-branch (4312+), `func = childForFieldName('function') ?? namedChild(0)`: + +1. `func.type === 'field_expression'` (method call `x.foo()`): property = + `field` field (`property` misses). receiver = object/operand/argument + fields → all null for rust → `func.namedChild(0)` (the `value`). + - receiver type in LITERAL_RECEIVER_TYPES (373) → emit NOTHING (#1230). + Rust members of the set: `string_literal`, `raw_string_literal`, + `integer_literal`, `float_literal`, `char_literal`, `boolean_literal`. + QUIRK: rust `array_expression`/`tuple_expression`/`struct_expression` + receivers are NOT in the set (it has `array`/`array_literal`, other + grammars' names) — `[1,2].len()` falls through to the bare-name path and + emits `calls` ref `len`. PRESERVE. + - receiver `identifier` (not in SKIP_RECEIVERS {self,this,cls,super}) → + `recv.method`. NOTE rust `self` is node type `self`, NOT `identifier`, + so `self.own()` skips this branch and lands on the fallthrough → bare + `own` (same net effect as SKIP, different path — probed). + - receiver `call_expression` + rust in the gate list (4413) → + chained-call re-encode: `innerFn = receiver.childForFieldName('function')`, + `innerCallee = text(innerFn).replace(/->/g,'.').replace(/\s+/g,'')`; + **rust re-encodes ONLY when `innerFn.type === 'scoped_identifier'`** + (4455) → `Foo::new().bar()` → ref `Foo::new().bar`; an instance chain + `x.foo().bar()` (innerFn field_expression) → bare `bar`. When not + re-encoding, calleeName = bare methodName. + - receiver anything else (`field_expression` 2-hop `v.field.method()`, + `parenthesized_expression`, `await_expression`, `self`) → bare + methodName (probed all four). +2. `func.type === 'scoped_identifier'` (4499) → calleeName = FULL text + (`Foo::new`, `m::helper2`, `std::mem::swap` — whatever the source spells, + whitespace included). +3. else → calleeName = raw func text: bare `helper` for identifier; + **`generic_function` (turbofish `helper::`) keeps the full + `helper::` text — unresolvable downstream, PRESERVE** (probed). + +Post-processing: the parenthesized-conversion regex (4530) can in principle +match `(Foo)(x)` shapes — rust parses a parenthesized callee as +`parenthesized_expression` so text starts `(` → regex CAN fire; harmless and +must match. Template-arg strip (4542) and cpp fn-ptr fan-out (4556) are +c/cpp-gated — NOT for rust. Finally one `calls` ref {callerId, name, line = +call startRow+1, column = call startColumn (UTF-16)}. Inner calls of a chain +are ALSO visited (the body walker recurses after extractCall), so +`Foo::new().bar()` emits BOTH `Foo::new().bar` and `Foo::new`. + +`extractCall` returns immediately when the nodeStack is empty — never the case +in practice (file node is pushed). + +### extractInstantiation — `struct_expression` (359, 4610) + +ctor = constructor/type/**name**(rust)/namedChild(0). Not +composite_literal/instance_expression → generic path: text; strip from first +`<`; then `lastDot = max(lastIndexOf('.'), lastIndexOf('::'))` → keep trailing +segment (`m::Widget { }` → `Widget`); trim; emit `instantiates` ref at the +struct_expression's position. Fires from visitNode (top-level expressions) AND +visitFunctionBody (5145). Top-level const/static initializers never reach it +(extractVariable skips walking — quirk noted above). + +### Rocket route macros — extractRustRouteMacro (5048), body-walker-only (5141) + +Gate: `this.language === 'rust'`; macroName = `node.namedChild(0)` (the +`macro` field identifier); name must be EXACTLY `routes` or `catchers` — a +scoped `rocket::routes![…]` has a scoped_identifier there whose text doesn't +match → skipped (PRESERVE). tokenTree = first namedChild of type `token_tree`. +fromId = nodeStack top. Walk `tokenTree.child(i)` (ALL children, anonymous +included): `identifier` tokens accumulate into `parts` (first one records +line/column); a `,` token flushes `parts.join('::')` as ONE ref +{referenceKind: **`references`**}; final flush after the loop (the closing `]` +is not a flush trigger — the trailing path flushes at end). Probed token +stream: `[ id :: id :: id , id ]` — `::` are anonymous and skipped by the +identifier/`,` switch. Consumed by `resolveRustPathReference` +(import-resolver.ts:1781). + +### emitRustUseBindingRefs (3451) — one `imports` ref per use binding + +Called from extractImport for every `use_declaration` (3217). Recursive +`collect(n, prefix)` over the declaration's namedChildren: + +- `identifier` → push `join(prefix, text)` (`join` = `prefix ? prefix+'::'+seg : seg`) +- `scoped_identifier` → push `prefix ? prefix+'::'+trim(fullText) : trim(fullText)` + (the FULL path text — `crate::m::Item`, `self::sub::read`) +- `scoped_use_list` → prefix' = join(prefix, trim(text of `path` field)); + recurse into `list` field (`?? namedChildren.find(type==='use_list')`) +- `use_list` → recurse each namedChild with same prefix +- `use_as_clause` → recurse the `path` field (`?? namedChild(0)`) — links the + SOURCE path, not the alias (probed: fields are path/alias) +- everything else (visibility_modifier, `use_wildcard`, bare `crate`/`self`/ + `super` nodes) → ignored + +Then per collected path: leaf = last `::` segment; skip if leaf ∈ +{self, super, crate, *} or empty; push {fromNodeId: file, referenceName: FULL +path, referenceKind:'imports', line/col of the collected node}. So +`use crate::m::{A, B as C, sub::D}` emits `crate::m::A`, `crate::m::B`, +`crate::m::sub::D` (plus the hook's root-module ref `crate` and the import +node named `crate`). + +### Inheritance — extractInheritance for rust (5291) + +Only ONE child type matters for rust nodes: **`trait_bounds`** (5515, on +trait_item — supertraits `trait Sub: Super + Display`). Per bound child: + +- `type_identifier` → name = text +- `generic_type` (`Deserialize<'de>`) → inner namedChild of type + `type_identifier` → its text +- `higher_ranked_trait_bound` (`for<'de> Deserialize<'de>`) → its + `generic_type` child's inner type_identifier, else its own direct + `type_identifier` +- **QUIRK, PRESERVE: `scoped_type_identifier` (`fmt::Debug`) matches NO case → + a path-qualified supertrait emits NOTHING** (probed: `trait Render: Base + + fmt::Debug` → only `Base`). + +Each yields an `extends` ref from the trait node at the bound's position. +Struct/enum extraction also calls extractInheritance; rust struct_item children +include `field_declaration_list` → the 5652 recursion descends, but rust +`field_declaration` always carries a `field_identifier` name so the Go +struct-embedding branch (5496) never fires. Verify with the torture fixture +anyway. + +### impl Trait for Type — extractRustImplItem (5690) + +- hasFor = any child (ALL children) with `type === 'for' && !isNamed` — plain + `impl Type { }` → return (no edge; getReceiverType handles member attachment). +- typeIdents = DIRECT namedChildren of type `type_identifier` | `generic_type` + | `scoped_type_identifier`; need ≥2 else return (v0.24.2 has `trait:` and + `type:` FIELDS, but the code deliberately uses positional filtering — + PRESERVE the positional logic). +- traitNode = FIRST, typeNode = LAST. traitName: scoped_type_identifier → + `source.substring(startIndex,endIndex)` (full `fmt::Display`); else + getNodeText. typeName: generic_type → inner type_identifier text (`Container`) + else text. +- targetId = `findNodeByName(typeName)` (5740): FIRST node in `this.nodes` + with that name and kind ∈ {struct, enum, class} — **NOT trait**, and + source-order dependent (the type must be defined EARLIER in the same file; + cross-file impls emit nothing). If found: push + {fromNodeId: **the TYPE's node id** (a back-reference), referenceName: + traitName (full path text), referenceKind:'implements', line/col of the + trait node}. + +### Type-annotation references (5752-6112) + +`rust` ∈ TYPE_ANNOTATION_LANGUAGES (5753). For every function/method: +extractTypeAnnotations (5788) walks (a) the `parameters` field subtree and +(b) the `return_type` field subtree with extractTypeRefsFromSubtree (6090), +emitting one `references` ref per **`type_identifier` leaf** whose text isn't +in BUILTIN_TYPES (5768). The set includes the rust primitives (`str bool +i8…u128 usize isize f32 f64 char`) — mostly redundant since rust primitives +parse as `primitive_type`, not `type_identifier` — plus cross-language rows +(`error`, `String` via the Scala block, `Int`/`Any`/…). Port the WHOLE set +verbatim: a rust `type_identifier` named `String` IS suppressed (Scala row), +while `Vec`/`Option`/`Box`/`Self` are NOT. QUIRKS, PRESERVE: + +- Generic parameters are emitted: `fn get(&self) -> &T` → ref `T`; + `Result` → refs `Result`, `Baz`, `E`. +- `-> Self` → ref `Self` (type_identifier, not builtin). +- `scoped_type_identifier` (`fmt::Formatter`) → only the inner + `type_identifier` leaf `Formatter` (the `path` identifier is not a + type_identifier); the ref is UNQUALIFIED. +- `where` clauses and `type_parameters` bounds are NOT walked (params + + return_type fields only; the type_parameters walk at 5863 is scala-gated). +- The trailing `type_annotation` child lookup (5873, and + extractVariableTypeAnnotation:6074 whose comment says "covers … Rust + `: Type`") is a NO-OP for rust — the grammar has no `type_annotation` node + (let/const types are direct `type` fields). Dead comment, no behavior. +- property_signature/method_signature branch (1283) — TS-only node types, + never rust. + +### Static-member refs, cpp-isms — NOT rust + +`rust` ∉ STATIC_MEMBER_LANGS (345) → extractStaticMemberRef no-ops (its call +in the body walker at 5218 must be a no-op in the walker too — cheap early +return). namespacePrefix, cppLocalFnPtrs, stack-construction, operator calls, +template strip: all c/cpp-gated, none apply. + +### Docstrings (tree-sitter-helpers.ts:95) + +`///` and `//!` are `line_comment` nodes; consecutive preceding named siblings +of the item accumulate (unshift → source order), then cleanCommentMarkers +strips `^\/\/[/!]?\s?` per line (multiline `gm` — the CRLF `^`-after-`\r` trap +from #1329 applies; use `js_multiline_strip` in docstring.rs). QUIRK, +PRESERVE: **an `attribute_item` between the doc comment and the item breaks +the sibling chain** — `/// doc` + `#[derive(Debug)]` + `struct Doc` → NO +docstring (probed; attribute_item is a named sibling and not a comment type). +DOCSTRING_WRAPPER_TYPES contains no rust wrappers → no climbing. Block +`/** */`-style (`block_comment`) is also accepted by the sibling scan and +`/*`-stripped. + +### Value-reference edges (398-931) — rust IS in VALUE_REF_LANGS (401) + +Port the full machinery (crib go.rs/tsjs): `CODEGRAPH_VALUE_REFS=0` kill; +MAX_VALUE_REF_NODES=20_000 caps BOTH the prune scan and each reader scan; +`isGeneratedFile` skip. + +- Targets (captureValueRefScope:735): created nodes of kind + constant/**variable** (rust consts are `variable` — still targets), name + length ≥3 AND `/[A-Z_]/` test, parent scope id starting `file:` (also + class:/module:/struct:/enum: — rust consts always land under file:). Count + per name in fileScopeValueCounts. +- Reader scopes: every function/method/constant/variable node. +- Shadow prune (803-878): DFS of the whole tree counting declarators of + target names — rust cases: `const_item`/`static_item` → bump + `childForFieldName('name')` (823-825); **`let_declaration`** (the shadow + source, 827) → left ?? `pattern` ?? namedChild(0); if the pattern is an + `identifier` bump it, else bump every namedChild of the pattern (tuple + patterns). bump() only counts `identifier`/`simple_identifier` nodes whose + text is a target. After the scan: `declCount > fileScopeCount` → target + deleted (a local `let MAX = …` shadows the file `const MAX`). +- Emission (880-930): per reader scope, DFS its node subtree (rust bodies are + children — the Dart/Pascal sibling pull at 891 is inert); each + `identifier` (also constant/name/simple_identifier — non-rust) whose text + maps to a target and target ≠ self-id and name ≠ scope's own name and not + yet seen → EDGE (not unresolved ref): {source: scopeId, target: targetId, + kind:'references', metadata:{valueRef:true}}, deduped per (scope,target). + +### Function-as-value capture (#756) — RUST_SPEC (function-ref.ts:217) + +idTypes={identifier}; dispatch: +`arguments`→args, `assignment_expression`→rhs(field `right`), +`field_initializer`→value(field `value`), `array_expression`→list, +`static_item`→varinit(field `value`), `let_declaration`→varinit(field `value`). +NO layers/unwrap/special/ungatedModes/addressOfOnly. QUIRK: **`const_item` is +NOT in the dispatch** — a `const TABLE: [fn(); 2] = [a, b];` captures via the +inner `array_expression`, but `const CB: fn() = handler;` captures nothing +(static_item does). Capture mechanics (function-ref.ts:408-597): + +- args/list: every namedChild is a candidate value. +- rhs: the `right` field, with the param-storage skip — if the LHS's last + identifier (`/([A-Za-z_$][A-Za-z0-9_$]*)\s*$/` on LHS text) EQUALS the RHS + text, skip (`o.cb = cb`). +- varinit: name/pattern field of type object_pattern/array_pattern/ + **tuple_pattern/struct_pattern** → skip whole container (destructuring); + else the `value` field. +- normalizeValue: bare `identifier` → candidate (NAME_STOPLIST drops + this/self/true/None/…). No unwrap → `&handler` (a rust `reference_expression`) + yields NOTHING — rust captures only bare identifiers. explicitRef = false + always (idTypes hit). +- Capture fires from visitNode:990 AND visitFunctionBody:5137 AND + scanFnRefSubtree (top-level initializers, halts at nested functionTypes, + depth ≤12). +- Flush gate (flushFnRefCandidates:639): generated-file skip; candidate name + must be in definedHere (same-file function/method NAMES) ∪ importedNames. + QUIRK, PRESERVE: importedNames admits `SIMPLE_NAME` (`/^[A-Za-z_$][A-Za-z0-9_$]*$/`) + or `QUALIFIED_IMPORT` with `.`/`\` separators only — **rust's `::`-separated + import refs (`crate::m::helper`) match NEITHER, so rust use-imports + contribute nothing to the gate** except single-segment ones (`use foo;` → + `foo`, and every root-module ref `crate`/`self`/`std`). Net: the rust fn-ref + gate is effectively "defined in this file". Survivors dedupe on + `${fromNodeId}|${name}` and push {referenceKind:'function_ref'}. + +### Misc shared paths + +- Import/refs positions: `line = startPosition.row + 1`, + `column = startPosition.column` — **UTF-16 code units** (textutil::col16), + as are `startIndex/endIndex` substrings and `.slice(0,100)` truncations. +- Refs carry NO filePath/language (the store denormalizes) — kernel wire + contract is exactly extractFromSource's return. +- `extract()` wraps everything: file node first, nodeStack=[fileId], no + packageNode for rust; flushFnRefCandidates then flushValueRefs at the end. +- Parse errors: the walker defers `has_error()` files (`defer:` signal); + wasm's error recovery is canonical. tree.delete()/source-release are + wasm-side concerns. + +## Frameworks that consume rust extraction artifacts (stay TS-side) + +`rustResolver` (resolution/frameworks/rust.ts) — detect: `Cargo.toml`. + +- **`extract()` (regex over raw source, runs in extractFromSource AFTER either + arm — NO port needed, but its INPUT contract must hold):** emits `route` + nodes with id `` `route:${filePath}:${line}:${METHOD}:${path}` `` (NOT + hashed), kind `route`, name `` `${METHOD} ${path}` ``, qualifiedName + `` `${filePath}::route:${path}` ``, language `rust`; plus one + `references` ref per handler FROM the route node (these framework refs DO + carry filePath+language — resolution/types' UnresolvedRef, unlike extraction + refs). Covers `#[get("/…")]` attributes (Actix/Rocket), Axum + `.route("/p", get(h))` chains, Actix builder `web::resource(...).to(h)`. +- **Extraction-side emissions the port MUST reproduce for rust resolution to + keep working:** (a) `emitRustUseBindingRefs`'s FULL-path `imports` refs and + (b) `extractRustRouteMacro`'s `::`-joined `references` refs — both consumed + by `resolveRustPathReference` (import-resolver.ts:1446/1781); (c) the + root-module `imports` ref that `resolveModule`/cargo-workspace mapping + resolves (module refs like `use foo;` → `src/foo.rs` / workspace crates). +- `cargo-workspace.ts` (path-aliases §) reads Cargo.toml manifests only — + untouched. + +## Gates (per plan §5, no exceptions) + +- **Torture fixture `torture.rs`** (+ CRLF variant, derived in-memory), pinning + at minimum: unit struct (NO node) / tuple struct / field struct; enum with + unit+tuple+struct variants; trait with supertraits incl. a SCOPED one + (`fmt::Debug` — dropped) + `function_signature_item` + default method + + associated type/const (no node; const value call attributes to trait); + inherent impl (methods, associated const → file-level `variable`); `impl + Trait for Type`; `impl fmt::Display for Type` (scoped trait name text); + `impl Generic` (generic-branch receiver); **`impl Trait for + Generic` (receiver = TRAIT bug)**; impl ABOVE its struct (no contains + edge); nested fn inside an impl method (becomes a method); `pub async fn` + (isAsync stays false); `-> Self` / `-> &Foo` / `-> Vec` / + `-> Result, E>` (returnType undefined) / `-> fmt::Result`; + turbofish call; `Foo::new().bar()` chain + instance chain `x.foo().bar()`; + `self.method()`; 2-hop `v.field.method()`; literal receiver `"x".len()` + (nothing); `m::helper()` scoped call; struct_expression plain + `m::Widget` + + inside fn args; use forms: single, grouped, `as` alias, nested group + path, `pub use`, wildcard (NO import node), bare `use foo;`; + `const X: T = OTHER;` (phantom second node) + static with array value; + file-scope const read + `let`-shadowed const (value-ref prune); fn-ref + shapes: `register(handler)`, `obj.cb = handler2`, `Widget { cb: handler }`, + `[cb_a, cb_b]`, `static CB: fn() = handler`, `let cb = handler`, tuple-let + skip; `routes![a::b::h1, h2]` + `catchers![x]` + `rocket::routes![…]` + (skipped) inside a body AND one at top level (skipped); doc comments incl. + `//!`, a `/* */` block, and the attribute-breaks-docstring case; a mod + with items (no module node, bare QNs). +- **Parity sweeps** (`scripts/kernel-parity.mjs`, order-sensitive full-object): + **ripgrep (small), tokio (medium), rust-analyzer (large)** — all three also + exercise heavy `pub use` re-export hubs and macro use. Then **full-init + dump-diffs byte-identical** (kernel arm vs `CODEGRAPH_KERNEL=0`, + `dump-graph.mjs`, cmp) on the same three. +- **Deferral-rate guard: default `--max-deferral 0.1` and expect FAR under it** + — rust is not macro-mangled C; parse-error incidence should sit in the + ts/java/py/go norm (0–0.42%). Double-digit deferral on a rust sweep means a + broken walker, not grammar reality (the c/cpp 0.5 exemption does NOT carry + over). +- Grammar-bump isolation: the vendored v0.24.2 wasm + `=0.24.2` crate pin land + FIRST with the full suite green (kernel-grammar-parity sha-matches parser.c; + crate + wasm move together or it fails). +- Suite green with `CODEGRAPH_KERNEL_EXPECT=1`; unit tests for the walker in + `__tests__/kernel-rustlang-parity.test.ts` (or folded into the existing + parity suites); changelog rides the existing kernel entry. +- `DEFAULT_ROUTED += rust` (kernel/index.ts:37) only after ALL of the above. +- Post-route perf sanity: remember decision §arch-2 — Cargo repos take the + decoded path (framework extract()), so measure the parse-loop, not the + raw-buffer transport. diff --git a/scripts/kernel-parity.mjs b/scripts/kernel-parity.mjs index ff926fd..552402f 100644 --- a/scripts/kernel-parity.mjs +++ b/scripts/kernel-parity.mjs @@ -48,7 +48,7 @@ if (paths.length === 0) { process.exit(2); } -const KERNEL_LANGS = new Set(['typescript', 'tsx', 'javascript', 'jsx', 'java', 'python', 'go', 'c', 'cpp']); +const KERNEL_LANGS = new Set(['typescript', 'tsx', 'javascript', 'jsx', 'java', 'python', 'go', 'c', 'cpp', 'rust']); const EXTS = new Map([ ['.ts', 'typescript'], ['.mts', 'typescript'], ['.cts', 'typescript'], ['.tsx', 'tsx'], ['.js', 'javascript'], ['.mjs', 'javascript'], @@ -58,6 +58,7 @@ const EXTS = new Map([ ['.c', 'c'], ['.h', 'detect'], ['.cpp', 'cpp'], ['.cc', 'cpp'], ['.cxx', 'cpp'], ['.hpp', 'cpp'], ['.hxx', 'cpp'], ['.metal', 'cpp'], ['.cu', 'cpp'], ['.cuh', 'cpp'], + ['.rs', 'rust'], // R7b ]); /** Collect candidate files. */ diff --git a/src/extraction/grammars.ts b/src/extraction/grammars.ts index 2c435b9..4d88bdc 100644 --- a/src/extraction/grammars.ts +++ b/src/extraction/grammars.ts @@ -297,6 +297,10 @@ const VENDORED_WASM_LANGS: ReadonlySet = new Set([ // the crates.io tarballs. `.metal`/`.cu` map to language 'cpp', so the // dialects ride the same (single, coherent) upgraded grammar. 'c', 'cpp', + // R7b (Rust kernel port prep): tree-sitter-rust v0.24.2 (77a3747), + // parser.c/scanner.c sha-matched against the crates.io tarball. Replaces the + // 2023-era tree-sitter-wasms build (ABI 14 → 15). + 'rust', ]); /** 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 2db49c7..9772ee7 100644 --- a/src/extraction/kernel/index.ts +++ b/src/extraction/kernel/index.ts @@ -48,6 +48,12 @@ const DEFAULT_ROUTED: ReadonlySet = new Set([ // scripts/kernel-parity.mjs --max-deferral). 'c', 'cpp', + // R7b (2026-07-20): parity swept 0-diff on ripgrep/tokio/rust-analyzer + // (2,108 files byte-parity) + full-init dump-diffs byte-identical. Rust + // deferral is ~0% on normal repos; token-macro-table sources (rust-analyzer's + // parser crates, 18%) error on BOTH arms — grammar-inherent, not a walker + // signal. + 'rust', ]); /** diff --git a/src/extraction/wasm/tree-sitter-rust.wasm b/src/extraction/wasm/tree-sitter-rust.wasm new file mode 100755 index 0000000000000000000000000000000000000000..d40458915bd851adb660e5cd7d552adf0aca9c2a GIT binary patch literal 1114303 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 mH