Add test for type annotations on definitions and in signatures
Bug: b/271543766
Change-Id: Ia6c995fbba76bc5c4621ad2e1342d164e2d23f22
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 1523915..704ce16 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -438,6 +438,14 @@
return addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS);
}
+ public T addKeepRuntimeVisibleTypeAnnotations() {
+ return addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_TYPE_ANNOTATIONS);
+ }
+
+ public T addKeepRuntimeInvisibleTypeAnnotations() {
+ return addKeepAttributes(ProguardKeepAttributes.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS);
+ }
+
public T addKeepAllAttributes() {
return addKeepAttributes("*");
}
diff --git a/src/test/java/com/android/tools/r8/annotations/TypeUseAnnotationWithGenericsTest.java b/src/test/java/com/android/tools/r8/annotations/TypeUseAnnotationWithGenericsTest.java
new file mode 100644
index 0000000..b5dbbe9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/annotations/TypeUseAnnotationWithGenericsTest.java
@@ -0,0 +1,120 @@
+// 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 org.junit.Assume.assumeTrue;
+
+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 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 TypeUseAnnotationWithGenericsTest extends TestBase {
+
+ private final String EXPECTED_JVM =
+ StringUtils.joinLines(
+ "printAnnotation - Class: " + typeName(NotNullTestRuntime.class),
+ "printAnnotation - Class: NULL",
+ "printAnnotation - Extends(0): " + typeName(NotNullTestRuntime.class),
+ "printAnnotation - Implements(0): " + typeName(NotNullTestRuntime.class),
+ "printAnnotation - Field: NULL",
+ "printAnnotation - Field: NULL",
+ "printAnnotation - Field(0): " + typeName(NotNullTestRuntime.class),
+ "printAnnotation - Method: NULL",
+ "printAnnotation - Method: NULL",
+ "printAnnotation - MethodReturnType(0): " + typeName(NotNullTestRuntime.class),
+ "printAnnotation - MethodParameter at 0(0): " + typeName(NotNullTestRuntime.class),
+ "printAnnotation - MethodParameter at 1(0): " + typeName(NotNullTestRuntime.class),
+ "printAnnotation - MethodException at 0(0): " + typeName(NotNullTestRuntime.class),
+ "printAnnotation - MethodException at 1(0): " + typeName(NotNullTestRuntime.class),
+ "Hello World!");
+
+ private final String EXPECTED_R8 =
+ StringUtils.joinLines(
+ "printAnnotation - Class: " + typeName(NotNullTestRuntime.class),
+ "printAnnotation - Class: NULL",
+ "printAnnotation - Field: NULL",
+ "printAnnotation - Field: NULL",
+ "printAnnotation - Method: NULL",
+ "printAnnotation - Method: NULL",
+ "Hello World!");
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ testForJvm(parameters)
+ .addProgramClasses(
+ MainWithTypeAndGeneric.class,
+ NotNullTestClass.class,
+ NotNullTestRuntime.class,
+ TestClassWithTypeAndGenericAnnotations.class,
+ SuperInterface.class)
+ .run(parameters.getRuntime(), MainWithTypeAndGeneric.class)
+ .assertSuccessWithOutputLines(EXPECTED_JVM);
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8(parameters.getBackend())
+ .addProgramClasses(
+ MainWithTypeAndGeneric.class,
+ NotNullTestClass.class,
+ NotNullTestRuntime.class,
+ TestClassWithTypeAndGenericAnnotations.class,
+ SuperInterface.class)
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), MainWithTypeAndGeneric.class)
+ .assertFailureWithErrorThatThrows(NoSuchMethodError.class);
+ }
+
+ @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()
+ .addKeepRuntimeVisibleTypeAnnotations()
+ .addKeepRuntimeInvisibleTypeAnnotations()
+ .addKeepAttributeSignature()
+ .addKeepMainRule(MainWithTypeAndGeneric.class)
+ .addKeepClassAndMembersRules(TestClassWithTypeAndGenericAnnotations.class)
+ .applyIf(parameters.isDexRuntime(), b -> b.addDontWarn(AnnotatedType.class))
+ .compile()
+ .inspect(
+ inspector -> {
+ // TODO(b/271543766): Add inspection of annotated types, even runtime invisible.
+ })
+ .run(parameters.getRuntime(), MainWithTypeAndGeneric.class)
+ .assertFailureWithErrorThatThrowsIf(parameters.isDexRuntime(), NoSuchMethodError.class)
+ .assertSuccessWithOutputLinesIf(parameters.isCfRuntime(), EXPECTED_R8);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/annotations/testclasses/MainWithTypeAndGeneric.java b/src/test/java/com/android/tools/r8/annotations/testclasses/MainWithTypeAndGeneric.java
new file mode 100644
index 0000000..b1ac103
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/annotations/testclasses/MainWithTypeAndGeneric.java
@@ -0,0 +1,59 @@
+// 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.testclasses;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedType;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.List;
+
+public class MainWithTypeAndGeneric {
+
+ public static void main(String[] args) throws Exception {
+ Class<TestClassWithTypeAndGenericAnnotations> testClass =
+ TestClassWithTypeAndGenericAnnotations.class;
+ printAnnotation("Class", testClass.getAnnotation(NotNullTestRuntime.class));
+ printAnnotation("Class", testClass.getAnnotation(NotNullTestClass.class));
+ printAnnotatedType("Extends", testClass.getAnnotatedSuperclass());
+ for (AnnotatedType annotatedInterface : testClass.getAnnotatedInterfaces()) {
+ printAnnotatedType("Implements", annotatedInterface);
+ }
+ Field field = testClass.getDeclaredField("field");
+ printAnnotation("Field", field.getAnnotation(NotNullTestRuntime.class));
+ printAnnotation("Field", field.getAnnotation(NotNullTestClass.class));
+ printAnnotatedType("Field", field.getAnnotatedType());
+ Method method = testClass.getDeclaredMethod("method", int.class, List.class, Object.class);
+ printAnnotation("Method", method.getAnnotation(NotNullTestRuntime.class));
+ printAnnotation("Method", method.getAnnotation(NotNullTestClass.class));
+ printAnnotatedType("MethodReturnType", method.getAnnotatedReturnType());
+ for (Annotation[] parameterAnnotation : method.getParameterAnnotations()) {
+ for (Annotation annotation : parameterAnnotation) {
+ printAnnotation("MethodParameter", annotation);
+ }
+ }
+ for (int i = 0; i < method.getAnnotatedParameterTypes().length; i++) {
+ printAnnotatedType("MethodParameter at " + i, method.getAnnotatedParameterTypes()[i]);
+ }
+ for (int i = 0; i < method.getAnnotatedExceptionTypes().length; i++) {
+ printAnnotatedType("MethodException at " + i, method.getAnnotatedExceptionTypes()[i]);
+ }
+ System.out.println("Hello World!");
+ }
+
+ public static void printAnnotation(String name, Annotation annotation) {
+ System.out.println(
+ "printAnnotation - "
+ + name
+ + ": "
+ + (annotation == null ? "NULL" : annotation.annotationType().getName()));
+ }
+
+ public static void printAnnotatedType(String name, AnnotatedType annotatedType) {
+ for (int i = 0; i < annotatedType.getAnnotations().length; i++) {
+ printAnnotation(name + "(" + i + ")", annotatedType.getAnnotations()[i]);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/annotations/testclasses/NotNullTestClass.java b/src/test/java/com/android/tools/r8/annotations/testclasses/NotNullTestClass.java
new file mode 100644
index 0000000..aa5a53d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/annotations/testclasses/NotNullTestClass.java
@@ -0,0 +1,14 @@
+// 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.testclasses;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.TYPE_USE)
+@Retention(RetentionPolicy.CLASS)
+public @interface NotNullTestClass {}
diff --git a/src/test/java/com/android/tools/r8/annotations/testclasses/NotNullTestRuntime.java b/src/test/java/com/android/tools/r8/annotations/testclasses/NotNullTestRuntime.java
new file mode 100644
index 0000000..52c5ced
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/annotations/testclasses/NotNullTestRuntime.java
@@ -0,0 +1,14 @@
+// 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.testclasses;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.TYPE_USE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface NotNullTestRuntime {}
diff --git a/src/test/java/com/android/tools/r8/annotations/testclasses/SuperInterface.java b/src/test/java/com/android/tools/r8/annotations/testclasses/SuperInterface.java
new file mode 100644
index 0000000..61e6ae6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/annotations/testclasses/SuperInterface.java
@@ -0,0 +1,7 @@
+// 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.testclasses;
+
+public interface SuperInterface<T> {}
diff --git a/src/test/java/com/android/tools/r8/annotations/testclasses/TestClassWithTypeAndGenericAnnotations.java b/src/test/java/com/android/tools/r8/annotations/testclasses/TestClassWithTypeAndGenericAnnotations.java
new file mode 100644
index 0000000..fe1853e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/annotations/testclasses/TestClassWithTypeAndGenericAnnotations.java
@@ -0,0 +1,39 @@
+// 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.testclasses;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+@NotNullTestRuntime
+@NotNullTestClass
+public class TestClassWithTypeAndGenericAnnotations<@NotNullTestRuntime @NotNullTestClass T>
+ extends @NotNullTestRuntime @NotNullTestClass Object
+ implements @NotNullTestRuntime @NotNullTestClass SuperInterface<
+ @NotNullTestRuntime @NotNullTestClass T> {
+
+ @NotNullTestRuntime @NotNullTestClass
+ List<@NotNullTestRuntime @NotNullTestClass Object> field = null;
+
+ @NotNullTestRuntime
+ @NotNullTestClass
+ <@NotNullTestRuntime @NotNullTestClass S>
+ List<@NotNullTestRuntime @NotNullTestClass Object> method(
+ @NotNullTestRuntime @NotNullTestClass int foo,
+ @NotNullTestRuntime @NotNullTestClass
+ List<@NotNullTestRuntime @NotNullTestClass String> bar,
+ S s)
+ throws @NotNullTestClass @NotNullTestRuntime RuntimeException,
+ @NotNullTestClass @NotNullTestRuntime IOException {
+ @NotNullTestRuntime
+ @NotNullTestClass
+ Object local = System.currentTimeMillis() > 0 ? new Object() : foo;
+ ArrayList<@NotNullTestRuntime @NotNullTestClass Object> objects = new ArrayList<>();
+ objects.add(foo);
+ this.field = objects;
+ return objects;
+ }
+}