From 69b81364636905298a8b58306920f344953df871 Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Tue, 3 Oct 2023 19:38:03 +0530 Subject: [PATCH] Avoid curly brace escape in f-string format spec (#7780) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary This PR fixes a bug in the lexer for f-string format spec where it would consider the `{{` (double curly braces) as an escape pattern. This is not the case as evident by the [PEP](https://peps.python.org/pep-0701/#how-to-produce-these-new-tokens) as well but I missed the part: > [..] > * **If in “format specifier mode” (see step 3), an opening brace ({) or a closing brace (}).** > * If not in “format specifier mode” (see step 3), an opening brace ({) or a closing brace (}) that is not immediately followed by another opening/closing brace. ## Test Plan Add a test case to verify the fix and update the snapshot. fixes: #7778 --- crates/ruff_python_parser/src/lexer.rs | 4 +- ...exer__tests__fstring_with_format_spec.snap | 69 ++++++++++++++++++- 2 files changed, 69 insertions(+), 4 deletions(-) diff --git a/crates/ruff_python_parser/src/lexer.rs b/crates/ruff_python_parser/src/lexer.rs index c2aa3772898fe..3ba20e78e88e8 100644 --- a/crates/ruff_python_parser/src/lexer.rs +++ b/crates/ruff_python_parser/src/lexer.rs @@ -620,7 +620,7 @@ impl<'source> Lexer<'source> { } } '{' => { - if self.cursor.second() == '{' { + if self.cursor.second() == '{' && !fstring.is_in_format_spec(self.nesting) { self.cursor.bump(); normalized .push_str(&self.source[TextRange::new(last_offset, self.offset())]); @@ -2047,7 +2047,7 @@ def f(arg=%timeit a = b): #[test] fn test_fstring_with_format_spec() { - let source = r#"f"{foo:} {x=!s:.3f} {x:.{y}f} {'':*^{1:{1}}}""#; + let source = r#"f"{foo:} {x=!s:.3f} {x:.{y}f} {'':*^{1:{1}}} {x:{{1}.pop()}}""#; assert_debug_snapshot!(lex_source(source)); } diff --git a/crates/ruff_python_parser/src/snapshots/ruff_python_parser__lexer__tests__fstring_with_format_spec.snap b/crates/ruff_python_parser/src/snapshots/ruff_python_parser__lexer__tests__fstring_with_format_spec.snap index c41165118ff93..db324bdef0c2d 100644 --- a/crates/ruff_python_parser/src/snapshots/ruff_python_parser__lexer__tests__fstring_with_format_spec.snap +++ b/crates/ruff_python_parser/src/snapshots/ruff_python_parser__lexer__tests__fstring_with_format_spec.snap @@ -191,11 +191,76 @@ expression: lex_source(source) 43..44, ), ( - FStringEnd, + FStringMiddle { + value: " ", + is_raw: false, + }, 44..45, ), + ( + Lbrace, + 45..46, + ), + ( + Name { + name: "x", + }, + 46..47, + ), + ( + Colon, + 47..48, + ), + ( + Lbrace, + 48..49, + ), + ( + Lbrace, + 49..50, + ), + ( + Int { + value: 1, + }, + 50..51, + ), + ( + Rbrace, + 51..52, + ), + ( + Dot, + 52..53, + ), + ( + Name { + name: "pop", + }, + 53..56, + ), + ( + Lpar, + 56..57, + ), + ( + Rpar, + 57..58, + ), + ( + Rbrace, + 58..59, + ), + ( + Rbrace, + 59..60, + ), + ( + FStringEnd, + 60..61, + ), ( Newline, - 45..45, + 61..61, ), ]