@optave/codegraph 3.10.0 → 3.11.1
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/README.md +40 -33
- package/dist/ast-analysis/engine.d.ts.map +1 -1
- package/dist/ast-analysis/engine.js +91 -60
- package/dist/ast-analysis/engine.js.map +1 -1
- package/dist/ast-analysis/rules/index.d.ts.map +1 -1
- package/dist/ast-analysis/rules/index.js +77 -0
- package/dist/ast-analysis/rules/index.js.map +1 -1
- package/dist/ast-analysis/visitor-utils.d.ts +3 -0
- package/dist/ast-analysis/visitor-utils.d.ts.map +1 -1
- package/dist/ast-analysis/visitor-utils.js +83 -49
- package/dist/ast-analysis/visitor-utils.js.map +1 -1
- package/dist/ast-analysis/visitors/ast-store-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/ast-store-visitor.js +78 -62
- package/dist/ast-analysis/visitors/ast-store-visitor.js.map +1 -1
- package/dist/ast-analysis/visitors/dataflow-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/dataflow-visitor.js +61 -42
- package/dist/ast-analysis/visitors/dataflow-visitor.js.map +1 -1
- package/dist/cli/commands/audit.js +1 -1
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/build.js +2 -0
- package/dist/cli/commands/build.js.map +1 -1
- package/dist/cli/commands/check.js +1 -1
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/children.js +1 -1
- package/dist/cli/commands/children.js.map +1 -1
- package/dist/cli/commands/diff-impact.js +1 -1
- package/dist/cli/commands/diff-impact.js.map +1 -1
- package/dist/cli/commands/embed.d.ts.map +1 -1
- package/dist/cli/commands/embed.js +49 -4
- package/dist/cli/commands/embed.js.map +1 -1
- package/dist/cli/commands/roles.js +1 -1
- package/dist/cli/commands/roles.js.map +1 -1
- package/dist/cli/commands/structure.js +1 -1
- package/dist/cli/commands/structure.js.map +1 -1
- package/dist/cli/shared/options.js +1 -1
- package/dist/cli/shared/options.js.map +1 -1
- package/dist/db/connection.d.ts.map +1 -1
- package/dist/db/connection.js +8 -0
- package/dist/db/connection.js.map +1 -1
- package/dist/domain/analysis/dependencies.d.ts.map +1 -1
- package/dist/domain/analysis/dependencies.js +106 -80
- package/dist/domain/analysis/dependencies.js.map +1 -1
- package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
- package/dist/domain/analysis/fn-impact.js +77 -52
- package/dist/domain/analysis/fn-impact.js.map +1 -1
- package/dist/domain/analysis/module-map.d.ts.map +1 -1
- package/dist/domain/analysis/module-map.js +132 -121
- package/dist/domain/analysis/module-map.js.map +1 -1
- package/dist/domain/graph/builder/helpers.d.ts +4 -4
- package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
- package/dist/domain/graph/builder/helpers.js +47 -33
- package/dist/domain/graph/builder/helpers.js.map +1 -1
- package/dist/domain/graph/builder/incremental.d.ts +6 -6
- package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
- package/dist/domain/graph/builder/incremental.js +148 -99
- package/dist/domain/graph/builder/incremental.js.map +1 -1
- package/dist/domain/graph/builder/pipeline.d.ts +1 -0
- package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
- package/dist/domain/graph/builder/pipeline.js +23 -637
- package/dist/domain/graph/builder/pipeline.js.map +1 -1
- package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/build-edges.js +141 -98
- package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
- package/dist/domain/graph/builder/stages/build-structure.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/build-structure.js +82 -65
- package/dist/domain/graph/builder/stages/build-structure.js.map +1 -1
- package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/detect-changes.js +84 -56
- package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
- package/dist/domain/graph/builder/stages/finalize.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/finalize.js +60 -51
- package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
- package/dist/domain/graph/builder/stages/insert-nodes.d.ts +8 -6
- package/dist/domain/graph/builder/stages/insert-nodes.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/insert-nodes.js +107 -122
- package/dist/domain/graph/builder/stages/insert-nodes.js.map +1 -1
- package/dist/domain/graph/builder/stages/native-db-lifecycle.d.ts +14 -0
- package/dist/domain/graph/builder/stages/native-db-lifecycle.d.ts.map +1 -0
- package/dist/domain/graph/builder/stages/native-db-lifecycle.js +77 -0
- package/dist/domain/graph/builder/stages/native-db-lifecycle.js.map +1 -0
- package/dist/domain/graph/builder/stages/native-orchestrator.d.ts +62 -0
- package/dist/domain/graph/builder/stages/native-orchestrator.d.ts.map +1 -0
- package/dist/domain/graph/builder/stages/native-orchestrator.js +747 -0
- package/dist/domain/graph/builder/stages/native-orchestrator.js.map +1 -0
- package/dist/domain/graph/builder/stages/resolve-imports.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/resolve-imports.js +73 -22
- package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
- package/dist/domain/graph/cycles.d.ts +6 -4
- package/dist/domain/graph/cycles.d.ts.map +1 -1
- package/dist/domain/graph/cycles.js +50 -55
- package/dist/domain/graph/cycles.js.map +1 -1
- package/dist/domain/graph/journal.d.ts.map +1 -1
- package/dist/domain/graph/journal.js +89 -70
- package/dist/domain/graph/journal.js.map +1 -1
- package/dist/domain/graph/watcher.d.ts.map +1 -1
- package/dist/domain/graph/watcher.js +28 -20
- package/dist/domain/graph/watcher.js.map +1 -1
- package/dist/domain/parser.d.ts +12 -23
- package/dist/domain/parser.d.ts.map +1 -1
- package/dist/domain/parser.js +153 -80
- package/dist/domain/parser.js.map +1 -1
- package/dist/domain/search/generator.d.ts +3 -1
- package/dist/domain/search/generator.d.ts.map +1 -1
- package/dist/domain/search/generator.js +68 -45
- package/dist/domain/search/generator.js.map +1 -1
- package/dist/domain/search/models.d.ts +18 -0
- package/dist/domain/search/models.d.ts.map +1 -1
- package/dist/domain/search/models.js +72 -4
- package/dist/domain/search/models.js.map +1 -1
- package/dist/domain/search/search/hybrid.d.ts.map +1 -1
- package/dist/domain/search/search/hybrid.js +49 -40
- package/dist/domain/search/search/hybrid.js.map +1 -1
- package/dist/domain/search/search/semantic.d.ts.map +1 -1
- package/dist/domain/search/search/semantic.js +69 -49
- package/dist/domain/search/search/semantic.js.map +1 -1
- package/dist/domain/wasm-worker-entry.js +209 -137
- package/dist/domain/wasm-worker-entry.js.map +1 -1
- package/dist/extractors/c.js +25 -6
- package/dist/extractors/c.js.map +1 -1
- package/dist/extractors/cpp.js +47 -6
- package/dist/extractors/cpp.js.map +1 -1
- package/dist/extractors/cuda.js +90 -14
- package/dist/extractors/cuda.js.map +1 -1
- package/dist/extractors/elixir.js +108 -4
- package/dist/extractors/elixir.js.map +1 -1
- package/dist/extractors/erlang.js +56 -20
- package/dist/extractors/erlang.js.map +1 -1
- package/dist/extractors/fsharp.d.ts +7 -0
- package/dist/extractors/fsharp.d.ts.map +1 -1
- package/dist/extractors/fsharp.js +94 -0
- package/dist/extractors/fsharp.js.map +1 -1
- package/dist/extractors/gleam.d.ts.map +1 -1
- package/dist/extractors/gleam.js +29 -33
- package/dist/extractors/gleam.js.map +1 -1
- package/dist/extractors/groovy.js +41 -1
- package/dist/extractors/groovy.js.map +1 -1
- package/dist/extractors/haskell.js +48 -4
- package/dist/extractors/haskell.js.map +1 -1
- package/dist/extractors/helpers.d.ts +79 -1
- package/dist/extractors/helpers.d.ts.map +1 -1
- package/dist/extractors/helpers.js +137 -0
- package/dist/extractors/helpers.js.map +1 -1
- package/dist/extractors/java.d.ts.map +1 -1
- package/dist/extractors/java.js +37 -49
- package/dist/extractors/java.js.map +1 -1
- package/dist/extractors/javascript.d.ts.map +1 -1
- package/dist/extractors/javascript.js +44 -44
- package/dist/extractors/javascript.js.map +1 -1
- package/dist/extractors/julia.js +198 -74
- package/dist/extractors/julia.js.map +1 -1
- package/dist/extractors/kotlin.js +4 -0
- package/dist/extractors/kotlin.js.map +1 -1
- package/dist/extractors/objc.js +184 -47
- package/dist/extractors/objc.js.map +1 -1
- package/dist/extractors/python.js +7 -4
- package/dist/extractors/python.js.map +1 -1
- package/dist/extractors/r.d.ts.map +1 -1
- package/dist/extractors/r.js +103 -87
- package/dist/extractors/r.js.map +1 -1
- package/dist/extractors/scala.d.ts.map +1 -1
- package/dist/extractors/scala.js +18 -32
- package/dist/extractors/scala.js.map +1 -1
- package/dist/extractors/solidity.d.ts.map +1 -1
- package/dist/extractors/solidity.js +55 -69
- package/dist/extractors/solidity.js.map +1 -1
- package/dist/extractors/verilog.js +80 -15
- package/dist/extractors/verilog.js.map +1 -1
- package/dist/features/boundaries.d.ts.map +1 -1
- package/dist/features/boundaries.js +49 -39
- package/dist/features/boundaries.js.map +1 -1
- package/dist/features/cfg.d.ts.map +1 -1
- package/dist/features/cfg.js +90 -63
- package/dist/features/cfg.js.map +1 -1
- package/dist/features/check.d.ts.map +1 -1
- package/dist/features/check.js +43 -34
- package/dist/features/check.js.map +1 -1
- package/dist/features/cochange.d.ts.map +1 -1
- package/dist/features/cochange.js +68 -56
- package/dist/features/cochange.js.map +1 -1
- package/dist/features/complexity.d.ts.map +1 -1
- package/dist/features/complexity.js +105 -75
- package/dist/features/complexity.js.map +1 -1
- package/dist/features/dataflow.d.ts.map +1 -1
- package/dist/features/dataflow.js +37 -29
- package/dist/features/dataflow.js.map +1 -1
- package/dist/features/flow.d.ts.map +1 -1
- package/dist/features/flow.js +31 -22
- package/dist/features/flow.js.map +1 -1
- package/dist/features/graph-enrichment.d.ts.map +1 -1
- package/dist/features/graph-enrichment.js +77 -70
- package/dist/features/graph-enrichment.js.map +1 -1
- package/dist/features/owners.d.ts +17 -26
- package/dist/features/owners.d.ts.map +1 -1
- package/dist/features/owners.js +120 -109
- package/dist/features/owners.js.map +1 -1
- package/dist/features/sequence.d.ts.map +1 -1
- package/dist/features/sequence.js +59 -54
- package/dist/features/sequence.js.map +1 -1
- package/dist/features/structure-query.d.ts.map +1 -1
- package/dist/features/structure-query.js +60 -60
- package/dist/features/structure-query.js.map +1 -1
- package/dist/features/structure.js +28 -36
- package/dist/features/structure.js.map +1 -1
- package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -1
- package/dist/graph/algorithms/leiden/optimiser.js +100 -69
- package/dist/graph/algorithms/leiden/optimiser.js.map +1 -1
- package/dist/graph/classifiers/roles.d.ts.map +1 -1
- package/dist/graph/classifiers/roles.js +63 -59
- package/dist/graph/classifiers/roles.js.map +1 -1
- package/dist/infrastructure/config.d.ts +1 -1
- package/dist/infrastructure/config.d.ts.map +1 -1
- package/dist/infrastructure/config.js +1 -1
- package/dist/infrastructure/config.js.map +1 -1
- package/dist/mcp/tool-registry.d.ts.map +1 -1
- package/dist/mcp/tool-registry.js +4 -0
- package/dist/mcp/tool-registry.js.map +1 -1
- package/dist/mcp/tools/semantic-search.d.ts +1 -0
- package/dist/mcp/tools/semantic-search.d.ts.map +1 -1
- package/dist/mcp/tools/semantic-search.js +1 -0
- package/dist/mcp/tools/semantic-search.js.map +1 -1
- package/dist/presentation/cfg.d.ts.map +1 -1
- package/dist/presentation/cfg.js +44 -29
- package/dist/presentation/cfg.js.map +1 -1
- package/dist/presentation/flow.d.ts.map +1 -1
- package/dist/presentation/flow.js +58 -38
- package/dist/presentation/flow.js.map +1 -1
- package/dist/types.d.ts +16 -2
- package/dist/types.d.ts.map +1 -1
- package/grammars/tree-sitter-erlang.wasm +0 -0
- package/grammars/tree-sitter-fsharp.wasm +0 -0
- package/grammars/tree-sitter-fsharp_signature.wasm +0 -0
- package/grammars/tree-sitter-gleam.wasm +0 -0
- package/package.json +10 -10
- package/src/ast-analysis/engine.ts +145 -61
- package/src/ast-analysis/rules/index.ts +87 -0
- package/src/ast-analysis/visitor-utils.ts +86 -46
- package/src/ast-analysis/visitors/ast-store-visitor.ts +104 -69
- package/src/ast-analysis/visitors/dataflow-visitor.ts +86 -47
- package/src/cli/commands/audit.ts +1 -1
- package/src/cli/commands/build.ts +2 -0
- package/src/cli/commands/check.ts +1 -1
- package/src/cli/commands/children.ts +1 -1
- package/src/cli/commands/diff-impact.ts +1 -1
- package/src/cli/commands/embed.ts +54 -4
- package/src/cli/commands/roles.ts +1 -1
- package/src/cli/commands/structure.ts +1 -1
- package/src/cli/shared/options.ts +1 -1
- package/src/db/connection.ts +8 -0
- package/src/domain/analysis/dependencies.ts +166 -85
- package/src/domain/analysis/fn-impact.ts +120 -50
- package/src/domain/analysis/module-map.ts +175 -140
- package/src/domain/graph/builder/helpers.ts +85 -76
- package/src/domain/graph/builder/incremental.ts +223 -131
- package/src/domain/graph/builder/pipeline.ts +32 -785
- package/src/domain/graph/builder/stages/build-edges.ts +207 -142
- package/src/domain/graph/builder/stages/build-structure.ts +115 -82
- package/src/domain/graph/builder/stages/detect-changes.ts +107 -64
- package/src/domain/graph/builder/stages/finalize.ts +72 -70
- package/src/domain/graph/builder/stages/insert-nodes.ts +154 -120
- package/src/domain/graph/builder/stages/native-db-lifecycle.ts +74 -0
- package/src/domain/graph/builder/stages/native-orchestrator.ts +942 -0
- package/src/domain/graph/builder/stages/resolve-imports.ts +79 -25
- package/src/domain/graph/cycles.ts +51 -49
- package/src/domain/graph/journal.ts +84 -69
- package/src/domain/graph/watcher.ts +29 -25
- package/src/domain/parser.ts +170 -67
- package/src/domain/search/generator.ts +132 -74
- package/src/domain/search/models.ts +75 -4
- package/src/domain/search/search/hybrid.ts +53 -42
- package/src/domain/search/search/semantic.ts +105 -65
- package/src/domain/wasm-worker-entry.ts +243 -153
- package/src/extractors/c.ts +27 -8
- package/src/extractors/cpp.ts +50 -8
- package/src/extractors/cuda.ts +90 -16
- package/src/extractors/elixir.ts +103 -4
- package/src/extractors/erlang.ts +63 -20
- package/src/extractors/fsharp.ts +104 -0
- package/src/extractors/gleam.ts +40 -39
- package/src/extractors/groovy.ts +45 -1
- package/src/extractors/haskell.ts +45 -4
- package/src/extractors/helpers.ts +205 -1
- package/src/extractors/java.ts +42 -45
- package/src/extractors/javascript.ts +44 -43
- package/src/extractors/julia.ts +191 -77
- package/src/extractors/kotlin.ts +4 -0
- package/src/extractors/objc.ts +171 -47
- package/src/extractors/python.ts +5 -3
- package/src/extractors/r.ts +104 -82
- package/src/extractors/scala.ts +24 -36
- package/src/extractors/solidity.ts +59 -78
- package/src/extractors/verilog.ts +83 -15
- package/src/features/boundaries.ts +64 -46
- package/src/features/cfg.ts +145 -74
- package/src/features/check.ts +60 -43
- package/src/features/cochange.ts +95 -72
- package/src/features/complexity.ts +134 -79
- package/src/features/dataflow.ts +57 -34
- package/src/features/flow.ts +48 -24
- package/src/features/graph-enrichment.ts +105 -70
- package/src/features/owners.ts +186 -146
- package/src/features/sequence.ts +99 -69
- package/src/features/structure-query.ts +94 -79
- package/src/features/structure.ts +56 -56
- package/src/graph/algorithms/leiden/optimiser.ts +142 -87
- package/src/graph/classifiers/roles.ts +64 -54
- package/src/infrastructure/config.ts +1 -1
- package/src/mcp/tool-registry.ts +5 -0
- package/src/mcp/tools/semantic-search.ts +2 -0
- package/src/presentation/cfg.ts +48 -32
- package/src/presentation/flow.ts +100 -52
- package/src/types.ts +16 -1
package/src/extractors/cuda.ts
CHANGED
|
@@ -265,10 +265,10 @@ function extractCudaParameters(paramListNode: TreeSitterNode | null): SubDeclara
|
|
|
265
265
|
if (!param || param.type !== 'parameter_declaration') continue;
|
|
266
266
|
const nameNode = param.childForFieldName('declarator');
|
|
267
267
|
if (nameNode) {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
268
|
+
// Reuse the field-name drill helper so function-type parameters like
|
|
269
|
+
// `void process(int callback(int))` yield the bare name `callback`
|
|
270
|
+
// instead of the raw declarator text, matching the native unwrap path.
|
|
271
|
+
const name = extractCudaFieldName(nameNode);
|
|
272
272
|
params.push({ name, kind: 'parameter', line: param.startPosition.row + 1 });
|
|
273
273
|
}
|
|
274
274
|
}
|
|
@@ -284,22 +284,96 @@ function extractCudaClassFields(classNode: TreeSitterNode): SubDeclaration[] {
|
|
|
284
284
|
const member = body.child(i);
|
|
285
285
|
if (!member || member.type !== 'field_declaration') continue;
|
|
286
286
|
const nameNode = member.childForFieldName('declarator');
|
|
287
|
-
if (nameNode)
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
287
|
+
if (!nameNode) continue;
|
|
288
|
+
// Skip method declarations — a `field_declaration` whose declarator
|
|
289
|
+
// (after unwrapping pointer/reference/array) is a `function_declarator`
|
|
290
|
+
// is a method signature in a header, not a data field. Native and WASM
|
|
291
|
+
// previously diverged on how to format these (native stripped the `*`
|
|
292
|
+
// from pointer-return types, WASM kept it), and both produced
|
|
293
|
+
// method-signature-shaped "property" entries that are not real fields.
|
|
294
|
+
if (isCudaMethodDeclarator(nameNode)) continue;
|
|
295
|
+
const name = extractCudaFieldName(nameNode);
|
|
296
|
+
fields.push({
|
|
297
|
+
name,
|
|
298
|
+
kind: 'property',
|
|
299
|
+
line: member.startPosition.row + 1,
|
|
300
|
+
visibility: extractModifierVisibility(member),
|
|
301
|
+
});
|
|
299
302
|
}
|
|
300
303
|
return fields;
|
|
301
304
|
}
|
|
302
305
|
|
|
306
|
+
const CUDA_DECLARATOR_WRAPPERS = new Set([
|
|
307
|
+
'pointer_declarator',
|
|
308
|
+
'reference_declarator',
|
|
309
|
+
'array_declarator',
|
|
310
|
+
'parenthesized_declarator',
|
|
311
|
+
]);
|
|
312
|
+
|
|
313
|
+
function isCudaMethodDeclarator(node: TreeSitterNode): boolean {
|
|
314
|
+
let current: TreeSitterNode | null = node;
|
|
315
|
+
while (current && CUDA_DECLARATOR_WRAPPERS.has(current.type)) {
|
|
316
|
+
current = current.childForFieldName('declarator');
|
|
317
|
+
}
|
|
318
|
+
if (current?.type !== 'function_declarator') return false;
|
|
319
|
+
// A `function_declarator` whose inner declarator is a `parenthesized_declarator`
|
|
320
|
+
// is a function-pointer (or function-reference) field — e.g. `void (*cb)(int)`
|
|
321
|
+
// parses as function_declarator > parenthesized_declarator > pointer_declarator >
|
|
322
|
+
// field_identifier. Those are real data fields, not method declarations.
|
|
323
|
+
const inner = current.childForFieldName('declarator');
|
|
324
|
+
return inner?.type !== 'parenthesized_declarator';
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Resolve the identifier of a declarator by walking through any combination of
|
|
329
|
+
* pointer/reference/array/parenthesized wrappers and `function_declarator`
|
|
330
|
+
* nodes. Used by both class-field extraction (where `function_declarator`
|
|
331
|
+
* indicates a function-pointer field after method declarations have been
|
|
332
|
+
* filtered out) and parameter extraction (where `function_declarator` wraps a
|
|
333
|
+
* bare function-type parameter name like `callback` in
|
|
334
|
+
* `void process(int callback(int))`).
|
|
335
|
+
*/
|
|
336
|
+
function extractCudaFieldName(decl: TreeSitterNode): string {
|
|
337
|
+
let current: TreeSitterNode | null = decl;
|
|
338
|
+
while (current) {
|
|
339
|
+
if (current.type === 'identifier' || current.type === 'field_identifier') {
|
|
340
|
+
return current.text;
|
|
341
|
+
}
|
|
342
|
+
if (CUDA_DECLARATOR_WRAPPERS.has(current.type) || current.type === 'function_declarator') {
|
|
343
|
+
const next = innerCudaDeclarator(current);
|
|
344
|
+
if (!next) break;
|
|
345
|
+
current = next;
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
return decl.text;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Find the inner declarator of a wrapper node. Most C++ declarator wrappers
|
|
355
|
+
* expose it via the `declarator` field, but some (e.g. `parenthesized_declarator`
|
|
356
|
+
* and `reference_declarator` in tree-sitter-cuda) have unnamed children — so
|
|
357
|
+
* fall back to scanning children for a declarator-shaped node.
|
|
358
|
+
*/
|
|
359
|
+
function innerCudaDeclarator(node: TreeSitterNode): TreeSitterNode | null {
|
|
360
|
+
const named = node.childForFieldName('declarator');
|
|
361
|
+
if (named) return named;
|
|
362
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
363
|
+
const child = node.child(i);
|
|
364
|
+
if (!child) continue;
|
|
365
|
+
if (
|
|
366
|
+
child.type === 'identifier' ||
|
|
367
|
+
child.type === 'field_identifier' ||
|
|
368
|
+
child.type === 'function_declarator' ||
|
|
369
|
+
CUDA_DECLARATOR_WRAPPERS.has(child.type)
|
|
370
|
+
) {
|
|
371
|
+
return child;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
return null;
|
|
375
|
+
}
|
|
376
|
+
|
|
303
377
|
function extractCudaEnumEntries(enumNode: TreeSitterNode): SubDeclaration[] {
|
|
304
378
|
const entries: SubDeclaration[] = [];
|
|
305
379
|
const body = findChild(enumNode, 'enumerator_list');
|
package/src/extractors/elixir.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type {
|
|
|
5
5
|
TreeSitterNode,
|
|
6
6
|
TreeSitterTree,
|
|
7
7
|
} from '../types.js';
|
|
8
|
-
import { findChild, nodeEndLine } from './helpers.js';
|
|
8
|
+
import { findChild, iterChildren, nodeEndLine, PUNCTUATION_TOKENS } from './helpers.js';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Extract symbols from Elixir files.
|
|
@@ -190,14 +190,113 @@ function extractElixirParams(defCallNode: TreeSitterNode): SubDeclaration[] {
|
|
|
190
190
|
for (let j = 0; j < innerArgs.childCount; j++) {
|
|
191
191
|
const param = innerArgs.child(j);
|
|
192
192
|
if (!param) continue;
|
|
193
|
-
|
|
194
|
-
params.push({ name: param.text, kind: 'parameter', line: param.startPosition.row + 1 });
|
|
195
|
-
}
|
|
193
|
+
collectElixirParamIdentifiers(param, params);
|
|
196
194
|
}
|
|
197
195
|
}
|
|
198
196
|
return params;
|
|
199
197
|
}
|
|
200
198
|
|
|
199
|
+
/**
|
|
200
|
+
* Walk a parameter pattern and emit each bound identifier as a `parameter`
|
|
201
|
+
* child. Handles bare identifiers, default-value `a \\ default`, list-cons
|
|
202
|
+
* `[head | tail]`, list `[a, b, c]`, tuple `{x, y}`, and map / struct
|
|
203
|
+
* destructuring (`%{k: v}`, `%Foo{k: v}`).
|
|
204
|
+
*
|
|
205
|
+
* Implemented as an iterative worklist (rather than recursion + helpers) so
|
|
206
|
+
* the call graph has no function-level cycle: only one function performs the
|
|
207
|
+
* traversal and it invokes only leaf helpers (`pushSubNodes`, `pushMapValues`).
|
|
208
|
+
*/
|
|
209
|
+
function collectElixirParamIdentifiers(root: TreeSitterNode, out: SubDeclaration[]): void {
|
|
210
|
+
const stack: TreeSitterNode[] = [root];
|
|
211
|
+
while (stack.length > 0) {
|
|
212
|
+
const node = stack.pop();
|
|
213
|
+
if (!node) continue;
|
|
214
|
+
switch (node.type) {
|
|
215
|
+
case 'identifier':
|
|
216
|
+
out.push({ name: node.text, kind: 'parameter', line: node.startPosition.row + 1 });
|
|
217
|
+
break;
|
|
218
|
+
case 'binary_operator':
|
|
219
|
+
pushElixirBinaryOperatorOperands(node, stack);
|
|
220
|
+
break;
|
|
221
|
+
case 'list':
|
|
222
|
+
case 'tuple':
|
|
223
|
+
pushElixirSequenceItems(node, stack);
|
|
224
|
+
break;
|
|
225
|
+
case 'map':
|
|
226
|
+
pushElixirMapValues(node, stack);
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Push the binding-relevant operands of a `binary_operator` parameter onto the
|
|
234
|
+
* worklist:
|
|
235
|
+
* - `name \\ default` (default-value) binds the left operand only.
|
|
236
|
+
* - `head | tail` (list-cons, appears inside a `list` pattern) binds both.
|
|
237
|
+
*/
|
|
238
|
+
function pushElixirBinaryOperatorOperands(node: TreeSitterNode, stack: TreeSitterNode[]): void {
|
|
239
|
+
const op = node.child(1);
|
|
240
|
+
if (!op) return;
|
|
241
|
+
if (op.type === '\\\\') {
|
|
242
|
+
const left = node.child(0);
|
|
243
|
+
if (left) stack.push(left);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
if (op.type === '|') {
|
|
247
|
+
const right = node.child(2);
|
|
248
|
+
const left = node.child(0);
|
|
249
|
+
if (right) stack.push(right);
|
|
250
|
+
if (left) stack.push(left);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Push the binding-relevant elements of a `list` or `tuple` parameter onto
|
|
256
|
+
* the worklist, skipping punctuation tokens.
|
|
257
|
+
*
|
|
258
|
+
* Items are pushed in reverse document order so that, with a LIFO stack, they
|
|
259
|
+
* are processed left-to-right — preserving the source ordering of bound names.
|
|
260
|
+
*/
|
|
261
|
+
function pushElixirSequenceItems(node: TreeSitterNode, stack: TreeSitterNode[]): void {
|
|
262
|
+
const items: TreeSitterNode[] = [...iterChildren(node, PUNCTUATION_TOKENS)];
|
|
263
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
264
|
+
stack.push(items[i] as TreeSitterNode);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Push the value side of every pair in a `map` or `%Foo{...}` parameter onto
|
|
270
|
+
* the worklist. The struct alias (`Foo`) is a type, not a bound identifier, so
|
|
271
|
+
* the leading `struct` child is intentionally skipped.
|
|
272
|
+
*
|
|
273
|
+
* Items are collected in document order and pushed in reverse so that, with a
|
|
274
|
+
* LIFO stack, they are processed left-to-right — preserving source ordering.
|
|
275
|
+
*/
|
|
276
|
+
function pushElixirMapValues(node: TreeSitterNode, stack: TreeSitterNode[]): void {
|
|
277
|
+
const parts: TreeSitterNode[] = [];
|
|
278
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
279
|
+
const content = node.child(i);
|
|
280
|
+
if (!content || content.type !== 'map_content') continue;
|
|
281
|
+
for (let j = 0; j < content.childCount; j++) {
|
|
282
|
+
const kws = content.child(j);
|
|
283
|
+
if (!kws || kws.type !== 'keywords') continue;
|
|
284
|
+
for (let k = 0; k < kws.childCount; k++) {
|
|
285
|
+
const pair = kws.child(k);
|
|
286
|
+
if (!pair || pair.type !== 'pair') continue;
|
|
287
|
+
for (let p = 0; p < pair.childCount; p++) {
|
|
288
|
+
const part = pair.child(p);
|
|
289
|
+
if (!part || part.type === 'keyword') continue;
|
|
290
|
+
parts.push(part);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
296
|
+
stack.push(parts[i] as TreeSitterNode);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
201
300
|
function handleDefprotocol(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
202
301
|
const args = findChild(node, 'arguments');
|
|
203
302
|
if (!args) return;
|
package/src/extractors/erlang.ts
CHANGED
|
@@ -70,7 +70,10 @@ function walkErlangNode(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
70
70
|
|
|
71
71
|
function handleModuleAttr(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
72
72
|
// module_attribute: - module ( atom ) .
|
|
73
|
-
|
|
73
|
+
// Prefer the named `name` field exposed by tree-sitter-erlang so we don't
|
|
74
|
+
// accidentally pick up the `module` keyword if a future grammar exposes it
|
|
75
|
+
// as a named `atom` child.
|
|
76
|
+
const nameNode = node.childForFieldName('name') ?? findChild(node, 'atom');
|
|
74
77
|
if (!nameNode) return;
|
|
75
78
|
|
|
76
79
|
ctx.definitions.push({
|
|
@@ -83,7 +86,10 @@ function handleModuleAttr(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
83
86
|
|
|
84
87
|
function handleRecordDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
85
88
|
// record_decl: - record ( atom , { record_field, ... } ) .
|
|
86
|
-
|
|
89
|
+
// Prefer the named `name` field exposed by tree-sitter-erlang; fall back to
|
|
90
|
+
// the first atom child for grammar versions that don't expose it. Mirrors
|
|
91
|
+
// the Rust `handle_record_decl` defensive pattern.
|
|
92
|
+
const nameNode = node.childForFieldName('name') ?? findChild(node, 'atom');
|
|
87
93
|
if (!nameNode) return;
|
|
88
94
|
|
|
89
95
|
const children: SubDeclaration[] = [];
|
|
@@ -112,7 +118,14 @@ function handleRecordDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
112
118
|
}
|
|
113
119
|
|
|
114
120
|
function handleTypeAlias(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
115
|
-
|
|
121
|
+
// type_alias: -type name(...) :: ty.
|
|
122
|
+
// Name is typically wrapped in a `type_name` node containing an `atom`.
|
|
123
|
+
// Mirrors the Rust `handle_type_alias` fallback so the two engines agree
|
|
124
|
+
// even when the grammar nests the name inside `type_name`.
|
|
125
|
+
const directAtom = findChild(node, 'atom');
|
|
126
|
+
const typeNameNode = !directAtom ? findChild(node, 'type_name') : null;
|
|
127
|
+
const wrappedAtom = typeNameNode ? findChild(typeNameNode, 'atom') : null;
|
|
128
|
+
const nameNode = directAtom ?? wrappedAtom;
|
|
116
129
|
if (!nameNode) return;
|
|
117
130
|
|
|
118
131
|
ctx.definitions.push({
|
|
@@ -134,13 +147,22 @@ function handleFunDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
134
147
|
|
|
135
148
|
function handleFunctionClause(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
136
149
|
// function_clause: atom expr_args clause_body
|
|
137
|
-
const nameNode = findChild(node, 'atom');
|
|
150
|
+
const nameNode = node.childForFieldName('name') ?? findChild(node, 'atom');
|
|
138
151
|
if (!nameNode) return;
|
|
139
152
|
|
|
140
|
-
// Don't duplicate if we already have this function
|
|
141
|
-
if (ctx.definitions.some((d) => d.name === nameNode.text && d.kind === 'function')) return;
|
|
142
|
-
|
|
143
153
|
const params = extractErlangParams(node);
|
|
154
|
+
const arity = params.length;
|
|
155
|
+
|
|
156
|
+
// Don't duplicate if we already have this function at the same arity.
|
|
157
|
+
// Erlang overloads by arity, so `foo/1` and `foo/2` are distinct definitions.
|
|
158
|
+
if (
|
|
159
|
+
ctx.definitions.some(
|
|
160
|
+
(d) =>
|
|
161
|
+
d.name === nameNode.text && d.kind === 'function' && (d.children?.length ?? 0) === arity,
|
|
162
|
+
)
|
|
163
|
+
) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
144
166
|
|
|
145
167
|
ctx.definitions.push({
|
|
146
168
|
name: nameNode.text,
|
|
@@ -154,31 +176,42 @@ function handleFunctionClause(node: TreeSitterNode, ctx: ExtractorOutput): void
|
|
|
154
176
|
|
|
155
177
|
function extractErlangParams(clauseNode: TreeSitterNode): SubDeclaration[] {
|
|
156
178
|
const params: SubDeclaration[] = [];
|
|
157
|
-
const argsNode = findChild(clauseNode, 'expr_args');
|
|
179
|
+
const argsNode = clauseNode.childForFieldName('args') ?? findChild(clauseNode, 'expr_args');
|
|
158
180
|
if (!argsNode) return params;
|
|
159
181
|
|
|
160
|
-
|
|
161
|
-
|
|
182
|
+
// Iterate named children so every argument pattern counts as one parameter,
|
|
183
|
+
// independent of whether it is a bare `var`/`atom` or a complex destructuring
|
|
184
|
+
// pattern (tuple, list, binary, etc.). Punctuation tokens are anonymous and
|
|
185
|
+
// therefore excluded automatically.
|
|
186
|
+
for (let i = 0; i < argsNode.namedChildCount; i++) {
|
|
187
|
+
const child = argsNode.namedChild(i);
|
|
162
188
|
if (!child) continue;
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
189
|
+
const label =
|
|
190
|
+
child.type === 'var' || child.type === 'atom'
|
|
191
|
+
? child.text
|
|
192
|
+
: // Placeholder for complex patterns so arity is preserved.
|
|
193
|
+
`_${i}`;
|
|
194
|
+
params.push({ name: label, kind: 'parameter', line: child.startPosition.row + 1 });
|
|
169
195
|
}
|
|
170
196
|
return params;
|
|
171
197
|
}
|
|
172
198
|
|
|
173
199
|
function handleDefine(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
174
200
|
// pp_define: -define(NAME, value).
|
|
201
|
+
// For parametric macros, the grammar wraps the name in a `macro_lhs(name, args)`
|
|
202
|
+
// node. Inside `macro_lhs` the name comes first, followed by `(`, the argument
|
|
203
|
+
// `var` children, and `)`. We must therefore try `atom` (lowercase macros,
|
|
204
|
+
// e.g. `-define(foo(X), X+1)`) before `var` (uppercase macros, e.g.
|
|
205
|
+
// `-define(FOO(X), X+1)`) — otherwise `findChild(.., 'var')` skips the leading
|
|
206
|
+
// atom and lands on the first argument variable, mislabeling the definition.
|
|
207
|
+
// Mirrors the Rust `handle_define` so both engines agree.
|
|
175
208
|
const nameNode =
|
|
176
209
|
findChild(node, 'var') || findChild(node, 'atom') || findChild(node, 'macro_lhs');
|
|
177
210
|
if (!nameNode) return;
|
|
178
211
|
|
|
179
212
|
const name =
|
|
180
213
|
nameNode.type === 'macro_lhs'
|
|
181
|
-
? (findChild(nameNode, 'var')?.text ?? nameNode.text)
|
|
214
|
+
? (findChild(nameNode, 'atom')?.text ?? findChild(nameNode, 'var')?.text ?? nameNode.text)
|
|
182
215
|
: nameNode.text;
|
|
183
216
|
|
|
184
217
|
ctx.definitions.push({
|
|
@@ -194,9 +227,15 @@ function handleInclude(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
194
227
|
if (!strNode) return;
|
|
195
228
|
|
|
196
229
|
const source = strNode.text.replace(/^"|"$/g, '');
|
|
230
|
+
// Preserve the distinction between local includes (`-include("foo.hrl")`)
|
|
231
|
+
// and OTP library includes (`-include_lib("kernel/include/file.hrl")`) so
|
|
232
|
+
// downstream consumers can apply the correct path-resolution strategy
|
|
233
|
+
// (local: relative to the source file; lib: relative to an OTP app root).
|
|
234
|
+
// Mirrors the Rust `handle_include` so both engines agree.
|
|
235
|
+
const kind = node.type === 'pp_include_lib' ? 'include_lib' : 'include';
|
|
197
236
|
ctx.imports.push({
|
|
198
237
|
source,
|
|
199
|
-
names: [
|
|
238
|
+
names: [kind],
|
|
200
239
|
line: node.startPosition.row + 1,
|
|
201
240
|
});
|
|
202
241
|
}
|
|
@@ -224,8 +263,12 @@ function handleImportAttr(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
224
263
|
}
|
|
225
264
|
|
|
226
265
|
function handleCall(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
227
|
-
// call: first child is function ref (atom or remote), then expr_args
|
|
228
|
-
|
|
266
|
+
// call: first named child is function ref (atom or remote), then expr_args.
|
|
267
|
+
// Using `namedChild(0)` rather than `child(0)` skips anonymous tokens
|
|
268
|
+
// (punctuation, keywords) so a future grammar revision that inserts a
|
|
269
|
+
// leading anonymous node won't silently drop the call. Mirrors the Rust
|
|
270
|
+
// `handle_call` so both engines emit the same set of calls.
|
|
271
|
+
const funcNode = node.namedChild(0);
|
|
229
272
|
if (!funcNode) return;
|
|
230
273
|
|
|
231
274
|
if (funcNode.type === 'atom' || funcNode.type === 'identifier') {
|
package/src/extractors/fsharp.ts
CHANGED
|
@@ -10,6 +10,13 @@ import { findChild, nodeEndLine } from './helpers.js';
|
|
|
10
10
|
/**
|
|
11
11
|
* Extract symbols from F# files.
|
|
12
12
|
*
|
|
13
|
+
* Grammar source: `tree-sitter-fsharp` v0.3.0 installed via a pinned GitHub
|
|
14
|
+
* tarball in `package.json` because the ionide/tree-sitter-fsharp project has
|
|
15
|
+
* no v0.3.0 release published to the npm registry. The cargo crate the native
|
|
16
|
+
* engine uses is also v0.3.0; both engines must stay aligned. Upgrading
|
|
17
|
+
* requires a manual edit of the tarball URL in `package.json` and
|
|
18
|
+
* `package-lock.json` — `npm update` will not bump this entry.
|
|
19
|
+
*
|
|
13
20
|
* tree-sitter-fsharp grammar notes:
|
|
14
21
|
* - named_module: top-level module declaration
|
|
15
22
|
* - function_declaration_left: LHS of `let name params = ...`
|
|
@@ -42,6 +49,14 @@ function walkFSharpNode(
|
|
|
42
49
|
case 'named_module':
|
|
43
50
|
nextModule = handleNamedModule(node, ctx);
|
|
44
51
|
break;
|
|
52
|
+
case 'module_defn':
|
|
53
|
+
// Nested signature module (`module Foo = ...`) in `.fsi` files,
|
|
54
|
+
// emitted by both the WASM (npm ionide tarball v0.3.0) and cargo
|
|
55
|
+
// v0.3.0 tree-sitter-fsharp signature grammars. Accumulate the
|
|
56
|
+
// dotted module path so nested `val` declarations are qualified
|
|
57
|
+
// as `Outer.Inner.foo` in parity with the native engine.
|
|
58
|
+
nextModule = handleModuleDefn(node, ctx, currentModule);
|
|
59
|
+
break;
|
|
45
60
|
case 'function_declaration_left':
|
|
46
61
|
handleFunctionDecl(node, ctx, currentModule);
|
|
47
62
|
break;
|
|
@@ -57,6 +72,9 @@ function walkFSharpNode(
|
|
|
57
72
|
case 'dot_expression':
|
|
58
73
|
handleDotExpression(node, ctx);
|
|
59
74
|
break;
|
|
75
|
+
case 'value_definition':
|
|
76
|
+
handleValueDefinition(node, ctx, currentModule);
|
|
77
|
+
break;
|
|
60
78
|
}
|
|
61
79
|
|
|
62
80
|
for (let i = 0; i < node.childCount; i++) {
|
|
@@ -79,6 +97,27 @@ function handleNamedModule(node: TreeSitterNode, ctx: ExtractorOutput): string |
|
|
|
79
97
|
return nameNode.text;
|
|
80
98
|
}
|
|
81
99
|
|
|
100
|
+
function handleModuleDefn(
|
|
101
|
+
node: TreeSitterNode,
|
|
102
|
+
ctx: ExtractorOutput,
|
|
103
|
+
currentModule: string | null,
|
|
104
|
+
): string | null {
|
|
105
|
+
// `module_defn` (cargo 0.3.0 signature grammar) wraps `module Foo = ...`
|
|
106
|
+
// sections inside an outer `namespace` or another module. The name is a
|
|
107
|
+
// direct `identifier` child.
|
|
108
|
+
const nameNode = findChild(node, 'identifier');
|
|
109
|
+
if (!nameNode) return currentModule;
|
|
110
|
+
|
|
111
|
+
const qualified = currentModule ? `${currentModule}.${nameNode.text}` : nameNode.text;
|
|
112
|
+
ctx.definitions.push({
|
|
113
|
+
name: qualified,
|
|
114
|
+
kind: 'module',
|
|
115
|
+
line: node.startPosition.row + 1,
|
|
116
|
+
endLine: nodeEndLine(node),
|
|
117
|
+
});
|
|
118
|
+
return qualified;
|
|
119
|
+
}
|
|
120
|
+
|
|
82
121
|
function handleFunctionDecl(
|
|
83
122
|
node: TreeSitterNode,
|
|
84
123
|
ctx: ExtractorOutput,
|
|
@@ -251,3 +290,68 @@ function handleDotExpression(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
251
290
|
ctx.calls.push(call);
|
|
252
291
|
}
|
|
253
292
|
}
|
|
293
|
+
|
|
294
|
+
// Handle `val name : type` declarations in `.fsi` signature files.
|
|
295
|
+
// The signature grammar reuses `value_definition` for `val` bindings,
|
|
296
|
+
// distinguished from the source grammar's `let` bindings by the first
|
|
297
|
+
// child being the literal `val` keyword. Source-file `value_definition`
|
|
298
|
+
// nodes (which start with `let`) are intentionally ignored to preserve
|
|
299
|
+
// `.fs` extractor parity.
|
|
300
|
+
function handleValueDefinition(
|
|
301
|
+
node: TreeSitterNode,
|
|
302
|
+
ctx: ExtractorOutput,
|
|
303
|
+
currentModule: string | null,
|
|
304
|
+
): void {
|
|
305
|
+
const first = node.child(0);
|
|
306
|
+
if (!first || first.type !== 'val') return;
|
|
307
|
+
|
|
308
|
+
const declLeft = findChild(node, 'value_declaration_left');
|
|
309
|
+
if (!declLeft) return;
|
|
310
|
+
|
|
311
|
+
const pattern = findChild(declLeft, 'identifier_pattern');
|
|
312
|
+
if (!pattern) return;
|
|
313
|
+
|
|
314
|
+
const ident =
|
|
315
|
+
findChild(findChild(pattern, 'long_identifier_or_op') ?? pattern, 'identifier') ??
|
|
316
|
+
findChild(pattern, 'identifier');
|
|
317
|
+
if (!ident) return;
|
|
318
|
+
|
|
319
|
+
// The npm and cargo tree-sitter-fsharp 0.3.0 grammars — though sharing a
|
|
320
|
+
// version tag — emit type signatures with different node shapes:
|
|
321
|
+
// • WASM (npm 0.3.0 ionide tarball): `function_type` is the explicit
|
|
322
|
+
// function-type kind, present as a direct child of `value_definition`
|
|
323
|
+
// for `a -> b` types; plain values (e.g. `val pi : float`) appear as
|
|
324
|
+
// `simple_type`.
|
|
325
|
+
// • Native (cargo 0.3.0): every type signature is wrapped in
|
|
326
|
+
// `curried_spec`. A function type contains one or more `arguments_spec`
|
|
327
|
+
// children; a plain value wraps a single `simple_type`.
|
|
328
|
+
// Classify as a function whenever `function_type` appears OR a
|
|
329
|
+
// `curried_spec` contains an `arguments_spec` child, so both engines stay
|
|
330
|
+
// in parity until the grammars converge.
|
|
331
|
+
let hasFunctionType = false;
|
|
332
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
333
|
+
const c = node.child(i);
|
|
334
|
+
if (!c) continue;
|
|
335
|
+
if (c.type === 'function_type') {
|
|
336
|
+
hasFunctionType = true;
|
|
337
|
+
break;
|
|
338
|
+
}
|
|
339
|
+
if (c.type === 'curried_spec') {
|
|
340
|
+
for (let j = 0; j < c.childCount; j++) {
|
|
341
|
+
if (c.child(j)?.type === 'arguments_spec') {
|
|
342
|
+
hasFunctionType = true;
|
|
343
|
+
break;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
if (hasFunctionType) break;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const name = currentModule ? `${currentModule}.${ident.text}` : ident.text;
|
|
351
|
+
ctx.definitions.push({
|
|
352
|
+
name,
|
|
353
|
+
kind: hasFunctionType ? 'function' : 'variable',
|
|
354
|
+
line: node.startPosition.row + 1,
|
|
355
|
+
endLine: nodeEndLine(node),
|
|
356
|
+
});
|
|
357
|
+
}
|