[groovy] branch GROOVY_2_5_X updated: GROOVY-9822: check for empty spec before recursive application (port to 2_5_X)

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[groovy] branch GROOVY_2_5_X updated: GROOVY-9822: check for empty spec before recursive application (port to 2_5_X)

paulk
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


The following commit(s) were added to refs/heads/GROOVY_2_5_X by this push:
     new 316dcc5  GROOVY-9822: check for empty spec before recursive application (port to 2_5_X)
316dcc5 is described below

commit 316dcc5ea8d4abf5f79781d82d32411b0b17eabb
Author: Eric Milles <[hidden email]>
AuthorDate: Thu Nov 19 11:55:29 2020 -0600

    GROOVY-9822: check for empty spec before recursive application (port to 2_5_X)
---
 .../groovy/tools/javac/JavaStubGenerator.java      |  2 +-
 .../transform/stc/StaticTypeCheckingSupport.java   | 65 +++++++++----------
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 72 +++++++++++++++++++++-
 3 files changed, 106 insertions(+), 33 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 ba6e1af..ff3171b 100644
--- a/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
+++ b/src/main/java/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
@@ -91,7 +91,7 @@ public class JavaStubGenerator {
         this.requireSuperResolved = requireSuperResolved;
         this.java5 = java5;
         this.encoding = encoding;
-        outputPath.mkdirs();
+        if (null != outputPath) outputPath.mkdirs();
     }
 
     public JavaStubGenerator(final File outputPath) {
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
index ef9846d..8f9a67e 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -111,6 +111,7 @@ import static org.codehaus.groovy.ast.ClassHelper.short_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.void_WRAPPER_TYPE;
 import static org.codehaus.groovy.ast.GenericsType.GenericsTypeName;
 import static org.codehaus.groovy.ast.tools.GenericsUtils.getSuperClass;
+import static org.codehaus.groovy.runtime.DefaultGroovyMethods.asBoolean;
 import static org.codehaus.groovy.syntax.Types.ASSIGN;
 import static org.codehaus.groovy.syntax.Types.BITWISE_AND;
 import static org.codehaus.groovy.syntax.Types.BITWISE_AND_EQUAL;
@@ -1926,42 +1927,44 @@ public abstract class StaticTypeCheckingSupport {
                                 || !OBJECT_TYPE.equals(upperBounds[0])));
     }
 
