Create a specific diagnostic for checkdiscard failure
Bug: 166258805
Change-Id: I997c9b1fc8e3a068c1ea53407206b6f65143aa61
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 9bed6c3..ab78b10 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.dex.ApplicationWriter;
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.dex.Marker.Tool;
+import com.android.tools.r8.errors.CheckDiscardDiagnostic;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -1082,16 +1083,10 @@
timing);
}
}
- for (DexDefinition definition : failed) {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- whyAreYouKeepingConsumer.printWhyAreYouKeeping(
- enqueuer.getGraphReporter().getGraphNode(definition.toReference()),
- new PrintStream(baos));
- options.reporter.info(
- new StringDiagnostic(
- "Item " + definition.toSourceString() + " was not discarded.\n" + baos.toString()));
- }
- options.reporter.error(new StringDiagnostic("Discard checks failed."));
+ options.reporter.error(
+ new CheckDiscardDiagnostic.Builder()
+ .addFailedItems(failed, enqueuer.getGraphReporter(), whyAreYouKeepingConsumer)
+ .build());
options.reporter.failIfPendingErrors();
}
diff --git a/src/main/java/com/android/tools/r8/errors/CheckDiscardDiagnostic.java b/src/main/java/com/android/tools/r8/errors/CheckDiscardDiagnostic.java
new file mode 100644
index 0000000..8073696
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/CheckDiscardDiagnostic.java
@@ -0,0 +1,75 @@
+// 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.errors;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.graph.DexDefinition;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import com.android.tools.r8.shaking.GraphReporter;
+import com.android.tools.r8.shaking.WhyAreYouKeepingConsumer;
+import com.google.common.collect.ImmutableList;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.List;
+
+@Keep
+public class CheckDiscardDiagnostic implements Diagnostic {
+
+ private final List<String> messages;
+
+ public static class Builder {
+ private ImmutableList.Builder<String> messagesBuilder = ImmutableList.builder();
+
+ public Builder addFailedItems(
+ List<DexDefinition> failed,
+ GraphReporter graphReporter,
+ WhyAreYouKeepingConsumer whyAreYouKeepingConsumer) {
+ for (DexDefinition definition : failed) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ whyAreYouKeepingConsumer.printWhyAreYouKeeping(
+ graphReporter.getGraphNode(definition.toReference()), new PrintStream(baos));
+ messagesBuilder.add(
+ "Item " + definition.toSourceString() + " was not discarded.\n" + baos.toString());
+ }
+ return this;
+ }
+
+ public CheckDiscardDiagnostic build() {
+ return new CheckDiscardDiagnostic(messagesBuilder.build());
+ }
+ }
+
+ private CheckDiscardDiagnostic(List<String> messages) {
+ this.messages = messages;
+ }
+
+ /** The origin of a -checkdiscarded failure is not unique. (The whole app is to blame.) */
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+
+ /** The position of a -checkdiscarded failure is always unknown. */
+ @Override
+ public Position getPosition() {
+ return Position.UNKNOWN;
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ StringBuilder builder = new StringBuilder("Discard checks failed.");
+ if (messages.size() > 0) {
+ builder.append(System.lineSeparator());
+ builder.append("The following items where not discarded");
+ messages.forEach(
+ message -> {
+ builder.append(System.lineSeparator());
+ builder.append(message);
+ });
+ }
+ return builder.toString();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/checkdiscarded/B162969014.java b/src/test/java/com/android/tools/r8/checkdiscarded/B162969014.java
index 6eb081f..f19d1c1 100644
--- a/src/test/java/com/android/tools/r8/checkdiscarded/B162969014.java
+++ b/src/test/java/com/android/tools/r8/checkdiscarded/B162969014.java
@@ -4,10 +4,11 @@
package com.android.tools.r8.checkdiscarded;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
+import static org.hamcrest.core.StringContains.containsString;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.AssumeNoSideEffects;
@@ -54,10 +55,17 @@
.enableAssumeNoSideEffectsAnnotations()
.enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
- .compile();
+ .compileWithExpectedDiagnostics(
+ diagnostics -> {
+ if (checkLogIsDiscarded) {
+ diagnostics.assertErrorsMatch(
+ diagnosticMessage(containsString("Discard checks failed.")));
+ } else {
+ diagnostics.assertNoErrors();
+ }
+ });
} catch (CompilationFailedException e) {
assertTrue(checkLogIsDiscarded);
- assertEquals(e.getCause().getMessage(), "Discard checks failed.");
return;
}
diff --git a/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardModifyDiagnosticsLevelTest.java b/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardModifyDiagnosticsLevelTest.java
index dd7757c..bac9468 100644
--- a/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardModifyDiagnosticsLevelTest.java
+++ b/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardModifyDiagnosticsLevelTest.java
@@ -18,10 +18,9 @@
import com.android.tools.r8.checkdiscarded.testclasses.Main;
import com.android.tools.r8.checkdiscarded.testclasses.UnusedClass;
import com.android.tools.r8.checkdiscarded.testclasses.UsedClass;
+import com.android.tools.r8.errors.CheckDiscardDiagnostic;
import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.collect.ImmutableList;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.hamcrest.Matcher;
@@ -51,7 +50,11 @@
}
private Matcher<Diagnostic> discardCheckFailedMatcher() {
- return diagnosticMessage(startsWith("Discard checks failed"));
+ return diagnosticMessage(
+ allOf(
+ startsWith("Discard checks failed"),
+ containsString("UsedClass was not discarded"),
+ containsString("is instantiated in")));
}
private Collection<Matcher<Diagnostic>> errorMatchers() {
@@ -67,15 +70,9 @@
}
private Collection<Matcher<Diagnostic>> infoMatchers() {
- List<Matcher<Diagnostic>> matchers = new ArrayList();
- matchers.add(
- allOf(
- diagnosticMessage(containsString("UsedClass was not discarded")),
- diagnosticMessage(containsString("is instantiated in"))));
- if (mappedLevel == DiagnosticsLevel.INFO) {
- matchers.add(discardCheckFailedMatcher());
- }
- return matchers;
+ return mappedLevel == DiagnosticsLevel.INFO
+ ? ImmutableList.of(discardCheckFailedMatcher())
+ : ImmutableList.of();
}
@Test
@@ -88,8 +85,7 @@
.addOptionsModification(this::noInlining)
.setDiagnosticsLevelModifier(
(level, diagnostic) -> {
- if (diagnostic instanceof StringDiagnostic
- && diagnostic.getDiagnosticMessage().equals("Discard checks failed.")) {
+ if (diagnostic instanceof CheckDiscardDiagnostic) {
return mappedLevel;
} else {
return level;
diff --git a/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java b/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java
index df38cb2..61f8917 100644
--- a/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java
+++ b/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java
@@ -23,7 +23,6 @@
import com.android.tools.r8.checkdiscarded.testclasses.WillStay;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.InternalOptions;
-import com.google.common.collect.ImmutableList;
import java.util.List;
import java.util.function.Consumer;
import org.junit.Test;
@@ -90,16 +89,16 @@
Consumer<TestDiagnosticMessages> check =
diagnostics ->
diagnostics
+ .assertNoInfos()
.assertNoWarnings()
- .assertErrorsMatch(diagnosticMessage(containsString("Discard checks failed")))
- .assertInfosMatch(
- ImmutableList.of(
+ .assertErrorsMatch(
+ diagnosticMessage(
allOf(
- diagnosticMessage(containsString("UsedClass was not discarded")),
- diagnosticMessage(containsString("is instantiated in"))),
- allOf(
- diagnosticMessage(containsString("Main was not discarded")),
- diagnosticMessage(containsString("is referenced in keep rule")))));
+ containsString("Discard checks failed"),
+ containsString("UsedClass was not discarded"),
+ containsString("is instantiated in"),
+ containsString("Main was not discarded"),
+ containsString("is referenced in keep rule"))));
compile(WillStay.class, false, check);
}
@@ -113,12 +112,14 @@
Consumer<TestDiagnosticMessages> check =
diagnostics ->
diagnostics
+ .assertNoInfos()
.assertNoWarnings()
- .assertErrorsMatch(diagnosticMessage(containsString("Discard checks failed")))
- .assertInfosMatch(
- allOf(
- diagnosticMessage(containsString("was not discarded")),
- diagnosticMessage(containsString("is invoked from"))));
+ .assertErrorsMatch(
+ diagnosticMessage(
+ allOf(
+ containsString("Discard checks failed"),
+ containsString("was not discarded"),
+ containsString("is invoked from"))));
compile(WillStay.class, true, check);
}