From 606b819e9175165be7051493426bad0413de34cd Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 17 Oct 2026 01:31:34 +0100 Subject: [PATCH] Fix crash on class using itself as a type variable --- mypy/semanal.py | 5 ++++- test-data/unit/check-newsemanal.test | 17 +++++++++++++++++ test-data/unit/check-python312.test | 6 ++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index a816108f59f4..fc76516170fa 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2476,7 +2476,10 @@ def analyze_unbound_tvar_impl( assert not is_unpacked or not is_typealias_param, "Mutually exclusive conditions" sym = self.lookup_qualified(t.name, t) if sym and isinstance(sym.node, PlaceholderNode): - self.record_incomplete_ref() + if self.final_iteration: + self.cannot_resolve_name(t.name, "name", t) + else: + self.record_incomplete_ref() if not is_unpacked and sym and isinstance(sym.node, ParamSpecExpr): if sym.fullname and not self.tvar_scope.allow_binding(sym.fullname): # It's bound by our type variable scope diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test index 8d391d4725e8..e945157d94b3 100644 --- a/test-data/unit/check-newsemanal.test +++ b/test-data/unit/check-newsemanal.test @@ -3316,3 +3316,20 @@ class D: ... x: A reveal_type(x) # N: Revealed type is "builtins.str | __main__.D" + +[case testBrokenClassUsingItselfAsTypeVarNoCrash] +from typing import Generic + +class C(Generic[C]): # E: Free type variable expected in Generic[...] \ + # E: Cannot resolve name "C" (possible cyclic definition) + pass + +class D(Generic[E]): # E: Free type variable expected in Generic[...] \ + # E: Cannot resolve name "E" (possible cyclic definition) + pass + +class E(Generic[DD]): # E: Free type variable expected in Generic[...] \ + # E: Cannot resolve name "DD" (possible cyclic definition) + pass + +DD = D # E: Cannot resolve name "DD" (possible cyclic definition) diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 7252969ff2aa..7fca62308e30 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -2438,3 +2438,9 @@ def test[*Us](*xs: *Us) -> None: # TODO: we should erase last_known_values from Parameters. reveal_type(print_args(2, *xs, 4)) # N: Revealed type is "__main__.C[[Literal[2]?, *args: *tuple[*Us, Literal[4]?]], tuple[Literal[2]?, *Us, Literal[4]?]]" [builtins fixtures/paramspec.pyi] + +[case testBrokenClassUsingItselfAsTypeVarNoCrashNewSyntax] +class C[C]: + x: C +c: C[int] +reveal_type(c.x) # N: Revealed type is "builtins.int"