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)

Reply via email to