checkapi: Allow default values for enum-type fields am: 308bbe08b3

Original change: https://android-review.googlesource.com/c/platform/system/tools/aidl/+/1687646

Change-Id: I5cdad65a3943c9bf32228d931abb4ce9d9ceaa89
diff --git a/aidl_checkapi.cpp b/aidl_checkapi.cpp
index c45626d..08d1798 100644
--- a/aidl_checkapi.cpp
+++ b/aidl_checkapi.cpp
@@ -231,6 +231,19 @@
                      });
 }
 
+static bool EvaluatesToZero(const AidlEnumDeclaration& enum_decl, const std::string& value) {
+  if (value == "") return true;
+  // Because --check_api runs with "valid" AIDL definitions, we can safely assume that
+  // the value is formatted as <scope>.<enumerator>.
+  auto enumerator_name = value.substr(value.find_last_of('.') + 1);
+  for (const auto& enumerator : enum_decl.GetEnumerators()) {
+    if (enumerator->GetName() == enumerator_name) {
+      return enumerator->GetValue()->Literal() == "0";
+    }
+  }
+  AIDL_FATAL(enum_decl) << "Can't find " << enumerator_name << " in " << enum_decl.GetName();
+}
+
 static bool are_compatible_parcelables(const AidlDefinedType& older, const AidlTypenames&,
                                        const AidlDefinedType& newer,
                                        const AidlTypenames& new_types) {
@@ -257,10 +270,18 @@
 
     string old_value = old_field->GetDefaultValue() ? old_field->GetDefaultValue()->Literal() : "";
     string new_value = new_field->GetDefaultValue() ? new_field->GetDefaultValue()->Literal() : "";
-    if (old_value != new_value) {
-      AIDL_ERROR(new_field) << "Changed default value: " << old_value << " to " << new_value << ".";
-      compatible = false;
+
+    if (old_value == new_value) {
+      continue;
     }
+    // For enum type fields, we accept setting explicit default value which is "zero"
+    auto enum_decl = new_types.GetEnumDeclaration(new_field->GetType());
+    if (old_value == "" && enum_decl && EvaluatesToZero(*enum_decl, new_value)) {
+      continue;
+    }
+
+    AIDL_ERROR(new_field) << "Changed default value: " << old_value << " to " << new_value << ".";
+    compatible = false;
   }
 
   // Reordering of fields is an incompatible change.
diff --git a/aidl_unittest.cpp b/aidl_unittest.cpp
index 8eef049..f1dce8d 100644
--- a/aidl_unittest.cpp
+++ b/aidl_unittest.cpp
@@ -2165,6 +2165,32 @@
   EXPECT_TRUE(::android::aidl::check_api(options_, io_delegate_));
 }
 
+TEST_F(AidlTestCompatibleChanges, CompatibleExplicitDefaults) {
+  io_delegate_.SetFileContents("old/p/Data.aidl",
+                               "package p;\n"
+                               "parcelable Data {\n"
+                               "  p.Enum e;\n"
+                               "}");
+  io_delegate_.SetFileContents("old/p/Enum.aidl",
+                               "package p;\n"
+                               "enum Enum {\n"
+                               "  FOO = 0,\n"
+                               "  BAR = 1,\n"
+                               "}");
+  io_delegate_.SetFileContents("new/p/Data.aidl",
+                               "package p;\n"
+                               "parcelable Data {\n"
+                               "  p.Enum e = p.Enum.FOO;\n"
+                               "}");
+  io_delegate_.SetFileContents("new/p/Enum.aidl",
+                               "package p;\n"
+                               "enum Enum {\n"
+                               "  FOO = 0,\n"
+                               "  BAR = 1,\n"
+                               "}");
+  EXPECT_TRUE(::android::aidl::check_api(options_, io_delegate_));
+}
+
 class AidlTestIncompatibleChanges : public AidlTest {
  protected:
   Options options_ = Options::From("aidl --checkapi old new");