| package org.checkerframework.framework.type; |
| |
| import javax.lang.model.element.AnnotationMirror; |
| import javax.lang.model.type.TypeKind; |
| import org.checkerframework.checker.nullness.qual.Nullable; |
| import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable; |
| import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedWildcardType; |
| import org.checkerframework.framework.type.visitor.DoubleAnnotatedTypeScanner; |
| import org.checkerframework.javacutil.BugInCF; |
| |
| /** |
| * Replaces or adds all the annotations in the parameter with the annotations from the visited type. |
| * An annotation is replaced if the parameter type already has an annotation in the same hierarchy |
| * at the same location as the visited type. |
| * |
| * <p>Example use: |
| * |
| * <pre>{@code |
| * AnnotatedTypeMirror visitType = ...; |
| * AnnotatedTypeMirror parameter = ...; |
| * visitType.accept(new AnnotatedTypeReplacer(), parameter); |
| * }</pre> |
| */ |
| public class AnnotatedTypeReplacer extends DoubleAnnotatedTypeScanner<Void> { |
| |
| /** |
| * Replaces or adds all annotations from {@code from} to {@code to}. Annotations from {@code from} |
| * will be used everywhere they exist, but annotations in {@code to} will be kept anywhere that |
| * {@code from} is unannotated. |
| * |
| * @param from the annotated type mirror from which to take new annotations |
| * @param to the annotated type mirror to which the annotations will be added |
| * @deprecated use {@link AnnotatedTypeFactory#replaceAnnotations(AnnotatedTypeMirror, |
| * AnnotatedTypeMirror)} instead. |
| */ |
| @Deprecated // 2021-03-25 |
| @SuppressWarnings("interning:not.interned") // assertion |
| public static void replace(final AnnotatedTypeMirror from, final AnnotatedTypeMirror to) { |
| if (from == to) { |
| throw new BugInCF("From == to"); |
| } |
| new AnnotatedTypeReplacer().visit(from, to); |
| } |
| |
| /** |
| * Replaces or adds annotations in {@code top}'s hierarchy from {@code from} to {@code to}. |
| * Annotations from {@code from} will be used everywhere they exist, but annotations in {@code to} |
| * will be kept anywhere that {@code from} is unannotated. |
| * |
| * @param from the annotated type mirror from which to take new annotations |
| * @param to the annotated type mirror to which the annotations will be added |
| * @param top the top type of the hierarchy whose annotations will be added |
| * @deprecated use {@link AnnotatedTypeFactory#replaceAnnotations(AnnotatedTypeMirror, |
| * AnnotatedTypeMirror, AnnotationMirror)} instead. |
| */ |
| @Deprecated // 2021-03-25 |
| @SuppressWarnings("interning:not.interned") // assertion |
| public static void replace( |
| final AnnotatedTypeMirror from, final AnnotatedTypeMirror to, final AnnotationMirror top) { |
| if (from == to) { |
| throw new BugInCF("from == to: %s", from); |
| } |
| new AnnotatedTypeReplacer(top).visit(from, to); |
| } |
| |
| /** If top != null we replace only the annotations in the hierarchy of top. */ |
| private AnnotationMirror top; |
| |
| /** Construct an AnnotatedTypeReplacer that will replace all annotations. */ |
| public AnnotatedTypeReplacer() { |
| this.top = null; |
| } |
| |
| /** |
| * Construct an AnnotatedTypeReplacer that will only replace annotations in {@code top}'s |
| * hierarchy. |
| * |
| * @param top if top != null, then only annotation in the hierarchy of top are affected |
| */ |
| public AnnotatedTypeReplacer(final AnnotationMirror top) { |
| this.top = top; |
| } |
| |
| /** |
| * If {@code top != null}, then only annotations in the hierarchy of {@code top} are affected; |
| * otherwise, all annotations are replaced. |
| * |
| * @param top if top != null, then only annotations in the hierarchy of top are replaced; |
| * otherwise, all annotations are replaced. |
| */ |
| public void setTop(@Nullable AnnotationMirror top) { |
| this.top = top; |
| } |
| |
| @SuppressWarnings("interning:not.interned") // assertion |
| @Override |
| protected Void defaultAction(AnnotatedTypeMirror from, AnnotatedTypeMirror to) { |
| assert from != to; |
| if (from != null && to != null) { |
| replaceAnnotations(from, to); |
| } |
| return null; |
| } |
| |
| /** |
| * Replace the annotations in to with the annotations in from, wherever from has an annotation. |
| * |
| * @param from the source of the annotations |
| * @param to the destination of the annotations, modified by this method |
| */ |
| protected void replaceAnnotations(final AnnotatedTypeMirror from, final AnnotatedTypeMirror to) { |
| if (top == null) { |
| to.replaceAnnotations(from.getAnnotations()); |
| } else { |
| final AnnotationMirror replacement = from.getAnnotationInHierarchy(top); |
| if (replacement != null) { |
| to.replaceAnnotation(from.getAnnotationInHierarchy(top)); |
| } |
| } |
| } |
| |
| @Override |
| public Void visitTypeVariable(AnnotatedTypeVariable from, AnnotatedTypeMirror to) { |
| resolvePrimaries(from, to); |
| return super.visitTypeVariable(from, to); |
| } |
| |
| @Override |
| public Void visitWildcard(AnnotatedWildcardType from, AnnotatedTypeMirror to) { |
| resolvePrimaries(from, to); |
| return super.visitWildcard(from, to); |
| } |
| |
| /** |
| * For type variables and wildcards, the absence of a primary annotations has an implied meaning |
| * on substitution. Therefore, in these cases we remove the primary annotation and rely on the |
| * fact that the bounds are also merged into the type to. |
| * |
| * @param from a type variable or wildcard |
| * @param to the destination annotated type mirror |
| */ |
| public void resolvePrimaries(AnnotatedTypeMirror from, AnnotatedTypeMirror to) { |
| if (from.getKind() == TypeKind.WILDCARD || from.getKind() == TypeKind.TYPEVAR) { |
| if (top != null) { |
| if (from.getAnnotationInHierarchy(top) == null) { |
| to.removeAnnotationInHierarchy(top); |
| } |
| } else { |
| for (final AnnotationMirror toPrimaryAnno : to.getAnnotations()) { |
| if (from.getAnnotationInHierarchy(toPrimaryAnno) == null) { |
| to.removeAnnotation(toPrimaryAnno); |
| } |
| } |
| } |
| } else { |
| throw new BugInCF( |
| "ResolvePrimaries's from argument should be a type variable OR wildcard%n" |
| + "from=%s%nto=%s", |
| from.toString(true), to.toString(true)); |
| } |
| } |
| } |