### Eclipse Workspace Patch 1.0 #P org.eclipse.jdt.core.tests.compiler Index: src/org/eclipse/jdt/core/tests/compiler/regression/GenericTypeTest.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericTypeTest.java,v retrieving revision 1.617 diff -u -r1.617 GenericTypeTest.java --- src/org/eclipse/jdt/core/tests/compiler/regression/GenericTypeTest.java 25 Apr 2007 13:38:39 -0000 1.617 +++ src/org/eclipse/jdt/core/tests/compiler/regression/GenericTypeTest.java 26 Apr 2007 18:42:09 -0000 @@ -31605,12 +31605,7 @@ "}", // ================= }, "----------\n" + - "1. ERROR in X.java (at line 7)\n" + - " abstract class GLinkElementView extends AbstractLinkView {}\n" + - " ^^^^^^^^^^^^^^^^\n" + - "The return type is incompatible with ILinkViewElement.getViewer(), AbstractEditPart.getViewer(), AbstractLinkView.getViewer()\n" + - "----------\n" + - "2. ERROR in X.java (at line 11)\n" + + "1. ERROR in X.java (at line 11)\n" + " public SheetViewer getViewer() { return null; } \n" + " ^^^^^^^^^^^\n" + "The return type is incompatible with AbstractEditPart.getViewer()\n" + Index: src/org/eclipse/jdt/core/tests/compiler/regression/MethodVerifyTest.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/MethodVerifyTest.java,v retrieving revision 1.126 diff -u -r1.126 MethodVerifyTest.java --- src/org/eclipse/jdt/core/tests/compiler/regression/MethodVerifyTest.java 24 Apr 2007 15:14:02 -0000 1.126 +++ src/org/eclipse/jdt/core/tests/compiler/regression/MethodVerifyTest.java 26 Apr 2007 18:42:10 -0000 @@ -708,27 +708,16 @@ new String[] { "A.java", "abstract class A implements I {}\n" + - "interface I extends J { Object foo(); }\n" + + "interface I extends J { Object foo(); }\n" + // with javac only this type gets an error "interface J { String foo(); }\n", "X.java", "abstract class X2 extends A implements J {}\n" }, "----------\n" + - "1. ERROR in A.java (at line 1)\n" + - " abstract class A implements I {}\n" + - " ^\n" + - "The return type is incompatible with J.foo(), I.foo()\n" + - "----------\n" + - "2. ERROR in A.java (at line 2)\n" + + "1. ERROR in A.java (at line 2)\n" + " interface I extends J { Object foo(); }\n" + " ^^^^^^\n" + "The return type is incompatible with J.foo()\n" + - "----------\n" + - "----------\n" + - "1. ERROR in X.java (at line 1)\n" + - " abstract class X2 extends A implements J {}\n" + - " ^^\n" + - "The return type is incompatible with I.foo(), J.foo()\n" + "----------\n" ); } @@ -7592,4 +7581,180 @@ "----------\n" ); } +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=162073 +public void test134() { + this.runNegativeTest( + new String[] { + "X.java", + "interface I {\n" + + " T foo(Number n);\n" + + "}\n" + + "interface J extends I {\n" + + " A foo(Number n);\n" + // warning: overrides foo(java.lang.Number) in I; return type requires unchecked conversion + "}\n" + + "abstract class A extends Exception implements Cloneable {}" + }, + "----------\n" + + "1. WARNING in X.java (at line 5)\n" + + " A foo(Number n);\n" + + " ^\n" + + "Type safety: The return type A for foo(Number) from the type J needs unchecked conversion to conform to T from the type I\n" + + "----------\n" + ); +} +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=162073 +public void test135() { + this.runNegativeTest( + new String[] { + "X.java", + "abstract class X implements J {}\n" + + "class X2 implements J {\n" + + " public A foo(Number n) { return null; }\n" + + "}\n" + + "abstract class Y extends X {}\n" + + "interface I {\n" + + " T foo(Number n);\n" + + "}\n" + + "interface J extends I {\n" + + " A foo(Number n);\n" + // warning: overrides foo(java.lang.Number) in I; return type requires unchecked conversion + "}\n" + + "abstract class A extends Exception implements Cloneable {}" + }, + "----------\n" + + "1. WARNING in X.java (at line 10)\n" + + " A foo(Number n);\n" + + " ^\n" + + "Type safety: The return type A for foo(Number) from the type J needs unchecked conversion to conform to T from the type I\n" + + "----------\n" + ); +} +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=162073 +public void test136() { + this.runNegativeTest( + new String[] { + "X.java", + "public abstract class X extends E {}\n" + + "class X2 extends E {\n" + + " @Override public A foo(Number n) { return null; }\n" + + "}\n" + + "abstract class Y extends X {}\n" + + "abstract class D {\n" + + " abstract T foo(Number n);\n" + + "}\n" + + "abstract class E extends D {\n" + + " @Override abstract A foo(Number n);\n" + // warning: overrides foo(java.lang.Number) in I; return type requires unchecked conversion + "}\n" + + "abstract class A extends Exception implements Cloneable {}" + }, + "----------\n" + + "1. WARNING in X.java (at line 10)\n" + + " @Override abstract A foo(Number n);\n" + + " ^\n" + + "Type safety: The return type A for foo(Number) from the type E needs unchecked conversion to conform to T from the type D\n" + + "----------\n" + // javac reports warnings against X AND Y about E.foo(), as well as reporting the warning on E.foo() twice + ); +} +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=162073 +public void test137() { + this.runNegativeTest( + new String[] { + "X.java", + "public abstract class X implements J {}\n" + + "interface I {\n" + + " & Cloneable> T foo(Number n);\n" + + "}\n" + + "interface J extends I {\n" + + " XX foo(Number n);\n" + + "}\n" + + "class Z { }\n" + + "class Y extends Z { }" + + "abstract class XX extends Y implements Cloneable {}" + }, + "----------\n" + + "1. WARNING in X.java (at line 6)\n" + + " XX foo(Number n);\n" + + " ^^\n" + + "Type safety: The return type XX for foo(Number) from the type J needs unchecked conversion to conform to T from the type I\n" + + "----------\n" + ); +} +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=162073 +public void test138() { + this.runNegativeTest( + new String[] { + "X.java", + "public abstract class X implements J {}\n" + + "interface I {\n" + + " A foo(Number n);\n" + + "}\n" + + "interface J extends I {\n" + + " A foo(Number n);\n" + + "}\n" + + "class A { }" + + "abstract class XX extends Exception implements Cloneable {}" + }, + "----------\n" + + "1. WARNING in X.java (at line 6)\n" + + " A foo(Number n);\n" + + " ^\n" + + "Type safety: The return type A for foo(Number) from the type J needs unchecked conversion to conform to A from the type I\n" + + "----------\n" + ); +} +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=162073 +public void test139() { + this.runNegativeTest( + new String[] { + "X.java", + "public abstract class X implements J {\n" + + " void foo() {}\n" + + " public XX foo(Number n) { return null; }\n" + + "}\n" + + "interface I {\n" + + " T foo(Number n);\n" + + "}\n" + + "interface J extends I {\n" + + " XX foo(Number n);\n" + + "}\n" + + "abstract class XX extends Exception implements Cloneable {}" + }, + "----------\n" + + "1. WARNING in X.java (at line 9)\n" + + " XX foo(Number n);\n" + + " ^^\n" + + "Type safety: The return type XX for foo(Number) from the type J needs unchecked conversion to conform to T from the type I\n" + + "----------\n" + ); +} +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=162073 +public void test140() { + this.runNegativeTest( + new String[] { + "X.java", + "public abstract class X implements J, K {}\n" + + "interface I {\n" + + " T foo(Number n);\n" + + "}\n" + + "interface J extends I {\n" + + " XX foo(Number n);\n" + + "}\n" + + "interface K {\n" + + " NullPointerException foo(Number n);\n" + + "}\n" + + "abstract class XX extends Exception implements Cloneable {}" + }, + "----------\n" + + "1. ERROR in X.java (at line 1)\n" + + " public abstract class X implements J, K {}\n" + + " ^\n" + + "The return type is incompatible with K.foo(Number), J.foo(Number)\n" + + "----------\n" + + "2. WARNING in X.java (at line 6)\n" + + " XX foo(Number n);\n" + + " ^^\n" + + "Type safety: The return type XX for foo(Number) from the type J needs unchecked conversion to conform to T from the type I\n" + + "----------\n" + ); +} } Index: src/org/eclipse/jdt/core/tests/compiler/regression/AmbiguousMethodTest.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AmbiguousMethodTest.java,v retrieving revision 1.41 diff -u -r1.41 AmbiguousMethodTest.java --- src/org/eclipse/jdt/core/tests/compiler/regression/AmbiguousMethodTest.java 26 Apr 2007 12:33:58 -0000 1.41 +++ src/org/eclipse/jdt/core/tests/compiler/regression/AmbiguousMethodTest.java 26 Apr 2007 18:42:05 -0000 @@ -1456,155 +1456,8 @@ }, ""); } -// https://bugs.eclipse.org/bugs/show_bug.cgi?id=162073 -public void test032() { - this.runConformTest( - new String[] { - "X.java", - "interface I {\n" + - " T foo(Number n);\n" + - "}\n" + - "interface J extends I {\n" + - " XX foo(Number n);\n" + - "}\n" + - "public abstract class X implements J {\n" + - "}\n" + - "abstract class XX extends Exception implements Cloneable {}" - }, - ""); -} -// https://bugs.eclipse.org/bugs/show_bug.cgi?id=162073 -// variant that shows that the use of a substitution is needed during the bounds -// check -public void test032a() { - this.runConformTest( - new String[] { - "X.java", - "class Z { }\n" + - "class Y extends Z { }" + - "interface I {\n" + - " & Cloneable> T foo(Number n);\n" + - "}\n" + - "interface J extends I {\n" + - " XX foo(Number n);\n" + - "}\n" + - "public abstract class X implements J {\n" + - "}\n" + - "abstract class XX extends Y implements Cloneable {}" - }, - ""); -} -// https://bugs.eclipse.org/bugs/show_bug.cgi?id=162073 -// variant -public void test032b() { - this.runConformTest( - new String[] { - "X.java", - "class A { }" + - "interface I {\n" + - " A foo(Number n);\n" + - "}\n" + - "interface J extends I {\n" + - " A foo(Number n);\n" + - "}\n" + - "public abstract class X implements J {\n" + - "}\n" + - "abstract class XX extends Exception implements Cloneable {}" - }, - ""); -} -// https://bugs.eclipse.org/bugs/show_bug.cgi?id=162073 -// variant -public void test032c() { - this.runConformTest( - new String[] { - "X.java", - "class A { }" + - "interface I {\n" + - " A foo(Number n);\n" + - "}\n" + - "interface J extends I {\n" + - " A foo(Number n);\n" + - "}\n" + - "public abstract class X implements J {\n" + - "}\n" + - "abstract class XX extends Exception implements Cloneable {}" - }, - ""); -} -// https://bugs.eclipse.org/bugs/show_bug.cgi?id=162073 -// variant -public void test032d() { - this.runConformTest( - new String[] { - "X.java", - "class A { }" + - "interface I {\n" + - " A foo(Number n);\n" + - "}\n" + - "interface J extends I {\n" + - " A foo(Number n);\n" + - "}\n" + - "public abstract class X implements J {\n" + - "}\n" + - "abstract class XX extends Exception implements Cloneable {}" - }, - ""); -} -// https://bugs.eclipse.org/bugs/show_bug.cgi?id=162073 -// variant -public void test033() { - this.runConformTest( - new String[] { - "X.java", - "interface I {\n" + - " T foo(Number n);\n" + - "}\n" + - "interface J extends I {\n" + - " XX foo(Number n);\n" + - "}\n" + - "public abstract class X implements J {\n" + - " void foo() {\n" + - " }\n" + - " public XX foo(Number n) {\n" + - " return null;\n" + - " }\n" + - "}\n" + - "abstract class XX extends Exception implements Cloneable {}" - }, - ""); -} -// https://bugs.eclipse.org/bugs/show_bug.cgi?id=162073 -// variant that rightly complains -public void test034() { - this.runNegativeTest( - new String[] { - "X.java", - "interface I {\n" + - " T foo(Number n);\n" + - "}\n" + - "interface J extends I {\n" + - " XX foo(Number n);\n" + - "}\n" + - "interface K {\n" + - " NullPointerException foo(Number n);\n" + - "}\n" + - "public abstract class X implements J, K {\n" + - "}\n" + - "abstract class XX extends Exception implements Cloneable {}" - }, - "----------\n" + - "1. WARNING in X.java (at line 5)\n" + - " XX foo(Number n);\n" + - " ^^\n" + - "Type safety: The return type XX for foo(Number) from the type J needs unchecked conversion to conform to T from the type I\n" + - "----------\n" + - "2. ERROR in X.java (at line 10)\n" + - " public abstract class X implements J, K {\n" + - " ^\n" + - "The return type is incompatible with K.foo(Number), J.foo(Number)\n" + - "----------\n"); -} +//tests 32-34 were moved to MethodVerityTest 134-140 + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=162065 // variant - the inheriting class implements foo public void test035() { #P org.eclipse.jdt.core Index: compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier15.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier15.java,v retrieving revision 1.75 diff -u -r1.75 MethodVerifier15.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier15.java 23 Apr 2007 06:51:39 -0000 1.75 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier15.java 26 Apr 2007 18:42:12 -0000 @@ -344,56 +344,16 @@ boolean checkInheritedReturnTypes(MethodBinding[] methods, int length) { if (methods[0].declaringClass.isClass()) return super.checkInheritedReturnTypes(methods, length); - if (length <= 1) { - return true; // no need to continue since only 1 inherited method is left - } - // get rid of overriden methods coming from interfaces - if any - MethodBinding methodsToCheck[] = new MethodBinding[length]; // must not nullify methods slots in place - int count = length; - for (int i = 0; i < length; i++) { - methodsToCheck[i] = methods[i]; - } - for (int i = 0; i < length; i++) { - MethodBinding existingMethod; - if ((existingMethod = methodsToCheck[i]) != null) { - for (int j = 0; j < length; j++) { - MethodBinding inheritedMethod; - if (i != j && (inheritedMethod = methodsToCheck[j]) != null && - existingMethod.declaringClass.implementsInterface(inheritedMethod.declaringClass, true)) { - MethodBinding substitute = computeSubstituteMethod(inheritedMethod, existingMethod); - if (substitute != null && - doesSubstituteMethodOverride(existingMethod, substitute) && - (existingMethod.returnType.isCompatibleWith(substitute.returnType) || - isReturnTypeSubstituable(substitute, existingMethod))) { - count--; - methodsToCheck[j] = null; - } - } - } - } - } - if (count < length) { - if (count == 1) { - return true; // no need to continue since only 1 inherited method is left - } - for (int i = 0, j = 0; j < count; i++) { - if (methodsToCheck[i] != null) { - methodsToCheck[j++] = methodsToCheck[i]; - } - } - methods = methodsToCheck; - length = count; - } // else keep methods unchanged for further checks // its possible in 1.5 that A is compatible with B & C, but B is not compatible with C for (int i = 0, l = length - 1; i < l;) { MethodBinding method = methods[i++]; - for (int j = i; j <= l; j++) { + nextMethod : for (int j = i; j <= l; j++) { if (!areReturnTypesCompatible(method, methods[j])) { if (this.type.isInterface()) for (int m = length; --m >= 0;) if (methods[m].declaringClass.id == TypeIds.T_JavaLangObject) - return false; // do not complain since the super interface already got blamed + continue nextMethod; // do not complain since the super interface already got blamed problemReporter().inheritedMethodsHaveIncompatibleReturnTypes(this.type, methods, length); return false; } @@ -621,84 +581,16 @@ // one has type variables and substituteTwo did not pass bounds check in computeSubstituteMethod() return one.typeVariables != Binding.NO_TYPE_VARIABLES && !(substituteTwo instanceof ParameterizedGenericMethodBinding); } -// caveat: returns false if a method is implemented but needs that a bridge -// method be generated +// caveat: returns false if a method is implemented that needs a bridge method boolean isInterfaceMethodImplemented(MethodBinding inheritedMethod, MethodBinding existingMethod, ReferenceBinding superType) { if (inheritedMethod.original() != inheritedMethod && existingMethod.declaringClass.isInterface()) return false; // must hold onto ParameterizedMethod to see if a bridge method is necessary inheritedMethod = computeSubstituteMethod(inheritedMethod, existingMethod); return inheritedMethod != null - && inheritedMethod.returnType == existingMethod.returnType + && inheritedMethod.returnType == existingMethod.returnType // keep around to produce bridge methods && super.isInterfaceMethodImplemented(inheritedMethod, existingMethod, superType); } -/** - * Return true iff the return type of existingMethod is a valid replacement for - * the one of substituteMethod in a method declaration, in the context specified - * thereafter. It is expected that substituteMethod is the result of the - * substitution of the type parameters of an inheritedMethod method according to - * the type parameters of existingMethod and the inheritance relationship - * between existingMethod's declaring type and inheritedMethod's declaring type, - * where inheritedMethod is a method inherited by existingMethod's declaring - * type which is override compatible with existingMethod, except maybe for - * their respective return types. If those conditions are not met, the result is - * unspecified. - * @param substituteMethod a proper substitute of a method inherited by existingMethod - * @param existingMethod the existing method under examination - * @return true if the return type of existingMethod is a valid substitute for - * the one of substituteMethod - */ -boolean isReturnTypeSubstituable(MethodBinding substituteMethod, MethodBinding existingMethod) { - class ReturnTypeSubstitution implements Substitution { - TypeBinding replaced, replacer; - ReturnTypeSubstitution(TypeBinding replaced, TypeBinding replacer) { - this.replaced = replaced; - this.replacer = replacer; - } - public LookupEnvironment environment() { - return environment; - } - public boolean isRawSubstitution() { - return false; - } - public TypeBinding substitute(TypeVariableBinding typeVariable) { - return typeVariable == replaced ? replacer : typeVariable; - } - } - if (substituteMethod.returnType instanceof TypeVariableBinding) { - return ((TypeVariableBinding) substituteMethod.returnType). - boundCheck( - new ReturnTypeSubstitution(substituteMethod.returnType, existingMethod.returnType), - existingMethod.returnType) == TypeConstants.OK; - } else if (substituteMethod.returnType instanceof ParameterizedTypeBinding) { - if (! (existingMethod.returnType instanceof ParameterizedTypeBinding)) { - return false; - } - ParameterizedTypeBinding substituteReturnType = (ParameterizedTypeBinding) substituteMethod.returnType, - existingReturnType = (ParameterizedTypeBinding) existingMethod.returnType; - if (substituteReturnType.actualType() != existingReturnType.actualType()) - return false; - for (int i = 0; i < substituteReturnType.arguments.length; i++) { - TypeBinding substituteArgumentType, existingArgumentType; - if (! (existingArgumentType = existingReturnType.arguments[i]).isCompatibleWith( - substituteArgumentType = substituteReturnType.arguments[i])) { - if (substituteArgumentType instanceof TypeVariableBinding) { - if (((TypeVariableBinding) substituteArgumentType). - boundCheck( - new ReturnTypeSubstitution(substituteArgumentType, existingArgumentType), - // we do not address the most general pattern of multiple type variables, nor the recursive case either - existingArgumentType) != TypeConstants.OK) { - return false; - } - } else { - return false; - } - } - } - return true; - } - return false; -} SimpleSet findSuperinterfaceCollisions(ReferenceBinding superclass, ReferenceBinding[] superInterfaces) { ReferenceBinding[] interfacesToVisit = null; int nextPosition = 0; Index: compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier.java,v retrieving revision 1.84 diff -u -r1.84 MethodVerifier.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier.java 23 Apr 2007 06:51:39 -0000 1.84 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier.java 26 Apr 2007 18:42:12 -0000 @@ -141,48 +141,53 @@ return; // do not repoort against subsequent inherited methods } CompilerOptions options = type.scope.compilerOptions(); + // need to find the overridden methods to avoid blaming this type for issues which are already reported against a supertype + // but cannot ignore an overridden inherited method completely when it comes to checking for bridge methods + int[] overriddenInheritedMethods = findOverriddenInheritedMethods(methods, length); nextMethod : for (int i = length; --i >= 0;) { MethodBinding inheritedMethod = methods[i]; - if (currentMethod.isStatic() != inheritedMethod.isStatic()) { // Cannot override a static method or hide an instance method - problemReporter(currentMethod).staticAndInstanceConflict(currentMethod, inheritedMethod); - continue nextMethod; - } - - // want to tag currentMethod even if return types are not equal - if (inheritedMethod.isAbstract()) { - if (inheritedMethod.declaringClass.isInterface()) { - currentMethod.modifiers |= ExtraCompilerModifiers.AccImplementing; + if (overriddenInheritedMethods == null || overriddenInheritedMethods[i] == 0) { + if (currentMethod.isStatic() != inheritedMethod.isStatic()) { // Cannot override a static method or hide an instance method + problemReporter(currentMethod).staticAndInstanceConflict(currentMethod, inheritedMethod); + continue nextMethod; + } + + // want to tag currentMethod even if return types are not equal + if (inheritedMethod.isAbstract()) { + if (inheritedMethod.declaringClass.isInterface()) { + currentMethod.modifiers |= ExtraCompilerModifiers.AccImplementing; + } else { + currentMethod.modifiers |= ExtraCompilerModifiers.AccImplementing | ExtraCompilerModifiers.AccOverriding; + } +// with the above change an abstract method is tagged as implementing the inherited abstract method +// if (!currentMethod.isAbstract() && inheritedMethod.isAbstract()) { +// if ((currentMethod.modifiers & CompilerModifiers.AccOverriding) == 0) +// currentMethod.modifiers |= CompilerModifiers.AccImplementing; } else { - currentMethod.modifiers |= ExtraCompilerModifiers.AccImplementing | ExtraCompilerModifiers.AccOverriding; + currentMethod.modifiers |= ExtraCompilerModifiers.AccOverriding; } -// with the above change an abstract method is tagged as implementing the inherited abstract method -// if (!currentMethod.isAbstract() && inheritedMethod.isAbstract()) { -// if ((currentMethod.modifiers & CompilerModifiers.AccOverriding) == 0) -// currentMethod.modifiers |= CompilerModifiers.AccImplementing; - } else { - currentMethod.modifiers |= ExtraCompilerModifiers.AccOverriding; - } - - if (!areReturnTypesCompatible(currentMethod, inheritedMethod)) - if (reportIncompatibleReturnTypeError(currentMethod, inheritedMethod)) - continue nextMethod; - - if (currentMethod.thrownExceptions != Binding.NO_EXCEPTIONS) - checkExceptions(currentMethod, inheritedMethod); - if (inheritedMethod.isFinal()) - problemReporter(currentMethod).finalMethodCannotBeOverridden(currentMethod, inheritedMethod); - if (!isAsVisible(currentMethod, inheritedMethod)) - problemReporter(currentMethod).visibilityConflict(currentMethod, inheritedMethod); - if (options.reportDeprecationWhenOverridingDeprecatedMethod && inheritedMethod.isViewedAsDeprecated()) { - if (!currentMethod.isViewedAsDeprecated() || options.reportDeprecationInsideDeprecatedCode) { - // check against the other inherited methods to see if they hide this inheritedMethod - ReferenceBinding declaringClass = inheritedMethod.declaringClass; - if (declaringClass.isInterface()) - for (int j = length; --j >= 0;) - if (i != j && methods[j].declaringClass.implementsInterface(declaringClass, false)) - continue nextMethod; - - problemReporter(currentMethod).overridesDeprecatedMethod(currentMethod, inheritedMethod); + + if (!areReturnTypesCompatible(currentMethod, inheritedMethod)) + if (reportIncompatibleReturnTypeError(currentMethod, inheritedMethod)) + continue nextMethod; + + if (currentMethod.thrownExceptions != Binding.NO_EXCEPTIONS) + checkExceptions(currentMethod, inheritedMethod); + if (inheritedMethod.isFinal()) + problemReporter(currentMethod).finalMethodCannotBeOverridden(currentMethod, inheritedMethod); + if (!isAsVisible(currentMethod, inheritedMethod)) + problemReporter(currentMethod).visibilityConflict(currentMethod, inheritedMethod); + if (options.reportDeprecationWhenOverridingDeprecatedMethod && inheritedMethod.isViewedAsDeprecated()) { + if (!currentMethod.isViewedAsDeprecated() || options.reportDeprecationInsideDeprecatedCode) { + // check against the other inherited methods to see if they hide this inheritedMethod + ReferenceBinding declaringClass = inheritedMethod.declaringClass; + if (declaringClass.isInterface()) + for (int j = length; --j >= 0;) + if (i != j && methods[j].declaringClass.implementsInterface(declaringClass, false)) + continue nextMethod; + + problemReporter(currentMethod).overridesDeprecatedMethod(currentMethod, inheritedMethod); + } } } checkForBridgeMethod(currentMethod, inheritedMethod, allInheritedMethods); @@ -231,6 +236,18 @@ // no op before 1.5 } void checkInheritedMethods(MethodBinding[] methods, int length) { + int[] overriddenInheritedMethods = findOverriddenInheritedMethods(methods, length); + if (overriddenInheritedMethods != null) { + // detected some overridden methods that can be ignored when checking return types + int index = 0; + MethodBinding[] closestMethods = new MethodBinding[length]; + for (int i = 0; i < length; i++) + if (overriddenInheritedMethods[i] == 0) + closestMethods[index++] = methods[i]; + methods = closestMethods; + length = index; + } + if (!checkInheritedReturnTypes(methods, length)) return; @@ -573,6 +590,42 @@ public boolean doesMethodOverride(MethodBinding method, MethodBinding inheritedMethod) { return areParametersEqual(method, inheritedMethod); } +SimpleSet findSuperinterfaceCollisions(ReferenceBinding superclass, ReferenceBinding[] superInterfaces) { + return null; // noop in 1.4 +} +int[] findOverriddenInheritedMethods(MethodBinding[] methods, int length) { + int[] toSkip = null; + if (length > 1) { + nextMethod : for (int i = 0; i < length; i++) { + if (toSkip != null && toSkip[i] == -1) continue nextMethod; + ReferenceBinding declaringClass = methods[i].declaringClass; + if (declaringClass.isInterface()) { + for (int j = 0; j < length; j++) { + if (i == j) continue; + ReferenceBinding declaringClass2 = methods[j].declaringClass; + if (declaringClass2.isInterface() && declaringClass2.implementsInterface(declaringClass, true)) { + if (toSkip == null) + toSkip = new int[length]; + toSkip[i] = -1; + continue nextMethod; + } + } + } else { + for (int j = 0; j < length; j++) { + if (i == j) continue; + ReferenceBinding declaringClass2 = methods[j].declaringClass; + if (!declaringClass2.isInterface() && declaringClass.isSuperclassOf(declaringClass2)) { + if (toSkip == null) + toSkip = new int[length]; + toSkip[i] = -1; + continue nextMethod; + } + } + } + } + } + return toSkip; +} boolean isAsVisible(MethodBinding newMethod, MethodBinding inheritedMethod) { if (inheritedMethod.modifiers == newMethod.modifiers) return true; @@ -624,9 +677,6 @@ reporter.referenceContext = currentMethod.sourceMethod(); return reporter; } -SimpleSet findSuperinterfaceCollisions(ReferenceBinding superclass, ReferenceBinding[] superInterfaces) { - return null; // noop in 1.4 -} /** * Return true and report an incompatibleReturnType error if currentMethod's * return type is strictly incompatible with inheritedMethod's, else return