org.checkerframework.framework.util.Heuristics Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of checker Show documentation
Show all versions of checker Show documentation
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.
package org.checkerframework.framework.util;
import com.sun.source.tree.BlockTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.IfTree;
import com.sun.source.tree.ParenthesizedTree;
import com.sun.source.tree.StatementTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.UnaryTree;
import com.sun.source.util.SimpleTreeVisitor;
import com.sun.source.util.TreePath;
import org.checkerframework.javacutil.TreePathUtil;
import org.checkerframework.javacutil.TreeUtils;
import java.util.ArrayDeque;
import java.util.Arrays;
/**
* Utilities for determining tree-based heuristics.
*
* For an example, see {@code org.checkerframework.checker.interning.InterningVisitor}.
*/
public class Heuristics {
/**
* Returns true if a tree has a particular set of direct parents, ignoring blocks and
* parentheses.
*
*
For example, to test whether an expression (specified by {@code path}) is immediately
* contained by an if statement which is immediately contained in a method, one would invoke:
*
*
* matchParents(path, Kind.IF, Tree.Kind.METHOD)
*
*
* @param path the path to match
* @param kinds the tree kinds to match against, in ascending order starting from the desired
* kind of the parent
* @return true if the tree path matches the desired kinds, skipping blocks and parentheses, for
* as many kinds as specified
*/
public static boolean matchParents(TreePath path, Tree.Kind... kinds) {
TreePath parentPath = path.getParentPath();
boolean result = true;
ArrayDeque queue = new ArrayDeque<>(Arrays.asList(kinds));
Tree tree;
while ((tree = parentPath.getLeaf()) != null) {
if (queue.isEmpty()) {
break;
}
if (tree.getKind() == Tree.Kind.BLOCK || tree.getKind() == Tree.Kind.PARENTHESIZED) {
parentPath = parentPath.getParentPath();
continue;
}
result &= queue.removeFirst() == parentPath.getLeaf().getKind();
parentPath = parentPath.getParentPath();
}
return result;
}
/** A base class for tree-matching algorithms. Skips parentheses by default. */
public static class Matcher extends SimpleTreeVisitor {
@Override
protected Boolean defaultAction(Tree tree, Void p) {
return false;
}
@Override
public Boolean visitParenthesized(ParenthesizedTree tree, Void p) {
return visit(tree.getExpression(), p);
}
/**
* Returns true if the given path matches this Matcher.
*
* @param path the path to test
* @return true if the given path matches this Matcher
*/
public boolean match(TreePath path) {
return visit(path.getLeaf(), null);
}
}
public static class PreceededBy extends Matcher {
private final Matcher matcher;
public PreceededBy(Matcher matcher) {
this.matcher = matcher;
}
@SuppressWarnings("interning:not.interned")
@Override
public boolean match(TreePath path) {
StatementTree stmt = TreePathUtil.enclosingOfClass(path, StatementTree.class);
if (stmt == null) {
return false;
}
TreePath p = path;
while (p.getLeaf() != stmt) {
p = p.getParentPath();
}
assert p.getLeaf() == stmt;
while (p != null && p.getLeaf() instanceof StatementTree) {
if (p.getParentPath().getLeaf() instanceof BlockTree) {
BlockTree block = (BlockTree) p.getParentPath().getLeaf();
for (StatementTree st : block.getStatements()) {
if (st == p.getLeaf()) {
break;
}
if (matcher.match(new TreePath(p, st))) {
return true;
}
}
}
p = p.getParentPath();
}
return false;
}
}
/**
* {@code match()} returns true if called on a path, any element of which matches the given
* matcher (supplied at object initialization). That matcher is usually one that matches only
* the leaf of a path, ignoring all other parts of it.
*/
public static class Within extends Matcher {
/**
* The matcher that {@code Within.match} will try, on every parent of the path it is given.
*/
private final Matcher matcher;
/**
* Create a new Within matcher.
*
* @param matcher the matcher that {@code Within.match} will try, on every parent of the
* path it is given
*/
public Within(Matcher matcher) {
this.matcher = matcher;
}
@Override
public boolean match(TreePath path) {
TreePath p = path;
while (p != null) {
if (matcher.match(p)) {
return true;
}
p = p.getParentPath();
}
return false;
}
}
/**
* {@code match()} returns true if called on a path whose leaf is within the "then" clause of an
* if whose condition matches the matcher (supplied at object initialization). Also returns true
* if the leaf is within the "else" of a negated condition that matches the supplied matcher.
*/
public static class WithinTrueBranch extends Matcher {
/** conditionMatcher for the condition */
private final Matcher matcher;
/**
* @param conditionMatcher for the condition
*/
public WithinTrueBranch(Matcher conditionMatcher) {
this.matcher = conditionMatcher;
}
@SuppressWarnings("interning:not.interned")
@Override
public boolean match(TreePath path) {
TreePath prev = path, p = path.getParentPath();
while (p != null) {
if (p.getLeaf().getKind() == Tree.Kind.IF) {
IfTree ifTree = (IfTree) p.getLeaf();
ExpressionTree cond = TreeUtils.withoutParens(ifTree.getCondition());
if (ifTree.getThenStatement() == prev.getLeaf()
&& matcher.match(new TreePath(p, cond))) {
return true;
}
if (cond.getKind() == Tree.Kind.LOGICAL_COMPLEMENT
&& matcher.match(new TreePath(p, ((UnaryTree) cond).getExpression()))) {
return true;
}
}
prev = p;
p = p.getParentPath();
}
return false;
}
}
/**
* {@code match()} returns true if called on a path whose leaf has the given kind (supplied at
* object initialization).
*/
public static class OfKind extends Matcher {
private final Tree.Kind kind;
private final Matcher matcher;
public OfKind(Tree.Kind kind, Matcher matcher) {
this.kind = kind;
this.matcher = matcher;
}
@Override
public boolean match(TreePath path) {
if (path.getLeaf().getKind() == kind) {
return matcher.match(path);
}
return false;
}
}
/** {@code match()} returns true if any of the given matchers returns true. */
public static class OrMatcher extends Matcher {
private final Matcher[] matchers;
public OrMatcher(Matcher... matchers) {
this.matchers = matchers;
}
@Override
public boolean match(TreePath path) {
for (Matcher matcher : matchers) {
if (matcher.match(path)) {
return true;
}
}
return false;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy