mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-19 05:58:01 +08:00
fix/embedding-cache-oom
5 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
932d937085 |
feat: add Zig language support (#1432)
* feat: add Zig language support Adds a Zig LanguageProvider grounded in @tree-sitter-grammars/tree-sitter-zig 1.1.2. The grammar's published peerOptional `tree-sitter@^0.22.1` is suppressed via an npm `overrides` entry that aliases the peer to the bundled `tree-sitter@0.21.x`; load-time smoke testing confirmed ABI compatibility. v1 capabilities: - .zig file detection + Prism syntax mapping - Top-level + nested function_declaration as Function/Method - struct/enum/union (anonymous in the grammar — owner name resolved from the enclosing variable_declaration in class/field/method extractors) - container_field as struct/union fields and enum variants - top-level const/var as Variable nodes - free + member call_expression as @call edges - @import("./foo.zig") local-file resolution; std and external packages return empty (no ghost edges) - pub keyword detection for export checking - no heritage hooks (Zig has no inheritance; queries never emit @heritage.*) Generic extractor changes (backward-compatible): - field-extractors/generic.ts and method-extractors/generic.ts: empty `bodyNodeTypes` falls back to the type declaration node itself as its own body container — needed because Zig's struct_declaration directly contains its container_field children. (The `extractOwnerName` hook this commit originally introduced now exists upstream; Zig just configures it.) Out of scope (deferred): - usingnamespace, build.zig.zon package graph, comptime/anytype - scope-resolution hooks (emitScopeCaptures, interpretImport, …): Zig is classified `experimental` and uses the generic fallback resolution path - cross-package imports (std, deps) Tests: - new fixture test/fixtures/sample-code/simple.zig - Zig describe block in tree-sitter-languages integration test - simple.zig added to parsing.test.ts fixture-existence list - Zig added to ingestion-utils detection unit test - Zig smoke case in parser-loader-abi.test.ts - Zig grammar registered in the grammar-literal validation gate * feat(zig): integrate build.zig.zon resolution + Union label from PR 1096 Ports the additive pieces of grgisme's standalone Zig provider PR (https://github.com/abhigyanpatwari/GitNexus/pull/1096) onto the rebased provider: - build.zig.zon `.path` dependency resolution for bare-name @import("pkg") (parseZigBuildZon / loadZigBuildZon in language-config.ts, resolveZigImportInternal in import-resolvers/zig.ts, wired through ImportConfigs.zigBuildZon). `.url` deps and repo-escaping paths return null cleanly. 13 unit tests. - `union(enum)` containers now produce `Union` nodes (not Struct): 'Union' added to ClassLikeNodeLabel + CLASS_LIKE_LABELS, the Zig query tags @definition.union, CONTAINER_TYPE_TO_LABEL maps union_declaration to 'Union'. The label was already plumbed graph-wide on main. - /^build$/ entry-point pattern (build.zig). - zig-basic lang-resolution fixture + resolvers integration test. Adapted to current main while porting: - labelOverride relabels container-nested fns Function → Method (mirrors isKotlinClassMethod); the structure phase no longer derives Method from the legacy method-extraction path for plain @definition.function captures. - IMPORTS/CALLS edges require scope-resolution hooks (emitScopeCaptures / interpretImport) since the legacy DAG removal; Zig does not implement them yet, so the integration test documents that with a skipped import-edge case. The resolver itself is wired into the resolver factory and becomes live when the hooks land. Not ported: named-bindings extractor (the legacy namedBindingExtractor API no longer exists) and the bespoke field extractor (the generic factory's extractOwnerName / empty-bodyNodeTypes hooks cover Zig). Co-authored-by: Garrett Griffin-Morales <grgisme@gmail.com> * feat(zig): scope-resolution hooks — IMPORTS and CALLS edges (Ring 3) Implements the registry-primary scope-resolution path for Zig, the prerequisite for cross-file edges since the legacy DAG removal. Adds the standard per-language stack under languages/zig/: - query.ts: scope query (containers as Class scopes, blocks, functions), declarations (container anchors placed on the container node itself so the def lands in its own Class scope and the name binding auto-hoists to the parent — populateClassOwnedMembers needs the class-like def among the class scope's ownedDefs), @import statements (#eq?-gated builtin), parameter/constructor type bindings, and call/constructor reference sites. The grammar is required lazily (optionalDependency). - captures.ts: emitZigScopeCaptures — groups query matches, drops the plain-variable group for container/import bindings (their dedicated rules bind the name), and relabels container-nested fns @declaration.function → @declaration.method (labelOverride parity). - interpret.ts: namespace-kind imports (const x = @import("…")) and type bindings — self-parameter convention marks the receiver, Zig sigils (*, ?, [], error unions, const) stripped from type names while dotted qualifiers (mod.T) are preserved for Case-3 namespace-prefix receiver dispatch. - simple-hooks.ts: parameter bindings stay function-local (Go rationale), local-over-import merge precedence, bounds-check arity (always 'unknown' today — no synthesized arity metadata). - scope-resolver.ts: emit-side wiring; build.zig.zon threads through loadResolutionConfig into the same resolveZigImportInternal the legacy resolver config wraps. fieldFallbackOnMethodLookup off (statically typed). Registered in SCOPE_RESOLVERS. The resolvers integration test un-skips the import-edge case and gains CALLS assertions: free call (main → helper) and receiver-bound method dispatch through a namespace-qualified constructor (var p = pioneer.Pioneer{…}; p.tick() → main → tick). * fix(zig): Union is class-like + missing-grammar warning (review pass) Self-review findings on the Zig branch: - scope/walkers.ts `isClassLike` and finalize-algorithm's CALLABLE_OR_TYPE_LIKE did not include 'Union': a `union(enum)` container's methods got no ownerId from populateClassOwnedMembers, so method dispatch on union receivers silently dropped. Widened both sets; the zig-basic fixture gains a Tag method + a CALLS assertion (main → isEnergy) that fails without the widening (verified by reverting). - optional-grammars.ts now lists tree-sitter-zig with an npm `probe` (it is an optionalDependency, not vendored): users with .zig files and no prebuild get the standard one-line stderr warning instead of a silently degraded index. - Deduplicated the container-method predicate: `isZigContainerMethod` + ZIG_CONTAINER_TYPES now live once in languages/zig/captures.ts and feed both the provider labelOverride and the scope-capture relabel. - README language matrices: Zig row now claims Type Annotations, Constructor Inference, and Config (build.zig.zon) — all true since the scope-resolution hooks landed. * fix(zig): anchor @declaration.variable to the binding identifier `(variable_declaration (identifier) @declaration.name)` matched EVERY identifier child of the node, so `const first = target;` also declared a phantom local named `target` in the enclosing block. That phantom shadowed the real function for later references (and starved callable-value-flow seeds of a target). The `.` anchor pins the pattern to the first named child — the bound name. Regression test in resolvers/zig.test.ts pins the capture set. * feat(zig): callable-value-flow captures + main's per-language conformance gates Post-rebase catch-up: since this branch forked, main added three "every registered language must appear here" tests. Each needs a Zig entry: - callable-value-flow (#2522): Zig now emits `@callable-flow.*` facts via `synthesizeCallableFlowCaptures` (ZIG_CALLABLE_CAPTURE_OPTIONS in zig/captures.ts). tree-sitter-zig's `call_expression` carries arguments as direct children with no wrapper node, which the shared helper could not decompose, so this adds a language-neutral `extractCallArguments` hook (mirror of `extractFunctionParameters`; `undefined` = shared path). Zig joins the provider matrix as 'matrix' with a real assign→copy→ argument→invoke case. - external-import-conformance (#2953): `@import("std")` beside a decoy `src/std.zig` resolves to nothing; the decoy stays reachable via the relative spelling. Zig holds the property (no suffix fallback), so it is a case, not a KNOWN_GAPS entry. - import-target-index-reuse contract (#2909): membership-probe-only fixture (minimumScans: 0, same shape as Rust). * fix(zig): address gitnexus-check review findings One commit per the bot's list so each item is easy to check off: - parser-loader: Zig row gains `userSkippable: true`, so `GITNEXUS_SKIP_OPTIONAL_GRAMMARS` (=1 or a list naming `zig`) disables it at analyze time like swift/dart/kotlin — as `optional-grammars.ts` already documented. Covered in parser-loader-skip-optional.test.ts. - export-detection: `zigExportChecker` stops at the first declaration it reaches. A non-`pub` fn inside `pub const T = struct {…}` was reported exported because the walk continued up to the wrapper. - import-resolvers/zig: a `.path = "."` dep normalizes to '' and no longer grows a leading slash (`/src/main.zig` could never match). - language-config: build.zig.zon parsing strips `//` comments string-aware (a `//` inside `.url = "https://…"` survives) and matches braces while skipping string literals, so a commented-out `.path` cannot declare a dep and a `}` in a comment/string cannot truncate the block. - method-extractors/configs/zig: the leading `self` receiver is excluded from `parameters` (Rust parity). Fixing that exposed a worse bug: the `parameters` node is a plain child of `function_declaration`, not a `parameters:` field, so `childForFieldName('parameters')` was always null and every Zig method had no parameters, no receiver and `isStatic: true`. One `zigParameterList` helper now feeds all three readers. - variable-extractors/configs/zig: container (`struct`/`enum`/`union`) and `@import` bindings are skipped via the same predicate the scope captures use (`isZigContainerOrImportBinding`), instead of the comment merely claiming they were. - tree-sitter-languages.test.ts: the "missing grammar" case now forces the absent-binding path through the loader's runtime opt-out on a fresh module instead of passing vacuously when the package is installed. New: test/unit/zig-extractors.test.ts (exports, receiver/parameters, variable guard); zig-import-resolver.test.ts gains the `.` dep, comment and brace cases. The `createFieldExtractor` heads-up needs no change: the added branch is unreachable for every existing config (none has empty `bodyNodeTypes`). * fix(zig): address second gitnexus-check review pass - import-resolvers/zig: `..` above the repository root now returns null instead of aliasing a same-named root file (`../bar.zig` from `main.zig` is not `bar.zig`); the stale "extension is stripped and re-added" comment is corrected to what the code does. - variable-extractors/configs/zig `extractType`: read the `type:` field only. The positional fallback returned the INITIALIZER of `const f = target;` as its type and gave up on compound annotations (`*Foo`, `?[]const u8`). The comment claiming 1.1.2 has no `type` field on variable_declaration was wrong (verified by AST dump) — and it is what led the review to suspect the callable-flow `extractAssignment` callback, which was already correct for `extern var f: T;`. - receiver detection: only a FIRST parameter named `self` is the receiver. `emitZigScopeCaptures` tags first-position parameters (`@type-binding.first-parameter`), `interpretZigTypeBinding` requires the tag as well as the name, so `zigReceiverBinding` no longer turns `fn f(a: u32, self: T)` into an instance method. - resolvers/zig.test.ts: both suites `describe.skipIf(!zigAvailable)` (Swift/Dart pattern) — the grammar is an optionalDependency. - tree-sitter-languages.test.ts: the Zig parsing case gates on `isLanguageAvailable` instead of a catch-all `return`, so an installed grammar that fails to load fails the test; comment no longer calls Dart and Swift npm optionalDependencies (they are vendored). - test/helpers/literal-collectors: `DIR_LANG` gains `zig`, so literals under `languages/zig/**` are validated against the Zig grammar alone rather than against every grammar. - walkers.ts `isShapeLike` doc: Union IS included (via isClassLike, wired by Zig's union member container); Typedef remains the only deferred one. - language-config `ZigBuildZonConfig.pathDeps` doc: values are the raw `.path` strings; the resolver normalizes. Tests: zig-import-resolver (+1), zig-extractors (+3). * fix(zig): address third gitnexus-check review pass - language-config `parseZigBuildZon`: the `.dependencies = .{` header and the per-entry `.<name> = .{` headers are matched only OUTSIDE string literals (per-offset string mask + `matchZonHeader`). A `.name` or `.description` value spelling `.dependencies = .{ .fake = .{ .path = … } }` used to be taken as the block and returned the fake dep instead of the real top-level one. - import-resolvers/zig: an absolute import (`@import("/foo.zig")`) returns null. The path walker skipped every empty component, so the leading `/` vanished and `/foo.zig` resolved as importer-relative `src/foo.zig` — an in-repo edge for an import Zig rejects as outside the module path. - tree-sitter-languages.test.ts: the Zig parsing case gates on the PACKAGE being installed (`createRequire().resolve`, minus a deliberate `GITNEXUS_SKIP_OPTIONAL_GRAMMARS` opt-out), not on `isLanguageAvailable`, which is false for absent AND for installed-but-broken bindings — so a load failure (ABI mismatch, bad export) now fails the test instead of skipping it, as the comment already claimed. - walkers.ts `isShapeLike` doc: `Union` sits in `isClassLike` because that is the label set the ownership walkers consult, not because unions inherit — Zig has no inheritance and no heritage hooks. The previous wording ("inheritance-capable owner") said otherwise. - Not re-fixed (already addressed in the second pass, findings carried over): "receiver = any parameter named self" — `interpretZigTypeBinding` only sources a first-position parameter as `self`; `zigReceiverBinding`'s doc now states that invariant. "`DIR_LANG` has no zig entry" — it does. Extended one level out: `BASENAME_LANGS` (`zig.ts`) and `PREFIX_LANGS` (`ZIG_`) map to the Zig grammar too, so extractor configs and the export-detection set are validated against Zig alone. That immediately caught a dead `childForFieldName('parameters')` in method-extractors/configs/zig `zigParameterList` (there is no such field; the named-child lookup was already the one doing the work) — removed. Tests: zig-import-resolver (+2: absolute path, header inside a string); both fail on the previous code. * fix(zig): address fourth gitnexus-check review pass - language-config: `parseZigBuildZon` only accepts a `.path` that is a DIRECT field of a dependency entry. Nested blocks inside the entry body are blanked (string-aware, offsets preserved) before the `.path` regex runs, and a match starting inside a string literal is rejected, so `.dep = .{ .url = "…", .meta = .{ .path = "x" } }` no longer becomes a path dep. Regression test in zig-import-resolver.test.ts (fails on the previous code). - tree-sitter-languages test: the "grammar is absent" case now drives the loader's real `source.load()` catch branch — `node:module` is stood in with a `createRequire` whose require throws MODULE_NOT_FOUND for `@tree-sitter-grammars/tree-sitter-zig` and delegates everything else — instead of the `GITNEXUS_SKIP_OPTIONAL_GRAMMARS` opt-out, which has its own test. It also asserts the opt-out flag is NOT set and that other grammars still load (non-fatal optional failure). Not re-fixed: - "Return type is read from the wrong tree-sitter field": tree-sitter-zig 1.1.2 has NO `return_type` field on function_declaration — the type after `)` is the `type` field (AST dump: `builtin_type "i32" field=type`; the proposed `childForFieldName('return_type')` is null for every function). A pin test in zig-extractors.test.ts asserts both the grammar fact and that `returnType` is extracted (`void`, `!*Counter`). - "`DIR_LANG` has no zig entry": it does (added in the first pass and answered again in the third); the finding is carried over unchanged. * fix(zig): address fifth gitnexus-check review pass - tree-sitter-languages test: the Zig "functions, structs, enums, and imports" case now asserts the `import.source` capture for `const std = @import("std");` (the fixture's only import), so a query change that drops Zig import matching fails it instead of passing unchanged. Not re-fixed: - "Return type is read from the wrong tree-sitter field": carried over from the fourth pass unchanged. tree-sitter-zig 1.1.2 has no `return_type` field on function_declaration; the return type IS the `type` field, and the pin test added in the fourth-pass commit (zig-extractors.test.ts, `childForFieldName('return_type')` is null, `returnType` = `void` / `!*Counter`) proves it. - "`DIR_LANG` has no zig entry": carried over unchanged for the third time; the entry exists since the second-pass commit. * feat(zig): export fn visibility, opaque containers, named test blocks, member ownership Ports the parts of upstream PR #305 (closed, unmerged) that our Zig provider lacked, plus two gaps found while porting. - `export fn` / `export var` (C-ABI linkage, never `pub`) are exported; the pub/export predicate is now shared by the export checker and the method/variable extractors' visibility (`hasZigVisibilityKeyword`). - `const H = opaque { … }` is a Struct-labelled container (it may own methods, never fields) in both the structure queries and the scope query; ZIG_CONTAINER_TYPES is the single source for the extractor configs. - `test "name" { … }` blocks are Function nodes named by the string node WITH quotes, so `test "add"` beside `fn add` cannot merge onto Function:<file>:add; `test_declaration` joins FUNCTION_NODE_TYPES and the Zig method config names it in the enclosing-function walk, so calls inside a test attribute to the test. Anonymous `test {}` and decl-tests `test add {}` are scopes without a node (an empty-name hook result stops the walk instead of falling through to the identifier of the function under test). - Empty container bodies (`struct {}`, `opaque {}`) no longer mint a nameless Property: tree-sitter-zig 1.1.2 recovers them as a container_field with a MISSING identifier; #not-eq? guards in both queries and the field extractor drop it. - Owner walk (`findEnclosingClassInfo`): an anonymous container bound by the enclosing `variable_declaration` takes the binding identifier, same shape as the Go `type_spec` branch. Before this NO Zig member had an owner — zero HAS_METHOD / HAS_PROPERTY edges for Zig. Not ported from #305, deliberately: `builtInNames` (a bare-name call-site drop filter; `alloc`/`free`/`append`/`print` are the most common user method names in Zig and `std.*` receivers are already external via the import binding), `usingnamespace` (removed in Zig 0.15), `@cImport`, `build.zig` ignore, and the web-app changes. * test(zig): absent-grammar case owns GITNEXUS_SKIP_OPTIONAL_GRAMMARS The loader parses the opt-out variable lazily once per module copy, so under `GITNEXUS_SKIP_OPTIONAL_GRAMMARS=zig` (or `all`) — a supported way to run — the fresh loader took the opt-out branch and the "not the opt-out path" assertion failed before the absent-binding path ran. Clear the variable for the fresh module and restore it in `finally`, instead of returning early: the branch stays exercised in every environment. Verified: fails on the previous code under `=zig`, passes with and without the variable now. * fix(zig): declare the Union relation pairs — analyze aborted on any union `gitnexus analyze` exited 1 on every Zig repository that declares a `union` (including the zig-basic fixture itself): the member-ownership commit made `union_declaration` a MEMBER_OWNER, so HAS_PROPERTY / HAS_METHOD edges are emitted FROM a Union node, but `Union` was not in LINKABLE_LABELS, so the schema's scope-bridge cross product never generated a `FROM Union` pair and LadybugDB rejected the edge (`labelPair: "Union|Property"`). Resolver tests stayed green because they never write to the DB. - `Union` joins LINKABLE_LABELS (also bridges `Tag{…}` constructor references); the three hand-written `→ Union` target pairs move to the generated half, per the STRUCTURAL_PAIR_DDL rule. - structural-pair-coverage gains an optional-grammar corpus with zig-basic (`Union|Property`, `Union|Method` sentinels), skipped when the grammar is absent. - Rust `union_item` note updated: the three gates it cited are widened. Note for reviewers: the DDL fingerprint changes (#2808), so existing indexes are rebuilt on next analyze. * feat(zig): resolve path deps through the dep's build.zig and src/root.zig The bare-name resolver only knew `src/<name>.zig` and `src/main.zig`. `zig init` has written `src/root.zig` for libraries since 0.12, so the default library layout never resolved. Now: the root the dep's own build.zig declares (`b.addModule("<name>", .{ .root_source_file = b.path("…") })`, name-matched module first), then src/root.zig, src/<name>.zig, src/main.zig. `normalizeZigDepPath` is shared by the loader and the resolver. * feat(zig): Const/Variable defs, member imports, receiver typing, generic type constructors Coverage gaps found by indexing idiomatic Zig against the branch: - Const / Variable nodes: ZIG_QUERIES had no @definition.const / @definition.variable, so `pub const VERSION`, error sets and type aliases were absent and zigVariableConfig never ran. Rules are gated on the literal `const` / `var` keyword — tree-sitter-zig 1.1.2 parses statement assignments (`x = 5;`, `x += 1;`, `_ = expr;`) as keyword- less `variable_declaration`s, and the scope query minted a phantom local per assignment and one `_` per discard. Container and @import bindings are skipped via `shouldSkipDefinitionCapture`. - Imports: `const X = @import("x.zig").X` (named / alias), `const X = ns.X` where `ns` is an @import binding of the file (promoted to a named import), and `pub usingnamespace @import(...)` (wildcard, with `expandsWildcardTo`). All three lost the file-level IMPORTS edge. - Receiver typing: `var x: T = undefined` / decl literals `const x: T = .init()` (annotation), `var c = T.init()` / `mod.T.init()` (call return), `List(u8){}` (instantiation literal); `normalizeZigTypeName` drops the comptime argument list. - Generic type constructors `fn List(comptime T: type) type { return struct {…}; }`: the returned container is a Struct/Union/Enum named after the fn, owns its members (HAS_METHOD / HAS_PROPERTY), binds in the module scope beside the Function def, and is emitted ahead of it so a named import binds the type. - `export` vs `pub`: `visibility` is now `pub`-only (Zig-module fact); `isExported` keeps `pub|export` (visible outside the unit, as C's external linkage). `export fn` without `pub` is not reachable from other Zig files. - Extractor configs share `zigContainerName`; ast-helpers' owner walk learns the type-constructor shape. Tests: zig-idioms fixture (10 resolver cases), extractor/interpret unit cases for each rule. * fix(zig): address sixth gitnexus-check review pass - Windows absolute `.path` deps (`C:\x`, `C:/x`) return null from `normalizeZigDepPath` like POSIX ones; a `/`-only check let them through as repo-relative. - `parseZigBuildZon` accepts the `.dependencies = .{` header only at brace depth 1 (a direct field of the file's `.{`), so a same-named field nested in an earlier struct cannot hijack the block. - `importsExecuteWhereWritten: false` on the provider: `@import` is compile-time name lookup (as C `#include`, Rust `use`); a body-level `@import` is no longer marked `runsOnlyWhenCalled`. - Namespace imports record the MODULE as `importedName` (`zigModuleNameOf`: last path segment without `.zig`), per the shared contract; the local handle stays `localName`. - Keyword-less `<ident> = @import(…)` (`_ = @import("x.zig")` in a test block) is a `side-effect` import: file edge, no binding. Only `const`/`var` declarations bind a name or feed alias promotion. - `extractZigFunctionName` doc: an empty name is falsy, so the enclosing- function walk skips the test node and continues to the File; it does not "end" there. Not re-fixed: "DIR_LANG has no zig entry" — carried over for the fourth pass in a row; `test/helpers/literal-collectors.ts` has had `zig` in `DIR_LANG` (line 91) and `BASENAME_LANGS` since the second-pass commit. Regression tests: absolute-path spellings, nested `.dependencies` decoy, namespace/side-effect interpretation, function-scoped `@import` not deferred (all four fail on the previous source). * fix(zig): address seventh gitnexus-check review pass - The scope query's `@import` binding rules are keyword-gated (`"const"` / `"var"`, first-child anchored) like every other binding rule, and the keyword-less `<ident> = @import(…)` statement has its own `@import.side-effect` rule. Tree-sitter queries cannot express "no keyword child", so that rule also matches the keyword shapes and `emitZigScopeCaptures` drops those (they are the binding rules' matches). Behaviour is unchanged from the sixth-pass fix — the existing side-effect test covers it — the query text now carries the guard the finding asked for. Not re-fixed: "DIR_LANG has no zig entry" — fifth pass in a row; `test/helpers/literal-collectors.ts` has had `zig` in `DIR_LANG` since the second-pass commit. Left for a human reviewer to close. * fix(zig): resolve @import of the repo's own build.zig modules (F3) Bare-name imports were resolved through build.zig.zon path deps only, so the module a repo's ROOT build.zig declares for itself — `b.addModule("lightpanda", .{ .root_source_file = b.path("src/lightpanda.zig") })`, imported by name from 378/567 Lightpanda files — never produced an IMPORTS edge, and nothing reached through `lp.X` resolved. A repo with a build.zig but no build.zig.zon got no resolution config at all. - language-config: `parseZigRootModules` (static scan of the root build.zig: `addModule("<name>", …root_source_file = b.path("<p>.zig")…)`, and `createModule`/`addModule` bindings named via `addImport("<name>", m)` or `.imports = &.{ .{ .name, .module = m } }`; generated / `.url` / computed modules are skipped) → `ZigBuildZonConfig.rootModules`. - `loadZigBuildZon` → `loadZigBuildConfig`: reads the zon AND the root build.zig; null only when neither contributes. - resolver: root modules are consulted before path deps; std/builtin/root still never resolve. - fixtures: zig-idioms gains a Lightpanda-shaped root module (+ decoy `addOptions().createModule()`); new zig-rootmodule (build.zig, no zon). Corpus (Lightpanda): IMPORTS 3014→3389 (378 edges to src/lightpanda.zig, was 0), CALLS 13885→13989, ns.f() 79.3%→83.3%, param.m() type=ns-qualified 43→46/417. * fix(zig): import every @import in expression position; resolve @import("x").f() Both query sets only saw `@import` as the value of a const/var or under `usingnamespace`, so an @import in any other position produced no file edge: Lightpanda's `pub const Interfaces = .{ @import("a.zig"), … }` registration table (288 modules), call arguments (`CounterEnum("size", @import("ArenaPool.zig").BucketSize)`), comparison operands (`JsApi == @import("x.zig").JsApi`) and member-call receivers (`try @import("dump.zig").root(...)`) — 417 of 3,401 in-repo import pairs had no IMPORTS edge, and the 80 inline-receiver calls resolved 0 times. Scope query: a catch-all `@import.inline` rule matches every `@import` builtin; `emitZigScopeCaptures` drops the ones a binding rule (or the keyword-less side-effect rule) already claimed (by string-node id) so a bound import is never doubled, emits the rest as side-effect imports once per distinct source per file, and binds a member-call receiver as a namespace import whose local name is the builtin's own text — the `@reference.receiver` text on that call is identical, so the shared namespace-receiver lookup (Case 1) resolves the member in the imported module. ZIG_QUERIES: the three variable_declaration/usingnamespace-anchored `@import` rules collapse into the same single builtin rule (the structure phase only skips import matches; one match per builtin keeps tree-sitter-languages' exact-capture assertion intact). Lightpanda corpus (zig-corpus-check, before → after): IMPORTS 3014 → 3426, in-repo pairs missing 417 → 5 (4 under a default-ignored `cache/` dir, 1 a commented-out import the census regex counts), `@import(..).f()` 0/80 → 72/80 (the 8 left are `@import("root")` and non-import builtins the census mislabels), CALLS 13885 → 13959; every other line unchanged. * feat(zig): model file-structs — a file with top-level fields is a Struct named after the file In Zig every file is a struct; one that declares top-level fields is an instantiable type whose name is the file stem (`Page.zig` declares `Page`, `@typeName` agrees), and its top-level `fn`s taking `self` are its methods. Lightpanda spells 413 of 567 files this way and, before this, `page.getArena()` on a `page: *Page` parameter resolved 23 of 993 times (2.3 %) — `impact` on `Page.getArena` reported 0 callers for 159 call sites, and 2,395 top-level fields were ownerless Property nodes. Definition phase: `((source_file (container_field …)) @definition.struct)` + the class extractor names it from the file path (`zigContainerName(source_file, filePath)`); top-level fns/fields are owned through the new `LanguageProvider.resolveFileTypeOwner` hook (consulted by `findEnclosingClassInfo` when the walk reaches the tree root, and by the method/field extractors' owner lookup) — ids become `Method:<file>:Page.getArena#0` / `Property:<file>:Page.session` with HAS_METHOD / HAS_PROPERTY edges. Scope phase: `emitZigScopeCaptures` emits a Class scope over the whole file (same range as the Module scope, nested under it — the pair `canParentScope` already admits) plus a Struct def anchored on it; member NAME bindings are hoisted back to the Module scope by `zigBindingScopeFor` so `Page.init()` (namespace member) keeps working, while ownedDefs stay in the Class scope so `populateClassOwnedMembers` stamps the owner. The file-level `const Page = @This();` alias no longer mints a Const (it would shadow the Struct); `@This()` aliases in type position (`self: *SigHandler` in Sighandler.zig, nested `Self`) are rewritten to the container name so receivers resolve. A namespace import of a `.zig` file gets a NAMED twin of the file stem so `x: *Page` in the importer binds the type as well as the module. Shared, additive: `resolveFileTypeOwner` hook; `filePath` threaded to `ClassExtractionConfig.extractName` / `extractOwnerName`; nameless `definition.struct` passes `getLabelFromCaptures` like `definition.class` already did (extractor synthesizes the name). Lightpanda corpus (zig-corpus-check): param receivers typed by a file import 23/993 → 993/993; `self.m()` 99.2 → 100 %; annotated locals 11.6 → 23.2 %; CALLS 13,885 → 15,689; HAS_METHOD 1,477 → 8,003; ownerless Property 2,395 → 276; Function/Method 8,378/1,518 → 2,562/7,330; no row regressed. Fixture `zig-filestruct` (Page/Session/Sighandler/util) + unit tests pin the shape, the stem naming, the alias rewrite, the namespace twin and the unchanged namespace-file behaviour. * test(zig): expression-position import case sees the file-struct type twin * fix(zig): stop reading member calls `x.f(arg)` as direct calls named `f` tree-sitter-zig spells `field_expression` as `object:`/`member:`; the shared callable-flow reader only knew `property`/`field`/`method`, so every Zig member call collapsed to a DIRECT call named after the member and the solver fanned each argument out to every same-named callable (4,761 cap warnings on Lightpanda, `Global.deinit -> Global.deinit` self-loops through `pub const release = deinit;`). Shared (grammar-neutral, receiver-gated): - `memberParts` also reads `member` (only C/C++ `offsetof_expression` and JS `class_body` expose that field, without a receiver field). - A member call is a field-stored-callable invoke only when a MEMBER store (`o->run = handler`, `self.f = target`) or a declared callable-typed field is visible — a same-named plain binding no longer gates it. - `direct-callee-name` requires a direct designator: `.init(x)`, `' '.join(x)`, `string.Join(x)` name no callee to seed by simple name. Zig: formals are numbered without the leading `self`, so `r.run(target)` joins `cb` and yields `Runner.run -> target`. Goldens for python/csharp regenerated: the only drift is the dropped `direct-callee-name` on `' '.join(...)`, `text.strip().ljust(...)`, `string.Join(...)`. Lightpanda: cap-warnings 4761 -> 2, cvf self-loops 10 -> 0, CALLS 13885 -> 13857 (28 removed, all callable-value-flow: 10 self-loops, 17 same-name fan-out, 1 lost `on -> TypeErased.start`; +1 correct `Arena.alloc -> allocator`). * fix(zig): bind container field types so `self.field.m()` resolves (F5) A container's field types were never bound on its Class scope: the scope query's `container_field` rule captured only the name, and `emitZigScopeCaptures` synthesized no `@type-binding.field` group. The compound resolver reads member types from that scope (`typeOfMemberOnClass` → `classScope.typeBindings.get(field)`), so `self.session.name()`, `self.counter.incr()` — Lightpanda's dominant cross-object call shape — resolved 9 of 2803 times (0.3 %). - query.ts: capture `type: (_)? @declaration.field-type` on `container_field` (enum variants have none). - captures.ts: per typed field, push a `@type-binding.field` group (name = field, type = the type text, `@This()` aliases rewritten to the container name like parameter types); anonymous inline containers are skipped. The binding lands on the container's Class scope — the file's Class scope for a file-struct — since `zigBindingScopeFor` hoists only declaration names. - query.ts/captures.ts: `const page = self.page;` / `var s = self.session;` one-level field aliases become `@type-binding.alias` bindings whose "type" is the RHS path; the resolver's member-alias branch re-resolves it as a receiver chain. Import aliases (`const Counter = counter.Counter;`) are dropped — they are named imports. - interpret.ts: `@type-binding.field` → 'annotation', `@type-binding.alias` → 'assignment-inferred' (an annotation on the same binding wins). Corpus (Lightpanda, zig-corpus-check): self.field.m() 11/2803 (0.4 %) → 1433/2803 (51.1 %); ident.m() bound=local-field-access 29/1902 (1.5 %) → 307/1902 (16.1 %); chain.m() 146/5022 (2.9 %) → 642/5022 (12.8 %); CALLS 15838 → 18066. self.m() / free f() / ns.f() unchanged. Tests: unit (zig-extractors) — one @type-binding.field per typed field with sigils stripped and aliases rewritten; the binding hosted on the container's Class scope (file-struct: the file's Class scope, not Module) with the written spelling as declaredSpelling; field aliases bound to the RHS path and never for import aliases. Integration (zig-idioms `holder.zig`, zig-filestruct `Page.zig`): `viaField → incr` ×2 into counter.zig, `viaAlias → get/twice`, `sessionName → name` / `sessionLabel → name` into Session.zig. All fail without the change. The optional-payload capture `if (self.opt) |c| c.incr()` is not asserted (F6). * fix(zig): `pub const X = @import(…)` at file scope republishes X (reexportsName) Lightpanda's `lightpanda.zig` is one long list of `pub const Arena = @import("Arena.zig");`, and most files name their types through it (`const lp = @import("lightpanda"); const Arena = lp.Arena;`, `arena: *lp.Arena`). The scope side treated those bindings as plain imports of the hub file, so the hub never published the names it re-exports and a third file's `const Arena = lp.Arena;` (promoted to a named import of `Arena` from the hub) found nothing. `emitZigScopeCaptures` now marks named/alias import groups whose declaration is a file-level `pub const` — the `@import(...).X` form, the alias promotion `pub const Bar = ns.Bar`, and the file-struct type twin of `pub const Arena = @import("Arena.zig")` — and `interpretZigImport` sets the shared contract's `reexportsName: true` on them (the Python `__init__.py` shape, consumed by `buildReexportClosures`). Private and fn-local bindings stay unflagged. Not covered here: a receiver ANNOTATED with the dotted hub path (`arena: *lp.Arena`) — Case 3 of the receiver-bound pass looks the member up with `findExportedDef`, which only sees locally declared names; following re-exports there is a shared change left for a follow-up. * fix(zig): type receivers through `const X = <type expr>;` aliases (F7) `const LocalAlias = Local;`, `const T2 = Thing;` (alias of an alias/import) and `const B = util.List(u8);` (an INSTANTIATED generic type constructor) were plain `@declaration.variable` bindings, so `LocalAlias.mk()`, `var l = LocalAlias.mk(); l.go()`, `T2.make()`, `B.init()`, `B{}` and `var x: B` all typed nothing (review repro r3-flow b1..b9; Lightpanda: `pub const Proto = HtmlElement;` x68, `const Allocator = std.mem.Allocator` x104, `pub const KeyIterator = GenericIterator(...)`, fn-local `const R = ...(...)`). Model: a `@type-binding.alias` binding of the alias NAME to the value's type text — Rust's `let x = y` / JS's `const B = Foo`, source 'assignment-inferred' — NOT a TypeAlias def. Reasons: (1) the shared machinery already chains typeBindings (`followChainedRef` in the extractor, `followChainPostFinalize` after propagation), so `var l = LocalAlias.mk()` and `var x: B` reach the target through the alias with no new shared code; (2) nothing shared follows a `TypeAlias` def to its target — `isShapeLike` only makes the alias itself a member owner (TS object-type aliases) — so a relabel would have needed language-named shared code; (3) graph node ids are UNCHANGED: every alias stays `Const:<file>:X`. `normalizeZigTypeName` already drops the comptime arguments, so `util.List(u8)` binds `util.List` and resolves through the namespace import (Case 3). The identifier / member shapes also take `var` (`var cur = orig; cur.go()` — the cursor idiom, same binding as Rust's `let x = y`). Heuristic, stated as such: a CALL value is kept only when the callee's last identifier is TitleCase (Zig's naming convention for types), because the grammar cannot tell `util.List(u8)` from `util.makeThing()` and the latter belongs to the call-return rules; the call-return group is dropped for the same TitleCase shape so the two never race on match order. Import bindings (`const Stack = @import("x.zig").Stack`), promoted namespace-member aliases, enum/decl literals (`.foo`) and the `type:` annotation of `var b: T = undefined;` are excluded. Not done: the two-hop `pub const bridge = js.Bridge(T); bridge.accessor()` chain. `js.Bridge` is a Function that RETURNS `bridge.Builder(T)` (a call, not a container), so the alias binds `js.Bridge`, Case 3 finds a Function with no members in js.zig, and Case 3b is skipped for a namespace head. Following that hop needs a namespace-member return-type route in shared code (or a Zig `resolveQualifiedReceiverMember` hook that re-implements member lookup without the model); left for a follow-up. Corpus (Lightpanda, `harness/zig-corpus-check.mjs`): CALLS 15838 -> 16051 (+213, 0 removed), `ident.m() bound=local-alias` 6/63 -> 36/63, `local-call` 159 -> 176, `module/unknown` 110 -> 116, `local-other` 271 -> 273; `self.m()`, `free f()`, `ns.f()` unchanged or up. * fix(zig): one alias rule set — F7's alias rules subsume F5's field-access alias rules * fix(zig): type locals through try/catch/orelse, return types and payload captures F6 of the gitnexus-check review. Three gaps in the value flow that types a local receiver, all measured on Lightpanda: 1. `@type-binding.call-return` needed the `call_expression` as the DIRECT value child, so `const p = try Page.init(…)` (2,551 sites), `… catch return` (410) and `… orelse return` typed nothing. The rule is now one keyword-gated declaration match; `emitZigScopeCaptures` unwraps `try`, `catch`, `orelse` and parentheses (`zigUnwrapValue`) and decides what the value types (`zigCallReturnTypeOf`): a module-level receiver still names the type (`Counter.init()` → Counter, Rust `Foo::new()`); a free call binds the callee name (`makeThing`); a member call on a fn-LOCAL receiver (parameter / local / payload — Zig forbids shadowing, so "declared in the fn" is exact) binds the compound `node.asElement()` the shared resolver walks to the method's return type — instead of typing `el` as `Node`. A TitleCase callee (`List(u8)`) is a type constructor and binds nothing. 2. No `@type-binding.return` existed. `fn make() !*Thing` now binds `make ↦ Thing` in the enclosing scope (Module for free fns, the container's Class scope for methods, where the compound resolver reads it). Builtins, `type`, `@TypeOf(…)` and comptime type parameters (`?*T`) bind nothing; `@This()` / `Self` returns name the container. `normalizeZigTypeName` now strips the error union BEFORE the payload's sigils, so `Allocator.Error!*Page` → `Page` (it used to leave `*Page`). 3. Payload captures had no binding at all. `populateZigRangeBindings` (registered as `populateRangeBindings`) types `for (items) |it| / |*it|`, `for (items, 0..) |it, i|`, `if (opt) |v|`, `if (call()) |v|`, `while (it.next()) |x|` from the SUBJECT's written type minus one layer (`[]T` element, `?T` payload) — declining when the layer is not visible (`ArrayList(T)`) — and the same projection for `const t = items[i]` / `opt.?` / `ptr.*`. `catch |err|` and `switch` prongs are skipped. Corpus (Lightpanda, gate before → after): CALLS 15838 → 17853; `ident.m() bound=local-try/catch/orelse` 11/1298 → 584/1298; `local-call` 159/1282 → 398/1282; `payload` 82/1085 → 233/1085; `other-recv:call_expression` 5/991 → 457/991; `local-other` 271 → 364; `self.m()` 100 %, `free f()` 97.5 %, `ns.f()` 83.4 % unchanged; nothing down. Not covered: `const t = ns.f()` (a namespace fn's return type across files — Case 3 has no path from a namespace head to a callable's return binding), and expression receivers (`items[i].run()`, `o.?.run()`). * fix(zig): resolve leftover fixture merge markers (Page.zig) * fix(zig): reconcile F6 value inference with F7 aliases and F5 field bindings - A fn-local TitleCase receiver (`const R = generic.List(u8); var l = R.init();`) is a type alias (F7), not a value local: `R.init()` names the type `R` like `Counter.init()` does at module level, so `l` chains R → util.List → push. F6's local-receiver rule now excludes TitleCase heads. - The F6 unit helper only collects the value-inferred / return kinds it owns; F5 field and F7 alias bindings for the same names are asserted in their own suites. * fix(zig): give function-local and anonymous containers an identity (F8) `const R = struct {…}` declared inside a fn (Lightpanda's reflection.zig has ~20, one per builder) all collapsed onto one `Struct:<file>:R` with one `R.get`; anonymous containers (`std.sort.pdq(…, struct { fn lessThan … } .lessThan)`, `const byte_size = struct { fn it … }.it;`, `?struct { min, max }` field types) had no identity at all, so their fns were OWNERLESS Methods (`Method:<file>:lessThan#3`) that collided across a file. `zigContainerName` now yields the graph IDENTITY on both phases: - function-local named: `<enclosing callable>$<name>` — `Reflect.string$R` (Java local-class `$` chain; `populateClassOwnedMembers` leaves it whole); - anonymous: `<host>$<ordinal>` — `build$1`, `Outer$1`, `Page$1` (javac's `Outer$1` numbering per host, in source order); - a `test` host is keyed `test@L<line>` (its string does not survive the class extractor's qualified-name normalization). `zigContainerBindingName` keeps the spelling code writes (`R`) for scope bindings and `@This()` alias rewrites (`@declaration.binding-name`). Structure phase: bare `(struct|enum|union|opaque_declaration)` rules mint the local/anonymous nodes via the class extractor; `shouldSkipDefinitionCapture` keeps exactly one rule per container (`zigContainerAnchor`); a new grammar-neutral `resolveContainerTypeOwner` provider hook lets the shared owner walk name a container from context, so `Method:<file>:Reflect.string$R .get#0` and its HAS_METHOD source agree by construction. Scope phase: the wrapper group splits name/binding-name for locals and anonymous containers get synthesized `@declaration.<kind>` defs (`is-synthetic`). Lightpanda: ownerless Methods 14 → 0, fns without a node 55 → 0, ownerless Properties 276 → 5, HAS_METHOD 8003 → 8074, HAS_PROPERTY 7275 → 7403, CALLS 15838 → 15857, Struct 1905 → 2199; no resolution bucket dropped. * fix(zig): re-add the implicit receiver on the call side so both method-call spellings reach the callback formal `extractFunctionParameters` sliced the leading `self` off the formals, which lined up `r.run(target)` (target@0 ↔ cb@0) but lost the explicit spelling `Runner.run(&r, target)` (&r@0, target@1 ↔ cb@0): the callback never joined its formal and `run → target` was missing (PR #1432 review by koriyoshi2041). Formals are numbered once per function while the receiver differs per call shape, so the fix lives in `extractCallArguments`: keep `self` as formal 0 and prepend the receiver as actual 0 when the callee is a member call on a VALUE receiver — chain head is a fn-local name that is not TitleCase, the same value-vs-type rule F6 uses. Namespace / type / decl-literal receivers (`Runner.init(cb)`, `helpers.apply(cb)`, `List(u8).init`, `.init(cb)`) get no prepend. Known residual gap, documented: a module-level value receiver (`global_runner.run(cb)`) is not fn-local and still misses. Tests: the F2 integration case now asserts both spellings plus a namespace call; the unit contract pins `self@0, cb@1` and the per-call actual index. * fix(zig): address eighth gitnexus-check review pass - Named dependency modules: `parseZigBuildModuleRoots` scanned `addModule("<name>", .{ … })` with a `[^}]*` regex, so a nested field before `.root_source_file` (`.imports = &.{ .{ … } }`) ended the match at the inner `}` and demoted the module to an unnamed fallback — the first exe/lib root in the file then answered `@import("<name>")`. The named lookup now walks the balanced `addModule(…)` argument list (same scanner as `parseZigRootModules`, comment-stripped, string-aware); the unnamed fallbacks are unchanged. Regression test in `zig-import-resolver.test.ts`. - Type-position `@import`: `var x: @import("m.zig").T = undefined;` was read as an import binding of `x` on both sides — the query rules match the `type:` child like a value, and `isZigContainerOrImportBinding` scanned every named child — so `x` was never declared and became a named import of `T`. The helper now skips the `type:` field, and `emitZigScopeCaptures` drops binding-rule matches whose `@import` sits in the annotation (`isZigTypePositionImport`) without claiming the source, so `x` binds as a variable and the file edge survives as a side-effect import. Regression tests in `zig-extractors.test.ts` (variable extractor + scope captures). - Union in the class-capture skip guard: the parse-worker's inline class-like predicate lacked `Union`, so a `Union` definition bypassed `shouldSkipClassCapture` unlike every other `ClassLikeNodeLabel`. Added the label; no Zig behavior changes (Zig defines no skip hook), so no test. - File-owned method ids in `findEnclosingFunctionId`: the arity lookup used `findEnclosingClassNode` while the owner came from the file-owner aware `cachedFindEnclosingClassInfo`, so a Zig file-struct's top-level fn produced `Method::Page.get` without the `#<arity>` suffix. It now uses `findEnclosingClassNodeOrFileOwner`, the definition-phase lookup. Consistency fix only: `ParseWorkerResult.calls` / `.assignments` (the sole consumers of this id) are merged but not read since #942 — CALLS edges come from the scope pipeline, whose ids were already right — so no observable graph change and no test. Not re-fixed: - `test/helpers/literal-collectors.ts` `DIR_LANG` has no `zig` entry (raised for the seventh time): the entry exists (`zig: SupportedLanguages.Zig`, added by the second-pass commit), so `languages/zig/**` literals are already validated against the Zig grammar alone; documented in the PR body since the fifth pass. * fix(zig): keyword-gate the constructor type-binding rules The three `@type-binding.constructor` rules (`const p = T{…}`, `mod.T{…}`, `List(u8){…}`) matched any `variable_declaration` with an identifier and a `struct_initializer`, keyword or not — and tree-sitter-zig 1.1.2 parses a re-assignment `p = T{…};` (and `_ = T{…};`) as the same node type. So an assignment minted a constructor binding for `p` in its own block, and one for `_`. Zig's static typing makes the extra binding redundant (`p` already carries its type from its declaration: annotation, constructor or inferred value), so it cost little, but it declared nothing and stood out against every other binding rule (`@declaration.variable`, the import rules, the call-return rules), which are keyword-gated for exactly this shape. Split each rule into `"const" .` / `"var" .` variants, like the call-return rules. Regression test in `zig-extractors.test.ts`: `p = T{…}`, `q = mod.T{…}`, `l = List(u8){}` and `_ = T{…}` after their declarations yield only the three declaration bindings (fails on the previous query). The zig, callable-value-flow, grammar-literal and tree-sitter-languages suites are unchanged. Raised twice by gitnexus-check (passes on 2026-08-18 12:42 and 12:56). * fix(zig): re-baseline the callable-flow capture fingerprints, keep `await f<T>(x)` a direct callee The `benchmarks (GITNEXUS_BENCH)` CI job gates two capture fingerprints that this branch's shared callable-flow change (3e62b99a) moved without re-baselining: `bench/python-scope/baseline-fingerprint.txt` and four languages in `bench/scope-capture/baselines.json` (csharp, cpp, typescript, kotlin). Both `--check` runs pass on origin/main and failed on this branch; every other language matched its baseline on the same run. Drift, verified by dumping the canonical matches on both trees: - csharp / kotlin / python: exactly the intended change — a MEMBER call (`string.Join(x)`, `.forEach { }`, `' '.join(x)`, `.ljust(w)`) no longer carries `direct-callee-name`; the argument fact is unchanged. - cpp: `choice.select(1)` (cpp-deleted-overload) drops an INDIRECT invoke + its synthetic `@reference.call.free` that were gated only by the same-named free `select` binding; the site is a genuine method call already captured as `@reference.call.member`. capture_groups_fp 4605 -> 4601. - typescript: `await svc.verify<T>(x)` (member) and `initializer()(cb)` (call-of-call) drop the name as intended. But `await verifyToken<T>(x)` — a DIRECT call — lost it too, because tree-sitter-typescript parses `await f<T>(x)` as `call_expression(function: await_expression(f), type_arguments, …)` and the new direct-designator gate saw an await_expression, not `f`. `wrappedExpression` now unwraps `await_expression` (no named field, so the field-based unwrap missed it), restoring parity with main for the direct spelling while the member spelling stays nameless. Regression test added; it fails without the unwrap on both assertions. Gates run locally: scope-capture --check (15 languages), python-scope --check, import-target --check, tsc, eslint, prettier, the callable-flow / golden / tripwire / resolver test files (33 files), full suite with coverage (82.9/71.5/89.1/86.4 vs 26/23/28/27 thresholds). * test(zig): fail CI when the optional Zig grammar is absent `@tree-sitter-grammars/tree-sitter-zig` is an optionalDependency, so every Zig suite gates on `isLanguageAvailable(Zig)` and the ABI load-smoke accepts a clean load failure for an optional grammar. Both are the right contract for a platform with no prebuild, and together they leave a hole: if the grammar never installed on any CI runner, this PR would merge with all eight Zig resolver suites plus the structure-phase suite reported green-by-skip, having never executed the native Zig parser once. Close it with the `GITNEXUS_REQUIRE_FTS` idiom already used for the FTS suites. `GITNEXUS_REQUIRE_ZIG=1` declares "this runner has a prebuild, the grammar MUST be here", and a missing grammar becomes a failure instead of a skip. tree-sitter-zig@1.1.2 publishes prebuilds for {darwin,linux,win32}- {x64,arm64}, so the flag is set on two required jobs that all run on covered platforms: the sharded ubuntu `tests` job and the three-OS `abi-assert` job. - test/helpers/optional-grammar.ts: the registry mapping a language to its require-variable, plus `describeGrammarPresence`, a presence assertion that FAILS when required-but-absent. Deliberately a separate test rather than flipping the suites from skip to fail: a skipped suite reports success, so only a failing test can turn "Zig never ran" into a red job. - parser-loader-abi.test.ts: the optional exemption is revoked for a language the environment declares required, so an ABI-broken Zig binding fails the smoke instead of passing as a clean absence. - optional-grammar-gate.test.ts: pins the two ways the gate could silently never fire — reading a variable name CI does not set, or accepting a value CI does not write. Nothing changes for a run that leaves the variable unset: local runs and any future prebuild-less platform still skip. Verified both directions -- `GITNEXUS_REQUIRE_ZIG=1` alone: 149 passed, 0 skipped; with `GITNEXUS_SKIP_OPTIONAL_GRAMMARS=1` forcing the grammar away it fails 2 tests with an actionable message; with the skip flag but no require flag it is green-by-skip exactly as before. Addresses the test-only blocker in the gitnexus-check review of #1432. * fix(zig): type the optional-grammar gate by grammar key, not language The gitnexus-check finding on parser-loader-abi.test.ts:155 is right, and none of the gates caught it: `tsconfig.json` includes only `src/**/*`, so neither `tsc --noEmit` nor CI typechecks the test tree, and vitest strips types without checking them. Confirmed with a scoped tsc run over the file: `error TS2345: Argument of type 'string' is not assignable to parameter of type 'SupportedLanguages'`. Not fixed with the proposed `key as SupportedLanguages` cast, which would assert something false: `listGrammarSources()` yields one row per SOURCES entry, including variants like `typescript:tsx` that are not enum members. `isOptionalGrammarRequired` now takes the grammar KEY it is really given, and the registry keeps a `satisfies Partial<Record<SupportedLanguages, string>>` so every key we write is still pinned to a real language. Two new cases cover the failure mode the type error was pointing at — a registry key that can never match what the ABI smoke passes, leaving the gate configured-looking and permanently inert: every OPTIONAL_GRAMMAR_ENV key must be a key `listGrammarSources()` yields and must be marked optional there, and an unregistered variant (`typescript:tsx`) must not be required even with the variable set. Same blind spot, two more latent errors in files this PR adds, both fixed: `Parser.Language` is not an exported member (use the `setLanguage` parameter type, as parser-loader-abi.test.ts already does), and the `ParsedImport` filter did not narrow the union, so `localName` was read through a `!` on an arm that has no such property — now a type predicate. The one remaining error under the same probe, in `resolvers/callable-value-flow.test.ts:319`, predates this branch (authored 2026-07-17, on main) and is left alone. structural-pair-coverage's optional-grammar case switches from `it.concurrent.each` to `it.concurrent.for`: only `for` passes the test context as a second argument (`each`'s callback is `(...args: T[])`), and that context carries the dynamic `skip()` the per-language gate calls. Behaviour is unchanged — grammar present: 10 passed; grammar forced away: 9 passed, 1 skipped. Whole test tree typechecking is a separate, much larger job: the same probe over `test/**` minus fixtures reports 734 pre-existing errors across the repo. Out of scope here. * fix(zig): address tenth gitnexus-check review pass - `normalizeZigDepPath`: normalize backslashes BEFORE the absolute-path check. A UNC dep (`\\server\share\dep`) used to slip past the check and normalize to the repo-relative `server/share/dep`; root-relative `\dep` had the same hole. Both now return null. Regression case added to the absolute-spellings test with the files those misreadings would resolve. - `bindPayloads`: a pointer capture `for (pages) |*p|` now records `*Page` (declaredSpelling) instead of `Page` — the `*` is an anonymous payload child before the identifier. Method dispatch is unchanged (`rawName` strips the sigil), but a deref projection `const q = p.*;` now sees the pointer layer. New fixture fn `viaPtrCaptureDeref` + assertion; fails on the previous code (verified by stashing the src fix). - `optional-grammar-gate.test.ts`: renamed the `typescript:tsx` case — the key IS a registry row; what makes it inert is the missing gate entry. Now also asserts a key no registry yields. - `structural-pair-coverage.test.ts`: header updated — ten tables (not eleven) are absent from every rule's target side; `Union` left the set when Zig made it linkable. - `language-classification.ts`: doc comment now names zig in the experimental set (added after Ring 1). Not re-fixed (invalid findings): - "owner-hook contract wired to an undeclared variable": stale-diff read — `findEnclosingClassInfo` declares `resolveFileTypeOwner` / `resolveContainerTypeOwner` as optional parameters (ast-helpers.ts:905, 917) and parse-worker threads them at every call site; tsc compiles clean. - "optional Zig grammar added unconditionally to the parsing fixture suite": the cited block only `fs.readFile`s the committed fixture file to assert it is non-empty — no parser or grammar load is involved. * fix(scope-resolution): mark construction-site CALLS edges in reason (opt-in), enable for Zig PR #1432 human review, item 2: a Zig struct literal `T{ .f = x }` (no parens) is modelled as a CALLS edge to the type — the Rust `T { .. }` / Go `T{}` shape — and nothing on the edge told it apart from an invocation (`get_next_spawn → SpawnRequest` from seven `return SpawnRequest{ … }`). `ScopeResolver.markConstructionSites` (default off): when set, the edge emitted for a `callForm === 'constructor'` site gets ` (constructor)` appended to its reason, in both emit paths — `local-call (constructor)` / `import-resolved (constructor)` in the free-call fallback and `scope-resolution: call (constructor)` in the reference bridge. The Zig resolver opts in. `Reference` gains an optional `callForm`, copied from the site by `buildReference`, so the bridge can see the form. Why `reason` and not a property or edge type: relationships carry no arbitrary properties, a new column changes the relation DDL and moves SCHEMA_FINGERPRINT, and `reason` is the channel the IMPLEMENTS `-pointer` receiver form already uses. Why opt-in: the unsuffixed strings are a pinned contract asserted verbatim by the other language suites (php/cpp constructor calls expect exactly `import-resolved`); every non-Zig edge stays byte-identical. Tests: `references-to-edges-call-form.test.ts` pins both vocabularies and the default-off behaviour; `zig.test.ts` asserts `Reflect.string → Accessor` / `Reflect.url → Accessor` carry `local-call (constructor)` next to a plain invocation, and that every marked edge targets a Struct. * feat(zig): track qualified struct literals (`mod.T{…}`) as construction sites PR #1432 re-test (issue comment on |
||
|
|
8864a0290c | feat: add Dart language support (#204) | ||
|
|
1afe9166aa |
feat: language-aware code intelligence — symbol resolution, MRO, constructor discrimination (#238)
* feat: add Method Resolution Order (MRO) with language-specific rules
Implement full MRO computation for multi-language inheritance hierarchies:
- HAS_METHOD edges: Class→Method ownership edges emitted during parsing
(both worker pool and sequential fallback paths)
- Method signatures: extract parameterCount and returnType from AST nodes
- C# heritage fix: distinguish EXTENDS vs IMPLEMENTS for base_list captures
using symbol table lookup + I[A-Z] naming heuristic fallback
- MRO processor (Phase 4.5): walks inheritance DAG, detects method-name
collisions across parents, applies language-specific resolution:
- C++: leftmost base class in declaration order wins
- C#/Java: class method wins over interface default
- Python: C3 linearization with cycle detection
- Rust: no auto-resolution (requires qualified syntax)
- Default: first definition in BFS order wins
- OVERRIDES edges emitted for resolved method collisions
- KuzuDB schema: Method table extended with parameterCount/returnType;
dedicated CSV writer and COPY query for 10-column Method rows
- MCP tools: updated Cypher examples for HAS_METHOD, OVERRIDES, diamond
72 tests across 5 test files covering MRO resolution, HAS_METHOD edges,
method signature extraction, C# heritage resolution, and integration
tests across C#/Rust/Python/TS/Java/C++.
* feat: add scope-based symbol resolution replacing raw lookupFuzzy
Introduces a shared 3-tier resolveSymbol function used by both
heritage-processor and call-processor:
1. Same-file (lookupExactFull — authoritative)
2. Import-scoped (filtered by ImportMap — high confidence)
3. Global fuzzy (first match — low confidence fallback)
Adds lookupExactFull to SymbolTable returning full SymbolDefinition
with type info needed for heritage Class/Interface disambiguation.
* refactor: tighten symbol resolution — Tier 3 refuses ambiguous matches
- lookupExactFull now O(1) via direct SymbolDefinition storage in fileIndex
(shared object references with globalIndex — zero additional memory)
- Added resolveSymbolInternal() preserving { definition, tier, candidateCount }
for test assertions and logging
- Tier 3 now returns null when multiple global candidates exist instead of
arbitrary allDefs[0] — a wrong edge is worse than no edge
- call-processor: renamed fuzzy-global → unique-global, removed dead branch
- 12 new tests: tier assertions, ambiguous refusal per language family,
heritage false-positive guard, O(1) shared reference verification
* fix: critical language support bugs in import resolution and MRO
Phase 5 critical fixes from all-language analysis:
- Python: add relative_import query capture (PEP 328) — `.models`, `..utils`
were silently dropped, producing zero ImportMap entries
- Rust: extract prefix from grouped imports `crate::module::{A, B}` — brace
groups previously failed resolution entirely
- Swift: use normalizedFileList for Windows path compatibility in module
import resolution (matches Go's resolveGoPackage pattern)
- MRO: fix c_sharp → csharp language name mismatch (enum is 'csharp'),
add Kotlin to C#/Java resolution rules (class method wins over interface)
* feat: add strict multi-language integration tests + fix C/C++ import resolution
Add 32 integration tests across 6 language fixtures (TypeScript, C#, C++,
Java, Python, Rust) with exact toBe/toEqual assertions validating heritage
edges, import resolution, and trait implementations.
Fix C/C++ import resolution bug where dot-to-slash conversion mangled
include paths (e.g. "animal.h" became "animal/h"). Now skips conversion
for C/C++ languages which use actual file paths in #include directives.
* fix: language-gate heritage heuristic, add Swift extension heritage, handle Rust grouped imports
- Gate I[A-Z] naming heuristic to C#/Java only (was firing for all languages)
- Swift unresolved types default to IMPLEMENTS (protocol conformance is the norm)
- Add tree-sitter query for Swift extension protocol conformance (extension Foo: Protocol)
- Handle Rust top-level grouped imports (use {crate::a, crate::b}) in both import loops
- Add 4 new heritage-processor tests (TypeScript refusal, Swift default, Swift Tier 1)
* feat: add Go struct embedding heritage + PackageMap optimization
Add Go struct embedding detection (anonymous fields → EXTENDS edges) via
new tree-sitter heritage query with named-field filtering in both
parse-worker and heritage-processor paths.
Implement PackageMap optimization for Go cross-package resolution:
replace O(N) file-level ImportMap expansion with directory-level suffix
matching (Tier 2b in symbol resolver). Graph IMPORTS edges are preserved
via addImportGraphEdge split.
Remove overly broad @definition.type from GO_QUERIES that was
double-matching structs/interfaces as TypeAlias nodes, breaking Tier 3
unique-global resolution.
Add Go fixture (go-pkg) with Admin→User embedding, cross-package calls,
and 7 integration tests covering structs, functions, imports, calls,
and heritage edges.
* test: add Kotlin heritage integration tests
Adds a kotlin-heritage fixture and 7 integration tests validating
class inheritance, interface implementation, JVM-style import
resolution, and symbol-table-driven EXTENDS/IMPLEMENTS disambiguation
via Kotlin delegation specifiers.
* feat: extract resolvers, add PHP tests, ambiguous tests for all languages
- Extract language-specific resolvers from import-processor.ts into
resolvers/ directory (P7): jvm, go, csharp, php, rust, standard, utils
- import-processor.ts reduced from 1412 to 711 lines (50% reduction)
- Add comprehensive PHP integration tests: PSR-4 imports, traits, enums,
heritage edges, method calls, MRO overrides
- Add ambiguous symbol resolution tests for all 9 languages verifying
correct disambiguation via import chains
- Split monolithic lang-resolution.test.ts (1080 lines) into 9 per-language
files under test/integration/resolvers/ with shared helpers
* feat: update integration tests to include resolver tests for multiple languages
* fix: address code review — schema gap, Rust impl name, Property OVERRIDES
Bugs fixed:
- Add 13 missing FROM/TO pairs in RELATION_SCHEMA for HAS_METHOD edges
(Class/Interface/Struct/Trait/Impl/Record to Method/Constructor/Property)
- Fix findEnclosingClassId to pick implementing type for Rust
impl Trait for Struct blocks (was picking trait name)
- Exclude Property nodes from MRO OVERRIDES collision detection
- Change MRO language fallback from typescript to unknown
Tests added:
- Unit: Property OVERRIDES exclusion (2 tests), Rust impl Trait for
Struct name resolution (2 tests), schema HAS_METHOD pair coverage
- Integration: no OVERRIDES targets Property nodes across all 9 languages
- PHP fixture: added shared $status property to both traits to create
real collision scenario for Property OVERRIDES exclusion test
Documentation:
- OVERRIDES edge direction (Class to Method), Go return type gap,
BFS first-reach heuristic limitation
* feat: harden CALLS-edge resolution — Phase 0 validation
- Fix same-file confidence (0.85 → 0.95) to correctly outrank import-scoped (0.9)
- Fix Tier 1 overload preservation: use globalIndex filter instead of fileIndex lookup
- Add callable-kind guard: refuse CALLS edges to Interface and Enum symbols
- Fix Kotlin countCallArguments: handle call_suffix → value_arguments nesting
- Fix Kotlin extractFunctionName: add simple_identifier to fallback search
- Strictly type findParameterList and countCallArguments (remove all `any`)
- Add arity-based call resolution integration tests for 9 languages
- Add unit regression tests for Interface/Enum CALLS refusal
* chore: remove C# build artifacts from fixtures
* feat: add call-form discrimination and ownerId to symbol table (Phase 1)
Add inferCallForm() and extractReceiverName() to distinguish free/member/constructor
calls at the AST level across all 9 languages. Add ownerId field to SymbolDefinition
linking Method/Constructor/Property to their owning class. Includes 36 unit tests
and member-call integration tests for all 9 languages (132 tests, 0 failures).
* feat: constructor/struct-literal resolution across all languages (Phase 2)
Add constructor discrimination to CALLS-edge resolution: new Foo(),
User{...} struct literals, and C# primary constructors now resolve to
Constructor/Class/Struct/Record nodes instead of being filtered out.
Queries: new_expression (C++), object_creation_expression (PHP),
composite_literal (Go), struct_expression (Rust), primary constructor
and implicit_object_creation_expression (C#).
Relaxes global tier in collectTieredCandidates to pass all candidates
through filterCallableCandidates, allowing kind/arity narrowing to
disambiguate at lower confidence.
* feat: receiver-constrained resolution with integration tests for all 9 languages
Add receiver-type filtering (Phase 3): when a member call like `user.save()`
has a known receiver type from TypeEnv, filter candidates by ownerId to
disambiguate methods with the same name across different classes.
Key changes:
- call-processor: build per-file TypeEnv, pass receiverTypeName to resolveCallTarget
- parse-worker: extract receiverTypeName from TypeEnv in worker thread
- resolveCallTarget: new step D filters by ownerId matching receiver type
- utils: extractReceiverName supports C++ field_expression (argument field)
- utils: findEnclosingClassId extracts Go method receiver types
- type-env: handle Go qualified_type, Kotlin user_type/variable_declaration
- parse-worker + parsing-processor: Function added to needsOwner for
Kotlin/Rust/Python class methods captured as Function nodes
Integration tests added for receiver-constrained resolution across all 9
languages: TypeScript, Java, Python, Go, Rust, C++, C#, Kotlin, PHP.
* feat: NamedImportMap, scoped TypeEnv, broadened signatures + TS rest-param variadic fix
Address all 4 PR #238 review items:
1. Remove redundant lookupFuzzy in processRoutesFromExtracted
2. Add NamedImportMap for TS/Python symbol-level import tracking (Tier 2a)
3. Make TypeEnv scope-aware (Map<scopeKey, Map<varName, type>>) to fix
non-deterministic receiver resolution across functions
4. Broaden extractMethodSignature: Go/Rust/C++ return types, variadic
detection for Go/Java/Python/C++/Kotlin/TypeScript rest params
Discovered and fixed: TS rest params (...args) were not detected as
variadic — added rest_pattern detection inside required_parameter nodes.
Integration tests added: scoped receiver, named import disambiguation,
and variadic call resolution for both TypeScript and Python.
* fix: alias import resolution, Go multi-assign TypeEnv, dead code removal
- NamedImportMap now stores {sourcePath, exportedName} so aliased imports
(import { User as U }) resolve U → User in the source file
- Named binding check moved before empty-allDefs early return in both
call-processor and symbol-resolver, fixing constructor calls via aliases
- Go extractFromGoShortVarDeclaration iterates all LHS/RHS pairs for
multi-assignment (user, repo := User{}, Repo{}) instead of only first
- Remove unused TYPED_DECLARATION_TYPES set (TYPED_PARAMETER_TYPES kept)
- Integration tests for both fixes (go-multi-assign, typescript-alias-imports)
* feat: alias import extraction for Kotlin, Rust, PHP, C# + integration tests
Add named import alias extraction to both pipeline paths
(import-processor.ts and parse-worker.ts) for Kotlin, Rust, PHP,
and C#. Add integration test fixtures and tests for all 5 languages
(Python alias extraction already worked, just needed the test).
Each test verifies: class detection, member call resolution through
aliases to correct target files, and IMPORTS edge emission.
* refactor: use SupportedLanguages enum everywhere instead of raw strings
Replace all raw language string literals and `language: string` types
with the SupportedLanguages enum across 10 files. This ensures
compile-time safety for language dispatch and eliminates dead
`language === 'tsx'` checks (tsx maps to TypeScript in the enum).
* fix: tier-ordering bug, re-export chains, PHP grouped imports, Java named imports
- Fix collectTieredCandidates tier-ordering: same-file now checked before
named bindings, preventing imports from shadowing local definitions
(matches resolveSymbolInternal priority order)
- Add re-export chain resolution for TypeScript/JavaScript barrel files:
export { X } from './base' and export type { X } from './base' now
followed up to 5 hops through NamedImportMap
- Fix PHP grouped import alias extraction: use App\Models\{User, Repo as R}
now correctly handled in both parse-worker and import-processor
- Add Java NamedImportMap support: import com.example.models.User now
records User as a named binding for precise disambiguation
- Add 16 new integration tests across TypeScript, PHP, and Java resolvers
(220 total resolver tests, all passing)
* refactor: consolidate alias extraction + add variadic/constructor/shadow integration tests
- Extract shared named-binding-extraction.ts from duplicate logic in
import-processor.ts and parse-worker.ts (net -200 lines)
- Deduplicate appendKotlinWildcard (now imported from resolvers/index.ts)
- Add integration tests: constructor calls (Kotlin, Python), variadic
resolution (Go, Java, C#, C++, Kotlin), re-export chains (Python),
local definition shadowing (Python, Go)
- Add TODO(stack-graph) for TypeEnv scope key collision
- 225 integration tests passing (was 223)
* fix: PHP non-aliased imports, Python node identity, re-export chain dedup + local-shadow tests
- PHP flat non-aliased imports (use App\Models\User) now stored in NamedImportMap
- PHP grouped non-aliased imports ({User} in {User, Repo as R}) now stored in NamedImportMap
- Python: replace non-public child.id with child.startIndex for node identity
- Extract shared walkBindingChain() from symbol-resolver and call-processor
- Add PHP variadic resolution fixture + test (variadic_parameter already covers PHP)
- Add local-shadow integration tests for Java, C#, Kotlin, Rust, PHP, C++ (6 languages)
* feat: Rust non-aliased use bindings, Kotlin non-aliased imports, re-export chain resolution
Extend NamedImportMap coverage for Rust and Kotlin non-aliased imports:
- Rust: rename collectUseAsClauses → collectRustBindings, extract terminal
scoped_identifier (use crate::models::User) and identifier in use_list
(use crate::models::{User, Repo}) into NamedImportMap. This also enables
pub use re-export chain following via walkBindingChain.
- Kotlin: extend extractKotlinNamedBindings to handle non-aliased imports
(import com.example.User), skipping wildcard imports.
- Add rust-reexport-chain fixture + 3 integration tests verifying Handler{}
resolves through mod.rs pub use to handler.rs.
- Add Kotlin heritage + constructor-calls reason assertions for non-aliased
import-resolved resolution.
- Add C# heritage test documenting namespace import tier behavior.
* fix: skip Kotlin lowercase member imports in NamedImportMap
Member imports like `import util.OneArg.writeAudit` (lowercase last
segment) must not populate NamedImportMap — same-named function imports
from different classes collide, breaking arity-based disambiguation.
Apply the same guard Java already uses: skip lowercase last segments.
* fix: skip spurious path-prefix bindings in Rust grouped imports
collectRustBindings was extracting the path segment (e.g. "models") from
`use crate::models::{User, Repo}` as a spurious NamedImportMap entry.
Skip scoped_identifier nodes that are direct children of scoped_use_list
since they are path prefixes, not importable symbols.
Adds rust-grouped-imports fixture and 4 integration tests verifying both
symbols resolve correctly and no spurious binding leaks through.
* fix: use startIndex in TypeEnv scope key to prevent same-name method collision
Two methods named identically in different classes within the same file
previously shared a scope key, causing non-deterministic type resolution.
Now keys use funcName@startIndex for uniqueness.
Also adds tests documenting destructuring assignment extraction gap.
* test: document C# namespace-level import limitation in named binding extraction
* test: document same-arity overload discrimination limitation in call processor
* perf: parallelize calls/heritage/routes processing in worker path
Worker path now runs processCallsFromExtracted, processHeritageFromExtracted,
and processRoutesFromExtracted via Promise.all instead of sequentially.
Safe because all three only read shared state and write via addRelationship's
dedup guard. Sequential fallback path stays sequential (shared LRU astCache).
Also fixes Rust collectRustBindings spurious path-prefix bindings for 3+ level
grouped imports, and adds @param JSDoc for walkBindingChain's allDefs invariant.
* docs: improve Promise.all safety comment and walkBindingChain JSDoc
Clarify that the parallelization safety comes from disjoint relationship
types + idempotent id-keyed Maps, not from lack of shared state (the
graph is shared). Strengthen allDefs JSDoc to describe silent-miss
consequence of passing pre-filtered results.
* refactor: extract language-specific processing into modular dispatch tables
Phase 1: Extract type binding logic from type-env.ts (635→125 LOC) into
type-extractors/ directory with per-language files and Record<SupportedLanguages,
LanguageTypeConfig> + satisfies dispatch.
Phase 2: Extract 5 config loaders from import-processor.ts into
language-config.ts (removed ~196 LOC of inline loaders).
Phase 3: Convert export-detection.ts switch/case to exhaustive
Record<SupportedLanguages, ExportChecker> + satisfies dispatch table,
fix node: any → SyntaxNode.
Also adds language feature matrix to README.
All 1146 unit tests and 433 integration tests pass.
* refactor: extract type binding logic into type-extractors/ directory (Phase 1)
Extract per-language type extraction from type-env.ts (635→125 LOC) into
type-extractors/ with Record<SupportedLanguages, LanguageTypeConfig> + satisfies
dispatch. 9 per-language files, shared helpers, and barrel index.
* refactor: extract config loaders to language-config.ts (Phase 2)
Move 5 language-specific config loaders and their type interfaces from
import-processor.ts into standalone language-config.ts module.
|
||
|
|
7376e92063 |
fix: consolidate C/C++/C#/Rust language support from 6 overlapping PRs (#237)
* fix: consolidate C/C++/C#/Rust language support from 6 overlapping PRs Merges fixes from PRs #163, #170, #178, #216, #227, #234 into a single coherent changeset with shared modules and deduplication. Phase 0 — Pre-merge consolidation: - Extract isNodeExported to shared export-detection.ts module - Extract TREE_SITTER_BUFFER_SIZE to shared constants.ts with adaptive sizing - Consolidate FUNCTION_NODE_TYPES, extractFunctionName, isBuiltInOrNoise from duplicated call-processor.ts and parse-worker.ts into shared utils.ts - Add query compilation smoke tests for all 12 languages Language fixes: - fix(c/cpp): isExported checks static linkage instead of returning false - fix(c/cpp): .h files parsed as C++ (tree-sitter-cpp is superset of C) - fix(c/cpp): expanded entry point patterns (~30 new for C, ~18 for C++) - fix(cpp): add typedef, union, macro, prototype, inline method queries - fix(c#): isExported scans sibling modifiers instead of parent walk - fix(c#): heritage queries use correct base_list AST structure - fix(c#): add framework detection, import resolution, entry point scoring - fix(rust): isExported scans sibling visibility_modifier in declaration - fix(builtins): remove open/read/write/close (real C POSIX syscalls) - fix(buffer): adaptive bufferSize (2x fileSize, 512KB-32MB range) - feat(ts/js): add call_expression query patterns for const assignments Deduplication: - call-processor.ts: -226 lines (uses shared utils) - parse-worker.ts: -320 lines (uses shared utils) - parsing-processor.ts: -156 lines (uses shared export-detection) * perf: fix review findings — hoist Sets, deduplicate DEFINITION_CAPTURE_KEYS - Hoist CSHARP_DECL_TYPES and RUST_DECL_TYPES to module-level constants in export-detection.ts (was allocating new Set on every isNodeExported call) - Extract DEFINITION_CAPTURE_KEYS and getDefinitionNodeFromCaptures to shared utils.ts (was duplicated in parsing-processor.ts and parse-worker.ts) - Pre-compute merged entry point patterns to avoid per-call array spread in calculateEntryPointScore * test: add C, C++, and Tree-sitter buffer size tests * fix: C/C++/Rust review findings + comprehensive test coverage (+72 tests) Source fixes: - Add Rust built-in noise (unwrap, clone, into, collect, panic, etc.) - C++ anonymous namespace → internal linkage (not exported) - Replace .text regex with storage_class_specifier child scan (perf) - Raise file skip threshold from 512KB to 32MB (TREE_SITTER_MAX_BUFFER) - Export TREE_SITTER_MAX_BUFFER from constants.ts - Add C++ double pointer query patterns to CPP_QUERIES - Add C#: record_struct, record_class, file_scoped_namespace to decl types - Add Rust: union_item to visibility scanning set Tests (214 → 286): - ingestion-utils: +24 (Rust/C# noise, pointer/ref/destructor extraction, buffer) - parsing: +36 (real AST C/C++ static/namespace, Rust/C#/Java/PHP/Swift edge cases) - tree-sitter-languages: +12 (query accuracy for C/C++/C#/Rust captures) |
||
|
|
8a100a76d3 |
test: add test suite with vitest (unit + integration + fixtures)
- 59 test files covering unit and integration tests - vitest config with coverage thresholds and fork pooling - Test fixtures (mini-repo + multi-language sample code) - Add vitest + coverage-v8 to devDependencies - Add test scripts (test, test:integration, test:all, test:watch, test:coverage) - Move typescript to devDependencies where it belongs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |