[KeepAnno] Task to create javadoc for keep annotations

Bug: b/248408342
Change-Id: I83ab88e9ae6d384051fa59c81c0efaaa590c273c
diff --git a/d8_r8/keepanno/build.gradle.kts b/d8_r8/keepanno/build.gradle.kts
index 376472a..3d981f6 100644
--- a/d8_r8/keepanno/build.gradle.kts
+++ b/d8_r8/keepanno/build.gradle.kts
@@ -34,4 +34,9 @@
     dependsOn(gradle.includedBuild("shared").task(":downloadDeps"))
     from(sourceSets.main.get().output)
   }
+
+  val keepAnnoAnnotationsDoc by registering(Javadoc::class) {
+    source = sourceSets.main.get().allJava
+    include("com/android/tools/r8/keepanno/annotations/*")
+  }
 }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsesReflection.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsesReflection.java
index e9edfaa..c7f0e87 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsesReflection.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsesReflection.java
@@ -24,14 +24,15 @@
  * <p>The annotation's 'additionalPreconditions' is optional and can specify additional conditions
  * that should be satisfied for the annotation to be in effect.
  *
- * <p>The translation of the @UsesReflection annotation into a @KeepEdge is as follows:
+ * <p>The translation of the {@link UsesReflection} annotation into a {@link KeepEdge} is as
+ * follows:
  *
  * <p>Assume the item of the annotation is denoted by 'CTX' and referred to as its context.
  *
  * <pre>
- * @UsesReflection(value = targets, [additionalPreconditions = preconditions])
- * ==>
- * @KeepEdge(
+ * UsesReflection(value = targets, [additionalPreconditions = preconditions])
+ * ===
+ * KeepEdge(
  *   consequences = targets,
  *   preconditions = {createConditionFromContext(CTX)} + preconditions
  * )
diff --git a/src/test/java/com/android/tools/r8/keepanno/doctests/UsesReflectionDocumentationTest.java b/src/test/java/com/android/tools/r8/keepanno/doctests/UsesReflectionDocumentationTest.java
new file mode 100644
index 0000000..0914396
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/doctests/UsesReflectionDocumentationTest.java
@@ -0,0 +1,111 @@
+// 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.keepanno.doctests;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
+import com.android.tools.r8.keepanno.annotations.UsesReflection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class UsesReflectionDocumentationTest extends TestBase {
+
+  static final String EXPECTED = StringUtils.lines("on Base", "on Sub");
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build();
+  }
+
+  public UsesReflectionDocumentationTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testReference() throws Exception {
+    testForRuntime(parameters)
+        .addProgramClasses(getInputClasses())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  @Test
+  public void testWithRuleExtraction() throws Exception {
+    testForR8(parameters.getBackend())
+        .enableExperimentalKeepAnnotations()
+        .addProgramClasses(getInputClasses())
+        .addKeepMainRule(TestClass.class)
+        .setMinApi(parameters)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  public List<Class<?>> getInputClasses() {
+    return ImmutableList.of(TestClass.class, BaseClass.class, SubClass.class, MyClass.class);
+  }
+
+  static class BaseClass {
+    void hiddenMethod() {
+      System.out.println("on Base");
+    }
+  }
+
+  static class SubClass extends BaseClass {
+    void hiddenMethod() {
+      System.out.println("on Sub");
+    }
+  }
+
+  /// DOC START: UsesReflection on virtual method
+  /* DOC TEXT START:
+  <p>If for example, your program is reflectively invoking a virtual method on some base class, you
+  should annotate the method that is performing the reflection with an annotation describing what
+  assumptions the reflective code is making.
+
+  <p>In the following example, the method `foo` is looking up the method with the name
+  `hiddenMethod` on objects that are instances of `BaseClass`. It is then invoking the method with
+   no other arguments than the receiver.
+
+  <p>The minimal requirement for this code to work is therefore that all methods with the name
+  `hiddenMethod` and the empty list of parameters are targeted if they are objects that are
+  instances of the class `BaseClass` or subclasses thereof.
+
+  <p>By placing the `UsesReflection` annotation on the method `foo` the annotation is only in
+  effect if the method `foo` is determined to be used by the shrinker.
+  So, if `foo` turns out to be dead code then the shrinker can remove `foo` and also ignore the
+  keep annotation.
+  DOC TEXT END */
+  static class MyClass {
+
+    @UsesReflection({
+      @KeepTarget(
+          instanceOfClassConstant = BaseClass.class,
+          methodName = "hiddenMethod",
+          methodParameters = {})
+    })
+    public void foo(BaseClass base) throws Exception {
+      base.getClass().getDeclaredMethod("hiddenMethod").invoke(base);
+    }
+  }
+
+  // DOC END
+
+  static class TestClass {
+
+    public static void main(String[] args) throws Exception {
+      new MyClass().foo(new BaseClass());
+      new MyClass().foo(new SubClass());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java b/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java
index 506beb3..2c31a25 100644
--- a/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java
+++ b/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java
@@ -784,7 +784,7 @@
                     KeepItemKind.ONLY_CLASS.name(),
                     "if annotating a member. Also, it is never valid to use kind",
                     KeepItemKind.ONLY_MEMBERS.name(),
-                    "as the API surface must keep the class if any member it to be accessible.")
+                    "as the API surface must keep the class if any member is to be accessible.")
                 .generate(this);
             println();
             generateMemberPropertiesNoBinding();
@@ -810,14 +810,18 @@
               "The annotation's 'additionalPreconditions' is optional and can specify additional"
                   + " conditions that should be satisfied for the annotation to be in effect.")
           .addParagraph(
-              "The translation of the @UsesReflection annotation into a @KeepEdge is as follows:")
+              "The translation of the "
+                  + docLink(UsesReflection.class)
+                  + " annotation into a "
+                  + docLink(KeepEdge.class)
+                  + " is as follows:")
           .addParagraph(
               "Assume the item of the annotation is denoted by 'CTX' and referred to as its"
                   + " context.")
           .addCodeBlock(
-              "@UsesReflection(value = targets, [additionalPreconditions = preconditions])",
-              "==>",
-              "@KeepEdge(",
+              "UsesReflection(value = targets, [additionalPreconditions = preconditions])",
+              "===",
+              "KeepEdge(",
               "  consequences = targets,",
               "  preconditions = {createConditionFromContext(CTX)} + preconditions",
               ")",
@@ -868,10 +872,14 @@
           .setDocTitle("Annotation to mark a class, field or method as being " + doc + ".")
           .addParagraph(
               "Note: Before using this annotation, consider if instead you can annotate the code"
-                  + " that is doing reflection with {@link UsesReflection}. Annotating the"
+                  + " that is doing reflection with "
+                  + docLink(UsesReflection.class)
+                  + ". Annotating the"
                   + " reflecting code is generally more clear and maintainable, and it also"
                   + " naturally gives rise to edges that describe just the reflected aspects of the"
-                  + " program. The {@link UsedByReflection} annotation is suitable for cases where"
+                  + " program. The "
+                  + docLink(UsedByReflection.class)
+                  + " annotation is suitable for cases where"
                   + " the reflecting code is not under user control, or in migrating away from"
                   + " rules.")
           .addParagraph(
@@ -922,6 +930,10 @@
       println("}");
     }
 
+    private String docLink(Class<?> clazz) {
+      return "{@link " + simpleName(clazz) + "}";
+    }
+
     private String docLink(KeepItemKind kind) {
       return "{@link KeepItemKind#" + kind.name() + "}";
     }