Skip to content

Commit

Permalink
Avoid syntax error when removing int over multiple lines
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Jan 2, 2025
1 parent 7671a3b commit be65404
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 18 deletions.
6 changes: 6 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/ruff/RUF046.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,9 @@ async def f():
int(1 @ 1)

int(1. if ... else .2)

int(1 +
1)

int(round(1,
0))
73 changes: 56 additions & 17 deletions crates/ruff_linter/src/rules/ruff/rules/unnecessary_cast_to_int.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
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_macros::{derive_message_formats, ViolationMetadata};
use ruff_python_ast::parenthesize::parenthesized_range;
use ruff_python_ast::{Arguments, Expr, ExprCall};
use ruff_python_semantic::analyze::type_inference::{NumberLike, PythonType, ResolvedPythonType};
use ruff_python_semantic::SemanticModel;
use ruff_python_trivia::CommentRanges;
use ruff_source_file::LineRanges;
use ruff_text_size::Ranged;

use crate::checkers::ast::Checker;
use crate::rules::ruff::rules::unnecessary_round::{
rounded_and_ndigits, InferredType, NdigitsValue, RoundedValue,
};
use crate::Locator;

/// ## What it does
/// Checks for `int` conversions of values that are already integers.
///
Expand Down Expand Up @@ -76,31 +81,48 @@ pub(crate) fn unnecessary_cast_to_int(checker: &mut Checker, call: &ExprCall) {
return;
};

let fix = unwrap_int_expression(checker, call, argument, applicability);
let diagnostic = Diagnostic::new(UnnecessaryCastToInt, call.range);
let fix = unwrap_int_expression(
call,
argument,
applicability,
checker.semantic(),
checker.locator(),
checker.comment_ranges(),
checker.source(),
);
let diagnostic = Diagnostic::new(UnnecessaryCastToInt, call.range());

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

/// Creates a fix that replaces `int(expression)` with `expression`.
fn unwrap_int_expression(
checker: &Checker,
call: &ExprCall,
argument: &Expr,
applicability: Applicability,
semantic: &SemanticModel,
locator: &Locator,
comment_ranges: &CommentRanges,
source: &str,
) -> Fix {
let (locator, semantic) = (checker.locator(), checker.semantic());

let argument_expr = locator.slice(argument.range());

let has_parent_expr = semantic.current_expression_parent().is_some();
let new_content = if has_parent_expr || argument.is_named_expr() {
format!("({argument_expr})")
let content = if let Some(range) = parenthesized_range(
argument.into(),
(&call.arguments).into(),
comment_ranges,
source,
) {
locator.slice(range).to_string()
} else {
argument_expr.to_string()
let parenthesize = semantic.current_expression_parent().is_some()
|| argument.is_named_expr()
|| locator.count_lines(argument.range()) > 0;
if parenthesize && !has_own_parentheses(argument) {
format!("({})", locator.slice(argument.range()))
} else {
locator.slice(argument.range()).to_string()
}
};

let edit = Edit::range_replacement(new_content, call.range);
let edit = Edit::range_replacement(content, call.range());
Fix::applicable_edit(edit, applicability)
}

Expand Down Expand Up @@ -206,3 +228,20 @@ fn round_applicability(checker: &Checker, arguments: &Arguments) -> Option<Appli
_ => None,
}
}

/// Returns `true` if the given [`Expr`] has its own parentheses (e.g., `()`, `[]`, `{}`).
fn has_own_parentheses(expr: &Expr) -> bool {
match expr {
Expr::ListComp(_)
| Expr::SetComp(_)
| Expr::DictComp(_)
| Expr::Subscript(_)
| Expr::List(_)
| Expr::Set(_)
| Expr::Dict(_)
| Expr::Call(_) => true,
Expr::Generator(generator) => generator.parenthesized,
Expr::Tuple(tuple) => tuple.parenthesized,
_ => false,
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
---
source: crates/ruff_linter/src/rules/ruff/mod.rs
snapshot_kind: text
---
RUF046.py:10:1: RUF046 [*] Value being casted is already an integer
|
Expand Down Expand Up @@ -976,3 +975,44 @@ RUF046.py:76:1: RUF046 [*] Value being casted is already an integer
77 77 |
78 78 |
79 79 | ### No errors

RUF046.py:158:1: RUF046 [*] Value being casted is already an integer
|
156 | int(1. if ... else .2)
157 |
158 | / int(1 +
159 | | 1)
| |______^ RUF046
160 |
161 | int(round(1,
|
= help: Remove unnecessary conversion to `int`

Safe fix
155 155 |
156 156 | int(1. if ... else .2)
157 157 |
158 |-int(1 +
158 |+(1 +
159 159 | 1)
160 160 |
161 161 | int(round(1,

RUF046.py:161:1: RUF046 [*] Value being casted is already an integer
|
159 | 1)
160 |
161 | / int(round(1,
162 | | 0))
| |_____________^ RUF046
|
= help: Remove unnecessary conversion to `int`

Safe fix
158 158 | int(1 +
159 159 | 1)
160 160 |
161 |-int(round(1,
162 |- 0))
161 |+round(1,
162 |+ 0)

0 comments on commit be65404

Please # to comment.