Scott Murphy Heiberg created GROOVY-12698:
---------------------------------------------
Summary: STC: annotations on a multiple-assignment declaration are
silently dropped by @CompileStatic
Key: GROOVY-12698
URL: https://issues.apache.org/jira/browse/GROOVY-12698
Project: Groovy
Issue Type: Bug
Affects Versions: 6.0.0
Reporter: Scott Murphy Heiberg
*Affects Version:* 6.0.0 (also the GROOVY_6_0_X branch at 6.0.1-SNAPSHOT and
master at 7.0.0-SNAPSHOT)
*Environment:* macOS 26.5, JDK 21.0.12 (BellSoft Liberica); groovy / groovyc
command line, same result with GroovyShell
h3. Problem
Inside a {{@CompileStatic}} class (or method), annotations placed on a
multiple-assignment declaration such as {{def (a, b) = ...}} are lost before
the class is generated. As a consequence the usual annotation checks are
skipped for that declaration: a plainly misplaced annotation is accepted
without any diagnostic.
{code:groovy}
@groovy.transform.CompileStatic
class C {
void m() {
@Override def (a, b) = [1, 2]
println a + b
}
}
new C().m()
{code}
{noformat}
$ groovy multi.groovy
3
{noformat}
The same declaration in a dynamic class, or a single declaration {{@Override
def a = 1}} in the static class, is rejected as expected:
{noformat}
multi.groovy: 4: Annotation @java.lang.Override is not allowed on element
LOCAL_VARIABLE
@ line 4, column 9.
@Override def (a, b) = [1, 2]
^
1 error
{noformat}
The duplicate-annotation check is skipped in the same way: {{@Deprecated
@Deprecated def (a, b) = [1, 2]}} compiles and runs in a {{@CompileStatic}}
class, while the dynamic class reports "Cannot specify duplicate annotation on
the same member : java.lang.Deprecated".
h3. Expected behaviour
A multiple-assignment declaration under static compilation should keep its
annotations, so that the static class reports the same diagnostics as the
dynamic class:
{noformat}
Annotation @java.lang.Override is not allowed on element LOCAL_VARIABLE
{noformat}
A multiple assignment carrying a valid local-variable annotation (for example
{{@SuppressWarnings('unused') def (a, b) = [1, 2]}}) must of course keep
compiling.
h3. Cause
{{org.codehaus.groovy.transform.sc.transformers.BinaryExpressionTransformer#transformMultipleAssignment}}
(instruction selection phase) rewrites {{def (a, b) = [1, 2]}} into one
{{DeclarationExpression}} per variable ({{def a = 1}}, {{def b = 2}}). Each new
declaration is built with {{new DeclarationExpression(left, operation, right)}}
followed only by {{setSourcePosition(right)}}; the annotations of the original
declaration are not copied, unlike
{{DeclarationExpression#transformExpression}}, which copies them.
{{ExtendedVerifier}} runs later (class generation phase) and performs the
target and duplicate checks on the rebuilt declarations, which by then carry no
annotations. Any later-phase transformation that inspects annotations on local
variable declarations is affected in the same way.
Copying the annotations onto each rebuilt single declaration restores the
checks: the static class then reports the diagnostic once per declared
variable, which is also what {{@Override def a = 1, b = 2}} reports in both
dynamic and static classes. A pull request with regression tests follows.
h3. Reproduces on
Groovy 6.0.0 (release), Groovy 6.0.1-SNAPSHOT (GROOVY_6_0_X at 7c0dcf17dd) and
Groovy 7.0.0-SNAPSHOT (master at d86ea7eb85).
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)