### Eclipse Workspace Patch 1.0 #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.96 diff -u -r1.96 MethodVerifier15.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier15.java 23 Jan 2009 19:50:08 -0000 1.96 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier15.java 13 Feb 2009 20:27:38 -0000 @@ -124,18 +124,10 @@ // so the parameters are equal and the return type is compatible b/w the currentMethod & the substituted inheritedMethod MethodBinding originalInherited = abstractMethod.original(); - if (originalInherited.returnType != concreteMethod.returnType) { - if (abstractMethod.returnType.leafComponentType().isParameterizedTypeWithActualArguments()) { - if (concreteMethod.returnType.leafComponentType().isRawType()) - problemReporter().unsafeReturnTypeOverride(concreteMethod, originalInherited, this.type); - } else if (abstractMethod.hasSubstitutedReturnType() && originalInherited.returnType.leafComponentType().isTypeVariable()) { - if (((TypeVariableBinding) originalInherited.returnType.leafComponentType()).declaringElement == originalInherited) { // see 81618 - type variable from inherited method - TypeBinding currentReturnType = concreteMethod.returnType.leafComponentType(); - if (!currentReturnType.isTypeVariable() || ((TypeVariableBinding) currentReturnType).declaringElement != concreteMethod) - problemReporter().unsafeReturnTypeOverride(concreteMethod, originalInherited, this.type); - } - } - } + if (originalInherited.returnType != concreteMethod.returnType) + if (!isAcceptableReturnTypeOverride(concreteMethod, abstractMethod)) + problemReporter().unsafeReturnTypeOverride(concreteMethod, originalInherited, this.type); + // check whether bridge method is already defined above for interface methods if (originalInherited.declaringClass.isInterface()) { if ((concreteMethod.declaringClass == this.type.superclass && this.type.superclass.isParameterizedType()) @@ -150,20 +142,9 @@ // so the parameters are equal and the return type is compatible b/w the currentMethod & the substituted inheritedMethod MethodBinding originalInherited = inheritedMethod.original(); - if (originalInherited.returnType != currentMethod.returnType) { -// if (currentMethod.returnType.needsUncheckedConversion(inheritedMethod.returnType)) { -// problemReporter(currentMethod).unsafeReturnTypeOverride(currentMethod, originalInherited, this.type); - if (inheritedMethod.returnType.leafComponentType().isParameterizedTypeWithActualArguments() - && currentMethod.returnType.leafComponentType().isRawType()) { + if (originalInherited.returnType != currentMethod.returnType) + if (!isAcceptableReturnTypeOverride(currentMethod, inheritedMethod)) problemReporter(currentMethod).unsafeReturnTypeOverride(currentMethod, originalInherited, this.type); - } else if (inheritedMethod.hasSubstitutedReturnType() && originalInherited.returnType.leafComponentType().isTypeVariable()) { - if (((TypeVariableBinding) originalInherited.returnType.leafComponentType()).declaringElement == originalInherited) { // see 81618 - type variable from inherited method - TypeBinding currentReturnType = currentMethod.returnType.leafComponentType(); - if (!currentReturnType.isTypeVariable() || ((TypeVariableBinding) currentReturnType).declaringElement != currentMethod) - problemReporter(currentMethod).unsafeReturnTypeOverride(currentMethod, originalInherited, this.type); - } - } - } if (this.type.addSyntheticBridgeMethod(originalInherited, currentMethod.original()) != null) { for (int i = 0, l = allInheritedMethods == null ? 0 : allInheritedMethods.length; i < l; i++) { @@ -586,21 +567,6 @@ public boolean doesMethodOverride(MethodBinding method, MethodBinding inheritedMethod) { return couldMethodOverride(method, inheritedMethod) && areMethodsCompatible(method, inheritedMethod); } -boolean hasGenericParameter(MethodBinding method) { - if (method.genericSignature() == null) return false; - - // may be only the return type that is generic, need to check parameters - TypeBinding[] params = method.parameters; - for (int i = 0, l = params.length; i < l; i++) { - TypeBinding param = params[i].leafComponentType(); - if (param instanceof ReferenceBinding) { - int modifiers = ((ReferenceBinding) param).modifiers; - if ((modifiers & ExtraCompilerModifiers.AccGenericSignature) != 0) - return true; - } - } - return false; -} boolean doTypeVariablesClash(MethodBinding one, MethodBinding substituteTwo) { // one has type variables and substituteTwo did not pass bounds check in computeSubstituteMethod() return one.typeVariables != Binding.NO_TYPE_VARIABLES && !(substituteTwo instanceof ParameterizedGenericMethodBinding); @@ -674,6 +640,45 @@ } return copy; } +boolean hasGenericParameter(MethodBinding method) { + if (method.genericSignature() == null) return false; + + // may be only the return type that is generic, need to check parameters + TypeBinding[] params = method.parameters; + for (int i = 0, l = params.length; i < l; i++) { + TypeBinding param = params[i].leafComponentType(); + if (param instanceof ReferenceBinding) { + int modifiers = ((ReferenceBinding) param).modifiers; + if ((modifiers & ExtraCompilerModifiers.AccGenericSignature) != 0) + return true; + } + } + return false; +} +boolean isAcceptableReturnTypeOverride(MethodBinding currentMethod, MethodBinding inheritedMethod) { + // called when currentMethod's return type is compatible with inheritedMethod's return type + + if (inheritedMethod.declaringClass.isRawType()) + return true; // since the inheritedMethod comes from a raw type, the return type is always acceptable + + MethodBinding originalInherited = inheritedMethod.original(); + TypeBinding originalInheritedReturnType = originalInherited.returnType.leafComponentType(); + if (originalInheritedReturnType.isParameterizedTypeWithActualArguments()) + return !currentMethod.returnType.leafComponentType().isRawType(); // raw types issue a warning if inherited is parameterized + + TypeBinding currentReturnType = currentMethod.returnType.leafComponentType(); + switch (currentReturnType.kind()) { + case Binding.TYPE_PARAMETER : + if (currentReturnType == inheritedMethod.returnType.leafComponentType()) + return true; + //$FALL-THROUGH$ + default : + if (originalInheritedReturnType.isTypeVariable()) + if (((TypeVariableBinding) originalInheritedReturnType).declaringElement == originalInherited) + return false; + return true; + } +} // 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()) @@ -731,6 +736,8 @@ return method.typeVariables == Binding.NO_TYPE_VARIABLES; } boolean isUnsafeReturnTypeOverride(MethodBinding currentMethod, MethodBinding inheritedMethod) { + // called when currentMethod's return type is NOT compatible with inheritedMethod's return type + // JLS 3 §8.4.5: more are accepted, with an unchecked conversion if (currentMethod.returnType == inheritedMethod.returnType.erasure()) { TypeBinding[] currentParams = currentMethod.parameters; #P org.eclipse.jdt.core.tests.compiler 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.174 diff -u -r1.174 MethodVerifyTest.java --- src/org/eclipse/jdt/core/tests/compiler/regression/MethodVerifyTest.java 23 Jan 2009 19:50:07 -0000 1.174 +++ src/org/eclipse/jdt/core/tests/compiler/regression/MethodVerifyTest.java 13 Feb 2009 20:27:40 -0000 @@ -9383,4 +9383,82 @@ "----------\n" ); } +//https://bugs.eclipse.org/bugs/show_bug.cgi?id=264881 +public void test184() { + this.runNegativeTest( + new String[] { + "X.java", + "class A {\n" + + " > T a() { return null; }\n" + + " U num() { return null; }\n" + + " T x() { return null; }\n" + + " T y() { return null; }\n" + + " T z() { return null; }\n" + + "}\n" + + "class B extends A {\n" + + " @Override A a() { return null; }\n" + + " @Override Double num() { return 1.0; }\n" + + " @Override Integer x() { return 1; }\n" + + " @Override Integer y() { return 1; }\n" + + " @Override Integer z() { return 1; }\n" + + "}\n" + + "class C extends A {\n" + + " @Override A a() { return null; }\n" + + " @Override Double num() { return 1.0; }\n" + + " @Override Integer x() { return 1; }\n" + + " @Override Integer y() { return 1; }\n" + + " @Override Integer z() { return 1; }\n" + + "}\n" + + "class M {\n" + + " Object m(Class c) { return null; }\n" + + " Object n(Class c) { return null; }\n" + + "}\n" + + "class N extends M {\n" + + " @Override T m(Class c) { return null; }\n" + + " @Override V n(Class c) { return null; }\n" + + "}" + }, + "----------\n" + + "1. WARNING in X.java (at line 6)\n" + + " T z() { return null; }\n" + + " ^^^^^^^\n" + + "The type parameter T should not be bounded by the final type Integer. Final types cannot be further extended\n" + + "----------\n" + + "2. WARNING in X.java (at line 9)\n" + + " @Override A a() { return null; }\n" + + " ^\n" + + "A is a raw type. References to generic type A should be parameterized\n" + + "----------\n" + + "3. WARNING in X.java (at line 9)\n" + + " @Override A a() { return null; }\n" + + " ^\n" + + "Type safety: The return type A for a() from the type B needs unchecked conversion to conform to T from the type A\n" + + "----------\n" + + "4. WARNING in X.java (at line 11)\n" + + " @Override Integer x() { return 1; }\n" + + " ^^^^^^^\n" + + "Type safety: The return type Integer for x() from the type B needs unchecked conversion to conform to T from the type A\n" + + "----------\n" + + "5. WARNING in X.java (at line 12)\n" + + " @Override Integer y() { return 1; }\n" + + " ^^^^^^^\n" + + "Type safety: The return type Integer for y() from the type B needs unchecked conversion to conform to T from the type A\n" + + "----------\n" + + "6. WARNING in X.java (at line 13)\n" + + " @Override Integer z() { return 1; }\n" + + " ^^^^^^^\n" + + "Type safety: The return type Integer for z() from the type B needs unchecked conversion to conform to T from the type A\n" + + "----------\n" + + "7. WARNING in X.java (at line 15)\n" + + " class C extends A {\n" + + " ^\n" + + "A is a raw type. References to generic type A should be parameterized\n" + + "----------\n" + + "8. WARNING in X.java (at line 16)\n" + + " @Override A a() { return null; }\n" + + " ^\n" + + "A is a raw type. References to generic type A should be parameterized\n" + + "----------\n" + ); +} }