pyright icon indicating copy to clipboard operation
pyright copied to clipboard

The undefined variable goes unnoticed

Open hanpari opened this issue 5 months ago • 6 comments

Environment data

  • Pylance version: 2025.6.2
  • OS and version: 6.6.87.2-microsoft-standard-WSL2 (Ubuntu)
  • Python version (& distribution if applicable, e.g. Anaconda): Python 3.12.2

Code Snippet


counter += 1

ends up with correct:

"counter" is not defined [PyLance]

while


for i in range(100):
    counter += 1

PyLance does not report anything

It seems that PyLance does not care about undefined variables in nested loops.

Expected behavior

The undefined variable in the nested loop is reported

Actual behavior

The undefined variable goes unnoticed.

hanpari avatar Jul 04 '25 18:07 hanpari

Hi @hanpari, I'm an AI Support assistant here to help with your issue. While the team reviews your request, I wanted to provide some possible tips and documentation that might help you in the meantime.

Similar Issues (FYI only, not solutions)

The following issues may or may not be related to your report. They are provided for reference only, as they may describe similar symptoms, affect the same feature, or address related concerns, but may not be the same issue.
  • Non-defined variable does not get reported when defined inside a loop: https://github.com/microsoft/pylance-release/issues/1888

The team will respond to your issue shortly. Please note this is a trial feature and may not be fully accurate. I hope these suggestions are helpful in the meantime. If this comment helped you, please give it a 👍. If the suggestion was not helpful or was incorrect, please give it a 👎. Your feedback helps us improve!

github-actions[bot] avatar Jul 04 '25 18:07 github-actions[bot]

Transferring to Pyright as I'm not sure if this is expected behavior.

We will report a reportPossiblyUnboundVariable diagnostic if you set python.analysis.typeCheckingMode to standard or strict. Or alternatively you could specifically enable that diagnostic rule via:

    "python.analysis.diagnosticSeverityOverrides": {
        "reportPossiblyUnboundVariable": "warning"
    }

The part I'm unclear about is that https://github.com/microsoft/pyright/issues/10648#issuecomment-3005141762 suggests that scenarios that would generate a NameError at runtime result in a reportUndefinedVariable diagnostic whereas scenarios that cause UnboundLocalError at runtime would result in a reportPossiblyUnboundVariable or reportUnboundVariable.

But in the scenario above and the scenarios in microsoft/pylance-release#1888, I get a NameError at runtime, so I'm wondering if they should be causing a reportUndefinedVariable diagnostic rather than reportPossiblyUnboundVariable.

debonte avatar Jul 04 '25 20:07 debonte

The reportUndefinedVariable and reportUnboundVariable (and its variant reportPossiblyUnboundVariable) are related. In this case, I would expect the reportUndefinedVariable check to report an error. This was the case prior to pyright 1.1.394. This version introduced an apparent regression as part of this bug fix.

As a temporary workaround, you can enable the reportUnboundVariable and reportPossiblyUnboundVariable checks in pylance or set the typeCheckingMode to standard as @debonte mentions above.

erictraut avatar Jul 04 '25 22:07 erictraut

The funny part is that the only way I got PyLance reported a warning for the code: "counter" is possibly unbound


for i in range(10):
    counter += 1

I need to turn on both settings like this:

    "python.analysis.diagnosticSeverityOverrides": {
        "reportPossiblyUnboundVariable": "warning",
        "reportUnboundVariable": "warning",
    },

If any of them is commented out, I got no warning at all. This is pretty weird since the first line should be enough.

And I somewhat doubt that the variable is possibly unbound. There is no other variable in this context defined, so for me the UnboundVariable should be the correct warning. Perhaps I miss some use case where is possible to get a variable into the namespace that was taken into consideration.

hanpari avatar Jul 05 '25 04:07 hanpari

As it turned out the setting python.analysis.typeCheckingMode is off by default. After turning it on, the issue disappears.

I would suggest turning it on by default.

Thanks, @debonte

hanpari avatar Jul 07 '25 18:07 hanpari

I came across a bit similar problem:

# pyright: strict

def test() -> None:
    def refine_elements() -> dict[int, str]:
        for i in range(25):
            refined_elements[i] = str(i)
            # Expected: "refined_elements" is not defined
            # Pyright: Type of "refined_elements" is "dict[int, str]"
            reveal_type(refined_elements)
        return refined_elements

    refined_elements = refine_elements()

test()

Andrej730 avatar Aug 08 '25 09:08 Andrej730