Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update: report double extra parens in no-extra-parens (fixes #12127) #12697

Merged
merged 11 commits into from Jan 16, 2020
77 changes: 51 additions & 26 deletions lib/rules/no-extra-parens.js
Expand Up @@ -169,6 +169,26 @@ module.exports = {
return ruleApplies(node) && isParenthesisedTwice(node);
}

/**
* Determines if a node that is expected to be parenthesised is surrounded by
yeonjuan marked this conversation as resolved.
Show resolved Hide resolved
* (potentially) invalid extra parentheses with considering precedence.
* @param {ASTNode} node The node to be checked.
* @param {number} precedenceLowerLimit The lower limit of precedence.
* @returns {boolean} True if the node is has an unexpected extra pair of parentheses.
* @private
*/
function hasExcessParensWithConsiderPrecedence(node, precedenceLowerLimit) {
yeonjuan marked this conversation as resolved.
Show resolved Hide resolved
if (ruleApplies(node) && isParenthesised(node)) {
if (
precedence(node) >= precedenceLowerLimit ||
isParenthesisedTwice(node)
) {
return true;
}
}
return false;
}

/**
* Determines if a node test expression is allowed to have a parenthesised assignment
* @param {ASTNode} node The node to be checked.
Expand Down Expand Up @@ -411,7 +431,7 @@ module.exports = {
function checkCallNew(node) {
const callee = node.callee;

if (hasExcessParens(callee) && precedence(callee) >= precedence(node)) {
if (hasExcessParensWithConsiderPrecedence(callee, precedence(node))) {
const hasNewParensException = callee.type === "NewExpression" && !isNewExpressionWithParens(callee);

if (
Expand All @@ -429,7 +449,9 @@ module.exports = {
}
}
node.arguments
.filter(arg => hasExcessParens(arg) && precedence(arg) >= PRECEDENCE_OF_ASSIGNMENT_EXPR)
.filter(arg => hasExcessParens(arg))
.filter(arg => precedence(arg) >= PRECEDENCE_OF_ASSIGNMENT_EXPR ||
hasDoubleExcessParens(arg))
mdjermanovic marked this conversation as resolved.
Show resolved Hide resolved
.forEach(report);
}

Expand All @@ -448,11 +470,22 @@ module.exports = {
node.left.type === "UnaryExpression" && isExponentiation;
const shouldSkipRight = NESTED_BINARY && (node.right.type === "BinaryExpression" || node.right.type === "LogicalExpression");

if (!shouldSkipLeft && hasExcessParens(node.left) && (leftPrecedence > prec || (leftPrecedence === prec && !isExponentiation))) {
report(node.left);
if (!shouldSkipLeft && hasExcessParens(node.left)) {
if (
(leftPrecedence > prec || (leftPrecedence === prec && !isExponentiation)) ||
hasDoubleExcessParens(node.left)
mdjermanovic marked this conversation as resolved.
Show resolved Hide resolved
) {
report(node.left);
}
}
if (!shouldSkipRight && hasExcessParens(node.right) && (rightPrecedence > prec || (rightPrecedence === prec && isExponentiation))) {
report(node.right);

if (!shouldSkipRight && hasExcessParens(node.right)) {
if (
(rightPrecedence > prec || (rightPrecedence === prec && isExponentiation)) ||
hasDoubleExcessParens(node.right)
) {
report(node.right);
}
}
}

Expand Down Expand Up @@ -651,7 +684,7 @@ module.exports = {
return {
ArrayExpression(node) {
node.elements
.filter(e => e && hasExcessParens(e) && precedence(e) >= PRECEDENCE_OF_ASSIGNMENT_EXPR)
.filter(e => e && hasExcessParensWithConsiderPrecedence(e, PRECEDENCE_OF_ASSIGNMENT_EXPR))
.forEach(report);
},

Expand All @@ -674,18 +707,14 @@ module.exports = {
if (astUtils.isOpeningParenToken(tokenBeforeFirst) && astUtils.isOpeningBraceToken(firstBodyToken)) {
tokensToIgnore.add(firstBodyToken);
}
if (hasExcessParens(node.body) && precedence(node.body) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
if (hasExcessParensWithConsiderPrecedence(node.body, PRECEDENCE_OF_ASSIGNMENT_EXPR)) {
report(node.body);
}
}
},

AssignmentExpression(node) {
if (isReturnAssignException(node)) {
return;
}

if (hasExcessParens(node.right) && precedence(node.right) >= precedence(node)) {
if (!isReturnAssignException(node) && hasExcessParensWithConsiderPrecedence(node.right, precedence(node))) {
report(node.right);
}
},
Expand All @@ -702,25 +731,24 @@ module.exports = {

ClassBody(node) {
node.body
.filter(member => member.type === "MethodDefinition" && member.computed &&
member.key && hasExcessParens(member.key) && precedence(member.key) >= PRECEDENCE_OF_ASSIGNMENT_EXPR)
.filter(member => member.type === "MethodDefinition" && member.computed && member.key)
.filter(member => hasExcessParensWithConsiderPrecedence(member.key, PRECEDENCE_OF_ASSIGNMENT_EXPR))
.forEach(member => report(member.key));
},

ConditionalExpression(node) {
if (isReturnAssignException(node)) {
return;
}

if (hasExcessParens(node.test) && precedence(node.test) >= precedence({ type: "LogicalExpression", operator: "||" })) {
if (hasExcessParensWithConsiderPrecedence(node.test, precedence({ type: "LogicalExpression", operator: "||" }))) {
report(node.test);
}

if (hasExcessParens(node.consequent) && precedence(node.consequent) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
if (hasExcessParensWithConsiderPrecedence(node.consequent, PRECEDENCE_OF_ASSIGNMENT_EXPR)) {
report(node.consequent);
}

if (hasExcessParens(node.alternate) && precedence(node.alternate) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
if (hasExcessParensWithConsiderPrecedence(node.alternate, PRECEDENCE_OF_ASSIGNMENT_EXPR)) {
report(node.alternate);
}
},
Expand Down Expand Up @@ -911,18 +939,15 @@ module.exports = {

ObjectExpression(node) {
node.properties
.filter(property => {
const value = property.value;

return value && hasExcessParens(value) && precedence(value) >= PRECEDENCE_OF_ASSIGNMENT_EXPR;
}).forEach(property => report(property.value));
.filter(property => property.value && hasExcessParensWithConsiderPrecedence(property.value, PRECEDENCE_OF_ASSIGNMENT_EXPR))
.forEach(property => report(property.value));
},

Property(node) {
if (node.computed) {
const { key } = node;

if (key && hasExcessParens(key) && precedence(key) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
if (key && hasExcessParensWithConsiderPrecedence(key, PRECEDENCE_OF_ASSIGNMENT_EXPR)) {
report(key);
}
}
Expand Down Expand Up @@ -1024,7 +1049,7 @@ module.exports = {
AssignmentPattern(node) {
const { right } = node;

if (right && hasExcessParens(right) && precedence(right) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
if (right && hasExcessParensWithConsiderPrecedence(right, PRECEDENCE_OF_ASSIGNMENT_EXPR)) {
report(right);
}
}
Expand Down
94 changes: 93 additions & 1 deletion tests/lib/rules/no-extra-parens.js
Expand Up @@ -2035,6 +2035,98 @@ ruleTester.run("no-extra-parens", rule, {
"SequenceExpression",
1,
{ parserOptions: { ecmaVersion: 2020 } }
)
),

// https://github.com/eslint/eslint/issues/12127
{
code: "[1, ((2, 3))];",
output: "[1, (2, 3)];",
errors: [{ messageId: "unexpected" }]
},
{
code: "const foo = () => ((bar, baz));",
output: "const foo = () => (bar, baz);",
errors: [{ messageId: "unexpected" }]
},
{
code: "foo = ((bar, baz));",
output: "foo = (bar, baz);",
errors: [{ messageId: "unexpected" }]
},
{
code: "foo + ((bar + baz));",
output: "foo + (bar + baz);",
errors: [{ messageId: "unexpected" }]
},
{
code: "((foo + bar)) + baz;",
output: "(foo + bar) + baz;",
errors: [{ messageId: "unexpected" }]
},
{
code: "foo * ((bar + baz));",
output: "foo * (bar + baz);",
errors: [{ messageId: "unexpected" }]
},
{
code: "((foo + bar)) * baz;",
output: "(foo + bar) * baz;",
errors: [{ messageId: "unexpected" }]
},
{
code: "new A(((foo, bar)))",
output: "new A((foo, bar))",
errors: [{ messageId: "unexpected" }]
},
{
code: "class A{ [((foo, bar))]() {} }",
output: "class A{ [(foo, bar)]() {} }",
errors: [{ messageId: "unexpected" }]
},
{
code: "new ((A, B))()",
output: "new (A, B)()",
errors: [{ messageId: "unexpected" }]
},
{
code: "((foo, bar)) ? bar : baz;",
output: "(foo, bar) ? bar : baz;",
errors: [{ messageId: "unexpected" }]
},
{
code: "((f ? o : o)) ? bar : baz;",
output: "(f ? o : o) ? bar : baz;",
errors: [{ messageId: "unexpected" }]
},
{
code: "((f = oo)) ? bar : baz;",
output: "(f = oo) ? bar : baz;",
errors: [{ messageId: "unexpected" }]
},
{
code: "foo ? ((bar, baz)) : baz;",
output: "foo ? (bar, baz) : baz;",
errors: [{ messageId: "unexpected" }]
},
{
code: "foo ? bar : ((bar, baz));",
output: "foo ? bar : (bar, baz);",
errors: [{ messageId: "unexpected" }]
},
{
code: "function foo(bar = ((baz1, baz2))) {}",
output: "function foo(bar = (baz1, baz2)) {}",
errors: [{ messageId: "unexpected" }]
},
{
code: "var foo = { bar: ((baz1, baz2)) };",
output: "var foo = { bar: (baz1, baz2) };",
errors: [{ messageId: "unexpected" }]
},
{
code: "var foo = { [((bar1, bar2))]: baz };",
output: "var foo = { [(bar1, bar2)]: baz };",
errors: [{ messageId: "unexpected" }]
}
]
});