1 | "use strict";
|
2 | var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
3 | if (kind === "m") throw new TypeError("Private method is not writable");
|
4 | if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
5 | if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
6 | return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
7 | };
|
8 | var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
9 | if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
10 | if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
11 | return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
12 | };
|
13 | var _UnusedVarsVisitor_scopeManager;
|
14 | Object.defineProperty(exports, "__esModule", { value: true });
|
15 | exports.collectUnusedVariables = void 0;
|
16 | const utils_1 = require("@typescript-eslint/utils");
|
17 | const scope_manager_1 = require("@typescript-eslint/scope-manager");
|
18 | const Visitor_1 = require("@typescript-eslint/scope-manager/dist/referencer/Visitor");
|
19 | class UnusedVarsVisitor extends Visitor_1.Visitor {
|
20 |
|
21 | constructor(context) {
|
22 | super({
|
23 | visitChildrenEvenIfSelectorExists: true,
|
24 | });
|
25 | _UnusedVarsVisitor_scopeManager.set(this, void 0);
|
26 |
|
27 |
|
28 |
|
29 | this.ClassDeclaration = this.visitClass;
|
30 | this.ClassExpression = this.visitClass;
|
31 | this.FunctionDeclaration = this.visitFunction;
|
32 | this.FunctionExpression = this.visitFunction;
|
33 | this.MethodDefinition = this.visitSetter;
|
34 | this.Property = this.visitSetter;
|
35 | this.TSCallSignatureDeclaration = this.visitFunctionTypeSignature;
|
36 | this.TSConstructorType = this.visitFunctionTypeSignature;
|
37 | this.TSConstructSignatureDeclaration = this.visitFunctionTypeSignature;
|
38 | this.TSDeclareFunction = this.visitFunctionTypeSignature;
|
39 | this.TSEmptyBodyFunctionExpression = this.visitFunctionTypeSignature;
|
40 | this.TSFunctionType = this.visitFunctionTypeSignature;
|
41 | this.TSMethodSignature = this.visitFunctionTypeSignature;
|
42 | __classPrivateFieldSet(this, _UnusedVarsVisitor_scopeManager, utils_1.ESLintUtils.nullThrows(context.getSourceCode().scopeManager, 'Missing required scope manager'), "f");
|
43 | }
|
44 | static collectUnusedVariables(context) {
|
45 | const program = context.getSourceCode().ast;
|
46 | const cached = this.RESULTS_CACHE.get(program);
|
47 | if (cached) {
|
48 | return cached;
|
49 | }
|
50 | const visitor = new this(context);
|
51 | visitor.visit(program);
|
52 | const unusedVars = visitor.collectUnusedVariables(visitor.getScope(program));
|
53 | this.RESULTS_CACHE.set(program, unusedVars);
|
54 | return unusedVars;
|
55 | }
|
56 | collectUnusedVariables(scope, unusedVariables = new Set()) {
|
57 | for (const variable of scope.variables) {
|
58 | if (
|
59 |
|
60 | scope.functionExpressionScope ||
|
61 |
|
62 | variable.eslintUsed ||
|
63 |
|
64 | variable instanceof scope_manager_1.ImplicitLibVariable ||
|
65 |
|
66 | isExported(variable) ||
|
67 |
|
68 | isMergableExported(variable) ||
|
69 |
|
70 | isUsedVariable(variable)) {
|
71 | continue;
|
72 | }
|
73 | unusedVariables.add(variable);
|
74 | }
|
75 | for (const childScope of scope.childScopes) {
|
76 | this.collectUnusedVariables(childScope, unusedVariables);
|
77 | }
|
78 | return unusedVariables;
|
79 | }
|
80 |
|
81 | getScope(currentNode) {
|
82 |
|
83 | const inner = currentNode.type !== utils_1.AST_NODE_TYPES.Program;
|
84 | let node = currentNode;
|
85 | while (node) {
|
86 | const scope = __classPrivateFieldGet(this, _UnusedVarsVisitor_scopeManager, "f").acquire(node, inner);
|
87 | if (scope) {
|
88 | if (scope.type === 'function-expression-name') {
|
89 | return scope.childScopes[0];
|
90 | }
|
91 | return scope;
|
92 | }
|
93 | node = node.parent;
|
94 | }
|
95 | return __classPrivateFieldGet(this, _UnusedVarsVisitor_scopeManager, "f").scopes[0];
|
96 | }
|
97 | markVariableAsUsed(variableOrIdentifierOrName, parent) {
|
98 | if (typeof variableOrIdentifierOrName !== 'string' &&
|
99 | !('type' in variableOrIdentifierOrName)) {
|
100 | variableOrIdentifierOrName.eslintUsed = true;
|
101 | return;
|
102 | }
|
103 | let name;
|
104 | let node;
|
105 | if (typeof variableOrIdentifierOrName === 'string') {
|
106 | name = variableOrIdentifierOrName;
|
107 | node = parent;
|
108 | }
|
109 | else {
|
110 | name = variableOrIdentifierOrName.name;
|
111 | node = variableOrIdentifierOrName;
|
112 | }
|
113 | let currentScope = this.getScope(node);
|
114 | while (currentScope) {
|
115 | const variable = currentScope.variables.find(scopeVar => scopeVar.name === name);
|
116 | if (variable) {
|
117 | variable.eslintUsed = true;
|
118 | return;
|
119 | }
|
120 | currentScope = currentScope.upper;
|
121 | }
|
122 | }
|
123 | visitClass(node) {
|
124 |
|
125 | const scope = this.getScope(node);
|
126 | for (const variable of scope.variables) {
|
127 | if (variable.identifiers[0] === scope.block.id) {
|
128 | this.markVariableAsUsed(variable);
|
129 | return;
|
130 | }
|
131 | }
|
132 | }
|
133 | visitFunction(node) {
|
134 | const scope = this.getScope(node);
|
135 |
|
136 | const variable = scope.set.get('arguments');
|
137 | if ((variable === null || variable === void 0 ? void 0 : variable.defs.length) === 0) {
|
138 | this.markVariableAsUsed(variable);
|
139 | }
|
140 | }
|
141 | visitFunctionTypeSignature(node) {
|
142 |
|
143 |
|
144 | for (const param of node.params) {
|
145 | this.visitPattern(param, name => {
|
146 | this.markVariableAsUsed(name);
|
147 | });
|
148 | }
|
149 | }
|
150 | visitSetter(node) {
|
151 | if (node.kind === 'set') {
|
152 |
|
153 | for (const param of node.value.params) {
|
154 | this.visitPattern(param, id => {
|
155 | this.markVariableAsUsed(id);
|
156 | });
|
157 | }
|
158 | }
|
159 | }
|
160 | ForInStatement(node) {
|
161 | |
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 |
|
168 |
|
169 |
|
170 |
|
171 |
|
172 |
|
173 |
|
174 |
|
175 |
|
176 |
|
177 | let idOrVariable;
|
178 | if (node.left.type === utils_1.AST_NODE_TYPES.VariableDeclaration) {
|
179 | const variable = __classPrivateFieldGet(this, _UnusedVarsVisitor_scopeManager, "f").getDeclaredVariables(node.left)[0];
|
180 | if (!variable) {
|
181 | return;
|
182 | }
|
183 | idOrVariable = variable;
|
184 | }
|
185 | if (node.left.type === utils_1.AST_NODE_TYPES.Identifier) {
|
186 | idOrVariable = node.left;
|
187 | }
|
188 | if (idOrVariable == null) {
|
189 | return;
|
190 | }
|
191 | let body = node.body;
|
192 | if (node.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
193 | if (node.body.body.length !== 1) {
|
194 | return;
|
195 | }
|
196 | body = node.body.body[0];
|
197 | }
|
198 | if (body.type !== utils_1.AST_NODE_TYPES.ReturnStatement) {
|
199 | return;
|
200 | }
|
201 | this.markVariableAsUsed(idOrVariable);
|
202 | }
|
203 | Identifier(node) {
|
204 | const scope = this.getScope(node);
|
205 | if (scope.type === utils_1.TSESLint.Scope.ScopeType.function &&
|
206 | node.name === 'this') {
|
207 |
|
208 | if ('params' in scope.block && scope.block.params.includes(node)) {
|
209 | this.markVariableAsUsed(node);
|
210 | }
|
211 | }
|
212 | }
|
213 | TSEnumDeclaration(node) {
|
214 |
|
215 |
|
216 | const scope = this.getScope(node);
|
217 | for (const variable of scope.variables) {
|
218 | this.markVariableAsUsed(variable);
|
219 | }
|
220 | }
|
221 | TSMappedType(node) {
|
222 |
|
223 |
|
224 | this.markVariableAsUsed(node.typeParameter.name);
|
225 | }
|
226 | TSModuleDeclaration(node) {
|
227 |
|
228 | if (node.global === true) {
|
229 | this.markVariableAsUsed('global', node.parent);
|
230 | }
|
231 | }
|
232 | TSParameterProperty(node) {
|
233 | let identifier = null;
|
234 | switch (node.parameter.type) {
|
235 | case utils_1.AST_NODE_TYPES.AssignmentPattern:
|
236 | if (node.parameter.left.type === utils_1.AST_NODE_TYPES.Identifier) {
|
237 | identifier = node.parameter.left;
|
238 | }
|
239 | break;
|
240 | case utils_1.AST_NODE_TYPES.Identifier:
|
241 | identifier = node.parameter;
|
242 | break;
|
243 | }
|
244 | if (identifier) {
|
245 | this.markVariableAsUsed(identifier);
|
246 | }
|
247 | }
|
248 | }
|
249 | _UnusedVarsVisitor_scopeManager = new WeakMap();
|
250 | UnusedVarsVisitor.RESULTS_CACHE = new WeakMap();
|
251 |
|
252 |
|
253 |
|
254 |
|
255 |
|
256 |
|
257 |
|
258 | function isInside(inner, outer) {
|
259 | return inner.range[0] >= outer.range[0] && inner.range[1] <= outer.range[1];
|
260 | }
|
261 |
|
262 |
|
263 |
|
264 |
|
265 |
|
266 |
|
267 |
|
268 | function isSelfReference(ref, nodes) {
|
269 | let scope = ref.from;
|
270 | while (scope) {
|
271 | if (nodes.has(scope.block)) {
|
272 | return true;
|
273 | }
|
274 | scope = scope.upper;
|
275 | }
|
276 | return false;
|
277 | }
|
278 | const MERGABLE_TYPES = new Set([
|
279 | utils_1.AST_NODE_TYPES.TSInterfaceDeclaration,
|
280 | utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration,
|
281 | utils_1.AST_NODE_TYPES.TSModuleDeclaration,
|
282 | utils_1.AST_NODE_TYPES.ClassDeclaration,
|
283 | utils_1.AST_NODE_TYPES.FunctionDeclaration,
|
284 | ]);
|
285 |
|
286 |
|
287 |
|
288 |
|
289 |
|
290 | function isMergableExported(variable) {
|
291 | var _a, _b;
|
292 |
|
293 | for (const def of variable.defs) {
|
294 |
|
295 |
|
296 |
|
297 | if (def.type === utils_1.TSESLint.Scope.DefinitionType.Parameter) {
|
298 | continue;
|
299 | }
|
300 | if ((MERGABLE_TYPES.has(def.node.type) &&
|
301 | ((_a = def.node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.ExportNamedDeclaration) ||
|
302 | ((_b = def.node.parent) === null || _b === void 0 ? void 0 : _b.type) === utils_1.AST_NODE_TYPES.ExportDefaultDeclaration) {
|
303 | return true;
|
304 | }
|
305 | }
|
306 | return false;
|
307 | }
|
308 |
|
309 |
|
310 |
|
311 |
|
312 |
|
313 | function isExported(variable) {
|
314 | const definition = variable.defs[0];
|
315 | if (definition) {
|
316 | let node = definition.node;
|
317 | if (node.type === utils_1.AST_NODE_TYPES.VariableDeclarator) {
|
318 | node = node.parent;
|
319 | }
|
320 | else if (definition.type === utils_1.TSESLint.Scope.DefinitionType.Parameter) {
|
321 | return false;
|
322 | }
|
323 | return node.parent.type.indexOf('Export') === 0;
|
324 | }
|
325 | return false;
|
326 | }
|
327 |
|
328 |
|
329 |
|
330 |
|
331 |
|
332 | function isUsedVariable(variable) {
|
333 | |
334 |
|
335 |
|
336 |
|
337 |
|
338 | function getFunctionDefinitions(variable) {
|
339 | const functionDefinitions = new Set();
|
340 | variable.defs.forEach(def => {
|
341 | var _a, _b;
|
342 |
|
343 | if (def.type === utils_1.TSESLint.Scope.DefinitionType.FunctionName) {
|
344 | functionDefinitions.add(def.node);
|
345 | }
|
346 |
|
347 | if (def.type === utils_1.TSESLint.Scope.DefinitionType.Variable &&
|
348 | (((_a = def.node.init) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.FunctionExpression ||
|
349 | ((_b = def.node.init) === null || _b === void 0 ? void 0 : _b.type) === utils_1.AST_NODE_TYPES.ArrowFunctionExpression)) {
|
350 | functionDefinitions.add(def.node.init);
|
351 | }
|
352 | });
|
353 | return functionDefinitions;
|
354 | }
|
355 | function getTypeDeclarations(variable) {
|
356 | const nodes = new Set();
|
357 | variable.defs.forEach(def => {
|
358 | if (def.node.type === utils_1.AST_NODE_TYPES.TSInterfaceDeclaration ||
|
359 | def.node.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration) {
|
360 | nodes.add(def.node);
|
361 | }
|
362 | });
|
363 | return nodes;
|
364 | }
|
365 | function getModuleDeclarations(variable) {
|
366 | const nodes = new Set();
|
367 | variable.defs.forEach(def => {
|
368 | if (def.node.type === utils_1.AST_NODE_TYPES.TSModuleDeclaration) {
|
369 | nodes.add(def.node);
|
370 | }
|
371 | });
|
372 | return nodes;
|
373 | }
|
374 | |
375 |
|
376 |
|
377 | function isInsideOneOf(ref, nodes) {
|
378 | for (const node of nodes) {
|
379 | if (isInside(ref.identifier, node)) {
|
380 | return true;
|
381 | }
|
382 | }
|
383 | return false;
|
384 | }
|
385 | |
386 |
|
387 |
|
388 |
|
389 |
|
390 |
|
391 |
|
392 |
|
393 |
|
394 |
|
395 |
|
396 |
|
397 |
|
398 |
|
399 | function getRhsNode(ref, prevRhsNode) {
|
400 | |
401 |
|
402 |
|
403 |
|
404 |
|
405 | function isInLoop(node) {
|
406 | let currentNode = node;
|
407 | while (currentNode) {
|
408 | if (utils_1.ASTUtils.isFunction(currentNode)) {
|
409 | break;
|
410 | }
|
411 | if (utils_1.ASTUtils.isLoop(currentNode)) {
|
412 | return true;
|
413 | }
|
414 | currentNode = currentNode.parent;
|
415 | }
|
416 | return false;
|
417 | }
|
418 | const id = ref.identifier;
|
419 | const parent = id.parent;
|
420 | const grandparent = parent.parent;
|
421 | const refScope = ref.from.variableScope;
|
422 | const varScope = ref.resolved.scope.variableScope;
|
423 | const canBeUsedLater = refScope !== varScope || isInLoop(id);
|
424 | |
425 |
|
426 |
|
427 |
|
428 | if (prevRhsNode && isInside(id, prevRhsNode)) {
|
429 | return prevRhsNode;
|
430 | }
|
431 | if (parent.type === utils_1.AST_NODE_TYPES.AssignmentExpression &&
|
432 | grandparent.type === utils_1.AST_NODE_TYPES.ExpressionStatement &&
|
433 | id === parent.left &&
|
434 | !canBeUsedLater) {
|
435 | return parent.right;
|
436 | }
|
437 | return null;
|
438 | }
|
439 | |
440 |
|
441 |
|
442 |
|
443 |
|
444 |
|
445 | function isReadForItself(ref, rhsNode) {
|
446 | |
447 |
|
448 |
|
449 |
|
450 |
|
451 |
|
452 |
|
453 |
|
454 |
|
455 |
|
456 |
|
457 |
|
458 |
|
459 | function isInsideOfStorableFunction(id, rhsNode) {
|
460 | |
461 |
|
462 |
|
463 |
|
464 |
|
465 | function getUpperFunction(node) {
|
466 | let currentNode = node;
|
467 | while (currentNode) {
|
468 | if (utils_1.ASTUtils.isFunction(currentNode)) {
|
469 | return currentNode;
|
470 | }
|
471 | currentNode = currentNode.parent;
|
472 | }
|
473 | return null;
|
474 | }
|
475 | |
476 |
|
477 |
|
478 |
|
479 |
|
480 |
|
481 |
|
482 |
|
483 |
|
484 |
|
485 | function isStorableFunction(funcNode, rhsNode) {
|
486 | let node = funcNode;
|
487 | let parent = funcNode.parent;
|
488 | while (parent && isInside(parent, rhsNode)) {
|
489 | switch (parent.type) {
|
490 | case utils_1.AST_NODE_TYPES.SequenceExpression:
|
491 | if (parent.expressions[parent.expressions.length - 1] !== node) {
|
492 | return false;
|
493 | }
|
494 | break;
|
495 | case utils_1.AST_NODE_TYPES.CallExpression:
|
496 | case utils_1.AST_NODE_TYPES.NewExpression:
|
497 | return parent.callee !== node;
|
498 | case utils_1.AST_NODE_TYPES.AssignmentExpression:
|
499 | case utils_1.AST_NODE_TYPES.TaggedTemplateExpression:
|
500 | case utils_1.AST_NODE_TYPES.YieldExpression:
|
501 | return true;
|
502 | default:
|
503 | if (parent.type.endsWith('Statement') ||
|
504 | parent.type.endsWith('Declaration')) {
|
505 | |
506 |
|
507 |
|
508 |
|
509 | return true;
|
510 | }
|
511 | }
|
512 | node = parent;
|
513 | parent = parent.parent;
|
514 | }
|
515 | return false;
|
516 | }
|
517 | const funcNode = getUpperFunction(id);
|
518 | return (!!funcNode &&
|
519 | isInside(funcNode, rhsNode) &&
|
520 | isStorableFunction(funcNode, rhsNode));
|
521 | }
|
522 | const id = ref.identifier;
|
523 | const parent = id.parent;
|
524 | const grandparent = parent.parent;
|
525 | return (ref.isRead() &&
|
526 |
|
527 | ((parent.type === utils_1.AST_NODE_TYPES.AssignmentExpression &&
|
528 | grandparent.type === utils_1.AST_NODE_TYPES.ExpressionStatement &&
|
529 | parent.left === id) ||
|
530 | (parent.type === utils_1.AST_NODE_TYPES.UpdateExpression &&
|
531 | grandparent.type === utils_1.AST_NODE_TYPES.ExpressionStatement) ||
|
532 | (!!rhsNode &&
|
533 | isInside(id, rhsNode) &&
|
534 | !isInsideOfStorableFunction(id, rhsNode))));
|
535 | }
|
536 | const functionNodes = getFunctionDefinitions(variable);
|
537 | const isFunctionDefinition = functionNodes.size > 0;
|
538 | const typeDeclNodes = getTypeDeclarations(variable);
|
539 | const isTypeDecl = typeDeclNodes.size > 0;
|
540 | const moduleDeclNodes = getModuleDeclarations(variable);
|
541 | const isModuleDecl = moduleDeclNodes.size > 0;
|
542 | let rhsNode = null;
|
543 | return variable.references.some(ref => {
|
544 | const forItself = isReadForItself(ref, rhsNode);
|
545 | rhsNode = getRhsNode(ref, rhsNode);
|
546 | return (ref.isRead() &&
|
547 | !forItself &&
|
548 | !(isFunctionDefinition && isSelfReference(ref, functionNodes)) &&
|
549 | !(isTypeDecl && isInsideOneOf(ref, typeDeclNodes)) &&
|
550 | !(isModuleDecl && isSelfReference(ref, moduleDeclNodes)));
|
551 | });
|
552 | }
|
553 |
|
554 |
|
555 |
|
556 |
|
557 |
|
558 |
|
559 |
|
560 |
|
561 | function collectUnusedVariables(context) {
|
562 | return UnusedVarsVisitor.collectUnusedVariables(context);
|
563 | }
|
564 | exports.collectUnusedVariables = collectUnusedVariables;
|
565 |
|
\ | No newline at end of file |