Skip to content

Commit

Permalink
proc-macros: Support deprecated methods for rpc client (#570)
Browse files Browse the repository at this point in the history
* proc-macros: Fix documentation typo of `rpc_identifier`

* proc-macros: Support deprecated methods for rpc client (#564)

Calling a deprecated method of the RPC client should warn
the user at compile-time.

Extract the `#[deprecated]` macro as is while parsing the
RpcMethod, and pass through the macro to the RPC client
rendering.

* tests/ui: Check deprecated method for rpc client (#564)

To ensure that the test will fail during compilation,
warnings are denied.

Check that the deprecate macro will generate warnings
just for the methods that are utilized.
  • Loading branch information
lexnv authored Nov 21, 2021
1 parent 9c6fd4b commit 9a3c1e9
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 2 deletions.
3 changes: 3 additions & 0 deletions proc-macros/src/render_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,12 @@ impl RpcDescription {
let parameters = self.encode_params(&method.params, &method.param_kind, &method.signature);
// Doc-comment to be associated with the method.
let docs = &method.docs;
// Mark the method as deprecated, if previously declared as so.
let deprecated = &method.deprecated;

let method = quote! {
#docs
#deprecated
async fn #rust_method_name(#rust_method_params) -> #returns {
self.#called_method(#rpc_method_name, #parameters).await
}
Expand Down
20 changes: 18 additions & 2 deletions proc-macros/src/rpc_macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ pub struct RpcMethod {
pub name: String,
pub blocking: bool,
pub docs: TokenStream2,
pub deprecated: TokenStream2,
pub params: Vec<(syn::PatIdent, syn::Type)>,
pub param_kind: ParamKind,
pub returns: Option<syn::Type>,
Expand All @@ -65,6 +66,10 @@ impl RpcMethod {

let sig = method.sig.clone();
let docs = extract_doc_comments(&method.attrs);
let deprecated = match find_attr(&method.attrs, "deprecated") {
Some(attr) => quote!(#attr),
None => quote!(),
};

if blocking && sig.asyncness.is_some() {
return Err(syn::Error::new(sig.span(), "Blocking method must be synchronous"));
Expand All @@ -90,7 +95,18 @@ impl RpcMethod {
// We've analyzed attributes and don't need them anymore.
method.attrs.clear();

Ok(Self { aliases, blocking, name, params, param_kind, returns, signature: method, docs, resources })
Ok(Self {
aliases,
blocking,
name,
params,
param_kind,
returns,
signature: method,
docs,
resources,
deprecated,
})
}
}

Expand Down Expand Up @@ -298,7 +314,7 @@ impl RpcDescription {
/// Based on the namespace, renders the full name of the RPC method/subscription.
/// Examples:
/// For namespace `foo` and method `makeSpam`, result will be `foo_makeSpam`.
/// For no namespace and method `makeSpam` it will be just `makeSpam.
/// For no namespace and method `makeSpam` it will be just `makeSpam`.
pub(crate) fn rpc_identifier<'a>(&self, method: &'a str) -> Cow<'a, str> {
if let Some(ns) = &self.namespace {
format!("{}_{}", ns, method).into()
Expand Down
67 changes: 67 additions & 0 deletions proc-macros/tests/ui/incorrect/rpc/rpc_deprecated_method.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//! Test that calling a deprecated method will generate warnings at compile-time.
// Treat warnings as errors to fail the build.
#![deny(warnings)]

use jsonrpsee::{
proc_macros::rpc,
types::{async_trait, RpcResult},
ws_client::*,
ws_server::WsServerBuilder,
};
use std::net::SocketAddr;

#[rpc(client, server)]
pub trait Deprecated {
// Deprecated method that is called by the client.
#[deprecated(since = "0.5.0", note = "please use `new_method` instead")]
#[method(name = "foo")]
async fn async_method(&self) -> RpcResult<u8>;

// Deprecated methods that are not called should not generate warnings.
#[deprecated(since = "0.5.0", note = "please use `new_method` instead")]
#[method(name = "foo_unused")]
async fn async_method_unused(&self) -> RpcResult<u8>;

// If the method is not marked as deprecated, should not generate warnings.
#[method(name = "bar")]
fn sync_method(&self) -> RpcResult<u8>;
}

pub struct DeprecatedServerImpl;

#[async_trait]
impl DeprecatedServer for DeprecatedServerImpl {
async fn async_method(&self) -> RpcResult<u8> {
Ok(16u8)
}

async fn async_method_unused(&self) -> RpcResult<u8> {
Ok(32u8)
}

fn sync_method(&self) -> RpcResult<u8> {
Ok(64u8)
}
}

pub async fn websocket_server() -> SocketAddr {
let server = WsServerBuilder::default().build("127.0.0.1:0").await.unwrap();
let addr = server.local_addr().unwrap();

server.start(DeprecatedServerImpl.into_rpc()).unwrap();

addr
}

#[tokio::main]
async fn main() {
let server_addr = websocket_server().await;
let server_url = format!("ws://{}", server_addr);
let client = WsClientBuilder::default().build(&server_url).await.unwrap();

// Calling this method should generate an warning.
assert_eq!(client.async_method().await.unwrap(), 16);
// Note: `async_method_unused` is not called, and should not generate warnings.
assert_eq!(client.sync_method().await.unwrap(), 64);
}
12 changes: 12 additions & 0 deletions proc-macros/tests/ui/incorrect/rpc/rpc_deprecated_method.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
error: use of deprecated associated function `DeprecatedClient::async_method`: please use `new_method` instead
--> $DIR/rpc_deprecated_method.rs:64:20
|
64 | assert_eq!(client.async_method().await.unwrap(), 16);
| ^^^^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/rpc_deprecated_method.rs:4:9
|
4 | #![deny(warnings)]
| ^^^^^^^^
= note: `#[deny(deprecated)]` implied by `#[deny(warnings)]`

0 comments on commit 9a3c1e9

Please sign in to comment.