[ty] Add type definitions for `Type::SpecialForm`s (#21544)

This commit is contained in:
Alex Waygood 2025-11-20 18:14:30 +00:00 committed by GitHub
parent 6e84f4fd7a
commit c4767f5aa8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 479 additions and 61 deletions

View File

@ -213,6 +213,7 @@ impl<'db> DefinitionsOrTargets<'db> {
| ty_python_semantic::types::TypeDefinition::Function(definition) | ty_python_semantic::types::TypeDefinition::Function(definition)
| ty_python_semantic::types::TypeDefinition::TypeVar(definition) | ty_python_semantic::types::TypeDefinition::TypeVar(definition)
| ty_python_semantic::types::TypeDefinition::TypeAlias(definition) | ty_python_semantic::types::TypeDefinition::TypeAlias(definition)
| ty_python_semantic::types::TypeDefinition::SpecialForm(definition)
| ty_python_semantic::types::TypeDefinition::NewType(definition) => { | ty_python_semantic::types::TypeDefinition::NewType(definition) => {
ResolvedDefinition::Definition(definition) ResolvedDefinition::Definition(definition)
} }

View File

@ -68,6 +68,134 @@ mod tests {
"); ");
} }
#[test]
fn goto_type_of_typing_dot_literal() {
let test = cursor_test(
r#"
from typing import Literal
a<CURSOR>b = 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
a<CURSOR>b = 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
a<CURSOR>b = 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
a<CURSOR>b = 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] #[test]
fn goto_type_of_expression_with_function_type() { fn goto_type_of_expression_with_function_type() {
let test = cursor_test( let test = cursor_test(

View File

@ -601,6 +601,25 @@ mod tests {
w[: int] = z 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 info[inlay-hint-location]: Inlay Hint Target
--> stdlib/builtins.pyi:348:7 --> stdlib/builtins.pyi:348:7
| |
@ -668,6 +687,44 @@ mod tests {
x4[: int], y4[: str] = (x3, y3) 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 info[inlay-hint-location]: Inlay Hint Target
--> stdlib/builtins.pyi:348:7 --> stdlib/builtins.pyi:348:7
| |
@ -772,6 +829,44 @@ mod tests {
x4[: int], y4[: str] = x3, y3 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 info[inlay-hint-location]: Inlay Hint Target
--> stdlib/builtins.pyi:348:7 --> stdlib/builtins.pyi:348:7
| |
@ -876,6 +971,44 @@ mod tests {
w[: tuple[int, str]] = z 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 info[inlay-hint-location]: Inlay Hint Target
--> stdlib/builtins.pyi:348:7 --> stdlib/builtins.pyi:348:7
| |
@ -978,6 +1111,63 @@ mod tests {
x3[: int], (y3[: str], z3[: int]) = (i(1), (s('abc'), i(2))) x3[: int], (y3[: str], z3[: int]) = (i(1), (s('abc'), i(2)))
x4[: int], (y4[: str], z4[: int]) = (x3, (y3, z3)) 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 info[inlay-hint-location]: Inlay Hint Target
--> stdlib/builtins.pyi:348:7 --> stdlib/builtins.pyi:348:7
| |
@ -1113,6 +1303,25 @@ mod tests {
z: int = i(1) z: int = i(1)
w[: int] = z 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 info[inlay-hint-location]: Inlay Hint Target
--> stdlib/builtins.pyi:348:7 --> stdlib/builtins.pyi:348:7
| |

View File

@ -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" # error: [invalid-type-form] "Int literals are not allowed in this context in a type expression"
InvalidList = List[1] 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] ListTooManyArgs = List[int, str]
# error: [invalid-type-form] "Int literals are not allowed in this context in a type expression" # 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" # error: [invalid-type-form] "Int literals are not allowed in this context in a type expression"
InvalidDict2 = Dict[str, 2] 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] 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] DictTooManyArgs = Dict[str, int, float]
def _( def _(

View File

@ -7503,7 +7503,7 @@ impl<'db> Type<'db> {
name = enum_literal.name(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( Type::KnownInstance(known_instance) => Type::StringLiteral(StringLiteralType::new(
db, db,
known_instance.repr(db).to_compact_string(), 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::string_literal(db, &format!("'{}'", literal.value(db).escape_default()))
} }
Type::LiteralString => Type::LiteralString, 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( Type::KnownInstance(known_instance) => Type::StringLiteral(StringLiteralType::new(
db, db,
known_instance.repr(db).to_compact_string(), known_instance.repr(db).to_compact_string(),
@ -7604,13 +7604,14 @@ impl<'db> Type<'db> {
Self::Union(_) | Self::Intersection(_) => None, Self::Union(_) | Self::Intersection(_) => None,
Self::SpecialForm(special_form) => special_form.definition(db),
// These types have no definition // These types have no definition
Self::Dynamic(_) Self::Dynamic(_)
| Self::Never | Self::Never
| Self::Callable(_) | Self::Callable(_)
| Self::AlwaysTruthy | Self::AlwaysTruthy
| Self::AlwaysFalsy | Self::AlwaysFalsy
| Self::SpecialForm(_)
| Self::TypeIs(_) => None, | Self::TypeIs(_) => None,
} }
} }

View File

@ -4828,7 +4828,7 @@ impl KnownClass {
} }
/// Return the module in which we should look up the definition for this class /// 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 { match self {
Self::Bool Self::Bool
| Self::Object | Self::Object

View File

@ -13,6 +13,7 @@ pub enum TypeDefinition<'db> {
TypeVar(Definition<'db>), TypeVar(Definition<'db>),
TypeAlias(Definition<'db>), TypeAlias(Definition<'db>),
NewType(Definition<'db>), NewType(Definition<'db>),
SpecialForm(Definition<'db>),
} }
impl TypeDefinition<'_> { impl TypeDefinition<'_> {
@ -23,6 +24,7 @@ impl TypeDefinition<'_> {
| Self::Function(definition) | Self::Function(definition)
| Self::TypeVar(definition) | Self::TypeVar(definition)
| Self::TypeAlias(definition) | Self::TypeAlias(definition)
| Self::SpecialForm(definition)
| Self::NewType(definition) => { | Self::NewType(definition) => {
let module = parsed_module(db, definition.file(db)).load(db); let module = parsed_module(db, definition.file(db)).load(db);
Some(definition.focus_range(db, &module)) Some(definition.focus_range(db, &module))
@ -41,6 +43,7 @@ impl TypeDefinition<'_> {
| Self::Function(definition) | Self::Function(definition)
| Self::TypeVar(definition) | Self::TypeVar(definition)
| Self::TypeAlias(definition) | Self::TypeAlias(definition)
| Self::SpecialForm(definition)
| Self::NewType(definition) => { | Self::NewType(definition) => {
let module = parsed_module(db, definition.file(db)).load(db); let module = parsed_module(db, definition.file(db)).load(db);
Some(definition.full_range(db, &module)) Some(definition.full_range(db, &module))

View File

@ -10872,7 +10872,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) { if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) {
builder.into_diagnostic(format_args!( builder.into_diagnostic(format_args!(
"`typing.{}` requires exactly one argument", "`typing.{}` requires exactly one argument",
special_form.repr() special_form.name()
)); ));
} }
Type::unknown() Type::unknown()
@ -10907,7 +10907,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
{ {
builder.into_diagnostic(format_args!( builder.into_diagnostic(format_args!(
"`typing.{}` requires exactly two arguments, got {}", "`typing.{}` requires exactly two arguments, got {}",
special_form.repr(), special_form.name(),
arguments.len() arguments.len()
)); ));
} }
@ -10931,7 +10931,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) { if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) {
builder.into_diagnostic(format_args!( builder.into_diagnostic(format_args!(
"`typing.{}` requires exactly two arguments, got 1", "`typing.{}` requires exactly two arguments, got 1",
special_form.repr() special_form.name()
)); ));
} }

View File

@ -1318,10 +1318,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
self.infer_type_expression(arguments_slice); self.infer_type_expression(arguments_slice);
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) { if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) {
let diag = builder.into_diagnostic(format_args!( let diag = builder.into_diagnostic(
"Special form `{}` expected exactly one type parameter", "Special form `typing.TypeIs` expected exactly one type parameter",
special_form.repr() );
));
diagnostic::add_type_expression_reference_link(diag); diagnostic::add_type_expression_reference_link(diag);
} }

View File

@ -4,6 +4,10 @@
use super::{ClassType, Type, class::KnownClass}; use super::{ClassType, Type, class::KnownClass};
use crate::db::Db; use crate::db::Db;
use crate::module_resolver::{KnownModule, file_to_module}; 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 ruff_db::files::File;
use std::str::FromStr; use std::str::FromStr;
@ -435,59 +439,132 @@ impl SpecialFormType {
} }
} }
/// Return the repr of the symbol at runtime /// Return the name of the symbol at runtime
pub(super) const fn repr(self) -> &'static str { pub(super) const fn name(self) -> &'static str {
match self { match self {
SpecialFormType::Any => "typing.Any", SpecialFormType::Any => "Any",
SpecialFormType::Annotated => "typing.Annotated", SpecialFormType::Annotated => "Annotated",
SpecialFormType::Literal => "typing.Literal", SpecialFormType::Literal => "Literal",
SpecialFormType::LiteralString => "typing.LiteralString", SpecialFormType::LiteralString => "LiteralString",
SpecialFormType::Optional => "typing.Optional", SpecialFormType::Optional => "Optional",
SpecialFormType::Union => "typing.Union", SpecialFormType::Union => "Union",
SpecialFormType::NoReturn => "typing.NoReturn", SpecialFormType::NoReturn => "NoReturn",
SpecialFormType::Never => "typing.Never", SpecialFormType::Never => "Never",
SpecialFormType::Tuple => "typing.Tuple", SpecialFormType::Tuple => "Tuple",
SpecialFormType::Type => "typing.Type", SpecialFormType::Type => "Type",
SpecialFormType::TypingSelf => "typing.Self", SpecialFormType::TypingSelf => "Self",
SpecialFormType::Final => "typing.Final", SpecialFormType::Final => "Final",
SpecialFormType::ClassVar => "typing.ClassVar", SpecialFormType::ClassVar => "ClassVar",
SpecialFormType::Callable => "typing.Callable", SpecialFormType::Callable => "Callable",
SpecialFormType::Concatenate => "typing.Concatenate", SpecialFormType::Concatenate => "Concatenate",
SpecialFormType::Unpack => "typing.Unpack", SpecialFormType::Unpack => "Unpack",
SpecialFormType::Required => "typing.Required", SpecialFormType::Required => "Required",
SpecialFormType::NotRequired => "typing.NotRequired", SpecialFormType::NotRequired => "NotRequired",
SpecialFormType::TypeAlias => "typing.TypeAlias", SpecialFormType::TypeAlias => "TypeAlias",
SpecialFormType::TypeGuard => "typing.TypeGuard", SpecialFormType::TypeGuard => "TypeGuard",
SpecialFormType::TypedDict => "typing.TypedDict", SpecialFormType::TypedDict => "TypedDict",
SpecialFormType::TypeIs => "typing.TypeIs", SpecialFormType::TypeIs => "TypeIs",
SpecialFormType::List => "typing.List", SpecialFormType::List => "List",
SpecialFormType::Dict => "typing.Dict", SpecialFormType::Dict => "Dict",
SpecialFormType::DefaultDict => "typing.DefaultDict", SpecialFormType::DefaultDict => "DefaultDict",
SpecialFormType::Set => "typing.Set", SpecialFormType::Set => "Set",
SpecialFormType::FrozenSet => "typing.FrozenSet", SpecialFormType::FrozenSet => "FrozenSet",
SpecialFormType::Counter => "typing.Counter", SpecialFormType::Counter => "Counter",
SpecialFormType::Deque => "typing.Deque", SpecialFormType::Deque => "Deque",
SpecialFormType::ChainMap => "typing.ChainMap", SpecialFormType::ChainMap => "ChainMap",
SpecialFormType::OrderedDict => "typing.OrderedDict", SpecialFormType::OrderedDict => "OrderedDict",
SpecialFormType::ReadOnly => "typing.ReadOnly", SpecialFormType::ReadOnly => "ReadOnly",
SpecialFormType::Unknown => "ty_extensions.Unknown", SpecialFormType::Unknown => "Unknown",
SpecialFormType::AlwaysTruthy => "ty_extensions.AlwaysTruthy", SpecialFormType::AlwaysTruthy => "AlwaysTruthy",
SpecialFormType::AlwaysFalsy => "ty_extensions.AlwaysFalsy", SpecialFormType::AlwaysFalsy => "AlwaysFalsy",
SpecialFormType::Not => "ty_extensions.Not", SpecialFormType::Not => "Not",
SpecialFormType::Intersection => "ty_extensions.Intersection", SpecialFormType::Intersection => "Intersection",
SpecialFormType::TypeOf => "ty_extensions.TypeOf", SpecialFormType::TypeOf => "TypeOf",
SpecialFormType::CallableTypeOf => "ty_extensions.CallableTypeOf", SpecialFormType::CallableTypeOf => "CallableTypeOf",
SpecialFormType::Top => "ty_extensions.Top", SpecialFormType::Top => "Top",
SpecialFormType::Bottom => "ty_extensions.Bottom", SpecialFormType::Bottom => "Bottom",
SpecialFormType::Protocol => "typing.Protocol", SpecialFormType::Protocol => "Protocol",
SpecialFormType::Generic => "typing.Generic", SpecialFormType::Generic => "Generic",
SpecialFormType::NamedTuple => "typing.NamedTuple", SpecialFormType::NamedTuple => "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<TypeDefinition<'_>> {
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 symbol_id = place_table(db, scope).symbol_id(self.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 { impl std::fmt::Display for SpecialFormType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 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()
)
} }
} }