Actualizacion maquina principal
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / no-unused-vars.js
1 /**
2  * @fileoverview Rule to flag declared but unused variables
3  * @author Ilya Volodin
4  */
5
6 "use strict";
7
8 //------------------------------------------------------------------------------
9 // Requirements
10 //------------------------------------------------------------------------------
11
12 const astUtils = require("./utils/ast-utils");
13
14 //------------------------------------------------------------------------------
15 // Rule Definition
16 //------------------------------------------------------------------------------
17
18 module.exports = {
19     meta: {
20         type: "problem",
21
22         docs: {
23             description: "disallow unused variables",
24             category: "Variables",
25             recommended: true,
26             url: "https://eslint.org/docs/rules/no-unused-vars"
27         },
28
29         schema: [
30             {
31                 oneOf: [
32                     {
33                         enum: ["all", "local"]
34                     },
35                     {
36                         type: "object",
37                         properties: {
38                             vars: {
39                                 enum: ["all", "local"]
40                             },
41                             varsIgnorePattern: {
42                                 type: "string"
43                             },
44                             args: {
45                                 enum: ["all", "after-used", "none"]
46                             },
47                             ignoreRestSiblings: {
48                                 type: "boolean"
49                             },
50                             argsIgnorePattern: {
51                                 type: "string"
52                             },
53                             caughtErrors: {
54                                 enum: ["all", "none"]
55                             },
56                             caughtErrorsIgnorePattern: {
57                                 type: "string"
58                             }
59                         }
60                     }
61                 ]
62             }
63         ]
64     },
65
66     create(context) {
67         const sourceCode = context.getSourceCode();
68
69         const REST_PROPERTY_TYPE = /^(?:RestElement|(?:Experimental)?RestProperty)$/u;
70
71         const config = {
72             vars: "all",
73             args: "after-used",
74             ignoreRestSiblings: false,
75             caughtErrors: "none"
76         };
77
78         const firstOption = context.options[0];
79
80         if (firstOption) {
81             if (typeof firstOption === "string") {
82                 config.vars = firstOption;
83             } else {
84                 config.vars = firstOption.vars || config.vars;
85                 config.args = firstOption.args || config.args;
86                 config.ignoreRestSiblings = firstOption.ignoreRestSiblings || config.ignoreRestSiblings;
87                 config.caughtErrors = firstOption.caughtErrors || config.caughtErrors;
88
89                 if (firstOption.varsIgnorePattern) {
90                     config.varsIgnorePattern = new RegExp(firstOption.varsIgnorePattern, "u");
91                 }
92
93                 if (firstOption.argsIgnorePattern) {
94                     config.argsIgnorePattern = new RegExp(firstOption.argsIgnorePattern, "u");
95                 }
96
97                 if (firstOption.caughtErrorsIgnorePattern) {
98                     config.caughtErrorsIgnorePattern = new RegExp(firstOption.caughtErrorsIgnorePattern, "u");
99                 }
100             }
101         }
102
103         /**
104          * Generate the warning message about the variable being
105          * defined and unused, including the ignore pattern if configured.
106          * @param {Variable} unusedVar eslint-scope variable object.
107          * @returns {string} The warning message to be used with this unused variable.
108          */
109         function getDefinedMessage(unusedVar) {
110             const defType = unusedVar.defs && unusedVar.defs[0] && unusedVar.defs[0].type;
111             let type;
112             let pattern;
113
114             if (defType === "CatchClause" && config.caughtErrorsIgnorePattern) {
115                 type = "args";
116                 pattern = config.caughtErrorsIgnorePattern.toString();
117             } else if (defType === "Parameter" && config.argsIgnorePattern) {
118                 type = "args";
119                 pattern = config.argsIgnorePattern.toString();
120             } else if (defType !== "Parameter" && config.varsIgnorePattern) {
121                 type = "vars";
122                 pattern = config.varsIgnorePattern.toString();
123             }
124
125             const additional = type ? ` Allowed unused ${type} must match ${pattern}.` : "";
126
127             return `'{{name}}' is defined but never used.${additional}`;
128         }
129
130         /**
131          * Generate the warning message about the variable being
132          * assigned and unused, including the ignore pattern if configured.
133          * @returns {string} The warning message to be used with this unused variable.
134          */
135         function getAssignedMessage() {
136             const additional = config.varsIgnorePattern ? ` Allowed unused vars must match ${config.varsIgnorePattern.toString()}.` : "";
137
138             return `'{{name}}' is assigned a value but never used.${additional}`;
139         }
140
141         //--------------------------------------------------------------------------
142         // Helpers
143         //--------------------------------------------------------------------------
144
145         const STATEMENT_TYPE = /(?:Statement|Declaration)$/u;
146
147         /**
148          * Determines if a given variable is being exported from a module.
149          * @param {Variable} variable eslint-scope variable object.
150          * @returns {boolean} True if the variable is exported, false if not.
151          * @private
152          */
153         function isExported(variable) {
154
155             const definition = variable.defs[0];
156
157             if (definition) {
158
159                 let node = definition.node;
160
161                 if (node.type === "VariableDeclarator") {
162                     node = node.parent;
163                 } else if (definition.type === "Parameter") {
164                     return false;
165                 }
166
167                 return node.parent.type.indexOf("Export") === 0;
168             }
169             return false;
170
171         }
172
173         /**
174          * Determines if a variable has a sibling rest property
175          * @param {Variable} variable eslint-scope variable object.
176          * @returns {boolean} True if the variable is exported, false if not.
177          * @private
178          */
179         function hasRestSpreadSibling(variable) {
180             if (config.ignoreRestSiblings) {
181                 return variable.defs.some(def => {
182                     const propertyNode = def.name.parent;
183                     const patternNode = propertyNode.parent;
184
185                     return (
186                         propertyNode.type === "Property" &&
187                         patternNode.type === "ObjectPattern" &&
188                         REST_PROPERTY_TYPE.test(patternNode.properties[patternNode.properties.length - 1].type)
189                     );
190                 });
191             }
192
193             return false;
194         }
195
196         /**
197          * Determines if a reference is a read operation.
198          * @param {Reference} ref An eslint-scope Reference
199          * @returns {boolean} whether the given reference represents a read operation
200          * @private
201          */
202         function isReadRef(ref) {
203             return ref.isRead();
204         }
205
206         /**
207          * Determine if an identifier is referencing an enclosing function name.
208          * @param {Reference} ref The reference to check.
209          * @param {ASTNode[]} nodes The candidate function nodes.
210          * @returns {boolean} True if it's a self-reference, false if not.
211          * @private
212          */
213         function isSelfReference(ref, nodes) {
214             let scope = ref.from;
215
216             while (scope) {
217                 if (nodes.indexOf(scope.block) >= 0) {
218                     return true;
219                 }
220
221                 scope = scope.upper;
222             }
223
224             return false;
225         }
226
227         /**
228          * Gets a list of function definitions for a specified variable.
229          * @param {Variable} variable eslint-scope variable object.
230          * @returns {ASTNode[]} Function nodes.
231          * @private
232          */
233         function getFunctionDefinitions(variable) {
234             const functionDefinitions = [];
235
236             variable.defs.forEach(def => {
237                 const { type, node } = def;
238
239                 // FunctionDeclarations
240                 if (type === "FunctionName") {
241                     functionDefinitions.push(node);
242                 }
243
244                 // FunctionExpressions
245                 if (type === "Variable" && node.init &&
246                     (node.init.type === "FunctionExpression" || node.init.type === "ArrowFunctionExpression")) {
247                     functionDefinitions.push(node.init);
248                 }
249             });
250             return functionDefinitions;
251         }
252
253         /**
254          * Checks the position of given nodes.
255          * @param {ASTNode} inner A node which is expected as inside.
256          * @param {ASTNode} outer A node which is expected as outside.
257          * @returns {boolean} `true` if the `inner` node exists in the `outer` node.
258          * @private
259          */
260         function isInside(inner, outer) {
261             return (
262                 inner.range[0] >= outer.range[0] &&
263                 inner.range[1] <= outer.range[1]
264             );
265         }
266
267         /**
268          * If a given reference is left-hand side of an assignment, this gets
269          * the right-hand side node of the assignment.
270          *
271          * In the following cases, this returns null.
272          *
273          * - The reference is not the LHS of an assignment expression.
274          * - The reference is inside of a loop.
275          * - The reference is inside of a function scope which is different from
276          *   the declaration.
277          * @param {eslint-scope.Reference} ref A reference to check.
278          * @param {ASTNode} prevRhsNode The previous RHS node.
279          * @returns {ASTNode|null} The RHS node or null.
280          * @private
281          */
282         function getRhsNode(ref, prevRhsNode) {
283             const id = ref.identifier;
284             const parent = id.parent;
285             const granpa = parent.parent;
286             const refScope = ref.from.variableScope;
287             const varScope = ref.resolved.scope.variableScope;
288             const canBeUsedLater = refScope !== varScope || astUtils.isInLoop(id);
289
290             /*
291              * Inherits the previous node if this reference is in the node.
292              * This is for `a = a + a`-like code.
293              */
294             if (prevRhsNode && isInside(id, prevRhsNode)) {
295                 return prevRhsNode;
296             }
297
298             if (parent.type === "AssignmentExpression" &&
299                 granpa.type === "ExpressionStatement" &&
300                 id === parent.left &&
301                 !canBeUsedLater
302             ) {
303                 return parent.right;
304             }
305             return null;
306         }
307
308         /**
309          * Checks whether a given function node is stored to somewhere or not.
310          * If the function node is stored, the function can be used later.
311          * @param {ASTNode} funcNode A function node to check.
312          * @param {ASTNode} rhsNode The RHS node of the previous assignment.
313          * @returns {boolean} `true` if under the following conditions:
314          *      - the funcNode is assigned to a variable.
315          *      - the funcNode is bound as an argument of a function call.
316          *      - the function is bound to a property and the object satisfies above conditions.
317          * @private
318          */
319         function isStorableFunction(funcNode, rhsNode) {
320             let node = funcNode;
321             let parent = funcNode.parent;
322
323             while (parent && isInside(parent, rhsNode)) {
324                 switch (parent.type) {
325                     case "SequenceExpression":
326                         if (parent.expressions[parent.expressions.length - 1] !== node) {
327                             return false;
328                         }
329                         break;
330
331                     case "CallExpression":
332                     case "NewExpression":
333                         return parent.callee !== node;
334
335                     case "AssignmentExpression":
336                     case "TaggedTemplateExpression":
337                     case "YieldExpression":
338                         return true;
339
340                     default:
341                         if (STATEMENT_TYPE.test(parent.type)) {
342
343                             /*
344                              * If it encountered statements, this is a complex pattern.
345                              * Since analyzeing complex patterns is hard, this returns `true` to avoid false positive.
346                              */
347                             return true;
348                         }
349                 }
350
351                 node = parent;
352                 parent = parent.parent;
353             }
354
355             return false;
356         }
357
358         /**
359          * Checks whether a given Identifier node exists inside of a function node which can be used later.
360          *
361          * "can be used later" means:
362          * - the function is assigned to a variable.
363          * - the function is bound to a property and the object can be used later.
364          * - the function is bound as an argument of a function call.
365          *
366          * If a reference exists in a function which can be used later, the reference is read when the function is called.
367          * @param {ASTNode} id An Identifier node to check.
368          * @param {ASTNode} rhsNode The RHS node of the previous assignment.
369          * @returns {boolean} `true` if the `id` node exists inside of a function node which can be used later.
370          * @private
371          */
372         function isInsideOfStorableFunction(id, rhsNode) {
373             const funcNode = astUtils.getUpperFunction(id);
374
375             return (
376                 funcNode &&
377                 isInside(funcNode, rhsNode) &&
378                 isStorableFunction(funcNode, rhsNode)
379             );
380         }
381
382         /**
383          * Checks whether a given reference is a read to update itself or not.
384          * @param {eslint-scope.Reference} ref A reference to check.
385          * @param {ASTNode} rhsNode The RHS node of the previous assignment.
386          * @returns {boolean} The reference is a read to update itself.
387          * @private
388          */
389         function isReadForItself(ref, rhsNode) {
390             const id = ref.identifier;
391             const parent = id.parent;
392             const granpa = parent.parent;
393
394             return ref.isRead() && (
395
396                 // self update. e.g. `a += 1`, `a++`
397                 (// in RHS of an assignment for itself. e.g. `a = a + 1`
398                     ((
399                         parent.type === "AssignmentExpression" &&
400                     granpa.type === "ExpressionStatement" &&
401                     parent.left === id
402                     ) ||
403                 (
404                     parent.type === "UpdateExpression" &&
405                     granpa.type === "ExpressionStatement"
406                 ) || rhsNode &&
407                 isInside(id, rhsNode) &&
408                 !isInsideOfStorableFunction(id, rhsNode)))
409             );
410         }
411
412         /**
413          * Determine if an identifier is used either in for-in loops.
414          * @param {Reference} ref The reference to check.
415          * @returns {boolean} whether reference is used in the for-in loops
416          * @private
417          */
418         function isForInRef(ref) {
419             let target = ref.identifier.parent;
420
421
422             // "for (var ...) { return; }"
423             if (target.type === "VariableDeclarator") {
424                 target = target.parent.parent;
425             }
426
427             if (target.type !== "ForInStatement") {
428                 return false;
429             }
430
431             // "for (...) { return; }"
432             if (target.body.type === "BlockStatement") {
433                 target = target.body.body[0];
434
435             // "for (...) return;"
436             } else {
437                 target = target.body;
438             }
439
440             // For empty loop body
441             if (!target) {
442                 return false;
443             }
444
445             return target.type === "ReturnStatement";
446         }
447
448         /**
449          * Determines if the variable is used.
450          * @param {Variable} variable The variable to check.
451          * @returns {boolean} True if the variable is used
452          * @private
453          */
454         function isUsedVariable(variable) {
455             const functionNodes = getFunctionDefinitions(variable),
456                 isFunctionDefinition = functionNodes.length > 0;
457             let rhsNode = null;
458
459             return variable.references.some(ref => {
460                 if (isForInRef(ref)) {
461                     return true;
462                 }
463
464                 const forItself = isReadForItself(ref, rhsNode);
465
466                 rhsNode = getRhsNode(ref, rhsNode);
467
468                 return (
469                     isReadRef(ref) &&
470                     !forItself &&
471                     !(isFunctionDefinition && isSelfReference(ref, functionNodes))
472                 );
473             });
474         }
475
476         /**
477          * Checks whether the given variable is after the last used parameter.
478          * @param {eslint-scope.Variable} variable The variable to check.
479          * @returns {boolean} `true` if the variable is defined after the last
480          * used parameter.
481          */
482         function isAfterLastUsedArg(variable) {
483             const def = variable.defs[0];
484             const params = context.getDeclaredVariables(def.node);
485             const posteriorParams = params.slice(params.indexOf(variable) + 1);
486
487             // If any used parameters occur after this parameter, do not report.
488             return !posteriorParams.some(v => v.references.length > 0 || v.eslintUsed);
489         }
490
491         /**
492          * Gets an array of variables without read references.
493          * @param {Scope} scope an eslint-scope Scope object.
494          * @param {Variable[]} unusedVars an array that saving result.
495          * @returns {Variable[]} unused variables of the scope and descendant scopes.
496          * @private
497          */
498         function collectUnusedVariables(scope, unusedVars) {
499             const variables = scope.variables;
500             const childScopes = scope.childScopes;
501             let i, l;
502
503             if (scope.type !== "global" || config.vars === "all") {
504                 for (i = 0, l = variables.length; i < l; ++i) {
505                     const variable = variables[i];
506
507                     // skip a variable of class itself name in the class scope
508                     if (scope.type === "class" && scope.block.id === variable.identifiers[0]) {
509                         continue;
510                     }
511
512                     // skip function expression names and variables marked with markVariableAsUsed()
513                     if (scope.functionExpressionScope || variable.eslintUsed) {
514                         continue;
515                     }
516
517                     // skip implicit "arguments" variable
518                     if (scope.type === "function" && variable.name === "arguments" && variable.identifiers.length === 0) {
519                         continue;
520                     }
521
522                     // explicit global variables don't have definitions.
523                     const def = variable.defs[0];
524
525                     if (def) {
526                         const type = def.type;
527
528                         // skip catch variables
529                         if (type === "CatchClause") {
530                             if (config.caughtErrors === "none") {
531                                 continue;
532                             }
533
534                             // skip ignored parameters
535                             if (config.caughtErrorsIgnorePattern && config.caughtErrorsIgnorePattern.test(def.name.name)) {
536                                 continue;
537                             }
538                         }
539
540                         if (type === "Parameter") {
541
542                             // skip any setter argument
543                             if ((def.node.parent.type === "Property" || def.node.parent.type === "MethodDefinition") && def.node.parent.kind === "set") {
544                                 continue;
545                             }
546
547                             // if "args" option is "none", skip any parameter
548                             if (config.args === "none") {
549                                 continue;
550                             }
551
552                             // skip ignored parameters
553                             if (config.argsIgnorePattern && config.argsIgnorePattern.test(def.name.name)) {
554                                 continue;
555                             }
556
557                             // if "args" option is "after-used", skip used variables
558                             if (config.args === "after-used" && astUtils.isFunction(def.name.parent) && !isAfterLastUsedArg(variable)) {
559                                 continue;
560                             }
561                         } else {
562
563                             // skip ignored variables
564                             if (config.varsIgnorePattern && config.varsIgnorePattern.test(def.name.name)) {
565                                 continue;
566                             }
567                         }
568                     }
569
570                     if (!isUsedVariable(variable) && !isExported(variable) && !hasRestSpreadSibling(variable)) {
571                         unusedVars.push(variable);
572                     }
573                 }
574             }
575
576             for (i = 0, l = childScopes.length; i < l; ++i) {
577                 collectUnusedVariables(childScopes[i], unusedVars);
578             }
579
580             return unusedVars;
581         }
582
583         //--------------------------------------------------------------------------
584         // Public
585         //--------------------------------------------------------------------------
586
587         return {
588             "Program:exit"(programNode) {
589                 const unusedVars = collectUnusedVariables(context.getScope(), []);
590
591                 for (let i = 0, l = unusedVars.length; i < l; ++i) {
592                     const unusedVar = unusedVars[i];
593
594                     // Report the first declaration.
595                     if (unusedVar.defs.length > 0) {
596                         context.report({
597                             node: unusedVar.identifiers[0],
598                             message: unusedVar.references.some(ref => ref.isWrite())
599                                 ? getAssignedMessage()
600                                 : getDefinedMessage(unusedVar),
601                             data: unusedVar
602                         });
603
604                     // If there are no regular declaration, report the first `/*globals*/` comment directive.
605                     } else if (unusedVar.eslintExplicitGlobalComments) {
606                         const directiveComment = unusedVar.eslintExplicitGlobalComments[0];
607
608                         context.report({
609                             node: programNode,
610                             loc: astUtils.getNameLocationInGlobalDirectiveComment(sourceCode, directiveComment, unusedVar.name),
611                             message: getDefinedMessage(unusedVar),
612                             data: unusedVar
613                         });
614                     }
615                 }
616             }
617         };
618
619     }
620 };