bpo-33306: Improve SyntaxError messages for unbalanced parentheses. (GH-6516)
This commit is contained in:
parent
bdabb0737c
commit
94cf308ee2
|
@ -1004,10 +1004,14 @@ non-important content
|
||||||
self.assertEqual('{d[0]}'.format(d=d), 'integer')
|
self.assertEqual('{d[0]}'.format(d=d), 'integer')
|
||||||
|
|
||||||
def test_invalid_expressions(self):
|
def test_invalid_expressions(self):
|
||||||
self.assertAllRaise(SyntaxError, 'invalid syntax',
|
self.assertAllRaise(SyntaxError,
|
||||||
[r"f'{a[4)}'",
|
r"closing parenthesis '\)' does not match "
|
||||||
r"f'{a(4]}'",
|
r"opening parenthesis '\[' \(<fstring>, line 1\)",
|
||||||
])
|
[r"f'{a[4)}'"])
|
||||||
|
self.assertAllRaise(SyntaxError,
|
||||||
|
r"closing parenthesis '\]' does not match "
|
||||||
|
r"opening parenthesis '\(' \(<fstring>, line 1\)",
|
||||||
|
[r"f'{a(4]}'"])
|
||||||
|
|
||||||
def test_errors(self):
|
def test_errors(self):
|
||||||
# see issue 26287
|
# see issue 26287
|
||||||
|
|
|
@ -133,7 +133,7 @@ class HelperFunctionsTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_addpackage_import_bad_syntax(self):
|
def test_addpackage_import_bad_syntax(self):
|
||||||
# Issue 10642
|
# Issue 10642
|
||||||
pth_dir, pth_fn = self.make_pth("import bad)syntax\n")
|
pth_dir, pth_fn = self.make_pth("import bad-syntax\n")
|
||||||
with captured_stderr() as err_out:
|
with captured_stderr() as err_out:
|
||||||
site.addpackage(pth_dir, pth_fn, set())
|
site.addpackage(pth_dir, pth_fn, set())
|
||||||
self.assertRegex(err_out.getvalue(), "line 1")
|
self.assertRegex(err_out.getvalue(), "line 1")
|
||||||
|
@ -143,7 +143,7 @@ class HelperFunctionsTests(unittest.TestCase):
|
||||||
# order doesn't matter. The next three could be a single check
|
# order doesn't matter. The next three could be a single check
|
||||||
# but my regex foo isn't good enough to write it.
|
# but my regex foo isn't good enough to write it.
|
||||||
self.assertRegex(err_out.getvalue(), 'Traceback')
|
self.assertRegex(err_out.getvalue(), 'Traceback')
|
||||||
self.assertRegex(err_out.getvalue(), r'import bad\)syntax')
|
self.assertRegex(err_out.getvalue(), r'import bad-syntax')
|
||||||
self.assertRegex(err_out.getvalue(), 'SyntaxError')
|
self.assertRegex(err_out.getvalue(), 'SyntaxError')
|
||||||
|
|
||||||
def test_addpackage_import_bad_exec(self):
|
def test_addpackage_import_bad_exec(self):
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Improved syntax error messages for unbalanced parentheses.
|
|
@ -1842,12 +1842,44 @@ tok_get(struct tok_state *tok, char **p_start, char **p_end)
|
||||||
case '(':
|
case '(':
|
||||||
case '[':
|
case '[':
|
||||||
case '{':
|
case '{':
|
||||||
|
#ifndef PGEN
|
||||||
|
if (tok->level >= MAXLEVEL) {
|
||||||
|
return syntaxerror(tok, "too many nested parentheses");
|
||||||
|
}
|
||||||
|
tok->parenstack[tok->level] = c;
|
||||||
|
tok->parenlinenostack[tok->level] = tok->lineno;
|
||||||
|
#endif
|
||||||
tok->level++;
|
tok->level++;
|
||||||
break;
|
break;
|
||||||
case ')':
|
case ')':
|
||||||
case ']':
|
case ']':
|
||||||
case '}':
|
case '}':
|
||||||
|
#ifndef PGEN
|
||||||
|
if (!tok->level) {
|
||||||
|
return syntaxerror(tok, "unmatched '%c'", c);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
tok->level--;
|
tok->level--;
|
||||||
|
#ifndef PGEN
|
||||||
|
int opening = tok->parenstack[tok->level];
|
||||||
|
if (!((opening == '(' && c == ')') ||
|
||||||
|
(opening == '[' && c == ']') ||
|
||||||
|
(opening == '{' && c == '}')))
|
||||||
|
{
|
||||||
|
if (tok->parenlinenostack[tok->level] != tok->lineno) {
|
||||||
|
return syntaxerror(tok,
|
||||||
|
"closing parenthesis '%c' does not match "
|
||||||
|
"opening parenthesis '%c' on line %d",
|
||||||
|
c, opening, tok->parenlinenostack[tok->level]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return syntaxerror(tok,
|
||||||
|
"closing parenthesis '%c' does not match "
|
||||||
|
"opening parenthesis '%c'",
|
||||||
|
c, opening);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ extern "C" {
|
||||||
#include "token.h" /* For token types */
|
#include "token.h" /* For token types */
|
||||||
|
|
||||||
#define MAXINDENT 100 /* Max indentation level */
|
#define MAXINDENT 100 /* Max indentation level */
|
||||||
|
#define MAXLEVEL 200 /* Max parentheses level */
|
||||||
|
|
||||||
enum decoding_state {
|
enum decoding_state {
|
||||||
STATE_INIT,
|
STATE_INIT,
|
||||||
|
@ -39,14 +40,16 @@ struct tok_state {
|
||||||
int lineno; /* Current line number */
|
int lineno; /* Current line number */
|
||||||
int level; /* () [] {} Parentheses nesting level */
|
int level; /* () [] {} Parentheses nesting level */
|
||||||
/* Used to allow free continuations inside them */
|
/* Used to allow free continuations inside them */
|
||||||
/* Stuff for checking on different tab sizes */
|
|
||||||
#ifndef PGEN
|
#ifndef PGEN
|
||||||
|
char parenstack[MAXLEVEL];
|
||||||
|
int parenlinenostack[MAXLEVEL];
|
||||||
/* pgen doesn't have access to Python codecs, it cannot decode the input
|
/* pgen doesn't have access to Python codecs, it cannot decode the input
|
||||||
filename. The bytes filename might be kept, but it is only used by
|
filename. The bytes filename might be kept, but it is only used by
|
||||||
indenterror() and it is not really needed: pgen only compiles one file
|
indenterror() and it is not really needed: pgen only compiles one file
|
||||||
(Grammar/Grammar). */
|
(Grammar/Grammar). */
|
||||||
PyObject *filename;
|
PyObject *filename;
|
||||||
#endif
|
#endif
|
||||||
|
/* Stuff for checking on different tab sizes */
|
||||||
int altindstack[MAXINDENT]; /* Stack of alternate indents */
|
int altindstack[MAXINDENT]; /* Stack of alternate indents */
|
||||||
/* Stuff for PEP 0263 */
|
/* Stuff for PEP 0263 */
|
||||||
enum decoding_state decoding_state;
|
enum decoding_state decoding_state;
|
||||||
|
|
Loading…
Reference in New Issue