From 34585e9d2f3cb74aa700b0b9ef3dc78b4168e740 Mon Sep 17 00:00:00 2001
From: Protobuf Team Bot <protobuf-github-bot@google.com>
Date: Mon, 21 Aug 2023 06:30:02 -0700
Subject: [PATCH] Clean up of rust messagegenerator and accessor generators.

It ended up being odd to construct the AccessorGenerators ahead of time given some specific Context<>: since each of the three methods on AccessorGenerators demands a different Context than the initial one so they shouldn't hold that Context anyway. It ended up just being a spooky parallel array to the Fields with an unenforced invariant that all methods would be called with a new Context<> but the same underlying FieldDescriptor later.

PiperOrigin-RevId: 558770643
---
 src/google/protobuf/compiler/rust/BUILD.bazel |   6 +-
 .../rust/accessors/accessor_generator.h       | 123 +++++++++++++++
 .../compiler/rust/accessors/accessors.cc      |  44 ++++--
 .../compiler/rust/accessors/accessors.h       |  57 +------
 .../compiler/rust/accessors/singular_bytes.cc | 130 +++++++---------
 .../rust/accessors/singular_message.cc        |  47 +++---
 .../rust/accessors/singular_scalar.cc         | 144 ++++++++----------
 .../rust/accessors/unsupported_field.cc       |  51 +++++++
 .../protobuf/compiler/rust/generator.cc       |   5 +-
 src/google/protobuf/compiler/rust/message.cc  |  42 +----
 src/google/protobuf/compiler/rust/message.h   |  16 +-
 11 files changed, 370 insertions(+), 295 deletions(-)
 create mode 100644 src/google/protobuf/compiler/rust/accessors/accessor_generator.h
 create mode 100644 src/google/protobuf/compiler/rust/accessors/unsupported_field.cc

diff --git a/src/google/protobuf/compiler/rust/BUILD.bazel b/src/google/protobuf/compiler/rust/BUILD.bazel
index 6936acb8c0..26008b43db 100644
--- a/src/google/protobuf/compiler/rust/BUILD.bazel
+++ b/src/google/protobuf/compiler/rust/BUILD.bazel
@@ -53,8 +53,12 @@ cc_library(
         "accessors/singular_bytes.cc",
         "accessors/singular_message.cc",
         "accessors/singular_scalar.cc",
+        "accessors/unsupported_field.cc",
+    ],
+    hdrs = [
+      "accessors/accessors.h",
+      "accessors/accessor_generator.h",
     ],
-    hdrs = ["accessors/accessors.h"],
     copts = COPTS,
     include_prefix = "google/protobuf/compiler/rust",
     deps = [
diff --git a/src/google/protobuf/compiler/rust/accessors/accessor_generator.h b/src/google/protobuf/compiler/rust/accessors/accessor_generator.h
new file mode 100644
index 0000000000..87e8ed886a
--- /dev/null
+++ b/src/google/protobuf/compiler/rust/accessors/accessor_generator.h
@@ -0,0 +1,123 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google LLC. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef GOOGLE_PROTOBUF_COMPILER_RUST_ACCESSORS_ACCESSOR_GENERATOR_H__
+#define GOOGLE_PROTOBUF_COMPILER_RUST_ACCESSORS_ACCESSOR_GENERATOR_H__
+
+#include <memory>
+
+#include "absl/log/absl_check.h"
+#include "google/protobuf/compiler/rust/context.h"
+#include "google/protobuf/descriptor.h"
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+namespace rust {
+
+class AccessorGenerator {
+ public:
+  AccessorGenerator() = default;
+  virtual ~AccessorGenerator() = default;
+
+  AccessorGenerator(const AccessorGenerator &) = delete;
+  AccessorGenerator(AccessorGenerator &&) = delete;
+  AccessorGenerator &operator=(const AccessorGenerator &) = delete;
+  AccessorGenerator &operator=(AccessorGenerator &&) = delete;
+
+  // Constructs a generator for the given field.
+  //
+  // Returns `nullptr` if there is no known generator for this field.
+  static std::unique_ptr<AccessorGenerator> For(Context<FieldDescriptor> field);
+
+  void GenerateMsgImpl(Context<FieldDescriptor> field) const {
+    InMsgImpl(field);
+  }
+  void GenerateExternC(Context<FieldDescriptor> field) const {
+    InExternC(field);
+  }
+  void GenerateThunkCc(Context<FieldDescriptor> field) const {
+    ABSL_CHECK(field.is_cpp());
+    InThunkCc(field);
+  }
+
+ private:
+  // Note: the virtual functions are duplicated as non-virtual public functions,
+  // so that we can customize prologue and epilogue behavior for these
+  // functions. For example, consider calling `field.printer.WithVars()` as a
+  // prologue to inject variables automatically.
+
+  // Called inside the main inherent `impl Msg {}` block.
+  virtual void InMsgImpl(Context<FieldDescriptor> field) const {}
+
+  // Called inside of a message's `extern "C" {}` block.
+  virtual void InExternC(Context<FieldDescriptor> field) const {}
+
+  // Called inside of an `extern "C" {}` block in the  `.thunk.cc` file, if such
+  // a file is being generated.
+  virtual void InThunkCc(Context<FieldDescriptor> field) const {}
+};
+
+class SingularScalar final : public AccessorGenerator {
+ public:
+  ~SingularScalar() override = default;
+  void InMsgImpl(Context<FieldDescriptor> field) const override;
+  void InExternC(Context<FieldDescriptor> field) const override;
+  void InThunkCc(Context<FieldDescriptor> field) const override;
+};
+
+class SingularBytes final : public AccessorGenerator {
+ public:
+  ~SingularBytes() override = default;
+  void InMsgImpl(Context<FieldDescriptor> field) const override;
+  void InExternC(Context<FieldDescriptor> field) const override;
+  void InThunkCc(Context<FieldDescriptor> field) const override;
+};
+
+class SingularMessage final : public AccessorGenerator {
+ public:
+  ~SingularMessage() override = default;
+  void InMsgImpl(Context<FieldDescriptor> field) const override;
+  void InExternC(Context<FieldDescriptor> field) const override;
+  void InThunkCc(Context<FieldDescriptor> field) const override;
+};
+
+class UnsupportedField final : public AccessorGenerator {
+ public:
+  ~UnsupportedField() override = default;
+  void InMsgImpl(Context<FieldDescriptor> field) const override;
+};
+
+}  // namespace rust
+}  // namespace compiler
+}  // namespace protobuf
+}  // namespace google
+
+#endif  // GOOGLE_PROTOBUF_COMPILER_RUST_ACCESSORS_ACCESSOR_GENERATOR_H__
diff --git a/src/google/protobuf/compiler/rust/accessors/accessors.cc b/src/google/protobuf/compiler/rust/accessors/accessors.cc
index d0b5ffaa39..1ca22f1dd6 100644
--- a/src/google/protobuf/compiler/rust/accessors/accessors.cc
+++ b/src/google/protobuf/compiler/rust/accessors/accessors.cc
@@ -32,6 +32,7 @@
 
 #include <memory>
 
