[`pyupgrade`] Add spaces between tokens as necessary to avoid syntax errors in `UP018` autofix (#17648)

Co-authored-by: Micha Reiser <micha@reiser.io>
This commit is contained in:
Victor Hugo Gomes 2025-05-07 04:34:08 -03:00 committed by GitHub
parent 04457f99b6
commit c504001b32
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 105 additions and 2 deletions

View File

@ -84,3 +84,9 @@ str(
'''Lorem
ipsum''' # Comment
).foo
# https://github.com/astral-sh/ruff/issues/17606
bool(True)and None
int(1)and None
float(1.)and None
bool(True)and()

View File

@ -161,13 +161,14 @@ pub(crate) fn native_literals(
keywords,
range: _,
},
range: _,
range: call_range,
} = call;
if !keywords.is_empty() || args.len() > 1 {
return;
}
let tokens = checker.tokens();
let semantic = checker.semantic();
let Some(builtin) = semantic.resolve_builtin_symbol(func) else {
@ -244,7 +245,20 @@ pub(crate) fn native_literals(
let arg_code = checker.locator().slice(arg);
let content = match (parent_expr, literal_type, has_unary_op) {
let mut needs_space = false;
// Look for the `Rpar` token of the call expression and check if there is a keyword token right
// next to it without any space separating them. Without this check, the fix for this
// rule would create a syntax error.
// Ex) `bool(True)and None` no space between `)` and the keyword `and`.
//
// Subtract 1 from the end of the range to include `Rpar` token in the slice.
if let [paren_token, next_token, ..] = tokens.after(call_range.sub_end(1.into()).end())
{
needs_space = next_token.kind().is_keyword()
&& paren_token.range().end() == next_token.range().start();
}
let mut content = match (parent_expr, literal_type, has_unary_op) {
// Expressions including newlines must be parenthesised to be valid syntax
(_, _, true) if find_newline(arg_code).is_some() => format!("({arg_code})"),
@ -265,6 +279,10 @@ pub(crate) fn native_literals(
_ => arg_code.to_string(),
};
if needs_space {
content.push(' ');
}
let applicability = if checker.comment_ranges().intersects(call.range) {
Applicability::Unsafe
} else {

View File

@ -602,6 +602,8 @@ UP018.py:83:1: UP018 [*] Unnecessary `str` call (rewrite as a literal)
85 | | ipsum''' # Comment
86 | | ).foo
| |_^ UP018
87 |
88 | # https://github.com/astral-sh/ruff/issues/17606
|
= help: Replace with string literal
@ -615,3 +617,80 @@ UP018.py:83:1: UP018 [*] Unnecessary `str` call (rewrite as a literal)
86 |-).foo
83 |+'''Lorem
84 |+ ipsum'''.foo
87 85 |
88 86 | # https://github.com/astral-sh/ruff/issues/17606
89 87 | bool(True)and None
UP018.py:89:1: UP018 [*] Unnecessary `bool` call (rewrite as a literal)
|
88 | # https://github.com/astral-sh/ruff/issues/17606
89 | bool(True)and None
| ^^^^^^^^^^ UP018
90 | int(1)and None
91 | float(1.)and None
|
= help: Replace with boolean literal
Safe fix
86 86 | ).foo
87 87 |
88 88 | # https://github.com/astral-sh/ruff/issues/17606
89 |-bool(True)and None
89 |+True and None
90 90 | int(1)and None
91 91 | float(1.)and None
92 92 | bool(True)and()
UP018.py:90:1: UP018 [*] Unnecessary `int` call (rewrite as a literal)
|
88 | # https://github.com/astral-sh/ruff/issues/17606
89 | bool(True)and None
90 | int(1)and None
| ^^^^^^ UP018
91 | float(1.)and None
92 | bool(True)and()
|
= help: Replace with integer literal
Safe fix
87 87 |
88 88 | # https://github.com/astral-sh/ruff/issues/17606
89 89 | bool(True)and None
90 |-int(1)and None
90 |+1 and None
91 91 | float(1.)and None
92 92 | bool(True)and()
UP018.py:91:1: UP018 [*] Unnecessary `float` call (rewrite as a literal)
|
89 | bool(True)and None
90 | int(1)and None
91 | float(1.)and None
| ^^^^^^^^^ UP018
92 | bool(True)and()
|
= help: Replace with float literal
Safe fix
88 88 | # https://github.com/astral-sh/ruff/issues/17606
89 89 | bool(True)and None
90 90 | int(1)and None
91 |-float(1.)and None
91 |+1. and None
92 92 | bool(True)and()
UP018.py:92:1: UP018 [*] Unnecessary `bool` call (rewrite as a literal)
|
90 | int(1)and None
91 | float(1.)and None
92 | bool(True)and()
| ^^^^^^^^^^ UP018
|
= help: Replace with boolean literal
Safe fix
89 89 | bool(True)and None
90 90 | int(1)and None
91 91 | float(1.)and None
92 |-bool(True)and()
92 |+True and()