[ruff] Do not simplify round() calls (RUF046) (#14832)

## Summary

Part 1 of the big change introduced in #14828. This temporarily causes
all fixes for `round(...)` to be considered unsafe, but they will
eventually be enhanced.

## Test Plan

`cargo nextest run` and `cargo insta test`.
This commit is contained in:
InSync 2024-12-09 22:51:27 +07:00 committed by GitHub
parent 68eb0a2511
commit c62ba48ad4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 569 additions and 374 deletions

View file

@ -1,9 +1,12 @@
import math import math
inferred_int = 1
inferred_float = 1.
### Safely fixable ### Safely fixable
# Arguments are not checked
int(id()) int(id())
int(len([])) int(len([]))
int(ord(foo)) int(ord(foo))
@ -17,6 +20,15 @@ int(math.lcm())
int(math.isqrt()) int(math.isqrt())
int(math.perm()) int(math.perm())
int(round(1, 0))
int(round(1, 10))
int(round(1))
int(round(1, None))
int(round(1.))
int(round(1., None))
### Unsafe ### Unsafe
@ -24,27 +36,35 @@ int(math.ceil())
int(math.floor()) int(math.floor())
int(math.trunc()) int(math.trunc())
int(round(inferred_int, 0))
int(round(inferred_int, 10))
### `round()` int(round(inferred_int))
int(round(inferred_int, None))
## Errors int(round(inferred_float))
int(round(0)) int(round(inferred_float, None))
int(round(0, 0))
int(round(0, None))
int(round(0.1)) int(round(unknown))
int(round(0.1, None)) int(round(unknown, None))
# Argument type is not checked
foo = type("Foo", (), {"__round__": lambda self: 4.2})()
int(round(foo)) ### No errors
int(round(foo, 0))
int(round(foo, None)) int(round(1, unknown))
int(round(1., unknown))
int(round(1., 0))
int(round(inferred_float, 0))
int(round(inferred_int, unknown))
int(round(inferred_float, unknown))
int(round(unknown, 0))
int(round(unknown, unknown))
## No errors
int(round(0, 3.14)) int(round(0, 3.14))
int(round(0, non_literal)) int(round(inferred_int, 3.14))
int(round(0, 0), base) int(round(0, 0), base)
int(round(0, 0, extra=keyword)) int(round(0, 0, extra=keyword))
int(round(0.1, 0))

View file

@ -1,7 +1,7 @@
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use ruff_diagnostics::{AlwaysFixableViolation, Applicability, Diagnostic, Edit, Fix}; use ruff_diagnostics::{AlwaysFixableViolation, Applicability, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, ViolationMetadata}; use ruff_macros::{derive_message_formats, ViolationMetadata};
use ruff_python_ast::{Arguments, Expr, ExprCall, ExprName, ExprNumberLiteral, Number}; use ruff_python_ast::{Arguments, Expr, ExprCall, ExprNumberLiteral, Number};
use ruff_python_semantic::analyze::typing; use ruff_python_semantic::analyze::typing;
use ruff_python_semantic::SemanticModel; use ruff_python_semantic::SemanticModel;
use ruff_text_size::TextRange; use ruff_text_size::TextRange;
@ -74,7 +74,7 @@ pub(crate) fn unnecessary_cast_to_int(checker: &mut Checker, call: &ExprCall) {
// Depends on `ndigits` and `number.__round__` // Depends on `ndigits` and `number.__round__`
["" | "builtins", "round"] => { ["" | "builtins", "round"] => {
if let Some(fix) = replace_with_shortened_round_call(checker, outer_range, arguments) { if let Some(fix) = replace_with_round(checker, outer_range, inner_range, arguments) {
fix fix
} else { } else {
return; return;
@ -89,9 +89,9 @@ pub(crate) fn unnecessary_cast_to_int(checker: &mut Checker, call: &ExprCall) {
_ => return, _ => return,
}; };
checker let diagnostic = Diagnostic::new(UnnecessaryCastToInt, call.range);
.diagnostics
.push(Diagnostic::new(UnnecessaryCastToInt, call.range).with_fix(fix)); checker.diagnostics.push(diagnostic.with_fix(fix));
} }
fn single_argument_to_int_call<'a>( fn single_argument_to_int_call<'a>(
@ -117,12 +117,29 @@ fn single_argument_to_int_call<'a>(
Some(argument) Some(argument)
} }
/// Returns an [`Edit`] when the call is of any of the forms: /// The type of the first argument to `round()`
/// * `round(integer)`, `round(integer, 0)`, `round(integer, None)` #[derive(Clone, Copy, Debug, Eq, PartialEq)]
/// * `round(whatever)`, `round(whatever, None)` enum Rounded {
fn replace_with_shortened_round_call( InferredInt,
InferredFloat,
LiteralInt,
LiteralFloat,
Other,
}
/// The type of the second argument to `round()`
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum Ndigits {
NotGiven,
LiteralInt,
LiteralNone,
Other,
}
fn replace_with_round(
checker: &Checker, checker: &Checker,
outer_range: TextRange, outer_range: TextRange,
inner_range: TextRange,
arguments: &Arguments, arguments: &Arguments,
) -> Option<Fix> { ) -> Option<Fix> {
if arguments.len() > 2 { if arguments.len() > 2 {
@ -132,48 +149,56 @@ fn replace_with_shortened_round_call(
let number = arguments.find_argument("number", 0)?; let number = arguments.find_argument("number", 0)?;
let ndigits = arguments.find_argument("ndigits", 1); let ndigits = arguments.find_argument("ndigits", 1);
let number_is_int = match number { let number_kind = match number {
Expr::Name(name) => is_int(checker.semantic(), name), Expr::Name(name) => {
Expr::NumberLiteral(ExprNumberLiteral { value, .. }) => matches!(value, Number::Int(..)), let semantic = checker.semantic();
_ => false,
match semantic.only_binding(name).map(|id| semantic.binding(id)) {
Some(binding) if typing::is_int(binding, semantic) => Rounded::InferredInt,
Some(binding) if typing::is_float(binding, semantic) => Rounded::InferredFloat,
_ => Rounded::Other,
}
}
Expr::NumberLiteral(ExprNumberLiteral { value, .. }) => match value {
Number::Int(..) => Rounded::LiteralInt,
Number::Float(..) => Rounded::LiteralFloat,
Number::Complex { .. } => Rounded::Other,
},
_ => Rounded::Other,
}; };
match ndigits { let ndigits_kind = match ndigits {
Some(Expr::NumberLiteral(ExprNumberLiteral { value, .. })) None => Ndigits::NotGiven,
if is_literal_zero(value) && number_is_int => {} Some(Expr::NoneLiteral(_)) => Ndigits::LiteralNone,
Some(Expr::NoneLiteral(_)) | None => {}
Some(Expr::NumberLiteral(ExprNumberLiteral {
value: Number::Int(..),
..
})) => Ndigits::LiteralInt,
_ => Ndigits::Other,
};
let applicability = match (number_kind, ndigits_kind) {
(Rounded::LiteralInt, Ndigits::LiteralInt)
| (Rounded::LiteralInt | Rounded::LiteralFloat, Ndigits::NotGiven | Ndigits::LiteralNone) => {
Applicability::Safe
}
(Rounded::InferredInt, Ndigits::LiteralInt)
| (
Rounded::InferredInt | Rounded::InferredFloat | Rounded::Other,
Ndigits::NotGiven | Ndigits::LiteralNone,
) => Applicability::Unsafe,
_ => return None, _ => return None,
}; };
let number_expr = checker.locator().slice(number); let edit = replace_with_inner(checker, outer_range, inner_range);
let new_content = format!("round({number_expr})");
let applicability = if number_is_int { Some(Fix::applicable_edit(edit, applicability))
Applicability::Safe
} else {
Applicability::Unsafe
};
Some(Fix::applicable_edit(
Edit::range_replacement(new_content, outer_range),
applicability,
))
}
fn is_int(semantic: &SemanticModel, name: &ExprName) -> bool {
let Some(binding) = semantic.only_binding(name).map(|id| semantic.binding(id)) else {
return false;
};
typing::is_int(binding, semantic)
}
fn is_literal_zero(value: &Number) -> bool {
let Number::Int(int) = value else {
return false;
};
matches!(int.as_u8(), Some(0))
} }
fn replace_with_inner(checker: &Checker, outer_range: TextRange, inner_range: TextRange) -> Edit { fn replace_with_inner(checker: &Checker, outer_range: TextRange, inner_range: TextRange) -> Edit {

View file

@ -2,429 +2,566 @@
source: crates/ruff_linter/src/rules/ruff/mod.rs source: crates/ruff_linter/src/rules/ruff/mod.rs
snapshot_kind: text snapshot_kind: text
--- ---
RUF046.py:7:1: RUF046 [*] Value being casted is already an integer
|
6 | # Arguments are not checked
7 | int(id())
| ^^^^^^^^^ RUF046
8 | int(len([]))
9 | int(ord(foo))
|
= help: Remove unnecessary conversion to `int`
Safe fix
4 4 | ### Safely fixable
5 5 |
6 6 | # Arguments are not checked
7 |-int(id())
7 |+id()
8 8 | int(len([]))
9 9 | int(ord(foo))
10 10 | int(hash(foo, bar))
RUF046.py:8:1: RUF046 [*] Value being casted is already an integer
|
6 | # Arguments are not checked
7 | int(id())
8 | int(len([]))
| ^^^^^^^^^^^^ RUF046
9 | int(ord(foo))
10 | int(hash(foo, bar))
|
= help: Remove unnecessary conversion to `int`
Safe fix
5 5 |
6 6 | # Arguments are not checked
7 7 | int(id())
8 |-int(len([]))
8 |+len([])
9 9 | int(ord(foo))
10 10 | int(hash(foo, bar))
11 11 | int(int(''))
RUF046.py:9:1: RUF046 [*] Value being casted is already an integer
|
7 | int(id())
8 | int(len([]))
9 | int(ord(foo))
| ^^^^^^^^^^^^^ RUF046
10 | int(hash(foo, bar))
11 | int(int(''))
|
= help: Remove unnecessary conversion to `int`
Safe fix
6 6 | # Arguments are not checked
7 7 | int(id())
8 8 | int(len([]))
9 |-int(ord(foo))
9 |+ord(foo)
10 10 | int(hash(foo, bar))
11 11 | int(int(''))
12 12 |
RUF046.py:10:1: RUF046 [*] Value being casted is already an integer RUF046.py:10:1: RUF046 [*] Value being casted is already an integer
| |
8 | int(len([])) 8 | ### Safely fixable
9 | int(ord(foo)) 9 |
10 | int(hash(foo, bar)) 10 | int(id())
| ^^^^^^^^^^^^^^^^^^^ RUF046 | ^^^^^^^^^ RUF046
11 | int(int('')) 11 | int(len([]))
12 | int(ord(foo))
| |
= help: Remove unnecessary conversion to `int` = help: Remove unnecessary conversion to `int`
Safe fix Safe fix
7 7 | int(id()) 7 7 |
8 8 | int(len([])) 8 8 | ### Safely fixable
9 9 | int(ord(foo)) 9 9 |
10 |-int(hash(foo, bar)) 10 |-int(id())
10 |+hash(foo, bar) 10 |+id()
11 11 | int(int('')) 11 11 | int(len([]))
12 12 | 12 12 | int(ord(foo))
13 13 | int(math.comb()) 13 13 | int(hash(foo, bar))
RUF046.py:11:1: RUF046 [*] Value being casted is already an integer RUF046.py:11:1: RUF046 [*] Value being casted is already an integer
| |
9 | int(ord(foo)) 10 | int(id())
10 | int(hash(foo, bar)) 11 | int(len([]))
11 | int(int(''))
| ^^^^^^^^^^^^ RUF046 | ^^^^^^^^^^^^ RUF046
12 | 12 | int(ord(foo))
13 | int(math.comb()) 13 | int(hash(foo, bar))
| |
= help: Remove unnecessary conversion to `int` = help: Remove unnecessary conversion to `int`
Safe fix Safe fix
8 8 | int(len([])) 8 8 | ### Safely fixable
9 9 | int(ord(foo)) 9 9 |
10 10 | int(hash(foo, bar)) 10 10 | int(id())
11 |-int(int('')) 11 |-int(len([]))
11 |+int('') 11 |+len([])
12 12 | 12 12 | int(ord(foo))
13 13 | int(math.comb()) 13 13 | int(hash(foo, bar))
14 14 | int(math.factorial()) 14 14 | int(int(''))
RUF046.py:12:1: RUF046 [*] Value being casted is already an integer
|
10 | int(id())
11 | int(len([]))
12 | int(ord(foo))
| ^^^^^^^^^^^^^ RUF046
13 | int(hash(foo, bar))
14 | int(int(''))
|
= help: Remove unnecessary conversion to `int`
Safe fix
9 9 |
10 10 | int(id())
11 11 | int(len([]))
12 |-int(ord(foo))
12 |+ord(foo)
13 13 | int(hash(foo, bar))
14 14 | int(int(''))
15 15 |
RUF046.py:13:1: RUF046 [*] Value being casted is already an integer RUF046.py:13:1: RUF046 [*] Value being casted is already an integer
| |
11 | int(int('')) 11 | int(len([]))
12 | 12 | int(ord(foo))
13 | int(math.comb()) 13 | int(hash(foo, bar))
| ^^^^^^^^^^^^^^^^ RUF046 | ^^^^^^^^^^^^^^^^^^^ RUF046
14 | int(math.factorial()) 14 | int(int(''))
15 | int(math.gcd())
| |
= help: Remove unnecessary conversion to `int` = help: Remove unnecessary conversion to `int`
Safe fix Safe fix
10 10 | int(hash(foo, bar)) 10 10 | int(id())
11 11 | int(int('')) 11 11 | int(len([]))
12 12 | 12 12 | int(ord(foo))
13 |-int(math.comb()) 13 |-int(hash(foo, bar))
13 |+math.comb() 13 |+hash(foo, bar)
14 14 | int(math.factorial()) 14 14 | int(int(''))
15 15 | int(math.gcd()) 15 15 |
16 16 | int(math.lcm()) 16 16 | int(math.comb())
RUF046.py:14:1: RUF046 [*] Value being casted is already an integer RUF046.py:14:1: RUF046 [*] Value being casted is already an integer
| |
13 | int(math.comb()) 12 | int(ord(foo))
14 | int(math.factorial()) 13 | int(hash(foo, bar))
| ^^^^^^^^^^^^^^^^^^^^^ RUF046 14 | int(int(''))
15 | int(math.gcd()) | ^^^^^^^^^^^^ RUF046
16 | int(math.lcm()) 15 |
16 | int(math.comb())
| |
= help: Remove unnecessary conversion to `int` = help: Remove unnecessary conversion to `int`
Safe fix Safe fix
11 11 | int(int('')) 11 11 | int(len([]))
12 12 | 12 12 | int(ord(foo))
13 13 | int(math.comb()) 13 13 | int(hash(foo, bar))
14 |-int(math.factorial()) 14 |-int(int(''))
14 |+math.factorial() 14 |+int('')
15 15 | int(math.gcd()) 15 15 |
16 16 | int(math.lcm()) 16 16 | int(math.comb())
17 17 | int(math.isqrt()) 17 17 | int(math.factorial())
RUF046.py:15:1: RUF046 [*] Value being casted is already an integer
|
13 | int(math.comb())
14 | int(math.factorial())
15 | int(math.gcd())
| ^^^^^^^^^^^^^^^ RUF046
16 | int(math.lcm())
17 | int(math.isqrt())
|
= help: Remove unnecessary conversion to `int`
Safe fix
12 12 |
13 13 | int(math.comb())
14 14 | int(math.factorial())
15 |-int(math.gcd())
15 |+math.gcd()
16 16 | int(math.lcm())
17 17 | int(math.isqrt())
18 18 | int(math.perm())
RUF046.py:16:1: RUF046 [*] Value being casted is already an integer RUF046.py:16:1: RUF046 [*] Value being casted is already an integer
| |
14 | int(math.factorial()) 14 | int(int(''))
15 | int(math.gcd()) 15 |
16 | int(math.lcm()) 16 | int(math.comb())
| ^^^^^^^^^^^^^^^ RUF046 | ^^^^^^^^^^^^^^^^ RUF046
17 | int(math.isqrt()) 17 | int(math.factorial())
18 | int(math.perm()) 18 | int(math.gcd())
| |
= help: Remove unnecessary conversion to `int` = help: Remove unnecessary conversion to `int`
Safe fix Safe fix
13 13 | int(math.comb()) 13 13 | int(hash(foo, bar))
14 14 | int(math.factorial()) 14 14 | int(int(''))
15 15 | int(math.gcd()) 15 15 |
16 |-int(math.lcm()) 16 |-int(math.comb())
16 |+math.lcm() 16 |+math.comb()
17 17 | int(math.isqrt()) 17 17 | int(math.factorial())
18 18 | int(math.perm()) 18 18 | int(math.gcd())
19 19 | 19 19 | int(math.lcm())
RUF046.py:17:1: RUF046 [*] Value being casted is already an integer RUF046.py:17:1: RUF046 [*] Value being casted is already an integer
| |
15 | int(math.gcd()) 16 | int(math.comb())
16 | int(math.lcm()) 17 | int(math.factorial())
17 | int(math.isqrt()) | ^^^^^^^^^^^^^^^^^^^^^ RUF046
| ^^^^^^^^^^^^^^^^^ RUF046 18 | int(math.gcd())
18 | int(math.perm()) 19 | int(math.lcm())
| |
= help: Remove unnecessary conversion to `int` = help: Remove unnecessary conversion to `int`
Safe fix Safe fix
14 14 | int(math.factorial()) 14 14 | int(int(''))
15 15 | int(math.gcd()) 15 15 |
16 16 | int(math.lcm()) 16 16 | int(math.comb())
17 |-int(math.isqrt()) 17 |-int(math.factorial())
17 |+math.isqrt() 17 |+math.factorial()
18 18 | int(math.perm()) 18 18 | int(math.gcd())
19 19 | 19 19 | int(math.lcm())
20 20 | 20 20 | int(math.isqrt())
RUF046.py:18:1: RUF046 [*] Value being casted is already an integer RUF046.py:18:1: RUF046 [*] Value being casted is already an integer
| |
16 | int(math.lcm()) 16 | int(math.comb())
17 | int(math.isqrt()) 17 | int(math.factorial())
18 | int(math.perm()) 18 | int(math.gcd())
| ^^^^^^^^^^^^^^^^ RUF046 | ^^^^^^^^^^^^^^^ RUF046
19 | int(math.lcm())
20 | int(math.isqrt())
| |
= help: Remove unnecessary conversion to `int` = help: Remove unnecessary conversion to `int`
Safe fix Safe fix
15 15 | int(math.gcd()) 15 15 |
16 16 | int(math.lcm()) 16 16 | int(math.comb())
17 17 | int(math.isqrt()) 17 17 | int(math.factorial())
18 |-int(math.perm()) 18 |-int(math.gcd())
18 |+math.perm() 18 |+math.gcd()
19 19 | 19 19 | int(math.lcm())
20 20 | 20 20 | int(math.isqrt())
21 21 | ### Unsafe 21 21 | int(math.perm())
RUF046.py:19:1: RUF046 [*] Value being casted is already an integer
|
17 | int(math.factorial())
18 | int(math.gcd())
19 | int(math.lcm())
| ^^^^^^^^^^^^^^^ RUF046
20 | int(math.isqrt())
21 | int(math.perm())
|
= help: Remove unnecessary conversion to `int`
Safe fix
16 16 | int(math.comb())
17 17 | int(math.factorial())
18 18 | int(math.gcd())
19 |-int(math.lcm())
19 |+math.lcm()
20 20 | int(math.isqrt())
21 21 | int(math.perm())
22 22 |
RUF046.py:20:1: RUF046 [*] Value being casted is already an integer
|
18 | int(math.gcd())
19 | int(math.lcm())
20 | int(math.isqrt())
| ^^^^^^^^^^^^^^^^^ RUF046
21 | int(math.perm())
|
= help: Remove unnecessary conversion to `int`
Safe fix
17 17 | int(math.factorial())
18 18 | int(math.gcd())
19 19 | int(math.lcm())
20 |-int(math.isqrt())
20 |+math.isqrt()
21 21 | int(math.perm())
22 22 |
23 23 | int(round(1, 0))
RUF046.py:21:1: RUF046 [*] Value being casted is already an integer
|
19 | int(math.lcm())
20 | int(math.isqrt())
21 | int(math.perm())
| ^^^^^^^^^^^^^^^^ RUF046
22 |
23 | int(round(1, 0))
|
= help: Remove unnecessary conversion to `int`
Safe fix
18 18 | int(math.gcd())
19 19 | int(math.lcm())
20 20 | int(math.isqrt())
21 |-int(math.perm())
21 |+math.perm()
22 22 |
23 23 | int(round(1, 0))
24 24 | int(round(1, 10))
RUF046.py:23:1: RUF046 [*] Value being casted is already an integer RUF046.py:23:1: RUF046 [*] Value being casted is already an integer
| |
21 | ### Unsafe 21 | int(math.perm())
22 | 22 |
23 | int(math.ceil()) 23 | int(round(1, 0))
| ^^^^^^^^^^^^^^^^ RUF046 | ^^^^^^^^^^^^^^^^ RUF046
24 | int(math.floor()) 24 | int(round(1, 10))
25 | int(math.trunc())
| |
= help: Remove unnecessary conversion to `int` = help: Remove unnecessary conversion to `int`
Unsafe fix Safe fix
20 20 | 20 20 | int(math.isqrt())
21 21 | ### Unsafe 21 21 | int(math.perm())
22 22 | 22 22 |
23 |-int(math.ceil()) 23 |-int(round(1, 0))
23 |+math.ceil() 23 |+round(1, 0)
24 24 | int(math.floor()) 24 24 | int(round(1, 10))
25 25 | int(math.trunc()) 25 25 |
26 26 | 26 26 | int(round(1))
RUF046.py:24:1: RUF046 [*] Value being casted is already an integer RUF046.py:24:1: RUF046 [*] Value being casted is already an integer
| |
23 | int(math.ceil()) 23 | int(round(1, 0))
24 | int(math.floor()) 24 | int(round(1, 10))
| ^^^^^^^^^^^^^^^^^ RUF046 | ^^^^^^^^^^^^^^^^^ RUF046
25 | int(math.trunc()) 25 |
26 | int(round(1))
| |
= help: Remove unnecessary conversion to `int` = help: Remove unnecessary conversion to `int`
Unsafe fix Safe fix
21 21 | ### Unsafe 21 21 | int(math.perm())
22 22 | 22 22 |
23 23 | int(math.ceil()) 23 23 | int(round(1, 0))
24 |-int(math.floor()) 24 |-int(round(1, 10))
24 |+math.floor() 24 |+round(1, 10)
25 25 | int(math.trunc()) 25 25 |
26 26 | 26 26 | int(round(1))
27 27 | 27 27 | int(round(1, None))
RUF046.py:25:1: RUF046 [*] Value being casted is already an integer RUF046.py:26:1: RUF046 [*] Value being casted is already an integer
| |
23 | int(math.ceil()) 24 | int(round(1, 10))
24 | int(math.floor()) 25 |
25 | int(math.trunc()) 26 | int(round(1))
| ^^^^^^^^^^^^^^^^^ RUF046
|
= help: Remove unnecessary conversion to `int`
Unsafe fix
22 22 |
23 23 | int(math.ceil())
24 24 | int(math.floor())
25 |-int(math.trunc())
25 |+math.trunc()
26 26 |
27 27 |
28 28 | ### `round()`
RUF046.py:31:1: RUF046 [*] Value being casted is already an integer
|
30 | ## Errors
31 | int(round(0))
| ^^^^^^^^^^^^^ RUF046 | ^^^^^^^^^^^^^ RUF046
32 | int(round(0, 0)) 27 | int(round(1, None))
33 | int(round(0, None))
| |
= help: Remove unnecessary conversion to `int` = help: Remove unnecessary conversion to `int`
Safe fix Safe fix
28 28 | ### `round()` 23 23 | int(round(1, 0))
29 29 | 24 24 | int(round(1, 10))
30 30 | ## Errors 25 25 |
31 |-int(round(0)) 26 |-int(round(1))
31 |+round(0) 26 |+round(1)
32 32 | int(round(0, 0)) 27 27 | int(round(1, None))
33 33 | int(round(0, None)) 28 28 |
34 34 | 29 29 | int(round(1.))
RUF046.py:32:1: RUF046 [*] Value being casted is already an integer RUF046.py:27:1: RUF046 [*] Value being casted is already an integer
| |
30 | ## Errors 26 | int(round(1))
31 | int(round(0)) 27 | int(round(1, None))
32 | int(round(0, 0))
| ^^^^^^^^^^^^^^^^ RUF046
33 | int(round(0, None))
|
= help: Remove unnecessary conversion to `int`
Safe fix
29 29 |
30 30 | ## Errors
31 31 | int(round(0))
32 |-int(round(0, 0))
32 |+round(0)
33 33 | int(round(0, None))
34 34 |
35 35 | int(round(0.1))
RUF046.py:33:1: RUF046 [*] Value being casted is already an integer
|
31 | int(round(0))
32 | int(round(0, 0))
33 | int(round(0, None))
| ^^^^^^^^^^^^^^^^^^^ RUF046 | ^^^^^^^^^^^^^^^^^^^ RUF046
34 | 28 |
35 | int(round(0.1)) 29 | int(round(1.))
| |
= help: Remove unnecessary conversion to `int` = help: Remove unnecessary conversion to `int`
Safe fix Safe fix
30 30 | ## Errors 24 24 | int(round(1, 10))
31 31 | int(round(0)) 25 25 |
32 32 | int(round(0, 0)) 26 26 | int(round(1))
33 |-int(round(0, None)) 27 |-int(round(1, None))
33 |+round(0) 27 |+round(1, None)
34 34 | 28 28 |
35 35 | int(round(0.1)) 29 29 | int(round(1.))
36 36 | int(round(0.1, None)) 30 30 | int(round(1., None))
RUF046.py:29:1: RUF046 [*] Value being casted is already an integer
|
27 | int(round(1, None))
28 |
29 | int(round(1.))
| ^^^^^^^^^^^^^^ RUF046
30 | int(round(1., None))
|
= help: Remove unnecessary conversion to `int`
Safe fix
26 26 | int(round(1))
27 27 | int(round(1, None))
28 28 |
29 |-int(round(1.))
29 |+round(1.)
30 30 | int(round(1., None))
31 31 |
32 32 |
RUF046.py:30:1: RUF046 [*] Value being casted is already an integer
|
29 | int(round(1.))
30 | int(round(1., None))
| ^^^^^^^^^^^^^^^^^^^^ RUF046
|
= help: Remove unnecessary conversion to `int`
Safe fix
27 27 | int(round(1, None))
28 28 |
29 29 | int(round(1.))
30 |-int(round(1., None))
30 |+round(1., None)
31 31 |
32 32 |
33 33 | ### Unsafe
RUF046.py:35:1: RUF046 [*] Value being casted is already an integer RUF046.py:35:1: RUF046 [*] Value being casted is already an integer
| |
33 | int(round(0, None)) 33 | ### Unsafe
34 | 34 |
35 | int(round(0.1)) 35 | int(math.ceil())
| ^^^^^^^^^^^^^^^ RUF046 | ^^^^^^^^^^^^^^^^ RUF046
36 | int(round(0.1, None)) 36 | int(math.floor())
37 | int(math.trunc())
| |
= help: Remove unnecessary conversion to `int` = help: Remove unnecessary conversion to `int`
Unsafe fix Unsafe fix
32 32 | int(round(0, 0)) 32 32 |
33 33 | int(round(0, None)) 33 33 | ### Unsafe
34 34 | 34 34 |
35 |-int(round(0.1)) 35 |-int(math.ceil())
35 |+round(0.1) 35 |+math.ceil()
36 36 | int(round(0.1, None)) 36 36 | int(math.floor())
37 37 | 37 37 | int(math.trunc())
38 38 | # Argument type is not checked 38 38 |
RUF046.py:36:1: RUF046 [*] Value being casted is already an integer RUF046.py:36:1: RUF046 [*] Value being casted is already an integer
| |
35 | int(round(0.1)) 35 | int(math.ceil())
36 | int(round(0.1, None)) 36 | int(math.floor())
| ^^^^^^^^^^^^^^^^^^^^^ RUF046 | ^^^^^^^^^^^^^^^^^ RUF046
37 | 37 | int(math.trunc())
38 | # Argument type is not checked
| |
= help: Remove unnecessary conversion to `int` = help: Remove unnecessary conversion to `int`
Unsafe fix Unsafe fix
33 33 | int(round(0, None)) 33 33 | ### Unsafe
34 34 | 34 34 |
35 35 | int(round(0.1)) 35 35 | int(math.ceil())
36 |-int(round(0.1, None)) 36 |-int(math.floor())
36 |+round(0.1) 36 |+math.floor()
37 37 | 37 37 | int(math.trunc())
38 38 | # Argument type is not checked 38 38 |
39 39 | foo = type("Foo", (), {"__round__": lambda self: 4.2})() 39 39 | int(round(inferred_int, 0))
RUF046.py:41:1: RUF046 [*] Value being casted is already an integer RUF046.py:37:1: RUF046 [*] Value being casted is already an integer
| |
39 | foo = type("Foo", (), {"__round__": lambda self: 4.2})() 35 | int(math.ceil())
40 | 36 | int(math.floor())
41 | int(round(foo)) 37 | int(math.trunc())
| ^^^^^^^^^^^^^^^ RUF046 | ^^^^^^^^^^^^^^^^^ RUF046
42 | int(round(foo, 0)) 38 |
43 | int(round(foo, None)) 39 | int(round(inferred_int, 0))
| |
= help: Remove unnecessary conversion to `int` = help: Remove unnecessary conversion to `int`
Unsafe fix Unsafe fix
38 38 | # Argument type is not checked 34 34 |
39 39 | foo = type("Foo", (), {"__round__": lambda self: 4.2})() 35 35 | int(math.ceil())
40 40 | 36 36 | int(math.floor())
41 |-int(round(foo)) 37 |-int(math.trunc())
41 |+round(foo) 37 |+math.trunc()
42 42 | int(round(foo, 0)) 38 38 |
43 43 | int(round(foo, None)) 39 39 | int(round(inferred_int, 0))
40 40 | int(round(inferred_int, 10))
RUF046.py:39:1: RUF046 [*] Value being casted is already an integer
|
37 | int(math.trunc())
38 |
39 | int(round(inferred_int, 0))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF046
40 | int(round(inferred_int, 10))
|
= help: Remove unnecessary conversion to `int`
Unsafe fix
36 36 | int(math.floor())
37 37 | int(math.trunc())
38 38 |
39 |-int(round(inferred_int, 0))
39 |+round(inferred_int, 0)
40 40 | int(round(inferred_int, 10))
41 41 |
42 42 | int(round(inferred_int))
RUF046.py:40:1: RUF046 [*] Value being casted is already an integer
|
39 | int(round(inferred_int, 0))
40 | int(round(inferred_int, 10))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF046
41 |
42 | int(round(inferred_int))
|
= help: Remove unnecessary conversion to `int`
Unsafe fix
37 37 | int(math.trunc())
38 38 |
39 39 | int(round(inferred_int, 0))
40 |-int(round(inferred_int, 10))
40 |+round(inferred_int, 10)
41 41 |
42 42 | int(round(inferred_int))
43 43 | int(round(inferred_int, None))
RUF046.py:42:1: RUF046 [*] Value being casted is already an integer
|
40 | int(round(inferred_int, 10))
41 |
42 | int(round(inferred_int))
| ^^^^^^^^^^^^^^^^^^^^^^^^ RUF046
43 | int(round(inferred_int, None))
|
= help: Remove unnecessary conversion to `int`
Unsafe fix
39 39 | int(round(inferred_int, 0))
40 40 | int(round(inferred_int, 10))
41 41 |
42 |-int(round(inferred_int))
42 |+round(inferred_int)
43 43 | int(round(inferred_int, None))
44 44 | 44 44 |
45 45 | int(round(inferred_float))
RUF046.py:43:1: RUF046 [*] Value being casted is already an integer RUF046.py:43:1: RUF046 [*] Value being casted is already an integer
| |
41 | int(round(foo)) 42 | int(round(inferred_int))
42 | int(round(foo, 0)) 43 | int(round(inferred_int, None))
43 | int(round(foo, None)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF046
| ^^^^^^^^^^^^^^^^^^^^^ RUF046
44 | 44 |
45 | ## No errors 45 | int(round(inferred_float))
| |
= help: Remove unnecessary conversion to `int` = help: Remove unnecessary conversion to `int`
Unsafe fix Unsafe fix
40 40 | 40 40 | int(round(inferred_int, 10))
41 41 | int(round(foo)) 41 41 |
42 42 | int(round(foo, 0)) 42 42 | int(round(inferred_int))
43 |-int(round(foo, None)) 43 |-int(round(inferred_int, None))
43 |+round(foo) 43 |+round(inferred_int, None)
44 44 | 44 44 |
45 45 | ## No errors 45 45 | int(round(inferred_float))
46 46 | int(round(0, 3.14)) 46 46 | int(round(inferred_float, None))
RUF046.py:45:1: RUF046 [*] Value being casted is already an integer
|
43 | int(round(inferred_int, None))
44 |
45 | int(round(inferred_float))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF046
46 | int(round(inferred_float, None))
|
= help: Remove unnecessary conversion to `int`
Unsafe fix
42 42 | int(round(inferred_int))
43 43 | int(round(inferred_int, None))
44 44 |
45 |-int(round(inferred_float))
45 |+round(inferred_float)
46 46 | int(round(inferred_float, None))
47 47 |
48 48 | int(round(unknown))
RUF046.py:46:1: RUF046 [*] Value being casted is already an integer
|
45 | int(round(inferred_float))
46 | int(round(inferred_float, None))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF046
47 |
48 | int(round(unknown))
|
= help: Remove unnecessary conversion to `int`
Unsafe fix
43 43 | int(round(inferred_int, None))
44 44 |
45 45 | int(round(inferred_float))
46 |-int(round(inferred_float, None))
46 |+round(inferred_float, None)
47 47 |
48 48 | int(round(unknown))
49 49 | int(round(unknown, None))
RUF046.py:48:1: RUF046 [*] Value being casted is already an integer
|
46 | int(round(inferred_float, None))
47 |
48 | int(round(unknown))
| ^^^^^^^^^^^^^^^^^^^ RUF046
49 | int(round(unknown, None))
|
= help: Remove unnecessary conversion to `int`
Unsafe fix
45 45 | int(round(inferred_float))
46 46 | int(round(inferred_float, None))
47 47 |
48 |-int(round(unknown))
48 |+round(unknown)
49 49 | int(round(unknown, None))
50 50 |
51 51 |
RUF046.py:49:1: RUF046 [*] Value being casted is already an integer
|
48 | int(round(unknown))
49 | int(round(unknown, None))
| ^^^^^^^^^^^^^^^^^^^^^^^^^ RUF046
|
= help: Remove unnecessary conversion to `int`
Unsafe fix
46 46 | int(round(inferred_float, None))
47 47 |
48 48 | int(round(unknown))
49 |-int(round(unknown, None))
49 |+round(unknown, None)
50 50 |
51 51 |
52 52 | ### No errors

View file

@ -665,6 +665,14 @@ impl BuiltinTypeChecker for IntChecker {
const EXPR_TYPE: PythonType = PythonType::Number(NumberLike::Integer); const EXPR_TYPE: PythonType = PythonType::Number(NumberLike::Integer);
} }
struct FloatChecker;
impl BuiltinTypeChecker for FloatChecker {
const BUILTIN_TYPE_NAME: &'static str = "float";
const TYPING_NAME: Option<&'static str> = None;
const EXPR_TYPE: PythonType = PythonType::Number(NumberLike::Float);
}
pub struct IoBaseChecker; pub struct IoBaseChecker;
impl TypeChecker for IoBaseChecker { impl TypeChecker for IoBaseChecker {
@ -850,6 +858,11 @@ pub fn is_int(binding: &Binding, semantic: &SemanticModel) -> bool {
check_type::<IntChecker>(binding, semantic) check_type::<IntChecker>(binding, semantic)
} }
/// Test whether the given binding can be considered an instance of `float`.
pub fn is_float(binding: &Binding, semantic: &SemanticModel) -> bool {
check_type::<FloatChecker>(binding, semantic)
}
/// Test whether the given binding can be considered a set. /// Test whether the given binding can be considered a set.
/// ///
/// For this, we check what value might be associated with it through it's initialization and /// For this, we check what value might be associated with it through it's initialization and