From c2773b4c6f3532accb99dd6d3041a3fdfc700811 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Mon, 1 Dec 2025 02:49:26 -0800 Subject: [PATCH] [ty] support `type[tuple[...]]` (#21652) Fixes https://github.com/astral-sh/ty/issues/1649 ## Summary We missed this when adding support for `type[]` of a specialized generic. ## Test Plan Added mdtests. --- .../resources/mdtest/subscript/tuple.md | 16 +++++++++++++--- .../src/types/infer/builder/type_expression.rs | 6 ++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/crates/ty_python_semantic/resources/mdtest/subscript/tuple.md b/crates/ty_python_semantic/resources/mdtest/subscript/tuple.md index 8de82f11d6..fd909aee41 100644 --- a/crates/ty_python_semantic/resources/mdtest/subscript/tuple.md +++ b/crates/ty_python_semantic/resources/mdtest/subscript/tuple.md @@ -329,6 +329,16 @@ reveal_type(tuple[int, str]) # revealed: reveal_type(tuple[int, ...]) # revealed: ``` +```py +from typing import Any + +def _(a: type[tuple], b: type[tuple[int]], c: type[tuple[int, ...]], d: type[tuple[Any, ...]]) -> None: + reveal_type(a) # revealed: type[tuple[Unknown, ...]] + reveal_type(b) # revealed: type[tuple[int]] + reveal_type(c) # revealed: type[tuple[int, ...]] + reveal_type(d) # revealed: type[tuple[Any, ...]] +``` + ## Inheritance ```toml @@ -392,7 +402,7 @@ class C(Tuple): ... reveal_mro(C) ``` -### Union subscript access +## Union subscript access ```py def test(val: tuple[str] | tuple[int]): @@ -402,7 +412,7 @@ def test2(val: tuple[str, None] | list[int | float]): reveal_type(val[0]) # revealed: str | int | float ``` -### Union subscript access with non-indexable type +## Union subscript access with non-indexable type ```py def test3(val: tuple[str] | tuple[int] | int): @@ -410,7 +420,7 @@ def test3(val: tuple[str] | tuple[int] | int): reveal_type(val[0]) # revealed: str | int | Unknown ``` -### Intersection subscript access +## Intersection subscript access ```py from ty_extensions import Intersection diff --git a/crates/ty_python_semantic/src/types/infer/builder/type_expression.rs b/crates/ty_python_semantic/src/types/infer/builder/type_expression.rs index 74f6607615..153f46dda3 100644 --- a/crates/ty_python_semantic/src/types/infer/builder/type_expression.rs +++ b/crates/ty_python_semantic/src/types/infer/builder/type_expression.rs @@ -691,6 +691,12 @@ impl<'db> TypeInferenceBuilder<'db, '_> { self.db(), todo_type!("type[T] for protocols").expect_dynamic(), ) + } else if class_literal.is_tuple(self.db()) { + let class_type = self + .infer_tuple_type_expression(parameters) + .map(|tuple_type| tuple_type.to_class_type(self.db())) + .unwrap_or_else(|| class_literal.default_specialization(self.db())); + SubclassOfType::from(self.db(), class_type) } else { match class_literal.generic_context(self.db()) { Some(generic_context) => {