From 4f57f3df6fc63f24e3099dfbfc85e07259a6123a Mon Sep 17 00:00:00 2001 From: sobolevn Date: Wed, 30 Apr 2025 14:38:29 +0300 Subject: [PATCH 1/4] gh-133196: Improve error message for `ft""` case --- Lib/test/test_syntax.py | 9 +++++++++ Parser/lexer/lexer.c | 9 +++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index 55492350d00714..7d653acc6a2418 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -1877,6 +1877,15 @@ Traceback (most recent call last): SyntaxError: cannot assign to f-string expression here. Maybe you meant '==' instead of '='? +>>> ft'abc' +Traceback (most recent call last): +SyntaxError: can't use 'f' and 't' string prefixes at the same time + +>>> x = 1 +>>> tf"{x=}" +Traceback (most recent call last): +SyntaxError: can't use 'f' and 't' string prefixes at the same time + >>> t'{x}' = 42 Traceback (most recent call last): SyntaxError: cannot assign to t-string expression here. Maybe you meant '==' instead of '='? diff --git a/Parser/lexer/lexer.c b/Parser/lexer/lexer.c index ca01cab9937a85..c80984fc550daf 100644 --- a/Parser/lexer/lexer.c +++ b/Parser/lexer/lexer.c @@ -660,10 +660,10 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t else if (!(saw_r || saw_u) && (c == 'r' || c == 'R')) { saw_r = 1; } - else if (!(saw_f || saw_b || saw_u || saw_t) && (c == 'f' || c == 'F')) { + else if (!(saw_f || saw_b || saw_u) && (c == 'f' || c == 'F')) { saw_f = 1; } - else if (!(saw_t || saw_b || saw_u || saw_f) && (c == 't' || c == 'T')) { + else if (!(saw_t || saw_b || saw_u) && (c == 't' || c == 'T')) { saw_t = 1; } else { @@ -671,6 +671,11 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t } c = tok_nextc(tok); if (c == '"' || c == '\'') { + if (saw_f && saw_t) { + return MAKE_TOKEN(_PyTokenizer_syntaxerror( + tok, + "can't use 'f' and 't' string prefixes at the same time")); + } if (saw_f || saw_t) { goto f_string_quote; } From b269ebb9bd8a8958bd3823ff0449f14e457c9c2e Mon Sep 17 00:00:00 2001 From: sobolevn Date: Wed, 30 Apr 2025 14:49:10 +0300 Subject: [PATCH 2/4] Also handle `tb` cases --- Lib/test/test_syntax.py | 8 ++++++++ Parser/lexer/lexer.c | 11 +++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index 7d653acc6a2418..d63e0458dbfa87 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -1886,6 +1886,14 @@ Traceback (most recent call last): SyntaxError: can't use 'f' and 't' string prefixes at the same time +>>> tb'' +Traceback (most recent call last): +SyntaxError: can't use 't' prefix on bytes + +>>> bt"text" +Traceback (most recent call last): +SyntaxError: can't use 't' prefix on bytes + >>> t'{x}' = 42 Traceback (most recent call last): SyntaxError: cannot assign to t-string expression here. Maybe you meant '==' instead of '='? diff --git a/Parser/lexer/lexer.c b/Parser/lexer/lexer.c index c80984fc550daf..724cdddbe59b06 100644 --- a/Parser/lexer/lexer.c +++ b/Parser/lexer/lexer.c @@ -648,7 +648,7 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t /* Process the various legal combinations of b"", r"", u"", and f"". */ int saw_b = 0, saw_r = 0, saw_u = 0, saw_f = 0, saw_t = 0; while (1) { - if (!(saw_b || saw_u || saw_f || saw_t) && (c == 'b' || c == 'B')) + if (!(saw_b || saw_u || saw_f) && (c == 'b' || c == 'B')) saw_b = 1; /* Since this is a backwards compatibility support literal we don't want to support it in arbitrary order like byte literals. */ @@ -663,7 +663,7 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t else if (!(saw_f || saw_b || saw_u) && (c == 'f' || c == 'F')) { saw_f = 1; } - else if (!(saw_t || saw_b || saw_u) && (c == 't' || c == 'T')) { + else if (!(saw_t || saw_u) && (c == 't' || c == 'T')) { saw_t = 1; } else { @@ -671,11 +671,18 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t } c = tok_nextc(tok); if (c == '"' || c == '\'') { + if (saw_b && saw_t) { + return MAKE_TOKEN(_PyTokenizer_syntaxerror( + tok, + "can't use 't' prefix on bytes")); + } if (saw_f && saw_t) { return MAKE_TOKEN(_PyTokenizer_syntaxerror( tok, "can't use 'f' and 't' string prefixes at the same time")); } + + // Handle valid f or t string creation: if (saw_f || saw_t) { goto f_string_quote; } From 40668a51a64c78ccf9fe516747d39093fa86f467 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Wed, 30 Apr 2025 15:19:57 +0300 Subject: [PATCH 3/4] Address review --- Lib/test/test_syntax.py | 5 ++--- Parser/lexer/lexer.c | 12 +++++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index d63e0458dbfa87..a62d7c60eee809 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -1881,18 +1881,17 @@ Traceback (most recent call last): SyntaxError: can't use 'f' and 't' string prefixes at the same time ->>> x = 1 >>> tf"{x=}" Traceback (most recent call last): SyntaxError: can't use 'f' and 't' string prefixes at the same time >>> tb'' Traceback (most recent call last): -SyntaxError: can't use 't' prefix on bytes +SyntaxError: can't use 'b' and 't' string prefixes at the same time >>> bt"text" Traceback (most recent call last): -SyntaxError: can't use 't' prefix on bytes +SyntaxError: can't use 'b' and 't' string prefixes at the same time >>> t'{x}' = 42 Traceback (most recent call last): diff --git a/Parser/lexer/lexer.c b/Parser/lexer/lexer.c index 724cdddbe59b06..b1d9fd296ae3f3 100644 --- a/Parser/lexer/lexer.c +++ b/Parser/lexer/lexer.c @@ -672,13 +672,15 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t c = tok_nextc(tok); if (c == '"' || c == '\'') { if (saw_b && saw_t) { - return MAKE_TOKEN(_PyTokenizer_syntaxerror( - tok, - "can't use 't' prefix on bytes")); + return MAKE_TOKEN(_PyTokenizer_syntaxerror_known_range( + tok, (int)(tok->start + 1 - tok->line_start), + (int)(tok->cur - tok->line_start), + "can't use 'b' and 't' string prefixes at the same time")); } if (saw_f && saw_t) { - return MAKE_TOKEN(_PyTokenizer_syntaxerror( - tok, + return MAKE_TOKEN(_PyTokenizer_syntaxerror_known_range( + tok, (int)(tok->start + 1 - tok->line_start), + (int)(tok->cur - tok->line_start), "can't use 'f' and 't' string prefixes at the same time")); } From a4aa4c089ee16d6c51f732f9ef8fe71bae0eb237 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Wed, 30 Apr 2025 16:48:30 +0300 Subject: [PATCH 4/4] together --- Lib/test/test_syntax.py | 8 ++++---- Parser/lexer/lexer.c | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index a62d7c60eee809..7ef4b735fcb805 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -1879,19 +1879,19 @@ >>> ft'abc' Traceback (most recent call last): -SyntaxError: can't use 'f' and 't' string prefixes at the same time +SyntaxError: can't use 'f' and 't' string prefixes together >>> tf"{x=}" Traceback (most recent call last): -SyntaxError: can't use 'f' and 't' string prefixes at the same time +SyntaxError: can't use 'f' and 't' string prefixes together >>> tb'' Traceback (most recent call last): -SyntaxError: can't use 'b' and 't' string prefixes at the same time +SyntaxError: can't use 'b' and 't' string prefixes together >>> bt"text" Traceback (most recent call last): -SyntaxError: can't use 'b' and 't' string prefixes at the same time +SyntaxError: can't use 'b' and 't' string prefixes together >>> t'{x}' = 42 Traceback (most recent call last): diff --git a/Parser/lexer/lexer.c b/Parser/lexer/lexer.c index b1d9fd296ae3f3..98adf7447c5626 100644 --- a/Parser/lexer/lexer.c +++ b/Parser/lexer/lexer.c @@ -675,13 +675,13 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t return MAKE_TOKEN(_PyTokenizer_syntaxerror_known_range( tok, (int)(tok->start + 1 - tok->line_start), (int)(tok->cur - tok->line_start), - "can't use 'b' and 't' string prefixes at the same time")); + "can't use 'b' and 't' string prefixes together")); } if (saw_f && saw_t) { return MAKE_TOKEN(_PyTokenizer_syntaxerror_known_range( tok, (int)(tok->start + 1 - tok->line_start), (int)(tok->cur - tok->line_start), - "can't use 'f' and 't' string prefixes at the same time")); + "can't use 'f' and 't' string prefixes together")); } // Handle valid f or t string creation: