Skip to content

Commit f3a1511

Browse files
committed
GROOVY-9972
1 parent 06f7e59 commit f3a1511

File tree

4 files changed

+240
-12
lines changed

4 files changed

+240
-12
lines changed

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

+78
Original file line numberDiff line numberDiff line change
@@ -1143,4 +1143,82 @@ public void testTypeChecked9970() {
11431143

11441144
runConformTest(sources, "");
11451145
}
1146+
1147+
@Test
1148+
public void testTypeChecked9972() {
1149+
//@formatter:off
1150+
String[] sources = {
1151+
"Main.groovy",
1152+
"@groovy.transform.TupleConstructor\n" +
1153+
"class A<T> {\n" +
1154+
" T p\n" +
1155+
"}\n" +
1156+
"class B {\n" +
1157+
" public String f = 'B#f'\n" +
1158+
"}\n" +
1159+
"@groovy.transform.TypeChecked\n" +
1160+
"void test() {\n" +
1161+
" A<B> x = true ? new A<>(new B()) : new A<>(new B())\n" +
1162+
" print x.p.f.toLowerCase()\n" +
1163+
"}\n" +
1164+
"test()\n",
1165+
};
1166+
//@formatter:on
1167+
1168+
runConformTest(sources, "b#f");
1169+
}
1170+
1171+
@Test
1172+
public void testTypeChecked9972a() {
1173+
//@formatter:off
1174+
String[] sources = {
1175+
"Main.groovy",
1176+
"@groovy.transform.TupleConstructor\n" +
1177+
"class A<T> {\n" +
1178+
" T p\n" +
1179+
"}\n" +
1180+
"class B {\n" +
1181+
" public String f = 'B#f'\n" +
1182+
"}\n" +
1183+
"@groovy.transform.TypeChecked\n" +
1184+
"void test(flag) {\n" +
1185+
" A<B> x = flag ? new A<>(new B()) : (flag ? new A<>(new B()) : new A<>(new B()))\n" +
1186+
" print x.p.f.toLowerCase()\n" +
1187+
"}\n" +
1188+
"test(true)\n" +
1189+
"test(false)\n",
1190+
};
1191+
//@formatter:on
1192+
1193+
runConformTest(sources, "b#fb#f");
1194+
}
1195+
1196+
@Test
1197+
public void testTypeChecked9972b() {
1198+
//@formatter:off
1199+
String[] sources = {
1200+
"Main.groovy",
1201+
"@groovy.transform.TupleConstructor\n" +
1202+
"class A<T> {\n" +
1203+
" T p\n" +
1204+
"}\n" +
1205+
"class B {\n" +
1206+
" public String f = 'B#f'\n" +
1207+
"}\n" +
1208+
"@groovy.transform.TypeChecked\n" +
1209+
"void test() {\n" +
1210+
" def x\n" +
1211+
" if (true) {\n" +
1212+
" x = new A<>(new B())\n" +
1213+
" } else {\n" +
1214+
" x = new A<>(new B())\n" +
1215+
" }\n" +
1216+
" print x.p.f.toLowerCase()\n" +
1217+
"}\n" +
1218+
"test()\n",
1219+
};
1220+
//@formatter:on
1221+
1222+
runConformTest(sources, "b#f");
1223+
}
11461224
}

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

+54-4
Original file line numberDiff line numberDiff line change
@@ -4423,8 +4423,7 @@ public void visitTernaryExpression(final TernaryExpression expression) {
44234423
if (hasInferredReturnType(trueExpression)) {
44244424
typeOfTrue = trueExpression.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE);
44254425
}
4426-
// TODO consider moving next two statements "up a level", i.e. have just one more widely invoked
4427-
// check but determine no -ve consequences first
4426+
/* GRECLIPSE edit -- GROOVY-9972
44284427
typeOfFalse = checkForTargetType(falseExpression, typeOfFalse);
44294428
typeOfTrue = checkForTargetType(trueExpression, typeOfTrue);
44304429
if (isNullConstant(trueExpression) || isNullConstant(falseExpression)) {
@@ -4442,13 +4441,25 @@ public void visitTernaryExpression(final TernaryExpression expression) {
44424441
// store type information
44434442
resultType = lowestUpperBound(typeOfTrue, typeOfFalse);
44444443
}
4444+
*/
4445+
if (isNullConstant(trueExpression) && isNullConstant(falseExpression)) { // GROOVY-5523
4446+
resultType = checkForTargetType(expression, UNKNOWN_PARAMETER_TYPE);
4447+
} else if (isNullConstant(trueExpression)) {
4448+
resultType = wrapTypeIfNecessary(checkForTargetType(falseExpression, typeOfFalse));
4449+
} else if (isNullConstant(falseExpression)) {
4450+
resultType = wrapTypeIfNecessary(checkForTargetType(trueExpression, typeOfTrue));
4451+
} else {
4452+
typeOfFalse = checkForTargetType(falseExpression, typeOfFalse);
4453+
typeOfTrue = checkForTargetType(trueExpression, typeOfTrue);
4454+
resultType = lowestUpperBound(typeOfTrue, typeOfFalse);
4455+
}
4456+
// GRECLIPSE end
44454457
storeType(expression, resultType);
44464458
popAssignmentTracking(oldTracker);
44474459
}
44484460

4449-
// currently just for empty literals, not for e.g. Collections.emptyList() at present
4450-
/// it seems attractive to want to do this for more cases but perhaps not all cases
44514461
private ClassNode checkForTargetType(final Expression expr, final ClassNode type) {
4462+
/* GRECLIPSE edit -- GROOVY-9972
44524463
BinaryExpression enclosingBinaryExpression = typeCheckingContext.getEnclosingBinaryExpression();
44534464
if (enclosingBinaryExpression instanceof DeclarationExpression
44544465
&& isEmptyCollection(expr) && isAssignment(enclosingBinaryExpression.getOperation().getType())) {
@@ -4466,8 +4477,38 @@ && isEmptyCollection(expr) && isAssignment(enclosingBinaryExpression.getOperatio
44664477
return adjustForTargetType(enclosingMethod.getReturnType(), type);
44674478
}
44684479
return type;
4480+
*/
4481+
ClassNode sourceType = type, targetType = null;
4482+
MethodNode enclosingMethod = typeCheckingContext.getEnclosingMethod();
4483+
BinaryExpression enclosingBinaryExpression = typeCheckingContext.getEnclosingBinaryExpression();
4484+
if (enclosingBinaryExpression instanceof DeclarationExpression
4485+
&& isAssignment(enclosingBinaryExpression.getOperation().getType())
4486+
&& isTypeSource(expr, enclosingBinaryExpression.getRightExpression())) {
4487+
targetType = enclosingBinaryExpression.getLeftExpression().getType();
4488+
} else if (currentField != null
4489+
&& isTypeSource(expr, currentField.getInitialExpression())) {
4490+
targetType = currentField.getType();
4491+
} else if (currentProperty != null
4492+
&& isTypeSource(expr, currentProperty.getInitialExpression())) {
4493+
targetType = currentProperty.getType();
4494+
} else if (enclosingMethod != null) {
4495+
// TODO: try enclosingMethod's code with isTypeSource(expr, ...)
4496+
targetType = enclosingMethod.getReturnType();
4497+
} // TODO: closure parameter default expression
4498+
4499+
if (expr instanceof ConstructorCallExpression) {
4500+
if (targetType == null) targetType = sourceType;
4501+
inferDiamondType((ConstructorCallExpression) expr, targetType);
4502+
} else if (targetType != null && missesGenericsTypes(sourceType)) {
4503+
// unchecked assignment with ternary/elvis, like "List<T> list = listOfT ?: []"
4504+
// the inferred type is the RHS type "completed" with generics information from LHS
4505+
return GenericsUtils.parameterizeType(targetType, sourceType.getPlainNodeReference());
4506+
}
4507+
return targetType != null && sourceType == UNKNOWN_PARAMETER_TYPE ? targetType : sourceType;
4508+
// GRECLIPSE end
44694509
}
44704510

