From 7d07533771580807bcb2bf622848689fe8444934 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Mon, 2 Oct 2017 22:05:59 +0300 Subject: [PATCH] PY-25605 EA-85878 Fix NPE: handle incomplete expressions in PyDemorganIntention --- .../intentions/PyDemorganIntention.java | 18 ++++++++++++------ .../missingOperand.py | 2 ++ .../missingOperand_after.py | 2 ++ .../PythonDemorganLawIntentionTest.java | 15 ++++++++++++--- 4 files changed, 28 insertions(+), 9 deletions(-) create mode 100644 python/testData/intentions/PythonDemorganLawIntentionTest/missingOperand.py create mode 100644 python/testData/intentions/PythonDemorganLawIntentionTest/missingOperand_after.py diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/PyDemorganIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/PyDemorganIntention.java index c1dfe278f1e0..907920f0674a 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/PyDemorganIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/PyDemorganIntention.java @@ -66,6 +66,7 @@ public class PyDemorganIntention extends PyBaseIntentionAction { PyBinaryExpression.class); assert expression != null; final PyElementType op = expression.getOperator(); + assert op != null; final String converted = convertConjunctionExpression(expression, op); replaceExpression(converted, expression); } @@ -85,7 +86,8 @@ public class PyDemorganIntention extends PyBaseIntentionAction { // TODO codeStyleManager.reformat(insertedElement) } - private static String convertConjunctionExpression(PyBinaryExpression exp, PyElementType tokenType) { + @NotNull + private static String convertConjunctionExpression(@NotNull PyBinaryExpression exp, @NotNull PyElementType tokenType) { final PyExpression lhs = exp.getLeftExpression(); final String lhsText; final String rhsText; @@ -108,8 +110,12 @@ public class PyDemorganIntention extends PyBaseIntentionAction { return lhsText + flippedConjunction + rhsText; } - private static String convertLeafExpression(PyExpression condition) { - if (isNegation(condition)) { + @NotNull + private static String convertLeafExpression(@Nullable PyExpression condition) { + if (condition == null) { + return ""; + } + else if (isNegation(condition)) { final PyExpression negated = getNegated(condition); if (negated == null) { return ""; @@ -125,11 +131,11 @@ public class PyDemorganIntention extends PyBaseIntentionAction { } @Nullable - private static PyExpression getNegated(PyExpression expression) { + private static PyExpression getNegated(@NotNull PyExpression expression) { return ((PyPrefixExpression)expression).getOperand(); // TODO strip () } - private static boolean isConjunctionExpression(PyExpression expression, PyElementType tokenType) { + private static boolean isConjunctionExpression(@Nullable PyExpression expression, @NotNull PyElementType tokenType) { if (expression instanceof PyBinaryExpression) { final PyElementType operator = ((PyBinaryExpression)expression).getOperator(); return operator == tokenType; @@ -137,7 +143,7 @@ public class PyDemorganIntention extends PyBaseIntentionAction { return false; } - private static boolean isNegation(PsiElement expression) { + private static boolean isNegation(@Nullable PsiElement expression) { if (!(expression instanceof PyPrefixExpression)) { return false; } diff --git a/python/testData/intentions/PythonDemorganLawIntentionTest/missingOperand.py b/python/testData/intentions/PythonDemorganLawIntentionTest/missingOperand.py new file mode 100644 index 000000000000..8841dc263365 --- /dev/null +++ b/python/testData/intentions/PythonDemorganLawIntentionTest/missingOperand.py @@ -0,0 +1,2 @@ +if a and b and : + pass \ No newline at end of file diff --git a/python/testData/intentions/PythonDemorganLawIntentionTest/missingOperand_after.py b/python/testData/intentions/PythonDemorganLawIntentionTest/missingOperand_after.py new file mode 100644 index 000000000000..11eae7baa00b --- /dev/null +++ b/python/testData/intentions/PythonDemorganLawIntentionTest/missingOperand_after.py @@ -0,0 +1,2 @@ +if not (not a or not b or): + pass \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/intentions/PythonDemorganLawIntentionTest.java b/python/testSrc/com/jetbrains/python/intentions/PythonDemorganLawIntentionTest.java index 6139bc13c129..5e02a92e1575 100644 --- a/python/testSrc/com/jetbrains/python/intentions/PythonDemorganLawIntentionTest.java +++ b/python/testSrc/com/jetbrains/python/intentions/PythonDemorganLawIntentionTest.java @@ -20,18 +20,27 @@ import com.jetbrains.python.PyBundle; public class PythonDemorganLawIntentionTest extends PyIntentionTestCase { public void testOr() { - doIntentionTest(PyBundle.message("INTN.demorgan.law")); + doTest(); } public void testNotOr() { - doIntentionTest(PyBundle.message("INTN.demorgan.law")); + doTest(); } public void testOrNot() { - doIntentionTest(PyBundle.message("INTN.demorgan.law")); + doTest(); } public void testComplex() { + doTest(); + } + + // PY-25605 + public void testMissingOperand() { + doTest(); + } + + private void doTest() { doIntentionTest(PyBundle.message("INTN.demorgan.law")); } } \ No newline at end of file