python / python/mypy

Does not catch variable declared global/nonlocal after appearing in the same scope.

Open
#13,560 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

feature topic-variable-scope
Dominant language
Python
Stars
20.6k
Forks
3.3k
PR merge metrics
PR metrics pending

Description

Bug Report

If a program references a variable and then declares it global or nonlocal, mypy does not catch this.

To Reproduce

foo.py:
x = 'x'

from typing import TYPE_CHECKING

def foo() -> None:
	x = 42
	global x
	if TYPE_CHECKING: reveal_type(x)
	print(x)
foo()

$ mypy foo.py
foo.py:8: note: Revealed type is 'builtins.str'

$ python3 foo.py
  File "foo.py", line 7
    global x
    ^
SyntaxError: name 'x' is assigned to before global declaration

Expected Behavior

mypy should report the error on line 7.
This applies to any other usage of 'x',

  • except (x): int (with no assignment)
    which is not a syntax error, as it is not a name binding for 'x'.
    When you fix mypy to catch these syntax errors, be sure and take this into account. The only difference between (x): int and x: int is in the value of ast3.AnnAssign.simple = 0 and 1, resp. ast3.AnnAssign.simple = 0 in both cases if there is an assignment.

Actual Behavior

No error (see above).

Suggestion

I would guess that the fix belongs in the semantic analyzer. When it encounters a global or nonlocal declaration, it should know that the variable name has already been used or bound.
Don't forget to check for ast3.AnnAssign.simple = 1 and not consider the variable to be bound, or even used, in this case.
Note that ast3.AnnAssign.target is an ast3.Name instance, and ast3.AnnAssign.target.expr_context is an ast3.Store instance. This is a bug in the parser. It should be ast3.Load(), or better yet, None, since the variable is not really used at all.

Your Environment

  • Mypy version used: 0.761
  • Python version used: 3.8.10
  • Operating system and version: Ubuntu (WSL on Windows)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in mypy's semantic analyzer and reproduce the issue with the foo.py example from the report. Trace handling of global and nonlocal declarations, including ast3.AnnAssign.simple cases, and verify that x is not incorrectly treated as used or bound for x: int without assignment. Done means mypy reports the syntax error while preserving the stated exception.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.