driftdetect-core 0.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 (221) hide show
  1. package/dist/analyzers/ast-analyzer.d.ts +251 -0
  2. package/dist/analyzers/ast-analyzer.d.ts.map +1 -0
  3. package/dist/analyzers/ast-analyzer.js +548 -0
  4. package/dist/analyzers/ast-analyzer.js.map +1 -0
  5. package/dist/analyzers/flow-analyzer.d.ts +241 -0
  6. package/dist/analyzers/flow-analyzer.d.ts.map +1 -0
  7. package/dist/analyzers/flow-analyzer.js +1219 -0
  8. package/dist/analyzers/flow-analyzer.js.map +1 -0
  9. package/dist/analyzers/index.d.ts +18 -0
  10. package/dist/analyzers/index.d.ts.map +1 -0
  11. package/dist/analyzers/index.js +19 -0
  12. package/dist/analyzers/index.js.map +1 -0
  13. package/dist/analyzers/semantic-analyzer.d.ts +252 -0
  14. package/dist/analyzers/semantic-analyzer.d.ts.map +1 -0
  15. package/dist/analyzers/semantic-analyzer.js +1182 -0
  16. package/dist/analyzers/semantic-analyzer.js.map +1 -0
  17. package/dist/analyzers/type-analyzer.d.ts +289 -0
  18. package/dist/analyzers/type-analyzer.d.ts.map +1 -0
  19. package/dist/analyzers/type-analyzer.js +1269 -0
  20. package/dist/analyzers/type-analyzer.js.map +1 -0
  21. package/dist/analyzers/types.d.ts +537 -0
  22. package/dist/analyzers/types.d.ts.map +1 -0
  23. package/dist/analyzers/types.js +11 -0
  24. package/dist/analyzers/types.js.map +1 -0
  25. package/dist/config/config-loader.d.ts +166 -0
  26. package/dist/config/config-loader.d.ts.map +1 -0
  27. package/dist/config/config-loader.js +429 -0
  28. package/dist/config/config-loader.js.map +1 -0
  29. package/dist/config/config-validator.d.ts +204 -0
  30. package/dist/config/config-validator.d.ts.map +1 -0
  31. package/dist/config/config-validator.js +632 -0
  32. package/dist/config/config-validator.js.map +1 -0
  33. package/dist/config/defaults.d.ts +8 -0
  34. package/dist/config/defaults.d.ts.map +1 -0
  35. package/dist/config/defaults.js +26 -0
  36. package/dist/config/defaults.js.map +1 -0
  37. package/dist/config/index.d.ts +10 -0
  38. package/dist/config/index.d.ts.map +1 -0
  39. package/dist/config/index.js +10 -0
  40. package/dist/config/index.js.map +1 -0
  41. package/dist/config/types.d.ts +47 -0
  42. package/dist/config/types.d.ts.map +1 -0
  43. package/dist/config/types.js +7 -0
  44. package/dist/config/types.js.map +1 -0
  45. package/dist/index.d.ts +37 -0
  46. package/dist/index.d.ts.map +1 -0
  47. package/dist/index.js +39 -0
  48. package/dist/index.js.map +1 -0
  49. package/dist/manifest/exporter.d.ts +21 -0
  50. package/dist/manifest/exporter.d.ts.map +1 -0
  51. package/dist/manifest/exporter.js +339 -0
  52. package/dist/manifest/exporter.js.map +1 -0
  53. package/dist/manifest/index.d.ts +14 -0
  54. package/dist/manifest/index.d.ts.map +1 -0
  55. package/dist/manifest/index.js +15 -0
  56. package/dist/manifest/index.js.map +1 -0
  57. package/dist/manifest/manifest-store.d.ts +111 -0
  58. package/dist/manifest/manifest-store.d.ts.map +1 -0
  59. package/dist/manifest/manifest-store.js +418 -0
  60. package/dist/manifest/manifest-store.js.map +1 -0
  61. package/dist/manifest/types.d.ts +238 -0
  62. package/dist/manifest/types.d.ts.map +1 -0
  63. package/dist/manifest/types.js +11 -0
  64. package/dist/manifest/types.js.map +1 -0
  65. package/dist/matcher/confidence-scorer.d.ts +188 -0
  66. package/dist/matcher/confidence-scorer.d.ts.map +1 -0
  67. package/dist/matcher/confidence-scorer.js +302 -0
  68. package/dist/matcher/confidence-scorer.js.map +1 -0
  69. package/dist/matcher/index.d.ts +24 -0
  70. package/dist/matcher/index.d.ts.map +1 -0
  71. package/dist/matcher/index.js +26 -0
  72. package/dist/matcher/index.js.map +1 -0
  73. package/dist/matcher/outlier-detector.d.ts +252 -0
  74. package/dist/matcher/outlier-detector.d.ts.map +1 -0
  75. package/dist/matcher/outlier-detector.js +544 -0
  76. package/dist/matcher/outlier-detector.js.map +1 -0
  77. package/dist/matcher/pattern-matcher.d.ts +169 -0
  78. package/dist/matcher/pattern-matcher.d.ts.map +1 -0
  79. package/dist/matcher/pattern-matcher.js +692 -0
  80. package/dist/matcher/pattern-matcher.js.map +1 -0
  81. package/dist/matcher/types.d.ts +476 -0
  82. package/dist/matcher/types.d.ts.map +1 -0
  83. package/dist/matcher/types.js +36 -0
  84. package/dist/matcher/types.js.map +1 -0
  85. package/dist/parsers/base-parser.d.ts +282 -0
  86. package/dist/parsers/base-parser.d.ts.map +1 -0
  87. package/dist/parsers/base-parser.js +421 -0
  88. package/dist/parsers/base-parser.js.map +1 -0
  89. package/dist/parsers/css-parser.d.ts +225 -0
  90. package/dist/parsers/css-parser.d.ts.map +1 -0
  91. package/dist/parsers/css-parser.js +477 -0
  92. package/dist/parsers/css-parser.js.map +1 -0
  93. package/dist/parsers/index.d.ts +15 -0
  94. package/dist/parsers/index.d.ts.map +1 -0
  95. package/dist/parsers/index.js +15 -0
  96. package/dist/parsers/index.js.map +1 -0
  97. package/dist/parsers/json-parser.d.ts +219 -0
  98. package/dist/parsers/json-parser.d.ts.map +1 -0
  99. package/dist/parsers/json-parser.js +602 -0
  100. package/dist/parsers/json-parser.js.map +1 -0
  101. package/dist/parsers/markdown-parser.d.ts +276 -0
  102. package/dist/parsers/markdown-parser.d.ts.map +1 -0
  103. package/dist/parsers/markdown-parser.js +731 -0
  104. package/dist/parsers/markdown-parser.js.map +1 -0
  105. package/dist/parsers/parser-manager.d.ts +294 -0
  106. package/dist/parsers/parser-manager.d.ts.map +1 -0
  107. package/dist/parsers/parser-manager.js +738 -0
  108. package/dist/parsers/parser-manager.js.map +1 -0
  109. package/dist/parsers/python-parser.d.ts +204 -0
  110. package/dist/parsers/python-parser.d.ts.map +1 -0
  111. package/dist/parsers/python-parser.js +517 -0
  112. package/dist/parsers/python-parser.js.map +1 -0
  113. package/dist/parsers/types.d.ts +43 -0
  114. package/dist/parsers/types.d.ts.map +1 -0
  115. package/dist/parsers/types.js +7 -0
  116. package/dist/parsers/types.js.map +1 -0
  117. package/dist/parsers/typescript-parser.d.ts +264 -0
  118. package/dist/parsers/typescript-parser.d.ts.map +1 -0
  119. package/dist/parsers/typescript-parser.js +658 -0
  120. package/dist/parsers/typescript-parser.js.map +1 -0
  121. package/dist/rules/evaluator.d.ts +305 -0
  122. package/dist/rules/evaluator.d.ts.map +1 -0
  123. package/dist/rules/evaluator.js +579 -0
  124. package/dist/rules/evaluator.js.map +1 -0
  125. package/dist/rules/index.d.ts +13 -0
  126. package/dist/rules/index.d.ts.map +1 -0
  127. package/dist/rules/index.js +13 -0
  128. package/dist/rules/index.js.map +1 -0
  129. package/dist/rules/quick-fix-generator.d.ts +334 -0
  130. package/dist/rules/quick-fix-generator.d.ts.map +1 -0
  131. package/dist/rules/quick-fix-generator.js +1075 -0
  132. package/dist/rules/quick-fix-generator.js.map +1 -0
  133. package/dist/rules/rule-engine.d.ts +241 -0
  134. package/dist/rules/rule-engine.d.ts.map +1 -0
  135. package/dist/rules/rule-engine.js +585 -0
  136. package/dist/rules/rule-engine.js.map +1 -0
  137. package/dist/rules/severity-manager.d.ts +394 -0
  138. package/dist/rules/severity-manager.d.ts.map +1 -0
  139. package/dist/rules/severity-manager.js +619 -0
  140. package/dist/rules/severity-manager.js.map +1 -0
  141. package/dist/rules/types.d.ts +370 -0
  142. package/dist/rules/types.d.ts.map +1 -0
  143. package/dist/rules/types.js +133 -0
  144. package/dist/rules/types.js.map +1 -0
  145. package/dist/rules/variant-manager.d.ts +388 -0
  146. package/dist/rules/variant-manager.d.ts.map +1 -0
  147. package/dist/rules/variant-manager.js +777 -0
  148. package/dist/rules/variant-manager.js.map +1 -0
  149. package/dist/scanner/change-detector.d.ts +164 -0
  150. package/dist/scanner/change-detector.d.ts.map +1 -0
  151. package/dist/scanner/change-detector.js +263 -0
  152. package/dist/scanner/change-detector.js.map +1 -0
  153. package/dist/scanner/dependency-graph.d.ts +270 -0
  154. package/dist/scanner/dependency-graph.d.ts.map +1 -0
  155. package/dist/scanner/dependency-graph.js +436 -0
  156. package/dist/scanner/dependency-graph.js.map +1 -0
  157. package/dist/scanner/file-walker.d.ts +127 -0
  158. package/dist/scanner/file-walker.d.ts.map +1 -0
  159. package/dist/scanner/file-walker.js +526 -0
  160. package/dist/scanner/file-walker.js.map +1 -0
  161. package/dist/scanner/index.d.ts +12 -0
  162. package/dist/scanner/index.d.ts.map +1 -0
  163. package/dist/scanner/index.js +12 -0
  164. package/dist/scanner/index.js.map +1 -0
  165. package/dist/scanner/types.d.ts +218 -0
  166. package/dist/scanner/types.d.ts.map +1 -0
  167. package/dist/scanner/types.js +10 -0
  168. package/dist/scanner/types.js.map +1 -0
  169. package/dist/scanner/worker-pool.d.ts +317 -0
  170. package/dist/scanner/worker-pool.d.ts.map +1 -0
  171. package/dist/scanner/worker-pool.js +571 -0
  172. package/dist/scanner/worker-pool.js.map +1 -0
  173. package/dist/store/cache-manager.d.ts +179 -0
  174. package/dist/store/cache-manager.d.ts.map +1 -0
  175. package/dist/store/cache-manager.js +391 -0
  176. package/dist/store/cache-manager.js.map +1 -0
  177. package/dist/store/history-store.d.ts +314 -0
  178. package/dist/store/history-store.d.ts.map +1 -0
  179. package/dist/store/history-store.js +707 -0
  180. package/dist/store/history-store.js.map +1 -0
  181. package/dist/store/index.d.ts +20 -0
  182. package/dist/store/index.d.ts.map +1 -0
  183. package/dist/store/index.js +26 -0
  184. package/dist/store/index.js.map +1 -0
  185. package/dist/store/lock-file-manager.d.ts +202 -0
  186. package/dist/store/lock-file-manager.d.ts.map +1 -0
  187. package/dist/store/lock-file-manager.js +475 -0
  188. package/dist/store/lock-file-manager.js.map +1 -0
  189. package/dist/store/pattern-store.d.ts +289 -0
  190. package/dist/store/pattern-store.d.ts.map +1 -0
  191. package/dist/store/pattern-store.js +936 -0
  192. package/dist/store/pattern-store.js.map +1 -0
  193. package/dist/store/schema-validator.d.ts +159 -0
  194. package/dist/store/schema-validator.d.ts.map +1 -0
  195. package/dist/store/schema-validator.js +1096 -0
  196. package/dist/store/schema-validator.js.map +1 -0
  197. package/dist/store/types.d.ts +585 -0
  198. package/dist/store/types.d.ts.map +1 -0
  199. package/dist/store/types.js +82 -0
  200. package/dist/store/types.js.map +1 -0
  201. package/dist/types/analysis.d.ts +19 -0
  202. package/dist/types/analysis.d.ts.map +1 -0
  203. package/dist/types/analysis.js +5 -0
  204. package/dist/types/analysis.js.map +1 -0
  205. package/dist/types/common.d.ts +7 -0
  206. package/dist/types/common.d.ts.map +1 -0
  207. package/dist/types/common.js +5 -0
  208. package/dist/types/common.js.map +1 -0
  209. package/dist/types/index.d.ts +12 -0
  210. package/dist/types/index.d.ts.map +1 -0
  211. package/dist/types/index.js +10 -0
  212. package/dist/types/index.js.map +1 -0
  213. package/dist/types/patterns.d.ts +40 -0
  214. package/dist/types/patterns.d.ts.map +1 -0
  215. package/dist/types/patterns.js +7 -0
  216. package/dist/types/patterns.js.map +1 -0
  217. package/dist/types/violations.d.ts +7 -0
  218. package/dist/types/violations.d.ts.map +1 -0
  219. package/dist/types/violations.js +7 -0
  220. package/dist/types/violations.js.map +1 -0
  221. package/package.json +46 -0
