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

org.checkerframework.framework.flow.CFTreeBuilder Maven / Gradle / Ivy

package org.checkerframework.framework.flow;

import com.sun.source.tree.Tree;
import com.sun.tools.javac.code.Attribute;
import com.sun.tools.javac.code.BoundKind;
import com.sun.tools.javac.code.Symbol.TypeSymbol;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.TypeTag;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotatedType;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCTypeApply;
import com.sun.tools.javac.util.List;

import org.checkerframework.javacutil.TypeAnnotationUtils;
import org.checkerframework.javacutil.trees.TreeBuilder;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.IntersectionType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.type.WildcardType;

/**
 * The TreeBuilder permits the creation of new AST Trees using the non-public Java compiler API
 * TreeMaker. Initially, it will support construction of desugared Trees required by the CFGBuilder,
 * e.g. the pieces of a desugared enhanced for loop.
 */
public class CFTreeBuilder extends TreeBuilder {

    /**
     * To avoid infinite recursions, record each wildcard that has been converted to a tree. This
     * set is cleared each time {@link #buildAnnotatedType(TypeMirror)} is called.
     */
    private final Set visitedWildcards = new HashSet<>();

    /**
     * Creates a {@code CFTreeBuilder}.
     *
     * @param env environment
     */
    public CFTreeBuilder(ProcessingEnvironment env) {
        super(env);
    }

    /**
     * Builds an AST Tree representing a type, including AnnotationTrees for its annotations.
     *
     * @param type the type
     * @return a Tree representing the type
     */
    public Tree buildAnnotatedType(TypeMirror type) {
        visitedWildcards.clear();
        return createAnnotatedType(type);
    }

    /**
     * Converts a list of AnnotationMirrors to the a corresponding list of new AnnotationTrees.
     *
     * @param annotations the annotations
     * @return new annotation trees representing the annotations
     */
    private List convertAnnotationMirrorsToAnnotationTrees(
            Collection annotations) {
        List annotationTrees = List.nil();

        for (AnnotationMirror am : annotations) {
            // TODO: what TypeAnnotationPosition should be used?
            Attribute.TypeCompound typeCompound =
                    TypeAnnotationUtils.createTypeCompoundFromAnnotationMirror(
                            am, TypeAnnotationUtils.unknownTAPosition(), env);
            JCAnnotation annotationTree = maker.Annotation(typeCompound);
            JCAnnotation typeAnnotationTree =
                    maker.TypeAnnotation(
                            annotationTree.getAnnotationType(), annotationTree.getArguments());

            typeAnnotationTree.attribute = typeCompound;

            annotationTrees = annotationTrees.append(typeAnnotationTree);
        }
        return annotationTrees;
    }

    /**
     * Builds an AST Tree representing a type, including AnnotationTrees for its annotations. This
     * internal method differs from the public {@link #buildAnnotatedType(TypeMirror)} only in that
     * it does not reset the list of visited wildcards.
     *
     * @param type the type for which to create a tree
     * @return a Tree representing the type
     */
    private Tree createAnnotatedType(TypeMirror type) {
        // Implementation based on com.sun.tools.javac.tree.TreeMaker.Type

        // Convert the annotations from a set of AnnotationMirrors
        // to a list of AnnotationTrees.
        java.util.List annotations = type.getAnnotationMirrors();
        List annotationTrees = convertAnnotationMirrorsToAnnotationTrees(annotations);

        // Convert the underlying type from a TypeMirror to an ExpressionTree and combine with the
        // AnnotationTrees to form a ClassTree of kind ANNOTATION_TYPE.
        JCExpression typeTree;
        switch (type.getKind()) {
            case BYTE:
                typeTree = maker.TypeIdent(TypeTag.BYTE);
                break;
            case CHAR:
                typeTree = maker.TypeIdent(TypeTag.CHAR);
                break;
            case SHORT:
                typeTree = maker.TypeIdent(TypeTag.SHORT);
                break;
            case INT:
                typeTree = maker.TypeIdent(TypeTag.INT);
                break;
            case LONG:
                typeTree = maker.TypeIdent(TypeTag.LONG);
                break;
            case FLOAT:
                typeTree = maker.TypeIdent(TypeTag.FLOAT);
                break;
            case DOUBLE:
                typeTree = maker.TypeIdent(TypeTag.DOUBLE);
                break;
            case BOOLEAN:
                typeTree = maker.TypeIdent(TypeTag.BOOLEAN);
                break;
            case VOID:
                typeTree = maker.TypeIdent(TypeTag.VOID);
                break;
            case TYPEVAR:
                // No recursive annotations.
                TypeVariable underlyingTypeVar = (TypeVariable) type;
                typeTree = maker.Ident((TypeSymbol) underlyingTypeVar.asElement());
                break;
            case WILDCARD:
                WildcardType wildcardType = (WildcardType) type;
                boolean visitedBefore = !visitedWildcards.add(wildcardType);
                if (!visitedBefore && wildcardType.getExtendsBound() != null) {
                    Tree annotatedExtendsBound =
                            createAnnotatedType(wildcardType.getExtendsBound());
                    typeTree =
                            maker.Wildcard(
                                    maker.TypeBoundKind(BoundKind.EXTENDS),
                                    (JCTree) annotatedExtendsBound);
                } else if (!visitedBefore && wildcardType.getSuperBound() != null) {
                    Tree annotatedSuperBound = createAnnotatedType(wildcardType.getSuperBound());
                    typeTree =
                            maker.Wildcard(
                                    maker.TypeBoundKind(BoundKind.SUPER),
                                    (JCTree) annotatedSuperBound);
                } else {
                    typeTree = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null);
                }
                break;
            case INTERSECTION:
                IntersectionType intersectionType = (IntersectionType) type;
                List components = List.nil();
                for (TypeMirror bound : intersectionType.getBounds()) {
                    components = components.append((JCExpression) createAnnotatedType(bound));
                }
                typeTree = maker.TypeIntersection(components);
                break;
                // case UNION:
                // TODO: case UNION similar to INTERSECTION, but write test first.
            case DECLARED:
                typeTree = maker.Type((Type) type);

                if (typeTree instanceof JCTypeApply) {
                    // Replace the type parameters with annotated versions.
                    DeclaredType annotatedDeclaredType = (DeclaredType) type;
                    List typeArgTrees = List.nil();
                    for (TypeMirror arg : annotatedDeclaredType.getTypeArguments()) {
                        typeArgTrees = typeArgTrees.append((JCExpression) createAnnotatedType(arg));
                    }
                    JCExpression clazz = (JCExpression) ((JCTypeApply) typeTree).getType();
                    typeTree = maker.TypeApply(clazz, typeArgTrees);
                }
                break;
            case ARRAY:
                ArrayType arrayType = (ArrayType) type;
                Tree componentTree = createAnnotatedType(arrayType.getComponentType());
                typeTree = maker.TypeArray((JCExpression) componentTree);
                break;
            case ERROR:
                typeTree = maker.TypeIdent(TypeTag.ERROR);
                break;
            default:
                assert false : "unexpected type: " + type;
                typeTree = null;
                break;
        }

        typeTree.setType((Type) type);

        if (annotationTrees.isEmpty()) {
            return typeTree;
        }

        JCAnnotatedType annotatedTypeTree = maker.AnnotatedType(annotationTrees, typeTree);
        annotatedTypeTree.setType((Type) type);

        return annotatedTypeTree;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy