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

org.checkerframework.framework.flow.CFCFGBuilder 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.44.0
Show newest version
package org.checkerframework.framework.flow;

import com.sun.source.tree.AssertTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.VariableTree;
import java.util.Collection;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import org.checkerframework.common.basetype.BaseTypeChecker;
import org.checkerframework.dataflow.cfg.ControlFlowGraph;
import org.checkerframework.dataflow.cfg.UnderlyingAST;
import org.checkerframework.dataflow.cfg.builder.CFGBuilder;
import org.checkerframework.dataflow.cfg.builder.CFGTranslationPhaseOne;
import org.checkerframework.dataflow.cfg.builder.CFGTranslationPhaseThree;
import org.checkerframework.dataflow.cfg.builder.CFGTranslationPhaseTwo;
import org.checkerframework.dataflow.cfg.builder.PhaseOneResult;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.GenericAnnotatedTypeFactory;
import org.checkerframework.javacutil.ElementUtils;
import org.checkerframework.javacutil.TreePathUtil;
import org.checkerframework.javacutil.TreeUtils;
import org.checkerframework.javacutil.UserError;

/**
 * A control-flow graph builder (see {@link CFGBuilder}) that knows about the Checker Framework
 * annotations and their representation as {@link AnnotatedTypeMirror}s.
 */
public class CFCFGBuilder extends CFGBuilder {
  /** This class should never be instantiated. Protected to still allow subclasses. */
  protected CFCFGBuilder() {}

  /** Build the control flow graph of some code. */
  public static ControlFlowGraph build(
      CompilationUnitTree root,
      UnderlyingAST underlyingAST,
      BaseTypeChecker checker,
      AnnotatedTypeFactory factory,
      ProcessingEnvironment env) {
    boolean assumeAssertionsEnabled = checker.hasOption("assumeAssertionsAreEnabled");
    boolean assumeAssertionsDisabled = checker.hasOption("assumeAssertionsAreDisabled");
    if (assumeAssertionsEnabled && assumeAssertionsDisabled) {
      throw new UserError(
          "Assertions cannot be assumed to be enabled and disabled at the same time.");
    }

    // Subcheckers with dataflow share control-flow graph structure to
    // allow a super-checker to query the stores of a subchecker.
    if (factory instanceof GenericAnnotatedTypeFactory) {
      GenericAnnotatedTypeFactory asGATF =
          (GenericAnnotatedTypeFactory) factory;
      if (asGATF.hasOrIsSubchecker) {
        ControlFlowGraph sharedCFG = asGATF.getSharedCFGForTree(underlyingAST.getCode());
        if (sharedCFG != null) {
          return sharedCFG;
        }
      }
    }

    CFTreeBuilder builder = new CFTreeBuilder(env);
    PhaseOneResult phase1result =
        new CFCFGTranslationPhaseOne(
                builder, checker, factory, assumeAssertionsEnabled, assumeAssertionsDisabled, env)
            .process(root, underlyingAST);
    ControlFlowGraph phase2result = CFGTranslationPhaseTwo.process(phase1result);
    ControlFlowGraph phase3result = CFGTranslationPhaseThree.process(phase2result);
    if (factory instanceof GenericAnnotatedTypeFactory) {
      GenericAnnotatedTypeFactory asGATF =
          (GenericAnnotatedTypeFactory) factory;
      if (asGATF.hasOrIsSubchecker) {
        asGATF.addSharedCFGForTree(underlyingAST.getCode(), phase3result);
      }
    }
    return phase3result;
  }

  /**
   * Given a SourceChecker and an AssertTree, returns whether the AssertTree uses
   * an @AssumeAssertion string that is relevant to the SourceChecker.
   *
   * @param checker the checker
   * @param tree an assert tree
   * @return true if the assert tree contains an @AssumeAssertion(checker) message string for any
   *     subchecker of the given checker's ultimate parent checker
   */
  public static boolean assumeAssertionsActivatedForAssertTree(
      BaseTypeChecker checker, AssertTree tree) {
    ExpressionTree detail = tree.getDetail();
    if (detail != null) {
      String msg = detail.toString();
      BaseTypeChecker ultimateParent = checker.getUltimateParentChecker();
      Collection prefixes = ultimateParent.getSuppressWarningsPrefixesOfSubcheckers();
      for (String prefix : prefixes) {
        String assumeAssert = "@AssumeAssertion(" + prefix + ")";
        if (msg.contains(assumeAssert)) {
          return true;
        }
      }
    }

    return false;
  }

