You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2019/08/13 08:08:28 UTC

[groovy] branch master updated: GROOVY-9031: correct trait property generics before writing stub methods

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

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


The following commit(s) were added to refs/heads/master by this push:
     new d321754  GROOVY-9031: correct trait property generics before writing stub methods
d321754 is described below

commit d321754e8cf7054c5bcf89c56c2eadfc0c01916e
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
---
 .../groovy/tools/javac/JavaStubGenerator.java      | 32 +++++++++-
 src/test/groovy/bugs/Groovy9031.groovy             | 72 ++++++++++++++++++++++
 2 files changed, 101 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 71049ec..879064f 100644
--- a/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
+++ b/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
@@ -67,13 +67,19 @@ 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.HashSet;
 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;
     private final String encoding;
@@ -171,17 +177,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
                 public void visitConstructor(ConstructorNode node) {
                     Statement stmt = node.getCode();
diff --git a/src/test/groovy/bugs/Groovy9031.groovy b/src/test/groovy/bugs/Groovy9031.groovy
new file mode 100644
index 0000000..6af7740
--- /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()]
+        )
+
+        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()
+        }
+    }
+}