Skip to content

Commit

Permalink
Support showing readonly fields as pub in rustdoc
Browse files Browse the repository at this point in the history
  • Loading branch information
dtolnay committed Apr 21, 2019
1 parent 316922b commit 11457e3
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 3 deletions.
39 changes: 39 additions & 0 deletions src/args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Syntax:
//
// #[readonly::make(doc = mycratename_doc_cfg)]
// pub struct MyStruct {...}
//
// If provided, the `doc` attribute will make readonly fields fully pub, meaning
// writeable, if the given cfg is set. This way the readonly fields end up
// visible in rustdoc. The caller is responsible for providing a doc comment
// that explains that the field is readonly.
//
// Intended usage is by placing the following in Cargo.toml:
//
// [package.metadata.docs.rs]
// rustdoc-args = ["--cfg", "mycratename_doc_cfg"]

use syn::parse::{Parse, ParseStream, Result};
use syn::{Ident, Token};

mod kw {
syn::custom_keyword!(doc);
}

pub struct Args {
pub doc_cfg: Option<Ident>,
}

impl Parse for Args {
fn parse(input: ParseStream) -> Result<Self> {
let doc_cfg = if input.is_empty() {
None
} else {
input.parse::<kw::doc>()?;
input.parse::<Token![=]>()?;
Some(input.parse()?)
};

Ok(Args { doc_cfg })
}
}
19 changes: 18 additions & 1 deletion src/expand.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::args::Args;
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::visit_mut::{self, VisitMut};
Expand All @@ -8,7 +9,7 @@ use syn::{

type Punctuated = syn::punctuated::Punctuated<Field, Token![,]>;

pub fn readonly(input: DeriveInput) -> Result<TokenStream> {
pub fn readonly(args: Args, input: DeriveInput) -> Result<TokenStream> {
let call_site = Span::call_site();

match &input.data {
Expand All @@ -23,8 +24,16 @@ pub fn readonly(input: DeriveInput) -> Result<TokenStream> {
}

let mut input = input;

let indices = find_and_strip_readonly_attrs(&mut input);

let original_input = args.doc_cfg.as_ref().map(|doc_cfg| {
quote! {
#[cfg(all(#doc_cfg, rustdoc))]
#input
}
});

if !has_defined_repr(&input) {
input.attrs.push(parse_quote!(#[repr(C)]));
}
Expand Down Expand Up @@ -58,11 +67,19 @@ pub fn readonly(input: DeriveInput) -> Result<TokenStream> {
readonly.ident = Ident::new(&format!("ReadOnly{}", input.ident), call_site);
let readonly_ident = &readonly.ident;

if let Some(doc_cfg) = args.doc_cfg {
let not_doc_cfg = parse_quote!(#[cfg(not(all(#doc_cfg, rustdoc)))]);
input.attrs.insert(0, not_doc_cfg);
}

Ok(quote! {
#original_input

#input

#readonly

#[doc(hidden)]
impl #impl_generics core::ops::Deref for #ident #ty_generics #where_clause {
type Target = #readonly_ident #ty_generics;

Expand Down
7 changes: 5 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,18 +62,21 @@

extern crate proc_macro;

mod args;
mod expand;

use crate::args::Args;
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};

#[proc_macro_attribute]
pub fn make(_args: TokenStream, tokens: TokenStream) -> TokenStream {
pub fn make(args: TokenStream, tokens: TokenStream) -> TokenStream {
let original = tokens.clone();
let args = parse_macro_input!(args as Args);
let input = parse_macro_input!(tokens as DeriveInput);

expand::readonly(input)
expand::readonly(args, input)
.unwrap_or_else(|e| {
let original = proc_macro2::TokenStream::from(original);
let compile_error = e.to_compile_error();
Expand Down

0 comments on commit 11457e3

Please sign in to comment.