| 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') ⇒ 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') → 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') → 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); |
| } |
| } |
| } |