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 //------------------------------------------------------------------------------
23 description
: "disallow variable redeclaration",
25 url
: "https://eslint.org/docs/rules/no-redeclare"
29 redeclared
: "'{{id}}' is already defined.",
30 redeclaredAsBuiltin
: "'{{id}}' is already defined as a built-in global variable.",
31 redeclaredBySyntax
: "'{{id}}' is already defined by a variable declaration."
38 builtinGlobals
: { type
: "boolean", default: true }
40 additionalProperties
: false
47 builtinGlobals
: Boolean(
48 context
.options
.length
=== 0 ||
49 context
.options
[0].builtinGlobals
52 const sourceCode
= context
.getSourceCode();
55 * Iterate declarations of a given variable.
56 * @param {escope.variable} variable The variable object to iterate declarations.
57 * @returns {IterableIterator<{type:string,node:ASTNode,loc:SourceLocation}>} The declarations.
59 function *iterateDeclarations(variable
) {
60 if (options
.builtinGlobals
&& (
61 variable
.eslintImplicitGlobalSetting
=== "readonly" ||
62 variable
.eslintImplicitGlobalSetting
=== "writable"
64 yield { type
: "builtin" };
67 for (const id
of variable
.identifiers
) {
68 yield { type
: "syntax", node
: id
, loc
: id
.loc
};
71 if (variable
.eslintExplicitGlobalComments
) {
72 for (const comment
of variable
.eslintExplicitGlobalComments
) {
76 loc
: astUtils
.getNameLocationInGlobalDirectiveComment(
87 * Find variables in a given scope and flag redeclared ones.
88 * @param {Scope} scope An eslint-scope scope object.
92 function findVariablesInScope(scope
) {
93 for (const variable
of scope
.variables
) {
97 ] = iterateDeclarations(variable
);
99 if (extraDeclarations
.length
=== 0) {
104 * If the type of a declaration is different from the type of
105 * the first declaration, it shows the location of the first
108 const detailMessageId
= declaration
.type
=== "builtin"
109 ? "redeclaredAsBuiltin"
110 : "redeclaredBySyntax";
111 const data
= { id
: variable
.name
};
113 // Report extra declarations.
114 for (const { type
, node
, loc
} of extraDeclarations
) {
115 const messageId
= type
=== declaration
.type
119 context
.report({ node
, loc
, messageId
, data
});
125 * Find variables in the current scope.
126 * @param {ASTNode} node The node of the current scope.
130 function checkForBlock(node
) {
131 const scope
= context
.getScope();
134 * In ES5, some node type such as `BlockStatement` doesn't have that scope.
135 * `scope.block` is a different node in such a case.
137 if (scope
.block
=== node
) {
138 findVariablesInScope(scope
);
144 const scope
= context
.getScope();
146 findVariablesInScope(scope
);
148 // Node.js or ES modules has a special scope.
150 scope
.type
=== "global" &&
151 scope
.childScopes
[0] &&
153 // The special scope's block is the Program node.
154 scope
.block
=== scope
.childScopes
[0].block
156 findVariablesInScope(scope
.childScopes
[0]);
160 FunctionDeclaration
: checkForBlock
,
161 FunctionExpression
: checkForBlock
,
162 ArrowFunctionExpression
: checkForBlock
,
164 StaticBlock
: checkForBlock
,
166 BlockStatement
: checkForBlock
,
167 ForStatement
: checkForBlock
,
168 ForInStatement
: checkForBlock
,
169 ForOfStatement
: checkForBlock
,
170 SwitchStatement
: checkForBlock