package org.checkerframework.framework.flow; import java.util.List; import java.util.Set; 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 org.checkerframework.common.basetype.BaseTypeChecker; import org.checkerframework.dataflow.analysis.Analysis; 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.javacutil.AnnotationUtils; import org.checkerframework.javacutil.Pair; /*>>> import org.checkerframework.checker.nullness.qual.*; */ /** * {@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. * * @author Charlie Garrett * @author Stefan Heule */ public abstract class CFAbstractAnalysis< V extends CFAbstractValue<V>, S extends CFAbstractStore<V, S>, T extends CFAbstractTransfer<V, S, T>> extends Analysis<V, S, T> { /** The qualifier hierarchy for which to track annotations. */ protected final QualifierHierarchy qualifierHierarchy; /** The type hierarchy. */ protected final TypeHierarchy typeHierarchy; /** 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 used to do error reporting. TODO: if it's only for error reporting, should it be an * (extended) ErrorHandler? */ protected final SourceChecker checker; /** Initial abstract types for fields. */ protected final List<Pair<VariableElement, V>> fieldValues; public CFAbstractAnalysis( BaseTypeChecker checker, GenericAnnotatedTypeFactory<V, S, T, ? extends CFAbstractAnalysis<V, S, T>> factory, List<Pair<VariableElement, V>> fieldValues, int maxCountBeforeWidening) { super(checker.getProcessingEnvironment(), null, maxCountBeforeWidening); qualifierHierarchy = factory.getQualifierHierarchy(); typeHierarchy = factory.getTypeHierarchy(); this.atypeFactory = factory; this.checker = checker; this.transferFunction = createTransferFunction(); this.fieldValues = fieldValues; } public CFAbstractAnalysis( BaseTypeChecker checker, GenericAnnotatedTypeFactory<V, S, T, ? extends CFAbstractAnalysis<V, S, T>> factory, List<Pair<VariableElement, V>> fieldValues) { this( checker, factory, fieldValues, // 10 is just a nice low default value. Type systems may wish to use or compute a // different value. factory.getQualifierHierarchy().implementsWidening() ? 10 : -1); } public List<Pair<VariableElement, V>> getFieldValues() { return fieldValues; } /** @return the transfer function to be used by the analysis */ public T createTransferFunction() { return atypeFactory.createFlowTransferFunction(this); } /** @return an empty store of the appropriate type */ public abstract S createEmptyStore(boolean sequentialSemantics); /** @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. In that case, * the annotations in the created value are the primary annotations on the extends bound. See * {@link CFAbstractValue} for an explanation. * * @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()); } /** * @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; } /** Perform a visualization of the CFG and analysis info for inspection. */ public void visualizeCFG() { atypeFactory.getCFGVisualizer().visualize(cfg, cfg.getEntryBlock(), this); } /** * Returns an abstract value containing an annotated type with the annotation {@code anno}, and * 'top' for all other hierarchies. The underlying type is {@link Object}. */ 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); } /** @see GenericAnnotatedTypeFactory#getTypeFactoryOfSubchecker(Class) */ @SuppressWarnings("TypeParameterUnusedInFormals") // Intentional abuse public <W extends GenericAnnotatedTypeFactory<?, ?, ?, ?>, U extends BaseTypeChecker> W getTypeFactoryOfSubchecker(Class<U> checkerClass) { return atypeFactory.getTypeFactoryOfSubchecker(checkerClass); } }