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

framework.src.org.checkerframework.framework.util.element.TypeVarUseApplier 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.util.element;

import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedArrayType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedIntersectionType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable;
import org.checkerframework.framework.type.ElementAnnotationApplier;
import org.checkerframework.javacutil.ErrorReporter;

import static org.checkerframework.framework.util.element.ElementAnnotationUtil.annotateViaTypeAnnoPosition;
import static org.checkerframework.framework.util.element.ElementAnnotationUtil.contains;
import static org.checkerframework.framework.util.element.ElementAnnotationUtil.getTypeAtLocation;

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

import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeParameterElement;

import com.sun.tools.javac.code.Attribute;
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import com.sun.tools.javac.code.Symbol.VarSymbol;
import com.sun.tools.javac.code.TargetType;
import com.sun.tools.javac.code.TypeAnnotationPosition;

/**
 * Apply annotations to the use of a type parameter declaration
 */
public class TypeVarUseApplier {

    public static void apply(final AnnotatedTypeMirror type, final Element element, final AnnotatedTypeFactory typeFactory)  {
        new TypeVarUseApplier(type, element, typeFactory).extractAndApply();
    }

    private static ElementKind[] acceptedKinds = {
        ElementKind.PARAMETER, ElementKind.FIELD, ElementKind.LOCAL_VARIABLE, ElementKind.RESOURCE_VARIABLE,
        ElementKind.METHOD
    };

    /**
     * @return true if type is an AnnotatedTypeVariable, or an AnnotatedArrayType with a type variable component,
     *         and the element is not a TYPE_PARAMETER
     */
    public static boolean accepts(AnnotatedTypeMirror type, Element element) {
        return ( type instanceof AnnotatedTypeVariable || isGenericArrayType(type)) &&
                contains(element.getKind(), acceptedKinds);
    }

    private static boolean isGenericArrayType(AnnotatedTypeMirror type) {
        return type instanceof AnnotatedArrayType &&
               getNestedComponentType(type) instanceof AnnotatedTypeVariable;
    }

    private static AnnotatedTypeMirror getNestedComponentType(AnnotatedTypeMirror type) {

        AnnotatedTypeMirror componentType = type;
        while (componentType instanceof AnnotatedArrayType) {
            componentType = ((AnnotatedArrayType) componentType).getComponentType();
        }

        return componentType;

    }

    // In order to avoid sprinkling code for type parameter uses all over the various locations
    // uses can show up we also handle generic array types.  T [] myTArr;
    private final AnnotatedArrayType arrayType;

    private final AnnotatedTypeVariable typeVariable;
    private final TypeParameterElement declarationElem;
    private final Element useElem;

    private AnnotatedTypeFactory typeFactory;

    TypeVarUseApplier(final AnnotatedTypeMirror type, final Element element, final AnnotatedTypeFactory typeFactory) {
        if (!accepts(type, element)) {
            ErrorReporter.errorAbort("TypeParamUseApplier does not accept type/element combination (" +
                                     " type ( " + type + " ) element ( " + element + " ) ");
        }

        if (isGenericArrayType(type)) {
            this.arrayType    = (AnnotatedArrayType) type;
            this.typeVariable = (AnnotatedTypeVariable) getNestedComponentType(type);
            this.declarationElem =  (TypeParameterElement) typeVariable.getUnderlyingType().asElement();
            this.useElem = element;
            this.typeFactory = typeFactory;

        } else {
            this.arrayType = null;
            this.typeVariable = (AnnotatedTypeVariable) type;
            this.declarationElem =  (TypeParameterElement) typeVariable.getUnderlyingType().asElement();
            this.useElem = element;
            this.typeFactory = typeFactory;

        }
    }

    /**
     * Applies the bound annotations from the declaration of the type parameter and
     * then applies the explicit annotations written on the type variable
     */
    public void extractAndApply() {

        // apply declaration annotations
        ElementAnnotationApplier.apply(typeVariable, declarationElem, typeFactory);

        final List annotations = getAnnotations( useElem, declarationElem );

        final List typeVarAnnotations;
        if (arrayType != null) {
            // if the outer-most type is an array type then we want to ensure the outer annotations
            // are not applied as the type variables primary annotation
            typeVarAnnotations = removeComponentAnnotations(arrayType, annotations);
            annotateViaTypeAnnoPosition(arrayType, annotations);

        } else {
            typeVarAnnotations = annotations;
        }

        for (final Attribute.TypeCompound annotation : typeVarAnnotations) {
            typeVariable.removeAnnotationInHierarchy(annotation);
            typeVariable.addAnnotation(annotation);

            final List upperBounds;
            if (typeVariable.getUpperBound() instanceof AnnotatedIntersectionType) {
                upperBounds = typeVariable.getUpperBound().directSuperTypes();
            } else {
                upperBounds = Arrays.asList(typeVariable.getUpperBound());
            }

            //TODO: Should we just make primary annotations on annotated intersection types apply to all of
            //TODO: them?  Que dealio?  What should we do?
            for (final AnnotatedTypeMirror bound : upperBounds) {
                bound.removeAnnotationInHierarchy(annotation);
                bound.addAnnotation(annotation);
            }
        }
    }

    private List removeComponentAnnotations(final AnnotatedArrayType arrayType,
                                                                    final List annotations) {

        final List componentAnnotations = new ArrayList();

        if (arrayType != null) {
            for (int i = 0; i < annotations.size(); ) {
                final Attribute.TypeCompound anno = annotations.get(i);
                if (isBaseComponent(arrayType, anno)) {
                    componentAnnotations.add(anno);
                    annotations.remove(anno);
                } else {
                    i++;
                }
            }
        }

        return componentAnnotations;
    }

    private boolean isBaseComponent(final AnnotatedArrayType arrayType, final Attribute.TypeCompound anno) {
        return getTypeAtLocation(arrayType, anno.getPosition().location).getClass().equals(AnnotatedTypeVariable.class);
    }

    /**
     * Depending on what element type the annotations are stored on, the relevant annotations might
     * be stored with different annotation positions.  getAnnotations finds the correct annotations
     * by annotation position and element kind and returns them
     */
    private static List getAnnotations(final Element useElem, final Element declarationElem) {
        final List annotations;
        switch (useElem.getKind()) {
            case METHOD:
                annotations = getReturnAnnos( useElem );
                break;

            case PARAMETER:
                annotations = getParameterAnnos( useElem );
                break;

            case FIELD:
            case LOCAL_VARIABLE:
            case RESOURCE_VARIABLE:
                annotations = getVariableAnnos( useElem );
                break;

            default:
                ErrorReporter.errorAbort("TypeVarUseApplier::extractAndApply : "        +
                        "Unhandled element kind " + useElem.getKind() +
                        "useElem ( " + useElem + " ) "                +
                        "declarationElem ( " + declarationElem + " ) ");
                annotations = null; // dead code
        }

        return annotations;
    }

    /**
     * @return annotations on an element that apply to variable declarations
     */
    private static List getVariableAnnos(final Element variableElem) {
        final VarSymbol varSymbol = (VarSymbol) variableElem;
        final List annotations = new ArrayList();

        for (Attribute.TypeCompound anno : varSymbol.getRawTypeAttributes()) {

            TypeAnnotationPosition pos = anno.position;
            switch (pos.type) {
                case FIELD:
                case LOCAL_VARIABLE:
                case RESOURCE_VARIABLE:
                case EXCEPTION_PARAMETER:
                    annotations.add(anno);
                    break;

                default:
            }
        }

        return annotations;
    }

    /**
     * Currently, the metadata for storing annotations (i.e. the Attribute.TypeCompounds) is null for binary-only
     * parameters and type parameters.  However, it is present on the method.  So in order to ensure that we correctly
     * retrieve the annotations we need to index from the method and retrieve the annotations from its metadata.
     * @return a list of annotations that were found on METHOD_FORMAL_PARAMETERS that match the parameter index
     * of the input element in the parent methods formal parameter list
     */
    private static List getParameterAnnos(final Element paramElem) {
        final Element enclosingElement = paramElem.getEnclosingElement();
        if (!(enclosingElement instanceof ExecutableElement)) {
            ErrorReporter.errorAbort("Bad element passed to TypeFromElement.getTypeParameterAnnotationAttributes: " +
                    "element: " + paramElem + " not found in enclosing executable: " + enclosingElement);
        }

        final MethodSymbol enclosingMethod = (MethodSymbol) paramElem.getEnclosingElement();

        final List annotations = enclosingMethod.getRawTypeAttributes();
        final int paramIndex = enclosingMethod.getParameters().indexOf(paramElem);

        final List result = new ArrayList();
        for (final Attribute.TypeCompound typeAnno : annotations) {
            if (typeAnno.position.type == TargetType.METHOD_FORMAL_PARAMETER) {
                if (typeAnno.position.parameter_index == paramIndex) {
                    result.add(typeAnno);
                }
            }
        }

        return result;
    }

    /**
     * @return the annotations on the return type of the input ExecutableElement
     */
    private static List getReturnAnnos(final Element methodElem) {
        if (!(methodElem instanceof ExecutableElement)) {
            ErrorReporter.errorAbort("Bad element passed to TypeVarUseApplier.getReturnAnnos:" + methodElem);
        }

        final MethodSymbol enclosingMethod = (MethodSymbol) methodElem;

        final List annotations = enclosingMethod.getRawTypeAttributes();
        final List result = new ArrayList();
        for (final Attribute.TypeCompound typeAnno : annotations) {
            if (typeAnno.position.type == TargetType.METHOD_RETURN) {
                result.add(typeAnno);
            }
        }

        return result;
    }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy