TypeOracle.getParameterizedType would erroneously throw an IllegalArgumentException for a nested generic type if if its parameterized form was requested before the nested generic type was fully resolved.
Found by: ispeters
Review by: spoon
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2119 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java
index a4a53c3..8105085 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java
@@ -339,8 +339,16 @@
if (genericType.getEnclosingType().isGenericType() != null
&& enclosingType.isParameterized() == null
&& enclosingType.isRawType() == null) {
- throw new IllegalArgumentException(
- "enclosingType needs to be a parameterized type or a raw type");
+ /*
+ * If the generic type is a non-static member type enclosed by a generic
+ * type then the enclosing type for this parameterized type should be
+ * raw or parameterized.
+ */
+ throw new IllegalArgumentException("Generic type '"
+ + genericType.getParameterizedQualifiedSourceName()
+ + "' is a non-static member type, but the enclosing type '"
+ + enclosingType.getQualifiedSourceName()
+ + "' is not a parameterized or raw type");
}
}
diff --git a/dev/core/src/com/google/gwt/dev/jdt/TypeOracleBuilder.java b/dev/core/src/com/google/gwt/dev/jdt/TypeOracleBuilder.java
index cfc615c..77592a6 100644
--- a/dev/core/src/com/google/gwt/dev/jdt/TypeOracleBuilder.java
+++ b/dev/core/src/com/google/gwt/dev/jdt/TypeOracleBuilder.java
@@ -101,6 +101,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.TreeMap;
import java.util.regex.Pattern;
/**
@@ -526,7 +527,7 @@
// Build a list that makes it easy to remove problems.
//
- final Map<String, CompilationUnitDeclaration> cudsByFileName = new HashMap<String, CompilationUnitDeclaration>();
+ final Map<String, CompilationUnitDeclaration> cudsByFileName = new TreeMap<String, CompilationUnitDeclaration>();
for (int i = 0; i < cuds.length; i++) {
CompilationUnitDeclaration cud = cuds[i];
char[] location = cud.getFileName();
@@ -1042,6 +1043,12 @@
jclassIsIntf);
}
+ /*
+ * Add modifiers since these are needed for
+ * TypeOracle.getParameterizedType's error checking code.
+ */
+ jrealClassType.addModifierBits(Shared.bindingToModifierBits(binding));
+
cacheManager.setTypeForBinding(binding, jrealClassType);
}
@@ -1598,9 +1605,10 @@
return false;
}
- // Add modifiers.
- //
- jtype.addModifierBits(Shared.bindingToModifierBits(clazz.binding));
+ /*
+ * Modifiers were added during processType since getParameterizedType
+ * depends on them being set.
+ */
// Try to resolve annotations, ignore any that fail.
Map<Class<? extends java.lang.annotation.Annotation>, java.lang.annotation.Annotation> declaredAnnotations = newAnnotationMap();
diff --git a/dev/core/test/com/google/gwt/dev/typeinfo/test/TypeOracleBuilderTest.java b/dev/core/test/com/google/gwt/dev/typeinfo/test/TypeOracleBuilderTest.java
index 65d709e..608c74d 100644
--- a/dev/core/test/com/google/gwt/dev/typeinfo/test/TypeOracleBuilderTest.java
+++ b/dev/core/test/com/google/gwt/dev/typeinfo/test/TypeOracleBuilderTest.java
@@ -44,13 +44,13 @@
private final String[] typeNames;
/**
- * Creates a new {@code TestCup} with several types. The first type in
+ * Creates a new {@code TestCup} with several types. The first type in
* {@code typeNames} is considered to be the main type.
- *
+ *
* @param packageName the package for the types in this {@code TestCup}
- * @param typeNames the types for this {@code TestCup}. Must have
- * at least one type. The first type is considered to be the main type
- * for this {@code TestCup}.
+ * @param typeNames the types for this {@code TestCup}. Must have at least
+ * one type. The first type is considered to be the main type for
+ * this {@code TestCup}.
*/
public TestCup(String packageName, String... typeNames) {
this.packageName = packageName;
@@ -84,6 +84,7 @@
public String[] getTypeNames() {
return typeNames;
}
+
public boolean isTransient() {
return true;
}
@@ -247,6 +248,51 @@
}
};
+ protected TestCup CU_ReferencesParameterizedTypeBeforeItsGenericFormHasBeenProcessed = new TestCup(
+ "parameterized.type.build.dependency", "Class0") {
+ @Override
+ public void check(JClassType type) throws NotFoundException {
+ }
+
+ public char[] getSource() throws UnableToCompleteException {
+ StringBuilder sb = new StringBuilder();
+ sb.append("package parameterized.type.build.dependency;\n");
+ sb.append("public class Class0 implements Class2.Inner<Object> {\n");
+ sb.append("}\n");
+ return sb.toString().toCharArray();
+ }
+ };
+
+ protected TestCup CU_DeclaresInnerGenericType = new TestCup(
+ "parameterized.type.build.dependency", "Class1") {
+ @Override
+ public void check(JClassType type) throws NotFoundException {
+ }
+
+ public char[] getSource() throws UnableToCompleteException {
+ StringBuilder sb = new StringBuilder();
+ sb.append("package parameterized.type.build.dependency;\n");
+ sb.append("public class Class1<T> {\n");
+ sb.append(" public interface Inner<T> {}\n");
+ sb.append("}\n");
+ return sb.toString().toCharArray();
+ }
+ };
+
+ protected TestCup CU_ExtendsParameterizedType = new TestCup(
+ "parameterized.type.build.dependency", "Class2") {
+ @Override
+ public void check(JClassType type) throws NotFoundException {
+ }
+
+ public char[] getSource() throws UnableToCompleteException {
+ StringBuilder sb = new StringBuilder();
+ sb.append("package parameterized.type.build.dependency;\n");
+ sb.append("public class Class2 extends Class1<Object> {}\n");
+ return sb.toString().toCharArray();
+ }
+ };
+
protected TestCup CU_FieldsAndTypes = new TestCup("test", "Fields",
"SomeType") {
public void check(JClassType type) throws NotFoundException {
@@ -557,8 +603,7 @@
}
};
- protected TestCup CU_OuterInner = new TestCup("test", "Outer",
- "Outer.Inner") {
+ protected TestCup CU_OuterInner = new TestCup("test", "Outer", "Outer.Inner") {
public void check(JClassType type) {
final String name = type.getSimpleSourceName();
@@ -728,6 +773,27 @@
checkTypes(types);
}
+ /**
+ * Tests that we can build nested parameterized types even if that happens
+ * while the type oracle is being built. This test assumes that
+ * CU_ReferencesParameterizedTypeBeforeItsGenericFormHasBeenProcessed will
+ * cause a parameterized form of CU_DeclaresInnerGenericType to be created
+ * before the type oracle has had a chance to resolve
+ * CU_DeclaresInnerGenericType.
+ */
+ public void testParameterizedTypeBuildDependencies()
+ throws UnableToCompleteException, NotFoundException {
+ TypeOracleBuilder tiob = createTypeInfoOracleBuilder();
+
+ tiob.addCompilationUnit(CU_ReferencesParameterizedTypeBeforeItsGenericFormHasBeenProcessed);
+ tiob.addCompilationUnit(CU_ExtendsParameterizedType);
+ tiob.addCompilationUnit(CU_DeclaresInnerGenericType);
+ tiob.addCompilationUnit(CU_Object);
+
+ TypeOracle tio = tiob.build(createTreeLogger());
+ assertNull(tio.findType("test.parameterizedtype.build.dependencies.Class2"));
+ }
+
public void testSyntaxErrors() throws TypeOracleException,
UnableToCompleteException {
TypeOracleBuilder tiob = createTypeInfoOracleBuilder();