Fixes problems introduced by the original fix to issue 1830 committed at r1944. It modified AbstractCompiler to satisfy INameEnvironment.findType requests with a .class file if we could not locate source code for the requested type. However, this change had several bad side effects if you referenced a binary type outside the context of an annotation or if you forgot to add a inherits to your GWT module XML file:
* TypeOracleBuilder would fail to fully resolve types which could result in NPEs in generators.
* GWTCompiler would generate ICEs if it ever encountered a binary type as part of its compilation.
This change adds a visitor to AbstractCompiler.CompilerImpl.process(CompilationUnitDeclaration, int) which scans the compilation unit for binary type refrences outside the context of an annotation. If it finds any, it will record the problem against the compilation unit as a normal JDT error.
Review by: scottb
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2191 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jdt/AbstractCompiler.java b/dev/core/src/com/google/gwt/dev/jdt/AbstractCompiler.java
index 2ff4800..defb5fe 100644
--- a/dev/core/src/com/google/gwt/dev/jdt/AbstractCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jdt/AbstractCompiler.java
@@ -179,6 +179,8 @@
JSORestrictionsChecker.check(cud);
+ BinaryTypeReferenceRestrictionsChecker.check(cud);
+
// Optionally remember this cud.
//
if (cuds != null) {
diff --git a/dev/core/src/com/google/gwt/dev/jdt/BinaryTypeReferenceRestrictionsChecker.java b/dev/core/src/com/google/gwt/dev/jdt/BinaryTypeReferenceRestrictionsChecker.java
new file mode 100644
index 0000000..d9b909f
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jdt/BinaryTypeReferenceRestrictionsChecker.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.jdt;
+
+import org.eclipse.jdt.core.compiler.IProblem;
+import org.eclipse.jdt.internal.compiler.CompilationResult;
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
+import org.eclipse.jdt.internal.compiler.ast.Annotation;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
+import org.eclipse.jdt.internal.compiler.problem.DefaultProblem;
+import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
+import org.eclipse.jdt.internal.compiler.util.Util;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Check a {@link CompilationUnitDeclaration} for references to binary types
+ * outside the context of an annotation.
+ */
+class BinaryTypeReferenceRestrictionsChecker {
+ /**
+ * Records the location from which a {@link BinaryTypeBinding} is referenced.
+ */
+ static class BinaryTypeReferenceSite {
+ private final Expression expression;
+ private final BinaryTypeBinding binaryTypeBinding;
+
+ BinaryTypeReferenceSite(Expression expression,
+ BinaryTypeBinding binaryTypeBinding) {
+ this.expression = expression;
+ this.binaryTypeBinding = binaryTypeBinding;
+ }
+
+ public BinaryTypeBinding getBinaryTypeBinding() {
+ return binaryTypeBinding;
+ }
+
+ public Expression getExpression() {
+ return expression;
+ }
+ }
+
+ /**
+ * Visits a {@link CompilationUnitDeclaration} and records all expressions
+ * which use a {@link BinaryTypeBinding}.
+ */
+ static class BinaryTypeReferenceVisitor extends TypeRefVisitor {
+ private final List<BinaryTypeReferenceSite> binaryTypeReferenceSites;
+
+ public BinaryTypeReferenceVisitor(
+ List<BinaryTypeReferenceSite> binaryTypeReferenceSites) {
+ this.binaryTypeReferenceSites = binaryTypeReferenceSites;
+ }
+
+ @Override
+ protected void onBinaryTypeRef(BinaryTypeBinding binding,
+ CompilationUnitDeclaration unitOfReferrer, Expression expression) {
+ if (!isValidBinaryTypeUsage(expression)) {
+ binaryTypeReferenceSites.add(new BinaryTypeReferenceSite(expression,
+ binding));
+ }
+ }
+
+ @Override
+ protected void onTypeRef(SourceTypeBinding referencedType,
+ CompilationUnitDeclaration unitOfReferrer) {
+ // do nothing
+ }
+ }
+
+ /**
+ * Scans a {@link CompilationUnitDeclaration} for expressions that use
+ * {@link BinaryTypeBinding}s outside the context of an annotation. For
+ * each unique use of a given {@link BinaryTypeBinding}, a error is reported
+ * against the {@link CompilationUnitDeclaration}.
+ */
+ public static void check(CompilationUnitDeclaration cud) {
+ List<BinaryTypeReferenceSite> binaryTypeReferenceSites = findInvalidBinaryTypeReferenceSites(cud);
+ Set<BinaryTypeBinding> invalidBindaryTypeBindings = new HashSet<BinaryTypeBinding>();
+
+ for (BinaryTypeReferenceSite binaryTypeReferenceSite : binaryTypeReferenceSites) {
+ BinaryTypeBinding binaryTypeBinding = binaryTypeReferenceSite.getBinaryTypeBinding();
+ if (invalidBindaryTypeBindings.contains(binaryTypeBinding)) {
+ continue;
+ }
+ invalidBindaryTypeBindings.add(binaryTypeBinding);
+
+ String qualifiedTypeName = binaryTypeBinding.debugName();
+ String error = formatBinaryTypeRefErrorMessage(qualifiedTypeName);
+
+ recordError(cud, binaryTypeReferenceSite.getExpression(), error);
+ }
+ }
+
+ static List<BinaryTypeReferenceSite> findInvalidBinaryTypeReferenceSites(
+ CompilationUnitDeclaration cud) {
+ List<BinaryTypeReferenceSite> binaryTypeReferenceSites = new ArrayList<BinaryTypeReferenceSite>();
+ BinaryTypeReferenceVisitor binaryTypeReferenceVisitor = new BinaryTypeReferenceVisitor(
+ binaryTypeReferenceSites);
+ cud.traverse(binaryTypeReferenceVisitor, cud.scope);
+ return binaryTypeReferenceSites;
+ }
+
+ static String formatBinaryTypeRefErrorMessage(String qualifiedTypeName) {
+ return "No source code is available for type " + qualifiedTypeName
+ + "; did you forget to inherit a required module?";
+ }
+
+ /**
+ * Returns <code>true</code> if a {@link BinaryTypeBinding} can be used from
+ * this particular {@link ASTNode} type.
+ */
+ static boolean isValidBinaryTypeUsage(Expression expression) {
+ return expression instanceof Annotation;
+ }
+
+ static void recordError(CompilationUnitDeclaration cud, ASTNode node,
+ String error) {
+ CompilationResult compResult = cud.compilationResult();
+ int[] lineEnds = compResult.getLineSeparatorPositions();
+ int startLine = Util.getLineNumber(node.sourceStart(), lineEnds, 0,
+ lineEnds.length - 1);
+ int startColumn = Util.searchColumnNumber(lineEnds, startLine,
+ node.sourceStart());
+ DefaultProblem problem = new DefaultProblem(compResult.fileName, error,
+ IProblem.ExternalProblemNotFixable, null, ProblemSeverities.Error,
+ node.sourceStart(), node.sourceEnd(), startLine, startColumn);
+ compResult.record(problem, cud);
+ }
+
+ private BinaryTypeReferenceRestrictionsChecker() {
+ // Not instantiable
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/jdt/TypeRefVisitor.java b/dev/core/src/com/google/gwt/dev/jdt/TypeRefVisitor.java
index 6ae0829..9924fbf 100644
--- a/dev/core/src/com/google/gwt/dev/jdt/TypeRefVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jdt/TypeRefVisitor.java
@@ -19,6 +19,7 @@
import org.eclipse.jdt.internal.compiler.ast.ArrayQualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference;
@@ -26,6 +27,7 @@
import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.Wildcard;
import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
+import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
@@ -40,79 +42,84 @@
@Override
public void endVisit(ArrayQualifiedTypeReference x, BlockScope scope) {
- maybeDispatch(scope, x.resolvedType);
+ maybeDispatch(scope, x, x.resolvedType);
}
@Override
public void endVisit(ArrayQualifiedTypeReference x, ClassScope scope) {
- maybeDispatch(scope, x.resolvedType);
+ maybeDispatch(scope, x, x.resolvedType);
}
@Override
public void endVisit(ArrayTypeReference x, BlockScope scope) {
- maybeDispatch(scope, x.resolvedType);
+ maybeDispatch(scope, x, x.resolvedType);
}
@Override
public void endVisit(ArrayTypeReference x, ClassScope scope) {
- maybeDispatch(scope, x.resolvedType);
+ maybeDispatch(scope, x, x.resolvedType);
}
@Override
public void endVisit(MessageSend messageSend, BlockScope scope) {
if (messageSend.binding.isStatic()) {
- maybeDispatch(scope, messageSend.actualReceiverType);
+ maybeDispatch(scope, messageSend, messageSend.actualReceiverType);
}
}
@Override
public void endVisit(ParameterizedQualifiedTypeReference x, BlockScope scope) {
- maybeDispatch(scope, x.resolvedType);
+ maybeDispatch(scope, x, x.resolvedType);
}
@Override
public void endVisit(ParameterizedQualifiedTypeReference x, ClassScope scope) {
- maybeDispatch(scope, x.resolvedType);
+ maybeDispatch(scope, x, x.resolvedType);
}
@Override
public void endVisit(ParameterizedSingleTypeReference x, BlockScope scope) {
- maybeDispatch(scope, x.resolvedType);
+ maybeDispatch(scope, x, x.resolvedType);
}
@Override
public void endVisit(ParameterizedSingleTypeReference x, ClassScope scope) {
- maybeDispatch(scope, x.resolvedType);
+ maybeDispatch(scope, x, x.resolvedType);
}
@Override
public void endVisit(QualifiedTypeReference x, BlockScope scope) {
- maybeDispatch(scope, x.resolvedType);
+ maybeDispatch(scope, x, x.resolvedType);
}
@Override
public void endVisit(QualifiedTypeReference x, ClassScope scope) {
- maybeDispatch(scope, x.resolvedType);
+ maybeDispatch(scope, x, x.resolvedType);
}
@Override
public void endVisit(SingleTypeReference x, BlockScope scope) {
- maybeDispatch(scope, x.resolvedType);
+ maybeDispatch(scope, x, x.resolvedType);
}
@Override
public void endVisit(SingleTypeReference x, ClassScope scope) {
- maybeDispatch(scope, x.resolvedType);
+ maybeDispatch(scope, x, x.resolvedType);
}
@Override
public void endVisit(Wildcard x, BlockScope scope) {
- maybeDispatch(scope, x.resolvedType);
+ maybeDispatch(scope, x, x.resolvedType);
}
@Override
public void endVisit(Wildcard x, ClassScope scope) {
- maybeDispatch(scope, x.resolvedType);
+ maybeDispatch(scope, x, x.resolvedType);
+ }
+
+ @SuppressWarnings("unused")
+ protected void onBinaryTypeRef(BinaryTypeBinding referencedType,
+ CompilationUnitDeclaration unitOfReferrer, Expression expression) {
}
protected abstract void onTypeRef(SourceTypeBinding referencedType,
@@ -128,13 +135,19 @@
return (CompilationUnitScope) scope;
}
- private void maybeDispatch(Scope referencedFrom, TypeBinding binding) {
+ private void maybeDispatch(Scope referencedFrom, Expression expression,
+ TypeBinding binding) {
if (binding instanceof SourceTypeBinding) {
SourceTypeBinding type = (SourceTypeBinding) binding;
CompilationUnitScope from = findUnitScope(referencedFrom);
onTypeRef(type, from.referenceContext);
} else if (binding instanceof ArrayBinding) {
- maybeDispatch(referencedFrom, ((ArrayBinding) binding).leafComponentType);
+ maybeDispatch(referencedFrom, expression,
+ ((ArrayBinding) binding).leafComponentType);
+ } else if (binding instanceof BinaryTypeBinding) {
+ CompilationUnitScope from = findUnitScope(referencedFrom);
+ onBinaryTypeRef((BinaryTypeBinding) binding, from.referenceContext,
+ expression);
} else {
// We don't care about other cases.
}
diff --git a/dev/core/test/com/google/gwt/core/ext/typeinfo/BinaryOnlyClass.java b/dev/core/test/com/google/gwt/core/ext/typeinfo/BinaryOnlyClass.java
new file mode 100644
index 0000000..381b7b8
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/typeinfo/BinaryOnlyClass.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.typeinfo;
+
+/**
+ * This class is defined outside of a GWT module, therefore it should
+ * only be available as a binary type.
+ */
+public class BinaryOnlyClass {
+}
diff --git a/dev/core/test/com/google/gwt/dev/jdt/BinaryTypeReferenceRestrictionsCheckerTest.java b/dev/core/test/com/google/gwt/dev/jdt/BinaryTypeReferenceRestrictionsCheckerTest.java
new file mode 100644
index 0000000..999c402
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/jdt/BinaryTypeReferenceRestrictionsCheckerTest.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.jdt;
+
+import com.google.gwt.dev.jdt.BinaryTypeReferenceRestrictionsChecker.BinaryTypeReferenceSite;
+
+import junit.framework.TestCase;
+
+import org.eclipse.jdt.core.compiler.CategorizedProblem;
+import org.eclipse.jdt.internal.compiler.CompilationResult;
+import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Annotation;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.MarkerAnnotation;
+import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.NormalAnnotation;
+import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation;
+import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.eclipse.jdt.internal.compiler.ast.Wildcard;
+import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation;
+import org.eclipse.jdt.internal.compiler.env.IBinaryField;
+import org.eclipse.jdt.internal.compiler.env.IBinaryMethod;
+import org.eclipse.jdt.internal.compiler.env.IBinaryNestedType;
+import org.eclipse.jdt.internal.compiler.env.IBinaryType;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
+import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
+import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
+import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
+import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ *
+ */
+public class BinaryTypeReferenceRestrictionsCheckerTest extends TestCase {
+ /**
+ * Mocks a binary type
+ */
+ class MockBinaryType implements IBinaryType {
+ private final String qualifiedTypeName;
+
+ MockBinaryType(String typeName) {
+ this.qualifiedTypeName = typeName;
+ }
+
+ public IBinaryAnnotation[] getAnnotations() {
+ return null;
+ }
+
+ public char[] getEnclosingTypeName() {
+ return null;
+ }
+
+ public IBinaryField[] getFields() {
+ return null;
+ }
+
+ public char[] getFileName() {
+ return (qualifiedTypeName.replace('.', File.separatorChar) + ".java").toCharArray();
+ }
+
+ public char[] getGenericSignature() {
+ return qualifiedTypeName.toCharArray();
+ }
+
+ public char[][] getInterfaceNames() {
+ return null;
+ }
+
+ public IBinaryNestedType[] getMemberTypes() {
+ return null;
+ }
+
+ public IBinaryMethod[] getMethods() {
+ return null;
+ }
+
+ public int getModifiers() {
+ return 0;
+ }
+
+ public char[] getName() {
+ return qualifiedTypeName.toCharArray();
+ }
+
+ public char[] getSourceName() {
+ return null;
+ }
+
+ public char[] getSuperclassName() {
+ return null;
+ }
+
+ public long getTagBits() {
+ return 0;
+ }
+
+ public boolean isAnonymous() {
+ return false;
+ }
+
+ public boolean isBinaryType() {
+ return true;
+ }
+
+ public boolean isLocal() {
+ return false;
+ }
+
+ public boolean isMember() {
+ return false;
+ }
+
+ public char[] sourceFileName() {
+ return null;
+ }
+ }
+
+ private static final String BINARY_TYPE_NAME = "BinaryType";
+
+ private static TypeReference createMockBinaryTypeReference(
+ BinaryTypeBinding binaryTypeBinding) {
+ SingleTypeReference singleTypeReference = new SingleTypeReference(null, 0);
+ singleTypeReference.resolvedType = binaryTypeBinding;
+ return singleTypeReference;
+ }
+
+ private static LookupEnvironment createMockLookupEnvironment() {
+ LookupEnvironment lookupEnvironment = new LookupEnvironment(null, null,
+ null, null);
+ lookupEnvironment.globalOptions = new CompilerOptions();
+ return lookupEnvironment;
+ }
+
+ public void testCheck() {
+ // fail("Not yet implemented");
+ }
+
+ /**
+ * Creates a mock {@link CompilationUnitDeclaration} that has binary type
+ * references in a superclass reference, in a method return type, in an
+ * annotation and in a local variable declaration. It then checks that the we
+ * find all of these locations except for the one used in an annotation.
+ */
+ public void testFindInvalidBinaryTypeReferenceSites() {
+ CompilationResult compilationResult = new CompilationResult(
+ "TestCompilationUnit.java".toCharArray(), 0, 0, 0);
+ CompilationUnitDeclaration cud = new CompilationUnitDeclaration(null,
+ compilationResult, 1);
+ LookupEnvironment lookupEnvironment = createMockLookupEnvironment();
+ cud.scope = new CompilationUnitScope(cud, lookupEnvironment);
+
+ TypeDeclaration typeDeclaration = new TypeDeclaration(compilationResult);
+ typeDeclaration.scope = new ClassScope(cud.scope, null);
+ cud.types = new TypeDeclaration[] {typeDeclaration};
+
+ BinaryTypeBinding binaryTypeBinding = new BinaryTypeBinding(null,
+ new MockBinaryType(BINARY_TYPE_NAME), lookupEnvironment);
+ typeDeclaration.superclass = createMockBinaryTypeReference(binaryTypeBinding);
+
+ MethodDeclaration methodDeclaration = new MethodDeclaration(
+ compilationResult);
+ methodDeclaration.scope = new MethodScope(typeDeclaration.scope, null,
+ false);
+ methodDeclaration.returnType = createMockBinaryTypeReference(binaryTypeBinding);
+
+ LocalDeclaration localDeclaration = new LocalDeclaration(null, 0, 0);
+ localDeclaration.type = createMockBinaryTypeReference(binaryTypeBinding);
+ methodDeclaration.statements = new Statement[] {localDeclaration};
+
+ Annotation annotation = new MarkerAnnotation(
+ createMockBinaryTypeReference(binaryTypeBinding), 0);
+ typeDeclaration.annotations = new Annotation[] {annotation};
+
+ typeDeclaration.methods = new AbstractMethodDeclaration[] {methodDeclaration};
+
+ /*
+ * Check that we find binary type references in the following expected
+ * locations.
+ */
+ Expression[] expectedExpressions = new Expression[] {
+ typeDeclaration.superclass, methodDeclaration.returnType,
+ localDeclaration.type};
+
+ List<BinaryTypeReferenceSite> binaryTypeReferenceSites = BinaryTypeReferenceRestrictionsChecker.findInvalidBinaryTypeReferenceSites(cud);
+ assertEquals(expectedExpressions.length, binaryTypeReferenceSites.size());
+ for (int i = 0; i < binaryTypeReferenceSites.size(); ++i) {
+ BinaryTypeReferenceSite binaryTypeReferenceSite = binaryTypeReferenceSites.get(i);
+ assertSame(binaryTypeBinding, binaryTypeReferenceSite.getBinaryTypeBinding());
+ assertSame(expectedExpressions[i], binaryTypeReferenceSite.getExpression());
+ }
+ }
+
+ public void testFormatBinaryTypeRefErrorMessage() {
+ String expectedMessage = "No source code is available for type MyClass; did you forget to inherit a required module?";
+ String actualMessage = BinaryTypeReferenceRestrictionsChecker.formatBinaryTypeRefErrorMessage("MyClass");
+ assertEquals(expectedMessage, actualMessage);
+ }
+
+ public void testIsValidBinaryTypeUsage() {
+ TypeReference typeReference = createMockBinaryTypeReference(null);
+
+ assertTrue(BinaryTypeReferenceRestrictionsChecker.isValidBinaryTypeUsage(new MarkerAnnotation(
+ typeReference, 0)));
+ assertTrue(BinaryTypeReferenceRestrictionsChecker.isValidBinaryTypeUsage(new SingleMemberAnnotation(
+ typeReference, 0)));
+ assertTrue(BinaryTypeReferenceRestrictionsChecker.isValidBinaryTypeUsage(new NormalAnnotation(
+ typeReference, 0)));
+ assertFalse(BinaryTypeReferenceRestrictionsChecker.isValidBinaryTypeUsage(new Wildcard(
+ Wildcard.UNBOUND)));
+ }
+
+ public void testRecordError() {
+ String fileName = "TestCompilationUnit.java";
+ String errorMessage = "Unit has errors";
+ CompilationResult compilationResult = new CompilationResult(
+ fileName.toCharArray(), 0, 0, 0);
+ CompilationUnitDeclaration cud = new CompilationUnitDeclaration(null,
+ compilationResult, 0);
+
+ // Pick an Expression subtype to pass in
+ BinaryTypeReferenceRestrictionsChecker.recordError(cud, new Wildcard(
+ Wildcard.EXTENDS), errorMessage);
+
+ CategorizedProblem[] errors = compilationResult.getErrors();
+ assertEquals(1, errors.length);
+ CategorizedProblem problem = errors[0];
+ assertTrue(problem.isError());
+ assertEquals(1, problem.getSourceLineNumber());
+ assertEquals(errorMessage, problem.getMessage());
+ }
+}
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 608c74d..9254f94 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
@@ -17,6 +17,7 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.BinaryOnlyAnnotation;
import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider;
import com.google.gwt.core.ext.typeinfo.JArrayType;
import com.google.gwt.core.ext.typeinfo.JClassType;
@@ -133,6 +134,20 @@
}
};
+ protected TestCup CU_Annotation = new TestCup("java.lang.annotation",
+ "Annotation") {
+ @Override
+ public void check(JClassType type) {
+ }
+
+ public char[] getSource() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("package java.lang.annotation;\n");
+ sb.append("interface Annotation { }\n");
+ return sb.toString().toCharArray();
+ }
+ };
+
protected TestCup CU_Assignable = new TestCup("test.sub", "Derived",
"BaseInterface", "DerivedInterface", "Derived.Nested") {
public void check(JClassType type) {
@@ -228,6 +243,22 @@
}
};
+ 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_DefaultClass = new TestCup("test", "DefaultClass") {
public void check(JClassType type) {
assertEquals("DefaultClass", type.getSimpleSourceName());
@@ -248,37 +279,6 @@
}
};
- 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
@@ -405,6 +405,19 @@
}
};
+ protected TestCup CU_File = new TestCup("java.io", "File") {
+ @Override
+ public void check(JClassType type) {
+ }
+
+ public char[] getSource() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("package java.io;");
+ sb.append("public class File { }\n");
+ return sb.toString().toCharArray();
+ }
+ };
+
protected TestCup CU_HasSyntaxErrors = new TestCup("test", "HasSyntaxErrors",
"NoSyntaxErrors") {
public void check(JClassType classInfo) {
@@ -639,6 +652,94 @@
}
};
+ /**
+ * This compilation unit references a binary only annotation. Note that the
+ * the fact that we do not use java.io.File outside the context of an
+ * annotation does not cause the test to break.
+ */
+ protected TestCup CU_ReferencesBinaryOnlyAnnotation = new TestCup(
+ "references.binary.annotation", "Test") {
+ @Override
+ public void check(JClassType type) {
+ }
+
+ public char[] getSource() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("package references.binary.annotation;");
+ sb.append("import java.io.File;");
+ sb.append("import com.google.gwt.core.ext.typeinfo.BinaryOnlyAnnotation;\n");
+ sb.append("@BinaryOnlyAnnotation(jreClassLiteralReference=File.class)");
+ sb.append("public interface Test { }\n");
+ return sb.toString().toCharArray();
+ }
+ };
+
+ protected TestCup CU_ReferencesBinaryOnlyClassAsMemberType = new TestCup(
+ "references.binary.clazz.member", "Test") {
+ @Override
+ public void check(JClassType type) {
+ }
+
+ public char[] getSource() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("package references.binary.clazz.member;");
+ sb.append("import com.google.gwt.core.ext.typeinfo.BinaryOnlyClass;\n");
+ sb.append("public class Test {\n");
+ sb.append(" BinaryOnlyClass binaryOnlyClass;");
+ sb.append("}\n");
+ return sb.toString().toCharArray();
+ }
+ };
+
+ protected TestCup CU_ReferencesBinaryOnlyClassAsSuperType = new TestCup(
+ "references.binary.clazz.supertype", "Test") {
+ @Override
+ public void check(JClassType type) {
+ }
+
+ public char[] getSource() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("package references.binary.class.supertype;");
+ sb.append("import com.google.gwt.core.ext.typeinfo.BinaryOnlyClass;\n");
+ sb.append("public class Test extends BinaryOnlyClass {}\n");
+ return sb.toString().toCharArray();
+ }
+ };
+
+ protected TestCup CU_ReferencesBinaryOnlyClassInExpression = new TestCup(
+ "references.binary.clazz.expression", "Test") {
+ @Override
+ public void check(JClassType type) {
+ }
+
+ public char[] getSource() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("package references.binary.class.expression;");
+ sb.append("public class Test extends BinaryOnlyClass {\n");
+ sb.append(" public void doStuff(Object) {}\n");
+ sb.append(" public void stuff() {\n");
+ sb.append(" doStuff(new BinaryOnlyClass());\n");
+ sb.append(" }\n");
+ sb.append("}\n");
+ return sb.toString().toCharArray();
+ }
+ };
+
+ 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_RefsInfectedCompilationUnit = new TestCup("test",
"RefsInfectedCompilationUnit") {
public void check(JClassType classInfo) {
@@ -794,6 +895,68 @@
assertNull(tio.findType("test.parameterizedtype.build.dependencies.Class2"));
}
+ /**
+ * Using a binary only annotation should not cause the type to error out.
+ */
+ public void testReferencesBinaryOnlyAnnotation()
+ throws UnableToCompleteException, NotFoundException {
+ TypeOracleBuilder tiob = createTypeInfoOracleBuilder();
+
+ tiob.addCompilationUnit(CU_Annotation);
+ tiob.addCompilationUnit(CU_File);
+ tiob.addCompilationUnit(CU_Object);
+ tiob.addCompilationUnit(CU_ReferencesBinaryOnlyAnnotation);
+
+ TypeOracle tio = tiob.build(createTreeLogger());
+ JClassType type = tio.getType("references.binary.annotation.Test");
+ assertNull(type.getAnnotation(BinaryOnlyAnnotation.class));
+ }
+
+ /*
+ * If a type references a binary only type then it should not end up in the
+ * TypeOracle.
+ */
+ public void testReferencesBinaryOnlyClassAsMemberType()
+ throws UnableToCompleteException {
+ TypeOracleBuilder tiob = createTypeInfoOracleBuilder();
+ tiob.addCompilationUnit(CU_Object);
+ tiob.addCompilationUnit(CU_ReferencesBinaryOnlyClassAsMemberType);
+
+ TypeOracle tio = tiob.build(createTreeLogger());
+ JClassType type = tio.findType("references.binary.clazz.member.Test");
+ assertNull(type);
+ }
+
+ /*
+ * If a type references a binary only type then it should not end up in the
+ * TypeOracle.
+ */
+ public void testReferencesBinaryOnlyTypeAsSuperType()
+ throws UnableToCompleteException {
+ TypeOracleBuilder tiob = createTypeInfoOracleBuilder();
+ tiob.addCompilationUnit(CU_Object);
+ tiob.addCompilationUnit(CU_ReferencesBinaryOnlyClassAsMemberType);
+
+ TypeOracle tio = tiob.build(createTreeLogger());
+ JClassType type = tio.findType("references.binary.clazz.supertype.Test");
+ assertNull(type);
+ }
+
+ /*
+ * If a type references a binary only type then it should not end up in the
+ * TypeOracle.
+ */
+ public void testReferencesBinaryOnlyTypeInExpression()
+ throws UnableToCompleteException {
+ TypeOracleBuilder tiob = createTypeInfoOracleBuilder();
+ tiob.addCompilationUnit(CU_Object);
+ tiob.addCompilationUnit(CU_ReferencesBinaryOnlyClassInExpression);
+
+ TypeOracle tio = tiob.build(createTreeLogger());
+ JClassType type = tio.findType("references.binary.clazz.expression.Test");
+ assertNull(type);
+ }
+
public void testSyntaxErrors() throws TypeOracleException,
UnableToCompleteException {
TypeOracleBuilder tiob = createTypeInfoOracleBuilder();