mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-11 03:38:07 +00:00
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).
This commit is contained in:
parent
fdfdd2e35f
commit
6f0f8b1dee
2 changed files with 51 additions and 4 deletions
|
|
@ -225,9 +225,18 @@ const ZIG_SCOPE_QUERY = `
|
|||
name: (identifier) @type-binding.name
|
||||
type: (_) @type-binding.type) @type-binding.parameter
|
||||
|
||||
;; Type bindings — constructor inference: const p = T{ ... }
|
||||
;; Type bindings — constructor inference: const p = T{ ... }. Keyword-gated
|
||||
;; like every binding rule: a keyword-less \`p = T{ ... };\` is a
|
||||
;; re-assignment (same node type in tree-sitter-zig 1.1.2), and Zig's static
|
||||
;; typing means \`p\` already carries its type from its declaration
|
||||
;; (annotation, constructor or inferred value) — the assignment declares
|
||||
;; nothing, and \`_ = T{ ... };\` must not bind \`_\`.
|
||||
(variable_declaration
|
||||
(identifier) @type-binding.name
|
||||
"const" . (identifier) @type-binding.name
|
||||
(struct_initializer
|
||||
(identifier) @type-binding.type)) @type-binding.constructor
|
||||
(variable_declaration
|
||||
"var" . (identifier) @type-binding.name
|
||||
(struct_initializer
|
||||
(identifier) @type-binding.type)) @type-binding.constructor
|
||||
|
||||
|
|
@ -236,7 +245,11 @@ const ZIG_SCOPE_QUERY = `
|
|||
;; receiver dispatch resolves the namespace prefix through the import
|
||||
;; binding (emitReceiverBoundCalls Case 3).
|
||||
(variable_declaration
|
||||
(identifier) @type-binding.name
|
||||
"const" . (identifier) @type-binding.name
|
||||
(struct_initializer
|
||||
(field_expression) @type-binding.type)) @type-binding.constructor
|
||||
(variable_declaration
|
||||
"var" . (identifier) @type-binding.name
|
||||
(struct_initializer
|
||||
(field_expression) @type-binding.type)) @type-binding.constructor
|
||||
|
||||
|
|
@ -244,7 +257,11 @@ const ZIG_SCOPE_QUERY = `
|
|||
;; The callee is the type constructor; \`normalizeZigTypeName\` drops the
|
||||
;; comptime argument list so \`List(u8)\` looks up \`List\`.
|
||||
(variable_declaration
|
||||
(identifier) @type-binding.name
|
||||
"const" . (identifier) @type-binding.name
|
||||
(struct_initializer
|
||||
(call_expression) @type-binding.type)) @type-binding.constructor
|
||||
(variable_declaration
|
||||
"var" . (identifier) @type-binding.name
|
||||
(struct_initializer
|
||||
(call_expression) @type-binding.type)) @type-binding.constructor
|
||||
|
||||
|
|
|
|||
|
|
@ -1390,6 +1390,36 @@ fn f() !void {
|
|||
]);
|
||||
});
|
||||
|
||||
it('a keyword-less re-assignment `p = T{…};` / `_ = T{…};` binds no constructor type (only declarations do)', () => {
|
||||
// tree-sitter-zig 1.1.2 parses assignments as `variable_declaration` too;
|
||||
// the constructor rules used to match them and mint a binding for `p` (and
|
||||
// for `_`) in the assignment's block. `p` already carries its type from its
|
||||
// declaration, so the assignment adds nothing — the rules are keyword-gated
|
||||
// like the import and call-return rules.
|
||||
const src = `
|
||||
fn f() void {
|
||||
var p = Thing{ .a = 1 };
|
||||
const q = mod.Thing{ .a = 1 };
|
||||
const l = List(u8){};
|
||||
p = Thing{ .a = 2 };
|
||||
q = mod.Thing{ .a = 2 };
|
||||
l = List(u8){};
|
||||
_ = Thing{ .a = 3 };
|
||||
}
|
||||
`;
|
||||
const ctor = (boundName: string, rawTypeName: string) => ({
|
||||
boundName,
|
||||
rawTypeName,
|
||||
source: 'constructor-inferred',
|
||||
kind: '@type-binding.constructor',
|
||||
});
|
||||
expect(bindingsOf(src)).toEqual([
|
||||
ctor('p', 'Thing'),
|
||||
ctor('q', 'mod.Thing'),
|
||||
ctor('l', 'List'),
|
||||
]);
|
||||
});
|
||||
|
||||
it('a wrapped struct literal (`try Thing{…}`) is a constructor binding', () => {
|
||||
expect(bindingsOf('fn f() !void { const t = try Thing{ .a = 1 }; }')).toEqual([
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue