Repository: z-galaxy/zbus (Rust)

Issue:
proxy macro panics when one of the methods is `fn r#type(&self)`
In GitLab by @MaxVerevkin on Oct 13, 2021, 17:43

```rust
#[dbus_proxy(
    interface = "org.freedesktop.UPower.Device",
    default_service = "org.freedesktop.UPower"
)]
trait Device {
    /// Type property
    #[dbus_proxy(property)]
    fn r#type(&self) -> zbus::Result<u32>;
}
```
Fails:
```
error: custom attribute panicked
  --> src/blocks/battery/zbus_upower.rs:24:1
   |
24 | / #[dbus_proxy(
25 | |     interface = "org.freedesktop.UPower.Device",
26 | |     default_service = "org.freedesktop.UPower"
27 | | )]
   | |__^
   |
   = help: message: `"cached_r#type"` is not a valid identifier
```


Discussion:
Is this with 1.x or latest 2.0 beta?

Any easy work around is to make use of `name` attribute to avoid keywords:

```rust
    #[dbus_proxy(property, name = "Type")]
    fn type_(&self) -> zbus::Result<u32>;
```

See also https://gitlab.freedesktop.org/dbus/zbus/-/issues/133.
In GitLab by @MaxVerevkin on Oct 14, 2021, 15:23

@zeenix Thanks for the workaround! This affects current master branch as well as latest beta.



Patch:
diff --git a/zbus_macros/src/iface.rs b/zbus_macros/src/iface.rs
index f9c2bd712..ca396c1d4 100644
--- a/zbus_macros/src/iface.rs
+++ b/zbus_macros/src/iface.rs
@@ -256,6 +256,8 @@ impl MethodInfo {
             if is_property && has_inputs {
                 assert!(name.starts_with("set_"));
                 name = name[4..].to_string();
+            } else if name.starts_with("r#") {
+                name = name[2..].to_string();
             }
             pascal_case(&name)
         });
