Version 1.4.49

Cherry-pick: Handle renaming of types in Signature annotations after vertical class merging
CL: https://r8-review.googlesource.com/c/r8/+/34472

Cherry-pick: Update test expectation
CL: https://r8-review.googlesource.com/c/r8/+/34479

Cherry-pick: Add test for signature annotation with vertical class merging
CL: https://r8-review.googlesource.com/c/r8/+/34403

Cherry-pick: Correctly handle '***' in ProguardTypeMatcher
CL: https://r8-review.googlesource.com/c/r8/+/34526

Cherry-pick: Add a failing test for use of '***'
CL: https://r8-review.googlesource.com/c/r8/+/34524

Cherry-pick: Only interpret pattern as include-all if separators are also included
CL: https://r8-review.googlesource.com/c/r8/+/34531

Cherry-pick: Don't use Class.getTypeName in test
CL: https://r8-review.googlesource.com/c/r8/+/34538

Bug: 124584385, 124357885
Change-Id: Id052501b8aa98014e54ed129409c11beed5fb7cd
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 37311e8..40c2ffa 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -624,9 +624,7 @@
       NamingLens namingLens;
       if (options.enableMinification) {
         timing.begin("Minification");
-        namingLens =
-            new Minifier(appView.appInfo().withLiveness(), rootSet, desugaredCallSites, options)
-                .run(timing);
+        namingLens = new Minifier(appView.withLiveness(), rootSet, desugaredCallSites).run(timing);
         timing.end();
       } else {
         namingLens = NamingLens.getIdentityLens();
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 893fefa..b31bcbd 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "1.4.48";
+  public static final String LABEL = "1.4.49";
 
   private Version() {
   }
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 bc81538..b59b79b 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -9,6 +9,7 @@
 import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromClassBinaryName;
 import static com.android.tools.r8.utils.DescriptorUtils.getPackageBinaryNameFromJavaType;
 
+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;
@@ -50,6 +51,7 @@
 
 class ClassNameMinifier {
 
+  private final AppView<AppInfoWithLiveness> appView;
   private final AppInfoWithLiveness appInfo;
   private final Reporter reporter;
   private final PackageObfuscationMode packageObfuscationMode;
@@ -74,11 +76,10 @@
   private final GenericSignatureParser<DexType> genericSignatureParser =
       new GenericSignatureParser<>(genericSignatureRewriter);
 
-  ClassNameMinifier(
-      AppInfoWithLiveness appInfo,
-      RootSet rootSet,
-      InternalOptions options) {
-    this.appInfo = appInfo;
+  ClassNameMinifier(AppView<AppInfoWithLiveness> appView, RootSet rootSet) {
+    this.appView = appView;
+    this.appInfo = appView.appInfo();
+    InternalOptions options = appView.options();
     this.reporter = options.reporter;
     this.packageObfuscationMode = options.getProguardConfiguration().getPackageObfuscationMode();
     this.isAccessModificationAllowed =
@@ -546,6 +547,7 @@
     @Override
     public DexType parsedTypeName(String name) {
       DexType type = appInfo.dexItemFactory.createType(getDescriptorFromClassBinaryName(name));
+      type = appView.graphLense().lookupType(type);
       DexString renamedDescriptor = renaming.getOrDefault(type, type.descriptor);
       renamedSignature.append(getClassBinaryNameFromDescriptor(renamedDescriptor.toString()));
       return type;
@@ -564,6 +566,7 @@
       String enclosingRenamedBinaryName =
           getClassBinaryNameFromDescriptor(
               renaming.getOrDefault(enclosingType, enclosingType.descriptor).toString());
+      type = appView.graphLense().lookupType(type);
       DexString renamedDescriptor = renaming.get(type);
       if (renamedDescriptor != null) {
         // Pick the renamed inner class from the fully renamed binary name.
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
index be339b6..e2127dd 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.naming;
 
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
@@ -10,7 +11,6 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
-import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Sets;
@@ -19,8 +19,8 @@
 
 class FieldNameMinifier extends MemberNameMinifier<DexField, DexType> {
 
-  FieldNameMinifier(AppInfoWithLiveness appInfo, RootSet rootSet, InternalOptions options) {
-    super(appInfo, rootSet, options);
+  FieldNameMinifier(AppView<AppInfoWithLiveness> appView, RootSet rootSet) {
+    super(appView, rootSet);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java
index 419bfbb..53957e4 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.naming;
 
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.CachedHashValueDexItem;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
@@ -18,6 +19,7 @@
 
 abstract class MemberNameMinifier<MemberType, StateType extends CachedHashValueDexItem> {
 
+  protected final AppView<AppInfoWithLiveness> appView;
   protected final AppInfoWithLiveness appInfo;
   protected final RootSet rootSet;
   protected final InternalOptions options;
@@ -34,10 +36,11 @@
   // which is useful for debugging.
   private final BiMap<DexType, NamingState<StateType, ?>> states = HashBiMap.create();
 
-  MemberNameMinifier(AppInfoWithLiveness appInfo, RootSet rootSet, InternalOptions options) {
-    this.appInfo = appInfo;
+  MemberNameMinifier(AppView<AppInfoWithLiveness> appView, RootSet rootSet) {
+    this.appView = appView;
+    this.appInfo = appView.appInfo();
     this.rootSet = rootSet;
-    this.options = options;
+    this.options = appView.options();
     this.dictionary = options.getProguardConfiguration().getObfuscationDictionary();
     this.useUniqueMemberNames = options.getProguardConfiguration().isUseUniqueClassMemberNames();
     this.overloadAggressively =
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index 8864271..2cd8712 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.naming;
 
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -91,11 +92,8 @@
 
   private final FrontierState frontierState = new FrontierState();
 
-  MethodNameMinifier(
-      AppInfoWithLiveness appInfo,
-      RootSet rootSet,
-      InternalOptions options) {
-    super(appInfo, rootSet, options);
+  MethodNameMinifier(AppView<AppInfoWithLiveness> appView, RootSet rootSet) {
+    super(appView, rootSet);
     equivalence =
         overloadAggressively
             ? MethodSignatureEquivalence.get()
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index 5333125..af18595 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.naming;
 
 import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -35,26 +36,27 @@
 
   static final char INNER_CLASS_SEPARATOR = '$';
 
+  private final AppView<AppInfoWithLiveness> appView;
   private final AppInfoWithLiveness appInfo;
   private final RootSet rootSet;
   private final Set<DexCallSite> desugaredCallSites;
   private final InternalOptions options;
 
   public Minifier(
-      AppInfoWithLiveness appInfo,
+      AppView<AppInfoWithLiveness> appView,
       RootSet rootSet,
-      Set<DexCallSite> desugaredCallSites,
-      InternalOptions options) {
-    this.appInfo = appInfo;
+      Set<DexCallSite> desugaredCallSites) {
+    this.appView = appView;
+    this.appInfo = appView.appInfo();
     this.rootSet = rootSet;
     this.desugaredCallSites = desugaredCallSites;
-    this.options = options;
+    this.options = appView.options();
   }
 
   public NamingLens run(Timing timing) {
     assert options.enableMinification;
     timing.begin("MinifyClasses");
-    ClassNameMinifier classNameMinifier = new ClassNameMinifier(appInfo, rootSet, options);
+    ClassNameMinifier classNameMinifier = new ClassNameMinifier(appView, rootSet);
     ClassRenaming classRenaming = classNameMinifier.computeRenaming(timing);
     timing.end();
 
@@ -64,16 +66,14 @@
 
     timing.begin("MinifyMethods");
     MethodRenaming methodRenaming =
-        new MethodNameMinifier(appInfo, rootSet, options)
-            .computeRenaming(desugaredCallSites, timing);
+        new MethodNameMinifier(appView, rootSet).computeRenaming(desugaredCallSites, timing);
     timing.end();
 
     assert new MinifiedRenaming(classRenaming, methodRenaming, FieldRenaming.empty(), appInfo)
         .verifyNoCollisions(appInfo.classes(), appInfo.dexItemFactory);
 
     timing.begin("MinifyFields");
-    FieldRenaming fieldRenaming =
-        new FieldNameMinifier(appInfo, rootSet, options).computeRenaming(timing);
+    FieldRenaming fieldRenaming = new FieldNameMinifier(appView, rootSet).computeRenaming(timing);
     timing.end();
 
     NamingLens lens = new MinifiedRenaming(classRenaming, methodRenaming, fieldRenaming, appInfo);
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
index dc8925a..996f83f 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
@@ -386,24 +386,44 @@
             wildcard = wildcards.get(wildcardIndex);
             assert wildcard.isPattern();
             wildcardPattern = wildcard.asPattern();
+
             boolean includeSeparators = pattern.length() > (i + 1) && pattern.charAt(i + 1) == '*';
-            int nextPatternIndex = i + (includeSeparators ? 2 : 1);
-            // Fast cases for the common case where a pattern ends with '**' or '*'.
+            boolean includeAll =
+                includeSeparators && pattern.length() > (i + 2) && pattern.charAt(i + 2) == '*';
+            int nextPatternIndex = i + 1;
+            if (includeAll) {
+              nextPatternIndex += 2;
+            } else if (includeSeparators) {
+              nextPatternIndex += 1;
+            }
+
+            // Fast cases for the common case where a pattern ends with  '*', '**', or '***'.
             if (nextPatternIndex == pattern.length()) {
-              wildcardPattern.setCaptured(name.substring(nameIndex, name.length()));
+              wildcardPattern.setCaptured(name.substring(nameIndex));
+              if (includeAll) {
+                return true;
+              }
               if (includeSeparators) {
                 return kind == ClassOrType.CLASS || !isArrayType(name);
               }
               boolean hasSeparators = containsSeparatorsStartingAt(name, nameIndex);
               return !hasSeparators && (kind == ClassOrType.CLASS || !isArrayType(name));
             }
+
             // Match the rest of the pattern against the (non-empty) rest of the class name.
             for (int nextNameIndex = nameIndex; nextNameIndex < name.length(); nextNameIndex++) {
               wildcardPattern.setCaptured(name.substring(nameIndex, nextNameIndex));
-              if (!includeSeparators && name.charAt(nextNameIndex) == '.') {
-                return matchClassOrTypeNameImpl(
-                    pattern, nextPatternIndex, name, nextNameIndex, wildcards, wildcardIndex + 1,
-                    kind);
+              if (!includeSeparators) {
+                if (name.charAt(nextNameIndex) == '.') {
+                  return matchClassOrTypeNameImpl(
+                      pattern,
+                      nextPatternIndex,
+                      name,
+                      nextNameIndex,
+                      wildcards,
+                      wildcardIndex + 1,
+                      kind);
+                }
               }
               if (kind == ClassOrType.TYPE && name.charAt(nextNameIndex) == '[') {
                 return matchClassOrTypeNameImpl(
@@ -416,11 +436,12 @@
                 return true;
               }
             }
-            // Finally, check the case where the '*' or '**' eats all of the class name.
-            wildcardPattern.setCaptured(name.substring(nameIndex, name.length()));
+
+            // Finally, check the case where the '*', '**', or '***' eats all of the class name.
+            wildcardPattern.setCaptured(name.substring(nameIndex));
             return matchClassOrTypeNameImpl(
-                pattern, nextPatternIndex, name, name.length(), wildcards, wildcardIndex + 1,
-                kind);
+                pattern, nextPatternIndex, name, name.length(), wildcards, wildcardIndex + 1, kind);
+
           case '?':
             wildcard = wildcards.get(wildcardIndex);
             assert wildcard.isPattern();
@@ -432,6 +453,7 @@
             nameIndex++;
             wildcardIndex++;
             break;
+
           case '<':
             wildcard = wildcards.get(wildcardIndex);
             assert wildcard.isBackReference();
@@ -446,6 +468,7 @@
             wildcardIndex++;
             i = pattern.indexOf(">", i);
             break;
+
           default:
             if (nameIndex == name.length() || patternChar != name.charAt(nameIndex++)) {
               return false;
diff --git a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
index 7b6052a..4dcb52f 100644
--- a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
+++ b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.shaking.Enqueuer;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.ProguardConfiguration;
 import com.android.tools.r8.shaking.RootSetBuilder;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
@@ -74,9 +75,12 @@
         new RootSetBuilder(appView, program, configuration.getRules(), options).run(executor);
 
     Enqueuer enqueuer = new Enqueuer(appView, options, null, options.forceProguardCompatibility);
-    AppInfoWithSubtyping appInfo =
+    AppInfoWithLiveness appInfo =
         enqueuer.traceApplication(rootSet, configuration.getDontWarnPatterns(), executor, timing);
-    return new Minifier(appInfo.withLiveness(), rootSet, Collections.emptySet(), options)
+    return new Minifier(
+        new AppView<>(appInfo, GraphLense.getIdentityLense(), options),
+        rootSet,
+        Collections.emptySet())
         .run(timing);
   }
 
diff --git a/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java b/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
new file mode 100644
index 0000000..37a0215
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
@@ -0,0 +1,102 @@
+// Copyright (c) 2019, 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.b124357885;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.graph.DexAnnotationElement;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.DexValue.DexValueArray;
+import com.android.tools.r8.graph.DexValue.DexValueString;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.AnnotationSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import org.junit.Test;
+
+public class B124357885Test extends TestBase {
+
+  private void checkSignatureAnnotation(CodeInspector inspector, AnnotationSubject signature) {
+    DexAnnotationElement[] elements = signature.getAnnotation().elements;
+    assertEquals(1, elements.length);
+    assertEquals("value", elements[0].name.toString());
+    assertTrue(elements[0].value instanceof DexValueArray);
+    DexValueArray array = (DexValueArray) elements[0].value;
+    StringBuilder builder = new StringBuilder();
+    for (DexValue value : array.getValues()) {
+      assertTrue(value instanceof DexValueString);
+      builder.append(((DexValueString) value).value);
+    }
+    String fooImplFinalDescriptor =
+        DescriptorUtils.javaTypeToDescriptor(inspector.clazz(FooImpl.class).getFinalName());
+    StringBuilder expected =
+        new StringBuilder()
+            .append("()")
+            // Remove the final ; from the descriptor to add the generic type.
+            .append(fooImplFinalDescriptor.substring(0, fooImplFinalDescriptor.length() - 1))
+            .append("<Ljava/lang/String;>")
+            // Add the ; after the generic type.
+            .append(";");
+    assertEquals(expected.toString(), builder.toString());
+  }
+
+  @Test
+  public void test() throws Exception {
+    R8TestCompileResult compileResult = testForR8(Backend.DEX)
+        .addProgramClasses(Main.class, Service.class, Foo.class, FooImpl.class)
+        .addKeepMainRule(Main.class)
+        .addKeepRules("-keepattributes Signature,InnerClasses,EnclosingMethod")
+        .compile()
+        .inspect(inspector -> {
+          assertThat(inspector.clazz(Main.class), allOf(isPresent(), not(isRenamed())));
+          assertThat(inspector.clazz(Service.class), allOf(isPresent(), isRenamed()));
+          assertThat(inspector.clazz(Foo.class), not(isPresent()));
+          assertThat(inspector.clazz(FooImpl.class), allOf(isPresent(), isRenamed()));
+          // TODO(124477502): Using uniqueMethodWithName("fooList") does not work.
+          assertEquals(1, inspector.clazz(Service.class).allMethods().size());
+          MethodSubject fooList = inspector.clazz(Service.class).allMethods().get(0);
+          AnnotationSubject signature = fooList.annotation("dalvik.annotation.Signature");
+          checkSignatureAnnotation(inspector, signature);
+        });
+
+        String fooImplFinalName = compileResult.inspector().clazz(FooImpl.class).getFinalName();
+
+        compileResult
+            .run(Main.class)
+            .assertSuccessWithOutput(StringUtils.lines(fooImplFinalName, fooImplFinalName));
+  }
+}
+
+class Main {
+  public static void main(String... args) throws Exception {
+    Method method = Service.class.getMethod("fooList");
+    ParameterizedType type = (ParameterizedType) method.getGenericReturnType();
+    Class<?> rawType = (Class<?>) type.getRawType();
+    System.out.println(rawType.getName());
+
+    // Convince R8 we only use subtypes to get class merging of Foo into FooImpl.
+    Foo<String> foo = new FooImpl<>();
+    System.out.println(foo.getClass().getCanonicalName());
+  }
+}
+
+interface Service {
+  Foo<String> fooList();
+}
+
+interface Foo<T> {}
+
+class FooImpl<T> implements Foo<T> {}
diff --git a/src/test/java/com/android/tools/r8/proguard/rules/ProguardMatchAllRuleWithPrefixTest.java b/src/test/java/com/android/tools/r8/proguard/rules/ProguardMatchAllRuleWithPrefixTest.java
new file mode 100644
index 0000000..d5f456b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/proguard/rules/ProguardMatchAllRuleWithPrefixTest.java
@@ -0,0 +1,59 @@
+// Copyright (c) 2019, 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.proguard.rules;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+
+/** Regression test for b/124584385. */
+public class ProguardMatchAllRuleWithPrefixTest extends TestBase {
+
+  @Test
+  public void test() throws Exception {
+    CodeInspector inspector =
+        testForR8(Backend.DEX)
+            .addProgramClasses(TestClass.class)
+            .addKeepRules(
+                "-keep,allowobfuscation class com.android.tools.r8.*** {",
+                "  com.android.tools.r8.*** methodA();",
+                "  com.android.tools.r8.***Class methodB();",
+                "  com.android.tools.r8.***[] methodC();",
+                "  com.android.tools.r8.***Class[] methodD();",
+                "}")
+            .compile()
+            .inspector();
+
+    ClassSubject classSubject = inspector.clazz(TestClass.class);
+    assertThat(classSubject, isPresent());
+    assertThat(classSubject.uniqueMethodWithName("methodA"), isPresent());
+    assertThat(classSubject.uniqueMethodWithName("methodB"), isPresent());
+    assertThat(classSubject.uniqueMethodWithName("methodC"), isPresent());
+    assertThat(classSubject.uniqueMethodWithName("methodD"), isPresent());
+  }
+
+  static class TestClass {
+
+    TestClass methodA() {
+      return new TestClass();
+    }
+
+    TestClass methodB() {
+      return new TestClass();
+    }
+
+    TestClass[] methodC() {
+      return new TestClass[0];
+    }
+
+    TestClass[] methodD() {
+      return new TestClass[0];
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
index 5eb2f22..d1249ab 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
@@ -104,4 +104,9 @@
   public boolean hasLocalVariableTable() {
     throw new Unreachable("Cannot determine if an absent method has a local variable table");
   }
+
+  @Override
+  public AnnotationSubject annotation(String name) {
+    return new AbsentAnnotationSubject();
+  }
 }
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 fe98075..43cbf20 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
@@ -321,6 +321,14 @@
     return obfuscatedTypeName != null ? obfuscatedTypeName : originalTypeName;
   }
 
+  String getOriginalTypeName(String minifiedTypeName) {
+    String originalTypeName = null;
+    if (mapping != null) {
+      originalTypeName = mapType(obfuscatedToOriginalMapping, minifiedTypeName);
+    }
+    return originalTypeName != null ? originalTypeName : minifiedTypeName;
+  }
+
   InstructionSubject createInstructionSubject(Instruction instruction) {
     DexInstructionSubject dexInst = new DexInstructionSubject(instruction);
     if (dexInst.isInvoke()) {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
index 5643091..544c8f8 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.graph.DexDebugEvent;
 import com.android.tools.r8.graph.DexDebugInfo;
@@ -119,18 +120,14 @@
     //     X method(X) -> a
     //
     // whereas the final signature is for X.a is "a (a)"
-    String[] OriginalParameters = new String[signature.parameters.length];
-    for (int i = 0; i < OriginalParameters.length; i++) {
-      String obfuscated = signature.parameters[i];
-      String original = codeInspector.obfuscatedToOriginalMapping.get(obfuscated);
-      OriginalParameters[i] = original != null ? original : obfuscated;
+    String[] originalParameters = new String[signature.parameters.length];
+    for (int i = 0; i < originalParameters.length; i++) {
+      originalParameters[i] = codeInspector.getOriginalTypeName(signature.parameters[i]);
     }
-    String obfuscatedReturnType = signature.type;
-    String originalReturnType = codeInspector.obfuscatedToOriginalMapping.get(obfuscatedReturnType);
-    String returnType = originalReturnType != null ? originalReturnType : obfuscatedReturnType;
+    String returnType = codeInspector.getOriginalTypeName(signature.type);
 
     MethodSignature lookupSignature =
-        new MethodSignature(signature.name, returnType, OriginalParameters);
+        new MethodSignature(signature.name, returnType, originalParameters);
 
     MemberNaming memberNaming = clazz.naming.lookup(lookupSignature);
     return memberNaming != null ? (MethodSignature) memberNaming.getOriginalSignature() : signature;
@@ -261,4 +258,13 @@
   public String toString() {
     return dexMethod.toSourceString();
   }
+
+  @Override
+  public AnnotationSubject annotation(String name) {
+    DexAnnotation annotation = codeInspector.findAnnotation(name, dexMethod.annotations);
+    return annotation == null
+        ? new AbsentAnnotationSubject()
+        : new FoundAnnotationSubject(annotation);
+  }
+
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
index 9cecb1d..a32097b 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
@@ -65,4 +65,6 @@
   public boolean isMethodSubject() {
     return true;
   }
+
+  public abstract AnnotationSubject annotation(String name);
 }