From 7cb4480fd1e1232cfae16cd0c528df671f889c11 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Fri, 30 Sep 2016 10:23:12 +0700 Subject: [PATCH] StreamApiMigrationInspection: references to non-final variables in intermediate operations were incorrectly handled sometimes --- .../StreamApiMigrationInspection.java | 20 +++++++++++++------ .../beforeAnyMatchReferNonFinal.java | 17 ++++++++++++++++ 2 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchReferNonFinal.java 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 f95d6aef1b42..0a5f773c4065 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 @@ -110,6 +110,12 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo return value != null && ReferencesSearch.search(variable, new LocalSearchScope(value)).findFirst() != null; } + static boolean isReferencedInOperations(PsiElement element, List operations) { + return ReferencesSearch + .search(element, new LocalSearchScope(operations.stream().map(Operation::getExpression).toArray(PsiElement[]::new))) + .findFirst() != null; + } + @Nullable static PsiReturnStatement getNextReturnStatement(PsiStatement statement) { PsiElement nextStatement = PsiTreeUtil.skipSiblingsForward(statement, PsiWhiteSpace.class, PsiComment.class); @@ -211,13 +217,9 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo if(!(operand instanceof PsiReferenceExpression)) return null; PsiElement element = ((PsiReferenceExpression)operand).resolve(); - // the referred variable is the same as non-final variable - if(!(element instanceof PsiLocalVariable) || !variables.contains(element)) return null; + // the referred variable is the same as non-final variable and not used in intermediate operations + if(!(element instanceof PsiLocalVariable) || !variables.contains(element) || isReferencedInOperations(element, operations)) return null; - // the referred variable is not used in intermediate operations - for(Operation operation : operations) { - if(ReferencesSearch.search(element, new LocalSearchScope(operation.getExpression())).findFirst() != null) return null; - } return (PsiLocalVariable)element; } @@ -543,6 +545,12 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo if (nonFinalVariables.isEmpty() && tb.getSingleStatement() instanceof PsiReturnStatement) { handleSingleReturn(statement, tb, operations); } + // Intermediate ops should not refer to non-final variables + if (StreamEx.of(operations).map(Operation::getExpression) + .flatCollection(expr -> PsiTreeUtil.collectElementsOfType(expr, PsiReferenceExpression.class)) + .map(PsiReferenceExpression::resolve).anyMatch(nonFinalVariables::contains)) { + return; + } PsiStatement[] statements = tb.getStatements(); if (statements.length == 2) { PsiStatement breakStatement = statements[1]; diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchReferNonFinal.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchReferNonFinal.java new file mode 100644 index 000000000000..800a3fb56dd0 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeAnyMatchReferNonFinal.java @@ -0,0 +1,17 @@ +// "Replace with anyMatch()" "false" + +import java.util.List; + +public class Main { + public void process(List strings) { + String item = "FOO"; + for(String str : strings) { + String trimmed = str.trim(); + if(trimmed.equals(item)) { + item = "BAR"; + break; + } + } + System.out.println(item); + } +}