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))); + } +}