@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.
- package/LICENSE +202 -0
- package/NOTICE +8 -0
- package/README.md +31 -0
- package/dist/__tests__/cache-key.test.d.ts +9 -0
- package/dist/__tests__/cache-key.test.d.ts.map +1 -0
- package/dist/__tests__/cache-key.test.js +49 -0
- package/dist/__tests__/cache-key.test.js.map +1 -0
- package/dist/__tests__/depends-on-emission.test.d.ts +15 -0
- package/dist/__tests__/depends-on-emission.test.d.ts.map +1 -0
- package/dist/__tests__/depends-on-emission.test.js +266 -0
- package/dist/__tests__/depends-on-emission.test.js.map +1 -0
- package/dist/__tests__/discover.test.d.ts +8 -0
- package/dist/__tests__/discover.test.d.ts.map +1 -0
- package/dist/__tests__/discover.test.js +65 -0
- package/dist/__tests__/discover.test.js.map +1 -0
- package/dist/__tests__/parse.test.d.ts +8 -0
- package/dist/__tests__/parse.test.d.ts.map +1 -0
- package/dist/__tests__/parse.test.js +64 -0
- package/dist/__tests__/parse.test.js.map +1 -0
- package/dist/__tests__/resolve-dependencies-branches.test.d.ts +18 -0
- package/dist/__tests__/resolve-dependencies-branches.test.d.ts.map +1 -0
- package/dist/__tests__/resolve-dependencies-branches.test.js +194 -0
- package/dist/__tests__/resolve-dependencies-branches.test.js.map +1 -0
- package/dist/__tests__/resolve.test.d.ts +23 -0
- package/dist/__tests__/resolve.test.d.ts.map +1 -0
- package/dist/__tests__/resolve.test.js +476 -0
- package/dist/__tests__/resolve.test.js.map +1 -0
- package/dist/__tests__/walk-branches.test.d.ts +13 -0
- package/dist/__tests__/walk-branches.test.d.ts.map +1 -0
- package/dist/__tests__/walk-branches.test.js +124 -0
- package/dist/__tests__/walk-branches.test.js.map +1 -0
- package/dist/__tests__/walk.test.d.ts +11 -0
- package/dist/__tests__/walk.test.d.ts.map +1 -0
- package/dist/__tests__/walk.test.js +324 -0
- package/dist/__tests__/walk.test.js.map +1 -0
- package/dist/body-digest.d.ts +31 -0
- package/dist/body-digest.d.ts.map +1 -0
- package/dist/body-digest.js +136 -0
- package/dist/body-digest.js.map +1 -0
- package/dist/cache-key.d.ts +19 -0
- package/dist/cache-key.d.ts.map +1 -0
- package/dist/cache-key.js +20 -0
- package/dist/cache-key.js.map +1 -0
- package/dist/discover.d.ts +22 -0
- package/dist/discover.d.ts.map +1 -0
- package/dist/discover.js +33 -0
- package/dist/discover.js.map +1 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +56 -0
- package/dist/index.js.map +1 -0
- package/dist/parse.d.ts +17 -0
- package/dist/parse.d.ts.map +1 -0
- package/dist/parse.js +14 -0
- package/dist/parse.js.map +1 -0
- package/dist/resolve-dependencies.d.ts +13 -0
- package/dist/resolve-dependencies.d.ts.map +1 -0
- package/dist/resolve-dependencies.js +261 -0
- package/dist/resolve-dependencies.js.map +1 -0
- package/dist/resolve.d.ts +45 -0
- package/dist/resolve.d.ts.map +1 -0
- package/dist/resolve.js +262 -0
- package/dist/resolve.js.map +1 -0
- package/dist/rule-hints.d.ts +16 -0
- package/dist/rule-hints.d.ts.map +1 -0
- package/dist/rule-hints.js +50 -0
- package/dist/rule-hints.js.map +1 -0
- package/dist/walk.d.ts +47 -0
- package/dist/walk.d.ts.map +1 -0
- package/dist/walk.js +564 -0
- package/dist/walk.js.map +1 -0
- 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"}
|
package/dist/resolve.js
ADDED
|
@@ -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
|