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
feat: check more expressions in no-extra-boolean-cast
#18222
base: main
Are you sure you want to change the base?
feat: check more expressions in no-extra-boolean-cast
#18222
Conversation
Hi @kirkwaiblinger!, thanks for the Pull Request The pull request title isn't properly formatted. We ask that you update the pull request title to match this format, as we use it to generate changelogs and automate releases.
To Fix: You can fix this problem by clicking 'Edit' next to the pull request title at the top of this page. Read more about contributing to ESLint here |
✅ Deploy Preview for docs-eslint ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
Hi @kirkwaiblinger!, thanks for the Pull Request The pull request title isn't properly formatted. We ask that you update the pull request title to match this format, as we use it to generate changelogs and automate releases.
To Fix: You can fix this problem by clicking 'Edit' next to the pull request title at the top of this page. Read more about contributing to ESLint here |
2 similar comments
Hi @kirkwaiblinger!, thanks for the Pull Request The pull request title isn't properly formatted. We ask that you update the pull request title to match this format, as we use it to generate changelogs and automate releases.
To Fix: You can fix this problem by clicking 'Edit' next to the pull request title at the top of this page. Read more about contributing to ESLint here |
Hi @kirkwaiblinger!, thanks for the Pull Request The pull request title isn't properly formatted. We ask that you update the pull request title to match this format, as we use it to generate changelogs and automate releases.
To Fix: You can fix this problem by clicking 'Edit' next to the pull request title at the top of this page. Read more about contributing to ESLint here |
Thanks for the PR, can you also add some examples in docs too? |
lib/rules/no-extra-boolean-cast.js
Outdated
case "ConditionalExpression": | ||
return precedence(node) <= precedence(parent); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was assuming that previousNode
is parent.test
(as in the description of this function on line 167, which we should also update), but now it can be consequent
/alternate
, in which case only SequenceExpression
would need parentheses.
For example:
/* eslint no-extra-boolean-cast: 2 */
if (a ? Boolean(b = c) : Boolean(d = e));
After the autofix:
/* eslint no-extra-boolean-cast: 2 */
if (a ? (b = c) : (d = e));
Parentheses around b = c
and d = e
are unnecessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one took me a while to figure out trying to make use of the existing infrastructure.
I would have expected to be able to write
if (previousNode === parent.test) {
return precedence(node) <= precedence(parent);
}
if (previousNode === parent.consequent || previousNode === parent.alternate) {
return precedence(node) < precedence(parent);
}
, which essentially just handles the right-associativity, but it seems that eslint's precedence() function differs from MDN's table, in that it treats AssignmentExpressions and similar as lower precedence than ternary, rather than equal... See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_precedence#table, vs
eslint/lib/rules/utils/ast-utils.js
Lines 1509 to 1602 in 239a7e2
getPrecedence(node) { | |
switch (node.type) { | |
case "SequenceExpression": | |
return 0; | |
case "AssignmentExpression": | |
case "ArrowFunctionExpression": | |
case "YieldExpression": | |
return 1; | |
case "ConditionalExpression": | |
return 3; | |
case "LogicalExpression": | |
switch (node.operator) { | |
case "||": | |
case "??": | |
return 4; | |
case "&&": | |
return 5; | |
// no default | |
} | |
/* falls through */ | |
case "BinaryExpression": | |
switch (node.operator) { | |
case "|": | |
return 6; | |
case "^": | |
return 7; | |
case "&": | |
return 8; | |
case "==": | |
case "!=": | |
case "===": | |
case "!==": | |
return 9; | |
case "<": | |
case "<=": | |
case ">": | |
case ">=": | |
case "in": | |
case "instanceof": | |
return 10; | |
case "<<": | |
case ">>": | |
case ">>>": | |
return 11; | |
case "+": | |
case "-": | |
return 12; | |
case "*": | |
case "/": | |
case "%": | |
return 13; | |
case "**": | |
return 15; | |
// no default | |
} | |
/* falls through */ | |
case "UnaryExpression": | |
case "AwaitExpression": | |
return 16; | |
case "UpdateExpression": | |
return 17; | |
case "CallExpression": | |
case "ChainExpression": | |
case "ImportExpression": | |
return 18; | |
case "NewExpression": | |
return 19; | |
default: | |
if (node.type in eslintVisitorKeys) { | |
return 20; | |
} | |
/* | |
* if the node is not a standard node that we know about, then assume it has the lowest precedence | |
* this will mean that rules will wrap unknown nodes in parentheses where applicable instead of | |
* unwrapping them and potentially changing the meaning of the code or introducing a syntax error. | |
*/ | |
return -1; | |
} | |
}, |
Think that there should be a followup to change to match the MDN version, or is it better to just leave the current system?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can consider changing this in a followup if the current logic is confusing and the new one would be better for practical use, but we'll need to check all rules that use getPrecedence()
to see if they would need to be updated.
@@ -61,6 +61,19 @@ do { | |||
for (; !!foo; ) { | |||
// ... | |||
} | |||
|
|||
// Complex expressions are also checked for their resulting expression(s). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Tanujkanti4441 Added some examples 👍
no-extra-boolean-cast
Looks good to me, but I'm not sure if we have a consensus on this change. Per the discussion in #18186, checking sequence expressions is accepted as a bug fix, but this also adds checking operands of I think it makes sense to check all these cases, but it looks unusual that checking operands of |
Agreed. Lmk what a good course of action is. I can easily remove parts of the PR deemed out of scope (and, if requested, re-add them if a separate issue for them is accepted), or I can wait and watch for further discussion on whether to proceed with this PR as-is. I am unopinionated; making the code changes is easier than consensus-building :) |
I think we can accept if ((a, b, c ?? (d, e, f ?? Boolean(g)))) {}
if ((a, b, c ?? (d, e, f ?? Boolean(g)))) {} then somehow it's a case of sequence expression and can be reported on |
Just to be clear, is there a specific action being requested here? |
Not yet! it's just an opinion on reporting |
@eslint/eslint-team thoughts about #18222 (comment)? |
I agree, this is a nice addition, but it does change this PR from a bug fix to a feature. I also agree that it doesn't make sense to check |
Hi everyone, it looks like we lost track of this pull request. Please review and see what the next steps are. This pull request will auto-close in 7 days without an update. |
ping @eslint/eslint-team. What are the next steps here? |
Sorry for the late reply. I agree that it makes sense to check the operands of |
@mdjermanovic thoughts on the feedback? |
Looks like we all agree that it makes sense to check all these cases, and the only question is how the new checks would be enabled. Since this is a stylistic rule, I don't think we can treat these as false negatives and thus enable the checks by default. I'd argue that the same applies to comma operands as well.
How about deprecating |
It looks like we are in agreement to take this approach. 👍 |
I like this resolution! As a suggestion, does anyone like the name |
Fixes #18186
Prerequisites checklist
What is the purpose of this pull request? (put an "X" next to an item)
[ ] Documentation update
[x] Bug fix (template)
[ ] New rule (template)
[ ] Changes an existing rule (template)
[ ] Add autofix to a rule
[ ] Add a CLI option
[ ] Add something to the core
[ ] Other, please explain:
fixes #18186
What changes did you make? (Give an overview)
Add support for checking "sequence expressions" ("comma operator") and right-hand-side operands of the nullish coalescing operator (??
).Note that neither is affected by the "enforceForLogicalOperands" option. The sequence expressions because it simply isn't one, and the??
because it is not one either in this context. (Yes, technically, the AST calls it a logical expression, but it's known to users as the nullish coalescing operator, and it really has nothing to do with logical operations at all, in the sense in which this rule applies to&&
and||
.)UPDATE - In addition to the cases mentioned in the issue (commas and??
), I was reminded while working on this (as a result of other linter work I've done) that checking the branches of ternaries applies yet again for the same reasons. I've updated the PR with that change, but can easily revert it if it's deemed an overstep. (see the following examples for further examples of the same recursion pattern applying to commas, ternaries, optional chains, and "logical expressions": https://github.com/typescript-eslint/typescript-eslint/blob/59bbf417702b53fe78d8a190da7054bc6c22b780/packages/eslint-plugin/src/rules/no-floating-promises.ts#L221-L324 or https://github.com/typescript-eslint/typescript-eslint/blob/59bbf417702b53fe78d8a190da7054bc6c22b780/packages/eslint-plugin/src/rules/prefer-find.ts#L44-L114)UPDATE 2 - After significant discussion in the PR, the approach is now to preserve existing behavior, deprecate
enforceForLogicalExpressions
, and create a new option (tentativelyenforceForInnerOperands
) that checks all of the recursive syntaxes:&&
,||
, RHS of??
, right-most expression of sequence expressions(a, b, c)
, and ternary consequent and alternate (test ? consequent : alternate
)