From 4de6960443151538b7470cf3e638daf4b407bcae Mon Sep 17 00:00:00 2001 From: Ross Hanson Date: Wed, 16 Apr 2014 21:09:26 -0400 Subject: [PATCH] KT-4568 Bug Fix: Refactored the convertNegatedExpressionWithDemorgansLaw intention to better handle longer and more complex expressions. Fixed bug and added appropriate test cases as well. --- ...atedExpressionWithDemorgansLawIntention.kt | 78 ++++++++++++------- .../complexNegation1.kt | 3 + .../complexNegation1.kt.after | 3 + .../complexNegation2.kt | 3 + .../complexNegation2.kt.after | 3 + .../CodeTransformationTestGenerated.java | 10 +++ 6 files changed, 70 insertions(+), 30 deletions(-) create mode 100644 idea/testData/intentions/convertNegatedExpressionWithDemorgansLaw/complexNegation1.kt create mode 100644 idea/testData/intentions/convertNegatedExpressionWithDemorgansLaw/complexNegation1.kt.after create mode 100644 idea/testData/intentions/convertNegatedExpressionWithDemorgansLaw/complexNegation2.kt create mode 100644 idea/testData/intentions/convertNegatedExpressionWithDemorgansLaw/complexNegation2.kt.after diff --git a/idea/src/org/jetbrains/jet/plugin/intentions/ConvertNegatedExpressionWithDemorgansLawIntention.kt b/idea/src/org/jetbrains/jet/plugin/intentions/ConvertNegatedExpressionWithDemorgansLawIntention.kt index 836d36c88b2..d015ce484a8 100644 --- a/idea/src/org/jetbrains/jet/plugin/intentions/ConvertNegatedExpressionWithDemorgansLawIntention.kt +++ b/idea/src/org/jetbrains/jet/plugin/intentions/ConvertNegatedExpressionWithDemorgansLawIntention.kt @@ -26,6 +26,9 @@ import org.jetbrains.jet.lang.psi.JetPsiFactory import com.intellij.psi.impl.source.tree.PsiErrorElementImpl import org.jetbrains.jet.lang.psi.JetPsiUtil import org.jetbrains.jet.lang.psi.JetExpression +import org.jetbrains.jet.lang.psi.JetConstantExpression +import org.jetbrains.jet.lang.psi.JetSimpleNameExpression +import java.util.ArrayList public class ConvertNegatedExpressionWithDemorgansLawIntention : JetSelfTargetingIntention( "convert.negated.expression.with.demorgans.law",javaClass() @@ -36,50 +39,65 @@ public class ConvertNegatedExpressionWithDemorgansLawIntention : JetSelfTargetin if (prefixOperator != JetTokens.EXCL) return false val parenthesizedExpression = element.getBaseExpression() as? JetParenthesizedExpression - var binaryElement = parenthesizedExpression?.getExpression() as? JetBinaryExpression - val operatorToken = binaryElement?.getOperationToken() ?: return false + val baseExpression = parenthesizedExpression?.getExpression() as? JetBinaryExpression ?: return false + val operatorToken = baseExpression.getOperationToken() ?: return false if (!(operatorToken == JetTokens.ANDAND || operatorToken == JetTokens.OROR)) { return false } - while (binaryElement != null) { // While loop lets us handle expressions of any length - val leftChild = binaryElement?.getLeft() - val rightChild = binaryElement?.getRight() - if (rightChild is PsiErrorElementImpl || operatorToken != binaryElement?.getOperationToken()) { - return false - } - binaryElement = leftChild as? JetBinaryExpression - } - - return true + val elements = splitBooleanSequence(baseExpression) ?: return false + return !(elements.any { x -> x is PsiErrorElementImpl }) } override fun applyTo(element: JetPrefixExpression, editor: Editor) { val parenthesizedExpression = element.getBaseExpression() as JetParenthesizedExpression - var binaryElement = parenthesizedExpression.getExpression() as? JetBinaryExpression - val operator = binaryElement!!.getOperationToken() - val operatorText = when(operator){ + val baseExpression = parenthesizedExpression.getExpression() as JetBinaryExpression + val operatorText = when(baseExpression.getOperationToken()){ JetTokens.ANDAND -> JetTokens.OROR.getValue() JetTokens.OROR -> JetTokens.ANDAND.getValue() - else -> throw IllegalArgumentException("Invalid operator: '$operator'. Only expressions using '&&' or '||' can be converted.") + else -> throw IllegalArgumentException( + "Invalid operator: '${baseExpression.getOperationToken()}'. Only expressions using '&&' or '||' can be converted.") } - var expressionText = "" + val elements = splitBooleanSequence(baseExpression) + val negatedElements = elements!!.map { exp -> handleSpecial(exp) } + val negatedExpression = negatedElements.subList(0,negatedElements.size()-1).foldRight( + "${negatedElements.last()}",{negated, exp-> "$exp $operatorText $negated"}) - while (binaryElement != null) { - val leftChild = binaryElement?.getLeft() - val rightChild = binaryElement?.getRight() - - val rightChildText = rightChild!!.getText() - expressionText = "$operatorText !$rightChildText $expressionText " - if (leftChild !is JetBinaryExpression) { - expressionText = "!${leftChild!!.getText()} $expressionText" - } - binaryElement = leftChild as? JetBinaryExpression - } - - val newExpression = JetPsiFactory.createExpression(element.getProject(),expressionText) + val newExpression = JetPsiFactory.createExpression(element.getProject(),negatedExpression) element.replace(newExpression) } + fun handleSpecial(expression: JetExpression) : String { + return when(expression) { + JetTokens.TRUE_KEYWORD -> JetTokens.FALSE_KEYWORD.getValue() + JetTokens.FALSE_KEYWORD -> JetTokens.TRUE_KEYWORD.getValue() + is JetSimpleNameExpression, is JetConstantExpression, is JetPrefixExpression, + is JetParenthesizedExpression -> "!${expression.getText()}" + else -> "!(${expression.getText()})" + } + } + + fun splitBooleanSequence(expression: JetBinaryExpression) : List? { + val itemList = ArrayList() + val firstOperator = expression.getOperationToken() + var currentItem : JetExpression? = expression + + while(currentItem as? JetBinaryExpression != null) { + val remainingExpression = currentItem as JetBinaryExpression + val operation = remainingExpression.getOperationToken() + if (!(operation == JetTokens.ANDAND || operation == JetTokens.OROR)) break + + if (operation != firstOperator) return null //Boolean sequence must be homogenous + + val leftChild = remainingExpression.getLeft() + val rightChild = remainingExpression.getRight() + itemList.add(rightChild as JetExpression) + currentItem = leftChild + } + + if (currentItem != null) itemList.add(currentItem!!) + return itemList + } + } diff --git a/idea/testData/intentions/convertNegatedExpressionWithDemorgansLaw/complexNegation1.kt b/idea/testData/intentions/convertNegatedExpressionWithDemorgansLaw/complexNegation1.kt new file mode 100644 index 00000000000..595c5b01bce --- /dev/null +++ b/idea/testData/intentions/convertNegatedExpressionWithDemorgansLaw/complexNegation1.kt @@ -0,0 +1,3 @@ +fun foo(a: Boolean, c: Int, d: Int) : Boolean { + return !(a && c == d) +} \ No newline at end of file diff --git a/idea/testData/intentions/convertNegatedExpressionWithDemorgansLaw/complexNegation1.kt.after b/idea/testData/intentions/convertNegatedExpressionWithDemorgansLaw/complexNegation1.kt.after new file mode 100644 index 00000000000..c89fffeb01a --- /dev/null +++ b/idea/testData/intentions/convertNegatedExpressionWithDemorgansLaw/complexNegation1.kt.after @@ -0,0 +1,3 @@ +fun foo(a: Boolean, c: Int, d: Int) : Boolean { + return !a || !(c == d) +} \ No newline at end of file diff --git a/idea/testData/intentions/convertNegatedExpressionWithDemorgansLaw/complexNegation2.kt b/idea/testData/intentions/convertNegatedExpressionWithDemorgansLaw/complexNegation2.kt new file mode 100644 index 00000000000..37d73b4b4f2 --- /dev/null +++ b/idea/testData/intentions/convertNegatedExpressionWithDemorgansLaw/complexNegation2.kt @@ -0,0 +1,3 @@ +fun foo(a: Int, b: Int, c: Int, d: Int) : Boolean { + return !(a < b && c == d && a < d) +} \ No newline at end of file diff --git a/idea/testData/intentions/convertNegatedExpressionWithDemorgansLaw/complexNegation2.kt.after b/idea/testData/intentions/convertNegatedExpressionWithDemorgansLaw/complexNegation2.kt.after new file mode 100644 index 00000000000..bf55eb732a6 --- /dev/null +++ b/idea/testData/intentions/convertNegatedExpressionWithDemorgansLaw/complexNegation2.kt.after @@ -0,0 +1,3 @@ +fun foo(a: Int, b: Int, c: Int, d: Int) : Boolean { + return !(a < b) || !(c == d) || !(a < d) +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/plugin/intentions/CodeTransformationTestGenerated.java b/idea/tests/org/jetbrains/jet/plugin/intentions/CodeTransformationTestGenerated.java index 32b7f8ddc86..0a88283cdd5 100644 --- a/idea/tests/org/jetbrains/jet/plugin/intentions/CodeTransformationTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/plugin/intentions/CodeTransformationTestGenerated.java @@ -3023,6 +3023,16 @@ public class CodeTransformationTestGenerated extends AbstractCodeTransformationT JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("idea/testData/intentions/convertNegatedExpressionWithDemorgansLaw"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("complexNegation1.kt") + public void testComplexNegation1() throws Exception { + doTestConvertNegatedExpressionWithDemorgansLaw("idea/testData/intentions/convertNegatedExpressionWithDemorgansLaw/complexNegation1.kt"); + } + + @TestMetadata("complexNegation2.kt") + public void testComplexNegation2() throws Exception { + doTestConvertNegatedExpressionWithDemorgansLaw("idea/testData/intentions/convertNegatedExpressionWithDemorgansLaw/complexNegation2.kt"); + } + @TestMetadata("conjunctionNegation1.kt") public void testConjunctionNegation1() throws Exception { doTestConvertNegatedExpressionWithDemorgansLaw("idea/testData/intentions/convertNegatedExpressionWithDemorgansLaw/conjunctionNegation1.kt");