PY-81926 Don't report using classes overriding __or__ in the Class1 | Class2 notation for unions

It should only be special cases for classes which *metaclasses*
override `__or__` so that this notation is possible.

Other type checkers treat such union of classes overriding
__or__ in their metaclass as Unknown.

(cherry picked from commit c16edee2346582745aa5090c3d8f568a4513afc1)

IJ-CR-176451

(cherry picked from commit 4dfc125bcfc625d1fe5577695e9a75449f40594a)

GitOrigin-RevId: 9f066646863ec321bcace0d2d10efe44a3c12728
This commit is contained in:
Mikhail Golubev
2025-09-05 14:04:06 +03:00
committed by intellij-monorepo-bot
parent 6ec2acfce8
commit dc29f4a9bc
2 changed files with 24 additions and 5 deletions

View File

@@ -817,11 +817,16 @@ public final class PyTypingTypeProvider extends PyTypeProviderWithCustomContext<
private static boolean typeHasOverloadedBitwiseOr(@NotNull PyType type, @NotNull PyExpression expression,
@NotNull Context context) {
if (type instanceof PyUnionType) return false;
PyType typeToClass = type instanceof PyClassLikeType ? ((PyClassLikeType)type).toClass() : type;
var resolved = typeToClass.resolveMember("__or__", expression, AccessDirection.READ,
PyResolveContext.defaultContext(context.getTypeContext()));
if (!(type instanceof PyClassType classType)) {
return false;
}
TypeEvalContext typeContext = context.getTypeContext();
PyClassLikeType metaClassType = classType.getMetaClassType(typeContext, true);
if (metaClassType == null) {
return false;
}
var resolved = metaClassType
.resolveMember("__or__", expression, AccessDirection.READ, PyResolveContext.defaultContext(typeContext));
if (resolved == null || resolved.isEmpty()) return false;
return StreamEx.of(resolved)

View File

@@ -2962,6 +2962,20 @@ public class PyTypeHintsInspectionTest extends PyInspectionTestCase {
doMultiFileTest();
}
// PY-81926
public void testUnionWithClassOverridingDunderOr() {
doTestByText("""
from typing import Self
class Cls:
def __or__(self, other: Self) -> Self:
return self
def foo(arg: Cls | None) -> None:
print(arg)
""");
}
@NotNull
@Override
protected Class<? extends PyInspection> getInspectionClass() {