@opensip-cli/graph-rust 0.1.0

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.
Files changed (72) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +8 -0
  3. package/README.md +31 -0
  4. package/dist/__tests__/cache-key.test.d.ts +9 -0
  5. package/dist/__tests__/cache-key.test.d.ts.map +1 -0
  6. package/dist/__tests__/cache-key.test.js +49 -0
  7. package/dist/__tests__/cache-key.test.js.map +1 -0
  8. package/dist/__tests__/depends-on-emission.test.d.ts +15 -0
  9. package/dist/__tests__/depends-on-emission.test.d.ts.map +1 -0
  10. package/dist/__tests__/depends-on-emission.test.js +266 -0
  11. package/dist/__tests__/depends-on-emission.test.js.map +1 -0
  12. package/dist/__tests__/discover.test.d.ts +8 -0
  13. package/dist/__tests__/discover.test.d.ts.map +1 -0
  14. package/dist/__tests__/discover.test.js +65 -0
  15. package/dist/__tests__/discover.test.js.map +1 -0
  16. package/dist/__tests__/parse.test.d.ts +8 -0
  17. package/dist/__tests__/parse.test.d.ts.map +1 -0
  18. package/dist/__tests__/parse.test.js +64 -0
  19. package/dist/__tests__/parse.test.js.map +1 -0
  20. package/dist/__tests__/resolve-dependencies-branches.test.d.ts +18 -0
  21. package/dist/__tests__/resolve-dependencies-branches.test.d.ts.map +1 -0
  22. package/dist/__tests__/resolve-dependencies-branches.test.js +194 -0
  23. package/dist/__tests__/resolve-dependencies-branches.test.js.map +1 -0
  24. package/dist/__tests__/resolve.test.d.ts +23 -0
  25. package/dist/__tests__/resolve.test.d.ts.map +1 -0
  26. package/dist/__tests__/resolve.test.js +476 -0
  27. package/dist/__tests__/resolve.test.js.map +1 -0
  28. package/dist/__tests__/walk-branches.test.d.ts +13 -0
  29. package/dist/__tests__/walk-branches.test.d.ts.map +1 -0
  30. package/dist/__tests__/walk-branches.test.js +124 -0
  31. package/dist/__tests__/walk-branches.test.js.map +1 -0
  32. package/dist/__tests__/walk.test.d.ts +11 -0
  33. package/dist/__tests__/walk.test.d.ts.map +1 -0
  34. package/dist/__tests__/walk.test.js +324 -0
  35. package/dist/__tests__/walk.test.js.map +1 -0
  36. package/dist/body-digest.d.ts +31 -0
  37. package/dist/body-digest.d.ts.map +1 -0
  38. package/dist/body-digest.js +136 -0
  39. package/dist/body-digest.js.map +1 -0
  40. package/dist/cache-key.d.ts +19 -0
  41. package/dist/cache-key.d.ts.map +1 -0
  42. package/dist/cache-key.js +20 -0
  43. package/dist/cache-key.js.map +1 -0
  44. package/dist/discover.d.ts +22 -0
  45. package/dist/discover.d.ts.map +1 -0
  46. package/dist/discover.js +33 -0
  47. package/dist/discover.js.map +1 -0
  48. package/dist/index.d.ts +53 -0
  49. package/dist/index.d.ts.map +1 -0
  50. package/dist/index.js +56 -0
  51. package/dist/index.js.map +1 -0
  52. package/dist/parse.d.ts +17 -0
  53. package/dist/parse.d.ts.map +1 -0
  54. package/dist/parse.js +14 -0
  55. package/dist/parse.js.map +1 -0
  56. package/dist/resolve-dependencies.d.ts +13 -0
  57. package/dist/resolve-dependencies.d.ts.map +1 -0
  58. package/dist/resolve-dependencies.js +261 -0
  59. package/dist/resolve-dependencies.js.map +1 -0
  60. package/dist/resolve.d.ts +45 -0
  61. package/dist/resolve.d.ts.map +1 -0
  62. package/dist/resolve.js +262 -0
  63. package/dist/resolve.js.map +1 -0
  64. package/dist/rule-hints.d.ts +16 -0
  65. package/dist/rule-hints.d.ts.map +1 -0
  66. package/dist/rule-hints.js +50 -0
  67. package/dist/rule-hints.js.map +1 -0
  68. package/dist/walk.d.ts +47 -0
  69. package/dist/walk.d.ts.map +1 -0
  70. package/dist/walk.js +564 -0
  71. package/dist/walk.js.map +1 -0
  72. package/package.json +54 -0
