Skip to content

Commit

Permalink
GROOVY-10063
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-milles committed Apr 28, 2021
1 parent 2730f3a commit aa1935a
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2373,4 +2373,24 @@ public void testTypeChecked10062() {

runConformTest(sources, "42");
}

@Test
public void testTypeChecked10063() {
//@formatter:off
String[] sources = {
"Main.groovy",
"static Tuple2<String,Integer> m() {\n" +
" new Tuple2<>('answer', 42)\n" +
"}\n" +
"@groovy.transform.TypeChecked\n" +
"void test() {\n" +
" def (String string, Integer number) = m()\n" +
" print number\n" +
"}\n" +
"test()\n",
};
//@formatter:on

runConformTest(sources, "42");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1265,7 +1265,7 @@ protected void pushInstanceOfTypeInfo(final Expression objectOfInstanceOf, final
}

private boolean typeCheckMultipleAssignmentAndContinue(Expression leftExpression, Expression rightExpression) {
// multiple assignment check
/* GRECLIPSE edit -- GROOVY-10063
if (!(leftExpression instanceof TupleExpression)) return true;
if (!(rightExpression instanceof ListExpression)) {
Expand Down Expand Up @@ -1293,10 +1293,68 @@ private boolean typeCheckMultipleAssignmentAndContinue(Expression leftExpression
storeType(tupleExpression, elemType);
}
}
*/
if (leftExpression instanceof TupleExpression) {
if (rightExpression instanceof VariableExpression || rightExpression instanceof PropertyExpression || rightExpression instanceof MethodCall) {
ClassNode inferredType = Optional.ofNullable(getType(rightExpression)).orElseGet(rightExpression::getType);
GenericsType[] genericsTypes = inferredType.getGenericsTypes();
ListExpression listExpression = new ListExpression();
listExpression.setSourcePosition(rightExpression);

// convert Tuple[1-16] bearing expressions to mock list for checking
for (int n = TUPLE_TYPES.indexOf(inferredType), i = 0; i < n; i += 1) {
ClassNode type = (genericsTypes != null ? genericsTypes[i].getType() : OBJECT_TYPE);
listExpression.addExpression(varX("v" + (i + 1), type));
}
if (!listExpression.getExpressions().isEmpty()) {
rightExpression = listExpression;
}
}

if (!(rightExpression instanceof ListExpression)) {
addStaticTypeError("Multiple assignments without list or tuple on the right-hand side are unsupported in static type checking mode", rightExpression);
return false;
}

TupleExpression tuple = (TupleExpression) leftExpression;
ListExpression values = (ListExpression) rightExpression;
List<Expression> tupleExpressions = tuple.getExpressions();
List<Expression> valueExpressions = values.getExpressions();

if (tupleExpressions.size() > valueExpressions.size()) {
addStaticTypeError("Incorrect number of values. Expected:" + tupleExpressions.size() + " Was:" + valueExpressions.size(), values);
return false;
}

for (int i = 0, n = tupleExpressions.size(); i < n; i += 1) {
ClassNode valueType = getType(valueExpressions.get(i));
ClassNode targetType = getType(tupleExpressions.get(i));
if (!isAssignableTo(valueType, targetType)) {
addStaticTypeError("Cannot assign value of type " + prettyPrintType(valueType) + " to variable of type " + prettyPrintType(targetType), rightExpression);
return false;
}
storeType(tupleExpressions.get(i), valueType);
}
}
// GRECLIPSE end
return true;
}

// GRECLIPSE add -- GROOVY-10063
private static final List<ClassNode> TUPLE_TYPES = Collections.unmodifiableList(Arrays.asList(
ClassHelper.makeWithoutCaching(groovy.lang.Tuple .class),
ClassHelper.makeWithoutCaching(groovy.lang.Tuple1.class),
ClassHelper.makeWithoutCaching(groovy.lang.Tuple2.class),
ClassHelper.makeWithoutCaching(groovy.lang.Tuple3.class),
ClassHelper.makeWithoutCaching(groovy.lang.Tuple4.class),
ClassHelper.makeWithoutCaching(groovy.lang.Tuple5.class),
ClassHelper.makeWithoutCaching(groovy.lang.Tuple6.class),
ClassHelper.makeWithoutCaching(groovy.lang.Tuple7.class),
ClassHelper.makeWithoutCaching(groovy.lang.Tuple8.class),
ClassHelper.makeWithoutCaching(groovy.lang.Tuple9.class)
));
// GRECLIPSE end

private static ClassNode adjustTypeForSpreading(ClassNode inferredRightExpressionType, Expression leftExpression) {
// imagine we have: list*.foo = 100
// then the assignment must be checked against [100], not 100
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1147,7 +1147,7 @@ protected void pushInstanceOfTypeInfo(final Expression objectOfInstanceOf, final
}

private boolean typeCheckMultipleAssignmentAndContinue(final Expression leftExpression, Expression rightExpression) {
// multiple assignment check
/* GRECLIPSE edit -- GROOVY-10063
if (!(leftExpression instanceof TupleExpression)) return true;
Expression transformedRightExpression = transformRightExpressionToSupportMultipleAssignment(rightExpression);
Expand Down Expand Up @@ -1178,10 +1178,56 @@ private boolean typeCheckMultipleAssignmentAndContinue(final Expression leftExpr
storeType(tupleExpression, elemType);
}
}
*/
if (leftExpression instanceof TupleExpression) {
if (rightExpression instanceof VariableExpression || rightExpression instanceof PropertyExpression || rightExpression instanceof MethodCall) {
ClassNode inferredType = Optional.ofNullable(getType(rightExpression)).orElseGet(rightExpression::getType);
GenericsType[] genericsTypes = inferredType.getGenericsTypes();
ListExpression listExpression = new ListExpression();
listExpression.setSourcePosition(rightExpression);

// convert Tuple[1-16] bearing expressions to mock list for checking
for (int n = TUPLE_TYPES.indexOf(inferredType), i = 0; i < n; i += 1) {
ClassNode type = (genericsTypes != null ? genericsTypes[i].getType() : OBJECT_TYPE);
listExpression.addExpression(varX("v" + (i + 1), type));
}
if (!listExpression.getExpressions().isEmpty()) {
rightExpression = listExpression;
}
}

if (!(rightExpression instanceof ListExpression)) {
addStaticTypeError("Multiple assignments without list or tuple on the right-hand side are unsupported in static type checking mode", rightExpression);
return false;
}

TupleExpression tuple = (TupleExpression) leftExpression;
ListExpression values = (ListExpression) rightExpression;
List<Expression> tupleExpressions = tuple.getExpressions();
List<Expression> valueExpressions = values.getExpressions();

if (tupleExpressions.size() > valueExpressions.size()) {
addStaticTypeError("Incorrect number of values. Expected:" + tupleExpressions.size() + " Was:" + valueExpressions.size(), values);
return false;
}

for (int i = 0, n = tupleExpressions.size(); i < n; i += 1) {
ClassNode valueType = getType(valueExpressions.get(i));
ClassNode targetType = getType(tupleExpressions.get(i));
if (!isAssignableTo(valueType, targetType)) {
addStaticTypeError("Cannot assign value of type " + prettyPrintType(valueType) + " to variable of type " + prettyPrintType(targetType), rightExpression);
return false;
}
storeType(tupleExpressions.get(i), valueType);
}
}
// GRECLIPSE end
return true;
}

private static final List<ClassNode> TUPLE_TYPES = Collections.unmodifiableList(Arrays.stream(TUPLE_CLASSES).map(ClassHelper::makeWithoutCaching).collect(java.util.stream.Collectors.toList()));

/* GRECLIPSE edit -- GROOVY-10063
private Expression transformRightExpressionToSupportMultipleAssignment(final Expression rightExpression) {
if (rightExpression instanceof ListExpression) {
return rightExpression;
Expand Down Expand Up @@ -1219,6 +1265,7 @@ private Expression transformRightExpressionToSupportMultipleAssignment(final Exp
return null;
}
*/

private static ClassNode adjustTypeForSpreading(final ClassNode inferredRightExpressionType, final Expression leftExpression) {
// imagine we have: list*.foo = 100
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1124,7 +1124,7 @@ protected void pushInstanceOfTypeInfo(final Expression objectOfInstanceOf, final
}

private boolean typeCheckMultipleAssignmentAndContinue(final Expression leftExpression, Expression rightExpression) {
// multiple assignment check
/* GRECLIPSE edit -- GROOVY-10063
if (!(leftExpression instanceof TupleExpression)) return true;
Expression transformedRightExpression = transformRightExpressionToSupportMultipleAssignment(rightExpression);
Expand Down Expand Up @@ -1155,10 +1155,56 @@ private boolean typeCheckMultipleAssignmentAndContinue(final Expression leftExpr
storeType(tupleExpression, elemType);
}
}
*/
if (leftExpression instanceof TupleExpression) {
if (rightExpression instanceof VariableExpression || rightExpression instanceof PropertyExpression || rightExpression instanceof MethodCall) {
ClassNode inferredType = Optional.ofNullable(getType(rightExpression)).orElseGet(rightExpression::getType);
GenericsType[] genericsTypes = inferredType.getGenericsTypes();
ListExpression listExpression = new ListExpression();
listExpression.setSourcePosition(rightExpression);

// convert Tuple[1-16] bearing expressions to mock list for checking
for (int n = TUPLE_TYPES.indexOf(inferredType), i = 0; i < n; i += 1) {
ClassNode type = (genericsTypes != null ? genericsTypes[i].getType() : OBJECT_TYPE);
listExpression.addExpression(varX("v" + (i + 1), type));
}
if (!listExpression.getExpressions().isEmpty()) {
rightExpression = listExpression;
}
}

if (!(rightExpression instanceof ListExpression)) {
addStaticTypeError("Multiple assignments without list or tuple on the right-hand side are unsupported in static type checking mode", rightExpression);
return false;
}

TupleExpression tuple = (TupleExpression) leftExpression;
ListExpression values = (ListExpression) rightExpression;
List<Expression> tupleExpressions = tuple.getExpressions();
List<Expression> valueExpressions = values.getExpressions();

if (tupleExpressions.size() > valueExpressions.size()) {
addStaticTypeError("Incorrect number of values. Expected:" + tupleExpressions.size() + " Was:" + valueExpressions.size(), values);
return false;
}

for (int i = 0, n = tupleExpressions.size(); i < n; i += 1) {
ClassNode valueType = getType(valueExpressions.get(i));
ClassNode targetType = getType(tupleExpressions.get(i));
if (!isAssignableTo(valueType, targetType)) {
addStaticTypeError("Cannot assign value of type " + prettyPrintType(valueType) + " to variable of type " + prettyPrintType(targetType), rightExpression);
return false;
}
storeType(tupleExpressions.get(i), valueType);
}
}
// GRECLIPSE end
return true;
}

private static final List<ClassNode> TUPLE_TYPES = Collections.unmodifiableList(Arrays.stream(TUPLE_CLASSES).map(ClassHelper::makeWithoutCaching).collect(java.util.stream.Collectors.toList()));

/* GRECLIPSE edit
private Expression transformRightExpressionToSupportMultipleAssignment(final Expression rightExpression) {
if (rightExpression instanceof ListExpression) {
return rightExpression;
Expand Down Expand Up @@ -1196,6 +1242,7 @@ private Expression transformRightExpressionToSupportMultipleAssignment(final Exp
return null;
}
*/

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

0 comments on commit aa1935a

Please sign in to comment.