-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
Unsoundly loses one branch when another is uninitialized, at top of loop #8526
Comments
Here's another bit of unsoundness involving uninitialized variables which is sort of an inverse to this one; not sure if it's the same underlying issue. If instead of using, and then initializing, the variable in a loop body, we do the same thing in a function expression's body (perhaps because we're expressing the same loop using let thing;
[0].forEach(() => {
thing.f(); // boom! but no error
thing = { f: () => {} };
}) Inspection shows that Flow sees the variable's type as the type that the code further down would initialize it to. Here again, if the variable is explicitly initialized to let other = undefined;
[0].forEach(() => {
other.f(); // error, correctly
other = { f: () => {} };
}) And here's a Try-Flow link for these. |
Kind of puzzling that Flow doesn't complain about this one on its own! As ESLint points out in the rule that we have to disable here, the `= undefined` has no effect on runtime behavior -- it just states explicitly what the code was already doing anyway. But once we do make it explicit, Flow starts noticing that the `undefined` was propagating into boolean expressions and to an argument passed to `isSameRecipient` -- which also justifies a check at the top of that function which by its types had looked dead. Fix that function's signature, and booleanize the value when using as a boolean. This Flow behavior is concerning enough that we should probably just stop saying `let foo;`, and always explicitly initialize to `undefined` when that's what we mean. (The code is potentially a bit clearer that way anyway.) We'll do that next. I've also reported the Flow issue upstream, as: facebook/flow#8526
Kind of puzzling that Flow doesn't complain about this one on its own! As ESLint points out in the rule that we have to disable here, the `= undefined` has no effect on runtime behavior -- it just states explicitly what the code was already doing anyway. But once we do make it explicit, Flow starts noticing that the `undefined` was propagating into boolean expressions and to an argument passed to `isSameRecipient` -- which also justifies a check at the top of that function which by its types had looked dead. Fix that function's signature, and booleanize the value when using as a boolean. This Flow behavior is concerning enough that we should probably just stop saying `let foo;`, and always explicitly initialize to `undefined` when that's what we mean. (The code is potentially a bit clearer that way anyway.) We'll do that next. I've also reported the Flow issue upstream, as: facebook/flow#8526
We've had this practice for a while, since I discovered the Flow bug facebook/flow#8526 that makes it necessary. But a more recent PR review reminded me that we hadn't documented it in the style guide: #4934 (comment) So do that.
This is now fixed. |
Flow version: v0.137.0 (and older versions on flow.org/try, including at least v0.37.0 which is the oldest)
Expected behavior
Should report an error in the following code, which fails at runtime with
TypeError
:Actual behavior
No error is reported.
On inspection with an IDE,
flow type-at-pos
, or$Flow$DebugPrint
, Flow is apparently seeing the variable's type where it appears at the top of the loop as simplyvoid
, when it should bevoid | {| x: number |}
. That'd be enough to cause this issue.A key element of the repro case is that on entering the loop, the variable is uninitialized. If instead the variable is initialized with
undefined
-- which should have the exact same behavior at runtime -- then Flow correctly reports an error:and inspection shows it correctly sees the variable's type as
void | {| x: number |}
.The text was updated successfully, but these errors were encountered: