eslint: require-atomic-updates false positive
Tell us about your environment
- ESLint Version: 6.0.1
- Node Version: 12.4.0
- npm Version: 6.9.0
What parser (default, Babel-ESLint, etc.) are you using?
Default
Please show your full configuration:
https://github.com/medikoo/eslint-config-medikoo/blob/master/index.js
What did you do? Please include the actual source code causing the issue, as well as the command that you used to run ESLint.
const fn = async someObj => {
let { foo } = someObj;
await Promise.resolve();
// Possible race condition: `someObj.bar` might be reassigned based on an outdated value of `someObj.bar`
someObj.bar = "whatever";
};
What did you expect to happen?
No error reported
What actually happened? Please include the actual, raw output from ESLint.
Error reported:
Possible race condition: `someObj.bar` might be reassigned based on an outdated value of
Are you willing to submit a pull request to fix this bug?
Not at this point
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 127
- Comments: 57 (11 by maintainers)
Commits related to this issue
- build: update eslintrc see: https://github.com/eslint/eslint/issues/11899 didn't figure out what happened — committed to sb-im/sdwc by rocka 5 years ago
- fix: Turn off 'require-atomic-updates' due to false positives More info: https://github.com/eslint/eslint/issues/11899 — committed to serverless/eslint-config by medikoo 5 years ago
- fix: disable bogus require-atomic-updates rule Refs: https://github.com/eslint/eslint/issues/11899 — committed to cheminfo/eslint-config by targos 5 years ago
- bump prettier and eslint (temporary?) disabling `require-atomic-updates` because of [false positives][1] [1]: https://github.com/eslint/eslint/issues/11899 — committed to zemlanin/scroll by zemlanin 5 years ago
- Add linting exception for session assignment I don't really see how this is a race condition. It seems like lots of false positives are flagged by this rule. - https://github.com/eslint/eslint/issues... — committed to cds-snc/cra-claim-tax-benefits by pcraig3 5 years ago
- Updated members-ssr middleware to async functions no-issue Also updates to use Object.assign rather than req.member = value to get around false positives from eslint: * https://github.com/eslint/... — committed to allouis/Ghost by allouis 5 years ago
- Updated members-ssr middleware to async functions no-issue Also updates to use Object.assign rather than req.member = value to get around false positives from eslint: * https://github.com/eslint/... — committed to TryGhost/Ghost by allouis 5 years ago
- Updated members-ssr middleware to async functions no-issue Also updates to use Object.assign rather than req.member = value to get around false positives from eslint: * https://github.com/eslint/... — committed to dhuang612/Ghost by allouis 5 years ago
- Fix linter errors eslint:recommended seems tohave gotten more sensitive. require-atomic-updates was disabled because "I think this is intentional behavior because that someObj object was read before ... — committed to smogon/pokemon-showdown by scheibo 5 years ago
- fix: disable require-atomic-updates rule This rule reports false positives and will soon be removed from the `eslint:recommended` ruleset. For more info, see: https://github.com/eslint/eslint/issues... — committed to goodeggs/eslint-plugin-goodeggs by ndhoule 5 years ago
- fix: disable require-atomic-updates rule This rule reports false positives and will soon be removed from the `eslint:recommended` ruleset. For more info, see: https://github.com/eslint/eslint/issues... — committed to goodeggs/eslint-plugin-goodeggs by ndhoule 5 years ago
- Update: remove require-atomic-updates from eslint:recommended (refs #11899) — committed to eslint/eslint by kaicataldo 5 years ago
- Update: do not recommend require-atomic-updates (refs #11899) — committed to eslint/eslint by kaicataldo 5 years ago
- disable rule require-atomic-updates https://github.com/eslint/eslint/issues/11899 — committed to maximelkin/knex by deleted user 5 years ago
- Update: do not recommend require-atomic-updates (refs #11899) (#12599) — committed to eslint/eslint by kaicataldo 5 years ago
- đ Disable require-atomic-updates This rule reports too many false positives. See https://github.com/eslint/eslint/issues/11899 — committed to joyn/eslint-config by lo1tuma 5 years ago
- Silence the require-atomic-updates false positive. The rule is fairly aggressive. See: https://github.com/eslint/eslint/issues/11899 — committed to Automattic/wp-calypso by ramonjd 4 years ago
- Disable "require-atomic-updates" This rule is too aggressive. Even writing Koa middleware in the exact recommended way triggers it. https://github.com/eslint/eslint/issues/11899 — committed to jessety/eslint-config by jessety 4 years ago
- feat: override require-atomic-updates require-atomic-updates is a bit too strict, and is currently being tracked as a bug here: https://github.com/eslint/eslint/issues/11899 — committed to google/gts by bcoe 4 years ago
I would say that if nothing else, error message is pretty misleading.
That is simply not true, because
someObj.bar = "whatever";
assigns string"whatever"
which is not depending onsomeObj.bar
in any way.Also, itâs not following the rule definition which clearly states that following must be true:
Ok, but thatâs pretty aggressive assumption, as in many cases that can be an intentional well-thought construct
In place where it reported for me, itâs totally intentional and thereâs no way to write it differently (aside of dropping async/await), due to that I needed to turn off this rule globally.
Still Iâd love it to report for me cases as
count += await getCount()
which are error-prone obviously (and even if not, easy to write in a way they do not report)Per the October 10 TSC Meeting, we intend to take the following actions:
eslint:recommended
in a semver-minor release (currently all changes toeslint:recommended
are semver-major).require-atomic-updates
fromeslint:recommended
in the next minor release.Of course, we still need to find a way to fix the rule, but hopefully this will reduce pain short-term. đ
There is a workaround:
This way the rule is not triggered (as expected). But here comes another question: given current (incorrect) behavior, shouldnât the rule be triggered on
Object.assign
as well?Reopening since this is an accepted bug. Assigning to myself so this doesnât get auto closed again. We still need to figure out how we want to move forward here.
It is my understanding that
await
prevents any further execution from happening, within that code block until the promise is resolved.someObj
is an argument passed into that function by reference andsomeObj
is accessible everywhere it is used within that code block originally posted.Even if you awaited a promise that did 100 things to the same object that
someObj
references, once that promise-work is done there is nothing to stop this code block from also settingsomeObj.bar = "whatever";
. Sure, the fulfilled promise could have madeAnotherReferenceTosomeObj.bar = "something specific";
, but that should get over-written bysomeObj.bar = "whatever";
To assume that the promise does this on a âmightâ, and invoke an eslint error, seems like an attempt to oversimplify async programming to me.
If eslint isnât sophisticated enough to point out BOTH lines of code, that are âracingâ to overwrite something (race condition), then it probably needs to keep its might-suspicions to itself (until it gains that sophistication). Donât block my build on a âmightâ. It takes two to race. If Iâve got a race condition, show me both lines of code that are racing each other.
Hereâs my example: https://github.com/eslint/eslint/issues/11967
Here is a perfectly valid koa snippet that is faulted by this rule:
Unfortunately, it looks like there wasnât enough interest from the team or community to implement this change. While we wish weâd be able to accommodate everyoneâs requests, we do need to prioritize. Weâve found that issues failing to reach accepted status after 21 days tend to never be accepted, and as such, we close those issues. This doesnât mean the idea isnât interesting or useful, just that itâs not something the team can commit to.
Thanks for contributing to ESLint and we appreciate your understanding.
Hereâs a pretty vanilla Express middleware that triggers this problem, as you might see in any app that does virtual hosting based on the URL, if youâre looking for a concrete example:
Demo link.
Sorry, folks - this should not have been closed. The TSC made a determination to remove this rule from
eslint:recommended
, and it looks like we forgot to update labels. Iâll make a PR for this right now.Unfortunately, it looks like there wasnât enough interest from the team or community to implement this change. While we wish weâd be able to accommodate everyoneâs requests, we do need to prioritize. Weâve found that accepted issues failing to be implemented after 90 days tend to never be implemented, and as such, we close those issues. This doesnât mean the idea isnât interesting or useful, just that itâs not something the team can commit to.
Thanks for contributing to ESLint and we appreciate your understanding.
@mysticatea May I ask what the plan is? Will the behavior of this rule be reverted, or otherwise? Current behavior is keeping people from upgrading to ESLint 6. It would be good to know what the official stance is. If itâs âwonât fixâ, then weâll just all turn off this rule and move on with our lives. If itâs âwill fix in 6.0.2â, then we know we can wait for a bit, and it will resolve itself.
If the error message reports that value was reassigned basing on previous value, but I am just overwriting a property without using any external values - then something is definitely wrong.
This rule wasnât triggering false positives for me before 6.x.
Auto-closing an accepted issue with over 100 votes. đ
@mikecbrant If you just want to subscribe to an issue, at the very top of this page, on the right hand side, under âassigneesâ, âlabelsâ, etc⌠thereâs a âsubscribeâ button that will notify you of updates to the issue:
Instead of +1ing the issue, please give it a âthumbs upâ at the top, so the rest of us donât all get a notification that you want to watch this issue. đ
I think first straightforward and important step, is to remove it from
eslint:recommended
group.Such controversial rule, which doesnât work with many typical cases, should not be recommended (IMO we can consider it a bug, that it landed there)
I do not think this should be considered as a bug, it is a true positive warning, IMHO.
With the code snippet posted in OP, two different calls of
fn
with a samesomeObj
do not mean the last value stayed insomeObj.bar
is assigned by the latter call offn
. Which explains why this pattern produces a race condition.I have written a blog post about promise base semaphore pattern. This pattern can help avoiding this race condition effectively.
It seems that there are enough examples of this rule not working as intended (or at least not working as described in docs). Iâd say that the discussion went long enough for the maintainers to chip in and give us some comment or resolution.
I think there are several possible courses of action:
@mysticatea Would you be so kind and comment on this?
I would like to speak up in support of the rule. Mutation and async programming is a source of many errors in the codebases I had worked on and this rule has been very beneficial to bring them to light. I would also like to speak up in favor of keeping it in recommended ruleset, to help expose these difficult issues for less experienced contributors. The false positives are, of course, a major issue, as it leads to the rule being disabled or ignored.
Perhaps we can introduce more ways to explicitly mark up objects that we are allowed to mutate? This can already be done somewhat using renaming a variable in oneâs scope, but it looks like a hack, since at the end of the day the object maintains identity:
An explicit comment could indicate the authorâs intent and judgement. At this stage it is almost identical to
eslint-disable-next-line require-atomic-updates
, but with a better communication of the reasons (and it works for the entire block):For common patterns like the Koa context, it would be convenient to whitelist the variables we are allowed to mutate by name in the configuration:
TSC Summary
require-atomic-updates
is currently on ineslint:recommended
and appears to be flagging safe code (see examples in comments above).TSC Question
Can we remove this rule from
eslint:recommended
while we decide how we want to move forward with the rule? This should be fine to do in a semver-minor release because it will result in fewer errors being reported.Iâm not even sure you should even warn when side-effects are obvious and likely intentional.
@ericmorand IMHO that is the responsibility of ESLint. It is to discourage use of patterns that are known to cause bugs. Example: you can omit
break
in a switch statementâs case, and this is a language feature, but is usually not what you want it to do. Omitting it may imply a bug. While testing may expose this bug, that is irrelevant to ESLint.Why? This philosophy of reporting possible bugs (based on patterns that are considered dangerous) goes way back in the history of linting (pre-JavaScript). ESLint did not start this. So perhaps your expectations of ESLint are not in line with its philosophy? That is fine of course, but then perhaps consider using a different tool.
Do note that Iâm not writing this as a defense for how this rule currently operates (scroll up to see my earlier comments).
Just my 2 cents.
I just got a build stuck because of require-atomic-updates even though the code works perfectly. I had to disable this rule. It doesnât make sense to me. This is not eslint responsibility to try to guess that something may not work as expected. This is the reponsibility of the test suite.
Here we have a rule that throw an error saying:
**Possible** race condition...
Possible. Eslint is not sure for a good reason: it canât be sure because only a test suite can be sure that a code works as expected.
More generally, there seems to be a lot of rules that try to guess that a piece of code will work as expected. Why is eslint doing this at all? Why trying to guess things that are anyway checked for sure by the test suite?
+1 (replying so I can watch issue)
this rule is triggering in a variety of scenarios on our repos that come up frequently in samples and tests, e.g.,
Iâm in agreement with @ronkorving that this would be much more valuable as a more specific rule along the lines of, donât do this:
This fails for Reactâs refs too:
@Lonniebiz what if the other line of code is located somewhere in a 3rd-party lib? Looking through entire node_modules to find that line does no sound as a good idea. Still, I agree with you. That suspicion should not be considered an error. A warning would be quite enough.
You can make an other rule to handle all possible race conditions, but this rule has the name: require-atomic-updates. Please read the documentation for this rule
@stinovlas already mentioned this:
Because of the last code changes this rule throws 4 errors for the Examples of correct code from the documentation:
Demo link
So please revert the code that it works again according to the documentation.
My 2 cents: all âpossibleâ rules should exist, because usually they arenât too much about guessing but about preventing bad practices and popular errors.
If you can fix the reported error by making your code better (no âhacksâ, just really better) - then the rule is good. If you need to silence the rule, because the code is all good and the only alternative is to write hacky code to fool the linter - then the rule is bad.
So currently I have nothing against other rules, itâs just
require-atomic-updates
that went crazy after the update.And the bonus thing: You can always change your config and disable all the âpossibleâ rules if they are annoying to you đ
I agree that
eslint:recommended
should not contain that rule because of too aggressive.This is a real issue (Iâm hitting all the examples pasted above), and one that was not mentioned in the migration guide for ESLint 6.0.0.
I can appreciate theories on how these cases are at least on paper unsafe, but that kind of behavior should then at least exist behind a flag. Right now, weâre losing a valuable feature because the only way to deal with this situation is to turn the rule off.
@denis-sokolov There are too many situations already, when people are treated as idiots (âfor their own safetyâ, of course). Do not create another one, please.
Thatâs not the only option. You could just switch it to a warning:
It does warn about a valid issue that wont always crop up, but when it does, it is the type of insidious bug that could take days to discover. There are quite a few comments in this thread that belie the commenterâs understanding of parallel programming.
I do agree that this should be a warning and not an error.
In my experience, most testers generally do not write test code to find race conditionsâmost automated tests Iâve seen test promises with a procedural mindset. This type of issue is generally far from âfor sureâ tested by the test suite.
This is wrong.
await
prevents any further execution from happening, within that invocation of that code block until the promise is resolved.someObj
is an argument passed into that function by reference andsomeObj
is accessible everywhere it is used within that code block and also everywhere else that has a reference to it.This means that while waiting for the promise to resolve something else can modify
someObj.bar
and that by then setting it later, you are overriding that modification and it is unclear if it is the intention of the code to ignore those other potential changes.They are both bad, but itâs a bug in the rule that it cannot detect the second one as a problem.
The assignment is safe if and only if it is the only assignment done to the value AND the expression evaluated is idempotent.
await import()
happens to be, but the lint rule doesnât know that.Why? Because the update is not atomic. It is possible that, between the
await
starting and it settling, the value ofcontrollerLogic
changed, which makes the premise of the branch (thatcontrollerLogic
is falsy) now invalid. You need to do another compare-and-swap operation instead of assuming the value it had beforeawait
is the same as it has after.For example, while
controllerLogic
isundefined
,.start()
is called multiple times before emptying the stack, there will be multiple assignments tocontrollerLogic
pending resolution of theawait
for the result of theimport()
call. We humans know thatimport()
will always return the same value for the same module (presuming no hot-module-reloading related bugs); but if each expression yielded a different value, you would have three different values forcontrollerLogic
andcontrollerLogic.go
, and the order they would execute is unpredictable (well, it is FIFO in the case of a normalimport()
implementation, but this is information we have that the lint rule does not).This response is perhaps overly pedantic, especially for a non-parallel language like (most uses of) javascript, but this is the kind of thing you need to keep in mind when writing concurrent code; this is why
Atomics
exist, this is why atomic machine language instructions exists, this is whystdatomic.h
exists.I think we should add an option to ignore assignments to properties. That is, to report only assignments to variables if this option is enabled.
Just to add to the pile, here is one more very-common way of writing middleware that would trigger this
The old implementation did add value, so I wish we could have that again. You could move the aggressive behavior behind an option.
I have a doubt about this code and this rule.
This is not good for eslint:
This one is good for eslint:
WHY?
You can always submit a PR to fix it, or a PR to remove it from recommended.
On Sun, Nov 24, 2019, 18:58 Greg Wardwell notifications@github.com wrote:
@Jessidhia it is still us humans who write code in 2019. We know better what we are doing (well, not all of us, but those who donât hardly use linters), so the rule is too aggressive and should not be enabled by default in the form it is implemented now. It is kind of if some rule gave an error if we use (or do not use) some pattern, say, factory method. It is too opinion-based for an (not perfect) algorithm.
Agreed with @mysticatea. Iâm not very familiar with this rule, but it sounds like we should consider doing the following:
require-atomic-updates
fromeslint:recommended
. We should be fine to release this in a semver-minor release because it will result in fewer errors being reported.We have to disable this rule in our company due to many false positives. In paper rule is great and should prevent many bugs, but feels like itâs not quite ready yet.