| package org.checkerframework.framework.util; |
| |
| import java.lang.annotation.Annotation; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.Set; |
| import java.util.StringJoiner; |
| import javax.lang.model.element.AnnotationMirror; |
| import javax.lang.model.element.Element; |
| import javax.lang.model.element.Name; |
| import org.checkerframework.checker.nullness.qual.Nullable; |
| import org.checkerframework.checker.signature.qual.CanonicalName; |
| import org.checkerframework.dataflow.qual.Pure; |
| import org.checkerframework.dataflow.qual.SideEffectFree; |
| import org.checkerframework.framework.qual.PolymorphicQualifier; |
| import org.checkerframework.framework.qual.SubtypeOf; |
| import org.checkerframework.framework.type.AnnotatedTypeFactory; |
| import org.checkerframework.framework.type.ElementQualifierHierarchy; |
| import org.checkerframework.framework.type.MostlyNoElementQualifierHierarchy; |
| import org.checkerframework.framework.type.NoElementQualifierHierarchy; |
| import org.checkerframework.framework.type.QualifierHierarchy; |
| import org.checkerframework.javacutil.AnnotationBuilder; |
| import org.checkerframework.javacutil.AnnotationUtils; |
| import org.checkerframework.javacutil.BugInCF; |
| import org.checkerframework.javacutil.TypeSystemError; |
| |
| /** |
| * Represents the type qualifier hierarchy of a type system that supports multiple separate subtype |
| * hierarchies. |
| * |
| * <p>This class is immutable and can be only created through {@link MultiGraphFactory}. |
| * |
| * @deprecated Use {@link ElementQualifierHierarchy}, {@link MostlyNoElementQualifierHierarchy}, or |
| * {@link NoElementQualifierHierarchy} instead. This class will be removed in a future release. |
| * <p>Here are instructions on how to convert from a subclass of MultiGraphQualifierHierarchy to |
| * the new implementations: |
| * <p>If the subclass implements isSubtype and calls super when annotations do not have |
| * elements, then use the following instructions to convert to {@link |
| * MostlyNoElementQualifierHierarchy}. |
| * <ol> |
| * <li>Change {@code extends MultiGraphQualifierHierarchy} to {@code extends |
| * MostlyNoElementQualifierHierarchy}. |
| * <li>Create a constructor matching super. |
| * <li>Implement isSubtypeWithElements, leastUpperBoundWithElements, and |
| * greatestLowerBoundWithElements. You may be able to reuse parts of your current |
| * implementation of isSubtype, leastUpperBound, and greatestLowerBound. |
| * </ol> |
| * <p>If the subclass implements isSubtype and does not call super in that implementation, then |
| * use the following instructions to convert to a subclass of {@link ElementQualifierHierarchy}. |
| * <ol> |
| * <li>Change {@code extends MultiGraphQualifierHierarchy} to {@code extends |
| * ElementQualifierHierarchy}. |
| * <li>Create a constructor matching super. |
| * <li>Implement {@link #leastUpperBound(AnnotationMirror, AnnotationMirror)} and {@link |
| * #greatestLowerBound(AnnotationMirror, AnnotationMirror)} if missing. (In the past, it |
| * was very easy to forget to implement these, now they are abstract methods.) |
| * </ol> |
| * If you wish to continue to use a subclass of {@link MultiGraphQualifierHierarchy} or {@link |
| * GraphQualifierHierarchy}, you may do so by adding the following to AnnotatedTypeFactory. |
| * (It's better to convert to one of the new classes because MultiGraphQualifierHierarchy and |
| * GraphQualifierHierarchy are buggy and no longer supported.) |
| * <p>If any qualifier has an annotation element without a default value, you will need to |
| * convert to one of the new subclasses. If you do not, then MultiGraphQualifierHierarchy will |
| * throw an exception with a message like "AnnotationBuilder.fromName: no value for element |
| * value() of checkers.inference.qual.VarAnnot". |
| * <pre> |
| * {@code @Override} |
| * {@code @SuppressWarnings("deprecation")} |
| * <code> public QualifierHierarchy createQualifierHierarchy() { |
| * return org.checkerframework.framework.util.MultiGraphQualifierHierarchy |
| * .createMultiGraphQualifierHierarchy(this); |
| * } |
| * </code> |
| * {@code @Override} |
| * {@code @SuppressWarnings("deprecation")} |
| * <code> public QualifierHierarchy createQualifierHierarchyWithMultiGraphFactory( |
| * org.checkerframework.framework.util.MultiGraphQualifierHierarchy.MultiGraphFactory |
| * factory) { |
| * return new YourSubclassQualifierHierarchy(factory); |
| * } |
| * </code></pre> |
| */ |
| @SuppressWarnings("interning") // Class is deprecated. |
| @Deprecated // 2020-09-10 |
| public class MultiGraphQualifierHierarchy implements QualifierHierarchy { |
| |
| /** |
| * Creates the QualifierHierarchy using {@link |
| * org.checkerframework.framework.util.MultiGraphQualifierHierarchy} |
| * |
| * @param annotatedTypeFactory annotated type factory |
| * @return qualifier hierarchy |
| * @deprecated Use {@link ElementQualifierHierarchy} instead. |
| */ |
| @Deprecated // 2020-09-10 |
| public static QualifierHierarchy createMultiGraphQualifierHierarchy( |
| AnnotatedTypeFactory annotatedTypeFactory) { |
| Set<Class<? extends Annotation>> supportedTypeQualifiers = |
| annotatedTypeFactory.getSupportedTypeQualifiers(); |
| MultiGraphFactory factory = new MultiGraphFactory(annotatedTypeFactory); |
| for (Class<? extends Annotation> typeQualifier : supportedTypeQualifiers) { |
| AnnotationMirror typeQualifierAnno = |
| AnnotationBuilder.fromClass(annotatedTypeFactory.getElementUtils(), typeQualifier); |
| factory.addQualifier(typeQualifierAnno); |
| // Polymorphic qualifiers can't declare their supertypes. |
| // An error is raised if one is present. |
| if (typeQualifier.getAnnotation(PolymorphicQualifier.class) != null) { |
| if (typeQualifier.getAnnotation(SubtypeOf.class) != null) { |
| // This is currently not supported. At some point we might add |
| // polymorphic qualifiers with upper and lower bounds. |
| throw new TypeSystemError( |
| "AnnotatedTypeFactory: " |
| + typeQualifier |
| + " is polymorphic and specifies super qualifiers." |
| + " Remove the @org.checkerframework.framework.qual.SubtypeOf or" |
| + " @org.checkerframework.framework.qual.PolymorphicQualifier annotation from" |
| + " it."); |
| } |
| continue; |
| } |
| if (typeQualifier.getAnnotation(SubtypeOf.class) == null) { |
| throw new TypeSystemError( |
| "AnnotatedTypeFactory: %s does not specify its super qualifiers.%n" |
| + "Add an @org.checkerframework.framework.qual.SubtypeOf annotation to it,%n" |
| + "or if it is an alias, exclude it from `createSupportedTypeQualifiers()`.%n", |
| typeQualifier); |
| } |
| Class<? extends Annotation>[] superQualifiers = |
| typeQualifier.getAnnotation(SubtypeOf.class).value(); |
| for (Class<? extends Annotation> superQualifier : superQualifiers) { |
| if (!supportedTypeQualifiers.contains(superQualifier)) { |
| throw new TypeSystemError( |
| "Found unsupported qualifier in SubTypeOf: %s on qualifier: %s", |
| superQualifier.getCanonicalName(), typeQualifier.getCanonicalName()); |
| } |
| if (superQualifier.getAnnotation(PolymorphicQualifier.class) != null) { |
| // This is currently not supported. No qualifier can have a polymorphic |
| // qualifier as super qualifier. |
| throw new TypeSystemError( |
| "Found polymorphic qualifier in SubTypeOf: %s on qualifier: %s", |
| superQualifier.getCanonicalName(), typeQualifier.getCanonicalName()); |
| } |
| AnnotationMirror superAnno = |
| AnnotationBuilder.fromClass(annotatedTypeFactory.getElementUtils(), superQualifier); |
| factory.addSubtype(typeQualifierAnno, superAnno); |
| } |
| } |
| |
| QualifierHierarchy hierarchy = factory.build(); |
| |
| if (!hierarchy.isValid()) { |
| throw new TypeSystemError( |
| "AnnotatedTypeFactory: invalid qualifier hierarchy: " |
| + hierarchy.getClass() |
| + " " |
| + hierarchy); |
| } |
| |
| return hierarchy; |
| } |
| |
| /** |
| * Factory used to create an instance of {@link GraphQualifierHierarchy}. A factory can be used to |
| * create at most one {@link GraphQualifierHierarchy}. |
| * |
| * <p>To create a hierarchy, a client may do so in three steps: |
| * |
| * <ol> |
| * <li>add qualifiers using {@link #addQualifier(AnnotationMirror)}; |
| * <li>add subtype relations using {@link #addSubtype(AnnotationMirror, AnnotationMirror)} |
| * <li>build the hierarchy and gets using {@link #build()}. |
| * </ol> |
| * |
| * Notice that {@link #addSubtype(AnnotationMirror, AnnotationMirror)} adds the two qualifiers to |
| * the hierarchy if they are not already in. |
| * |
| * <p>Also, once the client builds a hierarchy through {@link #build()}, no further modifications |
| * are allowed nor can it making a new instance. |
| * |
| * <p>Clients build the hierarchy using {@link #addQualifier(AnnotationMirror)} and {@link |
| * #addSubtype(AnnotationMirror, AnnotationMirror)}, then get the instance with calling {@link |
| * #build()} |
| * |
| * @deprecated Use {@link ElementQualifierHierarchy} instead. |
| */ |
| @Deprecated // 2020-09-10 |
| public static class MultiGraphFactory { |
| /** |
| * Map from qualifiers to the direct supertypes of the qualifier. Only the subtype relations |
| * given by addSubtype are in this mapping, no transitive relationships. It is immutable once |
| * GraphQualifierHierarchy is built. No polymorphic qualifiers are contained in this map. |
| */ |
| protected final Map<AnnotationMirror, Set<AnnotationMirror>> supertypesDirect; |
| |
| /** |
| * Map from qualifier hierarchy to the corresponding polymorphic qualifier. The key is: |
| * |
| * <ul> |
| * <li>the argument to @PolymorphicQualifier (typically the top qualifier in the hierarchy), |
| * or |
| * <li>"Annotation" if @PolymorphicQualifier is used without an argument, or |
| * </ul> |
| */ |
| protected final Map<AnnotationMirror, AnnotationMirror> polyQualifiers; |
| |
| /** The annotated type factory associated with this hierarchy. */ |
| protected final AnnotatedTypeFactory atypeFactory; |
| |
| /** Create a factory. */ |
| public MultiGraphFactory(AnnotatedTypeFactory atypeFactory) { |
| this.supertypesDirect = AnnotationUtils.createAnnotationMap(); |
| this.polyQualifiers = AnnotationUtils.createAnnotationMap(); |
| this.atypeFactory = atypeFactory; |
| } |
| |
| /** |
| * Adds the passed qualifier to the hierarchy. Clients need to specify its super qualifiers in |
| * subsequent calls to {@link #addSubtype(AnnotationMirror, AnnotationMirror)}. |
| */ |
| public void addQualifier(AnnotationMirror qual) { |
| assertNotBuilt(); |
| if (AnnotationUtils.containsSame(supertypesDirect.keySet(), qual)) { |
| return; |
| } |
| |
| @CanonicalName Name pqtopclass = getPolymorphicQualifierElement(qual); |
| if (pqtopclass != null) { |
| AnnotationMirror pqtop; |
| if (pqtopclass.contentEquals(Annotation.class.getName())) { |
| // A @PolymorphicQualifier with no value defaults to Annotation.class. |
| // That means there is only one top in the hierarchy. The top qualifier |
| // may not be known at this point, so use the qualifier itself. |
| // This is changed to top in MultiGraphQualifierHierarchy.addPolyRelations |
| pqtop = qual; |
| } else { |
| pqtop = AnnotationBuilder.fromName(atypeFactory.getElementUtils(), pqtopclass); |
| } |
| // use given top (which might be Annotation) as key |
| this.polyQualifiers.put(pqtop, qual); |
| } else { |
| supertypesDirect.put(qual, AnnotationUtils.createAnnotationSet()); |
| } |
| } |
| |
| /** |
| * Returns the {@link PolymorphicQualifier} meta-annotation on {@code qual} if one exists; |
| * otherwise return null. |
| * |
| * @param qual qualifier |
| * @return the {@link PolymorphicQualifier} meta-annotation on {@code qual} if one exists; |
| * otherwise return null |
| */ |
| private AnnotationMirror getPolymorphicQualifier(AnnotationMirror qual) { |
| if (qual == null) { |
| return null; |
| } |
| Element qualElt = qual.getAnnotationType().asElement(); |
| for (AnnotationMirror am : qualElt.getAnnotationMirrors()) { |
| if (atypeFactory.areSameByClass(am, PolymorphicQualifier.class)) { |
| return am; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * If {@code qual} is a polymorphic qualifier, return the class specified by the {@link |
| * PolymorphicQualifier} meta-annotation on the polymorphic qualifier is returned. Otherwise, |
| * return null. |
| * |
| * <p>This value identifies the qualifier hierarchy to which this polymorphic qualifier belongs. |
| * By convention, it is the top qualifier of the hierarchy. Use of {@code Annotation.class} is |
| * discouraged, because it can lead to ambiguity if used for multiple type systems. |
| * |
| * @param qual an annotation |
| * @return the name of the class specified by the {@link PolymorphicQualifier} meta-annotation |
| * on {@code qual}, if {@code qual} is a polymorphic qualifier; otherwise, null. |
| * @see org.checkerframework.framework.qual.PolymorphicQualifier#value() |
| */ |
| private @Nullable @CanonicalName Name getPolymorphicQualifierElement(AnnotationMirror qual) { |
| AnnotationMirror poly = getPolymorphicQualifier(qual); |
| |
| // System.out.println("poly: " + poly + " pq: " + |
| // PolymorphicQualifier.class.getCanonicalName()); |
| if (poly == null) { |
| return null; |
| } |
| @SuppressWarnings("deprecation" |
| // It would be possible to use the ExecutableElement for `PolymorphicQualifier.value` rather |
| // than the string "value", but it is not convenient to obtain the ExecutableElement. |
| ) |
| Name ret = AnnotationUtils.getElementValueClassName(poly, "value", true); |
| return ret; |
| } |
| |
| /** |
| * Adds a subtype relationship between the two type qualifiers. Assumes that both qualifiers are |
| * part of the same qualifier hierarchy; callers should ensure this. |
| * |
| * @param sub the sub type qualifier |
| * @param sup the super type qualifier |
| */ |
| public void addSubtype(AnnotationMirror sub, AnnotationMirror sup) { |
| assertNotBuilt(); |
| addQualifier(sub); |
| addQualifier(sup); |
| supertypesDirect.get(sub).add(sup); |
| } |
| |
| /** |
| * Returns an instance of {@link GraphQualifierHierarchy} that represents the hierarchy built so |
| * far. |
| */ |
| public QualifierHierarchy build() { |
| assertNotBuilt(); |
| QualifierHierarchy result = createQualifierHierarchy(); |
| wasBuilt = true; |
| return result; |
| } |
| |
| /** |
| * Create {@link QualifierHierarchy} |
| * |
| * @return QualifierHierarchy |
| */ |
| protected QualifierHierarchy createQualifierHierarchy() { |
| return atypeFactory.createQualifierHierarchyWithMultiGraphFactory(this); |
| } |
| |
| /** True if the factory has already been built. */ |
| private boolean wasBuilt = false; |
| |
| /** Throw an exception if the factory was already built. */ |
| protected void assertNotBuilt() { |
| if (wasBuilt) { |
| throw new BugInCF("MultiGraphQualifierHierarchy.Factory was already built."); |
| } |
| } |
| } |
| |
| /** |
| * The declared, direct supertypes for each qualifier, without added transitive relations. |
| * Immutable after construction finishes. No polymorphic qualifiers are contained in this map. |
| * |
| * @see MultiGraphQualifierHierarchy.MultiGraphFactory#supertypesDirect |
| */ |
| protected final Map<AnnotationMirror, Set<AnnotationMirror>> supertypesDirect; |
| |
| /** The transitive closure of the supertypesDirect. Immutable after construction finishes. */ |
| protected final Map<AnnotationMirror, Set<AnnotationMirror>> supertypesTransitive; |
| |
| /** The top qualifiers of the individual type hierarchies. */ |
| protected final Set<AnnotationMirror> tops; |
| |
| /** The bottom qualifiers of the type hierarchies. TODO: clarify relation to tops. */ |
| protected final Set<AnnotationMirror> bottoms; |
| |
| /** |
| * See {@link MultiGraphQualifierHierarchy.MultiGraphFactory#polyQualifiers}. |
| * |
| * @see MultiGraphQualifierHierarchy.MultiGraphFactory#polyQualifiers |
| */ |
| protected final Map<AnnotationMirror, AnnotationMirror> polyQualifiers; |
| |
| /** All qualifiers, including polymorphic qualifiers. */ |
| private final Set<AnnotationMirror> typeQualifiers; |
| |
| public MultiGraphQualifierHierarchy(MultiGraphFactory f) { |
| this(f, (Object[]) null); |
| } |
| |
| // Allow a subclass to provide additional constructor parameters that |
| // are simply passed back via a call to the "finish" method. |
| public MultiGraphQualifierHierarchy(MultiGraphFactory f, Object... args) { |
| super(); |
| // no need for copying as f.supertypes has no mutable references to it |
| // TODO: also make the Set of supertypes immutable? |
| this.supertypesDirect = Collections.unmodifiableMap(f.supertypesDirect); |
| |
| // Calculate the transitive closure |
| Map<AnnotationMirror, Set<AnnotationMirror>> supertypesTransitive = |
| transitiveClosure(f.supertypesDirect); |
| |
| Set<AnnotationMirror> newtops = findTops(supertypesTransitive); |
| Set<AnnotationMirror> newbottoms = findBottoms(supertypesTransitive); |
| |
| this.polyQualifiers = f.polyQualifiers; |
| |
| addPolyRelations(this, supertypesTransitive, this.polyQualifiers, newtops, newbottoms); |
| |
| finish(this, supertypesTransitive, this.polyQualifiers, newtops, newbottoms, args); |
| |
| this.tops = Collections.unmodifiableSet(newtops); |
| this.bottoms = Collections.unmodifiableSet(newbottoms); |
| // TODO: make polyQualifiers immutable also? |
| |
| this.supertypesTransitive = Collections.unmodifiableMap(supertypesTransitive); |
| Set<AnnotationMirror> typeQualifiers = AnnotationUtils.createAnnotationSet(); |
| typeQualifiers.addAll(supertypesTransitive.keySet()); |
| this.typeQualifiers = Collections.unmodifiableSet(typeQualifiers); |
| // System.out.println("MGH: " + this); |
| } |
| |
| @Override |
| public boolean isValid() { |
| return !typeQualifiers.isEmpty(); |
| } |
| |
| /** |
| * Method to finalize the qualifier hierarchy before it becomes unmodifiable. The parameters pass |
| * all fields and allow modification. |
| */ |
| protected void finish( |
| QualifierHierarchy qualHierarchy, |
| Map<AnnotationMirror, Set<AnnotationMirror>> supertypesTransitive, |
| Map<AnnotationMirror, AnnotationMirror> polyQualifiers, |
| Set<AnnotationMirror> tops, |
| Set<AnnotationMirror> bottoms, |
| Object... args) {} |
| |
| @SideEffectFree |
| @Override |
| public String toString() { |
| StringJoiner sj = new StringJoiner(System.lineSeparator()); |
| sj.add("Supertypes Graph: "); |
| |
| for (Map.Entry<AnnotationMirror, Set<AnnotationMirror>> qual : supertypesDirect.entrySet()) { |
| sj.add("\t" + qual.getKey() + " = " + qual.getValue()); |
| } |
| |
| sj.add("Supertypes Map: "); |
| |
| for (Map.Entry<AnnotationMirror, Set<AnnotationMirror>> qual : |
| supertypesTransitive.entrySet()) { |
| String keyOpen = "\t" + qual.getKey() + " = ["; |
| |
| Set<AnnotationMirror> supertypes = qual.getValue(); |
| |
| if (supertypes.size() == 1) { |
| // If there's only 1 supertype for this qual, then display that in the same row. |
| sj.add(keyOpen + supertypes.iterator().next() + "]"); |
| } else { |
| // otherwise, display each supertype in its own row |
| sj.add(keyOpen); |
| for (Iterator<AnnotationMirror> iterator = supertypes.iterator(); iterator.hasNext(); ) { |
| sj.add("\t\t" + iterator.next() + (iterator.hasNext() ? ", " : "")); |
| } |
| sj.add("\t\t]"); |
| } |
| } |
| |
| sj.add("Tops: " + tops); |
| sj.add("Bottoms: " + bottoms); |
| |
| return sj.toString(); |
| } |
| |
| @Override |
| public Set<? extends AnnotationMirror> getTopAnnotations() { |
| return this.tops; |
| } |
| |
| @Override |
| public AnnotationMirror getTopAnnotation(AnnotationMirror start) { |
| for (AnnotationMirror top : tops) { |
| if (AnnotationUtils.areSame(start, top) || isSubtype(start, top)) { |
| return top; |
| } |
| } |
| throw new BugInCF( |
| "MultiGraphQualifierHierarchy: did not find the top corresponding to qualifier " |
| + start |
| + " all tops: " |
| + tops); |
| } |
| |
| @Override |
| public Set<? extends AnnotationMirror> getBottomAnnotations() { |
| return this.bottoms; |
| } |
| |
| @Override |
| public AnnotationMirror getBottomAnnotation(AnnotationMirror start) { |
| for (AnnotationMirror bot : bottoms) { |
| if (AnnotationUtils.areSame(start, bot) || isSubtype(bot, start)) { |
| return bot; |
| } |
| } |
| throw new BugInCF( |
| "MultiGraphQualifierHierarchy: did not find the bottom corresponding to qualifier " |
| + start |
| + "; all bottoms: " |
| + bottoms |
| + "; this: " |
| + this); |
| } |
| |
| @Override |
| public AnnotationMirror getPolymorphicAnnotation(AnnotationMirror start) { |
| AnnotationMirror top = getTopAnnotation(start); |
| for (AnnotationMirror key : polyQualifiers.keySet()) { |
| if (key != null && AnnotationUtils.areSame(key, top)) { |
| return polyQualifiers.get(key); |
| } |
| } |
| // No polymorphic qualifier exists for that hierarchy. |
| return null; |
| } |
| |
| @Override |
| public boolean isSubtype( |
| Collection<? extends AnnotationMirror> rhs, Collection<? extends AnnotationMirror> lhs) { |
| if (lhs.isEmpty() || rhs.isEmpty()) { |
| throw new BugInCF( |
| "MultiGraphQualifierHierarchy: empty annotations in lhs: " + lhs + " or rhs: " + rhs); |
| } |
| if (lhs.size() != rhs.size()) { |
| throw new BugInCF( |
| "MultiGraphQualifierHierarchy: mismatched number of annotations in lhs: " |
| + lhs |
| + " and rhs: " |
| + rhs); |
| } |
| int valid = 0; |
| for (AnnotationMirror lhsAnno : lhs) { |
| for (AnnotationMirror rhsAnno : rhs) { |
| if (AnnotationUtils.areSame(getTopAnnotation(lhsAnno), getTopAnnotation(rhsAnno)) |
| && isSubtype(rhsAnno, lhsAnno)) { |
| ++valid; |
| } |
| } |
| } |
| return lhs.size() == valid; |
| } |
| |
| /** For caching results of lubs * */ |
| private Map<AnnotationPair, AnnotationMirror> lubs = null; |
| |
| @Override |
| public AnnotationMirror leastUpperBound(AnnotationMirror a1, AnnotationMirror a2) { |
| if (!AnnotationUtils.areSameByName(getTopAnnotation(a1), getTopAnnotation(a2))) { |
| return null; |
| } else if (isSubtype(a1, a2)) { |
| return a2; |
| } else if (isSubtype(a2, a1)) { |
| return a1; |
| } else if (AnnotationUtils.areSameByName(a1, a2)) { |
| return getTopAnnotation(a1); |
| } |
| if (lubs == null) { |
| lubs = calculateLubs(); |
| } |
| AnnotationPair pair = new AnnotationPair(a1, a2); |
| return lubs.get(pair); |
| } |
| |
| /** A cache of the results of glb computations. Maps from a pair of annotations to their glb. */ |
| private Map<AnnotationPair, AnnotationMirror> glbs = null; |
| |
| @Override |
| public AnnotationMirror greatestLowerBound(AnnotationMirror a1, AnnotationMirror a2) { |
| if (AnnotationUtils.areSameByName(a1, a2)) { |
| return AnnotationUtils.sameElementValues(a1, a2) ? a1 : getBottomAnnotation(a1); |
| } |
| if (glbs == null) { |
| glbs = calculateGlbs(); |
| } |
| AnnotationPair pair = new AnnotationPair(a1, a2); |
| return glbs.get(pair); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * <p>Most qualifiers have no value fields. However, two annotations with values are subtype of |
| * each other only if they have the same values. i.e. I(m) is a subtype of I(n) iff m = n. |
| * |
| * <p>When client specifies an annotation, a1, to be a subtype of annotation with values, a2, then |
| * a1 is a subtype of all instances of a2 regardless of a2 values. |
| * |
| * @param subAnno the sub qualifier |
| * @param superAnno the super qualifier |
| */ |
| @Override |
| public boolean isSubtype(AnnotationMirror subAnno, AnnotationMirror superAnno) { |
| checkAnnoInGraph(subAnno); |
| checkAnnoInGraph(superAnno); |
| |
| /* TODO: this optimization leads to recursion |
| for (AnnotationMirror top : tops) { |
| System.out.println("Looking at top: " + tops + " and " + anno1); |
| // We cannot use getRootAnnotation, as that would use subtyping and recurse |
| if (isSubtype(anno1, top) && AnnotationUtils.areSame(top, anno2)) { |
| return true; |
| } |
| }*/ |
| if (AnnotationUtils.areSameByName(subAnno, superAnno)) { |
| return AnnotationUtils.sameElementValues(subAnno, superAnno); |
| } |
| Set<AnnotationMirror> supermap1 = this.supertypesTransitive.get(subAnno); |
| return AnnotationUtils.containsSame(supermap1, superAnno); |
| } |
| |
| /** |
| * Throw a {@link BugInCF} if {@code a} is not in the {@link #supertypesTransitive} or {@link |
| * #polyQualifiers}. |
| * |
| * @param a qualifier |
| */ |
| private final void checkAnnoInGraph(AnnotationMirror a) { |
| if (AnnotationUtils.containsSame(supertypesTransitive.keySet(), a) |
| || AnnotationUtils.containsSame(polyQualifiers.values(), a)) { |
| return; |
| } |
| |
| if (a == null) { |
| throw new BugInCF( |
| "MultiGraphQualifierHierarchy found an unqualified type. Please ensure that " |
| + "your defaulting rules cover all cases and/or " |
| + "use a @DefaultQualifierInHierarchy annotation. " |
| + "Also ensure that overrides of addComputedTypeAnnotations call super."); |
| } else { |
| // System.out.println("MultiGraphQH: " + this); |
| throw new BugInCF( |
| "MultiGraphQualifierHierarchy found the unrecognized qualifier: " |
| + a |
| + ". Please ensure that the qualifier is correctly included in the subtype" |
| + " hierarchy."); |
| } |
| } |
| |
| /** |
| * Infer the tops of the subtype hierarchy. Simply finds the qualifiers that have no supertypes. |
| */ |
| // Not static to allow adaptation in subclasses. Only parameters should be modified. |
| protected Set<AnnotationMirror> findTops( |
| Map<AnnotationMirror, Set<AnnotationMirror>> supertypes) { |
| Set<AnnotationMirror> possibleTops = AnnotationUtils.createAnnotationSet(); |
| for (AnnotationMirror anno : supertypes.keySet()) { |
| if (supertypes.get(anno).isEmpty()) { |
| possibleTops.add(anno); |
| } |
| } |
| return possibleTops; |
| } |
| |
| /** |
| * Infer the bottoms of the subtype hierarchy. Simple finds the qualifiers that are not supertypes |
| * of other qualifiers. |
| */ |
| // Not static to allow adaptation in subclasses. Only parameters should be modified. |
| protected Set<AnnotationMirror> findBottoms( |
| Map<AnnotationMirror, Set<AnnotationMirror>> supertypes) { |
| Set<AnnotationMirror> possibleBottoms = AnnotationUtils.createAnnotationSet(); |
| possibleBottoms.addAll(supertypes.keySet()); |
| for (Set<AnnotationMirror> supers : supertypes.values()) { |
| possibleBottoms.removeAll(supers); |
| } |
| return possibleBottoms; |
| } |
| |
| /** Computes the transitive closure of the given map and returns it. */ |
| /* The method gets all required parameters passed in and could be static. However, |
| * we want to allow subclasses to adapt the behavior and therefore make it an instance method. |
| */ |
| protected Map<AnnotationMirror, Set<AnnotationMirror>> transitiveClosure( |
| Map<AnnotationMirror, Set<AnnotationMirror>> supertypes) { |
| Map<AnnotationMirror, Set<AnnotationMirror>> result = AnnotationUtils.createAnnotationMap(); |
| for (AnnotationMirror anno : supertypes.keySet()) { |
| // this method directly modifies result and is |
| // ignoring the returned value |
| findAllSupers(anno, supertypes, result); |
| } |
| return result; |
| } |
| |
| /** |
| * Add the relationships for polymorphic qualifiers. |
| * |
| * <p>A polymorphic qualifier, such as {@code PolyNull}, needs to be: |
| * |
| * <ol> |
| * <li>a subtype of the top qualifier (e.g. {@code Nullable}) |
| * <li>a supertype of all the bottom qualifiers (e.g. {@code NonNull}) |
| * </ol> |
| * |
| * Field supertypesTransitive is not set yet when this method is called -- use parameter fullMap |
| * instead. |
| */ |
| // The method gets all required parameters passed in and could be static. However, |
| // we want to allow subclasses to adapt the behavior and therefore make it an instance method. |
| protected void addPolyRelations( |
| QualifierHierarchy qualHierarchy, |
| Map<AnnotationMirror, Set<AnnotationMirror>> fullMap, |
| Map<AnnotationMirror, AnnotationMirror> polyQualifiers, |
| Set<AnnotationMirror> tops, |
| Set<AnnotationMirror> bottoms) { |
| if (polyQualifiers.isEmpty()) { |
| return; |
| } |
| |
| // Handle the case where @PolymorphicQualifier uses the default value Annotation.class. |
| if (polyQualifiers.size() == 1 && tops.size() == 1) { |
| Map.Entry<AnnotationMirror, AnnotationMirror> entry = |
| polyQualifiers.entrySet().iterator().next(); |
| AnnotationMirror poly = entry.getKey(); |
| AnnotationMirror maybeTop = entry.getValue(); |
| if (AnnotationUtils.areSameByName(poly, maybeTop)) { |
| // If the value of @PolymorphicQualifier is the default value, Annotation.class, |
| // then map is set to polyQual -> polyQual in |
| // MultiGraphQualifierHierarchy.MultiGraphFactory.addQualifier, |
| // because the top is unknown there. |
| // Reset it to top here. |
| polyQualifiers.put(tops.iterator().next(), poly); |
| polyQualifiers.remove(poly); |
| } |
| } |
| |
| for (Map.Entry<AnnotationMirror, AnnotationMirror> kv : polyQualifiers.entrySet()) { |
| AnnotationMirror declTop = kv.getKey(); |
| AnnotationMirror polyQualifier = kv.getValue(); |
| // Ensure that it's really the top of the hierarchy |
| Set<AnnotationMirror> declSupers = fullMap.get(declTop); |
| AnnotationMirror polyTop = null; |
| if (declSupers.isEmpty()) { |
| polyTop = declTop; |
| } else { |
| for (AnnotationMirror ds : declSupers) { |
| if (AnnotationUtils.containsSameByName(tops, ds)) { |
| polyTop = ds; |
| } |
| } |
| } |
| boolean found = (polyTop != null); |
| if (found) { |
| AnnotationUtils.updateMappingToImmutableSet( |
| fullMap, polyQualifier, Collections.singleton(polyTop)); |
| } else if (AnnotationUtils.areSameByName(polyQualifier, declTop)) { |
| throw new BugInCF( |
| "MultiGraphQualifierHierarchy.addPolyRelations: " |
| + "incorrect or missing top qualifier given in polymorphic qualifier " |
| + polyQualifier |
| + "; possible top qualifiers: " |
| + tops); |
| } else { |
| throw new BugInCF( |
| "MultiGraphQualifierHierarchy.addPolyRelations: " |
| + "incorrect top qualifier given in polymorphic qualifier: " |
| + polyQualifier |
| + " could not find: " |
| + polyTop); |
| } |
| |
| found = false; |
| AnnotationMirror bottom = null; |
| outer: |
| for (AnnotationMirror btm : bottoms) { |
| for (AnnotationMirror btmsuper : fullMap.get(btm)) { |
| if (AnnotationUtils.areSameByName(btmsuper, polyTop)) { |
| found = true; |
| bottom = btm; |
| break outer; |
| } |
| } |
| } |
| if (found) { |
| AnnotationUtils.updateMappingToImmutableSet( |
| fullMap, bottom, Collections.singleton(polyQualifier)); |
| } else { |
| // TODO: in a type system with a single qualifier this check will fail. |
| // throw new BugInCF("MultiGraphQualifierHierarchy.addPolyRelations: |
| // " + |
| // "incorrect top qualifier given in polymorphic qualifier: " |
| // |
| // + polyQualifier + " could not find bottom for: " + polyTop); |
| } |
| } |
| } |
| |
| private Map<AnnotationPair, AnnotationMirror> calculateLubs() { |
| Map<AnnotationPair, AnnotationMirror> newlubs = new HashMap<>(); |
| for (AnnotationMirror a1 : typeQualifiers) { |
| for (AnnotationMirror a2 : typeQualifiers) { |
| if (AnnotationUtils.areSameByName(a1, a2)) { |
| continue; |
| } |
| if (!AnnotationUtils.areSame(getTopAnnotation(a1), getTopAnnotation(a2))) { |
| continue; |
| } |
| AnnotationPair pair = new AnnotationPair(a1, a2); |
| if (newlubs.containsKey(pair)) { |
| continue; |
| } |
| AnnotationMirror lub = findLub(a1, a2); |
| newlubs.put(pair, lub); |
| } |
| } |
| return newlubs; |
| } |
| |
| /** |
| * Finds and returns the Least Upper Bound (LUB) of two annotation mirrors a1 and a2 by |
| * recursively climbing the qualifier hierarchy of a1 until one of them is a subtype of the other, |
| * or returns null if no subtype relationships can be found. |
| * |
| * @param a1 first annotation mirror |
| * @param a2 second annotation mirror |
| * @return the LUB of a1 and a2, or null if none can be found |
| */ |
| protected AnnotationMirror findLub(AnnotationMirror a1, AnnotationMirror a2) { |
| if (isSubtype(a1, a2)) { |
| return a2; |
| } |
| if (isSubtype(a2, a1)) { |
| return a1; |
| } |
| |
| assert getTopAnnotation(a1) == getTopAnnotation(a2) |
| : "MultiGraphQualifierHierarchy.findLub: this method may only be called " |
| + "with qualifiers from the same hierarchy. Found a1: " |
| + a1 |
| + " [top: " |
| + getTopAnnotation(a1) |
| + "], a2: " |
| + a2 |
| + " [top: " |
| + getTopAnnotation(a2) |
| + "]"; |
| |
| if (isPolymorphicQualifier(a1)) { |
| return findLubWithPoly(a1, a2); |
| } else if (isPolymorphicQualifier(a2)) { |
| return findLubWithPoly(a2, a1); |
| } |
| |
| Set<AnnotationMirror> outset = AnnotationUtils.createAnnotationSet(); |
| for (AnnotationMirror a1Super : supertypesDirect.get(a1)) { |
| // TODO: we take the first of the smallest supertypes, maybe we would |
| // get a different LUB if we used a different one? |
| AnnotationMirror a1Lub = findLub(a1Super, a2); |
| if (a1Lub != null) { |
| outset.add(a1Lub); |
| } else { |
| throw new BugInCF( |
| "GraphQualifierHierarchy could not determine LUB for " |
| + a1 |
| + " and " |
| + a2 |
| + ". Please ensure that the checker knows about all type qualifiers."); |
| } |
| } |
| return requireSingleton(outset, a1, a2, /*lub=*/ true); |
| } |
| |
| private AnnotationMirror findLubWithPoly(AnnotationMirror poly, AnnotationMirror other) { |
| AnnotationMirror bottom = getBottomAnnotation(other); |
| if (AnnotationUtils.areSame(bottom, other)) { |
| return poly; |
| } |
| |
| return getTopAnnotation(poly); |
| } |
| |
| @Override |
| public boolean isPolymorphicQualifier(AnnotationMirror qual) { |
| return AnnotationUtils.containsSame(polyQualifiers.values(), qual); |
| } |
| |
| /** Remove all supertypes of elements contained in the set. */ |
| private Set<AnnotationMirror> findSmallestTypes(Set<AnnotationMirror> inset) { |
| Set<AnnotationMirror> outset = AnnotationUtils.createAnnotationSet(); |
| outset.addAll(inset); |
| |
| for (AnnotationMirror a1 : inset) { |
| outset.removeIf(a2 -> a1 != a2 && isSubtype(a1, a2)); |
| } |
| return outset; |
| } |
| |
| /** Finds all the super qualifiers for a qualifier. */ |
| private static Set<AnnotationMirror> findAllSupers( |
| AnnotationMirror anno, |
| Map<AnnotationMirror, Set<AnnotationMirror>> supertypes, |
| Map<AnnotationMirror, Set<AnnotationMirror>> allSupersSoFar) { |
| Set<AnnotationMirror> supers = AnnotationUtils.createAnnotationSet(); |
| for (AnnotationMirror superAnno : supertypes.get(anno)) { |
| // add the current super to the superset |
| supers.add(superAnno); |
| // add all of current super's super into superset |
| supers.addAll(findAllSupers(superAnno, supertypes, allSupersSoFar)); |
| } |
| allSupersSoFar.put(anno, Collections.unmodifiableSet(supers)); |
| return supers; |
| } |
| |
| /** Returns a map from each possible pair of annotations to their glb. */ |
| private Map<AnnotationPair, AnnotationMirror> calculateGlbs() { |
| Map<AnnotationPair, AnnotationMirror> newglbs = new HashMap<>(); |
| for (AnnotationMirror a1 : typeQualifiers) { |
| for (AnnotationMirror a2 : typeQualifiers) { |
| if (AnnotationUtils.areSameByName(a1, a2)) { |
| continue; |
| } |
| if (!AnnotationUtils.areSame(getTopAnnotation(a1), getTopAnnotation(a2))) { |
| continue; |
| } |
| AnnotationPair pair = new AnnotationPair(a1, a2); |
| if (newglbs.containsKey(pair)) { |
| continue; |
| } |
| AnnotationMirror glb = findGlb(a1, a2); |
| newglbs.put(pair, glb); |
| } |
| } |
| return newglbs; |
| } |
| |
| private AnnotationMirror findGlb(AnnotationMirror a1, AnnotationMirror a2) { |
| if (isSubtype(a1, a2)) { |
| return a1; |
| } |
| if (isSubtype(a2, a1)) { |
| return a2; |
| } |
| |
| assert getTopAnnotation(a1) == getTopAnnotation(a2) |
| : "MultiGraphQualifierHierarchy.findGlb: this method may only be called " |
| + "with qualifiers from the same hierarchy. Found a1: " |
| + a1 |
| + " [top: " |
| + getTopAnnotation(a1) |
| + "], a2: " |
| + a2 |
| + " [top: " |
| + getTopAnnotation(a2) |
| + "]"; |
| |
| if (isPolymorphicQualifier(a1)) { |
| return findGlbWithPoly(a1, a2); |
| } else if (isPolymorphicQualifier(a2)) { |
| return findGlbWithPoly(a2, a1); |
| } |
| |
| Set<AnnotationMirror> outset = AnnotationUtils.createAnnotationSet(); |
| for (AnnotationMirror a1Sub : supertypesDirect.keySet()) { |
| if (isSubtype(a1Sub, a1) && !a1Sub.equals(a1)) { |
| AnnotationMirror a1lb = findGlb(a1Sub, a2); |
| if (a1lb != null) { |
| outset.add(a1lb); |
| } |
| } |
| } |
| return requireSingleton(outset, a1, a2, /*lub=*/ false); |
| } |
| |
| private AnnotationMirror findGlbWithPoly(AnnotationMirror poly, AnnotationMirror other) { |
| AnnotationMirror top = getTopAnnotation(other); |
| if (AnnotationUtils.areSame(top, other)) { |
| return poly; |
| } |
| |
| return getBottomAnnotation(poly); |
| } |
| |
| /** Remove all subtypes of elements contained in the set. */ |
| private Set<AnnotationMirror> findGreatestTypes(Set<AnnotationMirror> inset) { |
| Set<AnnotationMirror> outset = AnnotationUtils.createAnnotationSet(); |
| outset.addAll(inset); |
| |
| for (AnnotationMirror a1 : inset) { |
| Iterator<AnnotationMirror> outit = outset.iterator(); |
| while (outit.hasNext()) { |
| AnnotationMirror a2 = outit.next(); |
| if (a1 != a2 && isSubtype(a2, a1)) { |
| outit.remove(); |
| } |
| } |
| } |
| return outset; |
| } |
| |
| /** |
| * Require that outset is a singleton set, after polymorphic qualifiers have been removed. If not, |
| * report a bug: the type hierarchy is not a lattice. |
| * |
| * @param outset the set of upper or lower bounds of a1 and a2 (depending on whether lub==true) |
| * @param a1 the first annotation being lubbed or glbed |
| * @param a2 the second annotation being lubbed or glbed |
| * @param lub true if computing lub(a1, a2), false if computing glb(a1, a2) |
| * @return the unique element of outset; issues an error if outset.size() != 1 |
| */ |
| private AnnotationMirror requireSingleton( |
| Set<AnnotationMirror> outset, AnnotationMirror a1, AnnotationMirror a2, boolean lub) { |
| if (outset.size() == 0) { |
| throw new BugInCF( |
| "MultiGraphQualifierHierarchy could not determine " |
| + (lub ? "LUB" : "GLB") |
| + " for " |
| + a1 |
| + " and " |
| + a2 |
| + ". Please ensure that the checker knows about all type qualifiers."); |
| } else if (outset.size() == 1) { |
| return outset.iterator().next(); |
| } else { |
| // outset.size() > 1 |
| |
| outset = lub ? findSmallestTypes(outset) : findGreatestTypes(outset); |
| |
| AnnotationMirror result = null; |
| for (AnnotationMirror anno : outset) { |
| if (isPolymorphicQualifier(anno)) { |
| continue; |
| } else if (result == null) { |
| result = anno; |
| } else { |
| throw new BugInCF( |
| "Bug in checker implementation: type hierarchy is not a lattice.%n" |
| + "There is no unique " |
| + (lub ? "lub" : "glb") |
| + "(%s, %s).%n" |
| + "Two incompatible candidates are: %s %s", |
| a1, |
| a2, |
| result, |
| anno); |
| } |
| } |
| return result; |
| } |
| } |
| |
| /** Two annotations; used for caching the result of calls to lub and glb. */ |
| private static class AnnotationPair { |
| /** The first annotation. */ |
| public final AnnotationMirror a1; |
| /** The second annotation. */ |
| public final AnnotationMirror a2; |
| /** The cached hashCode of this; -1 until computed. */ |
| private int hashCode = -1; |
| |
| /** Create a new AnnotationPair. */ |
| public AnnotationPair(AnnotationMirror a1, AnnotationMirror a2) { |
| this.a1 = a1; |
| this.a2 = a2; |
| } |
| |
| @Pure |
| @Override |
| public int hashCode() { |
| if (hashCode == -1) { |
| hashCode = |
| Objects.hash(AnnotationUtils.annotationName(a1), AnnotationUtils.annotationName(a2)); |
| } |
| return hashCode; |
| } |
| |
| @Override |
| public boolean equals(@Nullable Object o) { |
| if (!(o instanceof AnnotationPair)) { |
| return false; |
| } |
| AnnotationPair other = (AnnotationPair) o; |
| if (AnnotationUtils.areSameByName(a1, other.a1) |
| && AnnotationUtils.areSameByName(a2, other.a2)) { |
| return true; |
| } |
| if (AnnotationUtils.areSameByName(a2, other.a1) |
| && AnnotationUtils.areSameByName(a1, other.a2)) { |
| return true; |
| } |
| return false; |
| } |
| |
| @SideEffectFree |
| @Override |
| public String toString() { |
| return "AnnotationPair(" + a1 + ", " + a2 + ")"; |
| } |
| } |
| } |