Skip to content

Commit f4a0f02

Browse files
committed
GROOVY-10337
1 parent d51ff52 commit f4a0f02

File tree

7 files changed

+111
-91
lines changed

7 files changed

+111
-91
lines changed

base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/TypeCheckedTests.java

+19
Original file line numberDiff line numberDiff line change
@@ -4564,6 +4564,25 @@ public void testTypeChecked10336() {
45644564
"----------\n");
45654565
}
45664566

4567+
@Test
4568+
public void testTypeChecked10337() {
4569+
//@formatter:off
4570+
String[] sources = {
4571+
"Main.groovy",
4572+
"class C<X,Y> {\n" +
4573+
" C(C<Y,? extends Y> c) {\n" +
4574+
" }\n" +
4575+
"}\n" +
4576+
"@groovy.transform.TypeChecked\n" +
4577+
"def <T> void test() {\n" +
4578+
" new C<Number,T>((C<T,T>)null)\n" + // cannot call ctor with argument C<T,T>
4579+
"}\n",
4580+
};
4581+
//@formatter:on
4582+
4583+
runNegativeTest(sources, "");
4584+
}
4585+
45674586
@Test
45684587
public void testTypeChecked10339() {
45694588
//@formatter:off

base/org.codehaus.groovy25/src/org/codehaus/groovy/ast/GenericsType.java

+10-18
Original file line numberDiff line numberDiff line change
@@ -98,35 +98,27 @@ private String toString(Set<String> visited) {
9898
}
9999

100100
private String nameOf(ClassNode theType) {
101-
StringBuilder ret = new StringBuilder();
102101
if (theType.isArray()) {
103-
ret.append(nameOf(theType.getComponentType()));
104-
ret.append("[]");
102+
return nameOf(theType.getComponentType()) + "[]";
105103
} else {
106-
ret.append(theType.getName());
104+
return theType.isGenericsPlaceHolder() ? theType.getUnresolvedName() : theType.getName();
107105
}
108-
return ret.toString();
109106
}
110107

111108
private String genericsBounds(ClassNode theType, Set<String> visited) {
112-
113109
StringBuilder ret = new StringBuilder();
114110

115-
if (theType.isArray()) {
111+
if (theType.getOuterClass() == null) {
116112
ret.append(nameOf(theType));
117-
} else if (theType.redirect() instanceof InnerClassNode) {
118-
InnerClassNode innerClassNode = (InnerClassNode) theType.redirect();
119-
String parentClassNodeName = innerClassNode.getOuterClass().getName();
120-
if (Modifier.isStatic(innerClassNode.getModifiers()) || innerClassNode.isInterface()) {
121-
ret.append(innerClassNode.getOuterClass().getName());
113+
} else {
114+
String parentClassNodeName = theType.getOuterClass().getName();
115+
if (Modifier.isStatic(theType.getModifiers()) || theType.isInterface()) {
116+
ret.append(parentClassNodeName);
122117
} else {
123-
ret.append(genericsBounds(innerClassNode.getOuterClass(), new HashSet<String>()));
118+
ret.append(genericsBounds(theType.getOuterClass(), new HashSet<>()));
124119
}
125-
ret.append(".");
126-
String typeName = theType.getName();
127-
ret.append(typeName.substring(parentClassNodeName.length() + 1));
128-
} else {
129-
ret.append(theType.getName());
120+
ret.append('.');
121+
ret.append(theType.getName(), parentClassNodeName.length() + 1, theType.getName().length());
130122
}
131123

132124
GenericsType[] genericsTypes = theType.getGenericsTypes();

base/org.codehaus.groovy25/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java

+13-6
Original file line numberDiff line numberDiff line change
@@ -1652,13 +1652,18 @@ private static boolean typeCheckMethodsWithGenerics(ClassNode receiver, ClassNod
16521652
// first parameter. While we normally allow generalization for the first
16531653
// parameter, in case of an extension method we must not.
16541654
Set<GenericsTypeName> fixedGenericsPlaceHolders = extractResolvedPlaceHolders(resolvedMethodGenerics);
1655-
1655+
// GRECLIPSE add -- GROOVY-10337
1656+
if ("<init>".equals(candidateMethod.getName())) {
1657+
fixedGenericsPlaceHolders.addAll(resolvedMethodGenerics.keySet());
1658+
}
1659+
int nthParameter = parameters.length - 1;
1660+
// GRECLIPSE end
16561661
for (int i = 0; i < arguments.length; i++) {
1657-
int pindex = min(i, parameters.length - 1);
1662+
int pindex = Math.min(i, nthParameter);
16581663
ClassNode wrappedArgument = arguments[i];
16591664
ClassNode type = parameters[pindex].getOriginType();
16601665

1661-
failure |= inferenceCheck(fixedGenericsPlaceHolders, resolvedMethodGenerics, type, wrappedArgument, i >= parameters.length - 1);
1666+
failure |= inferenceCheck(fixedGenericsPlaceHolders, resolvedMethodGenerics, type, wrappedArgument, i >= nthParameter);
16621667

16631668
// set real fixed generics for extension methods
16641669
if (isExtensionMethod && i == 0)
@@ -1701,6 +1706,9 @@ private static boolean inferenceCheck(Set<GenericsTypeName> fixedGenericsPlaceHo
17011706
extractGenericsConnections(connections, wrappedArgument, type);
17021707
// each found connection must comply with already found connections
17031708
boolean failure = !compatibleConnections(connections, resolvedMethodGenerics, fixedGenericsPlaceHolders);
1709+
// GRECLIPSE add -- GROOVY-10337
1710+
connections.keySet().removeAll(fixedGenericsPlaceHolders);
1711+
// GRECLIPSE end
17041712
// and then apply the found information to refine the method level
17051713
// information. This way the method level information slowly turns
17061714
// into information for the callsite
@@ -1712,9 +1720,8 @@ private static boolean inferenceCheck(Set<GenericsTypeName> fixedGenericsPlaceHo
17121720
// we use the provided information to transform the parameter
17131721
// into something that can exist in the callsite context
17141722
type = applyGenericsContext(resolvedMethodGenerics, type);
1715-
// there of course transformed parameter type and argument must fit
1716-
failure |= !typeCheckMethodArgumentWithGenerics(type, wrappedArgument, lastArg);
1717-
return failure;
1723+
// then of course transformed parameter type and argument must fit
1724+
return failure || !typeCheckMethodArgumentWithGenerics(type, wrappedArgument, lastArg);
17181725
}
17191726

17201727
private static GenericsType buildWildcardType(GenericsType origin) {

base/org.codehaus.groovy30/src/org/codehaus/groovy/ast/GenericsType.java

+4-9
Original file line numberDiff line numberDiff line change
@@ -97,22 +97,19 @@ private static String toString(final GenericsType gt, final Set<String> visited)
9797
}
9898

9999
private static String nameOf(final ClassNode theType) {
100-
StringBuilder ret = new StringBuilder();
101100
if (theType.isArray()) {
102-
ret.append(nameOf(theType.getComponentType()));
103-
ret.append("[]");
101+
return nameOf(theType.getComponentType()) + "[]";
104102
} else {
105-
ret.append(theType.getName());
103+
return theType.isGenericsPlaceHolder() ? theType.getUnresolvedName() : theType.getName();
106104
}
107-
return ret.toString();
108105
}
109106

110107
private static String genericsBounds(final ClassNode theType, final Set<String> visited) {
111108
StringBuilder ret = new StringBuilder();
112109

113-
if (theType.isArray()) {
110+
if (theType.getOuterClass() == null) {
114111
ret.append(nameOf(theType));
115-
} else if (theType.getOuterClass() != null) {
112+
} else {
116113
String parentClassNodeName = theType.getOuterClass().getName();
117114
if (Modifier.isStatic(theType.getModifiers()) || theType.isInterface()) {
118115
ret.append(parentClassNodeName);
@@ -121,8 +118,6 @@ private static String genericsBounds(final ClassNode theType, final Set<String>
121118
}
122119
ret.append('.');
123120
ret.append(theType.getName(), parentClassNodeName.length() + 1, theType.getName().length());
124-
} else {
125-
ret.append(theType.getName());
126121
}
127122

128123
GenericsType[] genericsTypes = theType.getGenericsTypes();

base/org.codehaus.groovy30/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java

+13-6
Original file line numberDiff line numberDiff line change
@@ -1546,13 +1546,18 @@ private static boolean typeCheckMethodsWithGenerics(final ClassNode receiver, fi
15461546
// first parameter. While we normally allow generalization for the first
15471547
// parameter, in case of an extension method we must not.
15481548
Set<GenericsTypeName> fixedGenericsPlaceHolders = extractResolvedPlaceHolders(resolvedMethodGenerics);
1549-
1549+
// GRECLIPSE add -- GROOVY-10337
1550+
if ("<init>".equals(candidateMethod.getName())) {
1551+
fixedGenericsPlaceHolders.addAll(resolvedMethodGenerics.keySet());
1552+
}
1553+
int nthParameter = parameters.length - 1;
1554+
// GRECLIPSE end
15501555
for (int i = 0, n = argumentTypes.length; i < n; i += 1) {
1551-
int pindex = min(i, parameters.length - 1);
1556+
int pindex = Math.min(i, nthParameter);
15521557
ClassNode wrappedArgument = argumentTypes[i];
15531558
ClassNode type = parameters[pindex].getOriginType();
15541559

1555-
failure = failure || inferenceCheck(fixedGenericsPlaceHolders, resolvedMethodGenerics, type, wrappedArgument, i >= parameters.length - 1);
1560+
failure = failure || inferenceCheck(fixedGenericsPlaceHolders, resolvedMethodGenerics, type, wrappedArgument, i >= nthParameter);
15561561

15571562
// set real fixed generics for extension methods
15581563
if (isExtensionMethod && i == 0)
@@ -1589,6 +1594,9 @@ private static boolean inferenceCheck(final Set<GenericsTypeName> fixedGenericsP
15891594
extractGenericsConnections(connections, wrappedArgument, type);
15901595
// each found connection must comply with already found connections
15911596
boolean failure = !compatibleConnections(connections, resolvedMethodGenerics, fixedGenericsPlaceHolders);
1597+
// GRECLIPSE add -- GROOVY-10337
1598+
connections.keySet().removeAll(fixedGenericsPlaceHolders);
1599+
// GRECLIPSE end
15921600
// and then apply the found information to refine the method level
15931601
// information. This way the method level information slowly turns
15941602
// into information for the callsite
@@ -1600,9 +1608,8 @@ private static boolean inferenceCheck(final Set<GenericsTypeName> fixedGenericsP
16001608
// we use the provided information to transform the parameter
16011609
// into something that can exist in the callsite context
16021610
type = applyGenericsContext(resolvedMethodGenerics, type);
1603-
// there of course transformed parameter type and argument must fit
1604-
failure = failure || !typeCheckMethodArgumentWithGenerics(type, wrappedArgument, lastArg);
1605-
return failure;
1611+
// then of course transformed parameter type and argument must fit
1612+
return failure || !typeCheckMethodArgumentWithGenerics(type, wrappedArgument, lastArg);
16061613
}
16071614

16081615
private static GenericsType buildWildcardType(final GenericsType origin) {

base/org.codehaus.groovy40/src/org/codehaus/groovy/ast/GenericsType.java

+19-27
Original file line numberDiff line numberDiff line change
@@ -101,34 +101,8 @@ private static String toString(final GenericsType gt, final Set<String> visited)
101101
return ret.toString();
102102
}
103103

104-
private static String nameOf(final ClassNode theType) {
105-
StringBuilder ret = new StringBuilder();
106-
if (theType.isArray()) {
107-
ret.append(nameOf(theType.getComponentType()));
108-
ret.append("[]");
109-
} else {
110-
ret.append(theType.getName());
111-
}
112-
return ret.toString();
113-
}
114-
115104
private static String genericsBounds(final ClassNode theType, final Set<String> visited) {
116-
StringBuilder ret = new StringBuilder();
117-
118-
if (theType.isArray()) {
119-
ret.append(nameOf(theType));
120-
} else if (theType.getOuterClass() != null) {
121-
String parentClassNodeName = theType.getOuterClass().getName();
122-
if (Modifier.isStatic(theType.getModifiers()) || theType.isInterface()) {
123-
ret.append(parentClassNodeName);
124-
} else {
125-
ret.append(genericsBounds(theType.getOuterClass(), new HashSet<>()));
126-
}
127-
ret.append('.');
128-
ret.append(theType.getName(), parentClassNodeName.length() + 1, theType.getName().length());
129-
} else {
130-
ret.append(theType.getName());
131-
}
105+
StringBuilder ret = appendName(theType, new StringBuilder());
132106

133107
GenericsType[] genericsTypes = theType.getGenericsTypes();
134108
if (genericsTypes == null || genericsTypes.length == 0) {
@@ -156,6 +130,24 @@ private static String genericsBounds(final ClassNode theType, final Set<String>
156130
return ret.toString();
157131
}
158132

133+
private static StringBuilder appendName(final ClassNode theType, final StringBuilder sb) {
134+
if (theType.isArray()) {
135+
appendName(theType.getComponentType(), sb).append("[]");
136+
} else if (theType.getOuterClass() != null) {
137+
String parentClassNodeName = theType.getOuterClass().getName();
138+
if (Modifier.isStatic(theType.getModifiers()) || theType.isInterface()) {
139+
sb.append(parentClassNodeName);
140+
} else {
141+
sb.append(genericsBounds(theType.getOuterClass(), new HashSet<>()));
142+
}
143+
sb.append('.');
144+
sb.append(theType.getName(), parentClassNodeName.length() + 1, theType.getName().length());
145+
} else {
146+
sb.append(theType.isGenericsPlaceHolder() ? theType.getUnresolvedName() : theType.getName());
147+
}
148+
return sb;
149+
}
150+
159151
public String getName() {
160152
return (isWildcard() ? "?" : name);
161153
}

base/org.codehaus.groovy40/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java

+33-25
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.apache.groovy.util.Maps;
2222
import org.codehaus.groovy.GroovyBugError;
2323
import org.codehaus.groovy.ast.ClassNode;
24+
import org.codehaus.groovy.ast.ConstructorNode;
2425
import org.codehaus.groovy.ast.GenericsType;
2526
import org.codehaus.groovy.ast.GenericsType.GenericsTypeName;
2627
import org.codehaus.groovy.ast.InnerClassNode;
@@ -71,7 +72,6 @@
7172
import java.util.regex.Matcher;
7273
import java.util.stream.BaseStream;
7374

74-
import static java.lang.Math.min;
7575
import static org.apache.groovy.ast.tools.ClassNodeUtils.addGeneratedMethod;
7676
import static org.apache.groovy.ast.tools.ClassNodeUtils.samePackageName;
7777
import static org.apache.groovy.ast.tools.ExpressionUtils.isNullConstant;
@@ -457,7 +457,7 @@ static int lastArgMatchesVarg(final Parameter[] parameters, final ClassNode... a
457457
ClassNode elementType = arrayType.getComponentType();
458458
ClassNode argumentType = argumentTypes[argumentTypes.length - 1];
459459
if (isNumberType(elementType) && isNumberType(argumentType) && !getWrapper(elementType).equals(getWrapper(argumentType))) return -1;
460-
return isAssignableTo(argumentType, elementType) ? min(getDistance(argumentType, arrayType), getDistance(argumentType, elementType)) : -1;
460+
return isAssignableTo(argumentType, elementType) ? Math.min(getDistance(argumentType, arrayType), getDistance(argumentType, elementType)) : -1;
461461
}
462462

463463
/**
@@ -1433,39 +1433,45 @@ private static boolean typeCheckMethodsWithGenerics(final ClassNode receiver, fi
14331433
}
14341434

14351435
boolean failure = false;
1436+
Set<GenericsTypeName> fixedPlaceHolders = Collections.emptySet();
1437+
Map<GenericsTypeName, GenericsType> resolvedMethodGenerics = new HashMap<>();
14361438
// correct receiver for inner class
14371439
// we assume the receiver is an instance of the declaring class of the
14381440
// candidate method, but findMethod returns also outer class methods
14391441
// for that receiver. For now we skip receiver based checks in that case
14401442
// TODO: correct generics for when receiver is to be skipped
14411443
boolean skipBecauseOfInnerClassNotReceiver = !implementsInterfaceOrIsSubclassOf(receiver, candidateMethod.getDeclaringClass());
1442-
// we have here different generics contexts we have to deal with.
1443-
// There is firstly the context given through the class, and the method.
1444-
// The method context may hide generics given through the class, but use
1445-
// the non-hidden ones.
1446-
Map<GenericsTypeName, GenericsType> resolvedMethodGenerics = new HashMap<>();
14471444
if (!skipBecauseOfInnerClassNotReceiver) {
1448-
addMethodLevelDeclaredGenerics(candidateMethod, resolvedMethodGenerics);
1449-
if (!resolvedMethodGenerics.isEmpty()) {
1450-
// first remove hidden generics
1451-
Map<GenericsTypeName, GenericsType> receiverGenerics = GenericsUtils.extractPlaceholders(receiver);
1452-
receiverGenerics.keySet().removeAll(resolvedMethodGenerics.keySet());
1453-
// then use the remaining information to refine the method generics
1454-
applyGenericsConnections(receiverGenerics, resolvedMethodGenerics);
1455-
}
1456-
// and then start our checks with the receiver
1457-
failure = inferenceCheck(Collections.emptySet(), resolvedMethodGenerics, candidateMethod.getDeclaringClass(), receiver, false);
1458-
}
1459-
// the outside context parts till now define placeholder we are not allowed to
1460-
// generalize, thus we save that for later use...
1461-
// extension methods are special, since they set the receiver as
1462-
// first parameter. While we normally allow generalization for the first
1463-
// parameter, in case of an extension method we must not.
1464-
Set<GenericsTypeName> fixedPlaceHolders = extractResolvedPlaceHolders(resolvedMethodGenerics);
1445+
if (candidateMethod instanceof ConstructorNode) {
1446+
resolvedMethodGenerics = GenericsUtils.extractPlaceholders(receiver);
1447+
fixedPlaceHolders = new HashSet<>(resolvedMethodGenerics.keySet());
1448+
} else {
1449+
// we have here different generics contexts we have to deal with.
1450+
// There is firstly the context given through the class, and the method.
1451+
// The method context may hide generics given through the class, but use
1452+
// the non-hidden ones.
1453+
addMethodLevelDeclaredGenerics(candidateMethod, resolvedMethodGenerics);
1454+
if (!resolvedMethodGenerics.isEmpty()) {
1455+
// first remove hidden generics
1456+
Map<GenericsTypeName, GenericsType> receiverGenerics = GenericsUtils.extractPlaceholders(receiver);
1457+
receiverGenerics.keySet().removeAll(resolvedMethodGenerics.keySet());
1458+
// then use the remaining information to refine the method generics
1459+
applyGenericsConnections(receiverGenerics, resolvedMethodGenerics);
1460+
}
1461+
// and then start our checks with the receiver
1462+
failure = inferenceCheck(fixedPlaceHolders, resolvedMethodGenerics, candidateMethod.getDeclaringClass(), receiver, false);
1463+
// the outside context parts till now define placeholder we are not allowed to
1464+
// generalize, thus we save that for later use...
1465+
// extension methods are special, since they set the receiver as
1466+
// first parameter. While we normally allow generalization for the first
1467+
// parameter, in case of an extension method we must not.
1468+
fixedPlaceHolders = extractResolvedPlaceHolders(resolvedMethodGenerics);
1469+
}
1470+
}
14651471

14661472
int lastParamIndex = parameters.length - 1;
14671473
for (int i = 0, n = argumentTypes.length; i < n; i += 1) {
1468-
ClassNode parameterType = parameters[min(i, lastParamIndex)].getOriginType();
1474+
ClassNode parameterType = parameters[Math.min(i, lastParamIndex)].getOriginType();
14691475
ClassNode argumentType = StaticTypeCheckingVisitor.wrapTypeIfNecessary(argumentTypes[i]);
14701476
failure |= inferenceCheck(fixedPlaceHolders, resolvedMethodGenerics, parameterType, argumentType, i >= lastParamIndex);
14711477

@@ -1524,6 +1530,8 @@ private static boolean inferenceCheck(final Set<GenericsTypeName> fixedPlaceHold
15241530
}
15251531
}
15261532

1533+
connections.keySet().removeAll(fixedPlaceHolders); // GROOVY-10337
1534+
15271535
// apply the new information to refine the method level information so
15281536
// that the information slowly becomes information for the callsite
15291537
applyGenericsConnections(connections, resolvedMethodGenerics);

0 commit comments

Comments
 (0)