Skip to content

Commit c60e23f

Browse files
authored
Update: fix let logic in for-in and for-of loops in no-extra-parens (#14011)
1 parent d76e8f6 commit c60e23f

2 files changed

Lines changed: 216 additions & 28 deletions

File tree

lib/rules/no-extra-parens.js

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -844,45 +844,49 @@ module.exports = {
844844
ExportDefaultDeclaration: node => checkExpressionOrExportStatement(node.declaration),
845845
ExpressionStatement: node => checkExpressionOrExportStatement(node.expression),
846846

847-
"ForInStatement, ForOfStatement"(node) {
848-
if (node.left.type !== "VariableDeclarator") {
847+
ForInStatement(node) {
848+
if (node.left.type !== "VariableDeclaration") {
849849
const firstLeftToken = sourceCode.getFirstToken(node.left, astUtils.isNotOpeningParenToken);
850850

851851
if (
852-
firstLeftToken.value === "let" && (
853-
854-
/*
855-
* If `let` is the only thing on the left side of the loop, it's the loop variable: `for ((let) of foo);`
856-
* Removing it will cause a syntax error, because it will be parsed as the start of a VariableDeclarator.
857-
*/
858-
(firstLeftToken.range[1] === node.left.range[1] || /*
859-
* If `let` is followed by a `[` token, it's a property access on the `let` value: `for ((let[foo]) of bar);`
860-
* Removing it will cause the property access to be parsed as a destructuring declaration of `foo` instead.
861-
*/
862-
astUtils.isOpeningBracketToken(
863-
sourceCode.getTokenAfter(firstLeftToken, astUtils.isNotClosingParenToken)
864-
))
852+
firstLeftToken.value === "let" &&
853+
astUtils.isOpeningBracketToken(
854+
sourceCode.getTokenAfter(firstLeftToken, astUtils.isNotClosingParenToken)
865855
)
866856
) {
857+
858+
// ForInStatement#left expression cannot start with `let[`.
867859
tokensToIgnore.add(firstLeftToken);
868860
}
869861
}
870862

871-
if (node.type === "ForOfStatement") {
872-
const hasExtraParens = node.right.type === "SequenceExpression"
873-
? hasDoubleExcessParens(node.right)
874-
: hasExcessParens(node.right);
863+
if (hasExcessParens(node.left)) {
864+
report(node.left);
865+
}
875866

876-
if (hasExtraParens) {
877-
report(node.right);
878-
}
879-
} else if (hasExcessParens(node.right)) {
867+
if (hasExcessParens(node.right)) {
880868
report(node.right);
881869
}
870+
},
871+
872+
ForOfStatement(node) {
873+
if (node.left.type !== "VariableDeclaration") {
874+
const firstLeftToken = sourceCode.getFirstToken(node.left, astUtils.isNotOpeningParenToken);
875+
876+
if (firstLeftToken.value === "let") {
877+
878+
// ForOfStatement#left expression cannot start with `let`.
879+
tokensToIgnore.add(firstLeftToken);
880+
}
881+
}
882882

883883
if (hasExcessParens(node.left)) {
884884
report(node.left);
885885
}
886+
887+
if (hasExcessParensWithPrecedence(node.right, PRECEDENCE_OF_ASSIGNMENT_EXPR)) {
888+
report(node.right);
889+
}
886890
},
887891

888892
ForStatement(node) {

tests/lib/rules/no-extra-parens.js

Lines changed: 189 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -615,10 +615,28 @@ ruleTester.run("no-extra-parens", rule, {
615615
"for ((let)[a]();;);",
616616
"for ((let[a]) + b;;);",
617617

618-
"for ((let) in foo);",
618+
// ForInStatement#left expression cannot start with `let[`. It would be parsed as a `let` declaration with array pattern, or a syntax error.
619619
"for ((let[foo]) in bar);",
620620
"for ((let)[foo] in bar);",
621621
"for ((let[foo].bar) in baz);",
622+
"for ((let[foo]).bar in baz);",
623+
"for ((let)[foo].bar in baz);",
624+
625+
// ForOfStatement#left expression cannot start with `let`. It's explicitly forbidden by the specification.
626+
"for ((let) of foo);",
627+
"for ((let).foo of bar);",
628+
"for ((let.foo) of bar);",
629+
"for ((let[foo]) of bar);",
630+
"for ((let)[foo] of bar);",
631+
"for ((let.foo.bar) of baz);",
632+
"for ((let.foo).bar of baz);",
633+
"for ((let).foo.bar of baz);",
634+
"for ((let[foo].bar) of baz);",
635+
"for ((let[foo]).bar of baz);",
636+
"for ((let)[foo].bar of baz);",
637+
"for ((let)().foo of bar);",
638+
"for ((let()).foo of bar);",
639+
"for ((let().foo) of bar);",
622640

623641
// https://github.com/eslint/eslint/issues/11706 (also in invalid[])
624642
"for (let a = (b in c); ;);",
@@ -1866,6 +1884,18 @@ ruleTester.run("no-extra-parens", rule, {
18661884
"Identifier",
18671885
1
18681886
),
1887+
invalid(
1888+
"for (foo of (baz = bar));",
1889+
"for (foo of baz = bar);",
1890+
"AssignmentExpression",
1891+
1
1892+
),
1893+
invalid(
1894+
"function* f() { for (foo of (yield bar)); }",
1895+
"function* f() { for (foo of yield bar); }",
1896+
"YieldExpression",
1897+
1
1898+
),
18691899
invalid(
18701900
"for (foo of ((bar, baz)));",
18711901
"for (foo of (bar, baz));",
@@ -2055,18 +2085,172 @@ ruleTester.run("no-extra-parens", rule, {
20552085
1
20562086
),
20572087

2088+
// ForInStatement#left expression cannot start with `let[`, but it can start with `let` if it isn't followed by `[`
2089+
invalid(
2090+
"for ((let) in foo);",
2091+
"for (let in foo);",
2092+
"Identifier",
2093+
1
2094+
),
2095+
invalid(
2096+
"for ((let())[a] in foo);",
2097+
"for (let()[a] in foo);",
2098+
"CallExpression",
2099+
1
2100+
),
2101+
invalid(
2102+
"for ((let.a) in foo);",
2103+
"for (let.a in foo);",
2104+
"MemberExpression",
2105+
1
2106+
),
2107+
invalid(
2108+
"for ((let).a in foo);",
2109+
"for (let.a in foo);",
2110+
"Identifier",
2111+
1
2112+
),
2113+
invalid(
2114+
"for ((let).a.b in foo);",
2115+
"for (let.a.b in foo);",
2116+
"Identifier",
2117+
1
2118+
),
2119+
invalid(
2120+
"for ((let).a[b] in foo);",
2121+
"for (let.a[b] in foo);",
2122+
"Identifier",
2123+
1
2124+
),
2125+
invalid(
2126+
"for ((let.a)[b] in foo);",
2127+
"for (let.a[b] in foo);",
2128+
"MemberExpression",
2129+
1
2130+
),
2131+
invalid(
2132+
"for ((let.a[b]) in foo);",
2133+
"for (let.a[b] in foo);",
2134+
"MemberExpression",
2135+
1
2136+
),
2137+
invalid(
2138+
"for (((let[a])) in foo);",
2139+
"for ((let[a]) in foo);",
2140+
"MemberExpression",
2141+
1
2142+
),
2143+
invalid(
2144+
"for (((let))[a] in foo);",
2145+
"for ((let)[a] in foo);",
2146+
"Identifier",
2147+
1
2148+
),
2149+
invalid(
2150+
"for (((let[a])).b in foo);",
2151+
"for ((let[a]).b in foo);",
2152+
"MemberExpression",
2153+
1
2154+
),
2155+
invalid(
2156+
"for (((let))[a].b in foo);",
2157+
"for ((let)[a].b in foo);",
2158+
"Identifier",
2159+
1
2160+
),
2161+
invalid(
2162+
"for (((let)[a]).b in foo);",
2163+
"for ((let)[a].b in foo);",
2164+
"MemberExpression",
2165+
1
2166+
),
2167+
invalid(
2168+
"for (((let[a]).b) in foo);",
2169+
"for ((let[a]).b in foo);",
2170+
"MemberExpression",
2171+
1
2172+
),
2173+
invalid(
2174+
"for ((Let[a]) in foo);",
2175+
"for (Let[a] in foo);",
2176+
"MemberExpression",
2177+
1
2178+
),
2179+
invalid(
2180+
"for ((lett)[a] in foo);",
2181+
"for (lett[a] in foo);",
2182+
"Identifier",
2183+
1
2184+
),
2185+
2186+
// ForOfStatement#left expression cannot start with `let`
2187+
invalid(
2188+
"for (((let)) of foo);",
2189+
"for ((let) of foo);",
2190+
"Identifier",
2191+
1
2192+
),
20582193
invalid(
2059-
"for ((let.foo) in bar);",
2060-
"for (let.foo in bar);",
2194+
"for (((let)).a of foo);",
2195+
"for ((let).a of foo);",
2196+
"Identifier",
2197+
1
2198+
),
2199+
invalid(
2200+
"for (((let))[a] of foo);",
2201+
"for ((let)[a] of foo);",
2202+
"Identifier",
2203+
1
2204+
),
2205+
invalid(
2206+
"for (((let).a) of foo);",
2207+
"for ((let).a of foo);",
2208+
"MemberExpression",
2209+
1
2210+
),
2211+
invalid(
2212+
"for (((let[a]).b) of foo);",
2213+
"for ((let[a]).b of foo);",
2214+
"MemberExpression",
2215+
1
2216+
),
2217+
invalid(
2218+
"for (((let).a).b of foo);",
2219+
"for ((let).a.b of foo);",
2220+
"MemberExpression",
2221+
1
2222+
),
2223+
invalid(
2224+
"for (((let).a.b) of foo);",
2225+
"for ((let).a.b of foo);",
20612226
"MemberExpression",
20622227
1
20632228
),
20642229
invalid(
2065-
"for ((let).foo.bar in baz);",
2066-
"for (let.foo.bar in baz);",
2230+
"for (((let.a).b) of foo);",
2231+
"for ((let.a).b of foo);",
2232+
"MemberExpression",
2233+
1
2234+
),
2235+
invalid(
2236+
"for (((let()).a) of foo);",
2237+
"for ((let()).a of foo);",
2238+
"MemberExpression",
2239+
1
2240+
),
2241+
invalid(
2242+
"for ((Let) of foo);",
2243+
"for (Let of foo);",
2244+
"Identifier",
2245+
1
2246+
),
2247+
invalid(
2248+
"for ((lett) of foo);",
2249+
"for (lett of foo);",
20672250
"Identifier",
20682251
1
20692252
),
2253+
20702254
invalid("for (a in (b, c));", "for (a in b, c);", "SequenceExpression", null),
20712255
invalid(
20722256
"(let)",

0 commit comments

Comments
 (0)