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

org.checkerframework.common.value.ValueCheckerUtils 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.48.2
Show newest version
package org.checkerframework.common.value;

import com.sun.source.tree.Tree;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.type.TypeMirror;
import org.checkerframework.checker.mustcall.qual.MustCallUnknown;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.common.value.qual.IntRange;
import org.checkerframework.common.value.qual.IntVal;
import org.checkerframework.common.value.qual.StringVal;
import org.checkerframework.common.value.util.NumberUtils;
import org.checkerframework.common.value.util.Range;
import org.checkerframework.dataflow.expression.JavaExpression;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.GenericAnnotatedTypeFactory;
import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.TypeSystemError;
import org.checkerframework.javacutil.TypesUtils;
import org.plumelib.util.CollectionsPlume;

/** Utility methods for the Value Checker. */
public class ValueCheckerUtils {

  /** Do not instantiate. */
  private ValueCheckerUtils() {
    throw new TypeSystemError("do not instantiate");
  }

  /**
   * Get a list of the values of an annotation, and then cast the values to a given type.
   *
   * @param anno the annotation that contains values
   * @param castTo the type that is casted to
   * @param atypeFactory the type factory
   * @return a list of values after the casting
   */
  public static List getValuesCastedToType(
      AnnotationMirror anno, TypeMirror castTo, ValueAnnotatedTypeFactory atypeFactory) {
    return getValuesCastedToType(anno, castTo, false, atypeFactory);
  }

  /**
   * Get a list of the values of an annotation, and then cast the values to a given type.
   *
   * @param anno the annotation that contains values
   * @param castTo the unannotated type that is casted to
   * @param isUnsigned true if the type being casted to is unsigned
   * @param atypeFactory the type factory
   * @return a list of values after the casting
   */
  public static List getValuesCastedToType(
      AnnotationMirror anno,
      TypeMirror castTo,
      boolean isUnsigned,
      ValueAnnotatedTypeFactory atypeFactory) {
    Class castType = TypesUtils.getClassFromType(castTo);
    List values;
    switch (AnnotationUtils.annotationName(anno)) {
      case ValueAnnotatedTypeFactory.DOUBLEVAL_NAME:
        values = convertDoubleVal(anno, castType, castTo, atypeFactory);
        break;
      case ValueAnnotatedTypeFactory.INTVAL_NAME:
        List longs = atypeFactory.getIntValues(anno);
        values = convertIntVal(longs, castType, castTo, isUnsigned);
        break;
      case ValueAnnotatedTypeFactory.INTRANGE_NAME:
        Range range = atypeFactory.getRange(anno);
        List rangeValues = getValuesFromRange(range, Long.class);
        values = convertIntVal(rangeValues, castType, castTo, isUnsigned);
        break;
      case ValueAnnotatedTypeFactory.STRINGVAL_NAME:
        values = convertStringVal(anno, castType, atypeFactory);
        break;
      case ValueAnnotatedTypeFactory.BOOLVAL_NAME:
        values = convertBoolVal(anno, castType, atypeFactory);
        break;
      case ValueAnnotatedTypeFactory.BOTTOMVAL_NAME:
      case ValueAnnotatedTypeFactory.ARRAYLEN_NAME:
        values = Collections.emptyList();
        break;
      default:
        values = null;
    }
    return values;
  }

  /** Get the minimum and maximum of a list and return a range bounded by them. */
  public static @Nullable Range getRangeFromValues(List values) {
    if (values == null) {
      return null;
    } else if (values.isEmpty()) {
      return Range.NOTHING;
    }
    return Range.create(values);
  }

  /**
   * Converts a long value to a boxed numeric type.
   *
   * @param value a long value
   * @param expectedType the boxed numeric type of the result
   * @return {@code value} converted to {@code expectedType} using standard conversion rules
   */
  private static  T convertLongToType(long value, Class expectedType) {
    Object convertedValue;
    if (expectedType == Integer.class) {
      convertedValue = (int) value;
    } else if (expectedType == Short.class) {
      convertedValue = (short) value;
    } else if (expectedType == Byte.class) {
      convertedValue = (byte) value;
    } else if (expectedType == Long.class) {
      convertedValue = value;
    } else if (expectedType == Double.class) {
      convertedValue = (double) value;
    } else if (expectedType == Float.class) {
      convertedValue = (float) value;
    } else if (expectedType == Character.class) {
      convertedValue = (char) value;
    } else {
      throw new UnsupportedOperationException(
          "ValueCheckerUtils: unexpected class: " + expectedType);
    }
    return expectedType.cast(convertedValue);
  }

