Skip to content

Commit

Permalink
bpo-40176: Improve error messages for unclosed string literals (GH-19346
Browse files Browse the repository at this point in the history
)

Automerge-Triggered-By: GH:isidentical
  • Loading branch information
isidentical committed Jan 20, 2021
1 parent c3f167d commit a698d52
Show file tree
Hide file tree
Showing 7 changed files with 34 additions and 32 deletions.
2 changes: 0 additions & 2 deletions Include/errcode.h
Expand Up @@ -26,8 +26,6 @@ extern "C" {
#define E_TOODEEP 20 /* Too many indentation levels */
#define E_DEDENT 21 /* No matching outer block for dedent */
#define E_DECODE 22 /* Error in decoding into Unicode */
#define E_EOFS 23 /* EOF in triple-quoted string */
#define E_EOLS 24 /* EOL in single-quoted string */
#define E_LINECONT 25 /* Unexpected characters after a line continuation */
#define E_BADSINGLE 27 /* Ill-formed single statement input */

Expand Down
24 changes: 13 additions & 11 deletions Lib/test/test_eof.py
Expand Up @@ -7,23 +7,25 @@
import unittest

class EOFTestCase(unittest.TestCase):
def test_EOFC(self):
expect = "EOL while scanning string literal (<string>, line 1)"
try:
eval("""'this is a test\
""")
except SyntaxError as msg:
self.assertEqual(str(msg), expect)
else:
raise support.TestFailed
def test_EOF_single_quote(self):
expect = "unterminated string literal (detected at line 1) (<string>, line 1)"
for quote in ("'", "\""):
try:
eval(f"""{quote}this is a test\
""")
except SyntaxError as msg:
self.assertEqual(str(msg), expect)
self.assertEqual(msg.offset, 1)
else:
raise support.TestFailed

def test_EOFS(self):
expect = ("EOF while scanning triple-quoted string literal "
"(<string>, line 1)")
expect = ("unterminated triple-quoted string literal (detected at line 1) (<string>, line 1)")
try:
eval("""'''this is a test""")
except SyntaxError as msg:
self.assertEqual(str(msg), expect)
self.assertEqual(msg.offset, 1)
else:
raise support.TestFailed

Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_exceptions.py
Expand Up @@ -206,7 +206,7 @@ def testSyntaxErrorOffset(self):
check(b'# -*- coding: cp1251 -*-\nPython = "\xcf\xb3\xf2\xee\xed" +',
2, 19, encoding='cp1251')
check(b'Python = "\xcf\xb3\xf2\xee\xed" +', 1, 18)
check('x = "a', 1, 7)
check('x = "a', 1, 5)
check('lambda x: x = 2', 1, 1)
check('f{a + b + c}', 1, 2)
check('[file for str(file) in []\n])', 1, 11)
Expand Down Expand Up @@ -238,7 +238,7 @@ def bar():
def baz():
'''quux'''
""", 9, 20)
""", 9, 24)
check("pass\npass\npass\n(1+)\npass\npass\npass", 4, 4)
check("(1+)", 1, 4)

Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_fstring.py
Expand Up @@ -661,7 +661,7 @@ def test_parens_in_expressions(self):
["f'{3)+(4}'",
])

self.assertAllRaise(SyntaxError, 'EOL while scanning string literal',
self.assertAllRaise(SyntaxError, 'unterminated string literal',
["f'{\n}'",
])

Expand Down
@@ -0,0 +1,2 @@
Syntax errors for unterminated string literals now point to the start
of the string instead of reporting EOF/EOL.
6 changes: 0 additions & 6 deletions Parser/pegen.c
Expand Up @@ -327,12 +327,6 @@ tokenizer_error(Parser *p)
case E_TOKEN:
msg = "invalid token";
break;
case E_EOFS:
RAISE_SYNTAX_ERROR("EOF while scanning triple-quoted string literal");
return -1;
case E_EOLS:
RAISE_SYNTAX_ERROR("EOL while scanning string literal");
return -1;
case E_EOF:
if (p->tok->level) {
raise_unclosed_parentheses_error(p);
Expand Down
26 changes: 16 additions & 10 deletions Parser/tokenizer.c
Expand Up @@ -1739,20 +1739,26 @@ tok_get(struct tok_state *tok, const char **p_start, const char **p_end)
/* Get rest of string */
while (end_quote_size != quote_size) {
c = tok_nextc(tok);
if (c == EOF) {
if (c == EOF || (quote_size == 1 && c == '\n')) {
// shift the tok_state's location into
// the start of string, and report the error
// from the initial quote character
tok->cur = (char *)tok->start;
tok->cur++;
tok->line_start = tok->multi_line_start;
int start = tok->lineno;
tok->lineno = tok->first_lineno;

if (quote_size == 3) {
tok->done = E_EOFS;
return syntaxerror(tok,
"unterminated triple-quoted string literal"
" (detected at line %d)", start);
}
else {
tok->done = E_EOLS;
return syntaxerror(tok,
"unterminated string literal (detected at"
" line %d)", start);
}
tok->cur = tok->inp;
return ERRORTOKEN;
}
if (quote_size == 1 && c == '\n') {
tok->done = E_EOLS;
tok->cur = tok->inp;
return ERRORTOKEN;
}
if (c == quote) {
end_quote_size += 1;
Expand Down

0 comments on commit a698d52

Please sign in to comment.