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 org.checkerframework.common.basetype.BaseTypeChecker;
import org.checkerframework.dataflow.cfg.CFGBuilder;
import org.checkerframework.dataflow.cfg.ControlFlowGraph;
import org.checkerframework.dataflow.cfg.UnderlyingAST;
import org.checkerframework.framework.source.SourceChecker;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.javacutil.ErrorReporter;
import org.checkerframework.javacutil.TreeUtils;
/**
* A control-flow graph builder (see {@link CFGBuilder}) that knows about the Checker Framework
* annotations and their representation as {@link AnnotatedTypeMirror}s.
*
* @author Stefan Heule
*/
public class CFCFGBuilder extends CFGBuilder {
/** The associated checker. */
protected final BaseTypeChecker checker;
/** Type factory to provide types used during CFG building. */
protected final AnnotatedTypeFactory factory;
public CFCFGBuilder(BaseTypeChecker checker, AnnotatedTypeFactory factory) {
super(
checker.hasOption("assumeAssertionsAreEnabled"),
checker.hasOption("assumeAssertionsAreDisabled"));
if (assumeAssertionsEnabled && assumeAssertionsDisabled) {
ErrorReporter.errorAbort(
"Assertions cannot be assumed to be enabled and disabled at the same time.");
}
this.checker = checker;
this.factory = factory;
}
/** Build the control flow graph of some code. */
@Override
public ControlFlowGraph run(
CompilationUnitTree root, ProcessingEnvironment env, UnderlyingAST underlyingAST) {
declaredClasses.clear();
declaredLambdas.clear();
CFTreeBuilder builder = new CFTreeBuilder(env);
PhaseOneResult phase1result =
new CFCFGTranslationPhaseOne()
.process(root, env, underlyingAST, exceptionalExitLabel, builder, factory);
ControlFlowGraph phase2result = new CFGTranslationPhaseTwo().process(phase1result);
ControlFlowGraph phase3result = CFGTranslationPhaseThree.process(phase2result);
return phase3result;
}
/*
* Given a SourceChecker and an AssertTree, returns whether the AssertTree
* uses an @AssumeAssertion string that is relevant to the SourceChecker.
*/
public static boolean assumeAssertionsActivatedForAssertTree(
SourceChecker checker, AssertTree tree) {
ExpressionTree detail = tree.getDetail();
if (detail != null) {
String msg = detail.toString();
Collection<String> warningKeys = checker.getSuppressWarningsKeys();
for (String warningKey : warningKeys) {
String key = "@AssumeAssertion(" + warningKey + ")";
if (msg.contains(key)) {
return true;
}
}
}
return false;
}
public class CFCFGTranslationPhaseOne extends CFGTranslationPhaseOne {
@Override
protected boolean assumeAssertionsEnabledFor(AssertTree tree) {
if (assumeAssertionsActivatedForAssertTree(checker, tree)) {
return true;
}
return super.assumeAssertionsEnabledFor(tree);
}
@Override
public void handleArtificialTree(Tree tree) {
// Record the method or class that encloses the newly created tree.
MethodTree enclosingMethod = TreeUtils.enclosingMethod(getCurrentPath());
if (enclosingMethod != null) {
Element methodElement = TreeUtils.elementFromDeclaration(enclosingMethod);
factory.setPathHack(tree, methodElement);
} else {
ClassTree enclosingClass = TreeUtils.enclosingClass(getCurrentPath());
if (enclosingClass != null) {
Element classElement = TreeUtils.elementFromDeclaration(enclosingClass);
factory.setPathHack(tree, classElement);
}
}
}
@Override
protected VariableTree createEnhancedForLoopIteratorVariable(
MethodInvocationTree iteratorCall, VariableElement variableElement) {
// We do not want to cache flow-insensitive types
// retrieved during CFG building.
boolean oldShouldCache = factory.shouldCache;
factory.shouldCache = false;
AnnotatedTypeMirror annotatedIteratorType = factory.getAnnotatedType(iteratorCall);
factory.shouldCache = oldShouldCache;
Tree annotatedIteratorTypeTree =
((CFTreeBuilder) treeBuilder).buildAnnotatedType(annotatedIteratorType);
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) {
// We do not want to cache flow-insensitive types
// retrieved during CFG building.
boolean oldShouldCache = factory.shouldCache;
factory.shouldCache = false;
AnnotatedTypeMirror annotatedArrayType = factory.getAnnotatedType(expression);
factory.shouldCache = oldShouldCache;
assert (annotatedArrayType instanceof AnnotatedTypeMirror.AnnotatedArrayType)
: "ArrayType must be represented by AnnotatedArrayType";
Tree annotatedArrayTypeTree =
((CFTreeBuilder) treeBuilder).buildAnnotatedType(annotatedArrayType);
handleArtificialTree(annotatedArrayTypeTree);
// Declare and initialize a temporary array variable
VariableTree arrayVariable =
treeBuilder.buildVariableDecl(
annotatedArrayTypeTree,
uniqueName("array"),
variableElement.getEnclosingElement(),
expression);
return arrayVariable;
}
}
}