@@ -0,0 +1,1182 @@
1
+ /**
2
+ * Semantic Analyzer - Symbol resolution and scope analysis
3
+ *
4
+ * Provides symbol resolution, scope analysis, symbol table construction,
5
+ * detection of unresolved references, and detection of shadowed variables.
6
+ * Supports TypeScript/JavaScript scope rules.
7
+ *
8
+ * @requirements 3.5 - Parser SHALL provide a unified AST query interface across all languages
9
+ */
10
+ /**
11
+ * Built-in global symbols for JavaScript/TypeScript
12
+ */
13
+ const BUILTIN_GLOBALS = new Set([
14
+ // Global objects
15
+ 'globalThis', 'window', 'self', 'global',
16
+ // Console
17
+ 'console',
18
+ // Timers
19
+ 'setTimeout', 'clearTimeout', 'setInterval', 'clearInterval',
20
+ 'setImmediate', 'clearImmediate',
21
+ // Encoding
22
+ 'atob', 'btoa',
23
+ // Fetch API
24
+ 'fetch', 'Request', 'Response', 'Headers',
25
+ // URL
26
+ 'URL', 'URLSearchParams',
27
+ // Events
28
+ 'Event', 'CustomEvent', 'EventTarget',
29
+ // DOM (browser)
30
+ 'document', 'navigator', 'location', 'history',
31
+ // Node.js
32
+ 'process', 'Buffer', '__dirname', '__filename', 'module', 'exports', 'require',
33
+ // Built-in constructors
34
+ 'Object', 'Array', 'String', 'Number', 'Boolean', 'Symbol', 'BigInt',
35
+ 'Function', 'Date', 'RegExp', 'Error', 'TypeError', 'RangeError',
36
+ 'SyntaxError', 'ReferenceError', 'EvalError', 'URIError',
37
+ 'Map', 'Set', 'WeakMap', 'WeakSet',
38
+ 'Promise', 'Proxy', 'Reflect',
39
+ 'ArrayBuffer', 'SharedArrayBuffer', 'DataView',
40
+ 'Int8Array', 'Uint8Array', 'Uint8ClampedArray',
41
+ 'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array',
42
+ 'Float32Array', 'Float64Array', 'BigInt64Array', 'BigUint64Array',
43
+ // Built-in functions
44
+ 'eval', 'isFinite', 'isNaN', 'parseFloat', 'parseInt',
45
+ 'decodeURI', 'decodeURIComponent', 'encodeURI', 'encodeURIComponent',
46
+ // Math and JSON
47
+ 'Math', 'JSON', 'Intl',
48
+ // TypeScript
49
+ 'undefined', 'NaN', 'Infinity',
50
+ ]);
51
+ /**
52
+ * Semantic Analyzer class for symbol resolution and scope analysis.
53
+ *
54
+ * Provides a unified interface for analyzing semantic information across
55
+ * TypeScript and JavaScript code.
56
+ *
57
+ * @requirements 3.5 - Unified AST query interface across all languages
58
+ */
59
+ export class SemanticAnalyzer {
60
+ /** Counter for generating unique scope IDs */
61
+ scopeIdCounter = 0;
62
+ /** All scopes discovered during analysis */
63
+ scopes = new Map();
64
+ /** Global symbol table */
65
+ symbolTable = new Map();
66
+ /** Unresolved references found during analysis */
67
+ unresolvedReferences = [];
68
+ /** Shadowed variables found during analysis */
69
+ shadowedVariables = [];
70
+ /** Current analysis options */
71
+ options = {};
72
+ /**
73
+ * Analyze an AST and produce semantic analysis results.
74
+ *
75
+ * @param ast - The AST to analyze
76
+ * @param options - Analysis options
77
+ * @returns Semantic analysis result
78
+ */
79
+ analyze(ast, options = {}) {
80
+ // Reset state for fresh analysis
81
+ this.reset();
82
+ this.options = options;
83
+ // Create global scope
84
+ const globalScope = this.createScope('global', null, {
85
+ start: ast.rootNode.startPosition,
86
+ end: ast.rootNode.endPosition,
87
+ });
88
+ // Add built-in symbols if requested
89
+ if (options.includeBuiltins !== false) {
90
+ this.addBuiltinSymbols(globalScope);
91
+ }
92
+ // First pass: collect all declarations and build scope tree
93
+ this.collectDeclarations(ast.rootNode, globalScope.id);
94
+ // Second pass: resolve references
95
+ if (options.trackReferences !== false) {
96
+ this.resolveReferences(ast.rootNode, globalScope.id);
97
+ }
98
+ // Build result
99
+ return this.buildResult();
100
+ }
101
+ /**
102
+ * Resolve a symbol reference to its definition.
103
+ *
104
+ * @param name - The symbol name to resolve
105
+ * @param scopeId - The scope to start resolution from
106
+ * @returns The resolved symbol info, or null if not found
107
+ */
108
+ resolveSymbol(name, scopeId) {
109
+ let currentScopeId = scopeId;
110
+ while (currentScopeId !== null) {
111
+ const scope = this.scopes.get(currentScopeId);
112
+ if (!scope)
113
+ break;
114
+ const symbol = scope.symbols.get(name);
115
+ if (symbol) {
116
+ return symbol;
117
+ }
118
+ currentScopeId = scope.parentId;
119
+ }
120
+ return null;
121
+ }
122
+ /**
123
+ * Get all symbols visible from a given scope.
124
+ *
125
+ * @param scopeId - The scope to get visible symbols from
126
+ * @returns Map of symbol names to symbol info
127
+ */
128
+ getVisibleSymbols(scopeId) {
129
+ const visible = new Map();
130
+ let currentScopeId = scopeId;
131
+ while (currentScopeId !== null) {
132
+ const scope = this.scopes.get(currentScopeId);
133
+ if (!scope)
134
+ break;
135
+ // Add symbols from this scope (don't override closer scopes)
136
+ for (const [name, symbol] of scope.symbols) {
137
+ if (!visible.has(name)) {
138
+ visible.set(name, symbol);
139
+ }
140
+ }
141
+ currentScopeId = scope.parentId;
142
+ }
143
+ return visible;
144
+ }
145
+ /**
146
+ * Get the scope containing a specific position.
147
+ *
148
+ * @param position - The position to find scope for
149
+ * @returns The most specific scope at that position, or null
150
+ */
151
+ getScopeAtPosition(position) {
152
+ let result = null;
153
+ for (const scope of this.scopes.values()) {
154
+ if (this.positionInRange(position, scope.location.start, scope.location.end)) {
155
+ // Keep the most specific (deepest) scope
156
+ if (!result || scope.depth > result.depth) {
157
+ result = scope;
158
+ }
159
+ }
160
+ }
161
+ return result ? this.toScopeInfo(result) : null;
162
+ }
163
+ // ============================================
164
+ // Private Helper Methods
165
+ // ============================================
166
+ /**
167
+ * Reset analyzer state for fresh analysis.
168
+ */
169
+ reset() {
170
+ this.scopeIdCounter = 0;
171
+ this.scopes.clear();
172
+ this.symbolTable.clear();
173
+ this.unresolvedReferences = [];
174
+ this.shadowedVariables = [];
175
+ this.options = {};
176
+ }
177
+ /**
178
+ * Create a new scope.
179
+ */
180
+ createScope(kind, parentId, location) {
181
+ const id = `scope_${this.scopeIdCounter++}`;
182
+ const parentScope = parentId ? this.scopes.get(parentId) : null;
183
+ const depth = parentScope ? parentScope.depth + 1 : 0;
184
+ const scope = {
185
+ id,
186
+ kind,
187
+ parentId,
188
+ childIds: [],
189
+ symbols: new Map(),
190
+ location,
191
+ depth,
192
+ };
193
+ this.scopes.set(id, scope);
194
+ // Add to parent's children
195
+ if (parentScope) {
196
+ parentScope.childIds.push(id);
197
+ }
198
+ return scope;
199
+ }
200
+ /**
201
+ * Add built-in global symbols.
202
+ */
203
+ addBuiltinSymbols(globalScope) {
204
+ for (const name of BUILTIN_GLOBALS) {
205
+ const symbol = this.createSymbol(name, 'variable', globalScope.id, {
206
+ start: { row: 0, column: 0 },
207
+ end: { row: 0, column: 0 },
208
+ });
209
+ symbol.visibility = 'public';
210
+ symbol.isExported = false;
211
+ symbol.isImported = false;
212
+ globalScope.symbols.set(name, symbol);
213
+ }
214
+ }
215
+ /**
216
+ * Create a new symbol.
217
+ */
218
+ createSymbol(name, kind, scopeId, location) {
219
+ return {
220
+ name,
221
+ kind,
222
+ location,
223
+ scopeId,
224
+ visibility: 'default',
225
+ isExported: false,
226
+ isImported: false,
227
+ references: [],
228
+ };
229
+ }
230
+ /**
231
+ * Add a symbol to a scope, checking for shadowing.
232
+ */
233
+ addSymbolToScope(symbol, scopeId) {
234
+ const scope = this.scopes.get(scopeId);
235
+ if (!scope)
236
+ return;
237
+ // Check for shadowing in parent scopes
238
+ if (this.options.detectShadowing !== false) {
239
+ const existingSymbol = this.resolveSymbol(symbol.name, scope.parentId || '');
240
+ if (existingSymbol && existingSymbol.scopeId !== scopeId) {
241
+ this.shadowedVariables.push({
242
+ name: symbol.name,
243
+ shadowLocation: symbol.location,
244
+ originalLocation: existingSymbol.location,
245
+ });
246
+ }
247
+ }
248
+ // Check for redeclaration in same scope (for let/const)
249
+ const existingInScope = scope.symbols.get(symbol.name);
250
+ if (existingInScope) {
251
+ // This is a redeclaration - could be an error depending on context
252
+ // For now, we just update the symbol
253
+ }
254
+ scope.symbols.set(symbol.name, symbol);
255
+ this.symbolTable.set(`${scopeId}:${symbol.name}`, symbol);
256
+ }
257
+ /**
258
+ * Collect declarations from AST nodes.
259
+ */
260
+ collectDeclarations(node, scopeId) {
261
+ const nodeType = node.type;
262
+ // Check max depth
263
+ const scope = this.scopes.get(scopeId);
264
+ if (scope && this.options.maxScopeDepth !== undefined &&
265
+ scope.depth >= this.options.maxScopeDepth) {
266
+ return;
267
+ }
268
+ // Handle different declaration types
269
+ switch (nodeType) {
270
+ case 'program':
271
+ case 'Program':
272
+ // Module scope (treat as global for now)
273
+ this.collectChildDeclarations(node, scopeId);
274
+ break;
275
+ case 'function_declaration':
276
+ case 'FunctionDeclaration':
277
+ this.collectFunctionDeclaration(node, scopeId);
278
+ break;
279
+ case 'arrow_function':
280
+ case 'ArrowFunctionExpression':
281
+ this.collectArrowFunction(node, scopeId);
282
+ break;
283
+ case 'method_definition':
284
+ case 'MethodDefinition':
285
+ this.collectMethodDefinition(node, scopeId);
286
+ break;
287
+ case 'class_declaration':
288
+ case 'ClassDeclaration':
289
+ this.collectClassDeclaration(node, scopeId);
290
+ break;
291
+ case 'variable_declaration':
292
+ case 'VariableDeclaration':
293
+ case 'lexical_declaration':
294
+ this.collectVariableDeclaration(node, scopeId);
295
+ break;
296
+ case 'import_statement':
297
+ case 'ImportDeclaration':
298
+ this.collectImportDeclaration(node, scopeId);
299
+ break;
300
+ case 'export_statement':
301
+ case 'ExportNamedDeclaration':
302
+ case 'ExportDefaultDeclaration':
303
+ this.collectExportDeclaration(node, scopeId);
304
+ break;
305
+ case 'interface_declaration':
306
+ case 'TSInterfaceDeclaration':
307
+ this.collectInterfaceDeclaration(node, scopeId);
308
+ break;
309
+ case 'type_alias_declaration':
310
+ case 'TSTypeAliasDeclaration':
311
+ this.collectTypeAliasDeclaration(node, scopeId);
312
+ break;
313
+ case 'enum_declaration':
314
+ case 'TSEnumDeclaration':
315
+ this.collectEnumDeclaration(node, scopeId);
316
+ break;
317
+ case 'statement_block':
318
+ case 'BlockStatement':
319
+ this.collectBlockStatement(node, scopeId);
320
+ break;
321
+ case 'for_statement':
322
+ case 'ForStatement':
323
+ case 'for_in_statement':
324
+ case 'ForInStatement':
325
+ case 'for_of_statement':
326
+ case 'ForOfStatement':
327
+ this.collectForStatement(node, scopeId);
328
+ break;
329
+ case 'if_statement':
330
+ case 'IfStatement':
331
+ this.collectIfStatement(node, scopeId);
332
+ break;
333
+ case 'switch_statement':
334
+ case 'SwitchStatement':
335
+ this.collectSwitchStatement(node, scopeId);
336
+ break;
337
+ case 'try_statement':
338
+ case 'TryStatement':
339
+ this.collectTryStatement(node, scopeId);
340
+ break;
341
+ case 'catch_clause':
342
+ case 'CatchClause':
343
+ this.collectCatchClause(node, scopeId);
344
+ break;
345
+ default:
346
+ // Recurse into children for other node types
347
+ this.collectChildDeclarations(node, scopeId);
348
+ break;
349
+ }
350
+ }
351
+ /**
352
+ * Collect declarations from child nodes.
353
+ */
354
+ collectChildDeclarations(node, scopeId) {
355
+ for (const child of node.children) {
356
+ this.collectDeclarations(child, scopeId);
357
+ }
358
+ }
359
+ /**
360
+ * Collect function declaration.
361
+ */
362
+ collectFunctionDeclaration(node, scopeId) {
363
+ const nameNode = this.findChildByType(node, 'identifier') ||
364
+ this.findChildByType(node, 'Identifier');
365
+ const name = nameNode?.text || '<anonymous>';
366
+ // Add function symbol to current scope
367
+ const symbol = this.createSymbol(name, 'function', scopeId, {
368
+ start: node.startPosition,
369
+ end: node.endPosition,
370
+ });
371
+ symbol.parameters = this.extractParameters(node);
372
+ this.addSymbolToScope(symbol, scopeId);
373
+ // Create function scope
374
+ const funcScope = this.createScope('function', scopeId, {
375
+ start: node.startPosition,
376
+ end: node.endPosition,
377
+ });
378
+ // Add parameters to function scope
379
+ for (const param of symbol.parameters || []) {
380
+ const paramSymbol = this.createSymbol(param.name, 'parameter', funcScope.id, param.location);
381
+ this.addSymbolToScope(paramSymbol, funcScope.id);
382
+ }
383
+ // Process function body
384
+ const body = this.findChildByType(node, 'statement_block') ||
385
+ this.findChildByType(node, 'BlockStatement');
386
+ if (body) {
387
+ this.collectChildDeclarations(body, funcScope.id);
388
+ }
389
+ }
390
+ /**
391
+ * Collect arrow function.
392
+ */
393
+ collectArrowFunction(node, scopeId) {
394
+ // Create function scope
395
+ const funcScope = this.createScope('function', scopeId, {
396
+ start: node.startPosition,
397
+ end: node.endPosition,
398
+ });
399
+ // Extract and add parameters
400
+ const params = this.extractParameters(node);
401
+ for (const param of params) {
402
+ const paramSymbol = this.createSymbol(param.name, 'parameter', funcScope.id, param.location);
403
+ this.addSymbolToScope(paramSymbol, funcScope.id);
404
+ }
405
+ // Process body
406
+ const body = this.findChildByType(node, 'statement_block') ||
407
+ this.findChildByType(node, 'BlockStatement');
408
+ if (body) {
409
+ this.collectChildDeclarations(body, funcScope.id);
410
+ }
411
+ else {
412
+ // Expression body - still process for nested functions
413
+ this.collectChildDeclarations(node, funcScope.id);
414
+ }
415
+ }
416
+ /**
417
+ * Collect method definition.
418
+ */
419
+ collectMethodDefinition(node, scopeId) {
420
+ const nameNode = this.findChildByType(node, 'property_identifier') ||
421
+ this.findChildByType(node, 'Identifier');
422
+ const name = nameNode?.text || '<anonymous>';
423
+ // Add method symbol to class scope
424
+ const symbol = this.createSymbol(name, 'method', scopeId, {
425
+ start: node.startPosition,
426
+ end: node.endPosition,
427
+ });
428
+ symbol.visibility = this.extractVisibility(node);
429
+ symbol.parameters = this.extractParameters(node);
430
+ this.addSymbolToScope(symbol, scopeId);
431
+ // Create method scope
432
+ const methodScope = this.createScope('function', scopeId, {
433
+ start: node.startPosition,
434
+ end: node.endPosition,
435
+ });
436
+ // Add 'this' to method scope
437
+ const thisSymbol = this.createSymbol('this', 'variable', methodScope.id, {
438
+ start: node.startPosition,
439
+ end: node.startPosition,
440
+ });
441
+ this.addSymbolToScope(thisSymbol, methodScope.id);
442
+ // Add parameters to method scope
443
+ for (const param of symbol.parameters || []) {
444
+ const paramSymbol = this.createSymbol(param.name, 'parameter', methodScope.id, param.location);
445
+ this.addSymbolToScope(paramSymbol, methodScope.id);
446
+ }
447
+ // Process method body
448
+ const body = this.findChildByType(node, 'statement_block') ||
449
+ this.findChildByType(node, 'BlockStatement');
450
+ if (body) {
451
+ this.collectChildDeclarations(body, methodScope.id);
452
+ }
453
+ }
454
+ /**
455
+ * Collect class declaration.
456
+ */
457
+ collectClassDeclaration(node, scopeId) {
458
+ const nameNode = this.findChildByType(node, 'type_identifier') ||
459
+ this.findChildByType(node, 'Identifier');
460
+ const name = nameNode?.text || '<anonymous>';
461
+ // Add class symbol to current scope
462
+ const symbol = this.createSymbol(name, 'class', scopeId, {
463
+ start: node.startPosition,
464
+ end: node.endPosition,
465
+ });
466
+ this.addSymbolToScope(symbol, scopeId);
467
+ // Create class scope
468
+ const classScope = this.createScope('class', scopeId, {
469
+ start: node.startPosition,
470
+ end: node.endPosition,
471
+ });
472
+ // Process class body
473
+ const body = this.findChildByType(node, 'class_body') ||
474
+ this.findChildByType(node, 'ClassBody');
475
+ if (body) {
476
+ // Collect class members
477
+ for (const child of body.children) {
478
+ if (child.type === 'method_definition' || child.type === 'MethodDefinition') {
479
+ this.collectMethodDefinition(child, classScope.id);
480
+ }
481
+ else if (child.type === 'public_field_definition' ||
482
+ child.type === 'PropertyDefinition' ||
483
+ child.type === 'field_definition') {
484
+ this.collectFieldDefinition(child, classScope.id);
485
+ }
486
+ else {
487
+ this.collectDeclarations(child, classScope.id);
488
+ }
489
+ }
490
+ }
491
+ }
492
+ /**
493
+ * Collect field definition.
494
+ */
495
+ collectFieldDefinition(node, scopeId) {
496
+ const nameNode = this.findChildByType(node, 'property_identifier') ||
497
+ this.findChildByType(node, 'Identifier');
498
+ const name = nameNode?.text;
499
+ if (name) {
500
+ const symbol = this.createSymbol(name, 'property', scopeId, {
501
+ start: node.startPosition,
502
+ end: node.endPosition,
503
+ });
504
+ symbol.visibility = this.extractVisibility(node);
505
+ this.addSymbolToScope(symbol, scopeId);
506
+ }
507
+ }
508
+ /**
509
+ * Collect variable declaration.
510
+ */
511
+ collectVariableDeclaration(node, scopeId) {
512
+ // Find all variable declarators
513
+ for (const child of node.children) {
514
+ if (child.type === 'variable_declarator' || child.type === 'VariableDeclarator') {
515
+ this.collectVariableDeclarator(child, scopeId);
516
+ }
517
+ }
518
+ }
519
+ /**
520
+ * Collect variable declarator.
521
+ */
522
+ collectVariableDeclarator(node, scopeId) {
523
+ // Handle destructuring patterns
524
+ const pattern = node.children[0];
525
+ if (!pattern)
526
+ return;
527
+ if (pattern.type === 'identifier' || pattern.type === 'Identifier') {
528
+ const symbol = this.createSymbol(pattern.text, 'variable', scopeId, {
529
+ start: pattern.startPosition,
530
+ end: pattern.endPosition,
531
+ });
532
+ this.addSymbolToScope(symbol, scopeId);
533
+ }
534
+ else if (pattern.type === 'object_pattern' || pattern.type === 'ObjectPattern') {
535
+ this.collectObjectPattern(pattern, scopeId);
536
+ }
537
+ else if (pattern.type === 'array_pattern' || pattern.type === 'ArrayPattern') {
538
+ this.collectArrayPattern(pattern, scopeId);
539
+ }
540
+ // Process initializer for nested functions
541
+ const initializer = this.findChildByType(node, 'arrow_function') ||
542
+ this.findChildByType(node, 'ArrowFunctionExpression') ||
543
+ this.findChildByType(node, 'function') ||
544
+ this.findChildByType(node, 'FunctionExpression');
545
+ if (initializer) {
546
+ this.collectDeclarations(initializer, scopeId);
547
+ }
548
+ }
549
+ /**
550
+ * Collect object destructuring pattern.
551
+ */
552
+ collectObjectPattern(node, scopeId) {
553
+ for (const child of node.children) {
554
+ if (child.type === 'shorthand_property_identifier_pattern' ||
555
+ child.type === 'shorthand_property_identifier') {
556
+ const symbol = this.createSymbol(child.text, 'variable', scopeId, {
557
+ start: child.startPosition,
558
+ end: child.endPosition,
559
+ });
560
+ this.addSymbolToScope(symbol, scopeId);
561
+ }
562
+ else if (child.type === 'pair_pattern' || child.type === 'Property') {
563
+ // { key: value } pattern
564
+ const valueNode = child.children[child.children.length - 1];
565
+ if (valueNode?.type === 'identifier' || valueNode?.type === 'Identifier') {
566
+ const symbol = this.createSymbol(valueNode.text, 'variable', scopeId, {
567
+ start: valueNode.startPosition,
568
+ end: valueNode.endPosition,
569
+ });
570
+ this.addSymbolToScope(symbol, scopeId);
571
+ }
572
+ else if (valueNode) {
573
+ // Nested pattern
574
+ this.collectPattern(valueNode, scopeId);
575
+ }
576
+ }
577
+ }
578
+ }
579
+ /**
580
+ * Collect array destructuring pattern.
581
+ */
582
+ collectArrayPattern(node, scopeId) {
583
+ for (const child of node.children) {
584
+ if (child.type === 'identifier' || child.type === 'Identifier') {
585
+ const symbol = this.createSymbol(child.text, 'variable', scopeId, {
586
+ start: child.startPosition,
587
+ end: child.endPosition,
588
+ });
589
+ this.addSymbolToScope(symbol, scopeId);
590
+ }
591
+ else if (child.type === 'rest_pattern' || child.type === 'RestElement') {
592
+ const restId = this.findChildByType(child, 'identifier') ||
593
+ this.findChildByType(child, 'Identifier');
594
+ if (restId) {
595
+ const symbol = this.createSymbol(restId.text, 'variable', scopeId, {
596
+ start: restId.startPosition,
597
+ end: restId.endPosition,
598
+ });
599
+ this.addSymbolToScope(symbol, scopeId);
600
+ }
601
+ }
602
+ else {
603
+ // Nested pattern
604
+ this.collectPattern(child, scopeId);
605
+ }
606
+ }
607
+ }
608
+ /**
609
+ * Collect any pattern type.
610
+ */
611
+ collectPattern(node, scopeId) {
612
+ if (node.type === 'identifier' || node.type === 'Identifier') {
613
+ const symbol = this.createSymbol(node.text, 'variable', scopeId, {
614
+ start: node.startPosition,
615
+ end: node.endPosition,
616
+ });
617
+ this.addSymbolToScope(symbol, scopeId);
618
+ }
619
+ else if (node.type === 'object_pattern' || node.type === 'ObjectPattern') {
620
+ this.collectObjectPattern(node, scopeId);
621
+ }
622
+ else if (node.type === 'array_pattern' || node.type === 'ArrayPattern') {
623
+ this.collectArrayPattern(node, scopeId);
624
+ }
625
+ }
626
+ /**
627
+ * Collect import declaration.
628
+ */
629
+ collectImportDeclaration(node, scopeId) {
630
+ // Find import clause
631
+ const importClause = this.findChildByType(node, 'import_clause');
632
+ if (importClause) {
633
+ for (const child of importClause.children) {
634
+ if (child.type === 'identifier' || child.type === 'Identifier') {
635
+ // Default import
636
+ const symbol = this.createSymbol(child.text, 'variable', scopeId, {
637
+ start: child.startPosition,
638
+ end: child.endPosition,
639
+ });
640
+ symbol.isImported = true;
641
+ this.addSymbolToScope(symbol, scopeId);
642
+ }
643
+ else if (child.type === 'named_imports') {
644
+ // Named imports
645
+ for (const spec of child.children) {
646
+ if (spec.type === 'import_specifier') {
647
+ const localName = this.findChildByType(spec, 'identifier') ||
648
+ spec.children[spec.children.length - 1];
649
+ if (localName && (localName.type === 'identifier' || localName.type === 'Identifier')) {
650
+ const symbol = this.createSymbol(localName.text, 'variable', scopeId, {
651
+ start: localName.startPosition,
652
+ end: localName.endPosition,
653
+ });
654
+ symbol.isImported = true;
655
+ this.addSymbolToScope(symbol, scopeId);
656
+ }
657
+ }
658
+ }
659
+ }
660
+ else if (child.type === 'namespace_import') {
661
+ // Namespace import (import * as name)
662
+ const nameNode = this.findChildByType(child, 'identifier');
663
+ if (nameNode) {
664
+ const symbol = this.createSymbol(nameNode.text, 'namespace', scopeId, {
665
+ start: nameNode.startPosition,
666
+ end: nameNode.endPosition,
667
+ });
668
+ symbol.isImported = true;
669
+ this.addSymbolToScope(symbol, scopeId);
670
+ }
671
+ }
672
+ }
673
+ }
674
+ // Handle direct named imports (ImportDeclaration style)
675
+ for (const child of node.children) {
676
+ if (child.type === 'ImportSpecifier') {
677
+ const localNode = this.findChildByType(child, 'Identifier');
678
+ if (localNode) {
679
+ const symbol = this.createSymbol(localNode.text, 'variable', scopeId, {
680
+ start: localNode.startPosition,
681
+ end: localNode.endPosition,
682
+ });
683
+ symbol.isImported = true;
684
+ this.addSymbolToScope(symbol, scopeId);
685
+ }
686
+ }
687
+ }
688
+ }
689
+ /**
690
+ * Collect export declaration.
691
+ */
692
+ collectExportDeclaration(node, scopeId) {
693
+ // Process the declaration being exported
694
+ for (const child of node.children) {
695
+ if (child.type === 'function_declaration' || child.type === 'FunctionDeclaration') {
696
+ this.collectFunctionDeclaration(child, scopeId);
697
+ // Mark as exported
698
+ const nameNode = this.findChildByType(child, 'identifier') ||
699
+ this.findChildByType(child, 'Identifier');
700
+ if (nameNode) {
701
+ const scope = this.scopes.get(scopeId);
702
+ const symbol = scope?.symbols.get(nameNode.text);
703
+ if (symbol) {
704
+ symbol.isExported = true;
705
+ }
706
+ }
707
+ }
708
+ else if (child.type === 'class_declaration' || child.type === 'ClassDeclaration') {
709
+ this.collectClassDeclaration(child, scopeId);
710
+ const nameNode = this.findChildByType(child, 'type_identifier') ||
711
+ this.findChildByType(child, 'Identifier');
712
+ if (nameNode) {
713
+ const scope = this.scopes.get(scopeId);
714
+ const symbol = scope?.symbols.get(nameNode.text);
715
+ if (symbol) {
716
+ symbol.isExported = true;
717
+ }
718
+ }
719
+ }
720
+ else if (child.type === 'variable_declaration' || child.type === 'VariableDeclaration' ||
721
+ child.type === 'lexical_declaration') {
722
+ this.collectVariableDeclaration(child, scopeId);
723
+ // Mark all declared variables as exported
724
+ for (const declarator of child.children) {
725
+ if (declarator.type === 'variable_declarator' || declarator.type === 'VariableDeclarator') {
726
+ const idNode = declarator.children[0];
727
+ if (idNode && (idNode.type === 'identifier' || idNode.type === 'Identifier')) {
728
+ const scope = this.scopes.get(scopeId);
729
+ const symbol = scope?.symbols.get(idNode.text);
730
+ if (symbol) {
731
+ symbol.isExported = true;
732
+ }
733
+ }
734
+ }
735
+ }
736
+ }
737
+ else if (child.type === 'interface_declaration' || child.type === 'TSInterfaceDeclaration') {
738
+ this.collectInterfaceDeclaration(child, scopeId);
739
+ }
740
+ else if (child.type === 'type_alias_declaration' || child.type === 'TSTypeAliasDeclaration') {
741
+ this.collectTypeAliasDeclaration(child, scopeId);
742
+ }
743
+ }
744
+ }
745
+ /**
746
+ * Collect interface declaration.
747
+ */
748
+ collectInterfaceDeclaration(node, scopeId) {
749
+ const nameNode = this.findChildByType(node, 'type_identifier') ||
750
+ this.findChildByType(node, 'Identifier');
751
+ const name = nameNode?.text;
752
+ if (name) {
753
+ const symbol = this.createSymbol(name, 'interface', scopeId, {
754
+ start: node.startPosition,
755
+ end: node.endPosition,
756
+ });
757
+ this.addSymbolToScope(symbol, scopeId);
758
+ }
759
+ }
760
+ /**
761
+ * Collect type alias declaration.
762
+ */
763
+ collectTypeAliasDeclaration(node, scopeId) {
764
+ const nameNode = this.findChildByType(node, 'type_identifier') ||
765
+ this.findChildByType(node, 'Identifier');
766
+ const name = nameNode?.text;
767
+ if (name) {
768
+ const symbol = this.createSymbol(name, 'type', scopeId, {
769
+ start: node.startPosition,
770
+ end: node.endPosition,
771
+ });
772
+ this.addSymbolToScope(symbol, scopeId);
773
+ }
774
+ }
775
+ /**
776
+ * Collect enum declaration.
777
+ */
778
+ collectEnumDeclaration(node, scopeId) {
779
+ const nameNode = this.findChildByType(node, 'identifier') ||
780
+ this.findChildByType(node, 'Identifier');
781
+ const name = nameNode?.text;
782
+ if (name) {
783
+ const symbol = this.createSymbol(name, 'enum', scopeId, {
784
+ start: node.startPosition,
785
+ end: node.endPosition,
786
+ });
787
+ this.addSymbolToScope(symbol, scopeId);
788
+ // Collect enum members
789
+ const body = this.findChildByType(node, 'enum_body');
790
+ if (body) {
791
+ for (const child of body.children) {
792
+ if (child.type === 'enum_assignment' || child.type === 'property_identifier') {
793
+ const memberName = this.findChildByType(child, 'property_identifier') ||
794
+ this.findChildByType(child, 'Identifier');
795
+ if (memberName) {
796
+ // Create enum member symbol (accessible via the enum name)
797
+ const memberSymbol = this.createSymbol(memberName.text, 'enumMember', scopeId, {
798
+ start: memberName.startPosition,
799
+ end: memberName.endPosition,
800
+ });
801
+ this.addSymbolToScope(memberSymbol, scopeId);
802
+ }
803
+ }
804
+ }
805
+ }
806
+ }
807
+ }
808
+ /**
809
+ * Collect block statement.
810
+ */
811
+ collectBlockStatement(node, scopeId) {
812
+ // Create block scope
813
+ const blockScope = this.createScope('block', scopeId, {
814
+ start: node.startPosition,
815
+ end: node.endPosition,
816
+ });
817
+ this.collectChildDeclarations(node, blockScope.id);
818
+ }
819
+ /**
820
+ * Collect for statement.
821
+ */
822
+ collectForStatement(node, scopeId) {
823
+ // Create loop scope
824
+ const loopScope = this.createScope('loop', scopeId, {
825
+ start: node.startPosition,
826
+ end: node.endPosition,
827
+ });
828
+ // Collect loop variable declarations
829
+ for (const child of node.children) {
830
+ if (child.type === 'variable_declaration' || child.type === 'VariableDeclaration' ||
831
+ child.type === 'lexical_declaration') {
832
+ this.collectVariableDeclaration(child, loopScope.id);
833
+ }
834
+ else if (child.type === 'statement_block' || child.type === 'BlockStatement') {
835
+ this.collectChildDeclarations(child, loopScope.id);
836
+ }
837
+ else {
838
+ this.collectDeclarations(child, loopScope.id);
839
+ }
840
+ }
841
+ }
842
+ /**
843
+ * Collect if statement.
844
+ */
845
+ collectIfStatement(node, scopeId) {
846
+ for (const child of node.children) {
847
+ if (child.type === 'statement_block' || child.type === 'BlockStatement') {
848
+ // Create conditional scope
849
+ const condScope = this.createScope('conditional', scopeId, {
850
+ start: child.startPosition,
851
+ end: child.endPosition,
852
+ });
853
+ this.collectChildDeclarations(child, condScope.id);
854
+ }
855
+ else if (child.type === 'else_clause') {
856
+ this.collectDeclarations(child, scopeId);
857
+ }
858
+ }
859
+ }
860
+ /**
861
+ * Collect switch statement.
862
+ */
863
+ collectSwitchStatement(node, scopeId) {
864
+ // Create switch scope
865
+ const switchScope = this.createScope('switch', scopeId, {
866
+ start: node.startPosition,
867
+ end: node.endPosition,
868
+ });
869
+ const body = this.findChildByType(node, 'switch_body');
870
+ if (body) {
871
+ this.collectChildDeclarations(body, switchScope.id);
872
+ }
873
+ }
874
+ /**
875
+ * Collect try statement.
876
+ */
877
+ collectTryStatement(node, scopeId) {
878
+ for (const child of node.children) {
879
+ if (child.type === 'statement_block' || child.type === 'BlockStatement') {
880
+ const tryScope = this.createScope('block', scopeId, {
881
+ start: child.startPosition,
882
+ end: child.endPosition,
883
+ });
884
+ this.collectChildDeclarations(child, tryScope.id);
885
+ }
886
+ else if (child.type === 'catch_clause' || child.type === 'CatchClause') {
887
+ this.collectCatchClause(child, scopeId);
888
+ }
889
+ else if (child.type === 'finally_clause') {
890
+ const finallyBlock = this.findChildByType(child, 'statement_block') ||
891
+ this.findChildByType(child, 'BlockStatement');
892
+ if (finallyBlock) {
893
+ const finallyScope = this.createScope('block', scopeId, {
894
+ start: finallyBlock.startPosition,
895
+ end: finallyBlock.endPosition,
896
+ });
897
+ this.collectChildDeclarations(finallyBlock, finallyScope.id);
898
+ }
899
+ }
900
+ }
901
+ }
902
+ /**
903
+ * Collect catch clause.
904
+ */
905
+ collectCatchClause(node, scopeId) {
906
+ // Create catch scope
907
+ const catchScope = this.createScope('catch', scopeId, {
908
+ start: node.startPosition,
909
+ end: node.endPosition,
910
+ });
911
+ // Add catch parameter
912
+ const param = this.findChildByType(node, 'identifier') ||
913
+ this.findChildByType(node, 'Identifier');
914
+ if (param) {
915
+ const symbol = this.createSymbol(param.text, 'parameter', catchScope.id, {
916
+ start: param.startPosition,
917
+ end: param.endPosition,
918
+ });
919
+ this.addSymbolToScope(symbol, catchScope.id);
920
+ }
921
+ // Process catch body
922
+ const body = this.findChildByType(node, 'statement_block') ||
923
+ this.findChildByType(node, 'BlockStatement');
924
+ if (body) {
925
+ this.collectChildDeclarations(body, catchScope.id);
926
+ }
927
+ }
928
+ /**
929
+ * Resolve references in AST nodes.
930
+ */
931
+ resolveReferences(node, scopeId) {
932
+ const nodeType = node.type;
933
+ // Update scope for scope-creating nodes
934
+ let currentScopeId = scopeId;
935
+ if (this.isNewScopeNode(node)) {
936
+ const matchingScope = this.findScopeForNode(node);
937
+ if (matchingScope) {
938
+ currentScopeId = matchingScope.id;
939
+ }
940
+ }
941
+ // Check for identifier references
942
+ if (nodeType === 'identifier' || nodeType === 'Identifier') {
943
+ this.resolveIdentifierReference(node, currentScopeId);
944
+ }
945
+ // Recurse into children
946
+ for (const child of node.children) {
947
+ this.resolveReferences(child, currentScopeId);
948
+ }
949
+ }
950
+ /**
951
+ * Check if a node creates a new scope.
952
+ */
953
+ isNewScopeNode(node) {
954
+ const scopeCreatingTypes = new Set([
955
+ 'function_declaration', 'FunctionDeclaration',
956
+ 'arrow_function', 'ArrowFunctionExpression',
957
+ 'method_definition', 'MethodDefinition',
958
+ 'class_declaration', 'ClassDeclaration',
959
+ 'statement_block', 'BlockStatement',
960
+ 'for_statement', 'ForStatement',
961
+ 'for_in_statement', 'ForInStatement',
962
+ 'for_of_statement', 'ForOfStatement',
963
+ 'switch_statement', 'SwitchStatement',
964
+ 'catch_clause', 'CatchClause',
965
+ ]);
966
+ return scopeCreatingTypes.has(node.type);
967
+ }
968
+ /**
969
+ * Find the scope that matches a node's location.
970
+ */
971
+ findScopeForNode(node) {
972
+ for (const scope of this.scopes.values()) {
973
+ if (scope.location.start.row === node.startPosition.row &&
974
+ scope.location.start.column === node.startPosition.column &&
975
+ scope.location.end.row === node.endPosition.row &&
976
+ scope.location.end.column === node.endPosition.column) {
977
+ return scope;
978
+ }
979
+ }
980
+ return null;
981
+ }
982
+ /**
983
+ * Resolve an identifier reference.
984
+ */
985
+ resolveIdentifierReference(node, scopeId) {
986
+ const name = node.text;
987
+ // Skip if this is a declaration (handled separately)
988
+ if (this.isDeclarationContext(node)) {
989
+ return;
990
+ }
991
+ // Skip property access (obj.prop - prop is not a reference)
992
+ if (this.isPropertyAccess(node)) {
993
+ return;
994
+ }
995
+ // Try to resolve the symbol
996
+ const symbol = this.resolveSymbol(name, scopeId);
997
+ if (symbol) {
998
+ // Add reference to the symbol
999
+ const reference = {
1000
+ location: {
1001
+ start: node.startPosition,
1002
+ end: node.endPosition,
1003
+ },
1004
+ kind: this.determineReferenceKind(node),
1005
+ isWrite: this.isWriteReference(node),
1006
+ };
1007
+ symbol.references.push(reference);
1008
+ }
1009
+ else {
1010
+ // Unresolved reference
1011
+ this.unresolvedReferences.push({
1012
+ location: {
1013
+ start: node.startPosition,
1014
+ end: node.endPosition,
1015
+ },
1016
+ kind: 'read',
1017
+ isWrite: false,
1018
+ });
1019
+ }
1020
+ }
1021
+ /**
1022
+ * Check if an identifier is in a declaration context.
1023
+ */
1024
+ isDeclarationContext(_node) {
1025
+ // This is a simplified check - in a real implementation,
1026
+ // we'd need to track parent nodes during traversal
1027
+ return false;
1028
+ }
1029
+ /**
1030
+ * Check if an identifier is a property access.
1031
+ */
1032
+ isPropertyAccess(_node) {
1033
+ // This is a simplified check
1034
+ return false;
1035
+ }
1036
+ /**
1037
+ * Determine the kind of reference.
1038
+ */
1039
+ determineReferenceKind(_node) {
1040
+ // Simplified - would need parent context for accurate determination
1041
+ return 'read';
1042
+ }
1043
+ /**
1044
+ * Check if a reference is a write (assignment).
1045
+ */
1046
+ isWriteReference(_node) {
1047
+ // Simplified - would need parent context for accurate determination
1048
+ return false;
1049
+ }
1050
+ /**
1051
+ * Extract parameters from a function node.
1052
+ */
1053
+ extractParameters(node) {
1054
+ const params = [];
1055
+ const paramsNode = this.findChildByType(node, 'formal_parameters') ||
1056
+ this.findChildByType(node, 'parameters');
1057
+ if (!paramsNode)
1058
+ return params;
1059
+ for (const child of paramsNode.children) {
1060
+ if (child.type === 'required_parameter' || child.type === 'optional_parameter' ||
1061
+ child.type === 'identifier' || child.type === 'Identifier') {
1062
+ const nameNode = child.type === 'identifier' || child.type === 'Identifier'
1063
+ ? child
1064
+ : this.findChildByType(child, 'identifier') || this.findChildByType(child, 'Identifier');
1065
+ if (nameNode) {
1066
+ params.push({
1067
+ name: nameNode.text,
1068
+ isOptional: child.type === 'optional_parameter',
1069
+ isRest: false,
1070
+ location: {
1071
+ start: nameNode.startPosition,
1072
+ end: nameNode.endPosition,
1073
+ },
1074
+ });
1075
+ }
1076
+ }
1077
+ else if (child.type === 'rest_pattern' || child.type === 'RestElement') {
1078
+ const nameNode = this.findChildByType(child, 'identifier') ||
1079
+ this.findChildByType(child, 'Identifier');
1080
+ if (nameNode) {
1081
+ params.push({
1082
+ name: nameNode.text,
1083
+ isOptional: false,
1084
+ isRest: true,
1085
+ location: {
1086
+ start: nameNode.startPosition,
1087
+ end: nameNode.endPosition,
1088
+ },
1089
+ });
1090
+ }
1091
+ }
1092
+ }
1093
+ return params;
1094
+ }
1095
+ /**
1096
+ * Extract visibility modifier from a node.
1097
+ */
1098
+ extractVisibility(node) {
1099
+ for (const child of node.children) {
1100
+ if (child.type === 'accessibility_modifier' || child.text === 'public') {
1101
+ return 'public';
1102
+ }
1103
+ else if (child.text === 'private') {
1104
+ return 'private';
1105
+ }
1106
+ else if (child.text === 'protected') {
1107
+ return 'protected';
1108
+ }
1109
+ }
1110
+ return 'default';
1111
+ }
1112
+ /**
1113
+ * Find a child node by type.
1114
+ */
1115
+ findChildByType(node, type) {
1116
+ for (const child of node.children) {
1117
+ if (child.type === type) {
1118
+ return child;
1119
+ }
1120
+ }
1121
+ return null;
1122
+ }
1123
+ /**
1124
+ * Check if a position is within a range.
1125
+ */
1126
+ positionInRange(position, start, end) {
1127
+ // Check if position is after start
1128
+ if (position.row < start.row)
1129
+ return false;
1130
+ if (position.row === start.row && position.column < start.column)
1131
+ return false;
1132
+ // Check if position is before end
1133
+ if (position.row > end.row)
1134
+ return false;
1135
+ if (position.row === end.row && position.column > end.column)
1136
+ return false;
1137
+ return true;
1138
+ }
1139
+ /**
1140
+ * Convert internal scope to ScopeInfo.
1141
+ */
1142
+ toScopeInfo(scope) {
1143
+ return {
1144
+ id: scope.id,
1145
+ kind: scope.kind,
1146
+ parentId: scope.parentId,
1147
+ childIds: scope.childIds,
1148
+ symbols: Array.from(scope.symbols.keys()),
1149
+ location: scope.location,
1150
+ depth: scope.depth,
1151
+ };
1152
+ }
1153
+ /**
1154
+ * Build the final analysis result.
1155
+ */
1156
+ buildResult() {
1157
+ // Build symbol table from all scopes
1158
+ const symbols = new Map();
1159
+ for (const scope of this.scopes.values()) {
1160
+ for (const [name, symbol] of scope.symbols) {
1161
+ // Use scope-qualified key to avoid collisions
1162
+ const key = `${scope.id}:${name}`;
1163
+ symbols.set(key, symbol);
1164
+ }
1165
+ }
1166
+ // Convert scopes to ScopeInfo array
1167
+ const scopes = Array.from(this.scopes.values()).map((s) => this.toScopeInfo(s));
1168
+ return {
1169
+ symbols,
1170
+ scopes,
1171
+ unresolvedReferences: this.unresolvedReferences,
1172
+ shadowedVariables: this.shadowedVariables,
1173
+ };
1174
+ }
1175
+ /**
1176
+ * Clear internal state.
1177
+ */
1178
+ clearCache() {
1179
+ this.reset();
1180
+ }
1181
+ }
1182
+ //# sourceMappingURL=semantic-analyzer.js.map