Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/refurb/FURB164.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,8 @@
_ = Decimal.from_float(float("-nan"))
_ = Decimal.from_float(float("\x2dnan"))
_ = Decimal.from_float(float("\N{HYPHEN-MINUS}nan"))

# See: https://github.com/astral-sh/ruff/issues/21257
# fixes must be safe
_ = Fraction.from_float(f=4.2)
_ = Fraction.from_decimal(dec=4)
39 changes: 22 additions & 17 deletions crates/ruff_linter/src/rules/refurb/rules/unnecessary_from_float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,9 @@ pub(crate) fn unnecessary_from_float(checker: &Checker, call: &ExprCall) {

// Check if we should suppress the fix due to type validation concerns
let is_type_safe = is_valid_argument_type(arg_value, method_name, constructor, checker);
let has_keywords = !call.arguments.keywords.is_empty();

// Determine fix safety
let applicability = if is_type_safe && !has_keywords {
let applicability = if is_type_safe {
Applicability::Safe
} else {
Applicability::Unsafe
Expand Down Expand Up @@ -210,21 +209,27 @@ fn is_valid_argument_type(
_ => false,
},
// Fraction.from_decimal accepts int, bool, Decimal
(MethodName::FromDecimal, Constructor::Fraction) => match resolved_type {
ResolvedPythonType::Atom(PythonType::Number(
NumberLike::Integer | NumberLike::Bool,
)) => true,
ResolvedPythonType::Unknown => is_int,
_ => {
// Check if it's a Decimal instance
arg_expr
.as_call_expr()
.and_then(|call| semantic.resolve_qualified_name(&call.func))
.is_some_and(|qualified_name| {
matches!(qualified_name.segments(), ["decimal", "Decimal"])
})
(MethodName::FromDecimal, Constructor::Fraction) => {
// First check if it's a Decimal constructor call
let is_decimal_call = arg_expr
.as_call_expr()
.and_then(|call| semantic.resolve_qualified_name(&call.func))
.is_some_and(|qualified_name| {
matches!(qualified_name.segments(), ["decimal", "Decimal"])
});

if is_decimal_call {
return true;
}
},

match resolved_type {
ResolvedPythonType::Atom(PythonType::Number(
NumberLike::Integer | NumberLike::Bool,
)) => true,
ResolvedPythonType::Unknown => is_int,
_ => false,
}
}
_ => false,
}
}
Expand Down Expand Up @@ -274,7 +279,7 @@ fn handle_non_finite_float_special_case(
return None;
}

let Expr::Call(ast::ExprCall {
let Expr::Call(ExprCall {
func, arguments, ..
}) = arg_value
else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ help: Replace with `Fraction` constructor
12 | _ = Fraction.from_decimal(Decimal("-4.2"))
13 | _ = Fraction.from_decimal(Decimal.from_float(4.2))
14 | _ = Decimal.from_float(0.1)
note: This is an unsafe fix and may change runtime behavior

FURB164 [*] Verbose method `from_decimal` in `Fraction` construction
--> FURB164.py:12:5
Expand All @@ -120,7 +119,6 @@ help: Replace with `Fraction` constructor
13 | _ = Fraction.from_decimal(Decimal.from_float(4.2))
14 | _ = Decimal.from_float(0.1)
15 | _ = Decimal.from_float(-0.5)
note: This is an unsafe fix and may change runtime behavior

FURB164 [*] Verbose method `from_decimal` in `Fraction` construction
--> FURB164.py:13:5
Expand Down Expand Up @@ -484,7 +482,6 @@ help: Replace with `Fraction` constructor
32 | _ = Decimal.from_float(f=4.2)
33 |
34 | # Cases with invalid argument counts - should not get fixes
note: This is an unsafe fix and may change runtime behavior

FURB164 Verbose method `from_float` in `Decimal` construction
--> FURB164.py:32:5
Expand Down Expand Up @@ -658,6 +655,7 @@ help: Replace with `Decimal` constructor
64 + _ = Decimal("nan")
65 | _ = Decimal.from_float(float("\x2dnan"))
66 | _ = Decimal.from_float(float("\N{HYPHEN-MINUS}nan"))
67 |

FURB164 [*] Verbose method `from_float` in `Decimal` construction
--> FURB164.py:65:5
Expand All @@ -675,6 +673,8 @@ help: Replace with `Decimal` constructor
- _ = Decimal.from_float(float("\x2dnan"))
65 + _ = Decimal("nan")
66 | _ = Decimal.from_float(float("\N{HYPHEN-MINUS}nan"))
67 |
68 | # See: https://github.com/astral-sh/ruff/issues/21257

FURB164 [*] Verbose method `from_float` in `Decimal` construction
--> FURB164.py:66:5
Expand All @@ -683,10 +683,47 @@ FURB164 [*] Verbose method `from_float` in `Decimal` construction
65 | _ = Decimal.from_float(float("\x2dnan"))
66 | _ = Decimal.from_float(float("\N{HYPHEN-MINUS}nan"))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
67 |
68 | # See: https://github.com/astral-sh/ruff/issues/21257
|
help: Replace with `Decimal` constructor
63 | # Cases with non-finite floats - should produce safe fixes
64 | _ = Decimal.from_float(float("-nan"))
65 | _ = Decimal.from_float(float("\x2dnan"))
- _ = Decimal.from_float(float("\N{HYPHEN-MINUS}nan"))
66 + _ = Decimal("nan")
67 |
68 | # See: https://github.com/astral-sh/ruff/issues/21257
69 | # fixes must be safe

FURB164 [*] Verbose method `from_float` in `Fraction` construction
--> FURB164.py:70:5
|
68 | # See: https://github.com/astral-sh/ruff/issues/21257
69 | # fixes must be safe
70 | _ = Fraction.from_float(f=4.2)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
71 | _ = Fraction.from_decimal(dec=4)
|
help: Replace with `Fraction` constructor
67 |
68 | # See: https://github.com/astral-sh/ruff/issues/21257
69 | # fixes must be safe
- _ = Fraction.from_float(f=4.2)
70 + _ = Fraction(4.2)
71 | _ = Fraction.from_decimal(dec=4)

FURB164 [*] Verbose method `from_decimal` in `Fraction` construction
--> FURB164.py:71:5
|
69 | # fixes must be safe
70 | _ = Fraction.from_float(f=4.2)
71 | _ = Fraction.from_decimal(dec=4)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: Replace with `Fraction` constructor
68 | # See: https://github.com/astral-sh/ruff/issues/21257
69 | # fixes must be safe
70 | _ = Fraction.from_float(f=4.2)
- _ = Fraction.from_decimal(dec=4)
71 + _ = Fraction(4)