diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/TypeCheckedTests.java b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/TypeCheckedTests.java index 8413e7a0d0..6e77e05158 100644 --- a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/TypeCheckedTests.java +++ b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/TypeCheckedTests.java @@ -492,4 +492,57 @@ public void testTypeChecked9803() { runConformTest(sources, "123"); } + + @Test + public void testTypeChecked9822() { + //@formatter:off + String[] sources = { + "Main.groovy", + "@groovy.transform.TypeChecked\n" + + "GraphTraversalSource test(Graph graph) {\n" + + " def strategy = ReadOnlyStrategy.instance()\n" + + " graph.traversal().withStrategies(strategy)\n" + + "}\n", + + "Types.java", // from org.apache.tinkerpop:gremlin-core:3.4.8 + "@SuppressWarnings(\"rawtypes\")\n" + + "interface TraversalStrategy extends Comparable> {\n" + + " interface VerificationStrategy extends TraversalStrategy {\n" + + " }\n" + + "}\n" + + "@SuppressWarnings(\"rawtypes\")\n" + + "abstract class AbstractTraversalStrategy implements TraversalStrategy {\n" + + "}\n" + + "abstract\n" + // don't want to implement Comparable + "class ReadOnlyStrategy extends AbstractTraversalStrategy\n" + + " implements TraversalStrategy.VerificationStrategy {\n" + + " static ReadOnlyStrategy instance() { return null; }\n" + + "}\n" + + "interface TraversalSource extends Cloneable, AutoCloseable {\n" + + " @SuppressWarnings(\"rawtypes\")\n" + + " default TraversalSource withStrategies(TraversalStrategy... strategies) {\n" + + " return null;\n" + + " }\n" + + "}\n" + + "abstract\n" + // don't want to implement AutoCloseable + "class GraphTraversalSource implements TraversalSource {\n" + + " @Override\n" + + " @SuppressWarnings(\"rawtypes\")\n" + + " public GraphTraversalSource withStrategies(TraversalStrategy... strategies) {\n" + + " return (GraphTraversalSource) TraversalSource.super.withStrategies(strategies);\n" + + " }\n" + + "}\n" + + "class Graph {\n" + + " public C traversal(Class c) {\n" + + " return null;\n" + + " }\n" + + " public GraphTraversalSource traversal() {\n" + + " return null;\n" + + " }\n" + + "}\n", + }; + //@formatter:on + + runNegativeTest(sources, ""); + } } diff --git a/base/org.codehaus.groovy25/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java b/base/org.codehaus.groovy25/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java index bf11521371..a3627c39c2 100644 --- a/base/org.codehaus.groovy25/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java +++ b/base/org.codehaus.groovy25/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java @@ -1993,9 +1993,7 @@ private static boolean hasNonTrivialBounds(GenericsType gt) { } // GRECLIPSE private->package - static ClassNode[] applyGenericsContext( - Map spec, ClassNode[] bounds - ) { + static ClassNode[] applyGenericsContext(final Map spec, final ClassNode[] bounds) { if (bounds == null) return null; ClassNode[] newBounds = new ClassNode[bounds.length]; for (int i = 0; i < bounds.length; i++) { @@ -2004,26 +2002,38 @@ static ClassNode[] applyGenericsContext( return newBounds; } - static ClassNode applyGenericsContext( - Map spec, ClassNode bound - ) { + static ClassNode applyGenericsContext(final Map spec, final ClassNode bound) { + /* GRECLIPSE edit -- GROOVY-9822 if (bound == null) return null; + */ + if (bound == null || !isUsingGenericsOrIsArrayUsingGenerics(bound)) { + return bound; + } + // GRECLIPSE end if (bound.isArray()) { return applyGenericsContext(spec, bound.getComponentType()).makeArray(); } + /* GRECLIPSE edit -- GROOVY-9822 if (!bound.isUsingGenerics()) return bound; ClassNode newBound = bound.getPlainNodeReference(); newBound.setGenericsTypes(applyGenericsContext(spec, bound.getGenericsTypes())); + */ + ClassNode newBound = bound.getPlainNodeReference(); + GenericsType[] gt = bound.getGenericsTypes(); + if (spec != null && !spec.isEmpty()) { + gt = applyGenericsContext(spec, gt); + } + newBound.setGenericsTypes(gt); + // GRECLIPSE end 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(); + String placeHolderName = gt[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); + clean.setGenericsTypes(gt); newBound = clean; } newBound.setGenericsPlaceHolder(true); diff --git a/base/org.codehaus.groovy30/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java b/base/org.codehaus.groovy30/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java index fdcdd9cdde..8f8db09232 100644 --- a/base/org.codehaus.groovy30/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java +++ b/base/org.codehaus.groovy30/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java @@ -1882,23 +1882,37 @@ static ClassNode[] applyGenericsContext(final Map spec, final ClassNode bound) { + /* GRECLIPSE edit -- GROOVY-9822 if (bound == null) return null; + */ + if (bound == null || !isUsingGenericsOrIsArrayUsingGenerics(bound)) { + return bound; + } + // GRECLIPSE end if (bound.isArray()) { return applyGenericsContext(spec, bound.getComponentType()).makeArray(); } + /* GRECLIPSE edit -- GROOVY-9822 if (!bound.isUsingGenerics()) return bound; ClassNode newBound = bound.getPlainNodeReference(); newBound.setGenericsTypes(applyGenericsContext(spec, bound.getGenericsTypes())); + */ + ClassNode newBound = bound.getPlainNodeReference(); + GenericsType[] gt = bound.getGenericsTypes(); + if (asBoolean(spec)) { + gt = applyGenericsContext(spec, gt); + } + newBound.setGenericsTypes(gt); + // GRECLIPSE end 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(); + String placeHolderName = gt[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); + clean.setGenericsTypes(gt); newBound = clean; } newBound.setGenericsPlaceHolder(true);