Merge "Enable errorprone check to ensure hashCode is implemented."
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index ae6326e..5255fc5 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -30,8 +30,7 @@
 import com.android.tools.r8.shaking.DiscardedChecker;
 import com.android.tools.r8.shaking.Enqueuer;
 import com.android.tools.r8.shaking.MainDexListBuilder;
-import com.android.tools.r8.shaking.ProguardClassNameList;
-import com.android.tools.r8.shaking.ProguardTypeMatcher.MatchSpecificType;
+import com.android.tools.r8.shaking.ProguardClassFilter;
 import com.android.tools.r8.shaking.ReasonPrinter;
 import com.android.tools.r8.shaking.RootSetBuilder;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
@@ -147,15 +146,9 @@
   }
 
   private Set<DexType> filterMissingClasses(Set<DexType> missingClasses,
-      ProguardClassNameList dontWarnPatterns) {
+      ProguardClassFilter dontWarnPatterns) {
     Set<DexType> result = new HashSet<>(missingClasses);
-    dontWarnPatterns.forEachTypeMatcher(matcher -> {
-      if (matcher instanceof MatchSpecificType) {
-        result.remove(((MatchSpecificType) matcher).type);
-      } else {
-        result.removeIf(matcher::matches);
-      }
-    });
+    dontWarnPatterns.filterOutMatches(result);
     return result;
   }
 
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 7eff6fa..5b3cfb5 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -477,7 +477,6 @@
     internal.useDiscardedChecker = useDiscardedChecker();
     assert !internal.ignoreMissingClasses;
     internal.ignoreMissingClasses = ignoreMissingClasses;
-    internal.keepAttributes.applyPatterns(proguardConfiguration.getKeepAttributesPatterns());
     internal.ignoreMissingClasses |= proguardConfiguration.isIgnoreWarnings();
     assert !internal.verbose;
     internal.mainDexKeepRules = mainDexKeepRules;
diff --git a/src/main/java/com/android/tools/r8/dex/DexFileReader.java b/src/main/java/com/android/tools/r8/dex/DexFileReader.java
index 8175834..770afdf 100644
--- a/src/main/java/com/android/tools/r8/dex/DexFileReader.java
+++ b/src/main/java/com/android/tools/r8/dex/DexFileReader.java
@@ -53,6 +53,7 @@
 import com.android.tools.r8.graph.OffsetToObjectMapping;
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.utils.ProgramResource.Kind;
+import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
 import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
 import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
 import java.io.ByteArrayOutputStream;
@@ -62,7 +63,6 @@
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Hashtable;
 import java.util.List;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
@@ -751,7 +751,7 @@
         file.getUshort();  // Skip padding ushort
       }
       if (triesSize > 0) {
-        Hashtable<Integer, Integer> handlerMap = new Hashtable<>();
+        Int2IntArrayMap handlerMap = new Int2IntArrayMap();
         // tries: try_item[tries_size].
         for (int i = 0; i < triesSize; i++) {
           int startAddr = file.getUint();
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index b1db651..dccf887 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -62,7 +62,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.Hashtable;
+import java.util.HashMap;
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
@@ -997,8 +997,8 @@
         = createObject2IntMap();
     private final Object2IntMap<DexProgramClass> classesWithData = createObject2IntMap();
     private final Object2IntMap<DexEncodedArray> encodedArrays = createObject2IntMap();
-    private final Hashtable<DexProgramClass, DexAnnotationDirectory> clazzToAnnotationDirectory
-        = new Hashtable<>();
+    private final Map<DexProgramClass, DexAnnotationDirectory> clazzToAnnotationDirectory
+        = new HashMap<>();
 
     private static <T> Object2IntMap<T> createObject2IntMap() {
       Object2IntMap<T> result = new Object2IntLinkedOpenHashMap<>();
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
index 093442b..a2caf1d 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
@@ -8,7 +8,7 @@
 import com.google.common.collect.Sets;
 import java.util.Collections;
 import java.util.HashSet;
-import java.util.Hashtable;
+import java.util.IdentityHashMap;
 import java.util.Map;
 import java.util.Set;
 import java.util.function.Function;
@@ -18,7 +18,7 @@
   // Set of missing classes, discovered during subtypeMap computation.
   private Set<DexType> missingClasses = Sets.newIdentityHashSet();
   // Map from types to their subtypes.
-  private final Hashtable<DexType, ImmutableSet<DexType>> subtypeMap = new Hashtable<>();
+  private final Map<DexType, ImmutableSet<DexType>> subtypeMap = new IdentityHashMap<>();
 
   public AppInfoWithSubtyping(DexApplication application) {
     super(application);
@@ -57,7 +57,7 @@
     return subtypes == null ? ImmutableSet.of() : subtypes;
   }
 
-  private void populateSuperType(Hashtable<DexType, Set<DexType>> map, DexType superType,
+  private void populateSuperType(Map<DexType, Set<DexType>> map, DexType superType,
       DexClass baseClass, Function<DexType, DexClass> definitions) {
     if (superType != null) {
       Set<DexType> set = map.computeIfAbsent(superType, ignore -> new HashSet<>());
@@ -68,7 +68,7 @@
     }
   }
 
-  private void populateAllSuperTypes(Hashtable<DexType, Set<DexType>> map, DexType holder,
+  private void populateAllSuperTypes(Map<DexType, Set<DexType>> map, DexType holder,
       DexClass baseClass, Function<DexType, DexClass> definitions) {
     DexClass holderClass = definitions.apply(holder);
     // Skip if no corresponding class is found.
@@ -98,7 +98,7 @@
   private void populateSubtypeMap(DirectMappedDexApplication app, DexItemFactory dexItemFactory) {
     dexItemFactory.clearSubtypeInformation();
     dexItemFactory.objectType.tagAsSubtypeRoot();
-    Hashtable<DexType, Set<DexType>> map = new Hashtable<>();
+    Map<DexType, Set<DexType>> map = new IdentityHashMap<>();
     for (DexClass clazz : Iterables.<DexClass>concat(app.classes(), app.libraryClasses())) {
       populateAllSuperTypes(map, clazz.type, clazz, app::definitionFor);
     }
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index 9267f7d..6a2fcbf 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -17,11 +17,11 @@
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.StringUtils;
+import it.unimi.dsi.fastutil.ints.Int2IntMap;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
@@ -359,7 +359,7 @@
       this.handlerIndex = NO_INDEX;
     }
 
-    public void setHandlerIndex(Hashtable<Integer, Integer> map) {
+    public void setHandlerIndex(Int2IntMap map) {
       handlerIndex = map.get(handlerOffset);
     }
 
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
index 504cc4a..1360964 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -17,7 +17,7 @@
 import java.io.IOException;
 import java.io.StringWriter;
 import java.io.Writer;
-import java.util.Hashtable;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.function.Consumer;
 
@@ -26,7 +26,7 @@
   private final ImmutableMap<String, ClassNaming> classNameMappings;
   private ImmutableBiMap<String, String> nameMapping;
 
-  private Hashtable<Signature, Signature> signatureMap = new Hashtable<>();
+  private Map<Signature, Signature> signatureMap = new HashMap<>();
 
   ClassNameMapper(Map<String, ClassNaming> classNameMappings) {
     this.classNameMappings = ImmutableMap.copyOf(classNameMappings);
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 c5e62e2..39ca020 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -71,7 +71,7 @@
     this.isAccessModificationAllowed = options.proguardConfiguration.isAccessModificationAllowed();
     this.packageDictionary = options.proguardConfiguration.getPackageObfuscationDictionary();
     this.classDictionary = options.proguardConfiguration.getClassObfuscationDictionary();
-    this.keepInnerClassStructure = options.keepAttributes.signature;
+    this.keepInnerClassStructure = options.proguardConfiguration.getKeepAttributes().signature;
 
     // Initialize top-level naming state.
     topLevelState = new Namespace(
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
index fe0ff72..552516d 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
@@ -12,19 +12,19 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexValue.DexValueString;
-import com.android.tools.r8.shaking.ProguardClassNameList;
+import com.android.tools.r8.shaking.ProguardClassFilter;
 import com.android.tools.r8.utils.DescriptorUtils;
 import java.util.Map;
 
 class IdentifierMinifier {
 
   private final AppInfo appInfo;
-  private final ProguardClassNameList adaptClassStrings;
+  private final ProguardClassFilter adaptClassStrings;
   private final NamingLens lens;
 
   IdentifierMinifier(
       AppInfo appInfo,
-      ProguardClassNameList adaptClassStrings,
+      ProguardClassFilter adaptClassStrings,
       NamingLens lens) {
     this.appInfo = appInfo;
     this.adaptClassStrings = adaptClassStrings;
@@ -32,7 +32,7 @@
   }
 
   void run() {
-    if (adaptClassStrings.size() != 0) {
+    if (!adaptClassStrings.isEmpty()) {
       handleAdaptClassStrings();
     }
     // TODO(b/36799092): Handle influx of string literals from call sites to annotated members.
diff --git a/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java b/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
index c10d0d7..3736008 100644
--- a/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
+++ b/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
@@ -31,7 +31,7 @@
   public void run() {
     String renameSourceFile = options.proguardConfiguration.getRenameSourceFileAttribute();
     // Return early if a user wants to keep the current source file attribute as-is.
-    if (renameSourceFile == null && options.keepAttributes.sourceFile) {
+    if (renameSourceFile == null && options.proguardConfiguration.getKeepAttributes().sourceFile) {
       return;
     }
     // Now, the user wants either to remove source file attribute or to rename it.
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
index 824f1bd..1698f7a 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -13,7 +13,6 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.InternalOptions.KeepAttributeOptions;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.function.Predicate;
@@ -21,14 +20,13 @@
 public class AnnotationRemover {
 
   private final AppInfoWithLiveness appInfo;
-  private final KeepAttributeOptions keep;
+  private final ProguardKeepAttributes keep;
 
   public AnnotationRemover(AppInfoWithLiveness appInfo, InternalOptions options) {
-    this(appInfo, options.keepAttributes);
+    this(appInfo, options.proguardConfiguration.getKeepAttributes());
   }
 
-  public AnnotationRemover(AppInfoWithLiveness appInfo,
-      KeepAttributeOptions keep) {
+  public AnnotationRemover(AppInfoWithLiveness appInfo, ProguardKeepAttributes keep) {
     this.appInfo = appInfo;
     this.keep = keep;
   }
@@ -91,7 +89,7 @@
         }
         break;
       case DexAnnotation.VISIBILITY_BUILD:
-        if (!keep.runtimeInvisibleParamterAnnotations) {
+        if (!keep.runtimeInvisibleParameterAnnotations) {
           return false;
         }
         break;
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassFilter.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassFilter.java
new file mode 100644
index 0000000..939dca3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassFilter.java
@@ -0,0 +1,64 @@
+// Copyright (c) 2017, 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.shaking;
+
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.shaking.ProguardTypeMatcher.MatchSpecificType;
+import com.google.common.collect.ImmutableList;
+import java.util.Set;
+
+public class ProguardClassFilter {
+  private final ImmutableList<ProguardClassNameList> patterns;
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static class Builder {
+    private final ImmutableList.Builder<ProguardClassNameList> patterns = ImmutableList.builder();
+
+    private Builder() {
+    }
+
+    public Builder addPattern(ProguardClassNameList pattern) {
+      patterns.add(pattern);
+      return this;
+    }
+
+    ProguardClassFilter build() {
+      return new ProguardClassFilter(patterns.build());
+    }
+  }
+
+  private ProguardClassFilter(ImmutableList<ProguardClassNameList> patterns) {
+    this.patterns = patterns;
+  }
+
+  public boolean isEmpty() {
+    return patterns.size() == 0;
+  }
+
+  public boolean matches(DexType type) {
+    for (ProguardClassNameList pattern : patterns) {
+      if (pattern.matches(type)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public void filterOutMatches(Set<DexType> types) {
+    for (ProguardClassNameList pattern : patterns) {
+      pattern.forEachTypeMatcher(matcher -> {
+        if (matcher instanceof MatchSpecificType) {
+          assert matcher.getSpecificType() != null;
+          types.remove(matcher.getSpecificType());
+        } else {
+          types.removeIf(matcher::matches);
+        }
+      });
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
index dc800a3..4a84321 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -6,7 +6,6 @@
 import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.naming.DictionaryReader;
-import com.android.tools.r8.utils.InternalOptions.KeepAttributeOptions;
 import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
 import com.google.common.collect.ImmutableList;
 import java.nio.file.Path;
@@ -34,7 +33,7 @@
     private boolean verbose;
     private String renameSourceFileAttribute;
     private final List<String> keepAttributePatterns = new ArrayList<>();
-    private ProguardClassNameList dontWarnPatterns;
+    private ProguardClassFilter.Builder dontWarnPatterns = ProguardClassFilter.builder();
     protected final List<ProguardConfigurationRule> rules = new ArrayList<>();
     private final DexItemFactory dexItemFactory;
     private boolean printSeeds;
@@ -44,8 +43,7 @@
     private Path packageObfuscationDictionary;
     private boolean useUniqueClassMemberNames;
     private boolean keepParameterNames;
-    private ProguardClassNameList adaptClassStrings = ProguardClassNameList.emptyList();
-
+    private ProguardClassFilter.Builder adaptClassStrings = ProguardClassFilter.builder();
     private Builder(DexItemFactory dexItemFactory) {
       this.dexItemFactory = dexItemFactory;
       resetProguardDefaults();
@@ -69,7 +67,7 @@
       verbose = false;
       renameSourceFileAttribute = null;
       keepAttributePatterns.clear();
-      dontWarnPatterns = ProguardClassNameList.emptyList();
+      dontWarnPatterns = ProguardClassFilter.builder();
       rules.clear();
       printSeeds = false;
       seedFile = null;
@@ -78,6 +76,7 @@
       packageObfuscationDictionary = null;
       useUniqueClassMemberNames = false;
       keepParameterNames = false;
+      adaptClassStrings = ProguardClassFilter.builder();
     }
 
     public void addInjars(List<FilteredClassPath> injars) {
@@ -162,8 +161,8 @@
       this.rules.add(rule);
     }
 
-    public void setDontWarnPatterns(ProguardClassNameList patterns) {
-      dontWarnPatterns = patterns;
+    public void addDontWarnPattern(ProguardClassNameList pattern) {
+      dontWarnPatterns.addPattern(pattern);
     }
 
     public void setSeedFile(Path seedFile) {
@@ -202,8 +201,8 @@
       return keepParameterNames;
     }
 
-    public void setAdaptClassStrings(ProguardClassNameList adaptClassStrings) {
-      this.adaptClassStrings = adaptClassStrings;
+    public void addAdaptClassStringsPattern(ProguardClassNameList pattern) {
+      adaptClassStrings.addPattern(pattern);
     }
 
     public ProguardConfiguration build() throws CompilationException {
@@ -226,7 +225,7 @@
           verbose,
           renameSourceFileAttribute,
           keepAttributePatterns,
-          dontWarnPatterns,
+          dontWarnPatterns.build(),
           rules,
           printSeeds,
           seedFile,
@@ -235,7 +234,7 @@
           DictionaryReader.readAllNames(packageObfuscationDictionary),
           useUniqueClassMemberNames,
           keepParameterNames,
-          adaptClassStrings);
+          adaptClassStrings.build());
     }
   }
 
@@ -256,8 +255,8 @@
   private final Path applyMappingFile;
   private final boolean verbose;
   private final String renameSourceFileAttribute;
-  private final ImmutableList<String> keepAttributesPatterns;
-  private final ProguardClassNameList dontWarnPatterns;
+  private final ProguardKeepAttributes keepAttributes;
+  private final ProguardClassFilter dontWarnPatterns;
   protected final ImmutableList<ProguardConfigurationRule> rules;
   private final boolean printSeeds;
   private final Path seedFile;
@@ -266,7 +265,7 @@
   private final ImmutableList<String> packageObfuscationDictionary;
   private boolean useUniqueClassMemberNames;
   private boolean keepParameterNames;
-  private final ProguardClassNameList adaptClassStrings;
+  private final ProguardClassFilter adaptClassStrings;
 
   private ProguardConfiguration(
       DexItemFactory factory,
@@ -287,7 +286,7 @@
       boolean verbose,
       String renameSourceFileAttribute,
       List<String> keepAttributesPatterns,
-      ProguardClassNameList dontWarnPatterns,
+      ProguardClassFilter dontWarnPatterns,
       List<ProguardConfigurationRule> rules,
       boolean printSeeds,
       Path seedFile,
@@ -296,7 +295,7 @@
       ImmutableList<String> packageObfuscationDictionary,
       boolean useUniqueClassMemberNames,
       boolean keepParameterNames,
-      ProguardClassNameList adaptClassStrings) {
+      ProguardClassFilter adaptClassStrings) {
     this.dexItemFactory = factory;
     this.injars = ImmutableList.copyOf(injars);
     this.libraryjars = ImmutableList.copyOf(libraryjars);
@@ -314,7 +313,7 @@
     this.applyMappingFile = applyMappingFile;
     this.verbose = verbose;
     this.renameSourceFileAttribute = renameSourceFileAttribute;
-    this.keepAttributesPatterns = ImmutableList.copyOf(keepAttributesPatterns);
+    this.keepAttributes = ProguardKeepAttributes.fromPatterns(keepAttributesPatterns);
     this.dontWarnPatterns = dontWarnPatterns;
     this.rules = ImmutableList.copyOf(rules);
     this.printSeeds = printSeeds;
@@ -338,7 +337,7 @@
     Builder builder = new Builder(dexItemFactory);
     builder.setObfuscating(false);
     builder.setShrinking(false);
-    builder.addKeepAttributePatterns(KeepAttributeOptions.KEEP_ALL);
+    builder.addKeepAttributePatterns(ProguardKeepAttributes.KEEP_ALL);
     builder.addRule(ProguardKeepRule.defaultKeepAllRule());
     return builder;
   }
@@ -415,11 +414,11 @@
     return renameSourceFileAttribute;
   }
 
-  public ImmutableList<String> getKeepAttributesPatterns() {
-    return keepAttributesPatterns;
+  public ProguardKeepAttributes getKeepAttributes() {
+    return keepAttributes;
   }
 
-  public ProguardClassNameList getDontWarnPatterns() {
+  public ProguardClassFilter getDontWarnPatterns() {
     return dontWarnPatterns;
   }
 
@@ -447,7 +446,7 @@
     return keepParameterNames;
   }
 
-  public ProguardClassNameList getAdaptClassStrings() {
+  public ProguardClassFilter getAdaptClassStrings() {
     return adaptClassStrings;
   }
 
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 ef63e40..40b7598 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -76,6 +76,7 @@
       DexItemFactory dexItemFactory, DiagnosticsHandler diagnosticsHandler) {
     this.dexItemFactory = dexItemFactory;
     configurationBuilder = ProguardConfiguration.builder(dexItemFactory);
+
     this.diagnosticsHandler = diagnosticsHandler;
   }
 
@@ -212,9 +213,9 @@
         configurationBuilder.setIgnoreWarnings(true);
       } else if (acceptString("dontwarn")) {
         if (isOptionalArgumentGiven()) {
-          configurationBuilder.setDontWarnPatterns(parseClassNames());
+          configurationBuilder.addDontWarnPattern(parseClassNames());
         } else {
-          configurationBuilder.setDontWarnPatterns(
+          configurationBuilder.addDontWarnPattern(
               ProguardClassNameList.singletonList(ProguardTypeMatcher.defaultAllMatcher()));
         }
       } else if (acceptString("repackageclasses")) {
@@ -292,9 +293,9 @@
       } else if (acceptString("adaptclassstrings")) {
         skipWhitespace();
         if (isOptionalArgumentGiven()) {
-          configurationBuilder.setAdaptClassStrings(parseClassNames());
+          configurationBuilder.addAdaptClassStringsPattern(parseClassNames());
         } else {
-          configurationBuilder.setAdaptClassStrings(
+          configurationBuilder.addAdaptClassStringsPattern(
               ProguardClassNameList.singletonList(ProguardTypeMatcher.defaultAllMatcher()));
         }
       } else if (acceptString("identifiernamestring")) {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java
new file mode 100644
index 0000000..487a5b4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java
@@ -0,0 +1,182 @@
+// Copyright (c) 2017, 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.shaking;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+
+public class ProguardKeepAttributes {
+
+  public static final String SOURCE_FILE = "SourceFile";
+  public static final String SOURCE_DIR = "SourceDir";
+  public static final String INNER_CLASSES = "InnerClasses";
+  public static final String ENCLOSING_METHOD = "EnclosingMethod";
+  public static final String SIGNATURE = "Signature";
+  public static final String EXCEPTIONS = "Exceptions";
+  public static final String SOURCE_DEBUG_EXTENSION = "SourceDebugExtension";
+  public static final String RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations";
+  public static final String RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations";
+  public static final String RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS =
+      "RuntimeVisibleParameterAnnotations";
+  public static final String RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS =
+      "RuntimeInvisibleParameterAnnotations";
+  public static final String RUNTIME_VISIBLE_TYPE_ANNOTATIONS = "RuntimeVisibleTypeAnnotations";
+  public static final String RUNTIME_INVISIBLE_TYPE_ANNOTATIONS =
+      "RuntimeInvisibleTypeAnnotations";
+  public static final String ANNOTATION_DEFAULT = "AnnotationDefault";
+
+  public static final List<String> KEEP_ALL = ImmutableList.of("*");
+
+  public boolean sourceFile = false;
+  public boolean sourceDir = false;
+  public boolean innerClasses = false;
+  public boolean enclosingMethod = false;
+  public boolean signature = false;
+  public boolean exceptions = false;
+  public boolean sourceDebugExtension = false;
+  public boolean runtimeVisibleAnnotations = false;
+  public boolean runtimeInvisibleAnnotations = false;
+  public boolean runtimeVisibleParameterAnnotations = false;
+  public boolean runtimeInvisibleParameterAnnotations = false;
+  public boolean runtimeVisibleTypeAnnotations = false;
+  public boolean runtimeInvisibleTypeAnnotations = false;
+  public boolean annotationDefault = false;
+
+  private ProguardKeepAttributes() {
+  }
+
+  public static ProguardKeepAttributes filterOnlySignatures() {
+    ProguardKeepAttributes result = new ProguardKeepAttributes();
+    result.applyPatterns(KEEP_ALL);
+    result.signature = false;
+    return result;
+  }
+
+  /**
+   * Implements ProGuards attribute matching rules.
+   *
+   * @see <a href="https://www.guardsquare.com/en/proguard/manual/attributes">ProGuard manual</a>.
+   */
+  private boolean update(boolean previous, String text, List<String> patterns) {
+    for (String pattern : patterns) {
+      if (previous) {
+        return true;
+      }
+      if (pattern.charAt(0) == '!') {
+        if (matches(pattern, 1, text, 0)) {
+          break;
+        }
+      } else {
+        previous = matches(pattern, 0, text, 0);
+      }
+    }
+    return previous;
+  }
+
+  private boolean matches(String pattern, int patternPos, String text, int textPos) {
+    while (patternPos < pattern.length()) {
+      char next = pattern.charAt(patternPos++);
+      if (next == '*') {
+        while (textPos < text.length()) {
+          if (matches(pattern, patternPos, text, textPos++)) {
+            return true;
+          }
+        }
+        return patternPos >= pattern.length();
+      } else {
+        if (textPos >= text.length() || text.charAt(textPos) != next) {
+          return false;
+        }
+        textPos++;
+      }
+    }
+    return textPos == text.length();
+  }
+
+  public static ProguardKeepAttributes fromPatterns(List<String> patterns) {
+    ProguardKeepAttributes keepAttributes = new ProguardKeepAttributes();
+    keepAttributes.applyPatterns(patterns);
+    return keepAttributes;
+  }
+
+  public void applyPatterns(List<String> patterns) {
+    sourceFile = update(sourceFile, SOURCE_FILE, patterns);
+    sourceDir = update(sourceDir, SOURCE_DIR, patterns);
+    innerClasses = update(innerClasses, INNER_CLASSES, patterns);
+    enclosingMethod = update(enclosingMethod, ENCLOSING_METHOD, patterns);
+    signature = update(signature, SIGNATURE, patterns);
+    exceptions = update(exceptions, EXCEPTIONS, patterns);
+    sourceDebugExtension = update(sourceDebugExtension, SOURCE_DEBUG_EXTENSION, patterns);
+    runtimeVisibleAnnotations = update(runtimeVisibleAnnotations, RUNTIME_VISIBLE_ANNOTATIONS,
+        patterns);
+    runtimeInvisibleAnnotations = update(runtimeInvisibleAnnotations,
+        RUNTIME_INVISIBLE_ANNOTATIONS, patterns);
+    runtimeVisibleParameterAnnotations = update(runtimeVisibleParameterAnnotations,
+        RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, patterns);
+    runtimeInvisibleParameterAnnotations = update(runtimeInvisibleParameterAnnotations,
+        RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS, patterns);
+    runtimeVisibleTypeAnnotations = update(runtimeVisibleTypeAnnotations,
+        RUNTIME_VISIBLE_TYPE_ANNOTATIONS, patterns);
+    runtimeInvisibleTypeAnnotations = update(runtimeInvisibleTypeAnnotations,
+        RUNTIME_INVISIBLE_TYPE_ANNOTATIONS, patterns);
+    annotationDefault = update(annotationDefault, ANNOTATION_DEFAULT, patterns);
+  }
+
+  public void ensureValid() {
+    if (innerClasses && !enclosingMethod) {
+      throw new CompilationError("Attribute InnerClasses requires EnclosingMethod attribute. "
+          + "Check -keepattributes directive.");
+    } else if (!innerClasses && enclosingMethod) {
+      throw new CompilationError("Attribute EnclosingMethod requires InnerClasses attribute. "
+          + "Check -keepattributes directive.");
+    } else if (signature && !innerClasses) {
+      throw new CompilationError("Attribute Signature requires InnerClasses attribute. Check "
+          + "-keepattributes directive.");
+    }
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (!(o instanceof ProguardKeepAttributes)) {
+      return false;
+    }
+    ProguardKeepAttributes other = (ProguardKeepAttributes) o;
+    return this.sourceFile == other.sourceFile
+        && this.sourceDir == other.sourceDir
+        && this.innerClasses == other.innerClasses
+        && this.enclosingMethod == other.enclosingMethod
+        && this.signature == other.signature
+        && this.exceptions == other.exceptions
+        && this.sourceDebugExtension == other.sourceDebugExtension
+        && this.runtimeVisibleAnnotations == other.runtimeVisibleAnnotations
+        && this.runtimeInvisibleAnnotations == other.runtimeInvisibleAnnotations
+        && this.runtimeVisibleParameterAnnotations == other.runtimeVisibleParameterAnnotations
+        && this.runtimeInvisibleParameterAnnotations == other.runtimeInvisibleParameterAnnotations
+        && this.runtimeVisibleTypeAnnotations == other.runtimeVisibleTypeAnnotations
+        && this.runtimeInvisibleTypeAnnotations == other.runtimeInvisibleTypeAnnotations
+        && this.annotationDefault == other.annotationDefault;
+  }
+
+  @Override
+  public int hashCode() {
+    return (this.sourceFile ? 1 : 0)
+        + (this.sourceDir ? 1 << 1 : 0)
+        + (this.innerClasses ? 1 << 2 : 0)
+        + (this.enclosingMethod ? 1 << 3 : 0)
+        + (this.signature ? 1 << 4 : 0)
+        + (this.exceptions ? 1 << 5 : 0)
+        + (this.sourceDebugExtension ? 1 << 6 : 0)
+        + (this.runtimeVisibleAnnotations ? 1 << 7 : 0)
+        + (this.runtimeInvisibleAnnotations ? 1 << 8 : 0)
+        + (this.runtimeVisibleParameterAnnotations ? 1 << 9 : 0)
+        + (this.runtimeInvisibleParameterAnnotations ? 1 << 10 : 0)
+        + (this.runtimeVisibleTypeAnnotations ? 1 << 11 : 0)
+        + (this.runtimeInvisibleTypeAnnotations ? 1 << 12 : 0)
+        + (this.annotationDefault ? 1 << 13 : 0);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 8d7171f..75f89be 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -6,7 +6,6 @@
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.Resource.Origin;
 import com.android.tools.r8.dex.Marker;
-import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.InvalidDebugInfoException;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -114,7 +113,6 @@
   public boolean skipMinification = false;
   public boolean disableAssertions = true;
   public boolean debugKeepRules = false;
-  public final KeepAttributeOptions keepAttributes = new KeepAttributeOptions();
   public boolean allowParameterName = false;
 
   public boolean debug = false;
@@ -282,132 +280,6 @@
     public boolean invertConditionals = false;
   }
 
-  public static class KeepAttributeOptions {
-
-    public static final String SOURCE_FILE = "SourceFile";
-    public static final String SOURCE_DIR = "SourceDir";
-    public static final String INNER_CLASSES = "InnerClasses";
-    public static final String ENCLOSING_METHOD = "EnclosingMethod";
-    public static final String SIGNATURE = "Signature";
-    public static final String EXCEPTIONS = "Exceptions";
-    public static final String SOURCE_DEBUG_EXTENSION = "SourceDebugExtension";
-    public static final String RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations";
-    public static final String RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations";
-    public static final String RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS =
-        "RuntimeVisibleParameterAnnotations";
-    public static final String RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS =
-        "RuntimeInvisibleParameterAnnotations";
-    public static final String RUNTIME_VISIBLE_TYPE_ANNOTATIONS = "RuntimeVisibleTypeAnnotations";
-    public static final String RUNTIME_INVISIBLE_TYPE_ANNOTATIONS =
-        "RuntimeInvisibleTypeAnnotations";
-    public static final String ANNOTATION_DEFAULT = "AnnotationDefault";
-
-    public static final List<String> KEEP_ALL = ImmutableList.of("*");
-
-    public boolean sourceFile = false;
-    public boolean sourceDir = false;
-    public boolean innerClasses = false;
-    public boolean enclosingMethod = false;
-    public boolean signature = false;
-    public boolean exceptions = false;
-    public boolean sourceDebugExtension = false;
-    public boolean runtimeVisibleAnnotations = false;
-    public boolean runtimeInvisibleAnnotations = false;
-    public boolean runtimeVisibleParameterAnnotations = false;
-    public boolean runtimeInvisibleParamterAnnotations = false;
-    public boolean runtimeVisibleTypeAnnotations = false;
-    public boolean runtimeInvisibleTypeAnnotations = false;
-    public boolean annotationDefault = false;
-
-    private KeepAttributeOptions() {
-
-    }
-
-    public static KeepAttributeOptions filterOnlySignatures() {
-      KeepAttributeOptions result = new KeepAttributeOptions();
-      result.applyPatterns(KEEP_ALL);
-      result.signature = false;
-      return result;
-    }
-
-    /**
-     * Implements ProGuards attribute matching rules.
-     *
-     * @see <a href="https://www.guardsquare.com/en/proguard/manual/attributes">ProGuard manual</a>.
-     */
-    private boolean update(boolean previous, String text, List<String> patterns) {
-      for (String pattern : patterns) {
-        if (previous) {
-          return true;
-        }
-        if (pattern.charAt(0) == '!') {
-          if (matches(pattern, 1, text, 0)) {
-            break;
-          }
-        } else {
-          previous = matches(pattern, 0, text, 0);
-        }
-      }
-      return previous;
-    }
-
-    private boolean matches(String pattern, int patternPos, String text, int textPos) {
-      while (patternPos < pattern.length()) {
-        char next = pattern.charAt(patternPos++);
-        if (next == '*') {
-          while (textPos < text.length()) {
-            if (matches(pattern, patternPos, text, textPos++)) {
-              return true;
-            }
-          }
-          return patternPos >= pattern.length();
-        } else {
-          if (textPos >= text.length() || text.charAt(textPos) != next) {
-            return false;
-          }
-          textPos++;
-        }
-      }
-      return textPos == text.length();
-    }
-
-    public void applyPatterns(List<String> patterns) {
-      sourceFile = update(sourceFile, SOURCE_FILE, patterns);
-      sourceDir = update(sourceDir, SOURCE_DIR, patterns);
-      innerClasses = update(innerClasses, INNER_CLASSES, patterns);
-      enclosingMethod = update(enclosingMethod, ENCLOSING_METHOD, patterns);
-      signature = update(signature, SIGNATURE, patterns);
-      exceptions = update(exceptions, EXCEPTIONS, patterns);
-      sourceDebugExtension = update(sourceDebugExtension, SOURCE_DEBUG_EXTENSION, patterns);
-      runtimeVisibleAnnotations = update(runtimeVisibleAnnotations, RUNTIME_VISIBLE_ANNOTATIONS,
-          patterns);
-      runtimeInvisibleAnnotations = update(runtimeInvisibleAnnotations,
-          RUNTIME_INVISIBLE_ANNOTATIONS, patterns);
-      runtimeVisibleParameterAnnotations = update(runtimeVisibleParameterAnnotations,
-          RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, patterns);
-      runtimeInvisibleParamterAnnotations = update(runtimeInvisibleParamterAnnotations,
-          RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS, patterns);
-      runtimeVisibleTypeAnnotations = update(runtimeVisibleTypeAnnotations,
-          RUNTIME_VISIBLE_TYPE_ANNOTATIONS, patterns);
-      runtimeInvisibleTypeAnnotations = update(runtimeInvisibleTypeAnnotations,
-          RUNTIME_INVISIBLE_TYPE_ANNOTATIONS, patterns);
-      annotationDefault = update(annotationDefault, ANNOTATION_DEFAULT, patterns);
-    }
-
-    public void ensureValid() {
-      if (innerClasses && !enclosingMethod) {
-        throw new CompilationError("Attribute InnerClasses requires EnclosingMethod attribute. "
-            + "Check -keepattributes directive.");
-      } else if (!innerClasses && enclosingMethod) {
-        throw new CompilationError("Attribute EnclosingMethod requires InnerClasses attribute. "
-            + "Check -keepattributes directive.");
-      } else if (signature && !innerClasses) {
-        throw new CompilationError("Attribute Signature requires InnerClasses attribute. Check "
-            + "-keepattributes directive.");
-      }
-    }
-  }
-
   public boolean canUseInvokePolymorphicOnVarHandle() {
     return minApiLevel >= AndroidApiLevel.P.getLevel();
   }
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 00777fa..6dc19aa 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -18,7 +18,6 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
 import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.InternalOptions.KeepAttributeOptions;
 import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
@@ -167,6 +166,25 @@
   }
 
   @Test
+  public void testDontWarnMultiple() throws Exception {
+    DexItemFactory dexItemFactory = new DexItemFactory();
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(dexItemFactory, diagnosticsHandler);
+    List<String> configuration1 = ImmutableList.of("-dontwarn foo.**, bar.**");
+    List<String> configuration2 = ImmutableList.of("-dontwarn foo.**", "-dontwarn bar.**");
+    for (List<String> configuration : ImmutableList.of(configuration1, configuration2)) {
+      parser.parse(createConfigurationForTesting(configuration));
+      ProguardConfiguration config = parser.getConfig();
+      assertTrue(
+          config.getDontWarnPatterns().matches(dexItemFactory.createType("Lfoo/Bar;")));
+      assertTrue(
+          config.getDontWarnPatterns().matches(dexItemFactory.createType("Lfoo/bar7Bar;")));
+      assertTrue(
+          config.getDontWarnPatterns().matches(dexItemFactory.createType("Lbar/Foo;")));
+    }
+  }
+
+  @Test
   public void testDontWarnAllExplicitly() throws Exception {
     DexItemFactory dexItemFactory = new DexItemFactory();
     ProguardConfigurationParser parser =
@@ -360,6 +378,26 @@
   }
 
   @Test
+  public void testAdaptClassStringsMultiple() throws Exception {
+    DexItemFactory dexItemFactory = new DexItemFactory();
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(dexItemFactory, diagnosticsHandler);
+    List<String> configuration1 = ImmutableList.of("-adaptclassstrings foo.**, bar.**");
+    List<String> configuration2 =
+        ImmutableList.of("-adaptclassstrings foo.**", "-adaptclassstrings bar.**");
+    for (List<String> configuration : ImmutableList.of(configuration1, configuration2)) {
+      parser.parse(createConfigurationForTesting(configuration));
+      ProguardConfiguration config = parser.getConfig();
+      assertTrue(
+          config.getAdaptClassStrings().matches(dexItemFactory.createType("Lfoo/Bar;")));
+      assertTrue(
+          config.getAdaptClassStrings().matches(dexItemFactory.createType("Lfoo/bar7Bar;")));
+      assertTrue(
+          config.getAdaptClassStrings().matches(dexItemFactory.createType("Lbar/Foo;")));
+    }
+  }
+
+  @Test
   public void testAdaptClassStringsAllExplicitly() throws Exception {
     DexItemFactory dexItemFactory = new DexItemFactory();
     ProguardConfigurationParser parser =
@@ -777,8 +815,8 @@
     parser.parse(createConfigurationForTesting(ImmutableList.of(config1, config2)));
     ProguardConfiguration config = parser.getConfig();
     assertEquals("PG", config.getRenameSourceFileAttribute());
-    assertTrue(config.getKeepAttributesPatterns().contains(KeepAttributeOptions.SOURCE_FILE));
-    assertTrue(config.getKeepAttributesPatterns().contains(KeepAttributeOptions.SOURCE_DIR));
+    assertTrue(config.getKeepAttributes().sourceFile);
+    assertTrue(config.getKeepAttributes().sourceDir);
   }
 
   @Test
@@ -790,14 +828,16 @@
     parser.parse(createConfigurationForTesting(ImmutableList.of(config1, config2)));
     ProguardConfiguration config = parser.getConfig();
     assertEquals("", config.getRenameSourceFileAttribute());
-    assertTrue(config.getKeepAttributesPatterns().contains(KeepAttributeOptions.SOURCE_FILE));
+    assertTrue(config.getKeepAttributes().sourceFile);
   }
 
   private void testKeepattributes(List<String> expected, String config) throws Exception {
     ProguardConfigurationParser parser =
         new ProguardConfigurationParser(new DexItemFactory(), diagnosticsHandler);
     parser.parse(createConfigurationForTesting(ImmutableList.of(config)));
-    assertEquals(expected, parser.getConfig().getKeepAttributesPatterns());
+    assertEquals(
+        ProguardKeepAttributes.fromPatterns(expected),
+        parser.getConfig().getKeepAttributes());
   }
 
   @Test
@@ -809,9 +849,11 @@
     testKeepattributes(xxxYYY, "-keepattributes xxx   ,   yyy");
     testKeepattributes(xxxYYY, "-keepattributes       xxx   ,   yyy     ");
     testKeepattributes(xxxYYY, "-keepattributes       xxx   ,   yyy     \n");
-    String config = "-keepattributes Exceptions,InnerClasses,Signature,Deprecated,\n" +
-                    "                SourceFile,LineNumberTable,*Annotation*,EnclosingMethod\n";
-    List<String> expected = ImmutableList.of("Exceptions", "InnerClasses", "Signature", "Deprecated",
+    String config =
+        "-keepattributes Exceptions,InnerClasses,Signature,Deprecated,\n"
+            + "          SourceFile,LineNumberTable,*Annotation*,EnclosingMethod\n";
+    List<String> expected = ImmutableList.of(
+        "Exceptions", "InnerClasses", "Signature", "Deprecated",
         "SourceFile", "LineNumberTable", "*Annotation*", "EnclosingMethod");
     testKeepattributes(expected, config);
   }
@@ -850,7 +892,6 @@
       parser.getConfig();
       fail();
     } catch (ProguardRuleParserException e) {
-      System.out.println(e);
       assertTrue(e.getMessage().contains("-keepparameternames is not supported"));
     }
   }
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 2737971..d8c43c9 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java
@@ -9,6 +9,9 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.shaking.ProguardTypeMatcher.ClassOrType;
 import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.Arrays;
+import java.util.List;
 import org.junit.Test;
 
 public class ProguardNameMatchingTest {
@@ -24,12 +27,20 @@
   }
 
   private static boolean matchClassName(String className, String... patterns) {
-    ProguardClassNameList.Builder builder = ProguardClassNameList.builder();
-    for (String pattern : patterns) {
-      boolean isNegated = pattern.startsWith("!");
-      String actualPattern = isNegated ? pattern.substring(1) : pattern;
-      builder.addClassName(isNegated,
-          ProguardTypeMatcher.create(actualPattern, ClassOrType.CLASS, dexItemFactory));
+    return matchClassName(className, ImmutableList.of(Arrays.asList(patterns)));
+  }
+
+  private static boolean matchClassName(String className, List<List<String>> patternsList) {
+    ProguardClassFilter.Builder builder = ProguardClassFilter.builder();
+    for (List<String> patterns : patternsList) {
+      ProguardClassNameList.Builder listBuilder = ProguardClassNameList.builder();
+      for (String pattern : patterns) {
+        boolean isNegated = pattern.startsWith("!");
+        String actualPattern = isNegated ? pattern.substring(1) : pattern;
+        listBuilder.addClassName(isNegated,
+            ProguardTypeMatcher.create(actualPattern, ClassOrType.CLASS, dexItemFactory));
+      }
+      builder.addPattern(listBuilder.build());
     }
     return builder.build()
         .matches(dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(className)));
@@ -65,6 +76,18 @@
 
     assertTrue(matchClassName("boobar", "!foobar", "*bar"));
     assertFalse(matchClassName("foobar", "!foobar", "*bar"));
+
+    assertFalse(matchClassName("foo", "!boo"));
+    assertFalse(matchClassName("foo", "baz,!boo"));
+
+    assertFalse(matchClassName("boo", "!boo", "**"));
+    assertTrue(matchClassName("boo", "**", "!boo"));
+    assertTrue(matchClassName("boo",
+        ImmutableList.of(ImmutableList.of("!boo"), ImmutableList.of("**"))));
+
+    assertFalse(matchClassName("boofoo", "!boo*,*foo,boofoo"));
+    assertTrue(matchClassName("boofoo",
+        ImmutableList.of(ImmutableList.of("!boo*,*foo"), ImmutableList.of("boofoo"))));
   }
 
   private void assertMatchesBasicTypes(String pattern) {