Estoy tratando de escribir un procesador de anotaciones en el formato JSR 269 que utiliza la API del árbol de compilación de javac para hacer algunos análisis de código fuente. Me interesan las expresiones de selección de miembros, como las llamadas a métodos.¿Cómo obtengo el tipo de expresión en un MemberSelectTree de un plugin javac?
Puedo obtener fácilmente el nombre del método (o campo, etc.) seleccionado. Pero quiero saber de qué tipo se está seleccionando al miembro, y parece que no puedo encontrar una forma directa de hacerlo. Trees.getTypeMirror
devuelve null
para todo lo que intento llamarlo (y el Javadoc no da pistas).
supongo que podría analizar exhaustivamente cada tipo de expresión en el lado izquierdo del miembro de seleccionar y determinar el tipo estático de la expresión mediante análisis recursivo: NewClassTree
, TypeCastTree
, MethodInvocationTree
, ArrayAccessTree
, y muchos otros. Pero esto parece mucho trabajo propenso a errores, y claramente javac ya conoce el tipo estático de la expresión, ya que necesita esta información para muchos propósitos. Pero, ¿cómo obtengo acceso a este tipo de información?
Lo que tengo hasta ahora:
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;
}
}
y lo que se imprime cuando se ejecuta en algún método simple de hacer que el código llama:
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
Gracias por esa pregunta, que realmente funciona como un gran ejemplo conciso para Compiler Tree API. Para el registro, arriba 'processingEnv' proviene de un método' init (ProcessingEnvironment) 'que también debe ser anulado. Ah, y JRockit 1.6.0_29 todavía muestra "nulo". – mgaert
No es necesario anular 'AbstractProcessor.init' en la mayoría de los casos. –