Make missing references an error in tracereferences

Bug: 169127026
Bug: 169546956
Change-Id: Ifc8ad05452a6e9a8b84b7dcbefaa46e3096866e2
diff --git a/src/main/java/com/android/tools/r8/tracereferences/Formatter.java b/src/main/java/com/android/tools/r8/tracereferences/Formatter.java
index 01b7f99..b572f26 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/Formatter.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/Formatter.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.tracereferences;
 
 import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.FieldReference;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.references.PackageReference;
 import com.android.tools.r8.references.TypeReference;
@@ -42,10 +41,6 @@
     output.append(StringUtils.lines(string));
   }
 
-  protected void appendLine() {
-    appendLine("");
-  }
-
   protected void printArguments(MethodReference method) {
     StringUtils.append(
         output,
@@ -56,33 +51,10 @@
 
   protected abstract void printConstructorName(MethodReference method);
 
-  private void printError(String message) {
-    append("# Error: " + message);
-  }
-
   protected abstract void printField(TracedField field);
 
   protected abstract void printMethod(TracedMethod method);
 
-  private void printFieldError(FieldReference field) {
-    appendLine(
-        field.getFieldType().getTypeName()
-            + " "
-            + field.getHolderClass().getTypeName()
-            + "."
-            + field.getFieldName());
-  }
-
-  private void printMethodError(MethodReference method) {
-    printReturn(method);
-    append(" ");
-    append(method.getHolderClass().getTypeName());
-    append(".");
-    append(method.getMethodName());
-    printArguments(method);
-    appendLine();
-  }
-
   protected abstract void printPackageNames(List<String> packageNames);
 
   protected void printReturn(MethodReference method) {
@@ -104,23 +76,20 @@
   protected abstract void printTypeFooter();
 
   void format(TraceReferencesResult result) {
-    int errors =
-        print(
-            result.types,
-            result.keepPackageNames,
-            result.fields,
-            result.methods,
-            result.missingDefinition);
-    assert errors == result.missingDefinition.size();
+    print(
+        result.types,
+        result.keepPackageNames,
+        result.fields,
+        result.methods,
+        result.missingDefinition);
   }
 
-  private int print(
+  private void print(
       Set<TracedClass> types,
       Set<PackageReference> keepPackageNames,
       Map<ClassReference, Set<TracedField>> fields,
       Map<ClassReference, Set<TracedMethod>> methods,
       Set<Object> missingDefinition) {
-    int errors = 0;
     List<TracedClass> sortedTypes = new ArrayList<>(types);
     sortedTypes.sort(Comparator.comparing(tracedClass -> tracedClass.getReference().getTypeName()));
     for (TracedClass type : sortedTypes) {
@@ -128,21 +97,13 @@
           methods.getOrDefault(type.getReference(), Collections.emptySet());
       Set<TracedField> fieldsForClass =
           fields.getOrDefault(type.getReference(), Collections.emptySet());
-      boolean typeMissing = missingDefinition.contains(type.getReference());
-      if (typeMissing) {
-        printError("Could not find definition for type " + type.getReference().getTypeName());
-        appendLine();
-        errors++;
-      } else {
-        printTypeHeader(type);
+      if (missingDefinition.contains(type.getReference())) {
+        continue;
       }
+      printTypeHeader(type);
       List<TracedMethod> sortedMethods = new ArrayList<>(methodsForClass.size());
       for (TracedMethod method : methodsForClass) {
-        assert method.isMissingDefinition() || !typeMissing;
         if (method.isMissingDefinition()) {
-          printError("Could not find definition for method ");
-          printMethodError(method.getReference());
-          errors++;
           continue;
         }
         assert method.getAccessFlags() != null;
@@ -156,18 +117,12 @@
       List<TracedField> sortedFields = new ArrayList<>(fieldsForClass);
       sortedFields.sort(Comparator.comparing(tracedField -> tracedField.getReference().toString()));
       for (TracedField field : sortedFields) {
-        assert field.isMissingDefinition() || !typeMissing;
         if (field.isMissingDefinition()) {
-          printError("Could not find definition for field ");
-          printFieldError(field.getReference());
-          errors++;
           continue;
         }
         printField(field);
       }
-      if (!typeMissing) {
-        printTypeFooter();
-      }
+      printTypeFooter();
     }
     List<String> packageNamesToKeep =
         keepPackageNames.stream()
@@ -175,6 +130,5 @@
             .sorted()
             .collect(Collectors.toList());
     printPackageNames(packageNamesToKeep);
-    return errors;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesAbortException.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesAbortException.java
deleted file mode 100644
index 8afab19..0000000
--- a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesAbortException.java
+++ /dev/null
@@ -1,6 +0,0 @@
-// Copyright (c) 2020, 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.tracereferences;
-
-public class TraceReferencesAbortException extends RuntimeException {}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
index 4d44912..1149b20 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
@@ -332,7 +332,7 @@
 
     private void checkDiagnostics(TracedReferenceBase<?, ?> tracedReference) {
       if (tracedReference.isMissingDefinition() && missingDefinitionReported.add(tracedReference)) {
-        diagnostics.warning(
+        diagnostics.error(
             new StringDiagnostic(
                 "Missing definition of " + tracedReference.getKindName() + " " + tracedReference));
       }
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java
index 42e867d..a312c2f 100644
--- a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java
@@ -5,6 +5,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertSame;
+import static org.junit.Assert.fail;
 
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.DiagnosticsChecker;
@@ -156,20 +157,27 @@
     Path output = dir.resolve("output.txt");
     DiagnosticsChecker diagnosticsChecker = new DiagnosticsChecker();
     TraceReferencesFormattingConsumer consumer = new TraceReferencesFormattingConsumer(format);
-    TraceReferences.run(
-        TraceReferencesCommand.builder(diagnosticsChecker)
-            .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
-            .addTargetFiles(targetJar)
-            .addSourceFiles(sourceJar)
-            .setConsumer(consumer)
-            .build());
-    assertEquals(expected, consumer.get());
-    if (diagnosticsCheckerConsumer != null) {
-      diagnosticsCheckerConsumer.accept(diagnosticsChecker);
-    } else {
-      assertEquals(0, diagnosticsChecker.errors.size());
-      assertEquals(0, diagnosticsChecker.warnings.size());
-      assertEquals(0, diagnosticsChecker.infos.size());
+    try {
+      TraceReferences.run(
+          TraceReferencesCommand.builder(diagnosticsChecker)
+              .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+              .addTargetFiles(targetJar)
+              .addSourceFiles(sourceJar)
+              .setConsumer(consumer)
+              .build());
+      assertEquals(expected, consumer.get());
+      if (diagnosticsCheckerConsumer != null) {
+        diagnosticsCheckerConsumer.accept(diagnosticsChecker);
+      } else {
+        assertEquals(0, diagnosticsChecker.errors.size());
+        assertEquals(0, diagnosticsChecker.warnings.size());
+        assertEquals(0, diagnosticsChecker.infos.size());
+      }
+    } catch (CompilationFailedException e) {
+      if (diagnosticsCheckerConsumer != null) {
+        diagnosticsCheckerConsumer.accept(diagnosticsChecker);
+      }
+      throw e;
     }
 
     TraceReferences.run(
@@ -270,64 +278,60 @@
   @Test
   public void testNoReferences_printUses() throws Throwable {
     String targetTypeName = Target.class.getTypeName();
-    runAndCheckOutput(
-        ImmutableList.of(OtherTarget.class),
-        ImmutableList.of(Source.class),
-        TraceReferencesFormattingConsumer.OutputFormat.PRINTUSAGE,
-        StringUtils.lines(
-            ImmutableList.of(
-                "# Error: Could not find definition for type " + targetTypeName,
-                "# Error: Could not find definition for method void "
-                    + targetTypeName
-                    + ".target(int)",
-                "# Error: Could not find definition for field int " + targetTypeName + ".field")),
-        diagnosticsChecker -> {
-          assertEquals(0, diagnosticsChecker.errors.size());
-          assertEquals(3, diagnosticsChecker.warnings.size());
-          assertEquals(0, diagnosticsChecker.infos.size());
-        });
+    try {
+      runAndCheckOutput(
+          ImmutableList.of(OtherTarget.class),
+          ImmutableList.of(Source.class),
+          TraceReferencesFormattingConsumer.OutputFormat.PRINTUSAGE,
+          StringUtils.lines(),
+          diagnosticsChecker -> {
+            assertEquals(3, diagnosticsChecker.errors.size());
+            assertEquals(0, diagnosticsChecker.warnings.size());
+            assertEquals(0, diagnosticsChecker.infos.size());
+          });
+      fail("Expected compilation to fail");
+    } catch (CompilationFailedException e) {
+      // Expected.
+    }
   }
 
   @Test
   public void testMissingReference_keepRules() throws Throwable {
     String targetTypeName = Target.class.getTypeName();
-    runAndCheckOutput(
-        ImmutableList.of(OtherTarget.class),
-        ImmutableList.of(Source.class),
-        TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES,
-        StringUtils.lines(
-            ImmutableList.of(
-                "# Error: Could not find definition for type " + targetTypeName,
-                "# Error: Could not find definition for method void "
-                    + targetTypeName
-                    + ".target(int)",
-                "# Error: Could not find definition for field int " + targetTypeName + ".field")),
-        diagnosticsChecker -> {
-          assertEquals(0, diagnosticsChecker.errors.size());
-          assertEquals(3, diagnosticsChecker.warnings.size());
-          assertEquals(0, diagnosticsChecker.infos.size());
-        });
+    try {
+      runAndCheckOutput(
+          ImmutableList.of(OtherTarget.class),
+          ImmutableList.of(Source.class),
+          TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES,
+          StringUtils.lines(),
+          diagnosticsChecker -> {
+            assertEquals(3, diagnosticsChecker.errors.size());
+            assertEquals(0, diagnosticsChecker.warnings.size());
+            assertEquals(0, diagnosticsChecker.infos.size());
+          });
+      fail("Expected compilation to fail");
+    } catch (CompilationFailedException e) {
+      // Expected.
+    }
   }
 
   @Test
   public void testNoReferences_keepRulesAllowObfuscation() throws Throwable {
-    String targetTypeName = Target.class.getTypeName();
-    runAndCheckOutput(
-        ImmutableList.of(OtherTarget.class),
-        ImmutableList.of(Source.class),
-        TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION,
-        StringUtils.lines(
-            ImmutableList.of(
-                "# Error: Could not find definition for type " + targetTypeName,
-                "# Error: Could not find definition for method void "
-                    + targetTypeName
-                    + ".target(int)",
-                "# Error: Could not find definition for field int " + targetTypeName + ".field")),
-        diagnosticsChecker -> {
-          assertEquals(0, diagnosticsChecker.errors.size());
-          assertEquals(3, diagnosticsChecker.warnings.size());
-          assertEquals(0, diagnosticsChecker.infos.size());
-        });
+    try {
+      runAndCheckOutput(
+          ImmutableList.of(OtherTarget.class),
+          ImmutableList.of(Source.class),
+          TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION,
+          StringUtils.lines(),
+          diagnosticsChecker -> {
+            assertEquals(3, diagnosticsChecker.errors.size());
+            assertEquals(0, diagnosticsChecker.warnings.size());
+            assertEquals(0, diagnosticsChecker.infos.size());
+          });
+      fail("Expected compilation to fail");
+    } catch (CompilationFailedException e) {
+      // Expected.
+    }
   }
 
   public static void zip(Path zipFile, String path, byte[] data) throws IOException {
@@ -350,24 +354,23 @@
         sourceJar,
         ToolHelper.getClassPathForTests(),
         ToolHelper.getClassFileForTestClass(Source.class));
-    runAndCheckOutput(
-        targetJar,
-        sourceJar,
-        TraceReferencesFormattingConsumer.OutputFormat.PRINTUSAGE,
-        StringUtils.lines(
-            ImmutableList.of(
-                "com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target",
-                "# Error: Could not find definition for method void"
-                    + " com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target"
-                    + ".target(int)",
-                "# Error: Could not find definition for field int"
-                    + " com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target"
-                    + ".field")),
-        diagnosticsChecker -> {
-          assertEquals(0, diagnosticsChecker.errors.size());
-          assertEquals(2, diagnosticsChecker.warnings.size());
-          assertEquals(0, diagnosticsChecker.infos.size());
-        });
+    try {
+      runAndCheckOutput(
+          targetJar,
+          sourceJar,
+          TraceReferencesFormattingConsumer.OutputFormat.PRINTUSAGE,
+          StringUtils.lines(
+              ImmutableList.of(
+                  "com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target")),
+          diagnosticsChecker -> {
+            assertEquals(2, diagnosticsChecker.errors.size());
+            assertEquals(0, diagnosticsChecker.warnings.size());
+            assertEquals(0, diagnosticsChecker.infos.size());
+          });
+      fail("Expected compilation to fail");
+    } catch (CompilationFailedException e) {
+      // Expected.
+    }
   }
 
   @Test
@@ -380,27 +383,27 @@
         sourceJar,
         ToolHelper.getClassPathForTests(),
         ToolHelper.getClassFileForTestClass(Source.class));
-    runAndCheckOutput(
-        targetJar,
-        sourceJar,
-        TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES,
-        StringUtils.lines(
-            ImmutableList.of(
-                "-keep class com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target"
-                    + " {",
-                "# Error: Could not find definition for method void"
-                    + " com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target"
-                    + ".target(int)",
-                "# Error: Could not find definition for field int"
-                    + " com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target"
-                    + ".field",
-                "}",
-                "-keeppackagenames com.android.tools.r8.tracereferences")),
-        diagnosticsChecker -> {
-          assertEquals(0, diagnosticsChecker.errors.size());
-          assertEquals(2, diagnosticsChecker.warnings.size());
-          assertEquals(0, diagnosticsChecker.infos.size());
-        });
+    try {
+      runAndCheckOutput(
+          targetJar,
+          sourceJar,
+          TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES,
+          StringUtils.lines(
+              ImmutableList.of(
+                  "-keep class"
+                      + " com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target"
+                      + " {",
+                  "}",
+                  "-keeppackagenames com.android.tools.r8.tracereferences")),
+          diagnosticsChecker -> {
+            assertEquals(2, diagnosticsChecker.errors.size());
+            assertEquals(0, diagnosticsChecker.warnings.size());
+            assertEquals(0, diagnosticsChecker.infos.size());
+          });
+      fail("Expected compilation to fail");
+    } catch (CompilationFailedException e) {
+      // Expected.
+    }
   }
 
   @Test
@@ -413,25 +416,26 @@
         sourceJar,
         ToolHelper.getClassPathForTests(),
         ToolHelper.getClassFileForTestClass(Source.class));
-    runAndCheckOutput(
-        targetJar,
-        sourceJar,
-        TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION,
-        StringUtils.lines(
-            ImmutableList.of(
-                "-keep,allowobfuscation class"
-                    + " com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target {",
-                "# Error: Could not find definition for method void"
-                    + " com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target.target(int)",
-                "# Error: Could not find definition for field int"
-                    + " com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target.field",
-                "}",
-                "-keeppackagenames com.android.tools.r8.tracereferences")),
-        diagnosticsChecker -> {
-          assertEquals(0, diagnosticsChecker.errors.size());
-          assertEquals(2, diagnosticsChecker.warnings.size());
-          assertEquals(0, diagnosticsChecker.infos.size());
-        });
+    try {
+      runAndCheckOutput(
+          targetJar,
+          sourceJar,
+          TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION,
+          StringUtils.lines(
+              ImmutableList.of(
+                  "-keep,allowobfuscation class"
+                      + " com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target {",
+                  "}",
+                  "-keeppackagenames com.android.tools.r8.tracereferences")),
+          diagnosticsChecker -> {
+            assertEquals(2, diagnosticsChecker.errors.size());
+            assertEquals(0, diagnosticsChecker.warnings.size());
+            assertEquals(0, diagnosticsChecker.infos.size());
+          });
+      fail("Expected compilation to fail");
+    } catch (CompilationFailedException e) {
+      // Expected.
+    }
   }
 
   private byte[] getClassWithTargetRemoved() throws IOException {
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesMissingReferencesInDexTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesMissingReferencesInDexTest.java
index 8a080e3..8b59d47 100644
--- a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesMissingReferencesInDexTest.java
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesMissingReferencesInDexTest.java
@@ -6,7 +6,9 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
+import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.DiagnosticsChecker;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -69,12 +71,17 @@
     DiagnosticsChecker diagnosticsChecker = new DiagnosticsChecker();
     MissingReferencesConsumer consumer = new MissingReferencesConsumer();
 
-    TraceReferences.run(
-        TraceReferencesCommand.builder(diagnosticsChecker)
-            .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
-            .addSourceFiles(sourceDex)
-            .setConsumer(consumer)
-            .build());
+    try {
+      TraceReferences.run(
+          TraceReferencesCommand.builder(diagnosticsChecker)
+              .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+              .addSourceFiles(sourceDex)
+              .setConsumer(consumer)
+              .build());
+      fail("Expected compilation to fail");
+    } catch (CompilationFailedException e) {
+      // Expected.
+    }
 
     assertTrue(consumer.acceptTypeCalled);
     assertTrue(consumer.acceptFieldCalled);
@@ -93,12 +100,17 @@
     DiagnosticsChecker diagnosticsChecker = new DiagnosticsChecker();
     MissingReferencesConsumer consumer = new MissingReferencesConsumer();
 
-    TraceReferences.run(
-        TraceReferencesCommand.builder(diagnosticsChecker)
-            .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
-            .addSourceFiles(sourceDex)
-            .setConsumer(consumer)
-            .build());
+    try {
+      TraceReferences.run(
+          TraceReferencesCommand.builder(diagnosticsChecker)
+              .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+              .addSourceFiles(sourceDex)
+              .setConsumer(consumer)
+              .build());
+      fail("Expected compilation to fail");
+    } catch (CompilationFailedException e) {
+      // Expected.
+    }
 
     assertFalse(consumer.acceptTypeCalled);
     assertTrue(consumer.acceptFieldCalled);