Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitd07daf3

Browse files
committed
Fix:no-shadow allows shadowing in the TDZ (fixes#2568)
1 parent1dee732 commitd07daf3

File tree

3 files changed

+225
-5
lines changed

3 files changed

+225
-5
lines changed

‎docs/rules/no-shadow.md‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,42 @@ if (true) {
4747
}
4848
```
4949

50+
##Options
51+
52+
This rule has a option for the hoisting behavior.
53+
54+
```json
55+
{
56+
"rules": {
57+
"no-shadow": [2, {"hoist":"functions"}]
58+
}
59+
}
60+
```
61+
62+
###hoist
63+
64+
The option has three settings:
65+
66+
*`all` - reports all shadowing before the outer variables/functions are defined.
67+
*`functions` (by default) - reports shadowing before the outer functions are defined.
68+
*`never` - never report shadowing before the outer variables/functions are defined.
69+
70+
Thought with the following codes:
71+
72+
```js
73+
if (true) {
74+
let a=3;
75+
let b=6;
76+
}
77+
78+
let a=5;
79+
functionb() {}
80+
```
81+
82+
*`all` - Both`let a` and`let b` in the`if` statement are considered warnings.
83+
*`functions` -`let b` is considered warnings. But`let a` in the`if` statement is not considered warnings. Because there is it before`let a` of the outer scope.
84+
*`never` - Both`let a` and`let b` in the`if` statement are not considered warnings. Because there are those before each declaration of the outer scope.
85+
5086
##Further Reading
5187

5288
*[Variable Shadowing](http://en.wikipedia.org/wiki/Variable_shadowing)

‎lib/rules/no-shadow.js‎

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212

1313
module.exports=function(context){
1414

15+
varoptions={
16+
hoist:(context.options[0]&&context.options[0].hoist)||"functions"
17+
};
18+
1519
/**
1620
* Checks if a variable of the class name in the class scope of ClassDeclaration.
1721
*
@@ -45,15 +49,44 @@ module.exports = function(context) {
4549
varinner=innerDef&&innerDef.name.range;
4650

4751
return(
48-
outer&&
49-
inner&&
52+
outer!=null&&
53+
inner!=null&&
5054
outer[0]<inner[0]&&
5155
inner[1]<outer[1]&&
5256
((innerDef.type==="FunctionName"&&innerDef.node.type==="FunctionExpression")||innerDef.node.type==="ClassExpression")&&
5357
outerScope===innerScope.upper
5458
);
5559
}
5660

61+
/**
62+
* Get a range of a variable's identifier node.
63+
*@param {Object} variable The variable to get.
64+
*@returns {Array|undefined} The range of the variable's identifier node.
65+
*/
66+
functiongetNameRange(variable){
67+
vardef=variable.defs[0];
68+
returndef&&def.name.range;
69+
}
70+
71+
/**
72+
* Checks if a variable is in TDZ of scopeVar.
73+
*@param {Object} variable The variable to check.
74+
*@param {Object} scopeVar The variable of TDZ.
75+
*@returns {boolean} Whether or not the variable is in TDZ of scopeVar.
76+
*/
77+
functionisInTdz(variable,scopeVar){
78+
varouterDef=scopeVar.defs[0];
79+
varinner=getNameRange(variable);
80+
varouter=getNameRange(scopeVar);
81+
return(
82+
inner!=null&&
83+
outer!=null&&
84+
inner[1]<outer[0]&&
85+
// Excepts FunctionDeclaration if is {"hoist":"function"}.
86+
(options.hoist!=="functions"||outerDef==null||outerDef.node.type!=="FunctionDeclaration")
87+
);
88+
}
89+
5790
/**
5891
* Checks if a variable is contained in the list of given scope variables.
5992
*@param {Object} variable The variable to check.
@@ -66,7 +99,8 @@ module.exports = function(context) {
6699
scopeVar.identifiers.length>0&&
67100
variable.name===scopeVar.name&&
68101
!isDuplicatedClassNameVariable(scopeVar)&&
69-
!isOnInitializer(variable,scopeVar)
102+
!isOnInitializer(variable,scopeVar)&&
103+
!(options.hoist!=="all"&&isInTdz(variable,scopeVar))
70104
);
71105
});
72106
}
@@ -133,4 +167,13 @@ module.exports = function(context) {
133167

134168
};
135169

136-
module.exports.schema=[];
170+
module.exports.schema=[
171+
{
172+
"type":"object",
173+
"properties":{
174+
"hoist":{
175+
"enum":["all","functions","never"]
176+
}
177+
}
178+
}
179+
];

‎tests/lib/rules/no-shadow.js‎

Lines changed: 142 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,33 @@ eslintTester.addRuleTest("lib/rules/no-shadow", {
2626
{code:"var a=3; var b = (x) => { a++; return x + a; }; setTimeout(() => { b(a); }, 0);",ecmaFeatures:{arrowFunctions:true}},
2727
{code:"class A {}",ecmaFeatures:{classes:true}},
2828
{code:"class A { constructor() { var a; } }",ecmaFeatures:{classes:true}},
29-
{code:"(function() { var A = class A {}; })()",ecmaFeatures:{classes:true}}
29+
{code:"(function() { var A = class A {}; })()",ecmaFeatures:{classes:true}},
30+
{code:"{ var a; } let a;",ecmaFeatures:{blockBindings:true}},// this case reports `no-redeclare`, not shadowing.
31+
{code:"{ let a; } let a;",options:[{hoist:"never"}],ecmaFeatures:{blockBindings:true}},
32+
{code:"{ let a; } var a;",options:[{hoist:"never"}],ecmaFeatures:{blockBindings:true}},
33+
{code:"{ let a; } function a() {}",options:[{hoist:"never"}],ecmaFeatures:{blockBindings:true}},
34+
{code:"{ const a = 0; } const a = 1;",options:[{hoist:"never"}],ecmaFeatures:{blockBindings:true}},
35+
{code:"{ const a = 0; } var a;",options:[{hoist:"never"}],ecmaFeatures:{blockBindings:true}},
36+
{code:"{ const a = 0; } function a() {}",options:[{hoist:"never"}],ecmaFeatures:{blockBindings:true}},
37+
{code:"function foo() { let a; } let a;",options:[{hoist:"never"}],ecmaFeatures:{blockBindings:true}},
38+
{code:"function foo() { let a; } var a;",options:[{hoist:"never"}],ecmaFeatures:{blockBindings:true}},
39+
{code:"function foo() { let a; } function a() {}",options:[{hoist:"never"}],ecmaFeatures:{blockBindings:true}},
40+
{code:"function foo() { var a; } let a;",options:[{hoist:"never"}],ecmaFeatures:{blockBindings:true}},
41+
{code:"function foo() { var a; } var a;",options:[{hoist:"never"}],ecmaFeatures:{blockBindings:true}},
42+
{code:"function foo() { var a; } function a() {}",options:[{hoist:"never"}],ecmaFeatures:{blockBindings:true}},
43+
{code:"function foo(a) { } let a;",options:[{hoist:"never"}],ecmaFeatures:{blockBindings:true}},
44+
{code:"function foo(a) { } var a;",options:[{hoist:"never"}],ecmaFeatures:{blockBindings:true}},
45+
{code:"function foo(a) { } function a() {}",options:[{hoist:"never"}],ecmaFeatures:{blockBindings:true}},
46+
{code:"{ let a; } let a;",ecmaFeatures:{blockBindings:true}},
47+
{code:"{ let a; } var a;",ecmaFeatures:{blockBindings:true}},
48+
{code:"{ const a = 0; } const a = 1;",ecmaFeatures:{blockBindings:true}},
49+
{code:"{ const a = 0; } var a;",ecmaFeatures:{blockBindings:true}},
50+
{code:"function foo() { let a; } let a;",ecmaFeatures:{blockBindings:true}},
51+
{code:"function foo() { let a; } var a;",ecmaFeatures:{blockBindings:true}},
52+
{code:"function foo() { var a; } let a;",ecmaFeatures:{blockBindings:true}},
53+
{code:"function foo() { var a; } var a;",ecmaFeatures:{blockBindings:true}},
54+
{code:"function foo(a) { } let a;",ecmaFeatures:{blockBindings:true}},
55+
{code:"function foo(a) { } var a;",ecmaFeatures:{blockBindings:true}}
3056
],
3157
invalid:[
3258
{
@@ -90,6 +116,121 @@ eslintTester.addRuleTest("lib/rules/no-shadow", {
90116
ecmaFeatures:{blockBindings:true},
91117
errors:[{message:"x is already declared in the upper scope.",type:"Identifier"}]
92118
},
119+
{
120+
code:"{ let a; } function a() {}",
121+
ecmaFeatures:{blockBindings:true},
122+
errors:[{message:"a is already declared in the upper scope.",type:"Identifier"}]
123+
},
124+
{
125+
code:"{ const a = 0; } function a() {}",
126+
ecmaFeatures:{blockBindings:true},
127+
errors:[{message:"a is already declared in the upper scope.",type:"Identifier"}]
128+
},
129+
{
130+
code:"function foo() { let a; } function a() {}",
131+
ecmaFeatures:{blockBindings:true},
132+
errors:[{message:"a is already declared in the upper scope.",type:"Identifier"}]
133+
},
134+
{
135+
code:"function foo() { var a; } function a() {}",
136+
ecmaFeatures:{blockBindings:true},
137+
errors:[{message:"a is already declared in the upper scope.",type:"Identifier"}]
138+
},
139+
{
140+
code:"function foo(a) { } function a() {}",
141+
ecmaFeatures:{blockBindings:true},
142+
errors:[{message:"a is already declared in the upper scope.",type:"Identifier"}]
143+
},
144+
{
145+
code:"{ let a; } let a;",
146+
options:[{hoist:"all"}],
147+
ecmaFeatures:{blockBindings:true},
148+
errors:[{message:"a is already declared in the upper scope.",type:"Identifier"}]
149+
},
150+
{
151+
code:"{ let a; } var a;",
152+
options:[{hoist:"all"}],
153+
ecmaFeatures:{blockBindings:true},
154+
errors:[{message:"a is already declared in the upper scope.",type:"Identifier"}]
155+
},
156+
{
157+
code:"{ let a; } function a() {}",
158+
options:[{hoist:"all"}],
159+
ecmaFeatures:{blockBindings:true},
160+
errors:[{message:"a is already declared in the upper scope.",type:"Identifier"}]
161+
},
162+
{
163+
code:"{ const a = 0; } const a = 1;",
164+
options:[{hoist:"all"}],
165+
ecmaFeatures:{blockBindings:true},
166+
errors:[{message:"a is already declared in the upper scope.",type:"Identifier"}]
167+
},
168+
{
169+
code:"{ const a = 0; } var a;",
170+
options:[{hoist:"all"}],
171+
ecmaFeatures:{blockBindings:true},
172+
errors:[{message:"a is already declared in the upper scope.",type:"Identifier"}]
173+
},
174+
{
175+
code:"{ const a = 0; } function a() {}",
176+
options:[{hoist:"all"}],
177+
ecmaFeatures:{blockBindings:true},
178+
errors:[{message:"a is already declared in the upper scope.",type:"Identifier"}]
179+
},
180+
{
181+
code:"function foo() { let a; } let a;",
182+
options:[{hoist:"all"}],
183+
ecmaFeatures:{blockBindings:true},
184+
errors:[{message:"a is already declared in the upper scope.",type:"Identifier"}]
185+
},
186+
{
187+
code:"function foo() { let a; } var a;",
188+
options:[{hoist:"all"}],
189+
ecmaFeatures:{blockBindings:true},
190+
errors:[{message:"a is already declared in the upper scope.",type:"Identifier"}]
191+
},
192+
{
193+
code:"function foo() { let a; } function a() {}",
194+
options:[{hoist:"all"}],
195+
ecmaFeatures:{blockBindings:true},
196+
errors:[{message:"a is already declared in the upper scope.",type:"Identifier"}]
197+
},
198+
{
199+
code:"function foo() { var a; } let a;",
200+
options:[{hoist:"all"}],
201+
ecmaFeatures:{blockBindings:true},
202+
errors:[{message:"a is already declared in the upper scope.",type:"Identifier"}]
203+
},
204+
{
205+
code:"function foo() { var a; } var a;",
206+
options:[{hoist:"all"}],
207+
ecmaFeatures:{blockBindings:true},
208+
errors:[{message:"a is already declared in the upper scope.",type:"Identifier"}]
209+
},
210+
{
211+
code:"function foo() { var a; } function a() {}",
212+
options:[{hoist:"all"}],
213+
ecmaFeatures:{blockBindings:true},
214+
errors:[{message:"a is already declared in the upper scope.",type:"Identifier"}]
215+
},
216+
{
217+
code:"function foo(a) { } let a;",
218+
options:[{hoist:"all"}],
219+
ecmaFeatures:{blockBindings:true},
220+
errors:[{message:"a is already declared in the upper scope.",type:"Identifier"}]
221+
},
222+
{
223+
code:"function foo(a) { } var a;",
224+
options:[{hoist:"all"}],
225+
ecmaFeatures:{blockBindings:true},
226+
errors:[{message:"a is already declared in the upper scope.",type:"Identifier"}]
227+
},
228+
{
229+
code:"function foo(a) { } function a() {}",
230+
options:[{hoist:"all"}],
231+
ecmaFeatures:{blockBindings:true},
232+
errors:[{message:"a is already declared in the upper scope.",type:"Identifier"}]
233+
},
93234
{
94235
code:"(function a() { function a(){} })()",
95236
errors:[{message:"a is already declared in the upper scope.",type:"Identifier"}]

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp