-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
[pylint
] - add fix for unary expressions in PLC2801
#9587
Conversation
38ee7ae
to
5a85f1d
Compare
|
code | total | + violation | - violation | + fix | - fix |
---|---|---|---|---|---|
PLC2801 | 96 | 48 | 0 | 0 | 48 |
5a85f1d
to
c1782ce
Compare
I see now that the |
@diceroll123 - Feel free to change, I won't get to this until tomorrow. |
CodSpeed Performance ReportMerging #9587 will not alter performanceComparing Summary
|
a825cc4
to
c49171c
Compare
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.
Would it be possible to generalize the fix to correctly handle operator precedence in general. There are some more cases mentioned on the linked Issue that this PR doesn't address.
if let Some(fixed) = fixed { | ||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(fixed, call.range()))); | ||
if let Some(mut fixed) = fixed { | ||
let is_in_unary = checker |
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.
The section here could use a comment explaining on why we're doing what we're doing below because it's not immediately obvious what it is about.
let rparen = | ||
SimpleTokenizer::starts_at(value.as_ref().end(), checker.locator().contents()) | ||
.find(|token| { | ||
token.kind == SimpleTokenKind::RParen && token.start() < call.end() | ||
}); | ||
|
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.
What happens if we have a.__sub__(((((b)))))
?
You can use call.arguments.start
(or end
) to get the parentheses offsets of the call expression and retain those.
@@ -156,8 +157,45 @@ pub(crate) fn unnecessary_dunder_call(checker: &mut Checker, call: &ast::ExprCal | |||
call.range(), | |||
); | |||
|
|||
if let Some(fixed) = fixed { | |||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(fixed, call.range()))); | |||
if let Some(mut fixed) = fixed { |
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 wonder if the fix produces invalid code if you have
4.__sub__(
3
+
4
)
What i understand is that we fix this to
a -
3
+
4
which Python doesn't like very much
if is_in_unary { | ||
// find the first ")" before our dunder method | ||
let rparen = | ||
SimpleTokenizer::starts_at(value.as_ref().end(), checker.locator().contents()) |
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.
Can we start after the attribute
to reduce the text that need to be searched?
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.
Can't start after the value in this case.
We're looking for this,
(-a).__sub__(1)
###^
and the difference between
(-a).__sub__(1)
and -a.__sub__(1)
It could possibly be optimized another way though, perhaps by checking the tokens between value
and the first Dot
🤔
874ef9b
to
7f3f81d
Compare
Made some tweaks! First and foremost: the fix is marked as unsafe. This could be conditional with more type logic in a future iteration, for sure. The arguments within the dunder method call will now stay in parentheses if it's anything more complex than a literal/name/attribute. I'm sure that logic may be fine to expand into other expression types. So, this example: print(a.__sub__(
3
+
4
)) now turns into print(a - (3
+
4
)) And since you explicitly asked, print(a.__sub__(((((2 - 1)))))) is now reduced to print(a - (2 - 1)) # PLC2801 Semantically it's the same, regardless of extra parens. Looking forward to opinions on improving it further! 😄 |
9875f6a
to
525ea07
Compare
let value_slice = checker.locator().slice(value.as_ref()); | ||
let arg_slice = checker.locator().slice(arg); | ||
|
||
if arg.is_attribute_expr() || arg.is_name_expr() || arg.is_literal_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.
I think we can exclude some more expressions here
- Calls
- Unary expressions
- Lambda
- If expressions (I think)?
- Dict / Set / List / Tuple and all comprehension variants (everything with parentheses)
- Generators
- Subscripts
- Starred (I think)
- Slices?
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.
Reworked it, added all of the above except Unary (because that's what got us this PR in the first place hah)
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.
Sorry for the late review. The uv
release took a lot of attention recently.
This is an excellent improvement. I think there are a few more situations where adding the parentheses isn't strictly necessary. You might want to move the logic into its own function to avoid coding it twice.
c73f701
to
c1ffe3f
Compare
c1ffe3f
to
b223783
Compare
b223783
to
1a14221
Compare
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!
## Summary Closes astral-sh#9572 Don't go easy on this review! ## Test Plan `cargo test`
Summary
Closes #9572
Don't go easy on this review!
Test Plan
cargo test