gitnexus 1.6.12-rc.4 → 1.6.12-rc.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,17 +11,14 @@
11
11
  * Conservative by design: we only tag an edge when we can prove the
12
12
  * gating expression evaluates to `false`. Anything ambiguous → live.
13
13
  *
14
- * Scope of v1:
14
+ * Supported scope:
15
15
  *
16
16
  * (a) **File-local** consts (`pub const FOO = false;`, plus const-to-const
17
17
  * aliases up to 5 hops), built once per file by `buildZigBoolConstMap`.
18
- * (b) **Cross-file** (`const cfg = @import("./cfg.zig"); if (cfg.FOO)`) is
19
- * NOT resolved yet. The evaluator keeps the seam for it (`importAliases`
20
- * + `lookupBoolsForPath`, consumed by the `field_expression` case), but
21
- * the only caller passes an empty alias map and a lookup that always
22
- * returns `undefined`, because the capture emitter runs in the parse
23
- * worker and sees only the current file. Tracked in #3162. Until then
24
- * every `cfg.FOO` condition folds to unknown, i.e. live.
18
+ * (b) **Cross-file** direct imports (`const cfg = @import("./cfg.zig");
19
+ * if (cfg.FOO)`) are enriched after per-file extraction. The workspace
20
+ * caller supplies `importAliases` and `lookupBoolsForPath`; the parse
21
+ * worker still uses empty/undefined inputs and remains file-local.
25
22
  *
26
23
  * Also out of scope: multi-hop member access (`cfg.sub.FOO`), re-exported
27
24
  * consts, runtime-evaluated bools (`const FOO = computeIt();`), and
@@ -16,6 +16,7 @@ import { resolveZigImportInternal } from '../../import-resolvers/zig.js';
16
16
  import { zigProvider } from '../zig.js';
17
17
  import { expandZigWildcardNames, zigArityCompatibility, zigMergeBindings } from './index.js';
18
18
  import { populateZigRangeBindings } from './range-binding.js';
19
+ import { populateZigWorkspaceStaticGating } from './workspace-static-gating.js';
19
20
  export const zigScopeResolver = {
20
21
  language: SupportedLanguages.Zig,
21
22
  languageProvider: zigProvider,
@@ -48,6 +49,7 @@ export const zigScopeResolver = {
48
49
  arityCompatibility: zigArityCompatibility,
49
50
  buildMro: (graph, parsedFiles, nodeLookup) => buildMro(graph, parsedFiles, nodeLookup, defaultLinearize),
50
51
  populateOwners: (parsed) => populateClassOwnedMembers(parsed),
52
+ populateWorkspaceReferences: populateZigWorkspaceStaticGating,
51
53
  // Payload captures — `for (items) |it|`, `if (opt) |v|`, `while (it.next())
52
54
  // |x|` — typed from the subject's binding after finalize (F6).
53
55
  populateRangeBindings: populateZigRangeBindings,
@@ -0,0 +1,8 @@
1
+ import type { ParsedFile } from '../../../../_shared/index.js';
2
+ export declare function populateZigWorkspaceStaticGating(parsedFiles: ParsedFile[], ctx: {
3
+ readonly fileContents: ReadonlyMap<string, string>;
4
+ readonly treeCache?: {
5
+ get(filePath: string): unknown;
6
+ };
7
+ readonly resolutionConfig?: unknown;
8
+ }): void;
@@ -0,0 +1,76 @@
1
+ import { getTreeSitterBufferSize } from '../../constants.js';
2
+ import { resolveZigImportInternal } from '../../import-resolvers/zig.js';
3
+ import { buildZigBoolConstMap, collectZigStaticGatedRanges, isPositionStaticGated, } from '../../call-extractors/zig-static-gating.js';
4
+ import { parseSourceSafe, ParseTimeoutError } from '../../../tree-sitter/safe-parse.js';
5
+ import { getZigParser } from './query.js';
6
+ export function populateZigWorkspaceStaticGating(parsedFiles, ctx) {
7
+ const parser = getZigParser();
8
+ const trees = new Map();
9
+ const bools = new Map();
10
+ for (const parsed of parsedFiles) {
11
+ const source = ctx.fileContents.get(parsed.filePath);
12
+ if (source === undefined)
13
+ continue;
14
+ let tree = ctx.treeCache?.get(parsed.filePath);
15
+ if (tree === undefined) {
16
+ try {
17
+ tree = parseSourceSafe(parser, source, undefined, {
18
+ bufferSize: getTreeSitterBufferSize(source),
19
+ });
20
+ }
21
+ catch (err) {
22
+ if (err instanceof ParseTimeoutError)
23
+ continue;
24
+ throw err;
25
+ }
26
+ }
27
+ trees.set(parsed.filePath, tree);
28
+ bools.set(parsed.filePath, buildZigBoolConstMap(tree.rootNode));
29
+ }
30
+ const knownPaths = new Set(trees.keys());
31
+ for (const [index, parsed] of parsedFiles.entries()) {
32
+ const tree = trees.get(parsed.filePath);
33
+ if (tree === undefined)
34
+ continue;
35
+ const aliases = collectImportAliases(tree, parsed.filePath, knownPaths, ctx.resolutionConfig);
36
+ if (aliases.size === 0)
37
+ continue;
38
+ const ranges = collectZigStaticGatedRanges(tree.rootNode, bools.get(parsed.filePath) ?? new Map(), aliases, (filePath) => bools.get(filePath));
39
+ if (ranges.length === 0)
40
+ continue;
41
+ const next = parsed.referenceSites.map((site) => site.kind === 'call' &&
42
+ site.staticGated !== true &&
43
+ isPositionStaticGated(site.atRange.startLine, site.atRange.startCol, ranges)
44
+ ? { ...site, staticGated: true }
45
+ : site);
46
+ parsedFiles[index] = Object.freeze({ ...parsed, referenceSites: Object.freeze(next) });
47
+ }
48
+ }
49
+ function collectImportAliases(tree, fromFile, knownPaths, resolutionConfig) {
50
+ const candidates = new Map();
51
+ const declarationCounts = new Map();
52
+ for (const decl of tree.rootNode.descendantsOfType('variable_declaration')) {
53
+ const names = decl.namedChildren.filter((node) => node.type === 'identifier');
54
+ const binding = names[0]?.text;
55
+ if (binding === undefined)
56
+ continue;
57
+ declarationCounts.set(binding, (declarationCounts.get(binding) ?? 0) + 1);
58
+ const builtin = decl.namedChildren.find((node) => node.type === 'builtin_function' && node.text.startsWith('@import('));
59
+ const raw = builtin?.descendantsOfType('string').at(0)?.text;
60
+ if (raw === undefined)
61
+ continue;
62
+ const specifier = raw.replace(/^['"]|['"]$/g, '');
63
+ const target = resolveZigImportInternal(fromFile, specifier, knownPaths, resolutionConfig);
64
+ if (target !== null)
65
+ candidates.set(binding, target);
66
+ }
67
+ const aliases = new Map();
68
+ for (const [binding, target] of candidates) {
69
+ // Alias lookup below is name-based rather than position-aware. If a name
70
+ // is redeclared in another lexical scope, fail open instead of applying
71
+ // either module's constants to every use of that spelling.
72
+ if (declarationCounts.get(binding) === 1)
73
+ aliases.set(binding, target);
74
+ }
75
+ return aliases;
76
+ }
@@ -615,6 +615,19 @@ export interface ScopeResolver {
615
615
  readonly populateWorkspaceOwners?: (parsedFiles: readonly ParsedFile[], ctx: {
616
616
  readonly fileContents: ReadonlyMap<string, string>;
617
617
  }) => void;
618
+ /**
619
+ * Optional workspace-wide enrichment of extracted reference sites. Runs
620
+ * after all files have been extracted and before reference finalization.
621
+ * Use this when a per-file capture needs conservative facts from an
622
+ * imported sibling (for example a compile-time branch constant).
623
+ */
624
+ readonly populateWorkspaceReferences?: (parsedFiles: ParsedFile[], ctx: {
625
+ readonly fileContents: ReadonlyMap<string, string>;
626
+ readonly treeCache?: {
627
+ get(filePath: string): unknown;
628
+ };
629
+ readonly resolutionConfig?: unknown;
630
+ }) => void;
618
631
  /**
619
632
  * Recognize a `super(...)`-style receiver text. Python returns
620
633
  * `/^super\s*\(/.test(t)`. Java returns `t === 'super'`. C++ may
@@ -55,6 +55,7 @@ const NOOP_OUTPUT = Object.freeze({
55
55
  /** Select source files that must be materialized for one resolver pass. */
56
56
  export function selectScopeSourcePathsToRead(provider, primaryFilePaths, preExtractedByPath) {
57
57
  const hasPostExtractHooks = provider.populateWorkspaceOwners !== undefined ||
58
+ provider.populateWorkspaceReferences !== undefined ||
58
59
  provider.populateNamespaceSiblings !== undefined ||
59
60
  provider.populateRangeBindings !== undefined ||
60
61
  provider.emitPostResolutionEdges !== undefined;
@@ -312,6 +312,11 @@ export function runScopeResolution(input, provider) {
312
312
  }
313
313
  logHeapProbe('sr-extract-end', `lang=${provider.language} parsedFiles=${parsedFiles.length} preExtractedHits=${preExtractedHits} skipped=${filesSkipped}`);
314
314
  provider.populateWorkspaceOwners?.(parsedFiles, { fileContents: getFileContents() });
315
+ provider.populateWorkspaceReferences?.(parsedFiles, {
316
+ fileContents: getFileContents(),
317
+ treeCache,
318
+ resolutionConfig: input.resolutionConfig,
319
+ });
315
320
  // A callable-flow-only provider has no reason to build the whole-graph
316
321
  // lookup or finalize ordinary references when none of its files emitted a
317
322
  // callable fact. This keeps the opt-in path proportional to source scanning
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.6.12-rc.4",
3
+ "version": "1.6.12-rc.5",
4
4
  "description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
5
5
  "author": "Abhigyan Patwari",
6
6
  "license": "PolyForm-Noncommercial-1.0.0",