Skip to content

Commit 9615572

Browse files
committed
GROOVY-10053
1 parent 040a09c commit 9615572

File tree

7 files changed

+121
-30
lines changed

7 files changed

+121
-30
lines changed

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

+25
Original file line numberDiff line numberDiff line change
@@ -2310,4 +2310,29 @@ public void testTypeChecked10052() {
23102310

23112311
runConformTest(sources, "xy");
23122312
}
2313+
2314+
@Test
2315+
public void testTypeChecked10053() {
2316+
if (Float.parseFloat(System.getProperty("java.specification.version")) > 8)
2317+
vmArguments = new String[] {"--add-opens", "java.base/java.util.stream=ALL-UNNAMED"};
2318+
2319+
//@formatter:off
2320+
String[] sources = {
2321+
"Main.groovy",
2322+
"Set<Number> f() {\n" +
2323+
" Collections.<Number>singleton(42)\n" +
2324+
"}\n" +
2325+
"@groovy.transform.TypeChecked\n" +
2326+
"def <N extends Number> Set<N> g(Class<N> t) {\n" +
2327+
" Set<N> result = new HashSet<>()\n" +
2328+
" f().stream().filter{n -> t.isInstance(n)}\n" +
2329+
" .map{n -> t.cast(n)}.forEach{n -> result.add(n)}\n" +
2330+
" return result\n" +
2331+
"}\n" +
2332+
"print g(Integer)\n",
2333+
};
2334+
//@formatter:on
2335+
2336+
runConformTest(sources, "[42]");
2337+
}
23132338
}

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

+7-2
Original file line numberDiff line numberDiff line change
@@ -1790,12 +1790,17 @@ static void applyGenericsConnections(
17901790
// GROOVY-6787: Don't override the original if the replacement doesn't respect the bounds otherwise
17911791
// the original bounds are lost, which can result in accepting an incompatible type as an argument.
17921792
ClassNode replacementType = extractType(newValue);
1793+
/* GRECLIPSE edit -- GROOVY-9998, GROOVY-10053
17931794
if (oldValue.isCompatibleWith(replacementType)) {
1794-
// GRECLIPSE add -- GROOVY-9998
1795+
*/
1796+
ClassNode suitabilityType = !replacementType.isGenericsPlaceHolder()
1797+
? replacementType : Optional.ofNullable(replacementType.getGenericsTypes())
1798+
.map(gts -> extractType(gts[0])).orElse(replacementType.redirect());
1799+
if (oldValue.isCompatibleWith(suitabilityType)) {
17951800
if (newValue.isWildcard() && newValue.getLowerBound() == null && newValue.getUpperBounds() == null) {
17961801
entry.setValue(new GenericsType(replacementType));
17971802
} else
1798-
// GRECLIPSE end
1803+
// GRECLIPSE end
17991804
entry.setValue(newValue);
18001805
if (newValue.isPlaceholder()) {
18011806
checkForMorePlaceholders = checkForMorePlaceholders || !equalIncludingGenerics(oldValue, newValue);

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

+23-24
Original file line numberDiff line numberDiff line change
@@ -2832,29 +2832,21 @@ public void visitClosureExpression(final ClosureExpression expression) {
28322832
}
28332833
}
28342834

2835-
// GRECLIPSE add -- GROOVY-9751
2835+
// GRECLIPSE add -- GROOVY-9751, GROOVY-9803, GROOVY-10053
28362836
@Override
28372837
public void visitMethodPointerExpression(final MethodPointerExpression expression) {
28382838
super.visitMethodPointerExpression(expression);
28392839
Expression nameExpr = expression.getMethodName();
28402840
if (nameExpr instanceof ConstantExpression
28412841
&& getType(nameExpr).equals(STRING_TYPE)) {
28422842
String nameText = nameExpr.getText();
2843-
2844-
if ("new".equals(nameText)) {
2845-
ClassNode receiverType = getType(expression.getExpression());
2846-
if (isClassClassNodeWrappingConcreteType(receiverType)) {
2847-
storeType(expression, wrapClosureType(receiverType));
2848-
}
2849-
return;
2850-
}
2851-
28522843
List<Receiver<String>> receivers = new ArrayList<>();
28532844
addReceivers(receivers, makeOwnerList(expression.getExpression()), false);
28542845

2846+
ClassNode receiverType = null;
28552847
List<MethodNode> candidates = EMPTY_METHODNODE_LIST;
28562848
for (Receiver<String> currentReceiver : receivers) {
2857-
ClassNode receiverType = wrapTypeIfNecessary(currentReceiver.getType());
2849+
receiverType = wrapTypeIfNecessary(currentReceiver.getType());
28582850

28592851
candidates = findMethodsWithGenerated(receiverType, nameText);
28602852
if (isBeingCompiled(receiverType)) candidates.addAll(GROOVY_OBJECT_TYPE.getMethods(nameText));
@@ -2867,11 +2859,16 @@ && getType(nameExpr).equals(STRING_TYPE)) {
28672859
}
28682860

28692861
if (!candidates.isEmpty()) {
2870-
candidates.stream().map(MethodNode::getReturnType)
2871-
.reduce(WideningCategories::lowestUpperBound)
2872-
.filter(returnType -> !returnType.equals(OBJECT_TYPE))
2873-
.ifPresent(returnType -> storeType(expression, wrapClosureType(returnType)));
2874-
expression.putNodeMetaData(MethodNode.class, candidates); // GROOVY-9803
2862+
Map<GenericsTypeName, GenericsType> gts = GenericsUtils.extractPlaceholders(receiverType);
2863+
candidates.stream().map(candidate -> applyGenericsContext(gts, candidate.getReturnType()))
2864+
.reduce(WideningCategories::lowestUpperBound).ifPresent(returnType -> {
2865+
storeType(expression, wrapClosureType(returnType));
2866+
});
2867+
expression.putNodeMetaData(MethodNode.class, candidates);
2868+
} else {
2869+
ClassNode type = wrapTypeIfNecessary(getType(expression.getExpression()));
2870+
if (isClassClassNodeWrappingConcreteType(type)) type = type.getGenericsTypes()[0].getType();
2871+
addStaticTypeError("Cannot find matching method " + type.getText() + "#" + nameText + ". Please check if the declared type is correct and if the method exists.", nameExpr);
28752872
}
28762873
}
28772874
}
@@ -6010,7 +6007,7 @@ private static void extractGenericsConnectionsForSuperClassAndInterfaces(final M
60106007
}
60116008
}
60126009

6013-
// GRECLIPSE add -- GROOVY-9803
6010+
// GRECLIPSE add -- GROOVY-9803, GROOVY-10053
60146011
private static MethodNode chooseMethod(final MethodPointerExpression source, final Supplier<ClassNode[]> samSignature) {
60156012
List<MethodNode> options = source.getNodeMetaData(MethodNode.class);
60166013
if (options == null || options.isEmpty()) {
@@ -6020,15 +6017,17 @@ private static MethodNode chooseMethod(final MethodPointerExpression source, fin
60206017
ClassNode[] paramTypes = samSignature.get();
60216018
return options.stream().filter((MethodNode option) -> {
60226019
ClassNode[] types = collateMethodReferenceParameterTypes(source, option);
6023-
if (types.length == paramTypes.length) {
6024-
for (int i = 0, n = types.length; i < n; i += 1) {
6025-
if (!types[i].isGenericsPlaceHolder() && !isAssignableTo(types[i], paramTypes[i])) {
6026-
return false;
6027-
}
6020+
final int n = types.length;
6021+
if (n != paramTypes.length) {
6022+
return false;
6023+
}
6024+
for (int i = 0; i < n; i += 1) {
6025+
// param type represents incoming argument type
6026+
if (!isAssignableTo(paramTypes[i], types[i])) {
6027+
return false;
60286028
}
6029-
return true;
60306029
}
6031-
return false;
6030+
return true;
60326031
}).findFirst().orElse(null); // TODO: order matches by param distance
60336032
}
60346033

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

+7-2
Original file line numberDiff line numberDiff line change
@@ -1704,12 +1704,17 @@ static void applyGenericsConnections(final Map<GenericsTypeName, GenericsType> c
17041704
// GROOVY-6787: Don't override the original if the replacement doesn't respect the bounds otherwise
17051705
// the original bounds are lost, which can result in accepting an incompatible type as an argument.
17061706
ClassNode replacementType = extractType(newValue);
1707+
/* GRECLIPSE edit -- GROOVY-9998, GROOVY-10053
17071708
if (oldValue.isCompatibleWith(replacementType)) {
1708-
// GRECLIPSE add -- GROOVY-9998
1709+
*/
1710+
ClassNode suitabilityType = !replacementType.isGenericsPlaceHolder()
1711+
? replacementType : Optional.ofNullable(replacementType.getGenericsTypes())
1712+
.map(gts -> extractType(gts[0])).orElse(replacementType.redirect());
1713+
if (oldValue.isCompatibleWith(suitabilityType)) {
17091714
if (newValue.isWildcard() && newValue.getLowerBound() == null && newValue.getUpperBounds() == null) {
17101715
entry.setValue(new GenericsType(replacementType));
17111716
} else
1712-
// GRECLIPSE end
1717+
// GRECLIPSE end
17131718
entry.setValue(newValue);
17141719
if (!checkForMorePlaceholders && newValue.isPlaceholder()) {
17151720
checkForMorePlaceholders = !equalIncludingGenerics(oldValue, newValue);

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

+26-1
Original file line numberDiff line numberDiff line change
@@ -2552,9 +2552,12 @@ && getType(nameExpr).equals(STRING_TYPE)) {
25522552
List<Receiver<String>> receivers = new ArrayList<>();
25532553
addReceivers(receivers, makeOwnerList(expression.getExpression()), false);
25542554

2555+
// GRECLIPSE add -- GROOVY-10053
2556+
ClassNode receiverType = null;
2557+
// GRECLIPSE end
25552558
List<MethodNode> candidates = EMPTY_METHODNODE_LIST;
25562559
for (Receiver<String> currentReceiver : receivers) {
2557-
ClassNode receiverType = wrapTypeIfNecessary(currentReceiver.getType());
2560+
receiverType = wrapTypeIfNecessary(currentReceiver.getType());
25582561

25592562
candidates = findMethodsWithGenerated(receiverType, nameText);
25602563
if (isBeingCompiled(receiverType)) candidates.addAll(GROOVY_OBJECT_TYPE.getMethods(nameText));
@@ -2574,10 +2577,18 @@ && getType(nameExpr).equals(STRING_TYPE)) {
25742577
}
25752578

25762579
if (!candidates.isEmpty()) {
2580+
/* GRECLIPSE edit -- GROOVY-10053
25772581
candidates.stream().map(MethodNode::getReturnType)
25782582
.reduce(WideningCategories::lowestUpperBound)
25792583
.filter(returnType -> !returnType.equals(OBJECT_TYPE))
25802584
.ifPresent(returnType -> storeType(expression, wrapClosureType(returnType)));
2585+
*/
2586+
Map<GenericsTypeName, GenericsType> gts = GenericsUtils.extractPlaceholders(receiverType);
2587+
candidates.stream().map(candidate -> applyGenericsContext(gts, candidate.getReturnType()))
2588+
.reduce(WideningCategories::lowestUpperBound).ifPresent(returnType -> {
2589+
storeType(expression, wrapClosureType(returnType));
2590+
});
2591+
// GRECLIPSE end
25812592
expression.putNodeMetaData(MethodNode.class, candidates);
25822593
} else if (!(expression instanceof MethodReferenceExpression)) {
25832594
ClassNode type = wrapTypeIfNecessary(getType(expression.getExpression()));
@@ -5715,6 +5726,7 @@ private static MethodNode chooseMethod(final MethodPointerExpression source, fin
57155726
ClassNode[] paramTypes = samSignature.get();
57165727
return options.stream().filter((MethodNode option) -> {
57175728
ClassNode[] types = collateMethodReferenceParameterTypes(source, option);
5729+
/* GRECLIPSE edit -- GROOVY-10053
57185730
if (types.length == paramTypes.length) {
57195731
for (int i = 0, n = types.length; i < n; i += 1) {
57205732
if (!types[i].isGenericsPlaceHolder() && !isAssignableTo(types[i], paramTypes[i])) {
@@ -5724,6 +5736,19 @@ private static MethodNode chooseMethod(final MethodPointerExpression source, fin
57245736
return true;
57255737
}
57265738
return false;
5739+
*/
5740+
final int n = types.length;
5741+
if (n != paramTypes.length) {
5742+
return false;
5743+
}
5744+
for (int i = 0; i < n; i += 1) {
5745+
// param type represents incoming argument type
5746+
if (!isAssignableTo(paramTypes[i], types[i])) {
5747+
return false;
5748+
}
5749+
}
5750+
return true;
5751+
// GRECLIPSE end
57275752
}).findFirst().orElse(null); // TODO: order matches by param distance
57285753
}
57295754

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

+7
Original file line numberDiff line numberDiff line change
@@ -1646,7 +1646,14 @@ static void applyGenericsConnections(final Map<GenericsTypeName, GenericsType> c
16461646
// GROOVY-6787: Don't override the original if the replacement doesn't respect the bounds otherwise
16471647
// the original bounds are lost, which can result in accepting an incompatible type as an argument.
16481648
ClassNode replacementType = extractType(newValue);
1649+
/* GRECLIPSE edit -- GROOVY-10053: accept type parameter from context as type argument for target
16491650
if (oldValue.isCompatibleWith(replacementType)) {
1651+
*/
1652+
ClassNode suitabilityType = !replacementType.isGenericsPlaceHolder()
1653+
? replacementType : Optional.ofNullable(replacementType.getGenericsTypes())
1654+
.map(gts -> extractType(gts[0])).orElse(replacementType.redirect());
1655+
if (oldValue.isCompatibleWith(suitabilityType)) {
1656+
// GRECLIPSE end
16501657
if (newValue.isWildcard() && newValue.getLowerBound() == null && newValue.getUpperBounds() == null) {
16511658
// GROOVY-9998: apply upper/lower bound for unknown
16521659
entry.setValue(new GenericsType(replacementType));

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

+26-1
Original file line numberDiff line numberDiff line change
@@ -2468,9 +2468,12 @@ && getType(nameExpr).equals(STRING_TYPE)) {
24682468
List<Receiver<String>> receivers = new ArrayList<>();
24692469
addReceivers(receivers, makeOwnerList(expression.getExpression()), false);
24702470

2471+
// GRECLIPSE add -- GROOVY-10053
2472+
ClassNode receiverType = null;
2473+
// GRECLIPSE end
24712474
List<MethodNode> candidates = EMPTY_METHODNODE_LIST;
24722475
for (Receiver<String> currentReceiver : receivers) {
2473-
ClassNode receiverType = wrapTypeIfNecessary(currentReceiver.getType());
2476+
receiverType = wrapTypeIfNecessary(currentReceiver.getType());
24742477

24752478
candidates = findMethodsWithGenerated(receiverType, nameText);
24762479
if (isBeingCompiled(receiverType)) candidates.addAll(GROOVY_OBJECT_TYPE.getMethods(nameText));
@@ -2490,10 +2493,18 @@ && getType(nameExpr).equals(STRING_TYPE)) {
24902493
}
24912494

24922495
if (!candidates.isEmpty()) {
2496+
/* GRECLIPSE edit -- GROOVY-10053
24932497
candidates.stream().map(MethodNode::getReturnType)
24942498
.reduce(WideningCategories::lowestUpperBound)
24952499
.filter(returnType -> !returnType.equals(OBJECT_TYPE))
24962500
.ifPresent(returnType -> storeType(expression, wrapClosureType(returnType)));
2501+
*/
2502+
Map<GenericsTypeName, GenericsType> gts = GenericsUtils.extractPlaceholders(receiverType);
2503+
candidates.stream().map(candidate -> applyGenericsContext(gts, candidate.getReturnType()))
2504+
.reduce(WideningCategories::lowestUpperBound).ifPresent(returnType -> {
2505+
storeType(expression, wrapClosureType(returnType));
2506+
});
2507+
// GRECLIPSE end
24972508
expression.putNodeMetaData(MethodNode.class, candidates);
24982509
} else if (!(expression instanceof MethodReferenceExpression)) {
24992510
ClassNode type = wrapTypeIfNecessary(getType(expression.getExpression()));
@@ -5493,6 +5504,7 @@ private static MethodNode chooseMethod(final MethodPointerExpression source, fin
54935504
ClassNode[] paramTypes = samSignature.get();
54945505
return options.stream().filter((MethodNode option) -> {
54955506
ClassNode[] types = collateMethodReferenceParameterTypes(source, option);
5507+
/* GRECLIPSE edit -- GROOVY-10053
54965508
if (types.length == paramTypes.length) {
54975509
for (int i = 0, n = types.length; i < n; i += 1) {
54985510
if (!types[i].isGenericsPlaceHolder() && !isAssignableTo(types[i], paramTypes[i])) {
@@ -5502,6 +5514,19 @@ private static MethodNode chooseMethod(final MethodPointerExpression source, fin
55025514
return true;
55035515
}
55045516
return false;
5517+
*/
5518+
final int n = types.length;
5519+
if (n != paramTypes.length) {
5520+
return false;
5521+
}
5522+
for (int i = 0; i < n; i += 1) {
5523+
// param type represents incoming argument type
5524+
if (!isAssignableTo(paramTypes[i], types[i])) {
5525+
return false;
5526+
}
5527+
}
5528+
return true;
5529+
// GRECLIPSE end
55055530
}).findFirst().orElse(null); // TODO: order matches by param distance
55065531
}
55075532

0 commit comments

Comments
 (0)