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",
24 category: "Best Practices",
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 BlockStatement: checkForBlock,
166 ForStatement: checkForBlock,
167 ForInStatement: checkForBlock,
168 ForOfStatement: checkForBlock,
169 SwitchStatement: checkForBlock