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