private TypeConstants.BoundCheckStatus internalBoundCheck( Substitution substitution, TypeBinding argumentType, Scope scope, ASTNode location) { if (argumentType == TypeBinding.NULL || TypeBinding.equalsEquals(argumentType, this)) { return BoundCheckStatus.OK; } boolean hasSubstitution = substitution != null; if (!(argumentType instanceof ReferenceBinding || argumentType.isArrayType())) return BoundCheckStatus.MISMATCH; // special case for re-entrant source types (selection, code assist, etc)... // can request additional types during hierarchy walk that are found as source types that also // 'need' to connect their hierarchy if (this.superclass == null) return BoundCheckStatus.OK; if (argumentType.kind() == Binding.WILDCARD_TYPE) { WildcardBinding wildcard = (WildcardBinding) argumentType; switch (wildcard.boundKind) { case Wildcard.EXTENDS: TypeBinding wildcardBound = wildcard.bound; if (TypeBinding.equalsEquals(wildcardBound, this)) return BoundCheckStatus.OK; boolean isArrayBound = wildcardBound.isArrayType(); if (!wildcardBound.isInterface()) { TypeBinding substitutedSuperType = hasSubstitution ? Scope.substitute(substitution, this.superclass) : this.superclass; if (substitutedSuperType.id != TypeIds.T_JavaLangObject) { if (isArrayBound) { if (!wildcardBound.isCompatibleWith(substitutedSuperType, scope)) return BoundCheckStatus.MISMATCH; } else { TypeBinding match = wildcardBound.findSuperTypeOriginatingFrom(substitutedSuperType); if (match != null) { if (substitutedSuperType.isProvablyDistinct(match)) { return BoundCheckStatus.MISMATCH; } } else { match = substitutedSuperType.findSuperTypeOriginatingFrom(wildcardBound); if (match != null) { if (match.isProvablyDistinct(wildcardBound)) { return BoundCheckStatus.MISMATCH; } } else { if (denotesRelevantSuperClass(wildcardBound) && denotesRelevantSuperClass(substitutedSuperType)) { // non-object real superclass should have produced a valid 'match' above return BoundCheckStatus.MISMATCH; } } } } } } boolean mustImplement = isArrayBound || ((ReferenceBinding) wildcardBound).isFinal(); for (int i = 0, length = this.superInterfaces.length; i < length; i++) { TypeBinding substitutedSuperType = hasSubstitution ? Scope.substitute(substitution, this.superInterfaces[i]) : this.superInterfaces[i]; if (isArrayBound) { if (!wildcardBound.isCompatibleWith(substitutedSuperType, scope)) return BoundCheckStatus.MISMATCH; } else { TypeBinding match = wildcardBound.findSuperTypeOriginatingFrom(substitutedSuperType); if (match != null) { if (substitutedSuperType.isProvablyDistinct(match)) { return BoundCheckStatus.MISMATCH; } } else if (mustImplement) { return BoundCheckStatus .MISMATCH; // cannot be extended further to satisfy missing bounds } } } break; case Wildcard.SUPER: // if the wildcard is lower-bounded by a type variable that has no relevant upper bound // there's nothing to check here (bug 282152): if (wildcard.bound.isTypeVariable() && ((TypeVariableBinding) wildcard.bound).superclass.id == TypeIds.T_JavaLangObject) break; return boundCheck(substitution, wildcard.bound, scope, location); case Wildcard.UNBOUND: break; } return BoundCheckStatus.OK; } boolean unchecked = false; boolean checkNullAnnotations = scope.environment().usesNullTypeAnnotations(); boolean haveReportedNullProblem = false; if (this.superclass.id != TypeIds.T_JavaLangObject) { TypeBinding substitutedSuperType = hasSubstitution ? Scope.substitute(substitution, this.superclass) : this.superclass; if (TypeBinding.notEquals(substitutedSuperType, argumentType)) { if (!argumentType.isCompatibleWith(substitutedSuperType, scope)) { return BoundCheckStatus.MISMATCH; } TypeBinding match = argumentType.findSuperTypeOriginatingFrom(substitutedSuperType); if (match != null) { // Enum#RAW is not a substitute for <E extends Enum<E>> (86838) if (match.isRawType() && substitutedSuperType.isBoundParameterizedType()) unchecked = true; } } if (location != null && checkNullAnnotations) { if (NullAnnotationMatching.analyse( this, argumentType, substitutedSuperType, substitution, -1, CheckMode.BOUND_CHECK) .isAnyMismatch()) { scope.problemReporter().nullityMismatchTypeArgument(this, argumentType, location); haveReportedNullProblem = true; } } } for (int i = 0, length = this.superInterfaces.length; i < length; i++) { TypeBinding substitutedSuperType = hasSubstitution ? Scope.substitute(substitution, this.superInterfaces[i]) : this.superInterfaces[i]; if (TypeBinding.notEquals(substitutedSuperType, argumentType)) { if (!argumentType.isCompatibleWith(substitutedSuperType, scope)) { return BoundCheckStatus.MISMATCH; } TypeBinding match = argumentType.findSuperTypeOriginatingFrom(substitutedSuperType); if (match != null) { // Enum#RAW is not a substitute for <E extends Enum<E>> (86838) if (match.isRawType() && substitutedSuperType.isBoundParameterizedType()) unchecked = true; } } if (location != null && checkNullAnnotations) { if (NullAnnotationMatching.analyse( this, argumentType, substitutedSuperType, substitution, -1, CheckMode.BOUND_CHECK) .isAnyMismatch()) { scope.problemReporter().nullityMismatchTypeArgument(this, argumentType, location); haveReportedNullProblem = true; } } } if (location != null && checkNullAnnotations && !haveReportedNullProblem) { long nullBits = this.tagBits & TagBits.AnnotationNullMASK; if (nullBits != 0 && nullBits != (argumentType.tagBits & TagBits.AnnotationNullMASK)) { scope.problemReporter().nullityMismatchTypeArgument(this, argumentType, location); haveReportedNullProblem = true; } } return unchecked ? BoundCheckStatus.UNCHECKED : haveReportedNullProblem ? BoundCheckStatus.NULL_PROBLEM : BoundCheckStatus.OK; }