All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.checkerframework.framework.flow.CFAbstractValue Maven / Gradle / Ivy

Go to download

The Checker Framework enhances Java's type system to make it more powerful and useful. This lets software developers detect and prevent errors in their Java programs. The Checker Framework includes compiler plug-ins ("checkers") that find bugs or verify their absence. It also permits you to write your own compiler plug-ins.

There is a newer version: 3.42.0-eisop4
Show newest version
package org.checkerframework.framework.flow;

import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.dataflow.analysis.AbstractValue;
import org.checkerframework.dataflow.analysis.Analysis;
import org.checkerframework.dataflow.qual.Pure;
import org.checkerframework.dataflow.qual.SideEffectFree;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedWildcardType;
import org.checkerframework.framework.type.GenericAnnotatedTypeFactory;
import org.checkerframework.framework.type.QualifierHierarchy;
import org.checkerframework.framework.util.AnnotatedTypes;
import org.checkerframework.javacutil.AnnotationMirrorSet;
import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.TypesUtils;
import org.plumelib.util.StringsPlume;

import java.util.Objects;

import javax.annotation.processing.ProcessingEnvironment;
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 javax.lang.model.type.WildcardType;
import javax.lang.model.util.Types;

/**
 * An implementation of an abstract value used by the Checker Framework
 * org.checkerframework.dataflow analysis.
 *
 * 

A value holds a set of annotations and a type mirror. The set of annotations represents the * primary annotation on a type; therefore, the set of annotations must have an annotation for each * hierarchy unless the type mirror is a type variable or a wildcard that extends a type variable. * Both type variables and wildcards may be missing a primary annotation. For this set of * annotations, there is an additional constraint that only wildcards that extend type variables can * be missing annotations. * *

In order to compute {@link #leastUpperBound(CFAbstractValue)} and {@link * #mostSpecific(CFAbstractValue, CFAbstractValue)}, the case where one value has an annotation in a * hierarchy and the other does not must be handled. For type variables, the {@link * AnnotatedTypeVariable} for the declaration of the type variable is used. The {@link * AnnotatedTypeVariable} is computed using the type mirror. For wildcards, it is not always * possible to get the {@link AnnotatedWildcardType} for the type mirror. However, a * CFAbstractValue's type mirror is only a wildcard if the type of some expression is a wildcard. * The type of an expression is only a wildcard because the Checker Framework does not implement * capture conversion. For these uses of uncaptured wildcards, only the primary annotation on the * upper bound is ever used. So, the set of annotations represents the primary annotation on the * wildcard's upper bound. If that upper bound is a type variable, then the set of annotations could * be missing an annotation in a hierarchy. * * @param the values that this CFAbstractValue wraps */ public abstract class CFAbstractValue> implements AbstractValue { /** The analysis class this value belongs to. */ protected final CFAbstractAnalysis analysis; /** The underlying (Java) type in this abstract value. */ protected final TypeMirror underlyingType; /** The annotations in this abstract value. */ protected final AnnotationMirrorSet annotations; /** * Creates a new CFAbstractValue. * * @param analysis the analysis class this value belongs to * @param annotations the annotations in this abstract value * @param underlyingType the underlying (Java) type in this abstract value */ protected CFAbstractValue( CFAbstractAnalysis analysis, AnnotationMirrorSet annotations, TypeMirror underlyingType) { this.analysis = analysis; this.annotations = annotations; this.underlyingType = underlyingType; assert validateSet( this.getAnnotations(), this.getUnderlyingType(), analysis.getTypeFactory()) : "Encountered invalid type: " + underlyingType + " annotations: " + StringsPlume.join(", ", annotations); } /** * Returns true if the set has an annotation from every hierarchy (or if it doesn't need to); * returns false if the set is missing an annotation from some hierarchy. * * @param annos set of annotations * @param typeMirror where the annotations are written * @param atypeFactory the type factory * @return true if no annotations are missing */ public static boolean validateSet( AnnotationMirrorSet annos, TypeMirror typeMirror, AnnotatedTypeFactory atypeFactory) { boolean canBeMissing = canBeMissingAnnotations(typeMirror); if (canBeMissing) { return true; } // TODO: temporarily disable stricter size comparison. // if (annos.size() != hierarchy.getTopAnnotations().size()) { // return false; // } // The size of the set matches, but maybe it contains multiple annos of one // hierarchy and none of another. QualifierHierarchy qualHierarchy = atypeFactory.getQualifierHierarchy(); for (AnnotationMirror top : qualHierarchy.getTopAnnotations()) { AnnotationMirror anno = qualHierarchy.findAnnotationInHierarchy(annos, top); if (anno == null) { return false; } } return true; } /** * Returns whether or not the set of annotations can be missing an annotation for any hierarchy. * * @return whether or not the set of annotations can be missing an annotation */ public boolean canBeMissingAnnotations() { return canBeMissingAnnotations(underlyingType); } /** * Returns true if it is OK for the given type mirror not to be annotated, such as for VOID, * NONE, PACKAGE, TYPEVAR, and some WILDCARD. * * @param typeMirror a type * @return true if it is OK for the given type mirror not to be annotated */ private static boolean canBeMissingAnnotations(TypeMirror typeMirror) { if (typeMirror == null) { return false; } if (typeMirror.getKind() == TypeKind.VOID || typeMirror.getKind() == TypeKind.NONE || typeMirror.getKind() == TypeKind.PACKAGE) { return true; } if (typeMirror.getKind() == TypeKind.WILDCARD) { return canBeMissingAnnotations(((WildcardType) typeMirror).getExtendsBound()); } return typeMirror.getKind() == TypeKind.TYPEVAR; } /** * Returns a set of annotations. If {@link #canBeMissingAnnotations()} returns true, then the * set of annotations may not have an annotation for every hierarchy. * *

To get the single annotation in a particular hierarchy, use {@link * QualifierHierarchy#findAnnotationInHierarchy}. * * @return a set of annotations */ @Pure public AnnotationMirrorSet getAnnotations() { return annotations; } @Pure public TypeMirror getUnderlyingType() { return underlyingType; } @SuppressWarnings("interning:not.interned") // efficiency pre-test @Override public boolean equals(@Nullable Object obj) { if (!(obj instanceof CFAbstractValue)) { return false; } CFAbstractValue other = (CFAbstractValue) obj; if (this.getUnderlyingType() != other.getUnderlyingType() && !analysis.getTypes() .isSameType(this.getUnderlyingType(), other.getUnderlyingType())) { return false; } return AnnotationUtils.areSame(this.getAnnotations(), other.getAnnotations()); } @Pure @Override public int hashCode() { return Objects.hash(getAnnotations(), underlyingType); } /** * Returns the string representation, using fully-qualified names. * * @return the string representation, using fully-qualified names */ @SideEffectFree public String toStringFullyQualified() { return "CFAV{" + annotations + ", " + underlyingType + '}'; } /** * Returns the string representation, using simple (not fully-qualified) names. * * @return the string representation, using simple (not fully-qualified) names */ @SideEffectFree public String toStringSimple() { return "CFAV{" + AnnotationUtils.toStringSimple(annotations) + ", " + TypesUtils.simpleTypeName(underlyingType) + '}'; } /** * Returns the string representation. * * @return the string representation */ @SideEffectFree @Override public String toString() { return toStringSimple(); } /** * Returns the more specific of two values {@code this} and {@code other}. If they do not * contain information for all hierarchies, then it is possible that information from both * {@code this} and {@code other} are taken. * *

If neither of the two is more specific for one of the hierarchies (i.e., if the two are * incomparable as determined by {@link QualifierHierarchy#isSubtypeShallow(AnnotationMirror, * TypeMirror, AnnotationMirror, TypeMirror)}, then the respective value from {@code backup} is * used. * * @param other the other value to obtain information from * @param backup the value to use if {@code this} and {@code other} are incomparable * @return the more specific of two values {@code this} and {@code other} */ public V mostSpecific(@Nullable V other, @Nullable V backup) { if (other == null) { @SuppressWarnings("unchecked") V v = (V) this; return v; } Types types = analysis.getTypes(); TypeMirror mostSpecifTypeMirror; if (types.isAssignable(this.getUnderlyingType(), other.getUnderlyingType())) { mostSpecifTypeMirror = this.getUnderlyingType(); } else if (types.isAssignable(other.getUnderlyingType(), this.getUnderlyingType())) { mostSpecifTypeMirror = other.getUnderlyingType(); } else if (TypesUtils.isErasedSubtype( this.getUnderlyingType(), other.getUnderlyingType(), types)) { mostSpecifTypeMirror = this.getUnderlyingType(); } else if (TypesUtils.isErasedSubtype( other.getUnderlyingType(), this.getUnderlyingType(), types)) { mostSpecifTypeMirror = other.getUnderlyingType(); } else { mostSpecifTypeMirror = this.getUnderlyingType(); } MostSpecificVisitor ms = new MostSpecificVisitor( this.getUnderlyingType(), other.getUnderlyingType(), backup); AnnotationMirrorSet mostSpecific = ms.combineSets( this.getUnderlyingType(), this.getAnnotations(), other.getUnderlyingType(), other.getAnnotations(), canBeMissingAnnotations(mostSpecifTypeMirror)); if (ms.error) { return backup; } return analysis.createAbstractValue(mostSpecific, mostSpecifTypeMirror); } /** Computes the most specific annotations. */ private class MostSpecificVisitor extends AnnotationSetCombiner { /** If set to true, then this visitor was unable to find a most specific annotation. */ boolean error = false; /** Set of annotations to use if a most specific value cannot be found. */ final AnnotationMirrorSet backupAMSet; /** * Create a {@link MostSpecificVisitor}. * * @param aTypeMirror type of the "a" value * @param bTypeMirror type of the "b" value * @param backup value to use if no most specific value is found */ @SuppressWarnings("UnusedVariable") // TODO clean this up public MostSpecificVisitor(TypeMirror aTypeMirror, TypeMirror bTypeMirror, V backup) { if (backup != null) { this.backupAMSet = backup.getAnnotations(); // this.backupTypeMirror = backup.getUnderlyingType(); // this.backupAtv = getEffectiveTypeVar(backupTypeMirror); } else { // this.backupAtv = null; // this.backupTypeMirror = null; this.backupAMSet = null; } } /** * Returns the backup annotation that is in the same hierarchy as {@code top}. * * @param top an annotation * @return the backup annotation that is in the same hierarchy as {@code top} */ private @Nullable AnnotationMirror getBackupAnnoIn(AnnotationMirror top) { if (backupAMSet == null) { // If there is no backup value, but one is required, then the resulting set will // not be the most specific. Indicate this with the error. error = true; return null; } QualifierHierarchy qualHierarchy = analysis.getTypeFactory().getQualifierHierarchy(); return qualHierarchy.findAnnotationInHierarchy(backupAMSet, top); } @Override protected @Nullable AnnotationMirror combineTwoAnnotations( AnnotationMirror a, TypeMirror aTypeMirror, AnnotationMirror b, TypeMirror bTypeMirror, AnnotationMirror top) { if (aTypeMirror == null) { throw new NullPointerException("combineTwoAnnotations: aTypeMirror==null"); } if (bTypeMirror == null) { throw new NullPointerException("combineTwoAnnotations: bTypeMirror==null"); } GenericAnnotatedTypeFactory gatf = analysis.getTypeFactory(); QualifierHierarchy qualHierarchy = gatf.getQualifierHierarchy(); if (gatf.hasQualifierParameterInHierarchy(TypesUtils.getTypeElement(aTypeMirror), top) && gatf.hasQualifierParameterInHierarchy( TypesUtils.getTypeElement(bTypeMirror), top)) { // Both types have qualifier parameters, so they are related by invariance rather // than subtyping. if (qualHierarchy.isSubtypeShallow(a, aTypeMirror, b, bTypeMirror) && qualHierarchy.isSubtypeShallow(b, bTypeMirror, a, aTypeMirror)) { return b; } } else if (qualHierarchy.isSubtypeShallow(a, aTypeMirror, b, bTypeMirror)) { // `a` may not be a subtype of `b`, if one of the type mirrors isn't relevant, // so return the lower of the two. return lowestQualifier(a, b); } else if (qualHierarchy.isSubtypeShallow(b, bTypeMirror, a, aTypeMirror)) { // `b` may not be a subtype of `a`, if one of the type mirrors isn't relevant, // so return the lower of the two. return lowestQualifier(a, b); } return getBackupAnnoIn(top); } /** * Returns the qualifier that is the lowest in the hierarchy. If the two qualifiers are not * comparable, then returns the qualifier that is ordered first by {@link * AnnotationUtils#compareAnnotationMirrors(AnnotationMirror, AnnotationMirror)}. * *

This is similar to glb, but one of the given qualifiers is always returned. * * @param qual1 a qualifier * @param qual2 a qualifier * @return the qualifier that is the lowest in the hierarchy */ private final AnnotationMirror lowestQualifier( AnnotationMirror qual1, AnnotationMirror qual2) { QualifierHierarchy qualHierarchy = analysis.getTypeFactory().getQualifierHierarchy(); if (qualHierarchy.isSubtypeQualifiersOnly(qual1, qual2)) { return qual1; } else if (qualHierarchy.isSubtypeQualifiersOnly(qual2, qual1)) { return qual2; } else { int i = AnnotationUtils.compareAnnotationMirrors(qual1, qual2); if (i > 0) { return qual2; } else { return qual1; } } } @Override protected @Nullable AnnotationMirror combineTwoTypeVars( AnnotatedTypeVariable aAtv, AnnotatedTypeVariable bAtv, AnnotationMirror top, boolean canCombinedSetBeMissingAnnos) { if (canCombinedSetBeMissingAnnos) { return null; } else { AnnotationMirror aUB = aAtv.getEffectiveAnnotationInHierarchy(top); AnnotationMirror bUB = bAtv.getEffectiveAnnotationInHierarchy(top); TypeMirror aTM = aAtv.getUnderlyingType(); TypeMirror bTM = bAtv.getUnderlyingType(); return combineTwoAnnotations(aUB, aTM, bUB, bTM, top); } } @Override protected @Nullable AnnotationMirror combineAnnotationWithTypeVar( AnnotationMirror annotation, AnnotatedTypeVariable typeVar, AnnotationMirror top, boolean canCombinedSetBeMissingAnnos) { AnnotationMirror upperBound = typeVar.getEffectiveAnnotationInHierarchy(top); TypeMirror upperBoundTM = typeVar.getUpperBound().getUnderlyingType(); if (!canCombinedSetBeMissingAnnos) { TypeVariable typeVarTM = typeVar.getUnderlyingType(); return combineTwoAnnotations(annotation, typeVarTM, upperBound, typeVarTM, top); } QualifierHierarchy qualHierarchy = analysis.getTypeFactory().getQualifierHierarchy(); AnnotationMirrorSet lBSet = AnnotatedTypes.findEffectiveLowerBoundAnnotations(qualHierarchy, typeVar); AnnotationMirror lowerBound = qualHierarchy.findAnnotationInHierarchy(lBSet, top); TypeMirror lowerBoundTM = typeVar.getLowerBound().getUnderlyingType(); TypeMirror typeVarTM = typeVar.getUnderlyingType(); if (qualHierarchy.isSubtypeShallow(upperBound, upperBoundTM, annotation, typeVarTM)) { // no anno is more specific than anno return null; } else if (qualHierarchy.isSubtypeShallow( annotation, typeVarTM, lowerBound, lowerBoundTM)) { return lowestQualifier(annotation, lowerBound); } else { return getBackupAnnoIn(top); } } } /** * {@inheritDoc} * *

Subclasses should override {@link #upperBound(CFAbstractValue, TypeMirror, boolean)} * instead of this method. */ @Override public final V leastUpperBound(@Nullable V other) { return upperBound(other, false); } /** * Compute the least upper bound of two abstract values. The returned value has a Java type of * {@code typeMirror}. {@code typeMirror} should be an upper bound of the Java types of {@code * this} an {@code other}, but it does not have be to the least upper bound. * *

Subclasses should override {@link #upperBound(CFAbstractValue, TypeMirror, boolean)} * instead of this method. * * @param other another value * @param typeMirror the underlying Java type of the returned value, which may or may not be the * least upper bound * @return the least upper bound of two abstract values */ public final V leastUpperBound(@Nullable V other, TypeMirror typeMirror) { return upperBound(other, typeMirror, false); } /** * Compute an upper bound of two values that is wider than the least upper bound of the two * values. Used to jump to a higher abstraction to allow faster termination of the fixed point * computations in {@link Analysis}. * *

A particular analysis might not require widening and should implement this method by * calling leastUpperBound. * *

Important: This method must fulfill the following contract: * *

    *
  • Does not change {@code this}. *
  • Does not change {@code previous}. *
  • Returns a fresh object which is not aliased yet. *
  • Returns an object of the same (dynamic) type as {@code this}, even if the signature is * more permissive. *
  • Is commutative. *
* * Subclasses should override {@link #upperBound(CFAbstractValue, TypeMirror, boolean)} instead * of this method. * * @param previous must be the previous value * @return an upper bound of two values that is wider than the least upper bound of the two * values */ public final V widenUpperBound(@Nullable V previous) { return upperBound(previous, true); } /** * Returns the least upper bound of this and {@code other}. * * @param other an abstract value * @param shouldWiden true if the lub should perform widening * @return the least upper bound of this and {@code other} */ private V upperBound(@Nullable V other, boolean shouldWiden) { if (other == null) { @SuppressWarnings("unchecked") V v = (V) this; return v; } ProcessingEnvironment processingEnv = analysis.getTypeFactory().getProcessingEnv(); TypeMirror lubTypeMirror = TypesUtils.leastUpperBound( this.getUnderlyingType(), other.getUnderlyingType(), processingEnv); return upperBound(other, lubTypeMirror, shouldWiden); } /** * Returns an upper bound of {@code this} and {@code other}. The underlying type of the value * returned is {@code upperBoundTypeMirror}. If {@code shouldWiden} is false, this method * returns the least upper bound of {@code this} and {@code other}. * *

This is the implementation of {@link #leastUpperBound(CFAbstractValue, TypeMirror)}, * {@link #leastUpperBound(CFAbstractValue)}, {@link #widenUpperBound(CFAbstractValue)}, and * {@link #upperBound(CFAbstractValue, boolean)}. Subclasses may override it. * * @param other an abstract value * @param upperBoundTypeMirror the underlying type of the returned value * @param shouldWiden true if the method should perform widening * @return an upper bound of this and {@code other} */ protected V upperBound( @Nullable V other, TypeMirror upperBoundTypeMirror, boolean shouldWiden) { ValueLub valueLub = new ValueLub(shouldWiden); AnnotationMirrorSet lub = valueLub.combineSets( this.getUnderlyingType(), this.getAnnotations(), other.getUnderlyingType(), other.getAnnotations(), canBeMissingAnnotations(upperBoundTypeMirror)); return analysis.createAbstractValue(lub, upperBoundTypeMirror); } /** * Computes the least upper bound or, if {@code shouldWiden} is true, an upper bounds of two * sets of annotations. The computation accounts for sets that are missing annotations in * hierarchies. */ protected class ValueLub extends AnnotationSetCombiner { /** * If true, this class computes an upper bound; if false, this class computes the least * upper bound. */ private final boolean widen; /** * Creates a {@link ValueLub}. * * @param shouldWiden if true, this class computes an upper bound */ public ValueLub(boolean shouldWiden) { this.widen = shouldWiden; } @Override protected @Nullable AnnotationMirror combineTwoAnnotations( AnnotationMirror a, TypeMirror aTypeMirror, AnnotationMirror b, TypeMirror bTypeMirror, AnnotationMirror top) { QualifierHierarchy qualHierarchy = analysis.getTypeFactory().getQualifierHierarchy(); if (widen) { return qualHierarchy.widenedUpperBound(a, b); } else { return qualHierarchy.leastUpperBoundShallow(a, aTypeMirror, b, bTypeMirror); } } @Override protected @Nullable AnnotationMirror combineTwoTypeVars( AnnotatedTypeVariable aAtv, AnnotatedTypeVariable bAtv, AnnotationMirror top, boolean canCombinedSetBeMissingAnnos) { if (canCombinedSetBeMissingAnnos) { // don't add an annotation return null; } else { AnnotationMirror aUB = aAtv.getEffectiveAnnotationInHierarchy(top); AnnotationMirror bUB = bAtv.getEffectiveAnnotationInHierarchy(top); return combineTwoAnnotations( aUB, aAtv.getUnderlyingType(), bUB, bAtv.getUnderlyingType(), top); } } @Override protected @Nullable AnnotationMirror combineAnnotationWithTypeVar( AnnotationMirror annotation, AnnotatedTypeVariable typeVar, AnnotationMirror top, boolean canCombinedSetBeMissingAnnos) { QualifierHierarchy qualHierarchy = analysis.getTypeFactory().getQualifierHierarchy(); TypeMirror typeVarTM = typeVar.getUnderlyingType(); if (canCombinedSetBeMissingAnnos) { // anno is the primary annotation on the use of a type variable. typeVar is a use of // the same type variable that does not have a primary annotation. The lub of the // two type variables is computed as follows. If anno is a subtype (or equal) to the // annotation on the lower bound of typeVar, then typeVar is the lub, so no // annotation is added to lubset. // If anno is a supertype of the annotation on the lower bound of typeVar, then the // lub is typeVar with a primary annotation of lub(anno, upperBound), where // upperBound is the annotation on the upper bound of typeVar. AnnotationMirrorSet lBSet = AnnotatedTypes.findEffectiveLowerBoundAnnotations(qualHierarchy, typeVar); AnnotationMirror lowerBound = qualHierarchy.findAnnotationInHierarchy(lBSet, top); if (qualHierarchy.isSubtypeQualifiersOnly(annotation, lowerBound)) { return null; } else { return combineTwoAnnotations( annotation, typeVarTM, typeVar.getEffectiveAnnotationInHierarchy(top), typeVarTM, top); } } else { return combineTwoAnnotations( annotation, typeVarTM, typeVar.getEffectiveAnnotationInHierarchy(top), typeVarTM, top); } } } /** * Compute the greatest lower bound of two values. * *

Important: This method must fulfill the following contract: * *

    *
  • Does not change {@code this}. *
  • Does not change {@code other}. *
  • Returns a fresh object which is not aliased yet. *
  • Returns an object of the same (dynamic) type as {@code this}, even if the signature is * more permissive. *
  • Is commutative. *
* * @param other another value * @return the greatest lower bound of two values */ public V greatestLowerBound(@Nullable V other) { if (other == null) { @SuppressWarnings("unchecked") V v = (V) this; return v; } ProcessingEnvironment processingEnv = analysis.getTypeFactory().getProcessingEnv(); TypeMirror glbTypeMirror = TypesUtils.greatestLowerBound( this.getUnderlyingType(), other.getUnderlyingType(), processingEnv); ValueGlb valueGlb = new ValueGlb(); AnnotationMirrorSet glb = valueGlb.combineSets( this.getUnderlyingType(), this.getAnnotations(), other.getUnderlyingType(), other.getAnnotations(), canBeMissingAnnotations(glbTypeMirror)); return analysis.createAbstractValue(glb, glbTypeMirror); } /** * Computes the GLB of two sets of annotations. The computation accounts for sets that are * missing annotations in hierarchies. */ protected class ValueGlb extends AnnotationSetCombiner { @Override protected @Nullable AnnotationMirror combineTwoAnnotations( AnnotationMirror a, TypeMirror aTypeMirror, AnnotationMirror b, TypeMirror bTypeMirror, AnnotationMirror top) { QualifierHierarchy qualHierarchy = analysis.getTypeFactory().getQualifierHierarchy(); return qualHierarchy.greatestLowerBoundShallow(a, aTypeMirror, b, bTypeMirror); } @Override protected @Nullable AnnotationMirror combineTwoTypeVars( AnnotatedTypeVariable aAtv, AnnotatedTypeVariable bAtv, AnnotationMirror top, boolean canCombinedSetBeMissingAnnos) { if (canCombinedSetBeMissingAnnos) { // don't add an annotation return null; } else { AnnotationMirror aUB = aAtv.getEffectiveAnnotationInHierarchy(top); AnnotationMirror bUB = bAtv.getEffectiveAnnotationInHierarchy(top); TypeMirror aTM = aAtv.getUnderlyingType(); TypeMirror bTM = bAtv.getUnderlyingType(); return combineTwoAnnotations(aUB, aTM, bUB, bTM, top); } } @Override protected @Nullable AnnotationMirror combineAnnotationWithTypeVar( AnnotationMirror annotation, AnnotatedTypeVariable typeVar, AnnotationMirror top, boolean canCombinedSetBeMissingAnnos) { TypeMirror typeVarTM = typeVar.getUnderlyingType(); if (canCombinedSetBeMissingAnnos) { // anno is the primary annotation on the use of a type variable. typeVar is a use of // the same type variable that does not have a primary annotation. The glb of the // two type variables is computed as follows. If anno is a supertype (or equal) to // the annotation on the upper bound of typeVar, then typeVar is the glb, so no // annotation is added to glbset. // If anno is a subtype of the annotation on the upper bound of typeVar, then the // glb is typeVar with a primary annotation of glb(anno, lowerBound), where // lowerBound is the annotation on the lower bound of typeVar. AnnotationMirror upperBound = typeVar.getEffectiveAnnotationInHierarchy(top); QualifierHierarchy qualHierarchy = analysis.getTypeFactory().getQualifierHierarchy(); if (qualHierarchy.isSubtypeQualifiersOnly(upperBound, annotation)) { return null; } else { AnnotationMirrorSet lBSet = AnnotatedTypes.findEffectiveLowerBoundAnnotations( qualHierarchy, typeVar); AnnotationMirror lowerBound = qualHierarchy.findAnnotationInHierarchy(lBSet, top); return combineTwoAnnotations(annotation, typeVarTM, lowerBound, typeVarTM, top); } } else { return combineTwoAnnotations( annotation, typeVarTM, typeVar.getEffectiveAnnotationInHierarchy(top), typeVarTM, top); } } } /** * Combines two sets of AnnotationMirrors by hierarchy. * *

Subclasses must define how to combine sets by implementing the following methods: * *

    *
  1. {@link #combineTwoAnnotations} *
  2. {@link #combineAnnotationWithTypeVar} *
  3. {@link #combineTwoTypeVars} *
* * If a set is missing an annotation in a hierarchy, and if the combined set can be missing an * annotation, then there must be a TypeVariable for the set that can be used to find * annotations on its bounds. */ protected abstract class AnnotationSetCombiner { /** * Combines the two sets. * * @param aTypeMirror the type mirror associated with {@code aSet} * @param aSet a set of annotation mirrors * @param bTypeMirror the type mirror associated with {@code bSet} * @param bSet a set of annotation mirrors * @param canCombinedSetBeMissingAnnos whether or not the combined set can be missing * annotations * @return the combined sets */ protected AnnotationMirrorSet combineSets( TypeMirror aTypeMirror, AnnotationMirrorSet aSet, TypeMirror bTypeMirror, AnnotationMirrorSet bSet, boolean canCombinedSetBeMissingAnnos) { if (aTypeMirror == null) { throw new NullPointerException("combineSets: aTypeMirror==null"); } if (bTypeMirror == null) { throw new NullPointerException("combineSets: bTypeMirror==null"); } AnnotatedTypeVariable aAtv = getEffectiveTypeVar(aTypeMirror); AnnotatedTypeVariable bAtv = getEffectiveTypeVar(bTypeMirror); QualifierHierarchy qualHierarchy = analysis.getTypeFactory().getQualifierHierarchy(); AnnotationMirrorSet tops = qualHierarchy.getTopAnnotations(); AnnotationMirrorSet combinedSets = new AnnotationMirrorSet(); for (AnnotationMirror top : tops) { AnnotationMirror a = qualHierarchy.findAnnotationInHierarchy(aSet, top); AnnotationMirror b = qualHierarchy.findAnnotationInHierarchy(bSet, top); AnnotationMirror result; if (a != null && b != null) { result = combineTwoAnnotations(a, aTypeMirror, b, bTypeMirror, top); } else if (a != null) { result = combineAnnotationWithTypeVar( a, bAtv, top, canCombinedSetBeMissingAnnos); } else if (b != null) { result = combineAnnotationWithTypeVar( b, aAtv, top, canCombinedSetBeMissingAnnos); } else { result = combineTwoTypeVars(aAtv, bAtv, top, canCombinedSetBeMissingAnnos); } if (result != null) { combinedSets.add(result); } } return combinedSets; } /** * Returns the result of combining the two annotations. This method is called when an * annotation exists in both sets for the hierarchy whose top is {@code top}. * * @param a an annotation in the hierarchy * @param aTypeMirror the type that is annotated by {@code a} * @param b an annotation in the hierarchy * @param bTypeMirror the type that is annotated by {@code b} * @param top the top annotation in the hierarchy * @return the result of combining the two annotations or null if no combination exists */ protected abstract @Nullable AnnotationMirror combineTwoAnnotations( AnnotationMirror a, TypeMirror aTypeMirror, AnnotationMirror b, TypeMirror bTypeMirror, AnnotationMirror top); /** * Returns the primary annotation that result from of combining the two {@link * AnnotatedTypeVariable}. If the result has no primary annotation, {@code null} is * returned. This method is called when no annotation exists in either sets for the * hierarchy whose top is {@code top}. * * @param aAtv a type variable that does not have a primary annotation in {@code top} * hierarchy * @param bAtv a type variable that does not have a primary annotation in {@code top} * hierarchy * @param top the top annotation in the hierarchy * @param canCombinedSetBeMissingAnnos whether or not * @return the result of combining the two type variables, which may be null */ protected abstract @Nullable AnnotationMirror combineTwoTypeVars( AnnotatedTypeVariable aAtv, AnnotatedTypeVariable bAtv, AnnotationMirror top, boolean canCombinedSetBeMissingAnnos); /** * Returns the result of combining {@code annotation} with {@code typeVar}. * *

This is called when an annotation exists for the hierarchy in one set, but not the * other. * * @param annotation an annotation * @param typeVar a type variable that does not have a primary annotation in the hierarchy * @param top the top annotation of the hierarchy * @param canCombinedSetBeMissingAnnos whether or not * @return the result of combining {@code annotation} with {@code typeVar} */ protected abstract @Nullable AnnotationMirror combineAnnotationWithTypeVar( AnnotationMirror annotation, AnnotatedTypeVariable typeVar, AnnotationMirror top, boolean canCombinedSetBeMissingAnnos); } /** * Returns the AnnotatedTypeVariable associated with the given TypeMirror or null. * *

If {@code typeMirror} is a type variable, then the {@link AnnotatedTypeVariable} of its * declaration is returned. If {@code typeMirror} is a wildcard whose extends bounds is a type * variable, then the {@link AnnotatedTypeVariable} for its declaration is returned. Otherwise, * {@code null} is returned. * * @param typeMirror a type mirror * @return the AnnotatedTypeVariable associated with the given TypeMirror or null */ private @Nullable AnnotatedTypeVariable getEffectiveTypeVar(@Nullable TypeMirror typeMirror) { if (typeMirror == null) { return null; } else if (typeMirror.getKind() == TypeKind.WILDCARD) { return getEffectiveTypeVar(((WildcardType) typeMirror).getExtendsBound()); } else if (typeMirror.getKind() == TypeKind.TYPEVAR) { TypeVariable typevar = ((TypeVariable) typeMirror); AnnotatedTypeMirror atm = analysis.getTypeFactory().getAnnotatedType(typevar.asElement()); return (AnnotatedTypeVariable) atm; } else { return null; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy