diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java b/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java index 298a355bfc58..ee16e2f8dc99 100644 --- a/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java @@ -448,7 +448,7 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor { boolean isStaticallyTrue = false; if (condition != null) { condition.accept(this); - isStaticallyTrue = PyEvaluator.evaluateAsBoolean(condition, false); + isStaticallyTrue = PyEvaluator.evaluateAsBooleanNoResolve(condition, false); } final Instruction head = myBuilder.prevInstruction; final PyElsePart elsePart = node.getElsePart(); @@ -481,7 +481,7 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor { } final Instruction head = myBuilder.prevInstruction; final PyElsePart elsePart = node.getElsePart(); - if (elsePart == null && !PyEvaluator.evaluateAsBoolean(source, false)) { + if (elsePart == null && !PyEvaluator.evaluateAsBooleanNoResolve(source, false)) { myBuilder.addPendingEdge(node, myBuilder.prevInstruction); } final PyStatementList list = forPart.getStatementList(); @@ -816,7 +816,7 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor { super.visitPyAssertStatement(node); final PyExpression[] args = node.getArguments(); // assert False - if (args.length >= 1 && !PyEvaluator.evaluateAsBoolean(args[0], true)) { + if (args.length >= 1 && !PyEvaluator.evaluateAsBooleanNoResolve(args[0], true)) { abruptFlow(node); return; } diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java b/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java index 1697792bd4f7..772b09207c7a 100644 --- a/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java @@ -120,8 +120,8 @@ public class PyTypeAssertionEvaluator extends PyRecursiveElementVisitor { } } - final Object leftValue = new PyEvaluator().evaluate(lhs); - final Object rightValue = new PyEvaluator().evaluate(rhs); + final Object leftValue = PyEvaluator.evaluateNoResolve(lhs, Object.class); + final Object rightValue = PyEvaluator.evaluateNoResolve(rhs, Object.class); if (leftValue instanceof Boolean && rightValue instanceof Boolean) { return; diff --git a/python/src/com/jetbrains/python/psi/impl/PyEvaluator.java b/python/src/com/jetbrains/python/psi/impl/PyEvaluator.java index 8d7182a8f7f8..2b65ca79ec91 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyEvaluator.java +++ b/python/src/com/jetbrains/python/psi/impl/PyEvaluator.java @@ -55,6 +55,11 @@ public class PyEvaluator { */ private boolean myEvaluateKeys = true; + /** + * if true, references will be resolved + */ + private boolean myEnableResolve = true; + public void setNamespace(@Nullable Map namespace) { myNamespace = namespace; } @@ -67,6 +72,10 @@ public class PyEvaluator { myEvaluateKeys = evaluateKeys; } + public void enableResolve(boolean enableResolve) { + myEnableResolve = enableResolve; + } + @Nullable @Contract("null -> null") public Object evaluate(@Nullable PyExpression expression) { @@ -242,6 +251,9 @@ public class PyEvaluator { if (myNamespace != null) { return myNamespace.get(expression.getReferencedName()); } + if (!myEnableResolve) { + return null; + } final ResolveResult[] results = expression.getReference(PyResolveContext.noImplicits()).multiResolve(false); if (results.length != 1) { return null; @@ -332,9 +344,9 @@ public class PyEvaluator { /** * Shortcut that evaluates expression with default params and casts it to particular type (if possible) * + * @param expected type * @param expression expression to evaluate * @param resultType expected type - * @param expected type * @return value if expression is evaluated to this type, null otherwise */ @Nullable @@ -342,6 +354,21 @@ public class PyEvaluator { return PyUtil.as(new PyEvaluator().evaluate(expression), resultType); } + /** + * Shortcut that evaluates expression with default params and disabled resolve and casts it to particular type (if possible) + * + * @param expected type + * @param expression expression to evaluate + * @param resultType expected type + * @return value if expression is evaluated to this type, null otherwise + */ + @Nullable + public static T evaluateNoResolve(@Nullable PyExpression expression, @NotNull Class resultType) { + final PyEvaluator evaluator = new PyEvaluator(); + evaluator.enableResolve(false); + return PyUtil.as(evaluator.evaluate(expression), resultType); + } + /** * Shortcut that evaluates expression and tries to determine if `bool` will return true for it * @@ -350,10 +377,39 @@ public class PyEvaluator { */ @Nullable public static Boolean evaluateAsBoolean(@Nullable PyExpression expression) { + return evaluateAsBoolean(prepareEvaluatorForBoolean(true), expression); + } + + /** + * Shortcut that evaluates expression and tries to determine if `bool` will return true for it + * + * @param expression expression to evaluate + * @return true if expression is evaluated to value so `bool` returns true for it + */ + @Nullable + public static Boolean evaluateAsBooleanNoResolve(@Nullable PyExpression expression) { + return evaluateAsBoolean(prepareEvaluatorForBoolean(false), expression); + } + + public static boolean evaluateAsBoolean(@Nullable PyExpression expression, boolean defaultValue) { + return ObjectUtils.notNull(evaluateAsBoolean(expression), defaultValue); + } + + public static boolean evaluateAsBooleanNoResolve(@Nullable PyExpression expression, boolean defaultValue) { + return ObjectUtils.notNull(evaluateAsBooleanNoResolve(expression), defaultValue); + } + + @NotNull + private static PyEvaluator prepareEvaluatorForBoolean(boolean enableResolve) { final PyEvaluator evaluator = new PyEvaluator(); evaluator.setEvaluateCollectionItems(false); evaluator.setEvaluateKeys(false); + evaluator.enableResolve(enableResolve); + return evaluator; + } + @Nullable + private static Boolean evaluateAsBoolean(@NotNull PyEvaluator evaluator, @Nullable PyExpression expression) { final Object result = evaluator.evaluate(expression); if (result instanceof Boolean) { @@ -374,8 +430,4 @@ public class PyEvaluator { return null; } - - public static boolean evaluateAsBoolean(@Nullable PyExpression expression, boolean defaultValue) { - return ObjectUtils.notNull(evaluateAsBoolean(expression), defaultValue); - } } diff --git a/python/testData/codeInsight/controlflow/fortrycontinue.txt b/python/testData/codeInsight/controlflow/fortrycontinue.txt index edf39b7f813c..bc5ad352e7f1 100644 --- a/python/testData/codeInsight/controlflow/fortrycontinue.txt +++ b/python/testData/codeInsight/controlflow/fortrycontinue.txt @@ -2,7 +2,7 @@ 1(2) element: PyAssignmentStatement 2(3) WRITE ACCESS: tests 3(4) element: PyForStatement -4(5) READ ACCESS: tests +4(5,17) READ ACCESS: tests 5(6) element: PyTargetExpression: t 6(7) WRITE ACCESS: t 7(8) element: PyTryExceptStatement diff --git a/python/testSrc/com/jetbrains/python/PyEvaluatorTest.java b/python/testSrc/com/jetbrains/python/PyEvaluatorTest.java index 00726fe32adf..a66ca7e26b18 100644 --- a/python/testSrc/com/jetbrains/python/PyEvaluatorTest.java +++ b/python/testSrc/com/jetbrains/python/PyEvaluatorTest.java @@ -281,6 +281,38 @@ public class PyEvaluatorTest extends PyTestCase { assertFalse(PyEvaluator.evaluateAsBoolean(parseExpression("[]"))); } + public void testEvaluateAsBooleanNoResolve() { + assertTrue(PyEvaluator.evaluateAsBooleanNoResolve(parseExpression("True"))); + assertFalse(PyEvaluator.evaluateAsBooleanNoResolve(parseExpression("False"))); + + assertTrue(PyEvaluator.evaluateAsBooleanNoResolve(parseExpression("\'a\'"))); + assertFalse(PyEvaluator.evaluateAsBooleanNoResolve(parseExpression("\'\'"))); + + assertTrue(PyEvaluator.evaluateAsBooleanNoResolve(parseExpression("1"))); + assertFalse(PyEvaluator.evaluateAsBooleanNoResolve(parseExpression("0"))); + + assertTrue(PyEvaluator.evaluateAsBooleanNoResolve(parseExpression("{1: 0}"))); + assertFalse(PyEvaluator.evaluateAsBooleanNoResolve(parseExpression("{}"))); + + assertTrue(PyEvaluator.evaluateAsBooleanNoResolve(parseExpression("[1]"))); + assertFalse(PyEvaluator.evaluateAsBooleanNoResolve(parseExpression("[]"))); + + assertNull(PyEvaluator.evaluateAsBooleanNoResolve(parseText("a = True\nexpr = a"))); + assertNull(PyEvaluator.evaluateAsBooleanNoResolve(parseText("a = False\nexpr = a"))); + + assertNull(PyEvaluator.evaluateAsBooleanNoResolve(parseText("a = \'a\'\nexpr = a"))); + assertNull(PyEvaluator.evaluateAsBooleanNoResolve(parseText("a = \'\'\nexpr = a"))); + + assertNull(PyEvaluator.evaluateAsBooleanNoResolve(parseText("a = 1\nexpr = a"))); + assertNull(PyEvaluator.evaluateAsBooleanNoResolve(parseText("a = 0\nexpr = a"))); + + assertNull(PyEvaluator.evaluateAsBooleanNoResolve(parseText("a = {1: 0}\nexpr = a"))); + assertNull(PyEvaluator.evaluateAsBooleanNoResolve(parseText("a = {}\nexpr = a"))); + + assertNull(PyEvaluator.evaluateAsBooleanNoResolve(parseText("a = [1]\nexpr = a"))); + assertNull(PyEvaluator.evaluateAsBooleanNoResolve(parseText("a = []\nexpr = a"))); + } + @NotNull private T byExpression(@NotNull String expression, @NotNull Class cls) { final Object value = new PyEvaluator().evaluate(parseExpression(expression));