2 * @fileoverview Rule to flag when the same variable is declared more then once.
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
12 const astUtils
= require("./utils/ast-utils");
14 //------------------------------------------------------------------------------
16 //------------------------------------------------------------------------------
18 /** @type {import('../shared/types').Rule} */
24 description
: "Disallow variable redeclaration",
26 url
: "https://eslint.org/docs/rules/no-redeclare"
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."
39 builtinGlobals
: { type
: "boolean", default: true }
41 additionalProperties
: false
48 builtinGlobals
: Boolean(
49 context
.options
.length
=== 0 ||
50 context
.options
[0].builtinGlobals
53 const sourceCode
= context
.getSourceCode();
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.
60 function *iterateDeclarations(variable
) {
61 if (options
.builtinGlobals
&& (
62 variable
.eslintImplicitGlobalSetting
=== "readonly" ||
63 variable
.eslintImplicitGlobalSetting
=== "writable"
65 yield { type
: "builtin" };
68 for (const id
of variable
.identifiers
) {
69 yield { type
: "syntax", node
: id
, loc
: id
.loc
};
72 if (variable
.eslintExplicitGlobalComments
) {
73 for (const comment
of variable
.eslintExplicitGlobalComments
) {
77 loc
: astUtils
.getNameLocationInGlobalDirectiveComment(
88 * Find variables in a given scope and flag redeclared ones.
89 * @param {Scope} scope An eslint-scope scope object.
93 function findVariablesInScope(scope
) {
94 for (const variable
of scope
.variables
) {
98 ] = iterateDeclarations(variable
);
100 if (extraDeclarations
.length
=== 0) {
105 * If the type of a declaration is different from the type of
106 * the first declaration, it shows the location of the first
109 const detailMessageId
= declaration
.type
=== "builtin"
110 ? "redeclaredAsBuiltin"
111 : "redeclaredBySyntax";
112 const data
= { id
: variable
.name
};
114 // Report extra declarations.
115 for (const { type
, node
, loc
} of extraDeclarations
) {
116 const messageId
= type
=== declaration
.type
120 context
.report({ node
, loc
, messageId
, data
});
126 * Find variables in the current scope.
127 * @param {ASTNode} node The node of the current scope.
131 function checkForBlock(node
) {
132 const scope
= context
.getScope();
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.
138 if (scope
.block
=== node
) {
139 findVariablesInScope(scope
);
145 const scope
= context
.getScope();
147 findVariablesInScope(scope
);
149 // Node.js or ES modules has a special scope.
151 scope
.type
=== "global" &&
152 scope
.childScopes
[0] &&
154 // The special scope's block is the Program node.
155 scope
.block
=== scope
.childScopes
[0].block
157 findVariablesInScope(scope
.childScopes
[0]);
161 FunctionDeclaration
: checkForBlock
,
162 FunctionExpression
: checkForBlock
,
163 ArrowFunctionExpression
: checkForBlock
,
165 StaticBlock
: checkForBlock
,
167 BlockStatement
: checkForBlock
,
168 ForStatement
: checkForBlock
,
169 ForInStatement
: checkForBlock
,
170 ForOfStatement
: checkForBlock
,
171 SwitchStatement
: checkForBlock