NPE in UnusedImportsRemover when processing files without definitions.

NPE when a source files contains a package line and at least one
import line but not class nor interface definitions (i.e. empty).

Bug: issue 8566.
(cherry picked from commit 04bc38b5070accc4c86504fc390484c21ded2c33)

Change-Id: Id917eb98e44c28a93fd2da69e3a1d9a23993975b
diff --git a/dev/core/src/com/google/gwt/dev/javac/UnusedImportsRemover.java b/dev/core/src/com/google/gwt/dev/javac/UnusedImportsRemover.java
index 13c9f9b..c269964 100644
--- a/dev/core/src/com/google/gwt/dev/javac/UnusedImportsRemover.java
+++ b/dev/core/src/com/google/gwt/dev/javac/UnusedImportsRemover.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 2013 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
@@ -174,8 +174,11 @@
     }
 
     AccumulateNamesVisitor astVisitor = new AccumulateNamesVisitor();
-    for (TypeDeclaration typeDecl : cud.types) {
-      typeDecl.traverse(astVisitor, cud.scope);
+
+    if (cud.types != null) {
+      for (TypeDeclaration typeDecl : cud.types) {
+        typeDecl.traverse(astVisitor, cud.scope);
+      }
     }
 
     // for some reason JDT does not traverse package annotations even if the traversal started at
diff --git a/dev/core/test/com/google/gwt/dev/javac/JdtCompilerTest.java b/dev/core/test/com/google/gwt/dev/javac/JdtCompilerTest.java
index 3215031..9f6285f 100644
--- a/dev/core/test/com/google/gwt/dev/javac/JdtCompilerTest.java
+++ b/dev/core/test/com/google/gwt/dev/javac/JdtCompilerTest.java
@@ -1,12 +1,12 @@
 /*
  * 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
@@ -15,7 +15,9 @@
  */
 package com.google.gwt.dev.javac;
 
+import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.dev.javac.testing.impl.JavaResourceBase;
+import com.google.gwt.dev.javac.testing.impl.MockJavaResource;
 
 import java.util.Collection;
 import java.util.List;
@@ -42,4 +44,89 @@
     units = compile(builders);
     assertUnitsCompiled(units);
   }
+
+  public void testCompileEmptyFile() throws Exception {
+    assertUnitCompilesWithNoErrors("com.example.Empty",
+        "package com.example;");
+  }
+
+  /**
+   * Test for issue 8566
+   */
+  public void testCompileEmptyFileWithImports() throws Exception {
+    assertUnitCompilesWithNoErrors("com.example.EmptyWithImports",
+        "package com.example;",
+        "import java.util.Collections;");
+  }
+
+  public void testRemoveUnusedImports() throws Exception {
+    // java.util.BlahBlahBlah is imported but does not exist, so it should generate a
+    // compile error if not removed.
+    JdtCompiler.setRemoveUnusedImports(false);
+    assertUnitHasErrors("com.example.UnusedImports",
+        "package com.example;",
+        "import java.util.BlahBlahBlah;",
+        "public class UnusedImports {",
+        "}");
+    JdtCompiler.setRemoveUnusedImports(true);
+    assertUnitCompilesWithNoErrors("com.example.UnusedImports",
+        "package com.example;",
+        "import java.util.BlahBlahBlah;",
+        "public class UnusedImports {",
+        "}");
+  }
+
+  public void testRemoveUnusedStaticImports() throws Exception {
+    // java.util.BlahBlahBlah.Blah is imported but does not exist, so it should generate a
+    // compile error if not removed.
+    JdtCompiler.setRemoveUnusedImports(false);
+    assertUnitHasErrors("com.example.UnusedStaticImports",
+        "package com.example;",
+        "import static java.util.BlahBlahBlah.Blah;",
+        "public class UnusedStaticImports {",
+        "}");
+    // If the imported static exists the it should compile even if unused imports are not removed
+    assertUnitCompilesWithNoErrors("com.example.UnusedStaticImports",
+        "package com.example;",
+        "import static java.lang.Float.valueOf;",
+        "public class UnusedStaticImports {",
+        "}");
+    JdtCompiler.setRemoveUnusedImports(true);
+    assertUnitCompilesWithNoErrors("com.example.UnusedStaticImports",
+        "package com.example;",
+        "import static java.util.BlahBlahBlah.Blah;",
+        "public class UnusedStaticImports {",
+        "}");
+  }
+
+  private void assertUnitCompilesWithNoErrors(String sourceName, String... sourceLines)
+      throws UnableToCompleteException {
+    CompilationUnit unit = compileUnit(sourceName, sourceLines);
+    assertNotNull(unit);
+    assertFalse(unit.isError());
+  }
+
+  private void assertUnitHasErrors(String sourceName, String... sourceLines)
+      throws UnableToCompleteException {
+    CompilationUnit unit = compileUnit(sourceName, sourceLines);
+    assertNotNull(unit);
+    assertTrue(unit.isError());
+  }
+
+  private CompilationUnit compileUnit(String sourceName, String[] sourceLines)
+      throws UnableToCompleteException {
+    MockJavaResource emptySourceFile =
+        JavaResourceBase.createMockJavaResource(sourceName, sourceLines);
+    List<CompilationUnit> units = compile(emptySourceFile);
+    return findUnitBySourceName(sourceName, units);
+  }
+
+  private CompilationUnit findUnitBySourceName(String sourceName, List<CompilationUnit> units) {
+    for (CompilationUnit unit : units) {
+      if (unit.getTypeName().equals(sourceName)) {
+        return unit;
+      }
+    }
+    return null;
+  }
 }