blob: 6722cef61dd7f90cafb9a4e658a54b266113b5fc [file] [log] [blame]
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 + ")";
}
}
}