package org.checkerframework.framework.flow; /*>>> import org.checkerframework.checker.nullness.qual.Nullable; */ import java.util.ArrayList; import java.util.Collection; import java.util.Set; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeVariable; import javax.lang.model.type.WildcardType; import javax.lang.model.util.Types; import org.checkerframework.dataflow.analysis.AbstractValue; import org.checkerframework.dataflow.qual.Pure; import org.checkerframework.dataflow.qual.SideEffectFree; import org.checkerframework.dataflow.util.HashCodeUtils; import org.checkerframework.framework.type.AnnotatedTypeMirror; import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable; import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedWildcardType; import org.checkerframework.framework.type.QualifierHierarchy; import org.checkerframework.framework.util.AnnotatedTypes; import org.checkerframework.framework.util.PluginUtil; import org.checkerframework.javacutil.AnnotationUtils; import org.checkerframework.javacutil.InternalUtils; import org.checkerframework.javacutil.TypesUtils; /** * An implementation of an abstract value used by the Checker Framework * org.checkerframework.dataflow analysis. * * <p>A value holds a set of annotations and a type mirror. The set of annotations represents the * primary annotation on a type; therefore, the set of annotations must have an annotation for each * hierarchy unless the type mirror is a type variable or a wildcard that extends a type variable. * Both type variables and wildcards may be missing a primary annotation. For this set of * annotations, there is an additional constraint that only wildcards that extend type variables can * be missing annotations. * * <p>In order to compute {@link #leastUpperBound(CFAbstractValue)} and {@link * #mostSpecific(CFAbstractValue, CFAbstractValue)}, the case where one value has an annotation in a * hierarchy and the other does not must be handled. For type variables, the {@link * AnnotatedTypeVariable} for the declaration of the type variable is used. The {@link * AnnotatedTypeVariable} is computed using the type mirror. For wildcards, it is not always * possible to get the {@link AnnotatedWildcardType} for the type mirror. However, a * CFAbstractValue's type mirror is only a wildcard if the type of some expression is a wildcard. * The type of an expression is only a wildcard because the Checker Framework does not implement * capture conversion. For these uses of uncaptured wildcards, only the primary annotation on the * upper bound is ever used. So, the set of annotations represents the primary annotation on the * wildcard's upper bound. If that upper bound is a type variable, then the set of annotations could * be missing an annotation in a hierarchy. * * @author Stefan Heule */ public abstract class CFAbstractValue<V extends CFAbstractValue<V>> implements AbstractValue<V> { /** The analysis class this value belongs to. */ protected final CFAbstractAnalysis<V, ?, ?> analysis; protected final TypeMirror underlyingType; protected final Set<AnnotationMirror> annotations; public CFAbstractValue( CFAbstractAnalysis<V, ?, ?> analysis, Set<AnnotationMirror> annotations, TypeMirror underlyingType) { this.analysis = analysis; this.annotations = annotations; this.underlyingType = underlyingType; assert validateSet( this.getAnnotations(), this.getUnderlyingType(), analysis.getTypeFactory().getQualifierHierarchy()) : "Encountered invalid type: " + underlyingType + " annotations: " + PluginUtil.join(", ", annotations); } public static boolean validateSet( Set<AnnotationMirror> annos, TypeMirror typeMirror, QualifierHierarchy hierarchy) { if (canBeMissingAnnotations(typeMirror)) { return true; } Set<AnnotationMirror> missingHierarchy = null; for (AnnotationMirror top : hierarchy.getTopAnnotations()) { AnnotationMirror anno = hierarchy.findAnnotationInHierarchy(annos, top); if (anno == null) { if (missingHierarchy == null) { missingHierarchy = AnnotationUtils.createAnnotationSet(); } missingHierarchy.add(top); } } return missingHierarchy == null; } /** * Returns whether or not the set of annotations can be missing an annotation for any hierarchy * * @return whether or not the set of annotations can be missing an annotation */ public boolean canBeMissingAnnotations() { return canBeMissingAnnotations(underlyingType); } private static boolean canBeMissingAnnotations(TypeMirror typeMirror) { if (typeMirror == null) { return false; } if (typeMirror.getKind() == TypeKind.VOID || typeMirror.getKind() == TypeKind.NONE || typeMirror.getKind() == TypeKind.PACKAGE) { return true; } if (typeMirror.getKind() == TypeKind.WILDCARD) { return canBeMissingAnnotations(((WildcardType) typeMirror).getExtendsBound()); } return typeMirror.getKind() == TypeKind.TYPEVAR; } /** * Returns a set of annotations. If {@link #canBeMissingAnnotations()} returns true, then the * set of annotations may not have an annotation for every hierarchy. * * @return returns a set of annotations */ @Pure public Set<AnnotationMirror> getAnnotations() { return annotations; } @Pure public TypeMirror getUnderlyingType() { return underlyingType; } @Override public boolean equals(Object obj) { if (obj == null || !(obj instanceof CFAbstractValue)) { return false; } CFAbstractValue<?> other = (CFAbstractValue<?>) obj; if (!analysis.getTypes().isSameType(this.getUnderlyingType(), other.getUnderlyingType())) { return false; } return AnnotationUtils.areSame(this.getAnnotations(), other.getAnnotations()); } @Pure @Override public int hashCode() { Collection<Object> objects = new ArrayList<>(); objects.addAll(getAnnotations()); objects.add(underlyingType); return HashCodeUtils.hash(objects); } /** @return the string representation as a comma-separated list */ @SideEffectFree @Override public String toString() { return "CFAbstractValue{" + "annotations=" + annotations + ", underlyingType=" + underlyingType + '}'; } /** * Returns the more specific version of two values {@code this} and {@code other}. If they do * not contain information for all hierarchies, then it is possible that information from both * {@code this} and {@code other} are taken. * * <p>If neither of the two is more specific for one of the hierarchies (i.e., if the two are * incomparable as determined by {@link QualifierHierarchy#isSubtype(AnnotationMirror, * AnnotationMirror)}, then the respective value from {@code backup} is used. */ public V mostSpecific(/*@Nullable*/ V other, /*@Nullable*/ V backup) { if (other == null) { @SuppressWarnings("unchecked") V v = (V) this; return v; } Types types = analysis.getTypes(); TypeMirror mostSpecifTypeMirror; if (types.isAssignable(this.getUnderlyingType(), other.getUnderlyingType())) { mostSpecifTypeMirror = this.getUnderlyingType(); } else if (types.isAssignable(other.getUnderlyingType(), this.getUnderlyingType())) { mostSpecifTypeMirror = other.getUnderlyingType(); } else if (TypesUtils.isErasedSubtype( types, this.getUnderlyingType(), other.getUnderlyingType())) { mostSpecifTypeMirror = this.getUnderlyingType(); } else if (TypesUtils.isErasedSubtype( types, other.getUnderlyingType(), this.getUnderlyingType())) { mostSpecifTypeMirror = other.getUnderlyingType(); } else { mostSpecifTypeMirror = this.getUnderlyingType(); } Set<AnnotationMirror> mostSpecific = AnnotationUtils.createAnnotationSet(); MostSpecificVisitor ms = new MostSpecificVisitor( mostSpecifTypeMirror, this.getUnderlyingType(), other.getUnderlyingType(), this.getAnnotations(), other.getAnnotations(), backup, mostSpecific); ms.visit(); if (ms.error) { return backup; } return analysis.createAbstractValue(mostSpecific, mostSpecifTypeMirror); } private class MostSpecificVisitor extends AnnotationSetAndTypeMirrorVisitor { boolean error = false; // TypeMirror backupTypeMirror; Set<AnnotationMirror> backupSet; // AnnotatedTypeVariable backupAtv; Set<AnnotationMirror> mostSpecific; public MostSpecificVisitor( TypeMirror result, TypeMirror aTypeMirror, TypeMirror bTypeMirror, Set<AnnotationMirror> aSet, Set<AnnotationMirror> bSet, V backup, Set<AnnotationMirror> mostSpecific) { super(result, aTypeMirror, bTypeMirror, aSet, bSet); this.mostSpecific = mostSpecific; if (backup != null) { this.backupSet = backup.getAnnotations(); // this.backupTypeMirror = backup.getUnderlyingType(); // this.backupAtv = getEffectTypeVar(backupTypeMirror); } else { // this.backupAtv = null; // this.backupTypeMirror = null; this.backupSet = null; } } private AnnotationMirror getBackUpAnnoIn(AnnotationMirror top) { if (backupSet == null) { // If there is no back up value, but on is required then the resulting set will // not be the most specific. Indicate this with the error. error = true; return null; } QualifierHierarchy hierarchy = analysis.getTypeFactory().getQualifierHierarchy(); return hierarchy.findAnnotationInHierarchy(backupSet, top); } @Override protected void visitAnnotationExistInBothSets( AnnotationMirror a, AnnotationMirror b, AnnotationMirror top) { QualifierHierarchy hierarchy = analysis.getTypeFactory().getQualifierHierarchy(); if (hierarchy.isSubtype(a, b)) { mostSpecific.add(a); } else if (hierarchy.isSubtype(b, a)) { mostSpecific.add(b); } else { AnnotationMirror backup = getBackUpAnnoIn(top); if (backup != null) { mostSpecific.add(backup); } } } @Override protected void visitNeitherAnnotationExistsInBothSets( AnnotatedTypeVariable aAtv, AnnotatedTypeVariable bAtv, AnnotationMirror top) { if (canBeMissingAnnotations(result)) { // don't add an annotation } else { AnnotationMirror aUB = aAtv.getUpperBound().getEffectiveAnnotationInHierarchy(top); AnnotationMirror bUB = bAtv.getUpperBound().getEffectiveAnnotationInHierarchy(top); visitAnnotationExistInBothSets(aUB, bUB, top); } } @Override protected void visitAnnotationExistInOneSet( AnnotationMirror anno, AnnotatedTypeVariable atv, AnnotationMirror top) { QualifierHierarchy hierarchy = analysis.getTypeFactory().getQualifierHierarchy(); AnnotationMirror upperBound = atv.getEffectiveAnnotationInHierarchy(top); if (!canBeMissingAnnotations(result)) { visitAnnotationExistInBothSets(anno, upperBound, top); return; } Set<AnnotationMirror> lBSet = AnnotatedTypes.findEffectiveLowerBoundAnnotations(hierarchy, atv); AnnotationMirror lowerBound = hierarchy.findAnnotationInHierarchy(lBSet, top); if (hierarchy.isSubtype(upperBound, anno)) { // no anno is more specific than anno } else if (hierarchy.isSubtype(anno, lowerBound)) { mostSpecific.add(anno); } else { AnnotationMirror backup = getBackUpAnnoIn(top); if (backup != null) { mostSpecific.add(backup); } } } } @Override public V leastUpperBound(/*@Nullable*/ V other) { return upperBound(other, false); } public V widenUpperBound(/*@Nullable*/ V other) { return upperBound(other, true); } private V upperBound(/*@Nullable*/ V other, boolean shouldWiden) { if (other == null) { @SuppressWarnings("unchecked") V v = (V) this; return v; } ProcessingEnvironment processingEnv = analysis.getTypeFactory().getProcessingEnv(); Set<AnnotationMirror> lub = AnnotationUtils.createAnnotationSet(); TypeMirror lubTypeMirror = InternalUtils.leastUpperBound( processingEnv, this.getUnderlyingType(), other.getUnderlyingType()); LubVisitor lubVisitor = new LubVisitor( lubTypeMirror, this.getUnderlyingType(), other.getUnderlyingType(), this.getAnnotations(), other.getAnnotations(), lub, shouldWiden); lubVisitor.visit(); return analysis.createAbstractValue(lub, lubTypeMirror); } class LubVisitor extends AnnotationSetAndTypeMirrorVisitor { Set<AnnotationMirror> lubSet; boolean widen; public LubVisitor( TypeMirror result, TypeMirror aTypeMirror, TypeMirror bTypeMirror, Set<AnnotationMirror> aSet, Set<AnnotationMirror> bSet, Set<AnnotationMirror> lubSet, boolean shouldWiden) { super(result, aTypeMirror, bTypeMirror, aSet, bSet); this.lubSet = lubSet; this.widen = shouldWiden; } private AnnotationMirror computeUpperBound(AnnotationMirror a, AnnotationMirror b) { QualifierHierarchy hierarchy = analysis.getTypeFactory().getQualifierHierarchy(); if (widen) { return hierarchy.widenUpperBound(a, b); } else { return hierarchy.leastUpperBound(a, b); } } @Override protected void visitAnnotationExistInBothSets( AnnotationMirror a, AnnotationMirror b, AnnotationMirror top) { lubSet.add(computeUpperBound(a, b)); } @Override protected void visitNeitherAnnotationExistsInBothSets( AnnotatedTypeVariable aAtv, AnnotatedTypeVariable bAtv, AnnotationMirror top) { if (canBeMissingAnnotations(result)) { // don't add an annotation } else { AnnotationMirror aUB = aAtv.getUpperBound().getEffectiveAnnotationInHierarchy(top); AnnotationMirror bUB = bAtv.getUpperBound().getEffectiveAnnotationInHierarchy(top); lubSet.add(computeUpperBound(aUB, bUB)); } } @Override protected void visitAnnotationExistInOneSet( AnnotationMirror anno, AnnotatedTypeVariable atv, AnnotationMirror top) { QualifierHierarchy hierarchy = analysis.getTypeFactory().getQualifierHierarchy(); AnnotationMirror upperBound = atv.getUpperBound().getEffectiveAnnotationInHierarchy(top); if (!canBeMissingAnnotations(result)) { lubSet.add(computeUpperBound(anno, upperBound)); } else { Set<AnnotationMirror> lBSet = AnnotatedTypes.findEffectiveLowerBoundAnnotations(hierarchy, atv); AnnotationMirror lowerBound = hierarchy.findAnnotationInHierarchy(lBSet, top); if (!hierarchy.isSubtype(anno, lowerBound)) { lubSet.add(computeUpperBound(anno, upperBound)); } } } } /** * Iterates through two sets of AnnotationMirrors by hierarchy and calls one of three methods * depending on whether an annotation exists for the hierarchy in each set. Also, passes a * {@link AnnotatedTypeVariable} if an annotation does not exist. */ protected abstract class AnnotationSetAndTypeMirrorVisitor { TypeMirror result; private AnnotatedTypeVariable aAtv; private AnnotatedTypeVariable bAtv; private Set<AnnotationMirror> aSet; private Set<AnnotationMirror> bSet; public AnnotationSetAndTypeMirrorVisitor( TypeMirror result, TypeMirror aTypeMirror, TypeMirror bTypeMirror, Set<AnnotationMirror> aSet, Set<AnnotationMirror> bSet) { this.result = result; this.aSet = aSet; this.bSet = bSet; this.aAtv = getEffectTypeVar(aTypeMirror); this.bAtv = getEffectTypeVar(bTypeMirror); } void visit() { QualifierHierarchy hierarchy = analysis.getTypeFactory().getQualifierHierarchy(); Set<? extends AnnotationMirror> tops = hierarchy.getTopAnnotations(); for (AnnotationMirror top : tops) { AnnotationMirror a = hierarchy.findAnnotationInHierarchy(aSet, top); AnnotationMirror b = hierarchy.findAnnotationInHierarchy(bSet, top); if (a != null && b != null) { visitAnnotationExistInBothSets(a, b, top); } else if (a != null) { visitAnnotationExistInOneSet(a, bAtv, top); } else if (b != null) { visitAnnotationExistInOneSet(b, aAtv, top); } else { visitNeitherAnnotationExistsInBothSets(aAtv, bAtv, top); } } } protected abstract void visitAnnotationExistInBothSets( AnnotationMirror a, AnnotationMirror b, AnnotationMirror top); protected abstract void visitNeitherAnnotationExistsInBothSets( AnnotatedTypeVariable aAtv, AnnotatedTypeVariable bAtv, AnnotationMirror top); protected abstract void visitAnnotationExistInOneSet( AnnotationMirror anno, AnnotatedTypeVariable atv, AnnotationMirror top); } /** * Returns the AnnotatedTypeVariable associated with the given TypeMirror or null. * * <p>If TypeMirror is a type variable, then the AnnotatedTypeVariable return is the declaration * of that TypeMirror. If the TypeMirror is a wildcard that extends a type variable, the * AnnotatedTypeVariable return is the declaration of that type variable. Otherwise, null is * returned. */ private AnnotatedTypeVariable getEffectTypeVar(TypeMirror typeMirror) { if (typeMirror == null) { return null; } else if (typeMirror.getKind() == TypeKind.WILDCARD) { return getEffectTypeVar(((WildcardType) typeMirror).getExtendsBound()); } else if (typeMirror.getKind() == TypeKind.TYPEVAR) { TypeVariable typevar = ((TypeVariable) typeMirror); AnnotatedTypeMirror atm = analysis.getTypeFactory().getAnnotatedType(typevar.asElement()); return (AnnotatedTypeVariable) atm; } else { return null; } } }