mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-10 03:27:59 +00:00
fix(rust): F70 — replace struct_expression name:(_) with three specific patterns (#2051)
* fix(rust): F70 — replace struct_expression name:(_) with 3 specific patterns
* fix(rust): F70 — cover scoped+turbofish struct literals (foo::Bar::<T> {})
The three patterns enumerate struct_expression.name as type_identifier /
scoped_type_identifier / generic_type_with_turbofish, but
generic_type_with_turbofish.type can itself be a scoped_identifier
(e.g. foo::Bar::<i32> {}), which the turbofish pattern — requiring
type:(type_identifier) — did not match. That dropped the constructor
reference entirely (verified: emitRustScopeCaptures returns 0 ctors for
foo::Bar::<i32> {} and a:🅱️:Bar::<i32> {}).
Add a fourth pattern that captures the trailing identifier of the scoped
turbofish path (scoped_identifier.name is an identifier, not a
type_identifier), and correct the comment that claimed all cases were
covered.
Strengthen rust-f70.test.ts: assert exactly one constructor per case, add
negative assertions guarding against the old full-path capture, and add
the scoped+turbofish and crate:: cases.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Gergo Magyar <gergomagyar@icloud.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
782f70cc07
commit
bb3642ad5f
2 changed files with 83 additions and 4 deletions
|
|
@ -134,11 +134,34 @@ const RUST_SCOPE_QUERY = `
|
|||
name: (identifier) @reference.name)) @reference.call.free
|
||||
|
||||
;; References — constructor calls (struct literal)
|
||||
;; Covers bare names (Foo {}), scoped (foo::bar::Baz {}), and turbofish
|
||||
;; (Foo::<T> {}) — the name: field resolves to the trailing identifier
|
||||
;; in all cases through tree-sitter-rust's grammar.
|
||||
;; tree-sitter-rust gives struct_expression.name one of three node types
|
||||
;; (type_identifier | scoped_type_identifier | generic_type_with_turbofish);
|
||||
;; the turbofish form additionally nests either a type_identifier or a
|
||||
;; scoped_identifier. We enumerate all four shapes below so the capture is
|
||||
;; always the trailing identifier (resolved scope-aware), not the full path:
|
||||
;; bare Foo {}
|
||||
;; scoped foo::bar::Baz {}
|
||||
;; turbofish Foo::<T> {}
|
||||
;; scoped+turbofish foo::Bar::<T> {}
|
||||
(struct_expression
|
||||
name: (_) @reference.name) @reference.call.constructor
|
||||
name: (type_identifier) @reference.name) @reference.call.constructor
|
||||
|
||||
;; Scoped struct (foo::bar::Baz {})
|
||||
(struct_expression
|
||||
name: (scoped_type_identifier
|
||||
name: (type_identifier) @reference.name)) @reference.call.constructor
|
||||
|
||||
;; Turbofish struct (Foo::<T> {})
|
||||
(struct_expression
|
||||
name: (generic_type_with_turbofish
|
||||
type: (type_identifier) @reference.name)) @reference.call.constructor
|
||||
|
||||
;; Scoped + turbofish struct (foo::Bar::<T> {}) — the turbofish wraps a
|
||||
;; scoped_identifier whose tail is an identifier (not a type_identifier).
|
||||
(struct_expression
|
||||
name: (generic_type_with_turbofish
|
||||
type: (scoped_identifier
|
||||
name: (identifier) @reference.name))) @reference.call.constructor
|
||||
|
||||
;; References — macro invocations (disjoint namespace from functions)
|
||||
;; Resolved via MacroRegistry → Macro defs only (never fn of the same name).
|
||||
|
|
|
|||
56
gitnexus/test/integration/resolvers/rust-f70.test.ts
Normal file
56
gitnexus/test/integration/resolvers/rust-f70.test.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/**
|
||||
* Tests for F70 — struct literal constructor calls (issue #1934).
|
||||
*/
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { emitRustScopeCaptures } from '../../../src/core/ingestion/languages/rust/index.js';
|
||||
import type { CaptureMatch } from 'gitnexus-shared';
|
||||
|
||||
describe('F70 — struct literal constructor calls', () => {
|
||||
it('bare struct Foo {} captures Foo as @reference.name', () => {
|
||||
const src = `fn f() { let _ = Foo { x: 1 }; }\n`;
|
||||
const matches = emitRustScopeCaptures(src, 'test.rs') as CaptureMatch[];
|
||||
const ctors = matches.filter((m) => m['@reference.call.constructor']);
|
||||
expect(ctors.length).toBe(1);
|
||||
expect(ctors[0]['@reference.name'].text).toBe('Foo');
|
||||
});
|
||||
|
||||
it('scoped struct foo::bar::Baz {} captures Baz (not the full path)', () => {
|
||||
const src = `fn f() { let _ = foo::bar::Baz { x: 1 }; }\n`;
|
||||
const matches = emitRustScopeCaptures(src, 'test.rs') as CaptureMatch[];
|
||||
const ctors = matches.filter((m) => m['@reference.call.constructor']);
|
||||
const names = ctors.map((m) => m['@reference.name']?.text);
|
||||
expect(ctors.length).toBe(1);
|
||||
expect(names).toContain('Baz');
|
||||
// Guard against regressing to the old wildcard, which captured the path.
|
||||
expect(names).not.toContain('foo::bar::Baz');
|
||||
});
|
||||
|
||||
it('turbofish struct Foo::<i32> {} captures Foo (not Foo::<i32>)', () => {
|
||||
const src = `fn f() { let _ = Foo::<i32> { x: 1 }; }\n`;
|
||||
const matches = emitRustScopeCaptures(src, 'test.rs') as CaptureMatch[];
|
||||
const ctors = matches.filter((m) => m['@reference.call.constructor']);
|
||||
const names = ctors.map((m) => m['@reference.name']?.text);
|
||||
expect(ctors.length).toBe(1);
|
||||
expect(names).toContain('Foo');
|
||||
expect(names).not.toContain('Foo::<i32>');
|
||||
});
|
||||
|
||||
it('scoped + turbofish struct foo::Bar::<i32> {} captures Bar', () => {
|
||||
const src = `fn f() { let _ = foo::Bar::<i32> { x: 1 }; }\n`;
|
||||
const matches = emitRustScopeCaptures(src, 'test.rs') as CaptureMatch[];
|
||||
const ctors = matches.filter((m) => m['@reference.call.constructor']);
|
||||
const names = ctors.map((m) => m['@reference.name']?.text);
|
||||
expect(ctors.length).toBe(1);
|
||||
expect(names).toContain('Bar');
|
||||
expect(names).not.toContain('foo::Bar::<i32>');
|
||||
});
|
||||
|
||||
it('crate-scoped struct crate::Foo {} captures Foo', () => {
|
||||
const src = `fn f() { let _ = crate::Foo { x: 1 }; }\n`;
|
||||
const matches = emitRustScopeCaptures(src, 'test.rs') as CaptureMatch[];
|
||||
const ctors = matches.filter((m) => m['@reference.call.constructor']);
|
||||
const names = ctors.map((m) => m['@reference.name']?.text);
|
||||
expect(ctors.length).toBe(1);
|
||||
expect(names).toContain('Foo');
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue