From a9ecefe92735eddeb6888b5dbb9eb556e63e8ce6 Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Fri, 18 Nov 2016 21:48:28 +0300 Subject: [PATCH] PY-21474 Fixed: Type checking confused by `None` default argument PyTypeAssertionEvaluator: Mark reference which is used as condition in PyConditionalExpression as non-None --- .../controlflow/PyTypeAssertionEvaluator.java | 8 +++++++- .../controlflow/conditionalexpression.txt | 12 ++++++------ python/testSrc/com/jetbrains/python/PyTypeTest.java | 10 ++++++++++ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java b/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java index 6ce86134922f..19250a1f4f29 100644 --- a/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java @@ -83,7 +83,7 @@ public class PyTypeAssertionEvaluator extends PyRecursiveElementVisitor { @Override public void visitPyReferenceExpression(final PyReferenceExpression node) { - if (node.getParent() instanceof PyIfPart) { + if (isUnderIf(node)) { pushAssertion(node, !myPositive, context -> PyNoneType.INSTANCE); return; } @@ -195,6 +195,12 @@ public class PyTypeAssertionEvaluator extends PyRecursiveElementVisitor { myStack.push(new Assertion(target, typeCallback)); } + private static boolean isUnderIf(@NotNull PyReferenceExpression node) { + final PsiElement parent = node.getParent(); + return parent instanceof PyIfPart || + parent instanceof PyConditionalExpression && node == ((PyConditionalExpression)parent).getCondition(); + } + static class Assertion { private final PyReferenceExpression element; private InstructionTypeCallback myFunction; diff --git a/python/testData/codeInsight/controlflow/conditionalexpression.txt b/python/testData/codeInsight/controlflow/conditionalexpression.txt index 49319efc93b2..3b8d5c00ead8 100644 --- a/python/testData/codeInsight/controlflow/conditionalexpression.txt +++ b/python/testData/codeInsight/controlflow/conditionalexpression.txt @@ -8,9 +8,9 @@ 7(8) WRITE ACCESS: c 8(9) element: PyAssignmentStatement 9(10) element: PyConditionalExpression -10(11,12) READ ACCESS: c -11(13) READ ACCESS: x -12(13) READ ACCESS: y -13(14) WRITE ACCESS: z -14() element: null - +10(11,13) READ ACCESS: c +11(12) ASSERTTYPE ACCESS: c +12(14) READ ACCESS: x +13(14) READ ACCESS: y +14(15) WRITE ACCESS: z +15() element: null \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyTypeTest.java b/python/testSrc/com/jetbrains/python/PyTypeTest.java index 7834ff6adb81..ca2e94c9646e 100644 --- a/python/testSrc/com/jetbrains/python/PyTypeTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypeTest.java @@ -1504,6 +1504,16 @@ public class PyTypeTest extends PyTestCase { "expr = foo(a=1, b=\"2\")"); } + // PY-21474 + public void testReassigningOptionalListWithDefaultValue() { + doTest("Union[List[str], list]", + "def x(things):\n" + + " \"\"\"\n" + + " :type things: None | list[str]\n" + + " \"\"\"\n" + + " expr = things if things else []"); + } + private static List getTypeEvalContexts(@NotNull PyExpression element) { return ImmutableList.of(TypeEvalContext.codeAnalysis(element.getProject(), element.getContainingFile()).withTracing(), TypeEvalContext.userInitiated(element.getProject(), element.getContainingFile()).withTracing());