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();