fix(zig): address gitnexus-check findings on 215f70e3

- receiver-bound Case 3 wraps its reason in constructionSiteReason, like
  Case 1 and the nested-type route of Case 2 (one vocabulary per provider)
- resolveZigImportInternal rejects drive-qualified absolute imports
  (C:\foo.zig), the same test normalizeZigDepPath applies; unit case added
- the compound resolver's chain seed also tries the whole receiver as the
  qualified class (opmod.Op), as a bare class-name head already does
- markConstructionSites contract text names the receiver-bound routes

The return_type field claim is refuted: tree-sitter-zig exposes a fn's
return type as the type field (checked on the grammar).
This commit is contained in:
Navid EMAD 2026-09-03 00:41:24 +02:00
parent 215f70e329
commit 5299c5521f
No known key found for this signature in database
5 changed files with 28 additions and 6 deletions

View file

@ -113,7 +113,11 @@ export function resolveZigImportInternal(
// `@import("/abs.zig")` as an import outside the module path). Splitting
// would drop the empty leading component and read `/foo.zig` as an
// importer-relative `foo.zig`, fabricating an in-repo edge.
if (trimmed.startsWith('/')) return null;
// A drive-qualified spelling (`C:/foo.zig`, normalized from `C:\foo.zig`)
// is absolute too: it carries a `/`, so without this guard it would take
// the importer-relative branch and probe `src/C:/foo.zig`. Same test as
// `normalizeZigDepPath`.
if (trimmed.startsWith('/') || /^[A-Za-z]:\//.test(trimmed)) return null;
// Path-bearing import: resolve relative to the current file's directory.
// Zig allows both "./foo.zig" and "foo.zig" — both are filesystem-relative.

View file

@ -897,8 +897,11 @@ export interface ScopeResolver {
* The marker rides in `reason` because relationships carry no arbitrary
* properties (adding one moves SCHEMA_FINGERPRINT the IMPLEMENTS
* `-pointer` precedent in `pipeline/run.ts`). Applies to the free-call
* fallback and the reference bridge; receiver-qualified construction sites
* are not tagged `constructor` by any provider today.
* fallback, the reference bridge and the receiver-bound paths a
* namespace-qualified literal (`mod.T{…}`, Case 1), a type nested in the
* receiver's class (`A.Item{}`, Case 2) and a dotted type binding (Case 3)
* all go through `constructionSiteReason` so an opted-in provider sees
* one vocabulary whichever path resolved the site.
*/
readonly markConstructionSites?: boolean;

View file

@ -1204,10 +1204,12 @@ export function resolveCompoundReceiverClass(
// itself, so a variant / static member hop is read off the class scope),
// and the remaining segments are walked as members. Longest first: the
// prefix is a class, not a value, and `a.B.C` must seed at `C`, not stop
// at `B` and read `C` as a member of it.
// at `B` and read `C` as a member of it. The whole receiver may be the
// class (`opmod.Op` — no member segment left), exactly as a bare class
// name head resolves to the class constant above.
let firstHop = 1;
if (currentClass === undefined && headType === undefined && options.resolveQualifiedClass) {
for (let k = parts.length - 1; k >= 2; k--) {
for (let k = parts.length; k >= 2; k--) {
const prefix = parts.slice(0, k).join('.');
if (prefix.includes('(')) continue;
const seeded = options.resolveQualifiedClass(prefix, inScope);

View file

@ -1771,7 +1771,15 @@ export function emitReceiverBoundCalls(
nodeLookup,
site,
memberDef,
memberDef.filePath !== parsed.filePath ? 'import-resolved' : 'global',
// Same marker rule as Case 1 / Case 2: a constructor-form site
// reached through a dotted type binding keeps its
// ` (constructor)` suffix when the provider opted in; for
// every other provider the string is unchanged.
constructionSiteReason(
memberDef.filePath !== parsed.filePath ? 'import-resolved' : 'global',
site,
provider.markConstructionSites,
),
seen,
// Explicit defaults so the trailing capture ctx (#2227 U2) can
// be threaded without changing dedup/confidence behavior.

View file

@ -64,6 +64,11 @@ describe('resolveZigImportInternal', () => {
expect(resolveZigImportInternal('src/main.zig', '/src/foo.zig', files)).toBeNull();
// Backslash-spelled absolute paths normalize to the same rejection.
expect(resolveZigImportInternal('src/main.zig', '\\foo.zig', files)).toBeNull();
// A drive-qualified spelling carries a `/` after normalization and used to
// take the importer-relative branch (probing `src/C:/foo.zig`).
const drive = new Set<string>([...files, 'src/C:/foo.zig']);
expect(resolveZigImportInternal('src/main.zig', 'C:\\foo.zig', drive)).toBeNull();
expect(resolveZigImportInternal('src/main.zig', 'C:/foo.zig', drive)).toBeNull();
// The relative spelling next to it still resolves.
expect(resolveZigImportInternal('src/main.zig', 'foo.zig', files)).toBe('src/foo.zig');
});