package org.checkerframework.framework.type.typeannotator; import com.sun.source.tree.Tree; import java.lang.annotation.Annotation; import java.util.EnumMap; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Map; import java.util.Set; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import org.checkerframework.framework.qual.ImplicitFor; import org.checkerframework.framework.type.AnnotatedTypeFactory; import org.checkerframework.framework.type.AnnotatedTypeMirror; import org.checkerframework.framework.type.QualifierHierarchy; import org.checkerframework.javacutil.AnnotationUtils; import org.checkerframework.javacutil.ErrorReporter; import org.checkerframework.javacutil.TypesUtils; /** * Adds annotations to a type based on the contents of a type. By default, this class honors the * {@link ImplicitFor} annotation and applies implicit annotations specified by {@link ImplicitFor} * for any type whose visitor is not overridden or does not call {@code super}; it is designed to be * invoked from {@link * org.checkerframework.framework.type.AnnotatedTypeFactory#addComputedTypeAnnotations(Element, * org.checkerframework.framework.type.AnnotatedTypeMirror)} and {@link * org.checkerframework.framework.type.AnnotatedTypeFactory#addComputedTypeAnnotations(Tree, * org.checkerframework.framework.type.AnnotatedTypeMirror)}. * * <p>{@link ImplicitsTypeAnnotator} traverses types deeply by default, except that it skips the * method receiver of executable types (for interoperability with {@link * org.checkerframework.framework.type.AnnotatedTypeFactory#annotateInheritedFromClass(org.checkerframework.framework.type.AnnotatedTypeMirror)}). * * <p>This class takes care of two of the attributes of {@link ImplicitFor}; the others are handled * in {@link org.checkerframework.framework.type.treeannotator.TreeAnnotator}. * * @see org.checkerframework.framework.type.treeannotator.TreeAnnotator */ public class ImplicitsTypeAnnotator extends TypeAnnotator { private final Map<TypeKind, Set<AnnotationMirror>> typeKinds; private final Map<Class<? extends AnnotatedTypeMirror>, Set<AnnotationMirror>> typeClasses; private final Map<String, Set<AnnotationMirror>> typeNames; private final QualifierHierarchy qualHierarchy; // private final AnnotatedTypeFactory atypeFactory; /** * Creates a {@link ImplicitsTypeAnnotator} from the given checker, using that checker to * determine the annotations that are in the type hierarchy. */ public ImplicitsTypeAnnotator(AnnotatedTypeFactory typeFactory) { super(typeFactory); this.typeKinds = new EnumMap<TypeKind, Set<AnnotationMirror>>(TypeKind.class); this.typeClasses = new HashMap<Class<? extends AnnotatedTypeMirror>, Set<AnnotationMirror>>(); this.typeNames = new IdentityHashMap<String, Set<AnnotationMirror>>(); this.qualHierarchy = typeFactory.getQualifierHierarchy(); // this.atypeFactory = atypeFactory; // Get type qualifiers from the checker. Set<Class<? extends Annotation>> quals = typeFactory.getSupportedTypeQualifiers(); // For each qualifier, read the @ImplicitFor annotation and put its type // classes and kinds into maps. for (Class<? extends Annotation> qual : quals) { ImplicitFor implicit = qual.getAnnotation(ImplicitFor.class); if (implicit == null) continue; AnnotationMirror theQual = AnnotationUtils.fromClass(typeFactory.getElementUtils(), qual); for (TypeKind typeKind : implicit.types()) { addTypeKind(typeKind, theQual); } for (Class<?> typeName : implicit.typeNames()) { addTypeName(typeName, theQual); } } } public void addTypeKind(TypeKind typeKind, AnnotationMirror theQual) { boolean res = qualHierarchy.updateMappingToMutableSet(typeKinds, typeKind, theQual); if (!res) { ErrorReporter.errorAbort( "TypeAnnotator: invalid update of typeKinds " + typeKinds + " at " + typeKind + " with " + theQual); } } public void addTypeClass( Class<? extends AnnotatedTypeMirror> typeClass, AnnotationMirror theQual) { boolean res = qualHierarchy.updateMappingToMutableSet(typeClasses, typeClass, theQual); if (!res) { ErrorReporter.errorAbort( "TypeAnnotator: invalid update of typeClasses " + typeClasses + " at " + typeClass + " with " + theQual); } } public void addTypeName(Class<?> typeName, AnnotationMirror theQual) { String typeNameString = typeName.getCanonicalName().intern(); boolean res = qualHierarchy.updateMappingToMutableSet(typeNames, typeNameString, theQual); if (!res) { ErrorReporter.errorAbort( "TypeAnnotator: invalid update of typeNames " + typeNames + " at " + typeName + " with " + theQual); } } @Override protected Void scan(AnnotatedTypeMirror type, Void p) { if (type == null) // on bounds, etc. return super.scan(type, p); // If the type's fully-qualified name is in the appropriate map, annotate // the type. Do this before looking at kind or class, as this information // is more specific. String qname = null; if (type.getKind() == TypeKind.DECLARED) { qname = TypesUtils.getQualifiedName((DeclaredType) type.getUnderlyingType()).toString(); } else if (type.getKind().isPrimitive()) { qname = type.getUnderlyingType().toString(); } qname = (qname == null) ? null : qname.intern(); if (qname != null && typeNames.containsKey(qname)) { Set<AnnotationMirror> fnd = typeNames.get(qname); type.addMissingAnnotations(fnd); } // If the type's kind or class is in the appropriate map, annotate the // type. if (typeKinds.containsKey(type.getKind())) { Set<AnnotationMirror> fnd = typeKinds.get(type.getKind()); type.addMissingAnnotations(fnd); } else if (!typeClasses.isEmpty()) { Class<? extends AnnotatedTypeMirror> t = type.getClass(); if (typeClasses.containsKey(t)) { Set<AnnotationMirror> fnd = typeClasses.get(t); type.addMissingAnnotations(fnd); } } return super.scan(type, p); } }