diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithCollectFix.java b/java/java-analysis-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithCollectFix.java index e092bdb21f79..523b8bb808c2 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithCollectFix.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/streamMigration/ReplaceWithCollectFix.java @@ -97,6 +97,36 @@ class ReplaceWithCollectFix extends MigrateToStreamFix { if(status != InitializerUsageStatus.UNKNOWN) { PsiExpression initializer = variable.getInitializer(); LOG.assertTrue(initializer != null); + PsiMethodCallExpression toArrayExpression = + StreamApiMigrationInspection.extractToArrayExpression(foreachStatement, methodCallExpression); + if(toArrayExpression != null) { + PsiType type = initializer.getType(); + if(type instanceof PsiClassType) { + String replacement = StreamApiMigrationInspection.COLLECTION_TO_ARRAY.get(((PsiClassType)type).rawType().getCanonicalText()); + if(replacement != null) { + builder.append(".").append(replacement); + PsiExpression[] args = toArrayExpression.getArgumentList().getExpressions(); + if(args.length == 0) { + builder.append("()"); + } else { + if(args.length != 1 || !(args[0] instanceof PsiNewExpression)) return; + PsiNewExpression newArray = (PsiNewExpression)args[0]; + PsiType arrayType = newArray.getType(); + if(arrayType == null) return; + String name = arrayType.getCanonicalText(); + builder.append('(').append(name).append("::new)"); + } + PsiElement result = + toArrayExpression.replace(elementFactory.createExpressionFromText(builder.toString(), toArrayExpression)); + removeLoop(foreachStatement); + if(status != InitializerUsageStatus.AT_WANTED_PLACE) { + variable.delete(); + } + simplifyAndFormat(project, result); + return; + } + } + } String callText = builder.append(".collect(java.util.stream.Collectors.") .append(createInitializerReplacementText(qualifierExpression.getType(), initializer)) .append(")").toString(); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/streamMigration/StreamApiMigrationInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/streamMigration/StreamApiMigrationInspection.java index 0a5f773c4065..50a64b359ceb 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/streamMigration/StreamApiMigrationInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/streamMigration/StreamApiMigrationInspection.java @@ -39,7 +39,9 @@ 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.EquivalenceChecker; import com.siyeh.ig.psiutils.ExpressionUtils; +import one.util.streamex.EntryStream; import one.util.streamex.StreamEx; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Nls; @@ -57,6 +59,14 @@ import static com.intellij.codeInspection.streamMigration.StreamApiMigrationInsp public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTool { private static final Logger LOG = Logger.getInstance("#" + StreamApiMigrationInspection.class.getName()); + static final Map COLLECTION_TO_ARRAY = EntryStream.of( + CommonClassNames.JAVA_UTIL_ARRAY_LIST, "toArray", + "java.util.LinkedList", "toArray", + CommonClassNames.JAVA_UTIL_HASH_SET, "distinct().toArray", + "java.util.LinkedHashSet", "distinct().toArray", + "java.util.TreeSet", "distinct().sorted().toArray" + ).toMap(); + public boolean REPLACE_TRIVIAL_FOREACH; public boolean SUGGEST_FOREACH; @@ -433,7 +443,8 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo int start = controlFlow.getEndOffset(declaration); int stop = controlFlow.getStartOffset(nextStatement); if(ControlFlowUtil.isVariableReferencedBetween(controlFlow, start, stop, var)) return UNKNOWN; - return ControlFlowUtil.isValueUsedWithoutVisitingStop(controlFlow, start, stop, var) ? AT_WANTED_PLACE : AT_WANTED_PLACE_ONLY; + if (!ControlFlowUtil.isValueUsedWithoutVisitingStop(controlFlow, start, stop, var)) return AT_WANTED_PLACE_ONLY; + return var.hasModifierProperty(PsiModifier.FINAL) ? UNKNOWN : AT_WANTED_PLACE; } static boolean isDeclarationJustBefore(PsiVariable var, PsiStatement nextStatement) { @@ -522,7 +533,10 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo } else { PsiMethodCallExpression methodCallExpression = tb.getSingleMethodCall(); if(canCollect(statement, methodCallExpression)) { - methodName = "collect"; + if(extractToArrayExpression(statement, methodCallExpression) != null) + methodName = "toArray"; + else + methodName = "collect"; } else { if (!SUGGEST_FOREACH) return; methodName = "forEach"; @@ -658,6 +672,73 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo } } + @Nullable + static PsiMethodCallExpression extractToArrayExpression(PsiForeachStatement statement, PsiMethodCallExpression expression) { + // return collection.toArray() or collection.toArray(new Type[0]) or collection.toArray(new Type[collection.size()]); + PsiElement nextElement = PsiTreeUtil.skipSiblingsForward(statement, PsiComment.class, PsiWhiteSpace.class); + PsiExpression toArrayCandidate; + if (nextElement instanceof PsiReturnStatement) { + toArrayCandidate = ((PsiReturnStatement)nextElement).getReturnValue(); + } + else { + PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(nextElement); + if (assignment != null) { + toArrayCandidate = assignment.getRExpression(); + } + else if (nextElement instanceof PsiDeclarationStatement) { + PsiElement[] elements = ((PsiDeclarationStatement)nextElement).getDeclaredElements(); + if (elements.length == 1 && elements[0] instanceof PsiLocalVariable) { + toArrayCandidate = ((PsiLocalVariable)elements[0]).getInitializer(); + } + else { + return null; + } + } + else { + return null; + } + } + if (!(toArrayCandidate instanceof PsiMethodCallExpression)) return null; + PsiMethodCallExpression call = (PsiMethodCallExpression)toArrayCandidate; + PsiReferenceExpression methodExpression = call.getMethodExpression(); + if (!"toArray".equals(methodExpression.getReferenceName())) return null; + PsiExpression qualifierExpression = methodExpression.getQualifierExpression(); + if (!(qualifierExpression instanceof PsiReferenceExpression)) return null; + PsiLocalVariable collectionVariable = extractCollectionVariable(expression.getMethodExpression().getQualifierExpression()); + if (collectionVariable == null || ((PsiReferenceExpression)qualifierExpression).resolve() != collectionVariable) return null; + PsiExpression initializer = collectionVariable.getInitializer(); + if (initializer == null) return null; + PsiType type = initializer.getType(); + if (!(type instanceof PsiClassType) || !COLLECTION_TO_ARRAY.containsKey(((PsiClassType)type).rawType().getCanonicalText())) { + return null; + } + + if (!(nextElement instanceof PsiReturnStatement) && !ReferencesSearch.search(collectionVariable, collectionVariable.getUseScope()) + .forEach(ref -> + ref.getElement() == collectionVariable || PsiTreeUtil.isAncestor(statement, ref.getElement(), false) || + PsiTreeUtil.isAncestor(toArrayCandidate, ref.getElement(), false) + )) { + return null; + } + + PsiExpression[] args = call.getArgumentList().getExpressions(); + if (args.length == 0) return call; + if (args.length != 1 || !(args[0] instanceof PsiNewExpression)) return null; + PsiNewExpression newArray = (PsiNewExpression)args[0]; + PsiExpression[] dimensions = newArray.getArrayDimensions(); + if (dimensions.length != 1) return null; + if (ExpressionUtils.isLiteral(dimensions[0], 0)) return call; + if (!(dimensions[0] instanceof PsiMethodCallExpression)) return null; + PsiMethodCallExpression maybeSizeCall = (PsiMethodCallExpression)dimensions[0]; + if (maybeSizeCall.getArgumentList().getExpressions().length != 0) return null; + PsiReferenceExpression maybeSizeExpression = maybeSizeCall.getMethodExpression(); + if (!"size".equals(maybeSizeExpression.getReferenceName()) || !EquivalenceChecker.getCanonicalPsiEquivalence() + .expressionsAreEquivalent(qualifierExpression, maybeSizeExpression.getQualifierExpression())) { + return null; + } + return call; + } + /** * Intermediate stream operation representation */ @@ -735,7 +816,8 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo } else if(myVariable.getType() instanceof PsiPrimitiveType) { operationName = "mapToObj"; } - return "." + operationName + "(" + LambdaUtil.createLambda(myVariable, myExpression) + ")"; + PsiExpression expression = myType == null ? myExpression : ExpressionUtils.convertInitializerToNormalExpression(myExpression, myType); + return "." + operationName + "(" + LambdaUtil.createLambda(myVariable, expression) + ")"; } } diff --git a/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java b/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java index 0aa1a410140f..74a1a3881f56 100644 --- a/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java +++ b/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -38,7 +38,6 @@ import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.codeStyle.VariableKind; -import com.intellij.psi.controlFlow.ControlFlowUtil; import com.intellij.psi.javadoc.PsiDocComment; import com.intellij.psi.javadoc.PsiDocTag; import com.intellij.psi.javadoc.PsiDocTagValue; @@ -53,7 +52,9 @@ import com.intellij.refactoring.introduceVariable.IntroduceVariableBase; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.HashMap; import com.intellij.util.containers.HashSet; +import com.siyeh.ig.psiutils.ExpressionUtils; import gnu.trove.THashMap; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -129,7 +130,7 @@ public class RefactoringUtil { } /** - * @see com.intellij.psi.codeStyle.CodeStyleManager#suggestUniqueVariableName(String, com.intellij.psi.PsiElement, boolean) + * @see JavaCodeStyleManager#suggestUniqueVariableName(String, PsiElement, boolean) * Cannot use method from code style manager: a collision with fieldToReplace is not a collision */ public static String suggestUniqueVariableName(String baseName, PsiElement place, PsiField fieldToReplace) { @@ -197,11 +198,7 @@ public class RefactoringUtil { final PsiImportList importList = ((PsiJavaFile)element.getContainingFile()).getImportList(); if (importList != null) { final PsiImportStaticStatement[] importStaticStatements = importList.getImportStaticStatements(); - for(PsiImportStaticStatement stmt: importStaticStatements) { - if (stmt.isOnDemand() && stmt.resolveTargetClass() == aClass) { - return true; - } - } + return Arrays.stream(importStaticStatements).anyMatch(stmt -> stmt.isOnDemand() && stmt.resolveTargetClass() == aClass); } } return false; @@ -390,7 +387,7 @@ public class RefactoringUtil { if (type != null && !isFunctionalType && isDenotable) { return type; } - ExpectedTypeInfo[] expectedTypes = ExpectedTypesProvider.getInstance(expr.getProject()).getExpectedTypes(expr, false); + ExpectedTypeInfo[] expectedTypes = ExpectedTypesProvider.getExpectedTypes(expr, false); if (expectedTypes.length == 1 || (isFunctionalType || !isDenotable)&& expectedTypes.length > 0 ) { type = expectedTypes[0].getType(); if (!type.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) return type; @@ -413,7 +410,7 @@ public class RefactoringUtil { private static PsiType getTypeByExpression(PsiExpression expr, final PsiElementFactory factory) { PsiType type = RefactoringChangeUtil.getTypeByExpression(expr); if (PsiType.NULL.equals(type)) { - ExpectedTypeInfo[] infos = ExpectedTypesProvider.getInstance(expr.getProject()).getExpectedTypes(expr, false); + ExpectedTypeInfo[] infos = ExpectedTypesProvider.getExpectedTypes(expr, false); if (infos.length == 1) { type = infos[0].getType(); } @@ -663,7 +660,8 @@ public class RefactoringUtil { return result; } - public static PsiExpression convertInitializerToNormalExpression(PsiExpression expression, PsiType forcedReturnType) + @Contract("null, _ -> null") + public static PsiExpression convertInitializerToNormalExpression(@Nullable PsiExpression expression, @Nullable PsiType forcedReturnType) throws IncorrectOperationException { if (expression instanceof PsiArrayInitializerExpression && (forcedReturnType == null || forcedReturnType instanceof PsiArrayType)) { return createNewExpressionFromArrayInitializer((PsiArrayInitializerExpression)expression, forcedReturnType); @@ -671,8 +669,8 @@ public class RefactoringUtil { return expression; } - public static PsiExpression createNewExpressionFromArrayInitializer(PsiArrayInitializerExpression initializer, PsiType forcedType) - throws IncorrectOperationException { + public static PsiExpression createNewExpressionFromArrayInitializer(PsiArrayInitializerExpression initializer, + @Nullable PsiType forcedType) throws IncorrectOperationException { PsiType initializerType = null; if (initializer != null) { if (forcedType != null) { @@ -686,14 +684,7 @@ public class RefactoringUtil { return initializer; } LOG.assertTrue(initializerType instanceof PsiArrayType); - PsiElementFactory factory = JavaPsiFacade.getInstance(initializer.getProject()).getElementFactory(); - PsiNewExpression result = - (PsiNewExpression)factory.createExpressionFromText("new " + initializerType.getPresentableText() + "{}", null); - result = (PsiNewExpression)CodeStyleManager.getInstance(initializer.getProject()).reformat(result); - PsiArrayInitializerExpression arrayInitializer = result.getArrayInitializer(); - LOG.assertTrue(arrayInitializer != null); - arrayInitializer.replace(initializer); - return result; + return ExpressionUtils.createNewExpressionFromArrayInitializer(initializer, (PsiArrayType)initializerType); } public static void makeMethodAbstract(@NotNull PsiClass targetClass, @NotNull PsiMethod method) throws IncorrectOperationException { @@ -1100,13 +1091,13 @@ public class RefactoringUtil { } public static void fixJavadocsForParams(PsiMethod method, Set newParameters) throws IncorrectOperationException { - fixJavadocsForParams(method, newParameters, Conditions.>alwaysFalse()); + fixJavadocsForParams(method, newParameters, Conditions.alwaysFalse()); } public static void fixJavadocsForParams(PsiMethod method, Set newParameters, Condition> eqCondition) throws IncorrectOperationException { - fixJavadocsForParams(method, newParameters, eqCondition, Conditions.alwaysTrue()); + fixJavadocsForParams(method, newParameters, eqCondition, Conditions.alwaysTrue()); } public static void fixJavadocsForParams(PsiMethod method, @@ -1296,7 +1287,7 @@ public class RefactoringUtil { @Nullable public static PsiTypeParameterList createTypeParameterListWithUsedTypeParameters(@Nullable final PsiTypeParameterList fromList, @NotNull final PsiElement... elements) { - return createTypeParameterListWithUsedTypeParameters(fromList, Conditions.alwaysTrue(), elements); + return createTypeParameterListWithUsedTypeParameters(fromList, Conditions.alwaysTrue(), elements); } @Nullable @@ -1317,7 +1308,7 @@ public class RefactoringUtil { PsiTypeParameter[] typeParameters = used.toArray(new PsiTypeParameter[used.size()]); - Arrays.sort(typeParameters, (tp1, tp2) -> tp1.getTextRange().getStartOffset() - tp2.getTextRange().getStartOffset()); + Arrays.sort(typeParameters, Comparator.comparingInt(tp -> tp.getTextRange().getStartOffset())); final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(elements[0].getProject()).getElementFactory(); try { @@ -1337,7 +1328,7 @@ public class RefactoringUtil { } public static void collectTypeParameters(final Set used, final PsiElement element) { - collectTypeParameters(used, element, Conditions.alwaysTrue()); + collectTypeParameters(used, element, Conditions.alwaysTrue()); } public static void collectTypeParameters(final Set used, final PsiElement element, final Condition filter) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterToArray.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterToArray.java new file mode 100644 index 000000000000..184192f192df --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterToArray.java @@ -0,0 +1,10 @@ +// "Replace with toArray" "true" + +import java.util.ArrayList; +import java.util.List; + +public class Main { + public Object[] testToArray(List data) { + return data.stream().filter(str -> !str.isEmpty()).toArray(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterToArrayAssignment.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterToArrayAssignment.java new file mode 100644 index 000000000000..ba3ebc853413 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterToArrayAssignment.java @@ -0,0 +1,13 @@ +// "Replace with toArray" "true" + +import java.util.ArrayList; +import java.util.List; + +public class Main { + public Object[] testToArray(List data) { + Object[] arr; + arr = data.stream().filter(str -> !str.isEmpty()).toArray(); + System.out.println(Arrays.toString(arr)); + return arr; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterToArrayDeclaration.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterToArrayDeclaration.java new file mode 100644 index 000000000000..8e5492678176 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterToArrayDeclaration.java @@ -0,0 +1,12 @@ +// "Replace with toArray" "true" + +import java.util.ArrayList; +import java.util.List; + +public class Main { + public Object[] testToArray(List data) { + Object[] arr = data.stream().filter(str -> !str.isEmpty()).toArray(); + System.out.println(Arrays.toString(arr)); + return arr; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterToArrayGeneric.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterToArrayGeneric.java new file mode 100644 index 000000000000..bac6de885ea5 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterToArrayGeneric.java @@ -0,0 +1,9 @@ +// "Replace with toArray" "true" + +import java.util.*; + +public class Main { + public List[] testToArray(List data) { + return data.stream().filter(str -> !str.isEmpty()).map(Collections::singletonList).distinct().toArray(List[]::new); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterToArrayHashSet.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterToArrayHashSet.java new file mode 100644 index 000000000000..8153541b95c3 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterToArrayHashSet.java @@ -0,0 +1,9 @@ +// "Replace with toArray" "true" + +import java.util.*; + +public class Main { + public String[] testToArray(List data) { + return data.stream().filter(str -> !str.isEmpty()).distinct().toArray(String[]::new); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterToArrayLinkedHashSet.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterToArrayLinkedHashSet.java new file mode 100644 index 000000000000..e8971cc9f0fe --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterToArrayLinkedHashSet.java @@ -0,0 +1,9 @@ +// "Replace with toArray" "true" + +import java.util.*; + +public class Main { + public CharSequence[] testToArray(List data) { + return data.stream().filter(str -> !str.isEmpty()).distinct().toArray(CharSequence[]::new); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterToArrayReusedCollection.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterToArrayReusedCollection.java new file mode 100644 index 000000000000..ba307445e460 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterToArrayReusedCollection.java @@ -0,0 +1,14 @@ +// "Replace with toArray" "true" + +import java.util.*; + +public class Main { + public String[] testToArray(List data) { + Set result = new LinkedHashSet<>(); + if(!data.isEmpty()) { + return data.stream().filter(str -> !str.isEmpty()).map(String::trim).distinct().toArray(String[]::new); + } + result.add("None"); + return result.toArray(new String[1]); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterToArrayTreeSet.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterToArrayTreeSet.java new file mode 100644 index 000000000000..b963957cb372 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterToArrayTreeSet.java @@ -0,0 +1,9 @@ +// "Replace with toArray" "true" + +import java.util.*; + +public class Main { + public Object[] testToArray(List data) { + return data.stream().filter(str -> !str.isEmpty()).distinct().sorted().toArray(String[]::new); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterToArrayTwoDimensional.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterToArrayTwoDimensional.java new file mode 100644 index 000000000000..74b8da2915ef --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterToArrayTwoDimensional.java @@ -0,0 +1,9 @@ +// "Replace with toArray" "true" + +import java.util.*; + +public class Main { + public String[][] testToArray(List data) { + return data.stream().filter(str -> !str.isEmpty()).map(str -> new String[]{str}).toArray(String[][]::new); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeCollectInterruptedUsedFinal.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeCollectInterruptedUsedFinal.java new file mode 100644 index 000000000000..ff47328fa415 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeCollectInterruptedUsedFinal.java @@ -0,0 +1,20 @@ +// "Replace with collect" "false" +import java.util.*; + +public class Collect { + class Person { + String getName() { + return ""; + } + } + + void collectNames(List persons){ + final Set names = new HashSet<>(), other = new HashSet<>(); + if(persons != null) { + for (Person person : persons) { + names.add(person.getName()); + } + } + System.out.println(names); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArray.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArray.java new file mode 100644 index 000000000000..e12ccb1160cc --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArray.java @@ -0,0 +1,15 @@ +// "Replace with toArray" "true" + +import java.util.ArrayList; +import java.util.List; + +public class Main { + public Object[] testToArray(List data) { + List result = new ArrayList<>(); + for (String str : data) { + if (!str.isEmpty()) + result.add(str); + } + return result.toArray(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayAssignment.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayAssignment.java new file mode 100644 index 000000000000..b241297e9bbc --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayAssignment.java @@ -0,0 +1,18 @@ +// "Replace with toArray" "true" + +import java.util.ArrayList; +import java.util.List; + +public class Main { + public Object[] testToArray(List data) { + Object[] arr; + List result = new ArrayList<>(); + for (String str : data) { + if (!str.isEmpty()) + result.add(str); + } + arr = result.toArray(); + System.out.println(Arrays.toString(arr)); + return arr; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayDeclaration.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayDeclaration.java new file mode 100644 index 000000000000..03a20e1e0e02 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayDeclaration.java @@ -0,0 +1,17 @@ +// "Replace with toArray" "true" + +import java.util.ArrayList; +import java.util.List; + +public class Main { + public Object[] testToArray(List data) { + List result = new ArrayList<>(); + for (String str : data) { + if (!str.isEmpty()) + result.add(str); + } + Object[] arr = result.toArray(); + System.out.println(Arrays.toString(arr)); + return arr; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayDeclarationCollectionUsed.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayDeclarationCollectionUsed.java new file mode 100644 index 000000000000..3d1d0c6e61a9 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayDeclarationCollectionUsed.java @@ -0,0 +1,17 @@ +// "Replace with toArray" "false" + +import java.util.ArrayList; +import java.util.List; + +public class Main { + public Object[] testToArray(List data) { + List result = new ArrayList<>(); + for (String str : data) { + if (!str.isEmpty()) + result.add(str); + } + Object[] arr = result.toArray(); + System.out.println(result); + return arr; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayGeneric.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayGeneric.java new file mode 100644 index 000000000000..165778900f5e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayGeneric.java @@ -0,0 +1,16 @@ +// "Replace with toArray" "true" + +import java.util.*; + +public class Main { + public List[] testToArray(List data) { + Set> result = new LinkedHashSet<>(); + for (String str : data) { + if (!str.isEmpty()) { + List list = Collections.singletonList(str); + result.add(list); + } + } + return result.toArray(new List[0]); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayHashSet.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayHashSet.java new file mode 100644 index 000000000000..d8b05590bc48 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayHashSet.java @@ -0,0 +1,14 @@ +// "Replace with toArray" "true" + +import java.util.*; + +public class Main { + public String[] testToArray(List data) { + Set result = new HashSet<>(); + for (String str : data) { + if (!str.isEmpty()) + result.add(str); + } + return result.toArray(new String[0]); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayIdentityHashSet.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayIdentityHashSet.java new file mode 100644 index 000000000000..ba8f1d924e1f --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayIdentityHashSet.java @@ -0,0 +1,15 @@ +// "Replace with toArray" "false" + +import java.util.ArrayList; +import java.util.List; + +public class Main { + public Object[] testToArray(List data) { + Set result = new IdentityHashSet<>(); + for (String str : data) { + if (!str.isEmpty()) + result.add(str); + } + return result.toArray(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayLinkedHashSet.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayLinkedHashSet.java new file mode 100644 index 000000000000..23ea8e73fdb0 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayLinkedHashSet.java @@ -0,0 +1,14 @@ +// "Replace with toArray" "true" + +import java.util.*; + +public class Main { + public CharSequence[] testToArray(List data) { + Set result = new LinkedHashSet<>(); + for (String str : data) { + if (!str.isEmpty()) + result.add(str); + } + return result.toArray(new CharSequence[result.size()]); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayReusedCollection.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayReusedCollection.java new file mode 100644 index 000000000000..c33bff99ebe0 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayReusedCollection.java @@ -0,0 +1,19 @@ +// "Replace with toArray" "true" + +import java.util.*; + +public class Main { + public String[] testToArray(List data) { + Set result = new LinkedHashSet<>(); + if(!data.isEmpty()) { + for (String str : data) { + if (!str.isEmpty()) { + result.add(str.trim()); + } + } + return result.toArray(new String[result.size()]); + } + result.add("None"); + return result.toArray(new String[1]); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayTreeSet.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayTreeSet.java new file mode 100644 index 000000000000..1a808b15363e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayTreeSet.java @@ -0,0 +1,14 @@ +// "Replace with toArray" "true" + +import java.util.*; + +public class Main { + public Object[] testToArray(List data) { + Set result = new TreeSet<>(); + for (String str : data) { + if (!str.isEmpty()) + result.add(str); + } + return result.toArray(new String[result.size()]); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayTwoDimensional.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayTwoDimensional.java new file mode 100644 index 000000000000..06b4562bdc75 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayTwoDimensional.java @@ -0,0 +1,16 @@ +// "Replace with toArray" "true" + +import java.util.*; + +public class Main { + public String[][] testToArray(List data) { + List result = new ArrayList<>(); + for (String str : data) { + if (!str.isEmpty()) { + String[] arr = {str}; + result.add(arr); + } + } + return result.toArray(new String[result.size()][]); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayWrongSize.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayWrongSize.java new file mode 100644 index 000000000000..64d5a06ff8df --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeToArrayWrongSize.java @@ -0,0 +1,15 @@ +// "Replace with toArray" "false" + +import java.util.ArrayList; +import java.util.List; + +public class Main { + public Object[] testToArray(List data) { + List result = new ArrayList<>(); + for (String str : data) { + if (!str.isEmpty()) + result.add(str); + } + return result.toArray(new String[10]); + } +} \ No newline at end of file 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 4449ed963598..0b2dee272d89 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExpressionUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExpressionUtils.java @@ -17,8 +17,10 @@ package com.siyeh.ig.psiutils; import com.intellij.codeInsight.AnnotationUtil; import com.intellij.codeInsight.NullableNotNullManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.psi.*; +import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.ConstantExpressionUtil; import com.intellij.psi.util.PsiTreeUtil; @@ -35,6 +37,7 @@ import java.util.HashSet; import java.util.Set; public class ExpressionUtils { + private static final Logger LOG = Logger.getInstance(ExpressionUtils.class); @NonNls static final Set convertableBoxedClassNames = new HashSet<>(3); static { @@ -789,4 +792,24 @@ public class ExpressionUtils { } return true; } + + @Contract("null, _ -> null") + public static PsiExpression convertInitializerToNormalExpression(@Nullable PsiExpression expression, @NotNull PsiType forcedReturnType) { + if (expression instanceof PsiArrayInitializerExpression && forcedReturnType instanceof PsiArrayType) { + return createNewExpressionFromArrayInitializer((PsiArrayInitializerExpression)expression, (PsiArrayType)forcedReturnType); + } + return expression; + } + + public static PsiExpression createNewExpressionFromArrayInitializer(@NotNull PsiArrayInitializerExpression initializer, + @NotNull PsiArrayType targetType) { + PsiElementFactory factory = JavaPsiFacade.getInstance(initializer.getProject()).getElementFactory(); + PsiNewExpression result = + (PsiNewExpression)factory.createExpressionFromText("new " + targetType.getPresentableText() + "{}", null); + result = (PsiNewExpression)CodeStyleManager.getInstance(initializer.getProject()).reformat(result); + PsiArrayInitializerExpression arrayInitializer = result.getArrayInitializer(); + LOG.assertTrue(arrayInitializer != null); + arrayInitializer.replace(initializer); + return result; + } } \ No newline at end of file