From 8bc878a05ce91e2213cd4c80d1a7e467ad0fa688 Mon Sep 17 00:00:00 2001 From: Stephan Schroevers Date: Fri, 26 May 2023 08:29:26 +0200 Subject: [PATCH] Improve `AbstractMatcherTestChecker` (#599) 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. --- .../matchers/AbstractMatcherTestChecker.java | 57 ++++++++++++++++--- 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/refaster-support/src/test/java/tech/picnic/errorprone/refaster/matchers/AbstractMatcherTestChecker.java b/refaster-support/src/test/java/tech/picnic/errorprone/refaster/matchers/AbstractMatcherTestChecker.java index c0361ab6e3..48fac2d44f 100644 --- a/refaster-support/src/test/java/tech/picnic/errorprone/refaster/matchers/AbstractMatcherTestChecker.java +++ b/refaster-support/src/test/java/tech/picnic/errorprone/refaster/matchers/AbstractMatcherTestChecker.java @@ -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; @@ -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. @@ -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. + * + *

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); + } }