Mattias Reichel created GROOVY-12426:
----------------------------------------
Summary: @Delegate generates no methods returning a nested class
of a joint-compiled Java source, and breaks other implementors of the interface
Key: GROOVY-12426
URL: https://issues.apache.org/jira/browse/GROOVY-12426
Project: Groovy
Issue Type: Bug
Components: Compiler
Affects Versions: 6.0.0
Reporter: Mattias Reichel
In a joint compilation, if a Groovy interface declares methods whose return
type is a static nested class of a Java source in the same compilation,
{{@Delegate}} of that interface generates no delegate methods for them. The
class then fails the abstract-method check.
After {{@Delegate}} has processed the interface, other classes in the same
compilation that implement the interface directly also fail the abstract-method
check, even though they implement the method.
This compiles on Groovy 5.1.3 and fails on 6.0.0. {{@CompileStatic}} is not
needed. A top-level Java class, or a nested class declared in Groovy, works.
h3. Steps to reproduce
{code:title=src/Payload.java}
public class Payload {
public static abstract class Result {
public abstract String text();
}
}
{code}
{code:title=src/Renderer.groovy}
interface Renderer {
Payload.Result render(Map arguments)
}
{code}
{code:title=src/DelegatingRenderer.groovy}
class DelegatingRenderer {
@Delegate final Renderer renderer
DelegatingRenderer(Renderer renderer) { this.renderer = renderer }
}
{code}
{noformat}
mkdir out
java -cp groovy-6.0.0.jar org.codehaus.groovy.tools.FileSystemCompiler -d out
-j src/Payload.java src/Renderer.groovy src/DelegatingRenderer.groovy
{noformat}
h4. Expected
Compilation succeeds, as on Groovy 5.1.3.
h4. Actual
{noformat}
src/DelegatingRenderer.groovy: 1: Can't have an abstract method in a
non-abstract class. The class 'DelegatingRenderer' must be declared abstract or
the method 'Payload$Result render(java.util.Map)' must be implemented.
@ line 1, column 1.
class DelegatingRenderer {
^
1 error
{noformat}
h3. Effect on other implementors of the interface
When the {{@Delegate}} class declares the affected method itself, it compiles,
but a class that implements the interface directly then fails:
{code:title=src/ForwardingRenderer.groovy}
class ForwardingRenderer {
@Delegate final Renderer renderer
ForwardingRenderer(Renderer renderer) { this.renderer = renderer }
Payload.Result render(Map arguments) { renderer.render(arguments) }
}
{code}
{code:title=src/DirectRenderer.groovy}
class DirectRenderer implements Renderer {
Payload.Result render(Map arguments) { null }
}
{code}
{noformat}
mkdir out
java -cp groovy-6.0.0.jar org.codehaus.groovy.tools.FileSystemCompiler -d out
-j src/Payload.java src/Renderer.groovy src/ForwardingRenderer.groovy
src/DirectRenderer.groovy
{noformat}
{noformat}
src/DirectRenderer.groovy: 1: Can't have an abstract method in a non-abstract
class. The class 'DirectRenderer' must be declared abstract or the method
'Payload$Result render(java.util.Map)' must be implemented.
{noformat}
{{DirectRenderer}} compiles on its own, and both files compile together on
5.1.3.
h3. Notes
Found while moving Apache Grails to Groovy 6: {{grails-views-gson}} has
{{@Delegate GrailsJsonViewHelper}} in {{TemplateRenderer}}, and the interface's
{{render(...)}} methods return {{JsonOutput.JsonWritable}}, a nested class of a
Java source in the same module. That failed first in {{TemplateRenderer}}, then
in {{DefaultGrailsJsonViewHelper}}, which implements the interface directly.
The workaround is to write all forwarding methods explicitly instead of using
{{@Delegate}}.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)