1 /*******************************************************************************
2 * Copyright (c) 2000, 2004 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.lookup;
13 import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
15 class MethodVerifier15 extends MethodVerifier {
17 MethodVerifier15(LookupEnvironment environment) {
20 boolean areMethodsEqual(MethodBinding one, MethodBinding substituteTwo) {
21 TypeBinding[] oneParams = one.parameters;
22 TypeBinding[] twoParams = substituteTwo.parameters;
23 boolean checkParameters = false;
24 if (oneParams != twoParams) {
25 int length = oneParams.length;
26 if (length != twoParams.length) return false; // no match
28 for (int i = 0; i < length; i++) {
29 if (oneParams[i] != twoParams[i]) {
30 checkParameters |= oneParams[i].leafComponentType().isParameterizedType();
31 if (!areTypesEqual(oneParams[i], twoParams[i])) {
32 while (!checkParameters && ++i < length)
33 checkParameters |= oneParams[i].leafComponentType().isParameterizedType();
34 if (one.areParameterErasuresEqual(substituteTwo)) // at least one parameter may cause a name clash
35 detectNameClash(one, substituteTwo, checkParameters);
36 return false; // no match but needed to check for a name clash
41 return !detectNameClash(one, substituteTwo, checkParameters);
43 boolean areReturnTypesEqual(MethodBinding one, MethodBinding substituteTwo) {
44 if (one.returnType == substituteTwo.returnType) return true;
46 // methods from classes are always before methods from interfaces
47 if (one.declaringClass.isClass() || one.declaringClass.implementsInterface(substituteTwo.declaringClass, true))
48 return one.returnType.isCompatibleWith(substituteTwo.returnType);
50 if (substituteTwo.declaringClass.implementsInterface(one.declaringClass, true))
51 return substituteTwo.returnType.isCompatibleWith(one.returnType);
53 // unrelated interfaces... one must be a subtype of the other
54 return one.returnType.isCompatibleWith(substituteTwo.returnType)
55 || substituteTwo.returnType.isCompatibleWith(one.returnType);
57 boolean areTypesEqual(TypeBinding one, TypeBinding two) {
58 if (one == two) return true;
61 case Binding.PARAMETERIZED_TYPE :
62 case Binding.RAW_TYPE :
63 return one.isEquivalentTo(two);
64 // case Binding.TYPE_PARAMETER : // won't work for variables from different classes - need substitution
67 // Can skip this since we resolved each method before comparing it, see computeSubstituteMethod()
68 // if (one instanceof UnresolvedReferenceBinding)
69 // return ((UnresolvedReferenceBinding) one).resolvedType == two;
70 // if (two instanceof UnresolvedReferenceBinding)
71 // return ((UnresolvedReferenceBinding) two).resolvedType == one;
72 return false; // all other type bindings are identical
74 boolean canSkipInheritedMethods() {
75 if (this.type.superclass() != null)
76 if (this.type.superclass().isAbstract() || this.type.superclass().isParameterizedType())
78 return this.type.superInterfaces() == NoSuperInterfaces;
80 boolean canSkipInheritedMethods(MethodBinding one, MethodBinding two) {
81 return two == null // already know one is not null
82 || (one.declaringClass == two.declaringClass && !one.declaringClass.isParameterizedType());
84 void checkForBridgeMethod(MethodBinding currentMethod, MethodBinding inheritedMethod) {
85 MethodBinding originalInherited = inheritedMethod.original();
86 if (inheritedMethod != originalInherited) {
87 MethodBinding[] toCheck = (MethodBinding[]) this.currentMethods.get(currentMethod.selector);
88 if (toCheck.length > 1) {
89 // must check to see if a bridge method will collide with another current method (see 77861)
90 for (int i = 0, length = toCheck.length; i < length; i++) {
91 if (currentMethod != toCheck[i] && toCheck[i].areParameterErasuresEqual(originalInherited)) {
92 problemReporter(toCheck[i]).methodNameClash(toCheck[i], originalInherited); // bridge method will collide
99 // so the parameters are equal and the return type is compatible b/w the currentMethod & the substituted inheritedMethod
100 // then when do you need a bridge method?
101 if (originalInherited.returnType != currentMethod.returnType) {
102 switch (originalInherited.returnType.leafComponentType().kind()) {
103 case Binding.PARAMETERIZED_TYPE :
104 if (!currentMethod.returnType.leafComponentType().isParameterizedType())
105 problemReporter(currentMethod).unsafeReturnTypeOverride(currentMethod, originalInherited, ((MethodDeclaration) currentMethod.sourceMethod()).returnType);
107 case Binding.TYPE_PARAMETER :
108 if (!currentMethod.returnType.leafComponentType().isTypeVariable())
109 problemReporter(currentMethod).unsafeReturnTypeOverride(currentMethod, originalInherited, ((MethodDeclaration) currentMethod.sourceMethod()).returnType);
113 this.type.addSyntheticBridgeMethod(originalInherited, currentMethod);
115 void checkInheritedMethods(MethodBinding[] methods, int length) {
117 nextMethod : for (int i = 0, l = length - 1; i < l;) {
118 MethodBinding method = methods[i++];
119 for (int j = i; j <= l; j++) {
120 if (method.declaringClass == methods[j].declaringClass && doesMethodOverride(method, methods[j])) {
121 // found an inherited ParameterizedType that defines duplicate methods
122 problemReporter().duplicateInheritedMethods(this.type, method, methods[j]);
124 methods[i - 1] = null;
129 if (count < length) {
130 if (count == 1) return; // no need to continue since only 1 inherited method is left
131 MethodBinding[] newMethods = new MethodBinding[count];
132 for (int i = length; --i >= 0;)
133 if (methods[i] != null)
134 newMethods[--count] = methods[i];
135 methods = newMethods;
136 length = newMethods.length;
139 super.checkInheritedMethods(methods, length);
141 MethodBinding computeSubstituteMethod(MethodBinding inheritedMethod, MethodBinding currentMethod) {
142 if (inheritedMethod == null) return null;
144 // due to hierarchy & compatibility checks, we need to ensure these 2 methods are resolved
145 // should we push these tests to where they're needed? returnType.isCompatibleWith && parameter isEquivalentTo ?
146 if (currentMethod.declaringClass instanceof BinaryTypeBinding)
147 ((BinaryTypeBinding) currentMethod.declaringClass).resolveTypesFor(currentMethod);
148 if (inheritedMethod.declaringClass instanceof BinaryTypeBinding)
149 ((BinaryTypeBinding) inheritedMethod.declaringClass).resolveTypesFor(inheritedMethod);
151 TypeVariableBinding[] inheritedTypeVariables = inheritedMethod.typeVariables();
152 if (inheritedTypeVariables == NoTypeVariables) return inheritedMethod;
153 TypeVariableBinding[] typeVariables = currentMethod == null ? NoTypeVariables : currentMethod.typeVariables;
155 int inheritedLength = inheritedTypeVariables.length;
156 int length = typeVariables.length;
157 TypeBinding[] arguments = new TypeBinding[inheritedLength];
158 if (inheritedLength <= length) {
159 System.arraycopy(typeVariables, 0, arguments, 0, inheritedLength);
161 System.arraycopy(typeVariables, 0, arguments, 0, length);
162 for (int i = length; i < inheritedLength; i++)
163 arguments[i] = inheritedTypeVariables[i].erasure();
165 ParameterizedGenericMethodBinding substitute =
166 new ParameterizedGenericMethodBinding(inheritedMethod, arguments, this.environment);
167 for (int i = 0; i < inheritedLength; i++)
168 if (!inheritedTypeVariables[i].boundCheck(substitute, arguments[i]))
169 return inheritedMethod; // incompatible due to bound check
172 boolean detectNameClash(MethodBinding one, MethodBinding substituteTwo, boolean checkParameters) {
173 if (doTypeVariablesClash(one, substituteTwo) || (checkParameters && doParametersClash(one, substituteTwo))) {
174 if (this.type == one.declaringClass)
175 problemReporter(one).methodNameClash(one, substituteTwo);
177 problemReporter().inheritedMethodsHaveNameClash(this.type, one, substituteTwo);
182 public boolean doesMethodOverride(MethodBinding method, MethodBinding inheritedMethod) {
183 return super.doesMethodOverride(method, computeSubstituteMethod(inheritedMethod, method));
185 boolean doParametersClash(MethodBinding one, MethodBinding substituteTwo) {
186 // must check each parameter pair to see if parameterized types are compatible
187 TypeBinding[] oneParams = one.parameters;
188 TypeBinding[] twoParams = substituteTwo.parameters;
189 for (int i = 0, l = oneParams.length; i < l; i++) {
190 if (oneParams[i] == twoParams[i]) continue;
191 if (!oneParams[i].leafComponentType().isParameterizedType()) continue;
193 if (!twoParams[i].leafComponentType().isParameterizedType()
194 || !oneParams[i].isEquivalentTo(twoParams[i])
195 || !twoParams[i].isEquivalentTo(oneParams[i])) {
201 boolean doTypeVariablesClash(MethodBinding one, MethodBinding substituteTwo) {
202 TypeBinding[] currentVars = one.typeVariables;
203 TypeBinding[] inheritedVars = substituteTwo.original().typeVariables;
204 return currentVars.length != inheritedVars.length
205 && currentVars.length > 0 && inheritedVars.length > 0; // must match unless all are replaced
206 // && currentVars.length > 0;
208 boolean hasBoundedParameters(ParameterizedTypeBinding parameterizedType) {
209 TypeBinding[] arguments = parameterizedType.arguments;
210 if (arguments == null) return false;
212 nextArg : for (int i = 0, l = arguments.length; i < l; i++) {
213 if (arguments[i].isWildcard())
214 if (((WildcardBinding) arguments[i]).kind == org.eclipse.jdt.internal.compiler.ast.Wildcard.UNBOUND)
216 if (arguments[i].isTypeVariable())
217 if (((TypeVariableBinding) arguments[i]).firstBound == null)
223 boolean isInterfaceMethodImplemented(MethodBinding inheritedMethod, MethodBinding existingMethod, ReferenceBinding superType) {
224 inheritedMethod = computeSubstituteMethod(inheritedMethod, existingMethod);
225 return inheritedMethod.returnType == existingMethod.returnType
226 && super.isInterfaceMethodImplemented(inheritedMethod, existingMethod, superType);