Reland "Tests and fixes for missing classes reported from annotations"
This reverts commit 42636872591d239eed3d292dd176fc0370a4f80f.
Change-Id: Id6d4b58f5e8d642b9db9defc32c31fa6f6769c5b
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 6cddfff..ac03c78 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -368,7 +368,8 @@
* A map from annotation classes to annotations that need to be processed should the classes ever
* become live.
*/
- private final Map<DexType, Set<DexAnnotation>> deferredAnnotations = new IdentityHashMap<>();
+ private final Map<DexType, Map<DexAnnotation, ProgramDefinition>> deferredAnnotations =
+ new IdentityHashMap<>();
/** Map of active if rules to speed up aapt2 generated keep rules. */
private Map<Wrapper<ProguardIfRule>, Set<ProguardIfRule>> activeIfRules;
@@ -1820,10 +1821,13 @@
// If this type has deferred annotations, we have to process those now, too.
if (clazz.isAnnotation()) {
- Set<DexAnnotation> annotations = deferredAnnotations.remove(clazz.type);
- if (annotations != null && !annotations.isEmpty()) {
- assert annotations.stream().allMatch(a -> a.annotation.type == clazz.type);
- annotations.forEach(annotation -> processAnnotation(clazz, annotation));
+ Map<DexAnnotation, ProgramDefinition> annotations =
+ deferredAnnotations.remove(clazz.getType());
+ if (annotations != null) {
+ assert annotations.keySet().stream()
+ .allMatch(a -> a.getAnnotationType() == clazz.getType());
+ annotations.forEach(
+ (annotation, annotatedItem) -> processAnnotation(annotatedItem, annotation));
}
}
@@ -1932,14 +1936,16 @@
private void processAnnotation(ProgramDefinition annotatedItem, DexAnnotation annotation) {
DexType type = annotation.getAnnotationType();
- recordTypeReference(type, annotatedItem);
- DexClass clazz = appView.definitionFor(type);
+ DexClass clazz = definitionFor(type, annotatedItem);
boolean annotationTypeIsLibraryClass = clazz == null || clazz.isNotProgramClass();
boolean isLive = annotationTypeIsLibraryClass || liveTypes.contains(clazz.asProgramClass());
if (!shouldKeepAnnotation(appView, annotatedItem.getDefinition(), annotation, isLive)) {
// Remember this annotation for later.
if (!annotationTypeIsLibraryClass) {
- deferredAnnotations.computeIfAbsent(type, ignore -> new HashSet<>()).add(annotation);
+ Map<DexAnnotation, ProgramDefinition> deferredAnnotationsForAnnotationType =
+ deferredAnnotations.computeIfAbsent(type, ignore -> new IdentityHashMap<>());
+ assert !deferredAnnotationsForAnnotationType.containsKey(annotation);
+ deferredAnnotationsForAnnotationType.put(annotation, annotatedItem);
}
return;
}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromClassAnnotationTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromClassAnnotationTest.java
new file mode 100644
index 0000000..0af31a3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromClassAnnotationTest.java
@@ -0,0 +1,62 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import org.junit.Test;
+
+// TODO(b/179456539): This test should fail without -keepattributes RuntimeVisibleAnnotations, but
+// we retain missing annotations even if there is no -keepattributes *Annotations*.
+public class MissingClassReferencedFromClassAnnotationTest extends MissingClassesTestBase {
+
+ private static final ClassReference referencedFrom = Reference.classFromClass(Main.class);
+
+ public MissingClassReferencedFromClassAnnotationTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom));
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(Main.class));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ TestDiagnosticMessages::assertNoMessages,
+ addDontWarn(MissingRuntimeAnnotation.class));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings());
+ }
+
+ @Override
+ ClassReference getMissingClassReference() {
+ return Reference.classFromClass(MissingRuntimeAnnotation.class);
+ }
+
+ @MissingRuntimeAnnotation
+ static class Main {
+
+ public static void main(String[] args) {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromClassAnnotationWithDataTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromClassAnnotationWithDataTest.java
new file mode 100644
index 0000000..0918d76
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromClassAnnotationWithDataTest.java
@@ -0,0 +1,76 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ThrowableConsumer;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import org.junit.Test;
+
+public class MissingClassReferencedFromClassAnnotationWithDataTest extends MissingClassesTestBase {
+
+ private static final ClassReference referencedFrom = Reference.classFromClass(Main.class);
+
+ public MissingClassReferencedFromClassAnnotationWithDataTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom),
+ addRuntimeAnnotation());
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ TestDiagnosticMessages::assertNoMessages,
+ addRuntimeAnnotation().andThen(addDontWarn(Main.class)));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ TestDiagnosticMessages::assertNoMessages,
+ addRuntimeAnnotation().andThen(addDontWarn(MissingClass.class)));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addRuntimeAnnotation().andThen(addIgnoreWarnings()));
+ }
+
+ private ThrowableConsumer<R8FullTestBuilder> addRuntimeAnnotation() {
+ return builder ->
+ builder
+ .addProgramClasses(RuntimeAnnotation.class)
+ .addKeepClassRules(RuntimeAnnotation.class)
+ .addKeepRuntimeVisibleAnnotations();
+ }
+
+ @RuntimeAnnotation(data = MissingClass.class)
+ static class Main {
+
+ public static void main(String[] args) {}
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface RuntimeAnnotation {
+ Class<?> data();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromFieldAnnotationTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromFieldAnnotationTest.java
new file mode 100644
index 0000000..7788831
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromFieldAnnotationTest.java
@@ -0,0 +1,67 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.Reference;
+import org.junit.Test;
+
+// TODO(b/179456539): This test should fail without -keepattributes RuntimeVisibleAnnotations, but
+// we retain missing annotations even if there is no -keepattributes *Annotations*.
+public class MissingClassReferencedFromFieldAnnotationTest extends MissingClassesTestBase {
+
+ private static final FieldReference referencedFrom =
+ Reference.field(Reference.classFromClass(Main.class), "FIELD", Reference.INT);
+
+ public MissingClassReferencedFromFieldAnnotationTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom));
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(Main.class));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ TestDiagnosticMessages::assertNoMessages,
+ addDontWarn(MissingRuntimeAnnotation.class));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings());
+ }
+
+ @Override
+ ClassReference getMissingClassReference() {
+ return Reference.classFromClass(MissingRuntimeAnnotation.class);
+ }
+
+ static class Main {
+
+ @MissingRuntimeAnnotation static int FIELD;
+
+ public static void main(String[] args) {
+ int ignore = FIELD;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromMethodAnnotationTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromMethodAnnotationTest.java
new file mode 100644
index 0000000..189b49e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromMethodAnnotationTest.java
@@ -0,0 +1,65 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import org.junit.Test;
+
+// TODO(b/179456539): This test should fail without -keepattributes RuntimeVisibleAnnotations, but
+// we retain missing annotations even if there is no -keepattributes *Annotations*.
+public class MissingClassReferencedFromMethodAnnotationTest extends MissingClassesTestBase {
+
+ private static final MethodReference referencedFrom =
+ MethodReferenceUtils.mainMethod(Reference.classFromClass(Main.class));
+
+ public MissingClassReferencedFromMethodAnnotationTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom));
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(Main.class));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ TestDiagnosticMessages::assertNoMessages,
+ addDontWarn(MissingRuntimeAnnotation.class));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings());
+ }
+
+ @Override
+ ClassReference getMissingClassReference() {
+ return Reference.classFromClass(MissingRuntimeAnnotation.class);
+ }
+
+ static class Main {
+
+ @MissingRuntimeAnnotation
+ public static void main(String[] args) {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromParameterAnnotationTest.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromParameterAnnotationTest.java
new file mode 100644
index 0000000..0017aa7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassReferencedFromParameterAnnotationTest.java
@@ -0,0 +1,64 @@
+// 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.missingclasses;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import org.junit.Test;
+
+// TODO(b/179456539): This test should fail without -keepattributes RuntimeVisibleAnnotations, but
+// we retain missing annotations even if there is no -keepattributes *Annotations*.
+public class MissingClassReferencedFromParameterAnnotationTest extends MissingClassesTestBase {
+
+ private static final MethodReference referencedFrom =
+ MethodReferenceUtils.mainMethod(Reference.classFromClass(Main.class));
+
+ public MissingClassReferencedFromParameterAnnotationTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testNoRules() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, diagnostics -> inspectDiagnosticsWithNoRules(diagnostics, referencedFrom));
+ }
+
+ @Test
+ public void testDontWarnMainClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class, TestDiagnosticMessages::assertNoMessages, addDontWarn(Main.class));
+ }
+
+ @Test
+ public void testDontWarnMissingClass() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ TestDiagnosticMessages::assertNoMessages,
+ addDontWarn(MissingRuntimeAnnotation.class));
+ }
+
+ @Test
+ public void testIgnoreWarnings() throws Exception {
+ compileWithExpectedDiagnostics(
+ Main.class,
+ diagnostics -> inspectDiagnosticsWithIgnoreWarnings(diagnostics, referencedFrom),
+ addIgnoreWarnings());
+ }
+
+ @Override
+ ClassReference getMissingClassReference() {
+ return Reference.classFromClass(MissingRuntimeAnnotation.class);
+ }
+
+ static class Main {
+
+ public static void main(@MissingRuntimeAnnotation String[] args) {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassesTestBase.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassesTestBase.java
index dafb7b4..c08622c 100644
--- a/src/test/java/com/android/tools/r8/missingclasses/MissingClassesTestBase.java
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassesTestBase.java
@@ -23,6 +23,8 @@
import com.android.tools.r8.utils.InternalOptions.TestingOptions;
import com.android.tools.r8.utils.MethodReferenceUtils;
import com.google.common.collect.ImmutableSet;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.function.Function;
import org.junit.runner.RunWith;
@@ -39,6 +41,9 @@
int field;
}
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface MissingRuntimeAnnotation {}
+
interface MissingInterface {}
private final TestParameters parameters;