blob: 55135230dc32769a4d9fa46769ac2cc9d606bf6c [file] [log] [blame]
package org.checkerframework.framework.type;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.type.TypeKind;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.framework.qual.AnnotatedFor;
import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.BugInCF;
import org.plumelib.util.StringsPlume;
/**
* Represents multiple type qualifier hierarchies. {@link #getWidth} gives the number of hierarchies
* that this object represents. Each hierarchy has its own top and bottom, and subtyping
* relationships exist only within each hierarchy.
*
* <p>Note the distinction in terminology between a qualifier hierarchy, which has one top and one
* bottom, and a {@code QualifierHierarchy}, which represents multiple qualifier hierarchies.
*
* <p>All type annotations need to be type qualifiers recognized within this hierarchy.
*
* <p>This assumes that every annotated type in a program is annotated with exactly one qualifier
* from each hierarchy.
*/
@AnnotatedFor("nullness")
public interface QualifierHierarchy {
/**
* Determine whether this is valid.
*
* @return whether this is valid
*/
default boolean isValid() {
return true;
}
// **********************************************************************
// Getter methods about this hierarchy
// **********************************************************************
/**
* Returns the width of this hierarchy, i.e. the expected number of annotations on any valid type.
*
* @return the width of this QualifierHierarchy
*/
default int getWidth() {
return getTopAnnotations().size();
}
/**
* Returns the top (ultimate super) type qualifiers in the type system. The size of this set is
* equal to {@link #getWidth}.
*
* @return the top (ultimate super) type qualifiers in the type system
*/
Set<? extends AnnotationMirror> getTopAnnotations();
/**
* Return the top qualifier for the given qualifier, that is, the qualifier that is a supertype of
* {@code qualifier} but no further supertypes exist.
*
* @param qualifier any qualifier from one of the qualifier hierarchies represented by this
* @return the top qualifier of {@code qualifier}'s hierarchy
*/
AnnotationMirror getTopAnnotation(AnnotationMirror qualifier);
/**
* Returns the bottom type qualifiers in the hierarchy. The size of this set is equal to {@link
* #getWidth}.
*
* @return the bottom type qualifiers in the hierarchy
*/
Set<? extends AnnotationMirror> getBottomAnnotations();
/**
* Return the bottom for the given qualifier, that is, the qualifier that is a subtype of {@code
* qualifier} but no further subtypes exist.
*
* @param qualifier any qualifier from one of the qualifier hierarchies represented by this
* @return the bottom qualifier of {@code qualifier}'s hierarchy
*/
AnnotationMirror getBottomAnnotation(AnnotationMirror qualifier);
/**
* Returns the polymorphic qualifier for the hierarchy containing {@code qualifier}, or {@code
* null} if there is no polymorphic qualifier in that hierarchy.
*
* @param qualifier any qualifier from one of the qualifier hierarchies represented by this
* @return the polymorphic qualifier for the hierarchy containing {@code qualifier}, or {@code
* null} if there is no polymorphic qualifier in that hierarchy
*/
@Nullable AnnotationMirror getPolymorphicAnnotation(AnnotationMirror qualifier);
/**
* Returns {@code true} if the qualifier is a polymorphic qualifier; otherwise, returns {@code
* false}.
*
* @param qualifier qualifier
* @return {@code true} if the qualifier is a polymorphic qualifier; otherwise, returns {@code
* false}.
*/
boolean isPolymorphicQualifier(AnnotationMirror qualifier);
// **********************************************************************
// Qualifier Hierarchy Queries
// **********************************************************************
/**
* Tests whether {@code subQualifier} is equal to or a sub-qualifier of {@code superQualifier},
* according to the type qualifier hierarchy.
*
* @param subQualifier possible subqualifier of {@code superQualifier}
* @param superQualifier possible superqualifier of {@code subQualifier}
* @return true iff {@code subQualifier} is a subqualifier of, or equal to, {@code superQualifier}
*/
boolean isSubtype(AnnotationMirror subQualifier, AnnotationMirror superQualifier);
/**
* Tests whether all qualifiers in {@code subQualifiers} are a subqualifier or equal to the
* qualifier in the same hierarchy in {@code superQualifiers}.
*
* @param subQualifiers set of qualifiers; exactly one per hierarchy
* @param superQualifiers set of qualifiers; exactly one per hierarchy
* @return true iff all qualifiers in {@code subQualifiers} are a subqualifier or equal to the
* qualifier in the same hierarchy in {@code superQualifiers}
*/
default boolean isSubtype(
Collection<? extends AnnotationMirror> subQualifiers,
Collection<? extends AnnotationMirror> superQualifiers) {
assertSameSize(subQualifiers, superQualifiers);
for (AnnotationMirror subQual : subQualifiers) {
AnnotationMirror superQual = findAnnotationInSameHierarchy(superQualifiers, subQual);
if (superQual == null) {
throw new BugInCF(
"QualifierHierarchy: missing annotation in hierarchy %s. found: %s",
subQual, StringsPlume.join(",", superQualifiers));
}
if (!isSubtype(subQual, superQual)) {
return false;
}
}
return true;
}
/**
* Returns the least upper bound (LUB) of the qualifiers {@code qualifier1} and {@code
* qualifier2}. Returns {@code null} if the qualifiers are not from the same qualifier hierarchy.
*
* <p>Examples:
*
* <ul>
* <li>For NonNull, leastUpperBound('Nullable', 'NonNull') &rArr; Nullable
* </ul>
*
* @param qualifier1 the first qualifier; may not be in the same hierarchy as {@code qualifier2}
* @param qualifier2 the second qualifier; may not be in the same hierarchy as {@code qualifier1}
* @return the least upper bound of the qualifiers, or {@code null} if the qualifiers are from
* different hierarchies
*/
// The fact that null is returned if the qualifiers are not in the same hierarchy is used by the
// collection version of LUB below.
@Nullable AnnotationMirror leastUpperBound(AnnotationMirror qualifier1, AnnotationMirror qualifier2);
/**
* Returns the least upper bound of the two sets of qualifiers. The result is the lub of the
* qualifier for the same hierarchy in each set.
*
* @param qualifiers1 set of qualifiers; exactly one per hierarchy
* @param qualifiers2 set of qualifiers; exactly one per hierarchy
* @return the least upper bound of the two sets of qualifiers
*/
default Set<? extends AnnotationMirror> leastUpperBounds(
Collection<? extends AnnotationMirror> qualifiers1,
Collection<? extends AnnotationMirror> qualifiers2) {
assertSameSize(qualifiers1, qualifiers2);
if (qualifiers1.isEmpty()) {
throw new BugInCF(
"QualifierHierarchy.leastUpperBounds: tried to determine LUB with empty sets");
}
Set<AnnotationMirror> result = AnnotationUtils.createAnnotationSet();
for (AnnotationMirror a1 : qualifiers1) {
for (AnnotationMirror a2 : qualifiers2) {
AnnotationMirror lub = leastUpperBound(a1, a2);
if (lub != null) {
result.add(lub);
}
}
}
assertSameSize(result, qualifiers1);
return result;
}
/**
* Returns the number of iterations dataflow should perform before {@link
* #widenedUpperBound(AnnotationMirror, AnnotationMirror)} is called or -1 if it should never be
* called.
*
* @return the number of iterations dataflow should perform before {@link
* #widenedUpperBound(AnnotationMirror, AnnotationMirror)} is called or -1 if it should never
* be called.
*/
default int numberOfIterationsBeforeWidening() {
return -1;
}
/**
* If the qualifier hierarchy has an infinite ascending chain, then the dataflow analysis might
* never reach a fixed point. To prevent this, implement this method such that it returns an upper
* bound for the two qualifiers that is a strict super type of the least upper bound. If this
* method is implemented, also override {@link #numberOfIterationsBeforeWidening()} to return a
* positive number.
*
* <p>{@code newQualifier} is newest qualifier dataflow computed for some expression and {@code
* previousQualifier} is the qualifier dataflow computed on the last iteration.
*
* <p>If the qualifier hierarchy has no infinite ascending chain, returns the least upper bound of
* the two annotations.
*
* @param newQualifier new qualifier dataflow computed for some expression; must be in the same
* hierarchy as {@code previousQualifier}
* @param previousQualifier the previous qualifier dataflow computed on the last iteration; must
* be in the same hierarchy as {@code previousQualifier}
* @return an upper bound that is higher than the least upper bound of newQualifier and
* previousQualifier (or the lub if the qualifier hierarchy does not require this)
*/
default AnnotationMirror widenedUpperBound(
AnnotationMirror newQualifier, AnnotationMirror previousQualifier) {
AnnotationMirror widenUpperBound = leastUpperBound(newQualifier, previousQualifier);
if (widenUpperBound == null) {
throw new BugInCF(
"Passed two unrelated qualifiers to QualifierHierarchy#widenedUpperBound. %s %s.",
newQualifier, previousQualifier);
}
return widenUpperBound;
}
/**
* Returns the greatest lower bound for the qualifiers qualifier1 and qualifier2. Returns null if
* the qualifiers are not from the same qualifier hierarchy.
*
* @param qualifier1 first qualifier
* @param qualifier2 second qualifier
* @return greatest lower bound of the two annotations or null if the two annotations are not from
* the same hierarchy
*/
// The fact that null is returned if the qualifiers are not in the same hierarchy is used by the
// collection version of LUB below.
@Nullable AnnotationMirror greatestLowerBound(AnnotationMirror qualifier1, AnnotationMirror qualifier2);
/**
* Returns the greatest lower bound of the two sets of qualifiers. The result is the lub of the
* qualifier for the same hierarchy in each set.
*
* @param qualifiers1 set of qualifiers; exactly one per hierarchy
* @param qualifiers2 set of qualifiers; exactly one per hierarchy
* @return the greatest lower bound of the two sets of qualifiers
*/
default Set<? extends AnnotationMirror> greatestLowerBounds(
Collection<? extends AnnotationMirror> qualifiers1,
Collection<? extends AnnotationMirror> qualifiers2) {
assertSameSize(qualifiers1, qualifiers2);
if (qualifiers1.isEmpty()) {
throw new BugInCF(
"QualifierHierarchy.greatestLowerBounds: tried to determine GLB with empty sets");
}
Set<AnnotationMirror> result = AnnotationUtils.createAnnotationSet();
for (AnnotationMirror a1 : qualifiers1) {
for (AnnotationMirror a2 : qualifiers2) {
AnnotationMirror glb = greatestLowerBound(a1, a2);
if (glb != null) {
result.add(glb);
}
}
}
assertSameSize(qualifiers1, qualifiers2, result);
return result;
}
/**
* Returns true if and only if {@link AnnotatedTypeMirror#getAnnotations()} can return a set with
* fewer qualifiers than the width of the QualifierHierarchy.
*
* @param type the type to test
* @return true if and only if {@link AnnotatedTypeMirror#getAnnotations()} can return a set with
* fewer qualifiers than the width of the QualifierHierarchy
*/
static boolean canHaveEmptyAnnotationSet(AnnotatedTypeMirror type) {
return type.getKind() == TypeKind.TYPEVAR
|| type.getKind() == TypeKind.WILDCARD
||
// TODO: or should the union/intersection be the LUB of the alternatives?
type.getKind() == TypeKind.UNION
|| type.getKind() == TypeKind.INTERSECTION;
}
/**
* Returns the annotation in {@code qualifiers} that is in the same hierarchy as {@code
* qualifier}.
*
* <p>The default implementation calls {@link #getTopAnnotation(AnnotationMirror)} and then calls
* {@link #findAnnotationInHierarchy(Collection, AnnotationMirror)}. So, if {@code qualifier} is a
* top qualifier, then call {@link #findAnnotationInHierarchy(Collection, AnnotationMirror)}
* directly is faster.
*
* @param qualifiers set of annotations to search
* @param qualifier annotation that is in the same hierarchy as the returned annotation
* @return annotation in the same hierarchy as qualifier, or null if one is not found
*/
default @Nullable AnnotationMirror findAnnotationInSameHierarchy(
Collection<? extends AnnotationMirror> qualifiers, AnnotationMirror qualifier) {
AnnotationMirror top = this.getTopAnnotation(qualifier);
return findAnnotationInHierarchy(qualifiers, top);
}
/**
* Returns the annotation in {@code qualifiers} that is in the hierarchy for which {@code top} is
* top.
*
* @param qualifiers set of annotations to search
* @param top the top annotation in the hierarchy to which the returned annotation belongs
* @return annotation in the same hierarchy as annotationMirror, or null if one is not found
*/
default @Nullable AnnotationMirror findAnnotationInHierarchy(
Collection<? extends AnnotationMirror> qualifiers, AnnotationMirror top) {
for (AnnotationMirror anno : qualifiers) {
if (isSubtype(anno, top)) {
return anno;
}
}
return null;
}
/**
* Update a mapping from {@code key} to a set of AnnotationMirrors. If {@code key} is not already
* in the map, then put it in the map with a value of a new set containing {@code qualifier}. If
* the map contains {@code key}, then add {@code qualifier} to the set to which {@code key} maps.
* If that set contains a qualifier in the same hierarchy as {@code qualifier}, then don't add it
* and return false.
*
* @param map the mapping to modify
* @param key the key to update or add
* @param qualifier the value to update or add
* @param <T> type of the map's keys
* @return true if the update was done; false if there was a qualifier hierarchy collision
*/
default <T> boolean updateMappingToMutableSet(
Map<T, Set<AnnotationMirror>> map, T key, AnnotationMirror qualifier) {
// https://github.com/typetools/checker-framework/issues/2000
@SuppressWarnings("nullness:argument")
boolean mapContainsKey = map.containsKey(key);
if (mapContainsKey) {
@SuppressWarnings("nullness:assignment") // key is a key for map.
@NonNull Set<AnnotationMirror> prevs = map.get(key);
AnnotationMirror old = findAnnotationInSameHierarchy(prevs, qualifier);
if (old != null) {
return false;
}
prevs.add(qualifier);
map.put(key, prevs);
} else {
Set<AnnotationMirror> set = AnnotationUtils.createAnnotationSet();
set.add(qualifier);
map.put(key, set);
}
return true;
}
/**
* Throws an exception if the given collections do not have the same size.
*
* @param c1 the first collection
* @param c2 the second collection
*/
static void assertSameSize(Collection<?> c1, Collection<?> c2) {
if (c1.size() != c2.size()) {
throw new BugInCF(
"inconsistent sizes (%d, %d):%n [%s]%n [%s]",
c1.size(), c2.size(), StringsPlume.join(",", c1), StringsPlume.join(",", c2));
}
}
/**
* Throws an exception if the result does not have the same size as the inputs (which are assumed
* to have the same size as one another).
*
* @param c1 the first collection
* @param c2 the second collection
* @param result the result collection
*/
static void assertSameSize(Collection<?> c1, Collection<?> c2, Collection<?> result) {
if (c1.size() != result.size() || c2.size() != result.size()) {
throw new BugInCF(
"inconsistent sizes (%d, %d, %d):%n %s%n %s%n %s",
c1.size(),
c2.size(),
result.size(),
StringsPlume.join(",", c1),
StringsPlume.join(",", c2),
StringsPlume.join(",", result));
}
}
// **********************************************************************
// Deprecated methods
// **********************************************************************
/**
* Tests whether {@code subQualifier} is a sub-qualifier of, or equal to, {@code superQualifier},
* according to the type qualifier hierarchy.
*
* <p>This method works even if the underlying Java type is a type variable. In that case, a
* 'null' AnnotationMirror is a legal argument that represents no annotation.
*
* @param subQualifier a qualifier that might be a subtype
* @param superQualifier a qualifier that might be a subtype
* @return true iff {@code subQualifier} is a subqualifier of, or equal to, {@code superQualifier}
* @deprecated Without the bounds of the type variable, it is not possible to correctly compute
* the subtype relationship between "no qualifier" and a qualifier. Use {@link
* TypeHierarchy#isSubtype(AnnotatedTypeMirror, AnnotatedTypeMirror)}.
*/
@Deprecated // 2020-07-29
default boolean isSubtypeTypeVariable(
@Nullable AnnotationMirror subQualifier, @Nullable AnnotationMirror superQualifier) {
if (subQualifier == null) {
// [] is a supertype of any qualifier, and [] <: []
return true;
} else if (superQualifier == null) {
// [] is a subtype of no qualifier (only [])
return false;
}
return isSubtype(subQualifier, superQualifier);
}
/**
* Tests whether {@code subQualifier} is a sub-qualifier of, or equal to, {@code superQualifier},
* according to the type qualifier hierarchy. This checks only the qualifiers, not the Java type.
*
* <p>This method takes an annotated type to decide if the type variable version of the method
* should be invoked, or if the normal version is sufficient (which provides more strict checks).
*
* @param subType used to decide whether to call isSubtypeTypeVariable
* @param superType used to decide whether to call isSubtypeTypeVariable
* @param subQualifier the type qualifier that might be a subtype
* @param superQualifier the type qualifier that might be a supertype
* @return true iff {@code subQualifier} is a subqualifier of, or equal to, {@code superQualifier}
* @deprecated Without the bounds of the type variable, it is not possible to correctly compute
* the subtype relationship between "no qualifier" and a qualifier. Use {@link
* TypeHierarchy#isSubtype(AnnotatedTypeMirror, AnnotatedTypeMirror)}.
*/
@Deprecated
default boolean isSubtype(
AnnotatedTypeMirror subType,
AnnotatedTypeMirror superType,
AnnotationMirror subQualifier,
AnnotationMirror superQualifier) {
if (canHaveEmptyAnnotationSet(subType) || canHaveEmptyAnnotationSet(superType)) {
return isSubtypeTypeVariable(subQualifier, superQualifier);
} else {
return isSubtype(subQualifier, superQualifier);
}
}
/**
* Tests whether there is any annotation in {@code supers} that is a superqualifier of, or equal
* to, some annotation in {@code subs}. {@code supers} and {@code subs} contain only the
* annotations, not the Java type.
*
* <p>This method takes an annotated type to decide if the type variable version of the method
* should be invoked, or if the normal version is sufficient (which provides more strict checks).
*
* @param subType used to decide whether to call isSubtypeTypeVariable
* @param superType used to decide whether to call isSubtypeTypeVariable
* @param subs the type qualifiers that might be a subtype
* @param supers the type qualifiers that might be a supertype
* @return true iff an annotation in {@code supers} is a supertype of, or equal to, one in {@code
* subs}
* @deprecated Without the bounds of the type variable, it is not possible to correctly compute
* the subtype relationship between "no qualifier" and a qualifier. Use {@link
* TypeHierarchy#isSubtype(AnnotatedTypeMirror, AnnotatedTypeMirror)}.
*/
@Deprecated // 2020-07-29
default boolean isSubtype(
AnnotatedTypeMirror subType,
AnnotatedTypeMirror superType,
Collection<? extends AnnotationMirror> subs,
Collection<AnnotationMirror> supers) {
if (canHaveEmptyAnnotationSet(subType) || canHaveEmptyAnnotationSet(superType)) {
return isSubtypeTypeVariable(subs, supers);
} else {
return isSubtype(subs, supers);
}
}
/**
* Tests whether there is any annotation in superAnnos that is a superqualifier of or equal to
* some annotation in subAnnos. superAnnos and subAnnos contain only the annotations, not the Java
* type.
*
* <p>This method works even if the underlying Java type is a type variable. In that case, the
* empty set is a legal argument that represents no annotation.
*
* @param subAnnos qualifiers
* @param superAnnos qualifiers
* @return true iff an annotation in superAnnos is a supertype of, or equal to, one in subAnnos
* @deprecated Without the bounds of the type variable, it is not possible to correctly compute
* the subtype relationship between "no qualifier" and a qualifier. Use {@link
* TypeHierarchy#isSubtype(AnnotatedTypeMirror, AnnotatedTypeMirror)}.
*/
// This method requires more revision.
@Deprecated // 2020-07-29
default boolean isSubtypeTypeVariable(
Collection<? extends AnnotationMirror> subAnnos,
Collection<? extends AnnotationMirror> superAnnos) {
for (AnnotationMirror top : getTopAnnotations()) {
AnnotationMirror rhsForTop = findAnnotationInHierarchy(subAnnos, top);
AnnotationMirror lhsForTop = findAnnotationInHierarchy(superAnnos, top);
if (!isSubtypeTypeVariable(rhsForTop, lhsForTop)) {
return false;
}
}
return true;
}
/**
* Returns the least upper bound for the qualifiers a1 and a2. Returns null if the qualifiers are
* not from the same qualifier hierarchy.
*
* <p>Examples:
*
* <ul>
* <li>For NonNull, leastUpperBound('Nullable', 'NonNull') &rarr; Nullable
* </ul>
*
* <p>This method works even if the underlying Java type is a type variable. In that case, a
* 'null' AnnotationMirror is a legal argument that represents no annotation.
*
* @param a1 anno1
* @param a2 anno2
* @return the least restrictive qualifiers for both types
* @deprecated Without the bounds of the type variable, it is not possible to correctly compute
* the relationship between "no qualifier" and a qualifier. Use {@link
* org.checkerframework.framework.util.AnnotatedTypes#leastUpperBound(AnnotatedTypeFactory,
* AnnotatedTypeMirror, AnnotatedTypeMirror)}.
*/
@Deprecated // 2020-07-29
default @Nullable AnnotationMirror leastUpperBoundTypeVariable(
AnnotationMirror a1, AnnotationMirror a2) {
if (a1 == null || a2 == null) {
// [] is a supertype of any qualifier, and [] <: []
return null;
}
return leastUpperBound(a1, a2);
}
/**
* Returns the least upper bound for the qualifiers a1 and a2. Returns null if the qualifiers are
* not from the same qualifier hierarchy.
*
* <p>Examples:
*
* <ul>
* <li>For NonNull, leastUpperBound('Nullable', 'NonNull') &rarr; Nullable
* </ul>
*
* <p>This method takes an annotated type to decide if the type variable version of the method
* should be invoked, or if the normal version is sufficient (which provides more strict checks).
*
* @param type1 type 1
* @param type2 type 2
* @param a1 annotation
* @param a2 annotation
* @return the least restrictive qualifiers for both types
* @deprecated Without the bounds of the type variable, it is not possible to correctly compute
* the relationship between "no qualifier" and a qualifier. Use {@link
* org.checkerframework.framework.util.AnnotatedTypes#leastUpperBound(AnnotatedTypeFactory,
* AnnotatedTypeMirror, AnnotatedTypeMirror)}.
*/
@Deprecated // 2020-07-29
default @Nullable AnnotationMirror leastUpperBound(
AnnotatedTypeMirror type1,
AnnotatedTypeMirror type2,
AnnotationMirror a1,
AnnotationMirror a2) {
if (canHaveEmptyAnnotationSet(type1) || canHaveEmptyAnnotationSet(type2)) {
return leastUpperBoundTypeVariable(a1, a2);
} else {
return leastUpperBound(a1, a2);
}
}
/**
* Returns the type qualifiers that are the least upper bound of the qualifiers in annos1 and
* annos2.
*
* <p>This is necessary for determining the type of a conditional expression ({@code ?:}), where
* the type of the expression is the least upper bound of the true and false clauses.
*
* <p>This method works even if the underlying Java type is a type variable. In that case, the
* empty set is a legal argument that represents no annotation.
*
* @param annos1 qualifiers
* @param annos2 qualifiers
* @return the least upper bound of annos1 and annos2
* @deprecated Without the bounds of the type variable, it is not possible to correctly compute
* the relationship between "no qualifier" and a qualifier. Use {@link
* org.checkerframework.framework.util.AnnotatedTypes#leastUpperBound(AnnotatedTypeFactory,
* AnnotatedTypeMirror, AnnotatedTypeMirror)}.
*/
@Deprecated // 2020-07-29
@SuppressWarnings("nullness") // Don't check deprecated method.
default Set<? extends AnnotationMirror> leastUpperBoundsTypeVariable(
Collection<? extends AnnotationMirror> annos1,
Collection<? extends AnnotationMirror> annos2) {
Set<AnnotationMirror> result = AnnotationUtils.createAnnotationSet();
for (AnnotationMirror top : getTopAnnotations()) {
AnnotationMirror anno1ForTop = null;
for (AnnotationMirror anno1 : annos1) {
if (isSubtypeTypeVariable(anno1, top)) {
anno1ForTop = anno1;
}
}
AnnotationMirror anno2ForTop = null;
for (AnnotationMirror anno2 : annos2) {
if (isSubtypeTypeVariable(anno2, top)) {
anno2ForTop = anno2;
}
}
AnnotationMirror t = leastUpperBoundTypeVariable(anno1ForTop, anno2ForTop);
if (t != null) {
result.add(t);
}
}
return result;
}
/**
* Returns the type qualifiers that are the least upper bound of the qualifiers in annos1 and
* annos2.
*
* <p>This is necessary for determining the type of a conditional expression ({@code ?:}), where
* the type of the expression is the least upper bound of the true and false clauses.
*
* <p>This method takes an annotated type to decide if the type variable version of the method
* should be invoked, or if the normal version is sufficient (which provides more strict checks).
*
* @param type1 annotated type
* @param type2 annotated type
* @param annos1 qualifiers
* @param annos2 qualifiers
* @return the least upper bound of annos1 and annos2
* @deprecated Without the bounds of the type variable, it is not possible to correctly compute
* the relationship between "no qualifier" and a qualifier. Use {@link
* org.checkerframework.framework.util.AnnotatedTypes#leastUpperBound(AnnotatedTypeFactory,
* AnnotatedTypeMirror, AnnotatedTypeMirror)}.
*/
@Deprecated // 2020-07-29
default Set<? extends AnnotationMirror> leastUpperBounds(
AnnotatedTypeMirror type1,
AnnotatedTypeMirror type2,
Collection<? extends AnnotationMirror> annos1,
Collection<AnnotationMirror> annos2) {
if (canHaveEmptyAnnotationSet(type1) || canHaveEmptyAnnotationSet(type2)) {
return leastUpperBoundsTypeVariable(annos1, annos2);
} else {
return leastUpperBounds(annos1, annos2);
}
}
/**
* Returns the greatest lower bound for the qualifiers a1 and a2. Returns null if the qualifiers
* are not from the same qualifier hierarchy.
*
* <p>This method works even if the underlying Java type is a type variable. In that case, a
* 'null' AnnotationMirror is a legal argument that represents no annotation.
*
* @param a1 first annotation
* @param a2 second annotation
* @return greatest lower bound of the two annotations
* @deprecated Without the bounds of the type variable, it is not possible to correctly compute
* the relationship between "no qualifier" and a qualifier
*/
@Deprecated // 2020-07-29
default @Nullable AnnotationMirror greatestLowerBoundTypeVariable(
AnnotationMirror a1, AnnotationMirror a2) {
if (a1 == null) {
// [] is a supertype of any qualifier, and [] <: []
return a2;
}
if (a2 == null) {
// [] is a supertype of any qualifier, and [] <: []
return a1;
}
return greatestLowerBound(a1, a2);
}
/**
* Returns the type qualifiers that are the greatest lower bound of the qualifiers in annos1 and
* annos2. Returns null if the qualifiers are not from the same qualifier hierarchy.
*
* <p>This method works even if the underlying Java type is a type variable. In that case, the
* empty set is a legal argument that represents no annotation.
*
* @param annos1 first collection of qualifiers
* @param annos2 second collection of qualifiers
* @return greatest lower bound of the two collections of qualifiers
* @deprecated Without the bounds of the type variable, it is not possible to correctly compute
* the relationship between "no qualifier" and a qualifier
*/
@Deprecated // 2020-07-29
@SuppressWarnings("nullness") // Don't check deprecated method.
default Set<? extends AnnotationMirror> greatestLowerBoundsTypeVariable(
Collection<? extends AnnotationMirror> annos1,
Collection<? extends AnnotationMirror> annos2) {
Set<AnnotationMirror> result = AnnotationUtils.createAnnotationSet();
for (AnnotationMirror top : getTopAnnotations()) {
AnnotationMirror anno1ForTop = null;
for (AnnotationMirror anno1 : annos1) {
if (isSubtypeTypeVariable(anno1, top)) {
anno1ForTop = anno1;
}
}
AnnotationMirror anno2ForTop = null;
for (AnnotationMirror anno2 : annos2) {
if (isSubtypeTypeVariable(anno2, top)) {
anno2ForTop = anno2;
}
}
AnnotationMirror t = greatestLowerBoundTypeVariable(anno1ForTop, anno2ForTop);
if (t != null) {
result.add(t);
}
}
return result;
}
/**
* Returns the greatest lower bound for the qualifiers a1 and a2. Returns null if the qualifiers
* are not from the same qualifier hierarchy.
*
* <p>This method takes an annotated type to decide if the type variable version of the method
* should be invoked, or if the normal version is sufficient (which provides more strict checks).
*
* @param type1 annotated type
* @param type2 annotated type
* @param a1 first annotation
* @param a2 second annotation
* @return greatest lower bound of the two annotations
* @deprecated Without the bounds of the type variable, it is not possible to correctly compute
* the relationship between "no qualifier" and a qualifier
*/
@Deprecated // 2020-07-29
default @Nullable AnnotationMirror greatestLowerBound(
AnnotatedTypeMirror type1,
AnnotatedTypeMirror type2,
AnnotationMirror a1,
AnnotationMirror a2) {
if (canHaveEmptyAnnotationSet(type1) || canHaveEmptyAnnotationSet(type2)) {
return greatestLowerBoundTypeVariable(a1, a2);
} else {
return greatestLowerBound(a1, a2);
}
}
/**
* Returns the type qualifiers that are the greatest lower bound of the qualifiers in annos1 and
* annos2. Returns null if the qualifiers are not from the same qualifier hierarchy.
*
* <p>This method takes an annotated type to decide if the type variable version of the method
* should be invoked, or if the normal version is sufficient (which provides more strict checks).
*
* @param type1 annotated type
* @param type2 annotated type
* @param annos1 first collection of qualifiers
* @param annos2 second collection of qualifiers
* @return greatest lower bound of the two collections of qualifiers
* @deprecated Without the bounds of the type variable, it is not possible to correctly compute
* the relationship between "no qualifier" and a qualifier
*/
@Deprecated // 2020-07-29
default Set<? extends AnnotationMirror> greatestLowerBounds(
AnnotatedTypeMirror type1,
AnnotatedTypeMirror type2,
Collection<? extends AnnotationMirror> annos1,
Collection<AnnotationMirror> annos2) {
if (canHaveEmptyAnnotationSet(type1) || canHaveEmptyAnnotationSet(type2)) {
return greatestLowerBoundsTypeVariable(annos1, annos2);
} else {
return greatestLowerBounds(annos1, annos2);
}
}
}