From 6284d1aff0db92c8347920f0451992a0cfdf98cc Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Thu, 20 Nov 2025 17:46:06 +0000 Subject: [PATCH 1/2] [ty] Add type definitions for `Type::SpecialForm`s --- crates/ty_ide/src/goto.rs | 1 + crates/ty_ide/src/goto_type_definition.rs | 128 +++++++++++ crates/ty_ide/src/inlay_hints.rs | 209 ++++++++++++++++++ crates/ty_python_semantic/src/types.rs | 3 +- crates/ty_python_semantic/src/types/class.rs | 2 +- .../src/types/definition.rs | 3 + .../src/types/special_form.rs | 73 ++++++ 7 files changed, 417 insertions(+), 2 deletions(-) diff --git a/crates/ty_ide/src/goto.rs b/crates/ty_ide/src/goto.rs index 2faacc4a86ca4..b680c9fb38cee 100644 --- a/crates/ty_ide/src/goto.rs +++ b/crates/ty_ide/src/goto.rs @@ -213,6 +213,7 @@ impl<'db> DefinitionsOrTargets<'db> { | ty_python_semantic::types::TypeDefinition::Function(definition) | ty_python_semantic::types::TypeDefinition::TypeVar(definition) | ty_python_semantic::types::TypeDefinition::TypeAlias(definition) + | ty_python_semantic::types::TypeDefinition::SpecialForm(definition) | ty_python_semantic::types::TypeDefinition::NewType(definition) => { ResolvedDefinition::Definition(definition) } diff --git a/crates/ty_ide/src/goto_type_definition.rs b/crates/ty_ide/src/goto_type_definition.rs index 53e366cd49ae6..ffdad707a0c19 100644 --- a/crates/ty_ide/src/goto_type_definition.rs +++ b/crates/ty_ide/src/goto_type_definition.rs @@ -68,6 +68,134 @@ mod tests { "); } + #[test] + fn goto_type_of_typing_dot_literal() { + let test = cursor_test( + r#" + from typing import Literal + + ab = Literal + "#, + ); + + assert_snapshot!(test.goto_type_definition(), @r" + info[goto-type-definition]: Type definition + --> stdlib/typing.pyi:351:1 + | + 349 | Final: _SpecialForm + 350 | + 351 | Literal: _SpecialForm + | ^^^^^^^ + 352 | TypedDict: _SpecialForm + | + info: Source + --> main.py:4:1 + | + 2 | from typing import Literal + 3 | + 4 | ab = Literal + | ^^ + | + "); + } + + // this is a slightly different case to the one above, + // since `Any` is a class in typeshed rather than a variable + #[test] + fn goto_type_of_typing_dot_any() { + let test = cursor_test( + r#" + from typing import Any + + ab = Any + "#, + ); + + assert_snapshot!(test.goto_type_definition(), @r#" + info[goto-type-definition]: Type definition + --> stdlib/typing.pyi:166:7 + | + 164 | # from _typeshed import AnnotationForm + 165 | + 166 | class Any: + | ^^^ + 167 | """Special type indicating an unconstrained type. + | + info: Source + --> main.py:4:1 + | + 2 | from typing import Any + 3 | + 4 | ab = Any + | ^^ + | + "#); + } + + // Similarly, `Generic` is a `type[]` type in typeshed + #[test] + fn goto_type_of_typing_dot_generic() { + let test = cursor_test( + r#" + from typing import Generic + + ab = Generic + "#, + ); + + assert_snapshot!(test.goto_type_definition(), @r" + info[goto-type-definition]: Type definition + --> stdlib/typing.pyi:770:1 + | + 768 | def __class_getitem__(cls, args: TypeVar | tuple[TypeVar, ...]) -> _Final: ... + 769 | + 770 | Generic: type[_Generic] + | ^^^^^^^ + 771 | + 772 | class _ProtocolMeta(ABCMeta): + | + info: Source + --> main.py:4:1 + | + 2 | from typing import Generic + 3 | + 4 | ab = Generic + | ^^ + | + "); + } + + #[test] + fn goto_type_of_ty_extensions_special_form() { + let test = cursor_test( + r#" + from ty_extensions import AlwaysTruthy + + ab = AlwaysTruthy + "#, + ); + + assert_snapshot!(test.goto_type_definition(), @r" + info[goto-type-definition]: Type definition + --> stdlib/ty_extensions.pyi:21:1 + | + 19 | # Types + 20 | Unknown = object() + 21 | AlwaysTruthy = object() + | ^^^^^^^^^^^^ + 22 | AlwaysFalsy = object() + | + info: Source + --> main.py:4:1 + | + 2 | from ty_extensions import AlwaysTruthy + 3 | + 4 | ab = AlwaysTruthy + | ^^ + | + "); + } + #[test] fn goto_type_of_expression_with_function_type() { let test = cursor_test( diff --git a/crates/ty_ide/src/inlay_hints.rs b/crates/ty_ide/src/inlay_hints.rs index 6a76bbba77749..3b7c351632f7b 100644 --- a/crates/ty_ide/src/inlay_hints.rs +++ b/crates/ty_ide/src/inlay_hints.rs @@ -601,6 +601,25 @@ mod tests { w[: int] = z --------------------------------------------- + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/typing.pyi:351:1 + | + 349 | Final: _SpecialForm + 350 | + 351 | Literal: _SpecialForm + | ^^^^^^^ + 352 | TypedDict: _SpecialForm + | + info: Source + --> main2.py:6:5 + | + 5 | x = 1 + 6 | y[: Literal[1]] = x + | ^^^^^^^ + 7 | z[: int] = i(1) + 8 | w[: int] = z + | + info[inlay-hint-location]: Inlay Hint Target --> stdlib/builtins.pyi:348:7 | @@ -668,6 +687,44 @@ mod tests { x4[: int], y4[: str] = (x3, y3) --------------------------------------------- + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/typing.pyi:351:1 + | + 349 | Final: _SpecialForm + 350 | + 351 | Literal: _SpecialForm + | ^^^^^^^ + 352 | TypedDict: _SpecialForm + | + info: Source + --> main2.py:8:6 + | + 7 | x1, y1 = (1, 'abc') + 8 | x2[: Literal[1]], y2[: Literal["abc"]] = (x1, y1) + | ^^^^^^^ + 9 | x3[: int], y3[: str] = (i(1), s('abc')) + 10 | x4[: int], y4[: str] = (x3, y3) + | + + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/typing.pyi:351:1 + | + 349 | Final: _SpecialForm + 350 | + 351 | Literal: _SpecialForm + | ^^^^^^^ + 352 | TypedDict: _SpecialForm + | + info: Source + --> main2.py:8:24 + | + 7 | x1, y1 = (1, 'abc') + 8 | x2[: Literal[1]], y2[: Literal["abc"]] = (x1, y1) + | ^^^^^^^ + 9 | x3[: int], y3[: str] = (i(1), s('abc')) + 10 | x4[: int], y4[: str] = (x3, y3) + | + info[inlay-hint-location]: Inlay Hint Target --> stdlib/builtins.pyi:348:7 | @@ -772,6 +829,44 @@ mod tests { x4[: int], y4[: str] = x3, y3 --------------------------------------------- + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/typing.pyi:351:1 + | + 349 | Final: _SpecialForm + 350 | + 351 | Literal: _SpecialForm + | ^^^^^^^ + 352 | TypedDict: _SpecialForm + | + info: Source + --> main2.py:8:6 + | + 7 | x1, y1 = 1, 'abc' + 8 | x2[: Literal[1]], y2[: Literal["abc"]] = x1, y1 + | ^^^^^^^ + 9 | x3[: int], y3[: str] = i(1), s('abc') + 10 | x4[: int], y4[: str] = x3, y3 + | + + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/typing.pyi:351:1 + | + 349 | Final: _SpecialForm + 350 | + 351 | Literal: _SpecialForm + | ^^^^^^^ + 352 | TypedDict: _SpecialForm + | + info: Source + --> main2.py:8:24 + | + 7 | x1, y1 = 1, 'abc' + 8 | x2[: Literal[1]], y2[: Literal["abc"]] = x1, y1 + | ^^^^^^^ + 9 | x3[: int], y3[: str] = i(1), s('abc') + 10 | x4[: int], y4[: str] = x3, y3 + | + info[inlay-hint-location]: Inlay Hint Target --> stdlib/builtins.pyi:348:7 | @@ -876,6 +971,44 @@ mod tests { w[: tuple[int, str]] = z --------------------------------------------- + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/typing.pyi:351:1 + | + 349 | Final: _SpecialForm + 350 | + 351 | Literal: _SpecialForm + | ^^^^^^^ + 352 | TypedDict: _SpecialForm + | + info: Source + --> main2.py:8:11 + | + 7 | x = (1, 'abc') + 8 | y[: tuple[Literal[1], Literal["abc"]]] = x + | ^^^^^^^ + 9 | z[: tuple[int, str]] = (i(1), s('abc')) + 10 | w[: tuple[int, str]] = z + | + + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/typing.pyi:351:1 + | + 349 | Final: _SpecialForm + 350 | + 351 | Literal: _SpecialForm + | ^^^^^^^ + 352 | TypedDict: _SpecialForm + | + info: Source + --> main2.py:8:23 + | + 7 | x = (1, 'abc') + 8 | y[: tuple[Literal[1], Literal["abc"]]] = x + | ^^^^^^^ + 9 | z[: tuple[int, str]] = (i(1), s('abc')) + 10 | w[: tuple[int, str]] = z + | + info[inlay-hint-location]: Inlay Hint Target --> stdlib/builtins.pyi:348:7 | @@ -978,6 +1111,63 @@ mod tests { x3[: int], (y3[: str], z3[: int]) = (i(1), (s('abc'), i(2))) x4[: int], (y4[: str], z4[: int]) = (x3, (y3, z3)) --------------------------------------------- + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/typing.pyi:351:1 + | + 349 | Final: _SpecialForm + 350 | + 351 | Literal: _SpecialForm + | ^^^^^^^ + 352 | TypedDict: _SpecialForm + | + info: Source + --> main2.py:8:6 + | + 7 | x1, (y1, z1) = (1, ('abc', 2)) + 8 | x2[: Literal[1]], (y2[: Literal["abc"]], z2[: Literal[2]]) = (x1, (y1, z1)) + | ^^^^^^^ + 9 | x3[: int], (y3[: str], z3[: int]) = (i(1), (s('abc'), i(2))) + 10 | x4[: int], (y4[: str], z4[: int]) = (x3, (y3, z3)) + | + + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/typing.pyi:351:1 + | + 349 | Final: _SpecialForm + 350 | + 351 | Literal: _SpecialForm + | ^^^^^^^ + 352 | TypedDict: _SpecialForm + | + info: Source + --> main2.py:8:25 + | + 7 | x1, (y1, z1) = (1, ('abc', 2)) + 8 | x2[: Literal[1]], (y2[: Literal["abc"]], z2[: Literal[2]]) = (x1, (y1, z1)) + | ^^^^^^^ + 9 | x3[: int], (y3[: str], z3[: int]) = (i(1), (s('abc'), i(2))) + 10 | x4[: int], (y4[: str], z4[: int]) = (x3, (y3, z3)) + | + + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/typing.pyi:351:1 + | + 349 | Final: _SpecialForm + 350 | + 351 | Literal: _SpecialForm + | ^^^^^^^ + 352 | TypedDict: _SpecialForm + | + info: Source + --> main2.py:8:47 + | + 7 | x1, (y1, z1) = (1, ('abc', 2)) + 8 | x2[: Literal[1]], (y2[: Literal["abc"]], z2[: Literal[2]]) = (x1, (y1, z1)) + | ^^^^^^^ + 9 | x3[: int], (y3[: str], z3[: int]) = (i(1), (s('abc'), i(2))) + 10 | x4[: int], (y4[: str], z4[: int]) = (x3, (y3, z3)) + | + info[inlay-hint-location]: Inlay Hint Target --> stdlib/builtins.pyi:348:7 | @@ -1113,6 +1303,25 @@ mod tests { z: int = i(1) w[: int] = z --------------------------------------------- + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/typing.pyi:351:1 + | + 349 | Final: _SpecialForm + 350 | + 351 | Literal: _SpecialForm + | ^^^^^^^ + 352 | TypedDict: _SpecialForm + | + info: Source + --> main2.py:6:5 + | + 5 | x: int = 1 + 6 | y[: Literal[1]] = x + | ^^^^^^^ + 7 | z: int = i(1) + 8 | w[: int] = z + | + info[inlay-hint-location]: Inlay Hint Target --> stdlib/builtins.pyi:348:7 | diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 0171b8faff31b..0d4e99526e22e 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -7604,13 +7604,14 @@ impl<'db> Type<'db> { Self::Union(_) | Self::Intersection(_) => None, + Self::SpecialForm(special_form) => special_form.definition(db), + // These types have no definition Self::Dynamic(_) | Self::Never | Self::Callable(_) | Self::AlwaysTruthy | Self::AlwaysFalsy - | Self::SpecialForm(_) | Self::TypeIs(_) => None, } } diff --git a/crates/ty_python_semantic/src/types/class.rs b/crates/ty_python_semantic/src/types/class.rs index 720bb9e91606e..b57e813e5213b 100644 --- a/crates/ty_python_semantic/src/types/class.rs +++ b/crates/ty_python_semantic/src/types/class.rs @@ -4828,7 +4828,7 @@ impl KnownClass { } /// Return the module in which we should look up the definition for this class - fn canonical_module(self, db: &dyn Db) -> KnownModule { + pub(super) fn canonical_module(self, db: &dyn Db) -> KnownModule { match self { Self::Bool | Self::Object diff --git a/crates/ty_python_semantic/src/types/definition.rs b/crates/ty_python_semantic/src/types/definition.rs index 9095dcea442fb..d0d04f52ab325 100644 --- a/crates/ty_python_semantic/src/types/definition.rs +++ b/crates/ty_python_semantic/src/types/definition.rs @@ -13,6 +13,7 @@ pub enum TypeDefinition<'db> { TypeVar(Definition<'db>), TypeAlias(Definition<'db>), NewType(Definition<'db>), + SpecialForm(Definition<'db>), } impl TypeDefinition<'_> { @@ -23,6 +24,7 @@ impl TypeDefinition<'_> { | Self::Function(definition) | Self::TypeVar(definition) | Self::TypeAlias(definition) + | Self::SpecialForm(definition) | Self::NewType(definition) => { let module = parsed_module(db, definition.file(db)).load(db); Some(definition.focus_range(db, &module)) @@ -41,6 +43,7 @@ impl TypeDefinition<'_> { | Self::Function(definition) | Self::TypeVar(definition) | Self::TypeAlias(definition) + | Self::SpecialForm(definition) | Self::NewType(definition) => { let module = parsed_module(db, definition.file(db)).load(db); Some(definition.full_range(db, &module)) diff --git a/crates/ty_python_semantic/src/types/special_form.rs b/crates/ty_python_semantic/src/types/special_form.rs index 54d9640b87caf..b9185824a2d77 100644 --- a/crates/ty_python_semantic/src/types/special_form.rs +++ b/crates/ty_python_semantic/src/types/special_form.rs @@ -4,6 +4,10 @@ use super::{ClassType, Type, class::KnownClass}; use crate::db::Db; use crate::module_resolver::{KnownModule, file_to_module}; +use crate::resolve_module; +use crate::semantic_index::place::ScopedPlaceId; +use crate::semantic_index::{FileScopeId, place_table, use_def_map}; +use crate::types::TypeDefinition; use ruff_db::files::File; use std::str::FromStr; @@ -484,6 +488,75 @@ impl SpecialFormType { SpecialFormType::NamedTuple => "typing.NamedTuple", } } + + /// Return the module(s) in which this special form could be defined + fn definition_modules(self) -> &'static [KnownModule] { + match self { + SpecialFormType::Any + | SpecialFormType::Annotated + | SpecialFormType::Literal + | SpecialFormType::LiteralString + | SpecialFormType::Optional + | SpecialFormType::Union + | SpecialFormType::NoReturn + | SpecialFormType::Never + | SpecialFormType::Tuple + | SpecialFormType::Type + | SpecialFormType::TypingSelf + | SpecialFormType::Final + | SpecialFormType::ClassVar + | SpecialFormType::Callable + | SpecialFormType::Concatenate + | SpecialFormType::Unpack + | SpecialFormType::Required + | SpecialFormType::NotRequired + | SpecialFormType::TypeAlias + | SpecialFormType::TypeGuard + | SpecialFormType::TypedDict + | SpecialFormType::TypeIs + | SpecialFormType::ReadOnly + | SpecialFormType::Protocol + | SpecialFormType::Generic + | SpecialFormType::NamedTuple + | SpecialFormType::List + | SpecialFormType::Dict + | SpecialFormType::DefaultDict + | SpecialFormType::Set + | SpecialFormType::FrozenSet + | SpecialFormType::Counter + | SpecialFormType::Deque + | SpecialFormType::ChainMap + | SpecialFormType::OrderedDict => &[KnownModule::Typing, KnownModule::TypingExtensions], + + SpecialFormType::Unknown + | SpecialFormType::AlwaysTruthy + | SpecialFormType::AlwaysFalsy + | SpecialFormType::Not + | SpecialFormType::Intersection + | SpecialFormType::TypeOf + | SpecialFormType::CallableTypeOf + | SpecialFormType::Top + | SpecialFormType::Bottom => &[KnownModule::TyExtensions], + } + } + + pub(super) fn definition(self, db: &dyn Db) -> Option> { + self.definition_modules() + .iter() + .find_map(|module| { + let file = resolve_module(db, &module.name())?.file(db)?; + let scope = FileScopeId::global().to_scope_id(db, file); + let name = self.repr().rsplit('.').next()?; + let symbol_id = place_table(db, scope).symbol_id(name)?; + + use_def_map(db, scope) + .end_of_scope_bindings(ScopedPlaceId::Symbol(symbol_id)) + .next()? + .binding + .definition() + }) + .map(TypeDefinition::SpecialForm) + } } impl std::fmt::Display for SpecialFormType { From 9c3aabb5d03b66172d915c5024c3cfb03c0755e3 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Thu, 20 Nov 2025 18:09:03 +0000 Subject: [PATCH 2/2] remove hack --- .../resources/mdtest/implicit_type_aliases.md | 6 +- crates/ty_python_semantic/src/types.rs | 4 +- .../src/types/infer/builder.rs | 6 +- .../types/infer/builder/type_expression.rs | 7 +- .../src/types/special_form.rs | 102 +++++++++--------- 5 files changed, 64 insertions(+), 61 deletions(-) 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 9095376a70860..4b28f150bccd8 100644 --- a/crates/ty_python_semantic/resources/mdtest/implicit_type_aliases.md +++ b/crates/ty_python_semantic/resources/mdtest/implicit_type_aliases.md @@ -1109,7 +1109,7 @@ from typing import List, Dict # error: [invalid-type-form] "Int literals are not allowed in this context in a type expression" InvalidList = List[1] -# error: [invalid-type-form] "`typing.typing.List` requires exactly one argument" +# error: [invalid-type-form] "`typing.List` requires exactly one argument" ListTooManyArgs = List[int, str] # error: [invalid-type-form] "Int literals are not allowed in this context in a type expression" @@ -1118,10 +1118,10 @@ InvalidDict1 = Dict[1, str] # error: [invalid-type-form] "Int literals are not allowed in this context in a type expression" InvalidDict2 = Dict[str, 2] -# error: [invalid-type-form] "`typing.typing.Dict` requires exactly two arguments, got 1" +# error: [invalid-type-form] "`typing.Dict` requires exactly two arguments, got 1" DictTooFewArgs = Dict[str] -# error: [invalid-type-form] "`typing.typing.Dict` requires exactly two arguments, got 3" +# error: [invalid-type-form] "`typing.Dict` requires exactly two arguments, got 3" DictTooManyArgs = Dict[str, int, float] def _( diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 0d4e99526e22e..7090f16df020d 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -7503,7 +7503,7 @@ impl<'db> Type<'db> { name = enum_literal.name(db) ), ), - Type::SpecialForm(special_form) => Type::string_literal(db, special_form.repr()), + Type::SpecialForm(special_form) => Type::string_literal(db, &special_form.to_string()), Type::KnownInstance(known_instance) => Type::StringLiteral(StringLiteralType::new( db, known_instance.repr(db).to_compact_string(), @@ -7525,7 +7525,7 @@ impl<'db> Type<'db> { Type::string_literal(db, &format!("'{}'", literal.value(db).escape_default())) } Type::LiteralString => Type::LiteralString, - Type::SpecialForm(special_form) => Type::string_literal(db, special_form.repr()), + Type::SpecialForm(special_form) => Type::string_literal(db, &special_form.to_string()), Type::KnownInstance(known_instance) => Type::StringLiteral(StringLiteralType::new( db, known_instance.repr(db).to_compact_string(), diff --git a/crates/ty_python_semantic/src/types/infer/builder.rs b/crates/ty_python_semantic/src/types/infer/builder.rs index dbfdac5209b69..bccd10f83f248 100644 --- a/crates/ty_python_semantic/src/types/infer/builder.rs +++ b/crates/ty_python_semantic/src/types/infer/builder.rs @@ -10872,7 +10872,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) { builder.into_diagnostic(format_args!( "`typing.{}` requires exactly one argument", - special_form.repr() + special_form.name() )); } Type::unknown() @@ -10907,7 +10907,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { { builder.into_diagnostic(format_args!( "`typing.{}` requires exactly two arguments, got {}", - special_form.repr(), + special_form.name(), arguments.len() )); } @@ -10931,7 +10931,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) { builder.into_diagnostic(format_args!( "`typing.{}` requires exactly two arguments, got 1", - special_form.repr() + special_form.name() )); } 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 75e4e1f94a59e..f044f9c087b80 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 @@ -1318,10 +1318,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> { self.infer_type_expression(arguments_slice); if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) { - let diag = builder.into_diagnostic(format_args!( - "Special form `{}` expected exactly one type parameter", - special_form.repr() - )); + let diag = builder.into_diagnostic( + "Special form `typing.TypeIs` expected exactly one type parameter", + ); diagnostic::add_type_expression_reference_link(diag); } diff --git a/crates/ty_python_semantic/src/types/special_form.rs b/crates/ty_python_semantic/src/types/special_form.rs index b9185824a2d77..423e1196bd6d7 100644 --- a/crates/ty_python_semantic/src/types/special_form.rs +++ b/crates/ty_python_semantic/src/types/special_form.rs @@ -439,53 +439,53 @@ impl SpecialFormType { } } - /// Return the repr of the symbol at runtime - pub(super) const fn repr(self) -> &'static str { + /// Return the name of the symbol at runtime + pub(super) const fn name(self) -> &'static str { match self { - SpecialFormType::Any => "typing.Any", - SpecialFormType::Annotated => "typing.Annotated", - SpecialFormType::Literal => "typing.Literal", - SpecialFormType::LiteralString => "typing.LiteralString", - SpecialFormType::Optional => "typing.Optional", - SpecialFormType::Union => "typing.Union", - SpecialFormType::NoReturn => "typing.NoReturn", - SpecialFormType::Never => "typing.Never", - SpecialFormType::Tuple => "typing.Tuple", - SpecialFormType::Type => "typing.Type", - SpecialFormType::TypingSelf => "typing.Self", - SpecialFormType::Final => "typing.Final", - SpecialFormType::ClassVar => "typing.ClassVar", - SpecialFormType::Callable => "typing.Callable", - SpecialFormType::Concatenate => "typing.Concatenate", - SpecialFormType::Unpack => "typing.Unpack", - SpecialFormType::Required => "typing.Required", - SpecialFormType::NotRequired => "typing.NotRequired", - SpecialFormType::TypeAlias => "typing.TypeAlias", - SpecialFormType::TypeGuard => "typing.TypeGuard", - SpecialFormType::TypedDict => "typing.TypedDict", - SpecialFormType::TypeIs => "typing.TypeIs", - SpecialFormType::List => "typing.List", - SpecialFormType::Dict => "typing.Dict", - SpecialFormType::DefaultDict => "typing.DefaultDict", - SpecialFormType::Set => "typing.Set", - SpecialFormType::FrozenSet => "typing.FrozenSet", - SpecialFormType::Counter => "typing.Counter", - SpecialFormType::Deque => "typing.Deque", - SpecialFormType::ChainMap => "typing.ChainMap", - SpecialFormType::OrderedDict => "typing.OrderedDict", - SpecialFormType::ReadOnly => "typing.ReadOnly", - SpecialFormType::Unknown => "ty_extensions.Unknown", - SpecialFormType::AlwaysTruthy => "ty_extensions.AlwaysTruthy", - SpecialFormType::AlwaysFalsy => "ty_extensions.AlwaysFalsy", - SpecialFormType::Not => "ty_extensions.Not", - SpecialFormType::Intersection => "ty_extensions.Intersection", - SpecialFormType::TypeOf => "ty_extensions.TypeOf", - SpecialFormType::CallableTypeOf => "ty_extensions.CallableTypeOf", - SpecialFormType::Top => "ty_extensions.Top", - SpecialFormType::Bottom => "ty_extensions.Bottom", - SpecialFormType::Protocol => "typing.Protocol", - SpecialFormType::Generic => "typing.Generic", - SpecialFormType::NamedTuple => "typing.NamedTuple", + SpecialFormType::Any => "Any", + SpecialFormType::Annotated => "Annotated", + SpecialFormType::Literal => "Literal", + SpecialFormType::LiteralString => "LiteralString", + SpecialFormType::Optional => "Optional", + SpecialFormType::Union => "Union", + SpecialFormType::NoReturn => "NoReturn", + SpecialFormType::Never => "Never", + SpecialFormType::Tuple => "Tuple", + SpecialFormType::Type => "Type", + SpecialFormType::TypingSelf => "Self", + SpecialFormType::Final => "Final", + SpecialFormType::ClassVar => "ClassVar", + SpecialFormType::Callable => "Callable", + SpecialFormType::Concatenate => "Concatenate", + SpecialFormType::Unpack => "Unpack", + SpecialFormType::Required => "Required", + SpecialFormType::NotRequired => "NotRequired", + SpecialFormType::TypeAlias => "TypeAlias", + SpecialFormType::TypeGuard => "TypeGuard", + SpecialFormType::TypedDict => "TypedDict", + SpecialFormType::TypeIs => "TypeIs", + SpecialFormType::List => "List", + SpecialFormType::Dict => "Dict", + SpecialFormType::DefaultDict => "DefaultDict", + SpecialFormType::Set => "Set", + SpecialFormType::FrozenSet => "FrozenSet", + SpecialFormType::Counter => "Counter", + SpecialFormType::Deque => "Deque", + SpecialFormType::ChainMap => "ChainMap", + SpecialFormType::OrderedDict => "OrderedDict", + SpecialFormType::ReadOnly => "ReadOnly", + SpecialFormType::Unknown => "Unknown", + SpecialFormType::AlwaysTruthy => "AlwaysTruthy", + SpecialFormType::AlwaysFalsy => "AlwaysFalsy", + SpecialFormType::Not => "Not", + SpecialFormType::Intersection => "Intersection", + SpecialFormType::TypeOf => "TypeOf", + SpecialFormType::CallableTypeOf => "CallableTypeOf", + SpecialFormType::Top => "Top", + SpecialFormType::Bottom => "Bottom", + SpecialFormType::Protocol => "Protocol", + SpecialFormType::Generic => "Generic", + SpecialFormType::NamedTuple => "NamedTuple", } } @@ -546,8 +546,7 @@ impl SpecialFormType { .find_map(|module| { let file = resolve_module(db, &module.name())?.file(db)?; let scope = FileScopeId::global().to_scope_id(db, file); - let name = self.repr().rsplit('.').next()?; - let symbol_id = place_table(db, scope).symbol_id(name)?; + let symbol_id = place_table(db, scope).symbol_id(self.name())?; use_def_map(db, scope) .end_of_scope_bindings(ScopedPlaceId::Symbol(symbol_id)) @@ -561,6 +560,11 @@ impl SpecialFormType { impl std::fmt::Display for SpecialFormType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(self.repr()) + write!( + f, + "{}.{}", + self.definition_modules()[0].as_str(), + self.name() + ) } }