Merge "Collect wildcards while parsing Proguard configuration contents."
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAlwaysInlineRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardAlwaysInlineRule.java
index ab8342d..8d4a14b 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAlwaysInlineRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAlwaysInlineRule.java
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
-import java.util.Set;
+import java.util.List;
 
 public class ProguardAlwaysInlineRule extends ProguardConfigurationRule {
 
@@ -29,7 +29,7 @@
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
-      Set<ProguardMemberRule> memberRules) {
+      List<ProguardMemberRule> memberRules) {
     super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
         classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java
index 736c778..39d0335 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
-import java.util.Set;
+import java.util.List;
 
 public class ProguardAssumeNoSideEffectRule extends ProguardConfigurationRule {
 
@@ -28,7 +28,7 @@
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
-      Set<ProguardMemberRule> memberRules) {
+      List<ProguardMemberRule> memberRules) {
     super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
         classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java
index 97cb5c3..0dafb11 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
-import java.util.Set;
+import java.util.List;
 
 public class ProguardAssumeValuesRule extends ProguardConfigurationRule {
   public static class Builder extends ProguardClassSpecification.Builder {
@@ -27,7 +27,7 @@
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
-      Set<ProguardMemberRule> memberRules) {
+      List<ProguardMemberRule> memberRules) {
     super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
         classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java
index da5bb26..2461732 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
-import java.util.Set;
+import java.util.List;
 
 public class ProguardCheckDiscardRule extends ProguardConfigurationRule {
 
@@ -29,7 +29,7 @@
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
-      Set<ProguardMemberRule> memberRules) {
+      List<ProguardMemberRule> memberRules) {
     super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
         classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
index 0a71f43..fd1d305 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
@@ -14,6 +14,7 @@
 import java.util.List;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 
 public abstract class ProguardClassNameList {
 
@@ -74,6 +75,10 @@
 
   public abstract boolean matches(DexType type);
 
+  protected Iterable<String> getWildcards() {
+    return ImmutableList.of();
+  }
+
   public abstract void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer);
 
   private static class EmptyClassNameList extends ProguardClassNameList {
@@ -135,6 +140,11 @@
     }
 
     @Override
+    protected Iterable<String> getWildcards() {
+      return className.getWildcards();
+    }
+
+    @Override
     public void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer) {
       consumer.accept(className);
     }
@@ -180,6 +190,14 @@
     }
 
     @Override
+    protected Iterable<String> getWildcards() {
+      return classNames.stream()
+          .map(ProguardTypeMatcher::getWildcards)
+          .flatMap(it -> StreamSupport.stream(it.spliterator(), false))
+          .collect(Collectors.toList());
+    }
+
+    @Override
     public void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer) {
       classNames.forEach(consumer);
     }
@@ -230,6 +248,14 @@
     }
 
     @Override
+    protected Iterable<String> getWildcards() {
+      return classNames.keySet().stream()
+          .map(ProguardTypeMatcher::getWildcards)
+          .flatMap(it -> StreamSupport.stream(it.spliterator(), false))
+          .collect(Collectors.toList());
+    }
+
+    @Override
     public void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer) {
       classNames.object2BooleanEntrySet().forEach(entry -> consumer.accept(entry.getKey()));
     }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
index cb81639..653f0cf 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
@@ -4,10 +4,10 @@
 package com.android.tools.r8.shaking;
 
 import com.android.tools.r8.utils.StringUtils;
