Add precise source information to Proguard rule parser

For each rule the exact start and end position in the source is
recorded together with the exact source that the rule was parsed from.

This is intended for generating better error messages.

Change-Id: I8148b8fc7768276b754ac82b1fc54cee5a5d2526
diff --git a/src/main/java/com/android/tools/r8/shaking/InlineRule.java b/src/main/java/com/android/tools/r8/shaking/InlineRule.java
index 565856c..f263bd6 100644
--- a/src/main/java/com/android/tools/r8/shaking/InlineRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/InlineRule.java
@@ -4,6 +4,8 @@
 package com.android.tools.r8.shaking;
 
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
 import java.util.List;
 
 public class InlineRule extends ProguardConfigurationRule {
@@ -12,20 +14,27 @@
     ALWAYS, FORCE, NEVER
   }
 
-  public static class Builder extends ProguardConfigurationRule.Builder {
+  public static class Builder extends ProguardConfigurationRule.Builder<InlineRule, Builder> {
 
     private Builder() {
+      super();
     }
 
     Type type;
 
+    @Override
+    public Builder self() {
+      return this;
+    }
+
     public Builder setType(Type type) {
       this.type = type;
       return this;
     }
 
+    @Override
     public InlineRule build() {
-      return new InlineRule(classAnnotation, classAccessFlags,
+      return new InlineRule(origin, getPosition(), source, classAnnotation, classAccessFlags,
           negatedClassAccessFlags, classTypeNegated, classType, classNames, inheritanceAnnotation,
           inheritanceClassName, inheritanceIsExtends, memberRules, type);
     }
@@ -34,6 +43,9 @@
   private final Type type;
 
   private InlineRule(
+      Origin origin,
+      Position position,
+      String source,
       ProguardTypeMatcher classAnnotation,
       ProguardAccessFlags classAccessFlags,
       ProguardAccessFlags negatedClassAccessFlags,
@@ -45,13 +57,14 @@
       boolean inheritanceIsExtends,
       List<ProguardMemberRule> memberRules,
       Type type) {
-    super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
-        classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+    super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags,
+        classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
+        inheritanceIsExtends, memberRules);
     this.type = type;
   }
 
-  public static InlineRule.Builder builder() {
-    return new InlineRule.Builder();
+  public static Builder builder() {
+    return new Builder();
   }
 
   public Type getType() {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java
index 39d0335..4c1f9ef 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java
@@ -3,22 +3,36 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
 import java.util.List;
 
 public class ProguardAssumeNoSideEffectRule extends ProguardConfigurationRule {
 
-  public static class Builder extends ProguardClassSpecification.Builder {
+  public static class Builder
+      extends ProguardConfigurationRule.Builder<ProguardAssumeNoSideEffectRule, Builder> {
 
-    private Builder() {}
+    private Builder() {
+      super();
+    }
 
+    @Override
+    public Builder self() {
+      return this;
+    }
+
+    @Override
     public ProguardAssumeNoSideEffectRule build() {
-      return new ProguardAssumeNoSideEffectRule(classAnnotation, classAccessFlags,
-          negatedClassAccessFlags, classTypeNegated, classType, classNames, inheritanceAnnotation,
-          inheritanceClassName, inheritanceIsExtends, memberRules);
+      return new ProguardAssumeNoSideEffectRule(origin, getPosition(), source, classAnnotation,
+          classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames,
+          inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
     }
   }
 
   private ProguardAssumeNoSideEffectRule(
+      Origin origin,
+      Position position,
+      String source,
       ProguardTypeMatcher classAnnotation,
       ProguardAccessFlags classAccessFlags,
       ProguardAccessFlags negatedClassAccessFlags,
@@ -29,8 +43,9 @@
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
       List<ProguardMemberRule> memberRules) {
-    super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
-        classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+    super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags,
+        classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
+        inheritanceIsExtends, memberRules);
   }
 
   /**
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java
index 0dafb11..8b8f4dd 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java
@@ -3,21 +3,36 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
 import java.util.List;
 
 public class ProguardAssumeValuesRule extends ProguardConfigurationRule {
-  public static class Builder extends ProguardClassSpecification.Builder {
 
-    private Builder() {}
+  public static class Builder
+      extends ProguardConfigurationRule.Builder<ProguardAssumeValuesRule, Builder> {
 
+    private Builder() {
+      super();
+    }
+
+    @Override
+    public Builder self() {
+      return this;
+    }
+
+    @Override
     public ProguardAssumeValuesRule build() {
-      return new ProguardAssumeValuesRule(classAnnotation, classAccessFlags, negatedClassAccessFlags,
-          classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
-          inheritanceIsExtends, memberRules);
+      return new ProguardAssumeValuesRule(origin, getPosition(), source, classAnnotation,
+          classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames,
+          inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
     }
   }
 
   private ProguardAssumeValuesRule(
+      Origin origin,
+      Position position,
+      String source,
       ProguardTypeMatcher classAnnotation,
       ProguardAccessFlags classAccessFlags,
       ProguardAccessFlags negatedClassAccessFlags,
@@ -28,8 +43,9 @@
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
       List<ProguardMemberRule> memberRules) {
-    super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
-        classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+    super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags,
+        classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
+        inheritanceIsExtends, memberRules);
   }
 
   /**
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java
index 2461732..664cb25 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java
@@ -3,23 +3,36 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
 import java.util.List;
 
 public class ProguardCheckDiscardRule extends ProguardConfigurationRule {
 
-  public static class Builder extends ProguardConfigurationRule.Builder {
+  public static class Builder extends
+      ProguardConfigurationRule.Builder<ProguardCheckDiscardRule, Builder> {
 
     private Builder() {
+      super();
     }
 
+    @Override
+    public Builder self() {
+      return this;
+    }
+
+    @Override
     public ProguardCheckDiscardRule build() {
-      return new ProguardCheckDiscardRule(classAnnotation, classAccessFlags,
-          negatedClassAccessFlags, classTypeNegated, classType, classNames, inheritanceAnnotation,
-          inheritanceClassName, inheritanceIsExtends, memberRules);
+      return new ProguardCheckDiscardRule(origin, getPosition(), source, classAnnotation,
+          classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames,
+          inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
     }
   }
 
   private ProguardCheckDiscardRule(
+      Origin origin,
+      Position position,
+      String source,
       ProguardTypeMatcher classAnnotation,
       ProguardAccessFlags classAccessFlags,
       ProguardAccessFlags negatedClassAccessFlags,
@@ -30,8 +43,9 @@
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
       List<ProguardMemberRule> memberRules) {
-    super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
-        classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+    super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags,
+        classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
+        inheritanceIsExtends, memberRules);
   }
 
   public static Builder builder() {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
index 8de361e..5d610a8 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
@@ -3,6 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import com.android.tools.r8.position.TextPosition;
+import com.android.tools.r8.position.TextRange;
 import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.ImmutableList;
 import java.util.LinkedList;
@@ -11,8 +15,13 @@
 
 public abstract class ProguardClassSpecification {
 
-  public static class Builder {
+  public abstract static class
+  Builder<C extends ProguardClassSpecification, B extends Builder<C, B>> {
 
+    protected Origin origin;
+    protected Position start;
+    protected Position end;
+    protected String source;
     protected ProguardTypeMatcher classAnnotation;
     protected ProguardAccessFlags classAccessFlags = new ProguardAccessFlags();
     protected ProguardAccessFlags negatedClassAccessFlags = new ProguardAccessFlags();
@@ -25,6 +34,46 @@
     protected List<ProguardMemberRule> memberRules = new LinkedList<>();
 
     protected Builder() {
+      this(Origin.unknown(), Position.UNKNOWN);
+    }
+
+    protected Builder(Origin origin, Position start) {
+      this.origin = origin;
+      this.start = start;
+    }
+
+    public abstract C build();
+
+    public abstract B self();
+
+    public B setOrigin(Origin origin) {
+      this.origin = origin;
+      return self();
+    }
+
+    public B setStart(Position start) {
+      this.start = start;
+      return self();
+    }
+
+    public B setEnd(Position end) {
+      this.end = end;
+      return self();
+    }
+
+    public B setSource(String source) {
+      this.source = source;
+      return self();
+    }
+
+    public Position getPosition() {
+      if (start == null) {
+        return Position.UNKNOWN;
+      }
+      if (end == null || !((start instanceof TextPosition) && (end instanceof TextPosition))) {
+        return start;
+      }
+      return new TextRange((TextPosition) start, (TextPosition) end);
     }
 
     public List<ProguardMemberRule> getMemberRules() {
@@ -117,6 +166,9 @@
     }
   }
 
+  private final Origin origin;
+  private final Position position;
+  private final String source;
   private final ProguardTypeMatcher classAnnotation;
   private final ProguardAccessFlags classAccessFlags;
   private final ProguardAccessFlags negatedClassAccessFlags;
@@ -129,6 +181,9 @@
   private final List<ProguardMemberRule> memberRules;
 
   protected ProguardClassSpecification(
+      Origin origin,
+      Position position,
+      String source,
       ProguardTypeMatcher classAnnotation,
       ProguardAccessFlags classAccessFlags,
       ProguardAccessFlags negatedClassAccessFlags,
@@ -139,6 +194,9 @@
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
       List<ProguardMemberRule> memberRules) {
+    this.origin = origin;
+    this.position = position;
+    this.source =source;
     this.classAnnotation = classAnnotation;
     this.classAccessFlags = classAccessFlags;
     this.negatedClassAccessFlags = negatedClassAccessFlags;
@@ -152,6 +210,18 @@
     this.memberRules = memberRules;
   }
 
+  public Origin getOrigin() {
+    return origin;
+  }
+
+  public Position getPosition() {
+    return position;
+  }
+
+  public String getSource() {
+    return source;
+  }
+
   public List<ProguardMemberRule> getMemberRules() {
     return memberRules;
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 5b22456..e070db9 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -228,7 +228,7 @@
       } else if (acceptString("keepparameternames")) {
         configurationBuilder.setKeepParameterNames(true, origin, getPosition(optionStart));
       } else if (acceptString("checkdiscard")) {
-        ProguardCheckDiscardRule rule = parseCheckDiscardRule();
+        ProguardCheckDiscardRule rule = parseCheckDiscardRule(optionStart);
         configurationBuilder.addRule(rule);
       } else if (acceptString("keepdirectories")) {
         // TODO(74279367): Report an error until it's fully supported.
@@ -237,10 +237,10 @@
         }
         parsePathFilter(configurationBuilder::addKeepDirectories);
       } else if (acceptString("keep")) {
-        ProguardKeepRule rule = parseKeepRule();
+        ProguardKeepRule rule = parseKeepRule(optionStart);
         configurationBuilder.addRule(rule);
       } else if (acceptString("whyareyoukeeping")) {
-        ProguardWhyAreYouKeepingRule rule = parseWhyAreYouKeepingRule();
+        ProguardWhyAreYouKeepingRule rule = parseWhyAreYouKeepingRule(optionStart);
         configurationBuilder.addRule(rule);
       } else if (acceptString("dontoptimize")) {
         configurationBuilder.disableOptimization();
@@ -316,10 +316,10 @@
       } else if (acceptString("applymapping")) {
         configurationBuilder.setApplyMappingFile(parseFileName());
       } else if (acceptString("assumenosideeffects")) {
-        ProguardAssumeNoSideEffectRule rule = parseAssumeNoSideEffectsRule();
+        ProguardAssumeNoSideEffectRule rule = parseAssumeNoSideEffectsRule(optionStart);
         configurationBuilder.addRule(rule);
       } else if (acceptString("assumevalues")) {
-        ProguardAssumeValuesRule rule = parseAssumeValuesRule();
+        ProguardAssumeValuesRule rule = parseAssumeValuesRule(optionStart);
         configurationBuilder.addRule(rule);
       } else if (acceptString("include")) {
         // Collect the parsed configuration until the include.
@@ -348,16 +348,16 @@
       } else if (acceptString("packageobfuscationdictionary")) {
         configurationBuilder.setPackageObfuscationDictionary(parseFileName());
       } else if (acceptString("alwaysinline")) {
-        InlineRule rule = parseInlineRule(Type.ALWAYS);
+        InlineRule rule = parseInlineRule(Type.ALWAYS, optionStart);
         configurationBuilder.addRule(rule);
       } else if (allowTestOptions && acceptString("forceinline")) {
-        InlineRule rule = parseInlineRule(Type.FORCE);
+        InlineRule rule = parseInlineRule(Type.FORCE, optionStart);
         configurationBuilder.addRule(rule);
         // Insert a matching -checkdiscard rule to ensure force inlining happens.
         ProguardCheckDiscardRule ruled = rule.asProguardCheckDiscardRule();
         configurationBuilder.addRule(ruled);
       } else if (allowTestOptions && acceptString("neverinline")) {
-        InlineRule rule = parseInlineRule(Type.NEVER);
+        InlineRule rule = parseInlineRule(Type.NEVER, optionStart);
         configurationBuilder.addRule(rule);
       } else if (acceptString("useuniqueclassmembernames")) {
         configurationBuilder.setUseUniqueClassMemberNames(true);
@@ -376,7 +376,7 @@
         }
         parsePathFilter(configurationBuilder::addAdaptResourceFilecontents);
       } else if (acceptString("identifiernamestring")) {
-        configurationBuilder.addRule(parseIdentifierNameStringRule());
+        configurationBuilder.addRule(parseIdentifierNameStringRule(optionStart));
       } else if (acceptString("if")) {
         configurationBuilder.addRule(parseIfRule(optionStart));
       } else {
@@ -545,9 +545,11 @@
       }
     }
 
-    private ProguardKeepRule parseKeepRule()
+    private ProguardKeepRule parseKeepRule(Position start)
         throws ProguardRuleParserException {
-      ProguardKeepRule.Builder keepRuleBuilder = ProguardKeepRule.builder();
+      ProguardKeepRule.Builder keepRuleBuilder = ProguardKeepRule.builder()
+          .setOrigin(origin)
+          .setStart(start);
       parseRuleTypeAndModifiers(keepRuleBuilder);
       parseClassSpec(keepRuleBuilder, false);
       if (keepRuleBuilder.getMemberRules().isEmpty()) {
@@ -560,13 +562,21 @@
         defaultRuleBuilder.setArguments(Collections.emptyList());
         keepRuleBuilder.getMemberRules().add(defaultRuleBuilder.build());
       }
+      Position end = getPosition();
+      keepRuleBuilder.setSource(getSourceSnippet(contents, start, end));
+      keepRuleBuilder.setEnd(end);
       return keepRuleBuilder.build();
     }
 
-    private ProguardWhyAreYouKeepingRule parseWhyAreYouKeepingRule()
+    private ProguardWhyAreYouKeepingRule parseWhyAreYouKeepingRule(Position start)
         throws ProguardRuleParserException {
-      ProguardWhyAreYouKeepingRule.Builder keepRuleBuilder = ProguardWhyAreYouKeepingRule.builder();
+      ProguardWhyAreYouKeepingRule.Builder keepRuleBuilder = ProguardWhyAreYouKeepingRule.builder()
+          .setOrigin(origin)
+          .setStart(start);
       parseClassSpec(keepRuleBuilder, false);
+      Position end = getPosition();
+      keepRuleBuilder.setSource(getSourceSnippet(contents, start, end));
+      keepRuleBuilder.setEnd(end);
       return keepRuleBuilder.build();
     }
 
@@ -577,37 +587,56 @@
       return keepRuleBuilder.build();
     }
 
-    private ProguardCheckDiscardRule parseCheckDiscardRule()
+    private ProguardCheckDiscardRule parseCheckDiscardRule(Position start)
         throws ProguardRuleParserException {
-      ProguardCheckDiscardRule.Builder keepRuleBuilder = ProguardCheckDiscardRule.builder();
+      ProguardCheckDiscardRule.Builder keepRuleBuilder = ProguardCheckDiscardRule.builder()
+          .setOrigin(origin)
+          .setStart(start);
       parseClassSpec(keepRuleBuilder, false);
+      Position end = getPosition();
+      keepRuleBuilder.setSource(getSourceSnippet(contents, start, end));
+      keepRuleBuilder.setEnd(end);
       return keepRuleBuilder.build();
     }
 
-    private InlineRule parseInlineRule(InlineRule.Type type)
+    private InlineRule parseInlineRule(InlineRule.Type type, Position start)
         throws ProguardRuleParserException {
-      InlineRule.Builder keepRuleBuilder = InlineRule.builder().setType(type);
+      InlineRule.Builder keepRuleBuilder = InlineRule.builder()
+          .setOrigin(origin)
+          .setStart(start)
+          .setType(type);
       parseClassSpec(keepRuleBuilder, false);
+      Position end = getPosition();
+      keepRuleBuilder.setSource(getSourceSnippet(contents, start, end));
+      keepRuleBuilder.setEnd(end);
       return keepRuleBuilder.build();
     }
 
-    private ProguardIdentifierNameStringRule parseIdentifierNameStringRule()
+    private ProguardIdentifierNameStringRule parseIdentifierNameStringRule(Position start)
         throws ProguardRuleParserException {
       ProguardIdentifierNameStringRule.Builder keepRuleBuilder =
-          ProguardIdentifierNameStringRule.builder();
+          ProguardIdentifierNameStringRule.builder()
+              .setOrigin(origin)
+              .setStart(start);
       parseClassSpec(keepRuleBuilder, false);
+      Position end = getPosition();
+      keepRuleBuilder.setSource(getSourceSnippet(contents, start, end));
+      keepRuleBuilder.setEnd(end);
       return keepRuleBuilder.build();
     }
 
     private ProguardIfRule parseIfRule(TextPosition optionStart)
         throws ProguardRuleParserException {
-      ProguardIfRule.Builder ifRuleBuilder = ProguardIfRule.builder();
+      ProguardIfRule.Builder ifRuleBuilder = ProguardIfRule.builder()
+          .setOrigin(origin)
+          .setStart(optionStart);
       parseClassSpec(ifRuleBuilder, false);
 
       // Required a subsequent keep rule.
       skipWhitespace();
+      Position keepStart = getPosition();
       if (acceptString("-keep")) {
-        ProguardKeepRule subsequentRule = parseKeepRule();
+        ProguardKeepRule subsequentRule = parseKeepRule(keepStart);
         ifRuleBuilder.setSubsequentRule(subsequentRule);
         ProguardIfRule ifRule = ifRuleBuilder.build();
         verifyAndLinkBackReferences(ifRule.getWildcards());
@@ -636,8 +665,11 @@
       }
     }
 
-    private void parseClassSpec(
-        ProguardConfigurationRule.Builder builder, boolean allowValueSpecification)
+    private
+    <C extends ProguardClassSpecification, B extends ProguardClassSpecification.Builder<C, B>>
+    void parseClassSpec(
+        ProguardClassSpecification.Builder<C, B> builder,
+        boolean allowValueSpecification)
         throws ProguardRuleParserException {
       parseClassFlagsAndAnnotations(builder);
       parseClassType(builder);
@@ -646,8 +678,7 @@
       parseMemberRules(builder, allowValueSpecification);
     }
 
-    private void parseRuleTypeAndModifiers(ProguardKeepRule.Builder builder)
-        throws ProguardRuleParserException {
+    private void parseRuleTypeAndModifiers(ProguardKeepRule.Builder builder) {
       if (acceptString("names")) {
         builder.setType(ProguardKeepRuleType.KEEP);
         builder.getModifiersBuilder().setAllowsShrinking(true);
@@ -800,12 +831,15 @@
           ClassOrType.CLASS, dexItemFactory));
     }
 
-    private void parseMemberRules(ProguardClassSpecification.Builder classSpecificationBuilder,
+    private
+    <C extends ProguardClassSpecification, B extends ProguardClassSpecification.Builder<C, B>>
+    void parseMemberRules(
+        ProguardClassSpecification.Builder<C, B> classSpecificationBuilder,
         boolean allowValueSpecification)
         throws ProguardRuleParserException {
       skipWhitespace();
       if (!eof() && acceptChar('{')) {
-        ProguardMemberRule rule = null;
+        ProguardMemberRule rule;
         while ((rule = parseMemberRule(allowValueSpecification)) != null) {
           classSpecificationBuilder.getMemberRules().add(rule);
         }
@@ -1086,16 +1120,27 @@
       return fileFilter;
     }
 
-    private ProguardAssumeNoSideEffectRule parseAssumeNoSideEffectsRule()
+    private ProguardAssumeNoSideEffectRule parseAssumeNoSideEffectsRule(Position start)
         throws ProguardRuleParserException {
-      ProguardAssumeNoSideEffectRule.Builder builder = ProguardAssumeNoSideEffectRule.builder();
+      ProguardAssumeNoSideEffectRule.Builder builder = ProguardAssumeNoSideEffectRule.builder()
+          .setOrigin(origin)
+          .setStart(start);
       parseClassSpec(builder, true);
+      Position end = getPosition();
+      builder.setSource(getSourceSnippet(contents, start, end));
+      builder.setEnd(end);
       return builder.build();
     }
 
-    private ProguardAssumeValuesRule parseAssumeValuesRule() throws ProguardRuleParserException {
-      ProguardAssumeValuesRule.Builder builder = ProguardAssumeValuesRule.builder();
+    private ProguardAssumeValuesRule parseAssumeValuesRule(Position start)
+        throws ProguardRuleParserException {
+      ProguardAssumeValuesRule.Builder builder = ProguardAssumeValuesRule.builder()
+          .setOrigin(origin)
+          .setStart(start);
       parseClassSpec(builder, true);
+      Position end = getPosition();
+      builder.setSource(getSourceSnippet(contents, start, end));
+      builder.setEnd(end);
       return builder.build();
     }
 
@@ -1549,7 +1594,24 @@
     private int getColumn() {
       return position - lineStartPosition + 1 /* column starts at 1 */;
     }
-  }
+
+    private String getSourceSnippet(String source, Position start, Position end) {
+      return start instanceof TextPosition && end instanceof TextPosition
+          ? getTextSourceSnippet(source, (TextPosition) start, (TextPosition) end)
+          : null;
+      }
+    }
+
+    private String getTextSourceSnippet(String source, TextPosition start, TextPosition end) {
+      long length = end.getOffset() - start.getOffset();
+      if (start.getOffset() < 0 || end.getOffset() < 0
+          || start.getOffset() >= source.length() || end.getOffset() > source.length()
+          || length <= 0) {
+        return null;
+      } else {
+        return source.substring((int) start.getOffset(), (int) end.getOffset());
+      }
+    }
 
   static class IdentifierPatternWithWildcards {
     final String pattern;
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
index e4c5691..f7282ff 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
 import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.Iterables;
 import java.util.Collections;
@@ -11,6 +13,9 @@
 
 public abstract class ProguardConfigurationRule extends ProguardClassSpecification {
   ProguardConfigurationRule(
+      Origin origin,
+      Position position,
+      String source,
       ProguardTypeMatcher classAnnotation,
       ProguardAccessFlags classAccessFlags,
       ProguardAccessFlags negatedClassAccessFlags,
@@ -21,8 +26,9 @@
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
       List<ProguardMemberRule> memberRules) {
-    super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
-        classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+    super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags,
+        classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
+        inheritanceIsExtends, memberRules);
   }
 
   abstract String typeString();
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java
index 07ffd85..8ff7670 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java
@@ -3,22 +3,35 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
 import java.util.List;
 
 public class ProguardIdentifierNameStringRule extends ProguardConfigurationRule {
 
-  public static class Builder extends ProguardConfigurationRule.Builder {
+  public static class Builder
+      extends ProguardConfigurationRule.Builder<ProguardIdentifierNameStringRule, Builder> {
     private Builder() {
+      super();
     }
 
+    @Override
+    public Builder self() {
+      return this;
+    }
+
+    @Override
     public ProguardIdentifierNameStringRule build() {
-      return new ProguardIdentifierNameStringRule(classAnnotation, classAccessFlags,
-          negatedClassAccessFlags, classTypeNegated, classType, classNames, inheritanceAnnotation,
-          inheritanceClassName, inheritanceIsExtends, memberRules);
+      return new ProguardIdentifierNameStringRule(origin, getPosition(), source, classAnnotation,
+          classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames,
+          inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
     }
   }
 
   private ProguardIdentifierNameStringRule(
+      Origin origin,
+      Position position,
+      String source,
       ProguardTypeMatcher classAnnotation,
       ProguardAccessFlags classAccessFlags,
       ProguardAccessFlags negatedClassAccessFlags,
@@ -29,8 +42,9 @@
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
       List<ProguardMemberRule> memberRules) {
-    super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
-        classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+    super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags,
+        classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
+        inheritanceIsExtends, memberRules);
   }
 
   public static Builder builder() {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
index 47f26ed..54a3f2f 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
@@ -3,18 +3,29 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
 import com.google.common.collect.Iterables;
 import java.util.List;
 import java.util.stream.Collectors;
 
-public class ProguardIfRule extends ProguardKeepRule {
+public class ProguardIfRule extends ProguardKeepRuleBase {
 
   final ProguardKeepRule subsequentRule;
 
-  public static class Builder extends ProguardKeepRule.Builder {
+  public static class Builder extends ProguardKeepRuleBase.Builder<ProguardIfRule, Builder> {
 
     ProguardKeepRule subsequentRule = null;
 
+    protected Builder() {
+      super();
+    }
+
+    @Override
+    public Builder self() {
+      return this;
+    }
+
     public void setSubsequentRule(ProguardKeepRule rule) {
       subsequentRule = rule;
     }
@@ -22,13 +33,17 @@
     @Override
     public ProguardIfRule build() {
       assert subsequentRule != null : "Option -if without a subsequent rule.";
-      return new ProguardIfRule(classAnnotation, classAccessFlags,
+      return new ProguardIfRule(origin, getPosition(), source, classAnnotation, classAccessFlags,
           negatedClassAccessFlags, classTypeNegated, classType, classNames, inheritanceAnnotation,
           inheritanceClassName, inheritanceIsExtends, memberRules, subsequentRule);
     }
   }
 
-  private ProguardIfRule(ProguardTypeMatcher classAnnotation,
+  private ProguardIfRule(
+      Origin origin,
+      Position position,
+      String source,
+      ProguardTypeMatcher classAnnotation,
       ProguardAccessFlags classAccessFlags,
       ProguardAccessFlags negatedClassAccessFlags, boolean classTypeNegated,
       ProguardClassType classType, ProguardClassNameList classNames,
@@ -36,8 +51,9 @@
       ProguardTypeMatcher inheritanceClassName, boolean inheritanceIsExtends,
       List<ProguardMemberRule> memberRules,
       ProguardKeepRule subsequentRule) {
-    super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
-        classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules,
+    super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags,
+        classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
+        inheritanceIsExtends, memberRules,
         ProguardKeepRuleType.CONDITIONAL, ProguardKeepRuleModifiers.builder().build());
     this.subsequentRule = subsequentRule;
   }
@@ -51,9 +67,11 @@
     return Iterables.concat(super.getWildcards(), subsequentRule.getWildcards());
   }
 
-  @Override
   protected ProguardIfRule materialize() {
     return new ProguardIfRule(
+        Origin.unknown(),
+        Position.UNKNOWN,
+        null,
         getClassAnnotation(),
         getClassAccessFlags(),
         getNegatedClassAccessFlags(),
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java
index 66fdd50..767ecf7 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java
@@ -3,23 +3,36 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
 import java.util.List;
 
 public class ProguardKeepPackageNamesRule extends ProguardConfigurationRule {
 
-  public static class Builder extends ProguardConfigurationRule.Builder {
+  public static class Builder
+      extends ProguardConfigurationRule.Builder<ProguardKeepPackageNamesRule, Builder> {
 
     private Builder() {
+      super();
     }
 
+    @Override
+    public Builder self() {
+      return this;
+    }
+
+    @Override
     public ProguardKeepPackageNamesRule build() {
-      return new ProguardKeepPackageNamesRule(classAnnotation, classAccessFlags,
-          negatedClassAccessFlags, classTypeNegated, classType, classNames, inheritanceAnnotation,
-          inheritanceClassName, inheritanceIsExtends, memberRules);
+      return new ProguardKeepPackageNamesRule(origin, getPosition(), source, classAnnotation,
+          classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames,
+          inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
     }
   }
 
   private ProguardKeepPackageNamesRule(
+      Origin origin,
+      Position position,
+      String source,
       ProguardTypeMatcher classAnnotation,
       ProguardAccessFlags classAccessFlags,
       ProguardAccessFlags negatedClassAccessFlags,
@@ -30,12 +43,13 @@
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
       List<ProguardMemberRule> memberRules) {
-    super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
-        classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+    super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags,
+        classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
+        inheritanceIsExtends, memberRules);
   }
 
-  public static ProguardKeepPackageNamesRule.Builder builder() {
-    return new ProguardKeepPackageNamesRule.Builder();
+  public static Builder builder() {
+    return new Builder();
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
index 6651dae..3c1fb2d 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
@@ -3,39 +3,37 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
 import java.util.List;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
-public class ProguardKeepRule extends ProguardConfigurationRule {
+public class ProguardKeepRule extends ProguardKeepRuleBase {
 
-  public static class Builder extends ProguardClassSpecification.Builder {
+  public static class Builder extends ProguardKeepRuleBase.Builder<ProguardKeepRule, Builder> {
 
-    private ProguardKeepRuleType type;
-    private final ProguardKeepRuleModifiers.Builder modifiersBuilder =
-        ProguardKeepRuleModifiers.builder();
-
-    protected Builder() {}
-
-    public void setType(ProguardKeepRuleType type) {
-      this.type = type;
+    protected Builder() {
+      super();
     }
 
-    public ProguardKeepRuleModifiers.Builder getModifiersBuilder() {
-      return modifiersBuilder;
+    @Override
+    public Builder self() {
+      return this;
     }
 
+    @Override
     public ProguardKeepRule build() {
-      return new ProguardKeepRule(classAnnotation, classAccessFlags, negatedClassAccessFlags,
-          classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
-          inheritanceIsExtends, memberRules, type, modifiersBuilder.build());
+      return new ProguardKeepRule(origin, getPosition(), source, classAnnotation, classAccessFlags,
+          negatedClassAccessFlags, classTypeNegated, classType, classNames, inheritanceAnnotation,
+          inheritanceClassName, inheritanceIsExtends, memberRules, type, modifiersBuilder.build());
     }
   }
 
-  private final ProguardKeepRuleType type;
-  private final ProguardKeepRuleModifiers modifiers;
-
   protected ProguardKeepRule(
+      Origin origin,
+      Position position,
+      String source,
       ProguardTypeMatcher classAnnotation,
       ProguardAccessFlags classAccessFlags,
       ProguardAccessFlags negatedClassAccessFlags,
@@ -48,10 +46,9 @@
       List<ProguardMemberRule> memberRules,
       ProguardKeepRuleType type,
       ProguardKeepRuleModifiers modifiers) {
-    super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
-        classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
-    this.type = type;
-    this.modifiers = modifiers;
+    super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags,
+        classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
+        inheritanceIsExtends, memberRules, type, modifiers);
   }
 
   /**
@@ -61,16 +58,11 @@
     return new Builder();
   }
 
-  public ProguardKeepRuleType getType() {
-    return type;
-  }
-
-  public ProguardKeepRuleModifiers getModifiers() {
-    return modifiers;
-  }
-
   protected ProguardKeepRule materialize() {
     return new ProguardKeepRule(
+        Origin.unknown(),
+        Position.UNKNOWN,
+        null,
         getClassAnnotation(),
         getClassAccessFlags(),
         getNegatedClassAccessFlags(),
@@ -93,25 +85,9 @@
       return false;
     }
     ProguardKeepRule that = (ProguardKeepRule) o;
-
-    if (type != that.type) {
-      return false;
-    }
-    if (!modifiers.equals(that.modifiers)) {
-      return false;
-    }
     return super.equals(that);
   }
 
-  @Override
-  public int hashCode() {
-    // Used multiplier 3 to avoid too much overflow when computing hashCode.
-    int result = type.hashCode();
-    result = 3 * result + modifiers.hashCode();
-    result = 3 * result + super.hashCode();
-    return result;
-  }
-
   static void appendNonEmpty(StringBuilder builder, String pre, Object item, String post) {
     if (item == null) {
       return;
@@ -128,16 +104,6 @@
     }
   }
 
-  @Override
-  String typeString() {
-    return type.toString();
-  }
-
-  @Override
-  String modifierString() {
-    return modifiers.toString();
-  }
-
   public static ProguardKeepRule defaultKeepAllRule(
       Consumer<ProguardKeepRuleModifiers.Builder> modifiers) {
     ProguardKeepRule.Builder builder = ProguardKeepRule.builder();
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleBase.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleBase.java
new file mode 100644
index 0000000..f67634e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleBase.java
@@ -0,0 +1,116 @@
+// Copyright (c) 2016, 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.origin.Origin;
+import com.android.tools.r8.position.Position;
+import java.util.List;
+
+public class ProguardKeepRuleBase extends ProguardConfigurationRule {
+
+  public static abstract class Builder<C extends ProguardKeepRuleBase, B extends Builder<C, B>>
+      extends ProguardConfigurationRule.Builder<C, B> {
+
+    protected ProguardKeepRuleType type;
+    protected final ProguardKeepRuleModifiers.Builder modifiersBuilder =
+        ProguardKeepRuleModifiers.builder();
+
+    protected Builder() {
+      super();
+    }
+
+    public void setType(ProguardKeepRuleType type) {
+      this.type = type;
+    }
+
+    public ProguardKeepRuleModifiers.Builder getModifiersBuilder() {
+      return modifiersBuilder;
+    }
+  }
+
+  private final ProguardKeepRuleType type;
+  private final ProguardKeepRuleModifiers modifiers;
+
+  protected ProguardKeepRuleBase(
+      Origin origin,
+      Position position,
+      String source,
+      ProguardTypeMatcher classAnnotation,
+      ProguardAccessFlags classAccessFlags,
+      ProguardAccessFlags negatedClassAccessFlags,
+      boolean classTypeNegated,
+      ProguardClassType classType,
+      ProguardClassNameList classNames,
+      ProguardTypeMatcher inheritanceAnnotation,
+      ProguardTypeMatcher inheritanceClassName,
+      boolean inheritanceIsExtends,
+      List<ProguardMemberRule> memberRules,
+      ProguardKeepRuleType type,
+      ProguardKeepRuleModifiers modifiers) {
+    super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags,
+        classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
+        inheritanceIsExtends, memberRules);
+    this.type = type;
+    this.modifiers = modifiers;
+  }
+
+  public ProguardKeepRuleType getType() {
+    return type;
+  }
+
+  public ProguardKeepRuleModifiers getModifiers() {
+    return modifiers;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (!(o instanceof ProguardKeepRuleBase)) {
+      return false;
+    }
+    ProguardKeepRuleBase that = (ProguardKeepRuleBase) o;
+
+    if (type != that.type) {
+      return false;
+    }
+    if (!modifiers.equals(that.modifiers)) {
+      return false;
+    }
+    return super.equals(that);
+  }
+
+  @Override
+  public int hashCode() {
+    // Used multiplier 3 to avoid too much overflow when computing hashCode.
+    int result = type.hashCode();
+    result = 3 * result + modifiers.hashCode();
+    result = 3 * result + super.hashCode();
+    return result;
+  }
+
+  static void appendNonEmpty(StringBuilder builder, String pre, Object item, String post) {
+    if (item == null) {
+      return;
+    }
+    String text = item.toString();
+    if (!text.isEmpty()) {
+      if (pre != null) {
+        builder.append(pre);
+      }
+      builder.append(text);
+      if (post != null) {
+        builder.append(post);
+      }
+    }
+  }
+
+  @Override
+  String typeString() {
+    return type.toString();
+  }
+
+  @Override
+  String modifierString() {
+    return modifiers.toString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java
index 73b5538..87200fb 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java
@@ -3,23 +3,36 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
 import java.util.List;
 
 public class ProguardWhyAreYouKeepingRule extends ProguardConfigurationRule {
 
-  public static class Builder extends ProguardConfigurationRule.Builder {
+  public static class Builder
+      extends ProguardConfigurationRule.Builder<ProguardWhyAreYouKeepingRule, Builder> {
 
     private Builder() {
+      super();
     }
 
+    @Override
+    public Builder self() {
+      return this;
+    }
+
+    @Override
     public ProguardWhyAreYouKeepingRule build() {
-      return new ProguardWhyAreYouKeepingRule(classAnnotation, classAccessFlags,
-          negatedClassAccessFlags, classTypeNegated, classType, classNames, inheritanceAnnotation,
-          inheritanceClassName, inheritanceIsExtends, memberRules);
+      return new ProguardWhyAreYouKeepingRule(origin, getPosition(), source, classAnnotation,
+          classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames,
+          inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
     }
   }
 
   private ProguardWhyAreYouKeepingRule(
+      Origin origin,
+      Position position,
+      String source,
       ProguardTypeMatcher classAnnotation,
       ProguardAccessFlags classAccessFlags,
       ProguardAccessFlags negatedClassAccessFlags,
@@ -30,12 +43,13 @@
       ProguardTypeMatcher inheritanceClassName,
       boolean inheritanceIsExtends,
       List<ProguardMemberRule> memberRules) {
-    super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
-        classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+    super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags,
+        classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
+        inheritanceIsExtends, memberRules);
   }
 
-  public static ProguardWhyAreYouKeepingRule.Builder builder() {
-    return new ProguardWhyAreYouKeepingRule.Builder();
+  public static Builder builder() {
+    return new Builder();
   }
 
   @Override
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 748866e..7325be3 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -41,6 +41,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.function.Function;
+import java.util.stream.Collectors;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -1813,6 +1814,66 @@
     testNotSupported("-adaptresourcefilecontents");
   }
 
+  private void checkRulesSourceSnippet(List<String> sourceRules) {
+    checkRulesSourceSnippet(sourceRules, sourceRules, false);
+  }
+
+  private void checkRulesSourceSnippet(
+      List<String> sourceRules, List<String> expected, boolean trim) {
+    reset();
+    parser.parse(createConfigurationForTesting(sourceRules));
+    verifyParserEndsCleanly();
+    List<ProguardConfigurationRule> rules = parser.getConfig().getRules();
+    assertEquals(expected.size(), rules.size());
+    for (int i = 0; i < expected.size(); i++) {
+      assertEquals(trim ? expected.get(i).trim() : expected.get(i), rules.get(i).getSource());
+    }
+  }
+
+  @Test
+  public void accurateSourceSnippet() {
+    String rule1 = String.join(System.lineSeparator(), ImmutableList.of("-keep class A  {  *;  }"));
+    String rule2 =
+        String.join(System.lineSeparator(), ImmutableList.of("-keep class A  ", "{  *;  ", "}"));
+    String rule3 =
+        String.join(
+            System.lineSeparator(), ImmutableList.of("-checkdiscard class A  ", "{  *;  ", "}"));
+
+    checkRulesSourceSnippet(ImmutableList.of(rule1));
+    checkRulesSourceSnippet(ImmutableList.of(rule2));
+    checkRulesSourceSnippet(ImmutableList.of(rule3));
+    checkRulesSourceSnippet(ImmutableList.of(rule1, rule2, rule3));
+  }
+
+  @Test
+  public void accurateSourceSnippet_withWhitespace() {
+    Iterable<String> nonEmptyWhiteSpace =
+        whiteSpace.stream().filter(space -> !space.equals("")).collect(Collectors.toList());
+    for (String space : nonEmptyWhiteSpace) {
+      String rule1 =
+          String.join(System.lineSeparator(), ImmutableList.of("  -keep class A  {  *;  }  "))
+              .replaceAll(" {2}", space);
+      String rule2 =
+          String.join(
+                  System.lineSeparator(), ImmutableList.of("-keep class A  ", "{  *;  ", "}", ""))
+              .replaceAll(" {2}", space);
+
+      checkRulesSourceSnippet(ImmutableList.of(rule1), ImmutableList.of(rule1), true);
+      checkRulesSourceSnippet(
+          ImmutableList.of("#Test comment ", "", rule1), ImmutableList.of(rule1), true);
+      checkRulesSourceSnippet(
+          ImmutableList.of("#Test comment ", "", rule1, "", "#Test comment ", ""),
+          ImmutableList.of(rule1),
+          true);
+      checkRulesSourceSnippet(ImmutableList.of(rule2), ImmutableList.of(rule2), true);
+      checkRulesSourceSnippet(ImmutableList.of(rule1, rule2), ImmutableList.of(rule1, rule2), true);
+      checkRulesSourceSnippet(
+          ImmutableList.of(
+              "#Test comment ", "", rule1, " ", "#Test comment ", "", rule2, "#Test comment ", ""),
+          ImmutableList.of(rule1, rule2),
+          true);
+    }
+  }
 
   private ProguardConfiguration parseAndVerifyParserEndsCleanly(List<String> config) {
     parser.parse(createConfigurationForTesting(config));