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

Fix false positive lint error with large number of branches #24287

Merged
merged 5 commits into from Apr 7, 2022

Conversation

scyron6
Copy link
Contributor

@scyron6 scyron6 commented Apr 6, 2022

…trc.js to use es2020.

Summary

Fixes #24279. JavaScript could not handle the large integer when there were numerous conditional statements before and after the hook. This was causing rounding errors which resulted in a conditional being evaluated incorrectly. I switched the functions to use BigInts instead and updated the eslintrc.js. If using BigInts is not acceptable, I'll be happy to try and find a different solution.

How did you test this change?

The user who created the issue linked to a repository that showed the bug. I copied that exact code and pasted into the tests file. This test now passes.
Link to repository: https://github.com/SanderRonde/eslint-hook-bug

Now when I open that file in my local instance, there is no error message for the hook.
image

When I run all tests in ESLintRulesOfHooks-test.js, everything passes.
image

Unfortunately, when I ran yarn test --prod, I got several errors that were unrelated to ESLintRulesOfHooks. I am not sure if this is normal.

@sizebot
Copy link

sizebot commented Apr 6, 2022

Comparing: af73043...8797f17

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.min.js = 131.40 kB 131.40 kB = 41.99 kB 41.99 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js = 136.45 kB 136.45 kB = 43.43 kB 43.43 kB
facebook-www/ReactDOM-prod.classic.js = 433.07 kB 433.07 kB = 79.60 kB 79.60 kB
facebook-www/ReactDOM-prod.modern.js = 418.07 kB 418.07 kB = 77.24 kB 77.24 kB
facebook-www/ReactDOMForked-prod.classic.js = 433.07 kB 433.07 kB = 79.61 kB 79.61 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
oss-experimental/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.production.min.js +0.35% 25.54 kB 25.63 kB +0.24% 8.76 kB 8.78 kB
oss-stable-semver/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.production.min.js +0.35% 25.54 kB 25.63 kB +0.24% 8.76 kB 8.78 kB
oss-stable/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.production.min.js +0.35% 25.54 kB 25.63 kB +0.24% 8.76 kB 8.78 kB

Generated by 🚫 dangerJS against 8797f17

@gaearon
Copy link
Collaborator

gaearon commented Apr 6, 2022

Fascinating.

@gaearon
Copy link
Collaborator

gaearon commented Apr 6, 2022

It seems worrying that we get to such high numbers. Does this also mean we're doing a ton of work and are slow in this case? Is this case pathological in some particular way? Can we think of any way to do less work for it?

@gaearon
Copy link
Collaborator

gaearon commented Apr 6, 2022

cc @calebmer you might find this interesting

@scyron6
Copy link
Contributor Author

scyron6 commented Apr 7, 2022

Work Context

Adding some screenshots from when I was working just to add some context. This first screenshot is before I added BigInts. The pathsFromStartToEnd and allPathsFromStartToEnd are the values that are being compared in the conditional for the error.
image

These are the values from the same code but with BigInts.
image

The code is using a recursive algorithm with time O(log n). I added a console log to the function to see the value at each step of recursion for countPathsFromStart.
image

Concluding Thoughts

I do not see any way to get around having very large integers if a user has complex code with a lot of conditionals. I have not done a deep dive into the recursive algorithm, but it seems well designed and runs in O(log n). As we have to make sure the hook is reachable from the initial segment and every final segment, I believe we will have to traverse the code somehow.

I'm wondering if there is a way to do this with simple multiplication. If there are 4 simple if else statements before a hook, then there are 2^4 pathsFromStart. We'd have to distinguish the different types of conditionals somehow, which the current recursive algorithm already takes care of.

@gaearon
Copy link
Collaborator

gaearon commented Apr 7, 2022

Can you do some kind of a stress test to see how this change affects performance for the common cases? I'm not sure if there are any perf pitfalls to using BigInts.

@scyron6
Copy link
Contributor Author

scyron6 commented Apr 7, 2022

Setup