-import java.util.Collections;
-import java.util.LinkedHashSet;
+import com.google.common.collect.ImmutableList;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Objects;
-import java.util.Set;
 
 public abstract class ProguardClassSpecification {
 
@@ -22,16 +22,16 @@
     protected ProguardTypeMatcher inheritanceAnnotation;
     protected ProguardTypeMatcher inheritanceClassName;
     protected boolean inheritanceIsExtends = false;
-    protected Set<ProguardMemberRule> memberRules = new LinkedHashSet<>();
+    protected List<ProguardMemberRule> memberRules = new LinkedList<>();
 
     protected Builder() {
     }
 
-    public Set<ProguardMemberRule> getMemberRules() {
+    public List<ProguardMemberRule> getMemberRules() {
       return memberRules;
     }
 
-    public void setMemberRules(Set<ProguardMemberRule> memberRules) {
+    public void setMemberRules(List<ProguardMemberRule> memberRules) {
       this.memberRules = memberRules;
     }
 
@@ -113,7 +113,7 @@
 
     protected void matchAllSpecification() {
       setClassNames(ProguardClassNameList.singletonList(ProguardTypeMatcher.defaultAllMatcher()));
-      setMemberRules(Collections.singleton(ProguardMemberRule.defaultKeepAllRule()));
+      setMemberRules(ImmutableList.of(ProguardMemberRule.defaultKeepAllRule()));
     }
   }
 
@@ -126,7 +126,7 @@
   private final ProguardTypeMatcher inheritanceAnnotation;
   private final ProguardTypeMatcher inheritanceClassName;
   private final boolean inheritanceIsExtends;
-  private final Set<ProguardMemberRule> memberRules;
+  private final List<ProguardMemberRule> memberRules;
 
   protected ProguardClassSpecification(
       ProguardTypeMatcher classAnnotation,
@@ -138,7 +138,7 @@
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
-      Set<ProguardMemberRule> memberRules) {
+      List<ProguardMemberRule> memberRules) {
     this.classAnnotation = classAnnotation;
     this.classAccessFlags = classAccessFlags;
     this.negatedClassAccessFlags = negatedClassAccessFlags;
@@ -152,7 +152,7 @@
     this.memberRules = memberRules;
   }
 
-  public Set<ProguardMemberRule> getMemberRules() {
+  public List<ProguardMemberRule> getMemberRules() {
     return memberRules;
   }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 9b89374..568ba54 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import static com.android.tools.r8.utils.DescriptorUtils.javaTypeToDescriptor;
+
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -16,7 +18,6 @@
 import com.android.tools.r8.shaking.ProguardConfiguration.Builder;
 import com.android.tools.r8.shaking.ProguardTypeMatcher.ClassOrType;
 import com.android.tools.r8.shaking.ProguardTypeMatcher.MatchSpecificType;
-import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.IdentifierUtils;
 import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
 import com.android.tools.r8.utils.LongInterval;
@@ -33,9 +34,12 @@
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 public class ProguardConfigurationParser {
 
@@ -511,7 +515,8 @@
         // If there are no member rules, a default rule for the parameterless constructor
         // applies. So we add that here.
         ProguardMemberRule.Builder defaultRuleBuilder = ProguardMemberRule.builder();
-        defaultRuleBuilder.setName(Constants.INSTANCE_INITIALIZER_NAME);
+        defaultRuleBuilder.setName(
+            IdentifierPatternWithWildcards.withoutWildcards(Constants.INSTANCE_INITIALIZER_NAME));
         defaultRuleBuilder.setRuleType(ProguardMemberType.INIT);
         defaultRuleBuilder.setArguments(Collections.emptyList());
         keepRuleBuilder.getMemberRules().add(defaultRuleBuilder.build());
@@ -565,10 +570,33 @@
       if (acceptString("-keep")) {
         ProguardKeepRule subsequentRule = parseKeepRule();
         ifRuleBuilder.setSubsequentRule(subsequentRule);
-        return ifRuleBuilder.build();
+        ProguardIfRule ifRule = ifRuleBuilder.build();
+        verifyWildcardRange(ifRule.getWildcards());
+        return ifRule;
       }
       throw reporter.fatalError(new StringDiagnostic(
-          "Option -if without a subsequent keep rule.", origin, getPosition(optionStart)));
+          "Expecting '-keep' option after '-if' option.", origin, getPosition(optionStart)));
+    }
+
+    private void verifyWildcardRange(Iterable<String> wildcards) {
+      Pattern backReference = Pattern.compile("<(\\d+)>");
+      int i = 1;
+      Iterator<String> iterator = wildcards.iterator();
+      while (iterator.hasNext()) {
+        String wildcard = iterator.next();
+        Matcher m = backReference.matcher(wildcard);
+        if (m.matches()) {
+          int n = Integer.parseInt(m.group(1));
+          if (i <= n) {
+            throw reporter.fatalError(new StringDiagnostic(
+                "Wildcard <" + n + "> is invalid.", origin, getPosition()));
+          }
+        } else {
+          // Increase the index of wildcards for non-back-reference only
+          // to not allow one back-reference to point to another back-reference
+          i++;
+        }
+      }
     }
 
     private void parseClassSpec(
@@ -639,14 +667,16 @@
       skipWhitespace();
       int startPosition = position;
       if (acceptChar('@')) {
-        String className = parseClassName();
+        IdentifierPatternWithWildcards identifierPatternWithWildcards = parseClassName();
+        String className = identifierPatternWithWildcards.pattern;
         if (className.equals("interface")) {
           // Not an annotation after all but a class type. Move position back to start
           // so this can be dealt with as a class type instead.
           position = startPosition;
           return null;
         }
-        return ProguardTypeMatcher.create(className, ClassOrType.CLASS, dexItemFactory);
+        return ProguardTypeMatcher.create(
+            identifierPatternWithWildcards, ClassOrType.CLASS, dexItemFactory);
       }
       return null;
     }
@@ -815,13 +845,14 @@
         ruleBuilder.setRuleType(ProguardMemberType.ALL_FIELDS);
       } else if (acceptString("<init>")) {
         ruleBuilder.setRuleType(ProguardMemberType.INIT);
-        ruleBuilder.setName("<init>");
+        ruleBuilder.setName(IdentifierPatternWithWildcards.withoutWildcards("<init>"));
         ruleBuilder.setArguments(parseArgumentList());
       } else {
-        String first = acceptIdentifierWithBackreference(IdentifierType.ANY);
+        IdentifierPatternWithWildcards first =
+            acceptIdentifierWithBackreference(IdentifierType.ANY);
         if (first != null) {
           skipWhitespace();
-          if (first.equals("*") && hasNextChar(';')) {
+          if (first.pattern.equals("*") && hasNextChar(';')) {
             ruleBuilder.setRuleType(ProguardMemberType.ALL);
           } else {
             if (hasNextChar('(')) {
@@ -829,7 +860,8 @@
               ruleBuilder.setName(first);
               ruleBuilder.setArguments(parseArgumentList());
             } else {
-              String second = acceptIdentifierWithBackreference(IdentifierType.ANY);
+              IdentifierPatternWithWildcards second =
+                  acceptIdentifierWithBackreference(IdentifierType.ANY);
               if (second != null) {
                 skipWhitespace();
                 if (hasNextChar('(')) {
@@ -880,7 +912,7 @@
                               .getTypeMatcher()).type;
                           DexType fieldClass =
                               dexItemFactory.createType(
-                                  DescriptorUtils.javaTypeToDescriptor(
+                                  javaTypeToDescriptor(
                                       qualifiedFieldNameOrInteger.substring(0, lastDotIndex)));
                           DexString fieldName =
                               dexItemFactory.createString(
@@ -918,13 +950,16 @@
         return arguments;
       }
       if (acceptString("...")) {
-        arguments
-            .add(ProguardTypeMatcher.create("...", ClassOrType.TYPE, dexItemFactory));
+        arguments.add(ProguardTypeMatcher.create(
+            IdentifierPatternWithWildcards.withoutWildcards("..."),
+            ClassOrType.TYPE,
+            dexItemFactory));
       } else {
-        for (String name = parseClassName(); name != null; name =
-            acceptChar(',') ? parseClassName() : null) {
-          arguments
-              .add(ProguardTypeMatcher.create(name, ClassOrType.TYPE, dexItemFactory));
+        for (IdentifierPatternWithWildcards identifierPatternWithWildcards = parseClassName();
+            identifierPatternWithWildcards != null;
+            identifierPatternWithWildcards = acceptChar(',') ? parseClassName() : null) {
+          arguments.add(ProguardTypeMatcher.create(
+              identifierPatternWithWildcards, ClassOrType.TYPE, dexItemFactory));
           skipWhitespace();
         }
       }
@@ -1126,13 +1161,17 @@
       return acceptString(CLASS_NAME_PREDICATE);
     }
 
-    private String acceptIdentifierWithBackreference(IdentifierType kind) {
+    private IdentifierPatternWithWildcards acceptIdentifierWithBackreference(IdentifierType kind) {
+      ImmutableList.Builder<String> wildcardsCollector = ImmutableList.builder();
+      StringBuilder currentAsterisks = null;
       StringBuilder currentBackreference = null;
       skipWhitespace();
       int start = position;
       int end = position;
       while (!eof(end)) {
         int current = contents.codePointAt(end);
+        // Should not be both in asterisk collecting state and back reference collecting state.
+        assert currentAsterisks == null || currentBackreference == null;
         if (currentBackreference != null) {
           if (current == '>') {
             try {
@@ -1146,29 +1185,55 @@
                   "Wildcard <" + currentBackreference.toString() + "> is invalid.",
                   origin, getPosition()));
             }
+            wildcardsCollector.add("<" + currentBackreference.toString() + ">");
             currentBackreference = null;
+            end += Character.charCount(current);
+            continue;
           } else if (('0' <= current && current <= '9')
-              // Only collect integer literal for the backreference.
+              // Only collect integer literal for the back reference.
               || (current == '-' && currentBackreference.length() == 0)) {
             currentBackreference.append((char) current);
+            end += Character.charCount(current);
+            continue;
           } else if (kind == IdentifierType.CLASS_NAME) {
             throw reporter.fatalError(new StringDiagnostic(
                 "Use of generics not allowed for java type.", origin, getPosition()));
           } else {
             // If not parsing a class name allow identifiers including <'s by canceling the
-            // collection of the backreference.
+            // collection of the back reference.
             currentBackreference = null;
           }
+        } else if (currentAsterisks != null) {
+          if (current == '*') {
+            currentAsterisks.append((char) current);
+            end += Character.charCount(current);
+            continue;
+          } else {
+            wildcardsCollector.add(currentAsterisks.toString());
+            currentAsterisks = null;
+          }
+        }
+        // From now on, neither in asterisk collecting state nor back reference collecting state.
+        assert currentAsterisks == null && currentBackreference == null;
+        if (current == '*') {
+          currentAsterisks = new StringBuilder();
+          currentAsterisks.append((char) current);
+          end += Character.charCount(current);
+        } else if (current == '?' || current == '%') {
+          wildcardsCollector.add(String.valueOf((char) current));
           end += Character.charCount(current);
         } else if (CLASS_NAME_PREDICATE.test(current) || current == '>') {
           end += Character.charCount(current);
         } else if (current == '<') {
           currentBackreference = new StringBuilder();
-          ++end;
+          end += Character.charCount(current);
         } else {
           break;
         }
       }
+      if (currentAsterisks != null) {
+        wildcardsCollector.add(currentAsterisks.toString());
+      }
       if (kind == IdentifierType.CLASS_NAME && currentBackreference != null) {
         // Proguard 6 reports this error message, so try to be compatible.
         throw reporter.fatalError(
@@ -1178,7 +1243,9 @@
         return null;
       }
       position = end;
-      return contents.substring(start, end);
+      return new IdentifierPatternWithWildcards(
+          contents.substring(start, end),
+          wildcardsCollector.build());
     }
 
     private String acceptFieldNameOrIntegerForReturn() {
@@ -1292,8 +1359,9 @@
       return name == null ? "" : name;
     }
 
-    private String parseClassName() throws ProguardRuleParserException {
-      String name = acceptIdentifierWithBackreference(IdentifierType.CLASS_NAME);
+    private IdentifierPatternWithWildcards parseClassName() throws ProguardRuleParserException {
+      IdentifierPatternWithWildcards name =
+          acceptIdentifierWithBackreference(IdentifierType.CLASS_NAME);
       if (name == null) {
         throw parseError("Class name expected");
       }
@@ -1408,4 +1476,22 @@
       return position - lineStartPosition + 1 /* column starts at 1 */;
     }
   }
+
+  static class IdentifierPatternWithWildcards {
+    final String pattern;
+    final List<String> wildcards;
+
+    IdentifierPatternWithWildcards(String pattern, List<String> wildcards) {
+      this.pattern = pattern;
+      this.wildcards = wildcards;
+    }
+
+    static IdentifierPatternWithWildcards withoutWildcards(String pattern) {
+      return new IdentifierPatternWithWildcards(pattern, ImmutableList.of());
+    }
+
+    boolean isMatchAllNames() {
+      return pattern.equals("*");
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
index af1948c..9791def 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
@@ -4,7 +4,11 @@
 package com.android.tools.r8.shaking;
 
 import com.android.tools.r8.utils.StringUtils;
-import java.util.Set;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 
 public abstract class ProguardConfigurationRule extends ProguardClassSpecification {
   ProguardConfigurationRule(
@@ -17,7 +21,7 @@
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
-      Set<ProguardMemberRule> memberRules) {
+      List<ProguardMemberRule> memberRules) {
     super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
         classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
   }
@@ -32,6 +36,25 @@
     return false;
   }
 
+  protected Iterable<String> getWildcards() {
+    ProguardTypeMatcher classAnnotation = getClassAnnotation();
+    ProguardTypeMatcher inheritanceAnnotation = getInheritanceAnnotation();
+    ProguardTypeMatcher inheritanceClassName = getInheritanceClassName();
+    List<ProguardMemberRule> memberRules = getMemberRules();
+    return Iterables.concat(
+        classAnnotation != null ? classAnnotation.getWildcards() : ImmutableList.of(),
+        getClassNames().getWildcards(),
+        inheritanceAnnotation != null ? inheritanceAnnotation.getWildcards() : ImmutableList.of(),
+        inheritanceClassName != null ? inheritanceClassName.getWildcards() : ImmutableList.of(),
+        memberRules != null
+            ? memberRules.stream()
+                .map(ProguardMemberRule::getWildcards)
+                .flatMap(it -> StreamSupport.stream(it.spliterator(), false))
+                .collect(Collectors.toList())
+            : ImmutableList.of()
+    );
+  }
+
   @Override
   public boolean equals(Object o) {
     if (!(o instanceof ProguardConfigurationRule)) {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
index 5852cd9..400422f 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.graph.DexItem;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards;
 import com.google.common.collect.ImmutableList;
 import java.util.Arrays;
 import java.util.List;
@@ -30,7 +31,7 @@
     if (clazz.hasDefaultInitializer()) {
       ProguardMemberRule.Builder memberRuleBuilder = ProguardMemberRule.builder();
       memberRuleBuilder.setRuleType(ProguardMemberType.INIT);
-      memberRuleBuilder.setName("<init>");
+      memberRuleBuilder.setName(IdentifierPatternWithWildcards.withoutWildcards("<init>"));
       memberRuleBuilder.setArguments(ImmutableList.of());
       builder.getMemberRules().add(memberRuleBuilder.build());
     }
@@ -54,7 +55,8 @@
     ProguardMemberRule.Builder memberRuleBuilder = ProguardMemberRule.builder();
     memberRuleBuilder.setRuleType(ProguardMemberType.FIELD);
     memberRuleBuilder.getAccessFlags().setFlags(field.accessFlags);
-    memberRuleBuilder.setName(field.field.name.toString());
+    memberRuleBuilder.setName(
+        IdentifierPatternWithWildcards.withoutWildcards(field.field.name.toString()));
     memberRuleBuilder.setTypeMatcher(ProguardTypeMatcher.create(field.field.type));
     builder.getMemberRules().add(memberRuleBuilder.build());
     return builder.build();
@@ -77,7 +79,8 @@
     ProguardMemberRule.Builder memberRuleBuilder = ProguardMemberRule.builder();
     memberRuleBuilder.setRuleType(ProguardMemberType.METHOD);
     memberRuleBuilder.getAccessFlags().setFlags(method.accessFlags);
-    memberRuleBuilder.setName(method.method.name.toString());
+    memberRuleBuilder.setName(
+        IdentifierPatternWithWildcards.withoutWildcards(method.method.name.toString()));
     memberRuleBuilder.setTypeMatcher(ProguardTypeMatcher.create(method.method.proto.returnType));
     List<ProguardTypeMatcher> arguments = Arrays.stream(method.method.proto.parameters.values)
         .map(ProguardTypeMatcher::create)
@@ -96,13 +99,15 @@
       DexField field = (DexField) item;
       holderType = field.getHolder();
       memberRuleBuilder.setRuleType(ProguardMemberType.FIELD);
-      memberRuleBuilder.setName(field.name.toString());
+      memberRuleBuilder.setName(
+          IdentifierPatternWithWildcards.withoutWildcards(field.name.toString()));
       memberRuleBuilder.setTypeMatcher(ProguardTypeMatcher.create(field.type));
     } else {
       DexMethod method = (DexMethod) item;
       holderType = method.getHolder();
       memberRuleBuilder.setRuleType(ProguardMemberType.METHOD);
-      memberRuleBuilder.setName(method.name.toString());
+      memberRuleBuilder.setName(
+          IdentifierPatternWithWildcards.withoutWildcards(method.name.toString()));
       memberRuleBuilder.setTypeMatcher(ProguardTypeMatcher.create(method.proto.returnType));
       List<ProguardTypeMatcher> arguments = Arrays.stream(method.proto.parameters.values)
           .map(ProguardTypeMatcher::create)
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java
index f0d7c63..07ffd85 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
-import java.util.Set;
+import java.util.List;
 
 public class ProguardIdentifierNameStringRule extends ProguardConfigurationRule {
 
@@ -28,7 +28,7 @@
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
-      Set<ProguardMemberRule> memberRules) {
+      List<ProguardMemberRule> memberRules) {
     super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
         classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
index 9659954..67a0572 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
@@ -3,7 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
-import java.util.Set;
+import com.google.common.collect.Iterables;
+import java.util.List;
 
 public class ProguardIfRule extends ProguardKeepRule {
 
@@ -32,7 +33,7 @@
       ProguardClassType classType, ProguardClassNameList classNames,
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName, boolean inheritanceIsExtends,
-      Set<ProguardMemberRule> memberRules,
+      List<ProguardMemberRule> memberRules,
       ProguardKeepRule subsequentRule) {
     super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
         classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules,
@@ -45,6 +46,11 @@
   }
 
   @Override
+  protected Iterable<String> getWildcards() {
+    return Iterables.concat(super.getWildcards(), subsequentRule.getWildcards());
+  }
+
+  @Override
   public boolean equals(Object o) {
     if (!(o instanceof ProguardIfRule)) {
       return false;
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java
index c4731c6..66fdd50 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
-import java.util.Set;
+import java.util.List;
 
 public class ProguardKeepPackageNamesRule extends ProguardConfigurationRule {
 
@@ -29,7 +29,7 @@
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
-      Set<ProguardMemberRule> memberRules) {
+      List<ProguardMemberRule> memberRules) {
     super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
         classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
index d3d6705..8756f1a 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
-import java.util.Set;
+import java.util.List;
 import java.util.function.Consumer;
 
 public class ProguardKeepRule extends ProguardConfigurationRule {
@@ -11,8 +11,8 @@
   public static class Builder extends ProguardClassSpecification.Builder {
 
     private ProguardKeepRuleType type;
-    private final ProguardKeepRuleModifiers.Builder modifiersBuilder
-        = ProguardKeepRuleModifiers.builder();
+    private final ProguardKeepRuleModifiers.Builder modifiersBuilder =
+        ProguardKeepRuleModifiers.builder();
 
     protected Builder() {}
 
@@ -44,7 +44,7 @@
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
-      Set<ProguardMemberRule> memberRules,
+      List<ProguardMemberRule> memberRules,
       ProguardKeepRuleType type,
       ProguardKeepRuleModifiers modifiers) {
     super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
index 5e1293f..1e089a9 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
@@ -7,9 +7,14 @@
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards;
 import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.util.Collections;
 import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 
 public class ProguardMemberRule {
 
@@ -19,8 +24,8 @@
     private ProguardAccessFlags accessFlags = new ProguardAccessFlags();
     private ProguardAccessFlags negatedAccessFlags = new ProguardAccessFlags();
     private ProguardMemberType ruleType;
-    private ProguardNameMatcher name;
     private ProguardTypeMatcher type;
+    private ProguardNameMatcher name;
     private List<ProguardTypeMatcher> arguments;
     private ProguardMemberRuleReturnValue returnValue;
 
@@ -50,10 +55,6 @@
       this.ruleType = ruleType;
     }
 
-    public void setName(String name) {
-      this.name = ProguardNameMatcher.create(name);
-    }
-
     public ProguardTypeMatcher getTypeMatcher() {
       return type;
     }
@@ -62,6 +63,10 @@
       this.type = type;
     }
 
+    public void setName(IdentifierPatternWithWildcards identifierPatternWithWildcards) {
+      this.name = ProguardNameMatcher.create(identifierPatternWithWildcards);
+    }
+
     public void setArguments(List<ProguardTypeMatcher> arguments) {
       this.arguments = arguments;
     }
@@ -76,8 +81,8 @@
 
     public ProguardMemberRule build() {
       assert isValid();
-      return new ProguardMemberRule(annotation, accessFlags, negatedAccessFlags, ruleType, name,
-          type, arguments, returnValue);
+      return new ProguardMemberRule(annotation, accessFlags, negatedAccessFlags, ruleType, type,
+          name, arguments, returnValue);
     }
   }
 
@@ -85,8 +90,8 @@
   private final ProguardAccessFlags accessFlags;
   private final ProguardAccessFlags negatedAccessFlags;
   private final ProguardMemberType ruleType;
-  private final ProguardNameMatcher name;
   private final ProguardTypeMatcher type;
+  private final ProguardNameMatcher name;
   private final List<ProguardTypeMatcher> arguments;
   private final ProguardMemberRuleReturnValue returnValue;
 
@@ -95,17 +100,17 @@
       ProguardAccessFlags accessFlags,
       ProguardAccessFlags negatedAccessFlags,
       ProguardMemberType ruleType,
-      ProguardNameMatcher name,
       ProguardTypeMatcher type,
+      ProguardNameMatcher name,
       List<ProguardTypeMatcher> arguments,
       ProguardMemberRuleReturnValue returnValue) {
     this.annotation = annotation;
     this.accessFlags = accessFlags;
     this.negatedAccessFlags = negatedAccessFlags;
     this.ruleType = ruleType;
-    this.name = name;
     this.type = type;
-    this.arguments = arguments != null ? ImmutableList.copyOf(arguments) : null;
+    this.name = name;
+    this.arguments = arguments != null ? Collections.unmodifiableList(arguments) : null;
     this.returnValue = returnValue;
   }
 
@@ -132,14 +137,14 @@
     return ruleType;
   }
 
-  public ProguardNameMatcher getName() {
-    return name;
-  }
-
   public ProguardTypeMatcher getType() {
     return type;
   }
 
+  public ProguardNameMatcher getName() {
+    return name;
+  }
+
   public List<ProguardTypeMatcher> getArguments() {
     return arguments;
   }
@@ -261,6 +266,20 @@
     return false;
   }
 
+  Iterable<String> getWildcards() {
+    return Iterables.concat(
+        annotation != null ? annotation.getWildcards() : ImmutableList.of(),
+        type != null ? type.getWildcards() : ImmutableList.of(),
+        name != null ? name.getWildcards() : ImmutableList.of(),
+        arguments != null
+            ? arguments.stream()
+                .map(ProguardTypeMatcher::getWildcards)
+                .flatMap(it -> StreamSupport.stream(it.spliterator(), false))
+                .collect(Collectors.toList())
+            : ImmutableList.of()
+    );
+  }
+
   @Override
   public boolean equals(Object o) {
     if (!(o instanceof ProguardMemberRule)) {
@@ -296,8 +315,8 @@
     result = 31 * result + accessFlags.hashCode();
     result = 31 * result + negatedAccessFlags.hashCode();
     result = 31 * result + (ruleType != null ? ruleType.hashCode() : 0);
-    result = 31 * result + (name != null ? name.hashCode() : 0);
     result = 31 * result + (type != null ? type.hashCode() : 0);
+    result = 31 * result + (name != null ? name.hashCode() : 0);
     result = 31 * result + (arguments != null ? arguments.hashCode() : 0);
     return result;
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java b/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java
index 9286d0a..b8f583d 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java
@@ -3,21 +3,25 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+
 public abstract class ProguardNameMatcher {
 
   private static final ProguardNameMatcher MATCH_ALL_NAMES = new MatchAllNames();
 
   private ProguardNameMatcher() {
-
   }
 
-  public static ProguardNameMatcher create(String pattern) {
-    if (pattern.equals("*")) {
+  public static ProguardNameMatcher create(
+      IdentifierPatternWithWildcards identifierPatternWithWildcards) {
+    if (identifierPatternWithWildcards.isMatchAllNames()) {
       return MATCH_ALL_NAMES;
-    } else if (pattern.contains("*") || pattern.contains("?")) {
-      return new MatchNamePattern(pattern);
+    } else if (identifierPatternWithWildcards.wildcards.isEmpty()) {
+      return new MatchSpecificName(identifierPatternWithWildcards.pattern);
     } else {
-      return new MatchSpecificName(pattern);
+      return new MatchNamePattern(identifierPatternWithWildcards);
     }
   }
 
@@ -63,6 +67,10 @@
 
   public abstract boolean matches(String name);
 
+  protected Iterable<String> getWildcards() {
+    return ImmutableList.of();
+  }
+
   private static class MatchAllNames extends ProguardNameMatcher {
 
     @Override
@@ -71,6 +79,11 @@
     }
 
     @Override
+    protected Iterable<String> getWildcards() {
+      return ImmutableList.of("*");
+    }
+
+    @Override
     public String toString() {
       return "*";
     }
@@ -79,9 +92,11 @@
   private static class MatchNamePattern extends ProguardNameMatcher {
 
     private final String pattern;
+    private final List<String> wildcards;
 
-    MatchNamePattern(String pattern) {
-      this.pattern = pattern;
+    MatchNamePattern(IdentifierPatternWithWildcards identifierPatternWithWildcards) {
+      this.pattern = identifierPatternWithWildcards.pattern;
+      this.wildcards = identifierPatternWithWildcards.wildcards;
     }
 
     @Override
@@ -90,6 +105,11 @@
     }
 
     @Override
+    protected Iterable<String> getWildcards() {
+      return wildcards;
+    }
+
+    @Override
     public String toString() {
       return pattern;
     }
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 809278f..51d11bb 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
@@ -3,9 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import static com.android.tools.r8.utils.DescriptorUtils.javaTypeToDescriptor;
+
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
 
 public abstract class ProguardTypeMatcher {
 
@@ -25,6 +29,10 @@
 
   public abstract boolean matches(DexType type);
 
+  protected Iterable<String> getWildcards() {
+    return ImmutableList.of();
+  }
+
   @Override
   public abstract String toString();
 
@@ -32,12 +40,14 @@
     return false;
   }
 
-  public static ProguardTypeMatcher create(String pattern, ClassOrType kind,
+  public static ProguardTypeMatcher create(
+      IdentifierPatternWithWildcards identifierPatternWithWildcards,
+      ClassOrType kind,
       DexItemFactory dexItemFactory) {
-    if (pattern == null) {
+    if (identifierPatternWithWildcards == null || identifierPatternWithWildcards.pattern == null) {
       return null;
     }
-    switch (pattern) {
+    switch (identifierPatternWithWildcards.pattern) {
       case MATCH_ALL_PATTERN:
         return MatchAllTypes.MATCH_ALL_TYPES;
       case MATCH_ANY_ARG_SEQUENCE_PATTERN:
@@ -49,11 +59,11 @@
       case MATCH_BASIC_PATTERN:
         return MatchBasicTypes.MATCH_BASIC_TYPES;
       default:
-        if (!pattern.contains("*") && !pattern.contains("%") && !pattern.contains("?")) {
-          return new MatchSpecificType(
-              dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(pattern)));
+        if (identifierPatternWithWildcards.wildcards.isEmpty()) {
+          return new MatchSpecificType(dexItemFactory.createType(
+              javaTypeToDescriptor(identifierPatternWithWildcards.pattern)));
         }
-        return new MatchTypePattern(pattern, kind);
+        return new MatchTypePattern(identifierPatternWithWildcards, kind);
     }
   }
 
@@ -85,6 +95,11 @@
     }
 
     @Override
+    protected Iterable<String> getWildcards() {
+      return ImmutableList.of(MATCH_ALL_PATTERN);
+    }
+
+    @Override
     public String toString() {
       return MATCH_ALL_PATTERN;
     }
@@ -150,6 +165,11 @@
     }
 
     @Override
+    protected Iterable<String> getWildcards() {
+      return ImmutableList.of(pattern);
+    }
+
+    @Override
     public String toString() {
       return pattern;
     }
@@ -175,6 +195,11 @@
     }
 
     @Override
+    protected Iterable<String> getWildcards() {
+      return ImmutableList.of(MATCH_BASIC_PATTERN);
+    }
+
+    @Override
     public String toString() {
       return MATCH_BASIC_PATTERN;
     }
@@ -230,10 +255,13 @@
   private static class MatchTypePattern extends ProguardTypeMatcher {
 
     private final String pattern;
+    private final List<String> wildcards;
     private final ClassOrType kind;
 
-    private MatchTypePattern(String pattern, ClassOrType kind) {
-      this.pattern = pattern;
+    private MatchTypePattern(
+        IdentifierPatternWithWildcards identifierPatternWithWildcards, ClassOrType kind) {
+      this.pattern = identifierPatternWithWildcards.pattern;
+      this.wildcards = identifierPatternWithWildcards.wildcards;
       this.kind = kind;
     }
 
@@ -244,6 +272,11 @@
       return matchClassOrTypeNameImpl(pattern, 0, typeName, 0, kind);
     }
 
+    @Override
+    protected Iterable<String> getWildcards() {
+      return wildcards;
+    }
+
     private static boolean matchClassOrTypeNameImpl(
         String pattern, int patternIndex, String className, int nameIndex, ClassOrType kind) {
       for (int i = patternIndex; i < pattern.length(); i++) {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java
index ef83926..73b5538 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
-import java.util.Set;
+import java.util.List;
 
 public class ProguardWhyAreYouKeepingRule extends ProguardConfigurationRule {
 
@@ -29,7 +29,7 @@
       ProguardTypeMatcher inheritanceAnnotation,
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
-      Set<ProguardMemberRule> memberRules) {
+      List<ProguardMemberRule> memberRules) {
     super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
         classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
   }
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 43a443e..e978b86 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -1183,6 +1183,11 @@
     return result.stdout;
   }
 
+  public static ProcessResult runProguardRaw(Path inJar, Path outJar, List<Path> config, Path map)
+      throws IOException {
+    return runProguardRaw(getProguardScript(), inJar, outJar, config, map);
+  }
+
   public static ProcessResult runProguardRaw(Path inJar, Path outJar, Path config, Path map)
       throws IOException {
     return runProguardRaw(getProguardScript(), inJar, outJar, ImmutableList.of(config), map);
@@ -1204,6 +1209,11 @@
   }
 
   public static ProcessResult runProguard6Raw(
+      Path inJar, Path outJar, List<Path> config, Path map) throws IOException {
+    return runProguardRaw(getProguard6Script(), inJar, outJar, config, map);
+  }
+
+  public static ProcessResult runProguard6Raw(
       Path inJar, Path outJar, Path lib, Path config, Path map) throws IOException {
     return runProguardRaw(getProguard6Script(), inJar, outJar, lib, ImmutableList.of(config), map);
   }
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index 89a7291..2dfe42e 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -30,6 +30,7 @@
 import com.android.tools.r8.origin.PathOrigin;
 import com.android.tools.r8.position.TextPosition;
 import com.android.tools.r8.position.TextRange;
+import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards;
 import com.android.tools.r8.utils.AbortException;
 import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
 import com.android.tools.r8.utils.DexInspector;
@@ -46,7 +47,6 @@
 import java.util.List;
 import java.util.function.Function;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 
 class EmptyMainClassForProguardTests {
@@ -205,7 +205,8 @@
     assertEquals("some.library.Class", rule.getInheritanceClassName().toString());
     ProguardMemberRule memberRule = rule.getMemberRules().iterator().next();
     assertTrue(memberRule.getAccessFlags().isProtected());
-    assertEquals(ProguardNameMatcher.create("getContents"), memberRule.getName());
+    assertEquals(ProguardNameMatcher.create(
+        IdentifierPatternWithWildcards.withoutWildcards("getContents")), memberRule.getName());
     assertEquals("java.lang.Object[][]", memberRule.getType().toString());
     assertEquals(ProguardMemberType.METHOD, memberRule.getRuleType());
     assertEquals(0, memberRule.getArguments().size());
@@ -1410,10 +1411,12 @@
     assertEquals(ProguardKeepRuleType.KEEP, if0.subsequentRule.getType());
     assertEquals("**$D<2>", if0.subsequentRule.getClassNames().toString());
     // TODO(b/73800755): Test <2> matches with expected wildcard: ** after '$R'.
+
+    verifyWithProguard6(proguardConfig);
   }
 
   @Test
-  public void parse_if_nthWildcard_notNumber() throws Exception {
+  public void parse_if_nthWildcard_notNumber_literalN() throws Exception {
     Path proguardConfig = writeTextToTempFile(
         "-if class **$R**",
         "-keep class **D<n>"
@@ -1424,7 +1427,6 @@
       parser.parse(proguardConfig);
       fail();
     } catch (AbortException e) {
-      System.out.println(handler.errors.get(0));
       checkDiagnostic(handler.errors, proguardConfig, 2, 13,
           "Use of generics not allowed for java type");
     }
@@ -1432,6 +1434,76 @@
   }
 
   @Test
+  public void parse_if_nthWildcard_notNumber_asterisk_inClassName() throws Exception {
+    Path proguardConfig = writeTextToTempFile(
+        "-if class **$R**",
+        "-keep class **D<*>"
+    );
+    try {
+      ProguardConfigurationParser parser =
+          new ProguardConfigurationParser(new DexItemFactory(), reporter);
+      parser.parse(proguardConfig);
+      fail();
+    } catch (AbortException e) {
+      checkDiagnostic(handler.errors, proguardConfig, 2, 13,
+          "Use of generics not allowed for java type");
+    }
+    verifyFailWithProguard6(proguardConfig, "Use of generics not allowed for java type");
+  }
+
+  @Test
+  public void parse_if_nthWildcard_notNumber_asterisk_inMemberName() throws Exception {
+    Path proguardConfig = writeTextToTempFile(
+        "-if class **$R**",
+        "-keep class **D<2> {",
+        "  int id<*>;",
+        "}"
+    );
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), reporter);
+    parser.parse(proguardConfig);
+    verifyParserEndsCleanly();
+
+    verifyWithProguard6(proguardConfig);
+  }
+
+  @Test
+  public void parse_if_nestedAngularBrackets_inMemberName() throws Exception {
+    Path proguardConfig = writeTextToTempFile(
+        "-if class **$R**",
+        "-keep class **D<2> {",
+        "  int id<<*>>;",
+        "}"
+    );
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), reporter);
+    parser.parse(proguardConfig);
+    verifyParserEndsCleanly();
+
+    verifyWithProguard6(proguardConfig);
+  }
+
+  @Test
+  public void parse_if_nestedAngularBrackets_outOfRange() throws Exception {
+    Path proguardConfig = writeTextToTempFile(
+        "-if class **$R**",
+        "-keep class **D<2> {",
+        "  int id<<4>>;",  // There are 3 previous referable wildcards in this rule.
+        "}"
+    );
+    try {
+      ProguardConfigurationParser parser =
+          new ProguardConfigurationParser(new DexItemFactory(), reporter);
+      parser.parse(proguardConfig);
+      fail();
+    } catch (AbortException e) {
+      checkDiagnostic(handler.errors, proguardConfig, 4, 2,
+          "Wildcard", "<4>", "invalid");
+    }
+    verifyFailWithProguard6(proguardConfig, "Invalid reference to wildcard (4,");
+  }
+
+  @Test
   public void parse_if_nthWildcard_outOfRange_tooSmall() throws Exception {
     Path proguardConfig = writeTextToTempFile(
         "-if class **$R**",
@@ -1446,9 +1518,9 @@
       checkDiagnostic(handler.errors, proguardConfig, 2, 13,
           "Wildcard", "<0>", "invalid");
     }
+    verifyFailWithProguard6(proguardConfig, "Invalid reference to wildcard (0,");
   }
 
-  @Ignore("b/73800755: verify the range of <n>")
   @Test
   public void parse_if_nthWildcard_outOfRange_tooBig() throws Exception {
     Path proguardConfig = writeTextToTempFile(
@@ -1461,12 +1533,12 @@
       parser.parse(proguardConfig);
       fail();
     } catch (AbortException e) {
-      checkDiagnostic(handler.errors, proguardConfig, 1, 1,
+      checkDiagnostic(handler.errors, proguardConfig, 3, 1,
           "Wildcard", "<4>", "invalid");
     }
+    verifyFailWithProguard6(proguardConfig, "Invalid reference to wildcard (4,");
   }
 
-  @Ignore("b/73800755: verify the range of <n>")
   @Test
   public void parse_if_nthWildcard_outOfRange_inIf() throws Exception {
     Path proguardConfig = writeTextToTempFile(
@@ -1479,12 +1551,12 @@
       parser.parse(proguardConfig);
       fail();
     } catch (AbortException e) {
-      checkDiagnostic(handler.errors, proguardConfig, 1, 1,
+      checkDiagnostic(handler.errors, proguardConfig, 3, 1,
           "Wildcard", "<2>", "invalid");
     }
+    verifyFailWithProguard6(proguardConfig, "Invalid reference to wildcard (2,");
   }
 
-  @Ignore("b/73800755: verify the range of <n>")
   @Test
   public void parse_if_nthWildcard_not_referable() throws Exception {
     Path proguardConfig = writeTextToTempFile(
@@ -1501,9 +1573,10 @@
       parser.parse(proguardConfig);
       fail();
     } catch (AbortException e) {
-      checkDiagnostic(handler.errors, proguardConfig, 1, 1,
+      checkDiagnostic(handler.errors, proguardConfig, 6, 2,
           "Wildcard", "<3>", "invalid");
     }
+    verifyFailWithProguard6(proguardConfig, "Invalid reference to wildcard (3,");
   }
 
   @Test
@@ -1519,8 +1592,9 @@
       fail();
     } catch (AbortException e) {
       checkDiagnostic(handler.errors, proguardConfig, 1, 1,
-          "without", "subsequent", "keep");
+          "Expecting", "'-keep'", "after", "'-if'");
     }
+    verifyFailWithProguard6(proguardConfig, "Expecting '-keep' option after '-if' option");
   }
 
   @Test
@@ -1535,8 +1609,9 @@
       fail();
     } catch (AbortException e) {
       checkDiagnostic(handler.errors, proguardConfig, 1, 1,
-          "without", "subsequent", "keep");
+          "Expecting", "'-keep'", "after", "'-if'");
     }
+    verifyFailWithProguard6(proguardConfig, "Expecting '-keep' option after '-if' option");
   }
 
   @Test
@@ -1690,9 +1765,35 @@
       );
       Path proguardedJar =
           File.createTempFile("proguarded", FileUtils.JAR_EXTENSION, temp.getRoot()).toPath();
-      ToolHelper
-          .runProguard(jarTestClasses(ImmutableList.of(classToKeepForTest)),
-              proguardedJar, ImmutableList.of(proguardConfig, additionalProguardConfig), null);
+      ProcessResult result = ToolHelper.runProguardRaw(
+          jarTestClasses(ImmutableList.of(classToKeepForTest)),
+          proguardedJar,
+          ImmutableList.of(proguardConfig, additionalProguardConfig),
+          null);
+      assertEquals(0, result.exitCode);
+      DexInspector proguardInspector = new DexInspector(readJar(proguardedJar));
+      assertEquals(1, proguardInspector.allClasses().size());
+    }
+  }
+
+  private void verifyWithProguard6(Path proguardConfig) throws Exception {
+    if (isRunProguard()) {
+      // Add a keep rule for the test class as Proguard will fail if the resulting output jar is
+      // empty
+      Class classToKeepForTest = EmptyMainClassForProguardTests.class;
+      Path additionalProguardConfig = writeTextToTempFile(
+          "-keep class " + classToKeepForTest.getCanonicalName() + " {",
+          "  public static void main(java.lang.String[]);",
+          "}"
+      );
+      Path proguardedJar =
+          File.createTempFile("proguarded", FileUtils.JAR_EXTENSION, temp.getRoot()).toPath();
+      ProcessResult result = ToolHelper.runProguard6Raw(
+          jarTestClasses(ImmutableList.of(classToKeepForTest)),
+          proguardedJar,
+          ImmutableList.of(proguardConfig, additionalProguardConfig),
+          null);
+      assertEquals(0, result.exitCode);
       DexInspector proguardInspector = new DexInspector(readJar(proguardedJar));
       assertEquals(1, proguardInspector.allClasses().size());
     }
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java
index d8c43c9..4a734a4 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java
@@ -7,6 +7,7 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards;
 import com.android.tools.r8.shaking.ProguardTypeMatcher.ClassOrType;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.google.common.collect.ImmutableList;
@@ -22,7 +23,8 @@
   private static final DexItemFactory dexItemFactory = new DexItemFactory();
 
   private static boolean matchTypeName(String typeName, String pattern) {
-    return ProguardTypeMatcher.create(pattern, ClassOrType.TYPE, dexItemFactory)
+    return ProguardTypeMatcher.create(
+        toIdentifierPatternWithWildCards(pattern), ClassOrType.TYPE, dexItemFactory)
         .matches(dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(typeName)));
   }
 
@@ -37,8 +39,8 @@
       for (String pattern : patterns) {
         boolean isNegated = pattern.startsWith("!");
         String actualPattern = isNegated ? pattern.substring(1) : pattern;
-        listBuilder.addClassName(isNegated,
-            ProguardTypeMatcher.create(actualPattern, ClassOrType.CLASS, dexItemFactory));
+        listBuilder.addClassName(isNegated, ProguardTypeMatcher.create(
+            toIdentifierPatternWithWildCards(actualPattern), ClassOrType.CLASS, dexItemFactory));
       }
       builder.addPattern(listBuilder.build());
     }
@@ -142,5 +144,36 @@
     assertFalse(ProguardNameMatcher.matchFieldOrMethodName("getObject?", "getObject"));
     assertTrue(ProguardNameMatcher.matchFieldOrMethodName("getObject?", "getObject1"));
     assertTrue(ProguardNameMatcher.matchFieldOrMethodName("getObject?", "getObject5"));
- }
+  }
+
+  private static IdentifierPatternWithWildcards toIdentifierPatternWithWildCards(String pattern) {
+    ImmutableList.Builder<String> builder = ImmutableList.builder();
+    String allPattern = "";
+    String backReference = "";
+    for (int i = 0; i < pattern.length(); i++) {
+      char patternChar = pattern.charAt(i);
+      if (patternChar == '?' || patternChar == '%') {
+        builder.add(String.valueOf(patternChar));
+      } else if (patternChar == '*') {
+        allPattern += patternChar;
+      } else if (patternChar == '<') {
+        backReference += patternChar;
+      } else if (patternChar == '>') {
+        backReference += patternChar;
+        builder.add(backReference);
+        backReference = "";
+      } else {
+        if (allPattern.length() > 0) {
+          builder.add(allPattern);
+          allPattern = "";
+        } else if (backReference.length() > 0) {
+          backReference += patternChar;
+        }
+      }
+    }
+    if (allPattern.length() > 0) {
+      builder.add(allPattern);
+    }
+    return new IdentifierPatternWithWildcards(pattern, builder.build());
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
index abbe3d3..1151648 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
@@ -50,7 +50,6 @@
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Set;
 import org.junit.Test;
 
 public class ForceProguardCompatibilityTest extends TestBase {
@@ -157,7 +156,7 @@
       assertEquals(1, classNames.size());
       assertEquals(testClass.getCanonicalName(),
           classNames.asSpecificDexTypes().get(0).toSourceString());
-      Set<ProguardMemberRule> memberRules = configuration.getRules().get(0).getMemberRules();
+      List<ProguardMemberRule> memberRules = configuration.getRules().get(0).getMemberRules();
       assertEquals(1, memberRules.size());
       assertEquals(ProguardMemberType.INIT, memberRules.iterator().next().getRuleType());
     } else {
@@ -279,7 +278,7 @@
         assertEquals(ProguardKeepRuleType.KEEP, rule.getType());
         assertTrue(rule.getModifiers().allowsObfuscation);
         assertTrue(rule.getModifiers().allowsOptimization);
-        Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+        List<ProguardMemberRule> memberRules = rule.getMemberRules();
         ProguardClassNameList classNames = rule.getClassNames();
         assertEquals(1, classNames.size());
         DexType type = classNames.asSpecificDexTypes().get(0);
@@ -295,7 +294,7 @@
         }
       });
       Iterables.filter(rules, ProguardIdentifierNameStringRule.class).forEach(rule -> {
-        Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+        List<ProguardMemberRule> memberRules = rule.getMemberRules();
         ProguardClassNameList classNames = rule.getClassNames();
         assertEquals(1, classNames.size());
         DexType type = classNames.asSpecificDexTypes().get(0);
@@ -388,7 +387,7 @@
         assertEquals(ProguardKeepRuleType.KEEP_CLASS_MEMBERS, rule.getType());
         assertTrue(rule.getModifiers().allowsObfuscation);
         assertTrue(rule.getModifiers().allowsOptimization);
-        Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+        List<ProguardMemberRule> memberRules = rule.getMemberRules();
         ProguardClassNameList classNames = rule.getClassNames();
         assertEquals(1, classNames.size());
         DexType type = classNames.asSpecificDexTypes().get(0);
@@ -403,7 +402,7 @@
         }
       });
       Iterables.filter(rules, ProguardIdentifierNameStringRule.class).forEach(rule -> {
-        Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+        List<ProguardMemberRule> memberRules = rule.getMemberRules();
         ProguardClassNameList classNames = rule.getClassNames();
         assertEquals(1, classNames.size());
         DexType type = classNames.asSpecificDexTypes().get(0);
@@ -504,7 +503,7 @@
         assertEquals(ProguardKeepRuleType.KEEP_CLASS_MEMBERS, rule.getType());
         assertTrue(rule.getModifiers().allowsObfuscation);
         assertTrue(rule.getModifiers().allowsOptimization);
-        Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+        List<ProguardMemberRule> memberRules = rule.getMemberRules();
         ProguardClassNameList classNames = rule.getClassNames();
         assertEquals(1, classNames.size());
         DexType type = classNames.asSpecificDexTypes().get(0);
@@ -519,7 +518,7 @@
       assertTrue(keptFields.containsKey("longField"));
       assertTrue(keptFields.containsKey("objField"));
       Iterables.filter(rules, ProguardIdentifierNameStringRule.class).forEach(rule -> {
-        Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+        List<ProguardMemberRule> memberRules = rule.getMemberRules();
         ProguardClassNameList classNames = rule.getClassNames();
         assertEquals(1, classNames.size());
         DexType type = classNames.asSpecificDexTypes().get(0);
@@ -683,7 +682,7 @@
   private void defaultMethodCompatibilityRules(ProguardConfiguration configuration) {
     assertEquals(1, configuration.getRules().size());
     ProguardConfigurationRule rule = configuration.getRules().get(0);
-    Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+    List<ProguardMemberRule> memberRules = rule.getMemberRules();
     ProguardClassNameList classNames = rule.getClassNames();
     assertEquals(1, classNames.size());
     DexType type = classNames.asSpecificDexTypes().get(0);
@@ -708,7 +707,7 @@
   private void defaultMethod2CompatibilityRules(ProguardConfiguration configuration) {
     assertEquals(1, configuration.getRules().size());
     ProguardConfigurationRule rule = configuration.getRules().get(0);
-    Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+    List<ProguardMemberRule> memberRules = rule.getMemberRules();
     ProguardClassNameList classNames = rule.getClassNames();
     assertEquals(1, classNames.size());
     DexType type = classNames.asSpecificDexTypes().get(0);