diff --git a/python/python-psi-impl/src/com/jetbrains/python/validation/CompatibilityVisitor.java b/python/python-psi-impl/src/com/jetbrains/python/validation/CompatibilityVisitor.java index 1ddeba3ee242..6d9cd2a3c4b4 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/validation/CompatibilityVisitor.java +++ b/python/python-psi-impl/src/com/jetbrains/python/validation/CompatibilityVisitor.java @@ -28,8 +28,13 @@ import com.jetbrains.python.codeInsight.imports.AddImportHelper; import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider; import com.jetbrains.python.inspections.quickfix.*; import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.impl.PyPsiUtils; -import com.jetbrains.python.psi.types.*; +import com.jetbrains.python.psi.resolve.PyResolveContext; +import com.jetbrains.python.psi.types.PyClassType; +import com.jetbrains.python.psi.types.PyType; +import com.jetbrains.python.psi.types.PyUnionType; +import com.jetbrains.python.psi.types.TypeEvalContext; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -729,6 +734,7 @@ public abstract class CompatibilityVisitor extends PyAnnotator { private void checkBitwiseOrUnionSyntax(@NotNull PyBinaryExpression node) { if (node.getOperator() != PyTokenTypes.OR) return; + final PsiFile file = node.getContainingFile(); if (file == null || file instanceof PyFile && @@ -737,10 +743,21 @@ public abstract class CompatibilityVisitor extends PyAnnotator { return; } + final TypeEvalContext context = TypeEvalContext.codeAnalysis(node.getProject(), node.getContainingFile()); + + final List resolvedVariants = PyUtil.multiResolveTopPriority(node.getReference(PyResolveContext.defaultContext(context))); + for (PsiElement resolved : resolvedVariants) { + if (resolved instanceof PyFunction) { + final PyClass containingClass = ((PyFunction)resolved).getContainingClass(); + if (containingClass == null) return; + final String classQualifiedName = containingClass.getQualifiedName(); + if (!PyNames.TYPE.equals(classQualifiedName) && !"types.Union".equals(classQualifiedName)) return; + } + } + // Consider only full expression not parts to have only one registered problem if (PsiTreeUtil.getParentOfType(node, PyBinaryExpression.class, true, PyStatement.class) != null) return; - final TypeEvalContext context = TypeEvalContext.codeAnalysis(node.getProject(), node.getContainingFile()); final Ref refType = PyTypingTypeProvider.getType(node, context); if (refType != null && refType.get() instanceof PyUnionType) { registerForAllMatchingVersions(level -> level.isOlderThan(LanguageLevel.PYTHON310), diff --git a/python/testData/highlighting/noErrorMetaClassOverloadBitwiseOrOperator.py b/python/testData/highlighting/noErrorMetaClassOverloadBitwiseOrOperator.py new file mode 100644 index 000000000000..869b80980f77 --- /dev/null +++ b/python/testData/highlighting/noErrorMetaClassOverloadBitwiseOrOperator.py @@ -0,0 +1,14 @@ +class MetaA(type): + def __or__(self, other): + return 42 + + +class A(metaclass=MetaA): + pass + + +class B: + pass + + +print(A | B) diff --git a/python/testData/highlighting/noErrorMetaClassOverloadBitwiseOrOperatorReturnTypesUnion.py b/python/testData/highlighting/noErrorMetaClassOverloadBitwiseOrOperatorReturnTypesUnion.py new file mode 100644 index 000000000000..ead370abff7c --- /dev/null +++ b/python/testData/highlighting/noErrorMetaClassOverloadBitwiseOrOperatorReturnTypesUnion.py @@ -0,0 +1,17 @@ +import types + + +class MetaA(type): + def __or__(self, other) -> types.Union: + return types.Union() + + +class A(metaclass=MetaA): + pass + + +class B: + pass + + +print(A | B) diff --git a/python/testSrc/com/jetbrains/python/PythonHighlightingTest.java b/python/testSrc/com/jetbrains/python/PythonHighlightingTest.java index 103dfbd7efdd..58d97f70125d 100644 --- a/python/testSrc/com/jetbrains/python/PythonHighlightingTest.java +++ b/python/testSrc/com/jetbrains/python/PythonHighlightingTest.java @@ -518,6 +518,16 @@ public class PythonHighlightingTest extends PyTestCase { doTest(LanguageLevel.PYTHON39, false, false); } + // PY-49697 + public void testNoErrorMetaClassOverloadBitwiseOrOperator() { + doTest(LanguageLevel.PYTHON39, false, false); + } + + // PY-49697 + public void testNoErrorMetaClassOverloadBitwiseOrOperatorReturnTypesUnion() { + doTest(LanguageLevel.PYTHON39, false, false); + } + @NotNull private static EditorColorsScheme createTemporaryColorScheme() { EditorColorsManager manager = EditorColorsManager.getInstance();