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

New: Fixable Disable Directives #78

Merged
merged 6 commits into from May 20, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
136 changes: 136 additions & 0 deletions designs/2021-fixable-disable-directives/design.md
@@ -0,0 +1,136 @@
- Repo: eslint/eslint
- Start Date: 2021-04-11
- RFC PR: https://github.com/eslint/rfcs/pull/78
- Authors: [Josh Goldberg](https://github.com/JoshuaKGoldberg)

# Fixable Disable Directives

## Summary

Inline disable directives such as `/* eslint-disable */` that do not suppress any violations may be detected with the `--report-unused-disable-directives` CLI option, but there is no way to automatically remove those comments.
This RFC proposes adding removal of those directives to the `--fix` CLI option as a new [`--fix-type`](https://eslint.org/docs/user-guide/command-line-interface#-fix-type): _**`directive`**_.

## Motivation

Manually deleting comments from `--report-unused-disable-directives` is cumbersome, especially in large repositories with many directives.
Users would benefit from a quick way to fix these without having to manually map between CLI output lines and files.

## Detailed Design

Recapping the existing flow of code:

- When the `--fix-type` CLI option is specified, `options.fix` is patched to filter on the `rule.meta` of each fix's corresponding `ruleId`
- Unused disable directives are calculated by an `applyDirectives` function within the `applyDisableDirectives` function called in the linter's `_verifyWithoutProcessors`
- These problems have a `ruleId` of `null`
- `_verifyWithoutProcessors` is called within the call stack of each of the 1-10 passes in the linter's `verifyAndFix`

This RFC proposes making three changes:

- In the patched `options.fix`, consider a problem's meta type to be `"directive"` if its `ruleId` is `null`
- Pass the `SourceCode` being linted as a parameter to `applyDisableDirectives`
- Add a `fix` method to the unused directive problems returned by `applyDirectives` that uses the `SourceCode`

### Fix Behavior

Directives where at least one rule is still used will have only the unused rule names removed from their source text.

Directives where all >=1 rules are unused will use the `SourceCode` to compute:

- If they are the only non-whitespace on their line, delete that line
- Otherwise, delete just the comment and any now-unnecessary surrounding whitespace

#### Fix Behavior Examples

```diff
- /* eslint-disable */
```

```diff
- // eslint-disable-next-line -- related explanation
```

```diff
- // eslint-disable-next-line unused, used
+ // eslint-disable-next-line used
```

```diff
- before /* eslint-disable-next-line -- related explanation */ after
+ before after
```

```diff
- before // eslint-disable-next-line -- related explanation
+ before
```

```diff
// before
- // eslint-disable-next-line
// after
```

Multiline block comments are already not allowed to be disable directives. [eslint/eslint#10334](https://github.com/eslint/eslint/issues/10334)

## Documentation

- [Command Line Interface > Fixing Problems](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems)'s `--fix` documentation should mention the added directives fixing and the new `--fix-type`
- [Rules > Report Unused ESLint Disable Comments](https://eslint.org/docs/user-guide/configuring/rules#report-unused-eslint-disable-comments) should link to that documentation

## Drawbacks

Like any new feature, this flag will slightly increase the complexity and maintenance costs of ESLint core.

This RFC's implementation would lock in the name for a new `--fix-type` even though we only have one concrete use case for it.
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if there other potential problems regarding directives we want to detect/fix in the future? --fix-type: directive doesn't indicate the specific directive problem, so would it be intended to encompass a variety of potential directive problems (perhaps incorporating new ones on major version bumps?)? Unused disable directives is just one potential problem, eslint-plugin-eslint-comments enumerates a host of other potential directive problems.


## Backwards Compatibility Analysis
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now all messages with fixes have a non-null rule ID. I could imagine e.g. an IDE integration assuming the rule ID is always a string when displaying or applying fixes. Our LintMessage docs never promise that typeof message.fix !== "undefined" implies typeof message.ruleId === "string", so this change is compatible with our stated API. While that means it's not a breaking change to the Node.js API, I wouldn't be surprised if it requires fixes in some integrations.


It is unlikely but not impossible that some `--fix` dependant users would rely on unused disable directives remaining in code.

Otherwise, this change is additive in behavior.

## Alternatives
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to include other potential API designs in this section, even if we don't choose to use them:

  • Using a more specific fix-type of unused-disable-directives
  • Providing a new option --fix-unused-disable-directives
  • Using a new ESLint core rule no-unused-disable-directives for detecting and fixing this problem, similar to the rule eslint-comments/no-unused-disable


- Applying directive fixes after the `verifyAndFix` passes
- Not ideal: deleting a comment could introduce new rule violations that would warrant running >=1 other pass
- Writing an external tool to apply these options
- Not ideal: the internal implementation is simple; external would have to deal with variant formatters, etc and suffer from the same introduced rule violations

## Open Questions

- Is `"directive"` a good name for the fix type?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only other name I'm thinking of is "eslint-disable", which is even more specific to this particular use if that's desirable.

- It feels like it could be too specific to be extended for other meta/configuration in the future.
- This RFC originally proposed `"meta"` but that goes too far in being overly vague.

## Help Needed

I'm looking forward to implementing this if approved! 🙌

## Frequently Asked Questions

> Will these be autofixed by default?
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the behavior when using both --fix and --fix-type directive but not --report-unused-disable-directives?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then no unused-disable-directives problems will be reported (and no fixed). :)
it's as the same as --fix-type=layout(or else) - if no layout rules enabled, no layout errors reported.

If `--fix` and `--report-unused-disable-directives` are both true, then yes.
There is no additional configuration that would need to be provided.

If `--fix` is true but `--report-unused-disable-directives` is not, there will be no behavior change from this RFC.
Unused directives would not add to reported problems and thus no new fixers would be added to them.

If `--fix` is not true but `--report-unused-disable-directives` is, there will be no observable change from this RFC for CLI clients.
Problems for unused directives will have a `fix` property attached but it will not be used without `--fix`.

> Can I opt out?
Yes.
If you are in the peculiar situation of needing to enable `--fix` and `--report-unused-disable-directives` _without_ fixing those directives _(why?)_, you can use `--fix-type` with all types except `directive`.

> How do the generated fixes compare to fixes from rules?
Problems reported by directive usage checking are joined with remaining rule violation problems in a single array.
This should allow directive fixing to seamlessly act similarly to rule fixing.

## Related Discussions

- [eslint/eslint#10334](https://github.com/eslint/eslint/issues/10334) - Report an error for eslint-disable-line comments that span multiple lines #1033)
- [eslint/eslint#9249](https://github.com/eslint/eslint/issues/9249) - Add CLI option to report unused eslint-disable directives
- [eslint/eslint#11815](https://github.com/eslint/eslint/issues/11815) - --report-unused-disable-directives should be autofixable