blob: cfc25d99dfa3f392fbbf7f6f376f8880e13701cd [file] [log] [blame]
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;
}
}
}