Skip to content

Commit b9b5c19

Browse files
committed
GROOVY-10688
1 parent 9d8b197 commit b9b5c19

File tree

4 files changed

+193
-125
lines changed

4 files changed

+193
-125
lines changed

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

+37-15
Original file line numberDiff line numberDiff line change
@@ -4610,22 +4610,24 @@ public void testTypeChecked10235() {
46104610
if (Float.parseFloat(System.getProperty("java.specification.version")) > 8)
46114611
vmArguments = new String[] {"--add-opens", "java.base/java.util.concurrent=ALL-UNNAMED"};
46124612

4613-
//@formatter:off
4614-
String[] sources = {
4615-
"Main.groovy",
4616-
"@groovy.transform.TypeChecked\n" +
4617-
"void test() {\n" +
4618-
" Set<Integer> integers = java.util.concurrent.ConcurrentHashMap.newKeySet()\n" +
4619-
" printSet(integers)\n" + // Cannot call printSet(Set<Integer>) with arguments [KeySetView<Object,Object>]
4620-
"}\n" +
4621-
"void printSet(Set<Integer> integers) {\n" +
4622-
" println(integers)\n" +
4623-
"}\n" +
4624-
"test()\n",
4625-
};
4626-
//@formatter:on
4613+
for (String x : new String[] {"", "true?new HashSet<>():", "false?new HashSet<>():"}) {
4614+
//@formatter:off
4615+
String[] sources = {
4616+
"Main.groovy",
4617+
"@groovy.transform.TypeChecked\n" +
4618+
"void test() {\n" +
4619+
" Set<Integer> integers = " + x + "java.util.concurrent.ConcurrentHashMap.newKeySet()\n" +
4620+
" printSet(integers)\n" + // Cannot call printSet(Set<Integer>) with arguments [KeySetView<Object,Object>]
4621+
"}\n" +
4622+
"void printSet(Set<Integer> integers) {\n" +
4623+
" println(integers)\n" +
4624+
"}\n" +
4625+
"test()\n",
4626+
};
4627+
//@formatter:on
46274628

4628-
runConformTest(sources, "[]");
4629+
runConformTest(sources, "[]");
4630+
}
46294631
}
46304632

46314633
@Test
@@ -6022,6 +6024,26 @@ public void testTypeChecked10673() {
60226024
runConformTest(sources, "1");
60236025
}
60246026

