-
Notifications
You must be signed in to change notification settings - Fork 1.6k
[ruff] Fix RUF033 breaking with named default expressions
#19115
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
[ruff] Fix RUF033 breaking with named default expressions
#19115
Conversation
|
885ff2c to
234e200
Compare
ntBre
left a comment
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.
Thanks! I think the results look right, but I have a couple of suggestions for the implementation.
| let initvar_name = ast::Expr::Name(ast::ExprName { | ||
| node_index: ast::AtomicNodeIndex::dummy(), |
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 don't think we need to switch all the way to using the code generator here. Can't we just check if the expression is_expr_named and add parens if so? Or I think we could even check the parenthesized_range and preserve the parens it came with.
It's nice to prefer range replacements because the generator strips out any comments, as well as being much more verbose.
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 managed to rewrite it using parenthesized_range. It's passing the same tests. But if feels crazy fragile. Looking at the code I cannot convince myself of its correctness and that I haven't missed any edge case. I don't feel comfortable with my name being attached to that code. Sorry.
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.
If you feel that strongly about it, we can keep the old implementation. I don't really see how it's more fragile, and we have similar checks in other rules, but this was only a slight preference on my part, not a blocker. I'd much rather fix the bug and give you credit for your work here. I'll approve and merge this if you want to revert/drop the last commit and reopen the PR.
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.
Where I think this is fragile: if the Syntax ever changes, string replacements like these bake in too many assumptions. That's how this bug happened in the first place: a new syntax construct (walrus operator) was introduced and the replacement logic wasn't up to the task.
Moreover, parenthesized_range calls parentheses_iterator, which in turn is documented with
/// Returns an iterator over the ranges of the optional parentheses surrounding an expression.
Yeah no... these parens are surely not optional 😬 It's quite unfortunate, I find, that the AST does not always include the parens where they're necessary in the expression's range. But neither does Python's own ast module 🤷
I slept a night over it and I'll re-open the PR. If not, I would've given you permission to take my code and commit it under your name. So regardless of how this ends some fix can be merged.
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.
Where I think this is fragile: if the Syntax ever changes, string replacements like these bake in too many assumptions. That's how this bug happened in the first place: a new syntax construct (walrus operator) was introduced and the replacement logic wasn't up to the task.
Ahh, I see where you're coming from. I think that explains the other assertion too. I guess I wasn't really thinking about it in the same way since the walrus operator and Python 3.8 predate Ruff by ~3 years. So it wasn't so much that something new came out and broke us, just that the initial rule implementation didn't account for everything that already existed.
Yeah no... these parens are surely not optional
I can also see what you mean here, but I always read this as referring to a Rust Option, like the return type of parenthesized_range, as in the expression may or may not have parentheses rather than may or may not require parentheses.
Anyway, I'm happy with either implementation. I'll give this another quick review now.
ntBre
left a comment
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.
Thanks again! Just one nit about a test case name, but this is otherwise good to go from my side.
ntBre
left a comment
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.
Thank you! I'll merge after the patch release finishes.
…hlight * 'main' of https://github.com/astral-sh/ruff: [ty] Minor: fix incomplete docstring (astral-sh#19534) [ty] Move server tests as integration tests (astral-sh#19522) [`ruff`] Offer fixes for `RUF039` in more cases (astral-sh#19065) [ty] Support `dataclasses.InitVar` (astral-sh#19527) [`ruff`] Fix `RUF033` breaking with named default expressions (astral-sh#19115) Update pre-commit hook name (astral-sh#19530) Bump 0.12.5 (astral-sh#19528) [ty] Rename type_api => ty_extensions (astral-sh#19523) [ty] Added support for "go to references" in ty playground. (astral-sh#19516) [ty] Return a tuple spec from the iterator protocol (astral-sh#19496) [ty] Exhaustiveness checking & reachability for `match` statements (astral-sh#19508) [ty] Fix narrowing and reachability of class patterns with arguments (astral-sh#19512)
* main: [ty] Added support for "document highlights" language server feature. (astral-sh#19515) Add support for specifying minimum dots in detected string imports (astral-sh#19538) [ty] Minor: fix incomplete docstring (astral-sh#19534) [ty] Move server tests as integration tests (astral-sh#19522) [`ruff`] Offer fixes for `RUF039` in more cases (astral-sh#19065) [ty] Support `dataclasses.InitVar` (astral-sh#19527) [`ruff`] Fix `RUF033` breaking with named default expressions (astral-sh#19115) Update pre-commit hook name (astral-sh#19530) Bump 0.12.5 (astral-sh#19528) [ty] Rename type_api => ty_extensions (astral-sh#19523) [ty] Added support for "go to references" in ty playground. (astral-sh#19516) # Conflicts: # crates/ty_server/src/server/api/requests.rs # crates/ty_server/tests/e2e/snapshots/e2e__initialize__initialization.snap # crates/ty_server/tests/e2e/snapshots/e2e__initialize__initialization_with_workspace.snap
## Summary The generated fix for `RUF033` would cause a syntax error for named expressions as parameter defaults. ```python from dataclasses import InitVar, dataclass @DataClass class Foo: def __post_init__(self, bar: int = (x := 1)) -> None: pass ``` would be turned into ```python from dataclasses import InitVar, dataclass @DataClass class Foo: x: InitVar[int] = x := 1 def __post_init__(self, bar: int = (x := 1)) -> None: pass ``` instead of the syntactically correct ```python # ... x: InitVar[int] = (x := 1) # ... ``` ## Test Plan Test reproducer (plus some extra tests) have been added to the test suite. ## Related Fixes: #18950
Summary
The generated fix for
RUF033would cause a syntax error for named expressions as parameter defaults.would be turned into
instead of the syntactically correct
Test Plan
Test reproducer (plus some extra tests) have been added to the test suite.
Related
Fixes: #18950