From 5f609b11bce2eaa0db0090e8ce9e62a797938800 Mon Sep 17 00:00:00 2001 From: Max Medvedev Date: Tue, 30 Apr 2013 12:32:04 +0400 Subject: [PATCH] IDEA-106374 Overriding method can change return type generics --- .../groovy/annotator/GroovyAnnotator.java | 58 +++++++++------ .../GroovyHighlightingTest.groovy | 71 +++++++++++++++++++ 2 files changed, 108 insertions(+), 21 deletions(-) diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java index 7cb03049ed19..4f71412c9309 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java @@ -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 diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/GroovyHighlightingTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/GroovyHighlightingTest.groovy index dcdf59c935ea..70a3b260972d 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/GroovyHighlightingTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/GroovyHighlightingTest.groovy @@ -1262,4 +1262,75 @@ class D { def foo(E t) {} }''') } + + void testOverriddenReturnType0() { + myFixture.addClass('class Base{}') + myFixture.addClass('class Inh extends Base{}') + testHighlighting('''\ +class A { + List foo() {} +} + +class B extends A { + List foo() {} //correct +} +''') + } + + void testOverriddenReturnType1() { + myFixture.addClass('class Base extends SuperBase {}') + myFixture.addClass('class Inh extends Base{}') + testHighlighting('''\ +class A { + List foo() {} +} + +class B extends A { + Collection foo() {} +} +''') + } + + void testOverriddenReturnType2() { + myFixture.addClass('class Base extends SuperBase {}') + myFixture.addClass('class Inh extends Base{}') + testHighlighting('''\ +class A { + List foo() {} +} + +class B extends A { + int foo() {} +} +''') + } + + void testOverriddenReturnType3() { + myFixture.addClass('class Base extends SuperBase {}') + myFixture.addClass('class Inh extends Base{}') + testHighlighting('''\ +class A { + Base[] foo() {} +} + +class B extends A { + Inh[] 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() {} +} +''') + } + } \ No newline at end of file