diff --git a/ast/src/ranged.rs b/ast/src/ranged.rs index f01c15a7..b50e22af 100644 --- a/ast/src/ranged.rs +++ b/ast/src/ranged.rs @@ -1,7 +1,5 @@ use crate::text_size::{TextRange, TextSize}; -pub use crate::builtin::*; - pub trait Ranged { fn range(&self) -> TextRange; diff --git a/parser/src/snapshots/rustpython_parser__string__tests__parse_fstring_escaped_brackets.snap b/parser/src/snapshots/rustpython_parser__string__tests__parse_fstring_escaped_brackets.snap new file mode 100644 index 00000000..7e458fdf --- /dev/null +++ b/parser/src/snapshots/rustpython_parser__string__tests__parse_fstring_escaped_brackets.snap @@ -0,0 +1,15 @@ +--- +source: parser/src/string.rs +expression: parse_ast +--- +[ + Constant( + ExprConstant { + range: 0..10, + value: Str( + "\\{x\\}", + ), + kind: None, + }, + ), +] diff --git a/parser/src/string.rs b/parser/src/string.rs index 22013432..2914aa28 100644 --- a/parser/src/string.rs +++ b/parser/src/string.rs @@ -503,7 +503,11 @@ impl<'a> StringParser<'a> { } '\\' if !self.kind.is_raw() => { self.next_char(); - content.push_str(&self.parse_escaped_char()?); + if let Some('{' | '}') = self.peek() { + content.push('\\'); + } else { + content.push_str(&self.parse_escaped_char()?); + } } _ => { content.push(ch); @@ -956,6 +960,13 @@ mod tests { insta::assert_debug_snapshot!(parse_ast); } + #[test] + fn test_parse_fstring_escaped_brackets() { + let source = "\\{{x\\}}"; + let parse_ast = parse_fstring(source).unwrap(); + insta::assert_debug_snapshot!(parse_ast); + } + #[test] fn test_parse_string_concat() { let source = "'Hello ' 'world'";