Paul King created GROOVY-12701:
----------------------------------

             Summary: @ASTTest on a declaration with several declarators fails 
with "BUG! ... Tried to overwrite existing meta data"
                 Key: GROOVY-12701
                 URL: https://issues.apache.org/jira/browse/GROOVY-12701
             Project: Groovy
          Issue Type: Bug
          Components: Compiler
    Affects Versions: 6.0.0, 5.1.3, 4.0.32
            Reporter: Paul King


h3. Description

{{@ASTTest}} on a local variable declaration or field declaration with more 
than one declarator makes the compiler fail with a {{GroovyBugError}}, whatever 
the test closure does:

{code:groovy}
import groovy.transform.ASTTest
import org.codehaus.groovy.ast.expr.DeclarationExpression

def m() {
    @ASTTest(value={
        assert node instanceof DeclarationExpression
    })
    def a = 1, b = 2
    a + b
}
println m()
{code}

{noformat}
BUG! exception in phase 'semantic analysis' in source unit '...' Tried to 
overwrite existing meta data 
org.codehaus.groovy.ast.AnnotationNode@...[@groovy.transform.ASTTest(phase=..., 
value={ ... })].
{noformat}

The same happens for fields:

{code:groovy}
import groovy.transform.ASTTest

class F {
    @ASTTest(value={
        assert node.name in ['x', 'y']
    })
    int x = 1, y = 2
}
{code}

and for any {{phase}}. A single declarator ({{def a = 1}}) and a multiple 
assignment ({{def (a, b) = [1, 2]}}) work.

Expected: the test closure runs once for each declared variable or field, with 
{{node}} bound to that declaration, as other local AST transformations are 
invoked for each declarator.

Reproduced with 4.0.32, 5.1.3, 6.0.0 and master.

h3. Cause

The parser attaches the same {{AnnotationNode}} instance to every declarator of 
the declaration ({{def a = 1, b = 2}} becomes two {{DeclarationExpression}}s, 
{{int x = 1, y = 2}} two {{FieldNode}}s, sharing the annotation). 
{{ASTTransformationVisitor}} therefore calls {{ASTTestTransformation#visit}} 
once per declarator with the same annotation node.

On the first call, {{ASTTestTransformation}} moves the test closure out of the 
annotation into node metadata and replaces the {{value}} member with an empty 
closure:

{code:groovy}
annotationNode.setNodeMetaData(ASTTestTransformation, member)
annotationNode.setMember('value', new ClosureExpression(Parameter.EMPTY_ARRAY, 
EmptyStatement.INSTANCE))
{code}

On the second call {{setNodeMetaData}} finds the key already set and throws. 
Even if it did not, it would store the empty replacement closure as the test.

h3. Suggested fix

In {{ASTTestTransformation#visit}}, do the conversion only the first time an 
annotation node is seen: if the node metadata is already set, reuse the stored 
test closure, skip the {{value}} checks and the replacement, and register an 
{{ASTTester}} for the current node ({{nodes[1]}}). The {{phase}} member is 
already rewritten into a form that the second call reads correctly.

This is a general hazard for local transformations that keep state on the 
annotation node: with several declarators, the same annotation node is passed 
for each annotated node.



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

Reply via email to