All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jdt.internal.compiler.lookup.MethodVerifier15 Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2000, 2009 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;

import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.util.HashtableOfObject;
import org.eclipse.jdt.internal.compiler.util.SimpleSet;

class MethodVerifier15 extends MethodVerifier {

MethodVerifier15(LookupEnvironment environment) {
	super(environment);
}
boolean areMethodsCompatible(MethodBinding one, MethodBinding two) {
	// use the original methods to test compatibility, but do not check visibility, etc
	one = one.original();
	two = two.original();

	TypeBinding match = one.declaringClass.findSuperTypeOriginatingFrom(two.declaringClass);
	if (!(match instanceof ReferenceBinding))
		return false; // method's declaringClass does not inherit from inheritedMethod's

	if (match != two.declaringClass) {
		MethodBinding[] superMethods = ((ReferenceBinding) match).getMethods(two.selector);
		for (int i = 0, length = superMethods.length; i < length; i++)
			if (superMethods[i].original() == two)
				return isParameterSubsignature(one, superMethods[i]);
	}

	return isParameterSubsignature(one, two);
}
boolean areParametersEqual(MethodBinding one, MethodBinding two) {
	TypeBinding[] oneArgs = one.parameters;
	TypeBinding[] twoArgs = two.parameters;
	if (oneArgs == twoArgs) return true;

	int length = oneArgs.length;
	if (length != twoArgs.length) return false;

	if (one.declaringClass.isInterface()) {
		for (int i = 0; i < length; i++)
			if (!areTypesEqual(oneArgs[i], twoArgs[i]))
				return false;
	} else {
		// methods with raw parameters are considered equal to inherited methods
		// with parameterized parameters for backwards compatibility, need a more complex check
		int i;
		foundRAW: for (i = 0; i < length; i++) {
			if (!areTypesEqual(oneArgs[i], twoArgs[i])) {
				if (oneArgs[i].leafComponentType().isRawType()) {
					if (oneArgs[i].dimensions() == twoArgs[i].dimensions() && oneArgs[i].leafComponentType().isEquivalentTo(twoArgs[i].leafComponentType())) {
						// raw mode does not apply if the method defines its own type variables
						if (one.typeVariables != Binding.NO_TYPE_VARIABLES)
							return false;
						// one parameter type is raw, hence all parameters types must be raw or non generic
						// otherwise we have a mismatch check backwards
						for (int j = 0; j < i; j++)
							if (oneArgs[j].leafComponentType().isParameterizedTypeWithActualArguments())
								return false;
						// switch to all raw mode
						break foundRAW;
					}
				}
				return false;
			}
		}
		// all raw mode for remaining parameters (if any)
		for (i++; i < length; i++) {
			if (!areTypesEqual(oneArgs[i], twoArgs[i])) {
				if (oneArgs[i].leafComponentType().isRawType())
					if (oneArgs[i].dimensions() == twoArgs[i].dimensions() && oneArgs[i].leafComponentType().isEquivalentTo(twoArgs[i].leafComponentType()))
						continue;
				return false;
			} else if (oneArgs[i].leafComponentType().isParameterizedTypeWithActualArguments()) {
				return false; // no remaining parameter can be a Parameterized type (if one has been converted then all RAW types must be converted)
			}
		}
	}
	return true;
}
boolean areReturnTypesCompatible(MethodBinding one, MethodBinding two) {
	if (one.returnType == two.returnType) return true;
	return areReturnTypesCompatible0(one, two);
}
boolean areTypesEqual(TypeBinding one, TypeBinding two) {
	if (one == two) return true;

	// need to consider X and X as the same 'type'
	if (one.isParameterizedType() && two.isParameterizedType())
		return one.isEquivalentTo(two) && two.isEquivalentTo(one);

	// Can skip this since we resolved each method before comparing it, see computeSubstituteMethod()
	//	if (one instanceof UnresolvedReferenceBinding)
	//		return ((UnresolvedReferenceBinding) one).resolvedType == two;
	//	if (two instanceof UnresolvedReferenceBinding)
	//		return ((UnresolvedReferenceBinding) two).resolvedType == one;
	return false; // all other type bindings are identical
}
boolean canSkipInheritedMethods() {
	if (this.type.superclass() != null)
		if (this.type.superclass().isAbstract() || this.type.superclass().isParameterizedType())
			return false;
	return this.type.superInterfaces() == Binding.NO_SUPERINTERFACES;
}
boolean canSkipInheritedMethods(MethodBinding one, MethodBinding two) {
	return two == null // already know one is not null
		|| (one.declaringClass == two.declaringClass && !one.declaringClass.isParameterizedType());
}
void checkConcreteInheritedMethod(MethodBinding concreteMethod, MethodBinding[] abstractMethods) {
	super.checkConcreteInheritedMethod(concreteMethod, abstractMethods);

	for (int i = 0, l = abstractMethods.length; i < l; i++) {
		MethodBinding abstractMethod = abstractMethods[i];
		if (concreteMethod.isVarargs() != abstractMethod.isVarargs())
			problemReporter().varargsConflict(concreteMethod, abstractMethod, this.type);

		// 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 (!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())
				|| this.type.superclass.erasure().findSuperTypeOriginatingFrom(originalInherited.declaringClass) == null)
					this.type.addSyntheticBridgeMethod(originalInherited, concreteMethod.original());
		}
	}
}
void checkForBridgeMethod(MethodBinding currentMethod, MethodBinding inheritedMethod, MethodBinding[] allInheritedMethods) {
	if (currentMethod.isVarargs() != inheritedMethod.isVarargs())
		problemReporter(currentMethod).varargsConflict(currentMethod, inheritedMethod, this.type);

	// 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 (!isAcceptableReturnTypeOverride(currentMethod, inheritedMethod))
			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++) {
			if (allInheritedMethods[i] != null && detectInheritedNameClash(originalInherited, allInheritedMethods[i].original()))
				return;
		}
	}
}
void checkForNameClash(MethodBinding currentMethod, MethodBinding inheritedMethod) {
	// sent from checkMethods() to compare a current method and an inherited method that are not 'equal'

	// error cases:
	//		abstract class AA { abstract void test(E element); }
	//		class A extends AA { public void test(Integer i) {} }
	//		public class B extends A { public void test(Comparable i) {} }
	//		interface I { void test(E element); }
	//		class A implements I { public void test(Integer i) {} }
	//		public class B extends A { public void test(Comparable i) {} }

	//		abstract class Y implements EqualityComparable, Equivalent {
	//			public boolean equalTo(Integer other) { return true; }
	//		}
	//		interface Equivalent { boolean equalTo(T other); }
	//		interface EqualityComparable { boolean equalTo(T other); }

	//		class Y implements EqualityComparable, Equivalent{
	//			public boolean equalTo(String other) { return true; }
	//			public boolean equalTo(Object other) { return true; }
	//		}
	//		interface Equivalent { boolean equalTo(T other); }
	//		interface EqualityComparable { boolean equalTo(Object other); }

	//		class A { void m(T t) {} }
	//		class B extends A { void m(S t) {}}
	//		class D extends B { void m(Number t) {}    void m(Integer t) {} }

	//		inheritedMethods does not include I.test since A has a valid implementation
	//		interface I> { void test(E element); }
	//		class A implements I { public void test(Integer i) {} }
	//		class B extends A { public void test(Comparable i) {} }

	if (currentMethod.declaringClass.isInterface() || inheritedMethod.isStatic()) return;

	if (!detectNameClash(currentMethod, inheritedMethod)) { // check up the hierarchy for skipped inherited methods
		TypeBinding[] currentParams = currentMethod.parameters;
		TypeBinding[] inheritedParams = inheritedMethod.parameters;
		int length = currentParams.length;
		if (length != inheritedParams.length) return; // no match

		for (int i = 0; i < length; i++)
			if (currentParams[i] != inheritedParams[i])
				if (currentParams[i].isBaseType() != inheritedParams[i].isBaseType() || !inheritedParams[i].isCompatibleWith(currentParams[i]))
					return; // no chance that another inherited method's bridge method can collide

		ReferenceBinding[] interfacesToVisit = null;
		int nextPosition = 0;
		ReferenceBinding superType = inheritedMethod.declaringClass;
		ReferenceBinding[] itsInterfaces = superType.superInterfaces();
		if (itsInterfaces != Binding.NO_SUPERINTERFACES) {
			nextPosition = itsInterfaces.length;
			interfacesToVisit = itsInterfaces;
		}
		superType = superType.superclass(); // now start with its superclass
		while (superType != null && superType.isValidBinding()) {
			MethodBinding[] methods = superType.getMethods(currentMethod.selector);
			for (int m = 0, n = methods.length; m < n; m++) {
				MethodBinding substitute = computeSubstituteMethod(methods[m], currentMethod);
				if (substitute != null && !isSubstituteParameterSubsignature(currentMethod, substitute) && detectNameClash(currentMethod, substitute))
					return;
			}
			if ((itsInterfaces = superType.superInterfaces()) != Binding.NO_SUPERINTERFACES) {
				if (interfacesToVisit == null) {
					interfacesToVisit = itsInterfaces;
					nextPosition = interfacesToVisit.length;
				} else {
					int itsLength = itsInterfaces.length;
					if (nextPosition + itsLength >= interfacesToVisit.length)
						System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition);
					nextInterface : for (int a = 0; a < itsLength; a++) {
						ReferenceBinding next = itsInterfaces[a];
						for (int b = 0; b < nextPosition; b++)
							if (next == interfacesToVisit[b]) continue nextInterface;
						interfacesToVisit[nextPosition++] = next;
					}
				}
			}
			superType = superType.superclass();
		}

		for (int i = 0; i < nextPosition; i++) {
			superType = interfacesToVisit[i];
			if (superType.isValidBinding()) {
				MethodBinding[] methods = superType.getMethods(currentMethod.selector);
				for (int m = 0, n = methods.length; m < n; m++){
					MethodBinding substitute = computeSubstituteMethod(methods[m], currentMethod);
					if (substitute != null && !isSubstituteParameterSubsignature(currentMethod, substitute) && detectNameClash(currentMethod, substitute))
						return;
				}
				if ((itsInterfaces = superType.superInterfaces()) != Binding.NO_SUPERINTERFACES) {
					int itsLength = itsInterfaces.length;
					if (nextPosition + itsLength >= interfacesToVisit.length)
						System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition);
					nextInterface : for (int a = 0; a < itsLength; a++) {
						ReferenceBinding next = itsInterfaces[a];
						for (int b = 0; b < nextPosition; b++)
							if (next == interfacesToVisit[b]) continue nextInterface;
						interfacesToVisit[nextPosition++] = next;
					}
				}
			}
		}
	}
}
void checkInheritedMethods(MethodBinding inheritedMethod, MethodBinding otherInheritedMethod) {
	// sent from checkMethods() to compare 2 inherited methods that are not 'equal'
	if (inheritedMethod.declaringClass.erasure() == otherInheritedMethod.declaringClass.erasure()) {
		boolean areDuplicates = inheritedMethod.hasSubstitutedParameters() && otherInheritedMethod.hasSubstitutedParameters()
			? inheritedMethod.areParametersEqual(otherInheritedMethod)
			: inheritedMethod.areParameterErasuresEqual(otherInheritedMethod);
		if (areDuplicates) {
			problemReporter().duplicateInheritedMethods(this.type, inheritedMethod, otherInheritedMethod);
			return;
		}
	}

	// the 2 inherited methods clash because of a parameterized type overrides a raw type
	//		interface I { void foo(A a); }
	//		class Y { void foo(A a) {} }
	//		abstract class X extends Y implements I { }
	//		class A {}
	// in this case the 2 inherited methods clash because of type variables
	//		interface I {  void foo(T t); }
	//		class Y {  void foo(T t) {} }
	//		abstract class X extends Y implements I {}

	if (inheritedMethod.declaringClass.isInterface() || inheritedMethod.isStatic()) return;

	detectInheritedNameClash(inheritedMethod.original(), otherInheritedMethod.original());
}
void checkInheritedMethods(MethodBinding[] methods, int length) {
	int count = length;
	int[] skip = new int[count];
	nextMethod : for (int i = 0, l = length - 1; i < l; i++) {
		if (skip[i] == -1) continue nextMethod;
		MethodBinding method = methods[i];
		MethodBinding[] duplicates = null;
		for (int j = i + 1; j <= l; j++) {
			MethodBinding method2 = methods[j];
			if (method.declaringClass == method2.declaringClass && areMethodsCompatible(method, method2)) {
				skip[j] = -1;
				if (duplicates == null)
					duplicates = new MethodBinding[length];
				duplicates[j] = method2;
			}
		}
		if (duplicates != null) {
			// found an inherited ParameterizedType that defines duplicate methods
			// if all methods are abstract or more than 1 concrete method exists, then consider them to be duplicates
			// if a single concrete method 'implements' the abstract methods, then do not report a duplicate error
			int concreteCount = method.isAbstract() ? 0 : 1;
			MethodBinding methodToKeep = method; // if a concrete method exists, keep it, otherwise keep the first method
			for (int m = 0, s = duplicates.length; m < s; m++) {
				if (duplicates[m] != null) {
					if (!duplicates[m].isAbstract()) {
						methodToKeep = duplicates[m];
						concreteCount++;
					}
				}
			}
			if (concreteCount != 1) {
				for (int m = 0, s = duplicates.length; m < s; m++) {
					if (duplicates[m] != null) {
						problemReporter().duplicateInheritedMethods(this.type, method, duplicates[m]);
						count--;
						if (methodToKeep == duplicates[m])
							methods[i] = null;
						else
							methods[m] = null;
					}
				}
			}
		}
	}
	if (count < length) {
		if (count == 1) return; // no need to continue since only 1 inherited method is left
		MethodBinding[] newMethods = new MethodBinding[count];
		for (int i = length; --i >= 0;)
			if (methods[i] != null)
				newMethods[--count] = methods[i];
		methods = newMethods;
		length = newMethods.length;
	}

	super.checkInheritedMethods(methods, length);
}
boolean checkInheritedReturnTypes(MethodBinding method, MethodBinding otherMethod) {
	if (areReturnTypesCompatible(method, otherMethod)) return true;

	if (!this.type.isInterface())
		if (method.declaringClass.isClass() || !this.type.implementsInterface(method.declaringClass, false))
			if (otherMethod.declaringClass.isClass() || !this.type.implementsInterface(otherMethod.declaringClass, false))
				return true; // do not complain since the superclass already got blamed

	// check to see if this is just a warning, if so report it & skip to next method
	if (isUnsafeReturnTypeOverride(method, otherMethod)) {
		if (!method.declaringClass.implementsInterface(otherMethod.declaringClass, false))
			problemReporter(method).unsafeReturnTypeOverride(method, otherMethod, this.type);
		return true;
	}

	return false;
}
void checkMethods() {
	boolean mustImplementAbstractMethods = mustImplementAbstractMethods();
	boolean skipInheritedMethods = mustImplementAbstractMethods && canSkipInheritedMethods(); // have a single concrete superclass so only check overridden methods
	char[][] methodSelectors = this.inheritedMethods.keyTable;
	nextSelector : for (int s = methodSelectors.length; --s >= 0;) {
		if (methodSelectors[s] == null) continue nextSelector;

		MethodBinding[] current = (MethodBinding[]) this.currentMethods.get(methodSelectors[s]);
		if (current == null && skipInheritedMethods)
			continue nextSelector;

		MethodBinding[] inherited = (MethodBinding[]) this.inheritedMethods.valueTable[s];
		if (inherited.length == 1 && current == null) { // handle the common case
			if (mustImplementAbstractMethods && inherited[0].isAbstract())
				checkAbstractMethod(inherited[0]);
			continue nextSelector;
		}

		int index = -1;
		int inheritedLength = inherited.length;
		MethodBinding[] matchingInherited = new MethodBinding[inherited.length];
		MethodBinding[] foundMatch = new MethodBinding[inherited.length]; // null is no match, otherwise value is matching currentMethod
		if (current != null) {
			for (int i = 0, length1 = current.length; i < length1; i++) {
				MethodBinding currentMethod = current[i];
				MethodBinding[] nonMatchingInherited = null;
				for (int j = 0; j < inheritedLength; j++) {
					MethodBinding inheritedMethod = computeSubstituteMethod(inherited[j], currentMethod);
					if (inheritedMethod != null) {
						if (foundMatch[j] == null && isSubstituteParameterSubsignature(currentMethod, inheritedMethod)) {
							matchingInherited[++index] = inheritedMethod;
							foundMatch[j] = currentMethod;
						} else {
							// best place to check each currentMethod against each non-matching inheritedMethod
							checkForNameClash(currentMethod, inheritedMethod);
							if (inheritedLength > 1) {
								if (nonMatchingInherited == null)
									nonMatchingInherited = new MethodBinding[inheritedLength];
								nonMatchingInherited[j] = inheritedMethod;
							}
						}
					}
				}
				if (index >= 0) {
					// see addtional comments in https://bugs.eclipse.org/bugs/show_bug.cgi?id=122881
					// if (index > 0 && currentMethod.declaringClass.isInterface()) // only check when inherited methods are from interfaces
					//	checkInheritedReturnTypes(matchingInherited, index + 1);
					checkAgainstInheritedMethods(currentMethod, matchingInherited, index + 1, nonMatchingInherited); // pass in the length of matching
					while (index >= 0) matchingInherited[index--] = null; // clear the contents of the matching methods
				}
			}
		}

		// skip tracks which inherited methods have matched other inherited methods
		// either because they match the same currentMethod or match each other
		boolean[] skip = new boolean[inheritedLength];
		for (int i = 0; i < inheritedLength; i++) {
			if (skip[i]) continue;
			MethodBinding inheritedMethod = inherited[i];
			MethodBinding matchMethod = foundMatch[i];
			if (matchMethod == null)
				matchingInherited[++index] = inheritedMethod;
			for (int j = i + 1; j < inheritedLength; j++) {
				MethodBinding otherInheritedMethod = inherited[j];
				if (matchMethod == foundMatch[j] && matchMethod != null)
					continue; // both inherited methods matched the same currentMethod
				if (canSkipInheritedMethods(inheritedMethod, otherInheritedMethod))
					continue;
				otherInheritedMethod = computeSubstituteMethod(otherInheritedMethod, inheritedMethod);
				if (otherInheritedMethod != null) {
					if (inheritedMethod.declaringClass != otherInheritedMethod.declaringClass
						&& isSubstituteParameterSubsignature(inheritedMethod, otherInheritedMethod)) {
							if (index == -1)
								matchingInherited[++index] = inheritedMethod;
							if (foundMatch[j] == null)
								matchingInherited[++index] = otherInheritedMethod;
							skip[j] = true;
					} else if (matchMethod == null && foundMatch[j] == null) {
						checkInheritedMethods(inheritedMethod, otherInheritedMethod);
					}
				}
			}
			if (index == -1) continue;

			if (index > 0)
				checkInheritedMethods(matchingInherited, index + 1); // pass in the length of matching
			else if (mustImplementAbstractMethods && matchingInherited[0].isAbstract())
				checkAbstractMethod(matchingInherited[0]);
			while (index >= 0) matchingInherited[index--] = null; // clear the previous contents of the matching methods
		}
	}
}
void checkTypeVariableMethods(TypeParameter typeParameter) {
	char[][] methodSelectors = this.inheritedMethods.keyTable;
	nextSelector : for (int s = methodSelectors.length; --s >= 0;) {
		if (methodSelectors[s] == null) continue nextSelector;
		MethodBinding[] inherited = (MethodBinding[]) this.inheritedMethods.valueTable[s];
		if (inherited.length == 1) continue nextSelector;

		int index = -1;
		MethodBinding[] matchingInherited = new MethodBinding[inherited.length];
		for (int i = 0, length = inherited.length; i < length; i++) {
			while (index >= 0) matchingInherited[index--] = null; // clear the previous contents of the matching methods
			MethodBinding inheritedMethod = inherited[i];
			if (inheritedMethod != null) {
				matchingInherited[++index] = inheritedMethod;
				for (int j = i + 1; j < length; j++) {
					MethodBinding otherInheritedMethod = inherited[j];
					if (canSkipInheritedMethods(inheritedMethod, otherInheritedMethod))
						continue;
					otherInheritedMethod = computeSubstituteMethod(otherInheritedMethod, inheritedMethod);
					if (otherInheritedMethod != null && isSubstituteParameterSubsignature(inheritedMethod, otherInheritedMethod)) {
						matchingInherited[++index] = otherInheritedMethod;
						inherited[j] = null; // do not want to find it again
					}
				}
			}
			if (index > 0) {
				MethodBinding first = matchingInherited[0];
				int count = index + 1;
				while (--count > 0 && areReturnTypesCompatible(first, matchingInherited[count])){/*empty*/}
				if (count > 0) {  // All inherited methods do NOT have the same vmSignature
					problemReporter().inheritedMethodsHaveIncompatibleReturnTypes(typeParameter, matchingInherited, index + 1);
					continue nextSelector;
				}
			}
		}
	}
}
MethodBinding computeSubstituteMethod(MethodBinding inheritedMethod, MethodBinding currentMethod) {
	if (inheritedMethod == null) return null;
	if (currentMethod.parameters.length != inheritedMethod.parameters.length) return null; // no match

	// due to hierarchy & compatibility checks, we need to ensure these 2 methods are resolved
	if (currentMethod.declaringClass instanceof BinaryTypeBinding)
		((BinaryTypeBinding) currentMethod.declaringClass).resolveTypesFor(currentMethod);
	if (inheritedMethod.declaringClass instanceof BinaryTypeBinding)
		((BinaryTypeBinding) inheritedMethod.declaringClass).resolveTypesFor(inheritedMethod);

	TypeVariableBinding[] inheritedTypeVariables = inheritedMethod.typeVariables;
	int inheritedLength = inheritedTypeVariables.length;
	if (inheritedLength == 0) return inheritedMethod; // no substitution needed
	TypeVariableBinding[] typeVariables = currentMethod.typeVariables;
	int length = typeVariables.length;
	if (length == 0)
		return inheritedMethod.asRawMethod(this.environment);
	if (length != inheritedLength)
		return inheritedMethod; // no match JLS 8.4.2

	// interface I {  void foo(T t); }
	// class X implements I { public  void foo(T t) {} }
	// for the above case, we do not want to answer the substitute method since its not a match
	TypeBinding[] arguments = new TypeBinding[length];
	System.arraycopy(typeVariables, 0, arguments, 0, length);
	ParameterizedGenericMethodBinding substitute =
		this.environment.createParameterizedGenericMethod(inheritedMethod, arguments);
	for (int i = 0; i < inheritedLength; i++) {
		TypeVariableBinding inheritedTypeVariable = inheritedTypeVariables[i];
		TypeBinding argument = arguments[i];
		if (argument instanceof TypeVariableBinding) {
			TypeVariableBinding typeVariable = (TypeVariableBinding) argument;
			if (typeVariable.firstBound == inheritedTypeVariable.firstBound) {
				if (typeVariable.firstBound == null)
					continue; // both are null
			} else if (typeVariable.firstBound != null && inheritedTypeVariable.firstBound != null) {
				if (typeVariable.firstBound.isClass() != inheritedTypeVariable.firstBound.isClass())
					return inheritedMethod; // not a match
			}
			if (Scope.substitute(substitute, inheritedTypeVariable.superclass) != typeVariable.superclass)
				return inheritedMethod; // not a match
			int interfaceLength = inheritedTypeVariable.superInterfaces.length;
			ReferenceBinding[] interfaces = typeVariable.superInterfaces;
			if (interfaceLength != interfaces.length)
				return inheritedMethod; // not a match
			// TODO (kent) another place where we expect the superinterfaces to be in the exact same order
			next : for (int j = 0; j < interfaceLength; j++) {
				TypeBinding superType = Scope.substitute(substitute, inheritedTypeVariable.superInterfaces[j]);
				for (int k = 0; k < interfaceLength; k++)
					if (superType == interfaces[k])
						continue next;
				return inheritedMethod; // not a match
			}
		} else if (inheritedTypeVariable.boundCheck(substitute, argument) != TypeConstants.OK) {
	    	return inheritedMethod;
		}
	}
   return substitute;
}
boolean detectInheritedNameClash(MethodBinding inherited, MethodBinding otherInherited) {
	if (!inherited.areParameterErasuresEqual(otherInherited))
		return false;
	if (this.environment.globalOptions.sourceLevel < ClassFileConstants.JDK1_7) {
		// with fix for http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6182950
		// we now ignore return types when detecting name clashes
		// FYI for now we will only make this change when compliance is set to 1.7 or higher
		if (inherited.returnType.erasure() != otherInherited.returnType.erasure())
			return false;
	}
	// skip it if otherInherited is defined by a subtype of inherited's declaringClass
	if (inherited.declaringClass.erasure() != otherInherited.declaringClass.erasure())
		if (inherited.declaringClass.findSuperTypeOriginatingFrom(otherInherited.declaringClass) != null)
			return false;

	problemReporter().inheritedMethodsHaveNameClash(this.type, inherited, otherInherited);
	return true;
}
boolean detectNameClash(MethodBinding current, MethodBinding inherited) {
	MethodBinding original = inherited.original(); // can be the same as inherited
	if (!current.areParameterErasuresEqual(original))
		return false;
	if (this.environment.globalOptions.sourceLevel < ClassFileConstants.JDK1_7) {
		// with fix for http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6182950
		// we now ignore return types when detecting name clashes
		// FYI for now we will only make this change when compliance is set to 1.7 or higher
		if (current.returnType.erasure() != original.returnType.erasure())
			return false;
	}

	problemReporter(current).methodNameClash(current, inherited.declaringClass.isRawType() ? inherited : original);
	return true;
}
public boolean doesMethodOverride(MethodBinding method, MethodBinding inheritedMethod) {
	return couldMethodOverride(method, inheritedMethod) && areMethodsCompatible(method, inheritedMethod);
}
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);
}
SimpleSet findSuperinterfaceCollisions(ReferenceBinding superclass, ReferenceBinding[] superInterfaces) {
	ReferenceBinding[] interfacesToVisit = null;
	int nextPosition = 0;
	ReferenceBinding[] itsInterfaces = superInterfaces;
	if (itsInterfaces != Binding.NO_SUPERINTERFACES) {
		nextPosition = itsInterfaces.length;
		interfacesToVisit = itsInterfaces;
	}

	boolean isInconsistent = this.type.isHierarchyInconsistent();
	ReferenceBinding superType = superclass;
	while (superType != null && superType.isValidBinding()) {
		isInconsistent |= superType.isHierarchyInconsistent();
		if ((itsInterfaces = superType.superInterfaces()) != Binding.NO_SUPERINTERFACES) {
			if (interfacesToVisit == null) {
				interfacesToVisit = itsInterfaces;
				nextPosition = interfacesToVisit.length;
			} else {
				int itsLength = itsInterfaces.length;
				if (nextPosition + itsLength >= interfacesToVisit.length)
					System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition);
				nextInterface : for (int a = 0; a < itsLength; a++) {
					ReferenceBinding next = itsInterfaces[a];
					for (int b = 0; b < nextPosition; b++)
						if (next == interfacesToVisit[b]) continue nextInterface;
					interfacesToVisit[nextPosition++] = next;
				}
			}
		}
		superType = superType.superclass();
	}

	for (int i = 0; i < nextPosition; i++) {
		superType = interfacesToVisit[i];
		if (superType.isValidBinding()) {
			isInconsistent |= superType.isHierarchyInconsistent();
			if ((itsInterfaces = superType.superInterfaces()) != Binding.NO_SUPERINTERFACES) {
				int itsLength = itsInterfaces.length;
				if (nextPosition + itsLength >= interfacesToVisit.length)
					System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition);
				nextInterface : for (int a = 0; a < itsLength; a++) {
					ReferenceBinding next = itsInterfaces[a];
					for (int b = 0; b < nextPosition; b++)
						if (next == interfacesToVisit[b]) continue nextInterface;
					interfacesToVisit[nextPosition++] = next;
				}
			}
		}
	}

	if (!isInconsistent) return null; // hierarchy is consistent so no collisions are possible
	SimpleSet copy = null;
	for (int i = 0; i < nextPosition; i++) {
		ReferenceBinding current = interfacesToVisit[i];
		if (current.isValidBinding()) {
			TypeBinding erasure = current.erasure();
			for (int j = i + 1; j < nextPosition; j++) {
				ReferenceBinding next = interfacesToVisit[j];
				if (next.isValidBinding() && next.erasure() == erasure) {
					if (copy == null)
						copy = new SimpleSet(nextPosition);
					copy.add(interfacesToVisit[i]);
					copy.add(interfacesToVisit[j]);
				}
			}
		}
	}
	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())
		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 // keep around to produce bridge methods
		&& doesMethodOverride(existingMethod, inheritedMethod);
}
public boolean isMethodSubsignature(MethodBinding method, MethodBinding inheritedMethod) {
	if (!org.eclipse.jdt.core.compiler.CharOperation.equals(method.selector, inheritedMethod.selector))
		return false;

	// need to switch back to the original if the method is from a ParameterizedType
	if (method.declaringClass.isParameterizedType())
		method = method.original();

	inheritedMethod = inheritedMethod.original();
	TypeBinding match = method.declaringClass.findSuperTypeOriginatingFrom(inheritedMethod.declaringClass);
	if ((match instanceof ReferenceBinding) && match != inheritedMethod.declaringClass) {
		MethodBinding[] superMethods = ((ReferenceBinding) match).getMethods(inheritedMethod.selector);
		for (int i = 0, length = superMethods.length; i < length; i++)
			if (superMethods[i].original() == inheritedMethod.original())
				return isParameterSubsignature(method, superMethods[i]);
	}

	return isParameterSubsignature(method, inheritedMethod);
}
boolean isParameterSubsignature(MethodBinding method, MethodBinding inheritedMethod) {
	MethodBinding substitute = computeSubstituteMethod(inheritedMethod, method);
	return substitute != null && isSubstituteParameterSubsignature(method, substitute);
}
// if method "overrides" substituteMethod then we can skip over substituteMethod while resolving a message send
// if it does not then a name clash error is likely
boolean isSubstituteParameterSubsignature(MethodBinding method, MethodBinding substituteMethod) {
	if (!areParametersEqual(method, substituteMethod)) {
		// method can still override substituteMethod in cases like :
		//  void c(U u) {}
		// @Override void c(Number n) {}
		// but method cannot have a "generic-enabled" parameter type
		if (substituteMethod.hasSubstitutedParameters() && method.areParameterErasuresEqual(substituteMethod))
			return method.typeVariables == Binding.NO_TYPE_VARIABLES && !hasGenericParameter(method);

		// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=279836
		if (method.declaringClass.isRawType() && substituteMethod.declaringClass.isRawType())
			if (method.hasSubstitutedParameters() && substituteMethod.hasSubstitutedParameters())
				return areMethodsCompatible(method, substituteMethod);

		return false;
	}

	if (substituteMethod instanceof ParameterizedGenericMethodBinding) {
		if (method.typeVariables != Binding.NO_TYPE_VARIABLES)
			return !((ParameterizedGenericMethodBinding) substituteMethod).isRaw;
		// since substituteMethod has substituted type variables, method cannot have a generic signature AND no variables -> its a name clash if it does
		return !hasGenericParameter(method);
	}

	// if method has its own variables, then substituteMethod failed bounds check in computeSubstituteMethod()
	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;
		TypeBinding[] inheritedParams = inheritedMethod.parameters;
		for (int i = 0, l = currentParams.length; i < l; i++)
			if (!areTypesEqual(currentParams[i], inheritedParams[i]))
				return true;
	}
	if (currentMethod.typeVariables == Binding.NO_TYPE_VARIABLES
		&& inheritedMethod.original().typeVariables != Binding.NO_TYPE_VARIABLES
		&& currentMethod.returnType.erasure().findSuperTypeOriginatingFrom(inheritedMethod.returnType.erasure()) != null) {
			return true;
	}
	return false;
}
boolean reportIncompatibleReturnTypeError(MethodBinding currentMethod, MethodBinding inheritedMethod) {
	if (isUnsafeReturnTypeOverride(currentMethod, inheritedMethod)) {
		problemReporter(currentMethod).unsafeReturnTypeOverride(currentMethod, inheritedMethod, this.type);
		return false;
	}
	return super.reportIncompatibleReturnTypeError(currentMethod, inheritedMethod);
}
void verify() {
	if (this.type.isAnnotationType())
		this.type.detectAnnotationCycle();

	super.verify();

	for (int i = this.type.typeVariables.length; --i >= 0;) {
		TypeVariableBinding var = this.type.typeVariables[i];
		// must verify bounds if the variable has more than 1
		if (var.superInterfaces == Binding.NO_SUPERINTERFACES) continue;
		if (var.superInterfaces.length == 1 && var.superclass.id == TypeIds.T_JavaLangObject) continue;

		this.currentMethods = new HashtableOfObject(0);
		ReferenceBinding superclass = var.superclass();
		if (superclass.kind() == Binding.TYPE_PARAMETER)
			superclass = (ReferenceBinding) superclass.erasure();
		ReferenceBinding[] itsInterfaces = var.superInterfaces();
		ReferenceBinding[] superInterfaces = new ReferenceBinding[itsInterfaces.length];
		for (int j = itsInterfaces.length; --j >= 0;) {
			superInterfaces[j] = itsInterfaces[j].kind() == Binding.TYPE_PARAMETER
				? (ReferenceBinding) itsInterfaces[j].erasure()
				: itsInterfaces[j];
		}
		computeInheritedMethods(superclass, superInterfaces);
		checkTypeVariableMethods(this.type.scope.referenceContext.typeParameters[i]);
	}
}
}