From f8f7f60fd6edad7f8b7b362f383173a4afae8ee4 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Fri, 17 Feb 2017 16:43:41 +0700 Subject: [PATCH] IDEA-167942 Migrate to simplified collection factories (JEP 269): support explicit addition chain --- .../Java9CollectionFactoryInspection.java | 63 ++++++++++++++++--- .../afterArrayListExplicit.java | 10 +++ .../afterHashSetExplicit.java | 11 ++++ .../afterHashSetExplicitReusedVar.java | 10 +++ .../beforeArrayListExplicit.java | 12 ++++ .../beforeArrayListExplicitMutableReused.java | 12 ++++ .../beforeHashSetExplicit.java | 14 +++++ .../beforeHashSetExplicitReusedVar.java | 13 ++++ 8 files changed, 137 insertions(+), 8 deletions(-) create mode 100644 java/java-tests/testData/inspection/java9CollectionFactory/afterArrayListExplicit.java create mode 100644 java/java-tests/testData/inspection/java9CollectionFactory/afterHashSetExplicit.java create mode 100644 java/java-tests/testData/inspection/java9CollectionFactory/afterHashSetExplicitReusedVar.java create mode 100644 java/java-tests/testData/inspection/java9CollectionFactory/beforeArrayListExplicit.java create mode 100644 java/java-tests/testData/inspection/java9CollectionFactory/beforeArrayListExplicitMutableReused.java create mode 100644 java/java-tests/testData/inspection/java9CollectionFactory/beforeHashSetExplicit.java create mode 100644 java/java-tests/testData/inspection/java9CollectionFactory/beforeHashSetExplicitReusedVar.java diff --git a/java/java-impl/src/com/intellij/codeInspection/java19api/Java9CollectionFactoryInspection.java b/java/java-impl/src/com/intellij/codeInspection/java19api/Java9CollectionFactoryInspection.java index 21a603a4b821..57cb4b8a45f0 100644 --- a/java/java-impl/src/com/intellij/codeInspection/java19api/Java9CollectionFactoryInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/java19api/Java9CollectionFactoryInspection.java @@ -21,22 +21,22 @@ import com.intellij.codeInspection.ProblemsHolder; import com.intellij.codeInspection.ex.BaseLocalInspectionTool; import com.intellij.openapi.project.Project; import com.intellij.psi.*; +import com.intellij.psi.controlFlow.DefUseUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; +import com.intellij.util.containers.ContainerUtil; import com.siyeh.ig.callMatcher.CallMapper; import com.siyeh.ig.callMatcher.CallMatcher; import com.siyeh.ig.psiutils.ClassUtils; import com.siyeh.ig.psiutils.CommentTracker; +import com.siyeh.ig.psiutils.ConstructionUtils; import com.siyeh.ig.psiutils.MethodCallUtils; import one.util.streamex.StreamEx; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; +import java.util.*; import static com.intellij.util.ObjectUtils.tryCast; @@ -88,12 +88,12 @@ public class Java9CollectionFactoryInspection extends BaseLocalInspectionTool { static class PrepopulatedCollectionModel { final List myContent; - final List myStatementsToDelete; + final List myElementsToDelete; final String myType; - PrepopulatedCollectionModel(List content, List delete, String type) { + PrepopulatedCollectionModel(List content, List delete, String type) { myContent = content; - myStatementsToDelete = delete; + myElementsToDelete = delete; myType = type; } @@ -115,6 +115,12 @@ public class Java9CollectionFactoryInspection extends BaseLocalInspectionTool { if(listDefinition instanceof PsiNewExpression) { return fromNewExpression((PsiNewExpression)listDefinition, "List", CommonClassNames.JAVA_UTIL_ARRAY_LIST); } + if (listDefinition instanceof PsiReferenceExpression) { + PsiLocalVariable variable = tryCast(((PsiReferenceExpression)listDefinition).resolve(), PsiLocalVariable.class); + if (variable != null) { + return fromVariable(variable, listDefinition, "List", CommonClassNames.JAVA_UTIL_ARRAY_LIST); + } + } return null; } @@ -133,6 +139,47 @@ public class Java9CollectionFactoryInspection extends BaseLocalInspectionTool { } } } + if (setDefinition instanceof PsiReferenceExpression) { + PsiLocalVariable variable = tryCast(((PsiReferenceExpression)setDefinition).resolve(), PsiLocalVariable.class); + if (variable != null) { + return fromVariable(variable, setDefinition, "Set", CommonClassNames.JAVA_UTIL_HASH_SET); + } + } + return null; + } + + @Nullable + private static PrepopulatedCollectionModel fromVariable(PsiLocalVariable variable, + PsiExpression expression, + String typeName, String collectionClass) { + PsiCodeBlock block = PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class); + PsiDeclarationStatement declaration = PsiTreeUtil.getParentOfType(variable, PsiDeclarationStatement.class); + if (block == null || declaration == null) return null; + PsiElement[] defs = DefUseUtil.getDefs(block, variable, expression); + if (defs.length == 1 && defs[0] == variable) { + PsiExpression initializer = variable.getInitializer(); + if (!ConstructionUtils.isEmptyCollectionInitializer(initializer)) return null; + PsiClassType type = tryCast(initializer.getType(), PsiClassType.class); + if (type == null || !type.rawType().equalsToText(collectionClass)) return null; + Set refs = ContainerUtil.set(DefUseUtil.getRefs(block, variable, initializer)); + refs.remove(expression); + PsiStatement cur = declaration; + List contents = new ArrayList<>(); + List elementsToRemove = new ArrayList<>(); + elementsToRemove.add(initializer); + while (true) { + cur = tryCast(PsiTreeUtil.skipSiblingsForward(cur, PsiComment.class, PsiWhiteSpace.class), PsiStatement.class); + if (PsiTreeUtil.isAncestor(cur, expression, false)) break; + if (!(cur instanceof PsiExpressionStatement)) return null; + PsiMethodCallExpression call = tryCast(((PsiExpressionStatement)cur).getExpression(), PsiMethodCallExpression.class); + if (!COLLECTION_ADD.test(call)) return null; + if (!refs.remove(call.getMethodExpression().getQualifierExpression())) return null; + contents.add(call.getArgumentList().getExpressions()[0]); + elementsToRemove.add(cur); + } + if (!refs.isEmpty()) return null; + return new PrepopulatedCollectionModel(contents, elementsToRemove, typeName); + } return null; } @@ -212,7 +259,7 @@ public class Java9CollectionFactoryInspection extends BaseLocalInspectionTool { PrepopulatedCollectionModel model = MAPPER.mapFirst(call); if(model == null) return; CommentTracker ct = new CommentTracker(); - model.myStatementsToDelete.forEach(ct::delete); + model.myElementsToDelete.forEach(ct::delete); ct.replaceAndRestoreComments(call, StreamEx.of(model.myContent).map(ct::text) .joining(",", "java.util." + model.myType + ".of(", ")")); } diff --git a/java/java-tests/testData/inspection/java9CollectionFactory/afterArrayListExplicit.java b/java/java-tests/testData/inspection/java9CollectionFactory/afterArrayListExplicit.java new file mode 100644 index 000000000000..29e332c90bcc --- /dev/null +++ b/java/java-tests/testData/inspection/java9CollectionFactory/afterArrayListExplicit.java @@ -0,0 +1,10 @@ +// "Replace with 'List.of' call" "true" +import java.util.*; + +public class Test { + public void testList() { + List list; + list = List.of(1, 2); + System.out.println(list); + } +} diff --git a/java/java-tests/testData/inspection/java9CollectionFactory/afterHashSetExplicit.java b/java/java-tests/testData/inspection/java9CollectionFactory/afterHashSetExplicit.java new file mode 100644 index 000000000000..81b7dd158366 --- /dev/null +++ b/java/java-tests/testData/inspection/java9CollectionFactory/afterHashSetExplicit.java @@ -0,0 +1,11 @@ +// "Replace with 'Set.of' call" "true" +import java.util.*; + +public class Test { + private static final Set MY_SET; + + static { + Set set; + MY_SET = Set.of("foo", "bar", "xyz"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/java9CollectionFactory/afterHashSetExplicitReusedVar.java b/java/java-tests/testData/inspection/java9CollectionFactory/afterHashSetExplicitReusedVar.java new file mode 100644 index 000000000000..31d4eb7e0032 --- /dev/null +++ b/java/java-tests/testData/inspection/java9CollectionFactory/afterHashSetExplicitReusedVar.java @@ -0,0 +1,10 @@ +// "Replace with 'Set.of' call" "true" +import java.util.*; + +public class Test { + public void test2() { + Set set; + set = Set.of("foo", "bar", "xyz"); + System.out.println(set); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/java9CollectionFactory/beforeArrayListExplicit.java b/java/java-tests/testData/inspection/java9CollectionFactory/beforeArrayListExplicit.java new file mode 100644 index 000000000000..07f26eb553cf --- /dev/null +++ b/java/java-tests/testData/inspection/java9CollectionFactory/beforeArrayListExplicit.java @@ -0,0 +1,12 @@ +// "Replace with 'List.of' call" "true" +import java.util.*; + +public class Test { + public void testList() { + List list = new ArrayList<>(); + list.add(1); + list.add(2); + list = Collections.unmodifiableList(list); + System.out.println(list); + } +} diff --git a/java/java-tests/testData/inspection/java9CollectionFactory/beforeArrayListExplicitMutableReused.java b/java/java-tests/testData/inspection/java9CollectionFactory/beforeArrayListExplicitMutableReused.java new file mode 100644 index 000000000000..8021c2d0e147 --- /dev/null +++ b/java/java-tests/testData/inspection/java9CollectionFactory/beforeArrayListExplicitMutableReused.java @@ -0,0 +1,12 @@ +// "Replace with 'List.of' call" "false" +import java.util.*; + +public class Test { + public void testList() { + List list = new ArrayList<>(); + list.add(1); + list.add(2); + List list2 = Collections.unmodifiableList(list); + System.out.println(list); // mutable list is reused here + } +} diff --git a/java/java-tests/testData/inspection/java9CollectionFactory/beforeHashSetExplicit.java b/java/java-tests/testData/inspection/java9CollectionFactory/beforeHashSetExplicit.java new file mode 100644 index 000000000000..568f6721d83a --- /dev/null +++ b/java/java-tests/testData/inspection/java9CollectionFactory/beforeHashSetExplicit.java @@ -0,0 +1,14 @@ +// "Replace with 'Set.of' call" "true" +import java.util.*; + +public class Test { + private static final Set MY_SET; + + static { + Set set = new HashSet<>(); + set.add("foo"); + set.add("bar"); + set.add("xyz"); + MY_SET = Collections.unmodifiableSet(set); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/java9CollectionFactory/beforeHashSetExplicitReusedVar.java b/java/java-tests/testData/inspection/java9CollectionFactory/beforeHashSetExplicitReusedVar.java new file mode 100644 index 000000000000..789fd696caae --- /dev/null +++ b/java/java-tests/testData/inspection/java9CollectionFactory/beforeHashSetExplicitReusedVar.java @@ -0,0 +1,13 @@ +// "Replace with 'Set.of' call" "true" +import java.util.*; + +public class Test { + public void test2() { + Set set = new HashSet<>(); + set.add("foo"); + set.add("bar"); + set.add("xyz"); + set = Collections.unmodifiableSet(set); + System.out.println(set); + } +} \ No newline at end of file