IDEA-167942 Migrate to simplified collection factories (JEP 269): support explicit addition chain

This commit is contained in:
Tagir Valeev
2017-02-27 10:35:34 +07:00
parent 2dda8819cc
commit f8f7f60fd6
8 changed files with 137 additions and 8 deletions
@@ -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<PsiExpression> myContent;
final List<PsiStatement> myStatementsToDelete;
final List<PsiElement> myElementsToDelete;
final String myType;
PrepopulatedCollectionModel(List<PsiExpression> content, List<PsiStatement> delete, String type) {
PrepopulatedCollectionModel(List<PsiExpression> content, List<PsiElement> 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<PsiElement> refs = ContainerUtil.set(DefUseUtil.getRefs(block, variable, initializer));
refs.remove(expression);
PsiStatement cur = declaration;
List<PsiExpression> contents = new ArrayList<>();
List<PsiElement> 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(", ")"));
}
@@ -0,0 +1,10 @@
// "Replace with 'List.of' call" "true"
import java.util.*;
public class Test {
public void testList() {
List<Integer> list;
list = List.of(1, 2);
System.out.println(list);
}
}
@@ -0,0 +1,11 @@
// "Replace with 'Set.of' call" "true"
import java.util.*;
public class Test {
private static final Set<String> MY_SET;
static {
Set<String> set;
MY_SET = Set.of("foo", "bar", "xyz");
}
}
@@ -0,0 +1,10 @@
// "Replace with 'Set.of' call" "true"
import java.util.*;
public class Test {
public void test2() {
Set<String> set;
set = Set.of("foo", "bar", "xyz");
System.out.println(set);
}
}
@@ -0,0 +1,12 @@
// "Replace with 'List.of' call" "true"
import java.util.*;
public class Test {
public void testList() {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list = Collections.unmodifi<caret>ableList(list);
System.out.println(list);
}
}
@@ -0,0 +1,12 @@
// "Replace with 'List.of' call" "false"
import java.util.*;
public class Test {
public void testList() {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
List<Integer> list2 = Collections.unmodifi<caret>ableList(list);
System.out.println(list); // mutable list is reused here
}
}
@@ -0,0 +1,14 @@
// "Replace with 'Set.of' call" "true"
import java.util.*;
public class Test {
private static final Set<String> MY_SET;
static {
Set<String> set = new HashSet<>();
set.add("foo");
set.add("bar");
set.add("xyz");
MY_SET = Collections.unmodif<caret>iableSet(set);
}
}
@@ -0,0 +1,13 @@
// "Replace with 'Set.of' call" "true"
import java.util.*;
public class Test {
public void test2() {
Set<String> set = new HashSet<>();
set.add("foo");
set.add("bar");
set.add("xyz");
set = Collections.unmodif<caret>iableSet(set);
System.out.println(set);
}
}