linguclaw 0.4.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 (168) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +161 -0
  3. package/dist/agent-system.d.ts +196 -0
  4. package/dist/agent-system.d.ts.map +1 -0
  5. package/dist/agent-system.js +738 -0
  6. package/dist/agent-system.js.map +1 -0
  7. package/dist/alphabeta.d.ts +54 -0
  8. package/dist/alphabeta.d.ts.map +1 -0
  9. package/dist/alphabeta.js +193 -0
  10. package/dist/alphabeta.js.map +1 -0
  11. package/dist/browser.d.ts +62 -0
  12. package/dist/browser.d.ts.map +1 -0
  13. package/dist/browser.js +224 -0
  14. package/dist/browser.js.map +1 -0
  15. package/dist/cli.d.ts +7 -0
  16. package/dist/cli.d.ts.map +1 -0
  17. package/dist/cli.js +565 -0
  18. package/dist/cli.js.map +1 -0
  19. package/dist/code-parser.d.ts +39 -0
  20. package/dist/code-parser.d.ts.map +1 -0
  21. package/dist/code-parser.js +385 -0
  22. package/dist/code-parser.js.map +1 -0
  23. package/dist/config.d.ts +66 -0
  24. package/dist/config.d.ts.map +1 -0
  25. package/dist/config.js +232 -0
  26. package/dist/config.js.map +1 -0
  27. package/dist/core/engine.d.ts +359 -0
  28. package/dist/core/engine.d.ts.map +1 -0
  29. package/dist/core/engine.js +127 -0
  30. package/dist/core/engine.js.map +1 -0
  31. package/dist/daemon.d.ts +29 -0
  32. package/dist/daemon.d.ts.map +1 -0
  33. package/dist/daemon.js +212 -0
  34. package/dist/daemon.js.map +1 -0
  35. package/dist/email-receiver.d.ts +63 -0
  36. package/dist/email-receiver.d.ts.map +1 -0
  37. package/dist/email-receiver.js +553 -0
  38. package/dist/email-receiver.js.map +1 -0
  39. package/dist/git-integration.d.ts +180 -0
  40. package/dist/git-integration.d.ts.map +1 -0
  41. package/dist/git-integration.js +850 -0
  42. package/dist/git-integration.js.map +1 -0
  43. package/dist/inbox.d.ts +84 -0
  44. package/dist/inbox.d.ts.map +1 -0
  45. package/dist/inbox.js +198 -0
  46. package/dist/inbox.js.map +1 -0
  47. package/dist/index.d.ts +6 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +41 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/languages/cpp.d.ts +51 -0
  52. package/dist/languages/cpp.d.ts.map +1 -0
  53. package/dist/languages/cpp.js +930 -0
  54. package/dist/languages/cpp.js.map +1 -0
  55. package/dist/languages/csharp.d.ts +79 -0
  56. package/dist/languages/csharp.d.ts.map +1 -0
  57. package/dist/languages/csharp.js +1776 -0
  58. package/dist/languages/csharp.js.map +1 -0
  59. package/dist/languages/go.d.ts +50 -0
  60. package/dist/languages/go.d.ts.map +1 -0
  61. package/dist/languages/go.js +882 -0
  62. package/dist/languages/go.js.map +1 -0
  63. package/dist/languages/java.d.ts +47 -0
  64. package/dist/languages/java.d.ts.map +1 -0
  65. package/dist/languages/java.js +649 -0
  66. package/dist/languages/java.js.map +1 -0
  67. package/dist/languages/python.d.ts +47 -0
  68. package/dist/languages/python.d.ts.map +1 -0
  69. package/dist/languages/python.js +655 -0
  70. package/dist/languages/python.js.map +1 -0
  71. package/dist/languages/rust.d.ts +61 -0
  72. package/dist/languages/rust.d.ts.map +1 -0
  73. package/dist/languages/rust.js +1064 -0
  74. package/dist/languages/rust.js.map +1 -0
  75. package/dist/logger.d.ts +20 -0
  76. package/dist/logger.d.ts.map +1 -0
  77. package/dist/logger.js +133 -0
  78. package/dist/logger.js.map +1 -0
  79. package/dist/longterm-memory.d.ts +47 -0
  80. package/dist/longterm-memory.d.ts.map +1 -0
  81. package/dist/longterm-memory.js +300 -0
  82. package/dist/longterm-memory.js.map +1 -0
  83. package/dist/memory.d.ts +42 -0
  84. package/dist/memory.d.ts.map +1 -0
  85. package/dist/memory.js +274 -0
  86. package/dist/memory.js.map +1 -0
  87. package/dist/messaging.d.ts +103 -0
  88. package/dist/messaging.d.ts.map +1 -0
  89. package/dist/messaging.js +645 -0
  90. package/dist/messaging.js.map +1 -0
  91. package/dist/multi-provider.d.ts +69 -0
  92. package/dist/multi-provider.d.ts.map +1 -0
  93. package/dist/multi-provider.js +484 -0
  94. package/dist/multi-provider.js.map +1 -0
  95. package/dist/orchestrator.d.ts +65 -0
  96. package/dist/orchestrator.d.ts.map +1 -0
  97. package/dist/orchestrator.js +441 -0
  98. package/dist/orchestrator.js.map +1 -0
  99. package/dist/plugins.d.ts +52 -0
  100. package/dist/plugins.d.ts.map +1 -0
  101. package/dist/plugins.js +215 -0
  102. package/dist/plugins.js.map +1 -0
  103. package/dist/prism-orchestrator.d.ts +26 -0
  104. package/dist/prism-orchestrator.d.ts.map +1 -0
  105. package/dist/prism-orchestrator.js +191 -0
  106. package/dist/prism-orchestrator.js.map +1 -0
  107. package/dist/prism.d.ts +46 -0
  108. package/dist/prism.d.ts.map +1 -0
  109. package/dist/prism.js +188 -0
  110. package/dist/prism.js.map +1 -0
  111. package/dist/privacy.d.ts +23 -0
  112. package/dist/privacy.d.ts.map +1 -0
  113. package/dist/privacy.js +220 -0
  114. package/dist/privacy.js.map +1 -0
  115. package/dist/proactive.d.ts +30 -0
  116. package/dist/proactive.d.ts.map +1 -0
  117. package/dist/proactive.js +260 -0
  118. package/dist/proactive.js.map +1 -0
  119. package/dist/refactoring-engine.d.ts +100 -0
  120. package/dist/refactoring-engine.d.ts.map +1 -0
  121. package/dist/refactoring-engine.js +717 -0
  122. package/dist/refactoring-engine.js.map +1 -0
  123. package/dist/resilience.d.ts +43 -0
  124. package/dist/resilience.d.ts.map +1 -0
  125. package/dist/resilience.js +200 -0
  126. package/dist/resilience.js.map +1 -0
  127. package/dist/safety.d.ts +40 -0
  128. package/dist/safety.d.ts.map +1 -0
  129. package/dist/safety.js +133 -0
  130. package/dist/safety.js.map +1 -0
  131. package/dist/sandbox.d.ts +33 -0
  132. package/dist/sandbox.d.ts.map +1 -0
  133. package/dist/sandbox.js +173 -0
  134. package/dist/sandbox.js.map +1 -0
  135. package/dist/scheduler.d.ts +72 -0
  136. package/dist/scheduler.d.ts.map +1 -0
  137. package/dist/scheduler.js +374 -0
  138. package/dist/scheduler.js.map +1 -0
  139. package/dist/semantic-memory.d.ts +70 -0
  140. package/dist/semantic-memory.d.ts.map +1 -0
  141. package/dist/semantic-memory.js +430 -0
  142. package/dist/semantic-memory.js.map +1 -0
  143. package/dist/skills.d.ts +97 -0
  144. package/dist/skills.d.ts.map +1 -0
  145. package/dist/skills.js +575 -0
  146. package/dist/skills.js.map +1 -0
  147. package/dist/static/dashboard.html +853 -0
  148. package/dist/static/hub.html +772 -0
  149. package/dist/static/index.html +818 -0
  150. package/dist/static/logo.svg +24 -0
  151. package/dist/static/workflow-editor.html +913 -0
  152. package/dist/tools.d.ts +67 -0
  153. package/dist/tools.d.ts.map +1 -0
  154. package/dist/tools.js +303 -0
  155. package/dist/tools.js.map +1 -0
  156. package/dist/types.d.ts +295 -0
  157. package/dist/types.d.ts.map +1 -0
  158. package/dist/types.js +90 -0
  159. package/dist/types.js.map +1 -0
  160. package/dist/web.d.ts +76 -0
  161. package/dist/web.d.ts.map +1 -0
  162. package/dist/web.js +2139 -0
  163. package/dist/web.js.map +1 -0
  164. package/dist/workflow-engine.d.ts +114 -0
  165. package/dist/workflow-engine.d.ts.map +1 -0
  166. package/dist/workflow-engine.js +855 -0
  167. package/dist/workflow-engine.js.map +1 -0
  168. package/package.json +77 -0
