mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 05:44:56 +00:00
[ty] Infer type[tuple[int, str]]
as the meta-type of tuple[int, str]
(#19741)
This commit is contained in:
parent
bc6e8b58ce
commit
41207ec901
7 changed files with 71 additions and 21 deletions
|
@ -790,6 +790,13 @@ impl<'db> Type<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
pub const fn into_subclass_of(self) -> Option<SubclassOfType<'db>> {
|
||||
match self {
|
||||
Type::SubclassOf(subclass_of) => Some(subclass_of),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn expect_class_literal(self) -> ClassLiteral<'db> {
|
||||
self.into_class_literal()
|
||||
|
@ -5496,10 +5503,8 @@ impl<'db> Type<'db> {
|
|||
Type::Callable(_) | Type::DataclassTransformer(_) => KnownClass::Type.to_instance(db),
|
||||
Type::ModuleLiteral(_) => KnownClass::ModuleType.to_class_literal(db),
|
||||
Type::Tuple(tuple) => tuple
|
||||
.to_class_type(db)
|
||||
.map(Type::from)
|
||||
.unwrap_or_else(Type::unknown),
|
||||
|
||||
.to_subclass_of(db)
|
||||
.unwrap_or_else(SubclassOfType::subclass_of_unknown),
|
||||
Type::TypeVar(typevar) => match typevar.bound_or_constraints(db) {
|
||||
None => KnownClass::Type.to_instance(db),
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => bound.to_meta_type(db),
|
||||
|
|
|
@ -268,6 +268,10 @@ pub enum ClassType<'db> {
|
|||
|
||||
#[salsa::tracked]
|
||||
impl<'db> ClassType<'db> {
|
||||
pub(super) const fn is_not_generic(self) -> bool {
|
||||
matches!(self, Self::NonGeneric(_))
|
||||
}
|
||||
|
||||
pub(super) fn normalized_impl(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
|
|
|
@ -143,6 +143,11 @@ impl<'db> AllMembers<'db> {
|
|||
Type::ClassLiteral(class_literal) => {
|
||||
self.extend_with_class_members(db, ty, class_literal);
|
||||
}
|
||||
Type::SubclassOf(subclass_of) => {
|
||||
if let Some(class) = subclass_of.subclass_of().into_class() {
|
||||
self.extend_with_class_members(db, ty, class.class_literal(db).0);
|
||||
}
|
||||
}
|
||||
Type::GenericAlias(generic_alias) => {
|
||||
let class_literal = generic_alias.origin(db);
|
||||
self.extend_with_class_members(db, ty, class_literal);
|
||||
|
|
|
@ -6069,7 +6069,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
// the `try_call` path below.
|
||||
// TODO: it should be possible to move these special cases into the `try_call_constructor`
|
||||
// path instead, or even remove some entirely once we support overloads fully.
|
||||
if !matches!(
|
||||
let has_special_cased_constructor = matches!(
|
||||
class.known(self.db()),
|
||||
Some(
|
||||
KnownClass::Bool
|
||||
|
@ -6083,20 +6083,21 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
| KnownClass::TypeAliasType
|
||||
| KnownClass::Deprecated
|
||||
)
|
||||
)
|
||||
|
||||
// Constructor calls to `tuple` and subclasses of `tuple` are handled in `Type::Bindings`,
|
||||
// but constructor calls to `tuple[int]`, `tuple[int, ...]`, `tuple[int, *tuple[str, ...]]` (etc.)
|
||||
// are handled by the default constructor-call logic (we synthesize a `__new__` method for them
|
||||
// in `ClassType::own_class_member()`).
|
||||
&& (callable_type.is_generic_alias() || !class.is_known(self.db(), KnownClass::Tuple))
|
||||
) || (
|
||||
// Constructor calls to `tuple` and subclasses of `tuple` are handled in `Type::Bindings`,
|
||||
// but constructor calls to `tuple[int]`, `tuple[int, ...]`, `tuple[int, *tuple[str, ...]]` (etc.)
|
||||
// are handled by the default constructor-call logic (we synthesize a `__new__` method for them
|
||||
// in `ClassType::own_class_member()`).
|
||||
class.is_known(self.db(), KnownClass::Tuple) && class.is_not_generic()
|
||||
);
|
||||
|
||||
// temporary special-casing for all subclasses of `enum.Enum`
|
||||
// until we support the functional syntax for creating enum classes
|
||||
&& KnownClass::Enum
|
||||
.to_class_literal(self.db())
|
||||
.to_class_type(self.db())
|
||||
.is_none_or(|enum_class| !class.is_subclass_of(self.db(), enum_class))
|
||||
if !has_special_cased_constructor
|
||||
&& KnownClass::Enum
|
||||
.to_class_literal(self.db())
|
||||
.to_class_type(self.db())
|
||||
.is_none_or(|enum_class| !class.is_subclass_of(self.db(), enum_class))
|
||||
{
|
||||
let argument_forms = vec![Some(ParameterForm::Value); call_arguments.len()];
|
||||
self.infer_argument_types(arguments, &mut call_arguments, &argument_forms);
|
||||
|
@ -8478,6 +8479,15 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
}
|
||||
}
|
||||
|
||||
let tuple_generic_alias = |db: &'db dyn Db, tuple: Option<TupleType<'db>>| {
|
||||
let tuple =
|
||||
tuple.unwrap_or_else(|| TupleType::homogeneous(db, Type::unknown()).unwrap());
|
||||
tuple
|
||||
.to_class_type(db)
|
||||
.map(Type::from)
|
||||
.unwrap_or_else(Type::unknown)
|
||||
};
|
||||
|
||||
// HACK ALERT: If we are subscripting a generic class, short-circuit the rest of the
|
||||
// subscript inference logic and treat this as an explicit specialization.
|
||||
// TODO: Move this logic into a custom callable, and update `find_name_in_mro` to return
|
||||
|
@ -8486,8 +8496,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
// special cases, too.
|
||||
if let Type::ClassLiteral(class) = value_ty {
|
||||
if class.is_tuple(self.db()) {
|
||||
return Type::tuple(self.infer_tuple_type_expression(slice))
|
||||
.to_meta_type(self.db());
|
||||
return tuple_generic_alias(self.db(), self.infer_tuple_type_expression(slice));
|
||||
}
|
||||
if let Some(generic_context) = class.generic_context(self.db()) {
|
||||
return self.infer_explicit_class_specialization(
|
||||
|
@ -8499,7 +8508,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
}
|
||||
}
|
||||
if let Type::SpecialForm(SpecialFormType::Tuple) = value_ty {
|
||||
return Type::tuple(self.infer_tuple_type_expression(slice)).to_meta_type(self.db());
|
||||
return tuple_generic_alias(self.db(), self.infer_tuple_type_expression(slice));
|
||||
}
|
||||
|
||||
let slice_ty = self.infer_expression(slice);
|
||||
|
|
|
@ -22,8 +22,8 @@ use std::hash::Hash;
|
|||
|
||||
use itertools::{Either, EitherOrBoth, Itertools};
|
||||
|
||||
use crate::types::Truthiness;
|
||||
use crate::types::class::{ClassType, KnownClass};
|
||||
use crate::types::{SubclassOfType, Truthiness};
|
||||
use crate::types::{
|
||||
Type, TypeMapping, TypeRelation, TypeTransformer, TypeVarInstance, TypeVarVariance,
|
||||
UnionBuilder, UnionType, cyclic::PairVisitor,
|
||||
|
@ -238,6 +238,11 @@ impl<'db> TupleType<'db> {
|
|||
})
|
||||
}
|
||||
|
||||
pub(crate) fn to_subclass_of(self, db: &'db dyn Db) -> Option<Type<'db>> {
|
||||
self.to_class_type(db)
|
||||
.map(|class| SubclassOfType::from(db, class))
|
||||
}
|
||||
|
||||
/// Return a normalized version of `self`.
|
||||
///
|
||||
/// See [`Type::normalized`] for more details.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue