From 0542d40dccbcfd3dfcf3a55ab77bbc631dd757fc Mon Sep 17 00:00:00 2001 From: Derek Benson Date: Tue, 16 Apr 2024 13:12:42 -0700 Subject: [PATCH] Rust: fix proto! msgtype matcher to look for :: separated idents instead of a ty to avoid syntax errors during parse PiperOrigin-RevId: 625429664 --- rust/proto_macro.rs | 73 +++++++++++++++++++++++----- rust/test/shared/proto_macro_test.rs | 15 ++++-- 2 files changed, 73 insertions(+), 15 deletions(-) diff --git a/rust/proto_macro.rs b/rust/proto_macro.rs index 94c0455be1bef..d3da607664687 100644 --- a/rust/proto_macro.rs +++ b/rust/proto_macro.rs @@ -1,7 +1,39 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2024 Google LLC. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +/// proto! enables the use of Rust struct initialization syntax to create +/// protobuf messages. The macro does its best to try and detect the +/// initialization of submessages, but it is only able to do so while the +/// submessage is being defined as part of the original struct literal. +/// Introducing an expression using () or {} as the value of a field will +/// require another call to this macro in order to return a submessage +/// literal.``` /* +/// Given the following proto definition +/// message Data { +/// int32 number = 1; +/// string letters = 2; +/// Data nested = 3; +/// } +/// */ +/// use protobuf::proto; +/// let message = proto!(Data { +/// number: 42, +/// letters: "Hello World", +/// nested: Data { +/// number: { +/// let x = 100; +/// x + 1 +/// } +/// } +/// }); ``` #[macro_export] macro_rules! proto { ($msgtype:ty { $($tt:tt)* }) => { - $crate::proto_internal!($msgtype { $($tt)* }); + $crate::proto_internal!($msgtype { $($tt)* }) } } @@ -9,29 +41,48 @@ macro_rules! proto { #[doc(hidden)] macro_rules! proto_internal { // nested message, - (@msg $msg:ident $submsg:ident : $msgtype:ty { $field:ident : $($value:tt)* }, $($rest:tt)*) => { - proto_internal!(@msg $msg $submsg : $msgtype { $field : $($value)* }); + (@msg $msg:ident $submsg:ident : $($msgtype:ident)::+ { $field:ident : $($value:tt)* }, $($rest:tt)*) => { + proto_internal!(@msg $msg $submsg : $($msgtype)::+ { $field : $($value)* }); + proto_internal!(@msg $msg $($rest)*); + }; + (@msg $msg:ident $submsg:ident : ::$($msgtype:ident)::+ { $field:ident : $($value:tt)* }, $($rest:tt)*) => { + proto_internal!(@msg $msg $submsg : ::$($msgtype)::+ { $field : $($value)* }); proto_internal!(@msg $msg $($rest)*); }; // nested message - (@msg $msg:ident $submsg:ident : $msgtype:ty { $field:ident : $($value:tt)* }) => { + (@msg $msg:ident $submsg:ident : $($msgtype:ident)::+ { $field:ident : $($value:tt)* }) => { + { + let mut $msg: <$($msgtype)::+ as $crate::Proxied>::Mut<'_> = $crate::__internal::paste!($msg.[<$submsg _mut>]()); + proto_internal!(@msg $msg $field : $($value)*); + } + }; + (@msg $msg:ident $submsg:ident : ::$($msgtype:ident)::+ { $field:ident : $($value:tt)* }) => { { - let mut $msg: <$msgtype as $crate::Proxied>::Mut<'_> = $crate::__internal::paste!($msg.[<$submsg _mut>]()); + let mut $msg: <::$($msgtype)::+ as $crate::Proxied>::Mut<'_> = $crate::__internal::paste!($msg.[<$submsg _mut>]()); proto_internal!(@msg $msg $field : $($value)*); } }; // empty nested message, - (@msg $msg:ident $submsg:ident : $msgtype:ty { }, $($rest:tt)*) => { - proto_internal!(@msg $msg $submsg : $msgtype { }); + (@msg $msg:ident $submsg:ident : $($msgtype:ident)::+ { }, $($rest:tt)*) => { + proto_internal!(@msg $msg $submsg : $($msgtype)::+ { }); + proto_internal!(@msg $msg $($rest)*); + }; + (@msg $msg:ident $submsg:ident : ::$($msgtype:ident)::+ { }, $($rest:tt)*) => { + proto_internal!(@msg $msg $submsg : ::$($msgtype)::+ { }); proto_internal!(@msg $msg $($rest)*); }; // empty nested message - (@msg $msg:ident $submsg:ident : $msgtype:ty { }) => { + (@msg $msg:ident $submsg:ident : $($msgtype:ident)::+ { }) => { + { + let mut $msg: <$($msgtype)::+ as $crate::Proxied>::Mut<'_> = $crate::__internal::paste!($msg.[<$submsg _mut>]()); + } + }; + (@msg $msg:ident $submsg:ident : ::$($msgtype:ident)::+ { }) => { { - let mut $msg = $crate::__internal::paste!($msg.[<$submsg _mut>]()); + let mut $msg: <::$($msgtype)::+ as $crate::Proxied>::Mut<'_> = $crate::__internal::paste!($msg.[<$submsg _mut>]()); } }; @@ -44,9 +95,7 @@ macro_rules! proto_internal { // field: expr (@msg $msg:ident $ident:ident : $expr:expr) => { - $crate::__internal::paste!{ - $msg.[]($expr); - } + $crate::__internal::paste!($msg.[]($expr)); }; (@msg $msg:ident) => {}; diff --git a/rust/test/shared/proto_macro_test.rs b/rust/test/shared/proto_macro_test.rs index 9f7d4e6418b64..9bf7c9aa27115 100644 --- a/rust/test/shared/proto_macro_test.rs +++ b/rust/test/shared/proto_macro_test.rs @@ -14,17 +14,26 @@ use unittest_proto::{ NestedTestAllTypes, TestAllTypes, }; +struct TestValue { + val: i64, +} + #[test] fn test_setting_literals() { let fixed64 = || 108; + let test_ref = |x: &i64| *x; + let test_ref_b = |x: &u32| *x; + let one_oh_seven = [107_u32]; + let value = TestValue { val: 106 }; + let msg = proto!(TestAllTypes { optional_int32: 101, optional_int64: 102, optional_uint32: 103, - optional_uint64: 104, + optional_uint64: if true { 104 } else { 99 }, optional_sint32: -105, - optional_sint64: 106, - optional_fixed32: 107, + optional_sint64: (test_ref(&value.val)), + optional_fixed32: { test_ref_b(&one_oh_seven[0]) }, optional_fixed64: fixed64(), //108 optional_sfixed32: 100 + 9, optional_sfixed64: {