-    private static ClassNode[] applyGenericsContext(
-            Map<GenericsTypeName, GenericsType> spec, ClassNode[] bounds
-    ) {
-        if (bounds == null) return null;
-        ClassNode[] newBounds = new ClassNode[bounds.length];
-        for (int i = 0; i < bounds.length; i++) {
-            newBounds[i] = applyGenericsContext(spec, bounds[i]);
+    static ClassNode[] applyGenericsContext(final Map<GenericsTypeName, GenericsType> spec, final ClassNode[] types) {
+        if (types == null) return null;
+        final int nTypes = types.length;
+        ClassNode[] newTypes = new ClassNode[nTypes];
+        for (int i = 0; i < nTypes; i += 1) {
+            newTypes[i] = applyGenericsContext(spec, types[i]);
         }
-        return newBounds;
+        return newTypes;
     }
 
-    static ClassNode applyGenericsContext(
-            Map<GenericsTypeName, GenericsType> spec, ClassNode bound
-    ) {
-        if (bound == null) return null;
-        if (bound.isArray()) {
-            return applyGenericsContext(spec, bound.getComponentType()).makeArray();
-        }
-        if (!bound.isUsingGenerics()) return bound;
-        ClassNode newBound = bound.getPlainNodeReference();
-        newBound.setGenericsTypes(applyGenericsContext(spec, bound.getGenericsTypes()));
-        if (bound.isGenericsPlaceHolder()) {
-            GenericsType[] gt = newBound.getGenericsTypes();
-            boolean hasBounds = hasNonTrivialBounds(gt[0]);
-            if (hasBounds || !gt[0].isPlaceholder()) return getCombinedBoundType(gt[0]);
-            String placeHolderName = newBound.getGenericsTypes()[0].getName();
-            if (!placeHolderName.equals(newBound.getUnresolvedName())) {
-                // we should produce a clean placeholder ClassNode here
-                ClassNode clean = make(placeHolderName);
-                clean.setGenericsTypes(newBound.getGenericsTypes());
-                clean.setRedirect(newBound);
-                newBound = clean;
+    static ClassNode applyGenericsContext(final Map<GenericsTypeName, GenericsType> spec, final ClassNode type) {
+        if (type == null || !isUsingGenericsOrIsArrayUsingGenerics(type)) {
+            return type;
+        }
+        if (type.isArray()) {
+            return applyGenericsContext(spec, type.getComponentType()).makeArray();
+        }
+        ClassNode newType = type.getPlainNodeReference();
+        GenericsType[] gt = type.getGenericsTypes();
+        if (asBoolean(spec)) {
+            gt = applyGenericsContext(spec, gt);
+        }
+        newType.setGenericsTypes(gt);
+        if (type.isGenericsPlaceHolder()) {
+            boolean nonTrivial = hasNonTrivialBounds(gt[0]);
+            if (nonTrivial || !gt[0].isPlaceholder()) {
+                return getCombinedBoundType(gt[0]);
             }
-            newBound.setGenericsPlaceHolder(true);
+            String placeholderName = gt[0].getName();
+            if (!placeholderName.equals(newType.getUnresolvedName())) {
+                ClassNode clean = make(placeholderName);
+                clean.setGenericsTypes(gt);
+                clean.setRedirect(newType);
+                newType = clean;
+            }
+            newType.setGenericsPlaceHolder(true);
         }
-        return newBound;
+        return newType;
     }
 
     private static ClassNode getCombinedBoundType(GenericsType genericsType) {
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index cf6880b..a22046e 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -19,6 +19,7 @@
 package groovy.transform.stc
 
 import groovy.transform.NotYetImplemented
+import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit
 
 /**
  * Unit tests for static type checking : generics.
@@ -1231,7 +1232,76 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
               }
             }
             new Element()
-'''
+        '''
+
+        // GROOVY-9822
+        config.with {
+            targetDirectory = createTempDir()
+            jointCompilationOptions = [stubDir: createTempDir()]
+        }
+        File parentDir = createTempDir()
+        try {
+            def a = new File(parentDir, 'Types.java')
+            a.write '''
+                import java.io.*;
+                import java.util.*;
+
+                // from org.apache.tinkerpop:gremlin-core:3.4.8
+
+                interface TraversalStrategy<S extends TraversalStrategy> extends Serializable, Comparable<Class<? extends TraversalStrategy>> {
+                    interface VerificationStrategy extends TraversalStrategy<VerificationStrategy> {
+                    }
+                }
+                abstract class AbstractTraversalStrategy<S extends TraversalStrategy> implements TraversalStrategy<S> {
+                }
+                abstract // don't want to implement Comparable
+                class ReadOnlyStrategy extends AbstractTraversalStrategy<TraversalStrategy.VerificationStrategy>
+                        implements TraversalStrategy.VerificationStrategy {
+                    static ReadOnlyStrategy instance() { return null; }
+                }
+
+                interface TraversalSource extends Cloneable, AutoCloseable {
+                    default TraversalSource withStrategies(TraversalStrategy... strategies) {
+                        return null;
+                    }
+                }
+                abstract // don't want to implement AutoCloseable
+                class GraphTraversalSource implements TraversalSource {
+                    @Override
+                    public GraphTraversalSource withStrategies(TraversalStrategy... strategies) {
+                        return (GraphTraversalSource) TraversalSource.super.withStrategies(strategies);
+                    }
+                }
+                class Graph {
+                    public <C extends TraversalSource> C traversal(Class<C> c) {
+                        return null;
+                    }
+                    public GraphTraversalSource traversal() {
+                        return null;
+                    }
+                }
+            '''
+            def b = new File(parentDir, 'Script.groovy')
+            b.write '''
+                GraphTraversalSource test(Graph graph) {
+                    def strategy = ReadOnlyStrategy.instance()
+                    graph.traversal().withStrategies(strategy)
+                }
+            '''
+
+            def loader = new GroovyClassLoader(this.class.classLoader)
+            def cu = new JavaAwareCompilationUnit(config, loader)
+            cu.addSources(a, b)
+            cu.compile()
+        } finally {
+            parentDir.deleteDir()
+            config.targetDirectory.deleteDir()
+        }
+    }
+
+    static File createTempDir() {
+        File tempDirectory = File.createTempDir("Groovy3464Bug", Long.toString(System.currentTimeMillis()))
+        return tempDirectory
     }
 
     void testRegressionInConstructorCheck() {