|
Lines 65-77
Link Here
|
| 65 |
this.environment = environment; |
65 |
this.environment = environment; |
| 66 |
this.allowCompatibleReturnTypes = |
66 |
this.allowCompatibleReturnTypes = |
| 67 |
environment.globalOptions.complianceLevel >= ClassFileConstants.JDK1_5 |
67 |
environment.globalOptions.complianceLevel >= ClassFileConstants.JDK1_5 |
| 68 |
&& environment.globalOptions.sourceLevel < ClassFileConstants.JDK1_5; |
68 |
&& environment.globalOptions.sourceLevel < ClassFileConstants.JDK1_5; |
| 69 |
} |
69 |
} |
| 70 |
boolean areMethodsCompatible(MethodBinding one, MethodBinding two) { |
70 |
//boolean areMethodsCompatible(MethodBinding one, MethodBinding two) { |
| 71 |
return isParameterSubsignature(one, two) && areReturnTypesCompatible(one, two); |
71 |
// return isParameterSubsignature(one, two) && areReturnTypesCompatible(one, two); |
|
|
72 |
//} |
| 73 |
|
| 74 |
static boolean areMethodsCompatible(MethodBinding one, MethodBinding two, LookupEnvironment localEnvironment) { |
| 75 |
// use the original methods to test compatibility, but do not check visibility, etc |
| 76 |
one = one.original(); |
| 77 |
two = one.findOriginalInheritedMethod(two); |
| 78 |
|
| 79 |
if (two == null) |
| 80 |
return false; // method's declaringClass does not inherit from inheritedMethod's |
| 81 |
|
| 82 |
return isParameterSubsignature(one, two, localEnvironment); |
| 72 |
} |
83 |
} |
|
|
84 |
|
| 85 |
boolean areMethodsCompatible(MethodBinding one, MethodBinding two) { |
| 86 |
return areMethodsCompatible(one, two, this.environment); |
| 87 |
} |
| 88 |
|
| 73 |
boolean areReturnTypesCompatible(MethodBinding one, MethodBinding two) { |
89 |
boolean areReturnTypesCompatible(MethodBinding one, MethodBinding two) { |
| 74 |
if (one.returnType == two.returnType) return true; |
90 |
if (one.returnType == two.returnType) return true; |
| 75 |
|
91 |
|
| 76 |
if (areTypesEqual(one.returnType, two.returnType)) return true; |
92 |
if (areTypesEqual(one.returnType, two.returnType)) return true; |
| 77 |
|
93 |
|
|
Lines 465-554
Link Here
|
| 465 |
compare concrete's exceptions against each abstract method |
481 |
compare concrete's exceptions against each abstract method |
| 466 |
else |
482 |
else |
| 467 |
complain about missing implementation only if type is NOT an interface or abstract |
483 |
complain about missing implementation only if type is NOT an interface or abstract |
| 468 |
*/ |
484 |
*/ |
| 469 |
abstract void checkMethods(); |
485 |
abstract void checkMethods(); |
| 470 |
private void _unusedCheckMethods() { |
|
|
| 471 |
boolean mustImplementAbstractMethods = mustImplementAbstractMethods(); |
| 472 |
boolean skipInheritedMethods = mustImplementAbstractMethods && canSkipInheritedMethods(); // have a single concrete superclass so only check overridden methods |
| 473 |
boolean isOrEnclosedByPrivateType = this.type.isOrEnclosedByPrivateType(); |
| 474 |
char[][] methodSelectors = this.inheritedMethods.keyTable; |
| 475 |
nextSelector : for (int s = methodSelectors.length; --s >= 0;) { |
| 476 |
if (methodSelectors[s] == null) continue nextSelector; |
| 477 |
|
| 478 |
MethodBinding[] current = (MethodBinding[]) this.currentMethods.get(methodSelectors[s]); |
| 479 |
MethodBinding[] inherited = (MethodBinding[]) this.inheritedMethods.valueTable[s]; |
| 480 |
|
| 481 |
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=296660, if current type is exposed, |
| 482 |
// inherited methods of super classes are too. current != null case handled below. |
| 483 |
if (current == null && !isOrEnclosedByPrivateType) { |
| 484 |
int length = inherited.length; |
| 485 |
for (int i = 0; i < length; i++){ |
| 486 |
inherited[i].original().modifiers |= ExtraCompilerModifiers.AccLocallyUsed; |
| 487 |
} |
| 488 |
} |
| 489 |
|
| 490 |
if (current == null && skipInheritedMethods) |
| 491 |
continue nextSelector; |
| 492 |
|
| 493 |
if (inherited.length == 1 && current == null) { // handle the common case |
| 494 |
if (mustImplementAbstractMethods && inherited[0].isAbstract()) |
| 495 |
checkAbstractMethod(inherited[0]); |
| 496 |
continue nextSelector; |
| 497 |
} |
| 498 |
|
| 499 |
int index = -1; |
| 500 |
MethodBinding[] matchingInherited = new MethodBinding[inherited.length]; |
| 501 |
if (current != null) { |
| 502 |
for (int i = 0, length1 = current.length; i < length1; i++) { |
| 503 |
MethodBinding currentMethod = current[i]; |
| 504 |
for (int j = 0, length2 = inherited.length; j < length2; j++) { |
| 505 |
MethodBinding inheritedMethod = computeSubstituteMethod(inherited[j], currentMethod); |
| 506 |
if (inheritedMethod != null) { |
| 507 |
if (isParameterSubsignature(currentMethod, inheritedMethod)) { |
| 508 |
matchingInherited[++index] = inheritedMethod; |
| 509 |
inherited[j] = null; // do not want to find it again |
| 510 |
} |
| 511 |
} |
| 512 |
} |
| 513 |
if (index >= 0) { |
| 514 |
checkAgainstInheritedMethods(currentMethod, matchingInherited, index + 1, inherited); // pass in the length of matching |
| 515 |
while (index >= 0) matchingInherited[index--] = null; // clear the contents of the matching methods |
| 516 |
} |
| 517 |
} |
| 518 |
} |
| 519 |
|
| 520 |
for (int i = 0, length = inherited.length; i < length; i++) { |
| 521 |
MethodBinding inheritedMethod = inherited[i]; |
| 522 |
if (inheritedMethod == null) continue; |
| 523 |
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=296660, if current type is exposed, |
| 524 |
// inherited methods of super classes are too. current == null case handled already. |
| 525 |
if (!isOrEnclosedByPrivateType && current != null) { |
| 526 |
inheritedMethod.original().modifiers |= ExtraCompilerModifiers.AccLocallyUsed; |
| 527 |
} |
| 528 |
matchingInherited[++index] = inheritedMethod; |
| 529 |
for (int j = i + 1; j < length; j++) { |
| 530 |
MethodBinding otherInheritedMethod = inherited[j]; |
| 531 |
if (canSkipInheritedMethods(inheritedMethod, otherInheritedMethod)) |
| 532 |
continue; |
| 533 |
otherInheritedMethod = computeSubstituteMethod(otherInheritedMethod, inheritedMethod); |
| 534 |
if (otherInheritedMethod != null) { |
| 535 |
if (isParameterSubsignature(inheritedMethod, otherInheritedMethod)) { |
| 536 |
matchingInherited[++index] = otherInheritedMethod; |
| 537 |
inherited[j] = null; // do not want to find it again |
| 538 |
} |
| 539 |
} |
| 540 |
} |
| 541 |
if (index == -1) continue; |
| 542 |
if (index > 0) |
| 543 |
checkInheritedMethods(matchingInherited, index + 1, null/*FIXME: required value not provided*/); // pass in the length of matching |
| 544 |
else if (mustImplementAbstractMethods && matchingInherited[0].isAbstract()) |
| 545 |
checkAbstractMethod(matchingInherited[0]); |
| 546 |
while (index >= 0) matchingInherited[index--] = null; // clear the contents of the matching methods |
| 547 |
} |
| 548 |
} |
| 549 |
} |
| 550 |
|
486 |
|
| 551 |
void checkPackagePrivateAbstractMethod(MethodBinding abstractMethod) { |
487 |
void checkPackagePrivateAbstractMethod(MethodBinding abstractMethod) { |
| 552 |
// check that the inherited abstract method (package private visibility) is implemented within the same package |
488 |
// check that the inherited abstract method (package private visibility) is implemented within the same package |
| 553 |
PackageBinding necessaryPackage = abstractMethod.declaringClass.fPackage; |
489 |
PackageBinding necessaryPackage = abstractMethod.declaringClass.fPackage; |
| 554 |
if (necessaryPackage == this.type.fPackage) return; // not a problem |
490 |
if (necessaryPackage == this.type.fPackage) return; // not a problem |
|
Lines 755-766
Link Here
|
| 755 |
MethodBinding computeSubstituteMethod(MethodBinding inheritedMethod, MethodBinding currentMethod) { |
691 |
MethodBinding computeSubstituteMethod(MethodBinding inheritedMethod, MethodBinding currentMethod) { |
| 756 |
if (inheritedMethod == null) return null; |
692 |
if (inheritedMethod == null) return null; |
| 757 |
if (currentMethod.parameters.length != inheritedMethod.parameters.length) return null; // no match |
693 |
if (currentMethod.parameters.length != inheritedMethod.parameters.length) return null; // no match |
| 758 |
return inheritedMethod; |
694 |
return inheritedMethod; |
| 759 |
} |
695 |
} |
|
|
696 |
static MethodBinding computeSubstituteMethod(MethodBinding inheritedMethod, MethodBinding currentMethod, LookupEnvironment localEnvironment) { |
| 697 |
if (inheritedMethod == null) return null; |
| 698 |
if (currentMethod.parameters.length != inheritedMethod.parameters.length) return null; // no match |
| 760 |
|
699 |
|
| 761 |
boolean couldMethodOverride(MethodBinding method, MethodBinding inheritedMethod) { |
700 |
// due to hierarchy & compatibility checks, we need to ensure these 2 methods are resolved |
|
|
701 |
if (currentMethod.declaringClass instanceof BinaryTypeBinding) |
| 702 |
((BinaryTypeBinding) currentMethod.declaringClass).resolveTypesFor(currentMethod); |
| 703 |
if (inheritedMethod.declaringClass instanceof BinaryTypeBinding) |
| 704 |
((BinaryTypeBinding) inheritedMethod.declaringClass).resolveTypesFor(inheritedMethod); |
| 705 |
|
| 706 |
TypeVariableBinding[] inheritedTypeVariables = inheritedMethod.typeVariables; |
| 707 |
int inheritedLength = inheritedTypeVariables.length; |
| 708 |
if (inheritedLength == 0) return inheritedMethod; // no substitution needed |
| 709 |
TypeVariableBinding[] typeVariables = currentMethod.typeVariables; |
| 710 |
int length = typeVariables.length; |
| 711 |
if (length == 0) |
| 712 |
return inheritedMethod.asRawMethod(localEnvironment); |
| 713 |
if (length != inheritedLength) |
| 714 |
return inheritedMethod; // no match JLS 8.4.2 |
| 715 |
|
| 716 |
// interface I { <T> void foo(T t); } |
| 717 |
// class X implements I { public <T extends I> void foo(T t) {} } |
| 718 |
// for the above case, we do not want to answer the substitute method since its not a match |
| 719 |
TypeBinding[] arguments = new TypeBinding[length]; |
| 720 |
System.arraycopy(typeVariables, 0, arguments, 0, length); |
| 721 |
ParameterizedGenericMethodBinding substitute = |
| 722 |
localEnvironment.createParameterizedGenericMethod(inheritedMethod, arguments); |
| 723 |
for (int i = 0; i < inheritedLength; i++) { |
| 724 |
TypeVariableBinding inheritedTypeVariable = inheritedTypeVariables[i]; |
| 725 |
TypeBinding argument = arguments[i]; |
| 726 |
if (argument instanceof TypeVariableBinding) { |
| 727 |
TypeVariableBinding typeVariable = (TypeVariableBinding) argument; |
| 728 |
if (typeVariable.firstBound == inheritedTypeVariable.firstBound) { |
| 729 |
if (typeVariable.firstBound == null) |
| 730 |
continue; // both are null |
| 731 |
} else if (typeVariable.firstBound != null && inheritedTypeVariable.firstBound != null) { |
| 732 |
if (typeVariable.firstBound.isClass() != inheritedTypeVariable.firstBound.isClass()) |
| 733 |
return inheritedMethod; // not a match |
| 734 |
} |
| 735 |
if (Scope.substitute(substitute, inheritedTypeVariable.superclass) != typeVariable.superclass) |
| 736 |
return inheritedMethod; // not a match |
| 737 |
int interfaceLength = inheritedTypeVariable.superInterfaces.length; |
| 738 |
ReferenceBinding[] interfaces = typeVariable.superInterfaces; |
| 739 |
if (interfaceLength != interfaces.length) |
| 740 |
return inheritedMethod; // not a match |
| 741 |
next : for (int j = 0; j < interfaceLength; j++) { |
| 742 |
TypeBinding superType = Scope.substitute(substitute, inheritedTypeVariable.superInterfaces[j]); |
| 743 |
for (int k = 0; k < interfaceLength; k++) |
| 744 |
if (superType == interfaces[k]) |
| 745 |
continue next; |
| 746 |
return inheritedMethod; // not a match |
| 747 |
} |
| 748 |
} else if (inheritedTypeVariable.boundCheck(substitute, argument) != TypeConstants.OK) { |
| 749 |
return inheritedMethod; |
| 750 |
} |
| 751 |
} |
| 752 |
return substitute; |
| 753 |
} |
| 754 |
static boolean couldMethodOverride(MethodBinding method, MethodBinding inheritedMethod) { |
| 762 |
if (!org.eclipse.jdt.core.compiler.CharOperation.equals(method.selector, inheritedMethod.selector)) |
755 |
if (!org.eclipse.jdt.core.compiler.CharOperation.equals(method.selector, inheritedMethod.selector)) |
| 763 |
return false; |
756 |
return false; |
| 764 |
if (method == inheritedMethod || method.isStatic() || inheritedMethod.isStatic()) |
757 |
if (method == inheritedMethod || method.isStatic() || inheritedMethod.isStatic()) |
| 765 |
return false; |
758 |
return false; |
| 766 |
if (inheritedMethod.isPrivate()) |
759 |
if (inheritedMethod.isPrivate()) |
|
Lines 774-796
Link Here
|
| 774 |
return false; |
767 |
return false; |
| 775 |
} |
768 |
} |
| 776 |
return true; |
769 |
return true; |
| 777 |
} |
770 |
} |
| 778 |
|
771 |
|
| 779 |
// Answer whether the method overrides the inheritedMethod |
772 |
|
| 780 |
// Check the necessary visibility rules & inheritance from the inheritedMethod's declaringClass |
773 |
public static boolean doesMethodOverride(MethodBinding method, MethodBinding inheritedMethod, LookupEnvironment localEnvironment) { |
| 781 |
// See isMethodSubsignature() for parameter comparisons |
774 |
return couldMethodOverride(method, inheritedMethod) && areMethodsCompatible(method, inheritedMethod, localEnvironment); |
|
|
775 |
} |
| 776 |
|
| 782 |
public boolean doesMethodOverride(MethodBinding method, MethodBinding inheritedMethod) { |
777 |
public boolean doesMethodOverride(MethodBinding method, MethodBinding inheritedMethod) { |
| 783 |
if (!couldMethodOverride(method, inheritedMethod)) |
778 |
return doesMethodOverride(method, inheritedMethod) && areMethodsCompatible(method, inheritedMethod, this.environment); |
| 784 |
return false; |
|
|
| 785 |
|
| 786 |
inheritedMethod = inheritedMethod.original(); |
| 787 |
TypeBinding match = method.declaringClass.findSuperTypeOriginatingFrom(inheritedMethod.declaringClass); |
| 788 |
if (!(match instanceof ReferenceBinding)) |
| 789 |
return false; // method's declaringClass does not inherit from inheritedMethod's |
| 790 |
|
| 791 |
return isParameterSubsignature(method, inheritedMethod); |
| 792 |
} |
779 |
} |
| 793 |
|
780 |
|
| 794 |
SimpleSet findSuperinterfaceCollisions(ReferenceBinding superclass, ReferenceBinding[] superInterfaces) { |
781 |
SimpleSet findSuperinterfaceCollisions(ReferenceBinding superclass, ReferenceBinding[] superInterfaces) { |
| 795 |
return null; // noop in 1.4 |
782 |
return null; // noop in 1.4 |
| 796 |
} |
783 |
} |
|
Lines 881-896
Link Here
|
| 881 |
return areParametersEqual(existingMethod, inheritedMethod) && existingMethod.declaringClass.implementsInterface(superType, true); |
868 |
return areParametersEqual(existingMethod, inheritedMethod) && existingMethod.declaringClass.implementsInterface(superType, true); |
| 882 |
} |
869 |
} |
| 883 |
|
870 |
|
| 884 |
public boolean isMethodSubsignature(MethodBinding method, MethodBinding inheritedMethod) { |
871 |
public boolean isMethodSubsignature(MethodBinding method, MethodBinding inheritedMethod) { |
| 885 |
return org.eclipse.jdt.core.compiler.CharOperation.equals(method.selector, inheritedMethod.selector) |
872 |
return org.eclipse.jdt.core.compiler.CharOperation.equals(method.selector, inheritedMethod.selector) |
| 886 |
&& isParameterSubsignature(method, inheritedMethod); |
873 |
&& isParameterSubsignature(method, inheritedMethod, this.environment); |
| 887 |
} |
874 |
} |
| 888 |
|
875 |
static boolean isParameterSubsignature(MethodBinding method, MethodBinding inheritedMethod, LookupEnvironment localEnvironment) { |
| 889 |
boolean isParameterSubsignature(MethodBinding method, MethodBinding inheritedMethod) { |
876 |
MethodBinding substitute = computeSubstituteMethod(inheritedMethod, method, localEnvironment); |
| 890 |
return areParametersEqual(method, inheritedMethod); |
877 |
return substitute != null && isSubstituteParameterSubsignature(method, substitute, localEnvironment); |
| 891 |
} |
878 |
} |
|
|
879 |
//boolean isParameterSubsignature(MethodBinding method, MethodBinding inheritedMethod) { |
| 880 |
// return areParametersEqual(method, inheritedMethod); |
| 881 |
//} |
| 892 |
|
882 |
|
| 893 |
boolean isSameClassOrSubclassOf(ReferenceBinding testClass, ReferenceBinding superclass) { |
883 |
boolean isSameClassOrSubclassOf(ReferenceBinding testClass, ReferenceBinding superclass) { |
| 894 |
do { |
884 |
do { |
| 895 |
if (testClass == superclass) return true; |
885 |
if (testClass == superclass) return true; |
| 896 |
} while ((testClass = testClass.superclass()) != null); |
886 |
} while ((testClass = testClass.superclass()) != null); |
|
Lines 986-991
Link Here
|
| 986 |
buffer.append('\n'); |
976 |
buffer.append('\n'); |
| 987 |
buffer.append("\t-inherited methods: "); //$NON-NLS-1$ |
977 |
buffer.append("\t-inherited methods: "); //$NON-NLS-1$ |
| 988 |
buffer.append(this.inheritedMethods); |
978 |
buffer.append(this.inheritedMethods); |
| 989 |
return buffer.toString(); |
979 |
return buffer.toString(); |
| 990 |
} |
980 |
} |
|
|
981 |
|
| 982 |
protected static boolean isSubstituteParameterSubsignature(MethodBinding method, MethodBinding substituteMethod, LookupEnvironment environment) { |
| 983 |
if (!areParametersEqual(method, substituteMethod)) { |
| 984 |
// method can still override substituteMethod in cases like : |
| 985 |
// <U extends Number> void c(U u) {} |
| 986 |
// @Override void c(Number n) {} |
| 987 |
// but method cannot have a "generic-enabled" parameter type |
| 988 |
if (substituteMethod.hasSubstitutedParameters() && method.areParameterErasuresEqual(substituteMethod)) |
| 989 |
return method.typeVariables == Binding.NO_TYPE_VARIABLES && !hasGenericParameter(method); |
| 990 |
|
| 991 |
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=279836 |
| 992 |
if (method.declaringClass.isRawType() && substituteMethod.declaringClass.isRawType()) |
| 993 |
if (method.hasSubstitutedParameters() && substituteMethod.hasSubstitutedParameters()) |
| 994 |
return areMethodsCompatible(method, substituteMethod, environment); |
| 995 |
|
| 996 |
return false; |
| 997 |
} |
| 998 |
|
| 999 |
if (substituteMethod instanceof ParameterizedGenericMethodBinding) { |
| 1000 |
if (method.typeVariables != Binding.NO_TYPE_VARIABLES) |
| 1001 |
return !((ParameterizedGenericMethodBinding) substituteMethod).isRaw; |
| 1002 |
// since substituteMethod has substituted type variables, method cannot have a generic signature AND no variables -> its a name clash if it does |
| 1003 |
return !hasGenericParameter(method); |
| 1004 |
} |
| 1005 |
|
| 1006 |
// if method has its own variables, then substituteMethod failed bounds check in computeSubstituteMethod() |
| 1007 |
return method.typeVariables == Binding.NO_TYPE_VARIABLES; |
| 1008 |
} |
| 1009 |
|
| 1010 |
protected boolean isSubstituteParameterSubsignature(MethodBinding method, MethodBinding substituteMethod) { |
| 1011 |
return isSubstituteParameterSubsignature(method, substituteMethod, this.environment); |
| 1012 |
} |
| 1013 |
|
| 1014 |
static boolean hasGenericParameter(MethodBinding method) { |
| 1015 |
if (method.genericSignature() == null) return false; |
| 1016 |
|
| 1017 |
// may be only the return type that is generic, need to check parameters |
| 1018 |
TypeBinding[] params = method.parameters; |
| 1019 |
for (int i = 0, l = params.length; i < l; i++) { |
| 1020 |
TypeBinding param = params[i].leafComponentType(); |
| 1021 |
if (param instanceof ReferenceBinding) { |
| 1022 |
int modifiers = ((ReferenceBinding) param).modifiers; |
| 1023 |
if ((modifiers & ExtraCompilerModifiers.AccGenericSignature) != 0) |
| 1024 |
return true; |
| 1025 |
} |
| 1026 |
} |
| 1027 |
return false; |
| 1028 |
} |
| 991 |
} |
1029 |
} |