Class ModifiabilityBaseAnnotatedTypeFactory

All Implemented Interfaces:
AnnotationProvider
Direct Known Subclasses:
GrowAnnotatedTypeFactory, IteratorAnnotatedTypeFactory, ReplaceAnnotatedTypeFactory, SeqGrowAnnotatedTypeFactory, ShrinkAnnotatedTypeFactory

public abstract class ModifiabilityBaseAnnotatedTypeFactory extends BaseAnnotatedTypeFactory
Shared annotated type factory logic for the Modifiability sub-checkers.
  • Field Details

    • ITERATOR_POLY_MOD

      protected final AnnotationMirror ITERATOR_POLY_MOD
      The @IteratorPolyMod qualifier.
    • collectionErasure

      protected final TypeMirror collectionErasure
      The erased java.util.Collection type.
    • mapErasure

      protected final TypeMirror mapErasure
      The erased java.util.Map type.
    • mapEntryErasure

      protected final TypeMirror mapEntryErasure
      The erased java.util.Map.Entry type.
    • iteratorErasure

      protected final TypeMirror iteratorErasure
      The erased java.util.Iterator type.
    • listIteratorErasure

      protected final TypeMirror listIteratorErasure
      The erased java.util.ListIterator type.
  • Constructor Details

    • ModifiabilityBaseAnnotatedTypeFactory

      protected ModifiabilityBaseAnnotatedTypeFactory(BaseTypeChecker checker)
      Creates a ModifiabilityBaseAnnotatedTypeFactory.
      Parameters:
      checker - the associated type-checker
  • Method Details

    • erasureOf

      protected final TypeMirror erasureOf(@UnderInitialization(BaseAnnotatedTypeFactory.class) ModifiabilityBaseAnnotatedTypeFactory this, @FullyQualifiedName String canonicalName)
      Returns the erasure of the named type.

      The parameter is @FullyQualifiedName rather than @CanonicalName, which is what Elements.getTypeElement(java.lang.CharSequence) really requires, because the Signature Checker cannot prove that a string literal such as "java.util.Map.Entry" is a canonical name.

      Parameters:
      canonicalName - the canonical name of a type that is always present
      Returns:
      the erasure of the named type
    • topAnnotation

      protected abstract AnnotationMirror topAnnotation()
      Returns the top qualifier of this checker's hierarchy, such as @MaybeGrowable.
      Returns:
      the top qualifier of this checker's hierarchy
    • positiveCapability

      protected abstract AnnotationMirror positiveCapability()
      Returns the positive capability qualifier, such as @Growable.
      Returns:
      the positive capability qualifier
    • negativeCapability

      protected abstract AnnotationMirror negativeCapability()
      Returns the negative capability qualifier, such as @Ungrowable. Only call this method if hasNegativeCapability() returns true.
      Returns:
      the negative capability qualifier
    • polyCapability

      protected abstract AnnotationMirror polyCapability()
      Returns the polymorphic capability qualifier, such as @PolyGrowable.
      Returns:
      the polymorphic capability qualifier
    • hasNegativeCapability

      protected boolean hasNegativeCapability()
      Returns true if this checker's hierarchy contains a negative qualifier, such as @Ungrowable. The Iterator hierarchy does not.
      Returns:
      true if this checker's hierarchy contains a negative qualifier
    • typeLacksCapability

      protected boolean typeLacksCapability(TypeMirror type)
      Returns true if type structurally cannot support this checker's capability, so that @Modifiable and @Unmodifiable weaken to the top qualifier on type. For example, Map.Entry cannot grow.
      Parameters:
      type - the type on which an alias was written; it is an upper bound, so it is never a type variable or a wildcard, and lacksCapability(javax.lang.model.type.TypeMirror, java.util.function.Predicate<javax.lang.model.type.TypeMirror>) has already decomposed intersection types, so it is never an intersection type either
      Returns:
      true if type structurally cannot support this checker's capability
    • typesWithCapability

      protected abstract List<TypeMirror> typesWithCapability()
      Returns erased types whose every subtype has this checker's capability, unless typeLacksCapability(javax.lang.model.type.TypeMirror) holds of the subtype. For example, the Replace Checker's result includes List.

      A type variable or wildcard may be instantiated by any subtype of its upper bound, including a subtype that also implements an unrelated interface. For example, a type variable whose upper bound is AbstractCollection or Serializable may be instantiated by HashSet, which cannot be replaced into. So an alias written on a type variable claims this checker's capability only if the upper bound is a subtype of one of these types.

      Returns:
      erased types whose subtypes have this checker's capability
    • polyLacksCapability

      protected boolean polyLacksCapability(TypeMirror type)
      Returns true if @PolyModifiable weakens to the top qualifier on type, rather than to this checker's polymorphic qualifier. This differs from typeLacksCapability(javax.lang.model.type.TypeMirror) because a polymorphic qualifier may usefully carry a capability that the type itself cannot exercise; for example, Map.Entry carries the replace capability of its map.
      Parameters:
      type - the type on which @PolyModifiable was written; it is an upper bound, so it is never a type variable or a wildcard, and lacksCapability(javax.lang.model.type.TypeMirror, java.util.function.Predicate<javax.lang.model.type.TypeMirror>) has already decomposed intersection types, so it is never an intersection type either
      Returns:
      true if @PolyModifiable weakens to the top qualifier on type
    • expandsModifiabilityAliases

      protected boolean expandsModifiabilityAliases()
      Returns true if this checker's hierarchy is one of the capabilities that the whole-modifiability aliases (@Modifiable, @Unmodifiable, @MaybeModifiable, @UnmodifiableParam, and @PolyModifiable) expand into. The Iterator hierarchy is not: it states what a collection's iterator preserves rather than whether a mutating method throws UnsupportedOperationException.
      Returns:
      true if the whole-modifiability aliases expand into this checker's hierarchy
    • canonicalAnnotation

      public AnnotationMirror canonicalAnnotation(AnnotationMirror annotation, @Nullable TypeMirror tm)
      Expands the whole-modifiability aliases into this hierarchy, with structural weakening only for aliases whose meaning depends on the annotated type.

      @Modifiable and @Unmodifiable claim every component capability, so on a type that structurally cannot exercise this checker's capability, they weaken to the top qualifier; see typeLacksCapability(javax.lang.model.type.TypeMirror). @PolyModifiable weakens under the different condition of polyLacksCapability(javax.lang.model.type.TypeMirror).

      When tm is null, as for an alias written in @DefaultQualifier, no structural weakening is applied.

      A type variable or wildcard is classified by its upper bound, so that, for example, <T extends Deque<String>> has the same capabilities as Deque. In addition, on a type variable or wildcard, @Modifiable and @Unmodifiable weaken to the top qualifier unless the upper bound is a subtype of a type that has this checker's capability; see typesWithCapability(). For example, @Modifiable T for an unbounded T is @MaybeReplaceable, because T may be Set.

      Overrides:
      canonicalAnnotation in class AnnotatedTypeFactory
      Parameters:
      annotation - the qualifier to canonicalize
      tm - the type the qualifier is applied to, or null
      Returns:
      the canonical annotation, which may be the given annotation
    • canonicalAnnotation

      public AnnotationMirror canonicalAnnotation(AnnotationMirror annotation)
      Description copied from class: AnnotatedTypeFactory
      Returns the canonical annotation for the passed annotation. May return its argument.

      This method canonicalAnnotation is called by AnnotatedTypeMirror.addAnnotation(javax.lang.model.element.AnnotationMirror), so it is called for every annotation added to a type.

      This implementation handles when the passed annotation is an alias of another annotation. Subclasses can do additional work.

      Overrides:
      canonicalAnnotation in class AnnotatedTypeFactory
      Parameters:
      annotation - the qualifier to canonicalize
      Returns:
      the canonical annotation, which may be the given annotation
    • refinedIteratorResultBound

      protected @Nullable TypeMirror refinedIteratorResultBound()
      Returns the erased type that the result of an iterator method must be a subtype of for this checker to refine the result, or null if this checker does not refine iterator results. The Shrink Checker refines the result of iterator() and listIterator(); the Grow and Replace Checkers refine only the result of listIterator(), because a plain Iterator can neither grow nor replace.
      Returns:
      the erased upper bound of the iterator results this checker refines, or null
    • methodFromUse

      protected AnnotatedTypeFactory.ParameterizedExecutableType methodFromUse(MethodInvocationTree tree, boolean inferTypeArgs)
      Description copied from class: AnnotatedTypeFactory
      Overrides:
      methodFromUse in class GenericAnnotatedTypeFactory<CFValue,CFStore,CFTransfer,CFAnalysis>
      Parameters:
      tree - a method invocation tree
      inferTypeArgs - true if type arguments should be inferred
      Returns:
      the type of the invoked method, any explicit type arguments, and if inferTypeArgs is true, any inferred type arguments
    • refineReturnTypeForPreservesModifiability

      protected void refineReturnTypeForPreservesModifiability(MethodInvocationTree tree, AnnotatedTypeMirror.AnnotatedExecutableType methodType)
      Refines the return type of a @PreservesModifiability method.

      If the method does not have exactly one formal parameter, which is not a varargs parameter, and a non-void result, then the annotation has no effect.

      Otherwise, if the declared return type has a qualifier other than the top qualifier, that declared qualifier is used. If the first argument has this checker's positive qualifier (for example, @Shrinkable), then so does the return type. For every other first argument, the return type is the top qualifier.

      Such a method cannot be annotated as @Poly*, because a negative (for example, @Unshrinkable) input could yield either a positive or a negative result. It would be imprecise to always use the top qualifier, because passing a positive argument guarantees a positive return type.

      This method is called by all five sub-checkers.

      Parameters:
      tree - an invocation of a @PreservesModifiability method
      methodType - the annotated executable type of the invoked method
    • refineIteratorReturnType

      protected void refineIteratorReturnType(MethodInvocationTree tree, AnnotatedTypeMirror.AnnotatedExecutableType methodType)
      Refines the result of iterator() and listIterator() based on @IteratorPolyMod.

      iterator() and listIterator() cannot be annotated as @PolyModifiable because not all collections preserve the modifiability of their iterators. (For example, CopyOnWriteArrayList has unmodifiable iterators even though the list is modifiable.) Thus, special treatment is needed for iterator methods.

      A declared negative result keeps its declared qualifier, since such an iterator never has the capability. A declared polymorphic result keeps the qualifier that polymorphic resolution gives it. Otherwise, the result qualifier is computed from the receiver: the iterator of a receiver with this checker's negative qualifier also has that negative qualifier, and the iterator of a receiver that has both this checker's positive qualifier and @IteratorPolyMod has the positive qualifier. In every other case the result is the top qualifier.

      A declared positive result is not kept, because it holds only when the receiver has the capability and preserves it. For example, ArrayList declares @Growable ListIterator<E> listIterator(), but an @Ungrowable ArrayList has an @Ungrowable list iterator and a @MaybeGrowable ArrayList has a @MaybeGrowable one.

      This method is called by the Grow, Shrink, and Replace Checkers; see refinedIteratorResultBound().

      Parameters:
      tree - the iterator method invocation
      methodType - the annotated executable type of the invoked method