Refactor @CovariantReturnType logic to own package

Change-Id: I998beb87a7c811c4477f29de2c1e62816da06e7f
diff --git a/src/main/java/com/android/tools/r8/desugar/covariantreturntype/CovariantReturnTypeAnnotationTransformer.java b/src/main/java/com/android/tools/r8/desugar/covariantreturntype/CovariantReturnTypeAnnotationTransformer.java
index 6bc1e23..47357cd 100644
--- a/src/main/java/com/android/tools/r8/desugar/covariantreturntype/CovariantReturnTypeAnnotationTransformer.java
+++ b/src/main/java/com/android/tools/r8/desugar/covariantreturntype/CovariantReturnTypeAnnotationTransformer.java
@@ -76,24 +76,31 @@
     this.references = new CovariantReturnTypeReferences(factory);
   }
 
+  public CovariantReturnTypeReferences getReferences() {
+    return references;
+  }
+
   public static void runIfNecessary(
       AppView<?> appView,
       IRConverter converter,
       CovariantReturnTypeAnnotationTransformerEventConsumer eventConsumer,
       ExecutorService executorService)
       throws ExecutionException {
+    if (shouldRun(appView)) {
+      new CovariantReturnTypeAnnotationTransformer(appView, converter)
+          .run(eventConsumer, executorService);
+    }
+  }
+
+  public static boolean shouldRun(AppView<?> appView) {
     if (!appView.options().processCovariantReturnTypeAnnotations) {
-      return;
+      return false;
     }
     assert !appView.options().isDesugaredLibraryCompilation();
     DexItemFactory factory = appView.dexItemFactory();
     DexString covariantReturnTypeDescriptor =
         factory.createString(CovariantReturnTypeReferences.COVARIANT_RETURN_TYPE_DESCRIPTOR);
-    if (factory.lookupType(covariantReturnTypeDescriptor) == null) {
-      return;
-    }
-    new CovariantReturnTypeAnnotationTransformer(appView, converter)
-        .run(eventConsumer, executorService);
+    return factory.lookupType(covariantReturnTypeDescriptor) != null;
   }
 
   private void run(
@@ -234,14 +241,6 @@
     return covariantReturnTypes;
   }
 
