Skip to content

Commit ba990b6

Browse files
authored
add DebugText for self-documenting f-strings (#6167)
1 parent 44a8d1c commit ba990b6

File tree

26 files changed

+148
-155
lines changed

26 files changed

+148
-155
lines changed

crates/ruff/src/rules/flynt/helpers.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use ruff_text_size::TextRange;
55
fn to_formatted_value_expr(inner: &Expr) -> Expr {
66
let node = ast::ExprFormattedValue {
77
value: Box::new(inner.clone()),
8+
debug_text: None,
89
conversion: ConversionFlag::None,
910
format_spec: None,
1011
range: TextRange::default(),

crates/ruff_python_ast/src/comparable.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,7 @@ pub struct ExprCall<'a> {
592592
#[derive(Debug, PartialEq, Eq, Hash)]
593593
pub struct ExprFormattedValue<'a> {
594594
value: Box<ComparableExpr<'a>>,
595+
debug_text: Option<&'a ast::DebugText>,
595596
conversion: ast::ConversionFlag,
596597
format_spec: Option<Box<ComparableExpr<'a>>>,
597598
}
@@ -849,11 +850,13 @@ impl<'a> From<&'a ast::Expr> for ComparableExpr<'a> {
849850
ast::Expr::FormattedValue(ast::ExprFormattedValue {
850851
value,
851852
conversion,
853+
debug_text,
852854
format_spec,
853855
range: _range,
854856
}) => Self::FormattedValue(ExprFormattedValue {
855857
value: value.into(),
856858
conversion: *conversion,
859+
debug_text: debug_text.as_ref(),
857860
format_spec: format_spec.as_ref().map(Into::into),
858861
}),
859862
ast::Expr::JoinedStr(ast::ExprJoinedStr {

crates/ruff_python_ast/src/nodes.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,7 @@ impl From<ExprCall> for Expr {
888888
pub struct ExprFormattedValue {
889889
pub range: TextRange,
890890
pub value: Box<Expr>,
891+
pub debug_text: Option<DebugText>,
891892
pub conversion: ConversionFlag,
892893
pub format_spec: Option<Box<Expr>>,
893894
}
@@ -925,6 +926,14 @@ impl ConversionFlag {
925926
}
926927
}
927928

929+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
930+
pub struct DebugText {
931+
/// The text between the `{` and the expression node.
932+
pub leading: String,
933+
/// The text between the expression and the conversion, the format_spec, or the `}`, depending on what's present in the source
934+
pub trailing: String,
935+
}
936+
928937
/// See also [JoinedStr](https://docs.python.org/3/library/ast.html#ast.JoinedStr)
929938
#[derive(Clone, Debug, PartialEq)]
930939
pub struct ExprJoinedStr {

crates/ruff_python_codegen/src/generator.rs

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ use std::ops::Deref;
55

66
use ruff_python_ast::{
77
self as ast, Alias, Arg, Arguments, BoolOp, CmpOp, Comprehension, Constant, ConversionFlag,
8-
ExceptHandler, Expr, Identifier, MatchCase, Operator, Pattern, Stmt, Suite, TypeParam,
9-
TypeParamParamSpec, TypeParamTypeVar, TypeParamTypeVarTuple, WithItem,
8+
DebugText, ExceptHandler, Expr, Identifier, MatchCase, Operator, Pattern, Stmt, Suite,
9+
TypeParam, TypeParamParamSpec, TypeParamTypeVar, TypeParamTypeVarTuple, WithItem,
1010
};
1111
use ruff_python_literal::escape::{AsciiEscape, Escape, UnicodeEscape};
1212

@@ -1189,10 +1189,16 @@ impl<'a> Generator<'a> {
11891189
}
11901190
Expr::FormattedValue(ast::ExprFormattedValue {
11911191
value,
1192+
debug_text,
11921193
conversion,
11931194
format_spec,
11941195
range: _range,
1195-
}) => self.unparse_formatted(value, *conversion, format_spec.as_deref()),
1196+
}) => self.unparse_formatted(
1197+
value,
1198+
debug_text.as_ref(),
1199+
*conversion,
1200+
format_spec.as_deref(),
1201+
),
11961202
Expr::JoinedStr(ast::ExprJoinedStr {
11971203
values,
11981204
range: _range,
@@ -1382,7 +1388,13 @@ impl<'a> Generator<'a> {
13821388
}
13831389
}
13841390

1385-
fn unparse_formatted(&mut self, val: &Expr, conversion: ConversionFlag, spec: Option<&Expr>) {
1391+
fn unparse_formatted(
1392+
&mut self,
1393+
val: &Expr,
1394+
debug_text: Option<&DebugText>,
1395+
conversion: ConversionFlag,
1396+
spec: Option<&Expr>,
1397+
) {
13861398
let mut generator = Generator::new(self.indent, self.quote, self.line_ending);
13871399
generator.unparse_expr(val, precedence::FORMATTED_VALUE);
13881400
let brace = if generator.buffer.starts_with('{') {
@@ -1392,8 +1404,17 @@ impl<'a> Generator<'a> {
13921404
"{"
13931405
};
13941406
self.p(brace);
1407+
1408+
if let Some(debug_text) = debug_text {
1409+
self.buffer += debug_text.leading.as_str();
1410+
}
1411+
13951412
self.buffer += &generator.buffer;
13961413

1414+
if let Some(debug_text) = debug_text {
1415+
self.buffer += debug_text.trailing.as_str();
1416+
}
1417+
13971418
if !conversion.is_none() {
13981419
self.p("!");
13991420
#[allow(clippy::cast_possible_truncation)]
@@ -1425,10 +1446,16 @@ impl<'a> Generator<'a> {
14251446
}
14261447
Expr::FormattedValue(ast::ExprFormattedValue {
14271448
value,
1449+
debug_text,
14281450
conversion,
14291451
format_spec,
14301452
range: _range,
1431-
}) => self.unparse_formatted(value, *conversion, format_spec.as_deref()),
1453+
}) => self.unparse_formatted(
1454+
value,
1455+
debug_text.as_ref(),
1456+
*conversion,
1457+
format_spec.as_deref(),
1458+
),
14321459
_ => unreachable!(),
14331460
}
14341461
}
@@ -1755,6 +1782,15 @@ class Foo:
17551782
assert_eq!(round_trip(r#"f'abc{"def"}{1}'"#), r#"f"abc{'def'}{1}""#);
17561783
}
17571784

1785+
#[test]
1786+
fn self_documenting_f_string() {
1787+
assert_round_trip!(r#"f"{ chr(65) = }""#);
1788+
assert_round_trip!(r#"f"{ chr(65) = !s}""#);
1789+
assert_round_trip!(r#"f"{ chr(65) = !r}""#);
1790+
assert_round_trip!(r#"f"{ chr(65) = :#x}""#);
1791+
assert_round_trip!(r#"f"{a=!r:0.05f}""#);
1792+
}
1793+
17581794
#[test]
17591795
fn indent() {
17601796
assert_eq!(

crates/ruff_python_parser/src/snapshots/ruff_python_parser__parser__tests__try.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ expression: parse_ast
112112
keywords: [],
113113
},
114114
),
115+
debug_text: None,
115116
conversion: None,
116117
format_spec: None,
117118
},
@@ -199,6 +200,7 @@ expression: parse_ast
199200
keywords: [],
200201
},
201202
),
203+
debug_text: None,
202204
conversion: None,
203205
format_spec: None,
204206
},

crates/ruff_python_parser/src/snapshots/ruff_python_parser__parser__tests__try_star.snap

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ expression: parse_ast
216216
keywords: [],
217217
},
218218
),
219+
debug_text: None,
219220
conversion: None,
220221
format_spec: None,
221222
},
@@ -249,6 +250,7 @@ expression: parse_ast
249250
ctx: Load,
250251
},
251252
),
253+
debug_text: None,
252254
conversion: None,
253255
format_spec: None,
254256
},
@@ -336,6 +338,7 @@ expression: parse_ast
336338
keywords: [],
337339
},
338340
),
341+
debug_text: None,
339342
conversion: None,
340343
format_spec: None,
341344
},
@@ -369,6 +372,7 @@ expression: parse_ast
369372
ctx: Load,
370373
},
371374
),
375+
debug_text: None,
372376
conversion: None,
373377
format_spec: None,
374378
},

crates/ruff_python_parser/src/snapshots/ruff_python_parser__string__tests__fstring_constant_range.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ expression: parse_ast
2929
ctx: Load,
3030
},
3131
),
32+
debug_text: None,
3233
conversion: None,
3334
format_spec: None,
3435
},
@@ -52,6 +53,7 @@ expression: parse_ast
5253
ctx: Load,
5354
},
5455
),
56+
debug_text: None,
5557
conversion: None,
5658
format_spec: None,
5759
},

crates/ruff_python_parser/src/snapshots/ruff_python_parser__string__tests__fstring_escaped_character.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ expression: parse_ast
2929
ctx: Load,
3030
},
3131
),
32+
debug_text: None,
3233
conversion: None,
3334
format_spec: None,
3435
},

crates/ruff_python_parser/src/snapshots/ruff_python_parser__string__tests__fstring_escaped_newline.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ expression: parse_ast
2929
ctx: Load,
3030
},
3131
),
32+
debug_text: None,
3233
conversion: None,
3334
format_spec: None,
3435
},

crates/ruff_python_parser/src/snapshots/ruff_python_parser__string__tests__fstring_line_continuation.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ expression: parse_ast
2929
ctx: Load,
3030
},
3131
),
32+
debug_text: None,
3233
conversion: None,
3334
format_spec: None,
3435
},

crates/ruff_python_parser/src/snapshots/ruff_python_parser__string__tests__fstring_parse_self_documenting_base.snap

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,6 @@ source: crates/ruff_python_parser/src/string.rs
33
expression: parse_ast
44
---
55
[
6-
Constant(
7-
ExprConstant {
8-
range: 2..9,
9-
value: Str(
10-
"user=",
11-
),
12-
kind: None,
13-
},
14-
),
15-
Constant(
16-
ExprConstant {
17-
range: 2..9,
18-
value: Str(
19-
"",
20-
),
21-
kind: None,
22-
},
23-
),
246
FormattedValue(
257
ExprFormattedValue {
268
range: 2..9,
@@ -31,7 +13,13 @@ expression: parse_ast
3113
ctx: Load,
3214
},
3315
),
34-
conversion: Repr,
16+
debug_text: Some(
17+
DebugText {
18+
leading: "",
19+
trailing: "=",
20+
},
21+
),
22+
conversion: None,
3523
format_spec: None,
3624
},
3725
),

crates/ruff_python_parser/src/snapshots/ruff_python_parser__string__tests__fstring_parse_self_documenting_base_more.snap

Lines changed: 14 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,6 @@ expression: parse_ast
1212
kind: None,
1313
},
1414
),
15-
Constant(
16-
ExprConstant {
17-
range: 6..13,
18-
value: Str(
19-
"user=",
20-
),
21-
kind: None,
22-
},
23-
),
24-
Constant(
25-
ExprConstant {
26-
range: 6..13,
27-
value: Str(
28-
"",
29-
),
30-
kind: None,
31-
},
32-
),
3315
FormattedValue(
3416
ExprFormattedValue {
3517
range: 6..13,
@@ -40,7 +22,13 @@ expression: parse_ast
4022
ctx: Load,
4123
},
4224
),
43-
conversion: Repr,
25+
debug_text: Some(
26+
DebugText {
27+
leading: "",
28+
trailing: "=",
29+
},
30+
),
31+
conversion: None,
4432
format_spec: None,
4533
},
4634
),
@@ -53,24 +41,6 @@ expression: parse_ast
5341
kind: None,
5442
},
5543
),
56-
Constant(
57-
ExprConstant {
58-
range: 28..37,
59-
value: Str(
60-
"second=",
61-
),
62-
kind: None,
63-
},
64-
),
65-
Constant(
66-
ExprConstant {
67-
range: 28..37,
68-
value: Str(
69-
"",
70-
),
71-
kind: None,
72-
},
73-
),
7444
FormattedValue(
7545
ExprFormattedValue {
7646
range: 28..37,
@@ -81,7 +51,13 @@ expression: parse_ast
8151
ctx: Load,
8252
},
8353
),
84-
conversion: Repr,
54+
debug_text: Some(
55+
DebugText {
56+
leading: "",
57+
trailing: "=",
58+
},
59+
),
60+
conversion: None,
8561
format_spec: None,
8662
},
8763
),

crates/ruff_python_parser/src/snapshots/ruff_python_parser__string__tests__fstring_parse_self_documenting_format.snap

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,6 @@ source: crates/ruff_python_parser/src/string.rs
33
expression: parse_ast
44
---
55
[
6-
Constant(
7-
ExprConstant {
8-
range: 2..13,
9-
value: Str(
10-
"user=",
11-
),
12-
kind: None,
13-
},
14-
),
15-
Constant(
16-
ExprConstant {
17-
range: 2..13,
18-
value: Str(
19-
"",
20-
),
21-
kind: None,
22-
},
23-
),
246
FormattedValue(
257
ExprFormattedValue {
268
range: 2..13,
@@ -31,6 +13,12 @@ expression: parse_ast
3113
ctx: Load,
3214
},
3315
),
16+
debug_text: Some(
17+
DebugText {
18+
leading: "",
19+
trailing: "=",
20+
},
21+
),
3422
conversion: None,
3523
format_spec: Some(
3624
JoinedStr(

0 commit comments

Comments
 (0)