5

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
Jesse Glick
  • 24,539
  • 10
  • 90
  • 112
  • Thanks for that question, which really doubles as a great concise example for the Compiler Tree API. For the record, above `processingEnv` comes from an `init(ProcessingEnvironment)` method that should also be overridden. Oh, and JRockit 1.6.0_29 still shows "null". – mgaert Dec 21 '11 at 23:25
  • No need to override `AbstractProcessor.init` in most cases. – Jesse Glick Jul 26 '13 at 14:06

1 Answers1

1

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.

Community
  • 1
  • 1
Jesse Glick
  • 24,539
  • 10
  • 90
  • 112
  • http://bitbucket.org/jglick/qualifiedpublic/src/f2d33fd97c83/src/qualifiedpublic/PublicProcessor.java seems to work - but only under JDK 7. I cannot get it to work at all using JDK 6's javac. – Jesse Glick Feb 18 '10 at 01:02
  • I am stuck on the same issue, and I cannot use Jdk7. Did you finally find any solution to this issue with Jdk1.6?? – Aditya Jain Jun 20 '13 at 12:23