filter(Predicate predicate) {
+ return flatMap(filtering(predicate));
+ }
+
+ default TreeParser guard(TreeParser predicate) {
+ return (trees, current, input) ->
+ this.parse(trees, current, input)
+ .flatMap(r -> predicate.parse(trees, current, r).map(_ -> r));
+ }
+
+ static TreeParser identity() {
+ return (_, _, input) -> Maybe.just(input);
+ }
+
+ static TreeParser mapping(Function mapper) {
+ return (_, _, input) -> Maybe.just(mapper.apply(input));
+ }
+
+ static TreeParser filtering(Predicate predicate) {
+ return (_, _, input) -> {
+ if (predicate.test(input)) {
+ return Maybe.just(input);
+ } else {
+ return Maybe.nothing();
+ }
+ };
+ }
+
+ static TreeParser notNull() {
+ return filtering(Objects::nonNull);
+ }
+
+ static TreeParser as(Class cls) {
+ return (_, _, input) -> {
+ if (cls.isInstance(input)) {
+ return Maybe.just(cls.cast(input));
+ } else {
+ return Maybe.nothing();
+ }
+ };
+ }
+
+ static TreeParser currentElement() {
+ return (trees, current, _) -> {
+ Element element = trees.getElement(current);
+ if (element != null) {
+ return Maybe.just(element);
+ } else {
+ return Maybe.nothing();
+ }
+ };
+ }
+
+ static TreeParser methodMatches(Method target) {
+ return TreeParser.as(ExecutableElement.class)
+ .filter(m -> m.getSimpleName().contentEquals(target.getName()))
+ .guard(
+ mapping(ExecutableElement::getEnclosingElement)
+ .flatMap(as(TypeElement.class))
+ .map(TypeElement::getQualifiedName)
+ .filter(name -> name.contentEquals(target.getDeclaringClass().getName())));
+ }
+
+ static TreeParser unaryCallArgument() {
+ return mapping(MethodInvocationTree::getArguments)
+ .filter(list -> list.size() == 1)
+ .map(List::getFirst);
+ }
+
+ static TreeParser newAnonymousClassBody() {
+ return TreeParser.as(NewClassTree.class)
+ .map(NewClassTree::getClassBody)
+ .flatMap(notNull());
+ }
+
+ static TreeParser singleImplementsClause() {
+ return mapping(ClassTree::getImplementsClause)
+ .flatMap(notNull())
+ .filter(list -> list.size() == 1)
+ .map(List::getFirst);
+ }
+
+ static TreeParser treeTypeMirror() {
+ return (trees, current, input) -> {
+ try {
+ TypeMirror typeMirror =
+ trees.getTypeMirror(trees.getPath(current.getCompilationUnit(), input));
+ return Maybe.just(typeMirror);
+ } catch (IllegalArgumentException e) {
+ return Maybe.nothing();
+ }
+ };
+ }
+
+ static TreeParser rawTypeMatches(Class> cls) {
+ return TreeParser.as(DeclaredType.class)
+ .guard(
+ declaredTypeElement()
+ .flatMap(as(TypeElement.class))
+ .map(TypeElement::getQualifiedName)
+ .filter(name -> name.contentEquals(cls.getName())));
+ }
+
+ static TreeParser unaryTypeArgument() {
+ return mapping(DeclaredType::getTypeArguments)
+ .filter(list -> list.size() == 1)
+ .map(List::getFirst);
+ }
+
+ static TreeParser declaredTypeElement() {
+ return mapping(DeclaredType::asElement).flatMap(notNull());
+ }
+}
diff --git a/src/main/java/com/garciat/typeclasses/processor/WitnessResolutionChecker.java b/src/main/java/com/garciat/typeclasses/processor/WitnessResolutionChecker.java
index a367f43..ba57600 100644
--- a/src/main/java/com/garciat/typeclasses/processor/WitnessResolutionChecker.java
+++ b/src/main/java/com/garciat/typeclasses/processor/WitnessResolutionChecker.java
@@ -4,33 +4,21 @@
import com.garciat.typeclasses.TypeClasses;
import com.garciat.typeclasses.api.Ty;
-import com.garciat.typeclasses.types.Maybe;
import com.garciat.typeclasses.types.Unit;
-import com.sun.source.tree.ClassTree;
-import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
-import com.sun.source.tree.NewClassTree;
-import com.sun.source.tree.Tree;
-import com.sun.source.util.JavacTask;
-import com.sun.source.util.Plugin;
-import com.sun.source.util.TaskEvent;
-import com.sun.source.util.TaskListener;
-import com.sun.source.util.TreePath;
import com.sun.source.util.TreePathScanner;
import com.sun.source.util.Trees;
import java.lang.reflect.Method;
-import java.util.List;
-import java.util.Objects;
-import java.util.function.Function;
-import java.util.function.Predicate;
+import java.util.Set;
+import javax.annotation.processing.*;
+import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
-public final class WitnessResolutionChecker implements Plugin {
+@SupportedAnnotationTypes("*")
+@SupportedSourceVersion(SourceVersion.RELEASE_25)
+public final class WitnessResolutionChecker extends AbstractProcessor {
private static final Method WITNESS_METHOD;
static {
@@ -41,28 +29,19 @@ public final class WitnessResolutionChecker implements Plugin {
}
}
+ private Trees trees;
+
@Override
- public String getName() {
- return "WitnessResolutionChecker";
+ public synchronized void init(ProcessingEnvironment processingEnv) {
+ this.trees = Trees.instance(processingEnv);
}
@Override
- public void init(JavacTask task, String... args) {
- task.addTaskListener(
- new TaskListener() {
- @Override
- public void finished(TaskEvent e) {
- if (e.getKind() != TaskEvent.Kind.ANALYZE) {
- return;
- }
-
- if (e.getCompilationUnit() == null) {
- return;
- }
-
- new WitnessCallScanner(Trees.instance(task)).scan(e.getCompilationUnit(), null);
- }
- });
+ public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ for (Element rootElement : roundEnv.getRootElements()) {
+ new WitnessCallScanner(trees).scan(trees.getPath(rootElement), null);
+ }
+ return false;
}
/** Scanner that finds calls to TypeClasses.witness() and validates them. */
@@ -77,16 +56,16 @@ private WitnessCallScanner(Trees trees) {
@Override
public Void visitMethodInvocation(MethodInvocationTree node, Void arg) {
- Parser.identity()
+ TreeParser.identity()
.guard(
- Parser.currentElement()
- .flatMap(Parser.methodMatches(WITNESS_METHOD)))
- .flatMap(Parser.unaryCallArgument())
- .flatMap(Parser.newAnonymousClassBody())
- .flatMap(Parser.singleImplementsClause())
- .flatMap(Parser.treeTypeMirror())
- .flatMap(Parser.rawTypeMatches(Ty.class))
- .flatMap(Parser.unaryTypeArgument())
+ TreeParser.currentElement()
+ .flatMap(TreeParser.methodMatches(WITNESS_METHOD)))
+ .flatMap(TreeParser.unaryCallArgument())
+ .flatMap(TreeParser.newAnonymousClassBody())
+ .flatMap(TreeParser.singleImplementsClause())
+ .flatMap(TreeParser.treeTypeMirror())
+ .flatMap(TreeParser.rawTypeMatches(Ty.class))
+ .flatMap(TreeParser.unaryTypeArgument())
.parse(trees, getCurrentPath(), node)
.fold(
Unit::unit,
@@ -104,135 +83,9 @@ public Void visitMethodInvocation(MethodInvocationTree node, Void arg) {
getCurrentPath().getCompilationUnit());
return unit();
},
- plan -> unit()));
+ _ -> unit()));
return super.visitMethodInvocation(node, arg);
}
}
}
-
-interface Parser {
- Maybe parse(Trees trees, TreePath current, T input);
-
- default Parser flatMap(Parser next) {
- return (trees, current, input) ->
- this.parse(trees, current, input).flatMap(r -> next.parse(trees, current, r));
- }
-
- default Parser map(Function mapper) {
- return flatMap(mapping(mapper));
- }
-
- default Parser filter(Predicate predicate) {
- return flatMap(filtering(predicate));
- }
-
- default Parser guard(Parser predicate) {
- return (trees, current, input) ->
- this.parse(trees, current, input)
- .flatMap(r -> predicate.parse(trees, current, r).map(x -> r));
- }
-
- static Parser identity() {
- return (trees, current, input) -> Maybe.just(input);
- }
-
- static Parser mapping(Function mapper) {
- return (trees, current, input) -> Maybe.just(mapper.apply(input));
- }
-
- static Parser filtering(Predicate predicate) {
- return (trees, current, input) -> {
- if (predicate.test(input)) {
- return Maybe.just(input);
- } else {
- return Maybe.nothing();
- }
- };
- }
-
- static Parser notNull() {
- return filtering(Objects::nonNull);
- }
-
- static Parser as(Class cls) {
- return (trees, current, input) -> {
- if (cls.isInstance(input)) {
- return Maybe.just(cls.cast(input));
- } else {
- return Maybe.nothing();
- }
- };
- }
-
- static Parser currentElement() {
- return (trees, current, input) -> {
- Element element = trees.getElement(current);
- if (element != null) {
- return Maybe.just(element);
- } else {
- return Maybe.nothing();
- }
- };
- }
-
- static Parser methodMatches(Method target) {
- return Parser.as(ExecutableElement.class)
- .filter(m -> m.getSimpleName().contentEquals(target.getName()))
- .guard(
- mapping(ExecutableElement::getEnclosingElement)
- .flatMap(as(TypeElement.class))
- .map(TypeElement::getQualifiedName)
- .filter(name -> name.contentEquals(target.getDeclaringClass().getName())));
- }
-
- static Parser unaryCallArgument() {
- return mapping(MethodInvocationTree::getArguments)
- .filter(list -> list.size() == 1)
- .map(List::getFirst);
- }
-
- static Parser newAnonymousClassBody() {
- return Parser.as(NewClassTree.class)
- .map(NewClassTree::getClassBody)
- .flatMap(notNull());
- }
-
- static Parser singleImplementsClause() {
- return mapping(ClassTree::getImplementsClause)
- .flatMap(notNull())
- .filter(list -> list.size() == 1)
- .map(List::getFirst);
- }
-
- static Parser treeTypeMirror() {
- return (trees, current, input) -> {
- try {
- TypeMirror typeMirror =
- trees.getTypeMirror(trees.getPath(current.getCompilationUnit(), input));
- return Maybe.just(typeMirror);
- } catch (IllegalArgumentException e) {
- return Maybe.nothing();
- }
- };
- }
-
- static Parser rawTypeMatches(Class> cls) {
- return Parser.as(DeclaredType.class)
- .guard(
- declaredTypeElement()
- .flatMap(as(TypeElement.class))
- .map(TypeElement::getQualifiedName)
- .filter(name -> name.contentEquals(cls.getName())));
- }
-
- static Parser unaryTypeArgument() {
- return mapping(DeclaredType::getTypeArguments)
- .filter(list -> list.size() == 1)
- .map(List::getFirst);
- }
-
- static Parser declaredTypeElement() {
- return mapping(DeclaredType::asElement).flatMap(notNull());
- }
-}
diff --git a/src/main/resources/META-INF/services/com.sun.source.util.Plugin b/src/main/resources/META-INF/services/javax.annotation.processing.Processor
similarity index 100%
rename from src/main/resources/META-INF/services/com.sun.source.util.Plugin
rename to src/main/resources/META-INF/services/javax.annotation.processing.Processor
diff --git a/src/test/java/com/garciat/typeclasses/processor/WitnessResolutionProcessorTest.java b/src/test/java/com/garciat/typeclasses/processor/WitnessResolutionCheckerTest.java
similarity index 79%
rename from src/test/java/com/garciat/typeclasses/processor/WitnessResolutionProcessorTest.java
rename to src/test/java/com/garciat/typeclasses/processor/WitnessResolutionCheckerTest.java
index cb44185..e31726e 100644
--- a/src/test/java/com/garciat/typeclasses/processor/WitnessResolutionProcessorTest.java
+++ b/src/test/java/com/garciat/typeclasses/processor/WitnessResolutionCheckerTest.java
@@ -7,15 +7,12 @@
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
-import javax.tools.DiagnosticCollector;
-import javax.tools.JavaFileObject;
-import javax.tools.StandardLocation;
-import javax.tools.ToolProvider;
+import javax.tools.*;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
-public class WitnessResolutionProcessorTest {
+public class WitnessResolutionCheckerTest {
@Nullable @TempDir Path tempDir;
@Test
@@ -41,12 +38,10 @@ public void test() throws IOException {
null,
fileManager,
diagnostics,
- List.of(
- "-Xplugin:WitnessResolutionChecker",
- "-classpath",
- System.getProperty("java.class.path")),
+ List.of("-classpath", System.getProperty("java.class.path")),
null,
compilationUnits);
+ task.setProcessors(List.of(new WitnessResolutionChecker()));
// When
boolean success = task.call();