@sun-asterisk/sunlint 1.2.2 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +40 -1
- package/CONTRIBUTING.md +533 -70
- package/README.md +16 -2
- 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/rules/enhanced-rules-registry.json +2503 -0
- package/config/rules/rules-registry-generated.json +785 -837
- 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 +32 -5
- package/core/config-manager.js +111 -0
- package/core/config-merger.js +61 -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/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 +560 -0
- package/core/semantic-rule-base.js +433 -0
- package/core/unified-rule-registry.js +484 -0
- package/docs/CONSTANTS-ARCHITECTURE.md +288 -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 +511 -78
- package/integrations/eslint/plugin/index.js +27 -27
- package/package.json +10 -6
- package/rules/common/C003_no_vague_abbreviations/analyzer.js +1 -1
- package/rules/common/C029_catch_block_logging/analyzer.js +17 -5
- 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/index.js +7 -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/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/engines/tree-sitter-parser.js +0 -0
- package/engines/universal-ast-engine.js +0 -0
- 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
|
@@ -85,33 +85,33 @@ const s058 = require("./rules/security/s058-no-ssrf.js");
|
|
|
85
85
|
|
|
86
86
|
module.exports = {
|
|
87
87
|
rules: {
|
|
88
|
-
"c002": c002,
|
|
89
|
-
"c003": c003,
|
|
90
|
-
"c006": c006,
|
|
91
|
-
"c010": c010,
|
|
92
|
-
"c013": c013,
|
|
93
|
-
"c014": c014,
|
|
94
|
-
"c017": c017,
|
|
95
|
-
"c018": c018,
|
|
96
|
-
"c030": c030,
|
|
97
|
-
"c035": c035,
|
|
98
|
-
"c023": c023,
|
|
99
|
-
"c029": c029,
|
|
100
|
-
"c041": c041,
|
|
101
|
-
"c042": c042,
|
|
102
|
-
"c043": c043,
|
|
103
|
-
"c047": c047,
|
|
104
|
-
"c072": c072,
|
|
105
|
-
"c075": c075,
|
|
106
|
-
"c076": c076,
|
|
107
|
-
"t002": t002,
|
|
108
|
-
"t003": t003,
|
|
109
|
-
"t004": t004,
|
|
110
|
-
"t007": t007,
|
|
111
|
-
"t010": t010,
|
|
112
|
-
"t019": t019,
|
|
113
|
-
"t020": t020,
|
|
114
|
-
"t021": t021,
|
|
88
|
+
"c002-no-duplicate-code": c002,
|
|
89
|
+
"c003-no-vague-abbreviations": c003,
|
|
90
|
+
"c006-function-name-verb-noun": c006,
|
|
91
|
+
"c010-limit-block-nesting": c010,
|
|
92
|
+
"c013-no-dead-code": c013,
|
|
93
|
+
"c014-abstract-dependency-preferred": c014,
|
|
94
|
+
"c017-limit-constructor-logic": c017,
|
|
95
|
+
"c018-no-generic-throw": c018,
|
|
96
|
+
"c030-use-custom-error-classes": c030,
|
|
97
|
+
"c035-no-empty-catch": c035,
|
|
98
|
+
"c023-no-duplicate-variable-name-in-scope": c023,
|
|
99
|
+
"c029-catch-block-logging": c029,
|
|
100
|
+
"c041-no-config-inline": c041,
|
|
101
|
+
"c042-boolean-name-prefix": c042,
|
|
102
|
+
"c043-no-console-or-print": c043,
|
|
103
|
+
"c047-no-duplicate-retry-logic": c047,
|
|
104
|
+
"c072-one-assert-per-test": c072,
|
|
105
|
+
"c075-explicit-function-return-types": c075,
|
|
106
|
+
"c076-single-behavior-per-test": c076,
|
|
107
|
+
"t002-interface-prefix-i": t002,
|
|
108
|
+
"t003-ts-ignore-reason": t003,
|
|
109
|
+
"t004-no-empty-type": t004,
|
|
110
|
+
"t007-no-fn-in-constructor": t007,
|
|
111
|
+
"t010-no-nested-union-tuple": t010,
|
|
112
|
+
"t019-no-this-assign": t019,
|
|
113
|
+
"t020-no-default-multi-export": t020,
|
|
114
|
+
"t021-limit-nested-generics": t021,
|
|
115
115
|
// Security rules
|
|
116
116
|
"typescript_s001": s001,
|
|
117
117
|
"typescript_s002": s002,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sun-asterisk/sunlint",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "☀️ SunLint - Multi-language static analysis tool for code quality and security | Sun* Engineering Standards",
|
|
5
5
|
"main": "cli.js",
|
|
6
6
|
"bin": {
|
|
@@ -40,17 +40,18 @@
|
|
|
40
40
|
"demo:file-targeting": "./demo-file-targeting.sh",
|
|
41
41
|
"lint": "node cli.js --config=.sunlint.json --input=.",
|
|
42
42
|
"lint:eslint-integration": "node cli.js --all --eslint-integration --input=.",
|
|
43
|
-
"build": "npm run copy-rules && echo 'Build completed with rules copy'",
|
|
43
|
+
"build": "npm run copy-rules && npm run generate-registry && echo 'Build completed with rules copy and registry generation'",
|
|
44
44
|
"copy-rules": "node scripts/copy-rules.js",
|
|
45
|
+
"generate-registry": "node scripts/generate-rules-registry.js",
|
|
45
46
|
"clean": "rm -rf coverage/ *.log reports/ *.tgz",
|
|
46
47
|
"postpack": "echo '📦 Package created successfully! Size: ' && ls -lh *.tgz | awk '{print $5}'",
|
|
47
48
|
"start": "node cli.js --help",
|
|
48
49
|
"version": "node cli.js --version",
|
|
49
|
-
"pack": "npm run copy-rules && npm pack",
|
|
50
|
+
"pack": "npm run copy-rules && npm run generate-registry && npm pack",
|
|
50
51
|
"publish:github": "npm publish --registry=https://npm.pkg.github.com",
|
|
51
52
|
"publish:npmjs": "npm publish --registry=https://registry.npmjs.org",
|
|
52
53
|
"publish:test": "npm publish --dry-run --registry=https://registry.npmjs.org",
|
|
53
|
-
"prepublishOnly": "npm run clean && npm run copy-rules"
|
|
54
|
+
"prepublishOnly": "npm run clean && npm run copy-rules && npm run generate-registry"
|
|
54
55
|
},
|
|
55
56
|
"keywords": [
|
|
56
57
|
"linting",
|
|
@@ -99,7 +100,8 @@
|
|
|
99
100
|
"espree": "^10.3.0",
|
|
100
101
|
"minimatch": "^10.0.3",
|
|
101
102
|
"node-fetch": "^3.3.2",
|
|
102
|
-
"table": "^6.8.2"
|
|
103
|
+
"table": "^6.8.2",
|
|
104
|
+
"ts-morph": "^26.0.0"
|
|
103
105
|
},
|
|
104
106
|
"peerDependencies": {
|
|
105
107
|
"typescript": ">=4.0.0"
|
|
@@ -126,8 +128,10 @@
|
|
|
126
128
|
},
|
|
127
129
|
"devDependencies": {
|
|
128
130
|
"@types/node": "^22.10.1",
|
|
131
|
+
"eslint-plugin-import": "^2.32.0",
|
|
129
132
|
"glob": "^11.0.3",
|
|
130
|
-
"jest": "^29.7.0"
|
|
133
|
+
"jest": "^29.7.0",
|
|
134
|
+
"typescript": "^5.8.3"
|
|
131
135
|
},
|
|
132
136
|
"bugs": {
|
|
133
137
|
"url": "https://github.com/sun-asterisk/engineer-excellence/issues"
|
|
@@ -295,7 +295,7 @@ class C003NoVagueAbbreviations {
|
|
|
295
295
|
const violation = this.checkVariableName(variableName, content, lineNumber, match.index);
|
|
296
296
|
if (violation) {
|
|
297
297
|
violations.push({
|
|
298
|
-
|
|
298
|
+
ruleId: 'C003',
|
|
299
299
|
severity: 'warning',
|
|
300
300
|
message: violation.message,
|
|
301
301
|
line: lineNumber,
|
|
@@ -8,30 +8,42 @@ const fs = require('fs');
|
|
|
8
8
|
const path = require('path');
|
|
9
9
|
|
|
10
10
|
class C029Analyzer {
|
|
11
|
-
constructor() {
|
|
11
|
+
constructor(options = {}) {
|
|
12
12
|
this.ruleId = 'C029';
|
|
13
13
|
this.ruleName = 'Enhanced Catch Block Error Logging';
|
|
14
14
|
this.description = 'Mọi catch block phải log nguyên nhân lỗi đầy đủ và bảo toàn context (Smart Pipeline 3-stage analysis)';
|
|
15
|
+
this.verbose = options.verbose || false;
|
|
15
16
|
|
|
16
17
|
// Load Smart Pipeline as primary analyzer
|
|
17
18
|
this.smartPipeline = null;
|
|
18
19
|
|
|
19
20
|
try {
|
|
20
21
|
this.smartPipeline = require('./analyzer-smart-pipeline.js');
|
|
21
|
-
|
|
22
|
+
if (this.verbose) {
|
|
23
|
+
console.log('[DEBUG] 🎯 C029: Smart Pipeline loaded (3-stage: Regex → AST → Data Flow)');
|
|
24
|
+
}
|
|
22
25
|
} catch (error) {
|
|
23
|
-
|
|
26
|
+
if (this.verbose) {
|
|
27
|
+
console.warn('[DEBUG] ⚠️ C029: Smart Pipeline failed, using fallback:', error.message);
|
|
28
|
+
}
|
|
24
29
|
this.smartPipeline = null;
|
|
25
30
|
}
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
async analyze(files, language, options = {}) {
|
|
34
|
+
// Store verbose option for this analysis
|
|
35
|
+
this.verbose = options.verbose || this.verbose || false;
|
|
36
|
+
|
|
29
37
|
// Use Smart Pipeline as primary choice
|
|
30
38
|
if (this.smartPipeline) {
|
|
31
|
-
|
|
39
|
+
if (this.verbose) {
|
|
40
|
+
console.log('[DEBUG] 🎯 C029: Using Smart Pipeline (3-stage analysis)...');
|
|
41
|
+
}
|
|
32
42
|
return await this.smartPipeline.analyze(files, language, options);
|
|
33
43
|
} else {
|
|
34
|
-
|
|
44
|
+
if (this.verbose) {
|
|
45
|
+
console.log('[DEBUG] 🔍 C029: Using fallback regex analysis...');
|
|
46
|
+
}
|
|
35
47
|
return await this.analyzeWithRegex(files, language, options);
|
|
36
48
|
}
|
|
37
49
|
}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* C047 Semantic Rule - Adapted for Shared Symbol Table
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const C047SymbolAnalyzerEnhanced = require('./symbol-analyzer-enhanced');
|
|
6
|
+
|
|
7
|
+
class C047SemanticRule extends C047SymbolAnalyzerEnhanced {
|
|
8
|
+
constructor(options = {}) {
|
|
9
|
+
super();
|
|
10
|
+
this.options = options;
|
|
11
|
+
this.verbose = options.verbose || false; // Store verbose setting
|
|
12
|
+
this.currentViolations = []; // Store violations for heuristic engine compatibility
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Initialize the semantic rule (required by heuristic engine)
|
|
17
|
+
*/
|
|
18
|
+
async initialize(semanticEngine = null) {
|
|
19
|
+
if (this.verbose) {
|
|
20
|
+
console.log(`[DEBUG] 🔧 Initializing C047 semantic rule...`);
|
|
21
|
+
}
|
|
22
|
+
// Store semantic engine reference if provided
|
|
23
|
+
if (semanticEngine) {
|
|
24
|
+
this.semanticEngine = semanticEngine;
|
|
25
|
+
}
|
|
26
|
+
// Load configuration from parent class
|
|
27
|
+
await this.loadConfiguration();
|
|
28
|
+
if (this.verbose) {
|
|
29
|
+
console.log(`[DEBUG] ✅ C047 semantic rule initialized`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Analyze single file (required by heuristic engine)
|
|
35
|
+
*/
|
|
36
|
+
async analyzeFile(filePath, options = {}) {
|
|
37
|
+
if (this.verbose) {
|
|
38
|
+
console.log(`[DEBUG] 🔍 C047: Analyzing file ${filePath}`);
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
// Use parent analyze method for single file
|
|
42
|
+
const violations = await this.analyze([filePath], 'typescript', options);
|
|
43
|
+
this.currentViolations = violations || [];
|
|
44
|
+
if (this.verbose || options.verbose) {
|
|
45
|
+
console.log(`✅ C047: Found ${this.currentViolations.length} violations in ${filePath}`);
|
|
46
|
+
}
|
|
47
|
+
} catch (error) {
|
|
48
|
+
if (this.verbose || options.verbose) {
|
|
49
|
+
console.error(`❌ C047 analysis failed for ${filePath}:`, error.message);
|
|
50
|
+
}
|
|
51
|
+
this.currentViolations = [];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get violations (required by heuristic engine)
|
|
57
|
+
*/
|
|
58
|
+
getViolations() {
|
|
59
|
+
return this.currentViolations;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Clear violations (required by heuristic engine)
|
|
64
|
+
*/
|
|
65
|
+
clearViolations() {
|
|
66
|
+
this.currentViolations = [];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* New method: Analyze using shared Symbol Table
|
|
71
|
+
* This is more efficient than creating separate ts-morph projects
|
|
72
|
+
*/
|
|
73
|
+
async analyzeWithSymbolTable(symbolTable, options = {}) {
|
|
74
|
+
if (this.verbose) {
|
|
75
|
+
console.log(`[DEBUG] 🔍 C047 Semantic Rule: Using shared Symbol Table...`);
|
|
76
|
+
}
|
|
77
|
+
const startTime = Date.now();
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
// Skip the project initialization since we use shared Symbol Table
|
|
81
|
+
if (this.verbose) {
|
|
82
|
+
console.log(`[DEBUG] 📋 Step 1: Using shared configuration...`);
|
|
83
|
+
}
|
|
84
|
+
await this.loadConfiguration();
|
|
85
|
+
|
|
86
|
+
// Use shared Symbol Table instead of creating new project
|
|
87
|
+
if (this.verbose) {
|
|
88
|
+
console.log(`[DEBUG] 🏗️ Step 2: Using shared Symbol Table...`);
|
|
89
|
+
}
|
|
90
|
+
this.project = symbolTable.project;
|
|
91
|
+
|
|
92
|
+
// Detect retry patterns using cached symbols
|
|
93
|
+
if (this.verbose) {
|
|
94
|
+
console.log(`[DEBUG] 🔍 Step 3: Detecting retry patterns with Symbol Table...`);
|
|
95
|
+
}
|
|
96
|
+
const allRetryPatterns = await this.detectRetryPatternsWithSymbolTable(symbolTable, options);
|
|
97
|
+
if (this.verbose) {
|
|
98
|
+
console.log(`[DEBUG] ✅ Pattern detection complete: ${allRetryPatterns.length} patterns`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Group by layers and flows
|
|
102
|
+
if (this.verbose) {
|
|
103
|
+
console.log(`[DEBUG] 📊 Step 4: Grouping patterns...`);
|
|
104
|
+
}
|
|
105
|
+
const layeredPatterns = this.groupByLayersAndFlows(allRetryPatterns);
|
|
106
|
+
if (this.verbose) {
|
|
107
|
+
console.log(`[DEBUG] ✅ Grouping complete`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Apply violation detection logic
|
|
111
|
+
if (this.verbose) {
|
|
112
|
+
console.log(`[DEBUG] ⚠️ Step 5: Detecting violations...`);
|
|
113
|
+
}
|
|
114
|
+
const violations = this.detectViolations(layeredPatterns);
|
|
115
|
+
if (this.verbose) {
|
|
116
|
+
console.log(`[DEBUG] ✅ Violation detection complete: ${violations.length} violations`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const duration = Date.now() - startTime;
|
|
120
|
+
if (this.verbose) {
|
|
121
|
+
console.log(`[DEBUG] 🎯 C047 Semantic analysis complete in ${duration}ms!`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (options.verbose) {
|
|
125
|
+
this.printAnalysisStats(allRetryPatterns, layeredPatterns, violations);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return violations;
|
|
129
|
+
|
|
130
|
+
} catch (error) {
|
|
131
|
+
console.error('❌ C047 Semantic rule failed:', error.message);
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Detect retry patterns using cached Symbol Table
|
|
138
|
+
*/
|
|
139
|
+
async detectRetryPatternsWithSymbolTable(symbolTable, options) {
|
|
140
|
+
if (this.verbose) {
|
|
141
|
+
console.log(`[DEBUG] 🔍 Detecting retry patterns with Symbol Table...`);
|
|
142
|
+
}
|
|
143
|
+
const allPatterns = [];
|
|
144
|
+
|
|
145
|
+
// Use cached source files from Symbol Table
|
|
146
|
+
const sourceFiles = symbolTable.sourceFiles;
|
|
147
|
+
if (this.verbose) {
|
|
148
|
+
console.log(`[DEBUG] 📄 Found ${sourceFiles.length} source files in Symbol Table`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
for (let i = 0; i < sourceFiles.length; i++) {
|
|
152
|
+
const sourceFile = sourceFiles[i];
|
|
153
|
+
const fileName = sourceFile.getBaseName();
|
|
154
|
+
|
|
155
|
+
if (this.verbose || options.verbose) {
|
|
156
|
+
console.log(`[DEBUG] 🔍 Analyzing ${i + 1}/${sourceFiles.length}: ${fileName}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
// Check if symbols are already cached
|
|
161
|
+
const cachedSymbols = symbolTable.getSymbols(sourceFile.getFilePath());
|
|
162
|
+
let filePatterns;
|
|
163
|
+
|
|
164
|
+
if (cachedSymbols && this.options.useSymbolCache) {
|
|
165
|
+
// Use cached symbols for faster analysis
|
|
166
|
+
filePatterns = await this.analyzeWithCachedSymbols(sourceFile, cachedSymbols);
|
|
167
|
+
} else {
|
|
168
|
+
// Fallback to direct AST analysis
|
|
169
|
+
filePatterns = await this.analyzeSourceFile(sourceFile);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
allPatterns.push(...filePatterns);
|
|
173
|
+
|
|
174
|
+
if (this.verbose || options.verbose) {
|
|
175
|
+
console.log(`[DEBUG] ✅ Found ${filePatterns.length} patterns in ${fileName}`);
|
|
176
|
+
}
|
|
177
|
+
} catch (error) {
|
|
178
|
+
if (this.verbose) {
|
|
179
|
+
console.warn(`[DEBUG] ⚠️ Error analyzing ${fileName}: ${error.message}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (this.verbose) {
|
|
185
|
+
console.log(`[DEBUG] 🎯 Total patterns detected: ${allPatterns.length}`);
|
|
186
|
+
}
|
|
187
|
+
return allPatterns;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Analyze using pre-cached symbols (faster)
|
|
192
|
+
*/
|
|
193
|
+
async analyzeWithCachedSymbols(sourceFile, cachedSymbols) {
|
|
194
|
+
const patterns = [];
|
|
195
|
+
const filePath = sourceFile.getFilePath() || sourceFile.getBaseName();
|
|
196
|
+
|
|
197
|
+
if (this.verbose) {
|
|
198
|
+
console.log(`[DEBUG] 📁 Analyzing ${require('path').basename(filePath)} with cached symbols`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Process cached classes
|
|
202
|
+
for (const classSymbol of cachedSymbols.classes) {
|
|
203
|
+
if (this.verbose) {
|
|
204
|
+
console.log(`[DEBUG] 📦 Cached class: ${classSymbol.name}`);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
for (const methodName of classSymbol.methods) {
|
|
208
|
+
const fullFunctionName = `${classSymbol.name}.${methodName}`;
|
|
209
|
+
if (this.verbose) {
|
|
210
|
+
console.log(`[DEBUG] 🎯 Cached method: ${fullFunctionName}`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Get the actual AST node for detailed analysis
|
|
214
|
+
const classNode = sourceFile.getClasses().find(c => c.getName() === classSymbol.name);
|
|
215
|
+
if (classNode) {
|
|
216
|
+
const methodNode = classNode.getMethods().find(m => m.getName() === methodName);
|
|
217
|
+
if (methodNode) {
|
|
218
|
+
const patterns_found = await this.analyzeFunction(methodNode, fullFunctionName, filePath);
|
|
219
|
+
patterns.push(...patterns_found);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Process cached functions
|
|
226
|
+
for (const functionSymbol of cachedSymbols.functions) {
|
|
227
|
+
if (this.verbose) {
|
|
228
|
+
console.log(`[DEBUG] 🔧 Cached function: ${functionSymbol.name}`);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const functionNode = sourceFile.getFunctions().find(f => f.getName() === functionSymbol.name);
|
|
232
|
+
if (functionNode) {
|
|
233
|
+
const patterns_found = await this.analyzeFunction(functionNode, functionSymbol.name, filePath);
|
|
234
|
+
patterns.push(...patterns_found);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Process cached variables (for React components)
|
|
239
|
+
for (const variableSymbol of cachedSymbols.variables) {
|
|
240
|
+
if (this.verbose) {
|
|
241
|
+
console.log(`[DEBUG] ⚡ Cached variable: ${variableSymbol.name}`);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const varDecl = sourceFile.getVariableDeclarations().find(v => v.getName() === variableSymbol.name);
|
|
245
|
+
if (varDecl) {
|
|
246
|
+
const initializer = varDecl.getInitializer();
|
|
247
|
+
if (initializer && (initializer.getKind() === require('ts-morph').SyntaxKind.ArrowFunction ||
|
|
248
|
+
initializer.getKind() === require('ts-morph').SyntaxKind.FunctionExpression)) {
|
|
249
|
+
|
|
250
|
+
// Check for useQuery calls with retry
|
|
251
|
+
const useQueryPatterns = this.detectUseQueryRetryPatterns(initializer, variableSymbol.name, filePath);
|
|
252
|
+
patterns.push(...useQueryPatterns);
|
|
253
|
+
|
|
254
|
+
// Also analyze for standard retry patterns
|
|
255
|
+
const patterns_found = await this.analyzeFunction(initializer, variableSymbol.name, filePath);
|
|
256
|
+
patterns.push(...patterns_found);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (this.verbose) {
|
|
262
|
+
console.log(`[DEBUG] 📊 Total patterns found with cached symbols: ${patterns.length}`);
|
|
263
|
+
}
|
|
264
|
+
return patterns;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Traditional analyze method (for backward compatibility)
|
|
269
|
+
*/
|
|
270
|
+
async analyze(files, language, options = {}) {
|
|
271
|
+
if (this.verbose) {
|
|
272
|
+
console.log(`[DEBUG] ⚠️ C047: Using traditional analysis (consider upgrading to Symbol Table)`);
|
|
273
|
+
}
|
|
274
|
+
return super.analyze(files, language, options);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
module.exports = C047SemanticRule;
|