package org.checkerframework.checker.nullness; import java.util.List; import java.util.Set; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeKind; import javax.lang.model.util.Types; import org.checkerframework.framework.type.AnnotatedTypeFactory; import org.checkerframework.framework.type.AnnotatedTypeMirror; import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType; import org.checkerframework.framework.type.visitor.AnnotatedTypeMerger; import org.checkerframework.framework.util.AnnotationBuilder; import org.checkerframework.framework.util.TypeArgumentMapper; import org.checkerframework.javacutil.AnnotationUtils; import org.checkerframework.javacutil.Pair; /** * KeyForPropagator is used to move nested KeyFor annotations from one side of a pseudo-assignment * to the other. The KeyForPropagationTreeAnnotator details the locations in which this occurs. * * @see org.checkerframework.checker.nullness.KeyForPropagationTreeAnnotator */ public class KeyForPropagator { public static enum PropagationDirection { // transfer FROM the super type to the subtype TO_SUBTYPE, // transfer FROM the subtype to the supertype TO_SUPERTYPE, // first execute TO_SUBTYPE then TO_SUPERTYPE, if TO_SUBTYPE actually transfers // an annotation for a particular type T then T will not be affected by the // TO_SUPERTYPE transfer because it will already have a KeyFor annotation BOTH } // The top type of the KeyFor hierarchy, this class will replace @UnknownKeyFor annotations. // It will also add annotations when they are misisng for types that require primary annotation // (i.e. not TypeVars, Wildcards, Intersections, or Unions). private final AnnotationMirror UNKNOWN_KEYFOR; public KeyForPropagator(AnnotationMirror unknownKeyfor) { this.UNKNOWN_KEYFOR = unknownKeyfor; } /** * Propagate annotations from the type arguments of one type to another. Which type is the * source and destination of the annotations depends on the direction parameter. Only @KeyFor * annotations are propagated and only if the type to which it would be propagated contains * an @UnknownKeyFor or contains no key for annotations of any kind. If any of the type * arguments are wildcards than they are ignored. * * <p>Note the primary annotations of subtype/supertype are not used. * * <p>Simple Example: * * <pre>{@code * typeOf(subtype) = ArrayList<@KeyFor("a") String> * typeOf(supertype) = List<@UnknownKeyFor String> * direction = TO_SUPERTYPE * }</pre> * * The type of supertype after propagate would be: {@code List<@KeyFor("a") String>} * * <p>A more complex example would be: * * <pre>{@code * typeOf(subtype) = HashMap<@UnknownKeyFor String, @KeyFor("b") List<@KeyFor("c") String>> * typeOf(supertype) = Map<@KeyFor("a") String, @KeyFor("b") List<@KeyFor("c") String>> * direction = TO_SUBTYPE * }</pre> * * The type of subtype after propagate would be: {@code HashMap<@KeyFor("a") * String, @KeyFor("b") List<@KeyFor("c") String>>} */ public void propagate( final AnnotatedDeclaredType subtype, final AnnotatedDeclaredType supertype, PropagationDirection direction, final AnnotatedTypeFactory typeFactory) { final TypeElement subtypeElement = (TypeElement) subtype.getUnderlyingType().asElement(); final TypeElement supertypeElement = (TypeElement) supertype.getUnderlyingType().asElement(); final Types types = typeFactory.getProcessingEnv().getTypeUtils(); // Note: The right hand side of this or expression will cover raw types if (subtype.getTypeArguments().isEmpty()) { return; } // else // this can happen for two reasons: // 1) the subclass introduced NEW type arguments when the superclass had none // 2) the supertype was RAW. // In either case, there is no reason to propagate if (supertype.getTypeArguments().isEmpty()) { return; } Set<Pair<Integer, Integer>> typeParamMappings = TypeArgumentMapper.mapTypeArgumentIndices(subtypeElement, supertypeElement, types); KeyForPropagationMerger merger = new KeyForPropagationMerger(typeFactory.getProcessingEnv()); final List<AnnotatedTypeMirror> subtypeArgs = subtype.getTypeArguments(); final List<AnnotatedTypeMirror> supertypeArgs = supertype.getTypeArguments(); for (final Pair<Integer, Integer> path : typeParamMappings) { final AnnotatedTypeMirror subtypeArg = subtypeArgs.get(path.first); final AnnotatedTypeMirror supertypeArg = supertypeArgs.get(path.second); if (subtypeArg.getKind() == TypeKind.WILDCARD || supertypeArg.getKind() == TypeKind.WILDCARD) { continue; } switch (direction) { case TO_SUBTYPE: merger.visit(supertypeArg, subtypeArg); break; case TO_SUPERTYPE: merger.visit(subtypeArg, supertypeArg); break; case BOTH: // note if they both have an annotation nothing will happen merger.visit(subtypeArg, supertypeArg); merger.visit(supertypeArg, subtypeArg); break; } } } /** * An annotated type merger that merges @KeyFor annotations and only if the type that is * receiving an annotation has an @UnknownKeyFor annotation or NO key for annotations. */ private class KeyForPropagationMerger extends AnnotatedTypeMerger { private final ProcessingEnvironment env; private KeyForPropagationMerger(ProcessingEnvironment env) { this.env = env; } @Override protected void replaceAnnotations(AnnotatedTypeMirror from, AnnotatedTypeMirror to) { final AnnotationMirror fromKeyFor = from.getAnnotationInHierarchy(UNKNOWN_KEYFOR); final AnnotationMirror toKeyFor = to.getAnnotationInHierarchy(UNKNOWN_KEYFOR); boolean toNeedsAnnotation = toKeyFor == null || AnnotationUtils.areSame(toKeyFor, UNKNOWN_KEYFOR); if (fromKeyFor != null && toNeedsAnnotation) { AnnotationBuilder annotationBuilder = new AnnotationBuilder(env, fromKeyFor); to.replaceAnnotation(annotationBuilder.build()); } } } }