org.checkerframework.framework.type.SupertypeFinder Maven / Gradle / Ivy
Show all versions of checker Show documentation
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.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.type.TypeVariable;
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.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 JLS section
* 4.10.2.
*
* @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 directSupertypes(AnnotatedDeclaredType type) {
SupertypeFindingVisitor supertypeFindingVisitor =
new SupertypeFindingVisitor(type.atypeFactory);
List supertypes =
supertypeFindingVisitor.visitDeclared(type.asUse(), 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 directSupertypes(
AnnotatedTypeMirror type) {
SupertypeFindingVisitor supertypeFindingVisitor =
new SupertypeFindingVisitor(type.atypeFactory);
List supertypes = supertypeFindingVisitor.visit(type, null);
type.atypeFactory.postDirectSuperTypes(type, supertypes);
return supertypes;
}
/** Computes the direct supertypes of annotated types. */
private static class SupertypeFindingVisitor
extends SimpleAnnotatedTypeVisitor, Void> {
/** Types util class. */
private final Types types;
/** Annotated type factory. */
private final AnnotatedTypeFactory atypeFactory;
/**
* Creates a {@code SupertypeFindingVisitor}.
*
* @param atypeFactory annotated type factory
*/
SupertypeFindingVisitor(AnnotatedTypeFactory atypeFactory) {
this.atypeFactory = atypeFactory;
this.types = atypeFactory.types;
}
@Override
public List defaultAction(AnnotatedTypeMirror t, Void p) {
return Collections.emptyList();
}
/**
* Primitive Rules:
*
* {@code
* double >1 float
* float >1 long
* long >1 int
* int >1 char
* int >1 short
* short >1 byte
* }
*
* For easiness:
*
* {@code
* boxed(primitiveType) >: primitiveType
* }
*/
@Override
public List visitPrimitive(AnnotatedPrimitiveType type, Void p) {
List superTypes = new ArrayList<>(1);
Set 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 visitDeclared(AnnotatedDeclaredType type, Void p) {
// Set annotations = type.getAnnotations();
TypeElement typeElement = (TypeElement) type.getUnderlyingType().asElement();
if (type.getTypeArguments().size() != typeElement.getTypeParameters().size()) {
if (!type.isUnderlyingTypeRaw()) {
throw new BugInCF(
"AnnotatedDeclaredType's element has a different number of type parameters than"
+ " type.%ntype=%s%nelement=%s",
type, typeElement);
}
}
List 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);
}
Map typeVarToTypeArg = getTypeVarToTypeArg(type);
List superTypesNew = new ArrayList<>();
for (AnnotatedDeclaredType dt : supertypes) {
type.atypeFactory.initializeAtm(dt);
superTypesNew.add(
(AnnotatedDeclaredType)
atypeFactory.getTypeVarSubstitutor().substitute(typeVarToTypeArg, dt));
}
return superTypesNew;
}
/**
* Creates a mapping from a type parameter to its corresponding annotated type argument for all
* type parameters of {@code type}, its enclosing types, and all super types of all {@code
* type}'s enclosing types.
*
* It does not get the type parameters of the supertypes of {@code type} because the result
* of this method is used to substitute the type arguments of the supertypes of {@code type}.
*
* @param type a type
* @return a mapping from each type parameter to its corresponding annotated type argument
*/
private Map getTypeVarToTypeArg(AnnotatedDeclaredType type) {
Map mapping = new HashMap<>();
// addTypeVarsFromEnclosingTypes can't be called with `type` because it calls
// `directSupertypes(types)`, which then calls this method. Add the type variables from `type`
// and then call addTypeVarsFromEnclosingTypes on the enclosing type.
addTypeVariablesToMapping(type, mapping);
addTypeVarsFromEnclosingTypes(type.getEnclosingType(), mapping);
return mapping;
}
/**
* Adds a mapping from a type parameter to its corresponding annotated type argument for all
* type parameters of {@code type}.
*
* @param type a type
* @param mapping type variable to type argument map; side-effected by this method
*/
private void addTypeVariablesToMapping(
AnnotatedDeclaredType type, Map mapping) {
TypeElement enclosingTypeElement = (TypeElement) type.getUnderlyingType().asElement();
List typeParams = enclosingTypeElement.getTypeParameters();
List typeArgs = type.getTypeArguments();
for (int i = 0; i < type.getTypeArguments().size(); ++i) {
AnnotatedTypeMirror typArg = typeArgs.get(i);
TypeParameterElement ele = typeParams.get(i);
mapping.put((TypeVariable) ele.asType(), typArg);
}
}
/**
* Adds a mapping from a type parameter to its corresponding annotated type argument for all
* type parameters of {@code enclosing} and its enclosing types. This method recurs on all the
* super types of {@code enclosing}.
*
* @param mapping type variable to type argument map; side-effected by this method
* @param enclosing a type
*/
private void addTypeVarsFromEnclosingTypes(
AnnotatedDeclaredType enclosing, Map mapping) {
while (enclosing != null) {
addTypeVariablesToMapping(enclosing, mapping);
for (AnnotatedDeclaredType enclSuper : directSupertypes(enclosing)) {
addTypeVarsFromEnclosingTypes(enclSuper, mapping);
}
enclosing = enclosing.getEnclosingType();
}
}
private List supertypesFromElement(
AnnotatedDeclaredType type, TypeElement typeElement) {
List 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.isUnderlyingTypeRaw()) {
st = types.erasure(st);
}
AnnotatedDeclaredType ast = (AnnotatedDeclaredType) atypeFactory.toAnnotatedType(st, false);
supertypes.add(ast);
if (type.isUnderlyingTypeRaw()) {
if (st.getKind() == TypeKind.DECLARED) {
final List typeArgs = ((DeclaredType) st).getTypeArguments();
final List 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.isUnderlyingTypeRaw()) {
for (AnnotatedDeclaredType adt : supertypes) {
adt.setIsUnderlyingTypeRaw();
}
}
return supertypes;
}
private List supertypesFromTree(
AnnotatedDeclaredType type, ClassTree classTree) {
List 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 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.isUnderlyingTypeRaw()) {
for (AnnotatedDeclaredType adt : supertypes) {
adt.setIsUnderlyingTypeRaw();
}
}
return supertypes;
}
/**
* All enums implicitly extend {@code Enum}, where {@code MyEnum} is the type of the
* enum. This method creates the AnnotatedTypeMirror for {@code Enum} 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>}.
*
* @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 bounds =
((AnnotatedDeclaredType) atypeFactory.getAnnotatedType(dt.asElement()))
.typeArgs
.get(0)
.getEffectiveAnnotations();
t.addAnnotations(bounds);
}
}
adt.addAnnotations(type.getAnnotations());
return adt;
}
/**
*
*
* {@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[ ]
* }
*/
@Override
public List visitArray(AnnotatedArrayType type, Void p) {
List superTypes = new ArrayList<>();
Set 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 visitTypeVariable(AnnotatedTypeVariable type, Void p) {
return Collections.singletonList(type.getUpperBound().deepCopy());
}
@Override
public List visitWildcard(AnnotatedWildcardType type, Void p) {
return Collections.singletonList(type.getExtendsBound().deepCopy());
}
}
}