Version 1.4.98

Cherry-pick: Issue warning if -injars or -libraryjars are given non-archive arguments.
CL: https://r8-review.googlesource.com/c/r8/+/38549

Cherry-pick: Extend testing utilities for diagnostic inspection.
CL: https://r8-review.googlesource.com/c/r8/+/38557

Bug: 133122577
Change-Id: I075fd6488f3800ca4d255d12d133197f680b2a50
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 6fa660a..3b51d48 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "1.4.97";
+  public static final String LABEL = "1.4.98";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/FilteredClassPath.java b/src/main/java/com/android/tools/r8/shaking/FilteredClassPath.java
index 9badf35..9bef056 100644
--- a/src/main/java/com/android/tools/r8/shaking/FilteredClassPath.java
+++ b/src/main/java/com/android/tools/r8/shaking/FilteredClassPath.java
@@ -3,10 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
 import com.google.common.collect.ImmutableList;
-import java.io.File;
 import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.util.List;
 
 /**
@@ -21,32 +21,36 @@
 
   private final Path path;
   private final ImmutableList<String> pattern;
+  private final Origin origin;
+  private final Position position;
 
-  public FilteredClassPath(Path path, List<String> pattern) {
+  public FilteredClassPath(Path path, List<String> pattern, Origin origin, Position position) {
     this.path = path;
     this.pattern = ImmutableList.copyOf(pattern);
+    this.origin = origin;
+    this.position = position;
   }
 
   private FilteredClassPath(Path path) {
-    this(path, ImmutableList.of());
-  }
-
-  public static FilteredClassPath unfiltered(File file) {
-    return new FilteredClassPath(file.toPath());
+    this(path, ImmutableList.of(), Origin.unknown(), Position.UNKNOWN);
   }
 
   public static FilteredClassPath unfiltered(Path path) {
     return new FilteredClassPath(path);
   }
 
-  public static FilteredClassPath unfiltered(String path) {
-    return new FilteredClassPath(Paths.get(path));
-  }
-
   public Path getPath() {
     return path;
   }
 
+  public Origin getOrigin() {
+    return origin;
+  }
+
+  public Position getPosition() {
+    return position;
+  }
+
   public boolean matchesFile(String name) {
     if (isUnfiltered()) {
       return true;
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 4a4a182..7ab8dd5 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -1226,13 +1226,14 @@
     private List<FilteredClassPath> parseClassPath() throws ProguardRuleParserException {
       List<FilteredClassPath> classPath = new ArrayList<>();
       skipWhitespace();
+      TextPosition position = getPosition();
       Path file = parseFileName(true);
       ImmutableList<String> filters = parseClassPathFilters();
-      classPath.add(new FilteredClassPath(file, filters));
+      classPath.add(new FilteredClassPath(file, filters, origin, position));
       while (acceptChar(File.pathSeparatorChar)) {
         file = parseFileName(true);
         filters = parseClassPathFilters();
-        classPath.add(new FilteredClassPath(file, filters));
+        classPath.add(new FilteredClassPath(file, filters, origin, position));
       }
       return classPath;
     }
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index 4c15524..ad09465 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -439,10 +439,17 @@
     /** Add filtered archives of program resources. */
     public Builder addFilteredProgramArchives(Collection<FilteredClassPath> filteredArchives) {
       for (FilteredClassPath archive : filteredArchives) {
-        assert isArchive(archive.getPath());
-        ArchiveResourceProvider archiveResourceProvider =
-            new ArchiveResourceProvider(archive, ignoreDexInArchive);
-        addProgramResourceProvider(archiveResourceProvider);
+        if (isArchive(archive.getPath())) {
+          ArchiveResourceProvider archiveResourceProvider =
+              new ArchiveResourceProvider(archive, ignoreDexInArchive);
+          addProgramResourceProvider(archiveResourceProvider);
+        } else {
+          reporter.error(
+              new StringDiagnostic(
+                  "Unexpected input type. Only archive types are supported, e.g., .jar, .zip, etc.",
+                  archive.getOrigin(),
+                  archive.getPosition()));
+        }
       }
       return this;
     }
@@ -502,13 +509,21 @@
     /** Add library file resources. */
     public Builder addFilteredLibraryArchives(Collection<FilteredClassPath> filteredArchives) {
       for (FilteredClassPath archive : filteredArchives) {
-        assert isArchive(archive.getPath());
-        try {
-          FilteredArchiveClassFileProvider provider = new FilteredArchiveClassFileProvider(archive);
-          archiveProvidersToClose.add(provider);
-          libraryResourceProviders.add(provider);
-        } catch (IOException e) {
-          reporter.error(new ExceptionDiagnostic(e, new PathOrigin(archive.getPath())));
+        if (isArchive(archive.getPath())) {
+          try {
+            FilteredArchiveClassFileProvider provider =
+                new FilteredArchiveClassFileProvider(archive);
+            archiveProvidersToClose.add(provider);
+            libraryResourceProviders.add(provider);
+          } catch (IOException e) {
+            reporter.error(new ExceptionDiagnostic(e, new PathOrigin(archive.getPath())));
+          }
+        } else {
+          reporter.error(
+              new StringDiagnostic(
+                  "Unexpected input type. Only archive types are supported, e.g., .jar, .zip, etc.",
+                  archive.getOrigin(),
+                  archive.getPosition()));
         }
       }
       return this;
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 5900399..74ba7d6 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -110,6 +110,10 @@
         graphConsumer);
   }
 
+  public Builder getBuilder() {
+    return builder;
+  }
+
   public R8TestBuilder addDataResources(List<DataEntryResource> resources) {
     resources.forEach(builder.getAppBuilder()::addDataResource);
     return self();
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index a6afe52..1560cab 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -85,6 +85,19 @@
     }
   }
 
+  public CR compileWithExpectedDiagnostics(
+      Consumer<TestDiagnosticMessages> diagnosticsConsumer) throws CompilationFailedException {
+    TestDiagnosticMessages diagnosticsHandler = getState().getDiagnosticsMessages();
+    try {
+      CR result = compile();
+      diagnosticsConsumer.accept(diagnosticsHandler);
+      return result;
+    } catch (CompilationFailedException e) {
+      diagnosticsConsumer.accept(diagnosticsHandler);
+      throw e;
+    }
+  }
+
   @Override
   public RR run(String mainClass)
       throws CompilationFailedException, ExecutionException, IOException {
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
index d1e55f4..cfa647a 100644
--- a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
@@ -22,6 +22,8 @@
 
   public TestDiagnosticMessages assertOnlyWarnings();
 
+  public TestDiagnosticMessages assertOnlyErrors();
+
   public TestDiagnosticMessages assertInfosCount(int count);
 
   public TestDiagnosticMessages assertWarningsCount(int count);
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
index a0ed928..ccffe02 100644
--- a/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
@@ -76,6 +76,13 @@
     return this;
   }
 
