-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
[red-knot] Add diagnostic for invalid unpacking #15086
Conversation
|
fe681d0
to
6a116dc
Compare
f0b0385
to
ff53cb1
Compare
fn tuple_ty_elements( | ||
&mut self, | ||
&self, | ||
expr: &ast::Expr, | ||
targets: &[ast::Expr], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additional changes:
- Expanded the comments in this method
- Avoid
&mut self
as it's not required (not sure why clippy didn't catch that) - Pass the
expr
which is required for the diagnostic location
# error: "Not enough values to unpack (expected 2, got 1)" | ||
for a, b in (1, 2, (3, "a"), 4, (5, "b"), "c"): | ||
reveal_type(a) # revealed: Unknown | Literal[3, 5] | LiteralString |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re-commenttng here,
Since "c"
doesn't have enough values to unpack, I think LiteralString
will never put at here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree it would be better if this were just Unknown | Literal[3, 5]
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think to do this consistently would mean changing a bunch of other cases, though -- it would mean that e.g. for (a, b, c) = (1, 2)
we would infer Unknown
(or Never
would also be reasonable) for all of a
, b
, c
, rather than inferring Literal[1]
for a
and Literal[2]
for b
. That is, today we try to match up and infer as many types as we can, even if the overall unpacking is an error; this would mean changing that.
The advantage of the current approach is that it can catch more errors at once, assuming the parts that we are able to line up are actually lined up correctly. But that may not be a reasonable assumption; if you have a mis-alignment it could actually be early in the unpacking, in which case we are just inferring wrong types for many of the targets, potentially leading to false positives in subsequent code.
It looks like our current behavior is more like pyright; mypy always just infers Any
for all targets in a mis-aligned unpacking.
I do think that the mypy behavior is more principled and we should probably switch to that, though I also don't think it's high priority to make that change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I did that for the advantage that you've mentioned and I'm fine changing the behavior as it's an error case and should be less often in practice. I'll expand the original issue with this task.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice!
# error: "Not enough values to unpack (expected 2, got 1)" | ||
for a, b in (1, 2, (3, "a"), 4, (5, "b"), "c"): | ||
reveal_type(a) # revealed: Unknown | Literal[3, 5] | LiteralString |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree it would be better if this were just Unknown | Literal[3, 5]
.
ff53cb1
to
9fd4eb8
Compare
* main: Add all PEP-585 names to UP006 rule (#5454) [`flake8-simplify`] More precise inference for dictionaries (`SIM300`) (#15164) `@no_type_check` support (#15122) Visit PEP 764 inline `TypedDict`s' keys as non-type-expressions (#15073) [red-knot] Add diagnostic for invalid unpacking (#15086) [`flake8-use-pathlib`] Catch redundant joins in `PTH201` and avoid syntax errors (#15177) Update Rust crate glob to v0.3.2 (#15185) Update astral-sh/setup-uv action to v5 (#15193) Update dependency mdformat-mkdocs to v4.1.1 (#15192) Update Rust crate serde_with to v3.12.0 (#15191) Update NPM Development dependencies (#15190) Update pre-commit hook rhysd/actionlint to v1.7.5 (#15189) Update Rust crate syn to v2.0.93 (#15188) Update Rust crate serde to v1.0.217 (#15187) Update Rust crate quote to v1.0.38 (#15186) Update Rust crate compact_str to v0.8.1 (#15184) [`flake8-type-checking`] Disable TC006 & TC007 in stub files (#15179) Test explicit shadowing involving `def`s (#15174) Fix typo in `NameImport.qualified_name` docstring (#15170) [airflow]: extend names moved from core to provider (AIR303) (#15159)
Summary
Part of #13773
This PR adds diagnostics when there is a length mismatch during unpacking between the number of target expressions and the number of types for the unpack value expression.
There are 3 cases of diagnostics here where the first two occurs when there isn't a starred expression and the last one occurs when there's a starred expression:
Examples for all each of the above cases:
The (3) case is a bit special because it uses a distinct wording "expected n or more" instead of "expected n" because of the starred expression.
Location
The diagnostic location is the target expression that's being unpacked. For nested targets, the location will be the nested expression. For example:
For future improvements, it would be useful to show the context for why this unpacking failed. For example, for why the expected number of targets is
n
, we can highlight the relevant elements for the value expression.In the ecosystem, Pyright uses the target expressions for location while mypy uses the value expression for the location. For example:
Test Plan
Update existing test cases TODO with the error directives.