[ty] Further improve details around which expressions should be deferred in stub files (#21456)

## Summary

- Always restore the previous `deferred_state` after parsing a type
expression: we don't want that state leaking out into other contexts
where we shouldn't be deferring expression inference
- Always defer the right-hand-side of a PEP-613 type alias in a stub
file, allowing for forward references on the right-hand side of `T:
TypeAlias = X | Y` in a stub file

Addresses @carljm's review in
https://github.com/astral-sh/ruff/pull/21401#discussion_r2524260153

## Test Plan

I added a regression test for a regression that the first version of
this PR introduced (we need to make sure the r.h.s. of a PEP-613
`TypeAlias`es is always deferred in a stub file)
This commit is contained in:
Alex Waygood
2025-11-14 21:07:02 +00:00
committed by GitHub
parent 2a2b719f00
commit 3e7e91724c
3 changed files with 58 additions and 7 deletions

View File

@@ -1,6 +1,8 @@
# PEP 613 type aliases
We do not support PEP 613 type aliases yet. For now, just make sure that we don't panic:
## No panics
We do not fully support PEP 613 type aliases yet. For now, just make sure that we don't panic:
```py
from typing import TypeAlias
@@ -15,3 +17,36 @@ RecursiveHomogeneousTuple: TypeAlias = tuple[int | "RecursiveHomogeneousTuple",
def _(rec: RecursiveHomogeneousTuple):
reveal_type(rec) # revealed: tuple[Divergent, ...]
```
## PEP-613 aliases in stubs are deferred
Although the right-hand side of a PEP-613 alias is a value expression, inference of this value is
deferred in a stub file, allowing for forward references:
`stub.pyi`:
```pyi
from typing import TypeAlias
MyAlias: TypeAlias = A | B
class A: ...
class B: ...
```
`module.py`:
```py
import stub
def f(x: stub.MyAlias): ...
f(stub.A())
f(stub.B())
class Unrelated: ...
# TODO: we should emit `[invalid-argument-type]` here
# (the alias is a `@Todo` because it's imported from another file)
f(Unrelated())
```