blob: 00e1f4f0ec523da5a3b6b7fbfeddef95ee42f6a3 [file] [log] [blame]
package org.checkerframework.checker.nullness;
import com.sun.source.tree.NewClassTree;
import com.sun.source.tree.Tree;
import com.sun.source.util.TreePath;
import java.util.List;
import java.util.Set;
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.checker.nullness.qual.UnknownKeyFor;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType;
import org.checkerframework.framework.type.AnnotatedTypeReplacer;
import org.checkerframework.framework.util.TypeArgumentMapper;
import org.checkerframework.framework.util.typeinference.TypeArgInferenceUtil;
import org.checkerframework.javacutil.Pair;
/**
* KeyForPropagator is used to move nested KeyFor annotations in type arguments 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.
*
* <p>This class will replace @UnknownKeyFor annotations. It will also add annotations when they
* are missing for types that require primary annotation (i.e. not TypeVars, Wildcards,
* Intersections, or Unions).
*/
private final AnnotationMirror UNKNOWN_KEYFOR;
/** Instance of {@link KeyForPropagationReplacer}. */
private final KeyForPropagationReplacer replacer = new KeyForPropagationReplacer();
/**
* Creates a KeyForPropagator
*
* @param unknownKeyfor an {@link UnknownKeyFor} annotation
*/
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);
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:
replacer.visit(supertypeArg, subtypeArg);
break;
case TO_SUPERTYPE:
replacer.visit(subtypeArg, supertypeArg);
break;
case BOTH:
// note if they both have an annotation nothing will happen
replacer.visit(subtypeArg, supertypeArg);
replacer.visit(supertypeArg, subtypeArg);
break;
}
}
}
/**
* Propagate annotations from the type arguments of {@code type} to the assignment context of
* {@code newClassTree} if one exists.
*
* @param newClassTree new class tree
* @param type annotated type of {@code newClassTree}
* @param atypeFactory factory
*/
public void propagateNewClassTree(
NewClassTree newClassTree,
AnnotatedTypeMirror type,
KeyForAnnotatedTypeFactory atypeFactory) {
Pair<Tree, AnnotatedTypeMirror> context = atypeFactory.getVisitorState().getAssignmentContext();
if (type.getKind() != TypeKind.DECLARED || context == null || context.first == null) {
return;
}
TreePath path = atypeFactory.getPath(newClassTree);
if (path == null) {
return;
}
AnnotatedTypeMirror assignedTo = TypeArgInferenceUtil.assignedTo(atypeFactory, path);
if (assignedTo == null) {
return;
}
// array types and boxed primitives etc don't require propagation
if (assignedTo.getKind() == TypeKind.DECLARED) {
propagate(
(AnnotatedDeclaredType) type,
(AnnotatedDeclaredType) assignedTo,
PropagationDirection.TO_SUBTYPE,
atypeFactory);
}
}
/**
* An {@link AnnotatedTypeReplacer} that copies the annotation in KeyFor hierarchy from the first
* types to the second type, if the second type is annotated with @UnknownKeyFor or has no
* annotation in the KeyFor hierarchy.
*/
private class KeyForPropagationReplacer extends AnnotatedTypeReplacer {
@Override
protected void replaceAnnotations(AnnotatedTypeMirror from, AnnotatedTypeMirror to) {
AnnotationMirror fromKeyFor = from.getAnnotationInHierarchy(UNKNOWN_KEYFOR);
if (fromKeyFor != null) {
if (to.hasAnnotation(UNKNOWN_KEYFOR)
|| to.getAnnotationInHierarchy(UNKNOWN_KEYFOR) == null) {
to.replaceAnnotation(fromKeyFor);
}
}
}
}
}