Add test for incorrect pruning with type annotations
Bug: b/271543766
Change-Id: If81d193bfbaf89a4e4643f93ef05b729465ccc25
diff --git a/src/test/java/com/android/tools/r8/annotations/TypeUseAnnotationPruneTest.java b/src/test/java/com/android/tools/r8/annotations/TypeUseAnnotationPruneTest.java
new file mode 100644
index 0000000..e9fcba0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/annotations/TypeUseAnnotationPruneTest.java
@@ -0,0 +1,122 @@
+// Copyright (c) 2023, 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.annotations;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.annotations.testclasses.MainWithTypeAndGeneric;
+import com.android.tools.r8.annotations.testclasses.NotNullTestClass;
+import com.android.tools.r8.annotations.testclasses.NotNullTestRuntime;
+import com.android.tools.r8.annotations.testclasses.SuperInterface;
+import com.android.tools.r8.annotations.testclasses.TestClassWithTypeAndGenericAnnotations;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
+import com.android.tools.r8.utils.codeinspector.FoundAnnotationSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.lang.reflect.AnnotatedType;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TypeUseAnnotationPruneTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ private String getExpected(String notNullTestRuntimeTypeName) {
+ return StringUtils.joinLines(
+ "printAnnotation - Class: " + notNullTestRuntimeTypeName,
+ "printAnnotation - Class: NULL",
+ "printAnnotation - Extends(0): " + notNullTestRuntimeTypeName,
+ "printAnnotation - Implements(0): " + notNullTestRuntimeTypeName,
+ "printAnnotation - Field: NULL",
+ "printAnnotation - Field: NULL",
+ "printAnnotation - Field(0): " + notNullTestRuntimeTypeName,
+ "printAnnotation - Method: NULL",
+ "printAnnotation - Method: NULL",
+ "printAnnotation - MethodReturnType(0): " + notNullTestRuntimeTypeName,
+ "printAnnotation - MethodParameter at 0(0): " + notNullTestRuntimeTypeName,
+ "printAnnotation - MethodParameter at 1(0): " + notNullTestRuntimeTypeName,
+ "printAnnotation - MethodException at 0(0): " + notNullTestRuntimeTypeName,
+ "printAnnotation - MethodException at 1(0): " + notNullTestRuntimeTypeName,
+ "Hello World!");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(
+ MainWithTypeAndGeneric.class,
+ NotNullTestClass.class,
+ NotNullTestRuntime.class,
+ TestClassWithTypeAndGenericAnnotations.class,
+ SuperInterface.class)
+ .setMinApi(parameters)
+ .addKeepClassRules(NotNullTestClass.class, NotNullTestRuntime.class, SuperInterface.class)
+ .addKeepRuntimeVisibleAnnotations()
+ .addKeepRuntimeInvisibleAnnotations()
+ .addKeepRuntimeVisibleParameterAnnotations()
+ .addKeepRuntimeInvisibleParameterAnnotations()
+ .addKeepAttributeSignature()
+ .addKeepAttributeExceptions()
+ .addKeepMainRule(MainWithTypeAndGeneric.class)
+ .addKeepClassAndMembersRules(TestClassWithTypeAndGenericAnnotations.class)
+ .applyIf(parameters.isDexRuntime(), b -> b.addDontWarn(AnnotatedType.class))
+ .compile()
+ .inspectWithOptions(
+ inspector -> {
+ ClassSubject notNullTestClass = inspector.clazz(NotNullTestClass.class);
+ assertThat(notNullTestClass, isPresent());
+ ClassSubject notNullTestRuntime = inspector.clazz(NotNullTestRuntime.class);
+ assertThat(notNullTestRuntime, isPresent());
+ ClassSubject clazz = inspector.clazz(TestClassWithTypeAndGenericAnnotations.class);
+ assertThat(clazz, isPresent());
+ if (parameters.isDexRuntime()) {
+ assertTrue(
+ clazz.annotations().stream()
+ .noneMatch(FoundAnnotationSubject::isTypeAnnotation));
+ } else {
+ // TODO(b/271543766): Assert that we have no type annotations
+ }
+ FieldSubject field = clazz.uniqueFieldWithOriginalName("field");
+ assertThat(field, isPresent());
+ if (parameters.isDexRuntime()) {
+ assertEquals(0, field.annotations().size());
+ } else {
+ // TODO(b/271543766): Assert that we have no annotations
+ assertEquals(4, field.annotations().size());
+ }
+ MethodSubject method = clazz.uniqueMethodWithOriginalName("method");
+ assertThat(method, isPresent());
+ // We create a dex annotation for the checked exception.
+ if (parameters.isDexRuntime()) {
+ assertEquals(1, method.annotations().size());
+ } else {
+ // TODO(b/271543766): Assert that we have no annotations
+ assertEquals(17, method.annotations().size());
+ }
+ },
+ options -> options.programConsumer = ClassFileConsumer.emptyConsumer())
+ .run(parameters.getRuntime(), MainWithTypeAndGeneric.class)
+ .assertFailureWithErrorThatThrowsIf(parameters.isDexRuntime(), NoSuchMethodError.class)
+ .assertSuccessWithOutputLinesIf(
+ parameters.isCfRuntime(), getExpected(typeName(NotNullTestRuntime.class)));
+ }
+}