Skip to content

Commit

Permalink
Fix for #1266: gather generics from placeholder references
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-milles committed Jun 10, 2021
1 parent 73f60f5 commit 70a96c4
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1127,18 +1127,16 @@ public void testGetClass3() {
@Test // GRECLIPSE-1696: Generic method type inference with @CompileStatic
public void testMethod1() {
String contents =
"import groovy.transform.CompileStatic\n" +
"class A {\n" +
" public <T> T myMethod(Class<T> claz) {\n" +
" return null\n" +
" }\n" +
" @CompileStatic\n" +
" static void main(String[] args) {\n" +
" A a = new A()\n" +
" def val = a.myMethod(String.class)\n" +
" val.trim()\n" +
" }\n" +
"}";
" def <T> T myMethod(Class<T> claz) {\n" +
" }\n" +
" @groovy.transform.CompileStatic\n" +
" static main(args) {\n" +
" A a = new A()\n" +
" def val = a.myMethod(String.class)\n" +
" val.trim()\n" +
" }\n" +
"}\n";
int start = contents.lastIndexOf("val");
int end = start + "val".length();
assertType(contents, start, end, "java.lang.String");
Expand All @@ -1148,15 +1146,14 @@ public void testMethod1() {
public void testMethod2() {
String contents =
"class A {\n" +
" public <T> T myMethod(Class<T> claz) {\n" +
" return null\n" +
" }\n" +
" static void main(String[] args) {\n" +
" A a = new A()\n" +
" def val = a.myMethod(String.class)\n" +
" val.trim()\n" +
" }\n" +
"}";
" def <T> T myMethod(Class<T> claz) {\n" +
" }\n" +
" static main(args) {\n" +
" A a = new A()\n" +
" def val = a.myMethod(String.class)\n" +
" val.trim()\n" +
" }\n" +
"}\n";
int start = contents.lastIndexOf("val");
int end = start + "val".length();
assertType(contents, start, end, "java.lang.String");
Expand All @@ -1165,17 +1162,15 @@ public void testMethod2() {
@Test // Generic method without object type inference with @CompileStatic
public void testMethod3() {
String contents =
"import groovy.transform.CompileStatic\n" +
"class A {\n" +
" public <T> T myMethod(Class<T> claz) {\n" +
" return null\n" +
" }\n" +
" @CompileStatic\n" +
" def m() {\n" +
" def val = myMethod(String.class)\n" +
" val.trim()\n" +
" }\n" +
"}";
" def <T> T myMethod(Class<T> claz) {\n" +
" }\n" +
" @groovy.transform.CompileStatic\n" +
" def m() {\n" +
" def val = myMethod(String.class)\n" +
" val.trim()\n" +
" }\n" +
"}\n";
int start = contents.lastIndexOf("val");
int end = start + "val".length();
assertType(contents, start, end, "java.lang.String");
Expand All @@ -1185,19 +1180,48 @@ public void testMethod3() {
public void testMethod4() {
String contents =
"class A {\n" +
" public <T> T myMethod(Class<T> claz) {\n" +
" return null\n" +
" }\n" +
" def m() {\n" +
" def val = myMethod(String.class)\n" +
" val.trim()\n" +
" }\n" +
"}";
" def <T> T myMethod(Class<T> claz) {\n" +
" }\n" +
" def m() {\n" +
" def val = myMethod(String.class)\n" +
" val.trim()\n" +
" }\n" +
"}\n";
int start = contents.lastIndexOf("val");
int end = start + "val".length();
assertType(contents, start, end, "java.lang.String");
}

@Test // https://github.com/groovy/groovy-eclipse/issues/1266
public void testMethod5() {
String contents =
"class C {\n" +
" private <N extends Number> N m(Class<N> t) {\n" +
" t.newInstance()\n" +
" }\n" +
" void test() {\n" +
" def n = m()\n" +
" }\n" +
"}\n";
assertType(contents, "m", "java.lang.Number");
assertType(contents, "n", "java.lang.Number");
}

@Test
public void testMethod6() {
String contents =
"class C {\n" +
" private <N extends Number> N m(Class<N> t) {\n" +
" t.newInstance()\n" +
" }\n" +
" void test() {\n" +
" def n = m(Byte)\n" +
" }\n" +
"}\n";
assertType(contents, "m", "java.lang.Byte");
assertType(contents, "n", "java.lang.Byte");
}

@Test // GRECLIPSE-1129: Static generic method type inference with @CompileStatic
public void testStaticMethod1() {
String contents =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,17 @@ public static GenericsMapper gatherGenerics(final ClassNode resolvedType) {
* @param declaringType a type that is somewhere in {@code resolvedType}'s hierarchy used to find the target of the mapping
*/
public static GenericsMapper gatherGenerics(final ClassNode resolvedType, final ClassNode declaringType) {
if (resolvedType.isArray() && declaringType.isArray()) {
return gatherGenerics(resolvedType.getComponentType(), declaringType.getComponentType());
}
if (declaringType.isGenericsPlaceHolder()) {
Map<String, ClassNode> resolved = new java.util.IdentityHashMap<>();
resolved.put(declaringType.getUnresolvedName(), resolvedType);
GenericsMapper mapper = new GenericsMapper();
mapper.allGenerics.add(resolved);
return mapper;
}

GenericsMapper mapper = new GenericsMapper();

ClassNode rCandidate = resolvedType;
Expand Down Expand Up @@ -261,6 +272,7 @@ protected GenericsMapper fillPlaceholders(final GenericsType[] typeParameters) {
if (type.getUnresolvedName().equals(tp.getName())) {
// replace placeholder "T" with type "Number" or "Object" or whatever
type = type.hasMultiRedirect() ? type.asGenericsType().getUpperBounds()[0] : type.redirect();
if (type.isGenericsPlaceHolder()) type = type.getSuperClass();
name2Type.setValue(type);
break;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2009-2020 the original author or authors.
* Copyright 2009-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -624,17 +624,19 @@ void bubbleUpdates(final VariableScope defaultBranch, final VariableScope... con

public static ClassNode resolveTypeParameterization(GenericsMapper mapper, ClassNode type) {
if (mapper.hasGenerics()) {
GenericsType[] parameterizedTypes = GroovyUtils.getGenericsTypes(type);
if (parameterizedTypes.length > 0) {
for (int i = 0, n = parameterizedTypes.length; i < n; i += 1) {
GenericsType parameterizedType = parameterizedTypes[i];
ClassNode maybe = resolveTypeParameterization(mapper, parameterizedType, type);
GenericsType[] genericsTypes = GroovyUtils.getGenericsTypes(type);
int n = genericsTypes.length;
if (n > 0) {
for (int i = 0; i < n; i += 1) {
ClassNode maybe = resolveTypeParameterization(mapper, genericsTypes[i], type);
if (maybe != type) {
assert n == 1;
type = maybe;
break;
}
}
} else if (type.isGenericsPlaceHolder()) {
type = mapper.findParameter(type.getUnresolvedName(), type.redirect());
}
}
return type;
Expand Down

0 comments on commit 70a96c4

Please sign in to comment.