From e6dd07e1593810191e7cd3bace4f2c9ec1697f3e Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Thu, 24 May 2018 16:19:20 +0300 Subject: [PATCH] utility to flatten nested polyadic expressions to avoid reparse diffs IDEA-192541 --- .../siyeh/ig/psiutils/ExpressionUtils.java | 22 +++++++++++++++++++ .../siyeh/ig/psiutils/ParenthesesUtils.java | 13 ++--------- .../src/com/siyeh/ipp/base/Intention.java | 17 ++++++++++---- .../ipp/bool/demorgans/FlattenPolyadic.java | 5 +++++ .../bool/demorgans/FlattenPolyadic_after.java | 5 +++++ .../ipp/bool/DemorgansIntentionTest.java | 1 + 6 files changed, 48 insertions(+), 15 deletions(-) create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/FlattenPolyadic.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/FlattenPolyadic_after.java diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExpressionUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExpressionUtils.java index 82e2186679c6..a4e6be21b9a3 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExpressionUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExpressionUtils.java @@ -1281,4 +1281,26 @@ public class ExpressionUtils { } return null; } + + /** + * Flattens second+ polyadic's operand replaced with another polyadic expression of the same type to the parent's operands. + * + * Otherwise reparse would produce different expression. + */ + public static PsiExpression replacePolyadicWithParent(PsiExpression expressionToReplace, PsiExpression replacement) { + PsiElement parent = expressionToReplace.getParent(); + if (parent instanceof PsiPolyadicExpression && + replacement instanceof PsiPolyadicExpression && + ((PsiPolyadicExpression)parent).getOperationTokenType() == ((PsiPolyadicExpression)replacement).getOperationTokenType()) { + int idx = ArrayUtil.indexOf(((PsiPolyadicExpression)parent).getOperands(), expressionToReplace); + if (idx > 0) { + PsiPolyadicExpression copyParentPolyadic = (PsiPolyadicExpression)parent.copy(); + new CommentTracker().replaceAndRestoreComments(copyParentPolyadic.getOperands()[idx], replacement); + PsiExpression recreateCopyFromText = JavaPsiFacade.getElementFactory(parent.getProject()) + .createExpressionFromText(copyParentPolyadic.getText(), parent); + return ((PsiPolyadicExpression)parent.replace(recreateCopyFromText)).getOperands()[idx]; + } + } + return null; + } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ParenthesesUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ParenthesesUtils.java index 664e78e28c09..878e9a1edec2 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ParenthesesUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ParenthesesUtils.java @@ -18,7 +18,6 @@ package com.siyeh.ig.psiutils; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiPrecedenceUtil; -import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -184,16 +183,8 @@ public class ParenthesesUtils { } final PsiElement parent = parenthesizedExpression.getParent(); if (!(parent instanceof PsiExpression) || !areParenthesesNeeded(body, (PsiExpression)parent, ignoreClarifyingParentheses)) { - final PsiExpression newExpression; - int idx; - if (parent instanceof PsiPolyadicExpression && (idx = ArrayUtil.indexOf(((PsiPolyadicExpression)parent).getOperands(), parenthesizedExpression)) > 0) { - PsiPolyadicExpression copyParentPolyadic = (PsiPolyadicExpression)parent.copy(); - new CommentTracker().replaceAndRestoreComments(copyParentPolyadic.getOperands()[idx], body); - PsiExpression recreateCopyFromText = JavaPsiFacade.getElementFactory(copyParentPolyadic.getProject()) - .createExpressionFromText(copyParentPolyadic.getText(), parent); - newExpression = ((PsiPolyadicExpression)parent.replace(recreateCopyFromText)).getOperands()[idx]; - } - else { + PsiExpression newExpression = ExpressionUtils.replacePolyadicWithParent(parenthesizedExpression, body); + if (newExpression == null){ newExpression = (PsiExpression)new CommentTracker().replaceAndRestoreComments(parenthesizedExpression, body); } removeParentheses(newExpression, ignoreClarifyingParentheses); diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/base/Intention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/base/Intention.java index 799581032a7f..402992288010 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/base/Intention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/base/Intention.java @@ -24,6 +24,7 @@ import com.intellij.psi.codeStyle.CodeStyleManager; import com.siyeh.IntentionPowerPackBundle; import com.siyeh.ig.psiutils.BoolUtils; import com.siyeh.ig.psiutils.CommentTracker; +import com.siyeh.ig.psiutils.ExpressionUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -74,13 +75,21 @@ public abstract class Intention extends BaseElementAtCaretIntentionAction { } expString = "!(" + newExpression + ')'; } - final PsiExpression newCall = factory.createExpressionFromText(expString, expression); assert expressionToReplace != null; - final PsiElement insertedElement = tracker.replaceAndRestoreComments(expressionToReplace, newCall); - final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project); - codeStyleManager.reformat(insertedElement); + PsiExpression newCall = factory.createExpressionFromText(expString, expression); + if (newCall instanceof PsiPolyadicExpression) { + PsiElement insertedElement = ExpressionUtils.replacePolyadicWithParent(expressionToReplace, newCall); + if (insertedElement != null) { + CodeStyleManager.getInstance(project).reformat(insertedElement); + return; + } + } + + PsiElement insertedElement = tracker.replaceAndRestoreComments(expressionToReplace, newCall); + CodeStyleManager.getInstance(project).reformat(insertedElement); } + @Nullable PsiElement findMatchingElement(@Nullable PsiElement element, Editor editor) { while (element != null) { diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/FlattenPolyadic.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/FlattenPolyadic.java new file mode 100644 index 000000000000..967f9ee1ea25 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/FlattenPolyadic.java @@ -0,0 +1,5 @@ +class A { + void foo(boolean a, boolean b, boolean c, boolean d) { + boolean f = a && !(b || c || d); + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/FlattenPolyadic_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/FlattenPolyadic_after.java new file mode 100644 index 000000000000..ee6bf6d48f38 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/FlattenPolyadic_after.java @@ -0,0 +1,5 @@ +class A { + void foo(boolean a, boolean b, boolean c, boolean d) { + boolean f = a && !b && !c && !d; + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/bool/DemorgansIntentionTest.java b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/bool/DemorgansIntentionTest.java index 6b42346a07f8..1907dad4d2db 100644 --- a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/bool/DemorgansIntentionTest.java +++ b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/bool/DemorgansIntentionTest.java @@ -23,6 +23,7 @@ public class DemorgansIntentionTest extends IPPTestCase { public void testNeedsMoreParentheses() { doTest(); } public void testNotTooManyParentheses() { doTest(); } public void testErrorElement() { assertIntentionNotAvailable(); } + public void testFlattenPolyadic() { doTest(); } @Override protected String getIntentionName() {