@opensip-cli/graph-rust 0.1.10 → 0.1.12

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.
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Rust `use` / `extern crate` dependency-site collection for the walker.
3
+ */
4
+ import { namedChildrenOf } from '@opensip-cli/graph-adapter-common';
5
+ /** Walk top-level `use` and `extern crate` declarations as dependency sites. */
6
+ export function collectDependencySites(file, moduleInitHash, out) {
7
+ for (const stmt of namedChildrenOf(file.tree.rootNode)) {
8
+ if (stmt.type === 'use_declaration') {
9
+ collectFromUseDeclaration(stmt, file, moduleInitHash, out);
10
+ }
11
+ else if (stmt.type === 'extern_crate_declaration') {
12
+ collectFromExternCrate(stmt, file, moduleInitHash, out);
13
+ }
14
+ }
15
+ }
16
+ function collectFromUseDeclaration(decl, file, ownerHash, out) {
17
+ const body = pickUsePathNode(decl);
18
+ if (!body)
19
+ return;
20
+ const ctx = {
21
+ file,
22
+ ownerHash,
23
+ line: decl.startPosition.row + 1,
24
+ column: decl.startPosition.column,
25
+ };
26
+ emitFromUseSegment(body, [], ctx, out);
27
+ }
28
+ function pickUsePathNode(decl) {
29
+ for (let i = decl.namedChildCount - 1; i >= 0; i--) {
30
+ const c = decl.namedChild(i);
31
+ if (!c)
32
+ continue;
33
+ if (c.type === 'visibility_modifier')
34
+ continue;
35
+ return c;
36
+ }
37
+ /* v8 ignore next */
38
+ return null;
39
+ }
40
+ function emitFromUseSegment(node, prefix, ctx, out) {
41
+ switch (node.type) {
42
+ case 'scoped_identifier':
43
+ case 'identifier':
44
+ case 'crate':
45
+ case 'super':
46
+ case 'self': {
47
+ emitFromPathLeaf(node, prefix, ctx, out);
48
+ return;
49
+ }
50
+ case 'use_as_clause': {
51
+ emitFromUseAsClause(node, prefix, ctx, out);
52
+ return;
53
+ }
54
+ case 'use_wildcard': {
55
+ emitFromUseWildcard(node, prefix, ctx, out);
56
+ return;
57
+ }
58
+ case 'scoped_use_list': {
59
+ emitFromScopedUseList(node, prefix, ctx, out);
60
+ return;
61
+ }
62
+ case 'use_list': {
63
+ emitFromUseList(node, prefix, ctx, out);
64
+ return;
65
+ }
66
+ /* v8 ignore start */
67
+ default: {
68
+ emitFromUnknownUseShape(node, prefix, ctx, out);
69
+ return;
70
+ }
71
+ /* v8 ignore stop */
72
+ }
73
+ }
74
+ function emitFromPathLeaf(node, prefix, ctx, out) {
75
+ const segments = decodePathSegments(node);
76
+ pushDepSite([...prefix, ...segments], node, ctx, out);
77
+ }
78
+ function emitFromUseAsClause(node, prefix, ctx, out) {
79
+ const inner = node.namedChild(0);
80
+ if (inner) {
81
+ emitFromUseSegment(inner, prefix, ctx, out);
82
+ }
83
+ }
84
+ function emitFromUseWildcard(node, prefix, ctx, out) {
85
+ const inner = node.namedChild(0);
86
+ const segments = inner ? decodePathSegments(inner) : [];
87
+ pushDepSite([...prefix, ...segments, '*'], node, ctx, out);
88
+ }
89
+ // @graph-ignore-next-line graph:cycle -- intentional recursion over nested `use` scoped-list AST nodes
90
+ function emitFromScopedUseList(node, prefix, ctx, out) {
91
+ const split = splitScopedUseListChildren(node);
92
+ if (split.list === null)
93
+ return;
94
+ const newPrefix = [...prefix, ...split.pathSegs];
95
+ emitUseListItems(split.list, newPrefix, ctx, out);
96
+ }
97
+ function splitScopedUseListChildren(node) {
98
+ let pathSegs = [];
99
+ let list = null;
100
+ for (const c of namedChildrenOf(node)) {
101
+ if (c.type === 'use_list') {
102
+ list = c;
103
+ }
104
+ else if (list === null) {
105
+ pathSegs = decodePathSegments(c);
106
+ }
107
+ }
108
+ return { pathSegs, list };
109
+ }
110
+ function emitFromUseList(node, prefix, ctx, out) {
111
+ emitUseListItems(node, prefix, ctx, out);
112
+ }
113
+ function emitUseListItems(list, prefix, ctx, out) {
114
+ for (const item of namedChildrenOf(list)) {
115
+ if (item.type === 'self') {
116
+ pushDepSite([...prefix], item, ctx, out);
117
+ continue;
118
+ }
119
+ emitFromUseSegment(item, prefix, ctx, out);
120
+ }
121
+ }
122
+ /* v8 ignore start */
123
+ function emitFromUnknownUseShape(node, prefix, ctx, out) {
124
+ const text = node.text;
125
+ if (text.length > 0) {
126
+ pushDepSite([...prefix, text], node, ctx, out);
127
+ }
128
+ }
129
+ /* v8 ignore stop */
130
+ function decodePathSegments(node) {
131
+ if (node.type === 'identifier' ||
132
+ node.type === 'crate' ||
133
+ node.type === 'super' ||
134
+ node.type === 'self') {
135
+ return [node.text];
136
+ }
137
+ if (node.type === 'scoped_identifier') {
138
+ const out = [];
139
+ for (const c of namedChildrenOf(node)) {
140
+ out.push(...decodePathSegments(c));
141
+ }
142
+ return out;
143
+ }
144
+ /* v8 ignore next */
145
+ return [];
146
+ }
147
+ function pushDepSite(segments, node, ctx, out) {
148
+ if (segments.length === 0) {
149
+ /* v8 ignore next */
150
+ return;
151
+ }
152
+ out.push({
153
+ nodeRef: node,
154
+ sourceFileRef: ctx.file,
155
+ ownerHash: ctx.ownerHash,
156
+ specifier: segments.join('::'),
157
+ line: ctx.line,
158
+ column: ctx.column,
159
+ });
160
+ }
161
+ function collectFromExternCrate(decl, file, ownerHash, out) {
162
+ for (const c of namedChildrenOf(decl)) {
163
+ if (c.type === 'identifier') {
164
+ out.push({
165
+ nodeRef: decl,
166
+ sourceFileRef: file,
167
+ ownerHash,
168
+ specifier: c.text,
169
+ line: decl.startPosition.row + 1,
170
+ column: decl.startPosition.column,
171
+ });
172
+ return;
173
+ }
174
+ }
175
+ }
176
+ //# sourceMappingURL=walk-use-sites.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"walk-use-sites.js","sourceRoot":"","sources":["../src/walk-use-sites.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"walk.d.ts","sourceRoot":"","sources":["../src/walk.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAiBH,OAAO,KAAK,EAAkB,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,KAAK,EAIV,SAAS,EACT,UAAU,EACX,MAAM,oBAAoB,CAAC;AAO5B,QAAA,MAAQ,UAAU,0BAIhB,CAAC;AAEH,OAAO,EAAE,UAAU,EAAE,CAAC;AAEtB,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,CAAC,iBAAiB,CAAC,GAAG,UAAU,CAE3E"}
1
+ {"version":3,"file":"walk.d.ts","sourceRoot":"","sources":["../src/walk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAuBH,OAAO,KAAK,EAAkB,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAOhE,QAAA,MAAQ,UAAU,0BAIhB,CAAC;AAEH,OAAO,EAAE,UAAU,EAAE,CAAC;AAEtB,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,CAAC,iBAAiB,CAAC,GAAG,UAAU,CAE3E"}
package/dist/walk.js CHANGED
@@ -1,6 +1,3 @@
1
- // @fitness-ignore-file file-length-limit -- tree-sitter language walker spanning AST cases; cohesive grammar-driven dispatch already split (body-digest, walk-metadata extracted by earlier pass), further split would fragment per-node logic.
2
- // @fitness-ignore-file context-mutation -- `ctx: WalkCtx` here is a function-scoped traversal accumulator (callSites array, occurrence sink, parser refs) threaded through the AST walk, NOT a shared request/execution context. `ctx.callSites.push(...)` is the intended local-accumulator append. The check's `LOCAL_DECLARATION_PATTERNS` heuristic doesn't see it because `ctx` arrives as a typed parameter, not via `const ctx = …`.
3
- // @fitness-ignore-file performance-anti-patterns -- spread used to flatten AST child nodes during tree-sitter walk; bounded by node arity at each step
4
1
  /**
5
2
  * Rust walkProject — emit FunctionOccurrences + CallSiteRecords.
6
3
  *
@@ -43,8 +40,10 @@
43
40
  * this (you can have `#[cfg(test)] mod tests` in any module).
44
41
  */
45
42
  import { relative, sep } from 'node:path';
46
- import { childrenOf, makeFileClassifier, namedChildrenOf, nameOf, record, runWalk, synthesizeModuleInit as buildModuleInit, } from '@opensip-cli/graph-adapter-common';
47
- import { digestRustBody, digestSyntheticBody } from './body-digest.js';
43
+ import { childrenOf, makeFileClassifier, record, runWalk, synthesizeModuleInit as buildModuleInit, } from '@opensip-cli/graph-adapter-common';
44
+ import { digestSyntheticBody } from './body-digest.js';
45
+ import { buildClosureOccurrence, buildFunctionOccurrence, implTargetName, } from './walk-helpers.js';
46
+ import { collectDependencySites } from './walk-use-sites.js';
48
47
  const TEST_PATH_RE = /(?:^|\/)tests?\//;
49
48
  const TEST_FILE_NAME_RE = /(?:^|\/)[^/]*_test\.rs$/;
50
49
  const GENERATED_PATH_RE = /\btarget\/|\.generated\./;
@@ -72,9 +71,6 @@ function walkFile(absPath, file, projectDirAbs, sinks) {
72
71
  qualifiedName: `${qualifiedBase}::<module-init>`,
73
72
  });
74
73
  record(out, moduleInit);
75
- // Phase 4 (DEC-498): walk top-level `use` (and `extern crate`)
76
- // declarations as dependency sites. Owner is the file's synthesized
77
- // module-init occurrence.
78
74
  collectDependencySites(file, moduleInit.bodyHash, dependencySites);
79
75
  const ctx = {
80
76
  file,
@@ -88,241 +84,7 @@ function walkFile(absPath, file, projectDirAbs, sinks) {
88
84
  for (const child of childrenOf(file.tree.rootNode))
89
85
  visit(child, initialFrame, ctx);
90
86
  }
91
- /**
92
- * Walk a Rust file's top-level `use_declaration` and `extern_crate_declaration`
93
- * nodes; emit one `DependencySiteRecord` per terminal path. Phase 4 of
94
- * opensip's substrate consolidation (DEC-498).
95
- *
96
- * `use_declaration`'s single named child is one of:
97
- *
98
- * - `scoped_identifier` — e.g. `use std::collections::HashMap;` →
99
- * emit specifier `'std::collections::HashMap'`.
100
- * - `identifier` — e.g. `use foo;` → emit specifier `'foo'`.
101
- * - `use_as_clause` — e.g. `use std::collections::HashMap as Map;` →
102
- * extract the underlying path (LHS of `as`); alias is dropped.
103
- * - `scoped_use_list` — grouped form. Combines a path prefix with a
104
- * `use_list` of one-or-more children. Each child may itself be a
105
- * `scoped_use_list` (nested groups), `scoped_identifier`,
106
- * `identifier`, `use_as_clause`, `use_wildcard`, or `self` (bare
107
- * `self` inside the list refers to the prefix itself).
108
- * - `use_list` — bare `{a, b}` without prefix (rare at top level —
109
- * usually appears under a `scoped_use_list`).
110
- * - `use_wildcard` — e.g. `use std::prelude::v1::*;` → emit specifier
111
- * ending in `::*`; resolver treats globs as unresolved (no single
112
- * module target). Documented v1 limitation.
113
- *
114
- * `extern_crate_declaration` — legacy Rust 2015-edition form (mostly
115
- * gone in 2018+). `extern crate foo;` → emit specifier `'foo'`.
116
- *
117
- * Visibility modifiers (`pub use ...`) and the `use`/`extern`/`crate`
118
- * keywords are ignored — we emit one dep site per import target.
119
- *
120
- * Out of scope at v1:
121
- * - Conditional imports inside function bodies (`fn f() { use foo; }`).
122
- * Rust permits these; the walker only inspects file top-level.
123
- * - `use ::absolute::path;` (leading `::`) — uncommon; would be
124
- * emitted with the leading separator stripped.
125
- */
126
- function collectDependencySites(file, moduleInitHash, out) {
127
- for (const stmt of namedChildrenOf(file.tree.rootNode)) {
128
- if (stmt.type === 'use_declaration') {
129
- collectFromUseDeclaration(stmt, file, moduleInitHash, out);
130
- }
131
- else if (stmt.type === 'extern_crate_declaration') {
132
- collectFromExternCrate(stmt, file, moduleInitHash, out);
133
- }
134
- }
135
- }
136
- function collectFromUseDeclaration(decl, file, ownerHash, out) {
137
- // The path-bearing child is the last named child (skipping
138
- // visibility_modifier on `pub use ...`).
139
- const body = pickUsePathNode(decl);
140
- if (!body)
141
- return;
142
- const ctx = {
143
- file,
144
- ownerHash,
145
- line: decl.startPosition.row + 1,
146
- column: decl.startPosition.column,
147
- out,
148
- };
149
- emitFromUseSegment(body, [], ctx);
150
- }
151
- function pickUsePathNode(decl) {
152
- // Walk named children in reverse, taking the first non-visibility node.
153
- for (let i = decl.namedChildCount - 1; i >= 0; i--) {
154
- const c = decl.namedChild(i);
155
- if (!c)
156
- continue;
157
- if (c.type === 'visibility_modifier')
158
- continue;
159
- return c;
160
- }
161
- /* v8 ignore next */
162
- return null;
163
- }
164
- /**
165
- * Walk one path-bearing sub-node of a use declaration. `prefix` is the
166
- * canonical path segments accumulated from enclosing `scoped_use_list`
167
- * groups. The function dispatches by node type and emits one
168
- * `DependencySiteRecord` per terminal path.
169
- */
170
- function emitFromUseSegment(node, prefix, ctx) {
171
- switch (node.type) {
172
- case 'scoped_identifier':
173
- case 'identifier':
174
- case 'crate':
175
- case 'super':
176
- case 'self': {
177
- emitFromPathLeaf(node, prefix, ctx);
178
- return;
179
- }
180
- case 'use_as_clause': {
181
- emitFromUseAsClause(node, prefix, ctx);
182
- return;
183
- }
184
- case 'use_wildcard': {
185
- emitFromUseWildcard(node, prefix, ctx);
186
- return;
187
- }
188
- case 'scoped_use_list': {
189
- emitFromScopedUseList(node, prefix, ctx);
190
- return;
191
- }
192
- case 'use_list': {
193
- emitFromUseList(node, prefix, ctx);
194
- return;
195
- }
196
- /* v8 ignore start */
197
- default: {
198
- emitFromUnknownUseShape(node, prefix, ctx);
199
- return;
200
- }
201
- /* v8 ignore stop */
202
- }
203
- }
204
- function emitFromPathLeaf(node, prefix, ctx) {
205
- const segments = decodePathSegments(node);
206
- pushDepSite([...prefix, ...segments], node, ctx);
207
- }
208
- function emitFromUseAsClause(node, prefix, ctx) {
209
- // `<path> as <alias>` — emit the underlying path only.
210
- const inner = node.namedChild(0);
211
- if (inner) {
212
- emitFromUseSegment(inner, prefix, ctx);
213
- }
214
- }
215
- function emitFromUseWildcard(node, prefix, ctx) {
216
- // `<path>::*` — single emission with `*` as the trailing segment.
217
- // The grammar nests the path under the wildcard (single named
218
- // child); we append `'*'` to the segments.
219
- const inner = node.namedChild(0);
220
- const segments = inner ? decodePathSegments(inner) : [];
221
- pushDepSite([...prefix, ...segments, '*'], node, ctx);
222
- }
223
- // @graph-ignore-next-line graph:cycle -- intentional recursion over nested `use` scoped-list AST nodes (path::{a, b::{c}})
224
- function emitFromScopedUseList(node, prefix, ctx) {
225
- // `<path>::{<list>}` — combine path + each list child.
226
- // Children order: a path-like (identifier / scoped_identifier /
227
- // crate / super / self) then a `use_list`.
228
- const split = splitScopedUseListChildren(node);
229
- if (split.list === null)
230
- return;
231
- const newPrefix = [...prefix, ...split.pathSegs];
232
- emitUseListItems(split.list, newPrefix, ctx);
233
- }
234
- function splitScopedUseListChildren(node) {
235
- let pathSegs = [];
236
- let list = null;
237
- for (const c of namedChildrenOf(node)) {
238
- if (c.type === 'use_list') {
239
- list = c;
240
- }
241
- else if (list === null) {
242
- pathSegs = decodePathSegments(c);
243
- }
244
- }
245
- return { pathSegs, list };
246
- }
247
- function emitFromUseList(node, prefix, ctx) {
248
- // Bare `{a, b}` — uncommon at top level; descend with current prefix.
249
- emitUseListItems(node, prefix, ctx);
250
- }
251
- function emitUseListItems(list, prefix, ctx) {
252
- for (const item of namedChildrenOf(list)) {
253
- if (item.type === 'self') {
254
- // `use a::b::{self, X}` — `self` refers to the parent path,
255
- // i.e. emit the prefix itself.
256
- pushDepSite([...prefix], item, ctx);
257
- continue;
258
- }
259
- emitFromUseSegment(item, prefix, ctx);
260
- }
261
- }
262
- /* v8 ignore start */
263
- function emitFromUnknownUseShape(node, prefix, ctx) {
264
- // Unknown shape — defensive fallback: emit raw text as a single
265
- // segment so downstream attribution still has something to show.
266
- const text = node.text;
267
- if (text.length > 0) {
268
- pushDepSite([...prefix, text], node, ctx);
269
- }
270
- }
271
- /* v8 ignore stop */
272
- /**
273
- * Decode a path-bearing node into canonical `::`-separated segments.
274
- * Accepts `scoped_identifier` (recursive), `identifier`, `crate`,
275
- * `super`, `self`. Returns `[]` for unknown shapes (caller skips).
276
- */
277
- function decodePathSegments(node) {
278
- if (node.type === 'identifier' ||
279
- node.type === 'crate' ||
280
- node.type === 'super' ||
281
- node.type === 'self') {
282
- return [node.text];
283
- }
284
- if (node.type === 'scoped_identifier') {
285
- const out = [];
286
- for (const c of namedChildrenOf(node)) {
287
- out.push(...decodePathSegments(c));
288
- }
289
- return out;
290
- }
291
- /* v8 ignore next */
292
- return [];
293
- }
294
- function pushDepSite(segments, node, ctx) {
295
- if (segments.length === 0) {
296
- /* v8 ignore next */
297
- return;
298
- }
299
- ctx.out.push({
300
- nodeRef: node,
301
- sourceFileRef: ctx.file,
302
- ownerHash: ctx.ownerHash,
303
- specifier: segments.join('::'),
304
- line: ctx.line,
305
- column: ctx.column,
306
- });
307
- }
308
- function collectFromExternCrate(decl, file, ownerHash, out) {
309
- // `extern crate <name>;` or `extern crate <name> as <alias>;`. The
310
- // crate name is the first identifier (not the `crate` keyword token).
311
- for (const c of namedChildrenOf(decl)) {
312
- if (c.type === 'identifier') {
313
- out.push({
314
- nodeRef: decl,
315
- sourceFileRef: file,
316
- ownerHash,
317
- specifier: c.text,
318
- line: decl.startPosition.row + 1,
319
- column: decl.startPosition.column,
320
- });
321
- return;
322
- }
323
- }
324
- }
325
- // @graph-ignore-next-line graph:cycle -- intentional recursive-descent AST visitor; the cycle is the traversal (visit re-enters via the impl/item helpers)
87
+ // @graph-ignore-next-line graph:cycle -- intentional recursive-descent AST visitor
326
88
  function visit(node, frame, ctx) {
327
89
  if (node.type === 'impl_item') {
328
90
  visitImpl(node, frame, ctx);
@@ -348,9 +110,6 @@ function visit(node, frame, ctx) {
348
110
  }
349
111
  function visitImpl(node, frame, ctx) {
350
112
  const typeName = implTargetName(node);
351
- // Don't emit a function for the `impl` block itself — its body is a
352
- // declaration list whose function_items are emitted as methods. Keep
353
- // module-init as the owner; descend with impl context.
354
113
  const childFrame = { ownerHash: frame.ownerHash, enclosingImpl: typeName };
355
114
  for (const child of childrenOf(node))
356
115
  visit(child, childFrame, ctx);
@@ -387,178 +146,4 @@ function visitClosure(node, frame, ctx) {
387
146
  }
388
147
  return true;
389
148
  }
390
- function buildFunctionOccurrence(node, frame, ctx) {
391
- const name = nameOf(node) ?? '<anon-fn>';
392
- const digest = digestRustBody(ctx.file.source.slice(node.startIndex, node.endIndex));
393
- const isTest = ctx.fileInTestFile || hasTestAttribute(node);
394
- const kind = classifyRustFunctionKind(name, frame.enclosingImpl);
395
- const qualifiedBase = ctx.filePathProjectRel.replace(/\.rs$/, '').split('/').join('::');
396
- const qualifiedName = frame.enclosingImpl === null
397
- ? `${qualifiedBase}::${name}`
398
- : `${qualifiedBase}::${frame.enclosingImpl}::${name}`;
399
- return {
400
- bodyHash: digest.hash,
401
- bodySize: digest.size,
402
- simpleName: name,
403
- qualifiedName,
404
- filePath: ctx.filePathProjectRel,
405
- line: node.startPosition.row + 1,
406
- column: node.startPosition.column,
407
- endLine: node.endPosition.row + 1,
408
- kind,
409
- params: extractParams(node),
410
- returnType: null,
411
- enclosingClass: frame.enclosingImpl,
412
- decorators: extractAttributes(node),
413
- visibility: classifyVisibility(node),
414
- inTestFile: isTest,
415
- definedInGenerated: ctx.definedInGenerated,
416
- calls: [],
417
- };
418
- }
419
- function buildClosureOccurrence(node, ctx) {
420
- const digest = digestRustBody(ctx.file.source.slice(node.startIndex, node.endIndex));
421
- const startLine = node.startPosition.row + 1;
422
- const startCol = node.startPosition.column;
423
- const simpleName = `<arrow:${ctx.filePathProjectRel}:${String(startLine)}:${String(startCol)}>`;
424
- const qualifiedBase = ctx.filePathProjectRel.replace(/\.rs$/, '').split('/').join('::');
425
- return {
426
- bodyHash: digest.hash,
427
- bodySize: digest.size,
428
- simpleName,
429
- qualifiedName: `${qualifiedBase}::<closure:${String(startLine)}:${String(startCol)}>`,
430
- filePath: ctx.filePathProjectRel,
431
- line: startLine,
432
- column: startCol,
433
- endLine: node.endPosition.row + 1,
434
- kind: 'arrow',
435
- params: extractClosureParams(node),
436
- returnType: null,
437
- enclosingClass: null,
438
- decorators: [],
439
- visibility: 'private',
440
- inTestFile: ctx.fileInTestFile,
441
- definedInGenerated: ctx.definedInGenerated,
442
- calls: [],
443
- };
444
- }
445
- // ── helpers ───────────────────────────────────────────────────────
446
- function implTargetName(node) {
447
- const ty = node.childForFieldName('type');
448
- if (ty)
449
- return ty.text;
450
- /* v8 ignore start -- defensive: tree-sitter-rust always exposes the `type`
451
- field on a well-formed impl_item, so these fallbacks fire only on
452
- malformed/partial ASTs that valid Rust source doesn't produce. */
453
- // Fallback: first type_identifier child.
454
- for (const c of namedChildrenOf(node)) {
455
- if (c.type === 'type_identifier' || c.type === 'generic_type')
456
- return c.text;
457
- }
458
- return '<anon-impl>';
459
- /* v8 ignore stop */
460
- }
461
- function classifyVisibility(node) {
462
- for (const c of childrenOf(node)) {
463
- if (c.type === 'visibility_modifier')
464
- return 'exported';
465
- }
466
- return 'module-local';
467
- }
468
- function extractParams(node) {
469
- const params = node.childForFieldName('parameters');
470
- if (!params)
471
- return [];
472
- return collectParamEntries(params);
473
- }
474
- // Closures use the same `parameters` field as `function_item`; an alias
475
- // keeps call-site names readable without duplicating the body
476
- // (sonarjs/no-identical-functions).
477
- const extractClosureParams = extractParams;
478
- function collectParamEntries(params) {
479
- const out = [];
480
- for (const child of namedChildrenOf(params)) {
481
- const param = decodeParam(child);
482
- if (param)
483
- out.push(param);
484
- }
485
- return out;
486
- }
487
- function decodeParam(child) {
488
- switch (child.type) {
489
- case 'self_parameter': {
490
- return { name: 'self', optional: false, rest: false };
491
- }
492
- case 'parameter': {
493
- const pat = child.childForFieldName('pattern') ?? child.namedChild(0);
494
- if (!pat)
495
- return null;
496
- return { name: pat.text, optional: false, rest: false };
497
- }
498
- /* v8 ignore start -- defensive: function_item / impl-method params arrive as
499
- `self_parameter` / `parameter` nodes; the bare-`identifier` (closure-shaped)
500
- and unexpected-node-kind fallbacks guard AST shapes the walk's param paths
501
- don't reach. */
502
- case 'identifier': {
503
- // Closure params often appear as bare identifiers.
504
- return { name: child.text, optional: false, rest: false };
505
- }
506
- default: {
507
- return null;
508
- }
509
- /* v8 ignore stop */
510
- }
511
- }
512
- function extractAttributes(node) {
513
- const out = [];
514
- // Attributes precede the function_item as siblings inside the parent.
515
- // tree-sitter-rust models them as `attribute_item` nodes preceding
516
- // the function_item, OR (more commonly in practice) as
517
- // `attribute_item` children of the function_item's parent that
518
- // appear before the function_item by source position.
519
- for (const c of childrenOf(node)) {
520
- if (c.type === 'attribute_item' || c.type === 'inner_attribute_item') {
521
- out.push(c.text.trim());
522
- }
523
- }
524
- // Also scan preceding siblings (attribute_item is structurally a
525
- // sibling of function_item under most parents).
526
- const parent = node.parent;
527
- if (parent) {
528
- for (const sib of parent.children) {
529
- // web-tree-sitter returns fresh Node wrappers per access, so the
530
- // `node` handle passed in is never reference-identical to its twin
531
- // in `parent.children`. Compare by stable byte offset instead of
532
- // `===` (which would never match → scan past `node` and wrongly
533
- // attribute later siblings' attributes to it).
534
- if (sib === null || sib.startIndex >= node.startIndex)
535
- break;
536
- if (sib.type === 'attribute_item' || sib.type === 'inner_attribute_item') {
537
- out.push(sib.text.trim());
538
- }
539
- }
540
- }
541
- // Dedupe.
542
- return [...new Set(out)];
543
- }
544
- function hasTestAttribute(node) {
545
- const attrs = extractAttributes(node);
546
- for (const a of attrs) {
547
- if (a.includes('#[test]'))
548
- return true;
549
- /* v8 ignore next */
550
- if (a.includes('cfg(test)'))
551
- return true;
552
- }
553
- return false;
554
- }
555
- // Body digest helpers (stripRustComments, normalizeWhitespace, sha256,
556
- // digestRustBody, digestSyntheticBody, BodyDigest) live in body-digest.ts.
557
- function classifyRustFunctionKind(name, enclosingImpl) {
558
- if (enclosingImpl === null)
559
- return 'function-declaration';
560
- if (name === 'new')
561
- return 'constructor';
562
- return 'method';
563
- }
564
149
  //# sourceMappingURL=walk.js.map