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

checker.src.org.checkerframework.checker.nullness.NullnessAnnotatedTypeFactory 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.42.0
Show newest version
package org.checkerframework.checker.nullness;

import org.checkerframework.checker.initialization.InitializationAnnotatedTypeFactory;
import org.checkerframework.checker.initialization.qual.FBCBottom;
import org.checkerframework.checker.initialization.qual.Initialized;
import org.checkerframework.checker.initialization.qual.UnderInitialization;
import org.checkerframework.checker.initialization.qual.UnknownInitialization;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.NonRaw;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.checker.nullness.qual.PolyNull;
import org.checkerframework.checker.nullness.qual.Raw;
import org.checkerframework.common.basetype.BaseTypeChecker;
import org.checkerframework.framework.flow.CFAbstractAnalysis;
import org.checkerframework.framework.qual.PolyAll;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeFormatter;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType;
import org.checkerframework.framework.type.GeneralAnnotatedTypeFactory;
import org.checkerframework.framework.type.QualifierHierarchy;
import org.checkerframework.framework.type.treeannotator.ImplicitsTreeAnnotator;
import org.checkerframework.framework.type.treeannotator.ListTreeAnnotator;
import org.checkerframework.framework.type.treeannotator.PropagationTreeAnnotator;
import org.checkerframework.framework.type.treeannotator.TreeAnnotator;
import org.checkerframework.framework.type.typeannotator.ImplicitsTypeAnnotator;
import org.checkerframework.framework.type.typeannotator.ListTypeAnnotator;
import org.checkerframework.framework.type.typeannotator.PropagationTypeAnnotator;
import org.checkerframework.framework.type.typeannotator.TypeAnnotator;
import org.checkerframework.framework.util.DependentTypes;
import org.checkerframework.framework.util.MultiGraphQualifierHierarchy.MultiGraphFactory;
import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.ElementUtils;
import org.checkerframework.javacutil.InternalUtils;
import org.checkerframework.javacutil.Pair;
import org.checkerframework.javacutil.TreeUtils;
import org.checkerframework.javacutil.TypesUtils;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.Elements;

import com.sun.source.tree.BinaryTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.CompoundAssignmentTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.NewClassTree;
import com.sun.source.tree.ReturnTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.UnaryTree;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreePath;

/**
 * The annotated type factory for the nullness type-system.
 */
