blob: 4714a631b0017ced8d0a5704dbd1fde079c5c519 [file] [log] [blame]
package org.checkerframework.checker.nullness;
import java.util.Set;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.type.TypeMirror;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.checker.nullness.qual.PolyNull;
import org.checkerframework.dataflow.qual.Pure;
import org.checkerframework.dataflow.qual.SideEffectFree;
import org.checkerframework.framework.flow.CFAbstractAnalysis;
import org.checkerframework.framework.flow.CFAbstractValue;
import org.checkerframework.framework.flow.CFValue;
import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.TypesUtils;
/**
* Behaves just like {@link CFValue}, but additionally tracks whether at this point {@link PolyNull}
* is known to be {@link NonNull} or {@link Nullable} (or not known to be either)
*/
public class NullnessValue extends CFAbstractValue<NullnessValue> {
/** True if, at this point, {@link PolyNull} is known to be {@link NonNull}. */
protected boolean isPolyNullNonNull;
/** True if, at this point, {@link PolyNull} is known to be {@link Nullable}. */
protected boolean isPolyNullNull;
public NullnessValue(
CFAbstractAnalysis<NullnessValue, ?, ?> analysis,
Set<AnnotationMirror> annotations,
TypeMirror underlyingType) {
super(analysis, annotations, underlyingType);
}
@Override
public NullnessValue leastUpperBound(NullnessValue other) {
NullnessValue result = super.leastUpperBound(other);
AnnotationMirror resultNullableAnno =
analysis.getTypeFactory().getAnnotationByClass(result.annotations, Nullable.class);
if (resultNullableAnno != null && other != null) {
if ((this.isPolyNullNonNull
&& this.containsNonNullOrPolyNull()
&& other.isPolyNullNull
&& other.containsNullableOrPolyNull())
|| (other.isPolyNullNonNull
&& other.containsNonNullOrPolyNull()
&& this.isPolyNullNull
&& this.containsNullableOrPolyNull())) {
result.annotations.remove(resultNullableAnno);
result.annotations.add(((NullnessAnnotatedTypeFactory) analysis.getTypeFactory()).POLYNULL);
}
}
return result;
}
/**
* Returns true if this value contans {@code @NonNull} or {@code @PolyNull}.
*
* @return true if this value contans {@code @NonNull} or {@code @PolyNull}
*/
@Pure
private boolean containsNonNullOrPolyNull() {
return analysis.getTypeFactory().containsSameByClass(annotations, NonNull.class)
|| analysis.getTypeFactory().containsSameByClass(annotations, PolyNull.class);
}
/**
* Returns true if this value contans {@code @Nullable} or {@code @PolyNull}.
*
* @return true if this value contans {@code @Nullable} or {@code @PolyNull}
*/
@Pure
private boolean containsNullableOrPolyNull() {
return analysis.getTypeFactory().containsSameByClass(annotations, Nullable.class)
|| analysis.getTypeFactory().containsSameByClass(annotations, PolyNull.class);
}
@SideEffectFree
@Override
public String toStringSimple() {
return "NV{"
+ AnnotationUtils.toStringSimple(annotations)
+ ", "
+ TypesUtils.simpleTypeName(underlyingType)
+ ", "
+ (isPolyNullNonNull ? 't' : 'f')
+ ' '
+ (isPolyNullNull ? 't' : 'f')
+ '}';
}
}