culpa-macros-1.0.2/.cargo_vcs_info.json0000644000000001440000000000100134210ustar { "git": { "sha1": "033a8706be7a9c1ea09cc1ed6c6d4c0b8b4136b9" }, "path_in_vcs": "macros" }culpa-macros-1.0.2/Cargo.toml0000644000000015570000000000100114300ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "culpa-macros" version = "1.0.2" description = "Internal implementation details of `culpa`" license = "MIT OR Apache-2.0" repository = "https://github.com/Nullus157/culpa" [lib] proc-macro = true [dependencies.proc-macro2] version = "1.0.63" [dependencies.quote] version = "1.0.29" [dependencies.syn] version = "2.0.22" features = [ "fold", "full", "parsing", ] culpa-macros-1.0.2/Cargo.toml.orig000064400000000000000000000005561046102023000151070ustar 00000000000000[package] name = "culpa-macros" version = "1.0.2" edition = "2018" license = "MIT OR Apache-2.0" description = "Internal implementation details of `culpa`" repository = "https://github.com/Nullus157/culpa" [lib] proc-macro = true [dependencies] quote = "1.0.29" proc-macro2 = "1.0.63" [dependencies.syn] features = ["fold", "full", "parsing"] version = "2.0.22" culpa-macros-1.0.2/src/args.rs000064400000000000000000000071571046102023000143150ustar 00000000000000// The Args type parses the arguments to the `#[throws]` macro. // // It is also responsible for transforming the return type by injecting // the return type and the error type into the wrapper type. use proc_macro2::Span; use syn::parse::{Parse, ParseStream, Result}; use syn::{GenericArgument, Path, PathArguments, ReturnType, Token, Type}; const WRAPPER_MUST_BE_PATH: &str = "Wrapper type must be a normal path type"; pub struct Args { error: Option, wrapper: Option, } impl Args { pub fn ret(&mut self, ret: ReturnType) -> ReturnType { let (arrow, ret) = match ret { ReturnType::Default => (arrow(), unit()), ReturnType::Type(arrow, ty) => (arrow, *ty), }; ReturnType::Type(arrow, Box::new(self.inject_to_wrapper(ret))) } fn inject_to_wrapper(&mut self, ret: Type) -> Type { if let Some(Type::Path(mut wrapper)) = self.wrapper.take() { let types = if let Some(error) = self.error.take() { vec![ret, error].into_iter().map(GenericArgument::Type) } else { vec![ret].into_iter().map(GenericArgument::Type) }; match innermost_path_arguments(&mut wrapper.path) { args @ &mut PathArguments::None => { *args = PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { colon2_token: None, lt_token: Token![<](Span::call_site()), args: types.collect(), gt_token: Token![>](Span::call_site()), }); } PathArguments::AngleBracketed(args) => args.args.extend(types), _ => panic!("{}", WRAPPER_MUST_BE_PATH), } Type::Path(wrapper) } else { panic!("{}", WRAPPER_MUST_BE_PATH) } } } impl Parse for Args { fn parse(input: ParseStream) -> Result { if input.is_empty() { return Ok(Args { error: Some(default_error()), wrapper: Some(result()), }); } let error = match input.peek(Token![as]) { true => None, false => { let error = input.parse()?; Some(match error { Type::Infer(_) => default_error(), _ => error, }) } }; let wrapper = Some(match input.parse::().is_ok() { true => input.parse()?, false => result(), }); Ok(Args { error, wrapper }) } } fn innermost_path_arguments(path: &mut Path) -> &mut PathArguments { let arguments = &mut path .segments .last_mut() .expect(WRAPPER_MUST_BE_PATH) .arguments; match arguments { PathArguments::None => arguments, PathArguments::AngleBracketed(args) => { match args.args.last_mut() { Some(GenericArgument::Type(Type::Path(inner))) => { innermost_path_arguments(&mut inner.path) } // Bizarre cases like `#[throw(_ as MyTryType<'a>)]` just not supported currently _ => panic!("Certain strange wrapper types not supported"), } } _ => panic!("{}", WRAPPER_MUST_BE_PATH), } } fn arrow() -> syn::token::RArrow { Token![->](Span::call_site()) } fn unit() -> Type { syn::parse_str("()").unwrap() } fn result() -> Type { syn::parse_str("::core::result::Result").unwrap() } fn default_error() -> Type { syn::parse_str("Error").unwrap() } culpa-macros-1.0.2/src/lib.rs000064400000000000000000000007741046102023000141250ustar 00000000000000extern crate proc_macro; mod args; mod throws; use proc_macro::TokenStream; use args::Args; use throws::Throws; #[proc_macro_attribute] pub fn throws(args: TokenStream, input: TokenStream) -> TokenStream { let args = syn::parse_macro_input!(args as Args); Throws::new(Some(args)).fold(input) } #[proc_macro_attribute] pub fn try_fn(args: TokenStream, input: TokenStream) -> TokenStream { assert!(args.to_string() == "", "try_fn does not take arguments"); Throws::new(None).fold(input) } culpa-macros-1.0.2/src/throws.rs000064400000000000000000000114411046102023000146760ustar 00000000000000// This module implements the Throws folder. // // The Throws folder actually visits the item being processed and performs two // processes: // - It ok wraps return expressions and inserts terminal Ok(())s. // - It delegates return type rewriting to the Args type. use proc_macro::TokenStream; use syn::fold::Fold; use crate::Args; pub struct Throws { args: Option, outer_fn: bool, return_type: syn::Type, } impl Throws { pub fn new(args: Option) -> Throws { Throws { args, outer_fn: true, return_type: syn::parse_quote!(()), } } pub fn fold(&mut self, input: TokenStream) -> TokenStream { if let Ok(item_fn) = syn::parse(input.clone()) { let item_fn = self.fold_item_fn(item_fn); quote::quote!(#item_fn).into() } else if let Ok(impl_item_fn) = syn::parse(input.clone()) { let impl_item_fn = self.fold_impl_item_fn(impl_item_fn); quote::quote!(#impl_item_fn).into() } else if let Ok(trait_item_fn) = syn::parse(input) { let trait_item_fn = self.fold_trait_item_fn(trait_item_fn); quote::quote!(#trait_item_fn).into() } else { panic!("#[throws] attribute can only be applied to functions and methods") } } } impl Fold for Throws { fn fold_item_fn(&mut self, i: syn::ItemFn) -> syn::ItemFn { if !self.outer_fn { return i; } let sig = syn::Signature { output: self.fold_return_type(i.sig.output), ..i.sig }; self.outer_fn = false; let inner = self.fold_block(*i.block); let block = Box::new(make_fn_block(&self.return_type, &inner)); syn::ItemFn { sig, block, ..i } } fn fold_impl_item_fn(&mut self, i: syn::ImplItemFn) -> syn::ImplItemFn { if !self.outer_fn { return i; } let sig = syn::Signature { output: self.fold_return_type(i.sig.output), ..i.sig }; self.outer_fn = false; let inner = self.fold_block(i.block); let block = make_fn_block(&self.return_type, &inner); syn::ImplItemFn { sig, block, ..i } } fn fold_trait_item_fn(&mut self, mut i: syn::TraitItemFn) -> syn::TraitItemFn { if !self.outer_fn { return i; } let sig = syn::Signature { output: self.fold_return_type(i.sig.output), ..i.sig }; self.outer_fn = false; let default = i.default.take().map(|block| { let inner = self.fold_block(block); make_fn_block(&self.return_type, &inner) }); syn::TraitItemFn { sig, default, ..i } } fn fold_expr_closure(&mut self, i: syn::ExprClosure) -> syn::ExprClosure { i // TODO } fn fold_expr_async(&mut self, i: syn::ExprAsync) -> syn::ExprAsync { i // TODO } fn fold_return_type(&mut self, i: syn::ReturnType) -> syn::ReturnType { if !self.outer_fn { return i; } let return_type = match &mut self.args { Some(args) => args.ret(i), None => i, }; let ty = match &return_type { syn::ReturnType::Type(_, ty) => (**ty).clone(), syn::ReturnType::Default => syn::Type::Infer(syn::parse_quote!(_)), }; struct ImplTraitToInfer; impl Fold for ImplTraitToInfer { fn fold_type(&mut self, i: syn::Type) -> syn::Type { match i { syn::Type::ImplTrait(_) => syn::Type::Infer(syn::parse_quote!(_)), i => syn::fold::fold_type(self, i), } } } self.return_type = ImplTraitToInfer.fold_type(ty); return_type } fn fold_expr_return(&mut self, i: syn::ExprReturn) -> syn::ExprReturn { let ok = match &i.expr { Some(expr) => ok(&self.return_type, expr), None => ok_unit(&self.return_type), }; syn::ExprReturn { expr: Some(Box::new(ok)), ..i } } } fn make_fn_block(ty: &syn::Type, inner: &syn::Block) -> syn::Block { let mut block: syn::Block = syn::parse2(quote::quote! {{ #[allow(clippy::diverging_sub_expression)] { let __ret = { #inner }; #[allow(unreachable_code)] <#ty as ::culpa::__internal::_Succeed>::from_ok(__ret) } }}) .unwrap(); block.brace_token = inner.brace_token; block } fn ok(ty: &syn::Type, expr: &syn::Expr) -> syn::Expr { syn::parse2(quote::quote!(<#ty as ::culpa::__internal::_Succeed>::from_ok(#expr))).unwrap() } fn ok_unit(ty: &syn::Type) -> syn::Expr { syn::parse2(quote::quote!(<#ty as ::culpa::__internal::_Succeed>::from_ok(()))).unwrap() }