This is an automated email from the ASF dual-hosted git repository. emilles pushed a commit to branch GROOVY-9822 in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 028649ab47b9468fd2b08147eb870c871a254532 Author: Eric Milles <[email protected]> AuthorDate: Thu Nov 19 11:55:29 2020 -0600 GROOVY-9822: check for empty spec before recursive application --- .../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 126afb0..89ea196 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; } private 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 345252b..4ac0910 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() {
