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

org.checkerframework.javacutil.TypeKindUtils 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.43.0
Show newest version
package org.checkerframework.javacutil;

import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import org.checkerframework.checker.nullness.qual.Nullable;

/** A utility class that helps with {@link TypeKind}s. */
public final class TypeKindUtils {

  /** This class cannot be instantiated. */
  private TypeKindUtils() {
    throw new AssertionError("Class TypeKindUtils cannot be instantiated.");
  }

  /**
   * Return true if the argument is one of INT, SHORT, BYTE, CHAR, LONG.
   *
   * @param typeKind the TypeKind to inspect
   * @return true if typeKind is a primitive integral type kind
   */
  public static boolean isIntegral(TypeKind typeKind) {
    switch (typeKind) {
      case INT:
      case SHORT:
      case BYTE:
      case CHAR:
      case LONG:
        return true;
      default:
        return false;
    }
  }

  /**
   * Return true if the argument is one of FLOAT, DOUBLE.
   *
   * @param typeKind the TypeKind to inspect
   * @return true if typeKind is a primitive floating point type kind
   */
  public static boolean isFloatingPoint(TypeKind typeKind) {
    switch (typeKind) {
      case FLOAT:
      case DOUBLE:
        return true;
      default:
        return false;
    }
  }

  /**
   * Returns true iff the argument is a primitive numeric type kind.
   *
   * @param typeKind a type kind
   * @return true if the argument is a primitive numeric type kind
   */
  public static boolean isNumeric(TypeKind typeKind) {
    switch (typeKind) {
      case BYTE:
      case CHAR:
      case DOUBLE:
      case FLOAT:
      case INT:
      case LONG:
      case SHORT:
        return true;
      default:
        return false;
    }
  }

  // Cannot create an overload that takes an AnnotatedTypeMirror because the javacutil
  // package must not depend on the framework package.
  /**
   * Given a primitive type, return its kind. Given a boxed primitive type, return the corresponding
   * primitive type kind. Otherwise, return null.
   *
   * @param type a primitive or boxed primitive type
   * @return a primitive type kind, or null
   */
  public static @Nullable TypeKind primitiveOrBoxedToTypeKind(TypeMirror type) {
    final TypeKind typeKind = type.getKind();
    if (typeKind.isPrimitive()) {
      return typeKind;
    }

    if (!(type instanceof DeclaredType)) {
      return null;
    }

    final String typeString = TypesUtils.getQualifiedName((DeclaredType) type).toString();

    switch (typeString) {
      case "java.lang.Byte":
        return TypeKind.BYTE;
      case "java.lang.Boolean":
        return TypeKind.BOOLEAN;
      case "java.lang.Character":
        return TypeKind.CHAR;
      case "java.lang.Double":
        return TypeKind.DOUBLE;
      case "java.lang.Float":
        return TypeKind.FLOAT;
      case "java.lang.Integer":
        return TypeKind.INT;
      case "java.lang.Long":
        return TypeKind.LONG;
      case "java.lang.Short":
        return TypeKind.SHORT;
      default:
        return null;
    }
  }

  // No overload that takes AnnotatedTypeMirror becasue javacutil cannot depend on framework.
  /**
   * Returns the widened numeric type for an arithmetic operation performed on a value of the left
   * type and the right type. Defined in JLS 5.6.2. We return a {@link TypeKind} because creating a
   * {@link TypeMirror} requires a {@link javax.lang.model.util.Types} object from the {@link
   * javax.annotation.processing.ProcessingEnvironment}.
   *
   * @param left a type mirror
   * @param right a type mirror
   * @return the result of widening numeric conversion, or NONE when the conversion cannot be
   *     performed
   */
  public static TypeKind widenedNumericType(TypeMirror left, TypeMirror right) {
    return widenedNumericType(left.getKind(), right.getKind());
  }

  /**
   * Given two type kinds, return the type kind they are widened to, when an arithmetic operation is
   * performed on them. Defined in JLS 5.6.2.
   *
   * @param a a type kind
   * @param b a type kind
   * @return the type kind to which they are widened, when an operation is performed on them
   */
  public static TypeKind widenedNumericType(TypeKind a, TypeKind b) {
    if (!isNumeric(a) || !isNumeric(b)) {
      return TypeKind.NONE;
    }

    if (a == TypeKind.DOUBLE || b == TypeKind.DOUBLE) {
      return TypeKind.DOUBLE;
    }

    if (a == TypeKind.FLOAT || b == TypeKind.FLOAT) {
      return TypeKind.FLOAT;
    }

    if (a == TypeKind.LONG || b == TypeKind.LONG) {
      return TypeKind.LONG;
    }

    return TypeKind.INT;
  }

  /** The type of primitive conversion: narrowing, widening, or same. */
  public enum PrimitiveConversionKind {
    /** The two primitive kinds are the same. */
    SAME,
    /**
     * The conversion is a widening primitive conversion.
     *
     * 

This includes byte to char, even though that is strictly a "widening and narrowing * primitive conversion", according to JLS 5.1.4. */ WIDENING, /** The conversion is a narrowing primitive conversion. */ NARROWING } /** * Return the type of primitive conversion between {@code from} and {@code to}. * *

The narrowing conversions include both short to char and char to short. * * @param from a primitive type * @param to a primitive type * @return the type of primitive conversion between {@code from} and {@code to} */ public static PrimitiveConversionKind getPrimitiveConversionKind(TypeKind from, TypeKind to) { if (from == TypeKind.BOOLEAN && to == TypeKind.BOOLEAN) { return PrimitiveConversionKind.SAME; } assert (isIntegral(from) || isFloatingPoint(from)) && (isIntegral(to) || isFloatingPoint(to)) : "getPrimitiveConversionKind " + from + " " + to; if (from == to) { return PrimitiveConversionKind.SAME; } boolean fromIntegral = isIntegral(from); boolean toFloatingPoint = isFloatingPoint(to); if (fromIntegral && toFloatingPoint) { return PrimitiveConversionKind.WIDENING; } boolean toIntegral = isIntegral(to); boolean fromFloatingPoint = isFloatingPoint(from); if (fromFloatingPoint && toIntegral) { return PrimitiveConversionKind.NARROWING; } if (numBits(from) < numBits(to)) { return PrimitiveConversionKind.WIDENING; } else { // If same number of bits (char to short or short to char), it is a narrowing. return PrimitiveConversionKind.NARROWING; } } /** * Returns the number of bits in the representation of a primitive type. Returns -1 if the type is * not a primitive type. * * @param tk a primitive type kind * @return the number of bits in its representation, or -1 if not integral */ private static int numBits(TypeKind tk) { switch (tk) { case BYTE: return 8; case SHORT: return 16; case CHAR: return 16; case INT: return 32; case LONG: return 64; case FLOAT: return 32; case DOUBLE: return 64; case BOOLEAN: default: return -1; } } /** * Returns the minimum value representable by the given ingetgral primitive type. * * @param tk a primitive type kind * @return the minimum value representable by the given integral primitive type. */ public static long minValue(TypeKind tk) { switch (tk) { case BYTE: return Byte.MIN_VALUE; case SHORT: return Short.MIN_VALUE; case CHAR: return Character.MIN_VALUE; case INT: return Integer.MIN_VALUE; case LONG: return Long.MIN_VALUE; case FLOAT: case DOUBLE: case BOOLEAN: default: throw new BugInCF(tk + " does not have a minimum value"); } } /** * Returns the maximum value representable by the given integral primitive type. * * @param tk a primitive type kind * @return the maximum value representable by the given integral primitive type. */ public static long maxValue(TypeKind tk) { switch (tk) { case BYTE: return Byte.MAX_VALUE; case SHORT: return Short.MAX_VALUE; case CHAR: return Character.MAX_VALUE; case INT: return Integer.MAX_VALUE; case LONG: return Long.MAX_VALUE; case FLOAT: case DOUBLE: case BOOLEAN: default: throw new BugInCF(tk + " does not have a maximum value"); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy