@sun-asterisk/sunlint 1.2.2 → 1.3.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 (64) hide show
  1. package/CHANGELOG.md +40 -1
  2. package/CONTRIBUTING.md +533 -70
  3. package/README.md +16 -2
  4. package/config/engines/engines-enhanced.json +86 -0
  5. package/config/engines/semantic-config.json +114 -0
  6. package/config/eslint-rule-mapping.json +50 -38
  7. package/config/rules/enhanced-rules-registry.json +2503 -0
  8. package/config/rules/rules-registry-generated.json +785 -837
  9. package/core/adapters/sunlint-rule-adapter.js +25 -30
  10. package/core/analysis-orchestrator.js +42 -2
  11. package/core/categories.js +52 -0
  12. package/core/category-constants.js +39 -0
  13. package/core/cli-action-handler.js +32 -5
  14. package/core/config-manager.js +111 -0
  15. package/core/config-merger.js +61 -0
  16. package/core/constants/categories.js +168 -0
  17. package/core/constants/defaults.js +165 -0
  18. package/core/constants/engines.js +185 -0
  19. package/core/constants/index.js +30 -0
  20. package/core/constants/rules.js +215 -0
  21. package/core/file-targeting-service.js +128 -7
  22. package/core/interfaces/rule-plugin.interface.js +207 -0
  23. package/core/plugin-manager.js +448 -0
  24. package/core/rule-selection-service.js +42 -15
  25. package/core/semantic-engine.js +560 -0
  26. package/core/semantic-rule-base.js +433 -0
  27. package/core/unified-rule-registry.js +484 -0
  28. package/docs/CONSTANTS-ARCHITECTURE.md +288 -0
  29. package/engines/core/base-engine.js +249 -0
  30. package/engines/engine-factory.js +275 -0
  31. package/engines/eslint-engine.js +171 -19
  32. package/engines/heuristic-engine.js +511 -78
  33. package/integrations/eslint/plugin/index.js +27 -27
  34. package/package.json +10 -6
  35. package/rules/common/C003_no_vague_abbreviations/analyzer.js +1 -1
  36. package/rules/common/C029_catch_block_logging/analyzer.js +17 -5
  37. package/rules/common/C047_no_duplicate_retry_logic/c047-semantic-rule.js +278 -0
  38. package/rules/common/C047_no_duplicate_retry_logic/symbol-analyzer-enhanced.js +968 -0
  39. package/rules/common/C047_no_duplicate_retry_logic/symbol-config.json +71 -0
  40. package/rules/index.js +7 -0
  41. package/scripts/category-manager.js +150 -0
  42. package/scripts/generate-rules-registry.js +88 -0
  43. package/scripts/migrate-rule-registry.js +157 -0
  44. package/scripts/validate-system.js +48 -0
  45. package/.sunlint.json +0 -35
  46. package/config/README.md +0 -88
  47. package/config/engines/eslint-rule-mapping.json +0 -74
  48. package/config/schemas/sunlint-schema.json +0 -0
  49. package/config/testing/test-s005-working.ts +0 -22
  50. package/core/multi-rule-runner.js +0 -0
  51. package/engines/tree-sitter-parser.js +0 -0
  52. package/engines/universal-ast-engine.js +0 -0
  53. package/rules/common/C029_catch_block_logging/analyzer-backup.js +0 -426
  54. package/rules/common/C029_catch_block_logging/analyzer-fixed.js +0 -130
  55. package/rules/common/C029_catch_block_logging/analyzer-multi-tech.js +0 -487
  56. package/rules/common/C029_catch_block_logging/analyzer-simple.js +0 -110
  57. package/rules/common/C029_catch_block_logging/ast-analyzer-backup.js +0 -441
  58. package/rules/common/C029_catch_block_logging/ast-analyzer-new.js +0 -127
  59. package/rules/common/C029_catch_block_logging/ast-analyzer.js +0 -133
  60. package/rules/common/C029_catch_block_logging/cfg-analyzer.js +0 -408
  61. package/rules/common/C029_catch_block_logging/dataflow-analyzer.js +0 -454
  62. package/rules/common/C029_catch_block_logging/multi-language-ast-engine.js +0 -700
  63. package/rules/common/C029_catch_block_logging/pattern-learning-analyzer.js +0 -568
  64. package/rules/common/C029_catch_block_logging/semantic-analyzer.js +0 -459