I took the eslint-hook-bug repository and made some adjustments. I copied app/foo.tsx (the file that previously failed because the ints were too big) and copied it to fail.tsx. I then removed a couple of the conditionals from app/foo.tsx so there would be no errors when run with the current version. Finally, I copied app/foo.tsx 100 times into the app folder for the stress test.

Stress Test

I checked out the main branch and ran yarn build eslint-plugin-react-hooks to simulate the current environment.
image

Eslint comes with feature that shows you how long each run takes. The command 'TIMING=1 yarn eslint app/*' will run the rule on every file in app and give us the total time eslint took to run. I ran the command 10 times and sent the output to main_runs.txt. I then ran the same command but with fail.tsx and also sent that output to main_runs.txt just so we can see the failure.
main_runs.txt

I then switched over to the branch that with BigInts and reran yarn build.
image

I then ran the same TIMING=1 yarn eslint app/* command 10 times and sent the output to big_int_runs.txt. I then ran it with fail.tsx to watch it pass this time.
big_int_runs.txt

Concluding Thoughts

The runtimes seemed very similar to me. If you would also like me to check the memory usage difference, let me know. That might take longer though as I'd have to do a lot of research on how to do that.

@gaearon
Copy link
Collaborator

gaearon commented Apr 7, 2022

Thanks for checking. Would you mind fixing the lint issue? I think you need to find the lint config we use for builds and add BigInt to known globals.

@gaearon gaearon changed the title Switched RulesOfHooks.js to use BigInt. Added test and updated .eslin… Fix false positive lint error with large number of branches Apr 7, 2022
yarn.lock Outdated Show resolved Hide resolved
.eslintrc.js Outdated Show resolved Hide resolved
@gaearon
Copy link
Collaborator

gaearon commented Apr 7, 2022

Looks like your commits aren't attributed to your GH user (maybe different email). Would you like to fix them up so that you appear as a contributor after merge? I'm not sure how this works.

@scyron6
Copy link
Contributor Author

scyron6 commented Apr 7, 2022

You were right I had an old email in there. Don't worry about it though.

@gaearon gaearon merged commit 1f7a901 into facebook:main Apr 7, 2022
@gaearon
Copy link
Collaborator

gaearon commented Apr 7, 2022

Thanks!

@scyron6 scyron6 deleted the bug-24279-conditional-hook branch April 7, 2022 23:32
rickhanlonii pushed a commit that referenced this pull request Apr 13, 2022
* Switched RulesOfHooks.js to use BigInt. Added test and updated .eslintrc.js to use es2020.

* Added BigInt as readonly global in eslintrc.cjs.js and eslintrc.cjs2015.js

* Added comment to RulesOfHooks.js that gets rid of BigInt eslint error

* Got rid of changes in .eslintrc.js and yarn.lock

* Move global down

Co-authored-by: stephen cyron <stephen.cyron@fdmgroup.com>
Co-authored-by: dan <dan.abramov@gmail.com>
facebook-github-bot pushed a commit to facebook/react-native that referenced this pull request Apr 14, 2022
Summary:
This sync includes the following changes:
- **[8dcedba15](facebook/react@8dcedba15 )**: Add fallback shim for AbortController ([#24285](facebook/react#24285)) //<Ricky>//
- **[b86baa1cb](facebook/react@b86baa1cb )**: Add back lost cache test ([#24317](facebook/react#24317)) //<Ricky>//
- **[bafe912a5](facebook/react@bafe912a5 )**: update types for InputContinuousLane and DefaultLane ([#24316](facebook/react#24316)) //<Leo>//
- **[4ebaeae40](facebook/react@4ebaeae40 )**: moved mutation code to passive ([#24251](facebook/react#24251)) //<Luna Ruan>//
- **[caa60e8fc](facebook/react@caa60e8fc )**: update types for NonIdleLanes and IdleLane ([#24313](facebook/react#24313)) //<Leo>//
- **[1f7a901d7](facebook/react@1f7a901d7 )**: Fix false positive lint error with large number of branches  ([#24287](facebook/react#24287)) //<Stephen Cyron>//
- **[f56dfe950](facebook/react@f56dfe950 )**: Warn on setState() in useInsertionEffect() ([#24298](facebook/react#24298)) //<dan>//
- **[d68b09def](facebook/react@d68b09def )**: Fix warning about setState in useEffect ([#24295](facebook/react#24295)) //<dan>//
- **[057915477](facebook/react@057915477 )**: Update create-subscription README ([#24294](facebook/react#24294)) //<dan>//

Changelog:
[General][Changed] - React Native sync for revisions e8f4a66...8dcedba

jest_e2e[run_all_tests]

Reviewed By: kacieb

Differential Revision: D35581147

fbshipit-source-id: 33661d77eb000fdedab7e506a458fc739eab0056
rickhanlonii pushed a commit that referenced this pull request Apr 14, 2022
* Switched RulesOfHooks.js to use BigInt. Added test and updated .eslintrc.js to use es2020.

* Added BigInt as readonly global in eslintrc.cjs.js and eslintrc.cjs2015.js

* Added comment to RulesOfHooks.js that gets rid of BigInt eslint error

* Got rid of changes in .eslintrc.js and yarn.lock

* Move global down

Co-authored-by: stephen cyron <stephen.cyron@fdmgroup.com>
Co-authored-by: dan <dan.abramov@gmail.com>
zhengjitf pushed a commit to zhengjitf/react that referenced this pull request Apr 15, 2022
…#24287)

* Switched RulesOfHooks.js to use BigInt. Added test and updated .eslintrc.js to use es2020.

* Added BigInt as readonly global in eslintrc.cjs.js and eslintrc.cjs2015.js

* Added comment to RulesOfHooks.js that gets rid of BigInt eslint error

* Got rid of changes in .eslintrc.js and yarn.lock

* Move global down

Co-authored-by: stephen cyron <stephen.cyron@fdmgroup.com>
Co-authored-by: dan <dan.abramov@gmail.com>
Saadnajmi pushed a commit to Saadnajmi/react-native-macos that referenced this pull request Jan 15, 2023
Summary:
This sync includes the following changes:
- **[8dcedba15](facebook/react@8dcedba15 )**: Add fallback shim for AbortController ([facebook#24285](facebook/react#24285)) //<Ricky>//
- **[b86baa1cb](facebook/react@b86baa1cb )**: Add back lost cache test ([facebook#24317](facebook/react#24317)) //<Ricky>//
- **[bafe912a5](facebook/react@bafe912a5 )**: update types for InputContinuousLane and DefaultLane ([facebook#24316](facebook/react#24316)) //<Leo>//
- **[4ebaeae40](facebook/react@4ebaeae40 )**: moved mutation code to passive ([facebook#24251](facebook/react#24251)) //<Luna Ruan>//
- **[caa60e8fc](facebook/react@caa60e8fc )**: update types for NonIdleLanes and IdleLane ([facebook#24313](facebook/react#24313)) //<Leo>//
- **[1f7a901d7](facebook/react@1f7a901d7 )**: Fix false positive lint error with large number of branches  ([facebook#24287](facebook/react#24287)) //<Stephen Cyron>//
- **[f56dfe950](facebook/react@f56dfe950 )**: Warn on setState() in useInsertionEffect() ([facebook#24298](facebook/react#24298)) //<dan>//
- **[d68b09def](facebook/react@d68b09def )**: Fix warning about setState in useEffect ([facebook#24295](facebook/react#24295)) //<dan>//
- **[057915477](facebook/react@057915477 )**: Update create-subscription README ([facebook#24294](facebook/react#24294)) //<dan>//

Changelog:
[General][Changed] - React Native sync for revisions e8f4a66...8dcedba

jest_e2e[run_all_tests]

Reviewed By: kacieb

Differential Revision: D35581147

fbshipit-source-id: 33661d77eb000fdedab7e506a458fc739eab0056
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants