Skip to content

Commit

Permalink
[ruff] Do not simplify round() calls (RUF046)
Browse files Browse the repository at this point in the history
  • Loading branch information
InSyncWithFoo committed Dec 8, 2024
1 parent 269e47b commit b9d18a5
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 84 deletions.
85 changes: 8 additions & 77 deletions crates/ruff_linter/src/rules/ruff/rules/unnecessary_cast_to_int.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use crate::checkers::ast::Checker;
use ruff_diagnostics::{AlwaysFixableViolation, Applicability, Diagnostic, Edit, Fix};
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, ViolationMetadata};
use ruff_python_ast::{Arguments, Expr, ExprCall, ExprName, ExprNumberLiteral, Number};
use ruff_python_semantic::analyze::typing;
use ruff_python_ast::{Expr, ExprCall};
use ruff_python_semantic::SemanticModel;
use ruff_text_size::TextRange;

Expand Down Expand Up @@ -58,7 +57,7 @@ pub(crate) fn unnecessary_cast_to_int(checker: &mut Checker, call: &ExprCall) {
return;
};

let (func, arguments) = (&inner_call.func, &inner_call.arguments);
let func = &inner_call.func;
let (outer_range, inner_range) = (call.range, inner_call.range);

let Some(qualified_name) = checker.semantic().resolve_qualified_name(func) else {
Expand All @@ -72,26 +71,17 @@ pub(crate) fn unnecessary_cast_to_int(checker: &mut Checker, call: &ExprCall) {
Fix::safe_edit(replace_with_inner(checker, outer_range, inner_range))
}

// Depends on `ndigits` and `number.__round__`
["" | "builtins", "round"] => {
if let Some(fix) = replace_with_shortened_round_call(checker, outer_range, arguments) {
fix
} else {
return;
}
}

// Depends on `__ceil__`/`__floor__`/`__trunc__`
["math", "ceil" | "floor" | "trunc"] => {
// Depends on `__ceil__`/`__floor__`/`__trunc__`/`__round__`
["math", "ceil" | "floor" | "trunc"] | ["" | "builtins", "round"] => {
Fix::unsafe_edit(replace_with_inner(checker, outer_range, inner_range))
}

_ => return,
};

checker
.diagnostics
.push(Diagnostic::new(UnnecessaryCastToInt, call.range).with_fix(fix));
let diagnostic = Diagnostic::new(UnnecessaryCastToInt, call.range);

checker.diagnostics.push(diagnostic.with_fix(fix));
}

fn single_argument_to_int_call<'a>(
Expand All @@ -117,65 +107,6 @@ fn single_argument_to_int_call<'a>(
Some(argument)
}

/// Returns an [`Edit`] when the call is of any of the forms:
/// * `round(integer)`, `round(integer, 0)`, `round(integer, None)`
/// * `round(whatever)`, `round(whatever, None)`
fn replace_with_shortened_round_call(
checker: &Checker,
outer_range: TextRange,
arguments: &Arguments,
) -> Option<Fix> {
if arguments.len() > 2 {
return None;
}

let number = arguments.find_argument("number", 0)?;
let ndigits = arguments.find_argument("ndigits", 1);

let number_is_int = match number {
Expr::Name(name) => is_int(checker.semantic(), name),
Expr::NumberLiteral(ExprNumberLiteral { value, .. }) => matches!(value, Number::Int(..)),
_ => false,
};

match ndigits {
Some(Expr::NumberLiteral(ExprNumberLiteral { value, .. }))
if is_literal_zero(value) && number_is_int => {}
Some(Expr::NoneLiteral(_)) | None => {}
_ => return None,
};

let number_expr = checker.locator().slice(number);
let new_content = format!("round({number_expr})");

let applicability = if number_is_int {
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 {
let inner_expr = checker.locator().slice(inner_range);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ RUF046.py:31:1: RUF046 [*] Value being casted is already an integer
|
= help: Remove unnecessary conversion to `int`

Safe fix
Unsafe fix
28 28 | ### `round()`
29 29 |
30 30 | ## Errors
Expand All @@ -316,12 +316,12 @@ RUF046.py:32:1: RUF046 [*] Value being casted is already an integer
|
= help: Remove unnecessary conversion to `int`

Safe fix
Unsafe fix
29 29 |
30 30 | ## Errors
31 31 | int(round(0))
32 |-int(round(0, 0))
32 |+round(0)
32 |+round(0, 0)
33 33 | int(round(0, None))
34 34 |
35 35 | int(round(0.1))
Expand All @@ -337,12 +337,12 @@ RUF046.py:33:1: RUF046 [*] Value being casted is already an integer
|
= help: Remove unnecessary conversion to `int`

Safe fix
Unsafe fix
30 30 | ## Errors
31 31 | int(round(0))
32 32 | int(round(0, 0))
33 |-int(round(0, None))
33 |+round(0)
33 |+round(0, None)
34 34 |
35 35 | int(round(0.1))
36 36 | int(round(0.1, None))
Expand Down Expand Up @@ -382,7 +382,7 @@ RUF046.py:36:1: RUF046 [*] Value being casted is already an integer
34 34 |
35 35 | int(round(0.1))
36 |-int(round(0.1, None))
36 |+round(0.1)
36 |+round(0.1, None)
37 37 |
38 38 | # Argument type is not checked
39 39 | foo = type("Foo", (), {"__round__": lambda self: 4.2})()
Expand All @@ -408,6 +408,25 @@ RUF046.py:41:1: RUF046 [*] Value being casted is already an integer
43 43 | int(round(foo, None))
44 44 |

RUF046.py:42:1: RUF046 [*] Value being casted is already an integer
|
41 | int(round(foo))
42 | int(round(foo, 0))
| ^^^^^^^^^^^^^^^^^^ RUF046
43 | int(round(foo, None))
|
= help: Remove unnecessary conversion to `int`

Unsafe fix
39 39 | foo = type("Foo", (), {"__round__": lambda self: 4.2})()
40 40 |
41 41 | int(round(foo))
42 |-int(round(foo, 0))
42 |+round(foo, 0)
43 43 | int(round(foo, None))
44 44 |
45 45 | ## No errors

RUF046.py:43:1: RUF046 [*] Value being casted is already an integer
|
41 | int(round(foo))
Expand All @@ -424,7 +443,82 @@ RUF046.py:43:1: RUF046 [*] Value being casted is already an integer
41 41 | int(round(foo))
42 42 | int(round(foo, 0))
43 |-int(round(foo, None))
43 |+round(foo)
43 |+round(foo, None)
44 44 |
45 45 | ## No errors
46 46 | int(round(0, 3.14))

RUF046.py:46:1: RUF046 [*] Value being casted is already an integer
|
45 | ## No errors
46 | int(round(0, 3.14))
| ^^^^^^^^^^^^^^^^^^^ RUF046
47 | int(round(0, non_literal))
48 | int(round(0, 0), base)
|
= help: Remove unnecessary conversion to `int`

Unsafe fix
43 43 | int(round(foo, None))
44 44 |
45 45 | ## No errors
46 |-int(round(0, 3.14))
46 |+round(0, 3.14)
47 47 | int(round(0, non_literal))
48 48 | int(round(0, 0), base)
49 49 | int(round(0, 0, extra=keyword))

RUF046.py:47:1: RUF046 [*] Value being casted is already an integer
|
45 | ## No errors
46 | int(round(0, 3.14))
47 | int(round(0, non_literal))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF046
48 | int(round(0, 0), base)
49 | int(round(0, 0, extra=keyword))
|
= help: Remove unnecessary conversion to `int`

Unsafe fix
44 44 |
45 45 | ## No errors
46 46 | int(round(0, 3.14))
47 |-int(round(0, non_literal))
47 |+round(0, non_literal)
48 48 | int(round(0, 0), base)
49 49 | int(round(0, 0, extra=keyword))
50 50 | int(round(0.1, 0))

RUF046.py:49:1: RUF046 [*] Value being casted is already an integer
|
47 | int(round(0, non_literal))
48 | int(round(0, 0), base)
49 | int(round(0, 0, extra=keyword))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF046
50 | int(round(0.1, 0))
|
= help: Remove unnecessary conversion to `int`

Unsafe fix
46 46 | int(round(0, 3.14))
47 47 | int(round(0, non_literal))
48 48 | int(round(0, 0), base)
49 |-int(round(0, 0, extra=keyword))
49 |+round(0, 0, extra=keyword)
50 50 | int(round(0.1, 0))

RUF046.py:50:1: RUF046 [*] Value being casted is already an integer
|
48 | int(round(0, 0), base)
49 | int(round(0, 0, extra=keyword))
50 | int(round(0.1, 0))
| ^^^^^^^^^^^^^^^^^^ RUF046
|
= help: Remove unnecessary conversion to `int`

Unsafe fix
47 47 | int(round(0, non_literal))
48 48 | int(round(0, 0), base)
49 49 | int(round(0, 0, extra=keyword))
50 |-int(round(0.1, 0))
50 |+round(0.1, 0)

0 comments on commit b9d18a5

Please sign in to comment.