blob: 795b18703126412570263e818e9ec235b13523a0 [file] [log] [blame]
package org.checkerframework.framework.type;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.Tree;
import java.io.Serializable;
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 java.util.Set;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
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.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Types;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedArrayType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedPrimitiveType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedWildcardType;
import org.checkerframework.framework.type.visitor.AnnotatedTypeScanner;
import org.checkerframework.framework.type.visitor.SimpleAnnotatedTypeVisitor;
import org.checkerframework.javacutil.BugInCF;
import org.checkerframework.javacutil.ElementUtils;
import org.checkerframework.javacutil.TreeUtils;
import org.checkerframework.javacutil.TypesUtils;
import org.plumelib.util.CollectionsPlume;
/**
* Finds the direct supertypes of an input AnnotatedTypeMirror. See <a
* href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-4.html#jls-4.10.2">JLS section
* 4.10.2</a>.
*
* @see Types#directSupertypes(TypeMirror)
*/
class SupertypeFinder {
// Version of method below for declared types
/**
* See {@link Types#directSupertypes(TypeMirror)}.
*
* @param type the type whose supertypes to return
* @return the immediate supertypes of {@code type}
* @see Types#directSupertypes(TypeMirror)
*/
public static List<AnnotatedDeclaredType> directSupertypes(AnnotatedDeclaredType type) {
SupertypeFindingVisitor supertypeFindingVisitor =
new SupertypeFindingVisitor(type.atypeFactory);
List<AnnotatedDeclaredType> supertypes = supertypeFindingVisitor.visitDeclared(type, null);
type.atypeFactory.postDirectSuperTypes(type, supertypes);
return supertypes;
}
// Version of method above for all types
/**
* See {@link Types#directSupertypes(TypeMirror)}.
*
* @param type the type whose supertypes to return
* @return the immediate supertypes of {@code type}
* @see Types#directSupertypes(TypeMirror)
*/
public static final List<? extends AnnotatedTypeMirror> directSupertypes(
AnnotatedTypeMirror type) {
SupertypeFindingVisitor supertypeFindingVisitor =
new SupertypeFindingVisitor(type.atypeFactory);
List<? extends AnnotatedTypeMirror> supertypes = supertypeFindingVisitor.visit(type, null);
type.atypeFactory.postDirectSuperTypes(type, supertypes);
return supertypes;
}
private static class SupertypeFindingVisitor
extends SimpleAnnotatedTypeVisitor<List<? extends AnnotatedTypeMirror>, Void> {
private final Types types;
private final AnnotatedTypeFactory atypeFactory;
private final TypeParamReplacer typeParamReplacer;
SupertypeFindingVisitor(AnnotatedTypeFactory atypeFactory) {
this.atypeFactory = atypeFactory;
this.types = atypeFactory.types;
this.typeParamReplacer = new TypeParamReplacer(types);
}
@Override
public List<AnnotatedTypeMirror> defaultAction(AnnotatedTypeMirror t, Void p) {
return Collections.emptyList();
}
/**
* Primitive Rules:
*
* <pre>{@code
* double >1 float
* float >1 long
* long >1 int
* int >1 char
* int >1 short
* short >1 byte
* }</pre>
*
* For easiness:
*
* <pre>{@code
* boxed(primitiveType) >: primitiveType
* }</pre>
*/
@Override
public List<AnnotatedTypeMirror> visitPrimitive(AnnotatedPrimitiveType type, Void p) {
List<AnnotatedTypeMirror> superTypes = new ArrayList<>(1);
Set<AnnotationMirror> annotations = type.getAnnotations();
// Find Boxed type
TypeElement boxed = types.boxedClass(type.getUnderlyingType());
AnnotatedDeclaredType boxedType = atypeFactory.getAnnotatedType(boxed);
boxedType.replaceAnnotations(annotations);
superTypes.add(boxedType);
TypeKind superPrimitiveType = null;
if (type.getKind() == TypeKind.BOOLEAN) {
// Nothing
} else if (type.getKind() == TypeKind.BYTE) {
superPrimitiveType = TypeKind.SHORT;
} else if (type.getKind() == TypeKind.CHAR) {
superPrimitiveType = TypeKind.INT;
} else if (type.getKind() == TypeKind.DOUBLE) {
// Nothing
} else if (type.getKind() == TypeKind.FLOAT) {
superPrimitiveType = TypeKind.DOUBLE;
} else if (type.getKind() == TypeKind.INT) {
superPrimitiveType = TypeKind.LONG;
} else if (type.getKind() == TypeKind.LONG) {
superPrimitiveType = TypeKind.FLOAT;
} else if (type.getKind() == TypeKind.SHORT) {
superPrimitiveType = TypeKind.INT;
} else {
assert false : "Forgot the primitive " + type;
}
if (superPrimitiveType != null) {
AnnotatedPrimitiveType superPrimitive =
(AnnotatedPrimitiveType)
atypeFactory.toAnnotatedType(types.getPrimitiveType(superPrimitiveType), false);
superPrimitive.addAnnotations(annotations);
superTypes.add(superPrimitive);
}
return superTypes;
}
@Override
public List<AnnotatedDeclaredType> visitDeclared(AnnotatedDeclaredType type, Void p) {
// Set<AnnotationMirror> annotations = type.getAnnotations();
TypeElement typeElement = (TypeElement) type.getUnderlyingType().asElement();
// Mapping of type variable to actual types
Map<TypeParameterElement, AnnotatedTypeMirror> mapping = new HashMap<>();
if (type.getTypeArguments().size() != typeElement.getTypeParameters().size()) {
if (!type.wasRaw()) {
throw new BugInCF(
"AnnotatedDeclaredType's element has a different number of type parameters than"
+ " type.%ntype=%s%nelement=%s",
type, typeElement);
}
}
AnnotatedDeclaredType enclosing = type;
while (enclosing != null) {
TypeElement enclosingTypeElement = (TypeElement) enclosing.getUnderlyingType().asElement();
List<AnnotatedTypeMirror> typeArgs = enclosing.getTypeArguments();
List<? extends TypeParameterElement> typeParams = enclosingTypeElement.getTypeParameters();
for (int i = 0; i < enclosing.getTypeArguments().size(); ++i) {
AnnotatedTypeMirror typArg = typeArgs.get(i);
TypeParameterElement ele = typeParams.get(i);
mapping.put(ele, typArg);
}
enclosing = enclosing.getEnclosingType();
}
List<AnnotatedDeclaredType> supertypes = new ArrayList<>();
ClassTree classTree = atypeFactory.trees.getTree(typeElement);
// Testing against enum and annotation. Ideally we can simply use element!
if (classTree != null) {
supertypes.addAll(supertypesFromTree(type, classTree));
} else {
supertypes.addAll(supertypesFromElement(type, typeElement));
// final Element elem = type.getElement() == null ? typeElement : type.getElement();
}
if (typeElement.getKind() == ElementKind.ANNOTATION_TYPE) {
TypeElement jlaElement =
atypeFactory.elements.getTypeElement(Annotation.class.getCanonicalName());
AnnotatedDeclaredType jlaAnnotation = atypeFactory.fromElement(jlaElement);
jlaAnnotation.addAnnotations(type.getAnnotations());
supertypes.add(jlaAnnotation);
}
for (AnnotatedDeclaredType dt : supertypes) {
typeParamReplacer.visit(dt, mapping);
}
return supertypes;
}
private List<AnnotatedDeclaredType> supertypesFromElement(
AnnotatedDeclaredType type, TypeElement typeElement) {
List<AnnotatedDeclaredType> supertypes = new ArrayList<>();
// Find the super types: Start with enums and superclass
if (typeElement.getKind() == ElementKind.ENUM) {
supertypes.add(createEnumSuperType(type, typeElement));
} else if (typeElement.getSuperclass().getKind() != TypeKind.NONE) {
DeclaredType superClass = (DeclaredType) typeElement.getSuperclass();
AnnotatedDeclaredType dt =
(AnnotatedDeclaredType) atypeFactory.toAnnotatedType(superClass, false);
supertypes.add(dt);
} else if (!ElementUtils.isObject(typeElement)) {
supertypes.add(AnnotatedTypeMirror.createTypeOfObject(atypeFactory));
}
for (TypeMirror st : typeElement.getInterfaces()) {
if (type.wasRaw()) {
st = types.erasure(st);
}
AnnotatedDeclaredType ast = (AnnotatedDeclaredType) atypeFactory.toAnnotatedType(st, false);
supertypes.add(ast);
if (type.wasRaw()) {
if (st.getKind() == TypeKind.DECLARED) {
final List<? extends TypeMirror> typeArgs = ((DeclaredType) st).getTypeArguments();
final List<AnnotatedTypeMirror> annotatedTypeArgs = ast.getTypeArguments();
for (int i = 0; i < typeArgs.size(); i++) {
atypeFactory.addComputedTypeAnnotations(
types.asElement(typeArgs.get(i)), annotatedTypeArgs.get(i));
}
}
}
}
ElementAnnotationApplier.annotateSupers(supertypes, typeElement);
if (type.wasRaw()) {
for (AnnotatedDeclaredType adt : supertypes) {
adt.setWasRaw();
}
}
return supertypes;
}
private List<AnnotatedDeclaredType> supertypesFromTree(
AnnotatedDeclaredType type, ClassTree classTree) {
List<AnnotatedDeclaredType> supertypes = new ArrayList<>();
if (classTree.getExtendsClause() != null) {
AnnotatedDeclaredType adt =
(AnnotatedDeclaredType)
atypeFactory.getAnnotatedTypeFromTypeTree(classTree.getExtendsClause());
supertypes.add(adt);
} else if (!ElementUtils.isObject(TreeUtils.elementFromDeclaration(classTree))) {
supertypes.add(AnnotatedTypeMirror.createTypeOfObject(atypeFactory));
}
for (Tree implemented : classTree.getImplementsClause()) {
AnnotatedDeclaredType adt =
(AnnotatedDeclaredType) atypeFactory.getAnnotatedTypeFromTypeTree(implemented);
if (adt.getTypeArguments().size() != adt.getUnderlyingType().getTypeArguments().size()
&& classTree.getSimpleName().contentEquals("")) {
// classTree is an anonymous class with a diamond.
List<AnnotatedTypeMirror> args =
CollectionsPlume.mapList(
(TypeParameterElement element) -> {
AnnotatedTypeMirror arg =
AnnotatedTypeMirror.createType(element.asType(), atypeFactory, false);
// TODO: After #979 is fixed, calculate the correct type
// using inference.
return atypeFactory.getUninferredWildcardType((AnnotatedTypeVariable) arg);
},
TypesUtils.getTypeElement(adt.getUnderlyingType()).getTypeParameters());
adt.setTypeArguments(args);
}
supertypes.add(adt);
}
TypeElement elem = TreeUtils.elementFromDeclaration(classTree);
if (elem.getKind() == ElementKind.ENUM) {
supertypes.add(createEnumSuperType(type, elem));
}
if (type.wasRaw()) {
for (AnnotatedDeclaredType adt : supertypes) {
adt.setWasRaw();
}
}
return supertypes;
}
/**
* All enums implicitly extend {@code Enum<MyEnum>}, where {@code MyEnum} is the type of the
* enum. This method creates the AnnotatedTypeMirror for {@code Enum<MyEnum>} where the
* annotation on {@code MyEnum} is copied from the annotation on the upper bound of the type
* argument to Enum. For example, {@code class Enum<E extend @HERE Enum<E>>}.
*
* @param type annotated type of an enum
* @param elem element corresponding to {@code type}
* @return enum super type
*/
private AnnotatedDeclaredType createEnumSuperType(
AnnotatedDeclaredType type, TypeElement elem) {
DeclaredType dt = (DeclaredType) elem.getSuperclass();
AnnotatedDeclaredType adt = (AnnotatedDeclaredType) atypeFactory.toAnnotatedType(dt, false);
for (AnnotatedTypeMirror t : adt.getTypeArguments()) {
// If the type argument of super is the same as the input type
if (atypeFactory.types.isSameType(t.getUnderlyingType(), type.getUnderlyingType())) {
Set<AnnotationMirror> bounds =
((AnnotatedDeclaredType) atypeFactory.getAnnotatedType(dt.asElement()))
.typeArgs
.get(0)
.getEffectiveAnnotations();
t.addAnnotations(bounds);
}
}
adt.addAnnotations(type.getAnnotations());
return adt;
}
/**
*
*
* <pre>{@code
* For type = A[ ] ==>
* Object >: A[ ]
* Clonable >: A[ ]
* java.io.Serializable >: A[ ]
*
* if A is reference type, then also
* B[ ] >: A[ ] for any B[ ] >: A[ ]
* }</pre>
*/
@Override
public List<AnnotatedTypeMirror> visitArray(AnnotatedArrayType type, Void p) {
List<AnnotatedTypeMirror> superTypes = new ArrayList<>();
Set<AnnotationMirror> annotations = type.getAnnotations();
final AnnotatedTypeMirror objectType = atypeFactory.getAnnotatedType(Object.class);
objectType.addAnnotations(annotations);
superTypes.add(objectType);
final AnnotatedTypeMirror cloneableType = atypeFactory.getAnnotatedType(Cloneable.class);
cloneableType.addAnnotations(annotations);
superTypes.add(cloneableType);
final AnnotatedTypeMirror serializableType =
atypeFactory.getAnnotatedType(Serializable.class);
serializableType.addAnnotations(annotations);
superTypes.add(serializableType);
for (AnnotatedTypeMirror sup : type.getComponentType().directSupertypes()) {
ArrayType arrType = atypeFactory.types.getArrayType(sup.getUnderlyingType());
AnnotatedArrayType aarrType =
(AnnotatedArrayType) atypeFactory.toAnnotatedType(arrType, false);
aarrType.setComponentType(sup);
aarrType.addAnnotations(annotations);
superTypes.add(aarrType);
}
return superTypes;
}
@Override
public List<AnnotatedTypeMirror> visitTypeVariable(AnnotatedTypeVariable type, Void p) {
return Collections.singletonList(type.getUpperBound().deepCopy());
}
@Override
public List<AnnotatedTypeMirror> visitWildcard(AnnotatedWildcardType type, Void p) {
return Collections.singletonList(type.getExtendsBound().deepCopy());
}
/**
* Note: The explanation below is my interpretation of why we have this code. I am not sure if
* this was the author's original intent but I can see no other reasoning, exercise caution:
*
* <p>Classes may have type parameters that are used in extends or implements clauses. E.g.
* {@code class MyList<T> extends List<T>}
*
* <p>Direct supertypes will contain a type {@code List<T>} but the type T may become out of
* sync with the annotations on type {@code MyList<T>}. To keep them in-sync, we substitute out
* the copy of T with the same reference to T that is on {@code MyList<T>}
*/
private static class TypeParamReplacer
extends AnnotatedTypeScanner<Void, Map<TypeParameterElement, AnnotatedTypeMirror>> {
private final Types types;
public TypeParamReplacer(Types types) {
this.types = types;
}
@Override
public Void visitDeclared(
AnnotatedDeclaredType type, Map<TypeParameterElement, AnnotatedTypeMirror> mapping) {
if (visitedNodes.containsKey(type)) {
return visitedNodes.get(type);
}
visitedNodes.put(type, null);
if (type.getEnclosingType() != null) {
scan(type.getEnclosingType(), mapping);
}
List<AnnotatedTypeMirror> args = new ArrayList<>(type.getTypeArguments().size());
for (AnnotatedTypeMirror arg : type.getTypeArguments()) {
Element elem = types.asElement(arg.getUnderlyingType());
if ((elem != null)
&& (elem.getKind() == ElementKind.TYPE_PARAMETER)
&& mapping.containsKey(elem)) {
AnnotatedTypeMirror other = mapping.get(elem).deepCopy();
other.replaceAnnotations(arg.getAnnotationsField());
args.add(other);
} else {
args.add(arg);
scan(arg, mapping);
}
}
type.setTypeArguments(args);
return null;
}
@Override
public Void visitArray(
AnnotatedArrayType type, Map<TypeParameterElement, AnnotatedTypeMirror> mapping) {
AnnotatedTypeMirror comptype = type.getComponentType();
Element elem = types.asElement(comptype.getUnderlyingType());
AnnotatedTypeMirror other;
if ((elem != null)
&& (elem.getKind() == ElementKind.TYPE_PARAMETER)
&& mapping.containsKey(elem)) {
other = mapping.get(elem);
other.replaceAnnotations(comptype.getAnnotationsField());
type.setComponentType(other);
} else {
scan(type.getComponentType(), mapping);
}
return null;
}
}
}
}