@sun-asterisk/sunlint 1.2.2 → 1.3.1

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 (124) hide show
  1. package/CHANGELOG.md +107 -1
  2. package/CONTRIBUTING.md +1654 -66
  3. package/README.md +19 -6
  4. package/config/ci-cd.json +54 -0
  5. package/config/development.json +56 -0
  6. package/config/engines/engines-enhanced.json +86 -0
  7. package/config/engines/semantic-config.json +114 -0
  8. package/config/eslint-rule-mapping.json +50 -38
  9. package/config/large-project.json +143 -0
  10. package/config/presets/all.json +0 -1
  11. package/config/release.json +70 -0
  12. package/config/rule-analysis-strategies.js +23 -4
  13. package/config/rules/S027-categories.json +122 -0
  14. package/config/rules/enhanced-rules-registry.json +2564 -0
  15. package/config/rules/rules-registry-generated.json +785 -837
  16. package/config/rules/rules-registry.json +13 -1
  17. package/core/adapters/sunlint-rule-adapter.js +25 -30
  18. package/core/analysis-orchestrator.js +42 -2
  19. package/core/categories.js +52 -0
  20. package/core/category-constants.js +39 -0
  21. package/core/cli-action-handler.js +53 -32
  22. package/core/cli-program.js +11 -3
  23. package/core/config-manager.js +111 -0
  24. package/core/config-merger.js +88 -0
  25. package/core/constants/categories.js +168 -0
  26. package/core/constants/defaults.js +165 -0
  27. package/core/constants/engines.js +185 -0
  28. package/core/constants/index.js +30 -0
  29. package/core/constants/rules.js +215 -0
  30. package/core/enhanced-rules-registry.js +3 -3
  31. package/core/file-targeting-service.js +128 -7
  32. package/core/interfaces/rule-plugin.interface.js +207 -0
  33. package/core/plugin-manager.js +448 -0
  34. package/core/rule-selection-service.js +42 -15
  35. package/core/semantic-engine.js +658 -0
  36. package/core/semantic-rule-base.js +433 -0
  37. package/core/unified-rule-registry.js +484 -0
  38. package/docs/COMMAND-EXAMPLES.md +134 -0
  39. package/docs/CONSTANTS-ARCHITECTURE.md +288 -0
  40. package/docs/LARGE-PROJECT-GUIDE.md +324 -0
  41. package/engines/core/base-engine.js +249 -0
  42. package/engines/engine-factory.js +275 -0
  43. package/engines/eslint-engine.js +171 -19
  44. package/engines/heuristic-engine.js +569 -78
  45. package/integrations/eslint/plugin/index.js +26 -28
  46. package/origin-rules/common-en.md +8 -8
  47. package/package.json +10 -6
  48. package/rules/common/C003_no_vague_abbreviations/analyzer.js +1 -1
  49. package/rules/common/C017_constructor_logic/analyzer.js +254 -17
  50. package/rules/common/C017_constructor_logic/semantic-analyzer.js +340 -0
  51. package/rules/common/C029_catch_block_logging/analyzer.js +17 -5
  52. package/rules/common/C033_separate_service_repository/README.md +78 -0
  53. package/rules/common/C033_separate_service_repository/analyzer.js +160 -0
  54. package/rules/common/C033_separate_service_repository/config.json +50 -0
  55. package/rules/common/C033_separate_service_repository/regex-based-analyzer.js +585 -0
  56. package/rules/common/C033_separate_service_repository/symbol-based-analyzer.js +368 -0
  57. package/rules/common/C035_error_logging_context/STRATEGY.md +99 -0
  58. package/rules/common/C035_error_logging_context/analyzer.js +230 -0
  59. package/rules/common/C035_error_logging_context/config.json +54 -0
  60. package/rules/common/C035_error_logging_context/regex-based-analyzer.js +299 -0
  61. package/rules/common/C035_error_logging_context/symbol-based-analyzer.js +454 -0
  62. package/rules/common/C040_centralized_validation/analyzer.js +165 -0
  63. package/rules/common/C040_centralized_validation/config.json +46 -0
  64. package/rules/common/C040_centralized_validation/regex-based-analyzer.js +243 -0
  65. package/rules/common/C040_centralized_validation/symbol-based-analyzer.js +416 -0
  66. package/rules/common/C047_no_duplicate_retry_logic/c047-semantic-rule.js +278 -0
  67. package/rules/common/C047_no_duplicate_retry_logic/symbol-analyzer-enhanced.js +968 -0
  68. package/rules/common/C047_no_duplicate_retry_logic/symbol-config.json +71 -0
  69. package/rules/common/{C076_single_test_behavior → C072_single_test_behavior}/analyzer.js +6 -6
  70. package/rules/common/C076_explicit_function_types/README.md +30 -0
  71. package/rules/common/C076_explicit_function_types/analyzer.js +172 -0
  72. package/rules/common/C076_explicit_function_types/config.json +15 -0
  73. package/rules/common/C076_explicit_function_types/semantic-analyzer.js +341 -0
  74. package/rules/index.js +8 -0
  75. package/rules/parser/rule-parser.js +13 -2
  76. package/rules/security/S005_no_origin_auth/README.md +226 -0
  77. package/rules/security/S005_no_origin_auth/analyzer.js +184 -0
  78. package/rules/security/S005_no_origin_auth/ast-analyzer.js +406 -0
  79. package/rules/security/S005_no_origin_auth/config.json +85 -0
  80. package/rules/security/S006_no_plaintext_recovery_codes/README.md +139 -0
  81. package/rules/security/S006_no_plaintext_recovery_codes/analyzer.js +306 -0
  82. package/rules/security/S006_no_plaintext_recovery_codes/config.json +48 -0
  83. package/rules/security/S007_no_plaintext_otp/README.md +198 -0
  84. package/rules/security/S007_no_plaintext_otp/analyzer.js +406 -0
  85. package/rules/security/S007_no_plaintext_otp/config.json +79 -0
  86. package/rules/security/S007_no_plaintext_otp/semantic-analyzer.js +609 -0
  87. package/rules/security/S007_no_plaintext_otp/semantic-config.json +195 -0
  88. package/rules/security/S007_no_plaintext_otp/semantic-wrapper.js +280 -0
  89. package/rules/security/S027_no_hardcoded_secrets/analyzer.js +180 -366
  90. package/rules/security/S027_no_hardcoded_secrets/categories.json +153 -0
  91. package/rules/security/S027_no_hardcoded_secrets/categorized-analyzer.js +250 -0
  92. package/scripts/category-manager.js +150 -0
  93. package/scripts/generate-rules-registry.js +88 -0
  94. package/scripts/migrate-rule-registry.js +157 -0
  95. package/scripts/prepare-release.sh +1 -1
  96. package/scripts/validate-system.js +48 -0
  97. package/.sunlint.json +0 -35
  98. package/config/README.md +0 -88
  99. package/config/engines/eslint-rule-mapping.json +0 -74
  100. package/config/schemas/sunlint-schema.json +0 -0
  101. package/config/testing/test-s005-working.ts +0 -22
  102. package/core/multi-rule-runner.js +0 -0
  103. package/docs/ESLINT-INTEGRATION-STRATEGY.md +0 -392
  104. package/docs/FUTURE_PACKAGES.md +0 -83
  105. package/docs/HEURISTIC_VS_AI.md +0 -113
  106. package/docs/PRODUCTION_DEPLOYMENT_ANALYSIS.md +0 -112
  107. package/docs/PRODUCTION_SIZE_IMPACT.md +0 -183
  108. package/docs/RELEASE_GUIDE.md +0 -230
  109. package/docs/STANDARDIZED-CATEGORY-FILTERING.md +0 -156
  110. package/engines/tree-sitter-parser.js +0 -0
  111. package/engines/universal-ast-engine.js +0 -0
  112. package/integrations/eslint/plugin/rules/common/c076-single-behavior-per-test.js +0 -254
  113. package/rules/common/C029_catch_block_logging/analyzer-backup.js +0 -426
  114. package/rules/common/C029_catch_block_logging/analyzer-fixed.js +0 -130
  115. package/rules/common/C029_catch_block_logging/analyzer-multi-tech.js +0 -487
  116. package/rules/common/C029_catch_block_logging/analyzer-simple.js +0 -110
  117. package/rules/common/C029_catch_block_logging/ast-analyzer-backup.js +0 -441
  118. package/rules/common/C029_catch_block_logging/ast-analyzer-new.js +0 -127
  119. package/rules/common/C029_catch_block_logging/ast-analyzer.js +0 -133
  120. package/rules/common/C029_catch_block_logging/cfg-analyzer.js +0 -408
  121. package/rules/common/C029_catch_block_logging/dataflow-analyzer.js +0 -454
  122. package/rules/common/C029_catch_block_logging/multi-language-ast-engine.js +0 -700
  123. package/rules/common/C029_catch_block_logging/pattern-learning-analyzer.js +0 -568
  124. package/rules/common/C029_catch_block_logging/semantic-analyzer.js +0 -459
