@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,568 +0,0 @@
1
- /**
2
- * C029 Pattern Learning Analyzer
3
- *
4
- * Uses machine learning techniques to learn from codebase patterns
5
- * and adapt detection rules based on project-specific conventions
6
- *
7
- * Technology Showcase: Adaptive rule learning from codebase patterns
8
- */
9
-
10
- class C029PatternLearningAnalyzer {
11
- constructor() {
12
- this.ruleId = 'C029';
13
- this.ruleName = 'Pattern Learning Enhanced Catch Analysis';
14
- this.description = 'Learns from codebase patterns to adapt error handling detection';
15
-
16
- // Pattern learning state
17
- this.learnedPatterns = {
18
- validErrorHandling: new Map(), // Pattern -> frequency
19
- invalidErrorHandling: new Map(), // Pattern -> frequency
20
- contextPatterns: new Map(), // Context -> handling style
21
- projectConventions: new Map() // Convention -> confidence
22
- };
23
-
24
- // Learning configuration
25
- this.learningConfig = {
26
- minPatternFrequency: 3, // Minimum occurrences to learn pattern
27
- contextWindowSize: 5, // Lines of context for pattern learning
28
- adaptationThreshold: 0.7, // Confidence threshold for pattern adaptation
29
- maxPatternsToLearn: 100 // Prevent memory bloat
30
- };
31
- }
32
-
33
- async analyze(files, language, options = {}) {
34
- const violations = [];
35
- console.log(`🤖 C029 Pattern Learning: Analyzing ${files.length} files with adaptive learning...`);
36
-
37
- // Phase 1: Learn patterns from codebase
38
- const learningPhase = await this.learnPatternsFromCodebase(files, language);
39
- console.log(`🧠 Learned ${learningPhase.patternsLearned} patterns from codebase`);
40
-
41
- // Phase 2: Apply learned patterns for detection
42
- for (const filePath of files) {
43
- try {
44
- const content = require('fs').readFileSync(filePath, 'utf8');
45
- const patternViolations = await this.analyzeWithLearnedPatterns(content, filePath, language);
46
- violations.push(...patternViolations);
47
-
48
- } catch (error) {
49
- console.warn(`C029 Pattern Learning skipping ${filePath}: ${error.message}`);
50
- }
51
- }
52
-
53
- // Phase 3: Report learning insights
54
- this.reportLearningInsights();
55
-
56
- return violations;
57
- }
58
-
59
- /**
60
- * Learn patterns from the entire codebase
61
- */
62
- async learnPatternsFromCodebase(files, language) {
63
- console.log(`🔍 Learning phase: Analyzing ${files.length} files for patterns...`);
64
-
65
- let patternsLearned = 0;
66
- const patternCandidates = new Map();
67
-
68
- for (const filePath of files) {
69
- try {
70
- const content = require('fs').readFileSync(filePath, 'utf8');
71
- const catchBlocks = this.extractCatchBlocksWithContext(content);
72
-
73
- for (const catchBlock of catchBlocks) {
74
- // Extract and categorize patterns
75
- const patterns = this.extractPatterns(catchBlock, content);
76
-
77
- for (const pattern of patterns) {
78
- const key = pattern.signature;
79
- if (!patternCandidates.has(key)) {
80
- patternCandidates.set(key, {
81
- pattern,
82
- frequency: 0,
83
- examples: [],
84
- contexts: []
85
- });
86
- }
87
-
88
- const candidate = patternCandidates.get(key);
89
- candidate.frequency++;
90
-
91
- if (candidate.examples.length < 5) {
92
- candidate.examples.push({
93
- file: filePath,
94
- code: catchBlock.code,
95
- lineNumber: catchBlock.lineNumber
96
- });
97
- }
98
-
99
- candidate.contexts.push(pattern.context);
100
- }
101
- }
102
-
103
- } catch (error) {
104
- // Skip problematic files during learning
105
- }
106
- }
107
-
108
- // Promote candidates to learned patterns
109
- for (const [key, candidate] of patternCandidates) {
110
- if (candidate.frequency >= this.learningConfig.minPatternFrequency) {
111
- this.promoteToLearnedPattern(candidate);
112
- patternsLearned++;
113
- }
114
- }
115
-
116
- // Learn project conventions
117
- this.learnProjectConventions(patternCandidates);
118
-
119
- return { patternsLearned };
120
- }
121
-
122
- /**
123
- * Extract patterns from catch block for learning
124
- */
125
- extractPatterns(catchBlock, fullContent) {
126
- const patterns = [];
127
- const content = catchBlock.content;
128
- const context = catchBlock.context;
129
-
130
- // 1. Error handling style patterns
131
- patterns.push(...this.extractHandlingStylePatterns(content, context));
132
-
133
- // 2. Naming convention patterns
134
- patterns.push(...this.extractNamingPatterns(content, context));
135
-
136
- // 3. Context-specific patterns
137
- patterns.push(...this.extractContextPatterns(content, context, catchBlock));
138
-
139
- // 4. Framework-specific patterns
140
- patterns.push(...this.extractFrameworkPatterns(content, fullContent));
141
-
142
- return patterns;
143
- }
144
-
145
- /**
146
- * Extract error handling style patterns
147
- */
148
- extractHandlingStylePatterns(content, context) {
149
- const patterns = [];
150
-
151
- // Pattern: Logging style
152
- if (/console\.(log|error|warn)/.test(content)) {
153
- patterns.push({
154
- type: 'handling_style',
155
- subtype: 'console_logging',
156
- signature: 'console_logging',
157
- confidence: 0.8,
158
- context: context
159
- });
160
- }
161
-
162
- // Pattern: Rethrowing style
163
- if (/throw/.test(content)) {
164
- patterns.push({
165
- type: 'handling_style',
166
- subtype: 'rethrowing',
167
- signature: 'rethrowing',
168
- confidence: 0.9,
169
- context: context
170
- });
171
- }
172
-
173
- // Pattern: Silent handling with return
174
- if (/return\s+(null|undefined|false|\[\]|\{\})/.test(content)) {
175
- patterns.push({
176
- type: 'handling_style',
177
- subtype: 'silent_return',
178
- signature: 'silent_return',
179
- confidence: 0.7,
180
- context: context
181
- });
182
- }
183
-
184
- // Pattern: State management
185
- if (/setState|dispatch|commit/.test(content)) {
186
- patterns.push({
187
- type: 'handling_style',
188
- subtype: 'state_management',
189
- signature: 'state_management',
190
- confidence: 0.8,
191
- context: context
192
- });
193
- }
194
-
195
- return patterns;
196
- }
197
-
198
- /**
199
- * Extract naming convention patterns
200
- */
201
- extractNamingPatterns(content, context) {
202
- const patterns = [];
203
-
204
- // Extract error parameter names
205
- const errorParamMatch = content.match(/catch\s*\(\s*(\w+)\s*\)/);
206
- if (errorParamMatch) {
207
- const paramName = errorParamMatch[1];
208
-
209
- patterns.push({
210
- type: 'naming_convention',
211
- subtype: 'error_parameter',
212
- signature: `error_param_${paramName}`,
213
- paramName,
214
- confidence: 0.6,
215
- context: context
216
- });
217
- }
218
-
219
- return patterns;
220
- }
221
-
222
- /**
223
- * Extract context-specific patterns
224
- */
225
- extractContextPatterns(content, context, catchBlock) {
226
- const patterns = [];
227
-
228
- // Pattern: Function context
229
- const functionContext = this.extractFunctionContext(context);
230
- if (functionContext) {
231
- patterns.push({
232
- type: 'context_pattern',
233
- subtype: 'function_context',
234
- signature: `function_${functionContext.type}`,
235
- functionType: functionContext.type,
236
- confidence: 0.7,
237
- context: context
238
- });
239
- }
240
-
241
- // Pattern: Class method context
242
- if (/class\s+\w+/.test(context.before)) {
243
- patterns.push({
244
- type: 'context_pattern',
245
- subtype: 'class_method',
246
- signature: 'class_method_error_handling',
247
- confidence: 0.8,
248
- context: context
249
- });
250
- }
251
-
252
- return patterns;
253
- }
254
-
255
- /**
256
- * Extract framework-specific patterns
257
- */
258
- extractFrameworkPatterns(content, fullContent) {
259
- const patterns = [];
260
-
261
- // React patterns
262
- if (/import.*react/i.test(fullContent)) {
263
- if (/setState|setError|setLoading/.test(content)) {
264
- patterns.push({
265
- type: 'framework_pattern',
266
- subtype: 'react_error_handling',
267
- signature: 'react_state_error',
268
- confidence: 0.9,
269
- framework: 'react'
270
- });
271
- }
272
- }
273
-
274
- // Redux patterns
275
- if (/rejectWithValue|dispatch/.test(content)) {
276
- patterns.push({
277
- type: 'framework_pattern',
278
- subtype: 'redux_error_handling',
279
- signature: 'redux_thunk_error',
280
- confidence: 0.9,
281
- framework: 'redux'
282
- });
283
- }
284
-
285
- // Express.js patterns
286
- if (/req\.|res\.|next\(/.test(content)) {
287
- patterns.push({
288
- type: 'framework_pattern',
289
- subtype: 'express_error_handling',
290
- signature: 'express_middleware_error',
291
- confidence: 0.8,
292
- framework: 'express'
293
- });
294
- }
295
-
296
- return patterns;
297
- }
298
-
299
- /**
300
- * Promote pattern candidate to learned pattern
301
- */
302
- promoteToLearnedPattern(candidate) {
303
- const pattern = candidate.pattern;
304
- const key = pattern.signature;
305
-
306
- // Determine if pattern represents valid or invalid handling
307
- const validity = this.assessPatternValidity(candidate);
308
-
309
- if (validity.isValid) {
310
- this.learnedPatterns.validErrorHandling.set(key, {
311
- pattern,
312
- frequency: candidate.frequency,
313
- confidence: validity.confidence,
314
- examples: candidate.examples.slice(0, 3) // Keep top examples
315
- });
316
- } else {
317
- this.learnedPatterns.invalidErrorHandling.set(key, {
318
- pattern,
319
- frequency: candidate.frequency,
320
- confidence: validity.confidence,
321
- examples: candidate.examples.slice(0, 3)
322
- });
323
- }
324
- }
325
-
326
- /**
327
- * Learn project-wide conventions
328
- */
329
- learnProjectConventions(patternCandidates) {
330
- const conventions = new Map();
331
-
332
- // Learn error parameter naming conventions
333
- const errorParamNames = new Map();
334
- for (const [key, candidate] of patternCandidates) {
335
- if (candidate.pattern.type === 'naming_convention') {
336
- const paramName = candidate.pattern.paramName;
337
- errorParamNames.set(paramName, (errorParamNames.get(paramName) || 0) + candidate.frequency);
338
- }
339
- }
340
-
341
- // Find dominant naming convention
342
- const dominantParamName = [...errorParamNames.entries()]
343
- .sort((a, b) => b[1] - a[1])[0];
344
-
345
- if (dominantParamName && dominantParamName[1] >= 5) {
346
- conventions.set('error_param_naming', {
347
- convention: dominantParamName[0],
348
- confidence: Math.min(dominantParamName[1] / 20, 0.9)
349
- });
350
- }
351
-
352
- // Learn logging preferences
353
- const loggingStyles = new Map();
354
- for (const [key, candidate] of patternCandidates) {
355
- if (candidate.pattern.subtype === 'console_logging') {
356
- loggingStyles.set('console', (loggingStyles.get('console') || 0) + candidate.frequency);
357
- }
358
- }
359
-
360
- this.learnedPatterns.projectConventions = conventions;
361
- }
362
-
363
- /**
364
- * Analyze file using learned patterns
365
- */
366
- async analyzeWithLearnedPatterns(content, filePath, language) {
367
- const violations = [];
368
- const isTestFile = this.isTestFile(filePath);
369
-
370
- const catchBlocks = this.extractCatchBlocksWithContext(content);
371
-
372
- for (const catchBlock of catchBlocks) {
373
- const analysis = this.analyzeBlockWithPatterns(catchBlock, content, isTestFile);
374
-
375
- if (analysis.isViolation) {
376
- violations.push({
377
- ruleId: this.ruleId,
378
- file: filePath,
379
- line: catchBlock.lineNumber,
380
- column: 1,
381
- message: analysis.message,
382
- severity: analysis.severity,
383
- code: catchBlock.code,
384
- type: analysis.type,
385
- confidence: analysis.confidence,
386
- suggestion: analysis.suggestion,
387
- learnedPattern: analysis.matchedPattern,
388
- adaptiveInsight: analysis.insight
389
- });
390
- }
391
- }
392
-
393
- return violations;
394
- }
395
-
396
- /**
397
- * Analyze catch block using learned patterns
398
- */
399
- analyzeBlockWithPatterns(catchBlock, fullContent, isTestFile) {
400
- const blockPatterns = this.extractPatterns(catchBlock, fullContent);
401
-
402
- // Check against learned valid patterns
403
- for (const pattern of blockPatterns) {
404
- const validMatch = this.learnedPatterns.validErrorHandling.get(pattern.signature);
405
- if (validMatch && validMatch.confidence >= this.learningConfig.adaptationThreshold) {
406
- return {
407
- isViolation: false,
408
- reason: 'matches_learned_valid_pattern',
409
- matchedPattern: validMatch.pattern
410
- };
411
- }
412
- }
413
-
414
- // Check against learned invalid patterns
415
- for (const pattern of blockPatterns) {
416
- const invalidMatch = this.learnedPatterns.invalidErrorHandling.get(pattern.signature);
417
- if (invalidMatch && invalidMatch.confidence >= this.learningConfig.adaptationThreshold) {
418
- return {
419
- isViolation: true,
420
- type: 'learned_invalid_pattern',
421
- message: `Error handling matches learned invalid pattern: ${pattern.signature}`,
422
- severity: 'warning',
423
- confidence: invalidMatch.confidence,
424
- suggestion: this.generateAdaptiveSuggestion(invalidMatch),
425
- matchedPattern: invalidMatch.pattern,
426
- insight: `Pattern seen ${invalidMatch.frequency} times in codebase`
427
- };
428
- }
429
- }
430
-
431
- // Apply project conventions
432
- const conventionViolation = this.checkProjectConventions(catchBlock, blockPatterns);
433
- if (conventionViolation) {
434
- return conventionViolation;
435
- }
436
-
437
- // Default analysis if no learned patterns match
438
- return this.defaultPatternAnalysis(catchBlock, isTestFile);
439
- }
440
-
441
- /**
442
- * Assess whether a pattern represents valid error handling
443
- */
444
- assessPatternValidity(candidate) {
445
- const pattern = candidate.pattern;
446
- const examples = candidate.examples;
447
-
448
- // Heuristic-based validity assessment
449
- let validityScore = 0.5;
450
-
451
- // Positive indicators
452
- if (pattern.subtype === 'console_logging') validityScore += 0.2;
453
- if (pattern.subtype === 'rethrowing') validityScore += 0.3;
454
- if (pattern.subtype === 'state_management') validityScore += 0.2;
455
-
456
- // Negative indicators
457
- if (pattern.subtype === 'silent_return') validityScore -= 0.3;
458
- if (pattern.signature.includes('empty')) validityScore -= 0.4;
459
-
460
- // Context indicators
461
- if (pattern.framework) validityScore += 0.1; // Framework-specific patterns often valid
462
-
463
- return {
464
- isValid: validityScore > 0.6,
465
- confidence: Math.min(Math.abs(validityScore - 0.5) * 2, 0.9)
466
- };
467
- }
468
-
469
- /**
470
- * Report learning insights
471
- */
472
- reportLearningInsights() {
473
- const totalValid = this.learnedPatterns.validErrorHandling.size;
474
- const totalInvalid = this.learnedPatterns.invalidErrorHandling.size;
475
- const conventions = this.learnedPatterns.projectConventions.size;
476
-
477
- console.log(`📊 Pattern Learning Results:`);
478
- console.log(` ✅ Valid patterns learned: ${totalValid}`);
479
- console.log(` ❌ Invalid patterns learned: ${totalInvalid}`);
480
- console.log(` 📏 Project conventions: ${conventions}`);
481
-
482
- // Report top patterns
483
- const topValid = [...this.learnedPatterns.validErrorHandling.entries()]
484
- .sort((a, b) => b[1].frequency - a[1].frequency)
485
- .slice(0, 3);
486
-
487
- if (topValid.length > 0) {
488
- console.log(` 🔝 Top valid patterns:`);
489
- for (const [signature, data] of topValid) {
490
- console.log(` - ${signature}: ${data.frequency} occurrences`);
491
- }
492
- }
493
- }
494
-
495
- // Helper methods (simplified implementations)
496
- extractCatchBlocksWithContext(content) {
497
- // Reuse from previous implementations
498
- return [];
499
- }
500
-
501
- extractFunctionContext(context) {
502
- if (/async\s+function/.test(context.before)) {
503
- return { type: 'async_function' };
504
- }
505
- if (/function/.test(context.before)) {
506
- return { type: 'regular_function' };
507
- }
508
- return null;
509
- }
510
-
511
- generateAdaptiveSuggestion(invalidMatch) {
512
- const pattern = invalidMatch.pattern;
513
-
514
- switch (pattern.subtype) {
515
- case 'silent_return':
516
- return 'Consider adding error logging before returning fallback value';
517
- case 'empty_catch':
518
- return 'Add error handling or explicit ignore with documentation';
519
- default:
520
- return `Improve error handling - pattern "${pattern.signature}" found to be problematic in this codebase`;
521
- }
522
- }
523
-
524
- checkProjectConventions(catchBlock, patterns) {
525
- // Check naming conventions
526
- const namingConvention = this.learnedPatterns.projectConventions.get('error_param_naming');
527
- if (namingConvention) {
528
- const errorParamPattern = patterns.find(p => p.type === 'naming_convention');
529
- if (errorParamPattern && errorParamPattern.paramName !== namingConvention.convention) {
530
- return {
531
- isViolation: true,
532
- type: 'naming_convention_violation',
533
- message: `Error parameter naming inconsistent with project convention (expected: ${namingConvention.convention})`,
534
- severity: 'info',
535
- confidence: namingConvention.confidence,
536
- suggestion: `Use '${namingConvention.convention}' for error parameter to match project convention`
537
- };
538
- }
539
- }
540
-
541
- return null;
542
- }
543
-
544
- defaultPatternAnalysis(catchBlock, isTestFile) {
545
- // Fallback to simple analysis if no patterns learned
546
- const content = catchBlock.content;
547
-
548
- if (!content || content.trim().length === 0) {
549
- return {
550
- isViolation: true,
551
- type: 'empty_catch_default',
552
- message: 'Empty catch block - no learned patterns to validate against',
553
- severity: 'error',
554
- confidence: 0.8,
555
- suggestion: 'Add error handling or learn from similar patterns in codebase'
556
- };
557
- }
558
-
559
- return { isViolation: false, reason: 'no_learned_patterns_default_pass' };
560
- }
561
-
562
- isTestFile(filePath) {
563
- const testPatterns = ['__tests__', '.test.', '.spec.', '/test/', '/tests/'];
564
- return testPatterns.some(pattern => filePath.includes(pattern));
565
- }
566
- }
567
-
568
- module.exports = new C029PatternLearningAnalyzer();