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

org.checkerframework.framework.util.element.TypeVarUseApplier 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.44.0
Show newest version
package org.checkerframework.framework.util.element;

import com.sun.tools.javac.code.Attribute;
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import com.sun.tools.javac.code.Symbol.VarSymbol;
import com.sun.tools.javac.code.TargetType;
import com.sun.tools.javac.code.TypeAnnotationPosition;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.type.TypeKind;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedArrayType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable;
import org.checkerframework.framework.type.ElementAnnotationApplier;
import org.checkerframework.framework.util.element.ElementAnnotationUtil.UnexpectedAnnotationLocationException;
import org.checkerframework.javacutil.BugInCF;

/** Apply annotations to the use of a type parameter declaration. */
public class TypeVarUseApplier {

  public static void apply(
      final AnnotatedTypeMirror type, final Element element, final AnnotatedTypeFactory typeFactory)
      throws UnexpectedAnnotationLocationException {
    new TypeVarUseApplier(type, element, typeFactory).extractAndApply();
  }

  private static ElementKind[] acceptedKinds = {
    ElementKind.PARAMETER,
    ElementKind.FIELD,
    ElementKind.LOCAL_VARIABLE,
    ElementKind.RESOURCE_VARIABLE,
    ElementKind.METHOD
  };

  /**
   * Returns true if type is an AnnotatedTypeVariable, or an AnnotatedArrayType with a type variable
   * component, and the element is not a TYPE_PARAMETER.
   *
   * @return true if type is an AnnotatedTypeVariable, or an AnnotatedArrayType with a type variable
   *     component, and the element is not a TYPE_PARAMETER
   */
  public static boolean accepts(AnnotatedTypeMirror type, Element element) {
    return (type instanceof AnnotatedTypeVariable || isGenericArrayType(type))
        && ElementAnnotationUtil.contains(element.getKind(), acceptedKinds);
  }

  private static boolean isGenericArrayType(AnnotatedTypeMirror type) {
    return type instanceof AnnotatedArrayType
        && getNestedComponentType(type) instanceof AnnotatedTypeVariable;
  }

  private static AnnotatedTypeMirror getNestedComponentType(AnnotatedTypeMirror type) {

    AnnotatedTypeMirror componentType = type;
    while (componentType instanceof AnnotatedArrayType) {
      componentType = ((AnnotatedArrayType) componentType).getComponentType();
    }

    return componentType;
  }

  // In order to avoid sprinkling code for type parameter uses all over the various locations
  // uses can show up we also handle generic array types.  T [] myTArr;
  private final AnnotatedArrayType arrayType;

  private final AnnotatedTypeVariable typeVariable;
  private final TypeParameterElement declarationElem;
  private final Element useElem;

  private AnnotatedTypeFactory typeFactory;

  TypeVarUseApplier(
      final AnnotatedTypeMirror type,
      final Element element,
      final AnnotatedTypeFactory typeFactory) {
    if (!accepts(type, element)) {
      throw new BugInCF(
          "TypeParamUseApplier does not accept type/element combination ("
              + " type ( "
              + type
              + " ) element ( "
              + element
              + " ) ");
    }

    if (isGenericArrayType(type)) {
      this.arrayType = (AnnotatedArrayType) type;
      this.typeVariable = (AnnotatedTypeVariable) getNestedComponentType(type);
      this.declarationElem = (TypeParameterElement) typeVariable.getUnderlyingType().asElement();
      this.useElem = element;
      this.typeFactory = typeFactory;

    } else {
      this.arrayType = null;
      this.typeVariable = (AnnotatedTypeVariable) type;
      this.declarationElem = (TypeParameterElement) typeVariable.getUnderlyingType().asElement();
      this.useElem = element;
      this.typeFactory = typeFactory;
    }
  }

  /**
   * Applies the bound annotations from the declaration of the type parameter and then applies the
   * explicit annotations written on the type variable.
   *
   * @throws UnexpectedAnnotationLocationException if invalid location for an annotation was found
   */
  public void extractAndApply() throws UnexpectedAnnotationLocationException {
    ElementAnnotationUtil.addDeclarationAnnotationsFromElement(
        typeVariable, useElem.getAnnotationMirrors());

    // apply declaration annotations
    ElementAnnotationApplier.apply(typeVariable, declarationElem, typeFactory);

    final List annotations = getAnnotations(useElem, declarationElem);

    final List typeVarAnnotations;
    if (arrayType != null) {
      // if the outer-most type is an array type then we want to ensure the outer annotations
      // are not applied as the type variables primary annotation
      typeVarAnnotations = removeComponentAnnotations(arrayType, annotations);
      ElementAnnotationUtil.annotateViaTypeAnnoPosition(arrayType, annotations);

    } else {
      typeVarAnnotations = annotations;
    }

    for (final Attribute.TypeCompound annotation : typeVarAnnotations) {
      typeVariable.replaceAnnotation(annotation);
    }
  }

  private List removeComponentAnnotations(
      final AnnotatedArrayType arrayType, final List annotations) {

    final List componentAnnotations = new ArrayList<>();

    if (arrayType != null) {
      for (int i = 0; i < annotations.size(); ) {
        final Attribute.TypeCompound anno = annotations.get(i);
        if (isBaseComponent(arrayType, anno)) {
          componentAnnotations.add(anno);
          annotations.remove(anno);
        } else {
          i++;
        }
      }
    }

    return componentAnnotations;
  }

  private boolean isBaseComponent(
      final AnnotatedArrayType arrayType, final Attribute.TypeCompound anno) {
    try {
      return ElementAnnotationUtil.getTypeAtLocation(arrayType, anno.getPosition().location)
              .getKind()
          == TypeKind.TYPEVAR;
    } catch (UnexpectedAnnotationLocationException ex) {
      return false;
    }
  }

  /**
   * Depending on what element type the annotations are stored on, the relevant annotations might be
   * stored with different annotation positions. getAnnotations finds the correct annotations by
   * annotation position and element kind and returns them
   */
  private static List getAnnotations(
      final Element useElem, final Element declarationElem) {
    final List annotations;
    switch (useElem.getKind()) {
      case METHOD:
        annotations = getReturnAnnos(useElem);
        break;

      case PARAMETER:
        annotations = getParameterAnnos(useElem);
        break;

      case FIELD:
      case LOCAL_VARIABLE:
      case RESOURCE_VARIABLE:
        annotations = getVariableAnnos(useElem);
        break;

      default:
        throw new BugInCF(
            "TypeVarUseApplier::extractAndApply : "
                + "Unhandled element kind "
                + useElem.getKind()
                + "useElem ( "
                + useElem
                + " ) "
                + "declarationElem ( "
                + declarationElem
                + " ) ");
    }

    return annotations;
  }

  /**
   * Returns annotations on an element that apply to variable declarations.
   *
   * @param variableElem the element whose annotations to check
   * @return annotations on an element that apply to variable declarations
   */
  private static List getVariableAnnos(final Element variableElem) {
    final VarSymbol varSymbol = (VarSymbol) variableElem;
    final List annotations = new ArrayList<>();

    for (Attribute.TypeCompound anno : varSymbol.getRawTypeAttributes()) {

      TypeAnnotationPosition pos = anno.position;
      switch (pos.type) {
        case FIELD:
        case LOCAL_VARIABLE:
        case RESOURCE_VARIABLE:
        case EXCEPTION_PARAMETER:
          annotations.add(anno);
          break;

        default:
      }
    }

    return annotations;
  }

  /**
   * Currently, the metadata for storing annotations (i.e. the Attribute.TypeCompounds) is null for
   * binary-only parameters and type parameters. However, it is present on the method. So in order
   * to ensure that we correctly retrieve the annotations we need to index from the method and
   * retrieve the annotations from its metadata.
   *
   * @return a list of annotations that were found on METHOD_FORMAL_PARAMETERS that match the
   *     parameter index of the input element in the parent methods formal parameter list
   */
  private static List getParameterAnnos(final Element paramElem) {
    final Element enclosingElement = paramElem.getEnclosingElement();
    if (!(enclosingElement instanceof ExecutableElement)) {
      throw new BugInCF(
          "Bad element passed to TypeFromElement.getTypeParameterAnnotationAttributes: "
              + "element: "
              + paramElem
              + " not found in enclosing executable: "
              + enclosingElement);
    }

    final MethodSymbol enclosingMethod = (MethodSymbol) enclosingElement;

    if (enclosingMethod.getKind() != ElementKind.CONSTRUCTOR
        && enclosingMethod.getKind() != ElementKind.METHOD) {
      // Initializer blocks don't have parameters, so there is nothing to do.
      return Collections.emptyList();
    }

    // TODO: for the parameter in a lambda expression, the enclosingMethod isn't
    // the lambda expression. Does this read the correct annotations?

    final int paramIndex = enclosingMethod.getParameters().indexOf(paramElem);
    final List annotations = enclosingMethod.getRawTypeAttributes();

    final List result = new ArrayList<>();
    for (final Attribute.TypeCompound typeAnno : annotations) {
      if (typeAnno.position.type == TargetType.METHOD_FORMAL_PARAMETER) {
        if (typeAnno.position.parameter_index == paramIndex) {
          result.add(typeAnno);
        }
      }
    }

    return result;
  }

  /**
   * Returns the annotations on the return type of the input ExecutableElement.
   *
   * @param methodElem the method whose return type annotations to return
   * @return the annotations on the return type of the input ExecutableElement
   */
  private static List getReturnAnnos(final Element methodElem) {
    if (!(methodElem instanceof ExecutableElement)) {
      throw new BugInCF("Bad element passed to TypeVarUseApplier.getReturnAnnos:" + methodElem);
    }

    final MethodSymbol enclosingMethod = (MethodSymbol) methodElem;

    final List annotations = enclosingMethod.getRawTypeAttributes();
    final List result = new ArrayList<>();
    for (final Attribute.TypeCompound typeAnno : annotations) {
      if (typeAnno.position.type == TargetType.METHOD_RETURN) {
        result.add(typeAnno);
      }
    }

    return result;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy