I want to add a generic field to some classes at compile time. To this objective, I was implement my own AST annotation and transformation classes by following the official documentation and annotate desired classes with AST annotation.
But I am getting this error at compile time:
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed: /home/.../groovy/Sample.groovy: -1: A transform used a generics containing ClassNode java.util.HashSet for the field x directly. You are not supposed to do this. Please create a new ClassNode referring to the old ClassNode and use the new ClassNode instead of the old one. Otherwise the compiler will create wrong descriptors and a potential NullPointerException in TypeResolver in the OpenJDK. If this is not your own doing, please report this bug to the writer of the transform. @ line -1, column -1.
Have I made a mistake?
Sample codes
For example, suppose I want to add a HashSet<Long>
field, named x
, to every class annotated by MyAST
annotation.
My AST annotation class:
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
@GroovyASTTransformationClass(classes = [MyASTTransformation.class])
public @interface MyAST {
}
My AST transformation class:
@CompileStatic
@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
public class MyASTTransformation implements ASTTransformation {
@Override
public void visit(ASTNode[] nodes, SourceUnit sourceUnit) {
ClassNode clazz = (ClassNode) nodes[1];
ClassNode longHashSetClass = new ClassNode(HashSet.class);
longHashSetClass.setGenericsTypes([new GenericsType(new ClassNode(Long.class))] as GenericsType[]);
FieldNode field = new FieldNode("x", FieldNode.ACC_PRIVATE, longHashSetClass, clazz, new ConstantExpression(null));
clazz.addField(field);
}
}
Sample annotated class:
@MyAST
public class Sample {
}
Note
When I eliminate the line longHashSetClass.setGenericsTypes([new GenericsType(new ClassNode(Long.class))] as GenericsType[]);
, everything is OK but type of x
is HashSet
instead of HashSet<Long>
at runtime.
You should use ClassHelper
or GenericUtils
in order to create a ClassNode :
import static org.codehaus.groovy.ast.ClassHelper.make
import static org.codehaus.groovy.ast.tools.GenericsUtils.makeClassSafeWithGenerics
...
ClassNode hashSet = makeClassSafeWithGenerics(HashSet, make(Long))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With