type migration: migrate getter return type and setter parameter anyway if initial- and target-types are assignable IDEA-119901

This commit is contained in:
Dmitry Batkovich
2015-12-04 15:07:43 +03:00
parent f1c89da84d
commit d58cff41fa
10 changed files with 164 additions and 9 deletions
@@ -15,6 +15,7 @@
*/
package com.intellij.refactoring.typeMigration;
import com.intellij.codeInsight.generation.GetterSetterPrototypeProvider;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
@@ -22,10 +23,7 @@ import com.intellij.psi.*;
import com.intellij.psi.impl.PsiSubstitutorImpl;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.psi.util.*;
import com.intellij.refactoring.typeMigration.usageInfo.TypeMigrationUsageInfo;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NotNull;
@@ -83,7 +81,7 @@ class TypeMigrationStatementProcessor extends JavaRecursiveElementVisitor {
break;
case TypeInfection.LEFT_INFECTED:
myLabeler.migrateExpressionType(rExpression, ltype, myStatement, TypeConversionUtil.isAssignable(ltype, rtype), true);
myLabeler.migrateExpressionType(rExpression, ltype, myStatement, TypeConversionUtil.isAssignable(ltype, rtype) && !isSetter(expression), true);
break;
case TypeInfection.RIGHT_INFECTED:
@@ -178,7 +176,7 @@ class TypeMigrationStatementProcessor extends JavaRecursiveElementVisitor {
}
@Override
public void visitReturnStatement(PsiReturnStatement statement) { // has to change method return type corresponding to new value type
public void visitReturnStatement(final PsiReturnStatement statement) { // has to change method return type corresponding to new value type
super.visitReturnStatement(statement);
final PsiElement method = PsiTreeUtil.getParentOfType(statement, PsiMethod.class, PsiLambdaExpression.class);
@@ -191,9 +189,8 @@ class TypeMigrationStatementProcessor extends JavaRecursiveElementVisitor {
}
final PsiType returnType = ((PsiMethod)method).getReturnType();
final PsiType valueType = myTypeEvaluator.evaluateType(value);
if (returnType != null && valueType != null) {
if (!myLabeler.addMigrationRoot(method, valueType, myStatement, TypeConversionUtil.isAssignable(returnType, valueType), true, true)
if (!myLabeler.addMigrationRoot(method, valueType, myStatement, TypeConversionUtil.isAssignable(returnType, valueType) && !isGetter(value, method), true, true)
&& TypeMigrationLabeler.typeContainsTypeParameters(returnType)) {
value.accept(this);
}
@@ -594,4 +591,41 @@ class TypeMigrationStatementProcessor extends JavaRecursiveElementVisitor {
return (left.isChanged() ? 1 : 0) + (right.isChanged() ? 2 : 0);
}
}
private static boolean isSetter(PsiAssignmentExpression expression) {
final PsiExpression lExpression = expression.getLExpression();
if (lExpression instanceof PsiReferenceExpression) {
final PsiElement resolved = ((PsiReferenceExpression)lExpression).resolve();
if (resolved instanceof PsiField) {
PsiField field = (PsiField) resolved;
final NavigatablePsiElement containingMethod = PsiTreeUtil.getParentOfType(expression, PsiMethod.class, PsiLambdaExpression.class);
if (containingMethod instanceof PsiMethod) {
final PsiMethod setter = PropertyUtil.findPropertySetter(field.getContainingClass(), field.getName(), field.hasModifierProperty(PsiModifier.STATIC), false);
if (containingMethod.isEquivalentTo(setter)) {
return true;
}
}
}
}
return false;
}
private static boolean isGetter(PsiExpression returnValue, PsiElement containingMethod) {
if (returnValue instanceof PsiReferenceExpression) {
final PsiElement resolved = ((PsiReferenceExpression)returnValue).resolve();
if (resolved instanceof PsiField) {
PsiField field = (PsiField)resolved;
final boolean isStatic = field.hasModifierProperty(PsiModifier.STATIC);
final PsiMethod[] getters = GetterSetterPrototypeProvider.findGetters(field.getContainingClass(), field.getName(), isStatic);
if (getters != null) {
for (PsiMethod getter : getters) {
if (containingMethod.isEquivalentTo(getter)) {
return true;
}
}
}
}
}
return false;
}
}
@@ -906,6 +906,14 @@ public class TypeMigrationTest extends TypeMigrationTestBase {
doTestFirstParamType("meth", myFactory.createTypeFromText("java.lang.String", null), myFactory.createTypeFromText("java.lang.Long", null));
}
public void testAssignableGetter() {
doTestFieldType("foo", "Test", PsiType.LONG, PsiType.INT);
}
public void testAssignableSetter() {
doTestFieldType("foo", "Test", PsiType.INT, PsiType.LONG);
}
private void doTestForeachParameter(final PsiType rootType, final PsiType migrationType) {
start(new RulesProvider() {
@Override
@@ -0,0 +1,15 @@
Types:
PsiField:foo : int
PsiLocalVariable:someNumber1 : int
PsiMethod:getFoo : int
PsiMethodCallExpression:someClass.getFoo() : int
PsiParameter:foo : int
PsiReferenceExpression:foo : int
PsiReferenceExpression:foo : int
PsiReferenceExpression:someNumber1 : int
PsiReferenceExpression:this.foo : int
Conversions:
New expression type changes:
Fails:
@@ -0,0 +1,20 @@
public class Test {
private int foo;
public int getFoo() {
return foo;
}
public void setFoo(int foo) {
this.foo = foo;
}
static void m(Test someClass) {
long someNumber = someClass.getFoo();
System.out.println(someNumber + 10);
int someNumber1 = 1123;
someClass.setFoo(someNumber1);
}
}
@@ -0,0 +1,20 @@
public class Test {
private long foo;
public long getFoo() {
return foo;
}
public void setFoo(long foo) {
this.foo = foo;
}
static void m(Test someClass) {
long someNumber = someClass.getFoo();
System.out.println(someNumber + 10);
long someNumber1 = 1123;
someClass.setFoo(someNumber1);
}
}
@@ -0,0 +1,17 @@
Types:
PsiField:foo : long
PsiLocalVariable:someNumber : long
PsiMethod:getFoo : long
PsiMethodCallExpression:someClass.getFoo() : long
PsiParameter:foo : long
PsiReferenceExpression:foo : long
PsiReferenceExpression:foo : long
PsiReferenceExpression:someNumber : long
PsiReferenceExpression:this.foo : long
Conversions:
someNumber + 10 -> $
someNumber1 -> $
New expression type changes:
Fails:
@@ -0,0 +1,20 @@
public class Test {
private long foo;
public long getFoo() {
return foo;
}
public void setFoo(long foo) {
this.foo = foo;
}
static void m(Test someClass) {
long someNumber = someClass.getFoo();
System.out.println(someNumber + 10);
int someNumber1 = 1123;
someClass.setFoo(someNumber1);
}
}
@@ -0,0 +1,20 @@
public class Test {
private int foo;
public int getFoo() {
return foo;
}
public void setFoo(int foo) {
this.foo = foo;
}
static void m(Test someClass) {
int someNumber = someClass.getFoo();
System.out.println(someNumber + 10);
int someNumber1 = 1123;
someClass.setFoo(someNumber1);
}
}
@@ -1,5 +1,6 @@
Types:
PsiField:foo : int
PsiMethod:getFoo : int
PsiParameter:foo : int
PsiReferenceExpression:foo : int
PsiReferenceExpression:foo : int
@@ -2,7 +2,7 @@ class Test {
private int foo;
private long bar;
public long getFoo() {
public int getFoo() {
return foo;
}