circle-ir 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/README.md +200 -0
- package/configs/sinks/code_injection.yaml +672 -0
- package/configs/sinks/command.yaml +917 -0
- package/configs/sinks/deserialization.yaml +105 -0
- package/configs/sinks/ldap.yaml +136 -0
- package/configs/sinks/nodejs.json +629 -0
- package/configs/sinks/path.yaml +715 -0
- package/configs/sinks/python.json +501 -0
- package/configs/sinks/rust.json +339 -0
- package/configs/sinks/sql.yaml +233 -0
- package/configs/sinks/ssrf.yaml +160 -0
- package/configs/sinks/xpath.yaml +121 -0
- package/configs/sinks/xss.yaml +727 -0
- package/configs/sources/db_sources.yaml +90 -0
- package/configs/sources/env_sources.yaml +94 -0
- package/configs/sources/express.json +197 -0
- package/configs/sources/file_sources.yaml +164 -0
- package/configs/sources/http_sources.yaml +379 -0
- package/configs/sources/io_sources.yaml +519 -0
- package/configs/sources/network_sources.yaml +99 -0
- package/configs/sources/python.json +230 -0
- package/configs/sources/rust.json +286 -0
- package/configs/sources/spring.yaml +70 -0
- package/dist/analysis/advisory-db.d.ts +86 -0
- package/dist/analysis/advisory-db.js +104 -0
- package/dist/analysis/advisory-db.js.map +1 -0
- package/dist/analysis/cargo-parser.d.ts +42 -0
- package/dist/analysis/cargo-parser.js +102 -0
- package/dist/analysis/cargo-parser.js.map +1 -0
- package/dist/analysis/config-loader.d.ts +37 -0
- package/dist/analysis/config-loader.js +1561 -0
- package/dist/analysis/config-loader.js.map +1 -0
- package/dist/analysis/constant-propagation/ast-utils.d.ts +25 -0
- package/dist/analysis/constant-propagation/ast-utils.js +34 -0
- package/dist/analysis/constant-propagation/ast-utils.js.map +1 -0
- package/dist/analysis/constant-propagation/evaluator.d.ts +32 -0
- package/dist/analysis/constant-propagation/evaluator.js +296 -0
- package/dist/analysis/constant-propagation/evaluator.js.map +1 -0
- package/dist/analysis/constant-propagation/index.d.ts +62 -0
- package/dist/analysis/constant-propagation/index.js +152 -0
- package/dist/analysis/constant-propagation/index.js.map +1 -0
- package/dist/analysis/constant-propagation/patterns.d.ts +8 -0
- package/dist/analysis/constant-propagation/patterns.js +126 -0
- package/dist/analysis/constant-propagation/patterns.js.map +1 -0
- package/dist/analysis/constant-propagation/propagator.d.ts +180 -0
- package/dist/analysis/constant-propagation/propagator.js +1985 -0
- package/dist/analysis/constant-propagation/propagator.js.map +1 -0
- package/dist/analysis/constant-propagation/types.d.ts +63 -0
- package/dist/analysis/constant-propagation/types.js +5 -0
- package/dist/analysis/constant-propagation/types.js.map +1 -0
- package/dist/analysis/constant-propagation.d.ts +9 -0
- package/dist/analysis/constant-propagation.js +18 -0
- package/dist/analysis/constant-propagation.js.map +1 -0
- package/dist/analysis/dependency-scanner.d.ts +79 -0
- package/dist/analysis/dependency-scanner.js +122 -0
- package/dist/analysis/dependency-scanner.js.map +1 -0
- package/dist/analysis/dfg-verifier.d.ts +116 -0
- package/dist/analysis/dfg-verifier.js +399 -0
- package/dist/analysis/dfg-verifier.js.map +1 -0
- package/dist/analysis/findings.d.ts +11 -0
- package/dist/analysis/findings.js +228 -0
- package/dist/analysis/findings.js.map +1 -0
- package/dist/analysis/index.d.ts +16 -0
- package/dist/analysis/index.js +18 -0
- package/dist/analysis/index.js.map +1 -0
- package/dist/analysis/interprocedural.d.ts +99 -0
- package/dist/analysis/interprocedural.js +526 -0
- package/dist/analysis/interprocedural.js.map +1 -0
- package/dist/analysis/path-finder.d.ts +133 -0
- package/dist/analysis/path-finder.js +354 -0
- package/dist/analysis/path-finder.js.map +1 -0
- package/dist/analysis/rules.d.ts +75 -0
- package/dist/analysis/rules.js +332 -0
- package/dist/analysis/rules.js.map +1 -0
- package/dist/analysis/semver.d.ts +27 -0
- package/dist/analysis/semver.js +127 -0
- package/dist/analysis/semver.js.map +1 -0
- package/dist/analysis/taint-matcher.d.ts +15 -0
- package/dist/analysis/taint-matcher.js +634 -0
- package/dist/analysis/taint-matcher.js.map +1 -0
- package/dist/analysis/taint-propagation.d.ts +67 -0
- package/dist/analysis/taint-propagation.js +298 -0
- package/dist/analysis/taint-propagation.js.map +1 -0
- package/dist/analysis/unresolved.d.ts +14 -0
- package/dist/analysis/unresolved.js +202 -0
- package/dist/analysis/unresolved.js.map +1 -0
- package/dist/analyzer.d.ts +43 -0
- package/dist/analyzer.js +1010 -0
- package/dist/analyzer.js.map +1 -0
- package/dist/browser/circle-ir.js +16576 -0
- package/dist/browser.d.ts +38 -0
- package/dist/browser.js +38 -0
- package/dist/browser.js.map +1 -0
- package/dist/core/circle-ir-core.cjs +13626 -0
- package/dist/core/circle-ir-core.d.ts +59 -0
- package/dist/core/circle-ir-core.js +13591 -0
- package/dist/core/extractors/calls.d.ts +13 -0
- package/dist/core/extractors/calls.js +1429 -0
- package/dist/core/extractors/calls.js.map +1 -0
- package/dist/core/extractors/cfg.d.ts +9 -0
- package/dist/core/extractors/cfg.js +519 -0
- package/dist/core/extractors/cfg.js.map +1 -0
- package/dist/core/extractors/dfg.d.ts +12 -0
- package/dist/core/extractors/dfg.js +1081 -0
- package/dist/core/extractors/dfg.js.map +1 -0
- package/dist/core/extractors/exports.d.ts +14 -0
- package/dist/core/extractors/exports.js +80 -0
- package/dist/core/extractors/exports.js.map +1 -0
- package/dist/core/extractors/imports.d.ts +9 -0
- package/dist/core/extractors/imports.js +739 -0
- package/dist/core/extractors/imports.js.map +1 -0
- package/dist/core/extractors/index.d.ts +10 -0
- package/dist/core/extractors/index.js +11 -0
- package/dist/core/extractors/index.js.map +1 -0
- package/dist/core/extractors/meta.d.ts +10 -0
- package/dist/core/extractors/meta.js +109 -0
- package/dist/core/extractors/meta.js.map +1 -0
- package/dist/core/extractors/types.d.ts +10 -0
- package/dist/core/extractors/types.js +1479 -0
- package/dist/core/extractors/types.js.map +1 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.js +8 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/parser.d.ts +84 -0
- package/dist/core/parser.js +250 -0
- package/dist/core/parser.js.map +1 -0
- package/dist/core-lib.d.ts +59 -0
- package/dist/core-lib.js +62 -0
- package/dist/core-lib.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/languages/index.d.ts +11 -0
- package/dist/languages/index.js +14 -0
- package/dist/languages/index.js.map +1 -0
- package/dist/languages/plugins/base.d.ts +44 -0
- package/dist/languages/plugins/base.js +82 -0
- package/dist/languages/plugins/base.js.map +1 -0
- package/dist/languages/plugins/index.d.ts +14 -0
- package/dist/languages/plugins/index.js +25 -0
- package/dist/languages/plugins/index.js.map +1 -0
- package/dist/languages/plugins/java.d.ts +49 -0
- package/dist/languages/plugins/java.js +402 -0
- package/dist/languages/plugins/java.js.map +1 -0
- package/dist/languages/plugins/javascript.d.ts +48 -0
- package/dist/languages/plugins/javascript.js +445 -0
- package/dist/languages/plugins/javascript.js.map +1 -0
- package/dist/languages/plugins/python.d.ts +47 -0
- package/dist/languages/plugins/python.js +480 -0
- package/dist/languages/plugins/python.js.map +1 -0
- package/dist/languages/plugins/rust.d.ts +47 -0
- package/dist/languages/plugins/rust.js +405 -0
- package/dist/languages/plugins/rust.js.map +1 -0
- package/dist/languages/registry.d.ts +30 -0
- package/dist/languages/registry.js +80 -0
- package/dist/languages/registry.js.map +1 -0
- package/dist/languages/types.d.ts +184 -0
- package/dist/languages/types.js +8 -0
- package/dist/languages/types.js.map +1 -0
- package/dist/resolution/cross-file.d.ts +146 -0
- package/dist/resolution/cross-file.js +439 -0
- package/dist/resolution/cross-file.js.map +1 -0
- package/dist/resolution/index.d.ts +12 -0
- package/dist/resolution/index.js +10 -0
- package/dist/resolution/index.js.map +1 -0
- package/dist/resolution/symbol-table.d.ts +136 -0
- package/dist/resolution/symbol-table.js +336 -0
- package/dist/resolution/symbol-table.js.map +1 -0
- package/dist/resolution/type-hierarchy.d.ts +124 -0
- package/dist/resolution/type-hierarchy.js +515 -0
- package/dist/resolution/type-hierarchy.js.map +1 -0
- package/dist/types/config.d.ts +45 -0
- package/dist/types/config.js +5 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/index.d.ts +392 -0
- package/dist/types/index.js +7 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/logger.d.ts +85 -0
- package/dist/utils/logger.js +198 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/wasm/tree-sitter-java.wasm +0 -0
- package/dist/wasm/tree-sitter-javascript.wasm +0 -0
- package/dist/wasm/tree-sitter-python.wasm +0 -0
- package/dist/wasm/tree-sitter-rust.wasm +0 -0
- package/dist/wasm/web-tree-sitter.wasm +0 -0
- package/docs/SPEC.md +1021 -0
- package/examples/browser-example.html +610 -0
- package/examples/node-example.ts +215 -0
- package/package.json +107 -0
- package/wasm/tree-sitter-java.wasm +0 -0
- package/wasm/tree-sitter-javascript.wasm +0 -0
- package/wasm/tree-sitter-python.wasm +0 -0
- package/wasm/tree-sitter-rust.wasm +0 -0
|
@@ -0,0 +1,1479 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type extractor - extracts classes, interfaces, enums, methods, and fields
|
|
3
|
+
*/
|
|
4
|
+
import { findNodes, getNodeText, getNodesFromCache } from '../parser.js';
|
|
5
|
+
/**
|
|
6
|
+
* Detect language from tree structure.
|
|
7
|
+
*/
|
|
8
|
+
function detectLanguage(tree) {
|
|
9
|
+
const root = tree.rootNode;
|
|
10
|
+
const jsNodeTypes = new Set([
|
|
11
|
+
'arrow_function', 'lexical_declaration', 'function_declaration',
|
|
12
|
+
'export_statement', 'import_statement'
|
|
13
|
+
]);
|
|
14
|
+
const javaNodeTypes = new Set([
|
|
15
|
+
'package_declaration', 'import_declaration', 'method_declaration',
|
|
16
|
+
'annotation'
|
|
17
|
+
]);
|
|
18
|
+
const pythonNodeTypes = new Set([
|
|
19
|
+
'class_definition', 'function_definition', 'decorated_definition',
|
|
20
|
+
'import_from_statement', 'import_statement'
|
|
21
|
+
]);
|
|
22
|
+
const rustNodeTypes = new Set([
|
|
23
|
+
'struct_item', 'impl_item', 'function_item', 'use_declaration',
|
|
24
|
+
'mod_item', 'trait_item', 'enum_item', 'macro_invocation'
|
|
25
|
+
]);
|
|
26
|
+
let jsScore = 0;
|
|
27
|
+
let javaScore = 0;
|
|
28
|
+
let pythonScore = 0;
|
|
29
|
+
let rustScore = 0;
|
|
30
|
+
for (let i = 0; i < Math.min(root.childCount, 20); i++) {
|
|
31
|
+
const child = root.child(i);
|
|
32
|
+
if (!child)
|
|
33
|
+
continue;
|
|
34
|
+
if (jsNodeTypes.has(child.type))
|
|
35
|
+
jsScore++;
|
|
36
|
+
if (javaNodeTypes.has(child.type))
|
|
37
|
+
javaScore++;
|
|
38
|
+
if (pythonNodeTypes.has(child.type))
|
|
39
|
+
pythonScore++;
|
|
40
|
+
if (rustNodeTypes.has(child.type))
|
|
41
|
+
rustScore++;
|
|
42
|
+
}
|
|
43
|
+
if (rustScore > jsScore && rustScore > javaScore && rustScore > pythonScore)
|
|
44
|
+
return 'rust';
|
|
45
|
+
if (pythonScore > jsScore && pythonScore > javaScore)
|
|
46
|
+
return 'python';
|
|
47
|
+
return jsScore > javaScore ? 'javascript' : 'java';
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Extract all type definitions from the tree.
|
|
51
|
+
*/
|
|
52
|
+
export function extractTypes(tree, cache, language) {
|
|
53
|
+
const effectiveLanguage = language ?? detectLanguage(tree);
|
|
54
|
+
const isJavaScript = effectiveLanguage === 'javascript' || effectiveLanguage === 'typescript';
|
|
55
|
+
const isPython = effectiveLanguage === 'python';
|
|
56
|
+
const isRust = effectiveLanguage === 'rust';
|
|
57
|
+
if (isRust) {
|
|
58
|
+
return extractRustTypes(tree, cache);
|
|
59
|
+
}
|
|
60
|
+
if (isPython) {
|
|
61
|
+
return extractPythonTypes(tree, cache);
|
|
62
|
+
}
|
|
63
|
+
if (isJavaScript) {
|
|
64
|
+
return extractJavaScriptTypes(tree, cache);
|
|
65
|
+
}
|
|
66
|
+
return extractJavaTypes(tree, cache);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Extract Java types.
|
|
70
|
+
*/
|
|
71
|
+
function extractJavaTypes(tree, cache) {
|
|
72
|
+
const types = [];
|
|
73
|
+
// Extract classes
|
|
74
|
+
const classes = getNodesFromCache(tree.rootNode, 'class_declaration', cache);
|
|
75
|
+
for (const cls of classes) {
|
|
76
|
+
types.push(extractClassInfo(cls));
|
|
77
|
+
}
|
|
78
|
+
// Extract interfaces
|
|
79
|
+
const interfaces = getNodesFromCache(tree.rootNode, 'interface_declaration', cache);
|
|
80
|
+
for (const iface of interfaces) {
|
|
81
|
+
types.push(extractInterfaceInfo(iface));
|
|
82
|
+
}
|
|
83
|
+
// Extract enums
|
|
84
|
+
const enums = getNodesFromCache(tree.rootNode, 'enum_declaration', cache);
|
|
85
|
+
for (const enumDecl of enums) {
|
|
86
|
+
types.push(extractEnumInfo(enumDecl));
|
|
87
|
+
}
|
|
88
|
+
return types;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Extract JavaScript/TypeScript types.
|
|
92
|
+
*/
|
|
93
|
+
function extractJavaScriptTypes(tree, cache) {
|
|
94
|
+
const types = [];
|
|
95
|
+
// Extract classes
|
|
96
|
+
const classes = getNodesFromCache(tree.rootNode, 'class_declaration', cache);
|
|
97
|
+
for (const cls of classes) {
|
|
98
|
+
types.push(extractJSClassInfo(cls));
|
|
99
|
+
}
|
|
100
|
+
// Extract standalone functions as a module-like type
|
|
101
|
+
const functions = getNodesFromCache(tree.rootNode, 'function_declaration', cache);
|
|
102
|
+
if (functions.length > 0) {
|
|
103
|
+
const moduleFunctions = [];
|
|
104
|
+
for (const func of functions) {
|
|
105
|
+
moduleFunctions.push(extractJSFunctionInfo(func));
|
|
106
|
+
}
|
|
107
|
+
// Create a synthetic module type for standalone functions
|
|
108
|
+
if (moduleFunctions.length > 0) {
|
|
109
|
+
types.push({
|
|
110
|
+
name: '<module>',
|
|
111
|
+
kind: 'class',
|
|
112
|
+
package: null,
|
|
113
|
+
extends: null,
|
|
114
|
+
implements: [],
|
|
115
|
+
annotations: [],
|
|
116
|
+
methods: moduleFunctions,
|
|
117
|
+
fields: [],
|
|
118
|
+
start_line: 1,
|
|
119
|
+
end_line: tree.rootNode.endPosition.row + 1,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Extract arrow functions assigned to const/let
|
|
124
|
+
const arrowFuncs = extractNamedArrowFunctions(tree, cache);
|
|
125
|
+
if (arrowFuncs.length > 0) {
|
|
126
|
+
// Add to existing module or create one
|
|
127
|
+
const moduleType = types.find(t => t.name === '<module>');
|
|
128
|
+
if (moduleType) {
|
|
129
|
+
moduleType.methods.push(...arrowFuncs);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
types.push({
|
|
133
|
+
name: '<module>',
|
|
134
|
+
kind: 'class',
|
|
135
|
+
package: null,
|
|
136
|
+
extends: null,
|
|
137
|
+
implements: [],
|
|
138
|
+
annotations: [],
|
|
139
|
+
methods: arrowFuncs,
|
|
140
|
+
fields: [],
|
|
141
|
+
start_line: 1,
|
|
142
|
+
end_line: tree.rootNode.endPosition.row + 1,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return types;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Extract Python types.
|
|
150
|
+
*/
|
|
151
|
+
function extractPythonTypes(tree, cache) {
|
|
152
|
+
const types = [];
|
|
153
|
+
// Extract classes (including decorated classes)
|
|
154
|
+
const classes = getNodesFromCache(tree.rootNode, 'class_definition', cache);
|
|
155
|
+
for (const cls of classes) {
|
|
156
|
+
types.push(extractPythonClassInfo(cls));
|
|
157
|
+
}
|
|
158
|
+
// Extract standalone functions as a module-like type
|
|
159
|
+
const functions = getNodesFromCache(tree.rootNode, 'function_definition', cache);
|
|
160
|
+
const topLevelFunctions = [];
|
|
161
|
+
for (const func of functions) {
|
|
162
|
+
// Only include top-level functions (not methods inside classes)
|
|
163
|
+
if (func.parent?.type === 'module' || func.parent?.type === 'decorated_definition') {
|
|
164
|
+
// Check if this is inside a class (nested in decorated_definition that's in a class)
|
|
165
|
+
let parent = func.parent;
|
|
166
|
+
let isInsideClass = false;
|
|
167
|
+
while (parent) {
|
|
168
|
+
if (parent.type === 'class_definition') {
|
|
169
|
+
isInsideClass = true;
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
if (parent.type === 'module')
|
|
173
|
+
break;
|
|
174
|
+
parent = parent.parent;
|
|
175
|
+
}
|
|
176
|
+
if (!isInsideClass) {
|
|
177
|
+
topLevelFunctions.push(extractPythonFunctionInfo(func));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// Create a synthetic module type for standalone functions
|
|
182
|
+
if (topLevelFunctions.length > 0 || types.length === 0) {
|
|
183
|
+
const moduleType = types.find(t => t.name === '<module>');
|
|
184
|
+
if (moduleType) {
|
|
185
|
+
moduleType.methods.push(...topLevelFunctions);
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
types.push({
|
|
189
|
+
name: '<module>',
|
|
190
|
+
kind: 'class',
|
|
191
|
+
package: null,
|
|
192
|
+
extends: null,
|
|
193
|
+
implements: [],
|
|
194
|
+
annotations: [],
|
|
195
|
+
methods: topLevelFunctions,
|
|
196
|
+
fields: [],
|
|
197
|
+
start_line: 1,
|
|
198
|
+
end_line: tree.rootNode.endPosition.row + 1,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return types;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Extract Python class information.
|
|
206
|
+
*/
|
|
207
|
+
function extractPythonClassInfo(node) {
|
|
208
|
+
const nameNode = node.childForFieldName('name');
|
|
209
|
+
const name = nameNode ? getNodeText(nameNode) : 'Anonymous';
|
|
210
|
+
// Extract base classes (Python supports multiple inheritance)
|
|
211
|
+
let extendsType = null;
|
|
212
|
+
const implementsList = [];
|
|
213
|
+
const superclassNode = node.childForFieldName('superclasses');
|
|
214
|
+
if (superclassNode) {
|
|
215
|
+
const baseClasses = extractPythonBaseClasses(superclassNode);
|
|
216
|
+
if (baseClasses.length > 0) {
|
|
217
|
+
extendsType = baseClasses[0];
|
|
218
|
+
implementsList.push(...baseClasses.slice(1));
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// Extract decorators as annotations
|
|
222
|
+
const annotations = extractPythonDecorators(node);
|
|
223
|
+
// Extract body
|
|
224
|
+
const body = node.childForFieldName('body');
|
|
225
|
+
const methods = body ? extractPythonMethods(body) : [];
|
|
226
|
+
const fields = body ? extractPythonFields(body, methods) : [];
|
|
227
|
+
return {
|
|
228
|
+
name,
|
|
229
|
+
kind: 'class',
|
|
230
|
+
package: null, // Python uses module paths, not package declarations
|
|
231
|
+
extends: extendsType,
|
|
232
|
+
implements: implementsList,
|
|
233
|
+
annotations,
|
|
234
|
+
methods,
|
|
235
|
+
fields,
|
|
236
|
+
start_line: node.startPosition.row + 1,
|
|
237
|
+
end_line: node.endPosition.row + 1,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Extract base classes from Python superclasses node.
|
|
242
|
+
*/
|
|
243
|
+
function extractPythonBaseClasses(node) {
|
|
244
|
+
const bases = [];
|
|
245
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
246
|
+
const child = node.child(i);
|
|
247
|
+
if (!child)
|
|
248
|
+
continue;
|
|
249
|
+
// Skip punctuation
|
|
250
|
+
if (child.type === ',' || child.type === '(' || child.type === ')')
|
|
251
|
+
continue;
|
|
252
|
+
if (child.type === 'identifier' || child.type === 'attribute') {
|
|
253
|
+
bases.push(getNodeText(child));
|
|
254
|
+
}
|
|
255
|
+
else if (child.type === 'argument_list') {
|
|
256
|
+
// For class Foo(Base1, Base2):
|
|
257
|
+
for (let j = 0; j < child.childCount; j++) {
|
|
258
|
+
const arg = child.child(j);
|
|
259
|
+
if (arg && (arg.type === 'identifier' || arg.type === 'attribute')) {
|
|
260
|
+
bases.push(getNodeText(arg));
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return bases;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Extract decorators from a Python class or function.
|
|
269
|
+
*/
|
|
270
|
+
function extractPythonDecorators(node) {
|
|
271
|
+
const decorators = [];
|
|
272
|
+
// Check if this node is wrapped in a decorated_definition
|
|
273
|
+
const parent = node.parent;
|
|
274
|
+
if (parent?.type === 'decorated_definition') {
|
|
275
|
+
for (let i = 0; i < parent.childCount; i++) {
|
|
276
|
+
const child = parent.child(i);
|
|
277
|
+
if (child?.type === 'decorator') {
|
|
278
|
+
let text = getNodeText(child);
|
|
279
|
+
// Remove the @ prefix
|
|
280
|
+
if (text.startsWith('@')) {
|
|
281
|
+
text = text.substring(1);
|
|
282
|
+
}
|
|
283
|
+
decorators.push(text);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return decorators;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Extract methods from a Python class body.
|
|
291
|
+
*/
|
|
292
|
+
function extractPythonMethods(body) {
|
|
293
|
+
const methods = [];
|
|
294
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
295
|
+
const child = body.child(i);
|
|
296
|
+
if (!child)
|
|
297
|
+
continue;
|
|
298
|
+
if (child.type === 'function_definition') {
|
|
299
|
+
methods.push(extractPythonMethodInfo(child));
|
|
300
|
+
}
|
|
301
|
+
else if (child.type === 'decorated_definition') {
|
|
302
|
+
// Find the function inside the decorated definition
|
|
303
|
+
for (let j = 0; j < child.childCount; j++) {
|
|
304
|
+
const inner = child.child(j);
|
|
305
|
+
if (inner?.type === 'function_definition') {
|
|
306
|
+
methods.push(extractPythonMethodInfo(inner));
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return methods;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Extract Python method information.
|
|
316
|
+
*/
|
|
317
|
+
function extractPythonMethodInfo(node) {
|
|
318
|
+
const nameNode = node.childForFieldName('name');
|
|
319
|
+
const name = nameNode ? getNodeText(nameNode) : 'anonymous';
|
|
320
|
+
// Extract parameters
|
|
321
|
+
const params = node.childForFieldName('parameters');
|
|
322
|
+
const parameters = params ? extractPythonParameters(params) : [];
|
|
323
|
+
// Extract return type annotation
|
|
324
|
+
const returnTypeNode = node.childForFieldName('return_type');
|
|
325
|
+
const returnType = returnTypeNode ? getNodeText(returnTypeNode) : null;
|
|
326
|
+
// Extract decorators as annotations
|
|
327
|
+
const annotations = extractPythonDecorators(node);
|
|
328
|
+
// Determine modifiers from decorators and method name
|
|
329
|
+
const modifiers = [];
|
|
330
|
+
if (annotations.includes('staticmethod'))
|
|
331
|
+
modifiers.push('static');
|
|
332
|
+
if (annotations.includes('classmethod'))
|
|
333
|
+
modifiers.push('classmethod');
|
|
334
|
+
if (annotations.includes('property'))
|
|
335
|
+
modifiers.push('property');
|
|
336
|
+
// Check for async
|
|
337
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
338
|
+
const child = node.child(i);
|
|
339
|
+
if (child?.type === 'async') {
|
|
340
|
+
modifiers.push('async');
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return {
|
|
345
|
+
name,
|
|
346
|
+
return_type: returnType,
|
|
347
|
+
parameters,
|
|
348
|
+
annotations,
|
|
349
|
+
modifiers,
|
|
350
|
+
start_line: node.startPosition.row + 1,
|
|
351
|
+
end_line: node.endPosition.row + 1,
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Extract Python function information (standalone function).
|
|
356
|
+
*/
|
|
357
|
+
function extractPythonFunctionInfo(node) {
|
|
358
|
+
// Same as method info extraction
|
|
359
|
+
return extractPythonMethodInfo(node);
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Extract Python parameters.
|
|
363
|
+
*/
|
|
364
|
+
function extractPythonParameters(params) {
|
|
365
|
+
const parameters = [];
|
|
366
|
+
for (let i = 0; i < params.childCount; i++) {
|
|
367
|
+
const child = params.child(i);
|
|
368
|
+
if (!child)
|
|
369
|
+
continue;
|
|
370
|
+
// Skip punctuation
|
|
371
|
+
if (child.type === ',' || child.type === '(' || child.type === ')' || child.type === ':')
|
|
372
|
+
continue;
|
|
373
|
+
if (child.type === 'identifier') {
|
|
374
|
+
parameters.push({
|
|
375
|
+
name: getNodeText(child),
|
|
376
|
+
type: null,
|
|
377
|
+
annotations: [],
|
|
378
|
+
line: child.startPosition.row + 1,
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
else if (child.type === 'typed_parameter') {
|
|
382
|
+
const nameNode = child.namedChild(0);
|
|
383
|
+
const typeNode = child.childForFieldName('type');
|
|
384
|
+
parameters.push({
|
|
385
|
+
name: nameNode ? getNodeText(nameNode) : 'arg',
|
|
386
|
+
type: typeNode ? getNodeText(typeNode) : null,
|
|
387
|
+
annotations: [],
|
|
388
|
+
line: child.startPosition.row + 1,
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
else if (child.type === 'default_parameter' || child.type === 'typed_default_parameter') {
|
|
392
|
+
const nameNode = child.childForFieldName('name');
|
|
393
|
+
const typeNode = child.childForFieldName('type');
|
|
394
|
+
parameters.push({
|
|
395
|
+
name: nameNode ? getNodeText(nameNode) : 'arg',
|
|
396
|
+
type: typeNode ? getNodeText(typeNode) : null,
|
|
397
|
+
annotations: [],
|
|
398
|
+
line: child.startPosition.row + 1,
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
else if (child.type === 'list_splat_pattern' || child.type === 'dictionary_splat_pattern') {
|
|
402
|
+
// *args or **kwargs
|
|
403
|
+
const nameNode = child.namedChild(0);
|
|
404
|
+
const prefix = child.type === 'dictionary_splat_pattern' ? '**' : '*';
|
|
405
|
+
parameters.push({
|
|
406
|
+
name: prefix + (nameNode ? getNodeText(nameNode) : 'args'),
|
|
407
|
+
type: null,
|
|
408
|
+
annotations: [],
|
|
409
|
+
line: child.startPosition.row + 1,
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return parameters;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Extract Python class fields from assignments in the class body.
|
|
417
|
+
*/
|
|
418
|
+
function extractPythonFields(body, methods) {
|
|
419
|
+
const fields = [];
|
|
420
|
+
const fieldNames = new Set();
|
|
421
|
+
// Look for class-level assignments
|
|
422
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
423
|
+
const child = body.child(i);
|
|
424
|
+
if (!child)
|
|
425
|
+
continue;
|
|
426
|
+
if (child.type === 'expression_statement') {
|
|
427
|
+
const expr = child.namedChild(0);
|
|
428
|
+
if (expr?.type === 'assignment') {
|
|
429
|
+
const left = expr.childForFieldName('left');
|
|
430
|
+
if (left?.type === 'identifier') {
|
|
431
|
+
const name = getNodeText(left);
|
|
432
|
+
if (!fieldNames.has(name)) {
|
|
433
|
+
fieldNames.add(name);
|
|
434
|
+
fields.push({
|
|
435
|
+
name,
|
|
436
|
+
type: null,
|
|
437
|
+
modifiers: [],
|
|
438
|
+
annotations: [],
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
// Also extract self.field assignments from __init__ method
|
|
446
|
+
const initMethod = methods.find(m => m.name === '__init__');
|
|
447
|
+
if (initMethod) {
|
|
448
|
+
// We need to look at the actual __init__ body for self.x = ... assignments
|
|
449
|
+
// This requires re-finding the __init__ method node
|
|
450
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
451
|
+
const child = body.child(i);
|
|
452
|
+
if (!child)
|
|
453
|
+
continue;
|
|
454
|
+
let funcNode = null;
|
|
455
|
+
if (child.type === 'function_definition') {
|
|
456
|
+
funcNode = child;
|
|
457
|
+
}
|
|
458
|
+
else if (child.type === 'decorated_definition') {
|
|
459
|
+
for (let j = 0; j < child.childCount; j++) {
|
|
460
|
+
const inner = child.child(j);
|
|
461
|
+
if (inner?.type === 'function_definition') {
|
|
462
|
+
funcNode = inner;
|
|
463
|
+
break;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
if (funcNode) {
|
|
468
|
+
const nameNode = funcNode.childForFieldName('name');
|
|
469
|
+
if (nameNode && getNodeText(nameNode) === '__init__') {
|
|
470
|
+
const funcBody = funcNode.childForFieldName('body');
|
|
471
|
+
if (funcBody) {
|
|
472
|
+
extractSelfAssignments(funcBody, fields, fieldNames);
|
|
473
|
+
}
|
|
474
|
+
break;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
return fields;
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Extract self.field assignments from a function body.
|
|
483
|
+
*/
|
|
484
|
+
function extractSelfAssignments(body, fields, fieldNames) {
|
|
485
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
486
|
+
const child = body.child(i);
|
|
487
|
+
if (!child)
|
|
488
|
+
continue;
|
|
489
|
+
if (child.type === 'expression_statement') {
|
|
490
|
+
const expr = child.namedChild(0);
|
|
491
|
+
if (expr?.type === 'assignment') {
|
|
492
|
+
const left = expr.childForFieldName('left');
|
|
493
|
+
if (left?.type === 'attribute') {
|
|
494
|
+
const obj = left.childForFieldName('object');
|
|
495
|
+
const attr = left.childForFieldName('attribute');
|
|
496
|
+
if (obj && getNodeText(obj) === 'self' && attr) {
|
|
497
|
+
const name = getNodeText(attr);
|
|
498
|
+
if (!fieldNames.has(name)) {
|
|
499
|
+
fieldNames.add(name);
|
|
500
|
+
fields.push({
|
|
501
|
+
name,
|
|
502
|
+
type: null,
|
|
503
|
+
modifiers: [],
|
|
504
|
+
annotations: [],
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Extract JavaScript class information.
|
|
515
|
+
*/
|
|
516
|
+
function extractJSClassInfo(node) {
|
|
517
|
+
const name = getIdentifier(node, 'name') ?? 'Anonymous';
|
|
518
|
+
// Extract superclass (extends)
|
|
519
|
+
let extendsType = null;
|
|
520
|
+
// Try to find class_heritage or heritage node
|
|
521
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
522
|
+
const child = node.child(i);
|
|
523
|
+
if (!child)
|
|
524
|
+
continue;
|
|
525
|
+
// In tree-sitter-javascript, extends is a class_heritage node
|
|
526
|
+
if (child.type === 'class_heritage') {
|
|
527
|
+
// Find the identifier inside class_heritage
|
|
528
|
+
for (let j = 0; j < child.childCount; j++) {
|
|
529
|
+
const grandChild = child.child(j);
|
|
530
|
+
if (grandChild && grandChild.type === 'identifier') {
|
|
531
|
+
extendsType = getNodeText(grandChild);
|
|
532
|
+
break;
|
|
533
|
+
}
|
|
534
|
+
// Also check for member_expression (e.g., Module.Class)
|
|
535
|
+
if (grandChild && grandChild.type === 'member_expression') {
|
|
536
|
+
extendsType = getNodeText(grandChild);
|
|
537
|
+
break;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
break;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
// Extract body
|
|
544
|
+
const body = node.childForFieldName('body');
|
|
545
|
+
const methods = body ? extractJSMethods(body) : [];
|
|
546
|
+
const fields = body ? extractJSFields(body) : [];
|
|
547
|
+
return {
|
|
548
|
+
name,
|
|
549
|
+
kind: 'class',
|
|
550
|
+
package: null,
|
|
551
|
+
extends: extendsType,
|
|
552
|
+
implements: [],
|
|
553
|
+
annotations: [],
|
|
554
|
+
methods,
|
|
555
|
+
fields,
|
|
556
|
+
start_line: node.startPosition.row + 1,
|
|
557
|
+
end_line: node.endPosition.row + 1,
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Extract JavaScript methods from a class body.
|
|
562
|
+
*/
|
|
563
|
+
function extractJSMethods(body) {
|
|
564
|
+
const methods = [];
|
|
565
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
566
|
+
const child = body.child(i);
|
|
567
|
+
if (!child)
|
|
568
|
+
continue;
|
|
569
|
+
if (child.type === 'method_definition') {
|
|
570
|
+
methods.push(extractJSMethodInfo(child));
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
return methods;
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* Extract JavaScript method information.
|
|
577
|
+
*/
|
|
578
|
+
function extractJSMethodInfo(node) {
|
|
579
|
+
const nameNode = node.childForFieldName('name');
|
|
580
|
+
const name = nameNode ? getNodeText(nameNode) : 'anonymous';
|
|
581
|
+
// Extract parameters
|
|
582
|
+
const params = node.childForFieldName('parameters');
|
|
583
|
+
const parameters = params ? extractJSParameters(params) : [];
|
|
584
|
+
// Check if it's a getter/setter
|
|
585
|
+
const modifiers = [];
|
|
586
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
587
|
+
const child = node.child(i);
|
|
588
|
+
if (child) {
|
|
589
|
+
if (child.type === 'get')
|
|
590
|
+
modifiers.push('getter');
|
|
591
|
+
if (child.type === 'set')
|
|
592
|
+
modifiers.push('setter');
|
|
593
|
+
if (child.type === 'static')
|
|
594
|
+
modifiers.push('static');
|
|
595
|
+
if (child.type === 'async')
|
|
596
|
+
modifiers.push('async');
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
return {
|
|
600
|
+
name,
|
|
601
|
+
return_type: null, // JavaScript doesn't have explicit return types
|
|
602
|
+
parameters,
|
|
603
|
+
annotations: [],
|
|
604
|
+
modifiers,
|
|
605
|
+
start_line: node.startPosition.row + 1,
|
|
606
|
+
end_line: node.endPosition.row + 1,
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Extract JavaScript function declaration information.
|
|
611
|
+
*/
|
|
612
|
+
function extractJSFunctionInfo(node) {
|
|
613
|
+
const nameNode = node.childForFieldName('name');
|
|
614
|
+
const name = nameNode ? getNodeText(nameNode) : 'anonymous';
|
|
615
|
+
// Extract parameters
|
|
616
|
+
const params = node.childForFieldName('parameters');
|
|
617
|
+
const parameters = params ? extractJSParameters(params) : [];
|
|
618
|
+
// Check for async/generator
|
|
619
|
+
const modifiers = [];
|
|
620
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
621
|
+
const child = node.child(i);
|
|
622
|
+
if (child) {
|
|
623
|
+
if (child.type === 'async')
|
|
624
|
+
modifiers.push('async');
|
|
625
|
+
if (child.type === '*')
|
|
626
|
+
modifiers.push('generator');
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
return {
|
|
630
|
+
name,
|
|
631
|
+
return_type: null,
|
|
632
|
+
parameters,
|
|
633
|
+
annotations: [],
|
|
634
|
+
modifiers,
|
|
635
|
+
start_line: node.startPosition.row + 1,
|
|
636
|
+
end_line: node.endPosition.row + 1,
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Extract named arrow functions (const foo = () => {})
|
|
641
|
+
*/
|
|
642
|
+
function extractNamedArrowFunctions(tree, cache) {
|
|
643
|
+
const functions = [];
|
|
644
|
+
// Find top-level variable declarations with arrow functions
|
|
645
|
+
const declarations = [
|
|
646
|
+
...getNodesFromCache(tree.rootNode, 'lexical_declaration', cache),
|
|
647
|
+
...getNodesFromCache(tree.rootNode, 'variable_declaration', cache),
|
|
648
|
+
];
|
|
649
|
+
for (const decl of declarations) {
|
|
650
|
+
// Only process top-level declarations
|
|
651
|
+
if (decl.parent?.type !== 'program' && decl.parent?.type !== 'export_statement') {
|
|
652
|
+
continue;
|
|
653
|
+
}
|
|
654
|
+
const declarators = findNodes(decl, 'variable_declarator');
|
|
655
|
+
for (const declarator of declarators) {
|
|
656
|
+
const nameNode = declarator.childForFieldName('name');
|
|
657
|
+
const valueNode = declarator.childForFieldName('value');
|
|
658
|
+
if (nameNode && nameNode.type === 'identifier' &&
|
|
659
|
+
valueNode && valueNode.type === 'arrow_function') {
|
|
660
|
+
const name = getNodeText(nameNode);
|
|
661
|
+
// Extract parameters from arrow function
|
|
662
|
+
const params = valueNode.childForFieldName('parameters');
|
|
663
|
+
const parameters = params ? extractJSParameters(params) : [];
|
|
664
|
+
// Check for single parameter without parentheses
|
|
665
|
+
if (!params) {
|
|
666
|
+
const paramNode = valueNode.childForFieldName('parameter');
|
|
667
|
+
if (paramNode && paramNode.type === 'identifier') {
|
|
668
|
+
parameters.push({
|
|
669
|
+
name: getNodeText(paramNode),
|
|
670
|
+
type: null,
|
|
671
|
+
annotations: [],
|
|
672
|
+
line: paramNode.startPosition.row + 1,
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
const modifiers = [];
|
|
677
|
+
for (let i = 0; i < valueNode.childCount; i++) {
|
|
678
|
+
const child = valueNode.child(i);
|
|
679
|
+
if (child && child.type === 'async') {
|
|
680
|
+
modifiers.push('async');
|
|
681
|
+
break;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
functions.push({
|
|
685
|
+
name,
|
|
686
|
+
return_type: null,
|
|
687
|
+
parameters,
|
|
688
|
+
annotations: [],
|
|
689
|
+
modifiers,
|
|
690
|
+
start_line: declarator.startPosition.row + 1,
|
|
691
|
+
end_line: declarator.endPosition.row + 1,
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
return functions;
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Extract JavaScript parameters.
|
|
700
|
+
*/
|
|
701
|
+
function extractJSParameters(params) {
|
|
702
|
+
const parameters = [];
|
|
703
|
+
for (let i = 0; i < params.childCount; i++) {
|
|
704
|
+
const child = params.child(i);
|
|
705
|
+
if (!child)
|
|
706
|
+
continue;
|
|
707
|
+
// Skip punctuation
|
|
708
|
+
if (child.type === ',' || child.type === '(' || child.type === ')')
|
|
709
|
+
continue;
|
|
710
|
+
if (child.type === 'identifier') {
|
|
711
|
+
parameters.push({
|
|
712
|
+
name: getNodeText(child),
|
|
713
|
+
type: null,
|
|
714
|
+
annotations: [],
|
|
715
|
+
line: child.startPosition.row + 1,
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
else if (child.type === 'assignment_pattern') {
|
|
719
|
+
// Default parameter: x = defaultValue
|
|
720
|
+
const leftNode = child.childForFieldName('left');
|
|
721
|
+
if (leftNode && leftNode.type === 'identifier') {
|
|
722
|
+
parameters.push({
|
|
723
|
+
name: getNodeText(leftNode),
|
|
724
|
+
type: null,
|
|
725
|
+
annotations: [],
|
|
726
|
+
line: child.startPosition.row + 1,
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
else if (child.type === 'rest_pattern' || child.type === 'rest_element') {
|
|
731
|
+
// Rest parameter: ...args
|
|
732
|
+
const nameNode = child.namedChildCount > 0 ? child.namedChild(0) : null;
|
|
733
|
+
if (nameNode && nameNode.type === 'identifier') {
|
|
734
|
+
parameters.push({
|
|
735
|
+
name: '...' + getNodeText(nameNode),
|
|
736
|
+
type: null,
|
|
737
|
+
annotations: [],
|
|
738
|
+
line: child.startPosition.row + 1,
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
else if (child.type === 'object_pattern' || child.type === 'array_pattern') {
|
|
743
|
+
// Destructuring parameter
|
|
744
|
+
parameters.push({
|
|
745
|
+
name: getNodeText(child),
|
|
746
|
+
type: null,
|
|
747
|
+
annotations: [],
|
|
748
|
+
line: child.startPosition.row + 1,
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
return parameters;
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Extract JavaScript class fields.
|
|
756
|
+
*/
|
|
757
|
+
function extractJSFields(body) {
|
|
758
|
+
const fields = [];
|
|
759
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
760
|
+
const child = body.child(i);
|
|
761
|
+
if (!child)
|
|
762
|
+
continue;
|
|
763
|
+
if (child.type === 'public_field_definition' || child.type === 'field_definition') {
|
|
764
|
+
const nameNode = child.childForFieldName('name');
|
|
765
|
+
const name = nameNode ? getNodeText(nameNode) : 'unknown';
|
|
766
|
+
const modifiers = [];
|
|
767
|
+
for (let j = 0; j < child.childCount; j++) {
|
|
768
|
+
const modifier = child.child(j);
|
|
769
|
+
if (modifier && modifier.type === 'static') {
|
|
770
|
+
modifiers.push('static');
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
fields.push({
|
|
774
|
+
name,
|
|
775
|
+
type: null,
|
|
776
|
+
modifiers,
|
|
777
|
+
annotations: [],
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
return fields;
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
784
|
+
* Extract class information.
|
|
785
|
+
*/
|
|
786
|
+
function extractClassInfo(node) {
|
|
787
|
+
const name = getIdentifier(node, 'name') ?? 'Unknown';
|
|
788
|
+
const annotations = extractAnnotations(node);
|
|
789
|
+
const modifiers = extractModifiers(node);
|
|
790
|
+
// Extract superclass
|
|
791
|
+
const superclass = node.childForFieldName('superclass');
|
|
792
|
+
const extendsType = superclass ? extractTypeName(superclass) : null;
|
|
793
|
+
// Extract interfaces - search for super_interfaces node
|
|
794
|
+
const implementsList = extractImplementsList(node);
|
|
795
|
+
// Extract body
|
|
796
|
+
const body = node.childForFieldName('body');
|
|
797
|
+
const methods = body ? extractMethods(body) : [];
|
|
798
|
+
const fields = body ? extractFields(body) : [];
|
|
799
|
+
// Get package from ancestors
|
|
800
|
+
const pkg = extractPackageFromAncestors(node);
|
|
801
|
+
return {
|
|
802
|
+
name,
|
|
803
|
+
kind: 'class',
|
|
804
|
+
package: pkg,
|
|
805
|
+
extends: extendsType,
|
|
806
|
+
implements: implementsList,
|
|
807
|
+
annotations,
|
|
808
|
+
methods,
|
|
809
|
+
fields,
|
|
810
|
+
start_line: node.startPosition.row + 1,
|
|
811
|
+
end_line: node.endPosition.row + 1,
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
/**
|
|
815
|
+
* Extract interface information.
|
|
816
|
+
*/
|
|
817
|
+
function extractInterfaceInfo(node) {
|
|
818
|
+
const name = getIdentifier(node, 'name') ?? 'Unknown';
|
|
819
|
+
const annotations = extractAnnotations(node);
|
|
820
|
+
// Extract extended interfaces
|
|
821
|
+
const extendsClause = node.childForFieldName('extends');
|
|
822
|
+
const extendsList = extendsClause ? extractTypeList(extendsClause) : [];
|
|
823
|
+
// Extract body
|
|
824
|
+
const body = node.childForFieldName('body');
|
|
825
|
+
const methods = body ? extractMethods(body) : [];
|
|
826
|
+
const fields = body ? extractFields(body) : [];
|
|
827
|
+
const pkg = extractPackageFromAncestors(node);
|
|
828
|
+
return {
|
|
829
|
+
name,
|
|
830
|
+
kind: 'interface',
|
|
831
|
+
package: pkg,
|
|
832
|
+
extends: extendsList.length > 0 ? extendsList[0] : null,
|
|
833
|
+
implements: extendsList.slice(1),
|
|
834
|
+
annotations,
|
|
835
|
+
methods,
|
|
836
|
+
fields,
|
|
837
|
+
start_line: node.startPosition.row + 1,
|
|
838
|
+
end_line: node.endPosition.row + 1,
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
/**
|
|
842
|
+
* Extract enum information.
|
|
843
|
+
*/
|
|
844
|
+
function extractEnumInfo(node) {
|
|
845
|
+
const name = getIdentifier(node, 'name') ?? 'Unknown';
|
|
846
|
+
const annotations = extractAnnotations(node);
|
|
847
|
+
// Extract interfaces
|
|
848
|
+
const implementsList = extractImplementsList(node);
|
|
849
|
+
// Extract body
|
|
850
|
+
const body = node.childForFieldName('body');
|
|
851
|
+
const methods = body ? extractMethods(body) : [];
|
|
852
|
+
const fields = body ? extractFields(body) : [];
|
|
853
|
+
const pkg = extractPackageFromAncestors(node);
|
|
854
|
+
return {
|
|
855
|
+
name,
|
|
856
|
+
kind: 'enum',
|
|
857
|
+
package: pkg,
|
|
858
|
+
extends: null,
|
|
859
|
+
implements: implementsList,
|
|
860
|
+
annotations,
|
|
861
|
+
methods,
|
|
862
|
+
fields,
|
|
863
|
+
start_line: node.startPosition.row + 1,
|
|
864
|
+
end_line: node.endPosition.row + 1,
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
/**
|
|
868
|
+
* Extract methods from a class/interface body.
|
|
869
|
+
*/
|
|
870
|
+
function extractMethods(body) {
|
|
871
|
+
const methods = [];
|
|
872
|
+
let precedingComment = null;
|
|
873
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
874
|
+
const child = body.child(i);
|
|
875
|
+
if (!child)
|
|
876
|
+
continue;
|
|
877
|
+
// Track block comments (Javadoc)
|
|
878
|
+
if (child.type === 'block_comment') {
|
|
879
|
+
precedingComment = child;
|
|
880
|
+
continue;
|
|
881
|
+
}
|
|
882
|
+
if (child.type === 'method_declaration' || child.type === 'constructor_declaration') {
|
|
883
|
+
methods.push(extractMethodInfo(child, precedingComment));
|
|
884
|
+
precedingComment = null; // Reset after using
|
|
885
|
+
}
|
|
886
|
+
else {
|
|
887
|
+
// Non-method, non-comment node - reset preceding comment
|
|
888
|
+
precedingComment = null;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
return methods;
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
894
|
+
* Extract method information.
|
|
895
|
+
*/
|
|
896
|
+
function extractMethodInfo(node, precedingComment = null) {
|
|
897
|
+
const isConstructor = node.type === 'constructor_declaration';
|
|
898
|
+
const name = getIdentifier(node, 'name') ?? (isConstructor ? '<init>' : 'unknown');
|
|
899
|
+
// Extract return type
|
|
900
|
+
const returnTypeNode = node.childForFieldName('type');
|
|
901
|
+
const returnType = returnTypeNode ? getNodeText(returnTypeNode) : (isConstructor ? null : 'void');
|
|
902
|
+
// Extract parameters
|
|
903
|
+
const params = node.childForFieldName('parameters');
|
|
904
|
+
const parameters = params ? extractParameters(params) : [];
|
|
905
|
+
// Extract annotations and modifiers
|
|
906
|
+
const annotations = extractAnnotations(node);
|
|
907
|
+
const modifiers = extractModifiers(node);
|
|
908
|
+
// Check for @sanitizer in Javadoc comment
|
|
909
|
+
if (precedingComment) {
|
|
910
|
+
const commentText = getNodeText(precedingComment);
|
|
911
|
+
if (commentText.includes('@sanitizer')) {
|
|
912
|
+
annotations.push('sanitizer');
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
return {
|
|
916
|
+
name,
|
|
917
|
+
return_type: returnType,
|
|
918
|
+
parameters,
|
|
919
|
+
annotations,
|
|
920
|
+
modifiers,
|
|
921
|
+
start_line: node.startPosition.row + 1,
|
|
922
|
+
end_line: node.endPosition.row + 1,
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* Extract parameters from a formal_parameters node.
|
|
927
|
+
*/
|
|
928
|
+
function extractParameters(params) {
|
|
929
|
+
const parameters = [];
|
|
930
|
+
for (let i = 0; i < params.childCount; i++) {
|
|
931
|
+
const child = params.child(i);
|
|
932
|
+
if (!child)
|
|
933
|
+
continue;
|
|
934
|
+
if (child.type === 'formal_parameter' || child.type === 'spread_parameter') {
|
|
935
|
+
const name = getIdentifier(child, 'name') ?? `arg${i}`;
|
|
936
|
+
const typeNode = child.childForFieldName('type');
|
|
937
|
+
const type = typeNode ? getNodeText(typeNode) : null;
|
|
938
|
+
const annotations = extractAnnotations(child);
|
|
939
|
+
const line = child.startPosition.row + 1;
|
|
940
|
+
parameters.push({ name, type, annotations, line });
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
return parameters;
|
|
944
|
+
}
|
|
945
|
+
/**
|
|
946
|
+
* Extract fields from a class body.
|
|
947
|
+
*/
|
|
948
|
+
function extractFields(body) {
|
|
949
|
+
const fields = [];
|
|
950
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
951
|
+
const child = body.child(i);
|
|
952
|
+
if (!child)
|
|
953
|
+
continue;
|
|
954
|
+
if (child.type === 'field_declaration') {
|
|
955
|
+
const typeNode = child.childForFieldName('type');
|
|
956
|
+
const type = typeNode ? getNodeText(typeNode) : null;
|
|
957
|
+
const annotations = extractAnnotations(child);
|
|
958
|
+
const modifiers = extractModifiers(child);
|
|
959
|
+
// Extract declarators (there can be multiple: int a, b, c;)
|
|
960
|
+
const declarators = findNodes(child, 'variable_declarator');
|
|
961
|
+
for (const decl of declarators) {
|
|
962
|
+
const name = getIdentifier(decl, 'name') ?? 'unknown';
|
|
963
|
+
fields.push({ name, type, modifiers, annotations });
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
return fields;
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* Extract annotations from a node.
|
|
971
|
+
*/
|
|
972
|
+
function extractAnnotations(node) {
|
|
973
|
+
const annotations = [];
|
|
974
|
+
// Look for modifiers node which contains annotations
|
|
975
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
976
|
+
const child = node.child(i);
|
|
977
|
+
if (!child)
|
|
978
|
+
continue;
|
|
979
|
+
if (child.type === 'modifiers') {
|
|
980
|
+
for (let j = 0; j < child.childCount; j++) {
|
|
981
|
+
const modifier = child.child(j);
|
|
982
|
+
if (modifier?.type === 'marker_annotation' || modifier?.type === 'annotation') {
|
|
983
|
+
// Get the full annotation text without the @ symbol
|
|
984
|
+
let text = getNodeText(modifier);
|
|
985
|
+
if (text.startsWith('@')) {
|
|
986
|
+
text = text.substring(1);
|
|
987
|
+
}
|
|
988
|
+
annotations.push(text);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
// Also check direct annotation children
|
|
993
|
+
if (child.type === 'marker_annotation' || child.type === 'annotation') {
|
|
994
|
+
let text = getNodeText(child);
|
|
995
|
+
if (text.startsWith('@')) {
|
|
996
|
+
text = text.substring(1);
|
|
997
|
+
}
|
|
998
|
+
annotations.push(text);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
return annotations;
|
|
1002
|
+
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Extract modifiers (public, private, static, etc.) from a node.
|
|
1005
|
+
*/
|
|
1006
|
+
function extractModifiers(node) {
|
|
1007
|
+
const modifiers = [];
|
|
1008
|
+
const modifierKeywords = new Set([
|
|
1009
|
+
'public', 'private', 'protected', 'static', 'final',
|
|
1010
|
+
'abstract', 'synchronized', 'native', 'transient', 'volatile'
|
|
1011
|
+
]);
|
|
1012
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
1013
|
+
const child = node.child(i);
|
|
1014
|
+
if (!child)
|
|
1015
|
+
continue;
|
|
1016
|
+
if (child.type === 'modifiers') {
|
|
1017
|
+
for (let j = 0; j < child.childCount; j++) {
|
|
1018
|
+
const modifier = child.child(j);
|
|
1019
|
+
if (modifier && modifierKeywords.has(modifier.type)) {
|
|
1020
|
+
modifiers.push(modifier.type);
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
// Direct modifier children
|
|
1025
|
+
if (modifierKeywords.has(child.type)) {
|
|
1026
|
+
modifiers.push(child.type);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
return modifiers;
|
|
1030
|
+
}
|
|
1031
|
+
/**
|
|
1032
|
+
* Extract implements list from a class declaration.
|
|
1033
|
+
*/
|
|
1034
|
+
function extractImplementsList(node) {
|
|
1035
|
+
// Try field name first - this returns super_interfaces node
|
|
1036
|
+
let interfaces = node.childForFieldName('interfaces');
|
|
1037
|
+
// Also search for super_interfaces node directly
|
|
1038
|
+
if (!interfaces) {
|
|
1039
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
1040
|
+
const child = node.child(i);
|
|
1041
|
+
if (child && child.type === 'super_interfaces') {
|
|
1042
|
+
interfaces = child;
|
|
1043
|
+
break;
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
if (!interfaces) {
|
|
1048
|
+
return [];
|
|
1049
|
+
}
|
|
1050
|
+
// super_interfaces contains a type_list - find it
|
|
1051
|
+
for (let i = 0; i < interfaces.childCount; i++) {
|
|
1052
|
+
const child = interfaces.child(i);
|
|
1053
|
+
if (child && child.type === 'type_list') {
|
|
1054
|
+
return extractTypeList(child);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
// Fallback to extracting directly from super_interfaces
|
|
1058
|
+
return extractTypeList(interfaces);
|
|
1059
|
+
}
|
|
1060
|
+
/**
|
|
1061
|
+
* Extract a list of types from a type_list or extends clause.
|
|
1062
|
+
*/
|
|
1063
|
+
function extractTypeList(node) {
|
|
1064
|
+
const types = [];
|
|
1065
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
1066
|
+
const child = node.child(i);
|
|
1067
|
+
if (!child)
|
|
1068
|
+
continue;
|
|
1069
|
+
if (child.type === 'type_identifier' || child.type === 'generic_type' || child.type === 'scoped_type_identifier') {
|
|
1070
|
+
types.push(getNodeText(child));
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
return types;
|
|
1074
|
+
}
|
|
1075
|
+
/**
|
|
1076
|
+
* Get identifier from a node by field name.
|
|
1077
|
+
*/
|
|
1078
|
+
function getIdentifier(node, fieldName) {
|
|
1079
|
+
const field = node.childForFieldName(fieldName);
|
|
1080
|
+
if (field) {
|
|
1081
|
+
return getNodeText(field);
|
|
1082
|
+
}
|
|
1083
|
+
return null;
|
|
1084
|
+
}
|
|
1085
|
+
/**
|
|
1086
|
+
* Extract type name from a type node, handling various type structures.
|
|
1087
|
+
*/
|
|
1088
|
+
function extractTypeName(node) {
|
|
1089
|
+
// For type_identifier, generic_type, scoped_type_identifier - use the text
|
|
1090
|
+
if (node.type === 'type_identifier' ||
|
|
1091
|
+
node.type === 'generic_type' ||
|
|
1092
|
+
node.type === 'scoped_type_identifier') {
|
|
1093
|
+
return getNodeText(node);
|
|
1094
|
+
}
|
|
1095
|
+
// For superclass node, find the actual type inside
|
|
1096
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
1097
|
+
const child = node.child(i);
|
|
1098
|
+
if (child && (child.type === 'type_identifier' ||
|
|
1099
|
+
child.type === 'generic_type' ||
|
|
1100
|
+
child.type === 'scoped_type_identifier')) {
|
|
1101
|
+
return getNodeText(child);
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
// Fallback to the node text
|
|
1105
|
+
return getNodeText(node);
|
|
1106
|
+
}
|
|
1107
|
+
/**
|
|
1108
|
+
* Extract package from ancestor nodes.
|
|
1109
|
+
*/
|
|
1110
|
+
function extractPackageFromAncestors(node) {
|
|
1111
|
+
let current = node.parent;
|
|
1112
|
+
while (current) {
|
|
1113
|
+
if (current.type === 'program') {
|
|
1114
|
+
const packageDecls = findNodes(current, 'package_declaration');
|
|
1115
|
+
if (packageDecls.length > 0) {
|
|
1116
|
+
const pkgDecl = packageDecls[0];
|
|
1117
|
+
// Try field name first
|
|
1118
|
+
const pkgNode = pkgDecl.childForFieldName('name');
|
|
1119
|
+
if (pkgNode) {
|
|
1120
|
+
return getNodeText(pkgNode);
|
|
1121
|
+
}
|
|
1122
|
+
// Fall back to finding scoped_identifier or identifier
|
|
1123
|
+
for (let i = 0; i < pkgDecl.childCount; i++) {
|
|
1124
|
+
const child = pkgDecl.child(i);
|
|
1125
|
+
if (child && (child.type === 'scoped_identifier' || child.type === 'identifier')) {
|
|
1126
|
+
return getNodeText(child);
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
current = current.parent;
|
|
1132
|
+
}
|
|
1133
|
+
return null;
|
|
1134
|
+
}
|
|
1135
|
+
// =============================================================================
|
|
1136
|
+
// Rust Type Extraction
|
|
1137
|
+
// =============================================================================
|
|
1138
|
+
/**
|
|
1139
|
+
* Extract Rust types (structs, enums, traits, impl blocks, functions).
|
|
1140
|
+
*/
|
|
1141
|
+
function extractRustTypes(tree, cache) {
|
|
1142
|
+
const types = [];
|
|
1143
|
+
const root = tree.rootNode;
|
|
1144
|
+
// Extract structs
|
|
1145
|
+
const structs = getNodesFromCache(root, 'struct_item', cache);
|
|
1146
|
+
for (const structNode of structs) {
|
|
1147
|
+
types.push(extractRustStructInfo(structNode));
|
|
1148
|
+
}
|
|
1149
|
+
// Extract enums
|
|
1150
|
+
const enums = getNodesFromCache(root, 'enum_item', cache);
|
|
1151
|
+
for (const enumNode of enums) {
|
|
1152
|
+
types.push(extractRustEnumInfo(enumNode));
|
|
1153
|
+
}
|
|
1154
|
+
// Extract traits (as interfaces)
|
|
1155
|
+
const traits = getNodesFromCache(root, 'trait_item', cache);
|
|
1156
|
+
for (const traitNode of traits) {
|
|
1157
|
+
types.push(extractRustTraitInfo(traitNode));
|
|
1158
|
+
}
|
|
1159
|
+
// Extract impl blocks and merge methods into their types
|
|
1160
|
+
const impls = getNodesFromCache(root, 'impl_item', cache);
|
|
1161
|
+
for (const implNode of impls) {
|
|
1162
|
+
const implInfo = extractRustImplInfo(implNode);
|
|
1163
|
+
// Find existing type and add methods
|
|
1164
|
+
const existingType = types.find(t => t.name === implInfo.typeName);
|
|
1165
|
+
if (existingType) {
|
|
1166
|
+
existingType.methods.push(...implInfo.methods);
|
|
1167
|
+
if (implInfo.traitName) {
|
|
1168
|
+
existingType.implements.push(implInfo.traitName);
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
else {
|
|
1172
|
+
// Create synthetic type for impl without struct definition
|
|
1173
|
+
types.push({
|
|
1174
|
+
name: implInfo.typeName,
|
|
1175
|
+
kind: 'class',
|
|
1176
|
+
package: null,
|
|
1177
|
+
extends: null,
|
|
1178
|
+
implements: implInfo.traitName ? [implInfo.traitName] : [],
|
|
1179
|
+
annotations: [],
|
|
1180
|
+
methods: implInfo.methods,
|
|
1181
|
+
fields: [],
|
|
1182
|
+
start_line: implNode.startPosition.row + 1,
|
|
1183
|
+
end_line: implNode.endPosition.row + 1,
|
|
1184
|
+
});
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
// Extract standalone functions as a module-like type
|
|
1188
|
+
const functions = getNodesFromCache(root, 'function_item', cache);
|
|
1189
|
+
const topLevelFunctions = [];
|
|
1190
|
+
for (const func of functions) {
|
|
1191
|
+
// Only include top-level functions (not inside impl blocks)
|
|
1192
|
+
if (func.parent?.type === 'source_file') {
|
|
1193
|
+
topLevelFunctions.push(extractRustFunctionInfo(func));
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
// Create a synthetic module type for standalone functions
|
|
1197
|
+
if (topLevelFunctions.length > 0) {
|
|
1198
|
+
types.push({
|
|
1199
|
+
name: '<module>',
|
|
1200
|
+
kind: 'class',
|
|
1201
|
+
package: null,
|
|
1202
|
+
extends: null,
|
|
1203
|
+
implements: [],
|
|
1204
|
+
annotations: [],
|
|
1205
|
+
methods: topLevelFunctions,
|
|
1206
|
+
fields: [],
|
|
1207
|
+
start_line: 1,
|
|
1208
|
+
end_line: root.endPosition.row + 1,
|
|
1209
|
+
});
|
|
1210
|
+
}
|
|
1211
|
+
return types;
|
|
1212
|
+
}
|
|
1213
|
+
/**
|
|
1214
|
+
* Extract Rust struct information.
|
|
1215
|
+
*/
|
|
1216
|
+
function extractRustStructInfo(node) {
|
|
1217
|
+
const nameNode = node.childForFieldName('name');
|
|
1218
|
+
const name = nameNode ? getNodeText(nameNode) : 'Anonymous';
|
|
1219
|
+
// Extract fields from field_declaration_list
|
|
1220
|
+
const fields = [];
|
|
1221
|
+
const fieldList = findChildByType(node, 'field_declaration_list');
|
|
1222
|
+
if (fieldList) {
|
|
1223
|
+
for (let i = 0; i < fieldList.childCount; i++) {
|
|
1224
|
+
const child = fieldList.child(i);
|
|
1225
|
+
if (child?.type === 'field_declaration') {
|
|
1226
|
+
const fieldNameNode = child.childForFieldName('name');
|
|
1227
|
+
const fieldTypeNode = child.childForFieldName('type');
|
|
1228
|
+
const visibility = extractRustVisibility(child);
|
|
1229
|
+
fields.push({
|
|
1230
|
+
name: fieldNameNode ? getNodeText(fieldNameNode) : 'unknown',
|
|
1231
|
+
type: fieldTypeNode ? getNodeText(fieldTypeNode) : null,
|
|
1232
|
+
modifiers: visibility ? [visibility] : [],
|
|
1233
|
+
annotations: [],
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
// Extract visibility
|
|
1239
|
+
const visibility = extractRustVisibility(node);
|
|
1240
|
+
const annotations = visibility ? [visibility] : [];
|
|
1241
|
+
// Extract derive macros as annotations
|
|
1242
|
+
const derives = extractRustDerives(node);
|
|
1243
|
+
annotations.push(...derives);
|
|
1244
|
+
return {
|
|
1245
|
+
name,
|
|
1246
|
+
kind: 'class',
|
|
1247
|
+
package: null,
|
|
1248
|
+
extends: null,
|
|
1249
|
+
implements: [],
|
|
1250
|
+
annotations,
|
|
1251
|
+
methods: [], // Methods are added from impl blocks
|
|
1252
|
+
fields,
|
|
1253
|
+
start_line: node.startPosition.row + 1,
|
|
1254
|
+
end_line: node.endPosition.row + 1,
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
/**
|
|
1258
|
+
* Extract Rust enum information.
|
|
1259
|
+
*/
|
|
1260
|
+
function extractRustEnumInfo(node) {
|
|
1261
|
+
const nameNode = node.childForFieldName('name');
|
|
1262
|
+
const name = nameNode ? getNodeText(nameNode) : 'Anonymous';
|
|
1263
|
+
// Extract variants as fields
|
|
1264
|
+
const fields = [];
|
|
1265
|
+
const body = node.childForFieldName('body');
|
|
1266
|
+
if (body) {
|
|
1267
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
1268
|
+
const child = body.child(i);
|
|
1269
|
+
if (child?.type === 'enum_variant') {
|
|
1270
|
+
const variantName = child.childForFieldName('name');
|
|
1271
|
+
fields.push({
|
|
1272
|
+
name: variantName ? getNodeText(variantName) : 'unknown',
|
|
1273
|
+
type: null,
|
|
1274
|
+
modifiers: [],
|
|
1275
|
+
annotations: [],
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
const visibility = extractRustVisibility(node);
|
|
1281
|
+
const annotations = visibility ? [visibility] : [];
|
|
1282
|
+
const derives = extractRustDerives(node);
|
|
1283
|
+
annotations.push(...derives);
|
|
1284
|
+
return {
|
|
1285
|
+
name,
|
|
1286
|
+
kind: 'enum',
|
|
1287
|
+
package: null,
|
|
1288
|
+
extends: null,
|
|
1289
|
+
implements: [],
|
|
1290
|
+
annotations,
|
|
1291
|
+
methods: [],
|
|
1292
|
+
fields,
|
|
1293
|
+
start_line: node.startPosition.row + 1,
|
|
1294
|
+
end_line: node.endPosition.row + 1,
|
|
1295
|
+
};
|
|
1296
|
+
}
|
|
1297
|
+
/**
|
|
1298
|
+
* Extract Rust trait information.
|
|
1299
|
+
*/
|
|
1300
|
+
function extractRustTraitInfo(node) {
|
|
1301
|
+
const nameNode = node.childForFieldName('name');
|
|
1302
|
+
const name = nameNode ? getNodeText(nameNode) : 'Anonymous';
|
|
1303
|
+
// Extract methods from trait body
|
|
1304
|
+
const methods = [];
|
|
1305
|
+
const body = node.childForFieldName('body');
|
|
1306
|
+
if (body) {
|
|
1307
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
1308
|
+
const child = body.child(i);
|
|
1309
|
+
if (child?.type === 'function_signature_item' || child?.type === 'function_item') {
|
|
1310
|
+
methods.push(extractRustFunctionInfo(child));
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
const visibility = extractRustVisibility(node);
|
|
1315
|
+
return {
|
|
1316
|
+
name,
|
|
1317
|
+
kind: 'interface',
|
|
1318
|
+
package: null,
|
|
1319
|
+
extends: null,
|
|
1320
|
+
implements: [],
|
|
1321
|
+
annotations: visibility ? [visibility] : [],
|
|
1322
|
+
methods,
|
|
1323
|
+
fields: [],
|
|
1324
|
+
start_line: node.startPosition.row + 1,
|
|
1325
|
+
end_line: node.endPosition.row + 1,
|
|
1326
|
+
};
|
|
1327
|
+
}
|
|
1328
|
+
/**
|
|
1329
|
+
* Extract Rust impl block information.
|
|
1330
|
+
*/
|
|
1331
|
+
function extractRustImplInfo(node) {
|
|
1332
|
+
// impl Trait for Type or impl Type
|
|
1333
|
+
let typeName = 'Unknown';
|
|
1334
|
+
let traitName = null;
|
|
1335
|
+
const typeNode = node.childForFieldName('type');
|
|
1336
|
+
if (typeNode) {
|
|
1337
|
+
typeName = getNodeText(typeNode);
|
|
1338
|
+
}
|
|
1339
|
+
// Check for trait impl (impl Trait for Type)
|
|
1340
|
+
const traitNode = node.childForFieldName('trait');
|
|
1341
|
+
if (traitNode) {
|
|
1342
|
+
traitName = getNodeText(traitNode);
|
|
1343
|
+
}
|
|
1344
|
+
// Extract methods
|
|
1345
|
+
const methods = [];
|
|
1346
|
+
const body = node.childForFieldName('body');
|
|
1347
|
+
if (body) {
|
|
1348
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
1349
|
+
const child = body.child(i);
|
|
1350
|
+
if (child?.type === 'function_item') {
|
|
1351
|
+
methods.push(extractRustFunctionInfo(child));
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
return { typeName, traitName, methods };
|
|
1356
|
+
}
|
|
1357
|
+
/**
|
|
1358
|
+
* Extract Rust function/method information.
|
|
1359
|
+
*/
|
|
1360
|
+
function extractRustFunctionInfo(node) {
|
|
1361
|
+
const nameNode = node.childForFieldName('name');
|
|
1362
|
+
const name = nameNode ? getNodeText(nameNode) : 'anonymous';
|
|
1363
|
+
// Extract parameters
|
|
1364
|
+
const paramsNode = node.childForFieldName('parameters');
|
|
1365
|
+
const parameters = paramsNode ? extractRustParameters(paramsNode) : [];
|
|
1366
|
+
// Extract return type
|
|
1367
|
+
const returnTypeNode = node.childForFieldName('return_type');
|
|
1368
|
+
const returnType = returnTypeNode ? getNodeText(returnTypeNode) : null;
|
|
1369
|
+
// Extract visibility and async modifier
|
|
1370
|
+
const modifiers = [];
|
|
1371
|
+
const visibility = extractRustVisibility(node);
|
|
1372
|
+
if (visibility)
|
|
1373
|
+
modifiers.push(visibility);
|
|
1374
|
+
// Check for async
|
|
1375
|
+
const functionModifiers = findChildByType(node, 'function_modifiers');
|
|
1376
|
+
if (functionModifiers) {
|
|
1377
|
+
for (let i = 0; i < functionModifiers.childCount; i++) {
|
|
1378
|
+
const child = functionModifiers.child(i);
|
|
1379
|
+
if (child?.type === 'async') {
|
|
1380
|
+
modifiers.push('async');
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
// Extract body for line count
|
|
1385
|
+
const body = node.childForFieldName('body');
|
|
1386
|
+
return {
|
|
1387
|
+
name,
|
|
1388
|
+
return_type: returnType,
|
|
1389
|
+
parameters,
|
|
1390
|
+
modifiers,
|
|
1391
|
+
annotations: [],
|
|
1392
|
+
start_line: node.startPosition.row + 1,
|
|
1393
|
+
end_line: node.endPosition.row + 1,
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1396
|
+
/**
|
|
1397
|
+
* Extract Rust function parameters.
|
|
1398
|
+
*/
|
|
1399
|
+
function extractRustParameters(paramsNode) {
|
|
1400
|
+
const parameters = [];
|
|
1401
|
+
for (let i = 0; i < paramsNode.childCount; i++) {
|
|
1402
|
+
const child = paramsNode.child(i);
|
|
1403
|
+
if (!child)
|
|
1404
|
+
continue;
|
|
1405
|
+
if (child.type === 'parameter') {
|
|
1406
|
+
const pattern = child.childForFieldName('pattern');
|
|
1407
|
+
const typeNode = child.childForFieldName('type');
|
|
1408
|
+
parameters.push({
|
|
1409
|
+
name: pattern ? getNodeText(pattern) : 'arg',
|
|
1410
|
+
type: typeNode ? getNodeText(typeNode) : null,
|
|
1411
|
+
annotations: [],
|
|
1412
|
+
line: child.startPosition.row + 1,
|
|
1413
|
+
});
|
|
1414
|
+
}
|
|
1415
|
+
else if (child.type === 'self_parameter') {
|
|
1416
|
+
// &self, &mut self, self
|
|
1417
|
+
parameters.push({
|
|
1418
|
+
name: getNodeText(child),
|
|
1419
|
+
type: 'Self',
|
|
1420
|
+
annotations: [],
|
|
1421
|
+
line: child.startPosition.row + 1,
|
|
1422
|
+
});
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
return parameters;
|
|
1426
|
+
}
|
|
1427
|
+
/**
|
|
1428
|
+
* Extract Rust visibility modifier (pub, pub(crate), etc.).
|
|
1429
|
+
*/
|
|
1430
|
+
function extractRustVisibility(node) {
|
|
1431
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
1432
|
+
const child = node.child(i);
|
|
1433
|
+
if (child?.type === 'visibility_modifier') {
|
|
1434
|
+
return getNodeText(child);
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
return null;
|
|
1438
|
+
}
|
|
1439
|
+
/**
|
|
1440
|
+
* Extract derive macros from Rust struct/enum.
|
|
1441
|
+
*/
|
|
1442
|
+
function extractRustDerives(node) {
|
|
1443
|
+
const derives = [];
|
|
1444
|
+
// Check for attribute items before the struct/enum
|
|
1445
|
+
let prev = node.previousSibling;
|
|
1446
|
+
while (prev) {
|
|
1447
|
+
if (prev.type === 'attribute_item') {
|
|
1448
|
+
const text = getNodeText(prev);
|
|
1449
|
+
// Parse #[derive(X, Y, Z)]
|
|
1450
|
+
const match = text.match(/#\[derive\(([^)]+)\)\]/);
|
|
1451
|
+
if (match) {
|
|
1452
|
+
const items = match[1].split(',').map(s => s.trim());
|
|
1453
|
+
derives.push(...items.map(d => `derive(${d})`));
|
|
1454
|
+
}
|
|
1455
|
+
else {
|
|
1456
|
+
// Other attributes like #[serde(rename_all = "camelCase")]
|
|
1457
|
+
derives.push(text.replace(/^#\[/, '').replace(/\]$/, ''));
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
else {
|
|
1461
|
+
break;
|
|
1462
|
+
}
|
|
1463
|
+
prev = prev.previousSibling;
|
|
1464
|
+
}
|
|
1465
|
+
return derives;
|
|
1466
|
+
}
|
|
1467
|
+
/**
|
|
1468
|
+
* Find child node by type.
|
|
1469
|
+
*/
|
|
1470
|
+
function findChildByType(node, type) {
|
|
1471
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
1472
|
+
const child = node.child(i);
|
|
1473
|
+
if (child?.type === type) {
|
|
1474
|
+
return child;
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
return null;
|
|
1478
|
+
}
|
|
1479
|
+
//# sourceMappingURL=types.js.map
|