6027+
@Test
6028+
public void testTypeChecked10688() {
6029+
//@formatter:off
6030+
String[] sources = {
6031+
"Main.groovy",
6032+
"class A<T, X> {\n" +
6033+
"}\n" +
6034+
"@groovy.transform.TypeChecked\n" +
6035+
"def <T> void test(\n" +
6036+
" A<Double, ? extends T> x) {\n" +
6037+
" A<Double, ? extends T> y = x\n" +
6038+
" A<Double, ? extends T> z = true ? y : x\n" +
6039+
"}\n" +
6040+
"test(null)\n",
6041+
};
6042+
//@formatter:on
6043+
6044+
runConformTest(sources);
6045+
}
6046+
60256047
@Test
60266048
public void testTypeChecked10698() {
60276049
//@formatter:off

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

+52-42
Original file line numberDiff line numberDiff line change
@@ -938,30 +938,9 @@ && isAssignment(enclosingBinaryExpression.getOperation().getType())) {
938938
if (!isEmptyDeclaration && isAssignment(op)) {
939939
if (rightExpression instanceof ConstructorCallExpression)
940940
inferDiamondType((ConstructorCallExpression) rightExpression, lType);
941-
942-
if (lType.isUsingGenerics() && missesGenericsTypes(resultType)) {
943-
// unchecked assignment
944-
// List<Type> list = new LinkedList()
945-
// Iterable<Type> iter = new LinkedList()
946-
// Collection<Type> coll = Collections.emptyList()
947-
// Collection<Type> view = ConcurrentHashMap.newKeySet()
948-
949-
// the inferred type of the binary expression is the type of the RHS
950-
// "completed" with generics type information available from the LHS
951-
if (lType.equals(resultType)) {
952-
if (!lType.isGenericsPlaceHolder()) resultType = lType;
953-
} else if (!resultType.isGenericsPlaceHolder()) { // GROOVY-10235, GROOVY-10324, et al.
954-
Map<GenericsTypeName, GenericsType> gt = new HashMap<>();
955-
extractGenericsConnections(gt, resultType, resultType.redirect());
956-
ClassNode sc = resultType;
957-
do { sc = getNextSuperClass(sc, lType);
958-
} while (sc != null && !sc.equals(lType));
959-
extractGenericsConnections(gt, lType, sc);
960-
961-
resultType = applyGenericsContext(gt, resultType.redirect());
962-
}
963-
}
964-
941+
// GRECLIPSE edit -- GROOVY-10235, GROOVY-10324, et al.
942+
resultType = adjustForTargetType(resultType, lType);
943+
// GRECLIPSE end
965944
ClassNode originType = getOriginalDeclarationType(leftExpression);
966945
typeCheckAssignment(expression, leftExpression, originType, rightExpression, resultType);
967946
// check for implicit conversion like "String a = 123", "int[] b = [1,2,3]", "List c = [].stream()", etc.
@@ -4914,36 +4893,34 @@ && isEmptyCollection(expr) && isAssignment(enclosingBinaryExpression.getOperatio
49144893
*/
49154894
ClassNode sourceType = type, targetType = null;
49164895
MethodNode enclosingMethod = typeCheckingContext.getEnclosingMethod();
4917-
BinaryExpression enclosingBinaryExpression = typeCheckingContext.getEnclosingBinaryExpression();
4918-
if (enclosingBinaryExpression != null
4919-
&& isAssignment(enclosingBinaryExpression.getOperation().getType())
4920-
&& isTypeSource(expr, enclosingBinaryExpression.getRightExpression())) {
4921-
targetType = getDeclaredOrInferredType(enclosingBinaryExpression.getLeftExpression());
4896+
MethodCall enclosingMethodCall = (MethodCall)typeCheckingContext.getEnclosingMethodCall();
4897+
BinaryExpression enclosingExpression = typeCheckingContext.getEnclosingBinaryExpression();
4898+
if (enclosingExpression != null
4899+
&& isAssignment(enclosingExpression.getOperation().getType())
4900+
&& isTypeSource(expr, enclosingExpression.getRightExpression())) {
4901+
targetType = getDeclaredOrInferredType(enclosingExpression.getLeftExpression());
4902+
} else if (enclosingMethodCall != null
4903+
&& InvocationWriter.makeArgumentList(enclosingMethodCall.getArguments())
4904+
.getExpressions().stream().anyMatch(arg -> isTypeSource(expr, arg))) {
4905+
// TODO: locate method target parameter
49224906
} else if (enclosingMethod != null
49234907
&& !enclosingMethod.isAbstract()
49244908
&& !enclosingMethod.isVoidMethod()
49254909
&& isTypeSource(expr, enclosingMethod)) {
49264910
targetType = enclosingMethod.getReturnType();
49274911
}
49284912

4929-
if (expr instanceof ConstructorCallExpression) {
4930-
if (targetType == null) targetType = OBJECT_TYPE;
4931-
inferDiamondType((ConstructorCallExpression) expr, targetType);
4932-
}
4913+
if (targetType == null) targetType = OBJECT_TYPE;
4914+
if (sourceType == UNKNOWN_PARAMETER_TYPE) return targetType;
49334915

4934-
if (targetType == null) return sourceType;
4916+
if (expr instanceof ConstructorCallExpression)
4917+
inferDiamondType((ConstructorCallExpression) expr, targetType);
49354918

4936-
if (!isPrimitiveType(getUnwrapper(targetType))
4937-
&& !targetType.equals(OBJECT_TYPE) && missesGenericsTypes(sourceType)) {
4938-
// unchecked assignment with ternary/elvis, like "List<T> list = listOfT ?: []"
4939-
// the inferred type is the RHS type "completed" with generics information from LHS
4940-
return GenericsUtils.parameterizeType(targetType, sourceType.getPlainNodeReference());
4941-
}
4942-
return targetType != null && sourceType == UNKNOWN_PARAMETER_TYPE ? targetType : sourceType;
4919+
return adjustForTargetType(sourceType, targetType);
49434920
// GRECLIPSE end
49444921
}
49454922

4946-
/* GRECLIPSE edit
4923+
/* GRECLIPSE edit -- GROOVY-10688
49474924
private static ClassNode adjustForTargetType(final ClassNode targetType, final ClassNode resultType) {
49484925
if (targetType.isUsingGenerics() && missesGenericsTypes(resultType)) {
49494926
// unchecked assignment within ternary/elvis
@@ -4956,6 +4933,39 @@ private static ClassNode adjustForTargetType(final ClassNode targetType, final C
49564933
return resultType;
49574934
}
49584935
*/
4936+
private static ClassNode adjustForTargetType(final ClassNode resultType, final ClassNode targetType) {
4937+
if (targetType.isUsingGenerics()
4938+
&& missesGenericsTypes(resultType)
4939+
// GROOVY-10324, GROOVY-10342, et al.
4940+
&& !resultType.isGenericsPlaceHolder()) {
4941+
// unchecked assignment
4942+
// List<Type> list = new LinkedList()
4943+
// Iterable<Type> iter = new LinkedList()
4944+
// Collection<Type> col1 = Collections.emptyList()
4945+
// Collection<Type> col2 = Collections.emptyList() ?: []
4946+
// Collection<Type> view = ConcurrentHashMap.newKeySet()
4947+
4948+
// the inferred type of the binary expression is the type of the RHS
4949+
// "completed" with generics type information available from the LHS
4950+
if (targetType.equals(resultType)) {
4951+
// GROOVY-6126, GROOVY-6558, GROOVY-6564, et al.
4952+
if (!targetType.isGenericsPlaceHolder()) return targetType;
4953+
} else {
4954+
// GROOVY-5640, GROOVY-9033, GROOVY-10220, GROOVY-10235, et al.
4955+
Map<GenericsTypeName, GenericsType> gt = new HashMap<>();
4956+
extractGenericsConnections(gt, resultType, resultType.redirect());
4957+
ClassNode sc = resultType;
4958+
do { sc = getNextSuperClass(sc, targetType);
4959+
} while (sc != null && !sc.equals(targetType));
4960+
extractGenericsConnections(gt, targetType, sc);
4961+
4962+
return applyGenericsContext(gt, resultType.redirect());
4963+
}
4964+
}
4965+
4966+
return resultType;
4967+
}
4968+
49594969
private static boolean isTypeSource(final Expression expr, final Expression right) {
49604970
if (right instanceof TernaryExpression) {
49614971
return isTypeSource(expr, ((TernaryExpression) right).getTrueExpression())

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

+57-38
Original file line numberDiff line numberDiff line change
@@ -856,30 +856,9 @@ && isAssignment(enclosingBinaryExpression.getOperation().getType())) {
856856
if (!isEmptyDeclaration && isAssignment(op)) {
857857
if (rightExpression instanceof ConstructorCallExpression)
858858
inferDiamondType((ConstructorCallExpression) rightExpression, lType);
859-
860-
if (lType.isUsingGenerics() && missesGenericsTypes(resultType)) {
861-
// unchecked assignment
862-
// List<Type> list = new LinkedList()
863-
// Iterable<Type> iter = new LinkedList()
864-
// Collection<Type> coll = Collections.emptyList()
865-
// Collection<Type> view = ConcurrentHashMap.newKeySet()
866-
867-
// the inferred type of the binary expression is the type of the RHS
868-
// "completed" with generics type information available from the LHS
869-
if (lType.equals(resultType)) {
870-
if (!lType.isGenericsPlaceHolder()) resultType = lType;
871-
} else if (!resultType.isGenericsPlaceHolder()) { // GROOVY-10235, GROOVY-10324, et al.
872-
Map<GenericsTypeName, GenericsType> gt = new HashMap<>();
873-
extractGenericsConnections(gt, resultType, resultType.redirect());
874-
ClassNode sc = resultType;
875-
do { sc = getNextSuperClass(sc, lType);
876-
} while (sc != null && !sc.equals(lType));
877-
extractGenericsConnections(gt, lType, sc);
878-
879-
resultType = applyGenericsContext(gt, resultType.redirect());
880-
}
881-
}
882-
859+
// GRECLIPSE edit -- GROOVY-10235, GROOVY-10324, et al.
860+
resultType = adjustForTargetType(resultType, lType);
861+
// GRECLIPSE end
883862
ClassNode originType = getOriginalDeclarationType(leftExpression);
884863
typeCheckAssignment(expression, leftExpression, originType, rightExpression, resultType);
885864
// check for implicit conversion like "String a = 123", "int[] b = [1,2,3]", "List c = [].stream()", etc.
@@ -4668,36 +4647,76 @@ private ClassNode checkForTargetType(final Expression expr, final ClassNode type
46684647
// GRECLIPSE end
46694648
ClassNode targetType = null;
46704649
MethodNode enclosingMethod = typeCheckingContext.getEnclosingMethod();
4671-
BinaryExpression enclosingBinaryExpression = typeCheckingContext.getEnclosingBinaryExpression();
4672-
if (enclosingBinaryExpression != null
4673-
&& isAssignment(enclosingBinaryExpression.getOperation().getType())
4674-
&& isTypeSource(expr, enclosingBinaryExpression.getRightExpression())) {
4675-
targetType = getDeclaredOrInferredType(enclosingBinaryExpression.getLeftExpression());
4650+
MethodCall enclosingMethodCall = (MethodCall)typeCheckingContext.getEnclosingMethodCall();
4651+
BinaryExpression enclosingExpression = typeCheckingContext.getEnclosingBinaryExpression();
4652+
if (enclosingExpression != null
4653+
&& isAssignment(enclosingExpression.getOperation().getType())
4654+
&& isTypeSource(expr, enclosingExpression.getRightExpression())) {
4655+
targetType = getDeclaredOrInferredType(enclosingExpression.getLeftExpression());
4656+
} else if (enclosingMethodCall != null
4657+
&& InvocationWriter.makeArgumentList(enclosingMethodCall.getArguments())
4658+
.getExpressions().stream().anyMatch(arg -> isTypeSource(expr, arg))) {
4659+
// TODO: locate method target parameter
46764660
} else if (enclosingMethod != null
46774661
&& !enclosingMethod.isAbstract()
46784662
&& !enclosingMethod.isVoidMethod()
46794663
&& isTypeSource(expr, enclosingMethod)) {
46804664
targetType = enclosingMethod.getReturnType();
46814665
}
4682-
// GRECLIPSE add -- GROOVY-10114
4683-
if (expr instanceof ConstructorCallExpression) {
4684-
if (targetType == null) targetType = OBJECT_TYPE;
4685-
inferDiamondType((ConstructorCallExpression) expr, targetType);
4686-
return sourceType;
4687-
}
4688-
// GRECLIPSE end
4689-
if (targetType == null) return sourceType;
46904666
/* GRECLIPSE edit
4667+
if (targetType == null) return sourceType;
46914668
if (expr instanceof ConstructorCallExpression) {
46924669
inferDiamondType((ConstructorCallExpression) expr, targetType);
4693-
} else*/ if (!isPrimitiveType(getUnwrapper(targetType)) && !targetType.equals(OBJECT_TYPE)
4670+
} else if (!isPrimitiveType(getUnwrapper(targetType)) && !targetType.equals(OBJECT_TYPE)
46944671
&& !sourceType.isGenericsPlaceHolder() && missesGenericsTypes(sourceType)) {
46954672
// unchecked assignment with ternary/elvis, like "List<T> list = listOfT ?: []"
46964673
// the inferred type is the RHS type "completed" with generics information from LHS
46974674
return GenericsUtils.parameterizeType(targetType, sourceType.getPlainNodeReference());
46984675
}
46994676
47004677
return sourceType != UNKNOWN_PARAMETER_TYPE ? sourceType : targetType;
4678+
*/
4679+
if (targetType == null) targetType = OBJECT_TYPE;
4680+
if (sourceType == UNKNOWN_PARAMETER_TYPE) return targetType;
4681+
4682+
if (expr instanceof ConstructorCallExpression) // GROOVY-10114
4683+
inferDiamondType((ConstructorCallExpression) expr, targetType);
4684+
4685+
return adjustForTargetType(sourceType, targetType); // GROOVY-10688
4686+
// GRECLIPSE end
4687+
}
4688+
4689+
private static ClassNode adjustForTargetType(final ClassNode resultType, final ClassNode targetType) {
4690+
if (targetType.isUsingGenerics()
4691+
&& missesGenericsTypes(resultType)
4692+
// GROOVY-10324, GROOVY-10342, et al.
4693+
&& !resultType.isGenericsPlaceHolder()) {
4694+
// unchecked assignment
4695+
// List<Type> list = new LinkedList()
4696+
// Iterable<Type> iter = new LinkedList()
4697+
// Collection<Type> col1 = Collections.emptyList()
4698+
// Collection<Type> col2 = Collections.emptyList() ?: []
4699+
// Collection<Type> view = ConcurrentHashMap.newKeySet()
4700+
4701+
// the inferred type of the binary expression is the type of the RHS
4702+
// "completed" with generics type information available from the LHS
4703+
if (targetType.equals(resultType)) {
4704+
// GROOVY-6126, GROOVY-6558, GROOVY-6564, et al.
4705+
if (!targetType.isGenericsPlaceHolder()) return targetType;
4706+
} else {
4707+
// GROOVY-5640, GROOVY-9033, GROOVY-10220, GROOVY-10235, et al.
4708+
Map<GenericsTypeName, GenericsType> gt = new HashMap<>();
4709+
extractGenericsConnections(gt, resultType, resultType.redirect());
4710+
ClassNode sc = resultType;
4711+
do { sc = getNextSuperClass(sc, targetType);
4712+
} while (sc != null && !sc.equals(targetType));
4713+
extractGenericsConnections(gt, targetType, sc);
4714+
4715+
return applyGenericsContext(gt, resultType.redirect());
4716+
}
4717+
}
4718+
4719+
return resultType;
47014720
}
47024721

47034722
private static boolean isTypeSource(final Expression expr, final Expression right) {

0 commit comments

Comments
 (0)