IDEA-166499 Stream API migration: support array filling in a loop

This commit is contained in:
Tagir Valeev
2017-01-12 17:37:10 +07:00
parent dd51b4ee06
commit 5b04785164
10 changed files with 202 additions and 1 deletions
@@ -16,6 +16,7 @@
package com.intellij.codeInspection.streamMigration;
import com.intellij.codeInsight.ExceptionUtil;
import com.intellij.codeInsight.PsiEquivalenceUtil;
import com.intellij.codeInsight.daemon.GroupNames;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInspection.BaseJavaBatchLocalInspectionTool;
@@ -566,6 +567,9 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
}
}
if (tb.getCountExpression() != null || tb.isEmpty()) return null;
if (nonFinalVariables.isEmpty() && extractArray(tb) != null) {
return new ToArrayMigration();
}
if (getAccumulatedVariable(tb, nonFinalVariables) != null) {
return new SumMigration();
}
@@ -691,6 +695,34 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
}
}
@Nullable
static PsiLocalVariable extractArray(TerminalBlock tb) {
CountingLoop loop = tb.getLastOperation(CountingLoop.class);
if(loop == null || loop.myIncluding) return null;
PsiAssignmentExpression assignment = tb.getSingleExpression(PsiAssignmentExpression.class);
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;
PsiReferenceExpression arrayReference = tryCast(arrayAccess.getArrayExpression(), PsiReferenceExpression.class);
if(arrayReference == null) return null;
PsiLocalVariable arrayVariable = tryCast(arrayReference.resolve(), PsiLocalVariable.class);
if(arrayVariable == null || getInitializerUsageStatus(arrayVariable, tb.getMainLoop()) == UNKNOWN) return null;
PsiNewExpression initializer = tryCast(arrayVariable.getInitializer(), PsiNewExpression.class);
if(initializer == null) return null;
PsiArrayType arrayType = tryCast(initializer.getType(), PsiArrayType.class);
if(arrayType == null || !isSupported(arrayType.getComponentType())) return null;
PsiExpression dimension = ArrayUtil.getFirstElement(initializer.getArrayDimensions());
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;
return arrayVariable;
}
/**
* Intermediate stream operation representation
*/
@@ -1085,7 +1117,7 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
final boolean myIncluding;
private CountingLoop(PsiLoopStatement loop,
PsiLocalVariable counter,
PsiVariable counter,
PsiExpression initializer,
PsiExpression bound,
boolean including) {
@@ -1106,6 +1138,10 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
return className+"."+methodName+"("+myExpression.getText()+", "+myBound.getText()+")";
}
CountingLoop withBound(PsiExpression bound) {
return new CountingLoop(getLoop(), getVariable(), getExpression(), bound, myIncluding);
}
@Override
boolean isWriteAllowed(PsiVariable variable, PsiExpression reference) {
if(variable == myVariable) {
@@ -0,0 +1,65 @@
/*
* 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.codeInspection.streamMigration.StreamApiMigrationInspection.CountingLoop;
import com.intellij.codeInspection.streamMigration.StreamApiMigrationInspection.InitializerUsageStatus;
import com.intellij.codeInspection.streamMigration.StreamApiMigrationInspection.MapOp;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import static com.intellij.util.ObjectUtils.tryCast;
/**
* @author Tagir Valeev
*/
public class ToArrayMigration extends BaseStreamApiMigration {
protected ToArrayMigration() {
super("toArray");
}
@Override
PsiElement migrate(@NotNull Project project, @NotNull PsiStatement body, @NotNull TerminalBlock tb) {
PsiLocalVariable arrayVariable = StreamApiMigrationInspection.extractArray(tb);
if(arrayVariable == null) return null;
PsiAssignmentExpression assignment = tb.getSingleExpression(PsiAssignmentExpression.class);
if(assignment == null) return null;
PsiExpression rValue = assignment.getRExpression();
if(rValue == null) return null;
PsiNewExpression initializer = tryCast(arrayVariable.getInitializer(), PsiNewExpression.class);
if(initializer == null) return null;
PsiExpression dimension = ArrayUtil.getFirstElement(initializer.getArrayDimensions());
if(dimension == null) return null;
CountingLoop loop = tb.getLastOperation(CountingLoop.class);
if(loop == null) return null;
PsiArrayType arrayType = tryCast(initializer.getType(), PsiArrayType.class);
if(arrayType == null) return null;
InitializerUsageStatus status = StreamApiMigrationInspection.getInitializerUsageStatus(arrayVariable, tb.getMainLoop());
if(status == InitializerUsageStatus.UNKNOWN) return null;
PsiType componentType = arrayType.getComponentType();
String supplier;
if(componentType instanceof PsiPrimitiveType || componentType.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) {
supplier = "";
} else {
supplier = arrayType.getCanonicalText()+"::new";
}
MapOp mapping = new MapOp(rValue, tb.getVariable(), assignment.getType());
String replacementText = loop.withBound(dimension).createReplacement() + mapping.createReplacement() + ".toArray(" + supplier + ")";
return replaceInitializer(tb.getMainLoop(), arrayVariable, initializer, replacementText, status);
}
}
@@ -0,0 +1,12 @@
// "Replace with toArray" "true"
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
public class Test {
public void test(List<Integer> ints) {
long[] arr = IntStream.range(0, ints.size()).mapToLong(ints::get).toArray();
System.out.println(Arrays.toString(arr));
}
}
@@ -0,0 +1,11 @@
// "Replace with toArray" "true"
import java.util.Arrays;
import java.util.stream.IntStream;
public class Test {
public void test(int bound) {
Object[] arr = IntStream.range(0, bound).boxed().toArray(Integer[]::new);
System.out.println(Arrays.toString(arr));
}
}
@@ -0,0 +1,11 @@
// "Replace with toArray" "true"
import java.util.Arrays;
import java.util.stream.IntStream;
public class Test {
public void test(int bound) {
Integer[][] arr = IntStream.range(0, bound).mapToObj(i -> new Integer[]{i}).toArray(Integer[][]::new);
System.out.println(Arrays.toString(arr));
}
}
@@ -0,0 +1,12 @@
// "Replace with toArray" "true"
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
public class Test {
public void test(List<List<String>> list) {
List<?>[] arr = IntStream.range(0, list.size()).mapToObj(list::get).toArray(List[]::new);
System.out.println(Arrays.toString(arr));
}
}
@@ -0,0 +1,14 @@
// "Replace with toArray" "true"
import java.util.Arrays;
import java.util.List;
public class Test {
public void test(List<Integer> ints) {
long[] arr = new long[ints.size()];
for(int <caret>i = 0; i < ints.size(); i++) {
arr[i] = ints.get(i);
}
System.out.println(Arrays.toString(arr));
}
}
@@ -0,0 +1,13 @@
// "Replace with toArray" "true"
import java.util.Arrays;
public class Test {
public void test(int bound) {
Object[] arr = new Integer[bound];
for(int <caret>i = 0; i < bound; i++) {
arr[i] = i;
}
System.out.println(Arrays.toString(arr));
}
}
@@ -0,0 +1,13 @@
// "Replace with toArray" "true"
import java.util.Arrays;
public class Test {
public void test(int bound) {
Integer[][] arr = new Integer[bound][];
for(int <caret>i = 0; i < arr.length; i++) {
arr[i] = new Integer[] {i};
}
System.out.println(Arrays.toString(arr));
}
}
@@ -0,0 +1,14 @@
// "Replace with toArray" "true"
import java.util.Arrays;
import java.util.List;
public class Test {
public void test(List<List<String>> list) {
List<?>[] arr = new List[list.size()];
for(i<caret>nt i = 0; i < arr.length; i++) {
arr[i] = list.get(i);
}
System.out.println(Arrays.toString(arr));
}
}