blob: 405577dc6d2ea0d5ec4ad7d3d1576005e3b81575 [file] [log] [blame]
package org.checkerframework.framework.flow;
import java.util.List;
import java.util.Set;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Types;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.common.basetype.BaseTypeChecker;
import org.checkerframework.dataflow.analysis.ForwardAnalysisImpl;
import org.checkerframework.dataflow.cfg.ControlFlowGraph;
import org.checkerframework.framework.source.SourceChecker;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedWildcardType;
import org.checkerframework.framework.type.GenericAnnotatedTypeFactory;
import org.checkerframework.framework.type.QualifierHierarchy;
import org.checkerframework.framework.type.TypeHierarchy;
import org.checkerframework.framework.util.dependenttypes.DependentTypesHelper;
import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.Pair;
/**
* {@link CFAbstractAnalysis} is an extensible org.checkerframework.dataflow analysis for the
* Checker Framework that tracks the annotations using a flow-sensitive analysis. It uses an {@link
* AnnotatedTypeFactory} to provide checker-specific logic how to combine types (e.g., what is the
* type of a string concatenation, given the types of the two operands) and as an abstraction
* function (e.g., determine the annotations on literals).
*
* <p>The purpose of this class is twofold: Firstly, it serves as factory for abstract values,
* stores and the transfer function. Furthermore, it makes it easy for the transfer function and the
* stores to access the {@link AnnotatedTypeFactory}, the qualifier hierarchy, etc.
*/
public abstract class CFAbstractAnalysis<
V extends CFAbstractValue<V>,
S extends CFAbstractStore<V, S>,
T extends CFAbstractTransfer<V, S, T>>
extends ForwardAnalysisImpl<V, S, T> {
/** The qualifier hierarchy for which to track annotations. */
protected final QualifierHierarchy qualifierHierarchy;
/** The type hierarchy. */
protected final TypeHierarchy typeHierarchy;
/**
* The dependent type helper used to standardize both annotations belonging to the type hierarchy,
* and contract expressions.
*/
protected final DependentTypesHelper dependentTypesHelper;
/** A type factory that can provide static type annotations for AST Trees. */
protected final GenericAnnotatedTypeFactory<V, S, T, ? extends CFAbstractAnalysis<V, S, T>>
atypeFactory;
/** A checker that contains command-line arguments and other information. */
protected final SourceChecker checker;
/** Initial abstract types for fields. */
protected final List<Pair<VariableElement, V>> fieldValues;
/** The associated processing environment. */
protected final ProcessingEnvironment env;
/** Instance of the types utility. */
protected final Types types;
/**
* Create a CFAbstractAnalysis.
*
* @param checker a checker that contains command-line arguments and other information
* @param factory an annotated type factory to introduce type and dataflow rules
* @param fieldValues initial abstract types for fields
* @param maxCountBeforeWidening number of times a block can be analyzed before widening
*/
protected CFAbstractAnalysis(
BaseTypeChecker checker,
GenericAnnotatedTypeFactory<V, S, T, ? extends CFAbstractAnalysis<V, S, T>> factory,
List<Pair<VariableElement, V>> fieldValues,
int maxCountBeforeWidening) {
super(maxCountBeforeWidening);
env = checker.getProcessingEnvironment();
types = env.getTypeUtils();
qualifierHierarchy = factory.getQualifierHierarchy();
typeHierarchy = factory.getTypeHierarchy();
dependentTypesHelper = factory.getDependentTypesHelper();
this.atypeFactory = factory;
this.checker = checker;
this.transferFunction = createTransferFunction();
// TODO: remove parameter and set to empty list.
this.fieldValues = fieldValues;
}
protected CFAbstractAnalysis(
BaseTypeChecker checker,
GenericAnnotatedTypeFactory<V, S, T, ? extends CFAbstractAnalysis<V, S, T>> factory,
List<Pair<VariableElement, V>> fieldValues) {
this(
checker,
factory,
fieldValues,
factory.getQualifierHierarchy().numberOfIterationsBeforeWidening());
}
public void performAnalysis(ControlFlowGraph cfg, List<Pair<VariableElement, V>> fieldValues) {
this.fieldValues.clear();
this.fieldValues.addAll(fieldValues);
super.performAnalysis(cfg);
}
public List<Pair<VariableElement, V>> getFieldValues() {
return fieldValues;
}
/**
* Returns the transfer function to be used by the analysis.
*
* @return the transfer function to be used by the analysis
*/
public T createTransferFunction() {
return atypeFactory.createFlowTransferFunction(this);
}
/**
* Returns an empty store of the appropriate type.
*
* @return an empty store of the appropriate type
*/
public abstract S createEmptyStore(boolean sequentialSemantics);
/**
* Returns an identical copy of the store {@code s}.
*
* @return an identical copy of the store {@code s}
*/
public abstract S createCopiedStore(S s);
/**
* Creates an abstract value from the annotated type mirror. The value contains the set of primary
* annotations on the type, unless the type is an AnnotatedWildcardType. For an
* AnnotatedWildcardType, the annotations in the created value are the primary annotations on the
* extends bound. See {@link CFAbstractValue} for an explanation.
*
* @param type the type to convert into an abstract value
* @return an abstract value containing the given annotated {@code type}
*/
public @Nullable V createAbstractValue(AnnotatedTypeMirror type) {
Set<AnnotationMirror> annos;
if (type.getKind() == TypeKind.WILDCARD) {
annos = ((AnnotatedWildcardType) type).getExtendsBound().getAnnotations();
} else {
annos = type.getAnnotations();
}
return createAbstractValue(annos, type.getUnderlyingType());
}
/**
* Returns an abstract value containing the given {@code annotations} and {@code underlyingType}.
* Returns null if the annotation set has missing annotations.
*
* @return an abstract value containing the given {@code annotations} and {@code underlyingType}
*/
public abstract @Nullable V createAbstractValue(
Set<AnnotationMirror> annotations, TypeMirror underlyingType);
/** Default implementation for {@link #createAbstractValue(Set, TypeMirror)}. */
public CFValue defaultCreateAbstractValue(
CFAbstractAnalysis<CFValue, ?, ?> analysis,
Set<AnnotationMirror> annotations,
TypeMirror underlyingType) {
if (!CFAbstractValue.validateSet(annotations, underlyingType, qualifierHierarchy)) {
return null;
}
return new CFValue(analysis, annotations, underlyingType);
}
public TypeHierarchy getTypeHierarchy() {
return typeHierarchy;
}
public GenericAnnotatedTypeFactory<V, S, T, ? extends CFAbstractAnalysis<V, S, T>>
getTypeFactory() {
return atypeFactory;
}
/**
* Returns an abstract value containing an annotated type with the annotation {@code anno}, and
* 'top' for all other hierarchies. The underlying type is {@code underlyingType}.
*/
public V createSingleAnnotationValue(AnnotationMirror anno, TypeMirror underlyingType) {
QualifierHierarchy hierarchy = getTypeFactory().getQualifierHierarchy();
Set<AnnotationMirror> annos = AnnotationUtils.createAnnotationSet();
annos.addAll(hierarchy.getTopAnnotations());
AnnotationMirror f = hierarchy.findAnnotationInSameHierarchy(annos, anno);
annos.remove(f);
annos.add(anno);
return createAbstractValue(annos, underlyingType);
}
/**
* Get the types utility.
*
* @return {@link #types}
*/
public Types getTypes() {
return types;
}
/**
* Get the processing environment.
*
* @return {@link #env}
*/
public ProcessingEnvironment getEnv() {
return env;
}
}