  /**
   * Get all possible values from the given type and cast them into a boxed primitive type. Returns
   * null if the list would have length greater than {@link ValueAnnotatedTypeFactory#MAX_VALUES}.
   *
   * 

{@code expectedType} must be a boxed type, not a primitive type, because primitive types * cannot be stored in a list. * * @param the type of the values to obtain * @param range the given range * @param expectedType the expected type * @return a list of all the values in the range, or null if there would be more than {@link * ValueAnnotatedTypeFactory#MAX_VALUES} */ public static @Nullable List getValuesFromRange( @Nullable Range range, Class expectedType) { if (range == null || range.isWiderThan(ValueAnnotatedTypeFactory.MAX_VALUES)) { return null; } if (range.isNothing()) { return Collections.emptyList(); } // The subtraction does not overflow, because the width has already been checked, so the // bound difference is less than ValueAnnotatedTypeFactory.MAX_VALUES. long boundDifference = range.to - range.from; // Each value is computed as a sum of the first value and an offset within the range, // to avoid having range.to as an upper bound of the loop. range.to can be Long.MAX_VALUE, // in which case a comparison value <= range.to would be always true. // boundDifference is always much smaller than Long.MAX_VALUE List values = new ArrayList<>((int) boundDifference + 1); for (long offset = 0; offset <= boundDifference; offset++) { long value = range.from + offset; values.add(convertLongToType(value, expectedType)); } return values; } /** * Converts a list of objects to a list of their string representations. * * @param origValues the objects to format * @return a list of the formatted objects */ private static @Nullable List convertToStringVal( List origValues) { if (origValues == null) { return null; } return CollectionsPlume.mapList(Object::toString, origValues); } /** * Convert the {@code value} argument/element of a @BoolVal annotation into a list. * * @param anno a @BoolVal annotation * @param newClass if String.class, the returned list is a {@code List} * @param atypeFactory the type factory, used for obtaining fields/elements from annotations * @return the {@code value} of a @BoolVal annotation, as a {@code List} or a {@code * List} */ private static List convertBoolVal( AnnotationMirror anno, Class newClass, ValueAnnotatedTypeFactory atypeFactory) { List bools = AnnotationUtils.getElementValueArray(anno, atypeFactory.boolValValueElement, Boolean.class); if (newClass == String.class) { return convertToStringVal(bools); } return bools; } /** * Convert the {@code value} argument/element of a {@code @StringVal} annotation into a list. * * @param anno a {@code @StringVal} annotation * @param newClass if char[].class, the returned list is a {@code List} * @param atypeFactory the type factory, used for obtaining fields/elements from annotations * @return the {@code value} of a {@code @StringVal} annotation, as a {@code List} or a * {@code List} */ private static List convertStringVal( AnnotationMirror anno, Class newClass, ValueAnnotatedTypeFactory atypeFactory) { List strings = atypeFactory.getStringValues(anno); if (newClass == char[].class) { return CollectionsPlume.mapList(String::toCharArray, strings); } return strings; } /** * Convert a list of longs to a given type * * @param longs the integral values to convert * @param newClass determines the type of the result * @param newType the type to which to cast, if newClass is numeric * @param isUnsigned if true, treat {@code newType} as unsigned * @return the {@code value} of a {@code @IntVal} annotation, as a {@code List} or a * {@code List} */ private static @Nullable List convertIntVal( List longs, Class newClass, TypeMirror newType, boolean isUnsigned) { if (longs == null) { return null; } if (newClass == String.class) { return convertToStringVal(longs); } else if (newClass == Character.class || newClass == char.class) { return CollectionsPlume.mapList((Long l) -> (char) l.longValue(), longs); } else if (newClass == Boolean.class) { throw new UnsupportedOperationException( "ValueAnnotatedTypeFactory: can't convert integral type to boolean"); } return NumberUtils.castNumbers(newType, isUnsigned, longs); } /** * Convert the {@code value} argument/element of a @StringVal annotation into a list. * * @param anno a {@code @DoubleVal} annotation * @param newClass the component type for the returned list * @param newType the component type for the returned list * @param atypeFactory the type factory, used for obtaining fields/elements from annotations * @return the {@code value} of a {@code @DoubleVal} annotation */ private static @Nullable List convertDoubleVal( AnnotationMirror anno, Class newClass, TypeMirror newType, ValueAnnotatedTypeFactory atypeFactory) { List doubles = atypeFactory.getDoubleValues(anno); if (doubles == null) { return null; } if (newClass == String.class) { return convertToStringVal(doubles); } else if (newClass == Character.class || newClass == char.class) { return CollectionsPlume.mapList((Double l) -> (char) l.doubleValue(), doubles); } else if (newClass == Boolean.class) { throw new UnsupportedOperationException( "ValueAnnotatedTypeFactory: can't convert double to boolean"); } return NumberUtils.castNumbers(newType, doubles); } /** * Gets a list of lengths for a list of string values. * * @param values list of string values * @return list of unique lengths of strings in {@code values} */ public static List getLengthsForStringValues(List values) { List lengths = CollectionsPlume.mapList(String::length, values); return CollectionsPlume.withoutDuplicatesSorted(lengths); } /** * Returns a range representing the possible integral values represented by the passed {@code * AnnotatedTypeMirror}. If the passed {@code AnnotatedTypeMirror} does not contain an {@code * IntRange} annotation or an {@code IntVal} annotation, returns null. */ public static @Nullable Range getPossibleValues( AnnotatedTypeMirror valueType, ValueAnnotatedTypeFactory valueAnnotatedTypeFactory) { if (valueAnnotatedTypeFactory.isIntRange(valueType.getPrimaryAnnotations())) { return valueAnnotatedTypeFactory.getRange(valueType.getPrimaryAnnotation(IntRange.class)); } else { List values = valueAnnotatedTypeFactory.getIntValues(valueType.getPrimaryAnnotation(IntVal.class)); if (values != null) { return Range.create(values); } else { return null; } } } /** * Either returns the exact value of the given tree according to the Constant Value Checker, or * null if the exact value is not known. This method should only be used by clients who need * exactly one value -- such as the LBC's binary operator rules -- and not by those that need to * know whether a valueType belongs to a particular qualifier. */ public static @Nullable Long getExactValue(Tree tree, ValueAnnotatedTypeFactory factory) { AnnotatedTypeMirror valueType = factory.getAnnotatedType(tree); Range possibleValues = getPossibleValues(valueType, factory); if (possibleValues != null && possibleValues.from == possibleValues.to) { return possibleValues.from; } else { return null; } } /** * Returns the exact value of an annotated element according to the Constant Value Checker, or * null if the exact value is not known. * * @param element the element to get the exact value from * @param factory a ValueAnnotatedTypeFactory used for annotation accessing * @return the exact value of the element if it is constant, or null otherwise */ public static @Nullable Long getExactValue(Element element, ValueAnnotatedTypeFactory factory) { AnnotatedTypeMirror valueType = factory.getAnnotatedType(element); Range possibleValues = getPossibleValues(valueType, factory); if (possibleValues != null && possibleValues.from == possibleValues.to) { return possibleValues.from; } else { return null; } } /** * Either returns the exact string value of the given tree according to the Constant Value * Checker, or null if the exact value is not known. This method should only be used by clients * who need exactly one value and not by those that need to know whether a valueType belongs to a * particular qualifier. */ public static @Nullable String getExactStringValue(Tree tree, ValueAnnotatedTypeFactory factory) { AnnotatedTypeMirror valueType = factory.getAnnotatedType(tree); if (valueType.hasPrimaryAnnotation(StringVal.class)) { AnnotationMirror valueAnno = valueType.getPrimaryAnnotation(StringVal.class); List possibleValues = AnnotationUtils.getElementValueArray( valueAnno, factory.stringValValueElement, String.class); if (possibleValues.size() == 1) { return possibleValues.get(0); } } return null; } /** * Finds the minimum value in a Value Checker type. If there is no information (such as when the * list of possible values is empty or null), returns null. Otherwise, returns the smallest value * in the list of possible values. */ public static @Nullable Long getMinValue(Tree tree, ValueAnnotatedTypeFactory factory) { AnnotatedTypeMirror valueType = factory.getAnnotatedType(tree); Range possibleValues = getPossibleValues(valueType, factory); if (possibleValues != null) { return possibleValues.from; } else { return null; } } /** * Finds the maximum value in a Value Checker type. If there is no information (such as when the * list of possible values is empty or null), returns null. Otherwise, returns the smallest value * in the list of possible values. */ public static @Nullable Long getMaxValue(Tree tree, ValueAnnotatedTypeFactory factory) { AnnotatedTypeMirror valueType = factory.getAnnotatedType(tree); Range possibleValues = getPossibleValues(valueType, factory); if (possibleValues != null) { return possibleValues.to; } else { return null; } } /** * Looks up the minlen of a member select tree. The tree must be an access to a sequence length. */ public static @Nullable Integer getMinLenFromTree(Tree tree, ValueAnnotatedTypeFactory valueATF) { AnnotatedTypeMirror minLenType = valueATF.getAnnotatedType(tree); Long min = valueATF.getMinimumIntegralValue(minLenType); if (min == null) { return null; } if (min < 0 || min > Integer.MAX_VALUE) { min = 0L; } return min.intValue(); } /** * Queries the Value Checker to determine if there is a known minimum length for the array * represented by {@code tree}. If not, returns 0. */ public static int getMinLen(Tree tree, ValueAnnotatedTypeFactory valueAnnotatedTypeFactory) { AnnotatedTypeMirror minLenType = valueAnnotatedTypeFactory.getAnnotatedType(tree); return valueAnnotatedTypeFactory.getMinLenValue(minLenType); } /** * Optimize the given JavaExpression. See {@link JavaExpressionOptimizer} for more details. * * @param je the expression to optimize * @param factory the annotated type factory * @return an optimized version of the argument */ public static JavaExpression optimize(JavaExpression je, AnnotatedTypeFactory factory) { ValueAnnotatedTypeFactory vatf = ((GenericAnnotatedTypeFactory) factory) .getTypeFactoryOfSubcheckerOrNull(ValueChecker.class); return new JavaExpressionOptimizer(vatf == null ? factory : vatf).convert(je); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy