Java8,如何在visitMethodInvocation中发现类和方法名?

时间:2014-06-13 07:27:55

标签: java annotations javac

对于Java7和Java8,我想在调用某些方法时生成警告。 如果用户编译时存在特定的jar,则会打印警告。

我编写了一个Annotation Processor并捕获了visitMethodInvocation()。现在,我想要解压缩类和方法名称将被调用。

有可能吗? 或者如何解决这个问题?

2 个答案:

答案 0 :(得分:4)

您可以执行以下操作:

package mystuff;

import com.sun.source.tree.*;
import com.sun.source.util.*;
import java.util.*;
import javax.annotation.processing.*;
import javax.lang.model.element.*;
import javax.tools.*;

@SupportedAnnotationTypes("*")
    public class Proc extends AbstractProcessor{
    @Override
        public boolean process(Set<?extends TypeElement>annotations,RoundEnvironment roundEnvironment){
        final Trees trees=Trees.instance(processingEnv);
        for(Element element:roundEnvironment.getRootElements()){
        TreePath path=trees.getPath(element);
        final CompilationUnitTree compilationUnit=path.getCompilationUnit();
        compilationUnit.accept(new TreeScanner<Object,Object>(){
            @Override
                public Object visitMethodInvocation(MethodInvocationTree tree,Object data){
                tree.getMethodSelect().accept(new SimpleTreeVisitor<Object,Object>(){
                    @Override
                    public Object visitMemberSelect(MemberSelectTree tree,Object data){
                    processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,String.format("class:  %1$s\nmethod: %2$s",tree.getExpression(),tree.getIdentifier()));
                    return null;
                    }
                },null);
                return null;
            }
            },null);
        }
        return true;
    }
    }

我使用该处理器处理以下类

package stuff;

import java.util.*;

@MyAnnotation
class MyProgram{
    public void run(){
    System.out.println("Hello World!");
    }
}

并取得了这个成果:

class:  System.out
  method: println

我很确定生成的方法名称是您正在寻找的。我很确定“课程”并不完全符合您的要求,但这是一个非常好的开始。

在我的示例中,您可能希望它为类打印“java.io.PrintStream”。为此,您可以使用processingEnv.getElementUtils().getTypeElement("java.lang.System")来获取表示系统类的TypeElement。然后,您可以使用processingEnv.getElementUtils().getAllMembers()来获取系统类的每个成员。迭代查找out。使用asType方法获取其类型。

前一段是粗略的简化。处理器不知道先验out是隐式导入的java.lang包的类的静态成员。因此,您的代码必须尝试并且无法找到以下类Systemjava.util.System(因为它在导入中),System.outjava.util.System.out和{{1 }}

我只处理了MemberSelect。您将不得不处理其他可能性,包括MethodInvocation。例如,java.lang.System.out应为class = Object,method = hashCode。

答案 1 :(得分:3)

作为@emory的优秀答案的替代方案,您可以考虑使用the Checker Framework提供的可插入类型检查注释处理。它的优点是它可以帮助您轻松确定方法调用者的类型。这是一个基于checker框架的示例处理器(在编译时将checker.jar添加到类路径中)。

@SupportedAnnotationTypes("*")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyTypeProcessor extends AbstractTypeProcessor {
    class MyTreePathScanner extends TreePathScanner<Void, Void> {
        private final Trees trees;
        private final TreePath root;

        public MyTreePathScanner(TreePath root) {
            this.trees = Trees.instance(processingEnv);
            this.root = root;
        }

        @Override
        public Void visitMemberSelect(MemberSelectTree node, Void aVoid) {
            ExpressionTree expression = node.getExpression();
            TreePath expr = TreePath.getPath(root, expression);
            TypeMirror type = trees.getTypeMirror(expr);
            Element typeElement = processingEnv.getTypeUtils().asElement(type);
            Optional<? extends Element> invoker = typeElement.getEnclosedElements().stream().filter(
                    e -> e.getSimpleName().equals(node.getIdentifier())).findFirst();
            if (invoker.isPresent() && invoker.get().getKind() == ElementKind.METHOD) {
                System.out.println("Type: " + typeElement + ", method: " + invoker.get());
            }
            return super.visitMemberSelect(node, aVoid);
        }

    }

    @Override
    public void typeProcess(TypeElement typeElement, TreePath root) {
        new MyTreePathScanner(root).scan(root, null);
    }
}

正在处理以下输入源。

public class Test {

    public void foo() {
    }

    public static void main(String[] args) {
        System.out.println("Hello world!");
        Test t = new Test();
        t.foo();
    }
}

这是输出:

Type: java.io.PrintStream, method: println()
Type: Test, method: foo()