@sun-asterisk/sunlint 1.2.1 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/rule-analysis-strategies.js +18 -2
- package/engines/eslint-engine.js +9 -11
- package/engines/heuristic-engine.js +55 -31
- package/package.json +2 -1
- package/rules/README.md +252 -0
- package/rules/common/C002_no_duplicate_code/analyzer.js +65 -0
- package/rules/common/C002_no_duplicate_code/config.json +23 -0
- package/rules/common/C003_no_vague_abbreviations/analyzer.js +418 -0
- package/rules/common/C003_no_vague_abbreviations/config.json +35 -0
- package/rules/common/C006_function_naming/analyzer.js +504 -0
- package/rules/common/C006_function_naming/config.json +86 -0
- package/rules/common/C006_function_naming/smart-analyzer.js +503 -0
- package/rules/common/C010_limit_block_nesting/analyzer.js +389 -0
- package/rules/common/C012_command_query_separation/analyzer.js +481 -0
- package/rules/common/C012_command_query_separation/ast-analyzer.js +495 -0
- package/rules/common/C013_no_dead_code/analyzer.js +206 -0
- package/rules/common/C014_dependency_injection/analyzer.js +338 -0
- package/rules/common/C017_constructor_logic/analyzer.js +314 -0
- package/rules/common/C019_log_level_usage/analyzer.js +362 -0
- package/rules/common/C019_log_level_usage/config.json +121 -0
- package/rules/common/C029_catch_block_logging/analyzer-backup.js +426 -0
- package/rules/common/C029_catch_block_logging/analyzer-fixed.js +130 -0
- package/rules/common/C029_catch_block_logging/analyzer-multi-tech.js +487 -0
- package/rules/common/C029_catch_block_logging/analyzer-simple.js +110 -0
- package/rules/common/C029_catch_block_logging/analyzer-smart-pipeline.js +755 -0
- package/rules/common/C029_catch_block_logging/analyzer.js +129 -0
- package/rules/common/C029_catch_block_logging/ast-analyzer-backup.js +441 -0
- package/rules/common/C029_catch_block_logging/ast-analyzer-new.js +127 -0
- package/rules/common/C029_catch_block_logging/ast-analyzer.js +133 -0
- package/rules/common/C029_catch_block_logging/cfg-analyzer.js +408 -0
- package/rules/common/C029_catch_block_logging/config.json +59 -0
- package/rules/common/C029_catch_block_logging/dataflow-analyzer.js +454 -0
- package/rules/common/C029_catch_block_logging/multi-language-ast-engine.js +700 -0
- package/rules/common/C029_catch_block_logging/pattern-learning-analyzer.js +568 -0
- package/rules/common/C029_catch_block_logging/semantic-analyzer.js +459 -0
- package/rules/common/C031_validation_separation/analyzer.js +186 -0
- package/rules/common/C041_no_sensitive_hardcode/analyzer.js +292 -0
- package/rules/common/C041_no_sensitive_hardcode/ast-analyzer.js +296 -0
- package/rules/common/C042_boolean_name_prefix/analyzer.js +300 -0
- package/rules/common/C043_no_console_or_print/analyzer.js +431 -0
- package/rules/common/C047_no_duplicate_retry_logic/analyzer.js +590 -0
- package/rules/common/C075_explicit_return_types/analyzer.js +103 -0
- package/rules/common/C076_single_test_behavior/analyzer.js +121 -0
- package/rules/docs/C002_no_duplicate_code.md +57 -0
- package/rules/docs/C031_validation_separation.md +72 -0
- package/rules/index.js +155 -0
- package/rules/migration/converter.js +385 -0
- package/rules/migration/mapping.json +164 -0
- package/rules/parser/constants.js +31 -0
- package/rules/parser/file-config.js +80 -0
- package/rules/parser/rule-parser-simple.js +305 -0
- package/rules/parser/rule-parser.js +527 -0
- package/rules/security/S015_insecure_tls_certificate/analyzer.js +150 -0
- package/rules/security/S015_insecure_tls_certificate/ast-analyzer.js +237 -0
- package/rules/security/S023_no_json_injection/analyzer.js +278 -0
- package/rules/security/S023_no_json_injection/ast-analyzer.js +359 -0
- package/rules/security/S026_json_schema_validation/analyzer.js +251 -0
- package/rules/security/S026_json_schema_validation/config.json +27 -0
- package/rules/security/S027_no_hardcoded_secrets/analyzer.js +436 -0
- package/rules/security/S027_no_hardcoded_secrets/config.json +29 -0
- package/rules/security/S029_csrf_protection/analyzer.js +330 -0
- package/rules/tests/C002_no_duplicate_code.test.js +50 -0
- package/rules/universal/C010/generic.js +0 -0
- package/rules/universal/C010/tree-sitter-analyzer.js +0 -0
- package/rules/utils/ast-utils.js +191 -0
- package/rules/utils/base-analyzer.js +98 -0
- package/rules/utils/pattern-matchers.js +239 -0
- package/rules/utils/rule-helpers.js +264 -0
- package/rules/utils/severity-constants.js +93 -0
- package/scripts/generate_insights.js +188 -0
- package/scripts/merge-reports.js +0 -424
- package/scripts/test-scripts/README.md +0 -22
- package/scripts/test-scripts/test-c041-comparison.js +0 -114
- package/scripts/test-scripts/test-c041-eslint.js +0 -67
- package/scripts/test-scripts/test-eslint-rules.js +0 -146
- package/scripts/test-scripts/test-real-world.js +0 -44
- package/scripts/test-scripts/test-rules-on-real-projects.js +0 -86
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const ts = require('typescript');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* SMART C006 ANALYZER - INTELLIGENT FUNCTION NAMING ANALYSIS
|
|
7
|
+
*
|
|
8
|
+
* 🧠 5-TIER ANALYSIS APPROACH:
|
|
9
|
+
* 1. Context Analysis: File path, imports, class context
|
|
10
|
+
* 2. Semantic Analysis: Function body inspection for intent
|
|
11
|
+
* 3. Architectural Layer Detection: UI, Logic, Data, Utils
|
|
12
|
+
* 4. Natural Language Processing: Verb/noun classification
|
|
13
|
+
* 5. Confidence Scoring: Multi-factor violation assessment
|
|
14
|
+
*/
|
|
15
|
+
class SmartC006Analyzer {
|
|
16
|
+
constructor() {
|
|
17
|
+
this.ruleId = 'C006';
|
|
18
|
+
this.ruleName = 'Smart Function Naming Convention';
|
|
19
|
+
this.description = 'Intelligent verb-noun naming pattern detection with context awareness';
|
|
20
|
+
|
|
21
|
+
// 🎯 CONFIDENCE THRESHOLDS
|
|
22
|
+
this.confidenceThresholds = {
|
|
23
|
+
HIGH: 0.8, // Clear violations
|
|
24
|
+
MEDIUM: 0.6, // Likely violations
|
|
25
|
+
LOW: 0.4 // Potential violations
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// 🗂️ ARCHITECTURAL CONTEXT PATTERNS
|
|
29
|
+
this.architecturalLayers = {
|
|
30
|
+
UI: ['component', 'view', 'page', 'screen', 'modal', 'dialog'],
|
|
31
|
+
DATA: ['service', 'repository', 'dao', 'api', 'client', 'adapter'],
|
|
32
|
+
UTILS: ['util', 'helper', 'tool', 'lib', 'common'],
|
|
33
|
+
LOGIC: ['controller', 'handler', 'processor', 'manager', 'engine']
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// 🎭 SEMANTIC INTENT PATTERNS
|
|
37
|
+
this.semanticPatterns = {
|
|
38
|
+
// Return patterns indicate getters
|
|
39
|
+
GETTER: [/return\s+[^;]+/, /=>\s*[^{]/, /\?\s*[^:]+:/],
|
|
40
|
+
// Assignment patterns indicate setters
|
|
41
|
+
SETTER: [/=\s*[^=]/, /\.push\(/, /\.set\(/],
|
|
42
|
+
// Conditional patterns indicate checkers
|
|
43
|
+
CHECKER: [/if\s*\(/, /\?\s*/, /return\s+(true|false)/],
|
|
44
|
+
// Side effect patterns indicate actions
|
|
45
|
+
ACTION: [/console\./, /fetch\(/, /\.send\(/, /\.post\(/]
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async analyze(files, language, config) {
|
|
50
|
+
const violations = [];
|
|
51
|
+
|
|
52
|
+
if (config.verbose) {
|
|
53
|
+
console.log(`🔧 [DEBUG] Starting Smart C006 Analysis on ${files.length} files...`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
for (const filePath of files) {
|
|
57
|
+
try {
|
|
58
|
+
const fileContent = fs.readFileSync(filePath, 'utf8');
|
|
59
|
+
const fileViolations = await this.analyzeFile(filePath, fileContent, language, config);
|
|
60
|
+
violations.push(...fileViolations);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
if (config.verbose) {
|
|
63
|
+
console.error(`⚠️ [DEBUG] Error analyzing file ${filePath}:`, error.message);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (config.verbose) {
|
|
69
|
+
console.log(`🔧 [DEBUG] Smart C006 Analysis complete: ${violations.length} violations found`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return violations;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async analyzeFile(filePath, content, language, config) {
|
|
76
|
+
if (language !== 'typescript' && language !== 'javascript') {
|
|
77
|
+
return []; // Focus on TS/JS for now
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const violations = [];
|
|
81
|
+
const lines = content.split('\n');
|
|
82
|
+
|
|
83
|
+
// 🏗️ TIER 1: ARCHITECTURAL CONTEXT ANALYSIS
|
|
84
|
+
const architecturalContext = this.analyzeArchitecturalContext(filePath, content);
|
|
85
|
+
|
|
86
|
+
// Parse TypeScript/JavaScript code
|
|
87
|
+
const sourceFile = ts.createSourceFile(
|
|
88
|
+
filePath,
|
|
89
|
+
content,
|
|
90
|
+
ts.ScriptTarget.Latest,
|
|
91
|
+
true
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const visit = (node) => {
|
|
95
|
+
// Analyze function declarations
|
|
96
|
+
if (ts.isFunctionDeclaration(node) && node.name && node.body) {
|
|
97
|
+
const analysis = this.smartAnalyzeFunctionName(
|
|
98
|
+
node.name.text,
|
|
99
|
+
node,
|
|
100
|
+
sourceFile,
|
|
101
|
+
architecturalContext,
|
|
102
|
+
content
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
if (analysis.isViolation && analysis.confidence >= this.confidenceThresholds.LOW) {
|
|
106
|
+
const namePosition = sourceFile.getLineAndCharacterOfPosition(node.name.getStart());
|
|
107
|
+
const line = namePosition.line + 1;
|
|
108
|
+
const column = namePosition.character + 1;
|
|
109
|
+
const lineText = lines[line - 1]?.trim() || '';
|
|
110
|
+
|
|
111
|
+
violations.push({
|
|
112
|
+
ruleId: this.ruleId,
|
|
113
|
+
file: filePath,
|
|
114
|
+
line,
|
|
115
|
+
column,
|
|
116
|
+
message: analysis.reason,
|
|
117
|
+
severity: this.getSeverityFromConfidence(analysis.confidence),
|
|
118
|
+
code: lineText,
|
|
119
|
+
type: analysis.type,
|
|
120
|
+
confidence: analysis.confidence,
|
|
121
|
+
suggestion: analysis.suggestion,
|
|
122
|
+
context: analysis.context
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Analyze method declarations
|
|
128
|
+
if (ts.isMethodDeclaration(node) && node.name) {
|
|
129
|
+
const analysis = this.smartAnalyzeFunctionName(
|
|
130
|
+
node.name.text,
|
|
131
|
+
node,
|
|
132
|
+
sourceFile,
|
|
133
|
+
architecturalContext,
|
|
134
|
+
content
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
if (analysis.isViolation && analysis.confidence >= this.confidenceThresholds.LOW) {
|
|
138
|
+
const namePosition = sourceFile.getLineAndCharacterOfPosition(node.name.getStart());
|
|
139
|
+
const line = namePosition.line + 1;
|
|
140
|
+
const column = namePosition.character + 1;
|
|
141
|
+
const lineText = lines[line - 1]?.trim() || '';
|
|
142
|
+
|
|
143
|
+
violations.push({
|
|
144
|
+
ruleId: this.ruleId,
|
|
145
|
+
file: filePath,
|
|
146
|
+
line,
|
|
147
|
+
column,
|
|
148
|
+
message: analysis.reason,
|
|
149
|
+
severity: this.getSeverityFromConfidence(analysis.confidence),
|
|
150
|
+
code: lineText,
|
|
151
|
+
type: analysis.type,
|
|
152
|
+
confidence: analysis.confidence,
|
|
153
|
+
suggestion: analysis.suggestion,
|
|
154
|
+
context: analysis.context
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Analyze arrow functions assigned to variables
|
|
160
|
+
if (ts.isVariableDeclaration(node) && node.name && ts.isIdentifier(node.name) &&
|
|
161
|
+
node.initializer && ts.isArrowFunction(node.initializer)) {
|
|
162
|
+
const analysis = this.smartAnalyzeFunctionName(
|
|
163
|
+
node.name.text,
|
|
164
|
+
node.initializer,
|
|
165
|
+
sourceFile,
|
|
166
|
+
architecturalContext,
|
|
167
|
+
content
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
if (analysis.isViolation && analysis.confidence >= this.confidenceThresholds.LOW) {
|
|
171
|
+
const namePosition = sourceFile.getLineAndCharacterOfPosition(node.name.getStart());
|
|
172
|
+
const line = namePosition.line + 1;
|
|
173
|
+
const column = namePosition.character + 1;
|
|
174
|
+
const lineText = lines[line - 1]?.trim() || '';
|
|
175
|
+
|
|
176
|
+
violations.push({
|
|
177
|
+
ruleId: this.ruleId,
|
|
178
|
+
file: filePath,
|
|
179
|
+
line,
|
|
180
|
+
column,
|
|
181
|
+
message: analysis.reason,
|
|
182
|
+
severity: this.getSeverityFromConfidence(analysis.confidence),
|
|
183
|
+
code: lineText,
|
|
184
|
+
type: analysis.type,
|
|
185
|
+
confidence: analysis.confidence,
|
|
186
|
+
suggestion: analysis.suggestion,
|
|
187
|
+
context: analysis.context
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
ts.forEachChild(node, visit);
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
visit(sourceFile);
|
|
196
|
+
return violations;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* 🏗️ TIER 1: ARCHITECTURAL CONTEXT ANALYSIS
|
|
201
|
+
* Determines what type of file/module this is
|
|
202
|
+
*/
|
|
203
|
+
analyzeArchitecturalContext(filePath, content) {
|
|
204
|
+
const fileName = path.basename(filePath, path.extname(filePath)).toLowerCase();
|
|
205
|
+
const fileDir = path.dirname(filePath).toLowerCase();
|
|
206
|
+
|
|
207
|
+
// Detect architectural layer
|
|
208
|
+
let layer = 'UNKNOWN';
|
|
209
|
+
for (const [layerName, patterns] of Object.entries(this.architecturalLayers)) {
|
|
210
|
+
if (patterns.some(pattern => fileName.includes(pattern) || fileDir.includes(pattern))) {
|
|
211
|
+
layer = layerName;
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Analyze imports for additional context
|
|
217
|
+
const imports = this.extractImports(content);
|
|
218
|
+
const isReactComponent = imports.some(imp => imp.includes('react')) || content.includes('JSX.Element');
|
|
219
|
+
const isTestFile = fileName.includes('test') || fileName.includes('spec');
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
layer,
|
|
223
|
+
isReactComponent,
|
|
224
|
+
isTestFile,
|
|
225
|
+
fileName,
|
|
226
|
+
imports
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* 🧠 TIER 2: SEMANTIC ANALYSIS
|
|
232
|
+
* Analyzes function body to understand intent
|
|
233
|
+
*/
|
|
234
|
+
analyzeSemanticIntent(functionNode, sourceFile, content) {
|
|
235
|
+
if (!functionNode.body) return 'UNKNOWN';
|
|
236
|
+
|
|
237
|
+
const functionText = content.substring(
|
|
238
|
+
functionNode.body.getStart(),
|
|
239
|
+
functionNode.body.getEnd()
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
// Check for different semantic patterns
|
|
243
|
+
for (const [intent, patterns] of Object.entries(this.semanticPatterns)) {
|
|
244
|
+
if (patterns.some(pattern => pattern.test(functionText))) {
|
|
245
|
+
return intent;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return 'UNKNOWN';
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* 🎯 TIER 3: INTELLIGENT VERB DETECTION
|
|
254
|
+
* Uses multiple strategies to detect verbs
|
|
255
|
+
*/
|
|
256
|
+
isVerbLikeName(functionName) {
|
|
257
|
+
// Strategy 1: Known verb prefixes (expanded beyond static list)
|
|
258
|
+
const verbPrefixes = [
|
|
259
|
+
'get', 'set', 'is', 'has', 'can', 'should', 'will', 'does',
|
|
260
|
+
'create', 'build', 'make', 'generate', 'construct', 'produce',
|
|
261
|
+
'update', 'modify', 'change', 'edit', 'alter', 'transform',
|
|
262
|
+
'delete', 'remove', 'destroy', 'clean', 'clear', 'reset',
|
|
263
|
+
'load', 'save', 'fetch', 'retrieve', 'find', 'search', 'query',
|
|
264
|
+
'validate', 'verify', 'check', 'confirm', 'ensure', 'test',
|
|
265
|
+
'calculate', 'compute', 'process', 'handle', 'manage', 'execute',
|
|
266
|
+
'send', 'receive', 'transmit', 'broadcast', 'emit', 'publish',
|
|
267
|
+
'parse', 'format', 'convert', 'map', 'filter', 'sort', 'group',
|
|
268
|
+
'connect', 'disconnect', 'open', 'close', 'start', 'stop', 'run',
|
|
269
|
+
'show', 'hide', 'display', 'render', 'draw', 'paint', 'animate',
|
|
270
|
+
'add', 'append', 'insert', 'push', 'pop', 'shift', 'splice',
|
|
271
|
+
'count', 'measure', 'monitor', 'watch', 'track', 'observe',
|
|
272
|
+
'refresh', 'restore', 'reload', 'retry', 'resume', 'redirect',
|
|
273
|
+
'select', 'toggle', 'switch', 'enable', 'disable', 'activate',
|
|
274
|
+
'expand', 'collapse', 'scroll', 'navigate', 'submit', 'cancel',
|
|
275
|
+
'on', 'handle', 'trigger', 'fire', 'dispatch', 'invoke', 'call'
|
|
276
|
+
];
|
|
277
|
+
|
|
278
|
+
// Strategy 2: Check if starts with known verb
|
|
279
|
+
const startsWithVerb = verbPrefixes.some(verb => {
|
|
280
|
+
const verbPattern = new RegExp(`^${verb}[A-Z]?`, 'i');
|
|
281
|
+
return verbPattern.test(functionName);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
if (startsWithVerb) return true;
|
|
285
|
+
|
|
286
|
+
// Strategy 3: Common verb suffixes that indicate actions
|
|
287
|
+
const actionSuffixes = ['ize', 'ise', 'fy', 'ate', 'en'];
|
|
288
|
+
if (actionSuffixes.some(suffix => functionName.endsWith(suffix))) {
|
|
289
|
+
return true;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Strategy 4: English verb patterns (basic NLP)
|
|
293
|
+
const verbPatterns = [
|
|
294
|
+
/^(re|un|pre|de|dis)[A-Z]/, // prefixed verbs: revalidate, unload, preprocess
|
|
295
|
+
/^[a-z]+ly[A-Z]/, // adverb-verb patterns: quicklyProcess
|
|
296
|
+
];
|
|
297
|
+
|
|
298
|
+
return verbPatterns.some(pattern => pattern.test(functionName));
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* 🎭 TIER 4: CONTEXT-AWARE NAMING RULES
|
|
303
|
+
* Different rules for different contexts
|
|
304
|
+
*/
|
|
305
|
+
getContextSpecificRules(architecturalContext, semanticIntent) {
|
|
306
|
+
const rules = {
|
|
307
|
+
allowedPatterns: [],
|
|
308
|
+
requiredPatterns: [],
|
|
309
|
+
suggestions: []
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
// React components have different naming conventions
|
|
313
|
+
if (architecturalContext.isReactComponent) {
|
|
314
|
+
rules.allowedPatterns.push(/^[A-Z][a-zA-Z]*$/); // PascalCase components
|
|
315
|
+
rules.allowedPatterns.push(/^use[A-Z][a-zA-Z]*$/); // React hooks
|
|
316
|
+
rules.allowedPatterns.push(/^handle[A-Z][a-zA-Z]*$/); // Event handlers
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Test files have different patterns
|
|
320
|
+
if (architecturalContext.isTestFile) {
|
|
321
|
+
rules.allowedPatterns.push(/^(test|it|describe|should|expect)[A-Z]?/);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Data layer functions often have CRUD patterns
|
|
325
|
+
if (architecturalContext.layer === 'DATA') {
|
|
326
|
+
rules.suggestions.push('Consider CRUD verbs: create, read, update, delete');
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// UI layer functions often have interaction verbs
|
|
330
|
+
if (architecturalContext.layer === 'UI') {
|
|
331
|
+
rules.suggestions.push('Consider UI verbs: show, hide, toggle, render, display');
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return rules;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* 🎯 TIER 5: COMPREHENSIVE SMART ANALYSIS
|
|
339
|
+
* Combines all tiers for intelligent assessment
|
|
340
|
+
*/
|
|
341
|
+
smartAnalyzeFunctionName(functionName, functionNode, sourceFile, architecturalContext, content) {
|
|
342
|
+
// Skip special functions
|
|
343
|
+
if (this.isSpecialFunction(functionName, architecturalContext)) {
|
|
344
|
+
return { isViolation: false };
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Get semantic intent
|
|
348
|
+
const semanticIntent = this.analyzeSemanticIntent(functionNode, sourceFile, content);
|
|
349
|
+
|
|
350
|
+
// Get context-specific rules
|
|
351
|
+
const contextRules = this.getContextSpecificRules(architecturalContext, semanticIntent);
|
|
352
|
+
|
|
353
|
+
// Check if allowed by context-specific patterns
|
|
354
|
+
if (contextRules.allowedPatterns.some(pattern => pattern.test(functionName))) {
|
|
355
|
+
return { isViolation: false };
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Check if name follows verb-noun pattern
|
|
359
|
+
const isVerbLike = this.isVerbLikeName(functionName);
|
|
360
|
+
|
|
361
|
+
if (isVerbLike) {
|
|
362
|
+
return { isViolation: false };
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// 🧮 CONFIDENCE CALCULATION
|
|
366
|
+
let confidence = 0.5; // Base confidence
|
|
367
|
+
|
|
368
|
+
// Boost confidence for clear noun-only patterns
|
|
369
|
+
if (/^[a-z]+$/.test(functionName)) {
|
|
370
|
+
confidence += 0.3; // Simple lowercase nouns: user, data
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (/^[a-z]+[A-Z][a-z]+$/.test(functionName)) {
|
|
374
|
+
confidence += 0.2; // Simple camelCase nouns: userData, userInfo
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Reduce confidence for complex names (might have hidden verbs)
|
|
378
|
+
if (functionName.length > 15) {
|
|
379
|
+
confidence -= 0.1;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Reduce confidence for utils/helpers (more flexible naming)
|
|
383
|
+
if (architecturalContext.layer === 'UTILS') {
|
|
384
|
+
confidence -= 0.2;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Reduce confidence for test files
|
|
388
|
+
if (architecturalContext.isTestFile) {
|
|
389
|
+
confidence -= 0.3;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Cap confidence
|
|
393
|
+
confidence = Math.min(Math.max(confidence, 0.1), 1.0);
|
|
394
|
+
|
|
395
|
+
// 💬 INTELLIGENT MESSAGING
|
|
396
|
+
const context = {
|
|
397
|
+
layer: architecturalContext.layer,
|
|
398
|
+
intent: semanticIntent,
|
|
399
|
+
isReactComponent: architecturalContext.isReactComponent,
|
|
400
|
+
isTestFile: architecturalContext.isTestFile
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
let reason = `Function '${functionName}' should follow verb-noun naming pattern`;
|
|
404
|
+
let suggestion = this.generateSmartSuggestion(functionName, semanticIntent, architecturalContext);
|
|
405
|
+
|
|
406
|
+
if (architecturalContext.layer !== 'UNKNOWN') {
|
|
407
|
+
reason += ` (${architecturalContext.layer} layer)`;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return {
|
|
411
|
+
isViolation: true,
|
|
412
|
+
reason,
|
|
413
|
+
type: 'smart_naming_violation',
|
|
414
|
+
confidence,
|
|
415
|
+
suggestion,
|
|
416
|
+
context
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* 💡 SMART SUGGESTION GENERATOR
|
|
422
|
+
*/
|
|
423
|
+
generateSmartSuggestion(functionName, semanticIntent, architecturalContext) {
|
|
424
|
+
const baseNoun = functionName.charAt(0).toUpperCase() + functionName.slice(1);
|
|
425
|
+
|
|
426
|
+
switch (semanticIntent) {
|
|
427
|
+
case 'GETTER':
|
|
428
|
+
return `get${baseNoun}()`;
|
|
429
|
+
case 'SETTER':
|
|
430
|
+
return `set${baseNoun}()`;
|
|
431
|
+
case 'CHECKER':
|
|
432
|
+
return `is${baseNoun}() or has${baseNoun}()`;
|
|
433
|
+
case 'ACTION':
|
|
434
|
+
return `process${baseNoun}() or handle${baseNoun}()`;
|
|
435
|
+
default:
|
|
436
|
+
if (architecturalContext.layer === 'DATA') {
|
|
437
|
+
return `fetch${baseNoun}() or create${baseNoun}()`;
|
|
438
|
+
}
|
|
439
|
+
if (architecturalContext.layer === 'UI') {
|
|
440
|
+
return `render${baseNoun}() or show${baseNoun}()`;
|
|
441
|
+
}
|
|
442
|
+
return `get${baseNoun}() or process${baseNoun}()`;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* 🛡️ ENHANCED SPECIAL FUNCTION DETECTION
|
|
448
|
+
*/
|
|
449
|
+
isSpecialFunction(name, architecturalContext) {
|
|
450
|
+
const specialFunctions = [
|
|
451
|
+
'constructor', 'toString', 'valueOf', 'toJSON',
|
|
452
|
+
'main', 'init', 'setup', 'teardown', 'build',
|
|
453
|
+
'onCreate', 'onDestroy', 'onStart', 'onStop',
|
|
454
|
+
'onPause', 'onResume', 'onSaveInstanceState',
|
|
455
|
+
'equals', 'hashCode', 'compareTo', 'clone',
|
|
456
|
+
'finalize', 'notify', 'notifyAll', 'wait'
|
|
457
|
+
];
|
|
458
|
+
|
|
459
|
+
// Basic special function check
|
|
460
|
+
if (specialFunctions.includes(name) || name.startsWith('_') || name.startsWith('$')) {
|
|
461
|
+
return true;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// React component names (PascalCase)
|
|
465
|
+
if (architecturalContext.isReactComponent && /^[A-Z][a-zA-Z]*$/.test(name)) {
|
|
466
|
+
return true;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// React hooks
|
|
470
|
+
if (name.startsWith('use') && /^use[A-Z]/.test(name)) {
|
|
471
|
+
return true;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Test function names
|
|
475
|
+
if (architecturalContext.isTestFile && /^(test|it|describe|should|expect)/.test(name)) {
|
|
476
|
+
return true;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
return false;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* 🎯 UTILITY METHODS
|
|
484
|
+
*/
|
|
485
|
+
extractImports(content) {
|
|
486
|
+
const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
487
|
+
const imports = [];
|
|
488
|
+
let match;
|
|
489
|
+
|
|
490
|
+
while ((match = importRegex.exec(content)) !== null) {
|
|
491
|
+
imports.push(match[1]);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
return imports;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
getSeverityFromConfidence(confidence) {
|
|
498
|
+
if (confidence >= this.confidenceThresholds.HIGH) return 'warning';
|
|
499
|
+
if (confidence >= this.confidenceThresholds.MEDIUM) return 'info';
|
|
500
|
+
return 'hint';
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
module.exports = new SmartC006Analyzer();
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ruleId": "C006",
|
|
3
|
+
"name": "Function Naming Convention",
|
|
4
|
+
"description": "Tên hàm phải là động từ/verb-noun pattern",
|
|
5
|
+
"category": "naming",
|
|
6
|
+
"severity": "warning",
|
|
7
|
+
"languages": ["typescript", "dart", "kotlin"],
|
|
8
|
+
"version": "1.0.0",
|
|
9
|
+
"status": "activated",
|
|
10
|
+
"tags": ["naming", "convention", "readability"],
|
|
11
|
+
"config": {
|
|
12
|
+
"commonVerbs": [
|
|
13
|
+
"get", "set", "is", "has", "can", "should", "will",
|
|
14
|
+
"create", "build", "make", "generate", "construct",
|
|
15
|
+
"update", "modify", "change", "edit", "alter",
|
|
16
|
+
"delete", "remove", "destroy", "clean", "clear",
|
|
17
|
+
"load", "save", "fetch", "retrieve", "find", "search",
|
|
18
|
+
"validate", "verify", "check", "confirm", "ensure",
|
|
19
|
+
"calculate", "compute", "process", "handle", "manage",
|
|
20
|
+
"send", "receive", "transmit", "broadcast", "emit",
|
|
21
|
+
"parse", "format", "transform", "convert", "map",
|
|
22
|
+
"filter", "sort", "group", "merge", "split",
|
|
23
|
+
"connect", "disconnect", "open", "close", "start", "stop",
|
|
24
|
+
"show", "hide", "display", "render", "draw", "paint",
|
|
25
|
+
"add", "append", "insert", "push", "pop", "shift",
|
|
26
|
+
"test", "debug", "log", "trace", "monitor", "watch"
|
|
27
|
+
],
|
|
28
|
+
"specialFunctions": [
|
|
29
|
+
"constructor", "toString", "valueOf", "toJSON",
|
|
30
|
+
"main", "init", "setup", "teardown", "build",
|
|
31
|
+
"onCreate", "onDestroy", "onStart", "onStop",
|
|
32
|
+
"onPause", "onResume", "onSaveInstanceState",
|
|
33
|
+
"equals", "hashCode", "compareTo", "clone",
|
|
34
|
+
"finalize", "notify", "notifyAll", "wait"
|
|
35
|
+
],
|
|
36
|
+
"patterns": {
|
|
37
|
+
"camelCase": "^[a-z][a-zA-Z0-9]*$",
|
|
38
|
+
"verbNoun": "^(get|set|is|has|can|create|update|delete|load|save|find|validate|process|handle|calculate|send|receive|parse|format|transform|filter|sort|connect|show|hide|add|remove)[A-Z][a-zA-Z0-9]*$"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"examples": {
|
|
42
|
+
"violations": [
|
|
43
|
+
{
|
|
44
|
+
"language": "typescript",
|
|
45
|
+
"code": "function userData() { return user.data; }",
|
|
46
|
+
"reason": "Function name is a noun - should start with a verb like 'getUserData'"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"language": "typescript",
|
|
50
|
+
"code": "function calculation() { return x + y; }",
|
|
51
|
+
"reason": "Should use verb-noun pattern like 'calculateSum' or 'performCalculation'"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"language": "dart",
|
|
55
|
+
"code": "String userInfo() { return 'info'; }",
|
|
56
|
+
"reason": "Function name should start with verb like 'getUserInfo'"
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
"valid": [
|
|
60
|
+
{
|
|
61
|
+
"language": "typescript",
|
|
62
|
+
"code": "function getUserData() { return user.data; }",
|
|
63
|
+
"reason": "Follows verb-noun pattern"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"language": "typescript",
|
|
67
|
+
"code": "function isValid() { return true; }",
|
|
68
|
+
"reason": "Starts with verb 'is'"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"language": "dart",
|
|
72
|
+
"code": "bool hasPermission() { return true; }",
|
|
73
|
+
"reason": "Starts with verb 'has'"
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
"fixes": {
|
|
78
|
+
"autoFixable": false,
|
|
79
|
+
"suggestions": [
|
|
80
|
+
"Start function names with verbs (get, set, is, has, create, update, etc.)",
|
|
81
|
+
"Use verb-noun pattern for clarity (e.g., getUserData, validateInput)",
|
|
82
|
+
"Avoid noun-only function names",
|
|
83
|
+
"Use camelCase convention"
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
}
|