mirror of https://github.com/astral-sh/ruff
add `structural_type_ordering`
This commit is contained in:
parent
b8d3feed53
commit
ffffe97b23
|
|
@ -492,6 +492,10 @@ impl File {
|
|||
.map_or(PySourceType::Python, PySourceType::from_extension),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn structural_ordering(self, db: &dyn Db, other: Self) -> std::cmp::Ordering {
|
||||
self.path(db).cmp(other.path(db))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for File {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use std::fmt::{Display, Formatter};
|
|||
/// * a file stored on the [host system](crate::system::System).
|
||||
/// * a virtual file stored on the [host system](crate::system::System).
|
||||
/// * a vendored file stored in the [vendored file system](crate::vendored::VendoredFileSystem).
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash, get_size2::GetSize)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord, get_size2::GetSize)]
|
||||
pub enum FilePath {
|
||||
/// Path to a file on the [host system](crate::system::System).
|
||||
System(SystemPathBuf),
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ impl ToOwned for VendoredPath {
|
|||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Hash, PartialOrd, Ord)]
|
||||
pub struct VendoredPathBuf(Utf8PathBuf);
|
||||
|
||||
impl get_size2::GetSize for VendoredPathBuf {
|
||||
|
|
|
|||
|
|
@ -2410,7 +2410,7 @@ class C3:
|
|||
self.x = [self.x[0].flip()]
|
||||
|
||||
# TODO: should be `list[Unknown | Sub] | list[Unknown | Base] | Unknown`
|
||||
reveal_type(C3(Sub()).x) # revealed: list[Unknown | Sub] | list[Divergent] | Unknown
|
||||
reveal_type(C3(Sub()).x) # revealed: list[Divergent] | list[Unknown | Sub] | Unknown
|
||||
```
|
||||
|
||||
And cycles between many attributes:
|
||||
|
|
@ -2469,7 +2469,7 @@ class ManyCycles2:
|
|||
|
||||
def f1(self: "ManyCycles2"):
|
||||
# TODO: should be list[Unknown | int] | list[Divergent] | Unknown
|
||||
reveal_type(self.x3) # revealed: list[Unknown | int] | list[Divergent] | list[Divergent] | Unknown
|
||||
reveal_type(self.x3) # revealed: list[Divergent] | list[Divergent] | list[Unknown | int] | Unknown
|
||||
|
||||
self.x1 = [self.x2] + [self.x3]
|
||||
self.x2 = [self.x1] + [self.x3]
|
||||
|
|
|
|||
|
|
@ -248,9 +248,9 @@ IntOrStr = TypeAliasType(get_name(), int | str)
|
|||
type OptNestedInt = int | tuple[OptNestedInt, ...] | None
|
||||
|
||||
def f(x: OptNestedInt) -> None:
|
||||
reveal_type(x) # revealed: int | None | tuple[OptNestedInt, ...]
|
||||
reveal_type(x) # revealed: tuple[OptNestedInt, ...] | None | int
|
||||
if x is not None:
|
||||
reveal_type(x) # revealed: int | tuple[OptNestedInt, ...]
|
||||
reveal_type(x) # revealed: tuple[OptNestedInt, ...] | int
|
||||
```
|
||||
|
||||
### Invalid self-referential
|
||||
|
|
@ -344,12 +344,12 @@ def f(x: A):
|
|||
reveal_type(y) # revealed: tuple[A]
|
||||
|
||||
def g(x: A | B):
|
||||
reveal_type(x) # revealed: None | tuple[B]
|
||||
reveal_type(x) # revealed: tuple[B] | None
|
||||
|
||||
from ty_extensions import Intersection
|
||||
|
||||
def h(x: Intersection[A, B]):
|
||||
reveal_type(x) # revealed: None | tuple[B]
|
||||
reveal_type(x) # revealed: tuple[B] | None
|
||||
```
|
||||
|
||||
### Self-recursive callable type
|
||||
|
|
|
|||
|
|
@ -102,6 +102,17 @@ impl<'db> Module<'db> {
|
|||
.as_deref()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn structural_ordering(self, db: &'db dyn Db, other: Self) -> std::cmp::Ordering {
|
||||
match (self, other) {
|
||||
(Module::File(left), Module::File(right)) => left.structural_ordering(db, right),
|
||||
(Module::Namespace(left), Module::Namespace(right)) => {
|
||||
left.name(db).cmp(right.name(db))
|
||||
}
|
||||
(Module::File(_), Module::Namespace(_)) => std::cmp::Ordering::Less,
|
||||
(Module::Namespace(_), Module::File(_)) => std::cmp::Ordering::Greater,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Module<'_> {
|
||||
|
|
@ -274,6 +285,14 @@ pub struct FileModule<'db> {
|
|||
pub(super) known: Option<KnownModule>,
|
||||
}
|
||||
|
||||
impl FileModule<'_> {
|
||||
pub fn structural_ordering(self, db: &dyn Db, other: Self) -> std::cmp::Ordering {
|
||||
self.name(db)
|
||||
.cmp(other.name(db))
|
||||
.then_with(|| self.file(db).structural_ordering(db, other.file(db)))
|
||||
}
|
||||
}
|
||||
|
||||
/// A namespace package.
|
||||
///
|
||||
/// Namespace packages are special because there are
|
||||
|
|
|
|||
|
|
@ -122,6 +122,17 @@ impl<'db> Definition<'db> {
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn structural_ordering(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
other: Definition<'db>,
|
||||
) -> std::cmp::Ordering {
|
||||
self.file(db)
|
||||
.cmp(&other.file(db))
|
||||
.then_with(|| self.file_scope(db).cmp(&other.file_scope(db)))
|
||||
.then_with(|| self.place(db).cmp(&other.place(db)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the module-level docstring for the given file
|
||||
|
|
|
|||
|
|
@ -347,7 +347,7 @@ impl Hash for MemberExprRef<'_> {
|
|||
|
||||
/// Uniquely identifies a member in a scope.
|
||||
#[newtype_index]
|
||||
#[derive(get_size2::GetSize, salsa::Update)]
|
||||
#[derive(PartialOrd, Ord, get_size2::GetSize, salsa::Update)]
|
||||
pub struct ScopedMemberId;
|
||||
|
||||
/// The members of a scope. Allows lookup by member path and [`ScopedMemberId`].
|
||||
|
|
|
|||
|
|
@ -133,7 +133,9 @@ impl std::fmt::Display for PlaceExprRef<'_> {
|
|||
}
|
||||
|
||||
/// ID that uniquely identifies a place inside a [`Scope`](super::FileScopeId).
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, get_size2::GetSize, salsa::Update)]
|
||||
#[derive(
|
||||
Debug, Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord, get_size2::GetSize, salsa::Update,
|
||||
)]
|
||||
pub enum ScopedPlaceId {
|
||||
Symbol(ScopedSymbolId),
|
||||
Member(ScopedMemberId),
|
||||
|
|
|
|||
|
|
@ -64,11 +64,21 @@ impl<'db> ScopeId<'db> {
|
|||
NodeWithScopeKind::GeneratorExpression(_) => "<generator>",
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn structural_ordering(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
other: ScopeId<'db>,
|
||||
) -> std::cmp::Ordering {
|
||||
self.file(db)
|
||||
.cmp(&other.file(db))
|
||||
.then_with(|| self.file_scope_id(db).cmp(&other.file_scope_id(db)))
|
||||
}
|
||||
}
|
||||
|
||||
/// ID that uniquely identifies a scope inside of a module.
|
||||
#[newtype_index]
|
||||
#[derive(salsa::Update, get_size2::GetSize)]
|
||||
#[derive(salsa::Update, get_size2::GetSize, PartialOrd, Ord)]
|
||||
pub struct FileScopeId;
|
||||
|
||||
impl FileScopeId {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use std::ops::{Deref, DerefMut};
|
|||
|
||||
/// Uniquely identifies a symbol in a given scope.
|
||||
#[newtype_index]
|
||||
#[derive(get_size2::GetSize)]
|
||||
#[derive(PartialOrd, Ord, get_size2::GetSize)]
|
||||
pub struct ScopedSymbolId;
|
||||
|
||||
/// A symbol in a given scope.
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ use ruff_python_ast::name::Name;
|
|||
use ruff_text_size::{Ranged, TextRange};
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
|
||||
use type_ordering::union_or_intersection_elements_ordering;
|
||||
use type_ordering::{structural_type_ordering, union_or_intersection_elements_ordering};
|
||||
|
||||
pub(crate) use self::builder::{IntersectionBuilder, UnionBuilder};
|
||||
pub use self::cyclic::CycleDetector;
|
||||
|
|
@ -653,6 +653,26 @@ impl<'db> PropertyInstanceType<'db> {
|
|||
|
||||
getter_equivalence.and(db, setter_equivalence)
|
||||
}
|
||||
|
||||
fn structural_ordering(self, db: &'db dyn Db, other: Self) -> std::cmp::Ordering {
|
||||
let getter_ord = match (self.getter(db), other.getter(db)) {
|
||||
(Some(left), Some(right)) => structural_type_ordering(db, &left, &right),
|
||||
(Some(_), None) => std::cmp::Ordering::Greater,
|
||||
(None, Some(_)) => std::cmp::Ordering::Less,
|
||||
(None, None) => std::cmp::Ordering::Equal,
|
||||
};
|
||||
|
||||
if getter_ord != std::cmp::Ordering::Equal {
|
||||
return getter_ord;
|
||||
}
|
||||
|
||||
match (self.setter(db), other.setter(db)) {
|
||||
(Some(left), Some(right)) => structural_type_ordering(db, &left, &right),
|
||||
(Some(_), None) => std::cmp::Ordering::Greater,
|
||||
(None, Some(_)) => std::cmp::Ordering::Less,
|
||||
(None, None) => std::cmp::Ordering::Equal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
|
|
@ -756,6 +776,30 @@ impl<'db> DataclassParams<'db> {
|
|||
params.field_specifiers(db),
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn structural_ordering(self, db: &'db dyn Db, other: Self) -> std::cmp::Ordering {
|
||||
let flag_ord = self.flags(db).bits().cmp(&other.flags(db).bits());
|
||||
if flag_ord != std::cmp::Ordering::Equal {
|
||||
return flag_ord;
|
||||
}
|
||||
|
||||
let self_fields = self.field_specifiers(db);
|
||||
let other_fields = other.field_specifiers(db);
|
||||
|
||||
let fields_count = self_fields.len().cmp(&other_fields.len());
|
||||
if fields_count != std::cmp::Ordering::Equal {
|
||||
return fields_count;
|
||||
}
|
||||
|
||||
for (self_field, other_field) in self_fields.iter().zip(other_fields.iter()) {
|
||||
let field_ord = structural_type_ordering(db, self_field, other_field);
|
||||
if field_ord != std::cmp::Ordering::Equal {
|
||||
return field_ord;
|
||||
}
|
||||
}
|
||||
|
||||
std::cmp::Ordering::Equal
|
||||
}
|
||||
}
|
||||
|
||||
/// Representation of a type: a set of possible values at runtime.
|
||||
|
|
@ -8764,6 +8808,61 @@ impl<'db> KnownInstanceType<'db> {
|
|||
fn repr(self, db: &'db dyn Db) -> impl std::fmt::Display + 'db {
|
||||
self.display_with(db, DisplaySettings::default())
|
||||
}
|
||||
|
||||
fn structural_ordering(self, db: &'db dyn Db, other: Self) -> std::cmp::Ordering {
|
||||
match (self, other) {
|
||||
(Self::SubscriptedProtocol(left), Self::SubscriptedProtocol(right))
|
||||
| (Self::SubscriptedGeneric(left), Self::SubscriptedGeneric(right))
|
||||
| (Self::GenericContext(left), Self::GenericContext(right)) => {
|
||||
left.structural_ordering(db, right)
|
||||
}
|
||||
(Self::TypeVar(left), Self::TypeVar(right)) => left
|
||||
.identity(db)
|
||||
.structural_ordering(db, right.identity(db)),
|
||||
(Self::TypeAliasType(left), Self::TypeAliasType(right)) => {
|
||||
left.structural_ordering(db, right)
|
||||
}
|
||||
(Self::Deprecated(left), Self::Deprecated(right)) => {
|
||||
left.structural_ordering(db, right)
|
||||
}
|
||||
(Self::Field(left), Self::Field(right)) => left.structural_ordering(db, right),
|
||||
// No need to compare structurally, they are used only in debugging contexts
|
||||
(Self::ConstraintSet(left), Self::ConstraintSet(right)) => left.cmp(&right),
|
||||
(Self::Specialization(left), Self::Specialization(right)) => {
|
||||
left.structural_ordering(db, right)
|
||||
}
|
||||
(Self::UnionType(left), Self::UnionType(right)) => left.structural_ordering(db, right),
|
||||
(Self::Literal(left), Self::Literal(right))
|
||||
| (Self::Annotated(left), Self::Annotated(right))
|
||||
| (Self::TypeGenericAlias(left), Self::TypeGenericAlias(right))
|
||||
| (Self::LiteralStringAlias(left), Self::LiteralStringAlias(right)) => {
|
||||
structural_type_ordering(db, &left.inner(db), &right.inner(db))
|
||||
}
|
||||
(Self::Callable(left), Self::Callable(right)) => left.structural_ordering(db, right),
|
||||
(Self::NewType(left), Self::NewType(right)) => left.structural_ordering(db, right),
|
||||
(left, right) => {
|
||||
let index = |instance| match instance {
|
||||
Self::SubscriptedProtocol(_) => 0,
|
||||
Self::SubscriptedGeneric(_) => 1,
|
||||
Self::TypeVar(_) => 2,
|
||||
Self::TypeAliasType(_) => 3,
|
||||
Self::Deprecated(_) => 4,
|
||||
Self::Field(_) => 5,
|
||||
Self::ConstraintSet(_) => 6,
|
||||
Self::GenericContext(_) => 7,
|
||||
Self::Specialization(_) => 8,
|
||||
Self::UnionType(_) => 9,
|
||||
Self::Literal(_) => 10,
|
||||
Self::Annotated(_) => 11,
|
||||
Self::TypeGenericAlias(_) => 12,
|
||||
Self::Callable(_) => 13,
|
||||
Self::LiteralStringAlias(_) => 14,
|
||||
Self::NewType(_) => 15,
|
||||
};
|
||||
index(left).cmp(&index(right))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A type that is determined to be divergent during recursive type inference.
|
||||
|
|
@ -8842,7 +8941,7 @@ impl std::fmt::Display for DynamicType {
|
|||
|
||||
bitflags! {
|
||||
/// Type qualifiers that appear in an annotation expression.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, salsa::Update, Hash)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, salsa::Update, Hash, PartialOrd, Ord)]
|
||||
pub(crate) struct TypeQualifiers: u8 {
|
||||
/// `typing.ClassVar`
|
||||
const CLASS_VAR = 1 << 0;
|
||||
|
|
@ -9161,6 +9260,17 @@ pub struct DeprecatedInstance<'db> {
|
|||
// The Salsa heap is tracked separately.
|
||||
impl get_size2::GetSize for DeprecatedInstance<'_> {}
|
||||
|
||||
impl DeprecatedInstance<'_> {
|
||||
fn structural_ordering(self, db: &dyn Db, other: DeprecatedInstance<'_>) -> std::cmp::Ordering {
|
||||
match (self.message(db), other.message(db)) {
|
||||
(Some(left), Some(right)) => left.structural_ordering(db, right),
|
||||
(None, Some(_)) => std::cmp::Ordering::Less,
|
||||
(Some(_), None) => std::cmp::Ordering::Greater,
|
||||
(None, None) => std::cmp::Ordering::Equal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains information about instances of `dataclasses.Field`, typically created using
|
||||
/// `dataclasses.field()`.
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
|
|
@ -9218,6 +9328,24 @@ impl<'db> FieldInstance<'db> {
|
|||
self.alias(db),
|
||||
))
|
||||
}
|
||||
|
||||
fn structural_ordering(self, db: &dyn Db, other: FieldInstance<'_>) -> std::cmp::Ordering {
|
||||
match (self.default_type(db), other.default_type(db)) {
|
||||
(Some(left), Some(right)) => {
|
||||
let ord = structural_type_ordering(db, &left, &right);
|
||||
if ord != std::cmp::Ordering::Equal {
|
||||
return ord;
|
||||
}
|
||||
}
|
||||
(None, Some(_)) => return std::cmp::Ordering::Less,
|
||||
(Some(_), None) => return std::cmp::Ordering::Greater,
|
||||
(None, None) => {}
|
||||
}
|
||||
self.init(db)
|
||||
.cmp(&other.init(db))
|
||||
.then_with(|| self.kw_only(db).cmp(&other.kw_only(db)))
|
||||
.then_with(|| self.alias(db).cmp(&other.alias(db)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether this typevar was created via the legacy `TypeVar` constructor, using PEP 695 syntax,
|
||||
|
|
@ -9271,6 +9399,19 @@ pub struct TypeVarIdentity<'db> {
|
|||
|
||||
impl get_size2::GetSize for TypeVarIdentity<'_> {}
|
||||
|
||||
impl TypeVarIdentity<'_> {
|
||||
fn structural_ordering(self, db: &dyn Db, other: TypeVarIdentity<'_>) -> std::cmp::Ordering {
|
||||
self.name(db).cmp(other.name(db)).then_with(|| {
|
||||
match (self.definition(db), other.definition(db)) {
|
||||
(Some(left), Some(right)) => left.structural_ordering(db, right),
|
||||
(None, Some(_)) => std::cmp::Ordering::Less,
|
||||
(Some(_), None) => std::cmp::Ordering::Greater,
|
||||
(None, None) => std::cmp::Ordering::Equal,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A specific instance of a type variable that has not been bound to a generic context yet.
|
||||
///
|
||||
/// This is usually not the type that you want; if you are working with a typevar, in a generic
|
||||
|
|
@ -9857,6 +9998,17 @@ impl<'db> BoundTypeVarInstance<'db> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn structural_ordering(self, db: &'db dyn Db, other: Self) -> std::cmp::Ordering {
|
||||
self.typevar(db)
|
||||
.identity(db)
|
||||
.structural_ordering(db, other.typevar(db).identity(db))
|
||||
.then_with(|| {
|
||||
self.binding_context(db)
|
||||
.definition()
|
||||
.cmp(&other.binding_context(db).definition())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn walk_bound_type_var_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
|
||||
|
|
@ -10184,6 +10336,27 @@ impl<'db> UnionTypeInstance<'db> {
|
|||
|
||||
Some(Self::new(db, value_expr_types, union_type))
|
||||
}
|
||||
|
||||
fn structural_ordering(self, db: &'db dyn Db, other: Self) -> std::cmp::Ordering {
|
||||
match (self._value_expr_types(db), other._value_expr_types(db)) {
|
||||
(Some(left_tys), Some(right_tys)) => {
|
||||
let len_count = left_tys.len().cmp(&right_tys.len());
|
||||
if len_count != std::cmp::Ordering::Equal {
|
||||
return len_count;
|
||||
}
|
||||
for (left, right) in left_tys.iter().zip(right_tys.iter()) {
|
||||
let ord = structural_type_ordering(db, left, right);
|
||||
if ord != std::cmp::Ordering::Equal {
|
||||
return ord;
|
||||
}
|
||||
}
|
||||
std::cmp::Ordering::Equal
|
||||
}
|
||||
(Some(_), None) => std::cmp::Ordering::Less,
|
||||
(None, Some(_)) => std::cmp::Ordering::Greater,
|
||||
(None, None) => std::cmp::Ordering::Equal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A salsa-interned `Type`
|
||||
|
|
@ -11589,6 +11762,14 @@ impl<'db> BoundMethodType<'db> {
|
|||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn structural_ordering(self, db: &'db dyn Db, other: Self) -> std::cmp::Ordering {
|
||||
self.function(db)
|
||||
.structural_ordering(db, other.function(db))
|
||||
.then_with(|| {
|
||||
structural_type_ordering(db, &self.self_instance(db), &other.self_instance(db))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// This type represents the set of all callable objects with a certain, possibly overloaded,
|
||||
|
|
@ -11777,6 +11958,15 @@ impl<'db> CallableType<'db> {
|
|||
.is_equivalent_to_impl(db, other.signatures(db), inferable, visitor)
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn structural_ordering(self, db: &'db dyn Db, other: Self) -> std::cmp::Ordering {
|
||||
self.is_function_like(db)
|
||||
.cmp(&other.is_function_like(db))
|
||||
.then_with(|| {
|
||||
self.signatures(db)
|
||||
.structural_ordering(db, other.signatures(db))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Converting a type "into a callable" can possibly return a _union_ of callables. Eventually,
|
||||
|
|
@ -12391,6 +12581,48 @@ impl<'db> KnownBoundMethodType<'db> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn structural_ordering(self, db: &'db dyn Db, other: Self) -> std::cmp::Ordering {
|
||||
match (self, other) {
|
||||
(
|
||||
KnownBoundMethodType::FunctionTypeDunderGet(self_function),
|
||||
KnownBoundMethodType::FunctionTypeDunderGet(other_function),
|
||||
) => self_function.structural_ordering(db, other_function),
|
||||
|
||||
(
|
||||
KnownBoundMethodType::FunctionTypeDunderCall(self_function),
|
||||
KnownBoundMethodType::FunctionTypeDunderCall(other_function),
|
||||
) => self_function.structural_ordering(db, other_function),
|
||||
|
||||
(
|
||||
KnownBoundMethodType::PropertyDunderGet(self_property),
|
||||
KnownBoundMethodType::PropertyDunderGet(other_property),
|
||||
) => self_property.structural_ordering(db, other_property),
|
||||
|
||||
(
|
||||
KnownBoundMethodType::PropertyDunderSet(self_property),
|
||||
KnownBoundMethodType::PropertyDunderSet(other_property),
|
||||
) => self_property.structural_ordering(db, other_property),
|
||||
|
||||
(left, right) => {
|
||||
let index = |known| match known {
|
||||
KnownBoundMethodType::FunctionTypeDunderGet(_) => 0,
|
||||
KnownBoundMethodType::FunctionTypeDunderCall(_) => 1,
|
||||
KnownBoundMethodType::PropertyDunderGet(_) => 2,
|
||||
KnownBoundMethodType::PropertyDunderSet(_) => 3,
|
||||
KnownBoundMethodType::StrStartswith(_) => 4,
|
||||
KnownBoundMethodType::ConstraintSetRange => 5,
|
||||
KnownBoundMethodType::ConstraintSetAlways => 6,
|
||||
KnownBoundMethodType::ConstraintSetNever => 7,
|
||||
KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_) => 8,
|
||||
KnownBoundMethodType::ConstraintSetSatisfies(_) => 9,
|
||||
KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_) => 10,
|
||||
KnownBoundMethodType::GenericContextSpecializeConstrained(_) => 11,
|
||||
};
|
||||
index(left).cmp(&index(right))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a specific instance of `types.WrapperDescriptorType`
|
||||
|
|
@ -12646,6 +12878,14 @@ impl<'db> ModuleLiteralType<'db> {
|
|||
|
||||
place_and_qualifiers
|
||||
}
|
||||
|
||||
pub(super) fn structural_ordering(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
other: ModuleLiteralType<'db>,
|
||||
) -> std::cmp::Ordering {
|
||||
self.module(db).structural_ordering(db, other.module(db))
|
||||
}
|
||||
}
|
||||
|
||||
/// # Ordering
|
||||
|
|
@ -12759,6 +12999,17 @@ impl<'db> PEP695TypeAliasType<'db> {
|
|||
fn normalized_impl(self, _db: &'db dyn Db, _visitor: &NormalizedVisitor<'db>) -> Self {
|
||||
self
|
||||
}
|
||||
|
||||
fn structural_ordering(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
other: PEP695TypeAliasType<'db>,
|
||||
) -> std::cmp::Ordering {
|
||||
self.name(db).cmp(other.name(db)).then_with(|| {
|
||||
self.rhs_scope(db)
|
||||
.structural_ordering(db, other.rhs_scope(db))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn generic_context_cycle_initial<'db>(
|
||||
|
|
@ -12832,6 +13083,21 @@ impl<'db> ManualPEP695TypeAliasType<'db> {
|
|||
.recursive_type_normalized_impl(db, div, true)?,
|
||||
))
|
||||
}
|
||||
|
||||
fn structural_ordering(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
other: ManualPEP695TypeAliasType<'db>,
|
||||
) -> std::cmp::Ordering {
|
||||
self.name(db).cmp(other.name(db)).then_with(|| {
|
||||
match (self.definition(db), other.definition(db)) {
|
||||
(Some(left), Some(right)) => left.structural_ordering(db, right),
|
||||
(None, None) => std::cmp::Ordering::Equal,
|
||||
(Some(_), None) => std::cmp::Ordering::Greater,
|
||||
(None, Some(_)) => std::cmp::Ordering::Less,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
|
|
@ -12952,6 +13218,21 @@ impl<'db> TypeAliasType<'db> {
|
|||
TypeAliasType::ManualPEP695(_) => self,
|
||||
}
|
||||
}
|
||||
|
||||
fn structural_ordering(self, db: &'db dyn Db, other: TypeAliasType<'db>) -> std::cmp::Ordering {
|
||||
match (self, other) {
|
||||
(TypeAliasType::PEP695(left), TypeAliasType::PEP695(right)) => {
|
||||
left.structural_ordering(db, right)
|
||||
}
|
||||
(TypeAliasType::ManualPEP695(left), TypeAliasType::ManualPEP695(right)) => {
|
||||
left.structural_ordering(db, right)
|
||||
}
|
||||
(TypeAliasType::PEP695(_), TypeAliasType::ManualPEP695(_)) => std::cmp::Ordering::Less,
|
||||
(TypeAliasType::ManualPEP695(_), TypeAliasType::PEP695(_)) => {
|
||||
std::cmp::Ordering::Greater
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Either the explicit `metaclass=` keyword of the class, or the inferred metaclass of one of its base classes.
|
||||
|
|
@ -13356,8 +13637,7 @@ impl<'db> IntersectionType<'db> {
|
|||
.map(|ty| ty.normalized_impl(db, visitor))
|
||||
.collect();
|
||||
|
||||
elements
|
||||
.sort_unstable_by(|l, r| union_or_intersection_elements_ordering(db, l, r, false));
|
||||
elements.sort_unstable_by(|l, r| union_or_intersection_elements_ordering(db, l, r));
|
||||
elements
|
||||
}
|
||||
|
||||
|
|
@ -13599,6 +13879,14 @@ impl<'db> StringLiteralType<'db> {
|
|||
pub(crate) fn python_len(self, db: &'db dyn Db) -> usize {
|
||||
self.value(db).chars().count()
|
||||
}
|
||||
|
||||
fn structural_ordering(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
other: StringLiteralType<'db>,
|
||||
) -> std::cmp::Ordering {
|
||||
self.value(db).cmp(other.value(db))
|
||||
}
|
||||
}
|
||||
|
||||
/// # Ordering
|
||||
|
|
@ -13618,6 +13906,14 @@ impl<'db> BytesLiteralType<'db> {
|
|||
pub(crate) fn python_len(self, db: &'db dyn Db) -> usize {
|
||||
self.value(db).len()
|
||||
}
|
||||
|
||||
fn structural_ordering(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
other: BytesLiteralType<'db>,
|
||||
) -> std::cmp::Ordering {
|
||||
self.value(db).cmp(other.value(db))
|
||||
}
|
||||
}
|
||||
|
||||
/// A singleton type corresponding to a specific enum member.
|
||||
|
|
@ -13646,6 +13942,17 @@ impl<'db> EnumLiteralType<'db> {
|
|||
pub(crate) fn enum_class_instance(self, db: &'db dyn Db) -> Type<'db> {
|
||||
self.enum_class(db).to_non_generic_instance(db)
|
||||
}
|
||||
|
||||
fn structural_ordering(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
other: EnumLiteralType<'db>,
|
||||
) -> std::cmp::Ordering {
|
||||
self.name(db).cmp(other.name(db)).then_with(|| {
|
||||
self.enum_class(db)
|
||||
.structural_ordering(db, other.enum_class(db))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
|
|
@ -13706,6 +14013,23 @@ impl<'db> TypeIsType<'db> {
|
|||
pub(crate) fn is_bound(self, db: &'db dyn Db) -> bool {
|
||||
self.place_info(db).is_some()
|
||||
}
|
||||
|
||||
pub(super) fn structural_ordering(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
other: TypeIsType<'db>,
|
||||
) -> std::cmp::Ordering {
|
||||
structural_type_ordering(db, &self.return_type(db), &other.return_type(db)).then_with(
|
||||
|| match (self.place_info(db), other.place_info(db)) {
|
||||
(Some((left_scope, left_place)), Some((right_scope, right_place))) => left_scope
|
||||
.structural_ordering(db, right_scope)
|
||||
.then_with(|| left_place.cmp(&right_place)),
|
||||
(None, Some(_)) => std::cmp::Ordering::Less,
|
||||
(Some(_), None) => std::cmp::Ordering::Greater,
|
||||
(None, None) => std::cmp::Ordering::Equal,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> VarianceInferable<'db> for TypeIsType<'db> {
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ use crate::types::enums::{enum_member_literals, enum_metadata};
|
|||
use crate::types::type_ordering::union_or_intersection_elements_ordering;
|
||||
use crate::types::{
|
||||
BytesLiteralType, IntersectionType, KnownClass, StringLiteralType, Type,
|
||||
TypeVarBoundOrConstraints, UnionType,
|
||||
TypeVarBoundOrConstraints, UnionType, structural_type_ordering,
|
||||
};
|
||||
use crate::{Db, FxOrderSet};
|
||||
use rustc_hash::FxHashSet;
|
||||
|
|
@ -613,7 +613,11 @@ impl<'db> UnionBuilder<'db> {
|
|||
}
|
||||
if self.order_elements {
|
||||
types.sort_unstable_by(|l, r| {
|
||||
union_or_intersection_elements_ordering(self.db, l, r, self.cycle_recovery)
|
||||
if self.cycle_recovery && self.recursively_defined.is_yes() {
|
||||
structural_type_ordering(self.db, l, r)
|
||||
} else {
|
||||
union_or_intersection_elements_ordering(self.db, l, r)
|
||||
}
|
||||
});
|
||||
}
|
||||
match types.len() {
|
||||
|
|
|
|||
|
|
@ -332,6 +332,19 @@ impl<'db> GenericAlias<'db> {
|
|||
pub(super) fn is_typed_dict(self, db: &'db dyn Db) -> bool {
|
||||
self.origin(db).is_typed_dict(db)
|
||||
}
|
||||
|
||||
pub(super) fn structural_ordering(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
other: GenericAlias<'db>,
|
||||
) -> std::cmp::Ordering {
|
||||
self.origin(db)
|
||||
.structural_ordering(db, other.origin(db))
|
||||
.then_with(|| {
|
||||
self.specialization(db)
|
||||
.structural_ordering(db, other.specialization(db))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> From<GenericAlias<'db>> for Type<'db> {
|
||||
|
|
@ -1278,6 +1291,19 @@ impl<'db> ClassType<'db> {
|
|||
pub(super) fn header_span(self, db: &'db dyn Db) -> Span {
|
||||
self.class_literal(db).0.header_span(db)
|
||||
}
|
||||
|
||||
pub(super) fn structural_ordering(self, db: &'db dyn Db, other: Self) -> std::cmp::Ordering {
|
||||
match (self, other) {
|
||||
(ClassType::NonGeneric(left), ClassType::NonGeneric(right)) => {
|
||||
left.structural_ordering(db, right)
|
||||
}
|
||||
(ClassType::Generic(left), ClassType::Generic(right)) => {
|
||||
left.structural_ordering(db, right)
|
||||
}
|
||||
(ClassType::NonGeneric(_), ClassType::Generic(_)) => std::cmp::Ordering::Less,
|
||||
(ClassType::Generic(_), ClassType::NonGeneric(_)) => std::cmp::Ordering::Greater,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn into_callable_cycle_initial<'db>(
|
||||
|
|
@ -3769,6 +3795,20 @@ impl<'db> ClassLiteral<'db> {
|
|||
pub(super) fn qualified_name(self, db: &'db dyn Db) -> QualifiedClassName<'db> {
|
||||
QualifiedClassName { db, class: self }
|
||||
}
|
||||
|
||||
pub(super) fn structural_ordering(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
other: ClassLiteral<'db>,
|
||||
) -> std::cmp::Ordering {
|
||||
self.name(db)
|
||||
.cmp(other.name(db))
|
||||
.then_with(|| self.known(db).cmp(&other.known(db)))
|
||||
.then_with(|| {
|
||||
self.body_scope(db)
|
||||
.structural_ordering(db, other.body_scope(db))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> From<ClassLiteral<'db>> for Type<'db> {
|
||||
|
|
@ -4050,7 +4090,7 @@ pub(super) enum DisjointBaseKind {
|
|||
/// Feel free to expand this enum if you ever find yourself using the same class in multiple
|
||||
/// places.
|
||||
/// Note: good candidates are any classes in `[crate::module_resolver::module::KnownModule]`
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, get_size2::GetSize)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, get_size2::GetSize)]
|
||||
#[cfg_attr(test, derive(strum_macros::EnumIter))]
|
||||
pub enum KnownClass {
|
||||
// To figure out where an stdlib symbol is defined, you can go into `crates/ty_vendored`
|
||||
|
|
|
|||
|
|
@ -1129,6 +1129,21 @@ impl<'db> FunctionType<'db> {
|
|||
updated_last_definition_signature,
|
||||
))
|
||||
}
|
||||
|
||||
pub(super) fn structural_ordering(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
other: FunctionType<'db>,
|
||||
) -> std::cmp::Ordering {
|
||||
self.name(db).cmp(other.name(db)).then_with(|| {
|
||||
match (self.updated_signature(db), other.updated_signature(db)) {
|
||||
(Some(left_sig), Some(right_sig)) => left_sig.structural_ordering(db, right_sig),
|
||||
(None, None) => std::cmp::Ordering::Equal,
|
||||
(Some(_), None) => std::cmp::Ordering::Greater,
|
||||
(None, Some(_)) => std::cmp::Ordering::Less,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate an `isinstance` call. Return `Truthiness::AlwaysTrue` if we can definitely infer that
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ use crate::types::{
|
|||
FindLegacyTypeVarsVisitor, HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor,
|
||||
KnownClass, KnownInstanceType, MaterializationKind, NormalizedVisitor, Type, TypeContext,
|
||||
TypeMapping, TypeRelation, TypeVarBoundOrConstraints, TypeVarIdentity, TypeVarInstance,
|
||||
TypeVarKind, TypeVarVariance, UnionType, declaration_type, walk_bound_type_var_type,
|
||||
TypeVarKind, TypeVarVariance, UnionType, declaration_type, structural_type_ordering,
|
||||
walk_bound_type_var_type,
|
||||
};
|
||||
use crate::{Db, FxOrderMap, FxOrderSet};
|
||||
|
||||
|
|
@ -602,6 +603,26 @@ impl<'db> GenericContext<'db> {
|
|||
) -> usize {
|
||||
ruff_memory_usage::order_map_heap_size(variables)
|
||||
}
|
||||
|
||||
pub(super) fn structural_ordering(self, db: &'db dyn Db, other: Self) -> std::cmp::Ordering {
|
||||
let left = self.variables_inner(db);
|
||||
let right = other.variables_inner(db);
|
||||
let variables_count = left.len().cmp(&right.len());
|
||||
if variables_count != std::cmp::Ordering::Equal {
|
||||
return variables_count;
|
||||
}
|
||||
for (left_key, left_value) in left {
|
||||
if let Some(right_value) = right.get(left_key) {
|
||||
let ord = left_value.structural_ordering(db, *right_value);
|
||||
if ord != std::cmp::Ordering::Equal {
|
||||
return ord;
|
||||
}
|
||||
} else {
|
||||
return std::cmp::Ordering::Greater;
|
||||
}
|
||||
}
|
||||
std::cmp::Ordering::Equal
|
||||
}
|
||||
}
|
||||
|
||||
fn inferable_typevars_cycle_initial<'db>(
|
||||
|
|
@ -1282,6 +1303,20 @@ impl<'db> Specialization<'db> {
|
|||
// A tuple's specialization will include all of its element types, so we don't need to also
|
||||
// look in `self.tuple`.
|
||||
}
|
||||
|
||||
pub(super) fn structural_ordering(self, db: &'db dyn Db, other: Self) -> std::cmp::Ordering {
|
||||
let types_count = self.types(db).len().cmp(&other.types(db).len());
|
||||
if types_count != std::cmp::Ordering::Equal {
|
||||
return types_count;
|
||||
}
|
||||
for (left, right) in self.types(db).iter().zip(other.types(db)) {
|
||||
let ord = structural_type_ordering(db, left, right);
|
||||
if ord != std::cmp::Ordering::Equal {
|
||||
return ord;
|
||||
}
|
||||
}
|
||||
std::cmp::Ordering::Equal
|
||||
}
|
||||
}
|
||||
|
||||
/// A mapping between type variables and types.
|
||||
|
|
|
|||
|
|
@ -547,6 +547,25 @@ impl<'db> NominalInstanceType<'db> {
|
|||
NominalInstanceInner::Object => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn structural_ordering(self, db: &'db dyn Db, other: Self) -> std::cmp::Ordering {
|
||||
match (self.0, other.0) {
|
||||
(NominalInstanceInner::Object, NominalInstanceInner::Object) => {
|
||||
std::cmp::Ordering::Equal
|
||||
}
|
||||
(NominalInstanceInner::Object, _) => std::cmp::Ordering::Less,
|
||||
(_, NominalInstanceInner::Object) => std::cmp::Ordering::Greater,
|
||||
(
|
||||
NominalInstanceInner::ExactTuple(tuple1),
|
||||
NominalInstanceInner::ExactTuple(tuple2),
|
||||
) => tuple1.structural_ordering(db, tuple2),
|
||||
(NominalInstanceInner::ExactTuple(_), _) => std::cmp::Ordering::Less,
|
||||
(_, NominalInstanceInner::ExactTuple(_)) => std::cmp::Ordering::Greater,
|
||||
(NominalInstanceInner::NonTuple(class1), NominalInstanceInner::NonTuple(class2)) => {
|
||||
class1.structural_ordering(db, class2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> From<NominalInstanceType<'db>> for Type<'db> {
|
||||
|
|
@ -835,6 +854,19 @@ impl<'db> ProtocolInstanceType<'db> {
|
|||
pub(super) fn interface(self, db: &'db dyn Db) -> ProtocolInterface<'db> {
|
||||
self.inner.interface(db)
|
||||
}
|
||||
|
||||
pub(super) fn structural_ordering(self, db: &'db dyn Db, other: Self) -> std::cmp::Ordering {
|
||||
match (self.inner, other.inner) {
|
||||
(Protocol::FromClass(left), Protocol::FromClass(right)) => {
|
||||
left.structural_ordering(db, *right)
|
||||
}
|
||||
(Protocol::Synthesized(left), Protocol::Synthesized(right)) => {
|
||||
left.structural_ordering(db, right)
|
||||
}
|
||||
(Protocol::FromClass(_), Protocol::Synthesized(_)) => std::cmp::Ordering::Less,
|
||||
(Protocol::Synthesized(_), Protocol::FromClass(_)) => std::cmp::Ordering::Greater,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> VarianceInferable<'db> for ProtocolInstanceType<'db> {
|
||||
|
|
@ -963,6 +995,14 @@ mod synthesized_protocol {
|
|||
self.0.recursive_type_normalized_impl(db, div, nested)?,
|
||||
))
|
||||
}
|
||||
|
||||
pub(in crate::types) fn structural_ordering(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
other: Self,
|
||||
) -> std::cmp::Ordering {
|
||||
self.0.structural_ordering(db, other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> VarianceInferable<'db> for SynthesizedProtocolType<'db> {
|
||||
|
|
|
|||
|
|
@ -194,6 +194,13 @@ impl<'db> NewType<'db> {
|
|||
self.try_map_base_class_type(db, |class_type| Some(f(class_type)))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub(super) fn structural_ordering(self, db: &'db dyn Db, other: Self) -> std::cmp::Ordering {
|
||||
self.name(db).cmp(other.name(db)).then_with(|| {
|
||||
self.definition(db)
|
||||
.structural_ordering(db, other.definition(db))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn walk_newtype_instance_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use itertools::Itertools;
|
|||
use ruff_python_ast::name::Name;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::types::TypeContext;
|
||||
use crate::types::{TypeContext, structural_type_ordering};
|
||||
use crate::{
|
||||
Db, FxOrderSet,
|
||||
place::{Definedness, Place, PlaceAndQualifiers, place_from_bindings, place_from_declarations},
|
||||
|
|
@ -461,6 +461,22 @@ impl<'db> ProtocolInterface<'db> {
|
|||
interface: self,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn structural_ordering(self, db: &'db dyn Db, other: Self) -> std::cmp::Ordering {
|
||||
let members_count = self.members(db).len().cmp(&other.members(db).len());
|
||||
if members_count != std::cmp::Ordering::Equal {
|
||||
return members_count;
|
||||
}
|
||||
|
||||
for (left, right) in self.members(db).zip(other.members(db)) {
|
||||
let ord = left.structural_ordering(db, &right);
|
||||
if ord != std::cmp::Ordering::Equal {
|
||||
return ord;
|
||||
}
|
||||
}
|
||||
|
||||
std::cmp::Ordering::Equal
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> VarianceInferable<'db> for ProtocolInterface<'db> {
|
||||
|
|
@ -813,6 +829,42 @@ impl<'a, 'db> ProtocolMember<'a, 'db> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn structural_ordering(&self, db: &'db dyn Db, other: &Self) -> std::cmp::Ordering {
|
||||
let name_ordering = self.name.cmp(other.name);
|
||||
if name_ordering != std::cmp::Ordering::Equal {
|
||||
return name_ordering;
|
||||
}
|
||||
let qualifiers_ordering = self.qualifiers.cmp(&other.qualifiers);
|
||||
if qualifiers_ordering != std::cmp::Ordering::Equal {
|
||||
return qualifiers_ordering;
|
||||
}
|
||||
|
||||
match (&self.kind, &other.kind) {
|
||||
(ProtocolMemberKind::Method(left), ProtocolMemberKind::Method(right)) => {
|
||||
left.structural_ordering(db, *right)
|
||||
}
|
||||
(ProtocolMemberKind::Property(left), ProtocolMemberKind::Property(right)) => {
|
||||
left.structural_ordering(db, *right)
|
||||
}
|
||||
(ProtocolMemberKind::Other(left), ProtocolMemberKind::Other(right)) => {
|
||||
structural_type_ordering(db, left, right)
|
||||
}
|
||||
(left, right) => {
|
||||
let left_index = match left {
|
||||
ProtocolMemberKind::Method(_) => 0,
|
||||
ProtocolMemberKind::Property(_) => 1,
|
||||
ProtocolMemberKind::Other(_) => 2,
|
||||
};
|
||||
let right_index = match right {
|
||||
ProtocolMemberKind::Method(_) => 0,
|
||||
ProtocolMemberKind::Property(_) => 1,
|
||||
ProtocolMemberKind::Other(_) => 2,
|
||||
};
|
||||
left_index.cmp(&right_index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if a declaration or binding to a given name in a protocol class body
|
||||
|
|
|
|||
|
|
@ -31,7 +31,8 @@ use crate::types::infer::nearest_enclosing_class;
|
|||
use crate::types::{
|
||||
ApplyTypeMappingVisitor, BoundTypeVarInstance, ClassLiteral, FindLegacyTypeVarsVisitor,
|
||||
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, KnownClass, MaterializationKind,
|
||||
NormalizedVisitor, TypeContext, TypeMapping, TypeRelation, VarianceInferable, todo_type,
|
||||
NormalizedVisitor, TypeContext, TypeMapping, TypeRelation, VarianceInferable,
|
||||
structural_type_ordering, todo_type,
|
||||
};
|
||||
use crate::{Db, FxOrderSet};
|
||||
use ruff_python_ast::{self as ast, name::Name};
|
||||
|
|
@ -367,6 +368,23 @@ impl<'db> CallableSignature<'db> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn structural_ordering(
|
||||
&self,
|
||||
db: &'db dyn Db,
|
||||
other: &CallableSignature<'db>,
|
||||
) -> std::cmp::Ordering {
|
||||
if self.overloads.len() != other.overloads.len() {
|
||||
return self.overloads.len().cmp(&other.overloads.len());
|
||||
}
|
||||
for (left_sig, right_sig) in self.overloads.iter().zip(&other.overloads) {
|
||||
let ord = left_sig.structural_ordering(db, right_sig);
|
||||
if ord != std::cmp::Ordering::Equal {
|
||||
return ord;
|
||||
}
|
||||
}
|
||||
std::cmp::Ordering::Equal
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'db> IntoIterator for &'a CallableSignature<'db> {
|
||||
|
|
@ -1294,6 +1312,26 @@ impl<'db> Signature<'db> {
|
|||
pub(crate) fn with_definition(self, definition: Option<Definition<'db>>) -> Self {
|
||||
Self { definition, ..self }
|
||||
}
|
||||
|
||||
fn structural_ordering(&self, db: &'db dyn Db, other: &Signature<'db>) -> std::cmp::Ordering {
|
||||
let parameters_count = self.parameters.len().cmp(&other.parameters.len());
|
||||
if parameters_count != std::cmp::Ordering::Equal {
|
||||
return parameters_count;
|
||||
}
|
||||
for (left, right) in self.parameters.iter().zip(&other.parameters) {
|
||||
let ord = left.structural_ordering(db, right);
|
||||
if ord != std::cmp::Ordering::Equal {
|
||||
return ord;
|
||||
}
|
||||
}
|
||||
|
||||
match (self.return_ty.as_ref(), other.return_ty.as_ref()) {
|
||||
(Some(left_ty), Some(right_ty)) => structural_type_ordering(db, left_ty, right_ty),
|
||||
(None, Some(_)) => std::cmp::Ordering::Less,
|
||||
(Some(_), None) => std::cmp::Ordering::Greater,
|
||||
(None, None) => std::cmp::Ordering::Equal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> VarianceInferable<'db> for &Signature<'db> {
|
||||
|
|
@ -2074,6 +2112,17 @@ impl<'db> Parameter<'db> {
|
|||
ParameterKind::Variadic { .. } | ParameterKind::KeywordVariadic { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn structural_ordering(&self, db: &'db dyn Db, other: &Parameter<'db>) -> std::cmp::Ordering {
|
||||
self.kind.cmp(&other.kind).then_with(|| {
|
||||
match (self.annotated_type.as_ref(), other.annotated_type.as_ref()) {
|
||||
(Some(left_ty), Some(right_ty)) => structural_type_ordering(db, left_ty, right_ty),
|
||||
(None, Some(_)) => std::cmp::Ordering::Less,
|
||||
(Some(_), None) => std::cmp::Ordering::Greater,
|
||||
(None, None) => std::cmp::Ordering::Equal,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)]
|
||||
|
|
@ -2115,6 +2164,46 @@ pub(crate) enum ParameterKind<'db> {
|
|||
},
|
||||
}
|
||||
|
||||
impl Ord for ParameterKind<'_> {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
match (self, other) {
|
||||
(
|
||||
Self::PositionalOnly { name: l_name, .. },
|
||||
Self::PositionalOnly { name: r_name, .. },
|
||||
) => l_name.cmp(r_name),
|
||||
(
|
||||
Self::PositionalOrKeyword { name: l_name, .. },
|
||||
Self::PositionalOrKeyword { name: r_name, .. },
|
||||
) => l_name.cmp(r_name),
|
||||
(Self::Variadic { name: l_name }, Self::Variadic { name: r_name }) => {
|
||||
l_name.cmp(r_name)
|
||||
}
|
||||
(Self::KeywordOnly { name: l_name, .. }, Self::KeywordOnly { name: r_name, .. }) => {
|
||||
l_name.cmp(r_name)
|
||||
}
|
||||
(Self::KeywordVariadic { name: l_name }, Self::KeywordVariadic { name: r_name }) => {
|
||||
l_name.cmp(r_name)
|
||||
}
|
||||
(left, right) => {
|
||||
let index = |param: &_| match param {
|
||||
Self::PositionalOnly { .. } => 0,
|
||||
Self::PositionalOrKeyword { .. } => 1,
|
||||
Self::Variadic { .. } => 2,
|
||||
Self::KeywordOnly { .. } => 3,
|
||||
Self::KeywordVariadic { .. } => 4,
|
||||
};
|
||||
index(left).cmp(&index(right))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for ParameterKind<'_> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> ParameterKind<'db> {
|
||||
fn apply_type_mapping_impl<'a>(
|
||||
&self,
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ use crate::types::generics::InferableTypeVars;
|
|||
use crate::types::{
|
||||
ApplyTypeMappingVisitor, BoundTypeVarInstance, FindLegacyTypeVarsVisitor, HasRelationToVisitor,
|
||||
IsDisjointVisitor, IsEquivalentVisitor, NormalizedVisitor, Type, TypeMapping, TypeRelation,
|
||||
UnionBuilder, UnionType,
|
||||
UnionBuilder, UnionType, structural_type_ordering,
|
||||
};
|
||||
use crate::types::{Truthiness, TypeContext};
|
||||
use crate::{Db, FxOrderSet, Program};
|
||||
|
|
@ -302,6 +302,10 @@ impl<'db> TupleType<'db> {
|
|||
pub(crate) fn is_single_valued(self, db: &'db dyn Db) -> bool {
|
||||
self.tuple(db).is_single_valued(db)
|
||||
}
|
||||
|
||||
pub(super) fn structural_ordering(self, db: &'db dyn Db, other: Self) -> std::cmp::Ordering {
|
||||
self.tuple(db).structural_ordering(db, other.tuple(db))
|
||||
}
|
||||
}
|
||||
|
||||
fn to_class_type_cycle_initial<'db>(
|
||||
|
|
@ -583,6 +587,22 @@ impl<'db> FixedLengthTuple<Type<'db>> {
|
|||
fn is_single_valued(&self, db: &'db dyn Db) -> bool {
|
||||
self.0.iter().all(|ty| ty.is_single_valued(db))
|
||||
}
|
||||
|
||||
fn structural_ordering(&self, db: &'db dyn Db, other: &Self) -> std::cmp::Ordering {
|
||||
let len_count = self.0.len().cmp(&other.0.len());
|
||||
if len_count != std::cmp::Ordering::Equal {
|
||||
return len_count;
|
||||
}
|
||||
|
||||
for (left, right) in self.0.iter().zip(&other.0) {
|
||||
let ord = structural_type_ordering(db, left, right);
|
||||
if ord != std::cmp::Ordering::Equal {
|
||||
return ord;
|
||||
}
|
||||
}
|
||||
|
||||
std::cmp::Ordering::Equal
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> PyIndex<'db> for &FixedLengthTuple<Type<'db>> {
|
||||
|
|
@ -1096,6 +1116,32 @@ impl<'db> VariableLengthTuple<Type<'db>> {
|
|||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn structural_ordering(&self, db: &'db dyn Db, other: &Self) -> std::cmp::Ordering {
|
||||
let prefix_count = self.prefix.len().cmp(&other.prefix.len());
|
||||
if prefix_count != std::cmp::Ordering::Equal {
|
||||
return prefix_count;
|
||||
}
|
||||
let suffix_count = self.suffix.len().cmp(&other.suffix.len());
|
||||
if suffix_count != std::cmp::Ordering::Equal {
|
||||
return suffix_count;
|
||||
}
|
||||
|
||||
for (left, right) in self.prefix.iter().zip(&other.prefix) {
|
||||
let ord = structural_type_ordering(db, left, right);
|
||||
if ord != std::cmp::Ordering::Equal {
|
||||
return ord;
|
||||
}
|
||||
}
|
||||
for (left, right) in self.suffix.iter().zip(&other.suffix) {
|
||||
let ord = structural_type_ordering(db, left, right);
|
||||
if ord != std::cmp::Ordering::Equal {
|
||||
return ord;
|
||||
}
|
||||
}
|
||||
|
||||
structural_type_ordering(db, &self.variable, &other.variable)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> PyIndex<'db> for &VariableLengthTuple<Type<'db>> {
|
||||
|
|
@ -1470,6 +1516,19 @@ impl<'db> Tuple<Type<'db>> {
|
|||
int_instance_ty,
|
||||
])
|
||||
}
|
||||
|
||||
pub(super) fn structural_ordering(&self, db: &'db dyn Db, other: &Self) -> std::cmp::Ordering {
|
||||
match (self, other) {
|
||||
(Tuple::Fixed(self_tuple), Tuple::Fixed(other_tuple)) => {
|
||||
self_tuple.structural_ordering(db, other_tuple)
|
||||
}
|
||||
(Tuple::Variable(self_tuple), Tuple::Variable(other_tuple)) => {
|
||||
self_tuple.structural_ordering(db, other_tuple)
|
||||
}
|
||||
(Tuple::Fixed(_), Tuple::Variable(_)) => std::cmp::Ordering::Less,
|
||||
(Tuple::Variable(_), Tuple::Fixed(_)) => std::cmp::Ordering::Greater,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<FixedLengthTuple<T>> for Tuple<T> {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,262 @@ use super::{
|
|||
DynamicType, TodoType, Type, TypeIsType, class_base::ClassBase, subclass_of::SubclassOfInner,
|
||||
};
|
||||
|
||||
pub(super) fn structural_type_ordering<'db>(
|
||||
db: &'db dyn Db,
|
||||
left: &Type<'db>,
|
||||
right: &Type<'db>,
|
||||
) -> Ordering {
|
||||
match (left, right) {
|
||||
(Type::Never, _) => Ordering::Less,
|
||||
(_, Type::Never) => Ordering::Greater,
|
||||
|
||||
(Type::LiteralString, _) => Ordering::Less,
|
||||
(_, Type::LiteralString) => Ordering::Greater,
|
||||
|
||||
(Type::BooleanLiteral(left), Type::BooleanLiteral(right)) => left.cmp(right),
|
||||
(Type::BooleanLiteral(_), _) => Ordering::Less,
|
||||
(_, Type::BooleanLiteral(_)) => Ordering::Greater,
|
||||
|
||||
(Type::IntLiteral(left), Type::IntLiteral(right)) => left.cmp(right),
|
||||
(Type::IntLiteral(_), _) => Ordering::Less,
|
||||
(_, Type::IntLiteral(_)) => Ordering::Greater,
|
||||
|
||||
(Type::StringLiteral(left), Type::StringLiteral(right)) => {
|
||||
left.structural_ordering(db, *right)
|
||||
}
|
||||
(Type::StringLiteral(_), _) => Ordering::Less,
|
||||
(_, Type::StringLiteral(_)) => Ordering::Greater,
|
||||
|
||||
(Type::BytesLiteral(left), Type::BytesLiteral(right)) => {
|
||||
left.structural_ordering(db, *right)
|
||||
}
|
||||
(Type::BytesLiteral(_), _) => Ordering::Less,
|
||||
(_, Type::BytesLiteral(_)) => Ordering::Greater,
|
||||
|
||||
(Type::EnumLiteral(left), Type::EnumLiteral(right)) => left.structural_ordering(db, *right),
|
||||
(Type::EnumLiteral(_), _) => Ordering::Less,
|
||||
(_, Type::EnumLiteral(_)) => Ordering::Greater,
|
||||
|
||||
(Type::FunctionLiteral(left), Type::FunctionLiteral(right)) => {
|
||||
left.structural_ordering(db, *right)
|
||||
}
|
||||
(Type::FunctionLiteral(_), _) => Ordering::Less,
|
||||
(_, Type::FunctionLiteral(_)) => Ordering::Greater,
|
||||
|
||||
(Type::BoundMethod(left), Type::BoundMethod(right)) => left.structural_ordering(db, *right),
|
||||
(Type::BoundMethod(_), _) => Ordering::Less,
|
||||
(_, Type::BoundMethod(_)) => Ordering::Greater,
|
||||
|
||||
(Type::KnownBoundMethod(left), Type::KnownBoundMethod(right)) => {
|
||||
left.structural_ordering(db, *right)
|
||||
}
|
||||
(Type::KnownBoundMethod(_), _) => Ordering::Less,
|
||||
(_, Type::KnownBoundMethod(_)) => Ordering::Greater,
|
||||
|
||||
(Type::WrapperDescriptor(left), Type::WrapperDescriptor(right)) => left.cmp(right),
|
||||
(Type::WrapperDescriptor(_), _) => Ordering::Less,
|
||||
(_, Type::WrapperDescriptor(_)) => Ordering::Greater,
|
||||
|
||||
(Type::DataclassDecorator(left), Type::DataclassDecorator(right)) => {
|
||||
left.structural_ordering(db, *right)
|
||||
}
|
||||
(Type::DataclassDecorator(_), _) => Ordering::Less,
|
||||
(_, Type::DataclassDecorator(_)) => Ordering::Greater,
|
||||
|
||||
(Type::DataclassTransformer(left), Type::DataclassTransformer(right)) => left.cmp(right),
|
||||
(Type::DataclassTransformer(_), _) => Ordering::Less,
|
||||
(_, Type::DataclassTransformer(_)) => Ordering::Greater,
|
||||
|
||||
(Type::Callable(left), Type::Callable(right)) => left.structural_ordering(db, *right),
|
||||
(Type::Callable(_), _) => Ordering::Less,
|
||||
(_, Type::Callable(_)) => Ordering::Greater,
|
||||
|
||||
(Type::ModuleLiteral(left), Type::ModuleLiteral(right)) => {
|
||||
left.structural_ordering(db, *right)
|
||||
}
|
||||
(Type::ModuleLiteral(_), _) => Ordering::Less,
|
||||
(_, Type::ModuleLiteral(_)) => Ordering::Greater,
|
||||
|
||||
(Type::ClassLiteral(left), Type::ClassLiteral(right)) => {
|
||||
left.structural_ordering(db, *right)
|
||||
}
|
||||
(Type::ClassLiteral(_), _) => Ordering::Less,
|
||||
(_, Type::ClassLiteral(_)) => Ordering::Greater,
|
||||
|
||||
(Type::GenericAlias(left), Type::GenericAlias(right)) => {
|
||||
left.structural_ordering(db, *right)
|
||||
}
|
||||
(Type::GenericAlias(_), _) => Ordering::Less,
|
||||
(_, Type::GenericAlias(_)) => Ordering::Greater,
|
||||
|
||||
(Type::SubclassOf(left), Type::SubclassOf(right)) => {
|
||||
match (left.subclass_of(), right.subclass_of()) {
|
||||
(SubclassOfInner::Class(left), SubclassOfInner::Class(right)) => {
|
||||
left.structural_ordering(db, right)
|
||||
}
|
||||
(SubclassOfInner::Class(_), _) => Ordering::Less,
|
||||
(_, SubclassOfInner::Class(_)) => Ordering::Greater,
|
||||
(SubclassOfInner::Dynamic(left), SubclassOfInner::Dynamic(right)) => {
|
||||
dynamic_elements_ordering(left, right)
|
||||
}
|
||||
(SubclassOfInner::TypeVar(left), SubclassOfInner::TypeVar(right)) => {
|
||||
left.structural_ordering(db, right)
|
||||
}
|
||||
(SubclassOfInner::TypeVar(_), _) => Ordering::Less,
|
||||
(_, SubclassOfInner::TypeVar(_)) => Ordering::Greater,
|
||||
}
|
||||
}
|
||||
|
||||
(Type::SubclassOf(_), _) => Ordering::Less,
|
||||
(_, Type::SubclassOf(_)) => Ordering::Greater,
|
||||
|
||||
(Type::TypeIs(left), Type::TypeIs(right)) => left.structural_ordering(db, *right),
|
||||
(Type::TypeIs(_), _) => Ordering::Less,
|
||||
(_, Type::TypeIs(_)) => Ordering::Greater,
|
||||
|
||||
(Type::NominalInstance(left), Type::NominalInstance(right)) => {
|
||||
left.structural_ordering(db, *right)
|
||||
}
|
||||
(Type::NominalInstance(_), _) => Ordering::Less,
|
||||
(_, Type::NominalInstance(_)) => Ordering::Greater,
|
||||
|
||||
(Type::ProtocolInstance(left), Type::ProtocolInstance(right)) => {
|
||||
left.structural_ordering(db, *right)
|
||||
}
|
||||
(Type::ProtocolInstance(_), _) => Ordering::Less,
|
||||
(_, Type::ProtocolInstance(_)) => Ordering::Greater,
|
||||
|
||||
(Type::TypeVar(left), Type::TypeVar(right)) => left.structural_ordering(db, *right),
|
||||
(Type::TypeVar(_), _) => Ordering::Less,
|
||||
(_, Type::TypeVar(_)) => Ordering::Greater,
|
||||
|
||||
(Type::AlwaysTruthy, _) => Ordering::Less,
|
||||
(_, Type::AlwaysTruthy) => Ordering::Greater,
|
||||
|
||||
(Type::AlwaysFalsy, _) => Ordering::Less,
|
||||
(_, Type::AlwaysFalsy) => Ordering::Greater,
|
||||
|
||||
(Type::BoundSuper(left), Type::BoundSuper(right)) => {
|
||||
(match (left.pivot_class(db), right.pivot_class(db)) {
|
||||
(ClassBase::Class(left), ClassBase::Class(right)) => {
|
||||
left.structural_ordering(db, right)
|
||||
}
|
||||
(ClassBase::Class(_), _) => Ordering::Less,
|
||||
(_, ClassBase::Class(_)) => Ordering::Greater,
|
||||
|
||||
(ClassBase::Protocol, _) => Ordering::Less,
|
||||
(_, ClassBase::Protocol) => Ordering::Greater,
|
||||
|
||||
(ClassBase::Generic, _) => Ordering::Less,
|
||||
(_, ClassBase::Generic) => Ordering::Greater,
|
||||
|
||||
(ClassBase::TypedDict, _) => Ordering::Less,
|
||||
(_, ClassBase::TypedDict) => Ordering::Greater,
|
||||
|
||||
(ClassBase::Dynamic(left), ClassBase::Dynamic(right)) => {
|
||||
dynamic_elements_ordering(left, right)
|
||||
}
|
||||
})
|
||||
.then_with(|| match (left.owner(db), right.owner(db)) {
|
||||
(SuperOwnerKind::Class(left), SuperOwnerKind::Class(right)) => {
|
||||
left.structural_ordering(db, right)
|
||||
}
|
||||
(SuperOwnerKind::Class(_), _) => Ordering::Less,
|
||||
(_, SuperOwnerKind::Class(_)) => Ordering::Greater,
|
||||
(SuperOwnerKind::Instance(left), SuperOwnerKind::Instance(right)) => {
|
||||
left.structural_ordering(db, right)
|
||||
}
|
||||
(SuperOwnerKind::Instance(_), _) => Ordering::Less,
|
||||
(_, SuperOwnerKind::Instance(_)) => Ordering::Greater,
|
||||
(SuperOwnerKind::Dynamic(left), SuperOwnerKind::Dynamic(right)) => {
|
||||
dynamic_elements_ordering(left, right)
|
||||
}
|
||||
})
|
||||
}
|
||||
(Type::BoundSuper(_), _) => Ordering::Less,
|
||||
(_, Type::BoundSuper(_)) => Ordering::Greater,
|
||||
|
||||
(Type::SpecialForm(left), Type::SpecialForm(right)) => left.cmp(right),
|
||||
(Type::SpecialForm(_), _) => Ordering::Less,
|
||||
(_, Type::SpecialForm(_)) => Ordering::Greater,
|
||||
|
||||
(Type::KnownInstance(left), Type::KnownInstance(right)) => {
|
||||
left.structural_ordering(db, *right)
|
||||
}
|
||||
(Type::KnownInstance(_), _) => Ordering::Less,
|
||||
(_, Type::KnownInstance(_)) => Ordering::Greater,
|
||||
|
||||
(Type::PropertyInstance(left), Type::PropertyInstance(right)) => {
|
||||
left.structural_ordering(db, *right)
|
||||
}
|
||||
(Type::PropertyInstance(_), _) => Ordering::Less,
|
||||
(_, Type::PropertyInstance(_)) => Ordering::Greater,
|
||||
|
||||
(Type::Dynamic(left), Type::Dynamic(right)) => dynamic_elements_ordering(*left, *right),
|
||||
(Type::Dynamic(_), _) => Ordering::Less,
|
||||
(_, Type::Dynamic(_)) => Ordering::Greater,
|
||||
|
||||
(Type::TypeAlias(left), Type::TypeAlias(right)) => left.structural_ordering(db, *right),
|
||||
(Type::TypeAlias(_), _) => Ordering::Less,
|
||||
(_, Type::TypeAlias(_)) => Ordering::Greater,
|
||||
|
||||
(Type::TypedDict(left), Type::TypedDict(right)) => left
|
||||
.defining_class()
|
||||
.structural_ordering(db, right.defining_class()),
|
||||
(Type::TypedDict(_), _) => Ordering::Less,
|
||||
(_, Type::TypedDict(_)) => Ordering::Greater,
|
||||
|
||||
(Type::NewTypeInstance(left), Type::NewTypeInstance(right)) => {
|
||||
left.structural_ordering(db, *right)
|
||||
}
|
||||
(Type::NewTypeInstance(_), _) => Ordering::Less,
|
||||
(_, Type::NewTypeInstance(_)) => Ordering::Greater,
|
||||
|
||||
(Type::Union(left), Type::Union(right)) => {
|
||||
if left.elements(db).len() != right.elements(db).len() {
|
||||
return left.elements(db).len().cmp(&right.elements(db).len());
|
||||
}
|
||||
for (left, right) in left.elements(db).iter().zip(right.elements(db)) {
|
||||
let ordering = structural_type_ordering(db, left, right);
|
||||
if ordering != Ordering::Equal {
|
||||
return ordering;
|
||||
}
|
||||
}
|
||||
Ordering::Equal
|
||||
}
|
||||
(Type::Union(_), _) => Ordering::Less,
|
||||
(_, Type::Union(_)) => Ordering::Greater,
|
||||
|
||||
(Type::Intersection(left), Type::Intersection(right)) => {
|
||||
// Lexicographically compare the elements of the two unequal intersections.
|
||||
let left_positive = left.positive(db);
|
||||
let right_positive = right.positive(db);
|
||||
if left_positive.len() != right_positive.len() {
|
||||
return left_positive.len().cmp(&right_positive.len());
|
||||
}
|
||||
let left_negative = left.negative(db);
|
||||
let right_negative = right.negative(db);
|
||||
if left_negative.len() != right_negative.len() {
|
||||
return left_negative.len().cmp(&right_negative.len());
|
||||
}
|
||||
for (left, right) in left_positive.iter().zip(right_positive) {
|
||||
let ordering = structural_type_ordering(db, left, right);
|
||||
if ordering != Ordering::Equal {
|
||||
return ordering;
|
||||
}
|
||||
}
|
||||
for (left, right) in left_negative.iter().zip(right_negative) {
|
||||
let ordering = structural_type_ordering(db, left, right);
|
||||
if ordering != Ordering::Equal {
|
||||
return ordering;
|
||||
}
|
||||
}
|
||||
|
||||
Ordering::Equal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return an [`Ordering`] that describes the canonical order in which two types should appear
|
||||
/// in an [`crate::types::IntersectionType`] or a [`crate::types::UnionType`] in order for them
|
||||
/// to be compared for equivalence.
|
||||
|
|
@ -26,22 +282,17 @@ pub(super) fn union_or_intersection_elements_ordering<'db>(
|
|||
db: &'db dyn Db,
|
||||
left: &Type<'db>,
|
||||
right: &Type<'db>,
|
||||
cycle_recovery: bool,
|
||||
) -> Ordering {
|
||||
// If we sort union types in a cycle recovery function, this check is not necessary
|
||||
// because the purpose is to stabilize the output and the sort order itself is not important.
|
||||
if !cycle_recovery {
|
||||
debug_assert_eq!(
|
||||
*left,
|
||||
left.normalized(db),
|
||||
"`left` must be normalized before a meaningful ordering can be established"
|
||||
);
|
||||
debug_assert_eq!(
|
||||
*right,
|
||||
right.normalized(db),
|
||||
"`right` must be normalized before a meaningful ordering can be established"
|
||||
);
|
||||
}
|
||||
debug_assert_eq!(
|
||||
*left,
|
||||
left.normalized(db),
|
||||
"`left` must be normalized before a meaningful ordering can be established"
|
||||
);
|
||||
debug_assert_eq!(
|
||||
*right,
|
||||
right.normalized(db),
|
||||
"`right` must be normalized before a meaningful ordering can be established"
|
||||
);
|
||||
|
||||
if left == right {
|
||||
return Ordering::Equal;
|
||||
|
|
@ -133,9 +384,7 @@ pub(super) fn union_or_intersection_elements_ordering<'db>(
|
|||
(Type::SubclassOf(_), _) => Ordering::Less,
|
||||
(_, Type::SubclassOf(_)) => Ordering::Greater,
|
||||
|
||||
(Type::TypeIs(left), Type::TypeIs(right)) => {
|
||||
typeis_ordering(db, *left, *right, cycle_recovery)
|
||||
}
|
||||
(Type::TypeIs(left), Type::TypeIs(right)) => typeis_ordering(db, *left, *right),
|
||||
(Type::TypeIs(_), _) => Ordering::Less,
|
||||
(_, Type::TypeIs(_)) => Ordering::Greater,
|
||||
|
||||
|
|
@ -246,15 +495,13 @@ pub(super) fn union_or_intersection_elements_ordering<'db>(
|
|||
return left_negative.len().cmp(&right_negative.len());
|
||||
}
|
||||
for (left, right) in left_positive.iter().zip(right_positive) {
|
||||
let ordering =
|
||||
union_or_intersection_elements_ordering(db, left, right, cycle_recovery);
|
||||
let ordering = union_or_intersection_elements_ordering(db, left, right);
|
||||
if ordering != Ordering::Equal {
|
||||
return ordering;
|
||||
}
|
||||
}
|
||||
for (left, right) in left_negative.iter().zip(right_negative) {
|
||||
let ordering =
|
||||
union_or_intersection_elements_ordering(db, left, right, cycle_recovery);
|
||||
let ordering = union_or_intersection_elements_ordering(db, left, right);
|
||||
if ordering != Ordering::Equal {
|
||||
return ordering;
|
||||
}
|
||||
|
|
@ -295,26 +542,17 @@ fn dynamic_elements_ordering(left: DynamicType, right: DynamicType) -> Ordering
|
|||
/// * Boundness: Unbound precedes bound
|
||||
/// * Symbol name: String comparison
|
||||
/// * Guarded type: [`union_or_intersection_elements_ordering`]
|
||||
fn typeis_ordering(
|
||||
db: &dyn Db,
|
||||
left: TypeIsType,
|
||||
right: TypeIsType,
|
||||
cycle_recovery: bool,
|
||||
) -> Ordering {
|
||||
fn typeis_ordering(db: &dyn Db, left: TypeIsType, right: TypeIsType) -> Ordering {
|
||||
let (left_ty, right_ty) = (left.return_type(db), right.return_type(db));
|
||||
|
||||
match (left.place_info(db), right.place_info(db)) {
|
||||
(None, Some(_)) => Ordering::Less,
|
||||
(Some(_), None) => Ordering::Greater,
|
||||
|
||||
(None, None) => {
|
||||
union_or_intersection_elements_ordering(db, &left_ty, &right_ty, cycle_recovery)
|
||||
}
|
||||
(None, None) => union_or_intersection_elements_ordering(db, &left_ty, &right_ty),
|
||||
|
||||
(Some(_), Some(_)) => match left.place_name(db).cmp(&right.place_name(db)) {
|
||||
Ordering::Equal => {
|
||||
union_or_intersection_elements_ordering(db, &left_ty, &right_ty, cycle_recovery)
|
||||
}
|
||||
Ordering::Equal => union_or_intersection_elements_ordering(db, &left_ty, &right_ty),
|
||||
ordering => ordering,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue