Stream API migration various fixes

1. findFirst() scenario can pull previous assignment (not declaration) now
2. anyMatch() fix did not work if there's single assignment to non-variable (e.g. array element)
3. if non-adjacent return becomes unreachable after findFirst()/anyMatch(), it returned automatically now
This commit is contained in:
Tagir Valeev
2016-10-07 11:59:52 +07:00
parent 3a7563c3f0
commit ebf320b288
11 changed files with 198 additions and 23 deletions
@@ -24,6 +24,7 @@ import com.intellij.openapi.project.Project;
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.util.PsiTreeUtil;
import com.siyeh.ig.psiutils.ExpressionUtils;
import one.util.streamex.StreamEx;
@@ -152,4 +153,16 @@ abstract class MigrateToStreamFix implements LocalQuickFix {
statement.delete();
}
}
static boolean isReachable(PsiReturnStatement target) {
ControlFlow flow;
try {
flow = ControlFlowFactory.getInstance(target.getProject())
.getControlFlow(target.getParent(), LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance());
}
catch (AnalysisCanceledException e) {
return true;
}
return ControlFlowUtil.isInstructionReachable(flow, flow.getStartOffset(target), 0);
}
}
@@ -19,6 +19,7 @@ import com.intellij.codeInsight.PsiEquivalenceUtil;
import com.intellij.codeInspection.streamMigration.StreamApiMigrationInspection.InitializerUsageStatus;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.util.RefactoringUtil;
import com.siyeh.ig.psiutils.ExpressionUtils;
import org.jetbrains.annotations.NotNull;
@@ -52,10 +53,12 @@ class ReplaceWithFindFirstFix extends MigrateToStreamFix {
if (!ExpressionUtils.isSimpleExpression(orElseExpression)) return null;
stream = generateOptionalUnwrap(stream, tb, value, orElseExpression, null);
restoreComments(loopStatement, body);
if (nextReturnStatement.getParent() == loopStatement.getParent()) {
boolean sibling = nextReturnStatement.getParent() == loopStatement.getParent();
PsiElement replacement = loopStatement.replace(elementFactory.createStatementFromText("return " + stream + ";", loopStatement));
if(sibling || !isReachable(nextReturnStatement)) {
nextReturnStatement.delete();
}
return loopStatement.replace(elementFactory.createStatementFromText("return " + stream + ";", loopStatement));
return replacement;
}
else {
PsiStatement[] statements = tb.getStatements();
@@ -84,6 +87,17 @@ class ReplaceWithFindFirstFix extends MigrateToStreamFix {
return replaceInitializer(loopStatement, var, initializer, replacementText, status);
}
}
PsiAssignmentExpression previousAssignment =
ExpressionUtils.getAssignment(PsiTreeUtil.skipSiblingsBackward(loopStatement, PsiWhiteSpace.class, PsiComment.class));
if(previousAssignment != null) {
PsiExpression prevRValue = previousAssignment.getRExpression();
PsiExpression prevLValue = previousAssignment.getLExpression();
if(prevRValue != null && prevLValue instanceof PsiReferenceExpression && ((PsiReferenceExpression)prevLValue).resolve() == var) {
previousAssignment.delete();
return loopStatement.replace(elementFactory.createStatementFromText(
var.getName() + " = " + generateOptionalUnwrap(stream, tb, value, prevRValue, var.getType()) + ";", loopStatement));
}
}
return loopStatement.replace(elementFactory.createStatementFromText(
var.getName() + " = " + generateOptionalUnwrap(stream, tb, value, lValue, var.getType()) + ";", loopStatement));
}
@@ -69,7 +69,11 @@ class ReplaceWithMatchFix extends MigrateToStreamFix {
removeLoop(loopStatement);
return returnValue.replace(elementFactory.createExpressionFromText(streamText, nextReturnStatement));
}
return loopStatement.replace(elementFactory.createStatementFromText("return " + streamText + ";", loopStatement));
PsiElement result = loopStatement.replace(elementFactory.createStatementFromText("return " + streamText + ";", loopStatement));
if(!isReachable(nextReturnStatement)) {
nextReturnStatement.delete();
}
return result;
}
}
}
@@ -84,27 +88,30 @@ class ReplaceWithMatchFix extends MigrateToStreamFix {
if(assignment != null) {
PsiExpression lValue = assignment.getLExpression();
PsiExpression rValue = assignment.getRExpression();
if (!(lValue instanceof PsiReferenceExpression) || rValue == null) return null;
PsiElement maybeVar = ((PsiReferenceExpression)lValue).resolve();
if(maybeVar instanceof PsiVariable) {
// Simplify single assignments like this:
// boolean flag = false;
// for(....) if(...) {flag = true; break;}
PsiVariable var = (PsiVariable)maybeVar;
PsiExpression initializer = var.getInitializer();
InitializerUsageStatus status = StreamApiMigrationInspection.getInitializerUsageStatus(var, loopStatement);
if(initializer != null && status != InitializerUsageStatus.UNKNOWN) {
String replacement;
if(ExpressionUtils.isLiteral(initializer, Boolean.FALSE) &&
ExpressionUtils.isLiteral(rValue, Boolean.TRUE)) {
replacement = streamText;
} else if(ExpressionUtils.isLiteral(initializer, Boolean.TRUE) &&
ExpressionUtils.isLiteral(rValue, Boolean.FALSE)) {
replacement = "!"+streamText;
} else {
replacement = streamText + "?" + rValue.getText() + ":" + initializer.getText();
if ((lValue instanceof PsiReferenceExpression) && rValue != null) {
PsiElement maybeVar = ((PsiReferenceExpression)lValue).resolve();
if (maybeVar instanceof PsiVariable) {
// Simplify single assignments like this:
// boolean flag = false;
// for(....) if(...) {flag = true; break;}
PsiVariable var = (PsiVariable)maybeVar;
PsiExpression initializer = var.getInitializer();
InitializerUsageStatus status = StreamApiMigrationInspection.getInitializerUsageStatus(var, loopStatement);
if (initializer != null && status != InitializerUsageStatus.UNKNOWN) {
String replacement;
if (ExpressionUtils.isLiteral(initializer, Boolean.FALSE) &&
ExpressionUtils.isLiteral(rValue, Boolean.TRUE)) {
replacement = streamText;
}
else if (ExpressionUtils.isLiteral(initializer, Boolean.TRUE) &&
ExpressionUtils.isLiteral(rValue, Boolean.FALSE)) {
replacement = "!" + streamText;
}
else {
replacement = streamText + "?" + rValue.getText() + ":" + initializer.getText();
}
return replaceInitializer(loopStatement, var, initializer, replacement, status);
}
return replaceInitializer(loopStatement, var, initializer, replacement, status);
}
}
}
@@ -0,0 +1,12 @@
// "Replace with anyMatch()" "true"
import java.util.List;
public class Main {
public void testAssignment(List<String> data) {
String[] found = {"no"};
if (data.stream().map(String::trim).anyMatch(trimmed -> !trimmed.isEmpty())) {
found[0] = "yes";
}
}
}
@@ -0,0 +1,13 @@
// "Replace with anyMatch()" "true"
import java.util.List;
public class Main {
boolean find(List<String> data) {
if(data != null) {
return data.stream().map(String::trim).anyMatch(trimmed -> trimmed.startsWith("xyz"));
} else {
throw new IllegalArgumentException();
}
}
}
@@ -0,0 +1,20 @@
// "Replace with findFirst()" "true"
import java.util.List;
import java.util.Map;
import java.util.Objects;
public class Main {
private int getInitialSize() {return 0;}
public void testMap(Map<String, List<String>> map) throws Exception {
int firstSize = 10;
System.out.println(firstSize);
// loop
// comment
firstSize = map.values().stream().filter(Objects::nonNull).findFirst().map(List::size).orElse(getInitialSize());
System.out.println(firstSize);
}
}
@@ -0,0 +1,15 @@
// "Replace with findFirst()" "true"
import java.util.Collection;
import java.util.List;
public class Main {
public static String find(List<List<String>> list) {
if(list == null) {
System.out.println("oops");
return "";
} else {
return list.stream().flatMap(Collection::stream).filter(string -> string.startsWith("ABC")).findFirst().orElse(null);
}
}
}
@@ -0,0 +1,16 @@
// "Replace with anyMatch()" "true"
import java.util.List;
public class Main {
public void testAssignment(List<String> data) {
String[] found = {"no"};
for(String str : da<caret>ta) {
String trimmed = str.trim();
if(!trimmed.isEmpty()) {
found[0] = "yes";
break;
}
}
}
}
@@ -0,0 +1,19 @@
// "Replace with anyMatch()" "true"
import java.util.List;
public class Main {
boolean find(List<String> data) {
if(data != null) {
for (String e : da<caret>ta) {
String trimmed = e.trim();
if (trimmed.startsWith("xyz")) {
return true;
}
}
} else {
throw new IllegalArgumentException();
}
return false;
}
}
@@ -0,0 +1,25 @@
// "Replace with findFirst()" "true"
import java.util.List;
import java.util.Map;
public class Main {
private int getInitialSize() {return 0;}
public void testMap(Map<String, List<String>> map) throws Exception {
int firstSize = 10;
System.out.println(firstSize);
firstSize = getInitialSize();
// loop
for(List<String> list : map.valu<caret>es()) {
if(list != null) {
firstSize = list.size();
// comment
break;
}
}
System.out.println(firstSize);
}
}
@@ -0,0 +1,21 @@
// "Replace with findFirst()" "true"
import java.util.List;
public class Main {
public static String find(List<List<String>> list) {
if(list == null) {
System.out.println("oops");
return "";
} else {
for (List<String> innerList : lis<caret>t) {
for (String string : innerList) {
if (string.startsWith("ABC")) {
return string;
}
}
}
}
return null;
}
}