package org.checkerframework.framework.type; /*>>> import org.checkerframework.checker.interning.qual.*; */ 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.framework.qual.PolyAll; import org.checkerframework.javacutil.AnnotationUtils; import org.checkerframework.javacutil.ErrorReporter; /** * Represents a type qualifier hierarchy. * * <p>All method parameter annotations need to be type qualifiers recognized within this hierarchy. * * <p>This assumes that any particular annotated type in a program is annotated with at least one * qualifier from the hierarchy. */ public abstract class QualifierHierarchy { /** * Determine whether the instance is valid. * * @return whether the instance is valid */ public boolean isValid() { // For most QH the simplest check is that there are qualifiers. return getTypeQualifiers().size() > 0; } // ********************************************************************** // Getter methods about this hierarchy // ********************************************************************** /** * Returns the width of this hierarchy, i.e. the expected number of annotations on any valid * type. */ public int getWidth() { return getTopAnnotations().size(); } /** @return the top (ultimate super) type qualifiers in the type system */ public abstract Set<? extends AnnotationMirror> getTopAnnotations(); /** * Return the top qualifier for the given qualifier, that is, the qualifier that is a supertype * of start but no further supertypes exist. */ public abstract AnnotationMirror getTopAnnotation(AnnotationMirror start); /** * Return the bottom for the given qualifier, that is, the qualifier that is a subtype of start * but no further subtypes exist. */ public abstract AnnotationMirror getBottomAnnotation(AnnotationMirror start); /** @return the bottom type qualifier in the hierarchy */ public abstract Set<? extends AnnotationMirror> getBottomAnnotations(); /** * @param start any qualifier from the type hierarchy * @return the polymorphic qualifier for that hierarchy */ public abstract AnnotationMirror getPolymorphicAnnotation(AnnotationMirror start); /** * Returns all type qualifiers in this type qualifier hierarchy. * * @return the fully qualified name represented in this hierarchy */ public abstract Set<? extends AnnotationMirror> getTypeQualifiers(); // ********************************************************************** // Qualifier Hierarchy Queries // ********************************************************************** /** * Tests whether rhs is equal to or a sub-qualifier of lhs, according to the type qualifier * hierarchy. This checks only the qualifiers, not the Java type. * * @return true iff rhs is a sub qualifier of lhs */ public abstract boolean isSubtype(AnnotationMirror rhs, AnnotationMirror lhs); /** * Tests whether there is any annotation in lhs that is a super qualifier of some annotation in * rhs. lhs and rhs contain only the annotations, not the Java type. * * @return true iff an annotation in lhs is a super of one in rhs */ public abstract boolean isSubtype( Collection<? extends AnnotationMirror> rhs, Collection<? extends AnnotationMirror> lhs); /** * Returns the least upper bound for the qualifiers a1 and a2. * * <p>Examples: * * <ul> * <li>For NonNull, leastUpperBound('Nullable', 'NonNull') ⇒ Nullable * </ul> * * The two qualifiers have to be from the same qualifier hierarchy. Otherwise, null will be * returned. * * @return the least restrictive qualifiers for both types */ public abstract AnnotationMirror leastUpperBound(AnnotationMirror a1, AnnotationMirror a2); /** * Returns whether or not this hierarchy implements {@link #widenUpperBound(AnnotationMirror, * AnnotationMirror)} * * @return whether or not this hierarchy implements {@link #widenUpperBound(AnnotationMirror, * AnnotationMirror)} */ public boolean implementsWidening() { return false; } /** * If the type 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 wider than the least upper bound. If this method is * implemented, also override {@link #implementsWidening()} and return true. * * <p>Otherwise, returns the least upper bound of the two annotations. * * @param a1 annotation * @param a2 annotation * @return an upper bound that is wider than the least upper bound of a1 and a2 (or the lub if * the type hierarchy does not require this) */ public AnnotationMirror widenUpperBound(AnnotationMirror a1, AnnotationMirror a2) { return leastUpperBound(a1, a2); } /** * Returns the greatest lower bound for the qualifiers a1 and a2. * * <p>The two qualifiers have to be from the same qualifier hierarchy. Otherwise, null will be * returned. * * @param a1 first annotation * @param a2 second annotation * @return greatest lower bound of the two annotations */ public abstract AnnotationMirror greatestLowerBound(AnnotationMirror a1, AnnotationMirror a2); /** * Returns the least upper bound of two types. Each type is represented as a set of type * qualifiers, as is the result. * * <p>Annos1 and annos2 must have the same size, and each annotation in them must be from a * different type hierarchy. * * <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. * * @param annos1 first collection of qualifiers * @param annos2 second collection of qualifiers * @return pairwise least upper bounds of elements of the input collections (which need not be * sorted in the same order) */ public Set<? extends AnnotationMirror> leastUpperBounds( Collection<? extends AnnotationMirror> annos1, Collection<? extends AnnotationMirror> annos2) { annos1 = replacePolyAll(annos1); annos2 = replacePolyAll(annos2); if (annos1.size() != annos2.size()) { ErrorReporter.errorAbort( "QualifierHierarchy.leastUpperBounds: tried to determine LUB with sets of different sizes!\n" + " Set 1: " + annos1 + " Set 2: " + annos2); } if (annos1.isEmpty()) { throw new Error( "QualifierHierarchy.leastUpperBounds: tried to determine LUB with empty sets!"); } Set<AnnotationMirror> result = AnnotationUtils.createAnnotationSet(); for (AnnotationMirror a1 : annos1) { for (AnnotationMirror a2 : annos2) { AnnotationMirror lub = leastUpperBound(a1, a2); if (lub != null) { result.add(lub); } } } assert result.size() == annos1.size() : "QualifierHierarchy.leastUpperBounds: resulting set has incorrect number of annotations!\n" + " Set 1: " + annos1 + " Set 2: " + annos2 + " LUB: " + result; return result; } /** * Returns a new set that is the passed set, but PolyAll has been replaced by a polymorphic * qualifiers, for hierarchies that do not have an annotation in the set. * * @param annos set of annotations * @return a new set with same annotations as anno, but PolyAll has been replaced with * polymorphic qualifiers */ protected Collection<? extends AnnotationMirror> replacePolyAll( Collection<? extends AnnotationMirror> annos) { Set<AnnotationMirror> returnAnnos = AnnotationUtils.createAnnotationSet(); for (AnnotationMirror top : getTopAnnotations()) { AnnotationMirror annotationInHierarchy = findAnnotationInHierarchy(annos, top); if (annotationInHierarchy != null) { returnAnnos.add(annotationInHierarchy); } } return returnAnnos; } /** * Returns the greatest lower bound of two types. Each type is represented as a set of type * qualifiers, as is the result. * * <p>Annos1 and annos2 must have the same size, and each annotation in them must be from a * different type hierarchy. * * @param annos1 first collection of qualifiers * @param annos2 second collection of qualifiers * @return pairwise greatest lower bounds of elements of the input collections (which need not * be sorted in the same order) */ public Set<? extends AnnotationMirror> greatestLowerBounds( Collection<? extends AnnotationMirror> annos1, Collection<? extends AnnotationMirror> annos2) { if (annos1.size() != annos2.size()) { ErrorReporter.errorAbort( "QualifierHierarchy.greatestLowerBounds: tried to determine GLB with sets of different sizes!\n" + " Set 1: " + annos1 + " Set 2: " + annos2); } if (annos1.isEmpty()) { ErrorReporter.errorAbort( "QualifierHierarchy.greatestLowerBounds: tried to determine GLB with empty sets!"); } Set<AnnotationMirror> result = AnnotationUtils.createAnnotationSet(); for (AnnotationMirror a1 : annos1) { for (AnnotationMirror a2 : annos2) { AnnotationMirror glb = greatestLowerBound(a1, a2); if (glb != null) { result.add(glb); } } } assert result.size() == annos1.size() : "QualifierHierarchy.greatestLowerBounds: resulting set has incorrect number of annotations!\n" + " Set 1: " + annos1 + " Set 2: " + annos2 + " GLB: " + result; return result; } /** * Tests whether {@code subAnno} is a sub-qualifier of {@code superAnno}, according to the type * qualifier hierarchy. This checks only the qualifiers, not the Java type. * * <p>This method works even if the underlying Java type is a type variable. In that case, a * 'null' AnnnotationMirror and the empty set represent a meaningful value (namely, no * annotation). * * @return true iff {@code subAnno} is a sub qualifier of {@code superAnno} */ public abstract boolean isSubtypeTypeVariable( AnnotationMirror subAnno, AnnotationMirror superAnno); /** * Tests whether there is any annotation in superAnnos that is a super qualifier of 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, a * 'null' AnnnotationMirror and the empty set represent a meaningful value (namely, no * annotation). * * @return true iff an annotation in superAnnos is a super of one in subAnnos */ // This method requires more revision. public abstract boolean isSubtypeTypeVariable( Collection<? extends AnnotationMirror> subAnnos, Collection<? extends AnnotationMirror> superAnnos); /** * Returns the least upper bound for the qualifiers a1 and a2. * * <p>Examples: * * <ul> * <li>For NonNull, leastUpperBound('Nullable', 'NonNull') → Nullable * </ul> * * The two qualifiers have to be from the same qualifier hierarchy. Otherwise, null will be * returned. * * <p>This method works even if the underlying Java type is a type variable. In that case, a * 'null' AnnnotationMirror and the empty set represent a meaningful value (namely, no * annotation). * * @return the least restrictive qualifiers for both types */ public abstract AnnotationMirror leastUpperBoundTypeVariable( AnnotationMirror a1, AnnotationMirror a2); /** * Returns the greatest lower bound for the qualifiers a1 and a2. * * <p>The two qualifiers have to be from the same qualifier hierarchy. Otherwise, null will be * returned. * * <p>This method works even if the underlying Java type is a type variable. In that case, a * 'null' AnnnotationMirror and the empty set represent a meaningful value (namely, no * annotation). * * @param a1 first annotation * @param a2 second annotation * @return greatest lower bound of the two annotations */ public abstract AnnotationMirror greatestLowerBoundTypeVariable( AnnotationMirror a1, AnnotationMirror 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, a * 'null' AnnnotationMirror and the empty set represent a meaningful value (namely, no * annotation). * * @return the least upper bound of annos1 and annos2 */ public 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 greatest lower bound of the qualifiers in annos1 and * annos2. * * <p>The two qualifiers have to be from the same qualifier hierarchy. Otherwise, null will be * returned. * * <p>This method works even if the underlying Java type is a type variable. In that case, a * 'null' AnnnotationMirror and the empty set represent a meaningful value (namely, no * annotation). * * @param annos1 first collection of qualifiers * @param annos2 second collection of qualifiers * @return greatest lower bound of the two collections of qualifiers */ public 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 true if and only if the given type can have empty annotation sets (and thus the * *TypeVariable methods need to be used). */ public 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; } /** * Tests whether {@code subAnno} is a sub-qualifier of {@code superAnno}, 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). * * @return true iff {@code subAnno} is a sub qualifier of {@code superAnno} */ public boolean isSubtype( AnnotatedTypeMirror subType, AnnotatedTypeMirror superType, AnnotationMirror subAnno, AnnotationMirror superAnno) { if (canHaveEmptyAnnotationSet(subType) || canHaveEmptyAnnotationSet(superType)) { return isSubtypeTypeVariable(subAnno, superAnno); } else { return isSubtype(subAnno, superAnno); } } /** * Tests whether there is any annotation in {@code supers} that is a super qualifier of 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). * * @return true iff an annotation in {@code supers} is a super of one in {@code subs} */ public 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); } } /** * Returns the least upper bound for the qualifiers a1 and a2. * * <p>Examples: * * <ul> * <li>For NonNull, leastUpperBound('Nullable', 'NonNull') → Nullable * </ul> * * The two qualifiers have to be from the same qualifier hierarchy. Otherwise, null will be * returned. * * <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). * * @return the least restrictive qualifiers for both types */ public 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 greatest lower bound for the qualifiers a1 and a2. * * <p>The two qualifiers have to be from the same qualifier hierarchy. Otherwise, null will be * returned. * * <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 a1 first annotation * @param a2 second annotation * @return greatest lower bound of the two annotations */ public 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 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). * * @return the least upper bound of annos1 and annos2 */ public 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 type qualifiers that are the greatest lower bound of the qualifiers in annos1 and * annos2. * * <p>The two qualifiers have to be from the same qualifier hierarchy. Otherwise, null will be * returned. * * <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 annos1 first collection of qualifiers * @param annos2 second collection of qualifiers * @return greatest lower bound of the two collections of qualifiers */ public 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); } } /** * @deprecated use {@link #findAnnotationInSameHierarchy(Collection, AnnotationMirror)} instead */ @Deprecated public AnnotationMirror findCorrespondingAnnotation( AnnotationMirror aliased, Collection<? extends AnnotationMirror> a) { return findAnnotationInSameHierarchy(a, aliased); } /** * Returns the annotation in annos that is in the same hierarchy as annotationMirror. * * <p>If the annotation in the hierarchy is PolyAll, then the polymorphic qualifier in the * hierarchy is returned instead of PolyAll. * * @param annos set of annotations to search * @param annotationMirror annotation that is in the same hierarchy as the returned annotation * @return annotation in the same hierarchy as annotationMirror, or null if one is not found */ public AnnotationMirror findAnnotationInSameHierarchy( Collection<? extends AnnotationMirror> annos, AnnotationMirror annotationMirror) { AnnotationMirror top = this.getTopAnnotation(annotationMirror); return findAnnotationInHierarchy(annos, top); } /** @deprecated use {@link #findAnnotationInHierarchy(Collection, AnnotationMirror)} instead */ @Deprecated public AnnotationMirror getAnnotationInHierarchy( Collection<? extends AnnotationMirror> annos, AnnotationMirror annotationMirror) { return findAnnotationInHierarchy(annos, annotationMirror); } /** * Returns the annotation in annos that is in the hierarchy for which annotationMirror is top. * * <p>If the annotation in the hierarchy is PolyAll, then the polymorphic qualifier in the * hierarchy is returned instead of PolyAll. * * @param annos 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 */ public AnnotationMirror findAnnotationInHierarchy( Collection<? extends AnnotationMirror> annos, AnnotationMirror top) { boolean hasPolyAll = false; for (AnnotationMirror anno : annos) { boolean isSubtype = isSubtype(anno, top); if (isSubtype && AnnotationUtils.areSameByClass(anno, PolyAll.class)) { // If the set contains @PolyAll, only return the polymorphic qualifier if annos // contains no other annotation in the hierarchy. hasPolyAll = true; } else if (isSubtype) { return anno; } } if (hasPolyAll) { return getPolymorphicAnnotation(top); } return null; } /** * Update a mapping from some key to a set of AnnotationMirrors. If the key already exists in * the mapping and the new qualifier is in the same qualifier hierarchy as any of the existing * qualifiers, do nothing and return false. If the key already exists in the mapping and the new * qualifier is not in the same qualifier hierarchy as any of the existing qualifiers, add the * qualifier to the existing set and return true. If the key does not exist in the mapping, add * the new qualifier as a singleton set and return true. * * @param map the mapping to modify * @param key the key to update * @param newQual the value to add * @return whether there was a qualifier hierarchy collision */ public <T> boolean updateMappingToMutableSet( Map<T, Set<AnnotationMirror>> map, T key, AnnotationMirror newQual) { if (!map.containsKey(key)) { Set<AnnotationMirror> set = AnnotationUtils.createAnnotationSet(); set.add(newQual); map.put(key, set); } else { Set<AnnotationMirror> prevs = map.get(key); for (AnnotationMirror p : prevs) { if (AnnotationUtils.areSame(getTopAnnotation(p), getTopAnnotation(newQual))) { return false; } } prevs.add(newQual); map.put(key, prevs); } return true; } }