Annotate remaining field semantics.

PiperOrigin-RevId: 509872256
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 9744204..19bd6b5 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 compiler {
 namespace cpp {
 namespace {
+using Semantic = ::google::protobuf::io::AnnotationCollector::Semantic;
 using Sub = ::google::protobuf::io::Printer::Sub;
 
 std::vector<Sub> Vars(const FieldDescriptor* field, const Options& opts) {
@@ -172,7 +173,8 @@
 
 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 @@
 };
 
 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 79f1015..a91a394 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 <memory>
 #include <string>
+#include <tuple>
 
 #include "absl/container/flat_hash_map.h"
 #include "absl/log/absl_check.h"
@@ -141,10 +142,12 @@
       "    ${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 6eaadef..b4bf2a9 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 @@
       "$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 5bbf298..b15801e 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 @@
 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<size_t> FixedSize(FieldDescriptor::Type type) {
@@ -193,18 +193,10 @@
 };
 
 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::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_),
+  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$();
 
-          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$();
 
-        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");
+    public:
+  )cc");
 }
 
 void RepeatedPrimitive::GenerateInlineAccessorDefinitions(
diff --git a/src/google/protobuf/compiler/cpp/message.cc b/src/google/protobuf/compiler/cpp/message.cc
index 88bb015..a9cb054 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::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 @@
          {"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 feb5a7f..e95ee56 100644
--- a/src/google/protobuf/compiler/cpp/metadata_test.cc
+++ b/src/google/protobuf/compiler/cpp/metadata_test.cc
@@ -177,6 +177,270 @@
   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<int> field_path{FileDescriptorProto::kMessageTypeFieldNumber, 0,
+                              DescriptorProto::kFieldFieldNumber, 0};
+  std::vector<const GeneratedCodeInfo::Annotation*> 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<string, int32> 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<int> field_path{FileDescriptorProto::kMessageTypeFieldNumber, 0,
+                              DescriptorProto::kFieldFieldNumber, 0};
+  std::vector<const GeneratedCodeInfo::Annotation*> 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<int> field_path{FileDescriptorProto::kMessageTypeFieldNumber, 0,
+                              DescriptorProto::kFieldFieldNumber, 0};
+  std::vector<const GeneratedCodeInfo::Annotation*> 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<int> field_path{FileDescriptorProto::kMessageTypeFieldNumber, 0,
+                              DescriptorProto::kFieldFieldNumber, 0};
+  std::vector<const GeneratedCodeInfo::Annotation*> 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<int> field_path{FileDescriptorProto::kMessageTypeFieldNumber, 0,
+                              DescriptorProto::kFieldFieldNumber, 0};
+  std::vector<const GeneratedCodeInfo::Annotation*> 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 @@
     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 @@
     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 @@
     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 @@
   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<int> 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<const GeneratedCodeInfo::Annotation*> 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