diff --git a/python/python-psi-impl/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java b/python/python-psi-impl/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java index 1589d7d26e82..2422c37e8aa3 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java +++ b/python/python-psi-impl/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java @@ -392,6 +392,8 @@ public class PyTypeAssertionEvaluator extends PyRecursiveElementVisitor { return null; }; } + + private static boolean isIfReferenceStatement(@NotNull PyExpression node) { return PsiTreeUtil.skipParentsOfType(node, PyParenthesizedExpression.class) instanceof PyIfPart; diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyBinaryExpressionImpl.java b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyBinaryExpressionImpl.java index 09d53f29f2f5..d3f5f960338f 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyBinaryExpressionImpl.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyBinaryExpressionImpl.java @@ -2,9 +2,11 @@ package com.jetbrains.python.psi.impl; import com.intellij.lang.ASTNode; +import com.intellij.openapi.util.Ref; import com.intellij.psi.PsiPolyVariantReference; import com.intellij.util.IncorrectOperationException; import com.jetbrains.python.PyNames; +import com.jetbrains.python.codeInsight.controlflow.PyTypeAssertionEvaluator; import com.jetbrains.python.psi.PyBinaryExpression; import com.jetbrains.python.psi.PyElementVisitor; import com.jetbrains.python.psi.PyExpression; @@ -53,14 +55,19 @@ public class PyBinaryExpressionImpl extends PyElementImpl implements PyBinaryExp @Override public @Nullable PyType getType(@NotNull TypeEvalContext context, @NotNull TypeEvalContext.Key key) { - if (isOperator("and") || isOperator("or")) { + if (isOperator(PyNames.AND) || isOperator(PyNames.OR)) { final PyExpression left = getLeftExpression(); - final PyType leftType = left != null ? context.getType(left) : null; + PyType leftType = left != null ? context.getType(left) : null; final PyExpression right = getRightExpression(); final PyType rightType = right != null ? context.getType(right) : null; if (leftType == null && rightType == null) { return null; } + if (isOperator(PyNames.OR)) { + // TODO: also exclude Literal[False, 0, ""] + leftType = Ref.deref(PyTypeAssertionEvaluator.createAssertionType( + leftType, PyBuiltinCache.getInstance(this).getNoneType(), false, true, context)); + } return PyUnionType.union(leftType, rightType); } final String referencedName = getReferencedName(); diff --git a/python/testSrc/com/jetbrains/python/Py3TypeTest.java b/python/testSrc/com/jetbrains/python/Py3TypeTest.java index 45b6fcd7aa83..99bf757e9ae3 100644 --- a/python/testSrc/com/jetbrains/python/Py3TypeTest.java +++ b/python/testSrc/com/jetbrains/python/Py3TypeTest.java @@ -150,6 +150,18 @@ public class Py3TypeTest extends PyTestCase { d = {} expr = d.pop("abc", None)"""); } + + // PY-83348 + public void testOrExpressionType() { + doTest("int | str", """ + def foo(x: int | None): + expr = x or "foo" + """); + doTest("str", """ + def foo(x: None): + expr = x or "foo" + """); + } public void testYieldInsideLambda() { // Checks that foo is not a generator