UNPKG

5.74 kBJavaScriptView Raw
1/**
2 * @fileoverview Rule to flag when the same variable is declared more then once.
3 * @author Ilya Volodin
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Requirements
10//------------------------------------------------------------------------------
11
12const astUtils = require("./utils/ast-utils");
13
14//------------------------------------------------------------------------------
15// Rule Definition
16//------------------------------------------------------------------------------
17
18module.exports = {
19 meta: {
20 type: "suggestion",
21
22 docs: {
23 description: "disallow variable redeclaration",
24 category: "Best Practices",
25 recommended: true,
26 url: "https://eslint.org/docs/rules/no-redeclare"
27 },
28
29 messages: {
30 redeclared: "'{{id}}' is already defined.",
31 redeclaredAsBuiltin: "'{{id}}' is already defined as a built-in global variable.",
32 redeclaredBySyntax: "'{{id}}' is already defined by a variable declaration."
33 },
34
35 schema: [
36 {
37 type: "object",
38 properties: {
39 builtinGlobals: { type: "boolean", default: true }
40 },
41 additionalProperties: false
42 }
43 ]
44 },
45
46 create(context) {
47 const options = {
48 builtinGlobals: Boolean(
49 context.options.length === 0 ||
50 context.options[0].builtinGlobals
51 )
52 };
53 const sourceCode = context.getSourceCode();
54
55 /**
56 * Iterate declarations of a given variable.
57 * @param {escope.variable} variable The variable object to iterate declarations.
58 * @returns {IterableIterator<{type:string,node:ASTNode,loc:SourceLocation}>} The declarations.
59 */
60 function *iterateDeclarations(variable) {
61 if (options.builtinGlobals && (
62 variable.eslintImplicitGlobalSetting === "readonly" ||
63 variable.eslintImplicitGlobalSetting === "writable"
64 )) {
65 yield { type: "builtin" };
66 }
67
68 for (const id of variable.identifiers) {
69 yield { type: "syntax", node: id, loc: id.loc };
70 }
71
72 if (variable.eslintExplicitGlobalComments) {
73 for (const comment of variable.eslintExplicitGlobalComments) {
74 yield {
75 type: "comment",
76 node: comment,
77 loc: astUtils.getNameLocationInGlobalDirectiveComment(
78 sourceCode,
79 comment,
80 variable.name
81 )
82 };
83 }
84 }
85 }
86
87 /**
88 * Find variables in a given scope and flag redeclared ones.
89 * @param {Scope} scope An eslint-scope scope object.
90 * @returns {void}
91 * @private
92 */
93 function findVariablesInScope(scope) {
94 for (const variable of scope.variables) {
95 const [
96 declaration,
97 ...extraDeclarations
98 ] = iterateDeclarations(variable);
99
100 if (extraDeclarations.length === 0) {
101 continue;
102 }
103
104 /*
105 * If the type of a declaration is different from the type of
106 * the first declaration, it shows the location of the first
107 * declaration.
108 */
109 const detailMessageId = declaration.type === "builtin"
110 ? "redeclaredAsBuiltin"
111 : "redeclaredBySyntax";
112 const data = { id: variable.name };
113
114 // Report extra declarations.
115 for (const { type, node, loc } of extraDeclarations) {
116 const messageId = type === declaration.type
117 ? "redeclared"
118 : detailMessageId;
119
120 context.report({ node, loc, messageId, data });
121 }
122 }
123 }
124
125 /**
126 * Find variables in the current scope.
127 * @param {ASTNode} node The node of the current scope.
128 * @returns {void}
129 * @private
130 */
131 function checkForBlock(node) {
132 const scope = context.getScope();
133
134 /*
135 * In ES5, some node type such as `BlockStatement` doesn't have that scope.
136 * `scope.block` is a different node in such a case.
137 */
138 if (scope.block === node) {
139 findVariablesInScope(scope);
140 }
141 }
142
143 return {
144 Program() {
145 const scope = context.getScope();
146
147 findVariablesInScope(scope);
148
149 // Node.js or ES modules has a special scope.
150 if (
151 scope.type === "global" &&
152 scope.childScopes[0] &&
153
154 // The special scope's block is the Program node.
155 scope.block === scope.childScopes[0].block
156 ) {
157 findVariablesInScope(scope.childScopes[0]);
158 }
159 },
160
161 FunctionDeclaration: checkForBlock,
162 FunctionExpression: checkForBlock,
163 ArrowFunctionExpression: checkForBlock,
164
165 BlockStatement: checkForBlock,
166 ForStatement: checkForBlock,
167 ForInStatement: checkForBlock,
168 ForOfStatement: checkForBlock,
169 SwitchStatement: checkForBlock
170 };
171 }
172};