Correctly handle '***' in ProguardTypeMatcher

Bug: 124584385
Change-Id: Ibe10c89cb036b0cd7feac9eeead1a25e0ddc033c
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..a59a08c 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,43 @@
             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 = 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 +435,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 +452,7 @@
             nameIndex++;
             wildcardIndex++;
             break;
+
           case '<':
             wildcard = wildcards.get(wildcardIndex);
             assert wildcard.isBackReference();
@@ -446,6 +467,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/proguard/rules/ProguardMatchAllRuleWithPrefixTest.java b/src/test/java/com/android/tools/r8/proguard/rules/ProguardMatchAllRuleWithPrefixTest.java
index 55731c9..d5f456b 100644
--- a/src/test/java/com/android/tools/r8/proguard/rules/ProguardMatchAllRuleWithPrefixTest.java
+++ b/src/test/java/com/android/tools/r8/proguard/rules/ProguardMatchAllRuleWithPrefixTest.java
@@ -8,24 +8,52 @@
 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.Ignore;
 import org.junit.Test;
 
 /** Regression test for b/124584385. */
 public class ProguardMatchAllRuleWithPrefixTest extends TestBase {
 
-  @Ignore("b/124584385")
   @Test
   public void test() throws Exception {
     CodeInspector inspector =
         testForR8(Backend.DEX)
             .addProgramClasses(TestClass.class)
-            .addKeepRules("-keep class com.android.tools.r8.***")
+            .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();
-    assertThat(inspector.clazz(TestClass.class), isPresent());
+
+    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 {}
+  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/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index aa083da..f585226 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 6581a47..dd7cd85 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
@@ -143,18 +143,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;