All Downloads are FREE. Search and download functionalities are using the official Maven repository.

framework.src.org.checkerframework.framework.type.SupertypeFinder Maven / Gradle / Ivy

Go to download

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.

There is a newer version: 3.42.0
Show newest version
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; } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy