mm_eslint 1.4.5 → 1.4.7
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/README.md +26 -0
- package/README_EN.md +27 -1
- package/index.js +153 -297
- package/{config.js → lib/config.js} +59 -32
- package/lib/detector/class_instance_name.js +246 -0
- package/lib/detector/class_name.js +261 -0
- package/lib/detector/const_name.js +529 -0
- package/lib/detector/function_name.js +318 -0
- package/lib/detector/index.js +18 -0
- package/lib/detector/name.js +626 -0
- package/lib/detector/object_name.js +245 -0
- package/lib/detector/param_name.js +247 -0
- package/lib/detector/variable_name.js +286 -0
- package/lib/fix/class_fix.js +83 -0
- package/lib/fix/export_fix.js +169 -0
- package/lib/fix/function_fix.js +85 -0
- package/lib/fix/index.js +21 -0
- package/lib/fix/method_fix.js +82 -0
- package/lib/fix/param_fix.js +63 -0
- package/lib/fix/property_fix.js +93 -0
- package/lib/fix/variable_fix.js +134 -0
- package/lib/fix.js +160 -0
- package/{util.js → lib/util.js} +8 -0
- package/{validator.js → lib/validator.js} +3 -3
- package/package.json +10 -13
- package/detector.js +0 -1291
- package/eslint.config.js +0 -25
- package/fix.js +0 -441
- package/handler.js +0 -993
- /package/{corrector.js → lib/corrector.js} +0 -0
- /package/{tip.js → lib/tip.js} +0 -0
package/detector.js
DELETED
|
@@ -1,1291 +0,0 @@
|
|
|
1
|
-
const { Validator } = require('./validator');
|
|
2
|
-
const { Corrector } = require('./corrector');
|
|
3
|
-
const { Tip } = require('./tip');
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* 类型检测器类
|
|
7
|
-
* 负责根据AST节点推断命名类型
|
|
8
|
-
*/
|
|
9
|
-
class Detector {
|
|
10
|
-
/**
|
|
11
|
-
* 构造函数
|
|
12
|
-
* @param {object} config 配置对象
|
|
13
|
-
*/
|
|
14
|
-
constructor(config) {
|
|
15
|
-
this.config = config;
|
|
16
|
-
this.validator = new Validator(config);
|
|
17
|
-
this.corrector = new Corrector(config);
|
|
18
|
-
this.tip = new Tip(config);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* 检测类声明类型
|
|
23
|
-
* @param {object} node AST节点
|
|
24
|
-
* @returns {string} 类型名称
|
|
25
|
-
*/
|
|
26
|
-
detectClassType(node) {
|
|
27
|
-
if (node.type === 'ClassDeclaration') {
|
|
28
|
-
return 'class';
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (node.type === 'ClassExpression') {
|
|
32
|
-
return 'class';
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (node.type === 'VariableDeclaration' &&
|
|
36
|
-
node.declarations &&
|
|
37
|
-
node.declarations.length > 0 &&
|
|
38
|
-
node.declarations[0].init &&
|
|
39
|
-
node.declarations[0].init.type === 'ClassExpression') {
|
|
40
|
-
return 'class';
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (node.type === 'AssignmentExpression' && node.right && node.right.type === 'ClassExpression') {
|
|
44
|
-
return 'class';
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (node.type === 'Property' && node.value && node.value.type === 'ClassExpression') {
|
|
48
|
-
return 'property-class';
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// 处理 module.exports = class user_manager {} 这种形式
|
|
52
|
-
if (node.type === 'AssignmentExpression' &&
|
|
53
|
-
node.left &&
|
|
54
|
-
node.left.type === 'MemberExpression' &&
|
|
55
|
-
node.left.object &&
|
|
56
|
-
node.left.object.type === 'Identifier' &&
|
|
57
|
-
node.left.object.name === 'module' &&
|
|
58
|
-
node.left.property &&
|
|
59
|
-
node.left.property.type === 'Identifier' &&
|
|
60
|
-
node.left.property.name === 'exports' &&
|
|
61
|
-
node.right &&
|
|
62
|
-
node.right.type === 'ClassExpression') {
|
|
63
|
-
return 'class';
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// 处理 module.exports = { Admin: Admin, AdminManager } 这种形式的引用
|
|
67
|
-
if ((node.type === 'Property' || node.type === 'ObjectProperty') && node.key && node.key.type === 'Identifier') {
|
|
68
|
-
// 检查是否在导出语句的上下文中
|
|
69
|
-
var parent = node.parent;
|
|
70
|
-
if (parent && parent.type === 'ObjectExpression') {
|
|
71
|
-
var grand_parent = parent.parent;
|
|
72
|
-
if (grand_parent && grand_parent.type === 'AssignmentExpression' &&
|
|
73
|
-
grand_parent.left && grand_parent.left.type === 'MemberExpression' &&
|
|
74
|
-
grand_parent.left.object && grand_parent.left.object.type === 'Identifier' &&
|
|
75
|
-
grand_parent.left.object.name === 'module' &&
|
|
76
|
-
grand_parent.left.property && grand_parent.left.property.type === 'Identifier' &&
|
|
77
|
-
grand_parent.left.property.name === 'exports') {
|
|
78
|
-
|
|
79
|
-
// 通用类型溯源:对于导出语句中的属性,溯源其引用的类型
|
|
80
|
-
var traced_type = this._traceReferenceType(node);
|
|
81
|
-
if (traced_type) {
|
|
82
|
-
return traced_type;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// 如果无法溯源到具体类型,返回null让其他检测器处理
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return null;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* 检测类实例类型
|
|
96
|
-
* @param {object} node AST节点
|
|
97
|
-
* @returns {string} 类型名称
|
|
98
|
-
*/
|
|
99
|
-
detectClassInstanceType(node) {
|
|
100
|
-
// VariableDeclaration 中的 new 表达式
|
|
101
|
-
if (node.type === 'VariableDeclaration') {
|
|
102
|
-
if (node.declarations && node.declarations.length > 0) {
|
|
103
|
-
var decl = node.declarations[0];
|
|
104
|
-
if (decl.init && decl.init.type === 'NewExpression') {
|
|
105
|
-
return 'class-instance';
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (node.type === 'Property' && node.value && node.value.type === 'NewExpression') {
|
|
111
|
-
return 'property-class-instance';
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (node.type === 'AssignmentExpression' && node.right && node.right.type === 'NewExpression') {
|
|
115
|
-
return 'property-class-instance';
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (node.type === 'PropertyDefinition' && node.key && node.value && node.value.type === 'NewExpression') {
|
|
119
|
-
// 判断是否是私有字段
|
|
120
|
-
if (node.key.type === 'PrivateIdentifier') {
|
|
121
|
-
return 'private-class-instance';
|
|
122
|
-
}
|
|
123
|
-
return node.static ? 'static-class-instance' : 'property-class-instance';
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// 正则表达式字面量视为类实例
|
|
127
|
-
if (node.type === 'VariableDeclaration') {
|
|
128
|
-
if (node.declarations && node.declarations.length > 0) {
|
|
129
|
-
var decl = node.declarations[0];
|
|
130
|
-
if (decl.init && decl.init.type === 'Literal' && decl.init.regex) {
|
|
131
|
-
return 'class-instance';
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (node.type === 'Property' && node.value && node.value.type === 'Literal' && node.value.regex) {
|
|
137
|
-
return 'property-class-instance';
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return null;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* 检测函数类型
|
|
145
|
-
* @param {object} node AST节点
|
|
146
|
-
* @returns {string} 类型名称
|
|
147
|
-
*/
|
|
148
|
-
detectFunctionType(node) {
|
|
149
|
-
if (node.type === 'FunctionDeclaration') {
|
|
150
|
-
return 'function';
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// 检测函数表达式内部的函数名
|
|
154
|
-
if (node.type === 'FunctionExpression' && node.id && node.id.type === 'Identifier') {
|
|
155
|
-
return 'function';
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (node.type === 'VariableDeclaration' && node.declarations && node.declarations.length > 0) {
|
|
159
|
-
var decl = node.declarations[0];
|
|
160
|
-
if (decl.init && (decl.init.type === 'FunctionExpression' || decl.init.type === 'ArrowFunctionExpression')) {
|
|
161
|
-
return 'function';
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// 检测VariableDeclarator节点中的箭头函数
|
|
166
|
-
if (node.type === 'VariableDeclarator' && node.init &&
|
|
167
|
-
(node.init.type === 'FunctionExpression' || node.init.type === 'ArrowFunctionExpression')) {
|
|
168
|
-
return 'function';
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
if (node.type === 'Property' && node.value &&
|
|
172
|
-
(node.value.type === 'FunctionExpression' || node.value.type === 'ArrowFunctionExpression')) {
|
|
173
|
-
return 'property-function';
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
if (node.type === 'MethodDefinition') {
|
|
177
|
-
// 检测私有方法(ES6私有方法语法 # 前缀)
|
|
178
|
-
if (node.key && node.key.type === 'PrivateIdentifier') {
|
|
179
|
-
return 'private-function';
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// 检测传统私有方法约定(_ 前缀)
|
|
183
|
-
if (node.key && node.key.type === 'Identifier' && node.key.name.startsWith('_')) {
|
|
184
|
-
return node.static ? 'static-function' : 'internal-function';
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
return node.static ? 'static-function' : 'function';
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
if (node.type === 'PropertyDefinition' && node.value &&
|
|
191
|
-
(node.value.type === 'FunctionExpression' || node.value.type === 'ArrowFunctionExpression')) {
|
|
192
|
-
return node.static ? 'static-function' : 'property-function';
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
if (node.type === 'AssignmentExpression' && node.right &&
|
|
196
|
-
(node.right.type === 'FunctionExpression' || node.right.type === 'ArrowFunctionExpression')) {
|
|
197
|
-
return 'property-function';
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
if (node.type === 'CallExpression' && node.callee && node.callee.type === 'Identifier') {
|
|
201
|
-
// 函数调用应该被识别为函数使用,而不是函数定义
|
|
202
|
-
return 'use-function';
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
return null;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* 检测变量类型
|
|
210
|
-
* @param {object} node AST节点
|
|
211
|
-
* @returns {string} 类型名称
|
|
212
|
-
*/
|
|
213
|
-
detectVariableType(node) {
|
|
214
|
-
// let/var 声明
|
|
215
|
-
if (node.type === 'VariableDeclaration' && (node.kind === 'let' || node.kind === 'var')) {
|
|
216
|
-
return 'variable';
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// for 循环中的变量
|
|
220
|
-
if (node.type === 'ForStatement' && node.init && node.init.type === 'VariableDeclaration' &&
|
|
221
|
-
(node.init.kind === 'let' || node.init.kind === 'var')) {
|
|
222
|
-
return 'variable';
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// 对象属性变量 - 先排除函数类型和常量属性
|
|
226
|
-
if ((node.type === 'Property' || node.type === 'ObjectProperty')) {
|
|
227
|
-
// 如果是函数类型,不应该识别为变量
|
|
228
|
-
if (node.value && (node.value.type === 'FunctionExpression' || node.value.type === 'ArrowFunctionExpression')) {
|
|
229
|
-
return null;
|
|
230
|
-
}
|
|
231
|
-
// 属性简写(如 { user_name })应该识别为变量
|
|
232
|
-
if (node.shorthand && node.value && node.value.type === 'Identifier') {
|
|
233
|
-
return 'property-variable';
|
|
234
|
-
}
|
|
235
|
-
// 只有字面量值且不是常量属性才识别为变量
|
|
236
|
-
if (this._isLiteralValue(node.value)) {
|
|
237
|
-
// 检查是否为常量属性(父级是const声明)
|
|
238
|
-
var parent_type = this._getParentDeclarationType(node);
|
|
239
|
-
if (parent_type !== 'const') {
|
|
240
|
-
return 'property-variable';
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// 类属性变量
|
|
246
|
-
if (node.type === 'PropertyDefinition' && this._isLiteralValue(node.value)) {
|
|
247
|
-
return node.static ? 'static-variable' : 'property-variable';
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// 赋值表达式
|
|
251
|
-
if (node.type === 'AssignmentExpression' && this._isLiteralValue(node.right)) {
|
|
252
|
-
return 'property-variable';
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
return null;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* 检测声明方式问题
|
|
260
|
-
* @param {object} node AST节点
|
|
261
|
-
* @returns {string} 问题类型
|
|
262
|
-
*/
|
|
263
|
-
detectDeclarationIssue(node) {
|
|
264
|
-
// const 声明但包含非基础字面量 - 应该用let而不是const
|
|
265
|
-
if (node.type === 'VariableDeclaration' && node.kind === 'const') {
|
|
266
|
-
if (node.declarations && node.declarations.length > 0) {
|
|
267
|
-
var decl = node.declarations[0];
|
|
268
|
-
if (decl.init && !this._isBasicLiteralValue(decl.init)) {
|
|
269
|
-
// 排除函数表达式和箭头函数表达式,它们应该使用const声明
|
|
270
|
-
if (decl.init.type === 'FunctionExpression' || decl.init.type === 'ArrowFunctionExpression') {
|
|
271
|
-
return null;
|
|
272
|
-
}
|
|
273
|
-
return 'const-should-be-let';
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
return null;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* 检测常量类型
|
|
283
|
-
* @param {object} node AST节点
|
|
284
|
-
* @returns {string} 类型名称
|
|
285
|
-
*/
|
|
286
|
-
detectConstantType(node) {
|
|
287
|
-
// const 声明 - 当右侧是字面量值(包括对象和数组)时视为常量
|
|
288
|
-
if (node.type === 'VariableDeclaration' && node.kind === 'const') {
|
|
289
|
-
// 检查右侧是否是字面量值
|
|
290
|
-
if (node.declarations && node.declarations.length > 0) {
|
|
291
|
-
var decl = node.declarations[0];
|
|
292
|
-
if (decl.init && this._isLiteralValue(decl.init)) {
|
|
293
|
-
return 'constant';
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// var 和 let 声明应该被识别为变量,而不是常量
|
|
299
|
-
if (node.type === 'VariableDeclaration' && (node.kind === 'var' || node.kind === 'let')) {
|
|
300
|
-
return null; // 返回null,让其他检测器处理
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// 对象属性常量 - 只有当父级是const声明且属性值是字面量时才视为常量
|
|
304
|
-
if ((node.type === 'Property' || node.type === 'ObjectProperty')) {
|
|
305
|
-
// 只有当值是字面量时才视为常量
|
|
306
|
-
if (this._isLiteralValue(node.value)) {
|
|
307
|
-
var parent_type = this._getParentDeclarationType(node);
|
|
308
|
-
if (parent_type === 'const') {
|
|
309
|
-
return 'property-constant';
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// 类属性常量 - 只有当属性名符合常量命名规范时才识别为常量
|
|
315
|
-
if (node.type === 'PropertyDefinition' && this._isLiteralValue(node.value)) {
|
|
316
|
-
if (node.static) {
|
|
317
|
-
return 'static-variable';
|
|
318
|
-
}
|
|
319
|
-
// 类中的静态属性应该被识别为静态属性-变量,而不是静态属性-常量
|
|
320
|
-
return 'variable';
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
return null;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* 检测参数类型
|
|
328
|
-
* @param {object} node AST节点
|
|
329
|
-
* @returns {string} 类型名称
|
|
330
|
-
*/
|
|
331
|
-
detectParamType(node) {
|
|
332
|
-
// 只在真正的函数参数节点上返回参数类型
|
|
333
|
-
if (node.type === 'Identifier' && node.parent &&
|
|
334
|
-
(node.parent.type === 'FunctionDeclaration' ||
|
|
335
|
-
node.parent.type === 'FunctionExpression' ||
|
|
336
|
-
node.parent.type === 'ArrowFunctionExpression') &&
|
|
337
|
-
node.parent.params && node.parent.params.includes(node)) {
|
|
338
|
-
|
|
339
|
-
// 增强版参数类型检测:根据使用上下文推断参数类型
|
|
340
|
-
var param_type = this._inferParamTypeFromContext(node);
|
|
341
|
-
|
|
342
|
-
// 如果使用上下文无法推断,尝试其他方法
|
|
343
|
-
if (!param_type) {
|
|
344
|
-
// 基于命名约定推断(优先级高于命名风格)
|
|
345
|
-
param_type = this._inferParamTypeByNamingConvention(node.name);
|
|
346
|
-
|
|
347
|
-
// 如果命名约定也无法推断,基于命名风格推断
|
|
348
|
-
if (!param_type) {
|
|
349
|
-
param_type = this._inferParamTypeByStyle(node.name);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// 对于箭头函数参数,优先推断为变量类型
|
|
354
|
-
if (node.parent && node.parent.type === 'ArrowFunctionExpression') {
|
|
355
|
-
param_type = 'variable';
|
|
356
|
-
}
|
|
357
|
-
return param_type ? 'param-' + param_type : 'param';
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
// 不是参数节点,返回null让其他检测器处理
|
|
361
|
-
return null;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
/**
|
|
365
|
-
* 根据上下文推断参数类型
|
|
366
|
-
* @param {object} param_node 参数节点
|
|
367
|
-
* @returns {string} 参数类型(function/class/variable)
|
|
368
|
-
*/
|
|
369
|
-
_inferParamTypeFromContext(param_node) {
|
|
370
|
-
var function_node = param_node.parent;
|
|
371
|
-
var param_name = param_node.name;
|
|
372
|
-
|
|
373
|
-
// 如果没有函数体,无法进行使用上下文推断
|
|
374
|
-
if (!function_node.body) {
|
|
375
|
-
return null; // 无法推断,返回null让上层逻辑处理
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// 收集参数在函数体中的所有使用方式
|
|
379
|
-
var usage_types = [];
|
|
380
|
-
|
|
381
|
-
this._traverseFunctionBody(function_node.body, function (node) {
|
|
382
|
-
// 检查调用表达式:param_name() → 函数类型
|
|
383
|
-
if (node.type === 'CallExpression' &&
|
|
384
|
-
node.callee.type === 'Identifier' &&
|
|
385
|
-
node.callee.name === param_name) {
|
|
386
|
-
usage_types.push('function_call');
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
// 检查new表达式:new param_name() → 类类型
|
|
390
|
-
else if (node.type === 'NewExpression' &&
|
|
391
|
-
node.callee.type === 'Identifier' &&
|
|
392
|
-
node.callee.name === param_name) {
|
|
393
|
-
usage_types.push('class_instantiation');
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
// 检查成员表达式:param_name.property → 对象类型
|
|
397
|
-
else if (node.type === 'MemberExpression' &&
|
|
398
|
-
node.object.type === 'Identifier' &&
|
|
399
|
-
node.object.name === param_name) {
|
|
400
|
-
usage_types.push('property_access');
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
// 检查方法调用:param_name.method() → 对象类型(类实例)
|
|
404
|
-
else if (node.type === 'CallExpression' &&
|
|
405
|
-
node.callee.type === 'MemberExpression' &&
|
|
406
|
-
node.callee.object.type === 'Identifier' &&
|
|
407
|
-
node.callee.object.name === param_name) {
|
|
408
|
-
usage_types.push('method_call');
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// 检查赋值表达式:var x = param_name → 变量类型
|
|
412
|
-
else if (node.type === 'VariableDeclarator' &&
|
|
413
|
-
node.init && node.init.type === 'Identifier' &&
|
|
414
|
-
node.init.name === param_name) {
|
|
415
|
-
usage_types.push('variable_assignment');
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// 检查作为参数传递:func(param_name) → 根据接收函数的上下文推断
|
|
419
|
-
else if (node.type === 'CallExpression') {
|
|
420
|
-
for (var i = 0; i < node.arguments.length; i++) {
|
|
421
|
-
var arg = node.arguments[i];
|
|
422
|
-
if (arg.type === 'Identifier' && arg.name === param_name) {
|
|
423
|
-
usage_types.push('function_argument');
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
// 检查直接使用:return name, var x = name → 变量类型
|
|
429
|
-
else if (node.type === 'Identifier' && node.name === param_name) {
|
|
430
|
-
// 排除已经在其他检测中处理的情况
|
|
431
|
-
var parent = node.parent;
|
|
432
|
-
if (!parent ||
|
|
433
|
-
(parent.type !== 'CallExpression' &&
|
|
434
|
-
parent.type !== 'NewExpression' &&
|
|
435
|
-
parent.type !== 'MemberExpression' &&
|
|
436
|
-
!(parent.type === 'VariableDeclarator' && parent.init === node))) {
|
|
437
|
-
usage_types.push('direct_usage');
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
return false; // 继续遍历,收集所有使用方式
|
|
442
|
-
});
|
|
443
|
-
|
|
444
|
-
// 根据使用方式推断参数类型
|
|
445
|
-
return this._inferTypeFromUsage(usage_types, param_name);
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
/**
|
|
449
|
-
* 根据使用方式推断参数类型
|
|
450
|
-
* @param {Array} usage_types 使用方式数组
|
|
451
|
-
* @param {string} param_name 参数名
|
|
452
|
-
* @returns {string} 推断的参数类型
|
|
453
|
-
*/
|
|
454
|
-
_inferTypeFromUsage(usage_types, param_name) {
|
|
455
|
-
// 如果没有使用方式,无法推断
|
|
456
|
-
if (usage_types.length === 0) {
|
|
457
|
-
return null;
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
// 优先级:函数调用 > 类实例化 > 方法调用 > 属性访问 > 函数参数 > 变量赋值 > 直接使用
|
|
461
|
-
var priority_map = {
|
|
462
|
-
'function_call': 7, // 最高优先级:明确是函数
|
|
463
|
-
'class_instantiation': 6, // 高优先级:明确是类
|
|
464
|
-
'method_call': 5, // 中高优先级:方法调用(类实例)
|
|
465
|
-
'property_access': 4, // 中优先级:对象属性访问
|
|
466
|
-
'function_argument': 3, // 低优先级:作为函数参数传递(根据接收函数推断)
|
|
467
|
-
'variable_assignment': 2, // 较低优先级:变量赋值
|
|
468
|
-
'direct_usage': 1 // 最低优先级:直接使用
|
|
469
|
-
};
|
|
470
|
-
|
|
471
|
-
// 找到最高优先级的用法
|
|
472
|
-
var highest_priority = 0;
|
|
473
|
-
var inferred_type = null;
|
|
474
|
-
|
|
475
|
-
for (var i = 0; i < usage_types.length; i++) {
|
|
476
|
-
var usage = usage_types[i];
|
|
477
|
-
var priority = priority_map[usage] || 0;
|
|
478
|
-
|
|
479
|
-
if (priority > highest_priority) {
|
|
480
|
-
highest_priority = priority;
|
|
481
|
-
|
|
482
|
-
// 根据使用方式映射到参数类型
|
|
483
|
-
switch (usage) {
|
|
484
|
-
case 'function_call':
|
|
485
|
-
inferred_type = 'function';
|
|
486
|
-
break;
|
|
487
|
-
case 'class_instantiation':
|
|
488
|
-
inferred_type = 'class';
|
|
489
|
-
break;
|
|
490
|
-
case 'method_call':
|
|
491
|
-
inferred_type = 'class-instance';
|
|
492
|
-
break;
|
|
493
|
-
case 'property_access':
|
|
494
|
-
inferred_type = 'object';
|
|
495
|
-
break;
|
|
496
|
-
case 'function_argument':
|
|
497
|
-
// 作为函数参数传递,需要根据接收函数的上下文推断
|
|
498
|
-
inferred_type = 'variable'; // 默认变量类型,让其他使用方式覆盖
|
|
499
|
-
break;
|
|
500
|
-
case 'variable_assignment':
|
|
501
|
-
case 'direct_usage':
|
|
502
|
-
inferred_type = 'variable';
|
|
503
|
-
break;
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
return inferred_type;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
/**
|
|
512
|
-
* 基于命名约定推断参数类型
|
|
513
|
-
* @param {string} param_name 参数名
|
|
514
|
-
* @returns {string} 推断的参数类型
|
|
515
|
-
*/
|
|
516
|
-
_inferParamTypeByNamingConvention(param_name) {
|
|
517
|
-
var lower_name = param_name.toLowerCase();
|
|
518
|
-
|
|
519
|
-
// 函数类型命名约定
|
|
520
|
-
if (lower_name.startsWith('on') ||
|
|
521
|
-
lower_name.startsWith('handle') ||
|
|
522
|
-
lower_name.endsWith('func') ||
|
|
523
|
-
lower_name.endsWith('callback') ||
|
|
524
|
-
lower_name.endsWith('handler')) {
|
|
525
|
-
return 'function';
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
// 类类型命名约定
|
|
529
|
-
if (lower_name.endsWith('manager') ||
|
|
530
|
-
lower_name.endsWith('service') ||
|
|
531
|
-
lower_name.endsWith('controller') ||
|
|
532
|
-
lower_name.endsWith('provider') ||
|
|
533
|
-
lower_name.endsWith('factory') ||
|
|
534
|
-
lower_name.endsWith('class') ||
|
|
535
|
-
lower_name.endsWith('model') ||
|
|
536
|
-
lower_name.endsWith('view') ||
|
|
537
|
-
lower_name.endsWith('component')) {
|
|
538
|
-
return 'class';
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
// 对象类型命名约定
|
|
542
|
-
if (lower_name.endsWith('obj') ||
|
|
543
|
-
lower_name.endsWith('object') ||
|
|
544
|
-
lower_name.endsWith('data') ||
|
|
545
|
-
lower_name.endsWith('info') ||
|
|
546
|
-
lower_name.endsWith('config') ||
|
|
547
|
-
lower_name.endsWith('options')) {
|
|
548
|
-
return 'variable';
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
return null;
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
/**
|
|
555
|
-
* 基于命名风格推断参数类型
|
|
556
|
-
* @param {string} param_name 参数名
|
|
557
|
-
* @returns {string} 推断的参数类型
|
|
558
|
-
*/
|
|
559
|
-
_inferParamTypeByStyle(param_name) {
|
|
560
|
-
var config = this.config || new (require('./config').Config)();
|
|
561
|
-
|
|
562
|
-
// 检查是否符合PascalCase(类类型)
|
|
563
|
-
if (config.getRegex('PascalCase').test(param_name)) {
|
|
564
|
-
return 'class';
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
// 检查是否符合snake_case(变量/对象类型)
|
|
568
|
-
if (config.getRegex('snake_case').test(param_name)) {
|
|
569
|
-
return 'variable';
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
// 检查是否符合camelCase(函数类型)
|
|
573
|
-
if (config.getRegex('camelCase').test(param_name)) {
|
|
574
|
-
return 'function';
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
// 默认变量类型
|
|
578
|
-
return 'variable';
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
/**
|
|
582
|
-
* 语义推断参数类型
|
|
583
|
-
* @param {object} param_node 参数节点
|
|
584
|
-
* @returns {string} 推断的参数类型
|
|
585
|
-
*/
|
|
586
|
-
_inferParamTypeSemantically(param_node) {
|
|
587
|
-
var function_node = param_node.parent;
|
|
588
|
-
var param_name = param_node.name;
|
|
589
|
-
|
|
590
|
-
// 收集函数体中所有标识符
|
|
591
|
-
var identifiers = [];
|
|
592
|
-
this._traverseFunctionBody(function_node.body, function (node) {
|
|
593
|
-
if (node.type === 'Identifier') {
|
|
594
|
-
identifiers.push({
|
|
595
|
-
name: node.name,
|
|
596
|
-
type: this._getIdentifierUsageType(node),
|
|
597
|
-
node: node
|
|
598
|
-
});
|
|
599
|
-
}
|
|
600
|
-
return false;
|
|
601
|
-
}.bind(this));
|
|
602
|
-
|
|
603
|
-
// 分析标识符与参数的语义关联
|
|
604
|
-
for (var i = 0; i < identifiers.length; i++) {
|
|
605
|
-
var identifier = identifiers[i];
|
|
606
|
-
|
|
607
|
-
// 检查标识符是否与参数名称有语义关联
|
|
608
|
-
if (this._hasSemanticRelation(param_name, identifier.name)) {
|
|
609
|
-
// 根据标识符的使用类型推断参数类型
|
|
610
|
-
switch (identifier.type) {
|
|
611
|
-
case 'function_call':
|
|
612
|
-
return 'function';
|
|
613
|
-
case 'class_instantiation':
|
|
614
|
-
return 'class';
|
|
615
|
-
case 'property_access':
|
|
616
|
-
return 'class-instance';
|
|
617
|
-
case 'variable_declaration':
|
|
618
|
-
return 'variable';
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
return null;
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
/**
|
|
627
|
-
* 检查两个标识符是否有语义关联
|
|
628
|
-
* @param {string} param_name 参数名
|
|
629
|
-
* @param {string} identifier_name 标识符名
|
|
630
|
-
* @returns {boolean} 是否有语义关联
|
|
631
|
-
*/
|
|
632
|
-
_hasSemanticRelation(param_name, identifier_name) {
|
|
633
|
-
// 简单的语义关联规则
|
|
634
|
-
|
|
635
|
-
// 1. 名称相似性检查(忽略大小写和下划线)
|
|
636
|
-
var normalized_param = param_name.toLowerCase().replace(/[_-]/g, '');
|
|
637
|
-
var normalized_ident = identifier_name.toLowerCase().replace(/[_-]/g, '');
|
|
638
|
-
|
|
639
|
-
if (normalized_param === normalized_ident) {
|
|
640
|
-
return true;
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
// 2. 包含关系检查
|
|
644
|
-
if (normalized_ident.includes(normalized_param) ||
|
|
645
|
-
normalized_param.includes(normalized_ident)) {
|
|
646
|
-
return true;
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
// 3. 词根相似性检查
|
|
650
|
-
var param_root = this._getWordRoot(normalized_param);
|
|
651
|
-
var ident_root = this._getWordRoot(normalized_ident);
|
|
652
|
-
|
|
653
|
-
if (param_root && ident_root && param_root === ident_root) {
|
|
654
|
-
return true;
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
return false;
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
/**
|
|
661
|
-
* 获取标识符的使用类型
|
|
662
|
-
* @param {object} identifier_node 标识符节点
|
|
663
|
-
* @returns {string} 使用类型
|
|
664
|
-
*/
|
|
665
|
-
_getIdentifierUsageType(identifier_node) {
|
|
666
|
-
var parent = identifier_node.parent;
|
|
667
|
-
|
|
668
|
-
if (parent.type === 'CallExpression' && parent.callee === identifier_node) {
|
|
669
|
-
return 'function_call';
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
if (parent.type === 'NewExpression' && parent.callee === identifier_node) {
|
|
673
|
-
return 'class_instantiation';
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
if (parent.type === 'MemberExpression' && parent.object === identifier_node) {
|
|
677
|
-
return 'property_access';
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
if (parent.type === 'VariableDeclarator' && parent.id === identifier_node) {
|
|
681
|
-
return 'variable_declaration';
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
return 'other';
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
/**
|
|
688
|
-
* 获取单词的词根(简化实现)
|
|
689
|
-
* @param {string} word 单词
|
|
690
|
-
* @returns {string} 词根
|
|
691
|
-
*/
|
|
692
|
-
_getWordRoot(word) {
|
|
693
|
-
// 简单的词根提取规则
|
|
694
|
-
var common_suffixes = ['s', 'es', 'ed', 'ing', 'er', 'or', 'ment', 'tion', 'ity'];
|
|
695
|
-
|
|
696
|
-
for (var i = 0; i < common_suffixes.length; i++) {
|
|
697
|
-
var suffix = common_suffixes[i];
|
|
698
|
-
if (word.endsWith(suffix) && word.length > suffix.length) {
|
|
699
|
-
return word.slice(0, -suffix.length);
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
return word;
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
/**
|
|
707
|
-
* 遍历函数体
|
|
708
|
-
* @param {object} body_node 函数体节点
|
|
709
|
-
* @param {function} visitor 访问器函数
|
|
710
|
-
*/
|
|
711
|
-
_traverseFunctionBody(body_node, visitor) {
|
|
712
|
-
if (!body_node || !visitor) return;
|
|
713
|
-
|
|
714
|
-
// 简单的AST遍历实现
|
|
715
|
-
var nodes_to_visit = [body_node];
|
|
716
|
-
|
|
717
|
-
while (nodes_to_visit.length > 0) {
|
|
718
|
-
var current_node = nodes_to_visit.pop();
|
|
719
|
-
|
|
720
|
-
// 调用访问器,如果返回true则停止遍历
|
|
721
|
-
if (visitor(current_node)) {
|
|
722
|
-
return;
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
// 添加子节点到遍历队列
|
|
726
|
-
if (current_node.body) {
|
|
727
|
-
if (Array.isArray(current_node.body)) {
|
|
728
|
-
nodes_to_visit.push(...current_node.body.reverse());
|
|
729
|
-
} else {
|
|
730
|
-
nodes_to_visit.push(current_node.body);
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
if (current_node.expression) {
|
|
735
|
-
nodes_to_visit.push(current_node.expression);
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
if (current_node.consequent) {
|
|
739
|
-
nodes_to_visit.push(current_node.consequent);
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
if (current_node.alternate) {
|
|
743
|
-
nodes_to_visit.push(current_node.alternate);
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
if (current_node.argument) {
|
|
747
|
-
nodes_to_visit.push(current_node.argument);
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
if (current_node.arguments && Array.isArray(current_node.arguments)) {
|
|
751
|
-
nodes_to_visit.push(...current_node.arguments.reverse());
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
if (current_node.elements && Array.isArray(current_node.elements)) {
|
|
755
|
-
nodes_to_visit.push(...current_node.elements.reverse());
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
if (current_node.properties && Array.isArray(current_node.properties)) {
|
|
759
|
-
nodes_to_visit.push(...current_node.properties.reverse());
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
/**
|
|
765
|
-
* 检测命名类型
|
|
766
|
-
* @param {object} node AST节点
|
|
767
|
-
* @param {string} name 名称
|
|
768
|
-
* @returns {object} 检测结果
|
|
769
|
-
*/
|
|
770
|
-
detect(node, name) {
|
|
771
|
-
var type = null;
|
|
772
|
-
|
|
773
|
-
// 按优先级检测类型 - 变量检测应该在常量检测之前
|
|
774
|
-
var detectors = [
|
|
775
|
-
this.detectClassType.bind(this),
|
|
776
|
-
this.detectClassInstanceType.bind(this),
|
|
777
|
-
this.detectFunctionType.bind(this), // 函数检测应该在变量检测之前
|
|
778
|
-
this.detectConstantType.bind(this), // 常量检测优先级提高
|
|
779
|
-
this.detectVariableType.bind(this), // 变量检测优先级降低
|
|
780
|
-
this.detectParamType.bind(this)
|
|
781
|
-
];
|
|
782
|
-
|
|
783
|
-
for (var i = 0; i < detectors.length; i++) {
|
|
784
|
-
type = detectors[i](node);
|
|
785
|
-
if (type) {
|
|
786
|
-
break;
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
// 如果检测到类实例、函数等特殊类型,跳过声明问题检测
|
|
791
|
-
if (type && (type.includes('class-instance') || type.includes('function'))) {
|
|
792
|
-
// 跳过声明问题检测,直接进行命名规范验证
|
|
793
|
-
} else {
|
|
794
|
-
// 检测声明方式问题(优先级较低)
|
|
795
|
-
var original_type = this.detectDeclarationIssue(node);
|
|
796
|
-
if (original_type) {
|
|
797
|
-
// 如果是声明方式问题,直接返回相应的错误,不进行命名规范验证
|
|
798
|
-
var message = this.tip.getDeclarationIssueMessage(original_type, name);
|
|
799
|
-
return {
|
|
800
|
-
name,
|
|
801
|
-
node,
|
|
802
|
-
message,
|
|
803
|
-
error_type: 'declaration',
|
|
804
|
-
severity: 'error',
|
|
805
|
-
fix_suggestion: null,
|
|
806
|
-
original_type
|
|
807
|
-
};
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
if (!type) {
|
|
812
|
-
type = 'variable'; // 默认类型
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
var error = this.validator.validate(name, type);
|
|
816
|
-
if (!error) {
|
|
817
|
-
return null;
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
var fix_suggestion = this.corrector.getSuggestion(error);
|
|
821
|
-
var message = this.tip.getFullMessage(error, fix_suggestion);
|
|
822
|
-
|
|
823
|
-
return {
|
|
824
|
-
name,
|
|
825
|
-
node,
|
|
826
|
-
message,
|
|
827
|
-
error_type: error.type,
|
|
828
|
-
severity: error.severity,
|
|
829
|
-
fix_suggestion: fix_suggestion,
|
|
830
|
-
original_type: type
|
|
831
|
-
};
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
/**
|
|
835
|
-
* 检测命名类型(仅返回类型,不验证)
|
|
836
|
-
* @param {object} node AST节点
|
|
837
|
-
* @returns {string} 检测到的类型
|
|
838
|
-
*/
|
|
839
|
-
detectTypeOnly(node) {
|
|
840
|
-
var type = null;
|
|
841
|
-
|
|
842
|
-
// 按优先级检测类型 - 变量检测应该在常量检测之前
|
|
843
|
-
var detectors = [
|
|
844
|
-
this.detectClassType.bind(this),
|
|
845
|
-
this.detectClassInstanceType.bind(this),
|
|
846
|
-
this.detectFunctionType.bind(this), // 函数检测应该在变量检测之前
|
|
847
|
-
this.detectConstantType.bind(this), // 常量检测优先级提高
|
|
848
|
-
this.detectVariableType.bind(this), // 变量检测优先级降低
|
|
849
|
-
this.detectParamType.bind(this)
|
|
850
|
-
];
|
|
851
|
-
|
|
852
|
-
for (var i = 0; i < detectors.length; i++) {
|
|
853
|
-
type = detectors[i](node);
|
|
854
|
-
if (type) {
|
|
855
|
-
break;
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
|
|
859
|
-
// 特殊处理:属性简写应该识别为变量,而不是常量
|
|
860
|
-
if (node.type === 'Property' && node.shorthand && node.value && node.value.type === 'Identifier') {
|
|
861
|
-
// 如果是属性简写,强制识别为变量
|
|
862
|
-
type = 'property-variable';
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
if (!type) {
|
|
866
|
-
type = 'variable'; // 默认类型
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
return type;
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
/**
|
|
873
|
-
* 判断是否为字面量值
|
|
874
|
-
* @param {object} node AST节点
|
|
875
|
-
* @returns {boolean} 是否为字面量
|
|
876
|
-
*/
|
|
877
|
-
_isLiteralValue(node) {
|
|
878
|
-
if (!node) {
|
|
879
|
-
return false;
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
switch (node.type) {
|
|
883
|
-
case 'Literal':
|
|
884
|
-
return !node.regex; // 排除正则表达式
|
|
885
|
-
case 'TemplateLiteral':
|
|
886
|
-
return true;
|
|
887
|
-
case 'Identifier':
|
|
888
|
-
var literal_identifiers = ['undefined', 'null', 'Infinity', 'NaN', 'true', 'false'];
|
|
889
|
-
return literal_identifiers.includes(node.name);
|
|
890
|
-
case 'ArrayExpression':
|
|
891
|
-
if (node.elements && node.elements.length > 0) {
|
|
892
|
-
for (var i = 0; i < node.elements.length; i++) {
|
|
893
|
-
if (!this._isLiteralValue(node.elements[i])) {
|
|
894
|
-
return false;
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
return true;
|
|
899
|
-
case 'ObjectExpression':
|
|
900
|
-
if (node.properties && node.properties.length > 0) {
|
|
901
|
-
for (var i = 0; i < node.properties.length; i++) {
|
|
902
|
-
var property = node.properties[i];
|
|
903
|
-
if (property.value && !this._isLiteralValue(property.value)) {
|
|
904
|
-
return false;
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
}
|
|
908
|
-
return true;
|
|
909
|
-
default:
|
|
910
|
-
return false;
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
/**
|
|
915
|
-
* 判断字符串是否符合PascalCase命名规范
|
|
916
|
-
* @param {string} name 名称
|
|
917
|
-
* @returns {boolean} 是否符合PascalCase
|
|
918
|
-
*/
|
|
919
|
-
_isPascalCase(name) {
|
|
920
|
-
if (!name || name.length === 0) {
|
|
921
|
-
return false;
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
// PascalCase要求首字母大写,后续字母可以大小写混合
|
|
925
|
-
// 但不能包含下划线或连字符
|
|
926
|
-
if (name.includes('_') || name.includes('-')) {
|
|
927
|
-
return false;
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
// 首字母必须是大写字母
|
|
931
|
-
if (!/^[A-Z]/.test(name)) {
|
|
932
|
-
return false;
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
// 整体必须符合标识符规范
|
|
936
|
-
if (!/^[A-Za-z][A-Za-z0-9]*$/.test(name)) {
|
|
937
|
-
return false;
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
return true;
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
/**
|
|
944
|
-
* 检查属性名是否在作用域内有对应的类实例变量声明
|
|
945
|
-
* @param {string} name 属性名
|
|
946
|
-
* @param {object} node AST节点
|
|
947
|
-
* @returns {boolean} 是否是类实例引用
|
|
948
|
-
*/
|
|
949
|
-
_isClassInstanceReference(name, node) {
|
|
950
|
-
if (!name || !node) {
|
|
951
|
-
return false;
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
// 遍历AST查找变量声明
|
|
955
|
-
var current_node = node;
|
|
956
|
-
while (current_node.parent) {
|
|
957
|
-
current_node = current_node.parent;
|
|
958
|
-
|
|
959
|
-
// 查找变量声明
|
|
960
|
-
if (current_node.type === 'VariableDeclaration') {
|
|
961
|
-
for (var i = 0; i < current_node.declarations.length; i++) {
|
|
962
|
-
var decl = current_node.declarations[i];
|
|
963
|
-
if (decl.id && decl.id.type === 'Identifier' && decl.id.name === name) {
|
|
964
|
-
// 检查是否为类实例声明(new表达式)
|
|
965
|
-
if (decl.init && decl.init.type === 'NewExpression') {
|
|
966
|
-
return true;
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
// 查找赋值表达式
|
|
973
|
-
if (current_node.type === 'AssignmentExpression' &&
|
|
974
|
-
current_node.left && current_node.left.type === 'Identifier' &&
|
|
975
|
-
current_node.left.name === name) {
|
|
976
|
-
// 检查是否为类实例赋值(new表达式)
|
|
977
|
-
if (current_node.right && current_node.right.type === 'NewExpression') {
|
|
978
|
-
return true;
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
// 如果到达程序根节点,停止搜索
|
|
983
|
-
if (current_node.type === 'Program') {
|
|
984
|
-
break;
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
|
|
988
|
-
return false;
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
/**
|
|
992
|
-
* 通用类型溯源:对于导出语句中的属性,溯源其引用的类型
|
|
993
|
-
* @param {object} node 属性节点
|
|
994
|
-
* @returns {string} 溯源到的类型,无法溯源则返回null
|
|
995
|
-
*/
|
|
996
|
-
_traceReferenceType(node) {
|
|
997
|
-
if (!node || !node.key || node.key.type !== 'Identifier') {
|
|
998
|
-
return null;
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
// 对于简写属性 { AdminManager },溯源属性名对应的类型
|
|
1002
|
-
if (node.shorthand) {
|
|
1003
|
-
return this._traceIdentifierType(node.key.name, node);
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
// 对于完整属性 { Admin_DD: Admin },溯源属性值对应的类型
|
|
1007
|
-
if (!node.shorthand && node.value && node.value.type === 'Identifier') {
|
|
1008
|
-
return this._traceIdentifierType(node.value.name, node);
|
|
1009
|
-
}
|
|
1010
|
-
|
|
1011
|
-
// 其他情况无法溯源
|
|
1012
|
-
return null;
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
/**
|
|
1016
|
-
* 溯源标识符的类型
|
|
1017
|
-
* @param {string} name 标识符名
|
|
1018
|
-
* @param {object} node AST节点
|
|
1019
|
-
* @returns {string} 溯源到的类型,无法溯源则返回null
|
|
1020
|
-
*/
|
|
1021
|
-
_traceIdentifierType(name, node) {
|
|
1022
|
-
if (!name || !node) {
|
|
1023
|
-
return null;
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
// 获取Program节点(AST根节点)
|
|
1027
|
-
var program_node = this._getProgramNode(node);
|
|
1028
|
-
if (!program_node || !program_node.body) {
|
|
1029
|
-
return null;
|
|
1030
|
-
}
|
|
1031
|
-
|
|
1032
|
-
// 遍历同级节点查找声明
|
|
1033
|
-
for (var i = 0; i < program_node.body.length; i++) {
|
|
1034
|
-
var sibling_node = program_node.body[i];
|
|
1035
|
-
|
|
1036
|
-
// 查找类声明
|
|
1037
|
-
if (sibling_node.type === 'ClassDeclaration') {
|
|
1038
|
-
if (sibling_node.id && sibling_node.id.type === 'Identifier' && sibling_node.id.name === name) {
|
|
1039
|
-
return 'class';
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
|
-
// 查找函数声明
|
|
1044
|
-
if (sibling_node.type === 'FunctionDeclaration') {
|
|
1045
|
-
if (sibling_node.id && sibling_node.id.type === 'Identifier' && sibling_node.id.name === name) {
|
|
1046
|
-
return 'function';
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
// 查找变量声明
|
|
1051
|
-
if (sibling_node.type === 'VariableDeclaration') {
|
|
1052
|
-
for (var j = 0; j < sibling_node.declarations.length; j++) {
|
|
1053
|
-
var decl = sibling_node.declarations[j];
|
|
1054
|
-
if (decl.id && decl.id.type === 'Identifier' && decl.id.name === name) {
|
|
1055
|
-
// 检查变量声明的类型
|
|
1056
|
-
if (decl.init) {
|
|
1057
|
-
// 类表达式
|
|
1058
|
-
if (decl.init.type === 'ClassExpression') {
|
|
1059
|
-
return 'class';
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
// 函数表达式
|
|
1063
|
-
if (decl.init.type === 'FunctionExpression' || decl.init.type === 'ArrowFunctionExpression') {
|
|
1064
|
-
return 'function';
|
|
1065
|
-
}
|
|
1066
|
-
|
|
1067
|
-
// new 表达式(类实例)
|
|
1068
|
-
if (decl.init.type === 'NewExpression') {
|
|
1069
|
-
return 'class-instance';
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
// 常量(只有const声明且为基础字面量)
|
|
1073
|
-
if (sibling_node.kind === 'const' && this._isBasicLiteralValue(decl.init)) {
|
|
1074
|
-
return 'constant';
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
// 普通变量
|
|
1078
|
-
return 'variable';
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
// 没有初始值的变量声明,默认为变量
|
|
1082
|
-
return 'variable';
|
|
1083
|
-
}
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
|
-
// 无法溯源到具体类型
|
|
1089
|
-
return null;
|
|
1090
|
-
}
|
|
1091
|
-
|
|
1092
|
-
/**
|
|
1093
|
-
* 检查属性名是否在作用域内有对应的类声明
|
|
1094
|
-
* @param {string} name 属性名
|
|
1095
|
-
* @param {object} node AST节点
|
|
1096
|
-
* @returns {boolean} 是否是类引用
|
|
1097
|
-
*/
|
|
1098
|
-
_isClassReference(name, node) {
|
|
1099
|
-
if (!name || !node) {
|
|
1100
|
-
return false;
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
|
-
console.log(' [DEBUG] _isClassReference called:', name);
|
|
1104
|
-
|
|
1105
|
-
// 在ESLint环境中,AST节点有parent属性,可以向上遍历
|
|
1106
|
-
// 获取Program节点(AST根节点)
|
|
1107
|
-
var program_node = this._getProgramNode(node);
|
|
1108
|
-
console.log(' [DEBUG] Program node:', program_node ? program_node.type : 'null');
|
|
1109
|
-
|
|
1110
|
-
if (!program_node || !program_node.body) {
|
|
1111
|
-
console.log(' [DEBUG] No program node or body');
|
|
1112
|
-
return false;
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
console.log(' [DEBUG] Program body length:', program_node.body.length);
|
|
1116
|
-
|
|
1117
|
-
// 遍历同级节点查找类声明
|
|
1118
|
-
for (var i = 0; i < program_node.body.length; i++) {
|
|
1119
|
-
var sibling_node = program_node.body[i];
|
|
1120
|
-
console.log(' [DEBUG] Sibling node', i, ':', sibling_node.type);
|
|
1121
|
-
|
|
1122
|
-
// 查找类声明
|
|
1123
|
-
if (sibling_node.type === 'ClassDeclaration') {
|
|
1124
|
-
console.log(' [DEBUG] Found ClassDeclaration:', sibling_node.id ? sibling_node.id.name : 'null');
|
|
1125
|
-
if (sibling_node.id && sibling_node.id.type === 'Identifier' && sibling_node.id.name === name) {
|
|
1126
|
-
console.log(' [DEBUG] Class reference found!');
|
|
1127
|
-
return true;
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
|
|
1131
|
-
// 查找变量声明中的类表达式
|
|
1132
|
-
if (sibling_node.type === 'VariableDeclaration') {
|
|
1133
|
-
for (var j = 0; j < sibling_node.declarations.length; j++) {
|
|
1134
|
-
var decl = sibling_node.declarations[j];
|
|
1135
|
-
if (decl.id && decl.id.type === 'Identifier' && decl.id.name === name) {
|
|
1136
|
-
// 检查是否为类表达式
|
|
1137
|
-
if (decl.init && decl.init.type === 'ClassExpression') {
|
|
1138
|
-
return true;
|
|
1139
|
-
}
|
|
1140
|
-
}
|
|
1141
|
-
}
|
|
1142
|
-
}
|
|
1143
|
-
}
|
|
1144
|
-
|
|
1145
|
-
console.log(' [DEBUG] No class reference found');
|
|
1146
|
-
return false;
|
|
1147
|
-
}
|
|
1148
|
-
|
|
1149
|
-
/**
|
|
1150
|
-
* 获取Program节点(AST根节点)
|
|
1151
|
-
* @param {object} node AST节点
|
|
1152
|
-
* @returns {object} Program节点
|
|
1153
|
-
*/
|
|
1154
|
-
_getProgramNode(node) {
|
|
1155
|
-
var current_node = node;
|
|
1156
|
-
|
|
1157
|
-
// 在ESLint环境中,AST节点有parent属性,可以向上遍历
|
|
1158
|
-
// 向上遍历直到找到Program节点
|
|
1159
|
-
while (current_node && current_node.parent) {
|
|
1160
|
-
current_node = current_node.parent;
|
|
1161
|
-
|
|
1162
|
-
// 如果找到Program节点,返回它
|
|
1163
|
-
if (current_node.type === 'Program') {
|
|
1164
|
-
return current_node;
|
|
1165
|
-
}
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
|
-
// 如果没有找到Program节点,返回null
|
|
1169
|
-
return null;
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
|
-
/**
|
|
1173
|
-
* 获取父级声明类型
|
|
1174
|
-
* @param {object} node AST节点
|
|
1175
|
-
* @returns {string} 声明类型
|
|
1176
|
-
*/
|
|
1177
|
-
_getParentDeclarationType(node) {
|
|
1178
|
-
var current_node = node;
|
|
1179
|
-
|
|
1180
|
-
while (current_node.parent) {
|
|
1181
|
-
current_node = current_node.parent;
|
|
1182
|
-
|
|
1183
|
-
if (current_node.type === 'VariableDeclarator' && current_node.parent && current_node.parent.kind) {
|
|
1184
|
-
return current_node.parent.kind;
|
|
1185
|
-
}
|
|
1186
|
-
|
|
1187
|
-
if (current_node.type === 'AssignmentExpression') {
|
|
1188
|
-
return 'variable';
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
|
|
1192
|
-
return 'unknown';
|
|
1193
|
-
}
|
|
1194
|
-
|
|
1195
|
-
/**
|
|
1196
|
-
* 获取父级对象节点
|
|
1197
|
-
* @param {object} node AST节点
|
|
1198
|
-
* @returns {object|null} 父级对象节点
|
|
1199
|
-
*/
|
|
1200
|
-
_getParentObject(node) {
|
|
1201
|
-
var current_node = node;
|
|
1202
|
-
|
|
1203
|
-
while (current_node.parent) {
|
|
1204
|
-
current_node = current_node.parent;
|
|
1205
|
-
|
|
1206
|
-
if (current_node.type === 'ObjectExpression') {
|
|
1207
|
-
return current_node;
|
|
1208
|
-
}
|
|
1209
|
-
|
|
1210
|
-
if (current_node.type === 'VariableDeclarator' || current_node.type === 'AssignmentExpression') {
|
|
1211
|
-
break;
|
|
1212
|
-
}
|
|
1213
|
-
}
|
|
1214
|
-
|
|
1215
|
-
return null;
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
/**
|
|
1219
|
-
* 判断是否为基础字面量值(排除正则表达式)
|
|
1220
|
-
* @param {object} node AST节点
|
|
1221
|
-
* @returns {boolean} 是否为基础字面量
|
|
1222
|
-
*/
|
|
1223
|
-
_isBasicLiteralValue(node) {
|
|
1224
|
-
if (!node) {
|
|
1225
|
-
return false;
|
|
1226
|
-
}
|
|
1227
|
-
|
|
1228
|
-
switch (node.type) {
|
|
1229
|
-
case 'Literal':
|
|
1230
|
-
// 排除正则表达式
|
|
1231
|
-
return !node.regex;
|
|
1232
|
-
case 'TemplateLiteral':
|
|
1233
|
-
return true;
|
|
1234
|
-
case 'ArrayExpression':
|
|
1235
|
-
// 数组必须所有元素都是基础字面量
|
|
1236
|
-
if (node.elements && node.elements.length > 0) {
|
|
1237
|
-
for (var i = 0; i < node.elements.length; i++) {
|
|
1238
|
-
if (!this._isBasicLiteralValue(node.elements[i])) {
|
|
1239
|
-
return false;
|
|
1240
|
-
}
|
|
1241
|
-
}
|
|
1242
|
-
}
|
|
1243
|
-
return true;
|
|
1244
|
-
case 'ObjectExpression':
|
|
1245
|
-
// 对象必须所有属性值都是基础字面量
|
|
1246
|
-
if (node.properties && node.properties.length > 0) {
|
|
1247
|
-
for (var i = 0; i < node.properties.length; i++) {
|
|
1248
|
-
var property = node.properties[i];
|
|
1249
|
-
if (property.value && !this._isBasicLiteralValue(property.value)) {
|
|
1250
|
-
return false;
|
|
1251
|
-
}
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
1254
|
-
return true;
|
|
1255
|
-
case 'Identifier':
|
|
1256
|
-
var basic_literal_identifiers = ['undefined', 'null', 'Infinity', 'NaN', 'true', 'false'];
|
|
1257
|
-
return basic_literal_identifiers.includes(node.name);
|
|
1258
|
-
default:
|
|
1259
|
-
return false;
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
|
|
1263
|
-
/**
|
|
1264
|
-
* 检查对象的所有属性是否都是字面量
|
|
1265
|
-
* @param {object} object_node 对象节点
|
|
1266
|
-
* @returns {boolean} 是否所有属性都是字面量
|
|
1267
|
-
*/
|
|
1268
|
-
_isObjectAllLiteral(object_node) {
|
|
1269
|
-
if (object_node.type !== 'ObjectExpression' || !object_node.properties) {
|
|
1270
|
-
return false;
|
|
1271
|
-
}
|
|
1272
|
-
|
|
1273
|
-
for (var i = 0; i < object_node.properties.length; i++) {
|
|
1274
|
-
var property = object_node.properties[i];
|
|
1275
|
-
|
|
1276
|
-
// 跳过解构赋值
|
|
1277
|
-
if (property.type !== 'Property') {
|
|
1278
|
-
continue;
|
|
1279
|
-
}
|
|
1280
|
-
|
|
1281
|
-
// 检查属性值是否为字面量
|
|
1282
|
-
if (!this._isLiteralValue(property.value)) {
|
|
1283
|
-
return false;
|
|
1284
|
-
}
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
return true;
|
|
1288
|
-
}
|
|
1289
|
-
}
|
|
1290
|
-
|
|
1291
|
-
module.exports = { Detector };
|