feat(tvix/eval): Add docstrings as documentation for builtins

Add a new `documentation: Option<&'static str>` field to Builtin, and
populate it in the `#[builtins]` macro with the docstring of the builtin
function, if any.

Change-Id: Ic68fdf9b314d15a780731974234e2ae43f6a44b0
Reviewed-on: https://cl.tvl.fyi/c/depot/+/7205
Tested-by: BuildkiteCI
Reviewed-by: tazjin <tazjin@tvl.su>
This commit is contained in:
Griffin Smith 2022-11-06 10:46:56 -05:00 committed by grfn
parent a1015ba1d7
commit 76d7671c8a
5 changed files with 54 additions and 2 deletions

View file

@ -2,11 +2,12 @@ extern crate proc_macro;
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::{quote_spanned, ToTokens};
use quote::{quote, quote_spanned, ToTokens};
use syn::parse::Parse;
use syn::spanned::Spanned;
use syn::{
parse_macro_input, parse_quote, FnArg, Ident, Item, ItemMod, LitStr, Pat, PatIdent, PatType,
parse2, parse_macro_input, parse_quote, Attribute, FnArg, Ident, Item, ItemMod, LitStr, Pat,
PatIdent, PatType, Token,
};
struct BuiltinArgs {
@ -21,6 +22,35 @@ impl Parse for BuiltinArgs {
}
}
fn extract_docstring(attrs: &[Attribute]) -> Option<LitStr> {
// Rust docstrings are transparently written pre-macro expansion into an attribute that looks
// like:
//
// #[doc = "docstring here"]
#[allow(dead_code)]
#[derive(Debug)]
struct Docstring {
eq: Token![=],
doc: LitStr,
}
impl Parse for Docstring {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
Ok(Self {
eq: input.parse()?,
doc: input.parse()?,
})
}
}
attrs
.iter()
.filter(|attr| attr.path.get_ident().into_iter().any(|id| id == "doc"))
.find_map(|attr| parse2::<Docstring>(attr.tokens.clone()).ok())
.map(|docstring| docstring.doc)
}
/// Mark the annotated module as a module for defining Nix builtins.
///
/// A function `fn builtins() -> Vec<Builtin>` will be defined within the annotated module,
@ -144,10 +174,16 @@ pub fn builtins(_args: TokenStream, item: TokenStream) -> TokenStream {
let mut reversed_args = args.clone();
reversed_args.reverse();
let docstring = match extract_docstring(&f.attrs) {
Some(docs) => quote!(Some(#docs)),
None => quote!(None),
};
builtins.push(quote_spanned! { builtin_attr.span() => {
crate::internal::Builtin::new(
#name,
&[#(#builtin_arguments),*],
#docstring,
|mut args: Vec<crate::Value>, vm: &mut crate::internal::VM| {
#(let #reversed_args = args.pop().unwrap();)*
#fn_name(vm, #(#args),*)