如何从 javac 插件获取 MemberSelectTree 中表达式的类型?
我正在尝试以 JSR 269 格式编写一个注释处理器,它使用 javac 的编译器树 API 来进行一些源代码分析。我对成员选择表达式感兴趣,例如方法调用。
我可以轻松获取所选方法(或字段等)的名称。但我想知道该成员是从什么类型中选择的,但我似乎无法找到一种直接的方法来做到这一点。对于我尝试调用它的所有内容,Trees.getTypeMirror
都会返回null
(并且Javadoc没有给出任何提示)。
我想我可以详尽地分析成员select左侧的每种表达式,并通过递归分析确定表达式的静态类型:NewClassTree
,TypeCastTree
,MethodIncationTree
、ArrayAccessTree
等等。但这似乎是很多容易出错的工作,并且显然 javac 已经知道表达式的静态类型,因为它需要此信息用于多种目的。但我如何获取此类信息呢?
到目前为止我所拥有的:
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;
}
}
以及在一些简单的代码上运行时打印的内容,进行方法调用:
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
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
发现方法调用的类Annotation Processor for java
似乎正在解决一个非常相似的问题,所以我将尝试使用那里给出的建议。不幸的是,它看起来并不简单,并且似乎需要使用 com.sun.tools.javac 包。
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.