framework.src.org.checkerframework.framework.type.SupertypeFinder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of checker Show documentation
Show all versions of checker Show documentation
The Checker Framework enhances Java’s type system to
make it more powerful and useful. This lets software developers
detect and prevent errors in their Java programs.
The Checker Framework includes compiler plug-ins ("checkers")
that find bugs or verify their absence. It also permits you to
write your own compiler plug-ins.
package org.checkerframework.framework.type;
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.ElementUtils;
import org.checkerframework.javacutil.ErrorReporter;
import org.checkerframework.javacutil.TreeUtils;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
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.Elements;
import javax.lang.model.util.Types;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.Tree;
/**
* Finds the direct supertypes of an input AnnotatedTypeMirror.
* See http://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.10.2
* @see Types#directSupertypes(TypeMirror)
*/
class SupertypeFinder {
// Version of method below for declared types
/** @see Types#directSupertypes(TypeMirror) */
public static List directSuperTypes(AnnotatedDeclaredType type) {
SupertypeFindingVisitor supertypeFindingVisitor = new SupertypeFindingVisitor(type.atypeFactory);
List supertypes = supertypeFindingVisitor.visitDeclared(type, null);
type.atypeFactory.postDirectSuperTypes(type, supertypes);
return supertypes;
}
// Version of method above for all types
/** @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;
}
private static class SupertypeFindingVisitor extends SimpleAnnotatedTypeVisitor, 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 defaultAction(AnnotatedTypeMirror t, Void p) {
return new ArrayList();
}
/**
* 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();
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) {
List supertypes = new ArrayList();
// Set annotations = type.getAnnotations();
TypeElement typeElement = (TypeElement) type.getUnderlyingType().asElement();
// Mapping of type variable to actual types
Map mapping = new HashMap<>();
if (type.getTypeArguments().size() != typeElement.getTypeParameters().size()) {
if (!type.wasRaw()) {
ErrorReporter.errorAbort(
"AnnotatedDeclaredType's element has a different number of type parameters than type.\n"
+ "type=" + type + "\n"
+ "element=" + typeElement);
}
}
for (int i = 0; i < type.getTypeArguments().size(); ++i) {
mapping.put(typeElement.getTypeParameters().get(i), type.getTypeArguments().get(i));
}
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 supertypesFromElement(AnnotatedDeclaredType type, TypeElement typeElement) {
List supertypes = new ArrayList();
// Find the super types: Start with enums and superclass
if (typeElement.getKind() == ElementKind.ENUM) {
DeclaredType dt = (DeclaredType) typeElement.getSuperclass();
AnnotatedDeclaredType adt = (AnnotatedDeclaredType) atypeFactory.toAnnotatedType(dt, false);
List tas = adt.getTypeArguments();
List newtas = new ArrayList();
for (AnnotatedTypeMirror t : tas) {
// If the type argument of super is the same as the input type
if (atypeFactory.types.isSameType(t.getUnderlyingType(), type.getUnderlyingType())) {
t.addAnnotations(type.getAnnotations());
newtas.add(t);
}
}
adt.setTypeArguments(newtas);
supertypes.add(adt);
} 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 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.wasRaw()) {
for (AnnotatedDeclaredType adt : supertypes) {
adt.setWasRaw();
}
}
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);
supertypes.add(adt);
}
TypeElement elem = TreeUtils.elementFromDeclaration(classTree);
if (elem.getKind() == ElementKind.ENUM) {
DeclaredType dt = (DeclaredType) elem.getSuperclass();
AnnotatedDeclaredType adt = (AnnotatedDeclaredType) atypeFactory.toAnnotatedType(dt, false);
List tas = adt.getTypeArguments();
List newtas = new ArrayList();
for (AnnotatedTypeMirror t : tas) {
// If the type argument of super is the same as the input type
if (atypeFactory.types.isSameType(t.getUnderlyingType(), type.getUnderlyingType())) {
t.addAnnotations(type.getAnnotations());
newtas.add(t);
}
}
adt.setTypeArguments(newtas);
supertypes.add(adt);
}
if (type.wasRaw()) {
for (AnnotatedDeclaredType adt : supertypes) {
adt.setWasRaw();
}
}
return supertypes;
}
/**
* {@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();
Elements elements = atypeFactory.elements;
final AnnotatedTypeMirror objectType =
atypeFactory.getAnnotatedType(elements.getTypeElement("java.lang.Object"));
objectType.addAnnotations(annotations);
superTypes.add(objectType);
final AnnotatedTypeMirror cloneableType =
atypeFactory.getAnnotatedType(elements.getTypeElement("java.lang.Cloneable"));
cloneableType.addAnnotations(annotations);
superTypes.add(cloneableType);
final AnnotatedTypeMirror serializableType =
atypeFactory.getAnnotatedType(elements.getTypeElement("java.io.Serializable"));
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) {
List superTypes = new ArrayList<>();
superTypes.add(type.getUpperBound().deepCopy());
return superTypes;
}
@Override
public List visitWildcard(AnnotatedWildcardType type, Void p) {
List superTypes = new ArrayList<>();
superTypes.add(type.getExtendsBound().deepCopy());
return superTypes;
}
/**
* 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:
*
* Classes may have type parameters that are used in extends or implements clauses.
* E.g.
* {@code class MyList extends List}
*
* Direct supertypes will contain a type {@code List} but the type T may become out of sync with
* the annotations on type {@code MyList}. To keep them in-sync, we substitute out the copy of T
* with the same reference to T that is on {@code MyList}
*/
private class TypeParamReplacer extends AnnotatedTypeScanner> {
private final Types types;
public TypeParamReplacer(Types types) {
this.types = types;
}
@Override
public Void visitDeclared(AnnotatedDeclaredType type, Map mapping) {
if (visitedNodes.containsKey(type)) {
return visitedNodes.get(type);
}
visitedNodes.put(type, null);
List args = new ArrayList();
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);
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 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;
}
}
}
}