Experimental support for checkenumunboxed
Bug: 234557774
Change-Id: I8d98ae5b27294d345e21900729b6abd641f7db23
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 91040b0..8d323ab 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -663,6 +663,10 @@
}
+ void setEnableExperimentalCheckEnumUnboxed() {
+ parserOptionsBuilder.setEnableExperimentalCheckEnumUnboxed(true);
+ }
+
void setEnableExperimentalConvertCheckNotNull() {
parserOptionsBuilder.setEnableExperimentalConvertCheckNotNull(true);
}
diff --git a/src/main/java/com/android/tools/r8/errors/CheckEnumUnboxedDiagnostic.java b/src/main/java/com/android/tools/r8/errors/CheckEnumUnboxedDiagnostic.java
new file mode 100644
index 0000000..3339192
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/CheckEnumUnboxedDiagnostic.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2022, 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.errors;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import com.google.common.collect.ImmutableList;
+import java.util.Comparator;
+import java.util.List;
+
+@Keep
+public class CheckEnumUnboxedDiagnostic implements Diagnostic {
+
+ private final List<String> messages;
+
+ CheckEnumUnboxedDiagnostic(List<String> messages) {
+ this.messages = messages;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /** The origin of a -checkenumunboxed failure is not unique. (The whole app is to blame.) */
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+
+ /** The position of a -checkenumunboxed failure is always unknown. */
+ @Override
+ public Position getPosition() {
+ return Position.UNKNOWN;
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ StringBuilder builder = new StringBuilder("Enum unboxing checks failed.");
+ for (String message : messages) {
+ builder.append(System.lineSeparator());
+ builder.append(message);
+ }
+ return builder.toString();
+ }
+
+ public static class Builder {
+
+ private final ImmutableList.Builder<String> messagesBuilder = ImmutableList.builder();
+
+ public Builder addFailedEnums(List<DexProgramClass> failed) {
+ failed.sort(Comparator.comparing(DexClass::getType));
+ for (DexProgramClass clazz : failed) {
+ messagesBuilder.add("Enum " + clazz.getTypeName() + " was not unboxed.");
+ }
+ return this;
+ }
+
+ public CheckEnumUnboxedDiagnostic build() {
+ return new CheckEnumUnboxedDiagnostic(messagesBuilder.build());
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 2dc8029..f94cde1 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -741,6 +741,7 @@
outliner.rewriteWithLens();
enumUnboxer.unboxEnums(appView, this, postMethodProcessorBuilder, executorService, feedback);
+ appView.unboxedEnums().checkEnumsUnboxed(appView);
GraphLens graphLensForSecondaryOptimizationPass = appView.graphLens();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumDataMap.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumDataMap.java
index 264fc7b..00c4f50 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumDataMap.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumDataMap.java
@@ -4,13 +4,18 @@
package com.android.tools.r8.ir.optimize.enums;
+import com.android.tools.r8.errors.CheckEnumUnboxedDiagnostic;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldKnownData;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Set;
public class EnumDataMap {
@@ -24,6 +29,23 @@
this.map = map;
}
+ public void checkEnumsUnboxed(AppView<AppInfoWithLiveness> appView) {
+ List<DexProgramClass> failed = new ArrayList<>();
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ if (clazz.isEnum()) {
+ if (appView.getKeepInfo(clazz).isCheckEnumUnboxedEnabled(appView.options())
+ && !isUnboxedEnum(clazz)) {
+ failed.add(clazz);
+ }
+ }
+ }
+ if (!failed.isEmpty()) {
+ CheckEnumUnboxedDiagnostic diagnostic =
+ CheckEnumUnboxedDiagnostic.builder().addFailedEnums(failed).build();
+ throw appView.reporter().fatalError(diagnostic);
+ }
+ }
+
public boolean isUnboxedEnum(DexProgramClass clazz) {
return isUnboxedEnum(clazz.getType());
}
diff --git a/src/main/java/com/android/tools/r8/shaking/CheckEnumUnboxedRule.java b/src/main/java/com/android/tools/r8/shaking/CheckEnumUnboxedRule.java
new file mode 100644
index 0000000..da2fbbe
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/CheckEnumUnboxedRule.java
@@ -0,0 +1,84 @@
+// Copyright (c) 2022, 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 CheckEnumUnboxedRule extends ProguardConfigurationRule {
+
+ public static final String RULE_NAME = "checkenumunboxed";
+
+ public static class Builder
+ extends ProguardConfigurationRule.Builder<CheckEnumUnboxedRule, Builder> {
+
+ private Builder() {
+ super();
+ }
+
+ @Override
+ public Builder self() {
+ return this;
+ }
+
+ @Override
+ public CheckEnumUnboxedRule build() {
+ return new CheckEnumUnboxedRule(
+ origin,
+ getPosition(),
+ source,
+ buildClassAnnotations(),
+ classAccessFlags,
+ negatedClassAccessFlags,
+ classTypeNegated,
+ classType,
+ classNames,
+ buildInheritanceAnnotations(),
+ inheritanceClassName,
+ inheritanceIsExtends,
+ memberRules);
+ }
+ }
+
+ protected CheckEnumUnboxedRule(
+ Origin origin,
+ Position position,
+ String source,
+ List<ProguardTypeMatcher> classAnnotations,
+ ProguardAccessFlags classAccessFlags,
+ ProguardAccessFlags negatedClassAccessFlags,
+ boolean classTypeNegated,
+ ProguardClassType classType,
+ ProguardClassNameList classNames,
+ List<ProguardTypeMatcher> inheritanceAnnotations,
+ ProguardTypeMatcher inheritanceClassName,
+ boolean inheritanceIsExtends,
+ List<ProguardMemberRule> memberRules) {
+ super(
+ origin,
+ position,
+ source,
+ classAnnotations,
+ classAccessFlags,
+ negatedClassAccessFlags,
+ classTypeNegated,
+ classType,
+ classNames,
+ inheritanceAnnotations,
+ inheritanceClassName,
+ inheritanceIsExtends,
+ memberRules);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ String typeString() {
+ return RULE_NAME;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java
index bf969b7..7199fcb 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java
@@ -32,8 +32,11 @@
return bottom().joiner();
}
+ private final boolean checkEnumUnboxed;
+
private KeepClassInfo(Builder builder) {
super(builder);
+ this.checkEnumUnboxed = builder.isCheckEnumUnboxedEnabled();
}
@Override
@@ -41,6 +44,14 @@
return new Builder(this);
}
+ public boolean isCheckEnumUnboxedEnabled(GlobalKeepInfoConfiguration configuration) {
+ return internalIsCheckEnumUnboxedEnabled();
+ }
+
+ boolean internalIsCheckEnumUnboxedEnabled() {
+ return checkEnumUnboxed;
+ }
+
public Joiner joiner() {
assert !isTop();
return new Joiner(this);
@@ -87,12 +98,34 @@
public static class Builder extends KeepInfo.Builder<Builder, KeepClassInfo> {
+ private boolean checkEnumUnboxed;
+
private Builder() {
super();
}
private Builder(KeepClassInfo original) {
super(original);
+ checkEnumUnboxed = original.internalIsCheckEnumUnboxedEnabled();
+ }
+
+ // Check enum unboxed.
+
+ public boolean isCheckEnumUnboxedEnabled() {
+ return checkEnumUnboxed;
+ }
+
+ public Builder setCheckEnumUnboxed(boolean checkEnumUnboxed) {
+ this.checkEnumUnboxed = checkEnumUnboxed;
+ return self();
+ }
+
+ public Builder setCheckEnumUnboxed() {
+ return setCheckEnumUnboxed(true);
+ }
+
+ public Builder unsetCheckEnumUnboxed() {
+ return setCheckEnumUnboxed(false);
}
@Override
@@ -116,9 +149,25 @@
}
@Override
+ boolean internalIsEqualTo(KeepClassInfo other) {
+ return super.internalIsEqualTo(other)
+ && isCheckEnumUnboxedEnabled() == other.internalIsCheckEnumUnboxedEnabled();
+ }
+
+ @Override
public KeepClassInfo doBuild() {
return new KeepClassInfo(this);
}
+
+ @Override
+ public Builder makeTop() {
+ return super.makeTop().unsetCheckEnumUnboxed();
+ }
+
+ @Override
+ public Builder makeBottom() {
+ return super.makeBottom().unsetCheckEnumUnboxed();
+ }
}
public static class Joiner extends KeepInfo.Joiner<Joiner, Builder, KeepClassInfo> {
@@ -127,6 +176,11 @@
super(info.builder());
}
+ public Joiner setCheckEnumUnboxed() {
+ builder.setCheckEnumUnboxed();
+ return self();
+ }
+
@Override
public Joiner asClassJoiner() {
return this;
@@ -135,7 +189,8 @@
@Override
public Joiner merge(Joiner joiner) {
// Should be extended to merge the fields of this class in case any are added.
- return super.merge(joiner);
+ return super.merge(joiner)
+ .applyIf(joiner.builder.isCheckEnumUnboxedEnabled(), Joiner::setCheckEnumUnboxed);
}
@Override
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 7dd6643..8b4210b 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -80,7 +80,7 @@
"keepkotlinmetadata");
private static final List<String> IGNORED_CLASS_DESCRIPTOR_OPTIONS =
- ImmutableList.of("isclassnamestring", "whyarenotsimple", "checkenumunboxed");
+ ImmutableList.of("isclassnamestring", "whyarenotsimple");
private static final List<String> WARNED_SINGLE_ARG_OPTIONS = ImmutableList.of(
// TODO(b/37137994): -outjars should be reported as errors, not just as warnings!
@@ -122,8 +122,9 @@
dexItemFactory,
reporter,
ProguardConfigurationParserOptions.builder()
- .setEnableExperimentalConvertCheckNotNull(true)
- .setEnableExperimentalWhyAreYouNotInlining(true)
+ .setEnableExperimentalCheckEnumUnboxed(false)
+ .setEnableExperimentalConvertCheckNotNull(false)
+ .setEnableExperimentalWhyAreYouNotInlining(false)
.setEnableTestingOptions(false)
.build());
}
@@ -484,6 +485,13 @@
private boolean parseExperimentalOption(TextPosition optionStart)
throws ProguardRuleParserException {
+ if (acceptString(CheckEnumUnboxedRule.RULE_NAME)) {
+ CheckEnumUnboxedRule checkEnumUnboxedRule = parseCheckEnumUnboxedRule(optionStart);
+ if (options.isExperimentalCheckEnumUnboxedEnabled()) {
+ configurationBuilder.addRule(checkEnumUnboxedRule);
+ }
+ return true;
+ }
if (acceptString(ConvertCheckNotNullRule.RULE_NAME)) {
ConvertCheckNotNullRule convertCheckNotNullRule = parseConvertCheckNotNullRule(optionStart);
if (options.isExperimentalConvertCheckNotNullEnabled()) {
@@ -1717,6 +1725,17 @@
return builder.build();
}
+ private CheckEnumUnboxedRule parseCheckEnumUnboxedRule(Position start)
+ throws ProguardRuleParserException {
+ CheckEnumUnboxedRule.Builder builder =
+ CheckEnumUnboxedRule.builder().setOrigin(origin).setStart(start);
+ parseClassSpec(builder);
+ Position end = getPosition();
+ builder.setSource(getSourceSnippet(contents, start, end));
+ builder.setEnd(end);
+ return builder.build();
+ }
+
private ConvertCheckNotNullRule parseConvertCheckNotNullRule(Position start)
throws ProguardRuleParserException {
ConvertCheckNotNullRule.Builder builder =
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParserOptions.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParserOptions.java
index 66afbba..8811df7 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParserOptions.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParserOptions.java
@@ -8,14 +8,17 @@
public class ProguardConfigurationParserOptions {
+ private final boolean enableExperimentalCheckEnumUnboxed;
private final boolean enableExperimentalConvertCheckNotNull;
private final boolean enableExperimentalWhyAreYouNotInlining;
private final boolean enableTestingOptions;
ProguardConfigurationParserOptions(
+ boolean enableExperimentalCheckEnumUnboxed,
boolean enableExperimentalConvertCheckNotNull,
boolean enableExperimentalWhyAreYouNotInlining,
boolean enableTestingOptions) {
+ this.enableExperimentalCheckEnumUnboxed = enableExperimentalCheckEnumUnboxed;
this.enableExperimentalConvertCheckNotNull = enableExperimentalConvertCheckNotNull;
this.enableExperimentalWhyAreYouNotInlining = enableExperimentalWhyAreYouNotInlining;
this.enableTestingOptions = enableTestingOptions;
@@ -25,6 +28,10 @@
return new Builder();
}
+ public boolean isExperimentalCheckEnumUnboxedEnabled() {
+ return enableExperimentalCheckEnumUnboxed;
+ }
+
public boolean isExperimentalConvertCheckNotNullEnabled() {
return enableExperimentalConvertCheckNotNull;
}
@@ -39,11 +46,15 @@
public static class Builder {
+ private boolean enableExperimentalCheckEnumUnboxed;
private boolean enableExperimentalConvertCheckNotNull;
private boolean enableExperimentalWhyAreYouNotInlining;
private boolean enableTestingOptions;
public Builder readEnvironment() {
+ enableExperimentalCheckEnumUnboxed =
+ parseSystemPropertyOrDefault(
+ "com.android.tools.r8.experimental.enablecheckenumunboxed", false);
enableExperimentalConvertCheckNotNull =
parseSystemPropertyOrDefault(
"com.android.tools.r8.experimental.enableconvertchecknotnull", false);
@@ -55,6 +66,12 @@
return this;
}
+ public Builder setEnableExperimentalCheckEnumUnboxed(
+ boolean enableExperimentalCheckEnumUnboxed) {
+ this.enableExperimentalCheckEnumUnboxed = enableExperimentalCheckEnumUnboxed;
+ return this;
+ }
+
public Builder setEnableExperimentalConvertCheckNotNull(
boolean enableExperimentalConvertCheckNotNull) {
this.enableExperimentalConvertCheckNotNull = enableExperimentalConvertCheckNotNull;
@@ -74,6 +91,7 @@
public ProguardConfigurationParserOptions build() {
return new ProguardConfigurationParserOptions(
+ enableExperimentalCheckEnumUnboxed,
enableExperimentalConvertCheckNotNull,
enableExperimentalWhyAreYouNotInlining,
enableTestingOptions);
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index 762a307..d804fa6 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -261,6 +261,8 @@
throw new Unreachable("-if rule will be evaluated separately, not here.");
} else if (rule.isProguardCheckDiscardRule()) {
evaluateCheckDiscardRule(clazz, rule.asProguardCheckDiscardRule());
+ } else if (rule instanceof CheckEnumUnboxedRule) {
+ evaluateCheckEnumUnboxedRule(clazz, (CheckEnumUnboxedRule) rule);
} else if (rule instanceof ProguardWhyAreYouKeepingRule) {
markClass(clazz, rule, ifRule);
markMatchingVisibleMethods(clazz, memberKeepRules, rule, null, true, ifRule);
@@ -1477,6 +1479,36 @@
context.markAsUsed();
}
+ private void evaluateCheckEnumUnboxedRule(DexClass clazz, CheckEnumUnboxedRule rule) {
+ if (clazz.isProgramClass()) {
+ if (clazz.isEnum()) {
+ dependentMinimumKeepInfo
+ .getOrCreateUnconditionalMinimumKeepInfo()
+ .getOrCreateMinimumKeepInfoFor(clazz.getType())
+ .asClassJoiner()
+ .setCheckEnumUnboxed();
+ } else {
+ StringDiagnostic warning =
+ new StringDiagnostic(
+ "The rule `"
+ + rule
+ + "` matches the non-enum class "
+ + clazz.getTypeName()
+ + ".");
+ appView.reporter().warning(warning);
+ }
+ } else {
+ StringDiagnostic warning =
+ new StringDiagnostic(
+ "The rule `"
+ + rule
+ + "` matches the non-program class "
+ + clazz.getTypeName()
+ + ".");
+ appView.reporter().warning(warning);
+ }
+ }
+
private void evaluateKeepRule(
ProgramDefinition item,
ProguardKeepRule context,
diff --git a/src/test/java/com/android/tools/r8/CheckEnumUnboxed.java b/src/test/java/com/android/tools/r8/CheckEnumUnboxed.java
new file mode 100644
index 0000000..11d8719
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/CheckEnumUnboxed.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2022, 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;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE})
+public @interface CheckEnumUnboxed {}
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index b3bb43e..ebe8352 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.shaking.CheckEnumUnboxedRule;
import com.android.tools.r8.shaking.CollectingGraphConsumer;
import com.android.tools.r8.shaking.KeepUnusedReturnValueRule;
import com.android.tools.r8.shaking.NoFieldTypeStrengtheningRule;
@@ -498,6 +499,12 @@
return addOptionsModification(options -> options.testing.allowInliningOfSynthetics = false);
}
+ public T enableCheckEnumUnboxedAnnotations() {
+ return addCheckEnumUnboxedAnnotation()
+ .addInternalMatchInterfaceRule(CheckEnumUnboxedRule.RULE_NAME, CheckEnumUnboxed.class)
+ .enableExperimentalCheckEnumUnboxed();
+ }
+
public T enableKeepUnusedReturnValueAnnotations() {
return addKeepUnusedReturnValueAnnotation()
.addInternalMatchAnnotationOnMethodRule(
@@ -655,6 +662,11 @@
return self();
}
+ public T enableExperimentalCheckEnumUnboxed() {
+ builder.setEnableExperimentalCheckEnumUnboxed();
+ return self();
+ }
+
public T enableExperimentalConvertCheckNotNull() {
builder.setEnableExperimentalConvertCheckNotNull();
return self();
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index a5c5302..58e5901 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -436,6 +436,10 @@
return addTestingAnnotation(AssumeNoSideEffects.class);
}
+ public final T addCheckEnumUnboxedAnnotation() {
+ return addTestingAnnotation(CheckEnumUnboxed.class);
+ }
+
public final T addConstantArgumentAnnotations() {
return addTestingAnnotation(KeepConstantArguments.class);
}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/CheckEnumUnboxedTest.java b/src/test/java/com/android/tools/r8/enumunboxing/CheckEnumUnboxedTest.java
new file mode 100644
index 0000000..c5a786c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/CheckEnumUnboxedTest.java
@@ -0,0 +1,80 @@
+// Copyright (c) 2022, 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.enumunboxing;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.equalTo;
+
+import com.android.tools.r8.CheckEnumUnboxed;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.errors.CheckEnumUnboxedDiagnostic;
+import com.android.tools.r8.utils.codeinspector.AssertUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class CheckEnumUnboxedTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ AssertUtils.assertFailsCompilation(
+ () ->
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .enableCheckEnumUnboxedAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compileWithExpectedDiagnostics(
+ diagnostics -> {
+ diagnostics.assertErrorsMatch(
+ allOf(
+ diagnosticType(CheckEnumUnboxedDiagnostic.class),
+ diagnosticMessage(
+ equalTo(
+ "Enum unboxing checks failed."
+ + System.lineSeparator()
+ + "Enum "
+ + EscapingEnum.class.getTypeName()
+ + " was not unboxed."))));
+ }));
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ EscapingEnum escapingEnum = System.currentTimeMillis() > 0 ? EscapingEnum.A : EscapingEnum.B;
+ System.out.println(escapingEnum);
+ UnboxedEnum unboxedEnum = System.currentTimeMillis() > 0 ? UnboxedEnum.A : UnboxedEnum.B;
+ System.out.println(unboxedEnum.ordinal());
+ }
+ }
+
+ @CheckEnumUnboxed
+ enum EscapingEnum {
+ A,
+ B
+ }
+
+ @CheckEnumUnboxed
+ enum UnboxedEnum {
+ A,
+ B
+ }
+}
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 5ff24ab..d0ef627 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -188,6 +188,7 @@
new DexItemFactory(),
reporter,
ProguardConfigurationParserOptions.builder()
+ .setEnableExperimentalCheckEnumUnboxed(false)
.setEnableExperimentalConvertCheckNotNull(false)
.setEnableExperimentalWhyAreYouNotInlining(false)
.setEnableTestingOptions(true)
@@ -754,7 +755,16 @@
@Test
public void testConvertCheckNotNullWithoutReturn() {
DexItemFactory dexItemFactory = new DexItemFactory();
- ProguardConfigurationParser parser = new ProguardConfigurationParser(dexItemFactory, reporter);
+ ProguardConfigurationParser parser =
+ new ProguardConfigurationParser(
+ dexItemFactory,
+ reporter,
+ ProguardConfigurationParserOptions.builder()
+ .setEnableExperimentalCheckEnumUnboxed(false)
+ .setEnableExperimentalConvertCheckNotNull(true)
+ .setEnableExperimentalWhyAreYouNotInlining(false)
+ .setEnableTestingOptions(false)
+ .build());
String rule = "-convertchecknotnull class C { void m(**, ...); }";
parser.parse(createConfigurationForTesting(ImmutableList.of(rule)));
verifyParserEndsCleanly();