+  public TestDiagnosticMessages assertOnlyErrors() {
+    assertEmpty("info", getInfos());
+    assertEmpty("warning", getWarnings());
+    assertNotEquals(0, getErrors().size());
+    return this;
+  }
+
   public TestDiagnosticMessages assertInfosCount(int count) {
     assertEquals(count, getInfos().size());
     return this;
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index ea774b8..0835f70 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -18,6 +18,8 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
 import com.android.tools.r8.shaking.FilteredClassPath;
 import com.android.tools.r8.shaking.ProguardConfiguration;
 import com.android.tools.r8.shaking.ProguardConfigurationParser;
@@ -1008,9 +1010,13 @@
   }
 
   public static void addFilteredAndroidJar(AndroidApp.Builder builder, AndroidApiLevel apiLevel) {
-    builder.addFilteredLibraryArchives(Collections.singletonList(
-        new FilteredClassPath(getAndroidJar(apiLevel),
-            ImmutableList.of("!junit/**", "!android/test/**"))));
+    builder.addFilteredLibraryArchives(
+        Collections.singletonList(
+            new FilteredClassPath(
+                getAndroidJar(apiLevel),
+                ImmutableList.of("!junit/**", "!android/test/**"),
+                Origin.unknown(),
+                Position.UNKNOWN)));
   }
 
   public static AndroidApp runD8(AndroidApp app) throws CompilationFailedException {
diff --git a/src/test/java/com/android/tools/r8/shaking/FilteredClassPathTest.java b/src/test/java/com/android/tools/r8/shaking/FilteredClassPathTest.java
index 7c243ae..31838fc 100644
--- a/src/test/java/com/android/tools/r8/shaking/FilteredClassPathTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/FilteredClassPathTest.java
@@ -8,6 +8,8 @@
 import com.android.tools.r8.ProgramResourceProvider;
 import com.android.tools.r8.ResourceException;
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.AndroidApp.Builder;
 import com.android.tools.r8.utils.ListUtils;
@@ -43,7 +45,8 @@
   }
 
   private static FilteredClassPath makeFilteredClassPath(Path path, List<String> filters) {
-    return new FilteredClassPath(path, ImmutableList.copyOf(filters));
+    return new FilteredClassPath(
+        path, ImmutableList.copyOf(filters), Origin.unknown(), Position.UNKNOWN);
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardDirectoryInputsTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardDirectoryInputsTest.java
new file mode 100644
index 0000000..67ed661
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardDirectoryInputsTest.java
@@ -0,0 +1,113 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.StringContains.containsString;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ByteDataView;
+import com.android.tools.r8.ClassFileConsumer.DirectoryConsumer;
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.TextPosition;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
+import java.util.Collections;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ProguardDirectoryInputsTest extends TestBase {
+
+  static final String EXPECTED = StringUtils.lines("Hello, world");
+
+  private final Backend backend;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static Backend[] data() {
+    return Backend.values();
+  }
+
+  public ProguardDirectoryInputsTest(Backend backend) {
+    this.backend = backend;
+  }
+
+  @Test(expected = CompilationFailedException.class)
+  public void testInJars() throws Exception {
+    test("injars");
+  }
+
+  @Test(expected = CompilationFailedException.class)
+  public void testLibraryJars() throws Exception {
+    test("libraryjars");
+  }
+
+  private void test(String option) throws Exception {
+    Path directory = temp.newFolder().toPath();
+    {
+      DirectoryConsumer consumer = new DirectoryConsumer(directory);
+      consumer.accept(
+          ByteDataView.of(ToolHelper.getClassAsBytes(OtherClass.class)),
+          DescriptorUtils.javaTypeToDescriptor(OtherClass.class.getTypeName()),
+          null);
+      consumer.finished(null);
+    }
+    if (backend == Backend.CF) {
+      testForJvm()
+          .addProgramClasses(TestClass.class)
+          .addClasspath(directory)
+          .run(TestClass.class)
+          .assertSuccessWithOutput(EXPECTED);
+    }
+
+    Origin origin =
+        new Origin(Origin.root()) {
+          @Override
+          public String part() {
+            return "pg-test-input";
+          }
+        };
+
+    String prefix = "-" + option + " ";
+    testForR8(backend)
+        .addProgramClasses(TestClass.class)
+        .apply(
+            b -> {
+              b.getBuilder()
+                  .addProguardConfiguration(
+                      Collections.singletonList(prefix + directory.toAbsolutePath()), origin);
+            })
+        .addKeepMainRule(TestClass.class)
+        .compileWithExpectedDiagnostics(diagnostics -> {
+          diagnostics.assertOnlyErrors();
+          diagnostics.assertErrorsCount(1);
+          Diagnostic diagnostic = diagnostics.getErrors().get(0);
+          assertThat(diagnostic.getDiagnosticMessage(), containsString("Unexpected input type"));
+          assertEquals(origin, diagnostic.getOrigin());
+          TextPosition position = (TextPosition) diagnostic.getPosition();
+          assertEquals(1, position.getLine());
+          assertEquals(prefix.length() + 1, position.getColumn());
+        });
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      System.out.println("Hello, world");
+    }
+  }
+
+  static class OtherClass {
+
+    public void foo() {
+      System.out.println("foo");
+    }
+  }
+}