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.
Files changed (194) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +200 -0
  3. package/configs/sinks/code_injection.yaml +672 -0
  4. package/configs/sinks/command.yaml +917 -0
  5. package/configs/sinks/deserialization.yaml +105 -0
  6. package/configs/sinks/ldap.yaml +136 -0
  7. package/configs/sinks/nodejs.json +629 -0
  8. package/configs/sinks/path.yaml +715 -0
  9. package/configs/sinks/python.json +501 -0
  10. package/configs/sinks/rust.json +339 -0
  11. package/configs/sinks/sql.yaml +233 -0
  12. package/configs/sinks/ssrf.yaml +160 -0
  13. package/configs/sinks/xpath.yaml +121 -0
  14. package/configs/sinks/xss.yaml +727 -0
  15. package/configs/sources/db_sources.yaml +90 -0
  16. package/configs/sources/env_sources.yaml +94 -0
  17. package/configs/sources/express.json +197 -0
  18. package/configs/sources/file_sources.yaml +164 -0
  19. package/configs/sources/http_sources.yaml +379 -0
  20. package/configs/sources/io_sources.yaml +519 -0
  21. package/configs/sources/network_sources.yaml +99 -0
  22. package/configs/sources/python.json +230 -0
  23. package/configs/sources/rust.json +286 -0
  24. package/configs/sources/spring.yaml +70 -0
  25. package/dist/analysis/advisory-db.d.ts +86 -0
  26. package/dist/analysis/advisory-db.js +104 -0
  27. package/dist/analysis/advisory-db.js.map +1 -0
  28. package/dist/analysis/cargo-parser.d.ts +42 -0
  29. package/dist/analysis/cargo-parser.js +102 -0
  30. package/dist/analysis/cargo-parser.js.map +1 -0
  31. package/dist/analysis/config-loader.d.ts +37 -0
  32. package/dist/analysis/config-loader.js +1561 -0
  33. package/dist/analysis/config-loader.js.map +1 -0
  34. package/dist/analysis/constant-propagation/ast-utils.d.ts +25 -0
  35. package/dist/analysis/constant-propagation/ast-utils.js +34 -0
  36. package/dist/analysis/constant-propagation/ast-utils.js.map +1 -0
  37. package/dist/analysis/constant-propagation/evaluator.d.ts +32 -0
  38. package/dist/analysis/constant-propagation/evaluator.js +296 -0
  39. package/dist/analysis/constant-propagation/evaluator.js.map +1 -0
  40. package/dist/analysis/constant-propagation/index.d.ts +62 -0
  41. package/dist/analysis/constant-propagation/index.js +152 -0
  42. package/dist/analysis/constant-propagation/index.js.map +1 -0
  43. package/dist/analysis/constant-propagation/patterns.d.ts +8 -0
  44. package/dist/analysis/constant-propagation/patterns.js +126 -0
  45. package/dist/analysis/constant-propagation/patterns.js.map +1 -0
  46. package/dist/analysis/constant-propagation/propagator.d.ts +180 -0
  47. package/dist/analysis/constant-propagation/propagator.js +1985 -0
  48. package/dist/analysis/constant-propagation/propagator.js.map +1 -0
  49. package/dist/analysis/constant-propagation/types.d.ts +63 -0
  50. package/dist/analysis/constant-propagation/types.js +5 -0
  51. package/dist/analysis/constant-propagation/types.js.map +1 -0
  52. package/dist/analysis/constant-propagation.d.ts +9 -0
  53. package/dist/analysis/constant-propagation.js +18 -0
  54. package/dist/analysis/constant-propagation.js.map +1 -0
  55. package/dist/analysis/dependency-scanner.d.ts +79 -0
  56. package/dist/analysis/dependency-scanner.js +122 -0
  57. package/dist/analysis/dependency-scanner.js.map +1 -0
  58. package/dist/analysis/dfg-verifier.d.ts +116 -0
  59. package/dist/analysis/dfg-verifier.js +399 -0
  60. package/dist/analysis/dfg-verifier.js.map +1 -0
  61. package/dist/analysis/findings.d.ts +11 -0
  62. package/dist/analysis/findings.js +228 -0
  63. package/dist/analysis/findings.js.map +1 -0
  64. package/dist/analysis/index.d.ts +16 -0
  65. package/dist/analysis/index.js +18 -0
  66. package/dist/analysis/index.js.map +1 -0
  67. package/dist/analysis/interprocedural.d.ts +99 -0
  68. package/dist/analysis/interprocedural.js +526 -0
  69. package/dist/analysis/interprocedural.js.map +1 -0
  70. package/dist/analysis/path-finder.d.ts +133 -0
  71. package/dist/analysis/path-finder.js +354 -0
  72. package/dist/analysis/path-finder.js.map +1 -0
  73. package/dist/analysis/rules.d.ts +75 -0
  74. package/dist/analysis/rules.js +332 -0
  75. package/dist/analysis/rules.js.map +1 -0
  76. package/dist/analysis/semver.d.ts +27 -0
  77. package/dist/analysis/semver.js +127 -0
  78. package/dist/analysis/semver.js.map +1 -0
  79. package/dist/analysis/taint-matcher.d.ts +15 -0
  80. package/dist/analysis/taint-matcher.js +634 -0
  81. package/dist/analysis/taint-matcher.js.map +1 -0
  82. package/dist/analysis/taint-propagation.d.ts +67 -0
  83. package/dist/analysis/taint-propagation.js +298 -0
  84. package/dist/analysis/taint-propagation.js.map +1 -0
  85. package/dist/analysis/unresolved.d.ts +14 -0
  86. package/dist/analysis/unresolved.js +202 -0
  87. package/dist/analysis/unresolved.js.map +1 -0
  88. package/dist/analyzer.d.ts +43 -0
  89. package/dist/analyzer.js +1010 -0
  90. package/dist/analyzer.js.map +1 -0
  91. package/dist/browser/circle-ir.js +16576 -0
  92. package/dist/browser.d.ts +38 -0
  93. package/dist/browser.js +38 -0
  94. package/dist/browser.js.map +1 -0
  95. package/dist/core/circle-ir-core.cjs +13626 -0
  96. package/dist/core/circle-ir-core.d.ts +59 -0
  97. package/dist/core/circle-ir-core.js +13591 -0
  98. package/dist/core/extractors/calls.d.ts +13 -0
  99. package/dist/core/extractors/calls.js +1429 -0
  100. package/dist/core/extractors/calls.js.map +1 -0
  101. package/dist/core/extractors/cfg.d.ts +9 -0
  102. package/dist/core/extractors/cfg.js +519 -0
  103. package/dist/core/extractors/cfg.js.map +1 -0
  104. package/dist/core/extractors/dfg.d.ts +12 -0
  105. package/dist/core/extractors/dfg.js +1081 -0
  106. package/dist/core/extractors/dfg.js.map +1 -0
  107. package/dist/core/extractors/exports.d.ts +14 -0
  108. package/dist/core/extractors/exports.js +80 -0
  109. package/dist/core/extractors/exports.js.map +1 -0
  110. package/dist/core/extractors/imports.d.ts +9 -0
  111. package/dist/core/extractors/imports.js +739 -0
  112. package/dist/core/extractors/imports.js.map +1 -0
  113. package/dist/core/extractors/index.d.ts +10 -0
  114. package/dist/core/extractors/index.js +11 -0
  115. package/dist/core/extractors/index.js.map +1 -0
  116. package/dist/core/extractors/meta.d.ts +10 -0
  117. package/dist/core/extractors/meta.js +109 -0
  118. package/dist/core/extractors/meta.js.map +1 -0
  119. package/dist/core/extractors/types.d.ts +10 -0
  120. package/dist/core/extractors/types.js +1479 -0
  121. package/dist/core/extractors/types.js.map +1 -0
  122. package/dist/core/index.d.ts +5 -0
  123. package/dist/core/index.js +8 -0
  124. package/dist/core/index.js.map +1 -0
  125. package/dist/core/parser.d.ts +84 -0
  126. package/dist/core/parser.js +250 -0
  127. package/dist/core/parser.js.map +1 -0
  128. package/dist/core-lib.d.ts +59 -0
  129. package/dist/core-lib.js +62 -0
  130. package/dist/core-lib.js.map +1 -0
  131. package/dist/index.d.ts +15 -0
  132. package/dist/index.js +20 -0
  133. package/dist/index.js.map +1 -0
  134. package/dist/languages/index.d.ts +11 -0
  135. package/dist/languages/index.js +14 -0
  136. package/dist/languages/index.js.map +1 -0
  137. package/dist/languages/plugins/base.d.ts +44 -0
  138. package/dist/languages/plugins/base.js +82 -0
  139. package/dist/languages/plugins/base.js.map +1 -0
  140. package/dist/languages/plugins/index.d.ts +14 -0
  141. package/dist/languages/plugins/index.js +25 -0
  142. package/dist/languages/plugins/index.js.map +1 -0
  143. package/dist/languages/plugins/java.d.ts +49 -0
  144. package/dist/languages/plugins/java.js +402 -0
  145. package/dist/languages/plugins/java.js.map +1 -0
  146. package/dist/languages/plugins/javascript.d.ts +48 -0
  147. package/dist/languages/plugins/javascript.js +445 -0
  148. package/dist/languages/plugins/javascript.js.map +1 -0
  149. package/dist/languages/plugins/python.d.ts +47 -0
  150. package/dist/languages/plugins/python.js +480 -0
  151. package/dist/languages/plugins/python.js.map +1 -0
  152. package/dist/languages/plugins/rust.d.ts +47 -0
  153. package/dist/languages/plugins/rust.js +405 -0
  154. package/dist/languages/plugins/rust.js.map +1 -0
  155. package/dist/languages/registry.d.ts +30 -0
  156. package/dist/languages/registry.js +80 -0
  157. package/dist/languages/registry.js.map +1 -0
  158. package/dist/languages/types.d.ts +184 -0
  159. package/dist/languages/types.js +8 -0
  160. package/dist/languages/types.js.map +1 -0
  161. package/dist/resolution/cross-file.d.ts +146 -0
  162. package/dist/resolution/cross-file.js +439 -0
  163. package/dist/resolution/cross-file.js.map +1 -0
  164. package/dist/resolution/index.d.ts +12 -0
  165. package/dist/resolution/index.js +10 -0
  166. package/dist/resolution/index.js.map +1 -0
  167. package/dist/resolution/symbol-table.d.ts +136 -0
  168. package/dist/resolution/symbol-table.js +336 -0
  169. package/dist/resolution/symbol-table.js.map +1 -0
  170. package/dist/resolution/type-hierarchy.d.ts +124 -0
  171. package/dist/resolution/type-hierarchy.js +515 -0
  172. package/dist/resolution/type-hierarchy.js.map +1 -0
  173. package/dist/types/config.d.ts +45 -0
  174. package/dist/types/config.js +5 -0
  175. package/dist/types/config.js.map +1 -0
  176. package/dist/types/index.d.ts +392 -0
  177. package/dist/types/index.js +7 -0
  178. package/dist/types/index.js.map +1 -0
  179. package/dist/utils/logger.d.ts +85 -0
  180. package/dist/utils/logger.js +198 -0
  181. package/dist/utils/logger.js.map +1 -0
  182. package/dist/wasm/tree-sitter-java.wasm +0 -0
  183. package/dist/wasm/tree-sitter-javascript.wasm +0 -0
  184. package/dist/wasm/tree-sitter-python.wasm +0 -0
  185. package/dist/wasm/tree-sitter-rust.wasm +0 -0
  186. package/dist/wasm/web-tree-sitter.wasm +0 -0
  187. package/docs/SPEC.md +1021 -0
  188. package/examples/browser-example.html +610 -0
  189. package/examples/node-example.ts +215 -0
  190. package/package.json +107 -0
  191. package/wasm/tree-sitter-java.wasm +0 -0
  192. package/wasm/tree-sitter-javascript.wasm +0 -0
  193. package/wasm/tree-sitter-python.wasm +0 -0
  194. 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