package org.eclipse.jdt.internal.compiler.ast;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.internal.compiler.flow.FlowContext;
import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding.ExternalAnnotationStatus;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.CaptureBinding;
import org.eclipse.jdt.internal.compiler.lookup.InvocationSite;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
import org.eclipse.jdt.internal.compiler.lookup.RawTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.Substitution;
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBindingVisitor;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding;
import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
public class NullAnnotationMatching {
public static final NullAnnotationMatching NULL_ANNOTATIONS_OK = new NullAnnotationMatching(Severity.OK, FlowInfo.UNKNOWN, null);
public static final NullAnnotationMatching NULL_ANNOTATIONS_OK_NONNULL = new NullAnnotationMatching(Severity.OK, FlowInfo.NON_NULL, null);
public static final NullAnnotationMatching NULL_ANNOTATIONS_UNCHECKED = new NullAnnotationMatching(Severity.UNCHECKED, FlowInfo.UNKNOWN, null);
public static final NullAnnotationMatching NULL_ANNOTATIONS_MISMATCH = new NullAnnotationMatching(Severity.MISMATCH, FlowInfo.UNKNOWN, null);
public enum CheckMode {
COMPATIBLE {
@Override boolean requiredNullableMatchesAll() {
return true;
}
},
EXACT,
BOUND_CHECK,
BOUND_SUPER_CHECK,
OVERRIDE_RETURN {
@Override CheckMode toDetail() {
return OVERRIDE;
}
},
OVERRIDE {
@Override boolean requiredNullableMatchesAll() {
return true;
}
@Override CheckMode toDetail() {
return OVERRIDE;
}
};
boolean requiredNullableMatchesAll() {
return false;
}
CheckMode toDetail() {
return CheckMode.EXACT;
}
}
private enum Severity {
OK,
LEGACY_WARNING,
UNCHECKED,
MISMATCH;
public Severity max(Severity severity) {
if (compareTo(severity) < 0)
return severity;
return this;
}
public boolean isAnyMismatch() {
return compareTo(LEGACY_WARNING) > 0;
}
}
private final Severity severity;
public final TypeBinding superTypeHint;
public final int nullStatus;
NullAnnotationMatching(Severity severity, int nullStatus, TypeBinding superTypeHint) {
this.severity = severity;
this.superTypeHint = superTypeHint;
this.nullStatus = nullStatus;
}
public boolean isAnyMismatch() { return this.severity.isAnyMismatch(); }
public boolean isUnchecked() { return this.severity == Severity.UNCHECKED; }
public boolean isDefiniteMismatch() { return this.severity == Severity.MISMATCH; }
public boolean wantToReport() { return this.severity == Severity.LEGACY_WARNING; }
public boolean isPotentiallyNullMismatch() {
return !isDefiniteMismatch() && this.nullStatus != -1 && (this.nullStatus & FlowInfo.POTENTIALLY_NULL) != 0;
}
public String superTypeHintName(CompilerOptions options, boolean shortNames) {
return String.valueOf(this.superTypeHint.nullAnnotatedReadableName(options, shortNames));
}
public static int checkAssignment(BlockScope currentScope, FlowContext flowContext,
VariableBinding var, FlowInfo flowInfo, int nullStatus, Expression expression, TypeBinding providedType)
{
if (providedType == null) return FlowInfo.UNKNOWN;
long lhsTagBits = 0L;
boolean hasReported = false;
boolean usesNullTypeAnnotations = currentScope.environment().usesNullTypeAnnotations();
if (!usesNullTypeAnnotations) {
lhsTagBits = var.tagBits & TagBits.AnnotationNullMASK;
} else {
if (expression instanceof ConditionalExpression && expression.isPolyExpression()) {
ConditionalExpression ce = ((ConditionalExpression) expression);
int status1 = NullAnnotationMatching.checkAssignment(currentScope, flowContext, var, flowInfo, ce.ifTrueNullStatus, ce.valueIfTrue, ce.valueIfTrue.resolvedType);
int status2 = NullAnnotationMatching.checkAssignment(currentScope, flowContext, var, flowInfo, ce.ifFalseNullStatus, ce.valueIfFalse, ce.valueIfFalse.resolvedType);
if (status1 == status2)
return status1;
return nullStatus;
} else if (expression instanceof SwitchExpression && expression.isPolyExpression()) {
SwitchExpression se = ((SwitchExpression) expression);
Expression[] resExprs = se.resultExpressions.toArray(new Expression[0]);
Expression re = resExprs[0];
int status0 = NullAnnotationMatching.checkAssignment(currentScope, flowContext, var, flowInfo, re.nullStatus(flowInfo, flowContext), re, re.resolvedType);
boolean identicalStatus = true;
for (int i = 1, l = resExprs.length; i < l; ++i) {
re = resExprs[i];
int otherStatus = NullAnnotationMatching.checkAssignment(currentScope, flowContext, var, flowInfo, re.nullStatus(flowInfo, flowContext), re, re.resolvedType);
identicalStatus &= status0 == otherStatus;
}
return identicalStatus ? status0 : nullStatus;
}
lhsTagBits = var.type.tagBits & TagBits.AnnotationNullMASK;
NullAnnotationMatching annotationStatus = analyse(var.type, providedType, null, null, nullStatus, expression, CheckMode.COMPATIBLE);
if (annotationStatus.isAnyMismatch()) {
flowContext.recordNullityMismatch(currentScope, expression, providedType, var.type, flowInfo, nullStatus, annotationStatus);
hasReported = true;
} else {
if (annotationStatus.wantToReport())
annotationStatus.report(currentScope);
if (annotationStatus.nullStatus != FlowInfo.UNKNOWN) {
return annotationStatus.nullStatus;
}
}
}
if (lhsTagBits == TagBits.AnnotationNonNull && nullStatus != FlowInfo.NON_NULL) {
if (!hasReported)
flowContext.recordNullityMismatch(currentScope, expression, providedType, var.type, flowInfo, nullStatus, null);
return FlowInfo.NON_NULL;
} else if (lhsTagBits == TagBits.AnnotationNullable && nullStatus == FlowInfo.UNKNOWN) {
if (usesNullTypeAnnotations && providedType.isTypeVariable() && (providedType.tagBits & TagBits.AnnotationNullMASK) == 0)
return FlowInfo.POTENTIALLY_NULL | FlowInfo.POTENTIALLY_NON_NULL;
return FlowInfo.POTENTIALLY_NULL | FlowInfo.POTENTIALLY_UNKNOWN;
}
return nullStatus;
}
public static NullAnnotationMatching analyse(TypeBinding requiredType, TypeBinding providedType, int nullStatus) {
return analyse(requiredType, providedType, null, null, nullStatus, null, CheckMode.COMPATIBLE);
}
public static NullAnnotationMatching analyse(TypeBinding requiredType, TypeBinding providedType, TypeBinding providedSubstitute, Substitution substitution,
int nullStatus, Expression providedExpression, CheckMode mode)
{
if (!requiredType.enterRecursiveFunction())
return NullAnnotationMatching.NULL_ANNOTATIONS_OK;
try {
Severity severity = Severity.OK;
TypeBinding superTypeHint = null;
NullAnnotationMatching okStatus = NullAnnotationMatching.NULL_ANNOTATIONS_OK;
if (areSameTypes(requiredType, providedType, providedSubstitute)) {
if ((requiredType.tagBits & TagBits.AnnotationNonNull) != 0)
return okNonNullStatus(providedExpression);
return okStatus;
}
if (requiredType instanceof TypeVariableBinding && substitution != null && (mode == CheckMode.EXACT || mode == CheckMode.COMPATIBLE || mode == CheckMode.BOUND_SUPER_CHECK)) {
requiredType.exitRecursiveFunction();
requiredType = Scope.substitute(substitution, requiredType);
if (!requiredType.enterRecursiveFunction())
return NullAnnotationMatching.NULL_ANNOTATIONS_OK;
if (areSameTypes(requiredType, providedType, providedSubstitute)) {
if ((requiredType.tagBits & TagBits.AnnotationNonNull) != 0)
return okNonNullStatus(providedExpression);
return okStatus;
}
}
if (mode == CheckMode.BOUND_CHECK && requiredType instanceof TypeVariableBinding) {
boolean passedBoundCheck = (substitution instanceof ParameterizedTypeBinding) && (((ParameterizedTypeBinding) substitution).tagBits & TagBits.PassedBoundCheck) != 0;
if (!passedBoundCheck) {
TypeBinding superClass = requiredType.superclass();
if (superClass != null && (superClass.hasNullTypeAnnotations() || substitution != null)) {
NullAnnotationMatching status = analyse(superClass, providedType, null, substitution, nullStatus, providedExpression, CheckMode.BOUND_SUPER_CHECK);
severity = severity.max(status.severity);
if (severity == Severity.MISMATCH)
return new NullAnnotationMatching(severity, nullStatus, superTypeHint);
}
TypeBinding[] superInterfaces = requiredType.superInterfaces();
if (superInterfaces != null) {
for (int i = 0; i < superInterfaces.length; i++) {
if (superInterfaces[i].hasNullTypeAnnotations() || substitution != null) {
NullAnnotationMatching status = analyse(superInterfaces[i], providedType, null, substitution, nullStatus, providedExpression, CheckMode.BOUND_SUPER_CHECK);
severity = severity.max(status.severity);
if (severity == Severity.MISMATCH)
return new NullAnnotationMatching(severity, nullStatus, superTypeHint);
}
}
}
}
}
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)
providedDimsTagBits = new long[dims+1];
int currentNullStatus = nullStatus;
for (int i=0; i<=dims; i++) {
long requiredBits = validNullTagBits(requiredDimsTagBits[i]);
long providedBits = validNullTagBits(providedDimsTagBits[i]);
if (i == 0 && requiredBits == TagBits.AnnotationNullable && nullStatus != -1 && mode.requiredNullableMatchesAll()) {
if (nullStatus == FlowInfo.NULL)
break;
} else {
if (i > 0)
currentNullStatus = -1;
Severity dimSeverity = computeNullProblemSeverity(requiredBits, providedBits, currentNullStatus, i == 0 ? mode : mode.toDetail(), false);
if (i > 0 && dimSeverity == Severity.UNCHECKED
&& providedExpression instanceof ArrayAllocationExpression
&& providedBits == 0 && requiredBits != 0)
{
Expression[] dimensions = ((ArrayAllocationExpression) providedExpression).dimensions;
Expression previousDim = dimensions[i-1];
if (previousDim instanceof IntLiteral && previousDim.constant.intValue() == 0) {
dimSeverity = Severity.OK;
nullStatus = -1;
break;
}
}
severity = severity.max(dimSeverity);
if (severity == Severity.MISMATCH) {
if (nullStatus == FlowInfo.NULL)
return new NullAnnotationMatching(severity, nullStatus, null);
return NullAnnotationMatching.NULL_ANNOTATIONS_MISMATCH;
}
}
if (severity == Severity.OK)
nullStatus = -1;
}
} 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() || requiredType.isTypeVariable()) {
long requiredBits = requiredNullTagBits(requiredType, mode);
if (requiredBits == TagBits.AnnotationNullable && nullStatus != -1 && mode.requiredNullableMatchesAll()) {
} else {
long providedBits = providedNullTagBits(providedType);
Severity s = computeNullProblemSeverity(requiredBits, providedBits, nullStatus, mode, requiredType.isTypeVariable());
if (s.isAnyMismatch() && requiredType.isWildcard() && requiredBits != 0) {
if (((WildcardBinding) requiredType).determineNullBitsFromDeclaration(null, null) == 0) {
s = Severity.OK;
}
}
severity = severity.max(s);
if (!severity.isAnyMismatch() && (providedBits & TagBits.AnnotationNullMASK) == TagBits.AnnotationNonNull)
okStatus = okNonNullStatus(providedExpression);
}
if (severity != Severity.MISMATCH && nullStatus != FlowInfo.NULL) {
TypeBinding providedSuper = providedType.findSuperTypeOriginatingFrom(requiredType);
TypeBinding providedSubstituteSuper = providedSubstitute != null ? providedSubstitute.findSuperTypeOriginatingFrom(requiredType) : null;
if (severity == Severity.UNCHECKED && requiredType.isTypeVariable() && providedType.isTypeVariable() && (providedSuper == requiredType || providedSubstituteSuper == requiredType)) {
severity = Severity.OK;
}
if (providedSuper != providedType)
superTypeHint = providedSuper;
if (requiredType.isParameterizedType() && providedSuper instanceof ParameterizedTypeBinding) {
TypeBinding[] requiredArguments = ((ParameterizedTypeBinding) requiredType).arguments;
TypeBinding[] providedArguments = ((ParameterizedTypeBinding) providedSuper).arguments;
TypeBinding[] providedSubstitutes = (providedSubstituteSuper instanceof ParameterizedTypeBinding) ? ((ParameterizedTypeBinding)providedSubstituteSuper).arguments : null;
if (requiredArguments != null && providedArguments != null && requiredArguments.length == providedArguments.length) {
for (int i = 0; i < requiredArguments.length; i++) {
TypeBinding providedArgSubstitute = providedSubstitutes != null ? providedSubstitutes[i] : null;
NullAnnotationMatching status = analyse(requiredArguments[i], providedArguments[i], providedArgSubstitute, substitution, -1, providedExpression, mode.toDetail());
severity = severity.max(status.severity);
if (severity == Severity.MISMATCH)
return new NullAnnotationMatching(severity, nullStatus, superTypeHint);
}
}
}
TypeBinding requiredEnclosing = requiredType.enclosingType();
TypeBinding providedEnclosing = providedType.enclosingType();
if (requiredEnclosing != null && providedEnclosing != null) {
TypeBinding providedEnclSubstitute = providedSubstitute != null ? providedSubstitute.enclosingType() : null;
NullAnnotationMatching status = analyse(requiredEnclosing, providedEnclosing, providedEnclSubstitute, substitution, -1, providedExpression, mode);
severity = severity.max(status.severity);
}
}
}
if (!severity.isAnyMismatch())
return okStatus;
return new NullAnnotationMatching(severity, nullStatus, superTypeHint);
} finally {
requiredType.exitRecursiveFunction();
}
}
public void report(Scope scope) {
}
public static NullAnnotationMatching okNonNullStatus(final Expression providedExpression) {
if (providedExpression instanceof MessageSend) {
final MethodBinding method = ((MessageSend) providedExpression).binding;
if (method != null && method.isValidBinding()) {
MethodBinding originalMethod = method.original();
TypeBinding originalDeclaringClass = originalMethod.declaringClass;
if (originalDeclaringClass instanceof BinaryTypeBinding
&& ((BinaryTypeBinding) originalDeclaringClass).externalAnnotationStatus.isPotentiallyUnannotatedLib()
&& originalMethod.returnType.isTypeVariable()
&& (originalMethod.returnType.tagBits & TagBits.AnnotationNullMASK) == 0)
{
final int severity = ((BinaryTypeBinding) originalDeclaringClass).externalAnnotationStatus == ExternalAnnotationStatus.NO_EEA_FILE
? ProblemSeverities.Warning : ProblemSeverities.Info;
return new NullAnnotationMatching(Severity.LEGACY_WARNING, FlowInfo.UNKNOWN, null) {
@Override
public void report(Scope scope) {
scope.problemReporter().nonNullTypeVariableInUnannotatedBinary(scope.environment(), method, providedExpression, severity);
}
};
}
}
}
return NullAnnotationMatching.NULL_ANNOTATIONS_OK_NONNULL;
}
protected static boolean areSameTypes(TypeBinding requiredType, TypeBinding providedType, TypeBinding providedSubstitute) {
if (requiredType == providedType)
return true;
if (requiredType.isParameterizedType() || requiredType.isArrayType())
return false;
if (TypeBinding.notEquals(requiredType, providedType)) {
if (requiredType instanceof CaptureBinding) {
TypeBinding lowerBound = ((CaptureBinding)requiredType).lowerBound;
if (lowerBound != null && areSameTypes(lowerBound, providedType, providedSubstitute))
return (requiredType.tagBits & TagBits.AnnotationNullMASK) == (providedType.tagBits & TagBits.AnnotationNullMASK);
} else if (requiredType.kind() == Binding.TYPE_PARAMETER && requiredType == providedSubstitute) {
return true;
} else if (providedType instanceof CaptureBinding) {
TypeBinding upperBound = ((CaptureBinding)providedType).upperBound();
if (upperBound != null && areSameTypes(requiredType, upperBound, providedSubstitute))
return (requiredType.tagBits & TagBits.AnnotationNullMASK) == (providedType.tagBits & TagBits.AnnotationNullMASK);
}
return false;
}
return (requiredType.tagBits & TagBits.AnnotationNullMASK) == (providedType.tagBits & TagBits.AnnotationNullMASK);
}
static long requiredNullTagBits(TypeBinding type, CheckMode mode) {
long tagBits = type.tagBits & TagBits.AnnotationNullMASK;
if (tagBits != 0)
return validNullTagBits(tagBits);
if (type.isWildcard()) {
return TagBits.AnnotationNullMASK;
}
if (type.isTypeVariable()) {
if (type.isCapture()) {
TypeBinding lowerBound = ((CaptureBinding) type).lowerBound;
if (lowerBound != null) {
tagBits = lowerBound.tagBits & TagBits.AnnotationNullMASK;
if (tagBits == TagBits.AnnotationNullable)
return TagBits.AnnotationNullable;
}
}
switch (mode) {
case BOUND_CHECK:
case BOUND_SUPER_CHECK:
case OVERRIDE:
case OVERRIDE_RETURN:
break;
default:
return TagBits.AnnotationNonNull;
}
}
return 0;
}
static long providedNullTagBits(TypeBinding type) {
long tagBits = type.tagBits & TagBits.AnnotationNullMASK;
if (tagBits != 0)
return validNullTagBits(tagBits);
if (type.isWildcard()) {
return TagBits.AnnotationNullMASK;
}
if (type.isTypeVariable()) {
TypeVariableBinding typeVariable = (TypeVariableBinding)type;
boolean haveNullBits = false;
if (typeVariable.isCapture()) {
TypeBinding lowerBound = ((CaptureBinding) typeVariable).lowerBound;
if (lowerBound != null) {
tagBits = lowerBound.tagBits & TagBits.AnnotationNullMASK;
if (tagBits == TagBits.AnnotationNullable)
return TagBits.AnnotationNullable;
haveNullBits |= (tagBits != 0);
}
}
if (typeVariable.firstBound != null) {
long boundBits = typeVariable.firstBound.tagBits & TagBits.AnnotationNullMASK;
if (boundBits == TagBits.AnnotationNonNull)
return TagBits.AnnotationNonNull;
haveNullBits |= (boundBits != 0);
}
if (haveNullBits)
return TagBits.AnnotationNullMASK;
}
return 0;
}
public static int nullStatusFromExpressionType(TypeBinding type) {
if (type.isFreeTypeVariable())
return FlowInfo.FREE_TYPEVARIABLE;
long bits = type.tagBits & TagBits.AnnotationNullMASK;
if (bits == 0)
return FlowInfo.UNKNOWN;
if (bits == TagBits.AnnotationNonNull)
return FlowInfo.NON_NULL;
return FlowInfo.POTENTIALLY_NON_NULL | FlowInfo.POTENTIALLY_NULL;
}
public static long validNullTagBits(long bits) {
bits &= TagBits.AnnotationNullMASK;
return bits == TagBits.AnnotationNullMASK ? 0 : bits;
}
public static TypeBinding moreDangerousType(TypeBinding one, TypeBinding two) {
if (one == null) return null;
long oneNullBits = validNullTagBits(one.tagBits);
long twoNullBits = validNullTagBits(two.tagBits);
if (oneNullBits != twoNullBits) {
if (oneNullBits == TagBits.AnnotationNullable)
return one;
if (twoNullBits == TagBits.AnnotationNullable)
return two;
if (oneNullBits == 0)
return one;
return two;
} else if (one != two) {
if (analyse(one, two, -1).isAnyMismatch())
return two;
}
return one;
}
private static Severity computeNullProblemSeverity(long requiredBits, long providedBits, int nullStatus, CheckMode mode, boolean requiredIsTypeVariable) {
if (requiredBits == providedBits)
return Severity.OK;
if (requiredBits == 0) {
switch (mode) {
case COMPATIBLE:
case BOUND_CHECK:
case BOUND_SUPER_CHECK:
case EXACT:
return Severity.OK;
case OVERRIDE_RETURN:
if (providedBits == TagBits.AnnotationNonNull)
return Severity.OK;
if (!requiredIsTypeVariable)
return Severity.OK;
return Severity.UNCHECKED;
case OVERRIDE:
return Severity.UNCHECKED;
}
} else if (requiredBits == TagBits.AnnotationNullMASK) {
return Severity.OK;
} else if (requiredBits == TagBits.AnnotationNonNull) {
switch (mode) {
case COMPATIBLE:
if (nullStatus == FlowInfo.NULL)
return Severity.MISMATCH;
case BOUND_SUPER_CHECK:
if (nullStatus == FlowInfo.NON_NULL)
return Severity.OK;
case BOUND_CHECK:
case EXACT:
case OVERRIDE_RETURN:
case OVERRIDE:
if (providedBits == 0)
return Severity.UNCHECKED;
return Severity.MISMATCH;
}
} else if (requiredBits == TagBits.AnnotationNullable) {
switch (mode) {
case COMPATIBLE:
case OVERRIDE_RETURN:
case BOUND_SUPER_CHECK:
return Severity.OK;
case BOUND_CHECK:
case EXACT:
if (providedBits == 0)
return Severity.UNCHECKED;
return Severity.MISMATCH;
case OVERRIDE:
return Severity.MISMATCH;
}
}
return Severity.OK;
}
static class SearchContradictions extends TypeBindingVisitor {
ReferenceBinding typeWithContradiction;
@Override
public boolean visit(ReferenceBinding referenceBinding) {
if ((referenceBinding.tagBits & TagBits.AnnotationNullMASK) == TagBits.AnnotationNullMASK) {
this.typeWithContradiction = referenceBinding;
return false;
}
return true;
}
@Override
public boolean visit(TypeVariableBinding typeVariable) {
if (!visit((ReferenceBinding)typeVariable))
return false;
long allNullBits = typeVariable.tagBits & TagBits.AnnotationNullMASK;
if (typeVariable.firstBound != null)
allNullBits = typeVariable.firstBound.tagBits & TagBits.AnnotationNullMASK;
for (TypeBinding otherBound : typeVariable.otherUpperBounds())
allNullBits |= otherBound.tagBits & TagBits.AnnotationNullMASK;
if (allNullBits == TagBits.AnnotationNullMASK) {
this.typeWithContradiction = typeVariable;
return false;
}
return true;
}
@Override
public boolean visit(RawTypeBinding rawType) {
return visit((ReferenceBinding)rawType);
}
@Override
public boolean visit(WildcardBinding wildcardBinding) {
long allNullBits = wildcardBinding.tagBits & TagBits.AnnotationNullMASK;
switch (wildcardBinding.boundKind) {
case Wildcard.EXTENDS:
allNullBits |= wildcardBinding.bound.tagBits & TagBits.AnnotationNonNull;
break;
case Wildcard.SUPER:
allNullBits |= wildcardBinding.bound.tagBits & TagBits.AnnotationNullable;
break;
}
if (allNullBits == TagBits.AnnotationNullMASK) {
this.typeWithContradiction = wildcardBinding;
return false;
}
return true;
}
@Override
public boolean visit(ParameterizedTypeBinding parameterizedTypeBinding) {
if (!visit((ReferenceBinding) parameterizedTypeBinding))
return false;
return super.visit(parameterizedTypeBinding);
}
}
public static MethodBinding checkForContradictions(MethodBinding method, Object location, Scope scope) {
int start = 0, end = 0;
if (location instanceof InvocationSite) {
start = ((InvocationSite) location).sourceStart();
end = ((InvocationSite) location).sourceEnd();
} else if (location instanceof ASTNode) {
start = ((ASTNode) location).sourceStart;
end = ((ASTNode) location).sourceEnd;
}
SearchContradictions searchContradiction = new SearchContradictions();
TypeBindingVisitor.visit(searchContradiction, method.returnType);
if (searchContradiction.typeWithContradiction != null) {
if (scope == null)
return new ProblemMethodBinding(method, method.selector, method.parameters, ProblemReasons.ContradictoryNullAnnotations);
scope.problemReporter().contradictoryNullAnnotationsInferred(method, start, end, location instanceof FunctionalExpression);
return method;
}
Expression[] arguments = null;
if (location instanceof Invocation)
arguments = ((Invocation)location).arguments();
for (int i = 0; i < method.parameters.length; i++) {
TypeBindingVisitor.visit(searchContradiction, method.parameters[i]);
if (searchContradiction.typeWithContradiction != null) {
if (scope == null)
return new ProblemMethodBinding(method, method.selector, method.parameters, ProblemReasons.ContradictoryNullAnnotations);
if (arguments != null && i < arguments.length)
scope.problemReporter().contradictoryNullAnnotationsInferred(method, arguments[i]);
else
scope.problemReporter().contradictoryNullAnnotationsInferred(method, start, end, location instanceof FunctionalExpression);
return method;
}
}
return method;
}
public static boolean hasContradictions(TypeBinding type) {
SearchContradictions searchContradiction = new SearchContradictions();
TypeBindingVisitor.visit(searchContradiction, type);
return searchContradiction.typeWithContradiction != null;
}
public static TypeBinding strongerType(TypeBinding type1, TypeBinding type2, LookupEnvironment environment) {
if ((type1.tagBits & TagBits.AnnotationNonNull) != 0)
return mergeTypeAnnotations(type1, type2, true, environment);
return mergeTypeAnnotations(type2, type1, true, environment);
}
public static TypeBinding[] weakerTypes(TypeBinding[] parameters1, TypeBinding[] parameters2, LookupEnvironment environment) {
TypeBinding[] newParameters = new TypeBinding[parameters1.length];
for (int i = 0; i < newParameters.length; i++) {
long tagBits1 = parameters1[i].tagBits;
long tagBits2 = parameters2[i].tagBits;
if ((tagBits1 & TagBits.AnnotationNullable) != 0)
newParameters[i] = mergeTypeAnnotations(parameters1[i], parameters2[i], true, environment);
else if ((tagBits2 & TagBits.AnnotationNullable) != 0)
newParameters[i] = mergeTypeAnnotations(parameters2[i], parameters1[i], true, environment);
else if ((tagBits1 & TagBits.AnnotationNonNull) == 0)
newParameters[i] = mergeTypeAnnotations(parameters1[i], parameters2[i], true, environment);
else
newParameters[i] = mergeTypeAnnotations(parameters2[i], parameters1[i], true, environment);
}
return newParameters;
}
private static TypeBinding mergeTypeAnnotations(TypeBinding type, TypeBinding otherType, boolean top, LookupEnvironment environment) {
TypeBinding mainType = type;
if (!top) {
AnnotationBinding[] otherAnnotations = otherType.getTypeAnnotations();
if (otherAnnotations != Binding.NO_ANNOTATIONS)
mainType = environment.createAnnotatedType(type, otherAnnotations);
}
if (mainType.isParameterizedType() && otherType.isParameterizedType()) {
ParameterizedTypeBinding ptb = (ParameterizedTypeBinding) type, otherPTB = (ParameterizedTypeBinding) otherType;
TypeBinding[] typeArguments = ptb.arguments;
TypeBinding[] otherTypeArguments = otherPTB.arguments;
TypeBinding[] newTypeArguments = new TypeBinding[typeArguments.length];
for (int i = 0; i < typeArguments.length; i++) {
newTypeArguments[i] = mergeTypeAnnotations(typeArguments[i], otherTypeArguments[i], false, environment);
}
return environment.createParameterizedType(ptb.genericType(), newTypeArguments, ptb.enclosingType());
}
return mainType;
}
@SuppressWarnings("nls")
@Override
public String toString() {
if (this == NULL_ANNOTATIONS_OK) return "OK";
if (this == NULL_ANNOTATIONS_MISMATCH) return "MISMATCH";
if (this == NULL_ANNOTATIONS_OK_NONNULL) return "OK NonNull";
if (this == NULL_ANNOTATIONS_UNCHECKED) return "UNCHECKED";
StringBuilder buf = new StringBuilder();
buf.append("Analysis result: severity="+this.severity);
buf.append(" nullStatus="+this.nullStatus);
return buf.toString();
}
}