]>
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 on line {{shadowedLine}} column {{shadowedColumn}}.",
48 noShadowGlobal
: "'{{name}}' is already a global variable."
55 builtinGlobals
: context
.options
[0] && context
.options
[0].builtinGlobals
,
56 hoist
: (context
.options
[0] && context
.options
[0].hoist
) || "functions",
57 allow
: (context
.options
[0] && context
.options
[0].allow
) || []
61 * Check if variable name is allowed.
62 * @param {ASTNode} variable The variable to check.
63 * @returns {boolean} Whether or not the variable name is allowed.
65 function isAllowed(variable
) {
66 return options
.allow
.indexOf(variable
.name
) !== -1;
70 * Checks if a variable of the class name in the class scope of ClassDeclaration.
72 * ClassDeclaration creates two variables of its name into its outer scope and its class scope.
73 * So we should ignore the variable in the class scope.
74 * @param {Object} variable The variable to check.
75 * @returns {boolean} Whether or not the variable of the class name in the class scope of ClassDeclaration.
77 function isDuplicatedClassNameVariable(variable
) {
78 const block
= variable
.scope
.block
;
80 return block
.type
=== "ClassDeclaration" && block
.id
=== variable
.identifiers
[0];
84 * Checks if a variable is inside the initializer of scopeVar.
86 * To avoid reporting at declarations such as `var a = function a() {};`.
87 * But it should report `var a = function(a) {};` or `var a = function() { function a() {} };`.
88 * @param {Object} variable The variable to check.
89 * @param {Object} scopeVar The scope variable to look for.
90 * @returns {boolean} Whether or not the variable is inside initializer of scopeVar.
92 function isOnInitializer(variable
, scopeVar
) {
93 const outerScope
= scopeVar
.scope
;
94 const outerDef
= scopeVar
.defs
[0];
95 const outer
= outerDef
&& outerDef
.parent
&& outerDef
.parent
.range
;
96 const innerScope
= variable
.scope
;
97 const innerDef
= variable
.defs
[0];
98 const inner
= innerDef
&& innerDef
.name
.range
;
103 outer
[0] < inner
[0] &&
104 inner
[1] < outer
[1] &&
105 ((innerDef
.type
=== "FunctionName" && innerDef
.node
.type
=== "FunctionExpression") || innerDef
.node
.type
=== "ClassExpression") &&
106 outerScope
=== innerScope
.upper
111 * Get a range of a variable's identifier node.
112 * @param {Object} variable The variable to get.
113 * @returns {Array|undefined} The range of the variable's identifier node.
115 function getNameRange(variable
) {
116 const def
= variable
.defs
[0];
118 return def
&& def
.name
.range
;
122 * Get declared line and column of a variable.
123 * @param {eslint-scope.Variable} variable The variable to get.
124 * @returns {Object} The declared line and column of the variable.
126 function getDeclaredLocation(variable
) {
127 const identifier
= variable
.identifiers
[0];
133 line
: identifier
.loc
.start
.line
,
134 column
: identifier
.loc
.start
.column
+ 1
145 * Checks if a variable is in TDZ of scopeVar.
146 * @param {Object} variable The variable to check.
147 * @param {Object} scopeVar The variable of TDZ.
148 * @returns {boolean} Whether or not the variable is in TDZ of scopeVar.
150 function isInTdz(variable
, scopeVar
) {
151 const outerDef
= scopeVar
.defs
[0];
152 const inner
= getNameRange(variable
);
153 const outer
= getNameRange(scopeVar
);
158 inner
[1] < outer
[0] &&
160 // Excepts FunctionDeclaration if is {"hoist":"function"}.
161 (options
.hoist
!== "functions" || !outerDef
|| outerDef
.node
.type
!== "FunctionDeclaration")
166 * Checks the current context for shadowed variables.
167 * @param {Scope} scope Fixme
170 function checkForShadows(scope
) {
171 const variables
= scope
.variables
;
173 for (let i
= 0; i
< variables
.length
; ++i
) {
174 const variable
= variables
[i
];
176 // Skips "arguments" or variables of a class name in the class scope of ClassDeclaration.
177 if (variable
.identifiers
.length
=== 0 ||
178 isDuplicatedClassNameVariable(variable
) ||
184 // Gets shadowed variable.
185 const shadowed
= astUtils
.getVariableByName(scope
.upper
, variable
.name
);
188 (shadowed
.identifiers
.length
> 0 || (options
.builtinGlobals
&& "writeable" in shadowed
)) &&
189 !isOnInitializer(variable
, shadowed
) &&
190 !(options
.hoist
!== "all" && isInTdz(variable
, shadowed
))
192 const location
= getDeclaredLocation(shadowed
);
193 const messageId
= location
.global
? "noShadowGlobal" : "noShadow";
194 const data
= { name
: variable
.name
};
196 if (!location
.global
) {
197 data
.shadowedLine
= location
.line
;
198 data
.shadowedColumn
= location
.column
;
201 node
: variable
.identifiers
[0],
211 const globalScope
= context
.getScope();
212 const stack
= globalScope
.childScopes
.slice();
214 while (stack
.length
) {
215 const scope
= stack
.pop();
217 stack
.push(...scope
.childScopes
);
218 checkForShadows(scope
);