@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,129 @@
1
+ /**
2
+ * C029 Analyzer - Smart Pipeline Integration
3
+ *
4
+ * This analyzer forwards to the Smart Pipeline for superior accuracy and performance
5
+ */
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+
10
+ class C029Analyzer {
11
+ constructor() {
12
+ this.ruleId = 'C029';
13
+ this.ruleName = 'Enhanced Catch Block Error Logging';
14
+ this.description = 'Mọi catch block phải log nguyên nhân lỗi đầy đủ và bảo toàn context (Smart Pipeline 3-stage analysis)';
15
+
16
+ // Load Smart Pipeline as primary analyzer
17
+ this.smartPipeline = null;
18
+
19
+ try {
20
+ this.smartPipeline = require('./analyzer-smart-pipeline.js');
21
+ console.log('🎯 C029: Smart Pipeline loaded (3-stage: Regex → AST → Data Flow)');
22
+ } catch (error) {
23
+ console.warn('⚠️ C029: Smart Pipeline failed, using fallback:', error.message);
24
+ this.smartPipeline = null;
25
+ }
26
+ }
27
+
28
+ async analyze(files, language, options = {}) {
29
+ // Use Smart Pipeline as primary choice
30
+ if (this.smartPipeline) {
31
+ console.log('🎯 C029: Using Smart Pipeline (3-stage analysis)...');
32
+ return await this.smartPipeline.analyze(files, language, options);
33
+ } else {
34
+ console.log('🔍 C029: Using fallback regex analysis...');
35
+ return await this.analyzeWithRegex(files, language, options);
36
+ }
37
+ }
38
+
39
+ async analyzeWithRegex(files, language, options = {}) {
40
+ const violations = [];
41
+
42
+ for (const filePath of files) {
43
+ if (options.verbose) {
44
+ console.log(`🔍 C029 Regex: Processing ${path.basename(filePath)}...`);
45
+ }
46
+
47
+ try {
48
+ const content = fs.readFileSync(filePath, 'utf8');
49
+ const fileViolations = await this.analyzeFile(filePath, content, language);
50
+ violations.push(...fileViolations);
51
+ } catch (error) {
52
+ console.warn(`⚠️ C029: Error processing ${filePath}:`, error.message);
53
+ }
54
+ }
55
+
56
+ return violations;
57
+ }
58
+
59
+ async analyzeFile(filePath, content, language) {
60
+ const violations = [];
61
+ const lines = content.split('\n');
62
+
63
+ for (let i = 0; i < lines.length; i++) {
64
+ const line = lines[i];
65
+
66
+ // Simple catch block detection
67
+ if (line.includes('catch') && line.includes('(')) {
68
+ const catchBlock = this.extractCatchBlock(lines, i);
69
+
70
+ if (this.isCatchBlockEmpty(catchBlock.content)) {
71
+ violations.push({
72
+ file: filePath,
73
+ line: i + 1,
74
+ column: line.indexOf('catch') + 1,
75
+ message: 'Empty catch block detected',
76
+ severity: 'error',
77
+ ruleId: this.ruleId,
78
+ type: 'empty_catch'
79
+ });
80
+ }
81
+ }
82
+ }
83
+
84
+ return violations;
85
+ }
86
+
87
+ extractCatchBlock(lines, startIndex) {
88
+ const content = [];
89
+ let braceCount = 0;
90
+ let inBlock = false;
91
+
92
+ for (let i = startIndex; i < lines.length; i++) {
93
+ const line = lines[i];
94
+ content.push(line);
95
+
96
+ for (const char of line) {
97
+ if (char === '{') {
98
+ braceCount++;
99
+ inBlock = true;
100
+ } else if (char === '}') {
101
+ braceCount--;
102
+ if (braceCount === 0 && inBlock) {
103
+ return { content, endIndex: i };
104
+ }
105
+ }
106
+ }
107
+ }
108
+
109
+ return { content, endIndex: startIndex };
110
+ }
111
+
112
+ isCatchBlockEmpty(content) {
113
+ const blockContent = content.join('\n');
114
+
115
+ // Remove comments and whitespace
116
+ const cleanContent = blockContent
117
+ .replace(/\/\*[\s\S]*?\*\//g, '') // Remove multi-line comments
118
+ .replace(/\/\/.*$/gm, '') // Remove single-line comments
119
+ .replace(/\s+/g, ' ') // Normalize whitespace
120
+ .trim();
121
+
122
+ // Check if only contains catch declaration and braces
123
+ const hasOnlyStructure = /^catch\s*\([^)]*\)\s*\{\s*\}$/.test(cleanContent);
124
+
125
+ return hasOnlyStructure;
126
+ }
127
+ }
128
+
129
+ module.exports = C029Analyzer;
@@ -0,0 +1,441 @@
1
+ /**
2
+ * AST-based C029 Analyzer - Smart Pipeline Integration
3
+ *
4
+ * This analyzer forwards to the Smart Pipeline for superior accuracy and performance
5
+ * Maintains compatibility with the heuristic engine's AST expectations
6
+ */
7
+
8
+ class C029ASTAnalyzer {
9
+ constructor() {
10
+ this.ruleId = 'C029';
11
+ this.ruleName = 'AST-Enhanced Catch Block Error Logging (Smart Pipeline)';
12
+ this.description = 'Catch blocks must log errors - using Smart Pipeline 3-stage analysis';
13
+
14
+ // Load Smart Pipeline
15
+ this.smartPipeline = null;
16
+
17
+ try {
18
+ this.smartPipeline = require('./analyzer-smart-pipeline.js');
19
+ console.log('🎯 C029 AST: Smart Pipeline loaded (Regex → AST → Data Flow)');
20
+ } catch (error) {
21
+ console.warn('⚠️ C029 AST: Smart Pipeline failed:', error.message);
22
+ this.smartPipeline = null;
23
+ }
24
+ }
25
+
26
+ async analyze(files, language, options = {}) {
27
+ // Use Smart Pipeline if available
28
+ if (this.smartPipeline) {
29
+ console.log('🎯 C029 AST: Using Smart Pipeline (3-stage analysis)...');
30
+ return await this.smartPipeline.analyze(files, language, options);
31
+ } else {
32
+ console.log('🔍 C029 AST: Using fallback analysis...');
33
+ return await this.fallbackAnalysis(files, language, options);
34
+ }
35
+ }
36
+
37
+ async fallbackAnalysis(files, language, options = {}) {
38
+ const violations = [];
39
+ const fs = require('fs');
40
+ const path = require('path');
41
+
42
+ console.log(`🔍 C029 AST: Processing ${files.length} files with fallback analysis...`);
43
+
44
+ for (const filePath of files) {
45
+ try {
46
+ const content = fs.readFileSync(filePath, 'utf8');
47
+ const fileViolations = await this.analyzeFile(filePath, content, language);
48
+ violations.push(...fileViolations);
49
+ } catch (error) {
50
+ console.warn(`⚠️ C029 AST: Error processing ${filePath}:`, error.message);
51
+ }
52
+ }
53
+
54
+ return violations;
55
+ }
56
+
57
+ async analyzeFile(filePath, content, language) {
58
+ const fileLanguage = this.getLanguageFromPath(filePath);
59
+ const ast = await this.parseAST(content, fileLanguage, filePath);
60
+
61
+ if (ast) {
62
+ const astViolations = this.analyzeCatchBlocksWithAST(ast, catchBlocks, filePath, content);
63
+ violations.push(...astViolations);
64
+ } else {
65
+ // Fallback to regex if AST fails
66
+ const regexViolations = this.analyzeCatchBlocksWithRegex(catchBlocks, filePath, content);
67
+ violations.push(...regexViolations);
68
+ }
69
+
70
+ } catch (error) {
71
+ console.warn(`C029 AST skipping ${filePath}: ${error.message}`);
72
+ }
73
+ }
74
+
75
+ // Give Node.js a chance to breathe between batches
76
+ if (i + batchSize < totalFiles) {
77
+ await new Promise(resolve => setImmediate(resolve));
78
+ }
79
+ }
80
+
81
+ return violations;
82
+ }
83
+
84
+ /**
85
+ * Step 1: Fast regex-based catch block detection
86
+ */
87
+ findCatchBlocksWithRegex(content, filePath) {
88
+ const catchBlocks = [];
89
+ const lines = content.split('\n');
90
+
91
+ lines.forEach((line, index) => {
92
+ const trimmedLine = line.trim();
93
+ if (this.isCatchBlockStart(trimmedLine)) {
94
+ // Extract catch block info using existing logic
95
+ const catchBlockInfo = this.extractCatchBlockInfo(lines, index);
96
+ catchBlocks.push({
97
+ startLine: index + 1,
98
+ content: catchBlockInfo.content,
99
+ parameter: this.extractCatchParameter(trimmedLine)
100
+ });
101
+ }
102
+ });
103
+
104
+ return catchBlocks;
105
+ }
106
+
107
+ /**
108
+ * Step 2: AST-based analysis for precise content understanding
109
+ */
110
+ async parseAST(content, language, filePath) {
111
+ try {
112
+ if (!this.astRegistry || !this.astRegistry.parseCode) {
113
+ return null;
114
+ }
115
+
116
+ return await this.astRegistry.parseCode(content, language, filePath);
117
+ } catch (error) {
118
+ // AST parsing failed, fall back to regex
119
+ return null;
120
+ }
121
+ }
122
+
123
+ analyzeCatchBlocksWithAST(ast, catchBlocks, filePath, content) {
124
+ const violations = [];
125
+ const isTestFile = this.isTestFile(filePath);
126
+
127
+ for (const catchBlock of catchBlocks) {
128
+ const analysis = this.analyzeSingleCatchBlock(ast, catchBlock, filePath, content, isTestFile);
129
+
130
+ if (analysis.isViolation) {
131
+ violations.push({
132
+ ruleId: this.ruleId,
133
+ file: filePath,
134
+ line: catchBlock.startLine,
135
+ column: 1,
136
+ message: analysis.message,
137
+ severity: analysis.severity,
138
+ code: catchBlock.content.split('\n')[0].trim(),
139
+ type: analysis.type,
140
+ confidence: analysis.confidence,
141
+ suggestion: analysis.suggestion
142
+ });
143
+ }
144
+ }
145
+
146
+ return violations;
147
+ }
148
+
149
+ analyzeSingleCatchBlock(ast, catchBlock, filePath, content, isTestFile) {
150
+ const parameter = catchBlock.parameter;
151
+ const catchContent = catchBlock.content.toLowerCase();
152
+ const originalContent = catchBlock.content;
153
+
154
+ // 1. Check for explicit ignore patterns
155
+ if (this.config.allowExplicitIgnore && this.isExplicitlyIgnored(parameter, catchContent)) {
156
+ return { isViolation: false, reason: 'explicitly_ignored' };
157
+ }
158
+
159
+ // 2. Empty catch block - always violation
160
+ if (this.isCatchBlockEmpty(catchContent)) {
161
+ return {
162
+ isViolation: true,
163
+ type: 'empty_catch_block',
164
+ message: 'Empty catch block - error is silently ignored',
165
+ severity: 'error',
166
+ confidence: 1.0,
167
+ suggestion: 'Add error logging or explicit ignore comment'
168
+ };
169
+ }
170
+
171
+ // 3. Test file context - allow test assertions
172
+ if (isTestFile && this.config.allowInTests && this.hasTestAssertions(catchContent, originalContent)) {
173
+ return { isViolation: false, reason: 'test_assertions' };
174
+ }
175
+
176
+ // 4. Redux/async patterns - allow thunk error handling
177
+ if (this.config.allowReduxPatterns && this.hasReduxErrorHandling(catchContent, originalContent)) {
178
+ return { isViolation: false, reason: 'redux_patterns' };
179
+ }
180
+
181
+ // 5. Check for logging or rethrowing
182
+ if (this.hasLoggingOrRethrow(catchContent, originalContent)) {
183
+ return { isViolation: false, reason: 'has_logging' };
184
+ }
185
+
186
+ // 6. Silent catch block - violation
187
+ return {
188
+ isViolation: true,
189
+ type: 'silent_catch_block',
190
+ message: 'Catch block must log error or rethrow - silent error handling hides bugs',
191
+ severity: 'error',
192
+ confidence: 0.9,
193
+ suggestion: 'Add error logging (console.error, logger.error) or rethrow the error'
194
+ };
195
+ }
196
+
197
+ /**
198
+ * Language-specific explicit ignore patterns
199
+ */
200
+ isExplicitlyIgnored(parameter, content) {
201
+ // Java style: catch(Exception ignored)
202
+ if (parameter && parameter.includes('ignored')) {
203
+ return true;
204
+ }
205
+
206
+ // Dart style: catch(_)
207
+ if (parameter && parameter.trim() === '_') {
208
+ return true;
209
+ }
210
+
211
+ // Explicit ignore comments
212
+ const ignorePatterns = [
213
+ '// ignore',
214
+ '/* ignore',
215
+ '// todo',
216
+ '// intentionally',
217
+ '// suppress'
218
+ ];
219
+
220
+ return ignorePatterns.some(pattern => content.includes(pattern));
221
+ }
222
+
223
+ /**
224
+ * Test file detection
225
+ */
226
+ isTestFile(filePath) {
227
+ const testPatterns = [
228
+ '__tests__',
229
+ '.test.',
230
+ '.spec.',
231
+ '/test/',
232
+ '/tests/',
233
+ 'test-',
234
+ 'spec-'
235
+ ];
236
+
237
+ return testPatterns.some(pattern => filePath.includes(pattern));
238
+ }
239
+
240
+ /**
241
+ * Enhanced test assertion detection
242
+ */
243
+ hasTestAssertions(content, originalContent) {
244
+ const testPatterns = [
245
+ 'expect(',
246
+ 'assert(',
247
+ 'should.',
248
+ 'toequal(',
249
+ 'tobecalled',
250
+ 'tocontain',
251
+ 'tohavebeencalled',
252
+ 'tobe(',
253
+ 'chai.',
254
+ 'sinon.',
255
+ 'jest.'
256
+ ];
257
+
258
+ return testPatterns.some(pattern =>
259
+ content.includes(pattern.toLowerCase()) ||
260
+ originalContent.toLowerCase().includes(pattern.toLowerCase())
261
+ );
262
+ }
263
+
264
+ /**
265
+ * Redux/async error handling patterns
266
+ */
267
+ hasReduxErrorHandling(content, originalContent) {
268
+ const reduxPatterns = [
269
+ 'rejectwithvalue',
270
+ 'dispatch(',
271
+ 'handleaxioserror',
272
+ 'seterror(',
273
+ 'return value',
274
+ 'thunk'
275
+ ];
276
+
277
+ return reduxPatterns.some(pattern =>
278
+ content.includes(pattern.toLowerCase()) ||
279
+ originalContent.toLowerCase().includes(pattern.toLowerCase())
280
+ );
281
+ }
282
+
283
+ /**
284
+ * Enhanced logging detection
285
+ */
286
+ hasLoggingOrRethrow(content, originalContent) {
287
+ // Throw statements
288
+ if (content.includes('throw ') || content.includes('throw;')) {
289
+ return true;
290
+ }
291
+
292
+ // Function calls with error parameter - likely error handlers
293
+ const errorHandlerPatterns = [
294
+ 'handle',
295
+ 'process',
296
+ 'report',
297
+ 'track',
298
+ 'capture',
299
+ 'send',
300
+ 'notify'
301
+ ];
302
+
303
+ // Check for function calls that take error as parameter
304
+ if (originalContent.match(/\w+\s*\(\s*error?\s*\)/i)) {
305
+ return true;
306
+ }
307
+
308
+ // Check for error handler function calls
309
+ if (errorHandlerPatterns.some(pattern =>
310
+ originalContent.toLowerCase().includes(pattern + 'error') ||
311
+ originalContent.toLowerCase().includes(pattern + '(error') ||
312
+ originalContent.toLowerCase().includes(pattern + '(err')
313
+ )) {
314
+ return true;
315
+ }
316
+
317
+ // Logging patterns
318
+ const loggingPatterns = [
319
+ 'console.error',
320
+ 'console.log',
321
+ 'console.warn',
322
+ 'logger.error',
323
+ 'log.error',
324
+ 'logger.warn',
325
+ 'winston.error',
326
+ 'bunyan.error',
327
+ 'pino.error',
328
+ '.error(',
329
+ '.warn(',
330
+ '.log(',
331
+ 'handleerror',
332
+ 'logerror'
333
+ ];
334
+
335
+ return loggingPatterns.some(pattern =>
336
+ content.includes(pattern) || originalContent.includes(pattern)
337
+ );
338
+ }
339
+
340
+ // Utility methods (reuse existing logic)
341
+ isCatchBlockStart(line) {
342
+ return line.includes('catch (') || line.includes('catch(');
343
+ }
344
+
345
+ extractCatchParameter(line) {
346
+ const match = line.match(/catch\s*\(\s*([^)]+)\s*\)/);
347
+ return match ? match[1].trim() : null;
348
+ }
349
+
350
+ extractCatchBlockInfo(lines, startIndex) {
351
+ const catchBlockLines = [];
352
+ let braceDepth = 0;
353
+ let foundCatchBrace = false;
354
+
355
+ for (let i = startIndex; i < lines.length; i++) {
356
+ const line = lines[i];
357
+ catchBlockLines.push(line);
358
+
359
+ if (this.isCatchBlockStart(line.trim())) {
360
+ for (const char of line) {
361
+ if (char === '{') {
362
+ foundCatchBrace = true;
363
+ braceDepth = 1;
364
+ }
365
+ }
366
+ } else if (foundCatchBrace) {
367
+ for (const char of line) {
368
+ if (char === '{') braceDepth++;
369
+ else if (char === '}') braceDepth--;
370
+ }
371
+
372
+ if (braceDepth === 0) break;
373
+ }
374
+ }
375
+
376
+ return {
377
+ content: catchBlockLines.join('\n')
378
+ };
379
+ }
380
+
381
+ isCatchBlockEmpty(content) {
382
+ const cleanedContent = content
383
+ .replace(/\/\*[\s\S]*?\*\//g, '') // Remove block comments
384
+ .replace(/\/\/.*$/gm, '') // Remove line comments
385
+ .replace(/\s+/g, ' ') // Normalize whitespace
386
+ .trim();
387
+
388
+ // Check if only braces remain
389
+ const bodyMatch = cleanedContent.match(/\{(.*)\}/s);
390
+ return !bodyMatch || bodyMatch[1].trim().length === 0;
391
+ }
392
+
393
+ getLanguageFromPath(filePath) {
394
+ const ext = filePath.split('.').pop().toLowerCase();
395
+
396
+ const languageMap = {
397
+ 'js': 'javascript',
398
+ 'jsx': 'javascript',
399
+ 'ts': 'typescript',
400
+ 'tsx': 'typescript',
401
+ 'mjs': 'javascript',
402
+ 'cjs': 'javascript',
403
+ 'dart': 'dart',
404
+ 'java': 'java',
405
+ 'kt': 'kotlin'
406
+ };
407
+
408
+ return languageMap[ext] || 'javascript';
409
+ }
410
+
411
+ /**
412
+ * Fallback regex analysis if AST fails
413
+ */
414
+ analyzeCatchBlocksWithRegex(catchBlocks, filePath, content) {
415
+ const violations = [];
416
+ const isTestFile = this.isTestFile(filePath);
417
+
418
+ for (const catchBlock of catchBlocks) {
419
+ const analysis = this.analyzeSingleCatchBlock(null, catchBlock, filePath, content, isTestFile);
420
+
421
+ if (analysis.isViolation) {
422
+ violations.push({
423
+ ruleId: this.ruleId,
424
+ file: filePath,
425
+ line: catchBlock.startLine,
426
+ column: 1,
427
+ message: analysis.message,
428
+ severity: analysis.severity,
429
+ code: catchBlock.content.split('\n')[0].trim(),
430
+ type: analysis.type,
431
+ confidence: analysis.confidence * 0.8, // Lower confidence for regex fallback
432
+ suggestion: analysis.suggestion
433
+ });
434
+ }
435
+ }
436
+
437
+ return violations;
438
+ }
439
+ }
440
+
441
+ module.exports = new C029ASTAnalyzer();
@@ -0,0 +1,127 @@
1
+ /**
2
+ * AST-based C029 Analyzer - Smart Pipeline Integration
3
+ *
4
+ * This analyzer forwards to the Smart Pipeline for superior accuracy and performance
5
+ * Maintains compatibility with the heuristic engine's AST expectations
6
+ */
7
+
8
+ class C029ASTAnalyzer {
9
+ constructor() {
10
+ this.ruleId = 'C029';
11
+ this.ruleName = 'AST-Enhanced Catch Block Error Logging (Smart Pipeline)';
12
+ this.description = 'Catch blocks must log errors - using Smart Pipeline 3-stage analysis';
13
+
14
+ // Load Smart Pipeline
15
+ this.smartPipeline = null;
16
+
17
+ try {
18
+ this.smartPipeline = require('./analyzer-smart-pipeline.js');
19
+ console.log('🎯 C029 AST: Smart Pipeline loaded (Regex → AST → Data Flow)');
20
+ } catch (error) {
21
+ console.warn('⚠️ C029 AST: Smart Pipeline failed:', error.message);
22
+ this.smartPipeline = null;
23
+ }
24
+ }
25
+
26
+ async analyze(files, language, options = {}) {
27
+ // Use Smart Pipeline if available
28
+ if (this.smartPipeline) {
29
+ console.log('🎯 C029 AST: Using Smart Pipeline (3-stage analysis)...');
30
+ return await this.smartPipeline.analyze(files, language, options);
31
+ } else {
32
+ console.log('🔍 C029 AST: Using fallback analysis...');
33
+ return await this.fallbackAnalysis(files, language, options);
34
+ }
35
+ }
36
+
37
+ async fallbackAnalysis(files, language, options = {}) {
38
+ const violations = [];
39
+ const fs = require('fs');
40
+ const path = require('path');
41
+
42
+ console.log(`🔍 C029 AST: Processing ${files.length} files with fallback analysis...`);
43
+
44
+ for (const filePath of files) {
45
+ try {
46
+ const content = fs.readFileSync(filePath, 'utf8');
47
+ const fileViolations = await this.analyzeFile(filePath, content, language);
48
+ violations.push(...fileViolations);
49
+ } catch (error) {
50
+ console.warn(`⚠️ C029 AST: Error processing ${filePath}:`, error.message);
51
+ }
52
+ }
53
+
54
+ return violations;
55
+ }
56
+
57
+ async analyzeFile(filePath, content, language) {
58
+ const violations = [];
59
+ const lines = content.split('\n');
60
+
61
+ for (let i = 0; i < lines.length; i++) {
62
+ const line = lines[i];
63
+
64
+ // Simple catch block detection for fallback
65
+ if (line.includes('catch') && line.includes('(')) {
66
+ const catchBlock = this.extractCatchBlock(lines, i);
67
+
68
+ if (this.isCatchBlockEmpty(catchBlock.content)) {
69
+ violations.push({
70
+ file: filePath,
71
+ line: i + 1,
72
+ column: line.indexOf('catch') + 1,
73
+ message: 'Empty catch block detected (fallback analysis)',
74
+ severity: 'error',
75
+ ruleId: this.ruleId,
76
+ type: 'empty_catch_fallback'
77
+ });
78
+ }
79
+ }
80
+ }
81
+
82
+ return violations;
83
+ }
84
+
85
+ extractCatchBlock(lines, startIndex) {
86
+ const content = [];
87
+ let braceCount = 0;
88
+ let inBlock = false;
89
+
90
+ for (let i = startIndex; i < lines.length; i++) {
91
+ const line = lines[i];
92
+ content.push(line);
93
+
94
+ for (const char of line) {
95
+ if (char === '{') {
96
+ braceCount++;
97
+ inBlock = true;
98
+ } else if (char === '}') {
99
+ braceCount--;
100
+ if (braceCount === 0 && inBlock) {
101
+ return { content, endIndex: i };
102
+ }
103
+ }
104
+ }
105
+ }
106
+
107
+ return { content, endIndex: startIndex };
108
+ }
109
+
110
+ isCatchBlockEmpty(content) {
111
+ const blockContent = content.join('\n');
112
+
113
+ // Remove comments and whitespace
114
+ const cleanContent = blockContent
115
+ .replace(/\/\*[\s\S]*?\*\//g, '') // Remove multi-line comments
116
+ .replace(/\/\/.*$/gm, '') // Remove single-line comments
117
+ .replace(/\s+/g, ' ') // Normalize whitespace
118
+ .trim();
119
+
120
+ // Check if only contains catch declaration and braces
121
+ const hasOnlyStructure = /^catch\s*\([^)]*\)\s*\{\s*\}$/.test(cleanContent);
122
+
123
+ return hasOnlyStructure;
124
+ }
125
+ }
126
+
127
+ module.exports = C029ASTAnalyzer;