type migration: allow to migrate elements with type parameters if root contains these type parameters (IDEA-147944)

This commit is contained in:
Dmitry Batkovich
2015-12-09 19:29:28 +03:00
parent c38a8f8c74
commit 0750185bef
6 changed files with 66 additions and 11 deletions
@@ -25,6 +25,7 @@ import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.impl.source.PsiClassReferenceType;
import com.intellij.psi.impl.source.PsiImmediateClassType;
import com.intellij.psi.javadoc.PsiDocTagValue;
import com.intellij.psi.search.PsiSearchScopeUtil;
@@ -37,11 +38,9 @@ import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.refactoring.typeMigration.usageInfo.OverridenUsageInfo;
import com.intellij.refactoring.typeMigration.usageInfo.OverriderUsageInfo;
import com.intellij.refactoring.typeMigration.usageInfo.TypeMigrationUsageInfo;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.Query;
import com.intellij.util.*;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.graph.DFSTBuilder;
import com.intellij.util.graph.GraphGenerator;
@@ -468,7 +467,15 @@ public class TypeMigrationLabeler {
type = userDefinedType ? type : TypeEvaluator.substituteType(type, originalType, isContraVariantPosition);
if (!userDefinedType) {
if (typeContainsTypeParameters(originalType)) return false;
final Set<PsiTypeParameter> collector;
if (originalType instanceof PsiClassReferenceType) {
collector = new HashSet<PsiTypeParameter>();
final PsiJavaCodeReferenceElement reference = ((PsiClassReferenceType)originalType).getReference();
RefactoringUtil.collectTypeParameters(collector, reference);
} else {
collector = Collections.emptySet();
}
if (typeContainsTypeParameters(originalType, collector)) return false;
}
if (type instanceof PsiCapturedWildcardType) {
@@ -571,14 +578,17 @@ public class TypeMigrationLabeler {
}
}
static boolean typeContainsTypeParameters(PsiType originalType) {
static boolean typeContainsTypeParameters(@Nullable PsiType originalType, @NotNull Set<PsiTypeParameter> excluded) {
if (originalType instanceof PsiClassType) {
final PsiClassType psiClassType = (PsiClassType)originalType;
if (psiClassType.resolve() instanceof PsiTypeParameter) {
return true;
}
for (PsiType paramType : psiClassType.getParameters()) {
if (paramType instanceof PsiClassType && ((PsiClassType)paramType).resolve() instanceof PsiTypeParameter) return true;
if (paramType instanceof PsiClassType) {
final PsiClass resolved = ((PsiClassType)paramType).resolve();
if (resolved instanceof PsiTypeParameter && !excluded.contains(resolved)) return true;
}
}
}
return false;
@@ -191,7 +191,7 @@ class TypeMigrationStatementProcessor extends JavaRecursiveElementVisitor {
final PsiType valueType = myTypeEvaluator.evaluateType(value);
if (returnType != null && valueType != null) {
if (!myLabeler.addMigrationRoot(method, valueType, myStatement, TypeConversionUtil.isAssignable(returnType, valueType) && !isGetter(value, method), true, true)
&& TypeMigrationLabeler.typeContainsTypeParameters(returnType)) {
&& TypeMigrationLabeler.typeContainsTypeParameters(returnType, Collections.<PsiTypeParameter>emptySet())) {
value.accept(this);
}
}
@@ -862,8 +862,8 @@ public class TypeMigrationTest extends TypeMigrationTestBase {
}
public void testT139() {
doTestForeachParameter(myFactory.createTypeFromText("java.lang.String", null),
myFactory.createTypeFromText("java.lang.Integer", null));
doTestForeachParameter(
myFactory.createTypeFromText("java.lang.Integer", null));
}
public void testT140() {
@@ -914,7 +914,32 @@ public class TypeMigrationTest extends TypeMigrationTestBase {
doTestFieldType("foo", "Test", PsiType.INT, PsiType.LONG);
}
private void doTestForeachParameter(final PsiType rootType, final PsiType migrationType) {
public void testMethodReturnTypeWithTypeParameter() {
doTestReturnType("meth", myFactory.createTypeFromText("java.util.List<T>", null));
}
private void doTestReturnType(final String methodName, final PsiType migrationType) {
start(new RulesProvider() {
@Override
public TypeMigrationRules provide() throws Exception {
final TypeMigrationRules rules = new TypeMigrationRules();
rules.setMigrationRootType(migrationType);
return rules;
}
@Override
public PsiElement victims(PsiClass aClass) {
for (PsiMethod method : PsiTreeUtil.findChildrenOfType(aClass, PsiMethod.class)) {
if (methodName.equals(method.getName())) {
return method;
}
}
throw new AssertionError();
}
});
}
private void doTestForeachParameter(final PsiType migrationType) {
start(new RulesProvider() {
@Override
public TypeMigrationRules provide() {
@@ -0,0 +1,9 @@
Types:
PsiMethod:meth : java.util.List<T>
PsiParameter:p : java.util.List<T>
PsiReferenceExpression:p : java.util.List<T>
Conversions:
New expression type changes:
Fails:
@@ -0,0 +1,6 @@
import java.util.List;
import java.util.Set;
public class Test {
public <T> List<T> meth(List<T> p) { return p; }
}
@@ -0,0 +1,5 @@
import java.util.Set;
public class Test {
public <T> Set<T> meth(Set<T> p) { return p; }
}