This is an automated email from the ASF dual-hosted git repository.
emilles 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 cee45d7 GROOVY-10337: STC: don't mutate explicit constructor call
type arguments
cee45d7 is described below
commit cee45d768bb65d1254a61a6dd4ce3c6a1f2ee3ca
Author: Eric Milles <[email protected]>
AuthorDate: Wed Nov 3 10:00:12 2021 -0500
GROOVY-10337: STC: don't mutate explicit constructor call type arguments
---
.../java/org/codehaus/groovy/ast/GenericsType.java | 46 +++++++---------
.../transform/stc/StaticTypeCheckingSupport.java | 62 ++++++++++++----------
.../groovy/transform/stc/GenericsSTCTest.groovy | 13 +++++
3 files changed, 67 insertions(+), 54 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/ast/GenericsType.java
b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
index d162c1a..0aae08e 100644
--- a/src/main/java/org/codehaus/groovy/ast/GenericsType.java
+++ b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
@@ -94,34 +94,8 @@ public class GenericsType extends ASTNode {
return ret.toString();
}
- private static String nameOf(final ClassNode theType) {
- StringBuilder ret = new StringBuilder();
- if (theType.isArray()) {
- ret.append(nameOf(theType.getComponentType()));
- ret.append("[]");
- } else {
- ret.append(theType.getName());
- }
- return ret.toString();
- }
-
private static String genericsBounds(final ClassNode theType, final
Set<String> visited) {
- StringBuilder ret = new StringBuilder();
-
- if (theType.isArray()) {
- ret.append(nameOf(theType));
- } else if (theType.getOuterClass() != null) {
- String parentClassNodeName = theType.getOuterClass().getName();
- if (Modifier.isStatic(theType.getModifiers()) ||
theType.isInterface()) {
- ret.append(parentClassNodeName);
- } else {
- ret.append(genericsBounds(theType.getOuterClass(), new
HashSet<>()));
- }
- ret.append('.');
- ret.append(theType.getName(), parentClassNodeName.length() + 1,
theType.getName().length());
- } else {
- ret.append(theType.getName());
- }
+ StringBuilder ret = appendName(theType, new StringBuilder());
GenericsType[] genericsTypes = theType.getGenericsTypes();
if (genericsTypes == null || genericsTypes.length == 0) {
@@ -149,6 +123,24 @@ public class GenericsType extends ASTNode {
return ret.toString();
}
+ private static StringBuilder appendName(final ClassNode theType, final
StringBuilder sb) {
+ if (theType.isArray()) {
+ appendName(theType.getComponentType(), sb).append("[]");
+ } else if (theType.getOuterClass() != null) {
+ String parentClassNodeName = theType.getOuterClass().getName();
+ if (Modifier.isStatic(theType.getModifiers()) ||
theType.isInterface()) {
+ sb.append(parentClassNodeName);
+ } else {
+ sb.append(genericsBounds(theType.getOuterClass(), new
HashSet<>()));
+ }
+ sb.append('.');
+ sb.append(theType.getName(), parentClassNodeName.length() + 1,
theType.getName().length());
+ } else {
+ sb.append(theType.isGenericsPlaceHolder() ?
theType.getUnresolvedName() : theType.getName());
+ }
+ return sb;
+ }
+
public String getName() {
return (isWildcard() ? "?" : name);
}
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 3c47f79..19e961b 100644
---
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -21,6 +21,7 @@ package org.codehaus.groovy.transform.stc;
import org.apache.groovy.util.Maps;
import org.codehaus.groovy.GroovyBugError;
import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ConstructorNode;
import org.codehaus.groovy.ast.GenericsType;
import org.codehaus.groovy.ast.GenericsType.GenericsTypeName;
import org.codehaus.groovy.ast.InnerClassNode;
@@ -71,7 +72,6 @@ import java.util.UUID;
import java.util.regex.Matcher;
import java.util.stream.BaseStream;
-import static java.lang.Math.min;
import static org.apache.groovy.ast.tools.ClassNodeUtils.addGeneratedMethod;
import static org.apache.groovy.ast.tools.ClassNodeUtils.samePackageName;
import static org.apache.groovy.ast.tools.ExpressionUtils.isNullConstant;
@@ -451,7 +451,7 @@ public abstract class StaticTypeCheckingSupport {
ClassNode elementType = arrayType.getComponentType();
ClassNode argumentType = argumentTypes[argumentTypes.length - 1];
if (isNumberType(elementType) && isNumberType(argumentType) &&
!getWrapper(elementType).equals(getWrapper(argumentType))) return -1;
- return isAssignableTo(argumentType, elementType) ?
min(getDistance(argumentType, arrayType), getDistance(argumentType,
elementType)) : -1;
+ return isAssignableTo(argumentType, elementType) ?
Math.min(getDistance(argumentType, arrayType), getDistance(argumentType,
elementType)) : -1;
}
/**
@@ -1424,39 +1424,45 @@ public abstract class StaticTypeCheckingSupport {
}
boolean failure = false;
+ Set<GenericsTypeName> fixedPlaceHolders = Collections.emptySet();
+ Map<GenericsTypeName, GenericsType> resolvedMethodGenerics = new
HashMap<>();
// correct receiver for inner class
// we assume the receiver is an instance of the declaring class of the
// candidate method, but findMethod returns also outer class methods
// for that receiver. For now we skip receiver based checks in that
case
// TODO: correct generics for when receiver is to be skipped
boolean skipBecauseOfInnerClassNotReceiver =
!implementsInterfaceOrIsSubclassOf(receiver,
candidateMethod.getDeclaringClass());
- // we have here different generics contexts we have to deal with.
- // There is firstly the context given through the class, and the
method.
- // The method context may hide generics given through the class, but
use
- // the non-hidden ones.
- Map<GenericsTypeName, GenericsType> resolvedMethodGenerics = new
HashMap<>();
if (!skipBecauseOfInnerClassNotReceiver) {
- addMethodLevelDeclaredGenerics(candidateMethod,
resolvedMethodGenerics);
- if (!resolvedMethodGenerics.isEmpty()) {
- // first remove hidden generics
- Map<GenericsTypeName, GenericsType> receiverGenerics =
GenericsUtils.extractPlaceholders(receiver);
-
receiverGenerics.keySet().removeAll(resolvedMethodGenerics.keySet());
- // then use the remaining information to refine the method
generics
- applyGenericsConnections(receiverGenerics,
resolvedMethodGenerics);
- }
- // and then start our checks with the receiver
- failure = inferenceCheck(Collections.emptySet(),
resolvedMethodGenerics, candidateMethod.getDeclaringClass(), receiver, false);
- }
- // the outside context parts till now define placeholder we are not
allowed to
- // generalize, thus we save that for later use...
- // extension methods are special, since they set the receiver as
- // first parameter. While we normally allow generalization for the
first
- // parameter, in case of an extension method we must not.
- Set<GenericsTypeName> fixedPlaceHolders =
extractResolvedPlaceHolders(resolvedMethodGenerics);
+ if (candidateMethod instanceof ConstructorNode) {
+ resolvedMethodGenerics =
GenericsUtils.extractPlaceholders(receiver);
+ fixedPlaceHolders = new
HashSet<>(resolvedMethodGenerics.keySet());
+ } else {
+ // we have here different generics contexts we have to deal
with.
+ // There is firstly the context given through the class, and
the method.
+ // The method context may hide generics given through the
class, but use
+ // the non-hidden ones.
+ addMethodLevelDeclaredGenerics(candidateMethod,
resolvedMethodGenerics);
+ if (!resolvedMethodGenerics.isEmpty()) {
+ // first remove hidden generics
+ Map<GenericsTypeName, GenericsType> receiverGenerics =
GenericsUtils.extractPlaceholders(receiver);
+
receiverGenerics.keySet().removeAll(resolvedMethodGenerics.keySet());
+ // then use the remaining information to refine the method
generics
+ applyGenericsConnections(receiverGenerics,
resolvedMethodGenerics);
+ }
+ // and then start our checks with the receiver
+ failure = inferenceCheck(fixedPlaceHolders,
resolvedMethodGenerics, candidateMethod.getDeclaringClass(), receiver, false);
+ // the outside context parts till now define placeholder we
are not allowed to
+ // generalize, thus we save that for later use...
+ // extension methods are special, since they set the receiver
as
+ // first parameter. While we normally allow generalization for
the first
+ // parameter, in case of an extension method we must not.
+ fixedPlaceHolders =
extractResolvedPlaceHolders(resolvedMethodGenerics);
+ }
+ }
int lastParamIndex = parameters.length - 1;
for (int i = 0, n = argumentTypes.length; i < n; i += 1) {
- ClassNode parameterType = parameters[min(i,
lastParamIndex)].getOriginType();
+ ClassNode parameterType = parameters[Math.min(i,
lastParamIndex)].getOriginType();
ClassNode argumentType =
StaticTypeCheckingVisitor.wrapTypeIfNecessary(argumentTypes[i]);
failure |= inferenceCheck(fixedPlaceHolders,
resolvedMethodGenerics, parameterType, argumentType, i >= lastParamIndex);
@@ -1515,6 +1521,8 @@ public abstract class StaticTypeCheckingSupport {
}
}
+ connections.keySet().removeAll(fixedPlaceHolders); // GROOVY-10337
+
// apply the new information to refine the method level information so
// that the information slowly becomes information for the callsite
applyGenericsConnections(connections, resolvedMethodGenerics);
@@ -1590,9 +1598,9 @@ public abstract class StaticTypeCheckingSupport {
GenericsTypeName name = new
GenericsTypeName(oldValue.getName());
GenericsType newValue = connections.get(name); // find "V"
in T=V
if (newValue == oldValue) continue;
- if (newValue == null) { // GROOVY-10315
+ if (newValue == null) {
newValue = connections.get(entry.getKey());
- if (newValue != null) {
+ if (newValue != null) { // GROOVY-10315, GROOVY-10317
ClassNode o = makeClassSafe0(CLASS_Type, oldValue),
n = makeClassSafe0(CLASS_Type, newValue);
newValue =
lowestUpperBound(o,n).getGenericsTypes()[0];
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 925848a..0669a23 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -2884,6 +2884,19 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase
{
'''
}
+ // GROOVY-10337
+ void testShouldFindMethodEvenWithRepeatNames4() {
+ assertScript '''
+ class C<X,Y> {
+ C(C<Y,? extends Y> that) {
+ }
+ }
+ def <T> void test() {
+ new C<Number,T>((C<T,T>)null) // cannot call ctor with
argument C<T,T>
+ }
+ '''
+ }
+
// GROOVY-5893
void testPlusInClosure() {
['def', 'var', 'Object', 'Number', 'Integer', 'Comparable'].each {
type ->