Skip to content

[refurb] Unsafe fixes in FURB161 #17240

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

VascoSch92
Copy link
Contributor

@VascoSch92 VascoSch92 commented Apr 6, 2025

The PR fixes #16457 .

Specifically, FURB161 is marked safe, but the rule generates safe fixes only in specific cases. Therefore, we attempt to mark the fix as unsafe when we are not in one of these cases.

For instances, the fix is marked as aunsafe just in case of strings (as pointed out in the issue). Let me know if I should change something.

Copy link
Contributor

github-actions bot commented Apr 6, 2025

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

@ntBre ntBre added bug Something isn't working fixes Related to suggested fixes for violations labels Apr 6, 2025
@ntBre ntBre self-assigned this Apr 6, 2025
Copy link
Member

@MichaReiser MichaReiser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for looking into this. Could you update the rule's documentation with a ## Fif safety header and explain when the fix is safe (or isn't)? This will also make reviewing easier

@dscorbett
Copy link

This PR doesn’t fully fix #16457. The point of that issue is that FURB161 should be unsafe by default, with a few exceptions. This PR keeps it safe by default, the only exception being string literals.

@VascoSch92 VascoSch92 requested a review from MichaReiser April 7, 2025 19:24
@VascoSch92
Copy link
Contributor Author

This PR doesn’t fully fix #16457. The point of that issue is that FURB161 should be unsafe by default, with a few exceptions. This PR keeps it safe by default, the only exception being string literals.

Thanks for the explanation. I approved the modification to make the rule unsafe if we can not determine if the argument of the bin method is of type int or bool

@dscorbett
Copy link

Using ResolvedPythonType::from would help catch more expressions than literals, like -1 or 1 + 2.

Copy link
Contributor

@ntBre ntBre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this! Overall I think this is good, but we can simplify things a bit by using a fix Applicability, and then I think we can do a better job with type inference with ResolvedPythonType::from, specifically for Expr::BinOp (cases like 1 + 2) and for Expr::UnOp (cases like -1).

If we extend the safe handling to bool, we may also be able to resolve expressions like True or False.

Comment on lines +30 to +33
/// ## Fix safety
/// This rule's fix is marked as unsafe, as the `bit_count()` method can be used when the argument
/// to the `bin()` is an instance of a type that implements the `__index__` and `bit_count` methods.
///
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also want to say explicitly when it is safe, since it's not always unsafe. Maybe something like this?

Suggested change
/// ## Fix safety
/// This rule's fix is marked as unsafe, as the `bit_count()` method can be used when the argument
/// to the `bin()` is an instance of a type that implements the `__index__` and `bit_count` methods.
///
/// ## Fix safety
/// This rule's fix is marked as unsafe unless the argument to `bin` can be inferred as
/// an instance of a type that implements the `__index__` and `bit_count` methods.
///

I think we could also possibly drop the mention of __index__ since we're always just calling bit_count right? Based on my reading of the __index__ docs, it's actually a requirement for calling bin itself rather than a requirement to call bit_count as seems more relevant here.

// it.
let parenthesize = match arg {
// it. Moreover, we check if the fix is safe.
let (parenthesize, fix_is_safe): (bool, bool) = match arg {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think instead of returning a second bool here, you can just return an Applicability and use that with Fix::applicable_edit instead of branching on fix_is_safe down below.

@@ -160,7 +166,7 @@ pub(crate) fn bit_count(checker: &Checker, call: &ExprCall) {
| Expr::NoneLiteral(_)
| Expr::EllipsisLiteral(_)
| Expr::Attribute(_)
| Expr::Subscript(_) => false,
| Expr::Subscript(_) => (false, false),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't highlight the line directly, but I think Expr::BooleanLiteral should also allow a safe edit, at least that's what it sounds like from the issue.

@@ -12,7 +12,7 @@ FURB161.py:6:9: FURB161 [*] Use of `bin(x).count('1')`
|
= help: Replace with `(x).bit_count()`

Safe fix
Unsafe fix
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a separate issue from the changes here, but I don't think Expr::Names need to be parenthesized. Let's not modify this here, but I'm surprised to see (x).bit_count instead of x.bit_count.

@@ -116,7 +116,7 @@ FURB161.py:11:9: FURB161 [*] Use of `bin(0x10 + 0x1000).count('1')`
|
= help: Replace with `(0x10 + 0x1000).bit_count()`

Safe fix
Unsafe fix
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is also a bit unfortunate because this is obviously an integer. I think this would be helped by ResolvedPythonType::from as @dscorbett suggested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working fixes Related to suggested fixes for violations
Projects
None yet
Development

Successfully merging this pull request may close these issues.

FURB161 fix should be marked unsafe in some contexts
4 participants