blob: 35a200b453365d97a38514ac234cd7c5df2dfe7d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.wst.jsdt.internal.compiler.lookup;
import org.eclipse.wst.jsdt.core.compiler.CharOperation;
import org.eclipse.wst.jsdt.internal.compiler.ast.MethodDeclaration;
class MethodVerifier15 extends MethodVerifier {
MethodVerifier15(LookupEnvironment environment) {
super(environment);
}
boolean areTypesEqual(TypeBinding one, TypeBinding two) {
return one == two || super.areTypesEqual(one.erasure(), two.erasure());
}
void checkAgainstInheritedMethods(MethodBinding currentMethod, MethodBinding[] methods, int length) {
// methods includes the inherited methods that the currentMethod must comply with
// likely only 1 but could be more if mutiple declared supertypes define the method (1 superclass & 1 to many declared interfaces)
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
this.problemReporter(currentMethod).staticAndInstanceConflict(currentMethod, inheritedMethod);
continue nextMethod;
}
// curentMethod is always resolved as its defined by the source type BUT the inheritedMethod may not be
// so now with generics, the inheritedMethod should be resolved since we don't want to waste time dealing
// with Unresolved types over & over
if (inheritedMethod.declaringClass instanceof BinaryTypeBinding)
((BinaryTypeBinding) inheritedMethod.declaringClass).resolveTypesFor(inheritedMethod);
// must check each parameter pair to see if you have raw to parameterized conversions
TypeBinding[] currentArgs = currentMethod.parameters;
TypeBinding[] inheritedArgs = inheritedMethod.parameters;
if (currentArgs != inheritedArgs) {
for (int j = 0, k = currentArgs.length; j < k; j++) {
TypeBinding currentArg = currentArgs[j].leafComponentType();
TypeBinding inheritedArg = inheritedArgs[j].leafComponentType();
if (currentArg != inheritedArg) {
if (currentArg.isParameterizedType() && hasBoundedParameters((ParameterizedTypeBinding) currentArg)) {
if (inheritedArg.isRawType()) {
// if (inheritedArg.isRawType() || !inheritedArg.isEquivalentTo(currentArg)) {
this.problemReporter(currentMethod).methodNameClash(currentMethod, inheritedMethod);
continue nextMethod;
}
}
}
}
}
if (!currentMethod.isAbstract() && inheritedMethod.isAbstract()) {
if ((currentMethod.modifiers & CompilerModifiers.AccOverriding) == 0)
currentMethod.modifiers |= CompilerModifiers.AccImplementing;
} else {
currentMethod.modifiers |= CompilerModifiers.AccOverriding;
}
boolean addBridgeMethod = inheritedMethod.hasSubstitutedReturnType();
if (currentMethod.returnType != inheritedMethod.returnType) {
// can be [] of Class#RAW vs. Class<T>
if (!isReturnTypeSubstituable(currentMethod, inheritedMethod)) {
this.problemReporter(currentMethod).incompatibleReturnType(currentMethod, inheritedMethod);
continue nextMethod;
}
TypeBinding inheritedReturnType = inheritedMethod.returnType.leafComponentType();
TypeBinding returnType = currentMethod.returnType.leafComponentType();
if (inheritedReturnType.isRawType()) {
if (returnType.isParameterizedType() && hasBoundedParameters((ParameterizedTypeBinding) returnType)) {
this.problemReporter(currentMethod).methodNameClash(currentMethod, inheritedMethod);
continue nextMethod;
}
} else if (inheritedReturnType.isParameterizedType()) {
if (!returnType.isParameterizedType())
this.problemReporter(currentMethod).unsafeReturnTypeOverride(currentMethod, inheritedMethod, ((MethodDeclaration)currentMethod.sourceMethod()).returnType);
} else if (inheritedReturnType.isTypeVariable()) {
this.problemReporter(currentMethod).unsafeReturnTypeOverride(currentMethod, inheritedMethod, ((MethodDeclaration)currentMethod.sourceMethod()).returnType);
}
addBridgeMethod = true;
}
if (addBridgeMethod || inheritedMethod.hasSubstitutedParameters()) {
MethodBinding original = inheritedMethod.original();
if (!areReturnTypesEqual(original, currentMethod) || !areParametersEqual(original, currentMethod))
this.type.addSyntheticBridgeMethod(original, currentMethod);
}
if (currentMethod.thrownExceptions != NoExceptions)
this.checkExceptions(currentMethod, inheritedMethod);
if (inheritedMethod.isFinal())
this.problemReporter(currentMethod).finalMethodCannotBeOverridden(currentMethod, inheritedMethod);
if (!this.isAsVisible(currentMethod, inheritedMethod))
this.problemReporter(currentMethod).visibilityConflict(currentMethod, inheritedMethod);
if (environment.options.reportDeprecationWhenOverridingDeprecatedMethod && inheritedMethod.isViewedAsDeprecated()) {
if (!currentMethod.isViewedAsDeprecated() || environment.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;
this.problemReporter(currentMethod).overridesDeprecatedMethod(currentMethod, inheritedMethod);
}
}
}
}
boolean doesMethodOverride(MethodBinding method, MethodBinding inheritedMethod) {
return areParametersEqual(method, inheritedMethod) && isReturnTypeSubstituable(method, inheritedMethod);
}
boolean hasBoundedParameters(ParameterizedTypeBinding parameterizedType) {
TypeBinding[] arguments = parameterizedType.arguments;
if (arguments == null) return false;
nextArg : for (int i = 0, l = arguments.length; i < l; i++) {
if (arguments[i].isWildcard())
if (((WildcardBinding) arguments[i]).kind == org.eclipse.wst.jsdt.internal.compiler.ast.Wildcard.UNBOUND)
continue nextArg;
if (arguments[i].isTypeVariable())
if (((TypeVariableBinding) arguments[i]).firstBound == null)
continue nextArg;
return true;
}
return false;
}
boolean isReturnTypeSubstituable(MethodBinding one, MethodBinding two) {
if (one.returnType == two.returnType) return true;
return isTypeSubstituable(one.returnType.erasure(), two.returnType.erasure());
}
boolean isTypeSubstituable(TypeBinding one, TypeBinding two) {
if (one == two) return true;
if (one.isArrayType() || two.isArrayType()) {
if (one.isArrayType() != two.isArrayType()) return false;
ArrayBinding arrayOne = (ArrayBinding) one;
ArrayBinding arrayTwo = (ArrayBinding) two;
if (arrayOne.dimensions != arrayTwo.dimensions) return false;
one = arrayOne.leafComponentType;
two = arrayTwo.leafComponentType;
}
if (one.isBaseType() || two.isBaseType()) return false;
ReferenceBinding subType = (ReferenceBinding) one;
ReferenceBinding superType = (ReferenceBinding) two;
if (CharOperation.equals(subType.compoundName, superType.compoundName)) return true;
superType = BinaryTypeBinding.resolveType(superType, this.environment, true);
subType = BinaryTypeBinding.resolveType(subType, this.environment, true);
if (superType.isInterface())
return subType.implementsInterface(superType, true);
return subType.isClass() && isSameClassOrSubclassOf(subType, superType);
}
}