Skip to content

fix(parser): repair Zig support — grammar nodes were never matched#393

Open
geeknik wants to merge 1 commit intotirth8205:mainfrom
geeknik:main
Open

fix(parser): repair Zig support — grammar nodes were never matched#393
geeknik wants to merge 1 commit intotirth8205:mainfrom
geeknik:main

Conversation

@geeknik
Copy link
Copy Markdown
Contributor

@geeknik geeknik commented Apr 27, 2026

Zig was registered in EXTENSION_TO_LANGUAGE and the per-language node-type tables, but the mapped types (fn_proto, fn_decl, container_declaration, call_expression, builtin_call_expr) don't exist in the tree-sitter-zig grammar shipped by tree_sitter_language_pack — that grammar emits PascalCase nodes (FnProto, VarDecl, SuffixExpr, FnCallArguments, FieldOrFnCall, BUILTINIDENTIFIER, ContainerDecl, TestDecl). Result: every Zig file produced only its File node, with zero functions, structs, imports, or calls.

Rather than rename and lose information, route Zig through a constructs handler (mirroring the Lua/Elixir pattern) so we can bridge FnProto's sibling Block, distinguish struct/union/enum/opaque ContainerDecl kinds, and split @import out as IMPORTS_FROM rather than CALLS.

  • Empty _CLASS_TYPES/_FUNCTION_TYPES/_IMPORT_TYPES/_CALL_TYPES for "zig" with comments explaining why generic dispatch can't be used.
  • Add _extract_zig_constructs dispatch in _extract_from_tree.
  • Implement _handle_zig_fn_decl, _handle_zig_var_decl, _handle_zig_test_decl, _zig_extract_import_target, and _extract_zig_calls_in_subtree. The call walker emits CALLS for any SuffixExpr/FieldOrFnCall whose direct children include FnCallArguments, resolving the callee to the immediately preceding IDENTIFIER or BUILTINIDENTIFIER. @import is filtered out so it isn't double-counted.
  • Add a Zig branch to _resolve_module_to_file: relative @import("./x.zig") resolves to an absolute file path; std and build-graph module names stay unresolved (resolving the latter would require parsing build.zig).
  • Add tests/fixtures/sample_zig.zig + sample_zig_util.zig covering top-level fns, struct with methods, enum, tagged union, std + relative @import, @intcast builtin call, and a labelled test block.
  • Add TestZigParsing to tests/test_multilang.py (11 tests).

Verified on a 41-file Zig project: before the fix, build produced 0 nodes / 0 edges; after, 393 unique nodes (214 Function, 124 Test, 55 Class) and 2839 edges (2353 CALLS, 393 CONTAINS, 93 IMPORTS_FROM), with hub detection correctly surfacing Reader.next, sanitize helpers, and Response.deinit as the most-called sites.

Zig was registered in EXTENSION_TO_LANGUAGE and the per-language node-type
tables, but the mapped types (fn_proto, fn_decl, container_declaration,
call_expression, builtin_call_expr) don't exist in the tree-sitter-zig
grammar shipped by tree_sitter_language_pack — that grammar emits PascalCase
nodes (FnProto, VarDecl, SuffixExpr, FnCallArguments, FieldOrFnCall,
BUILTINIDENTIFIER, ContainerDecl, TestDecl). Result: every Zig file produced
only its File node, with zero functions, structs, imports, or calls.

Rather than rename and lose information, route Zig through a constructs
handler (mirroring the Lua/Elixir pattern) so we can bridge FnProto's
sibling Block, distinguish struct/union/enum/opaque ContainerDecl kinds,
and split @import out as IMPORTS_FROM rather than CALLS.

- Empty _CLASS_TYPES/_FUNCTION_TYPES/_IMPORT_TYPES/_CALL_TYPES for "zig"
  with comments explaining why generic dispatch can't be used.
- Add _extract_zig_constructs dispatch in _extract_from_tree.
- Implement _handle_zig_fn_decl, _handle_zig_var_decl,
  _handle_zig_test_decl, _zig_extract_import_target, and
  _extract_zig_calls_in_subtree. The call walker emits CALLS for any
  SuffixExpr/FieldOrFnCall whose direct children include FnCallArguments,
  resolving the callee to the immediately preceding IDENTIFIER or
  BUILTINIDENTIFIER. @import is filtered out so it isn't double-counted.
- Add a Zig branch to _resolve_module_to_file: relative @import("./x.zig")
  resolves to an absolute file path; std and build-graph module names stay
  unresolved (resolving the latter would require parsing build.zig).
- Add tests/fixtures/sample_zig.zig + sample_zig_util.zig covering
  top-level fns, struct with methods, enum, tagged union, std + relative
  @import, @intcast builtin call, and a labelled test block.
- Add TestZigParsing to tests/test_multilang.py (11 tests).

Verified on a 41-file Zig project (Tributary): before the fix, build
produced 0 nodes / 0 edges; after, 393 unique nodes (214 Function,
124 Test, 55 Class) and 2839 edges (2353 CALLS, 393 CONTAINS, 93
IMPORTS_FROM), with hub detection correctly surfacing Reader.next,
sanitize helpers, and Response.deinit as the most-called sites.
@geeknik
Copy link
Copy Markdown
Contributor Author

geeknik commented Apr 27, 2026

Those CI failures aren't from my PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant