@stupidloud/codegraph 0.8.1 → 0.9.9
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 +319 -152
- package/dist/bin/codegraph.d.ts +4 -0
- package/dist/bin/codegraph.d.ts.map +1 -1
- package/dist/bin/codegraph.js +354 -90
- package/dist/bin/codegraph.js.map +1 -1
- package/dist/bin/node-version-check.d.ts +17 -0
- package/dist/bin/node-version-check.d.ts.map +1 -1
- package/dist/bin/node-version-check.js +37 -0
- package/dist/bin/node-version-check.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -11
- package/dist/config.js.map +1 -1
- package/dist/context/formatter.d.ts.map +1 -1
- package/dist/context/formatter.js +25 -6
- package/dist/context/formatter.js.map +1 -1
- package/dist/context/index.d.ts +22 -0
- package/dist/context/index.d.ts.map +1 -1
- package/dist/context/index.js +257 -6
- package/dist/context/index.js.map +1 -1
- package/dist/context/markers.d.ts +19 -0
- package/dist/context/markers.d.ts.map +1 -0
- package/dist/context/markers.js +22 -0
- package/dist/context/markers.js.map +1 -0
- package/dist/db/index.d.ts +30 -1
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +75 -25
- package/dist/db/index.js.map +1 -1
- package/dist/db/queries.d.ts +104 -0
- package/dist/db/queries.d.ts.map +1 -1
- package/dist/db/queries.js +328 -31
- package/dist/db/queries.js.map +1 -1
- package/dist/db/sqlite-adapter.d.ts +24 -23
- package/dist/db/sqlite-adapter.d.ts.map +1 -1
- package/dist/db/sqlite-adapter.js +54 -174
- package/dist/db/sqlite-adapter.js.map +1 -1
- package/dist/directory.d.ts.map +1 -1
- package/dist/directory.js +6 -20
- package/dist/directory.js.map +1 -1
- package/dist/extraction/generated-detection.d.ts +30 -0
- package/dist/extraction/generated-detection.d.ts.map +1 -0
- package/dist/extraction/generated-detection.js +80 -0
- package/dist/extraction/generated-detection.js.map +1 -0
- package/dist/extraction/grammars.d.ts +23 -1
- package/dist/extraction/grammars.d.ts.map +1 -1
- package/dist/extraction/grammars.js +107 -3
- package/dist/extraction/grammars.js.map +1 -1
- package/dist/extraction/index.d.ts +22 -14
- package/dist/extraction/index.d.ts.map +1 -1
- package/dist/extraction/index.js +272 -183
- package/dist/extraction/index.js.map +1 -1
- package/dist/extraction/languages/c-cpp.d.ts.map +1 -1
- package/dist/extraction/languages/c-cpp.js +45 -0
- package/dist/extraction/languages/c-cpp.js.map +1 -1
- package/dist/extraction/languages/csharp.d.ts.map +1 -1
- package/dist/extraction/languages/csharp.js +2 -1
- package/dist/extraction/languages/csharp.js.map +1 -1
- package/dist/extraction/languages/go.d.ts.map +1 -1
- package/dist/extraction/languages/go.js +18 -2
- package/dist/extraction/languages/go.js.map +1 -1
- package/dist/extraction/languages/index.d.ts.map +1 -1
- package/dist/extraction/languages/index.js +6 -0
- package/dist/extraction/languages/index.js.map +1 -1
- package/dist/extraction/languages/java.d.ts.map +1 -1
- package/dist/extraction/languages/java.js +6 -0
- package/dist/extraction/languages/java.js.map +1 -1
- package/dist/extraction/languages/kotlin.d.ts.map +1 -1
- package/dist/extraction/languages/kotlin.js +6 -0
- package/dist/extraction/languages/kotlin.js.map +1 -1
- package/dist/extraction/languages/lua.d.ts +3 -0
- package/dist/extraction/languages/lua.d.ts.map +1 -0
- package/dist/extraction/languages/lua.js +150 -0
- package/dist/extraction/languages/lua.js.map +1 -0
- package/dist/extraction/languages/luau.d.ts +3 -0
- package/dist/extraction/languages/luau.d.ts.map +1 -0
- package/dist/extraction/languages/luau.js +37 -0
- package/dist/extraction/languages/luau.js.map +1 -0
- package/dist/extraction/languages/objc.d.ts +3 -0
- package/dist/extraction/languages/objc.d.ts.map +1 -0
- package/dist/extraction/languages/objc.js +133 -0
- package/dist/extraction/languages/objc.js.map +1 -0
- package/dist/extraction/mybatis-extractor.d.ts +48 -0
- package/dist/extraction/mybatis-extractor.d.ts.map +1 -0
- package/dist/extraction/mybatis-extractor.js +198 -0
- package/dist/extraction/mybatis-extractor.js.map +1 -0
- package/dist/extraction/tree-sitter-types.d.ts +14 -0
- package/dist/extraction/tree-sitter-types.d.ts.map +1 -1
- package/dist/extraction/tree-sitter.d.ts +84 -0
- package/dist/extraction/tree-sitter.d.ts.map +1 -1
- package/dist/extraction/tree-sitter.js +715 -16
- package/dist/extraction/tree-sitter.js.map +1 -1
- package/dist/extraction/vue-extractor.d.ts +15 -0
- package/dist/extraction/vue-extractor.d.ts.map +1 -1
- package/dist/extraction/vue-extractor.js +88 -0
- package/dist/extraction/vue-extractor.js.map +1 -1
- package/dist/extraction/wasm/tree-sitter-lua.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-luau.wasm +0 -0
- package/dist/extraction/wasm-runtime-flags.d.ts +38 -0
- package/dist/extraction/wasm-runtime-flags.d.ts.map +1 -0
- package/dist/extraction/wasm-runtime-flags.js +106 -0
- package/dist/extraction/wasm-runtime-flags.js.map +1 -0
- package/dist/graph/traversal.d.ts.map +1 -1
- package/dist/graph/traversal.js +76 -38
- package/dist/graph/traversal.js.map +1 -1
- package/dist/index.d.ts +77 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +133 -19
- package/dist/index.js.map +1 -1
- package/dist/installer/config-writer.d.ts +7 -8
- package/dist/installer/config-writer.d.ts.map +1 -1
- package/dist/installer/config-writer.js +7 -27
- package/dist/installer/config-writer.js.map +1 -1
- package/dist/installer/index.d.ts +51 -16
- package/dist/installer/index.d.ts.map +1 -1
- package/dist/installer/index.js +120 -29
- package/dist/installer/index.js.map +1 -1
- package/dist/installer/instructions-template.d.ts +11 -21
- package/dist/installer/instructions-template.d.ts.map +1 -1
- package/dist/installer/instructions-template.js +12 -56
- package/dist/installer/instructions-template.js.map +1 -1
- package/dist/installer/targets/antigravity.d.ts +57 -0
- package/dist/installer/targets/antigravity.d.ts.map +1 -0
- package/dist/installer/targets/antigravity.js +308 -0
- package/dist/installer/targets/antigravity.js.map +1 -0
- package/dist/installer/targets/claude.d.ts +26 -1
- package/dist/installer/targets/claude.d.ts.map +1 -1
- package/dist/installer/targets/claude.js +118 -40
- package/dist/installer/targets/claude.js.map +1 -1
- package/dist/installer/targets/codex.d.ts.map +1 -1
- package/dist/installer/targets/codex.js +15 -13
- package/dist/installer/targets/codex.js.map +1 -1
- package/dist/installer/targets/cursor.d.ts.map +1 -1
- package/dist/installer/targets/cursor.js +61 -36
- package/dist/installer/targets/cursor.js.map +1 -1
- package/dist/installer/targets/gemini.d.ts +26 -0
- package/dist/installer/targets/gemini.d.ts.map +1 -0
- package/dist/installer/targets/gemini.js +167 -0
- package/dist/installer/targets/gemini.js.map +1 -0
- package/dist/installer/targets/hermes.d.ts +18 -0
- package/dist/installer/targets/hermes.d.ts.map +1 -0
- package/dist/installer/targets/hermes.js +359 -0
- package/dist/installer/targets/hermes.js.map +1 -0
- package/dist/installer/targets/kiro.d.ts +27 -0
- package/dist/installer/targets/kiro.d.ts.map +1 -0
- package/dist/installer/targets/kiro.js +178 -0
- package/dist/installer/targets/kiro.js.map +1 -0
- package/dist/installer/targets/opencode.d.ts.map +1 -1
- package/dist/installer/targets/opencode.js +15 -13
- package/dist/installer/targets/opencode.js.map +1 -1
- package/dist/installer/targets/registry.d.ts.map +1 -1
- package/dist/installer/targets/registry.js +8 -0
- package/dist/installer/targets/registry.js.map +1 -1
- package/dist/installer/targets/shared.d.ts.map +1 -1
- package/dist/installer/targets/shared.js +3 -2
- package/dist/installer/targets/shared.js.map +1 -1
- package/dist/installer/targets/types.d.ts +1 -16
- package/dist/installer/targets/types.d.ts.map +1 -1
- package/dist/mcp/daemon-paths.d.ts +46 -0
- package/dist/mcp/daemon-paths.d.ts.map +1 -0
- package/dist/mcp/daemon-paths.js +125 -0
- package/dist/mcp/daemon-paths.js.map +1 -0
- package/dist/mcp/daemon.d.ts +161 -0
- package/dist/mcp/daemon.d.ts.map +1 -0
- package/dist/mcp/daemon.js +403 -0
- package/dist/mcp/daemon.js.map +1 -0
- package/dist/mcp/engine.d.ts +105 -0
- package/dist/mcp/engine.d.ts.map +1 -0
- package/dist/mcp/engine.js +270 -0
- package/dist/mcp/engine.js.map +1 -0
- package/dist/mcp/index.d.ts +70 -52
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +355 -331
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/proxy.d.ts +81 -0
- package/dist/mcp/proxy.d.ts.map +1 -0
- package/dist/mcp/proxy.js +510 -0
- package/dist/mcp/proxy.js.map +1 -0
- package/dist/mcp/server-instructions.d.ts +1 -1
- package/dist/mcp/server-instructions.d.ts.map +1 -1
- package/dist/mcp/server-instructions.js +21 -21
- package/dist/mcp/session.d.ts +77 -0
- package/dist/mcp/session.d.ts.map +1 -0
- package/dist/mcp/session.js +294 -0
- package/dist/mcp/session.js.map +1 -0
- package/dist/mcp/tools.d.ts +171 -15
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +1714 -298
- package/dist/mcp/tools.js.map +1 -1
- package/dist/mcp/transport.d.ts +111 -29
- package/dist/mcp/transport.d.ts.map +1 -1
- package/dist/mcp/transport.js +181 -71
- package/dist/mcp/transport.js.map +1 -1
- package/dist/mcp/version.d.ts +19 -0
- package/dist/mcp/version.d.ts.map +1 -0
- package/dist/mcp/version.js +71 -0
- package/dist/mcp/version.js.map +1 -0
- package/dist/resolution/callback-synthesizer.d.ts +10 -0
- package/dist/resolution/callback-synthesizer.d.ts.map +1 -0
- package/dist/resolution/callback-synthesizer.js +1300 -0
- package/dist/resolution/callback-synthesizer.js.map +1 -0
- package/dist/resolution/frameworks/csharp.d.ts.map +1 -1
- package/dist/resolution/frameworks/csharp.js +36 -8
- package/dist/resolution/frameworks/csharp.js.map +1 -1
- package/dist/resolution/frameworks/drupal.d.ts +51 -0
- package/dist/resolution/frameworks/drupal.d.ts.map +1 -0
- package/dist/resolution/frameworks/drupal.js +367 -0
- package/dist/resolution/frameworks/drupal.js.map +1 -0
- package/dist/resolution/frameworks/expo-modules.d.ts +3 -0
- package/dist/resolution/frameworks/expo-modules.d.ts.map +1 -0
- package/dist/resolution/frameworks/expo-modules.js +143 -0
- package/dist/resolution/frameworks/expo-modules.js.map +1 -0
- package/dist/resolution/frameworks/express.d.ts.map +1 -1
- package/dist/resolution/frameworks/express.js +102 -19
- package/dist/resolution/frameworks/express.js.map +1 -1
- package/dist/resolution/frameworks/fabric.d.ts +3 -0
- package/dist/resolution/frameworks/fabric.d.ts.map +1 -0
- package/dist/resolution/frameworks/fabric.js +354 -0
- package/dist/resolution/frameworks/fabric.js.map +1 -0
- package/dist/resolution/frameworks/go.d.ts.map +1 -1
- package/dist/resolution/frameworks/go.js +6 -3
- package/dist/resolution/frameworks/go.js.map +1 -1
- package/dist/resolution/frameworks/index.d.ts +6 -0
- package/dist/resolution/frameworks/index.d.ts.map +1 -1
- package/dist/resolution/frameworks/index.js +29 -1
- package/dist/resolution/frameworks/index.js.map +1 -1
- package/dist/resolution/frameworks/java.d.ts.map +1 -1
- package/dist/resolution/frameworks/java.js +339 -12
- package/dist/resolution/frameworks/java.js.map +1 -1
- package/dist/resolution/frameworks/laravel.d.ts.map +1 -1
- package/dist/resolution/frameworks/laravel.js +17 -8
- package/dist/resolution/frameworks/laravel.js.map +1 -1
- package/dist/resolution/frameworks/nestjs.d.ts.map +1 -1
- package/dist/resolution/frameworks/nestjs.js +324 -0
- package/dist/resolution/frameworks/nestjs.js.map +1 -1
- package/dist/resolution/frameworks/play.d.ts +19 -0
- package/dist/resolution/frameworks/play.d.ts.map +1 -0
- package/dist/resolution/frameworks/play.js +111 -0
- package/dist/resolution/frameworks/play.js.map +1 -0
- package/dist/resolution/frameworks/python.d.ts.map +1 -1
- package/dist/resolution/frameworks/python.js +134 -16
- package/dist/resolution/frameworks/python.js.map +1 -1
- package/dist/resolution/frameworks/react-native.d.ts +3 -0
- package/dist/resolution/frameworks/react-native.d.ts.map +1 -0
- package/dist/resolution/frameworks/react-native.js +360 -0
- package/dist/resolution/frameworks/react-native.js.map +1 -0
- package/dist/resolution/frameworks/react.d.ts.map +1 -1
- package/dist/resolution/frameworks/react.js +96 -3
- package/dist/resolution/frameworks/react.js.map +1 -1
- package/dist/resolution/frameworks/ruby.d.ts.map +1 -1
- package/dist/resolution/frameworks/ruby.js +106 -2
- package/dist/resolution/frameworks/ruby.js.map +1 -1
- package/dist/resolution/frameworks/rust.d.ts.map +1 -1
- package/dist/resolution/frameworks/rust.js +102 -5
- package/dist/resolution/frameworks/rust.js.map +1 -1
- package/dist/resolution/frameworks/swift-objc.d.ts +37 -0
- package/dist/resolution/frameworks/swift-objc.d.ts.map +1 -0
- package/dist/resolution/frameworks/swift-objc.js +252 -0
- package/dist/resolution/frameworks/swift-objc.js.map +1 -0
- package/dist/resolution/frameworks/swift.d.ts.map +1 -1
- package/dist/resolution/frameworks/swift.js +30 -6
- package/dist/resolution/frameworks/swift.js.map +1 -1
- package/dist/resolution/go-module.d.ts +26 -0
- package/dist/resolution/go-module.d.ts.map +1 -0
- package/dist/resolution/go-module.js +78 -0
- package/dist/resolution/go-module.js.map +1 -0
- package/dist/resolution/import-resolver.d.ts +28 -0
- package/dist/resolution/import-resolver.d.ts.map +1 -1
- package/dist/resolution/import-resolver.js +617 -5
- package/dist/resolution/import-resolver.js.map +1 -1
- package/dist/resolution/index.d.ts +11 -0
- package/dist/resolution/index.d.ts.map +1 -1
- package/dist/resolution/index.js +196 -10
- package/dist/resolution/index.js.map +1 -1
- package/dist/resolution/lru-cache.d.ts +24 -0
- package/dist/resolution/lru-cache.d.ts.map +1 -0
- package/dist/resolution/lru-cache.js +62 -0
- package/dist/resolution/lru-cache.js.map +1 -0
- package/dist/resolution/name-matcher.d.ts.map +1 -1
- package/dist/resolution/name-matcher.js +212 -0
- package/dist/resolution/name-matcher.js.map +1 -1
- package/dist/resolution/swift-objc-bridge.d.ts +134 -0
- package/dist/resolution/swift-objc-bridge.d.ts.map +1 -0
- package/dist/resolution/swift-objc-bridge.js +256 -0
- package/dist/resolution/swift-objc-bridge.js.map +1 -0
- package/dist/resolution/types.d.ts +44 -0
- package/dist/resolution/types.d.ts.map +1 -1
- package/dist/resolution/workspace-packages.d.ts +48 -0
- package/dist/resolution/workspace-packages.d.ts.map +1 -0
- package/dist/resolution/workspace-packages.js +208 -0
- package/dist/resolution/workspace-packages.js.map +1 -0
- package/dist/search/query-utils.d.ts +18 -0
- package/dist/search/query-utils.d.ts.map +1 -1
- package/dist/search/query-utils.js +30 -0
- package/dist/search/query-utils.js.map +1 -1
- package/dist/sync/git-hooks.d.ts.map +1 -1
- package/dist/sync/git-hooks.js +2 -0
- package/dist/sync/git-hooks.js.map +1 -1
- package/dist/sync/index.d.ts +3 -1
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +8 -1
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/watcher.d.ts +214 -12
- package/dist/sync/watcher.d.ts.map +1 -1
- package/dist/sync/watcher.js +467 -55
- package/dist/sync/watcher.js.map +1 -1
- package/dist/sync/worktree.d.ts +54 -0
- package/dist/sync/worktree.d.ts.map +1 -0
- package/dist/sync/worktree.js +137 -0
- package/dist/sync/worktree.js.map +1 -0
- package/dist/types.d.ts +9 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +14 -0
- package/dist/types.js.map +1 -1
- package/dist/utils.js +1 -1
- package/package.json +2 -2
- package/scripts/add-lang/bench.sh +60 -0
- package/scripts/add-lang/check-grammar.mjs +75 -0
- package/scripts/add-lang/dump-ast.mjs +103 -0
- package/scripts/add-lang/verify-extraction.mjs +70 -0
- package/scripts/agent-eval/arms-F.sh +21 -0
- package/scripts/agent-eval/arms-matrix.sh +37 -0
- package/scripts/agent-eval/bench-readme.sh +28 -0
- package/scripts/agent-eval/bench-why-repo.sh +22 -0
- package/scripts/agent-eval/block-read-hook.sh +19 -0
- package/scripts/agent-eval/hook-settings.json +15 -0
- package/scripts/agent-eval/itrun.sh +24 -11
- package/scripts/agent-eval/parse-arms.mjs +116 -0
- package/scripts/agent-eval/parse-bench-readme.mjs +84 -0
- package/scripts/agent-eval/probe-context.mjs +21 -0
- package/scripts/agent-eval/probe-explore.mjs +40 -0
- package/scripts/agent-eval/probe-node.mjs +20 -0
- package/scripts/agent-eval/probe-sweep.mjs +119 -0
- package/scripts/agent-eval/probe-trace.mjs +20 -0
- package/scripts/agent-eval/run-arms.sh +56 -0
- package/scripts/agent-eval/seq-matrix.mjs +137 -0
- package/scripts/build-bundle.sh +118 -0
- package/scripts/npm-sdk.js +75 -0
- package/scripts/npm-shim.js +246 -0
- package/scripts/pack-npm.sh +119 -0
- package/scripts/prepare-release.mjs +270 -0
- package/scripts/patch-tree-sitter-dart.js +0 -112
- package/scripts/release.sh +0 -68
|
@@ -48,6 +48,7 @@ const liquid_extractor_1 = require("./liquid-extractor");
|
|
|
48
48
|
const svelte_extractor_1 = require("./svelte-extractor");
|
|
49
49
|
const dfm_extractor_1 = require("./dfm-extractor");
|
|
50
50
|
const vue_extractor_1 = require("./vue-extractor");
|
|
51
|
+
const mybatis_extractor_1 = require("./mybatis-extractor");
|
|
51
52
|
const frameworks_1 = require("../resolution/frameworks");
|
|
52
53
|
// Re-export for backward compatibility
|
|
53
54
|
var tree_sitter_helpers_2 = require("./tree-sitter-helpers");
|
|
@@ -56,6 +57,9 @@ Object.defineProperty(exports, "generateNodeId", { enumerable: true, get: functi
|
|
|
56
57
|
* Extract the name from a node based on language
|
|
57
58
|
*/
|
|
58
59
|
function extractName(node, source, extractor) {
|
|
60
|
+
const hookName = extractor.resolveName?.(node, source);
|
|
61
|
+
if (hookName)
|
|
62
|
+
return hookName;
|
|
59
63
|
// Try field name first
|
|
60
64
|
const nameNode = (0, tree_sitter_helpers_1.getChildByField)(node, extractor.nameField);
|
|
61
65
|
if (nameNode) {
|
|
@@ -72,6 +76,19 @@ function extractName(node, source, extractor) {
|
|
|
72
76
|
const innerName = (0, tree_sitter_helpers_1.getChildByField)(resolved, 'declarator') || resolved.namedChild(0);
|
|
73
77
|
return innerName ? (0, tree_sitter_helpers_1.getNodeText)(innerName, source) : (0, tree_sitter_helpers_1.getNodeText)(resolved, source);
|
|
74
78
|
}
|
|
79
|
+
// Lua: `function t.f()` / `function t:m()` — the name node is a dot/method
|
|
80
|
+
// index expression; the simple name is the trailing field/method (the table
|
|
81
|
+
// receiver is captured separately via getReceiverType).
|
|
82
|
+
if (resolved.type === 'dot_index_expression') {
|
|
83
|
+
const field = (0, tree_sitter_helpers_1.getChildByField)(resolved, 'field');
|
|
84
|
+
if (field)
|
|
85
|
+
return (0, tree_sitter_helpers_1.getNodeText)(field, source);
|
|
86
|
+
}
|
|
87
|
+
if (resolved.type === 'method_index_expression') {
|
|
88
|
+
const method = (0, tree_sitter_helpers_1.getChildByField)(resolved, 'method');
|
|
89
|
+
if (method)
|
|
90
|
+
return (0, tree_sitter_helpers_1.getNodeText)(method, source);
|
|
91
|
+
}
|
|
75
92
|
return (0, tree_sitter_helpers_1.getNodeText)(resolved, source);
|
|
76
93
|
}
|
|
77
94
|
// For Dart method_signature, look inside inner signature types
|
|
@@ -205,7 +222,16 @@ class TreeSitterExtractor {
|
|
|
205
222
|
this.nodes.push(fileNode);
|
|
206
223
|
// Push file node onto stack so top-level declarations get contains edges
|
|
207
224
|
this.nodeStack.push(fileNode.id);
|
|
225
|
+
// File-level package declaration (Kotlin/Java). Creates an implicit
|
|
226
|
+
// `namespace` node wrapping every top-level declaration so their
|
|
227
|
+
// qualifiedName carries the FQN — required for cross-file import
|
|
228
|
+
// resolution on JVM languages where filename ≠ class name.
|
|
229
|
+
const packageNodeId = this.extractFilePackage(this.tree.rootNode);
|
|
230
|
+
if (packageNodeId)
|
|
231
|
+
this.nodeStack.push(packageNodeId);
|
|
208
232
|
this.visitNode(this.tree.rootNode);
|
|
233
|
+
if (packageNodeId)
|
|
234
|
+
this.nodeStack.pop();
|
|
209
235
|
this.nodeStack.pop();
|
|
210
236
|
}
|
|
211
237
|
catch (error) {
|
|
@@ -370,6 +396,17 @@ class TreeSitterExtractor {
|
|
|
370
396
|
// their own `calls` refs.
|
|
371
397
|
else if (INSTANTIATION_KINDS.has(nodeType)) {
|
|
372
398
|
this.extractInstantiation(node);
|
|
399
|
+
// Java/C# `new T(...) { ... }` — anonymous class with body. Without
|
|
400
|
+
// extracting it as a class node + its methods, the interface→impl
|
|
401
|
+
// synthesizer (Phase 5.5) can't bridge T's abstract methods to the
|
|
402
|
+
// anonymous overrides, and an agent investigating a call through T
|
|
403
|
+
// (`strategy.iterator(...)` where strategy is a Strategy lambda body)
|
|
404
|
+
// has to Read the file to find the actual implementation.
|
|
405
|
+
const anonBody = this.findAnonymousClassBody(node);
|
|
406
|
+
if (anonBody) {
|
|
407
|
+
this.extractAnonymousClass(node, anonBody);
|
|
408
|
+
skipChildren = true;
|
|
409
|
+
}
|
|
373
410
|
}
|
|
374
411
|
// (Decorator handling lives inside the symbol-creating extractors
|
|
375
412
|
// — extractClass / extractFunction / extractProperty — because the
|
|
@@ -379,6 +416,20 @@ class TreeSitterExtractor {
|
|
|
379
416
|
else if (nodeType === 'impl_item') {
|
|
380
417
|
this.extractRustImplItem(node);
|
|
381
418
|
}
|
|
419
|
+
// TypeScript interface members: property_signature (`foo: T`, `foo?: T`)
|
|
420
|
+
// and method_signature (`foo(arg: A): R`) both carry type annotations the
|
|
421
|
+
// interface walker would otherwise drop. Extract them as `references`
|
|
422
|
+
// edges from the interface so resolvers can wire callers/impact for
|
|
423
|
+
// types that only appear in interface members.
|
|
424
|
+
else if ((nodeType === 'property_signature' || nodeType === 'method_signature') &&
|
|
425
|
+
this.isInsideClassLikeNode() &&
|
|
426
|
+
this.TYPE_ANNOTATION_LANGUAGES.has(this.language)) {
|
|
427
|
+
const parentId = this.nodeStack[this.nodeStack.length - 1];
|
|
428
|
+
if (parentId) {
|
|
429
|
+
this.extractTypeAnnotations(node, parentId);
|
|
430
|
+
}
|
|
431
|
+
// don't skipChildren — nested signatures still need traversal
|
|
432
|
+
}
|
|
382
433
|
// Visit children (unless the extract method already visited them)
|
|
383
434
|
if (!skipChildren) {
|
|
384
435
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
@@ -399,6 +450,19 @@ class TreeSitterExtractor {
|
|
|
399
450
|
return null;
|
|
400
451
|
}
|
|
401
452
|
const id = (0, tree_sitter_helpers_1.generateNodeId)(this.filePath, kind, name, node.startPosition.row + 1);
|
|
453
|
+
// Some grammars (e.g. Dart) model a function/method body as a *sibling* of
|
|
454
|
+
// the signature node, so the declaration node's own range is just the
|
|
455
|
+
// signature line. Extend endLine to the resolved body when it sits beyond
|
|
456
|
+
// the node so the node spans its body — required for any body-level analysis
|
|
457
|
+
// (callees, the callback synthesizer's body scan, context slices). Guarded to
|
|
458
|
+
// only ever extend: for child-body grammars the body is within range (no-op).
|
|
459
|
+
let endLine = node.endPosition.row + 1;
|
|
460
|
+
if (kind === 'function' || kind === 'method') {
|
|
461
|
+
const body = this.extractor?.resolveBody?.(node, this.extractor.bodyField);
|
|
462
|
+
if (body && body.endPosition.row + 1 > endLine) {
|
|
463
|
+
endLine = body.endPosition.row + 1;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
402
466
|
const newNode = {
|
|
403
467
|
id,
|
|
404
468
|
kind,
|
|
@@ -407,7 +471,7 @@ class TreeSitterExtractor {
|
|
|
407
471
|
filePath: this.filePath,
|
|
408
472
|
language: this.language,
|
|
409
473
|
startLine: node.startPosition.row + 1,
|
|
410
|
-
endLine
|
|
474
|
+
endLine,
|
|
411
475
|
startColumn: node.startPosition.column,
|
|
412
476
|
endColumn: node.endPosition.column,
|
|
413
477
|
updatedAt: Date.now(),
|
|
@@ -439,6 +503,32 @@ class TreeSitterExtractor {
|
|
|
439
503
|
}
|
|
440
504
|
return null;
|
|
441
505
|
}
|
|
506
|
+
/**
|
|
507
|
+
* Find a `packageTypes` child under the root, create a `namespace` node
|
|
508
|
+
* for it, and return its id so the caller can scope top-level
|
|
509
|
+
* declarations underneath. Returns null when no package header is
|
|
510
|
+
* present (script files, .kts without a package).
|
|
511
|
+
*/
|
|
512
|
+
extractFilePackage(rootNode) {
|
|
513
|
+
const types = this.extractor?.packageTypes;
|
|
514
|
+
if (!types || types.length === 0 || !this.extractor?.extractPackage)
|
|
515
|
+
return null;
|
|
516
|
+
let pkgNode = null;
|
|
517
|
+
for (let i = 0; i < rootNode.namedChildCount; i++) {
|
|
518
|
+
const child = rootNode.namedChild(i);
|
|
519
|
+
if (child && types.includes(child.type)) {
|
|
520
|
+
pkgNode = child;
|
|
521
|
+
break;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
if (!pkgNode)
|
|
525
|
+
return null;
|
|
526
|
+
const pkgName = this.extractor.extractPackage(pkgNode, this.source);
|
|
527
|
+
if (!pkgName)
|
|
528
|
+
return null;
|
|
529
|
+
const ns = this.createNode('namespace', pkgName, pkgNode);
|
|
530
|
+
return ns?.id ?? null;
|
|
531
|
+
}
|
|
442
532
|
/**
|
|
443
533
|
* Build qualified name from node stack
|
|
444
534
|
*/
|
|
@@ -497,7 +587,7 @@ class TreeSitterExtractor {
|
|
|
497
587
|
/**
|
|
498
588
|
* Extract a function
|
|
499
589
|
*/
|
|
500
|
-
extractFunction(node) {
|
|
590
|
+
extractFunction(node, nameOverride) {
|
|
501
591
|
if (!this.extractor)
|
|
502
592
|
return;
|
|
503
593
|
// If the language provides getReceiverType and this function has a receiver
|
|
@@ -506,12 +596,17 @@ class TreeSitterExtractor {
|
|
|
506
596
|
this.extractMethod(node);
|
|
507
597
|
return;
|
|
508
598
|
}
|
|
509
|
-
|
|
599
|
+
// nameOverride is supplied only for explicitly-named anonymous functions the
|
|
600
|
+
// caller resolved itself (e.g. arrow values of exported-const object members
|
|
601
|
+
// — SvelteKit actions). Inline-object arrows reached by the general walker
|
|
602
|
+
// get no override, so they still fall through to the <anonymous> skip below.
|
|
603
|
+
let name = nameOverride ?? extractName(node, this.source, this.extractor);
|
|
510
604
|
// For arrow functions and function expressions assigned to variables,
|
|
511
605
|
// resolve the name from the parent variable_declarator.
|
|
512
606
|
// e.g. `export const useAuth = () => { ... }` — the arrow_function node
|
|
513
607
|
// has no `name` field; the name lives on the variable_declarator.
|
|
514
|
-
if (
|
|
608
|
+
if (!nameOverride &&
|
|
609
|
+
name === '<anonymous>' &&
|
|
515
610
|
(node.type === 'arrow_function' || node.type === 'function_expression')) {
|
|
516
611
|
const parent = node.parent;
|
|
517
612
|
if (parent?.type === 'variable_declarator') {
|
|
@@ -521,8 +616,19 @@ class TreeSitterExtractor {
|
|
|
521
616
|
}
|
|
522
617
|
}
|
|
523
618
|
}
|
|
524
|
-
if (name === '<anonymous>')
|
|
525
|
-
|
|
619
|
+
if (name === '<anonymous>') {
|
|
620
|
+
// Don't emit a node for the anonymous wrapper itself, but still visit its
|
|
621
|
+
// body: AMD/RequireJS and CommonJS module wrappers (`define([], function(){…})`,
|
|
622
|
+
// `(function(){…})()`) hold named inner functions and calls that would
|
|
623
|
+
// otherwise be lost — the dispatcher set skipChildren, so nothing else
|
|
624
|
+
// descends into this subtree. (#528)
|
|
625
|
+
const body = this.extractor.resolveBody?.(node, this.extractor.bodyField)
|
|
626
|
+
?? (0, tree_sitter_helpers_1.getChildByField)(node, this.extractor.bodyField);
|
|
627
|
+
if (body) {
|
|
628
|
+
this.visitFunctionBody(body, '');
|
|
629
|
+
}
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
526
632
|
// Check for misparse artifacts (e.g. C++ macros causing "namespace detail" functions)
|
|
527
633
|
// Skip the node but still visit the body for calls and structural nodes
|
|
528
634
|
if (this.extractor.isMisparsedFunction?.(name, node)) {
|
|
@@ -617,6 +723,11 @@ class TreeSitterExtractor {
|
|
|
617
723
|
// in inline objects). These are ephemeral and create noise (e.g., Svelte context
|
|
618
724
|
// objects: `ctx.set({ get view() { ... } })`).
|
|
619
725
|
if (node.parent?.type === 'object' || node.parent?.type === 'object_expression') {
|
|
726
|
+
const body = this.extractor.resolveBody?.(node, this.extractor.bodyField)
|
|
727
|
+
?? (0, tree_sitter_helpers_1.getChildByField)(node, this.extractor.bodyField);
|
|
728
|
+
if (body) {
|
|
729
|
+
this.visitFunctionBody(body, '');
|
|
730
|
+
}
|
|
620
731
|
return;
|
|
621
732
|
}
|
|
622
733
|
// Not inside a class-like node and no receiver type, treat as function
|
|
@@ -818,12 +929,13 @@ class TreeSitterExtractor {
|
|
|
818
929
|
const docstring = (0, tree_sitter_helpers_1.getPrecedingDocstring)(node, this.source);
|
|
819
930
|
const visibility = this.extractor.getVisibility?.(node);
|
|
820
931
|
const isStatic = this.extractor.isStatic?.(node) ?? false;
|
|
821
|
-
|
|
822
|
-
const nameNode =
|
|
823
|
-
|
|
824
|
-
|
|
932
|
+
const hookName = this.extractor.extractPropertyName?.(node, this.source);
|
|
933
|
+
const nameNode = hookName
|
|
934
|
+
? null
|
|
935
|
+
: (0, tree_sitter_helpers_1.getChildByField)(node, 'name') || node.namedChildren.find(c => c.type === 'identifier');
|
|
936
|
+
const name = hookName ?? (nameNode ? (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source) : null);
|
|
937
|
+
if (!name)
|
|
825
938
|
return;
|
|
826
|
-
const name = (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source);
|
|
827
939
|
// Get property type from the type child (first named child that isn't modifier or identifier)
|
|
828
940
|
const typeNode = node.namedChildren.find(c => c.type !== 'modifier' && c.type !== 'modifiers'
|
|
829
941
|
&& c.type !== 'identifier' && c.type !== 'accessor_list'
|
|
@@ -840,6 +952,10 @@ class TreeSitterExtractor {
|
|
|
840
952
|
// decorator->target relationship for class properties too.
|
|
841
953
|
if (propNode) {
|
|
842
954
|
this.extractDecoratorsFor(node, propNode.id);
|
|
955
|
+
// Emit `references` edges from the property to types named in its
|
|
956
|
+
// type annotation (#381). The generic walker handles TS-style
|
|
957
|
+
// `type_annotation` children; the C# branch walks the `type` field.
|
|
958
|
+
this.extractTypeAnnotations(node, propNode.id);
|
|
843
959
|
}
|
|
844
960
|
}
|
|
845
961
|
/**
|
|
@@ -912,8 +1028,15 @@ class TreeSitterExtractor {
|
|
|
912
1028
|
});
|
|
913
1029
|
// Java/Kotlin annotations / TS field decorators sit on the
|
|
914
1030
|
// outer field_declaration, not on the individual declarator.
|
|
915
|
-
if (fieldNode)
|
|
1031
|
+
if (fieldNode) {
|
|
916
1032
|
this.extractDecoratorsFor(node, fieldNode.id);
|
|
1033
|
+
// Same as properties: emit `references` to the field's annotated
|
|
1034
|
+
// type. The outer `field_declaration` is the right scope to
|
|
1035
|
+
// search from — C# carries the `type` inside `variable_declaration`
|
|
1036
|
+
// and the language-aware path in `extractTypeAnnotations` descends
|
|
1037
|
+
// into that wrapper (#381).
|
|
1038
|
+
this.extractTypeAnnotations(node, fieldNode.id);
|
|
1039
|
+
}
|
|
917
1040
|
}
|
|
918
1041
|
}
|
|
919
1042
|
else {
|
|
@@ -930,6 +1053,114 @@ class TreeSitterExtractor {
|
|
|
930
1053
|
}
|
|
931
1054
|
}
|
|
932
1055
|
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Extract function-valued properties of an object literal as named function
|
|
1058
|
+
* nodes (named by their property key). Shared by the two object-of-functions
|
|
1059
|
+
* shapes in extractVariable: the object as a direct const value, and the
|
|
1060
|
+
* object returned by a store-initializer call. Handles both `key: () => {}` /
|
|
1061
|
+
* `key: function() {}` pairs and method shorthand `key() {}`.
|
|
1062
|
+
*/
|
|
1063
|
+
extractObjectLiteralFunctions(obj) {
|
|
1064
|
+
for (let i = 0; i < obj.namedChildCount; i++) {
|
|
1065
|
+
const member = obj.namedChild(i);
|
|
1066
|
+
if (!member)
|
|
1067
|
+
continue;
|
|
1068
|
+
if (member.type === 'pair') {
|
|
1069
|
+
const key = (0, tree_sitter_helpers_1.getChildByField)(member, 'key');
|
|
1070
|
+
const value = (0, tree_sitter_helpers_1.getChildByField)(member, 'value');
|
|
1071
|
+
if (key && value && (value.type === 'arrow_function' || value.type === 'function_expression')) {
|
|
1072
|
+
this.extractFunction(value, this.objectKeyName(key));
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
else if (member.type === 'method_definition') {
|
|
1076
|
+
// Method shorthand: `{ fetchUser() {...} }`. extractMethod deliberately
|
|
1077
|
+
// skips object-literal methods, so route through extractFunction with an
|
|
1078
|
+
// explicit name (method_definition exposes a `body` field, so resolveBody
|
|
1079
|
+
// falls through to it and the node spans the full method).
|
|
1080
|
+
const key = (0, tree_sitter_helpers_1.getChildByField)(member, 'name');
|
|
1081
|
+
if (key)
|
|
1082
|
+
this.extractFunction(member, this.objectKeyName(key));
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
/** Property-key text with surrounding quotes stripped (`'foo'` → `foo`). */
|
|
1087
|
+
objectKeyName(key) {
|
|
1088
|
+
return (0, tree_sitter_helpers_1.getNodeText)(key, this.source).replace(/^['"`]|['"`]$/g, '');
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Given a `call_expression` initializer (`create((set, get) => ({...}))`),
|
|
1092
|
+
* find the object literal RETURNED by a function argument — descending through
|
|
1093
|
+
* nested call_expression arguments so middleware wrappers are unwrapped
|
|
1094
|
+
* (`create(persist((set, get) => ({...}), {...}))`, devtools, immer,
|
|
1095
|
+
* subscribeWithSelector). Returns null when no such object is found — the
|
|
1096
|
+
* common case for ordinary call initializers — so this stays cheap and silent
|
|
1097
|
+
* rather than guessing. Keyed purely on AST shape; no library names.
|
|
1098
|
+
*/
|
|
1099
|
+
findInitializerReturnedObject(callNode, depth = 0) {
|
|
1100
|
+
if (depth > 4)
|
|
1101
|
+
return null;
|
|
1102
|
+
const args = (0, tree_sitter_helpers_1.getChildByField)(callNode, 'arguments');
|
|
1103
|
+
if (!args)
|
|
1104
|
+
return null;
|
|
1105
|
+
for (let i = 0; i < args.namedChildCount; i++) {
|
|
1106
|
+
const arg = args.namedChild(i);
|
|
1107
|
+
if (!arg)
|
|
1108
|
+
continue;
|
|
1109
|
+
if (arg.type === 'arrow_function' || arg.type === 'function_expression') {
|
|
1110
|
+
const obj = this.functionReturnedObject(arg);
|
|
1111
|
+
if (obj)
|
|
1112
|
+
return obj;
|
|
1113
|
+
}
|
|
1114
|
+
else if (arg.type === 'call_expression') {
|
|
1115
|
+
const obj = this.findInitializerReturnedObject(arg, depth + 1);
|
|
1116
|
+
if (obj)
|
|
1117
|
+
return obj;
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
return null;
|
|
1121
|
+
}
|
|
1122
|
+
/**
|
|
1123
|
+
* The object literal a function expression returns — either the `=> ({...})`
|
|
1124
|
+
* arrow form (a parenthesized_expression wrapping an object) or a
|
|
1125
|
+
* `=> { return {...} }` block. Returns null for any other body shape.
|
|
1126
|
+
*/
|
|
1127
|
+
functionReturnedObject(fnNode) {
|
|
1128
|
+
const body = (0, tree_sitter_helpers_1.getChildByField)(fnNode, 'body');
|
|
1129
|
+
if (!body)
|
|
1130
|
+
return null;
|
|
1131
|
+
const asObject = (n) => {
|
|
1132
|
+
if (!n)
|
|
1133
|
+
return null;
|
|
1134
|
+
if (n.type === 'object' || n.type === 'object_expression')
|
|
1135
|
+
return n;
|
|
1136
|
+
if (n.type === 'parenthesized_expression') {
|
|
1137
|
+
for (let i = 0; i < n.namedChildCount; i++) {
|
|
1138
|
+
const inner = asObject(n.namedChild(i));
|
|
1139
|
+
if (inner)
|
|
1140
|
+
return inner;
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
return null;
|
|
1144
|
+
};
|
|
1145
|
+
// `(set, get) => ({...})` — body is the (parenthesized) object directly.
|
|
1146
|
+
const direct = asObject(body);
|
|
1147
|
+
if (direct)
|
|
1148
|
+
return direct;
|
|
1149
|
+
// `(set, get) => { return {...} }` — scan top-level return statements.
|
|
1150
|
+
if (body.type === 'statement_block') {
|
|
1151
|
+
for (let i = 0; i < body.namedChildCount; i++) {
|
|
1152
|
+
const stmt = body.namedChild(i);
|
|
1153
|
+
if (stmt?.type !== 'return_statement')
|
|
1154
|
+
continue;
|
|
1155
|
+
for (let j = 0; j < stmt.namedChildCount; j++) {
|
|
1156
|
+
const obj = asObject(stmt.namedChild(j));
|
|
1157
|
+
if (obj)
|
|
1158
|
+
return obj;
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
return null;
|
|
1163
|
+
}
|
|
933
1164
|
/**
|
|
934
1165
|
* Extract a variable declaration (const, let, var, etc.)
|
|
935
1166
|
*
|
|
@@ -981,6 +1212,42 @@ class TreeSitterExtractor {
|
|
|
981
1212
|
if (varNode) {
|
|
982
1213
|
this.extractVariableTypeAnnotation(child, varNode.id);
|
|
983
1214
|
}
|
|
1215
|
+
// Exported const object-of-functions — extract each function-valued
|
|
1216
|
+
// property as a function named by its key + walk its body so its
|
|
1217
|
+
// calls are captured. Two shapes, both keyed on AST shape (not on any
|
|
1218
|
+
// library name):
|
|
1219
|
+
// `export const actions = { default: async () => {} }` — object is
|
|
1220
|
+
// the DIRECT value (SvelteKit form actions / handler maps / route
|
|
1221
|
+
// tables).
|
|
1222
|
+
// `export const useStore = create((set, get) => ({ fetchUser:
|
|
1223
|
+
// async () => {} }))` — object is RETURNED by an initializer call,
|
|
1224
|
+
// possibly through middleware wrappers (persist/devtools/immer).
|
|
1225
|
+
// Covers Zustand/Redux/Pinia/MobX stores generically. Without
|
|
1226
|
+
// this, store actions exist only as object-literal properties —
|
|
1227
|
+
// never nodes — so `node`/`callers` on `fetchUser` return "not
|
|
1228
|
+
// found" and the agent Reads the store to reconstruct the flow.
|
|
1229
|
+
// Scoped to EXPORTED consts to exclude inline-object noise
|
|
1230
|
+
// (`ctx.set({...})`) the object-method skip deliberately avoids.
|
|
1231
|
+
const objectOfFns = valueNode && (valueNode.type === 'object' || valueNode.type === 'object_expression')
|
|
1232
|
+
? valueNode
|
|
1233
|
+
: valueNode?.type === 'call_expression'
|
|
1234
|
+
? this.findInitializerReturnedObject(valueNode)
|
|
1235
|
+
: null;
|
|
1236
|
+
const extractObjectMethods = isExported && !!objectOfFns;
|
|
1237
|
+
// Visit the initializer body for calls — EXCEPT object literals (their
|
|
1238
|
+
// function-valued properties are extracted below) and the store-factory
|
|
1239
|
+
// call whose returned object we extract method-by-method below (walking
|
|
1240
|
+
// the whole call would re-visit those method arrows and mis-attribute
|
|
1241
|
+
// their inner calls to the file/module scope).
|
|
1242
|
+
if (valueNode &&
|
|
1243
|
+
valueNode.type !== 'object' &&
|
|
1244
|
+
valueNode.type !== 'object_expression' &&
|
|
1245
|
+
!(extractObjectMethods && valueNode.type === 'call_expression')) {
|
|
1246
|
+
this.visitFunctionBody(valueNode, '');
|
|
1247
|
+
}
|
|
1248
|
+
if (extractObjectMethods && objectOfFns) {
|
|
1249
|
+
this.extractObjectLiteralFunctions(objectOfFns);
|
|
1250
|
+
}
|
|
984
1251
|
}
|
|
985
1252
|
}
|
|
986
1253
|
}
|
|
@@ -1039,6 +1306,25 @@ class TreeSitterExtractor {
|
|
|
1039
1306
|
}
|
|
1040
1307
|
}
|
|
1041
1308
|
}
|
|
1309
|
+
else if (this.language === 'lua' || this.language === 'luau') {
|
|
1310
|
+
// Lua/Luau: variable_declaration → assignment_statement → variable_list
|
|
1311
|
+
// (name: identifier...) = expression_list. `local x, y = 1, 2`
|
|
1312
|
+
// declares multiple names; only plain identifiers are locals.
|
|
1313
|
+
const assign = node.namedChildren.find((c) => c.type === 'assignment_statement') ?? node;
|
|
1314
|
+
const varList = assign.namedChildren.find((c) => c.type === 'variable_list');
|
|
1315
|
+
const exprList = assign.namedChildren.find((c) => c.type === 'expression_list');
|
|
1316
|
+
const values = exprList ? exprList.namedChildren : [];
|
|
1317
|
+
const names = varList ? varList.namedChildren.filter((c) => c.type === 'identifier') : [];
|
|
1318
|
+
names.forEach((nameNode, i) => {
|
|
1319
|
+
const name = (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source);
|
|
1320
|
+
if (!name)
|
|
1321
|
+
return;
|
|
1322
|
+
const valueNode = values[i];
|
|
1323
|
+
const initValue = valueNode ? (0, tree_sitter_helpers_1.getNodeText)(valueNode, this.source).slice(0, 100) : undefined;
|
|
1324
|
+
const initSignature = initValue ? `= ${initValue}${initValue.length >= 100 ? '...' : ''}` : undefined;
|
|
1325
|
+
this.createNode(kind, name, nameNode, { docstring, signature: initSignature, isExported });
|
|
1326
|
+
});
|
|
1327
|
+
}
|
|
1042
1328
|
else {
|
|
1043
1329
|
// Generic fallback for other languages
|
|
1044
1330
|
// Try to find identifier children
|
|
@@ -1148,10 +1434,89 @@ class TreeSitterExtractor {
|
|
|
1148
1434
|
const value = (0, tree_sitter_helpers_1.getChildByField)(node, 'value');
|
|
1149
1435
|
if (value) {
|
|
1150
1436
|
this.extractTypeRefsFromSubtree(value, typeAliasNode.id);
|
|
1437
|
+
// `type X = { foo: T; bar(): T }` — make the members first-class
|
|
1438
|
+
// property/method nodes under the type alias so `recorder.stop()`
|
|
1439
|
+
// can attach the call edge to `RecorderHandle.stop` instead of
|
|
1440
|
+
// an unrelated class method picked by path-proximity (#359).
|
|
1441
|
+
if (this.language === 'typescript' || this.language === 'tsx') {
|
|
1442
|
+
this.extractTsTypeAliasMembers(value, typeAliasNode);
|
|
1443
|
+
}
|
|
1151
1444
|
}
|
|
1152
1445
|
}
|
|
1153
1446
|
return false;
|
|
1154
1447
|
}
|
|
1448
|
+
/**
|
|
1449
|
+
* Surface the members of a TypeScript `type X = { ... }` (or intersection
|
|
1450
|
+
* thereof) as `property` / `method` nodes under the type-alias node. Only
|
|
1451
|
+
* walks the immediate object_type / intersection operands so anonymous
|
|
1452
|
+
* nested object types inside generic arguments (`Promise<{ ok: true }>`)
|
|
1453
|
+
* don't produce phantom members.
|
|
1454
|
+
*/
|
|
1455
|
+
extractTsTypeAliasMembers(value, typeAliasNode) {
|
|
1456
|
+
const objectTypes = [];
|
|
1457
|
+
if (value.type === 'object_type') {
|
|
1458
|
+
objectTypes.push(value);
|
|
1459
|
+
}
|
|
1460
|
+
else if (value.type === 'intersection_type') {
|
|
1461
|
+
for (let i = 0; i < value.namedChildCount; i++) {
|
|
1462
|
+
const op = value.namedChild(i);
|
|
1463
|
+
if (op && op.type === 'object_type')
|
|
1464
|
+
objectTypes.push(op);
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
else {
|
|
1468
|
+
return;
|
|
1469
|
+
}
|
|
1470
|
+
this.nodeStack.push(typeAliasNode.id);
|
|
1471
|
+
for (const objType of objectTypes) {
|
|
1472
|
+
for (let i = 0; i < objType.namedChildCount; i++) {
|
|
1473
|
+
const child = objType.namedChild(i);
|
|
1474
|
+
if (!child)
|
|
1475
|
+
continue;
|
|
1476
|
+
if (child.type !== 'property_signature' && child.type !== 'method_signature')
|
|
1477
|
+
continue;
|
|
1478
|
+
const nameNode = (0, tree_sitter_helpers_1.getChildByField)(child, 'name');
|
|
1479
|
+
const memberName = nameNode ? (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source) : '';
|
|
1480
|
+
if (!memberName)
|
|
1481
|
+
continue;
|
|
1482
|
+
// `foo: () => T` and `foo(): T` are functionally a method on the
|
|
1483
|
+
// type contract. Treat the property_signature with a function-typed
|
|
1484
|
+
// annotation as a method too so call sites can resolve to it.
|
|
1485
|
+
const memberKind = child.type === 'method_signature'
|
|
1486
|
+
? 'method'
|
|
1487
|
+
: this.isTsFunctionTypedProperty(child) ? 'method' : 'property';
|
|
1488
|
+
const docstring = (0, tree_sitter_helpers_1.getPrecedingDocstring)(child, this.source);
|
|
1489
|
+
const signature = (0, tree_sitter_helpers_1.getNodeText)(child, this.source);
|
|
1490
|
+
this.createNode(memberKind, memberName, child, {
|
|
1491
|
+
docstring,
|
|
1492
|
+
signature,
|
|
1493
|
+
qualifiedName: `${typeAliasNode.name}::${memberName}`,
|
|
1494
|
+
});
|
|
1495
|
+
// Emit `references` edges from the type alias to types named in the
|
|
1496
|
+
// member's signature, matching the interface-member behavior added in
|
|
1497
|
+
// #432. We attach refs to the type-alias parent (consistent with
|
|
1498
|
+
// interface property_signature treatment).
|
|
1499
|
+
this.extractTypeAnnotations(child, typeAliasNode.id);
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
this.nodeStack.pop();
|
|
1503
|
+
}
|
|
1504
|
+
/**
|
|
1505
|
+
* `foo: () => T` → property_signature whose type_annotation contains a
|
|
1506
|
+
* `function_type`. Treat that as a method-shaped contract member, since
|
|
1507
|
+
* the call site `obj.foo()` has identical semantics to `bar(): T`.
|
|
1508
|
+
*/
|
|
1509
|
+
isTsFunctionTypedProperty(propertySignature) {
|
|
1510
|
+
const typeAnno = (0, tree_sitter_helpers_1.getChildByField)(propertySignature, 'type');
|
|
1511
|
+
if (!typeAnno)
|
|
1512
|
+
return false;
|
|
1513
|
+
for (let i = 0; i < typeAnno.namedChildCount; i++) {
|
|
1514
|
+
const inner = typeAnno.namedChild(i);
|
|
1515
|
+
if (inner && inner.type === 'function_type')
|
|
1516
|
+
return true;
|
|
1517
|
+
}
|
|
1518
|
+
return false;
|
|
1519
|
+
}
|
|
1155
1520
|
// extractExportedVariables removed — the walker now descends into
|
|
1156
1521
|
// export_statement children and the inner declaration's dedicated
|
|
1157
1522
|
// extractor (extractVariable, extractFunction, extractClass, etc.)
|
|
@@ -1300,7 +1665,25 @@ class TreeSitterExtractor {
|
|
|
1300
1665
|
if (nameField && objectField && (node.type === 'method_invocation' || node.type === 'member_call_expression' || node.type === 'scoped_call_expression')) {
|
|
1301
1666
|
// Method call with explicit receiver: receiver.method() / $receiver->method() / ClassName::method()
|
|
1302
1667
|
const methodName = (0, tree_sitter_helpers_1.getNodeText)(nameField, this.source);
|
|
1303
|
-
|
|
1668
|
+
// Java `this.userbo.toLogin2()` parses as method_invocation(object=field_access(this, userbo)).
|
|
1669
|
+
// Without unwrapping, receiverName is `this.userbo` and the name-matcher's
|
|
1670
|
+
// single-dot receiver regex fails. Pull out the immediate field after `this.`
|
|
1671
|
+
// so the receiver is the field name (`userbo`), which the resolver can then
|
|
1672
|
+
// look up in the enclosing class's field declarations.
|
|
1673
|
+
let receiverName;
|
|
1674
|
+
if (objectField.type === 'field_access') {
|
|
1675
|
+
const inner = (0, tree_sitter_helpers_1.getChildByField)(objectField, 'object');
|
|
1676
|
+
const fld = (0, tree_sitter_helpers_1.getChildByField)(objectField, 'field');
|
|
1677
|
+
if (inner && fld && (inner.type === 'this' || inner.type === 'this_expression')) {
|
|
1678
|
+
receiverName = (0, tree_sitter_helpers_1.getNodeText)(fld, this.source);
|
|
1679
|
+
}
|
|
1680
|
+
else {
|
|
1681
|
+
receiverName = (0, tree_sitter_helpers_1.getNodeText)(objectField, this.source);
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
else {
|
|
1685
|
+
receiverName = (0, tree_sitter_helpers_1.getNodeText)(objectField, this.source);
|
|
1686
|
+
}
|
|
1304
1687
|
// Strip PHP $ prefix from variable names
|
|
1305
1688
|
receiverName = receiverName.replace(/^\$/, '');
|
|
1306
1689
|
if (methodName) {
|
|
@@ -1314,13 +1697,51 @@ class TreeSitterExtractor {
|
|
|
1314
1697
|
}
|
|
1315
1698
|
}
|
|
1316
1699
|
}
|
|
1700
|
+
else if (node.type === 'message_expression') {
|
|
1701
|
+
// ObjC message expressions emit one `method` field child per selector
|
|
1702
|
+
// keyword: `[obj a:1 b:2 c:3]` has three `method=identifier` siblings.
|
|
1703
|
+
// Joining them with `:` reconstructs the full selector and matches the
|
|
1704
|
+
// multi-part selector names produced by the ObjC method_definition
|
|
1705
|
+
// extractor (`extractObjcMethodName` in languages/objc.ts). Without this
|
|
1706
|
+
// join, multi-keyword call sites only emitted the first keyword and never
|
|
1707
|
+
// resolved to their target methods (e.g. `GET:parameters:headers:...` had
|
|
1708
|
+
// zero callers despite obviously being called).
|
|
1709
|
+
const methodKeywords = [];
|
|
1710
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
1711
|
+
if (node.fieldNameForNamedChild(i) === 'method') {
|
|
1712
|
+
const kw = node.namedChild(i);
|
|
1713
|
+
if (kw)
|
|
1714
|
+
methodKeywords.push((0, tree_sitter_helpers_1.getNodeText)(kw, this.source));
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
if (methodKeywords.length > 0) {
|
|
1718
|
+
const methodName = methodKeywords.length === 1
|
|
1719
|
+
? methodKeywords[0]
|
|
1720
|
+
: methodKeywords.map((k) => `${k}:`).join('');
|
|
1721
|
+
const receiverField = (0, tree_sitter_helpers_1.getChildByField)(node, 'receiver');
|
|
1722
|
+
const SKIP_RECEIVERS = new Set(['self', 'super']);
|
|
1723
|
+
if (receiverField && receiverField.type !== 'message_expression') {
|
|
1724
|
+
const receiverName = (0, tree_sitter_helpers_1.getNodeText)(receiverField, this.source);
|
|
1725
|
+
if (receiverName && !SKIP_RECEIVERS.has(receiverName)) {
|
|
1726
|
+
calleeName = `${receiverName}.${methodName}`;
|
|
1727
|
+
}
|
|
1728
|
+
else {
|
|
1729
|
+
calleeName = methodName;
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
else {
|
|
1733
|
+
calleeName = methodName;
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1317
1737
|
else {
|
|
1318
1738
|
const func = (0, tree_sitter_helpers_1.getChildByField)(node, 'function') || node.namedChild(0);
|
|
1319
1739
|
if (func) {
|
|
1320
|
-
if (func.type === 'member_expression' || func.type === 'attribute' || func.type === 'selector_expression' || func.type === 'navigation_expression') {
|
|
1740
|
+
if (func.type === 'member_expression' || func.type === 'attribute' || func.type === 'selector_expression' || func.type === 'navigation_expression' || func.type === 'field_expression') {
|
|
1321
1741
|
// Method call: obj.method() or obj.field.method()
|
|
1322
1742
|
// Go uses selector_expression with 'field', JS/TS uses member_expression with 'property'
|
|
1323
1743
|
// Kotlin uses navigation_expression with navigation_suffix > simple_identifier
|
|
1744
|
+
// C/C++ use field_expression for both `obj.method()` and `ptr->method()`
|
|
1324
1745
|
let property = (0, tree_sitter_helpers_1.getChildByField)(func, 'property') || (0, tree_sitter_helpers_1.getChildByField)(func, 'field');
|
|
1325
1746
|
if (!property) {
|
|
1326
1747
|
const child1 = func.namedChild(1);
|
|
@@ -1338,9 +1759,12 @@ class TreeSitterExtractor {
|
|
|
1338
1759
|
// This helps the resolver distinguish method calls from bare function calls
|
|
1339
1760
|
// (e.g., Python's console.print() vs builtin print())
|
|
1340
1761
|
// Skip self/this/cls as they don't aid resolution
|
|
1341
|
-
const receiver = (0, tree_sitter_helpers_1.getChildByField)(func, 'object') ||
|
|
1762
|
+
const receiver = (0, tree_sitter_helpers_1.getChildByField)(func, 'object') ||
|
|
1763
|
+
(0, tree_sitter_helpers_1.getChildByField)(func, 'operand') ||
|
|
1764
|
+
(0, tree_sitter_helpers_1.getChildByField)(func, 'argument') ||
|
|
1765
|
+
func.namedChild(0);
|
|
1342
1766
|
const SKIP_RECEIVERS = new Set(['self', 'this', 'cls', 'super']);
|
|
1343
|
-
if (receiver && (receiver.type === 'identifier' || receiver.type === 'simple_identifier')) {
|
|
1767
|
+
if (receiver && (receiver.type === 'identifier' || receiver.type === 'simple_identifier' || receiver.type === 'field_identifier')) {
|
|
1344
1768
|
const receiverName = (0, tree_sitter_helpers_1.getNodeText)(receiver, this.source);
|
|
1345
1769
|
if (!SKIP_RECEIVERS.has(receiverName)) {
|
|
1346
1770
|
calleeName = `${receiverName}.${methodName}`;
|
|
@@ -1421,6 +1845,76 @@ class TreeSitterExtractor {
|
|
|
1421
1845
|
});
|
|
1422
1846
|
}
|
|
1423
1847
|
}
|
|
1848
|
+
/**
|
|
1849
|
+
* Find a `class_body` child of an `object_creation_expression` — the
|
|
1850
|
+
* marker for an anonymous class (`new T() { ... }`). Returns the body
|
|
1851
|
+
* node so the caller can walk it as the anon class's members.
|
|
1852
|
+
*/
|
|
1853
|
+
findAnonymousClassBody(node) {
|
|
1854
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
1855
|
+
const child = node.namedChild(i);
|
|
1856
|
+
// Java: `class_body`. C# uses the same node kind.
|
|
1857
|
+
if (child && (child.type === 'class_body' || child.type === 'declaration_list')) {
|
|
1858
|
+
return child;
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
return null;
|
|
1862
|
+
}
|
|
1863
|
+
/**
|
|
1864
|
+
* Extract a Java/C# anonymous class — `new T() { ...members }`. Emits a
|
|
1865
|
+
* `class` node named `<T$anon@line>`, an `extends` reference to T (so
|
|
1866
|
+
* Phase 5.5 interface-impl can bridge), and walks the body so its
|
|
1867
|
+
* `method_declaration` members become method nodes under the anon class.
|
|
1868
|
+
*
|
|
1869
|
+
* Why this matters: without anon-class extraction, the overrides inside
|
|
1870
|
+
* a lambda-returned `new T() { @Override int foo(){...} }` are not nodes,
|
|
1871
|
+
* so a call through T.foo (the abstract parent method) has no static
|
|
1872
|
+
* target — the agent has to Read the file to find the implementation.
|
|
1873
|
+
*/
|
|
1874
|
+
extractAnonymousClass(node, body) {
|
|
1875
|
+
if (!this.extractor)
|
|
1876
|
+
return;
|
|
1877
|
+
// The instantiated type sits in the same field/position that
|
|
1878
|
+
// extractInstantiation reads from. Use the same lookup so the anon
|
|
1879
|
+
// class's `extends` target matches the `instantiates` edge.
|
|
1880
|
+
const typeNode = (0, tree_sitter_helpers_1.getChildByField)(node, 'constructor') ||
|
|
1881
|
+
(0, tree_sitter_helpers_1.getChildByField)(node, 'type') ||
|
|
1882
|
+
(0, tree_sitter_helpers_1.getChildByField)(node, 'name') ||
|
|
1883
|
+
node.namedChild(0);
|
|
1884
|
+
let typeName = typeNode ? (0, tree_sitter_helpers_1.getNodeText)(typeNode, this.source) : 'Object';
|
|
1885
|
+
const ltIdx = typeName.indexOf('<');
|
|
1886
|
+
if (ltIdx > 0)
|
|
1887
|
+
typeName = typeName.slice(0, ltIdx);
|
|
1888
|
+
const lastDot = Math.max(typeName.lastIndexOf('.'), typeName.lastIndexOf('::'));
|
|
1889
|
+
if (lastDot >= 0)
|
|
1890
|
+
typeName = typeName.slice(lastDot + 1).replace(/^[:.]/, '');
|
|
1891
|
+
typeName = typeName.trim() || 'Object';
|
|
1892
|
+
const anonName = `<${typeName}$anon@${node.startPosition.row + 1}>`;
|
|
1893
|
+
const classNode = this.createNode('class', anonName, node, {});
|
|
1894
|
+
if (!classNode)
|
|
1895
|
+
return;
|
|
1896
|
+
// The anonymous class implicitly extends/implements the named type.
|
|
1897
|
+
// We can't tell at extraction time whether T is a class or an interface,
|
|
1898
|
+
// so emit `extends`. Resolution will still bind T to whatever it is, and
|
|
1899
|
+
// Phase 5.5 (which already handles both `extends` and `implements`) will
|
|
1900
|
+
// bridge T's methods to the override names found in the anon body.
|
|
1901
|
+
this.unresolvedReferences.push({
|
|
1902
|
+
fromNodeId: classNode.id,
|
|
1903
|
+
referenceName: typeName,
|
|
1904
|
+
referenceKind: 'extends',
|
|
1905
|
+
line: typeNode?.startPosition.row ?? node.startPosition.row,
|
|
1906
|
+
column: typeNode?.startPosition.column ?? node.startPosition.column,
|
|
1907
|
+
});
|
|
1908
|
+
// Walk the body's children so method_declaration nodes inside become
|
|
1909
|
+
// method nodes scoped to the anon class.
|
|
1910
|
+
this.nodeStack.push(classNode.id);
|
|
1911
|
+
for (let i = 0; i < body.namedChildCount; i++) {
|
|
1912
|
+
const child = body.namedChild(i);
|
|
1913
|
+
if (child)
|
|
1914
|
+
this.visitNode(child);
|
|
1915
|
+
}
|
|
1916
|
+
this.nodeStack.pop();
|
|
1917
|
+
}
|
|
1424
1918
|
/**
|
|
1425
1919
|
* Scan `declNode` and its preceding siblings (within the parent's
|
|
1426
1920
|
* named children) for decorator nodes, emitting a `decorates`
|
|
@@ -1551,6 +2045,14 @@ class TreeSitterExtractor {
|
|
|
1551
2045
|
// about `call_expression`, so constructor invocations
|
|
1552
2046
|
// produced no graph edges at all.
|
|
1553
2047
|
this.extractInstantiation(node);
|
|
2048
|
+
// Anonymous class with body: `new T() { ... }` (Java/C#). Extract as
|
|
2049
|
+
// a class so interface-impl synthesis (Phase 5.5) can bridge T's
|
|
2050
|
+
// methods to the overrides — same rationale as in visitNode.
|
|
2051
|
+
const anonBody = this.findAnonymousClassBody(node);
|
|
2052
|
+
if (anonBody) {
|
|
2053
|
+
this.extractAnonymousClass(node, anonBody);
|
|
2054
|
+
return;
|
|
2055
|
+
}
|
|
1554
2056
|
}
|
|
1555
2057
|
else if (this.extractor.extractBareCall) {
|
|
1556
2058
|
const calleeName = this.extractor.extractBareCall(node, this.source);
|
|
@@ -1567,6 +2069,20 @@ class TreeSitterExtractor {
|
|
|
1567
2069
|
}
|
|
1568
2070
|
}
|
|
1569
2071
|
}
|
|
2072
|
+
// Nested NAMED functions inside a body — function declarations and named
|
|
2073
|
+
// function expressions like `.on('mount', function onmount(){})` — become
|
|
2074
|
+
// their own nodes so the graph can link to them (callback handlers, local
|
|
2075
|
+
// helpers). Anonymous arrows/expressions fall through to the default
|
|
2076
|
+
// recursion below, keeping their inner calls attributed to the enclosing
|
|
2077
|
+
// function: this bounds the new nodes to NAMED functions only (no explosion,
|
|
2078
|
+
// no lost edges). extractFunction walks the nested body itself, so we return.
|
|
2079
|
+
if (this.extractor.functionTypes.includes(nodeType)) {
|
|
2080
|
+
const nestedName = extractName(node, this.source, this.extractor);
|
|
2081
|
+
if (nestedName && nestedName !== '<anonymous>') {
|
|
2082
|
+
this.extractFunction(node);
|
|
2083
|
+
return;
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
1570
2086
|
// Extract structural nodes found inside function bodies.
|
|
1571
2087
|
// Each extract method visits its own children, so we return after extracting.
|
|
1572
2088
|
if (this.extractor.classTypes.includes(nodeType)) {
|
|
@@ -1608,6 +2124,42 @@ class TreeSitterExtractor {
|
|
|
1608
2124
|
* Extract inheritance relationships
|
|
1609
2125
|
*/
|
|
1610
2126
|
extractInheritance(node, classId) {
|
|
2127
|
+
// Objective-C @interface MyClass : NSObject <ProtoA, ProtoB>
|
|
2128
|
+
if (node.type === 'class_interface') {
|
|
2129
|
+
const superclass = (0, tree_sitter_helpers_1.getChildByField)(node, 'superclass');
|
|
2130
|
+
if (superclass) {
|
|
2131
|
+
const name = (0, tree_sitter_helpers_1.getNodeText)(superclass, this.source);
|
|
2132
|
+
this.unresolvedReferences.push({
|
|
2133
|
+
fromNodeId: classId,
|
|
2134
|
+
referenceName: name,
|
|
2135
|
+
referenceKind: 'extends',
|
|
2136
|
+
line: superclass.startPosition.row + 1,
|
|
2137
|
+
column: superclass.startPosition.column,
|
|
2138
|
+
});
|
|
2139
|
+
}
|
|
2140
|
+
for (let j = 0; j < node.namedChildCount; j++) {
|
|
2141
|
+
const argList = node.namedChild(j);
|
|
2142
|
+
if (argList?.type !== 'parameterized_arguments')
|
|
2143
|
+
continue;
|
|
2144
|
+
for (let k = 0; k < argList.namedChildCount; k++) {
|
|
2145
|
+
const typeName = argList.namedChild(k);
|
|
2146
|
+
if (!typeName)
|
|
2147
|
+
continue;
|
|
2148
|
+
const typeId = typeName.namedChildren.find((c) => c.type === 'type_identifier' || c.type === 'identifier');
|
|
2149
|
+
if (!typeId)
|
|
2150
|
+
continue;
|
|
2151
|
+
const protocolName = (0, tree_sitter_helpers_1.getNodeText)(typeId, this.source);
|
|
2152
|
+
this.unresolvedReferences.push({
|
|
2153
|
+
fromNodeId: classId,
|
|
2154
|
+
referenceName: protocolName,
|
|
2155
|
+
referenceKind: 'implements',
|
|
2156
|
+
line: typeId.startPosition.row + 1,
|
|
2157
|
+
column: typeId.startPosition.column,
|
|
2158
|
+
});
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
return;
|
|
2162
|
+
}
|
|
1611
2163
|
// Look for extends/implements clauses
|
|
1612
2164
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
1613
2165
|
const child = node.namedChild(i);
|
|
@@ -1635,6 +2187,24 @@ class TreeSitterExtractor {
|
|
|
1635
2187
|
}
|
|
1636
2188
|
}
|
|
1637
2189
|
}
|
|
2190
|
+
// C++ base classes: `class Derived : public Base, private Other` →
|
|
2191
|
+
// base_class_clause holds access specifiers + base type(s). Emit an extends
|
|
2192
|
+
// ref per base type (skip the public/private/protected keywords).
|
|
2193
|
+
if (child.type === 'base_class_clause') {
|
|
2194
|
+
for (const t of child.namedChildren) {
|
|
2195
|
+
if (t.type === 'type_identifier' ||
|
|
2196
|
+
t.type === 'qualified_identifier' ||
|
|
2197
|
+
t.type === 'template_type') {
|
|
2198
|
+
this.unresolvedReferences.push({
|
|
2199
|
+
fromNodeId: classId,
|
|
2200
|
+
referenceName: (0, tree_sitter_helpers_1.getNodeText)(t, this.source),
|
|
2201
|
+
referenceKind: 'extends',
|
|
2202
|
+
line: t.startPosition.row + 1,
|
|
2203
|
+
column: t.startPosition.column,
|
|
2204
|
+
});
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
1638
2208
|
if (child.type === 'implements_clause' ||
|
|
1639
2209
|
child.type === 'class_interface_clause' ||
|
|
1640
2210
|
child.type === 'super_interfaces' || // Java class implements
|
|
@@ -1905,6 +2475,16 @@ class TreeSitterExtractor {
|
|
|
1905
2475
|
return;
|
|
1906
2476
|
if (!this.TYPE_ANNOTATION_LANGUAGES.has(this.language))
|
|
1907
2477
|
return;
|
|
2478
|
+
// C# tree-sitter doesn't produce `type_identifier` leaves — it uses
|
|
2479
|
+
// `identifier`, `predefined_type`, `qualified_name`, `generic_name`,
|
|
2480
|
+
// etc. — so the generic walker below emits zero references for it.
|
|
2481
|
+
// Dispatch to a C#-aware path that only walks type-position subtrees
|
|
2482
|
+
// (the `type` field of a parameter/method/property/field), so
|
|
2483
|
+
// parameter NAMES never accidentally surface as type refs (#381).
|
|
2484
|
+
if (this.language === 'csharp') {
|
|
2485
|
+
this.extractCsharpTypeRefs(node, nodeId);
|
|
2486
|
+
return;
|
|
2487
|
+
}
|
|
1908
2488
|
// Extract parameter type annotations
|
|
1909
2489
|
const params = (0, tree_sitter_helpers_1.getChildByField)(node, this.extractor.paramsField || 'parameters');
|
|
1910
2490
|
if (params) {
|
|
@@ -1921,6 +2501,112 @@ class TreeSitterExtractor {
|
|
|
1921
2501
|
this.extractTypeRefsFromSubtree(typeAnnotation, nodeId);
|
|
1922
2502
|
}
|
|
1923
2503
|
}
|
|
2504
|
+
/**
|
|
2505
|
+
* Extract C# type references from a node that owns a type position —
|
|
2506
|
+
* a method/constructor declaration, a property declaration, or a
|
|
2507
|
+
* field declaration (which wraps `variable_declaration → type`).
|
|
2508
|
+
*
|
|
2509
|
+
* Walks ONLY into known type fields, so parameter names like
|
|
2510
|
+
* `request` in `Build(UserDto request)` are never mis-emitted as
|
|
2511
|
+
* type references. Once inside a type subtree, `walkCsharpTypePosition`
|
|
2512
|
+
* recognizes C#'s actual type-leaf node kinds (`identifier`,
|
|
2513
|
+
* `qualified_name`, `generic_name`, `array_type`, `nullable_type`,
|
|
2514
|
+
* `tuple_type`, …) — none of which are `type_identifier`. Closes #381.
|
|
2515
|
+
*/
|
|
2516
|
+
extractCsharpTypeRefs(node, nodeId) {
|
|
2517
|
+
// Return type / property type — the field is named `type`.
|
|
2518
|
+
const directType = (0, tree_sitter_helpers_1.getChildByField)(node, 'type');
|
|
2519
|
+
if (directType)
|
|
2520
|
+
this.walkCsharpTypePosition(directType, nodeId);
|
|
2521
|
+
// Field declarations wrap declarators in a `variable_declaration`
|
|
2522
|
+
// whose `type` field carries the type. The outer `field_declaration`
|
|
2523
|
+
// has no `type` field of its own, so the call above is a no-op here
|
|
2524
|
+
// and we descend one level.
|
|
2525
|
+
const varDecl = node.namedChildren.find((c) => c.type === 'variable_declaration');
|
|
2526
|
+
if (varDecl) {
|
|
2527
|
+
const vdType = (0, tree_sitter_helpers_1.getChildByField)(varDecl, 'type');
|
|
2528
|
+
if (vdType)
|
|
2529
|
+
this.walkCsharpTypePosition(vdType, nodeId);
|
|
2530
|
+
}
|
|
2531
|
+
// Method / constructor parameters. The field name on
|
|
2532
|
+
// `method_declaration` is `parameters`; it points at a
|
|
2533
|
+
// `parameter_list` whose `parameter` children each have their own
|
|
2534
|
+
// `type` field. Walking ONLY the type field skips parameter NAMES,
|
|
2535
|
+
// which would otherwise mis-emit as type references.
|
|
2536
|
+
const params = (0, tree_sitter_helpers_1.getChildByField)(node, 'parameters');
|
|
2537
|
+
if (params) {
|
|
2538
|
+
for (let i = 0; i < params.namedChildCount; i++) {
|
|
2539
|
+
const child = params.namedChild(i);
|
|
2540
|
+
if (!child || child.type !== 'parameter')
|
|
2541
|
+
continue;
|
|
2542
|
+
const paramType = (0, tree_sitter_helpers_1.getChildByField)(child, 'type');
|
|
2543
|
+
if (paramType)
|
|
2544
|
+
this.walkCsharpTypePosition(paramType, nodeId);
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
}
|
|
2548
|
+
/**
|
|
2549
|
+
* Walk a C# subtree that is KNOWN to be in a type position
|
|
2550
|
+
* (return type, parameter type, property type, field type, generic
|
|
2551
|
+
* argument). Identifiers here are type names, not parameter names.
|
|
2552
|
+
*/
|
|
2553
|
+
walkCsharpTypePosition(node, fromNodeId) {
|
|
2554
|
+
// `predefined_type` is int/string/bool/etc. — never a project ref.
|
|
2555
|
+
if (node.type === 'predefined_type')
|
|
2556
|
+
return;
|
|
2557
|
+
// Bare type name: `Foo` in `Foo bar`, or the `Foo` inside `List<Foo>`.
|
|
2558
|
+
if (node.type === 'identifier') {
|
|
2559
|
+
const name = (0, tree_sitter_helpers_1.getNodeText)(node, this.source);
|
|
2560
|
+
if (name && !this.BUILTIN_TYPES.has(name)) {
|
|
2561
|
+
this.unresolvedReferences.push({
|
|
2562
|
+
fromNodeId,
|
|
2563
|
+
referenceName: name,
|
|
2564
|
+
referenceKind: 'references',
|
|
2565
|
+
line: node.startPosition.row + 1,
|
|
2566
|
+
column: node.startPosition.column,
|
|
2567
|
+
});
|
|
2568
|
+
}
|
|
2569
|
+
return;
|
|
2570
|
+
}
|
|
2571
|
+
// `Namespace.Foo` → the rightmost identifier is the type. Emit the
|
|
2572
|
+
// full qualified name as the reference; the resolver can still match
|
|
2573
|
+
// on the trailing simple name when needed.
|
|
2574
|
+
if (node.type === 'qualified_name') {
|
|
2575
|
+
const text = (0, tree_sitter_helpers_1.getNodeText)(node, this.source);
|
|
2576
|
+
const last = text.split('.').pop() ?? text;
|
|
2577
|
+
if (last && !this.BUILTIN_TYPES.has(last)) {
|
|
2578
|
+
this.unresolvedReferences.push({
|
|
2579
|
+
fromNodeId,
|
|
2580
|
+
referenceName: last,
|
|
2581
|
+
referenceKind: 'references',
|
|
2582
|
+
line: node.startPosition.row + 1,
|
|
2583
|
+
column: node.startPosition.column,
|
|
2584
|
+
});
|
|
2585
|
+
}
|
|
2586
|
+
return;
|
|
2587
|
+
}
|
|
2588
|
+
// `(int Code, Foo Payload)` — tuple element has BOTH a `type` and a
|
|
2589
|
+
// `name` field; descending into all named children would mis-emit
|
|
2590
|
+
// the element name (`Code`, `Payload`) as a type ref. Walk only the
|
|
2591
|
+
// type field.
|
|
2592
|
+
if (node.type === 'tuple_element') {
|
|
2593
|
+
const t = (0, tree_sitter_helpers_1.getChildByField)(node, 'type');
|
|
2594
|
+
if (t)
|
|
2595
|
+
this.walkCsharpTypePosition(t, fromNodeId);
|
|
2596
|
+
return;
|
|
2597
|
+
}
|
|
2598
|
+
// Composite type nodes — recurse into named children. Covers
|
|
2599
|
+
// `generic_name` (head identifier + `type_argument_list`),
|
|
2600
|
+
// `nullable_type`, `array_type`, `pointer_type`, `tuple_type`,
|
|
2601
|
+
// `ref_type`, and any newer wrapping shapes the grammar adds.
|
|
2602
|
+
// Identifiers reached here are all type-positional (parameter/field
|
|
2603
|
+
// names are gated out before we descend).
|
|
2604
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
2605
|
+
const child = node.namedChild(i);
|
|
2606
|
+
if (child)
|
|
2607
|
+
this.walkCsharpTypePosition(child, fromNodeId);
|
|
2608
|
+
}
|
|
2609
|
+
}
|
|
1924
2610
|
/**
|
|
1925
2611
|
* Extract type references from a variable's type annotation.
|
|
1926
2612
|
*/
|
|
@@ -2357,6 +3043,19 @@ function extractFromSource(filePath, source, language, frameworkNames) {
|
|
|
2357
3043
|
const extractor = new liquid_extractor_1.LiquidExtractor(filePath, source);
|
|
2358
3044
|
result = extractor.extract();
|
|
2359
3045
|
}
|
|
3046
|
+
else if (detectedLanguage === 'xml') {
|
|
3047
|
+
// Custom extractor for MyBatis mapper XML. Non-mapper XML returns just a
|
|
3048
|
+
// file node so the watcher tracks it without emitting symbols.
|
|
3049
|
+
const extractor = new mybatis_extractor_1.MyBatisExtractor(filePath, source);
|
|
3050
|
+
result = extractor.extract();
|
|
3051
|
+
}
|
|
3052
|
+
else if ((0, grammars_1.isFileLevelOnlyLanguage)(detectedLanguage)) {
|
|
3053
|
+
// No symbol extraction at this stage — files are tracked at the file-record
|
|
3054
|
+
// level only. Framework extractors (Drupal routing yml, Spring `@Value`
|
|
3055
|
+
// resolution against application.yml/application.properties) run later and
|
|
3056
|
+
// add per-file nodes/references when they apply.
|
|
3057
|
+
result = { nodes: [], edges: [], unresolvedReferences: [], errors: [], durationMs: 0 };
|
|
3058
|
+
}
|
|
2360
3059
|
else if (detectedLanguage === 'pascal' &&
|
|
2361
3060
|
(fileExtension === '.dfm' || fileExtension === '.fmx')) {
|
|
2362
3061
|
// Use custom extractor for DFM/FMX form files
|