Skip to content

Commit 724f552

Browse files
committed
GROOVY-9033
1 parent cb5e3a8 commit 724f552

File tree

7 files changed

+238
-6
lines changed

7 files changed

+238
-6
lines changed

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

+66
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,72 @@ public void testTypeChecked8984a() {
900900
"----------\n");
901901
}
902902

903+
@Test
904+
public void testTypeChecked9033() {
905+
//@formatter:off
906+
String[] sources = {
907+
"Main.groovy",
908+
"@groovy.transform.TypeChecked\n" +
909+
"List<String> test() {\n" +
910+
" def list = []\n" +
911+
" list << null\n" +
912+
" return list\n" +
913+
"}\n",
914+
};
915+
//@formatter:on
916+
917+
runNegativeTest(sources,
918+
"----------\n" +
919+
"1. ERROR in Main.groovy (at line 5)\n" +
920+
"\treturn list\n" +
921+
"\t ^^^^\n" +
922+
"Groovy:[Static type checking] - Incompatible generic argument types. Cannot assign java.util.List<java.lang.Object> to: java.util.List<java.lang.String>\n" +
923+
"----------\n");
924+
}
925+
926+
@Test
927+
public void testTypeChecked9033a() {
928+
//@formatter:off
929+
String[] sources = {
930+
"Main.groovy",
931+
"@groovy.transform.TypeChecked\n" +
932+
"void test() {\n" +
933+
" def map = [key: []]\n" +
934+
" map.add('foo','bar')\n" +
935+
"}\n" +
936+
"test()",
937+
};
938+
//@formatter:on
939+
940+
runNegativeTest(sources,
941+
"----------\n" +
942+
"1. ERROR in Main.groovy (at line 4)\n" +
943+
"\tmap.add('foo','bar')\n" +
944+
"\t^^^^^^^^^^^^^^^^^^^^\n" +
945+
"Groovy:[Static type checking] - Cannot find matching method java.util.LinkedHashMap#add(java.lang.String, java.lang.String). Please check if the declared type is correct and if the method exists.\n" +
946+
"----------\n");
947+
}
948+
949+
@Test
950+
public void testTypeChecked9033b() {
951+
//@formatter:off
952+
String[] sources = {
953+
"Main.groovy",
954+
"@groovy.transform.TypeChecked\n" +
955+
"void test() {\n" +
956+
" @groovy.transform.ASTTest(phase=INSTRUCTION_SELECTION, value={\n" +
957+
" def type = node.getNodeMetaData(org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_TYPE)\n" +
958+
" assert type.toString(false) == 'java.util.LinkedList<java.lang.String>'\n" +
959+
" })\n" +
960+
" Iterable<String> list = new LinkedList()\n" +
961+
"}\n" +
962+
"test()",
963+
};
964+
//@formatter:on
965+
966+
runConformTest(sources);
967+
}
968+
903969
@Test
904970
public void testTypeChecked9460() {
905971
//@formatter:off

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

+43-4
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@
2020
package org.codehaus.groovy.transform.stc;
2121

2222
import org.codehaus.groovy.GroovyBugError;
23-
import org.codehaus.groovy.ast.ClassHelper;
2423
import org.codehaus.groovy.ast.ClassNode;
2524
import org.codehaus.groovy.ast.GenericsType;
25+
import org.codehaus.groovy.ast.GenericsType.GenericsTypeName;
2626
import org.codehaus.groovy.ast.MethodNode;
2727
import org.codehaus.groovy.ast.Parameter;
2828
import org.codehaus.groovy.ast.Variable;
@@ -114,8 +114,6 @@
114114
import static org.codehaus.groovy.ast.ClassHelper.makeWithoutCaching;
115115
import static org.codehaus.groovy.ast.ClassHelper.short_TYPE;
116116
import static org.codehaus.groovy.ast.ClassHelper.void_WRAPPER_TYPE;
117-
import static org.codehaus.groovy.ast.GenericsType.GenericsTypeName;
118-
import static org.codehaus.groovy.ast.tools.GenericsUtils.getSuperClass;
119117
import static org.codehaus.groovy.runtime.DefaultGroovyMethods.asBoolean;
120118
import static org.codehaus.groovy.syntax.Types.ASSIGN;
121119
import static org.codehaus.groovy.syntax.Types.BITWISE_AND;
@@ -1419,6 +1417,7 @@ protected static GenericsType fullyResolve(GenericsType gt, Map<GenericsTypeName
14191417
}
14201418

14211419
protected static ClassNode fullyResolveType(final ClassNode type, final Map<GenericsTypeName, GenericsType> placeholders) {
1420+
/* GRECLIPSE edit -- GROOVY-9033
14221421
if (type.isUsingGenerics() && !type.isGenericsPlaceHolder()) {
14231422
GenericsType[] gts = type.getGenericsTypes();
14241423
if (gts != null) {
@@ -1445,6 +1444,35 @@ protected static ClassNode fullyResolveType(final ClassNode type, final Map<Gene
14451444
} else if (type.isArray()) {
14461445
return fullyResolveType(type.getComponentType(), placeholders).makeArray();
14471446
}
1447+
*/
1448+
if (type.isArray()) {
1449+
return fullyResolveType(type.getComponentType(), placeholders).makeArray();
1450+
}
1451+
if (type.isUsingGenerics()) {
1452+
if (type.isGenericsPlaceHolder()) {
1453+
GenericsType gt = placeholders.get(new GenericsTypeName(type.getUnresolvedName()));
1454+
if (gt != null) {
1455+
return gt.getType();
1456+
}
1457+
ClassNode cn = type.redirect();
1458+
return cn != type ? cn : OBJECT_TYPE;
1459+
} else {
1460+
GenericsType[] gts = type.getGenericsTypes();
1461+
if (gts != null) {
1462+
gts = Arrays.stream(gts).map(gt -> {
1463+
if (gt.isPlaceholder()) {
1464+
GenericsTypeName gtn = new GenericsTypeName(gt.getName());
1465+
return placeholders.getOrDefault(gtn, extractType(gt).asGenericsType());
1466+
}
1467+
return fullyResolve(gt, placeholders);
1468+
}).toArray(GenericsType[]::new);
1469+
}
1470+
ClassNode cn = type.getPlainNodeReference();
1471+
cn.setGenericsTypes(gts);
1472+
return cn;
1473+
}
1474+
}
1475+
// GRECLIPSE end
14481476
return type;
14491477
}
14501478

@@ -1876,7 +1904,7 @@ static void extractGenericsConnections(Map<GenericsTypeName, GenericsType> conne
18761904
}
18771905
} else {
18781906
// have first to find matching super class or interface
1879-
ClassNode superClass = getSuperClass(type, target);
1907+
ClassNode superClass = GenericsUtils.getSuperClass(type, target);
18801908

18811909
if (superClass != null) {
18821910
/* GRECLIPSE edit -- GROOVY-9735
@@ -1900,7 +1928,11 @@ static void extractGenericsConnections(Map<GenericsTypeName, GenericsType> conne
19001928

19011929
public static ClassNode getCorrectedClassNode(ClassNode type, ClassNode superClass, boolean handlingGenerics) {
19021930
ClassNode corrected;
1931+
/* GRECLIPSE edit -- GROOVY-9033
19031932
if (handlingGenerics && missesGenericsTypes(type)) {
1933+
*/
1934+
if (handlingGenerics && GenericsUtils.hasUnresolvedGenerics(type)) {
1935+
// GRECLIPSE end
19041936
corrected = superClass.getPlainNodeReference();
19051937
} else {
19061938
corrected = GenericsUtils.correctToGenericsSpecRecurse(GenericsUtils.createGenericsSpec(type), superClass);
@@ -2363,6 +2395,7 @@ public static boolean isParameterizedWithString(ClassNode node) {
23632395
}
23642396

23652397
public static boolean missesGenericsTypes(ClassNode cn) {
2398+
/* GRECLIPSE edit -- GROOVY-9033
23662399
if (cn.isArray()) return missesGenericsTypes(cn.getComponentType());
23672400
GenericsType[] cnTypes = cn.getGenericsTypes();
23682401
GenericsType[] rnTypes = cn.redirect().getGenericsTypes();
@@ -2373,6 +2406,12 @@ public static boolean missesGenericsTypes(ClassNode cn) {
23732406
}
23742407
}
23752408
return false;
2409+
*/
2410+
while (cn.isArray()) cn = cn.getComponentType();
2411+
GenericsType[] cnGenerics = cn.getGenericsTypes();
2412+
GenericsType[] rnGenerics = cn.redirect().getGenericsTypes();
2413+
return cnGenerics == null ? rnGenerics != null : GenericsUtils.hasUnresolvedGenerics(cn);
2414+
// GRECLIPSE end
23762415
}
23772416

23782417
/**

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

+15
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,7 @@ else if (op == LOGICAL_OR) {
935935
}
936936
}
937937

938+
/* GRECLIPSE edit -- GROOVY-9033
938939
if (lType.isUsingGenerics() && missesGenericsTypes(resultType) && isAssignment(op)) {
939940
// unchecked assignment
940941
// examples:
@@ -948,6 +949,16 @@ else if (op == LOGICAL_OR) {
948949
949950
resultType = completedType;
950951
}
952+
*/
953+
if (isAssignment(op)) {
954+
if (lType.isUsingGenerics() && missesGenericsTypes(resultType)) {
955+
resultType = GenericsUtils.parameterizeType(lType, resultType.getPlainNodeReference());
956+
} else if (lType.equals(OBJECT_TYPE) && GenericsUtils.hasUnresolvedGenerics(resultType)) { // def list = []
957+
Map<GenericsTypeName, GenericsType> placeholders = extractGenericsParameterMapOfThis(typeCheckingContext);
958+
resultType = fullyResolveType(resultType, Optional.ofNullable(placeholders).orElseGet(Collections::emptyMap));
959+
}
960+
} else
961+
// GRECLIPSE end
951962

952963
if (isArrayOp(op)
953964
&& !lType.isArray()
@@ -6269,6 +6280,10 @@ private static Map<GenericsTypeName, GenericsType> extractPlaceHolders(MethodNod
62696280
protected boolean typeCheckMethodsWithGenericsOrFail(ClassNode receiver, ClassNode[] arguments, MethodNode candidateMethod, Expression location) {
62706281
if (!typeCheckMethodsWithGenerics(receiver, arguments, candidateMethod)) {
62716282
Map<GenericsTypeName, GenericsType> classGTs = GenericsUtils.extractPlaceholders(receiver);
6283+
// GRECLIPSE add -- GROOVY-9033
6284+
applyGenericsConnections(extractGenericsParameterMapOfThis(typeCheckingContext), classGTs);
6285+
addMethodLevelDeclaredGenerics(candidateMethod, classGTs);
6286+
// GRECLIPSE end
62726287
ClassNode[] ptypes = new ClassNode[candidateMethod.getParameters().length];
62736288
final Parameter[] parameters = candidateMethod.getParameters();
62746289
for (int i = 0; i < parameters.length; i++) {

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

+42-1
Original file line numberDiff line numberDiff line change
@@ -1350,6 +1350,7 @@ protected static GenericsType fullyResolve(GenericsType gt, final Map<GenericsTy
13501350
}
13511351

13521352
protected static ClassNode fullyResolveType(final ClassNode type, final Map<GenericsTypeName, GenericsType> placeholders) {
1353+
/* GRECLIPSE edit -- GROOVY-9033
13531354
if (type.isUsingGenerics() && !type.isGenericsPlaceHolder()) {
13541355
GenericsType[] gts = type.getGenericsTypes();
13551356
if (gts != null) {
@@ -1376,6 +1377,35 @@ protected static ClassNode fullyResolveType(final ClassNode type, final Map<Gene
13761377
} else if (type.isArray()) {
13771378
return fullyResolveType(type.getComponentType(), placeholders).makeArray();
13781379
}
1380+
*/
1381+
if (type.isArray()) {
1382+
return fullyResolveType(type.getComponentType(), placeholders).makeArray();
1383+
}
1384+
if (type.isUsingGenerics()) {
1385+
if (type.isGenericsPlaceHolder()) {
1386+
GenericsType gt = placeholders.get(new GenericsTypeName(type.getUnresolvedName()));
1387+
if (gt != null) {
1388+
return gt.getType();
1389+
}
1390+
ClassNode cn = type.redirect();
1391+
return cn != type ? cn : OBJECT_TYPE;
1392+
} else {
1393+
GenericsType[] gts = type.getGenericsTypes();
1394+
if (gts != null) {
1395+
gts = Arrays.stream(gts).map(gt -> {
1396+
if (gt.isPlaceholder()) {
1397+
GenericsTypeName gtn = new GenericsTypeName(gt.getName());
1398+
return placeholders.getOrDefault(gtn, extractType(gt).asGenericsType());
1399+
}
1400+
return fullyResolve(gt, placeholders);
1401+
}).toArray(GenericsType[]::new);
1402+
}
1403+
ClassNode cn = type.getPlainNodeReference();
1404+
cn.setGenericsTypes(gts);
1405+
return cn;
1406+
}
1407+
}
1408+
// GRECLIPSE end
13791409
return type;
13801410
}
13811411

@@ -1818,7 +1848,11 @@ static void extractGenericsConnections(final Map<GenericsTypeName, GenericsType>
18181848
}
18191849

18201850
public static ClassNode getCorrectedClassNode(final ClassNode type, final ClassNode superClass, final boolean handlingGenerics) {
1851+
/* GRECLIPSE edit -- GROOVY-9033
18211852
if (handlingGenerics && missesGenericsTypes(type)) return superClass.getPlainNodeReference();
1853+
*/
1854+
if (handlingGenerics && GenericsUtils.hasUnresolvedGenerics(type)) return superClass.getPlainNodeReference();
1855+
// GRECLIPSE end
18221856
return GenericsUtils.correctToGenericsSpecRecurse(GenericsUtils.createGenericsSpec(type), superClass);
18231857
}
18241858

@@ -2222,7 +2256,8 @@ public static boolean isParameterizedWithString(final ClassNode node) {
22222256
return node.getSuperClass() != null && isParameterizedWithString(node.getUnresolvedSuperClass());
22232257
}
22242258

2225-
public static boolean missesGenericsTypes(final ClassNode cn) {
2259+
public static boolean missesGenericsTypes(ClassNode cn) {
2260+
/* GRECLIPSE edit -- GROOVY-9033
22262261
if (cn.isArray()) return missesGenericsTypes(cn.getComponentType());
22272262
GenericsType[] cnTypes = cn.getGenericsTypes();
22282263
GenericsType[] rnTypes = cn.redirect().getGenericsTypes();
@@ -2242,6 +2277,12 @@ public static boolean missesGenericsTypes(final ClassNode cn) {
22422277
}
22432278
}
22442279
return false;
2280+
*/
2281+
while (cn.isArray()) cn = cn.getComponentType();
2282+
GenericsType[] cnGenerics = cn.getGenericsTypes();
2283+
GenericsType[] rnGenerics = cn.redirect().getGenericsTypes();
2284+
return cnGenerics == null ? rnGenerics != null : GenericsUtils.hasUnresolvedGenerics(cn);
2285+
// GRECLIPSE end
22452286
}
22462287

22472288
/**

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

+15
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,7 @@ else if (op == LOGICAL_OR) {
846846
}
847847

848848
boolean isAssignment = isAssignment(expression.getOperation().getType());
849+
/* GRECLIPSE edit -- GROOVY-9033
849850
if (isAssignment && lType.isUsingGenerics() && missesGenericsTypes(resultType)) {
850851
// unchecked assignment
851852
// examples:
@@ -859,6 +860,16 @@ else if (op == LOGICAL_OR) {
859860
860861
resultType = completedType;
861862
}
863+
*/
864+
if (isAssignment) {
865+
if (lType.isUsingGenerics() && missesGenericsTypes(resultType)) {
866+
resultType = GenericsUtils.parameterizeType(lType, resultType.getPlainNodeReference());
867+
} else if (lType.equals(OBJECT_TYPE) && GenericsUtils.hasUnresolvedGenerics(resultType)) { // def list = []
868+
Map<GenericsTypeName, GenericsType> placeholders = extractGenericsParameterMapOfThis(typeCheckingContext);
869+
resultType = fullyResolveType(resultType, Optional.ofNullable(placeholders).orElseGet(Collections::emptyMap));
870+
}
871+
} else
872+
// GRECLIPSE end
862873

863874
if (isArrayOp(op)
864875
&& !lType.isArray()
@@ -6035,6 +6046,10 @@ private static Map<GenericsTypeName, GenericsType> extractPlaceHolders(final Met
60356046
protected boolean typeCheckMethodsWithGenericsOrFail(final ClassNode receiver, final ClassNode[] arguments, final MethodNode candidateMethod, final Expression location) {
60366047
if (!typeCheckMethodsWithGenerics(receiver, arguments, candidateMethod)) {
60376048
Map<GenericsTypeName, GenericsType> classGTs = GenericsUtils.extractPlaceholders(receiver);
6049+
// GRECLIPSE add -- GROOVY-9033
6050+
applyGenericsConnections(extractGenericsParameterMapOfThis(typeCheckingContext), classGTs);
6051+
addMethodLevelDeclaredGenerics(candidateMethod, classGTs);
6052+
// GRECLIPSE end
60386053
Parameter[] parameters = candidateMethod.getParameters();
60396054
ClassNode[] paramTypes = new ClassNode[parameters.length];
60406055
for (int i = 0, n = parameters.length; i < n; i += 1) {

0 commit comments

Comments
 (0)