From bb464ed924ffaf66c2489813e961b6c003ffb555 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Sat, 13 Dec 2025 20:23:16 +0000 Subject: [PATCH] [ty] Use unqualified names for displays of `TypeAliasType`s and unbound `ParamSpec`s/`TypeVar`s (#21960) --- crates/ty_ide/src/hover.rs | 8 +- crates/ty_ide/src/inlay_hints.rs | 143 ++++++++++++++---- .../resources/mdtest/call/methods.md | 2 +- .../resources/mdtest/class/super.md | 2 +- .../mdtest/generics/legacy/paramspec.md | 2 +- .../mdtest/generics/legacy/variables.md | 8 +- .../mdtest/generics/pep695/paramspec.md | 10 +- .../mdtest/generics/pep695/variables.md | 8 +- .../resources/mdtest/implicit_type_aliases.md | 2 +- .../resources/mdtest/pep695_type_aliases.md | 4 +- .../regression/paramspec_on_python39.md | 2 +- .../ty_python_semantic/src/types/display.rs | 6 +- .../src/types/infer/tests.rs | 12 +- 13 files changed, 149 insertions(+), 60 deletions(-) diff --git a/crates/ty_ide/src/hover.rs b/crates/ty_ide/src/hover.rs index 7389b4c1cc..2960b9da83 100644 --- a/crates/ty_ide/src/hover.rs +++ b/crates/ty_ide/src/hover.rs @@ -3057,10 +3057,10 @@ def function(): ); assert_snapshot!(test.hover(), @r" - typing.TypeVar + TypeVar --------------------------------------------- ```python - typing.TypeVar + TypeVar ``` --------------------------------------------- info[hover]: Hovered content is @@ -3120,10 +3120,10 @@ def function(): ); assert_snapshot!(test.hover(), @r" - typing.TypeVar + TypeVar --------------------------------------------- ```python - typing.TypeVar + TypeVar ``` --------------------------------------------- info[hover]: Hovered content is diff --git a/crates/ty_ide/src/inlay_hints.rs b/crates/ty_ide/src/inlay_hints.rs index 4f9858e61e..a958b4451d 100644 --- a/crates/ty_ide/src/inlay_hints.rs +++ b/crates/ty_ide/src/inlay_hints.rs @@ -6635,26 +6635,9 @@ mod tests { assert_snapshot!(test.inlay_hints(), @r" from typing import Protocol, TypeVar - T[: typing.TypeVar] = TypeVar([name=]'T') + T = TypeVar([name=]'T') Strange[: ] = Protocol[T] --------------------------------------------- - info[inlay-hint-location]: Inlay Hint Target - --> main.py:3:1 - | - 2 | from typing import Protocol, TypeVar - 3 | T = TypeVar('T') - | ^ - 4 | Strange = Protocol[T] - | - info: Source - --> main2.py:3:5 - | - 2 | from typing import Protocol, TypeVar - 3 | T[: typing.TypeVar] = TypeVar([name=]'T') - | ^^^^^^^^^^^^^^ - 4 | Strange[: ] = Protocol[T] - | - info[inlay-hint-location]: Inlay Hint Target --> stdlib/typing.pyi:276:13 | @@ -6666,11 +6649,11 @@ mod tests { 278 | bound: Any | None = None, # AnnotationForm | info: Source - --> main2.py:3:32 + --> main2.py:3:14 | 2 | from typing import Protocol, TypeVar - 3 | T[: typing.TypeVar] = TypeVar([name=]'T') - | ^^^^ + 3 | T = TypeVar([name=]'T') + | ^^^^ 4 | Strange[: ] = Protocol[T] | @@ -6687,7 +6670,7 @@ mod tests { --> main2.py:4:26 | 2 | from typing import Protocol, TypeVar - 3 | T[: typing.TypeVar] = TypeVar([name=]'T') + 3 | T = TypeVar([name=]'T') 4 | Strange[: ] = Protocol[T] | ^^^^^^^^^^^^^^^ | @@ -6704,18 +6687,124 @@ mod tests { --> main2.py:4:42 | 2 | from typing import Protocol, TypeVar - 3 | T[: typing.TypeVar] = TypeVar([name=]'T') + 3 | T = TypeVar([name=]'T') 4 | Strange[: ] = Protocol[T] | ^ | + "); + } + #[test] + fn test_paramspec_creation_inlay_hint() { + let mut test = inlay_hint_test( + " + from typing import ParamSpec + P = ParamSpec('P')", + ); + + assert_snapshot!(test.inlay_hints(), @r" + from typing import ParamSpec + P = ParamSpec([name=]'P') --------------------------------------------- - info[inlay-hint-edit]: File after edits + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/typing.pyi:552:17 + | + 550 | def __new__( + 551 | cls, + 552 | name: str, + | ^^^^ + 553 | *, + 554 | bound: Any | None = None, # AnnotationForm + | info: Source + --> main2.py:3:16 + | + 2 | from typing import ParamSpec + 3 | P = ParamSpec([name=]'P') + | ^^^^ + | + "); + } - from typing import Protocol, TypeVar - T: typing.TypeVar = TypeVar('T') - Strange = Protocol[T] + #[test] + fn test_typealiastype_creation_inlay_hint() { + let mut test = inlay_hint_test( + " + from typing_extensions import TypeAliasType + A = TypeAliasType('A', str)", + ); + + assert_snapshot!(test.inlay_hints(), @r#" + from typing_extensions import TypeAliasType + A = TypeAliasType([name=]'A', [value=]str) + --------------------------------------------- + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/typing.pyi:2032:26 + | + 2030 | """ + 2031 | + 2032 | def __new__(cls, name: str, value: Any, *, type_params: tuple[_TypeParameter, ...] = ()) -> Self: ... + | ^^^^ + 2033 | @property + 2034 | def __value__(self) -> Any: ... # AnnotationForm + | + info: Source + --> main2.py:3:20 + | + 2 | from typing_extensions import TypeAliasType + 3 | A = TypeAliasType([name=]'A', [value=]str) + | ^^^^ + | + + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/typing.pyi:2032:37 + | + 2030 | """ + 2031 | + 2032 | def __new__(cls, name: str, value: Any, *, type_params: tuple[_TypeParameter, ...] = ()) -> Self: ... + | ^^^^^ + 2033 | @property + 2034 | def __value__(self) -> Any: ... # AnnotationForm + | + info: Source + --> main2.py:3:32 + | + 2 | from typing_extensions import TypeAliasType + 3 | A = TypeAliasType([name=]'A', [value=]str) + | ^^^^^ + | + "#); + } + + #[test] + fn test_typevartuple_creation_inlay_hint() { + let mut test = inlay_hint_test( + " + from typing_extensions import TypeVarTuple + Ts = TypeVarTuple('Ts')", + ); + + assert_snapshot!(test.inlay_hints(), @r" + from typing_extensions import TypeVarTuple + Ts = TypeVarTuple([name=]'Ts') + --------------------------------------------- + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/typing.pyi:412:30 + | + 410 | def has_default(self) -> bool: ... + 411 | if sys.version_info >= (3, 13): + 412 | def __new__(cls, name: str, *, default: Any = ...) -> Self: ... # AnnotationForm + | ^^^^ + 413 | elif sys.version_info >= (3, 12): + 414 | def __new__(cls, name: str) -> Self: ... + | + info: Source + --> main2.py:3:20 + | + 2 | from typing_extensions import TypeVarTuple + 3 | Ts = TypeVarTuple([name=]'Ts') + | ^^^^ + | "); } diff --git a/crates/ty_python_semantic/resources/mdtest/call/methods.md b/crates/ty_python_semantic/resources/mdtest/call/methods.md index a2c7d043b4..eb02c134f3 100644 --- a/crates/ty_python_semantic/resources/mdtest/call/methods.md +++ b/crates/ty_python_semantic/resources/mdtest/call/methods.md @@ -201,7 +201,7 @@ python-version = "3.12" ```py type IntOrStr = int | str -reveal_type(IntOrStr.__or__) # revealed: bound method typing.TypeAliasType.__or__(right: Any, /) -> _SpecialForm +reveal_type(IntOrStr.__or__) # revealed: bound method TypeAliasType.__or__(right: Any, /) -> _SpecialForm ``` ## Method calls on types not disjoint from `None` diff --git a/crates/ty_python_semantic/resources/mdtest/class/super.md b/crates/ty_python_semantic/resources/mdtest/class/super.md index 4c3d09560f..ab96e93ca5 100644 --- a/crates/ty_python_semantic/resources/mdtest/class/super.md +++ b/crates/ty_python_semantic/resources/mdtest/class/super.md @@ -567,7 +567,7 @@ def f(x: int): super(x, x) type IntAlias = int - # error: [invalid-super-argument] "`typing.TypeAliasType` is not a valid class" + # error: [invalid-super-argument] "`TypeAliasType` is not a valid class" super(IntAlias, 0) # error: [invalid-super-argument] "`str` is not an instance or subclass of `` in `super(, str)` call" diff --git a/crates/ty_python_semantic/resources/mdtest/generics/legacy/paramspec.md b/crates/ty_python_semantic/resources/mdtest/generics/legacy/paramspec.md index ec3d608506..7a365b6405 100644 --- a/crates/ty_python_semantic/resources/mdtest/generics/legacy/paramspec.md +++ b/crates/ty_python_semantic/resources/mdtest/generics/legacy/paramspec.md @@ -9,7 +9,7 @@ from typing import ParamSpec P = ParamSpec("P") reveal_type(type(P)) # revealed: -reveal_type(P) # revealed: typing.ParamSpec +reveal_type(P) # revealed: ParamSpec reveal_type(P.__name__) # revealed: Literal["P"] ``` diff --git a/crates/ty_python_semantic/resources/mdtest/generics/legacy/variables.md b/crates/ty_python_semantic/resources/mdtest/generics/legacy/variables.md index 13547e81be..b419b61e71 100644 --- a/crates/ty_python_semantic/resources/mdtest/generics/legacy/variables.md +++ b/crates/ty_python_semantic/resources/mdtest/generics/legacy/variables.md @@ -22,7 +22,7 @@ from typing import TypeVar T = TypeVar("T") reveal_type(type(T)) # revealed: -reveal_type(T) # revealed: typing.TypeVar +reveal_type(T) # revealed: TypeVar reveal_type(T.__name__) # revealed: Literal["T"] ``` @@ -146,7 +146,7 @@ from typing import TypeVar T = TypeVar("T", default=int) reveal_type(type(T)) # revealed: -reveal_type(T) # revealed: typing.TypeVar +reveal_type(T) # revealed: TypeVar reveal_type(T.__default__) # revealed: int reveal_type(T.__bound__) # revealed: None reveal_type(T.__constraints__) # revealed: tuple[()] @@ -187,7 +187,7 @@ from typing import TypeVar T = TypeVar("T", bound=int) reveal_type(type(T)) # revealed: -reveal_type(T) # revealed: typing.TypeVar +reveal_type(T) # revealed: TypeVar reveal_type(T.__bound__) # revealed: int reveal_type(T.__constraints__) # revealed: tuple[()] @@ -211,7 +211,7 @@ from typing import TypeVar T = TypeVar("T", int, str) reveal_type(type(T)) # revealed: -reveal_type(T) # revealed: typing.TypeVar +reveal_type(T) # revealed: TypeVar reveal_type(T.__constraints__) # revealed: tuple[int, str] S = TypeVar("S") diff --git a/crates/ty_python_semantic/resources/mdtest/generics/pep695/paramspec.md b/crates/ty_python_semantic/resources/mdtest/generics/pep695/paramspec.md index 92e1e64116..354521288e 100644 --- a/crates/ty_python_semantic/resources/mdtest/generics/pep695/paramspec.md +++ b/crates/ty_python_semantic/resources/mdtest/generics/pep695/paramspec.md @@ -12,7 +12,7 @@ python-version = "3.13" ```py def foo1[**P]() -> None: - reveal_type(P) # revealed: typing.ParamSpec + reveal_type(P) # revealed: ParamSpec ``` ## Bounds and constraints @@ -45,14 +45,14 @@ The default value for a `ParamSpec` can be either a list of types, `...`, or ano ```py def foo2[**P = ...]() -> None: - reveal_type(P) # revealed: typing.ParamSpec + reveal_type(P) # revealed: ParamSpec def foo3[**P = [int, str]]() -> None: - reveal_type(P) # revealed: typing.ParamSpec + reveal_type(P) # revealed: ParamSpec def foo4[**P, **Q = P](): - reveal_type(P) # revealed: typing.ParamSpec - reveal_type(Q) # revealed: typing.ParamSpec + reveal_type(P) # revealed: ParamSpec + reveal_type(Q) # revealed: ParamSpec ``` Other values are invalid. diff --git a/crates/ty_python_semantic/resources/mdtest/generics/pep695/variables.md b/crates/ty_python_semantic/resources/mdtest/generics/pep695/variables.md index 8338227538..d70c130649 100644 --- a/crates/ty_python_semantic/resources/mdtest/generics/pep695/variables.md +++ b/crates/ty_python_semantic/resources/mdtest/generics/pep695/variables.md @@ -17,7 +17,7 @@ instances of `typing.TypeVar`, just like legacy type variables. ```py def f[T](): reveal_type(type(T)) # revealed: - reveal_type(T) # revealed: typing.TypeVar + reveal_type(T) # revealed: TypeVar reveal_type(T.__name__) # revealed: Literal["T"] ``` @@ -33,7 +33,7 @@ python-version = "3.13" ```py def f[T = int](): reveal_type(type(T)) # revealed: - reveal_type(T) # revealed: typing.TypeVar + reveal_type(T) # revealed: TypeVar reveal_type(T.__default__) # revealed: int reveal_type(T.__bound__) # revealed: None reveal_type(T.__constraints__) # revealed: tuple[()] @@ -66,7 +66,7 @@ class Invalid[S = T]: ... ```py def f[T: int](): reveal_type(type(T)) # revealed: - reveal_type(T) # revealed: typing.TypeVar + reveal_type(T) # revealed: TypeVar reveal_type(T.__bound__) # revealed: int reveal_type(T.__constraints__) # revealed: tuple[()] @@ -79,7 +79,7 @@ def g[S](): ```py def f[T: (int, str)](): reveal_type(type(T)) # revealed: - reveal_type(T) # revealed: typing.TypeVar + reveal_type(T) # revealed: TypeVar reveal_type(T.__constraints__) # revealed: tuple[int, str] reveal_type(T.__bound__) # revealed: None diff --git a/crates/ty_python_semantic/resources/mdtest/implicit_type_aliases.md b/crates/ty_python_semantic/resources/mdtest/implicit_type_aliases.md index 977991611c..ef6f07283f 100644 --- a/crates/ty_python_semantic/resources/mdtest/implicit_type_aliases.md +++ b/crates/ty_python_semantic/resources/mdtest/implicit_type_aliases.md @@ -400,7 +400,7 @@ reveal_type(ListOrTuple) # revealed: T@MyCallable'> reveal_type(AnnotatedType) # revealed: ]'> -reveal_type(TransparentAlias) # revealed: typing.TypeVar +reveal_type(TransparentAlias) # revealed: TypeVar reveal_type(MyOptional) # revealed: def _( diff --git a/crates/ty_python_semantic/resources/mdtest/pep695_type_aliases.md b/crates/ty_python_semantic/resources/mdtest/pep695_type_aliases.md index 799d1fc36f..aadae3f907 100644 --- a/crates/ty_python_semantic/resources/mdtest/pep695_type_aliases.md +++ b/crates/ty_python_semantic/resources/mdtest/pep695_type_aliases.md @@ -12,7 +12,7 @@ python-version = "3.12" ```py type IntOrStr = int | str -reveal_type(IntOrStr) # revealed: typing.TypeAliasType +reveal_type(IntOrStr) # revealed: TypeAliasType reveal_type(IntOrStr.__name__) # revealed: Literal["IntOrStr"] x: IntOrStr = 1 @@ -205,7 +205,7 @@ from typing_extensions import TypeAliasType, Union IntOrStr = TypeAliasType("IntOrStr", Union[int, str]) -reveal_type(IntOrStr) # revealed: typing.TypeAliasType +reveal_type(IntOrStr) # revealed: TypeAliasType reveal_type(IntOrStr.__name__) # revealed: Literal["IntOrStr"] diff --git a/crates/ty_python_semantic/resources/mdtest/regression/paramspec_on_python39.md b/crates/ty_python_semantic/resources/mdtest/regression/paramspec_on_python39.md index 1760669cf0..f0dd802adb 100644 --- a/crates/ty_python_semantic/resources/mdtest/regression/paramspec_on_python39.md +++ b/crates/ty_python_semantic/resources/mdtest/regression/paramspec_on_python39.md @@ -13,7 +13,7 @@ diagnostic message for `invalid-exception-caught` expects to construct `typing.P def foo[**P]() -> None: try: pass - # error: [invalid-exception-caught] "Invalid object caught in an exception handler: Object has type `typing.ParamSpec`" + # error: [invalid-exception-caught] "Invalid object caught in an exception handler: Object has type `ParamSpec`" except P: pass ``` diff --git a/crates/ty_python_semantic/src/types/display.rs b/crates/ty_python_semantic/src/types/display.rs index f6bee388f5..790fcc404f 100644 --- a/crates/ty_python_semantic/src/types/display.rs +++ b/crates/ty_python_semantic/src/types/display.rs @@ -2358,7 +2358,7 @@ impl<'db> FmtDetailed<'db> for DisplayKnownInstanceRepr<'db> { .fmt_detailed(f)?; f.write_str("'>") } else { - f.with_type(ty).write_str("typing.TypeAliasType") + f.with_type(ty).write_str("TypeAliasType") } } // This is a legacy `TypeVar` _outside_ of any generic class or function, so we render @@ -2366,9 +2366,9 @@ impl<'db> FmtDetailed<'db> for DisplayKnownInstanceRepr<'db> { // have a `Type::TypeVar(_)`, which is rendered as the typevar's name. KnownInstanceType::TypeVar(typevar_instance) => { if typevar_instance.kind(self.db).is_paramspec() { - f.with_type(ty).write_str("typing.ParamSpec") + f.with_type(ty).write_str("ParamSpec") } else { - f.with_type(ty).write_str("typing.TypeVar") + f.with_type(ty).write_str("TypeVar") } } KnownInstanceType::Deprecated(_) => f.write_str("warnings.deprecated"), diff --git a/crates/ty_python_semantic/src/types/infer/tests.rs b/crates/ty_python_semantic/src/types/infer/tests.rs index 9f6ae996be..90c79fd252 100644 --- a/crates/ty_python_semantic/src/types/infer/tests.rs +++ b/crates/ty_python_semantic/src/types/infer/tests.rs @@ -222,14 +222,14 @@ fn pep695_type_params() { ); }; - check_typevar("T", "typing.TypeVar", None, None, None); - check_typevar("U", "typing.TypeVar", Some("A"), None, None); - check_typevar("V", "typing.TypeVar", None, Some(&["A", "B"]), None); - check_typevar("W", "typing.TypeVar", None, None, Some("A")); - check_typevar("X", "typing.TypeVar", Some("A"), None, Some("A1")); + check_typevar("T", "TypeVar", None, None, None); + check_typevar("U", "TypeVar", Some("A"), None, None); + check_typevar("V", "TypeVar", None, Some(&["A", "B"]), None); + check_typevar("W", "TypeVar", None, None, Some("A")); + check_typevar("X", "TypeVar", Some("A"), None, Some("A1")); // a typevar with less than two constraints is treated as unconstrained - check_typevar("Y", "typing.TypeVar", None, None, None); + check_typevar("Y", "TypeVar", None, None, None); } /// Test that a symbol known to be unbound in a scope does not still trigger cycle-causing