org.checkerframework.framework.type.QualifierHierarchy Maven / Gradle / Ivy
Show all versions of checker Show documentation
package org.checkerframework.framework.type;
import org.checkerframework.checker.mustcall.qual.MustCallUnknown;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.framework.qual.AnnotatedFor;
import org.checkerframework.javacutil.AnnotationMirrorSet;
import org.checkerframework.javacutil.BugInCF;
import org.plumelib.util.StringsPlume;
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 javax.lang.model.type.TypeMirror;
/**
* Represents multiple type qualifier hierarchies. {@link #getWidth} gives the number of hierarchies
* that this object represents. Each hierarchy has its own top and bottom, and subtyping
* relationships exist only within each hierarchy.
*
* Note the distinction in terminology between a qualifier hierarchy, which has one top and one
* bottom, and a {@code QualifierHierarchy}, which represents multiple qualifier hierarchies.
*
*
All type annotations need to be type qualifiers recognized within this hierarchy.
*
*
This assumes that every annotated type in a program is annotated with exactly one qualifier
* from each hierarchy.
*/
@AnnotatedFor("nullness")
public abstract class QualifierHierarchy {
/** The associated type factory. This is used only for checking whether types are relevant. */
protected GenericAnnotatedTypeFactory atypeFactory;
/**
* Creates a new QualifierHierarchy.
*
* @param atypeFactory the associated type factory
*/
public QualifierHierarchy(GenericAnnotatedTypeFactory atypeFactory) {
this.atypeFactory = atypeFactory;
}
/**
* Determine whether this QualifierHierarchy is valid.
*
* @return true if this QualifierHierarchy is valid
*/
public boolean isValid() {
return true;
}
// **********************************************************************
// Getter methods about this hierarchy
// **********************************************************************
/**
* Returns the width of this hierarchy, i.e. the expected number of annotations on any valid
* type.
*
* @return the width of this QualifierHierarchy
*/
public int getWidth() {
return getTopAnnotations().size();
}
/**
* Returns the top (ultimate super) type qualifiers in the type system. The size of this set is
* equal to {@link #getWidth}.
*
* @return the top (ultimate super) type qualifiers in the type system
*/
public abstract AnnotationMirrorSet getTopAnnotations();
/**
* Return the top qualifier for the given qualifier, that is, the qualifier that is a supertype
* of {@code qualifier} but no further supertypes exist.
*
* @param qualifier any qualifier from one of the qualifier hierarchies represented by this
* @return the top qualifier of {@code qualifier}'s hierarchy
*/
public abstract AnnotationMirror getTopAnnotation(AnnotationMirror qualifier);
/**
* Returns the bottom type qualifiers in the hierarchy. The size of this set is equal to {@link
* #getWidth}.
*
* @return the bottom type qualifiers in the hierarchy
*/
public abstract AnnotationMirrorSet getBottomAnnotations();
/**
* Return the bottom for the given qualifier, that is, the qualifier that is a subtype of {@code
* qualifier} but no further subtypes exist.
*
* @param qualifier any qualifier from one of the qualifier hierarchies represented by this
* @return the bottom qualifier of {@code qualifier}'s hierarchy
*/
public abstract AnnotationMirror getBottomAnnotation(AnnotationMirror qualifier);
/**
* Returns the polymorphic qualifier for the hierarchy containing {@code qualifier}, or {@code
* null} if there is no polymorphic qualifier in that hierarchy.
*
* @param qualifier any qualifier from one of the qualifier hierarchies represented by this
* @return the polymorphic qualifier for the hierarchy containing {@code qualifier}, or {@code
* null} if there is no polymorphic qualifier in that hierarchy
*/
public abstract @Nullable AnnotationMirror getPolymorphicAnnotation(AnnotationMirror qualifier);
/**
* Returns {@code true} if the qualifier is a polymorphic qualifier; otherwise, returns {@code
* false}.
*
* @param qualifier qualifier
* @return {@code true} if the qualifier is a polymorphic qualifier; otherwise, returns {@code
* false}.
*/
public abstract boolean isPolymorphicQualifier(AnnotationMirror qualifier);
/**
* Returns the parametric qualifier for the hierarchy containing {@code qualifier}, or {@code
* null} if there is no parametric qualifier in that hierarchy.
*
* @param qualifier any qualifier from one of the qualifier hierarchies represented by this
* @return the parametric qualifier for the hierarchy containing {@code qualifier}, or {@code
* null} if there is no polymorphic qualifier in that hierarchy
*/
public @Nullable AnnotationMirror getParametricQualifier(AnnotationMirror qualifier) {
return null;
}
/**
* Returns {@code true} if the qualifier is a parametric qualifier; otherwise, returns {@code
* false}.
*
* @param qualifier qualifier
* @return {@code true} if the qualifier is a parametric qualifier; otherwise, returns {@code
* false}.
*/
public boolean isParametricQualifier(AnnotationMirror qualifier) {
return false;
}
// **********************************************************************
// Qualifier Hierarchy Queries
// **********************************************************************
/**
* Tests whether {@code subQualifier} is equal to or a sub-qualifier of {@code superQualifier},
* according to the type qualifier hierarchy, ignoring Java basetypes.
*
*
Clients should generally call {@link #isSubtypeShallow}. However, subtypes should
* generally override this method (if needed).
*
*
This method behaves the same as {@link #isSubtypeQualifiersOnly(AnnotationMirror,
* AnnotationMirror)}, which calls this method. This method is for clients inside the framework,
* and it has {@code protected} access to prevent use by clients outside the framework. This
* makes it easy to find places where code outside the framework is ignoring Java basetypes --
* at calls to {@link #isSubtypeQualifiersOnly}.
*
* @param subQualifier possible subqualifier
* @param superQualifier possible superqualifier
* @return true iff {@code subQualifier} is a subqualifier of, or equal to, {@code
* superQualifier}
*/
protected abstract boolean isSubtypeQualifiers(
AnnotationMirror subQualifier, AnnotationMirror superQualifier);
/**
* Tests whether {@code subQualifier} is equal to or a sub-qualifier of {@code superQualifier},
* according to the type qualifier hierarchy, ignoring Java basetypes.
*
*
This method is for clients outside the framework, and should not be used by framework
* code.
*
* @param subQualifier possible subqualifier
* @param superQualifier possible superqualifier
* @return true iff {@code subQualifier} is a subqualifier of, or equal to, {@code
* superQualifier}
*/
public final boolean isSubtypeQualifiersOnly(
AnnotationMirror subQualifier, AnnotationMirror superQualifier) {
return isSubtypeQualifiers(subQualifier, superQualifier);
}
/**
* Tests whether {@code subQualifier} is equal to or a sub-qualifier of {@code superQualifier},
* according to the type qualifier hierarchy. The types {@code subType} and {@code superType}
* are not necessarily in a Java subtyping relationship with one another and are only used by
* this method for special cases when qualifier subtyping depends on the Java basetype.
*
*
Clients should usually call {@code isSubtypeShallow()} (this method). Rarely, to ignore
* the Java basetype, a client can call {@link #isSubtypeQualifiersOnly}.
*
*
Subtypes should override {@link #isSubtypeQualifiers} (not this method), unless qualifier
* subtyping depends on Java basetypes.
*
* @param subQualifier possible subqualifier
* @param subType the Java basetype associated with {@code subQualifier}
* @param superQualifier possible superqualifier
* @param superType the Java basetype associated with {@code superQualifier}
* @return true iff {@code subQualifier} is a subqualifier of, or equal to, {@code
* superQualifier}
*/
@SuppressWarnings({"nullness", "keyfor"}) // AnnotatedTypeFactory hasn't been annotated.
public boolean isSubtypeShallow(
AnnotationMirror subQualifier,
TypeMirror subType,
AnnotationMirror superQualifier,
TypeMirror superType) {
if (!atypeFactory.isRelevant(subType) || !atypeFactory.isRelevant(superType)) {
// At least one of the types is not relevant.
return true;
}
return isSubtypeQualifiers(subQualifier, superQualifier);
}
/**
* Tests whether {@code subQualifier} is equal to or a sub-qualifier of {@code superQualifier},
* according to the type qualifier hierarchy. The type {@code typeMirror} is only used by this
* method for special cases when qualifier subtyping depends on the Java basetype.
*
*
Clients should usually call {@link #isSubtypeShallow(AnnotationMirror, AnnotationMirror,
* TypeMirror)} (this method) or {@link #isSubtypeShallow(AnnotationMirror, TypeMirror,
* AnnotationMirror, TypeMirror)}. Rarely, to ignore the Java basetype, a client can call {@link
* #isSubtypeQualifiersOnly}.
*
*
Subtypes should override {@link #isSubtypeQualifiers} (not this method), unless qualifier
* subtyping depends on Java basetypes.
*
* @param subQualifier possible subqualifier
* @param superQualifier possible superqualifier
* @param typeMirror the Java basetype associated with both {@code subQualifier} and {@code
* superQualifier}
* @return true iff {@code subQualifier} is a subqualifier of, or equal to, {@code
* superQualifier}
*/
public final boolean isSubtypeShallow(
AnnotationMirror subQualifier, AnnotationMirror superQualifier, TypeMirror typeMirror) {
return isSubtypeShallow(subQualifier, typeMirror, superQualifier, typeMirror);
}
/**
* Tests whether all qualifiers in {@code subQualifiers} are a subqualifier or equal to the
* qualifier in the same hierarchy in {@code superQualifiers}. The types {@code subType} and
* {@code superType} are not necessarily in a Java subtyping relationship with one another and
* are only used by this method for special cases when qualifier subtyping depends on the Java
* basetype.
*
*
Subtypes more often override {@link #isSubtypeShallow(AnnotationMirror, TypeMirror,
* AnnotationMirror, TypeMirror)} than this method.
*
* @param subQualifiers set of qualifiers; exactly one per hierarchy
* @param subType the type associated with {@code subQualifiers}
* @param superQualifiers set of qualifiers; exactly one per hierarchy
* @param superType the type associated with {@code superQualifiers}
* @return true iff all qualifiers in {@code subQualifiers} are a subqualifier or equal to the
* qualifier in the same hierarchy in {@code superQualifiers}
*/
public final boolean isSubtypeShallow(
Collection subQualifiers,
TypeMirror subType,
Collection superQualifiers,
TypeMirror superType) {
assertSameSize(subQualifiers, superQualifiers);
for (AnnotationMirror subQual : subQualifiers) {
AnnotationMirror superQual = findAnnotationInSameHierarchy(superQualifiers, subQual);
if (superQual == null) {
throw new BugInCF(
"QualifierHierarchy: missing annotation in hierarchy %s. found: %s",
subQual, StringsPlume.join(",", superQualifiers));
}
if (!isSubtypeShallow(subQual, subType, superQual, superType)) {
return false;
}
}
return true;
}
/**
* Tests whether all qualifiers in {@code subQualifiers} are a subqualifier of or equal to the
* qualifier in the same hierarchy in {@code superQualifiers}. The type {@code typeMirror} is
* only used by this method for special cases when qualifier subtyping depends on the Java
* basetype.
*
*
Subtypes more often override {@link #isSubtypeShallow(AnnotationMirror, TypeMirror,
* AnnotationMirror, TypeMirror)} than this method.
*
* @param subQualifiers a set of qualifiers; exactly one per hierarchy
* @param superQualifiers a set of qualifiers; exactly one per hierarchy
* @param typeMirror the type associated with both sets of qualifiers
* @return true iff all qualifiers in {@code subQualifiers} are a subqualifier or equal to the
* qualifier in the same hierarchy in {@code superQualifiers}
*/
public final boolean isSubtypeShallow(
Collection subQualifiers,
Collection superQualifiers,
TypeMirror typeMirror) {
return isSubtypeShallow(subQualifiers, typeMirror, superQualifiers, typeMirror);
}
/**
* Returns the least upper bound (LUB) of the qualifiers {@code qualifier1} and {@code
* qualifier2}. Returns {@code null} if the qualifiers are not from the same qualifier
* hierarchy. Ignores Java basetypes.
*
*
Examples:
*
*
* - For NonNull, leastUpperBound('Nullable', 'NonNull') ⇒ Nullable
*
*
* @param qualifier1 the first qualifier; may not be in the same hierarchy as {@code qualifier2}
* @param qualifier2 the second qualifier; may not be in the same hierarchy as {@code
* qualifier1}
* @return the least upper bound of the qualifiers, or {@code null} if the qualifiers are from
* different hierarchies
*/
// The fact that null is returned if the qualifiers are not in the same hierarchy is used by the
// collection version of LUB below.
protected abstract @Nullable AnnotationMirror leastUpperBoundQualifiers(
AnnotationMirror qualifier1, AnnotationMirror qualifier2);
/**
* Returns the least upper bound (LUB) of the qualifiers {@code qualifier1} and {@code
* qualifier2}. Returns {@code null} if the qualifiers are not from the same qualifier
* hierarchy. Ignores Java basetypes.
*
* Examples:
*
*
* - For NonNull, leastUpperBound('Nullable', 'NonNull') ⇒ Nullable
*
*
* @param qualifier1 the first qualifier; may not be in the same hierarchy as {@code qualifier2}
* @param qualifier2 the second qualifier; may not be in the same hierarchy as {@code
* qualifier1}
* @return the least upper bound of the qualifiers, or {@code null} if the qualifiers are from
* different hierarchies
*/
// The fact that null is returned if the qualifiers are not in the same hierarchy is used by the
// collection version of LUB below.
public final @Nullable AnnotationMirror leastUpperBoundQualifiersOnly(
AnnotationMirror qualifier1, AnnotationMirror qualifier2) {
return leastUpperBoundQualifiers(qualifier1, qualifier2);
}
/**
* Returns the least upper bound (LUB) of the qualifiers {@code qualifier1} and {@code
* qualifier2}. Returns {@code null} if the qualifiers are not from the same qualifier
* hierarchy.
*
* Examples:
*
*
* - leastUpperBound('Nullable', 'NonNull') ⇒ Nullable
*
*
* @param qualifier1 the first qualifier; may not be in the same hierarchy as {@code qualifier2}
* @param tm1 the type on which qualifier1 appears
* @param qualifier2 the second qualifier; may not be in the same hierarchy as {@code
* qualifier1}
* @param tm2 the type on which qualifier2 appears
* @return the least upper bound of the qualifiers, or {@code null} if the qualifiers are from
* different hierarchies
*/
// The fact that null is returned if the qualifiers are not in the same hierarchy is used by the
// collection version of LUB below.
@SuppressWarnings({"nullness", "keyfor"}) // AnnotatedTypeFactory hasn't been annotated.
public @Nullable AnnotationMirror leastUpperBoundShallow(
AnnotationMirror qualifier1,
TypeMirror tm1,
AnnotationMirror qualifier2,
TypeMirror tm2) {
boolean tm1IsRelevant = atypeFactory.isRelevant(tm1);
boolean tm2IsRelevant = atypeFactory.isRelevant(tm2);
if (tm1IsRelevant == tm2IsRelevant) {
return leastUpperBoundQualifiers(qualifier1, qualifier2);
} else if (tm1IsRelevant) {
return qualifier1;
} else { // if (tm2IsRelevant) {
return qualifier2;
}
}
/**
* Returns the least upper bound of the two sets of qualifiers. The result is the lub of the
* qualifier for the same hierarchy in each set.
*
* @param qualifiers1 set of qualifiers; exactly one per hierarchy
* @param tm1 the type on which qualifiers1 appear
* @param qualifiers2 set of qualifiers; exactly one per hierarchy
* @param tm2 the type on which qualifiers2 appear
* @return the least upper bound of the two sets of qualifiers
*/
public final Set leastUpperBoundsShallow(
Collection qualifiers1,
TypeMirror tm1,
Collection qualifiers2,
TypeMirror tm2) {
assertSameSize(qualifiers1, qualifiers2);
if (qualifiers1.isEmpty()) {
throw new BugInCF(
"QualifierHierarchy.leastUpperBounds: tried to determine LUB with empty sets");
}
AnnotationMirrorSet result = new AnnotationMirrorSet();
for (AnnotationMirror a1 : qualifiers1) {
for (AnnotationMirror a2 : qualifiers2) {
AnnotationMirror lub = leastUpperBoundShallow(a1, tm1, a2, tm2);
if (lub != null) {
result.add(lub);
}
}
}
assertSameSize(result, qualifiers1);
return result;
}
/**
* Returns the number of iterations dataflow should perform before {@link
* #widenedUpperBound(AnnotationMirror, AnnotationMirror)} is called or -1 if it should never be
* called.
*
* @return the number of iterations dataflow should perform before {@link
* #widenedUpperBound(AnnotationMirror, AnnotationMirror)} is called or -1 if it should
* never be called.
*/
public int numberOfIterationsBeforeWidening() {
return -1;
}
/**
* If the qualifier hierarchy has an infinite ascending chain, then the dataflow analysis might
* never reach a fixed point. To prevent this, implement this method such that it returns an
* upper bound for the two qualifiers that is a strict super type of the least upper bound. If
* this method is implemented, also override {@link #numberOfIterationsBeforeWidening()} to
* return a positive number.
*
* {@code newQualifier} is newest qualifier dataflow computed for some expression and {@code
* previousQualifier} is the qualifier dataflow computed on the last iteration.
*
*
If the qualifier hierarchy has no infinite ascending chain, returns the least upper bound
* of the two annotations.
*
* @param newQualifier new qualifier dataflow computed for some expression; must be in the same
* hierarchy as {@code previousQualifier}
* @param previousQualifier the previous qualifier dataflow computed on the last iteration; must
* be in the same hierarchy as {@code previousQualifier}
* @return an upper bound that is higher than the least upper bound of newQualifier and
* previousQualifier (or the lub if the qualifier hierarchy does not require this)
*/
public AnnotationMirror widenedUpperBound(
AnnotationMirror newQualifier, AnnotationMirror previousQualifier) {
AnnotationMirror widenedUpperBound =
leastUpperBoundQualifiers(newQualifier, previousQualifier);
if (widenedUpperBound == null) {
throw new BugInCF(
"widenedUpperBound(%s, %s): unrelated qualifiers",
newQualifier, previousQualifier);
}
return widenedUpperBound;
}
/**
* Returns the greatest lower bound for the qualifiers qualifier1 and qualifier2. Returns null
* if the qualifiers are not from the same qualifier hierarchy.
*
* @param qualifier1 first qualifier
* @param qualifier2 second qualifier
* @return greatest lower bound of the two annotations, or null if the two annotations are not
* from the same hierarchy
*/
// The fact that null is returned if the qualifiers are not in the same hierarchy is used by the
// collection version of LUB below.
protected abstract @Nullable AnnotationMirror greatestLowerBoundQualifiers(
AnnotationMirror qualifier1, AnnotationMirror qualifier2);
/**
* Returns the greatest lower bound for the qualifiers qualifier1 and qualifier2. Returns null
* if the qualifiers are not from the same qualifier hierarchy.
*
* @param qualifier1 first qualifier
* @param qualifier2 second qualifier
* @return greatest lower bound of the two annotations, or null if the two annotations are not
* from the same hierarchy
*/
// The fact that null is returned if the qualifiers are not in the same hierarchy is used by the
// collection version of LUB below.
public final @Nullable AnnotationMirror greatestLowerBoundQualifiersOnly(
AnnotationMirror qualifier1, AnnotationMirror qualifier2) {
return greatestLowerBoundQualifiers(qualifier1, qualifier2);
}
/**
* Returns the greatest lower bound for the qualifiers qualifier1 and qualifier2. Returns null
* if the qualifiers are not from the same qualifier hierarchy.
*
* @param qualifier1 first qualifier
* @param tm1 the type that is annotated by qualifier1
* @param qualifier2 second qualifier
* @param tm2 the type that is annotated by qualifier2
* @return greatest lower bound of the two annotations, or null if the two annotations are not
* from the same hierarchy
*/
@SuppressWarnings({"nullness", "keyfor"}) // AnnotatedTypeFactory hasn't been annotated.
public @Nullable AnnotationMirror greatestLowerBoundShallow(
AnnotationMirror qualifier1,
TypeMirror tm1,
AnnotationMirror qualifier2,
TypeMirror tm2) {
boolean tm1IsRelevant = atypeFactory.isRelevant(tm1);
boolean tm2IsRelevant = atypeFactory.isRelevant(tm2);
if (tm1IsRelevant == tm2IsRelevant) {
return greatestLowerBoundQualifiers(qualifier1, qualifier2);
} else if (tm1IsRelevant) {
return qualifier1;
} else { // if (tm2IsRelevant) {
return qualifier2;
}
}
/**
* Returns the greatest lower bound of the two sets of qualifiers. The result is the lub of the
* qualifier for the same hierarchy in each set.
*
* @param qualifiers1 set of qualifiers; exactly one per hierarchy
* @param tm1 the type that is annotated by qualifier1
* @param qualifiers2 set of qualifiers; exactly one per hierarchy
* @param tm2 the type that is annotated by qualifier2
* @return the greatest lower bound of the two sets of qualifiers
*/
public final Set greatestLowerBoundsShallow(
Collection qualifiers1,
TypeMirror tm1,
Collection qualifiers2,
TypeMirror tm2) {
assertSameSize(qualifiers1, qualifiers2);
if (qualifiers1.isEmpty()) {
throw new BugInCF(
"QualifierHierarchy.greatestLowerBounds: tried to determine GLB with empty"
+ " sets");
}
AnnotationMirrorSet result = new AnnotationMirrorSet();
for (AnnotationMirror a1 : qualifiers1) {
for (AnnotationMirror a2 : qualifiers2) {
AnnotationMirror glb = greatestLowerBoundShallow(a1, tm1, a2, tm2);
if (glb != null) {
result.add(glb);
}
}
}
assertSameSize(qualifiers1, qualifiers2, result);
return result;
}
/**
* Returns true if and only if {@link AnnotatedTypeMirror#getAnnotations()} can return a set
* with fewer qualifiers than the width of the QualifierHierarchy.
*
* @param type the type to test
* @return true if and only if {@link AnnotatedTypeMirror#getAnnotations()} can return a set
* with fewer qualifiers than the width of the QualifierHierarchy
*/
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;
}
/**
* Returns the annotation in {@code qualifiers} that is in the same hierarchy as {@code
* qualifier}.
*
*
The default implementation calls {@link #getTopAnnotation(AnnotationMirror)} and then
* calls {@link #findAnnotationInHierarchy(Collection, AnnotationMirror)}. So, if {@code
* qualifier} is a top qualifier, then call {@link #findAnnotationInHierarchy(Collection,
* AnnotationMirror)} directly is faster.
*
* @param qualifiers set of annotations to search
* @param qualifier annotation that is in the same hierarchy as the returned annotation
* @return annotation in the same hierarchy as qualifier, or null if one is not found
*/
public @Nullable AnnotationMirror findAnnotationInSameHierarchy(
Collection qualifiers, AnnotationMirror qualifier) {
AnnotationMirror top = this.getTopAnnotation(qualifier);
return findAnnotationInHierarchy(qualifiers, top);
}
/**
* Returns the annotation in {@code qualifiers} that is in the hierarchy for which {@code top}
* is top.
*
* @param qualifiers set of annotations to search
* @param top the top annotation in the hierarchy to which the returned annotation belongs
* @return annotation in the same hierarchy as annotationMirror, or null if one is not found
*/
public @Nullable AnnotationMirror findAnnotationInHierarchy(
Collection qualifiers, AnnotationMirror top) {
for (AnnotationMirror anno : qualifiers) {
if (isSubtypeQualifiers(anno, top)) {
return anno;
}
}
return null;
}
/**
* Update a mapping from {@code key} to a set of AnnotationMirrors. If {@code key} is not
* already in the map, then put it in the map with a value of a new set containing {@code
* qualifier}. If the map contains {@code key}, then add {@code qualifier} to the set to which
* {@code key} maps. If that set contains a qualifier in the same hierarchy as {@code
* qualifier}, then don't add it and return false.
*
* @param map the mapping to modify
* @param key the key to update or add
* @param qualifier the value to update or add
* @param type of the map's keys
* @return true if the update was done; false if there was a qualifier hierarchy collision
*/
public boolean updateMappingToMutableSet(
Map map, T key, AnnotationMirror qualifier) {
// https://github.com/typetools/checker-framework/issues/2000
@SuppressWarnings("nullness:argument.type.incompatible")
boolean mapContainsKey = map.containsKey(key);
if (mapContainsKey) {
@SuppressWarnings("nullness:assignment.type.incompatible") // key is a key for map.
@NonNull AnnotationMirrorSet prevs = map.get(key);
AnnotationMirror old = findAnnotationInSameHierarchy(prevs, qualifier);
if (old != null) {
return false;
}
prevs.add(qualifier);
map.put(key, prevs);
} else {
AnnotationMirrorSet set = new AnnotationMirrorSet();
set.add(qualifier);
map.put(key, set);
}
return true;
}
/**
* Throws an exception if the given collections do not have the same size.
*
* @param c1 the first collection
* @param c2 the second collection
*/
public static void assertSameSize(Collection c1, Collection c2) {
if (c1.size() != c2.size()) {
throw new BugInCF(
"inconsistent sizes (%d, %d):%n [%s]%n [%s]",
c1.size(), c2.size(), StringsPlume.join(",", c1), StringsPlume.join(",", c2));
}
}
/**
* Throws an exception if the result and the inputs do not all have the same size.
*
* @param c1 the first collection
* @param c2 the second collection
* @param result the result collection
*/
public static void assertSameSize(
@MustCallUnknown Collection c1,
@MustCallUnknown Collection c2,
@MustCallUnknown Collection result) {
if (c1.size() != result.size() || c2.size() != result.size()) {
throw new BugInCF(
"inconsistent sizes (%d, %d, %d):%n %s%n %s%n %s",
c1.size(),
c2.size(),
result.size(),
StringsPlume.join(",", c1),
StringsPlume.join(",", c2),
StringsPlume.join(",", result));
}
}
}