org.checkerframework.framework.type.AnnotatedTypeMirror Maven / Gradle / Ivy
Show all versions of checker Show documentation
package org.checkerframework.framework.type;
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import org.checkerframework.checker.formatter.qual.FormatMethod;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.dataflow.qual.Pure;
import org.checkerframework.dataflow.qual.SideEffectFree;
import org.checkerframework.framework.type.visitor.AnnotatedTypeVisitor;
import org.checkerframework.framework.util.AnnotatedTypes;
import org.checkerframework.javacutil.AnnotationBuilder;
import org.checkerframework.javacutil.AnnotationMirrorSet;
import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.BugInCF;
import org.checkerframework.javacutil.ElementUtils;
import org.checkerframework.javacutil.TypeKindUtils;
import org.plumelib.util.CollectionsPlume;
import org.plumelib.util.DeepCopyable;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.IntersectionType;
import javax.lang.model.type.NoType;
import javax.lang.model.type.NullType;
import javax.lang.model.type.PrimitiveType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.type.UnionType;
import javax.lang.model.type.WildcardType;
import javax.lang.model.util.Types;
/**
* Represents an annotated type in the Java programming language, including:
*
*
* - {@link AnnotatedPrimitiveType primitive types},
*
- {@link AnnotatedDeclaredType declared types} (class and interface types),
*
- {@link AnnotatedArrayType array types},
*
- {@link AnnotatedTypeVariable type variables},
*
- {@link AnnotatedWildcardType wildcard type arguments},
*
- {@link AnnotatedExecutableType executable types} (their signature and return types),
*
- {@link AnnotatedIntersectionType intersection types},
*
- {@link AnnotatedUnionType union types},
*
- {@link AnnotatedNullType the null type}, and
*
- {@link AnnotatedNoType pseudo-types} corresponding to packages and to the keyword {@code
* void}.
*
*
* To implement operations based on the class of an {@code AnnotatedTypeMirror} object, either
* use a {@link AnnotatedTypeVisitor visitor} or use the result of the {@link #getKind()} method.
*
*
This class is mutable. Use methods {@link #shallowCopy()}, {@link #deepCopy()}, {@link
* #shallowCopy(boolean)}, or {@link #deepCopy(boolean)} to get a shallow or deep copy of an
* annotated type.
*
* @see TypeMirror
*/
public abstract class AnnotatedTypeMirror implements DeepCopyable {
/** An EqualityAtmComparer. */
protected static final EqualityAtmComparer EQUALITY_COMPARER = new EqualityAtmComparer();
/** A HashcodeAtmVisitor. */
protected static final HashcodeAtmVisitor HASHCODE_VISITOR = new HashcodeAtmVisitor();
/** The factory to use for lazily creating annotated types. */
protected final AnnotatedTypeFactory atypeFactory;
/** The actual type wrapped by this AnnotatedTypeMirror. */
protected final TypeMirror underlyingType;
/**
* Saves the result of {@code underlyingType.toString().hashCode()} to use when computing the
* hash code of this. (Because AnnotatedTypeMirrors are mutable, the hash code for this cannot
* be saved.) Call {@link #getUnderlyingTypeHashCode()} rather than using the field directly.
*/
private int underlyingTypeHashCode = -1;
/** The annotations on this type. */
// AnnotationMirror doesn't override Object.hashCode, .equals, so we use
// the class name of Annotation instead.
// Caution: Assumes that a type can have at most one AnnotationMirror for any Annotation type.
protected final AnnotationMirrorSet primaryAnnotations = new AnnotationMirrorSet();
// /** The explicitly written annotations on this type. */
// TODO: use this to cache the result once computed? For generic types?
// protected final AnnotationMirrorSet explicitannotations =
// new AnnotationMirrorSet();
/**
* Constructor for AnnotatedTypeMirror.
*
* @param underlyingType the underlying type
* @param atypeFactory used to create further types and to access global information (Types,
* Elements, ...)
*/
private AnnotatedTypeMirror(TypeMirror underlyingType, AnnotatedTypeFactory atypeFactory) {
this.underlyingType = underlyingType;
assert atypeFactory != null;
this.atypeFactory = atypeFactory;
}
/// This class doesn't customize the clone() method; use deepCopy() instead.
// @Override
// public AnnotatedTypeMirror clone() { ... }
/**
* Creates an AnnotatedTypeMirror for the provided type. The result contains no annotations.
*
* @param type the underlying type for the resulting AnnotatedTypeMirror
* @param atypeFactory the type factory that will build the result
* @param isDeclaration true if the result should represent a declaration, rather than a use, of
* a type
* @return an AnnotatedTypeMirror whose underlying type is {@code type}
*/
public static AnnotatedTypeMirror createType(
TypeMirror type, AnnotatedTypeFactory atypeFactory, boolean isDeclaration) {
if (type == null) {
throw new BugInCF("AnnotatedTypeMirror.createType: input type must not be null");
}
AnnotatedTypeMirror result;
switch (type.getKind()) {
case ARRAY:
result = new AnnotatedArrayType((ArrayType) type, atypeFactory);
break;
case DECLARED:
result =
new AnnotatedDeclaredType((DeclaredType) type, atypeFactory, isDeclaration);
break;
case ERROR:
throw new ErrorTypeKindException(
"AnnotatedTypeMirror.createType: input is not compilable. Found error type:"
+ " "
+ type);
case EXECUTABLE:
result = new AnnotatedExecutableType((ExecutableType) type, atypeFactory);
break;
case VOID:
case PACKAGE:
case NONE:
result = new AnnotatedNoType((NoType) type, atypeFactory);
break;
case NULL:
result = new AnnotatedNullType((NullType) type, atypeFactory);
break;
case TYPEVAR:
result =
new AnnotatedTypeVariable((TypeVariable) type, atypeFactory, isDeclaration);
break;
case WILDCARD:
result = new AnnotatedWildcardType((WildcardType) type, atypeFactory);
break;
case INTERSECTION:
result = new AnnotatedIntersectionType((IntersectionType) type, atypeFactory);
break;
case UNION:
result = new AnnotatedUnionType((UnionType) type, atypeFactory);
break;
default:
if (type.getKind().isPrimitive()) {
result = new AnnotatedPrimitiveType((PrimitiveType) type, atypeFactory);
break;
}
throw new BugInCF(
"AnnotatedTypeMirror.createType: unidentified type "
+ type
+ " ("
+ type.getKind()
+ ")");
}
/*if (jctype.isAnnotated()) {
result.addAnnotations(jctype.getAnnotationMirrors());
}*/
return result;
}
@Override
public final boolean equals(@Nullable Object o) {
if (o == this) {
return true;
}
if (!(o instanceof AnnotatedTypeMirror)) {
return false;
}
return EQUALITY_COMPARER.visit(this, (AnnotatedTypeMirror) o, null);
}
@Pure
@Override
public final int hashCode() {
return HASHCODE_VISITOR.visit(this);
}
/**
* Applies a visitor to this type.
*
* @param the return type of the visitor's methods
* @param the type of the additional parameter to the visitor's methods
* @param v the visitor operating on this type
* @param p additional parameter to the visitor
* @return a visitor-specified result
*/
public abstract R accept(AnnotatedTypeVisitor v, P p);
/**
* Returns the {@code kind} of this type.
*
* @return the kind of this type
*/
public TypeKind getKind() {
return underlyingType.getKind();
}
/**
* Given a primitive type, return its kind. Given a boxed primitive type, return the
* corresponding primitive type kind. Otherwise, return null.
*
* @return a primitive type kind if this is a primitive type or boxed primitive type; otherwise
* null
*/
public @Nullable TypeKind getPrimitiveKind() {
return TypeKindUtils.primitiveOrBoxedToTypeKind(getUnderlyingType());
}
/**
* Returns the underlying unannotated Java type, which this wraps.
*
* @return the underlying type
*/
public TypeMirror getUnderlyingType() {
return underlyingType;
}
/**
* Returns true if this type mirror represents a declaration, rather than a use, of a type.
*
* For example, {@code class List { ... }} declares a new type {@code List}, while
* {@code List} is a use of the type.
*
* @return true if this represents a declaration
*/
public boolean isDeclaration() {
return false;
}
public AnnotatedTypeMirror asUse() {
return this;
}
/**
* Returns true if this type has a primary annotation in the same hierarchy as {@code
* annotation}.
*
* This method does not account for annotations in deep types (type arguments, array
* components, etc).
*
* @param annotation the qualifier hierarchy to check for
* @return true iff this type has a primary annotation in the same hierarchy as {@code
* annotation}.
*/
// typetools: hasPrimaryAnnotationInHierarchy
public boolean hasAnnotationInHierarchy(AnnotationMirror annotation) {
return getAnnotationInHierarchy(annotation) != null;
}
/**
* Returns the primary annotation on this type that is in the same hierarchy as {@code
* annotation}. For {@link AnnotatedTypeVariable}s and {@link AnnotatedWildcardType}s, {@code
* null} may be returned when the upper bound may have an annotation with that class, so {@link
* #getEffectiveAnnotationInHierarchy(AnnotationMirror)} should be called instead.
*
*
This method does not account for annotations in deep types (type arguments, array
* components, etc).
*
*
May return null if the receiver is a type variable or a wildcard without a primary
* annotation, or if the receiver is not yet fully annotated.
*
* @param annotation an annotation in the qualifier hierarchy to check for
* @return the annotation mirror whose class is named {@code annoNAme} or null
*/
// typetools: getPrimaryAnnotationInHierarchy
public @Nullable AnnotationMirror getAnnotationInHierarchy(AnnotationMirror annotation) {
if (primaryAnnotations.isEmpty()) {
return null;
}
AnnotationMirror canonical = annotation;
if (!atypeFactory.isSupportedQualifier(canonical)) {
canonical = atypeFactory.canonicalAnnotation(annotation);
if (canonical == null) {
// This can happen if annotation is unrelated to this AnnotatedTypeMirror.
return null;
}
}
if (atypeFactory.isSupportedQualifier(canonical)) {
QualifierHierarchy qualHierarchy = atypeFactory.getQualifierHierarchy();
AnnotationMirror anno =
qualHierarchy.findAnnotationInSameHierarchy(primaryAnnotations, canonical);
if (anno != null) {
return anno;
}
}
return null;
}
/**
* Returns the "effective" annotation from the same hierarchy as {@code annotation}, otherwise
* returns {@code null}.
*
*
An effective annotation is the annotation on the type itself, or on the upper/extends
* bound of a type variable/wildcard (recursively, until a class type is reached).
*
* @param annotation an annotation in the qualifier hierarchy to check for
* @return an annotation from the same hierarchy as {@code annotation} if present
*/
public @Nullable AnnotationMirror getEffectiveAnnotationInHierarchy(
AnnotationMirror annotation) {
AnnotationMirror canonical = annotation;
if (!atypeFactory.isSupportedQualifier(canonical)) {
canonical = atypeFactory.canonicalAnnotation(annotation);
}
if (atypeFactory.isSupportedQualifier(canonical)) {
QualifierHierarchy qualHierarchy = this.atypeFactory.getQualifierHierarchy();
AnnotationMirror anno =
qualHierarchy.findAnnotationInSameHierarchy(
getEffectiveAnnotations(), canonical);
if (anno != null) {
return anno;
}
}
return null;
}
/**
* Returns the primary annotations on this type. For {@link AnnotatedTypeVariable}s and {@link
* AnnotatedWildcardType}s, the returned annotations may be empty or missing annotations in
* hierarchies, so {@link #getEffectiveAnnotations()} should be called instead.
*
*
It does not include annotations in deep types (type arguments, array components, etc).
*
*
To get the single primary annotation in a particular hierarchy, use {@link
* #getAnnotationInHierarchy}.
*
* @return an unmodifiable set of the annotations on this
*/
// typetools: getPrimaryAnnotations
// typetools: removed method getPrimaryAnnotation
public final AnnotationMirrorSet getAnnotations() {
return AnnotationMirrorSet.unmodifiableSet(primaryAnnotations);
}
/**
* Returns the annotations on this type; mutations affect this object, because the return type
* is an alias of the {@code annotations} field. It does not include annotations in deep types
* (type arguments, array components, etc).
*
*
The returned set should not be modified, but for efficiency reasons modification is not
* prevented. Modifications might break invariants.
*
* @return the set of the annotations on this; mutations affect this object
*/
// typetools: getPrimaryAnnotationsField
protected final AnnotationMirrorSet getAnnotationsField() {
return primaryAnnotations;
}
/**
* Returns the "effective" annotations on this type, i.e. the annotations on the type itself, or
* on the upper/extends bound of a type variable/wildcard (recursively, until a class type is
* reached). If this is fully-annotated, the returned set will contain one annotation per
* hierarchy.
*
* @return a set of the annotations on this
*/
// TODO: When the current, deprecated `getAnnotations()` (deprecation date 2023-06-15) is
// removed,
// rename all the "getEffectiveAnnotation...()" methods to just "getAnnotation...()".
// EISOP will not do this renaming, it would introduce inconsistent behavior with how
// getAnnotations in javac APIs works.
// Removed getEffectiveAnnotation
public AnnotationMirrorSet getEffectiveAnnotations() {
AnnotationMirrorSet effectiveAnnotations = getErased().getAnnotations();
// assert atypeFactory.qualHierarchy.getWidth() == effectiveAnnotations
// .size() : "Invalid number of effective annotations ("
// + effectiveAnnotations + "). Should be "
// + atypeFactory.qualHierarchy.getWidth() + " but is "
// + effectiveAnnotations.size() + ". Type: " + this;
return effectiveAnnotations;
}
/**
* Returns the primary annotation on this type whose class is {@code annoClass}. For {@link
* AnnotatedTypeVariable}s and {@link AnnotatedWildcardType}s, {@code null} may be returned when
* the upper bound may have an annotation with that class, so {@link
* #getEffectiveAnnotation(Class)} should be called instead.
*
* @param annoClass annotation class
* @return the annotation mirror whose class is {@code annoClass} or null
*/
// typetools: getPrimaryAnnotation
public @Nullable AnnotationMirror getAnnotation(Class annoClass) {
for (AnnotationMirror annoMirror : primaryAnnotations) {
if (atypeFactory.areSameByClass(annoMirror, annoClass)) {
return annoMirror;
}
}
return null;
}
/**
* Returns the primary annotations on this type whose annotation class name {@code annoName}.
* For {@link AnnotatedTypeVariable}s and {@link AnnotatedWildcardType}s, {@code null} may be
* returned when the upper bound may have an annotation with that class, so {@link
* #getEffectiveAnnotation(Class)} should be called instead.
*
* @param annoName annotation class name
* @return the annotation mirror whose class is named {@code annoName} or null
*/
// typetools: getPrimaryAnnotation
public @Nullable AnnotationMirror getAnnotation(String annoName) {
for (AnnotationMirror annoMirror : primaryAnnotations) {
if (AnnotationUtils.areSameByName(annoMirror, annoName)) {
return annoMirror;
}
}
return null;
}
/**
* Returns the set of explicitly written annotations on this type that are supported by this
* checker. This is useful to check the validity of annotations explicitly present on a type, as
* flow inference might add annotations that were not previously present. Note that since
* AnnotatedTypeMirror instances are created for type uses, this method will return explicit
* annotations in type use locations but will not return explicit annotations that had an impact
* on defaulting, such as an explicit annotation on a class declaration. For example, given:
*
*
{@code @MyExplicitAnno class MyClass {}; MyClass myClassInstance; }
*
*
the result of calling {@code
* atypeFactory.getAnnotatedType(variableTreeForMyClassInstance).getExplicitAnnotations()}
*
*
will not contain {@code @MyExplicitAnno}.
*
* @return the set of explicitly written annotations on this type that are supported by this
* checker
*/
public AnnotationMirrorSet getExplicitAnnotations() {
// TODO JSR 308: The explicit type annotations should be always present
AnnotationMirrorSet explicitAnnotations = new AnnotationMirrorSet();
List typeAnnotations =
this.getUnderlyingType().getAnnotationMirrors();
for (AnnotationMirror explicitAnno : typeAnnotations) {
if (atypeFactory.isSupportedQualifier(explicitAnno)) {
explicitAnnotations.add(explicitAnno);
}
}
return explicitAnnotations;
}
/**
* Returns true if this type has a primary annotation that is the same as {@code a}.
*
*
This method considers the annotation's values. If the type is {@code @A("s") @B(3)
* Object}, then a call with {@code @A("t")} or {@code @A} will return false, whereas a call
* with {@code @B(3)} will return true.
*
*
In contrast to {@link #hasAnnotationRelaxed(AnnotationMirror)} this method also compares
* annotation values.
*
* @param a the annotation to check for
* @return true iff this type has a primary annotation that is the same as {@code a}
* @see #hasAnnotationRelaxed(AnnotationMirror)
*/
// typetools: hasPrimaryAnnotation
public boolean hasAnnotation(AnnotationMirror a) {
return AnnotationUtils.containsSame(primaryAnnotations, a);
}
/**
* Returns true if this type has a primary annotation that has the same annotation type as
* {@code a}. This method does not consider an annotation's values.
*
* @param a the class of annotation to check for
* @return true iff the type contains an annotation with the same type as the annotation given
* by {@code a}
*/
public boolean hasAnnotation(Class a) {
return getAnnotation(a) != null;
}
/**
* Returns the "effective" annotation on this type with the class {@code annoClass} or {@code
* null} if this type does not have one.
*
*
An effective annotation is the annotation on the type itself, or on the upper/extends
* bound of a type variable/wildcard (recursively, until a class type is reached).
*
* @param annoClass annotation class
* @return the effective annotation with the same class as {@code annoClass}
*/
public @Nullable AnnotationMirror getEffectiveAnnotation(
Class annoClass) {
for (AnnotationMirror annoMirror : getEffectiveAnnotations()) {
if (atypeFactory.areSameByClass(annoMirror, annoClass)) {
return annoMirror;
}
}
return null;
}
/**
* A version of {@link #hasAnnotation(Class)} that considers annotations on the upper bound of
* wildcards and type variables.
*/
public boolean hasEffectiveAnnotation(Class a) {
return getEffectiveAnnotation(a) != null;
}
/**
* A version of {@link #hasAnnotation(AnnotationMirror)} that considers annotations on the upper
* bound of wildcards and type variables.
*/
public boolean hasEffectiveAnnotation(AnnotationMirror a) {
return AnnotationUtils.containsSame(getEffectiveAnnotations(), a);
}
/**
* Returns true if this type contains the given annotation explicitly written at declaration.
* This method considers the annotation's values. If the type is {@code @A("s") @B(3) Object}, a
* call with {@code @A("t")} or {@code @A} will return false, whereas a call with {@code @B(3)}
* will return true.
*
*
In contrast to {@link #hasExplicitAnnotationRelaxed(AnnotationMirror)} this method also
* compares annotation values.
*
*
See the documentation for {@link #getExplicitAnnotations()} for details on which explicit
* annotations are not included.
*
* @param a the annotation to check for
* @return true iff the annotation {@code a} is explicitly written on the type
* @see #hasExplicitAnnotationRelaxed(AnnotationMirror)
* @see #getExplicitAnnotations()
*/
public boolean hasExplicitAnnotation(AnnotationMirror a) {
return AnnotationUtils.containsSame(getExplicitAnnotations(), a);
}
/**
* Returns true if this type has a primary annotation that has the same annotation class as
* {@code a}.
*
*
This method does not consider an annotation's values. If the type is {@code @A("s") @B(3)
* Object}, then a call with {@code @A("t")}, {@code @A}, or {@code @B} will return true.
*
* @param a the annotation to check for
* @return true iff the type has a primary annotation with the same type as {@code a}
* @see #hasAnnotation(AnnotationMirror)
*/
// typetools: hasPrimaryAnnotationRelaxed
public boolean hasAnnotationRelaxed(AnnotationMirror a) {
return AnnotationUtils.containsSameByName(primaryAnnotations, a);
}
/**
* A version of {@link #hasAnnotationRelaxed(AnnotationMirror)} that considers annotations on
* the upper bound of wildcards and type variables.
*/
public boolean hasEffectiveAnnotationRelaxed(AnnotationMirror a) {
return AnnotationUtils.containsSameByName(getEffectiveAnnotations(), a);
}
/**
* A version of {@link #hasAnnotationRelaxed(AnnotationMirror)} that only considers annotations
* that are explicitly written on the type.
*
*
See the documentation for {@link #getExplicitAnnotations()} for details on which explicit
* annotations are not included.
*/
public boolean hasExplicitAnnotationRelaxed(AnnotationMirror a) {
return AnnotationUtils.containsSameByName(getExplicitAnnotations(), a);
}
/**
* Returns true if this type contains an explicitly written annotation with the same annotation
* type as a particular annotation. This method does not consider an annotation's values.
*
*
See the documentation for {@link #getExplicitAnnotations()} for details on which explicit
* annotations are not included.
*
* @param a the class of annotation to check for
* @return true iff the type contains an explicitly written annotation with the same type as the
* annotation given by {@code a}
* @see #getExplicitAnnotations()
*/
public boolean hasExplicitAnnotation(Class a) {
return AnnotationUtils.containsSameByName(getExplicitAnnotations(), getAnnotation(a));
}
/**
* Adds the canonical version of {@code annotation} as a primary annotation of this type and, in
* the case of {@link AnnotatedTypeVariable}s, {@link AnnotatedWildcardType}s, and {@link
* AnnotatedIntersectionType}s, adds it to all bounds. (The canonical version is found via
* {@link AnnotatedTypeFactory#canonicalAnnotation}.) If the canonical version of {@code
* annotation} is not a supported qualifier, then no annotation is added. If this type already
* has annotation in the same hierarchy as {@code annotation}, the behavior of this method is
* undefined.
*
* @param annotation the annotation to add
*/
public void addAnnotation(AnnotationMirror annotation) {
if (annotation == null) {
throw new BugInCF("AnnotatedTypeMirror.addAnnotation: null argument.");
}
if (atypeFactory.isSupportedQualifier(annotation)) {
this.primaryAnnotations.add(annotation);
} else {
AnnotationMirror canonical = atypeFactory.canonicalAnnotation(annotation);
if (atypeFactory.isSupportedQualifier(canonical)) {
addAnnotation(canonical);
}
}
}
/**
* Adds an annotation to this type, removing any existing primary annotations from the same
* qualifier hierarchy first.
*
* @param a the annotation to add
*/
public void replaceAnnotation(AnnotationMirror a) {
this.removeAnnotationInHierarchy(a);
this.addAnnotation(a);
}
/**
* Adds an annotation to this type.
*
* @param a the class of the annotation to add
* @deprecated This method creates a new {@code AnnotationMirror} every time it is called.
* Instead of calling this method, store the {@code AnnotationMirror} in a field and use
* {@link #addAnnotation(AnnotationMirror)} instead.
*/
@Deprecated // 2023-06-15
public void addAnnotation(Class a) {
AnnotationMirror anno = AnnotationBuilder.fromClass(atypeFactory.elements, a);
addAnnotation(anno);
}
/**
* Adds the canonical version of all {@code annotations} as primary annotations of this type
* and, in the case of {@link AnnotatedTypeVariable}s, {@link AnnotatedWildcardType}s, and
* {@link AnnotatedIntersectionType}s, adds them to all bounds. (The canonical version is found
* via {@link AnnotatedTypeFactory#canonicalAnnotation}.) If the canonical version of an {@code
* annotation} is not a supported qualifier, then that annotation is not add added. If this type
* already has annotation in the same hierarchy as any of the {@code annotations}, the behavior
* of this method is undefined.
*
* @param annotations the annotations to add
*/
public void addAnnotations(Iterable annotations) {
for (AnnotationMirror a : annotations) {
this.addAnnotation(a);
}
}
/**
* Adds only the annotations in {@code annotations} that the type does not already have a
* primary annotation in the same hierarchy.
*
*
The canonical version of the {@code annotations} are added as primary annotations of this
* type and, in the case of {@link AnnotatedTypeVariable}s, {@link AnnotatedWildcardType}s, and
* {@link AnnotatedIntersectionType}s, adds them to all bounds. (The canonical version is found
* via {@link AnnotatedTypeFactory#canonicalAnnotation}.) If the canonical version of an
* annotation is not a supported qualifier, then that annotation is not add added.
*
* @param annotations the annotations to add
*/
public void addMissingAnnotations(Iterable annotations) {
for (AnnotationMirror a : annotations) {
addMissingAnnotation(a);
}
}
/**
* Add {@code annotation} if the type does not already have a primary annotation in the same
* hierarchy.
*
*
The canonical version of the {@code annotation} is added as a primary annotation of this
* type and (in the case of {@link AnnotatedTypeVariable}s, {@link AnnotatedWildcardType}s, and
* {@link AnnotatedIntersectionType}s) added to all bounds. (The canonical version is found via
* {@link AnnotatedTypeFactory#canonicalAnnotation}.) If the canonical version of an {@code
* annotation} is not a supported qualifier, then that annotation is not add added.
*
* @param annotation the annotations to add
*/
public void addMissingAnnotation(AnnotationMirror annotation) {
if (!this.hasAnnotationInHierarchy(annotation)) {
this.addAnnotation(annotation);
}
}
/**
* Adds multiple annotations to this type, removing any existing primary annotations from the
* same qualifier hierarchy first.
*
* @param replAnnos the annotations to replace
*/
public void replaceAnnotations(Iterable replAnnos) {
for (AnnotationMirror a : replAnnos) {
this.replaceAnnotation(a);
}
}
/**
* Removes a primary annotation from the type.
*
* @param a the annotation to remove
* @return true if the annotation was removed, false if the type's annotations were unchanged
*/
// typetools removePrimaryAnnotation
public boolean removeAnnotation(AnnotationMirror a) {
AnnotationMirror anno = AnnotationUtils.getSame(primaryAnnotations, a);
if (anno != null) {
return primaryAnnotations.remove(anno);
}
return false;
}
/**
* Removes a primary annotation of the given class from the type.
*
* @param a the class of the annotation to remove
* @return true if the annotation was removed, false if the type's annotations were unchanged
*/
// typetools: removePrimaryAnnotationByClass
public boolean removeAnnotationByClass(Class a) {
AnnotationMirror anno = atypeFactory.getAnnotationByClass(primaryAnnotations, a);
if (anno != null) {
return this.removeAnnotation(anno);
}
return false;
}
/**
* Remove any primary annotation that is in the same qualifier hierarchy as the parameter.
*
* @param a an annotation from the same qualifier hierarchy
* @return if an annotation was removed
*/
// typetools removePrimaryAnnotationInHierarchy
public boolean removeAnnotationInHierarchy(AnnotationMirror a) {
AnnotationMirror prev = this.getAnnotationInHierarchy(a);
if (prev != null) {
return this.removeAnnotation(prev);
}
return false;
}
/**
* Remove an annotation that is in the same qualifier hierarchy as the parameter, unless it's
* the top annotation.
*
* @param a an annotation from the same qualifier hierarchy
* @return if an annotation was removed
* @deprecated This will be removed in a future release
*/
@Deprecated // 2023-06-15
public boolean removeNonTopAnnotationInHierarchy(AnnotationMirror a) {
AnnotationMirror prev = this.getAnnotationInHierarchy(a);
QualifierHierarchy qualHierarchy = this.atypeFactory.getQualifierHierarchy();
if (prev != null && !prev.equals(qualHierarchy.getTopAnnotation(a))) {
return this.removeAnnotation(prev);
}
return false;
}
/**
* Removes multiple primary annotations from the type.
*
* @param annotations the annotations to remove
* @return true if at least one annotation was removed, false if the type's annotations were
* unchanged
*/
// typetools: removePrimaryAnnotations
public boolean removeAnnotations(Iterable annotations) {
boolean changed = false;
for (AnnotationMirror a : annotations) {
changed |= this.removeAnnotation(a);
}
return changed;
}
/** Removes all primary annotations on this type. */
// typetools: clearPrimaryAnnotations
public void clearAnnotations() {
primaryAnnotations.clear();
}
@SideEffectFree
@Override
public final String toString() {
return atypeFactory.getAnnotatedTypeFormatter().format(this);
}
@SideEffectFree
public final String toString(boolean verbose) {
return atypeFactory.getAnnotatedTypeFormatter().format(this, verbose);
}
/**
* Returns the erasure type of this type, according to JLS specifications.
*
* @see https://docs.oracle.com/javase/specs/jls/se17/html/jls-4.html#jls-4.6
* @return the erasure of this AnnotatedTypeMirror, this is always a copy even if the erasure
* and the original type are equivalent
*/
public AnnotatedTypeMirror getErased() {
return deepCopy();
}
/**
* Returns a deep copy of this type. A deep copy implies that each component type is copied
* recursively and the returned type refers to those copies in its component locations.
*
*
Note: deepCopy provides two important properties in the returned copy:
*
*
* - Structure preservation -- The exact structure of the original AnnotatedTypeMirror is
* preserved in the copy including all component types.
*
- Annotation preservation -- All of the annotations from the original AnnotatedTypeMirror
* and its components have been copied to the new type.
*
*
* If copyAnnotations is set to false, the second property, Annotation preservation, is removed.
* This is useful for cases in which the user may want to copy the structure of a type exactly
* but NOT its annotations.
*
* @return a deep copy
*/
public abstract AnnotatedTypeMirror deepCopy(boolean copyAnnotations);
/**
* Returns a deep copy of this type with annotations.
*
* Each subclass implements this method with the subclass return type. The method body must
* always be a call to deepCopy(true).
*
* @return a deep copy of this type with annotations
* @see #deepCopy(boolean)
*/
@Override
public abstract AnnotatedTypeMirror deepCopy();
/**
* Returns a shallow copy of this type. A shallow copy implies that each component type in the
* output copy refers to the same object as the object being copied.
*
* @param copyAnnotations whether copy should have annotations, i.e. whether field {@code
* annotations} should be copied.
*/
public abstract AnnotatedTypeMirror shallowCopy(boolean copyAnnotations);
/**
* Returns a shallow copy of this type with annotations.
*
*
Each subclass implements this method with the subclass return type. The method body must
* always be a call to shallowCopy(true).
*
* @see #shallowCopy(boolean)
* @return a shallow copy of this type with annotations
*/
public abstract AnnotatedTypeMirror shallowCopy();
/**
* Returns whether this type or any component type is a wildcard type for which Java 7 type
* inference is insufficient. See issue 979, or the documentation on AnnotatedWildcardType.
*
* @return whether this type or any component type is a wildcard type for which Java 7 type
* inference is insufficient
*/
public boolean containsUninferredTypeArguments() {
return atypeFactory.containsUninferredTypeArguments(this);
}
/**
* Create an {@link AnnotatedDeclaredType} with the underlying type of {@link Object}. It
* includes any annotations placed by {@link AnnotatedTypeFactory#fromElement(Element)}.
*
* @param atypeFactory type factory to use
* @return AnnotatedDeclaredType for Object
*/
protected static AnnotatedDeclaredType createTypeOfObject(AnnotatedTypeFactory atypeFactory) {
AnnotatedDeclaredType objectType =
atypeFactory.fromElement(
atypeFactory.elements.getTypeElement(Object.class.getCanonicalName()));
objectType.declaration = false;
return objectType;
}
/**
* Create an {@link AnnotatedDeclaredType} with the underlying type of {@code java.lang.Record}.
* It includes any annotations placed by {@link AnnotatedTypeFactory#fromElement(Element)}.
*
* @param atypeFactory type factory to use
* @return AnnotatedDeclaredType for Record
*/
protected static AnnotatedDeclaredType createTypeOfRecord(AnnotatedTypeFactory atypeFactory) {
AnnotatedDeclaredType recordType =
atypeFactory.fromElement(atypeFactory.elements.getTypeElement("java.lang.Record"));
recordType.declaration = false;
return recordType;
}
/**
* Returns the result of calling {@code underlyingType.toString().hashcode()}. This method saves
* the result in a field so that it isn't recomputed each time.
*
* @return the result of calling {@code underlyingType.toString().hashcode()}
*/
public int getUnderlyingTypeHashCode() {
if (underlyingTypeHashCode == -1) {
underlyingTypeHashCode = underlyingType.toString().hashCode();
}
return underlyingTypeHashCode;
}
/** Represents a declared type (whether class or interface). */
public static class AnnotatedDeclaredType extends AnnotatedTypeMirror {
/** Parametrized Type Arguments. */
protected List typeArgs;
/**
* Whether the type was initially raw, i.e. the user did not provide the type arguments.
* typeArgs will contain inferred type arguments, which might be too conservative at the
* moment. TODO: improve inference.
*
* Ideally, the field would be final. However, when we determine the supertype of a raw
* type, we need to set isUnderlyingTypeRaw for the supertype.
*/
private boolean isUnderlyingTypeRaw;
/** The enclosing type. May be null. May be changed. */
protected @Nullable AnnotatedDeclaredType enclosingType;
/** True if this represents a declaration, rather than a use, of a type. */
private boolean declaration;
/**
* Constructor for this type. The result contains no annotations.
*
* @param type underlying kind of this type
* @param atypeFactory the AnnotatedTypeFactory used to create this type
*/
private AnnotatedDeclaredType(
DeclaredType type, AnnotatedTypeFactory atypeFactory, boolean declaration) {
super(type, atypeFactory);
TypeElement typeelem = (TypeElement) type.asElement();
DeclaredType declty = (DeclaredType) typeelem.asType();
isUnderlyingTypeRaw =
!declty.getTypeArguments().isEmpty() && type.getTypeArguments().isEmpty();
TypeMirror encl = type.getEnclosingType();
if (encl.getKind() == TypeKind.DECLARED) {
this.enclosingType =
(AnnotatedDeclaredType) createType(encl, atypeFactory, declaration);
} else if (encl.getKind() == TypeKind.NONE) {
this.enclosingType = null;
} else {
throw new BugInCF(
"AnnotatedDeclaredType: unsupported enclosing type: "
+ type.getEnclosingType()
+ " ("
+ encl.getKind()
+ ")");
}
this.declaration = declaration;
}
@Override
public boolean isDeclaration() {
return declaration;
}
@Override
public AnnotatedDeclaredType deepCopy(boolean copyAnnotations) {
return (AnnotatedDeclaredType) new AnnotatedTypeCopier(copyAnnotations).visit(this);
}
@Override
public AnnotatedDeclaredType deepCopy() {
return deepCopy(true);
}
@Override
public AnnotatedDeclaredType asUse() {
if (!this.isDeclaration()) {
return this;
}
AnnotatedDeclaredType result = this.shallowCopy(true);
result.declaration = false;
if (this.enclosingType != null) {
result.enclosingType = this.enclosingType.asUse();
}
// setTypeArguments calls asUse on all the new type arguments.
result.setTypeArguments(typeArgs);
// If "this" is a type declaration with a type variable that references itself, e.g.
// MyClass>, then the type variable is a declaration, i.e. the first
// T, but the reference to the type variable is a use, i.e. the second T. When "this"
// is converted to a use, then both type variables are uses and should be the same
// object.
// The code below does this.
Map mapping = new HashMap<>(typeArgs.size());
for (AnnotatedTypeMirror typeArg : result.getTypeArguments()) {
AnnotatedTypeVariable typeVar = (AnnotatedTypeVariable) typeArg;
mapping.put(typeVar.getUnderlyingType(), typeVar);
}
for (AnnotatedTypeMirror typeArg : result.getTypeArguments()) {
AnnotatedTypeVariable typeVar = (AnnotatedTypeVariable) typeArg;
AnnotatedTypeMirror upperBound =
atypeFactory
.getTypeVarSubstitutor()
.substituteWithoutCopyingTypeArguments(
mapping, typeVar.getUpperBound());
typeVar.setUpperBound(upperBound);
}
return result;
}
@Override
public R accept(AnnotatedTypeVisitor v, P p) {
return v.visitDeclared(this, p);
}
/**
* Sets the type arguments on this type.
*
* @param ts a list of type arguments to be captured by this method
*/
public void setTypeArguments(List ts) {
if (ts == null || ts.isEmpty()) {
typeArgs = Collections.emptyList();
} else if (isDeclaration()) {
for (AnnotatedTypeMirror typeArg : ts) {
if (typeArg.getKind() != TypeKind.TYPEVAR) {
throw new BugInCF(
"Type declaration must have type variables as type arguments. Found %s",
typeArg);
}
if (!typeArg.isDeclaration()) {
throw new BugInCF(
"Type declarations must have type variables that are declarations. Found %s",
typeArg);
}
}
typeArgs = Collections.unmodifiableList(ts);
} else {
List uses =
CollectionsPlume.mapList(AnnotatedTypeMirror::asUse, ts);
typeArgs = Collections.unmodifiableList(uses);
}
}
/**
* Returns the type argument for this type.
*
* @return the type argument for this type
*/
public List getTypeArguments() {
if (typeArgs != null) {
return typeArgs;
} else if (isUnderlyingTypeRaw()) {
// Initialize the type arguments with uninferred wildcards.
BoundsInitializer.initializeTypeArgs(this);
return typeArgs;
} else if (getUnderlyingType().getTypeArguments().isEmpty()) {
typeArgs = Collections.emptyList();
return typeArgs;
} else {
// Initialize type argument for a non-raw declared type that has type arguments/
BoundsInitializer.initializeTypeArgs(this);
return typeArgs;
}
}
/**
* Returns true if the underlying type is raw. The receiver of this method is not raw,
* however; its annotated type arguments have been inferred.
*
* @return true iff the type was raw
*/
public boolean isUnderlyingTypeRaw() {
return isUnderlyingTypeRaw;
}
/**
* Set the isUnderlyingTypeRaw flag to true. This should only be necessary when determining
* the supertypes of a raw type.
*/
protected void setIsUnderlyingTypeRaw() {
this.isUnderlyingTypeRaw = true;
}
@Override
public DeclaredType getUnderlyingType() {
return (DeclaredType) underlyingType;
}
@Override
public List directSupertypes() {
return Collections.unmodifiableList(SupertypeFinder.directSupertypes(this));
}
@Override
public AnnotatedDeclaredType shallowCopy() {
return shallowCopy(true);
}
@Override
public AnnotatedDeclaredType shallowCopy(boolean copyAnnotations) {
AnnotatedDeclaredType type =
new AnnotatedDeclaredType(getUnderlyingType(), atypeFactory, declaration);
if (copyAnnotations) {
type.addAnnotations(this.getAnnotationsField());
}
type.setEnclosingType(getEnclosingType());
type.setTypeArguments(getTypeArguments());
return type;
}
/**
* Return the declared type with its type arguments removed. This also replaces the
* underlying type with its erasure.
*
* @return a fresh copy of the declared type with no type arguments
*/
@Override
public AnnotatedDeclaredType getErased() {
AnnotatedDeclaredType erased =
(AnnotatedDeclaredType)
AnnotatedTypeMirror.createType(
atypeFactory.types.erasure(underlyingType),
atypeFactory,
false);
erased.addAnnotations(this.getAnnotations());
AnnotatedDeclaredType erasedEnclosing = erased.getEnclosingType();
AnnotatedDeclaredType thisEnclosing = this.getEnclosingType();
while (erasedEnclosing != null) {
erasedEnclosing.addAnnotations(thisEnclosing.getAnnotations());
erasedEnclosing = erasedEnclosing.getEnclosingType();
thisEnclosing = thisEnclosing.getEnclosingType();
}
return erased;
}
/**
* Sets the enclosing type.
*
* @param enclosingType the new enclosing type
*/
public void setEnclosingType(@Nullable AnnotatedDeclaredType enclosingType) {
this.enclosingType = enclosingType;
}
/**
* Returns the enclosing type, as in the type of {@code A} in the type {@code A.B}. May
* return null.
*
* @return enclosingType the enclosing type, or null if this is a top-level type
*/
public @Nullable AnnotatedDeclaredType getEnclosingType() {
return enclosingType;
}
}
/**
* Represents a type of an executable. An executable is a method, constructor, or initializer.
*/
public static class AnnotatedExecutableType extends AnnotatedTypeMirror {
private @MonotonicNonNull ExecutableElement element;
private AnnotatedExecutableType(ExecutableType type, AnnotatedTypeFactory factory) {
super(type, factory);
}
/** The parameter types; an unmodifiable list. */
private @MonotonicNonNull List paramTypes = null;
/** Whether {@link paramTypes} has been computed. */
private boolean paramTypesComputed = false;
/**
* The receiver type of this executable type; null for static methods and constructors of
* top-level classes.
*/
private @Nullable AnnotatedDeclaredType receiverType;
/**
* The varargs type is the last element of {@link paramTypes} if the method or constructor
* accepts a variable number of arguments and the {@link paramTypes} has not been expanded
* yet. This type needs to be stored in the field to avoid being affected by calling {@link
* AnnotatedTypes#adaptParameters(AnnotatedTypeFactory, AnnotatedExecutableType, List)}.
*/
private @MonotonicNonNull AnnotatedArrayType varargType = null;
/** Whether {@link receiverType} has been computed. */
private boolean receiverTypeComputed = false;
/** The return type. */
private AnnotatedTypeMirror returnType;
/** Whether {@link returnType} has been computed. */
private boolean returnTypeComputed = false;
/** The thrown types; an unmodifiable list. */
private List thrownTypes;
/** Whether {@link thrownTypes} has been computed. */
private boolean thrownTypesComputed = false;
/** The type variables; an unmodifiable list. */
private List typeVarTypes;
/** Whether {@link typeVarTypes} has been computed. */
private boolean typeVarTypesComputed = false;
/**
* Returns true if this type represents a varargs method.
*
* @return true if this type represents a varargs method
*/
public boolean isVarArgs() {
return this.element.isVarArgs();
}
@Override
public R accept(AnnotatedTypeVisitor v, P p) {
return v.visitExecutable(this, p);
}
@Override
public ExecutableType getUnderlyingType() {
return (ExecutableType) this.underlyingType;
}
/**
* It never makes sense to add annotations to an executable type. Instead, they should be
* added to the appropriate component.
*
* @deprecated add to the appropriate component
*/
@Deprecated // not for removal
@Override
public void addAnnotation(AnnotationMirror annotation) {
assert false : "AnnotatedExecutableType.addAnnotation should never be called";
}
/**
* Sets the parameter types of this executable type, excluding the receiver.If paramTypes
* has been computed and this type is a varargs method, computes and store {@link
* varargType} before calling this method, @see {@link varargType}
*
* @param params an unmodifiable list of parameter types to be captured by this method,
* excluding the receiver
*/
/*package-private*/ void setParameterTypes(List params) {
if (paramTypesComputed && isVarArgs() && varargType == null) {
throw new BugInCF("Set vararg type before resetting parameter types");
}
paramTypes = params;
paramTypesComputed = true;
}
/**
* Returns the parameter types of this executable type, excluding the receiver.
*
* @return the parameter types of this executable type, excluding the receiver
*/
public List getParameterTypes() {
if (!paramTypesComputed) {
assert paramTypes == null;
List underlyingParameterTypes =
((ExecutableType) underlyingType).getParameterTypes();
if (underlyingParameterTypes.isEmpty()) {
setParameterTypes(Collections.emptyList());
} else {
List newParamTypes =
new ArrayList<>(underlyingParameterTypes.size());
for (TypeMirror t : underlyingParameterTypes) {
if (t.getKind() == TypeKind.ERROR) {
// Maybe the input is uncompilable, or maybe the type is not completed
// yet (see Issue #244).
throw new ErrorTypeKindException(
"Problem with parameter type of %s.%s: %s [%s %s]",
element,
element.getEnclosingElement(),
t,
t.getKind(),
t.getClass());
}
newParamTypes.add(createType(t, atypeFactory, false));
}
setParameterTypes(Collections.unmodifiableList(newParamTypes));
}
}
// No need to copy or wrap; it is an unmodifiable list.
return paramTypes;
}
/**
* Sets the vararg type of this executable type.
*
* @param varargType the vararg type of this executable type
*/
/*package-private*/ void setVarargType(@NonNull AnnotatedArrayType varargType) {
this.varargType = varargType;
}
/**
* Computes the vararg type of this executable type and stores it in {@link varargType}.
*
* This method computes {@link varargType} using the {@link paramTypes} of this
* executable type. To use the {@link paramTypes} from different executable type, use {@link
* #computeVarargType(AnnotatedExecutableType)}.
*/
/*package-private*/ void computeVarargType() {
computeVarargType(paramTypes);
}
/**
* Computes the vararg type using the passed executable type and stores it in this {@link
* varargType}.
*
* @param annotatedExecutableType an AnnotatedExecutableType
*/
/*package-private*/ void computeVarargType(
AnnotatedExecutableType annotatedExecutableType) {
computeVarargType(annotatedExecutableType.getParameterTypes());
}
/**
* Helper function for {@link #computeVarargType()} and {@link
* #computeVarargType(AnnotatedExecutableType)}.
*
* @param paramTypes the parameter types to determine the vararg type
*/
private void computeVarargType(List paramTypes) {
if (!isVarArgs()) {
return;
}
varargType = (AnnotatedArrayType) paramTypes.get(paramTypes.size() - 1);
}
/**
* Returns the vararg type of this executable type.
*
* @return the vararg type of this executable type
*/
public @Nullable AnnotatedArrayType getVarargType() {
return varargType;
}
/**
* Sets the return type of this executable type.
*
* @param returnType the new return type
*/
/*package-private*/ void setReturnType(AnnotatedTypeMirror returnType) {
this.returnType = returnType;
returnTypeComputed = true;
}
/**
* The return type of a method or constructor. For constructors, the return type is not
* VOID, but the type of the enclosing class.
*
* @return the return type of this executable type
*/
public AnnotatedTypeMirror getReturnType() {
if (!returnTypeComputed) {
assert returnType == null : "returnType = " + returnType;
if (element != null && ((ExecutableType) underlyingType).getReturnType() != null) {
TypeMirror aret = ((ExecutableType) underlyingType).getReturnType();
if (aret.getKind() == TypeKind.ERROR) {
// Maybe the input is uncompilable, or maybe the type is not completed yet
// (see Issue #244).
throw new ErrorTypeKindException(
"Problem with return type of %s.%s: %s [%s %s]",
element,
element.getEnclosingElement(),
aret,
aret.getKind(),
aret.getClass());
}
if (((MethodSymbol) element).isConstructor()) {
// For constructors, the underlying return type is void.
// Take the type of the enclosing class instead.
aret = element.getEnclosingElement().asType();
if (aret.getKind() == TypeKind.ERROR) {
throw new ErrorTypeKindException(
"Input is not compilable; problem with constructor %s return type: %s [%s %s]"
+ " (enclosing element = %s [%s])",
element,
aret,
aret.getKind(),
aret.getClass(),
element.getEnclosingElement(),
element.getEnclosingElement().getClass());
}
}
returnType = createType(aret, atypeFactory, false);
}
returnTypeComputed = true;
}
return returnType;
}
/**
* Sets the receiver type on this executable type.
*
* @param receiverType the receiver type
*/
/*package-private*/ void setReceiverType(@Nullable AnnotatedDeclaredType receiverType) {
this.receiverType = receiverType;
receiverTypeComputed = true;
}
/**
* Returns the receiver type of this executable type; null for static methods and
* constructors of top-level classes.
*
* @return the receiver type of this executable type; null for static methods and
* constructors of top-level classes
*/
public @Nullable AnnotatedDeclaredType getReceiverType() {
if (!receiverTypeComputed) {
assert receiverType == null;
Element element = getElement();
if (ElementUtils.hasReceiver(element)) {
// Initial value of `encl`; might be updated.
TypeElement encl = ElementUtils.enclosingTypeElement(element);
if (element.getKind() == ElementKind.CONSTRUCTOR) {
// Can only reach this branch if we're the constructor of a nested class
encl = ElementUtils.enclosingTypeElement(encl.getEnclosingElement());
}
TypeMirror enclType = encl.asType();
if (enclType.getKind() == TypeKind.ERROR) {
// Maybe the input is uncompilable, or maybe the type is not completed yet
// (see Issue #244).
throw new ErrorTypeKindException(
"Problem with receiver type of %s.%s: %s [%s %s]",
element,
element.getEnclosingElement(),
enclType,
enclType.getKind(),
enclType.getClass());
}
AnnotatedTypeMirror type = createType(enclType, atypeFactory, false);
assert type instanceof AnnotatedDeclaredType;
receiverType = (AnnotatedDeclaredType) type;
}
receiverTypeComputed = true;
}
return receiverType;
}
/**
* Sets the thrown types of this executable type.
*
* @param thrownTypes an unmodifiable list of thrown types to be captured by this method
*/
/*package-private*/ void setThrownTypes(List thrownTypes) {
this.thrownTypes = thrownTypes;
thrownTypesComputed = true;
}
/**
* Returns the thrown types of this executable type.
*
* @return the thrown types of this executable type
*/
public List getThrownTypes() {
if (!thrownTypesComputed) {
assert thrownTypes == null;
List underlyingThrownTypes =
((ExecutableType) underlyingType).getThrownTypes();
if (underlyingThrownTypes.isEmpty()) {
setThrownTypes(Collections.emptyList());
} else {
List newThrownTypes =
new ArrayList<>(underlyingThrownTypes.size());
for (TypeMirror t : underlyingThrownTypes) {
if (t.getKind() == TypeKind.ERROR) {
// Maybe the input is uncompilable, or maybe the type is not completed
// yet (see Issue #244).
throw new ErrorTypeKindException(
"Problem with thrown type of %s.%s: %s [%s %s]",
element,
element.getEnclosingElement(),
t,
t.getKind(),
t.getClass());
}
newThrownTypes.add(createType(t, atypeFactory, false));
}
setThrownTypes(Collections.unmodifiableList(newThrownTypes));
}
}
// No need to copy or wrap; it is an unmodifiable list.
return thrownTypes;
}
/**
* Sets the type variables associated with this executable type.
*
* @param types an unmodifiable list of type variables of this executable type to be
* captured by this method
*/
/*package-private*/ void setTypeVariables(List types) {
typeVarTypes = types;
typeVarTypesComputed = true;
}
/**
* Returns the type variables of this executable type, if any.
*
* @return the type variables of this executable type, if any
*/
public List getTypeVariables() {
if (!typeVarTypesComputed) {
assert typeVarTypes == null;
List underlyingTypeVariables =
((ExecutableType) underlyingType).getTypeVariables();
if (underlyingTypeVariables.isEmpty()) {
setTypeVariables(Collections.emptyList());
} else {
List newTypeVarTypes =
new ArrayList<>(underlyingTypeVariables.size());
for (TypeMirror t : underlyingTypeVariables) {
if (t.getKind() == TypeKind.ERROR) {
// Maybe the input is uncompilable, or maybe the type is not completed
// yet (see Issue #244).
throw new ErrorTypeKindException(
"Problem with type variables of %s.%s: %s [%s %s]",
element,
element.getEnclosingElement(),
t,
t.getKind(),
t.getClass());
}
newTypeVarTypes.add(
(AnnotatedTypeVariable) createType(t, atypeFactory, true));
}
setTypeVariables(Collections.unmodifiableList(newTypeVarTypes));
}
}
// No need to copy or wrap; it is an unmodifiable list.
return typeVarTypes;
}
@Override
public AnnotatedExecutableType deepCopy(boolean copyAnnotations) {
return (AnnotatedExecutableType) new AnnotatedTypeCopier(copyAnnotations).visit(this);
}
@Override
public AnnotatedExecutableType deepCopy() {
return deepCopy(true);
}
@Override
public AnnotatedExecutableType shallowCopy(boolean copyAnnotations) {
AnnotatedExecutableType type =
new AnnotatedExecutableType(getUnderlyingType(), atypeFactory);
type.setElement(getElement());
type.setParameterTypes(getParameterTypes());
if (getVarargType() != null) {
type.setVarargType(getVarargType());
} else {
type.computeVarargType();
}
type.setReceiverType(getReceiverType());
type.setReturnType(getReturnType());
type.setThrownTypes(getThrownTypes());
type.setTypeVariables(getTypeVariables());
return type;
}
@Override
public AnnotatedExecutableType shallowCopy() {
return shallowCopy(true);
}
/**
* Returns the element of this AnnotatedExecutableType.
*
* @return the element of this AnnotatedExecutableType
*/
public ExecutableElement getElement() {
return element;
}
/**
* Sets the element of this AnnotatedExecutableType.
*
* @param elem the new element for this AnnotatedExecutableType
*/
public void setElement(ExecutableElement elem) {
this.element = elem;
}
@Override
public AnnotatedExecutableType getErased() {
AnnotatedExecutableType type =
new AnnotatedExecutableType(
(ExecutableType) atypeFactory.types.erasure(getUnderlyingType()),
atypeFactory);
type.setElement(getElement());
type.setParameterTypes(erasureList(getParameterTypes()));
if (getVarargType() != null) {
type.setVarargType(getVarargType().getErased());
} else {
type.computeVarargType();
}
if (getReceiverType() != null) {
type.setReceiverType(getReceiverType().getErased());
} else {
type.setReceiverType(null);
}
type.setReturnType(getReturnType().getErased());
type.setThrownTypes(erasureList(getThrownTypes()));
return type;
}
/**
* Returns the erased types corresponding to the given types.
*
* @param lst annotated type mirrors
* @return erased annotated type mirrors in an unmodifiable list
*/
private List erasureList(List lst) {
if (lst.isEmpty()) {
return Collections.emptyList();
} else {
return Collections.unmodifiableList(
CollectionsPlume.mapList(AnnotatedTypeMirror::getErased, lst));
}
}
}
/**
* Represents Array types in java. A multidimensional array type is represented as an array type
* whose component type is also an array type.
*/
public static class AnnotatedArrayType extends AnnotatedTypeMirror {
private AnnotatedArrayType(ArrayType type, AnnotatedTypeFactory factory) {
super(type, factory);
}
/** The component type of this array type. */
private AnnotatedTypeMirror componentType;
@Override
public R accept(AnnotatedTypeVisitor v, P p) {
return v.visitArray(this, p);
}
@Override
public ArrayType getUnderlyingType() {
return (ArrayType) this.underlyingType;
}
/**
* Sets the component type of this array.
*
* @param type the component type
*/
public void setComponentType(AnnotatedTypeMirror type) {
this.componentType = type;
}
/**
* Returns the component type of this array.
*
* @return the component type of this array
*/
public AnnotatedTypeMirror getComponentType() {
if (componentType == null) { // lazy init
setComponentType(
createType(
((ArrayType) underlyingType).getComponentType(),
atypeFactory,
false));
}
return componentType;
}
@Override
public AnnotatedArrayType deepCopy(boolean copyAnnotations) {
return (AnnotatedArrayType) new AnnotatedTypeCopier(copyAnnotations).visit(this);
}
@Override
public AnnotatedArrayType deepCopy() {
return deepCopy(true);
}
@Override
public AnnotatedArrayType shallowCopy(boolean copyAnnotations) {
AnnotatedArrayType type =
new AnnotatedArrayType((ArrayType) underlyingType, atypeFactory);
if (copyAnnotations) {
type.addAnnotations(this.getAnnotationsField());
}
type.setComponentType(getComponentType());
return type;
}
@Override
public AnnotatedArrayType shallowCopy() {
return shallowCopy(true);
}
@Override
public AnnotatedArrayType getErased() {
// IMPORTANT NOTE: The returned type is a fresh Object because
// the componentType is the only component of arrays and the
// call to getErased will return a fresh object.
// | T[ ] | = |T| [ ]
AnnotatedArrayType at = shallowCopy();
AnnotatedTypeMirror ct = at.getComponentType().getErased();
at.setComponentType(ct);
return at;
}
}
/**
* Throw an exception if the boundType is null or a declaration.
*
* @param boundDescription the variety of bound: "Lower", "Super", or "Extends"
* @param boundType the type being tested
* @param thisType the object for which boundType is a bound
*/
private static void checkBound(
String boundDescription, AnnotatedTypeMirror boundType, AnnotatedTypeMirror thisType) {
if (boundType == null || boundType.isDeclaration()) {
throw new BugInCF(
"%s bounds should never be null or a declaration.%n new bound = %s%n type ="
+ " %s",
boundDescription, boundType, thisType);
}
}
/**
* Represents a type variable. A type variable may be explicitly declared by a type parameter of
* a type, method, or constructor. A type variable may also be declared implicitly, as by the
* capture conversion of a wildcard type argument (see chapter 5 of The Java Language
* Specification, Third Edition).
*/
public static class AnnotatedTypeVariable extends AnnotatedTypeMirror {
private AnnotatedTypeVariable(
TypeVariable type, AnnotatedTypeFactory atypeFactory, boolean declaration) {
super(type, atypeFactory);
this.declaration = declaration;
}
/** The lower bound of the type variable. */
private AnnotatedTypeMirror lowerBound;
/** The upper bound of the type variable. */
private AnnotatedTypeMirror upperBound;
private boolean declaration;
@Override
public boolean isDeclaration() {
return declaration;
}
@Override
public void addAnnotation(AnnotationMirror annotation) {
super.addAnnotation(annotation);
fixupBoundAnnotations();
}
@Override
public boolean removeAnnotation(AnnotationMirror a) {
boolean ret = super.removeAnnotation(a);
if (lowerBound != null) {
ret |= lowerBound.removeAnnotation(a);
}
if (upperBound != null) {
ret |= upperBound.removeAnnotation(a);
}
return ret;
}
/**
* Change whether this {@code AnnotatedTypeVariable} is considered a use or a declaration
* (use this method with caution).
*
* @param declaration true if this type variable should be considered a declaration
*/
public void setDeclaration(boolean declaration) {
this.declaration = declaration;
}
@Override
public AnnotatedTypeVariable asUse() {
if (!this.isDeclaration()) {
return this;
}
AnnotatedTypeVariable result = this.shallowCopy();
result.declaration = false;
Map mapping = new HashMap<>(1);
mapping.put(getUnderlyingType(), result);
AnnotatedTypeMirror upperBound =
atypeFactory
.getTypeVarSubstitutor()
.substituteWithoutCopyingTypeArguments(mapping, result.getUpperBound());
result.setUpperBound(upperBound);
return result;
}
@Override
public R accept(AnnotatedTypeVisitor v, P p) {
return v.visitTypeVariable(this, p);
}
@Override
public TypeVariable getUnderlyingType() {
return (TypeVariable) this.underlyingType;
}
/**
* Set the lower bound of this variable type.
*
* Returns the lower bound of this type variable. While a type parameter cannot include
* an explicit lower bound declaration, capture conversion can produce a type variable with
* a non-trivial lower bound. Type variables otherwise have a lower bound of NullType.
*
* @param type the lower bound type
*/
/*package-private*/ void setLowerBound(AnnotatedTypeMirror type) {
checkBound("Lower", type, this);
this.lowerBound = type;
fixupBoundAnnotations();
}
/**
* Get the lower bound field directly, bypassing any lazy initialization. This method is
* necessary to prevent infinite recursions in initialization. In general, prefer
* getLowerBound.
*
* @return the lower bound field
*/
public AnnotatedTypeMirror getLowerBoundField() {
return lowerBound;
}
/**
* Returns the lower bound type of this type variable.
*
* @return the lower bound type of this type variable
*/
public AnnotatedTypeMirror getLowerBound() {
if (lowerBound == null) { // lazy init
BoundsInitializer.initializeBounds(this);
fixupBoundAnnotations();
}
return lowerBound;
}
// If the lower bound was not present in underlyingType, then its annotation was defaulted
// from the AnnotatedTypeFactory. If the lower bound annotation is a supertype of the upper
// bound annotation, then the type is ill-formed. In that case, change the defaulted lower
// bound to be consistent with the explicitly-written upper bound.
//
// As a concrete example, if the default annotation is @Nullable, then the type "X extends
// @NonNull Y" should not be converted into "X extends @NonNull Y super @Nullable
// bottomtype" but be converted into "X extends @NonNull Y super @NonNull bottomtype".
//
// In addition, ensure consistency of annotations on type variables
// and the upper bound. Assume class C.
// The type of "@Nullable X" has to be "@Nullable X extends @Nullable Object",
// because otherwise the annotations are inconsistent.
private void fixupBoundAnnotations() {
if (!this.getAnnotationsField().isEmpty()) {
AnnotationMirrorSet newAnnos = this.getAnnotationsField();
if (upperBound != null) {
upperBound.replaceAnnotations(newAnnos);
}
// Note:
// if the lower bound is a type variable then when we place annotations on the
// primary annotation this will actually cause the type variable to be exact and
// propagate the primary annotation to the type variable because primary annotations
// overwrite the upper and lower bounds of type variables when
// getUpperBound/getLowerBound is called.
if (lowerBound != null) {
lowerBound.replaceAnnotations(newAnnos);
}
}
}
/**
* Set the upper bound of this variable type.
*
* @param type the upper bound type
*/
/*package-private*/ void setUpperBound(AnnotatedTypeMirror type) {
checkBound("Upper", type, this);
this.upperBound = type;
fixupBoundAnnotations();
}
/**
* Get the upper bound field directly, bypassing any lazy initialization. This method is
* necessary to prevent infinite recursions in initialization. In general, prefer
* getUpperBound.
*
* @return the upper bound field
*/
public AnnotatedTypeMirror getUpperBoundField() {
return upperBound;
}
/**
* Get the upper bound of the type variable, possibly lazily initializing it. Attention: If
* the upper bound is lazily initialized, it will not contain any annotations! Callers of
* the method have to make sure that an AnnotatedTypeFactory first processed the bound.
*
* @return the upper bound type of this type variable
*/
public AnnotatedTypeMirror getUpperBound() {
if (upperBound == null) { // lazy init
BoundsInitializer.initializeBounds(this);
fixupBoundAnnotations();
}
return upperBound;
}
public AnnotatedTypeParameterBounds getBounds() {
return new AnnotatedTypeParameterBounds(getUpperBound(), getLowerBound());
}
public AnnotatedTypeParameterBounds getBoundFields() {
return new AnnotatedTypeParameterBounds(getUpperBoundField(), getLowerBoundField());
}
@Override
public AnnotatedTypeVariable deepCopy(boolean copyAnnotations) {
return (AnnotatedTypeVariable) new AnnotatedTypeCopier(copyAnnotations).visit(this);
}
@Override
public AnnotatedTypeVariable deepCopy() {
return deepCopy(true);
}
@Override
public AnnotatedTypeVariable shallowCopy(boolean copyAnnotations) {
// Because type variables can refer to themselves, they can't be shallow copied, so
// return a deep copy instead.
AnnotatedTypeVariable type = deepCopy(true);
if (!copyAnnotations) {
type.getAnnotationsField().clear();
}
return type;
}
@Override
public AnnotatedTypeVariable shallowCopy() {
return shallowCopy(true);
}
/**
* This method will traverse the upper bound of this type variable calling getErased until
* it finds the concrete upper bound. e.g.
*
* {@code , T extends S, S extends List>}
*
* A call to getErased will return the type List
*
* @return the erasure of the upper bound of this type
* IMPORTANT NOTE: getErased should always return a FRESH object. This will occur for
* type variables if all other getErased methods are implemented appropriately.
* Therefore, to avoid extra copy calls, this method will not call deepCopy on
* getUpperBound
*/
@Override
public AnnotatedTypeMirror getErased() {
// |T extends A&B| = |A|
return this.getUpperBound().getErased();
}
}
/**
* A pseudo-type used where no actual type is appropriate. The kinds of NoType are:
*
*
* - VOID -- corresponds to the keyword void.
*
- PACKAGE -- the pseudo-type of a package element.
*
- NONE -- used in other cases where no actual type is appropriate; for example, the
* superclass of java.lang.Object.
*
*/
public static class AnnotatedNoType extends AnnotatedTypeMirror {
private AnnotatedNoType(NoType type, AnnotatedTypeFactory factory) {
super(type, factory);
}
// No need for methods
// Might like to override annotate(), include(), execlude()
// AS NoType does not accept any annotations
@Override
public R accept(AnnotatedTypeVisitor v, P p) {
return v.visitNoType(this, p);
}
@Override
public NoType getUnderlyingType() {
return (NoType) this.underlyingType;
}
@Override
public AnnotatedNoType deepCopy(boolean copyAnnotations) {
return (AnnotatedNoType) new AnnotatedTypeCopier(copyAnnotations).visit(this);
}
@Override
public AnnotatedNoType deepCopy() {
return deepCopy(true);
}
@Override
public AnnotatedNoType shallowCopy(boolean copyAnnotations) {
AnnotatedNoType type = new AnnotatedNoType((NoType) underlyingType, atypeFactory);
if (copyAnnotations) {
type.addAnnotations(this.getAnnotationsField());
}
return type;
}
@Override
public AnnotatedNoType shallowCopy() {
return shallowCopy(true);
}
}
/** Represents the null type. This is the type of the expression {@code null}. */
public static class AnnotatedNullType extends AnnotatedTypeMirror {
private AnnotatedNullType(NullType type, AnnotatedTypeFactory factory) {
super(type, factory);
}
@Override
public R accept(AnnotatedTypeVisitor v, P p) {
return v.visitNull(this, p);
}
@Override
public NullType getUnderlyingType() {
return (NullType) this.underlyingType;
}
@Override
public AnnotatedNullType deepCopy(boolean copyAnnotations) {
return (AnnotatedNullType) new AnnotatedTypeCopier(copyAnnotations).visit(this);
}
@Override
public AnnotatedNullType deepCopy() {
return deepCopy(true);
}
@Override
public AnnotatedNullType shallowCopy(boolean copyAnnotations) {
AnnotatedNullType type = new AnnotatedNullType((NullType) underlyingType, atypeFactory);
if (copyAnnotations) {
type.addAnnotations(this.getAnnotationsField());
}
return type;
}
@Override
public AnnotatedNullType shallowCopy() {
return shallowCopy(true);
}
}
/**
* Represents a primitive type. These include {@code boolean}, {@code byte}, {@code short},
* {@code int}, {@code long}, {@code char}, {@code float}, and {@code double}.
*/
public static class AnnotatedPrimitiveType extends AnnotatedTypeMirror {
private AnnotatedPrimitiveType(PrimitiveType type, AnnotatedTypeFactory factory) {
super(type, factory);
}
@Override
public R accept(AnnotatedTypeVisitor v, P p) {
return v.visitPrimitive(this, p);
}
@Override
public PrimitiveType getUnderlyingType() {
return (PrimitiveType) this.underlyingType;
}
@Override
public AnnotatedPrimitiveType deepCopy(boolean copyAnnotations) {
return (AnnotatedPrimitiveType) new AnnotatedTypeCopier(copyAnnotations).visit(this);
}
@Override
public AnnotatedPrimitiveType deepCopy() {
return deepCopy(true);
}
@Override
public AnnotatedPrimitiveType shallowCopy(boolean copyAnnotations) {
AnnotatedPrimitiveType type =
new AnnotatedPrimitiveType((PrimitiveType) underlyingType, atypeFactory);
if (copyAnnotations) {
type.addAnnotations(this.getAnnotationsField());
}
return type;
}
@Override
public AnnotatedPrimitiveType shallowCopy() {
return shallowCopy(true);
}
}
/**
* Represents a wildcard type argument. Examples include:
*
* ? ? extends Number ? super T
*
*
A wildcard may have its upper bound explicitly set by an extends clause, its lower bound
* explicitly set by a super clause, or neither (but not both).
*/
public static class AnnotatedWildcardType extends AnnotatedTypeMirror {
/** Lower ({@code super}) bound. */
private AnnotatedTypeMirror superBound;
/** Upper ({@code extends} bound. */
private AnnotatedTypeMirror extendsBound;
/**
* The type variable to which this wildcard is an argument. Used to initialize the upper
* bound of unbounded wildcards and wildcards in raw types.
*/
@SuppressWarnings("nullness") // is reset during initialization
private @NonNull TypeVariable typeVariable = null;
private AnnotatedWildcardType(WildcardType type, AnnotatedTypeFactory factory) {
super(type, factory);
}
@Override
public void addAnnotation(AnnotationMirror annotation) {
super.addAnnotation(annotation);
fixupBoundAnnotations();
}
@Override
public boolean removeAnnotation(AnnotationMirror a) {
boolean ret = super.removeAnnotation(a);
if (superBound != null) {
ret |= superBound.removeAnnotation(a);
}
if (extendsBound != null) {
ret |= extendsBound.removeAnnotation(a);
}
return ret;
}
/**
* Sets the super bound of this wildcard.
*
* @param type the type of the lower bound
*/
/*package-private*/ void setSuperBound(AnnotatedTypeMirror type) {
checkBound("Super", type, this);
this.superBound = type;
fixupBoundAnnotations();
}
public AnnotatedTypeMirror getSuperBoundField() {
return superBound;
}
/**
* Returns the lower bound of this wildcard. If no lower bound is explicitly declared,
* returns an {@link AnnotatedNullType}.
*
* @return the lower bound of this wildcard, or an {@link AnnotatedNullType} if none is
* explicitly declared
*/
public AnnotatedTypeMirror getSuperBound() {
if (superBound == null) {
BoundsInitializer.initializeSuperBound(this);
fixupBoundAnnotations();
}
return this.superBound;
}
/**
* Sets the upper bound of this wildcard.
*
* @param type the type of the upper bound
*/
/*package-private*/ void setExtendsBound(AnnotatedTypeMirror type) {
checkBound("Extends", type, this);
this.extendsBound = type;
fixupBoundAnnotations();
}
public AnnotatedTypeMirror getExtendsBoundField() {
return extendsBound;
}
/**
* Returns the upper bound of this wildcard. If no upper bound is explicitly declared,
* returns the upper bound of the type variable to which the wildcard is bound.
*
* @return the upper bound of this wildcard. If no upper bound is explicitly declared,
* returns the upper bound of the type variable to which the wildcard is bound.
*/
public AnnotatedTypeMirror getExtendsBound() {
if (extendsBound == null) {
BoundsInitializer.initializeExtendsBound(this);
fixupBoundAnnotations();
}
return this.extendsBound;
}
private void fixupBoundAnnotations() {
if (!this.getAnnotationsField().isEmpty()) {
if (superBound != null) {
superBound.replaceAnnotations(this.getAnnotationsField());
}
if (extendsBound != null) {
extendsBound.replaceAnnotations(this.getAnnotationsField());
}
}
}
/**
* Sets type variable to which this wildcard is an argument. This method should only be
* called during initialization of the type.
*
* @param typeParameterElement the type variable to which this wildcard is an argument
*/
/*package-private*/ void setTypeVariable(TypeParameterElement typeParameterElement) {
this.typeVariable = (TypeVariable) typeParameterElement.asType();
}
/**
* Sets type variable to which this wildcard is an argument. This method should only be
* called during initialization of the type.
*
* @param typeVariable the type variable to which this wildcard is an argument
*/
/*package-private*/ void setTypeVariable(TypeVariable typeVariable) {
this.typeVariable = typeVariable;
}
/**
* Returns the type variable to which this wildcard is an argument. Used to initialize the
* upper bound of wildcards in raw types.
*
* @return the type variable to which this wildcard is an argument
*/
public TypeVariable getTypeVariable() {
return typeVariable;
}
@Override
public R accept(AnnotatedTypeVisitor v, P p) {
return v.visitWildcard(this, p);
}
@Override
public WildcardType getUnderlyingType() {
return (WildcardType) this.underlyingType;
}
@Override
public AnnotatedWildcardType deepCopy(boolean copyAnnotations) {
return (AnnotatedWildcardType) new AnnotatedTypeCopier(copyAnnotations).visit(this);
}
@Override
public AnnotatedWildcardType deepCopy() {
return deepCopy(true);
}
@Override
public AnnotatedWildcardType shallowCopy(boolean copyAnnotations) {
// Because wildcards can refer to themselves, they can't be shallow copied, so return a
// deep copy instead.
AnnotatedWildcardType type = deepCopy(true);
if (!copyAnnotations) {
type.getAnnotationsField().clear();
}
return type;
}
@Override
public AnnotatedWildcardType shallowCopy() {
return shallowCopy(true);
}
/**
* @see
* org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable#getErased()
*/
@Override
public AnnotatedTypeMirror getErased() {
// |? extends A&B| = |A|
return getExtendsBound().getErased();
}
// Remove the uninferredTypeArgument once method type
// argument inference and raw type handling is improved.
private boolean uninferredTypeArgument = false;
/**
* Set that this wildcard is from an uninferred type argument. This method should only be
* used within the framework. Once issues that depend on this hack, in particular Issue 979,
* are fixed, this must be removed.
*/
public void setUninferredTypeArgument() {
uninferredTypeArgument = true;
}
/**
* Returns whether or not this wildcard is a type argument for which inference failed to
* infer a type.
*
* @return true if this wildcard is a type argument for which inference failed
*/
public boolean isUninferredTypeArgument() {
return uninferredTypeArgument;
}
}
/**
* Represents an intersection type.
*
* For example: {@code MyObject & Serializable & Comparable}
*/
public static class AnnotatedIntersectionType extends AnnotatedTypeMirror {
/**
* A list of the bounds of this which are also its direct super types.
*
* Is set by {@link #shallowCopy}.
*/
protected List bounds;
/**
* Creates an {@code AnnotatedIntersectionType} with the underlying type {@code type}. The
* result contains no annotations.
*
* @param type underlying kind of this type
* @param atypeFactory the factory used to construct this intersection type
*/
private AnnotatedIntersectionType(
IntersectionType type, AnnotatedTypeFactory atypeFactory) {
super(type, atypeFactory);
}
/**
* {@inheritDoc}
*
* Also, copies {@code a} to all the bounds.
*
* @param annotation the annotation to add
*/
@Override
public void addAnnotation(AnnotationMirror annotation) {
super.addAnnotation(annotation);
fixupBoundAnnotations();
}
@Override
public boolean removeAnnotation(AnnotationMirror a) {
boolean ret = super.removeAnnotation(a);
if (bounds != null) {
for (AnnotatedTypeMirror bound : bounds) {
ret |= bound.removeAnnotation(a);
}
}
return ret;
}
/**
* Copies {@link #primaryAnnotations} to all the bounds, replacing any existing annotations
* in the same hierarchy.
*/
private void fixupBoundAnnotations() {
if (!this.getAnnotationsField().isEmpty()) {
AnnotationMirrorSet newAnnos = this.getAnnotationsField();
if (bounds != null) {
for (AnnotatedTypeMirror bound : bounds) {
bound.replaceAnnotations(newAnnos);
}
}
}
}
@Override
public R accept(AnnotatedTypeVisitor v, P p) {
return v.visitIntersection(this, p);
}
@Override
public IntersectionType getUnderlyingType() {
return (IntersectionType) super.getUnderlyingType();
}
@Override
public AnnotatedIntersectionType deepCopy(boolean copyAnnotations) {
return (AnnotatedIntersectionType) new AnnotatedTypeCopier(copyAnnotations).visit(this);
}
@Override
public AnnotatedIntersectionType deepCopy() {
return deepCopy(true);
}
@Override
public AnnotatedIntersectionType shallowCopy(boolean copyAnnotations) {
AnnotatedIntersectionType type =
new AnnotatedIntersectionType((IntersectionType) underlyingType, atypeFactory);
if (copyAnnotations) {
type.addAnnotations(this.getAnnotationsField());
}
type.bounds = this.bounds;
return type;
}
@Override
public AnnotatedIntersectionType shallowCopy() {
return shallowCopy(true);
}
/**
* {@inheritDoc}
*
* This returns the same types as {@link #getBounds()}.
*
* @return the direct super types of this
*/
@Override
public List directSupertypes() {
return getBounds();
}
/**
* This returns the bounds of the intersection type. Although only declared types can appear
* in an explicitly written intersections, during capture conversion, intersections with
* other kinds of types are created.
*
*
This returns the same types as {@link #directSupertypes()}.
*
* @return the bounds of this, which are also the direct super types of this
*/
public List getBounds() {
if (bounds == null) {
List ubounds =
((IntersectionType) underlyingType).getBounds();
List res =
CollectionsPlume.mapList(
(TypeMirror bnd) -> createType(bnd, atypeFactory, false), ubounds);
bounds = Collections.unmodifiableList(res);
fixupBoundAnnotations();
}
return bounds;
}
/**
* Sets the bounds.
*
* @param bounds a list of bounds to be captured by this method
*/
public void setBounds(List bounds) {
this.bounds = bounds;
}
/**
* Copy the first annotation (in each hierarchy) on a bound to the primary annotation
* location of the intersection type.
*
* For example, in the type {@code @NonNull Object & @Initialized @Nullable
* Serializable}, {@code @Nullable} and {@code @Initialized} are copied to the primary
* annotation location.
*/
public void copyIntersectionBoundAnnotations() {
AnnotationMirrorSet annos = new AnnotationMirrorSet();
for (AnnotatedTypeMirror bound : getBounds()) {
for (AnnotationMirror a : bound.getAnnotations()) {
if (atypeFactory.getQualifierHierarchy().findAnnotationInSameHierarchy(annos, a)
== null) {
annos.add(a);
}
}
}
addAnnotations(annos);
}
}
// TODO: Ensure union types are handled everywhere.
// TODO: Should field "annotations" contain anything?
public static class AnnotatedUnionType extends AnnotatedTypeMirror {
/**
* Creates a new AnnotatedUnionType.
*
* @param type underlying kind of this type
* @param atypeFactory type factory
*/
private AnnotatedUnionType(UnionType type, AnnotatedTypeFactory atypeFactory) {
super(type, atypeFactory);
}
@Override
public R accept(AnnotatedTypeVisitor v, P p) {
return v.visitUnion(this, p);
}
@Override
public AnnotatedUnionType deepCopy(boolean copyAnnotations) {
return (AnnotatedUnionType) new AnnotatedTypeCopier(copyAnnotations).visit(this);
}
@Override
public AnnotatedUnionType deepCopy() {
return deepCopy(true);
}
@Override
public AnnotatedUnionType shallowCopy(boolean copyAnnotations) {
AnnotatedUnionType type =
new AnnotatedUnionType((UnionType) underlyingType, atypeFactory);
if (copyAnnotations) {
type.addAnnotations(this.getAnnotationsField());
}
type.alternatives = this.alternatives;
return type;
}
@Override
public AnnotatedUnionType shallowCopy() {
return shallowCopy(true);
}
/**
* The types that are unioned to form this AnnotatedUnionType.
*
* Is set by {@link #getAlternatives} and {@link #shallowCopy}.
*/
protected @MonotonicNonNull List alternatives;
/**
* Returns the types that are unioned to form this AnnotatedUnionType.
*
* @return the types that are unioned to form this AnnotatedUnionType
*/
public List getAlternatives() {
if (alternatives == null) {
List ualts = ((UnionType) underlyingType).getAlternatives();
List res =
CollectionsPlume.mapList(
(TypeMirror alt) ->
(AnnotatedDeclaredType)
createType(alt, atypeFactory, false),
ualts);
alternatives = Collections.unmodifiableList(res);
}
return alternatives;
}
}
/**
* This method returns a list of AnnotatedTypeMirrors where the Java type of each ATM is an
* immediate supertype (class or interface) of the Java type of this. The interface types, if
* any, appear at the end of the list. If the directSuperType has type arguments, then the
* annotations on those type arguments are taken with proper translation from the declaration of
* the Java type of this.
*
* For example,
*
*
* {@code class B { ... } }
* {@code class A extends B<@NonNull String> { ... } }
* {@code @Nullable A a;}
*
*
* The direct supertype of the ATM {@code @Nullable A} is {@code @Nullable B<@NonNull String>}.
*
* An example with more complex type arguments:
*
*
* {@code class D { ... } }
* {@code class A extends D { ... } }
* {@code @Nullable A<@NonNull String, @NonNull Object> a;}
*
*
* The direct supertype of the ATM {@code @Nullable A<@NonNull String, @NonNull Object>} is
* {@code @Nullable B<@NonNull Object, @NonNull String>}.
*
* An example with more than one direct supertype:
*
*
* {@code class B implements List { ... } }
* {@code class A extends B<@NonNull String> implements List { ... } }
* {@code @Nullable A a;}
*
*
* The direct supertypes of the ATM {@code @Nullable A} are {@code @Nullable B <@NonNull
* String>} and {@code @Nullable List<@NonNull Integer>}.
*
* @return the immediate supertypes of this
* @see Types#directSupertypes(TypeMirror)
*/
public List directSupertypes() {
return SupertypeFinder.directSupertypes(this);
}
/**
* Returns true if this type has a primary annotation in the same hierarchy as {@code
* annotation}.
*
* @param annotation the qualifier hierarchy to check for
* @return true iff this type has a primary annotation in the same hierarchy as {@code
* annotation}.
* @deprecated use {@link #hasAnnotationInHierarchy(AnnotationMirror)}
*/
@Deprecated // 2023-06-15
public boolean isAnnotatedInHierarchy(AnnotationMirror annotation) {
return hasAnnotationInHierarchy(annotation);
}
/** An ERROR TypeKind was found. */
@SuppressWarnings("serial")
public static class ErrorTypeKindException extends Error {
/**
* Creates an ErrorTypeKindException.
*
* @param format format string
* @param args arguments to the format string
*/
@FormatMethod
public ErrorTypeKindException(String format, Object... args) {
super(String.format(format, args));
}
}
}