| /******************************************************************************* |
| * Copyright (c) 2000, 2013 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 |
| * Stephan Herrmann - Contributions for |
| * bug 186342 - [compiler][null] Using annotations for null checking |
| * bug 365519 - editorial cleanup after bug 186342 and bug 365387 |
| * bug 388281 - [compiler][null] inheritance of null annotations as an option |
| * bug 388795 - [compiler] detection of name clash depends on order of super interfaces |
| * bug 388739 - [1.8][compiler] consider default methods when detecting whether a class needs to be declared abstract |
| * bug 390883 - [1.8][compiler] Unable to override default method |
| * bug 395002 - Self bound generic class doesn't resolve bounds properly for wildcards for certain parametrisation. |
| * bug 401246 - [1.8][compiler] abstract class method should now trump conflicting default methods |
| * bug 401796 - [1.8][compiler] don't treat default methods as overriding an independent inherited abstract method |
| * bug 403867 - [1.8][compiler] Suspect error about duplicate default methods |
| * bug 391376 - [1.8] check interaction of default methods with bridge methods and generics |
| * bug 395681 - [compiler] Improve simulation of javac6 behavior from bug 317719 after fixing bug 388795 |
| * bug 409473 - [compiler] JDT cannot compile against JRE 1.8 |
| * Bug 420080 - [1.8] Overridden Default method is reported as duplicated |
| * Bug 404690 - [1.8][compiler] revisit bridge generation after VM bug is fixed |
| * Bug 410325 - [1.7][compiler] Generified method override different between javac and eclipse compiler |
| *******************************************************************************/ |
| package org.aspectj.org.eclipse.jdt.internal.compiler.lookup; |
| |
| |
| import java.util.Arrays; |
| |
| import org.aspectj.org.eclipse.jdt.internal.compiler.ast.ASTNode; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Argument; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeParameter; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeReference; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.impl.CompilerOptions; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.problem.ProblemSeverities; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.util.HashtableOfObject; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.util.SimpleSet; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.util.Sorting; |
| |
| class MethodVerifier15 extends MethodVerifier { |
| |
| MethodVerifier15(LookupEnvironment environment) { |
| super(environment); |
| } |
| // Given `overridingMethod' which overrides `inheritedMethod' answer whether some subclass method that |
| // differs in erasure from overridingMethod could override `inheritedMethod' |
| protected boolean canOverridingMethodDifferInErasure(MethodBinding overridingMethod, MethodBinding inheritedMethod) { |
| if (overridingMethod.areParameterErasuresEqual(inheritedMethod)) |
| return false; // no further change in signature is possible due to parameterization. |
| if (overridingMethod.declaringClass.isRawType()) |
| return false; // no parameterization is happening anyways. |
| return true; |
| } |
| 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 |
| || (TypeBinding.equalsEquals(one.declaringClass, two.declaringClass) && !one.declaringClass.isParameterizedType()); |
| } |
| void checkConcreteInheritedMethod(MethodBinding concreteMethod, MethodBinding[] abstractMethods) { |
| super.checkConcreteInheritedMethod(concreteMethod, abstractMethods); |
| boolean analyseNullAnnotations = this.environment.globalOptions.isAnnotationBasedNullAnalysisEnabled; |
| // TODO (stephan): unclear if this srcMethod is actually needed |
| AbstractMethodDeclaration srcMethod = null; |
| if (analyseNullAnnotations && this.type.equals(concreteMethod.declaringClass)) // is currentMethod from the current type? |
| srcMethod = concreteMethod.sourceMethod(); |
| boolean hasNonNullDefault = concreteMethod.hasNonNullDefault(); |
| 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 (TypeBinding.notEquals(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 |
| // skip generation of bridge method for current class & method if an equivalent |
| // bridge will be/would have been generated in the context of the super class since |
| // the bridge itself will be inherited. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=298362 |
| if (originalInherited.declaringClass.isInterface()) { |
| if ((TypeBinding.equalsEquals(concreteMethod.declaringClass, this.type.superclass) && this.type.superclass.isParameterizedType() && !areMethodsCompatible(concreteMethod, originalInherited)) |
| || this.type.superclass.erasure().findSuperTypeOriginatingFrom(originalInherited.declaringClass) == null) |
| this.type.addSyntheticBridgeMethod(originalInherited, concreteMethod.original()); |
| } |
| if (analyseNullAnnotations && !concreteMethod.isStatic() && !abstractMethod.isStatic()) { |
| checkNullSpecInheritance(concreteMethod, srcMethod, hasNonNullDefault, true, abstractMethod, this.type.scope, null); |
| } |
| } |
| } |
| 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 (TypeBinding.notEquals(originalInherited.returnType, currentMethod.returnType)) |
| if (!isAcceptableReturnTypeOverride(currentMethod, inheritedMethod)) |
| problemReporter(currentMethod).unsafeReturnTypeOverride(currentMethod, originalInherited, this.type); |
| |
| MethodBinding bridge = this.type.addSyntheticBridgeMethod(originalInherited, currentMethod.original()); |
| if (bridge != null) { |
| for (int i = 0, l = allInheritedMethods == null ? 0 : allInheritedMethods.length; i < l; i++) { |
| if (allInheritedMethods[i] != null && detectInheritedNameClash(originalInherited, allInheritedMethods[i].original())) |
| return; |
| } |
| // See if the new bridge clashes with any of the user methods of the class. For this check |
| // we should check for "method descriptor clash" and not just "method signature clash". Really |
| // what we are checking is whether there is a contention for the method dispatch table slot. |
| // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=293615. |
| MethodBinding[] current = (MethodBinding[]) this.currentMethods.get(bridge.selector); |
| for (int i = current.length - 1; i >= 0; --i) { |
| final MethodBinding thisMethod = current[i]; |
| if (thisMethod.areParameterErasuresEqual(bridge) && TypeBinding.equalsEquals(thisMethod.returnType.erasure(), bridge.returnType.erasure())) { |
| // use inherited method for problem reporting. |
| problemReporter(thisMethod).methodNameClash(thisMethod, inheritedMethod.declaringClass.isRawType() ? inheritedMethod : inheritedMethod.original(), ProblemSeverities.Error); |
| 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<E extends Comparable> { abstract void test(E element); } |
| // class A extends AA<Integer> { public void test(Integer i) {} } |
| // public class B extends A { public void test(Comparable i) {} } |
| // interface I<E extends Comparable> { void test(E element); } |
| // class A implements I<Integer> { public void test(Integer i) {} } |
| // public class B extends A { public void test(Comparable i) {} } |
| |
| // abstract class Y implements EqualityComparable<Integer>, Equivalent<String> { |
| // public boolean equalTo(Integer other) { return true; } |
| // } |
| // interface Equivalent<T> { boolean equalTo(T other); } |
| // interface EqualityComparable<T> { boolean equalTo(T other); } |
| |
| // class Y implements EqualityComparable, Equivalent<String>{ |
| // public boolean equalTo(String other) { return true; } |
| // public boolean equalTo(Object other) { return true; } |
| // } |
| // interface Equivalent<T> { boolean equalTo(T other); } |
| // interface EqualityComparable { boolean equalTo(Object other); } |
| |
| // class A<T extends Number> { void m(T t) {} } |
| // class B<S extends Integer> extends A<S> { void m(S t) {}} |
| // class D extends B<Integer> { void m(Number t) {} void m(Integer t) {} } |
| |
| // inheritedMethods does not include I.test since A has a valid implementation |
| // interface I<E extends Comparable<E>> { void test(E element); } |
| // class A implements I<Integer> { public void test(Integer i) {} } |
| // class B extends A { public void test(Comparable i) {} } |
| |
| if (inheritedMethod.isStatic() || currentMethod.isStatic()) { |
| MethodBinding original = inheritedMethod.original(); // can be the same as inherited |
| if (this.type.scope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_7 && currentMethod.areParameterErasuresEqual(original)) { |
| problemReporter(currentMethod).methodNameClashHidden(currentMethod, inheritedMethod.declaringClass.isRawType() ? inheritedMethod : original); |
| } |
| return; // no chance of bridge method's clashing |
| } |
| |
| if (!detectNameClash(currentMethod, inheritedMethod, false)) { // 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 (TypeBinding.notEquals(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, true)) |
| 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 (TypeBinding.equalsEquals(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, true)) |
| 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 (TypeBinding.equalsEquals(next, interfacesToVisit[b])) continue nextInterface; |
| interfacesToVisit[nextPosition++] = next; |
| } |
| } |
| } |
| } |
| } |
| } |
| void checkInheritedMethods(MethodBinding inheritedMethod, MethodBinding otherInheritedMethod) { |
| |
| // 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<String> a) {} } |
| // abstract class X extends Y implements I { } |
| // class A<T> {} |
| // in this case the 2 inherited methods clash because of type variables |
| // interface I { <T, S> void foo(T t); } |
| // class Y { <T> void foo(T t) {} } |
| // abstract class X extends Y implements I {} |
| |
| if (inheritedMethod.isStatic()) return; |
| if (this.environment.globalOptions.complianceLevel < ClassFileConstants.JDK1_7 && inheritedMethod.declaringClass.isInterface()) |
| return; // JDK7 checks for name clashes in interface inheritance, while JDK6 and below don't. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=354229 |
| |
| detectInheritedNameClash(inheritedMethod.original(), otherInheritedMethod.original()); |
| } |
| // 8.4.8.4 |
| void checkInheritedMethods(MethodBinding[] methods, int length, boolean[] isOverridden, boolean[] isInherited) { |
| boolean continueInvestigation = true; |
| MethodBinding concreteMethod = null; |
| MethodBinding abstractSuperClassMethod = null; |
| boolean playingTrump = false; // invariant: playingTrump => (concreteMethod == null) |
| for (int i = 0; i < length; i++) { |
| if (!methods[i].declaringClass.isInterface() |
| && TypeBinding.notEquals(methods[i].declaringClass, this.type) |
| && methods[i].isAbstract()) |
| { |
| abstractSuperClassMethod = methods[i]; |
| break; |
| } |
| } |
| for (int i = 0; i < length; i++) { |
| // methods not inherited as of 8.4.8 cannot create a name clash, |
| // but could still cause errors against return types etc. (below) |
| if (isInherited[i] && !methods[i].isAbstract()) { |
| // 8.4.8.4 defines an exception for default methods if |
| // (a) there exists an abstract method declared in a superclass of C and inherited by C |
| // (b) that is override-equivalent with the two methods. |
| if (methods[i].isDefaultMethod() |
| && abstractSuperClassMethod != null // condition (a) |
| && areParametersEqual(abstractSuperClassMethod, methods[i]) // condition (b)... |
| && concreteMethod == null) { |
| // skip, class method trumps this default method (concreteMethod remains null) |
| playingTrump = true; |
| } else { |
| playingTrump = false; |
| if (concreteMethod != null) { |
| // re-checking compatibility is needed for https://bugs.eclipse.org/346029 |
| if (isOverridden[i] && areMethodsCompatible(concreteMethod, methods[i])) { |
| continue; |
| } |
| // https://bugs.eclipse.org/195802 with https://bugs.eclipse.org/410325 |
| // If a replace method (from findReplacedMethod()) is the rawified version of another |
| // don't count this as duplicates: |
| // (Not asking ParameterizedGenericMethodBinding.isRawMethod(), |
| // because that is true only for methods of a RawTypeBinding, |
| // but here we look for rawness regarding the method's type variables). |
| if (TypeBinding.equalsEquals(concreteMethod.declaringClass, methods[i].declaringClass) |
| && concreteMethod.typeVariables.length != methods[i].typeVariables.length) |
| { |
| if (concreteMethod.typeVariables == Binding.NO_TYPE_VARIABLES |
| && concreteMethod.original() == methods[i]) |
| continue; |
| if (methods[i].typeVariables == Binding.NO_TYPE_VARIABLES |
| && methods[i].original() == concreteMethod) |
| continue; |
| } |
| |
| problemReporter().duplicateInheritedMethods(this.type, concreteMethod, methods[i]); |
| continueInvestigation = false; |
| } |
| concreteMethod = methods[i]; |
| } |
| } |
| } |
| if (continueInvestigation) { |
| if (playingTrump) { |
| // multiple abstract & default methods are OK on this branch, but then the class must be declared abstract: |
| if (!this.type.isAbstract()) { |
| problemReporter().abstractMethodMustBeImplemented(this.type, abstractSuperClassMethod); |
| return; |
| } |
| } else { |
| if (concreteMethod != null && concreteMethod.isDefaultMethod()) { |
| if (this.environment.globalOptions.complianceLevel >= ClassFileConstants.JDK1_8) { |
| if (!checkInheritedDefaultMethods(methods, length)) |
| return; |
| } |
| } |
| } |
| super.checkInheritedMethods(methods, length, isOverridden, isInherited); |
| } |
| } |
| boolean checkInheritedDefaultMethods(MethodBinding[] methods, int length) { |
| // JLS8 9.4.1 (interface) and 8.4.8.4 (class): |
| // default method clashes with other inherited method which is override-equivalent |
| if (length < 2) return true; |
| boolean ok = true; |
| findDefaultMethod: for (int i=0; i<length; i++) { |
| if (methods[i].isDefaultMethod()) { |
| findEquivalent: for (int j=0; j<length; j++) { |
| if (j == i) continue findEquivalent; |
| if (isMethodSubsignature(methods[i], methods[j])) { |
| if (!doesMethodOverride(methods[i], methods[j]) && !doesMethodOverride(methods[j], methods[i])) { |
| problemReporter().inheritedDefaultMethodConflictsWithOtherInherited(this.type, methods[i], methods[j]); |
| ok = false; |
| } |
| continue findDefaultMethod; |
| } |
| } |
| } |
| } |
| return ok; |
| } |
| boolean checkInheritedReturnTypes(MethodBinding method, MethodBinding otherMethod) { |
| if (areReturnTypesCompatible(method, otherMethod)) return true; |
| |
| /* We used to have some checks here to see if we would have already blamed the super type and if so avoid blaming |
| the current type again. I have gotten rid of them as they in fact short circuit error reporting in cases where |
| they should not. This means that occasionally we would report the error twice - the diagnostics is valid however, |
| albeit arguably redundant. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=334313. For an example of a test |
| where we do this extra reporting see org.aspectj.org.eclipse.jdt.core.tests.compiler.regression.MethodVerifyTest.test159() |
| */ |
| // 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 checkAgainstInheritedMethods(MethodBinding currentMethod, MethodBinding[] methods, int length, MethodBinding[] allInheritedMethods) |
| { |
| super.checkAgainstInheritedMethods(currentMethod, methods, length, allInheritedMethods); |
| CompilerOptions options = this.environment.globalOptions; |
| if (options.isAnnotationBasedNullAnalysisEnabled |
| && (currentMethod.tagBits & TagBits.IsNullnessKnown) == 0) |
| { |
| // if annotations are inherited these have been checked during STB.resolveTypesFor() (for methods explicit in this.type) |
| AbstractMethodDeclaration srcMethod = null; |
| if (this.type.equals(currentMethod.declaringClass)) // is currentMethod from the current type? |
| srcMethod = currentMethod.sourceMethod(); |
| boolean hasNonNullDefault = currentMethod.hasNonNullDefault(); |
| for (int i = length; --i >= 0;) |
| if (!currentMethod.isStatic() && !methods[i].isStatic()) |
| checkNullSpecInheritance(currentMethod, srcMethod, hasNonNullDefault, true, methods[i], this.type.scope, null); |
| } |
| } |
| |
| void checkNullSpecInheritance(MethodBinding currentMethod, AbstractMethodDeclaration srcMethod, |
| boolean hasNonNullDefault, boolean complain, MethodBinding inheritedMethod, Scope scope, InheritedNonNullnessInfo[] inheritedNonNullnessInfos) |
| { |
| complain &= !currentMethod.isConstructor(); |
| if (!hasNonNullDefault && !complain && !this.environment.globalOptions.inheritNullAnnotations) { |
| // nothing to be done, take the shortcut |
| currentMethod.tagBits |= TagBits.IsNullnessKnown; |
| return; |
| } |
| // in this context currentMethod can be inherited, too. Recurse if needed. |
| if (TypeBinding.notEquals(currentMethod.declaringClass, this.type) |
| && (currentMethod.tagBits & TagBits.IsNullnessKnown) == 0) |
| { |
| this.buddyImplicitNullAnnotationsVerifier.checkImplicitNullAnnotations(currentMethod, srcMethod, complain, scope); |
| } |
| super.checkNullSpecInheritance(currentMethod, srcMethod, hasNonNullDefault, complain, inheritedMethod, scope, inheritedNonNullnessInfos); |
| } |
| |
| void reportRawReferences() { |
| CompilerOptions compilerOptions = this.type.scope.compilerOptions(); |
| if (compilerOptions.sourceLevel < ClassFileConstants.JDK1_5 // shouldn't whine at all |
| || compilerOptions.reportUnavoidableGenericTypeProblems) { // must have already whined |
| return; |
| } |
| /* Code below is only for a method that does not override/implement a super type method. If it were to, |
| it would have been handled in checkAgainstInheritedMethods. |
| */ |
| Object [] methodArray = this.currentMethods.valueTable; |
| for (int s = methodArray.length; --s >= 0;) { |
| if (methodArray[s] == null) continue; |
| MethodBinding[] current = (MethodBinding[]) methodArray[s]; |
| for (int i = 0, length = current.length; i < length; i++) { |
| MethodBinding currentMethod = current[i]; |
| if ((currentMethod.modifiers & (ExtraCompilerModifiers.AccImplementing | ExtraCompilerModifiers.AccOverriding)) == 0) { |
| AbstractMethodDeclaration methodDecl = currentMethod.sourceMethod(); |
| if (methodDecl == null) return; |
| TypeBinding [] parameterTypes = currentMethod.parameters; |
| Argument[] arguments = methodDecl.arguments; |
| for (int j = 0, size = currentMethod.parameters.length; j < size; j++) { |
| TypeBinding parameterType = parameterTypes[j]; |
| Argument arg = arguments[j]; |
| if (parameterType.leafComponentType().isRawType() |
| && compilerOptions.getSeverity(CompilerOptions.RawTypeReference) != ProblemSeverities.Ignore |
| && (arg.type.bits & ASTNode.IgnoreRawTypeCheck) == 0) { |
| methodDecl.scope.problemReporter().rawTypeReference(arg.type, parameterType); |
| } |
| } |
| if (!methodDecl.isConstructor() && methodDecl instanceof MethodDeclaration) { |
| TypeReference returnType = ((MethodDeclaration) methodDecl).returnType; |
| TypeBinding methodType = currentMethod.returnType; |
| if (returnType != null) { |
| if (methodType.leafComponentType().isRawType() |
| && compilerOptions.getSeverity(CompilerOptions.RawTypeReference) != ProblemSeverities.Ignore |
| && (returnType.bits & ASTNode.IgnoreRawTypeCheck) == 0) { |
| methodDecl.scope.problemReporter().rawTypeReference(returnType, methodType); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| public void reportRawReferences(MethodBinding currentMethod, MethodBinding inheritedMethod) { |
| CompilerOptions compilerOptions = this.type.scope.compilerOptions(); |
| if (compilerOptions.sourceLevel < ClassFileConstants.JDK1_5 // shouldn't whine at all |
| || compilerOptions.reportUnavoidableGenericTypeProblems) { // must have already whined |
| return; |
| } |
| AbstractMethodDeclaration methodDecl = currentMethod.sourceMethod(); |
| if (methodDecl == null) return; |
| TypeBinding [] parameterTypes = currentMethod.parameters; |
| TypeBinding [] inheritedParameterTypes = inheritedMethod.parameters; |
| Argument[] arguments = methodDecl.arguments; |
| for (int j = 0, size = currentMethod.parameters.length; j < size; j++) { |
| TypeBinding parameterType = parameterTypes[j]; |
| TypeBinding inheritedParameterType = inheritedParameterTypes[j]; |
| Argument arg = arguments[j]; |
| if (parameterType.leafComponentType().isRawType()) { |
| if (inheritedParameterType.leafComponentType().isRawType()) { |
| arg.binding.tagBits |= TagBits.ForcedToBeRawType; |
| } else { |
| if (compilerOptions.getSeverity(CompilerOptions.RawTypeReference) != ProblemSeverities.Ignore |
| && (arg.type.bits & ASTNode.IgnoreRawTypeCheck) == 0) { |
| methodDecl.scope.problemReporter().rawTypeReference(arg.type, parameterType); |
| } |
| } |
| } |
| } |
| TypeReference returnType = null; |
| if (!methodDecl.isConstructor() && methodDecl instanceof MethodDeclaration && (returnType = ((MethodDeclaration) methodDecl).returnType) != null) { |
| final TypeBinding inheritedMethodType = inheritedMethod.returnType; |
| final TypeBinding methodType = currentMethod.returnType; |
| if (methodType.leafComponentType().isRawType()) { |
| if (inheritedMethodType.leafComponentType().isRawType()) { |
| // |
| } else { |
| if ((returnType.bits & ASTNode.IgnoreRawTypeCheck) == 0 |
| && compilerOptions.getSeverity(CompilerOptions.RawTypeReference) != ProblemSeverities.Ignore) { |
| methodDecl.scope.problemReporter().rawTypeReference(returnType, methodType); |
| } |
| } |
| } |
| } |
| } |
| |
| void checkMethods() { |
| boolean mustImplementAbstractMethods = mustImplementAbstractMethods(); |
| boolean skipInheritedMethods = mustImplementAbstractMethods && canSkipInheritedMethods(); // have a single concrete superclass so only check overridden methods |
| boolean isOrEnclosedByPrivateType = this.type.isOrEnclosedByPrivateType(); |
| 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]); |
| MethodBinding[] inherited = (MethodBinding[]) this.inheritedMethods.valueTable[s]; |
| // ensure that if we have a concrete method this shows up at position [0]: |
| inherited = Sorting.concreteFirst(inherited, inherited.length); |
| |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=296660, if current type is exposed, |
| // inherited methods of super classes are too. current != null case handled below. |
| if (current == null && !isOrEnclosedByPrivateType) { |
| int length = inherited.length; |
| for (int i = 0; i < length; i++){ |
| inherited[i].original().modifiers |= ExtraCompilerModifiers.AccLocallyUsed; |
| } |
| } |
| if (current == null && this.type.isPublic()) { |
| int length = inherited.length; |
| for (int i = 0; i < length; i++) { |
| MethodBinding inheritedMethod = inherited[i]; |
| if (inheritedMethod.isPublic() && (!inheritedMethod.declaringClass.isInterface() && !inheritedMethod.declaringClass.isPublic())) |
| this.type.addSyntheticBridgeMethod(inheritedMethod.original()); |
| } |
| } |
| |
| if (current == null && skipInheritedMethods) |
| continue nextSelector; |
| |
| 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[inheritedLength]; |
| MethodBinding[] foundMatch = new MethodBinding[inheritedLength]; // null is no match, otherwise value is matching currentMethod |
| |
| // skip tracks inherited methods which can be safely ignored for one of these reasons: |
| // - methods that have matched other inherited methods |
| // either because they match the same currentMethod or match each other |
| // - methods that are overridden by a current method |
| boolean[] skip = new boolean[inheritedLength]; |
| boolean[] isOverridden = new boolean[inheritedLength]; |
| boolean[] isInherited = new boolean[inheritedLength]; |
| Arrays.fill(isInherited, true); |
| 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) { |
| // AspectJ Extension - removing first condition here as it causes us to have problems when an ITD |
| // provides the implementation. Not the right solution |
| if (/*foundMatch[j] == null && */isSubstituteParameterSubsignature(currentMethod, inheritedMethod)) { |
| // already checked compatibility, do visibility etc. also indicate overriding? If so ignore inheritedMethod further downstream |
| isOverridden[j] = skip[j] = couldMethodOverride(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 |
| } |
| } |
| } |
| // first round: collect information into skip and isOverridden by comparing all pairs: |
| // (and perform some side effects : bridge methods & use flags) |
| for (int i = 0; i < inheritedLength; i++) { |
| MethodBinding matchMethod = foundMatch[i]; |
| |
| if (matchMethod == null && current != null && this.type.isPublic()) { // current == null case handled already. |
| MethodBinding inheritedMethod = inherited[i]; |
| if (inheritedMethod.isPublic() && (!inheritedMethod.declaringClass.isInterface() && !inheritedMethod.declaringClass.isPublic())) { |
| this.type.addSyntheticBridgeMethod(inheritedMethod.original()); |
| } |
| } |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=296660, if current type is exposed, |
| // inherited methods of super classes are too. current == null case handled already. |
| if (!isOrEnclosedByPrivateType && matchMethod == null && current != null) { |
| inherited[i].original().modifiers |= ExtraCompilerModifiers.AccLocallyUsed; |
| } |
| MethodBinding inheritedMethod = inherited[i]; |
| 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; |
| // Skip the otherInheritedMethod if it is completely replaced by inheritedMethod |
| // This elimination used to happen rather eagerly in computeInheritedMethods step |
| // itself earlier. (https://bugs.eclipse.org/bugs/show_bug.cgi?id=302358) |
| if (TypeBinding.notEquals(inheritedMethod.declaringClass, otherInheritedMethod.declaringClass)) { |
| // these method calls produce their effect as side-effects into skip and isOverridden: |
| if (isSkippableOrOverridden(inheritedMethod, otherInheritedMethod, skip, isOverridden, isInherited, j)) |
| continue; |
| if (isSkippableOrOverridden(otherInheritedMethod, inheritedMethod, skip, isOverridden, isInherited, i)) |
| continue; |
| } |
| } |
| } |
| // second round: collect and check matchingInherited, directly check methods with no replacing etc. |
| for (int i = 0; i < inheritedLength; i++) { |
| MethodBinding matchMethod = foundMatch[i]; |
| if (skip[i]) continue; |
| MethodBinding inheritedMethod = inherited[i]; |
| if (matchMethod == null) |
| matchingInherited[++index] = inheritedMethod; |
| for (int j = i + 1; j < inheritedLength; j++) { |
| if (foundMatch[j] == null) { |
| MethodBinding otherInheritedMethod = inherited[j]; |
| if (matchMethod == foundMatch[j] && matchMethod != null) |
| continue; // both inherited methods matched the same currentMethod |
| if (canSkipInheritedMethods(inheritedMethod, otherInheritedMethod)) |
| continue; |
| |
| MethodBinding replaceMatch; |
| if ((replaceMatch = findReplacedMethod(inheritedMethod, otherInheritedMethod)) != null) { |
| matchingInherited[++index] = replaceMatch; |
| skip[j] = true; |
| } else if ((replaceMatch = findReplacedMethod(otherInheritedMethod, inheritedMethod)) != null) { |
| matchingInherited[++index] = replaceMatch; |
| skip[j] = true; |
| } else if (matchMethod == null) { |
| // none replaced by the other, check these methods against each other now: |
| checkInheritedMethods(inheritedMethod, otherInheritedMethod); |
| } |
| } |
| } |
| if (index == -1) continue; |
| |
| if (index > 0) { |
| int length = index + 1; |
| boolean[] matchingIsOverridden; |
| boolean[] matchingIsInherited; |
| if (length != inheritedLength) { // transfer inherited & overridden status to align with subset of methods. |
| matchingIsOverridden = new boolean[length]; |
| matchingIsInherited = new boolean[length]; |
| for (int j = 0; j < length; j++) { |
| for (int k = 0; k < inheritedLength; k++) { |
| if (matchingInherited[j] == inherited[k]) { |
| matchingIsOverridden[j] = isOverridden[k]; |
| matchingIsInherited[j] = isInherited[k]; |
| break; |
| } |
| } |
| } |
| } else { |
| matchingIsOverridden = isOverridden; |
| matchingIsInherited = isInherited; |
| } |
| |
| checkInheritedMethods(matchingInherited, length, matchingIsOverridden, matchingIsInherited); // pass in the length of matching |
| } |
| else if (mustImplementAbstractMethods && matchingInherited[0].isAbstract() && matchMethod == null) |
| checkAbstractMethod(matchingInherited[0]); |
| while (index >= 0) matchingInherited[index--] = null; // clear the previous contents of the matching methods |
| } |
| } |
| } |
| /* mark as skippable |
| * - any interface method implemented by a class method |
| * - an x method (x in {class, interface}), for which another x method with a subsignature was found |
| * mark as isOverridden |
| * - any skippable method as defined above iff it is actually overridden by the specific method (disregarding visibility etc.) |
| * Note, that 'idx' corresponds to the position of 'general' in the arrays 'skip' and 'isOverridden' |
| * TODO(stephan) currently (as of Bug 410325), the boarder between skip and isOverridden is blurred, |
| * should reassess after more experience with this patch. |
| */ |
| boolean isSkippableOrOverridden(MethodBinding specific, MethodBinding general, boolean[] skip, boolean[] isOverridden, boolean[] isInherited, int idx) { |
| boolean specificIsInterface = specific.declaringClass.isInterface(); |
| boolean generalIsInterface = general.declaringClass.isInterface(); |
| if (!specificIsInterface && generalIsInterface) { |
| if (!specific.isAbstract() && isParameterSubsignature(specific, general)) { |
| // 8.4.8: abstract and default methods are not inherited if a concrete method with a subsignature is defined or inherited in C |
| isInherited[idx] = false; |
| return true; |
| } else if (isInterfaceMethodImplemented(general, specific, general.declaringClass)) { |
| skip[idx] = true; |
| isOverridden[idx] = true; |
| return true; |
| } |
| } else if (specificIsInterface == generalIsInterface) { |
| if (specific.declaringClass.isCompatibleWith(general.declaringClass) && isMethodSubsignature(specific, general)) { |
| skip[idx] = true; |
| isOverridden[idx] = true; |
| return true; |
| } |
| } |
| return false; |
| } |
| /* 'general' is considered as replaced by 'specific' if |
| * - 'specific' is "at least as concrete as" 'general' |
| * - 'specific' has a signature that is a subsignature of the substituted signature of 'general' (as seen from specific's declaring class) |
| * - default methods should also be considered replaced by class methods that meet the signature that is a subsignature criteria. |
| */ |
| MethodBinding findReplacedMethod(MethodBinding specific, MethodBinding general) { |
| MethodBinding generalSubstitute = computeSubstituteMethod(general, specific); |
| if (generalSubstitute != null |
| && (!specific.isAbstract() || general.isAbstract() || (general.isDefaultMethod() && specific.declaringClass.isClass())) // if (abstract(specific) => abstract(general)) check if 'specific' overrides 'general' |
| && isSubstituteParameterSubsignature(specific, generalSubstitute)) |
| { |
| return generalSubstitute; |
| } |
| return null; |
| } |
| 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) { |
| MethodBinding match = matchingInherited[count]; |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=314556 |
| MethodBinding interfaceMethod = null, implementation = null; |
| if (first.declaringClass.isInterface()) { |
| interfaceMethod = first; |
| } else if (first.declaringClass.isClass()) { |
| implementation = first; |
| } |
| if (match.declaringClass.isInterface()) { |
| interfaceMethod = match; |
| } else if (match.declaringClass.isClass()) { |
| implementation = match; |
| } |
| if (interfaceMethod != null && implementation != null && !isAsVisible(implementation, interfaceMethod)) |
| problemReporter().inheritedMethodReducesVisibility(typeParameter, implementation, new MethodBinding [] {interfaceMethod}); |
| |
| if (areReturnTypesCompatible(first, match)) continue; |
| // unrelated interfaces - check to see if return types are compatible |
| if (first.declaringClass.isInterface() && match.declaringClass.isInterface() && areReturnTypesCompatible(match, first)) |
| continue; |
| break; |
| } |
| if (count > 0) { // All inherited methods do NOT have the same vmSignature |
| problemReporter().inheritedMethodsHaveIncompatibleReturnTypes(typeParameter, matchingInherited, index + 1); |
| continue nextSelector; |
| } |
| } |
| } |
| } |
| } |
| boolean detectInheritedNameClash(MethodBinding inherited, MethodBinding otherInherited) { |
| if (!inherited.areParameterErasuresEqual(otherInherited)) |
| return false; |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=322001 |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=323693 |
| // When reporting a name clash between two inherited methods, we should not look for a |
| // signature clash, but instead should be looking for method descriptor clash. |
| if (TypeBinding.notEquals(inherited.returnType.erasure(), otherInherited.returnType.erasure())) |
| return false; |
| // skip it if otherInherited is defined by a subtype of inherited's declaringClass or vice versa. |
| // avoid being order sensitive and check with the roles reversed also. |
| if (TypeBinding.notEquals(inherited.declaringClass.erasure(), otherInherited.declaringClass.erasure())) { |
| if (inherited.declaringClass.findSuperTypeOriginatingFrom(otherInherited.declaringClass) != null) |
| return false; |
| if (otherInherited.declaringClass.findSuperTypeOriginatingFrom(inherited.declaringClass) != null) |
| return false; |
| } |
| |
| problemReporter().inheritedMethodsHaveNameClash(this.type, inherited, otherInherited); |
| return true; |
| } |
| boolean detectNameClash(MethodBinding current, MethodBinding inherited, boolean treatAsSynthetic) { |
| MethodBinding methodToCheck = inherited; |
| MethodBinding original = methodToCheck.original(); // can be the same as inherited |
| if (!current.areParameterErasuresEqual(original)) |
| return false; |
| int severity = ProblemSeverities.Error; |
| if (this.environment.globalOptions.complianceLevel == ClassFileConstants.JDK1_6) { |
| // for 1.6 return types also need to be checked |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=317719 |
| if (TypeBinding.notEquals(current.returnType.erasure(), original.returnType.erasure())) |
| severity = ProblemSeverities.Warning; |
| } |
| if (!treatAsSynthetic) { |
| // For a user method, see if current class overrides the inherited method. If it does, |
| // then any grievance we may have ought to be against the current class's method and |
| // NOT against any super implementations. https://bugs.eclipse.org/bugs/show_bug.cgi?id=293615 |
| |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=315978 : we now defer this rather expensive |
| // check to just before reporting (the incorrect) name clash. In the event there is no name |
| // clash to report to begin with (the common case), no penalty needs to be paid. |
| MethodBinding[] currentNamesakes = (MethodBinding[]) this.currentMethods.get(inherited.selector); |
| if (currentNamesakes.length > 1) { // we know it ought to at least one and that current is NOT the override |
| for (int i = 0, length = currentNamesakes.length; i < length; i++) { |
| MethodBinding currentMethod = currentNamesakes[i]; |
| if (currentMethod != current && doesMethodOverride(currentMethod, inherited)) { |
| methodToCheck = currentMethod; |
| break; |
| } |
| } |
| } |
| } |
| original = methodToCheck.original(); // can be the same as inherited |
| if (!current.areParameterErasuresEqual(original)) |
| return false; |
| original = inherited.original(); // For error reporting use, inherited.original() |
| problemReporter(current).methodNameClash(current, inherited.declaringClass.isRawType() ? inherited : original, severity); |
| if (severity == ProblemSeverities.Warning) return false; |
| return true; |
| } |
| 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 (TypeBinding.equalsEquals(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 (TypeBinding.equalsEquals(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() && TypeBinding.equalsEquals(next.erasure(), erasure)) { |
| if (copy == null) |
| copy = new SimpleSet(nextPosition); |
| copy.add(interfacesToVisit[i]); |
| copy.add(interfacesToVisit[j]); |
| } |
| } |
| } |
| } |
| return copy; |
| } |
| 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 (TypeBinding.equalsEquals(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 |
| && (TypeBinding.equalsEquals(inheritedMethod.returnType, existingMethod.returnType) // need to keep around to produce bridge methods? ... |
| || (TypeBinding.notEquals(this.type, existingMethod.declaringClass) // ... not if inheriting the bridge situation from a superclass |
| && !existingMethod.declaringClass.isInterface())) |
| && doesMethodOverride(existingMethod, inheritedMethod); |
| } |
| public boolean isMethodSubsignature(MethodBinding method, MethodBinding inheritedMethod) { |
| if (!org.aspectj.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(); |
| |
| MethodBinding inheritedOriginal = method.findOriginalInheritedMethod(inheritedMethod); |
| return isParameterSubsignature(method, inheritedOriginal == null ? inheritedMethod : inheritedOriginal); |
| } |
| 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 (TypeBinding.equalsEquals(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(); |
| |
| reportRawReferences(); |
| |
| 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]); |
| } |
| } |
| } |