From a04823cfadbf8cb6dbf64b1432c8568db4a8e020 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 25 Aug 2025 15:27:55 +0100 Subject: [PATCH] [ty] Completely ignore typeshed's stub for `Any` (#20079) --- .../resources/mdtest/annotations/any.md | 43 +++++++++++++- .../type_properties/is_assignable_to.md | 10 ++-- crates/ty_python_semantic/src/types.rs | 2 +- crates/ty_python_semantic/src/types/class.rs | 25 ++------ .../src/types/class_base.rs | 9 +-- .../ty_python_semantic/src/types/display.rs | 3 - .../ty_python_semantic/src/types/function.rs | 59 +++++++++++++------ crates/ty_python_semantic/src/types/infer.rs | 33 +++++------ .../ty_python_semantic/src/types/instance.rs | 20 +++---- crates/ty_python_semantic/src/types/narrow.rs | 10 +--- .../src/types/special_form.rs | 6 +- .../src/types/subclass_of.rs | 19 ++---- 12 files changed, 134 insertions(+), 105 deletions(-) diff --git a/crates/ty_python_semantic/resources/mdtest/annotations/any.md b/crates/ty_python_semantic/resources/mdtest/annotations/any.md index d4b1e6f502..e5244051f0 100644 --- a/crates/ty_python_semantic/resources/mdtest/annotations/any.md +++ b/crates/ty_python_semantic/resources/mdtest/annotations/any.md @@ -137,6 +137,22 @@ from unittest.mock import MagicMock x: int = MagicMock() ``` +## Runtime properties + +`typing.Any` is a class at runtime on Python 3.11+, and `typing_extensions.Any` is always a class. +On earlier versions of Python, `typing.Any` was an instance of `typing._SpecialForm`, but this is +not currently modeled by ty. We currently infer `Any` has having all attributes a class would have +on all versions of Python: + +```py +from typing import Any +from ty_extensions import TypeOf, static_assert, is_assignable_to + +reveal_type(Any.__base__) # revealed: type | None +reveal_type(Any.__bases__) # revealed: tuple[type, ...] +static_assert(is_assignable_to(TypeOf[Any], type)) +``` + ## Invalid `Any` cannot be parameterized: @@ -144,7 +160,32 @@ x: int = MagicMock() ```py from typing import Any -# error: [invalid-type-form] "Type `typing.Any` expected no type parameter" +# error: [invalid-type-form] "Special form `typing.Any` expected no type parameter" def f(x: Any[int]): reveal_type(x) # revealed: Unknown ``` + +`Any` cannot be called (this leads to a `TypeError` at runtime): + +```py +Any() # error: [call-non-callable] "Object of type `typing.Any` is not callable" +``` + +`Any` also cannot be used as a metaclass (under the hood, this leads to an implicit call to `Any`): + +```py +class F(metaclass=Any): ... # error: [invalid-metaclass] "Metaclass type `typing.Any` is not callable" +``` + +And `Any` cannot be used in `isinstance()` checks: + +```py +# error: [invalid-argument-type] "`typing.Any` cannot be used with `isinstance()`: This call will raise `TypeError` at runtime" +isinstance("", Any) +``` + +But `issubclass()` checks are fine: + +```py +issubclass(object, Any) # no error! +``` diff --git a/crates/ty_python_semantic/resources/mdtest/type_properties/is_assignable_to.md b/crates/ty_python_semantic/resources/mdtest/type_properties/is_assignable_to.md index 5338b25ce8..869625c678 100644 --- a/crates/ty_python_semantic/resources/mdtest/type_properties/is_assignable_to.md +++ b/crates/ty_python_semantic/resources/mdtest/type_properties/is_assignable_to.md @@ -221,11 +221,11 @@ static_assert(is_assignable_to(type[Unknown], Meta)) static_assert(is_assignable_to(Meta, type[Any])) static_assert(is_assignable_to(Meta, type[Unknown])) -class AnyMeta(metaclass=Any): ... - -static_assert(is_assignable_to(type[AnyMeta], type)) -static_assert(is_assignable_to(type[AnyMeta], type[object])) -static_assert(is_assignable_to(type[AnyMeta], type[Any])) +def _(x: Any): + class AnyMeta(metaclass=x): ... + static_assert(is_assignable_to(type[AnyMeta], type)) + static_assert(is_assignable_to(type[AnyMeta], type[object])) + static_assert(is_assignable_to(type[AnyMeta], type[Any])) from typing import TypeVar, Generic, Any diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index f8fbda0850..d29130ebad 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -5528,7 +5528,6 @@ impl<'db> Type<'db> { // https://typing.python.org/en/latest/spec/special-types.html#special-cases-for-float-and-complex Type::ClassLiteral(class) => { let ty = match class.known(db) { - Some(KnownClass::Any) => Type::any(), Some(KnownClass::Complex) => UnionType::from_elements( db, [ @@ -5622,6 +5621,7 @@ impl<'db> Type<'db> { Type::SpecialForm(special_form) => match special_form { SpecialFormType::Never | SpecialFormType::NoReturn => Ok(Type::Never), SpecialFormType::LiteralString => Ok(Type::LiteralString), + SpecialFormType::Any => Ok(Type::any()), SpecialFormType::Unknown => Ok(Type::unknown()), SpecialFormType::AlwaysTruthy => Ok(Type::AlwaysTruthy), SpecialFormType::AlwaysFalsy => Ok(Type::AlwaysFalsy), diff --git a/crates/ty_python_semantic/src/types/class.rs b/crates/ty_python_semantic/src/types/class.rs index 58fe067963..2baeffd80e 100644 --- a/crates/ty_python_semantic/src/types/class.rs +++ b/crates/ty_python_semantic/src/types/class.rs @@ -3485,7 +3485,6 @@ pub enum KnownClass { // Typeshed NoneType, // Part of `types` for Python >= 3.10 // Typing - Any, Awaitable, Generator, Deprecated, @@ -3568,8 +3567,7 @@ impl KnownClass { Self::NoneType => Some(Truthiness::AlwaysFalse), - Self::Any - | Self::BaseException + Self::BaseException | Self::Exception | Self::ExceptionGroup | Self::Object @@ -3689,7 +3687,6 @@ impl KnownClass { // Anything with a *runtime* MRO (N.B. sometimes different from the MRO that typeshed gives!) // with length >2, or anything that is implemented in pure Python, is not a solid base. Self::ABCMeta - | Self::Any | Self::Awaitable | Self::Generator | Self::Enum @@ -3762,7 +3759,6 @@ impl KnownClass { | KnownClass::AsyncGeneratorType | KnownClass::CoroutineType | KnownClass::NoneType - | KnownClass::Any | KnownClass::StdlibAlias | KnownClass::SpecialForm | KnownClass::TypeVar @@ -3839,7 +3835,6 @@ impl KnownClass { | KnownClass::AsyncGeneratorType | KnownClass::CoroutineType | KnownClass::NoneType - | KnownClass::Any | KnownClass::StdlibAlias | KnownClass::SpecialForm | KnownClass::TypeVar @@ -3916,7 +3911,6 @@ impl KnownClass { | KnownClass::AsyncGeneratorType | KnownClass::CoroutineType | KnownClass::NoneType - | KnownClass::Any | KnownClass::StdlibAlias | KnownClass::SpecialForm | KnownClass::TypeVar @@ -3967,8 +3961,7 @@ impl KnownClass { | Self::NamedTupleLike | Self::Generator => true, - Self::Any - | Self::Bool + Self::Bool | Self::Object | Self::Bytes | Self::Bytearray @@ -4037,7 +4030,6 @@ impl KnownClass { pub(crate) fn name(self, db: &dyn Db) -> &'static str { match self { - Self::Any => "Any", Self::Bool => "bool", Self::Object => "object", Self::Bytes => "bytes", @@ -4347,8 +4339,7 @@ impl KnownClass { | Self::UnionType | Self::WrapperDescriptorType => KnownModule::Types, Self::NoneType => KnownModule::Typeshed, - Self::Any - | Self::Awaitable + Self::Awaitable | Self::Generator | Self::SpecialForm | Self::TypeVar @@ -4409,8 +4400,7 @@ impl KnownClass { | Self::UnionType | Self::NotImplementedType => Some(true), - Self::Any - | Self::Bool + Self::Bool | Self::Object | Self::Bytes | Self::Bytearray @@ -4489,8 +4479,7 @@ impl KnownClass { | Self::TypeAliasType | Self::NotImplementedType => true, - Self::Any - | Self::Bool + Self::Bool | Self::Object | Self::Bytes | Self::Bytearray @@ -4565,7 +4554,6 @@ impl KnownClass { // We assert that this match is exhaustive over the right-hand side in the unit test // `known_class_roundtrip_from_str()` let candidate = match class_name { - "Any" => Self::Any, "bool" => Self::Bool, "object" => Self::Object, "bytes" => Self::Bytes, @@ -4655,8 +4643,7 @@ impl KnownClass { /// Return `true` if the module of `self` matches `module` fn check_module(self, db: &dyn Db, module: KnownModule) -> bool { match self { - Self::Any - | Self::Bool + Self::Bool | Self::Object | Self::Bytes | Self::Bytearray diff --git a/crates/ty_python_semantic/src/types/class_base.rs b/crates/ty_python_semantic/src/types/class_base.rs index 08eda64f09..85cadc0b5d 100644 --- a/crates/ty_python_semantic/src/types/class_base.rs +++ b/crates/ty_python_semantic/src/types/class_base.rs @@ -77,13 +77,7 @@ impl<'db> ClassBase<'db> { ) -> Option { match ty { Type::Dynamic(dynamic) => Some(Self::Dynamic(dynamic)), - Type::ClassLiteral(literal) => { - if literal.is_known(db, KnownClass::Any) { - Some(Self::Dynamic(DynamicType::Any)) - } else { - Some(Self::Class(literal.default_specialization(db))) - } - } + Type::ClassLiteral(literal) => Some(Self::Class(literal.default_specialization(db))), Type::GenericAlias(generic) => Some(Self::Class(ClassType::Generic(generic))), Type::NominalInstance(instance) if instance.class(db).is_known(db, KnownClass::GenericAlias) => @@ -201,6 +195,7 @@ impl<'db> ClassBase<'db> { | SpecialFormType::AlwaysTruthy | SpecialFormType::AlwaysFalsy => None, + SpecialFormType::Any => Some(Self::Dynamic(DynamicType::Any)), SpecialFormType::Unknown => Some(Self::unknown()), SpecialFormType::Protocol => Some(Self::Protocol), diff --git a/crates/ty_python_semantic/src/types/display.rs b/crates/ty_python_semantic/src/types/display.rs index b8ec4bd61a..534cdf8e82 100644 --- a/crates/ty_python_semantic/src/types/display.rs +++ b/crates/ty_python_semantic/src/types/display.rs @@ -76,9 +76,6 @@ impl Display for DisplayType<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let representation = self.ty.representation(self.db, self.settings); match self.ty { - Type::ClassLiteral(literal) if literal.is_known(self.db, KnownClass::Any) => { - write!(f, "typing.Any") - } Type::IntLiteral(_) | Type::BooleanLiteral(_) | Type::StringLiteral(_) diff --git a/crates/ty_python_semantic/src/types/function.rs b/crates/ty_python_semantic/src/types/function.rs index 8f71854fd8..691068f896 100644 --- a/crates/ty_python_semantic/src/types/function.rs +++ b/crates/ty_python_semantic/src/types/function.rs @@ -68,7 +68,7 @@ use crate::types::call::{Binding, CallArguments}; use crate::types::constraints::Constraints; use crate::types::context::InferContext; use crate::types::diagnostic::{ - REDUNDANT_CAST, STATIC_ASSERT_ERROR, TYPE_ASSERTION_FAILURE, + INVALID_ARGUMENT_TYPE, REDUNDANT_CAST, STATIC_ASSERT_ERROR, TYPE_ASSERTION_FAILURE, report_bad_argument_to_get_protocol_members, report_bad_argument_to_protocol_interface, report_runtime_check_against_non_runtime_checkable_protocol, }; @@ -79,8 +79,8 @@ use crate::types::visitor::any_over_type; use crate::types::{ BoundMethodType, BoundTypeVarInstance, CallableType, ClassBase, ClassLiteral, ClassType, DeprecatedInstance, DynamicType, HasRelationToVisitor, IsEquivalentVisitor, KnownClass, - NormalizedVisitor, Truthiness, Type, TypeMapping, TypeRelation, UnionBuilder, all_members, - walk_type_mapping, + NormalizedVisitor, SpecialFormType, Truthiness, Type, TypeMapping, TypeRelation, UnionBuilder, + all_members, walk_type_mapping, }; use crate::{Db, FxOrderSet, ModuleName, resolve_module}; @@ -1454,25 +1454,48 @@ impl KnownFunction { } KnownFunction::IsInstance | KnownFunction::IsSubclass => { - let [Some(first_arg), Some(Type::ClassLiteral(class))] = parameter_types else { + let [Some(first_arg), Some(second_argument)] = parameter_types else { return; }; - if let Some(protocol_class) = class.into_protocol_class(db) { - if !protocol_class.is_runtime_checkable(db) { - report_runtime_check_against_non_runtime_checkable_protocol( - context, - call_expression, - protocol_class, - self, - ); - } - } + match second_argument { + Type::ClassLiteral(class) => { + if let Some(protocol_class) = class.into_protocol_class(db) { + if !protocol_class.is_runtime_checkable(db) { + report_runtime_check_against_non_runtime_checkable_protocol( + context, + call_expression, + protocol_class, + self, + ); + } + } - if self == KnownFunction::IsInstance { - overload.set_return_type( - is_instance_truthiness(db, *first_arg, *class).into_type(db), - ); + if self == KnownFunction::IsInstance { + overload.set_return_type( + is_instance_truthiness(db, *first_arg, *class).into_type(db), + ); + } + } + // The special-casing here is necessary because we recognise the symbol `typing.Any` as an + // instance of `type` at runtime. Even once we understand typeshed's annotation for + // `isinstance()`, we'd continue to accept calls such as `isinstance(x, typing.Any)` without + // emitting a diagnostic if we didn't have this branch. + Type::SpecialForm(SpecialFormType::Any) + if self == KnownFunction::IsInstance => + { + let Some(builder) = + context.report_lint(&INVALID_ARGUMENT_TYPE, call_expression) + else { + return; + }; + let mut diagnostic = builder.into_diagnostic(format_args!( + "`typing.Any` cannot be used with `isinstance()`" + )); + diagnostic + .set_primary_message("This call will raise `TypeError` at runtime"); + } + _ => {} } } diff --git a/crates/ty_python_semantic/src/types/infer.rs b/crates/ty_python_semantic/src/types/infer.rs index 43c1abed21..b7fe22465f 100644 --- a/crates/ty_python_semantic/src/types/infer.rs +++ b/crates/ty_python_semantic/src/types/infer.rs @@ -3080,15 +3080,19 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { let maybe_known_class = KnownClass::try_from_file_and_name(self.db(), self.file(), name); - let ty = if maybe_known_class.is_none() - && &name.id == "NamedTuple" - && matches!( + let in_typing_module = || { + matches!( file_to_module(self.db(), self.file()).and_then(|module| module.known(self.db())), Some(KnownModule::Typing | KnownModule::TypingExtensions) - ) { - Type::SpecialForm(SpecialFormType::NamedTuple) - } else { - Type::from(ClassLiteral::new( + ) + }; + + let ty = match (maybe_known_class, &*name.id) { + (None, "NamedTuple") if in_typing_module() => { + Type::SpecialForm(SpecialFormType::NamedTuple) + } + (None, "Any") if in_typing_module() => Type::SpecialForm(SpecialFormType::Any), + _ => Type::from(ClassLiteral::new( self.db(), name.id.clone(), body_scope, @@ -3096,7 +3100,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { deprecated, dataclass_params, dataclass_transformer_params, - )) + )), }; self.add_declaration_with_binding( @@ -10242,9 +10246,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> { let name_ty = self.infer_expression(slice); match name_ty { Type::ClassLiteral(class_literal) => { - if class_literal.is_known(self.db(), KnownClass::Any) { - SubclassOfType::subclass_of_any() - } else if class_literal.is_protocol(self.db()) { + if class_literal.is_protocol(self.db()) { SubclassOfType::from( self.db(), todo_type!("type[T] for protocols").expect_dynamic(), @@ -10256,6 +10258,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> { ) } } + Type::SpecialForm(SpecialFormType::Any) => SubclassOfType::subclass_of_any(), Type::SpecialForm(SpecialFormType::Unknown) => { SubclassOfType::subclass_of_unknown() } @@ -10339,13 +10342,6 @@ impl<'db> TypeInferenceBuilder<'db, '_> { self.infer_expression(&subscript.slice); Type::unknown() } - Type::ClassLiteral(literal) if literal.is_known(self.db(), KnownClass::Any) => { - self.infer_expression(slice); - if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) { - builder.into_diagnostic("Type `typing.Any` expected no type parameter"); - } - Type::unknown() - } Type::SpecialForm(special_form) => { self.infer_parameterized_special_form_type_expression(subscript, special_form) } @@ -10919,6 +10915,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> { | SpecialFormType::TypeAlias | SpecialFormType::TypedDict | SpecialFormType::Unknown + | SpecialFormType::Any | SpecialFormType::NamedTuple => { self.infer_type_expression(arguments_slice); diff --git a/crates/ty_python_semantic/src/types/instance.rs b/crates/ty_python_semantic/src/types/instance.rs index 8c3cdbb786..1a403e11ea 100644 --- a/crates/ty_python_semantic/src/types/instance.rs +++ b/crates/ty_python_semantic/src/types/instance.rs @@ -12,7 +12,7 @@ use crate::types::enums::is_single_member_enum; use crate::types::protocol_class::walk_protocol_interface; use crate::types::tuple::{TupleSpec, TupleType}; use crate::types::{ - ApplyTypeMappingVisitor, ClassBase, DynamicType, HasRelationToVisitor, IsDisjointVisitor, + ApplyTypeMappingVisitor, ClassBase, HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, NormalizedVisitor, TypeMapping, TypeRelation, VarianceInferable, }; use crate::{Db, FxOrderSet}; @@ -23,20 +23,20 @@ impl<'db> Type<'db> { pub(crate) fn instance(db: &'db dyn Db, class: ClassType<'db>) -> Self { let (class_literal, specialization) = class.class_literal(db); - match class_literal.known(db) { - Some(KnownClass::Any) => Type::Dynamic(DynamicType::Any), - Some(KnownClass::Tuple) => Type::tuple(TupleType::new( + if class_literal.is_known(db, KnownClass::Tuple) { + Type::tuple(TupleType::new( db, specialization .and_then(|spec| Some(Cow::Borrowed(spec.tuple(db)?))) .unwrap_or_else(|| Cow::Owned(TupleSpec::homogeneous(Type::unknown()))) .as_ref(), - )), - _ if class_literal.is_protocol(db) => { - Self::ProtocolInstance(ProtocolInstanceType::from_class(class)) - } - _ if class_literal.is_typed_dict(db) => Type::typed_dict(class), - _ => Type::non_tuple_instance(class), + )) + } else if class_literal.is_protocol(db) { + Self::ProtocolInstance(ProtocolInstanceType::from_class(class)) + } else if class_literal.is_typed_dict(db) { + Type::typed_dict(class) + } else { + Type::non_tuple_instance(class) } } diff --git a/crates/ty_python_semantic/src/types/narrow.rs b/crates/ty_python_semantic/src/types/narrow.rs index d1df76dee6..46a0b5a8f5 100644 --- a/crates/ty_python_semantic/src/types/narrow.rs +++ b/crates/ty_python_semantic/src/types/narrow.rs @@ -183,15 +183,7 @@ impl ClassInfoConstraintFunction { match classinfo { Type::TypeAlias(alias) => self.generate_constraint(db, alias.value_type(db)), - Type::ClassLiteral(class_literal) => { - // At runtime (on Python 3.11+), this will return `True` for classes that actually - // do inherit `typing.Any` and `False` otherwise. We could accurately model that? - if class_literal.is_known(db, KnownClass::Any) { - None - } else { - Some(constraint_fn(class_literal)) - } - } + Type::ClassLiteral(class_literal) => Some(constraint_fn(class_literal)), Type::SubclassOf(subclass_of_ty) => match subclass_of_ty.subclass_of() { SubclassOfInner::Class(ClassType::NonGeneric(class)) => Some(constraint_fn(class)), // It's not valid to use a generic alias as the second argument to `isinstance()` or `issubclass()`, diff --git a/crates/ty_python_semantic/src/types/special_form.rs b/crates/ty_python_semantic/src/types/special_form.rs index 494e7331ce..de4dc4d3f1 100644 --- a/crates/ty_python_semantic/src/types/special_form.rs +++ b/crates/ty_python_semantic/src/types/special_form.rs @@ -27,6 +27,7 @@ use std::str::FromStr; get_size2::GetSize, )] pub enum SpecialFormType { + Any, /// The symbol `typing.Annotated` (which can also be found as `typing_extensions.Annotated`) Annotated, /// The symbol `typing.Literal` (which can also be found as `typing_extensions.Literal`) @@ -162,7 +163,7 @@ impl SpecialFormType { | Self::Protocol // actually `_ProtocolMeta` at runtime but this is what typeshed says | Self::ReadOnly => KnownClass::SpecialForm, - Self::Generic => KnownClass::Type, + Self::Generic | Self::Any => KnownClass::Type, Self::List | Self::Dict @@ -245,6 +246,7 @@ impl SpecialFormType { | Self::TypingSelf | Self::Protocol | Self::NamedTuple + | Self::Any | Self::ReadOnly => { matches!(module, KnownModule::Typing | KnownModule::TypingExtensions) } @@ -317,6 +319,7 @@ impl SpecialFormType { | Self::TypeIs | Self::ReadOnly | Self::Protocol + | Self::Any | Self::Generic => false, } } @@ -324,6 +327,7 @@ impl SpecialFormType { /// Return the repr of the symbol at runtime pub(super) const fn repr(self) -> &'static str { match self { + SpecialFormType::Any => "typing.Any", SpecialFormType::Annotated => "typing.Annotated", SpecialFormType::Literal => "typing.Literal", SpecialFormType::LiteralString => "typing.LiteralString", diff --git a/crates/ty_python_semantic/src/types/subclass_of.rs b/crates/ty_python_semantic/src/types/subclass_of.rs index 38f399ae56..63c7c13d51 100644 --- a/crates/ty_python_semantic/src/types/subclass_of.rs +++ b/crates/ty_python_semantic/src/types/subclass_of.rs @@ -7,7 +7,7 @@ use crate::types::variance::VarianceInferable; use crate::types::{ ApplyTypeMappingVisitor, BindingContext, BoundTypeVarInstance, ClassType, DynamicType, HasRelationToVisitor, IsDisjointVisitor, KnownClass, MemberLookupPolicy, NormalizedVisitor, - Type, TypeMapping, TypeRelation, TypeVarInstance, + SpecialFormType, Type, TypeMapping, TypeRelation, TypeVarInstance, }; use crate::{Db, FxOrderSet}; @@ -47,14 +47,10 @@ impl<'db> SubclassOfType<'db> { SubclassOfInner::Class(class) => { if class.is_final(db) { Type::from(class) + } else if class.is_object(db) { + KnownClass::Type.to_instance(db) } else { - match class.known(db) { - Some(KnownClass::Object) => KnownClass::Type.to_instance(db), - Some(KnownClass::Any) => Type::SubclassOf(Self { - subclass_of: SubclassOfInner::Dynamic(DynamicType::Any), - }), - _ => Type::SubclassOf(Self { subclass_of }), - } + Type::SubclassOf(Self { subclass_of }) } } } @@ -288,12 +284,9 @@ impl<'db> SubclassOfInner<'db> { pub(crate) fn try_from_type(db: &'db dyn Db, ty: Type<'db>) -> Option { match ty { Type::Dynamic(dynamic) => Some(Self::Dynamic(dynamic)), - Type::ClassLiteral(literal) => Some(if literal.is_known(db, KnownClass::Any) { - Self::Dynamic(DynamicType::Any) - } else { - Self::Class(literal.default_specialization(db)) - }), + Type::ClassLiteral(literal) => Some(Self::Class(literal.default_specialization(db))), Type::GenericAlias(generic) => Some(Self::Class(ClassType::Generic(generic))), + Type::SpecialForm(SpecialFormType::Any) => Some(Self::Dynamic(DynamicType::Any)), _ => None, } }