Skip to content

Commit a698d52

Browse files
authored
bpo-40176: Improve error messages for unclosed string literals (GH-19346)
Automerge-Triggered-By: GH:isidentical
1 parent c3f167d commit a698d52

File tree

7 files changed

+34
-32
lines changed

7 files changed

+34
-32
lines changed

Include/errcode.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ extern "C" {
2626
#define E_TOODEEP 20 /* Too many indentation levels */
2727
#define E_DEDENT 21 /* No matching outer block for dedent */
2828
#define E_DECODE 22 /* Error in decoding into Unicode */
29-
#define E_EOFS 23 /* EOF in triple-quoted string */
30-
#define E_EOLS 24 /* EOL in single-quoted string */
3129
#define E_LINECONT 25 /* Unexpected characters after a line continuation */
3230
#define E_BADSINGLE 27 /* Ill-formed single statement input */
3331

Lib/test/test_eof.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,25 @@
77
import unittest
88

99
class EOFTestCase(unittest.TestCase):
10-
def test_EOFC(self):
11-
expect = "EOL while scanning string literal (<string>, line 1)"
12-
try:
13-
eval("""'this is a test\
14-
""")
15-
except SyntaxError as msg:
16-
self.assertEqual(str(msg), expect)
17-
else:
18-
raise support.TestFailed
10+
def test_EOF_single_quote(self):
11+
expect = "unterminated string literal (detected at line 1) (<string>, line 1)"
12+
for quote in ("'", "\""):
13+
try:
14+
eval(f"""{quote}this is a test\
15+
""")
16+
except SyntaxError as msg:
17+
self.assertEqual(str(msg), expect)
18+
self.assertEqual(msg.offset, 1)
19+
else:
20+
raise support.TestFailed
1921

2022
def test_EOFS(self):
21-
expect = ("EOF while scanning triple-quoted string literal "
22-
"(<string>, line 1)")
23+
expect = ("unterminated triple-quoted string literal (detected at line 1) (<string>, line 1)")
2324
try:
2425
eval("""'''this is a test""")
2526
except SyntaxError as msg:
2627
self.assertEqual(str(msg), expect)
28+
self.assertEqual(msg.offset, 1)
2729
else:
2830
raise support.TestFailed
2931

Lib/test/test_exceptions.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ def testSyntaxErrorOffset(self):
206206
check(b'# -*- coding: cp1251 -*-\nPython = "\xcf\xb3\xf2\xee\xed" +',
207207
2, 19, encoding='cp1251')
208208
check(b'Python = "\xcf\xb3\xf2\xee\xed" +', 1, 18)
209-
check('x = "a', 1, 7)
209+
check('x = "a', 1, 5)
210210
check('lambda x: x = 2', 1, 1)
211211
check('f{a + b + c}', 1, 2)
212212
check('[file for str(file) in []\n])', 1, 11)
@@ -238,7 +238,7 @@ def bar():
238238
239239
def baz():
240240
'''quux'''
241-
""", 9, 20)
241+
""", 9, 24)
242242
check("pass\npass\npass\n(1+)\npass\npass\npass", 4, 4)
243243
check("(1+)", 1, 4)
244244

Lib/test/test_fstring.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,7 @@ def test_parens_in_expressions(self):
661661
["f'{3)+(4}'",
662662
])
663663

664-
self.assertAllRaise(SyntaxError, 'EOL while scanning string literal',
664+
self.assertAllRaise(SyntaxError, 'unterminated string literal',
665665
["f'{\n}'",
666666
])
667667

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Syntax errors for unterminated string literals now point to the start
2+
of the string instead of reporting EOF/EOL.

Parser/pegen.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -327,12 +327,6 @@ tokenizer_error(Parser *p)
327327
case E_TOKEN:
328328
msg = "invalid token";
329329
break;
330-
case E_EOFS:
331-
RAISE_SYNTAX_ERROR("EOF while scanning triple-quoted string literal");
332-
return -1;
333-
case E_EOLS:
334-
RAISE_SYNTAX_ERROR("EOL while scanning string literal");
335-
return -1;
336330
case E_EOF:
337331
if (p->tok->level) {
338332
raise_unclosed_parentheses_error(p);

Parser/tokenizer.c

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1739,20 +1739,26 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end)
17391739
/* Get rest of string */
17401740
while (end_quote_size != quote_size) {
17411741
c = tok_nextc(tok);
1742-
if (c == EOF) {
1742+
if (c == EOF || (quote_size == 1 && c == '\n')) {
1743+
// shift the tok_state's location into
1744+
// the start of string, and report the error
1745+
// from the initial quote character
1746+
tok->cur = (char *)tok->start;
1747+
tok->cur++;
1748+
tok->line_start = tok->multi_line_start;
1749+
int start = tok->lineno;
1750+
tok->lineno = tok->first_lineno;
1751+
17431752
if (quote_size == 3) {
1744-
tok->done = E_EOFS;
1753+
return syntaxerror(tok,
1754+
"unterminated triple-quoted string literal"
1755+
" (detected at line %d)", start);
17451756
}
17461757
else {
1747-
tok->done = E_EOLS;
1758+
return syntaxerror(tok,
1759+
"unterminated string literal (detected at"
1760+
" line %d)", start);
17481761
}
1749-
tok->cur = tok->inp;
1750-
return ERRORTOKEN;
1751-
}
1752-
if (quote_size == 1 && c == '\n') {
1753-
tok->done = E_EOLS;
1754-
tok->cur = tok->inp;
1755-
return ERRORTOKEN;
17561762
}
17571763
if (c == quote) {
17581764
end_quote_size += 1;

0 commit comments

Comments
 (0)