Skip to content

Commit

Permalink
fix(proc macros): generate documentation for trait methods. (#453)
Browse files Browse the repository at this point in the history
* fix(proc macros): generate documentation

Fixes #449

* revert proc macro example

* add test that docs are generated

* use import

* address grumbles

* make the code less ugly

* simplify more

* fix grumbles: check ident before parse

* more grumbles: filter_map -> filter
  • Loading branch information
niklasad1 authored Sep 8, 2021
1 parent ffa504e commit 089aa11
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 14 deletions.
14 changes: 13 additions & 1 deletion proc-macros/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
// DEALINGS IN THE SOFTWARE.

use crate::visitor::{FindAllParams, FindSubscriptionParams};
use proc_macro2::Span;
use proc_macro2::{Span, TokenStream as TokenStream2};
use proc_macro_crate::{crate_name, FoundCrate};
use quote::quote;
use std::collections::HashSet;
Expand Down Expand Up @@ -165,6 +165,18 @@ pub(crate) fn is_option(ty: &syn::Type) -> bool {
false
}

/// Iterates over all Attribute's and parses only the attributes that are doc comments.
///
/// Note that `doc comments` are expanded into `#[doc = "some comment"]`
/// Thus, if the attribute starts with `doc` => it's regarded as a doc comment.
pub(crate) fn extract_doc_comments(attrs: &[syn::Attribute]) -> TokenStream2 {
let docs = attrs.iter().filter(|attr| attr.path.is_ident("doc")).filter(|attr| match attr.parse_meta() {
Ok(syn::Meta::NameValue(meta)) if matches!(&meta.lit, syn::Lit::Str(_)) => true,
_ => false,
});
quote! ( #(#docs)* )
}

#[cfg(test)]
mod tests {
use super::is_option;
Expand Down
8 changes: 4 additions & 4 deletions proc-macros/src/render_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,10 @@ impl RpcDescription {
};

// Doc-comment to be associated with the method.
let doc_comment = format!("Invokes the RPC method `{}`.", rpc_method_name);
let docs = &method.docs;

let method = quote! {
#[doc = #doc_comment]
#docs
async fn #rust_method_name(#rust_method_params) -> #returns {
self.#called_method(#rpc_method_name, #parameters).await
}
Expand Down Expand Up @@ -151,10 +151,10 @@ impl RpcDescription {
};

// Doc-comment to be associated with the method.
let doc_comment = format!("Subscribes to the RPC method `{}`.", rpc_sub_name);
let docs = &sub.docs;

let method = quote! {
#[doc = #doc_comment]
#docs
async fn #rust_method_name(#rust_method_params) -> #returns {
self.subscribe(#rpc_sub_name, #parameters, #rpc_unsub_name).await
}
Expand Down
23 changes: 17 additions & 6 deletions proc-macros/src/render_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ impl RpcDescription {

let method_impls = self.render_methods()?;
let into_rpc_impl = self.render_into_rpc()?;

let async_trait = self.jrps_server_item(quote! { types::__reexports::async_trait });

// Doc-comment to be associated with the server.
Expand All @@ -58,14 +57,26 @@ impl RpcDescription {
}

fn render_methods(&self) -> Result<TokenStream2, syn::Error> {
let methods = self.methods.iter().map(|method| &method.signature);
let methods = self.methods.iter().map(|method| {
let docs = &method.docs;
let method_sig = &method.signature;
quote! {
#docs
#method_sig
}
});

let subscription_sink_ty = self.jrps_server_item(quote! { SubscriptionSink });
let subscriptions = self.subscriptions.iter().cloned().map(|mut sub| {
let subscriptions = self.subscriptions.iter().map(|sub| {
let docs = &sub.docs;
let subscription_sink_ty = self.jrps_server_item(quote! { SubscriptionSink });
// Add `SubscriptionSink` as the second input parameter to the signature.
let subscription_sink: syn::FnArg = syn::parse_quote!(subscription_sink: #subscription_sink_ty);
sub.signature.sig.inputs.insert(1, subscription_sink);
sub.signature
let mut sub_sig = sub.signature.clone();
sub_sig.sig.inputs.insert(1, subscription_sink);
quote! {
#docs
#sub_sig
}
});

Ok(quote! {
Expand Down
10 changes: 7 additions & 3 deletions proc-macros/src/rpc_macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

//! Declaration of the JSON RPC generator procedural macros.
use crate::{attributes, respan::Respan};
use crate::{attributes, helpers::extract_doc_comments, respan::Respan};

use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
Expand All @@ -35,6 +35,7 @@ use syn::Attribute;
#[derive(Debug, Clone)]
pub struct RpcMethod {
pub name: String,
pub docs: TokenStream2,
pub params: Vec<(syn::PatIdent, syn::Type)>,
pub returns: Option<syn::Type>,
pub signature: syn::TraitItemMethod,
Expand All @@ -46,6 +47,7 @@ impl RpcMethod {
let attributes = attributes::Method::from_attributes(&method.attrs).respan(&method.attrs.first())?;
let sig = method.sig.clone();
let name = attributes.name.value();
let docs = extract_doc_comments(&method.attrs);
let aliases = attributes.aliases.map(|a| a.value().split(',').map(Into::into).collect()).unwrap_or_default();
let params: Vec<_> = sig
.inputs
Expand All @@ -67,13 +69,14 @@ impl RpcMethod {
// We've analyzed attributes and don't need them anymore.
method.attrs.clear();

Ok(Self { aliases, name, params, returns, signature: method })
Ok(Self { aliases, name, params, returns, signature: method, docs })
}
}

#[derive(Debug, Clone)]
pub struct RpcSubscription {
pub name: String,
pub docs: TokenStream2,
pub unsubscribe: String,
pub params: Vec<(syn::PatIdent, syn::Type)>,
pub item: syn::Type,
Expand All @@ -87,6 +90,7 @@ impl RpcSubscription {
let attributes = attributes::Subscription::from_attributes(&sub.attrs).respan(&sub.attrs.first())?;
let sig = sub.sig.clone();
let name = attributes.name.value();
let docs = extract_doc_comments(&sub.attrs);
let unsubscribe = build_unsubscribe_method(&name);
let item = attributes.item;
let aliases = attributes.aliases.map(|a| a.value().split(',').map(Into::into).collect()).unwrap_or_default();
Expand All @@ -107,7 +111,7 @@ impl RpcSubscription {
// We've analyzed attributes and don't need them anymore.
sub.attrs.clear();

Ok(Self { name, unsubscribe, unsubscribe_aliases, params, item, signature: sub, aliases })
Ok(Self { name, unsubscribe, unsubscribe_aliases, params, item, signature: sub, aliases, docs })
}
}

Expand Down
18 changes: 18 additions & 0 deletions proc-macros/tests/ui/correct/rpc_deny_missing_docs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//! Test to check that the proc macros actually generates documentation.
#![deny(missing_docs)]

use jsonrpsee::proc_macros::rpc;

#[rpc(client, server)]
pub trait ApiWithoutDocumentation {
/// Async method.
#[method(name = "foo")]
async fn async_method(&self) -> jsonrpsee::types::JsonRpcResult<u8>;

/// Subscription docs.
#[subscription(name = "sub", item = String)]
fn sub(&self);
}

fn main() {}

0 comments on commit 089aa11

Please sign in to comment.