@@ -1,74 +0,0 @@
1
- {
2
- "C005": [
3
- "max-statements-per-line",
4
- "complexity"
5
- ],
6
- "C006": [
7
- "func-names",
8
- "func-name-matching",
9
- "@typescript-eslint/naming-convention"
10
- ],
11
- "C007": [
12
- "spaced-comment",
13
- "no-inline-comments",
14
- "no-warning-comments"
15
- ],
16
- "C012": [
17
- "consistent-return",
18
- "no-void",
19
- "@typescript-eslint/no-confusing-void-expression"
20
- ],
21
- "C014": [
22
- "no-new",
23
- "no-new-wrappers",
24
- "@typescript-eslint/no-unnecessary-constructor"
25
- ],
26
- "C015": [
27
- "@typescript-eslint/naming-convention",
28
- "camelcase"
29
- ],
30
- "C019": [
31
- "no-console",
32
- "no-alert",
33
- "no-debugger"
34
- ],
35
- "C031": [
36
- "no-implicit-coercion",
37
- "eqeqeq",
38
- "@typescript-eslint/strict-boolean-expressions"
39
- ],
40
- "C032": [
41
- "no-new",
42
- "@typescript-eslint/no-floating-promises",
43
- "no-constructor-return"
44
- ],
45
- "C033": [
46
- "prefer-const",
47
- "no-var",
48
- "@typescript-eslint/prefer-readonly"
49
- ],
50
- "C034": [
51
- "no-global-assign",
52
- "no-implicit-globals",
53
- "@typescript-eslint/no-namespace"
54
- ],
55
- "C035": [
56
- "no-empty-catch",
57
- "@typescript-eslint/no-unused-vars"
58
- ],
59
- "C037": [
60
- "consistent-return",
61
- "@typescript-eslint/explicit-function-return-type",
62
- "@typescript-eslint/explicit-module-boundary-types"
63
- ],
64
- "C038": [
65
- "import/no-dynamic-require",
66
- "import/order",
67
- "@typescript-eslint/no-var-requires"
68
- ],
69
- "C040": [
70
- "no-duplicate-imports",
71
- "import/no-duplicates",
72
- "@typescript-eslint/no-duplicate-imports"
73
- ]
74
- }
File without changes
@@ -1,22 +0,0 @@
1
- function doAdminTask() {}
2
- function doAction() {}
3
- function isAuthenticated(req: any): boolean {
4
- return true;
5
- }
6
-
7
- function doPost(request: any, response: any) { // ❌
8
- const origin = request.getHeader("Origin");
9
-
10
- if (origin === "https://admin.example.com") {
11
- doAdminTask();
12
- }
13
- }
14
-
15
- function doPost_safe(request: any, response: any) { // ✅
16
- const origin = request.getHeader("Origin");
17
- console.log("Origin:", origin);
18
-
19
- if (isAuthenticated(request)) {
20
- doAction();
21
- }
22
- }
File without changes
File without changes
File without changes
@@ -1,426 +0,0 @@
1
- const fs = require('fs');
2
- cons async analyze(files, language, options = {}) {
3
- // Use Smart Pipeline as primary choice
4
- if (this.smartPipeline) {
5
- console.log('🎯 C029: Using Smart Pipeline (3-stage analysis)...');
6
- return await this.smartPipeline.analyze(files, language, options);
7
- } else if (this.astAnalyzer) {
8
- console.log('🚀 C029: Using AST-enhanced analysis...');
9
- return await this.astAnalyzer.analyze(files, language, options);
10
- } else {
11
- console.log('🔍 C029: Using regex-based analysis...');
12
- return await this.analyzeWithRegex(files, language, options);
13
- }
14
- }ire('path');
15
- const { PatternMatcher } = require('../../utils/pattern-matchers');
16
- const { RuleHelper } = require('../../utils/rule-helpers');
17
-
18
- class C029Analyzer {
19
- constructor() {
20
- this.ruleId = 'C029';
21
- this.ruleName = 'Enhanced Catch Block Error Logging';
22
- 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)';
23
-
24
- // Load Smart Pipeline as primary analyzer
25
- this.smartPipeline = null;
26
-
27
- try {
28
- this.smartPipeline = require('./analyzer-smart-pipeline.js');
29
- console.log('🎯 C029: Smart Pipeline loaded (3-stage: Regex → AST → Data Flow)');
30
- } catch (error) {
31
- console.warn('⚠️ C029: Smart Pipeline failed, falling back:', error.message);
32
-
33
- // Fallback to simpler analyzers
34
- try {
35
- this.astAnalyzer = require('./ast-analyzer.js');
36
- console.log('🚀 C029: AST analyzer loaded (hybrid approach)');
37
- } catch (error) {
38
- console.warn('⚠️ C029: AST analyzer failed:', error.message);
39
- }
40
- }
41
- }
42
-
43
- async analyze(files, language, options = {}) {
44
- // Use Smart Pipeline as primary choice
45
- if (this.smartPipeline) {
46
- console.log('🎯 C029: Using Smart Pipeline (3-stage analysis)...');
47
- return await this.smartPipeline.analyze(files, language, options);
48
- } else if (this.astAnalyzer) {
49
- console.log('🚀 C029: Using AST-enhanced analysis...');
50
- return await this.astAnalyzer.analyze(files, language, options);
51
- } else {
52
- console.log('🔍 C029: Using regex-based analysis...');
53
- return await this.analyzeWithRegex(files, language, options);
54
- }
55
- }
56
- console.log('🧠 C029: Using Data Flow analysis (IDE-level)...');
57
- return await this.dataFlowAnalyzer.analyze(files, language, options);
58
- } else if (this.astAnalyzer) {
59
- console.log('� C029: Using AST-enhanced analysis...');
60
- return await this.astAnalyzer.analyze(files, language, options);
61
- } else {
62
- console.log('🔍 C029: Using regex-based analysis...');
63
- return await this.analyzeWithRegex(files, language, options);
64
- }
65
- }
66
-
67
- async analyzeWithRegex(files, language, options = {}) {
68
- const violations = [];
69
-
70
- for (const filePath of files) {
71
- if (options.verbose) {
72
- console.log(`🔍 Running pattern analysis on ${path.basename(filePath)}`);
73
- }
74
-
75
- try {
76
- const content = fs.readFileSync(filePath, 'utf8');
77
- const fileViolations = await this.analyzeFile(filePath, content, language, options);
78
- violations.push(...fileViolations);
79
- } catch (error) {
80
- console.warn(`⚠️ Failed to analyze ${filePath}: ${error.message}`);
81
- }
82
- }
83
-
84
- return violations;
85
- }
86
-
87
- async analyzeFile(filePath, content, language, config) {
88
- switch (language) {
89
- case 'typescript':
90
- case 'javascript':
91
- return this.analyzeTypeScript(filePath, content, config);
92
- default:
93
- return [];
94
- }
95
- }
96
-
97
- async analyzeTypeScript(filePath, content, config) {
98
- const violations = [];
99
- const lines = content.split('\n');
100
-
101
- // Focus on core functionality - only detect truly silent catch blocks
102
- violations.push(...this.findSilentCatchBlocks(lines, filePath));
103
- // Disabled strict checks to reduce false positives:
104
- // violations.push(...this.findInadequateErrorLogging(lines, filePath));
105
- // violations.push(...this.findMissingErrorContext(lines, filePath));
106
-
107
- return violations;
108
- }
109
-
110
- /**
111
- * Core functionality from ESLint C029 - detect silent catch blocks
112
- */
113
- findSilentCatchBlocks(lines, filePath) {
114
- const violations = [];
115
-
116
- lines.forEach((line, index) => {
117
- const lineNumber = index + 1;
118
- const trimmedLine = line.trim();
119
-
120
- // Detect catch block start
121
- if (this.isCatchBlockStart(trimmedLine)) {
122
- const catchBlockInfo = this.extractCatchBlockInfo(lines, index);
123
-
124
- if (catchBlockInfo.isEmpty) {
125
- violations.push({
126
- ruleId: this.ruleId,
127
- file: filePath,
128
- line: lineNumber,
129
- column: line.indexOf('catch') + 1,
130
- message: 'Empty catch block - error is silently ignored',
131
- severity: 'error',
132
- code: trimmedLine,
133
- type: 'empty_catch_block',
134
- confidence: 1.0,
135
- suggestion: 'Add error logging or rethrowing in catch block'
136
- });
137
- } else if (!catchBlockInfo.hasLoggingOrRethrow) {
138
- violations.push({
139
- ruleId: this.ruleId,
140
- file: filePath,
141
- line: lineNumber,
142
- column: line.indexOf('catch') + 1,
143
- message: 'Catch block must log error or rethrow - silent error handling hides bugs',
144
- severity: 'error',
145
- code: trimmedLine,
146
- type: 'silent_catch_block',
147
- confidence: 0.9,
148
- suggestion: 'Add error logging (console.error, logger.error) or rethrow the error'
149
- });
150
- }
151
- }
152
- });
153
-
154
- return violations;
155
- }
156
-
157
- /**
158
- * SunLint enhanced functionality - detect inadequate error logging
159
- */
160
- findInadequateErrorLogging(lines, filePath) {
161
- const violations = [];
162
-
163
- lines.forEach((line, index) => {
164
- const lineNumber = index + 1;
165
- const trimmedLine = line.trim();
166
-
167
- if (this.isCatchBlockStart(trimmedLine)) {
168
- const catchBlockInfo = this.extractCatchBlockInfo(lines, index);
169
-
170
- if (catchBlockInfo.hasLoggingOrRethrow && !catchBlockInfo.hasAdequateLogging) {
171
- violations.push({
172
- ruleId: this.ruleId,
173
- file: filePath,
174
- line: lineNumber,
175
- column: line.indexOf('catch') + 1,
176
- message: 'Error logging should include error message, stack trace, and context',
177
- severity: 'warning',
178
- code: trimmedLine,
179
- type: 'inadequate_error_logging',
180
- confidence: 0.8,
181
- suggestion: 'Include error.message, error.stack, and relevant context in error logging'
182
- });
183
- }
184
- }
185
- });
186
-
187
- return violations;
188
- }
189
-
190
- /**
191
- * SunLint enhanced functionality - detect missing error context
192
- * DISABLED: Too strict, causing false positives
193
- */
194
- findMissingErrorContext(lines, filePath) {
195
- // Disabled to reduce false positives
196
- return [];
197
-
198
- /* Original strict implementation:
199
- const violations = [];
200
-
201
- lines.forEach((line, index) => {
202
- const lineNumber = index + 1;
203
- const trimmedLine = line.trim();
204
-
205
- if (this.isCatchBlockStart(trimmedLine)) {
206
- const catchBlockInfo = this.extractCatchBlockInfo(lines, index);
207
-
208
- if (catchBlockInfo.hasLoggingOrRethrow && !catchBlockInfo.hasContextualLogging) {
209
- violations.push({
210
- ruleId: this.ruleId,
211
- file: filePath,
212
- line: lineNumber,
213
- column: line.indexOf('catch') + 1,
214
- message: 'Error logging should include operational context (function name, input parameters)',
215
- severity: 'info',
216
- code: trimmedLine,
217
- type: 'missing_error_context',
218
- confidence: 0.7,
219
- suggestion: 'Include function name, input parameters, and operational context in error logging'
220
- });
221
- }
222
- }
223
- });
224
-
225
- return violations;
226
- */
227
- }
228
-
229
- isCatchBlockStart(line) {
230
- return line.includes('catch (') || line.includes('catch(');
231
- }
232
-
233
- extractCatchBlockInfo(lines, startIndex) {
234
- const catchBlockLines = [];
235
- let braceDepth = 0;
236
- let foundCatchBrace = false;
237
-
238
- for (let i = startIndex; i < lines.length; i++) {
239
- const line = lines[i];
240
- catchBlockLines.push(line);
241
-
242
- // Check if this is the catch line with opening brace
243
- if (this.isCatchBlockStart(line.trim())) {
244
- // Count braces in the catch line itself
245
- for (const char of line) {
246
- if (char === '{') {
247
- foundCatchBrace = true;
248
- braceDepth = 1; // Start counting from 1 for the catch block
249
- }
250
- }
251
- } else if (foundCatchBrace) {
252
- // Count braces in subsequent lines
253
- for (const char of line) {
254
- if (char === '{') {
255
- braceDepth++;
256
- } else if (char === '}') {
257
- braceDepth--;
258
- }
259
- }
260
-
261
- // If we've closed all braces, we're done
262
- if (braceDepth === 0) {
263
- break;
264
- }
265
- }
266
- }
267
-
268
- const content = catchBlockLines.join('\n').toLowerCase();
269
- const originalContent = catchBlockLines.join('\n');
270
-
271
- return {
272
- isEmpty: this.isCatchBlockEmpty(catchBlockLines),
273
- hasLoggingOrRethrow: this.hasLoggingOrRethrow(content, originalContent),
274
- hasAdequateLogging: this.hasAdequateErrorLogging(content, originalContent),
275
- hasContextualLogging: this.hasContextualErrorLogging(content, originalContent)
276
- };
277
- }
278
-
279
- isCatchBlockEmpty(catchBlockLines) {
280
- if (catchBlockLines.length === 0) return true;
281
-
282
- // Join all lines and find the content between braces
283
- const fullContent = catchBlockLines.join('\n');
284
-
285
- // Find the opening brace and closing brace
286
- let openBraceIndex = fullContent.indexOf('{');
287
- let closeBraceIndex = fullContent.lastIndexOf('}');
288
-
289
- if (openBraceIndex === -1 || closeBraceIndex === -1) {
290
- return true; // Malformed catch block
291
- }
292
-
293
- // Extract body content between braces
294
- const bodyContent = fullContent.substring(openBraceIndex + 1, closeBraceIndex).trim();
295
-
296
- // Remove comments and whitespace
297
- const cleanedBody = bodyContent
298
- .replace(/\/\*[\s\S]*?\*\//g, '') // Remove block comments
299
- .replace(/\/\/.*$/gm, '') // Remove line comments
300
- .replace(/\s+/g, ' ') // Normalize whitespace
301
- .trim();
302
-
303
- return cleanedBody.length === 0;
304
- }
305
-
306
- hasLoggingOrRethrow(content, originalContent) {
307
- // Check for throw statements
308
- if (content.includes('throw ') || content.includes('throw;')) {
309
- return true;
310
- }
311
-
312
- // Check for test assertions - valid form of error handling
313
- const testPatterns = [
314
- 'expect(',
315
- 'assert(',
316
- 'tobeinstanceof',
317
- 'toequal(',
318
- 'tohavebeencalled',
319
- 'toBe(',
320
- 'toHaveBeenCalledWith'
321
- ];
322
-
323
- const hasTestAssertions = testPatterns.some(pattern =>
324
- content.includes(pattern.toLowerCase()) || originalContent.toLowerCase().includes(pattern.toLowerCase())
325
- );
326
-
327
- if (hasTestAssertions) {
328
- return true;
329
- }
330
-
331
- // Check for Redux/async thunk error handling
332
- const reduxErrorHandlers = [
333
- 'handleaxioserror',
334
- 'rejectwithvalue',
335
- 'dispatch(',
336
- 'seterror(',
337
- 'return value',
338
- 'return rejectwithvalue'
339
- ];
340
-
341
- const hasReduxHandling = reduxErrorHandlers.some(pattern =>
342
- content.includes(pattern.toLowerCase()) || originalContent.toLowerCase().includes(pattern.toLowerCase())
343
- );
344
-
345
- if (hasReduxHandling) {
346
- return true;
347
- }
348
-
349
- // Check for logging patterns (expanded from original)
350
- const loggingPatterns = [
351
- 'console.error',
352
- 'console.log',
353
- 'console.warn',
354
- 'logger.error',
355
- 'log.error',
356
- 'logger.warn',
357
- 'log.warn',
358
- 'winston.error',
359
- 'bunyan.error',
360
- 'pino.error',
361
- '.error(',
362
- '.warn(',
363
- '.log('
364
- ];
365
-
366
- // Accept basic error logging - don't require context
367
- const hasBasicLogging = loggingPatterns.some(pattern =>
368
- content.includes(pattern) || originalContent.includes(pattern)
369
- );
370
-
371
- return hasBasicLogging;
372
- }
373
-
374
- hasAdequateErrorLogging(content, originalContent) {
375
- // Check for error properties being logged
376
- const errorProperties = [
377
- 'error.message',
378
- 'error.stack',
379
- 'err.message',
380
- 'err.stack',
381
- 'e.message',
382
- 'e.stack',
383
- 'error.name',
384
- 'error.cause'
385
- ];
386
-
387
- const hasErrorProperties = errorProperties.some(prop =>
388
- content.includes(prop) || originalContent.includes(prop)
389
- );
390
-
391
- // Check for comprehensive error logging
392
- const hasLogging = this.hasLoggingOrRethrow(content, originalContent);
393
-
394
- return hasLogging && hasErrorProperties;
395
- }
396
-
397
- hasContextualErrorLogging(content, originalContent) {
398
- // Check for contextual information in logging
399
- const contextualPatterns = [
400
- 'function',
401
- 'method',
402
- 'operation',
403
- 'input',
404
- 'parameters',
405
- 'context',
406
- 'state',
407
- 'request',
408
- 'response',
409
- 'userId',
410
- 'sessionId',
411
- 'transactionId'
412
- ];
413
-
414
- // Check if logging includes contextual information
415
- const hasContextualInfo = contextualPatterns.some(pattern =>
416
- content.includes(pattern) || originalContent.includes(pattern)
417
- );
418
-
419
- // Check for template literals or string concatenation (indicates contextual logging)
420
- const hasTemplateLogging = originalContent.includes('${') || originalContent.includes('" + ') || originalContent.includes("' + ");
421
-
422
- return hasContextualInfo || hasTemplateLogging;
423
- }
424
- }
425
-
426
- module.exports = new C029Analyzer();
@@ -1,130 +0,0 @@
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
- const SmartPipelineClass = require('./analyzer-smart-pipeline.js');
21
- this.smartPipeline = new SmartPipelineClass();
22
- console.log('🎯 C029: Smart Pipeline loaded (3-stage: Regex → AST → Data Flow)');
23
- } catch (error) {
24
- console.warn('⚠️ C029: Smart Pipeline failed, using fallback:', error.message);
25
- this.smartPipeline = null;
26
- }
27
- }
28
-
29
- async analyze(files, language, options = {}) {
30
- // Use Smart Pipeline as primary choice
31
- if (this.smartPipeline) {
32
- console.log('🎯 C029: Using Smart Pipeline (3-stage analysis)...');
33
- return await this.smartPipeline.analyze(files, language, options);
34
- } else {
35
- console.log('🔍 C029: Using fallback regex analysis...');
36
- return await this.analyzeWithRegex(files, language, options);
37
- }
38
- }
39
-
40
- async analyzeWithRegex(files, language, options = {}) {
41
- const violations = [];
42
-
43
- for (const filePath of files) {
44
- if (options.verbose) {
45
- console.log(`🔍 C029 Regex: Processing ${path.basename(filePath)}...`);
46
- }
47
-
48
- try {
49
- const content = fs.readFileSync(filePath, 'utf8');
50
- const fileViolations = await this.analyzeFile(filePath, content, language);
51
- violations.push(...fileViolations);
52
- } catch (error) {
53
- console.warn(`⚠️ C029: Error processing ${filePath}:`, error.message);
54
- }
55
- }
56
-
57
- return violations;
58
- }
59
-
60
- async analyzeFile(filePath, content, language) {
61
- const violations = [];
62
- const lines = content.split('\n');
63
-
64
- for (let i = 0; i < lines.length; i++) {
65
- const line = lines[i];
66
-
67
- // Simple catch block detection
68
- if (line.includes('catch') && line.includes('(')) {
69
- const catchBlock = this.extractCatchBlock(lines, i);
70
-
71
- if (this.isCatchBlockEmpty(catchBlock.content)) {
72
- violations.push({
73
- file: filePath,
74
- line: i + 1,
75
- column: line.indexOf('catch') + 1,
76
- message: 'Empty catch block detected',
77
- severity: 'error',
78
- ruleId: this.ruleId,
79
- type: 'empty_catch'
80
- });
81
- }
82
- }
83
- }
84
-
85
- return violations;
86
- }
87
-
88
- extractCatchBlock(lines, startIndex) {
89
- const content = [];
90
- let braceCount = 0;
91
- let inBlock = false;
92
-
93
- for (let i = startIndex; i < lines.length; i++) {
94
- const line = lines[i];
95
- content.push(line);
96
-
97
- for (const char of line) {
98
- if (char === '{') {
99
- braceCount++;
100
- inBlock = true;
101
- } else if (char === '}') {
102
- braceCount--;
103
- if (braceCount === 0 && inBlock) {
104
- return { content, endIndex: i };
105
- }
106
- }
107
- }
108
- }
109
-
110
- return { content, endIndex: startIndex };
111
- }
112
-
113
- isCatchBlockEmpty(content) {
114
- const blockContent = content.join('\n');
115
-
116
- // Remove comments and whitespace
117
- const cleanContent = blockContent
118
- .replace(/\/\*[\s\S]*?\*\//g, '') // Remove multi-line comments
119
- .replace(/\/\/.*$/gm, '') // Remove single-line comments
120
- .replace(/\s+/g, ' ') // Normalize whitespace
121
- .trim();
122
-
123
- // Check if only contains catch declaration and braces
124
- const hasOnlyStructure = /^catch\s*\([^)]*\)\s*\{\s*\}$/.test(cleanContent);
125
-
126
- return hasOnlyStructure;
127
- }
128
- }
129
-
130
- module.exports = C029Analyzer;