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

org.checkerframework.framework.type.BoundsInitializer 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.43.0
Show newest version
package org.checkerframework.framework.type;

import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.util.Context;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.IntersectionType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.type.WildcardType;
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.AnnotatedIntersectionType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedNoType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedNullType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedPrimitiveType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedUnionType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedWildcardType;
import org.checkerframework.framework.type.visitor.AnnotatedTypeVisitor;
import org.checkerframework.framework.util.PluginUtil;
import org.checkerframework.javacutil.ErrorReporter;
import org.checkerframework.javacutil.TypesUtils;

/**
 * BoundsInitializer creates AnnotatedTypeMirrors (without annotations) for the bounds of type
 * variables and wildcards. Its static helper methods are called from AnnotatedTypeMirror. When an
 * initializer method is called for a particular bound, the entirety of that bound, including
 * circular references, will be created.
 */
public class BoundsInitializer {
    // ============================================================================================
    // Static helper methods called from AnnotatedTypeMirror to initialize bounds of wildcards or
    // type variables
    // ============================================================================================

    /**
     * Initializes the type arguments of {@code declaredType}. The upper bound of unbound wildcards
     * is set to the upper bound of the type parameter for which it is an argument. If {@code
     * declaredType} is raw, then the type arguments are uninferred wildcards.
     *
     * @param declaredType type whose arguments are initialized.
     */
    public static void initializeTypeArgs(AnnotatedDeclaredType declaredType) {
        final DeclaredType actualType = (DeclaredType) declaredType.actualType;
        if (actualType.getTypeArguments().isEmpty() && !declaredType.wasRaw()) {
            // No type arguments to infer.
            return;
        }

        final TypeElement typeElement =
                (TypeElement) declaredType.atypeFactory.types.asElement(actualType);
        final List typeArgs = new ArrayList<>();

        // Create AnnotatedTypeMirror for each type argument and store them in the typeArgsMap.
        Map typeArgMap = new HashMap<>();
        for (int i = 0; i < typeElement.getTypeParameters().size(); i++) {
            TypeMirror javaTypeArg;
            if (declaredType.wasRaw()) {
                TypeVariable typeVariable =
                        (TypeVariable) typeElement.getTypeParameters().get(i).asType();
                javaTypeArg = getUpperBoundAsWildcard(typeVariable, declaredType.atypeFactory);
            } else {
                javaTypeArg = declaredType.getUnderlyingType().getTypeArguments().get(i);
            }

            final AnnotatedTypeMirror typeArg =
                    AnnotatedTypeMirror.createType(javaTypeArg, declaredType.atypeFactory, false);
            if (typeArg.getKind() == TypeKind.WILDCARD) {
                AnnotatedWildcardType wildcardType = (AnnotatedWildcardType) typeArg;
                wildcardType.setTypeVariable(typeElement.getTypeParameters().get(i));
                if (declaredType.wasRaw()) {
                    wildcardType.setUninferredTypeArgument();
                }
            }
            typeArgs.add(typeArg);
            typeArgMap.put((TypeVariable) typeElement.getTypeParameters().get(i).asType(), typeArg);
        }

        // Initialize type argument bounds using the typeArgsMap.
        for (AnnotatedTypeMirror typeArg : typeArgs) {
            switch (typeArg.getKind()) {
                case WILDCARD:
                    AnnotatedWildcardType wildcardType = (AnnotatedWildcardType) typeArg;
                    initializeExtendsBound(wildcardType, typeArgMap);
                    initializeSuperBound(wildcardType, typeArgMap);
                    break;
                case TYPEVAR:
                    initializeBounds((AnnotatedTypeVariable) typeArg, typeArgMap);
                    break;
                default:
                    // do nothing
            }
        }
        declaredType.typeArgs = Collections.unmodifiableList(typeArgs);
    }

    /**
     * Returns a wildcard whose upper bound is the same as {@code typeVariable}. If the upper bound
     * is an intersection, then this method returns an unbound wildcard.
     */
    private static WildcardType getUpperBoundAsWildcard(
            TypeVariable typeVariable, AnnotatedTypeFactory factory) {
        TypeMirror upperBound = typeVariable.getUpperBound();
        switch (upperBound.getKind()) {
            case ARRAY:
            case DECLARED:
            case TYPEVAR:
                return factory.types.getWildcardType(upperBound, null);
            case INTERSECTION:
                // Can't create a wildcard with an intersection as the upper bound, so use
                // an unbound wildcard instead.  The extends bound of the
                // AnnotatedWildcardType will be initialized properly by this class.
                return factory.types.getWildcardType(null, null);
            default:
                ErrorReporter.errorAbort(
                        "Unexpected upper bound kind: %s type: %s",
                        upperBound.getKind(), upperBound);
                return null; // dead code
        }
    }

    /**
     * Create the entire lower bound and upper bound, with no missing information, for typeVar. If a
     * typeVar is recursive the appropriate cycles will be introduced in the type
     *
     * @param typeVar the type variable whose lower bound is being initialized
     */
    public static void initializeBounds(final AnnotatedTypeVariable typeVar) {
        initializeBounds(typeVar, null);
    }

    /**
     * Create the entire lower bound and upper bound, with no missing information, for typeVar. If a
     * typeVar is recursive the appropriate cycles will be introduced in the type
     *
     * @param typeVar the type variable whose lower bound is being initialized
     * @param map a mapping of type parameters to type arguments. May be null.
     */
    private static void initializeBounds(
            final AnnotatedTypeVariable typeVar, Map map) {
        final Set annos = saveAnnotations(typeVar);

        InitializerVisitor visitor =
                new InitializerVisitor(new TypeVariableStructure(null, typeVar), map);
        visitor.initializeLowerBound(typeVar);
        visitor.resolveTypeVarReferences(typeVar);

        InitializerVisitor visitor2 =
                new InitializerVisitor(new TypeVariableStructure(null, typeVar), map);
        visitor2.initializeUpperBound(typeVar);
        visitor2.resolveTypeVarReferences(typeVar);

        restoreAnnotations(typeVar, annos);
    }

    /**
     * If we are initializing a type variable with a primary annotation than we should first
     * initialize it as if it were a declaration (i.e. as if it had no primary annotations) and then
     * apply the primary annotations. We do this so that when we make copies of the original type to
     * represent recursive references the recursive references don't have the primary annotation.
     *
     * 
{@code
     * e.g.   given the declaration {@code >}
     *        if we do not do this, the NonNull on the use @NonNull E
     *        would be copied to the primary annotation on E in the bound {@code List}
     *        i.e. the use would be {@code <@NonNull E extends @NonNull List<@NonNull E>>}
     *             rather than      {@code <@NonNull E extends @NonNull List>}
     * }
*/ private static Set saveAnnotations(final AnnotatedTypeMirror type) { if (!type.getAnnotationsField().isEmpty()) { final Set annos = new HashSet<>(type.getAnnotations()); type.clearAnnotations(); return annos; } return null; } private static void restoreAnnotations( final AnnotatedTypeMirror type, final Set annos) { if (annos != null) { type.addAnnotations(annos); } } /** * Create the entire super bound, with no missing information, for wildcard. If a wildcard is * recursive the appropriate cycles will be introduced in the type * * @param wildcard the wildcard whose lower bound is being initialized */ public static void initializeSuperBound(final AnnotatedWildcardType wildcard) { initializeSuperBound(wildcard, null); } /** * Create the entire super bound, with no missing information, for wildcard. If a wildcard is * recursive the appropriate cycles will be introduced in the type * * @param wildcard the wildcard whose lower bound is being initialized * @param map a mapping of type parameters to type arguments. May be null. */ private static void initializeSuperBound( final AnnotatedWildcardType wildcard, Map map) { final Set annos = saveAnnotations(wildcard); InitializerVisitor visitor = new InitializerVisitor(new WildcardStructure(), map); visitor.initializeSuperBound(wildcard); visitor.resolveTypeVarReferences(wildcard); restoreAnnotations(wildcard, annos); } /** * Create the entire extends bound, with no missing information, for wildcard. If a wildcard is * recursive the appropriate cycles will be introduced in the type * * @param wildcard the wildcard whose extends bound is being initialized */ public static void initializeExtendsBound(final AnnotatedWildcardType wildcard) { initializeExtendsBound(wildcard, null); } /** * Create the entire extends bound, with no missing information, for wildcard. If a wildcard is * recursive the appropriate cycles will be introduced in the type * * @param wildcard the wildcard whose extends bound is being initialized * @param map a mapping of type parameters to type arguments. May be null. */ private static void initializeExtendsBound( final AnnotatedWildcardType wildcard, Map map) { final Set annos = saveAnnotations(wildcard); InitializerVisitor visitor = new InitializerVisitor(new WildcardStructure(), map); visitor.initializeExtendsBound(wildcard); visitor.resolveTypeVarReferences(wildcard); restoreAnnotations(wildcard, annos); } // ============================================================================================ // Classes and methods used to make the above static helper methods work // ============================================================================================ /** * Creates the AnnotatedTypeMirrors (without annotations) for the bounds of all type variables * and wildcards in a given type. If the type is recursive, {@code T extends Comparable}, * then all references to the same type variable are references to the same AnnotatedTypeMirror. */ private static class InitializerVisitor implements AnnotatedTypeVisitor { /** * The BoundStructure starting from the first wildcard or type variable bound initialization * that kicked this visitation off */ private final BoundStructure topLevelStructure; private BoundStructure currentStructure = null; private final Map typeVarToStructure = new HashMap<>(); // private final Map typeVarToRecord = new HashMap<>(); private final Map wildcards = new HashMap<>(); private final Map intersections = new HashMap<>(); private final Map typevars; // need current bound path public InitializerVisitor( BoundStructure boundStructure, Map typevars) { this.topLevelStructure = boundStructure; this.currentStructure = boundStructure; if (typevars != null) { this.typevars = typevars; } else { this.typevars = new HashMap<>(); } if (boundStructure instanceof TypeVariableStructure) { TypeVariableStructure typeVarStruct = (TypeVariableStructure) boundStructure; typeVarToStructure.put(typeVarStruct.typeVar, typeVarStruct); } } // ---------------------------------------------------------------------------------------- // Visit methods that keep track of the path traversed through type variable bounds, and the // wildcards/intersections that have been encountered. // ---------------------------------------------------------------------------------------- @Override public Void visit(AnnotatedTypeMirror type) { type.accept(this, null); return null; } @Override public Void visit(AnnotatedTypeMirror type, Void aVoid) { visit(type); return null; } @Override public Void visitDeclared(AnnotatedDeclaredType type, Void aVoid) { initializeTypeArgs(type); return null; } @Override public Void visitIntersection(AnnotatedIntersectionType type, Void aVoid) { if (intersections.containsKey(type.getUnderlyingType())) { return null; } intersections.put((IntersectionType) type.getUnderlyingType(), type); final List supertypes = type.directSuperTypes(); for (int i = 0; i < supertypes.size(); i++) { final AnnotatedDeclaredType supertype = supertypes.get(i); final BoundPathNode node = addPathNode(new IntersectionNode(i)); visit(supertype); removePathNode(node); } return null; } @Override public Void visitUnion(AnnotatedUnionType type, Void aVoid) { final List alts = type.getAlternatives(); for (int i = 0; i < alts.size(); i++) { final AnnotatedDeclaredType alt = alts.get(i); final BoundPathNode node = addPathNode(new UnionNode(i)); visit(alt); removePathNode(node); } return null; } @Override public Void visitArray(AnnotatedArrayType type, Void aVoid) { if (!TypesUtils.isPrimitive(type.getComponentType().getUnderlyingType())) { // Only recur on component type if it's not a primitive. // Array component types are the only place a primitive is allowed in bounds final BoundPathNode componentNode = addPathNode(new ArrayComponentNode()); type.setComponentType(getOrVisit(type.getComponentType())); removePathNode(componentNode); } return null; } @Override public Void visitTypeVariable(AnnotatedTypeVariable type, Void aVoid) { this.currentStructure.addTypeVar(type.getUnderlyingType()); if (!haveSeenTypeVar(type)) { pushNewTypeVarStruct(type); initializeUpperBound(type); initializeLowerBound(type); popCurrentTypeVarStruct(type); } return null; } @Override public Void visitNull(AnnotatedNullType type, Void aVoid) { return null; } @Override public Void visitWildcard(AnnotatedWildcardType wildcard, Void aVoid) { if (wildcard.getSuperBoundField() == null) { initializeSuperBound(wildcard); } else { ErrorReporter.errorAbort( "Wildcard super field should not be initialized:\n" + "wildcard=" + wildcard.toString() + "currentPath=" + currentStructure.currentPath); } if (wildcard.getExtendsBoundField() == null) { initializeExtendsBound(wildcard); } else { ErrorReporter.errorAbort( "Wildcard extends field should not be initialized:\n" + "wildcard=" + wildcard.toString() + "currentPath=" + currentStructure.currentPath); } return null; } @Override public Void visitPrimitive(AnnotatedPrimitiveType type, Void aVoid) { return invalidType(type); } @Override public Void visitNoType(AnnotatedNoType type, Void aVoid) { return invalidType(type); } @Override public Void visitExecutable(AnnotatedExecutableType type, Void aVoid) { return invalidType(type); } /** * If the underlying type of (@code type} has been visited before, return the previous * AnnotatedTypeMirror. Otherwise, visit {@code type} and return it. * * @param type type to visit. * @return {@code type} or an AnnotatedTypeMirror with the same underlying type that was * previously visited. */ public AnnotatedTypeMirror getOrVisit(final AnnotatedTypeMirror type) { switch (type.getKind()) { case WILDCARD: final AnnotatedWildcardType wildcard = (AnnotatedWildcardType) type; if (wildcards.containsKey(wildcard.getUnderlyingType())) { return wildcards.get(wildcard.getUnderlyingType()); } break; case INTERSECTION: if (intersections.containsKey(type.getUnderlyingType())) { return intersections.get(type.getUnderlyingType()); } break; case TYPEVAR: if (typevars.containsKey(type.getUnderlyingType())) { return typevars.get(type.getUnderlyingType()); } break; default: // do nothing } visit(type); return type; } // ---------------------------------------------------------------------------------------- // public void initializeUpperBound(final AnnotatedTypeVariable typeVar) { final AnnotatedTypeMirror upperBound = createAndSetUpperBound(typeVar); final BoundPathNode pathNode = new UpperBoundNode(); addPathNode(pathNode); visit(upperBound); removePathNode(pathNode); } public void initializeLowerBound(final AnnotatedTypeVariable typeVar) { final AnnotatedTypeMirror lowerBound = createAndSetLowerBound(typeVar); final BoundPathNode pathNode = new LowerBoundNode(); addPathNode(pathNode); visit(lowerBound); removePathNode(pathNode); } public void initializeSuperBound(final AnnotatedWildcardType wildcard) { final AnnotatedTypeFactory typeFactory = wildcard.atypeFactory; final WildcardType underlyingType = wildcard.getUnderlyingType(); TypeMirror underlyingSuperBound = underlyingType.getSuperBound(); if (underlyingSuperBound == null) { underlyingSuperBound = TypesUtils.wildLowerBound( underlyingType, wildcard.atypeFactory.processingEnv); } final AnnotatedTypeMirror superBound = AnnotatedTypeMirror.createType(underlyingSuperBound, typeFactory, false); wildcard.setSuperBound(superBound); this.wildcards.put(wildcard.getUnderlyingType(), wildcard); final BoundPathNode superNode = addPathNode(new SuperNode()); visit(superBound); removePathNode(superNode); } public void initializeExtendsBound(final AnnotatedWildcardType wildcard) { final AnnotatedTypeFactory typeFactory = wildcard.atypeFactory; WildcardType javaWildcardType = wildcard.getUnderlyingType(); TypeMirror javaExtendsBound; if (javaWildcardType.getExtendsBound() != null) { // If the wildcard type has an extends bound, use it. javaExtendsBound = javaWildcardType.getExtendsBound(); } else if (wildcard.getTypeVariable() != null) { // Otherwise use the upper bound of the type variable associated with this wildcard. javaExtendsBound = wildcard.getTypeVariable().getUpperBound(); } else { // Otherwise use the upper bound of the java wildcard. javaExtendsBound = TypesUtils.wildUpperBound( javaWildcardType, wildcard.atypeFactory.processingEnv); } if (wildcard.isUninferredTypeArgument()) { rawTypeWildcards.put(wildcard.getTypeVariable(), wildcard.getUnderlyingType()); } final AnnotatedTypeMirror extendsBound = AnnotatedTypeMirror.createType(javaExtendsBound, typeFactory, false); wildcard.setExtendsBound(extendsBound); this.wildcards.put(wildcard.getUnderlyingType(), wildcard); final BoundPathNode extendsNode = addPathNode(new ExtendsNode()); visit(extendsBound); removePathNode(extendsNode); } private void initializeTypeArgs(final AnnotatedDeclaredType declaredType) { final DeclaredType actualType = (DeclaredType) declaredType.actualType; if (actualType.getTypeArguments().isEmpty() && !declaredType.wasRaw()) { return; } final TypeElement typeElement = (TypeElement) declaredType.atypeFactory.types.asElement(actualType); List typeArgs; if (declaredType.typeArgs == null) { typeArgs = new ArrayList<>(); for (int i = 0; i < typeElement.getTypeParameters().size(); i++) { TypeMirror javaTypeArg = getJavaType(declaredType, typeElement.getTypeParameters(), i); final AnnotatedTypeMirror atmArg = AnnotatedTypeMirror.createType( javaTypeArg, declaredType.atypeFactory, false); typeArgs.add(atmArg); if (atmArg.getKind() == TypeKind.WILDCARD && declaredType.wasRaw()) { ((AnnotatedWildcardType) atmArg).setUninferredTypeArgument(); } } } else { typeArgs = declaredType.typeArgs; } List typeArgReplacements = new ArrayList<>(typeArgs.size()); for (int i = 0; i < typeArgs.size(); i++) { AnnotatedTypeMirror typeArg = typeArgs.get(i); BoundPathNode node = addPathNode(new TypeArgNode(i)); if (typeArg.getKind() == TypeKind.WILDCARD) { ((AnnotatedWildcardType) typeArg) .setTypeVariable(typeElement.getTypeParameters().get(i)); } typeArgReplacements.add(getOrVisit(typeArg)); removePathNode(node); } declaredType.setTypeArguments(typeArgReplacements); } /** * Store the wildcards created as type arguments to raw types. * *

{@code class Foo {}} The upper bound of the wildcard in {@code Foo} * is {@code Foo}. The type argument of {@code Foo} is initialized to {@code ? extends Foo}. * The type argument of {@code Foo} in {@code ? extends Foo} needs to be initialized to the * same type argument as the first {@code Foo} so that * BoundsInitializer.InitializerVisitor#getOrVisit will return the cached * AnnotatedWildcardType. */ private final Map rawTypeWildcards = new HashMap<>(); /** * Returns the underlying java type of the {@code i}-th type argument of {@code type}. If * {@code type} is raw, then a new wildcard is created or returned from {@code * rawTypeWildcards}. * * @param type declared type * @param parameters elements of the type parameters * @param i index of the type parameter * @return the underlying java type of the {@code i}-th type argument of {@code type} */ private TypeMirror getJavaType( AnnotatedDeclaredType type, List parameters, int i) { if (type.wasRaw()) { TypeVariable typeVariable = (TypeVariable) parameters.get(i).asType(); if (rawTypeWildcards.containsKey(typeVariable)) { return rawTypeWildcards.get(typeVariable); } WildcardType wildcard = getUpperBoundAsWildcard(typeVariable, type.atypeFactory); rawTypeWildcards.put(typeVariable, wildcard); return wildcard; } else { return type.getUnderlyingType().getTypeArguments().get(i); } } public static Void invalidType(final AnnotatedTypeMirror atm) { ErrorReporter.errorAbort( "Unexpected type in Wildcard bound:\n" + "kind=" + atm.getKind() + "\n" + "atm=" + atm); return null; // dead code } public BoundPathNode addPathNode(final BoundPathNode node) { currentStructure.currentPath.add(node); return node; } public BoundPathNode removePathNode(final BoundPathNode node) { if (currentStructure.currentPath.getLast() != node) { ErrorReporter.errorAbort( "Cannot remove node: " + node + " It is not the last item.\n" + "node=" + node + "\n" + "currentPath=" + currentStructure.currentPath); } // else currentStructure.currentPath.removeLast(); return node; } public void pushNewTypeVarStruct(final AnnotatedTypeVariable typeVar) { if (typeVarToStructure.containsKey(typeVar.getUnderlyingType())) { ErrorReporter.errorAbort( "Starting a TypeVarStructure that already exists!\n" + "typeVar=" + typeVar + "\n" + "currentStructure=" + currentStructure); } final TypeVariableStructure typeVarStruct = new TypeVariableStructure(currentStructure, typeVar); typeVarToStructure.put(typeVar.getUnderlyingType(), typeVarStruct); this.currentStructure = typeVarStruct; } public boolean haveSeenTypeVar(final AnnotatedTypeVariable typeVariable) { return typeVarToStructure.containsKey(typeVariable.getUnderlyingType()); } public void popCurrentTypeVarStruct(final AnnotatedTypeVariable typeVar) { if (!(this.currentStructure instanceof TypeVariableStructure)) { ErrorReporter.errorAbort( "Trying to pop WildcardStructure!\n" + "typeVar=" + typeVar + "\n" + "currentStucture=" + currentStructure + "\n"); } // else final TypeVariableStructure toPop = (TypeVariableStructure) this.currentStructure; if (toPop.typeVar != typeVar) { this.currentStructure = toPop.parent; } } public ReferenceMap createReferenceMap(final BoundStructure boundStruct) { final ReferenceMap refMap = new ReferenceMap(); for (Entry entry : boundStruct.pathToTypeVar.entrySet()) { TypeVariableStructure targetStructure = typeVarToStructure.get(entry.getValue()); AnnotatedTypeVariable template = targetStructure.annotatedTypeVar; refMap.put(entry.getKey(), template.deepCopy().asUse()); addImmediateTypeVarPaths(refMap, entry.getKey(), targetStructure); } return refMap; } public void addImmediateTypeVarPaths( ReferenceMap refMap, BoundPath basePath, TypeVariableStructure targetStruct) { // explain typevar sleds for (BoundPath path : targetStruct.immediateBoundTypeVars) { final BoundPath newPath = basePath.copy(); newPath.add(path.getFirst()); TypeVariable immTypeVar = targetStruct.pathToTypeVar.get(path); TypeVariableStructure immTvStructure = typeVarToStructure.get(immTypeVar); AnnotatedTypeVariable template = immTvStructure.annotatedTypeVar; refMap.put(newPath, template.deepCopy()); } } /** * A mapping of paths to the type that should be placed at the end of that path for all atvs * that of sourceType */ @SuppressWarnings("serial") private static class ReferenceMap extends LinkedHashMap { // TODO: EXPLAINED LINK DUE TO TYPEVAR SLED } public void resolveTypeVarReferences(final AnnotatedTypeMirror boundedType) { final List annotatedTypeVars = new ArrayList<>(); final Map typeVarToRefMap = new HashMap<>(); for (final TypeVariableStructure typeVarStruct : typeVarToStructure.values()) { ReferenceMap refMap = createReferenceMap(typeVarStruct); typeVarToRefMap.put(typeVarStruct.typeVar, refMap); annotatedTypeVars.addAll(refMap.values()); } for (final AnnotatedTypeVariable atv : annotatedTypeVars) { fixTypeVarPathReference(atv, typeVarToRefMap); } if (topLevelStructure instanceof WildcardStructure) { fixWildcardPathReference((AnnotatedWildcardType) boundedType, typeVarToRefMap); } else { final AnnotatedTypeVariable typeVar = (AnnotatedTypeVariable) boundedType; fixTypeVarPathReference(typeVar, typeVarToRefMap); } } public void fixWildcardPathReference( final AnnotatedWildcardType wildcard, final Map typeVarToRefMap) { final ReferenceMap topLevelMap = createReferenceMap(topLevelStructure); for (AnnotatedTypeVariable typeVar : topLevelMap.values()) { fixTypeVarPathReference(typeVar, typeVarToRefMap); } for (Entry pathToRef : topLevelMap.entrySet()) { final AnnotatedTypeMirror parent = traverseToParent(wildcard, pathToRef.getKey()); final BoundPathNode terminus = pathToRef.getKey().getLast(); terminus.setType(parent, pathToRef.getValue()); } } public void fixTypeVarPathReference( final AnnotatedTypeVariable type, Map typeVarToRefMap) { final ReferenceMap refMap = typeVarToRefMap.get(type.getUnderlyingType()); for (final Entry pathToRef : refMap.entrySet()) { final BoundPath path = pathToRef.getKey(); final AnnotatedTypeVariable replacement = pathToRef.getValue().asUse(); AnnotatedTypeMirror parent = traverseToParent(type, path); BoundPathNode terminus = path.getLast(); terminus.replaceType(parent, replacement); } } public AnnotatedTypeMirror traverseToParent( final AnnotatedTypeMirror start, final BoundPath path) { AnnotatedTypeMirror current = start; for (int i = 0; i < path.size() - 1; i++) { // Note that the last element in path isn't inspected. current = path.get(i).next(current); } return current; } } private static AnnotatedTypeMirror createAndSetUpperBound(final AnnotatedTypeVariable typeVar) { final AnnotatedTypeMirror upperBound = AnnotatedTypeMirror.createType( typeVar.getUnderlyingType().getUpperBound(), typeVar.atypeFactory, false); typeVar.setUpperBound(upperBound); return upperBound; } private static AnnotatedTypeMirror createAndSetLowerBound(final AnnotatedTypeVariable typeVar) { TypeMirror lb = typeVar.getUnderlyingType().getLowerBound(); if (lb == null) { // Use bottom type to ensure there is a lower bound. Context context = ((JavacProcessingEnvironment) typeVar.atypeFactory.processingEnv).getContext(); Symtab syms = Symtab.instance(context); lb = syms.botType; } final AnnotatedTypeMirror lowerBound = AnnotatedTypeMirror.createType(lb, typeVar.atypeFactory, false); typeVar.setLowerBound(lowerBound); return lowerBound; } private static boolean isImmediateBoundPath(final BoundPath path) { if (path.size() == 1) { switch (path.getFirst().kind) { case UpperBound: case LowerBound: return true; default: // do nothing } } return false; } private abstract static class BoundStructure { /** * A mapping of all BoundPaths to TypeVariables for all type variables contained within * annotatedTypeVar */ public final Map pathToTypeVar = new LinkedHashMap<>(); public final BoundPath currentPath = new BoundPath(); public BoundStructure() {} public void addTypeVar(final TypeVariable typeVariable) { pathToTypeVar.put(this.currentPath.copy(), typeVariable); } } private static class WildcardStructure extends BoundStructure {} private static class TypeVariableStructure extends BoundStructure { /** The type variable whose structure is being described */ public final TypeVariable typeVar; /** * The first annotated type variable that was encountered and traversed in order to describe * typeVar. It is expanded during visitation and it is later used as a template for other * uses of typeVar */ public final AnnotatedTypeVariable annotatedTypeVar; /** The boundStructure that was active before this one */ private final BoundStructure parent; /** * If this type variable is upper or lower bounded by another type variable (not a declared * type or intersection) then this variable will contain the path to that type variable * //TODO: Add link to explanation * *

e.g. {@code T extends E} ⇒ The structure for T will have an * immediateBoundTypeVars = List(UpperBound) The BoundPaths here must exist in pathToTypeVar */ public Set immediateBoundTypeVars = new LinkedHashSet<>(); public TypeVariableStructure( final BoundStructure parent, final AnnotatedTypeVariable annotatedTypeVar) { this.parent = parent; this.typeVar = annotatedTypeVar.getUnderlyingType(); this.annotatedTypeVar = annotatedTypeVar; } @Override public void addTypeVar(TypeVariable typeVariable) { final BoundPath copy = currentPath.copy(); pathToTypeVar.put(copy, typeVariable); if (isImmediateBoundPath(copy)) { immediateBoundTypeVars.add(copy); } } } /** A linked list of BoundPathNodes whose equals method is a referential equality check */ @SuppressWarnings({"serial", "JdkObsolete"}) private static class BoundPath extends LinkedList { @Override public boolean equals(final Object obj) { return this == obj; } @Override public String toString() { return PluginUtil.join(",", this); } public BoundPath copy() { final BoundPath copy = new BoundPath(); for (final BoundPathNode node : this) { copy.add(node.copy()); } return copy; } } // BoundPathNode's are a step in a "type path" that are used to private abstract static class BoundPathNode { enum Kind { Extends, Super, UpperBound, LowerBound, ArrayComponent, Intersection, Union, TypeArg } public Kind kind; public TypeKind typeKind; BoundPathNode() {} BoundPathNode(final BoundPathNode template) { this.kind = template.kind; this.typeKind = template.typeKind; } @Override public String toString() { return kind.toString(); } public AnnotatedTypeMirror next(final AnnotatedTypeMirror parent) { abortIfParentNotKind(typeKind, null, parent); return getType(parent); } public void replaceType( final AnnotatedTypeMirror parent, final AnnotatedTypeVariable replacement) { abortIfParentNotKind(typeKind, replacement, parent); setType(parent, replacement); } public abstract void setType( final AnnotatedTypeMirror parent, final AnnotatedTypeVariable replacement); public abstract AnnotatedTypeMirror getType(final AnnotatedTypeMirror parent); public abstract BoundPathNode copy(); } private static class ExtendsNode extends BoundPathNode { ExtendsNode() { kind = Kind.Extends; typeKind = TypeKind.WILDCARD; } ExtendsNode(ExtendsNode template) { super(template); } @Override public void setType(AnnotatedTypeMirror parent, AnnotatedTypeVariable replacement) { ((AnnotatedWildcardType) parent).setExtendsBound(replacement); } @Override public AnnotatedTypeMirror getType(final AnnotatedTypeMirror parent) { return ((AnnotatedWildcardType) parent).getExtendsBound(); } @Override public BoundPathNode copy() { return new ExtendsNode(this); } } private static class SuperNode extends BoundPathNode { SuperNode() { kind = Kind.Super; typeKind = TypeKind.WILDCARD; } SuperNode(SuperNode template) { super(template); } @Override public void setType(AnnotatedTypeMirror parent, AnnotatedTypeVariable replacement) { ((AnnotatedWildcardType) parent).setSuperBound(replacement); } @Override public AnnotatedTypeMirror getType(final AnnotatedTypeMirror parent) { return ((AnnotatedWildcardType) parent).getSuperBound(); } @Override public BoundPathNode copy() { return new SuperNode(this); } } private static class UpperBoundNode extends BoundPathNode { UpperBoundNode() { kind = Kind.UpperBound; typeKind = TypeKind.TYPEVAR; } UpperBoundNode(UpperBoundNode template) { super(template); } @Override public void setType(AnnotatedTypeMirror parent, AnnotatedTypeVariable replacement) { ((AnnotatedTypeVariable) parent).setUpperBound(replacement); } @Override public AnnotatedTypeMirror getType(final AnnotatedTypeMirror parent) { final AnnotatedTypeVariable parentAtv = (AnnotatedTypeVariable) parent; if (parentAtv.getUpperBoundField() != null) { return parentAtv.getUpperBoundField(); } return createAndSetUpperBound((AnnotatedTypeVariable) parent); } @Override public BoundPathNode copy() { return new UpperBoundNode(this); } } private static class LowerBoundNode extends BoundPathNode { LowerBoundNode() { kind = Kind.LowerBound; typeKind = TypeKind.TYPEVAR; } LowerBoundNode(LowerBoundNode template) { super(template); } @Override public void setType(AnnotatedTypeMirror parent, AnnotatedTypeVariable replacement) { ((AnnotatedTypeVariable) parent).setLowerBound(replacement); } @Override public AnnotatedTypeMirror getType(final AnnotatedTypeMirror parent) { final AnnotatedTypeVariable parentAtv = (AnnotatedTypeVariable) parent; if (parentAtv.getLowerBoundField() != null) { return parentAtv.getLowerBoundField(); } // else //TODO: I think this should never happen at this point, throw exception return createAndSetLowerBound((AnnotatedTypeVariable) parent); } @Override public BoundPathNode copy() { return new LowerBoundNode(this); } } private static class ArrayComponentNode extends BoundPathNode { ArrayComponentNode() { kind = Kind.ArrayComponent; typeKind = TypeKind.ARRAY; } ArrayComponentNode(ArrayComponentNode template) { super(template); } @Override public void setType(AnnotatedTypeMirror parent, AnnotatedTypeVariable replacement) { ((AnnotatedArrayType) parent).setComponentType(replacement); } @Override public AnnotatedTypeMirror getType(final AnnotatedTypeMirror parent) { return ((AnnotatedArrayType) parent).getComponentType(); } @Override public BoundPathNode copy() { return new ArrayComponentNode(this); } } private static class IntersectionNode extends BoundPathNode { public final int superIndex; IntersectionNode(int superIndex) { this.superIndex = superIndex; kind = Kind.Intersection; typeKind = TypeKind.INTERSECTION; } IntersectionNode(IntersectionNode template) { super(template); superIndex = template.superIndex; } @Override public String toString() { return super.toString() + "( superIndex=" + superIndex + " )"; } @Override public void setType(AnnotatedTypeMirror parent, AnnotatedTypeVariable replacement) { ErrorReporter.errorAbort( "Type variables cannot be intersection bounds!\n" + "parent=" + parent + "\n" + "replacement=" + replacement); } @Override public AnnotatedTypeMirror getType(final AnnotatedTypeMirror parent) { final AnnotatedIntersectionType isect = (AnnotatedIntersectionType) parent; if (parent.directSuperTypes().size() <= superIndex) { ErrorReporter.errorAbort( "Invalid superIndex( " + superIndex + " ):\n" + "parent=" + parent); } return isect.directSuperTypes().get(superIndex); } @Override public BoundPathNode copy() { return new IntersectionNode(this); } } private static class UnionNode extends BoundPathNode { public final int altIndex; UnionNode(int altIndex) { this.altIndex = altIndex; kind = Kind.Union; typeKind = TypeKind.UNION; } UnionNode(UnionNode template) { super(template); altIndex = template.altIndex; } @Override public String toString() { return super.toString() + "( altIndex=" + altIndex + " )"; } @Override public void setType(AnnotatedTypeMirror parent, AnnotatedTypeVariable replacement) { ErrorReporter.errorAbort( "Union types cannot be intersection bounds!\n" + "parent=" + parent + "\n" + "replacement=" + replacement); } @Override public AnnotatedTypeMirror getType(final AnnotatedTypeMirror parent) { final AnnotatedUnionType isect = (AnnotatedUnionType) parent; if (parent.directSuperTypes().size() <= altIndex) { ErrorReporter.errorAbort( "Invalid altIndex( " + altIndex + " ):\n" + "parent=" + parent); } return isect.directSuperTypes().get(altIndex); } @Override public BoundPathNode copy() { return new UnionNode(this); } } private static class TypeArgNode extends BoundPathNode { public final int argIndex; TypeArgNode(int argIndex) { this.argIndex = argIndex; kind = Kind.TypeArg; typeKind = TypeKind.DECLARED; } TypeArgNode(TypeArgNode template) { super(template); argIndex = template.argIndex; } @Override public String toString() { return super.toString() + "( argIndex=" + argIndex + " )"; } @Override public void setType(AnnotatedTypeMirror parent, AnnotatedTypeVariable replacement) { abortIfParentNotKind(TypeKind.DECLARED, replacement, parent); final AnnotatedDeclaredType parentAdt = (AnnotatedDeclaredType) parent; List typeArgs = new ArrayList<>(parentAdt.getTypeArguments()); if (argIndex >= typeArgs.size()) { ErrorReporter.errorAbort( "Invalid type arg index!\n" + "parent=" + parent + "\n" + "replacement=" + replacement + "\n" + "argIndex=" + argIndex); } typeArgs.add(argIndex, replacement); typeArgs.remove(argIndex + 1); parentAdt.setTypeArguments(typeArgs); } @Override public AnnotatedTypeMirror getType(final AnnotatedTypeMirror parent) { final AnnotatedDeclaredType parentAdt = (AnnotatedDeclaredType) parent; List typeArgs = parentAdt.getTypeArguments(); if (argIndex >= typeArgs.size()) { ErrorReporter.errorAbort( "Invalid type arg index!\n" + "parent=" + parent + "\n" + "argIndex=" + argIndex); } return typeArgs.get(argIndex); } @Override public BoundPathNode copy() { return new TypeArgNode(this); } } public static void abortIfParentNotKind( final TypeKind typeKind, final AnnotatedTypeVariable type, final AnnotatedTypeMirror parent) { if (parent.getKind().equals(typeKind)) { return; } final StringBuilder builder = new StringBuilder(); builder.append("Unexpected parent kind:\n"); builder.append("parent="); builder.append(parent); builder.append("\n"); builder.append("replacement="); builder.append(type); builder.append("\n"); builder.append("expected="); builder.append(typeKind); builder.append("\n"); ErrorReporter.errorAbort(builder.toString()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy