Skip to content

Commit

Permalink
Improve AbstractMatcherTestChecker
Browse files Browse the repository at this point in the history
These changes enable testing of a wider range of `Matcher` implementations. In
a nutshell:
- The `Matcher` under test is now passed `VisitorState` instances with an
  accurate `TreePath`.
- The `Matcher` under test is no longer consulted for method select and cast
  type expressions, mirroring Refaster behavior.
  • Loading branch information
Stephan202 authored and rickie committed May 25, 2023
1 parent b399ef8 commit 386fd74
Showing 1 changed file with 48 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.ImportTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.TypeCastTree;
import com.sun.source.util.TreePath;
import com.sun.source.util.TreeScanner;
import org.jspecify.annotations.Nullable;

Expand All @@ -31,18 +34,27 @@ abstract class AbstractMatcherTestChecker extends BugChecker implements Compilat

@Override
public Description matchCompilationUnit(CompilationUnitTree compilationUnit, VisitorState state) {
new TreeScanner<@Nullable Void, @Nullable Void>() {
new TreeScanner<@Nullable Void, TreePath>() {
@Override
public @Nullable Void scan(Tree tree, @Nullable Void unused) {
if (tree instanceof ExpressionTree && delegate.matches((ExpressionTree) tree, state)) {
state.reportMatch(describeMatch(tree));
public @Nullable Void scan(@Nullable Tree tree, TreePath treePath) {
if (tree == null) {
return null;
}

return super.scan(tree, unused);
TreePath path = new TreePath(treePath, tree);
if (tree instanceof ExpressionTree) {
ExpressionTree expressionTree = (ExpressionTree) tree;
if (!isMethodSelect(expressionTree, path)
&& delegate.matches(expressionTree, state.withPath(path))) {
state.reportMatch(describeMatch(tree));
}
}

return super.scan(tree, path);
}

@Override
public @Nullable Void visitImport(ImportTree node, @Nullable Void unused) {
public @Nullable Void visitImport(ImportTree tree, TreePath path) {
/*
* We're not interested in matching import statements. While components of these
* can be `ExpressionTree`s, they will never be matched by Refaster.
Expand All @@ -51,15 +63,42 @@ public Description matchCompilationUnit(CompilationUnitTree compilationUnit, Vis
}

@Override
public @Nullable Void visitMethod(MethodTree node, @Nullable Void unused) {
public @Nullable Void visitMethod(MethodTree tree, TreePath path) {
/*
* We're not interested in matching e.g. parameter and return type declarations. While these
* can be `ExpressionTree`s, they will never be matched by Refaster.
*/
return scan(node.getBody(), unused);
return scan(tree.getBody(), new TreePath(path, tree));
}

@Override
public @Nullable Void visitTypeCast(TypeCastTree tree, TreePath path) {
/*
* We're not interested in matching the parenthesized type subtree that is part of a type
* cast expression. While such trees can be `ExpressionTree`s, they will never be matched by
* Refaster.
*/
return scan(tree.getExpression(), new TreePath(path, tree));
}
}.scan(compilationUnit, null);
}.scan(compilationUnit, state.getPath());

return Description.NO_MATCH;
}

/**
* Tells whether the given {@link ExpressionTree} is the {@link
* MethodInvocationTree#getMethodSelect() method select} portion of a method invocation.
*
* <p>Such {@link ExpressionTree}s will never be matched by Refaster.
*/
private static boolean isMethodSelect(ExpressionTree tree, TreePath path) {
TreePath parentPath = path.getParentPath();
if (parentPath == null) {
return false;
}

Tree parentTree = parentPath.getLeaf();
return parentTree instanceof MethodInvocationTree
&& ((MethodInvocationTree) parentTree).getMethodSelect().equals(tree);
}
}

0 comments on commit 386fd74

Please sign in to comment.