You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2020/06/25 09:48:42 UTC

[groovy] branch GROOVY_2_5_X updated (9fb05cc6 -> e7ab52e)

This is an automated email from the ASF dual-hosted git repository.

paulk pushed a change to branch GROOVY_2_5_X
in repository https://gitbox.apache.org/repos/asf/groovy.git.


    from 9fb05cc6 TEST: show actual output when test fails
     new 95a10c8  GROOVY-9031: correct trait property generics before writing stub methods (port to 2_5_X)
     new e7ab52e  GROOVY-9606: Traits using generics generate incorrect stub for Methods (port to 2_5_X)

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../codehaus/groovy/ast/tools/GenericsUtils.java   |  2 +
 .../groovy/tools/javac/JavaStubGenerator.java      | 40 ++++++++++--
 .../{Groovy7721Bug.groovy => Groovy9031.groovy}    | 76 ++++++++++------------
 src/test/groovy/bugs/Groovy9606.groovy             | 74 +++++++++++++++++++++
 4 files changed, 145 insertions(+), 47 deletions(-)
 copy src/test/groovy/bugs/{Groovy7721Bug.groovy => Groovy9031.groovy} (51%)
 create mode 100644 src/test/groovy/bugs/Groovy9606.groovy


[groovy] 01/02: GROOVY-9031: correct trait property generics before writing stub methods (port to 2_5_X)

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch GROOVY_2_5_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 95a10c899c8b2e7d6f4d86c1f45eb745885f406c
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Mon Aug 12 14:43:57 2019 -0500

    GROOVY-9031: correct trait property generics before writing stub methods (port to 2_5_X)
---
 .../groovy/tools/javac/JavaStubGenerator.java      | 33 +++++++++-
 src/test/groovy/bugs/Groovy9031.groovy             | 72 ++++++++++++++++++++++
 2 files changed, 102 insertions(+), 3 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java b/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
index c99cf43..e38cc64 100644
--- a/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
+++ b/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
@@ -63,10 +63,17 @@ import java.io.Writer;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+
+import static org.codehaus.groovy.ast.tools.GenericsUtils.correctToGenericsSpec;
+import static org.codehaus.groovy.ast.tools.GenericsUtils.createGenericsSpec;
 
 public class JavaStubGenerator {
     private final boolean java5;
@@ -147,17 +154,37 @@ public class JavaStubGenerator {
         try {
             Verifier verifier = new Verifier() {
                 @Override
-                public void visitClass(final ClassNode node) {
-                    List<Statement> savedStatements = new ArrayList<Statement>(node.getObjectInitializerStatements());
+                public void visitClass(ClassNode node) {
+                    List<Statement> savedStatements = new ArrayList<>(node.getObjectInitializerStatements());
                     super.visitClass(node);
                     node.getObjectInitializerStatements().addAll(savedStatements);
-                    for (ClassNode trait : Traits.findTraits(node)) {
+
+                    for (ClassNode trait : findTraits(node)) {
+                        // GROOVY-9031: replace property type placeholder with resolved type from trait generics
+                        Map<String, ClassNode> generics = trait.isUsingGenerics() ? createGenericsSpec(trait) : null;
                         for (PropertyNode traitProperty : trait.getProperties()) {
+                            ClassNode traitPropertyType = traitProperty.getType();
+                            traitProperty.setType(correctToGenericsSpec(generics, traitPropertyType));
                             super.visitProperty(traitProperty);
+                            traitProperty.setType(traitPropertyType);
                         }
                     }
                 }
 
+                private Iterable<ClassNode> findTraits(ClassNode node) {
+                    Set<ClassNode> traits = new LinkedHashSet<>();
+
+                    LinkedList<ClassNode> todo = new LinkedList<>();
+                    Collections.addAll(todo, node.getInterfaces());
+                    while (!todo.isEmpty()) {
+                        ClassNode next = todo.removeLast();
+                        if (Traits.isTrait(next)) traits.add(next);
+                        Collections.addAll(todo, next.getInterfaces());
+                    }
+
+                    return traits;
+                }
+
                 @Override
                 protected FinalVariableAnalyzer.VariableNotFinalCallback getFinalVariablesCallback() {
                     return null;
diff --git a/src/test/groovy/bugs/Groovy9031.groovy b/src/test/groovy/bugs/Groovy9031.groovy
new file mode 100644
index 0000000..8a1aba4
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy9031.groovy
@@ -0,0 +1,72 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.bugs
+
+import org.codehaus.groovy.control.CompilerConfiguration
+import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit
+import org.junit.Test
+
+final class Groovy9031 {
+
+    @Test
+    void testGenerics() {
+        def config = new CompilerConfiguration(
+            targetDirectory: File.createTempDir(),
+            jointCompilationOptions: [stubDir: File.createTempDir(), keepStubs: true]
+        )
+
+        def parentDir = File.createTempDir()
+        try {
+            def a = new File(parentDir, 'Trait.groovy')
+            a.write '''
+                trait Trait<V> {
+                    V value
+                }
+            '''
+            def b = new File(parentDir, 'TraitImpl.groovy')
+            b.write '''
+                class TraitImpl implements Trait<String> {
+                }
+            '''
+            def c = new File(parentDir, 'Whatever.java')
+            c.write '''
+                class Whatever {
+                    void meth() {
+                        new TraitImpl().getValue();
+                    }
+                }
+            '''
+
+            def loader = new GroovyClassLoader(this.class.classLoader)
+            def cu = new JavaAwareCompilationUnit(config, loader)
+            cu.addSources(a, b, c)
+            cu.compile()
+
+            def stub = new File(config.jointCompilationOptions.stubDir, 'TraitImpl.java')
+            def text = stub.text
+
+            assert text.contains('java.lang.String getValue()')
+            assert text.contains('void setValue(java.lang.String value)')
+        } finally {
+            parentDir.deleteDir()
+            config.targetDirectory.deleteDir()
+            config.jointCompilationOptions.stubDir.deleteDir()
+        }
+    }
+}


[groovy] 02/02: GROOVY-9606: Traits using generics generate incorrect stub for Methods (port to 2_5_X)

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch GROOVY_2_5_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit e7ab52ee9ca9a38ae2f13289f02c412b0b386d98
Author: Paul King <pa...@asert.com.au>
AuthorDate: Wed Jun 24 22:25:47 2020 +1000

    GROOVY-9606: Traits using generics generate incorrect stub for Methods (port to 2_5_X)
---
 .../codehaus/groovy/ast/tools/GenericsUtils.java   |  2 +
 .../groovy/tools/javac/JavaStubGenerator.java      | 35 +++++-----
 src/test/groovy/bugs/Groovy9606.groovy             | 74 ++++++++++++++++++++++
 3 files changed, 95 insertions(+), 16 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
index 38049ae..6abc821 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
@@ -324,6 +324,8 @@ public class GenericsUtils {
     }
 
     public static MethodNode correctToGenericsSpec(Map<String, ClassNode> genericsSpec, MethodNode mn) {
+        if (genericsSpec == null) return mn;
+        if (mn.getGenericsTypes() != null) genericsSpec = addMethodGenerics(mn, genericsSpec);
         ClassNode correctedType = correctToGenericsSpecRecurse(genericsSpec, mn.getReturnType());
         Parameter[] origParameters = mn.getParameters();
         Parameter[] newParameters = new Parameter[origParameters.length];
diff --git a/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java b/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
index e38cc64..ba6e1af 100644
--- a/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
+++ b/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
@@ -146,6 +146,20 @@ public class JavaStubGenerator {
         }
     }
 
+    private static Iterable<ClassNode> findTraits(ClassNode node) {
+        Set<ClassNode> traits = new LinkedHashSet<>();
+
+        LinkedList<ClassNode> todo = new LinkedList<>();
+        Collections.addAll(todo, node.getInterfaces());
+        while (!todo.isEmpty()) {
+            ClassNode next = todo.removeLast();
+            if (Traits.isTrait(next)) traits.add(next);
+            Collections.addAll(todo, next.getInterfaces());
+        }
+
+        return traits;
+    }
+
     private void printClassContents(PrintWriter out, ClassNode classNode) throws FileNotFoundException {
         if (classNode instanceof InnerClassNode && ((InnerClassNode) classNode).isAnonymous()) {
             // if it is an anonymous inner class, don't generate the stub code for it.
@@ -171,20 +185,6 @@ public class JavaStubGenerator {
                     }
                 }
 
-                private Iterable<ClassNode> findTraits(ClassNode node) {
-                    Set<ClassNode> traits = new LinkedHashSet<>();
-
-                    LinkedList<ClassNode> todo = new LinkedList<>();
-                    Collections.addAll(todo, node.getInterfaces());
-                    while (!todo.isEmpty()) {
-                        ClassNode next = todo.removeLast();
-                        if (Traits.isTrait(next)) traits.add(next);
-                        Collections.addAll(todo, next.getInterfaces());
-                    }
-
-                    return traits;
-                }
-
                 @Override
                 protected FinalVariableAnalyzer.VariableNotFinalCallback getFinalVariablesCallback() {
                     return null;
@@ -348,9 +348,12 @@ public class JavaStubGenerator {
         }
 
         // print the methods from traits
-        for (ClassNode trait : Traits.findTraits(classNode)) {
+        for (ClassNode trait : findTraits(classNode)) {
+            Map<String, ClassNode> generics = trait.isUsingGenerics() ? createGenericsSpec(trait) : null;
             List<MethodNode> traitMethods = trait.getMethods();
-            for (MethodNode traitMethod : traitMethods) {
+            for (MethodNode traitOrigMethod : traitMethods) {
+                // GROOVY-9606: replace method return type and parameter type placeholder with resolved type from trait generics
+                MethodNode traitMethod = correctToGenericsSpec(generics, traitOrigMethod);
                 MethodNode existingMethod = classNode.getMethod(traitMethod.getName(), traitMethod.getParameters());
                 if (existingMethod != null) continue;
                 for (MethodNode propertyMethod : propertyMethods) {
diff --git a/src/test/groovy/bugs/Groovy9606.groovy b/src/test/groovy/bugs/Groovy9606.groovy
new file mode 100644
index 0000000..b158310
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy9606.groovy
@@ -0,0 +1,74 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.bugs
+
+import org.codehaus.groovy.control.CompilerConfiguration
+import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit
+import org.junit.Test
+
+final class Groovy9606 {
+
+    @Test
+    void testGenerics() {
+        def config = new CompilerConfiguration(
+            targetDirectory: File.createTempDir(),
+            jointCompilationOptions: [
+                    stubDir: File.createTempDir(),
+                    keepStubs: true   // keep stubs to read the stubs later in this test
+            ]
+        )
+
+        def parentDir = File.createTempDir()
+        try {
+            def a = new File(parentDir, 'SomeGroovyTrait.groovy')
+            a.write '''
+                trait SomeGroovyTrait<D> {
+                    List<D> list(List<D> front, D last) {
+                        front + [last]
+                    }
+                }
+            '''
+            def b = new File(parentDir, 'SomeGroovyClass.groovy')
+            b.write '''
+                class SomeGroovyClass implements SomeGroovyTrait<String> { }
+            '''
+            def c = new File(parentDir, 'SomeJavaClass.java')
+            c.write '''
+                public class SomeJavaClass {
+                    SomeGroovyClass sgc;
+                }
+            '''
+
+            def loader = new GroovyClassLoader(this.class.classLoader)
+            def cu = new JavaAwareCompilationUnit(config, loader)
+            cu.addSources(a, b, c)
+            cu.compile()
+
+            def stub = new File(config.jointCompilationOptions.stubDir, 'SomeGroovyClass.java')
+            def text = stub.text
+
+            assert text.contains('java.util.List<java.lang.String> list')
+            assert text.contains('java.util.List<java.lang.String> front, java.lang.String last')
+        } finally {
+            parentDir.deleteDir()
+            config.targetDirectory.deleteDir()
+            config.jointCompilationOptions.stubDir.deleteDir()
+        }
+    }
+}