@@ -1,110 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
-
4
- class C029Analyzer {
5
- constructor() {
6
- this.ruleId = 'C029';
7
- this.ruleName = 'Enhanced Catch Block Error Logging';
8
- this.description = 'Smart 3-stage pipeline: Regex → AST → Data Flow analysis';
9
-
10
- // Load smart pipeline analyzer
11
- this.smartPipeline = null;
12
-
13
- try {
14
- this.smartPipeline = require('./analyzer-smart-pipeline.js');
15
- console.log('🎯 C029: Smart Pipeline loaded (Regex → AST → Data Flow)');
16
- } catch (error) {
17
- console.warn('⚠️ C029: Smart Pipeline failed, falling back to basic analysis:', error.message);
18
- }
19
- }
20
-
21
- async analyze(files, language, options = {}) {
22
- // Use smart pipeline for optimal performance and accuracy
23
- if (this.smartPipeline) {
24
- console.log('🎯 C029: Using Smart Pipeline (Regex → AST → Data Flow)...');
25
- return await this.smartPipeline.analyze(files, language, options);
26
- } else {
27
- console.log('🔍 C029: Using basic regex analysis (fallback)...');
28
- return await this.analyzeWithRegex(files, language, options);
29
- }
30
- }
31
-
32
- async analyzeWithRegex(files, language, options = {}) {
33
- const violations = [];
34
-
35
- for (const filePath of files) {
36
- if (options.verbose) {
37
- console.log(`Analyzing ${path.basename(filePath)}...`);
38
- }
39
-
40
- try {
41
- const content = fs.readFileSync(filePath, 'utf8');
42
- const lines = content.split('\n');
43
-
44
- for (let i = 0; i < lines.length; i++) {
45
- const line = lines[i];
46
-
47
- // Basic catch block detection
48
- if (/catch\s*\([^)]*\)\s*\{/.test(line)) {
49
- const catchBlock = this.extractBasicCatchBlock(lines, i);
50
-
51
- if (this.isBasicViolation(catchBlock, filePath)) {
52
- violations.push({
53
- ruleId: this.ruleId,
54
- file: filePath,
55
- line: i + 1,
56
- column: 1,
57
- message: 'Catch block should log error information',
58
- severity: 'error',
59
- code: line.trim(),
60
- suggestion: 'Add console.error() or appropriate logging'
61
- });
62
- }
63
- }
64
- }
65
- } catch (error) {
66
- console.warn(`Error analyzing ${filePath}:`, error.message);
67
- }
68
- }
69
-
70
- return violations;
71
- }
72
-
73
- extractBasicCatchBlock(lines, startIndex) {
74
- let braceCount = 0;
75
- let content = '';
76
-
77
- for (let i = startIndex; i < lines.length; i++) {
78
- const line = lines[i];
79
- content += line + '\n';
80
-
81
- braceCount += (line.match(/\{/g) || []).length;
82
- braceCount -= (line.match(/\}/g) || []).length;
83
-
84
- if (braceCount === 0 && i > startIndex) {
85
- break;
86
- }
87
- }
88
-
89
- return content.trim();
90
- }
91
-
92
- isBasicViolation(catchContent, filePath) {
93
- // Skip test files
94
- if (/(__tests__|\.test\.|\.spec\.|\/test\/|\/tests\/)/.test(filePath)) {
95
- return false;
96
- }
97
-
98
- // Empty catch block
99
- if (!catchContent || catchContent.replace(/[{}\s]/g, '').length === 0) {
100
- return true;
101
- }
102
-
103
- // No logging patterns
104
- const hasLogging = /console\.(log|error|warn|debug)|logger?\.(error|warn|info|debug)/.test(catchContent);
105
-
106
- return !hasLogging;
107
- }
108
- }
109
-
110
- module.exports = new C029Analyzer();
@@ -1,441 +0,0 @@
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();
@@ -1,127 +0,0 @@
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;