[ty] Completely ignore typeshed's stub for Any (#20079)

This commit is contained in:
Alex Waygood 2025-08-25 15:27:55 +01:00 committed by GitHub
parent d0bcf56bd9
commit a04823cfad
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 134 additions and 105 deletions

View file

@ -137,6 +137,22 @@ from unittest.mock import MagicMock
x: int = 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 ## Invalid
`Any` cannot be parameterized: `Any` cannot be parameterized:
@ -144,7 +160,32 @@ x: int = MagicMock()
```py ```py
from typing import Any 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]): def f(x: Any[int]):
reveal_type(x) # revealed: Unknown 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!
```

View file

@ -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[Any]))
static_assert(is_assignable_to(Meta, type[Unknown])) static_assert(is_assignable_to(Meta, type[Unknown]))
class AnyMeta(metaclass=Any): ... def _(x: Any):
class AnyMeta(metaclass=x): ...
static_assert(is_assignable_to(type[AnyMeta], type)) 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[object]))
static_assert(is_assignable_to(type[AnyMeta], type[Any])) static_assert(is_assignable_to(type[AnyMeta], type[Any]))
from typing import TypeVar, Generic, Any from typing import TypeVar, Generic, Any

View file

@ -5528,7 +5528,6 @@ impl<'db> Type<'db> {
// https://typing.python.org/en/latest/spec/special-types.html#special-cases-for-float-and-complex // https://typing.python.org/en/latest/spec/special-types.html#special-cases-for-float-and-complex
Type::ClassLiteral(class) => { Type::ClassLiteral(class) => {
let ty = match class.known(db) { let ty = match class.known(db) {
Some(KnownClass::Any) => Type::any(),
Some(KnownClass::Complex) => UnionType::from_elements( Some(KnownClass::Complex) => UnionType::from_elements(
db, db,
[ [
@ -5622,6 +5621,7 @@ impl<'db> Type<'db> {
Type::SpecialForm(special_form) => match special_form { Type::SpecialForm(special_form) => match special_form {
SpecialFormType::Never | SpecialFormType::NoReturn => Ok(Type::Never), SpecialFormType::Never | SpecialFormType::NoReturn => Ok(Type::Never),
SpecialFormType::LiteralString => Ok(Type::LiteralString), SpecialFormType::LiteralString => Ok(Type::LiteralString),
SpecialFormType::Any => Ok(Type::any()),
SpecialFormType::Unknown => Ok(Type::unknown()), SpecialFormType::Unknown => Ok(Type::unknown()),
SpecialFormType::AlwaysTruthy => Ok(Type::AlwaysTruthy), SpecialFormType::AlwaysTruthy => Ok(Type::AlwaysTruthy),
SpecialFormType::AlwaysFalsy => Ok(Type::AlwaysFalsy), SpecialFormType::AlwaysFalsy => Ok(Type::AlwaysFalsy),

View file

@ -3485,7 +3485,6 @@ pub enum KnownClass {
// Typeshed // Typeshed
NoneType, // Part of `types` for Python >= 3.10 NoneType, // Part of `types` for Python >= 3.10
// Typing // Typing
Any,
Awaitable, Awaitable,
Generator, Generator,
Deprecated, Deprecated,
@ -3568,8 +3567,7 @@ impl KnownClass {
Self::NoneType => Some(Truthiness::AlwaysFalse), Self::NoneType => Some(Truthiness::AlwaysFalse),
Self::Any Self::BaseException
| Self::BaseException
| Self::Exception | Self::Exception
| Self::ExceptionGroup | Self::ExceptionGroup
| Self::Object | Self::Object
@ -3689,7 +3687,6 @@ impl KnownClass {
// Anything with a *runtime* MRO (N.B. sometimes different from the MRO that typeshed gives!) // 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. // with length >2, or anything that is implemented in pure Python, is not a solid base.
Self::ABCMeta Self::ABCMeta
| Self::Any
| Self::Awaitable | Self::Awaitable
| Self::Generator | Self::Generator
| Self::Enum | Self::Enum
@ -3762,7 +3759,6 @@ impl KnownClass {
| KnownClass::AsyncGeneratorType | KnownClass::AsyncGeneratorType
| KnownClass::CoroutineType | KnownClass::CoroutineType
| KnownClass::NoneType | KnownClass::NoneType
| KnownClass::Any
| KnownClass::StdlibAlias | KnownClass::StdlibAlias
| KnownClass::SpecialForm | KnownClass::SpecialForm
| KnownClass::TypeVar | KnownClass::TypeVar
@ -3839,7 +3835,6 @@ impl KnownClass {
| KnownClass::AsyncGeneratorType | KnownClass::AsyncGeneratorType
| KnownClass::CoroutineType | KnownClass::CoroutineType
| KnownClass::NoneType | KnownClass::NoneType
| KnownClass::Any
| KnownClass::StdlibAlias | KnownClass::StdlibAlias
| KnownClass::SpecialForm | KnownClass::SpecialForm
| KnownClass::TypeVar | KnownClass::TypeVar
@ -3916,7 +3911,6 @@ impl KnownClass {
| KnownClass::AsyncGeneratorType | KnownClass::AsyncGeneratorType
| KnownClass::CoroutineType | KnownClass::CoroutineType
| KnownClass::NoneType | KnownClass::NoneType
| KnownClass::Any
| KnownClass::StdlibAlias | KnownClass::StdlibAlias
| KnownClass::SpecialForm | KnownClass::SpecialForm
| KnownClass::TypeVar | KnownClass::TypeVar
@ -3967,8 +3961,7 @@ impl KnownClass {
| Self::NamedTupleLike | Self::NamedTupleLike
| Self::Generator => true, | Self::Generator => true,
Self::Any Self::Bool
| Self::Bool
| Self::Object | Self::Object
| Self::Bytes | Self::Bytes
| Self::Bytearray | Self::Bytearray
@ -4037,7 +4030,6 @@ impl KnownClass {
pub(crate) fn name(self, db: &dyn Db) -> &'static str { pub(crate) fn name(self, db: &dyn Db) -> &'static str {
match self { match self {
Self::Any => "Any",
Self::Bool => "bool", Self::Bool => "bool",
Self::Object => "object", Self::Object => "object",
Self::Bytes => "bytes", Self::Bytes => "bytes",
@ -4347,8 +4339,7 @@ impl KnownClass {
| Self::UnionType | Self::UnionType
| Self::WrapperDescriptorType => KnownModule::Types, | Self::WrapperDescriptorType => KnownModule::Types,
Self::NoneType => KnownModule::Typeshed, Self::NoneType => KnownModule::Typeshed,
Self::Any Self::Awaitable
| Self::Awaitable
| Self::Generator | Self::Generator
| Self::SpecialForm | Self::SpecialForm
| Self::TypeVar | Self::TypeVar
@ -4409,8 +4400,7 @@ impl KnownClass {
| Self::UnionType | Self::UnionType
| Self::NotImplementedType => Some(true), | Self::NotImplementedType => Some(true),
Self::Any Self::Bool
| Self::Bool
| Self::Object | Self::Object
| Self::Bytes | Self::Bytes
| Self::Bytearray | Self::Bytearray
@ -4489,8 +4479,7 @@ impl KnownClass {
| Self::TypeAliasType | Self::TypeAliasType
| Self::NotImplementedType => true, | Self::NotImplementedType => true,
Self::Any Self::Bool
| Self::Bool
| Self::Object | Self::Object
| Self::Bytes | Self::Bytes
| Self::Bytearray | Self::Bytearray
@ -4565,7 +4554,6 @@ impl KnownClass {
// We assert that this match is exhaustive over the right-hand side in the unit test // We assert that this match is exhaustive over the right-hand side in the unit test
// `known_class_roundtrip_from_str()` // `known_class_roundtrip_from_str()`
let candidate = match class_name { let candidate = match class_name {
"Any" => Self::Any,
"bool" => Self::Bool, "bool" => Self::Bool,
"object" => Self::Object, "object" => Self::Object,
"bytes" => Self::Bytes, "bytes" => Self::Bytes,
@ -4655,8 +4643,7 @@ impl KnownClass {
/// Return `true` if the module of `self` matches `module` /// Return `true` if the module of `self` matches `module`
fn check_module(self, db: &dyn Db, module: KnownModule) -> bool { fn check_module(self, db: &dyn Db, module: KnownModule) -> bool {
match self { match self {
Self::Any Self::Bool
| Self::Bool
| Self::Object | Self::Object
| Self::Bytes | Self::Bytes
| Self::Bytearray | Self::Bytearray

View file

@ -77,13 +77,7 @@ impl<'db> ClassBase<'db> {
) -> Option<Self> { ) -> Option<Self> {
match ty { match ty {
Type::Dynamic(dynamic) => Some(Self::Dynamic(dynamic)), Type::Dynamic(dynamic) => Some(Self::Dynamic(dynamic)),
Type::ClassLiteral(literal) => { Type::ClassLiteral(literal) => Some(Self::Class(literal.default_specialization(db))),
if literal.is_known(db, KnownClass::Any) {
Some(Self::Dynamic(DynamicType::Any))
} else {
Some(Self::Class(literal.default_specialization(db)))
}
}
Type::GenericAlias(generic) => Some(Self::Class(ClassType::Generic(generic))), Type::GenericAlias(generic) => Some(Self::Class(ClassType::Generic(generic))),
Type::NominalInstance(instance) Type::NominalInstance(instance)
if instance.class(db).is_known(db, KnownClass::GenericAlias) => if instance.class(db).is_known(db, KnownClass::GenericAlias) =>
@ -201,6 +195,7 @@ impl<'db> ClassBase<'db> {
| SpecialFormType::AlwaysTruthy | SpecialFormType::AlwaysTruthy
| SpecialFormType::AlwaysFalsy => None, | SpecialFormType::AlwaysFalsy => None,
SpecialFormType::Any => Some(Self::Dynamic(DynamicType::Any)),
SpecialFormType::Unknown => Some(Self::unknown()), SpecialFormType::Unknown => Some(Self::unknown()),
SpecialFormType::Protocol => Some(Self::Protocol), SpecialFormType::Protocol => Some(Self::Protocol),

View file

@ -76,9 +76,6 @@ impl Display for DisplayType<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let representation = self.ty.representation(self.db, self.settings); let representation = self.ty.representation(self.db, self.settings);
match self.ty { match self.ty {
Type::ClassLiteral(literal) if literal.is_known(self.db, KnownClass::Any) => {
write!(f, "typing.Any")
}
Type::IntLiteral(_) Type::IntLiteral(_)
| Type::BooleanLiteral(_) | Type::BooleanLiteral(_)
| Type::StringLiteral(_) | Type::StringLiteral(_)

View file

@ -68,7 +68,7 @@ use crate::types::call::{Binding, CallArguments};
use crate::types::constraints::Constraints; use crate::types::constraints::Constraints;
use crate::types::context::InferContext; use crate::types::context::InferContext;
use crate::types::diagnostic::{ 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_bad_argument_to_get_protocol_members, report_bad_argument_to_protocol_interface,
report_runtime_check_against_non_runtime_checkable_protocol, report_runtime_check_against_non_runtime_checkable_protocol,
}; };
@ -79,8 +79,8 @@ use crate::types::visitor::any_over_type;
use crate::types::{ use crate::types::{
BoundMethodType, BoundTypeVarInstance, CallableType, ClassBase, ClassLiteral, ClassType, BoundMethodType, BoundTypeVarInstance, CallableType, ClassBase, ClassLiteral, ClassType,
DeprecatedInstance, DynamicType, HasRelationToVisitor, IsEquivalentVisitor, KnownClass, DeprecatedInstance, DynamicType, HasRelationToVisitor, IsEquivalentVisitor, KnownClass,
NormalizedVisitor, Truthiness, Type, TypeMapping, TypeRelation, UnionBuilder, all_members, NormalizedVisitor, SpecialFormType, Truthiness, Type, TypeMapping, TypeRelation, UnionBuilder,
walk_type_mapping, all_members, walk_type_mapping,
}; };
use crate::{Db, FxOrderSet, ModuleName, resolve_module}; use crate::{Db, FxOrderSet, ModuleName, resolve_module};
@ -1454,25 +1454,48 @@ impl KnownFunction {
} }
KnownFunction::IsInstance | KnownFunction::IsSubclass => { 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; return;
}; };
if let Some(protocol_class) = class.into_protocol_class(db) { match second_argument {
if !protocol_class.is_runtime_checkable(db) { Type::ClassLiteral(class) => {
report_runtime_check_against_non_runtime_checkable_protocol( if let Some(protocol_class) = class.into_protocol_class(db) {
context, if !protocol_class.is_runtime_checkable(db) {
call_expression, report_runtime_check_against_non_runtime_checkable_protocol(
protocol_class, context,
self, call_expression,
); protocol_class,
} self,
} );
}
}
if self == KnownFunction::IsInstance { if self == KnownFunction::IsInstance {
overload.set_return_type( overload.set_return_type(
is_instance_truthiness(db, *first_arg, *class).into_type(db), 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");
}
_ => {}
} }
} }

View file

@ -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 maybe_known_class = KnownClass::try_from_file_and_name(self.db(), self.file(), name);
let ty = if maybe_known_class.is_none() let in_typing_module = || {
&& &name.id == "NamedTuple" matches!(
&& matches!(
file_to_module(self.db(), self.file()).and_then(|module| module.known(self.db())), file_to_module(self.db(), self.file()).and_then(|module| module.known(self.db())),
Some(KnownModule::Typing | KnownModule::TypingExtensions) 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(), self.db(),
name.id.clone(), name.id.clone(),
body_scope, body_scope,
@ -3096,7 +3100,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
deprecated, deprecated,
dataclass_params, dataclass_params,
dataclass_transformer_params, dataclass_transformer_params,
)) )),
}; };
self.add_declaration_with_binding( self.add_declaration_with_binding(
@ -10242,9 +10246,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
let name_ty = self.infer_expression(slice); let name_ty = self.infer_expression(slice);
match name_ty { match name_ty {
Type::ClassLiteral(class_literal) => { Type::ClassLiteral(class_literal) => {
if class_literal.is_known(self.db(), KnownClass::Any) { if class_literal.is_protocol(self.db()) {
SubclassOfType::subclass_of_any()
} else if class_literal.is_protocol(self.db()) {
SubclassOfType::from( SubclassOfType::from(
self.db(), self.db(),
todo_type!("type[T] for protocols").expect_dynamic(), 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) => { Type::SpecialForm(SpecialFormType::Unknown) => {
SubclassOfType::subclass_of_unknown() SubclassOfType::subclass_of_unknown()
} }
@ -10339,13 +10342,6 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
self.infer_expression(&subscript.slice); self.infer_expression(&subscript.slice);
Type::unknown() 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) => { Type::SpecialForm(special_form) => {
self.infer_parameterized_special_form_type_expression(subscript, special_form) self.infer_parameterized_special_form_type_expression(subscript, special_form)
} }
@ -10919,6 +10915,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
| SpecialFormType::TypeAlias | SpecialFormType::TypeAlias
| SpecialFormType::TypedDict | SpecialFormType::TypedDict
| SpecialFormType::Unknown | SpecialFormType::Unknown
| SpecialFormType::Any
| SpecialFormType::NamedTuple => { | SpecialFormType::NamedTuple => {
self.infer_type_expression(arguments_slice); self.infer_type_expression(arguments_slice);

View file

@ -12,7 +12,7 @@ use crate::types::enums::is_single_member_enum;
use crate::types::protocol_class::walk_protocol_interface; use crate::types::protocol_class::walk_protocol_interface;
use crate::types::tuple::{TupleSpec, TupleType}; use crate::types::tuple::{TupleSpec, TupleType};
use crate::types::{ use crate::types::{
ApplyTypeMappingVisitor, ClassBase, DynamicType, HasRelationToVisitor, IsDisjointVisitor, ApplyTypeMappingVisitor, ClassBase, HasRelationToVisitor, IsDisjointVisitor,
IsEquivalentVisitor, NormalizedVisitor, TypeMapping, TypeRelation, VarianceInferable, IsEquivalentVisitor, NormalizedVisitor, TypeMapping, TypeRelation, VarianceInferable,
}; };
use crate::{Db, FxOrderSet}; use crate::{Db, FxOrderSet};
@ -23,20 +23,20 @@ impl<'db> Type<'db> {
pub(crate) fn instance(db: &'db dyn Db, class: ClassType<'db>) -> Self { pub(crate) fn instance(db: &'db dyn Db, class: ClassType<'db>) -> Self {
let (class_literal, specialization) = class.class_literal(db); let (class_literal, specialization) = class.class_literal(db);
match class_literal.known(db) { if class_literal.is_known(db, KnownClass::Tuple) {
Some(KnownClass::Any) => Type::Dynamic(DynamicType::Any), Type::tuple(TupleType::new(
Some(KnownClass::Tuple) => Type::tuple(TupleType::new(
db, db,
specialization specialization
.and_then(|spec| Some(Cow::Borrowed(spec.tuple(db)?))) .and_then(|spec| Some(Cow::Borrowed(spec.tuple(db)?)))
.unwrap_or_else(|| Cow::Owned(TupleSpec::homogeneous(Type::unknown()))) .unwrap_or_else(|| Cow::Owned(TupleSpec::homogeneous(Type::unknown())))
.as_ref(), .as_ref(),
)), ))
_ if class_literal.is_protocol(db) => { } else if class_literal.is_protocol(db) {
Self::ProtocolInstance(ProtocolInstanceType::from_class(class)) Self::ProtocolInstance(ProtocolInstanceType::from_class(class))
} } else if class_literal.is_typed_dict(db) {
_ if class_literal.is_typed_dict(db) => Type::typed_dict(class), Type::typed_dict(class)
_ => Type::non_tuple_instance(class), } else {
Type::non_tuple_instance(class)
} }
} }

View file

@ -183,15 +183,7 @@ impl ClassInfoConstraintFunction {
match classinfo { match classinfo {
Type::TypeAlias(alias) => self.generate_constraint(db, alias.value_type(db)), Type::TypeAlias(alias) => self.generate_constraint(db, alias.value_type(db)),
Type::ClassLiteral(class_literal) => { Type::ClassLiteral(class_literal) => Some(constraint_fn(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::SubclassOf(subclass_of_ty) => match subclass_of_ty.subclass_of() { Type::SubclassOf(subclass_of_ty) => match subclass_of_ty.subclass_of() {
SubclassOfInner::Class(ClassType::NonGeneric(class)) => Some(constraint_fn(class)), 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()`, // It's not valid to use a generic alias as the second argument to `isinstance()` or `issubclass()`,

