blob: 82021f41b7b55efc1d6822d5045e2550c7ef1fda [file] [log] [blame]
package org.checkerframework.framework.source;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreePath;
import com.sun.source.util.TreePathScanner;
import com.sun.source.util.Trees;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import org.checkerframework.javacutil.BugInCF;
import org.checkerframework.javacutil.TreeUtils;
/**
* An AST visitor that provides a variety of compiler utilities and interfaces to facilitate
* type-checking.
*/
public abstract class SourceVisitor<R, P> extends TreePathScanner<R, P> {
/** The {@link Trees} instance to use for scanning. */
protected final Trees trees;
/** The {@link Elements} helper to use when scanning. */
protected final Elements elements;
/** The {@link Types} helper to use when scanning. */
protected final Types types;
/** The root of the AST that this {@link SourceVisitor} will scan. */
protected CompilationUnitTree root;
/** A set of trees that are annotated with {@code @SuppressWarnings}. */
public final List<Tree> treesWithSuppressWarnings;
/** Whether or not a warning should be issued for unneeded warning suppressions. */
private final boolean warnUnneededSuppressions;
/**
* Creates a {@link SourceVisitor} to use for scanning a source tree.
*
* @param checker the checker to invoke on the input source tree
*/
protected SourceVisitor(SourceChecker checker) {
// Use the checker's processing environment to get the helpers we need.
ProcessingEnvironment env = checker.getProcessingEnvironment();
this.trees = Trees.instance(env);
this.elements = env.getElementUtils();
this.types = env.getTypeUtils();
this.treesWithSuppressWarnings = new ArrayList<>();
this.warnUnneededSuppressions = checker.hasOption("warnUnneededSuppressions");
}
/**
* Set the CompilationUnitTree to be used during any visits. For any later calls of {@code
* com.sun.source.util.TreePathScanner.scan(TreePath, P)}, the CompilationUnitTree of the TreePath
* has to be equal to {@code root}.
*/
public void setRoot(CompilationUnitTree root) {
this.root = root;
}
/**
* Store the last Tree visited by the SourceVisitor. This is necessary because the finally blocks
* in {@link com.sun.source.util.TreePathScanner#scan(TreePath, Object)} and {@link
* com.sun.source.util.TreePathScanner#scan(Tree, Object)} set the visited Path to null. This
* field is used to report a rough location for the error in {@link
* org.checkerframework.framework.source.SourceChecker#logBugInCF(BugInCF)}.
*/
/*package-private*/ Tree lastVisited;
/** Entry point for a type processor: the TreePath leaf is a top-level type tree within root. */
public void visit(TreePath path) {
lastVisited = path.getLeaf();
this.scan(path, null);
}
@Override
public R scan(Tree tree, P p) {
lastVisited = tree;
return super.scan(tree, p);
}
@Override
public R visitClass(ClassTree classTree, P p) {
storeSuppressWarningsAnno(classTree);
return super.visitClass(classTree, p);
}
@Override
public R visitVariable(VariableTree variableTree, P p) {
storeSuppressWarningsAnno(variableTree);
return super.visitVariable(variableTree, p);
}
@Override
public R visitMethod(MethodTree node, P p) {
storeSuppressWarningsAnno(node);
return super.visitMethod(node, p);
}
/**
* If {@code tree} has a {@code @SuppressWarnings} add it to treesWithSuppressWarnings.
*
* @param tree a declaration on which a {@code @SuppressWarnings} annotation may be placed
*/
private void storeSuppressWarningsAnno(Tree tree) {
if (!warnUnneededSuppressions) {
return;
}
Element elt = TreeUtils.elementFromTree(tree);
if (elt.getAnnotation(SuppressWarnings.class) != null) {
treesWithSuppressWarnings.add(tree);
}
}
}