@sun-asterisk/sunlint 1.2.1 → 1.2.2

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 (77) hide show
  1. package/config/rule-analysis-strategies.js +18 -2
  2. package/engines/eslint-engine.js +9 -11
  3. package/engines/heuristic-engine.js +55 -31
  4. package/package.json +2 -1
  5. package/rules/README.md +252 -0
  6. package/rules/common/C002_no_duplicate_code/analyzer.js +65 -0
  7. package/rules/common/C002_no_duplicate_code/config.json +23 -0
  8. package/rules/common/C003_no_vague_abbreviations/analyzer.js +418 -0
  9. package/rules/common/C003_no_vague_abbreviations/config.json +35 -0
  10. package/rules/common/C006_function_naming/analyzer.js +504 -0
  11. package/rules/common/C006_function_naming/config.json +86 -0
  12. package/rules/common/C006_function_naming/smart-analyzer.js +503 -0
  13. package/rules/common/C010_limit_block_nesting/analyzer.js +389 -0
  14. package/rules/common/C012_command_query_separation/analyzer.js +481 -0
  15. package/rules/common/C012_command_query_separation/ast-analyzer.js +495 -0
  16. package/rules/common/C013_no_dead_code/analyzer.js +206 -0
  17. package/rules/common/C014_dependency_injection/analyzer.js +338 -0
  18. package/rules/common/C017_constructor_logic/analyzer.js +314 -0
  19. package/rules/common/C019_log_level_usage/analyzer.js +362 -0
  20. package/rules/common/C019_log_level_usage/config.json +121 -0
  21. package/rules/common/C029_catch_block_logging/analyzer-backup.js +426 -0
  22. package/rules/common/C029_catch_block_logging/analyzer-fixed.js +130 -0
  23. package/rules/common/C029_catch_block_logging/analyzer-multi-tech.js +487 -0
  24. package/rules/common/C029_catch_block_logging/analyzer-simple.js +110 -0
  25. package/rules/common/C029_catch_block_logging/analyzer-smart-pipeline.js +755 -0
  26. package/rules/common/C029_catch_block_logging/analyzer.js +129 -0
  27. package/rules/common/C029_catch_block_logging/ast-analyzer-backup.js +441 -0
  28. package/rules/common/C029_catch_block_logging/ast-analyzer-new.js +127 -0
  29. package/rules/common/C029_catch_block_logging/ast-analyzer.js +133 -0
  30. package/rules/common/C029_catch_block_logging/cfg-analyzer.js +408 -0
  31. package/rules/common/C029_catch_block_logging/config.json +59 -0
  32. package/rules/common/C029_catch_block_logging/dataflow-analyzer.js +454 -0
  33. package/rules/common/C029_catch_block_logging/multi-language-ast-engine.js +700 -0
  34. package/rules/common/C029_catch_block_logging/pattern-learning-analyzer.js +568 -0
  35. package/rules/common/C029_catch_block_logging/semantic-analyzer.js +459 -0
  36. package/rules/common/C031_validation_separation/analyzer.js +186 -0
  37. package/rules/common/C041_no_sensitive_hardcode/analyzer.js +292 -0
  38. package/rules/common/C041_no_sensitive_hardcode/ast-analyzer.js +296 -0
  39. package/rules/common/C042_boolean_name_prefix/analyzer.js +300 -0
  40. package/rules/common/C043_no_console_or_print/analyzer.js +431 -0
  41. package/rules/common/C047_no_duplicate_retry_logic/analyzer.js +590 -0
  42. package/rules/common/C075_explicit_return_types/analyzer.js +103 -0
  43. package/rules/common/C076_single_test_behavior/analyzer.js +121 -0
  44. package/rules/docs/C002_no_duplicate_code.md +57 -0
  45. package/rules/docs/C031_validation_separation.md +72 -0
  46. package/rules/index.js +155 -0
  47. package/rules/migration/converter.js +385 -0
  48. package/rules/migration/mapping.json +164 -0
  49. package/rules/parser/constants.js +31 -0
  50. package/rules/parser/file-config.js +80 -0
  51. package/rules/parser/rule-parser-simple.js +305 -0
  52. package/rules/parser/rule-parser.js +527 -0
  53. package/rules/security/S015_insecure_tls_certificate/analyzer.js +150 -0
  54. package/rules/security/S015_insecure_tls_certificate/ast-analyzer.js +237 -0
  55. package/rules/security/S023_no_json_injection/analyzer.js +278 -0
  56. package/rules/security/S023_no_json_injection/ast-analyzer.js +359 -0
  57. package/rules/security/S026_json_schema_validation/analyzer.js +251 -0
  58. package/rules/security/S026_json_schema_validation/config.json +27 -0
  59. package/rules/security/S027_no_hardcoded_secrets/analyzer.js +436 -0
  60. package/rules/security/S027_no_hardcoded_secrets/config.json +29 -0
  61. package/rules/security/S029_csrf_protection/analyzer.js +330 -0
  62. package/rules/tests/C002_no_duplicate_code.test.js +50 -0
  63. package/rules/universal/C010/generic.js +0 -0
  64. package/rules/universal/C010/tree-sitter-analyzer.js +0 -0
  65. package/rules/utils/ast-utils.js +191 -0
  66. package/rules/utils/base-analyzer.js +98 -0
  67. package/rules/utils/pattern-matchers.js +239 -0
  68. package/rules/utils/rule-helpers.js +264 -0
  69. package/rules/utils/severity-constants.js +93 -0
  70. package/scripts/generate_insights.js +188 -0
  71. package/scripts/merge-reports.js +0 -424
  72. package/scripts/test-scripts/README.md +0 -22
  73. package/scripts/test-scripts/test-c041-comparison.js +0 -114
  74. package/scripts/test-scripts/test-c041-eslint.js +0 -67
  75. package/scripts/test-scripts/test-eslint-rules.js +0 -146
  76. package/scripts/test-scripts/test-real-world.js +0 -44
  77. package/scripts/test-scripts/test-rules-on-real-projects.js +0 -86
