From cac90d62030685195aa3de1f0001c53b9c1dabe5 Mon Sep 17 00:00:00 2001 From: Eric Milles Date: Tue, 17 May 2022 09:45:48 -0500 Subject: [PATCH] GROOVY-10365: type argument/parameter relationships: no type param bound --- .../org/codehaus/groovy/ast/GenericsType.java | 4 +- .../groovy/ast/tools/GenericsUtils.java | 74 ++++++++++--------- .../transform/stc/GenericsSTCTest.groovy | 21 ++++++ 3 files changed, 62 insertions(+), 37 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/ast/GenericsType.java b/src/main/java/org/codehaus/groovy/ast/GenericsType.java index a84cb9c6739..ae469aec9b5 100644 --- a/src/main/java/org/codehaus/groovy/ast/GenericsType.java +++ b/src/main/java/org/codehaus/groovy/ast/GenericsType.java @@ -118,6 +118,8 @@ private static String genericsBounds(final ClassNode theType, final Set private static StringBuilder appendName(final ClassNode theType, final StringBuilder sb) { if (theType.isArray()) { appendName(theType.getComponentType(), sb).append("[]"); + } else if (theType.isGenericsPlaceHolder()) { + sb.append(theType.getUnresolvedName()); } else if (theType.getOuterClass() != null) { String parentClassNodeName = theType.getOuterClass().getName(); if (Modifier.isStatic(theType.getModifiers()) || theType.isInterface()) { @@ -128,7 +130,7 @@ private static StringBuilder appendName(final ClassNode theType, final StringBui sb.append('.'); sb.append(theType.getName(), parentClassNodeName.length() + 1, theType.getName().length()); } else { - sb.append(theType.isGenericsPlaceHolder() ? theType.getUnresolvedName() : theType.getName()); + sb.append(theType.getName()); } return sb; } diff --git a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java index 374204f7c4a..f98a8eca2e7 100644 --- a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java +++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java @@ -40,6 +40,7 @@ import org.codehaus.groovy.runtime.memoize.EvictableCache; import java.lang.ref.SoftReference; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -50,7 +51,6 @@ import java.util.Map; import java.util.Objects; import java.util.Set; -import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; @@ -142,6 +142,11 @@ public static GenericsType buildWildcardType(final ClassNode... types) { return gt; } + /** + * Returns the type parameter/argument relationships of the specified type. + * + * @param type the class node to check + */ public static Map extractPlaceholders(final ClassNode type) { Map placeholders = new HashMap<>(); extractPlaceholders(type, placeholders); @@ -149,8 +154,8 @@ public static Map extractPlaceholde } /** - * For a given classnode, fills in the supplied map with the parameterized - * types it defines. + * Populates the supplied map with the type parameter/argument relationships + * of the specified type. * * @param type the class node to check * @param placeholders the generics type information collector @@ -164,56 +169,53 @@ public static void extractPlaceholders(final ClassNode type, final Map extractor = (GenericsType gt) -> { - ClassNode lowerBound = gt.getLowerBound(); - if (lowerBound != null) { - extractPlaceholders(lowerBound, placeholders); - } - ClassNode[] upperBounds = gt.getUpperBounds(); - if (upperBounds != null) { - for (ClassNode upperBound : upperBounds) { - extractPlaceholders(upperBound, placeholders); - } - } - }; + GenericsType[] parameterized = type.getGenericsTypes(); int n; + if (parameterized == null || (n = parameterized.length) == 0) return; - // GROOVY-8609, GROOVY-10067 + // GROOVY-8609, GROOVY-10067, etc. if (type.isGenericsPlaceHolder()) { GenericsType gt = parameterized[0]; - placeholders.put(new GenericsType.GenericsTypeName(gt.getName()), gt); - extractor.accept(gt); + placeholders.putIfAbsent(new GenericsType.GenericsTypeName(gt.getName()), gt); return; } GenericsType[] redirectGenericsTypes = type.redirect().getGenericsTypes(); - if (redirectGenericsTypes == null) redirectGenericsTypes = parameterized; - else if (redirectGenericsTypes.length != parameterized.length) { + if (redirectGenericsTypes == null) { + redirectGenericsTypes = parameterized; + } else if (redirectGenericsTypes.length != n) { throw new GroovyBugError("Expected earlier checking to detect generics parameter arity mismatch" + "\nExpected: " + type.getName() + toGenericTypesString(redirectGenericsTypes) + "\nSupplied: " + type.getName() + toGenericTypesString(parameterized)); } - List valueList = new LinkedList<>(); - for (int i = 0, n = redirectGenericsTypes.length; i < n; i += 1) { + List typeArguments = new ArrayList<>(n); + for (int i = 0; i < n; i += 1) { GenericsType rgt = redirectGenericsTypes[i]; if (rgt.isPlaceholder()) { - GenericsType.GenericsTypeName name = new GenericsType.GenericsTypeName(rgt.getName()); - if (!placeholders.containsKey(name)) { - GenericsType value = parameterized[i]; - placeholders.put(name, value); - valueList.add(value); - } + GenericsType typeArgument = parameterized[i]; + placeholders.computeIfAbsent(new GenericsType.GenericsTypeName(rgt.getName()), name -> { + typeArguments.add(typeArgument); + return typeArgument; + }); } } - for (GenericsType value : valueList) { - if (value.isWildcard()) { - extractor.accept(value); - } else if (!value.isPlaceholder()) { - extractPlaceholders(value.getType(), placeholders); + // examine non-placeholder type args + for (GenericsType gt : typeArguments) { + if (gt.isWildcard()) { + ClassNode lowerBound = gt.getLowerBound(); + if (lowerBound != null) { + extractPlaceholders(lowerBound, placeholders); + } else { + ClassNode[] upperBounds = gt.getUpperBounds(); + if (upperBounds != null) { + for (ClassNode upperBound : upperBounds) { + extractPlaceholders(upperBound, placeholders); + } + } + } + } else if (!gt.isPlaceholder()) { + extractPlaceholders(gt.getType(), placeholders); } } } diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy index 98977f83d2c..a93faf60f6e 100644 --- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy +++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy @@ -3136,6 +3136,27 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase { ''' } + // GROOVY-10365 + void testCorrectlyBoundedTypeParameterTypeArgument() { + assertScript ''' + interface I { + } + class A implements I { + double m(Integer i) { + i.doubleValue() + } + } + class B { + public int f + double test(A a) { // "T" is re-purposed + a.m(f) // Cannot call A#m(java.lang.Integer) with arguments [int] + } + } + double result = new B(f:2).test(new A<>()) + assert result == 2.0d + ''' + } + void testOutOfBoundsByExtendsGenericParameterType() { shouldFailWithMessages ''' class Foo {