[pyupgrade] Detect literals with unary operators (UP018) (#10060)
Fix #10029.
This commit is contained in:
@@ -33,7 +33,7 @@ bool(b"")
|
||||
bool(1.0)
|
||||
int().denominator
|
||||
|
||||
# These become string or byte literals
|
||||
# These become literals
|
||||
str()
|
||||
str("foo")
|
||||
str("""
|
||||
@@ -53,3 +53,9 @@ bool(False)
|
||||
|
||||
# These become a literal but retain parentheses
|
||||
int(1).denominator
|
||||
|
||||
# These too are literals in spirit
|
||||
int(+1)
|
||||
int(-1)
|
||||
float(+1.0)
|
||||
float(-1.0)
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::str::FromStr;
|
||||
|
||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::{self as ast, Expr, LiteralExpressionRef};
|
||||
use ruff_python_ast::{self as ast, Expr, LiteralExpressionRef, UnaryOp};
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
@@ -198,15 +198,31 @@ pub(crate) fn native_literals(
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
Some(arg) => {
|
||||
let Some(literal_expr) = arg.as_literal_expr() else {
|
||||
let literal_expr = if let Some(literal_expr) = arg.as_literal_expr() {
|
||||
// Skip implicit concatenated strings.
|
||||
if literal_expr.is_implicit_concatenated() {
|
||||
return;
|
||||
}
|
||||
literal_expr
|
||||
} else if let Expr::UnaryOp(ast::ExprUnaryOp {
|
||||
op: UnaryOp::UAdd | UnaryOp::USub,
|
||||
operand,
|
||||
..
|
||||
}) = arg
|
||||
{
|
||||
if let Some(literal_expr) = operand
|
||||
.as_literal_expr()
|
||||
.filter(|expr| matches!(expr, LiteralExpressionRef::NumberLiteral(_)))
|
||||
{
|
||||
literal_expr
|
||||
} else {
|
||||
// Only allow unary operators for numbers.
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Skip implicit string concatenations.
|
||||
if literal_expr.is_implicit_concatenated() {
|
||||
return;
|
||||
}
|
||||
|
||||
let Ok(arg_literal_type) = LiteralType::try_from(literal_expr) else {
|
||||
return;
|
||||
};
|
||||
@@ -221,14 +237,8 @@ pub(crate) fn native_literals(
|
||||
// Ex) `(7).denominator` is valid but `7.denominator` is not
|
||||
// Note that floats do not have this problem
|
||||
// Ex) `(1.0).real` is valid and `1.0.real` is too
|
||||
let content = match (parent_expr, arg) {
|
||||
(
|
||||
Some(Expr::Attribute(_)),
|
||||
Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||
value: ast::Number::Int(_),
|
||||
..
|
||||
}),
|
||||
) => format!("({arg_code})"),
|
||||
let content = match (parent_expr, literal_type) {
|
||||
(Some(Expr::Attribute(_)), LiteralType::Int) => format!("({arg_code})"),
|
||||
_ => arg_code.to_string(),
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ source: crates/ruff_linter/src/rules/pyupgrade/mod.rs
|
||||
---
|
||||
UP018.py:37:1: UP018 [*] Unnecessary `str` call (rewrite as a literal)
|
||||
|
|
||||
36 | # These become string or byte literals
|
||||
36 | # These become literals
|
||||
37 | str()
|
||||
| ^^^^^ UP018
|
||||
38 | str("foo")
|
||||
@@ -14,7 +14,7 @@ UP018.py:37:1: UP018 [*] Unnecessary `str` call (rewrite as a literal)
|
||||
ℹ Safe fix
|
||||
34 34 | int().denominator
|
||||
35 35 |
|
||||
36 36 | # These become string or byte literals
|
||||
36 36 | # These become literals
|
||||
37 |-str()
|
||||
37 |+""
|
||||
38 38 | str("foo")
|
||||
@@ -23,7 +23,7 @@ UP018.py:37:1: UP018 [*] Unnecessary `str` call (rewrite as a literal)
|
||||
|
||||
UP018.py:38:1: UP018 [*] Unnecessary `str` call (rewrite as a literal)
|
||||
|
|
||||
36 | # These become string or byte literals
|
||||
36 | # These become literals
|
||||
37 | str()
|
||||
38 | str("foo")
|
||||
| ^^^^^^^^^^ UP018
|
||||
@@ -34,7 +34,7 @@ UP018.py:38:1: UP018 [*] Unnecessary `str` call (rewrite as a literal)
|
||||
|
||||
ℹ Safe fix
|
||||
35 35 |
|
||||
36 36 | # These become string or byte literals
|
||||
36 36 | # These become literals
|
||||
37 37 | str()
|
||||
38 |-str("foo")
|
||||
38 |+"foo"
|
||||
@@ -55,7 +55,7 @@ UP018.py:39:1: UP018 [*] Unnecessary `str` call (rewrite as a literal)
|
||||
= help: Replace with string literal
|
||||
|
||||
ℹ Safe fix
|
||||
36 36 | # These become string or byte literals
|
||||
36 36 | # These become literals
|
||||
37 37 | str()
|
||||
38 38 | str("foo")
|
||||
39 |-str("""
|
||||
@@ -304,6 +304,8 @@ UP018.py:55:1: UP018 [*] Unnecessary `int` call (rewrite as a literal)
|
||||
54 | # These become a literal but retain parentheses
|
||||
55 | int(1).denominator
|
||||
| ^^^^^^ UP018
|
||||
56 |
|
||||
57 | # These too are literals in spirit
|
||||
|
|
||||
= help: Replace with integer literal
|
||||
|
||||
@@ -313,5 +315,82 @@ UP018.py:55:1: UP018 [*] Unnecessary `int` call (rewrite as a literal)
|
||||
54 54 | # These become a literal but retain parentheses
|
||||
55 |-int(1).denominator
|
||||
55 |+(1).denominator
|
||||
56 56 |
|
||||
57 57 | # These too are literals in spirit
|
||||
58 58 | int(+1)
|
||||
|
||||
UP018.py:58:1: UP018 [*] Unnecessary `int` call (rewrite as a literal)
|
||||
|
|
||||
57 | # These too are literals in spirit
|
||||
58 | int(+1)
|
||||
| ^^^^^^^ UP018
|
||||
59 | int(-1)
|
||||
60 | float(+1.0)
|
||||
|
|
||||
= help: Replace with integer literal
|
||||
|
||||
ℹ Safe fix
|
||||
55 55 | int(1).denominator
|
||||
56 56 |
|
||||
57 57 | # These too are literals in spirit
|
||||
58 |-int(+1)
|
||||
58 |++1
|
||||
59 59 | int(-1)
|
||||
60 60 | float(+1.0)
|
||||
61 61 | float(-1.0)
|
||||
|
||||
UP018.py:59:1: UP018 [*] Unnecessary `int` call (rewrite as a literal)
|
||||
|
|
||||
57 | # These too are literals in spirit
|
||||
58 | int(+1)
|
||||
59 | int(-1)
|
||||
| ^^^^^^^ UP018
|
||||
60 | float(+1.0)
|
||||
61 | float(-1.0)
|
||||
|
|
||||
= help: Replace with integer literal
|
||||
|
||||
ℹ Safe fix
|
||||
56 56 |
|
||||
57 57 | # These too are literals in spirit
|
||||
58 58 | int(+1)
|
||||
59 |-int(-1)
|
||||
59 |+-1
|
||||
60 60 | float(+1.0)
|
||||
61 61 | float(-1.0)
|
||||
|
||||
UP018.py:60:1: UP018 [*] Unnecessary `float` call (rewrite as a literal)
|
||||
|
|
||||
58 | int(+1)
|
||||
59 | int(-1)
|
||||
60 | float(+1.0)
|
||||
| ^^^^^^^^^^^ UP018
|
||||
61 | float(-1.0)
|
||||
|
|
||||
= help: Replace with float literal
|
||||
|
||||
ℹ Safe fix
|
||||
57 57 | # These too are literals in spirit
|
||||
58 58 | int(+1)
|
||||
59 59 | int(-1)
|
||||
60 |-float(+1.0)
|
||||
60 |++1.0
|
||||
61 61 | float(-1.0)
|
||||
|
||||
UP018.py:61:1: UP018 [*] Unnecessary `float` call (rewrite as a literal)
|
||||
|
|
||||
59 | int(-1)
|
||||
60 | float(+1.0)
|
||||
61 | float(-1.0)
|
||||
| ^^^^^^^^^^^ UP018
|
||||
|
|
||||
= help: Replace with float literal
|
||||
|
||||
ℹ Safe fix
|
||||
58 58 | int(+1)
|
||||
59 59 | int(-1)
|
||||
60 60 | float(+1.0)
|
||||
61 |-float(-1.0)
|
||||
61 |+-1.0
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user