Skip to content

Commit a05d059

Browse files
committed
GROOVY-10088
1 parent 74e24d8 commit a05d059

File tree

4 files changed

+68
-13
lines changed

4 files changed

+68
-13
lines changed

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

+28
Original file line numberDiff line numberDiff line change
@@ -2655,4 +2655,32 @@ public void testTypeChecked10067() {
26552655

26562656
runConformTest(sources);
26572657
}
2658+
2659+
@Test
2660+
public void testTypeChecked10088() {
2661+
//@formatter:off
2662+
String[] sources = {
2663+
"Main.groovy",
2664+
"@groovy.transform.TypeChecked\n" +
2665+
"void test() {\n" +
2666+
" new D<Number>().p = 'x'\n" +
2667+
"}\n",
2668+
2669+
"Types.groovy",
2670+
"class C<T> {\n" +
2671+
" void setP(T t) { }\n" +
2672+
"}\n" +
2673+
"class D<X> extends C<X> {\n" +
2674+
"}\n",
2675+
};
2676+
//@formatter:on
2677+
2678+
runNegativeTest(sources,
2679+
"----------\n" +
2680+
"1. ERROR in Main.groovy (at line 3)\n" +
2681+
"\tnew D<Number>().p = 'x'\n" +
2682+
"\t^^^^^^^^^^^^^^^^^^^^^^^\n" +
2683+
"Groovy:[Static type checking] - Cannot assign value of type java.lang.String to variable of type java.lang.Number\n" +
2684+
"----------\n");
2685+
}
26582686
}

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

+14-5
Original file line numberDiff line numberDiff line change
@@ -1120,8 +1120,16 @@ private boolean ensureValidSetter(final Expression expression, final Expression
11201120
// but we must check if the binary expression is an assignment
11211121
// because we need to check if a setter uses @DelegatesTo
11221122
VariableExpression ve = varX("%", setterInfo.receiverType);
1123-
// GRECLIPSE add -- GROOVY-8409, et al.
1123+
// GRECLIPSE add -- GROOVY-8409, GROOVY-10088, et al.
11241124
ve.setType(setterInfo.receiverType);
1125+
Function<MethodNode, ClassNode> firstParamType = (method) -> {
1126+
ClassNode type = method.getParameters()[0].getOriginType();
1127+
if (!method.isStatic() && !(method instanceof ExtensionMethodNode) && GenericsUtils.hasUnresolvedGenerics(type)) {
1128+
Map<GenericsTypeName, GenericsType> spec = extractPlaceHolders(null, setterInfo.receiverType, method.getDeclaringClass());
1129+
type = applyGenericsContext(spec, type);
1130+
}
1131+
return type;
1132+
};
11251133
// GRECLIPSE end
11261134
// for compound assignment "x op= y" find type as if it was "x = (x op y)"
11271135
final Expression newRightExpression = isCompoundAssignment(expression)
@@ -1135,7 +1143,7 @@ private boolean ensureValidSetter(final Expression expression, final Expression
11351143
// this may happen if there's a setter of type boolean/String/Class, and that we are using the property
11361144
// notation AND that the RHS is not a boolean/String/Class
11371145
for (MethodNode setter : setterInfo.setters) {
1138-
ClassNode type = getWrapper(setter.getParameters()[0].getOriginType());
1146+
ClassNode type = getWrapper(firstParamType.apply(setter));
11391147
if (Boolean_TYPE.equals(type) || STRING_TYPE.equals(type) || CLASS_Type.equals(type)) {
11401148
call = callX(ve, setterInfo.name, castX(type, newRightExpression));
11411149
call.setImplicitThis(false);
@@ -1150,13 +1158,14 @@ private boolean ensureValidSetter(final Expression expression, final Expression
11501158
if (directSetterCandidate != null) {
11511159
for (MethodNode setter : setterInfo.setters) {
11521160
if (setter == directSetterCandidate) {
1153-
leftExpression.putNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET, directSetterCandidate);
1154-
storeType(leftExpression, getType(newRightExpression));
1161+
leftExpression.putNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET, setter);
1162+
leftExpression.removeNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
1163+
storeType(leftExpression, firstParamType.apply(setter));
11551164
break;
11561165
}
11571166
}
11581167
} else {
1159-
ClassNode firstSetterType = setterInfo.setters.iterator().next().getParameters()[0].getOriginType();
1168+
ClassNode firstSetterType = firstParamType.apply(setterInfo.setters.get(0));
11601169
addAssignmentError(firstSetterType, getType(newRightExpression), expression);
11611170
return true;
11621171
}

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

+12-4
Original file line numberDiff line numberDiff line change
@@ -1000,8 +1000,16 @@ private boolean ensureValidSetter(final Expression expression, final Expression
10001000
// but we must check if the binary expression is an assignment
10011001
// because we need to check if a setter uses @DelegatesTo
10021002
VariableExpression ve = varX("%", setterInfo.receiverType);
1003-
// GRECLIPSE add -- GROOVY-8409, et al.
1003+
// GRECLIPSE add -- GROOVY-8409, GROOVY-10088, et al.
10041004
ve.setType(setterInfo.receiverType);
1005+
Function<MethodNode, ClassNode> firstParamType = (method) -> {
1006+
ClassNode type = method.getParameters()[0].getOriginType();
1007+
if (!method.isStatic() && !(method instanceof ExtensionMethodNode) && GenericsUtils.hasUnresolvedGenerics(type)) {
1008+
Map<GenericsTypeName, GenericsType> spec = extractPlaceHolders(null, setterInfo.receiverType, method.getDeclaringClass());
1009+
type = applyGenericsContext(spec, type);
1010+
}
1011+
return type;
1012+
};
10051013
// GRECLIPSE end
10061014
// for compound assignment "x op= y" find type as if it was "x = (x op y)"
10071015
Expression newRightExpression = isCompoundAssignment(expression)
@@ -1015,7 +1023,7 @@ private boolean ensureValidSetter(final Expression expression, final Expression
10151023
// this may happen if there's a setter of type boolean/String/Class, and that we are using the property
10161024
// notation AND that the RHS is not a boolean/String/Class
10171025
for (MethodNode setter : setterInfo.setters) {
1018-
ClassNode type = getWrapper(setter.getParameters()[0].getOriginType());
1026+
ClassNode type = getWrapper(firstParamType.apply(setter));
10191027
if (Boolean_TYPE.equals(type) || STRING_TYPE.equals(type) || CLASS_Type.equals(type)) {
10201028
call = callX(ve, setterInfo.name, castX(type, newRightExpression));
10211029
call.setImplicitThis(false);
@@ -1032,13 +1040,13 @@ private boolean ensureValidSetter(final Expression expression, final Expression
10321040
if (setter == directSetterCandidate) {
10331041
leftExpression.putNodeMetaData(DIRECT_METHOD_CALL_TARGET, directSetterCandidate);
10341042
leftExpression.removeNodeMetaData(INFERRED_TYPE); // clear assumption
1035-
storeType(leftExpression, getType(newRightExpression));
1043+
storeType(leftExpression, firstParamType.apply(setter));
10361044
break;
10371045
}
10381046
}
10391047
return false;
10401048
} else {
1041-
ClassNode firstSetterType = setterInfo.setters.get(0).getParameters()[0].getOriginType();
1049+
ClassNode firstSetterType = firstParamType.apply(setterInfo.setters.get(0));
10421050
addAssignmentError(firstSetterType, getType(newRightExpression), expression);
10431051
return true;
10441052
}

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

+14-4
Original file line numberDiff line numberDiff line change
@@ -1003,12 +1003,22 @@ private boolean ensureValidSetter(final Expression expression, final Expression
10031003
visitMethodCallExpression(call);
10041004
return call.getNodeMetaData(DIRECT_METHOD_CALL_TARGET);
10051005
};
1006+
// GRECLIPSE add -- GROOVY-10088
1007+
Function<MethodNode, ClassNode> setterType = setter -> {
1008+
ClassNode type = setter.getParameters()[0].getOriginType();
1009+
if (!setter.isStatic() && !(setter instanceof ExtensionMethodNode) && GenericsUtils.hasUnresolvedGenerics(type)) {
1010+
Map<GenericsTypeName, GenericsType> spec = extractPlaceHolders(null, setterInfo.receiverType, setter.getDeclaringClass());
1011+
type = applyGenericsContext(spec, type);
1012+
}
1013+
return type;
1014+
};
1015+
// GRECLIPSE end
10061016

10071017
MethodNode methodTarget = setterCall.apply(newRightExpression);
10081018
if (methodTarget == null && !isCompoundAssignment(expression)) {
10091019
// if no direct match, try implicit conversion
10101020
for (MethodNode setter : setterInfo.setters) {
1011-
ClassNode lType = setter.getParameters()[0].getOriginType();
1021+
ClassNode lType = setterType.apply(setter);
10121022
ClassNode rType = getDeclaredOrInferredType(newRightExpression);
10131023
if (checkCompatibleAssignmentTypes(lType, rType, newRightExpression, false)) {
10141024
methodTarget = setterCall.apply(castX(lType, newRightExpression));
@@ -1023,14 +1033,14 @@ private boolean ensureValidSetter(final Expression expression, final Expression
10231033
for (MethodNode setter : setterInfo.setters) {
10241034
if (setter == methodTarget) {
10251035
leftExpression.putNodeMetaData(DIRECT_METHOD_CALL_TARGET, methodTarget);
1026-
leftExpression.removeNodeMetaData(INFERRED_TYPE); // clear the assumption
1027-
storeType(leftExpression, methodTarget.getParameters()[0].getOriginType());
1036+
leftExpression.removeNodeMetaData(INFERRED_TYPE); // clear assumption
1037+
storeType(leftExpression, setterType.apply(methodTarget));
10281038
break;
10291039
}
10301040
}
10311041
return false;
10321042
} else {
1033-
ClassNode firstSetterType = setterInfo.setters.get(0).getParameters()[0].getOriginType();
1043+
ClassNode firstSetterType = setterType.apply(setterInfo.setters.get(0));
10341044
addAssignmentError(firstSetterType, getType(newRightExpression), expression);
10351045
return true;
10361046
}

0 commit comments

Comments
 (0)