Class DependentTypesHelper

java.lang.Object
org.checkerframework.framework.util.dependenttypes.DependentTypesHelper
Direct Known Subclasses:
OffsetDependentTypesHelper

public class DependentTypesHelper extends Object
A class that helps checkers use qualifiers that are represented by annotations with Java expression strings. This class performs the following main functions:
  1. Converts the expression strings in an AnnotationMirror am, by creating a new annotation whose Java expression elements are the result of the conversion. See convertAnnotationMirror(StringToJavaExpression, AnnotationMirror), though clients do not call it (they call other methods in this class, which eventually call it). Subclasses can specialize this process by overriding methods in this class. Methods in this class always standardize Java expressions and may additionally viewpoint-adapt or delocalize expressions. Below is an explanation of each kind of conversion.
    • Standardization: the expressions in the annotations are converted such that two expression strings that are equivalent are made to be equal. For example, an instance field f may appear in an expression string as "f" or "this.f"; this class standardizes both strings to "this.f". All dependent type annotations must be standardized so that the implementation of QualifierHierarchy.isSubtypeShallow(AnnotationMirror, TypeMirror, AnnotationMirror, TypeMirror) can assume that two expressions are equivalent if their string representations are equals().
    • Viewpoint-adaption: converts an expression to some use site. For example, in method bodies, formal parameter references such as "#2" are converted to the name of the formal parameter. Another example, is at method call site, "this" is converted to the receiver of the method invocation.
    • Delocalization: removes all expressions with references to local variables that are not parameters and changes parameters to the "#1" syntax.
  2. If any of the conversions above results in an invalid expression, this class changes invalid expression strings to an error string that includes the reason why the expression is invalid. For example, @KeyFor("m") would be changed to @KeyFor("[error for expression: m error: m: identifier not found]") if m is not a valid identifier. This allows subtyping checks to assume that if two strings are equal and not errors, they reference the same valid Java expression.
  3. Checks annotated types for error strings that have been added by this class and issues an error if any are found.

Steps 2 and 3 are separated so that an error is issued only once per invalid expression string rather than every time the expression string is parsed. (The expression string is parsed multiple times because annotated types are created multiple times.)

  • Field Details

    • factory

      protected final AnnotatedTypeFactory factory
      AnnotatedTypeFactory
    • objectTM

      protected final TypeMirror objectTM
      The type mirror for java.lang.Object.
  • Constructor Details

    • DependentTypesHelper

      public DependentTypesHelper(AnnotatedTypeFactory factory)
      Creates a DependentTypesHelper.
      Parameters:
      factory - annotated type factory
  • Method Details

    • hasDependentAnnotations

      public boolean hasDependentAnnotations()
      Returns true if any qualifier in the type system is a dependent type annotation.
      Returns:
      true if any qualifier in the type system is a dependent type annotation
    • createDependentTypesTreeAnnotator

      public TreeAnnotator createDependentTypesTreeAnnotator()
      Creates a TreeAnnotator that viewpoint-adapts dependent type annotations.
      Returns:
      a new TreeAnnotator that viewpoint-adapts dependent type annotations
    • atParameterizedTypeUse

      public void atParameterizedTypeUse(List<AnnotatedTypeParameterBounds> bounds, TypeElement typeUse)
      Viewpoint-adapts the dependent type annotations on the bounds of the type parameters of the declaration of typeUse to typeUse.
      Parameters:
      bounds - annotated types of the bounds of the type parameters; its elements are side-effected by this method (but the list itself is not side-effected)
      typeUse - a use of a type with type parameter bounds bounds
    • atMethodInvocation

      public void atMethodInvocation(AnnotatedTypeMirror.AnnotatedExecutableType methodType, MethodInvocationTree methodInvocationTree)
      Viewpoint-adapts the dependent type annotations in the methodType to the methodInvocationTree.

      methodType has been viewpoint-adapted to the call site, except for any dependent type annotations. This method viewpoint-adapts the dependent type annotations.

      Parameters:
      methodType - type of the method invocation; is side-effected by this method
      methodInvocationTree - use of the method
    • atConstructorInvocation

      public void atConstructorInvocation(AnnotatedTypeMirror.AnnotatedExecutableType constructorType, NewClassTree newClassTree)
      Viewpoint-adapts the dependent type annotations in the constructorType to the newClassTree.

      constructorType has been viewpoint-adapted to the call site, except for any dependent type annotations. This method viewpoint-adapts the dependent type annotations.

      Parameters:
      constructorType - type of the constructor invocation; is side-effected by this method
      newClassTree - invocation of the constructor
    • atFieldAccess

      public void atFieldAccess(AnnotatedTypeMirror type, MemberSelectTree fieldAccess)
      Viewpoint-adapts the Java expressions in annotations written on a field declaration to the use at fieldAccess.
      Parameters:
      type - its type; is side-effected by this method
      fieldAccess - a field access
    • atMethodBody

      public void atMethodBody(AnnotatedTypeMirror atm, MethodTree methodDeclTree)
      Viewpoint-adapts the Java expressions in annotations written on the signature of the method declaration (for example, a return type) to the body of the method. This means the parameter syntax, e.g. "#2", is converted to the names of the parameter.
      Parameters:
      atm - a type at the method signature; is side-effected by this method
      methodDeclTree - a method declaration
    • atTypeDecl

      public void atTypeDecl(AnnotatedTypeMirror type, TypeElement typeElt)
      Standardizes the Java expressions in annotations to a type declaration.
      Parameters:
      type - the type of the type declaration; is side-effected by this method
      typeElt - the element of the type declaration
    • atVariableDeclaration

      public void atVariableDeclaration(AnnotatedTypeMirror type, Tree declarationTree, VariableElement variableElt)
      Standardize the Java expressions in annotations in a variable declaration. Converts the parameter syntax, e.g "#1", to the parameter name.
      Parameters:
      type - the type of the variable declaration; is side-effected by this method
      declarationTree - the variable declaration
      variableElt - the element of the variable declaration
    • atExpression

      public void atExpression(AnnotatedTypeMirror annotatedType, ExpressionTree expressionTree)
      Standardize the Java expressions in annotations in written in the expressionTree. Also, converts the parameter syntax, e.g. "#1", to the parameter name.

      expressionTree must be an expressions which can contain explicitly written annotations, namely a NewClassTree, NewArrayTree, or TypeCastTree. For example, this method standardizes the KeyFor annotation in (@KeyFor("map") String) key .

      Parameters:
      annotatedType - its type; is side-effected by this method
      expressionTree - a NewClassTree, NewArrayTree, or TypeCastTree
    • atLocalVariable

      public void atLocalVariable(AnnotatedTypeMirror type, Element elt)
      Standardize the Java expressions in annotations in a type. Converts the parameter syntax, e.g. "#2", to the parameter name.
      Parameters:
      type - the type to standardize; is side-effected by this method
      elt - the element whose type is type
    • delocalize

      public void delocalize(AnnotatedTypeMirror atm, MethodTree methodDeclTree)
      Viewpoint-adapt all dependent type annotations to the method declaration, methodDeclTree. This method changes occurrences of formal parameter names to the "#2" syntax, and it removes expressions that contain other local variables.

      If a Java expression in atm references local variables (other than formal parameters), the expression is removed from the annotation. This could result in dependent type annotations with empty lists of expressions. If this is a problem, a subclass can override buildAnnotation(AnnotationMirror, Map) to do something besides creating an annotation with a empty list.

      Parameters:
      atm - type to viewpoint-adapt; is side-effected by this method
      methodDeclTree - the method declaration to which the annotations are viewpoint-adapted
    • delocalizeAtCallsite

      public void delocalizeAtCallsite(AnnotatedTypeMirror atm, Tree invocationTree, List<Node> arguments, @Nullable Node receiver, ExecutableElement methodElt)
      Delocalizes dependent type annotations in atm so that they can be placed on the declaration of the given method or constructor being invoked. Used by whole program inference to infer dependent types for method/constructor parameters based on the actual arguments used at call sites.
      Parameters:
      atm - the annotated type mirror to delocalize
      invocationTree - the method or constructor invocation
      arguments - the actual arguments to the method or constructor
      receiver - the actual receiver, if there was one; null if not
      methodElt - the declaration of the method or constructor being invoked
    • convertAnnotatedTypeMirror

      protected void convertAnnotatedTypeMirror(StringToJavaExpression stringToJavaExpr, AnnotatedTypeMirror type)
      Calls convertAnnotationMirror(StringToJavaExpression, AnnotationMirror) on each annotation mirror on type with stringToJavaExpr. And replaces the annotation with the one created by convertAnnotationMirror, if it's not null. If it is null, the original annotation is used. See convertAnnotationMirror(StringToJavaExpression, AnnotationMirror) for more details.
      Parameters:
      stringToJavaExpr - function to convert a string to a JavaExpression
      type - the type that is side-effected by this method
    • convertAnnotationMirror

      public @Nullable AnnotationMirror convertAnnotationMirror(StringToJavaExpression stringToJavaExpr, AnnotationMirror anno)
      Given an annotation anno, this method builds a new annotation with the Java expressions transformed according to stringToJavaExpr. If anno is not a dependent type annotation, null is returned.

      If stringToJavaExpr returns null, then that expression is removed from the returned annotation.

      Instead of overriding this method, subclasses can override the following methods to change the behavior of this class:

      Parameters:
      stringToJavaExpr - function that converts strings to JavaExpressions
      anno - annotation mirror
      Returns:
      an annotation created by applying stringToJavaExpr to all expression strings in anno, or null if there would be no effect
    • transform

      protected @Nullable JavaExpression transform(JavaExpression javaExpr)
      This method is for subclasses to override to change JavaExpressions in some way before they are inserted into new annotations. This method is called after parsing and viewpoint-adaptation have occurred. javaExpr may be a DependentTypesHelper.PassThroughExpression.

      If null is returned then the expression is not added to the new annotation.

      The default implementation returns the argument, but subclasses may override it.

      Parameters:
      javaExpr - a JavaExpression
      Returns:
      a transformed JavaExpression or null if no transformation exists
    • shouldPassThroughExpression

      protected boolean shouldPassThroughExpression(String expression)
      Whether or not expression should be passed to the new annotation unchanged. If this method returns true, the expression is not parsed.

      The default implementation returns true if the expression is an expression error according to DependentTypesError.isExpressionError(String). Subclasses may override this method to add additional logic.

      Parameters:
      expression - an expression string in a dependent types annotation
      Returns:
      whether or not expression should be passed through unchanged to the new annotation
    • buildAnnotation

      protected AnnotationMirror buildAnnotation(AnnotationMirror originalAnno, Map<ExecutableElement,List<JavaExpression>> elementMap)
      Create a new annotation of the same type as originalAnno using the provided elementMap.
      Parameters:
      originalAnno - the annotation passed to convertAnnotationMirror(StringToJavaExpression, AnnotationMirror) (this method is a helper method for convertAnnotationMirror(StringToJavaExpression, AnnotationMirror))
      elementMap - a mapping from element of originalAnno to JavaExpressions
      Returns:
      an annotation created from elementMap
    • createError

      protected org.checkerframework.framework.util.dependenttypes.DependentTypesHelper.PassThroughExpression createError(String expression, JavaExpressionParseUtil.JavaExpressionParseException e)
      Creates a JavaExpression representing the exception thrown when parsing expression.
      Parameters:
      expression - an expression that caused e when parsed
      e - the exception thrown when parsing expression
      Returns:
      a Java expression
    • createError

      protected org.checkerframework.framework.util.dependenttypes.DependentTypesHelper.PassThroughExpression createError(String expression, String error)
      Creates a JavaExpression representing the error caused when parsing expression
      Parameters:
      expression - an expression that caused error when parsed
      error - the error message caused by expression
      Returns:
      a Java expression
    • checkTypeForErrorExpressions

      public void checkTypeForErrorExpressions(AnnotatedTypeMirror atm, Tree errorTree)
      Reports an expression.unparsable error for each Java expression in the given type that is an expression error string.
      Parameters:
      atm - annotated type to check for expression errors
      errorTree - the tree at which to report any found errors
    • reportErrors

      protected void reportErrors(Tree errorTree, List<DependentTypesError> errors)
      Report the given errors as "expression.unparsable".
      Parameters:
      errorTree - where to report the errors
      errors - the errors to report
    • checkAnnotationForErrorExpressions

      public void checkAnnotationForErrorExpressions(AnnotationMirror annotation, Tree errorTree)
      Reports a flowexpr.parse.error error for each Java expression in the given annotation that is an expression error string.
      Parameters:
      annotation - annotation to check
      errorTree - location at which to issue errors
    • checkClassForErrorExpressions

      public void checkClassForErrorExpressions(ClassTree classTree, AnnotatedTypeMirror.AnnotatedDeclaredType type)
      Reports an expression.unparsable error for each Java expression in the given class declaration AnnotatedTypeMirror that is an expression error string. Note that this reports errors in the class declaration itself, not the body or extends/implements clauses.
      Parameters:
      classTree - class to check
      type - annotated type of the class
    • checkMethodForErrorExpressions

      public void checkMethodForErrorExpressions(MethodTree methodDeclTree, AnnotatedTypeMirror.AnnotatedExecutableType type)
      Reports an expression.unparsable error for each Java expression in the method declaration AnnotatedTypeMirror that is an expression error string.
      Parameters:
      methodDeclTree - method to check
      type - annotated type of the method