Check for last used rule being a negation
Bug: 174824047
Change-Id: Iace1b8705ee3b1fb0e537c4de86f2333b9b7a41b
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
index d00bc9a..ae80f4c 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
@@ -332,13 +332,15 @@
@Override
public boolean matches(DexType type) {
+ boolean lastWasNegated = false;
for (Entry<ProguardTypeMatcher> className : classNames.object2BooleanEntrySet()) {
if (className.getKey().matches(type)) {
// If we match a negation, abort as non-match. If we match a positive, return true.
return !className.getBooleanValue();
}
+ lastWasNegated = className.getBooleanValue();
}
- return false;
+ return lastWasNegated;
}
@Override
diff --git a/src/test/java/com/android/tools/r8/shaking/negatedrules/NegatedKeepRulesTest.java b/src/test/java/com/android/tools/r8/shaking/negatedrules/NegatedKeepRulesTest.java
index e452fee..a0f56b6 100644
--- a/src/test/java/com/android/tools/r8/shaking/negatedrules/NegatedKeepRulesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/negatedrules/NegatedKeepRulesTest.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.shaking.negatedrules;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assume.assumeTrue;
@@ -19,7 +18,7 @@
import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.Subject;
import org.hamcrest.Matcher;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -61,112 +60,110 @@
inspector -> {
assertThat(inspector.clazz(A.class), isPresent());
assertThat(inspector.clazz(B.class), not(isPresent()));
+ assertThat(inspector.clazz(C.class), not(isPresent()));
+ assertThat(inspector.clazz(D.class), not(isPresent()));
+ assertThat(inspector.clazz(FooBar.class), not(isPresent()));
+ assertThat(inspector.clazz(BarBar.class), not(isPresent()));
});
}
@Test
public void testNegationR8Compat() throws Exception {
- // TODO(b/174824047): Should not emit info regarding unused keep rule.
- // TODO(b/174824047): The class A should be present.
- testNegation(
- testForR8Compat(parameters.getBackend()).allowUnusedProguardConfigurationRules(),
- not(isPresent()));
+ testNegation(testForR8Compat(parameters.getBackend()));
}
@Test
public void testNegationPlainR8Full() throws Exception {
- // TODO(b/174824047): Should not emit info regarding unused keep rule.
- // TODO(b/174824047): The class A should be present.
- testNegation(
- testForR8(parameters.getBackend()).allowUnusedProguardConfigurationRules(),
- not(isPresent()));
+ testNegation(testForR8(parameters.getBackend()));
}
@Test
public void testNegationProguard() throws Exception {
assumeTrue(parameters.isCfRuntime());
- testNegation(testForProguard(ProguardVersion.V7_0_0).addKeepRules("-dontwarn"), isPresent());
+ testNegation(testForProguard(ProguardVersion.V7_0_0).addKeepRules("-dontwarn"));
}
- public void testNegation(
- TestShrinkerBuilder<?, ?, ?, ?, ?> testBuilder, Matcher<? super ClassSubject> matcher)
- throws Exception {
+ public void testNegation(TestShrinkerBuilder<?, ?, ?, ?, ?> testBuilder) throws Exception {
run(testBuilder.addKeepRules("-keep class !" + B.class.getTypeName() + " { *; }"))
.inspect(
inspector -> {
- assertThat(inspector.clazz(A.class), matcher);
+ assertThat(inspector.clazz(A.class), isPresent());
assertThat(inspector.clazz(B.class), not(isPresent()));
+ assertThat(inspector.clazz(C.class), isPresent());
+ assertThat(inspector.clazz(D.class), isPresent());
+ assertThat(inspector.clazz(FooBar.class), isPresent());
+ assertThat(inspector.clazz(BarBar.class), isPresent());
});
}
@Test
public void testExtendsR8Compat() throws Exception {
- testExtends(testForR8Compat(parameters.getBackend()), isPresent());
+ testExtends(testForR8Compat(parameters.getBackend()));
}
@Test
public void testExtendsR8Full() throws Exception {
- testExtends(testForR8(parameters.getBackend()), not(isPresent()));
+ testExtends(testForR8(parameters.getBackend()));
}
@Test
public void testExtendsProguard() throws Exception {
- testExtends(testForProguard(ProguardVersion.V7_0_0).addKeepRules("-dontwarn"), isPresent());
+ testExtends(testForProguard(ProguardVersion.V7_0_0).addKeepRules("-dontwarn"));
}
- public void testExtends(
- TestShrinkerBuilder<?, ?, ?, ?, ?> testBuilder, Matcher<? super ClassSubject> matcher)
- throws Exception {
+ public void testExtends(TestShrinkerBuilder<?, ?, ?, ?, ?> testBuilder) throws Exception {
run(testBuilder.addKeepRules("-keep class ** extends " + A.class.getTypeName() + " { *; }"))
.inspect(
inspector -> {
- assertThat(inspector.clazz(A.class), matcher);
- assertThat(inspector.clazz(B.class), isPresentAndNotRenamed());
+ // A is only kept in full-mode because we are keeping two sub-types. For full-mode,
+ // A could be removed. This is shown in the testNegatedExtends test.
+ assertThat(inspector.clazz(A.class), isPresent());
+ assertThat(inspector.clazz(B.class), isPresent());
+ assertThat(inspector.clazz(C.class), not(isPresent()));
+ assertThat(inspector.clazz(D.class), isPresent());
+ assertThat(inspector.clazz(FooBar.class), not(isPresent()));
+ assertThat(inspector.clazz(BarBar.class), not(isPresent()));
});
}
@Test
public void testNegatedExtendsR8Compat() throws Exception {
- // TODO(b/174824047): Should not emit info regarding unused keep rule.
- testNegatedExtends(
- testForR8Compat(parameters.getBackend()).allowUnusedProguardConfigurationRules());
+ testNegatedExtends(testForR8Compat(parameters.getBackend()), isPresent());
}
@Test
public void testNegatedExtendsR8Full() throws Exception {
- // TODO(b/174824047): Should not emit info regarding unused keep rule.
- testNegatedExtends(testForR8(parameters.getBackend()).allowUnusedProguardConfigurationRules());
+ testNegatedExtends(testForR8(parameters.getBackend()), not(isPresent()));
}
@Test
public void testNegatedExtendsProguard() throws Exception {
- testNegatedExtends(testForProguard(ProguardVersion.V7_0_0).addKeepRules("-dontwarn"));
+ testNegatedExtends(
+ testForProguard(ProguardVersion.V7_0_0).addKeepRules("-dontwarn"), isPresent());
}
- public void testNegatedExtends(TestShrinkerBuilder<?, ?, ?, ?, ?> testBuilder) throws Exception {
- run(testBuilder
- .addKeepRules("-keep class !** extends " + A.class.getTypeName() + " { *; }")
- .addKeepClassRules(C.class)) // <-- kept to have an output
+ public void testNegatedExtends(
+ TestShrinkerBuilder<?, ?, ?, ?, ?> testBuilder, Matcher<Subject> aMatcher) throws Exception {
+ // The negation binds closer than extends (at least for us).
+ run(testBuilder.addKeepRules("-keep class !**B extends " + A.class.getTypeName() + " { *; }"))
.inspect(
inspector -> {
- // TODO(b/174824047): These seems to be incorrect behavior, since one could expect A
- // and C to be kept and not B, since (A extends A) = (C extends A) = false and the
- // negation would become true.
- assertThat(inspector.clazz(A.class), not(isPresent()));
+ assertThat(inspector.clazz(A.class), aMatcher);
assertThat(inspector.clazz(B.class), not(isPresent()));
- assertThat(inspector.clazz(C.class), isPresent());
+ assertThat(inspector.clazz(C.class), not(isPresent()));
+ assertThat(inspector.clazz(D.class), isPresent());
+ assertThat(inspector.clazz(FooBar.class), not(isPresent()));
+ assertThat(inspector.clazz(BarBar.class), not(isPresent()));
});
}
@Test
public void testNegatedWithStarsR8Compat() throws Exception {
- // TODO(b/174824047): Should not emit info regarding unused keep rule.
testNegatedWithStars(testForR8Compat(parameters.getBackend()));
}
@Test
public void testNegatedWithStarsR8Full() throws Exception {
- // TODO(b/174824047): Should not emit info regarding unused keep rule.
testNegatedWithStars(testForR8(parameters.getBackend()));
}
@@ -182,18 +179,20 @@
inspector -> {
assertThat(inspector.clazz(A.class), isPresent());
assertThat(inspector.clazz(B.class), not(isPresent()));
+ assertThat(inspector.clazz(C.class), isPresent());
+ assertThat(inspector.clazz(D.class), isPresent());
+ assertThat(inspector.clazz(FooBar.class), isPresent());
+ assertThat(inspector.clazz(BarBar.class), isPresent());
});
}
@Test
public void testMultipleNegatedWithStarsR8Compat() throws Exception {
- // TODO(b/174824047): Should not emit info regarding unused keep rule.
testMultipleNegatedWithStars(testForR8Compat(parameters.getBackend()));
}
@Test
public void testMultipleNegatedWithStarsR8Full() throws Exception {
- // TODO(b/174824047): Should not emit info regarding unused keep rule.
testMultipleNegatedWithStars(testForR8(parameters.getBackend()));
}
@@ -210,13 +209,73 @@
inspector -> {
assertThat(inspector.clazz(A.class), isPresent());
assertThat(inspector.clazz(B.class), not(isPresent()));
+ assertThat(inspector.clazz(C.class), not(isPresent()));
+ assertThat(inspector.clazz(D.class), isPresent());
+ assertThat(inspector.clazz(FooBar.class), isPresent());
+ assertThat(inspector.clazz(BarBar.class), isPresent());
+ });
+ }
+
+ @Test
+ public void testFooBarR8Compat() throws Exception {
+ testFooBar(testForR8Compat(parameters.getBackend()));
+ }
+
+ @Test
+ public void testFooBarR8Full() throws Exception {
+ testFooBar(testForR8(parameters.getBackend()));
+ }
+
+ @Test
+ public void testFooBarProguard() throws Exception {
+ testFooBar(testForProguard(ProguardVersion.V7_0_0).addKeepRules("-dontwarn"));
+ }
+
+ public void testFooBar(TestShrinkerBuilder<?, ?, ?, ?, ?> testBuilder) throws Exception {
+ run(testBuilder.addKeepRules("-keep class !" + FooBar.class.getTypeName() + ", **Bar { *; }"))
+ .inspect(
+ inspector -> {
+ assertThat(inspector.clazz(BarBar.class), isPresent());
+ assertThat(inspector.clazz(FooBar.class), not(isPresent()));
+ assertThat(inspector.clazz(A.class), not(isPresent()));
+ assertThat(inspector.clazz(B.class), not(isPresent()));
+ assertThat(inspector.clazz(C.class), not(isPresent()));
+ assertThat(inspector.clazz(D.class), not(isPresent()));
+ });
+ }
+
+ @Test
+ public void testMultipleNegatedR8Compat() throws Exception {
+ testMultipleNegated(testForR8Compat(parameters.getBackend()));
+ }
+
+ @Test
+ public void testMultipleNegatedR8Full() throws Exception {
+ testMultipleNegated(testForR8(parameters.getBackend()));
+ }
+
+ @Test
+ public void testMultipleNegatedProguard() throws Exception {
+ testMultipleNegated(testForProguard(ProguardVersion.V7_0_0).addKeepRules("-dontwarn"));
+ }
+
+ public void testMultipleNegated(TestShrinkerBuilder<?, ?, ?, ?, ?> testBuilder) throws Exception {
+ run(testBuilder.addKeepRules("-keep class !" + FooBar.class.getTypeName() + ", !**Bar { *; }"))
+ .inspect(
+ inspector -> {
+ assertThat(inspector.clazz(BarBar.class), not(isPresent()));
+ assertThat(inspector.clazz(FooBar.class), not(isPresent()));
+ assertThat(inspector.clazz(A.class), isPresent());
+ assertThat(inspector.clazz(B.class), isPresent());
+ assertThat(inspector.clazz(C.class), isPresent());
+ assertThat(inspector.clazz(D.class), isPresent());
});
}
private TestCompileResult<?, ?> run(TestShrinkerBuilder<?, ?, ?, ?, ?> testBuilder)
throws Exception {
return testBuilder
- .addProgramClasses(A.class, B.class, C.class)
+ .addProgramClasses(A.class, B.class, C.class, D.class, FooBar.class, BarBar.class)
.setMinApi(AndroidApiLevel.B)
.noMinification()
.compile();
@@ -224,12 +283,13 @@
public static class A {}
- public static class B extends A {
-
- public static String test() {
- return "Hello World!";
- }
- }
+ public static class B extends A {}
public static class C {}
+
+ public static class D extends A {}
+
+ public static class FooBar {}
+
+ public static class BarBar {}
}