@@ -0,0 +1,700 @@
1
+ /**
2
+ * C029 Multi-Language AST Engine
3
+ *
4
+ * Provides unified AST-based analysis across multiple programming languages
5
+ * with language-specific adaptations for error handling patterns
6
+ *
7
+ * Technology Showcase: Cross-language compatibility and unified analysis
8
+ */
9
+
10
+ class C029MultiLanguageASTEngine {
11
+ constructor() {
12
+ this.ruleId = 'C029';
13
+ this.ruleName = 'Multi-Language Error Handling Analysis';
14
+ this.description = 'Unified AST-based error handling analysis across languages';
15
+
16
+ // Language-specific parsers and configurations
17
+ this.languageSupport = {
18
+ 'javascript': {
19
+ parser: 'babel',
20
+ fileExtensions: ['.js', '.jsx', '.mjs'],
21
+ errorHandlingPatterns: ['try-catch', 'promise-catch', 'async-catch'],
22
+ keywords: { try: 'try', catch: 'catch', throw: 'throw', error: 'Error' }
23
+ },
24
+ 'typescript': {
25
+ parser: 'typescript',
26
+ fileExtensions: ['.ts', '.tsx'],
27
+ errorHandlingPatterns: ['try-catch', 'promise-catch', 'async-catch', 'generic-error'],
28
+ keywords: { try: 'try', catch: 'catch', throw: 'throw', error: 'Error' }
29
+ },
30
+ 'java': {
31
+ parser: 'tree-sitter-java',
32
+ fileExtensions: ['.java'],
33
+ errorHandlingPatterns: ['try-catch-finally', 'throws-declaration', 'checked-exceptions'],
34
+ keywords: { try: 'try', catch: 'catch', throw: 'throw', throws: 'throws', finally: 'finally' }
35
+ },
36
+ 'kotlin': {
37
+ parser: 'tree-sitter-kotlin',
38
+ fileExtensions: ['.kt', '.kts'],
39
+ errorHandlingPatterns: ['try-catch', 'result-type', 'elvis-operator'],
40
+ keywords: { try: 'try', catch: 'catch', throw: 'throw', result: 'Result' }
41
+ },
42
+ 'dart': {
43
+ parser: 'tree-sitter-dart',
44
+ fileExtensions: ['.dart'],
45
+ errorHandlingPatterns: ['try-catch-on', 'future-catchError', 'stream-error'],
46
+ keywords: { try: 'try', catch: 'catch', on: 'on', throw: 'throw', rethrow: 'rethrow' }
47
+ },
48
+ 'python': {
49
+ parser: 'tree-sitter-python',
50
+ fileExtensions: ['.py', '.pyw'],
51
+ errorHandlingPatterns: ['try-except-finally', 'context-manager', 'exception-chaining'],
52
+ keywords: { try: 'try', except: 'except', raise: 'raise', finally: 'finally' }
53
+ },
54
+ 'go': {
55
+ parser: 'tree-sitter-go',
56
+ fileExtensions: ['.go'],
57
+ errorHandlingPatterns: ['error-return', 'panic-recover', 'defer-recover'],
58
+ keywords: { if: 'if', err: 'err', panic: 'panic', recover: 'recover', defer: 'defer' }
59
+ },
60
+ 'rust': {
61
+ parser: 'tree-sitter-rust',
62
+ fileExtensions: ['.rs'],
63
+ errorHandlingPatterns: ['result-match', 'option-match', 'panic-catch'],
64
+ keywords: { match: 'match', Result: 'Result', Option: 'Option', panic: 'panic!' }
65
+ }
66
+ };
67
+
68
+ // Cross-language error handling concepts
69
+ this.universalConcepts = {
70
+ 'error_propagation': ['throw', 'raise', 'panic', 'return_error'],
71
+ 'error_handling': ['catch', 'except', 'match', 'if_err'],
72
+ 'resource_cleanup': ['finally', 'defer', 'drop', 'using'],
73
+ 'error_logging': ['log', 'print', 'console', 'debug', 'warn'],
74
+ 'error_transformation': ['wrap', 'chain', 'convert', 'map_err']
75
+ };
76
+ }
77
+
78
+ async analyze(files, language, options = {}) {
79
+ const violations = [];
80
+ console.log(`🌐 C029 Multi-Language AST: Analyzing ${files.length} ${language} files...`);
81
+
82
+ const langConfig = this.languageSupport[language.toLowerCase()];
83
+ if (!langConfig) {
84
+ console.warn(`⚠️ Language ${language} not supported by Multi-Language AST Engine`);
85
+ return violations;
86
+ }
87
+
88
+ for (const filePath of files) {
89
+ try {
90
+ const content = require('fs').readFileSync(filePath, 'utf8');
91
+ const languageViolations = await this.analyzeWithLanguageSupport(
92
+ content, filePath, language, langConfig
93
+ );
94
+ violations.push(...languageViolations);
95
+
96
+ } catch (error) {
97
+ console.warn(`C029 Multi-Language AST skipping ${filePath}: ${error.message}`);
98
+ }
99
+ }
100
+
101
+ return violations;
102
+ }
103
+
104
+ /**
105
+ * Analyze file with language-specific support
106
+ */
107
+ async analyzeWithLanguageSupport(content, filePath, language, langConfig) {
108
+ const violations = [];
109
+
110
+ try {
111
+ // Parse with language-specific parser
112
+ const ast = await this.parseWithLanguageParser(content, language, langConfig);
113
+
114
+ // Extract error handling constructs
115
+ const errorHandlingNodes = this.extractErrorHandlingNodes(ast, langConfig);
116
+
117
+ // Analyze each error handling construct
118
+ for (const node of errorHandlingNodes) {
119
+ const analysis = this.analyzeErrorHandlingNode(node, content, langConfig, language);
120
+
121
+ if (analysis.isViolation) {
122
+ violations.push({
123
+ ruleId: this.ruleId,
124
+ file: filePath,
125
+ line: analysis.line,
126
+ column: analysis.column,
127
+ message: analysis.message,
128
+ severity: analysis.severity,
129
+ code: analysis.code,
130
+ type: analysis.type,
131
+ confidence: analysis.confidence,
132
+ suggestion: analysis.suggestion,
133
+ language: language,
134
+ errorHandlingPattern: analysis.pattern,
135
+ crossLanguageInsight: analysis.insight
136
+ });
137
+ }
138
+ }
139
+
140
+ } catch (parseError) {
141
+ console.warn(`AST parsing failed for ${filePath}: ${parseError.message}`);
142
+ // Fallback to regex-based analysis
143
+ return this.fallbackAnalysis(content, filePath, language, langConfig);
144
+ }
145
+
146
+ return violations;
147
+ }
148
+
149
+ /**
150
+ * Parse content with appropriate language parser
151
+ */
152
+ async parseWithLanguageParser(content, language, langConfig) {
153
+ switch (language.toLowerCase()) {
154
+ case 'javascript':
155
+ case 'typescript':
156
+ return this.parseJavaScriptTypeScript(content, language);
157
+
158
+ case 'java':
159
+ return this.parseJava(content);
160
+
161
+ case 'kotlin':
162
+ return this.parseKotlin(content);
163
+
164
+ case 'dart':
165
+ return this.parseDart(content);
166
+
167
+ case 'python':
168
+ return this.parsePython(content);
169
+
170
+ case 'go':
171
+ return this.parseGo(content);
172
+
173
+ case 'rust':
174
+ return this.parseRust(content);
175
+
176
+ default:
177
+ throw new Error(`Parser not implemented for ${language}`);
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Extract error handling nodes from AST
183
+ */
184
+ extractErrorHandlingNodes(ast, langConfig) {
185
+ const errorNodes = [];
186
+
187
+ // Language-specific node extraction
188
+ for (const pattern of langConfig.errorHandlingPatterns) {
189
+ const nodes = this.extractNodesForPattern(ast, pattern, langConfig);
190
+ errorNodes.push(...nodes);
191
+ }
192
+
193
+ return errorNodes;
194
+ }
195
+
196
+ /**
197
+ * JavaScript/TypeScript AST parsing
198
+ */
199
+ parseJavaScriptTypeScript(content, language) {
200
+ // Simplified AST representation for JavaScript/TypeScript
201
+ const tryBlocks = [];
202
+ const lines = content.split('\n');
203
+
204
+ for (let i = 0; i < lines.length; i++) {
205
+ const line = lines[i];
206
+
207
+ // Find try blocks
208
+ if (/\btry\s*\{/.test(line)) {
209
+ const tryBlock = this.extractJSTryBlock(lines, i);
210
+ if (tryBlock) {
211
+ tryBlocks.push({
212
+ type: 'TryStatement',
213
+ startLine: i + 1,
214
+ endLine: tryBlock.endLine,
215
+ tryBlock: tryBlock.tryBody,
216
+ catchBlock: tryBlock.catchBody,
217
+ finallyBlock: tryBlock.finallyBody,
218
+ language: language
219
+ });
220
+ }
221
+ }
222
+ }
223
+
224
+ return { errorHandlingNodes: tryBlocks };
225
+ }
226
+
227
+ /**
228
+ * Java AST parsing
229
+ */
230
+ parseJava(content) {
231
+ const javaNodes = [];
232
+ const lines = content.split('\n');
233
+
234
+ for (let i = 0; i < lines.length; i++) {
235
+ const line = lines[i];
236
+
237
+ // Java try-catch-finally
238
+ if (/\btry\s*\{/.test(line)) {
239
+ const tryBlock = this.extractJavaTryBlock(lines, i);
240
+ if (tryBlock) {
241
+ javaNodes.push({
242
+ type: 'TryStatement',
243
+ startLine: i + 1,
244
+ language: 'java',
245
+ tryBlock: tryBlock.tryBody,
246
+ catchBlocks: tryBlock.catchBlocks, // Java can have multiple catch blocks
247
+ finallyBlock: tryBlock.finallyBody,
248
+ exceptionTypes: tryBlock.exceptionTypes
249
+ });
250
+ }
251
+ }
252
+
253
+ // Method throws declaration
254
+ if (/throws\s+\w+/.test(line)) {
255
+ javaNodes.push({
256
+ type: 'ThrowsDeclaration',
257
+ startLine: i + 1,
258
+ language: 'java',
259
+ exceptions: this.extractThrowsExceptions(line)
260
+ });
261
+ }
262
+ }
263
+
264
+ return { errorHandlingNodes: javaNodes };
265
+ }
266
+
267
+ /**
268
+ * Kotlin AST parsing
269
+ */
270
+ parseKotlin(content) {
271
+ const kotlinNodes = [];
272
+ const lines = content.split('\n');
273
+
274
+ for (let i = 0; i < lines.length; i++) {
275
+ const line = lines[i];
276
+
277
+ // Kotlin try-catch
278
+ if (/\btry\s*\{/.test(line)) {
279
+ const tryBlock = this.extractKotlinTryBlock(lines, i);
280
+ if (tryBlock) {
281
+ kotlinNodes.push({
282
+ type: 'TryExpression',
283
+ startLine: i + 1,
284
+ language: 'kotlin',
285
+ tryBlock: tryBlock.tryBody,
286
+ catchBlock: tryBlock.catchBody,
287
+ finallyBlock: tryBlock.finallyBody,
288
+ isExpression: tryBlock.isExpression
289
+ });
290
+ }
291
+ }
292
+
293
+ // Kotlin Result type usage
294
+ if (/Result</.test(line)) {
295
+ kotlinNodes.push({
296
+ type: 'ResultUsage',
297
+ startLine: i + 1,
298
+ language: 'kotlin',
299
+ resultType: this.extractResultType(line)
300
+ });
301
+ }
302
+ }
303
+
304
+ return { errorHandlingNodes: kotlinNodes };
305
+ }
306
+
307
+ /**
308
+ * Dart AST parsing
309
+ */
310
+ parseDart(content) {
311
+ const dartNodes = [];
312
+ const lines = content.split('\n');
313
+
314
+ for (let i = 0; i < lines.length; i++) {
315
+ const line = lines[i];
316
+
317
+ // Dart try-catch-on
318
+ if (/\btry\s*\{/.test(line)) {
319
+ const tryBlock = this.extractDartTryBlock(lines, i);
320
+ if (tryBlock) {
321
+ dartNodes.push({
322
+ type: 'TryStatement',
323
+ startLine: i + 1,
324
+ language: 'dart',
325
+ tryBlock: tryBlock.tryBody,
326
+ catchBlocks: tryBlock.catchBlocks,
327
+ onBlocks: tryBlock.onBlocks,
328
+ finallyBlock: tryBlock.finallyBody
329
+ });
330
+ }
331
+ }
332
+
333
+ // Future.catchError
334
+ if (/\.catchError/.test(line)) {
335
+ dartNodes.push({
336
+ type: 'FutureCatchError',
337
+ startLine: i + 1,
338
+ language: 'dart',
339
+ handler: this.extractCatchErrorHandler(line)
340
+ });
341
+ }
342
+ }
343
+
344
+ return { errorHandlingNodes: dartNodes };
345
+ }
346
+
347
+ /**
348
+ * Python AST parsing
349
+ */
350
+ parsePython(content) {
351
+ const pythonNodes = [];
352
+ const lines = content.split('\n');
353
+
354
+ for (let i = 0; i < lines.length; i++) {
355
+ const line = lines[i].trim();
356
+
357
+ // Python try-except-finally
358
+ if (/^try:/.test(line)) {
359
+ const tryBlock = this.extractPythonTryBlock(lines, i);
360
+ if (tryBlock) {
361
+ pythonNodes.push({
362
+ type: 'TryStatement',
363
+ startLine: i + 1,
364
+ language: 'python',
365
+ tryBlock: tryBlock.tryBody,
366
+ exceptBlocks: tryBlock.exceptBlocks,
367
+ elseBlock: tryBlock.elseBody,
368
+ finallyBlock: tryBlock.finallyBody
369
+ });
370
+ }
371
+ }
372
+
373
+ // Context manager (with statement)
374
+ if (/^with\s+/.test(line)) {
375
+ pythonNodes.push({
376
+ type: 'WithStatement',
377
+ startLine: i + 1,
378
+ language: 'python',
379
+ contextManager: this.extractContextManager(line)
380
+ });
381
+ }
382
+ }
383
+
384
+ return { errorHandlingNodes: pythonNodes };
385
+ }
386
+
387
+ /**
388
+ * Go AST parsing
389
+ */
390
+ parseGo(content) {
391
+ const goNodes = [];
392
+ const lines = content.split('\n');
393
+
394
+ for (let i = 0; i < lines.length; i++) {
395
+ const line = lines[i];
396
+
397
+ // Go error checking pattern
398
+ if (/if\s+err\s*!=\s*nil/.test(line)) {
399
+ goNodes.push({
400
+ type: 'ErrorCheck',
401
+ startLine: i + 1,
402
+ language: 'go',
403
+ errorVariable: 'err',
404
+ checkPattern: 'nil_comparison'
405
+ });
406
+ }
407
+
408
+ // Panic-recover pattern
409
+ if (/\bpanic\s*\(/.test(line)) {
410
+ goNodes.push({
411
+ type: 'PanicStatement',
412
+ startLine: i + 1,
413
+ language: 'go',
414
+ panicValue: this.extractPanicValue(line)
415
+ });
416
+ }
417
+
418
+ if (/\brecover\s*\(\s*\)/.test(line)) {
419
+ goNodes.push({
420
+ type: 'RecoverStatement',
421
+ startLine: i + 1,
422
+ language: 'go'
423
+ });
424
+ }
425
+ }
426
+
427
+ return { errorHandlingNodes: goNodes };
428
+ }
429
+
430
+ /**
431
+ * Analyze specific error handling node
432
+ */
433
+ analyzeErrorHandlingNode(node, content, langConfig, language) {
434
+ switch (node.type) {
435
+ case 'TryStatement':
436
+ case 'TryExpression':
437
+ return this.analyzeTryStatement(node, content, langConfig, language);
438
+
439
+ case 'ErrorCheck':
440
+ return this.analyzeErrorCheck(node, content, langConfig, language);
441
+
442
+ case 'ThrowsDeclaration':
443
+ return this.analyzeThrowsDeclaration(node, content, langConfig, language);
444
+
445
+ case 'ResultUsage':
446
+ return this.analyzeResultUsage(node, content, langConfig, language);
447
+
448
+ case 'FutureCatchError':
449
+ return this.analyzeFutureCatchError(node, content, langConfig, language);
450
+
451
+ case 'WithStatement':
452
+ return this.analyzeWithStatement(node, content, langConfig, language);
453
+
454
+ case 'PanicStatement':
455
+ return this.analyzePanicStatement(node, content, langConfig, language);
456
+
457
+ default:
458
+ return { isViolation: false, reason: 'unknown_node_type' };
459
+ }
460
+ }
461
+
462
+ /**
463
+ * Analyze try statement (universal pattern)
464
+ */
465
+ analyzeTryStatement(node, content, langConfig, language) {
466
+ const catchBlock = node.catchBlock || node.catchBlocks?.[0];
467
+
468
+ if (!catchBlock || this.isEmpty(catchBlock)) {
469
+ return {
470
+ isViolation: true,
471
+ type: 'empty_catch_block',
472
+ message: `Empty ${langConfig.keywords.catch || 'catch'} block in ${language}`,
473
+ severity: 'error',
474
+ line: node.startLine,
475
+ column: 1,
476
+ code: this.getNodeCode(node, content),
477
+ confidence: 0.9,
478
+ suggestion: this.generateLanguageSpecificSuggestion(language, 'empty_catch'),
479
+ pattern: 'try-catch',
480
+ insight: `Cross-language analysis: Empty error handling detected`
481
+ };
482
+ }
483
+
484
+ // Check for logging
485
+ const hasLogging = this.hasErrorLogging(catchBlock, langConfig, language);
486
+ if (!hasLogging) {
487
+ return {
488
+ isViolation: true,
489
+ type: 'missing_error_logging',
490
+ message: `Missing error logging in ${language} ${langConfig.keywords.catch || 'catch'} block`,
491
+ severity: 'warning',
492
+ line: node.startLine,
493
+ column: 1,
494
+ code: this.getNodeCode(node, content),
495
+ confidence: 0.7,
496
+ suggestion: this.generateLanguageSpecificSuggestion(language, 'add_logging'),
497
+ pattern: 'try-catch',
498
+ insight: `Cross-language best practice: Add error logging for debugging`
499
+ };
500
+ }
501
+
502
+ return { isViolation: false, reason: 'proper_error_handling' };
503
+ }
504
+
505
+ /**
506
+ * Analyze Go error check pattern
507
+ */
508
+ analyzeErrorCheck(node, content, langConfig, language) {
509
+ const lines = content.split('\n');
510
+ const errorHandlingLine = lines[node.startLine]; // Line after the error check
511
+
512
+ if (!errorHandlingLine || errorHandlingLine.trim() === '') {
513
+ return {
514
+ isViolation: true,
515
+ type: 'go_empty_error_handling',
516
+ message: 'Empty error handling after error check in Go',
517
+ severity: 'error',
518
+ line: node.startLine + 1,
519
+ column: 1,
520
+ code: lines.slice(node.startLine - 1, node.startLine + 2).join('\n'),
521
+ confidence: 0.9,
522
+ suggestion: 'Add error handling logic (logging, return, panic, etc.)',
523
+ pattern: 'error-return',
524
+ insight: 'Go idiom: Always handle checked errors explicitly'
525
+ };
526
+ }
527
+
528
+ return { isViolation: false, reason: 'go_error_handled' };
529
+ }
530
+
531
+ /**
532
+ * Generate language-specific suggestions
533
+ */
534
+ generateLanguageSpecificSuggestion(language, violationType) {
535
+ const suggestions = {
536
+ 'javascript': {
537
+ 'empty_catch': 'Add console.error(error) or appropriate error handling',
538
+ 'add_logging': 'Consider adding console.error(error) or using a logging library'
539
+ },
540
+ 'typescript': {
541
+ 'empty_catch': 'Add console.error(error) with proper typing',
542
+ 'add_logging': 'Consider using a typed logging interface'
543
+ },
544
+ 'java': {
545
+ 'empty_catch': 'Add logger.error() or throw new RuntimeException()',
546
+ 'add_logging': 'Use logger.error() or System.err.println()'
547
+ },
548
+ 'kotlin': {
549
+ 'empty_catch': 'Add println(error) or use Result<T> for error handling',
550
+ 'add_logging': 'Consider using logging framework or Result type'
551
+ },
552
+ 'dart': {
553
+ 'empty_catch': 'Add print(error) or rethrow for debugging',
554
+ 'add_logging': 'Use print() for simple cases or logging package'
555
+ },
556
+ 'python': {
557
+ 'empty_catch': 'Add print(e) or logging.error(e)',
558
+ 'add_logging': 'Use logging.exception() to include stack trace'
559
+ },
560
+ 'go': {
561
+ 'empty_catch': 'Add fmt.Printf("Error: %v", err) or return err',
562
+ 'add_logging': 'Use log.Printf() or return the error'
563
+ },
564
+ 'rust': {
565
+ 'empty_catch': 'Use eprintln!() or return Err() for proper error handling',
566
+ 'add_logging': 'Consider using log crate or eprintln! macro'
567
+ }
568
+ };
569
+
570
+ return suggestions[language]?.[violationType] || 'Add appropriate error handling for this language';
571
+ }
572
+
573
+ // Helper methods for language-specific parsing
574
+ extractJSTryBlock(lines, startIndex) {
575
+ // Simplified extraction for JavaScript/TypeScript try blocks
576
+ let braceCount = 0;
577
+ let inTry = false;
578
+ let tryBody = '';
579
+ let catchBody = '';
580
+ let finallyBody = '';
581
+ let currentSection = 'try';
582
+
583
+ for (let i = startIndex; i < lines.length; i++) {
584
+ const line = lines[i];
585
+
586
+ // Count braces to find block boundaries
587
+ braceCount += (line.match(/\{/g) || []).length;
588
+ braceCount -= (line.match(/\}/g) || []).length;
589
+
590
+ if (line.includes('catch')) currentSection = 'catch';
591
+ if (line.includes('finally')) currentSection = 'finally';
592
+
593
+ if (currentSection === 'try') tryBody += line + '\n';
594
+ else if (currentSection === 'catch') catchBody += line + '\n';
595
+ else if (currentSection === 'finally') finallyBody += line + '\n';
596
+
597
+ if (braceCount === 0 && i > startIndex) {
598
+ return {
599
+ endLine: i + 1,
600
+ tryBody: tryBody.trim(),
601
+ catchBody: catchBody.trim(),
602
+ finallyBody: finallyBody.trim()
603
+ };
604
+ }
605
+ }
606
+
607
+ return null;
608
+ }
609
+
610
+ isEmpty(blockContent) {
611
+ if (!blockContent) return true;
612
+
613
+ const cleaned = blockContent
614
+ .replace(/\/\*[\s\S]*?\*\//g, '') // Remove block comments
615
+ .replace(/\/\/.*$/gm, '') // Remove line comments
616
+ .replace(/\s+/g, '') // Remove whitespace
617
+ .replace(/[{}]/g, ''); // Remove braces
618
+
619
+ return cleaned.length === 0;
620
+ }
621
+
622
+ hasErrorLogging(blockContent, langConfig, language) {
623
+ if (!blockContent) return false;
624
+
625
+ // Language-specific logging patterns
626
+ const loggingPatterns = {
627
+ 'javascript': /console\.(log|error|warn|debug)/,
628
+ 'typescript': /console\.(log|error|warn|debug)/,
629
+ 'java': /(logger?\.(error|warn|info|debug)|System\.(err|out)\.print)/,
630
+ 'kotlin': /(println|print|Log\.[dewi])/,
631
+ 'dart': /(print|debugPrint|log\.)/,
632
+ 'python': /(print|logging\.(error|warning|info|debug)|logger\.(error|warning))/,
633
+ 'go': /(fmt\.(Print|Error)|log\.(Print|Error))/,
634
+ 'rust': /(println!|eprintln!|log::|debug!|error!)/
635
+ };
636
+
637
+ const pattern = loggingPatterns[language.toLowerCase()];
638
+ return pattern ? pattern.test(blockContent) : false;
639
+ }
640
+
641
+ getNodeCode(node, content) {
642
+ const lines = content.split('\n');
643
+ const startLine = Math.max(0, node.startLine - 1);
644
+ const endLine = Math.min(lines.length, node.endLine || node.startLine + 2);
645
+
646
+ return lines.slice(startLine, endLine).join('\n');
647
+ }
648
+
649
+ fallbackAnalysis(content, filePath, language, langConfig) {
650
+ // Fallback to simple regex-based analysis when AST parsing fails
651
+ console.log(`📄 Falling back to regex analysis for ${filePath}`);
652
+
653
+ const violations = [];
654
+ const lines = content.split('\n');
655
+
656
+ for (let i = 0; i < lines.length; i++) {
657
+ const line = lines[i];
658
+
659
+ // Universal empty catch pattern
660
+ if (/catch\s*\([^)]*\)\s*\{\s*\}/.test(line)) {
661
+ violations.push({
662
+ ruleId: this.ruleId,
663
+ file: filePath,
664
+ line: i + 1,
665
+ column: 1,
666
+ message: `Empty catch block detected (${language} - fallback analysis)`,
667
+ severity: 'error',
668
+ code: line.trim(),
669
+ type: 'empty_catch_fallback',
670
+ confidence: 0.6,
671
+ suggestion: this.generateLanguageSpecificSuggestion(language, 'empty_catch'),
672
+ language: language,
673
+ crossLanguageInsight: 'Fallback analysis - consider AST-based analysis for better accuracy'
674
+ });
675
+ }
676
+ }
677
+
678
+ return violations;
679
+ }
680
+
681
+ // Placeholder methods for additional language parsers (to be implemented)
682
+ parseRust(content) { return { errorHandlingNodes: [] }; }
683
+ extractJavaTryBlock(lines, startIndex) { return null; }
684
+ extractKotlinTryBlock(lines, startIndex) { return null; }
685
+ extractDartTryBlock(lines, startIndex) { return null; }
686
+ extractPythonTryBlock(lines, startIndex) { return null; }
687
+ extractThrowsExceptions(line) { return []; }
688
+ extractResultType(line) { return ''; }
689
+ extractCatchErrorHandler(line) { return ''; }
690
+ extractContextManager(line) { return ''; }
691
+ extractPanicValue(line) { return ''; }
692
+ extractNodesForPattern(ast, pattern, langConfig) { return []; }
693
+ analyzeThrowsDeclaration(node, content, langConfig, language) { return { isViolation: false }; }
694
+ analyzeResultUsage(node, content, langConfig, language) { return { isViolation: false }; }
695
+ analyzeFutureCatchError(node, content, langConfig, language) { return { isViolation: false }; }
696
+ analyzeWithStatement(node, content, langConfig, language) { return { isViolation: false }; }
697
+ analyzePanicStatement(node, content, langConfig, language) { return { isViolation: false }; }
698
+ }
699
+
700
+ module.exports = new C029MultiLanguageASTEngine();