Support without-presence scalars better.

Only emit has_field() if the field support presence. Only emit field_opt() getter if the field is both optional and supports presence.

PiperOrigin-RevId: 555133374
pull/13470/head
Protobuf Team Bot 2 years ago committed by Copybara-Service
parent 161989080b
commit 9a7ca55ca5
  1. 11
      rust/test/BUILD
  2. 12
      rust/test/shared/BUILD
  3. 62
      rust/test/shared/accessors_proto3_test.rs
  4. 8
      rust/test/shared/accessors_test.rs
  5. 95
      src/google/protobuf/compiler/rust/accessors/singular_bytes.cc
  6. 69
      src/google/protobuf/compiler/rust/accessors/singular_scalar.cc

@ -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",

@ -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"],

@ -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"");
}

@ -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]

@ -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<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")},
},
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<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")},
},
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

@ -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<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")},
},
{{"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<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")},
},
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

Loading…
Cancel
Save