Skip to content

Commit 51ddcac

Browse files
committed
Fix for #1189: check super class interfaces for Map
1 parent e982ca9 commit 51ddcac

File tree

4 files changed

+71
-5
lines changed

4 files changed

+71
-5
lines changed

base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/GenericInferencingTests.java

+49-3
Original file line numberDiff line numberDiff line change
@@ -665,14 +665,60 @@ public void testMapOfMaps() {
665665
}
666666

667667
@Test
668-
public void testTypeExtendsMap() {
668+
public void testTypeExtendsMap1() {
669669
String contents =
670-
"interface Config extends Map<String, Number> {}\n" +
671-
"void meth(Config config) {\n" +
670+
"interface I extends Map<String, Number> {}\n" +
671+
"void m(I config) {\n" +
672672
" def xxx = config.whatever\n" +
673+
" def yyy = config['whatever']\n" +
673674
"}\n";
674675

675676
assertType(contents, "xxx", "java.lang.Number");
677+
assertType(contents, "yyy", "java.lang.Number");
678+
}
679+
680+
@Test
681+
public void testTypeExtendsMap2() {
682+
String contents =
683+
"interface I extends Map<String, Number> {}\n" +
684+
"abstract class A implements I {}\n" +
685+
"void m(A config) {\n" +
686+
" def xxx = config.whatever\n" +
687+
" def yyy = config['whatever']\n" +
688+
"}\n";
689+
690+
assertType(contents, "xxx", "java.lang.Number");
691+
assertType(contents, "yyy", "java.lang.Number");
692+
}
693+
694+
@Test // https://github.com/groovy/groovy-eclipse/issues/1189
695+
public void testTypeExtendsMap3() {
696+
String contents =
697+
"interface I extends Map<String, Number> {}\n" +
698+
"abstract class A implements I {}\n" +
699+
"class C extends A {}\n" +
700+
"void m(C config) {\n" +
701+
" def xxx = config.whatever\n" +
702+
" def yyy = config['whatever']\n" +
703+
"}\n";
704+
705+
assertType(contents, "xxx", "java.lang.Number");
706+
assertType(contents, "yyy", "java.lang.Number");
707+
}
708+
709+
@Test // https://github.com/groovy/groovy-eclipse/issues/1189
710+
public void testTypeExtendsMap4() {
711+
String contents =
712+
"interface I<T> extends Map<String, T> {}\n" +
713+
"abstract class A<U> implements I<U> {}\n" +
714+
"class C extends A<Number> {}\n" +
715+
"void m(C config) {\n" +
716+
" def xxx = config.whatever\n" +
717+
" def yyy = config['whatever']\n" +
718+
"}\n";
719+
720+
assertType(contents, "xxx", "java.lang.Number");
721+
assertType(contents, "yyy", "java.lang.Number");
676722
}
677723

678724
@Test

base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/core/util/GroovyUtils.java

+20
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.Collections;
2121
import java.util.Comparator;
2222
import java.util.HashSet;
23+
import java.util.LinkedHashSet;
2324
import java.util.LinkedList;
2425
import java.util.List;
2526
import java.util.Map;
@@ -50,6 +51,7 @@
5051
import org.codehaus.groovy.ast.expr.TernaryExpression;
5152
import org.codehaus.groovy.ast.expr.VariableExpression;
5253
import org.codehaus.groovy.ast.tools.GeneralUtils;
54+
import org.codehaus.groovy.ast.tools.GenericsUtils;
5355
import org.codehaus.groovy.control.CompilePhase;
5456
import org.codehaus.groovy.runtime.MetaClassHelper;
5557
import org.codehaus.groovy.transform.ASTTransformation;
@@ -131,6 +133,24 @@ public static String[] splitName(ClassNode node) {
131133
return new String[] {name.substring(0, Math.max(0, index)), name.substring(index + 1)};
132134
}
133135

136+
public static Set<ClassNode> getAllInterfaces(ClassNode node) {
137+
Set<ClassNode> result = new LinkedHashSet<>();
138+
if (node.isInterface()) result.add(node);
139+
addAllInterfaces(result, node);
140+
return result;
141+
}
142+
143+
private static void addAllInterfaces(Set<ClassNode> result, ClassNode source) {
144+
for (ClassNode face : source.getInterfaces()) {
145+
face = GenericsUtils.parameterizeType(source, face);
146+
if (result.add(face)) addAllInterfaces(result, face);
147+
}
148+
ClassNode sc = source.redirect().getUnresolvedSuperClass(false);
149+
if (sc != null && !sc.equals(ClassHelper.OBJECT_TYPE)) {
150+
addAllInterfaces(result, GenericsUtils.parameterizeType(source, sc));
151+
}
152+
}
153+
134154
public static Stream<AnnotationNode> getAnnotations(AnnotatedNode node, String name) {
135155
return node.getAnnotations().stream().filter(an -> an.getClassNode().getName().equals(name));
136156
}

base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/CategoryTypeLookup.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public TypeLookupResult lookupType(final Expression node, final VariableScope sc
9292
ClassNode resolvedType = method.getReturnType();
9393
// getAt(Object,String):Object supersedes getAt(Map<K,V>,Object):V when first param is String or GString; restore return type V for user experience
9494
if ("getAt".equals(simpleName) && VariableScope.OBJECT_CLASS_NODE.equals(resolvedType) && isOrImplements(selfType, VariableScope.MAP_CLASS_NODE)) {
95-
for (ClassNode face : selfType.getAllInterfaces()) {
95+
for (ClassNode face : GroovyUtils.getAllInterfaces(selfType)) {
9696
if (face.equals(VariableScope.MAP_CLASS_NODE)) { // Map<K,V>
9797
GenericsType[] generics = GroovyUtils.getGenericsTypes(face);
9898
if (generics.length == 2) resolvedType = generics[1].getType();

base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/SimpleTypeLookup.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,7 @@ protected ASTNode findDeclaration(final String name, final ClassNode declaringTy
633633
resolvedType = VariableScope.VOID_CLASS_NODE;
634634
} else {
635635
resolvedType = VariableScope.OBJECT_CLASS_NODE;
636-
for (ClassNode face : declaringType.getAllInterfaces()) {
636+
for (ClassNode face : GroovyUtils.getAllInterfaces(declaringType)) {
637637
if (face.equals(VariableScope.MAP_CLASS_NODE)) { // Map<K,V>
638638
GenericsType[] generics = GroovyUtils.getGenericsTypes(face);
639639
if (generics.length == 2) resolvedType = generics[1].getType();

0 commit comments

Comments
 (0)