org.checkerframework.framework.type.TypeVariableSubstitutor Maven / Gradle / Ivy
Show all versions of checker Show documentation
package org.checkerframework.framework.type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
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 org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable;
import org.checkerframework.javacutil.TypesUtils;
/** TypeVariableSusbtitutor replaces type variables from a declaration with arguments to its use. */
public class TypeVariableSubstitutor {
/**
* Given a mapping from type variable to its type argument, replace each instance of a type
* variable with a copy of type argument.
*
* @see #substituteTypeVariable(AnnotatedTypeMirror,
* org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable)
* @param typeVarToTypeArgument a mapping from type variable to its type argument
* @param type the type to substitute
* @return a copy of type with its type variables substituted
*/
public AnnotatedTypeMirror substitute(
final Map typeVarToTypeArgument,
final AnnotatedTypeMirror type) {
return new Visitor(typeVarToTypeArgument, true).visit(type);
}
/**
* Given a mapping from type variable to its type argument, replace each instance of a type
* variable with the given type argument.
*
* @see #substituteTypeVariable(AnnotatedTypeMirror,
* org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable)
* @param typeVarToTypeArgument a mapping from type variable to its type argument
* @param type the type to substitute
* @return a copy of type with its type variables substituted
*/
public AnnotatedTypeMirror substituteWithoutCopyingTypeArguments(
Map typeVarToTypeArgument, AnnotatedTypeMirror type) {
return new Visitor(typeVarToTypeArgument, false).visit(type);
}
/**
* 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 deep copy of argument with the appropriate annotations applied
*/
protected AnnotatedTypeMirror substituteTypeVariable(
final AnnotatedTypeMirror argument, final AnnotatedTypeVariable use) {
final AnnotatedTypeMirror substitute = argument.deepCopy(true);
substitute.addAnnotations(argument.getAnnotationsField());
if (!use.getAnnotationsField().isEmpty()) {
substitute.replaceAnnotations(use.getAnnotations());
}
return substitute;
}
/**
* Visitor that makes the substitution. This is an inner class so that its methods cannot be
* called by clients of {@link TypeVariableSubstitutor}.
*/
protected class Visitor extends AnnotatedTypeCopier {
/**
* A mapping from {@link TypeParameterElement} to the {@link AnnotatedTypeMirror} that should
* replace its uses.
*/
private final Map elementToArgMap;
/**
* A list of type variables that should be replaced by the type mirror at the same index in
* {@code typeMirrors}
*/
private final List typeVars;
/**
* A list of TypeMirrors that should replace the type variable at the same index in {@code
* typeVars}
*/
private final List typeMirrors;
/** Whether or not a copy of type argument should be substituted. */
private final boolean copyArgument;
/**
* Creates the Visitor.
*
* @param typeParamToArg mapping from TypeVariable to the AnnotatedTypeMirror that will replace
* it
* @param copyArgument whether or not a copy of type argument should be substituted
*/
public Visitor(
final Map typeParamToArg, boolean copyArgument) {
int size = typeParamToArg.size();
elementToArgMap = new HashMap<>(size);
typeVars = new ArrayList<>(size);
typeMirrors = new ArrayList<>(size);
for (Map.Entry paramToArg : typeParamToArg.entrySet()) {
elementToArgMap.put(
(TypeParameterElement) paramToArg.getKey().asElement(), paramToArg.getValue());
typeVars.add(paramToArg.getKey());
typeMirrors.add(paramToArg.getValue().getUnderlyingType());
}
this.copyArgument = copyArgument;
}
@Override
protected T makeCopy(T original) {
if (original.getKind() == TypeKind.TYPEVAR) {
return super.makeCopy(original);
}
TypeMirror s =
TypesUtils.substitute(
original.getUnderlyingType(),
typeVars,
typeMirrors,
original.atypeFactory.processingEnv);
@SuppressWarnings("unchecked")
T copy =
(T) AnnotatedTypeMirror.createType(s, original.atypeFactory, original.isDeclaration());
maybeCopyPrimaryAnnotations(original, copy);
return copy;
}
@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);
if (copyArgument) {
return substituteTypeVariable(argument, original);
} else {
return argument;
}
}
}
return super.visitTypeVariable(original, originalToCopy);
}
}
}