Add Message::Clear() and MessageMut::Clear() (behind the Clear trait)
Distinct from any clear_submsg(), this clears the message contents and doesn't affect presence on parent (and also allows for clearing owned messages which don't exist as a field to be cleared).
PiperOrigin-RevId: 656453234
diff --git a/rust/codegen_traits.rs b/rust/codegen_traits.rs
index ddfe3cf..ad8dd64 100644
--- a/rust/codegen_traits.rs
+++ b/rust/codegen_traits.rs
@@ -11,7 +11,7 @@
use create::Parse;
use read::Serialize;
use std::fmt::Debug;
-use write::ClearAndParse;
+use write::{Clear, ClearAndParse};
/// A trait that all generated owned message types implement.
pub trait Message: MutProxied
@@ -20,8 +20,7 @@
// Read traits:
+ Debug + Serialize
// Write traits:
- // TODO: Msg should impl Clear.
- + ClearAndParse
+ + Clear + ClearAndParse
// Thread safety:
+ Send + Sync
// Copy/Clone:
@@ -47,10 +46,12 @@
// Read traits:
+ Debug + Serialize
// Write traits:
- // TODO: MsgMut should impl Clear and ClearAndParse.
+ // TODO: MsgMut should impl ClearAndParse.
+ + Clear
// Thread safety:
+ Sync
- // Copy/Clone: (Neither)
+ // Copy/Clone:
+ // (Neither)
{
#[doc(hidden)]
type Message: Message;
diff --git a/rust/test/shared/accessors_proto3_test.rs b/rust/test/shared/accessors_proto3_test.rs
index 63aaf1a..1df120d 100644
--- a/rust/test/shared/accessors_proto3_test.rs
+++ b/rust/test/shared/accessors_proto3_test.rs
@@ -5,9 +5,12 @@
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
-/// Tests covering accessors for singular bool, int32, int64, and bytes fields
-/// on proto3.
+//! Tests covering accessors for singular bool, int32, int64, and bytes fields
+//! on proto3.
+
use googletest::prelude::*;
+use protobuf::prelude::*;
+
use protobuf::Optional;
use unittest_proto3_optional_rust_proto::{test_proto3_optional, TestProto3Optional};
use unittest_proto3_rust_proto::{test_all_types, TestAllTypes};
@@ -251,3 +254,29 @@
msg.set_optional_string_piece("hello");
assert_that!(msg.optional_string_piece(), eq("hello"));
}
+
+#[googletest::test]
+fn test_msg_clear() {
+ let mut m = TestAllTypes::new();
+ m.set_optional_int32(42);
+ assert_that!(m.optional_int32(), eq(42));
+ m.clear();
+ assert_that!(m.optional_int32(), eq(0));
+}
+
+#[googletest::test]
+fn test_submsg_clear() {
+ let mut m = TestAllTypes::new();
+ let mut sub = m.optional_nested_message_mut();
+ sub.set_bb(7);
+
+ assert_that!(m.has_optional_nested_message(), eq(true));
+ assert_that!(m.optional_nested_message().bb(), eq(7));
+
+ m.optional_nested_message_mut().clear();
+
+ // .clear() on the submsg doesn't affect its presence on the parent:
+ assert_that!(m.has_optional_nested_message(), eq(true));
+ // ...but it does clear the submsg's value:
+ assert_that!(m.optional_nested_message().bb(), eq(0));
+}
diff --git a/rust/test/shared/accessors_test.rs b/rust/test/shared/accessors_test.rs
index 2a7795e..af020f8 100644
--- a/rust/test/shared/accessors_test.rs
+++ b/rust/test/shared/accessors_test.rs
@@ -8,6 +8,8 @@
//! Tests covering accessors for singular bool, int32, int64, and bytes fields.
use googletest::prelude::*;
+use protobuf::prelude::*;
+
use protobuf::{Optional, ProtoBytes, ProtoStr, ProtoString};
use std::borrow::Cow;
use std::ffi::OsString;
@@ -917,3 +919,29 @@
assert_that!(msg.optional_string_piece(), eq("hello"));
assert_that!(msg.has_optional_string_piece(), eq(true));
}
+
+#[googletest::test]
+fn test_msg_clear() {
+ let mut m = TestAllTypes::new();
+ m.set_optional_int32(42);
+ assert_that!(m.has_optional_int32(), eq(true));
+ m.clear();
+ assert_that!(m.has_optional_int32(), eq(false));
+}
+
+#[googletest::test]
+fn test_submsg_clear() {
+ let mut m = TestAllTypes::new();
+ let mut sub = m.optional_nested_message_mut();
+ sub.set_bb(7);
+
+ assert_that!(m.has_optional_nested_message(), eq(true));
+ assert_that!(m.optional_nested_message().bb(), eq(7));
+
+ m.optional_nested_message_mut().clear();
+
+ // .clear() on the submsg doesn't affect its presence on the parent:
+ assert_that!(m.has_optional_nested_message(), eq(true));
+ // ...but it does clear the submsg's value:
+ assert_that!(m.optional_nested_message().bb(), eq(0));
+}
diff --git a/rust/upb/lib.rs b/rust/upb/lib.rs
index 67319af..3511918 100644
--- a/rust/upb/lib.rs
+++ b/rust/upb/lib.rs
@@ -32,8 +32,9 @@
mod message;
pub use message::{
- upb_Message, upb_Message_DeepClone, upb_Message_DeepCopy, upb_Message_IsEqual,
- upb_Message_MergeFrom, upb_Message_New, upb_Message_SetBaseField, RawMessage,
+ upb_Message, upb_Message_Clear, upb_Message_DeepClone, upb_Message_DeepCopy,
+ upb_Message_IsEqual, upb_Message_MergeFrom, upb_Message_New, upb_Message_SetBaseField,
+ RawMessage,
};
mod message_value;
diff --git a/rust/upb/message.rs b/rust/upb/message.rs
index 67d1e06..b78e495 100644
--- a/rust/upb/message.rs
+++ b/rust/upb/message.rs
@@ -13,10 +13,20 @@
pub type RawMessage = NonNull<upb_Message>;
extern "C" {
- /// SAFETY: No constraints.
+ /// SAFETY:
+ /// - `mini_table` and `arena` must be valid to deref
pub fn upb_Message_New(mini_table: *const upb_MiniTable, arena: RawArena)
-> Option<RawMessage>;
+ /// SAFETY:
+ /// - `m` and `mini_table` must be valid to deref
+ /// - `mini_table` must be the MiniTable associtaed with `m`
+ pub fn upb_Message_Clear(m: RawMessage, mini_table: *const upb_MiniTable);
+
+ /// SAFETY:
+ /// - All four arguments must be valid to deref
+ /// - `mini_table` must be the MiniTable associated with both `dst` and
+ /// `src`
pub fn upb_Message_DeepCopy(
dst: RawMessage,
src: RawMessage,
@@ -24,18 +34,29 @@
arena: RawArena,
);
+ /// SAFETY:
+ /// - All three arguments must be valid to deref
+ /// - `mini_table` must be the MiniTable associated with `m`
pub fn upb_Message_DeepClone(
m: RawMessage,
mini_table: *const upb_MiniTable,
arena: RawArena,
) -> Option<RawMessage>;
+ /// SAFETY:
+ /// - `m` and `mini_table` must be valid to deref
+ /// - `mini_table` must be the MiniTable associated with `m`
+ /// - `val` must be a pointer to legally readable memory of the correct type
+ /// for the field described by `mini_table`
pub fn upb_Message_SetBaseField(
m: RawMessage,
mini_table: *const upb_MiniTableField,
val: *const std::ffi::c_void,
);
+ /// SAFETY:
+ /// - All four arguments must be valid to deref
+ /// - `mini_table` must be the MiniTable associated with both `m1` and `m2`
pub fn upb_Message_IsEqual(
m1: RawMessage,
m2: RawMessage,
@@ -43,6 +64,11 @@
options: i32,
) -> bool;
+ /// SAFETY:
+ /// - `dst`, `src`, `mini_table` and `arena` must be valid to deref
+ /// - `extreg` must be valid to deref or nullptr
+ /// - `mini_table` must be the MiniTable associated with both `dst` and
+ /// `src`
pub fn upb_Message_MergeFrom(
dst: RawMessage,
src: RawMessage,
diff --git a/src/google/protobuf/compiler/rust/message.cc b/src/google/protobuf/compiler/rust/message.cc
index a6dca4b..c3c5013 100644
--- a/src/google/protobuf/compiler/rust/message.cc
+++ b/src/google/protobuf/compiler/rust/message.cc
@@ -92,6 +92,28 @@
ABSL_LOG(FATAL) << "unreachable";
}
+void MessageMutClear(Context& ctx, const Descriptor& msg) {
+ switch (ctx.opts().kernel) {
+ case Kernel::kCpp:
+ ctx.Emit({{"clear_thunk", ThunkName(ctx, msg, "clear")}},
+ R"rs(
+ unsafe { $clear_thunk$(self.raw_msg()) }
+ )rs");
+ return;
+ case Kernel::kUpb:
+ ctx.Emit(
+ {
+ {"minitable", UpbMinitableName(msg)},
+ },
+ R"rs(
+ unsafe {
+ $pbr$::upb_Message_Clear(self.raw_msg(), $std$::ptr::addr_of!($minitable$))
+ }
+ )rs");
+ return;
+ }
+}
+
void MessageClearAndParse(Context& ctx, const Descriptor& msg) {
switch (ctx.opts().kernel) {
case Kernel::kCpp:
@@ -180,6 +202,7 @@
{
{"new_thunk", ThunkName(ctx, msg, "new")},
{"delete_thunk", ThunkName(ctx, msg, "delete")},
+ {"clear_thunk", ThunkName(ctx, msg, "clear")},
{"serialize_thunk", ThunkName(ctx, msg, "serialize")},
{"parse_thunk", ThunkName(ctx, msg, "parse")},
{"copy_from_thunk", ThunkName(ctx, msg, "copy_from")},
@@ -200,6 +223,7 @@
R"rs(
fn $new_thunk$() -> $pbr$::RawMessage;
fn $delete_thunk$(raw_msg: $pbr$::RawMessage);
+ fn $clear_thunk$(raw_msg: $pbr$::RawMessage);
fn $serialize_thunk$(raw_msg: $pbr$::RawMessage, out: &mut $pbr$::SerializedData) -> bool;
fn $parse_thunk$(raw_msg: $pbr$::RawMessage, data: $pbr$::SerializedData) -> bool;
fn $copy_from_thunk$(dst: $pbr$::RawMessage, src: $pbr$::RawMessage);
@@ -824,6 +848,7 @@
{{"Msg", RsSafeName(msg.name())},
{"Msg::new", [&] { MessageNew(ctx, msg); }},
{"Msg::serialize", [&] { MessageSerialize(ctx, msg); }},
+ {"MsgMut::clear", [&] { MessageMutClear(ctx, msg); }},
{"Msg::clear_and_parse", [&] { MessageClearAndParse(ctx, msg); }},
{"Msg::drop", [&] { MessageDrop(ctx, msg); }},
{"Msg::debug", [&] { MessageDebug(ctx, msg); }},
@@ -977,6 +1002,12 @@
}
}
+ impl $pb$::Clear for $Msg$ {
+ fn clear(&mut self) {
+ self.as_mut().clear()
+ }
+ }
+
impl $pb$::ClearAndParse for $Msg$ {
fn clear_and_parse(&mut self, data: &[u8]) -> Result<(), $pb$::ParseError> {
self.clear_and_parse(data)
@@ -1096,6 +1127,12 @@
}
}
+ impl $pb$::Clear for $Msg$Mut<'_> {
+ fn clear(&mut self) {
+ $MsgMut::clear$
+ }
+ }
+
#[allow(dead_code)]
impl<'msg> $Msg$Mut<'msg> {
#[doc(hidden)]
@@ -1312,6 +1349,7 @@
{"QualifiedMsg", cpp::QualifiedClassName(&msg)},
{"new_thunk", ThunkName(ctx, msg, "new")},
{"delete_thunk", ThunkName(ctx, msg, "delete")},
+ {"clear_thunk", ThunkName(ctx, msg, "clear")},
{"serialize_thunk", ThunkName(ctx, msg, "serialize")},
{"parse_thunk", ThunkName(ctx, msg, "parse")},
{"copy_from_thunk", ThunkName(ctx, msg, "copy_from")},
@@ -1354,6 +1392,9 @@
extern $abi$ {
void* $new_thunk$() { return new $QualifiedMsg$(); }
void $delete_thunk$(void* ptr) { delete static_cast<$QualifiedMsg$*>(ptr); }
+ void $clear_thunk$(void* ptr) {
+ static_cast<$QualifiedMsg$*>(ptr)->Clear();
+ }
bool $serialize_thunk$($QualifiedMsg$* msg, google::protobuf::rust::SerializedData* out) {
return google::protobuf::rust::SerializeMsg(msg, out);
}