Revert "Move syntetic finalization over repackaging"

This reverts commit eacf70bb13b04f6542d457a8866d9b8986e67da5.

Reason for revert: Breaking the bots

Change-Id: Ie70dc8b668b5505c59905ba2b4d8d5693ac4a7f1
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index e237dcc..885ada8 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -70,6 +70,7 @@
 import com.android.tools.r8.naming.signature.GenericSignatureRewriter;
 import com.android.tools.r8.optimize.ClassAndMemberPublicizer;
 import com.android.tools.r8.optimize.MemberRebindingAnalysis;
+import com.android.tools.r8.optimize.MemberRebindingIdentityLens;
 import com.android.tools.r8.optimize.MemberRebindingIdentityLensFactory;
 import com.android.tools.r8.optimize.VisibilityBridgeRemover;
 import com.android.tools.r8.optimize.bridgehoisting.BridgeHoisting;
@@ -711,15 +712,9 @@
         assert !options.isShrinking();
       }
 
-      // Insert a member rebinding oracle in the graph to ensure that all subsequent rewritings of
-      // the application has an applied oracle for looking up non-rebound references.
-      appView.setGraphLens(MemberRebindingIdentityLensFactory.create(appView, executorService));
-
-      if (appView.appInfo().hasLiveness()) {
-        SyntheticFinalization.finalizeWithLiveness(appView.withLiveness(), executorService);
-      } else {
-        SyntheticFinalization.finalizeWithClassHierarchy(appView, executorService);
-      }
+      MemberRebindingIdentityLens memberRebindingLens =
+          MemberRebindingIdentityLensFactory.create(appView, executorService);
+      appView.setGraphLens(memberRebindingLens);
 
       // Read any -applymapping input to allow for repackaging to not relocate the classes.
       timing.begin("read -applymapping file");
@@ -733,13 +728,24 @@
         RepackagingLens lens =
             new Repackaging(appView.withLiveness()).run(appBuilder, executorService, timing);
         if (lens != null) {
-          appView.rewriteWithLensAndApplication(lens, appBuilder.build());
+          // Specify to use the member rebinding lens as the parent lens during the rewriting. This
+          // is needed to ensure that the rebound references are available during lens lookups.
+          // TODO(b/168282032): This call-site should not have to think about the parent lens that
+          //  is used for the rewriting. Once the new member rebinding lens replaces the old member
+          //  rebinding analysis it should be possible to clean this up.
+          appView.rewriteWithLensAndApplication(
+              lens, appBuilder.build(), memberRebindingLens.getPrevious());
         }
       }
       if (appView.appInfo().hasLiveness()) {
         assert Repackaging.verifyIdentityRepackaging(appView.withLiveness());
       }
 
+      if (appView.appInfo().hasLiveness()) {
+        SyntheticFinalization.finalizeWithLiveness(appView.withLiveness(), executorService);
+      } else {
+        SyntheticFinalization.finalizeWithClassHierarchy(appView, executorService);
+      }
 
       // Clear the reference type lattice element cache. This is required since class merging may
       // need to build IR.
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index c3cbe39..b740f3d 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -795,7 +795,6 @@
     while (firstUnappliedLens.getPrevious() != appliedLens) {
       GraphLens previousLens = firstUnappliedLens.getPrevious();
       assert previousLens.isNonIdentityLens();
-      assert previousLens != appView.codeLens();
       firstUnappliedLens = previousLens.asNonIdentityLens();
     }
 
@@ -803,24 +802,22 @@
     // TODO(b/182129249): Once the member rebinding phase has been removed, the MemberRebindingLens
     //  should be removed and all uses of FieldRebindingIdentityLens should be replaced by
     //  MemberRebindingIdentityLens.
