| package org.checkerframework.framework.flow; |
| |
| import java.util.Objects; |
| 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.checker.nullness.qual.Nullable; |
| import org.checkerframework.dataflow.analysis.AbstractValue; |
| import org.checkerframework.dataflow.analysis.Analysis; |
| import org.checkerframework.dataflow.qual.Pure; |
| import org.checkerframework.dataflow.qual.SideEffectFree; |
| 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.javacutil.AnnotationUtils; |
| import org.checkerframework.javacutil.TypesUtils; |
| import org.plumelib.util.StringsPlume; |
| |
| /** |
| * 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. |
| */ |
| public abstract class CFAbstractValue<V extends CFAbstractValue<V>> implements AbstractValue<V> { |
| |
| /** The analysis class this value belongs to. */ |
| protected final CFAbstractAnalysis<V, ?, ?> analysis; |
| |
| /** The underlying (Java) type in this abstract value. */ |
| protected final TypeMirror underlyingType; |
| /** The annotations in this abstract value. */ |
| protected final Set<AnnotationMirror> annotations; |
| |
| /** |
| * Creates a new CFAbstractValue. |
| * |
| * @param analysis the analysis class this value belongs to |
| * @param annotations the annotations in this abstract value |
| * @param underlyingType the underlying (Java) type in this abstract value |
| */ |
| protected 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: " |
| + StringsPlume.join(", ", annotations); |
| } |
| |
| /** |
| * Returns true if the set has an annotation from every hierarchy (or if it doesn't need to); |
| * returns false if the set is missing an annotation from some hierarchy. |
| * |
| * @param annos set of annotations |
| * @param typeMirror where the annotations are written |
| * @param hierarchy the qualifier hierarchy |
| * @return true if no annotations are missing |
| */ |
| 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); |
| } |
| |
| /** |
| * Returns true if it is OK for the given type mirror not to be annotated, such as for VOID, NONE, |
| * PACKAGE, TYPEVAR, and some WILDCARD. |
| * |
| * @param typeMirror a type |
| * @return true if it is OK for the given type mirror not to be annotated |
| */ |
| 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. |
| * |
| * <p>To get the single annotation in a particular hierarchy, use {@link |
| * QualifierHierarchy#findAnnotationInHierarchy}. |
| * |
| * @return a set of annotations |
| */ |
| @Pure |
| public Set<AnnotationMirror> getAnnotations() { |
| return annotations; |
| } |
| |
| @Pure |
| public TypeMirror getUnderlyingType() { |
| return underlyingType; |
| } |
| |
| @SuppressWarnings("interning:not.interned") // efficiency pre-test |
| @Override |
| public boolean equals(@Nullable Object obj) { |
| if (!(obj instanceof CFAbstractValue)) { |
| return false; |
| } |
| |
| CFAbstractValue<?> other = (CFAbstractValue<?>) obj; |
| if (this.getUnderlyingType() != other.getUnderlyingType() |
| && !analysis.getTypes().isSameType(this.getUnderlyingType(), other.getUnderlyingType())) { |
| return false; |
| } |
| return AnnotationUtils.areSame(this.getAnnotations(), other.getAnnotations()); |
| } |
| |
| @Pure |
| @Override |
| public int hashCode() { |
| return Objects.hash(getAnnotations(), underlyingType); |
| } |
| |
| /** |
| * Returns the string representation, using fully-qualified names. |
| * |
| * @return the string representation, using fully-qualified names |
| */ |
| @SideEffectFree |
| public String toStringFullyQualified() { |
| return "CFAV{" + annotations + ", " + underlyingType + '}'; |
| } |
| |
| /** |
| * Returns the string representation, using simple (not fully-qualified) names. |
| * |
| * @return the string representation, using simple (not fully-qualified) names |
| */ |
| @SideEffectFree |
| public String toStringSimple() { |
| return "CFAV{" |
| + AnnotationUtils.toStringSimple(annotations) |
| + ", " |
| + TypesUtils.simpleTypeName(underlyingType) |
| + '}'; |
| } |
| |
| /** |
| * Returns the string representation. |
| * |
| * @return the string representation |
| */ |
| @SideEffectFree |
| @Override |
| public String toString() { |
| return toStringSimple(); |
| } |
| |
| /** |
| * 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( |
| this.getUnderlyingType(), other.getUnderlyingType(), types)) { |
| mostSpecifTypeMirror = this.getUnderlyingType(); |
| } else if (TypesUtils.isErasedSubtype( |
| other.getUnderlyingType(), this.getUnderlyingType(), types)) { |
| mostSpecifTypeMirror = other.getUnderlyingType(); |
| } else { |
| mostSpecifTypeMirror = this.getUnderlyingType(); |
| } |
| |
| MostSpecificVisitor ms = |
| new MostSpecificVisitor(this.getUnderlyingType(), other.getUnderlyingType(), backup); |
| Set<AnnotationMirror> mostSpecific = |
| ms.combineSets( |
| this.getUnderlyingType(), |
| this.getAnnotations(), |
| other.getUnderlyingType(), |
| other.getAnnotations(), |
| canBeMissingAnnotations(mostSpecifTypeMirror)); |
| if (ms.error) { |
| return backup; |
| } |
| return analysis.createAbstractValue(mostSpecific, mostSpecifTypeMirror); |
| } |
| |
| /** Computes the most specific annotations. */ |
| private class MostSpecificVisitor extends AnnotationSetCombiner { |
| /** If set to true, then this visitor was unable to find a most specific annotation. */ |
| boolean error = false; |
| |
| /** Set of annotations to use if a most specific value cannot be found. */ |
| final Set<AnnotationMirror> backupSet; |
| |
| /** TypeMirror for the "a" value. */ |
| final TypeMirror aTypeMirror; |
| |
| /** TypeMirror for the "b" value. */ |
| final TypeMirror bTypeMirror; |
| |
| /** |
| * Create a {@link MostSpecificVisitor}. |
| * |
| * @param aTypeMirror type of the "a" value |
| * @param bTypeMirror type of the "b" value |
| * @param backup value to use if no most specific value is found |
| */ |
| public MostSpecificVisitor(TypeMirror aTypeMirror, TypeMirror bTypeMirror, V backup) { |
| this.aTypeMirror = aTypeMirror; |
| this.bTypeMirror = bTypeMirror; |
| 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 one 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 @Nullable AnnotationMirror combineTwoAnnotations( |
| AnnotationMirror a, AnnotationMirror b, AnnotationMirror top) { |
| QualifierHierarchy hierarchy = analysis.getTypeFactory().getQualifierHierarchy(); |
| if (analysis |
| .getTypeFactory() |
| .hasQualifierParameterInHierarchy(TypesUtils.getTypeElement(aTypeMirror), top) |
| && analysis |
| .getTypeFactory() |
| .hasQualifierParameterInHierarchy(TypesUtils.getTypeElement(bTypeMirror), top)) { |
| // Both types have qualifier parameters, so they are related by invariance rather than |
| // subtyping. |
| if (hierarchy.isSubtype(a, b) && hierarchy.isSubtype(b, a)) { |
| return b; |
| } |
| } else if (hierarchy.isSubtype(a, b)) { |
| return a; |
| } else if (hierarchy.isSubtype(b, a)) { |
| return b; |
| } |
| return getBackUpAnnoIn(top); |
| } |
| |
| @Override |
| protected @Nullable AnnotationMirror combineNoAnnotations( |
| AnnotatedTypeVariable aAtv, |
| AnnotatedTypeVariable bAtv, |
| AnnotationMirror top, |
| boolean canCombinedSetBeMissingAnnos) { |
| if (canCombinedSetBeMissingAnnos) { |
| return null; |
| } else { |
| AnnotationMirror aUB = aAtv.getEffectiveAnnotationInHierarchy(top); |
| AnnotationMirror bUB = bAtv.getEffectiveAnnotationInHierarchy(top); |
| return combineTwoAnnotations(aUB, bUB, top); |
| } |
| } |
| |
| @Override |
| protected @Nullable AnnotationMirror combineOneAnnotation( |
| AnnotationMirror annotation, |
| AnnotatedTypeVariable typeVar, |
| AnnotationMirror top, |
| boolean canCombinedSetBeMissingAnnos) { |
| |
| QualifierHierarchy hierarchy = analysis.getTypeFactory().getQualifierHierarchy(); |
| AnnotationMirror upperBound = typeVar.getEffectiveAnnotationInHierarchy(top); |
| |
| if (!canCombinedSetBeMissingAnnos) { |
| return combineTwoAnnotations(annotation, upperBound, top); |
| } |
| Set<AnnotationMirror> lBSet = |
| AnnotatedTypes.findEffectiveLowerBoundAnnotations(hierarchy, typeVar); |
| AnnotationMirror lowerBound = hierarchy.findAnnotationInHierarchy(lBSet, top); |
| if (hierarchy.isSubtype(upperBound, annotation)) { |
| // no anno is more specific than anno |
| return null; |
| } else if (hierarchy.isSubtype(annotation, lowerBound)) { |
| return annotation; |
| } else { |
| return getBackUpAnnoIn(top); |
| } |
| } |
| } |
| |
| @Override |
| public V leastUpperBound(@Nullable V other) { |
| return upperBound(other, false); |
| } |
| |
| /** |
| * Compute an upper bound of two values that is wider than the least upper bound of the two |
| * values. Used to jump to a higher abstraction to allow faster termination of the fixed point |
| * computations in {@link Analysis}. |
| * |
| * <p>A particular analysis might not require widening and should implement this method by calling |
| * leastUpperBound. |
| * |
| * <p><em>Important</em>: This method must fulfill the following contract: |
| * |
| * <ul> |
| * <li>Does not change {@code this}. |
| * <li>Does not change {@code previous}. |
| * <li>Returns a fresh object which is not aliased yet. |
| * <li>Returns an object of the same (dynamic) type as {@code this}, even if the signature is |
| * more permissive. |
| * <li>Is commutative. |
| * </ul> |
| * |
| * @param previous must be the previous value |
| * @return an upper bound of two values that is wider than the least upper bound of the two values |
| */ |
| public V widenUpperBound(@Nullable V previous) { |
| return upperBound(previous, 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(); |
| TypeMirror lubTypeMirror = |
| TypesUtils.leastUpperBound( |
| this.getUnderlyingType(), other.getUnderlyingType(), processingEnv); |
| |
| ValueLub valueLub = new ValueLub(shouldWiden); |
| Set<AnnotationMirror> lub = |
| valueLub.combineSets( |
| this.getUnderlyingType(), |
| this.getAnnotations(), |
| other.getUnderlyingType(), |
| other.getAnnotations(), |
| canBeMissingAnnotations(lubTypeMirror)); |
| return analysis.createAbstractValue(lub, lubTypeMirror); |
| } |
| |
| /** |
| * Computes the least upper bound or, if {@code shouldWiden} is true, an upper bounds of two sets |
| * of annotations. The computation accounts for sets that are missing annotations in hierarchies. |
| */ |
| protected class ValueLub extends AnnotationSetCombiner { |
| |
| /** |
| * If true, this class computes an upper bound; if false, this class computes the least upper |
| * bound. |
| */ |
| private final boolean widen; |
| |
| /** |
| * Creates a {@link ValueLub}. |
| * |
| * @param shouldWiden if true, this class computes an upper bound |
| */ |
| public ValueLub(boolean shouldWiden) { |
| this.widen = shouldWiden; |
| } |
| |
| @Override |
| protected @Nullable AnnotationMirror combineTwoAnnotations( |
| AnnotationMirror a, AnnotationMirror b, AnnotationMirror top) { |
| QualifierHierarchy hierarchy = analysis.getTypeFactory().getQualifierHierarchy(); |
| if (widen) { |
| return hierarchy.widenedUpperBound(a, b); |
| } else { |
| return hierarchy.leastUpperBound(a, b); |
| } |
| } |
| |
| @Override |
| protected @Nullable AnnotationMirror combineNoAnnotations( |
| AnnotatedTypeVariable aAtv, |
| AnnotatedTypeVariable bAtv, |
| AnnotationMirror top, |
| boolean canCombinedSetBeMissingAnnos) { |
| if (canCombinedSetBeMissingAnnos) { |
| // don't add an annotation |
| return null; |
| } else { |
| AnnotationMirror aUB = aAtv.getEffectiveAnnotationInHierarchy(top); |
| AnnotationMirror bUB = bAtv.getEffectiveAnnotationInHierarchy(top); |
| return combineTwoAnnotations(aUB, bUB, top); |
| } |
| } |
| |
| @Override |
| protected @Nullable AnnotationMirror combineOneAnnotation( |
| AnnotationMirror annotation, |
| AnnotatedTypeVariable typeVar, |
| AnnotationMirror top, |
| boolean canCombinedSetBeMissingAnnos) { |
| QualifierHierarchy hierarchy = analysis.getTypeFactory().getQualifierHierarchy(); |
| if (canCombinedSetBeMissingAnnos) { |
| // anno is the primary annotation on the use of a type variable. typeVar is a use of the |
| // same type variable that does not have a primary annotation. The lub of the two type |
| // variables is computed as follows. If anno is a subtype (or equal) to the annotation on |
| // the lower bound of typeVar, then typeVar is the lub, so no annotation is added to lubset. |
| // If anno is a supertype of the annotation on the lower bound of typeVar, then the lub is |
| // typeVar with a primary annotation of lub(anno, upperBound), where upperBound is the |
| // annotation on the upper bound of typeVar. |
| Set<AnnotationMirror> lBSet = |
| AnnotatedTypes.findEffectiveLowerBoundAnnotations(hierarchy, typeVar); |
| AnnotationMirror lowerBound = hierarchy.findAnnotationInHierarchy(lBSet, top); |
| if (hierarchy.isSubtype(annotation, lowerBound)) { |
| return null; |
| } else { |
| return combineTwoAnnotations( |
| annotation, typeVar.getEffectiveAnnotationInHierarchy(top), top); |
| } |
| } else { |
| return combineTwoAnnotations( |
| annotation, typeVar.getEffectiveAnnotationInHierarchy(top), top); |
| } |
| } |
| } |
| |
| /** |
| * Compute the greatest lower bound of two values. |
| * |
| * <p><em>Important</em>: This method must fulfill the following contract: |
| * |
| * <ul> |
| * <li>Does not change {@code this}. |
| * <li>Does not change {@code other}. |
| * <li>Returns a fresh object which is not aliased yet. |
| * <li>Returns an object of the same (dynamic) type as {@code this}, even if the signature is |
| * more permissive. |
| * <li>Is commutative. |
| * </ul> |
| * |
| * @param other another value |
| * @return the greatest lower bound of two values |
| */ |
| public V greatestLowerBound(@Nullable V other) { |
| if (other == null) { |
| @SuppressWarnings("unchecked") |
| V v = (V) this; |
| return v; |
| } |
| ProcessingEnvironment processingEnv = analysis.getTypeFactory().getProcessingEnv(); |
| TypeMirror glbTypeMirror = |
| TypesUtils.greatestLowerBound( |
| this.getUnderlyingType(), other.getUnderlyingType(), processingEnv); |
| |
| ValueGlb valueGlb = new ValueGlb(); |
| Set<AnnotationMirror> glb = |
| valueGlb.combineSets( |
| this.getUnderlyingType(), |
| this.getAnnotations(), |
| other.getUnderlyingType(), |
| other.getAnnotations(), |
| canBeMissingAnnotations(glbTypeMirror)); |
| return analysis.createAbstractValue(glb, glbTypeMirror); |
| } |
| |
| /** |
| * Computes the GLB of two sets of annotations. The computation accounts for sets that are missing |
| * annotations in hierarchies. |
| */ |
| protected class ValueGlb extends AnnotationSetCombiner { |
| |
| @Override |
| protected @Nullable AnnotationMirror combineTwoAnnotations( |
| AnnotationMirror a, AnnotationMirror b, AnnotationMirror top) { |
| QualifierHierarchy hierarchy = analysis.getTypeFactory().getQualifierHierarchy(); |
| return hierarchy.greatestLowerBound(a, b); |
| } |
| |
| @Override |
| protected @Nullable AnnotationMirror combineNoAnnotations( |
| AnnotatedTypeVariable aAtv, |
| AnnotatedTypeVariable bAtv, |
| AnnotationMirror top, |
| boolean canCombinedSetBeMissingAnnos) { |
| if (canCombinedSetBeMissingAnnos) { |
| // don't add an annotation |
| return null; |
| } else { |
| AnnotationMirror aUB = aAtv.getEffectiveAnnotationInHierarchy(top); |
| AnnotationMirror bUB = bAtv.getEffectiveAnnotationInHierarchy(top); |
| return combineTwoAnnotations(aUB, bUB, top); |
| } |
| } |
| |
| @Override |
| protected @Nullable AnnotationMirror combineOneAnnotation( |
| AnnotationMirror annotation, |
| AnnotatedTypeVariable typeVar, |
| AnnotationMirror top, |
| boolean canCombinedSetBeMissingAnnos) { |
| QualifierHierarchy hierarchy = analysis.getTypeFactory().getQualifierHierarchy(); |
| if (canCombinedSetBeMissingAnnos) { |
| // anno is the primary annotation on the use of a type variable. typeVar is a use of the |
| // same type variable that does not have a primary annotation. The glb of the two type |
| // variables is computed as follows. If anno is a supertype (or equal) to the annotation on |
| // the upper bound of typeVar, then typeVar is the glb, so no annotation is added to glbset. |
| // If anno is a subtype of the annotation on the upper bound of typeVar, then the glb is |
| // typeVar with a primary annotation of glb(anno, lowerBound), where lowerBound is the |
| // annotation on the lower bound of typeVar. |
| AnnotationMirror upperBound = typeVar.getEffectiveAnnotationInHierarchy(top); |
| if (hierarchy.isSubtype(upperBound, annotation)) { |
| return null; |
| } else { |
| Set<AnnotationMirror> lBSet = |
| AnnotatedTypes.findEffectiveLowerBoundAnnotations(hierarchy, typeVar); |
| AnnotationMirror lowerBound = hierarchy.findAnnotationInHierarchy(lBSet, top); |
| return combineTwoAnnotations(annotation, lowerBound, top); |
| } |
| } else { |
| return combineTwoAnnotations( |
| annotation, typeVar.getEffectiveAnnotationInHierarchy(top), top); |
| } |
| } |
| } |
| |
| /** |
| * Combines two sets of AnnotationMirrors by hierarchy. |
| * |
| * <p>Subclasses must define how to combine sets by implementing the following methods: |
| * |
| * <ol> |
| * <li>{@code #combineTwoAnnotations(AnnotationMirror, AnnotationMirror, AnnotationMirror)} |
| * <li>{@code #combineOneAnnotation(AnnotationMirror, AnnotatedTypeVariable, AnnotationMirror, |
| * boolean)} |
| * <li>{@code #combineNoAnnotations(AnnotatedTypeVariable, AnnotatedTypeVariable, |
| * AnnotationMirror, boolean)} |
| * </ol> |
| * |
| * If a set is missing an annotation in a hierarchy, and if the combined set can be missing an |
| * annotation, then there must be a TypeVariable for the set that can be used to find annotations |
| * on its bounds. |
| */ |
| protected abstract class AnnotationSetCombiner { |
| |
| /** |
| * Combines the two sets. |
| * |
| * @param aTypeMirror the type mirror associated with {@code aSet} |
| * @param aSet a set of annotation mirrors |
| * @param bTypeMirror the type mirror associated with {@code bSet} |
| * @param bSet a set of annotation mirrors |
| * @param canCombinedSetBeMissingAnnos whether or not the combined set can be missing |
| * annotations |
| * @return the combined sets |
| */ |
| protected Set<AnnotationMirror> combineSets( |
| TypeMirror aTypeMirror, |
| Set<AnnotationMirror> aSet, |
| TypeMirror bTypeMirror, |
| Set<AnnotationMirror> bSet, |
| boolean canCombinedSetBeMissingAnnos) { |
| |
| AnnotatedTypeVariable aAtv = getEffectTypeVar(aTypeMirror); |
| AnnotatedTypeVariable bAtv = getEffectTypeVar(bTypeMirror); |
| QualifierHierarchy hierarchy = analysis.getTypeFactory().getQualifierHierarchy(); |
| Set<? extends AnnotationMirror> tops = hierarchy.getTopAnnotations(); |
| Set<AnnotationMirror> combinedSets = AnnotationUtils.createAnnotationSet(); |
| for (AnnotationMirror top : tops) { |
| AnnotationMirror a = hierarchy.findAnnotationInHierarchy(aSet, top); |
| AnnotationMirror b = hierarchy.findAnnotationInHierarchy(bSet, top); |
| AnnotationMirror result; |
| if (a != null && b != null) { |
| result = combineTwoAnnotations(a, b, top); |
| } else if (a != null) { |
| result = combineOneAnnotation(a, bAtv, top, canCombinedSetBeMissingAnnos); |
| } else if (b != null) { |
| result = combineOneAnnotation(b, aAtv, top, canCombinedSetBeMissingAnnos); |
| } else { |
| result = combineNoAnnotations(aAtv, bAtv, top, canCombinedSetBeMissingAnnos); |
| } |
| if (result != null) { |
| combinedSets.add(result); |
| } |
| } |
| return combinedSets; |
| } |
| |
| /** |
| * Returns the result of combining the two annotations. This method is called when an annotation |
| * exists in both sets for the hierarchy whose top is {@code top}. |
| * |
| * @param a an annotation in the hierarchy |
| * @param b an annotation in the hierarchy |
| * @param top the top annotation in the hierarchy |
| * @return the result of combining the two annotations or null if no combination exists |
| */ |
| protected abstract @Nullable AnnotationMirror combineTwoAnnotations( |
| AnnotationMirror a, AnnotationMirror b, AnnotationMirror top); |
| |
| /** |
| * Returns the primary annotation that result from of combining the two {@link |
| * AnnotatedTypeVariable}. If the result has not primary annotation, the {@code null} is |
| * returned. This method is called when no annotation exists in either sets for the hierarchy |
| * whose top is {@code top}. |
| * |
| * @param aAtv a type variable that does not have a primary annotation in {@code top} hierarchy |
| * @param bAtv a type variable that does not have a primary annotation in {@code top} hierarchy |
| * @param top the top annotation in the hierarchy |
| * @param canCombinedSetBeMissingAnnos whether or not |
| * @return the result of combining the two type variables, which may be null |
| */ |
| protected abstract @Nullable AnnotationMirror combineNoAnnotations( |
| AnnotatedTypeVariable aAtv, |
| AnnotatedTypeVariable bAtv, |
| AnnotationMirror top, |
| boolean canCombinedSetBeMissingAnnos); |
| |
| /** |
| * Returns the result of combining {@code annotation} with {@code typeVar}. |
| * |
| * <p>This is called when an annotation exists for the hierarchy in on set, but not the other. |
| * |
| * @param annotation an annotation |
| * @param typeVar a type variable that does not have a primary annotation in the hierarchy |
| * @param top the top annotation of the hierarchy |
| * @param canCombinedSetBeMissingAnnos whether or not |
| * @return the result of combining {@code annotation} with {@code typeVar} |
| */ |
| protected abstract @Nullable AnnotationMirror combineOneAnnotation( |
| AnnotationMirror annotation, |
| AnnotatedTypeVariable typeVar, |
| AnnotationMirror top, |
| boolean canCombinedSetBeMissingAnnos); |
| } |
| |
| /** |
| * Returns the AnnotatedTypeVariable associated with the given TypeMirror or null. |
| * |
| * <p>If {@code typeMirror} is a type variable, then the {@link AnnotatedTypeVariable} of its |
| * declaration is returned. If {@code typeMirror} is a wildcard whose extends bounds is a type |
| * variable, then the {@link AnnotatedTypeVariable} for its declaration is returned. Otherwise, |
| * {@code null} is returned. |
| * |
| * @param typeMirror a type mirror |
| * @return the AnnotatedTypeVariable associated with the given TypeMirror or null |
| */ |
| private @Nullable AnnotatedTypeVariable getEffectTypeVar(@Nullable 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; |
| } |
| } |
| } |