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

framework.src.org.checkerframework.framework.util.element.ParamApplier 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.ElementAnnotationApplier;
import org.checkerframework.javacutil.ErrorReporter;
import org.checkerframework.javacutil.Pair;

import static org.checkerframework.framework.util.element.ElementAnnotationUtil.annotateViaTypeAnnoPosition;
import static org.checkerframework.framework.util.element.ElementAnnotationUtil.partitionByTargetType;

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

import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.VariableElement;

import com.sun.source.tree.LambdaExpressionTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.VariableTree;
import com.sun.tools.javac.code.Attribute;
import com.sun.tools.javac.code.Attribute.TypeCompound;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.TargetType;

import static com.sun.tools.javac.code.TargetType.CAST;
import static com.sun.tools.javac.code.TargetType.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT;
import static com.sun.tools.javac.code.TargetType.CONSTRUCTOR_REFERENCE;
import static com.sun.tools.javac.code.TargetType.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT;
import static com.sun.tools.javac.code.TargetType.EXCEPTION_PARAMETER;
import static com.sun.tools.javac.code.TargetType.INSTANCEOF;
import static com.sun.tools.javac.code.TargetType.LOCAL_VARIABLE;
import static com.sun.tools.javac.code.TargetType.METHOD_FORMAL_PARAMETER;
import static com.sun.tools.javac.code.TargetType.METHOD_INVOCATION_TYPE_ARGUMENT;
import static com.sun.tools.javac.code.TargetType.METHOD_RECEIVER;
import static com.sun.tools.javac.code.TargetType.METHOD_REFERENCE;
import static com.sun.tools.javac.code.TargetType.METHOD_REFERENCE_TYPE_ARGUMENT;
import static com.sun.tools.javac.code.TargetType.METHOD_RETURN;
import static com.sun.tools.javac.code.TargetType.METHOD_TYPE_PARAMETER;
import static com.sun.tools.javac.code.TargetType.METHOD_TYPE_PARAMETER_BOUND;
import static com.sun.tools.javac.code.TargetType.NEW;
import static com.sun.tools.javac.code.TargetType.RESOURCE_VARIABLE;
import static com.sun.tools.javac.code.TargetType.THROWS;

/**
 * Adds annotations to one formal parameter of a method or lambda within a method.
 */
public class ParamApplier extends IndexedElementAnnotationApplier {

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

    public static int RECEIVER_PARAM_INDEX = Integer.MIN_VALUE;

    public static boolean accepts(final AnnotatedTypeMirror type, final Element element) {
        return element.getKind() == ElementKind.PARAMETER;
    }

    private final Symbol.MethodSymbol enclosingMethod;
    private final boolean isLambdaParam;
    private final Integer lambdaParamIndex;
    private final LambdaExpressionTree lambdaTree;

    ParamApplier(AnnotatedTypeMirror type, Element element, AnnotatedTypeFactory typeFactory) {
        super(type, element);
        enclosingMethod = getParentMethod( element );

        if (enclosingMethod.getKind() != ElementKind.INSTANCE_INIT
         && enclosingMethod.getKind() != ElementKind.STATIC_INIT
         && enclosingMethod.getParameters().contains(element)) {
            lambdaTree = null;
            isLambdaParam = false;
            lambdaParamIndex = null;

        } else {
            Pair paramToEnclosingLambda =
                ElementAnnotationApplier.getParamAndLambdaTree((VariableElement) element, typeFactory);

            if (paramToEnclosingLambda != null) {
                VariableTree paramDecl = paramToEnclosingLambda.first;
                lambdaTree = paramToEnclosingLambda.second;
                isLambdaParam = true;
                lambdaParamIndex = lambdaTree.getParameters().indexOf(paramDecl);

            } else {
                lambdaTree = null;
                isLambdaParam = false;
                lambdaParamIndex = null;
            }
        }
    }

    /**
     * @return the index of element its parent method's parameter list.
     * Integer.MIN_VALUE if the element is the receiver parameter.
     */
    @Override
    public int getElementIndex() {
        if (isLambdaParam) {
            return lambdaParamIndex;
        }

        if (isReceiver(element)) {
            return RECEIVER_PARAM_INDEX;
        }

        final int paramIndex = enclosingMethod.getParameters().indexOf(element);
        if (paramIndex == -1) {
            ErrorReporter.errorAbort("Could not find parameter Element in parameter list! " +
                    "Parameter( " + element + " ) Parent ( " + enclosingMethod + " ) ");
        }

        return paramIndex;
    }

    /**
     * @return the parameter index of anno's TypeAnnotationPosition
     */
    @Override
    public int getTypeCompoundIndex(Attribute.TypeCompound anno) {
        return anno.getPosition().parameter_index;
    }

    /**
     * @return {TargetType.METHOD_FORMAL_PARAMETER, TargetType.METHOD_RECEIVER}
     */
    @Override
    protected TargetType[] annotatedTargets() {
        return new TargetType[]{ METHOD_FORMAL_PARAMETER, METHOD_RECEIVER };
    }

    /**
     * @return any annotation TargetType that can be found on a method
     */
    @Override
    protected TargetType[] validTargets() {
        return new TargetType []{
             METHOD_FORMAL_PARAMETER, METHOD_RETURN, THROWS, METHOD_TYPE_PARAMETER, METHOD_TYPE_PARAMETER_BOUND,
             LOCAL_VARIABLE, RESOURCE_VARIABLE, EXCEPTION_PARAMETER, NEW, CAST, INSTANCEOF,
             METHOD_INVOCATION_TYPE_ARGUMENT, CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT, METHOD_REFERENCE,
             CONSTRUCTOR_REFERENCE, METHOD_REFERENCE_TYPE_ARGUMENT, CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT
        };
    }

    /**
     * @return the TypeCompounds (annotations) of the enclosing method for this parameter
     */
    @Override
    protected Iterable getRawTypeAttributes() {
        return enclosingMethod.getRawTypeAttributes();
    }

    @Override
    protected Map> sift(Iterable typeCompounds) {
        // this will sift out the annotations that do not have the right position index
        final Map> targetClassToAnnos = super.sift(typeCompounds);

        final List targeted = targetClassToAnnos.get(TargetClass.TARGETED);
        final List valid    = targetClassToAnnos.get(TargetClass.VALID);

        // if this is a lambdaParam, filter out from targeted those annos that apply to method formal parameters
        // if this is a method formal param, filter out from targeted those annos that apply to lambdas
        int i = 0;
        while (i < targeted.size()) {
            final Tree onLambda = targeted.get(i).position.onLambda;
            if (onLambda == null) {
                if (!isLambdaParam) {
                    ++i;
                } else {
                    valid.add(targeted.remove(i));
                }

            } else {
                if (onLambda.equals(this.lambdaTree)) {
                    ++i;
                } else {
                    valid.add(targeted.remove(i));
                }

            }
        }

        return targetClassToAnnos;
    }

    /**
     * @param targeted type compounds with formal method parameter target types with parameter_index == getIndex
     */
    @Override
    protected void handleTargeted(final List targeted) {

        final List formalParams = new ArrayList<>();
        Map> targetToAnnos = partitionByTargetType(targeted, formalParams, METHOD_RECEIVER);

        if (isReceiver(element)) {
            annotateViaTypeAnnoPosition(type, targetToAnnos.get(METHOD_RECEIVER));

        } else {
            annotateViaTypeAnnoPosition(type, formalParams);

        }
    }

    /**
     * @return true if element represents the receiver parameter of a method
     */
    private boolean isReceiver(final Element element) {
        return element.getKind() == ElementKind.PARAMETER && element.getSimpleName().contentEquals("this");
    }

    @Override
    protected boolean isAccepted() {
        return accepts(type, element);
    }


    /**
     * Return the enclosing MethodSymbol of the given element, throwing an exception of the symbol's enclosing
     * element is not a MethodSymbol
     * @param methodChildElem some element that is a child of a method typeDeclaration (e.g. a parameter or return type)
     * @return the MethodSymbol of the method containing methodChildElem
     */
    public static Symbol.MethodSymbol getParentMethod(final Element methodChildElem) {
        if (!( methodChildElem.getEnclosingElement() instanceof Symbol.MethodSymbol)) {
            throw new RuntimeException("Element is not a direct child of a MethodSymbol. Element ( " + methodChildElem +
                    " parent ( " + methodChildElem.getEnclosingElement() + " ) ");
        }
        return (Symbol.MethodSymbol) methodChildElem.getEnclosingElement();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy