[groovy] branch master updated: GROOVY-9822: check for empty spec before recursive application (closes #1426)

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

[groovy] branch master updated: GROOVY-9822: check for empty spec before recursive application (closes #1426)

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

paulk 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 93a1ee3  GROOVY-9822: check for empty spec before recursive application (closes #1426)
93a1ee3 is described below

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

    GROOVY-9822: check for empty spec before recursive application (closes #1426)
---
 .../transform/stc/StaticTypeCheckingSupport.java   | 70 ++++++++++++----------
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 66 +++++++++++++++++++-
 2 files changed, 103 insertions(+), 33 deletions(-)

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 5034dec..488f3f6 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -1866,38 +1866,44 @@ public abstract class StaticTypeCheckingSupport {
         return false;
     }
 
-    static ClassNode[] applyGenericsContext(final Map<GenericsTypeName, GenericsType> spec, final ClassNode[] bounds) {
-        if (bounds == null) return null;
-        ClassNode[] newBounds = new ClassNode[bounds.length];
-        for (int i = 0, n = bounds.length; i < n; i += 1) {
-            newBounds[i] = applyGenericsContext(spec, bounds[i]);
-        }
-        return newBounds;
-    }
-
-    static ClassNode applyGenericsContext(final Map<GenericsTypeName, GenericsType> spec, final 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;
-            }
-            newBound.setGenericsPlaceHolder(true);
-        }
-        return newBound;
+    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 newTypes;
+    }
+
+    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]);
+            }
+            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 newType;
     }
 
     static ClassNode getCombinedBoundType(final GenericsType genericsType) {
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 6cc3a86..5db067d 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -1507,7 +1507,71 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
               }
             }
             new Element()
-'''
+        '''
+
+        // GROOVY-9822
+        config.with {
+            targetDirectory = File.createTempDir()
+            jointCompilationOptions = [memStub: true]
+        }
+        File parentDir = File.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()
+        }
     }
 
     void testRegressionInConstructorCheck() {