mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 02:38:25 +00:00
[ruff
] Parenthesize arguments to int
when removing int
would change semantics in unnecessary-cast-to-int
(RUF046
) (#15277)
When removing `int` in calls like `int(expr)` we may need to keep parentheses around `expr` even when it is a function call or subscript, since there may be newlines in between the function/value name and the opening parentheses/bracket of the argument. This PR implements that logic. Closes #15263 --------- Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
This commit is contained in:
parent
3b3c2c5aa4
commit
71ad9a2ab1
3 changed files with 350 additions and 7 deletions
|
@ -160,3 +160,49 @@ int(1 +
|
|||
|
||||
int(round(1,
|
||||
0))
|
||||
|
||||
# function calls may need to retain parentheses
|
||||
# if the parentheses for the call itself
|
||||
# lie on the next line.
|
||||
# See https://github.com/astral-sh/ruff/issues/15263
|
||||
int(round
|
||||
(1))
|
||||
|
||||
int(round # a comment
|
||||
# and another comment
|
||||
(10)
|
||||
)
|
||||
|
||||
int(round (17)) # this is safe without parens
|
||||
|
||||
int( round (
|
||||
17
|
||||
)) # this is also safe without parens
|
||||
|
||||
int((round) # Comment
|
||||
(42)
|
||||
)
|
||||
|
||||
int((round # Comment
|
||||
)(42)
|
||||
)
|
||||
|
||||
int( # Unsafe fix because of this comment
|
||||
( # Comment
|
||||
(round
|
||||
) # Comment
|
||||
)(42)
|
||||
)
|
||||
|
||||
int(
|
||||
round(
|
||||
42
|
||||
) # unsafe fix because of this comment
|
||||
)
|
||||
|
||||
int(
|
||||
round(
|
||||
42
|
||||
)
|
||||
# unsafe fix because of this comment
|
||||
)
|
||||
|
|
|
@ -4,9 +4,9 @@ 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_python_trivia::{lines_after_ignoring_trivia, CommentRanges};
|
||||
use ruff_source_file::LineRanges;
|
||||
use ruff_text_size::Ranged;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::rules::ruff::rules::unnecessary_round::{
|
||||
|
@ -114,12 +114,36 @@ fn unwrap_int_expression(
|
|||
let parenthesize = semantic.current_expression_parent().is_some()
|
||||
|| argument.is_named_expr()
|
||||
|| locator.count_lines(argument.range()) > 0;
|
||||
if parenthesize && !has_own_parentheses(argument) {
|
||||
if parenthesize && !has_own_parentheses(argument, comment_ranges, source) {
|
||||
format!("({})", locator.slice(argument.range()))
|
||||
} else {
|
||||
locator.slice(argument.range()).to_string()
|
||||
}
|
||||
};
|
||||
|
||||
// Since we're deleting the complement of the argument range within
|
||||
// the call range, we have to check both ends for comments.
|
||||
//
|
||||
// For example:
|
||||
// ```python
|
||||
// int( # comment
|
||||
// round(
|
||||
// 42.1
|
||||
// ) # comment
|
||||
// )
|
||||
// ```
|
||||
let applicability = {
|
||||
let call_to_arg_start = TextRange::new(call.start(), argument.start());
|
||||
let arg_to_call_end = TextRange::new(argument.end(), call.end());
|
||||
if comment_ranges.intersects(call_to_arg_start)
|
||||
|| comment_ranges.intersects(arg_to_call_end)
|
||||
{
|
||||
Applicability::Unsafe
|
||||
} else {
|
||||
applicability
|
||||
}
|
||||
};
|
||||
|
||||
let edit = Edit::range_replacement(content, call.range());
|
||||
Fix::applicable_edit(edit, applicability)
|
||||
}
|
||||
|
@ -229,16 +253,49 @@ fn round_applicability(arguments: &Arguments, semantic: &SemanticModel) -> Optio
|
|||
}
|
||||
|
||||
/// Returns `true` if the given [`Expr`] has its own parentheses (e.g., `()`, `[]`, `{}`).
|
||||
fn has_own_parentheses(expr: &Expr) -> bool {
|
||||
fn has_own_parentheses(expr: &Expr, comment_ranges: &CommentRanges, source: &str) -> bool {
|
||||
match expr {
|
||||
Expr::ListComp(_)
|
||||
| Expr::SetComp(_)
|
||||
| Expr::DictComp(_)
|
||||
| Expr::Subscript(_)
|
||||
| Expr::List(_)
|
||||
| Expr::Set(_)
|
||||
| Expr::Dict(_)
|
||||
| Expr::Call(_) => true,
|
||||
| Expr::Dict(_) => true,
|
||||
Expr::Call(call_expr) => {
|
||||
// A call where the function and parenthesized
|
||||
// argument(s) appear on separate lines
|
||||
// requires outer parentheses. That is:
|
||||
// ```
|
||||
// (f
|
||||
// (10))
|
||||
// ```
|
||||
// is different than
|
||||
// ```
|
||||
// f
|
||||
// (10)
|
||||
// ```
|
||||
let func_end = parenthesized_range(
|
||||
call_expr.func.as_ref().into(),
|
||||
call_expr.into(),
|
||||
comment_ranges,
|
||||
source,
|
||||
)
|
||||
.unwrap_or(call_expr.func.range())
|
||||
.end();
|
||||
lines_after_ignoring_trivia(func_end, source) == 0
|
||||
}
|
||||
Expr::Subscript(subscript_expr) => {
|
||||
// Same as above
|
||||
let subscript_end = parenthesized_range(
|
||||
subscript_expr.value.as_ref().into(),
|
||||
subscript_expr.into(),
|
||||
comment_ranges,
|
||||
source,
|
||||
)
|
||||
.unwrap_or(subscript_expr.value.range())
|
||||
.end();
|
||||
lines_after_ignoring_trivia(subscript_end, source) == 0
|
||||
}
|
||||
Expr::Generator(generator) => generator.parenthesized,
|
||||
Expr::Tuple(tuple) => tuple.parenthesized,
|
||||
_ => false,
|
||||
|
|
|
@ -1005,6 +1005,8 @@ RUF046.py:161:1: RUF046 [*] Value being cast to `int` is already an integer
|
|||
161 | / int(round(1,
|
||||
162 | | 0))
|
||||
| |_____________^ RUF046
|
||||
163 |
|
||||
164 | # function calls may need to retain parentheses
|
||||
|
|
||||
= help: Remove unnecessary `int` call
|
||||
|
||||
|
@ -1016,3 +1018,241 @@ RUF046.py:161:1: RUF046 [*] Value being cast to `int` is already an integer
|
|||
162 |- 0))
|
||||
161 |+round(1,
|
||||
162 |+ 0)
|
||||
163 163 |
|
||||
164 164 | # function calls may need to retain parentheses
|
||||
165 165 | # if the parentheses for the call itself
|
||||
|
||||
RUF046.py:168:1: RUF046 [*] Value being cast to `int` is already an integer
|
||||
|
|
||||
166 | # lie on the next line.
|
||||
167 | # See https://github.com/astral-sh/ruff/issues/15263
|
||||
168 | / int(round
|
||||
169 | | (1))
|
||||
| |____^ RUF046
|
||||
170 |
|
||||
171 | int(round # a comment
|
||||
|
|
||||
= help: Remove unnecessary `int` call
|
||||
|
||||
ℹ Safe fix
|
||||
165 165 | # if the parentheses for the call itself
|
||||
166 166 | # lie on the next line.
|
||||
167 167 | # See https://github.com/astral-sh/ruff/issues/15263
|
||||
168 |-int(round
|
||||
168 |+(round
|
||||
169 169 | (1))
|
||||
170 170 |
|
||||
171 171 | int(round # a comment
|
||||
|
||||
RUF046.py:171:1: RUF046 [*] Value being cast to `int` is already an integer
|
||||
|
|
||||
169 | (1))
|
||||
170 |
|
||||
171 | / int(round # a comment
|
||||
172 | | # and another comment
|
||||
173 | | (10)
|
||||
174 | | )
|
||||
| |_^ RUF046
|
||||
175 |
|
||||
176 | int(round (17)) # this is safe without parens
|
||||
|
|
||||
= help: Remove unnecessary `int` call
|
||||
|
||||
ℹ Safe fix
|
||||
168 168 | int(round
|
||||
169 169 | (1))
|
||||
170 170 |
|
||||
171 |-int(round # a comment
|
||||
171 |+(round # a comment
|
||||
172 172 | # and another comment
|
||||
173 |-(10)
|
||||
174 |-)
|
||||
173 |+(10))
|
||||
175 174 |
|
||||
176 175 | int(round (17)) # this is safe without parens
|
||||
177 176 |
|
||||
|
||||
RUF046.py:176:1: RUF046 [*] Value being cast to `int` is already an integer
|
||||
|
|
||||
174 | )
|
||||
175 |
|
||||
176 | int(round (17)) # this is safe without parens
|
||||
| ^^^^^^^^^^^^^^^ RUF046
|
||||
177 |
|
||||
178 | int( round (
|
||||
|
|
||||
= help: Remove unnecessary `int` call
|
||||
|
||||
ℹ Safe fix
|
||||
173 173 | (10)
|
||||
174 174 | )
|
||||
175 175 |
|
||||
176 |-int(round (17)) # this is safe without parens
|
||||
176 |+round (17) # this is safe without parens
|
||||
177 177 |
|
||||
178 178 | int( round (
|
||||
179 179 | 17
|
||||
|
||||
RUF046.py:178:1: RUF046 [*] Value being cast to `int` is already an integer
|
||||
|
|
||||
176 | int(round (17)) # this is safe without parens
|
||||
177 |
|
||||
178 | / int( round (
|
||||
179 | | 17
|
||||
180 | | )) # this is also safe without parens
|
||||
| |______________^ RUF046
|
||||
181 |
|
||||
182 | int((round) # Comment
|
||||
|
|
||||
= help: Remove unnecessary `int` call
|
||||
|
||||
ℹ Safe fix
|
||||
175 175 |
|
||||
176 176 | int(round (17)) # this is safe without parens
|
||||
177 177 |
|
||||
178 |-int( round (
|
||||
178 |+round (
|
||||
179 179 | 17
|
||||
180 |- )) # this is also safe without parens
|
||||
180 |+ ) # this is also safe without parens
|
||||
181 181 |
|
||||
182 182 | int((round) # Comment
|
||||
183 183 | (42)
|
||||
|
||||
RUF046.py:182:1: RUF046 [*] Value being cast to `int` is already an integer
|
||||
|
|
||||
180 | )) # this is also safe without parens
|
||||
181 |
|
||||
182 | / int((round) # Comment
|
||||
183 | | (42)
|
||||
184 | | )
|
||||
| |_^ RUF046
|
||||
185 |
|
||||
186 | int((round # Comment
|
||||
|
|
||||
= help: Remove unnecessary `int` call
|
||||
|
||||
ℹ Safe fix
|
||||
179 179 | 17
|
||||
180 180 | )) # this is also safe without parens
|
||||
181 181 |
|
||||
182 |-int((round) # Comment
|
||||
183 |-(42)
|
||||
184 |-)
|
||||
182 |+((round) # Comment
|
||||
183 |+(42))
|
||||
185 184 |
|
||||
186 185 | int((round # Comment
|
||||
187 186 | )(42)
|
||||
|
||||
RUF046.py:186:1: RUF046 [*] Value being cast to `int` is already an integer
|
||||
|
|
||||
184 | )
|
||||
185 |
|
||||
186 | / int((round # Comment
|
||||
187 | | )(42)
|
||||
188 | | )
|
||||
| |_^ RUF046
|
||||
189 |
|
||||
190 | int( # Unsafe fix because of this comment
|
||||
|
|
||||
= help: Remove unnecessary `int` call
|
||||
|
||||
ℹ Safe fix
|
||||
183 183 | (42)
|
||||
184 184 | )
|
||||
185 185 |
|
||||
186 |-int((round # Comment
|
||||
186 |+(round # Comment
|
||||
187 187 | )(42)
|
||||
188 |-)
|
||||
189 188 |
|
||||
190 189 | int( # Unsafe fix because of this comment
|
||||
191 190 | ( # Comment
|
||||
|
||||
RUF046.py:190:1: RUF046 [*] Value being cast to `int` is already an integer
|
||||
|
|
||||
188 | )
|
||||
189 |
|
||||
190 | / int( # Unsafe fix because of this comment
|
||||
191 | | ( # Comment
|
||||
192 | | (round
|
||||
193 | | ) # Comment
|
||||
194 | | )(42)
|
||||
195 | | )
|
||||
| |_^ RUF046
|
||||
196 |
|
||||
197 | int(
|
||||
|
|
||||
= help: Remove unnecessary `int` call
|
||||
|
||||
ℹ Unsafe fix
|
||||
187 187 | )(42)
|
||||
188 188 | )
|
||||
189 189 |
|
||||
190 |-int( # Unsafe fix because of this comment
|
||||
191 190 | ( # Comment
|
||||
192 191 | (round
|
||||
193 192 | ) # Comment
|
||||
194 193 | )(42)
|
||||
195 |-)
|
||||
196 194 |
|
||||
197 195 | int(
|
||||
198 196 | round(
|
||||
|
||||
RUF046.py:197:1: RUF046 [*] Value being cast to `int` is already an integer
|
||||
|
|
||||
195 | )
|
||||
196 |
|
||||
197 | / int(
|
||||
198 | | round(
|
||||
199 | | 42
|
||||
200 | | ) # unsafe fix because of this comment
|
||||
201 | | )
|
||||
| |_^ RUF046
|
||||
202 |
|
||||
203 | int(
|
||||
|
|
||||
= help: Remove unnecessary `int` call
|
||||
|
||||
ℹ Unsafe fix
|
||||
194 194 | )(42)
|
||||
195 195 | )
|
||||
196 196 |
|
||||
197 |-int(
|
||||
198 |- round(
|
||||
197 |+round(
|
||||
199 198 | 42
|
||||
200 |- ) # unsafe fix because of this comment
|
||||
201 |-)
|
||||
199 |+ )
|
||||
202 200 |
|
||||
203 201 | int(
|
||||
204 202 | round(
|
||||
|
||||
RUF046.py:203:1: RUF046 [*] Value being cast to `int` is already an integer
|
||||
|
|
||||
201 | )
|
||||
202 |
|
||||
203 | / int(
|
||||
204 | | round(
|
||||
205 | | 42
|
||||
206 | | )
|
||||
207 | | # unsafe fix because of this comment
|
||||
208 | | )
|
||||
| |_^ RUF046
|
||||
|
|
||||
= help: Remove unnecessary `int` call
|
||||
|
||||
ℹ Unsafe fix
|
||||
200 200 | ) # unsafe fix because of this comment
|
||||
201 201 | )
|
||||
202 202 |
|
||||
203 |-int(
|
||||
204 |- round(
|
||||
203 |+round(
|
||||
205 204 | 42
|
||||
206 |- )
|
||||
207 |-# unsafe fix because of this comment
|
||||
208 |-)
|
||||
205 |+ )
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue