From f5f5a1faf1dcc276a06d98ea51ed69c9c480dca8 Mon Sep 17 00:00:00 2001 From: Protobuf Team Bot Date: Wed, 15 Feb 2023 10:30:37 -0800 Subject: [PATCH] Annotate remaining field semantics. PiperOrigin-RevId: 509872256 --- .../cpp/field_generators/enum_field.cc | 13 +- .../cpp/field_generators/map_field.cc | 7 +- .../cpp/field_generators/message_field.cc | 6 +- .../cpp/field_generators/primitive_field.cc | 72 ++-- src/google/protobuf/compiler/cpp/message.cc | 6 +- .../protobuf/compiler/cpp/metadata_test.cc | 321 +++++++++++++++++- 6 files changed, 365 insertions(+), 60 deletions(-) diff --git a/src/google/protobuf/compiler/cpp/field_generators/enum_field.cc b/src/google/protobuf/compiler/cpp/field_generators/enum_field.cc index 9744204030..19bd6b58e4 100644 --- a/src/google/protobuf/compiler/cpp/field_generators/enum_field.cc +++ b/src/google/protobuf/compiler/cpp/field_generators/enum_field.cc @@ -51,6 +51,7 @@ namespace protobuf { namespace compiler { namespace cpp { namespace { +using Semantic = ::google::protobuf::io::AnnotationCollector::Semantic; using Sub = ::google::protobuf::io::Printer::Sub; std::vector Vars(const FieldDescriptor* field, const Options& opts) { @@ -172,7 +173,8 @@ class SingularEnum : public FieldGeneratorBase { void SingularEnum::GenerateAccessorDeclarations(io::Printer* p) const { auto v = p->WithVars( - AnnotatedAccessors(field_, {"", "set_", "_internal_", "_internal_set_"})); + AnnotatedAccessors(field_, {"", "_internal_", "_internal_set_"})); + auto vs = p->WithVars(AnnotatedAccessors(field_, {"set_"}, Semantic::kSet)); p->Emit(R"cc( $DEPRECATED$ $Enum$ $name$() const; $DEPRECATED$ void $set_name$($Enum$ value); @@ -331,9 +333,12 @@ class RepeatedEnum : public FieldGeneratorBase { }; void RepeatedEnum::GenerateAccessorDeclarations(io::Printer* p) const { - auto v = p->WithVars( - AnnotatedAccessors(field_, {"", "set_", "add_", "mutable_", "_internal_", - "_internal_add_", "_internal_mutable_"})); + auto v = p->WithVars(AnnotatedAccessors( + field_, {"", "_internal_", "_internal_add_", "_internal_mutable_"})); + auto vs = + p->WithVars(AnnotatedAccessors(field_, {"set_", "add_"}, Semantic::kSet)); + auto vm = + p->WithVars(AnnotatedAccessors(field_, {"mutable_"}, Semantic::kAlias)); p->Emit(R"cc( public: diff --git a/src/google/protobuf/compiler/cpp/field_generators/map_field.cc b/src/google/protobuf/compiler/cpp/field_generators/map_field.cc index 79f1015c68..a91a3947e7 100644 --- a/src/google/protobuf/compiler/cpp/field_generators/map_field.cc +++ b/src/google/protobuf/compiler/cpp/field_generators/map_field.cc @@ -30,6 +30,7 @@ #include #include +#include #include "absl/container/flat_hash_map.h" #include "absl/log/absl_check.h" @@ -141,10 +142,12 @@ void MapFieldGenerator::GenerateAccessorDeclarations( " ${1$_internal_mutable_$name$$}$();\n" "public:\n" "$deprecated_attr$const ::$proto_ns$::Map< $key_cpp$, $val_cpp$ >&\n" - " ${1$$name$$}$() const;\n" + " ${1$$name$$}$() const;\n", + descriptor_); + format( "$deprecated_attr$::$proto_ns$::Map< $key_cpp$, $val_cpp$ >*\n" " ${1$mutable_$name$$}$();\n", - descriptor_); + std::make_tuple(descriptor_, GeneratedCodeInfo::Annotation::ALIAS)); } void MapFieldGenerator::GenerateInlineAccessorDefinitions( diff --git a/src/google/protobuf/compiler/cpp/field_generators/message_field.cc b/src/google/protobuf/compiler/cpp/field_generators/message_field.cc index 6eaadefefd..b4bf2a91a8 100644 --- a/src/google/protobuf/compiler/cpp/field_generators/message_field.cc +++ b/src/google/protobuf/compiler/cpp/field_generators/message_field.cc @@ -809,9 +809,11 @@ void RepeatedMessageFieldGenerator::GenerateAccessorDeclarations( "$type$* ${1$_internal_add_$name$$}$();\n" "public:\n", descriptor_); + format("$deprecated_attr$const $type$& ${1$$name$$}$(int index) const;\n", + descriptor_); + format("$deprecated_attr$$type$* ${1$add_$name$$}$();\n", + std::make_tuple(descriptor_, GeneratedCodeInfo::Annotation::SET)); format( - "$deprecated_attr$const $type$& ${1$$name$$}$(int index) const;\n" - "$deprecated_attr$$type$* ${1$add_$name$$}$();\n" "$deprecated_attr$const ::$proto_ns$::RepeatedPtrField< $type$ >&\n" " ${1$$name$$}$() const;\n", descriptor_); diff --git a/src/google/protobuf/compiler/cpp/field_generators/primitive_field.cc b/src/google/protobuf/compiler/cpp/field_generators/primitive_field.cc index 5bbf298341..b15801ea51 100644 --- a/src/google/protobuf/compiler/cpp/field_generators/primitive_field.cc +++ b/src/google/protobuf/compiler/cpp/field_generators/primitive_field.cc @@ -56,7 +56,7 @@ namespace { using ::google::protobuf::internal::WireFormat; using ::google::protobuf::internal::WireFormatLite; using Sub = ::google::protobuf::io::Printer::Sub; -using Annotation = ::google::protobuf::GeneratedCodeInfo::Annotation; +using Semantic = ::google::protobuf::io::AnnotationCollector::Semantic; // For encodings with fixed sizes, returns that size in bytes. absl::optional FixedSize(FieldDescriptor::Type type) { @@ -193,18 +193,10 @@ class SingularPrimitive final : public FieldGeneratorBase { }; void SingularPrimitive::GenerateAccessorDeclarations(io::Printer* p) const { + auto v = p->WithVars( + AnnotatedAccessors(field_, {"", "_internal_", "_internal_set_"})); + auto vs = p->WithVars(AnnotatedAccessors(field_, {"set_"}, Semantic::kSet)); p->Emit( - { - Sub("name", p->LookupVar("name")).AnnotatedAs(field_), - Sub("set_name", absl::StrCat("set_", p->LookupVar("name"))) - .AnnotatedAs(field_), - Sub("_internal_name", - absl::StrCat("_internal_", p->LookupVar("name"))) - .AnnotatedAs(field_), - Sub("_internal_set_name", - absl::StrCat("_internal_set_", p->LookupVar("name"))) - .AnnotatedAs(field_), - }, R"cc( $DEPRECATED$ $Type$ $name$() const; $DEPRECATED$ void $set_name$($Type$ value); @@ -397,41 +389,27 @@ void RepeatedPrimitive::GeneratePrivateMembers(io::Printer* p) const { } void RepeatedPrimitive::GenerateAccessorDeclarations(io::Printer* p) const { - p->Emit( - { - Sub("name", p->LookupVar("name")).AnnotatedAs(field_), - Sub("set_name", absl::StrCat("set_", p->LookupVar("name"))) - .AnnotatedAs(field_), - Sub("add_name", absl::StrCat("add_", p->LookupVar("name"))) - .AnnotatedAs(field_), - Sub("mutable_name", absl::StrCat("mutable_", p->LookupVar("name"))) - .AnnotatedAs(field_), - - Sub("_internal_name", - absl::StrCat("_internal_", p->LookupVar("name"))) - .AnnotatedAs(field_), - Sub("_internal_add_name", - absl::StrCat("_internal_add_", p->LookupVar("name"))) - .AnnotatedAs(field_), - Sub("_internal_mutable_name", - absl::StrCat("_internal_mutable_", p->LookupVar("name"))) - .AnnotatedAs(field_), - }, - R"cc( - $DEPRECATED$ $Type$ $name$(int index) const; - $DEPRECATED$ void $set_name$(int index, $Type$ value); - $DEPRECATED$ void $add_name$($Type$ value); - $DEPRECATED$ const $pb$::RepeatedField<$Type$>& $name$() const; - $DEPRECATED$ $pb$::RepeatedField<$Type$>* $mutable_name$(); - - private: - $Type$ $_internal_name$(int index) const; - void $_internal_add_name$($Type$ value); - const $pb$::RepeatedField<$Type$>& $_internal_name$() const; - $pb$::RepeatedField<$Type$>* $_internal_mutable_name$(); - - public: - )cc"); + auto v = p->WithVars(AnnotatedAccessors( + field_, {"", "_internal_", "_internal_add_", "_internal_mutable_"})); + auto vs = + p->WithVars(AnnotatedAccessors(field_, {"set_", "add_"}, Semantic::kSet)); + auto va = + p->WithVars(AnnotatedAccessors(field_, {"mutable_"}, Semantic::kAlias)); + p->Emit(R"cc( + $DEPRECATED$ $Type$ $name$(int index) const; + $DEPRECATED$ void $set_name$(int index, $Type$ value); + $DEPRECATED$ void $add_name$($Type$ value); + $DEPRECATED$ const $pb$::RepeatedField<$Type$>& $name$() const; + $DEPRECATED$ $pb$::RepeatedField<$Type$>* $mutable_name$(); + + private: + $Type$ $_internal_name$(int index) const; + void $_internal_add_name$($Type$ value); + const $pb$::RepeatedField<$Type$>& $_internal_name$() const; + $pb$::RepeatedField<$Type$>* $_internal_mutable_name$(); + + public: + )cc"); } void RepeatedPrimitive::GenerateInlineAccessorDefinitions( diff --git a/src/google/protobuf/compiler/cpp/message.cc b/src/google/protobuf/compiler/cpp/message.cc index 88bb015518..a9cb0545d0 100644 --- a/src/google/protobuf/compiler/cpp/message.cc +++ b/src/google/protobuf/compiler/cpp/message.cc @@ -85,6 +85,7 @@ using ::google::protobuf::internal::WireFormat; using ::google::protobuf::internal::WireFormatLite; using ::google::protobuf::internal::cpp::HasHasbit; using ::google::protobuf::internal::cpp::Utf8CheckMode; +using Semantic = ::google::protobuf::io::AnnotationCollector::Semantic; using Sub = ::google::protobuf::io::Printer::Sub; static constexpr int kNoHasbit = -1; @@ -730,7 +731,10 @@ void MessageGenerator::GenerateFieldAccessorDeclarations(io::Printer* p) { {"clearer", [&] { p->Emit({Sub("clear_name", absl::StrCat("clear_", name)) - .AnnotatedAs(field)}, + .AnnotatedAs({ + field, + Semantic::kSet, + })}, R"cc( $deprecated_attr $void $clear_name$() $impl$; )cc"); diff --git a/src/google/protobuf/compiler/cpp/metadata_test.cc b/src/google/protobuf/compiler/cpp/metadata_test.cc index feb5a7f66c..e95ee566f8 100644 --- a/src/google/protobuf/compiler/cpp/metadata_test.cc +++ b/src/google/protobuf/compiler/cpp/metadata_test.cc @@ -177,6 +177,270 @@ TEST_F(CppMetadataTest, RangeChecksWork) { EXPECT_FALSE(atu::GetAnnotationSubstring(test, annotation).has_value()); } +constexpr absl::string_view kEnumFieldTestFile = R"( + syntax = "proto2"; + package foo; + enum Enum { VALUE = 0; } + message Message { + optional Enum efield = 1; + repeated Enum refield = 2; + } +)"; + +TEST_F(CppMetadataTest, AnnotatesEnumSemantics) { + FileDescriptorProto file; + GeneratedCodeInfo info; + std::string pb_h; + atu::AddFile("test.proto", kEnumFieldTestFile); + EXPECT_TRUE(CaptureMetadata("test.proto", &file, &pb_h, &info, nullptr, + nullptr, nullptr)); + EXPECT_EQ("Message", file.message_type(0).name()); + std::vector field_path{FileDescriptorProto::kMessageTypeFieldNumber, 0, + DescriptorProto::kFieldFieldNumber, 0}; + std::vector annotations; + atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations); + EXPECT_TRUE(!annotations.empty()); + for (const auto* annotation : annotations) { + auto substring = atu::GetAnnotationSubstring(pb_h, *annotation); + ASSERT_TRUE(substring.has_value()); + if (*substring == "efield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE, + annotation->semantic()); + } else if (*substring == "set_efield" || *substring == "clear_efield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET, + annotation->semantic()); + } + } + field_path.back() = 1; + annotations.clear(); + atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations); + EXPECT_TRUE(!annotations.empty()); + for (const auto* annotation : annotations) { + auto substring = atu::GetAnnotationSubstring(pb_h, *annotation); + ASSERT_TRUE(substring.has_value()); + if (*substring == "refield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE, + annotation->semantic()); + } else if (*substring == "set_refield" || *substring == "clear_refield" || + *substring == "add_refield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET, + annotation->semantic()); + } else if (*substring == "mutable_refield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS, + annotation->semantic()); + } + } +} + +constexpr absl::string_view kMapFieldTestFile = R"( + syntax = "proto2"; + package foo; + message Message { + map mfield = 1; + } +)"; + +TEST_F(CppMetadataTest, AnnotatesMapSemantics) { + FileDescriptorProto file; + GeneratedCodeInfo info; + std::string pb_h; + atu::AddFile("test.proto", kMapFieldTestFile); + EXPECT_TRUE(CaptureMetadata("test.proto", &file, &pb_h, &info, nullptr, + nullptr, nullptr)); + EXPECT_EQ("Message", file.message_type(0).name()); + std::vector field_path{FileDescriptorProto::kMessageTypeFieldNumber, 0, + DescriptorProto::kFieldFieldNumber, 0}; + std::vector annotations; + atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations); + EXPECT_TRUE(!annotations.empty()); + for (const auto* annotation : annotations) { + auto substring = atu::GetAnnotationSubstring(pb_h, *annotation); + ASSERT_TRUE(substring.has_value()); + if (*substring == "mfield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE, + annotation->semantic()); + } else if (*substring == "clear_mfield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET, + annotation->semantic()); + } else if (*substring == "mutable_mfield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS, + annotation->semantic()); + } + } +} + +constexpr absl::string_view kPrimFieldTestFile = R"( + syntax = "proto2"; + package foo; + message Message { + optional int32 ifield = 1; + repeated int32 rifield = 2; + } +)"; + +TEST_F(CppMetadataTest, AnnotatesPrimSemantics) { + FileDescriptorProto file; + GeneratedCodeInfo info; + std::string pb_h; + atu::AddFile("test.proto", kPrimFieldTestFile); + EXPECT_TRUE(CaptureMetadata("test.proto", &file, &pb_h, &info, nullptr, + nullptr, nullptr)); + EXPECT_EQ("Message", file.message_type(0).name()); + std::vector field_path{FileDescriptorProto::kMessageTypeFieldNumber, 0, + DescriptorProto::kFieldFieldNumber, 0}; + std::vector annotations; + atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations); + EXPECT_TRUE(!annotations.empty()); + for (const auto* annotation : annotations) { + auto substring = atu::GetAnnotationSubstring(pb_h, *annotation); + ASSERT_TRUE(substring.has_value()); + if (*substring == "ifield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE, + annotation->semantic()); + } else if (*substring == "set_ifield" || *substring == "clear_ifield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET, + annotation->semantic()); + } + } + field_path.back() = 1; + annotations.clear(); + atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations); + EXPECT_TRUE(!annotations.empty()); + for (const auto* annotation : annotations) { + auto substring = atu::GetAnnotationSubstring(pb_h, *annotation); + ASSERT_TRUE(substring.has_value()); + if (*substring == "rifield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE, + annotation->semantic()); + } else if (*substring == "set_rifield" || *substring == "clear_rifield" || + *substring == "add_rifield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET, + annotation->semantic()); + } else if (*substring == "mutable_rifield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS, + annotation->semantic()); + } + } +} + +constexpr absl::string_view kCordFieldTestFile = R"( + syntax = "proto2"; + package foo; + message Message { + optional string sfield = 1 [ctype = CORD]; + repeated string rsfield = 2 [ctype = CORD]; + } +)"; + +TEST_F(CppMetadataTest, AnnotatesCordSemantics) { + FileDescriptorProto file; + GeneratedCodeInfo info; + std::string pb_h; + atu::AddFile("test.proto", kCordFieldTestFile); + EXPECT_TRUE(CaptureMetadata("test.proto", &file, &pb_h, &info, nullptr, + nullptr, nullptr)); + EXPECT_EQ("Message", file.message_type(0).name()); + std::vector field_path{FileDescriptorProto::kMessageTypeFieldNumber, 0, + DescriptorProto::kFieldFieldNumber, 0}; + std::vector annotations; + atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations); + EXPECT_TRUE(!annotations.empty()); + for (const auto* annotation : annotations) { + auto substring = atu::GetAnnotationSubstring(pb_h, *annotation); + ASSERT_TRUE(substring.has_value()); + if (*substring == "sfield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE, + annotation->semantic()); + } else if (*substring == "set_sfield" || *substring == "clear_sfield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET, + annotation->semantic()); + } else if (*substring == "mutable_sfield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS, + annotation->semantic()); + } + } + field_path.back() = 1; + annotations.clear(); + atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations); + EXPECT_TRUE(!annotations.empty()); + for (const auto* annotation : annotations) { + auto substring = atu::GetAnnotationSubstring(pb_h, *annotation); + ASSERT_TRUE(substring.has_value()); + if (*substring == "rsfield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE, + annotation->semantic()); + } else if (*substring == "set_rsfield" || *substring == "clear_rsfield" || + *substring == "add_rsfield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET, + annotation->semantic()); + } else if (*substring == "mutable_rsfield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS, + annotation->semantic()); + } + } +} + +constexpr absl::string_view kStringPieceFieldTestFile = R"( + syntax = "proto2"; + package foo; + message Message { + optional string sfield = 1 [ctype = STRING_PIECE]; + repeated string rsfield = 2 [ctype = STRING_PIECE]; + } +)"; + +TEST_F(CppMetadataTest, AnnotatesStringPieceSemantics) { + FileDescriptorProto file; + GeneratedCodeInfo info; + std::string pb_h; + atu::AddFile("test.proto", kStringPieceFieldTestFile); + EXPECT_TRUE(CaptureMetadata("test.proto", &file, &pb_h, &info, nullptr, + nullptr, nullptr)); + EXPECT_EQ("Message", file.message_type(0).name()); + std::vector field_path{FileDescriptorProto::kMessageTypeFieldNumber, 0, + DescriptorProto::kFieldFieldNumber, 0}; + std::vector annotations; + atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations); + EXPECT_TRUE(!annotations.empty()); + for (const auto* annotation : annotations) { + auto substring = atu::GetAnnotationSubstring(pb_h, *annotation); + ASSERT_TRUE(substring.has_value()); + if (*substring == "sfield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE, + annotation->semantic()); + } else if (*substring == "set_sfield" || *substring == "set_alias_sfield" || + *substring == "clear_sfield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET, + annotation->semantic()); + } else if (*substring == "mutable_sfield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS, + annotation->semantic()); + } + } + field_path.back() = 1; + annotations.clear(); + atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations); + EXPECT_TRUE(!annotations.empty()); + for (const auto* annotation : annotations) { + auto substring = atu::GetAnnotationSubstring(pb_h, *annotation); + ASSERT_TRUE(substring.has_value()); + if (*substring == "rsfield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE, + annotation->semantic()); + } else if (*substring == "set_rsfield" || + *substring == "set_alias_rsfield" || + *substring == "clear_rsfield" || + *substring == "add_alias_rsfield" || + *substring == "add_rsfield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET, + annotation->semantic()); + } else if (*substring == "mutable_rsfield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS, + annotation->semantic()); + } + } +} + constexpr absl::string_view kStringFieldTestFile = R"( syntax = "proto2"; package foo; @@ -205,7 +469,7 @@ TEST_F(CppMetadataTest, AnnotatesStringSemantics) { if (*substring == "sfield") { EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE, annotation->semantic()); - } else if (*substring == "set_sfield") { + } else if (*substring == "set_sfield" || *substring == "clear_sfield") { EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET, annotation->semantic()); } else if (*substring == "mutable_sfield") { @@ -223,7 +487,8 @@ TEST_F(CppMetadataTest, AnnotatesStringSemantics) { if (*substring == "rsfield") { EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE, annotation->semantic()); - } else if (*substring == "set_rsfield") { + } else if (*substring == "set_rsfield" || *substring == "clear_rsfield" || + *substring == "add_rsfield") { EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET, annotation->semantic()); } else if (*substring == "mutable_rsfield") { @@ -265,6 +530,9 @@ TEST_F(CppMetadataTest, AnnotatesMessageSemantics) { if (*substring == "mfield") { EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE, annotation->semantic()); + } else if (*substring == "clear_mfield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET, + annotation->semantic()); } else if (*substring == "mutable_mfield") { EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS, annotation->semantic()); @@ -277,15 +545,60 @@ TEST_F(CppMetadataTest, AnnotatesMessageSemantics) { for (const auto* annotation : annotations) { auto substring = atu::GetAnnotationSubstring(pb_h, *annotation); ASSERT_TRUE(substring.has_value()); - if (substring == "rmfield") { + if (*substring == "rmfield") { EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE, annotation->semantic()); - } else if (substring == "mutable_rmfield") { + } else if (*substring == "add_rmfield" || *substring == "clear_rmfield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET, + annotation->semantic()); + } else if (*substring == "mutable_rmfield") { EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS, annotation->semantic()); } } } + +constexpr absl::string_view kLazyMessageFieldTestFile = R"( + syntax = "proto2"; + package foo; + message SMessage { } + message Message { + optional SMessage mfield = 1 [lazy=true]; + } +)"; + +TEST_F(CppMetadataTest, AnnotatesLazyMessageSemantics) { + FileDescriptorProto file; + GeneratedCodeInfo info; + std::string pb_h; + atu::AddFile("test.proto", kLazyMessageFieldTestFile); + EXPECT_TRUE(CaptureMetadata("test.proto", &file, &pb_h, &info, nullptr, + nullptr, nullptr)); + EXPECT_EQ("Message", file.message_type(1).name()); + std::vector field_path; + field_path.push_back(FileDescriptorProto::kMessageTypeFieldNumber); + field_path.push_back(1); + field_path.push_back(DescriptorProto::kFieldFieldNumber); + field_path.push_back(0); + std::vector annotations; + atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations); + EXPECT_TRUE(!annotations.empty()); + for (const auto* annotation : annotations) { + auto substring = atu::GetAnnotationSubstring(pb_h, *annotation); + ASSERT_TRUE(substring.has_value()); + if (*substring == "mfield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE, + annotation->semantic()); + } else if (*substring == "mutable_mfield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS, + annotation->semantic()); + } else if (*substring == "set_encoded_mfield" || + *substring == "clear_mfield") { + EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET, + annotation->semantic()); + } + } +} } // namespace } // namespace cpp } // namespace compiler