[ty] Rewrite Type::any_over_type using a new generalised TypeVisitor trait (#19094)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks-instrumented (push) Blocked by required conditions
CI / benchmarks-walltime (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run

This commit is contained in:
Alex Waygood 2025-07-03 19:19:23 +01:00 committed by GitHub
parent 77a5c5ac80
commit 333191b7f7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 769 additions and 209 deletions

View file

@ -43,6 +43,7 @@ pub mod pull_types;
type FxOrderSet<V> = ordermap::set::OrderSet<V, BuildHasherDefault<FxHasher>>;
type FxIndexMap<K, V> = indexmap::IndexMap<K, V, BuildHasherDefault<FxHasher>>;
type FxIndexSet<V> = indexmap::IndexSet<V, BuildHasherDefault<FxHasher>>;
/// Returns the default registry with all known semantic lints.
pub fn default_lint_registry() -> &'static LintRegistry {

View file

@ -19,7 +19,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(crate) use self::cyclic::TypeTransformer;
pub use self::diagnostic::TypeCheckDiagnostics;
pub(crate) use self::diagnostic::register_lints;
pub(crate) use self::infer::{
@ -42,12 +42,15 @@ use crate::types::diagnostic::{INVALID_TYPE_FORM, UNSUPPORTED_BOOL_CONVERSION};
use crate::types::function::{
DataclassTransformerParams, FunctionSpans, FunctionType, KnownFunction,
};
use crate::types::generics::{GenericContext, PartialSpecialization, Specialization};
use crate::types::generics::{
GenericContext, PartialSpecialization, Specialization, walk_generic_context,
walk_partial_specialization, walk_specialization,
};
pub use crate::types::ide_support::all_members;
use crate::types::infer::infer_unpack_types;
use crate::types::mro::{Mro, MroError, MroIterator};
pub(crate) use crate::types::narrow::infer_narrowing_constraint;
use crate::types::signatures::{Parameter, ParameterForm, Parameters};
use crate::types::signatures::{Parameter, ParameterForm, Parameters, walk_signature};
use crate::types::tuple::{TupleSpec, TupleType};
pub use crate::util::diagnostics::add_inferred_python_version_hint_to_diagnostic;
use crate::{Db, FxOrderSet, Module, Program};
@ -79,6 +82,7 @@ mod subclass_of;
mod tuple;
mod type_ordering;
mod unpacker;
mod visitor;
mod definition;
#[cfg(test)]
@ -368,6 +372,19 @@ pub struct PropertyInstanceType<'db> {
setter: Option<Type<'db>>,
}
fn walk_property_instance_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
property: PropertyInstanceType<'db>,
visitor: &mut V,
) {
if let Some(getter) = property.getter(db) {
visitor.visit_type(db, getter);
}
if let Some(setter) = property.setter(db) {
visitor.visit_type(db, setter);
}
}
// The Salsa heap is tracked separately.
impl get_size2::GetSize for PropertyInstanceType<'_> {}
@ -382,7 +399,7 @@ impl<'db> PropertyInstanceType<'db> {
Self::new(db, getter, setter)
}
fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeTransformer<'db>) -> Self {
Self::new(
db,
self.getter(db).map(|ty| ty.normalized_impl(db, visitor)),
@ -410,14 +427,6 @@ impl<'db> PropertyInstanceType<'db> {
self.setter(db).map(|ty| ty.materialize(db, variance)),
)
}
fn any_over_type(self, db: &'db dyn Db, type_fn: &dyn Fn(Type<'db>) -> bool) -> bool {
self.getter(db)
.is_some_and(|ty| ty.any_over_type(db, type_fn))
|| self
.setter(db)
.is_some_and(|ty| ty.any_over_type(db, type_fn))
}
}
bitflags! {
@ -751,110 +760,6 @@ impl<'db> Type<'db> {
}
}
/// 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) {
return true;
}
match self {
Self::AlwaysFalsy
| Self::AlwaysTruthy
| Self::Never
| Self::BooleanLiteral(_)
| Self::BytesLiteral(_)
| Self::ModuleLiteral(_)
| Self::FunctionLiteral(_)
| Self::ClassLiteral(_)
| Self::SpecialForm(_)
| Self::KnownInstance(_)
| Self::StringLiteral(_)
| Self::IntLiteral(_)
| Self::LiteralString
| Self::Dynamic(_)
| Self::BoundMethod(_)
| Self::WrapperDescriptor(_)
| Self::MethodWrapper(_)
| Self::DataclassDecorator(_)
| Self::DataclassTransformer(_) => false,
Self::GenericAlias(generic) => generic
.specialization(db)
.types(db)
.iter()
.copied()
.any(|ty| ty.any_over_type(db, type_fn)),
Self::Callable(callable) => {
let signatures = callable.signatures(db);
signatures.iter().any(|signature| {
signature.parameters().iter().any(|param| {
param
.annotated_type()
.is_some_and(|ty| ty.any_over_type(db, type_fn))
}) || signature
.return_ty
.is_some_and(|ty| ty.any_over_type(db, type_fn))
})
}
Self::SubclassOf(subclass_of) => {
Type::from(subclass_of.subclass_of()).any_over_type(db, type_fn)
}
Self::TypeVar(typevar) => match typevar.bound_or_constraints(db) {
None => false,
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
bound.any_over_type(db, type_fn)
}
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => constraints
.elements(db)
.iter()
.any(|constraint| constraint.any_over_type(db, type_fn)),
},
Self::BoundSuper(bound_super) => {
Type::from(bound_super.pivot_class(db)).any_over_type(db, type_fn)
|| Type::from(bound_super.owner(db)).any_over_type(db, type_fn)
}
Self::Tuple(tuple) => tuple
.tuple(db)
.all_elements()
.any(|ty| ty.any_over_type(db, type_fn)),
Self::Union(union) => union
.elements(db)
.iter()
.any(|ty| ty.any_over_type(db, type_fn)),
Self::Intersection(intersection) => {
intersection
.positive(db)
.iter()
.any(|ty| ty.any_over_type(db, type_fn))
|| intersection
.negative(db)
.iter()
.any(|ty| ty.any_over_type(db, type_fn))
}
Self::ProtocolInstance(protocol) => protocol.any_over_type(db, type_fn),
Self::PropertyInstance(property) => property.any_over_type(db, type_fn),
Self::NominalInstance(instance) => match instance.class {
ClassType::NonGeneric(_) => false,
ClassType::Generic(generic) => generic
.specialization(db)
.types(db)
.iter()
.any(|ty| ty.any_over_type(db, type_fn)),
},
Self::TypeIs(type_is) => type_is.return_type(db).any_over_type(db, type_fn),
}
}
pub const fn into_class_literal(self) -> Option<ClassLiteral<'db>> {
match self {
Type::ClassLiteral(class_type) => Some(class_type),
@ -1068,12 +973,16 @@ 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();
let mut visitor = TypeTransformer::default();
self.normalized_impl(db, &mut visitor)
}
#[must_use]
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
pub(crate) fn normalized_impl(
self,
db: &'db dyn Db,
visitor: &mut TypeTransformer<'db>,
) -> Self {
match self {
Type::Union(union) => {
visitor.visit(self, |v| Type::Union(union.normalized_impl(db, v)))
@ -5733,6 +5642,22 @@ pub enum TypeMapping<'a, 'db> {
PromoteLiterals,
}
fn walk_type_mapping<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
mapping: &TypeMapping<'_, 'db>,
visitor: &mut V,
) {
match mapping {
TypeMapping::Specialization(specialization) => {
walk_specialization(db, *specialization, visitor);
}
TypeMapping::PartialSpecialization(specialization) => {
walk_partial_specialization(db, specialization, visitor);
}
TypeMapping::PromoteLiterals => {}
}
}
impl<'db> TypeMapping<'_, 'db> {
fn to_owned(&self) -> TypeMapping<'db, 'db> {
match self {
@ -5746,7 +5671,7 @@ impl<'db> TypeMapping<'_, 'db> {
}
}
fn normalized_impl(&self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
fn normalized_impl(&self, db: &'db dyn Db, visitor: &mut TypeTransformer<'db>) -> Self {
match self {
TypeMapping::Specialization(specialization) => {
TypeMapping::Specialization(specialization.normalized_impl(db, visitor))
@ -5797,8 +5722,27 @@ pub enum KnownInstanceType<'db> {
TypeAliasType(TypeAliasType<'db>),
}
fn walk_known_instance_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
known_instance: KnownInstanceType<'db>,
visitor: &mut V,
) {
match known_instance {
KnownInstanceType::SubscriptedProtocol(context)
| KnownInstanceType::SubscriptedGeneric(context) => {
walk_generic_context(db, context, visitor);
}
KnownInstanceType::TypeVar(typevar) => {
visitor.visit_type_var_type(db, typevar);
}
KnownInstanceType::TypeAliasType(type_alias) => {
visitor.visit_type_alias_type(db, type_alias);
}
}
}
impl<'db> KnownInstanceType<'db> {
fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeTransformer<'db>) -> Self {
match self {
Self::SubscriptedProtocol(context) => {
Self::SubscriptedProtocol(context.normalized_impl(db, visitor))
@ -6169,6 +6113,19 @@ pub struct TypeVarInstance<'db> {
// The Salsa heap is tracked separately.
impl get_size2::GetSize for TypeVarInstance<'_> {}
fn walk_type_var_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
type_var: TypeVarInstance<'db>,
visitor: &mut V,
) {
if let Some(bounds) = type_var.bound_or_constraints(db) {
walk_type_var_bounds(db, bounds, visitor);
}
if let Some(default_type) = type_var.default_ty(db) {
visitor.visit_type(db, default_type);
}
}
impl<'db> TypeVarInstance<'db> {
pub(crate) fn is_legacy(self, db: &'db dyn Db) -> bool {
matches!(self.kind(db), TypeVarKind::Legacy)
@ -6190,7 +6147,11 @@ impl<'db> TypeVarInstance<'db> {
}
}
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
pub(crate) fn normalized_impl(
self,
db: &'db dyn Db,
visitor: &mut TypeTransformer<'db>,
) -> Self {
Self::new(
db,
self.name(db),
@ -6245,8 +6206,21 @@ pub enum TypeVarBoundOrConstraints<'db> {
Constraints(UnionType<'db>),
}
fn walk_type_var_bounds<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
bounds: TypeVarBoundOrConstraints<'db>,
visitor: &mut V,
) {
match bounds {
TypeVarBoundOrConstraints::UpperBound(bound) => visitor.visit_type(db, bound),
TypeVarBoundOrConstraints::Constraints(constraints) => {
visitor.visit_union_type(db, constraints);
}
}
}
impl<'db> TypeVarBoundOrConstraints<'db> {
fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeTransformer<'db>) -> Self {
match self {
TypeVarBoundOrConstraints::UpperBound(bound) => {
TypeVarBoundOrConstraints::UpperBound(bound.normalized_impl(db, visitor))
@ -7149,6 +7123,15 @@ pub struct BoundMethodType<'db> {
// The Salsa heap is tracked separately.
impl get_size2::GetSize for BoundMethodType<'_> {}
fn walk_bound_method_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
method: BoundMethodType<'db>,
visitor: &mut V,
) {
visitor.visit_function_type(db, method.function(db));
visitor.visit_type(db, method.self_instance(db));
}
impl<'db> BoundMethodType<'db> {
pub(crate) fn into_callable_type(self, db: &'db dyn Db) -> Type<'db> {
Type::Callable(CallableType::new(
@ -7164,7 +7147,7 @@ impl<'db> BoundMethodType<'db> {
))
}
fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeTransformer<'db>) -> Self {
Self::new(
db,
self.function(db).normalized_impl(db, visitor),
@ -7214,6 +7197,16 @@ pub struct CallableType<'db> {
is_function_like: bool,
}
pub(super) fn walk_callable_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
ty: CallableType<'db>,
visitor: &mut V,
) {
for signature in &ty.signatures(db).overloads {
walk_signature(db, signature, visitor);
}
}
// The Salsa heap is tracked separately.
impl get_size2::GetSize for CallableType<'_> {}
@ -7271,7 +7264,7 @@ impl<'db> CallableType<'db> {
/// Return a "normalized" version of this `Callable` type.
///
/// See [`Type::normalized`] for more details.
fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeTransformer<'db>) -> Self {
CallableType::new(
db,
self.signatures(db).normalized_impl(db, visitor),
@ -7338,6 +7331,30 @@ pub enum MethodWrapperKind<'db> {
StrStartswith(StringLiteralType<'db>),
}
pub(super) fn walk_method_wrapper_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
method_wrapper: MethodWrapperKind<'db>,
visitor: &mut V,
) {
match method_wrapper {
MethodWrapperKind::FunctionTypeDunderGet(function) => {
visitor.visit_function_type(db, function);
}
MethodWrapperKind::FunctionTypeDunderCall(function) => {
visitor.visit_function_type(db, function);
}
MethodWrapperKind::PropertyDunderGet(property) => {
visitor.visit_property_instance_type(db, property);
}
MethodWrapperKind::PropertyDunderSet(property) => {
visitor.visit_property_instance_type(db, property);
}
MethodWrapperKind::StrStartswith(string_literal) => {
visitor.visit_type(db, Type::StringLiteral(string_literal));
}
}
}
impl<'db> MethodWrapperKind<'db> {
fn has_relation_to(self, db: &'db dyn Db, other: Self, relation: TypeRelation) -> bool {
match (self, other) {
@ -7405,7 +7422,7 @@ impl<'db> MethodWrapperKind<'db> {
}
}
fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeTransformer<'db>) -> Self {
match self {
MethodWrapperKind::FunctionTypeDunderGet(function) => {
MethodWrapperKind::FunctionTypeDunderGet(function.normalized_impl(db, visitor))
@ -7512,6 +7529,14 @@ pub struct PEP695TypeAliasType<'db> {
// The Salsa heap is tracked separately.
impl get_size2::GetSize for PEP695TypeAliasType<'_> {}
fn walk_pep_695_type_alias<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
type_alias: PEP695TypeAliasType<'db>,
visitor: &mut V,
) {
visitor.visit_type(db, type_alias.value_type(db));
}
#[salsa::tracked]
impl<'db> PEP695TypeAliasType<'db> {
pub(crate) fn definition(self, db: &'db dyn Db) -> Definition<'db> {
@ -7531,7 +7556,7 @@ impl<'db> PEP695TypeAliasType<'db> {
definition_expression_type(db, definition, &type_alias_stmt_node.value)
}
fn normalized_impl(self, _db: &'db dyn Db, _visitor: &mut TypeVisitor<'db>) -> Self {
fn normalized_impl(self, _db: &'db dyn Db, _visitor: &mut TypeTransformer<'db>) -> Self {
self
}
}
@ -7551,8 +7576,16 @@ pub struct BareTypeAliasType<'db> {
// The Salsa heap is tracked separately.
impl get_size2::GetSize for BareTypeAliasType<'_> {}
fn walk_bare_type_alias<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
type_alias: BareTypeAliasType<'db>,
visitor: &mut V,
) {
visitor.visit_type(db, type_alias.value(db));
}
impl<'db> BareTypeAliasType<'db> {
fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeTransformer<'db>) -> Self {
Self::new(
db,
self.name(db),
@ -7570,8 +7603,27 @@ pub enum TypeAliasType<'db> {
Bare(BareTypeAliasType<'db>),
}
fn walk_type_alias_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
type_alias: TypeAliasType<'db>,
visitor: &mut V,
) {
match type_alias {
TypeAliasType::PEP695(type_alias) => {
walk_pep_695_type_alias(db, type_alias, visitor);
}
TypeAliasType::Bare(type_alias) => {
walk_bare_type_alias(db, type_alias, visitor);
}
}
}
impl<'db> TypeAliasType<'db> {
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
pub(crate) fn normalized_impl(
self,
db: &'db dyn Db,
visitor: &mut TypeTransformer<'db>,
) -> Self {
match self {
TypeAliasType::PEP695(type_alias) => {
TypeAliasType::PEP695(type_alias.normalized_impl(db, visitor))
@ -7618,6 +7670,16 @@ pub struct UnionType<'db> {
pub elements: Box<[Type<'db>]>,
}
pub(crate) fn walk_union<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
union: UnionType<'db>,
visitor: &mut V,
) {
for element in union.elements(db) {
visitor.visit_type(db, *element);
}
}
// The Salsa heap is tracked separately.
impl get_size2::GetSize for UnionType<'_> {}
@ -7787,10 +7849,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())
self.normalized_impl(db, &mut TypeTransformer::default())
}
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
pub(crate) fn normalized_impl(
self,
db: &'db dyn Db,
visitor: &mut TypeTransformer<'db>,
) -> Self {
let mut new_elements: Vec<Type<'db>> = self
.elements(db)
.iter()
@ -7841,6 +7907,19 @@ pub struct IntersectionType<'db> {
// The Salsa heap is tracked separately.
impl get_size2::GetSize for IntersectionType<'_> {}
pub(super) fn walk_intersection_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
intersection: IntersectionType<'db>,
visitor: &mut V,
) {
for element in intersection.positive(db) {
visitor.visit_type(db, *element);
}
for element in intersection.negative(db) {
visitor.visit_type(db, *element);
}
}
impl<'db> IntersectionType<'db> {
/// Return a new `IntersectionType` instance with the positive and negative types sorted
/// according to a canonical ordering, and other normalizations applied to each element as applicable.
@ -7848,15 +7927,19 @@ 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();
let mut visitor = TypeTransformer::default();
self.normalized_impl(db, &mut visitor)
}
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
pub(crate) fn normalized_impl(
self,
db: &'db dyn Db,
visitor: &mut TypeTransformer<'db>,
) -> Self {
fn normalized_set<'db>(
db: &'db dyn Db,
elements: &FxOrderSet<Type<'db>>,
visitor: &mut TypeVisitor<'db>,
visitor: &mut TypeTransformer<'db>,
) -> FxOrderSet<Type<'db>> {
let mut elements: FxOrderSet<Type<'db>> = elements
.iter()
@ -8109,7 +8192,7 @@ pub enum SuperOwnerKind<'db> {
}
impl<'db> SuperOwnerKind<'db> {
fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeTransformer<'db>) -> Self {
match self {
SuperOwnerKind::Dynamic(dynamic) => SuperOwnerKind::Dynamic(dynamic.normalized()),
SuperOwnerKind::Class(class) => {
@ -8197,6 +8280,15 @@ pub struct BoundSuperType<'db> {
// The Salsa heap is tracked separately.
impl get_size2::GetSize for BoundSuperType<'_> {}
fn walk_bound_super_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
bound_super: BoundSuperType<'db>,
visitor: &mut V,
) {
visitor.visit_type(db, bound_super.pivot_class(db).into());
visitor.visit_type(db, bound_super.owner(db).into_type());
}
impl<'db> BoundSuperType<'db> {
/// Attempts to build a `Type::BoundSuper` based on the given `pivot_class` and `owner`.
///
@ -8358,7 +8450,11 @@ impl<'db> BoundSuperType<'db> {
}
}
pub(super) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
pub(super) fn normalized_impl(
self,
db: &'db dyn Db,
visitor: &mut TypeTransformer<'db>,
) -> Self {
Self::new(
db,
self.pivot_class(db).normalized_impl(db, visitor),
@ -8375,6 +8471,14 @@ pub struct TypeIsType<'db> {
place_info: Option<(ScopeId<'db>, ScopedPlaceId)>,
}
fn walk_typeis_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
typeis_type: TypeIsType<'db>,
visitor: &mut V,
) {
visitor.visit_type(db, typeis_type.return_type(db));
}
// The Salsa heap is tracked separately.
impl get_size2::GetSize for TypeIsType<'_> {}

View file

@ -15,14 +15,14 @@ use crate::semantic_index::{DeclarationWithConstraint, SemanticIndex};
use crate::types::context::InferContext;
use crate::types::diagnostic::{INVALID_LEGACY_TYPE_VARIABLE, INVALID_TYPE_ALIAS_TYPE};
use crate::types::function::{DataclassTransformerParams, KnownFunction};
use crate::types::generics::{GenericContext, Specialization};
use crate::types::generics::{GenericContext, Specialization, walk_specialization};
use crate::types::infer::nearest_enclosing_class;
use crate::types::signatures::{CallableSignature, Parameter, Parameters, Signature};
use crate::types::tuple::TupleType;
use crate::types::{
BareTypeAliasType, Binding, BoundSuperError, BoundSuperType, CallableType, DataclassParams,
KnownInstanceType, TypeAliasType, TypeMapping, TypeRelation, TypeVarBoundOrConstraints,
TypeVarInstance, TypeVarKind, TypeVisitor, infer_definition_types,
KnownInstanceType, TypeAliasType, TypeMapping, TypeRelation, TypeTransformer,
TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind, infer_definition_types,
};
use crate::{
Db, FxOrderSet, KnownModule, Program,
@ -177,11 +177,23 @@ pub struct GenericAlias<'db> {
pub(crate) specialization: Specialization<'db>,
}
pub(super) fn walk_generic_alias<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
alias: GenericAlias<'db>,
visitor: &mut V,
) {
walk_specialization(db, alias.specialization(db), visitor);
}
// The Salsa heap is tracked separately.
impl get_size2::GetSize for GenericAlias<'_> {}
impl<'db> GenericAlias<'db> {
pub(super) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
pub(super) fn normalized_impl(
self,
db: &'db dyn Db,
visitor: &mut TypeTransformer<'db>,
) -> Self {
Self::new(
db,
self.origin(db),
@ -252,7 +264,11 @@ pub enum ClassType<'db> {
#[salsa::tracked]
impl<'db> ClassType<'db> {
pub(super) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
pub(super) fn normalized_impl(
self,
db: &'db dyn Db,
visitor: &mut TypeTransformer<'db>,
) -> Self {
match self {
Self::NonGeneric(_) => self,
Self::Generic(generic) => Self::Generic(generic.normalized_impl(db, visitor)),

View file

@ -2,7 +2,7 @@ use crate::Db;
use crate::types::generics::Specialization;
use crate::types::{
ClassType, DynamicType, KnownClass, KnownInstanceType, MroError, MroIterator, SpecialFormType,
Type, TypeMapping, TypeVisitor, todo_type,
Type, TypeMapping, TypeTransformer, todo_type,
};
/// Enumeration of the possible kinds of types we allow in class bases.
@ -31,7 +31,11 @@ impl<'db> ClassBase<'db> {
Self::Dynamic(DynamicType::Unknown)
}
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
pub(crate) fn normalized_impl(
self,
db: &'db dyn Db,
visitor: &mut TypeTransformer<'db>,
) -> Self {
match self {
Self::Dynamic(dynamic) => Self::Dynamic(dynamic.normalized()),
Self::Class(class) => Self::Class(class.normalized_impl(db, visitor)),

View file

@ -1,12 +1,12 @@
use crate::FxOrderSet;
use crate::FxIndexSet;
use crate::types::Type;
#[derive(Debug, Default)]
pub(crate) struct TypeVisitor<'db> {
seen: FxOrderSet<Type<'db>>,
pub(crate) struct TypeTransformer<'db> {
seen: FxIndexSet<Type<'db>>,
}
impl<'db> TypeVisitor<'db> {
impl<'db> TypeTransformer<'db> {
pub(crate) fn visit(
&mut self,
ty: Type<'db>,

View file

@ -70,12 +70,13 @@ use crate::types::diagnostic::{
report_bad_argument_to_get_protocol_members,
report_runtime_check_against_non_runtime_checkable_protocol,
};
use crate::types::generics::GenericContext;
use crate::types::generics::{GenericContext, walk_generic_context};
use crate::types::narrow::ClassInfoConstraintFunction;
use crate::types::signatures::{CallableSignature, Signature};
use crate::types::visitor::any_over_type;
use crate::types::{
BoundMethodType, CallableType, DynamicType, KnownClass, Type, TypeMapping, TypeRelation,
TypeVarInstance, TypeVisitor,
TypeTransformer, TypeVarInstance, walk_type_mapping,
};
use crate::{Db, FxOrderSet, ModuleName, resolve_module};
@ -421,6 +422,16 @@ pub struct FunctionLiteral<'db> {
inherited_generic_context: Option<GenericContext<'db>>,
}
fn walk_function_literal<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
function: FunctionLiteral<'db>,
visitor: &mut V,
) {
if let Some(context) = function.inherited_generic_context(db) {
walk_generic_context(db, context, visitor);
}
}
#[salsa::tracked]
impl<'db> FunctionLiteral<'db> {
fn with_inherited_generic_context(
@ -545,7 +556,7 @@ impl<'db> FunctionLiteral<'db> {
}))
}
fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeTransformer<'db>) -> Self {
let context = self
.inherited_generic_context(db)
.map(|ctx| ctx.normalized_impl(db, visitor));
@ -570,6 +581,17 @@ pub struct FunctionType<'db> {
// The Salsa heap is tracked separately.
impl get_size2::GetSize for FunctionType<'_> {}
pub(super) fn walk_function_type<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
function: FunctionType<'db>,
visitor: &mut V,
) {
walk_function_literal(db, function.literal(db), visitor);
for mapping in function.type_mappings(db) {
walk_type_mapping(db, mapping, visitor);
}
}
#[salsa::tracked]
impl<'db> FunctionType<'db> {
pub(crate) fn with_inherited_generic_context(
@ -819,11 +841,15 @@ impl<'db> FunctionType<'db> {
}
pub(crate) fn normalized(self, db: &'db dyn Db) -> Self {
let mut visitor = TypeVisitor::default();
let mut visitor = TypeTransformer::default();
self.normalized_impl(db, &mut visitor)
}
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
pub(crate) fn normalized_impl(
self,
db: &'db dyn Db,
visitor: &mut TypeTransformer<'db>,
) -> Self {
let mappings: Box<_> = self
.type_mappings(db)
.iter()
@ -1148,8 +1174,8 @@ impl KnownFunction {
let contains_unknown_or_todo =
|ty| matches!(ty, Type::Dynamic(dynamic) if dynamic != DynamicType::Any);
if source_type.is_equivalent_to(db, *casted_type)
&& !casted_type.any_over_type(db, &|ty| contains_unknown_or_todo(ty))
&& !source_type.any_over_type(db, &|ty| contains_unknown_or_todo(ty))
&& !any_over_type(db, *source_type, &contains_unknown_or_todo)
&& !any_over_type(db, *casted_type, &contains_unknown_or_todo)
{
let builder = context.report_lint(&REDUNDANT_CAST, call_expression)?;
builder.into_diagnostic(format_args!(

View file

@ -10,8 +10,8 @@ use crate::types::instance::{NominalInstanceType, Protocol, ProtocolInstanceType
use crate::types::signatures::{Parameter, Parameters, Signature};
use crate::types::tuple::{TupleSpec, TupleType};
use crate::types::{
KnownInstanceType, Type, TypeMapping, TypeRelation, TypeVarBoundOrConstraints, TypeVarInstance,
TypeVarVariance, TypeVisitor, UnionType, declaration_type,
KnownInstanceType, Type, TypeMapping, TypeRelation, TypeTransformer, TypeVarBoundOrConstraints,
TypeVarInstance, TypeVarVariance, UnionType, declaration_type,
};
use crate::{Db, FxOrderSet};
@ -30,6 +30,16 @@ pub struct GenericContext<'db> {
pub(crate) variables: FxOrderSet<TypeVarInstance<'db>>,
}
pub(super) fn walk_generic_context<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
context: GenericContext<'db>,
visitor: &mut V,
) {
for typevar in context.variables(db) {
visitor.visit_type_var_type(db, *typevar);
}
}
// The Salsa heap is tracked separately.
impl get_size2::GetSize for GenericContext<'_> {}
@ -233,7 +243,11 @@ impl<'db> GenericContext<'db> {
Specialization::new(db, self, expanded.into_boxed_slice(), None)
}
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
pub(crate) fn normalized_impl(
self,
db: &'db dyn Db,
visitor: &mut TypeTransformer<'db>,
) -> Self {
let variables: FxOrderSet<_> = self
.variables(db)
.iter()
@ -279,6 +293,20 @@ pub struct Specialization<'db> {
tuple_inner: Option<TupleType<'db>>,
}
pub(super) fn walk_specialization<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
specialization: Specialization<'db>,
visitor: &mut V,
) {
walk_generic_context(db, specialization.generic_context(db), visitor);
for ty in specialization.types(db) {
visitor.visit_type(db, *ty);
}
if let Some(tuple) = specialization.tuple_inner(db) {
visitor.visit_tuple_type(db, tuple);
}
}
impl<'db> Specialization<'db> {
/// Returns the tuple spec for a specialization of the `tuple` class.
pub(crate) fn tuple(self, db: &'db dyn Db) -> &'db TupleSpec<'db> {
@ -376,7 +404,11 @@ impl<'db> Specialization<'db> {
Specialization::new(db, self.generic_context(db), types, None)
}
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
pub(crate) fn normalized_impl(
self,
db: &'db dyn Db,
visitor: &mut TypeTransformer<'db>,
) -> Self {
let types: Box<[_]> = self
.types(db)
.iter()
@ -518,6 +550,17 @@ pub struct PartialSpecialization<'a, 'db> {
types: Cow<'a, [Type<'db>]>,
}
pub(super) fn walk_partial_specialization<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
specialization: &PartialSpecialization<'_, 'db>,
visitor: &mut V,
) {
walk_generic_context(db, specialization.generic_context, visitor);
for ty in &*specialization.types {
visitor.visit_type(db, *ty);
}
}
impl<'db> PartialSpecialization<'_, 'db> {
/// Returns the type that a typevar is mapped to, or None if the typevar isn't part of this
/// mapping.
@ -536,7 +579,7 @@ impl<'db> PartialSpecialization<'_, 'db> {
pub(crate) fn normalized_impl(
&self,
db: &'db dyn Db,
visitor: &mut TypeVisitor<'db>,
visitor: &mut TypeTransformer<'db>,
) -> PartialSpecialization<'db, 'db> {
let generic_context = self.generic_context.normalized_impl(db, visitor);
let types: Cow<_> = self

View file

@ -5,8 +5,9 @@ use std::marker::PhantomData;
use super::protocol_class::ProtocolInterface;
use super::{ClassType, KnownClass, SubclassOfType, Type, TypeVarVariance};
use crate::place::PlaceAndQualifiers;
use crate::types::protocol_class::walk_protocol_interface;
use crate::types::tuple::TupleType;
use crate::types::{DynamicType, TypeMapping, TypeRelation, TypeVarInstance, TypeVisitor};
use crate::types::{DynamicType, TypeMapping, TypeRelation, TypeTransformer, TypeVarInstance};
use crate::{Db, FxOrderSet};
pub(super) use synthesized_protocol::SynthesizedProtocolType;
@ -44,7 +45,7 @@ impl<'db> Type<'db> {
SynthesizedProtocolType::new(
db,
ProtocolInterface::with_property_members(db, members),
&mut TypeVisitor::default(),
&mut TypeTransformer::default(),
),
))
}
@ -74,6 +75,14 @@ pub struct NominalInstanceType<'db> {
_phantom: PhantomData<()>,
}
pub(super) fn walk_nominal_instance_type<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
nominal: NominalInstanceType<'db>,
visitor: &mut V,
) {
visitor.visit_type(db, nominal.class.into());
}
impl<'db> NominalInstanceType<'db> {
// Keep this method private, so that the only way of constructing `NominalInstanceType`
// instances is through the `Type::instance` constructor function.
@ -84,7 +93,11 @@ impl<'db> NominalInstanceType<'db> {
}
}
pub(super) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
pub(super) fn normalized_impl(
self,
db: &'db dyn Db,
visitor: &mut TypeTransformer<'db>,
) -> Self {
Self::from_class(self.class.normalized_impl(db, visitor))
}
@ -160,6 +173,14 @@ pub struct ProtocolInstanceType<'db> {
_phantom: PhantomData<()>,
}
pub(super) fn walk_protocol_instance_type<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
protocol: ProtocolInstanceType<'db>,
visitor: &mut V,
) {
walk_protocol_interface(db, protocol.inner.interface(db), visitor);
}
impl<'db> ProtocolInstanceType<'db> {
// Keep this method private, so that the only way of constructing `ProtocolInstanceType`
// instances is through the `Type::instance` constructor function.
@ -205,7 +226,7 @@ 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();
let mut visitor = TypeTransformer::default();
self.normalized_impl(db, &mut visitor)
}
@ -215,7 +236,7 @@ impl<'db> ProtocolInstanceType<'db> {
pub(super) fn normalized_impl(
self,
db: &'db dyn Db,
visitor: &mut TypeVisitor<'db>,
visitor: &mut TypeTransformer<'db>,
) -> Type<'db> {
let object = KnownClass::Object.to_instance(db);
if object.satisfies_protocol(db, self, TypeRelation::Subtyping) {
@ -229,15 +250,6 @@ impl<'db> ProtocolInstanceType<'db> {
}
}
/// Return `true` if the types of any of the members match the closure passed in.
pub(super) fn any_over_type(
self,
db: &'db dyn Db,
type_fn: &dyn Fn(Type<'db>) -> bool,
) -> bool {
self.inner.interface(db).any_over_type(db, type_fn)
}
/// Return `true` if this protocol type has the given type relation to the protocol `other`.
///
/// TODO: consider the types of the members as well as their existence
@ -348,7 +360,7 @@ impl<'db> Protocol<'db> {
mod synthesized_protocol {
use crate::types::protocol_class::ProtocolInterface;
use crate::types::{TypeMapping, TypeVarInstance, TypeVarVariance, TypeVisitor};
use crate::types::{TypeMapping, TypeTransformer, TypeVarInstance, TypeVarVariance};
use crate::{Db, FxOrderSet};
/// A "synthesized" protocol type that is dissociated from a class definition in source code.
@ -369,7 +381,7 @@ mod synthesized_protocol {
pub(super) fn new(
db: &'db dyn Db,
interface: ProtocolInterface<'db>,
visitor: &mut TypeVisitor<'db>,
visitor: &mut TypeTransformer<'db>,
) -> Self {
Self(interface.normalized_impl(db, visitor))
}

View file

@ -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, TypeVisitor,
Type, TypeMapping, TypeQualifiers, TypeRelation, TypeTransformer, TypeVarInstance,
signatures::{Parameter, Parameters},
},
};
@ -76,6 +76,16 @@ pub(super) struct ProtocolInterface<'db> {
impl get_size2::GetSize for ProtocolInterface<'_> {}
pub(super) fn walk_protocol_interface<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
interface: ProtocolInterface<'db>,
visitor: &mut V,
) {
for member in interface.members(db) {
walk_protocol_member(db, &member, visitor);
}
}
impl<'db> ProtocolInterface<'db> {
/// Synthesize a new protocol interface with the given members.
///
@ -152,17 +162,11 @@ impl<'db> ProtocolInterface<'db> {
.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.
pub(super) fn any_over_type(
pub(super) fn normalized_impl(
self,
db: &'db dyn Db,
type_fn: &dyn Fn(Type<'db>) -> bool,
) -> bool {
self.members(db)
.any(|member| member.any_over_type(db, type_fn))
}
pub(super) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
visitor: &mut TypeTransformer<'db>,
) -> Self {
Self::new(
db,
self.inner(db)
@ -220,10 +224,10 @@ pub(super) struct ProtocolMemberData<'db> {
impl<'db> ProtocolMemberData<'db> {
fn normalized(&self, db: &'db dyn Db) -> Self {
self.normalized_impl(db, &mut TypeVisitor::default())
self.normalized_impl(db, &mut TypeTransformer::default())
}
fn normalized_impl(&self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
fn normalized_impl(&self, db: &'db dyn Db, visitor: &mut TypeTransformer<'db>) -> Self {
Self {
kind: self.kind.normalized_impl(db, visitor),
qualifiers: self.qualifiers,
@ -261,7 +265,7 @@ enum ProtocolMemberKind<'db> {
}
impl<'db> ProtocolMemberKind<'db> {
fn normalized_impl(&self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
fn normalized_impl(&self, db: &'db dyn Db, visitor: &mut TypeTransformer<'db>) -> Self {
match self {
ProtocolMemberKind::Method(callable) => {
ProtocolMemberKind::Method(callable.normalized_impl(db, visitor))
@ -324,6 +328,20 @@ pub(super) struct ProtocolMember<'a, 'db> {
qualifiers: TypeQualifiers,
}
fn walk_protocol_member<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
member: &ProtocolMember<'_, 'db>,
visitor: &mut V,
) {
match member.kind {
ProtocolMemberKind::Method(method) => visitor.visit_type(db, method),
ProtocolMemberKind::Property(property) => {
visitor.visit_property_instance_type(db, property);
}
ProtocolMemberKind::Other(ty) => visitor.visit_type(db, ty),
}
}
impl<'a, 'db> ProtocolMember<'a, 'db> {
pub(super) fn name(&self) -> &'a str {
self.name
@ -371,14 +389,6 @@ impl<'a, 'db> ProtocolMember<'a, 'db> {
}
}
}
fn any_over_type(&self, db: &'db dyn Db, type_fn: &dyn Fn(Type<'db>) -> bool) -> bool {
match &self.kind {
ProtocolMemberKind::Method(callable) => callable.any_over_type(db, type_fn),
ProtocolMemberKind::Property(property) => property.any_over_type(db, type_fn),
ProtocolMemberKind::Other(ty) => ty.any_over_type(db, type_fn),
}
}
}
/// Returns `true` if a declaration or binding to a given name in a protocol class body

View file

@ -15,9 +15,9 @@ use std::{collections::HashMap, slice::Iter};
use itertools::EitherOrBoth;
use smallvec::{SmallVec, smallvec};
use super::{DynamicType, Type, TypeVarVariance, TypeVisitor, definition_expression_type};
use super::{DynamicType, Type, TypeTransformer, TypeVarVariance, definition_expression_type};
use crate::semantic_index::definition::Definition;
use crate::types::generics::GenericContext;
use crate::types::generics::{GenericContext, walk_generic_context};
use crate::types::{TypeMapping, TypeRelation, TypeVarInstance, todo_type};
use crate::{Db, FxOrderSet};
use ruff_python_ast::{self as ast, name::Name};
@ -61,7 +61,11 @@ impl<'db> CallableSignature<'db> {
)
}
pub(crate) fn normalized_impl(&self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
pub(crate) fn normalized_impl(
&self,
db: &'db dyn Db,
visitor: &mut TypeTransformer<'db>,
) -> Self {
Self::from_overloads(
self.overloads
.iter()
@ -233,6 +237,29 @@ pub struct Signature<'db> {
pub(crate) return_ty: Option<Type<'db>>,
}
pub(super) fn walk_signature<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
signature: &Signature<'db>,
visitor: &mut V,
) {
if let Some(generic_context) = &signature.generic_context {
walk_generic_context(db, *generic_context, visitor);
}
if let Some(inherited_generic_context) = &signature.inherited_generic_context {
walk_generic_context(db, *inherited_generic_context, visitor);
}
// By default we usually don't visit the type of the default value,
// as it isn't relevant to most things
for parameter in &signature.parameters {
if let Some(ty) = parameter.annotated_type() {
visitor.visit_type(db, ty);
}
}
if let Some(return_ty) = &signature.return_ty {
visitor.visit_type(db, *return_ty);
}
}
impl<'db> Signature<'db> {
pub(crate) fn new(parameters: Parameters<'db>, return_ty: Option<Type<'db>>) -> Self {
Self {
@ -334,7 +361,11 @@ impl<'db> Signature<'db> {
}
}
pub(crate) fn normalized_impl(&self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
pub(crate) fn normalized_impl(
&self,
db: &'db dyn Db,
visitor: &mut TypeTransformer<'db>,
) -> Self {
Self {
generic_context: self
.generic_context
@ -1276,7 +1307,11 @@ 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_impl(&self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
pub(crate) fn normalized_impl(
&self,
db: &'db dyn Db,
visitor: &mut TypeTransformer<'db>,
) -> Self {
let Parameter {
annotated_type,
kind,

View file

@ -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, TypeVisitor,
TypeTransformer, TypeVarInstance,
};
use crate::{Db, FxOrderSet};
@ -16,6 +16,14 @@ pub struct SubclassOfType<'db> {
subclass_of: SubclassOfInner<'db>,
}
pub(super) fn walk_subclass_of_type<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
subclass_of: SubclassOfType<'db>,
visitor: &mut V,
) {
visitor.visit_type(db, Type::from(subclass_of.subclass_of));
}
impl<'db> SubclassOfType<'db> {
/// Construct a new [`Type`] instance representing a given class object (or a given dynamic type)
/// and all possible subclasses of that class object/dynamic type.
@ -171,7 +179,11 @@ impl<'db> SubclassOfType<'db> {
}
}
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
pub(crate) fn normalized_impl(
self,
db: &'db dyn Db,
visitor: &mut TypeTransformer<'db>,
) -> Self {
Self {
subclass_of: self.subclass_of.normalized_impl(db, visitor),
}
@ -228,7 +240,11 @@ impl<'db> SubclassOfInner<'db> {
}
}
pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
pub(crate) fn normalized_impl(
self,
db: &'db dyn Db,
visitor: &mut TypeTransformer<'db>,
) -> Self {
match self {
Self::Class(class) => Self::Class(class.normalized_impl(db, visitor)),
Self::Dynamic(dynamic) => Self::Dynamic(dynamic.normalized()),

View file

@ -24,8 +24,8 @@ use itertools::{Either, EitherOrBoth, Itertools};
use crate::types::class::{ClassType, KnownClass};
use crate::types::{
Type, TypeMapping, TypeRelation, TypeVarInstance, TypeVarVariance, TypeVisitor, UnionBuilder,
UnionType,
Type, TypeMapping, TypeRelation, TypeTransformer, TypeVarInstance, TypeVarVariance,
UnionBuilder, UnionType,
};
use crate::util::subscript::{Nth, OutOfBoundsError, PyIndex, PySlice, StepSizeZeroError};
use crate::{Db, FxOrderSet};
@ -88,6 +88,16 @@ pub struct TupleType<'db> {
pub(crate) tuple: TupleSpec<'db>,
}
pub(super) fn walk_tuple_type<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
tuple: TupleType<'db>,
visitor: &mut V,
) {
for element in tuple.tuple(db).all_elements() {
visitor.visit_type(db, *element);
}
}
// The Salsa heap is tracked separately.
impl get_size2::GetSize for TupleType<'_> {}
@ -178,7 +188,7 @@ impl<'db> TupleType<'db> {
pub(crate) fn normalized_impl(
self,
db: &'db dyn Db,
visitor: &mut TypeVisitor<'db>,
visitor: &mut TypeTransformer<'db>,
) -> Option<Self> {
TupleType::new(db, self.tuple(db).normalized_impl(db, visitor))
}
@ -332,7 +342,7 @@ impl<'db> FixedLengthTuple<Type<'db>> {
}
#[must_use]
fn normalized_impl(&self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
fn normalized_impl(&self, db: &'db dyn Db, visitor: &mut TypeTransformer<'db>) -> Self {
Self::from_elements(self.0.iter().map(|ty| ty.normalized_impl(db, visitor)))
}
@ -645,7 +655,11 @@ impl<'db> VariableLengthTuple<Type<'db>> {
}
#[must_use]
fn normalized_impl(&self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> TupleSpec<'db> {
fn normalized_impl(
&self,
db: &'db dyn Db,
visitor: &mut TypeTransformer<'db>,
) -> TupleSpec<'db> {
let prefix = self
.prenormalized_prefix_elements(db, None)
.map(|ty| ty.normalized_impl(db, visitor))
@ -985,7 +999,11 @@ impl<'db> Tuple<Type<'db>> {
}
}
pub(crate) fn normalized_impl(&self, db: &'db dyn Db, visitor: &mut TypeVisitor<'db>) -> Self {
pub(crate) fn normalized_impl(
&self,
db: &'db dyn Db,
visitor: &mut TypeTransformer<'db>,
) -> Self {
match self {
Tuple::Fixed(tuple) => Tuple::Fixed(tuple.normalized_impl(db, visitor)),
Tuple::Variable(tuple) => tuple.normalized_impl(db, visitor),

View file

@ -0,0 +1,275 @@
use crate::{
Db, FxIndexSet,
types::{
BoundMethodType, BoundSuperType, CallableType, GenericAlias, IntersectionType,
KnownInstanceType, MethodWrapperKind, NominalInstanceType, PropertyInstanceType,
ProtocolInstanceType, SubclassOfType, Type, TypeAliasType, TypeIsType, TypeVarInstance,
UnionType,
class::walk_generic_alias,
function::{FunctionType, walk_function_type},
instance::{walk_nominal_instance_type, walk_protocol_instance_type},
subclass_of::walk_subclass_of_type,
tuple::{TupleType, walk_tuple_type},
walk_bound_method_type, walk_bound_super_type, walk_callable_type, walk_intersection_type,
walk_known_instance_type, walk_method_wrapper_type, walk_property_instance_type,
walk_type_alias_type, walk_type_var_type, walk_typeis_type, walk_union,
},
};
/// A visitor trait that recurses into nested types.
///
/// The trait does not guard against infinite recursion out of the box,
/// but it makes it easy for implementors of the trait to do so.
/// See [`any_over_type`] for an example of how to do this.
pub(crate) trait TypeVisitor<'db> {
fn visit_type(&mut self, db: &'db dyn Db, ty: Type<'db>);
fn visit_union_type(&mut self, db: &'db dyn Db, union: UnionType<'db>) {
walk_union(db, union, self);
}
fn visit_intersection_type(&mut self, db: &'db dyn Db, intersection: IntersectionType<'db>) {
walk_intersection_type(db, intersection, self);
}
fn visit_tuple_type(&mut self, db: &'db dyn Db, tuple: TupleType<'db>) {
walk_tuple_type(db, tuple, self);
}
fn visit_callable_type(&mut self, db: &'db dyn Db, callable: CallableType<'db>) {
walk_callable_type(db, callable, self);
}
fn visit_property_instance_type(
&mut self,
db: &'db dyn Db,
property: PropertyInstanceType<'db>,
) {
walk_property_instance_type(db, property, self);
}
fn visit_typeis_type(&mut self, db: &'db dyn Db, type_is: TypeIsType<'db>) {
walk_typeis_type(db, type_is, self);
}
fn visit_subclass_of_type(&mut self, db: &'db dyn Db, subclass_of: SubclassOfType<'db>) {
walk_subclass_of_type(db, subclass_of, self);
}
fn visit_generic_alias_type(&mut self, db: &'db dyn Db, alias: GenericAlias<'db>) {
walk_generic_alias(db, alias, self);
}
fn visit_function_type(&mut self, db: &'db dyn Db, function: FunctionType<'db>) {
walk_function_type(db, function, self);
}
fn visit_bound_method_type(&mut self, db: &'db dyn Db, method: BoundMethodType<'db>) {
walk_bound_method_type(db, method, self);
}
fn visit_bound_super_type(&mut self, db: &'db dyn Db, bound_super: BoundSuperType<'db>) {
walk_bound_super_type(db, bound_super, self);
}
fn visit_nominal_instance_type(&mut self, db: &'db dyn Db, nominal: NominalInstanceType<'db>) {
walk_nominal_instance_type(db, nominal, self);
}
fn visit_type_var_type(&mut self, db: &'db dyn Db, type_var: TypeVarInstance<'db>) {
walk_type_var_type(db, type_var, self);
}
fn visit_protocol_instance_type(
&mut self,
db: &'db dyn Db,
protocol: ProtocolInstanceType<'db>,
) {
walk_protocol_instance_type(db, protocol, self);
}
fn visit_method_wrapper_type(
&mut self,
db: &'db dyn Db,
method_wrapper: MethodWrapperKind<'db>,
) {
walk_method_wrapper_type(db, method_wrapper, self);
}
fn visit_known_instance_type(
&mut self,
db: &'db dyn Db,
known_instance: KnownInstanceType<'db>,
) {
walk_known_instance_type(db, known_instance, self);
}
fn visit_type_alias_type(&mut self, db: &'db dyn Db, type_alias: TypeAliasType<'db>) {
walk_type_alias_type(db, type_alias, self);
}
}
/// Enumeration of types that may contain other types, such as unions, intersections, and generics.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
enum NonAtomicType<'db> {
Union(UnionType<'db>),
Intersection(IntersectionType<'db>),
Tuple(TupleType<'db>),
FunctionLiteral(FunctionType<'db>),
BoundMethod(BoundMethodType<'db>),
BoundSuper(BoundSuperType<'db>),
MethodWrapper(MethodWrapperKind<'db>),
Callable(CallableType<'db>),
GenericAlias(GenericAlias<'db>),
KnownInstance(KnownInstanceType<'db>),
SubclassOf(SubclassOfType<'db>),
NominalInstance(NominalInstanceType<'db>),
PropertyInstance(PropertyInstanceType<'db>),
TypeIs(TypeIsType<'db>),
TypeVar(TypeVarInstance<'db>),
ProtocolInstance(ProtocolInstanceType<'db>),
}
enum TypeKind<'db> {
Atomic,
NonAtomic(NonAtomicType<'db>),
}
impl<'db> From<Type<'db>> for TypeKind<'db> {
fn from(ty: Type<'db>) -> Self {
match ty {
Type::AlwaysFalsy
| Type::AlwaysTruthy
| Type::Never
| Type::LiteralString
| Type::IntLiteral(_)
| Type::BooleanLiteral(_)
| Type::StringLiteral(_)
| Type::BytesLiteral(_)
| Type::DataclassDecorator(_)
| Type::DataclassTransformer(_)
| Type::WrapperDescriptor(_)
| Type::ModuleLiteral(_)
| Type::ClassLiteral(_)
| Type::SpecialForm(_)
| Type::Dynamic(_) => TypeKind::Atomic,
// Non-atomic types
Type::FunctionLiteral(function) => {
TypeKind::NonAtomic(NonAtomicType::FunctionLiteral(function))
}
Type::Intersection(intersection) => {
TypeKind::NonAtomic(NonAtomicType::Intersection(intersection))
}
Type::Union(union) => TypeKind::NonAtomic(NonAtomicType::Union(union)),
Type::Tuple(tuple) => TypeKind::NonAtomic(NonAtomicType::Tuple(tuple)),
Type::BoundMethod(method) => TypeKind::NonAtomic(NonAtomicType::BoundMethod(method)),
Type::BoundSuper(bound_super) => {
TypeKind::NonAtomic(NonAtomicType::BoundSuper(bound_super))
}
Type::MethodWrapper(method_wrapper) => {
TypeKind::NonAtomic(NonAtomicType::MethodWrapper(method_wrapper))
}
Type::Callable(callable) => TypeKind::NonAtomic(NonAtomicType::Callable(callable)),
Type::GenericAlias(alias) => TypeKind::NonAtomic(NonAtomicType::GenericAlias(alias)),
Type::KnownInstance(known_instance) => {
TypeKind::NonAtomic(NonAtomicType::KnownInstance(known_instance))
}
Type::SubclassOf(subclass_of) => {
TypeKind::NonAtomic(NonAtomicType::SubclassOf(subclass_of))
}
Type::NominalInstance(nominal) => {
TypeKind::NonAtomic(NonAtomicType::NominalInstance(nominal))
}
Type::ProtocolInstance(protocol) => {
TypeKind::NonAtomic(NonAtomicType::ProtocolInstance(protocol))
}
Type::PropertyInstance(property) => {
TypeKind::NonAtomic(NonAtomicType::PropertyInstance(property))
}
Type::TypeVar(type_var) => TypeKind::NonAtomic(NonAtomicType::TypeVar(type_var)),
Type::TypeIs(type_is) => TypeKind::NonAtomic(NonAtomicType::TypeIs(type_is)),
}
}
}
fn walk_non_atomic_type<'db, V: TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
non_atomic_type: NonAtomicType<'db>,
visitor: &mut V,
) {
match non_atomic_type {
NonAtomicType::FunctionLiteral(function) => visitor.visit_function_type(db, function),
NonAtomicType::Intersection(intersection) => {
visitor.visit_intersection_type(db, intersection);
}
NonAtomicType::Union(union) => visitor.visit_union_type(db, union),
NonAtomicType::Tuple(tuple) => visitor.visit_tuple_type(db, tuple),
NonAtomicType::BoundMethod(method) => visitor.visit_bound_method_type(db, method),
NonAtomicType::BoundSuper(bound_super) => visitor.visit_bound_super_type(db, bound_super),
NonAtomicType::MethodWrapper(method_wrapper) => {
visitor.visit_method_wrapper_type(db, method_wrapper);
}
NonAtomicType::Callable(callable) => visitor.visit_callable_type(db, callable),
NonAtomicType::GenericAlias(alias) => visitor.visit_generic_alias_type(db, alias),
NonAtomicType::KnownInstance(known_instance) => {
visitor.visit_known_instance_type(db, known_instance);
}
NonAtomicType::SubclassOf(subclass_of) => visitor.visit_subclass_of_type(db, subclass_of),
NonAtomicType::NominalInstance(nominal) => visitor.visit_nominal_instance_type(db, nominal),
NonAtomicType::PropertyInstance(property) => {
visitor.visit_property_instance_type(db, property);
}
NonAtomicType::TypeIs(type_is) => visitor.visit_typeis_type(db, type_is),
NonAtomicType::TypeVar(type_var) => visitor.visit_type_var_type(db, type_var),
NonAtomicType::ProtocolInstance(protocol) => {
visitor.visit_protocol_instance_type(db, protocol);
}
}
}
/// Return `true` if `ty`, or any of the types contained in `ty`, match the closure passed in.
///
/// The function guards against infinite recursion
/// by keeping track of the non-atomic types it has already seen.
pub(super) fn any_over_type<'db>(
db: &'db dyn Db,
ty: Type<'db>,
query: &dyn Fn(Type<'db>) -> bool,
) -> bool {
struct AnyOverTypeVisitor<'db, 'a> {
query: &'a dyn Fn(Type<'db>) -> bool,
seen_types: FxIndexSet<NonAtomicType<'db>>,
found_matching_type: bool,
}
impl<'db> TypeVisitor<'db> for AnyOverTypeVisitor<'db, '_> {
fn visit_type(&mut self, db: &'db dyn Db, ty: Type<'db>) {
if self.found_matching_type {
return;
}
self.found_matching_type |= (self.query)(ty);
if self.found_matching_type {
return;
}
match TypeKind::from(ty) {
TypeKind::Atomic => {}
TypeKind::NonAtomic(non_atomic_type) => {
if !self.seen_types.insert(non_atomic_type) {
// If we have already seen this type, we can skip it.
return;
}
walk_non_atomic_type(db, non_atomic_type, self);
}
}
}
}
let mut visitor = AnyOverTypeVisitor {
query,
seen_types: FxIndexSet::default(),
found_matching_type: false,
};
visitor.visit_type(db, ty);
visitor.found_matching_type
}