]>
git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/no-shadow.js
2 * @fileoverview Rule to flag on declaring variables already declared in the outer scope
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
12 const astUtils
= require("./utils/ast-utils");
14 //------------------------------------------------------------------------------
16 //------------------------------------------------------------------------------
23 description
: "disallow variable declarations from shadowing variables declared in the outer scope",
24 category
: "Variables",
26 url
: "https://eslint.org/docs/rules/no-shadow"
33 builtinGlobals
: { type
: "boolean", default: false },
34 hoist
: { enum: ["all", "functions", "never"], default: "functions" },
42 additionalProperties
: false
47 noShadow
: "'{{name}}' is already declared in the upper scope."
54 builtinGlobals
: context
.options
[0] && context
.options
[0].builtinGlobals
,
55 hoist
: (context
.options
[0] && context
.options
[0].hoist
) || "functions",
56 allow
: (context
.options
[0] && context
.options
[0].allow
) || []
60 * Check if variable name is allowed.
61 * @param {ASTNode} variable The variable to check.
62 * @returns {boolean} Whether or not the variable name is allowed.
64 function isAllowed(variable
) {
65 return options
.allow
.indexOf(variable
.name
) !== -1;
69 * Checks if a variable of the class name in the class scope of ClassDeclaration.
71 * ClassDeclaration creates two variables of its name into its outer scope and its class scope.
72 * So we should ignore the variable in the class scope.
73 * @param {Object} variable The variable to check.
74 * @returns {boolean} Whether or not the variable of the class name in the class scope of ClassDeclaration.
76 function isDuplicatedClassNameVariable(variable
) {
77 const block
= variable
.scope
.block
;
79 return block
.type
=== "ClassDeclaration" && block
.id
=== variable
.identifiers
[0];
83 * Checks if a variable is inside the initializer of scopeVar.
85 * To avoid reporting at declarations such as `var a = function a() {};`.
86 * But it should report `var a = function(a) {};` or `var a = function() { function a() {} };`.
87 * @param {Object} variable The variable to check.
88 * @param {Object} scopeVar The scope variable to look for.
89 * @returns {boolean} Whether or not the variable is inside initializer of scopeVar.
91 function isOnInitializer(variable
, scopeVar
) {
92 const outerScope
= scopeVar
.scope
;
93 const outerDef
= scopeVar
.defs
[0];
94 const outer
= outerDef
&& outerDef
.parent
&& outerDef
.parent
.range
;
95 const innerScope
= variable
.scope
;
96 const innerDef
= variable
.defs
[0];
97 const inner
= innerDef
&& innerDef
.name
.range
;
102 outer
[0] < inner
[0] &&
103 inner
[1] < outer
[1] &&
104 ((innerDef
.type
=== "FunctionName" && innerDef
.node
.type
=== "FunctionExpression") || innerDef
.node
.type
=== "ClassExpression") &&
105 outerScope
=== innerScope
.upper
110 * Get a range of a variable's identifier node.
111 * @param {Object} variable The variable to get.
112 * @returns {Array|undefined} The range of the variable's identifier node.
114 function getNameRange(variable
) {
115 const def
= variable
.defs
[0];
117 return def
&& def
.name
.range
;
121 * Checks if a variable is in TDZ of scopeVar.
122 * @param {Object} variable The variable to check.
123 * @param {Object} scopeVar The variable of TDZ.
124 * @returns {boolean} Whether or not the variable is in TDZ of scopeVar.
126 function isInTdz(variable
, scopeVar
) {
127 const outerDef
= scopeVar
.defs
[0];
128 const inner
= getNameRange(variable
);
129 const outer
= getNameRange(scopeVar
);
134 inner
[1] < outer
[0] &&
136 // Excepts FunctionDeclaration if is {"hoist":"function"}.
137 (options
.hoist
!== "functions" || !outerDef
|| outerDef
.node
.type
!== "FunctionDeclaration")
142 * Checks the current context for shadowed variables.
143 * @param {Scope} scope Fixme
146 function checkForShadows(scope
) {
147 const variables
= scope
.variables
;
149 for (let i
= 0; i
< variables
.length
; ++i
) {
150 const variable
= variables
[i
];
152 // Skips "arguments" or variables of a class name in the class scope of ClassDeclaration.
153 if (variable
.identifiers
.length
=== 0 ||
154 isDuplicatedClassNameVariable(variable
) ||
160 // Gets shadowed variable.
161 const shadowed
= astUtils
.getVariableByName(scope
.upper
, variable
.name
);
164 (shadowed
.identifiers
.length
> 0 || (options
.builtinGlobals
&& "writeable" in shadowed
)) &&
165 !isOnInitializer(variable
, shadowed
) &&
166 !(options
.hoist
!== "all" && isInTdz(variable
, shadowed
))
169 node
: variable
.identifiers
[0],
170 messageId
: "noShadow",
179 const globalScope
= context
.getScope();
180 const stack
= globalScope
.childScopes
.slice();
182 while (stack
.length
) {
183 const scope
= stack
.pop();
185 stack
.push(...scope
.childScopes
);
186 checkForShadows(scope
);