// additional parameter strict: if true we do not tolerate incompatibly missing annotations on // type parameters (for overriding analysis) public static NullAnnotationMatching analyse( TypeBinding requiredType, TypeBinding providedType, int nullStatus, boolean strict) { int severity = 0; TypeBinding superTypeHint = null; if (requiredType instanceof ArrayBinding) { long[] requiredDimsTagBits = ((ArrayBinding) requiredType).nullTagBitsPerDimension; if (requiredDimsTagBits != null) { int dims = requiredType.dimensions(); if (requiredType.dimensions() == providedType.dimensions()) { long[] providedDimsTagBits = ((ArrayBinding) providedType).nullTagBitsPerDimension; if (providedDimsTagBits == null) { severity = 1; // required is annotated, provided not, need unchecked conversion } else { for (int i = 0; i <= dims; i++) { long requiredBits = validNullTagBits(requiredDimsTagBits[i]); long providedBits = validNullTagBits(providedDimsTagBits[i]); if (i > 0) nullStatus = -1; // don't use beyond the outermost dimension severity = Math.max( severity, computeNullProblemSeverity(requiredBits, providedBits, nullStatus, strict)); if (severity == 2) return NullAnnotationMatching.NULL_ANNOTATIONS_MISMATCH; } } } else if (providedType.id == TypeIds.T_null) { if (dims > 0 && requiredDimsTagBits[0] == TagBits.AnnotationNonNull) return NullAnnotationMatching.NULL_ANNOTATIONS_MISMATCH; } } } else if (requiredType.hasNullTypeAnnotations() || providedType.hasNullTypeAnnotations()) { long requiredBits = requiredNullTagBits(requiredType); if (requiredBits != TagBits.AnnotationNullable // nullable lhs accepts everything, ... || nullStatus == -1) // only at detail/recursion even nullable must be matched exactly { long providedBits = providedNullTagBits(providedType); severity = computeNullProblemSeverity( requiredBits, providedBits, nullStatus, strict && nullStatus == -1); } if (severity < 2) { TypeBinding providedSuper = providedType.findSuperTypeOriginatingFrom(requiredType); if (providedSuper != providedType) // $IDENTITY-COMPARISON$ superTypeHint = providedSuper; if (requiredType.isParameterizedType() && providedSuper instanceof ParameterizedTypeBinding) { // TODO(stephan): handle providedType.isRaw() TypeBinding[] requiredArguments = ((ParameterizedTypeBinding) requiredType).arguments; TypeBinding[] providedArguments = ((ParameterizedTypeBinding) providedSuper).arguments; if (requiredArguments != null && providedArguments != null && requiredArguments.length == providedArguments.length) { for (int i = 0; i < requiredArguments.length; i++) { NullAnnotationMatching status = analyse(requiredArguments[i], providedArguments[i], -1, strict); severity = Math.max(severity, status.severity); if (severity == 2) return new NullAnnotationMatching(severity, superTypeHint); } } } TypeBinding requiredEnclosing = requiredType.enclosingType(); TypeBinding providedEnclosing = providedType.enclosingType(); if (requiredEnclosing != null && providedEnclosing != null) { NullAnnotationMatching status = analyse(requiredEnclosing, providedEnclosing, -1, strict); severity = Math.max(severity, status.severity); } } } if (severity == 0) return NullAnnotationMatching.NULL_ANNOTATIONS_OK; return new NullAnnotationMatching(severity, superTypeHint); }
public boolean checkUnsafeCast( Scope scope, TypeBinding castType, TypeBinding expressionType, TypeBinding match, boolean isNarrowing) { if (TypeBinding.equalsEquals(match, castType)) { if (!isNarrowing && TypeBinding.equalsEquals( match, this.resolvedType .leafComponentType()) // do not tag as unnecessary when recursing through upper // bounds && !(expressionType.isParameterizedType() && expressionType.isProvablyDistinct(castType))) { tagAsUnnecessaryCast(scope, castType); } return true; } if (match != null) { if (isNarrowing ? match.isProvablyDistinct(expressionType) : castType.isProvablyDistinct(match)) { return false; } } switch (castType.kind()) { case Binding.PARAMETERIZED_TYPE: if (!castType.isReifiable()) { if (match == null) { // unrelated types this.bits |= ASTNode.UnsafeCast; return true; } switch (match.kind()) { case Binding.PARAMETERIZED_TYPE: if (isNarrowing) { // [JLS 5.5] T <: S if (expressionType.isRawType() || !expressionType.isEquivalentTo(match)) { this.bits |= ASTNode.UnsafeCast; return true; } // [JLS 5.5] S has no subtype X != T, such that |X| == |T| // if I2<T,U> extends I1<T>, then cast from I1<T> to I2<T,U> is unchecked ParameterizedTypeBinding paramCastType = (ParameterizedTypeBinding) castType; ParameterizedTypeBinding paramMatch = (ParameterizedTypeBinding) match; // easy case if less parameters on match TypeBinding[] castArguments = paramCastType.arguments; int length = castArguments == null ? 0 : castArguments.length; if (paramMatch.arguments == null || length > paramMatch.arguments.length) { this.bits |= ASTNode.UnsafeCast; } else if ((paramCastType.tagBits & (TagBits.HasDirectWildcard | TagBits.HasTypeVariable)) != 0) { // verify alternate cast type, substituting different type arguments nextAlternateArgument: for (int i = 0; i < length; i++) { switch (castArguments[i].kind()) { case Binding.WILDCARD_TYPE: case Binding.TYPE_PARAMETER: break; // check substituting with other default: continue nextAlternateArgument; // no alternative possible } TypeBinding[] alternateArguments; // need to clone for each iteration to avoid env paramtype cache interference System.arraycopy( paramCastType.arguments, 0, alternateArguments = new TypeBinding[length], 0, length); alternateArguments[i] = scope.getJavaLangObject(); LookupEnvironment environment = scope.environment(); ParameterizedTypeBinding alternateCastType = environment.createParameterizedType( (ReferenceBinding) castType.erasure(), alternateArguments, castType.enclosingType()); if (TypeBinding.equalsEquals( alternateCastType.findSuperTypeOriginatingFrom(expressionType), match)) { this.bits |= ASTNode.UnsafeCast; break; } } } return true; } else { // [JLS 5.5] T >: S if (!match.isEquivalentTo(castType)) { this.bits |= ASTNode.UnsafeCast; return true; } } break; case Binding.RAW_TYPE: this.bits |= ASTNode.UnsafeCast; // upcast since castType is known to be bound paramType return true; default: if (isNarrowing) { // match is not parameterized or raw, then any other subtype of match will erase to // |T| this.bits |= ASTNode.UnsafeCast; return true; } break; } } break; case Binding.ARRAY_TYPE: TypeBinding leafType = castType.leafComponentType(); if (isNarrowing && (!leafType.isReifiable() || leafType.isTypeVariable())) { this.bits |= ASTNode.UnsafeCast; return true; } break; case Binding.TYPE_PARAMETER: this.bits |= ASTNode.UnsafeCast; return true; // (disabled) https://bugs.eclipse.org/bugs/show_bug.cgi?id=240807 // case Binding.TYPE : // if (isNarrowing && match == null && expressionType.isParameterizedType()) { // this.bits |= ASTNode.UnsafeCast; // return true; // } // break; } if (!isNarrowing && TypeBinding.equalsEquals( match, this.resolvedType .leafComponentType())) { // do not tag as unnecessary when recursing through upper // bounds tagAsUnnecessaryCast(scope, castType); } return true; }
static boolean isEqual( org.eclipse.jdt.internal.compiler.lookup.TypeBinding typeBinding, org.eclipse.jdt.internal.compiler.lookup.TypeBinding typeBinding2, HashSet visitedTypes) { if (typeBinding == typeBinding2) return true; if (typeBinding == null || typeBinding2 == null) return false; switch (typeBinding.kind()) { case Binding.BASE_TYPE: if (!typeBinding2.isBaseType()) { return false; } return typeBinding.id == typeBinding2.id; case Binding.ARRAY_TYPE: if (!typeBinding2.isArrayType()) { return false; } return typeBinding.dimensions() == typeBinding2.dimensions() && isEqual( typeBinding.leafComponentType(), typeBinding2.leafComponentType(), visitedTypes); case Binding.PARAMETERIZED_TYPE: if (!typeBinding2.isParameterizedType()) { return false; } ParameterizedTypeBinding parameterizedTypeBinding = (ParameterizedTypeBinding) typeBinding; ParameterizedTypeBinding parameterizedTypeBinding2 = (ParameterizedTypeBinding) typeBinding2; return CharOperation.equals( parameterizedTypeBinding.compoundName, parameterizedTypeBinding2.compoundName) && (parameterizedTypeBinding.modifiers & (ExtraCompilerModifiers.AccJustFlag | ClassFileConstants.AccInterface | ClassFileConstants.AccEnum | ClassFileConstants.AccAnnotation)) == (parameterizedTypeBinding2.modifiers & (ExtraCompilerModifiers.AccJustFlag | ClassFileConstants.AccInterface | ClassFileConstants.AccEnum | ClassFileConstants.AccAnnotation)) && isEqual( parameterizedTypeBinding.arguments, parameterizedTypeBinding2.arguments, visitedTypes) && isEqual( parameterizedTypeBinding.enclosingType(), parameterizedTypeBinding2.enclosingType(), visitedTypes); case Binding.WILDCARD_TYPE: if (typeBinding2.kind() != Binding.WILDCARD_TYPE) { return false; } WildcardBinding wildcardBinding = (WildcardBinding) typeBinding; WildcardBinding wildcardBinding2 = (WildcardBinding) typeBinding2; return isEqual(wildcardBinding.bound, wildcardBinding2.bound, visitedTypes) && wildcardBinding.boundKind == wildcardBinding2.boundKind; case Binding.INTERSECTION_TYPE: if (typeBinding2.kind() != Binding.INTERSECTION_TYPE) { return false; } WildcardBinding intersectionBinding = (WildcardBinding) typeBinding; WildcardBinding intersectionBinding2 = (WildcardBinding) typeBinding2; return isEqual(intersectionBinding.bound, intersectionBinding2.bound, visitedTypes) && isEqual( intersectionBinding.otherBounds, intersectionBinding2.otherBounds, visitedTypes); case Binding.TYPE_PARAMETER: if (!(typeBinding2.isTypeVariable())) { return false; } if (typeBinding.isCapture()) { if (!(typeBinding2.isCapture())) { return false; } CaptureBinding captureBinding = (CaptureBinding) typeBinding; CaptureBinding captureBinding2 = (CaptureBinding) typeBinding2; if (captureBinding.position == captureBinding2.position) { if (visitedTypes.contains(typeBinding)) return true; visitedTypes.add(typeBinding); return isEqual(captureBinding.wildcard, captureBinding2.wildcard, visitedTypes) && isEqual(captureBinding.sourceType, captureBinding2.sourceType, visitedTypes); } return false; } TypeVariableBinding typeVariableBinding = (TypeVariableBinding) typeBinding; TypeVariableBinding typeVariableBinding2 = (TypeVariableBinding) typeBinding2; if (CharOperation.equals(typeVariableBinding.sourceName, typeVariableBinding2.sourceName)) { if (visitedTypes.contains(typeBinding)) return true; visitedTypes.add(typeBinding); return isEqual( typeVariableBinding.declaringElement, typeVariableBinding2.declaringElement, visitedTypes) && isEqual( typeVariableBinding.superclass(), typeVariableBinding2.superclass(), visitedTypes) && isEqual( typeVariableBinding.superInterfaces(), typeVariableBinding2.superInterfaces(), visitedTypes); } return false; case Binding.GENERIC_TYPE: if (!typeBinding2.isGenericType()) { return false; } ReferenceBinding referenceBinding = (ReferenceBinding) typeBinding; ReferenceBinding referenceBinding2 = (ReferenceBinding) typeBinding2; return CharOperation.equals(referenceBinding.compoundName, referenceBinding2.compoundName) && (referenceBinding.modifiers & (ExtraCompilerModifiers.AccJustFlag | ClassFileConstants.AccInterface | ClassFileConstants.AccEnum | ClassFileConstants.AccAnnotation)) == (referenceBinding2.modifiers & (ExtraCompilerModifiers.AccJustFlag | ClassFileConstants.AccInterface | ClassFileConstants.AccEnum | ClassFileConstants.AccAnnotation)) && isEqual( referenceBinding.typeVariables(), referenceBinding2.typeVariables(), visitedTypes) && isEqual( referenceBinding.enclosingType(), referenceBinding2.enclosingType(), visitedTypes); case Binding.RAW_TYPE: default: if (!(typeBinding2 instanceof ReferenceBinding)) { return false; } referenceBinding = (ReferenceBinding) typeBinding; referenceBinding2 = (ReferenceBinding) typeBinding2; char[] constantPoolName = referenceBinding.constantPoolName(); char[] constantPoolName2 = referenceBinding2.constantPoolName(); // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=116833 if (constantPoolName == null) { if (constantPoolName2 != null) { return false; } if (!CharOperation.equals( referenceBinding.computeUniqueKey(), referenceBinding2.computeUniqueKey())) { return false; } } else { if (constantPoolName2 == null) { return false; } if (!CharOperation.equals(constantPoolName, constantPoolName2)) { return false; } } return CharOperation.equals(referenceBinding.compoundName, referenceBinding2.compoundName) && (!referenceBinding2.isGenericType()) && (referenceBinding.isRawType() == referenceBinding2.isRawType()) && ((referenceBinding.modifiers & ~ClassFileConstants.AccSuper) & (ExtraCompilerModifiers.AccJustFlag | ClassFileConstants.AccInterface | ClassFileConstants.AccEnum | ClassFileConstants.AccAnnotation)) == ((referenceBinding2.modifiers & ~ClassFileConstants.AccSuper) & (ExtraCompilerModifiers.AccJustFlag | ClassFileConstants.AccInterface | ClassFileConstants.AccEnum | ClassFileConstants.AccAnnotation)) && isEqual( referenceBinding.enclosingType(), referenceBinding2.enclosingType(), visitedTypes); } }