blob: ebce18eefacb242dc2fed28bb7a21094e816b30a [file] [log] [blame]
package org.checkerframework.framework.type;
import java.util.Set;
import javax.lang.model.element.AnnotationMirror;
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.AnnotatedTypeVariable;
import org.checkerframework.framework.util.AnnotatedTypes;
import org.checkerframework.javacutil.BugInCF;
/** Utility class for applying the annotations inferred by dataflow to a given type. */
public class DefaultInferredTypesApplier {
// At the moment, only Inference uses the omitSubtypingCheck option.
// In actuality the subtyping check should be unnecessary since inferred
// types should be subtypes of their declaration.
private final boolean omitSubtypingCheck;
private final QualifierHierarchy hierarchy;
private final AnnotatedTypeFactory factory;
public DefaultInferredTypesApplier(QualifierHierarchy hierarchy, AnnotatedTypeFactory factory) {
this(false, hierarchy, factory);
}
public DefaultInferredTypesApplier(
boolean omitSubtypingCheck, QualifierHierarchy hierarchy, AnnotatedTypeFactory factory) {
this.omitSubtypingCheck = omitSubtypingCheck;
this.hierarchy = hierarchy;
this.factory = factory;
}
/**
* For each top in qualifier hierarchy, traverse inferred and copy the required annotations over
* to type.
*
* @param type the type to which annotations are being applied
* @param inferredSet the type inferred by data flow
* @param inferredTypeMirror underlying inferred type
*/
public void applyInferredType(
final AnnotatedTypeMirror type,
final Set<AnnotationMirror> inferredSet,
TypeMirror inferredTypeMirror) {
if (inferredSet == null) {
return;
}
if (inferredTypeMirror.getKind() == TypeKind.WILDCARD) {
// Dataflow might infer a wildcard that extends a type variable for types that are
// actually type variables. Use the type variable instead.
while (inferredTypeMirror.getKind() == TypeKind.WILDCARD
&& (((WildcardType) inferredTypeMirror).getExtendsBound() != null)) {
inferredTypeMirror = ((WildcardType) inferredTypeMirror).getExtendsBound();
}
}
for (final AnnotationMirror top : hierarchy.getTopAnnotations()) {
AnnotationMirror inferred = hierarchy.findAnnotationInHierarchy(inferredSet, top);
apply(type, inferred, inferredTypeMirror, top);
}
}
private void apply(
AnnotatedTypeMirror type,
AnnotationMirror inferred,
TypeMirror inferredTypeMirror,
AnnotationMirror top) {
AnnotationMirror primary = type.getAnnotationInHierarchy(top);
if (inferred == null) {
if (primary == null) {
// Type doesn't have a primary either, nothing to remove
} else if (type.getKind() == TypeKind.TYPEVAR) {
removePrimaryAnnotationTypeVar(
(AnnotatedTypeVariable) type, inferredTypeMirror, top, primary);
} else {
removePrimaryTypeVarApplyUpperBound(type, inferredTypeMirror, top, primary);
}
} else {
if (primary == null) {
Set<AnnotationMirror> lowerbounds =
AnnotatedTypes.findEffectiveLowerBoundAnnotations(hierarchy, type);
primary = hierarchy.findAnnotationInHierarchy(lowerbounds, top);
}
if ((omitSubtypingCheck || hierarchy.isSubtype(inferred, primary))) {
type.replaceAnnotation(inferred);
}
}
}
private void removePrimaryTypeVarApplyUpperBound(
AnnotatedTypeMirror type,
TypeMirror inferredTypeMirror,
AnnotationMirror top,
AnnotationMirror notInferred) {
if (inferredTypeMirror.getKind() != TypeKind.TYPEVAR) {
throw new BugInCF("Inferred value should not be missing annotations: " + inferredTypeMirror);
}
TypeVariable typeVar = (TypeVariable) inferredTypeMirror;
AnnotatedTypeVariable typeVariableDecl =
(AnnotatedTypeVariable) factory.getAnnotatedType(typeVar.asElement());
AnnotationMirror upperBound = typeVariableDecl.getEffectiveAnnotationInHierarchy(top);
if (omitSubtypingCheck || hierarchy.isSubtype(upperBound, notInferred)) {
type.replaceAnnotation(upperBound);
}
}
private void removePrimaryAnnotationTypeVar(
AnnotatedTypeVariable annotatedTypeVariable,
TypeMirror inferredTypeMirror,
AnnotationMirror top,
AnnotationMirror previousAnnotation) {
if (inferredTypeMirror.getKind() != TypeKind.TYPEVAR) {
throw new BugInCF("Missing annos");
}
TypeVariable typeVar = (TypeVariable) inferredTypeMirror;
AnnotatedTypeVariable typeVariableDecl =
(AnnotatedTypeVariable) factory.getAnnotatedType(typeVar.asElement());
AnnotationMirror upperBound = typeVariableDecl.getEffectiveAnnotationInHierarchy(top);
if (omitSubtypingCheck || hierarchy.isSubtype(upperBound, previousAnnotation)) {
annotatedTypeVariable.removeAnnotationInHierarchy(top);
AnnotationMirror ub = typeVariableDecl.getUpperBound().getAnnotationInHierarchy(top);
apply(annotatedTypeVariable.getUpperBound(), ub, typeVar.getUpperBound(), top);
AnnotationMirror lb = typeVariableDecl.getLowerBound().getAnnotationInHierarchy(top);
apply(annotatedTypeVariable.getLowerBound(), lb, typeVar.getLowerBound(), top);
}
}
}