[
https://issues.apache.org/jira/browse/GROOVY-12743?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Scott Murphy Heiberg updated GROOVY-12743:
------------------------------------------
Description:
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.
was:
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); the contracts issue on trait
methods reports them as unsupported. Pull request to follow.
> 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)