Reland "Do not rewrite generic signatures in target of merged classes"

This reverts commit ceac696c81a3a23a960ebd0acb4992019c29e053.

Bug: 147386014
Change-Id: I55e5b7a6ffcc23188e7c631d577f0ca824cd093e
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
index d0240e9..d13cc3b 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
@@ -24,12 +25,14 @@
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
+import com.android.tools.r8.utils.IteratorUtils;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
@@ -132,7 +135,15 @@
     timing.end();
 
     timing.begin("rename-generic");
-    new GenericSignatureRewriter(appView, renaming).run(classes);
+    new GenericSignatureRewriter(appView, renaming)
+        .run(
+            new Iterable<DexProgramClass>() {
+              @Override
+              public Iterator<DexProgramClass> iterator() {
+                return IteratorUtils.<DexClass, DexProgramClass>filter(
+                    classes.iterator(), DexClass::isProgramClass);
+              }
+            });
     timing.end();
 
     timing.begin("rename-arrays");
diff --git a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureAction.java b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureAction.java
index 6753e06..7cb3506 100644
--- a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureAction.java
+++ b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureAction.java
@@ -9,11 +9,17 @@
  */
 public interface GenericSignatureAction<T> {
 
+  enum ParserPosition {
+    CLASS_SUPER_OR_INTERFACE_ANNOTATION,
+    ENCLOSING_INNER_OR_TYPE_ANNOTATION,
+    MEMBER_ANNOTATION
+  }
+
   public void parsedSymbol(char symbol);
 
   public void parsedIdentifier(String identifier);
 
-  public T parsedTypeName(String name);
+  public T parsedTypeName(String name, ParserPosition isTopLevel);
 
   public T parsedInnerTypeName(T enclosingType, String name);
 
diff --git a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureParser.java b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureParser.java
index c1f6a7d..dc0aa8c 100644
--- a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureParser.java
+++ b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureParser.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.naming.signature;
 
+import com.android.tools.r8.naming.signature.GenericSignatureAction.ParserPosition;
 import java.lang.reflect.GenericSignatureFormatError;
 import java.nio.CharBuffer;
 
@@ -50,7 +51,7 @@
  */
 public class GenericSignatureParser<T> {
 
-  private final GenericSignatureAction<T> actions;
+  private GenericSignatureAction<T> actions;
 
   /*
    * Parser:
@@ -110,7 +111,7 @@
     try {
       actions.start();
       setInput(signature);
-      parseFieldTypeSignature();
+      parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION);
       actions.stop();
     } catch (GenericSignatureFormatError e) {
       throw e;
@@ -141,11 +142,11 @@
     parseOptFormalTypeParameters();
 
     // SuperclassSignature ::= ClassTypeSignature.
-    parseClassTypeSignature();
+    parseClassTypeSignature(ParserPosition.CLASS_SUPER_OR_INTERFACE_ANNOTATION);
 
     while (symbol > 0) {
       // SuperinterfaceSignature ::= ClassTypeSignature.
-      parseClassTypeSignature();
+      parseClassTypeSignature(ParserPosition.CLASS_SUPER_OR_INTERFACE_ANNOTATION);
     }
   }
 
@@ -178,28 +179,28 @@
     expect(':');
 
     if (symbol == 'L' || symbol == '[' || symbol == 'T') {
-      parseFieldTypeSignature();
+      parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION);
     }
 
     while (symbol == ':') {
       // InterfaceBound ::= ":" FieldTypeSignature.
       actions.parsedSymbol(symbol);
       scanSymbol();
-      parseFieldTypeSignature();
+      parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION);
     }
   }
 
-  private void parseFieldTypeSignature() {
+  private void parseFieldTypeSignature(ParserPosition parserPosition) {
     // FieldTypeSignature ::= ClassTypeSignature | ArrayTypeSignature | TypeVariableSignature.
     switch (symbol) {
       case 'L':
-        parseClassTypeSignature();
+        parseClassTypeSignature(parserPosition);
         break;
       case '[':
-        // ArrayTypeSignature ::= "[" TypSignature.
+        // ArrayTypeSignature ::= "[" TypeSignature.
         actions.parsedSymbol(symbol);
         scanSymbol();
-        updateTypeSignature();
+        updateTypeSignature(parserPosition);
         break;
       case 'T':
         updateTypeVariableSignature();
@@ -209,7 +210,7 @@
     }
   }
 
-  private void parseClassTypeSignature() {
+  private void parseClassTypeSignature(ParserPosition parserPosition) {
     // ClassTypeSignature ::= "L" {Ident "/"} Ident OptTypeArguments {"." Ident OptTypeArguments}
     //  ";".
     actions.parsedSymbol(symbol);
@@ -226,18 +227,22 @@
     }
 
     qualIdent.append(this.identifier);
-    T parsedEnclosingType = actions.parsedTypeName(qualIdent.toString());
+    T parsedEnclosingType = actions.parsedTypeName(qualIdent.toString(), parserPosition);
 
-    updateOptTypeArguments();
-
-    while (symbol == '.') {
-      // Deal with Member Classes:
-      actions.parsedSymbol(symbol);
-      scanSymbol();
-      scanIdentifier();
-      assert identifier != null;
-      parsedEnclosingType = actions.parsedInnerTypeName(parsedEnclosingType, identifier);
+    if (parsedEnclosingType != null) {
+      // We should only parse any optional type arguments and member classes if we have not merged
+      // the class into the current subtype.
       updateOptTypeArguments();
+
+      while (symbol == '.') {
+        // Deal with Member Classes.
+        actions.parsedSymbol(symbol);
+        scanSymbol();
+        scanIdentifier();
+        assert identifier != null;
+        parsedEnclosingType = actions.parsedInnerTypeName(parsedEnclosingType, identifier);
+        updateOptTypeArguments();
+      }
     }
 
     actions.parsedSymbol(symbol);
@@ -268,13 +273,13 @@
     } else if (symbol == '+') {
       actions.parsedSymbol(symbol);
       scanSymbol();
-      parseFieldTypeSignature();
+      parseFieldTypeSignature(ParserPosition.ENCLOSING_INNER_OR_TYPE_ANNOTATION);
     } else if (symbol == '-') {
       actions.parsedSymbol(symbol);
       scanSymbol();
-      parseFieldTypeSignature();
+      parseFieldTypeSignature(ParserPosition.ENCLOSING_INNER_OR_TYPE_ANNOTATION);
     } else {
-      parseFieldTypeSignature();
+      parseFieldTypeSignature(ParserPosition.ENCLOSING_INNER_OR_TYPE_ANNOTATION);
     }
   }
 
@@ -291,7 +296,7 @@
     expect(';');
   }
 
-  private void updateTypeSignature() {
+  private void updateTypeSignature(ParserPosition parserPosition) {
     switch (symbol) {
       case 'B':
       case 'C':
@@ -306,7 +311,7 @@
         break;
       default:
         // Not an elementary type, but a FieldTypeSignature.
-        parseFieldTypeSignature();
+        parseFieldTypeSignature(parserPosition);
     }
   }
 
@@ -319,7 +324,7 @@
     expect('(');
 
     while (symbol != ')' && (symbol > 0)) {
-      updateTypeSignature();
+      updateTypeSignature(ParserPosition.MEMBER_ANNOTATION);
     }
 
     actions.parsedSymbol(symbol);
@@ -336,7 +341,7 @@
         if (symbol == 'T') {
           updateTypeVariableSignature();
         } else {
-          parseClassTypeSignature();
+          parseClassTypeSignature(ParserPosition.MEMBER_ANNOTATION);
         }
       } while (symbol == '^');
     }
@@ -345,7 +350,7 @@
   private void updateReturnType() {
     // ReturnType ::= TypeSignature | "V".
     if (symbol != 'V') {
-      updateTypeSignature();
+      updateTypeSignature(ParserPosition.MEMBER_ANNOTATION);
     } else {
       actions.parsedSymbol(symbol);
       scanSymbol();
diff --git a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
index c3b7188..13c2c43 100644
--- a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
+++ b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
@@ -10,8 +10,8 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexDefinition;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.origin.Origin;
@@ -43,15 +43,16 @@
     this.reporter = appView.options().reporter;
   }
 
-  public void run(Iterable<? extends DexClass> classes) {
+  public void run(Iterable<? extends DexProgramClass> classes) {
     final GenericSignatureCollector genericSignatureCollector = new GenericSignatureCollector();
     final GenericSignatureParser<DexType> genericSignatureParser =
         new GenericSignatureParser<>(genericSignatureCollector);
-    // classes may not be the same as appInfo().classes() if applymapping is used on classpath
+    // Classes may not be the same as appInfo().classes() if applymapping is used on classpath
     // arguments. If that is the case, the ProguardMapMinifier will pass in all classes that is
     // either ProgramClass or has a mapping. This is then transitively called inside the
     // ClassNameMinifier.
-    for (DexClass clazz : classes) {
+    for (DexProgramClass clazz : classes) {
+      genericSignatureCollector.setCurrentClassContext(clazz);
       clazz.annotations =
           rewriteGenericSignatures(
               clazz.annotations,
@@ -152,13 +153,22 @@
 
   private class GenericSignatureCollector implements GenericSignatureAction<DexType> {
     private StringBuilder renamedSignature;
+    private DexProgramClass currentClassContext;
 
-    public String getRenamedSignature() {
+    String getRenamedSignature() {
       return renamedSignature.toString();
     }
 
+    void setCurrentClassContext(DexProgramClass clazz) {
+      currentClassContext = clazz;
+    }
+
     @Override
     public void parsedSymbol(char symbol) {
+      if (symbol == ';' && renamedSignature.charAt(renamedSignature.length() - 1) == ';') {
+        // The type was never written (maybe because it was merged with it's subtype)
+        return;
+      }
       renamedSignature.append(symbol);
     }
 
@@ -168,13 +178,24 @@
     }
 
     @Override
-    public DexType parsedTypeName(String name) {
-      DexType type = appView.dexItemFactory().createType(getDescriptorFromClassBinaryName(name));
-      type = appView.graphLense().lookupType(type);
+    public DexType parsedTypeName(String name, ParserPosition parserPosition) {
+      String originalDescriptor = getDescriptorFromClassBinaryName(name);
+      DexType type =
+          appView.graphLense().lookupType(appView.dexItemFactory().createType(originalDescriptor));
       if (appView.appInfo().wasPruned(type)) {
         type = appView.dexItemFactory().objectType;
       }
       DexString renamedDescriptor = renaming.getOrDefault(type, type.descriptor);
+      if (parserPosition == ParserPosition.CLASS_SUPER_OR_INTERFACE_ANNOTATION
+          && currentClassContext != null) {
+        // We may have merged the type down to the current class type.
+        DexString classDescriptor = currentClassContext.type.descriptor;
+        if (!originalDescriptor.equals(classDescriptor.toString())
+            && renamedDescriptor.equals(classDescriptor)) {
+          renamedSignature.deleteCharAt(renamedSignature.length() - 1);
+          return null;
+        }
+      }
       renamedSignature.append(getClassBinaryNameFromDescriptor(renamedDescriptor.toString()));
       return type;
     }
@@ -197,6 +218,7 @@
       type = appView.graphLense().lookupType(type);
       DexString renamedDescriptor = renaming.get(type);
       if (renamedDescriptor != null) {
+        // TODO(b/147504070): If this is a merged class equal to the class context, do not add.
         // Pick the renamed inner class from the fully renamed binary name.
         String fullRenamedBinaryName =
             getClassBinaryNameFromDescriptor(renamedDescriptor.toString());
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 69b77cd..62241d5 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -383,7 +383,7 @@
       }
     }
     if (clazz.getEnclosingMethod() != null || !clazz.getInnerClasses().isEmpty()) {
-      // TODO(herhut): Consider supporting merging of enclosing-method and inner-class attributes.
+      // TODO(b/147504070): Consider merging of enclosing-method and inner-class attributes.
       if (Log.ENABLED) {
         AbortReason.UNSUPPORTED_ATTRIBUTES.printLogMessageForClass(clazz);
       }
@@ -440,7 +440,7 @@
       return false;
     }
     if (targetClass.getEnclosingMethod() != null || !targetClass.getInnerClasses().isEmpty()) {
-      // TODO(herhut): Consider supporting merging of enclosing-method and inner-class attributes.
+      // TODO(b/147504070): Consider merging of enclosing-method and inner-class attributes.
       if (Log.ENABLED) {
         AbortReason.UNSUPPORTED_ATTRIBUTES.printLogMessageForClass(clazz);
       }
diff --git a/src/main/java/com/android/tools/r8/utils/IteratorUtils.java b/src/main/java/com/android/tools/r8/utils/IteratorUtils.java
index 7ffce35..f38c02a 100644
--- a/src/main/java/com/android/tools/r8/utils/IteratorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/IteratorUtils.java
@@ -16,7 +16,8 @@
 
 public class IteratorUtils {
 
-  public static <T, S extends T> Iterator<S> filter(Iterator<T> iterator, Predicate<T> predicate) {
+  public static <T, S extends T> Iterator<S> filter(
+      Iterator<? extends T> iterator, Predicate<T> predicate) {
     return new Iterator<S>() {
 
       private S next = advance();
diff --git a/src/test/java/com/android/tools/r8/naming/GenericSignatureParserTest.java b/src/test/java/com/android/tools/r8/naming/GenericSignatureParserTest.java
index b264ef5..4489b75 100644
--- a/src/test/java/com/android/tools/r8/naming/GenericSignatureParserTest.java
+++ b/src/test/java/com/android/tools/r8/naming/GenericSignatureParserTest.java
@@ -22,6 +22,7 @@
 import org.junit.Test;
 
 public class GenericSignatureParserTest extends TestBase {
+
   private static class ReGenerateGenericSignatureRewriter
       implements GenericSignatureAction<String> {
 
@@ -42,7 +43,7 @@
     }
 
     @Override
-    public String parsedTypeName(String name) {
+    public String parsedTypeName(String name, ParserPosition parserPosition) {
       renamedSignature.append(name);
       return name;
     }
@@ -389,7 +390,7 @@
       }
 
       @Override
-      public String parsedTypeName(String name) {
+      public String parsedTypeName(String name, ParserPosition parserPosition) {
         throw exceptionSupplier.get();
       }
     }
diff --git a/src/test/java/com/android/tools/r8/naming/signature/SignatureOfMergedClassesTest.java b/src/test/java/com/android/tools/r8/naming/signature/SignatureOfMergedClassesTest.java
new file mode 100644
index 0000000..a9fc20f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/signature/SignatureOfMergedClassesTest.java
@@ -0,0 +1,122 @@
+// Copyright (c) 2020, 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.naming.signature;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.naming.signature.merging.I;
+import com.android.tools.r8.naming.signature.merging.ImplI;
+import com.android.tools.r8.naming.signature.merging.ImplK;
+import com.android.tools.r8.naming.signature.merging.InterfaceToKeep;
+import com.android.tools.r8.naming.signature.merging.J;
+import com.android.tools.r8.naming.signature.merging.K;
+import java.io.IOException;
+import java.lang.invoke.LambdaConversionException;
+import java.lang.reflect.Type;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class SignatureOfMergedClassesTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public SignatureOfMergedClassesTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testRemovalOfMergedInterfaceOnSameClass()
+      throws IOException, CompilationFailedException, ExecutionException,
+          LambdaConversionException {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(
+            ImplI.class, ImplK.class, I.class, J.class, InterfaceToKeep.class, K.class, Main.class)
+        .addKeepMainRule(Main.class)
+        .addKeepClassRules(InterfaceToKeep.class)
+        .addKeepAttributes("Signature, InnerClasses, EnclosingMethod, *Annotation*")
+        .setMinApi(parameters.getApiLevel())
+        .noMinification()
+        .addOptionsModification(
+            internalOptions -> {
+              internalOptions.enableUnusedInterfaceRemoval = false;
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(
+            "ImplI.foo",
+            "ImplI: com.android.tools.r8.naming.signature.merging.InterfaceToKeep<java.lang.Void>",
+            "K: com.android.tools.r8.naming.signature.merging.InterfaceToKeep<java.lang.Void>",
+            "ImplK.foo",
+            "ImplK.bar",
+            "ImplK: interface com.android.tools.r8.naming.signature.merging.K")
+        .inspect(
+            codeInspector -> {
+              assertThat(codeInspector.clazz(I.class), not(isPresent()));
+              assertThat(codeInspector.clazz(J.class), not(isPresent()));
+            });
+  }
+
+  @Test
+  public void testKeepingOneSelfOnInterface()
+      throws ExecutionException, CompilationFailedException, IOException {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(Foo.class, InterfaceToKeep.class)
+        .addKeepMainRule(Foo.class)
+        .addKeepClassRules(InterfaceToKeep.class)
+        .addKeepAttributes("Signature, InnerClasses, EnclosingMethod, *Annotation*")
+        .setMinApi(parameters.getApiLevel())
+        .noMinification()
+        .addOptionsModification(
+            internalOptions -> {
+              internalOptions.enableUnusedInterfaceRemoval = false;
+            })
+        .run(parameters.getRuntime(), Foo.class)
+        .assertSuccessWithOutputLines(
+            "com.android.tools.r8.naming.signature.merging.InterfaceToKeep"
+                + "<com.android.tools.r8.naming.signature.SignatureOfMergedClassesTest$Foo>");
+  }
+
+  public static class Foo implements InterfaceToKeep<Foo> {
+
+    public static void main(String[] args) {
+      for (Type genericInterface : Foo.class.getGenericInterfaces()) {
+        System.out.println(genericInterface);
+      }
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      new ImplI().foo();
+      for (Type genericInterface : ImplI.class.getGenericInterfaces()) {
+        System.out.println("ImplI: " + genericInterface);
+      }
+      for (Type genericInterface : K.class.getGenericInterfaces()) {
+        System.out.println("K: " + genericInterface);
+      }
+      K k = new ImplK();
+      k.foo();
+      k.bar();
+      for (Type genericInterface : ImplK.class.getGenericInterfaces()) {
+        System.out.println("ImplK: " + genericInterface);
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/signature/merging/I.java b/src/test/java/com/android/tools/r8/naming/signature/merging/I.java
new file mode 100644
index 0000000..237786c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/signature/merging/I.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2020, 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.naming.signature.merging;
+
+public interface I {
+  void foo();
+}
diff --git a/src/test/java/com/android/tools/r8/naming/signature/merging/ImplI.java b/src/test/java/com/android/tools/r8/naming/signature/merging/ImplI.java
new file mode 100644
index 0000000..b842386
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/signature/merging/ImplI.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2020, 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.naming.signature.merging;
+
+public class ImplI implements InterfaceToKeep<Void>, I {
+
+  @Override
+  public void foo() {
+    System.out.println("ImplI.foo");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/signature/merging/ImplK.java b/src/test/java/com/android/tools/r8/naming/signature/merging/ImplK.java
new file mode 100644
index 0000000..3c23d56
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/signature/merging/ImplK.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2020, 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.naming.signature.merging;
+
+public class ImplK implements K {
+
+  @Override
+  public void foo() {
+    System.out.println("ImplK.foo");
+  }
+
+  @Override
+  public void bar() {
+    System.out.println("ImplK.bar");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/signature/merging/InterfaceToKeep.java b/src/test/java/com/android/tools/r8/naming/signature/merging/InterfaceToKeep.java
new file mode 100644
index 0000000..6bebcd0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/signature/merging/InterfaceToKeep.java
@@ -0,0 +1,7 @@
+// Copyright (c) 2020, 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.naming.signature.merging;
+
+public interface InterfaceToKeep<T> {}
diff --git a/src/test/java/com/android/tools/r8/naming/signature/merging/J.java b/src/test/java/com/android/tools/r8/naming/signature/merging/J.java
new file mode 100644
index 0000000..444a5e8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/signature/merging/J.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2020, 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.naming.signature.merging;
+
+public interface J {
+  void foo();
+}
diff --git a/src/test/java/com/android/tools/r8/naming/signature/merging/K.java b/src/test/java/com/android/tools/r8/naming/signature/merging/K.java
new file mode 100644
index 0000000..4b8ed50
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/signature/merging/K.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2020, 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.naming.signature.merging;
+
+public interface K extends InterfaceToKeep<Void>, J {
+  void bar();
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index 4aec544..1a4dcc16 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -423,7 +423,7 @@
     }
 
     @Override
-    public String parsedTypeName(String name) {
+    public String parsedTypeName(String name, ParserPosition parserPosition) {
       String type = name;
       if (obfuscatedToOriginalMapping != null) {
         String original = mapType(obfuscatedToOriginalMapping, name);