mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-17 11:22:55 +00:00
## Summary
cf. https://github.com/astral-sh/ruff/pull/20962
In the following code, `foo` in the comprehension was not reported as
unresolved:
```python
# error: [unresolved-reference] "Name `foo` used when not defined"
foo
foo = [
# no error!
# revealed: Divergent
reveal_type(x) for _ in () for x in [foo]
]
baz = [
# error: [unresolved-reference] "Name `baz` used when not defined"
# revealed: Unknown
reveal_type(x) for _ in () for x in [baz]
]
```
In fact, this is a more serious bug than it looks: for `foo`,
[`explicit_global_symbol` is
called](6cc3393ccd/crates/ty_python_semantic/src/types/infer/builder.rs (L8052)),
causing a symbol that should actually be `Undefined` to be reported as
being of type `Divergent`.
This PR fixes this bug. As a result, the code in
`mdtest/regression/pr_20962_comprehension_panics.md` no longer panics.
## Test Plan
`corpus\cyclic_symbol_in_comprehension.py` is added.
New tests are added in `mdtest/comprehensions/basic.md`.
---------
Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Carl Meyer <carl@astral.sh>
4.2 KiB
4.2 KiB
Deferred annotations
Deferred annotations in stubs always resolve
mod.pyi:
def get_foo() -> Foo: ...
class Foo: ...
from mod import get_foo
reveal_type(get_foo()) # revealed: Foo
Deferred annotations in regular code fail
In (regular) source files, annotations are not deferred. This also tests that imports from
__future__ that are not annotations are ignored.
from __future__ import with_statement as annotations
# error: [unresolved-reference]
def get_foo() -> Foo: ...
class Foo: ...
reveal_type(get_foo()) # revealed: Unknown
Deferred annotations in regular code with __future__.annotations
If __future__.annotations is imported, annotations are deferred.
from __future__ import annotations
def get_foo() -> Foo:
return Foo()
class Foo: ...
reveal_type(get_foo()) # revealed: Foo
Deferred self-reference annotations in a class definition
[environment]
python-version = "3.12"
from __future__ import annotations
class Foo:
this: Foo
# error: [unresolved-reference]
_ = Foo()
# error: [unresolved-reference]
[Foo for _ in range(1)]
a = int
def f(self, x: Foo):
reveal_type(x) # revealed: Foo
def g(self) -> Foo:
_: Foo = self
return self
class Bar:
foo: Foo
b = int
def f(self, x: Foo):
return self
# error: [unresolved-reference]
def g(self) -> Bar:
return self
# error: [unresolved-reference]
def h[T: Bar](self):
pass
class Baz[T: Foo]:
pass
# error: [unresolved-reference] "Name `Foo` used when not defined"
# error: [unresolved-reference] "Name `Bar` used when not defined"
class Qux(Foo, Bar, Baz):
pass
# error: [unresolved-reference] "Name `Foo` used when not defined"
# error: [unresolved-reference] "Name `Bar` used when not defined"
class Quux[_T](Foo, Bar, Baz):
pass
# error: [unresolved-reference]
type S = a
type T = b
type U = Foo
# error: [unresolved-reference]
type V = Bar
type W = Baz
def h[T: Bar]():
# error: [unresolved-reference]
return Bar()
type Baz = Foo
Non-deferred self-reference annotations in a class definition
[environment]
python-version = "3.12"
class Foo:
# error: [unresolved-reference]
this: Foo
ok: "Foo"
# error: [unresolved-reference]
_ = Foo()
# error: [unresolved-reference]
[Foo for _ in range(1)]
a = int
# error: [unresolved-reference]
def f(self, x: Foo):
reveal_type(x) # revealed: Unknown
# error: [unresolved-reference]
def g(self) -> Foo:
_: Foo = self
return self
class Bar:
# error: [unresolved-reference]
foo: Foo
b = int
# error: [unresolved-reference]
def f(self, x: Foo):
return self
# error: [unresolved-reference]
def g(self) -> Bar:
return self
# error: [unresolved-reference]
def h[T: Bar](self):
pass
class Baz[T: Foo]:
pass
# error: [unresolved-reference] "Name `Foo` used when not defined"
# error: [unresolved-reference] "Name `Bar` used when not defined"
class Qux(Foo, Bar, Baz):
pass
# error: [unresolved-reference] "Name `Foo` used when not defined"
# error: [unresolved-reference] "Name `Bar` used when not defined"
class Quux[_T](Foo, Bar, Baz):
pass
# error: [unresolved-reference]
type S = a
type T = b
type U = Foo
# error: [unresolved-reference]
type V = Bar
type W = Baz
def h[T: Bar]():
# error: [unresolved-reference]
return Bar()
type Qux = Foo
def _():
class C:
# error: [unresolved-reference]
def f(self) -> C:
return self
Base class references
Not deferred by future.annotations
from __future__ import annotations
class A(B): # error: [unresolved-reference]
pass
class B:
pass
Deferred in stub files
class A(B): ...
class B: ...