diff --git a/rust/test/BUILD b/rust/test/BUILD index 30e7d1e28c..2ff1bc8543 100644 --- a/rust/test/BUILD +++ b/rust/test/BUILD @@ -6,13 +6,10 @@ load( "rust_upb_proto_library", ) -UNITTEST_PROTO_TARGET = ( - "//src/google/protobuf:test_protos" -) - -UNITTEST_CC_PROTO_TARGET = ( - "//src/google/protobuf:cc_test_protos" -) +UNITTEST_PROTO_TARGET = "//src/google/protobuf:test_protos" +UNITTEST_CC_PROTO_TARGET = "//src/google/protobuf:cc_test_protos" +UNITTEST_PROTO3_TARGET = "//src/google/protobuf:test_protos" +UNITTEST_PROTO3_CC_TARGET = "//src/google/protobuf:cc_test_protos" alias( name = "unittest_cc_proto", diff --git a/rust/test/shared/BUILD b/rust/test/shared/BUILD index 0f65d1eff9..ac1486e27d 100644 --- a/rust/test/shared/BUILD +++ b/rust/test/shared/BUILD @@ -96,6 +96,18 @@ rust_test( deps = ["//rust/test:unittest_upb_rust_proto"], ) +rust_test( + name = "accessors_proto3_cpp_test", + srcs = ["accessors_proto3_test.rs"], + deps = ["//rust/test:unittest_proto3_cc_rust_proto"], +) + +rust_test( + name = "accessors_proto3_upb_test", + srcs = ["accessors_proto3_test.rs"], + deps = ["//rust/test:unittest_proto3_upb_rust_proto"], +) + rust_test( name = "serialization_upb_test", srcs = ["serialization_test.rs"], diff --git a/rust/test/shared/accessors_proto3_test.rs b/rust/test/shared/accessors_proto3_test.rs new file mode 100644 index 0000000000..9a002b68eb --- /dev/null +++ b/rust/test/shared/accessors_proto3_test.rs @@ -0,0 +1,62 @@ +// 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. + +/// Tests covering accessors for singular bool, int32, int64, and bytes fields +/// on proto3. +use unittest_proto3::proto3_unittest::TestAllTypes; + +#[test] +fn test_fixed32_accessors() { + let mut msg = TestAllTypes::new(); + assert_eq!(msg.optional_fixed32(), 0); + + msg.optional_fixed32_set(Some(99)); + assert_eq!(msg.optional_fixed32(), 99); + + msg.optional_fixed32_set(None); + assert_eq!(msg.optional_fixed32(), 0); +} + +#[test] +fn test_optional_bytes_accessors() { + let mut msg = TestAllTypes::new(); + // Note: even though its named 'optional_bytes' the field is actually not proto3 + // optional, so it does not support presence. + assert_eq!(msg.optional_bytes(), b""); + + msg.optional_bytes_set(Some(b"accessors_test")); + assert_eq!(msg.optional_bytes(), b"accessors_test"); + + msg.optional_bytes_set(None); + assert_eq!(msg.optional_bytes(), b""); + + msg.optional_bytes_set(Some(b"")); + assert_eq!(msg.optional_bytes(), b""); +} diff --git a/rust/test/shared/accessors_test.rs b/rust/test/shared/accessors_test.rs index ccc71d1a1b..55e700d35e 100644 --- a/rust/test/shared/accessors_test.rs +++ b/rust/test/shared/accessors_test.rs @@ -216,16 +216,16 @@ fn test_optional_bool_accessors() { #[test] fn test_optional_bytes_accessors() { let mut msg = TestAllTypes::new(); - assert_eq!(msg.optional_bytes(), None); + assert_eq!(msg.optional_bytes_opt(), None); msg.optional_bytes_set(Some(b"accessors_test")); - assert_eq!(msg.optional_bytes().unwrap(), b"accessors_test"); + assert_eq!(msg.optional_bytes_opt().unwrap(), b"accessors_test"); msg.optional_bytes_set(None); - assert_eq!(msg.optional_bytes(), None); + assert_eq!(msg.optional_bytes_opt(), None); msg.optional_bytes_set(Some(b"")); - assert_eq!(msg.optional_bytes().unwrap(), b""); + assert_eq!(msg.optional_bytes_opt().unwrap(), b""); } #[test] diff --git a/src/google/protobuf/compiler/rust/accessors/singular_bytes.cc b/src/google/protobuf/compiler/rust/accessors/singular_bytes.cc index 831fec10c5..3380fc6ff7 100644 --- a/src/google/protobuf/compiler/rust/accessors/singular_bytes.cc +++ b/src/google/protobuf/compiler/rust/accessors/singular_bytes.cc @@ -54,16 +54,26 @@ class SingularBytes final : public AccessorGenerator { {"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; + } + unsafe { + Some($getter_thunk$(self.msg).as_ref()) + } + })rs"); + }}, }, R"rs( - pub fn $field$(&self) -> Option<&[u8]> { - if !unsafe { $hazzer_thunk$(self.msg) } { - return None; - } - unsafe { - Some($getter_thunk$(self.msg).as_ref()) - } + pub fn r#$field$(&self) -> &[u8] { + unsafe { $getter_thunk$(self.msg).as_ref() } } + $getter_opt$ pub fn $field$_set(&mut self, val: Option<&[u8]>) { match val { Some(val) => @@ -79,15 +89,20 @@ class SingularBytes final : public AccessorGenerator { } void InExternC(Context 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")}, - }, - R"rs( + 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( + $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); @@ -95,29 +110,33 @@ class SingularBytes final : public AccessorGenerator { } void InThunkCc(Context 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")}, - }, - R"cc( - bool $hazzer_thunk$($QualifiedMsg$* msg) { - return msg->has_$field$(); - } - ::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"); + 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 $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 diff --git a/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc b/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc index 2db381f8c4..36ddcdb39c 100644 --- a/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc +++ b/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc @@ -63,6 +63,7 @@ class SingularScalar final : public AccessorGenerator { {"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) } { @@ -91,15 +92,20 @@ class SingularScalar final : public AccessorGenerator { void InExternC(Context 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")}, - }, + {{"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( - fn $hazzer_thunk$(raw_msg: $pbi$::RawMessage) -> bool; + $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); @@ -107,27 +113,32 @@ class SingularScalar final : public AccessorGenerator { } void InThunkCc(Context 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")}, - }, - R"cc( - bool $hazzer_thunk$($QualifiedMsg$* msg) { - return msg->has_$field$(); - } - $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"); + 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 $clearer_thunk$($QualifiedMsg$* msg) { msg->clear_$field$(); } + )cc"); } }; } // namespace