J'essaye d'écrire un processeur d'annotation au format JSR 269 qui utilise l'API Compiler Tree de javac pour faire une analyse de code source. Je suis intéressé par les expressions de sélection de membres, telles que les appels de méthode.Comment puis-je obtenir le type de l'expression dans un MemberSelectTree à partir d'un plugin javac?
Je peux facilement obtenir le nom de la méthode (ou du champ, etc.) sélectionné. Mais je veux savoir de quel type le membre est sélectionné, et je n'arrive pas à trouver un moyen simple de le faire. Trees.getTypeMirror
renvoie null
pour tout ce que j'essaie de l'appeler (et le Javadoc ne donne aucun indice).
Je suppose que je pourrais analyser de façon exhaustive tous les types d'expression sur le côté gauche de l'élément de sélection et déterminer le type statique de l'expression par analyse récursive: NewClassTree
, TypeCastTree
, MethodInvocationTree
, ArrayAccessTree
, et bien d'autres. Mais cela semble être un travail sujet aux erreurs, et javac connaît déjà le type statique de l'expression, car il a besoin de cette information à de nombreuses fins. Mais comment puis-je avoir accès à ce type d'informations?
Ce que j'ai jusqu'à présent:
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;
}
}
et ce qu'il imprime lorsqu'il est exécuté sur un code simple, faire des appels méthode:
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
Merci pour cette question, qui est vraiment un excellent exemple concis pour l'API de l'arbre de compilation. Pour l'anecdote, ci-dessus 'processingEnv' vient d'une méthode' init (ProcessingEnvironment) 'qui devrait aussi être surchargée. Oh, et JRockit 1.6.0_29 montre toujours "null". – mgaert
Pas besoin de surcharger 'AbstractProcessor.init' dans la plupart des cas. –