[ruff] Unnecessary rounding (RUF057) (#14828)

Co-authored-by: Micha Reiser <micha@reiser.io>
This commit is contained in:
InSync 2025-01-02 16:00:57 +07:00 committed by GitHub
parent f8c9665742
commit 89ea0371a4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 660 additions and 67 deletions

View file

@ -0,0 +1,59 @@
inferred_int = 1
inferred_float = 1.
round(42) # Error (safe)
round(42, None) # Error (safe)
round(42, 2) # Error (safe)
round(42, inferred_int) # Error (safe)
round(42, 3 + 4) # Error (safe)
round(42, foo) # Error (unsafe)
round(42.) # No error
round(42., None) # No error
round(42., 2) # No error
round(42., inferred_int) # No error
round(42., 3 + 4) # No error
round(42., foo) # No error
round(4 + 2) # Error (safe)
round(4 + 2, None) # Error (safe)
round(4 + 2, 2) # Error (safe)
round(4 + 2, inferred_int) # Error (safe)
round(4 + 2, 3 + 4) # Error (safe)
round(4 + 2, foo) # Error (unsafe)
round(4. + 2.) # No error
round(4. + 2., None) # No error
round(4. + 2., 2) # No error
round(4. + 2., inferred_int) # No error
round(4. + 2., 3 + 4) # No error
round(4. + 2., foo) # No error
round(inferred_int) # Error (unsafe)
round(inferred_int, None) # Error (unsafe)
round(inferred_int, 2) # Error (unsafe)
round(inferred_int, inferred_int) # Error (unsafe)
round(inferred_int, 3 + 4) # Error (unsafe)
round(inferred_int, foo) # No error
round(inferred_float) # No error
round(inferred_float, None) # No error
round(inferred_float, 2) # No error
round(inferred_float, inferred_int) # No error
round(inferred_float, 3 + 4) # No error
round(inferred_float, foo) # No error
round(lorem) # No error
round(lorem, None) # No error
round(lorem, 2) # No error
round(lorem, inferred_int) # No error
round(lorem, 3 + 4) # No error
round(lorem, foo) # No error

View file

@ -1114,6 +1114,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
if checker.enabled(Rule::FalsyDictGetFallback) { if checker.enabled(Rule::FalsyDictGetFallback) {
ruff::rules::falsy_dict_get_fallback(checker, expr); ruff::rules::falsy_dict_get_fallback(checker, expr);
} }
if checker.enabled(Rule::UnnecessaryRound) {
ruff::rules::unnecessary_round(checker, call);
}
} }
Expr::Dict(dict) => { Expr::Dict(dict) => {
if checker.any_enabled(&[ if checker.any_enabled(&[

View file

@ -992,6 +992,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Ruff, "052") => (RuleGroup::Preview, rules::ruff::rules::UsedDummyVariable), (Ruff, "052") => (RuleGroup::Preview, rules::ruff::rules::UsedDummyVariable),
(Ruff, "055") => (RuleGroup::Preview, rules::ruff::rules::UnnecessaryRegularExpression), (Ruff, "055") => (RuleGroup::Preview, rules::ruff::rules::UnnecessaryRegularExpression),
(Ruff, "056") => (RuleGroup::Preview, rules::ruff::rules::FalsyDictGetFallback), (Ruff, "056") => (RuleGroup::Preview, rules::ruff::rules::FalsyDictGetFallback),
(Ruff, "057") => (RuleGroup::Preview, rules::ruff::rules::UnnecessaryRound),
(Ruff, "100") => (RuleGroup::Stable, rules::ruff::rules::UnusedNOQA), (Ruff, "100") => (RuleGroup::Stable, rules::ruff::rules::UnusedNOQA),
(Ruff, "101") => (RuleGroup::Stable, rules::ruff::rules::RedirectedNOQA), (Ruff, "101") => (RuleGroup::Stable, rules::ruff::rules::RedirectedNOQA),

View file

@ -419,6 +419,7 @@ mod tests {
#[test_case(Rule::UnnecessaryRegularExpression, Path::new("RUF055_1.py"))] #[test_case(Rule::UnnecessaryRegularExpression, Path::new("RUF055_1.py"))]
#[test_case(Rule::UnnecessaryCastToInt, Path::new("RUF046.py"))] #[test_case(Rule::UnnecessaryCastToInt, Path::new("RUF046.py"))]
#[test_case(Rule::PytestRaisesAmbiguousPattern, Path::new("RUF043.py"))] #[test_case(Rule::PytestRaisesAmbiguousPattern, Path::new("RUF043.py"))]
#[test_case(Rule::UnnecessaryRound, Path::new("RUF057.py"))]
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> { fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!( let snapshot = format!(
"preview__{}_{}", "preview__{}_{}",

View file

@ -38,6 +38,7 @@ pub(crate) use unnecessary_iterable_allocation_for_first_element::*;
pub(crate) use unnecessary_key_check::*; pub(crate) use unnecessary_key_check::*;
pub(crate) use unnecessary_nested_literal::*; pub(crate) use unnecessary_nested_literal::*;
pub(crate) use unnecessary_regular_expression::*; pub(crate) use unnecessary_regular_expression::*;
pub(crate) use unnecessary_round::*;
pub(crate) use unraw_re_pattern::*; pub(crate) use unraw_re_pattern::*;
pub(crate) use unsafe_markup_use::*; pub(crate) use unsafe_markup_use::*;
pub(crate) use unused_async::*; pub(crate) use unused_async::*;
@ -90,6 +91,7 @@ mod unnecessary_iterable_allocation_for_first_element;
mod unnecessary_key_check; mod unnecessary_key_check;
mod unnecessary_nested_literal; mod unnecessary_nested_literal;
mod unnecessary_regular_expression; mod unnecessary_regular_expression;
mod unnecessary_round;
mod unraw_re_pattern; mod unraw_re_pattern;
mod unsafe_markup_use; mod unsafe_markup_use;
mod unused_async; mod unused_async;

View file

@ -1,9 +1,11 @@
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::rules::ruff::rules::unnecessary_round::{
rounded_and_ndigits, InferredType, NdigitsValue, RoundedValue,
};
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, ExprNumberLiteral, Number}; use ruff_python_ast::{Arguments, Expr, ExprCall};
use ruff_python_semantic::analyze::type_inference::{NumberLike, PythonType, ResolvedPythonType}; use ruff_python_semantic::analyze::type_inference::{NumberLike, PythonType, ResolvedPythonType};
use ruff_python_semantic::analyze::typing;
use ruff_python_semantic::SemanticModel; use ruff_python_semantic::SemanticModel;
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
@ -76,12 +78,13 @@ pub(crate) fn unnecessary_cast_to_int(checker: &mut Checker, call: &ExprCall) {
let fix = unwrap_int_expression(checker, call, argument, applicability); let fix = unwrap_int_expression(checker, call, argument, applicability);
let diagnostic = Diagnostic::new(UnnecessaryCastToInt, call.range); let diagnostic = Diagnostic::new(UnnecessaryCastToInt, call.range);
checker.diagnostics.push(diagnostic.with_fix(fix)); checker.diagnostics.push(diagnostic.with_fix(fix));
} }
/// Creates a fix that replaces `int(expression)` with `expression`. /// Creates a fix that replaces `int(expression)` with `expression`.
fn unwrap_int_expression( fn unwrap_int_expression(
checker: &mut Checker, checker: &Checker,
call: &ExprCall, call: &ExprCall,
argument: &Expr, argument: &Expr,
applicability: Applicability, applicability: Applicability,
@ -148,78 +151,56 @@ fn single_argument_to_int_call<'a>(
Some(argument) Some(argument)
} }
/// The type of the first argument to `round()`
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum Rounded {
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,
}
/// Determines the [`Applicability`] for a `round(..)` call. /// Determines the [`Applicability`] for a `round(..)` call.
/// ///
/// The Applicability depends on the `ndigits` and the number argument. /// The Applicability depends on the `ndigits` and the number argument.
fn round_applicability(checker: &Checker, arguments: &Arguments) -> Option<Applicability> { fn round_applicability(checker: &Checker, arguments: &Arguments) -> Option<Applicability> {
if arguments.len() > 2 { let (_rounded, rounded_value, ndigits_value) = rounded_and_ndigits(checker, arguments)?;
return None;
}
let number = arguments.find_argument_value("number", 0)?; match (rounded_value, ndigits_value) {
let ndigits = arguments.find_argument_value("ndigits", 1); // ```python
// int(round(2, 0))
// int(round(2))
// int(round(2, None))
// ```
(
RoundedValue::Int(InferredType::Equivalent),
NdigitsValue::Int(InferredType::Equivalent)
| NdigitsValue::NotGiven
| NdigitsValue::LiteralNone,
) => Some(Applicability::Safe),
let number_kind = match number { // ```python
Expr::Name(name) => { // int(round(2.0))
let semantic = checker.semantic(); // int(round(2.0, None))
// ```
(
RoundedValue::Float(InferredType::Equivalent),
NdigitsValue::NotGiven | NdigitsValue::LiteralNone,
) => Some(Applicability::Safe),
match semantic.only_binding(name).map(|id| semantic.binding(id)) { // ```python
Some(binding) if typing::is_int(binding, semantic) => Rounded::InferredInt, // a: int = 2 # or True
Some(binding) if typing::is_float(binding, semantic) => Rounded::InferredFloat, // int(round(a, 1))
_ => Rounded::Other, // int(round(a))
} // int(round(a, None))
} // ```
(
RoundedValue::Int(InferredType::AssignableTo),
NdigitsValue::Int(InferredType::Equivalent)
| NdigitsValue::NotGiven
| NdigitsValue::LiteralNone,
) => Some(Applicability::Unsafe),
Expr::NumberLiteral(ExprNumberLiteral { value, .. }) => match value { // ```python
Number::Int(..) => Rounded::LiteralInt, // int(round(2.0))
Number::Float(..) => Rounded::LiteralFloat, // int(round(2.0, None))
Number::Complex { .. } => Rounded::Other, // int(round(x))
}, // int(round(x, None))
// ```
_ => Rounded::Other, (
}; RoundedValue::Float(InferredType::AssignableTo) | RoundedValue::Other,
NdigitsValue::NotGiven | NdigitsValue::LiteralNone,
let ndigits_kind = match ndigits {
None => Ndigits::NotGiven,
Some(Expr::NoneLiteral(_)) => Ndigits::LiteralNone,
Some(Expr::NumberLiteral(ExprNumberLiteral {
value: Number::Int(..),
..
})) => Ndigits::LiteralInt,
_ => Ndigits::Other,
};
match (number_kind, ndigits_kind) {
(Rounded::LiteralInt, Ndigits::LiteralInt)
| (Rounded::LiteralInt | Rounded::LiteralFloat, Ndigits::NotGiven | Ndigits::LiteralNone) => {
Some(Applicability::Safe)
}
(Rounded::InferredInt, Ndigits::LiteralInt)
| (
Rounded::InferredInt | Rounded::InferredFloat | Rounded::Other,
Ndigits::NotGiven | Ndigits::LiteralNone,
) => Some(Applicability::Unsafe), ) => Some(Applicability::Unsafe),
_ => None, _ => None,

View file

@ -0,0 +1,201 @@
use crate::checkers::ast::Checker;
use ruff_diagnostics::{AlwaysFixableViolation, Applicability, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, ViolationMetadata};
use ruff_python_ast::{Arguments, Expr, ExprCall};
use ruff_python_semantic::analyze::type_inference::{NumberLike, PythonType, ResolvedPythonType};
use ruff_python_semantic::analyze::typing;
use ruff_text_size::Ranged;
/// ## What it does
/// Checks for `round()` calls that have no effect on the input.
///
/// ## Why is this bad?
/// Rounding a value that's already an integer is unnecessary.
/// It's clearer to use the value directly.
///
/// ## Example
///
/// ```python
/// a = round(1, 0)
/// ```
///
/// Use instead:
///
/// ```python
/// a = 1
/// ```
#[derive(ViolationMetadata)]
pub(crate) struct UnnecessaryRound;
impl AlwaysFixableViolation for UnnecessaryRound {
#[derive_message_formats]
fn message(&self) -> String {
"Value being rounded is already an integer".to_string()
}
fn fix_title(&self) -> String {
"Remove unnecessary `round` call".to_string()
}
}
/// RUF057
pub(crate) fn unnecessary_round(checker: &mut Checker, call: &ExprCall) {
let arguments = &call.arguments;
if !checker.semantic().match_builtin_expr(&call.func, "round") {
return;
}
let Some((rounded, rounded_value, ndigits_value)) = rounded_and_ndigits(checker, arguments)
else {
return;
};
let applicability = match (rounded_value, ndigits_value) {
// ```python
// rounded(1, unknown)
// ```
(RoundedValue::Int(InferredType::Equivalent), NdigitsValue::Other) => Applicability::Unsafe,
(_, NdigitsValue::Other) => return,
// ```python
// some_int: int
//
// rounded(1)
// rounded(1, None)
// rounded(1, 42)
// rounded(1, 4 + 2)
// rounded(1, some_int)
// ```
(RoundedValue::Int(InferredType::Equivalent), _) => Applicability::Safe,
// ```python
// some_int: int
// some_other_int: int
//
// rounded(some_int)
// rounded(some_int, None)
// rounded(some_int, 42)
// rounded(some_int, 4 + 2)
// rounded(some_int, some_other_int)
// ```
(RoundedValue::Int(InferredType::AssignableTo), _) => Applicability::Unsafe,
_ => return,
};
let edit = unwrap_round_call(checker, call, rounded);
let fix = Fix::applicable_edit(edit, applicability);
let diagnostic = Diagnostic::new(UnnecessaryRound, call.range);
checker.diagnostics.push(diagnostic.with_fix(fix));
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum InferredType {
/// The value is an exact instance of the type in question.
Equivalent,
/// The value is an instance of the type in question or a subtype thereof.
AssignableTo,
}
/// The type of the first argument to `round()`
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum RoundedValue {
Int(InferredType),
Float(InferredType),
Other,
}
/// The type of the second argument to `round()`
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum NdigitsValue {
NotGiven,
LiteralNone,
Int(InferredType),
Other,
}
/// Extracts the rounded and `ndigits` values from `arguments`.
///
/// Returns a tripled where the first element is the rounded value's expression, the second is the rounded value,
///and the third is the `ndigits` value.
pub(super) fn rounded_and_ndigits<'a>(
checker: &Checker,
arguments: &'a Arguments,
) -> Option<(&'a Expr, RoundedValue, NdigitsValue)> {
if arguments.len() > 2 {
return None;
}
let rounded = arguments.find_argument_value("number", 0)?;
let ndigits = arguments.find_argument_value("ndigits", 1);
let rounded_kind = match rounded {
Expr::Name(name) => {
let semantic = checker.semantic();
match semantic.only_binding(name).map(|id| semantic.binding(id)) {
Some(binding) if typing::is_int(binding, semantic) => {
RoundedValue::Int(InferredType::AssignableTo)
}
Some(binding) if typing::is_float(binding, semantic) => {
RoundedValue::Float(InferredType::AssignableTo)
}
_ => RoundedValue::Other,
}
}
_ => match ResolvedPythonType::from(rounded) {
ResolvedPythonType::Atom(PythonType::Number(NumberLike::Integer)) => {
RoundedValue::Int(InferredType::Equivalent)
}
ResolvedPythonType::Atom(PythonType::Number(NumberLike::Float)) => {
RoundedValue::Float(InferredType::Equivalent)
}
_ => RoundedValue::Other,
},
};
let ndigits_kind = match ndigits {
None => NdigitsValue::NotGiven,
Some(Expr::NoneLiteral(_)) => NdigitsValue::LiteralNone,
Some(Expr::Name(name)) => {
let semantic = checker.semantic();
match semantic.only_binding(name).map(|id| semantic.binding(id)) {
Some(binding) if typing::is_int(binding, semantic) => {
NdigitsValue::Int(InferredType::AssignableTo)
}
_ => NdigitsValue::Other,
}
}
Some(ndigits) => match ResolvedPythonType::from(ndigits) {
ResolvedPythonType::Atom(PythonType::Number(NumberLike::Integer)) => {
NdigitsValue::Int(InferredType::Equivalent)
}
_ => NdigitsValue::Other,
},
};
Some((rounded, rounded_kind, ndigits_kind))
}
fn unwrap_round_call(checker: &Checker, call: &ExprCall, rounded: &Expr) -> Edit {
let (locator, semantic) = (checker.locator(), checker.semantic());
let rounded_expr = locator.slice(rounded.range());
let has_parent_expr = semantic.current_expression_parent().is_some();
let new_content = if has_parent_expr || rounded.is_named_expr() {
format!("({rounded_expr})")
} else {
rounded_expr.to_string()
};
Edit::range_replacement(new_content, call.range)
}

View file

@ -0,0 +1,344 @@
---
source: crates/ruff_linter/src/rules/ruff/mod.rs
snapshot_kind: text
---
RUF057.py:6:1: RUF057 [*] Value being rounded is already an integer
|
6 | round(42) # Error (safe)
| ^^^^^^^^^ RUF057
7 | round(42, None) # Error (safe)
8 | round(42, 2) # Error (safe)
|
= help: Remove unnecessary `round` call
Safe fix
3 3 |
4 4 |
5 5 |
6 |-round(42) # Error (safe)
6 |+42 # Error (safe)
7 7 | round(42, None) # Error (safe)
8 8 | round(42, 2) # Error (safe)
9 9 | round(42, inferred_int) # Error (safe)
RUF057.py:7:1: RUF057 [*] Value being rounded is already an integer
|
6 | round(42) # Error (safe)
7 | round(42, None) # Error (safe)
| ^^^^^^^^^^^^^^^ RUF057
8 | round(42, 2) # Error (safe)
9 | round(42, inferred_int) # Error (safe)
|
= help: Remove unnecessary `round` call
Safe fix
4 4 |
5 5 |
6 6 | round(42) # Error (safe)
7 |-round(42, None) # Error (safe)
7 |+42 # Error (safe)
8 8 | round(42, 2) # Error (safe)
9 9 | round(42, inferred_int) # Error (safe)
10 10 | round(42, 3 + 4) # Error (safe)
RUF057.py:8:1: RUF057 [*] Value being rounded is already an integer
|
6 | round(42) # Error (safe)
7 | round(42, None) # Error (safe)
8 | round(42, 2) # Error (safe)
| ^^^^^^^^^^^^ RUF057
9 | round(42, inferred_int) # Error (safe)
10 | round(42, 3 + 4) # Error (safe)
|
= help: Remove unnecessary `round` call
Safe fix
5 5 |
6 6 | round(42) # Error (safe)
7 7 | round(42, None) # Error (safe)
8 |-round(42, 2) # Error (safe)
8 |+42 # Error (safe)
9 9 | round(42, inferred_int) # Error (safe)
10 10 | round(42, 3 + 4) # Error (safe)
11 11 | round(42, foo) # Error (unsafe)
RUF057.py:9:1: RUF057 [*] Value being rounded is already an integer
|
7 | round(42, None) # Error (safe)
8 | round(42, 2) # Error (safe)
9 | round(42, inferred_int) # Error (safe)
| ^^^^^^^^^^^^^^^^^^^^^^^ RUF057
10 | round(42, 3 + 4) # Error (safe)
11 | round(42, foo) # Error (unsafe)
|
= help: Remove unnecessary `round` call
Safe fix
6 6 | round(42) # Error (safe)
7 7 | round(42, None) # Error (safe)
8 8 | round(42, 2) # Error (safe)
9 |-round(42, inferred_int) # Error (safe)
9 |+42 # Error (safe)
10 10 | round(42, 3 + 4) # Error (safe)
11 11 | round(42, foo) # Error (unsafe)
12 12 |
RUF057.py:10:1: RUF057 [*] Value being rounded is already an integer
|
8 | round(42, 2) # Error (safe)
9 | round(42, inferred_int) # Error (safe)
10 | round(42, 3 + 4) # Error (safe)
| ^^^^^^^^^^^^^^^^ RUF057
11 | round(42, foo) # Error (unsafe)
|
= help: Remove unnecessary `round` call
Safe fix
7 7 | round(42, None) # Error (safe)
8 8 | round(42, 2) # Error (safe)
9 9 | round(42, inferred_int) # Error (safe)
10 |-round(42, 3 + 4) # Error (safe)
10 |+42 # Error (safe)
11 11 | round(42, foo) # Error (unsafe)
12 12 |
13 13 |
RUF057.py:11:1: RUF057 [*] Value being rounded is already an integer
|
9 | round(42, inferred_int) # Error (safe)
10 | round(42, 3 + 4) # Error (safe)
11 | round(42, foo) # Error (unsafe)
| ^^^^^^^^^^^^^^ RUF057
|
= help: Remove unnecessary `round` call
Unsafe fix
8 8 | round(42, 2) # Error (safe)
9 9 | round(42, inferred_int) # Error (safe)
10 10 | round(42, 3 + 4) # Error (safe)
11 |-round(42, foo) # Error (unsafe)
11 |+42 # Error (unsafe)
12 12 |
13 13 |
14 14 | round(42.) # No error
RUF057.py:22:1: RUF057 [*] Value being rounded is already an integer
|
22 | round(4 + 2) # Error (safe)
| ^^^^^^^^^^^^ RUF057
23 | round(4 + 2, None) # Error (safe)
24 | round(4 + 2, 2) # Error (safe)
|
= help: Remove unnecessary `round` call
Safe fix
19 19 | round(42., foo) # No error
20 20 |
21 21 |
22 |-round(4 + 2) # Error (safe)
22 |+4 + 2 # Error (safe)
23 23 | round(4 + 2, None) # Error (safe)
24 24 | round(4 + 2, 2) # Error (safe)
25 25 | round(4 + 2, inferred_int) # Error (safe)
RUF057.py:23:1: RUF057 [*] Value being rounded is already an integer
|
22 | round(4 + 2) # Error (safe)
23 | round(4 + 2, None) # Error (safe)
| ^^^^^^^^^^^^^^^^^^ RUF057
24 | round(4 + 2, 2) # Error (safe)
25 | round(4 + 2, inferred_int) # Error (safe)
|
= help: Remove unnecessary `round` call
Safe fix
20 20 |
21 21 |
22 22 | round(4 + 2) # Error (safe)
23 |-round(4 + 2, None) # Error (safe)
23 |+4 + 2 # Error (safe)
24 24 | round(4 + 2, 2) # Error (safe)
25 25 | round(4 + 2, inferred_int) # Error (safe)
26 26 | round(4 + 2, 3 + 4) # Error (safe)
RUF057.py:24:1: RUF057 [*] Value being rounded is already an integer
|
22 | round(4 + 2) # Error (safe)
23 | round(4 + 2, None) # Error (safe)
24 | round(4 + 2, 2) # Error (safe)
| ^^^^^^^^^^^^^^^ RUF057
25 | round(4 + 2, inferred_int) # Error (safe)
26 | round(4 + 2, 3 + 4) # Error (safe)
|
= help: Remove unnecessary `round` call
Safe fix
21 21 |
22 22 | round(4 + 2) # Error (safe)
23 23 | round(4 + 2, None) # Error (safe)
24 |-round(4 + 2, 2) # Error (safe)
24 |+4 + 2 # Error (safe)
25 25 | round(4 + 2, inferred_int) # Error (safe)
26 26 | round(4 + 2, 3 + 4) # Error (safe)
27 27 | round(4 + 2, foo) # Error (unsafe)
RUF057.py:25:1: RUF057 [*] Value being rounded is already an integer
|
23 | round(4 + 2, None) # Error (safe)
24 | round(4 + 2, 2) # Error (safe)
25 | round(4 + 2, inferred_int) # Error (safe)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF057
26 | round(4 + 2, 3 + 4) # Error (safe)
27 | round(4 + 2, foo) # Error (unsafe)
|
= help: Remove unnecessary `round` call
Safe fix
22 22 | round(4 + 2) # Error (safe)
23 23 | round(4 + 2, None) # Error (safe)
24 24 | round(4 + 2, 2) # Error (safe)
25 |-round(4 + 2, inferred_int) # Error (safe)
25 |+4 + 2 # Error (safe)
26 26 | round(4 + 2, 3 + 4) # Error (safe)
27 27 | round(4 + 2, foo) # Error (unsafe)
28 28 |
RUF057.py:26:1: RUF057 [*] Value being rounded is already an integer
|
24 | round(4 + 2, 2) # Error (safe)
25 | round(4 + 2, inferred_int) # Error (safe)
26 | round(4 + 2, 3 + 4) # Error (safe)
| ^^^^^^^^^^^^^^^^^^^ RUF057
27 | round(4 + 2, foo) # Error (unsafe)
|
= help: Remove unnecessary `round` call
Safe fix
23 23 | round(4 + 2, None) # Error (safe)
24 24 | round(4 + 2, 2) # Error (safe)
25 25 | round(4 + 2, inferred_int) # Error (safe)
26 |-round(4 + 2, 3 + 4) # Error (safe)
26 |+4 + 2 # Error (safe)
27 27 | round(4 + 2, foo) # Error (unsafe)
28 28 |
29 29 |
RUF057.py:27:1: RUF057 [*] Value being rounded is already an integer
|
25 | round(4 + 2, inferred_int) # Error (safe)
26 | round(4 + 2, 3 + 4) # Error (safe)
27 | round(4 + 2, foo) # Error (unsafe)
| ^^^^^^^^^^^^^^^^^ RUF057
|
= help: Remove unnecessary `round` call
Unsafe fix
24 24 | round(4 + 2, 2) # Error (safe)
25 25 | round(4 + 2, inferred_int) # Error (safe)
26 26 | round(4 + 2, 3 + 4) # Error (safe)
27 |-round(4 + 2, foo) # Error (unsafe)
27 |+4 + 2 # Error (unsafe)
28 28 |
29 29 |
30 30 | round(4. + 2.) # No error
RUF057.py:38:1: RUF057 [*] Value being rounded is already an integer
|
38 | round(inferred_int) # Error (unsafe)
| ^^^^^^^^^^^^^^^^^^^ RUF057
39 | round(inferred_int, None) # Error (unsafe)
40 | round(inferred_int, 2) # Error (unsafe)
|
= help: Remove unnecessary `round` call
Unsafe fix
35 35 | round(4. + 2., foo) # No error
36 36 |
37 37 |
38 |-round(inferred_int) # Error (unsafe)
38 |+inferred_int # Error (unsafe)
39 39 | round(inferred_int, None) # Error (unsafe)
40 40 | round(inferred_int, 2) # Error (unsafe)
41 41 | round(inferred_int, inferred_int) # Error (unsafe)
RUF057.py:39:1: RUF057 [*] Value being rounded is already an integer
|
38 | round(inferred_int) # Error (unsafe)
39 | round(inferred_int, None) # Error (unsafe)
| ^^^^^^^^^^^^^^^^^^^^^^^^^ RUF057
40 | round(inferred_int, 2) # Error (unsafe)
41 | round(inferred_int, inferred_int) # Error (unsafe)
|
= help: Remove unnecessary `round` call
Unsafe fix
36 36 |
37 37 |
38 38 | round(inferred_int) # Error (unsafe)
39 |-round(inferred_int, None) # Error (unsafe)
39 |+inferred_int # Error (unsafe)
40 40 | round(inferred_int, 2) # Error (unsafe)
41 41 | round(inferred_int, inferred_int) # Error (unsafe)
42 42 | round(inferred_int, 3 + 4) # Error (unsafe)
RUF057.py:40:1: RUF057 [*] Value being rounded is already an integer
|
38 | round(inferred_int) # Error (unsafe)
39 | round(inferred_int, None) # Error (unsafe)
40 | round(inferred_int, 2) # Error (unsafe)
| ^^^^^^^^^^^^^^^^^^^^^^ RUF057
41 | round(inferred_int, inferred_int) # Error (unsafe)
42 | round(inferred_int, 3 + 4) # Error (unsafe)
|
= help: Remove unnecessary `round` call
Unsafe fix
37 37 |
38 38 | round(inferred_int) # Error (unsafe)
39 39 | round(inferred_int, None) # Error (unsafe)
40 |-round(inferred_int, 2) # Error (unsafe)
40 |+inferred_int # Error (unsafe)
41 41 | round(inferred_int, inferred_int) # Error (unsafe)
42 42 | round(inferred_int, 3 + 4) # Error (unsafe)
43 43 | round(inferred_int, foo) # No error
RUF057.py:41:1: RUF057 [*] Value being rounded is already an integer
|
39 | round(inferred_int, None) # Error (unsafe)
40 | round(inferred_int, 2) # Error (unsafe)
41 | round(inferred_int, inferred_int) # Error (unsafe)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF057
42 | round(inferred_int, 3 + 4) # Error (unsafe)
43 | round(inferred_int, foo) # No error
|
= help: Remove unnecessary `round` call
Unsafe fix
38 38 | round(inferred_int) # Error (unsafe)
39 39 | round(inferred_int, None) # Error (unsafe)
40 40 | round(inferred_int, 2) # Error (unsafe)
41 |-round(inferred_int, inferred_int) # Error (unsafe)
41 |+inferred_int # Error (unsafe)
42 42 | round(inferred_int, 3 + 4) # Error (unsafe)
43 43 | round(inferred_int, foo) # No error
44 44 |
RUF057.py:42:1: RUF057 [*] Value being rounded is already an integer
|
40 | round(inferred_int, 2) # Error (unsafe)
41 | round(inferred_int, inferred_int) # Error (unsafe)
42 | round(inferred_int, 3 + 4) # Error (unsafe)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF057
43 | round(inferred_int, foo) # No error
|
= help: Remove unnecessary `round` call
Unsafe fix
39 39 | round(inferred_int, None) # Error (unsafe)
40 40 | round(inferred_int, 2) # Error (unsafe)
41 41 | round(inferred_int, inferred_int) # Error (unsafe)
42 |-round(inferred_int, 3 + 4) # Error (unsafe)
42 |+inferred_int # Error (unsafe)
43 43 | round(inferred_int, foo) # No error
44 44 |
45 45 |

1
ruff.schema.json generated
View file

@ -3873,6 +3873,7 @@
"RUF052", "RUF052",
"RUF055", "RUF055",
"RUF056", "RUF056",
"RUF057",
"RUF1", "RUF1",
"RUF10", "RUF10",
"RUF100", "RUF100",