diff --git a/zbus_macros/src/proxy.rs b/zbus_macros/src/proxy.rs
index a718c433e..33f3149d4 100644
--- a/zbus_macros/src/proxy.rs
+++ b/zbus_macros/src/proxy.rs
@@ -208,19 +208,22 @@ pub fn create_proxy(
             let method_attrs = MethodAttributes::parse(&m.attrs)?;
             let property = method_attrs.property.as_ref();
 
-            let method_name = m.sig.ident.to_string();
+            let rust_method_name = m.sig.ident.to_string();
+            let r#_stripped_rust_method_name = rust_method_name
+                .strip_prefix("r#")
+                .unwrap_or(rust_method_name.as_str());
 
             let is_signal = method_attrs.signal;
             let is_property = property.is_some();
             let has_inputs = m.sig.inputs.len() > 1;
 
-            let member_name = method_attrs.name.clone().unwrap_or_else(|| {
+            let dbus_member_name = method_attrs.name.clone().unwrap_or_else(|| {
                 case::pascal_or_camel_case(
                     if is_property && has_inputs {
-                        assert!(method_name.starts_with("set_"));
-                        &method_name[4..]
+                        assert!(r#_stripped_rust_method_name.starts_with("set_"));
+                        &r#_stripped_rust_method_name[4..]
                     } else {
-                        &method_name
+                        r#_stripped_rust_method_name
                     },
                     true,
                 )
@@ -236,12 +239,12 @@ pub fn create_proxy(
                 };
 
                 if let PropertyEmitsChangedSignal::False = emits_changed_signal {
-                    uncached_properties.push(member_name.clone());
+                    uncached_properties.push(dbus_member_name.clone());
                 }
 
                 gen_proxy_property(
-                    &member_name,
-                    &method_name,
+                    &dbus_member_name,
+                    r#_stripped_rust_method_name,
                     m,
                     &async_opts,
                     emits_changed_signal,
@@ -250,8 +253,8 @@ pub fn create_proxy(
                 let (method, types) = gen_proxy_signal(
                     &proxy_name,
                     &iface_name,
-                    &member_name,
-                    &method_name,
+                    &dbus_member_name,
+                    r#_stripped_rust_method_name,
                     m,
                     &async_opts,
                     visibility,
@@ -261,7 +264,13 @@ pub fn create_proxy(
 
                 method
             } else {
-                gen_proxy_method_call(&member_name, &method_name, m, method_attrs, &async_opts)?
+                gen_proxy_method_call(
+                    &dbus_member_name,
+                    &rust_method_name,
+                    m,
+                    method_attrs,
+                    &async_opts,
+                )?
             };
             methods.extend(m);
         }
@@ -465,8 +474,8 @@ pub fn create_proxy(
 }
 
 fn gen_proxy_method_call(
-    method_name: &str,
-    snake_case_name: &str,
+    dbus_member_name: &str,
+    rust_method_name: &str,
     m: &TraitItemFn,
     method_attrs: MethodAttributes,
     async_opts: &AsyncOpts,
@@ -542,7 +551,8 @@ fn gen_proxy_method_call(
         _ => None,
     };
 
-    let method = Ident::new(snake_case_name, Span::call_site());
+    let mut method = parse_str::<Ident>(rust_method_name)?;
+    method.set_span(Span::call_site());
     let inputs = &m.sig.inputs;
     let mut generics = m.sig.generics.clone();
     let where_clause = generics.where_clause.get_or_insert(parse_quote!(where));
@@ -593,7 +603,7 @@ fn gen_proxy_method_call(
             pub #usage #signature {
                 let object_path: #zbus::zvariant::OwnedObjectPath =
                     self.0.call(
-                        #method_name,
+                        #dbus_member_name,
                         &#zbus::zvariant::DynamicTuple((#(#args,)*)),
                     )
                     #wait?;
@@ -628,7 +638,7 @@ fn gen_proxy_method_call(
                 Ok(quote! {
                     #(#other_attrs)*
                     pub #usage #signature {
-                        self.0.call_with_flags::<_, _, ()>(#method_name, #method_flags, #body)#wait?;
+                        self.0.call_with_flags::<_, _, ()>(#dbus_member_name, #method_flags, #body)#wait?;
                         ::std::result::Result::Ok(())
                     }
                 })
@@ -636,7 +646,7 @@ fn gen_proxy_method_call(
                 Ok(quote! {
                     #(#other_attrs)*
                     pub #usage #signature {
-                        let reply = self.0.call_with_flags(#method_name, #method_flags, #body)#wait?;
+                        let reply = self.0.call_with_flags(#dbus_member_name, #method_flags, #body)#wait?;
 
                         // SAFETY: This unwrap() cannot fail due to the guarantees in
                         // call_with_flags, which can only return Ok(None) if the
@@ -652,7 +662,7 @@ fn gen_proxy_method_call(
             Ok(quote! {
                 #(#other_attrs)*
                 pub #usage #signature {
-                    let reply = self.0.call(#method_name, #body)#wait?;
+                    let reply = self.0.call(#dbus_member_name, #body)#wait?;
                     ::std::result::Result::Ok(reply)
                 }
             })
@@ -662,7 +672,7 @@ fn gen_proxy_method_call(
 
 fn gen_proxy_property(
     property_name: &str,
-    method_name: &str,
+    rust_method_name: &str,
     m: &TraitItemFn,
     async_opts: &AsyncOpts,
     emits_changed_signal: PropertyEmitsChangedSignal,
@@ -717,7 +727,7 @@ fn gen_proxy_property(
         let receive_method = match emits_changed_signal {
             PropertyEmitsChangedSignal::True | PropertyEmitsChangedSignal::Invalidates => {
                 let (_, ty_generics, where_clause) = m.sig.generics.split_for_impl();
-                let receive = format_ident!("receive_{}_changed", method_name);
+                let receive = format_ident!("receive_{}_changed", rust_method_name);
                 let gen_doc = format!(
                     "Create a stream for the `{property_name}` property changes. \
                 This is a convenient wrapper around [`{proxy_name}::receive_property_changed`]."
@@ -743,7 +753,7 @@ fn gen_proxy_property(
             PropertyEmitsChangedSignal::True
             | PropertyEmitsChangedSignal::Invalidates
             | PropertyEmitsChangedSignal::Const => {
-                let cached_getter = format_ident!("cached_{}", method_name);
+                let cached_getter = format_ident!("cached_{}", rust_method_name);
                 let cached_doc = format!(
                     " Get the cached value of the `{property_name}` property, or `None` if the property is not cached.",
                 );
@@ -794,7 +804,7 @@ fn gen_proxy_signal(
     proxy_name: &Ident,
     iface_name: &str,
     signal_name: &str,
-    snake_case_name: &str,
+    rust_method_name: &str,
     method: &TraitItemFn,
     async_opts: &AsyncOpts,
     visibility: &Visibility,
@@ -882,8 +892,8 @@ fn gen_proxy_signal(
             quote! { proxy::SignalStream },
         )
     };
-    let receiver_name = format_ident!("receive_{snake_case_name}");
-    let receiver_with_args_name = format_ident!("receive_{snake_case_name}_with_args");
+    let receiver_name = format_ident!("receive_{rust_method_name}");
+    let receiver_with_args_name = format_ident!("receive_{rust_method_name}_with_args");
     let stream_name = format_ident!("{signal_name}{trait_name}");
     let signal_args = format_ident!("{signal_name}Args");
     let signal_name_ident = format_ident!("{signal_name}");


Test patch:
diff --git a/zbus/tests/iface_and_proxy/client.rs b/zbus/tests/iface_and_proxy/client.rs
index 8cb30a0dc..db61331ca 100644
--- a/zbus/tests/iface_and_proxy/client.rs
+++ b/zbus/tests/iface_and_proxy/client.rs
@@ -65,10 +65,15 @@ pub async fn my_iface_test(conn: Connection, event: Event) -> zbus::Result<u32>
             .find(|&n| n.name().is_some_and(|n| n == name))
             .expect("Child node not exist");
     }
-    assert!(node
+    let my_iface = node
         .interfaces()
         .iter()
-        .any(|i| i.name() == "org.freedesktop.MyIface"));
+        .find(|i| i.name() == "org.freedesktop.MyIface")
+        .unwrap();
+    // Test if the r# prefix for the keyword was removed
+    assert!(my_iface.methods().iter().any(|m| m.name() == "Type"));
+    assert!(my_iface.properties().iter().any(|p| p.name() == "Let"));
+    assert!(my_iface.signals().iter().any(|s| s.name() == "Match"));
 
     let proxy = MyIfaceProxy::builder(&conn)
         .destination("org.freedesktop.MyService")?
@@ -153,6 +158,10 @@ pub async fn my_iface_test(conn: Connection, event: Event) -> zbus::Result<u32>
     proxy.test_no_autostart().await?;
     proxy.test_interactive_auth().await?;
 
+    assert_eq!(proxy.r#let().await?, 0);
+    proxy.set_let(1).await?;
+    assert_eq!(proxy.r#let().await?, 1);
+
     let err = proxy.fail_property().await;
     assert_eq!(
         err.unwrap_err(),
diff --git a/zbus/tests/iface_and_proxy/iface.rs b/zbus/tests/iface_and_proxy/iface.rs
index 7d869f10c..d9f297c63 100644
--- a/zbus/tests/iface_and_proxy/iface.rs
+++ b/zbus/tests/iface_and_proxy/iface.rs
@@ -20,6 +20,7 @@ pub struct MyIface {
     emits_changed_invalidates: u32,
     emits_changed_const: u32,
     emits_changed_false: u32,
+    r#let: u32,
 }
 
 impl MyIface {
@@ -32,6 +33,7 @@ impl MyIface {
             emits_changed_invalidates: 0,
             emits_changed_const: 0,
             emits_changed_false: 0,
+            r#let: 0,
         }
     }
 }
@@ -181,6 +183,12 @@ impl MyIface {
             .unwrap();
     }
 
+    #[instrument]
+    async fn r#type(&self) -> zbus::fdo::Result<String> {
+        debug!("`r#type` called.");
+        Ok("r# prefix method works".into())
+    }
+
     #[cfg(feature = "option-as-array")]
     #[instrument]
     async fn optional_args(&self, arg: Option<&str>) -> zbus::fdo::Result<Option<String>> {
@@ -363,6 +371,9 @@ impl MyIface {
     #[zbus(signal)]
     pub async fn alert_count(emitter: &SignalEmitter<'_>, val: u32) -> zbus::Result<()>;
 
+    #[zbus(signal)]
+    pub async fn r#match(emitter: &SignalEmitter<'_>) -> zbus::Result<()>;
+
     #[instrument]
     #[zbus(property)]
     fn emits_changed_default(&self) -> u32 {
@@ -438,6 +449,21 @@ impl MyIface {
         Ok(())
     }
 
+    #[instrument]
+    #[zbus(property)]
+    fn r#let(&self) -> u32 {
+        debug!("`Let` getter called.");
+        self.r#let
+    }
+
+    #[instrument]
+    #[zbus(property)]
+    fn set_let(&mut self, val: u32) -> zbus::fdo::Result<()> {
+        debug!("`Let` setter called.");
+        self.r#let = val;
+        Ok(())
+    }
+
     async fn never_return(&self) {
         debug!("`NeverReturn` called.");