IDEA-106374 Overriding method can change return type generics

This commit is contained in:
Max Medvedev
2013-04-30 16:27:13 +04:00
parent 99a2216bad
commit 5f609b11bc
2 changed files with 108 additions and 21 deletions
@@ -895,13 +895,42 @@ public class GroovyAnnotator extends GroovyElementVisitor {
}
@Nullable
private static String checkSuperMethodSignature(PsiMethod superMethod,
MethodSignatureBackedByPsiMethod superMethodSignature,
PsiType superReturnType,
PsiMethod method,
MethodSignatureBackedByPsiMethod methodSignature,
PsiType returnType) {
if (superReturnType == null) return null;
private static String checkSuperMethodSignature(@NotNull PsiMethod superMethod,
@NotNull MethodSignatureBackedByPsiMethod superMethodSignature,
@NotNull PsiType superReturnType,
@NotNull PsiMethod method,
@NotNull MethodSignatureBackedByPsiMethod methodSignature,
@NotNull PsiType returnType) {
PsiType substitutedSuperReturnType = substituteSuperReturnType(superMethodSignature, methodSignature, superReturnType);
if (returnType.equals(substitutedSuperReturnType)) return null;
final PsiType rawReturnType = TypeConversionUtil.erasure(returnType);
final PsiType rawSuperReturnType = TypeConversionUtil.erasure(substitutedSuperReturnType);
if (returnType instanceof PsiClassType && substitutedSuperReturnType instanceof PsiClassType) {
if (TypeConversionUtil.isAssignable(rawSuperReturnType, rawReturnType)) {
return null;
}
}
else if (returnType instanceof PsiArrayType && superReturnType instanceof PsiArrayType) {
if (rawReturnType.equals(rawSuperReturnType)) {
return null;
}
}
String qName = getQNameOfMember(method);
String baseQName = getQNameOfMember(superMethod);
final String presentation = returnType.getCanonicalText() + " " + GroovyPresentationUtil.getSignaturePresentation(methodSignature);
final String basePresentation =
superReturnType.getCanonicalText() + " " + GroovyPresentationUtil.getSignaturePresentation(superMethodSignature);
return GroovyBundle.message("return.type.is.incompatible", presentation, qName, basePresentation, baseQName);
}
@NotNull
private static PsiType substituteSuperReturnType(@NotNull MethodSignatureBackedByPsiMethod superMethodSignature,
@NotNull MethodSignatureBackedByPsiMethod methodSignature,
@NotNull PsiType superReturnType) {
PsiType substitutedSuperReturnType;
if (!superMethodSignature.isRaw() && superMethodSignature.equals(methodSignature)) { //see 8.4.5
PsiSubstitutor unifyingSubstitutor = MethodSignatureUtil.getSuperMethodSignatureSubstitutor(methodSignature,
@@ -913,20 +942,7 @@ public class GroovyAnnotator extends GroovyElementVisitor {
else {
substitutedSuperReturnType = TypeConversionUtil.erasure(superReturnType);
}
if (returnType.equals(substitutedSuperReturnType)) return null;
if (!(returnType instanceof PsiPrimitiveType) &&
substitutedSuperReturnType.getDeepComponentType() instanceof PsiClassType &&
TypeConversionUtil.isAssignable(substitutedSuperReturnType, returnType)) {
return null;
}
String qName = getQNameOfMember(method);
String baseQName = getQNameOfMember(superMethod);
final String presentation = returnType.getCanonicalText() + " " + GroovyPresentationUtil.getSignaturePresentation(methodSignature);
final String basePresentation =
superReturnType.getCanonicalText() + " " + GroovyPresentationUtil.getSignaturePresentation(superMethodSignature);
return GroovyBundle.message("return.type.is.incompatible", presentation, qName, basePresentation, baseQName);
return substitutedSuperReturnType;
}
@NotNull
@@ -1262,4 +1262,75 @@ class D<T, E> {
def foo(E t) {}
}''')
}
void testOverriddenReturnType0() {
myFixture.addClass('class Base{}')
myFixture.addClass('class Inh extends Base{}')
testHighlighting('''\
class A {
List<Base> foo() {}
}
class B extends A {
List<Inh> foo() {} //correct
}
''')
}
void testOverriddenReturnType1() {
myFixture.addClass('class Base extends SuperBase {}')
myFixture.addClass('class Inh extends Base{}')
testHighlighting('''\
class A {
List<Base> foo() {}
}
class B extends A {
<error>Collection<Base></error> foo() {}
}
''')
}
void testOverriddenReturnType2() {
myFixture.addClass('class Base extends SuperBase {}')
myFixture.addClass('class Inh extends Base{}')
testHighlighting('''\
class A {
List<Base> foo() {}
}
class B extends A {
<error>int</error> foo() {}
}
''')
}
void testOverriddenReturnType3() {
myFixture.addClass('class Base extends SuperBase {}')
myFixture.addClass('class Inh extends Base{}')
testHighlighting('''\
class A {
Base[] foo() {}
}
class B extends A {
<error>Inh[]</error> foo() {}
}
''')
}
void testOverriddenReturnType4() {
myFixture.addClass('class Base extends SuperBase {}')
myFixture.addClass('class Inh extends Base{}')
testHighlighting('''\
class A {
Base[] foo() {}
}
class B extends A {
Base[] foo() {}
}
''')
}
}