@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.
- package/CHANGELOG.md +107 -1
- package/CONTRIBUTING.md +1654 -66
- package/README.md +19 -6
- package/config/ci-cd.json +54 -0
- package/config/development.json +56 -0
- package/config/engines/engines-enhanced.json +86 -0
- package/config/engines/semantic-config.json +114 -0
- package/config/eslint-rule-mapping.json +50 -38
- package/config/large-project.json +143 -0
- package/config/presets/all.json +0 -1
- package/config/release.json +70 -0
- package/config/rule-analysis-strategies.js +23 -4
- package/config/rules/S027-categories.json +122 -0
- package/config/rules/enhanced-rules-registry.json +2564 -0
- package/config/rules/rules-registry-generated.json +785 -837
- package/config/rules/rules-registry.json +13 -1
- package/core/adapters/sunlint-rule-adapter.js +25 -30
- package/core/analysis-orchestrator.js +42 -2
- package/core/categories.js +52 -0
- package/core/category-constants.js +39 -0
- package/core/cli-action-handler.js +53 -32
- package/core/cli-program.js +11 -3
- package/core/config-manager.js +111 -0
- package/core/config-merger.js +88 -0
- package/core/constants/categories.js +168 -0
- package/core/constants/defaults.js +165 -0
- package/core/constants/engines.js +185 -0
- package/core/constants/index.js +30 -0
- package/core/constants/rules.js +215 -0
- package/core/enhanced-rules-registry.js +3 -3
- package/core/file-targeting-service.js +128 -7
- package/core/interfaces/rule-plugin.interface.js +207 -0
- package/core/plugin-manager.js +448 -0
- package/core/rule-selection-service.js +42 -15
- package/core/semantic-engine.js +658 -0
- package/core/semantic-rule-base.js +433 -0
- package/core/unified-rule-registry.js +484 -0
- package/docs/COMMAND-EXAMPLES.md +134 -0
- package/docs/CONSTANTS-ARCHITECTURE.md +288 -0
- package/docs/LARGE-PROJECT-GUIDE.md +324 -0
- package/engines/core/base-engine.js +249 -0
- package/engines/engine-factory.js +275 -0
- package/engines/eslint-engine.js +171 -19
- package/engines/heuristic-engine.js +569 -78
- package/integrations/eslint/plugin/index.js +26 -28
- package/origin-rules/common-en.md +8 -8
- package/package.json +10 -6
- package/rules/common/C003_no_vague_abbreviations/analyzer.js +1 -1
- package/rules/common/C017_constructor_logic/analyzer.js +254 -17
- package/rules/common/C017_constructor_logic/semantic-analyzer.js +340 -0
- package/rules/common/C029_catch_block_logging/analyzer.js +17 -5
- package/rules/common/C033_separate_service_repository/README.md +78 -0
- package/rules/common/C033_separate_service_repository/analyzer.js +160 -0
- package/rules/common/C033_separate_service_repository/config.json +50 -0
- package/rules/common/C033_separate_service_repository/regex-based-analyzer.js +585 -0
- package/rules/common/C033_separate_service_repository/symbol-based-analyzer.js +368 -0
- package/rules/common/C035_error_logging_context/STRATEGY.md +99 -0
- package/rules/common/C035_error_logging_context/analyzer.js +230 -0
- package/rules/common/C035_error_logging_context/config.json +54 -0
- package/rules/common/C035_error_logging_context/regex-based-analyzer.js +299 -0
- package/rules/common/C035_error_logging_context/symbol-based-analyzer.js +454 -0
- package/rules/common/C040_centralized_validation/analyzer.js +165 -0
- package/rules/common/C040_centralized_validation/config.json +46 -0
- package/rules/common/C040_centralized_validation/regex-based-analyzer.js +243 -0
- package/rules/common/C040_centralized_validation/symbol-based-analyzer.js +416 -0
- package/rules/common/C047_no_duplicate_retry_logic/c047-semantic-rule.js +278 -0
- package/rules/common/C047_no_duplicate_retry_logic/symbol-analyzer-enhanced.js +968 -0
- package/rules/common/C047_no_duplicate_retry_logic/symbol-config.json +71 -0
- package/rules/common/{C076_single_test_behavior → C072_single_test_behavior}/analyzer.js +6 -6
- package/rules/common/C076_explicit_function_types/README.md +30 -0
- package/rules/common/C076_explicit_function_types/analyzer.js +172 -0
- package/rules/common/C076_explicit_function_types/config.json +15 -0
- package/rules/common/C076_explicit_function_types/semantic-analyzer.js +341 -0
- package/rules/index.js +8 -0
- package/rules/parser/rule-parser.js +13 -2
- package/rules/security/S005_no_origin_auth/README.md +226 -0
- package/rules/security/S005_no_origin_auth/analyzer.js +184 -0
- package/rules/security/S005_no_origin_auth/ast-analyzer.js +406 -0
- package/rules/security/S005_no_origin_auth/config.json +85 -0
- package/rules/security/S006_no_plaintext_recovery_codes/README.md +139 -0
- package/rules/security/S006_no_plaintext_recovery_codes/analyzer.js +306 -0
- package/rules/security/S006_no_plaintext_recovery_codes/config.json +48 -0
- package/rules/security/S007_no_plaintext_otp/README.md +198 -0
- package/rules/security/S007_no_plaintext_otp/analyzer.js +406 -0
- package/rules/security/S007_no_plaintext_otp/config.json +79 -0
- package/rules/security/S007_no_plaintext_otp/semantic-analyzer.js +609 -0
- package/rules/security/S007_no_plaintext_otp/semantic-config.json +195 -0
- package/rules/security/S007_no_plaintext_otp/semantic-wrapper.js +280 -0
- package/rules/security/S027_no_hardcoded_secrets/analyzer.js +180 -366
- package/rules/security/S027_no_hardcoded_secrets/categories.json +153 -0
- package/rules/security/S027_no_hardcoded_secrets/categorized-analyzer.js +250 -0
- package/scripts/category-manager.js +150 -0
- package/scripts/generate-rules-registry.js +88 -0
- package/scripts/migrate-rule-registry.js +157 -0
- package/scripts/prepare-release.sh +1 -1
- package/scripts/validate-system.js +48 -0
- package/.sunlint.json +0 -35
- package/config/README.md +0 -88
- package/config/engines/eslint-rule-mapping.json +0 -74
- package/config/schemas/sunlint-schema.json +0 -0
- package/config/testing/test-s005-working.ts +0 -22
- package/core/multi-rule-runner.js +0 -0
- package/docs/ESLINT-INTEGRATION-STRATEGY.md +0 -392
- package/docs/FUTURE_PACKAGES.md +0 -83
- package/docs/HEURISTIC_VS_AI.md +0 -113
- package/docs/PRODUCTION_DEPLOYMENT_ANALYSIS.md +0 -112
- package/docs/PRODUCTION_SIZE_IMPACT.md +0 -183
- package/docs/RELEASE_GUIDE.md +0 -230
- package/docs/STANDARDIZED-CATEGORY-FILTERING.md +0 -156
- package/engines/tree-sitter-parser.js +0 -0
- package/engines/universal-ast-engine.js +0 -0
- package/integrations/eslint/plugin/rules/common/c076-single-behavior-per-test.js +0 -254
- package/rules/common/C029_catch_block_logging/analyzer-backup.js +0 -426
- package/rules/common/C029_catch_block_logging/analyzer-fixed.js +0 -130
- package/rules/common/C029_catch_block_logging/analyzer-multi-tech.js +0 -487
- package/rules/common/C029_catch_block_logging/analyzer-simple.js +0 -110
- package/rules/common/C029_catch_block_logging/ast-analyzer-backup.js +0 -441
- package/rules/common/C029_catch_block_logging/ast-analyzer-new.js +0 -127
- package/rules/common/C029_catch_block_logging/ast-analyzer.js +0 -133
- package/rules/common/C029_catch_block_logging/cfg-analyzer.js +0 -408
- package/rules/common/C029_catch_block_logging/dataflow-analyzer.js +0 -454
- package/rules/common/C029_catch_block_logging/multi-language-ast-engine.js +0 -700
- package/rules/common/C029_catch_block_logging/pattern-learning-analyzer.js +0 -568
- 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();
|