in an earlier post i briefly talked about how SyntaxError is a lie, showing some expressions which aren't prohibited by the grammar but which still raise SyntaxError when compiled. this blog post expands on that with a bunch more examples.
(see the very end for notes on what i did and didn't decide to include in this list)
the list
return/yield outside function
>>> return 67
File "<python-input-0>", line 1
return 67
^^^^^^^^^
SyntaxError: 'return' outside function
>>> yield 67
File "<python-input-1>", line 1
yield 67
^^^^^^^^
SyntaxError: 'yield' outside function
break/continue outside loop
>>> if True:
... break
...
File "<python-input-2>", line 2
break
^^^^^
SyntaxError: 'break' outside loop
>>> def f():
... continue
...
File "<python-input-3>", line 2
continue
^^^^^^^^
SyntaxError: 'continue' not properly in loop
await outside function
>>> await f()
File "<python-input-4>", line 1
await f()
^^^^^^^^^
SyntaxError: 'await' outside function
await outside async function
>>> def f():
... await g()
...
File "<python-input-5>", line 2
await g()
^^^^^^^^^
SyntaxError: 'await' outside async function
async for/with outside async function
>>> async for i in []: pass
File "<python-input-6>", line 1
async for i in []: pass
^^^^^^^^^^^^^^^^^^^^^^^
SyntaxError: 'async for' outside async function
>>> def f():
... async with 67 as x: pass
...
File "<python-input-7>", line 2
async with 67 as x: pass
^^^^^^^^^^^^^^^^^^^^^^^^
SyntaxError: 'async with' outside async function
cannot assign to __debug__
>>> __debug__ = 67
File "<python-input-8>", line 1
__debug__ = 67
^^^^^^^^^
SyntaxError: cannot assign to __debug__
>>> (__debug__ := 67)
File "<python-input-9>", line 1
(__debug__ := 67)
^^^^^^^^^
SyntaxError: cannot assign to __debug__
>>> x.__debug__ = 67
File "<python-input-10>", line 1
x.__debug__ = 67
^^^^^^^^^^^
SyntaxError: cannot assign to __debug__
cannot delete __debug__
>>> del __debug__
File "<python-input-11>", line 1
del __debug__
^^^^^^^^^
SyntaxError: cannot delete __debug__
>>> del x.__debug__
File "<python-input-12>", line 1
del x.__debug__
^^^^^^^^^^^
SyntaxError: cannot delete __debug__
assignment expression cannot rebind comprehension iteration variable
>>> (i := 67 for i in x)
File "<python-input-13>", line 1
(i := 67 for i in x)
^
SyntaxError: assignment expression cannot rebind comprehension iteration variable 'i'
keyword argument repeated
>>> f(x=67, x=420)
File "<python-input-14>", line 1
f(x=67, x=420)
^^^^^
SyntaxError: keyword argument repeated: x
duplicate parameter in function definition
>>> def f(x, x): pass
File "<python-input-15>", line 1
def f(x, x): pass
^
SyntaxError: duplicate argument 'x' in function definition
duplicate type parameter
>>> type T[x, x] = int
File "<python-input-16>", line 1
type T[x, x] = int
^
SyntaxError: duplicate type parameter 'x'
nonlocal declaration not allowed at module level
>>> nonlocal x
File "<python-input-17>", line 1
nonlocal x
^^^^^^^^^^
SyntaxError: nonlocal declaration not allowed at module level
no binding found for nonlocal
>>> def f():
... nonlocal x
...
File "<python-input-18>", line 2
nonlocal x
^^^^^^^^^^
SyntaxError: no binding for nonlocal 'x' found
parameter name can't be global or nonlocal
>>> def f(x):
... global x
...
File "<python-input-19>", line 2
global x
^^^^^^^^
SyntaxError: name 'x' is parameter and global
>>> def f(x):
... nonlocal x
...
File "<python-input-20>", line 2
nonlocal x
^^^^^^^^^^
SyntaxError: name 'x' is parameter and nonlocal
name is nonlocal and global
>>> def f():
... global x
... nonlocal x
...
File "<python-input-21>", line 2
global x
^^^^^^^^
SyntaxError: name 'x' is nonlocal and global
name is assigned to before global/nonlocal declaration
>>> def f():
... x = 67
... global x
...
File "<python-input-22>", line 3
global x
^^^^^^^^
SyntaxError: name 'x' is assigned to before global declaration
>>> def f():
... x = 67
... nonlocal x
...
File "<python-input-23>", line 3
nonlocal x
^^^^^^^^^^
SyntaxError: name 'x' is assigned to before nonlocal declaration
annotated name can't be global/nonlocal
>>> def f():
... global x
... x: int = 67
...
File "<python-input-24>", line 3
x: int = 67
^^^^^^^^^^^
SyntaxError: annotated name 'x' can't be global
>>> def f(x):
... def g():
... nonlocal x
... x: int = 67
...
File "<python-input-25>", line 4
x: int = 67
^^^^^^^^^^^
SyntaxError: annotated name 'x' can't be nonlocal
footnote about what i did and didn't include in this list
the line between "syntax" error and "semantic" error is kinda blurry, especially for an interpreted language like python. most of the above examples will fail during parsing, but despite this, i view them as semantic errors, because they aren't prohibited by the grammar.
even still, there's lots of examples which i omitted because i find it ambiguous whether they should be considered "syntax" errors. i've only included examples which i believe are decidedly semantic, hence why i say this list is "incomplete". but there's certainly room for endless disagreement and bikeshedding.
my background is mostly compilers, not interpreters (yes i'm aware the line there is also extremely blurry, those definitions are mostly vibes but that's fine for what i'm trying to say here). so if i'm writing a compiler, i'd check that a break statement is used inside of a loop during the type-checking phase, rather than the parsing phase. so that's a big part of why i consider break outside of a loop to be a semantic error, rather than a syntax error. but for a python interpreter, where there's no separate type checking phase before bytecode is emitted from the AST, catching this at parse-time is a perfectly reasonable thing to do, so someone else might say this is a syntax error because of that. i'm not sure it's possible to resolve the disagreement there, so i've written this post based on my own vibes. i hope this makes sense