package org.checkerframework.framework.type;
/*>>>
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.checker.interning.qual.*;
*/
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
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;
import org.checkerframework.dataflow.qual.Pure;
import org.checkerframework.dataflow.qual.SideEffectFree;
import org.checkerframework.framework.qual.PolyAll;
import org.checkerframework.framework.type.visitor.AnnotatedTypeVisitor;
import org.checkerframework.framework.util.AnnotatedTypes;
import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.ElementUtils;
import org.checkerframework.javacutil.ErrorReporter;
/**
* Represents an annotated type in the Java programming language. Types include primitive types,
* declared types (class and interface types), array types, type variables, and the null type. Also
* represented are wildcard type arguments, the signature and return types of executables, and
* pseudo-types corresponding to packages and to the keyword {@code void}.
*
* <p>Types should be compared using the utility methods in {@link AnnotatedTypes}. There is no
* guarantee that any particular type will always be represented by the same object.
*
* <p>To implement operations based on the class of an {@code AnnotatedTypeMirror} object, either
* use a visitor or use the result of the {@link #getKind()} method.
*
* @see TypeMirror
*/
public abstract class AnnotatedTypeMirror {
/**
* Creates the appropriate AnnotatedTypeMirror specific wrapper for the provided type
*
* @param isDeclaration true if the result should is a type declaration
*/
public static AnnotatedTypeMirror createType(
TypeMirror type, AnnotatedTypeFactory atypeFactory, boolean isDeclaration) {
if (type == null) {
ErrorReporter.errorAbort(
"AnnotatedTypeMirror.createType: input type must not be null!");
return 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:
ErrorReporter.errorAbort(
"AnnotatedTypeMirror.createType: input should type-check already! Found error type: "
+ type);
return null; // dead code
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;
}
ErrorReporter.errorAbort(
"AnnotatedTypeMirror.createType: unidentified type "
+ type
+ " ("
+ type.getKind()
+ ")");
return null; // dead code
}
/*if (jctype.isAnnotated()) {
result.addAnnotations(jctype.getAnnotationMirrors());
}*/
return result;
}
protected static final EqualityAtmComparer equalityComparer = new EqualityAtmComparer();
protected static final HashcodeAtmVisitor hashcodeVisitor = new HashcodeAtmVisitor();
/** The factory to use for lazily creating annotated types. */
protected final AnnotatedTypeFactory atypeFactory;
/** Actual type wrapped with this AnnotatedTypeMirror */
protected final TypeMirror actualType;
/** 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. JSR308 is pushing to have this change.
private final Set<AnnotationMirror> annotations = AnnotationUtils.createAnnotationSet();
/** The explicitly written annotations on this type. */
// TODO: use this to cache the result once computed? For generic types?
// protected final Set<AnnotationMirror> explicitannotations = AnnotationUtils.createAnnotationSet();
/**
* Constructor for AnnotatedTypeMirror.
*
* @param type the underlying type
* @param atypeFactory used to create further types and to access global information (Types,
* Elements, ...)
*/
private AnnotatedTypeMirror(TypeMirror type, AnnotatedTypeFactory atypeFactory) {
this.actualType = type;
assert atypeFactory != null;
this.atypeFactory = atypeFactory;
}
@Override
public final boolean equals(Object o) {
if (o == this) {
return true;
}
if (o == null || !(o instanceof AnnotatedTypeMirror)) {
return false;
}
return equalityComparer.visit(this, (AnnotatedTypeMirror) o, null);
}
@Pure
@Override
public final int hashCode() {
return hashcodeVisitor.visit(this);
}
/**
* Applies a visitor to this type.
*
* @param <R> the return type of the visitor's methods
* @param <P> 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, P> R accept(AnnotatedTypeVisitor<R, P> v, P p);
/**
* Returns the {@code kind} of this type
*
* @return the kind of this type
*/
public TypeKind getKind() {
return actualType.getKind();
}
/**
* Returns the underlying unannotated Java type, which this wraps
*
* @return the underlying type
*/
public TypeMirror getUnderlyingType() {
return actualType;
}
/**
* Returns true if this type mirror represents a declaration, rather than a use, of a type.
*
* <p>For example, {@code class List<T> { ... }} declares a new type {@code List<T>}, while
* {@code List<Integer>} 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 an annotation from the given sub-hierarchy targets this type.
*
* <p>It doesn't account for annotations in deep types (type arguments, array components, etc).
*
* @param p the qualifier hierarchy to check for
* @return true iff an annotation from the same hierarchy as p is present
*/
public boolean isAnnotatedInHierarchy(AnnotationMirror p) {
return getAnnotationInHierarchy(p) != null;
}
/**
* Returns an annotation from the given sub-hierarchy, if such an annotation targets this type;
* otherwise returns null.
*
* <p>It doesn't account for annotations in deep types (type arguments, array components, etc).
*
* @param p the qualifier hierarchy to check for
* @return an annotation from the same hierarchy as p if present
*/
public AnnotationMirror getAnnotationInHierarchy(AnnotationMirror p) {
AnnotationMirror aliased = p;
if (!atypeFactory.isSupportedQualifier(aliased)) {
aliased = atypeFactory.aliasedAnnotation(p);
}
if (atypeFactory.isSupportedQualifier(aliased)) {
QualifierHierarchy qualHier = this.atypeFactory.getQualifierHierarchy();
AnnotationMirror anno = qualHier.findAnnotationInSameHierarchy(annotations, aliased);
if (anno != null) {
return anno;
}
}
return null;
}
/**
* Returns an annotation from the given sub-hierarchy, if such an annotation is present on this
* type or on its extends bounds; otherwise returns null.
*
* <p>It doesn't account for annotations in deep types (type arguments, array components, etc).
*
* @param p the qualifier hierarchy to check for
* @return an annotation from the same hierarchy as p if present
*/
public AnnotationMirror getEffectiveAnnotationInHierarchy(AnnotationMirror p) {
AnnotationMirror aliased = p;
if (!atypeFactory.isSupportedQualifier(aliased)) {
aliased = atypeFactory.aliasedAnnotation(p);
}
if (atypeFactory.isSupportedQualifier(aliased)) {
QualifierHierarchy qualHier = this.atypeFactory.getQualifierHierarchy();
AnnotationMirror anno =
qualHier.findAnnotationInSameHierarchy(getEffectiveAnnotations(), aliased);
if (anno != null) {
return anno;
}
}
return null;
}
/**
* Returns the annotations on this type.
*
* <p>It does not include annotations in deep types (type arguments, array components, etc).
*
* @return a unmodifiable set of the annotations on this
*/
public final Set<AnnotationMirror> getAnnotations() {
return Collections.unmodifiableSet(annotations);
}
/**
* Returns the annotations on this type.
*
* <p>It does not include annotations in deep types (type arguments, array components, etc).
*
* <p>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, directly
*/
protected final Set<AnnotationMirror> getAnnotationsField() {
return annotations;
}
/**
* 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).
*
* @return a set of the annotations on this
*/
public Set<AnnotationMirror> getEffectiveAnnotations() {
Set<AnnotationMirror> 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.toString();
return effectiveAnnotations;
}
/**
* Returns the actual annotation mirror used to annotate this type, whose name equals the passed
* annotationName if one exists, null otherwise.
*
* @return the annotation mirror for annotationName
*/
public AnnotationMirror getAnnotation(Name annotationName) {
assert annotationName != null : "Null annotationName in getAnnotation";
return getAnnotation(annotationName.toString().intern());
}
/**
* Returns the actual annotation mirror used to annotate this type, whose name equals the string
* argument if one exists, null otherwise.
*
* @return the annotation mirror for annotationStr
*/
public AnnotationMirror getAnnotation(/*@Interned*/ String annotationStr) {
assert annotationStr != null : "Null annotationName in getAnnotation";
for (AnnotationMirror anno : getAnnotations()) {
if (AnnotationUtils.areSameByName(anno, annotationStr)) {
return anno;
}
}
return null;
}
/**
* Returns the actual annotation mirror used to annotate this type, whose Class equals the
* passed annoClass if one exists, null otherwise.
*
* @param annoClass annotation class
* @return the annotation mirror for anno
*/
public AnnotationMirror getAnnotation(Class<? extends Annotation> annoClass) {
for (AnnotationMirror annoMirror : getAnnotations()) {
if (AnnotationUtils.areSameByClass(annoMirror, annoClass)) {
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:
*
* <p>{@code @MyExplicitAnno class MyClass { }; MyClass myClassInstance; }
*
* <p>the result of calling {@code
* atypeFactory.getAnnotatedType(variableTreeForMyClassInstance).getExplicitAnnotations()}
*
* <p>will not contain {@code @MyExplicitAnno}.
*
* @return the set of explicitly written annotations on this type that are supported by this
* checker
*/
public Set<AnnotationMirror> getExplicitAnnotations() {
// TODO JSR 308: The explicit type annotations should be always present
Set<AnnotationMirror> explicitAnnotations = AnnotationUtils.createAnnotationSet();
List<? extends AnnotationMirror> typeAnnotations =
this.getUnderlyingType().getAnnotationMirrors();
Set<? extends AnnotationMirror> validAnnotations =
atypeFactory.getQualifierHierarchy().getTypeQualifiers();
for (AnnotationMirror explicitAnno : typeAnnotations) {
for (AnnotationMirror validAnno : validAnnotations) {
if (AnnotationUtils.areSameIgnoringValues(explicitAnno, validAnno)) {
explicitAnnotations.add(explicitAnno);
}
}
}
return explicitAnnotations;
}
/**
* Determines whether this type contains the given annotation. This method considers the
* annotation's values, that is, if the type is "@A("s") @B(3) Object" a call with "@A("t") or
* "@A" will return false, whereas a call with "@B(3)" will return true.
*
* <p>In contrast to {@link #hasAnnotationRelaxed(AnnotationMirror)} this method also compares
* annotation values.
*
* @param a the annotation to check for
* @return true iff the type contains the annotation {@code a}
* @see #hasAnnotationRelaxed(AnnotationMirror)
*/
public boolean hasAnnotation(AnnotationMirror a) {
return AnnotationUtils.containsSame(getAnnotations(), a);
}
/**
* Determines whether this type contains the given annotation.
*
* @param a the annotation name to check for
* @return true iff the type contains the annotation {@code a}
* @see #hasAnnotationRelaxed(AnnotationMirror)
*/
public boolean hasAnnotation(Name a) {
return getAnnotation(a) != null;
}
/**
* Determines whether this type contains an annotation with the same annotation type as a
* particular annotation. 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<? extends Annotation> a) {
return getAnnotation(a) != null;
}
/**
* Returns the actual effective annotation mirror used to annotate this type, whose Class equals
* the passed annoClass if one exists, null otherwise.
*
* @param annoClass annotation class
* @return the annotation mirror for anno
*/
public AnnotationMirror getEffectiveAnnotation(Class<? extends Annotation> annoClass) {
for (AnnotationMirror annoMirror : getEffectiveAnnotations()) {
if (AnnotationUtils.areSameByClass(annoMirror, annoClass)) {
return annoMirror;
}
}
return null;
}
/**
* A version of hasAnnotation that considers annotations on the upper bound of wildcards and
* type variables.
*
* @see #hasAnnotation(Class)
*/
public boolean hasEffectiveAnnotation(Class<? extends Annotation> a) {
return getEffectiveAnnotation(a) != null;
}
/**
* A version of hasAnnotation that considers annotations on the upper bound of wildcards and
* type variables.
*
* @see #hasAnnotation(AnnotationMirror)
*/
public boolean hasEffectiveAnnotation(AnnotationMirror a) {
return AnnotationUtils.containsSame(getEffectiveAnnotations(), a);
}
/**
* Determines whether this type contains the given annotation explicitly written at declaration.
* This method considers the annotation's values, that is, if the type is "@A("s") @B(3) Object"
* a call with "@A("t") or "@A" will return false, whereas a call with "@B(3)" will return true.
*
* <p>In contrast to {@link #hasExplicitAnnotationRelaxed(AnnotationMirror)} this method also
* compares annotation values.
*
* <p>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);
}
/**
* Determines whether this type contains an annotation with the same annotation type as a
* particular annotation. This method does not consider an annotation's values, that is, if the
* type is "@A("s") @B(3) Object" a call with "@A("t"), "@A", or "@B" will return true.
*
* @param a the annotation to check for
* @return true iff the type contains an annotation with the same type as the annotation given
* by {@code a}
* @see #hasAnnotation(AnnotationMirror)
*/
public boolean hasAnnotationRelaxed(AnnotationMirror a) {
return AnnotationUtils.containsSameIgnoringValues(getAnnotations(), a);
}
/**
* A version of hasAnnotationRelaxed that considers annotations on the upper bound of wildcards
* and type variables.
*
* @see #hasAnnotationRelaxed(AnnotationMirror)
*/
public boolean hasEffectiveAnnotationRelaxed(AnnotationMirror a) {
return AnnotationUtils.containsSameIgnoringValues(getEffectiveAnnotations(), a);
}
/**
* A version of hasAnnotationRelaxed that only considers annotations that are explicitly written
* on the type.
*
* <p>See the documentation for {@link #getExplicitAnnotations()} for details on which explicit
* annotations are not included.
*
* @see #hasAnnotationRelaxed(AnnotationMirror)
* @see #getExplicitAnnotations()
*/
public boolean hasExplicitAnnotationRelaxed(AnnotationMirror a) {
return AnnotationUtils.containsSameIgnoringValues(getExplicitAnnotations(), a);
}
/**
* Determines whether 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.
*
* <p>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<? extends Annotation> a) {
return AnnotationUtils.containsSameIgnoringValues(
getExplicitAnnotations(), getAnnotation(a));
}
/**
* Adds an annotation to this type. Only annotations supported by the type factory are added.
*
* @param a the annotation to add
*/
public void addAnnotation(AnnotationMirror a) {
if (a == null) {
ErrorReporter.errorAbort(
"AnnotatedTypeMirror.addAnnotation: null is not a valid annotation.");
}
if (atypeFactory.isSupportedQualifier(a)) {
this.annotations.add(a);
} else {
AnnotationMirror aliased = atypeFactory.aliasedAnnotation(a);
if (atypeFactory.isSupportedQualifier(aliased)) {
addAnnotation(aliased);
}
}
}
/**
* Adds an annotation to this type, removing any existing annotation from the same qualifier
* hierarchy first.
*
* @param a the annotation to add
*/
public void replaceAnnotation(AnnotationMirror a) {
if (!AnnotationUtils.areSameByClass(a, PolyAll.class)) {
this.removeAnnotationInHierarchy(a);
}
this.addAnnotation(a);
}
/**
* Adds an annotation to this type.
*
* @param a the class of the annotation to add
*/
public void addAnnotation(Class<? extends Annotation> a) {
AnnotationMirror anno = AnnotationUtils.fromClass(atypeFactory.elements, a);
addAnnotation(anno);
}
/**
* Adds multiple annotations to this type.
*
* @param annotations the annotations to add
*/
public void addAnnotations(Iterable<? extends AnnotationMirror> annotations) {
for (AnnotationMirror a : annotations) {
this.addAnnotation(a);
}
}
/**
* Adds those annotations to the current type, for which no annotation from the same qualifier
* hierarchy is present.
*
* @param annotations the annotations to add
*/
public void addMissingAnnotations(Iterable<? extends AnnotationMirror> annotations) {
for (AnnotationMirror a : annotations) {
if (!this.isAnnotatedInHierarchy(a)) {
this.addAnnotation(a);
}
}
}
/**
* Adds multiple annotations to this type, removing any existing annotations from the same
* qualifier hierarchy first.
*
* @param replAnnos the annotations to replace
*/
public void replaceAnnotations(Iterable<? extends AnnotationMirror> replAnnos) {
for (AnnotationMirror a : replAnnos) {
this.replaceAnnotation(a);
}
}
/**
* Removes an 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
*/
public boolean removeAnnotation(AnnotationMirror a) {
// Going from the AnnotationMirror to its name and then calling
// getAnnotation ensures that we get the canonical AnnotationMirror that can be
// removed.
// TODO: however, this also means that if we are annotated with "@I(1)" and
// remove "@I(2)" it will be removed. Is this what we want?
// It's currently necessary for the Lock Checker.
AnnotationMirror anno = getAnnotation(AnnotationUtils.annotationName(a));
if (anno != null) {
return annotations.remove(anno);
} else {
return false;
}
}
public boolean removeAnnotation(Class<? extends Annotation> a) {
AnnotationMirror anno = AnnotationUtils.fromClass(atypeFactory.elements, a);
if (anno == null || !atypeFactory.isSupportedQualifier(anno)) {
ErrorReporter.errorAbort(
"AnnotatedTypeMirror.removeAnnotation called with un-supported class: " + a);
}
return removeAnnotation(anno);
}
/**
* Remove any 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
*/
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
*/
public boolean removeNonTopAnnotationInHierarchy(AnnotationMirror a) {
AnnotationMirror prev = this.getAnnotationInHierarchy(a);
QualifierHierarchy qualHier = this.atypeFactory.getQualifierHierarchy();
if (prev != null && !prev.equals(qualHier.getTopAnnotation(a))) {
return this.removeAnnotation(prev);
}
return false;
}
/**
* Removes multiple 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
*/
public boolean removeAnnotations(Iterable<? extends AnnotationMirror> annotations) {
boolean changed = false;
for (AnnotationMirror a : annotations) {
changed |= this.removeAnnotation(a);
}
return changed;
}
/**
* Removes all primary annotations on this type. Make sure to add an annotation again, e.g.
* Unqualified.
*
* <p>This method should only be used in very specific situations. For individual type systems,
* it is generally better to use {@link #removeAnnotation(AnnotationMirror)} and similar
* methods.
*/
public void clearAnnotations() {
annotations.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 the this type, according to JLS specifications.
*
* @see <a
* href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.6">https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.6</a>
* @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.
*
* <p>Note: deepCopy provides two important properties in the returned copy:
*
* <ol>
* <li>Structure preservation -- The exact structure of the original AnnotatedTypeMirror is
* preserved in the copy including all component types.
* <li>Annotation preservation -- All of the annotations from the original AnnotatedTypeMirror
* and its components have been copied to the new type.
* </ol>
*
* 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(final boolean copyAnnotations);
/**
* @return a deep copy of this type with annotations
* @see #deepCopy(boolean)
*/
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 copie.
*
* @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.
*
* @see #shallowCopy(boolean)
*/
public abstract AnnotatedTypeMirror shallowCopy();
protected static AnnotatedDeclaredType createTypeOfObject(AnnotatedTypeFactory atypeFactory) {
AnnotatedDeclaredType objectType =
atypeFactory.fromElement(
atypeFactory.elements.getTypeElement(Object.class.getCanonicalName()));
objectType.declaration = false;
return objectType;
}
/** Represents a declared type (whether class or interface). */
public static class AnnotatedDeclaredType extends AnnotatedTypeMirror {
/** Parametrized Type Arguments */
protected List<AnnotatedTypeMirror> 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.
*
* <p>Ideally, the field would be final. However, when we determine the supertype of a raw
* type, we need to set wasRaw for the supertype.
*/
private boolean wasRaw;
/** The enclosing Type */
protected AnnotatedDeclaredType enclosingType;
protected List<AnnotatedDeclaredType> supertypes = null;
private boolean declaration;
/**
* Constructor for this type
*
* @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();
wasRaw = !declty.getTypeArguments().isEmpty() && type.getTypeArguments().isEmpty();
TypeMirror encl = type.getEnclosingType();
if (encl.getKind() == TypeKind.DECLARED) {
this.enclosingType =
(AnnotatedDeclaredType) createType(encl, atypeFactory, declaration);
// Force instantiation of type arguments of enclosing type.
this.enclosingType.getTypeArguments();
} else if (encl.getKind() != TypeKind.NONE) {
ErrorReporter.errorAbort(
"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;
List<AnnotatedTypeMirror> newArgs = new ArrayList<>();
for (AnnotatedTypeMirror arg : result.getTypeArguments()) {
switch (arg.getKind()) {
case TYPEVAR:
AnnotatedTypeVariable paramTypevar = (AnnotatedTypeVariable) arg;
newArgs.add(paramTypevar.asUse());
break;
case WILDCARD:
AnnotatedWildcardType paramWildcard = (AnnotatedWildcardType) arg;
newArgs.add(paramWildcard.asUse());
break;
default:
newArgs.add(arg);
}
}
result.setTypeArguments(newArgs);
return result;
}
@Override
public <R, P> R accept(AnnotatedTypeVisitor<R, P> v, P p) {
return v.visitDeclared(this, p);
}
/**
* Sets the type arguments on this type
*
* @param ts the type arguments
*/
// WMD
public void setTypeArguments(List<? extends AnnotatedTypeMirror> ts) {
if (ts == null || ts.isEmpty()) {
typeArgs = Collections.emptyList();
} else {
if (isDeclaration()) {
// TODO: check that all args are really declarations
typeArgs = Collections.unmodifiableList(ts);
} else {
List<AnnotatedTypeMirror> uses = new ArrayList<>();
for (AnnotatedTypeMirror t : ts) {
uses.add(t.asUse());
}
typeArgs = Collections.unmodifiableList(uses);
}
}
}
/** @return the type argument for this type */
public List<AnnotatedTypeMirror> getTypeArguments() {
if (typeArgs == null) {
typeArgs = new ArrayList<AnnotatedTypeMirror>();
if (wasRaw()) {
// TODO: This doesn't handle recursive type parameter
// e.g. class Pair<Y extends List<Y>> { ... }
// Type argument inference for raw types can be improved. See Issue #635.
// https://github.com/typetools/checker-framework/issues/635
AnnotatedDeclaredType declaration =
atypeFactory.fromElement((TypeElement) getUnderlyingType().asElement());
for (AnnotatedTypeMirror typeParam : declaration.getTypeArguments()) {
AnnotatedWildcardType wct =
atypeFactory.getUninferredWildcardType(
(AnnotatedTypeVariable) typeParam);
typeArgs.add(wct);
}
} else if (!getUnderlyingType().getTypeArguments().isEmpty()) { // lazy init
for (TypeMirror t : getUnderlyingType().getTypeArguments()) {
typeArgs.add(createType(t, atypeFactory, declaration));
}
}
typeArgs = Collections.unmodifiableList(typeArgs);
}
return typeArgs;
}
/**
* Returns true if the type was raw, that is, type arguments were not provided but instead
* inferred.
*
* @return true iff the type was raw
*/
public boolean wasRaw() {
return wasRaw;
}
/**
* Set the wasRaw flag to true. This should only be necessary when determining the
* supertypes of a raw type.
*/
protected void setWasRaw() {
this.wasRaw = true;
}
@Override
public DeclaredType getUnderlyingType() {
return (DeclaredType) actualType;
}
@Override
public List<AnnotatedDeclaredType> directSuperTypes() {
if (supertypes == null) {
supertypes = Collections.unmodifiableList(SupertypeFinder.directSuperTypes(this));
}
return supertypes;
}
/*
* Return the direct super types field without lazy initialization;
* originally to prevent infinite recursion in IGJATF.postDirectSuperTypes.
*
* TODO: find a nicer way, see the single caller in QualifierDefaults
* for comment.
*/
public List<AnnotatedDeclaredType> directSuperTypesField() {
return supertypes;
}
@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() {
// 1. |G<T_1, ..., T_n>| = |G|
// 2. |T.C| = |T|.C
if (!getTypeArguments().isEmpty()) {
// Handle case 1.
AnnotatedDeclaredType rType =
(AnnotatedDeclaredType)
AnnotatedTypeMirror.createType(
atypeFactory.types.erasure(actualType),
atypeFactory,
declaration);
rType.addAnnotations(getAnnotations());
rType.setTypeArguments(Collections.<AnnotatedTypeMirror>emptyList());
return rType.getErased();
} else if ((getEnclosingType() != null)
&& (getEnclosingType().getKind() != TypeKind.NONE)) {
// Handle case 2
// Shallow copy provides a fresh type when there are no type arguments
// and we set the enclosing type
// Therefore, we do not need to use deepCopy
AnnotatedDeclaredType rType = shallowCopy();
AnnotatedDeclaredType et = getEnclosingType();
rType.setEnclosingType(et.getErased());
return rType;
} else {
return this.deepCopy();
}
}
/* Using this equals method resulted in an infinite recursion
* with type variables. TODO: Keep track of visited type variables?
@Override
public boolean equals(Object o) {
boolean res = super.equals(o);
if (res && (o instanceof AnnotatedDeclaredType)) {
AnnotatedDeclaredType dt = (AnnotatedDeclaredType) o;
List<AnnotatedTypeMirror> mytas = this.getTypeArguments();
List<AnnotatedTypeMirror> othertas = dt.getTypeArguments();
for (int i = 0; i < mytas.size(); ++i) {
if (!mytas.get(i).equals(othertas.get(i))) {
System.out.println("in AnnotatedDeclaredType; this: " + this + " and " + o);
res = false;
break;
}
}
}
return res;
}
*/
/** Sets the enclosing type */
/*default-visibility*/ void setEnclosingType(AnnotatedDeclaredType enclosingType) {
this.enclosingType = enclosingType;
}
/**
* Returns the enclosing type, as in the type of {@code A} in the type {@code A.B}.
*
* @return enclosingType the enclosing type
*/
public 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 ExecutableElement element;
private AnnotatedExecutableType(ExecutableType type, AnnotatedTypeFactory factory) {
super(type, factory);
}
protected final List<AnnotatedTypeMirror> paramTypes = new ArrayList<AnnotatedTypeMirror>();
protected AnnotatedDeclaredType receiverType;
protected AnnotatedTypeMirror returnType;
protected final List<AnnotatedTypeMirror> throwsTypes =
new ArrayList<AnnotatedTypeMirror>();
protected final List<AnnotatedTypeVariable> typeVarTypes =
new ArrayList<AnnotatedTypeVariable>();
/** @return true if this type represents a varargs method */
public boolean isVarArgs() {
return this.element.isVarArgs();
}
@Override
public <R, P> R accept(AnnotatedTypeVisitor<R, P> v, P p) {
return v.visitExecutable(this, p);
}
@Override
public ExecutableType getUnderlyingType() {
return (ExecutableType) this.actualType;
}
/* TODO: it never makes sense to add annotations to an executable type -
* instead, they should be added to the right component.
* For simpler, more regular use, we might want to allow querying for annotations.
*
@Override
public void addAnnotations(Iterable<? extends AnnotationMirror> annotations) {
// Thread.dumpStack();
super.addAnnotations(annotations);
}
@Override
public void addAnnotation(AnnotationMirror a) {
// Thread.dumpStack();
super.addAnnotation(a);
}
@Override
public void addAnnotation(Class<? extends Annotation> a) {
// Thread.dumpStack();
super.addAnnotation(a);
}
@Override
public Set<AnnotationMirror> getAnnotations() {
Thread.dumpStack();
return null;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof AnnotatedExecutableType)) {
return false;
}
// TODO compare components
return true;
}
*/
/**
* Sets the parameter types of this executable type
*
* @param params the parameter types
*/
void setParameterTypes(List<? extends AnnotatedTypeMirror> params) {
paramTypes.clear();
paramTypes.addAll(params);
}
/** @return the parameter types of this executable type */
public List<AnnotatedTypeMirror> getParameterTypes() {
if (paramTypes.isEmpty()
&& !((ExecutableType) actualType).getParameterTypes().isEmpty()) { // lazy init
for (TypeMirror t : ((ExecutableType) actualType).getParameterTypes()) {
paramTypes.add(createType(t, atypeFactory, false));
}
}
return Collections.unmodifiableList(paramTypes);
}
/**
* Sets the return type of this executable type
*
* @param returnType the return type
*/
void setReturnType(AnnotatedTypeMirror returnType) {
this.returnType = returnType;
}
/**
* 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 (returnType == null
&& element != null
&& ((ExecutableType) actualType).getReturnType() != null) { // lazy init
TypeMirror aret = ((ExecutableType) actualType).getReturnType();
if (((MethodSymbol) element).isConstructor()) {
// For constructors, the underlying return type is void.
// Take the type of the enclosing class instead.
aret = element.getEnclosingElement().asType();
}
returnType = createType(aret, atypeFactory, false);
}
return returnType;
}
/**
* Sets the receiver type on this executable type
*
* @param receiverType the receiver type
*/
void setReceiverType(AnnotatedDeclaredType receiverType) {
this.receiverType = receiverType;
}
/**
* @return the receiver type of this executable type; null for static methods and
* constructors of top-level classes
*/
public /*@Nullable*/ AnnotatedDeclaredType getReceiverType() {
if (receiverType == null
// Static methods don't have a receiver
&& !ElementUtils.isStatic(getElement())
// Array constructors should also not have a receiver. Array members have a getEnclosingElement().getEnclosingElement() of NONE
&& (!(getElement().getKind() == ElementKind.CONSTRUCTOR
&& getElement()
.getEnclosingElement()
.getSimpleName()
.toString()
.equals("Array")
&& getElement()
.getEnclosingElement()
.getEnclosingElement()
.asType()
.getKind()
== TypeKind.NONE))
// Top-level constructors don't have a receiver
&& (getElement().getKind() != ElementKind.CONSTRUCTOR
|| getElement().getEnclosingElement().getEnclosingElement().getKind()
!= ElementKind.PACKAGE)) {
TypeElement encl = ElementUtils.enclosingClass(getElement());
if (getElement().getKind() == ElementKind.CONSTRUCTOR) {
// Can only reach this branch if we're the constructor of a nested class
encl = ElementUtils.enclosingClass(encl.getEnclosingElement());
}
AnnotatedTypeMirror type = createType(encl.asType(), atypeFactory, false);
assert type instanceof AnnotatedDeclaredType;
receiverType = (AnnotatedDeclaredType) type;
}
return receiverType;
}
/**
* Sets the thrown types of this executable type
*
* @param thrownTypes the thrown types
*/
void setThrownTypes(List<? extends AnnotatedTypeMirror> thrownTypes) {
this.throwsTypes.clear();
this.throwsTypes.addAll(thrownTypes);
}
/** @return the thrown types of this executable type */
public List<AnnotatedTypeMirror> getThrownTypes() {
if (throwsTypes.isEmpty()
&& !((ExecutableType) actualType).getThrownTypes().isEmpty()) { // lazy init
for (TypeMirror t : ((ExecutableType) actualType).getThrownTypes()) {
throwsTypes.add(createType(t, atypeFactory, false));
}
}
return Collections.unmodifiableList(throwsTypes);
}
/**
* Sets the type variables associated with this executable type
*
* @param types the type variables of this executable type
*/
void setTypeVariables(List<AnnotatedTypeVariable> types) {
typeVarTypes.clear();
typeVarTypes.addAll(types);
}
/** @return the type variables of this executable type, if any */
public List<AnnotatedTypeVariable> getTypeVariables() {
if (typeVarTypes.isEmpty()
&& !((ExecutableType) actualType).getTypeVariables().isEmpty()) { // lazy init
for (TypeMirror t : ((ExecutableType) actualType).getTypeVariables()) {
typeVarTypes.add((AnnotatedTypeVariable) createType(t, atypeFactory, true));
}
}
return Collections.unmodifiableList(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());
type.setReceiverType(getReceiverType());
type.setReturnType(getReturnType());
type.setThrownTypes(getThrownTypes());
type.setTypeVariables(getTypeVariables());
return type;
}
@Override
public AnnotatedExecutableType shallowCopy() {
return shallowCopy(true);
}
public /*@NonNull*/ ExecutableElement getElement() {
return element;
}
public void setElement(/*@NonNull*/ 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 (getReceiverType() != null) {
type.setReceiverType(getReceiverType().getErased());
} else {
type.setReceiverType(null);
}
type.setReturnType(getReturnType().getErased());
type.setThrownTypes(erasureList(getThrownTypes()));
return type;
}
private List<AnnotatedTypeMirror> erasureList(Iterable<? extends AnnotatedTypeMirror> lst) {
List<AnnotatedTypeMirror> erased = new ArrayList<AnnotatedTypeMirror>();
for (AnnotatedTypeMirror t : lst) {
erased.add(t.getErased());
}
return erased;
}
}
/**
* 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, P> R accept(AnnotatedTypeVisitor<R, P> v, P p) {
return v.visitArray(this, p);
}
@Override
public ArrayType getUnderlyingType() {
return (ArrayType) this.actualType;
}
/**
* Sets the component type of this array
*
* @param type the component type
*/
// WMD
public void setComponentType(AnnotatedTypeMirror type) {
this.componentType = type;
}
/** @return the component type of this array */
public AnnotatedTypeMirror getComponentType() {
if (componentType == null) // lazy init
setComponentType(
createType(
((ArrayType) actualType).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) actualType, 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;
}
}
/**
* 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 a) {
super.addAnnotation(a);
fixupBoundAnnotations();
}
/**
* 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;
return result;
}
@Override
public <R, P> R accept(AnnotatedTypeVisitor<R, P> v, P p) {
return v.visitTypeVariable(this, p);
}
@Override
public TypeVariable getUnderlyingType() {
return (TypeVariable) this.actualType;
}
/**
* Set the lower bound of this variable type
*
* <p>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
*/
void setLowerBound(AnnotatedTypeMirror type) {
if (type == null || type.isDeclaration()) {
ErrorReporter.errorAbort(
"Lower bounds should never be null or a declaration.\n"
+ " new bound = "
+ type
+ "\n 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;
}
/** @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 actualType, 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<X extends @Nullable Object>.
// The type of "@Nullable X" has to be "@Nullable X extends @Nullable Object",
// because otherwise the annotations are inconsistent.
private void fixupBoundAnnotations() {
// We allow the above replacement first because primary annotations might not have annotations for
// all hierarchies, so we don't want to avoid placing bottom on the lower bound for those hierarchies that
// don't have a qualifier in primaryAnnotations
if (!this.getAnnotationsField().isEmpty()) {
if (upperBound != null) {
replaceUpperBoundAnnotations();
}
// 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(this.getAnnotationsField());
}
}
}
/**
* Replaces (or adds if none exist) the primary annotation of all upper bounds of typeVar,
* the AnnotatedTypeVariable with the annotations provided. The AnnotatedTypeVariable will
* only have multiple upper bounds if the upper bound is an intersection.
*/
private void replaceUpperBoundAnnotations() {
if (upperBound.getKind() == TypeKind.INTERSECTION) {
final List<AnnotatedDeclaredType> bounds =
((AnnotatedIntersectionType) upperBound).directSuperTypes();
for (final AnnotatedDeclaredType bound : bounds) {
bound.replaceAnnotations(this.getAnnotationsField());
}
} else {
upperBound.replaceAnnotations(this.getAnnotationsField());
}
}
/**
* Set the upper bound of this variable type
*
* @param type the upper bound type
*/
void setUpperBound(AnnotatedTypeMirror type) {
if (type == null || type.isDeclaration()) {
ErrorReporter.errorAbort(
"Upper bounds should never be null or a declaration.\n"
+ " new bound = "
+ type
+ "\n 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());
}
/** Used to terminate recursion into upper bounds. */
private boolean inUpperBounds = false;
@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) {
AnnotatedTypeVariable type =
new AnnotatedTypeVariable(
((TypeVariable) actualType), atypeFactory, declaration);
if (copyAnnotations) {
type.addAnnotations(this.getAnnotationsField());
}
if (!inUpperBounds) {
inUpperBounds = true;
type.inUpperBounds = true;
type.setUpperBound(getUpperBound().shallowCopy());
inUpperBounds = false;
type.inUpperBounds = false;
}
type.setLowerBound(getLowerBound().shallowCopy());
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.
*
* <pre>{@code <E extends T>, T extends S, S extends List<String>>}</pre>
*
* A call to getErased will return the type List
*
* @return the erasure of the upper bound of this type
* <p>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:
*
* <ul>
* <li>VOID -- corresponds to the keyword void.
* <li>PACKAGE -- the pseudo-type of a package element.
* <li>NONE -- used in other cases where no actual type is appropriate; for example, the
* superclass of java.lang.Object.
* </ul>
*/
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, P> R accept(AnnotatedTypeVisitor<R, P> v, P p) {
return v.visitNoType(this, p);
}
@Override
public NoType getUnderlyingType() {
return (NoType) this.actualType;
}
@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) actualType, 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, P> R accept(AnnotatedTypeVisitor<R, P> v, P p) {
return v.visitNull(this, p);
}
@Override
public NullType getUnderlyingType() {
return (NullType) this.actualType;
}
@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) actualType, 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, P> R accept(AnnotatedTypeVisitor<R, P> v, P p) {
return v.visitPrimitive(this, p);
}
@Override
public PrimitiveType getUnderlyingType() {
return (PrimitiveType) this.actualType;
}
@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) actualType, atypeFactory);
if (copyAnnotations) {
type.addAnnotations(this.getAnnotationsField());
}
return type;
}
@Override
public AnnotatedPrimitiveType shallowCopy() {
return shallowCopy(true);
}
}
/**
* Represents a wildcard type argument. Examples include:
*
* <p>? ? extends Number ? super T
*
* <p>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 {
/** SuperBound */
private AnnotatedTypeMirror superBound;
/** ExtendBound */
private AnnotatedTypeMirror extendsBound;
private AnnotatedWildcardType(WildcardType type, AnnotatedTypeFactory factory) {
super(type, factory);
}
@Override
public void addAnnotation(AnnotationMirror a) {
super.addAnnotation(a);
fixupBoundAnnotations();
}
/**
* Sets the super bound of this wild card
*
* @param type the type of the lower bound
*/
void setSuperBound(AnnotatedTypeMirror type) {
if (type == null || type.isDeclaration()) {
ErrorReporter.errorAbort(
"Super bounds should never be null or a declaration.\n"
+ " new bound = "
+ type
+ "\n type = "
+ this);
}
this.superBound = type;
fixupBoundAnnotations();
}
public AnnotatedTypeMirror getSuperBoundField() {
return superBound;
}
/**
* @return the lower bound of this wildcard. If no lower bound is explicitly declared,
* {@code null} is returned.
*/
public AnnotatedTypeMirror getSuperBound() {
if (superBound == null) {
BoundsInitializer.initializeSuperBound(this);
fixupBoundAnnotations();
}
return this.superBound;
}
/**
* Sets the upper bound of this wild card
*
* @param type the type of the upper bound
*/
void setExtendsBound(AnnotatedTypeMirror type) {
if (type == null || type.isDeclaration()) {
ErrorReporter.errorAbort(
"Extends bounds should never be null or a declaration.\n"
+ " new bound = "
+ type
+ "\n type = "
+ this);
}
this.extendsBound = type;
fixupBoundAnnotations();
}
public AnnotatedTypeMirror getExtendsBoundField() {
return extendsBound;
}
/**
* @return the upper bound of this wildcard. If no upper bound is explicitly declared, the
* upper bound of the type variable to which the wildcard is bound is used.
*/
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());
}
}
}
@Override
public <R, P> R accept(AnnotatedTypeVisitor<R, P> v, P p) {
return v.visitWildcard(this, p);
}
@Override
public WildcardType getUnderlyingType() {
return (WildcardType) this.actualType;
}
@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) {
AnnotatedWildcardType type =
new AnnotatedWildcardType((WildcardType) actualType, atypeFactory);
type.setExtendsBound(getExtendsBound().shallowCopy());
type.setSuperBound(getSuperBound().shallowCopy());
if (copyAnnotations) {
type.addAnnotations(this.getAnnotationsField());
}
type.uninferredTypeArgument = uninferredTypeArgument;
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 returns whether or not this wildcard is a type argument for which inference
* failed
*/
public boolean isUninferredTypeArgument() {
return uninferredTypeArgument;
}
}
public static class AnnotatedIntersectionType extends AnnotatedTypeMirror {
/**
* AnnotatedIntersectionTypes are created by type parameters whose bounds include an &.
* For example: {@code <T extends MyObject & Serializable & Comparable<MyObject>>}
*
* <p>The bound {@code MyObject & Serializable & Comparable} is an intersection type
* with direct supertypes [MyObject, Serializable, Comparable]
*
* @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);
}
@Override
public <R, P> R accept(AnnotatedTypeVisitor<R, P> v, P p) {
return v.visitIntersection(this, p);
}
@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) actualType, atypeFactory);
if (copyAnnotations) {
type.addAnnotations(this.getAnnotationsField());
}
type.supertypes = this.supertypes;
return type;
}
@Override
public AnnotatedIntersectionType shallowCopy() {
return shallowCopy(true);
}
protected List<AnnotatedDeclaredType> supertypes;
@Override
public List<AnnotatedDeclaredType> directSuperTypes() {
if (supertypes == null) {
List<? extends TypeMirror> ubounds = ((IntersectionType) actualType).getBounds();
List<AnnotatedDeclaredType> res =
new ArrayList<AnnotatedDeclaredType>(ubounds.size());
for (TypeMirror bnd : ubounds) {
res.add((AnnotatedDeclaredType) createType(bnd, atypeFactory, false));
}
supertypes = Collections.unmodifiableList(res);
}
return supertypes;
}
public List<AnnotatedDeclaredType> directSuperTypesField() {
return supertypes;
}
void setDirectSuperTypes(List<AnnotatedDeclaredType> supertypes) {
this.supertypes = new ArrayList<AnnotatedDeclaredType>(supertypes);
}
}
// TODO: Ensure union types are handled everywhere.
// TODO: Should field "annotations" contain anything?
public static class AnnotatedUnionType extends AnnotatedTypeMirror {
/**
* Constructor for this type
*
* @param type underlying kind of this type
* @param atypeFactory TODO
*/
private AnnotatedUnionType(UnionType type, AnnotatedTypeFactory atypeFactory) {
super(type, atypeFactory);
}
@Override
public <R, P> R accept(AnnotatedTypeVisitor<R, P> 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) actualType, atypeFactory);
if (copyAnnotations) {
type.addAnnotations(this.getAnnotationsField());
}
type.alternatives = this.alternatives;
return type;
}
@Override
public AnnotatedUnionType shallowCopy() {
return shallowCopy(true);
}
protected List<AnnotatedDeclaredType> alternatives;
public List<AnnotatedDeclaredType> getAlternatives() {
if (alternatives == null) {
List<? extends TypeMirror> ualts = ((UnionType) actualType).getAlternatives();
List<AnnotatedDeclaredType> res =
new ArrayList<AnnotatedDeclaredType>(ualts.size());
for (TypeMirror alt : ualts) {
res.add((AnnotatedDeclaredType) createType(alt, atypeFactory, false));
}
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. 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.
*
* <p>For example,
*
* <pre>
* {@code class B<T> { ... } }
* {@code class A extends B<@NonNull String> { ... } }
* {@code @Nullable A a;}
* </pre>
*
* The direct supertype of the ATM {@code @Nullable A} is {@code @Nullable B<@NonNull String>}.
*
* <p>An example with more complex type arguments:
*
* <pre>
* {@code class D<Q,R> { ... } }
* {@code class A<T,S> extends D<S,T> { ... } }
* {@code @Nullable A<@NonNull String, @NonNull Object> a;}
* </pre>
*
* The direct supertype of the ATM {@code @Nullable A<@NonNull String, @NonNull Object>} is
* {@code @Nullable B<@NonNull Object, @NonNull String>}.
*
* <p>An example with more than one direct supertype:
*
* <pre>
* {@code class B<T> implements List<Integer> { ... } }
* {@code class A extends B<@NonNull String> implements List<Integer> { ... } }
* {@code @Nullable A a;}
* </pre>
*
* The direct supertypes of the ATM {@code @Nullable A} are {@code @Nullable B <@NonNull
* String>} and {@code @Nullable List<@NonNull Integer>}.
*
* @see Types#directSupertypes(TypeMirror)
*/
public List<? extends AnnotatedTypeMirror> directSuperTypes() {
return SupertypeFinder.directSuperTypes(this);
}
}