Ich versuche, einen Annotationsprozessor im JSR 269-Format zu schreiben, der die Compilerbaum-API von javac verwendet, um eine Quellcodeanalyse durchzuführen. Ich bin an Elementauswahlausdrücken wie Methodenaufrufen interessiert.Wie bekomme ich den Typ des Ausdrucks in einem MemberSelectTree von einem Javac-Plugin?
Ich kann leicht den Namen der Methode (oder Feld, etc.) erhalten, die ausgewählt wird. Aber ich möchte wissen, aus welchem Typ das Mitglied ausgewählt wurde, und ich kann keinen einfachen Weg finden, dies zu tun. Trees.getTypeMirror
gibt null
für alles zurück, was ich versuche, es anzurufen (und das Javadoc gibt keine Hinweise).
Ich glaube, ich erschöpfend jede Art von Ausdruck auf der linken Seite des Elements auswählen und bestimmen den statischen Typ des Ausdrucks durch rekursive Analyse analysieren konnte: NewClassTree
, TypeCastTree
, MethodInvocationTree
, ArrayAccessTree
, und viele andere. Aber das scheint eine Menge fehleranfälliger Arbeit zu sein, und eindeutig kennt javac bereits den statischen Typ des Ausdrucks, da er diese Information für viele Zwecke benötigt. Aber wie bekomme ich Zugang zu diesen Typinformationen?
Was ich habe, so weit:
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;
}
}
und was es gedruckt wird, wenn auf einigen einfachen Code Herstellungsverfahren laufen ruft:
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
Vielen Dank für diese Frage, die wirklich ein großartiges Beispiel für die Compiler Tree API ist. Für den Datensatz kommt über "processingEnv" von einer 'init (ProcessingEnvironment)' Methode, die auch überschrieben werden sollte. Oh, und JRockit 1.6.0_29 zeigt immer noch "null". – mgaert
In den meisten Fällen ist es nicht nötig, 'AbstractProcessor.init' zu überschreiben. –