public class NullnessAnnotatedTypeFactory
    extends InitializationAnnotatedTypeFactory {

    /** Annotation constants */
    protected final AnnotationMirror NONNULL, NULLABLE, POLYNULL, MONOTONIC_NONNULL;

    /** Dependent types instance. */
    protected final DependentTypes dependentTypes;

    protected final SystemGetPropertyHandler systemGetPropertyHandler;
    protected final CollectionToArrayHeuristics collectionToArrayHeuristics;

    /**
     * Factory for arbitrary qualifiers, used for declarations and "unused"
     * qualifier.
     */
    protected final GeneralAnnotatedTypeFactory generalFactory;

    /** Cache for the nullness annotations */
    protected final Set> nullnessAnnos;


    @SuppressWarnings("deprecation") // aliasing to deprecated annotation
    public NullnessAnnotatedTypeFactory(BaseTypeChecker checker, boolean useFbc) {
        super(checker, useFbc);

        NONNULL = AnnotationUtils.fromClass(elements, NonNull.class);
        NULLABLE = AnnotationUtils.fromClass(elements, Nullable.class);
        POLYNULL = AnnotationUtils.fromClass(elements, PolyNull.class);
        MONOTONIC_NONNULL = AnnotationUtils.fromClass(elements, MonotonicNonNull.class);

        Set> tempNullnessAnnos = new LinkedHashSet<>();
        tempNullnessAnnos.add(NonNull.class);
        tempNullnessAnnos.add(MonotonicNonNull.class);
        tempNullnessAnnos.add(Nullable.class);
        tempNullnessAnnos.add(PolyNull.class);
        tempNullnessAnnos.add(PolyAll.class);
        nullnessAnnos = Collections.unmodifiableSet(tempNullnessAnnos);

        addAliasedAnnotation(org.checkerframework.checker.nullness.qual.LazyNonNull.class, MONOTONIC_NONNULL);

        // If you update the following, also update ../../../manual/nullness-checker.tex .
        // Aliases for @Nonnull:
        addAliasedAnnotation(com.sun.istack.internal.NotNull.class, NONNULL);
        addAliasedAnnotation(edu.umd.cs.findbugs.annotations.NonNull.class, NONNULL);
        addAliasedAnnotation(javax.annotation.Nonnull.class, NONNULL);
        addAliasedAnnotation(javax.validation.constraints.NotNull.class, NONNULL);
        addAliasedAnnotation(lombok.NonNull.class, NONNULL);
        addAliasedAnnotation(org.eclipse.jdt.annotation.NonNull.class, NONNULL);
        addAliasedAnnotation(org.eclipse.jgit.annotations.NonNull.class, NONNULL);
        addAliasedAnnotation(org.jetbrains.annotations.NotNull.class, NONNULL);
        addAliasedAnnotation(org.netbeans.api.annotations.common.NonNull.class, NONNULL);
        addAliasedAnnotation(org.jmlspecs.annotation.NonNull.class, NONNULL);
        addAliasedAnnotation(android.annotation.NonNull.class, NONNULL);
        addAliasedAnnotation(android.support.annotation.NonNull.class, NONNULL);

        // Aliases for @Nullable:
        addAliasedAnnotation(com.sun.istack.internal.Nullable.class, NULLABLE);
        addAliasedAnnotation(edu.umd.cs.findbugs.annotations.CheckForNull.class, NULLABLE);
        addAliasedAnnotation(edu.umd.cs.findbugs.annotations.Nullable.class, NULLABLE);
        addAliasedAnnotation(edu.umd.cs.findbugs.annotations.UnknownNullness.class, NULLABLE);
        addAliasedAnnotation(javax.annotation.CheckForNull.class, NULLABLE);
        addAliasedAnnotation(javax.annotation.Nullable.class, NULLABLE);
        addAliasedAnnotation(org.eclipse.jdt.annotation.Nullable.class, NULLABLE);
        addAliasedAnnotation(org.eclipse.jgit.annotations.Nullable.class, NULLABLE);
        addAliasedAnnotation(org.jetbrains.annotations.Nullable.class, NULLABLE);
        addAliasedAnnotation(org.netbeans.api.annotations.common.CheckForNull.class, NULLABLE);
        addAliasedAnnotation(org.netbeans.api.annotations.common.NullAllowed.class, NULLABLE);
        addAliasedAnnotation(org.netbeans.api.annotations.common.NullUnknown.class, NULLABLE);
        addAliasedAnnotation(org.jmlspecs.annotation.Nullable.class, NULLABLE);
        addAliasedAnnotation(android.annotation.Nullable.class, NULLABLE);
        addAliasedAnnotation(android.support.annotation.Nullable.class, NULLABLE);

        // Add compatibility annotations:
        addAliasedAnnotation(org.checkerframework.checker.nullness.compatqual.NullableDecl.class, NULLABLE);
        addAliasedAnnotation(org.checkerframework.checker.nullness.compatqual.PolyNullDecl.class, POLYNULL);
        addAliasedAnnotation(org.checkerframework.checker.nullness.compatqual.NonNullDecl.class, NONNULL);
        addAliasedAnnotation(org.checkerframework.checker.nullness.compatqual.MonotonicNonNullDecl.class, MONOTONIC_NONNULL);
        addAliasedAnnotation(org.checkerframework.checker.nullness.compatqual.NullableType.class, NULLABLE);
        addAliasedAnnotation(org.checkerframework.checker.nullness.compatqual.PolyNullType.class, POLYNULL);
        addAliasedAnnotation(org.checkerframework.checker.nullness.compatqual.NonNullType.class, NONNULL);
        addAliasedAnnotation(org.checkerframework.checker.nullness.compatqual.MonotonicNonNullType.class, MONOTONIC_NONNULL);

        // TODO: These heuristics are just here temporarily. They all either
        // need to be replaced, or carefully checked for correctness.
        generalFactory = new GeneralAnnotatedTypeFactory(checker);
        // Alias the same generalFactory below and ensure that setRoot updates it.
        dependentTypes = new DependentTypes(checker, generalFactory);

        systemGetPropertyHandler = new SystemGetPropertyHandler(processingEnv, this);

        postInit();

        // do this last, as it might use the factory again.
        this.collectionToArrayHeuristics = new CollectionToArrayHeuristics(
                processingEnv, this);
    }

    @Override
    protected Set> createSupportedTypeQualifiers() {
        // NullnessATF is used by both NullnessChecker and NullnessRawnessChecker, load the correct set of qualifiers here
        AbstractNullnessChecker ckr = (AbstractNullnessChecker) checker;
        // if useFbc is true, then it is the NullnessChecker
        if (ckr.useFbc) {
            return Collections.unmodifiableSet(
                    new LinkedHashSet>(
                            Arrays.asList(Nullable.class, MonotonicNonNull.class, NonNull.class, UnderInitialization.class,
                                    Initialized.class, UnknownInitialization.class, FBCBottom.class, PolyNull.class, PolyAll.class)));
        }
        // otherwise, it is the NullnessRawnessChecker
        else {
            return Collections.unmodifiableSet(
                    new LinkedHashSet>(
                            Arrays.asList(Nullable.class, MonotonicNonNull.class, NonNull.class, NonRaw.class, Raw.class,
                                    // PolyRaw.class, //TODO: support PolyRaw in the future
                                    PolyNull.class, PolyAll.class)));
        }
    }

    @Override
    public void setRoot(CompilationUnitTree root) {
        generalFactory.setRoot(root);
        super.setRoot(root);
    }

    // handle dependent types
    @Override
    protected void addComputedTypeAnnotations(Tree tree, AnnotatedTypeMirror type, boolean useFlow) {
        super.addComputedTypeAnnotations(tree, type, useFlow);
        dependentTypes.handle(tree, type);
    }

    /**
     * For types of left-hand side of an assignment, this method replaces {@link PolyNull} or
     * {@link PolyAll} with {@link Nullable} if the org.checkerframework.dataflow analysis
     * has determined that this is allowed soundly.
     * For example:
     *
     * 
 @PolyNull String foo(@PolyNull String param) {
     *    if (param == null) {
     *        //  @PolyNull is really @Nullable, so change
     *        // the type of param to @Nullable.
     *        param = null;
     *    }
     *    return param;
     * }
     * 
* * @param lhsType type to replace whose polymorphic qualifier will be replaced * @param context tree used to get dataflow value */ protected void replacePolyQualifier(AnnotatedTypeMirror lhsType, Tree context) { if (lhsType.hasAnnotation(PolyNull.class) || lhsType.hasAnnotation(PolyAll.class)) { NullnessValue inferred = getInferredValueFor(context); if (inferred != null && inferred.isPolyNullNull) { lhsType.replaceAnnotation(NULLABLE); } } } // handle dependent types @Override public Pair> constructorFromUse( NewClassTree tree) { Pair> fromUse = super.constructorFromUse(tree); AnnotatedExecutableType constructor = fromUse.first; dependentTypes.handleConstructor(tree, generalFactory.getAnnotatedType(tree), constructor); return fromUse; } @Override public List getUninitializedInvariantFields( NullnessStore store, TreePath path, boolean isStatic, List receiverAnnotations) { List candidates = super.getUninitializedInvariantFields(store, path, isStatic, receiverAnnotations); List result = new ArrayList<>(); for (VariableTree c : candidates) { AnnotatedTypeMirror type = getAnnotatedType(c); boolean isPrimitive = TypesUtils.isPrimitive(type.getUnderlyingType()); if (!isPrimitive) { // primitives do not need to be initialized result.add(c); } } return result; } @Override protected NullnessAnalysis createFlowAnalysis(List> fieldValues) { return new NullnessAnalysis(checker, this, fieldValues); } @Override public NullnessTransfer createFlowTransferFunction(CFAbstractAnalysis analysis) { return new NullnessTransfer((NullnessAnalysis) analysis); } /** * @return an AnnotatedTypeFormatter that does not print the qualifiers on null literals */ @Override protected AnnotatedTypeFormatter createAnnotatedTypeFormatter() { return new NullnessAnnotatedTypeFormatter(checker.hasOption("printVerboseGenerics"), checker.hasOption("printAllQualifiers")); } @Override public Pair> methodFromUse( MethodInvocationTree tree) { Pair> mfuPair = super .methodFromUse(tree); AnnotatedExecutableType method = mfuPair.first; systemGetPropertyHandler.handle(tree, method); collectionToArrayHeuristics.handle(tree, method); return mfuPair; } @Override public AnnotatedTypeMirror getMethodReturnType(MethodTree m, ReturnTree r) { AnnotatedTypeMirror result = super.getMethodReturnType(m, r); replacePolyQualifier(result, r); return result; } @Override protected TypeAnnotator createTypeAnnotator() { ImplicitsTypeAnnotator implicitsTypeAnnotator = new ImplicitsTypeAnnotator(this); implicitsTypeAnnotator.addTypeClass(AnnotatedTypeMirror.AnnotatedNoType.class, NONNULL); implicitsTypeAnnotator.addTypeClass(AnnotatedTypeMirror.AnnotatedPrimitiveType.class, NONNULL); return new ListTypeAnnotator( new PropagationTypeAnnotator(this), implicitsTypeAnnotator, new NullnessTypeAnnotator(this), new CommitmentTypeAnnotator(this) ); } @Override protected TreeAnnotator createTreeAnnotator() { ImplicitsTreeAnnotator implicitsTreeAnnotator = new ImplicitsTreeAnnotator(this); implicitsTreeAnnotator.addTreeKind(Tree.Kind.NEW_CLASS, NONNULL); implicitsTreeAnnotator.addTreeKind(Tree.Kind.NEW_ARRAY, NONNULL); return new ListTreeAnnotator( // DebugListTreeAnnotator(new Tree.Kind[] {Tree.Kind.CONDITIONAL_EXPRESSION}, new NullnessPropagationAnnotator(this), implicitsTreeAnnotator, new NullnessTreeAnnotator(this), new CommitmentTreeAnnotator(this) ); } /** * If the element is {@link NonNull} when used in a static member access, * modifies the element's type (by adding {@link NonNull}). * * @param elt * the element being accessed * @param type * the type of the element {@code elt} */ private void annotateIfStatic(Element elt, AnnotatedTypeMirror type) { if (elt == null) { return; } if (elt.getKind().isClass() || elt.getKind().isInterface() // Workaround for System.{out,in,err} issue: assume all static // fields in java.lang.System are nonnull. || isSystemField(elt)) { type.replaceAnnotation(NONNULL); } } private static boolean isSystemField(Element elt) { if (!elt.getKind().isField()) { return false; } if (!ElementUtils.isStatic(elt) || !ElementUtils.isFinal(elt)) { return false; } VariableElement var = (VariableElement) elt; // Heuristic: if we have a static final field in a system package, // treat it as NonNull (many like Boolean.TYPE and System.out // have constant value null but are set by the VM). boolean inJavaPackage = ElementUtils.getQualifiedClassName(var) .toString().startsWith("java."); return (var.getConstantValue() != null || var.getSimpleName().contentEquals("class") || inJavaPackage); } /** * Nullness doesn't call propagation on binary and unary because * the result is always @Initialized (the default qualifier). * * Would this be valid to move into CommitmentTreeAnnotator. */ protected class NullnessPropagationAnnotator extends PropagationTreeAnnotator { public NullnessPropagationAnnotator(AnnotatedTypeFactory atypeFactory) { super(atypeFactory); } @Override public Void visitBinary(BinaryTree node, AnnotatedTypeMirror type) { return null; } @Override public Void visitUnary(UnaryTree node, AnnotatedTypeMirror type) { return null; } } protected class NullnessTreeAnnotator extends TreeAnnotator /*extends InitializationAnnotatedTypeFactory.CommitmentTreeAnnotator*/ { public NullnessTreeAnnotator(NullnessAnnotatedTypeFactory atypeFactory) { super(atypeFactory); } @Override public Void visitMemberSelect(MemberSelectTree node, AnnotatedTypeMirror type) { Element elt = TreeUtils.elementFromUse(node); assert elt != null; // case 8: class in static member access annotateIfStatic(elt, type); return null; } @Override public Void visitVariable(VariableTree node, AnnotatedTypeMirror type) { Element elt = InternalUtils.symbol(node); if (elt.getKind() == ElementKind.EXCEPTION_PARAMETER) { if (!type.isAnnotatedInHierarchy(NONNULL)) { // case 9. exception parameter type.addAnnotation(NONNULL); } } return null; } @Override public Void visitIdentifier(IdentifierTree node, AnnotatedTypeMirror type) { Element elt = TreeUtils.elementFromUse(node); assert elt != null; // case 8. static method access annotateIfStatic(elt, type); if (elt.getKind() == ElementKind.EXCEPTION_PARAMETER) { // TODO: It's surprising that we have to do this in // both visitVariable and visitIdentifier. This should // already be handled by applying the defaults anyway. // case 9. exception parameter type.replaceAnnotation(NONNULL); } return null; } // The result of a binary operation is always non-null. @Override public Void visitBinary(BinaryTree node, AnnotatedTypeMirror type) { type.replaceAnnotation(NONNULL); return null; } // The result of a compound operation is always non-null. @Override public Void visitCompoundAssignment(CompoundAssignmentTree node, AnnotatedTypeMirror type) { type.replaceAnnotation(NONNULL); // Commitment will run after for initialization defaults return null; } // The result of a unary operation is always non-null. @Override public Void visitUnary(UnaryTree node, AnnotatedTypeMirror type) { type.replaceAnnotation(NONNULL); return null; } // The result of newly allocated structures is always non-null. @Override public Void visitNewClass(NewClassTree node, AnnotatedTypeMirror type) { type.replaceAnnotation(NONNULL); return null; } } protected class NullnessTypeAnnotator extends InitializationAnnotatedTypeFactory.CommitmentTypeAnnotator { public NullnessTypeAnnotator(InitializationAnnotatedTypeFactory atypeFactory) { super(atypeFactory); } } /** * @return the list of annotations of the non-null type system */ public Set> getNullnessAnnotations() { return nullnessAnnos; } @Override public Set> getInvalidConstructorReturnTypeAnnotations() { Set> l = new HashSet<>( super.getInvalidConstructorReturnTypeAnnotations()); l.addAll(getNullnessAnnotations()); return l; } @Override public AnnotationMirror getFieldInvariantAnnotation() { Elements elements = processingEnv.getElementUtils(); return AnnotationUtils.fromClass(elements, NonNull.class); } @Override public QualifierHierarchy createQualifierHierarchy(MultiGraphFactory factory) { return new NullnessQualifierHierarchy(factory, (Object[]) null); } protected class NullnessQualifierHierarchy extends InitializationQualifierHierarchy { public NullnessQualifierHierarchy(MultiGraphFactory f, Object[] arg) { super(f, arg); } @Override public boolean isSubtype(AnnotationMirror rhs, AnnotationMirror lhs) { if (isInitializationAnnotation(rhs) || isInitializationAnnotation(lhs)) { return this.isSubtypeInitialization(rhs, lhs); } return super.isSubtype(rhs, lhs); } @Override public AnnotationMirror leastUpperBound(AnnotationMirror a1, AnnotationMirror a2) { if (isInitializationAnnotation(a1) || isInitializationAnnotation(a2)) { return this.leastUpperBoundInitialization(a1, a2); } return super.leastUpperBound(a1, a2); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy