Skip to content

Commit ec79e32

Browse files
committed
GROOVY-9910, GROOVY-10072, GROOVY-10372, GROOVY-11083
1 parent b81c3f5 commit ec79e32

6 files changed

Lines changed: 183 additions & 12 deletions

File tree

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8193,6 +8193,8 @@ public void testCompileStatic11029() {
81938193

81948194
@Test
81958195
public void testCompileStatic11060() {
8196+
assumeTrue(isAtLeastGroovy(50));
8197+
81968198
//@formatter:off
81978199
String[] sources = {
81988200
"Main.groovy",

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

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3200,6 +3200,29 @@ public void testTypeChecked9907() {
32003200
runConformTest(sources, "1");
32013201
}
32023202

3203+
@Test
3204+
public void testTypeChecked9910() {
3205+
//@formatter:off
3206+
String[] sources = {
3207+
"Main.groovy",
3208+
"@groovy.transform.TypeChecked\n" +
3209+
"void test() {\n" +
3210+
" java.util.stream.IntStream.range(0, 2).forEach(\n" +
3211+
" {String s ->}\n" + // no reference to "s"
3212+
" )\n" +
3213+
"}\n",
3214+
};
3215+
//@formatter:on
3216+
3217+
runNegativeTest(sources,
3218+
"----------\n" +
3219+
"1. ERROR in Main.groovy (at line 4)\n" +
3220+
"\t{String s ->}\n" +
3221+
"\t ^^^^^^^^\n" +
3222+
"Groovy:[Static type checking] - Expected type int for closure parameter: s\n" +
3223+
"----------\n");
3224+
}
3225+
32033226
@Test
32043227
public void testTypeChecked9915() {
32053228
for (String type : new String[] {"List", "Collection", "Iterable"}) {
@@ -5886,6 +5909,97 @@ public void testTypeChecked10368() {
58865909
}
58875910
}
58885911

5912+
@Test
5913+
public void testTypeChecked10372() {
5914+
//@formatter:off
5915+
String[] sources = {
5916+
"Main.groovy",
5917+
"import java.util.function.Consumer\n" +
5918+
"@groovy.transform.TypeChecked\n" +
5919+
"void test() {\n" +
5920+
" Consumer<String> c = {Date d ->}\n" +
5921+
"}\n",
5922+
};
5923+
//@formatter:on
5924+
5925+
runNegativeTest(sources,
5926+
"----------\n" +
5927+
"1. ERROR in Main.groovy (at line 4)\n" +
5928+
"\tConsumer<String> c = {Date d ->}\n" +
5929+
"\t ^^^^^^\n" +
5930+
"Groovy:[Static type checking] - Expected type java.lang.String for closure parameter: d\n" +
5931+
"----------\n");
5932+
}
5933+
5934+
@Test
5935+
public void testTypeChecked10372a() {
5936+
assumeTrue(isParrotParser());
5937+
5938+
//@formatter:off
5939+
String[] sources = {
5940+
"Main.groovy",
5941+
"import java.util.function.Consumer\n" +
5942+
"@groovy.transform.TypeChecked\n" +
5943+
"void test() {\n" +
5944+
" Consumer<String> c = (Date d) -> {}\n" +
5945+
"}\n",
5946+
};
5947+
//@formatter:on
5948+
5949+
runNegativeTest(sources,
5950+
"----------\n" +
5951+
"1. ERROR in Main.groovy (at line 4)\n" +
5952+
"\tConsumer<String> c = (Date d) -> {}\n" +
5953+
"\t ^^^^^^\n" +
5954+
"Groovy:[Static type checking] - Expected type java.lang.String for lambda parameter: d\n" +
5955+
"----------\n");
5956+
}
5957+
5958+
@Test
5959+
public void testTypeChecked10372b() {
5960+
//@formatter:off
5961+
String[] sources = {
5962+
"Main.groovy",
5963+
"import java.util.function.Consumer\n" +
5964+
"void setFoo(Consumer<String> c) {}\n" +
5965+
"@groovy.transform.TypeChecked\n" +
5966+
"void test() {\n" +
5967+
" foo = {Date d ->}\n" +
5968+
" foo = {s -> s.indexOf(' ')}\n" +
5969+
"}\n",
5970+
};
5971+
//@formatter:on
5972+
5973+
runNegativeTest(sources,
5974+
"----------\n" +
5975+
"1. ERROR in Main.groovy (at line 5)\n" +
5976+
"\tfoo = {Date d ->}\n" +
5977+
"\t ^^^^^^\n" +
5978+
"Groovy:[Static type checking] - Expected type java.lang.String for closure parameter: d\n" +
5979+
"----------\n");
5980+
}
5981+
5982+
@Test
5983+
public void testTypeChecked10372c() {
5984+
//@formatter:off
5985+
String[] sources = {
5986+
"Main.groovy",
5987+
"@groovy.transform.TypeChecked\n" +
5988+
"void test() {\n" +
5989+
" ['a','b'].collect{Date d ->}\n" +
5990+
"}\n",
5991+
};
5992+
//@formatter:on
5993+
5994+
runNegativeTest(sources,
5995+
"----------\n" +
5996+
"1. ERROR in Main.groovy (at line 3)\n" +
5997+
"\t['a','b'].collect{Date d ->}\n" +
5998+
"\t ^^^^^^\n" +
5999+
"Groovy:[Static type checking] - Expected type java.lang.String for closure parameter: d\n" +
6000+
"----------\n");
6001+
}
6002+
58896003
@Test
58906004
public void testTypeChecked10414() {
58916005
//@formatter:off
@@ -6933,4 +7047,27 @@ public void testTypeChecked10981a() {
69337047

69347048
runConformTest(sources, "1");
69357049
}
7050+
7051+
@Test
7052+
public void testTypeChecked11083() {
7053+
//@formatter:off
7054+
String[] sources = {
7055+
"Main.groovy",
7056+
"import java.util.function.Consumer\n" +
7057+
"void setFoo(Consumer<Number> c) {}\n" +
7058+
"@groovy.transform.TypeChecked\n" +
7059+
"void test(Date d) {\n" +
7060+
" foo = {n = d ->}\n" +
7061+
"}\n",
7062+
};
7063+
//@formatter:on
7064+
7065+
runNegativeTest(sources,
7066+
"----------\n" +
7067+
"1. ERROR in Main.groovy (at line 5)\n" +
7068+
"\tfoo = {n = d ->}\n" +
7069+
"\t ^\n" +
7070+
"Groovy:[Static type checking] - Cannot assign value of type java.util.Date to variable of type java.lang.Number\n" +
7071+
"----------\n");
7072+
}
69367073
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1344,7 +1344,7 @@ public static boolean isUsingGenericsOrIsArrayUsingGenerics(final ClassNode cn)
13441344
if (cn.isArray()) {
13451345
return isUsingGenericsOrIsArrayUsingGenerics(cn.getComponentType());
13461346
}
1347-
return (cn.isUsingGenerics() && cn.getGenericsTypes() != null);
1347+
return (cn.isUsingGenerics() && (cn.getGenericsTypes() != null || cn.isGenericsPlaceHolder())); // GRECLIPSE add
13481348
}
13491349

13501350
/**

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

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -958,7 +958,7 @@ private void inferParameterAndReturnTypesOfClosureOnRHS(final ClassNode lhsType,
958958

959959
if (samParameterTypes.length == 1 && hasImplicitParameter(rhsExpression)) {
960960
Variable it = rhsExpression.getVariableScope().getDeclaredVariable("it"); // GROOVY-7141
961-
closureParameters = new Parameter[] {it instanceof Parameter ? (Parameter) it : new Parameter(DYNAMIC_TYPE, "")};
961+
closureParameters = new Parameter[]{it instanceof Parameter ? (Parameter) it : new Parameter(ClassHelper.dynamicType(), "")};
962962
}
963963

964964
int n = closureParameters.length;
@@ -969,6 +969,9 @@ private void inferParameterAndReturnTypesOfClosureOnRHS(final ClassNode lhsType,
969969
if (parameter.isDynamicTyped()) {
970970
parameter.setType(samParameterTypes[i]);
971971
}
972+
// GRECLIPSE add -- GROOVY-10372
973+
else checkParamType(parameter, samParameterTypes[i], i == n-1, rhsExpression instanceof LambdaExpression);
974+
// GRECLIPSE end
972975
}
973976
} else {
974977
String descriptor = toMethodParametersString(findSAM(lhsType).getName(), samParameterTypes);
@@ -3314,27 +3317,40 @@ protected void inferClosureParameterTypes(final ClassNode receiver, final Expres
33143317
}
33153318
}
33163319
}
3317-
3320+
/* GRECLIPSE edit
33183321
ClassNode[] samParamTypes = GenericsUtils.parameterizeSAM(applyGenericsContext(context, target.getType())).getV1();
3319-
3322+
*/
33203323
ClassNode[] paramTypes = expression.getNodeMetaData(CLOSURE_ARGUMENTS);
33213324
if (paramTypes == null) {
3325+
// GRECLIPSE add
3326+
ClassNode targetType = target.getType();
3327+
if (targetType != null && targetType.isGenericsPlaceHolder())
3328+
targetType = getCombinedBoundType(targetType.asGenericsType());
3329+
targetType = applyGenericsContext(context, targetType); // fill place-holders
3330+
ClassNode[] samParamTypes = GenericsUtils.parameterizeSAM(targetType).getV1();
3331+
// GRECLIPSE end
33223332
int n; Parameter[] p = expression.getParameters();
33233333
if (p == null) {
33243334
// zero parameters
33253335
paramTypes = ClassNode.EMPTY_ARRAY;
33263336
} else if ((n = p.length) == 0) {
33273337
// implicit parameter(s)
33283338
paramTypes = samParamTypes;
3329-
} else { // TODO: error for length mismatch
3339+
} else {
33303340
paramTypes = Arrays.copyOf(samParamTypes, n);
3341+
// GRECLIPSE add -- GROOVY-9910, GROOVY-10072, GROOVY-10372
3342+
for (int i = 0, j = Math.min(n, samParamTypes.length); i < j; i += 1) {
3343+
if (p[i].isDynamicTyped()) { p[i].setType(samParamTypes[i]); } else
3344+
checkParamType(p[i], paramTypes[i], i == n-1, expression instanceof LambdaExpression);
3345+
}
3346+
// GRECLIPSE end
33313347
}
3332-
expression.putNodeMetaData(CLOSURE_ARGUMENTS, paramTypes);
33333348
// GRECLIPSE add -- GROOVY-8499
33343349
if (paramTypes.length != samParamTypes.length) {
33353350
addError("Incorrect number of parameters. Expected " + samParamTypes.length + " but found " + paramTypes.length, expression);
33363351
}
33373352
// GRECLIPSE end
3353+
expression.putNodeMetaData(CLOSURE_ARGUMENTS, paramTypes);
33383354
}
33393355
}
33403356
}
@@ -3450,15 +3466,26 @@ private void doInferClosureParameterTypes(final ClassNode receiver, final Expres
34503466
addError("Incorrect number of parameters. Expected " + inferred.length + " but found " + n, expression);
34513467
}
34523468
}
3469+
/* GRECLIPSE edit
34533470
if (!typeCheckMethodArgumentWithGenerics(declaredType, inferredType, i == n-1)) {
34543471
addError("Expected parameter of type " + prettyPrintType(inferredType) + " but got " + prettyPrintType(declaredType), closureParam.getType());
34553472
}
3473+
*/
3474+
checkParamType(closureParam, inferredType, i == n-1, false);
3475+
// GRECLIPSE end
34563476
typeCheckingContext.controlStructureVariables.put(closureParam, inferredType);
34573477
}
34583478
}
34593479
}
34603480
}
34613481

3482+
// GRECLIPSE add
3483+
private void checkParamType(final Parameter source, final ClassNode target, final boolean isLast, final boolean lambda) {
3484+
if (!typeCheckMethodArgumentWithGenerics(source.getOriginType(), target, isLast))
3485+
addStaticTypeError("Expected type " + prettyPrintType(target) + " for " + (lambda ? "lambda" : "closure") + " parameter: " + source.getName(), source);
3486+
}
3487+
// GRECLIPSE end
3488+
34623489
private ClassNode[] resolveGenericsFromTypeHint(final ClassNode receiver, final Expression arguments, final MethodNode selectedMethod, final ClassNode[] signature) {
34633490
ClassNode dummyResultNode = new ClassNode("ClForInference$" + UNIQUE_LONG.incrementAndGet(), 0, OBJECT_TYPE).getPlainNodeReference();
34643491
GenericsType[] genericTypes = new GenericsType[signature.length];

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3016,18 +3016,21 @@ protected void inferClosureParameterTypes(final ClassNode receiver, final Expres
30163016
} else if ((n = p.length) == 0) {
30173017
// implicit parameter(s)
30183018
paramTypes = samParamTypes;
3019-
} else { // TODO: error for length mismatch
3019+
} else {
30203020
paramTypes = Arrays.copyOf(samParamTypes, n);
3021-
for (int i = 0; i < Math.min(n, samParamTypes.length); i += 1) {
3021+
for (int i = 0, j = Math.min(n, samParamTypes.length); i < j; i += 1) {
3022+
// GRECLIPSE add -- GROOVY-10072, GROOVY-11083
3023+
if (p[i].isDynamicTyped()) { p[i].setType(samParamTypes[i]); } else
3024+
// GRECLIPSE end
30223025
checkParamType(p[i], paramTypes[i], i == n-1, expression instanceof LambdaExpression);
30233026
}
30243027
}
3025-
expression.putNodeMetaData(CLOSURE_ARGUMENTS, paramTypes);
30263028
// GRECLIPSE add -- GROOVY-8499
30273029
if (paramTypes.length != samParamTypes.length) {
30283030
addError("Incorrect number of parameters. Expected " + samParamTypes.length + " but found " + paramTypes.length, expression);
30293031
}
30303032
// GRECLIPSE end
3033+
expression.putNodeMetaData(CLOSURE_ARGUMENTS, paramTypes);
30313034
}
30323035
}
30333036
}

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2513,7 +2513,6 @@ && isStringType(getType(nameExpr))) {
25132513
}
25142514

25152515
if (!candidates.isEmpty()) {
2516-
int nParameters = candidates.stream().mapToInt(m -> m.getParameters().length).reduce((i,j) -> i == j ? i : -1).getAsInt();
25172516
Map<GenericsTypeName, GenericsType> gts = GenericsUtils.extractPlaceholders(receiverType);
25182517
candidates.stream().map(candidate -> applyGenericsContext(gts, candidate.getReturnType()))
25192518
.reduce(WideningCategories::lowestUpperBound).ifPresent(returnType -> {
@@ -3147,14 +3146,17 @@ protected void inferClosureParameterTypes(final ClassNode receiver, final Expres
31473146
paramTypes = samParamTypes;
31483147
} else { // TODO: error for length mismatch
31493148
paramTypes = Arrays.copyOf(samParamTypes, n);
3150-
for (int i = 0; i < Math.min(n, samParamTypes.length); i += 1) {
3149+
for (int i = 0, j = Math.min(n, samParamTypes.length); i < j; i += 1) {
3150+
// GRECLIPSE add -- GROOVY-10072, GROOVY-11083
3151+
if (p[i].isDynamicTyped()) { p[i].setType(samParamTypes[i]); } else
3152+
// GRECLIPSE end
31513153
checkParamType(p[i], paramTypes[i], i == n-1, expression instanceof LambdaExpression);
31523154
}
31533155
}
3154-
expression.putNodeMetaData(CLOSURE_ARGUMENTS, paramTypes);
31553156
if (paramTypes.length != samParamTypes.length) { // GROOVY-8499
31563157
addError("Incorrect number of parameters. Expected " + samParamTypes.length + " but found " + paramTypes.length, expression);
31573158
}
3159+
expression.putNodeMetaData(CLOSURE_ARGUMENTS, paramTypes);
31583160
}
31593161
}
31603162
}

0 commit comments

Comments
 (0)