blob: 35ae6bf561bff31e0182b08e813de0bbfe8a27d2 [file] [log] [blame]
package org.checkerframework.framework.type.typeannotator;
import com.sun.tools.javac.code.Type.WildcardType;
import java.util.ArrayDeque;
import java.util.List;
import java.util.Set;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeKind;
import org.checkerframework.checker.interning.qual.FindDistinct;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedWildcardType;
import org.checkerframework.javacutil.BugInCF;
import org.checkerframework.javacutil.TypesUtils;
import org.plumelib.util.StringsPlume;
/**
* {@link PropagationTypeAnnotator} adds qualifiers to types where the qualifier to add should be
* transferred from one or more other types.
*
* <p>At the moment, the only function PropagationTypeAnnotator provides, is the propagation of
* generic type parameter annotations to unannotated wildcards with missing bounds annotations.
*
* @see
* #visitWildcard(org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedWildcardType,
* Object)
* <p>PropagationTypeAnnotator traverses trees deeply by default.
*/
public class PropagationTypeAnnotator extends TypeAnnotator {
// The PropagationTypeAnnotator is called recursively via
// TypeAnnotatorUtil.eraseBoundsThenAnnotate.
// This flag prevents infinite recursion.
private boolean pause = false;
private ArrayDeque<AnnotatedDeclaredType> parents = new ArrayDeque<>();
public PropagationTypeAnnotator(AnnotatedTypeFactory typeFactory) {
super(typeFactory);
}
@Override
public void reset() {
if (!pause) {
// when the PropagationTypeAnnotator is called recursively we don't
// want the visit method to reset the list of visited types
super.reset();
}
}
/*
* When pause == true, the PropagationTypeAnnotator caused a recursive call
* and there is no need to execute the PropagationTypeAnnotator
*/
@Override
protected Void scan(AnnotatedTypeMirror type, Void aVoid) {
if (pause) {
return null;
}
return super.scan(type, aVoid);
}
/**
* Sometimes the underlying type parameters of AnnotatedWildcardTypes are not available on the
* wildcards themselves. Instead, record enclosing class to find the type parameter to use as a
* backup in visitWildcards.
*
* @param declaredType type to record
*/
@Override
public Void visitDeclared(AnnotatedDeclaredType declaredType, Void aVoid) {
if (pause) {
return null;
}
if (declaredType.wasRaw()) {
// Copy annotations from the declaration to the wildcards.
AnnotatedDeclaredType declaration =
(AnnotatedDeclaredType)
typeFactory.fromElement(declaredType.getUnderlyingType().asElement());
List<AnnotatedTypeMirror> typeArgs = declaredType.getTypeArguments();
for (int i = 0; i < typeArgs.size(); i++) {
if (typeArgs.get(i).getKind() != TypeKind.WILDCARD
|| !((AnnotatedWildcardType) typeArgs.get(i)).isUninferredTypeArgument()) {
// Sometimes the framework infers a more precise type argument, so just use it.
continue;
}
AnnotatedTypeVariable typeParam =
(AnnotatedTypeVariable) declaration.getTypeArguments().get(i);
AnnotatedWildcardType wct = (AnnotatedWildcardType) typeArgs.get(i);
wct.getExtendsBound().replaceAnnotations(typeParam.getUpperBound().getAnnotations());
wct.getSuperBound().replaceAnnotations(typeParam.getLowerBound().getAnnotations());
wct.replaceAnnotations(typeParam.getAnnotations());
}
}
parents.addFirst(declaredType);
super.visitDeclared(declaredType, aVoid);
parents.removeFirst();
return null;
}
/**
* Rather than defaulting the missing bounds of a wildcard, find the bound annotations on the type
* parameter it replaced. Place those annotations on the wildcard.
*
* @param wildcardAtm type to annotate
*/
@Override
public Void visitWildcard(AnnotatedWildcardType wildcardAtm, Void aVoid) {
if (visitedNodes.containsKey(wildcardAtm) || pause) {
return null;
}
visitedNodes.put(wildcardAtm, null);
final WildcardType wildcard = (WildcardType) wildcardAtm.getUnderlyingType();
Element typeParamElement = TypesUtils.wildcardToTypeParam(wildcard);
if (typeParamElement == null) {
typeParamElement =
parents.isEmpty()
? null
: getTypeParamFromEnclosingClass(wildcardAtm, parents.peekFirst());
}
if (typeParamElement != null) {
pause = true;
AnnotatedTypeVariable typeParam =
(AnnotatedTypeVariable) typeFactory.getAnnotatedType(typeParamElement);
pause = false;
final Set<? extends AnnotationMirror> tops =
typeFactory.getQualifierHierarchy().getTopAnnotations();
if (wildcard.isUnbound()) {
propagateExtendsBound(wildcardAtm, typeParam, tops);
propagateSuperBound(wildcardAtm, typeParam, tops);
} else if (wildcard.isExtendsBound()) {
propagateSuperBound(wildcardAtm, typeParam, tops);
} else { // is super bound
propagateExtendsBound(wildcardAtm, typeParam, tops);
}
}
scan(wildcardAtm.getExtendsBound(), null);
scan(wildcardAtm.getSuperBound(), null);
return null;
}
private void propagateSuperBound(
AnnotatedWildcardType wildcard,
AnnotatedTypeVariable typeParam,
Set<? extends AnnotationMirror> tops) {
applyAnnosFromBound(wildcard.getSuperBound(), typeParam.getLowerBound(), tops);
}
private void propagateExtendsBound(
AnnotatedWildcardType wildcard,
AnnotatedTypeVariable typeParam,
Set<? extends AnnotationMirror> tops) {
applyAnnosFromBound(wildcard.getExtendsBound(), typeParam.getUpperBound(), tops);
}
/**
* Take the primary annotations from typeParamBound and place them as primary annotations on
* wildcard bound.
*/
private void applyAnnosFromBound(
final AnnotatedTypeMirror wildcardBound,
final AnnotatedTypeMirror typeParamBound,
final Set<? extends AnnotationMirror> tops) {
// Type variables do not need primary annotations.
// The type variable will have annotations placed on its
// bounds via its declaration or defaulting rules
if (wildcardBound.getKind() == TypeKind.TYPEVAR
|| typeParamBound.getKind() == TypeKind.TYPEVAR) {
return;
}
for (final AnnotationMirror top : tops) {
if (wildcardBound.getAnnotationInHierarchy(top) == null) {
final AnnotationMirror typeParamAnno = typeParamBound.getAnnotationInHierarchy(top);
if (typeParamAnno == null) {
throw new BugInCF(
StringsPlume.joinLines(
"Missing annotation on type parameter",
"top=" + top,
"wildcardBound=" + wildcardBound,
"typeParamBound=" + typeParamBound));
} // else
wildcardBound.addAnnotation(typeParamAnno);
}
}
}
/**
* Search parent's type arguments for wildcard. Using the index of wildcard, find the
* corresponding type parameter element and return it. Returns null if the wildcard is the result
* of substitution and therefore not in the list of type arguments.
*
* @param wildcard the wildcard type whose corresponding type argument to determine
* @param parent the type that may have a type argument corresponding to {@code wildcard}
* @return the type argument in {@code parent} that corresponds to {@code wildcard}
*/
private Element getTypeParamFromEnclosingClass(
final @FindDistinct AnnotatedWildcardType wildcard, final AnnotatedDeclaredType parent) {
Integer wildcardIndex = null;
int currentIndex = 0;
for (AnnotatedTypeMirror typeArg : parent.getTypeArguments()) {
// the only cases in which the wildcard is not one of the type arguments are cases in
// which they should have been replaced by capture
if (typeArg == wildcard) {
wildcardIndex = currentIndex;
break;
}
currentIndex += 1;
}
if (wildcardIndex != null) {
final TypeElement typeElement =
(TypeElement)
typeFactory.getProcessingEnv().getTypeUtils().asElement(parent.getUnderlyingType());
return typeElement.getTypeParameters().get(wildcardIndex);
}
return null;
}
}