mirror of https://github.com/astral-sh/ruff
[ty] recognize non-fully-static specializations
This commit is contained in:
parent
41fa082414
commit
f68dbfdef1
|
|
@ -130,3 +130,13 @@ static_assert(is_fully_static(TypeOf[static]))
|
|||
static_assert(not is_fully_static(CallableTypeOf[gradual]))
|
||||
static_assert(is_fully_static(CallableTypeOf[static]))
|
||||
```
|
||||
|
||||
## Generics
|
||||
|
||||
```py
|
||||
from typing import Any
|
||||
from ty_extensions import static_assert, is_fully_static
|
||||
|
||||
static_assert(is_fully_static(list[int]))
|
||||
static_assert(not is_fully_static(list[Any]))
|
||||
```
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ from typing_extensions import _NoDefaultType
|
|||
static_assert(is_subtype_of(sys.version_info.__class__, AlwaysTruthy))
|
||||
static_assert(is_subtype_of(types.EllipsisType, AlwaysTruthy))
|
||||
static_assert(is_subtype_of(_NoDefaultType, AlwaysTruthy))
|
||||
static_assert(is_subtype_of(slice, AlwaysTruthy))
|
||||
static_assert(is_subtype_of(slice[int, int, int], AlwaysTruthy))
|
||||
static_assert(is_subtype_of(types.FunctionType, AlwaysTruthy))
|
||||
static_assert(is_subtype_of(types.MethodType, AlwaysTruthy))
|
||||
static_assert(is_subtype_of(typing.TypeVar, AlwaysTruthy))
|
||||
|
|
|
|||
|
|
@ -2144,6 +2144,7 @@ impl<'db> Type<'db> {
|
|||
| Type::PropertyInstance(_) => true,
|
||||
|
||||
Type::ProtocolInstance(protocol) => protocol.is_fully_static(db),
|
||||
Type::NominalInstance(instance) => instance.is_fully_static(db),
|
||||
|
||||
Type::TypeVar(typevar) => match typevar.bound_or_constraints(db) {
|
||||
None => true,
|
||||
|
|
@ -2159,24 +2160,8 @@ impl<'db> Type<'db> {
|
|||
!matches!(bound_super.pivot_class(db), ClassBase::Dynamic(_))
|
||||
&& !matches!(bound_super.owner(db), SuperOwnerKind::Dynamic(_))
|
||||
}
|
||||
Type::ClassLiteral(_) | Type::GenericAlias(_) | Type::NominalInstance(_) => {
|
||||
// TODO: Ideally, we would iterate over the MRO of the class, check if all
|
||||
// bases are fully static, and only return `true` if that is the case.
|
||||
//
|
||||
// This does not work yet, because we currently infer `Unknown` for some
|
||||
// generic base classes that we don't understand yet. For example, `str`
|
||||
// is defined as `class str(Sequence[str])` in typeshed and we currently
|
||||
// compute its MRO as `(str, Unknown, object)`. This would make us think
|
||||
// that `str` is a gradual type, which causes all sorts of downstream
|
||||
// issues because it does not participate in equivalence/subtyping etc.
|
||||
//
|
||||
// Another problem is that we run into problems if we eagerly query the
|
||||
// MRO of class literals here. I have not fully investigated this, but
|
||||
// iterating over the MRO alone, without even acting on it, causes us to
|
||||
// infer `Unknown` for many classes.
|
||||
|
||||
true
|
||||
}
|
||||
Type::ClassLiteral(class) => class.is_fully_static(db),
|
||||
Type::GenericAlias(alias) => alias.is_fully_static(db),
|
||||
Type::Union(union) => union.is_fully_static(db),
|
||||
Type::Intersection(intersection) => intersection.is_fully_static(db),
|
||||
// TODO: Once we support them, make sure that we return `false` for other types
|
||||
|
|
|
|||
|
|
@ -167,6 +167,10 @@ impl<'db> GenericAlias<'db> {
|
|||
self.origin(db).definition(db)
|
||||
}
|
||||
|
||||
pub(crate) fn is_fully_static(self, db: &'db dyn Db) -> bool {
|
||||
self.origin(db).is_fully_static(db) && self.specialization(db).is_fully_static(db)
|
||||
}
|
||||
|
||||
fn apply_type_mapping<'a>(self, db: &'db dyn Db, type_mapping: TypeMapping<'a, 'db>) -> Self {
|
||||
Self::new(
|
||||
db,
|
||||
|
|
@ -246,6 +250,13 @@ impl<'db> ClassType<'db> {
|
|||
self.known(db) == Some(known_class)
|
||||
}
|
||||
|
||||
pub(crate) fn is_fully_static(self, db: &'db dyn Db) -> bool {
|
||||
match self {
|
||||
Self::NonGeneric(class_literal) => class_literal.is_fully_static(db),
|
||||
Self::Generic(alias) => alias.is_fully_static(db),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return `true` if this class represents the builtin class `object`
|
||||
pub(crate) fn is_object(self, db: &'db dyn Db) -> bool {
|
||||
self.is_known(db, KnownClass::Object)
|
||||
|
|
@ -506,6 +517,14 @@ impl<'db> ClassLiteral<'db> {
|
|||
self.known(db) == Some(known_class)
|
||||
}
|
||||
|
||||
#[expect(clippy::unused_self)]
|
||||
pub(crate) fn is_fully_static(self, _db: &'db dyn Db) -> bool {
|
||||
// TODO: Ideally, we would iterate over the MRO of the class, check if all
|
||||
// bases are fully static, and only return `true` if that is the case. But there may be
|
||||
// cycle issues trying to infer base classes this eagerly.
|
||||
true
|
||||
}
|
||||
|
||||
pub(crate) fn generic_context(self, db: &'db dyn Db) -> Option<GenericContext<'db>> {
|
||||
// Several typeshed definitions examine `sys.version_info`. To break cycles, we hard-code
|
||||
// the knowledge that this class is not generic.
|
||||
|
|
|
|||
|
|
@ -292,6 +292,10 @@ pub struct Specialization<'db> {
|
|||
}
|
||||
|
||||
impl<'db> Specialization<'db> {
|
||||
pub(crate) fn is_fully_static(self, db: &'db dyn Db) -> bool {
|
||||
self.types(db).iter().all(|ty| ty.is_fully_static(db))
|
||||
}
|
||||
|
||||
pub(crate) fn type_mapping(self) -> TypeMapping<'db, 'db> {
|
||||
TypeMapping::Specialization(self)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,6 +66,10 @@ impl<'db> NominalInstanceType<'db> {
|
|||
self.class
|
||||
}
|
||||
|
||||
pub(super) fn is_fully_static(self, db: &'db dyn Db) -> bool {
|
||||
self.class.is_fully_static(db)
|
||||
}
|
||||
|
||||
pub(super) fn is_subtype_of(self, db: &'db dyn Db, other: Self) -> bool {
|
||||
// N.B. The subclass relation is fully static
|
||||
self.class.is_subclass_of(db, other.class)
|
||||
|
|
|
|||
Loading…
Reference in New Issue