mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 10:48:32 +00:00
[ty] Normalize recursive types using Any (#19003)
## Summary This just replaces one temporary solution to recursive protocols (the `SelfReference` mechanism) with another one (track seen types when recursively descending in `normalize` and replace recursive references with `Any`). But this temporary solution can handle mutually-recursive types, not just self-referential ones, and it's sufficient for the primer ecosystem and some other projects we are testing on to no longer stack overflow. The follow-up here will be to properly handle these self-references instead of replacing them with `Any`. We will also eventually need cycle detection on more recursive-descent type transformations and tests. ## Test Plan Existing tests (including recursive-protocol tests) and primer. Added mdtest for mutually-recursive protocols that stack-overflowed before this PR.
This commit is contained in:
parent
34052a1185
commit
2ae0bd9464
14 changed files with 356 additions and 368 deletions
|
@ -1729,6 +1729,21 @@ def _(r: Recursive):
|
|||
reveal_type(r.method(r).callable1(1).direct.t[1][1]) # revealed: Recursive
|
||||
```
|
||||
|
||||
### Mutually-recursive protocols
|
||||
|
||||
```py
|
||||
from typing import Protocol
|
||||
from ty_extensions import is_equivalent_to, static_assert
|
||||
|
||||
class Foo(Protocol):
|
||||
x: "Bar"
|
||||
|
||||
class Bar(Protocol):
|
||||
x: Foo
|
||||
|
||||
static_assert(is_equivalent_to(Foo, Bar))
|
||||
```
|
||||
|
||||
### Regression test: narrowing with self-referential protocols
|
||||
|
||||
This snippet caused us to panic on an early version of the implementation for protocols.
|
||||
|
|
|
@ -10,7 +10,7 @@ jax # too many iterations
|
|||
mypy # too many iterations (self-recursive type alias)
|
||||
packaging # too many iterations
|
||||
pandas # slow (9s)
|
||||
pandera # stack overflow
|
||||
pandera # too many iterations
|
||||
pip # vendors packaging, see above
|
||||
pylint # cycle panics (self-recursive type alias)
|
||||
pyodide # too many cycle iterations
|
||||
|
@ -19,5 +19,4 @@ setuptools # vendors packaging, see above
|
|||
spack # slow, success, but mypy-primer hangs processing the output
|
||||
spark # too many iterations
|
||||
steam.py # hangs (single threaded)
|
||||
tornado # bad use-def map (https://github.com/astral-sh/ty/issues/365)
|
||||
xarray # too many iterations
|
||||
|
|
|
@ -110,6 +110,7 @@ strawberry
|
|||
streamlit
|
||||
svcs
|
||||
sympy
|
||||
tornado
|
||||
trio
|
||||
twine
|
||||
typeshed-stats
|
||||
|
|
|
@ -21,6 +21,7 @@ use ruff_text_size::{Ranged, TextRange};
|
|||
use type_ordering::union_or_intersection_elements_ordering;
|
||||
|
||||
pub(crate) use self::builder::{IntersectionBuilder, UnionBuilder};
|
||||
pub(crate) use self::cyclic::TypeVisitor;
|
||||
pub use self::diagnostic::TypeCheckDiagnostics;
|
||||
pub(crate) use self::diagnostic::register_lints;
|
||||
pub(crate) use self::infer::{
|
||||
|
@ -63,6 +64,7 @@ mod call;
|
|||
mod class;
|
||||
mod class_base;
|
||||
mod context;
|
||||
mod cyclic;
|
||||
mod diagnostic;
|
||||
mod display;
|
||||
mod function;
|
||||
|
@ -384,11 +386,11 @@ impl<'db> PropertyInstanceType<'db> {
|
|||
Self::new(db, getter, setter)
|
||||
}
|
||||
|
||||
fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
Self::new(
|
||||
db,
|
||||
self.getter(db).map(|ty| ty.normalized(db)),
|
||||
self.setter(db).map(|ty| ty.normalized(db)),
|
||||
self.getter(db).map(|ty| ty.normalized_impl(db, visitor)),
|
||||
self.setter(db).map(|ty| ty.normalized_impl(db, visitor)),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -572,9 +574,9 @@ pub enum Type<'db> {
|
|||
/// An instance of a typevar in a generic class or function. When the generic class or function
|
||||
/// is specialized, we will replace this typevar with its specialization.
|
||||
TypeVar(TypeVarInstance<'db>),
|
||||
// A bound super object like `super()` or `super(A, A())`
|
||||
// This type doesn't handle an unbound super object like `super(A)`; for that we just use
|
||||
// a `Type::NominalInstance` of `builtins.super`.
|
||||
/// A bound super object like `super()` or `super(A, A())`
|
||||
/// This type doesn't handle an unbound super object like `super(A)`; for that we just use
|
||||
/// a `Type::NominalInstance` of `builtins.super`.
|
||||
BoundSuper(BoundSuperType<'db>),
|
||||
/// A subtype of `bool` that allows narrowing in both positive and negative cases.
|
||||
TypeIs(TypeIsType<'db>),
|
||||
|
@ -753,85 +755,6 @@ impl<'db> Type<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Replace references to the class `class` with a self-reference marker. This is currently
|
||||
/// used for recursive protocols, but could probably be extended to self-referential type-
|
||||
/// aliases and similar.
|
||||
#[must_use]
|
||||
pub fn replace_self_reference(&self, db: &'db dyn Db, class: ClassLiteral<'db>) -> Type<'db> {
|
||||
match self {
|
||||
Self::ProtocolInstance(protocol) => {
|
||||
Self::ProtocolInstance(protocol.replace_self_reference(db, class))
|
||||
}
|
||||
|
||||
Self::Union(union) => UnionType::from_elements(
|
||||
db,
|
||||
union
|
||||
.elements(db)
|
||||
.iter()
|
||||
.map(|ty| ty.replace_self_reference(db, class)),
|
||||
),
|
||||
|
||||
Self::Intersection(intersection) => IntersectionBuilder::new(db)
|
||||
.positive_elements(
|
||||
intersection
|
||||
.positive(db)
|
||||
.iter()
|
||||
.map(|ty| ty.replace_self_reference(db, class)),
|
||||
)
|
||||
.negative_elements(
|
||||
intersection
|
||||
.negative(db)
|
||||
.iter()
|
||||
.map(|ty| ty.replace_self_reference(db, class)),
|
||||
)
|
||||
.build(),
|
||||
|
||||
Self::Tuple(tuple) => TupleType::from_elements(
|
||||
db,
|
||||
tuple
|
||||
.tuple(db)
|
||||
.all_elements()
|
||||
.map(|ty| ty.replace_self_reference(db, class)),
|
||||
),
|
||||
|
||||
Self::Callable(callable) => Self::Callable(callable.replace_self_reference(db, class)),
|
||||
|
||||
Self::GenericAlias(_) | Self::TypeVar(_) => {
|
||||
// TODO: replace self-references in generic aliases and typevars
|
||||
*self
|
||||
}
|
||||
|
||||
Self::TypeIs(type_is) => type_is.with_type(
|
||||
db,
|
||||
type_is.return_type(db).replace_self_reference(db, class),
|
||||
),
|
||||
|
||||
Self::Dynamic(_)
|
||||
| Self::AlwaysFalsy
|
||||
| Self::AlwaysTruthy
|
||||
| Self::Never
|
||||
| Self::BooleanLiteral(_)
|
||||
| Self::BytesLiteral(_)
|
||||
| Self::StringLiteral(_)
|
||||
| Self::IntLiteral(_)
|
||||
| Self::LiteralString
|
||||
| Self::FunctionLiteral(_)
|
||||
| Self::ModuleLiteral(_)
|
||||
| Self::ClassLiteral(_)
|
||||
| Self::NominalInstance(_)
|
||||
| Self::SpecialForm(_)
|
||||
| Self::KnownInstance(_)
|
||||
| Self::PropertyInstance(_)
|
||||
| Self::BoundMethod(_)
|
||||
| Self::WrapperDescriptor(_)
|
||||
| Self::MethodWrapper(_)
|
||||
| Self::DataclassDecorator(_)
|
||||
| Self::DataclassTransformer(_)
|
||||
| Self::SubclassOf(_)
|
||||
| Self::BoundSuper(_) => *self,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return `true` if `self`, or any of the types contained in `self`, match the closure passed in.
|
||||
pub fn any_over_type(self, db: &'db dyn Db, type_fn: &dyn Fn(Type<'db>) -> bool) -> bool {
|
||||
if type_fn(self) {
|
||||
|
@ -1149,26 +1072,62 @@ impl<'db> Type<'db> {
|
|||
/// - Converts class-based protocols into synthesized protocols
|
||||
#[must_use]
|
||||
pub fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
let mut visitor = TypeVisitor::default();
|
||||
self.normalized_impl(db, &mut visitor)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
match self {
|
||||
Type::Union(union) => Type::Union(union.normalized(db)),
|
||||
Type::Intersection(intersection) => Type::Intersection(intersection.normalized(db)),
|
||||
Type::Tuple(tuple) => Type::tuple(tuple.normalized(db)),
|
||||
Type::Callable(callable) => Type::Callable(callable.normalized(db)),
|
||||
Type::ProtocolInstance(protocol) => protocol.normalized(db),
|
||||
Type::NominalInstance(instance) => Type::NominalInstance(instance.normalized(db)),
|
||||
Type::Dynamic(dynamic) => Type::Dynamic(dynamic.normalized()),
|
||||
Type::FunctionLiteral(function) => Type::FunctionLiteral(function.normalized(db)),
|
||||
Type::PropertyInstance(property) => Type::PropertyInstance(property.normalized(db)),
|
||||
Type::MethodWrapper(method_kind) => Type::MethodWrapper(method_kind.normalized(db)),
|
||||
Type::BoundMethod(method) => Type::BoundMethod(method.normalized(db)),
|
||||
Type::BoundSuper(bound_super) => Type::BoundSuper(bound_super.normalized(db)),
|
||||
Type::GenericAlias(generic) => Type::GenericAlias(generic.normalized(db)),
|
||||
Type::SubclassOf(subclass_of) => Type::SubclassOf(subclass_of.normalized(db)),
|
||||
Type::TypeVar(typevar) => Type::TypeVar(typevar.normalized(db)),
|
||||
Type::KnownInstance(known_instance) => {
|
||||
Type::KnownInstance(known_instance.normalized(db))
|
||||
Type::Union(union) => {
|
||||
visitor.visit(self, |v| Type::Union(union.normalized_impl(db, v)))
|
||||
}
|
||||
Type::TypeIs(type_is) => type_is.with_type(db, type_is.return_type(db).normalized(db)),
|
||||
Type::Intersection(intersection) => visitor.visit(self, |v| {
|
||||
Type::Intersection(intersection.normalized_impl(db, v))
|
||||
}),
|
||||
Type::Tuple(tuple) => {
|
||||
visitor.visit(self, |v| Type::tuple(tuple.normalized_impl(db, v)))
|
||||
}
|
||||
Type::Callable(callable) => {
|
||||
visitor.visit(self, |v| Type::Callable(callable.normalized_impl(db, v)))
|
||||
}
|
||||
Type::ProtocolInstance(protocol) => {
|
||||
visitor.visit(self, |v| protocol.normalized_impl(db, v))
|
||||
}
|
||||
Type::NominalInstance(instance) => visitor.visit(self, |v| {
|
||||
Type::NominalInstance(instance.normalized_impl(db, v))
|
||||
}),
|
||||
Type::FunctionLiteral(function) => visitor.visit(self, |v| {
|
||||
Type::FunctionLiteral(function.normalized_impl(db, v))
|
||||
}),
|
||||
Type::PropertyInstance(property) => visitor.visit(self, |v| {
|
||||
Type::PropertyInstance(property.normalized_impl(db, v))
|
||||
}),
|
||||
Type::MethodWrapper(method_kind) => visitor.visit(self, |v| {
|
||||
Type::MethodWrapper(method_kind.normalized_impl(db, v))
|
||||
}),
|
||||
Type::BoundMethod(method) => {
|
||||
visitor.visit(self, |v| Type::BoundMethod(method.normalized_impl(db, v)))
|
||||
}
|
||||
Type::BoundSuper(bound_super) => visitor.visit(self, |v| {
|
||||
Type::BoundSuper(bound_super.normalized_impl(db, v))
|
||||
}),
|
||||
Type::GenericAlias(generic) => {
|
||||
visitor.visit(self, |v| Type::GenericAlias(generic.normalized_impl(db, v)))
|
||||
}
|
||||
Type::SubclassOf(subclass_of) => visitor.visit(self, |v| {
|
||||
Type::SubclassOf(subclass_of.normalized_impl(db, v))
|
||||
}),
|
||||
Type::TypeVar(typevar) => {
|
||||
visitor.visit(self, |v| Type::TypeVar(typevar.normalized_impl(db, v)))
|
||||
}
|
||||
Type::KnownInstance(known_instance) => visitor.visit(self, |v| {
|
||||
Type::KnownInstance(known_instance.normalized_impl(db, v))
|
||||
}),
|
||||
Type::TypeIs(type_is) => visitor.visit(self, |v| {
|
||||
type_is.with_type(db, type_is.return_type(db).normalized_impl(db, v))
|
||||
}),
|
||||
Type::Dynamic(dynamic) => Type::Dynamic(dynamic.normalized()),
|
||||
Type::LiteralString
|
||||
| Type::AlwaysFalsy
|
||||
| Type::AlwaysTruthy
|
||||
|
@ -5743,13 +5702,13 @@ impl<'db> TypeMapping<'_, 'db> {
|
|||
}
|
||||
}
|
||||
|
||||
fn normalized(&self, db: &'db dyn Db) -> Self {
|
||||
fn normalized_impl(&self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
match self {
|
||||
TypeMapping::Specialization(specialization) => {
|
||||
TypeMapping::Specialization(specialization.normalized(db))
|
||||
TypeMapping::Specialization(specialization.normalized_impl(db, visitor))
|
||||
}
|
||||
TypeMapping::PartialSpecialization(partial) => {
|
||||
TypeMapping::PartialSpecialization(partial.normalized(db))
|
||||
TypeMapping::PartialSpecialization(partial.normalized_impl(db, visitor))
|
||||
}
|
||||
TypeMapping::PromoteLiterals => TypeMapping::PromoteLiterals,
|
||||
}
|
||||
|
@ -5795,12 +5754,18 @@ pub enum KnownInstanceType<'db> {
|
|||
}
|
||||
|
||||
impl<'db> KnownInstanceType<'db> {
|
||||
fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
match self {
|
||||
Self::SubscriptedProtocol(context) => Self::SubscriptedProtocol(context.normalized(db)),
|
||||
Self::SubscriptedGeneric(context) => Self::SubscriptedGeneric(context.normalized(db)),
|
||||
Self::TypeVar(typevar) => Self::TypeVar(typevar.normalized(db)),
|
||||
Self::TypeAliasType(type_alias) => Self::TypeAliasType(type_alias.normalized(db)),
|
||||
Self::SubscriptedProtocol(context) => {
|
||||
Self::SubscriptedProtocol(context.normalized_impl(db, visitor))
|
||||
}
|
||||
Self::SubscriptedGeneric(context) => {
|
||||
Self::SubscriptedGeneric(context.normalized_impl(db, visitor))
|
||||
}
|
||||
Self::TypeVar(typevar) => Self::TypeVar(typevar.normalized_impl(db, visitor)),
|
||||
Self::TypeAliasType(type_alias) => {
|
||||
Self::TypeAliasType(type_alias.normalized_impl(db, visitor))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6181,14 +6146,15 @@ impl<'db> TypeVarInstance<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
Self::new(
|
||||
db,
|
||||
self.name(db),
|
||||
self.definition(db),
|
||||
self.bound_or_constraints(db).map(|b| b.normalized(db)),
|
||||
self.bound_or_constraints(db)
|
||||
.map(|b| b.normalized_impl(db, visitor)),
|
||||
self.variance(db),
|
||||
self.default_ty(db).map(|d| d.normalized(db)),
|
||||
self.default_ty(db).map(|d| d.normalized_impl(db, visitor)),
|
||||
self.kind(db),
|
||||
)
|
||||
}
|
||||
|
@ -6236,13 +6202,13 @@ pub enum TypeVarBoundOrConstraints<'db> {
|
|||
}
|
||||
|
||||
impl<'db> TypeVarBoundOrConstraints<'db> {
|
||||
fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
match self {
|
||||
TypeVarBoundOrConstraints::UpperBound(bound) => {
|
||||
TypeVarBoundOrConstraints::UpperBound(bound.normalized(db))
|
||||
TypeVarBoundOrConstraints::UpperBound(bound.normalized_impl(db, visitor))
|
||||
}
|
||||
TypeVarBoundOrConstraints::Constraints(constraints) => {
|
||||
TypeVarBoundOrConstraints::Constraints(constraints.normalized(db))
|
||||
TypeVarBoundOrConstraints::Constraints(constraints.normalized_impl(db, visitor))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7154,11 +7120,11 @@ impl<'db> BoundMethodType<'db> {
|
|||
))
|
||||
}
|
||||
|
||||
fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
Self::new(
|
||||
db,
|
||||
self.function(db).normalized(db),
|
||||
self.self_instance(db).normalized(db),
|
||||
self.function(db).normalized_impl(db, visitor),
|
||||
self.self_instance(db).normalized_impl(db, visitor),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -7261,10 +7227,10 @@ impl<'db> CallableType<'db> {
|
|||
/// Return a "normalized" version of this `Callable` type.
|
||||
///
|
||||
/// See [`Type::normalized`] for more details.
|
||||
fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
CallableType::new(
|
||||
db,
|
||||
self.signatures(db).normalized(db),
|
||||
self.signatures(db).normalized_impl(db, visitor),
|
||||
self.is_function_like(db),
|
||||
)
|
||||
}
|
||||
|
@ -7305,15 +7271,6 @@ impl<'db> CallableType<'db> {
|
|||
.signatures(db)
|
||||
.is_equivalent_to(db, other.signatures(db))
|
||||
}
|
||||
|
||||
/// See [`Type::replace_self_reference`].
|
||||
fn replace_self_reference(self, db: &'db dyn Db, class: ClassLiteral<'db>) -> Self {
|
||||
CallableType::new(
|
||||
db,
|
||||
self.signatures(db).replace_self_reference(db, class),
|
||||
self.is_function_like(db),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a specific instance of `types.MethodWrapperType`
|
||||
|
@ -7404,19 +7361,19 @@ impl<'db> MethodWrapperKind<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
match self {
|
||||
MethodWrapperKind::FunctionTypeDunderGet(function) => {
|
||||
MethodWrapperKind::FunctionTypeDunderGet(function.normalized(db))
|
||||
MethodWrapperKind::FunctionTypeDunderGet(function.normalized_impl(db, visitor))
|
||||
}
|
||||
MethodWrapperKind::FunctionTypeDunderCall(function) => {
|
||||
MethodWrapperKind::FunctionTypeDunderCall(function.normalized(db))
|
||||
MethodWrapperKind::FunctionTypeDunderCall(function.normalized_impl(db, visitor))
|
||||
}
|
||||
MethodWrapperKind::PropertyDunderGet(property) => {
|
||||
MethodWrapperKind::PropertyDunderGet(property.normalized(db))
|
||||
MethodWrapperKind::PropertyDunderGet(property.normalized_impl(db, visitor))
|
||||
}
|
||||
MethodWrapperKind::PropertyDunderSet(property) => {
|
||||
MethodWrapperKind::PropertyDunderSet(property.normalized(db))
|
||||
MethodWrapperKind::PropertyDunderSet(property.normalized_impl(db, visitor))
|
||||
}
|
||||
MethodWrapperKind::StrStartswith(_) => self,
|
||||
}
|
||||
|
@ -7530,7 +7487,7 @@ impl<'db> PEP695TypeAliasType<'db> {
|
|||
definition_expression_type(db, definition, &type_alias_stmt_node.value)
|
||||
}
|
||||
|
||||
fn normalized(self, _db: &'db dyn Db) -> Self {
|
||||
fn normalized_impl(self, _db: &'db dyn Db, _visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -7551,12 +7508,12 @@ pub struct BareTypeAliasType<'db> {
|
|||
impl get_size2::GetSize for BareTypeAliasType<'_> {}
|
||||
|
||||
impl<'db> BareTypeAliasType<'db> {
|
||||
fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
Self::new(
|
||||
db,
|
||||
self.name(db),
|
||||
self.definition(db),
|
||||
self.value(db).normalized(db),
|
||||
self.value(db).normalized_impl(db, visitor),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -7570,10 +7527,14 @@ pub enum TypeAliasType<'db> {
|
|||
}
|
||||
|
||||
impl<'db> TypeAliasType<'db> {
|
||||
pub(crate) fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
match self {
|
||||
TypeAliasType::PEP695(type_alias) => TypeAliasType::PEP695(type_alias.normalized(db)),
|
||||
TypeAliasType::Bare(type_alias) => TypeAliasType::Bare(type_alias.normalized(db)),
|
||||
TypeAliasType::PEP695(type_alias) => {
|
||||
TypeAliasType::PEP695(type_alias.normalized_impl(db, visitor))
|
||||
}
|
||||
TypeAliasType::Bare(type_alias) => {
|
||||
TypeAliasType::Bare(type_alias.normalized_impl(db, visitor))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7782,10 +7743,14 @@ impl<'db> UnionType<'db> {
|
|||
/// See [`Type::normalized`] for more details.
|
||||
#[must_use]
|
||||
pub(crate) fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
self.normalized_impl(db, &mut TypeVisitor::default())
|
||||
}
|
||||
|
||||
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
let mut new_elements: Vec<Type<'db>> = self
|
||||
.elements(db)
|
||||
.iter()
|
||||
.map(|element| element.normalized(db))
|
||||
.map(|element| element.normalized_impl(db, visitor))
|
||||
.collect();
|
||||
new_elements.sort_unstable_by(|l, r| union_or_intersection_elements_ordering(db, l, r));
|
||||
UnionType::new(db, new_elements.into_boxed_slice())
|
||||
|
@ -7839,12 +7804,20 @@ impl<'db> IntersectionType<'db> {
|
|||
/// See [`Type::normalized`] for more details.
|
||||
#[must_use]
|
||||
pub(crate) fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
let mut visitor = TypeVisitor::default();
|
||||
self.normalized_impl(db, &mut visitor)
|
||||
}
|
||||
|
||||
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
fn normalized_set<'db>(
|
||||
db: &'db dyn Db,
|
||||
elements: &FxOrderSet<Type<'db>>,
|
||||
visitor: &mut TypeVisitor<'db>,
|
||||
) -> FxOrderSet<Type<'db>> {
|
||||
let mut elements: FxOrderSet<Type<'db>> =
|
||||
elements.iter().map(|ty| ty.normalized(db)).collect();
|
||||
let mut elements: FxOrderSet<Type<'db>> = elements
|
||||
.iter()
|
||||
.map(|ty| ty.normalized_impl(db, visitor))
|
||||
.collect();
|
||||
|
||||
elements.sort_unstable_by(|l, r| union_or_intersection_elements_ordering(db, l, r));
|
||||
elements
|
||||
|
@ -7852,8 +7825,8 @@ impl<'db> IntersectionType<'db> {
|
|||
|
||||
IntersectionType::new(
|
||||
db,
|
||||
normalized_set(db, self.positive(db)),
|
||||
normalized_set(db, self.negative(db)),
|
||||
normalized_set(db, self.positive(db), visitor),
|
||||
normalized_set(db, self.negative(db), visitor),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -8092,11 +8065,15 @@ pub enum SuperOwnerKind<'db> {
|
|||
}
|
||||
|
||||
impl<'db> SuperOwnerKind<'db> {
|
||||
fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
match self {
|
||||
SuperOwnerKind::Dynamic(dynamic) => SuperOwnerKind::Dynamic(dynamic.normalized()),
|
||||
SuperOwnerKind::Class(class) => SuperOwnerKind::Class(class.normalized(db)),
|
||||
SuperOwnerKind::Instance(instance) => SuperOwnerKind::Instance(instance.normalized(db)),
|
||||
SuperOwnerKind::Class(class) => {
|
||||
SuperOwnerKind::Class(class.normalized_impl(db, visitor))
|
||||
}
|
||||
SuperOwnerKind::Instance(instance) => {
|
||||
SuperOwnerKind::Instance(instance.normalized_impl(db, visitor))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8337,11 +8314,11 @@ impl<'db> BoundSuperType<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
pub(super) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
Self::new(
|
||||
db,
|
||||
self.pivot_class(db).normalized(db),
|
||||
self.owner(db).normalized(db),
|
||||
self.pivot_class(db).normalized_impl(db, visitor),
|
||||
self.owner(db).normalized_impl(db, visitor),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ use crate::types::tuple::TupleType;
|
|||
use crate::types::{
|
||||
BareTypeAliasType, Binding, BoundSuperError, BoundSuperType, CallableType, DataclassParams,
|
||||
KnownInstanceType, TypeAliasType, TypeMapping, TypeRelation, TypeVarBoundOrConstraints,
|
||||
TypeVarInstance, TypeVarKind, infer_definition_types,
|
||||
TypeVarInstance, TypeVarKind, TypeVisitor, infer_definition_types,
|
||||
};
|
||||
use crate::{
|
||||
Db, FxOrderSet, KnownModule, Program,
|
||||
|
@ -182,8 +182,12 @@ pub struct GenericAlias<'db> {
|
|||
impl get_size2::GetSize for GenericAlias<'_> {}
|
||||
|
||||
impl<'db> GenericAlias<'db> {
|
||||
pub(super) fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
Self::new(db, self.origin(db), self.specialization(db).normalized(db))
|
||||
pub(super) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
Self::new(
|
||||
db,
|
||||
self.origin(db),
|
||||
self.specialization(db).normalized_impl(db, visitor),
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn materialize(self, db: &'db dyn Db, variance: TypeVarVariance) -> Self {
|
||||
|
@ -249,10 +253,10 @@ pub enum ClassType<'db> {
|
|||
|
||||
#[salsa::tracked]
|
||||
impl<'db> ClassType<'db> {
|
||||
pub(super) fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
pub(super) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
match self {
|
||||
Self::NonGeneric(_) => self,
|
||||
Self::Generic(generic) => Self::Generic(generic.normalized(db)),
|
||||
Self::Generic(generic) => Self::Generic(generic.normalized_impl(db, visitor)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::Db;
|
|||
use crate::types::generics::Specialization;
|
||||
use crate::types::{
|
||||
ClassType, DynamicType, KnownClass, KnownInstanceType, MroError, MroIterator, SpecialFormType,
|
||||
Type, TypeMapping, todo_type,
|
||||
Type, TypeMapping, TypeVisitor, todo_type,
|
||||
};
|
||||
|
||||
/// Enumeration of the possible kinds of types we allow in class bases.
|
||||
|
@ -31,10 +31,10 @@ impl<'db> ClassBase<'db> {
|
|||
Self::Dynamic(DynamicType::Unknown)
|
||||
}
|
||||
|
||||
pub(crate) fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
match self {
|
||||
Self::Dynamic(dynamic) => Self::Dynamic(dynamic.normalized()),
|
||||
Self::Class(class) => Self::Class(class.normalized(db)),
|
||||
Self::Class(class) => Self::Class(class.normalized_impl(db, visitor)),
|
||||
Self::Protocol | Self::Generic => self,
|
||||
}
|
||||
}
|
||||
|
|
26
crates/ty_python_semantic/src/types/cyclic.rs
Normal file
26
crates/ty_python_semantic/src/types/cyclic.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
use crate::FxOrderSet;
|
||||
use crate::types::Type;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct TypeVisitor<'db> {
|
||||
seen: FxOrderSet<Type<'db>>,
|
||||
}
|
||||
|
||||
impl<'db> TypeVisitor<'db> {
|
||||
pub(crate) fn visit(
|
||||
&mut self,
|
||||
ty: Type<'db>,
|
||||
func: impl FnOnce(&mut Self) -> Type<'db>,
|
||||
) -> Type<'db> {
|
||||
if !self.seen.insert(ty) {
|
||||
// TODO: proper recursive type handling
|
||||
|
||||
// This must be Any, not e.g. a todo type, because Any is the normalized form of the
|
||||
// dynamic type (that is, todo types are normalized to Any).
|
||||
return Type::any();
|
||||
}
|
||||
let ret = func(self);
|
||||
self.seen.pop();
|
||||
ret
|
||||
}
|
||||
}
|
|
@ -75,7 +75,7 @@ use crate::types::narrow::ClassInfoConstraintFunction;
|
|||
use crate::types::signatures::{CallableSignature, Signature};
|
||||
use crate::types::{
|
||||
BoundMethodType, CallableType, DynamicType, KnownClass, Type, TypeMapping, TypeRelation,
|
||||
TypeVarInstance,
|
||||
TypeVarInstance, TypeVisitor,
|
||||
};
|
||||
use crate::{Db, FxOrderSet, ModuleName, resolve_module};
|
||||
|
||||
|
@ -545,10 +545,10 @@ impl<'db> FunctionLiteral<'db> {
|
|||
}))
|
||||
}
|
||||
|
||||
fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
let context = self
|
||||
.inherited_generic_context(db)
|
||||
.map(|ctx| ctx.normalized(db));
|
||||
.map(|ctx| ctx.normalized_impl(db, visitor));
|
||||
Self::new(db, self.last_definition(db), context)
|
||||
}
|
||||
}
|
||||
|
@ -819,12 +819,17 @@ impl<'db> FunctionType<'db> {
|
|||
}
|
||||
|
||||
pub(crate) fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
let mut visitor = TypeVisitor::default();
|
||||
self.normalized_impl(db, &mut visitor)
|
||||
}
|
||||
|
||||
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
let mappings: Box<_> = self
|
||||
.type_mappings(db)
|
||||
.iter()
|
||||
.map(|mapping| mapping.normalized(db))
|
||||
.map(|mapping| mapping.normalized_impl(db, visitor))
|
||||
.collect();
|
||||
Self::new(db, self.literal(db).normalized(db), mappings)
|
||||
Self::new(db, self.literal(db).normalized_impl(db, visitor), mappings)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ use crate::types::signatures::{Parameter, Parameters, Signature};
|
|||
use crate::types::tuple::{TupleSpec, TupleType};
|
||||
use crate::types::{
|
||||
KnownInstanceType, Type, TypeMapping, TypeRelation, TypeVarBoundOrConstraints, TypeVarInstance,
|
||||
TypeVarVariance, UnionType, declaration_type,
|
||||
TypeVarVariance, TypeVisitor, UnionType, declaration_type,
|
||||
};
|
||||
use crate::{Db, FxOrderSet};
|
||||
|
||||
|
@ -233,11 +233,11 @@ impl<'db> GenericContext<'db> {
|
|||
Specialization::new(db, self, expanded.into_boxed_slice(), None)
|
||||
}
|
||||
|
||||
pub(crate) fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
let variables: FxOrderSet<_> = self
|
||||
.variables(db)
|
||||
.iter()
|
||||
.map(|ty| ty.normalized(db))
|
||||
.map(|ty| ty.normalized_impl(db, visitor))
|
||||
.collect();
|
||||
Self::new(db, variables)
|
||||
}
|
||||
|
@ -376,9 +376,15 @@ impl<'db> Specialization<'db> {
|
|||
Specialization::new(db, self.generic_context(db), types, None)
|
||||
}
|
||||
|
||||
pub(crate) fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
let types: Box<[_]> = self.types(db).iter().map(|ty| ty.normalized(db)).collect();
|
||||
let tuple_inner = self.tuple_inner(db).and_then(|tuple| tuple.normalized(db));
|
||||
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
let types: Box<[_]> = self
|
||||
.types(db)
|
||||
.iter()
|
||||
.map(|ty| ty.normalized_impl(db, visitor))
|
||||
.collect();
|
||||
let tuple_inner = self
|
||||
.tuple_inner(db)
|
||||
.and_then(|tuple| tuple.normalized_impl(db, visitor));
|
||||
Self::new(db, self.generic_context(db), types, tuple_inner)
|
||||
}
|
||||
|
||||
|
@ -526,9 +532,17 @@ impl<'db> PartialSpecialization<'_, 'db> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn normalized(&self, db: &'db dyn Db) -> PartialSpecialization<'db, 'db> {
|
||||
let generic_context = self.generic_context.normalized(db);
|
||||
let types: Cow<_> = self.types.iter().map(|ty| ty.normalized(db)).collect();
|
||||
pub(crate) fn normalized_impl(
|
||||
&self,
|
||||
db: &'db dyn Db,
|
||||
visitor: &mut TypeVisitor<'db>,
|
||||
) -> PartialSpecialization<'db, 'db> {
|
||||
let generic_context = self.generic_context.normalized_impl(db, visitor);
|
||||
let types: Cow<_> = self
|
||||
.types
|
||||
.iter()
|
||||
.map(|ty| ty.normalized_impl(db, visitor))
|
||||
.collect();
|
||||
|
||||
PartialSpecialization {
|
||||
generic_context,
|
||||
|
|
|
@ -6,7 +6,7 @@ use super::protocol_class::ProtocolInterface;
|
|||
use super::{ClassType, KnownClass, SubclassOfType, Type, TypeVarVariance};
|
||||
use crate::place::{Place, PlaceAndQualifiers};
|
||||
use crate::types::tuple::TupleType;
|
||||
use crate::types::{ClassLiteral, DynamicType, TypeMapping, TypeRelation, TypeVarInstance};
|
||||
use crate::types::{DynamicType, TypeMapping, TypeRelation, TypeVarInstance, TypeVisitor};
|
||||
use crate::{Db, FxOrderSet};
|
||||
|
||||
pub(super) use synthesized_protocol::SynthesizedProtocolType;
|
||||
|
@ -41,7 +41,11 @@ impl<'db> Type<'db> {
|
|||
M: IntoIterator<Item = (&'a str, Type<'db>)>,
|
||||
{
|
||||
Self::ProtocolInstance(ProtocolInstanceType::synthesized(
|
||||
SynthesizedProtocolType::new(db, ProtocolInterface::with_property_members(db, members)),
|
||||
SynthesizedProtocolType::new(
|
||||
db,
|
||||
ProtocolInterface::with_property_members(db, members),
|
||||
&mut TypeVisitor::default(),
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -80,8 +84,8 @@ impl<'db> NominalInstanceType<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
Self::from_class(self.class.normalized(db))
|
||||
pub(super) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
Self::from_class(self.class.normalized_impl(db, visitor))
|
||||
}
|
||||
|
||||
pub(super) fn materialize(self, db: &'db dyn Db, variance: TypeVarVariance) -> Self {
|
||||
|
@ -201,31 +205,30 @@ impl<'db> ProtocolInstanceType<'db> {
|
|||
///
|
||||
/// See [`Type::normalized`] for more details.
|
||||
pub(super) fn normalized(self, db: &'db dyn Db) -> Type<'db> {
|
||||
let mut visitor = TypeVisitor::default();
|
||||
self.normalized_impl(db, &mut visitor)
|
||||
}
|
||||
|
||||
/// Return a "normalized" version of this `Protocol` type.
|
||||
///
|
||||
/// See [`Type::normalized`] for more details.
|
||||
pub(super) fn normalized_impl(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
visitor: &mut TypeVisitor<'db>,
|
||||
) -> Type<'db> {
|
||||
let object = KnownClass::Object.to_instance(db);
|
||||
if object.satisfies_protocol(db, self, TypeRelation::Subtyping) {
|
||||
return object;
|
||||
}
|
||||
match self.inner {
|
||||
Protocol::FromClass(_) => Type::ProtocolInstance(Self::synthesized(
|
||||
SynthesizedProtocolType::new(db, self.inner.interface(db)),
|
||||
SynthesizedProtocolType::new(db, self.inner.interface(db), visitor),
|
||||
)),
|
||||
Protocol::Synthesized(_) => Type::ProtocolInstance(self),
|
||||
}
|
||||
}
|
||||
|
||||
/// Replace references to `class` with a self-reference marker
|
||||
pub(super) fn replace_self_reference(self, db: &'db dyn Db, class: ClassLiteral<'db>) -> Self {
|
||||
match self.inner {
|
||||
Protocol::FromClass(class_type) if class_type.class_literal(db).0 == class => {
|
||||
ProtocolInstanceType::synthesized(SynthesizedProtocolType::new(
|
||||
db,
|
||||
ProtocolInterface::SelfReference,
|
||||
))
|
||||
}
|
||||
_ => self,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return `true` if the types of any of the members match the closure passed in.
|
||||
pub(super) fn any_over_type(
|
||||
self,
|
||||
|
@ -352,7 +355,7 @@ impl<'db> Protocol<'db> {
|
|||
|
||||
mod synthesized_protocol {
|
||||
use crate::types::protocol_class::ProtocolInterface;
|
||||
use crate::types::{TypeMapping, TypeVarInstance, TypeVarVariance};
|
||||
use crate::types::{TypeMapping, TypeVarInstance, TypeVarVariance, TypeVisitor};
|
||||
use crate::{Db, FxOrderSet};
|
||||
|
||||
/// A "synthesized" protocol type that is dissociated from a class definition in source code.
|
||||
|
@ -370,8 +373,12 @@ mod synthesized_protocol {
|
|||
pub(in crate::types) struct SynthesizedProtocolType<'db>(ProtocolInterface<'db>);
|
||||
|
||||
impl<'db> SynthesizedProtocolType<'db> {
|
||||
pub(super) fn new(db: &'db dyn Db, interface: ProtocolInterface<'db>) -> Self {
|
||||
Self(interface.normalized(db))
|
||||
pub(super) fn new(
|
||||
db: &'db dyn Db,
|
||||
interface: ProtocolInterface<'db>,
|
||||
visitor: &mut TypeVisitor<'db>,
|
||||
) -> Self {
|
||||
Self(interface.normalized_impl(db, visitor))
|
||||
}
|
||||
|
||||
pub(super) fn materialize(self, db: &'db dyn Db, variance: TypeVarVariance) -> Self {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::{collections::BTreeMap, ops::Deref};
|
||||
|
||||
use itertools::{Either, Itertools};
|
||||
use itertools::Itertools;
|
||||
|
||||
use ruff_python_ast::name::Name;
|
||||
|
||||
|
@ -10,7 +10,7 @@ use crate::{
|
|||
semantic_index::{place_table, use_def_map},
|
||||
types::{
|
||||
CallableType, ClassBase, ClassLiteral, KnownFunction, PropertyInstanceType, Signature,
|
||||
Type, TypeMapping, TypeQualifiers, TypeRelation, TypeVarInstance,
|
||||
Type, TypeMapping, TypeQualifiers, TypeRelation, TypeVarInstance, TypeVisitor,
|
||||
signatures::{Parameter, Parameters},
|
||||
},
|
||||
};
|
||||
|
@ -62,26 +62,19 @@ impl<'db> Deref for ProtocolClassLiteral<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
/// The interface of a protocol: the members of that protocol, and the types of those members.
|
||||
///
|
||||
/// # Ordering
|
||||
/// Ordering is based on the protocol interface member's salsa-assigned id and not on its members.
|
||||
/// The id may change between runs, or when the protocol instance members was garbage collected and recreated.
|
||||
#[salsa::interned(debug)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub(super) struct ProtocolInterfaceMembers<'db> {
|
||||
pub(super) struct ProtocolInterface<'db> {
|
||||
#[returns(ref)]
|
||||
inner: BTreeMap<Name, ProtocolMemberData<'db>>,
|
||||
}
|
||||
|
||||
impl get_size2::GetSize for ProtocolInterfaceMembers<'_> {}
|
||||
|
||||
/// The interface of a protocol: the members of that protocol, and the types of those members.
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Eq, PartialEq, Hash, salsa::Update, PartialOrd, Ord, get_size2::GetSize,
|
||||
)]
|
||||
pub(super) enum ProtocolInterface<'db> {
|
||||
Members(ProtocolInterfaceMembers<'db>),
|
||||
SelfReference,
|
||||
}
|
||||
impl get_size2::GetSize for ProtocolInterface<'_> {}
|
||||
|
||||
impl<'db> ProtocolInterface<'db> {
|
||||
/// Synthesize a new protocol interface with the given members.
|
||||
|
@ -112,11 +105,11 @@ impl<'db> ProtocolInterface<'db> {
|
|||
)
|
||||
})
|
||||
.collect();
|
||||
Self::Members(ProtocolInterfaceMembers::new(db, members))
|
||||
Self::new(db, members)
|
||||
}
|
||||
|
||||
fn empty(db: &'db dyn Db) -> Self {
|
||||
Self::Members(ProtocolInterfaceMembers::new(db, BTreeMap::default()))
|
||||
Self::new(db, BTreeMap::default())
|
||||
}
|
||||
|
||||
pub(super) fn members<'a>(
|
||||
|
@ -126,16 +119,11 @@ impl<'db> ProtocolInterface<'db> {
|
|||
where
|
||||
'db: 'a,
|
||||
{
|
||||
match self {
|
||||
Self::Members(members) => {
|
||||
Either::Left(members.inner(db).iter().map(|(name, data)| ProtocolMember {
|
||||
name,
|
||||
kind: data.kind,
|
||||
qualifiers: data.qualifiers,
|
||||
}))
|
||||
}
|
||||
Self::SelfReference => Either::Right(std::iter::empty()),
|
||||
}
|
||||
self.inner(db).iter().map(|(name, data)| ProtocolMember {
|
||||
name,
|
||||
kind: data.kind,
|
||||
qualifiers: data.qualifiers,
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn member_by_name<'a>(
|
||||
|
@ -143,29 +131,20 @@ impl<'db> ProtocolInterface<'db> {
|
|||
db: &'db dyn Db,
|
||||
name: &'a str,
|
||||
) -> Option<ProtocolMember<'a, 'db>> {
|
||||
match self {
|
||||
Self::Members(members) => members.inner(db).get(name).map(|data| ProtocolMember {
|
||||
name,
|
||||
kind: data.kind,
|
||||
qualifiers: data.qualifiers,
|
||||
}),
|
||||
Self::SelfReference => None,
|
||||
}
|
||||
self.inner(db).get(name).map(|data| ProtocolMember {
|
||||
name,
|
||||
kind: data.kind,
|
||||
qualifiers: data.qualifiers,
|
||||
})
|
||||
}
|
||||
|
||||
/// Return `true` if if all members on `self` are also members of `other`.
|
||||
///
|
||||
/// TODO: this method should consider the types of the members as well as their names.
|
||||
pub(super) fn is_sub_interface_of(self, db: &'db dyn Db, other: Self) -> bool {
|
||||
match (self, other) {
|
||||
(Self::Members(self_members), Self::Members(other_members)) => self_members
|
||||
.inner(db)
|
||||
.keys()
|
||||
.all(|member_name| other_members.inner(db).contains_key(member_name)),
|
||||
_ => {
|
||||
unreachable!("Enclosing protocols should never be a self-reference marker")
|
||||
}
|
||||
}
|
||||
self.inner(db)
|
||||
.keys()
|
||||
.all(|member_name| other.inner(db).contains_key(member_name))
|
||||
}
|
||||
|
||||
/// Return `true` if the types of any of the members match the closure passed in.
|
||||
|
@ -178,32 +157,24 @@ impl<'db> ProtocolInterface<'db> {
|
|||
.any(|member| member.any_over_type(db, type_fn))
|
||||
}
|
||||
|
||||
pub(super) fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
match self {
|
||||
Self::Members(members) => Self::Members(ProtocolInterfaceMembers::new(
|
||||
db,
|
||||
members
|
||||
.inner(db)
|
||||
.iter()
|
||||
.map(|(name, data)| (name.clone(), data.normalized(db)))
|
||||
.collect::<BTreeMap<_, _>>(),
|
||||
)),
|
||||
Self::SelfReference => Self::SelfReference,
|
||||
}
|
||||
pub(super) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
Self::new(
|
||||
db,
|
||||
self.inner(db)
|
||||
.iter()
|
||||
.map(|(name, data)| (name.clone(), data.normalized_impl(db, visitor)))
|
||||
.collect::<BTreeMap<_, _>>(),
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn materialize(self, db: &'db dyn Db, variance: TypeVarVariance) -> Self {
|
||||
match self {
|
||||
Self::Members(members) => Self::Members(ProtocolInterfaceMembers::new(
|
||||
db,
|
||||
members
|
||||
.inner(db)
|
||||
.iter()
|
||||
.map(|(name, data)| (name.clone(), data.materialize(db, variance)))
|
||||
.collect::<BTreeMap<_, _>>(),
|
||||
)),
|
||||
Self::SelfReference => Self::SelfReference,
|
||||
}
|
||||
Self::new(
|
||||
db,
|
||||
self.inner(db)
|
||||
.iter()
|
||||
.map(|(name, data)| (name.clone(), data.materialize(db, variance)))
|
||||
.collect::<BTreeMap<_, _>>(),
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn specialized_and_normalized<'a>(
|
||||
|
@ -211,22 +182,18 @@ impl<'db> ProtocolInterface<'db> {
|
|||
db: &'db dyn Db,
|
||||
type_mapping: &TypeMapping<'a, 'db>,
|
||||
) -> Self {
|
||||
match self {
|
||||
Self::Members(members) => Self::Members(ProtocolInterfaceMembers::new(
|
||||
db,
|
||||
members
|
||||
.inner(db)
|
||||
.iter()
|
||||
.map(|(name, data)| {
|
||||
(
|
||||
name.clone(),
|
||||
data.apply_type_mapping(db, type_mapping).normalized(db),
|
||||
)
|
||||
})
|
||||
.collect::<BTreeMap<_, _>>(),
|
||||
)),
|
||||
Self::SelfReference => Self::SelfReference,
|
||||
}
|
||||
Self::new(
|
||||
db,
|
||||
self.inner(db)
|
||||
.iter()
|
||||
.map(|(name, data)| {
|
||||
(
|
||||
name.clone(),
|
||||
data.apply_type_mapping(db, type_mapping).normalized(db),
|
||||
)
|
||||
})
|
||||
.collect::<BTreeMap<_, _>>(),
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn find_legacy_typevars(
|
||||
|
@ -234,13 +201,8 @@ impl<'db> ProtocolInterface<'db> {
|
|||
db: &'db dyn Db,
|
||||
typevars: &mut FxOrderSet<TypeVarInstance<'db>>,
|
||||
) {
|
||||
match self {
|
||||
Self::Members(members) => {
|
||||
for data in members.inner(db).values() {
|
||||
data.find_legacy_typevars(db, typevars);
|
||||
}
|
||||
}
|
||||
Self::SelfReference => {}
|
||||
for data in self.inner(db).values() {
|
||||
data.find_legacy_typevars(db, typevars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -253,8 +215,12 @@ pub(super) struct ProtocolMemberData<'db> {
|
|||
|
||||
impl<'db> ProtocolMemberData<'db> {
|
||||
fn normalized(&self, db: &'db dyn Db) -> Self {
|
||||
self.normalized_impl(db, &mut TypeVisitor::default())
|
||||
}
|
||||
|
||||
fn normalized_impl(&self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
Self {
|
||||
kind: self.kind.normalized(db),
|
||||
kind: self.kind.normalized_impl(db, visitor),
|
||||
qualifiers: self.qualifiers,
|
||||
}
|
||||
}
|
||||
|
@ -290,15 +256,17 @@ enum ProtocolMemberKind<'db> {
|
|||
}
|
||||
|
||||
impl<'db> ProtocolMemberKind<'db> {
|
||||
fn normalized(&self, db: &'db dyn Db) -> Self {
|
||||
fn normalized_impl(&self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
match self {
|
||||
ProtocolMemberKind::Method(callable) => {
|
||||
ProtocolMemberKind::Method(callable.normalized(db))
|
||||
ProtocolMemberKind::Method(callable.normalized_impl(db, visitor))
|
||||
}
|
||||
ProtocolMemberKind::Property(property) => {
|
||||
ProtocolMemberKind::Property(property.normalized(db))
|
||||
ProtocolMemberKind::Property(property.normalized_impl(db, visitor))
|
||||
}
|
||||
ProtocolMemberKind::Other(ty) => {
|
||||
ProtocolMemberKind::Other(ty.normalized_impl(db, visitor))
|
||||
}
|
||||
ProtocolMemberKind::Other(ty) => ProtocolMemberKind::Other(ty.normalized(db)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -513,15 +481,15 @@ fn cached_protocol_interface<'db>(
|
|||
(Type::Callable(callable), BoundOnClass::Yes)
|
||||
if callable.is_function_like(db) =>
|
||||
{
|
||||
ProtocolMemberKind::Method(ty.replace_self_reference(db, class))
|
||||
ProtocolMemberKind::Method(ty)
|
||||
}
|
||||
// TODO: method members that have `FunctionLiteral` types should be upcast
|
||||
// to `CallableType` so that two protocols with identical method members
|
||||
// are recognized as equivalent.
|
||||
(Type::FunctionLiteral(_function), BoundOnClass::Yes) => {
|
||||
ProtocolMemberKind::Method(ty.replace_self_reference(db, class))
|
||||
ProtocolMemberKind::Method(ty)
|
||||
}
|
||||
_ => ProtocolMemberKind::Other(ty.replace_self_reference(db, class)),
|
||||
_ => ProtocolMemberKind::Other(ty),
|
||||
};
|
||||
|
||||
let member = ProtocolMemberData { kind, qualifiers };
|
||||
|
@ -530,7 +498,7 @@ fn cached_protocol_interface<'db>(
|
|||
);
|
||||
}
|
||||
|
||||
ProtocolInterface::Members(ProtocolInterfaceMembers::new(db, members))
|
||||
ProtocolInterface::new(db, members)
|
||||
}
|
||||
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
|
|
|
@ -15,10 +15,10 @@ use std::{collections::HashMap, slice::Iter};
|
|||
use itertools::EitherOrBoth;
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
|
||||
use super::{DynamicType, Type, TypeVarVariance, definition_expression_type};
|
||||
use super::{DynamicType, Type, TypeVarVariance, TypeVisitor, definition_expression_type};
|
||||
use crate::semantic_index::definition::Definition;
|
||||
use crate::types::generics::GenericContext;
|
||||
use crate::types::{ClassLiteral, TypeMapping, TypeRelation, TypeVarInstance, todo_type};
|
||||
use crate::types::{TypeMapping, TypeRelation, TypeVarInstance, todo_type};
|
||||
use crate::{Db, FxOrderSet};
|
||||
use ruff_python_ast::{self as ast, name::Name};
|
||||
|
||||
|
@ -61,11 +61,11 @@ impl<'db> CallableSignature<'db> {
|
|||
)
|
||||
}
|
||||
|
||||
pub(crate) fn normalized(&self, db: &'db dyn Db) -> Self {
|
||||
pub(crate) fn normalized_impl(&self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
Self::from_overloads(
|
||||
self.overloads
|
||||
.iter()
|
||||
.map(|signature| signature.normalized(db)),
|
||||
.map(|signature| signature.normalized_impl(db, visitor)),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -197,17 +197,6 @@ impl<'db> CallableSignature<'db> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn replace_self_reference(&self, db: &'db dyn Db, class: ClassLiteral<'db>) -> Self {
|
||||
Self {
|
||||
overloads: self
|
||||
.overloads
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|signature| signature.replace_self_reference(db, class))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'db> IntoIterator for &'a CallableSignature<'db> {
|
||||
|
@ -345,16 +334,22 @@ impl<'db> Signature<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn normalized(&self, db: &'db dyn Db) -> Self {
|
||||
pub(crate) fn normalized_impl(&self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
Self {
|
||||
generic_context: self.generic_context.map(|ctx| ctx.normalized(db)),
|
||||
inherited_generic_context: self.inherited_generic_context.map(|ctx| ctx.normalized(db)),
|
||||
generic_context: self
|
||||
.generic_context
|
||||
.map(|ctx| ctx.normalized_impl(db, visitor)),
|
||||
inherited_generic_context: self
|
||||
.inherited_generic_context
|
||||
.map(|ctx| ctx.normalized_impl(db, visitor)),
|
||||
parameters: self
|
||||
.parameters
|
||||
.iter()
|
||||
.map(|param| param.normalized(db))
|
||||
.map(|param| param.normalized_impl(db, visitor))
|
||||
.collect(),
|
||||
return_ty: self.return_ty.map(|return_ty| return_ty.normalized(db)),
|
||||
return_ty: self
|
||||
.return_ty
|
||||
.map(|return_ty| return_ty.normalized_impl(db, visitor)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -873,28 +868,6 @@ impl<'db> Signature<'db> {
|
|||
|
||||
true
|
||||
}
|
||||
|
||||
/// See [`Type::replace_self_reference`].
|
||||
pub(crate) fn replace_self_reference(
|
||||
mut self,
|
||||
db: &'db dyn Db,
|
||||
class: ClassLiteral<'db>,
|
||||
) -> Self {
|
||||
// TODO: also replace self references in generic context
|
||||
|
||||
self.parameters = self
|
||||
.parameters
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|param| param.replace_self_reference(db, class))
|
||||
.collect();
|
||||
|
||||
if let Some(ty) = self.return_ty.as_mut() {
|
||||
*ty = ty.replace_self_reference(db, class);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)]
|
||||
|
@ -1303,7 +1276,7 @@ impl<'db> Parameter<'db> {
|
|||
/// Normalize nested unions and intersections in the annotated type, if any.
|
||||
///
|
||||
/// See [`Type::normalized`] for more details.
|
||||
pub(crate) fn normalized(&self, db: &'db dyn Db) -> Self {
|
||||
pub(crate) fn normalized_impl(&self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
let Parameter {
|
||||
annotated_type,
|
||||
kind,
|
||||
|
@ -1311,7 +1284,7 @@ impl<'db> Parameter<'db> {
|
|||
} = self;
|
||||
|
||||
// Ensure unions and intersections are ordered in the annotated type (if there is one)
|
||||
let annotated_type = annotated_type.map(|ty| ty.normalized(db));
|
||||
let annotated_type = annotated_type.map(|ty| ty.normalized_impl(db, visitor));
|
||||
|
||||
// Ensure that parameter names are stripped from positional-only, variadic and keyword-variadic parameters.
|
||||
// Ensure that we only record whether a parameter *has* a default
|
||||
|
@ -1444,14 +1417,6 @@ impl<'db> Parameter<'db> {
|
|||
ParameterKind::Variadic { .. } | ParameterKind::KeywordVariadic { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// See [`Type::replace_self_reference`].
|
||||
fn replace_self_reference(mut self, db: &'db (dyn Db), class: ClassLiteral<'db>) -> Self {
|
||||
if let Some(ty) = self.annotated_type.as_mut() {
|
||||
*ty = ty.replace_self_reference(db, class);
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)]
|
||||
|
|
|
@ -3,7 +3,7 @@ use ruff_python_ast::name::Name;
|
|||
use crate::place::PlaceAndQualifiers;
|
||||
use crate::types::{
|
||||
ClassType, DynamicType, KnownClass, MemberLookupPolicy, Type, TypeMapping, TypeRelation,
|
||||
TypeVarInstance,
|
||||
TypeVarInstance, TypeVisitor,
|
||||
};
|
||||
use crate::{Db, FxOrderSet};
|
||||
|
||||
|
@ -171,9 +171,9 @@ impl<'db> SubclassOfType<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
Self {
|
||||
subclass_of: self.subclass_of.normalized(db),
|
||||
subclass_of: self.subclass_of.normalized_impl(db, visitor),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,9 +228,9 @@ impl<'db> SubclassOfInner<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
match self {
|
||||
Self::Class(class) => Self::Class(class.normalized(db)),
|
||||
Self::Class(class) => Self::Class(class.normalized_impl(db, visitor)),
|
||||
Self::Dynamic(dynamic) => Self::Dynamic(dynamic.normalized()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,8 @@ use itertools::{Either, EitherOrBoth, Itertools};
|
|||
|
||||
use crate::types::class::{ClassType, KnownClass};
|
||||
use crate::types::{
|
||||
Type, TypeMapping, TypeRelation, TypeVarInstance, TypeVarVariance, UnionBuilder, UnionType,
|
||||
Type, TypeMapping, TypeRelation, TypeVarInstance, TypeVarVariance, TypeVisitor, UnionBuilder,
|
||||
UnionType,
|
||||
};
|
||||
use crate::util::subscript::{Nth, OutOfBoundsError, PyIndex, PySlice, StepSizeZeroError};
|
||||
use crate::{Db, FxOrderSet};
|
||||
|
@ -174,8 +175,12 @@ impl<'db> TupleType<'db> {
|
|||
///
|
||||
/// See [`Type::normalized`] for more details.
|
||||
#[must_use]
|
||||
pub(crate) fn normalized(self, db: &'db dyn Db) -> Option<Self> {
|
||||
TupleType::new(db, self.tuple(db).normalized(db))
|
||||
pub(crate) fn normalized_impl(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
visitor: &mut TypeVisitor<'db>,
|
||||
) -> Option<Self> {
|
||||
TupleType::new(db, self.tuple(db).normalized_impl(db, visitor))
|
||||
}
|
||||
|
||||
pub(crate) fn materialize(self, db: &'db dyn Db, variance: TypeVarVariance) -> Option<Self> {
|
||||
|
@ -327,8 +332,8 @@ impl<'db> FixedLengthTuple<Type<'db>> {
|
|||
}
|
||||
|
||||
#[must_use]
|
||||
fn normalized(&self, db: &'db dyn Db) -> Self {
|
||||
Self::from_elements(self.0.iter().map(|ty| ty.normalized(db)))
|
||||
fn normalized_impl(&self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
Self::from_elements(self.0.iter().map(|ty| ty.normalized_impl(db, visitor)))
|
||||
}
|
||||
|
||||
fn materialize(&self, db: &'db dyn Db, variance: TypeVarVariance) -> Self {
|
||||
|
@ -640,14 +645,16 @@ impl<'db> VariableLengthTuple<Type<'db>> {
|
|||
}
|
||||
|
||||
#[must_use]
|
||||
fn normalized(&self, db: &'db dyn Db) -> TupleSpec<'db> {
|
||||
Self::mixed(
|
||||
self.prenormalized_prefix_elements(db, None)
|
||||
.map(|ty| ty.normalized(db)),
|
||||
self.variable.normalized(db),
|
||||
self.prenormalized_suffix_elements(db, None)
|
||||
.map(|ty| ty.normalized(db)),
|
||||
)
|
||||
fn normalized_impl(&self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> TupleSpec<'db> {
|
||||
let prefix = self
|
||||
.prenormalized_prefix_elements(db, None)
|
||||
.map(|ty| ty.normalized_impl(db, visitor))
|
||||
.collect::<Vec<_>>();
|
||||
let suffix = self
|
||||
.prenormalized_suffix_elements(db, None)
|
||||
.map(|ty| ty.normalized_impl(db, visitor))
|
||||
.collect::<Vec<_>>();
|
||||
Self::mixed(prefix, self.variable.normalized(db), suffix)
|
||||
}
|
||||
|
||||
fn materialize(&self, db: &'db dyn Db, variance: TypeVarVariance) -> TupleSpec<'db> {
|
||||
|
@ -977,10 +984,10 @@ impl<'db> Tuple<Type<'db>> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn normalized(&self, db: &'db dyn Db) -> Self {
|
||||
pub(crate) fn normalized_impl(&self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||
match self {
|
||||
Tuple::Fixed(tuple) => Tuple::Fixed(tuple.normalized(db)),
|
||||
Tuple::Variable(tuple) => tuple.normalized(db),
|
||||
Tuple::Fixed(tuple) => Tuple::Fixed(tuple.normalized_impl(db, visitor)),
|
||||
Tuple::Variable(tuple) => tuple.normalized_impl(db, visitor),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue