diff --git a/java/java-impl/src/com/intellij/codeInspection/streamMigration/MigrateToStreamFix.java b/java/java-impl/src/com/intellij/codeInspection/streamMigration/MigrateToStreamFix.java index 2a34ec766033..894233d15073 100644 --- a/java/java-impl/src/com/intellij/codeInspection/streamMigration/MigrateToStreamFix.java +++ b/java/java-impl/src/com/intellij/codeInspection/streamMigration/MigrateToStreamFix.java @@ -25,6 +25,7 @@ import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.controlFlow.*; +import com.intellij.psi.impl.PsiDiamondTypeUtil; import com.intellij.psi.util.PsiTreeUtil; import com.siyeh.ig.psiutils.ExpressionUtils; import one.util.streamex.StreamEx; @@ -109,9 +110,39 @@ abstract class MigrateToStreamFix implements LocalQuickFix { static void simplifyAndFormat(@NotNull Project project, PsiElement result) { if (result == null) return; LambdaCanBeMethodReferenceInspection.replaceAllLambdasWithMethodReferences(result); + removeRedundantTypeArguments(project, result); CodeStyleManager.getInstance(project).reformat(JavaCodeStyleManager.getInstance(project).shortenClassReferences(result)); } + private static void removeRedundantTypeArguments(@NotNull Project project, PsiElement result) { + PsiElement[] typedCalls = PsiTreeUtil.collectElements(result, e -> { + if (!(e instanceof PsiMethodCallExpression)) return false; + PsiMethodCallExpression call = (PsiMethodCallExpression)e; + if (call.getTypeArguments().length == 0) return false; + PsiMethod method = call.resolveMethod(); + if (method == null) return false; + PsiClass aClass = method.getContainingClass(); + if (aClass == null) return false; + String className = aClass.getQualifiedName(); + // We remove only those which related to Stream API calls trying to preserve ones which were originally in code + return className != null && className.startsWith("java.util.stream."); + }); + for(PsiElement typedCall : typedCalls) { + PsiMethodCallExpression call = (PsiMethodCallExpression)typedCall; + PsiType[] arguments = call.getTypeArguments(); + PsiMethod method = call.resolveMethod(); + if(method != null) { + PsiTypeParameter[] parameters = method.getTypeParameters(); + if(arguments.length == parameters.length && + PsiDiamondTypeUtil.areTypeArgumentsRedundant(arguments, call, false, method, parameters)) { + PsiMethodCallExpression expr = + (PsiMethodCallExpression)JavaPsiFacade.getInstance(project).getElementFactory().createExpressionFromText("foo()", null); + call.getTypeArgumentList().replace(expr.getTypeArgumentList()); + } + } + } + } + static void restoreComments(PsiLoopStatement loopStatement, PsiStatement body) { final PsiElement parent = loopStatement.getParent(); for (PsiElement comment : PsiTreeUtil.findChildrenOfType(body, PsiComment.class)) { 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 a837bc5ccb91..dce703e08a4a 100644 --- a/java/java-impl/src/com/intellij/codeInspection/streamMigration/StreamApiMigrationInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/streamMigration/StreamApiMigrationInspection.java @@ -572,7 +572,9 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo PsiBreakStatement.class, PsiReturnStatement.class, PsiThrowStatement.class); int startOffset = controlFlow.getStartOffset(body); int endOffset = controlFlow.getEndOffset(body); + PsiElement surrounder = PsiTreeUtil.getParentOfType(statement, PsiLambdaExpression.class, PsiClass.class); final List nonFinalVariables = StreamEx.of(ControlFlowUtil.getUsedVariables(controlFlow, startOffset, endOffset)) + .remove(variable -> PsiTreeUtil.getParentOfType(variable, PsiLambdaExpression.class, PsiClass.class) != surrounder) .remove(variable -> isVariableSuitableForStream(variable, statement, tb)).toList(); if (exitPoints.isEmpty()) { @@ -978,6 +980,9 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo operationName = "mapToObj"; } PsiExpression expression = myType == null ? myExpression : RefactoringUtil.convertInitializerToNormalExpression(myExpression, myType); + if(myType != null && !(myType instanceof PsiPrimitiveType)) { + operationName = "<"+myType.getCanonicalText()+">"+operationName; + } return "." + operationName + "(" + LambdaUtil.createLambda(myVariable, expression) + ")"; } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterCastExpected.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterCastExpected.java index cbf887b56104..06440af2b9fb 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterCastExpected.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterCastExpected.java @@ -5,7 +5,7 @@ import java.util.stream.Collectors; class Test { public static List> fromString(final T src, Function> extractor) { - final List> result = extractor.apply(src).stream().map((Function>) TokenFilter::new).collect(Collectors.toList()); + final List> result = extractor.apply(src).stream().>map(TokenFilter::new).collect(Collectors.toList()); return result; } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterCollectAnonymous.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterCollectAnonymous.java new file mode 100644 index 000000000000..2d76ac58d30f --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterCollectAnonymous.java @@ -0,0 +1,18 @@ +// "Replace with collect" "true" +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class Main { + public List test(List list) { + List result = list.stream().map(s -> new Runnable() { + @Override + public void run() { + String str = s; + if (str.isEmpty()) str = "none"; + System.out.println(str); + } + }).collect(Collectors.toList()); + return result; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterCollectLambda.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterCollectLambda.java new file mode 100644 index 000000000000..6b3abfb6273c --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterCollectLambda.java @@ -0,0 +1,15 @@ +// "Replace with collect" "true" +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class Main { + public List test(List list) { + List result = list.stream().map(s -> () -> { + String str = s; + if (str.isEmpty()) str = "none"; + System.out.println(str); + }).collect(Collectors.toList()); + return result; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeCollectAnonymous.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeCollectAnonymous.java new file mode 100644 index 000000000000..952611720164 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeCollectAnonymous.java @@ -0,0 +1,21 @@ +// "Replace with collect" "true" +import java.util.ArrayList; +import java.util.List; + +public class Main { + public List test(List list) { + List result = new ArrayList<>(); + for(String s : list) { + Runnable r = new Runnable() { + @Override + public void run() { + String str = s; + if (str.isEmpty()) str = "none"; + System.out.println(str); + } + }; + result.add(r); + } + return result; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeCollectLambda.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeCollectLambda.java new file mode 100644 index 000000000000..5eefccff5c64 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeCollectLambda.java @@ -0,0 +1,18 @@ +// "Replace with collect" "true" +import java.util.ArrayList; +import java.util.List; + +public class Main { + public List test(List list) { + List result = new ArrayList<>(); + for(String s : list) { + Runnable r = () -> { + String str = s; + if (str.isEmpty()) str = "none"; + System.out.println(str); + }; + result.add(r); + } + return result; + } +} \ No newline at end of file