Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I get the type of the expression in a MemberSelectTree from a javac plugin?

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
like image 971
Jesse Glick Avatar asked Nov 15 '22 13:11

Jesse Glick


1 Answers

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.

like image 174
Jesse Glick Avatar answered Dec 15 '22 22:12

Jesse Glick