Skip to content

Commit

Permalink
[ruff] Avoid reporting when ndigits is possibly negative (`RUF057…
Browse files Browse the repository at this point in the history
…`) (#15234)
  • Loading branch information
InSyncWithFoo authored Jan 3, 2025
1 parent 75015b0 commit 842f882
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 285 deletions.
23 changes: 15 additions & 8 deletions crates/ruff_linter/resources/test/fixtures/ruff/RUF057.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@
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, -2) # No error
round(42, inferred_int) # No error
round(42, 3 + 4) # No error
round(42, foo) # No error


round(42.) # No error
round(42., None) # No error
round(42., 2) # No error
round(42., -2) # No error
round(42., inferred_int) # No error
round(42., 3 + 4) # No error
round(42., foo) # No error
Expand All @@ -22,14 +24,16 @@
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, -2) # No error
round(4 + 2, inferred_int) # No error
round(4 + 2, 3 + 4) # No error
round(4 + 2, foo) # No error


round(4. + 2.) # No error
round(4. + 2., None) # No error
round(4. + 2., 2) # 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
Expand All @@ -38,14 +42,16 @@
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, -2) # No error
round(inferred_int, inferred_int) # No error
round(inferred_int, 3 + 4) # No error
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, -2) # No error
round(inferred_float, inferred_int) # No error
round(inferred_float, 3 + 4) # No error
round(inferred_float, foo) # No error
Expand All @@ -54,6 +60,7 @@
round(lorem) # No error
round(lorem, None) # No error
round(lorem, 2) # No error
round(lorem, -2) # No error
round(lorem, inferred_int) # No error
round(lorem, 3 + 4) # No error
round(lorem, foo) # No error
23 changes: 12 additions & 11 deletions crates/ruff_linter/src/rules/ruff/rules/unnecessary_cast_to_int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,37 +179,38 @@ fn round_applicability(arguments: &Arguments, semantic: &SemanticModel) -> Optio

match (rounded_value, ndigits_value) {
// ```python
// int(round(2, -1))
// int(round(2, 0))
// int(round(2))
// int(round(2, None))
// ```
(
RoundedValue::Int(InferredType::Equivalent),
NdigitsValue::Int(InferredType::Equivalent)
| NdigitsValue::NotGiven
| NdigitsValue::LiteralNone,
NdigitsValue::LiteralInt { .. }
| NdigitsValue::Int(InferredType::Equivalent)
| NdigitsValue::NotGivenOrNone,
) => Some(Applicability::Safe),

// ```python
// int(round(2.0))
// int(round(2.0, None))
// ```
(
RoundedValue::Float(InferredType::Equivalent),
NdigitsValue::NotGiven | NdigitsValue::LiteralNone,
) => Some(Applicability::Safe),
(RoundedValue::Float(InferredType::Equivalent), NdigitsValue::NotGivenOrNone) => {
Some(Applicability::Safe)
}

// ```python
// a: int = 2 # or True
// int(round(a, -2))
// int(round(a, 1))
// int(round(a))
// int(round(a, None))
// ```
(
RoundedValue::Int(InferredType::AssignableTo),
NdigitsValue::Int(InferredType::Equivalent)
| NdigitsValue::NotGiven
| NdigitsValue::LiteralNone,
NdigitsValue::LiteralInt { .. }
| NdigitsValue::Int(InferredType::Equivalent)
| NdigitsValue::NotGivenOrNone,
) => Some(Applicability::Unsafe),

// ```python
Expand All @@ -220,7 +221,7 @@ fn round_applicability(arguments: &Arguments, semantic: &SemanticModel) -> Optio
// ```
(
RoundedValue::Float(InferredType::AssignableTo) | RoundedValue::Other,
NdigitsValue::NotGiven | NdigitsValue::LiteralNone,
NdigitsValue::NotGivenOrNone,
) => Some(Applicability::Unsafe),

_ => None,
Expand Down
37 changes: 23 additions & 14 deletions crates/ruff_linter/src/rules/ruff/rules/unnecessary_round.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::checkers::ast::Checker;
use crate::Locator;
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_ast::{Arguments, Expr, ExprCall, ExprNumberLiteral, Number};
use ruff_python_semantic::analyze::type_inference::{NumberLike, PythonType, ResolvedPythonType};
use ruff_python_semantic::analyze::typing;
use ruff_python_semantic::SemanticModel;
Expand Down Expand Up @@ -52,14 +52,14 @@ pub(crate) fn unnecessary_round(checker: &mut Checker, call: &ExprCall) {
return;
};

let applicability = match (rounded_value, ndigits_value) {
// ```python
// rounded(1, unknown)
// ```
(RoundedValue::Int(InferredType::Equivalent), NdigitsValue::Other) => Applicability::Unsafe,

(_, NdigitsValue::Other) => return,
if !matches!(
ndigits_value,
NdigitsValue::NotGivenOrNone | NdigitsValue::LiteralInt { is_negative: false }
) {
return;
}

let applicability = match rounded_value {
// ```python
// some_int: int
//
Expand All @@ -69,7 +69,7 @@ pub(crate) fn unnecessary_round(checker: &mut Checker, call: &ExprCall) {
// rounded(1, 4 + 2)
// rounded(1, some_int)
// ```
(RoundedValue::Int(InferredType::Equivalent), _) => Applicability::Safe,
RoundedValue::Int(InferredType::Equivalent) => Applicability::Safe,

// ```python
// some_int: int
Expand All @@ -81,7 +81,7 @@ pub(crate) fn unnecessary_round(checker: &mut Checker, call: &ExprCall) {
// rounded(some_int, 4 + 2)
// rounded(some_int, some_other_int)
// ```
(RoundedValue::Int(InferredType::AssignableTo), _) => Applicability::Unsafe,
RoundedValue::Int(InferredType::AssignableTo) => Applicability::Unsafe,

_ => return,
};
Expand Down Expand Up @@ -113,8 +113,8 @@ pub(super) enum RoundedValue {
/// The type of the second argument to `round()`
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum NdigitsValue {
NotGiven,
LiteralNone,
NotGivenOrNone,
LiteralInt { is_negative: bool },
Int(InferredType),
Other,
}
Expand Down Expand Up @@ -157,8 +157,7 @@ pub(super) fn rounded_and_ndigits<'a>(
};

let ndigits_kind = match ndigits {
None => NdigitsValue::NotGiven,
Some(Expr::NoneLiteral(_)) => NdigitsValue::LiteralNone,
None | Some(Expr::NoneLiteral(_)) => NdigitsValue::NotGivenOrNone,

Some(Expr::Name(name)) => {
match semantic.only_binding(name).map(|id| semantic.binding(id)) {
Expand All @@ -169,6 +168,16 @@ pub(super) fn rounded_and_ndigits<'a>(
}
}

Some(Expr::NumberLiteral(ExprNumberLiteral {
value: Number::Int(int),
..
})) => match int.as_i64() {
None => NdigitsValue::Int(InferredType::Equivalent),
Some(value) => NdigitsValue::LiteralInt {
is_negative: value < 0,
},
},

Some(ndigits) => match ResolvedPythonType::from(ndigits) {
ResolvedPythonType::Atom(PythonType::Number(NumberLike::Integer)) => {
NdigitsValue::Int(InferredType::Equivalent)
Expand Down
Loading

0 comments on commit 842f882

Please sign in to comment.