-    GraphLens newMemberRebindingLens = GraphLens.getIdentityLens();
-    if (!firstUnappliedLens.isMemberRebindingLens()
-        && !firstUnappliedLens.isMemberRebindingIdentityLens()) {
-      NonIdentityGraphLens appliedMemberRebindingLens =
-          firstUnappliedLens.findPreviousUntil(
-              previous ->
-                  previous.isMemberRebindingLens() || previous.isMemberRebindingIdentityLens(),
-              previous -> previous != appView.codeLens());
-      if (appliedMemberRebindingLens != null) {
-        newMemberRebindingLens =
-            appliedMemberRebindingLens.isMemberRebindingLens()
-                ? appliedMemberRebindingLens
-                    .asMemberRebindingLens()
-                    .toRewrittenFieldRebindingLens(appView, appliedLens)
-                : appliedMemberRebindingLens
-                    .asMemberRebindingIdentityLens()
-                    .toRewrittenMemberRebindingIdentityLens(appView, appliedLens);
-      }
+    NonIdentityGraphLens appliedMemberRebindingLens =
+        firstUnappliedLens.findPrevious(
+            previous ->
+                previous.isMemberRebindingLens() || previous.isMemberRebindingIdentityLens());
+    GraphLens newMemberRebindingLens;
+    if (appliedMemberRebindingLens != null) {
+      newMemberRebindingLens =
+          appliedMemberRebindingLens.isMemberRebindingLens()
+              ? appliedMemberRebindingLens
+                  .asMemberRebindingLens()
+                  .toRewrittenFieldRebindingLens(appView, appliedLens)
+              : appliedMemberRebindingLens
+                  .asMemberRebindingIdentityLens()
+                  .toRewrittenMemberRebindingIdentityLens(appView, appliedLens);
+    } else {
+      newMemberRebindingLens = GraphLens.getIdentityLens();
     }
 
     firstUnappliedLens.withAlternativeParentLens(
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java
index 188211d..952c31d 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -400,7 +400,7 @@
 
   public abstract DexType lookupType(DexType type, GraphLens applied);
 
-  @Deprecated
+  // This overload can be used when the graph lens is known to be context insensitive.
   public final DexMethod lookupMethod(DexMethod method) {
     assert verifyIsContextFreeForMethod(method);
     return lookupMethod(method, null, null).getReference();
@@ -410,47 +410,22 @@
     return lookupMethod(method, context.getReference(), Type.DIRECT);
   }
 
-  public final MethodLookupResult lookupInvokeDirect(
-      DexMethod method, ProgramMethod context, GraphLens codeLens) {
-    return lookupMethod(method, context.getReference(), Type.DIRECT, codeLens);
-  }
-
   public final MethodLookupResult lookupInvokeInterface(DexMethod method, ProgramMethod context) {
     return lookupMethod(method, context.getReference(), Type.INTERFACE);
   }
 
-  public final MethodLookupResult lookupInvokeInterface(
-      DexMethod method, ProgramMethod context, GraphLens codeLens) {
-    return lookupMethod(method, context.getReference(), Type.INTERFACE, codeLens);
-  }
-
   public final MethodLookupResult lookupInvokeStatic(DexMethod method, ProgramMethod context) {
     return lookupMethod(method, context.getReference(), Type.STATIC);
   }
 
-  public final MethodLookupResult lookupInvokeStatic(
-      DexMethod method, ProgramMethod context, GraphLens codeLens) {
-    return lookupMethod(method, context.getReference(), Type.STATIC, codeLens);
-  }
-
   public final MethodLookupResult lookupInvokeSuper(DexMethod method, ProgramMethod context) {
     return lookupMethod(method, context.getReference(), Type.SUPER);
   }
 
-  public final MethodLookupResult lookupInvokeSuper(
-      DexMethod method, ProgramMethod context, GraphLens codeLens) {
-    return lookupMethod(method, context.getReference(), Type.SUPER, codeLens);
-  }
-
   public final MethodLookupResult lookupInvokeVirtual(DexMethod method, ProgramMethod context) {
     return lookupMethod(method, context.getReference(), Type.VIRTUAL);
   }
 
-  public final MethodLookupResult lookupInvokeVirtual(
-      DexMethod method, ProgramMethod context, GraphLens codeLens) {
-    return lookupMethod(method, context.getReference(), Type.VIRTUAL, codeLens);
-  }
-
   public final MethodLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
     return lookupMethod(method, context, type, null);
   }
@@ -841,13 +816,6 @@
       return previous.isNonIdentityLens() ? previous.asNonIdentityLens().find(predicate) : null;
     }
 
-    public final <T extends NonIdentityGraphLens> T findPreviousUntil(
-        Predicate<NonIdentityGraphLens> predicate,
-        Predicate<NonIdentityGraphLens> stoppingCriterion) {
-      T found = findPrevious(predicate.or(stoppingCriterion));
-      return (found == null || stoppingCriterion.test(found)) ? null : found;
-    }
-
     public final void withAlternativeParentLens(GraphLens lens, Action action) {
       GraphLens oldParent = getPrevious();
       previousLens = lens;
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingAnnotationTracer.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingAnnotationTracer.java
index d119f1c..f52b8b7 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingAnnotationTracer.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingAnnotationTracer.java
@@ -13,19 +13,16 @@
 import com.android.tools.r8.graph.DexEncodedAnnotation;
 import com.android.tools.r8.graph.DexMethodHandle;
 import com.android.tools.r8.graph.DexValue;
-import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.ParameterAnnotationsList;
 
 public class RepackagingAnnotationTracer {
 
   private final AppInfoWithClassHierarchy appInfo;
-  private final GraphLens graphLens;
   private final RepackagingUseRegistry registry;
 
   public RepackagingAnnotationTracer(
       AppView<? extends AppInfoWithClassHierarchy> appView, RepackagingUseRegistry registry) {
     this.appInfo = appView.appInfo();
-    this.graphLens = appView.graphLens();
     this.registry = registry;
   }
 
@@ -42,7 +39,7 @@
   }
 
   private void traceEncodedAnnotation(DexEncodedAnnotation annotation) {
-    registry.registerTypeReference(annotation.type, graphLens);
+    registry.registerTypeReference(annotation.type);
     annotation.forEachElement(this::traceAnnotationElement);
   }
 
@@ -97,14 +94,11 @@
         break;
 
       case METHOD_TYPE:
-        value
-            .asDexValueMethodType()
-            .getValue()
-            .forEachType(type -> registry.registerTypeReference(type, graphLens));
+        value.asDexValueMethodType().getValue().forEachType(registry::registerTypeReference);
         break;
 
       case TYPE:
-        registry.registerTypeReference(value.asDexValueType().getValue(), graphLens);
+        registry.registerTypeReference(value.asDexValueType().getValue());
         break;
 
       default:
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
index e17be11..9854d69 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
@@ -105,8 +105,8 @@
         new RepackagingUseRegistry(appView, this, clazz, libraryBoundaryNode);
 
     // Trace the references to the immediate super types.
-    registry.registerTypeReference(clazz.getSuperType(), appView.graphLens());
-    clazz.interfaces.forEach(type -> registry.registerTypeReference(type, appView.graphLens()));
+    registry.registerTypeReference(clazz.getSuperType());
+    clazz.interfaces.forEach(registry::registerTypeReference);
 
     // Trace the references from the class annotations.
     new RepackagingAnnotationTracer(appView, registry).trace(clazz.annotations());
@@ -114,10 +114,10 @@
     // Trace the references in the nest host and/or members.
     if (clazz.isInANest()) {
       if (clazz.isNestHost()) {
-        clazz.forEachNestMember(type -> registry.registerTypeReference(type, appView.graphLens()));
+        clazz.forEachNestMember(registry::registerTypeReference);
       } else {
         assert clazz.isNestMember();
-        registry.registerTypeReference(clazz.getNestHost(), appView.graphLens());
+        registry.registerTypeReference(clazz.getNestHost());
       }
     }
 
@@ -139,7 +139,7 @@
         new RepackagingUseRegistry(appView, this, field, libraryBoundaryNode);
 
     // Trace the type of the field.
-    registry.registerTypeReference(field.getReference().getType(), appView.graphLens());
+    registry.registerTypeReference(field.getReference().getType());
 
     // Trace the references in the field annotations.
     new RepackagingAnnotationTracer(appView, registry).trace(field.getDefinition().annotations());
@@ -151,9 +151,7 @@
         new RepackagingUseRegistry(appView, this, method, libraryBoundaryNode);
 
     // Trace the type references in the method signature.
-    definition
-        .getProto()
-        .forEachType(type -> registry.registerTypeReference(type, appView.graphLens()));
+    definition.getProto().forEachType(registry::registerTypeReference);
 
     // Check if this overrides a package-private method.
     DexClass superClass =
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
index 1339cbf..5c5a857 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
@@ -9,7 +9,6 @@
 import com.android.tools.r8.graph.AccessFlags;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ClassAccessFlags;
-import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClassAndMember;
 import com.android.tools.r8.graph.DexField;
@@ -17,7 +16,6 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.EnclosingMethodAttribute;
-import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.InnerClassAttribute;
 import com.android.tools.r8.graph.MemberResolutionResult;
@@ -38,13 +36,10 @@
 
   private final AppInfoWithLiveness appInfo;
   private final InternalOptions options;
-  private final GraphLens graphLens;
   private final RepackagingConstraintGraph constraintGraph;
   private final InitClassLens initClassLens;
   private final RepackagingConstraintGraph.Node node;
   private final RepackagingConstraintGraph.Node missingTypeNode;
-  private final GraphLens codeLens;
-  private final ProgramMethod methodContext;
 
   public RepackagingUseRegistry(
       AppView<AppInfoWithLiveness> appView,
@@ -56,18 +51,8 @@
     this.options = appView.options();
     this.constraintGraph = constraintGraph;
     this.initClassLens = appView.initClassLens();
-    this.graphLens = appView.graphLens();
     this.node = constraintGraph.getNode(context.getDefinition());
     this.missingTypeNode = missingTypeNode;
-    GraphLens codeLens = appView.graphLens();
-    if (context.isMethod()) {
-      Code code = context.asMethod().getDefinition().getCode();
-      if (code != null) {
-        codeLens = code.getCodeLens(appView);
-      }
-    }
-    this.codeLens = codeLens;
-    methodContext = context.isMethod() ? context.asMethod() : null;
   }
 
   private boolean isOnlyAccessibleFromSamePackage(DexClass referencedClass) {
@@ -75,8 +60,11 @@
     if (accessFlags.isPackagePrivate()) {
       return true;
     }
-    return accessFlags.isProtected()
-        && !appInfo.isSubtype(getContext().getContextType(), referencedClass.getType());
+    if (accessFlags.isProtected()
+        && !appInfo.isSubtype(getContext().getContextType(), referencedClass.getType())) {
+      return true;
+    }
+    return false;
   }
 
   private boolean isOnlyAccessibleFromSamePackage(
@@ -104,7 +92,7 @@
   }
 
   public void registerFieldAccess(DexField field) {
-    registerMemberAccess(appInfo.resolveField(graphLens.lookupField(field)), false);
+    registerMemberAccess(appInfo.resolveField(field), false);
   }
 
   public ProgramMethod registerMethodReference(DexMethod method) {
@@ -201,46 +189,32 @@
 
   @Override
   public void registerInitClass(DexType type) {
-    registerMemberAccess(
-        appInfo.resolveField(initClassLens.getInitClassField(graphLens.lookupClassType(type))),
-        false);
+    registerFieldAccess(initClassLens.getInitClassField(type));
   }
 
   @Override
   public void registerInvokeVirtual(DexMethod invokedMethod) {
-    registerMemberAccessForInvoke(
-        appInfo.resolveMethod(
-            graphLens.lookupInvokeVirtual(invokedMethod, methodContext, codeLens).getReference(),
-            false));
+    registerMemberAccessForInvoke(appInfo.resolveMethod(invokedMethod, false));
   }
 
   @Override
   public void registerInvokeDirect(DexMethod invokedMethod) {
-    registerMemberAccessForInvoke(
-        appInfo.unsafeResolveMethodDueToDexFormat(
-            graphLens.lookupInvokeDirect(invokedMethod, methodContext, codeLens).getReference()));
+    registerMemberAccessForInvoke(appInfo.unsafeResolveMethodDueToDexFormat(invokedMethod));
   }
 
   @Override
   public void registerInvokeStatic(DexMethod invokedMethod) {
-    registerMemberAccessForInvoke(
-        appInfo.unsafeResolveMethodDueToDexFormat(
-            graphLens.lookupInvokeStatic(invokedMethod, methodContext, codeLens).getReference()));
+    registerMemberAccessForInvoke(appInfo.unsafeResolveMethodDueToDexFormat(invokedMethod));
   }
 
   @Override
   public void registerInvokeInterface(DexMethod invokedMethod) {
-    registerMemberAccessForInvoke(
-        appInfo.resolveMethod(
-            graphLens.lookupInvokeInterface(invokedMethod, methodContext, codeLens).getReference(),
-            true));
+    registerMemberAccessForInvoke(appInfo.resolveMethod(invokedMethod, true));
   }
 
   @Override
   public void registerInvokeSuper(DexMethod invokedMethod) {
-    registerMemberAccessForInvoke(
-        appInfo.unsafeResolveMethodDueToDexFormat(
-            graphLens.lookupInvokeSuper(invokedMethod, methodContext, codeLens).getReference()));
+    registerMemberAccessForInvoke(appInfo.unsafeResolveMethodDueToDexFormat(invokedMethod));
   }
 
   @Override
@@ -255,7 +229,7 @@
 
   @Override
   public void registerNewInstance(DexType type) {
-    registerTypeAccess(graphLens.lookupClassType(type));
+    registerTypeAccess(type);
   }
 
   @Override
@@ -270,16 +244,12 @@
 
   @Override
   public void registerTypeReference(DexType type) {
-    registerTypeReference(type, codeLens);
-  }
-
-  public void registerTypeReference(DexType type, GraphLens applied) {
-    registerTypeAccess(graphLens.lookupType(type, applied));
+    registerTypeAccess(type);
   }
 
   @Override
   public void registerInstanceOf(DexType type) {
-    registerTypeAccess(graphLens.lookupType(type));
+    registerTypeAccess(type);
   }
 
   public void registerEnclosingMethodAttribute(EnclosingMethodAttribute enclosingMethodAttribute) {
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingLambdaRepackageTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingLambdaRepackageTest.java
index 6c799bd..c85c55b 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingLambdaRepackageTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingLambdaRepackageTest.java
@@ -4,7 +4,7 @@
 
 package com.android.tools.r8.naming.applymapping;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
@@ -50,8 +50,14 @@
             .addApplyMapping(firstRunResult.proguardMap())
             .compile()
             .run(parameters.getRuntime(), Main.class);
-    assertEquals(firstRunResult.proguardMap(), secondRunResult.proguardMap());
-    secondRunResult.assertSuccessWithOutputLines("Hello World");
+    if (parameters.isDexRuntime()) {
+      // TODO(b/218793832): Should be the same map.
+      assertNotEquals(firstRunResult.proguardMap(), secondRunResult.proguardMap());
+    }
+    secondRunResult
+        .assertSuccessWithOutputLinesIf(parameters.isCfRuntime(), "Hello World")
+        // TODO(b/218793832): Should not fail with an error.
+        .assertFailureWithErrorThatThrowsIf(parameters.isDexRuntime(), IllegalAccessError.class);
   }
 
   @NeverClassInline
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithSyntheticItemTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithSyntheticItemTest.java
index 7611b1c..1a3bfca 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithSyntheticItemTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithSyntheticItemTest.java
@@ -64,7 +64,11 @@
                       .filter(item -> item.getFinalName().startsWith("foo"))
                       .collect(Collectors.toList());
               assertEquals(1, classesStartingWithfoo.size());
-              String expectedOriginalNamePrefix = typeName(A.class) + "$$ExternalSyntheticLambda0";
+              // TODO(b/172014416): We should not be able to look this up through the repackage name
+              String expectedOriginalNamePrefix =
+                  isFlattenPackageHierarchy()
+                      ? "foo.a.RepackageWithSyntheticItemTest$A"
+                      : "foo.RepackageWithSyntheticItemTest$A";
               assertThat(
                   classesStartingWithfoo.get(0).getOriginalName(),
                   containsString(expectedOriginalNamePrefix));
diff --git a/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java b/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
index 369d584..f85290d 100644
--- a/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
@@ -10,6 +10,7 @@
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.R8;
@@ -291,7 +292,8 @@
             .apply(this::configureHorizontalClassMerging)
             .compile()
             .graphInspector();
-    assertRetainedClassesEqual(referenceInspector, ifHasMemberThenKeepClassInspector);
+    assertRetainedClassesEqual(
+        referenceInspector, ifHasMemberThenKeepClassInspector, true, true, true, true);
   }
 
   private void configureHorizontalClassMerging(R8FullTestBuilder testBuilder) {
@@ -311,27 +313,58 @@
 
   private void assertRetainedClassesEqual(
       GraphInspector referenceResult, GraphInspector conditionalResult) {
+    assertRetainedClassesEqual(referenceResult, conditionalResult, false, false, false, false);
+  }
+
+  private void assertRetainedClassesEqual(
+      GraphInspector referenceResult,
+      GraphInspector conditionalResult,
+      boolean expectReferenceIsLarger,
+      boolean expectReferenceIsLargerOnlyBySynthetics,
+      boolean expectConditionalIsLarger,
+      boolean expectConditionalIsLargerOnlyBySynthetics) {
     Set<String> referenceClasses =
         new TreeSet<>(
             referenceResult.codeInspector().allClasses().stream()
                 .map(FoundClassSubject::getOriginalName)
                 .collect(Collectors.toSet()));
+
     Set<String> conditionalClasses =
         conditionalResult.codeInspector().allClasses().stream()
             .map(FoundClassSubject::getOriginalName)
             .collect(Collectors.toSet());
-    Set<String> notInReference =
-        new TreeSet<>(Sets.difference(conditionalClasses, referenceClasses));
-    assertEquals(
-        "Classes in -if rule that are not in -keepclassmembers rule",
-        Collections.emptySet(),
-        notInReference);
-    Set<String> notInConditional =
-        new TreeSet<>(Sets.difference(referenceClasses, conditionalClasses));
-    assertEquals(
-        "Classes in -keepclassmembers rule that are not in -if rule",
-        Collections.emptySet(),
-        notInConditional);
+    {
+      Set<String> notInReference =
+          new TreeSet<>(Sets.difference(conditionalClasses, referenceClasses));
+      if (expectConditionalIsLarger) {
+        assertFalse("Expected classes in -if rule to retain more.", notInReference.isEmpty());
+        if (expectConditionalIsLargerOnlyBySynthetics) {
+          assertAllClassesAreSynthetics(notInReference);
+        }
+      } else {
+        assertEquals(
+            "Classes in -if rule that are not in -keepclassmembers rule",
+            Collections.emptySet(),
+            notInReference);
+      }
+    }
+    {
+      Set<String> notInConditional =
+          new TreeSet<>(Sets.difference(referenceClasses, conditionalClasses));
+      if (expectReferenceIsLarger) {
+        assertFalse(
+            "Expected classes in -keepclassmembers rule to retain more.",
+            notInConditional.isEmpty());
+        if (expectReferenceIsLargerOnlyBySynthetics) {
+          assertAllClassesAreSynthetics(notInConditional);
+        }
+      } else {
+        assertEquals(
+            "Classes in -keepclassmembers rule that are not in -if rule",
+            Collections.emptySet(),
+            notInConditional);
+      }
+    }
   }
 
   private void assertAllClassesAreSynthetics(Set<String> classNames) {