mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 21:34:57 +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
|
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
|
### Regression test: narrowing with self-referential protocols
|
||||||
|
|
||||||
This snippet caused us to panic on an early version of the implementation for 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)
|
mypy # too many iterations (self-recursive type alias)
|
||||||
packaging # too many iterations
|
packaging # too many iterations
|
||||||
pandas # slow (9s)
|
pandas # slow (9s)
|
||||||
pandera # stack overflow
|
pandera # too many iterations
|
||||||
pip # vendors packaging, see above
|
pip # vendors packaging, see above
|
||||||
pylint # cycle panics (self-recursive type alias)
|
pylint # cycle panics (self-recursive type alias)
|
||||||
pyodide # too many cycle iterations
|
pyodide # too many cycle iterations
|
||||||
|
@ -19,5 +19,4 @@ setuptools # vendors packaging, see above
|
||||||
spack # slow, success, but mypy-primer hangs processing the output
|
spack # slow, success, but mypy-primer hangs processing the output
|
||||||
spark # too many iterations
|
spark # too many iterations
|
||||||
steam.py # hangs (single threaded)
|
steam.py # hangs (single threaded)
|
||||||
tornado # bad use-def map (https://github.com/astral-sh/ty/issues/365)
|
|
||||||
xarray # too many iterations
|
xarray # too many iterations
|
||||||
|
|
|
@ -110,6 +110,7 @@ strawberry
|
||||||
streamlit
|
streamlit
|
||||||
svcs
|
svcs
|
||||||
sympy
|
sympy
|
||||||
|
tornado
|
||||||
trio
|
trio
|
||||||
twine
|
twine
|
||||||
typeshed-stats
|
typeshed-stats
|
||||||
|
|
|
@ -21,6 +21,7 @@ use ruff_text_size::{Ranged, TextRange};
|
||||||
use type_ordering::union_or_intersection_elements_ordering;
|
use type_ordering::union_or_intersection_elements_ordering;
|
||||||
|
|
||||||
pub(crate) use self::builder::{IntersectionBuilder, UnionBuilder};
|
pub(crate) use self::builder::{IntersectionBuilder, UnionBuilder};
|
||||||
|
pub(crate) use self::cyclic::TypeVisitor;
|
||||||
pub use self::diagnostic::TypeCheckDiagnostics;
|
pub use self::diagnostic::TypeCheckDiagnostics;
|
||||||
pub(crate) use self::diagnostic::register_lints;
|
pub(crate) use self::diagnostic::register_lints;
|
||||||
pub(crate) use self::infer::{
|
pub(crate) use self::infer::{
|
||||||
|
@ -63,6 +64,7 @@ mod call;
|
||||||
mod class;
|
mod class;
|
||||||
mod class_base;
|
mod class_base;
|
||||||
mod context;
|
mod context;
|
||||||
|
mod cyclic;
|
||||||
mod diagnostic;
|
mod diagnostic;
|
||||||
mod display;
|
mod display;
|
||||||
mod function;
|
mod function;
|
||||||
|
@ -384,11 +386,11 @@ impl<'db> PropertyInstanceType<'db> {
|
||||||
Self::new(db, getter, setter)
|
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(
|
Self::new(
|
||||||
db,
|
db,
|
||||||
self.getter(db).map(|ty| ty.normalized(db)),
|
self.getter(db).map(|ty| ty.normalized_impl(db, visitor)),
|
||||||
self.setter(db).map(|ty| ty.normalized(db)),
|
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
|
/// 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.
|
/// is specialized, we will replace this typevar with its specialization.
|
||||||
TypeVar(TypeVarInstance<'db>),
|
TypeVar(TypeVarInstance<'db>),
|
||||||
// A bound super object like `super()` or `super(A, A())`
|
/// 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
|
/// This type doesn't handle an unbound super object like `super(A)`; for that we just use
|
||||||
// a `Type::NominalInstance` of `builtins.super`.
|
/// a `Type::NominalInstance` of `builtins.super`.
|
||||||
BoundSuper(BoundSuperType<'db>),
|
BoundSuper(BoundSuperType<'db>),
|
||||||
/// A subtype of `bool` that allows narrowing in both positive and negative cases.
|
/// A subtype of `bool` that allows narrowing in both positive and negative cases.
|
||||||
TypeIs(TypeIsType<'db>),
|
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.
|
/// 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 {
|
pub fn any_over_type(self, db: &'db dyn Db, type_fn: &dyn Fn(Type<'db>) -> bool) -> bool {
|
||||||
if type_fn(self) {
|
if type_fn(self) {
|
||||||
|
@ -1149,26 +1072,62 @@ impl<'db> Type<'db> {
|
||||||
/// - Converts class-based protocols into synthesized protocols
|
/// - Converts class-based protocols into synthesized protocols
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn normalized(self, db: &'db dyn Db) -> Self {
|
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 {
|
match self {
|
||||||
Type::Union(union) => Type::Union(union.normalized(db)),
|
Type::Union(union) => {
|
||||||
Type::Intersection(intersection) => Type::Intersection(intersection.normalized(db)),
|
visitor.visit(self, |v| Type::Union(union.normalized_impl(db, v)))
|
||||||
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::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::LiteralString
|
||||||
| Type::AlwaysFalsy
|
| Type::AlwaysFalsy
|
||||||
| Type::AlwaysTruthy
|
| 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 {
|
match self {
|
||||||
TypeMapping::Specialization(specialization) => {
|
TypeMapping::Specialization(specialization) => {
|
||||||
TypeMapping::Specialization(specialization.normalized(db))
|
TypeMapping::Specialization(specialization.normalized_impl(db, visitor))
|
||||||
}
|
}
|
||||||
TypeMapping::PartialSpecialization(partial) => {
|
TypeMapping::PartialSpecialization(partial) => {
|
||||||
TypeMapping::PartialSpecialization(partial.normalized(db))
|
TypeMapping::PartialSpecialization(partial.normalized_impl(db, visitor))
|
||||||
}
|
}
|
||||||
TypeMapping::PromoteLiterals => TypeMapping::PromoteLiterals,
|
TypeMapping::PromoteLiterals => TypeMapping::PromoteLiterals,
|
||||||
}
|
}
|
||||||
|
@ -5795,12 +5754,18 @@ pub enum KnownInstanceType<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> 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 {
|
match self {
|
||||||
Self::SubscriptedProtocol(context) => Self::SubscriptedProtocol(context.normalized(db)),
|
Self::SubscriptedProtocol(context) => {
|
||||||
Self::SubscriptedGeneric(context) => Self::SubscriptedGeneric(context.normalized(db)),
|
Self::SubscriptedProtocol(context.normalized_impl(db, visitor))
|
||||||
Self::TypeVar(typevar) => Self::TypeVar(typevar.normalized(db)),
|
}
|
||||||
Self::TypeAliasType(type_alias) => Self::TypeAliasType(type_alias.normalized(db)),
|
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(
|
Self::new(
|
||||||
db,
|
db,
|
||||||
self.name(db),
|
self.name(db),
|
||||||
self.definition(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.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),
|
self.kind(db),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -6236,13 +6202,13 @@ pub enum TypeVarBoundOrConstraints<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> 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 {
|
match self {
|
||||||
TypeVarBoundOrConstraints::UpperBound(bound) => {
|
TypeVarBoundOrConstraints::UpperBound(bound) => {
|
||||||
TypeVarBoundOrConstraints::UpperBound(bound.normalized(db))
|
TypeVarBoundOrConstraints::UpperBound(bound.normalized_impl(db, visitor))
|
||||||
}
|
}
|
||||||
TypeVarBoundOrConstraints::Constraints(constraints) => {
|
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(
|
Self::new(
|
||||||
db,
|
db,
|
||||||
self.function(db).normalized(db),
|
self.function(db).normalized_impl(db, visitor),
|
||||||
self.self_instance(db).normalized(db),
|
self.self_instance(db).normalized_impl(db, visitor),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7261,10 +7227,10 @@ impl<'db> CallableType<'db> {
|
||||||
/// Return a "normalized" version of this `Callable` type.
|
/// Return a "normalized" version of this `Callable` type.
|
||||||
///
|
///
|
||||||
/// See [`Type::normalized`] for more details.
|
/// 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(
|
CallableType::new(
|
||||||
db,
|
db,
|
||||||
self.signatures(db).normalized(db),
|
self.signatures(db).normalized_impl(db, visitor),
|
||||||
self.is_function_like(db),
|
self.is_function_like(db),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -7305,15 +7271,6 @@ impl<'db> CallableType<'db> {
|
||||||
.signatures(db)
|
.signatures(db)
|
||||||
.is_equivalent_to(db, other.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`
|
/// 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 {
|
match self {
|
||||||
MethodWrapperKind::FunctionTypeDunderGet(function) => {
|
MethodWrapperKind::FunctionTypeDunderGet(function) => {
|
||||||
MethodWrapperKind::FunctionTypeDunderGet(function.normalized(db))
|
MethodWrapperKind::FunctionTypeDunderGet(function.normalized_impl(db, visitor))
|
||||||
}
|
}
|
||||||
MethodWrapperKind::FunctionTypeDunderCall(function) => {
|
MethodWrapperKind::FunctionTypeDunderCall(function) => {
|
||||||
MethodWrapperKind::FunctionTypeDunderCall(function.normalized(db))
|
MethodWrapperKind::FunctionTypeDunderCall(function.normalized_impl(db, visitor))
|
||||||
}
|
}
|
||||||
MethodWrapperKind::PropertyDunderGet(property) => {
|
MethodWrapperKind::PropertyDunderGet(property) => {
|
||||||
MethodWrapperKind::PropertyDunderGet(property.normalized(db))
|
MethodWrapperKind::PropertyDunderGet(property.normalized_impl(db, visitor))
|
||||||
}
|
}
|
||||||
MethodWrapperKind::PropertyDunderSet(property) => {
|
MethodWrapperKind::PropertyDunderSet(property) => {
|
||||||
MethodWrapperKind::PropertyDunderSet(property.normalized(db))
|
MethodWrapperKind::PropertyDunderSet(property.normalized_impl(db, visitor))
|
||||||
}
|
}
|
||||||
MethodWrapperKind::StrStartswith(_) => self,
|
MethodWrapperKind::StrStartswith(_) => self,
|
||||||
}
|
}
|
||||||
|
@ -7530,7 +7487,7 @@ impl<'db> PEP695TypeAliasType<'db> {
|
||||||
definition_expression_type(db, definition, &type_alias_stmt_node.value)
|
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
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7551,12 +7508,12 @@ pub struct BareTypeAliasType<'db> {
|
||||||
impl get_size2::GetSize for BareTypeAliasType<'_> {}
|
impl get_size2::GetSize for BareTypeAliasType<'_> {}
|
||||||
|
|
||||||
impl<'db> BareTypeAliasType<'db> {
|
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(
|
Self::new(
|
||||||
db,
|
db,
|
||||||
self.name(db),
|
self.name(db),
|
||||||
self.definition(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> {
|
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 {
|
match self {
|
||||||
TypeAliasType::PEP695(type_alias) => TypeAliasType::PEP695(type_alias.normalized(db)),
|
TypeAliasType::PEP695(type_alias) => {
|
||||||
TypeAliasType::Bare(type_alias) => TypeAliasType::Bare(type_alias.normalized(db)),
|
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.
|
/// See [`Type::normalized`] for more details.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn normalized(self, db: &'db dyn Db) -> Self {
|
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
|
let mut new_elements: Vec<Type<'db>> = self
|
||||||
.elements(db)
|
.elements(db)
|
||||||
.iter()
|
.iter()
|
||||||
.map(|element| element.normalized(db))
|
.map(|element| element.normalized_impl(db, visitor))
|
||||||
.collect();
|
.collect();
|
||||||
new_elements.sort_unstable_by(|l, r| union_or_intersection_elements_ordering(db, l, r));
|
new_elements.sort_unstable_by(|l, r| union_or_intersection_elements_ordering(db, l, r));
|
||||||
UnionType::new(db, new_elements.into_boxed_slice())
|
UnionType::new(db, new_elements.into_boxed_slice())
|
||||||
|
@ -7839,12 +7804,20 @@ impl<'db> IntersectionType<'db> {
|
||||||
/// See [`Type::normalized`] for more details.
|
/// See [`Type::normalized`] for more details.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn normalized(self, db: &'db dyn Db) -> Self {
|
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>(
|
fn normalized_set<'db>(
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
elements: &FxOrderSet<Type<'db>>,
|
elements: &FxOrderSet<Type<'db>>,
|
||||||
|
visitor: &mut TypeVisitor<'db>,
|
||||||
) -> FxOrderSet<Type<'db>> {
|
) -> FxOrderSet<Type<'db>> {
|
||||||
let mut elements: FxOrderSet<Type<'db>> =
|
let mut elements: FxOrderSet<Type<'db>> = elements
|
||||||
elements.iter().map(|ty| ty.normalized(db)).collect();
|
.iter()
|
||||||
|
.map(|ty| ty.normalized_impl(db, visitor))
|
||||||
|
.collect();
|
||||||
|
|
||||||
elements.sort_unstable_by(|l, r| union_or_intersection_elements_ordering(db, l, r));
|
elements.sort_unstable_by(|l, r| union_or_intersection_elements_ordering(db, l, r));
|
||||||
elements
|
elements
|
||||||
|
@ -7852,8 +7825,8 @@ impl<'db> IntersectionType<'db> {
|
||||||
|
|
||||||
IntersectionType::new(
|
IntersectionType::new(
|
||||||
db,
|
db,
|
||||||
normalized_set(db, self.positive(db)),
|
normalized_set(db, self.positive(db), visitor),
|
||||||
normalized_set(db, self.negative(db)),
|
normalized_set(db, self.negative(db), visitor),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8092,11 +8065,15 @@ pub enum SuperOwnerKind<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> 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 {
|
match self {
|
||||||
SuperOwnerKind::Dynamic(dynamic) => SuperOwnerKind::Dynamic(dynamic.normalized()),
|
SuperOwnerKind::Dynamic(dynamic) => SuperOwnerKind::Dynamic(dynamic.normalized()),
|
||||||
SuperOwnerKind::Class(class) => SuperOwnerKind::Class(class.normalized(db)),
|
SuperOwnerKind::Class(class) => {
|
||||||
SuperOwnerKind::Instance(instance) => SuperOwnerKind::Instance(instance.normalized(db)),
|
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(
|
Self::new(
|
||||||
db,
|
db,
|
||||||
self.pivot_class(db).normalized(db),
|
self.pivot_class(db).normalized_impl(db, visitor),
|
||||||
self.owner(db).normalized(db),
|
self.owner(db).normalized_impl(db, visitor),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ use crate::types::tuple::TupleType;
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
BareTypeAliasType, Binding, BoundSuperError, BoundSuperType, CallableType, DataclassParams,
|
BareTypeAliasType, Binding, BoundSuperError, BoundSuperType, CallableType, DataclassParams,
|
||||||
KnownInstanceType, TypeAliasType, TypeMapping, TypeRelation, TypeVarBoundOrConstraints,
|
KnownInstanceType, TypeAliasType, TypeMapping, TypeRelation, TypeVarBoundOrConstraints,
|
||||||
TypeVarInstance, TypeVarKind, infer_definition_types,
|
TypeVarInstance, TypeVarKind, TypeVisitor, infer_definition_types,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
Db, FxOrderSet, KnownModule, Program,
|
Db, FxOrderSet, KnownModule, Program,
|
||||||
|
@ -182,8 +182,12 @@ pub struct GenericAlias<'db> {
|
||||||
impl get_size2::GetSize for GenericAlias<'_> {}
|
impl get_size2::GetSize for GenericAlias<'_> {}
|
||||||
|
|
||||||
impl<'db> GenericAlias<'db> {
|
impl<'db> GenericAlias<'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 {
|
||||||
Self::new(db, self.origin(db), self.specialization(db).normalized(db))
|
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 {
|
pub(super) fn materialize(self, db: &'db dyn Db, variance: TypeVarVariance) -> Self {
|
||||||
|
@ -249,10 +253,10 @@ pub enum ClassType<'db> {
|
||||||
|
|
||||||
#[salsa::tracked]
|
#[salsa::tracked]
|
||||||
impl<'db> ClassType<'db> {
|
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 {
|
match self {
|
||||||
Self::NonGeneric(_) => 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::generics::Specialization;
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
ClassType, DynamicType, KnownClass, KnownInstanceType, MroError, MroIterator, SpecialFormType,
|
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.
|
/// Enumeration of the possible kinds of types we allow in class bases.
|
||||||
|
@ -31,10 +31,10 @@ impl<'db> ClassBase<'db> {
|
||||||
Self::Dynamic(DynamicType::Unknown)
|
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 {
|
match self {
|
||||||
Self::Dynamic(dynamic) => Self::Dynamic(dynamic.normalized()),
|
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,
|
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::signatures::{CallableSignature, Signature};
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
BoundMethodType, CallableType, DynamicType, KnownClass, Type, TypeMapping, TypeRelation,
|
BoundMethodType, CallableType, DynamicType, KnownClass, Type, TypeMapping, TypeRelation,
|
||||||
TypeVarInstance,
|
TypeVarInstance, TypeVisitor,
|
||||||
};
|
};
|
||||||
use crate::{Db, FxOrderSet, ModuleName, resolve_module};
|
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
|
let context = self
|
||||||
.inherited_generic_context(db)
|
.inherited_generic_context(db)
|
||||||
.map(|ctx| ctx.normalized(db));
|
.map(|ctx| ctx.normalized_impl(db, visitor));
|
||||||
Self::new(db, self.last_definition(db), context)
|
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 {
|
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
|
let mappings: Box<_> = self
|
||||||
.type_mappings(db)
|
.type_mappings(db)
|
||||||
.iter()
|
.iter()
|
||||||
.map(|mapping| mapping.normalized(db))
|
.map(|mapping| mapping.normalized_impl(db, visitor))
|
||||||
.collect();
|
.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::tuple::{TupleSpec, TupleType};
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
KnownInstanceType, Type, TypeMapping, TypeRelation, TypeVarBoundOrConstraints, TypeVarInstance,
|
KnownInstanceType, Type, TypeMapping, TypeRelation, TypeVarBoundOrConstraints, TypeVarInstance,
|
||||||
TypeVarVariance, UnionType, declaration_type,
|
TypeVarVariance, TypeVisitor, UnionType, declaration_type,
|
||||||
};
|
};
|
||||||
use crate::{Db, FxOrderSet};
|
use crate::{Db, FxOrderSet};
|
||||||
|
|
||||||
|
@ -233,11 +233,11 @@ impl<'db> GenericContext<'db> {
|
||||||
Specialization::new(db, self, expanded.into_boxed_slice(), None)
|
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
|
let variables: FxOrderSet<_> = self
|
||||||
.variables(db)
|
.variables(db)
|
||||||
.iter()
|
.iter()
|
||||||
.map(|ty| ty.normalized(db))
|
.map(|ty| ty.normalized_impl(db, visitor))
|
||||||
.collect();
|
.collect();
|
||||||
Self::new(db, variables)
|
Self::new(db, variables)
|
||||||
}
|
}
|
||||||
|
@ -376,9 +376,15 @@ impl<'db> Specialization<'db> {
|
||||||
Specialization::new(db, self.generic_context(db), types, None)
|
Specialization::new(db, self.generic_context(db), types, 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 types: Box<[_]> = self.types(db).iter().map(|ty| ty.normalized(db)).collect();
|
let types: Box<[_]> = self
|
||||||
let tuple_inner = self.tuple_inner(db).and_then(|tuple| tuple.normalized(db));
|
.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)
|
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> {
|
pub(crate) fn normalized_impl(
|
||||||
let generic_context = self.generic_context.normalized(db);
|
&self,
|
||||||
let types: Cow<_> = self.types.iter().map(|ty| ty.normalized(db)).collect();
|
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 {
|
PartialSpecialization {
|
||||||
generic_context,
|
generic_context,
|
||||||
|
|
|
@ -6,7 +6,7 @@ use super::protocol_class::ProtocolInterface;
|
||||||
use super::{ClassType, KnownClass, SubclassOfType, Type, TypeVarVariance};
|
use super::{ClassType, KnownClass, SubclassOfType, Type, TypeVarVariance};
|
||||||
use crate::place::{Place, PlaceAndQualifiers};
|
use crate::place::{Place, PlaceAndQualifiers};
|
||||||
use crate::types::tuple::TupleType;
|
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};
|
use crate::{Db, FxOrderSet};
|
||||||
|
|
||||||
pub(super) use synthesized_protocol::SynthesizedProtocolType;
|
pub(super) use synthesized_protocol::SynthesizedProtocolType;
|
||||||
|
@ -41,7 +41,11 @@ impl<'db> Type<'db> {
|
||||||
M: IntoIterator<Item = (&'a str, Type<'db>)>,
|
M: IntoIterator<Item = (&'a str, Type<'db>)>,
|
||||||
{
|
{
|
||||||
Self::ProtocolInstance(ProtocolInstanceType::synthesized(
|
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 {
|
pub(super) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||||
Self::from_class(self.class.normalized(db))
|
Self::from_class(self.class.normalized_impl(db, visitor))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn materialize(self, db: &'db dyn Db, variance: TypeVarVariance) -> Self {
|
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.
|
/// See [`Type::normalized`] for more details.
|
||||||
pub(super) fn normalized(self, db: &'db dyn Db) -> Type<'db> {
|
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);
|
let object = KnownClass::Object.to_instance(db);
|
||||||
if object.satisfies_protocol(db, self, TypeRelation::Subtyping) {
|
if object.satisfies_protocol(db, self, TypeRelation::Subtyping) {
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
match self.inner {
|
match self.inner {
|
||||||
Protocol::FromClass(_) => Type::ProtocolInstance(Self::synthesized(
|
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),
|
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.
|
/// Return `true` if the types of any of the members match the closure passed in.
|
||||||
pub(super) fn any_over_type(
|
pub(super) fn any_over_type(
|
||||||
self,
|
self,
|
||||||
|
@ -352,7 +355,7 @@ impl<'db> Protocol<'db> {
|
||||||
|
|
||||||
mod synthesized_protocol {
|
mod synthesized_protocol {
|
||||||
use crate::types::protocol_class::ProtocolInterface;
|
use crate::types::protocol_class::ProtocolInterface;
|
||||||
use crate::types::{TypeMapping, TypeVarInstance, TypeVarVariance};
|
use crate::types::{TypeMapping, TypeVarInstance, TypeVarVariance, TypeVisitor};
|
||||||
use crate::{Db, FxOrderSet};
|
use crate::{Db, FxOrderSet};
|
||||||
|
|
||||||
/// A "synthesized" protocol type that is dissociated from a class definition in source code.
|
/// 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>);
|
pub(in crate::types) struct SynthesizedProtocolType<'db>(ProtocolInterface<'db>);
|
||||||
|
|
||||||
impl<'db> SynthesizedProtocolType<'db> {
|
impl<'db> SynthesizedProtocolType<'db> {
|
||||||
pub(super) fn new(db: &'db dyn Db, interface: ProtocolInterface<'db>) -> Self {
|
pub(super) fn new(
|
||||||
Self(interface.normalized(db))
|
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 {
|
pub(super) fn materialize(self, db: &'db dyn Db, variance: TypeVarVariance) -> Self {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{collections::BTreeMap, ops::Deref};
|
use std::{collections::BTreeMap, ops::Deref};
|
||||||
|
|
||||||
use itertools::{Either, Itertools};
|
use itertools::Itertools;
|
||||||
|
|
||||||
use ruff_python_ast::name::Name;
|
use ruff_python_ast::name::Name;
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ use crate::{
|
||||||
semantic_index::{place_table, use_def_map},
|
semantic_index::{place_table, use_def_map},
|
||||||
types::{
|
types::{
|
||||||
CallableType, ClassBase, ClassLiteral, KnownFunction, PropertyInstanceType, Signature,
|
CallableType, ClassBase, ClassLiteral, KnownFunction, PropertyInstanceType, Signature,
|
||||||
Type, TypeMapping, TypeQualifiers, TypeRelation, TypeVarInstance,
|
Type, TypeMapping, TypeQualifiers, TypeRelation, TypeVarInstance, TypeVisitor,
|
||||||
signatures::{Parameter, Parameters},
|
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
|
||||||
/// Ordering is based on the protocol interface member's salsa-assigned id and not on its members.
|
/// 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.
|
/// The id may change between runs, or when the protocol instance members was garbage collected and recreated.
|
||||||
#[salsa::interned(debug)]
|
#[salsa::interned(debug)]
|
||||||
#[derive(PartialOrd, Ord)]
|
#[derive(PartialOrd, Ord)]
|
||||||
pub(super) struct ProtocolInterfaceMembers<'db> {
|
pub(super) struct ProtocolInterface<'db> {
|
||||||
#[returns(ref)]
|
#[returns(ref)]
|
||||||
inner: BTreeMap<Name, ProtocolMemberData<'db>>,
|
inner: BTreeMap<Name, ProtocolMemberData<'db>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl get_size2::GetSize for ProtocolInterfaceMembers<'_> {}
|
impl get_size2::GetSize for ProtocolInterface<'_> {}
|
||||||
|
|
||||||
/// 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<'db> ProtocolInterface<'db> {
|
impl<'db> ProtocolInterface<'db> {
|
||||||
/// Synthesize a new protocol interface with the given members.
|
/// Synthesize a new protocol interface with the given members.
|
||||||
|
@ -112,11 +105,11 @@ impl<'db> ProtocolInterface<'db> {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
Self::Members(ProtocolInterfaceMembers::new(db, members))
|
Self::new(db, members)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn empty(db: &'db dyn Db) -> Self {
|
fn empty(db: &'db dyn Db) -> Self {
|
||||||
Self::Members(ProtocolInterfaceMembers::new(db, BTreeMap::default()))
|
Self::new(db, BTreeMap::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn members<'a>(
|
pub(super) fn members<'a>(
|
||||||
|
@ -126,16 +119,11 @@ impl<'db> ProtocolInterface<'db> {
|
||||||
where
|
where
|
||||||
'db: 'a,
|
'db: 'a,
|
||||||
{
|
{
|
||||||
match self {
|
self.inner(db).iter().map(|(name, data)| ProtocolMember {
|
||||||
Self::Members(members) => {
|
name,
|
||||||
Either::Left(members.inner(db).iter().map(|(name, data)| ProtocolMember {
|
kind: data.kind,
|
||||||
name,
|
qualifiers: data.qualifiers,
|
||||||
kind: data.kind,
|
})
|
||||||
qualifiers: data.qualifiers,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
Self::SelfReference => Either::Right(std::iter::empty()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn member_by_name<'a>(
|
pub(super) fn member_by_name<'a>(
|
||||||
|
@ -143,29 +131,20 @@ impl<'db> ProtocolInterface<'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
) -> Option<ProtocolMember<'a, 'db>> {
|
) -> Option<ProtocolMember<'a, 'db>> {
|
||||||
match self {
|
self.inner(db).get(name).map(|data| ProtocolMember {
|
||||||
Self::Members(members) => members.inner(db).get(name).map(|data| ProtocolMember {
|
name,
|
||||||
name,
|
kind: data.kind,
|
||||||
kind: data.kind,
|
qualifiers: data.qualifiers,
|
||||||
qualifiers: data.qualifiers,
|
})
|
||||||
}),
|
|
||||||
Self::SelfReference => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return `true` if if all members on `self` are also members of `other`.
|
/// 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.
|
/// 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 {
|
pub(super) fn is_sub_interface_of(self, db: &'db dyn Db, other: Self) -> bool {
|
||||||
match (self, other) {
|
self.inner(db)
|
||||||
(Self::Members(self_members), Self::Members(other_members)) => self_members
|
.keys()
|
||||||
.inner(db)
|
.all(|member_name| other.inner(db).contains_key(member_name))
|
||||||
.keys()
|
|
||||||
.all(|member_name| other_members.inner(db).contains_key(member_name)),
|
|
||||||
_ => {
|
|
||||||
unreachable!("Enclosing protocols should never be a self-reference marker")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return `true` if the types of any of the members match the closure passed in.
|
/// 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))
|
.any(|member| member.any_over_type(db, type_fn))
|
||||||
}
|
}
|
||||||
|
|
||||||
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::new(
|
||||||
Self::Members(members) => Self::Members(ProtocolInterfaceMembers::new(
|
db,
|
||||||
db,
|
self.inner(db)
|
||||||
members
|
.iter()
|
||||||
.inner(db)
|
.map(|(name, data)| (name.clone(), data.normalized_impl(db, visitor)))
|
||||||
.iter()
|
.collect::<BTreeMap<_, _>>(),
|
||||||
.map(|(name, data)| (name.clone(), data.normalized(db)))
|
)
|
||||||
.collect::<BTreeMap<_, _>>(),
|
|
||||||
)),
|
|
||||||
Self::SelfReference => Self::SelfReference,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn materialize(self, db: &'db dyn Db, variance: TypeVarVariance) -> Self {
|
pub(super) fn materialize(self, db: &'db dyn Db, variance: TypeVarVariance) -> Self {
|
||||||
match self {
|
Self::new(
|
||||||
Self::Members(members) => Self::Members(ProtocolInterfaceMembers::new(
|
db,
|
||||||
db,
|
self.inner(db)
|
||||||
members
|
.iter()
|
||||||
.inner(db)
|
.map(|(name, data)| (name.clone(), data.materialize(db, variance)))
|
||||||
.iter()
|
.collect::<BTreeMap<_, _>>(),
|
||||||
.map(|(name, data)| (name.clone(), data.materialize(db, variance)))
|
)
|
||||||
.collect::<BTreeMap<_, _>>(),
|
|
||||||
)),
|
|
||||||
Self::SelfReference => Self::SelfReference,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn specialized_and_normalized<'a>(
|
pub(super) fn specialized_and_normalized<'a>(
|
||||||
|
@ -211,22 +182,18 @@ impl<'db> ProtocolInterface<'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
type_mapping: &TypeMapping<'a, 'db>,
|
type_mapping: &TypeMapping<'a, 'db>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
match self {
|
Self::new(
|
||||||
Self::Members(members) => Self::Members(ProtocolInterfaceMembers::new(
|
db,
|
||||||
db,
|
self.inner(db)
|
||||||
members
|
.iter()
|
||||||
.inner(db)
|
.map(|(name, data)| {
|
||||||
.iter()
|
(
|
||||||
.map(|(name, data)| {
|
name.clone(),
|
||||||
(
|
data.apply_type_mapping(db, type_mapping).normalized(db),
|
||||||
name.clone(),
|
)
|
||||||
data.apply_type_mapping(db, type_mapping).normalized(db),
|
})
|
||||||
)
|
.collect::<BTreeMap<_, _>>(),
|
||||||
})
|
)
|
||||||
.collect::<BTreeMap<_, _>>(),
|
|
||||||
)),
|
|
||||||
Self::SelfReference => Self::SelfReference,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn find_legacy_typevars(
|
pub(super) fn find_legacy_typevars(
|
||||||
|
@ -234,13 +201,8 @@ impl<'db> ProtocolInterface<'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
typevars: &mut FxOrderSet<TypeVarInstance<'db>>,
|
typevars: &mut FxOrderSet<TypeVarInstance<'db>>,
|
||||||
) {
|
) {
|
||||||
match self {
|
for data in self.inner(db).values() {
|
||||||
Self::Members(members) => {
|
data.find_legacy_typevars(db, typevars);
|
||||||
for data in members.inner(db).values() {
|
|
||||||
data.find_legacy_typevars(db, typevars);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self::SelfReference => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -253,8 +215,12 @@ pub(super) struct ProtocolMemberData<'db> {
|
||||||
|
|
||||||
impl<'db> ProtocolMemberData<'db> {
|
impl<'db> ProtocolMemberData<'db> {
|
||||||
fn normalized(&self, db: &'db dyn Db) -> Self {
|
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 {
|
Self {
|
||||||
kind: self.kind.normalized(db),
|
kind: self.kind.normalized_impl(db, visitor),
|
||||||
qualifiers: self.qualifiers,
|
qualifiers: self.qualifiers,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -290,15 +256,17 @@ enum ProtocolMemberKind<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> 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 {
|
match self {
|
||||||
ProtocolMemberKind::Method(callable) => {
|
ProtocolMemberKind::Method(callable) => {
|
||||||
ProtocolMemberKind::Method(callable.normalized(db))
|
ProtocolMemberKind::Method(callable.normalized_impl(db, visitor))
|
||||||
}
|
}
|
||||||
ProtocolMemberKind::Property(property) => {
|
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)
|
(Type::Callable(callable), BoundOnClass::Yes)
|
||||||
if callable.is_function_like(db) =>
|
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
|
// TODO: method members that have `FunctionLiteral` types should be upcast
|
||||||
// to `CallableType` so that two protocols with identical method members
|
// to `CallableType` so that two protocols with identical method members
|
||||||
// are recognized as equivalent.
|
// are recognized as equivalent.
|
||||||
(Type::FunctionLiteral(_function), BoundOnClass::Yes) => {
|
(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 };
|
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)]
|
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||||
|
|
|
@ -15,10 +15,10 @@ use std::{collections::HashMap, slice::Iter};
|
||||||
use itertools::EitherOrBoth;
|
use itertools::EitherOrBoth;
|
||||||
use smallvec::{SmallVec, smallvec};
|
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::semantic_index::definition::Definition;
|
||||||
use crate::types::generics::GenericContext;
|
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 crate::{Db, FxOrderSet};
|
||||||
use ruff_python_ast::{self as ast, name::Name};
|
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::from_overloads(
|
||||||
self.overloads
|
self.overloads
|
||||||
.iter()
|
.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> {
|
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 {
|
Self {
|
||||||
generic_context: self.generic_context.map(|ctx| ctx.normalized(db)),
|
generic_context: self
|
||||||
inherited_generic_context: self.inherited_generic_context.map(|ctx| ctx.normalized(db)),
|
.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: self
|
||||||
.parameters
|
.parameters
|
||||||
.iter()
|
.iter()
|
||||||
.map(|param| param.normalized(db))
|
.map(|param| param.normalized_impl(db, visitor))
|
||||||
.collect(),
|
.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
|
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)]
|
#[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.
|
/// Normalize nested unions and intersections in the annotated type, if any.
|
||||||
///
|
///
|
||||||
/// See [`Type::normalized`] for more details.
|
/// 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 {
|
let Parameter {
|
||||||
annotated_type,
|
annotated_type,
|
||||||
kind,
|
kind,
|
||||||
|
@ -1311,7 +1284,7 @@ impl<'db> Parameter<'db> {
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
// Ensure unions and intersections are ordered in the annotated type (if there is one)
|
// 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 parameter names are stripped from positional-only, variadic and keyword-variadic parameters.
|
||||||
// Ensure that we only record whether a parameter *has* a default
|
// Ensure that we only record whether a parameter *has* a default
|
||||||
|
@ -1444,14 +1417,6 @@ impl<'db> Parameter<'db> {
|
||||||
ParameterKind::Variadic { .. } | ParameterKind::KeywordVariadic { .. } => None,
|
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)]
|
#[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::place::PlaceAndQualifiers;
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
ClassType, DynamicType, KnownClass, MemberLookupPolicy, Type, TypeMapping, TypeRelation,
|
ClassType, DynamicType, KnownClass, MemberLookupPolicy, Type, TypeMapping, TypeRelation,
|
||||||
TypeVarInstance,
|
TypeVarInstance, TypeVisitor,
|
||||||
};
|
};
|
||||||
use crate::{Db, FxOrderSet};
|
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 {
|
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 {
|
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()),
|
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::class::{ClassType, KnownClass};
|
||||||
use crate::types::{
|
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::util::subscript::{Nth, OutOfBoundsError, PyIndex, PySlice, StepSizeZeroError};
|
||||||
use crate::{Db, FxOrderSet};
|
use crate::{Db, FxOrderSet};
|
||||||
|
@ -174,8 +175,12 @@ impl<'db> TupleType<'db> {
|
||||||
///
|
///
|
||||||
/// See [`Type::normalized`] for more details.
|
/// See [`Type::normalized`] for more details.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn normalized(self, db: &'db dyn Db) -> Option<Self> {
|
pub(crate) fn normalized_impl(
|
||||||
TupleType::new(db, self.tuple(db).normalized(db))
|
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> {
|
pub(crate) fn materialize(self, db: &'db dyn Db, variance: TypeVarVariance) -> Option<Self> {
|
||||||
|
@ -327,8 +332,8 @@ impl<'db> FixedLengthTuple<Type<'db>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn normalized(&self, db: &'db dyn Db) -> Self {
|
fn normalized_impl(&self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
|
||||||
Self::from_elements(self.0.iter().map(|ty| ty.normalized(db)))
|
Self::from_elements(self.0.iter().map(|ty| ty.normalized_impl(db, visitor)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn materialize(&self, db: &'db dyn Db, variance: TypeVarVariance) -> Self {
|
fn materialize(&self, db: &'db dyn Db, variance: TypeVarVariance) -> Self {
|
||||||
|
@ -640,14 +645,16 @@ impl<'db> VariableLengthTuple<Type<'db>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn normalized(&self, db: &'db dyn Db) -> TupleSpec<'db> {
|
fn normalized_impl(&self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> TupleSpec<'db> {
|
||||||
Self::mixed(
|
let prefix = self
|
||||||
self.prenormalized_prefix_elements(db, None)
|
.prenormalized_prefix_elements(db, None)
|
||||||
.map(|ty| ty.normalized(db)),
|
.map(|ty| ty.normalized_impl(db, visitor))
|
||||||
self.variable.normalized(db),
|
.collect::<Vec<_>>();
|
||||||
self.prenormalized_suffix_elements(db, None)
|
let suffix = self
|
||||||
.map(|ty| ty.normalized(db)),
|
.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> {
|
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 {
|
match self {
|
||||||
Tuple::Fixed(tuple) => Tuple::Fixed(tuple.normalized(db)),
|
Tuple::Fixed(tuple) => Tuple::Fixed(tuple.normalized_impl(db, visitor)),
|
||||||
Tuple::Variable(tuple) => tuple.normalized(db),
|
Tuple::Variable(tuple) => tuple.normalized_impl(db, visitor),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue