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

org.checkerframework.common.accumulation.AccumulationValue Maven / Gradle / Ivy

package org.checkerframework.common.accumulation;

import java.util.HashSet;
import java.util.Set;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.dataflow.analysis.TransferResult;
import org.checkerframework.dataflow.cfg.node.Node;
import org.checkerframework.framework.flow.CFAbstractAnalysis;
import org.checkerframework.framework.flow.CFAbstractValue;
import org.checkerframework.javacutil.AnnotationMirrorSet;

/**
 * AccumulationValue holds additional information about accumulated facts ("values", not to be
 * confused with "Value" in the name of this class) that cannot be stored in the accumulation type,
 * because they are not a refinement of that type. This situation occurs for type variables and
 * wildcards, for which calling {@link AccumulationTransfer#accumulate(Node, TransferResult,
 * String...)} would otherwise have no effect (since the types are invariant: T is not a supertype
 * of Accumulator(a) T unless both bounds of T are supertypes of Accumulator(a)). This enables an
 * accumulation checker (or, typically, a client of that accumulation checker) to resolve
 * accumulated facts even on types that are type variables. For example, the Resource Leak Checker
 * uses this facility to check that calls to close() on variables whose type is a type variable have
 * actually occurred, such as in this example:
 *
 * 

 *   public static <T extends java.io.Closeable> void close(
 *       @Owning @MustCall("close") T value) throws Exception {
 *     value.close();
 *   }
 * 
*/ public class AccumulationValue extends CFAbstractValue { /** * If the underlying type is a type variable or a wildcard, then this is a set of accumulated * values. Otherwise, it is null. */ private @Nullable Set accumulatedValues = null; /** * Creates a new CFAbstractValue. * * @param analysis the analysis class this value belongs to * @param annotations the annotations in this abstract value * @param underlyingType the underlying (Java) type in this abstract value */ protected AccumulationValue( CFAbstractAnalysis analysis, AnnotationMirrorSet annotations, TypeMirror underlyingType) { super(analysis, annotations, underlyingType); if (underlyingType.getKind() == TypeKind.TYPEVAR || underlyingType.getKind() == TypeKind.WILDCARD) { AccumulationAnnotatedTypeFactory typeFactory = (AccumulationAnnotatedTypeFactory) analysis.getTypeFactory(); AnnotationMirror accumulator = null; for (AnnotationMirror anm : annotations) { if (typeFactory.isAccumulatorAnnotation(anm)) { accumulator = anm; break; } } if (accumulator != null) { accumulatedValues = new HashSet<>(typeFactory.getAccumulatedValues(accumulator)); } } } /** * If the underlying type is a type variable or a wildcard, then this is a set of accumulated * values. Otherwise, it is null. * * @return the set (this is not a copy of the set, but an alias) */ public @Nullable Set getAccumulatedValues() { return accumulatedValues; } @Override protected AccumulationValue upperBound( @Nullable AccumulationValue other, TypeMirror upperBoundTypeMirror, boolean shouldWiden) { AccumulationValue lub = super.upperBound(other, upperBoundTypeMirror, shouldWiden); if (other == null || other.accumulatedValues == null || this.accumulatedValues == null) { return lub; } // Lub the accumulatedValues by intersecting the lists as if they were sets. lub.accumulatedValues = new HashSet<>(this.accumulatedValues.size()); lub.accumulatedValues.addAll(this.accumulatedValues); lub.accumulatedValues.retainAll(other.accumulatedValues); if (lub.accumulatedValues.isEmpty()) { lub.accumulatedValues = null; } return lub; } @Override public @Nullable AccumulationValue mostSpecific( AccumulationValue other, AccumulationValue backup) { if (other == null) { return this; } AccumulationValue mostSpecific = super.mostSpecific(other, backup); if (mostSpecific != null) { mostSpecific.addAccumulatedValues(this.accumulatedValues); mostSpecific.addAccumulatedValues(other.accumulatedValues); return mostSpecific; } // mostSpecific is null if the two types are not comparable. This is normally // because one of this or other is a type variable and annotations is empty, but the // other annotations are not empty. In this case, copy the accumulatedValues to the // value with no annotations and return it as most specific. if (other.getAnnotations().isEmpty()) { mostSpecific = new AccumulationValue( analysis, AnnotationMirrorSet.emptySet(), other.getUnderlyingType()); mostSpecific.addAccumulatedValues(this.accumulatedValues); mostSpecific.addAccumulatedValues(other.accumulatedValues); return mostSpecific; } else if (this.getAnnotations().isEmpty()) { mostSpecific = new AccumulationValue( analysis, AnnotationMirrorSet.emptySet(), other.getUnderlyingType()); mostSpecific.addAccumulatedValues(this.accumulatedValues); mostSpecific.addAccumulatedValues(other.accumulatedValues); return mostSpecific; } return null; } /** * Merges its argument into the {@link #accumulatedValues} field of this. * * @param newAccumulatedValues a new list of accumulated values */ private void addAccumulatedValues(Set newAccumulatedValues) { if (newAccumulatedValues == null || newAccumulatedValues.isEmpty()) { return; } if (accumulatedValues == null) { accumulatedValues = new HashSet<>(newAccumulatedValues.size()); } accumulatedValues.addAll(newAccumulatedValues); } @Override public String toString() { String superToString = super.toString(); // remove last '}' superToString = superToString.substring(0, superToString.length() - 1); return superToString + ", " + "[" + (accumulatedValues == null ? "null" : String.join(", ", accumulatedValues.toArray(new String[0]))) + "] }"; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy