Merge "Make Shrinker-specific utils instance methods."
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index 297d6df..34a3a93 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -6,7 +6,6 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.Reporter;
 import java.nio.file.Path;
@@ -36,7 +35,7 @@
     programConsumer = null;
     mode = null;
     minApiLevel = 0;
-    reporter = new Reporter(new DefaultDiagnosticsHandler());
+    reporter = new Reporter();
     enableDesugaring = true;
     optimizeMultidexForLinearAlloc = false;
   }
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 964f699..2441034 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -7,7 +7,6 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
@@ -38,20 +37,20 @@
     }
   }
 
-  private static class DefaultD8DiagnosticsHandler extends DefaultDiagnosticsHandler {
+  private static class DefaultD8DiagnosticsHandler implements DiagnosticsHandler {
 
     @Override
     public void error(Diagnostic error) {
       if (error instanceof DexFileOverflowDiagnostic) {
         DexFileOverflowDiagnostic overflowDiagnostic = (DexFileOverflowDiagnostic) error;
         if (!overflowDiagnostic.hasMainDexSpecification()) {
-          super.error(
+          DiagnosticsHandler.super.error(
               new StringDiagnostic(
                   overflowDiagnostic.getDiagnosticMessage() + ". Try supplying a main-dex list"));
           return;
         }
       }
-      super.error(error);
+      DiagnosticsHandler.super.error(error);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java b/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
index 2d06da8..ec7bc53 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
@@ -12,7 +12,6 @@
 import com.android.tools.r8.shaking.ProguardConfigurationSourceFile;
 import com.android.tools.r8.shaking.ProguardConfigurationSourceStrings;
 import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
@@ -199,7 +198,7 @@
     this.factory = new DexItemFactory();
     this.mainDexKeepRules = ImmutableList.of();
     this.mainDexListConsumer = null;
-    this.reporter = new Reporter(new DefaultDiagnosticsHandler());
+    this.reporter = new Reporter();
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 78a9d3f..96e1e99 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -17,7 +17,6 @@
 import com.android.tools.r8.shaking.ProguardConfigurationSourceFile;
 import com.android.tools.r8.shaking.ProguardConfigurationSourceStrings;
 import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
 import com.android.tools.r8.utils.ExceptionDiagnostic;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.InternalOptions;
@@ -58,21 +57,21 @@
   @Keep
   public static class Builder extends BaseCompilerCommand.Builder<R8Command, Builder> {
 
-    private static class DefaultR8DiagnosticsHandler extends DefaultDiagnosticsHandler {
+    private static class DefaultR8DiagnosticsHandler implements DiagnosticsHandler {
 
       @Override
       public void error(Diagnostic error) {
         if (error instanceof DexFileOverflowDiagnostic) {
           DexFileOverflowDiagnostic overflowDiagnostic = (DexFileOverflowDiagnostic) error;
           if (!overflowDiagnostic.hasMainDexSpecification()) {
-            super.error(
+            DiagnosticsHandler.super.error(
                 new StringDiagnostic(
                     overflowDiagnostic.getDiagnosticMessage()
                         + ". Try supplying a main-dex list or main-dex rules"));
             return;
           }
         }
-        super.error(error);
+        DiagnosticsHandler.super.error(error);
       }
     }
 
diff --git a/src/main/java/com/android/tools/r8/ReadKeepFile.java b/src/main/java/com/android/tools/r8/ReadKeepFile.java
index 07d5044..c953c8d 100644
--- a/src/main/java/com/android/tools/r8/ReadKeepFile.java
+++ b/src/main/java/com/android/tools/r8/ReadKeepFile.java
@@ -5,7 +5,6 @@
 
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.shaking.ProguardConfigurationParser;
-import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.Timing;
 import java.nio.file.Paths;
@@ -22,8 +21,7 @@
   private void readProguardKeepFile(String fileName) {
     System.out.println("  - reading " + fileName);
     timing.begin("Reading " + fileName);
-    new ProguardConfigurationParser(new DexItemFactory(),
-        new Reporter(new DefaultDiagnosticsHandler()))
+    new ProguardConfigurationParser(new DexItemFactory(), new Reporter())
         .parse(Paths.get(fileName));
     timing.end();
   }
diff --git a/src/main/java/com/android/tools/r8/dex/DexParser.java b/src/main/java/com/android/tools/r8/dex/DexParser.java
index d762646..657e029 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -59,7 +59,6 @@
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.PathOrigin;
-import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
 import com.android.tools.r8.utils.Pair;
 import com.google.common.io.ByteStreams;
 import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
@@ -97,8 +96,9 @@
   }
 
   private static DexSection[] parseMapFrom(DexReader dexReader) {
-    DexParser dexParser = new DexParser(dexReader,
-        ClassKind.PROGRAM, new DexItemFactory(), new DefaultDiagnosticsHandler());
+    DexParser dexParser =
+        new DexParser(
+            dexReader, ClassKind.PROGRAM, new DexItemFactory(), new DiagnosticsHandler() {});
     return dexParser.dexSections;
   }
 
diff --git a/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java b/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java
index 321482e..f83012f 100644
--- a/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java
+++ b/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java
@@ -12,7 +12,6 @@
 import com.android.tools.r8.Keep;
 import com.android.tools.r8.origin.PathOrigin;
 import com.android.tools.r8.utils.AbortException;
-import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
 import com.android.tools.r8.utils.ExceptionDiagnostic;
 import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.FeatureClassMapping;
@@ -87,7 +86,7 @@
 
   @Keep
   public static final class Options {
-    private final DiagnosticsHandler diagnosticsHandler = new DefaultDiagnosticsHandler();
+    private final DiagnosticsHandler diagnosticsHandler = new DiagnosticsHandler() {};
     private List<String> inputArchives = new ArrayList<>();
     private List<FeatureJar> featureJars = new ArrayList<>();
     private List<String> baseJars = new ArrayList<>();
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 400e5ba..0f0b5e5 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.graph;
 
 import static org.objectweb.asm.ClassReader.SKIP_CODE;
+import static org.objectweb.asm.ClassReader.SKIP_DEBUG;
 import static org.objectweb.asm.ClassReader.SKIP_FRAMES;
 import static org.objectweb.asm.Opcodes.ACC_DEPRECATED;
 import static org.objectweb.asm.Opcodes.ASM6;
@@ -27,6 +28,7 @@
 import com.android.tools.r8.graph.DexValue.DexValueString;
 import com.android.tools.r8.graph.DexValue.DexValueType;
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.FieldSignatureEquivalence;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.MethodSignatureEquivalence;
@@ -93,9 +95,18 @@
     input.reset();
 
     ClassReader reader = new ClassReader(input);
+
+    int parsingOptions = SKIP_FRAMES | SKIP_CODE;
+
+    // If the source-file and source-debug-extension attributes are not kept we can skip all debug
+    // related attributes when parsing the class structure.
+    ProguardKeepAttributes keep = application.options.proguardConfiguration.getKeepAttributes();
+    if (!keep.sourceFile && !keep.sourceDebugExtension) {
+      parsingOptions |= SKIP_DEBUG;
+    }
     reader.accept(
         new CreateDexClassVisitor(origin, classKind, reader.b, application, classConsumer),
-        SKIP_FRAMES | SKIP_CODE);
+        parsingOptions);
   }
 
   private static int cleanAccessFlags(int access) {
diff --git a/src/main/java/com/android/tools/r8/graph/JarCode.java b/src/main/java/com/android/tools/r8/graph/JarCode.java
index b978408..0668d05 100644
--- a/src/main/java/com/android/tools/r8/graph/JarCode.java
+++ b/src/main/java/com/android/tools/r8/graph/JarCode.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import java.io.PrintWriter;
@@ -163,7 +164,7 @@
       InternalOptions options,
       ValueNumberGenerator generator,
       Position callerPosition) {
-    if (!options.debug || options.testing.removeLocalsTable) {
+    if (!options.debug || !options.proguardConfiguration.getKeepAttributes().localVariableTable) {
       node.localVariables.clear();
     }
     JarSourceCode source =
@@ -241,9 +242,16 @@
   }
 
   private void parseCode(ReparseContext context, boolean useJsrInliner) {
+    // If the keep attributes do not specify keeping LocalVariableTable, LocalVariableTypeTable or
+    // LineNumberTable, then we can skip parsing all the debug related attributes during code read.
+    int parsingOptions = ClassReader.SKIP_FRAMES;
+    ProguardKeepAttributes keep = application.options.proguardConfiguration.getKeepAttributes();
+    if (!keep.localVariableTable && !keep.localVariableTypeTable && !keep.lineNumberTable) {
+      parsingOptions |= ClassReader.SKIP_DEBUG;
+    }
     SecondVisitor classVisitor = new SecondVisitor(createCodeLocator(context), useJsrInliner);
     try {
-      new ClassReader(context.classCache).accept(classVisitor, ClassReader.SKIP_FRAMES);
+      new ClassReader(context.classCache).accept(classVisitor, parsingOptions);
     } catch (Exception exception) {
       throw new CompilationError(
           "Unable to parse method `" + method.toSourceString() + "`", exception);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index c773eff..d3b57cd 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -46,10 +46,12 @@
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.shaking.VerticalClassMerger.VerticallyMergedClasses;
 import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableSet;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.ListIterator;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
 public class LensCodeRewriter {
@@ -69,18 +71,24 @@
     this.options = options;
   }
 
-  private Value makeOutValue(Instruction insn, IRCode code) {
+  private Value makeOutValue(
+      Instruction insn,
+      IRCode code,
+      ImmutableSet.Builder<Value> collector) {
     if (insn.outValue() == null) {
       return null;
     } else {
-      return code.createValue(insn.outValue().getTypeLattice(), insn.getLocalInfo());
+      Value newValue = code.createValue(insn.outValue().getTypeLattice(), insn.getLocalInfo());
+      collector.add(newValue);
+      return newValue;
     }
   }
 
   /**
    * Replace type appearances, invoke targets and field accesses with actual definitions.
    */
-  public void rewrite(IRCode code, DexEncodedMethod method) {
+  public Set<Value> rewrite(IRCode code, DexEncodedMethod method) {
+    ImmutableSet.Builder<Value> valueCollector = ImmutableSet.builder();
     ListIterator<BasicBlock> blocks = code.blocks.listIterator();
     while (blocks.hasNext()) {
       BasicBlock block = blocks.next();
@@ -118,10 +126,7 @@
               handle, method, NOT_ARGUMENT_TO_LAMBDA_METAFACTORY);
           if (newHandle != handle) {
             ConstMethodHandle newInstruction =
-                new ConstMethodHandle(
-                    code.createValue(
-                        current.outValue().getTypeLattice(), current.getLocalInfo()),
-                    newHandle);
+                new ConstMethodHandle(makeOutValue(current, code, valueCollector), newHandle);
             iterator.replaceCurrentInstruction(newInstruction);
           }
         } else if (current.isInvokeMethod()) {
@@ -145,8 +150,7 @@
             // Fix up the return type if needed.
             if (actualTarget.proto.returnType != invokedMethod.proto.returnType
                 && newInvoke.outValue() != null) {
-              Value newValue = code.createValue(
-                  newInvoke.outValue().getTypeLattice(), invoke.getLocalInfo());
+              Value newValue = makeOutValue(newInvoke, code, valueCollector);
               newInvoke.outValue().replaceUsers(newValue);
               CheckCast cast =
                   new CheckCast(
@@ -204,60 +208,66 @@
           CheckCast checkCast = current.asCheckCast();
           DexType newType = graphLense.lookupType(checkCast.getType());
           if (newType != checkCast.getType()) {
-            CheckCast newCheckCast =
-                new CheckCast(makeOutValue(checkCast, code), checkCast.object(), newType);
+            CheckCast newCheckCast = new CheckCast(
+                makeOutValue(checkCast, code, valueCollector), checkCast.object(), newType);
             iterator.replaceCurrentInstruction(newCheckCast);
           }
         } else if (current.isConstClass()) {
           ConstClass constClass = current.asConstClass();
           DexType newType = graphLense.lookupType(constClass.getValue());
           if (newType != constClass.getValue()) {
-            ConstClass newConstClass = new ConstClass(makeOutValue(constClass, code), newType);
+            ConstClass newConstClass = new ConstClass(
+                makeOutValue(constClass, code, valueCollector), newType);
             iterator.replaceCurrentInstruction(newConstClass);
           }
         } else if (current.isInstanceOf()) {
           InstanceOf instanceOf = current.asInstanceOf();
           DexType newType = graphLense.lookupType(instanceOf.type());
           if (newType != instanceOf.type()) {
-            InstanceOf newInstanceOf = new InstanceOf(makeOutValue(instanceOf, code),
-                instanceOf.value(), newType);
+            InstanceOf newInstanceOf = new InstanceOf(
+                makeOutValue(instanceOf, code, valueCollector), instanceOf.value(), newType);
             iterator.replaceCurrentInstruction(newInstanceOf);
           }
         } else if (current.isInvokeMultiNewArray()) {
           InvokeMultiNewArray multiNewArray = current.asInvokeMultiNewArray();
           DexType newType = graphLense.lookupType(multiNewArray.getArrayType());
           if (newType != multiNewArray.getArrayType()) {
-            InvokeMultiNewArray newMultiNewArray = new InvokeMultiNewArray(
-                newType, makeOutValue(multiNewArray, code), multiNewArray.inValues());
+            InvokeMultiNewArray newMultiNewArray =
+                new InvokeMultiNewArray(
+                    newType,
+                    makeOutValue(multiNewArray, code, valueCollector),
+                    multiNewArray.inValues());
             iterator.replaceCurrentInstruction(newMultiNewArray);
           }
         } else if (current.isInvokeNewArray()) {
           InvokeNewArray newArray = current.asInvokeNewArray();
           DexType newType = graphLense.lookupType(newArray.getArrayType());
           if (newType != newArray.getArrayType()) {
-            InvokeNewArray newNewArray = new InvokeNewArray(newType, makeOutValue(newArray, code),
-                newArray.inValues());
+            InvokeNewArray newNewArray = new InvokeNewArray(
+                newType, makeOutValue(newArray, code, valueCollector), newArray.inValues());
             iterator.replaceCurrentInstruction(newNewArray);
           }
         } else if (current.isNewArrayEmpty()) {
           NewArrayEmpty newArrayEmpty = current.asNewArrayEmpty();
           DexType newType = graphLense.lookupType(newArrayEmpty.type);
           if (newType != newArrayEmpty.type) {
-            NewArrayEmpty newNewArray = new NewArrayEmpty(makeOutValue(newArrayEmpty, code),
-                newArrayEmpty.size(), newType);
+            NewArrayEmpty newNewArray = new NewArrayEmpty(
+                makeOutValue(newArrayEmpty, code, valueCollector), newArrayEmpty.size(), newType);
             iterator.replaceCurrentInstruction(newNewArray);
           }
         } else if (current.isNewInstance()) {
           NewInstance newInstance= current.asNewInstance();
           DexType newClazz = graphLense.lookupType(newInstance.clazz);
           if (newClazz != newInstance.clazz) {
-            NewInstance newNewInstance = new NewInstance(newClazz, makeOutValue(newInstance, code));
+            NewInstance newNewInstance = new NewInstance(
+                newClazz, makeOutValue(newInstance, code, valueCollector));
             iterator.replaceCurrentInstruction(newNewInstance);
           }
         }
       }
     }
     assert code.isConsistentSSA();
+    return valueCollector.build();
   }
 
   // If the given invoke is on the form "invoke-direct A.<init>, v0, ..." and the definition of
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java
index fe31b14..49f99dc 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java
@@ -16,6 +16,9 @@
   public static final String ENCLOSING_METHOD = "EnclosingMethod";
   public static final String SIGNATURE = "Signature";
   public static final String EXCEPTIONS = "Exceptions";
+  public static final String LINE_NUMBER_TABLE = "LineNumberTable";
+  public static final String LOCAL_VARIABLE_TABLE = "LocalVariableTable";
+  public static final String LOCAL_VARIABLE_TYPE_TABLE = "LocalVariableTypeTable";
   public static final String SOURCE_DEBUG_EXTENSION = "SourceDebugExtension";
   public static final String RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations";
   public static final String RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations";
@@ -36,6 +39,9 @@
   public boolean enclosingMethod = false;
   public boolean signature = false;
   public boolean exceptions = false;
+  public boolean lineNumberTable = false;
+  public boolean localVariableTable = false;
+  public boolean localVariableTypeTable = false;
   public boolean sourceDebugExtension = false;
   public boolean runtimeVisibleAnnotations = false;
   public boolean runtimeInvisibleAnnotations = false;
@@ -108,6 +114,9 @@
     innerClasses = update(innerClasses, INNER_CLASSES, patterns);
     enclosingMethod = update(enclosingMethod, ENCLOSING_METHOD, patterns);
     signature = update(signature, SIGNATURE, patterns);
+    lineNumberTable = update(lineNumberTable, LINE_NUMBER_TABLE, patterns);
+    localVariableTable = update(localVariableTable, LOCAL_VARIABLE_TABLE, patterns);
+    localVariableTypeTable = update(localVariableTypeTable, LOCAL_VARIABLE_TYPE_TABLE, patterns);
     exceptions = update(exceptions, EXCEPTIONS, patterns);
     sourceDebugExtension = update(sourceDebugExtension, SOURCE_DEBUG_EXTENSION, patterns);
     runtimeVisibleAnnotations = update(runtimeVisibleAnnotations, RUNTIME_VISIBLE_ANNOTATIONS,
@@ -146,6 +155,18 @@
       throw new CompilationError("Attribute Signature requires InnerClasses attribute. Check "
           + "-keepattributes directive.");
     }
+    if (forceProguardCompatibility && localVariableTable && !lineNumberTable) {
+      // If locals are kept, assume line numbers should be kept too.
+      lineNumberTable = true;
+      compatibility.addKeepAttributePatterns(
+          ImmutableList.of(ProguardKeepAttributes.LINE_NUMBER_TABLE));
+    }
+    if (localVariableTable && !lineNumberTable) {
+      throw new CompilationError(
+          "Attribute " + LOCAL_VARIABLE_TABLE
+              + " requires " + LINE_NUMBER_TABLE
+              + ". Check -keepattributes directive.");
+    }
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index f087295..a4f55c3 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -79,7 +79,7 @@
   }
 
   static Reporter defaultReporter() {
-    return new Reporter(new DefaultDiagnosticsHandler());
+    return new Reporter();
   }
 
   /**
diff --git a/src/main/java/com/android/tools/r8/utils/DefaultDiagnosticsHandler.java b/src/main/java/com/android/tools/r8/utils/DefaultDiagnosticsHandler.java
deleted file mode 100644
index 68c2840..0000000
--- a/src/main/java/com/android/tools/r8/utils/DefaultDiagnosticsHandler.java
+++ /dev/null
@@ -1,9 +0,0 @@
-// 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.utils;
-
-import com.android.tools.r8.DiagnosticsHandler;
-
-public class DefaultDiagnosticsHandler implements DiagnosticsHandler {
-}
diff --git a/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java b/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
index 44cfc89..dcf521d 100644
--- a/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
+++ b/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
@@ -85,7 +85,7 @@
   }
 
   public static FeatureClassMapping fromSpecification(Path file) throws FeatureMappingException {
-    return fromSpecification(file, new DefaultDiagnosticsHandler());
+    return fromSpecification(file, new DiagnosticsHandler() {});
   }
 
   public static FeatureClassMapping fromSpecification(Path file, DiagnosticsHandler reporter)
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 e0e4227..828a463 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -57,7 +57,7 @@
 
   // Constructor for testing and/or other utilities.
   public InternalOptions() {
-    reporter = new Reporter(new DefaultDiagnosticsHandler());
+    reporter = new Reporter();
     itemFactory = new DexItemFactory();
     proguardConfiguration = ProguardConfiguration.defaultConfiguration(itemFactory, reporter);
   }
@@ -462,7 +462,6 @@
     public boolean nondeterministicCycleElimination = false;
     public Set<Inliner.Reason> validInliningReasons = null;
     public boolean suppressExperimentalCfBackendWarning = false;
-    public boolean removeLocalsTable = false;
   }
 
   public boolean canUseInvokePolymorphicOnVarHandle() {
diff --git a/src/main/java/com/android/tools/r8/utils/Reporter.java b/src/main/java/com/android/tools/r8/utils/Reporter.java
index 37f57da..5a1bd06 100644
--- a/src/main/java/com/android/tools/r8/utils/Reporter.java
+++ b/src/main/java/com/android/tools/r8/utils/Reporter.java
@@ -20,6 +20,10 @@
   private Diagnostic lastError;
   private final Collection<Throwable> suppressedExceptions = new ArrayList<>();
 
+  public Reporter() {
+    this(new DiagnosticsHandler() {});
+  }
+
   public Reporter(DiagnosticsHandler clientHandler) {
     this.clientHandler = clientHandler;
   }
diff --git a/src/test/examples/classmerging/keep-rules.txt b/src/test/examples/classmerging/keep-rules.txt
index 97be613..5e70808 100644
--- a/src/test/examples/classmerging/keep-rules.txt
+++ b/src/test/examples/classmerging/keep-rules.txt
@@ -2,6 +2,9 @@
 # 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.
 
+# Keep line numbers to ensure method mappings in the map file.
+-keepattributes LineNumberTable
+
 # Keep the application entry point. Get rid of everything that is not
 # reachable from there.
 -keep public class classmerging.Test {
@@ -57,6 +60,3 @@
 }
 
 -printmapping
-
-# TODO(herhut): Consider supporting merging of inner-class attributes.
-# -keepattributes *
\ No newline at end of file
diff --git a/src/test/examples/shaking1/keep-rules.txt b/src/test/examples/shaking1/keep-rules.txt
index 66cf1c6..82786d5 100644
--- a/src/test/examples/shaking1/keep-rules.txt
+++ b/src/test/examples/shaking1/keep-rules.txt
@@ -2,6 +2,8 @@
 # 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.
 
+-keepattributes LineNumberTable
+
 # Keep the application entry point. Get rid of everything that is not
 # reachable from there.
 -keep public class shaking1.Shaking {
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index fa35d0d..6f93df4 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -16,7 +16,6 @@
 import com.android.tools.r8.code.Instruction;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.CfCode;
-import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.SmaliWriter;
@@ -711,7 +710,7 @@
 
   protected ProcessResult runOnVMRaw(AndroidApp app, Class<?> mainClass, Backend backend)
       throws IOException {
-    return runOnVMRaw(app, mainClass.getCanonicalName(), backend);
+    return runOnVMRaw(app, mainClass.getTypeName(), backend);
   }
 
   protected ProcessResult runOnVMRaw(AndroidApp app, String mainClass, Backend backend)
@@ -830,13 +829,6 @@
         .map(kind::cast);
   }
 
-  protected Stream<CfInstruction> filterInstructionKind(
-      CfCode code, Class<? extends CfInstruction> kind) {
-    return code.getInstructions().stream()
-        .filter(kind::isInstance)
-        .map(kind::cast);
-  }
-
   public enum MinifyMode {
     NONE,
     JAVA,
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index f5eaa25..6dde9ed 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -23,7 +23,6 @@
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.AndroidAppConsumers;
-import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ListUtils;
@@ -808,7 +807,7 @@
   public static ProguardConfiguration loadProguardConfiguration(
       DexItemFactory factory, List<Path> configPaths)
       throws IOException, ProguardRuleParserException {
-    Reporter reporter = new Reporter(new DefaultDiagnosticsHandler());
+    Reporter reporter = new Reporter();
     if (configPaths.isEmpty()) {
       return ProguardConfiguration.defaultConfiguration(factory, reporter);
     }
@@ -949,7 +948,7 @@
   }
 
   public static ProcessResult runJava(Class clazz) throws Exception {
-    String main = clazz.getCanonicalName();
+    String main = clazz.getTypeName();
     Path path = getClassPathForTests();
     return runJava(path, main);
   }
diff --git a/src/test/java/com/android/tools/r8/cf/MethodHandleTestRunner.java b/src/test/java/com/android/tools/r8/cf/MethodHandleTestRunner.java
index 6333638..b85b15b 100644
--- a/src/test/java/com/android/tools/r8/cf/MethodHandleTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/MethodHandleTestRunner.java
@@ -19,7 +19,6 @@
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.Reporter;
 import java.nio.file.Path;
@@ -184,7 +183,7 @@
       if (frontend == Frontend.CF && compilationMode == CompilationMode.DEBUG) {
         // TODO(b/79725635): Investigate why these tests fail on the buildbot.
         // Use a Reporter to extract origin info to standard error.
-        new Reporter(new DefaultDiagnosticsHandler()).error(e);
+        new Reporter().error(e);
         // Print the stack trace since this is not always printed by JUnit.
         e.printStackTrace();
         Assume.assumeNoException(
diff --git a/src/test/java/com/android/tools/r8/debug/ArraySimplificationLineNumberTestRunner.java b/src/test/java/com/android/tools/r8/debug/ArraySimplificationLineNumberTestRunner.java
index 308e8d7..e0b953a 100644
--- a/src/test/java/com/android/tools/r8/debug/ArraySimplificationLineNumberTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debug/ArraySimplificationLineNumberTestRunner.java
@@ -27,7 +27,7 @@
     DebugTestConfig d8NoLocals = new D8DebugTestConfig().compileAndAdd(
         temp,
         Collections.singletonList(ToolHelper.getClassFileForTestClass(CLASS)),
-        options -> options.testing.removeLocalsTable = true);
+        options -> options.proguardConfiguration.getKeepAttributes().localVariableTable = false);
 
     new DebugStreamComparator()
         .add("CF", streamDebugTest(cf, NAME, NO_FILTER))
diff --git a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
index a518a36..7e3c10a 100644
--- a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
+++ b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
@@ -28,7 +28,6 @@
 import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.origin.SynthesizedOrigin;
-import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Reporter;
@@ -136,8 +135,7 @@
     DexApplication application = builder.build();
 
     CollectInfoConsumer consumer = new CollectInfoConsumer();
-    InternalOptions options = new InternalOptions(dexItemFactory,
-        new Reporter(new DefaultDiagnosticsHandler()));
+    InternalOptions options = new InternalOptions(dexItemFactory, new Reporter());
     options.programConsumer = consumer;
     ApplicationWriter writer =
         new ApplicationWriter(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NullArrayAndNullObjectValueTest.java b/src/test/java/com/android/tools/r8/ir/optimize/NullArrayAndNullObjectValueTest.java
index 7b1c369..faff5a4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/NullArrayAndNullObjectValueTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/NullArrayAndNullObjectValueTest.java
@@ -9,13 +9,13 @@
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.D8;
 import com.android.tools.r8.D8Command;
+import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.jasmin.JasminBuilder;
 import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
 import com.android.tools.r8.jasmin.JasminTestBase;
-import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
 import com.google.common.collect.ImmutableList;
 import java.nio.file.Path;
 import org.junit.Assert;
@@ -127,7 +127,7 @@
         "return");
 
     Path riJar = temp.getRoot().toPath().resolve("ri-out.jar");
-    jasminBuilder.writeJar(riJar, new DefaultDiagnosticsHandler());
+    jasminBuilder.writeJar(riJar, new DiagnosticsHandler() {});
     ProcessResult riResult = ToolHelper.runJava(riJar, "TestClass");
     Assert.assertEquals(riResult.toString(), 0, riResult.exitCode);
 
@@ -174,7 +174,7 @@
         "return");
 
     Path riJar = temp.getRoot().toPath().resolve("ri-out.jar");
-    jasminBuilder.writeJar(riJar, new DefaultDiagnosticsHandler());
+    jasminBuilder.writeJar(riJar, new DiagnosticsHandler() {});
     ProcessResult riResult = ToolHelper.runJava(riJar, "TestClass");
     Assert.assertEquals(riResult.toString(), 1, riResult.exitCode);
     assertTrue(riResult.stderr.contains("VerifyError"));
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/checkcast/CheckCastDebugTestRunner.java b/src/test/java/com/android/tools/r8/ir/optimize/checkcast/CheckCastDebugTestRunner.java
index 44720ed..1eddbd3 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/checkcast/CheckCastDebugTestRunner.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/checkcast/CheckCastDebugTestRunner.java
@@ -13,19 +13,18 @@
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.cf.code.CfCheckCast;
 import com.android.tools.r8.debug.CfDebugTestConfig;
 import com.android.tools.r8.debug.DebugTestBase;
 import com.android.tools.r8.debug.DebugTestConfig;
 import com.android.tools.r8.debug.DexDebugTestConfig;
-import com.android.tools.r8.graph.Code;
-import com.android.tools.r8.code.CheckCast;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Streams;
 import java.nio.file.Path;
 import java.util.Arrays;
 import java.util.Collection;
@@ -82,7 +81,8 @@
     ClassSubject classSubject = inspector.clazz(MAIN);
     MethodSubject method = classSubject.method("void", "differentLocals", ImmutableList.of());
     assertThat(method, isPresent());
-    long count = countCheckCast(method.getMethod().getCode());
+    long count =
+        Streams.stream(method.iterateInstructions(InstructionSubject::isCheckCast)).count();
     assertEquals(1, count);
 
     DebugTestConfig config = backend == Backend.CF
@@ -134,7 +134,8 @@
     ClassSubject classSubject = inspector.clazz(MAIN);
     MethodSubject method = classSubject.method("void", "sameLocal", ImmutableList.of());
     assertThat(method, isPresent());
-    long count = countCheckCast(method.getMethod().getCode());
+    long count =
+        Streams.stream(method.iterateInstructions(InstructionSubject::isCheckCast)).count();
     assertEquals(1, count);
 
     DebugTestConfig config = backend == Backend.CF
@@ -166,13 +167,4 @@
     );
   }
 
-  private long countCheckCast(Code code) {
-    if (backend == Backend.DEX) {
-      return filterInstructionKind(code.asDexCode(), CheckCast.class).count();
-    } else {
-      assert backend == Backend.CF;
-      return filterInstructionKind(code.asCfCode(), CfCheckCast.class).count();
-    }
-  }
-
 }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
index caa697b..e65ebca 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
@@ -40,6 +40,7 @@
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.FieldAccessInstructionSubject;
@@ -429,10 +430,11 @@
   }
 
   private String getProguardConfig(String main) {
-    return keepMainProguardConfiguration(main)
-        + "\n"
-        + "-dontobfuscate\n"
-        + "-allowaccessmodification";
+    return StringUtils.joinLines(
+        keepMainProguardConfiguration(main),
+        "-dontobfuscate",
+        "-allowaccessmodification",
+        "-keepattributes LineNumberTable");
   }
 
   private void configure(InternalOptions options) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index 67afdd7..6b7a7c2 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -10,7 +10,6 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.code.InvokeStatic;
 import com.android.tools.r8.code.NewInstance;
 import com.android.tools.r8.code.SgetObject;
 import com.android.tools.r8.graph.DexClass;
@@ -19,9 +18,12 @@
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
+import com.google.common.collect.Streams;
 import java.util.Collection;
 import java.util.List;
 import java.util.Set;
@@ -229,8 +231,8 @@
   private List<String> collectStaticCalls(ClassSubject clazz, String methodName, String... params) {
     assertNotNull(clazz);
     MethodSignature signature = new MethodSignature(methodName, "void", params);
-    DexCode code = clazz.method(signature).getMethod().getCode().asDexCode();
-    return filterInstructionKind(code, InvokeStatic.class)
+    MethodSubject method = clazz.method(signature);
+    return Streams.stream(method.iterateInstructions(InstructionSubject::isInvokeStatic))
         .map(insn -> insn.getMethod().toSourceString())
         .sorted()
         .collect(Collectors.toList());
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index 43bf86e..4f91609 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -59,7 +59,6 @@
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.AndroidAppConsumers;
-import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.InternalOptions;
@@ -660,7 +659,7 @@
       List<String> classes, int minApi, boolean intermediate, int methodCount)
       throws IOException, ExecutionException {
     return generateApplication(
-        classes, minApi, intermediate, methodCount, new DefaultDiagnosticsHandler());
+        classes, minApi, intermediate, methodCount, new DiagnosticsHandler() {});
   }
 
   private static AndroidApp generateApplication(
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/IndirectSuperInterfaceTest.java b/src/test/java/com/android/tools/r8/memberrebinding/IndirectSuperInterfaceTest.java
new file mode 100644
index 0000000..b280242
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/memberrebinding/IndirectSuperInterfaceTest.java
@@ -0,0 +1,95 @@
+// Copyright (c) 2018, 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.memberrebinding;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.R8Command.Builder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidAppConsumers;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class IndirectSuperInterfaceTest extends TestBase {
+
+  public interface Interface {
+    @NeverInline
+    default void foo() {
+      System.out.print("Interface::foo ");
+    }
+  }
+
+  public static class A implements Interface {
+    // Intentionally empty.
+  }
+
+  public static class B extends A {
+    @Override
+    public void foo() {
+      System.out.print("B::foo ");
+      super.foo();
+    }
+
+    public static void main(String[] args) {
+      new B().foo();
+    }
+  }
+
+  private final Backend backend;
+
+  @Parameters(name = "{0}")
+  public static Backend[] setup() {
+    return new Backend[] {Backend.CF, Backend.DEX};
+  }
+
+  public IndirectSuperInterfaceTest(Backend backend) {
+    this.backend = backend;
+  }
+
+  @Test
+  public void test() throws Exception {
+    String expected = "B::foo Interface::foo ";
+    String reference = runOnJava(B.class);
+    assertEquals(expected, reference);
+
+    AndroidAppConsumers sink = new AndroidAppConsumers();
+    Builder builder =
+        R8Command.builder()
+            .addClassProgramData(ToolHelper.getClassAsBytes(Interface.class), Origin.unknown())
+            .addClassProgramData(ToolHelper.getClassAsBytes(A.class), Origin.unknown())
+            .addClassProgramData(ToolHelper.getClassAsBytes(B.class), Origin.unknown())
+            .setProgramConsumer(sink.wrapProgramConsumer(emptyConsumer(backend)))
+            .addLibraryFiles(runtimeJar(backend))
+            .addProguardConfiguration(
+                ImmutableList.of(
+                    "-keep class " + Interface.class.getTypeName(),
+                    "-keep class " + A.class.getTypeName(),
+                    keepMainProguardConfigurationWithInliningAnnotation(B.class)),
+                Origin.unknown());
+    ToolHelper.allowTestProguardOptions(builder);
+    if (backend == Backend.DEX) {
+      builder.setMinApiLevel(ToolHelper.getMinApiLevelForDexVm().getLevel());
+    }
+    R8.run(builder.build());
+
+    ProcessResult result = runOnVMRaw(sink.build(), B.class, backend);
+
+    // TODO(b/117407667): Assert the test does not fail once fixed.
+    assertTrue(result.toString(), result.exitCode == (backend == Backend.DEX ? 0 : 1));
+    if (result.exitCode == 0) {
+      assertEquals(reference, result.stdout);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
index e340c98..719fe80 100644
--- a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
+++ b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
@@ -15,7 +15,6 @@
 import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.shaking.RootSetBuilder;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
-import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.ThreadUtils;
@@ -70,8 +69,7 @@
       throws IOException, ProguardRuleParserException, ExecutionException {
     ProguardConfiguration configuration =
         ToolHelper.loadProguardConfiguration(dexItemFactory, configPaths);
-    InternalOptions options = new InternalOptions(configuration,
-        new Reporter(new DefaultDiagnosticsHandler()));
+    InternalOptions options = new InternalOptions(configuration, new Reporter());
 
     ExecutorService executor = ThreadUtils.getExecutorService(1);
 
diff --git a/src/test/java/com/android/tools/r8/naming/WarnReflectiveAccessTest.java b/src/test/java/com/android/tools/r8/naming/WarnReflectiveAccessTest.java
index 9ccc94b..5127a59 100644
--- a/src/test/java/com/android/tools/r8/naming/WarnReflectiveAccessTest.java
+++ b/src/test/java/com/android/tools/r8/naming/WarnReflectiveAccessTest.java
@@ -112,6 +112,7 @@
                         + " <methods>;"
                         + "}",
                     "-printmapping",
+                    "-keepattributes LineNumberTable",
                     reflectionRules),
                 Origin.unknown())
             .setOutput(out, outputMode(backend));
diff --git a/src/test/java/com/android/tools/r8/shaking/KeepAttributesTest.java b/src/test/java/com/android/tools/r8/shaking/KeepAttributesTest.java
new file mode 100644
index 0000000..275d2e9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/KeepAttributesTest.java
@@ -0,0 +1,122 @@
+// Copyright (c) 2018, 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.debuginfo.DebugInfoInspector;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.shaking.forceproguardcompatibility.keepattributes.TestKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class KeepAttributesTest extends TestBase {
+
+  public static final Class CLASS = TestKeepAttributes.class;
+
+  @Test
+  public void discardAllAttributes()
+      throws CompilationFailedException, IOException, ExecutionException {
+    List<String> keepRules = ImmutableList.of(
+        "-keep class ** { *; }"
+    );
+    CodeInspector inspector = compile(keepRules);
+    DebugInfoInspector debugInfo = debugInfoForMain(inspector);
+    checkLineNumbers(false, debugInfo);
+    checkLocals(false, debugInfo);
+  }
+
+  @Test
+  public void keepLineNumberTable()
+      throws CompilationFailedException, IOException, ExecutionException {
+    List<String> keepRules = ImmutableList.of(
+        "-keep class ** { *; }",
+        "-keepattributes " + ProguardKeepAttributes.LINE_NUMBER_TABLE
+    );
+    CodeInspector inspector = compile(keepRules);
+    DebugInfoInspector debugInfo = debugInfoForMain(inspector);
+    checkLineNumbers(true, debugInfo);
+    checkLocals(false, debugInfo);
+  }
+
+  @Test
+  public void keepLineNumberTableAndLocalVariableTable()
+      throws CompilationFailedException, IOException, ExecutionException {
+    List<String> keepRules = ImmutableList.of(
+        "-keep class ** { *; }",
+        "-keepattributes "
+            + ProguardKeepAttributes.LINE_NUMBER_TABLE
+            + ", "
+            + ProguardKeepAttributes.LOCAL_VARIABLE_TABLE
+    );
+    CodeInspector inspector = compile(keepRules);
+    DebugInfoInspector debugInfo = debugInfoForMain(inspector);
+    checkLineNumbers(true, debugInfo);
+    checkLocals(true, debugInfo);
+  }
+
+  @Test
+  public void keepLocalVariableTable() throws IOException, ExecutionException {
+    List<String> keepRules = ImmutableList.of(
+        "-keep class ** { *; }",
+        "-keepattributes " + ProguardKeepAttributes.LOCAL_VARIABLE_TABLE
+    );
+    // Compiling with a keep rule for locals but no line results in an error in R8.
+    try {
+      compile(keepRules);
+    } catch (CompilationFailedException e) {
+      assertTrue(e.getCause().getMessage().contains(ProguardKeepAttributes.LOCAL_VARIABLE_TABLE));
+      assertTrue(e.getCause().getMessage().contains(ProguardKeepAttributes.LINE_NUMBER_TABLE));
+      return;
+    }
+    fail("Expected error");
+  }
+
+  private CodeInspector compile(List<String> keepRules)
+      throws CompilationFailedException, IOException, ExecutionException {
+    Path dexOut = temp.getRoot().toPath().resolve("dex.zip");
+    R8.run(R8Command.builder()
+        .setMode(CompilationMode.DEBUG)
+        .addProgramFiles(ToolHelper.getClassFileForTestClass(CLASS))
+        .addLibraryFiles(ToolHelper.getDefaultAndroidJar())
+        .addProguardConfiguration(keepRules, Origin.unknown())
+        .setProgramConsumer(new DexIndexedConsumer.ArchiveConsumer(dexOut))
+        .build());
+    ToolHelper.runArtRaw(dexOut.toString(), CLASS.getCanonicalName());
+    return new CodeInspector(dexOut);
+  }
+
+  private DebugInfoInspector debugInfoForMain(CodeInspector inspector) {
+    return new DebugInfoInspector(
+        inspector,
+        CLASS.getCanonicalName(),
+        new MethodSignature("main", "void", Collections.singleton("java.lang.String[]")));
+  }
+
+  private void checkLineNumbers(boolean expected, DebugInfoInspector debugInfo) {
+    assertEquals("Expected " + (expected ? "line entries" : "no line entries"),
+        expected, debugInfo.getEntries().stream().anyMatch(e -> e.lineEntry));
+  }
+
+  private void checkLocals(boolean expected, DebugInfoInspector debugInfo) {
+    assertEquals("Expected " + (expected ? "locals" : "no locals"),
+        expected, debugInfo.getEntries().stream().anyMatch(e -> !e.locals.isEmpty()));
+  }
+}
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 f8907d6..1ee19bc 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -28,7 +28,6 @@
 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.FileUtils;
 import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
 import com.android.tools.r8.utils.KeepingDiagnosticHandler;
@@ -208,8 +207,7 @@
   public void parseNonJavaIdentifiers() throws Exception {
     DexItemFactory dexItemFactory = new DexItemFactory();
     ProguardConfigurationParser parser =
-        new ProguardConfigurationParser(dexItemFactory,
-            new Reporter(new DefaultDiagnosticsHandler()));
+        new ProguardConfigurationParser(dexItemFactory, new Reporter());
     String nonJavaIdentifiers =
         String.join("\n", ImmutableList.of(
             "-keep class -package-.-ClassNameWithDash-{",
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 7e4c4ef..9f9b797 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
@@ -33,7 +33,6 @@
 import com.android.tools.r8.shaking.forceproguardcompatibility.keepattributes.TestKeepAttributes;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -162,8 +161,7 @@
 
     // Check the Proguard compatibility rules generated.
     ProguardConfigurationParser parser =
-        new ProguardConfigurationParser(new DexItemFactory(),
-            new Reporter(new DefaultDiagnosticsHandler()));
+        new ProguardConfigurationParser(new DexItemFactory(), new Reporter());
     parser.parse(proguardCompatibilityRules);
     ProguardConfiguration configuration = parser.getConfigRawForTesting();
     if (forceProguardCompatibility && hasDefaultConstructor) {
@@ -283,8 +281,7 @@
 
     // Check the Proguard compatibility rules generated.
     ProguardConfigurationParser parser =
-        new ProguardConfigurationParser(new DexItemFactory(),
-            new Reporter(new DefaultDiagnosticsHandler()));
+        new ProguardConfigurationParser(new DexItemFactory(), new Reporter());
     parser.parse(proguardCompatibilityRules);
     ProguardConfiguration configuration = parser.getConfigRawForTesting();
     if (forceProguardCompatibility) {
@@ -383,8 +380,7 @@
 
     // Check the Proguard compatibility rules generated.
     ProguardConfigurationParser parser =
-        new ProguardConfigurationParser(new DexItemFactory(),
-            new Reporter(new DefaultDiagnosticsHandler()));
+        new ProguardConfigurationParser(new DexItemFactory(), new Reporter());
     parser.parse(proguardCompatibilityRules);
     ProguardConfiguration configuration = parser.getConfigRawForTesting();
     if (forceProguardCompatibility) {
@@ -489,8 +485,7 @@
 
     // Check the Proguard compatibility rules generated.
     ProguardConfigurationParser parser =
-        new ProguardConfigurationParser(new DexItemFactory(),
-            new Reporter(new DefaultDiagnosticsHandler()));
+        new ProguardConfigurationParser(new DexItemFactory(), new Reporter());
     parser.parse(proguardCompatibilityRules);
     ProguardConfiguration configuration = parser.getConfigRawForTesting();
     if (forceProguardCompatibility) {
@@ -605,8 +600,7 @@
 
     // Check the Proguard compatibility configuration generated.
     ProguardConfigurationParser parser =
-        new ProguardConfigurationParser(new DexItemFactory(),
-            new Reporter(new DefaultDiagnosticsHandler()));
+        new ProguardConfigurationParser(new DexItemFactory(), new Reporter());
     parser.parse(proguardCompatibilityRules);
     System.out.println(proguardCompatibilityRules);
     ProguardConfiguration configuration = parser.getConfigRawForTesting();
@@ -657,8 +651,7 @@
     inspection.accept(new CodeInspector(app));
     // Check the Proguard compatibility configuration generated.
     ProguardConfigurationParser parser =
-        new ProguardConfigurationParser(new DexItemFactory(),
-            new Reporter(new DefaultDiagnosticsHandler()));
+        new ProguardConfigurationParser(new DexItemFactory(), new Reporter());
     parser.parse(proguardCompatibilityRules);
     ProguardConfiguration configuration = parser.getConfigRawForTesting();
     compatInspection.accept(configuration);
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
index 3203f9c..dedb839 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
@@ -24,6 +24,8 @@
 import com.android.tools.r8.cf.code.CfStackInstruction;
 import com.android.tools.r8.cf.code.CfSwitch;
 import com.android.tools.r8.cf.code.CfThrow;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.ir.code.ValueType;
 import org.objectweb.asm.Opcodes;
 
@@ -40,6 +42,41 @@
   }
 
   @Override
+  public boolean isInstancePut() {
+    return instruction instanceof CfFieldInstruction
+        && ((CfFieldInstruction) instruction).getOpcode() == Opcodes.PUTFIELD;
+  }
+
+  @Override
+  public boolean isStaticPut() {
+    return instruction instanceof CfFieldInstruction
+        && ((CfFieldInstruction) instruction).getOpcode() == Opcodes.PUTSTATIC;
+  }
+
+  @Override
+  public boolean isInstanceGet() {
+    return instruction instanceof CfFieldInstruction
+        && ((CfFieldInstruction) instruction).getOpcode() == Opcodes.GETFIELD;
+  }
+
+  @Override
+  public boolean isStaticGet() {
+    return instruction instanceof CfFieldInstruction
+        && ((CfFieldInstruction) instruction).getOpcode() == Opcodes.GETSTATIC;
+  }
+
+  @Override
+  public DexField getField() {
+    assert isFieldAccess();
+    return ((CfFieldInstruction) instruction).getField();
+  }
+
+  @Override
+  public boolean isInvoke() {
+    return instruction instanceof CfInvoke || instruction instanceof CfInvokeDynamic;
+  }
+
+  @Override
   public boolean isInvokeVirtual() {
     return instruction instanceof CfInvoke
         && ((CfInvoke) instruction).getOpcode() == Opcodes.INVOKEVIRTUAL;
@@ -58,6 +95,12 @@
   }
 
   @Override
+  public DexMethod getMethod() {
+    assert isInvoke();
+    return ((CfInvoke) instruction).getMethod();
+  }
+
+  @Override
   public boolean isNop() {
     return instruction instanceof CfNop;
   }
@@ -105,40 +148,11 @@
   }
 
   @Override
-  public boolean isInvoke() {
-    return instruction instanceof CfInvoke || instruction instanceof CfInvokeDynamic;
-  }
-
-  @Override
   public boolean isNewInstance() {
     return instruction instanceof CfNew;
   }
 
   @Override
-  public boolean isInstancePut() {
-    return instruction instanceof CfFieldInstruction
-        && ((CfFieldInstruction) instruction).getOpcode() == Opcodes.PUTFIELD;
-  }
-
-  @Override
-  public boolean isStaticPut() {
-    return instruction instanceof CfFieldInstruction
-        && ((CfFieldInstruction) instruction).getOpcode() == Opcodes.PUTSTATIC;
-  }
-
-  @Override
-  public boolean isInstanceGet() {
-    return instruction instanceof CfFieldInstruction
-        && ((CfFieldInstruction) instruction).getOpcode() == Opcodes.GETFIELD;
-  }
-
-  @Override
-  public boolean isStaticGet() {
-    return instruction instanceof CfFieldInstruction
-        && ((CfFieldInstruction) instruction).getOpcode() == Opcodes.GETSTATIC;
-  }
-
-  @Override
   public boolean isCheckCast() {
     return instruction instanceof CfCheckCast;
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
index 4ea699a..e1d74c1 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
@@ -67,6 +67,8 @@
 import com.android.tools.r8.code.SputShort;
 import com.android.tools.r8.code.SputWide;
 import com.android.tools.r8.code.Throw;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
 
 public class DexInstructionSubject implements InstructionSubject {
   protected final Instruction instruction;
@@ -81,6 +83,65 @@
   }
 
   @Override
+  public boolean isInstanceGet() {
+    return instruction instanceof Iget
+        || instruction instanceof IgetBoolean
+        || instruction instanceof IgetByte
+        || instruction instanceof IgetShort
+        || instruction instanceof IgetChar
+        || instruction instanceof IgetWide
+        || instruction instanceof IgetObject;
+  }
+
+  @Override
+  public boolean isInstancePut() {
+    return instruction instanceof Iput
+        || instruction instanceof IputBoolean
+        || instruction instanceof IputByte
+        || instruction instanceof IputShort
+        || instruction instanceof IputChar
+        || instruction instanceof IputWide
+        || instruction instanceof IputObject;
+  }
+
+  @Override
+  public boolean isStaticGet() {
+    return instruction instanceof Sget
+        || instruction instanceof SgetBoolean
+        || instruction instanceof SgetByte
+        || instruction instanceof SgetShort
+        || instruction instanceof SgetChar
+        || instruction instanceof SgetWide
+        || instruction instanceof SgetObject;
+  }
+
+  @Override
+  public boolean isStaticPut() {
+    return instruction instanceof Sput
+        || instruction instanceof SputBoolean
+        || instruction instanceof SputByte
+        || instruction instanceof SputShort
+        || instruction instanceof SputChar
+        || instruction instanceof SputWide
+        || instruction instanceof SputObject;
+  }
+
+  @Override
+  public DexField getField() {
+    assert isFieldAccess();
+    return instruction.getField();
+  }
+
+  @Override
+  public boolean isInvoke() {
+    return isInvokeVirtual()
+        || isInvokeInterface()
+        || isInvokeDirect()
+        || isInvokeSuper()
+        || isInvokeStatic();
+  }
+
+  @Override
   public boolean isInvokeVirtual() {
     return instruction instanceof InvokeVirtual || instruction instanceof InvokeVirtualRange;
   }
@@ -95,6 +156,20 @@
     return instruction instanceof InvokeStatic || instruction instanceof InvokeStaticRange;
   }
 
+  public boolean isInvokeSuper() {
+    return instruction instanceof InvokeSuper || instruction instanceof InvokeSuperRange;
+  }
+
+  public boolean isInvokeDirect() {
+    return instruction instanceof InvokeDirect || instruction instanceof InvokeDirectRange;
+  }
+
+  @Override
+  public DexMethod getMethod() {
+    assert isInvoke();
+    return instruction.getMethod();
+  }
+
   @Override
   public boolean isNop() {
     return instruction instanceof Nop;
@@ -147,15 +222,6 @@
   }
 
   @Override
-  public boolean isInvoke() {
-    return isInvokeVirtual()
-        || isInvokeInterface()
-        || isInvokeDirect()
-        || isInvokeSuper()
-        || isInvokeStatic();
-  }
-
-  @Override
   public boolean isNewInstance() {
     return instruction instanceof NewInstance;
   }
@@ -170,63 +236,11 @@
     return isCheckCast() && ((CheckCast) instruction).getType().toString().equals(type);
   }
 
-  public boolean isInvokeSuper() {
-    return instruction instanceof InvokeSuper || instruction instanceof InvokeSuperRange;
-  }
-
-  public boolean isInvokeDirect() {
-    return instruction instanceof InvokeDirect || instruction instanceof InvokeDirectRange;
-  }
-
   public boolean isConst4() {
     return instruction instanceof Const4;
   }
 
   @Override
-  public boolean isInstanceGet() {
-    return instruction instanceof Iget
-        || instruction instanceof IgetBoolean
-        || instruction instanceof IgetByte
-        || instruction instanceof IgetShort
-        || instruction instanceof IgetChar
-        || instruction instanceof IgetWide
-        || instruction instanceof IgetObject;
-  }
-
-  @Override
-  public boolean isInstancePut() {
-    return instruction instanceof Iput
-        || instruction instanceof IputBoolean
-        || instruction instanceof IputByte
-        || instruction instanceof IputShort
-        || instruction instanceof IputChar
-        || instruction instanceof IputWide
-        || instruction instanceof IputObject;
-  }
-
-  @Override
-  public boolean isStaticGet() {
-    return instruction instanceof Sget
-        || instruction instanceof SgetBoolean
-        || instruction instanceof SgetByte
-        || instruction instanceof SgetShort
-        || instruction instanceof SgetChar
-        || instruction instanceof SgetWide
-        || instruction instanceof SgetObject;
-  }
-
-  @Override
-  public boolean isStaticPut() {
-    return instruction instanceof Sput
-        || instruction instanceof SputBoolean
-        || instruction instanceof SputByte
-        || instruction instanceof SputShort
-        || instruction instanceof SputChar
-        || instruction instanceof SputWide
-        || instruction instanceof SputObject;
-  }
-
-  @Override
   public boolean isIf() {
     return instruction instanceof IfEq
         || instruction instanceof IfEqz
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
index e7eaa60..fb0e0d3 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
@@ -4,6 +4,9 @@
 
 package com.android.tools.r8.utils.codeinspector;
 
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+
 public interface InstructionSubject {
 
   enum JumboStringMode {
@@ -13,12 +16,26 @@
 
   boolean isFieldAccess();
 
+  boolean isInstancePut();
+
+  boolean isStaticPut();
+
+  boolean isInstanceGet();
+
+  boolean isStaticGet();
+
+  DexField getField();
+
+  boolean isInvoke();
+
   boolean isInvokeVirtual();
 
   boolean isInvokeInterface();
 
   boolean isInvokeStatic();
 
+  DexMethod getMethod();
+
   boolean isNop();
 
   boolean isConstString(JumboStringMode jumboStringMode);
@@ -37,18 +54,8 @@
 
   boolean isThrow();
 
-  boolean isInvoke();
-
   boolean isNewInstance();
 
-  boolean isInstancePut();
-
-  boolean isStaticPut();
-
-  boolean isInstanceGet();
-
-  boolean isStaticGet();
-
   boolean isCheckCast();
 
   boolean isCheckCast(String type);