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

framework.src.org.checkerframework.framework.type.treeannotator.PropagationTreeAnnotator 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.treeannotator;

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

import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.type.TypeKind;

import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedArrayType;
import org.checkerframework.framework.type.QualifierHierarchy;
import org.checkerframework.javacutil.Pair;

import com.sun.source.tree.BinaryTree;
import com.sun.source.tree.CompoundAssignmentTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.NewArrayTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.TypeCastTree;
import com.sun.source.tree.UnaryTree;


/**
 * {@link PropagationTreeAnnotator} adds qualifiers to types where the resulting
 * type is a function of an input type, e.g. the result of a binary operation
 * is a LUB of the type of expressions in the binary operation.
 *
 * {@link PropagationTreeAnnotator} is generally ran first by {@link ListTreeAnnotator}
 * since the trees it handles are not usually targets of @implicit for.
 *
 * {@link PropagationTreeAnnotator} does not traverse trees deeply by default.
 *
 * @see TreeAnnotator
 */
public class PropagationTreeAnnotator extends TreeAnnotator {

    private final QualifierHierarchy qualHierarchy;

    /**
     * Creates a {@link org.checkerframework.framework.type.typeannotator.ImplicitsTypeAnnotator} from the given checker, using that checker's
     * type hierarchy.
     */
    public PropagationTreeAnnotator(AnnotatedTypeFactory atypeFactory) {
        super(atypeFactory);
        this.qualHierarchy = atypeFactory.getQualifierHierarchy();
    }

    @Override
    public Void visitNewArray(NewArrayTree tree, AnnotatedTypeMirror type) {
        assert type.getKind() == TypeKind.ARRAY : "PropagationTreeAnnotator.visitNewArray: should be an array type";

        AnnotatedTypeMirror componentType = ((AnnotatedArrayType)type).getComponentType();

        Collection prev = null;
        if (tree.getInitializers() != null &&
                tree.getInitializers().size() != 0) {
            // We have initializers, either with or without an array type.

            for (ExpressionTree init: tree.getInitializers()) {
                AnnotatedTypeMirror initType = atypeFactory.getAnnotatedType(init);
                // initType might be a typeVariable, so use effectiveAnnotations.
                Collection annos = initType.getEffectiveAnnotations();

                prev = (prev == null) ? annos : qualHierarchy.leastUpperBounds(prev, annos);
            }
        } else {
            prev = componentType.getAnnotations();
        }

        assert prev != null : "PropagationTreeAnnotator.visitNewArray: violated assumption about qualifiers";

        Pair context = atypeFactory.getVisitorState().getAssignmentContext();
        Collection post;

        if (context != null && context.second != null && context.second instanceof AnnotatedArrayType) {
            AnnotatedTypeMirror contextComponentType = ((AnnotatedArrayType) context.second).getComponentType();
            // Only compare the qualifiers that existed in the array type
            // Defaulting wasn't performed yet, so prev might have fewer qualifiers than
            // contextComponentType, which would cause a failure.
            // TODO: better solution?
            boolean prevIsSubtype = true;
            for (AnnotationMirror am : prev) {
                if (contextComponentType.isAnnotatedInHierarchy(am) &&
                        !this.qualHierarchy.isSubtype(am, contextComponentType.getAnnotationInHierarchy(am))) {
                    prevIsSubtype = false;
                }
            }
            // TODO: checking conformance of component kinds is a basic sanity check
            // It fails for array initializer expressions. Those should be handled nicer.
            if (contextComponentType.getKind() == componentType.getKind() &&
                    (prev.isEmpty() ||
                    (!contextComponentType.getAnnotations().isEmpty() &&
                            prevIsSubtype))) {
                post = contextComponentType.getAnnotations();
            } else {
                // The type of the array initializers is incompatible with the
                // context type!
                // Somebody else will complain.
                post = prev;
            }
        } else {
            // No context is available - simply use what we have.
            post = prev;
        }
        componentType.addMissingAnnotations(post);

        return null;
    }

    @Override
    public Void visitCompoundAssignment(CompoundAssignmentTree node, AnnotatedTypeMirror type) {
        AnnotatedTypeMirror rhs = atypeFactory.getAnnotatedType(node.getExpression());
        AnnotatedTypeMirror lhs = atypeFactory.getAnnotatedType(node.getVariable());
        Set lubs = qualHierarchy.leastUpperBounds(rhs.getAnnotations(), lhs.getAnnotations());
        type.addMissingAnnotations(lubs);
        return null;
    }

    @Override
    public Void visitBinary(BinaryTree node, AnnotatedTypeMirror type) {
        AnnotatedTypeMirror a = atypeFactory.getAnnotatedType(node.getLeftOperand());
        AnnotatedTypeMirror b = atypeFactory.getAnnotatedType(node.getRightOperand());
        Set lubs = qualHierarchy.leastUpperBounds(a.getEffectiveAnnotations(), b.getEffectiveAnnotations());
        type.addMissingAnnotations(lubs);
        return null;
    }

    @Override
    public Void visitUnary(UnaryTree node, AnnotatedTypeMirror type) {
        AnnotatedTypeMirror exp = atypeFactory.getAnnotatedType(node.getExpression());
        type.addMissingAnnotations(exp.getAnnotations());
        return null;
    }

    /*
     * TODO: would this make sense in general?
    @Override
    public Void visitConditionalExpression(ConditionalExpressionTree node, AnnotatedTypeMirror type) {
        if (!type.isAnnotated()) {
            AnnotatedTypeMirror a = typeFactory.getAnnotatedType(node.getTrueExpression());
            AnnotatedTypeMirror b = typeFactory.getAnnotatedType(node.getFalseExpression());
            Set lubs = qualHierarchy.leastUpperBounds(a.getEffectiveAnnotations(), b.getEffectiveAnnotations());
            type.replaceAnnotations(lubs);
        }
        return super.visitConditionalExpression(node, type);
    }*/

    @Override
    public Void visitTypeCast(TypeCastTree node, AnnotatedTypeMirror type) {

        AnnotatedTypeMirror exprType = atypeFactory.getAnnotatedType(node.getExpression());
        if (type.getKind() == TypeKind.TYPEVAR ) {
            if (exprType.getKind() == TypeKind.TYPEVAR) {
                // If both types are type variables, take the direct annotations.
                type.addMissingAnnotations(exprType.getAnnotations());
            }
            // else do nothing
            // TODO: What should we do if the type is a type variable, but the expression
            // is not?
        } else {
            // Use effective annotations from the expression, to get upper bound
            // of type variables.
            type.addMissingAnnotations(exprType.getEffectiveAnnotations());
        }
        return null;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy