由于特定原因,我想使用 Checker Framework 及其子类型检查器。为了使这个检查器工作,我必须使用ElementType.TYPE_PARAMETER
and ElementType.TYPE_USE
。但是,我想在编译为类文件之前将它们从局部变量中删除。
例如,假设我有以下带有自定义@FirstName
和的代码@LastName
(两者都必须在类级别保留RetentionPolicy.CLASS
):
@FirstName String firstName = ...;
@LastName String lastName = ...;
...
firstName = lastName; // illegal, the error is generated by Checker Framework because the first name cannot be assigned to the last name
但出于另一个原因,我想从“at”字节码级别的局部变量中删除注释,就好像源代码只是:
String firstName = ...;
String lastName = ...;
...
firstName = lastName; // totally fine and legal in Java
如果我了解它可以完成的方式,注释处理是一种方法。因此,如果这是正确的做法,那么我必须按以下顺序链接一些注释处理器:
org.checkerframework.common.subtyping.SubtypingChecker
.- 我的自定义“删除局部变量注释”注释处理器;
好吧,深入研究javac
工作原理对我来说是一个极大的挑战。到目前为止我已经实施的是:
@SupportedOptions(RemoveLocalVariableAnnotationsProcessor.ANNOTATIONS_OPTION)
@SupportedAnnotationTypes("*")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public final class RemoveLocalVariableAnnotationsProcessor
extends AbstractProcessor {
private static final Pattern commaPattern = Pattern.compile(",");
static final String ANNOTATIONS_OPTION = "RemoveLocalVariableAnnotationsProcessor.annotations";
@Nonnull
private Predicate<? super Class<? extends Annotation>> annotationClasses = clazz -> false;
@Override
public void init(@Nonnull final ProcessingEnvironment environment) {
super.init(environment);
final Messager messager = environment.getMessager();
final Map<String, String> options = environment.getOptions();
@Nullable
final String annotationsOption = options.get(ANNOTATIONS_OPTION);
if ( annotationsOption != null ) {
annotationClasses = commaPattern.splitAsStream(annotationsOption)
.<Class<? extends Annotation>>flatMap(className -> {
try {
@SuppressWarnings("unchecked")
final Class<? extends Annotation> clazz = (Class<? extends Annotation>) Class.forName(className);
if ( !clazz.isAnnotation() ) {
messager.printMessage(Diagnostic.Kind.WARNING, "Not an annotation: " + className);
return Stream.empty();
}
return Stream.of(clazz);
} catch ( final ClassNotFoundException ex ) {
messager.printMessage(Diagnostic.Kind.WARNING, "Cannot find " + className);
return Stream.empty();
}
})
.collect(Collectors.collectingAndThen(Collectors.toSet(), Collections::unmodifiableSet))
::contains;
}
final Trees trees = Trees.instance(environment);
final JavacTask javacTask = JavacTask.instance(environment);
javacTask.addTaskListener(new RemoverTaskListener(trees, messager));
}
@Override
public boolean process(final Set<? extends TypeElement> annotations, final RoundEnvironment environment) {
// do nothing: ElementType.TYPE_USE and ElementType.TYPE_PARAMETER seem to be unable to be analyzed here
return false;
}
private static final class RemoverTaskListener
implements TaskListener {
private final Trees trees;
private final Messager messager;
private RemoverTaskListener(final Trees trees, final Messager messager) {
this.trees = trees;
this.messager = messager;
}
@Override
public void started(final TaskEvent taskEvent) {
if ( taskEvent.getKind() == TaskEvent.Kind.ANALYZE ) {
final TreeScanner<?, ?> remover = new Remover(trees, messager);
remover.scan(taskEvent.getCompilationUnit(), null);
}
}
@Override
public void finished(final TaskEvent taskEvent) {
// do nothing
}
private static final class Remover
extends TreePathScanner<Void, Void> {
private final Trees trees;
private final Messager messager;
private Remover(final Trees trees, final Messager messager) {
this.trees = trees;
this.messager = messager;
}
@Override
public Void visitVariable(final VariableTree variableTree, final Void nothing) {
super.visitVariable(variableTree, nothing);
final Symbol symbol = (Symbol) trees.getElement(trees.getPath(getCurrentPath().getCompilationUnit(), variableTree));
if ( !symbol.hasTypeAnnotations() || symbol.getKind() != ElementKind.LOCAL_VARIABLE ) {
return nothing;
}
final List<? extends AnnotationTree> annotationTrees = variableTree.getModifiers().getAnnotations();
if ( annotationTrees.isEmpty() ) {
return nothing;
}
messager.printMessage(Diagnostic.Kind.WARNING, "TODO: " + symbol);
for ( final AnnotationTree annotationTree : annotationTrees ) {
// TODO how to align AnnotationTree and java.lang.annotation.Annotation?
// TODO how to remove the annotation from the local variable?
}
return nothing;
}
}
}
}
如您所见,它没有按预期工作。
从局部变量中删除注释的正确方法是什么?我的意思是,我该如何实现它?javac
如果可能的话,由于 Maven 构建集成的细节,我想坚持使用注释处理器。