-
-
Notifications
You must be signed in to change notification settings - Fork 4.6k
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
fix: Remove no-extra-parens
autofix for potential directives
#17022
Conversation
✅ Deploy Preview for docs-eslint canceled.
|
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.
Overall LGTM. One spot where I thought we could clarify things a bit.
Can also update the documentation to note this change?
Thanks @nzakas, I've added a sentence to the Rule Details. I think there is no special way to format a note like this? |
lib/rules/no-extra-parens.js
Outdated
if (isParenthesisedTwice(node)) { | ||
return true; | ||
} | ||
return !astUtils.isExpressionInOrJustAfterDirectivePrologue(sourceCode, node.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.
I thought we'd just check if the parent is ExpressionStatement as we don't know what fixes other rules are applying in the code that precedes it.
For example:
/* eslint no-extra-parens: "error" */
/* eslint prettier/prettier: "error" */
; //
("use strict");
after --fix
, "use strict"
still ends up being a directive:
/* eslint no-extra-parens: "error" */
/* eslint prettier/prettier: "error" */
//
"use strict";
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.
Well, if compatibility with Prettier is a concern then we could just check the parent node as you suggest. I don't know how we could stop third-party rules from interfering if another autofix is applied to the ExpressionStatement
node itself or to its children rather than to a sibling node (e.g. ("use " + "strict");
→ ("use strict");
), but from what I can tell, that is not what Prettier does.
In this case we should also change the behavior of the quotes
rule, because fox example
/* eslint quotes: "error" */
/* eslint prettier/prettier: "error" */
; //
`use strict`;
autofixes to
/* eslint quotes: "error" */
/* eslint prettier/prettier: "error" */
//
"use strict";
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.
Well, if compatibility with Prettier is a concern then we could just check the parent node as you suggest.
Not specifically with Prettier, any custom or third-party rules.
I don't know how we could stop third-party rules from interfering if another autofix is applied to the
ExpressionStatement
node itself or to its children rather than to a sibling node (e.g.("use " + "strict");
→("use strict");
),
That shouldn't cause problems because the ranges will be overlapping so ESLint would apply only one of the fixes:
https://eslint.org/docs/latest/extend/custom-rules#conflicting-fixes
I think that non-directive ExpressionStatement > Literal is a very edge case, and it's a meaningless code that should be manually fixed anyway, so I'd vote for the safest solution which is also the simplest for us. @nzakas what do you think?
/* eslint quotes: "error" */ /* eslint prettier/prettier: "error" */ ; // `use strict`;
Yes, I don't think this should be autofixed either.
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.
That shouldn't cause problems because the ranges will be overlapping so ESLint would apply only one of the fixes:
https://eslint.org/docs/latest/extend/custom-rules#conflicting-fixes
Ah, understood. I didn't realize that removing the parentheses is handled as one single fix by the linter. Thanks for the explanation @mdjermanovic.
docs/src/rules/no-extra-parens.md
Outdated
@@ -21,6 +21,8 @@ This rule always ignores extra parentheses around the following: | |||
* immediately-invoked function expressions (also known as IIFEs) such as `var x = (function () {})();` and `var x = (function () {}());` to avoid conflicts with the [wrap-iife](wrap-iife) rule | |||
* arrow function arguments to avoid conflicts with the [arrow-parens](arrow-parens) rule | |||
|
|||
Problems reported by this rule can be fixed automatically, except when removing the parentheses would create a new directive. |
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.
I was thinking more that we need to show some examples, as I don't know that most people will understand what "removing the parentheses would create a new directive" actually means.
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.
Thanks for the feedback @nzakas! Would it be also fine for you if we disable the autofix for all string literals that are direct children of an ExpressionStatement
, as suggested in this comment by @mdjermanovic? This would improve interoperability with third-party rules at the expenses of a few autofixes in some rare edge cases.
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.
Yup, that's fine with me. We don't guarantee that all autofixes will be applied, so it's fine to pick and choose.
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. |
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. |
Don't close. Not stale.
@nzakas there's a reply from @fasttime for you to look into here. Thanks. |
Thanks, replied! |
lib/rules/no-extra-parens.js
Outdated
if (isParenthesisedTwice(node)) { | ||
return true; | ||
} | ||
return node.parent.type !== "ExpressionStatement"; |
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.
Shall we also check if the ExpressionStatement
is at the top level of a program or function body?
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.
I think yes, if it isn't at the top level of a program or function body then the fix seems safe.
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.
I think yes, if it isn't at the top level of a program or function body then the fix seems safe.
Thanks! This is done in 7a2800d. I also took the time to refactor similar code in other rules that do the same kind of checks.
Thanks for the suggestions @mdjermanovic @nzakas. I've updated the autofix logic as per discussion above and I've added an example to the rule docs that shows the effect of the disabled autofix. I've also reverted my changes in |
lib/rules/quotes.js
Outdated
@@ -212,7 +213,7 @@ module.exports = { | |||
|
|||
// Directive Prologues. | |||
case "ExpressionStatement": | |||
return isPartOfDirectivePrologue(node); | |||
return !astUtils.isParenthesised(sourceCode, node) && isExpressionInOrJustAfterDirectivePrologue(node); |
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 checking for parentheses can produce more lint errors in existing code:
/* eslint quotes: ["error", "backtick"] */
("use strict") // error after this change
So we should tag this as feat:
per our semver polices. However, since the PR is about no-extra-parens, and this change isn't just refactoring, it might be better to extract all changes to the quotes rule into a separate PR?
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.
However, since the PR is about no-extra-parens, and this change isn't just refactoring, it might be better to extract all changes to the quotes rule into a separate PR?
Agreed. These fixes in the quotes
rule were not expected from my side, they came more as a side effect of refactoring. I'm going to revert these changes for now and I will submit a new PR to update quotes
once the changes in ast-utils.js
are merged.
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.
Done in 9d94ba4.
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.
LGTM, thanks! Leaving it open for @nzakas to verify.
@fasttime this should also be rebased because it includes a change in the docs. |
Co-authored-by: Nicholas C. Zakas <nicholas@humanwhocodes.com>
Co-authored-by: Nicholas C. Zakas <nicholas@humanwhocodes.com>
9d94ba4
to
74004b9
Compare
|
Rebased on top of main. I have no idea why the EasyCLA check is failing. |
/easycla |
|
I think the bug in CLA is when a commit is co-authored. Maybe try amending the commit to remove the co-author? |
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.
LGTM, thanks!
Thanks @snitin315, I hadn't noticed that there is a problem with the EasyCLA bot. Now that you mention it, since this is not the only affected PR, maybe we should just wait until the issue is fixed? |
/easycla |
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.
LGTM. Thanks!
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [eslint](https://eslint.org) ([source](https://github.com/eslint/eslint)) | devDependencies | minor | [`8.42.0` -> `8.43.0`](https://renovatebot.com/diffs/npm/eslint/8.42.0/8.43.0) | --- ### Release Notes <details> <summary>eslint/eslint (eslint)</summary> ### [`v8.43.0`](https://github.com/eslint/eslint/releases/tag/v8.43.0) [Compare Source](eslint/eslint@v8.42.0...v8.43.0) #### Features - [`14581ff`](eslint/eslint@14581ff) feat: directive prologue detection and autofix condition in `quotes` ([#​17284](eslint/eslint#17284)) (Francesco Trotta) - [`e50fac3`](eslint/eslint@e50fac3) feat: add declaration loc to message in block-scoped-var ([#​17252](eslint/eslint#17252)) (Milos Djermanovic) - [`1b7faf0`](eslint/eslint@1b7faf0) feat: add `skipJSXText` option to `no-irregular-whitespace` rule ([#​17182](eslint/eslint#17182)) (Azat S) #### Bug Fixes - [`5338b56`](eslint/eslint@5338b56) fix: normalize `cwd` passed to `ESLint`/`FlatESLint` constructor ([#​17277](eslint/eslint#17277)) (Milos Djermanovic) - [`54383e6`](eslint/eslint@54383e6) fix: Remove `no-extra-parens` autofix for potential directives ([#​17022](eslint/eslint#17022)) (Francesco Trotta) #### Documentation - [`8b855ea`](eslint/eslint@8b855ea) docs: resubmit pr17061 doc changes ([#​17292](eslint/eslint#17292)) (唯然) - [`372722e`](eslint/eslint@372722e) docs: resubmit pr17012 doc changes ([#​17293](eslint/eslint#17293)) (唯然) - [`67e7af3`](eslint/eslint@67e7af3) docs: resubmit custom-rules doc changes ([#​17294](eslint/eslint#17294)) (唯然) - [`9e3d77c`](eslint/eslint@9e3d77c) docs: Resubmit Fix formatting in Custom Rules docs ([#​17281](eslint/eslint#17281)) (Milos Djermanovic) - [`503647a`](eslint/eslint@503647a) docs: Resubmit markVariableAsUsed docs ([#​17280](eslint/eslint#17280)) (Nicholas C. Zakas) - [`e0cf0d8`](eslint/eslint@e0cf0d8) docs: Custom rule & plugin tutorial ([#​170...
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:
References #16988
What changes did you make? (Give an overview)
This PR fixes an issue with the
no-extra-parens
rule that could cause the autofix to change the behavior of the code. The solution consists in disabling the autofix in situations where it would otherwise introduce a new directive.This solution has been discussed in issue #16988, and I started working on the
no-extra-parens
because I think that there is sufficient consensus on how to handle the autofix in the case of this specific rule.The problem addressed here is that a statement like
("use strict");
can mutate into ause strict
directive if the parentheses are removed, and that would change the semantics of the code. To determine whether the autofix should be disabled, we decided to check if a string literal is the sole child of anExpressionStatement
.Since there is no danger that the
no-extra-parens
andquotes
rule fix the same node concurrently (see #17022 (comment)), it is safe forno-extra-parens
to remove parentheses from a template literal.Is there anything you'd like reviewers to focus on?