我认为我本可以使用@mernst的回应在技术上实现我的目标,所以我很感激这个建议。但是,我发现另一种更适合我的途径,因为我正在开发商业产品并且无法包含Checker框架(其GPL许可证与我们的GPL许可证不兼容)。
在我的解决方案中,我使用自己的“标准”java注释处理器来构建所有用.@Unsafe
然后,我开发了一个javac插件。插件API使您可以轻松找到AST中任何方法的每个调用。通过使用这个问题中的一些提示,我能够从方法调用树AST节点中确定类和方法名称。然后,我将这些方法调用与我创建的早期“列表”进行比较,其中包含注释的方法,并在需要时发出警告。@Unsafe
这是我的javac插件的缩写版本。
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.util.JavacTask;
import com.sun.source.util.Plugin;
import com.sun.source.util.TaskEvent;
import com.sun.source.util.TaskEvent.Kind;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeInfo;
import com.sun.source.util.TaskListener;
import com.sun.source.util.TreeScanner;
public class UnsafePlugin implements Plugin, TaskListener {
@Override
public String getName() {
return "UnsafePlugin";
}
@Override
public void init(JavacTask task, String... args) {
task.addTaskListener(this);
}
@Override
public void finished(TaskEvent taskEvt) {
if (taskEvt.getKind() == Kind.ANALYZE) {
taskEvt.getCompilationUnit().accept(new TreeScanner<Void, Void>() {
@Override
public Void visitMethodInvocation(MethodInvocationTree methodInv, Void v) {
Element method = TreeInfo.symbol((JCTree) methodInv.getMethodSelect());
TypeElement invokedClass = (TypeElement) method.getEnclosingElement();
String className = invokedClass.toString();
String methodName = methodInv.getMethodSelect().toString().replaceAll(".*\\.", "");
System.out.println("Method Invocation: " + className + " : " + methodName);
return super.visitMethodInvocation(methodInv, v);
}
}, null);
}
}
@Override
public void started(TaskEvent taskEvt) {
}
}
注意 - 为了调用 javac 插件,您必须在命令行上提供参数:
javac -processorpath build/unsafe-plugin.jar -Xplugin:UnsafePlugin
此外,您必须在 unsafe-plugin 中有一个文件.jar其中包含插件的完全限定名称:META-INF/services/com.sun.source.util.Plugin
com.unsafetest.javac.UnsafePlugin