blob: ffd9006e1c42a5532c6c1cc97348b25e938f59cc [file] [log] [blame]
package org.checkerframework.framework.util.element;
import com.sun.tools.javac.code.Attribute.TypeCompound;
import com.sun.tools.javac.code.TargetType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.type.TypeKind;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedIntersectionType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable;
import org.checkerframework.framework.util.element.ElementAnnotationUtil.UnexpectedAnnotationLocationException;
import org.checkerframework.javacutil.BugInCF;
/**
* Applies Element annotations to a single AnnotatedTypeVariable representing a type parameter.
* Note, the index of IndexedElementAnnotationApplier refers to the type parameter's index in the
* list that encloses it.
*/
abstract class TypeParamElementAnnotationApplier extends IndexedElementAnnotationApplier {
/**
* Returns true if element is a TYPE_PARAMETER.
*
* @param typeMirror ignored
* @param element the element that might be a TYPE_PARAMETER
* @return true if element is a TYPE_PARAMETER
*/
public static boolean accepts(AnnotatedTypeMirror typeMirror, Element element) {
return element.getKind() == ElementKind.TYPE_PARAMETER;
}
protected final AnnotatedTypeVariable typeParam;
protected final AnnotatedTypeFactory typeFactory;
/**
* Returns target type that represents the location of the lower bound of element.
*
* @return target type that represents the location of the lower bound of element
*/
protected abstract TargetType lowerBoundTarget();
/**
* Returns target type that represents the location of the upper bound of element.
*
* @return target type that represents the location of the upper bound of element
*/
protected abstract TargetType upperBoundTarget();
TypeParamElementAnnotationApplier(
final AnnotatedTypeVariable type,
final Element element,
final AnnotatedTypeFactory typeFactory) {
super(type, element);
this.typeParam = type;
this.typeFactory = typeFactory;
}
/**
* Returns the lower bound and upper bound targets.
*
* @return the lower bound and upper bound targets
*/
@Override
protected TargetType[] annotatedTargets() {
return new TargetType[] {lowerBoundTarget(), upperBoundTarget()};
}
/**
* Returns the parameter_index of anno's TypeAnnotationPosition which will actually point to the
* type parameter's index in its enclosing type parameter list.
*
* @return the parameter_index of anno's TypeAnnotationPosition which will actually point to the
* type parameter's index in its enclosing type parameter list
*/
@Override
public int getTypeCompoundIndex(final TypeCompound anno) {
return anno.getPosition().parameter_index;
}
/**
* @param targeted the list of annotations that were on the lower/upper bounds of the type
* parameter
* <p>Note: When handling type parameters we NEVER add primary annotations to the type
* parameter. Primary annotations are reserved for the use of a type parameter (e.g. @Nullable
* T t; )
* <p>If an annotation is present on the type parameter itself, it represents the lower-bound
* annotation of that type parameter. Any annotation on the extends bound of a type parameter
* is placed on that bound.
*/
@Override
protected void handleTargeted(final List<TypeCompound> targeted)
throws UnexpectedAnnotationLocationException {
final int paramIndex = getElementIndex();
final List<TypeCompound> upperBoundAnnos = new ArrayList<>();
final List<TypeCompound> lowerBoundAnnos = new ArrayList<>();
for (final TypeCompound anno : targeted) {
final AnnotationMirror aliasedAnno = typeFactory.canonicalAnnotation(anno);
final AnnotationMirror canonicalAnno = (aliasedAnno != null) ? aliasedAnno : anno;
if (anno.position.parameter_index != paramIndex
|| !typeFactory.isSupportedQualifier(canonicalAnno)) {
continue;
}
if (ElementAnnotationUtil.isOnComponentType(anno)) {
applyComponentAnnotation(anno);
} else if (anno.position.type == upperBoundTarget()) {
upperBoundAnnos.add(anno);
} else {
lowerBoundAnnos.add(anno);
}
}
applyLowerBounds(lowerBoundAnnos);
applyUpperBounds(upperBoundAnnos);
}
/**
* Applies a list of annotations to the upperBound of the type parameter. If the type of the upper
* bound is an intersection we must first find the correct location for each annotation.
*/
private void applyUpperBounds(final List<TypeCompound> upperBounds) {
if (!upperBounds.isEmpty()) {
final AnnotatedTypeMirror upperBoundType = typeParam.getUpperBound();
if (upperBoundType.getKind() == TypeKind.INTERSECTION) {
final List<AnnotatedTypeMirror> bounds =
((AnnotatedIntersectionType) upperBoundType).getBounds();
final int boundIndexOffset = ElementAnnotationUtil.getBoundIndexOffset(bounds);
for (final TypeCompound anno : upperBounds) {
final int boundIndex = anno.position.bound_index + boundIndexOffset;
if (boundIndex < 0 || boundIndex > bounds.size()) {
throw new BugInCF(
"Invalid bound index on element annotation ( "
+ anno
+ " ) "
+ "for type ( "
+ typeParam
+ " ) with "
+ "upper bound ( "
+ typeParam.getUpperBound()
+ " ) "
+ "and boundIndex( "
+ boundIndex
+ " ) ");
}
bounds.get(boundIndex).replaceAnnotation(anno); // TODO: WHY NOT ADD?
}
((AnnotatedIntersectionType) upperBoundType).copyIntersectionBoundAnnotations();
} else {
upperBoundType.addAnnotations(upperBounds);
}
}
}
/**
* In the event of multiple annotations on an AnnotatedNullType lower bound we want to preserve
* the multiple annotations so that a type.invalid error is issued later.
*
* @param annos the annotations to add to the lower bound
*/
private void applyLowerBounds(final List<? extends AnnotationMirror> annos) {
if (!annos.isEmpty()) {
final AnnotatedTypeMirror lowerBound = typeParam.getLowerBound();
for (AnnotationMirror anno : annos) {
lowerBound.addAnnotation(anno);
}
}
}
private void addAnnotationToMap(
final AnnotatedTypeMirror type,
final TypeCompound anno,
final Map<AnnotatedTypeMirror, List<TypeCompound>> typeToAnnos) {
List<TypeCompound> annoList = typeToAnnos.computeIfAbsent(type, __ -> new ArrayList<>());
annoList.add(anno);
}
private void applyComponentAnnotation(final TypeCompound anno)
throws UnexpectedAnnotationLocationException {
final AnnotatedTypeMirror upperBoundType = typeParam.getUpperBound();
Map<AnnotatedTypeMirror, List<TypeCompound>> typeToAnnotations = new HashMap<>();
if (anno.position.type == upperBoundTarget()) {
if (upperBoundType.getKind() == TypeKind.INTERSECTION) {
final List<AnnotatedTypeMirror> bounds =
((AnnotatedIntersectionType) upperBoundType).getBounds();
final int boundIndex =
anno.position.bound_index + ElementAnnotationUtil.getBoundIndexOffset(bounds);
if (boundIndex < 0 || boundIndex > bounds.size()) {
throw new BugInCF(
"Invalid bound index on element annotation ( "
+ anno
+ " ) "
+ "for type ( "
+ typeParam
+ " ) with upper bound ( "
+ typeParam.getUpperBound()
+ " )");
}
addAnnotationToMap(bounds.get(boundIndex), anno, typeToAnnotations);
} else {
addAnnotationToMap(upperBoundType, anno, typeToAnnotations);
}
} else {
addAnnotationToMap(typeParam.getLowerBound(), anno, typeToAnnotations);
}
for (Map.Entry<AnnotatedTypeMirror, List<TypeCompound>> typeToAnno :
typeToAnnotations.entrySet()) {
ElementAnnotationUtil.annotateViaTypeAnnoPosition(typeToAnno.getKey(), typeToAnno.getValue());
}
}
}