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

feat(eslint-plugin): [strict-enums] new rule #4864

Closed
wants to merge 74 commits into from

Conversation

Zamiell
Copy link
Contributor

@Zamiell Zamiell commented Apr 26, 2022

PR Checklist

Overview

Makes working with enums not suck. See the docs that I wrote for the rule.

This PR is a draft; I am still working on it but I will start a PR for now in case anyone wants to give me early feedback.

@nx-cloud
Copy link

nx-cloud bot commented Apr 26, 2022

☁️ Nx Cloud Report

CI is running/has finished running commands for commit 871e9ca. As they complete they will appear below. Click to see the status, the terminal output, and the build insights.

📂 See all runs for this branch


✅ Successfully ran 43 targets

Sent with 💌 from NxCloud.

@typescript-eslint
Copy link
Contributor

Thanks for the PR, @Zamiell!

typescript-eslint is a 100% community driven project, and we are incredibly grateful that you are contributing to that community.

The core maintainers work on this in their personal time, so please understand that it may not be possible for them to review your work immediately.

Thanks again!


🙏 Please, if you or your company is finding typescript-eslint valuable, help us sustain the project by sponsoring it transparently on https://opencollective.com/typescript-eslint. As a thank you, your profile/company logo will be added to our main README which receives thousands of unique visitors per day.

@netlify
Copy link

netlify bot commented Apr 26, 2022

Deploy Preview for typescript-eslint ready!

Name Link
🔨 Latest commit 871e9ca
🔍 Latest deploy log https://app.netlify.com/sites/typescript-eslint/deploys/6296c3849d15340008441833
😎 Deploy Preview https://deploy-preview-4864--typescript-eslint.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

@codecov
Copy link

codecov bot commented Apr 26, 2022

Codecov Report

Merging #4864 (a50f6e1) into main (7275977) will decrease coverage by 2.72%.
The diff coverage is 81.85%.

❗ Current head a50f6e1 differs from pull request most recent head 871e9ca. Consider uploading reports for the commit 871e9ca to get more accurate results

@@            Coverage Diff             @@
##             main    #4864      +/-   ##
==========================================
- Coverage   94.25%   91.53%   -2.73%     
==========================================
  Files         153      228      +75     
  Lines        8305    10856    +2551     
  Branches     2702     3351     +649     