@@ -0,0 +1,261 @@
1
+ /**
2
+ * @fileoverview Rust `use`-path → dependency-edge resolution.
3
+ *
4
+ * Extracted from `resolve.ts` so the call-site resolver and the
5
+ * dependency resolver each live in a focused module. This file owns the
6
+ * Rust-specific module-path logic (Cargo.toml lookup, `crate`/`self`/
7
+ * `super`/`<pkg-name>` rewriting, file-path → module-path mapping).
8
+ *
9
+ * Phase 4 of opensip's substrate consolidation (DEC-498).
10
+ */
11
+ import { readFileSync } from 'node:fs';
12
+ import { join } from 'node:path';
13
+ export function resolveDependencies(sites, catalog, projectDirAbs) {
14
+ const packageName = readCargoPackageName(projectDirAbs);
15
+ const { moduleInitByModulePath, modulePathByFilePath } = buildCrateModuleIndex(catalog);
16
+ const out = new Map();
17
+ for (const site of sites) {
18
+ const importerModulePath = lookupImporterModulePath(catalog, modulePathByFilePath, site.ownerHash);
19
+ const edge = buildDependencyEdge(site, packageName, importerModulePath, moduleInitByModulePath);
20
+ appendDependencyEdge(out, site.ownerHash, edge);
21
+ }
22
+ return out;
23
+ }
24
+ /**
25
+ * Walk the catalog once to derive the two indices used during `use`-path
26
+ * resolution: `modulePath → moduleInit bodyHash` (for forward lookup) and
27
+ * `filePath → modulePath` (used to rewrite `super::` / `self::` paths
28
+ * relative to the importer's own module).
29
+ */
30
+ function buildCrateModuleIndex(catalog) {
31
+ const moduleInitByModulePath = new Map();
32
+ const modulePathByFilePath = new Map();
33
+ for (const occs of Object.values(catalog.functions)) {
34
+ if (!occs)
35
+ continue;
36
+ for (const o of occs) {
37
+ if (o.kind !== 'module-init')
38
+ continue;
39
+ const modulePath = filePathToRustModulePath(o.filePath);
40
+ if (modulePath === null)
41
+ continue;
42
+ moduleInitByModulePath.set(modulePath, o.bodyHash);
43
+ modulePathByFilePath.set(o.filePath, modulePath);
44
+ }
45
+ }
46
+ return { moduleInitByModulePath, modulePathByFilePath };
47
+ }
48
+ /**
49
+ * Resolve the importer's module path (e.g. `crate::foo::bar`) from its
50
+ * `ownerHash`. Returns `null` when the owner isn't a recognizable crate
51
+ * module — `super::` / `self::` rewriting is then impossible.
52
+ */
53
+ function lookupImporterModulePath(catalog, modulePathByFilePath, ownerHash) {
54
+ const importerFilePath = filePathOfOwner(catalog, ownerHash);
55
+ if (importerFilePath === null)
56
+ return null;
57
+ return modulePathByFilePath.get(importerFilePath) ?? null;
58
+ }
59
+ /** Build a single `DependencyEdge` for one dependency site. */
60
+ function buildDependencyEdge(site, packageName, importerModulePath, moduleInitByModulePath) {
61
+ const to = resolveRustUseSpecifier(site.specifier, packageName, importerModulePath, moduleInitByModulePath);
62
+ return {
63
+ to,
64
+ line: site.line,
65
+ column: site.column,
66
+ specifier: site.specifier,
67
+ };
68
+ }
69
+ /** Append a dependency edge to the per-owner bucket, creating the bucket on first write. */
70
+ function appendDependencyEdge(out, ownerHash, edge) {
71
+ const existing = out.get(ownerHash);
72
+ if (existing === undefined) {
73
+ out.set(ownerHash, [edge]);
74
+ return;
75
+ }
76
+ existing.push(edge);
77
+ }
78
+ /**
79
+ * Look up the importer's filePath from the catalog via owner bodyHash.
80
+ * Needed for `super::` / `self::` rewriting since the catalog already
81
+ * carries the project-relative filePath.
82
+ */
83
+ function filePathOfOwner(catalog, ownerHash) {
84
+ for (const occs of Object.values(catalog.functions)) {
85
+ if (!occs)
86
+ continue;
87
+ for (const o of occs) {
88
+ if (o.bodyHash === ownerHash)
89
+ return o.filePath;
90
+ }
91
+ }
92
+ /* v8 ignore next */
93
+ return null;
94
+ }
95
+ /**
96
+ * Extract the package name from `Cargo.toml`'s `[package]` section.
97
+ * Strategy: line-grep for `name = "<value>"` inside the `[package]`
98
+ * section header. We intentionally do NOT pull in a TOML parser — this
99
+ * is a v1 limitation. Returns `null` when `Cargo.toml` is missing,
100
+ * unparseable, or has no `[package] name = …`.
101
+ *
102
+ * Edge cases NOT handled (deferred):
103
+ * - `[workspace]`-only roots without a `[package]` (Cargo workspace
104
+ * virtual-manifest); we'd need to recurse into `members = […]`.
105
+ * - `[package] name = 'single-quoted'` (TOML allows this; rare).
106
+ * - Multi-line table arrays / nested tables that re-open `[package]`.
107
+ * - Dev-dependencies / feature flags — irrelevant to resolution.
108
+ */
109
+ function readCargoPackageName(projectDirAbs) {
110
+ let content;
111
+ try {
112
+ content = readFileSync(join(projectDirAbs, 'Cargo.toml'), 'utf8');
113
+ }
114
+ catch {
115
+ // @fitness-ignore-next-line error-handling-quality -- missing/unreadable Cargo.toml is the expected "no package name" signal for projects that aren't a Cargo crate; caller treats null as "skip package-name resolution".
116
+ return null;
117
+ }
118
+ let inPackage = false;
119
+ for (const rawLine of content.split('\n')) {
120
+ const line = rawLine.trim();
121
+ if (line.length === 0 || line.startsWith('#'))
122
+ continue;
123
+ if (line.startsWith('[')) {
124
+ // New section header — `[package]` enters, anything else exits.
125
+ inPackage = /^\[package\]\s*$/.test(line);
126
+ continue;
127
+ }
128
+ if (!inPackage)
129
+ continue;
130
+ const match = /^name\s*=\s*"([^"]+)"\s*$/.exec(line);
131
+ if (match)
132
+ return match[1] ?? null;
133
+ }
134
+ return null;
135
+ }
136
+ /**
137
+ * Map a project-relative POSIX filePath to its Rust crate module path.
138
+ * Returns `null` when the file isn't recognizable as a crate module
139
+ * (e.g. lives outside `src/`, or doesn't follow the canonical layout).
140
+ *
141
+ * Conventions handled:
142
+ * - `src/lib.rs` → `crate`
143
+ * - `src/main.rs` → `crate`
144
+ * - `src/<n>.rs` → `crate::<n>`
145
+ * - `src/<n>/mod.rs` → `crate::<n>`
146
+ * - `src/<a>/<b>.rs` → `crate::<a>::<b>`
147
+ * - `src/<a>/<b>/mod.rs` → `crate::<a>::<b>`
148
+ *
149
+ * Files outside `src/` (e.g. `tests/it.rs`, `examples/foo.rs`,
150
+ * `benches/bench.rs`) return `null` — they're separate compilation
151
+ * units, not part of the library/binary crate's module tree.
152
+ */
153
+ function filePathToRustModulePath(filePath) {
154
+ if (!filePath.endsWith('.rs'))
155
+ return null;
156
+ if (!filePath.startsWith('src/') && filePath !== 'src.rs')
157
+ return null;
158
+ // Strip `src/` prefix.
159
+ const rel = filePath.slice('src/'.length);
160
+ if (rel === 'lib.rs' || rel === 'main.rs')
161
+ return 'crate';
162
+ // Strip trailing `.rs`.
163
+ const noExt = rel.slice(0, -'.rs'.length);
164
+ // Treat `…/mod` (from `…/mod.rs`) as the parent directory itself.
165
+ const segments = noExt.split('/');
166
+ if (segments.at(-1) === 'mod')
167
+ segments.pop();
168
+ if (segments.length === 0)
169
+ return 'crate';
170
+ return ['crate', ...segments].join('::');
171
+ }
172
+ /**
173
+ * Resolve one Rust `use`-specifier to its target module-init
174
+ * bodyHash(es). Returns `[]` for stdlib, third-party, globs, and
175
+ * unresolvable relative paths.
176
+ *
177
+ * Multi-target: at v1 every match is a single module (Rust modules
178
+ * aren't directory-spanning the way Go packages are), so the returned
179
+ * array is either `[]` or a single hash. The `readonly string[]` shape
180
+ * is kept for engine-model symmetry.
181
+ */
182
+ function resolveRustUseSpecifier(specifier, packageName, importerModulePath, moduleInitByModulePath) {
183
+ // Glob — documented v1 limitation. Skip resolution.
184
+ if (specifier.endsWith('::*') || specifier === '*')
185
+ return [];
186
+ const segments = specifier.split('::');
187
+ if (segments.length === 0 || segments[0] === undefined)
188
+ return [];
189
+ // Rewrite the head segment into an absolute `crate::…` path.
190
+ const absolute = rewriteToAbsoluteModulePath(segments, packageName, importerModulePath);
191
+ if (absolute === null)
192
+ return [];
193
+ return lookupRustModule(absolute, moduleInitByModulePath);
194
+ }
195
+ /**
196
+ * Rewrite a `use`-path's segments into the absolute `crate::…` form
197
+ * suitable for catalog lookup. Returns `null` when the path is external
198
+ * (stdlib, third-party crate other than the host package).
199
+ */
200
+ function rewriteToAbsoluteModulePath(segments, packageName, importerModulePath) {
201
+ const head = segments[0];
202
+ if (head === undefined) /* v8 ignore next */
203
+ return null;
204
+ if (head === 'crate') {
205
+ return segments;
206
+ }
207
+ if (head === 'self') {
208
+ // `self::x::y` — current module + remainder.
209
+ if (importerModulePath === null)
210
+ return null;
211
+ const current = importerModulePath.split('::');
212
+ return [...current, ...segments.slice(1)];
213
+ }
214
+ if (head === 'super') {
215
+ // Count consecutive leading `super` segments and walk up that many.
216
+ if (importerModulePath === null)
217
+ return null;
218
+ let supers = 0;
219
+ while (segments[supers] === 'super')
220
+ supers++;
221
+ const current = importerModulePath.split('::');
222
+ // Strip `supers` trailing module segments from the current path.
223
+ // Note: `current` always starts with `crate`, so we must not strip
224
+ // past index 1.
225
+ const remaining = current.slice(0, Math.max(1, current.length - supers));
226
+ return [...remaining, ...segments.slice(supers)];
227
+ }
228
+ // External crate reference, possibly the host package referring to
229
+ // itself by name (`<package-name>::foo` ≡ `crate::foo`).
230
+ if (packageName !== null && head === toRustIdent(packageName)) {
231
+ return ['crate', ...segments.slice(1)];
232
+ }
233
+ return null;
234
+ }
235
+ /**
236
+ * Look up a fully-qualified Rust module path against the catalog. The
237
+ * specifier may name a module OR an item inside a module (a type, fn,
238
+ * const, etc.). Tree-sitter can't distinguish the two, so we walk from
239
+ * the longest module-prefix match toward `crate`, returning the first
240
+ * hit.
241
+ */
242
+ function lookupRustModule(segments, moduleInitByModulePath) {
243
+ const cur = [...segments];
244
+ while (cur.length > 0) {
245
+ const key = cur.join('::');
246
+ const hash = moduleInitByModulePath.get(key);
247
+ if (hash !== undefined)
248
+ return [hash];
249
+ cur.pop();
250
+ }
251
+ return [];
252
+ }
253
+ /**
254
+ * Cargo package names allow `-` but Rust identifiers don't — Cargo
255
+ * substitutes `-` → `_` when synthesizing the crate's Rust-visible
256
+ * name. Mirror that.
257
+ */
258
+ function toRustIdent(packageName) {
259
+ return packageName.replaceAll('-', '_');
260
+ }
261
+ //# sourceMappingURL=resolve-dependencies.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-dependencies.js","sourceRoot":"","sources":["../src/resolve-dependencies.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Rust resolveCallSites — name-based catalog lookup with `impl`-block
3
+ * receiver-type context.
4
+ *
5
+ * Tree-sitter has no symbol table; we resolve by simple name. For
6
+ * each call site:
7
+ *
8
+ * 1. Decode the called expression. Five shapes matter:
9
+ * - `foo(args)` — call target is `foo`.
10
+ * - `obj.method(args)` — field_expression call target is
11
+ * the trailing `field_identifier`.
12
+ * - `Type::method(args)` — scoped_identifier; target is the
13
+ * trailing identifier.
14
+ * - `path::to::fn(args)` — same scoped_identifier shape.
15
+ * - `name!(args)` — macro_invocation target is the
16
+ * leading identifier.
17
+ *
18
+ * 2. Look up matching catalog entries. For method calls
19
+ * (`obj.method`), we narrow by `enclosingClass` if the receiver
20
+ * type is statically known (literal, simple-typed local). The
21
+ * narrow is best-effort, NOT type-aware — we don't track types
22
+ * across statements. With the narrow:
23
+ * - 1 method match in the receiver's impl → 'high' confidence
24
+ * ... actually no — even with narrowing, tree-sitter never
25
+ * produces 'high' for ordinary calls, because the receiver
26
+ * type itself is name-based. We use 'medium' for the
27
+ * narrowed case and 'low' for the un-narrowed case.
28
+ * Confidence ladder for plain calls:
29
+ * - 0 matches → `to: []`, resolution `'unknown'`, confidence `'low'`
30
+ * - 1 match → `to: [hash]`, resolution `'static'`, confidence `'medium'`
31
+ * - N matches → `to: [allHashes]`, resolution `'method-dispatch'`,
32
+ * confidence `'low'`
33
+ *
34
+ * Macros are emitted as edges with `resolution: 'unknown'` and
35
+ * `confidence: 'low'` since macros are rarely first-party functions
36
+ * in the catalog. Their value to the call-graph is letting
37
+ * `no-side-effect-path` see `println!` calls; the edge text carries
38
+ * the macro name for that match.
39
+ *
40
+ * Per I-4: this function does NOT mutate the input catalog.
41
+ */
42
+ import type { RustParsedProject } from './parse.js';
43
+ import type { ResolveInput, ResolveOutput } from '@opensip-cli/graph';
44
+ export declare function resolveCallSites(input: ResolveInput<RustParsedProject>): ResolveOutput;
45
+ //# sourceMappingURL=resolve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAaH,OAAO,KAAK,EAAkB,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,KAAK,EAKV,YAAY,EACZ,aAAa,EACd,MAAM,oBAAoB,CAAC;AAyB5B,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,YAAY,CAAC,iBAAiB,CAAC,GAAG,aAAa,CAwCtF"}
@@ -0,0 +1,262 @@
1
+ /**
2
+ * Rust resolveCallSites — name-based catalog lookup with `impl`-block
3
+ * receiver-type context.
4
+ *
5
+ * Tree-sitter has no symbol table; we resolve by simple name. For
6
+ * each call site:
7
+ *
8
+ * 1. Decode the called expression. Five shapes matter:
9
+ * - `foo(args)` — call target is `foo`.
10
+ * - `obj.method(args)` — field_expression call target is
11
+ * the trailing `field_identifier`.
12
+ * - `Type::method(args)` — scoped_identifier; target is the
13
+ * trailing identifier.
14
+ * - `path::to::fn(args)` — same scoped_identifier shape.
15
+ * - `name!(args)` — macro_invocation target is the
16
+ * leading identifier.
17
+ *
18
+ * 2. Look up matching catalog entries. For method calls
19
+ * (`obj.method`), we narrow by `enclosingClass` if the receiver
20
+ * type is statically known (literal, simple-typed local). The
21
+ * narrow is best-effort, NOT type-aware — we don't track types
22
+ * across statements. With the narrow:
23
+ * - 1 method match in the receiver's impl → 'high' confidence
24
+ * ... actually no — even with narrowing, tree-sitter never
25
+ * produces 'high' for ordinary calls, because the receiver
26
+ * type itself is name-based. We use 'medium' for the
27
+ * narrowed case and 'low' for the un-narrowed case.
28
+ * Confidence ladder for plain calls:
29
+ * - 0 matches → `to: []`, resolution `'unknown'`, confidence `'low'`
30
+ * - 1 match → `to: [hash]`, resolution `'static'`, confidence `'medium'`
31
+ * - N matches → `to: [allHashes]`, resolution `'method-dispatch'`,
32
+ * confidence `'low'`
33
+ *
34
+ * Macros are emitted as edges with `resolution: 'unknown'` and
35
+ * `confidence: 'low'` since macros are rarely first-party functions
36
+ * in the catalog. Their value to the call-graph is letting
37
+ * `no-side-effect-path` see `println!` calls; the edge text carries
38
+ * the macro name for that match.
39
+ *
40
+ * Per I-4: this function does NOT mutate the input catalog.
41
+ */
42
+ import { logger } from '@opensip-cli/core';
43
+ import { appendEdge, createMutableStats, pushCreationEdge, truncateForCallEdge, } from '@opensip-cli/graph';
44
+ import { isReturnValueDiscarded } from '@opensip-cli/graph-adapter-common';
45
+ import { resolveDependencies } from './resolve-dependencies.js';
46
+ function rustPosition(node, file) {
47
+ return {
48
+ line: node.startPosition.row + 1,
49
+ column: node.startPosition.column,
50
+ text: file.source.slice(node.startIndex, node.endIndex),
51
+ };
52
+ }
53
+ export function resolveCallSites(input) {
54
+ logger.info({ evt: 'graph.edges.start', module: 'graph:edges:rust' });
55
+ const index = buildIndex(input.catalog.functions);
56
+ const edgesByOwner = new Map();
57
+ const stats = createMutableStats();
58
+ const sink = { edgesByOwner, stats };
59
+ for (const r of input.callSites) {
60
+ const node = r.nodeRef;
61
+ const file = r.sourceFileRef;
62
+ if (r.kind === 'creation') {
63
+ if (r.childHash === undefined)
64
+ continue;
65
+ pushCreationEdge(rustPosition(node, file), r.ownerHash, r.childHash, sink);
66
+ continue;
67
+ }
68
+ pushCallEdge(node, file, r.ownerHash, index, sink);
69
+ }
70
+ const finalStats = {
71
+ totalCallSites: stats.totalCallSites,
72
+ resolvedHigh: stats.resolvedHigh,
73
+ resolvedMedium: stats.resolvedMedium,
74
+ resolvedLow: stats.resolvedLow,
75
+ unresolved: stats.unresolved,
76
+ };
77
+ logger.info({ evt: 'graph.edges.complete', module: 'graph:edges:rust', ...finalStats });
78
+ // Phase 4 (DEC-498): resolve dependency sites if any. Mirrors the
79
+ // Python adapter's relative-import handling, adapted to Rust's
80
+ // `crate::` / `super::` / `self::` path prefixes and Cargo's
81
+ // `src/lib.rs` / `src/main.rs` / `src/foo.rs` / `src/foo/mod.rs`
82
+ // module layout conventions.
83
+ const dependenciesByOwner = input.dependencySites && input.dependencySites.length > 0
84
+ ? resolveDependencies(input.dependencySites, input.catalog, input.projectDirAbs)
85
+ : undefined;
86
+ return dependenciesByOwner === undefined
87
+ ? { edgesByOwner, stats: finalStats }
88
+ : { edgesByOwner, dependenciesByOwner, stats: finalStats };
89
+ }
90
+ function buildIndex(functions) {
91
+ const all = new Map();
92
+ const methods = new Map();
93
+ for (const [name, occs] of Object.entries(functions)) {
94
+ if (!occs)
95
+ continue;
96
+ if (name.startsWith('<'))
97
+ continue;
98
+ const list = all.get(name) ?? [];
99
+ for (const o of occs) {
100
+ list.push(o);
101
+ if (o.enclosingClass !== null) {
102
+ const key = `${o.enclosingClass}::${o.simpleName}`;
103
+ const ml = methods.get(key) ?? [];
104
+ ml.push(o);
105
+ methods.set(key, ml);
106
+ }
107
+ }
108
+ all.set(name, list);
109
+ }
110
+ return { all, methods };
111
+ }
112
+ function pushCallEdge(node, file, ownerHash, index, sink) {
113
+ const { edgesByOwner, stats } = sink;
114
+ stats.totalCallSites++;
115
+ const target = decodeCallTarget(node);
116
+ const pos = rustPosition(node, file);
117
+ const truncated = truncateForCallEdge(pos.text);
118
+ const discarded = isReturnValueDiscarded(node);
119
+ const edge = resolveTarget(target, index, {
120
+ line: pos.line,
121
+ column: pos.column,
122
+ text: truncated,
123
+ discarded,
124
+ });
125
+ stats.apply(edge);
126
+ appendEdge(edgesByOwner, ownerHash, edge);
127
+ }
128
+ function decodeCallTarget(node) {
129
+ if (node.type === 'macro_invocation') {
130
+ const m = node.childForFieldName('macro') ?? node.namedChild(0);
131
+ if (!m)
132
+ return null;
133
+ return { name: m.text.split('::').pop() ?? m.text, receiverType: null, isMacro: true };
134
+ }
135
+ if (node.type !== 'call_expression')
136
+ return null;
137
+ const fn = node.childForFieldName('function');
138
+ if (!fn)
139
+ return null;
140
+ if (fn.type === 'identifier') {
141
+ return { name: fn.text, receiverType: null, isMacro: false };
142
+ }
143
+ if (fn.type === 'field_expression') {
144
+ const field = fn.childForFieldName('field');
145
+ if (!field)
146
+ return null;
147
+ return { name: field.text, receiverType: null, isMacro: false };
148
+ }
149
+ if (fn.type === 'scoped_identifier') {
150
+ const name = fn.childForFieldName('name') ?? fn.namedChild(fn.namedChildCount - 1);
151
+ if (!name)
152
+ return null;
153
+ const path = fn.childForFieldName('path');
154
+ const receiver = decodeReceiverPath(path);
155
+ return { name: name.text, receiverType: receiver, isMacro: false };
156
+ }
157
+ return null;
158
+ }
159
+ function decodeReceiverPath(path) {
160
+ if (!path)
161
+ return null;
162
+ // For `Type::name`, path is a `type_identifier` or `identifier`.
163
+ // For `mod::Type::name`, path is a `scoped_identifier` whose own
164
+ // trailing component is the type. We walk down the path looking for
165
+ // the last `type_identifier` / `identifier`.
166
+ if (path.type === 'type_identifier' || path.type === 'identifier')
167
+ return path.text;
168
+ if (path.type === 'scoped_identifier') {
169
+ const inner = path.childForFieldName('name') ?? path.namedChild(path.namedChildCount - 1);
170
+ return inner ? inner.text : null;
171
+ }
172
+ return null;
173
+ }
174
+ function resolveTarget(target, index, loc) {
175
+ if (target === null) {
176
+ return {
177
+ to: [],
178
+ line: loc.line,
179
+ column: loc.column,
180
+ resolution: 'unknown',
181
+ confidence: 'low',
182
+ text: loc.text,
183
+ discarded: loc.discarded,
184
+ };
185
+ }
186
+ // Macros: tag the edge for side-effect detection but mark unresolved.
187
+ // The edge text carries `name!` so rules can match against the
188
+ // primitive list (e.g. `println!`).
189
+ if (target.isMacro) {
190
+ return {
191
+ to: [],
192
+ line: loc.line,
193
+ column: loc.column,
194
+ resolution: 'unknown',
195
+ confidence: 'low',
196
+ text: `${target.name}! ${loc.text}`,
197
+ discarded: loc.discarded,
198
+ };
199
+ }
200
+ // Receiver-narrowed lookup if we have a Type::method shape.
201
+ if (target.receiverType !== null) {
202
+ const narrowed = index.methods.get(`${target.receiverType}::${target.name}`);
203
+ if (narrowed && narrowed.length > 0) {
204
+ const hashes = narrowed.map((o) => o.bodyHash);
205
+ return {
206
+ to: hashes,
207
+ line: loc.line,
208
+ column: loc.column,
209
+ resolution: hashes.length === 1 ? 'static' : 'method-dispatch',
210
+ confidence: 'medium',
211
+ text: loc.text,
212
+ discarded: loc.discarded,
213
+ };
214
+ }
215
+ // Receiver was named but no method — fall through to broad name lookup.
216
+ }
217
+ const matches = index.all.get(target.name);
218
+ if (!matches || matches.length === 0) {
219
+ return {
220
+ to: [],
221
+ line: loc.line,
222
+ column: loc.column,
223
+ resolution: 'unknown',
224
+ confidence: 'low',
225
+ text: loc.text,
226
+ discarded: loc.discarded,
227
+ };
228
+ }
229
+ if (matches.length === 1) {
230
+ const only = matches[0];
231
+ if (!only) {
232
+ return {
233
+ to: [],
234
+ line: loc.line,
235
+ column: loc.column,
236
+ resolution: 'unknown',
237
+ confidence: 'low',
238
+ text: loc.text,
239
+ discarded: loc.discarded,
240
+ };
241
+ }
242
+ return {
243
+ to: [only.bodyHash],
244
+ line: loc.line,
245
+ column: loc.column,
246
+ resolution: 'static',
247
+ confidence: 'medium',
248
+ text: loc.text,
249
+ discarded: loc.discarded,
250
+ };
251
+ }
252
+ return {
253
+ to: matches.map((o) => o.bodyHash),
254
+ line: loc.line,
255
+ column: loc.column,
256
+ resolution: 'method-dispatch',
257
+ confidence: 'low',
258
+ text: loc.text,
259
+ discarded: loc.discarded,
260
+ };
261
+ }
262
+ //# sourceMappingURL=resolve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Rust rule hints — language-specific signals for rules.
3
+ *
4
+ * Lands in PR 6 of plan docs/plans/10-graph-language-pluggability.md.
5
+ *
6
+ * `isTestFile` is file-path-based here for simplicity: `tests/`
7
+ * directory or `*_test.rs`. Note that Rust convention also supports
8
+ * `#[test]`-annotated functions in any file (commonly inside
9
+ * `#[cfg(test)] mod tests { … }`); the walk pass tags those
10
+ * occurrences' `inTestFile` flag as true based on the attribute, so
11
+ * `test-only-reachable` still works for them. The hint here is the
12
+ * file-level fallback for rules that consult the predicate directly.
13
+ */
14
+ import type { RuleHints } from '@opensip-cli/graph';
15
+ export declare const rustRuleHints: RuleHints;
16
+ //# sourceMappingURL=rule-hints.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-hints.d.ts","sourceRoot":"","sources":["../src/rule-hints.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAkCpD,eAAO,MAAM,aAAa,EAAE,SAK3B,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Rust rule hints — language-specific signals for rules.
3
+ *
4
+ * Lands in PR 6 of plan docs/plans/10-graph-language-pluggability.md.
5
+ *
6
+ * `isTestFile` is file-path-based here for simplicity: `tests/`
7
+ * directory or `*_test.rs`. Note that Rust convention also supports
8
+ * `#[test]`-annotated functions in any file (commonly inside
9
+ * `#[cfg(test)] mod tests { … }`); the walk pass tags those
10
+ * occurrences' `inTestFile` flag as true based on the attribute, so
11
+ * `test-only-reachable` still works for them. The hint here is the
12
+ * file-level fallback for rules that consult the predicate directly.
13
+ */
14
+ import { isTestFile } from './walk.js';
15
+ const RUST_SIDE_EFFECT_PRIMITIVES = [
16
+ 'println!',
17
+ 'eprintln!',
18
+ 'print!',
19
+ 'eprint!',
20
+ 'std::fs::read',
21
+ 'std::fs::write',
22
+ 'std::fs::remove_file',
23
+ 'std::fs::remove_dir',
24
+ 'std::fs::create_dir',
25
+ 'std::io::stdin',
26
+ 'std::io::stdout',
27
+ 'std::io::stderr',
28
+ 'std::process::exit',
29
+ 'std::process::abort',
30
+ 'std::env::set_var',
31
+ 'std::env::remove_var',
32
+ 'std::thread::sleep',
33
+ 'rand::random',
34
+ ];
35
+ // `raise` doesn't exist in Rust; the closest analogues are `panic!`,
36
+ // `return Err(...)`, and `?` — but only `panic!` is structural. Use
37
+ // the `panic!` macro shape.
38
+ const RUST_THROW_REGEX = /\bpanic!\s*\(/;
39
+ const RUST_GENERATED_FILE_PATTERNS = [
40
+ '**/target/**',
41
+ '**/build/**',
42
+ '**/*.generated.rs',
43
+ ];
44
+ export const rustRuleHints = {
45
+ isTestFile,
46
+ generatedFilePatterns: RUST_GENERATED_FILE_PATTERNS,
47
+ sideEffectPrimitives: RUST_SIDE_EFFECT_PRIMITIVES,
48
+ throwSyntaxRegex: RUST_THROW_REGEX,
49
+ };
50
+ //# sourceMappingURL=rule-hints.js.map