mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 13:24:57 +00:00
[ty] Completely ignore typeshed's stub for Any
(#20079)
This commit is contained in:
parent
d0bcf56bd9
commit
a04823cfad
12 changed files with 134 additions and 105 deletions
|
@ -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!
|
||||||
|
```
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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(_)
|
||||||
|
|
|
@ -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");
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()`,
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue