Is there an instance in the core language/runtime where a closure literal is used as part of an annotation but not for an AST transformation? I'm not sure what the compiler does when it encounters a closure expression as the value for an annotation attribute (of type Class). But the AST transforms that do this -- ASTTest and AutoImplement come to mind -- take the closure code and use it to generate methods. I think GContracts is this way as well.
After reading your example more closely, I see that a single-valued annotation attribute works as expected but the multi-value case fails. From: Remko Popma <remko.po...@gmail.com> Sent: Monday, November 16, 2020 7:02 PM To: Groovy_Developers <dev@groovy.apache.org> Subject: Re: Closures in annotations I’m probably overlooking something simple but I’m not seeing it yet. The below code demonstrates the issue when trying to pass a Groovy closure to the @Option(converter = ...)attribute: class ClosureTest { static class Demo { @picocli.CommandLine.Option(names = "-x", completionCandidates = {["A", "B", "C"]}, converter = [{ str -> java.security.MessageDigest.getInstance(str) }]) java.security.MessageDigest digest } static void main(String[] args) { def annotation = Demo.class.getDeclaredField("digest").getAnnotation(picocli.CommandLine.Option) Class ok = annotation.completionCandidates() assert ok != null assert Closure.class.isAssignableFrom(ok) assert ["A", "B", "C"] == ((Closure) ok.getConstructor(Object, Object).newInstance(null, null)).call() Class[] bad = annotation.converter() assert bad != null assert bad.length == 1 // this assert fails: //Exception in thread "main" Assertion failed: // //assert bad.length == 1 // | | | // [] 0 false // // at org.codehaus.groovy.runtime.InvokerHelper.assertFailed(InvokerHelper.java:434) // at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.assertFailed(ScriptBytecodeAdapter.java:670) // at closure.ClosureTest.main(ClosureTest.groovy:18) } } On Mon, Nov 16, 2020 at 21:16 Remko Popma <remko.po...@gmail.com<mailto:remko.po...@gmail.com>> wrote: PS The ITypeConverter interface definition is here: https://picocli.info/apidocs/picocli/CommandLine.ITypeConverter.html<https://nam02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fpicocli.info%2Fapidocs%2Fpicocli%2FCommandLine.ITypeConverter.html&data=04%7C01%7Ceric.milles%40thomsonreuters.com%7C1c5e266b8280484ce44c08d88a94782d%7C62ccb8646a1a4b5d8e1c397dec1a8258%7C0%7C0%7C637411717560284723%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=FNAGjAoww3EIKmOnW1kRvE3ZDTQF7k5Ar0Hbo0oHi2k%3D&reserved=0> On Mon, Nov 16, 2020 at 21:08 Remko Popma <remko.po...@gmail.com<mailto:remko.po...@gmail.com>> wrote: Hi all, I have a question about passing closures to annotations in Groovy. To illustrate, consider the @Option annotation in the picocli library. Relevant attributes are `completionCandidates` and `converter`, defined in Java as follows: @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) public @interface Option { Class<? extends ITypeConverter<?>>[] converter() default {}; Class<? extends Iterable<String>> completionCandidates() default NoCompletionCandidates.class; ... } I am working on a change to picocli<https://nam02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fremkop%2Fpicocli%2Fissues%2F1258&data=04%7C01%7Ceric.milles%40thomsonreuters.com%7C1c5e266b8280484ce44c08d88a94782d%7C62ccb8646a1a4b5d8e1c397dec1a8258%7C0%7C0%7C637411717560284723%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=AzDZ2%2F5vCf88nV%2BgT3r%2BKBTYW%2BGrrnHUuaduX6ibIsM%3D&reserved=0> that would allow users to specify closures for these and other attributes. User code could look like this: @Option(names = '-s', completionCandidates = {["A", "B", "C"]}) @Field String s @Option(names = '-a', converter = [{ str -> MessageDigest.getInstance(str) }] ) @Field MessageDigest algorithm I think this would be a nice addition and would make picocli more "groovy". I have a prototype implementation, but it appears that only the first example ( completionCandidates = {["A", "B", "C"]} ) works as expected. When stepping through my prototype test in a debugger, it looks like the second example (the converter attribute) receives a zero-length array of classes when invoked from Groovy. I tried with Groovy 2.4.10 and 3.0.6. Is this a known limitation of Groovy? Is there a way to work around this? I can provide an example project to reproduce this if that is helpful. Kind regards, Remko