diff --git a/crates/ty_python_semantic/resources/mdtest/type_properties/is_equivalent_to.md b/crates/ty_python_semantic/resources/mdtest/type_properties/is_equivalent_to.md index 83579a0e06..ebb350ee28 100644 --- a/crates/ty_python_semantic/resources/mdtest/type_properties/is_equivalent_to.md +++ b/crates/ty_python_semantic/resources/mdtest/type_properties/is_equivalent_to.md @@ -133,6 +133,11 @@ class Single(Enum): VALUE = 1 static_assert(is_equivalent_to(P | Q | Single, Literal[Single.VALUE] | Q | P)) + +static_assert(is_equivalent_to(Any, Any | Intersection[Any, str])) +static_assert(is_equivalent_to(Any, Intersection[str, Any] | Any)) +static_assert(is_equivalent_to(Any, Any | Intersection[Any, Not[None]])) +static_assert(is_equivalent_to(Any, Intersection[Not[None], Any] | Any)) ``` ## Tuples diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index b2cb2d61f1..7b643a168a 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -1050,6 +1050,13 @@ impl<'db> Type<'db> { } } + pub(crate) const fn into_intersection(self) -> Option> { + match self { + Type::Intersection(intersection_type) => Some(intersection_type), + _ => None, + } + } + #[cfg(test)] #[track_caller] pub(crate) fn expect_union(self) -> UnionType<'db> { diff --git a/crates/ty_python_semantic/src/types/builder.rs b/crates/ty_python_semantic/src/types/builder.rs index cc1c35790a..556edcb368 100644 --- a/crates/ty_python_semantic/src/types/builder.rs +++ b/crates/ty_python_semantic/src/types/builder.rs @@ -504,9 +504,16 @@ impl<'db> UnionBuilder<'db> { if should_simplify_full && !matches!(element_type, Type::TypeAlias(_)) { if ty.is_equivalent_to(self.db, element_type) || ty.is_subtype_of(self.db, element_type) + || ty.into_intersection().is_some_and(|intersection| { + intersection.positive(self.db).contains(&element_type) + }) { return; - } else if element_type.is_subtype_of(self.db, ty) { + } else if element_type.is_subtype_of(self.db, ty) + || element_type + .into_intersection() + .is_some_and(|intersection| intersection.positive(self.db).contains(&ty)) + { to_remove.push(index); } else if ty_negated.is_subtype_of(self.db, element_type) { // We add `ty` to the union. We just checked that `~ty` is a subtype of an