Skip to content

Commit

Permalink
fix: logical-assignment-operators rule to report logical expressions …
Browse files Browse the repository at this point in the history
…with 3 operands
  • Loading branch information
ota-meshi committed Sep 23, 2023
1 parent 299bfae commit e76bcec
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 3 deletions.
34 changes: 31 additions & 3 deletions lib/rules/logical-assignment-operators.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,31 @@ function isInsideWithBlock(node) {
return node.parent.type === "WithStatement" && node.parent.body === node ? true : isInsideWithBlock(node.parent);
}

/**
* Gets the leftmost operand of a consecutive logical expression.
* @param {SourceCode} sourceCode The ESLint source code object
* @param {LogicalExpression} node LogicalExpression
* @returns {Expression} Leftmost operand
*/
function getLeftmostOperand(sourceCode, node) {
let left = node.left;

while (left.type === "LogicalExpression" && left.operator === node.operator) {

if (astUtils.isParenthesised(sourceCode, left)) {

/*
* It should have associativity,
* but ignore it if use parentheses to make the evaluation order clear.
*/
return left;
}
left = left.left;
}
return left;

}

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
Expand Down Expand Up @@ -318,7 +343,10 @@ module.exports = {

// foo = foo || bar
"AssignmentExpression[operator='='][right.type='LogicalExpression']"(assignment) {
if (!astUtils.isSameReference(assignment.left, assignment.right.left)) {
const leftOperand = getLeftmostOperand(sourceCode, assignment.right);

if (!astUtils.isSameReference(assignment.left, leftOperand)
) {
return;
}

Expand All @@ -342,10 +370,10 @@ module.exports = {
yield ruleFixer.insertTextBefore(assignmentOperatorToken, assignment.right.operator);

// -> foo ||= bar
const logicalOperatorToken = getOperatorToken(assignment.right);
const logicalOperatorToken = getOperatorToken(leftOperand.parent);
const firstRightOperandToken = sourceCode.getTokenAfter(logicalOperatorToken);

yield ruleFixer.removeRange([assignment.right.range[0], firstRightOperandToken.range[0]]);
yield ruleFixer.removeRange([leftOperand.parent.range[0], firstRightOperandToken.range[0]]);
}
};

Expand Down
97 changes: 97 additions & 0 deletions tests/lib/rules/logical-assignment-operators.js
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,24 @@ ruleTester.run("logical-assignment-operators", rule, {
}, {
code: "a.b = a.b || c",
options: ["never"]
},

// 3 or more operands
{
code: "a = a && b || c",
options: ["always"]
},
{
code: "a = a && b && c || d",
options: ["always"]
},
{
code: "a = (a || b) || c", // Ignore parentheses if used.
options: ["always"]
},
{
code: "a = (a && b) && c", // Ignore parentheses if used.
options: ["always"]
}
],
invalid: [
Expand Down Expand Up @@ -1511,6 +1529,85 @@ ruleTester.run("logical-assignment-operators", rule, {
output: "(a.b.c ||= d) as number"
}]
}]
},

// 3 or more operands
{
code: "a = a || b || c",
output: "a ||= b || c",
options: ["always"],
errors: [{
messageId: "assignment",
type: "AssignmentExpression",
data: { operator: "||=" },
suggestions: []
}]
},
{
code: "a = a && b && c",
output: "a &&= b && c",
options: ["always"],
errors: [{
messageId: "assignment",
type: "AssignmentExpression",
data: { operator: "&&=" },
suggestions: []
}]
},
{
code: "a = a || b && c",
output: "a ||= b && c",
options: ["always"],
errors: [{
messageId: "assignment",
type: "AssignmentExpression",
data: { operator: "||=" },
suggestions: []
}]
},
{
code: "a = a || b || c || d",
output: "a ||= b || c || d",
options: ["always"],
errors: [{
messageId: "assignment",
type: "AssignmentExpression",
data: { operator: "||=" },
suggestions: []
}]
},
{
code: "a = a && b && c && d",
output: "a &&= b && c && d",
options: ["always"],
errors: [{
messageId: "assignment",
type: "AssignmentExpression",
data: { operator: "&&=" },
suggestions: []
}]
},
{
code: "a = a || b || c && d",
output: "a ||= b || c && d",
options: ["always"],
errors: [{
messageId: "assignment",
type: "AssignmentExpression",
data: { operator: "||=" },
suggestions: []
}]
},
{
code: "a = a || b && c || d",
output: "a ||= b && c || d",
options: ["always"],
errors: [{
messageId: "assignment",
type: "AssignmentExpression",
data: { operator: "||=" },
suggestions: []
}]
}
]
});

0 comments on commit e76bcec

Please sign in to comment.