diff --git a/java/java-impl/src/com/intellij/codeInspection/streamMigration/BaseStreamApiMigration.java b/java/java-impl/src/com/intellij/codeInspection/streamMigration/BaseStreamApiMigration.java index 0ae365df0b0d..ac53283837a6 100644 --- a/java/java-impl/src/com/intellij/codeInspection/streamMigration/BaseStreamApiMigration.java +++ b/java/java-impl/src/com/intellij/codeInspection/streamMigration/BaseStreamApiMigration.java @@ -21,7 +21,6 @@ import com.intellij.psi.controlFlow.*; import com.intellij.psi.util.PsiTreeUtil; import com.siyeh.ig.psiutils.ControlFlowUtils; import com.siyeh.ig.psiutils.ControlFlowUtils.InitializerUsageStatus; -import com.siyeh.ig.psiutils.ExpressionUtils; import org.jetbrains.annotations.NotNull; /** @@ -46,22 +45,25 @@ abstract class BaseStreamApiMigration { return myShouldWarn; } - static PsiElement replaceWithNumericAddition(PsiLoopStatement loopStatement, - PsiVariable var, - String streamText, - PsiType expressionType) { + static PsiElement replaceWithOperation(PsiLoopStatement loopStatement, + PsiVariable var, + String streamText, + PsiType expressionType, + OperationReductionMigration.ReductionOperation reductionOperation) { PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(loopStatement.getProject()); restoreComments(loopStatement, loopStatement.getBody()); InitializerUsageStatus status = ControlFlowUtils.getInitializerUsageStatus(var, loopStatement); - if (status != ControlFlowUtils.InitializerUsageStatus.UNKNOWN) { + if (status != InitializerUsageStatus.UNKNOWN) { PsiExpression initializer = var.getInitializer(); - if (ExpressionUtils.isZero(initializer)) { + if (initializer != null && reductionOperation.getInitializerExpressionRestriction().test(initializer)) { PsiType type = var.getType(); - String replacement = (type.equals(expressionType) ? "" : "(" + type.getCanonicalText() + ") ") + streamText; + String replacement = (type.isAssignableFrom(expressionType) ? "" : "(" + type.getCanonicalText() + ") ") + streamText; return replaceInitializer(loopStatement, var, initializer, replacement, status); } } - return loopStatement.replace(elementFactory.createStatementFromText(var.getName() + "+=" + streamText + ";", loopStatement)); + return loopStatement + .replace(elementFactory.createStatementFromText(var.getName() + reductionOperation.getOperation() + "=" + streamText + ";", + loopStatement)); } static PsiElement replaceInitializer(PsiLoopStatement loopStatement, @@ -71,12 +73,13 @@ abstract class BaseStreamApiMigration { InitializerUsageStatus status) { Project project = loopStatement.getProject(); PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project); - if(status == ControlFlowUtils.InitializerUsageStatus.DECLARED_JUST_BEFORE) { + if (status == ControlFlowUtils.InitializerUsageStatus.DECLARED_JUST_BEFORE) { initializer.replace(elementFactory.createExpressionFromText(replacement, loopStatement)); removeLoop(loopStatement); return var; - } else { - if(status == ControlFlowUtils.InitializerUsageStatus.AT_WANTED_PLACE_ONLY) { + } + else { + if (status == ControlFlowUtils.InitializerUsageStatus.AT_WANTED_PLACE_ONLY) { initializer.delete(); } return diff --git a/java/java-impl/src/com/intellij/codeInspection/streamMigration/CountMigration.java b/java/java-impl/src/com/intellij/codeInspection/streamMigration/CountMigration.java index 284859166178..47317ec23267 100644 --- a/java/java-impl/src/com/intellij/codeInspection/streamMigration/CountMigration.java +++ b/java/java-impl/src/com/intellij/codeInspection/streamMigration/CountMigration.java @@ -19,6 +19,8 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.*; import org.jetbrains.annotations.NotNull; +import static com.intellij.codeInspection.streamMigration.OperationReductionMigration.SUM_OPERATION; + /** * @author Tagir Valeev */ @@ -39,6 +41,6 @@ class CountMigration extends BaseStreamApiMigration { PsiElement element = ((PsiReferenceExpression)operand).resolve(); if (!(element instanceof PsiLocalVariable)) return null; PsiLocalVariable var = (PsiLocalVariable)element; - return replaceWithNumericAddition(tb.getMainLoop(), var, tb.generate() + ".count()", PsiType.LONG); + return replaceWithOperation(tb.getMainLoop(), var, tb.generate() + ".count()", PsiType.LONG, SUM_OPERATION); } } diff --git a/java/java-impl/src/com/intellij/codeInspection/streamMigration/OperationReductionMigration.java b/java/java-impl/src/com/intellij/codeInspection/streamMigration/OperationReductionMigration.java new file mode 100644 index 000000000000..878a28ae96ba --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInspection/streamMigration/OperationReductionMigration.java @@ -0,0 +1,191 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInspection.streamMigration; + +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.TypeConversionUtil; +import com.siyeh.ig.psiutils.ExpressionUtils; +import com.siyeh.ig.psiutils.ParenthesesUtils; +import org.jetbrains.annotations.NotNull; + +import java.util.Locale; +import java.util.function.Predicate; + +/** + * Created by Roman Ivanov. + */ +public class OperationReductionMigration extends BaseStreamApiMigration { + private final ReductionOperation myReductionOperation; + + protected OperationReductionMigration(boolean shouldWarn, + ReductionOperation context) { + super(shouldWarn, "reduce()"); + myReductionOperation = context; + } + + @Override + PsiElement migrate(@NotNull Project project, @NotNull PsiStatement body, @NotNull TerminalBlock tb) { + PsiAssignmentExpression assignment = tb.getSingleExpression(PsiAssignmentExpression.class); + if (assignment == null) return null; + PsiVariable var = StreamApiMigrationInspection.extractAccumulator(assignment, myReductionOperation.getCompoundAssignmentOp()); + if (var == null) return null; + + PsiExpression operand = StreamApiMigrationInspection.extractOperand(assignment, myReductionOperation.getCompoundAssignmentOp()); + if (operand == null) return null; + PsiType type = var.getType(); + + PsiType operandType = operand.getType(); + if (operandType != null && !TypeConversionUtil.isAssignable(type, operandType)) { + operand = JavaPsiFacade.getElementFactory(project).createExpressionFromText( + "(" + type.getCanonicalText() + ")" + ParenthesesUtils.getText(operand, ParenthesesUtils.MULTIPLICATIVE_PRECEDENCE), operand); + } + JavaCodeStyleManager javaStyle = JavaCodeStyleManager.getInstance(project); + String leftOperand = javaStyle.suggestUniqueVariableName("a", body, true); + String rightOperand = javaStyle.suggestUniqueVariableName("b", body, true); + + if(type.equals(PsiType.BOOLEAN)) { + type = PsiType.BOOLEAN.getBoxedType(body); // hack to avoid .map(b -> b) when boxing needed + } + + PsiExpression initializer = var.getInitializer(); + String identity = initializer != null && myReductionOperation.getInitializerExpressionRestriction().test(initializer) + ? initializer.getText() + : myReductionOperation.myIdentity; + String stream = tb.add(new StreamApiMigrationInspection.MapOp(operand, tb.getVariable(), type)).generate() + + String.format(Locale.ENGLISH, ".reduce(%s, (%s, %s) -> %s %s %s)", + identity, leftOperand, rightOperand, leftOperand, + myReductionOperation.getOperation(), rightOperand); + return replaceWithOperation(tb.getMainLoop(), var, stream, type, myReductionOperation); + } + + static class ReductionOperation { + private final IElementType myCompoundAssignmentOp; + private final Predicate myInitializerReplaceCondition; + private final Predicate myAccumulatorRestriction; + private final String myIdentity; + private final String myOperation; + + + public ReductionOperation(IElementType compoundAssignmentOp, + Predicate initializerReplaceCondition, + Predicate accumulatorRestriction, + String identity, + String operation) { + myCompoundAssignmentOp = compoundAssignmentOp; + myInitializerReplaceCondition = initializerReplaceCondition; + myAccumulatorRestriction = accumulatorRestriction; + myIdentity = identity; + myOperation = operation; + } + + public IElementType getCompoundAssignmentOp() { + return myCompoundAssignmentOp; + } + + public Predicate getInitializerExpressionRestriction() { + return myInitializerReplaceCondition; + } + + public String getIdentity() { + return myIdentity; + } + + public String getOperation() { + return myOperation; + } + + public Predicate getAccumulatorRestriction() { + return myAccumulatorRestriction; + } + } + + static final ReductionOperation SUM_OPERATION = new ReductionOperation( + JavaTokenType.PLUSEQ, + ExpressionUtils::isZero, + OperationReductionMigration::arithmeticTypeRestriction, + "0", + "+" + ); + + static final ReductionOperation[] OPERATIONS = { + new ReductionOperation( + JavaTokenType.ASTERISKEQ, + ExpressionUtils::isOne, + OperationReductionMigration::arithmeticTypeRestriction, + "1", + "*" + ), + new ReductionOperation( + JavaTokenType.ANDEQ, + expression -> Boolean.TRUE.equals(ExpressionUtils.computeConstantExpression(expression)), + OperationReductionMigration::booleanTypeRestriction, + "true", + "&&" + ), + new ReductionOperation( + JavaTokenType.OREQ, + expression -> Boolean.FALSE.equals(ExpressionUtils.computeConstantExpression(expression)), + OperationReductionMigration::booleanTypeRestriction, + "false", + "||" + ), + new ReductionOperation( + JavaTokenType.OREQ, + ExpressionUtils::isZero, + OperationReductionMigration::arithmeticTypeRestriction, + "0", + "|" + ), + new ReductionOperation( + JavaTokenType.ANDEQ, + OperationReductionMigration::isMinusOne, + OperationReductionMigration::bitwiseTypeRestriction, + "-1", + "&" + ), + new ReductionOperation( + JavaTokenType.XOREQ, + ExpressionUtils::isZero, + OperationReductionMigration::bitwiseTypeRestriction, + "0", + "^" + ) + }; + + private static boolean isMinusOne(PsiExpression expression) { + Object constant = ExpressionUtils.computeConstantExpression(expression); + if(constant == null) { + return false; + } + return (constant instanceof Integer || constant instanceof Long) && ((Number)constant).longValue() == -1; + } + + static boolean bitwiseTypeRestriction(@NotNull PsiVariable variable) { + return variable.getType() instanceof PsiPrimitiveType + && (variable.getType().equals(PsiType.INT) || variable.getType().equals(PsiType.LONG)); + } + + static boolean arithmeticTypeRestriction(@NotNull PsiVariable variable) { + return variable.getType() instanceof PsiPrimitiveType && !variable.getType().equals(PsiType.FLOAT); + } + + private static boolean booleanTypeRestriction(@NotNull PsiVariable variable) { + return variable.getType().equalsToText("boolean") || variable.getType().equalsToText(CommonClassNames.JAVA_LANG_BOOLEAN); + } +} diff --git a/java/java-impl/src/com/intellij/codeInspection/streamMigration/StreamApiMigrationInspection.java b/java/java-impl/src/com/intellij/codeInspection/streamMigration/StreamApiMigrationInspection.java index cb8986867797..c953acc8b324 100644 --- a/java/java-impl/src/com/intellij/codeInspection/streamMigration/StreamApiMigrationInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/streamMigration/StreamApiMigrationInspection.java @@ -34,6 +34,7 @@ import com.intellij.psi.*; import com.intellij.psi.controlFlow.*; import com.intellij.psi.search.LocalSearchScope; import com.intellij.psi.search.searches.ReferencesSearch; +import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; @@ -51,6 +52,7 @@ import java.util.Collection; import java.util.List; import java.util.Objects; +import static com.intellij.codeInspection.streamMigration.OperationReductionMigration.SUM_OPERATION; import static com.intellij.util.ObjectUtils.tryCast; import static com.siyeh.ig.psiutils.ControlFlowUtils.InitializerUsageStatus.UNKNOWN; @@ -109,22 +111,22 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo @Nullable static PsiReturnStatement getNextReturnStatement(PsiStatement statement) { PsiElement nextStatement = PsiTreeUtil.skipWhitespacesAndCommentsForward(statement); - if(nextStatement instanceof PsiReturnStatement) return (PsiReturnStatement)nextStatement; + if (nextStatement instanceof PsiReturnStatement) return (PsiReturnStatement)nextStatement; PsiElement parent = statement.getParent(); - if(parent instanceof PsiCodeBlock) { + if (parent instanceof PsiCodeBlock) { PsiStatement[] statements = ((PsiCodeBlock)parent).getStatements(); - if(statements.length == 0 || statements[statements.length-1] != statement) return null; + if (statements.length == 0 || statements[statements.length - 1] != statement) return null; parent = parent.getParent(); - if(!(parent instanceof PsiBlockStatement)) return null; + if (!(parent instanceof PsiBlockStatement)) return null; parent = parent.getParent(); } - if(parent instanceof PsiIfStatement) return getNextReturnStatement((PsiStatement)parent); + if (parent instanceof PsiIfStatement) return getNextReturnStatement((PsiStatement)parent); return null; } @Contract("null, null -> true; null, !null -> false") private static boolean sameReference(PsiExpression expr1, PsiExpression expr2) { - if(expr1 == null && expr2 == null) return true; + if (expr1 == null && expr2 == null) return true; if (!(expr1 instanceof PsiReferenceExpression) || !(expr2 instanceof PsiReferenceExpression)) return false; PsiReferenceExpression ref1 = (PsiReferenceExpression)expr1; PsiReferenceExpression ref2 = (PsiReferenceExpression)expr2; @@ -140,36 +142,53 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo */ @Nullable static PsiExpression extractAddend(PsiAssignmentExpression assignment) { - if(JavaTokenType.PLUSEQ.equals(assignment.getOperationTokenType())) { - return assignment.getRExpression(); - } else if(JavaTokenType.EQ.equals(assignment.getOperationTokenType())) { - if (assignment.getRExpression() instanceof PsiBinaryExpression) { - PsiBinaryExpression binOp = (PsiBinaryExpression)assignment.getRExpression(); - if(JavaTokenType.PLUS.equals(binOp.getOperationTokenType())) { - if(sameReference(binOp.getLOperand(), assignment.getLExpression())) { - return binOp.getROperand(); - } - if(sameReference(binOp.getROperand(), assignment.getLExpression())) { - return binOp.getLOperand(); - } + return extractOperand(assignment, JavaTokenType.PLUSEQ); + } + + + @Nullable + static PsiExpression extractOperand(PsiAssignmentExpression assignment, IElementType compoundAssignmentOp) { + if (compoundAssignmentOp.equals(assignment.getOperationTokenType())) { + return assignment.getRExpression(); + } + else if (JavaTokenType.EQ.equals(assignment.getOperationTokenType())) { + if (assignment.getRExpression() instanceof PsiBinaryExpression) { + PsiBinaryExpression binOp = (PsiBinaryExpression)assignment.getRExpression(); + IElementType op = TypeConversionUtil.convertEQtoOperation(compoundAssignmentOp); + if (op.equals(binOp.getOperationTokenType())) { + if (sameReference(binOp.getLOperand(), assignment.getLExpression())) { + return binOp.getROperand(); + } + if (sameReference(binOp.getROperand(), assignment.getLExpression())) { + return binOp.getLOperand(); } } } - return null; + } + return null; } + @Nullable - static PsiVariable extractAccumulator(PsiAssignmentExpression assignment) { + static PsiVariable extractSumAccumulator(PsiAssignmentExpression assignment) { + return extractAccumulator(assignment, JavaTokenType.PLUSEQ); + } + + + @Nullable + static PsiVariable extractAccumulator(PsiAssignmentExpression assignment, IElementType compoundAssignmentOp) { PsiReferenceExpression lExpr = tryCast(assignment.getLExpression(), PsiReferenceExpression.class); - if(lExpr == null) return null; + if (lExpr == null) return null; PsiVariable var = tryCast(lExpr.resolve(), PsiVariable.class); - if(var == null) return null; - if(JavaTokenType.PLUSEQ.equals(assignment.getOperationTokenType())) { + if (var == null) return null; + if (compoundAssignmentOp.equals(assignment.getOperationTokenType())) { return var; - } else if(JavaTokenType.EQ.equals(assignment.getOperationTokenType())) { + } + else if (JavaTokenType.EQ.equals(assignment.getOperationTokenType())) { if (assignment.getRExpression() instanceof PsiBinaryExpression) { PsiBinaryExpression binOp = (PsiBinaryExpression)assignment.getRExpression(); - if(JavaTokenType.PLUS.equals(binOp.getOperationTokenType())) { + IElementType op = TypeConversionUtil.convertEQtoOperation(compoundAssignmentOp); + if (op.equals(binOp.getOperationTokenType())) { PsiExpression left = binOp.getLOperand(); PsiExpression right = binOp.getROperand(); if (sameReference(left, lExpr) || sameReference(right, lExpr)) { @@ -189,17 +208,19 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo */ @Contract("null -> null") static PsiExpression extractIncrementedLValue(PsiExpression expression) { - if(expression instanceof PsiPostfixExpression) { - if(JavaTokenType.PLUSPLUS.equals(((PsiPostfixExpression)expression).getOperationTokenType())) { + if (expression instanceof PsiPostfixExpression) { + if (JavaTokenType.PLUSPLUS.equals(((PsiPostfixExpression)expression).getOperationTokenType())) { return ((PsiPostfixExpression)expression).getOperand(); } - } else if(expression instanceof PsiPrefixExpression) { - if(JavaTokenType.PLUSPLUS.equals(((PsiPrefixExpression)expression).getOperationTokenType())) { + } + else if (expression instanceof PsiPrefixExpression) { + if (JavaTokenType.PLUSPLUS.equals(((PsiPrefixExpression)expression).getOperationTokenType())) { return ((PsiPrefixExpression)expression).getOperand(); } - } else if(expression instanceof PsiAssignmentExpression) { + } + else if (expression instanceof PsiAssignmentExpression) { PsiAssignmentExpression assignment = (PsiAssignmentExpression)expression; - if(ExpressionUtils.isLiteral(extractAddend(assignment), 1)) { + if (ExpressionUtils.isLiteral(extractAddend(assignment), 1)) { return assignment.getLExpression(); } } @@ -209,11 +230,11 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo @Nullable private static PsiLocalVariable getIncrementedVariable(PsiExpression expression, TerminalBlock tb, List variables) { // have only one non-final variable - if(variables.size() != 1) return null; + if (variables.size() != 1) return null; // have single expression which is either ++x or x++ or x+=1 or x=x+1 PsiReferenceExpression operand = tryCast(extractIncrementedLValue(expression), PsiReferenceExpression.class); - if(operand == null) return null; + if (operand == null) return null; PsiLocalVariable variable = tryCast(operand.resolve(), PsiLocalVariable.class); // the referred variable is the same as non-final variable and not used in intermediate operations @@ -224,23 +245,26 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo } @Nullable - private static PsiVariable getAccumulatedVariable(TerminalBlock tb, List variables) { + private static PsiVariable getAccumulatedVariable(TerminalBlock tb, + List variables, + OperationReductionMigration.ReductionOperation operation) { + IElementType compoundAssignmentOp = operation.getCompoundAssignmentOp(); // have only one non-final variable - if(variables.size() != 1) return null; + if (variables.size() != 1) return null; PsiAssignmentExpression assignment = tb.getSingleExpression(PsiAssignmentExpression.class); - if(assignment == null) return null; - PsiVariable var = extractAccumulator(assignment); + if (assignment == null) return null; + PsiVariable var = extractAccumulator(assignment, compoundAssignmentOp); // the referred variable is the same as non-final variable - if(var == null || !variables.contains(var)) return null; - if (!(var.getType() instanceof PsiPrimitiveType) || var.getType().equalsToText("float")) return null; + if (var == null || !variables.contains(var)) return null; + if (!operation.getAccumulatorRestriction().test(var)) return null; // the referred variable is not used in intermediate operations - if(tb.isReferencedInOperations(var)) return null; - PsiExpression addend = extractAddend(assignment); - LOG.assertTrue(addend != null); - if(VariableAccessUtils.variableIsUsed(var, addend)) return null; + if (tb.isReferencedInOperations(var)) return null; + PsiExpression operand = extractOperand(assignment, compoundAssignmentOp); + LOG.assertTrue(operand != null); + if (VariableAccessUtils.variableIsUsed(var, operand)) return null; return var; } @@ -284,7 +308,8 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo if (tb.isEmpty()) { // like "if(++count == limit) break" variable = getIncrementedVariable(counter, tb, nonFinalVariables); - } else if (!ExpressionUtils.isReferenceTo(counter, variable)) { + } + else if (!ExpressionUtils.isReferenceTo(counter, variable)) { return false; } return variable != null && @@ -310,7 +335,7 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo } static boolean isVariableSuitableForStream(PsiVariable variable, PsiStatement statement, TerminalBlock tb) { - if(ReferencesSearch.search(variable).forEach(ref -> { + if (ReferencesSearch.search(variable).forEach(ref -> { PsiExpression expression = tryCast(ref.getElement(), PsiExpression.class); return expression == null || !PsiUtil.isAccessedForWriting(expression) || @@ -324,14 +349,16 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo static String tryUnbox(PsiVariable variable) { PsiType type = variable.getType(); String mapOp = null; - if(type.equals(PsiType.INT)) { + if (type.equals(PsiType.INT)) { mapOp = "mapToInt"; - } else if(type.equals(PsiType.LONG)) { + } + else if (type.equals(PsiType.LONG)) { mapOp = "mapToLong"; - } else if(type.equals(PsiType.DOUBLE)) { + } + else if (type.equals(PsiType.DOUBLE)) { mapOp = "mapToDouble"; } - return mapOp == null ? "" : "."+mapOp+"("+variable.getName()+" -> "+variable.getName()+")"; + return mapOp == null ? "" : "." + mapOp + "(" + variable.getName() + " -> " + variable.getName() + ")"; } static boolean isExpressionDependsOnUpdatedCollections(PsiExpression condition, @@ -403,9 +430,9 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo void processLoop(PsiLoopStatement statement) { final PsiStatement body = statement.getBody(); - if(body == null) return; + if (body == null) return; StreamSource source = StreamSource.tryCreate(statement); - if(source == null) return; + if (source == null) return; if (!ExceptionUtil.getThrownCheckedExceptions(body).isEmpty()) return; TerminalBlock tb = TerminalBlock.from(source, body); @@ -435,7 +462,7 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo } int startOffset = controlFlow.getStartOffset(body); int endOffset = controlFlow.getEndOffset(body); - if(startOffset < 0 || endOffset < 0) return null; + if (startOffset < 0 || endOffset < 0) return null; PsiElement surrounder = PsiTreeUtil.getParentOfType(loop, PsiLambdaExpression.class, PsiClass.class); final List nonFinalVariables = StreamEx.of(ControlFlowUtil.getUsedVariables(controlFlow, startOffset, endOffset)) .remove(variable -> variable instanceof PsiField) @@ -447,11 +474,11 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo } if (nonFinalVariables.isEmpty()) { CollectMigration.CollectTerminal terminal = CollectMigration.extractCollectTerminal(tb); - if(terminal != null) { + if (terminal != null) { boolean addAll = loop instanceof PsiForeachStatement && !tb.hasOperations() && isAddAllCall(tb); // Don't suggest to convert the loop which can be trivially replaced via addAll: // this is covered by UseBulkOperationInspection and ManualArrayToCollectionCopyInspection - if(addAll) return null; + if (addAll) return null; boolean shouldWarn = REPLACE_TRIVIAL_FOREACH || tb.hasOperations() || tb.getLastOperation() instanceof BufferedReaderLines || @@ -463,9 +490,14 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo if (nonFinalVariables.isEmpty() && extractArray(tb) != null) { return new ToArrayMigration(true); } - if (getAccumulatedVariable(tb, nonFinalVariables) != null) { + if (getAccumulatedVariable(tb, nonFinalVariables, SUM_OPERATION) != null) { return new SumMigration(true); } + for (OperationReductionMigration.ReductionOperation reductionOperation : OperationReductionMigration.OPERATIONS) { + if (getAccumulatedVariable(tb, nonFinalVariables, reductionOperation) != null) { + return new OperationReductionMigration(true, reductionOperation); + } + } Collection exitPoints = tb.findExitPoints(controlFlow); if (exitPoints == null) return null; if (exitPoints.isEmpty() && nonFinalVariables.isEmpty()) { @@ -508,14 +540,14 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo } if (nonFinalVariables.size() == 1) { PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(statement); - if(assignment == null) return null; + if (assignment == null) return null; PsiReferenceExpression lValue = tryCast(assignment.getLExpression(), PsiReferenceExpression.class); if (lValue == null) return null; PsiVariable var = tryCast(lValue.resolve(), PsiVariable.class); - if(var == null || !nonFinalVariables.contains(var)) return null; + if (var == null || !nonFinalVariables.contains(var)) return null; PsiExpression rValue = assignment.getRExpression(); - if(rValue == null || VariableAccessUtils.variableIsUsed(var, rValue)) return null; - if(tb.getVariable().getType() instanceof PsiPrimitiveType && !ExpressionUtils.isReferenceTo(rValue, tb.getVariable())) return null; + if (rValue == null || VariableAccessUtils.variableIsUsed(var, rValue)) return null; + if (tb.getVariable().getType() instanceof PsiPrimitiveType && !ExpressionUtils.isReferenceTo(rValue, tb.getVariable())) return null; return new FindFirstMigration(shouldWarn); } return null; @@ -537,12 +569,12 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo else { methodName = "noneMatch"; FilterOp lastFilter = tb.getLastOperation(FilterOp.class); - if(lastFilter != null && (lastFilter.isNegated() ^ BoolUtils.isNegation(lastFilter.getExpression()))) { + if (lastFilter != null && (lastFilter.isNegated() ^ BoolUtils.isNegation(lastFilter.getExpression()))) { methodName = "allMatch"; } } - if(nextReturnStatement.getParent() == statement.getParent() || - ExpressionUtils.isLiteral(nextReturnStatement.getReturnValue(), !foundResult)) { + if (nextReturnStatement.getParent() == statement.getParent() || + ExpressionUtils.isLiteral(nextReturnStatement.getReturnValue(), !foundResult)) { return new MatchMigration(shouldWarn, methodName); } } @@ -553,8 +585,8 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo } return new MatchMigration(shouldWarn, "anyMatch"); } - if(nextReturnStatement != null && ExpressionUtils.isSimpleExpression(nextReturnStatement.getReturnValue()) - && (!(tb.getVariable().getType() instanceof PsiPrimitiveType) || ExpressionUtils.isReferenceTo(value, tb.getVariable()))) { + if (nextReturnStatement != null && ExpressionUtils.isSimpleExpression(nextReturnStatement.getReturnValue()) + && (!(tb.getVariable().getType() instanceof PsiPrimitiveType) || ExpressionUtils.isReferenceTo(value, tb.getVariable()))) { return new FindFirstMigration(shouldWarn); } return null; @@ -564,7 +596,7 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo private TextRange getRange(boolean shouldWarn, PsiLoopStatement statement) { boolean wholeStatement = myIsOnTheFly && (!shouldWarn || InspectionProjectProfileManager.isInformationLevel(getShortName(), statement)); - if(statement instanceof PsiForeachStatement) { + if (statement instanceof PsiForeachStatement) { PsiJavaToken rParenth = ((PsiForeachStatement)statement).getRParenth(); if (wholeStatement && rParenth != null) { return new TextRange(statement.getTextOffset(), rParenth.getTextOffset() + 1); @@ -572,7 +604,8 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo PsiExpression iteratedValue = ((PsiForeachStatement)statement).getIteratedValue(); LOG.assertTrue(iteratedValue != null); return iteratedValue.getTextRange(); - } else if(statement instanceof PsiForStatement) { + } + else if (statement instanceof PsiForStatement) { PsiJavaToken rParenth = ((PsiForStatement)statement).getRParenth(); if (wholeStatement && rParenth != null) { return new TextRange(statement.getTextOffset(), rParenth.getTextOffset() + 1); @@ -580,14 +613,16 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo PsiStatement initialization = ((PsiForStatement)statement).getInitialization(); LOG.assertTrue(initialization != null); return initialization.getTextRange(); - } else if(statement instanceof PsiWhileStatement) { + } + else if (statement instanceof PsiWhileStatement) { PsiJavaToken rParenth = ((PsiWhileStatement)statement).getRParenth(); if (wholeStatement && rParenth != null) { return new TextRange(statement.getTextOffset(), rParenth.getTextOffset() + 1); } return statement.getFirstChild().getTextRange(); - } else { - throw new IllegalStateException("Unexpected statement type: "+statement); + } + else { + throw new IllegalStateException("Unexpected statement type: " + statement); } } } @@ -595,28 +630,28 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo @Nullable static PsiLocalVariable extractArray(TerminalBlock tb) { CountingLoopSource loop = tb.getLastOperation(CountingLoopSource.class); - if(loop == null || loop.myIncluding) return null; + if (loop == null || loop.myIncluding) return null; PsiAssignmentExpression assignment = tb.getSingleExpression(PsiAssignmentExpression.class); - if(assignment == null || !assignment.getOperationTokenType().equals(JavaTokenType.EQ)) return null; + if (assignment == null || !assignment.getOperationTokenType().equals(JavaTokenType.EQ)) return null; PsiArrayAccessExpression arrayAccess = tryCast(assignment.getLExpression(), PsiArrayAccessExpression.class); - if(arrayAccess == null) return null; - if(!ExpressionUtils.isReferenceTo(arrayAccess.getIndexExpression(), loop.getVariable())) return null; + if (arrayAccess == null) return null; + if (!ExpressionUtils.isReferenceTo(arrayAccess.getIndexExpression(), loop.getVariable())) return null; PsiReferenceExpression arrayReference = tryCast(arrayAccess.getArrayExpression(), PsiReferenceExpression.class); - if(arrayReference == null) return null; + if (arrayReference == null) return null; PsiLocalVariable arrayVariable = tryCast(arrayReference.resolve(), PsiLocalVariable.class); - if(arrayVariable == null || ControlFlowUtils.getInitializerUsageStatus(arrayVariable, tb.getMainLoop()) == UNKNOWN) return null; + if (arrayVariable == null || ControlFlowUtils.getInitializerUsageStatus(arrayVariable, tb.getMainLoop()) == UNKNOWN) return null; PsiNewExpression initializer = tryCast(arrayVariable.getInitializer(), PsiNewExpression.class); - if(initializer == null) return null; + if (initializer == null) return null; PsiArrayType arrayType = tryCast(initializer.getType(), PsiArrayType.class); - if(arrayType == null || !StreamApiUtil.isSupportedStreamElement(arrayType.getComponentType())) return null; + if (arrayType == null || !StreamApiUtil.isSupportedStreamElement(arrayType.getComponentType())) return null; PsiExpression dimension = ArrayUtil.getFirstElement(initializer.getArrayDimensions()); - if(dimension == null) return null; + if (dimension == null) return null; PsiExpression bound = loop.myBound; if (!PsiEquivalenceUtil.areElementsEquivalent(dimension, bound) && !ExpressionUtils.isReferenceTo(ExpressionUtils.getArrayFromLengthExpression(bound), arrayVariable)) { return null; } - if(VariableAccessUtils.variableIsUsed(arrayVariable, assignment.getRExpression())) return null; + if (VariableAccessUtils.variableIsUsed(arrayVariable, assignment.getRExpression())) return null; return arrayVariable; } @@ -712,8 +747,8 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo @Override PsiExpression makeIntermediateExpression(PsiElementFactory factory) { - return factory.createExpressionFromText(myFlatMapOp.getStreamExpression()+".anyMatch("+ - LambdaUtil.createLambda(myMatchVariable, myExpression)+")", myExpression); + return factory.createExpressionFromText(myFlatMapOp.getStreamExpression() + ".anyMatch(" + + LambdaUtil.createLambda(myMatchVariable, myExpression) + ")", myExpression); } @Override @@ -760,16 +795,18 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo PsiType inType = myVariable.getType(); PsiType outType = mySource.getVariable().getType(); String lambda = myVariable.getName() + " -> " + getStreamExpression(); - if(outType instanceof PsiPrimitiveType && !outType.equals(inType)) { - if(outType.equals(PsiType.INT)) { + if (outType instanceof PsiPrimitiveType && !outType.equals(inType)) { + if (outType.equals(PsiType.INT)) { operation = "flatMapToInt"; - } else if(outType.equals(PsiType.LONG)) { + } + else if (outType.equals(PsiType.LONG)) { operation = "flatMapToLong"; - } else if(outType.equals(PsiType.DOUBLE)) { + } + else if (outType.equals(PsiType.DOUBLE)) { operation = "flatMapToDouble"; } } - if(inType instanceof PsiPrimitiveType && !outType.equals(inType)) { + if (inType instanceof PsiPrimitiveType && !outType.equals(inType)) { return ".mapToObj(" + lambda + ")." + operation + "(" + CommonClassNames.JAVA_UTIL_FUNCTION_FUNCTION + ".identity())"; } return "." + operation + "(" + lambda + ")"; @@ -827,7 +864,7 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo @Override void cleanUp() { - if(myCounterVariable != null) { + if (myCounterVariable != null) { myCounterVariable.delete(); } } @@ -838,7 +875,7 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo } private String getLimitExpression() { - if(myDelta == 0) { + if (myDelta == 0) { return myExpression.getText(); } if (myExpression instanceof PsiLiteralExpression) { @@ -876,14 +913,14 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo @Contract("null -> null") static StreamSource tryCreate(PsiLoopStatement statement) { - if(statement instanceof PsiForStatement) { + if (statement instanceof PsiForStatement) { return CountingLoopSource.from((PsiForStatement)statement); } - if(statement instanceof PsiForeachStatement) { + if (statement instanceof PsiForeachStatement) { ArrayStream source = ArrayStream.from((PsiForeachStatement)statement); return source == null ? CollectionStream.from((PsiForeachStatement)statement) : source; } - if(statement instanceof PsiWhileStatement) { + if (statement instanceof PsiWhileStatement) { return BufferedReaderLines.from((PsiWhileStatement)statement); } return null; @@ -897,7 +934,7 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo @Override String createReplacement() { - return myExpression.getText()+".lines()"; + return myExpression.getText() + ".lines()"; } @Override @@ -914,30 +951,30 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo public static BufferedReaderLines from(PsiWhileStatement whileLoop) { // while ((line = br.readLine()) != null) PsiBinaryExpression binOp = tryCast(PsiUtil.skipParenthesizedExprDown(whileLoop.getCondition()), PsiBinaryExpression.class); - if(binOp == null) return null; - if(!JavaTokenType.NE.equals(binOp.getOperationTokenType())) return null; + if (binOp == null) return null; + if (!JavaTokenType.NE.equals(binOp.getOperationTokenType())) return null; PsiExpression operand = ExpressionUtils.getValueComparedWithNull(binOp); - if(operand == null) return null; + if (operand == null) return null; PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(PsiUtil.skipParenthesizedExprDown(operand)); - if(assignment == null) return null; + if (assignment == null) return null; PsiReferenceExpression lValue = tryCast(assignment.getLExpression(), PsiReferenceExpression.class); - if(lValue == null) return null; + if (lValue == null) return null; PsiLocalVariable var = tryCast(lValue.resolve(), PsiLocalVariable.class); - if(var == null) return null; - if(!ReferencesSearch.search(var).forEach(ref -> { + if (var == null) return null; + if (!ReferencesSearch.search(var).forEach(ref -> { return PsiTreeUtil.isAncestor(whileLoop, ref.getElement(), true); })) { return null; } PsiMethodCallExpression call = tryCast(PsiUtil.skipParenthesizedExprDown(assignment.getRExpression()), PsiMethodCallExpression.class); if (call == null || call.getArgumentList().getExpressions().length != 0) return null; - if(!"readLine".equals(call.getMethodExpression().getReferenceName())) return null; + if (!"readLine".equals(call.getMethodExpression().getReferenceName())) return null; PsiExpression readerExpression = call.getMethodExpression().getQualifierExpression(); - if(readerExpression == null) return null; + if (readerExpression == null) return null; PsiMethod method = call.resolveMethod(); - if(method == null) return null; + if (method == null) return null; PsiClass aClass = method.getContainingClass(); - if(aClass == null || !"java.io.BufferedReader".equals(aClass.getQualifiedName())) return null; + if (aClass == null || !"java.io.BufferedReader".equals(aClass.getQualifiedName())) return null; return new BufferedReaderLines(whileLoop, var, readerExpression); } } @@ -1053,7 +1090,7 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo public String createReplacement() { String className = myVariable.getType().equals(PsiType.LONG) ? "java.util.stream.LongStream" : "java.util.stream.IntStream"; String methodName = myIncluding ? "rangeClosed" : "range"; - return className+"."+methodName+"("+myExpression.getText()+", "+myBound.getText()+")"; + return className + "." + methodName + "(" + myExpression.getText() + ", " + myBound.getText() + ")"; } CountingLoopSource withBound(PsiExpression bound) { @@ -1062,9 +1099,9 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo @Override boolean isWriteAllowed(PsiVariable variable, PsiExpression reference) { - if(variable == myVariable) { + if (variable == myVariable) { PsiForStatement forStatement = PsiTreeUtil.getParentOfType(variable, PsiForStatement.class); - if(forStatement != null) { + if (forStatement != null) { return PsiTreeUtil.isAncestor(forStatement.getUpdate(), reference, false); } } diff --git a/java/java-impl/src/com/intellij/codeInspection/streamMigration/SumMigration.java b/java/java-impl/src/com/intellij/codeInspection/streamMigration/SumMigration.java index 199804433683..c33821ad90ec 100644 --- a/java/java-impl/src/com/intellij/codeInspection/streamMigration/SumMigration.java +++ b/java/java-impl/src/com/intellij/codeInspection/streamMigration/SumMigration.java @@ -22,6 +22,8 @@ import com.intellij.psi.util.TypeConversionUtil; import com.siyeh.ig.psiutils.ParenthesesUtils; import org.jetbrains.annotations.NotNull; +import static com.intellij.codeInspection.streamMigration.OperationReductionMigration.SUM_OPERATION; + /** * @author Tagir Valeev */ @@ -33,7 +35,7 @@ class SumMigration extends BaseStreamApiMigration { PsiElement migrate(@NotNull Project project, @NotNull PsiStatement body, @NotNull TerminalBlock tb) { PsiAssignmentExpression assignment = tb.getSingleExpression(PsiAssignmentExpression.class); if (assignment == null) return null; - PsiVariable var = StreamApiMigrationInspection.extractAccumulator(assignment); + PsiVariable var = StreamApiMigrationInspection.extractSumAccumulator(assignment); if (var == null) return null; PsiExpression addend = StreamApiMigrationInspection.extractAddend(assignment); @@ -49,6 +51,6 @@ class SumMigration extends BaseStreamApiMigration { "(" + type.getCanonicalText() + ")" + ParenthesesUtils.getText(addend, ParenthesesUtils.MULTIPLICATIVE_PRECEDENCE), addend); } String stream = tb.add(new MapOp(addend, tb.getVariable(), type)).generate()+".sum()"; - return replaceWithNumericAddition(tb.getMainLoop(), var, stream, type); + return replaceWithOperation(tb.getMainLoop(), var, stream, type, SUM_OPERATION); } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnd.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnd.java new file mode 100644 index 000000000000..f21fbc5ca84a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnd.java @@ -0,0 +1,11 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testBitwiseAnd() { + int[] arr = new int[]{1, 2, 3, 4}; + int acc = 12443; + acc &= Arrays.stream(arr).reduce(-1, (a, b) -> a & b); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAndHighestBit.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAndHighestBit.java new file mode 100644 index 000000000000..ba3dc1393d65 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAndHighestBit.java @@ -0,0 +1,10 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testBitwiseAndHighestBit() { + int[] arr = {0x80000000, 0xffffffff}; + int acc = Arrays.stream(arr).reduce(-1, (a, b) -> a & b); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAndReplacingHexInitializer.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAndReplacingHexInitializer.java new file mode 100644 index 000000000000..5da6f3db4c83 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAndReplacingHexInitializer.java @@ -0,0 +1,10 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testBitwiseAndReplacingInitializer() { + int[] arr = new int[]{1, 2, 3, 4}; + int acc = Arrays.stream(arr).reduce(0xFFFFFFFF, (a, b) -> a & b); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAndReplacingInitializer.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAndReplacingInitializer.java new file mode 100644 index 000000000000..f374b022224e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAndReplacingInitializer.java @@ -0,0 +1,10 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testBitwiseAndReplacingInitializer() { + int[] arr = new int[]{1, 2, 3, 4}; + int acc = Arrays.stream(arr).reduce(-1, (a, b) -> a & b); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAndReplacingLongInitializer.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAndReplacingLongInitializer.java new file mode 100644 index 000000000000..16799556d670 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAndReplacingLongInitializer.java @@ -0,0 +1,10 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testBitwiseAndReplacingLongInitializer() { + long[] arr = new long[]{1, 2, 3, 4}; + long acc = Arrays.stream(arr).reduce(-1l, (a, b) -> a & b); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterConjunction.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterConjunction.java new file mode 100644 index 000000000000..748ce0c3b4ad --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterConjunction.java @@ -0,0 +1,10 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testConjunction() { + List booleans = new ArrayList<>(); + boolean acc = booleans.stream().reduce(true, (a, b) -> a && b); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterConjunctionBoxed.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterConjunctionBoxed.java new file mode 100644 index 000000000000..a0e9a835f02a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterConjunctionBoxed.java @@ -0,0 +1,10 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testConjunctionBoxed() { + List booleans = new ArrayList<>(); + Boolean acc = booleans.stream().reduce(true, (a, b) -> a && b); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterDisjunction.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterDisjunction.java new file mode 100644 index 000000000000..2c1f430ea0b1 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterDisjunction.java @@ -0,0 +1,10 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testDisjunction() { + List booleans = new ArrayList<>(); + boolean acc = booleans.stream().reduce(false, (a, b) -> a || b); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterMultiply.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterMultiply.java new file mode 100644 index 000000000000..22397296435b --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterMultiply.java @@ -0,0 +1,10 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testMultiply() { + int[] arr = new int[]{1, 2, 3, 4}; + int acc = Arrays.stream(arr).reduce(1, (a, b) -> a * b); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterMultiplyComplexInitializer.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterMultiplyComplexInitializer.java new file mode 100644 index 000000000000..202ce6b3dabd --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterMultiplyComplexInitializer.java @@ -0,0 +1,12 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testMultiplyWithComplexInitializer() { + int[] arr = new int[]{1, 2, 3, 4}; + int x = 10; + int acc = 12 + x; + acc *= Arrays.stream(arr).reduce(1, (a, b) -> a * b); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterMultiplyConflictingVariable.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterMultiplyConflictingVariable.java new file mode 100644 index 000000000000..06b7c488c40c --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterMultiplyConflictingVariable.java @@ -0,0 +1,13 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testMultiplyWithConflictingNamesInScope() { + int a = 1; + int b = 2; + int b1 = 3; + int[] arr = new int[]{1, 2, 3, 4}; + int acc = Arrays.stream(arr).reduce(1, (a1, b2) -> a1 * b2); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterMultiplyNarrowingTypeInitializer.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterMultiplyNarrowingTypeInitializer.java new file mode 100644 index 000000000000..058d76c99898 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterMultiplyNarrowingTypeInitializer.java @@ -0,0 +1,11 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testMultiplyWithNarrowingTypeInitializer() { + int[] arr = new int[]{1, 2, 3, 4}; + short acc = (byte) 12; + acc *= Arrays.stream(arr).map(i -> (short) i).reduce(1, (a, b) -> a * b); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterMultiplyNotOneInitializer.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterMultiplyNotOneInitializer.java new file mode 100644 index 000000000000..5e5c656daa94 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterMultiplyNotOneInitializer.java @@ -0,0 +1,11 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testMultiplyWithNotOneInitializerConstant() { + int[] arr = new int[]{1, 2, 3, 4}; + int acc = 12; + acc *= Arrays.stream(arr).reduce(1, (a, b) -> a * b); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterMultiplyWithMapping.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterMultiplyWithMapping.java new file mode 100644 index 000000000000..a403d0cbc79e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterMultiplyWithMapping.java @@ -0,0 +1,10 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testMultiplyWithMapping() { + int[] arr = new int[]{1, 2, 3, 4}; + int acc = Arrays.stream(arr).map(i -> i + 2).reduce(1, (a, b) -> a * b); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterOr.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterOr.java new file mode 100644 index 000000000000..a2bca0bb3991 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterOr.java @@ -0,0 +1,11 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testBitwiseOr() { + int[] arr = new int[]{1, 2, 3, 4}; + int acc = 12443; + acc |= Arrays.stream(arr).reduce(0, (a, b) -> a | b); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterXor.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterXor.java new file mode 100644 index 000000000000..c776eb1da7fd --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterXor.java @@ -0,0 +1,11 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testBitwiseXor() { + int[] arr = new int[]{1, 2, 3, 4}; + int acc = 12443; + acc ^= Arrays.stream(arr).reduce(0, (a, b) -> a ^ b); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterXorLong.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterXorLong.java new file mode 100644 index 000000000000..b7b5f7e19f2c --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterXorLong.java @@ -0,0 +1,11 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testBitwiseXorLong() { + int[] arr = new int[]{1, 2, 3, 4}; + long acc = 12443; + acc ^= Arrays.stream(arr).asLongStream().reduce(0, (a, b) -> a ^ b); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnd.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnd.java new file mode 100644 index 000000000000..754d480397f0 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnd.java @@ -0,0 +1,13 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testBitwiseAnd() { + int[] arr = new int[]{1, 2, 3, 4}; + int acc = 12443; + for (int i: arr) { + acc &= i; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAndHighestBit.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAndHighestBit.java new file mode 100644 index 000000000000..010e6a6f9944 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAndHighestBit.java @@ -0,0 +1,13 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testBitwiseAndHighestBit() { + int[] arr = {0x80000000, 0xffffffff}; + int acc = -1; + for (int i: arr) { + acc &= i; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAndReplacingHexInitializer.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAndReplacingHexInitializer.java new file mode 100644 index 000000000000..d7be798d0c39 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAndReplacingHexInitializer.java @@ -0,0 +1,13 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testBitwiseAndReplacingInitializer() { + int[] arr = new int[]{1, 2, 3, 4}; + int acc = 0xFFFFFFFF; + for (int i: arr) { + acc &= i; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAndReplacingInitializer.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAndReplacingInitializer.java new file mode 100644 index 000000000000..809ad3ada7ae --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAndReplacingInitializer.java @@ -0,0 +1,13 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testBitwiseAndReplacingInitializer() { + int[] arr = new int[]{1, 2, 3, 4}; + int acc = -1; + for (int i: arr) { + acc &= i; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAndReplacingLongInitializer.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAndReplacingLongInitializer.java new file mode 100644 index 000000000000..916175fab8ec --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAndReplacingLongInitializer.java @@ -0,0 +1,13 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testBitwiseAndReplacingLongInitializer() { + long[] arr = new long[]{1, 2, 3, 4}; + long acc = -1l; + for (long i: arr) { + acc &= i; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeConjunction.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeConjunction.java new file mode 100644 index 000000000000..e0f24ee966fa --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeConjunction.java @@ -0,0 +1,13 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testConjunction() { + List booleans = new ArrayList<>(); + boolean acc = true; + for (Boolean bool : booleans) { + acc &= bool; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeConjunctionBoxed.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeConjunctionBoxed.java new file mode 100644 index 000000000000..895b92fe15f5 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeConjunctionBoxed.java @@ -0,0 +1,13 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testConjunctionBoxed() { + List booleans = new ArrayList<>(); + Boolean acc = true; + for (Boolean bool : booleans) { + acc &= bool; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeDisjunction.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeDisjunction.java new file mode 100644 index 000000000000..bea52173efa3 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeDisjunction.java @@ -0,0 +1,13 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testDisjunction() { + List booleans = new ArrayList<>(); + boolean acc = false; + for (Boolean bool : booleans) { + acc |= bool; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeMultiply.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeMultiply.java new file mode 100644 index 000000000000..6a08fd1a4851 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeMultiply.java @@ -0,0 +1,13 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testMultiply() { + int[] arr = new int[]{1, 2, 3, 4}; + int acc = 1; + for (int i : arr) { + acc *= i; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeMultiplyComplexInitializer.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeMultiplyComplexInitializer.java new file mode 100644 index 000000000000..c05000b6a714 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeMultiplyComplexInitializer.java @@ -0,0 +1,14 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testMultiplyWithComplexInitializer() { + int[] arr = new int[]{1, 2, 3, 4}; + int x = 10; + int acc = 12 + x; + for (int i : arr) { + acc *= i; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeMultiplyConflictingVariable.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeMultiplyConflictingVariable.java new file mode 100644 index 000000000000..674d8d57e6e9 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeMultiplyConflictingVariable.java @@ -0,0 +1,16 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testMultiplyWithConflictingNamesInScope() { + int a = 1; + int b = 2; + int b1 = 3; + int[] arr = new int[]{1, 2, 3, 4}; + int acc = 1; + for (int i : arr) { + acc *= i; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeMultiplyNarrowingTypeInitializer.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeMultiplyNarrowingTypeInitializer.java new file mode 100644 index 000000000000..01d5439e8b07 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeMultiplyNarrowingTypeInitializer.java @@ -0,0 +1,13 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testMultiplyWithNarrowingTypeInitializer() { + int[] arr = new int[]{1, 2, 3, 4}; + short acc = (byte) 12; + for (int i : arr) { + acc *= i; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeMultiplyNotOneInitializer.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeMultiplyNotOneInitializer.java new file mode 100644 index 000000000000..7341f6820ba3 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeMultiplyNotOneInitializer.java @@ -0,0 +1,13 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testMultiplyWithNotOneInitializerConstant() { + int[] arr = new int[]{1, 2, 3, 4}; + int acc = 12; + for (int i : arr) { + acc *= i; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeMultiplyWithMapping.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeMultiplyWithMapping.java new file mode 100644 index 000000000000..bcc147d34844 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeMultiplyWithMapping.java @@ -0,0 +1,13 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testMultiplyWithMapping() { + int[] arr = new int[]{1, 2, 3, 4}; + int acc = 1; + for (int i : arr) { + acc *= i + 2; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeOr.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeOr.java new file mode 100644 index 000000000000..67c45f8edf5b --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeOr.java @@ -0,0 +1,13 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testBitwiseOr() { + int[] arr = new int[]{1, 2, 3, 4}; + int acc = 12443; + for (int i: arr) { + acc |= i; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeXor.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeXor.java new file mode 100644 index 000000000000..1945bd753bb7 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeXor.java @@ -0,0 +1,13 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testBitwiseXor() { + int[] arr = new int[]{1, 2, 3, 4}; + int acc = 12443; + for (int i: arr) { + acc ^= i; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeXorLong.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeXorLong.java new file mode 100644 index 000000000000..e39cf515dd8d --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeXorLong.java @@ -0,0 +1,13 @@ +// "Replace with reduce()" "true" + +import java.util.*; + +public class Main { + public void testBitwiseXorLong() { + int[] arr = new int[]{1, 2, 3, 4}; + long acc = 12443; + for (int i: arr) { + acc ^= i; + } + } +} \ No newline at end of file