I am trying to write an annotation processor in the JSR 269 format which uses javac's Compiler Tree API to do some source code analysis. I am interested in member select expressions, such as method calls.
I can easily get the name of the method (or field, etc.) being selected. But I want to know what type the member is being selected from, and I cannot seem to find a straightforward way to do this. Trees.getTypeMirror
returns null
for everything I try calling it on (and the Javadoc gives no hints).
I suppose I could exhaustively analyze every kind of expression on the left side of the member select and determine the static type of the expression by recursive analysis: NewClassTree
, TypeCastTree
, MethodInvocationTree
, ArrayAccessTree
, and many others. But this seems like a lot of error-prone work, and clearly javac already knows the static type of the expression since it needs this information for many purposes. But how do I get access to this type information?
What I have so far:
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.util.TreePath;
import com.sun.source.util.TreePathScanner;
import com.sun.source.util.Trees;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
@SupportedAnnotationTypes("*")
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class PublicProcessor extends AbstractProcessor {
public @Override boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (Element e : roundEnv.getRootElements()) {
final Trees trees = Trees.instance(processingEnv);
final TreePath root = trees.getPath(e);
new TreePathScanner<Void,Void>() {
public @Override Void visitMethodInvocation(MethodInvocationTree node, Void p) {
System.err.println("visiting method invocation: " + node + " of kind: " + node.getMethodSelect().getKind());
TreePath expr = TreePath.getPath(root, node);
System.err.println(" of type: " + trees.getTypeMirror(expr));
return super.visitMethodInvocation(node, p);
}
public @Override Void visitMemberSelect(MemberSelectTree node, Void p) {
System.err.println("accessing member: " + node.getIdentifier());
System.err.println(" from: " + getCurrentPath().getCompilationUnit().getSourceFile().toUri());
TreePath expr = TreePath.getPath(root, node.getExpression());
System.err.println(" in expr: " + expr.getLeaf());
System.err.println(" of type: " + trees.getTypeMirror(expr));
return super.visitMemberSelect(node, p);
}
}.scan(root, null);
}
return true;
}
}
and what it prints when run on some simple code making method calls:
visiting method invocation: new Class().method() of kind: MEMBER_SELECT
of type: null
accessing member: method
from: .../Whatever.java
in expr: new Class()
of type: null
Discover the class of a methodinvocation in the Annotation Processor for java
seems to be addressing a very similar question, so I will try to use the advice given there. Unfortunately it does not look straightforward, and use of the com.sun.tools.javac
package appears to be required.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With