Skip to content

Commit aa1935a

Browse files
committed
GROOVY-10063
1 parent 2730f3a commit aa1935a

File tree

4 files changed

+175
-3
lines changed

4 files changed

+175
-3
lines changed

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

+20
Original file line numberDiff line numberDiff line change
@@ -2373,4 +2373,24 @@ public void testTypeChecked10062() {
23732373

23742374
runConformTest(sources, "42");
23752375
}
2376+
2377+
@Test
2378+
public void testTypeChecked10063() {
2379+
//@formatter:off
2380+
String[] sources = {
2381+
"Main.groovy",
2382+
"static Tuple2<String,Integer> m() {\n" +
2383+
" new Tuple2<>('answer', 42)\n" +
2384+
"}\n" +
2385+
"@groovy.transform.TypeChecked\n" +
2386+
"void test() {\n" +
2387+
" def (String string, Integer number) = m()\n" +
2388+
" print number\n" +
2389+
"}\n" +
2390+
"test()\n",
2391+
};
2392+
//@formatter:on
2393+
2394+
runConformTest(sources, "42");
2395+
}
23762396
}

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

+59-1
Original file line numberDiff line numberDiff line change
@@ -1265,7 +1265,7 @@ protected void pushInstanceOfTypeInfo(final Expression objectOfInstanceOf, final
12651265
}
12661266

12671267
private boolean typeCheckMultipleAssignmentAndContinue(Expression leftExpression, Expression rightExpression) {
1268-
// multiple assignment check
1268+
/* GRECLIPSE edit -- GROOVY-10063
12691269
if (!(leftExpression instanceof TupleExpression)) return true;
12701270
12711271
if (!(rightExpression instanceof ListExpression)) {
@@ -1293,10 +1293,68 @@ private boolean typeCheckMultipleAssignmentAndContinue(Expression leftExpression
12931293
storeType(tupleExpression, elemType);
12941294
}
12951295
}
1296+
*/
1297+
if (leftExpression instanceof TupleExpression) {
1298+
if (rightExpression instanceof VariableExpression || rightExpression instanceof PropertyExpression || rightExpression instanceof MethodCall) {
1299+
ClassNode inferredType = Optional.ofNullable(getType(rightExpression)).orElseGet(rightExpression::getType);
1300+
GenericsType[] genericsTypes = inferredType.getGenericsTypes();
1301+
ListExpression listExpression = new ListExpression();
1302+
listExpression.setSourcePosition(rightExpression);
1303+
1304+
// convert Tuple[1-16] bearing expressions to mock list for checking
1305+
for (int n = TUPLE_TYPES.indexOf(inferredType), i = 0; i < n; i += 1) {
1306+
ClassNode type = (genericsTypes != null ? genericsTypes[i].getType() : OBJECT_TYPE);
1307+
listExpression.addExpression(varX("v" + (i + 1), type));
1308+
}
1309+
if (!listExpression.getExpressions().isEmpty()) {
1310+
rightExpression = listExpression;
1311+
}
1312+
}
12961313

1314+
if (!(rightExpression instanceof ListExpression)) {
1315+
addStaticTypeError("Multiple assignments without list or tuple on the right-hand side are unsupported in static type checking mode", rightExpression);
1316+
return false;
1317+
}
1318+
1319+
TupleExpression tuple = (TupleExpression) leftExpression;
1320+
ListExpression values = (ListExpression) rightExpression;
1321+
List<Expression> tupleExpressions = tuple.getExpressions();
1322+
List<Expression> valueExpressions = values.getExpressions();
1323+
1324+
if (tupleExpressions.size() > valueExpressions.size()) {
1325+
addStaticTypeError("Incorrect number of values. Expected:" + tupleExpressions.size() + " Was:" + valueExpressions.size(), values);
1326+
return false;
1327+
}
1328+
1329+
for (int i = 0, n = tupleExpressions.size(); i < n; i += 1) {
1330+
ClassNode valueType = getType(valueExpressions.get(i));
1331+
ClassNode targetType = getType(tupleExpressions.get(i));
1332+
if (!isAssignableTo(valueType, targetType)) {
1333+
addStaticTypeError("Cannot assign value of type " + prettyPrintType(valueType) + " to variable of type " + prettyPrintType(targetType), rightExpression);
1334+
return false;
1335+
}
1336+
storeType(tupleExpressions.get(i), valueType);
1337+
}
1338+
}
1339+
// GRECLIPSE end
12971340
return true;
12981341
}
12991342

1343+
// GRECLIPSE add -- GROOVY-10063
1344+
private static final List<ClassNode> TUPLE_TYPES = Collections.unmodifiableList(Arrays.asList(
1345+
ClassHelper.makeWithoutCaching(groovy.lang.Tuple .class),
1346+
ClassHelper.makeWithoutCaching(groovy.lang.Tuple1.class),
1347+
ClassHelper.makeWithoutCaching(groovy.lang.Tuple2.class),
1348+
ClassHelper.makeWithoutCaching(groovy.lang.Tuple3.class),
1349+
ClassHelper.makeWithoutCaching(groovy.lang.Tuple4.class),
1350+
ClassHelper.makeWithoutCaching(groovy.lang.Tuple5.class),
1351+
ClassHelper.makeWithoutCaching(groovy.lang.Tuple6.class),
1352+
ClassHelper.makeWithoutCaching(groovy.lang.Tuple7.class),
1353+
ClassHelper.makeWithoutCaching(groovy.lang.Tuple8.class),
1354+
ClassHelper.makeWithoutCaching(groovy.lang.Tuple9.class)
1355+
));
1356+
// GRECLIPSE end
1357+
13001358
private static ClassNode adjustTypeForSpreading(ClassNode inferredRightExpressionType, Expression leftExpression) {
13011359
// imagine we have: list*.foo = 100
13021360
// then the assignment must be checked against [100], not 100

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

+48-1
Original file line numberDiff line numberDiff line change
@@ -1147,7 +1147,7 @@ protected void pushInstanceOfTypeInfo(final Expression objectOfInstanceOf, final
11471147
}
11481148

11491149
private boolean typeCheckMultipleAssignmentAndContinue(final Expression leftExpression, Expression rightExpression) {
1150-
// multiple assignment check
1150+
/* GRECLIPSE edit -- GROOVY-10063
11511151
if (!(leftExpression instanceof TupleExpression)) return true;
11521152
11531153
Expression transformedRightExpression = transformRightExpressionToSupportMultipleAssignment(rightExpression);
@@ -1178,10 +1178,56 @@ private boolean typeCheckMultipleAssignmentAndContinue(final Expression leftExpr
11781178
storeType(tupleExpression, elemType);
11791179
}
11801180
}
1181+
*/
1182+
if (leftExpression instanceof TupleExpression) {
1183+
if (rightExpression instanceof VariableExpression || rightExpression instanceof PropertyExpression || rightExpression instanceof MethodCall) {
1184+
ClassNode inferredType = Optional.ofNullable(getType(rightExpression)).orElseGet(rightExpression::getType);
1185+
GenericsType[] genericsTypes = inferredType.getGenericsTypes();
1186+
ListExpression listExpression = new ListExpression();
1187+
listExpression.setSourcePosition(rightExpression);
1188+
1189+
// convert Tuple[1-16] bearing expressions to mock list for checking
1190+
for (int n = TUPLE_TYPES.indexOf(inferredType), i = 0; i < n; i += 1) {
1191+
ClassNode type = (genericsTypes != null ? genericsTypes[i].getType() : OBJECT_TYPE);
1192+
listExpression.addExpression(varX("v" + (i + 1), type));
1193+
}
1194+
if (!listExpression.getExpressions().isEmpty()) {
1195+
rightExpression = listExpression;
1196+
}
1197+
}
1198+
1199+
if (!(rightExpression instanceof ListExpression)) {
1200+
addStaticTypeError("Multiple assignments without list or tuple on the right-hand side are unsupported in static type checking mode", rightExpression);
1201+
return false;
1202+
}
1203+
1204+
TupleExpression tuple = (TupleExpression) leftExpression;
1205+
ListExpression values = (ListExpression) rightExpression;
1206+
List<Expression> tupleExpressions = tuple.getExpressions();
1207+
List<Expression> valueExpressions = values.getExpressions();
11811208

1209+
if (tupleExpressions.size() > valueExpressions.size()) {
1210+
addStaticTypeError("Incorrect number of values. Expected:" + tupleExpressions.size() + " Was:" + valueExpressions.size(), values);
1211+
return false;
1212+
}
1213+
1214+
for (int i = 0, n = tupleExpressions.size(); i < n; i += 1) {
1215+
ClassNode valueType = getType(valueExpressions.get(i));
1216+
ClassNode targetType = getType(tupleExpressions.get(i));
1217+
if (!isAssignableTo(valueType, targetType)) {
1218+
addStaticTypeError("Cannot assign value of type " + prettyPrintType(valueType) + " to variable of type " + prettyPrintType(targetType), rightExpression);
1219+
return false;
1220+
}
1221+
storeType(tupleExpressions.get(i), valueType);
1222+
}
1223+
}
1224+
// GRECLIPSE end
11821225
return true;
11831226
}
11841227

1228+
private static final List<ClassNode> TUPLE_TYPES = Collections.unmodifiableList(Arrays.stream(TUPLE_CLASSES).map(ClassHelper::makeWithoutCaching).collect(java.util.stream.Collectors.toList()));
1229+
1230+
/* GRECLIPSE edit -- GROOVY-10063
11851231
private Expression transformRightExpressionToSupportMultipleAssignment(final Expression rightExpression) {
11861232
if (rightExpression instanceof ListExpression) {
11871233
return rightExpression;
@@ -1219,6 +1265,7 @@ private Expression transformRightExpressionToSupportMultipleAssignment(final Exp
12191265
12201266
return null;
12211267
}
1268+
*/
12221269

12231270
private static ClassNode adjustTypeForSpreading(final ClassNode inferredRightExpressionType, final Expression leftExpression) {
12241271
// imagine we have: list*.foo = 100

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

+48-1
Original file line numberDiff line numberDiff line change
@@ -1124,7 +1124,7 @@ protected void pushInstanceOfTypeInfo(final Expression objectOfInstanceOf, final
11241124
}
11251125

11261126
private boolean typeCheckMultipleAssignmentAndContinue(final Expression leftExpression, Expression rightExpression) {
1127-
// multiple assignment check
1127+
/* GRECLIPSE edit -- GROOVY-10063
11281128
if (!(leftExpression instanceof TupleExpression)) return true;
11291129
11301130
Expression transformedRightExpression = transformRightExpressionToSupportMultipleAssignment(rightExpression);
@@ -1155,10 +1155,56 @@ private boolean typeCheckMultipleAssignmentAndContinue(final Expression leftExpr
11551155
storeType(tupleExpression, elemType);
11561156
}
11571157
}
1158+
*/
1159+
if (leftExpression instanceof TupleExpression) {
1160+
if (rightExpression instanceof VariableExpression || rightExpression instanceof PropertyExpression || rightExpression instanceof MethodCall) {
1161+
ClassNode inferredType = Optional.ofNullable(getType(rightExpression)).orElseGet(rightExpression::getType);
1162+
GenericsType[] genericsTypes = inferredType.getGenericsTypes();
1163+
ListExpression listExpression = new ListExpression();
1164+
listExpression.setSourcePosition(rightExpression);
1165+
1166+
// convert Tuple[1-16] bearing expressions to mock list for checking
1167+
for (int n = TUPLE_TYPES.indexOf(inferredType), i = 0; i < n; i += 1) {
1168+
ClassNode type = (genericsTypes != null ? genericsTypes[i].getType() : OBJECT_TYPE);
1169+
listExpression.addExpression(varX("v" + (i + 1), type));
1170+
}
1171+
if (!listExpression.getExpressions().isEmpty()) {
1172+
rightExpression = listExpression;
1173+
}
1174+
}
1175+
1176+
if (!(rightExpression instanceof ListExpression)) {
1177+
addStaticTypeError("Multiple assignments without list or tuple on the right-hand side are unsupported in static type checking mode", rightExpression);
1178+
return false;
1179+
}
1180+
1181+
TupleExpression tuple = (TupleExpression) leftExpression;
1182+
ListExpression values = (ListExpression) rightExpression;
1183+
List<Expression> tupleExpressions = tuple.getExpressions();
1184+
List<Expression> valueExpressions = values.getExpressions();
11581185

1186+
if (tupleExpressions.size() > valueExpressions.size()) {
1187+
addStaticTypeError("Incorrect number of values. Expected:" + tupleExpressions.size() + " Was:" + valueExpressions.size(), values);
1188+
return false;
1189+
}
1190+
1191+
for (int i = 0, n = tupleExpressions.size(); i < n; i += 1) {
1192+
ClassNode valueType = getType(valueExpressions.get(i));
1193+
ClassNode targetType = getType(tupleExpressions.get(i));
1194+
if (!isAssignableTo(valueType, targetType)) {
1195+
addStaticTypeError("Cannot assign value of type " + prettyPrintType(valueType) + " to variable of type " + prettyPrintType(targetType), rightExpression);
1196+
return false;
1197+
}
1198+
storeType(tupleExpressions.get(i), valueType);
1199+
}
1200+
}
1201+
// GRECLIPSE end
11591202
return true;
11601203
}
11611204

1205+
private static final List<ClassNode> TUPLE_TYPES = Collections.unmodifiableList(Arrays.stream(TUPLE_CLASSES).map(ClassHelper::makeWithoutCaching).collect(java.util.stream.Collectors.toList()));
1206+
1207+
/* GRECLIPSE edit
11621208
private Expression transformRightExpressionToSupportMultipleAssignment(final Expression rightExpression) {
11631209
if (rightExpression instanceof ListExpression) {
11641210
return rightExpression;
@@ -1196,6 +1242,7 @@ private Expression transformRightExpressionToSupportMultipleAssignment(final Exp
11961242
11971243
return null;
11981244
}
1245+
*/
11991246

12001247
private ClassNode adjustTypeForSpreading(final ClassNode inferredRightExpressionType, final Expression leftExpression) {
12011248
// imagine we have: list*.foo = 100

0 commit comments

Comments
 (0)