Avoid parenthesizing octal/hex or binary literals in object positions (#8160)

This commit is contained in:
Micha Reiser 2023-10-24 23:12:52 +09:00 committed by GitHub
parent 84979f9673
commit 8b665f40c8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 27 additions and 116 deletions

View file

@ -38,14 +38,13 @@ impl FormatNodeRule<ExprAttribute> for FormatExprAttribute {
let format_inner = format_with(|f: &mut PyFormatter| {
let parenthesize_value =
// If the value is an integer, we need to parenthesize it to avoid a syntax error.
matches!(
value.as_ref(),
Expr::Constant(ExprConstant {
value: Constant::Int(_) | Constant::Float(_),
..
})
) || is_expression_parenthesized(value.into(), f.context().comments().ranges(), f.context().source());
is_base_ten_number_literal(value.as_ref(), f.context().source()) || {
is_expression_parenthesized(
value.into(),
f.context().comments().ranges(),
f.context().source(),
)
};
if call_chain_layout == CallChainLayout::Fluent {
if parenthesize_value {
@ -164,3 +163,23 @@ impl NeedsParentheses for ExprAttribute {
}
}
}
// Non Hex, octal or binary number literals need parentheses to disambiguate the attribute `.` from
// a decimal point. Floating point numbers don't strictly need parentheses but it reads better (rather than 0.0.test()).
fn is_base_ten_number_literal(expr: &Expr, source: &str) -> bool {
if let Some(ExprConstant { value, range }) = expr.as_constant_expr() {
match value {
Constant::Float(_) => true,
Constant::Int(_) => {
let text = &source[*range];
!matches!(
text.as_bytes().get(0..2),
Some([b'0', b'x' | b'X' | b'o' | b'O' | b'b' | b'B'])
)
}
_ => false,
}
} else {
false
}
}

View file

@ -1,108 +0,0 @@
---
source: crates/ruff_python_formatter/tests/fixtures.rs
input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/attribute_access_on_number_literals.py
---
## Input
```py
x = 123456789 .bit_count()
x = (123456).__abs__()
x = .1.is_integer()
x = 1. .imag
x = 1E+1.imag
x = 1E-1.real
x = 123456789.123456789.hex()
x = 123456789.123456789E123456789 .real
x = 123456789E123456789 .conjugate()
x = 123456789J.real
x = 123456789.123456789J.__add__(0b1011.bit_length())
x = 0XB1ACC.conjugate()
x = 0B1011 .conjugate()
x = 0O777 .real
x = 0.000000006 .hex()
x = -100.0000J
if 10 .real:
...
y = 100[no]
y = 100(no)
```
## Black Differences
```diff
--- Black
+++ Ruff
@@ -8,10 +8,10 @@
x = (123456789.123456789e123456789).real
x = (123456789e123456789).conjugate()
x = 123456789j.real
-x = 123456789.123456789j.__add__(0b1011.bit_length())
-x = 0xB1ACC.conjugate()
-x = 0b1011.conjugate()
-x = 0o777.real
+x = 123456789.123456789j.__add__((0b1011).bit_length())
+x = (0xB1ACC).conjugate()
+x = (0b1011).conjugate()
+x = (0o777).real
x = (0.000000006).hex()
x = -100.0000j
```
## Ruff Output
```py
x = (123456789).bit_count()
x = (123456).__abs__()
x = (0.1).is_integer()
x = (1.0).imag
x = (1e1).imag
x = (1e-1).real
x = (123456789.123456789).hex()
x = (123456789.123456789e123456789).real
x = (123456789e123456789).conjugate()
x = 123456789j.real
x = 123456789.123456789j.__add__((0b1011).bit_length())
x = (0xB1ACC).conjugate()
x = (0b1011).conjugate()
x = (0o777).real
x = (0.000000006).hex()
x = -100.0000j
if (10).real:
...
y = 100[no]
y = 100(no)
```
## Black Output
```py
x = (123456789).bit_count()
x = (123456).__abs__()
x = (0.1).is_integer()
x = (1.0).imag
x = (1e1).imag
x = (1e-1).real
x = (123456789.123456789).hex()
x = (123456789.123456789e123456789).real
x = (123456789e123456789).conjugate()
x = 123456789j.real
x = 123456789.123456789j.__add__(0b1011.bit_length())
x = 0xB1ACC.conjugate()
x = 0b1011.conjugate()
x = 0o777.real
x = (0.000000006).hex()
x = -100.0000j
if (10).real:
...
y = 100[no]
y = 100(no)
```