Fix checkdiscard processing.
We used to implement the wrong semantics for checkdiscard, which made
all checkdiscard tests we typically see pass.
Also clean up proguard rule processing in general. Instead of using
fake keep modifiers, always use specialized subclasses.
Bug:
Change-Id: I9c6ad2625bdb6a95a8ff5fc669c09c77651c0b11
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java
new file mode 100644
index 0000000..54302db
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java
@@ -0,0 +1,47 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexAccessFlags;
+import java.util.List;
+import java.util.Set;
+
+public class ProguardCheckDiscardRule extends ProguardConfigurationRule {
+
+ public static class Builder extends ProguardConfigurationRule.Builder {
+
+ private Builder() {
+ }
+
+ public ProguardCheckDiscardRule build() {
+ return new ProguardCheckDiscardRule(classAnnotation, classAccessFlags,
+ negatedClassAccessFlags, classTypeNegated, classType, classNames, inheritanceAnnotation,
+ inheritanceClassName, inheritanceIsExtends, memberRules);
+ }
+ }
+
+ private ProguardCheckDiscardRule(
+ ProguardTypeMatcher classAnnotation,
+ DexAccessFlags classAccessFlags,
+ DexAccessFlags negatedClassAccessFlags,
+ boolean classTypeNegated,
+ ProguardClassType classType,
+ List<ProguardTypeMatcher> classNames,
+ ProguardTypeMatcher inheritanceAnnotation,
+ ProguardTypeMatcher inheritanceClassName,
+ boolean inheritanceIsExtends,
+ Set<ProguardMemberRule> memberRules) {
+ super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
+ classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ String typeString() {
+ return "checkdiscard";
+ }
+}
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 17e5128..31fb77d 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -137,16 +137,16 @@
} else if (acceptString("keepattributes")) {
parseKeepAttributes();
} else if (acceptString("keeppackagenames")) {
- ProguardKeepRule rule = parseKeepPackageNamesRule();
+ ProguardKeepPackageNamesRule rule = parseKeepPackageNamesRule();
configurationBuilder.addRule(rule);
} else if (acceptString("checkdiscard")) {
- ProguardKeepRule rule = parseCheckDiscardRule();
+ ProguardCheckDiscardRule rule = parseCheckDiscardRule();
configurationBuilder.addRule(rule);
} else if (acceptString("keep")) {
ProguardKeepRule rule = parseKeepRule();
configurationBuilder.addRule(rule);
} else if (acceptString("whyareyoukeeping")) {
- ProguardKeepRule rule = parseWhyAreYouKeepingRule();
+ ProguardWhyAreYouKeepingRule rule = parseWhyAreYouKeepingRule();
configurationBuilder.addRule(rule);
} else if (acceptString("dontoptimize")) {
configurationBuilder.setOptimize(false);
@@ -351,37 +351,36 @@
ProguardKeepRule.Builder keepRuleBuilder = ProguardKeepRule.builder();
parseRuleTypeAndModifiers(keepRuleBuilder);
parseClassSpec(keepRuleBuilder, false);
+ if (keepRuleBuilder.getMemberRules().isEmpty()) {
+ // If there are no member rules, a default rule for the parameterless constructor
+ // applies. So we add that here.
+ ProguardMemberRule.Builder defaultRuleBuilder = ProguardMemberRule.builder();
+ defaultRuleBuilder.setName(Constants.INSTANCE_INITIALIZER_NAME);
+ defaultRuleBuilder.setRuleType(ProguardMemberType.INIT);
+ defaultRuleBuilder.setArguments(Collections.emptyList());
+ keepRuleBuilder.getMemberRules().add(defaultRuleBuilder.build());
+ }
return keepRuleBuilder.build();
}
- private ProguardKeepRule parseWhyAreYouKeepingRule()
+ private ProguardWhyAreYouKeepingRule parseWhyAreYouKeepingRule()
throws ProguardRuleParserException {
- ProguardKeepRule.Builder keepRuleBuilder = ProguardKeepRule.builder();
- keepRuleBuilder.getModifiersBuilder().setFlagsToHaveNoEffect();
- keepRuleBuilder.getModifiersBuilder().whyAreYouKeeping = true;
- keepRuleBuilder.setType(ProguardKeepRuleType.KEEP);
+ ProguardWhyAreYouKeepingRule.Builder keepRuleBuilder = ProguardWhyAreYouKeepingRule.builder();
parseClassSpec(keepRuleBuilder, false);
return keepRuleBuilder.build();
}
- private ProguardKeepRule parseKeepPackageNamesRule()
+ private ProguardKeepPackageNamesRule parseKeepPackageNamesRule()
throws ProguardRuleParserException {
- ProguardKeepRule.Builder keepRuleBuilder = ProguardKeepRule.builder();
- keepRuleBuilder.getModifiersBuilder().setFlagsToHaveNoEffect();
- keepRuleBuilder.getModifiersBuilder().keepPackageNames = true;
- keepRuleBuilder.setType(ProguardKeepRuleType.KEEP);
+ ProguardKeepPackageNamesRule.Builder keepRuleBuilder = ProguardKeepPackageNamesRule.builder();
keepRuleBuilder.setClassNames(parseClassNames());
return keepRuleBuilder.build();
}
- private ProguardKeepRule parseCheckDiscardRule()
+ private ProguardCheckDiscardRule parseCheckDiscardRule()
throws ProguardRuleParserException {
- ProguardKeepRule.Builder keepRuleBuilder = ProguardKeepRule.builder();
- keepRuleBuilder.getModifiersBuilder().setFlagsToHaveNoEffect();
- keepRuleBuilder.getModifiersBuilder().checkDiscarded = true;
+ ProguardCheckDiscardRule.Builder keepRuleBuilder = ProguardCheckDiscardRule.builder();
parseClassSpec(keepRuleBuilder, false);
- keepRuleBuilder.setType(keepRuleBuilder.getMemberRules().isEmpty() ? ProguardKeepRuleType.KEEP
- : ProguardKeepRuleType.KEEP_CLASS_MEMBERS);
return keepRuleBuilder.build();
}
@@ -528,14 +527,6 @@
}
skipWhitespace();
expectChar('}');
- } else {
- // If there are no member rules, a default rule for the parameterless constructor
- // applies. So we add that here.
- ProguardMemberRule.Builder defaultRuleBuilder = ProguardMemberRule.builder();
- defaultRuleBuilder.setName(Constants.INSTANCE_INITIALIZER_NAME);
- defaultRuleBuilder.setRuleType(ProguardMemberType.INIT);
- defaultRuleBuilder.setArguments(Collections.emptyList());
- classSpecificationBuilder.getMemberRules().add(defaultRuleBuilder.build());
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java
new file mode 100644
index 0000000..787a31c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java
@@ -0,0 +1,47 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexAccessFlags;
+import java.util.List;
+import java.util.Set;
+
+public class ProguardKeepPackageNamesRule extends ProguardConfigurationRule {
+
+ public static class Builder extends ProguardConfigurationRule.Builder {
+
+ private Builder() {
+ }
+
+ public ProguardKeepPackageNamesRule build() {
+ return new ProguardKeepPackageNamesRule(classAnnotation, classAccessFlags,
+ negatedClassAccessFlags, classTypeNegated, classType, classNames, inheritanceAnnotation,
+ inheritanceClassName, inheritanceIsExtends, memberRules);
+ }
+ }
+
+ private ProguardKeepPackageNamesRule(
+ ProguardTypeMatcher classAnnotation,
+ DexAccessFlags classAccessFlags,
+ DexAccessFlags negatedClassAccessFlags,
+ boolean classTypeNegated,
+ ProguardClassType classType,
+ List<ProguardTypeMatcher> classNames,
+ ProguardTypeMatcher inheritanceAnnotation,
+ ProguardTypeMatcher inheritanceClassName,
+ boolean inheritanceIsExtends,
+ Set<ProguardMemberRule> memberRules) {
+ super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
+ classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+ }
+
+ public static ProguardKeepPackageNamesRule.Builder builder() {
+ return new ProguardKeepPackageNamesRule.Builder();
+ }
+
+ @Override
+ String typeString() {
+ return "keeppackagenames";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java
index e2f53b5..5db35cd 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java
@@ -8,51 +8,30 @@
public boolean allowsShrinking = false;
public boolean allowsOptimization = false;
public boolean allowsObfuscation = false;
- public boolean whyAreYouKeeping = false;
public boolean includeDescriptorClasses = false;
- public boolean keepPackageNames = false;
- public boolean checkDiscarded = false;
-
- void setFlagsToHaveNoEffect() {
- allowsShrinking = true;
- allowsOptimization = true;
- allowsObfuscation = true;
- whyAreYouKeeping = false;
- includeDescriptorClasses = false;
- keepPackageNames = false;
- }
private Builder() {}
ProguardKeepRuleModifiers build() {
return new ProguardKeepRuleModifiers(allowsShrinking, allowsOptimization, allowsObfuscation,
- whyAreYouKeeping, includeDescriptorClasses, keepPackageNames, checkDiscarded);
+ includeDescriptorClasses);
}
}
public final boolean allowsShrinking;
public final boolean allowsOptimization;
public final boolean allowsObfuscation;
- public final boolean whyAreYouKeeping;
public final boolean includeDescriptorClasses;
- public final boolean keepPackageNames;
- public final boolean checkDiscarded;
private ProguardKeepRuleModifiers(
boolean allowsShrinking,
boolean allowsOptimization,
boolean allowsObfuscation,
- boolean whyAreYouKeeping,
- boolean includeDescriptorClasses,
- boolean keepPackageNames,
- boolean checkDiscarded) {
+ boolean includeDescriptorClasses) {
this.allowsShrinking = allowsShrinking;
this.allowsOptimization = allowsOptimization;
this.allowsObfuscation = allowsObfuscation;
- this.whyAreYouKeeping = whyAreYouKeeping;
this.includeDescriptorClasses = includeDescriptorClasses;
- this.keepPackageNames = keepPackageNames;
- this.checkDiscarded = checkDiscarded;
}
/**
* Create a new empty builder.
@@ -71,8 +50,7 @@
return allowsShrinking == that.allowsShrinking
&& allowsOptimization == that.allowsOptimization
&& allowsObfuscation == that.allowsObfuscation
- && includeDescriptorClasses == that.includeDescriptorClasses
- && keepPackageNames == that.keepPackageNames;
+ && includeDescriptorClasses == that.includeDescriptorClasses;
}
@Override
@@ -80,9 +58,7 @@
return (allowsShrinking ? 1 : 0)
| (allowsOptimization ? 2 : 0)
| (allowsObfuscation ? 4 : 0)
- | (whyAreYouKeeping ? 8 : 0)
- | (includeDescriptorClasses ? 16 : 0)
- | (keepPackageNames ? 32 : 0);
+ | (includeDescriptorClasses ? 8 : 0);
}
@Override
@@ -91,9 +67,7 @@
appendWithComma(builder, allowsObfuscation, "allowobfuscation");
appendWithComma(builder, allowsShrinking, "allowshrinking");
appendWithComma(builder, allowsOptimization, "allowoptimization");
- appendWithComma(builder, whyAreYouKeeping, "whyareyoukeeping");
appendWithComma(builder, includeDescriptorClasses, "includedescriptorclasses");
- appendWithComma(builder, keepPackageNames, "keeppackagenames");
return builder.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
new file mode 100644
index 0000000..581a0db
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java
@@ -0,0 +1,47 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexAccessFlags;
+import java.util.List;
+import java.util.Set;
+
+public class ProguardWhyAreYouKeepingRule extends ProguardConfigurationRule {
+
+ public static class Builder extends ProguardConfigurationRule.Builder {
+
+ private Builder() {
+ }
+
+ public ProguardWhyAreYouKeepingRule build() {
+ return new ProguardWhyAreYouKeepingRule(classAnnotation, classAccessFlags,
+ negatedClassAccessFlags, classTypeNegated, classType, classNames, inheritanceAnnotation,
+ inheritanceClassName, inheritanceIsExtends, memberRules);
+ }
+ }
+
+ private ProguardWhyAreYouKeepingRule(
+ ProguardTypeMatcher classAnnotation,
+ DexAccessFlags classAccessFlags,
+ DexAccessFlags negatedClassAccessFlags,
+ boolean classTypeNegated,
+ ProguardClassType classType,
+ List<ProguardTypeMatcher> classNames,
+ ProguardTypeMatcher inheritanceAnnotation,
+ ProguardTypeMatcher inheritanceClassName,
+ boolean inheritanceIsExtends,
+ Set<ProguardMemberRule> memberRules) {
+ super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
+ classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+ }
+
+ public static ProguardWhyAreYouKeepingRule.Builder builder() {
+ return new ProguardWhyAreYouKeepingRule.Builder();
+ }
+
+ @Override
+ String typeString() {
+ return "whyareyoukeeping";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 6ac5694..1ddd7d3 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -3,9 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
-import com.google.common.base.Equivalence.Wrapper;
-import com.google.common.collect.Sets;
-
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationSet;
@@ -24,9 +21,11 @@
import com.android.tools.r8.shaking.ProguardTypeMatcher.MatchSpecificType;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.android.tools.r8.utils.ThreadUtils;
-
+import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.Sets;
import java.io.PrintStream;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
@@ -185,12 +184,23 @@
}
case KEEP: {
markClass(clazz, rule);
- markClass(clazz, rule);
markMatchingVisibleMethods(clazz, memberKeepRules, rule, null);
markMatchingFields(clazz, memberKeepRules, rule, null);
break;
}
}
+ } else if (rule instanceof ProguardCheckDiscardRule) {
+ if (memberKeepRules.isEmpty()) {
+ markClass(clazz, rule);
+ } else {
+ markMatchingFields(clazz, memberKeepRules, rule, clazz.type);
+ markMatchingMethods(clazz, memberKeepRules, rule, clazz.type);
+ }
+ } else if (rule instanceof ProguardWhyAreYouKeepingRule
+ || rule instanceof ProguardKeepPackageNamesRule) {
+ markClass(clazz, rule);
+ markMatchingVisibleMethods(clazz, memberKeepRules, rule, null);
+ markMatchingFields(clazz, memberKeepRules, rule, null);
} else if (rule instanceof ProguardAssumeNoSideEffectRule) {
markMatchingVisibleMethods(clazz, memberKeepRules, rule, null);
markMatchingFields(clazz, memberKeepRules, rule, null);
@@ -247,13 +257,24 @@
Collection<ProguardMemberRule> memberKeepRules, ProguardConfigurationRule rule,
DexType onlyIfClassKept) {
Set<Wrapper<DexMethod>> methodsMarked = new HashSet<>();
+ Arrays.stream(clazz.directMethods()).forEach(method ->
+ markMethod(method, memberKeepRules, rule, methodsMarked, onlyIfClassKept));
while (clazz != null) {
- clazz.forEachMethod(method ->
+ Arrays.stream(clazz.virtualMethods()).forEach(method ->
markMethod(method, memberKeepRules, rule, methodsMarked, onlyIfClassKept));
clazz = application.definitionFor(clazz.superType);
}
}
+ private void markMatchingMethods(DexClass clazz,
+ Collection<ProguardMemberRule> memberKeepRules, ProguardConfigurationRule rule,
+ DexType onlyIfClassKept) {
+ Arrays.stream(clazz.directMethods()).forEach(method ->
+ markMethod(method, memberKeepRules, rule, null, onlyIfClassKept));
+ Arrays.stream(clazz.virtualMethods()).forEach(method ->
+ markMethod(method, memberKeepRules, rule, null, onlyIfClassKept));
+ }
+
private void markMatchingFields(DexClass clazz,
Collection<ProguardMemberRule> memberKeepRules, ProguardConfigurationRule rule,
DexType onlyIfClassKept) {
@@ -375,7 +396,8 @@
private void markMethod(DexEncodedMethod method, Collection<ProguardMemberRule> rules,
ProguardConfigurationRule context, Set<Wrapper<DexMethod>> methodsMarked,
DexType onlyIfClassKept) {
- if (methodsMarked.contains(MethodSignatureEquivalence.get().wrap(method.method))) {
+ if ((methodsMarked != null)
+ && methodsMarked.contains(MethodSignatureEquivalence.get().wrap(method.method))) {
return;
}
for (ProguardMemberRule rule : rules) {
@@ -384,6 +406,9 @@
Log.verbose(getClass(), "Marking method `%s` due to `%s { %s }`.", method, context,
rule);
}
+ if (methodsMarked != null) {
+ methodsMarked.add(MethodSignatureEquivalence.get().wrap(method.method));
+ }
addItemToSets(method, context, rule, onlyIfClassKept);
}
}
@@ -461,24 +486,19 @@
if (!modifiers.allowsObfuscation) {
noObfuscation.add(item);
}
- if (modifiers.whyAreYouKeeping) {
- assert onlyIfClassKept == null;
- reasonAsked.add(item);
- }
- if (modifiers.keepPackageNames) {
- assert onlyIfClassKept == null;
- keepPackageName.add(item);
- }
if (modifiers.includeDescriptorClasses) {
includeDescriptorClasses(item, keepRule);
}
- if (modifiers.checkDiscarded) {
- checkDiscarded.add(item);
- }
} else if (context instanceof ProguardAssumeNoSideEffectRule) {
noSideEffects.put(item, rule);
+ } else if (context instanceof ProguardWhyAreYouKeepingRule) {
+ reasonAsked.add(item);
+ } else if (context instanceof ProguardKeepPackageNamesRule) {
+ keepPackageName.add(item);
} else if (context instanceof ProguardAssumeValuesRule) {
assumedValues.put(item, rule);
+ } else if (context instanceof ProguardCheckDiscardRule) {
+ checkDiscarded.add(item);
}
}
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/bridgestoremove/RemoveVisibilityBridgeMethodsTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java
similarity index 87%
rename from src/test/java/com/android/tools/r8/bridgeremoval/bridgestoremove/RemoveVisibilityBridgeMethodsTest.java
rename to src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java
index c33d5b4..6e3c114 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/bridgestoremove/RemoveVisibilityBridgeMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java
@@ -2,11 +2,13 @@
// 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.bridgeremoval.bridgestoremove;
+package com.android.tools.r8.bridgeremoval;
import static org.junit.Assert.assertFalse;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.bridgeremoval.bridgestoremove.Main;
+import com.android.tools.r8.bridgeremoval.bridgestoremove.Outer;
import com.android.tools.r8.utils.DexInspector;
import com.google.common.collect.ImmutableList;
import java.lang.reflect.Method;
diff --git a/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java b/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java
new file mode 100644
index 0000000..a56c0c8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java
@@ -0,0 +1,74 @@
+// 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.checkdiscarded;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.checkdiscarded.testclasses.Main;
+import com.android.tools.r8.checkdiscarded.testclasses.UnusedClass;
+import com.android.tools.r8.checkdiscarded.testclasses.UsedClass;
+import com.android.tools.r8.checkdiscarded.testclasses.WillBeGone;
+import com.android.tools.r8.checkdiscarded.testclasses.WillStay;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class CheckDiscardedTest extends TestBase {
+
+ private void run(boolean obfuscate, Class annotation, boolean checkMembers, boolean shouldFail)
+ throws Exception {
+ List<Class> classes = ImmutableList.of(
+ UnusedClass.class,
+ UsedClass.class,
+ Main.class);
+ String proguardConfig = keepMainProguardConfiguration(Main.class, true, obfuscate)
+ + checkDiscardRule(checkMembers, annotation);
+ try {
+ compileWithR8(classes, proguardConfig, this::noInlining);
+ } catch (CompilationError e) {
+ Assert.assertTrue(shouldFail);
+ return;
+ }
+ Assert.assertFalse(shouldFail);
+ }
+
+ private void noInlining(InternalOptions options) {
+ options.inlineAccessors = false;
+ }
+
+ private String checkDiscardRule(boolean member, Class annotation) {
+ if (member) {
+ return "-checkdiscard class * { @" + annotation.getCanonicalName() + " *; }";
+ } else {
+ return "-checkdiscard @" + annotation.getCanonicalName() + " class *";
+ }
+ }
+
+ @Test
+ public void classesAreGone() throws Exception {
+ run(false, WillBeGone.class, false, false);
+ run(true, WillBeGone.class, false, false);
+ }
+
+ @Test
+ public void classesAreNotGone() throws Exception {
+ run(false, WillStay.class, false, true);
+ run(true, WillStay.class, false, true);
+ }
+
+ @Test
+ public void membersAreGone() throws Exception {
+ run(false, WillBeGone.class, true, false);
+ run(true, WillBeGone.class, true, false);
+ }
+
+ @Test
+ public void membersAreNotGone() throws Exception {
+ run(false, WillStay.class, true, true);
+ run(true, WillStay.class, true, true);
+ }
+
+}
diff --git a/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/Main.java b/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/Main.java
new file mode 100644
index 0000000..677330a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/Main.java
@@ -0,0 +1,13 @@
+// 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.checkdiscarded.testclasses;
+
+@WillStay
+public class Main {
+
+ public static void main(String... args) {
+ UsedClass usedClass = new UsedClass();
+ System.out.println(usedClass.hello());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/UnusedClass.java b/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/UnusedClass.java
new file mode 100644
index 0000000..1b1689b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/UnusedClass.java
@@ -0,0 +1,9 @@
+// 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.checkdiscarded.testclasses;
+
+@WillBeGone
+public class UnusedClass {
+
+}
diff --git a/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/UsedClass.java b/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/UsedClass.java
new file mode 100644
index 0000000..253c114
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/UsedClass.java
@@ -0,0 +1,18 @@
+// 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.checkdiscarded.testclasses;
+
+@WillStay
+public class UsedClass {
+
+ @WillStay
+ public String hello() {
+ return "hello";
+ }
+
+ @WillBeGone
+ public String world() {
+ return "world";
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/WillBeGone.java b/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/WillBeGone.java
new file mode 100644
index 0000000..9176911
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/WillBeGone.java
@@ -0,0 +1,8 @@
+// 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.checkdiscarded.testclasses;
+
+public @interface WillBeGone {
+
+}
diff --git a/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/WillStay.java b/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/WillStay.java
new file mode 100644
index 0000000..e1925c3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/WillStay.java
@@ -0,0 +1,8 @@
+// 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.checkdiscarded.testclasses;
+
+public @interface WillStay {
+
+}