From 884043aaccd1bc09dea255474faf74ec653912c8 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Fri, 9 Sep 2016 17:41:19 +0700 Subject: [PATCH] IDEA-161002 Migration to Stream API: convert return true/return false to anyMatch/noneMatch --- .../StreamApiMigrationInspection.java | 67 +++++++++++++++++++ .../streamApiMigration/afterAnyMatch.java | 9 +++ .../streamApiMigration/afterNoneMatch.java | 9 +++ .../streamApiMigration/beforeAnyMatch.java | 15 +++++ .../streamApiMigration/beforeNoneMatch.java | 14 ++++ 5 files changed, 114 insertions(+) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatch.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterNoneMatch.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatch.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeNoneMatch.java diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/StreamApiMigrationInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/StreamApiMigrationInspection.java index 155db7783e91..b501606a0894 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/StreamApiMigrationInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/StreamApiMigrationInspection.java @@ -180,6 +180,24 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo ProblemHighlightType.GENERIC_ERROR_OR_WARNING, fixes.toArray(new LocalQuickFix[fixes.size()])); } + } else { + if(tb.getSingleStatement() instanceof PsiReturnStatement) { + PsiReturnStatement returnStatement = (PsiReturnStatement)tb.getSingleStatement(); + PsiExpression value = returnStatement.getReturnValue(); + if(isLiteral(value, Boolean.TRUE) || isLiteral(value, Boolean.FALSE)) { + boolean foundResult = (boolean)((PsiLiteralExpression)value).getValue(); + PsiElement nextStatement = PsiTreeUtil.skipSiblingsForward(statement, PsiWhiteSpace.class, PsiComment.class); + if(nextStatement instanceof PsiReturnStatement) { + PsiReturnStatement nextReturnStatement = (PsiReturnStatement)nextStatement; + if(isLiteral(nextReturnStatement.getReturnValue(), !foundResult)) { + String methodName = foundResult ? "anyMatch" : "noneMatch"; + holder.registerProblem(iteratedValue, "Can be replaced with "+methodName+"() call", + ProblemHighlightType.GENERIC_ERROR_OR_WARNING, + new ReplaceWithMatchFix(methodName)); + } + } + } + } } } } @@ -802,6 +820,55 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo } } + private static class ReplaceWithMatchFix extends MigrateToStreamFix { + + private final String myMethodName; + + public ReplaceWithMatchFix(String methodName) { + myMethodName = methodName; + } + + @NotNull + @Override + public String getFamilyName() { + return "Replace with " + myMethodName + "()"; + } + + @Override + void migrate(@NotNull Project project, + @NotNull ProblemDescriptor descriptor, + @NotNull PsiForeachStatement foreachStatement, + @NotNull PsiExpression iteratedValue, + @NotNull PsiStatement body, + @NotNull TerminalBlock tb, + @NotNull List intermediateOps) { + PsiReturnStatement returnStatement = (PsiReturnStatement)tb.getSingleStatement(); + PsiExpression value = returnStatement.getReturnValue(); + if(!isLiteral(value, Boolean.TRUE) && !isLiteral(value, Boolean.FALSE)) return; + boolean foundResult = (boolean)((PsiLiteralExpression)value).getValue(); + PsiElement nextStatement = PsiTreeUtil.skipSiblingsForward(foreachStatement, PsiWhiteSpace.class, PsiComment.class); + if(!(nextStatement instanceof PsiReturnStatement)) return; + PsiReturnStatement nextReturnStatement = (PsiReturnStatement)nextStatement; + if(!isLiteral(nextReturnStatement.getReturnValue(), !foundResult)) return; + String methodName = foundResult ? "anyMatch" : "noneMatch"; + final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project); + String streamText = generateStream(iteratedValue, intermediateOps).toString(); + PsiExpression stream = + elementFactory.createExpressionFromText(streamText, foreachStatement); + if(!(stream instanceof PsiMethodCallExpression)) return; + PsiElement nameElement = ((PsiMethodCallExpression)stream).getMethodExpression().getReferenceNameElement(); + if(nameElement != null && nameElement.getText().equals("filter")) { + nameElement.replace(elementFactory.createIdentifier(methodName)); + streamText = stream.getText(); + } else { + streamText += "."+methodName+"("+tb.getVariable().getName()+" -> true)"; + } + PsiElement result = foreachStatement.replace(elementFactory.createStatementFromText("return " + streamText + ";", foreachStatement)); + nextReturnStatement.delete(); + simplifyAndFormat(project, result); + } + } + private static class ReplaceWithSumFix extends MigrateToStreamFix { @NotNull diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatch.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatch.java new file mode 100644 index 000000000000..8aa5ac5f6c67 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterAnyMatch.java @@ -0,0 +1,9 @@ +// "Replace with anyMatch()" "true" + +import java.util.List; + +public class Main { + boolean find(List data) { + return data.stream().map(String::trim).anyMatch(trimmed -> trimmed.startsWith("xyz")); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterNoneMatch.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterNoneMatch.java new file mode 100644 index 000000000000..baaccd8b931a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterNoneMatch.java @@ -0,0 +1,9 @@ +// "Replace with noneMatch()" "true" + +import java.util.Arrays; + +public class Main { + boolean find(String[][] data) { + return Arrays.stream(data).flatMap(Arrays::stream).noneMatch(str -> str.startsWith("xyz")); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatch.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatch.java new file mode 100644 index 000000000000..8a6c2b5ec4c5 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatch.java @@ -0,0 +1,15 @@ +// "Replace with anyMatch()" "true" + +import java.util.List; + +public class Main { + boolean find(List data) { + for(String e : data) { + String trimmed = e.trim(); + if(trimmed.startsWith("xyz")) { + return true; + } + } + return false; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeNoneMatch.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeNoneMatch.java new file mode 100644 index 000000000000..5bce7626f4da --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeNoneMatch.java @@ -0,0 +1,14 @@ +// "Replace with noneMatch()" "true" + +public class Main { + boolean find(String[][] data) { + for(String[] arr : data) { + for(String str : arr) { + if(str.startsWith("xyz")) { + return false; + } + } + } + return true; + } +}