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

framework.src.org.checkerframework.framework.type.TypeFromExpressionVisitor 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.AnnotatedExecutableType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedWildcardType;
import org.checkerframework.framework.type.visitor.AnnotatedTypeMerger;
import org.checkerframework.framework.util.AnnotatedTypes;
import org.checkerframework.javacutil.ErrorReporter;
import org.checkerframework.javacutil.InternalUtils;
import org.checkerframework.javacutil.Pair;
import org.checkerframework.javacutil.TreeUtils;

import java.util.ArrayList;
import java.util.List;

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.type.TypeKind;

import com.sun.source.tree.AnnotatedTypeTree;
import com.sun.source.tree.ArrayAccessTree;
import com.sun.source.tree.ArrayTypeTree;
import com.sun.source.tree.AssignmentTree;
import com.sun.source.tree.BinaryTree;
import com.sun.source.tree.CompoundAssignmentTree;
import com.sun.source.tree.ConditionalExpressionTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.InstanceOfTree;
import com.sun.source.tree.IntersectionTypeTree;
import com.sun.source.tree.LambdaExpressionTree;
import com.sun.source.tree.LiteralTree;
import com.sun.source.tree.MemberReferenceTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.NewArrayTree;
import com.sun.source.tree.NewClassTree;
import com.sun.source.tree.ParameterizedTypeTree;
import com.sun.source.tree.ParenthesizedTree;
import com.sun.source.tree.PrimitiveTypeTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.TypeCastTree;
import com.sun.source.tree.UnaryTree;
import com.sun.source.tree.WildcardTree;

/**
 * Converts ExpressionTrees into AnnotatedTypeMirrors
 */
class TypeFromExpressionVisitor extends TypeFromTreeVisitor {

    @Override
    public AnnotatedTypeMirror visitAnnotatedType(AnnotatedTypeTree node,
                                                  AnnotatedTypeFactory f) {
        return f.fromTypeTree(node);
    }

    @Override
    public AnnotatedTypeMirror visitArrayAccess(ArrayAccessTree node,
                                                AnnotatedTypeFactory f) {

        Pair preAssCtxt = f.visitorState.getAssignmentContext();
        try {
            // TODO: what other trees shouldn't maintain the context?
            f.visitorState.setAssignmentContext(null);

            AnnotatedTypeMirror type = f.getAnnotatedType(node.getExpression());
            assert type instanceof AnnotatedArrayType;
            return ((AnnotatedArrayType)type).getComponentType();
        } finally {
            f.visitorState.setAssignmentContext(preAssCtxt);
        }
    }

    @Override
    public AnnotatedTypeMirror visitAssignment(AssignmentTree node,
                                               AnnotatedTypeFactory f) {

        // Recurse on the type of the variable.
        return visit(node.getVariable(), f);
    }

    @Override
    public AnnotatedTypeMirror visitBinary(BinaryTree node,
                                           AnnotatedTypeFactory f) {
        AnnotatedTypeMirror res = f.type(node);
        // TODO: why do we need to clear the type?
        res.clearAnnotations();
        return res;
    }

    @Override
    public AnnotatedTypeMirror visitCompoundAssignment(
            CompoundAssignmentTree node, AnnotatedTypeFactory f) {

        // Recurse on the type of the variable.
        AnnotatedTypeMirror res = visit(node.getVariable(), f);
        // TODO: why do we need to clear the type?
        res.clearAnnotations();
        return res;
    }

    @Override
    public AnnotatedTypeMirror visitConditionalExpression(
            ConditionalExpressionTree node, AnnotatedTypeFactory f) {

        AnnotatedTypeMirror trueType = f.getAnnotatedType(node.getTrueExpression());
        AnnotatedTypeMirror falseType = f.getAnnotatedType(node.getFalseExpression());

        // here
        if (trueType.equals(falseType)) {
            return trueType;
        }

        // TODO: We would want this:
        /*
        AnnotatedTypeMirror alub = f.type(node);
        trueType = f.atypes.asSuper(trueType, alub);
        falseType = f.atypes.asSuper(falseType, alub);
        */

        // instead of:
        AnnotatedTypeMirror alub = f.type(node);
        AnnotatedTypeMirror assuper;
        assuper = AnnotatedTypes.asSuper(f.types, f, trueType, alub);
        if (assuper != null) {
            trueType = assuper;
        }
        assuper = AnnotatedTypes.asSuper(f.types, f, falseType, alub);
        if (assuper != null) {
            falseType = assuper;
        }
        // however, asSuper returns null for compound types,
        // e.g. see Ternary test case for Nullness Checker.
        // TODO: Can we adapt asSuper to handle those correctly?

        if (trueType.equals(falseType)) {
            return trueType;
        }

        List types = new ArrayList(2);
        types.add(trueType);
        types.add(falseType);
        AnnotatedTypes.annotateAsLub(f.processingEnv, f, alub, types);

        return alub;
    }

    @Override
    public AnnotatedTypeMirror visitIdentifier(IdentifierTree node,
                                               AnnotatedTypeFactory f) {
        if (node.getName().contentEquals("this")
                || node.getName().contentEquals("super")) {
            AnnotatedDeclaredType res = f.getSelfType(node);
            return res;
        }

        Element elt = TreeUtils.elementFromUse(node);
        AnnotatedTypeMirror selfType = f.getImplicitReceiverType(node);
        if (selfType != null) {
            return AnnotatedTypes.asMemberOf(f.types, f, selfType, elt).asUse();
        }

        return f.getAnnotatedType(elt);
    }

    @Override
    public AnnotatedTypeMirror visitInstanceOf(InstanceOfTree node,
                                               AnnotatedTypeFactory f) {
        return f.type(node);
    }

    @Override
    public AnnotatedTypeMirror visitLiteral(LiteralTree node,
                                            AnnotatedTypeFactory f) {
        return f.type(node);
    }

    @Override
    public AnnotatedTypeMirror visitMemberSelect(MemberSelectTree node,
                                                 AnnotatedTypeFactory f) {
        Element elt = TreeUtils.elementFromUse(node);

        if (TreeUtils.isClassLiteral(node)) {
            // the type of a class literal is the type of the "class" element.
            return f.getAnnotatedType(elt);
        }
        switch (elt.getKind()) {
            case METHOD:
            case PACKAGE: // "java.lang" in new java.lang.Short("2")
            case CLASS:  // o instanceof MyClass.InnerClass
            case ENUM:
            case INTERFACE: // o instanceof MyClass.InnerInterface
            case ANNOTATION_TYPE:
                return f.fromElement(elt);
            default:
                // Fall-through.
        }

        if (node.getIdentifier().contentEquals("this")) {
            // TODO: why don't we use getSelfType here?
            return f.getEnclosingType((TypeElement) InternalUtils.symbol(node.getExpression()), node);
        } else {
            // We need the original t with the implicit annotations
            AnnotatedTypeMirror t = f.getAnnotatedType(node.getExpression());
            if (t instanceof AnnotatedDeclaredType || t instanceof AnnotatedArrayType
                    || t instanceof AnnotatedTypeVariable) {
                return AnnotatedTypes.asMemberOf(f.types, f, t, elt).asUse();
            }
        }
        ErrorReporter.errorAbort("TypeFromExpressionVisitor.visitMemberSelect unexpected element or type: " + node.toString());
        return null; // dead code
    }

    @Override
    public AnnotatedTypeMirror visitMethodInvocation(
            MethodInvocationTree node, AnnotatedTypeFactory f) {

        AnnotatedExecutableType ex = f.methodFromUse(node).first;
        return ex.getReturnType().asUse();
    }

    @Override
    public AnnotatedTypeMirror visitNewArray(NewArrayTree node,
                                             AnnotatedTypeFactory f) {

        // Don't use fromTypeTree here, because node.getType() is not an
        // array type!
        AnnotatedArrayType result = (AnnotatedArrayType)f.type(node);

        if (node.getType() == null) // e.g., byte[] b = {(byte)1, (byte)2};
            return result;

        annotateArrayAsArray(result, node, f);

        return result;
    }

    private AnnotatedTypeMirror descendBy(AnnotatedTypeMirror type, int depth) {
        AnnotatedTypeMirror result = type;
        while (depth > 0) {
            result = ((AnnotatedArrayType)result).getComponentType();
            depth--;
        }
        return result;
    }

    private void annotateArrayAsArray(AnnotatedArrayType result, NewArrayTree node, AnnotatedTypeFactory f) {
        // Copy annotations from the type.
        AnnotatedTypeMirror treeElem = f.fromTypeTree(node.getType());
        boolean hasInit = node.getInitializers() != null;
        AnnotatedTypeMirror typeElem = descendBy(result,
                hasInit ? 1 : node.getDimensions().size());
        while (true) {
            typeElem.addAnnotations(treeElem.getAnnotations());
            if (!(treeElem instanceof AnnotatedArrayType)) break;
            assert typeElem instanceof AnnotatedArrayType;
            treeElem = ((AnnotatedArrayType)treeElem).getComponentType();
            typeElem = ((AnnotatedArrayType)typeElem).getComponentType();
        }
        // Add all dimension annotations.
        int idx = 0;
        AnnotatedTypeMirror level = result;
        while (level.getKind() == TypeKind.ARRAY) {
            AnnotatedArrayType array = (AnnotatedArrayType)level;
            List annos = InternalUtils.annotationsFromArrayCreation(node, idx++);
            array.addAnnotations(annos);
            level = array.getComponentType();
        }

        // Add top-level annotations.
        result.addAnnotations(InternalUtils.annotationsFromArrayCreation(node, -1));
    }

    /**
     * Creates an AnnotatedDeclaredType for the NewClassTree and adds, for each hierarchy, one of:
     * 
    *
  • an explicit annotation on the new class expression ({@code new @HERE MyClass()} ), or
  • *
  • an explicit annotation on the declaration of the class ({@code @HERE class MyClass {}} ), or
  • *
  • an explicit annotation on the declaration of the constructor ({@code @HERE public MyClass() {}} ), or
  • *
  • no annotation for a this hierarchy.
  • *
* * @param node NewClassTree * @param f the type factory * @return AnnotatedDeclaredType of {@code node} */ @Override public AnnotatedTypeMirror visitNewClass(NewClassTree node, AnnotatedTypeFactory f) { // constructorFromUse return type has implicits // so use fromNewClass which does diamond inference and only // contains explicit annotations and those inherited from the class declaration AnnotatedDeclaredType type = f.fromNewClass(node); // Enum constructors lead to trouble. // TODO: is there more to check? Can one annotate them? if (isNewEnum(type)) { return type; } // Add annotations that are on the constructor declaration. // constructorFromUse gives us resolution of polymorphic qualifiers. // However, it also applies defaulting, so we might apply too many qualifiers. // Therefore, ensure to only add the qualifiers that are explicitly on // the constructor, but then take the possibly substituted qualifier. AnnotatedExecutableType ex = f.constructorFromUse(node).first; AnnotatedTypes.copyOnlyExplicitConstructorAnnotations(f, type, ex); return type; } @Override public AnnotatedTypeMirror visitMemberReference(MemberReferenceTree node, AnnotatedTypeFactory f) { AnnotatedDeclaredType type = (AnnotatedDeclaredType) f.toAnnotatedType(InternalUtils.typeOf(node), false); return type; } @Override public AnnotatedTypeMirror visitLambdaExpression(LambdaExpressionTree node, AnnotatedTypeFactory f) { AnnotatedDeclaredType type = (AnnotatedDeclaredType) f.toAnnotatedType(InternalUtils.typeOf(node), false); return type; } private boolean isNewEnum(AnnotatedDeclaredType type) { return type.getUnderlyingType().asElement().getKind() == ElementKind.ENUM; } @Override public AnnotatedTypeMirror visitParenthesized(ParenthesizedTree node, AnnotatedTypeFactory f) { // Recurse on the expression inside the parens. return visit(node.getExpression(), f); } @Override public AnnotatedTypeMirror visitTypeCast(TypeCastTree node, AnnotatedTypeFactory f) { // Use the annotated type of the type in the cast. return f.fromTypeTree(node.getType()); } @Override public AnnotatedTypeMirror visitUnary(UnaryTree node, AnnotatedTypeFactory f) { // TODO: why not visit(node.getExpression(), f) return f.type(node); } @Override public AnnotatedTypeMirror visitWildcard(WildcardTree node, AnnotatedTypeFactory f) { AnnotatedTypeMirror bound = visit(node.getBound(), f); AnnotatedTypeMirror result = f.type(node); assert result instanceof AnnotatedWildcardType; // the first time getSuperBound/getExtendsBound is called the bound of this wildcard will be // appropriately initialized where for the type of node, instead of replacing that bound // we merge the annotations onto the initialized bound // This ensures that the structure of the wildcard will match that created by BoundsInitializer/createType if (node.getKind() == Tree.Kind.SUPER_WILDCARD) { AnnotatedTypeMerger.merge(bound, ((AnnotatedWildcardType) result).getSuperBound()); } else if (node.getKind() == Tree.Kind.EXTENDS_WILDCARD) { AnnotatedTypeMerger.merge(bound, ((AnnotatedWildcardType) result).getExtendsBound()); } return result; } @Override public AnnotatedTypeMirror visitPrimitiveType(PrimitiveTypeTree node, AnnotatedTypeFactory f) { // for e.g. "int.class" return f.fromTypeTree(node); } @Override public AnnotatedTypeMirror visitArrayType(ArrayTypeTree node, AnnotatedTypeFactory f) { // for e.g. "int[].class" return f.fromTypeTree(node); } @Override public AnnotatedTypeMirror visitParameterizedType(ParameterizedTypeTree node, AnnotatedTypeFactory f) { return f.fromTypeTree(node); } @Override public AnnotatedTypeMirror visitIntersectionType(IntersectionTypeTree node, AnnotatedTypeFactory f) { return f.fromTypeTree(node); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy