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

framework.src.org.checkerframework.qualframework.poly.SimpleQualifierParameterAnnotationConverter 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.qualframework.poly;


import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.ErrorReporter;
import org.checkerframework.qualframework.poly.PolyQual.GroundQual;
import org.checkerframework.qualframework.poly.PolyQual.QualVar;
import org.checkerframework.qualframework.util.ExtendedExecutableType;
import org.checkerframework.qualframework.util.ExtendedTypeMirror;

import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * SimpleQualifierParameterAnnotationConverter abstracts the logic to convert annotations to qualifiers
 * for typical qual-poly types systems that that support @Wild, @Var, and qualifier parameters.
 *
 * {@link SimpleQualifierParameterAnnotationConverter#getQualifier} should be implemented to convert
 * an annotation to a type system specific qualifier (e.g. @Regex or @Tainted).
 *
 */
public abstract class SimpleQualifierParameterAnnotationConverter implements QualifierParameterAnnotationConverter {

    /**
     * The default "Target" in an annotation is the primary qualifier.
     * We can't use null in the annotation, so we use this special value.
     */
    public static final String PRIMARY_TARGET="_primary";
    public static final String TARGET_PARAM_NAME = "param";
    /** The name of the qualifier parameter to use for polymorphic qualifiers. */
    public static final String POLY_NAME = "_poly";

    // Annotation field names
    protected static final String SOURCE_VALUE_NAME = "arg";
    protected static final String WILDCARD_NAME = "wildcard";

    protected final String MULTI_ANNO_NAME_PREFIX;
    protected final CombiningOperation lowerOp;
    protected final CombiningOperation upperOp;
    protected final Q BOTTOM;
    protected final Q TOP;
    protected final Q DEFAULT_QUAL;

    private final Class classAnno;
    private final Class methodAnno;
    private final Class polyAnno;
    private final Class varAnno;
    private final Class wildAnno;

    private final Set supportedAnnotationNames;
    private final Set specialCaseAnnotations;

    /**
     * Construct a SimpleQualifierParameterAnnotationConverter.
     */
    public SimpleQualifierParameterAnnotationConverter(AnnotationConverterConfiguration config) {

        this.MULTI_ANNO_NAME_PREFIX = config.getMultiAnnoNamePrefix();
        if (config.getSupportedAnnotationNames() == null ||
                config.getSupportedAnnotationNames().isEmpty()) {
            ErrorReporter.errorAbort("supportedAnnotationNames must be a list of type system qualifiers.");
        }
        this.supportedAnnotationNames = config.getSupportedAnnotationNames();

        if (config.getSpecialCaseAnnotations() == null) {
            this.specialCaseAnnotations = new HashSet<>();
        } else {
            this.specialCaseAnnotations = config.getSpecialCaseAnnotations();
        }
        this.lowerOp = config.getLowerOp();
        this.upperOp = config.getUpperOp();
        this.classAnno = config.getClassAnno();
        this.methodAnno = config.getMethodAnno();
        this.polyAnno = config.getPolyAnno();
        this.varAnno = config.getVarAnno();
        this.wildAnno = config.getWildAnno();
        this.TOP = config.getTop();
        this.BOTTOM = config.getBottom();
        this.DEFAULT_QUAL = config.getDefaultQual();
    }

    /**
     * Create a type system qualifier based on an annotation.
     *
     * @param anno the annotation
     * @return the resulting qualifier
     */
    public abstract Q getQualifier(AnnotationMirror anno);

    /**
     * Special case handle the AnnotationMirror. Useful for when more control
     * is need when processing an annotation.
     */
    protected QualParams specialCaseHandle(AnnotationMirror anno) {
        return null;
    }

    /**
     * Convert a list of AnnotationMirrors to a QualParams. Each AnnotationMirror is converted into a QualParams.
     * The resulting QualParams are merged together to create the result.
     *
     * If no primary qualifier is found, DEFAULT_QUAL will be used.
     *
     * @param annos the collection of type annotations to parse
     * @return the QualParams
     */
    @Override
    public QualParams fromAnnotations(Collection annos) {
        Map> params = new HashMap<>();
        PolyQual primary = null;
        for (AnnotationMirror anno : annos) {
            Map> qualMap;
            PolyQual newPrimary;
            if (specialCaseAnnotations.contains(AnnotationUtils.annotationName(anno))) {
                QualParams result = specialCaseHandle(anno);
                qualMap = result;
                newPrimary = result.getPrimary();
            } else {
                qualMap = getQualifierMap(anno);
                newPrimary = getPrimaryAnnotation(anno);
            }

            mergeParams(params, qualMap);

            if (primary != null && newPrimary != null) {
                primary = primary.combineWith(newPrimary, lowerOp);
            } else {
                primary = newPrimary;
            }
        }

        if (primary == null) {
            primary = new GroundQual<>(DEFAULT_QUAL);
        }

        return new QualParams<>(params, primary);
    }

    /**
     * Merge two QualParam maps. Each annotation will be converted into a Map, so the map from
     * multiple annotations will need to be merged together.
     */
    private void mergeParams(
            Map> params,
            Map> newParams) {
        if (newParams == null) {
            return;
        }

        for (String name : newParams.keySet()) {
            if (!params.containsKey(name)) {
                params.put(name, newParams.get(name));
                continue;
            }

            Wildcard oldWild = params.get(name);
            Wildcard newWild = newParams.get(name);
            Wildcard combinedWild = oldWild.combineWith(newWild, lowerOp, upperOp);

            // System.err.printf("COMBINE[%s]: %s + %s = %s\n", name, oldWild, newWild, combinedWild);
            params.put(name, combinedWild);
        }
    }

    private Map> getQualifierMap(AnnotationMirror anno) {
        String name = AnnotationUtils.annotationName(anno);

        Map> result = null;
        if (name.startsWith(MULTI_ANNO_NAME_PREFIX)) {
            result = new HashMap<>();
            List subAnnos = AnnotationUtils.getElementValueArray(
                    anno, "value", AnnotationMirror.class, true);
            for (AnnotationMirror subAnno : subAnnos) {
                mergeParams(result, getQualifierMap(subAnno));
            }

        } else if (supportedAnnotationNames.contains(name)) {
            Q qual = getQualifier(anno);
            String target = AnnotationUtils.getElementValue(anno, TARGET_PARAM_NAME, String.class, true);
            if (!PRIMARY_TARGET.equals(target)) {
                result = Collections.singletonMap(target, handleWildcard(anno, new Wildcard<>(qual)));
            }

        } else if (name.equals(varAnno.getName())) {
            String target = AnnotationUtils.getElementValue(anno, TARGET_PARAM_NAME, String.class, true);
            String value = AnnotationUtils.getElementValue(anno, SOURCE_VALUE_NAME, String.class, true);
            if (!PRIMARY_TARGET.equals(target)) {
                Wildcard valueWild = handleWildcard(anno, new Wildcard<>(
                        new QualVar<>(value, BOTTOM, TOP)));
                result = Collections.singletonMap(target, valueWild);
            }

        } else if (name.equals(polyAnno.getName())) {
            String target = AnnotationUtils.getElementValue(anno, TARGET_PARAM_NAME, String.class, true);
            if (!PRIMARY_TARGET.equals(target)) {
                Wildcard polyWild = new Wildcard<>(
                        new QualVar<>(POLY_NAME, BOTTOM, TOP));
                result = Collections.singletonMap(target, polyWild);
            }

        } else if (name.equals(wildAnno.getName())) {
            String target = AnnotationUtils.getElementValue(anno, TARGET_PARAM_NAME, String.class, true);
            result = Collections.singletonMap(target, new Wildcard<>(BOTTOM, TOP));
        }

        return result;
    }

    private Wildcard handleWildcard(AnnotationMirror anno, Wildcard current) {
        org.checkerframework.qualframework.poly.qual.Wildcard wildcard =
                AnnotationUtils.getElementValueEnum(anno, WILDCARD_NAME,
                        org.checkerframework.qualframework.poly.qual.Wildcard.class, true);

        switch (wildcard) {
            case SUPER:
                return new Wildcard<>(current.getLowerBound(), new GroundQual<>(TOP));
            case EXTENDS:
                return new Wildcard<>(new GroundQual<>(BOTTOM), current.getUpperBound());
            default:
                return current;
        }
    }

    private PolyQual getPrimaryAnnotation(AnnotationMirror anno) {

        String name = AnnotationUtils.annotationName(anno);
        PolyQual newQual = null;

        if (supportedAnnotationNames.contains(name)) {
            Q qual = getQualifier(anno);
            String target = AnnotationUtils.getElementValue(anno, TARGET_PARAM_NAME, String.class, true);
            if (PRIMARY_TARGET.equals(target)) {
                newQual = new GroundQual<>(qual);
            }

        } else if (name.equals(varAnno.getName())) {
            String target = AnnotationUtils.getElementValue(anno, TARGET_PARAM_NAME, String.class, true);
            String value = AnnotationUtils.getElementValue(anno, SOURCE_VALUE_NAME, String.class, true);
            if (PRIMARY_TARGET.equals(target)) {
                newQual = new QualVar<>(value, BOTTOM, TOP);
            }

        } else if (name.equals(polyAnno.getName())) {
            String target = AnnotationUtils.getElementValue(anno, TARGET_PARAM_NAME, String.class, true);
            if (PRIMARY_TARGET.equals(target)) {
                newQual = new QualVar<>(POLY_NAME, BOTTOM, TOP);
            }
        }
        return newQual;
    }

    @Override
    public boolean isAnnotationSupported(AnnotationMirror anno) {
        String name = AnnotationUtils.annotationName(anno);
        // Avoid running getQualifierMap on Multi* annotations, since that could
        // involve a nontrivial amount of work.
        return name.startsWith(MULTI_ANNO_NAME_PREFIX)
            || specialCaseAnnotations.contains(name)
            || getQualifierMap(anno) != null
            || getPrimaryAnnotation(anno) != null;
    }

    @Override
    public Set getDeclaredParameters(Element elt, Set declAnnotations, ExtendedTypeMirror type) {
        Set result = new HashSet<>();
        try {
            for (AnnotationMirror anno: declAnnotations) {
                if (AnnotationUtils.areSameByClass(anno, methodAnno)
                    || AnnotationUtils.areSameByClass(anno, classAnno)) {

                    result.add(AnnotationUtils.getElementValue(anno, "value",
                            String.class, true));
                }
            }
        } catch (Exception e) {
            ErrorReporter.errorAbort("AnnotationConverter not configured correctly. Error looking up 'value' field.");
        }

        switch (elt.getKind()) {
            case CONSTRUCTOR:
            case METHOD:
                if (hasPolyAnnotation((ExtendedExecutableType)type)) {
                    result.add(POLY_NAME);
                }
                break;

            default:
                break;
        }

        return result;
    }

    /**
     * @return true if type has a polymorphic qualifier
     */
    private boolean hasPolyAnnotation(ExtendedExecutableType type) {
        if (hasPolyAnnotationCheck(type.getReturnType())) {
            return true;
        }
        if (hasPolyAnnotationCheck(type.getReceiverType())) {
            return true;
        }
        for (ExtendedTypeMirror param : type.getParameterTypes()) {
            if (hasPolyAnnotationCheck(param)) {
                return true;
            }
        }
        return false;
    }

    /**
     * @return true if type has a polymorphic qualifier
     */
    protected boolean hasPolyAnnotationCheck(ExtendedTypeMirror type) {
        if (type == null) {
            return false;
        }
        for (AnnotationMirror anno : type.getAnnotationMirrors()) {
            if (AnnotationUtils.annotationName(anno).equals(polyAnno.getName())) {
                return true;
            }
        }
        return false;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy