[KeepAnno] Extract class and any members items as a disjoint rule
Bug: b/322104143
Bug: b/323136645
Bug: b/321674067
Change-Id: Ia9617c8fd1e0a469a8e7426f0de864ed02e3cb00
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
index c5d43c5..dba2375 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
@@ -386,16 +386,26 @@
OnTargetCallback callback) {
TargetKeepKind keepKind = TargetKeepKind.JUST_MEMBERS;
List<KeepBindingSymbol> targetMembers = new ArrayList<>();
+ boolean isAllMembersTarget = false;
for (KeepBindingSymbol targetReference : targets) {
KeepItemPattern item = bindings.get(targetReference).getItem();
if (item.isClassItemPattern()) {
keepKind = TargetKeepKind.CLASS_AND_MEMBERS;
- } else {
+ } else if (!isAllMembersTarget) {
KeepMemberItemPattern memberItemPattern = item.asMemberItemPattern();
+ if (memberItemPattern.getMemberPattern().isAllMembers()) {
+ targetMembers.clear();
+ isAllMembersTarget = true;
+ }
memberPatterns.putIfAbsent(targetReference, memberItemPattern.getMemberPattern());
targetMembers.add(targetReference);
}
}
+ if (isAllMembersTarget && keepKind == TargetKeepKind.CLASS_AND_MEMBERS) {
+ // If the rule is keeping the class and all of its members, the member pattern should
+ // match even the empty set of members, e.g., be disjoint from keeping the class.
+ keepKind = TargetKeepKind.CLASS_OR_MEMBERS;
+ }
if (targetMembers.isEmpty()) {
keepKind = TargetKeepKind.CLASS_OR_MEMBERS;
}
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepEmptyClassTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepEmptyClassTest.java
index f4f769b..c1e5683 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepEmptyClassTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepEmptyClassTest.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.keepanno;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.keepanno.annotations.KeepItemKind;
@@ -25,7 +25,6 @@
public class KeepEmptyClassTest extends KeepAnnoTestBase {
static final String EXPECTED = StringUtils.lines("B, #members: 0");
- static final String UNEXPECTED = StringUtils.lines("b, #members: 0");
@Parameter public KeepAnnoParameters parameters;
@@ -43,8 +42,7 @@
transformer(B.class).removeMethods(MethodPredicate.all()).transform())
.setExcludedOuterClass(getClass())
.run(TestClass.class)
- // TODO(b/322104143): The any-member pattern does not match due to classeswithmembers.
- .assertSuccessWithOutput(parameters.isReference() ? EXPECTED : UNEXPECTED)
+ .assertSuccessWithOutput(EXPECTED)
.applyIf(parameters.isShrinker(), r -> r.inspect(this::checkOutput));
}
@@ -53,8 +51,7 @@
}
private void checkOutput(CodeInspector inspector) {
- // TODO(b/322104143): The class should not be renamed.
- assertThat(inspector.clazz(B.class), isPresentAndRenamed());
+ assertThat(inspector.clazz(B.class), isPresentAndNotRenamed());
}
static class A {
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepFieldValueApiTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepFieldValueApiTest.java
index dfca281..9ac84d1 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepFieldValueApiTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepFieldValueApiTest.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.keepanno;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
@@ -68,11 +67,7 @@
.addProgramFiles(lib.get())
.setMinApi(parameters.parameters())
.run(parameters.getRuntime(), TestClass.class)
- // TODO(b/322104143): The -keepclasseswithmembers rule does not keep B and its members.
- .applyIf(
- parameters.isPG(),
- r -> r.assertFailureWithErrorThatThrows(ClassNotFoundException.class),
- r -> r.assertSuccessWithOutput(EXPECTED));
+ .assertSuccessWithOutput(EXPECTED);
}
public List<Class<?>> getLibraryClasses() {
@@ -88,11 +83,6 @@
assertThat(aClass, isPresent());
assertThat(aClass.uniqueFieldWithFinalName("CLASS"), isPresent());
ClassSubject bClass = inspector.clazz(B.class);
- if (parameters.isPG()) {
- // TODO(b/322104143): The -keepclasseswithmembers rule does not keep B and its members.
- assertThat(bClass, isAbsent());
- return;
- }
assertThat(bClass, isPresent());
assertThat(bClass.uniqueMethodWithOriginalName("foo"), isPresent());
assertThat(bClass.uniqueMethodWithOriginalName("bar"), isPresent());