From 821b6c83f0e922294700ba5f09bc3f5452bf33e3 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Mon, 12 Sep 2016 11:36:31 +0700 Subject: [PATCH] IDEA-CR-13651 BoolUtils.getNegatedExpressionText() used and fixed for float/double (to preserve semantics for NaNs) --- .../StreamApiMigrationInspection.java | 39 ++----------------- .../afterContinueNegateFloat.java | 14 +++++++ .../beforeContinueNegateFloat.java | 19 +++++++++ .../src/com/siyeh/ig/psiutils/BoolUtils.java | 4 +- .../siyeh/ig/psiutils/ComparisonUtils.java | 26 +++++++++++-- 5 files changed, 62 insertions(+), 40 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterContinueNegateFloat.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeContinueNegateFloat.java diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/StreamApiMigrationInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/StreamApiMigrationInspection.java index 8a7dfdf33e0c..4d77a95de225 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/StreamApiMigrationInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/StreamApiMigrationInspection.java @@ -37,8 +37,8 @@ import com.intellij.psi.util.*; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.IntArrayList; +import com.siyeh.ig.psiutils.BoolUtils; import com.siyeh.ig.psiutils.ExpressionUtils; -import com.siyeh.ig.psiutils.ParenthesesUtils; import one.util.streamex.StreamEx; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Nls; @@ -961,43 +961,10 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo myNegated = negated; } - @Contract("null -> false") - private static boolean isFloating(PsiExpression operand) { - if(operand == null) return false; - PsiType type = operand.getType(); - return type instanceof PsiPrimitiveType && (type.equalsToText("double") || type.equalsToText("float")); - } - - private static PsiExpression negate(PsiExpression expression, PsiElementFactory factory) { - if (expression instanceof PsiPrefixExpression && - JavaTokenType.EXCL.equals(((PsiPrefixExpression)expression).getOperationTokenType())) { - return PsiUtil.skipParenthesizedExprDown(((PsiPrefixExpression)expression).getOperand()); - } - if (expression instanceof PsiBinaryExpression) { - PsiBinaryExpression binOp = (PsiBinaryExpression)expression; - if(JavaTokenType.EQEQ.equals(binOp.getOperationTokenType()) || JavaTokenType.NE.equals(binOp.getOperationTokenType())) { - PsiExpression left = binOp.getLOperand(); - PsiExpression right = binOp.getROperand(); - // for float/double types == and != are not strictly opposite (NaNs are treated specially) - if (right != null && !isFloating(left) && !isFloating(right)) { - return factory.createExpressionFromText( - left.getText() + (JavaTokenType.EQEQ.equals(binOp.getOperationTokenType()) ? "!=" : "==") + right.getText(), expression); - } - } - } - String expString; - if (ParenthesesUtils.getPrecedence(expression) > ParenthesesUtils.PREFIX_PRECEDENCE) { - expString = "!(" + expression.getText() + ')'; - } - else { - expString = '!' + expression.getText(); - } - return factory.createExpressionFromText(expString, expression); - } - @Override public String createReplacement(PsiElementFactory factory) { - PsiExpression expression = myNegated ? negate(myExpression, factory) : myExpression; + PsiExpression expression = + myNegated ? factory.createExpressionFromText(BoolUtils.getNegatedExpressionText(myExpression), myExpression) : myExpression; return ".filter(" + compoundLambdaOrMethodReference(myVariable, expression, "java.util.function.Predicate", new PsiType[] {myVariable.getType()}) + ")"; diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterContinueNegateFloat.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterContinueNegateFloat.java new file mode 100644 index 000000000000..7be3e306e818 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterContinueNegateFloat.java @@ -0,0 +1,14 @@ +// "Replace with sum()" "true" + +import java.util.List; + +public class Main { + interface Person { + double getAge(); + } + + public double test(List collection) { + double d = collection.stream().filter(person -> !(person.getAge() == 10)).mapToDouble(Person::getAge).sum(); + return d; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeContinueNegateFloat.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeContinueNegateFloat.java new file mode 100644 index 000000000000..96422d674264 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeContinueNegateFloat.java @@ -0,0 +1,19 @@ +// "Replace with sum()" "true" + +import java.util.List; + +public class Main { + interface Person { + double getAge(); + } + + public double test(List collection) { + double d = 0; + for(Person person : collection) { + if(person.getAge() == 10) + continue; + d = d + person.getAge(); + } + return d; + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/BoolUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/BoolUtils.java index da66f34fb6fd..7f093941dec3 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/BoolUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/BoolUtils.java @@ -17,6 +17,7 @@ package com.siyeh.ig.psiutils; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.TypeConversionUtil; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -84,7 +85,8 @@ public class BoolUtils { } return ParenthesesUtils.getText(negated, precedence); } - else if (ComparisonUtils.isComparison(expression)) { + else if (ComparisonUtils.isComparison(expression) && + !TypeConversionUtil.isFloatOrDoubleType(ComparisonUtils.getComparisonType(expression))) { final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)expression; final String negatedComparison = ComparisonUtils.getNegatedComparison(polyadicExpression.getOperationTokenType()); final StringBuilder result = new StringBuilder(); diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ComparisonUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ComparisonUtils.java index 7c7dc43fbd36..38e3fba16307 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ComparisonUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ComparisonUtils.java @@ -15,10 +15,9 @@ */ package com.siyeh.ig.psiutils; -import com.intellij.psi.JavaTokenType; -import com.intellij.psi.PsiExpression; -import com.intellij.psi.PsiPolyadicExpression; +import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.TypeConversionUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -60,6 +59,27 @@ public class ComparisonUtils { s_invertedComparisons.put(JavaTokenType.LE, ">"); } + /** + * Returns the actual type of compared values in comparison expression after unboxing and promotion if applicable. + * + * @param expression the expression to get the type of compared values + * @return the resulting type or null if expression is not a comparison or type is not known. + */ + @Nullable + public static PsiType getComparisonType(PsiExpression expression) { + if (!(expression instanceof PsiBinaryExpression)) return null; + PsiBinaryExpression binOp = (PsiBinaryExpression)expression; + IElementType tokenType = binOp.getOperationTokenType(); + if (!isComparisonOperation(tokenType)) return null; + PsiExpression left = binOp.getLOperand(); + PsiExpression right = binOp.getROperand(); + PsiType lType = left.getType(); + PsiType rType = right == null ? null : right.getType(); + if (lType == null || rType == null) return null; + if (lType.equals(rType)) return lType; + return TypeConversionUtil.unboxAndBalanceTypes(lType, rType); + } + public static boolean isComparison(@Nullable PsiExpression expression) { if (!(expression instanceof PsiPolyadicExpression)) { return false;