Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add DebugText for self-documenting f-strings #6167

Merged
merged 5 commits into from
Aug 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/ruff/src/rules/flynt/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use ruff_text_size::TextRange;
fn to_formatted_value_expr(inner: &Expr) -> Expr {
let node = ast::ExprFormattedValue {
value: Box::new(inner.clone()),
debug_text: None,
conversion: ConversionFlag::None,
format_spec: None,
range: TextRange::default(),
Expand Down
3 changes: 3 additions & 0 deletions crates/ruff_python_ast/src/comparable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,7 @@ pub struct ExprCall<'a> {
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct ExprFormattedValue<'a> {
value: Box<ComparableExpr<'a>>,
debug_text: Option<&'a ast::DebugText>,
conversion: ast::ConversionFlag,
format_spec: Option<Box<ComparableExpr<'a>>>,
}
Expand Down Expand Up @@ -849,11 +850,13 @@ impl<'a> From<&'a ast::Expr> for ComparableExpr<'a> {
ast::Expr::FormattedValue(ast::ExprFormattedValue {
value,
conversion,
debug_text,
format_spec,
range: _range,
}) => Self::FormattedValue(ExprFormattedValue {
value: value.into(),
conversion: *conversion,
debug_text: debug_text.as_ref(),
format_spec: format_spec.as_ref().map(Into::into),
}),
ast::Expr::JoinedStr(ast::ExprJoinedStr {
Expand Down
9 changes: 9 additions & 0 deletions crates/ruff_python_ast/src/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,7 @@ impl From<ExprCall> for Expr {
pub struct ExprFormattedValue {
pub range: TextRange,
pub value: Box<Expr>,
pub debug_text: Option<DebugText>,
pub conversion: ConversionFlag,
pub format_spec: Option<Box<Expr>>,
}
Expand Down Expand Up @@ -925,6 +926,14 @@ impl ConversionFlag {
}
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct DebugText {
/// The text between the `{` and the expression node.
pub leading: String,
/// The text between the expression and the conversion, the format_spec, or the `}`, depending on what's present in the source
pub trailing: String,
}

/// See also [JoinedStr](https://docs.python.org/3/library/ast.html#ast.JoinedStr)
#[derive(Clone, Debug, PartialEq)]
pub struct ExprJoinedStr {
Expand Down
46 changes: 41 additions & 5 deletions crates/ruff_python_codegen/src/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use std::ops::Deref;

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

Expand Down Expand Up @@ -1189,10 +1189,16 @@ impl<'a> Generator<'a> {
}
Expr::FormattedValue(ast::ExprFormattedValue {
value,
debug_text,
conversion,
format_spec,
range: _range,
}) => self.unparse_formatted(value, *conversion, format_spec.as_deref()),
}) => self.unparse_formatted(
value,
debug_text.as_ref(),
*conversion,
format_spec.as_deref(),
),
Expr::JoinedStr(ast::ExprJoinedStr {
values,
range: _range,
Expand Down Expand Up @@ -1382,7 +1388,13 @@ impl<'a> Generator<'a> {
}
}

fn unparse_formatted(&mut self, val: &Expr, conversion: ConversionFlag, spec: Option<&Expr>) {
fn unparse_formatted(
&mut self,
val: &Expr,
debug_text: Option<&DebugText>,
conversion: ConversionFlag,
spec: Option<&Expr>,
) {
let mut generator = Generator::new(self.indent, self.quote, self.line_ending);
generator.unparse_expr(val, precedence::FORMATTED_VALUE);
let brace = if generator.buffer.starts_with('{') {
Expand All @@ -1392,8 +1404,17 @@ impl<'a> Generator<'a> {
"{"
};
self.p(brace);

if let Some(debug_text) = debug_text {
self.buffer += debug_text.leading.as_str();
}

self.buffer += &generator.buffer;

if let Some(debug_text) = debug_text {
self.buffer += debug_text.trailing.as_str();
}

if !conversion.is_none() {
self.p("!");
#[allow(clippy::cast_possible_truncation)]
Expand Down Expand Up @@ -1425,10 +1446,16 @@ impl<'a> Generator<'a> {
}
Expr::FormattedValue(ast::ExprFormattedValue {
value,
debug_text,
conversion,
format_spec,
range: _range,
}) => self.unparse_formatted(value, *conversion, format_spec.as_deref()),
}) => self.unparse_formatted(
value,
debug_text.as_ref(),
*conversion,
format_spec.as_deref(),
),
_ => unreachable!(),
}
}
Expand Down Expand Up @@ -1755,6 +1782,15 @@ class Foo:
assert_eq!(round_trip(r#"f'abc{"def"}{1}'"#), r#"f"abc{'def'}{1}""#);
}

#[test]
fn self_documenting_f_string() {
assert_round_trip!(r#"f"{ chr(65) = }""#);
assert_round_trip!(r#"f"{ chr(65) = !s}""#);
assert_round_trip!(r#"f"{ chr(65) = !r}""#);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind adding a test case with a format specification?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If possible, an additional test case to mix both conversion and format specification would be nice as well: f"{a=!r:0.05f}"

assert_round_trip!(r#"f"{ chr(65) = :#x}""#);
assert_round_trip!(r#"f"{a=!r:0.05f}""#);
}

#[test]
fn indent() {
assert_eq!(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ expression: parse_ast
keywords: [],
},
),
debug_text: None,
conversion: None,
format_spec: None,
},
Expand Down Expand Up @@ -199,6 +200,7 @@ expression: parse_ast
keywords: [],
},
),
debug_text: None,
conversion: None,
format_spec: None,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ expression: parse_ast
keywords: [],
},
),
debug_text: None,
conversion: None,
format_spec: None,
},
Expand Down Expand Up @@ -249,6 +250,7 @@ expression: parse_ast
ctx: Load,
},
),
debug_text: None,
conversion: None,
format_spec: None,
},
Expand Down Expand Up @@ -336,6 +338,7 @@ expression: parse_ast
keywords: [],
},
),
debug_text: None,
conversion: None,
format_spec: None,
},
Expand Down Expand Up @@ -369,6 +372,7 @@ expression: parse_ast
ctx: Load,
},
),
debug_text: None,
conversion: None,
format_spec: None,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ expression: parse_ast
ctx: Load,
},
),
debug_text: None,
conversion: None,
format_spec: None,
},
Expand All @@ -52,6 +53,7 @@ expression: parse_ast
ctx: Load,
},
),
debug_text: None,
conversion: None,
format_spec: None,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ expression: parse_ast
ctx: Load,
},
),
debug_text: None,
conversion: None,
format_spec: None,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ expression: parse_ast
ctx: Load,
},
),
debug_text: None,
conversion: None,
format_spec: None,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ expression: parse_ast
ctx: Load,
},
),
debug_text: None,
conversion: None,
format_spec: None,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,6 @@ source: crates/ruff_python_parser/src/string.rs
expression: parse_ast
---
[
Constant(
ExprConstant {
range: 2..9,
value: Str(
"user=",
),
kind: None,
},
),
Constant(
ExprConstant {
range: 2..9,
value: Str(
"",
),
kind: None,
},
),
FormattedValue(
ExprFormattedValue {
range: 2..9,
Expand All @@ -31,7 +13,13 @@ expression: parse_ast
ctx: Load,
},
),
conversion: Repr,
debug_text: Some(
DebugText {
leading: "",
trailing: "=",
},
),
conversion: None,
format_spec: None,
},
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,6 @@ expression: parse_ast
kind: None,
},
),
Constant(
ExprConstant {
range: 6..13,
value: Str(
"user=",
),
kind: None,
},
),
Constant(
ExprConstant {
range: 6..13,
value: Str(
"",
),
kind: None,
},
),
FormattedValue(
ExprFormattedValue {
range: 6..13,
Expand All @@ -40,7 +22,13 @@ expression: parse_ast
ctx: Load,
},
),
conversion: Repr,
debug_text: Some(
DebugText {
leading: "",
trailing: "=",
},
),
conversion: None,
format_spec: None,
},
),
Expand All @@ -53,24 +41,6 @@ expression: parse_ast
kind: None,
},
),
Constant(
ExprConstant {
range: 28..37,
value: Str(
"second=",
),
kind: None,
},
),
Constant(
ExprConstant {
range: 28..37,
value: Str(
"",
),
kind: None,
},
),
FormattedValue(
ExprFormattedValue {
range: 28..37,
Expand All @@ -81,7 +51,13 @@ expression: parse_ast
ctx: Load,
},
),
conversion: Repr,
debug_text: Some(
DebugText {
leading: "",
trailing: "=",
},
),
conversion: None,
format_spec: None,
},
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,6 @@ source: crates/ruff_python_parser/src/string.rs
expression: parse_ast
---
[
Constant(
ExprConstant {
range: 2..13,
value: Str(
"user=",
),
kind: None,
},
),
Constant(
ExprConstant {
range: 2..13,
value: Str(
"",
),
kind: None,
},
),
FormattedValue(
ExprFormattedValue {
range: 2..13,
Expand All @@ -31,6 +13,12 @@ expression: parse_ast
ctx: Load,
},
),
debug_text: Some(
DebugText {
leading: "",
trailing: "=",
},
),
conversion: None,
format_spec: Some(
JoinedStr(
Expand Down
Loading
Loading