mirror of
https://github.com/astral-sh/ruff
synced 2026-01-21 05:20:49 -05:00
[ty] Optimize Type::negate() (#22402)
This commit is contained in:
@@ -1455,7 +1455,58 @@ impl<'db> Type<'db> {
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn negate(&self, db: &'db dyn Db) -> Type<'db> {
|
||||
IntersectionBuilder::new(db).add_negative(*self).build()
|
||||
// Avoid invoking the `IntersectionBuilder` for negations that are trivial.
|
||||
//
|
||||
// We verify that this always produces the same result as
|
||||
// `IntersectionBuilder::new(db).add_negative(*self).build()` via the
|
||||
// property test `all_negated_types_identical_to_intersection_with_single_negated_element`
|
||||
match self {
|
||||
Type::Never => Type::object(),
|
||||
|
||||
Type::Dynamic(_) => *self,
|
||||
|
||||
Type::NominalInstance(instance) if instance.is_object() => Type::Never,
|
||||
|
||||
Type::AlwaysTruthy
|
||||
| Type::AlwaysFalsy
|
||||
| Type::BooleanLiteral(_)
|
||||
| Type::KnownBoundMethod(_)
|
||||
| Type::KnownInstance(_)
|
||||
| Type::SpecialForm(_)
|
||||
| Type::BoundSuper(_)
|
||||
| Type::FunctionLiteral(_)
|
||||
| Type::TypeIs(_)
|
||||
| Type::TypeGuard(_)
|
||||
| Type::TypeVar(_)
|
||||
| Type::TypedDict(_)
|
||||
| Type::NewTypeInstance(_)
|
||||
| Type::NominalInstance(_)
|
||||
| Type::ProtocolInstance(_)
|
||||
| Type::ModuleLiteral(_)
|
||||
| Type::ClassLiteral(_)
|
||||
| Type::GenericAlias(_)
|
||||
| Type::SubclassOf(_)
|
||||
| Type::PropertyInstance(_)
|
||||
| Type::IntLiteral(_)
|
||||
| Type::StringLiteral(_)
|
||||
| Type::BytesLiteral(_)
|
||||
| Type::LiteralString
|
||||
| Type::DataclassDecorator(_)
|
||||
| Type::DataclassTransformer(_)
|
||||
| Type::Callable(_)
|
||||
| Type::WrapperDescriptor(_)
|
||||
| Type::EnumLiteral(_)
|
||||
| Type::TypeAlias(_)
|
||||
| Type::BoundMethod(_) => Type::Intersection(IntersectionType::new(
|
||||
db,
|
||||
FxOrderSet::default(),
|
||||
NegativeIntersectionElements::Single(*self),
|
||||
)),
|
||||
|
||||
Type::Union(_) | Type::Intersection(_) => {
|
||||
IntersectionBuilder::new(db).add_negative(*self).build()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
||||
@@ -81,7 +81,7 @@ macro_rules! type_property_test {
|
||||
|
||||
mod stable {
|
||||
use super::union;
|
||||
use crate::types::{CallableType, KnownClass, Type};
|
||||
use crate::types::{CallableType, IntersectionBuilder, KnownClass, Type};
|
||||
|
||||
// Reflexivity: `T` is equivalent to itself.
|
||||
type_property_test!(
|
||||
@@ -234,9 +234,16 @@ mod stable {
|
||||
// the Liskov violation). All you need to do is to create a class that subclasses
|
||||
// `Iterable` but assigns `__iter__ = None` in the class body (or similar).
|
||||
type_property_test!(
|
||||
all_type_assignable_to_iterable_are_iterable, db,
|
||||
all_types_assignable_to_iterable_are_iterable, db,
|
||||
forall types t. t.is_assignable_to(db, KnownClass::Iterable.to_specialized_instance(db, [Type::object()])) => t.try_iterate(db).is_ok()
|
||||
);
|
||||
|
||||
// Our optimized `Type::negate()` function should always produce the exact same type
|
||||
// as going "the long way" via the `IntersectionBuilder`.
|
||||
type_property_test!(
|
||||
all_negated_types_identical_to_intersection_with_single_negated_element, db,
|
||||
forall types t. t.negate(db) == IntersectionBuilder::new(db).add_negative(t).build()
|
||||
);
|
||||
}
|
||||
|
||||
/// This module contains property tests that currently lead to many false positives.
|
||||
|
||||
Reference in New Issue
Block a user