Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/query-group-macro/src/lib.rs')
| -rw-r--r-- | crates/query-group-macro/src/lib.rs | 256 |
1 files changed, 0 insertions, 256 deletions
diff --git a/crates/query-group-macro/src/lib.rs b/crates/query-group-macro/src/lib.rs deleted file mode 100644 index 9f7459066d..0000000000 --- a/crates/query-group-macro/src/lib.rs +++ /dev/null @@ -1,256 +0,0 @@ -//! A macro that mimics the old Salsa-style `#[query_group]` macro. - -use std::vec; - -use proc_macro::TokenStream; -use proc_macro2::Span; -use queries::{Queries, TrackedQuery, Transparent}; -use quote::{ToTokens, format_ident, quote}; -use syn::parse::ParseStream; -use syn::spanned::Spanned; -use syn::visit_mut::VisitMut; -use syn::{Attribute, FnArg, ItemTrait, Path, TraitItem, parse_quote, parse_quote_spanned}; - -mod queries; - -#[proc_macro_attribute] -pub fn query_group(args: TokenStream, input: TokenStream) -> TokenStream { - match query_group_impl(args, input.clone()) { - Ok(tokens) => tokens, - Err(e) => token_stream_with_error(input, e), - } -} - -struct SalsaAttr { - name: String, - tts: TokenStream, - span: Span, -} - -impl std::fmt::Debug for SalsaAttr { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(fmt, "{:?}", self.name) - } -} - -impl TryFrom<syn::Attribute> for SalsaAttr { - type Error = syn::Attribute; - - fn try_from(attr: syn::Attribute) -> Result<SalsaAttr, syn::Attribute> { - if is_not_salsa_attr_path(attr.path()) { - return Err(attr); - } - - let span = attr.span(); - - let name = attr.path().segments[1].ident.to_string(); - let tts = match attr.meta { - syn::Meta::Path(path) => path.into_token_stream(), - syn::Meta::List(ref list) => { - let tts = list - .into_token_stream() - .into_iter() - .skip(attr.path().to_token_stream().into_iter().count()); - proc_macro2::TokenStream::from_iter(tts) - } - syn::Meta::NameValue(nv) => nv.into_token_stream(), - } - .into(); - - Ok(SalsaAttr { name, tts, span }) - } -} - -fn is_not_salsa_attr_path(path: &syn::Path) -> bool { - path.segments.first().map(|s| s.ident != "salsa").unwrap_or(true) || path.segments.len() != 2 -} - -fn filter_attrs(attrs: Vec<Attribute>) -> (Vec<Attribute>, Vec<SalsaAttr>) { - let mut other = vec![]; - let mut salsa = vec![]; - // Leave non-salsa attributes untouched. These are - // attributes that don't start with `salsa::` or don't have - // exactly two segments in their path. - for attr in attrs { - match SalsaAttr::try_from(attr) { - Ok(it) => salsa.push(it), - Err(it) => other.push(it), - } - } - (other, salsa) -} - -#[derive(Debug, Clone, PartialEq, Eq)] -enum QueryKind { - TrackedWithSalsaStruct, - Transparent, -} - -pub(crate) fn query_group_impl( - _args: proc_macro::TokenStream, - input: proc_macro::TokenStream, -) -> Result<proc_macro::TokenStream, syn::Error> { - let mut item_trait = syn::parse::<ItemTrait>(input)?; - - let supertraits = &item_trait.supertraits; - - let db_attr: Attribute = parse_quote! { - #[salsa_macros::db] - }; - item_trait.attrs.push(db_attr); - - let trait_name_ident = &item_trait.ident.clone(); - let input_struct_name = format_ident!("{}Data", trait_name_ident); - let create_data_ident = format_ident!("create_data_{}", trait_name_ident); - - let mut trait_methods = vec![]; - - for item in &mut item_trait.items { - if let syn::TraitItem::Fn(method) = item { - let signature = &method.sig; - - let (_attrs, salsa_attrs) = filter_attrs(method.attrs.clone()); - - let mut query_kind = QueryKind::TrackedWithSalsaStruct; - let mut invoke = None; - - let params: Vec<FnArg> = signature.inputs.clone().into_iter().collect(); - let pat_and_tys = params - .into_iter() - .filter(|fn_arg| matches!(fn_arg, FnArg::Typed(_))) - .map(|fn_arg| match fn_arg { - FnArg::Typed(pat_type) => pat_type, - FnArg::Receiver(_) => unreachable!("this should have been filtered out"), - }) - .collect::<Vec<syn::PatType>>(); - - for SalsaAttr { name, tts, span } in salsa_attrs { - match name.as_str() { - "invoke" => { - let path = syn::parse::<Parenthesized<Path>>(tts)?; - invoke = Some(path.0.clone()); - if query_kind != QueryKind::Transparent { - query_kind = QueryKind::TrackedWithSalsaStruct; - } - } - "tracked" if method.default.is_some() => { - query_kind = QueryKind::TrackedWithSalsaStruct; - } - "transparent" => { - query_kind = QueryKind::Transparent; - } - _ => return Err(syn::Error::new(span, format!("unknown attribute `{name}`"))), - } - } - - let syn::ReturnType::Type(_, _) = signature.output.clone() else { - return Err(syn::Error::new(signature.span(), "Queries must have a return type")); - }; - - if let Some(block) = &mut method.default { - SelfToDbRewriter.visit_block_mut(block); - } - - match (query_kind, invoke) { - (QueryKind::TrackedWithSalsaStruct, invoke) => { - let method = TrackedQuery { - trait_name: trait_name_ident.clone(), - signature: signature.clone(), - pat_and_tys: pat_and_tys.clone(), - invoke, - default: method.default.take(), - }; - - trait_methods.push(Queries::TrackedQuery(method)) - } - (QueryKind::Transparent, invoke) => { - let method = Transparent { - signature: method.sig.clone(), - pat_and_tys: pat_and_tys.clone(), - invoke, - default: method.default.take(), - }; - trait_methods.push(Queries::Transparent(method)); - } - } - } - } - - let input_struct = quote! { - #[salsa_macros::input] - pub(crate) struct #input_struct_name {} - }; - - let create_data_method = quote! { - #[allow(non_snake_case)] - #[salsa_macros::tracked] - fn #create_data_ident(db: &dyn #trait_name_ident) -> #input_struct_name { - #input_struct_name::new(db) - } - }; - - let trait_impl = quote! { - #[salsa_macros::db] - impl<DB> #trait_name_ident for DB - where - DB: #supertraits, - { - #(#trait_methods)* - } - }; - RemoveAttrsFromTraitMethods.visit_item_trait_mut(&mut item_trait); - - let out = quote! { - #item_trait - - #trait_impl - - #input_struct - - #create_data_method - } - .into(); - - Ok(out) -} - -/// Parenthesis helper -pub(crate) struct Parenthesized<T>(pub(crate) T); - -impl<T> syn::parse::Parse for Parenthesized<T> -where - T: syn::parse::Parse, -{ - fn parse(input: ParseStream<'_>) -> syn::Result<Self> { - let content; - syn::parenthesized!(content in input); - content.parse::<T>().map(Parenthesized) - } -} - -struct RemoveAttrsFromTraitMethods; - -impl VisitMut for RemoveAttrsFromTraitMethods { - fn visit_item_trait_mut(&mut self, i: &mut syn::ItemTrait) { - for item in &mut i.items { - if let TraitItem::Fn(trait_item_fn) = item { - trait_item_fn.attrs = vec![]; - } - } - } -} - -pub(crate) fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenStream { - tokens.extend(TokenStream::from(error.into_compile_error())); - tokens -} - -struct SelfToDbRewriter; - -impl VisitMut for SelfToDbRewriter { - fn visit_expr_path_mut(&mut self, i: &mut syn::ExprPath) { - if i.path.is_ident("self") { - i.path = parse_quote_spanned!(i.path.span() => db); - } - } -} |