==========================================
+ Hits         7828     9937    +2109     
- Misses        263      637     +374     
- Partials      214      282      +68     
Flag Coverage Δ
unittest 91.53% <81.85%> (-2.73%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
packages/eslint-plugin/src/configs/all.ts 100.00% <ø> (ø)
...ackages/eslint-plugin/src/rules/no-implied-eval.ts 97.01% <ø> (-0.05%) ⬇️
packages/eslint-plugin/src/rules/return-await.ts 99.05% <ø> (ø)
packages/eslint-plugin/src/rules/unbound-method.ts 92.13% <0.00%> (ø)
packages/eslint-plugin/src/util/misc.ts 93.75% <ø> (-0.25%) ⬇️
packages/type-utils/src/getEnum.ts 14.28% <14.28%> (ø)
...s/eslint-plugin/src/util/collectUnusedVariables.ts 93.16% <50.00%> (ø)
packages/type-utils/src/typeFlagUtils.ts 65.38% <55.55%> (ø)
packages/eslint-plugin/src/rules/strict-enums.ts 84.43% <84.43%> (ø)
...t-plugin/src/rules/no-confusing-void-expression.ts 98.83% <100.00%> (-0.02%) ⬇️
... and 86 more

@Zamiell
Copy link
Contributor Author

Zamiell commented Apr 26, 2022

No idea why linting is failing in CI. It passes locally when I do npm run lint.

@Zamiell Zamiell force-pushed the strict-enums branch 7 times, most recently from 13de4e4 to b12c2c2 Compare April 27, 2022 16:26
@Zamiell Zamiell marked this pull request as ready for review April 27, 2022 16:27
@Zamiell Zamiell force-pushed the strict-enums branch 4 times, most recently from cf8a868 to 7484e80 Compare April 27, 2022 16:56
@Zamiell
Copy link
Contributor Author

Zamiell commented Apr 28, 2022

I also found a bug with the typeChecker.getTypeAtLocation method while writing this PR.
If you pass it ts.VariableDeclaration instead of ts.BindingName, it will cause a run-time error.

Which means that the function signature of this method needs to be updated in the TypeScript repository.
This is over my pay grade to fix, but someone should look into this.

@bradzacher bradzacher removed the awaiting response Issues waiting for a reply from the OP or another party label May 16, 2022
Copy link
Member

@bradzacher bradzacher left a comment

Choose a reason for hiding this comment

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

This is looking really good to me as it currently stands.

HOWEVER, I do see a large DevX problem in terms of "flags" enums. Now, flags enums are known to be unsafe.
Personally, I think in JS you should be using a Set or array to store the a list of possible values, but I get why one might use a "flags" enum.:

  • they're often used is for memory efficiency.
    • for example - this is why TS went with a "flags" enum for its fields - saves a lot of memory across an entire project's ASTs.
  • some would argue that there's a nice DevX to using bitwise-ops on flag enums.
    • personally I think that they're a bit confusing to think about, though I can understand how they're familiar for someone comfortable with lower-level languages.

The point is though, this may be something we need to solve for in terms of devx.
Right now the answer is "too bad, so sad - union in number", but I don't think this is a good answer for those users because it destroys the type documentation for the usecases, for example:
image
image
image

Whilst I understand that in these cases it is "technically correct" for the type to be number, it's also not truly correct as it suggests things like "it's valid to pass 100 to this function", when it's not - because 100 satisfies neither Foo.A, nor Foo.B.

So with all that said, I think we need to determine a means for people to "opt out" of the rule for flags enums.

Perhaps it's something as simple as adding an option with an "allow flags naming pattern" similar to the no-unused-vars rule's varsIgnorePattern option:

{
  "@typescript-eslint/strict-enums": [
    "error",
    // treats any enum with name ending in "Flags" as a flags enum
    {"flagsEnumPattern": "Flags^"},
  ],
}

This way users can have the rule on for strict usecase, and opt-out (with a clear naming pattern) known "unsafe" usecases.

I know we'd certainly find value within this very codebase thinks to the TS flag enums!

WDYT?

@@ -65,7 +65,7 @@ class Reference {
/**
* In some cases, a reference may be a type, value or both a type and value reference.
*/
readonly #referenceType: ReferenceTypeFlag;
readonly #referenceType: number | ReferenceTypeFlag;
Copy link
Member

Choose a reason for hiding this comment

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

instead of unioning in number here, could we define TypeOrNumber as an enum member, and remove the bitwise operations on the enum?

*/
export function getTypeFlags(type: ts.Type): ts.TypeFlags {
let flags: ts.TypeFlags = 0;
export function getTypeFlags(type: ts.Type): number | ts.TypeFlags {
Copy link
Member

Choose a reason for hiding this comment

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

@JoshuaKGoldberg the point @Zamiell was making was that the code is written like this:

getTypeFlags(flag.one | flag.two | ...);

when could be written like this:

getTypeFlags(new Set([flag.one, flag.two, ...]);

packages/eslint-plugin/src/rules/strict-enums.ts Outdated Show resolved Hide resolved
Comment on lines 762 to 763
VariableDeclaration(node): void {
for (const declaration of node.declarations) {
Copy link
Member

Choose a reason for hiding this comment

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

nitpick - we don't care about the declaration itself, just the declarators.

Suggested change
VariableDeclaration(node): void {
for (const declaration of node.declarations) {
VariableDeclarator(node): void {

This is good because it saves you manually iterating and it means your report will be cleaner.

I.e. instead of:

const x = 1,
^^^^^^^^^^^^
      y: Foo = 99;
^^^^^^^^^^^^^^^^^

(where ^ is the report underline)

you'll instead have:

const x = 1,
      y: Foo = 99;
      ^^^^^^^^^^^

Which is clearer and more accurate!

packages/eslint-plugin/src/rules/strict-enums.ts Outdated Show resolved Hide resolved
packages/eslint-plugin/src/rules/strict-enums.ts Outdated Show resolved Hide resolved
Zamiell and others added 3 commits May 30, 2022 03:41
Co-authored-by: Brad Zacher <brad.zacher@gmail.com>
Co-authored-by: Brad Zacher <brad.zacher@gmail.com>
Co-authored-by: Brad Zacher <brad.zacher@gmail.com>
@bradzacher bradzacher added the awaiting response Issues waiting for a reply from the OP or another party label May 31, 2022
@Zamiell
Copy link
Contributor Author

Zamiell commented May 31, 2022

There are branch conflicts in thre README.md files.

  1. Why are there 2 duplicated README.md files? That doesn't make any sense.
  2. Is there a script to generate the README.md tables?

@Zamiell
Copy link
Contributor Author

Zamiell commented Jun 1, 2022

Also, after Brad's updates, I get the following errors when building:

james@1qaz MINGW64 /d/Repositories/typescript-eslint/packages/eslint-plugin (strict-enums)
$ yarn build
yarn run v1.22.19
$ tsc -b tsconfig.build.json
../types/src/ts-estree.ts:1:27 - error TS2307: Cannot find module './generated
/ast-spec' or its corresponding type declarations.

1 import * as TSESTree from './generated/ast-spec';
                            ~~~~~~~~~~~~~~~~~~~~~~

../types/src/ts-estree.ts:4:16 - error TS2664: Invalid module name in augmenta
tion, module './generated/ast-spec' cannot be found.

4 declare module './generated/ast-spec' {
                 ~~~~~~~~~~~~~~~~~~~~~~

../types/src/ts-estree.ts:21:27 - error TS2307: Cannot find module './generate
d/ast-spec' or its corresponding type declarations.

21 export * as TSESTree from './generated/ast-spec';
                             ~~~~~~~~~~~~~~~~~~~~~~

../types/src/index.ts:1:49 - error TS2307: Cannot find module './generated/ast
-spec' or its corresponding type declarations.

1 export { AST_NODE_TYPES, AST_TOKEN_TYPES } from './generated/ast-spec';
                                                  ~~~~~~~~~~~~~~~~~~~~~~

../visitor-keys/src/get-keys.ts:1:26 - error TS2307: Cannot find module '@type
script-eslint/types' or its corresponding type declarations.

1 import { TSESTree } from '@typescript-eslint/types';
                           ~~~~~~~~~~~~~~~~~~~~~~~~~~

../visitor-keys/src/visitor-keys.ts:1:42 - error TS2307: Cannot find module '@
typescript-eslint/types' or its corresponding type declarations.

1 import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/types';
                                           ~~~~~~~~~~~~~~~~~~~~~~~~~~

../visitor-keys/src/visitor-keys.ts:152:62 - error TS2345: Argument of type 'A
dditionalKeys' is not assignable to parameter of type 'VisitorKeys'.
  'string' index signatures are incompatible.
    Type 'readonly GetNodeTypeKeys<T>[] | undefined' is not assignable to type
 'readonly string[]'.
      Type 'undefined' is not assignable to type 'readonly string[]'.

152 const visitorKeys: VisitorKeys = eslintVisitorKeys.unionWith(additionalKey
s);
                                                                 ~~~~~~~~~~~~~
~


Found 7 errors.

Anyone know why that is? I'm unable to work on the branch until that is resolved.

@bradzacher bradzacher removed the awaiting response Issues waiting for a reply from the OP or another party label Jun 13, 2022
Copy link
Member

@bradzacher bradzacher left a comment

Choose a reason for hiding this comment

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

I think this is looking good so far.
I'm happy if we want to get this in and out to people to play with.

Fix the merge conflicts and we can get to it!

Comment on lines +27 to +34
## Banned Patterns

This rule bans:

1. Enum incrementing/decrementing - `incorrectIncrement`
1. Mismatched enum declarations/assignments - `mismatchedAssignment`
1. Mismatched enum comparisons - `mismatchedComparison`
1. Mismatched enum function arguments - `mismatchedFunctionArgument`
Copy link
Member

Choose a reason for hiding this comment

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

this is just a less-verbose duplicate of "Error Information" below - so let's remove this.

@bradzacher bradzacher added the awaiting response Issues waiting for a reply from the OP or another party label Jul 18, 2022
@Zamiell
Copy link
Contributor Author

Zamiell commented Jul 19, 2022

Brad can you respond to my messages on May 31st?

@bradzacher
Copy link
Member

bradzacher commented Aug 7, 2022

@Zamiell - both packages/eslint-plugin/README.md and packages/eslint-plugin/docs/rules/README.md no longer have rules table in them.

At the time the "canonical" table was the former (root readme), and the latter was added as part of the website build.

However the rules table is now no longer generated in source files - instead they are generated as part of the website build.

@JoshuaKGoldberg
Copy link
Member

👋 @Zamiell, just checking in - do you still have time to work on this? And if so, are there still open questions you're waiting on answers to?

@JoshuaKGoldberg
Copy link
Member

Closing this PR as it's been stale for a few months without activity. Feel free to reopen @Zamiell if you have time - but no worries if not! If anybody wants to drive it forward, please do post your own PR - and if you use this as a start, consider adding Co-authored-by: Zamiell at the end of your PR description. Thanks! 😊

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Nov 24, 2022
@JoshuaKGoldberg
Copy link
Member

Moving to #6091. I want these checks to exist 😄

@Zamiell Zamiell deleted the strict-enums branch September 17, 2023 13:30
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
awaiting response Issues waiting for a reply from the OP or another party enhancement: new plugin rule New rule request for eslint-plugin
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants