@sun-asterisk/sunlint 1.3.1 → 1.3.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.
Files changed (66) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/CONTRIBUTING.md +210 -1691
  3. package/config/rule-analysis-strategies.js +17 -1
  4. package/config/rules/enhanced-rules-registry.json +369 -1135
  5. package/config/rules/rules-registry-generated.json +1 -1
  6. package/core/enhanced-rules-registry.js +2 -1
  7. package/core/semantic-engine.js +15 -3
  8. package/core/semantic-rule-base.js +4 -2
  9. package/engines/heuristic-engine.js +65 -4
  10. package/integrations/eslint/plugin/rules/common/c003-no-vague-abbreviations.js +59 -1
  11. package/integrations/eslint/plugin/rules/common/c006-function-name-verb-noun.js +26 -1
  12. package/integrations/eslint/plugin/rules/common/c030-use-custom-error-classes.js +54 -19
  13. package/origin-rules/common-en.md +11 -7
  14. package/package.json +1 -1
  15. package/rules/common/C002_no_duplicate_code/analyzer.js +334 -36
  16. package/rules/common/C003_no_vague_abbreviations/analyzer.js +220 -35
  17. package/rules/common/C006_function_naming/analyzer.js +29 -3
  18. package/rules/common/C010_limit_block_nesting/analyzer.js +181 -337
  19. package/rules/common/C010_limit_block_nesting/config.json +64 -0
  20. package/rules/common/C010_limit_block_nesting/regex-based-analyzer.js +379 -0
  21. package/rules/common/C010_limit_block_nesting/symbol-based-analyzer.js +231 -0
  22. package/rules/common/C013_no_dead_code/analyzer.js +75 -177
  23. package/rules/common/C013_no_dead_code/config.json +61 -0
  24. package/rules/common/C013_no_dead_code/regex-based-analyzer.js +345 -0
  25. package/rules/common/C013_no_dead_code/symbol-based-analyzer.js +640 -0
  26. package/rules/common/C014_dependency_injection/analyzer.js +48 -313
  27. package/rules/common/C014_dependency_injection/config.json +26 -0
  28. package/rules/common/C014_dependency_injection/symbol-based-analyzer.js +751 -0
  29. package/rules/common/C018_no_throw_generic_error/analyzer.js +232 -0
  30. package/rules/common/C018_no_throw_generic_error/config.json +50 -0
  31. package/rules/common/C018_no_throw_generic_error/regex-based-analyzer.js +387 -0
  32. package/rules/common/C018_no_throw_generic_error/symbol-based-analyzer.js +314 -0
  33. package/rules/common/C019_log_level_usage/analyzer.js +110 -317
  34. package/rules/common/C019_log_level_usage/pattern-analyzer.js +88 -0
  35. package/rules/common/C019_log_level_usage/system-log-analyzer.js +1267 -0
  36. package/rules/common/C023_no_duplicate_variable/analyzer.js +180 -0
  37. package/rules/common/C023_no_duplicate_variable/config.json +50 -0
  38. package/rules/common/C023_no_duplicate_variable/symbol-based-analyzer.js +158 -0
  39. package/rules/common/C024_no_scatter_hardcoded_constants/analyzer.js +180 -0
  40. package/rules/common/C024_no_scatter_hardcoded_constants/config.json +50 -0
  41. package/rules/common/C024_no_scatter_hardcoded_constants/symbol-based-analyzer.js +181 -0
  42. package/rules/common/C030_use_custom_error_classes/analyzer.js +200 -0
  43. package/rules/common/C035_error_logging_context/analyzer.js +3 -1
  44. package/rules/index.js +5 -1
  45. package/rules/security/S009_no_insecure_encryption/README.md +158 -0
  46. package/rules/security/S009_no_insecure_encryption/analyzer.js +319 -0
  47. package/rules/security/S009_no_insecure_encryption/config.json +55 -0
  48. package/rules/security/S010_no_insecure_encryption/README.md +224 -0
  49. package/rules/security/S010_no_insecure_encryption/analyzer.js +493 -0
  50. package/rules/security/S010_no_insecure_encryption/config.json +48 -0
  51. package/rules/security/S016_no_sensitive_querystring/STRATEGY.md +149 -0
  52. package/rules/security/S016_no_sensitive_querystring/analyzer.js +276 -0
  53. package/rules/security/S016_no_sensitive_querystring/config.json +127 -0
  54. package/rules/security/S016_no_sensitive_querystring/regex-based-analyzer.js +258 -0
  55. package/rules/security/S016_no_sensitive_querystring/symbol-based-analyzer.js +495 -0
  56. package/rules/security/S048_no_current_password_in_reset/README.md +222 -0
  57. package/rules/security/S048_no_current_password_in_reset/analyzer.js +366 -0
  58. package/rules/security/S048_no_current_password_in_reset/config.json +48 -0
  59. package/rules/security/S055_content_type_validation/README.md +176 -0
  60. package/rules/security/S055_content_type_validation/analyzer.js +312 -0
  61. package/rules/security/S055_content_type_validation/config.json +48 -0
  62. package/rules/utils/rule-helpers.js +140 -1
  63. package/scripts/consolidate-config.js +116 -0
  64. package/config/rules/S027-categories.json +0 -122
  65. package/config/rules/rules-registry.json +0 -777
  66. package/rules/common/C006_function_naming/smart-analyzer.js +0 -503
@@ -1,503 +0,0 @@
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 file of files) {
57
- try {
58
- const fileViolations = await this.analyzeFile(file, config);
59
- violations.push(...fileViolations);
60
- } catch (error) {
61
- if (config.verbose) {
62
- console.log(`⚠️ [DEBUG] C006 analysis failed for ${path.basename(file)}: ${error.message}`);
63
- }
64
- }
65
- }
66
-
67
- if (config.verbose) {
68
- console.log(`🔧 [DEBUG] Smart C006 Analysis complete: ${violations.length} violations found`);
69
- }
70
-
71
- return violations;
72
- }
73
-
74
- async analyzeFile(filePath, content, language, config) {
75
- if (language !== 'typescript' && language !== 'javascript') {
76
- return []; // Focus on TS/JS for now
77
- }
78
-
79
- const violations = [];
80
- const lines = content.split('\n');
81
-
82
- // 🏗️ TIER 1: ARCHITECTURAL CONTEXT ANALYSIS
83
- const architecturalContext = this.analyzeArchitecturalContext(filePath, content);
84
-
85
- // Parse TypeScript/JavaScript code
86
- const sourceFile = ts.createSourceFile(
87
- filePath,
88
- content,
89
- ts.ScriptTarget.Latest,
90
- true
91
- );
92
-
93
- const visit = (node) => {
94
- // Analyze function declarations
95
- if (ts.isFunctionDeclaration(node) && node.name && node.body) {
96
- const analysis = this.smartAnalyzeFunctionName(
97
- node.name.text,
98
- node,
99
- sourceFile,
100
- architecturalContext,
101
- content
102
- );
103
-
104
- if (analysis.isViolation && analysis.confidence >= this.confidenceThresholds.LOW) {
105
- const namePosition = sourceFile.getLineAndCharacterOfPosition(node.name.getStart());
106
- const line = namePosition.line + 1;
107
- const column = namePosition.character + 1;
108
- const lineText = lines[line - 1]?.trim() || '';
109
-
110
- violations.push({
111
- ruleId: this.ruleId,
112
- file: filePath,
113
- line,
114
- column,
115
- message: analysis.reason,
116
- severity: this.getSeverityFromConfidence(analysis.confidence),
117
- code: lineText,
118
- type: analysis.type,
119
- confidence: analysis.confidence,
120
- suggestion: analysis.suggestion,
121
- context: analysis.context
122
- });
123
- }
124
- }
125
-
126
- // Analyze method declarations
127
- if (ts.isMethodDeclaration(node) && node.name) {
128
- const analysis = this.smartAnalyzeFunctionName(
129
- node.name.text,
130
- node,
131
- sourceFile,
132
- architecturalContext,
133
- content
134
- );
135
-
136
- if (analysis.isViolation && analysis.confidence >= this.confidenceThresholds.LOW) {
137
- const namePosition = sourceFile.getLineAndCharacterOfPosition(node.name.getStart());
138
- const line = namePosition.line + 1;
139
- const column = namePosition.character + 1;
140
- const lineText = lines[line - 1]?.trim() || '';
141
-
142
- violations.push({
143
- ruleId: this.ruleId,
144
- file: filePath,
145
- line,
146
- column,
147
- message: analysis.reason,
148
- severity: this.getSeverityFromConfidence(analysis.confidence),
149
- code: lineText,
150
- type: analysis.type,
151
- confidence: analysis.confidence,
152
- suggestion: analysis.suggestion,
153
- context: analysis.context
154
- });
155
- }
156
- }
157
-
158
- // Analyze arrow functions assigned to variables
159
- if (ts.isVariableDeclaration(node) && node.name && ts.isIdentifier(node.name) &&
160
- node.initializer && ts.isArrowFunction(node.initializer)) {
161
- const analysis = this.smartAnalyzeFunctionName(
162
- node.name.text,
163
- node.initializer,
164
- sourceFile,
165
- architecturalContext,
166
- content
167
- );
168
-
169
- if (analysis.isViolation && analysis.confidence >= this.confidenceThresholds.LOW) {
170
- const namePosition = sourceFile.getLineAndCharacterOfPosition(node.name.getStart());
171
- const line = namePosition.line + 1;
172
- const column = namePosition.character + 1;
173
- const lineText = lines[line - 1]?.trim() || '';
174
-
175
- violations.push({
176
- ruleId: this.ruleId,
177
- file: filePath,
178
- line,
179
- column,
180
- message: analysis.reason,
181
- severity: this.getSeverityFromConfidence(analysis.confidence),
182
- code: lineText,
183
- type: analysis.type,
184
- confidence: analysis.confidence,
185
- suggestion: analysis.suggestion,
186
- context: analysis.context
187
- });
188
- }
189
- }
190
-
191
- ts.forEachChild(node, visit);
192
- };
193
-
194
- visit(sourceFile);
195
- return violations;
196
- }
197
-
198
- /**
199
- * 🏗️ TIER 1: ARCHITECTURAL CONTEXT ANALYSIS
200
- * Determines what type of file/module this is
201
- */
202
- analyzeArchitecturalContext(filePath, content) {
203
- const fileName = path.basename(filePath, path.extname(filePath)).toLowerCase();
204
- const fileDir = path.dirname(filePath).toLowerCase();
205
-
206
- // Detect architectural layer
207
- let layer = 'UNKNOWN';
208
- for (const [layerName, patterns] of Object.entries(this.architecturalLayers)) {
209
- if (patterns.some(pattern => fileName.includes(pattern) || fileDir.includes(pattern))) {
210
- layer = layerName;
211
- break;
212
- }
213
- }
214
-
215
- // Analyze imports for additional context
216
- const imports = this.extractImports(content);
217
- const isReactComponent = imports.some(imp => imp.includes('react')) || content.includes('JSX.Element');
218
- const isTestFile = fileName.includes('test') || fileName.includes('spec');
219
-
220
- return {
221
- layer,
222
- isReactComponent,
223
- isTestFile,
224
- fileName,
225
- imports
226
- };
227
- }
228
-
229
- /**
230
- * 🧠 TIER 2: SEMANTIC ANALYSIS
231
- * Analyzes function body to understand intent
232
- */
233
- analyzeSemanticIntent(functionNode, sourceFile, content) {
234
- if (!functionNode.body) return 'UNKNOWN';
235
-
236
- const functionText = content.substring(
237
- functionNode.body.getStart(),
238
- functionNode.body.getEnd()
239
- );
240
-
241
- // Check for different semantic patterns
242
- for (const [intent, patterns] of Object.entries(this.semanticPatterns)) {
243
- if (patterns.some(pattern => pattern.test(functionText))) {
244
- return intent;
245
- }
246
- }
247
-
248
- return 'UNKNOWN';
249
- }
250
-
251
- /**
252
- * 🎯 TIER 3: INTELLIGENT VERB DETECTION
253
- * Uses multiple strategies to detect verbs
254
- */
255
- isVerbLikeName(functionName) {
256
- // Strategy 1: Known verb prefixes (expanded beyond static list)
257
- const verbPrefixes = [
258
- 'get', 'set', 'is', 'has', 'can', 'should', 'will', 'does',
259
- 'create', 'build', 'make', 'generate', 'construct', 'produce',
260
- 'update', 'modify', 'change', 'edit', 'alter', 'transform',
261
- 'delete', 'remove', 'destroy', 'clean', 'clear', 'reset',
262
- 'load', 'save', 'fetch', 'retrieve', 'find', 'search', 'query',
263
- 'validate', 'verify', 'check', 'confirm', 'ensure', 'test',
264
- 'calculate', 'compute', 'process', 'handle', 'manage', 'execute',
265
- 'send', 'receive', 'transmit', 'broadcast', 'emit', 'publish',
266
- 'parse', 'format', 'convert', 'map', 'filter', 'sort', 'group',
267
- 'connect', 'disconnect', 'open', 'close', 'start', 'stop', 'run',
268
- 'show', 'hide', 'display', 'render', 'draw', 'paint', 'animate',
269
- 'add', 'append', 'insert', 'push', 'pop', 'shift', 'splice',
270
- 'count', 'measure', 'monitor', 'watch', 'track', 'observe',
271
- 'refresh', 'restore', 'reload', 'retry', 'resume', 'redirect',
272
- 'select', 'toggle', 'switch', 'enable', 'disable', 'activate',
273
- 'expand', 'collapse', 'scroll', 'navigate', 'submit', 'cancel',
274
- 'on', 'handle', 'trigger', 'fire', 'dispatch', 'invoke', 'call'
275
- ];
276
-
277
- // Strategy 2: Check if starts with known verb
278
- const startsWithVerb = verbPrefixes.some(verb => {
279
- const verbPattern = new RegExp(`^${verb}[A-Z]?`, 'i');
280
- return verbPattern.test(functionName);
281
- });
282
-
283
- if (startsWithVerb) return true;
284
-
285
- // Strategy 3: Common verb suffixes that indicate actions
286
- const actionSuffixes = ['ize', 'ise', 'fy', 'ate', 'en'];
287
- if (actionSuffixes.some(suffix => functionName.endsWith(suffix))) {
288
- return true;
289
- }
290
-
291
- // Strategy 4: English verb patterns (basic NLP)
292
- const verbPatterns = [
293
- /^(re|un|pre|de|dis)[A-Z]/, // prefixed verbs: revalidate, unload, preprocess
294
- /^[a-z]+ly[A-Z]/, // adverb-verb patterns: quicklyProcess
295
- ];
296
-
297
- return verbPatterns.some(pattern => pattern.test(functionName));
298
- }
299
-
300
- /**
301
- * 🎭 TIER 4: CONTEXT-AWARE NAMING RULES
302
- * Different rules for different contexts
303
- */
304
- getContextSpecificRules(architecturalContext, semanticIntent) {
305
- const rules = {
306
- allowedPatterns: [],
307
- requiredPatterns: [],
308
- suggestions: []
309
- };
310
-
311
- // React components have different naming conventions
312
- if (architecturalContext.isReactComponent) {
313
- rules.allowedPatterns.push(/^[A-Z][a-zA-Z]*$/); // PascalCase components
314
- rules.allowedPatterns.push(/^use[A-Z][a-zA-Z]*$/); // React hooks
315
- rules.allowedPatterns.push(/^handle[A-Z][a-zA-Z]*$/); // Event handlers
316
- }
317
-
318
- // Test files have different patterns
319
- if (architecturalContext.isTestFile) {
320
- rules.allowedPatterns.push(/^(test|it|describe|should|expect)[A-Z]?/);
321
- }
322
-
323
- // Data layer functions often have CRUD patterns
324
- if (architecturalContext.layer === 'DATA') {
325
- rules.suggestions.push('Consider CRUD verbs: create, read, update, delete');
326
- }
327
-
328
- // UI layer functions often have interaction verbs
329
- if (architecturalContext.layer === 'UI') {
330
- rules.suggestions.push('Consider UI verbs: show, hide, toggle, render, display');
331
- }
332
-
333
- return rules;
334
- }
335
-
336
- /**
337
- * 🎯 TIER 5: COMPREHENSIVE SMART ANALYSIS
338
- * Combines all tiers for intelligent assessment
339
- */
340
- smartAnalyzeFunctionName(functionName, functionNode, sourceFile, architecturalContext, content) {
341
- // Skip special functions
342
- if (this.isSpecialFunction(functionName, architecturalContext)) {
343
- return { isViolation: false };
344
- }
345
-
346
- // Get semantic intent
347
- const semanticIntent = this.analyzeSemanticIntent(functionNode, sourceFile, content);
348
-
349
- // Get context-specific rules
350
- const contextRules = this.getContextSpecificRules(architecturalContext, semanticIntent);
351
-
352
- // Check if allowed by context-specific patterns
353
- if (contextRules.allowedPatterns.some(pattern => pattern.test(functionName))) {
354
- return { isViolation: false };
355
- }
356
-
357
- // Check if name follows verb-noun pattern
358
- const isVerbLike = this.isVerbLikeName(functionName);
359
-
360
- if (isVerbLike) {
361
- return { isViolation: false };
362
- }
363
-
364
- // 🧮 CONFIDENCE CALCULATION
365
- let confidence = 0.5; // Base confidence
366
-
367
- // Boost confidence for clear noun-only patterns
368
- if (/^[a-z]+$/.test(functionName)) {
369
- confidence += 0.3; // Simple lowercase nouns: user, data
370
- }
371
-
372
- if (/^[a-z]+[A-Z][a-z]+$/.test(functionName)) {
373
- confidence += 0.2; // Simple camelCase nouns: userData, userInfo
374
- }
375
-
376
- // Reduce confidence for complex names (might have hidden verbs)
377
- if (functionName.length > 15) {
378
- confidence -= 0.1;
379
- }
380
-
381
- // Reduce confidence for utils/helpers (more flexible naming)
382
- if (architecturalContext.layer === 'UTILS') {
383
- confidence -= 0.2;
384
- }
385
-
386
- // Reduce confidence for test files
387
- if (architecturalContext.isTestFile) {
388
- confidence -= 0.3;
389
- }
390
-
391
- // Cap confidence
392
- confidence = Math.min(Math.max(confidence, 0.1), 1.0);
393
-
394
- // 💬 INTELLIGENT MESSAGING
395
- const context = {
396
- layer: architecturalContext.layer,
397
- intent: semanticIntent,
398
- isReactComponent: architecturalContext.isReactComponent,
399
- isTestFile: architecturalContext.isTestFile
400
- };
401
-
402
- let reason = `Function '${functionName}' should follow verb-noun naming pattern`;
403
- let suggestion = this.generateSmartSuggestion(functionName, semanticIntent, architecturalContext);
404
-
405
- if (architecturalContext.layer !== 'UNKNOWN') {
406
- reason += ` (${architecturalContext.layer} layer)`;
407
- }
408
-
409
- return {
410
- isViolation: true,
411
- reason,
412
- type: 'smart_naming_violation',
413
- confidence,
414
- suggestion,
415
- context
416
- };
417
- }
418
-
419
- /**
420
- * 💡 SMART SUGGESTION GENERATOR
421
- */
422
- generateSmartSuggestion(functionName, semanticIntent, architecturalContext) {
423
- const baseNoun = functionName.charAt(0).toUpperCase() + functionName.slice(1);
424
-
425
- switch (semanticIntent) {
426
- case 'GETTER':
427
- return `get${baseNoun}()`;
428
- case 'SETTER':
429
- return `set${baseNoun}()`;
430
- case 'CHECKER':
431
- return `is${baseNoun}() or has${baseNoun}()`;
432
- case 'ACTION':
433
- return `process${baseNoun}() or handle${baseNoun}()`;
434
- default:
435
- if (architecturalContext.layer === 'DATA') {
436
- return `fetch${baseNoun}() or create${baseNoun}()`;
437
- }
438
- if (architecturalContext.layer === 'UI') {
439
- return `render${baseNoun}() or show${baseNoun}()`;
440
- }
441
- return `get${baseNoun}() or process${baseNoun}()`;
442
- }
443
- }
444
-
445
- /**
446
- * 🛡️ ENHANCED SPECIAL FUNCTION DETECTION
447
- */
448
- isSpecialFunction(name, architecturalContext) {
449
- const specialFunctions = [
450
- 'constructor', 'toString', 'valueOf', 'toJSON',
451
- 'main', 'init', 'setup', 'teardown', 'build',
452
- 'onCreate', 'onDestroy', 'onStart', 'onStop',
453
- 'onPause', 'onResume', 'onSaveInstanceState',
454
- 'equals', 'hashCode', 'compareTo', 'clone',
455
- 'finalize', 'notify', 'notifyAll', 'wait'
456
- ];
457
-
458
- // Basic special function check
459
- if (specialFunctions.includes(name) || name.startsWith('_') || name.startsWith('$')) {
460
- return true;
461
- }
462
-
463
- // React component names (PascalCase)
464
- if (architecturalContext.isReactComponent && /^[A-Z][a-zA-Z]*$/.test(name)) {
465
- return true;
466
- }
467
-
468
- // React hooks
469
- if (name.startsWith('use') && /^use[A-Z]/.test(name)) {
470
- return true;
471
- }
472
-
473
- // Test function names
474
- if (architecturalContext.isTestFile && /^(test|it|describe|should|expect)/.test(name)) {
475
- return true;
476
- }
477
-
478
- return false;
479
- }
480
-
481
- /**
482
- * 🎯 UTILITY METHODS
483
- */
484
- extractImports(content) {
485
- const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
486
- const imports = [];
487
- let match;
488
-
489
- while ((match = importRegex.exec(content)) !== null) {
490
- imports.push(match[1]);
491
- }
492
-
493
- return imports;
494
- }
495
-
496
- getSeverityFromConfidence(confidence) {
497
- if (confidence >= this.confidenceThresholds.HIGH) return 'warning';
498
- if (confidence >= this.confidenceThresholds.MEDIUM) return 'info';
499
- return 'hint';
500
- }
501
- }
502
-
503
- module.exports = new SmartC006Analyzer();