[ 
https://issues.apache.org/jira/browse/GROOVY-12743?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18140812#comment-18140812
 ] 

ASF GitHub Bot commented on GROOVY-12743:
-----------------------------------------

codeconsole opened a new pull request, #3278:
URL: https://github.com/apache/groovy/pull/3278

   Fixes [GROOVY-12743](https://issues.apache.org/jira/browse/GROOVY-12743).
   
   Any annotation whose member is given a closure, placed on a trait method 
with a body, crashed the compiler with `UnsupportedOperationException` from 
`AbstractList.add` when a class implemented the trait. 
`TraitComposer.createForwarderMethod` passed `Collections.emptyList()` as the 
"not copied" list to `GeneralUtils.copyAnnotatedNodeAnnotations`, which adds 
every annotation with a closure member to that list (the closure is still a 
`ClosureExpression` in canonicalization; it becomes a class only at class 
generation). `TraitASTTransformation.createInterfaceStaticForwarder` already 
passes a real list.
   
   The forwarder path now passes a real list as well. Behaviour is otherwise 
unchanged: the annotation stays on the trait method and the helper method and 
is, as before for closure members, not copied to the forwarder.
   
   The groovy-contracts annotations `@ThrowsIf` and `@Decreases` on a trait 
method hit this crash first; whether they can be enforced on a trait method is 
a separate question that 
[GROOVY-12741](https://issues.apache.org/jira/browse/GROOVY-12741) answers by 
rejecting them for now.
   
   Tests: a new test in `TraitASTTransformationTest` compiles a trait method 
carrying a closure-member annotation and checks the annotation on the trait and 
helper methods and its absence on the forwarder; it fails on master with the 
exception above. `TraitASTTransformationTest` passes with the fix (294 tests), 
and so does the `groovy-contracts` module.
   




> An annotation with a closure member on a trait method fails with 
> UnsupportedOperationException in TraitComposer (seen with groovy-contracts 
> @ThrowsIf and @Decreases)
> ---------------------------------------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: GROOVY-12743
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12743
>             Project: Groovy
>          Issue Type: Bug
>    Affects Versions: 6.0.0
>            Reporter: Scott Murphy Heiberg
>            Priority: Major
>
> h3. Problem
> Any annotation whose member is given a closure, placed on a trait method with 
> a body, crashes the compiler when a class implements the trait:
> {code:groovy}
> import java.lang.annotation.*
> @Retention(RetentionPolicy.RUNTIME)
> @Target(ElementType.METHOD)
> @interface Check { Class value() }
> trait T {
>     @Check({ it > 0 }) int m() { 1 }
> }
> class C implements T {}
> {code}
> {noformat}
> General error during semantic analysis: 
> java.lang.UnsupportedOperationException
>       at java.base/java.util.AbstractList.add(AbstractList.java:155)
>       at 
> org.codehaus.groovy.ast.tools.GeneralUtils.copyAnnotatedNodeAnnotations(GeneralUtils.java:1867)
>       at 
> org.codehaus.groovy.transform.trait.TraitComposer.createForwarderMethod(TraitComposer.java:358)
>       at 
> org.codehaus.groovy.transform.trait.TraitComposer.applyTrait(TraitComposer.java:167)
> {noformat}
> The groovy-contracts annotations {{@ThrowsIf}} and {{@Decreases}} on a trait 
> method with a body fail the same way. Reproduced on Groovy 6.0.0 and on 
> master (df1fe66ed1).
> h3. Cause
> {{TraitComposer.createForwarderMethod}} passes an unmodifiable empty list as 
> the "not copied" annotations to 
> {{GeneralUtils.copyAnnotatedNodeAnnotations}}, with a comment that it should 
> always stay empty. The closure of an annotation member is still a 
> {{ClosureExpression}} in the canonicalization phase (it becomes a class only 
> at class generation), so the helper adds the annotation to that list and 
> {{AbstractList.add}} throws. 
> {{TraitASTTransformation.createInterfaceStaticForwarder}} already uses a real 
> list.
> h3. Fix
> {{TraitComposer}} uses a real list; the annotation stays on the trait method 
> and the helper method and is, as before for closure members, not copied to 
> the forwarder. Whether {{@ThrowsIf}} and {{@Decreases}} can then be enforced 
> on a trait method is a groovy-contracts question (their transforms run after 
> the trait transform has rewritten the body, so a condition that refers to 
> trait state has no receiver in the helper class); GROOVY-12741 reports them 
> as unsupported. Pull request to follow.



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to