Support nullable types in Rust/NDK

In NDK/Rust, a few variants of nullable types were not supported.
- nullable parcelable/ParcelFileDescriptor
- nullable list or nullable array of parcelables/ParcelFileDescriptors

Bug: 175744740
Test: aidl_integration_test
Change-Id: I68c6551520fee5525d38688f52386c8092533d29
diff --git a/aidl_unittest.cpp b/aidl_unittest.cpp
index 7e33e6b..1e6ec6e 100644
--- a/aidl_unittest.cpp
+++ b/aidl_unittest.cpp
@@ -4685,125 +4685,181 @@
 };
 
 const TypeParam kTypeParams[] = {
-    {"primitive", "int"},   {"primitiveArray", "int[]"},
-    {"String", "String"},   {"StringArray", "String[]"},
-    {"IBinder", "IBinder"}, {"ParcelFileDescriptor", "ParcelFileDescriptor"},
-    {"parcelable", "Foo"},  {"enum", "a.Enum"},
-    {"union", "a.Union"},   {"interface", "a.IBar"},
+    {"primitive", "int"},    {"primitiveArray", "int[]"},
+    {"String", "String"},    {"StringArray", "String[]"},
+    {"IBinder", "IBinder"},  {"ParcelFileDescriptor", "ParcelFileDescriptor"},
+    {"parcelable", "a.Foo"}, {"enum", "a.Enum"},
+    {"union", "a.Union"},    {"interface", "a.IBar"},
 };
 
