Provide more information on the MissingDefinitionsDiagnostic

Add access to the references for the missing items.

Bug: 169127026
Change-Id: I5bf5f05b341dd53e43755a87f122ec999e7e0e15
diff --git a/src/main/java/com/android/tools/r8/tracereferences/MissingDefinitionsDiagnostic.java b/src/main/java/com/android/tools/r8/tracereferences/MissingDefinitionsDiagnostic.java
index 8fd9edb..12a2462 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/MissingDefinitionsDiagnostic.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/MissingDefinitionsDiagnostic.java
@@ -7,10 +7,9 @@
 import com.android.tools.r8.Keep;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.Position;
-import com.android.tools.r8.tracereferences.Tracer.TracedClassImpl;
-import com.android.tools.r8.tracereferences.Tracer.TracedFieldImpl;
-import com.android.tools.r8.tracereferences.Tracer.TracedMethodImpl;
-import com.android.tools.r8.tracereferences.Tracer.TracedReferenceBase;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.MethodReference;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
@@ -18,14 +17,14 @@
 @Keep
 public class MissingDefinitionsDiagnostic implements Diagnostic {
 
-  private final Set<TracedClassImpl> missingClasses;
-  private final Set<TracedFieldImpl> missingFields;
-  private final Set<TracedMethodImpl> missingMethods;
+  private final Set<ClassReference> missingClasses;
+  private final Set<FieldReference> missingFields;
+  private final Set<MethodReference> missingMethods;
 
   MissingDefinitionsDiagnostic(
-      Set<TracedClassImpl> missingClasses,
-      Set<TracedFieldImpl> missingFields,
-      Set<TracedMethodImpl> missingMethods) {
+      Set<ClassReference> missingClasses,
+      Set<FieldReference> missingFields,
+      Set<MethodReference> missingMethods) {
     this.missingClasses = missingClasses;
     this.missingFields = missingFields;
     this.missingMethods = missingMethods;
@@ -41,15 +40,25 @@
     return Position.UNKNOWN;
   }
 
-  private <T extends TracedReferenceBase<?, ?>> void appendSorted(
-      StringBuilder builder, Set<T> missing) {
+  private <T> void appendSorted(StringBuilder builder, Set<T> missing) {
     missing.stream()
-        .map(element -> element.getReference())
         .map(Object::toString)
         .sorted()
         .forEach(item -> builder.append("  ").append(item).append(System.lineSeparator()));
   }
 
+  public Set<ClassReference> getMissingClasses() {
+    return missingClasses;
+  }
+
+  public Set<FieldReference> getMissingFields() {
+    return missingFields;
+  }
+
+  public Set<MethodReference> getMissingMethods() {
+    return missingMethods;
+  }
+
   @Override
   public String getDiagnosticMessage() {
     StringBuilder builder = new StringBuilder("Tracereferences found ");
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 8b571a8..539ac24 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
@@ -259,9 +259,9 @@
     private final TraceReferencesConsumer consumer;
     private DexProgramClass context;
     private final DiagnosticsHandler diagnostics;
-    private final Set<TracedClassImpl> missingClasses = new HashSet<>();
-    private final Set<TracedFieldImpl> missingFields = new HashSet<>();
-    private final Set<TracedMethodImpl> missingMethods = new HashSet<>();
+    private final Set<ClassReference> missingClasses = new HashSet<>();
+    private final Set<FieldReference> missingFields = new HashSet<>();
+    private final Set<MethodReference> missingMethods = new HashSet<>();
 
     UseCollector(
         DexItemFactory factory, TraceReferencesConsumer consumer, DiagnosticsHandler diagnostics) {
@@ -347,10 +347,10 @@
       collectMissing(tracedMethod, missingMethods);
     }
 
-    private <T extends TracedReferenceBase<?, ?>> void collectMissing(
-        T tracedReference, Set<T> missingCollection) {
+    private <R, T extends TracedReferenceBase<R, ?>> void collectMissing(
+        T tracedReference, Set<R> missingCollection) {
       if (tracedReference.isMissingDefinition()) {
-        missingCollection.add(tracedReference);
+        missingCollection.add(tracedReference.getReference());
       }
     }
 
diff --git a/src/test/java/com/android/tools/r8/DiagnosticsChecker.java b/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
index 09e32da..bda7fe1 100644
--- a/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
+++ b/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
@@ -48,7 +48,7 @@
     void run(DiagnosticsHandler handler) throws CompilationFailedException;
   }
 
-  private static void checkContains(String snippet, List<Diagnostic> diagnostics) {
+  public static void checkContains(String snippet, List<Diagnostic> diagnostics) {
     List<String> messages = ListUtils.map(diagnostics, Diagnostic::getDiagnosticMessage);
     System.out.println("Expecting match for '" + snippet + "'");
     System.out.println("StdErr:\n" + messages);
@@ -60,6 +60,10 @@
         diagnostics.stream().anyMatch(d -> d.getDiagnosticMessage().contains(snippet)));
   }
 
+  public static void checkContains(Collection<String> snippets, List<Diagnostic> diagnostics) {
+    snippets.forEach(snippet -> checkContains(snippet, diagnostics));
+  }
+
   public void checkErrorsContains(String snippet) {
     checkContains(snippet, errors);
   }
@@ -84,7 +88,20 @@
       runner.run(handler);
       fail("Failure expected");
     } catch (CompilationFailedException e) {
-      snippets.forEach(snippet -> checkContains(snippet, handler.errors));
+      checkContains(snippets, handler.errors);
+      throw e;
+    }
+  }
+
+  public static void checkErrorDiagnostics(
+      Consumer<DiagnosticsChecker> checker, FailingRunner runner)
+      throws CompilationFailedException {
+    DiagnosticsChecker handler = new DiagnosticsChecker();
+    try {
+      runner.run(handler);
+      fail("Failure expected");
+    } catch (CompilationFailedException e) {
+      checker.accept(handler);
       throw e;
     }
   }
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest.java
index 551c8a1..cad4538 100644
--- a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest.java
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.tracereferences;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import com.android.tools.r8.CompilationFailedException;
@@ -12,13 +14,16 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import java.nio.file.Path;
+import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -60,25 +65,55 @@
             .build();
 
     String prefix = "  Lcom/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest$";
+    List<String> snippets =
+        ImmutableList.of(
+            "Tracereferences found 3 classe(s), 2 field(s) and 4 method(s) without definition",
+            StringUtils.lines(
+                "Classe(s) without definition:",
+                prefix + "Target1;",
+                prefix + "Target2;",
+                prefix + "Target3;"),
+            StringUtils.lines(
+                "Field(s) without definition:",
+                prefix + "Target;missingField1:I",
+                prefix + "Target;missingField2:I"),
+            StringUtils.lines(
+                "Method(s) without definition:",
+                prefix + "Target1;<init>()V",
+                prefix + "Target2;<init>()V",
+                prefix + "Target3;<init>()V",
+                prefix + "Target;missingMethod()V"));
     try {
-      DiagnosticsChecker.checkErrorsContains(
-          ImmutableList.of(
-              "Tracereferences found 3 classe(s), 2 field(s) and 4 method(s) without definition",
-              StringUtils.lines(
-                  "Classe(s) without definition:",
-                  prefix + "Target1;",
-                  prefix + "Target2;",
-                  prefix + "Target3;"),
-              StringUtils.lines(
-                  "Field(s) without definition:",
-                  prefix + "Target;missingField1:I",
-                  prefix + "Target;missingField2:I"),
-              StringUtils.lines(
-                  "Method(s) without definition:",
-                  prefix + "Target1;<init>()V",
-                  prefix + "Target2;<init>()V",
-                  prefix + "Target3;<init>()V",
-                  prefix + "Target;missingMethod()V")),
+      DiagnosticsChecker.checkErrorDiagnostics(
+          checker -> {
+            DiagnosticsChecker.checkContains(snippets, checker.errors);
+            try {
+              assertEquals(1, checker.errors.size());
+              assertTrue(checker.errors.get(0) instanceof MissingDefinitionsDiagnostic);
+              MissingDefinitionsDiagnostic diagnostic =
+                  (MissingDefinitionsDiagnostic) checker.errors.get(0);
+              assertEquals(
+                  diagnostic.getMissingClasses(),
+                  ImmutableSet.of(
+                      Reference.classFromClass(Target1.class),
+                      Reference.classFromClass(Target2.class),
+                      Reference.classFromClass(Target3.class)));
+              assertEquals(
+                  diagnostic.getMissingFields(),
+                  ImmutableSet.of(
+                      Reference.fieldFromField(Target.class.getField("missingField1")),
+                      Reference.fieldFromField(Target.class.getField("missingField2"))));
+              assertEquals(
+                  diagnostic.getMissingMethods(),
+                  ImmutableSet.of(
+                      Reference.methodFromMethod(Target1.class.getDeclaredConstructor()),
+                      Reference.methodFromMethod(Target2.class.getDeclaredConstructor()),
+                      Reference.methodFromMethod(Target3.class.getDeclaredConstructor()),
+                      Reference.methodFromMethod(Target.class.getMethod("missingMethod"))));
+            } catch (ReflectiveOperationException e) {
+              fail("Unexpected exception");
+            }
+          },
           handler ->
               TraceReferences.run(
                   TraceReferencesCommand.builder(handler)
@@ -125,16 +160,37 @@
             .build();
 
     String prefix = "  Lcom/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest$";
+    List<String> snippets =
+        ImmutableList.of(
+            "Tracereferences found 2 field(s) and 1 method(s) without definition",
+            StringUtils.lines(
+                "Field(s) without definition:",
+                prefix + "Target;missingField1:I",
+                prefix + "Target;missingField2:I"),
+            StringUtils.lines("Method(s) without definition:", prefix + "Target;missingMethod()V"));
     try {
-      DiagnosticsChecker.checkErrorsContains(
-          ImmutableList.of(
-              "Tracereferences found 2 field(s) and 1 method(s) without definition",
-              StringUtils.lines(
-                  "Field(s) without definition:",
-                  prefix + "Target;missingField1:I",
-                  prefix + "Target;missingField2:I"),
-              StringUtils.lines(
-                  "Method(s) without definition:", prefix + "Target;missingMethod()V")),
+      DiagnosticsChecker.checkErrorDiagnostics(
+          checker -> {
+            DiagnosticsChecker.checkContains(snippets, checker.errors);
+            try {
+              assertEquals(1, checker.errors.size());
+              assertTrue(checker.errors.get(0) instanceof MissingDefinitionsDiagnostic);
+              MissingDefinitionsDiagnostic diagnostic =
+                  (MissingDefinitionsDiagnostic) checker.errors.get(0);
+              assertEquals(diagnostic.getMissingClasses(), ImmutableSet.of());
+              assertEquals(
+                  diagnostic.getMissingFields(),
+                  ImmutableSet.of(
+                      Reference.fieldFromField(Target.class.getField("missingField1")),
+                      Reference.fieldFromField(Target.class.getField("missingField2"))));
+              assertEquals(
+                  diagnostic.getMissingMethods(),
+                  ImmutableSet.of(
+                      Reference.methodFromMethod(Target.class.getMethod("missingMethod"))));
+            } catch (ReflectiveOperationException e) {
+              fail("Unexpected exception");
+            }
+          },
           handler ->
               TraceReferences.run(
                   TraceReferencesCommand.builder(handler)
@@ -175,12 +231,29 @@
             .build();
 
     String prefix = "  Lcom/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest$";
+    List<String> snippets =
+        ImmutableList.of(
+            "Tracereferences found 1 method(s) without definition",
+            StringUtils.lines("Method(s) without definition:", prefix + "Target;missingMethod()V"));
     try {
-      DiagnosticsChecker.checkErrorsContains(
-          ImmutableList.of(
-              "Tracereferences found 1 method(s) without definition",
-              StringUtils.lines(
-                  "Method(s) without definition:", prefix + "Target;missingMethod()V")),
+      DiagnosticsChecker.checkErrorDiagnostics(
+          checker -> {
+            DiagnosticsChecker.checkContains(snippets, checker.errors);
+            try {
+              assertEquals(1, checker.errors.size());
+              assertTrue(checker.errors.get(0) instanceof MissingDefinitionsDiagnostic);
+              MissingDefinitionsDiagnostic diagnostic =
+                  (MissingDefinitionsDiagnostic) checker.errors.get(0);
+              assertEquals(diagnostic.getMissingClasses(), ImmutableSet.of());
+              assertEquals(diagnostic.getMissingFields(), ImmutableSet.of());
+              assertEquals(
+                  diagnostic.getMissingMethods(),
+                  ImmutableSet.of(
+                      Reference.methodFromMethod(Target.class.getMethod("missingMethod"))));
+            } catch (ReflectiveOperationException e) {
+              fail("Unexpected exception");
+            }
+          },
           handler ->
               TraceReferences.run(
                   TraceReferencesCommand.builder(handler)