  /**
   * A specialized phase-one CFG builder, with a few modifications that make use of the type
   * factory. It is responsible for: 1) translating foreach loops so that the declarations of their
   * iteration variables have the right annotations, 2) registering the containing elements of
   * artificial trees with the relevant type factories, and 3) generating appropriate assertion CFG
   * structure in the presence of @AssumeAssertion assertion strings which mention the checker or
   * its supercheckers.
   */
  protected static class CFCFGTranslationPhaseOne extends CFGTranslationPhaseOne {
    /** The associated checker. */
    protected final BaseTypeChecker checker;

    /** Type factory to provide types used during CFG building. */
    protected final AnnotatedTypeFactory factory;

    public CFCFGTranslationPhaseOne(
        CFTreeBuilder builder,
        BaseTypeChecker checker,
        AnnotatedTypeFactory factory,
        boolean assumeAssertionsEnabled,
        boolean assumeAssertionsDisabled,
        ProcessingEnvironment env) {
      super(builder, factory, assumeAssertionsEnabled, assumeAssertionsDisabled, env);
      this.checker = checker;
      this.factory = factory;
    }

    @Override
    protected boolean assumeAssertionsEnabledFor(AssertTree tree) {
      if (assumeAssertionsActivatedForAssertTree(checker, tree)) {
        return true;
      }
      return super.assumeAssertionsEnabledFor(tree);
    }

    @Override
    public void handleArtificialTree(Tree tree) {
      MethodTree enclosingMethod = TreePathUtil.enclosingMethod(getCurrentPath());
      if (enclosingMethod != null) {
        Element methodElement = TreeUtils.elementFromDeclaration(enclosingMethod);
        factory.setEnclosingElementForArtificialTree(tree, methodElement);
      } else {
        ClassTree enclosingClass = TreePathUtil.enclosingClass(getCurrentPath());
        if (enclosingClass != null) {
          Element classElement = TreeUtils.elementFromDeclaration(enclosingClass);
          factory.setEnclosingElementForArtificialTree(tree, classElement);
        }
      }
    }

    @Override
    protected VariableTree createEnhancedForLoopIteratorVariable(
        MethodInvocationTree iteratorCall, VariableElement variableElement) {
      Tree annotatedIteratorTypeTree =
          ((CFTreeBuilder) treeBuilder).buildAnnotatedType(TreeUtils.typeOf(iteratorCall));
      handleArtificialTree(annotatedIteratorTypeTree);

      // Declare and initialize a new, unique iterator variable
      VariableTree iteratorVariable =
          treeBuilder.buildVariableDecl(
              annotatedIteratorTypeTree,
              uniqueName("iter"),
              variableElement.getEnclosingElement(),
              iteratorCall);
      return iteratorVariable;
    }

    @Override
    protected VariableTree createEnhancedForLoopArrayVariable(
        ExpressionTree expression, VariableElement variableElement) {

      TypeMirror type = null;
      if (TreeUtils.isLocalVariable(expression)) {
        // It is necessary to get the elt because just getting the type of expression
        // directly (via TreeUtils.typeOf) doesn't include annotations on the declarations
        // of local variables, for some reason.
        Element elt = TreeUtils.elementFromTree(expression);
        if (elt != null) {
          type = ElementUtils.getType(elt);
        }
      }

      // In all other cases cases, instead get the type of the expression. This case is
      // also triggered when the type from the element is not an array, which can occur
      // if the declaration of the local is a generic, such as in
      // framework/tests/all-systems/java8inference/Issue1775.java.
      // Getting the type from the expression itself guarantees the result will be an array.
      if (type == null || type.getKind() != TypeKind.ARRAY) {
        TypeMirror expressionType = TreeUtils.typeOf(expression);
        type = expressionType;
      }

      assert (type instanceof ArrayType) : "array types must be represented by ArrayType";

      Tree annotatedArrayTypeTree = ((CFTreeBuilder) treeBuilder).buildAnnotatedType(type);
      handleArtificialTree(annotatedArrayTypeTree);

      // Declare and initialize a temporary array variable
      VariableTree arrayVariable =
          treeBuilder.buildVariableDecl(
              annotatedArrayTypeTree,
              uniqueName("array"),
              variableElement.getEnclosingElement(),
              expression);
      return arrayVariable;
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy