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

framework.src.org.checkerframework.framework.type.TypeVariableSubstitutor Maven / Gradle / Ivy

package org.checkerframework.framework.type;

import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedArrayType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable;

import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Map.Entry;

import javax.lang.model.element.Element;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.util.Types;

/**
 * TypeVariableSusbtitutor replaces type variables from a declaration with arguments to its use.
 */
public class TypeVariableSubstitutor {

    /**
     * Given a mapping between type variable's to typeArgument, replace each instance of
     * type variable with a copy of type argument.
     * @see #substituteTypeVariable(AnnotatedTypeMirror, org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable)
     * @return a copy of typeMirror with its type variables substituted
     */
    public AnnotatedTypeMirror substitute(final Map typeParamToArg,
            final AnnotatedTypeMirror typeMirror) {

        return new Visitor(typeParamToArg).visit(typeMirror);
    }

    /**
     * Given the types of a type parameter declaration, the argument to that type parameter declaration,
     * and a given use of that declaration, return a substitute for the use with the correct annotations.
     *
     * To determine what primary annotations are correct for the substitute the following rules are used:
     * If the type variable use has a primary annotation then apply that primary annotation to the substitute.
     * Otherwise, use the annotations of the argument.
     *
     * @param argument    the argument to declaration (this will be a value in typeParamToArg)
     * @param use  the use that is being replaced
     * @return a shallow copy of argument with the appropriate annotations applied
     */
    protected AnnotatedTypeMirror substituteTypeVariable(final AnnotatedTypeMirror argument,
                                                         final AnnotatedTypeVariable use) {
        final AnnotatedTypeMirror substitute = argument.shallowCopy(false);
        substitute.addAnnotations(argument.getAnnotationsField());

        if (!use.getAnnotationsField().isEmpty()) {
            substitute.replaceAnnotations(use.getAnnotations());
        }

        return substitute;
    }

    protected class Visitor extends AnnotatedTypeCopier {
        private final Map elementToArgMap;

        public Visitor(final Map typeParamToArg) {
            elementToArgMap = new HashMap<>();

            for (Entry paramToArg : typeParamToArg.entrySet()) {
                elementToArgMap.put((TypeParameterElement) paramToArg.getKey().asElement(), paramToArg.getValue());
            }
        }

        @Override
        public AnnotatedTypeMirror visitArray(AnnotatedArrayType original,
                                              IdentityHashMap originalToCopy) {
            if (originalToCopy.containsKey(original)) {
                return originalToCopy.get(original);
            }

            final AnnotatedArrayType copy =  (AnnotatedArrayType) AnnotatedTypeMirror.createType(
                    original.getUnderlyingType(), original.atypeFactory, original.isDeclaration());
            maybeCopyPrimaryAnnotations(original, copy);
            originalToCopy.put(original, copy);

            // Substitution (along with any other operation that changes the component types of an AnnotatedTypeMirror)
            // may change the underlying Java type of components without updating the underlying Java
            // type of the parent type.  We use the underlying type for various purposes (including equals/hashcode)
            // so this can lead to unpredictable behavior.  Currently, we update the underlying type when
            // substituting on arrays in order to avoid an ErrorAbort in LubTypeVariableAnnotator.
            //TODO: Presumably there are more cases in which we want to do this
            final AnnotatedTypeMirror componentType = visit(original.getComponentType(), originalToCopy);
            final Types types = componentType.atypeFactory.types;

            final AnnotatedArrayType correctedCopy;
            if (!types.isSameType(componentType.getUnderlyingType(), copy.getUnderlyingType()) &&
                componentType.getKind() != TypeKind.WILDCARD) { //TODO: THIS SHOULD BE CAPTURE CONVERTED
                final TypeMirror underlyingType = types.getArrayType(componentType.getUnderlyingType());
                correctedCopy = (AnnotatedArrayType) AnnotatedTypeMirror.createType(underlyingType, copy.atypeFactory, false);
                correctedCopy.addAnnotations(copy.getAnnotations());

            } else {
                correctedCopy = copy;
            }

            correctedCopy.setComponentType(componentType);

            return correctedCopy;
        }

        @Override
        public AnnotatedTypeMirror visitTypeVariable(
                AnnotatedTypeVariable original,
                IdentityHashMap originalToCopy) {

            if (visitingExecutableTypeParam) {
                // AnnotatedExecutableType differs from AnnotatedDeclaredType in that its list of
                // type parameters cannot be adapted in place since the AnnotatedExecutable.typeVarTypes
                // field is of type AnnotatedTypeVariable and not AnnotatedTypeMirror.
                // When substituting, all component types that contain a use of the executable's type parameters
                // will be substituted.  The executable's type parameters will have their bounds substituted
                // but the top-level AnnotatedTypeVariable's will remain
                visitingExecutableTypeParam = false;
                return super.visitTypeVariable(original, originalToCopy);

            } else {
                final Element typeVarElem = original.getUnderlyingType().asElement();
                if (elementToArgMap.containsKey(typeVarElem)) {
                    final AnnotatedTypeMirror argument = elementToArgMap.get(typeVarElem);
                    return substituteTypeVariable(argument, original);
                }
            }

            return super.visitTypeVariable(original, originalToCopy);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy