StreamApiMigrationInspection improvements: expression+break conversion to anyMatch; pull out cast of stream.findFirst().map(x -> (T)x).orElse(null) to (T) stream.findFirst().orElse(null)

This commit is contained in:
Tagir Valeev
2016-09-26 15:52:55 +07:00
parent c52a93044c
commit e69109e521
17 changed files with 281 additions and 40 deletions
@@ -44,6 +44,7 @@ class ReplaceWithFindFirstFix extends MigrateToStreamFix {
@NotNull List<Operation> operations) {
PsiStatement statement = tb.getSingleStatement();
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
String stream = generateStream(iteratedValue, operations).append(".findFirst()").toString();
if (statement instanceof PsiReturnStatement) {
PsiReturnStatement returnStatement = (PsiReturnStatement)statement;
PsiExpression value = returnStatement.getReturnValue();
@@ -52,14 +53,10 @@ class ReplaceWithFindFirstFix extends MigrateToStreamFix {
if (nextReturnStatement == null) return;
PsiExpression orElseExpression = nextReturnStatement.getReturnValue();
if (!ExpressionUtils.isSimpleExpression(orElseExpression)) return;
StringBuilder builder = generateStream(iteratedValue, operations).append(".findFirst()");
if (!(value instanceof PsiReferenceExpression) || ((PsiReferenceExpression)value).resolve() != tb.getVariable()) {
builder.append(".map(").append(tb.getVariable().getName()).append(" -> ").append(value.getText()).append(")");
}
builder.append(".orElse(").append(orElseExpression.getText()).append(")");
stream = generateOptionalUnwrap(stream, tb, value, orElseExpression);
restoreComments(foreachStatement, body);
boolean siblings = nextReturnStatement.getParent() == foreachStatement.getParent();
PsiElement result = foreachStatement.replace(elementFactory.createStatementFromText("return " + builder + ";", foreachStatement));
PsiElement result = foreachStatement.replace(elementFactory.createStatementFromText("return " + stream + ";", foreachStatement));
if (siblings) {
nextReturnStatement.delete();
}
@@ -77,24 +74,38 @@ class ReplaceWithFindFirstFix extends MigrateToStreamFix {
PsiVariable var = (PsiVariable)element;
PsiExpression value = assignment.getRExpression();
if (value == null) return;
StringBuilder builder = generateStream(iteratedValue, operations).append(".findFirst()");
if (!(value instanceof PsiReferenceExpression) || ((PsiReferenceExpression)value).resolve() != tb.getVariable()) {
builder.append(".map(").append(tb.getVariable().getName()).append(" -> ").append(value.getText()).append(")");
}
restoreComments(foreachStatement, body);
if (StreamApiMigrationInspection.isDeclarationJustBefore(var, foreachStatement)) {
PsiExpression initializer = var.getInitializer();
if (initializer != null) {
PsiElement result =
initializer.replace(elementFactory.createExpressionFromText(builder + ".orElse(" + initializer.getText() + ")", initializer));
initializer.replace(elementFactory.createExpressionFromText(generateOptionalUnwrap(stream, tb, value, initializer), initializer));
removeLoop(foreachStatement);
simplifyAndFormat(project, result);
return;
}
}
PsiElement result = foreachStatement.replace(
elementFactory.createStatementFromText(var.getName() + " = " + builder + ".orElse(" + var.getName() + ");", foreachStatement));
PsiElement result = foreachStatement.replace(elementFactory.createStatementFromText(
var.getName() + " = " + generateOptionalUnwrap(stream, tb, value, lValue) + ";", foreachStatement));
simplifyAndFormat(project, result);
}
}
private static String generateOptionalUnwrap(String stream, @NotNull StreamApiMigrationInspection.TerminalBlock tb,
PsiExpression trueExpression, PsiExpression falseExpression) {
PsiVariable var = tb.getVariable();
if (!StreamApiMigrationInspection.isIdentityMapping(var, trueExpression)) {
if(trueExpression instanceof PsiTypeCastExpression && ExpressionUtils.isNullLiteral(falseExpression)) {
PsiTypeCastExpression castExpression = (PsiTypeCastExpression)trueExpression;
PsiTypeElement castType = castExpression.getCastType();
// pull cast outside to avoid the .map() step
if(castType != null && StreamApiMigrationInspection.isIdentityMapping(var, castExpression.getOperand())) {
return "(" + castType.getText() + ")" + stream + ".orElse(null)";
}
}
stream += ".map(" + StreamApiMigrationInspection.createLambda(var, trueExpression) + ")";
}
stream += ".orElse(" + falseExpression.getText() + ")";
return stream;
}
}
@@ -21,6 +21,7 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.siyeh.ig.psiutils.BoolUtils;
import com.siyeh.ig.psiutils.ExpressionUtils;
import org.jetbrains.annotations.NotNull;
import java.util.List;
@@ -51,31 +52,66 @@ class ReplaceWithMatchFix extends MigrateToStreamFix {
@NotNull PsiStatement body,
@NotNull StreamApiMigrationInspection.TerminalBlock tb,
@NotNull List<Operation> operations) {
PsiReturnStatement returnStatement = (PsiReturnStatement)tb.getSingleStatement();
PsiExpression value = returnStatement.getReturnValue();
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
restoreComments(foreachStatement, body);
if (StreamApiMigrationInspection.isLiteral(value, Boolean.TRUE) || StreamApiMigrationInspection.isLiteral(value, Boolean.FALSE)) {
boolean foundResult = (boolean)((PsiLiteralExpression)value).getValue();
PsiReturnStatement nextReturnStatement = StreamApiMigrationInspection.getNextReturnStatement(foreachStatement);
if (nextReturnStatement != null && StreamApiMigrationInspection.isLiteral(nextReturnStatement.getReturnValue(), !foundResult)) {
String methodName = foundResult ? "anyMatch" : "noneMatch";
String streamText = generateStream(iteratedValue, operations).toString();
streamText = addTerminalOperation(streamText, methodName, foreachStatement, tb);
boolean siblings = nextReturnStatement.getParent() == foreachStatement.getParent();
PsiElement result =
foreachStatement.replace(elementFactory.createStatementFromText("return " + streamText + ";", foreachStatement));
if (siblings) {
nextReturnStatement.delete();
if(tb.getSingleStatement() instanceof PsiReturnStatement) {
PsiReturnStatement returnStatement = (PsiReturnStatement)tb.getSingleStatement();
PsiExpression value = returnStatement.getReturnValue();
if (StreamApiMigrationInspection.isLiteral(value, Boolean.TRUE) || StreamApiMigrationInspection.isLiteral(value, Boolean.FALSE)) {
boolean foundResult = (boolean)((PsiLiteralExpression)value).getValue();
PsiReturnStatement nextReturnStatement = StreamApiMigrationInspection.getNextReturnStatement(foreachStatement);
if (nextReturnStatement != null && StreamApiMigrationInspection.isLiteral(nextReturnStatement.getReturnValue(), !foundResult)) {
String methodName = foundResult ? "anyMatch" : "noneMatch";
String streamText = generateStream(iteratedValue, operations).toString();
streamText = addTerminalOperation(streamText, methodName, foreachStatement, tb);
restoreComments(foreachStatement, body);
boolean siblings = nextReturnStatement.getParent() == foreachStatement.getParent();
PsiElement result =
foreachStatement.replace(elementFactory.createStatementFromText("return " + streamText + ";", foreachStatement));
if (siblings) {
nextReturnStatement.delete();
}
simplifyAndFormat(project, result);
return;
}
simplifyAndFormat(project, result);
return;
}
}
if (!StreamApiMigrationInspection.isVariableReferenced(tb.getVariable(), value)) {
PsiStatement[] statements = tb.getStatements();
if(statements.length == 1 || (statements.length == 2 && statements[1] instanceof PsiBreakStatement)) {
restoreComments(foreachStatement, body);
String streamText = generateStream(iteratedValue, operations).toString();
streamText = addTerminalOperation(streamText, "anyMatch", foreachStatement, tb);
String replacement = "if(" + streamText + "){" + returnStatement.getText() + "}";
PsiStatement statement = statements[0];
PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(statement);
if(assignment != null) {
PsiExpression lValue = assignment.getLExpression();
PsiExpression rValue = assignment.getRExpression();
if (!(lValue instanceof PsiReferenceExpression) || rValue == null) return;
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();
if(initializer != null && StreamApiMigrationInspection.isDeclarationJustBefore(var, foreachStatement)) {
String replacement;
if(StreamApiMigrationInspection.isLiteral(initializer, Boolean.FALSE) &&
StreamApiMigrationInspection.isLiteral(rValue, Boolean.TRUE)) {
replacement = streamText;
} else if(StreamApiMigrationInspection.isLiteral(initializer, Boolean.TRUE) &&
StreamApiMigrationInspection.isLiteral(rValue, Boolean.FALSE)) {
replacement = "!"+streamText;
} else {
replacement = streamText + "?" + rValue.getText() + ":" + initializer.getText();
}
PsiElement result = initializer.replace(elementFactory.createExpressionFromText(replacement, initializer));
removeLoop(foreachStatement);
simplifyAndFormat(project, result);
return;
}
}
}
String replacement = "if(" + streamText + "){" + statement.getText() + "}";
PsiElement result = foreachStatement.replace(elementFactory.createStatementFromText(replacement, foreachStatement));
simplifyAndFormat(project, result);
}
@@ -516,14 +516,19 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
if (nonFinalVariables.isEmpty() && tb.getSingleStatement() instanceof PsiReturnStatement) {
handleSingleReturn(statement, tb, operations);
}
if (nonFinalVariables.size() == 1) {
PsiStatement[] statements = tb.getStatements();
if (statements.length == 2) {
PsiStatement breakStatement = statements[1];
if (!(breakStatement instanceof PsiBreakStatement) ||
((PsiBreakStatement)breakStatement).findExitedStatement() != statement) {
return;
}
PsiStatement[] statements = tb.getStatements();
if (statements.length == 2) {
PsiStatement breakStatement = statements[1];
if (!(breakStatement instanceof PsiBreakStatement) ||
((PsiBreakStatement)breakStatement).findExitedStatement() != statement) {
return;
}
if (ReferencesSearch.search(tb.getVariable(), new LocalSearchScope(statements)).findFirst() == null
&& exitPoints.size() == 1 && exitPoints.contains(breakStatement)) {
registerProblem(statement, "anyMatch", new ReplaceWithMatchFix("anyMatch"));
return;
}
if (nonFinalVariables.size() == 1) {
PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(statements[0]);
if(assignment == null) return;
PsiExpression lValue = assignment.getLExpression();
@@ -566,6 +571,9 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
}
}
if (!isVariableReferenced(tb.getVariable(), value)) {
if(!REPLACE_TRIVIAL_FOREACH && operations.isEmpty() || (operations.size() == 1 && operations.get(0) instanceof FilterOp)) {
return;
}
registerProblem(statement, "anyMatch", new ReplaceWithMatchFix("anyMatch"));
}
if(nextReturnStatement != null && ExpressionUtils.isSimpleExpression(nextReturnStatement.getReturnValue())
@@ -0,0 +1,9 @@
// "Replace with anyMatch()" "true"
import java.util.List;
public class Main {
public void testAssignment(List<String> data) {
String found = data.stream().map(String::trim).anyMatch(trimmed -> !trimmed.isEmpty()) ? "yes" : "no";
}
}
@@ -0,0 +1,9 @@
// "Replace with anyMatch()" "true"
import java.util.List;
public class Main {
public void testAssignment(List<String> data) {
boolean found = data.stream().map(String::trim).anyMatch(trimmed -> !trimmed.isEmpty());
}
}
@@ -0,0 +1,9 @@
// "Replace with anyMatch()" "true"
import java.util.List;
public class Main {
public void testAssignment(List<String> data) {
boolean found = !data.stream().map(String::trim).anyMatch(trimmed -> !trimmed.isEmpty());
}
}
@@ -0,0 +1,16 @@
// "Replace with anyMatch()" "true"
import java.util.List;
public class Main {
public void testAssignment(List<String> data) {
boolean found = false;
if(Math.random() > 0.5) {
found = true;
} else {
if (data.stream().map(String::trim).anyMatch(trimmed -> !trimmed.isEmpty())) {
found = true;
}
}
}
}
@@ -0,0 +1,11 @@
// "Replace with anyMatch()" "true"
import java.util.List;
public class Main {
public void testAssignment(List<String> data) {
if (data.stream().map(String::trim).anyMatch(trimmed -> !trimmed.isEmpty())) {
System.out.println("Found!!!");
}
}
}
@@ -0,0 +1,10 @@
// "Replace with findFirst()" "true"
import java.util.List;
public class Main {
public void testCast(List<Object> data) {
String found = (String) data.stream().filter(obj -> obj instanceof String).findFirst().orElse(null);
System.out.println(found);
}
}
@@ -0,0 +1,9 @@
// "Replace with findFirst()" "true"
import java.util.List;
public class Main {
public String testCast(List<Object> data) {
return (String) data.stream().filter(obj -> obj instanceof String).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 = "yes";
break;
}
}
}
}
@@ -0,0 +1,16 @@
// "Replace with anyMatch()" "true"
import java.util.List;
public class Main {
public void testAssignment(List<String> data) {
boolean found = false;
for(String str : da<caret>ta) {
String trimmed = str.trim();
if(!trimmed.isEmpty()) {
found = true;
break;
}
}
}
}
@@ -0,0 +1,16 @@
// "Replace with anyMatch()" "true"
import java.util.List;
public class Main {
public void testAssignment(List<String> data) {
boolean found = true;
for(String str : da<caret>ta) {
String trimmed = str.trim();
if(!trimmed.isEmpty()) {
found = false;
break;
}
}
}
}
@@ -0,0 +1,20 @@
// "Replace with anyMatch()" "true"
import java.util.List;
public class Main {
public void testAssignment(List<String> data) {
boolean found = false;
if(Math.random() > 0.5) {
found = true;
} else {
for (String str : da<caret>ta) {
String trimmed = str.trim();
if (!trimmed.isEmpty()) {
found = true;
break;
}
}
}
}
}
@@ -0,0 +1,15 @@
// "Replace with anyMatch()" "true"
import java.util.List;
public class Main {
public void testAssignment(List<String> data) {
for (String str : d<caret>ata) {
String trimmed = str.trim();
if (!trimmed.isEmpty()) {
System.out.println("Found!!!");
break;
}
}
}
}
@@ -0,0 +1,16 @@
// "Replace with findFirst()" "true"
import java.util.List;
public class Main {
public void testCast(List<Object> data) {
String found = null;
for (Object obj : da<caret>ta) {
if(obj instanceof String) {
found = (String)obj;
break;
}
}
System.out.println(found);
}
}
@@ -0,0 +1,14 @@
// "Replace with findFirst()" "true"
import java.util.List;
public class Main {
public String testCast(List<Object> data) {
for (Object obj : d<caret>ata) {
if(obj instanceof String) {
return (String)obj;
}
}
return null;
}
}