Skip to content

Commit 8c83c23

Browse files
ilevkivskyiserhiy-storchaka
authored andcommitted
bpo-28936: Detect lexically first syntax error first (python#4097)
Lexically first global and nonlocal syntax errors at given scope should be detected first.
1 parent 66c88ce commit 8c83c23

File tree

3 files changed

+45
-27
lines changed

3 files changed

+45
-27
lines changed

Lib/test/test_syntax.py

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -399,13 +399,26 @@
399399
400400
Misuse of the nonlocal and global statement can lead to a few unique syntax errors.
401401
402+
>>> def f():
403+
... print(x)
404+
... global x
405+
Traceback (most recent call last):
406+
...
407+
SyntaxError: name 'x' is used prior to global declaration
408+
402409
>>> def f():
403410
... x = 1
404411
... global x
405412
Traceback (most recent call last):
406413
...
407414
SyntaxError: name 'x' is assigned to before global declaration
408415
416+
>>> def f(x):
417+
... global x
418+
Traceback (most recent call last):
419+
...
420+
SyntaxError: name 'x' is parameter and global
421+
409422
>>> def f():
410423
... x = 1
411424
... def g():
@@ -560,7 +573,6 @@
560573

561574
import re
562575
import unittest
563-
import warnings
564576

565577
from test import support
566578

@@ -596,19 +608,25 @@ def test_assign_call(self):
596608
def test_assign_del(self):
597609
self._check_error("del f()", "delete")
598610

599-
def test_global_err_then_warn(self):
600-
# Bug #763201: The SyntaxError raised for one global statement
601-
# shouldn't be clobbered by a SyntaxWarning issued for a later one.
611+
def test_global_param_err_first(self):
602612
source = """if 1:
603613
def error(a):
604614
global a # SyntaxError
605-
def warning():
615+
def error2():
616+
b = 1
617+
global b # SyntaxError
618+
"""
619+
self._check_error(source, "parameter and global", lineno=3)
620+
621+
def test_nonlocal_param_err_first(self):
622+
source = """if 1:
623+
def error(a):
624+
nonlocal a # SyntaxError
625+
def error2():
606626
b = 1
607-
global b # SyntaxWarning
627+
global b # SyntaxError
608628
"""
609-
warnings.filterwarnings(action='ignore', category=SyntaxWarning)
610-
self._check_error(source, "global")
611-
warnings.filters.pop(0)
629+
self._check_error(source, "parameter and nonlocal", lineno=3)
612630

613631
def test_break_outside_loop(self):
614632
self._check_error("break", "outside loop")
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Ensure that lexically first syntax error involving a parameter and ``global``
2+
or ``nonlocal`` is detected first at a given scope. Patch by Ivan Levkivskyi.

Python/symtable.c

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@
99
#include "structmember.h"
1010

1111
/* error strings used for warnings */
12+
#define GLOBAL_PARAM \
13+
"name '%U' is parameter and global"
14+
15+
#define NONLOCAL_PARAM \
16+
"name '%U' is parameter and nonlocal"
17+
1218
#define GLOBAL_AFTER_ASSIGN \
1319
"name '%U' is assigned to before global declaration"
1420

@@ -465,12 +471,6 @@ analyze_name(PySTEntryObject *ste, PyObject *scopes, PyObject *name, long flags,
465471
PyObject *global)
466472
{
467473
if (flags & DEF_GLOBAL) {
468-
if (flags & DEF_PARAM) {
469-
PyErr_Format(PyExc_SyntaxError,
470-
"name '%U' is parameter and global",
471-
name);
472-
return error_at_directive(ste, name);
473-
}
474474
if (flags & DEF_NONLOCAL) {
475475
PyErr_Format(PyExc_SyntaxError,
476476
"name '%U' is nonlocal and global",
@@ -485,12 +485,6 @@ analyze_name(PySTEntryObject *ste, PyObject *scopes, PyObject *name, long flags,
485485
return 1;
486486
}
487487
if (flags & DEF_NONLOCAL) {
488-
if (flags & DEF_PARAM) {
489-
PyErr_Format(PyExc_SyntaxError,
490-
"name '%U' is parameter and nonlocal",
491-
name);
492-
return error_at_directive(ste, name);
493-
}
494488
if (!bound) {
495489
PyErr_Format(PyExc_SyntaxError,
496490
"nonlocal declaration not allowed at module level");
@@ -1284,9 +1278,11 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
12841278
long cur = symtable_lookup(st, name);
12851279
if (cur < 0)
12861280
VISIT_QUIT(st, 0);
1287-
if (cur & (DEF_LOCAL | USE | DEF_ANNOT)) {
1288-
char* msg;
1289-
if (cur & USE) {
1281+
if (cur & (DEF_PARAM | DEF_LOCAL | USE | DEF_ANNOT)) {
1282+
const char* msg;
1283+
if (cur & DEF_PARAM) {
1284+
msg = GLOBAL_PARAM;
1285+
} else if (cur & USE) {
12901286
msg = GLOBAL_AFTER_USE;
12911287
} else if (cur & DEF_ANNOT) {
12921288
msg = GLOBAL_ANNOT;
@@ -1315,9 +1311,11 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
13151311
long cur = symtable_lookup(st, name);
13161312
if (cur < 0)
13171313
VISIT_QUIT(st, 0);
1318-
if (cur & (DEF_LOCAL | USE | DEF_ANNOT)) {
1319-
char* msg;
1320-
if (cur & USE) {
1314+
if (cur & (DEF_PARAM | DEF_LOCAL | USE | DEF_ANNOT)) {
1315+
const char* msg;
1316+
if (cur & DEF_PARAM) {
1317+
msg = NONLOCAL_PARAM;
1318+
} else if (cur & USE) {
13211319
msg = NONLOCAL_AFTER_USE;
13221320
} else if (cur & DEF_ANNOT) {
13231321
msg = NONLOCAL_ANNOT;

0 commit comments

Comments
 (0)