View file

@ -27,6 +27,7 @@ use std::str::FromStr;
get_size2::GetSize, get_size2::GetSize,
)] )]
pub enum SpecialFormType { pub enum SpecialFormType {
Any,
/// The symbol `typing.Annotated` (which can also be found as `typing_extensions.Annotated`) /// The symbol `typing.Annotated` (which can also be found as `typing_extensions.Annotated`)
Annotated, Annotated,
/// The symbol `typing.Literal` (which can also be found as `typing_extensions.Literal`) /// 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::Protocol // actually `_ProtocolMeta` at runtime but this is what typeshed says
| Self::ReadOnly => KnownClass::SpecialForm, | Self::ReadOnly => KnownClass::SpecialForm,
Self::Generic => KnownClass::Type, Self::Generic | Self::Any => KnownClass::Type,
Self::List Self::List
| Self::Dict | Self::Dict
@ -245,6 +246,7 @@ impl SpecialFormType {
| Self::TypingSelf | Self::TypingSelf
| Self::Protocol | Self::Protocol
| Self::NamedTuple | Self::NamedTuple
| Self::Any
| Self::ReadOnly => { | Self::ReadOnly => {
matches!(module, KnownModule::Typing | KnownModule::TypingExtensions) matches!(module, KnownModule::Typing | KnownModule::TypingExtensions)
} }
@ -317,6 +319,7 @@ impl SpecialFormType {
| Self::TypeIs | Self::TypeIs
| Self::ReadOnly | Self::ReadOnly
| Self::Protocol | Self::Protocol
| Self::Any
| Self::Generic => false, | Self::Generic => false,
} }
} }
@ -324,6 +327,7 @@ impl SpecialFormType {
/// Return the repr of the symbol at runtime /// Return the repr of the symbol at runtime
pub(super) const fn repr(self) -> &'static str { pub(super) const fn repr(self) -> &'static str {
match self { match self {
SpecialFormType::Any => "typing.Any",
SpecialFormType::Annotated => "typing.Annotated", SpecialFormType::Annotated => "typing.Annotated",
SpecialFormType::Literal => "typing.Literal", SpecialFormType::Literal => "typing.Literal",
SpecialFormType::LiteralString => "typing.LiteralString", SpecialFormType::LiteralString => "typing.LiteralString",

View file

@ -7,7 +7,7 @@ use crate::types::variance::VarianceInferable;
use crate::types::{ use crate::types::{
ApplyTypeMappingVisitor, BindingContext, BoundTypeVarInstance, ClassType, DynamicType, ApplyTypeMappingVisitor, BindingContext, BoundTypeVarInstance, ClassType, DynamicType,
HasRelationToVisitor, IsDisjointVisitor, KnownClass, MemberLookupPolicy, NormalizedVisitor, HasRelationToVisitor, IsDisjointVisitor, KnownClass, MemberLookupPolicy, NormalizedVisitor,
Type, TypeMapping, TypeRelation, TypeVarInstance, SpecialFormType, Type, TypeMapping, TypeRelation, TypeVarInstance,
}; };
use crate::{Db, FxOrderSet}; use crate::{Db, FxOrderSet};
@ -47,14 +47,10 @@ impl<'db> SubclassOfType<'db> {
SubclassOfInner::Class(class) => { SubclassOfInner::Class(class) => {
if class.is_final(db) { if class.is_final(db) {
Type::from(class) Type::from(class)
} else if class.is_object(db) {
KnownClass::Type.to_instance(db)
} else { } else {
match class.known(db) { Type::SubclassOf(Self { subclass_of })
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 }),
}
} }
} }
} }
@ -288,12 +284,9 @@ impl<'db> SubclassOfInner<'db> {
pub(crate) fn try_from_type(db: &'db dyn Db, ty: Type<'db>) -> Option<Self> { pub(crate) fn try_from_type(db: &'db dyn Db, ty: Type<'db>) -> Option<Self> {
match ty { match ty {
Type::Dynamic(dynamic) => Some(Self::Dynamic(dynamic)), Type::Dynamic(dynamic) => Some(Self::Dynamic(dynamic)),
Type::ClassLiteral(literal) => Some(if literal.is_known(db, KnownClass::Any) { Type::ClassLiteral(literal) => Some(Self::Class(literal.default_specialization(db))),
Self::Dynamic(DynamicType::Any)
} else {
Self::Class(literal.default_specialization(db))
}),
Type::GenericAlias(generic) => Some(Self::Class(ClassType::Generic(generic))), Type::GenericAlias(generic) => Some(Self::Class(ClassType::Generic(generic))),
Type::SpecialForm(SpecialFormType::Any) => Some(Self::Dynamic(DynamicType::Any)),
_ => None, _ => None,
} }
} }