4511+
/* GRECLIPSE edit
44714512
private static ClassNode adjustForTargetType(final ClassNode targetType, final ClassNode resultType) {
44724513
if (targetType.isUsingGenerics() && missesGenericsTypes(resultType)) {
44734514
// unchecked assignment within ternary/elvis
@@ -4479,6 +4520,15 @@ private static ClassNode adjustForTargetType(final ClassNode targetType, final C
44794520
}
44804521
return resultType;
44814522
}
4523+
*/
4524+
private static boolean isTypeSource(final Expression expr, final Expression right) {
4525+
if (right instanceof TernaryExpression) {
4526+
return isTypeSource(expr, ((TernaryExpression) right).getTrueExpression())
4527+
|| isTypeSource(expr, ((TernaryExpression) right).getFalseExpression());
4528+
}
4529+
return expr == right;
4530+
}
4531+
// GRECLIPSE end
44824532

44834533
private static boolean isEmptyCollection(Expression expr) {
44844534
return (expr instanceof ListExpression && ((ListExpression) expr).getExpressions().isEmpty())

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

+54-4
Original file line numberDiff line numberDiff line change
@@ -4253,8 +4253,7 @@ public void visitTernaryExpression(final TernaryExpression expression) {
42534253
if (hasInferredReturnType(trueExpression)) {
42544254
typeOfTrue = trueExpression.getNodeMetaData(INFERRED_RETURN_TYPE);
42554255
}
4256-
// TODO consider moving next two statements "up a level", i.e. have just one more widely invoked
4257-
// check but determine no -ve consequences first
4256+
/* GRECLIPSE edit -- GROOVY-9972
42584257
typeOfFalse = checkForTargetType(falseExpression, typeOfFalse);
42594258
typeOfTrue = checkForTargetType(trueExpression, typeOfTrue);
42604259
if (isNullConstant(trueExpression) || isNullConstant(falseExpression)) {
@@ -4272,13 +4271,25 @@ public void visitTernaryExpression(final TernaryExpression expression) {
42724271
// store type information
42734272
resultType = lowestUpperBound(typeOfTrue, typeOfFalse);
42744273
}
4274+
*/
4275+
if (isNullConstant(trueExpression) && isNullConstant(falseExpression)) { // GROOVY-5523
4276+
resultType = checkForTargetType(expression, UNKNOWN_PARAMETER_TYPE);
4277+
} else if (isNullConstant(trueExpression)) {
4278+
resultType = wrapTypeIfNecessary(checkForTargetType(falseExpression, typeOfFalse));
4279+
} else if (isNullConstant(falseExpression)) {
4280+
resultType = wrapTypeIfNecessary(checkForTargetType(trueExpression, typeOfTrue));
4281+
} else {
4282+
typeOfFalse = checkForTargetType(falseExpression, typeOfFalse);
4283+
typeOfTrue = checkForTargetType(trueExpression, typeOfTrue);
4284+
resultType = lowestUpperBound(typeOfTrue, typeOfFalse);
4285+
}
4286+
// GRECLIPSE end
42754287
storeType(expression, resultType);
42764288
popAssignmentTracking(oldTracker);
42774289
}
42784290

4279-
// currently just for empty literals, not for e.g. Collections.emptyList() at present
4280-
/// it seems attractive to want to do this for more cases but perhaps not all cases
42814291
private ClassNode checkForTargetType(final Expression expr, final ClassNode type) {
4292+
/* GRECLIPSE edit -- GROOVY-9972
42824293
BinaryExpression enclosingBinaryExpression = typeCheckingContext.getEnclosingBinaryExpression();
42834294
if (enclosingBinaryExpression instanceof DeclarationExpression
42844295
&& isEmptyCollection(expr) && isAssignment(enclosingBinaryExpression.getOperation().getType())) {
@@ -4296,8 +4307,38 @@ && isEmptyCollection(expr) && isAssignment(enclosingBinaryExpression.getOperatio
42964307
return adjustForTargetType(enclosingMethod.getReturnType(), type);
42974308
}
42984309
return type;
4310+
*/
4311+
ClassNode sourceType = type, targetType = null;
4312+
MethodNode enclosingMethod = typeCheckingContext.getEnclosingMethod();
4313+
BinaryExpression enclosingBinaryExpression = typeCheckingContext.getEnclosingBinaryExpression();
4314+
if (enclosingBinaryExpression instanceof DeclarationExpression
4315+
&& isAssignment(enclosingBinaryExpression.getOperation().getType())
4316+
&& isTypeSource(expr, enclosingBinaryExpression.getRightExpression())) {
4317+
targetType = enclosingBinaryExpression.getLeftExpression().getType();
4318+
} else if (currentField != null
4319+
&& isTypeSource(expr, currentField.getInitialExpression())) {
4320+
targetType = currentField.getType();
4321+
} else if (currentProperty != null
4322+
&& isTypeSource(expr, currentProperty.getInitialExpression())) {
4323+
targetType = currentProperty.getType();
4324+
} else if (enclosingMethod != null) {
4325+
// TODO: try enclosingMethod's code with isTypeSource(expr, ...)
4326+
targetType = enclosingMethod.getReturnType();
4327+
} // TODO: closure parameter default expression
4328+
4329+
if (expr instanceof ConstructorCallExpression) {
4330+
if (targetType == null) targetType = sourceType;
4331+
inferDiamondType((ConstructorCallExpression) expr, targetType);
4332+
} else if (targetType != null && missesGenericsTypes(sourceType)) {
4333+
// unchecked assignment with ternary/elvis, like "List<T> list = listOfT ?: []"
4334+
// the inferred type is the RHS type "completed" with generics information from LHS
4335+
return GenericsUtils.parameterizeType(targetType, sourceType.getPlainNodeReference());
4336+
}
4337+
return targetType != null && sourceType == UNKNOWN_PARAMETER_TYPE ? targetType : sourceType;
4338+
// GRECLIPSE end
42994339
}
43004340

4341+
/* GRECLIPSE edit
43014342
private static ClassNode adjustForTargetType(final ClassNode targetType, final ClassNode resultType) {
43024343
if (targetType.isUsingGenerics() && missesGenericsTypes(resultType)) {
43034344
// unchecked assignment within ternary/elvis
@@ -4309,6 +4350,15 @@ private static ClassNode adjustForTargetType(final ClassNode targetType, final C
43094350
}
43104351
return resultType;
43114352
}
4353+
*/
4354+
private static boolean isTypeSource(final Expression expr, final Expression right) {
4355+
if (right instanceof TernaryExpression) {
4356+
return isTypeSource(expr, ((TernaryExpression) right).getTrueExpression())
4357+
|| isTypeSource(expr, ((TernaryExpression) right).getFalseExpression());
4358+
}
4359+
return expr == right;
4360+
}
4361+
// GRECLIPSE end
43124362

43134363
private static boolean isEmptyCollection(final Expression expr) {
43144364
return (expr instanceof ListExpression && ((ListExpression) expr).getExpressions().isEmpty())

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

+54-4
Original file line numberDiff line numberDiff line change
@@ -4219,8 +4219,7 @@ public void visitTernaryExpression(final TernaryExpression expression) {
42194219
if (hasInferredReturnType(trueExpression)) {
42204220
typeOfTrue = trueExpression.getNodeMetaData(INFERRED_RETURN_TYPE);
42214221
}
4222-
// TODO consider moving next two statements "up a level", i.e. have just one more widely invoked
4223-
// check but determine no -ve consequences first
4222+
/* GRECLIPSE edit -- GROOVY-9972
42244223
typeOfFalse = checkForTargetType(falseExpression, typeOfFalse);
42254224
typeOfTrue = checkForTargetType(trueExpression, typeOfTrue);
42264225
if (isNullConstant(trueExpression) || isNullConstant(falseExpression)) {
@@ -4238,13 +4237,25 @@ public void visitTernaryExpression(final TernaryExpression expression) {
42384237
// store type information
42394238
resultType = lowestUpperBound(typeOfTrue, typeOfFalse);
42404239
}
4240+
*/
4241+
if (isNullConstant(trueExpression) && isNullConstant(falseExpression)) { // GROOVY-5523
4242+
resultType = checkForTargetType(expression, UNKNOWN_PARAMETER_TYPE);
4243+
} else if (isNullConstant(trueExpression)) {
4244+
resultType = wrapTypeIfNecessary(checkForTargetType(falseExpression, typeOfFalse));
4245+
} else if (isNullConstant(falseExpression)) {
4246+
resultType = wrapTypeIfNecessary(checkForTargetType(trueExpression, typeOfTrue));
4247+
} else {
4248+
typeOfFalse = checkForTargetType(falseExpression, typeOfFalse);
4249+
typeOfTrue = checkForTargetType(trueExpression, typeOfTrue);
4250+
resultType = lowestUpperBound(typeOfTrue, typeOfFalse);
4251+
}
4252+
// GRECLIPSE end
42414253
storeType(expression, resultType);
42424254
popAssignmentTracking(oldTracker);
42434255
}
42444256

4245-
// currently just for empty literals, not for e.g. Collections.emptyList() at present
4246-
/// it seems attractive to want to do this for more cases but perhaps not all cases
42474257
private ClassNode checkForTargetType(final Expression expr, final ClassNode type) {
4258+
/* GRECLIPSE edit -- GROOVY-9972
42484259
BinaryExpression enclosingBinaryExpression = typeCheckingContext.getEnclosingBinaryExpression();
42494260
if (enclosingBinaryExpression instanceof DeclarationExpression
42504261
&& isEmptyCollection(expr) && isAssignment(enclosingBinaryExpression.getOperation().getType())) {
@@ -4262,8 +4273,38 @@ && isEmptyCollection(expr) && isAssignment(enclosingBinaryExpression.getOperatio
42624273
return adjustForTargetType(enclosingMethod.getReturnType(), type);
42634274
}
42644275
return type;
4276+
*/
4277+
ClassNode sourceType = type, targetType = null;
4278+
MethodNode enclosingMethod = typeCheckingContext.getEnclosingMethod();
4279+
BinaryExpression enclosingBinaryExpression = typeCheckingContext.getEnclosingBinaryExpression();
4280+
if (enclosingBinaryExpression instanceof DeclarationExpression
4281+
&& isAssignment(enclosingBinaryExpression.getOperation().getType())
4282+
&& isTypeSource(expr, enclosingBinaryExpression.getRightExpression())) {
4283+
targetType = enclosingBinaryExpression.getLeftExpression().getType();
4284+
} else if (currentField != null
4285+
&& isTypeSource(expr, currentField.getInitialExpression())) {
4286+
targetType = currentField.getType();
4287+
} else if (currentProperty != null
4288+
&& isTypeSource(expr, currentProperty.getInitialExpression())) {
4289+
targetType = currentProperty.getType();
4290+
} else if (enclosingMethod != null) {
4291+
// TODO: try enclosingMethod's code with isTypeSource(expr, ...)
4292+
targetType = enclosingMethod.getReturnType();
4293+
} // TODO: closure parameter default expression
4294+
4295+
if (expr instanceof ConstructorCallExpression) {
4296+
if (targetType == null) targetType = sourceType;
4297+
inferDiamondType((ConstructorCallExpression) expr, targetType);
4298+
} else if (targetType != null && missesGenericsTypes(sourceType)) {
4299+
// unchecked assignment with ternary/elvis, like "List<T> list = listOfT ?: []"
4300+
// the inferred type is the RHS type "completed" with generics information from LHS
4301+
return GenericsUtils.parameterizeType(targetType, sourceType.getPlainNodeReference());
4302+
}
4303+
return targetType != null && sourceType == UNKNOWN_PARAMETER_TYPE ? targetType : sourceType;
4304+
// GRECLIPSE end
42654305
}
42664306

4307+
/* GRECLIPSE edit
42674308
private static ClassNode adjustForTargetType(final ClassNode targetType, final ClassNode resultType) {
42684309
if (targetType.isUsingGenerics() && missesGenericsTypes(resultType)) {
42694310
// unchecked assignment within ternary/elvis
@@ -4275,6 +4316,15 @@ private static ClassNode adjustForTargetType(final ClassNode targetType, final C
42754316
}
42764317
return resultType;
42774318
}
4319+
*/
4320+
private static boolean isTypeSource(final Expression expr, final Expression right) {
4321+
if (right instanceof TernaryExpression) {
4322+
return isTypeSource(expr, ((TernaryExpression) right).getTrueExpression())
4323+
|| isTypeSource(expr, ((TernaryExpression) right).getFalseExpression());
4324+
}
4325+
return expr == right;
4326+
}
4327+
// GRECLIPSE end
42784328

42794329
private static boolean isEmptyCollection(final Expression expr) {
42804330
return (expr instanceof ListExpression && ((ListExpression) expr).getExpressions().isEmpty())

0 commit comments

Comments
 (0)