mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 10:48:32 +00:00
[ty] Deeply normalize many types (#18222)
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 (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
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 (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
This commit is contained in:
parent
32403dfb28
commit
60b486abce
5 changed files with 255 additions and 22 deletions
|
@ -357,6 +357,14 @@ impl<'db> PropertyInstanceType<'db> {
|
|||
Self::new(db, getter, setter)
|
||||
}
|
||||
|
||||
fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
Self::new(
|
||||
db,
|
||||
self.getter(db).map(|ty| ty.normalized(db)),
|
||||
self.setter(db).map(|ty| ty.normalized(db)),
|
||||
)
|
||||
}
|
||||
|
||||
fn find_legacy_typevars(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
|
@ -1005,28 +1013,17 @@ impl<'db> Type<'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(_) => Type::any(),
|
||||
Type::LiteralString
|
||||
| Type::PropertyInstance(_)
|
||||
| Type::AlwaysFalsy
|
||||
| Type::AlwaysTruthy
|
||||
| Type::BooleanLiteral(_)
|
||||
| Type::BytesLiteral(_)
|
||||
| Type::StringLiteral(_)
|
||||
| Type::Never
|
||||
| Type::FunctionLiteral(_)
|
||||
| Type::MethodWrapper(_)
|
||||
| Type::BoundMethod(_)
|
||||
| Type::WrapperDescriptor(_)
|
||||
| Type::DataclassDecorator(_)
|
||||
| Type::DataclassTransformer(_)
|
||||
| Type::ModuleLiteral(_)
|
||||
| Type::ClassLiteral(_)
|
||||
| Type::KnownInstance(_)
|
||||
| Type::IntLiteral(_)
|
||||
| Type::BoundSuper(_)
|
||||
| Type::SubclassOf(_) => self,
|
||||
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::KnownInstance(known_instance) => {
|
||||
Type::KnownInstance(known_instance.normalized(db))
|
||||
}
|
||||
Type::TypeVar(typevar) => match typevar.bound_or_constraints(db) {
|
||||
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
|
||||
Type::TypeVar(TypeVarInstance::new(
|
||||
|
@ -1052,6 +1049,19 @@ impl<'db> Type<'db> {
|
|||
}
|
||||
None => self,
|
||||
},
|
||||
Type::LiteralString
|
||||
| Type::AlwaysFalsy
|
||||
| Type::AlwaysTruthy
|
||||
| Type::BooleanLiteral(_)
|
||||
| Type::BytesLiteral(_)
|
||||
| Type::StringLiteral(_)
|
||||
| Type::Never
|
||||
| Type::WrapperDescriptor(_)
|
||||
| Type::DataclassDecorator(_)
|
||||
| Type::DataclassTransformer(_)
|
||||
| Type::ModuleLiteral(_)
|
||||
| Type::ClassLiteral(_)
|
||||
| Type::IntLiteral(_) => self,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5604,6 +5614,18 @@ impl<'db> TypeMapping<'_, 'db> {
|
|||
TypeMapping::PromoteLiterals => TypeMapping::PromoteLiterals,
|
||||
}
|
||||
}
|
||||
|
||||
fn normalized(&self, db: &'db dyn Db) -> Self {
|
||||
match self {
|
||||
TypeMapping::Specialization(specialization) => {
|
||||
TypeMapping::Specialization(specialization.normalized(db))
|
||||
}
|
||||
TypeMapping::PartialSpecialization(partial) => {
|
||||
TypeMapping::PartialSpecialization(partial.normalized(db))
|
||||
}
|
||||
TypeMapping::PromoteLiterals => TypeMapping::PromoteLiterals,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
|
||||
|
@ -5627,6 +5649,13 @@ pub enum DynamicType {
|
|||
TodoPEP695ParamSpec,
|
||||
}
|
||||
|
||||
impl DynamicType {
|
||||
#[expect(clippy::unused_self)]
|
||||
fn normalized(self) -> Self {
|
||||
Self::Any
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DynamicType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
|
@ -5877,6 +5906,18 @@ impl<'db> TypeVarInstance<'db> {
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
Self::new(
|
||||
db,
|
||||
self.name(db),
|
||||
self.definition(db),
|
||||
self.bound_or_constraints(db).map(|b| b.normalized(db)),
|
||||
self.variance(db),
|
||||
self.default_ty(db).map(|d| d.normalized(db)),
|
||||
self.kind(db),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update)]
|
||||
|
@ -5893,6 +5934,19 @@ pub enum TypeVarBoundOrConstraints<'db> {
|
|||
Constraints(UnionType<'db>),
|
||||
}
|
||||
|
||||
impl<'db> TypeVarBoundOrConstraints<'db> {
|
||||
fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
match self {
|
||||
TypeVarBoundOrConstraints::UpperBound(bound) => {
|
||||
TypeVarBoundOrConstraints::UpperBound(bound.normalized(db))
|
||||
}
|
||||
TypeVarBoundOrConstraints::Constraints(constraints) => {
|
||||
TypeVarBoundOrConstraints::Constraints(constraints.normalized(db))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Error returned if a type is not (or may not be) a context manager.
|
||||
#[derive(Debug)]
|
||||
enum ContextManagerError<'db> {
|
||||
|
@ -7123,6 +7177,29 @@ impl<'db> FunctionType<'db> {
|
|||
.is_gradual_equivalent_to(db, other.into_callable_type(db))
|
||||
}
|
||||
|
||||
fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
let context = self
|
||||
.inherited_generic_context(db)
|
||||
.map(|ctx| ctx.normalized(db));
|
||||
|
||||
let mappings: Box<_> = self
|
||||
.type_mappings(db)
|
||||
.iter()
|
||||
.map(|mapping| mapping.normalized(db))
|
||||
.collect();
|
||||
|
||||
Self::new(
|
||||
db,
|
||||
self.name(db),
|
||||
self.known(db),
|
||||
self.body_scope(db),
|
||||
self.decorators(db),
|
||||
self.dataclass_transformer_params(db),
|
||||
context,
|
||||
mappings,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a tuple of two spans. The first is
|
||||
/// the span for the identifier of the function
|
||||
/// definition for `self`. The second is
|
||||
|
@ -7410,6 +7487,14 @@ impl<'db> BoundMethodType<'db> {
|
|||
))
|
||||
}
|
||||
|
||||
fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
Self::new(
|
||||
db,
|
||||
self.function(db).normalized(db),
|
||||
self.self_instance(db).normalized(db),
|
||||
)
|
||||
}
|
||||
|
||||
fn is_subtype_of(self, db: &'db dyn Db, other: Self) -> bool {
|
||||
// A bound method is a typically a subtype of itself. However, we must explicitly verify
|
||||
// the subtyping of the underlying function signatures (since they might be specialized
|
||||
|
@ -7813,6 +7898,24 @@ impl<'db> MethodWrapperKind<'db> {
|
|||
) => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
match self {
|
||||
MethodWrapperKind::FunctionTypeDunderGet(function) => {
|
||||
MethodWrapperKind::FunctionTypeDunderGet(function.normalized(db))
|
||||
}
|
||||
MethodWrapperKind::FunctionTypeDunderCall(function) => {
|
||||
MethodWrapperKind::FunctionTypeDunderCall(function.normalized(db))
|
||||
}
|
||||
MethodWrapperKind::PropertyDunderGet(property) => {
|
||||
MethodWrapperKind::PropertyDunderGet(property.normalized(db))
|
||||
}
|
||||
MethodWrapperKind::PropertyDunderSet(property) => {
|
||||
MethodWrapperKind::PropertyDunderSet(property.normalized(db))
|
||||
}
|
||||
MethodWrapperKind::StrStartswith(_) => self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a specific instance of `types.WrapperDescriptorType`
|
||||
|
@ -7907,6 +8010,10 @@ impl<'db> PEP695TypeAliasType<'db> {
|
|||
let definition = self.definition(db);
|
||||
definition_expression_type(db, definition, &type_alias_stmt_node.value)
|
||||
}
|
||||
|
||||
fn normalized(self, _db: &'db dyn Db) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// # Ordering
|
||||
|
@ -7921,6 +8028,17 @@ pub struct BareTypeAliasType<'db> {
|
|||
pub value: Type<'db>,
|
||||
}
|
||||
|
||||
impl<'db> BareTypeAliasType<'db> {
|
||||
fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
Self::new(
|
||||
db,
|
||||
self.name(db),
|
||||
self.definition(db),
|
||||
self.value(db).normalized(db),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, salsa::Update)]
|
||||
pub enum TypeAliasType<'db> {
|
||||
PEP695(PEP695TypeAliasType<'db>),
|
||||
|
@ -7928,10 +8046,17 @@ pub enum TypeAliasType<'db> {
|
|||
}
|
||||
|
||||
impl<'db> TypeAliasType<'db> {
|
||||
pub(crate) fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
match self {
|
||||
TypeAliasType::PEP695(type_alias) => TypeAliasType::PEP695(type_alias.normalized(db)),
|
||||
TypeAliasType::Bare(type_alias) => TypeAliasType::Bare(type_alias.normalized(db)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn name(self, db: &'db dyn Db) -> &'db str {
|
||||
match self {
|
||||
TypeAliasType::PEP695(type_alias) => type_alias.name(db),
|
||||
TypeAliasType::Bare(type_alias) => type_alias.name(db).as_str(),
|
||||
TypeAliasType::Bare(type_alias) => type_alias.name(db),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8595,6 +8720,14 @@ pub enum SuperOwnerKind<'db> {
|
|||
}
|
||||
|
||||
impl<'db> SuperOwnerKind<'db> {
|
||||
fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
match self {
|
||||
SuperOwnerKind::Dynamic(dynamic) => SuperOwnerKind::Dynamic(dynamic.normalized()),
|
||||
SuperOwnerKind::Class(class) => SuperOwnerKind::Class(class.normalized(db)),
|
||||
SuperOwnerKind::Instance(instance) => SuperOwnerKind::Instance(instance.normalized(db)),
|
||||
}
|
||||
}
|
||||
|
||||
fn iter_mro(self, db: &'db dyn Db) -> impl Iterator<Item = ClassBase<'db>> {
|
||||
match self {
|
||||
SuperOwnerKind::Dynamic(dynamic) => {
|
||||
|
@ -8829,6 +8962,14 @@ impl<'db> BoundSuperType<'db> {
|
|||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
Self::new(
|
||||
db,
|
||||
self.pivot_class(db).normalized(db),
|
||||
self.owner(db).normalized(db),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure that the `Type` enum does not grow unexpectedly.
|
||||
|
|
|
@ -31,6 +31,19 @@ impl<'db> ClassBase<'db> {
|
|||
Self::Dynamic(DynamicType::Unknown)
|
||||
}
|
||||
|
||||
pub(crate) fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
match self {
|
||||
Self::Dynamic(dynamic) => Self::Dynamic(dynamic.normalized()),
|
||||
Self::Class(class) => Self::Class(class.normalized(db)),
|
||||
Self::Protocol(generic_context) => {
|
||||
Self::Protocol(generic_context.map(|context| context.normalized(db)))
|
||||
}
|
||||
Self::Generic(generic_context) => {
|
||||
Self::Generic(generic_context.map(|context| context.normalized(db)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn display(self, db: &'db dyn Db) -> impl std::fmt::Display + 'db {
|
||||
struct Display<'db> {
|
||||
base: ClassBase<'db>,
|
||||
|
|
|
@ -237,6 +237,15 @@ impl<'db> GenericContext<'db> {
|
|||
|
||||
Specialization::new(db, self, expanded.into_boxed_slice())
|
||||
}
|
||||
|
||||
pub(crate) fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
let variables: FxOrderSet<_> = self
|
||||
.variables(db)
|
||||
.iter()
|
||||
.map(|ty| ty.normalized(db))
|
||||
.collect();
|
||||
Self::new(db, variables, self.origin(db))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
|
||||
|
@ -561,6 +570,16 @@ impl<'db> PartialSpecialization<'_, 'db> {
|
|||
types: Cow::from(self.types.clone().into_owned()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn normalized(&self, db: &'db dyn Db) -> PartialSpecialization<'db, 'db> {
|
||||
let generic_context = self.generic_context.normalized(db);
|
||||
let types: Cow<_> = self.types.iter().map(|ty| ty.normalized(db)).collect();
|
||||
|
||||
PartialSpecialization {
|
||||
generic_context,
|
||||
types,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs type inference between parameter annotations and argument types, producing a
|
||||
|
|
|
@ -153,6 +153,53 @@ impl<'db> KnownInstanceType<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
match self {
|
||||
Self::Annotated
|
||||
| Self::Literal
|
||||
| Self::LiteralString
|
||||
| Self::Optional
|
||||
| Self::Union
|
||||
| Self::NoReturn
|
||||
| Self::Never
|
||||
| Self::Tuple
|
||||
| Self::Type
|
||||
| Self::TypingSelf
|
||||
| Self::Final
|
||||
| Self::ClassVar
|
||||
| Self::Callable
|
||||
| Self::Concatenate
|
||||
| Self::Unpack
|
||||
| Self::Required
|
||||
| Self::NotRequired
|
||||
| Self::TypeAlias
|
||||
| Self::TypeGuard
|
||||
| Self::TypedDict
|
||||
| Self::TypeIs
|
||||
| Self::List
|
||||
| Self::Dict
|
||||
| Self::DefaultDict
|
||||
| Self::Set
|
||||
| Self::FrozenSet
|
||||
| Self::Counter
|
||||
| Self::Deque
|
||||
| Self::ChainMap
|
||||
| Self::OrderedDict
|
||||
| Self::ReadOnly
|
||||
| Self::Unknown
|
||||
| Self::AlwaysTruthy
|
||||
| Self::AlwaysFalsy
|
||||
| Self::Not
|
||||
| Self::Intersection
|
||||
| Self::TypeOf
|
||||
| Self::CallableTypeOf => self,
|
||||
Self::TypeVar(tvar) => Self::TypeVar(tvar.normalized(db)),
|
||||
Self::Protocol(ctx) => Self::Protocol(ctx.map(|ctx| ctx.normalized(db))),
|
||||
Self::Generic(ctx) => Self::Generic(ctx.map(|ctx| ctx.normalized(db))),
|
||||
Self::TypeAliasType(alias) => Self::TypeAliasType(alias.normalized(db)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the repr of the symbol at runtime
|
||||
pub(crate) fn repr(self, db: &'db dyn Db) -> impl Display + 'db {
|
||||
KnownInstanceRepr {
|
||||
|
|
|
@ -122,6 +122,12 @@ impl<'db> SubclassOfType<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
Self {
|
||||
subclass_of: self.subclass_of.normalized(db),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_instance(self, db: &'db dyn Db) -> Type<'db> {
|
||||
match self.subclass_of {
|
||||
SubclassOfInner::Class(class) => Type::instance(db, class),
|
||||
|
@ -173,6 +179,13 @@ impl<'db> SubclassOfInner<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn normalized(self, db: &'db dyn Db) -> Self {
|
||||
match self {
|
||||
Self::Class(class) => Self::Class(class.normalized(db)),
|
||||
Self::Dynamic(dynamic) => Self::Dynamic(dynamic.normalized()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn try_from_type(db: &'db dyn Db, ty: Type<'db>) -> Option<Self> {
|
||||
match ty {
|
||||
Type::Dynamic(dynamic) => Some(Self::Dynamic(dynamic)),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue