From 76d7671c8a7fa624947e3523d635f0608aae2d07 Mon Sep 17 00:00:00 2001 From: Griffin Smith Date: Sun, 6 Nov 2022 10:46:56 -0500 Subject: 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 --- tvix/eval/builtin-macros/src/lib.rs | 40 +++++++++++++++++++++++++++++++-- tvix/eval/builtin-macros/tests/tests.rs | 4 ++++ 2 files changed, 42 insertions(+), 2 deletions(-) (limited to 'tvix/eval/builtin-macros') diff --git a/tvix/eval/builtin-macros/src/lib.rs b/tvix/eval/builtin-macros/src/lib.rs index 8276e421c5d8..e815749ec62c 100644 --- a/tvix/eval/builtin-macros/src/lib.rs +++ b/tvix/eval/builtin-macros/src/lib.rs @@ -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 { + // 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 { + 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::(attr.tokens.clone()).ok()) + .map(|docstring| docstring.doc) +} + /// Mark the annotated module as a module for defining Nix builtins. /// /// A function `fn builtins() -> Vec` 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, vm: &mut crate::internal::VM| { #(let #reversed_args = args.pop().unwrap();)* #fn_name(vm, #(#args),*) diff --git a/tvix/eval/builtin-macros/tests/tests.rs b/tvix/eval/builtin-macros/tests/tests.rs index d07020f69bb7..b270594b0f33 100644 --- a/tvix/eval/builtin-macros/tests/tests.rs +++ b/tvix/eval/builtin-macros/tests/tests.rs @@ -7,6 +7,7 @@ mod builtins { use tvix_eval::internal::VM; use tvix_eval::{ErrorKind, Value}; + /// Test docstring #[builtin("identity")] pub fn builtin_identity(_vm: &mut VM, x: Value) -> Result { Ok(x) @@ -22,4 +23,7 @@ mod builtins { fn builtins() { let builtins = builtins::builtins(); assert_eq!(builtins.len(), 2); + + let identity = builtins.iter().find(|b| b.name() == "identity").unwrap(); + assert_eq!(identity.documentation(), Some(" Test docstring")); } -- cgit 1.4.1