-const std::map<std::string, std::string> kListSupportExpectations = {
-    {"cpp_primitive", "A generic type cannot have any primitive type parameters."},
-    {"java_primitive", "A generic type cannot have any primitive type parameters."},
-    {"ndk_primitive", "A generic type cannot have any primitive type parameters."},
-    {"rust_primitive", "A generic type cannot have any primitive type parameters."},
-    {"cpp_primitiveArray", "List of arrays is not supported."},
-    {"java_primitiveArray", "List of arrays is not supported."},
-    {"ndk_primitiveArray", "List of arrays is not supported."},
-    {"rust_primitiveArray", "List of arrays is not supported."},
-    {"cpp_String", ""},
-    {"java_String", ""},
-    {"ndk_String", ""},
-    {"rust_String", ""},
-    {"cpp_StringArray", "List of arrays is not supported."},
-    {"java_StringArray", "List of arrays is not supported."},
-    {"ndk_StringArray", "List of arrays is not supported."},
-    {"rust_StringArray", "List of arrays is not supported."},
-    {"cpp_IBinder", ""},
-    {"java_IBinder", ""},
-    {"ndk_IBinder", ""},
-    {"rust_IBinder", ""},
-    {"cpp_ParcelFileDescriptor", ""},
-    {"java_ParcelFileDescriptor", ""},
-    {"ndk_ParcelFileDescriptor", ""},
-    {"rust_ParcelFileDescriptor", ""},
-    {"cpp_interface", "List<a.IBar> is not supported."},
-    {"java_interface", "List<a.IBar> is not supported."},
-    {"ndk_interface", "List<a.IBar> is not supported."},
-    {"rust_interface", "List<a.IBar> is not supported."},
-    {"cpp_parcelable", ""},
-    {"java_parcelable", ""},
-    {"ndk_parcelable", ""},
-    {"rust_parcelable", ""},
-    {"cpp_enum", "A generic type cannot have any primitive type parameters."},
-    {"java_enum", "A generic type cannot have any primitive type parameters."},
-    {"ndk_enum", "A generic type cannot have any primitive type parameters."},
-    {"rust_enum", "A generic type cannot have any primitive type parameters."},
-    {"cpp_union", ""},
-    {"java_union", ""},
-    {"ndk_union", ""},
-    {"rust_union", ""},
+struct ExpectedResult {
+  string expected_error;
+  string expected_error_for_nullable;
 };
 
-const std::map<std::string, std::string> kArraySupportExpectations = {
-    {"cpp_primitive", ""},
-    {"java_primitive", ""},
-    {"ndk_primitive", ""},
-    {"rust_primitive", ""},
-    {"cpp_primitiveArray", "Can only have one dimensional arrays."},
-    {"java_primitiveArray", "Can only have one dimensional arrays."},
-    {"ndk_primitiveArray", "Can only have one dimensional arrays."},
-    {"rust_primitiveArray", "Can only have one dimensional arrays."},
-    {"cpp_String", ""},
-    {"java_String", ""},
-    {"ndk_String", ""},
-    {"rust_String", ""},
-    {"cpp_StringArray", "Can only have one dimensional arrays."},
-    {"java_StringArray", "Can only have one dimensional arrays."},
-    {"ndk_StringArray", "Can only have one dimensional arrays."},
-    {"rust_StringArray", "Can only have one dimensional arrays."},
-    {"cpp_IBinder", ""},
-    {"java_IBinder", ""},
-    {"ndk_IBinder", ""},
-    {"rust_IBinder", ""},
-    {"cpp_ParcelFileDescriptor", ""},
-    {"java_ParcelFileDescriptor", ""},
-    {"ndk_ParcelFileDescriptor", ""},
-    {"rust_ParcelFileDescriptor", ""},
-    {"cpp_interface", "Binder type cannot be an array"},
-    {"java_interface", "Binder type cannot be an array"},
-    {"ndk_interface", "Binder type cannot be an array"},
-    {"rust_interface", "Binder type cannot be an array"},
-    {"cpp_parcelable", ""},
-    {"java_parcelable", ""},
-    {"ndk_parcelable", ""},
-    {"rust_parcelable", ""},
-    {"cpp_enum", ""},
-    {"java_enum", ""},
-    {"ndk_enum", ""},
-    {"rust_enum", ""},
-    {"cpp_union", ""},
-    {"java_union", ""},
-    {"ndk_union", ""},
-    {"rust_union", ""},
+const std::map<std::string, ExpectedResult> kListSupportExpectations = {
+    {"cpp_primitive", {"A generic type cannot", "A generic type cannot"}},
+    {"java_primitive", {"A generic type cannot", "A generic type cannot"}},
+    {"ndk_primitive", {"A generic type cannot", "A generic type cannot"}},
+    {"rust_primitive", {"A generic type cannot", "A generic type cannot"}},
+    {"cpp_primitiveArray", {"List of arrays is not supported", "List of arrays is not supported"}},
+    {"java_primitiveArray", {"List of arrays is not supported", "List of arrays is not supported"}},
+    {"ndk_primitiveArray", {"List of arrays is not supported", "List of arrays is not supported"}},
+    {"rust_primitiveArray", {"List of arrays is not supported", "List of arrays is not supported"}},
+    {"cpp_String", {"", ""}},
+    {"java_String", {"", ""}},
+    {"ndk_String", {"", ""}},
+    {"rust_String", {"", ""}},
+    {"cpp_StringArray", {"List of arrays is not supported", "List of arrays is not supported"}},
+    {"java_StringArray", {"List of arrays is not supported", "List of arrays is not supported"}},
+    {"ndk_StringArray", {"List of arrays is not supported", "List of arrays is not supported"}},
+    {"rust_StringArray", {"List of arrays is not supported", "List of arrays is not supported"}},
+    {"cpp_IBinder", {"", ""}},
+    {"java_IBinder", {"", ""}},
+    {"ndk_IBinder", {"", ""}},
+    {"rust_IBinder", {"", ""}},
+    {"cpp_ParcelFileDescriptor", {"", ""}},
+    {"java_ParcelFileDescriptor", {"", ""}},
+    {"ndk_ParcelFileDescriptor", {"", ""}},
+    {"rust_ParcelFileDescriptor", {"", ""}},
+    {"cpp_interface", {"List<a.IBar> is not supported", "List<a.IBar> is not supported"}},
+    {"java_interface", {"List<a.IBar> is not supported", "List<a.IBar> is not supported"}},
+    {"ndk_interface", {"List<a.IBar> is not supported", "List<a.IBar> is not supported"}},
+    {"rust_interface", {"List<a.IBar> is not supported", "List<a.IBar> is not supported"}},
+    {"cpp_parcelable", {"", ""}},
+    {"java_parcelable", {"", ""}},
+    {"ndk_parcelable", {"", ""}},
+    {"rust_parcelable", {"", ""}},
+    {"cpp_enum", {"A generic type cannot", "A generic type cannot"}},
+    {"java_enum", {"A generic type cannot", "A generic type cannot"}},
+    {"ndk_enum", {"A generic type cannot", "A generic type cannot"}},
+    {"rust_enum", {"A generic type cannot", "A generic type cannot"}},
+    {"cpp_union", {"", ""}},
+    {"java_union", {"", ""}},
+    {"ndk_union", {"", ""}},
+    {"rust_union", {"", ""}},
 };
 
-class AidlTypeParamTest : public testing::TestWithParam<std::tuple<Options::Language, TypeParam>> {
+const std::map<std::string, ExpectedResult> kArraySupportExpectations = {
+    {"cpp_primitive", {"", ""}},
+    {"java_primitive", {"", ""}},
+    {"ndk_primitive", {"", ""}},
+    {"rust_primitive", {"", ""}},
+    {"cpp_primitiveArray", {"Can only have one dimensional", "Can only have one dimensional"}},
+    {"java_primitiveArray", {"Can only have one dimensional", "Can only have one dimensional"}},
+    {"ndk_primitiveArray", {"Can only have one dimensional", "Can only have one dimensional"}},
+    {"rust_primitiveArray", {"Can only have one dimensional", "Can only have one dimensional"}},
+    {"cpp_String", {"", ""}},
+    {"java_String", {"", ""}},
+    {"ndk_String", {"", ""}},
+    {"rust_String", {"", ""}},
+    {"cpp_StringArray", {"Can only have one dimensional", "Can only have one dimensional"}},
+    {"java_StringArray", {"Can only have one dimensional", "Can only have one dimensional"}},
+    {"ndk_StringArray", {"Can only have one dimensional", "Can only have one dimensional"}},
+    {"rust_StringArray", {"Can only have one dimensional", "Can only have one dimensional"}},
+    {"cpp_IBinder", {"", ""}},
+    {"java_IBinder", {"", ""}},
+    {"ndk_IBinder", {"", ""}},
+    {"rust_IBinder", {"", ""}},
+    {"cpp_ParcelFileDescriptor", {"", ""}},
+    {"java_ParcelFileDescriptor", {"", ""}},
+    {"ndk_ParcelFileDescriptor", {"", ""}},
+    {"rust_ParcelFileDescriptor", {"", ""}},
+    {"cpp_interface", {"Binder type cannot be an array", "Binder type cannot be an array"}},
+    {"java_interface", {"Binder type cannot be an array", "Binder type cannot be an array"}},
+    {"ndk_interface", {"Binder type cannot be an array", "Binder type cannot be an array"}},
+    {"rust_interface", {"Binder type cannot be an array", "Binder type cannot be an array"}},
+    {"cpp_parcelable", {"", ""}},
+    {"java_parcelable", {"", ""}},
+    {"ndk_parcelable", {"", ""}},
+    {"rust_parcelable", {"", ""}},
+    {"cpp_enum", {"", ""}},
+    {"java_enum", {"", ""}},
+    {"ndk_enum", {"", ""}},
+    {"rust_enum", {"", ""}},
+    {"cpp_union", {"", ""}},
+    {"java_union", {"", ""}},
+    {"ndk_union", {"", ""}},
+    {"rust_union", {"", ""}},
+};
+
+const std::map<std::string, ExpectedResult> kFieldSupportExpectations = {
+    {"cpp_primitive", {"", "cannot get nullable annotation"}},
+    {"java_primitive", {"", "cannot get nullable annotation"}},
+    {"ndk_primitive", {"", "cannot get nullable annotation"}},
+    {"rust_primitive", {"", "cannot get nullable annotation"}},
+    {"cpp_primitiveArray", {"", ""}},
+    {"java_primitiveArray", {"", ""}},
+    {"ndk_primitiveArray", {"", ""}},
+    {"rust_primitiveArray", {"", ""}},
+    {"cpp_String", {"", ""}},
+    {"java_String", {"", ""}},
+    {"ndk_String", {"", ""}},
+    {"rust_String", {"", ""}},
+    {"cpp_StringArray", {"", ""}},
+    {"java_StringArray", {"", ""}},
+    {"ndk_StringArray", {"", ""}},
+    {"rust_StringArray", {"", ""}},
+    {"cpp_IBinder", {"", ""}},
+    {"java_IBinder", {"", ""}},
+    {"ndk_IBinder", {"", ""}},
+    {"rust_IBinder", {"", ""}},
+    {"cpp_ParcelFileDescriptor", {"", ""}},
+    {"java_ParcelFileDescriptor", {"", ""}},
+    {"ndk_ParcelFileDescriptor", {"", ""}},
+    {"rust_ParcelFileDescriptor", {"", ""}},
+    {"cpp_interface", {"", ""}},
+    {"java_interface", {"", ""}},
+    {"ndk_interface", {"", ""}},
+    {"rust_interface", {"", ""}},
+    {"cpp_parcelable", {"", ""}},
+    {"java_parcelable", {"", ""}},
+    {"ndk_parcelable", {"", ""}},
+    {"rust_parcelable", {"", ""}},
+    {"cpp_enum", {"", "cannot get nullable annotation"}},
+    {"java_enum", {"", "cannot get nullable annotation"}},
+    {"ndk_enum", {"", "cannot get nullable annotation"}},
+    {"rust_enum", {"", "cannot get nullable annotation"}},
+    {"cpp_union", {"", ""}},
+    {"java_union", {"", ""}},
+    {"ndk_union", {"", ""}},
+    {"rust_union", {"", ""}},
+};
+
+class AidlTypeParamTest
+    : public testing::TestWithParam<std::tuple<Options::Language, TypeParam, bool>> {
  public:
   void Run(const std::string& generic_type_decl,
-           const std::map<std::string, std::string>& expectations) {
+           const std::map<std::string, ExpectedResult>& expectations) {
     const auto& param = GetParam();
     const auto& lang = to_string(std::get<0>(param));
     const auto& kind = std::get<1>(param).kind;
+    const bool nullable = std::get<2>(param);
 
     FakeIoDelegate io;
     io.SetFileContents("a/IBar.aidl", "package a; interface IBar { }");
     io.SetFileContents("a/Enum.aidl", "package a; enum Enum { A }");
     io.SetFileContents("a/Union.aidl", "package a; union Union { int a; }");
+    io.SetFileContents("a/Foo.aidl", "package a; parcelable Foo { int a; }");
     std::string decl = fmt::format(generic_type_decl, std::get<1>(param).literal);
-    io.SetFileContents("a/Foo.aidl", "package a; parcelable Foo { " + decl + " f; }");
+    if (nullable) {
+      decl = "@nullable " + decl;
+    }
+    io.SetFileContents("a/Target.aidl", "package a; parcelable Target { " + decl + " f; }");
 
     const auto options =
-        Options::From(fmt::format("aidl -I . --lang={} a/Foo.aidl -o out -h out", lang));
+        Options::From(fmt::format("aidl -I . --lang={} a/Target.aidl -o out -h out", lang));
     CaptureStderr();
     compile_aidl(options, io);
     auto it = expectations.find(lang + "_" + kind);
-    EXPECT_TRUE(it != expectations.end());
+    ASSERT_TRUE(it != expectations.end()) << "missing expectation for " << lang << "_" << kind;
     const string err = GetCapturedStderr();
-    if (it->second.empty()) {
+    const string expected_error =
+        nullable ? it->second.expected_error_for_nullable : it->second.expected_error;
+    if (expected_error.empty()) {
       EXPECT_EQ("", err);
     } else {
-      EXPECT_THAT(err, testing::HasSubstr(it->second));
+      EXPECT_THAT(err, testing::HasSubstr(expected_error));
     }
   }
 };
@@ -4812,9 +4868,13 @@
     AidlTestSuite, AidlTypeParamTest,
     testing::Combine(testing::Values(Options::Language::CPP, Options::Language::JAVA,
                                      Options::Language::NDK, Options::Language::RUST),
-                     testing::ValuesIn(kTypeParams)),
-    [](const testing::TestParamInfo<std::tuple<Options::Language, TypeParam>>& info) {
-      return to_string(std::get<0>(info.param)) + "_" + std::get<1>(info.param).kind;
+                     testing::ValuesIn(kTypeParams), testing::Values(true, false)),
+    [](const testing::TestParamInfo<std::tuple<Options::Language, TypeParam, bool>>& info) {
+      string name = to_string(std::get<0>(info.param)) + "_" + std::get<1>(info.param).kind;
+      if (std::get<2>(info.param)) {
+        name += "_nullable";
+      }
+      return name;
     });
 
 TEST_P(AidlTypeParamTest, ListSupportedTypes) {
@@ -4825,5 +4885,9 @@
   Run("{}[]", kArraySupportExpectations);
 }
 
+TEST_P(AidlTypeParamTest, ParcelableFieldTypes) {
+  Run("{}", kFieldSupportExpectations);
+}
+
 }  // namespace aidl
 }  // namespace android