@@ -0,0 +1,1776 @@
1
+ "use strict";
2
+ /**
3
+ * C# Language Support for LinguClaw
4
+ * Advanced parser with LINQ, async/await, and .NET ecosystem analysis
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.CSharpLanguageSupport = exports.CSharpAnalyzer = exports.CSharpParser = void 0;
8
+ class CSharpParser {
9
+ source = '';
10
+ lines = [];
11
+ currentLine = 0;
12
+ parse(source, filePath) {
13
+ this.source = source;
14
+ this.lines = source.split('\n');
15
+ this.currentLine = 0;
16
+ try {
17
+ const ast = this.parseCSharp(source, filePath);
18
+ return {
19
+ ast,
20
+ errors: this.findSyntaxErrors(),
21
+ warnings: this.findPreprocessorIssues(),
22
+ tokens: [],
23
+ comments: this.extractComments(),
24
+ };
25
+ }
26
+ catch (error) {
27
+ return this.fallbackParse(filePath);
28
+ }
29
+ }
30
+ parseCSharp(source, filePath) {
31
+ const root = {
32
+ type: 'CompilationUnit',
33
+ id: `${filePath}:0:0`,
34
+ location: this.createLocation(filePath, 0, 0, this.lines.length, 0),
35
+ children: [],
36
+ metadata: { language: 'csharp' },
37
+ };
38
+ // Parse using directives
39
+ const usings = this.parseUsingDirectives(source, filePath);
40
+ root.children.push(...usings);
41
+ // Parse namespace declarations
42
+ const namespaces = this.parseNamespaces(source, filePath);
43
+ root.children.push(...namespaces);
44
+ // Parse top-level statements (C# 9.0+)
45
+ const topLevel = this.parseTopLevelStatements(source, filePath);
46
+ if (topLevel.length > 0) {
47
+ root.children.push(...topLevel);
48
+ }
49
+ return root;
50
+ }
51
+ parseUsingDirectives(source, filePath) {
52
+ const directives = [];
53
+ const usingRegex = /^(global\s+)?using\s+(static\s+)?([\w.]+)\s*(?:=\s*([\w.<>]+))?\s*;/gm;
54
+ let match;
55
+ while ((match = usingRegex.exec(source)) !== null) {
56
+ const isGlobal = !!match[1];
57
+ const isStatic = !!match[2];
58
+ const namespace = match[3];
59
+ const alias = match[4];
60
+ const lineNum = source.substring(0, match.index).split('\n').length;
61
+ const node = {
62
+ type: 'UsingDirective',
63
+ id: `${filePath}:${lineNum}:0`,
64
+ location: this.createLocation(filePath, lineNum, 0, lineNum, match[0].length),
65
+ children: [],
66
+ metadata: {
67
+ isGlobal,
68
+ isStatic,
69
+ namespace,
70
+ alias,
71
+ isAlias: !!alias,
72
+ },
73
+ };
74
+ directives.push(node);
75
+ }
76
+ return directives;
77
+ }
78
+ parseNamespaces(source, filePath) {
79
+ const namespaces = [];
80
+ const lines = source.split('\n');
81
+ let i = 0;
82
+ while (i < lines.length) {
83
+ const line = lines[i].trim();
84
+ if (line.startsWith('namespace ')) {
85
+ const nsDecl = this.parseNamespace(lines, i, filePath);
86
+ if (nsDecl) {
87
+ namespaces.push(nsDecl.node);
88
+ i = nsDecl.endIndex + 1;
89
+ continue;
90
+ }
91
+ }
92
+ else if (line.startsWith('file ')) {
93
+ // File-scoped namespace (C# 10.0+)
94
+ const fileNsDecl = this.parseFileScopedNamespace(lines, i, filePath);
95
+ if (fileNsDecl) {
96
+ namespaces.push(fileNsDecl.node);
97
+ i = fileNsDecl.endIndex + 1;
98
+ continue;
99
+ }
100
+ }
101
+ i++;
102
+ }
103
+ return namespaces;
104
+ }
105
+ parseNamespace(lines, startIdx, filePath) {
106
+ const line = lines[startIdx].trim();
107
+ const match = line.match(/namespace\s+([\w.]+)\s*{/);
108
+ if (!match)
109
+ return null;
110
+ const name = match[1];
111
+ let braceCount = 1;
112
+ let endIdx = startIdx;
113
+ for (let i = startIdx + 1; i < lines.length && braceCount > 0; i++) {
114
+ for (const char of lines[i]) {
115
+ if (char === '{')
116
+ braceCount++;
117
+ if (char === '}')
118
+ braceCount--;
119
+ }
120
+ if (braceCount === 0) {
121
+ endIdx = i;
122
+ break;
123
+ }
124
+ }
125
+ const body = lines.slice(startIdx + 1, endIdx);
126
+ const members = this.parseTypeDeclarations(body.join('\n'), filePath);
127
+ const node = {
128
+ type: 'NamespaceDeclaration',
129
+ id: `${filePath}:${startIdx + 1}:0`,
130
+ location: this.createLocation(filePath, startIdx + 1, 0, endIdx + 1, lines[endIdx]?.length || 0),
131
+ children: members,
132
+ metadata: {
133
+ name,
134
+ isFileScoped: false,
135
+ memberCount: members.length,
136
+ },
137
+ };
138
+ return { node, endIndex: endIdx };
139
+ }
140
+ parseFileScopedNamespace(lines, startIdx, filePath) {
141
+ const line = lines[startIdx].trim();
142
+ // File-scoped namespace ends with semicolon, not braces
143
+ const match = line.match(/namespace\s+([\w.]+)\s*;/);
144
+ if (!match)
145
+ return null;
146
+ const name = match[1];
147
+ // All remaining declarations belong to this namespace
148
+ const remainingLines = lines.slice(startIdx + 1);
149
+ const members = this.parseTypeDeclarations(remainingLines.join('\n'), filePath);
150
+ const node = {
151
+ type: 'NamespaceDeclaration',
152
+ id: `${filePath}:${startIdx + 1}:0`,
153
+ location: this.createLocation(filePath, startIdx + 1, 0, lines.length, 0),
154
+ children: members,
155
+ metadata: {
156
+ name,
157
+ isFileScoped: true,
158
+ memberCount: members.length,
159
+ },
160
+ };
161
+ return { node, endIndex: lines.length - 1 };
162
+ }
163
+ parseTypeDeclarations(source, filePath) {
164
+ const declarations = [];
165
+ const lines = source.split('\n');
166
+ let i = 0;
167
+ while (i < lines.length) {
168
+ const line = lines[i].trim();
169
+ // Skip empty lines, comments, attributes
170
+ if (!line || line.startsWith('//') || line.startsWith('#') || line.startsWith('[')) {
171
+ if (line.startsWith('[')) {
172
+ // Parse attributes
173
+ const attrDecl = this.parseAttribute(lines, i, filePath);
174
+ if (attrDecl) {
175
+ i = attrDecl.endIndex + 1;
176
+ continue;
177
+ }
178
+ }
179
+ i++;
180
+ continue;
181
+ }
182
+ // Class
183
+ if (this.isClassDeclaration(line)) {
184
+ const classDecl = this.parseClass(lines, i, filePath);
185
+ if (classDecl) {
186
+ declarations.push(classDecl.node);
187
+ i = classDecl.endIndex + 1;
188
+ continue;
189
+ }
190
+ }
191
+ // Interface
192
+ if (this.isInterfaceDeclaration(line)) {
193
+ const ifaceDecl = this.parseInterface(lines, i, filePath);
194
+ if (ifaceDecl) {
195
+ declarations.push(ifaceDecl.node);
196
+ i = ifaceDecl.endIndex + 1;
197
+ continue;
198
+ }
199
+ }
200
+ // Struct
201
+ if (this.isStructDeclaration(line)) {
202
+ const structDecl = this.parseStruct(lines, i, filePath);
203
+ if (structDecl) {
204
+ declarations.push(structDecl.node);
205
+ i = structDecl.endIndex + 1;
206
+ continue;
207
+ }
208
+ }
209
+ // Enum
210
+ if (this.isEnumDeclaration(line)) {
211
+ const enumDecl = this.parseEnum(lines, i, filePath);
212
+ if (enumDecl) {
213
+ declarations.push(enumDecl.node);
214
+ i = enumDecl.endIndex + 1;
215
+ continue;
216
+ }
217
+ }
218
+ // Record
219
+ if (this.isRecordDeclaration(line)) {
220
+ const recordDecl = this.parseRecord(lines, i, filePath);
221
+ if (recordDecl) {
222
+ declarations.push(recordDecl.node);
223
+ i = recordDecl.endIndex + 1;
224
+ continue;
225
+ }
226
+ }
227
+ // Delegate
228
+ if (this.isDelegateDeclaration(line)) {
229
+ const delegateDecl = this.parseDelegate(lines, i, filePath);
230
+ if (delegateDecl) {
231
+ declarations.push(delegateDecl.node);
232
+ i = delegateDecl.endIndex + 1;
233
+ continue;
234
+ }
235
+ }
236
+ i++;
237
+ }
238
+ return declarations;
239
+ }
240
+ isClassDeclaration(line) {
241
+ return /^(?:public|private|protected|internal|abstract|sealed|static|partial|unsafe)?\s*(?:class)\s+\w+/.test(line);
242
+ }
243
+ isInterfaceDeclaration(line) {
244
+ return /^(?:public|private|protected|internal|partial)?\s*interface\s+\w+/.test(line);
245
+ }
246
+ isStructDeclaration(line) {
247
+ return /^(?:public|private|protected|internal|readonly|ref|partial|unsafe)?\s*struct\s+\w+/.test(line);
248
+ }
249
+ isEnumDeclaration(line) {
250
+ return /^(?:public|private|protected|internal)?\s*enum\s+\w+/.test(line);
251
+ }
252
+ isRecordDeclaration(line) {
253
+ return /^(?:public|private|protected|internal|abstract|sealed|readonly|ref|partial)?\s*record\s+(?:class|struct\s+)?\w+/.test(line);
254
+ }
255
+ isDelegateDeclaration(line) {
256
+ return /^(?:public|private|protected|internal)?\s*delegate\s+/.test(line);
257
+ }
258
+ parseClass(lines, startIdx, filePath) {
259
+ const line = lines[startIdx].trim();
260
+ // Extract modifiers and class name
261
+ const match = line.match(/^(.*?)class\s+(\w+)(?:<([^>]+)>)?(?:\s*:\s*([^{]+))?/);
262
+ if (!match)
263
+ return null;
264
+ const modifiers = match[1].trim().split(/\s+/).filter(m => m);
265
+ const name = match[2];
266
+ const typeParams = match[3];
267
+ const inheritance = match[4];
268
+ let endIdx = startIdx;
269
+ let braceCount = 0;
270
+ let foundOpenBrace = false;
271
+ for (let i = startIdx; i < lines.length; i++) {
272
+ for (const char of lines[i]) {
273
+ if (char === '{') {
274
+ braceCount++;
275
+ foundOpenBrace = true;
276
+ }
277
+ if (char === '}')
278
+ braceCount--;
279
+ }
280
+ if (foundOpenBrace && braceCount === 0) {
281
+ endIdx = i;
282
+ break;
283
+ }
284
+ }
285
+ const body = lines.slice(startIdx, endIdx + 1).join('\n');
286
+ const members = this.parseClassMembers(body, filePath);
287
+ const node = {
288
+ type: 'ClassDeclaration',
289
+ id: `${filePath}:${startIdx + 1}:0`,
290
+ location: this.createLocation(filePath, startIdx + 1, 0, endIdx + 1, lines[endIdx]?.length || 0),
291
+ children: members,
292
+ metadata: {
293
+ name,
294
+ modifiers,
295
+ typeParameters: typeParams ? typeParams.split(',').map(p => p.trim()) : [],
296
+ isGeneric: !!typeParams,
297
+ isAbstract: modifiers.includes('abstract'),
298
+ isSealed: modifiers.includes('sealed'),
299
+ isStatic: modifiers.includes('static'),
300
+ isPartial: modifiers.includes('partial'),
301
+ isUnsafe: modifiers.includes('unsafe'),
302
+ inheritance: this.parseInheritance(inheritance),
303
+ baseClass: inheritance?.split(',')[0]?.trim() || null,
304
+ implementedInterfaces: inheritance?.split(',').slice(1).map(i => i.trim()) || [],
305
+ },
306
+ };
307
+ return { node, endIndex: endIdx };
308
+ }
309
+ parseInterface(lines, startIdx, filePath) {
310
+ const line = lines[startIdx].trim();
311
+ const match = line.match(/^(.*?)interface\s+(\w+)(?:<([^>]+)>)?(?:\s*:\s*([^{]+))?/);
312
+ if (!match)
313
+ return null;
314
+ const modifiers = match[1].trim().split(/\s+/).filter(m => m);
315
+ const name = match[2];
316
+ const typeParams = match[3];
317
+ const inheritance = match[4];
318
+ let endIdx = startIdx;
319
+ let braceCount = 0;
320
+ let foundOpenBrace = false;
321
+ for (let i = startIdx; i < lines.length; i++) {
322
+ for (const char of lines[i]) {
323
+ if (char === '{') {
324
+ braceCount++;
325
+ foundOpenBrace = true;
326
+ }
327
+ if (char === '}')
328
+ braceCount--;
329
+ }
330
+ if (foundOpenBrace && braceCount === 0) {
331
+ endIdx = i;
332
+ break;
333
+ }
334
+ }
335
+ const body = lines.slice(startIdx, endIdx + 1).join('\n');
336
+ const members = this.parseInterfaceMembers(body, filePath);
337
+ const node = {
338
+ type: 'InterfaceDeclaration',
339
+ id: `${filePath}:${startIdx + 1}:0`,
340
+ location: this.createLocation(filePath, startIdx + 1, 0, endIdx + 1, lines[endIdx]?.length || 0),
341
+ children: members,
342
+ metadata: {
343
+ name,
344
+ modifiers,
345
+ typeParameters: typeParams ? typeParams.split(',').map(p => p.trim()) : [],
346
+ isGeneric: !!typeParams,
347
+ isPartial: modifiers.includes('partial'),
348
+ extendedInterfaces: inheritance?.split(',').map(i => i.trim()) || [],
349
+ },
350
+ };
351
+ return { node, endIndex: endIdx };
352
+ }
353
+ parseStruct(lines, startIdx, filePath) {
354
+ const line = lines[startIdx].trim();
355
+ const match = line.match(/^(.*?)struct\s+(\w+)(?:<([^>]+)>)?(?:\s*:\s*([^{]+))?/);
356
+ if (!match)
357
+ return null;
358
+ const modifiers = match[1].trim().split(/\s+/).filter(m => m);
359
+ const name = match[2];
360
+ const typeParams = match[3];
361
+ const inheritance = match[4];
362
+ let endIdx = startIdx;
363
+ let braceCount = 0;
364
+ let foundOpenBrace = false;
365
+ for (let i = startIdx; i < lines.length; i++) {
366
+ for (const char of lines[i]) {
367
+ if (char === '{') {
368
+ braceCount++;
369
+ foundOpenBrace = true;
370
+ }
371
+ if (char === '}')
372
+ braceCount--;
373
+ }
374
+ if (foundOpenBrace && braceCount === 0) {
375
+ endIdx = i;
376
+ break;
377
+ }
378
+ }
379
+ const body = lines.slice(startIdx, endIdx + 1).join('\n');
380
+ const members = this.parseStructMembers(body, filePath);
381
+ const node = {
382
+ type: 'StructDeclaration',
383
+ id: `${filePath}:${startIdx + 1}:0`,
384
+ location: this.createLocation(filePath, startIdx + 1, 0, endIdx + 1, lines[endIdx]?.length || 0),
385
+ children: members,
386
+ metadata: {
387
+ name,
388
+ modifiers,
389
+ typeParameters: typeParams ? typeParams.split(',').map(p => p.trim()) : [],
390
+ isGeneric: !!typeParams,
391
+ isReadonly: modifiers.includes('readonly'),
392
+ isRef: modifiers.includes('ref'),
393
+ isPartial: modifiers.includes('partial'),
394
+ isUnsafe: modifiers.includes('unsafe'),
395
+ implementedInterfaces: inheritance?.split(',').map(i => i.trim()) || [],
396
+ },
397
+ };
398
+ return { node, endIndex: endIdx };
399
+ }
400
+ parseRecord(lines, startIdx, filePath) {
401
+ const line = lines[startIdx].trim();
402
+ const match = line.match(/^(.*?)record\s+(?:class|struct\s+)?(\w+)(?:<([^>]+)>)?(?:\s*\(([^)]*)\))?(?:\s*:\s*([^{]+))?/);
403
+ if (!match)
404
+ return null;
405
+ const modifiers = match[1].trim().split(/\s+/).filter(m => m);
406
+ const name = match[2];
407
+ const typeParams = match[3];
408
+ const positionalParams = match[4];
409
+ const inheritance = match[5];
410
+ let endIdx = startIdx;
411
+ let braceCount = 0;
412
+ let foundOpenBrace = false;
413
+ for (let i = startIdx; i < lines.length; i++) {
414
+ for (const char of lines[i]) {
415
+ if (char === '{') {
416
+ braceCount++;
417
+ foundOpenBrace = true;
418
+ }
419
+ if (char === '}')
420
+ braceCount--;
421
+ }
422
+ if ((foundOpenBrace && braceCount === 0) || (positionalParams && !inheritance && lines[i].includes(';'))) {
423
+ endIdx = i;
424
+ break;
425
+ }
426
+ }
427
+ const node = {
428
+ type: 'RecordDeclaration',
429
+ id: `${filePath}:${startIdx + 1}:0`,
430
+ location: this.createLocation(filePath, startIdx + 1, 0, endIdx + 1, lines[endIdx]?.length || 0),
431
+ children: [],
432
+ metadata: {
433
+ name,
434
+ modifiers,
435
+ typeParameters: typeParams ? typeParams.split(',').map(p => p.trim()) : [],
436
+ isGeneric: !!typeParams,
437
+ isClass: !modifiers.includes('struct') && !line.includes('record struct'),
438
+ isStruct: modifiers.includes('struct') || line.includes('record struct'),
439
+ positionalParameters: positionalParams ? positionalParams.split(',').map(p => p.trim()) : [],
440
+ inheritance: this.parseInheritance(inheritance),
441
+ isAbstract: modifiers.includes('abstract'),
442
+ isSealed: modifiers.includes('sealed'),
443
+ },
444
+ };
445
+ return { node, endIndex: endIdx };
446
+ }
447
+ parseEnum(lines, startIdx, filePath) {
448
+ const line = lines[startIdx].trim();
449
+ const match = line.match(/^(.*?)enum\s+(\w+)(?:\s*:\s*(\w+))?/);
450
+ if (!match)
451
+ return null;
452
+ const modifiers = match[1].trim().split(/\s+/).filter(m => m);
453
+ const name = match[2];
454
+ const underlyingType = match[3] || 'int';
455
+ let endIdx = startIdx;
456
+ let braceCount = 0;
457
+ let foundOpenBrace = false;
458
+ for (let i = startIdx; i < lines.length; i++) {
459
+ for (const char of lines[i]) {
460
+ if (char === '{') {
461
+ braceCount++;
462
+ foundOpenBrace = true;
463
+ }
464
+ if (char === '}')
465
+ braceCount--;
466
+ }
467
+ if (foundOpenBrace && braceCount === 0) {
468
+ endIdx = i;
469
+ break;
470
+ }
471
+ }
472
+ const body = lines.slice(startIdx, endIdx + 1).join('\n');
473
+ const values = this.extractEnumValues(body);
474
+ const node = {
475
+ type: 'EnumDeclaration',
476
+ id: `${filePath}:${startIdx + 1}:0`,
477
+ location: this.createLocation(filePath, startIdx + 1, 0, endIdx + 1, lines[endIdx]?.length || 0),
478
+ children: [],
479
+ metadata: {
480
+ name,
481
+ modifiers,
482
+ underlyingType,
483
+ values,
484
+ hasFlagsAttribute: modifiers.includes('[Flags]') || body.includes('[Flags]'),
485
+ },
486
+ };
487
+ return { node, endIndex: endIdx };
488
+ }
489
+ parseDelegate(lines, startIdx, filePath) {
490
+ const line = lines[startIdx].trim();
491
+ const match = line.match(/^(.*?)delegate\s+([\w<>\[\],\s]+)\s+(\w+)\s*\(([^)]*)\)\s*;/);
492
+ if (!match)
493
+ return null;
494
+ const modifiers = match[1].trim().split(/\s+/).filter(m => m);
495
+ const returnType = match[2].trim();
496
+ const name = match[3];
497
+ const parameters = match[4];
498
+ const node = {
499
+ type: 'DelegateDeclaration',
500
+ id: `${filePath}:${startIdx + 1}:0`,
501
+ location: this.createLocation(filePath, startIdx + 1, 0, startIdx + 1, line.length),
502
+ children: [],
503
+ metadata: {
504
+ name,
505
+ modifiers,
506
+ returnType,
507
+ parameters: parameters ? parameters.split(',').map(p => p.trim()) : [],
508
+ },
509
+ };
510
+ return { node, endIndex: startIdx };
511
+ }
512
+ parseAttribute(lines, startIdx, filePath) {
513
+ const line = lines[startIdx].trim();
514
+ const match = line.match(/^\[([\w]+)(?:\(([^)]*)\))?\]\s*(?:$|\/\/)/);
515
+ if (!match)
516
+ return null;
517
+ const name = match[1];
518
+ const arguments_ = match[2];
519
+ // Check if it's a target-specific attribute [target: Attribute]
520
+ const targetMatch = line.match(/^\[(\w+):\s*([\w]+)/);
521
+ const node = {
522
+ type: 'Attribute',
523
+ id: `${filePath}:${startIdx + 1}:0`,
524
+ location: this.createLocation(filePath, startIdx + 1, 0, startIdx + 1, line.length),
525
+ children: [],
526
+ metadata: {
527
+ name: targetMatch ? targetMatch[2] : name,
528
+ target: targetMatch ? targetMatch[1] : null,
529
+ arguments: arguments_ || null,
530
+ },
531
+ };
532
+ return { node, endIndex: startIdx };
533
+ }
534
+ parseClassMembers(body, filePath) {
535
+ const members = [];
536
+ const lines = body.split('\n');
537
+ for (let i = 0; i < lines.length; i++) {
538
+ const line = lines[i].trim();
539
+ // Skip empty lines and comments
540
+ if (!line || line.startsWith('//') || line.startsWith('/*'))
541
+ continue;
542
+ // Method
543
+ if (this.isMethodDeclaration(line)) {
544
+ const methodDecl = this.parseMethod(lines, i, filePath);
545
+ if (methodDecl) {
546
+ members.push(methodDecl.node);
547
+ i = methodDecl.endIndex;
548
+ }
549
+ }
550
+ // Property
551
+ if (this.isPropertyDeclaration(line)) {
552
+ const propDecl = this.parseProperty(lines, i, filePath);
553
+ if (propDecl) {
554
+ members.push(propDecl.node);
555
+ i = propDecl.endIndex;
556
+ }
557
+ }
558
+ // Field
559
+ if (this.isFieldDeclaration(line)) {
560
+ const fieldDecl = this.parseField(lines, i, filePath);
561
+ if (fieldDecl) {
562
+ members.push(fieldDecl.node);
563
+ i = fieldDecl.endIndex;
564
+ }
565
+ }
566
+ // Constructor
567
+ if (this.isConstructorDeclaration(line)) {
568
+ const ctorDecl = this.parseConstructor(lines, i, filePath);
569
+ if (ctorDecl) {
570
+ members.push(ctorDecl.node);
571
+ i = ctorDecl.endIndex;
572
+ }
573
+ }
574
+ // Destructor/Finalizer
575
+ if (line.includes('~' + this.extractClassName(body))) {
576
+ const dtorDecl = this.parseDestructor(lines, i, filePath);
577
+ if (dtorDecl) {
578
+ members.push(dtorDecl.node);
579
+ i = dtorDecl.endIndex;
580
+ }
581
+ }
582
+ // Event
583
+ if (this.isEventDeclaration(line)) {
584
+ const eventDecl = this.parseEvent(lines, i, filePath);
585
+ if (eventDecl) {
586
+ members.push(eventDecl.node);
587
+ i = eventDecl.endIndex;
588
+ }
589
+ }
590
+ // Indexer
591
+ if (this.isIndexerDeclaration(line)) {
592
+ const indexerDecl = this.parseIndexer(lines, i, filePath);
593
+ if (indexerDecl) {
594
+ members.push(indexerDecl.node);
595
+ i = indexerDecl.endIndex;
596
+ }
597
+ }
598
+ // Operator
599
+ if (this.isOperatorDeclaration(line)) {
600
+ const operatorDecl = this.parseOperator(lines, i, filePath);
601
+ if (operatorDecl) {
602
+ members.push(operatorDecl.node);
603
+ i = operatorDecl.endIndex;
604
+ }
605
+ }
606
+ // Nested type
607
+ if (this.isNestedTypeDeclaration(line)) {
608
+ const nestedDecl = this.parseNestedType(lines, i, filePath);
609
+ if (nestedDecl) {
610
+ members.push(nestedDecl.node);
611
+ i = nestedDecl.endIndex;
612
+ }
613
+ }
614
+ }
615
+ return members;
616
+ }
617
+ parseInterfaceMembers(body, filePath) {
618
+ const members = [];
619
+ const lines = body.split('\n');
620
+ for (let i = 0; i < lines.length; i++) {
621
+ const line = lines[i].trim();
622
+ // Method signature
623
+ const methodMatch = line.match(/([\w<>\[\],\s]+)\s+(\w+)\s*\(([^)]*)\)\s*;/);
624
+ if (methodMatch) {
625
+ members.push({
626
+ type: 'MethodDeclaration',
627
+ id: `${filePath}:${i + 1}:0`,
628
+ location: this.createLocation(filePath, i + 1, 0, i + 1, line.length),
629
+ children: [],
630
+ metadata: {
631
+ name: methodMatch[2],
632
+ returnType: methodMatch[1].trim(),
633
+ parameters: methodMatch[3],
634
+ isAbstract: true,
635
+ },
636
+ });
637
+ }
638
+ // Property signature
639
+ const propMatch = line.match(/([\w<>\[\],\s]+)\s+(\w+)\s*\{\s*(get|set)\s*;\s*(?:get|set)?\s*;?\s*\}/);
640
+ if (propMatch) {
641
+ members.push({
642
+ type: 'PropertyDeclaration',
643
+ id: `${filePath}:${i + 1}:0`,
644
+ location: this.createLocation(filePath, i + 1, 0, i + 1, line.length),
645
+ children: [],
646
+ metadata: {
647
+ name: propMatch[2],
648
+ type: propMatch[1].trim(),
649
+ hasGetter: line.includes('get'),
650
+ hasSetter: line.includes('set'),
651
+ },
652
+ });
653
+ }
654
+ // Indexer signature
655
+ const indexerMatch = line.match(/([\w<>\[\],\s]+)\s+this\s*\[([^\]]+)\]\s*\{/);
656
+ if (indexerMatch) {
657
+ members.push({
658
+ type: 'IndexerDeclaration',
659
+ id: `${filePath}:${i + 1}:0`,
660
+ location: this.createLocation(filePath, i + 1, 0, i + 1, line.length),
661
+ children: [],
662
+ metadata: {
663
+ type: indexerMatch[1].trim(),
664
+ parameters: indexerMatch[2],
665
+ },
666
+ });
667
+ }
668
+ // Event signature
669
+ const eventMatch = line.match(/event\s+([\w<>]+)\s+(\w+)\s*;/);
670
+ if (eventMatch) {
671
+ members.push({
672
+ type: 'EventDeclaration',
673
+ id: `${filePath}:${i + 1}:0`,
674
+ location: this.createLocation(filePath, i + 1, 0, i + 1, line.length),
675
+ children: [],
676
+ metadata: {
677
+ name: eventMatch[2],
678
+ type: eventMatch[1].trim(),
679
+ },
680
+ });
681
+ }
682
+ }
683
+ return members;
684
+ }
685
+ parseStructMembers(body, filePath) {
686
+ // Structs have same members as classes
687
+ return this.parseClassMembers(body, filePath);
688
+ }
689
+ isMethodDeclaration(line) {
690
+ return /(?:public|private|protected|internal|static|virtual|abstract|override|sealed|extern|async|partial|unsafe)?\s*(?:[\w<>\[\],\s]+)\s+\w+\s*\(/.test(line) &&
691
+ !line.includes('class ') &&
692
+ !line.includes('struct ') &&
693
+ !line.includes('interface ') &&
694
+ !line.includes('enum ') &&
695
+ !line.includes('delegate ') &&
696
+ !line.includes('record ');
697
+ }
698
+ isPropertyDeclaration(line) {
699
+ return /(?:public|private|protected|internal|static|virtual|abstract|override|sealed|extern)?\s*(?:[\w<>\[\],\s]+)\s+\w+\s*\{/.test(line) &&
700
+ !line.includes('class ') &&
701
+ !line.includes('new ') &&
702
+ !line.includes('(');
703
+ }
704
+ isFieldDeclaration(line) {
705
+ return /(?:public|private|protected|internal|static|readonly|volatile|const|fixed|unsafe)?\s*(?:[\w<>\[\],\s]+)\s+\w+\s*(?:=|;)/.test(line) &&
706
+ !line.includes('(') &&
707
+ !line.includes('{') &&
708
+ !line.includes('class ') &&
709
+ !line.includes('struct ') &&
710
+ !line.includes('interface ');
711
+ }
712
+ isConstructorDeclaration(line) {
713
+ return /(?:public|private|protected|internal|static|extern|unsafe)?\s*\w+\s*\([^)]*\)\s*(?::\s*base\s*\(|:\s*this\s*\()?\s*\{/.test(line);
714
+ }
715
+ isEventDeclaration(line) {
716
+ return /(?:public|private|protected|internal|static|virtual|abstract|override|sealed|extern)?\s*event\s+/.test(line);
717
+ }
718
+ isIndexerDeclaration(line) {
719
+ return /(?:public|private|protected|internal|static|virtual|abstract|override|sealed|extern)?\s*(?:[\w<>\[\],\s]+)\s+this\s*\[/.test(line);
720
+ }
721
+ isOperatorDeclaration(line) {
722
+ return /(?:public|static|extern)?\s*(?:[\w<>\[\],\s]+)\s+operator\s+/.test(line);
723
+ }
724
+ isNestedTypeDeclaration(line) {
725
+ return /(?:public|private|protected|internal)?\s*(?:class|struct|interface|enum|record)\s+/.test(line);
726
+ }
727
+ parseMethod(lines, startIdx, filePath) {
728
+ const line = lines[startIdx].trim();
729
+ // Match method signature with modifiers
730
+ const match = line.match(/^(.*?)\s*([\w<>\[\],\s]+)\s+(\w+)\s*\(([^)]*)\)(?:\s*where\s+[^;]+)?\s*\{/);
731
+ if (!match) {
732
+ // Check for expression-bodied method
733
+ const exprMatch = line.match(/^(.*?)\s*([\w<>\[\],\s]+)\s+(\w+)\s*\(([^)]*)\)\s*=>\s*(.+);/);
734
+ if (exprMatch) {
735
+ return this.parseExpressionBodiedMethod(lines, startIdx, filePath, exprMatch);
736
+ }
737
+ return null;
738
+ }
739
+ const modifiers = match[1].trim().split(/\s+/).filter(m => m);
740
+ const returnType = match[2].trim();
741
+ const name = match[3];
742
+ const parameters = match[4];
743
+ // Find method body end
744
+ let endIdx = startIdx;
745
+ let braceCount = 0;
746
+ let foundOpenBrace = false;
747
+ for (let i = startIdx; i < lines.length; i++) {
748
+ for (const char of lines[i]) {
749
+ if (char === '{') {
750
+ braceCount++;
751
+ foundOpenBrace = true;
752
+ }
753
+ if (char === '}')
754
+ braceCount--;
755
+ }
756
+ if (foundOpenBrace && braceCount === 0) {
757
+ endIdx = i;
758
+ break;
759
+ }
760
+ }
761
+ const node = {
762
+ type: 'MethodDeclaration',
763
+ id: `${filePath}:${startIdx + 1}:0`,
764
+ location: this.createLocation(filePath, startIdx + 1, 0, endIdx + 1, lines[endIdx]?.length || 0),
765
+ children: [],
766
+ metadata: {
767
+ name,
768
+ returnType,
769
+ parameters: parameters ? parameters.split(',').map(p => p.trim()) : [],
770
+ modifiers,
771
+ isPublic: modifiers.includes('public'),
772
+ isPrivate: modifiers.includes('private'),
773
+ isProtected: modifiers.includes('protected'),
774
+ isInternal: modifiers.includes('internal'),
775
+ isStatic: modifiers.includes('static'),
776
+ isVirtual: modifiers.includes('virtual'),
777
+ isAbstract: modifiers.includes('abstract'),
778
+ isOverride: modifiers.includes('override'),
779
+ isSealed: modifiers.includes('sealed'),
780
+ isExtern: modifiers.includes('extern'),
781
+ isAsync: modifiers.includes('async'),
782
+ isPartial: modifiers.includes('partial'),
783
+ isUnsafe: modifiers.includes('unsafe'),
784
+ isExpressionBodied: false,
785
+ hasBody: true,
786
+ },
787
+ };
788
+ return { node, endIndex: endIdx };
789
+ }
790
+ parseExpressionBodiedMethod(lines, startIdx, filePath, match) {
791
+ const modifiers = match[1].trim().split(/\s+/).filter(m => m);
792
+ const returnType = match[2].trim();
793
+ const name = match[3];
794
+ const parameters = match[4];
795
+ const node = {
796
+ type: 'MethodDeclaration',
797
+ id: `${filePath}:${startIdx + 1}:0`,
798
+ location: this.createLocation(filePath, startIdx + 1, 0, startIdx + 1, lines[startIdx].length),
799
+ children: [],
800
+ metadata: {
801
+ name,
802
+ returnType,
803
+ parameters: parameters ? parameters.split(',').map(p => p.trim()) : [],
804
+ modifiers,
805
+ isPublic: modifiers.includes('public'),
806
+ isStatic: modifiers.includes('static'),
807
+ isVirtual: modifiers.includes('virtual'),
808
+ isOverride: modifiers.includes('override'),
809
+ isAsync: modifiers.includes('async'),
810
+ isExpressionBodied: true,
811
+ hasBody: true,
812
+ },
813
+ };
814
+ return { node, endIndex: startIdx };
815
+ }
816
+ parseProperty(lines, startIdx, filePath) {
817
+ const line = lines[startIdx].trim();
818
+ // Auto-property or full property
819
+ const match = line.match(/^(.*?)\s*([\w<>\[\],\s]+)\s+(\w+)\s*\{/);
820
+ if (!match)
821
+ return null;
822
+ const modifiers = match[1].trim().split(/\s+/).filter(m => m);
823
+ const type = match[2].trim();
824
+ const name = match[3];
825
+ // Find property end
826
+ let endIdx = startIdx;
827
+ let braceCount = 0;
828
+ let foundOpenBrace = false;
829
+ for (let i = startIdx; i < lines.length; i++) {
830
+ const currentLine = lines[i];
831
+ for (const char of currentLine) {
832
+ if (char === '{') {
833
+ braceCount++;
834
+ foundOpenBrace = true;
835
+ }
836
+ if (char === '}')
837
+ braceCount--;
838
+ }
839
+ if (foundOpenBrace && braceCount === 0) {
840
+ endIdx = i;
841
+ break;
842
+ }
843
+ // Expression-bodied property
844
+ if (currentLine.includes('=>') && !foundOpenBrace) {
845
+ if (currentLine.includes(';')) {
846
+ endIdx = i;
847
+ break;
848
+ }
849
+ }
850
+ }
851
+ const body = lines.slice(startIdx, endIdx + 1).join('\n');
852
+ const node = {
853
+ type: 'PropertyDeclaration',
854
+ id: `${filePath}:${startIdx + 1}:0`,
855
+ location: this.createLocation(filePath, startIdx + 1, 0, endIdx + 1, lines[endIdx]?.length || 0),
856
+ children: [],
857
+ metadata: {
858
+ name,
859
+ type,
860
+ modifiers,
861
+ hasGetter: body.includes('get'),
862
+ hasSetter: body.includes('set'),
863
+ isAutoProperty: body.includes('get;') && body.includes('set;') && !body.includes('{ get {'),
864
+ isInitOnly: body.includes('init;'),
865
+ isExpressionBodied: body.includes('=>'),
866
+ isRequired: modifiers.includes('required'),
867
+ },
868
+ };
869
+ return { node, endIndex: endIdx };
870
+ }
871
+ parseField(lines, startIdx, filePath) {
872
+ const line = lines[startIdx].trim();
873
+ const match = line.match(/^(.*?)\s*([\w<>\[\],\s]+)\s+(\w+)\s*(?:=\s*([^;]+))?;/);
874
+ if (!match)
875
+ return null;
876
+ const modifiers = match[1].trim().split(/\s+/).filter(m => m);
877
+ const type = match[2].trim();
878
+ const name = match[3];
879
+ const initializer = match[4];
880
+ const node = {
881
+ type: 'FieldDeclaration',
882
+ id: `${filePath}:${startIdx + 1}:0`,
883
+ location: this.createLocation(filePath, startIdx + 1, 0, startIdx + 1, line.length),
884
+ children: [],
885
+ metadata: {
886
+ name,
887
+ type,
888
+ modifiers,
889
+ isPublic: modifiers.includes('public'),
890
+ isPrivate: modifiers.includes('private'),
891
+ isProtected: modifiers.includes('protected'),
892
+ isInternal: modifiers.includes('internal'),
893
+ isStatic: modifiers.includes('static'),
894
+ isReadonly: modifiers.includes('readonly'),
895
+ isVolatile: modifiers.includes('volatile'),
896
+ isConst: modifiers.includes('const'),
897
+ isFixed: modifiers.includes('fixed'),
898
+ initializer: initializer?.trim(),
899
+ },
900
+ };
901
+ return { node, endIndex: startIdx };
902
+ }
903
+ parseConstructor(lines, startIdx, filePath) {
904
+ const line = lines[startIdx].trim();
905
+ const match = line.match(/^(.*?)\s*(\w+)\s*\(([^)]*)\)(?:\s*:\s*(base|this)\s*\(([^)]*)\))?\s*\{/);
906
+ if (!match)
907
+ return null;
908
+ const modifiers = match[1].trim().split(/\s+/).filter(m => m);
909
+ const name = match[2];
910
+ const parameters = match[3];
911
+ const initializerType = match[4]; // base or this
912
+ const initializerArgs = match[5];
913
+ // Find constructor body end
914
+ let endIdx = startIdx;
915
+ let braceCount = 0;
916
+ let foundOpenBrace = false;
917
+ for (let i = startIdx; i < lines.length; i++) {
918
+ for (const char of lines[i]) {
919
+ if (char === '{') {
920
+ braceCount++;
921
+ foundOpenBrace = true;
922
+ }
923
+ if (char === '}')
924
+ braceCount--;
925
+ }
926
+ if (foundOpenBrace && braceCount === 0) {
927
+ endIdx = i;
928
+ break;
929
+ }
930
+ }
931
+ const node = {
932
+ type: 'ConstructorDeclaration',
933
+ id: `${filePath}:${startIdx + 1}:0`,
934
+ location: this.createLocation(filePath, startIdx + 1, 0, endIdx + 1, lines[endIdx]?.length || 0),
935
+ children: [],
936
+ metadata: {
937
+ name,
938
+ parameters: parameters ? parameters.split(',').map(p => p.trim()) : [],
939
+ modifiers,
940
+ isPublic: modifiers.includes('public'),
941
+ isPrivate: modifiers.includes('private'),
942
+ isProtected: modifiers.includes('protected'),
943
+ isInternal: modifiers.includes('internal'),
944
+ isStatic: modifiers.includes('static'), // Static constructor
945
+ initializerType,
946
+ initializerArgs,
947
+ },
948
+ };
949
+ return { node, endIndex: endIdx };
950
+ }
951
+ parseDestructor(lines, startIdx, filePath) {
952
+ const line = lines[startIdx].trim();
953
+ const match = line.match(/^(.*?)\s*~(\w+)\s*\(\s*\)\s*\{/);
954
+ if (!match)
955
+ return null;
956
+ const modifiers = match[1].trim().split(/\s+/).filter(m => m);
957
+ const name = match[2];
958
+ // Find destructor body end
959
+ let endIdx = startIdx;
960
+ let braceCount = 0;
961
+ let foundOpenBrace = false;
962
+ for (let i = startIdx; i < lines.length; i++) {
963
+ for (const char of lines[i]) {
964
+ if (char === '{') {
965
+ braceCount++;
966
+ foundOpenBrace = true;
967
+ }
968
+ if (char === '}')
969
+ braceCount--;
970
+ }
971
+ if (foundOpenBrace && braceCount === 0) {
972
+ endIdx = i;
973
+ break;
974
+ }
975
+ }
976
+ const node = {
977
+ type: 'DestructorDeclaration',
978
+ id: `${filePath}:${startIdx + 1}:0`,
979
+ location: this.createLocation(filePath, startIdx + 1, 0, endIdx + 1, lines[endIdx]?.length || 0),
980
+ children: [],
981
+ metadata: {
982
+ name,
983
+ modifiers,
984
+ isExtern: modifiers.includes('extern'),
985
+ isUnsafe: modifiers.includes('unsafe'),
986
+ },
987
+ };
988
+ return { node, endIndex: endIdx };
989
+ }
990
+ parseEvent(lines, startIdx, filePath) {
991
+ const line = lines[startIdx].trim();
992
+ // Event field or property
993
+ const fieldMatch = line.match(/^(.*?)\s*event\s+([\w<>]+)\s+(\w+)\s*(?:=\s*([^;]+))?;/);
994
+ if (fieldMatch) {
995
+ const modifiers = fieldMatch[1].trim().split(/\s+/).filter(m => m);
996
+ const type = fieldMatch[2].trim();
997
+ const name = fieldMatch[3];
998
+ const node = {
999
+ type: 'EventDeclaration',
1000
+ id: `${filePath}:${startIdx + 1}:0`,
1001
+ location: this.createLocation(filePath, startIdx + 1, 0, startIdx + 1, line.length),
1002
+ children: [],
1003
+ metadata: {
1004
+ name,
1005
+ type,
1006
+ modifiers,
1007
+ isField: true,
1008
+ isPublic: modifiers.includes('public'),
1009
+ isStatic: modifiers.includes('static'),
1010
+ isVirtual: modifiers.includes('virtual'),
1011
+ isAbstract: modifiers.includes('abstract'),
1012
+ isOverride: modifiers.includes('override'),
1013
+ isSealed: modifiers.includes('sealed'),
1014
+ },
1015
+ };
1016
+ return { node, endIndex: startIdx };
1017
+ }
1018
+ // Event property (with add/remove)
1019
+ const propMatch = line.match(/^(.*?)\s*event\s+([\w<>]+)\s+(\w+)\s*\{/);
1020
+ if (propMatch) {
1021
+ const modifiers = propMatch[1].trim().split(/\s+/).filter(m => m);
1022
+ const type = propMatch[2].trim();
1023
+ const name = propMatch[3];
1024
+ // Find property end
1025
+ let endIdx = startIdx;
1026
+ let braceCount = 0;
1027
+ let foundOpenBrace = false;
1028
+ for (let i = startIdx; i < lines.length; i++) {
1029
+ for (const char of lines[i]) {
1030
+ if (char === '{') {
1031
+ braceCount++;
1032
+ foundOpenBrace = true;
1033
+ }
1034
+ if (char === '}')
1035
+ braceCount--;
1036
+ }
1037
+ if (foundOpenBrace && braceCount === 0) {
1038
+ endIdx = i;
1039
+ break;
1040
+ }
1041
+ }
1042
+ const body = lines.slice(startIdx, endIdx + 1).join('\n');
1043
+ const node = {
1044
+ type: 'EventDeclaration',
1045
+ id: `${filePath}:${startIdx + 1}:0`,
1046
+ location: this.createLocation(filePath, startIdx + 1, 0, endIdx + 1, lines[endIdx]?.length || 0),
1047
+ children: [],
1048
+ metadata: {
1049
+ name,
1050
+ type,
1051
+ modifiers,
1052
+ isField: false,
1053
+ hasAdd: body.includes('add'),
1054
+ hasRemove: body.includes('remove'),
1055
+ },
1056
+ };
1057
+ return { node, endIndex: endIdx };
1058
+ }
1059
+ return null;
1060
+ }
1061
+ parseIndexer(lines, startIdx, filePath) {
1062
+ const line = lines[startIdx].trim();
1063
+ const match = line.match(/^(.*?)\s*([\w<>\[\],\s]+)\s+this\s*\[([^\]]+)\]\s*\{/);
1064
+ if (!match)
1065
+ return null;
1066
+ const modifiers = match[1].trim().split(/\s+/).filter(m => m);
1067
+ const type = match[2].trim();
1068
+ const parameters = match[3];
1069
+ // Find indexer body end
1070
+ let endIdx = startIdx;
1071
+ let braceCount = 0;
1072
+ let foundOpenBrace = false;
1073
+ for (let i = startIdx; i < lines.length; i++) {
1074
+ for (const char of lines[i]) {
1075
+ if (char === '{') {
1076
+ braceCount++;
1077
+ foundOpenBrace = true;
1078
+ }
1079
+ if (char === '}')
1080
+ braceCount--;
1081
+ }
1082
+ if (foundOpenBrace && braceCount === 0) {
1083
+ endIdx = i;
1084
+ break;
1085
+ }
1086
+ }
1087
+ const body = lines.slice(startIdx, endIdx + 1).join('\n');
1088
+ const node = {
1089
+ type: 'IndexerDeclaration',
1090
+ id: `${filePath}:${startIdx + 1}:0`,
1091
+ location: this.createLocation(filePath, startIdx + 1, 0, endIdx + 1, lines[endIdx]?.length || 0),
1092
+ children: [],
1093
+ metadata: {
1094
+ type,
1095
+ parameters: parameters.split(',').map(p => p.trim()),
1096
+ modifiers,
1097
+ hasGetter: body.includes('get'),
1098
+ hasSetter: body.includes('set'),
1099
+ isPublic: modifiers.includes('public'),
1100
+ isVirtual: modifiers.includes('virtual'),
1101
+ isAbstract: modifiers.includes('abstract'),
1102
+ isOverride: modifiers.includes('override'),
1103
+ isSealed: modifiers.includes('sealed'),
1104
+ },
1105
+ };
1106
+ return { node, endIndex: endIdx };
1107
+ }
1108
+ parseOperator(lines, startIdx, filePath) {
1109
+ const line = lines[startIdx].trim();
1110
+ const match = line.match(/^(.*?)\s*([\w<>\[\],\s]+)\s+operator\s+(\S+)\s*\(([^)]*)\)\s*\{/);
1111
+ if (!match)
1112
+ return null;
1113
+ const modifiers = match[1].trim().split(/\s+/).filter(m => m);
1114
+ const returnType = match[2].trim();
1115
+ const operator = match[3];
1116
+ const parameters = match[4];
1117
+ // Find operator body end
1118
+ let endIdx = startIdx;
1119
+ let braceCount = 0;
1120
+ let foundOpenBrace = false;
1121
+ for (let i = startIdx; i < lines.length; i++) {
1122
+ for (const char of lines[i]) {
1123
+ if (char === '{') {
1124
+ braceCount++;
1125
+ foundOpenBrace = true;
1126
+ }
1127
+ if (char === '}')
1128
+ braceCount--;
1129
+ }
1130
+ if (foundOpenBrace && braceCount === 0) {
1131
+ endIdx = i;
1132
+ break;
1133
+ }
1134
+ }
1135
+ const node = {
1136
+ type: 'OperatorDeclaration',
1137
+ id: `${filePath}:${startIdx + 1}:0`,
1138
+ location: this.createLocation(filePath, startIdx + 1, 0, endIdx + 1, lines[endIdx]?.length || 0),
1139
+ children: [],
1140
+ metadata: {
1141
+ operator,
1142
+ returnType,
1143
+ parameters: parameters ? parameters.split(',').map(p => p.trim()) : [],
1144
+ modifiers,
1145
+ isImplicit: operator === 'implicit',
1146
+ isExplicit: operator === 'explicit',
1147
+ isConversion: operator === 'implicit' || operator === 'explicit',
1148
+ isPublic: modifiers.includes('public'),
1149
+ isStatic: modifiers.includes('static'),
1150
+ isExtern: modifiers.includes('extern'),
1151
+ },
1152
+ };
1153
+ return { node, endIndex: endIdx };
1154
+ }
1155
+ parseNestedType(lines, startIdx, filePath) {
1156
+ const line = lines[startIdx].trim();
1157
+ if (line.includes('class ')) {
1158
+ return this.parseClass(lines, startIdx, filePath);
1159
+ }
1160
+ else if (line.includes('struct ')) {
1161
+ return this.parseStruct(lines, startIdx, filePath);
1162
+ }
1163
+ else if (line.includes('interface ')) {
1164
+ return this.parseInterface(lines, startIdx, filePath);
1165
+ }
1166
+ else if (line.includes('enum ')) {
1167
+ return this.parseEnum(lines, startIdx, filePath);
1168
+ }
1169
+ else if (line.includes('record ')) {
1170
+ return this.parseRecord(lines, startIdx, filePath);
1171
+ }
1172
+ return null;
1173
+ }
1174
+ parseTopLevelStatements(source, filePath) {
1175
+ const statements = [];
1176
+ // C# 9.0+ allows top-level statements (no Main method required)
1177
+ // Check if there are any top-level statements before namespace/class declarations
1178
+ const lines = source.split('\n');
1179
+ let foundTypeDeclaration = false;
1180
+ for (let i = 0; i < lines.length; i++) {
1181
+ const line = lines[i].trim();
1182
+ // Skip comments and usings
1183
+ if (!line || line.startsWith('//') || line.startsWith('using ') || line.startsWith('#'))
1184
+ continue;
1185
+ // Check for type declaration
1186
+ if (/^(?:public|internal|private|protected|class|struct|interface|enum|record)\s+/.test(line)) {
1187
+ foundTypeDeclaration = true;
1188
+ break;
1189
+ }
1190
+ // This is likely a top-level statement
1191
+ if (!foundTypeDeclaration && line && !line.startsWith('{') && !line.startsWith('}')) {
1192
+ statements.push({
1193
+ type: 'TopLevelStatement',
1194
+ id: `${filePath}:${i + 1}:0`,
1195
+ location: this.createLocation(filePath, i + 1, 0, i + 1, line.length),
1196
+ children: [],
1197
+ metadata: {
1198
+ statement: line,
1199
+ },
1200
+ });
1201
+ }
1202
+ }
1203
+ return statements;
1204
+ }
1205
+ parseInheritance(inheritance) {
1206
+ if (!inheritance)
1207
+ return [];
1208
+ const result = [];
1209
+ const parts = inheritance.split(',');
1210
+ for (const part of parts) {
1211
+ const trimmed = part.trim();
1212
+ if (trimmed) {
1213
+ // In C#, first type is base class (if class), rest are interfaces
1214
+ // Or all are interfaces (if struct/interface)
1215
+ result.push({
1216
+ type: trimmed,
1217
+ name: trimmed,
1218
+ isInterface: trimmed.startsWith('I') && /^I[A-Z]/.test(trimmed),
1219
+ });
1220
+ }
1221
+ }
1222
+ return result;
1223
+ }
1224
+ extractEnumValues(body) {
1225
+ const values = [];
1226
+ const valueRegex = /(\w+)\s*(?:=\s*([^,\n]+))?/g;
1227
+ let match;
1228
+ while ((match = valueRegex.exec(body)) !== null) {
1229
+ const name = match[1];
1230
+ if (name && !['enum', 'class', 'struct', 'public', 'private', 'protected', 'internal'].includes(name)) {
1231
+ const valueStr = match[2]?.trim();
1232
+ const value = valueStr ? parseInt(valueStr) : undefined;
1233
+ values.push({
1234
+ name,
1235
+ value: isNaN(value) ? undefined : value,
1236
+ flags: valueStr?.includes('<<') || valueStr?.includes('|'),
1237
+ });
1238
+ }
1239
+ }
1240
+ return values;
1241
+ }
1242
+ extractClassName(body) {
1243
+ const match = body.match(/class\s+(\w+)/);
1244
+ return match ? match[1] : '';
1245
+ }
1246
+ findSyntaxErrors() {
1247
+ const errors = [];
1248
+ let braceCount = 0;
1249
+ let parenCount = 0;
1250
+ let angleBracketCount = 0;
1251
+ for (let i = 0; i < this.lines.length; i++) {
1252
+ const line = this.lines[i];
1253
+ let inString = false;
1254
+ let stringChar = '';
1255
+ for (let j = 0; j < line.length; j++) {
1256
+ const char = line[j];
1257
+ const prevChar = j > 0 ? line[j - 1] : '';
1258
+ // Handle strings
1259
+ if ((char === '"' || char === "'" || char === '`') && prevChar !== '\\') {
1260
+ if (!inString) {
1261
+ inString = true;
1262
+ stringChar = char;
1263
+ }
1264
+ else if (stringChar === char) {
1265
+ inString = false;
1266
+ }
1267
+ continue;
1268
+ }
1269
+ if (inString)
1270
+ continue;
1271
+ // Handle comments
1272
+ if (char === '/' && line[j + 1] === '/')
1273
+ break; // Single line comment
1274
+ if (char === '/' && line[j + 1] === '*') {
1275
+ // Multi-line comment start - skip until end
1276
+ j++;
1277
+ continue;
1278
+ }
1279
+ // Count braces, parens, angle brackets
1280
+ if (char === '{')
1281
+ braceCount++;
1282
+ if (char === '}')
1283
+ braceCount--;
1284
+ if (char === '(')
1285
+ parenCount++;
1286
+ if (char === ')')
1287
+ parenCount--;
1288
+ if (char === '<' && /[\w\s]/.test(line[j + 1] || ''))
1289
+ angleBracketCount++;
1290
+ if (char === '>' && /[\w\s,.)\]]/.test(line[j + 1] || ''))
1291
+ angleBracketCount--;
1292
+ }
1293
+ if (braceCount < 0) {
1294
+ errors.push({ message: 'Unexpected }', line: i + 1, severity: 'error' });
1295
+ braceCount = 0;
1296
+ }
1297
+ if (parenCount < 0) {
1298
+ errors.push({ message: 'Unexpected )', line: i + 1, severity: 'error' });
1299
+ parenCount = 0;
1300
+ }
1301
+ }
1302
+ if (braceCount > 0) {
1303
+ errors.push({ message: `Unclosed braces: ${braceCount}`, line: this.lines.length, severity: 'error' });
1304
+ }
1305
+ if (parenCount > 0) {
1306
+ errors.push({ message: `Unclosed parentheses: ${parenCount}`, line: this.lines.length, severity: 'error' });
1307
+ }
1308
+ return errors;
1309
+ }
1310
+ findPreprocessorIssues() {
1311
+ const warnings = [];
1312
+ for (let i = 0; i < this.lines.length; i++) {
1313
+ const line = this.lines[i].trim();
1314
+ // Check for missing #nullable directive in modern C#
1315
+ if (i === 0 && !line.includes('#nullable')) {
1316
+ warnings.push({
1317
+ message: 'Consider adding #nullable enable for null safety',
1318
+ line: 1,
1319
+ severity: 'info',
1320
+ });
1321
+ }
1322
+ // Check for region directives without endregion
1323
+ if (line.startsWith('#region') && !this.source.includes('#endregion')) {
1324
+ warnings.push({
1325
+ message: '#region directive without matching #endregion',
1326
+ line: i + 1,
1327
+ severity: 'warning',
1328
+ });
1329
+ }
1330
+ }
1331
+ return warnings;
1332
+ }
1333
+ extractComments() {
1334
+ const comments = [];
1335
+ for (let i = 0; i < this.lines.length; i++) {
1336
+ const line = this.lines[i];
1337
+ // Single line comments
1338
+ const singleIdx = line.indexOf('//');
1339
+ if (singleIdx !== -1) {
1340
+ comments.push({
1341
+ type: 'Line',
1342
+ value: line.substring(singleIdx + 2).trim(),
1343
+ line: i + 1,
1344
+ });
1345
+ }
1346
+ }
1347
+ // Block comments (simplified)
1348
+ const blockRegex = /\/\*[\s\S]*?\*\//g;
1349
+ let match;
1350
+ let sourceIndex = 0;
1351
+ while ((match = blockRegex.exec(this.source)) !== null) {
1352
+ const lineNum = this.source.substring(0, match.index).split('\n').length;
1353
+ comments.push({
1354
+ type: 'Block',
1355
+ value: match[0].substring(2, match[0].length - 2).trim(),
1356
+ line: lineNum,
1357
+ });
1358
+ }
1359
+ // XML documentation comments
1360
+ const xmlDocRegex = /\/\/\/\s*(.+)/g;
1361
+ while ((match = xmlDocRegex.exec(this.source)) !== null) {
1362
+ const lineNum = this.source.substring(0, match.index).split('\n').length;
1363
+ comments.push({
1364
+ type: 'Documentation',
1365
+ value: match[1].trim(),
1366
+ line: lineNum,
1367
+ });
1368
+ }
1369
+ return comments;
1370
+ }
1371
+ createLocation(file, startLine, startCol, endLine, endCol) {
1372
+ return {
1373
+ file,
1374
+ startLine,
1375
+ startColumn: startCol,
1376
+ endLine,
1377
+ endColumn: endCol,
1378
+ byteOffset: 0,
1379
+ };
1380
+ }
1381
+ fallbackParse(filePath) {
1382
+ return {
1383
+ ast: this.parseCSharp(this.source, filePath),
1384
+ errors: this.findSyntaxErrors(),
1385
+ warnings: this.findPreprocessorIssues(),
1386
+ tokens: [],
1387
+ comments: this.extractComments(),
1388
+ };
1389
+ }
1390
+ }
1391
+ exports.CSharpParser = CSharpParser;
1392
+ class CSharpAnalyzer {
1393
+ analyze(ast, context) {
1394
+ const symbolTable = this.buildSymbolTable(ast);
1395
+ const asyncAnalysis = this.analyzeAsyncPatterns(ast);
1396
+ const linqAnalysis = this.analyzeLinqUsage(ast);
1397
+ const nullableAnalysis = this.analyzeNullableReferenceTypes(ast);
1398
+ const securityIssues = this.findSecurityIssues(ast);
1399
+ const dotnetAnalysis = this.analyzeDotNetPatterns(ast);
1400
+ return {
1401
+ symbols: symbolTable,
1402
+ callGraph: this.buildCallGraph(ast),
1403
+ dataFlow: { definitions: new Map(), uses: new Map(), taintedSources: [], sinks: [] },
1404
+ controlFlow: { nodes: [], edges: [], loops: [], branches: [] },
1405
+ typeInference: new Map(),
1406
+ metrics: this.calculateMetrics(ast),
1407
+ suggestions: [
1408
+ ...asyncAnalysis.suggestions,
1409
+ ...linqAnalysis.suggestions,
1410
+ ...nullableAnalysis.suggestions,
1411
+ ...dotnetAnalysis.suggestions,
1412
+ ...securityIssues.map(i => ({
1413
+ type: 'security',
1414
+ severity: i.severity,
1415
+ message: i.description,
1416
+ remediation: i.remediation,
1417
+ })),
1418
+ ],
1419
+ };
1420
+ }
1421
+ buildSymbolTable(ast) {
1422
+ return {
1423
+ variables: new Map(),
1424
+ functions: new Map(),
1425
+ classes: new Map(),
1426
+ modules: new Map(),
1427
+ imports: [],
1428
+ exports: [],
1429
+ };
1430
+ }
1431
+ analyzeAsyncPatterns(ast) {
1432
+ const suggestions = [];
1433
+ let asyncMethodCount = 0;
1434
+ let syncOverAsyncCount = 0;
1435
+ let missingAwaitCount = 0;
1436
+ const traverse = (node) => {
1437
+ // Count async methods
1438
+ if (node.type === 'MethodDeclaration' && node.metadata.isAsync) {
1439
+ asyncMethodCount++;
1440
+ }
1441
+ // Check for sync-over-async (Task.Result, Task.Wait(), .GetAwaiter().GetResult())
1442
+ if (node.type === 'MethodInvocation') {
1443
+ const method = node.metadata.method || '';
1444
+ if (method.includes('.Result') || method.includes('.Wait()') || method.includes('.GetAwaiter().GetResult()')) {
1445
+ syncOverAsyncCount++;
1446
+ suggestions.push({
1447
+ type: 'performance',
1448
+ severity: 'warning',
1449
+ message: 'Sync-over-async detected - can cause deadlocks',
1450
+ remediation: 'Use await instead of .Result or .Wait()',
1451
+ });
1452
+ }
1453
+ }
1454
+ // Check for missing await on async methods
1455
+ if (node.type === 'MethodInvocation') {
1456
+ const method = node.metadata.method || '';
1457
+ const isAsyncCall = method.includes('Async');
1458
+ if (isAsyncCall && !node.metadata.isAwaited) {
1459
+ missingAwaitCount++;
1460
+ }
1461
+ }
1462
+ node.children.forEach(traverse);
1463
+ };
1464
+ traverse(ast);
1465
+ if (asyncMethodCount > 0 && syncOverAsyncCount > 0) {
1466
+ suggestions.push({
1467
+ type: 'performance',
1468
+ severity: 'warning',
1469
+ message: `Found ${syncOverAsyncCount} sync-over-async calls in async codebase`,
1470
+ remediation: 'Consider using async/await throughout the call stack',
1471
+ });
1472
+ }
1473
+ return { suggestions };
1474
+ }
1475
+ analyzeLinqUsage(ast) {
1476
+ const suggestions = [];
1477
+ let deferredExecutionCount = 0;
1478
+ let immediateExecutionCount = 0;
1479
+ let multipleEnumerationCount = 0;
1480
+ const traverse = (node) => {
1481
+ // Check for LINQ method calls
1482
+ if (node.type === 'MethodInvocation') {
1483
+ const method = node.metadata.method || '';
1484
+ // Deferred execution methods (ToList, ToArray, First, Single, etc. trigger execution)
1485
+ const immediateMethods = ['ToList', 'ToArray', 'First', 'FirstOrDefault', 'Single', 'SingleOrDefault', 'Last', 'LastOrDefault', 'Count', 'Any', 'All'];
1486
+ const deferredMethods = ['Where', 'Select', 'OrderBy', 'ThenBy', 'GroupBy', 'Join', 'Skip', 'Take', 'Distinct'];
1487
+ if (immediateMethods.some(m => method.includes(m))) {
1488
+ immediateExecutionCount++;
1489
+ }
1490
+ if (deferredMethods.some(m => method.includes(m))) {
1491
+ deferredExecutionCount++;
1492
+ }
1493
+ // Multiple enumeration pattern
1494
+ if (method.includes('Count()') || method.includes('Any()')) {
1495
+ // Check if same enumerable is used again later
1496
+ const parent = node.parent;
1497
+ if (parent && parent.children) {
1498
+ const sameVarUsages = parent.children.filter(c => c.metadata?.variable === node.metadata.variable);
1499
+ if (sameVarUsages.length > 1) {
1500
+ multipleEnumerationCount++;
1501
+ }
1502
+ }
1503
+ }
1504
+ }
1505
+ node.children.forEach(traverse);
1506
+ };
1507
+ traverse(ast);
1508
+ if (deferredExecutionCount > 0 && immediateExecutionCount === 0) {
1509
+ suggestions.push({
1510
+ type: 'performance',
1511
+ severity: 'info',
1512
+ message: 'LINQ query without terminal operator - deferred execution may cause multiple evaluations',
1513
+ remediation: 'Add .ToList() or .ToArray() if the result is enumerated multiple times',
1514
+ });
1515
+ }
1516
+ return { suggestions };
1517
+ }
1518
+ analyzeNullableReferenceTypes(ast) {
1519
+ const suggestions = [];
1520
+ let nullableEnabled = false;
1521
+ let nullForgivingCount = 0;
1522
+ let potentialNullDereferenceCount = 0;
1523
+ const traverse = (node) => {
1524
+ // Check for #nullable directive
1525
+ if (node.type === 'PreprocessorDirective' && node.metadata.directive === 'nullable') {
1526
+ nullableEnabled = node.metadata.value?.includes('enable');
1527
+ }
1528
+ // Check for null-forgiving operator (!)
1529
+ if (node.type === 'MemberAccess' && node.metadata.hasNullForgiving) {
1530
+ nullForgivingCount++;
1531
+ }
1532
+ // Check for potential null dereference
1533
+ if (node.type === 'MemberAccess') {
1534
+ const variable = node.metadata.variable || '';
1535
+ const isNullable = node.metadata.isNullable;
1536
+ const hasNullCheck = node.metadata.hasNullCheck;
1537
+ if (isNullable && !hasNullCheck && !node.metadata.hasNullForgiving) {
1538
+ potentialNullDereferenceCount++;
1539
+ }
1540
+ }
1541
+ node.children.forEach(traverse);
1542
+ };
1543
+ traverse(ast);
1544
+ if (!nullableEnabled) {
1545
+ suggestions.push({
1546
+ type: 'modernization',
1547
+ severity: 'info',
1548
+ message: 'Nullable reference types not enabled',
1549
+ remediation: 'Add #nullable enable to enable null safety analysis',
1550
+ });
1551
+ }
1552
+ if (nullForgivingCount > 5) {
1553
+ suggestions.push({
1554
+ type: 'code-quality',
1555
+ severity: 'warning',
1556
+ message: `Excessive use of null-forgiving operator (!) - ${nullForgivingCount} instances`,
1557
+ remediation: 'Review null safety assumptions and add proper null checks',
1558
+ });
1559
+ }
1560
+ return { suggestions };
1561
+ }
1562
+ analyzeDotNetPatterns(ast) {
1563
+ const suggestions = [];
1564
+ let disposableNotDisposed = 0;
1565
+ let stringConcatInLoop = 0;
1566
+ let boxingCount = 0;
1567
+ const traverse = (node) => {
1568
+ // IDisposable not disposed
1569
+ if (node.type === 'VariableDeclaration' && node.metadata.type) {
1570
+ const type = node.metadata.type;
1571
+ const disposableTypes = ['Stream', 'HttpClient', 'SqlConnection', 'DbContext', 'FileStream', 'MemoryStream'];
1572
+ if (disposableTypes.some(t => type.includes(t))) {
1573
+ const hasUsing = node.metadata.hasUsingDeclaration || node.metadata.isInUsingBlock;
1574
+ if (!hasUsing) {
1575
+ disposableNotDisposed++;
1576
+ suggestions.push({
1577
+ type: 'resource-leak',
1578
+ severity: 'warning',
1579
+ message: `IDisposable type '${type}' may not be disposed properly`,
1580
+ remediation: 'Use using declaration or try-finally with Dispose()',
1581
+ });
1582
+ }
1583
+ }
1584
+ }
1585
+ // String concatenation in loop
1586
+ if (node.type === 'ForStatement' || node.type === 'WhileStatement' || node.type === 'ForEachStatement') {
1587
+ const body = node.children.find(c => c.type === 'Block');
1588
+ if (body) {
1589
+ const hasStringConcat = body.children.some(c => c.type === 'BinaryExpression' &&
1590
+ (c.metadata.operator === '+' || c.metadata.operator === '+=') &&
1591
+ (c.metadata.leftType === 'string' || c.metadata.rightType === 'string'));
1592
+ if (hasStringConcat) {
1593
+ stringConcatInLoop++;
1594
+ suggestions.push({
1595
+ type: 'performance',
1596
+ severity: 'warning',
1597
+ message: 'String concatenation in loop detected',
1598
+ remediation: 'Use StringBuilder for efficient string concatenation in loops',
1599
+ });
1600
+ }
1601
+ }
1602
+ }
1603
+ // Boxing/unboxing
1604
+ if (node.type === 'CastExpression') {
1605
+ const fromType = node.metadata.fromType;
1606
+ const toType = node.metadata.toType;
1607
+ if ((fromType === 'int' || fromType === 'double' || fromType === 'bool') && toType === 'object') {
1608
+ boxingCount++;
1609
+ }
1610
+ }
1611
+ node.children.forEach(traverse);
1612
+ };
1613
+ traverse(ast);
1614
+ if (boxingCount > 5) {
1615
+ suggestions.push({
1616
+ type: 'performance',
1617
+ severity: 'info',
1618
+ message: `Potential boxing operations detected: ${boxingCount}`,
1619
+ remediation: 'Use generics to avoid boxing, or ensure type safety at compile time',
1620
+ });
1621
+ }
1622
+ return { suggestions };
1623
+ }
1624
+ findSecurityIssues(ast) {
1625
+ const issues = [];
1626
+ const traverse = (node) => {
1627
+ // SQL Injection
1628
+ if (node.type === 'MethodInvocation') {
1629
+ const method = node.metadata.method || '';
1630
+ if (method.includes('ExecuteSqlCommand') || method.includes('FromSqlRaw') || method.includes('FromSqlInterpolated')) {
1631
+ const hasInterpolation = node.metadata.hasStringInterpolation;
1632
+ const isRaw = method.includes('Raw') || !method.includes('Interpolated');
1633
+ if (isRaw && hasInterpolation) {
1634
+ issues.push({
1635
+ id: 'CS001',
1636
+ severity: 'critical',
1637
+ category: 'sql-injection',
1638
+ location: node.location,
1639
+ description: 'Potential SQL injection via string interpolation',
1640
+ remediation: 'Use parameterized queries or FromSqlInterpolated instead',
1641
+ falsePositiveLikelihood: 0.2,
1642
+ });
1643
+ }
1644
+ }
1645
+ // Path traversal
1646
+ if (method.includes('File.ReadAllText') || method.includes('File.Open') || method.includes('FileStream')) {
1647
+ const hasUserInput = node.metadata.hasUserInput;
1648
+ if (hasUserInput && !node.metadata.hasPathValidation) {
1649
+ issues.push({
1650
+ id: 'CS002',
1651
+ severity: 'high',
1652
+ category: 'path-traversal',
1653
+ location: node.location,
1654
+ description: 'Potential path traversal vulnerability',
1655
+ remediation: 'Validate file paths using Path.GetFullPath and check for directory traversal',
1656
+ falsePositiveLikelihood: 0.3,
1657
+ });
1658
+ }
1659
+ }
1660
+ // Deserialization
1661
+ if (method.includes('JsonSerializer.Deserialize') || method.includes('BinaryFormatter.Deserialize')) {
1662
+ if (method.includes('BinaryFormatter')) {
1663
+ issues.push({
1664
+ id: 'CS003',
1665
+ severity: 'critical',
1666
+ category: 'deserialization',
1667
+ location: node.location,
1668
+ description: 'BinaryFormatter is obsolete and insecure',
1669
+ remediation: 'Use JsonSerializer with TypeInfoHandling.None or DataContractSerializer',
1670
+ falsePositiveLikelihood: 0.1,
1671
+ });
1672
+ }
1673
+ const hasTypeNameHandling = node.metadata.hasTypeNameHandling;
1674
+ if (hasTypeNameHandling) {
1675
+ issues.push({
1676
+ id: 'CS004',
1677
+ severity: 'critical',
1678
+ category: 'deserialization',
1679
+ location: node.location,
1680
+ description: 'TypeNameHandling can lead to remote code execution',
1681
+ remediation: 'Disable TypeNameHandling or use a custom SerializationBinder',
1682
+ falsePositiveLikelihood: 0.1,
1683
+ });
1684
+ }
1685
+ }
1686
+ // Insecure random
1687
+ if (method.includes('Random.') || method.includes('new Random()')) {
1688
+ if (node.metadata.isSecuritySensitive) {
1689
+ issues.push({
1690
+ id: 'CS005',
1691
+ severity: 'medium',
1692
+ category: 'weak-cryptography',
1693
+ location: node.location,
1694
+ description: 'System.Random is not cryptographically secure',
1695
+ remediation: 'Use RandomNumberGenerator for security-sensitive operations',
1696
+ falsePositiveLikelihood: 0.5,
1697
+ });
1698
+ }
1699
+ }
1700
+ // Debug.Assert in production
1701
+ if (method.includes('Debug.Assert')) {
1702
+ issues.push({
1703
+ id: 'CS006',
1704
+ severity: 'low',
1705
+ category: 'debug-code',
1706
+ location: node.location,
1707
+ description: 'Debug.Assert is removed in release builds',
1708
+ remediation: 'Use proper validation with exceptions for production code',
1709
+ falsePositiveLikelihood: 0.7,
1710
+ });
1711
+ }
1712
+ }
1713
+ // Weak hashing
1714
+ if (node.type === 'MethodInvocation') {
1715
+ const method = node.metadata.method || '';
1716
+ if (method.includes('MD5') || method.includes('SHA1')) {
1717
+ issues.push({
1718
+ id: 'CS007',
1719
+ severity: 'high',
1720
+ category: 'weak-cryptography',
1721
+ location: node.location,
1722
+ description: 'Weak hashing algorithm detected',
1723
+ remediation: 'Use SHA256 or SHA512 for hashing',
1724
+ falsePositiveLikelihood: 0.3,
1725
+ });
1726
+ }
1727
+ }
1728
+ // Hardcoded secrets
1729
+ if (node.type === 'VariableDeclaration' && node.metadata.initializer) {
1730
+ const init = node.metadata.initializer;
1731
+ const name = node.metadata.name || '';
1732
+ if (/password|secret|key|token|connectionstring/i.test(name)) {
1733
+ if (init.includes('"') && init.length > 10) {
1734
+ issues.push({
1735
+ id: 'CS008',
1736
+ severity: 'critical',
1737
+ category: 'secrets',
1738
+ location: node.location,
1739
+ description: 'Potential hardcoded secret detected',
1740
+ remediation: 'Use configuration files or secret management (Azure Key Vault, AWS Secrets Manager)',
1741
+ falsePositiveLikelihood: 0.4,
1742
+ });
1743
+ }
1744
+ }
1745
+ }
1746
+ node.children.forEach(traverse);
1747
+ };
1748
+ traverse(ast);
1749
+ return issues;
1750
+ }
1751
+ buildCallGraph(ast) {
1752
+ return { nodes: [], edges: [], entryPoints: [], deadCode: [] };
1753
+ }
1754
+ calculateMetrics(ast) {
1755
+ return {
1756
+ linesOfCode: 0,
1757
+ logicalLines: 0,
1758
+ commentLines: 0,
1759
+ blankLines: 0,
1760
+ cyclomaticComplexity: 0,
1761
+ cognitiveComplexity: 0,
1762
+ halsteadMetrics: { operators: 0, operands: 0, uniqueOperators: 0, uniqueOperands: 0, volume: 0, difficulty: 0, effort: 0, timeToProgram: 0, bugsDelivered: 0 },
1763
+ maintainabilityIndex: 0,
1764
+ duplicateRate: 0,
1765
+ };
1766
+ }
1767
+ }
1768
+ exports.CSharpAnalyzer = CSharpAnalyzer;
1769
+ exports.CSharpLanguageSupport = {
1770
+ id: 'csharp',
1771
+ name: 'C#',
1772
+ extensions: ['.cs', '.csx', '.cake'],
1773
+ parser: new CSharpParser(),
1774
+ analyzer: new CSharpAnalyzer(),
1775
+ };
1776
+ //# sourceMappingURL=csharp.js.map