Skip to content

Commit 7fc6dfa

Browse files
committed
Simplify tree matching code
1 parent d0c16a0 commit 7fc6dfa

1 file changed

Lines changed: 114 additions & 48 deletions

File tree

Lines changed: 114 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
11
package com.garciat.typeclasses.processor;
22

3+
import static com.garciat.typeclasses.types.Unit.unit;
4+
35
import com.garciat.typeclasses.TypeClasses;
46
import com.garciat.typeclasses.api.Ty;
5-
import com.garciat.typeclasses.types.Either;
6-
import com.sun.source.tree.*;
7-
import com.sun.source.util.*;
7+
import com.garciat.typeclasses.types.Maybe;
8+
import com.garciat.typeclasses.types.Unit;
9+
import com.sun.source.tree.ClassTree;
10+
import com.sun.source.tree.ExpressionTree;
11+
import com.sun.source.tree.MethodInvocationTree;
12+
import com.sun.source.tree.NewClassTree;
13+
import com.sun.source.tree.Tree;
14+
import com.sun.source.util.JavacTask;
15+
import com.sun.source.util.Plugin;
16+
import com.sun.source.util.TaskEvent;
17+
import com.sun.source.util.TaskListener;
18+
import com.sun.source.util.TreePath;
19+
import com.sun.source.util.TreePathScanner;
20+
import com.sun.source.util.Trees;
821
import java.lang.reflect.Method;
9-
import javax.lang.model.element.Element;
1022
import javax.lang.model.element.ExecutableElement;
1123
import javax.lang.model.element.TypeElement;
1224
import javax.lang.model.type.DeclaredType;
@@ -60,54 +72,108 @@ private WitnessCallScanner(Trees trees) {
6072

6173
@Override
6274
public Void visitMethodInvocation(MethodInvocationTree node, Void arg) {
63-
Element element = trees.getElement(getCurrentPath());
64-
65-
if (isMethodCall(WITNESS_METHOD, element)) {
66-
// Found a call to TypeClasses.witness()
67-
// The first argument is expected to be of the form "new Ty<>() {}"
68-
ExpressionTree firstArg = node.getArguments().getFirst();
69-
70-
// Check if it's a "new Ty<>() {}" anonymous class creation
71-
if (firstArg instanceof NewClassTree newClass) {
72-
Tree tyApp = newClass.getClassBody().getImplementsClause().getFirst();
73-
74-
TypeMirror typeMirror =
75-
trees.getTypeMirror(trees.getPath(getCurrentPath().getCompilationUnit(), tyApp));
76-
77-
// Try to extract witness type and verify resolution
78-
if (typeMirror instanceof DeclaredType declaredType) {
79-
TypeMirror witnessTypeMirror = declaredType.getTypeArguments().getFirst();
80-
81-
ParsedType target = system.parse(witnessTypeMirror);
82-
83-
switch (WitnessResolution.resolve(system, target)) {
84-
case Either.Left<
85-
WitnessResolution.ResolutionError, WitnessResolution.InstantiationPlan>(
86-
var error) ->
87-
this.trees.printMessage(
88-
Diagnostic.Kind.ERROR,
89-
"Failed to resolve witness for type: "
90-
+ witnessTypeMirror
91-
+ "\nReason: "
92-
+ error.format(),
93-
getCurrentPath().getLeaf(),
94-
getCurrentPath().getCompilationUnit());
95-
case Either.Right<
96-
WitnessResolution.ResolutionError, WitnessResolution.InstantiationPlan>
97-
ignore -> {}
98-
}
99-
}
100-
}
101-
}
75+
Parser.unaryMethodCallArgument(WITNESS_METHOD)
76+
.flatMap(Parser.newAnonymousClassBody())
77+
.flatMap(Parser.singleImplementsClause())
78+
.flatMap(Parser.treeTypeMirror())
79+
.flatMap(Parser.rawTypeMatches(Ty.class))
80+
.flatMap(Parser.unaryTypeArgument())
81+
.parse(trees, getCurrentPath(), node)
82+
.fold(
83+
Unit::unit,
84+
witnessType ->
85+
WitnessResolution.resolve(system, system.parse(witnessType))
86+
.fold(
87+
error -> {
88+
this.trees.printMessage(
89+
Diagnostic.Kind.ERROR,
90+
"Failed to resolve witness for type: "
91+
+ witnessType
92+
+ "\nReason: "
93+
+ error.format(),
94+
getCurrentPath().getLeaf(),
95+
getCurrentPath().getCompilationUnit());
96+
return unit();
97+
},
98+
plan -> unit()));
10299

103100
return super.visitMethodInvocation(node, arg);
104101
}
105102
}
103+
}
104+
105+
interface Parser<T, R> {
106+
Maybe<R> parse(Trees trees, TreePath current, T input);
107+
108+
default <S> Parser<T, S> flatMap(Parser<R, S> next) {
109+
return (trees, current, input) ->
110+
this.parse(trees, current, input).flatMap(r -> next.parse(trees, current, r));
111+
}
112+
113+
static <T> Parser<MethodInvocationTree, ExpressionTree> unaryMethodCallArgument(Method target) {
114+
return (trees, current, input) -> {
115+
if (trees.getElement(current) instanceof ExecutableElement method
116+
&& method.getSimpleName().contentEquals(target.getName())
117+
&& method.getEnclosingElement() instanceof TypeElement methodOwner
118+
&& methodOwner.getQualifiedName().contentEquals(target.getDeclaringClass().getName())
119+
&& input.getArguments().size() == 1) {
120+
return Maybe.just(input.getArguments().getFirst());
121+
} else {
122+
return Maybe.nothing();
123+
}
124+
};
125+
}
126+
127+
static Parser<ExpressionTree, ClassTree> newAnonymousClassBody() {
128+
return (trees, current, input) -> {
129+
if (input instanceof NewClassTree newClass && newClass.getClassBody() != null) {
130+
return Maybe.just(newClass.getClassBody());
131+
} else {
132+
return Maybe.nothing();
133+
}
134+
};
135+
}
136+
137+
static Parser<ClassTree, Tree> singleImplementsClause() {
138+
return (trees, current, input) -> {
139+
if (input.getImplementsClause() != null && input.getImplementsClause().size() == 1) {
140+
return Maybe.just(input.getImplementsClause().getFirst());
141+
} else {
142+
return Maybe.nothing();
143+
}
144+
};
145+
}
146+
147+
static Parser<Tree, TypeMirror> treeTypeMirror() {
148+
return (trees, current, input) -> {
149+
try {
150+
TypeMirror typeMirror =
151+
trees.getTypeMirror(trees.getPath(current.getCompilationUnit(), input));
152+
return Maybe.just(typeMirror);
153+
} catch (IllegalArgumentException e) {
154+
return Maybe.nothing();
155+
}
156+
};
157+
}
158+
159+
static Parser<TypeMirror, DeclaredType> rawTypeMatches(Class<?> cls) {
160+
return (trees, current, input) -> {
161+
if (input instanceof DeclaredType declaredType
162+
&& declaredType.asElement() instanceof TypeElement typeElement
163+
&& typeElement.getQualifiedName().contentEquals(cls.getName())) {
164+
return Maybe.just(declaredType);
165+
}
166+
return Maybe.nothing();
167+
};
168+
}
106169

107-
private static boolean isMethodCall(Method target, Element element) {
108-
return element instanceof ExecutableElement method
109-
&& method.getSimpleName().contentEquals(target.getName())
110-
&& method.getEnclosingElement() instanceof TypeElement methodOwner
111-
&& methodOwner.getQualifiedName().contentEquals(target.getDeclaringClass().getName());
170+
static Parser<DeclaredType, TypeMirror> unaryTypeArgument() {
171+
return (trees, current, input) -> {
172+
if (input.getTypeArguments().size() == 1) {
173+
return Maybe.just(input.getTypeArguments().getFirst());
174+
} else {
175+
return Maybe.nothing();
176+
}
177+
};
112178
}
113179
}

0 commit comments

Comments
 (0)