+#include "google/protobuf/compiler/rust/accessors/accessor_generator.h"
 #include "google/protobuf/compiler/rust/context.h"
 #include "google/protobuf/descriptor.h"
 #include "google/protobuf/descriptor.pb.h"
@@ -40,15 +41,22 @@ namespace google {
 namespace protobuf {
 namespace compiler {
 namespace rust {
-std::unique_ptr<AccessorGenerator> AccessorGenerator::For(
-    Context<FieldDescriptor> field) {
+
+namespace {
+
+std::unique_ptr<AccessorGenerator> AccessorGeneratorFor(
+    const FieldDescriptor& desc) {
   // We do not support [ctype=FOO] (used to set the field type in C++ to
   // cord or string_piece) in V0 API.
-  if (field.desc().options().has_ctype()) {
-    return nullptr;
+  if (desc.options().has_ctype()) {
+    return std::make_unique<UnsupportedField>();
+  }
+
+  if (desc.is_repeated()) {
+    return std::make_unique<UnsupportedField>();
   }
 
-  switch (field.desc().type()) {
+  switch (desc.type()) {
     case FieldDescriptor::TYPE_INT32:
     case FieldDescriptor::TYPE_INT64:
     case FieldDescriptor::TYPE_FIXED32:
@@ -62,19 +70,31 @@ std::unique_ptr<AccessorGenerator> AccessorGenerator::For(
     case FieldDescriptor::TYPE_FLOAT:
     case FieldDescriptor::TYPE_DOUBLE:
     case FieldDescriptor::TYPE_BOOL:
-      if (field.desc().is_repeated()) return nullptr;
-      return ForSingularScalar(field);
+      return std::make_unique<SingularScalar>();
     case FieldDescriptor::TYPE_BYTES:
-      if (field.desc().is_repeated()) return nullptr;
-      return ForSingularBytes(field);
+      return std::make_unique<SingularBytes>();
     case FieldDescriptor::TYPE_MESSAGE:
-      if (field.desc().is_repeated()) return nullptr;
-      return ForSingularMessage(field);
+      return std::make_unique<SingularMessage>();
 
     default:
-      return nullptr;
+      return std::make_unique<UnsupportedField>();
   }
 }
+
+}  // namespace
+
+void GenerateAccessorMsgImpl(Context<FieldDescriptor> field) {
+  AccessorGeneratorFor(field.desc())->GenerateMsgImpl(field);
+}
+
+void GenerateAccessorExternC(Context<FieldDescriptor> field) {
+  AccessorGeneratorFor(field.desc())->GenerateExternC(field);
+}
+
+void GenerateAccessorThunkCc(Context<FieldDescriptor> field) {
+  AccessorGeneratorFor(field.desc())->GenerateThunkCc(field);
+}
+
 }  // namespace rust
 }  // namespace compiler
 }  // namespace protobuf
diff --git a/src/google/protobuf/compiler/rust/accessors/accessors.h b/src/google/protobuf/compiler/rust/accessors/accessors.h
index 1b4eb7b13f..0fbb09617d 100644
--- a/src/google/protobuf/compiler/rust/accessors/accessors.h
+++ b/src/google/protobuf/compiler/rust/accessors/accessors.h
@@ -31,9 +31,6 @@
 #ifndef GOOGLE_PROTOBUF_COMPILER_RUST_ACCESSORS_ACCESSORS_H__
 #define GOOGLE_PROTOBUF_COMPILER_RUST_ACCESSORS_ACCESSORS_H__
 
-#include <memory>
-
-#include "absl/log/absl_check.h"
 #include "google/protobuf/compiler/rust/context.h"
 #include "google/protobuf/descriptor.h"
 
@@ -42,57 +39,9 @@ namespace protobuf {
 namespace compiler {
 namespace rust {
 
-class AccessorGenerator {
- public:
-  AccessorGenerator() = default;
-  virtual ~AccessorGenerator() = default;
-
-  AccessorGenerator(const AccessorGenerator &) = delete;
-  AccessorGenerator(AccessorGenerator &&) = delete;
-  AccessorGenerator &operator=(const AccessorGenerator &) = delete;
-  AccessorGenerator &operator=(AccessorGenerator &&) = delete;
-
-  // Constructs a generator for the given field.
-  //
-  // Returns `nullptr` if there is no known generator for this field.
-  static std::unique_ptr<AccessorGenerator> For(Context<FieldDescriptor> field);
-
-  void GenerateMsgImpl(Context<FieldDescriptor> field) const {
-    InMsgImpl(field);
-  }
-  void GenerateExternC(Context<FieldDescriptor> field) const {
-    InExternC(field);
-  }
-  void GenerateThunkCc(Context<FieldDescriptor> field) const {
-    ABSL_CHECK(field.is_cpp());
-    InThunkCc(field);
-  }
-
- private:
-  // Note: the virtual functions are duplicated as non-virtual public functions,
-  // so that we can customize prologue and epilogue behavior for these
-  // functions. For example, consider calling `field.printer.WithVars()` as a
-  // prologue to inject variables automatically.
-
-  // Called inside the main inherent `impl Msg {}` block.
-  virtual void InMsgImpl(Context<FieldDescriptor> field) const {}
-
-  // Called inside of a message's `extern "C" {}` block.
-  virtual void InExternC(Context<FieldDescriptor> field) const {}
-
-  // Called inside of an `extern "C" {}` block in the  `.thunk.cc` file, if such
-  // a file is being generated.
-  virtual void InThunkCc(Context<FieldDescriptor> field) const {}
-
-  // These static factories are defined in the corresponding implementation
-  // files for each implementation of AccessorGenerator.
-  static std::unique_ptr<AccessorGenerator> ForSingularScalar(
-      Context<FieldDescriptor> field);
-  static std::unique_ptr<AccessorGenerator> ForSingularBytes(
-      Context<FieldDescriptor> field);
-  static std::unique_ptr<AccessorGenerator> ForSingularMessage(
-      Context<FieldDescriptor> field);
-};
+void GenerateAccessorMsgImpl(Context<FieldDescriptor> field);
+void GenerateAccessorExternC(Context<FieldDescriptor> field);
+void GenerateAccessorThunkCc(Context<FieldDescriptor> field);
 
 }  // namespace rust
 }  // namespace compiler
diff --git a/src/google/protobuf/compiler/rust/accessors/singular_bytes.cc b/src/google/protobuf/compiler/rust/accessors/singular_bytes.cc
index 3380fc6ff7..3deb5a61e4 100644
--- a/src/google/protobuf/compiler/rust/accessors/singular_bytes.cc
+++ b/src/google/protobuf/compiler/rust/accessors/singular_bytes.cc
@@ -28,11 +28,9 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-#include <memory>
-
 #include "absl/strings/string_view.h"
 #include "google/protobuf/compiler/cpp/helpers.h"
-#include "google/protobuf/compiler/rust/accessors/accessors.h"
+#include "google/protobuf/compiler/rust/accessors/accessor_generator.h"
 #include "google/protobuf/compiler/rust/context.h"
 #include "google/protobuf/compiler/rust/naming.h"
 #include "google/protobuf/descriptor.h"
@@ -41,24 +39,20 @@ namespace google {
 namespace protobuf {
 namespace compiler {
 namespace rust {
-namespace {
-class SingularBytes final : public AccessorGenerator {
- public:
-  ~SingularBytes() override = default;
 
-  void InMsgImpl(Context<FieldDescriptor> field) const override {
-    field.Emit(
-        {
-            {"field", field.desc().name()},
-            {"hazzer_thunk", Thunk(field, "has")},
-            {"getter_thunk", Thunk(field, "get")},
-            {"setter_thunk", Thunk(field, "set")},
-            {"clearer_thunk", Thunk(field, "clear")},
-            {"getter_opt",
-             [&] {
-               if (!field.desc().is_optional()) return;
-               if (!field.desc().has_presence()) return;
-               field.Emit({}, R"rs(
+void SingularBytes::InMsgImpl(Context<FieldDescriptor> field) const {
+  field.Emit(
+      {
+          {"field", field.desc().name()},
+          {"hazzer_thunk", Thunk(field, "has")},
+          {"getter_thunk", Thunk(field, "get")},
+          {"setter_thunk", Thunk(field, "set")},
+          {"clearer_thunk", Thunk(field, "clear")},
+          {"getter_opt",
+           [&] {
+             if (!field.desc().is_optional()) return;
+             if (!field.desc().has_presence()) return;
+             field.Emit({}, R"rs(
                   pub fn $field$_opt(&self) -> Option<&[u8]> {
                     if !unsafe { $hazzer_thunk$(self.msg) } {
                       return None;
@@ -67,9 +61,9 @@ class SingularBytes final : public AccessorGenerator {
                       Some($getter_thunk$(self.msg).as_ref())
                     }
                   })rs");
-             }},
-        },
-        R"rs(
+           }},
+      },
+      R"rs(
           pub fn r#$field$(&self) -> &[u8] {
             unsafe { $getter_thunk$(self.msg).as_ref() }
           }
@@ -86,65 +80,59 @@ class SingularBytes final : public AccessorGenerator {
             }
           }
         )rs");
-  }
+}
 
-  void InExternC(Context<FieldDescriptor> field) const override {
-    field.Emit({{"hazzer_thunk", Thunk(field, "has")},
-                {"getter_thunk", Thunk(field, "get")},
-                {"setter_thunk", Thunk(field, "set")},
-                {"clearer_thunk", Thunk(field, "clear")},
-                {"hazzer",
-                 [&] {
-                   if (field.desc().has_presence()) {
-                     field.Emit(R"rs(
+void SingularBytes::InExternC(Context<FieldDescriptor> field) const {
+  field.Emit({{"hazzer_thunk", Thunk(field, "has")},
+              {"getter_thunk", Thunk(field, "get")},
+              {"setter_thunk", Thunk(field, "set")},
+              {"clearer_thunk", Thunk(field, "clear")},
+              {"hazzer",
+               [&] {
+                 if (field.desc().has_presence()) {
+                   field.Emit(R"rs(
           fn $hazzer_thunk$(raw_msg: $pbi$::RawMessage) -> bool;
         )rs");
-                   }
-                 }}},
-               R"rs(
+                 }
+               }}},
+             R"rs(
           $hazzer$
           fn $getter_thunk$(raw_msg: $pbi$::RawMessage) -> $pbi$::PtrAndLen;
           fn $setter_thunk$(raw_msg: $pbi$::RawMessage, val: *const u8, len: usize);
           fn $clearer_thunk$(raw_msg: $pbi$::RawMessage);
         )rs");
-  }
+}
 
-  void InThunkCc(Context<FieldDescriptor> field) const override {
-    field.Emit({{"field", field.desc().name()},
-                {"QualifiedMsg",
-                 cpp::QualifiedClassName(field.desc().containing_type())},
-                {"hazzer_thunk", Thunk(field, "has")},
-                {"getter_thunk", Thunk(field, "get")},
-                {"setter_thunk", Thunk(field, "set")},
-                {"clearer_thunk", Thunk(field, "clear")},
-                {"hazzer",
-                 [&] {
-                   if (field.desc().has_presence()) {
-                     field.Emit(R"cc(
-                       bool $hazzer_thunk$($QualifiedMsg$* msg) {
-                         return msg->has_$field$();
-                       })cc");
-                   }
-                 }}},
-               R"cc(
-                 $hazzer$;
-                 ::google::protobuf::rust_internal::PtrAndLen $getter_thunk$($QualifiedMsg$* msg) {
-                   absl::string_view val = msg->$field$();
-                   return google::protobuf::rust_internal::PtrAndLen(val.data(), val.size());
+void SingularBytes::InThunkCc(Context<FieldDescriptor> field) const {
+  field.Emit({{"field", field.desc().name()},
+              {"QualifiedMsg",
+               cpp::QualifiedClassName(field.desc().containing_type())},
+              {"hazzer_thunk", Thunk(field, "has")},
+              {"getter_thunk", Thunk(field, "get")},
+              {"setter_thunk", Thunk(field, "set")},
+              {"clearer_thunk", Thunk(field, "clear")},
+              {"hazzer",
+               [&] {
+                 if (field.desc().has_presence()) {
+                   field.Emit(R"cc(
+                     bool $hazzer_thunk$($QualifiedMsg$* msg) {
+                       return msg->has_$field$();
+                     })cc");
                  }
-                 void $setter_thunk$($QualifiedMsg$* msg, const char* ptr, ::std::size_t size) {
-                   msg->set_$field$(absl::string_view(ptr, size));
-                 }
-                 void $clearer_thunk$($QualifiedMsg$* msg) { msg->clear_$field$(); }
-               )cc");
-  }
-};
-}  // namespace
-
-std::unique_ptr<AccessorGenerator> AccessorGenerator::ForSingularBytes(
-    Context<FieldDescriptor> field) {
-  return std::make_unique<SingularBytes>();
+               }}},
+             R"cc(
+               $hazzer$;
+               ::google::protobuf::rust_internal::PtrAndLen $getter_thunk$($QualifiedMsg$* msg) {
+                 absl::string_view val = msg->$field$();
+                 return google::protobuf::rust_internal::PtrAndLen(val.data(), val.size());
+               }
+               void $setter_thunk$($QualifiedMsg$* msg, const char* ptr, ::std::size_t size) {
+                 msg->set_$field$(absl::string_view(ptr, size));
+               }
+               void $clearer_thunk$($QualifiedMsg$* msg) { msg->clear_$field$(); }
+             )cc");
 }
+
 }  // namespace rust
 }  // namespace compiler
 }  // namespace protobuf
diff --git a/src/google/protobuf/compiler/rust/accessors/singular_message.cc b/src/google/protobuf/compiler/rust/accessors/singular_message.cc
index c10fb086f7..c939714639 100644
--- a/src/google/protobuf/compiler/rust/accessors/singular_message.cc
+++ b/src/google/protobuf/compiler/rust/accessors/singular_message.cc
@@ -29,53 +29,42 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "absl/strings/string_view.h"
-#include "google/protobuf/compiler/rust/accessors/accessors.h"
+#include "google/protobuf/compiler/rust/accessors/accessor_generator.h"
 #include "google/protobuf/compiler/rust/context.h"
-#include "google/protobuf/compiler/rust/naming.h"
 #include "google/protobuf/descriptor.h"
 
 namespace google {
 namespace protobuf {
 namespace compiler {
 namespace rust {
-namespace {
-class SingularMessage final : public AccessorGenerator {
- public:
-  ~SingularMessage() override = default;
 
-  void InMsgImpl(Context<FieldDescriptor> field) const override {
-    field.Emit(
-        {
-            {"field", field.desc().name()},
-        },
-        R"rs(
+void SingularMessage::InMsgImpl(Context<FieldDescriptor> field) const {
+  field.Emit(
+      {
+          {"field", field.desc().name()},
+      },
+      R"rs(
           // inMsgImpl
           pub fn r#$field$(&self) -> $Msg$View {
             $Msg$View { msg: self.msg, _phantom: std::marker::PhantomData }
           }
         )rs");
-  }
+}
 
-  void InExternC(Context<FieldDescriptor> field) const override {
-    field.Emit({},
-               R"rs(
+void SingularMessage::InExternC(Context<FieldDescriptor> field) const {
+  field.Emit({},
+             R"rs(
                  // inExternC
                )rs");
-  }
-
-  void InThunkCc(Context<FieldDescriptor> field) const override {
-    field.Emit({},
-               R"cc(
-                 // inThunkCC
-               )cc");
-  }
-};
-}  // namespace
+}
 
-std::unique_ptr<AccessorGenerator> AccessorGenerator::ForSingularMessage(
-    Context<FieldDescriptor> field) {
-  return std::make_unique<SingularMessage>();
+void SingularMessage::InThunkCc(Context<FieldDescriptor> field) const {
+  field.Emit({},
+             R"cc(
+               // inThunkCC
+             )cc");
 }
+
 }  // namespace rust
 }  // namespace compiler
 }  // namespace protobuf
diff --git a/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc b/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc
index 36ddcdb39c..6be8f44eef 100644
--- a/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc
+++ b/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc
@@ -28,11 +28,9 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-#include <memory>
-
 #include "absl/strings/string_view.h"
 #include "google/protobuf/compiler/cpp/helpers.h"
-#include "google/protobuf/compiler/rust/accessors/accessors.h"
+#include "google/protobuf/compiler/rust/accessors/accessor_generator.h"
 #include "google/protobuf/compiler/rust/context.h"
 #include "google/protobuf/compiler/rust/naming.h"
 #include "google/protobuf/descriptor.h"
@@ -41,30 +39,26 @@ namespace google {
 namespace protobuf {
 namespace compiler {
 namespace rust {
-namespace {
-class SingularScalar final : public AccessorGenerator {
- public:
-  ~SingularScalar() override = default;
 
-  void InMsgImpl(Context<FieldDescriptor> field) const override {
-    field.Emit(
-        {
-            {"field", field.desc().name()},
-            {"Scalar", PrimitiveRsTypeName(field)},
-            {"hazzer_thunk", Thunk(field, "has")},
-            {"getter",
-             [&] {
-               field.Emit({}, R"rs(
+void SingularScalar::InMsgImpl(Context<FieldDescriptor> field) const {
+  field.Emit(
+      {
+          {"field", field.desc().name()},
+          {"Scalar", PrimitiveRsTypeName(field)},
+          {"hazzer_thunk", Thunk(field, "has")},
+          {"getter",
+           [&] {
+             field.Emit({}, R"rs(
                   pub fn r#$field$(&self) -> $Scalar$ {
                     unsafe { $getter_thunk$(self.msg) }
                   }
                 )rs");
-             }},
-            {"getter_opt",
-             [&] {
-               if (!field.desc().is_optional()) return;
-               if (!field.desc().has_presence()) return;
-               field.Emit({}, R"rs(
+           }},
+          {"getter_opt",
+           [&] {
+             if (!field.desc().is_optional()) return;
+             if (!field.desc().has_presence()) return;
+             field.Emit({}, R"rs(
                   pub fn r#$field$_opt(&self) -> Option<$Scalar$> {
                     if !unsafe { $hazzer_thunk$(self.msg) } {
                       return None;
@@ -72,12 +66,12 @@ class SingularScalar final : public AccessorGenerator {
                     Some(unsafe { $getter_thunk$(self.msg) })
                   }
                   )rs");
-             }},
-            {"getter_thunk", Thunk(field, "get")},
-            {"setter_thunk", Thunk(field, "set")},
-            {"clearer_thunk", Thunk(field, "clear")},
-        },
-        R"rs(
+           }},
+          {"getter_thunk", Thunk(field, "get")},
+          {"setter_thunk", Thunk(field, "set")},
+          {"clearer_thunk", Thunk(field, "clear")},
+      },
+      R"rs(
           $getter$
           $getter_opt$
 
@@ -88,65 +82,59 @@ class SingularScalar final : public AccessorGenerator {
             }
           }
         )rs");
-  }
+}
 
-  void InExternC(Context<FieldDescriptor> field) const override {
-    field.Emit(
-        {{"Scalar", PrimitiveRsTypeName(field)},
-         {"hazzer_thunk", Thunk(field, "has")},
-         {"getter_thunk", Thunk(field, "get")},
-         {"setter_thunk", Thunk(field, "set")},
-         {"clearer_thunk", Thunk(field, "clear")},
-         {"hazzer",
-          [&] {
-            if (field.desc().has_presence()) {
-              field.Emit(
-                  R"rs(fn $hazzer_thunk$(raw_msg: $pbi$::RawMessage) -> bool;)rs");
-            }
-          }}},
-        R"rs(
+void SingularScalar::InExternC(Context<FieldDescriptor> field) const {
+  field.Emit(
+      {{"Scalar", PrimitiveRsTypeName(field)},
+       {"hazzer_thunk", Thunk(field, "has")},
+       {"getter_thunk", Thunk(field, "get")},
+       {"setter_thunk", Thunk(field, "set")},
+       {"clearer_thunk", Thunk(field, "clear")},
+       {"hazzer",
+        [&] {
+          if (field.desc().has_presence()) {
+            field.Emit(
+                R"rs(fn $hazzer_thunk$(raw_msg: $pbi$::RawMessage) -> bool;)rs");
+          }
+        }}},
+      R"rs(
           $hazzer$
           fn $getter_thunk$(raw_msg: $pbi$::RawMessage) -> $Scalar$;
           fn $setter_thunk$(raw_msg: $pbi$::RawMessage, val: $Scalar$);
           fn $clearer_thunk$(raw_msg: $pbi$::RawMessage);
         )rs");
-  }
+}
 
-  void InThunkCc(Context<FieldDescriptor> field) const override {
-    field.Emit({{"field", cpp::FieldName(&field.desc())},
-                {"Scalar", cpp::PrimitiveTypeName(field.desc().cpp_type())},
-                {"QualifiedMsg",
-                 cpp::QualifiedClassName(field.desc().containing_type())},
-                {"hazzer_thunk", Thunk(field, "has")},
-                {"getter_thunk", Thunk(field, "get")},
-                {"setter_thunk", Thunk(field, "set")},
-                {"clearer_thunk", Thunk(field, "clear")},
-                {"hazzer",
-                 [&] {
-                   if (field.desc().has_presence()) {
-                     field.Emit(R"cc(
-                       bool $hazzer_thunk$($QualifiedMsg$* msg) {
-                         return msg->has_$field$();
-                       }
-                     )cc");
-                   }
-                 }}},
-               R"cc(
-                 $hazzer$;
-                 $Scalar$ $getter_thunk$($QualifiedMsg$* msg) { return msg->$field$(); }
-                 void $setter_thunk$($QualifiedMsg$* msg, $Scalar$ val) {
-                   msg->set_$field$(val);
+void SingularScalar::InThunkCc(Context<FieldDescriptor> field) const {
+  field.Emit({{"field", cpp::FieldName(&field.desc())},
+              {"Scalar", cpp::PrimitiveTypeName(field.desc().cpp_type())},
+              {"QualifiedMsg",
+               cpp::QualifiedClassName(field.desc().containing_type())},
+              {"hazzer_thunk", Thunk(field, "has")},
+              {"getter_thunk", Thunk(field, "get")},
+              {"setter_thunk", Thunk(field, "set")},
+              {"clearer_thunk", Thunk(field, "clear")},
+              {"hazzer",
+               [&] {
+                 if (field.desc().has_presence()) {
+                   field.Emit(R"cc(
+                     bool $hazzer_thunk$($QualifiedMsg$* msg) {
+                       return msg->has_$field$();
+                     }
+                   )cc");
                  }
-                 void $clearer_thunk$($QualifiedMsg$* msg) { msg->clear_$field$(); }
-               )cc");
-  }
-};
-}  // namespace
-
-std::unique_ptr<AccessorGenerator> AccessorGenerator::ForSingularScalar(
-    Context<FieldDescriptor> field) {
-  return std::make_unique<SingularScalar>();
+               }}},
+             R"cc(
+               $hazzer$;
+               $Scalar$ $getter_thunk$($QualifiedMsg$* msg) { return msg->$field$(); }
+               void $setter_thunk$($QualifiedMsg$* msg, $Scalar$ val) {
+                 msg->set_$field$(val);
+               }
+               void $clearer_thunk$($QualifiedMsg$* msg) { msg->clear_$field$(); }
+             )cc");
 }
+
 }  // namespace rust
 }  // namespace compiler
 }  // namespace protobuf
diff --git a/src/google/protobuf/compiler/rust/accessors/unsupported_field.cc b/src/google/protobuf/compiler/rust/accessors/unsupported_field.cc
new file mode 100644
index 0000000000..f8ff82151a
--- /dev/null
+++ b/src/google/protobuf/compiler/rust/accessors/unsupported_field.cc
@@ -0,0 +1,51 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google LLC. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "absl/strings/string_view.h"
+#include "google/protobuf/compiler/rust/accessors/accessor_generator.h"
+#include "google/protobuf/compiler/rust/context.h"
+#include "google/protobuf/descriptor.h"
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+namespace rust {
+
+void UnsupportedField::InMsgImpl(Context<FieldDescriptor> field) const {
+  field.Emit(R"rs(
+    // Unsupported! :(
+    )rs");
+  field.printer().PrintRaw("\n");
+}
+
+}  // namespace rust
+}  // namespace compiler
+}  // namespace protobuf
+}  // namespace google
diff --git a/src/google/protobuf/compiler/rust/generator.cc b/src/google/protobuf/compiler/rust/generator.cc
index 02a92a6dc7..a3668a485d 100644
--- a/src/google/protobuf/compiler/rust/generator.cc
+++ b/src/google/protobuf/compiler/rust/generator.cc
@@ -289,8 +289,7 @@ bool RustGenerator::Generate(const FileDescriptor* file_desc,
   for (int i = 0; i < file.desc().message_type_count(); ++i) {
     auto msg = file.WithDesc(file.desc().message_type(i));
 
-    MessageGenerator gen(msg);
-    gen.GenerateRs(msg);
+    GenerateRs(msg);
     msg.printer().PrintRaw("\n");
 
     if (file.is_cpp()) {
@@ -299,7 +298,7 @@ bool RustGenerator::Generate(const FileDescriptor* file_desc,
       thunks_msg.Emit({{"Msg", msg.desc().full_name()}}, R"cc(
         // $Msg$
       )cc");
-      gen.GenerateThunksCc(thunks_msg);
+      GenerateThunksCc(thunks_msg);
       thunks_msg.printer().PrintRaw("\n");
     }
   }
diff --git a/src/google/protobuf/compiler/rust/message.cc b/src/google/protobuf/compiler/rust/message.cc
index a42c550c09..64027f97ab 100644
--- a/src/google/protobuf/compiler/rust/message.cc
+++ b/src/google/protobuf/compiler/rust/message.cc
@@ -205,18 +205,7 @@ void MessageDrop(Context<Descriptor> msg) {
 }
 }  // namespace
 
-MessageGenerator::MessageGenerator(Context<Descriptor> msg) {
-  accessors_.resize(msg.desc().field_count());
-  for (int i = 0; i < msg.desc().field_count(); ++i) {
-    auto field = msg.WithDesc(msg.desc().field(i));
-    accessors_[i] = AccessorGenerator::For(field);
-    if (accessors_[i] == nullptr) {
-      ABSL_LOG(WARNING) << "unsupported field: " << field.desc().full_name();
-    }
-  }
-}
-
-void MessageGenerator::GenerateRs(Context<Descriptor> msg) {
+void GenerateRs(Context<Descriptor> msg) {
   if (msg.desc().map_key() != nullptr) {
     ABSL_LOG(WARNING) << "unsupported map field: " << msg.desc().full_name();
     return;
@@ -233,31 +222,19 @@ void MessageGenerator::GenerateRs(Context<Descriptor> msg) {
           {"accessor_fns",
            [&] {
              for (int i = 0; i < msg.desc().field_count(); ++i) {
-               auto& gen = accessors_[i];
                auto field = msg.WithDesc(*msg.desc().field(i));
                msg.Emit({{"comment", FieldInfoComment(field)}}, R"rs(
                  // $comment$
                )rs");
 
-               if (gen == nullptr) {
-                 msg.Emit({{"field", field.desc().full_name()}}, R"rs(
-                  // Unsupported! :(
-                 )rs");
-                 msg.printer().PrintRaw("\n");
-                 continue;
-               }
-
-               gen->GenerateMsgImpl(field);
+               GenerateAccessorMsgImpl(field);
                msg.printer().PrintRaw("\n");
              }
            }},
           {"accessor_externs",
            [&] {
              for (int i = 0; i < msg.desc().field_count(); ++i) {
-               auto& gen = accessors_[i];
-               if (gen == nullptr) continue;
-
-               gen->GenerateExternC(msg.WithDesc(*msg.desc().field(i)));
+               GenerateAccessorExternC(msg.WithDesc(*msg.desc().field(i)));
                msg.printer().PrintRaw("\n");
              }
            }},
@@ -273,8 +250,7 @@ void MessageGenerator::GenerateRs(Context<Descriptor> msg) {
                                ++i) {
                             auto nested_msg =
                                 msg.WithDesc(msg.desc().nested_type(i));
-                            MessageGenerator gen(nested_msg);
-                            gen.GenerateRs(nested_msg);
+                            GenerateRs(nested_msg);
                           }
                         }}},
                       R"rs(
@@ -398,7 +374,7 @@ void MessageGenerator::GenerateRs(Context<Descriptor> msg) {
 }
 
 // Generates code for a particular message in `.pb.thunk.cc`.
-void MessageGenerator::GenerateThunksCc(Context<Descriptor> msg) {
+void GenerateThunksCc(Context<Descriptor> msg) {
   ABSL_CHECK(msg.is_cpp());
   if (msg.desc().map_key() != nullptr) {
     ABSL_LOG(WARNING) << "unsupported map field: " << msg.desc().full_name();
@@ -419,17 +395,13 @@ void MessageGenerator::GenerateThunksCc(Context<Descriptor> msg) {
              for (int i = 0; i < msg.desc().nested_type_count(); ++i) {
                Context<Descriptor> nested_msg =
                    msg.WithDesc(msg.desc().nested_type(i));
-               MessageGenerator gen(nested_msg);
-               gen.GenerateThunksCc(nested_msg);
+               GenerateThunksCc(nested_msg);
              }
            }},
           {"accessor_thunks",
            [&] {
              for (int i = 0; i < msg.desc().field_count(); ++i) {
-               auto& gen = accessors_[i];
-               if (gen == nullptr) continue;
-
-               gen->GenerateThunkCc(msg.WithDesc(*msg.desc().field(i)));
+               GenerateAccessorThunkCc(msg.WithDesc(*msg.desc().field(i)));
              }
            }},
       },
diff --git a/src/google/protobuf/compiler/rust/message.h b/src/google/protobuf/compiler/rust/message.h
index 608c69a56b..ad8ab8037e 100644
--- a/src/google/protobuf/compiler/rust/message.h
+++ b/src/google/protobuf/compiler/rust/message.h
@@ -43,19 +43,11 @@ namespace protobuf {
 namespace compiler {
 namespace rust {
 
-class MessageGenerator final {
- public:
-  explicit MessageGenerator(Context<Descriptor> msg);
+// Generates code for a particular message in `.pb.rs`.
+void GenerateRs(Context<Descriptor> msg);
 
-  // Generates code for a particular message in `.pb.rs`.
-  void GenerateRs(Context<Descriptor> msg);
-
-  // Generates code for a particular message in `.pb.thunk.cc`.
-  void GenerateThunksCc(Context<Descriptor> msg);
-
- private:
-  std::vector<std::unique_ptr<AccessorGenerator>> accessors_;
-};
+// Generates code for a particular message in `.pb.thunk.cc`.
+void GenerateThunksCc(Context<Descriptor> msg);
 
 }  // namespace rust
 }  // namespace compiler