Scott Murphy Heiberg created GROOVY-12743:
---------------------------------------------
Summary: 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
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.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)