Skip to content

Commit 7fb8fb6

Browse files
committed
gh-149277: Fix error position for invalid numeric literals
1 parent 219768f commit 7fb8fb6

4 files changed

Lines changed: 25 additions & 5 deletions

File tree

Lib/test/test_grammar.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import_helper,
2222
skip_emscripten_stack_overflow,
2323
skip_wasi_stack_overflow,
24+
subTests,
2425
)
2526
from test.support.numbers import (
2627
VALID_UNDERSCORE_LITERALS,
@@ -180,6 +181,27 @@ def check(test, error=False):
180181
check("[0x1for x in ()]")
181182
check("[0xfor x in ()]")
182183

184+
@subTests('source,offset,msg',
185+
[("0xfg", 4, "hexadecimal"),
186+
("0x9g", 4, "hexadecimal"),
187+
("0b1z", 4, "binary"),
188+
("0o7q", 4, "octal"),
189+
("9spam", 2, " decimal"),
190+
("0xfspam", 4, "hexadecimal"),
191+
("1.0x", 4, " decimal"),
192+
("1e3w", 4, " decimal"),
193+
("1jz", 3, "imaginary"),
194+
("0xI", 3, "hexadecimal"),
195+
("0bz", 3, "binary"),
196+
])
197+
def test_end_of_numerical_literals_offset(self, source, offset, msg):
198+
# gh-149277: verify the error caret points at the first invalid
199+
# character, not the last valid digit.
200+
with self.assertRaises(SyntaxError) as cm:
201+
compile(source, "<test>", "eval")
202+
self.assertEqual(cm.exception.offset, offset)
203+
self.assertIn(msg, cm.exception.msg)
204+
183205
def test_string_literals(self):
184206
x = ''; y = ""; self.assertTrue(len(x) == 0 and x == y)
185207
x = '\''; y = "'"; self.assertTrue(len(x) == 1 and x == y and ord(x) == 39)

Lib/test/test_tokenize.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2277,7 +2277,7 @@ def test_extra_tokens_relaxes_lexer_errors(self):
22772277
cases = [
22782278
(
22792279
"2sin(x)",
2280-
("invalid decimal literal", (1, 1)),
2280+
("invalid decimal literal", (1, 2)),
22812281
[
22822282
(token.NUMBER, "2", (1, 0), (1, 1)),
22832283
(token.NAME, "sin", (1, 1), (1, 4)),
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix the :exc:`SyntaxError` caret position for invalid numeric literals to
2+
point at the first invalid character.

Parser/lexer/number.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ verify_end_of_number(struct tok_state *tok, int c, const char *kind) {
8080
}
8181
else /* In future releases, only error will remain. */
8282
if (c < 128 && is_potential_identifier_char(c)) {
83-
tok_backup(tok, c);
8483
_PyTokenizer_syntaxerror(tok, "invalid %s literal", kind);
8584
return 0;
8685
}
@@ -130,7 +129,6 @@ _PyLexer_scan_number(struct tok_state *tok, struct token *token, int c,
130129
c = tok_nextc(tok);
131130
}
132131
if (!Py_ISXDIGIT(c)) {
133-
tok_backup(tok, c);
134132
return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid hexadecimal literal"));
135133
}
136134
do {
@@ -154,7 +152,6 @@ _PyLexer_scan_number(struct tok_state *tok, struct token *token, int c,
154152
"invalid digit '%c' in octal literal", c));
155153
}
156154
else {
157-
tok_backup(tok, c);
158155
return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid octal literal"));
159156
}
160157
}
@@ -182,7 +179,6 @@ _PyLexer_scan_number(struct tok_state *tok, struct token *token, int c,
182179
return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid digit '%c' in binary literal", c));
183180
}
184181
else {
185-
tok_backup(tok, c);
186182
return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid binary literal"));
187183
}
188184
}

0 commit comments

Comments
 (0)