diff --git a/mypy/expandtype.py b/mypy/expandtype.py index fd507216a6be..af436c3b465f 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -392,9 +392,11 @@ def expand_unpack(self, t: UnpackType) -> list[Type]: or isinstance(repl, TypeVarTupleType) ): return [UnpackType(typ=repl)] - elif isinstance(repl, (AnyType, UninhabitedType)): + elif isinstance(repl, (AnyType, UninhabitedType, UnionType)): # Replace *Ts = Any with *Ts = *tuple[Any, ...] and same for Never. # These types may appear here as a result of user error or failed inference. + # Unions may be inferred from e.g. conditional expressions used as type + # arguments (GH#22108) — carry them over instead of crashing. return [UnpackType(t.type.tuple_fallback.copy_modified(args=[repl]))] else: raise RuntimeError(f"Invalid type replacement to expand: {repl}") diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index b287e82b3d4a..dc667787c583 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -297,6 +297,15 @@ def test_expand_naked_type_var(self) -> None: self.assert_expand(self.fx.t, [(self.fx.t.id, self.fx.a)], self.fx.a) self.assert_expand(self.fx.t, [(self.fx.s.id, self.fx.a)], self.fx.t) + def test_expand_unpack_union_replacement(self) -> None: + # GH#22108: expanding *Ts with a union replacement (e.g. inferred from a + # conditional expression) must not crash expand_unpack — the union is + # carried over into the expansion, same as the Any/Never replacements. + union_repl = UnionType([self.tuple(UnpackType(self.tuple(self.fx.anyt))), self.fx.anyt]) + orig = self.tuple(UnpackType(self.fx.ts)) + exp = mypy.expandtype.expand_type(orig, {self.fx.ts.id: union_repl}) + self.assertIn("Any", str(exp)) + def test_expand_basic_generic_types(self) -> None: self.assert_expand(self.fx.gt, [(self.fx.t.id, self.fx.a)], self.fx.ga)