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 org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable; import org.checkerframework.framework.util.AnnotatedTypes; import org.checkerframework.javacutil.ErrorReporter; /** 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, final TypeMirror inferredTypeMirror) { if (inferredSet == null) { return; } 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(type, inferredTypeMirror, top, primary); } else { removePrimaryTypeVarApplyUpperBound(type, inferredTypeMirror, top, primary); } } else { if (primary == null) { Set<AnnotationMirror> lowerbounds = AnnotatedTypes.findEffectiveLowerBoundAnnotations(hierarchy, type); AnnotationMirror lowerbound = hierarchy.findAnnotationInHierarchy(lowerbounds, top); if (omitSubtypingCheck || hierarchy.isSubtype(inferred, lowerbound)) { type.replaceAnnotation(inferred); } } else 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) { ErrorReporter.errorAbort( "Inferred value should not be missing annotations: " + inferredTypeMirror); return; } 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( AnnotatedTypeMirror type, TypeMirror typeMirror, AnnotationMirror top, final AnnotationMirror notInferred) { if (typeMirror.getKind() != TypeKind.TYPEVAR) { ErrorReporter.errorAbort("Missing annos"); return; } TypeVariable typeVar = (TypeVariable) typeMirror; AnnotatedTypeVariable typeVariableDecl = (AnnotatedTypeVariable) factory.getAnnotatedType(typeVar.asElement()); AnnotationMirror upperBound = typeVariableDecl.getEffectiveAnnotationInHierarchy(top); if (omitSubtypingCheck || hierarchy.isSubtype(upperBound, notInferred)) { AnnotatedTypeVariable typeTV = (AnnotatedTypeVariable) type; type.removeAnnotationInHierarchy(top); AnnotationMirror ub = typeVariableDecl.getUpperBound().getAnnotationInHierarchy(top); apply(typeTV.getUpperBound(), ub, typeVar.getUpperBound(), top); AnnotationMirror lb = typeVariableDecl.getLowerBound().getAnnotationInHierarchy(top); apply(typeTV.getLowerBound(), lb, typeVar.getLowerBound(), top); } } }