-  public boolean hasCovariantReturnTypeAnnotation(ProgramMethod method) {
-    return method
-        .getAnnotations()
-        .hasAnnotation(
-            annotation ->
-                references.isOneOfCovariantReturnTypeAnnotations(annotation.getAnnotationType()));
-  }
-
   private void getCovariantReturnTypesFromAnnotation(
       ProgramMethod method, DexEncodedAnnotation annotation, Set<DexType> covariantReturnTypes) {
     boolean hasPresentAfterElement = false;
diff --git a/src/main/java/com/android/tools/r8/desugar/covariantreturntype/CovariantReturnTypeEnqueuerExtension.java b/src/main/java/com/android/tools/r8/desugar/covariantreturntype/CovariantReturnTypeEnqueuerExtension.java
new file mode 100644
index 0000000..4e8573f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/desugar/covariantreturntype/CovariantReturnTypeEnqueuerExtension.java
@@ -0,0 +1,116 @@
+// Copyright (c) 2024, 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.desugar.covariantreturntype;
+
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.errors.NonKeptMethodWithCovariantReturnTypeAnnotationDiagnostic;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramDefinition;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.analysis.EnqueuerAnalysis;
+import com.android.tools.r8.shaking.Enqueuer;
+import com.android.tools.r8.shaking.EnqueuerWorklist;
+import com.android.tools.r8.shaking.KeepInfo;
+import com.android.tools.r8.shaking.KeepMethodInfo;
+import com.android.tools.r8.shaking.MinimumKeepInfoCollection;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.collections.ProgramMethodMap;
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+public class CovariantReturnTypeEnqueuerExtension extends EnqueuerAnalysis {
+
+  private final AppView<? extends AppInfoWithClassHierarchy> appView;
+  private final CovariantReturnTypeAnnotationTransformer transformer;
+
+  private final Map<DexProgramClass, List<ProgramMethod>> pendingCovariantReturnTypeDesugaring =
+      new IdentityHashMap<>();
+
+  public CovariantReturnTypeEnqueuerExtension(
+      AppView<? extends AppInfoWithClassHierarchy> appView) {
+    this.appView = appView;
+    this.transformer = new CovariantReturnTypeAnnotationTransformer(appView);
+  }
+
+  public static void register(
+      AppView<? extends AppInfoWithClassHierarchy> appView, Enqueuer enqueuer) {
+    if (enqueuer.getMode().isInitialTreeShaking()
+        && CovariantReturnTypeAnnotationTransformer.shouldRun(appView)) {
+      enqueuer.registerAnalysis(new CovariantReturnTypeEnqueuerExtension(appView));
+    }
+  }
+
+  @Override
+  public void processNewlyLiveMethod(
+      ProgramMethod method,
+      ProgramDefinition context,
+      Enqueuer enqueuer,
+      EnqueuerWorklist worklist) {
+    if (hasCovariantReturnTypeAnnotation(method)) {
+      pendingCovariantReturnTypeDesugaring
+          .computeIfAbsent(method.getHolder(), ignoreKey(ArrayList::new))
+          .add(method);
+    }
+  }
+
+  private boolean hasCovariantReturnTypeAnnotation(ProgramMethod method) {
+    CovariantReturnTypeReferences references = transformer.getReferences();
+    DexAnnotationSet annotations = method.getAnnotations();
+    return annotations.hasAnnotation(
+        annotation ->
+            references.isOneOfCovariantReturnTypeAnnotations(annotation.getAnnotationType()));
+  }
+
+  @Override
+  public void notifyFixpoint(
+      Enqueuer enqueuer, EnqueuerWorklist worklist, ExecutorService executorService, Timing timing)
+      throws ExecutionException {
+    if (pendingCovariantReturnTypeDesugaring.isEmpty()) {
+      return;
+    }
+    ProgramMethodMap<Diagnostic> errors = ProgramMethodMap.createConcurrent();
+    transformer.processMethods(
+        pendingCovariantReturnTypeDesugaring,
+        (bridge, target) -> {
+          KeepMethodInfo.Joiner bridgeKeepInfo =
+              getKeepInfoForCovariantReturnTypeBridge(target, errors);
+          enqueuer.getKeepInfo().registerCompilerSynthesizedMethod(bridge);
+          enqueuer.applyMinimumKeepInfoWhenLiveOrTargeted(bridge, bridgeKeepInfo);
+          enqueuer.getProfileCollectionAdditions().addMethodIfContextIsInProfile(bridge, target);
+        },
+        executorService);
+    errors.forEachValue(appView.reporter()::error);
+    pendingCovariantReturnTypeDesugaring.clear();
+  }
+
+  private KeepMethodInfo.Joiner getKeepInfoForCovariantReturnTypeBridge(
+      ProgramMethod target, ProgramMethodMap<Diagnostic> errors) {
+    KeepInfo.Joiner<?, ?, ?> targetKeepInfo =
+        appView
+            .rootSet()
+            .getDependentMinimumKeepInfo()
+            .getUnconditionalMinimumKeepInfoOrDefault(MinimumKeepInfoCollection.empty())
+            .getOrDefault(target.getReference(), null);
+    if (targetKeepInfo == null) {
+      targetKeepInfo = KeepMethodInfo.newEmptyJoiner();
+    }
+    InternalOptions options = appView.options();
+    if ((options.isMinifying() && targetKeepInfo.isMinificationAllowed())
+        || (options.isOptimizing() && targetKeepInfo.isOptimizationAllowed())
+        || (options.isShrinking() && targetKeepInfo.isShrinkingAllowed())) {
+      errors.computeIfAbsent(target, NonKeptMethodWithCovariantReturnTypeAnnotationDiagnostic::new);
+    }
+    return targetKeepInfo.asMethodJoiner();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
index d587b61..791b3b4 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
@@ -15,6 +15,8 @@
 import com.android.tools.r8.shaking.Enqueuer;
 import com.android.tools.r8.shaking.EnqueuerWorklist;
 import com.android.tools.r8.utils.Timing;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
 
 public abstract class EnqueuerAnalysis {
 
@@ -57,7 +59,9 @@
    * Called when the Enqueuer reaches a fixpoint. This may happen multiple times, since each
    * analysis may enqueue items into the worklist upon the fixpoint using {@param worklist}.
    */
-  public void notifyFixpoint(Enqueuer enqueuer, EnqueuerWorklist worklist, Timing timing) {}
+  public void notifyFixpoint(
+      Enqueuer enqueuer, EnqueuerWorklist worklist, ExecutorService executorService, Timing timing)
+      throws ExecutionException {}
 
   /**
    * Called when the Enqueuer has reached the final fixpoint. Each analysis may use this callback to
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
index 04c491e..f3299a3 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
@@ -135,7 +135,11 @@
     return new EnqueuerAnalysis() {
       @Override
       @SuppressWarnings("ReferenceEquality")
-      public void notifyFixpoint(Enqueuer enqueuer, EnqueuerWorklist worklist, Timing timing) {
+      public void notifyFixpoint(
+          Enqueuer enqueuer,
+          EnqueuerWorklist worklist,
+          ExecutorService executorService,
+          Timing timing) {
         builders.forEach(
             (builder, dynamicMethod) -> {
               if (seen.add(builder)) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
index c9a6021..0ce0317 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
@@ -54,6 +54,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ExecutorService;
 import java.util.function.Predicate;
 
 // TODO(b/112437944): Handle cycles in the graph + add a test that fails with the current
@@ -179,7 +180,11 @@
   }
 
   @Override
-  public void notifyFixpoint(Enqueuer enqueuer, EnqueuerWorklist worklist, Timing timing) {
+  public void notifyFixpoint(
+      Enqueuer enqueuer,
+      EnqueuerWorklist worklist,
+      ExecutorService executorService,
+      Timing timing) {
     timing.begin("[Proto] Extend fixpoint");
     populateExtensionGraph(enqueuer);
 
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 8850e03..1781300 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -22,11 +22,10 @@
 import com.android.tools.r8.cf.code.CfInvoke;
 import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
 import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
-import com.android.tools.r8.desugar.covariantreturntype.CovariantReturnTypeAnnotationTransformer;
+import com.android.tools.r8.desugar.covariantreturntype.CovariantReturnTypeEnqueuerExtension;
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.code.CfOrDexInstruction;
 import com.android.tools.r8.errors.InterfaceDesugarMissingTypeDiagnostic;
-import com.android.tools.r8.errors.NonKeptMethodWithCovariantReturnTypeAnnotationDiagnostic;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
 import com.android.tools.r8.features.IsolatedFeatureSplitsChecker;
@@ -480,10 +479,6 @@
   private final CfInstructionDesugaringCollection desugaring;
   private final ProgramMethodSet pendingCodeDesugaring = ProgramMethodSet.create();
 
-  private final CovariantReturnTypeAnnotationTransformer covariantReturnTypeAnnotationTransformer;
-  private final Map<DexProgramClass, List<ProgramMethod>> pendingCovariantReturnTypeDesugaring =
-      new IdentityHashMap<>();
-
   // Collections for tracing progress on interface method desugaring.
 
   // The pending method move set is all the methods that need to be moved to companions.
@@ -543,6 +538,7 @@
           shrinker -> registerAnalysis(shrinker.createEnqueuerAnalysis()));
       IsolatedFeatureSplitsChecker.register(appView, this);
       ResourceAccessAnalysis.register(appView, this);
+      CovariantReturnTypeEnqueuerExtension.register(appView, this);
     }
 
     targetedMethods = new LiveMethodsSet(graphReporter::registerMethod);
@@ -557,12 +553,9 @@
     if (mode.isInitialTreeShaking()) {
       desugaring = CfInstructionDesugaringCollection.create(appView, appView.apiLevelCompute());
       interfaceProcessor = InterfaceProcessor.create(appView);
-      covariantReturnTypeAnnotationTransformer =
-          new CovariantReturnTypeAnnotationTransformer(appView);
     } else {
       desugaring = CfInstructionDesugaringCollection.empty();
       interfaceProcessor = null;
-      covariantReturnTypeAnnotationTransformer = null;
     }
 
     objectAllocationInfoCollection =
@@ -4312,7 +4305,6 @@
     // registered first and no dependencies may exist among them.
     SyntheticAdditions additions = new SyntheticAdditions(appView.createProcessorContext());
     desugar(additions);
-    processCovariantReturnTypeAnnotations();
     synthesizeInterfaceMethodBridges();
     if (additions.isEmpty()) {
       return;
@@ -4335,11 +4327,6 @@
   }
 
   private boolean addToPendingDesugaring(ProgramMethod method) {
-    if (covariantReturnTypeAnnotationTransformer.hasCovariantReturnTypeAnnotation(method)) {
-      pendingCovariantReturnTypeDesugaring
-          .computeIfAbsent(method.getHolder(), ignoreKey(ArrayList::new))
-          .add(method);
-    }
     if (options.isInterfaceMethodDesugaringEnabled()) {
       if (mustMoveToInterfaceCompanionMethod(method)) {
         // TODO(b/199043500): Once "live moved methods" are tracked this can avoid the code check.
@@ -4474,44 +4461,6 @@
     }
   }
 
-  private void processCovariantReturnTypeAnnotations() throws ExecutionException {
-    if (pendingCovariantReturnTypeDesugaring.isEmpty()) {
-      return;
-    }
-    ProgramMethodMap<Diagnostic> errors = ProgramMethodMap.createConcurrent();
-    covariantReturnTypeAnnotationTransformer.processMethods(
-        pendingCovariantReturnTypeDesugaring,
-        (bridge, target) -> {
-          KeepMethodInfo.Joiner bridgeKeepInfo =
-              getKeepInfoForCovariantReturnTypeBridge(target, errors);
-          keepInfo.registerCompilerSynthesizedMethod(bridge);
-          applyMinimumKeepInfoWhenLiveOrTargeted(bridge, bridgeKeepInfo);
-          profileCollectionAdditions.addMethodIfContextIsInProfile(bridge, target);
-        },
-        executorService);
-    errors.forEachValue(appView.reporter()::error);
-    pendingCovariantReturnTypeDesugaring.clear();
-  }
-
-  private KeepMethodInfo.Joiner getKeepInfoForCovariantReturnTypeBridge(
-      ProgramMethod target, ProgramMethodMap<Diagnostic> errors) {
-    KeepInfo.Joiner<?, ?, ?> targetKeepInfo =
-        appView
-            .rootSet()
-            .getDependentMinimumKeepInfo()
-            .getUnconditionalMinimumKeepInfoOrDefault(MinimumKeepInfoCollection.empty())
-            .getOrDefault(target.getReference(), null);
-    if (targetKeepInfo == null) {
-      targetKeepInfo = KeepMethodInfo.newEmptyJoiner();
-    }
-    if ((options.isMinifying() && targetKeepInfo.isMinificationAllowed())
-        || (options.isOptimizing() && targetKeepInfo.isOptimizationAllowed())
-        || (options.isShrinking() && targetKeepInfo.isShrinkingAllowed())) {
-      errors.computeIfAbsent(target, NonKeptMethodWithCovariantReturnTypeAnnotationDiagnostic::new);
-    }
-    return targetKeepInfo.asMethodJoiner();
-  }
-
   private void synthesizeInterfaceMethodBridges() {
     for (InterfaceMethodSyntheticBridgeAction action : syntheticInterfaceMethodBridges.values()) {
       ProgramMethod bridge = action.getMethodToKeep();
@@ -4860,7 +4809,9 @@
 
         // Notify each analysis that a fixpoint has been reached, and give each analysis an
         // opportunity to add items to the worklist.
-        analyses.forEach(analysis -> analysis.notifyFixpoint(this, worklist, timing));
+        for (EnqueuerAnalysis analysis : analyses) {
+          analysis.notifyFixpoint(this, worklist, executorService, timing);
+        }
         if (!worklist.isEmpty()) {
           continue;
         }