diff --git a/ast/src/ranged.rs b/ast/src/ranged.rs index f01c15a..b50e22a 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 0000000..7e458fd --- /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 2201343..2914aa2 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'";