From af88321fc14a95b3613ec11a7f665db3c468c944 Mon Sep 17 00:00:00 2001 From: HampusM Date: Sun, 1 Oct 2023 21:15:00 +0200 Subject: refactor: remove impossible unwrap in injectable macro This unwrap couldn't possibly be Err and can be removed by creating method call expressions by hand --- macros/src/injectable/implementation.rs | 139 +++++++++++++++++++------------- macros/src/lib.rs | 1 + macros/src/util/mod.rs | 1 + macros/src/util/syn_ext.rs | 114 ++++++++++++++++++++++++++ macros/src/util/syn_path.rs | 26 ++++++ 5 files changed, 227 insertions(+), 54 deletions(-) create mode 100644 macros/src/util/syn_ext.rs (limited to 'macros/src') diff --git a/macros/src/injectable/implementation.rs b/macros/src/injectable/implementation.rs index 20c0d86..e2bcd3e 100644 --- a/macros/src/injectable/implementation.rs +++ b/macros/src/injectable/implementation.rs @@ -1,15 +1,33 @@ -use std::error::Error; - use proc_macro2::{Ident, Span}; use quote::{format_ident, quote, ToTokens}; use syn::spanned::Spanned; -use syn::{parse2, ExprMethodCall, FnArg, ImplItemMethod, ItemImpl, ReturnType, Type}; +use syn::{ + Expr, + ExprCall, + ExprLit, + ExprMethodCall, + ExprPath, + FnArg, + GenericMethodArgument, + ImplItemMethod, + ItemImpl, + MethodTurbofish, + ReturnType, + Type, +}; use crate::injectable::dependency::{DependencyError, IDependency}; use crate::util::error::diagnostic_error_enum; use crate::util::item_impl::find_impl_method_by_name_mut; use crate::util::string::camelcase_to_snakecase; -use crate::util::syn_path::SynPathExt; +use crate::util::syn_ext::{ + ExprCallExt, + ExprLitExt, + ExprMethodCallExt, + ExprPathExt, + MethodTurbofishExt, +}; +use crate::util::syn_path::{syn_path, SynPathExt}; const DI_CONTAINER_VAR_NAME: &str = "di_container"; const DEPENDENCY_HISTORY_VAR_NAME: &str = "dependency_history"; @@ -152,8 +170,7 @@ impl InjectableImpl is_async, &di_container_var, &dependency_history_var, - ) - .unwrap(); + ); let injectable_impl = if is_async { self.expand_async_impl( @@ -311,7 +328,7 @@ impl InjectableImpl is_async: bool, di_container_var: &Ident, dependency_history_var: &Ident, - ) -> Result, Box> + ) -> Vec { dependencies .iter() @@ -326,27 +343,49 @@ impl InjectableImpl .collect() } + fn create_binding_options(dependency: &Dep) -> Expr + { + let binding_options_new = Expr::Call(ExprCall::new( + Expr::Path(ExprPath::new(syn_path!( + syrette::di_container::BindingOptions::new + ))), + [], + )); + + match dependency.get_name() { + Some(name) => Expr::MethodCall(ExprMethodCall::new( + binding_options_new, + format_ident!("name"), + [Expr::Lit(ExprLit::new(name.clone()))], + )), + None => binding_options_new, + } + } + fn create_single_get_dep_method_call( dependency: &Dep, is_async: bool, di_container_var: &Ident, dependency_history_var: &Ident, - ) -> Result> + ) -> proc_macro2::TokenStream { let dep_interface = dependency.get_interface(); - let maybe_name_fn = dependency - .get_name() - .as_ref() - .map(|name| quote! { .name(#name) }); - - let method_call = parse2::(quote! { - #di_container_var.get_bound::<#dep_interface>( - #dependency_history_var.clone(), - syrette::di_container::BindingOptions::new() - #maybe_name_fn - ) - })?; + let method_call = ExprMethodCall::new( + Expr::Path(ExprPath::new(di_container_var.clone().into())), + format_ident!("get_bound"), + [ + Expr::MethodCall(ExprMethodCall::new( + Expr::Path(ExprPath::new(dependency_history_var.clone().into())), + format_ident!("clone"), + [], + )), + Self::create_binding_options(dependency), + ], + ) + .with_turbofish(MethodTurbofish::new([GenericMethodArgument::Type( + dep_interface.clone(), + )])); let ptr_name = dependency.get_ptr().to_string(); @@ -367,7 +406,7 @@ impl InjectableImpl let dep_interface_str = dep_interface.to_token_stream().to_string(); - Ok(quote! { + quote! { #do_method_call .map_err(|err| #resolve_failed_error { reason: Box::new(err), @@ -378,7 +417,7 @@ impl InjectableImpl reason: err, dependency_name: #dep_interface_str })? - }) + } } fn build_dependencies( @@ -793,14 +832,12 @@ mod tests let di_container_var_ident = format_ident!("{}", DI_CONTAINER_VAR_NAME); let dep_history_var_ident = format_ident!("{}", DEPENDENCY_HISTORY_VAR_NAME); - let output = - InjectableImpl::::create_single_get_dep_method_call( - &mock_dependency, - false, - &format_ident!("{}", DI_CONTAINER_VAR_NAME), - &format_ident!("{}", DEPENDENCY_HISTORY_VAR_NAME), - ) - .unwrap(); + let output = InjectableImpl::::create_single_get_dep_method_call( + &mock_dependency, + false, + &format_ident!("{}", DI_CONTAINER_VAR_NAME), + &format_ident!("{}", DEPENDENCY_HISTORY_VAR_NAME), + ); assert_eq!( parse2::(output).unwrap(), @@ -847,14 +884,12 @@ mod tests let di_container_var_ident = format_ident!("{}", DI_CONTAINER_VAR_NAME); let dep_history_var_ident = format_ident!("{}", DEPENDENCY_HISTORY_VAR_NAME); - let output = - InjectableImpl::::create_single_get_dep_method_call( - &mock_dependency, - false, - &format_ident!("{}", DI_CONTAINER_VAR_NAME), - &format_ident!("{}", DEPENDENCY_HISTORY_VAR_NAME), - ) - .unwrap(); + let output = InjectableImpl::::create_single_get_dep_method_call( + &mock_dependency, + false, + &format_ident!("{}", DI_CONTAINER_VAR_NAME), + &format_ident!("{}", DEPENDENCY_HISTORY_VAR_NAME), + ); assert_eq!( parse2::(output).unwrap(), @@ -899,14 +934,12 @@ mod tests let di_container_var_ident = format_ident!("{}", DI_CONTAINER_VAR_NAME); let dep_history_var_ident = format_ident!("{}", DEPENDENCY_HISTORY_VAR_NAME); - let output = - InjectableImpl::::create_single_get_dep_method_call( - &mock_dependency, - true, - &format_ident!("{}", DI_CONTAINER_VAR_NAME), - &format_ident!("{}", DEPENDENCY_HISTORY_VAR_NAME), - ) - .unwrap(); + let output = InjectableImpl::::create_single_get_dep_method_call( + &mock_dependency, + true, + &format_ident!("{}", DI_CONTAINER_VAR_NAME), + &format_ident!("{}", DEPENDENCY_HISTORY_VAR_NAME), + ); assert_eq!( parse2::(output).unwrap(), @@ -954,14 +987,12 @@ mod tests let di_container_var_ident = format_ident!("{}", DI_CONTAINER_VAR_NAME); let dep_history_var_ident = format_ident!("{}", DEPENDENCY_HISTORY_VAR_NAME); - let output = - InjectableImpl::::create_single_get_dep_method_call( - &mock_dependency, - true, - &format_ident!("{}", DI_CONTAINER_VAR_NAME), - &format_ident!("{}", DEPENDENCY_HISTORY_VAR_NAME), - ) - .unwrap(); + let output = InjectableImpl::::create_single_get_dep_method_call( + &mock_dependency, + true, + &format_ident!("{}", DI_CONTAINER_VAR_NAME), + &format_ident!("{}", DEPENDENCY_HISTORY_VAR_NAME), + ); assert_eq!( parse2::(output).unwrap(), diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 232483e..7928e03 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,5 +1,6 @@ #![cfg_attr(doc_cfg, feature(doc_cfg))] #![deny(clippy::all, clippy::pedantic, missing_docs, unsafe_code)] +#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::panic))] #![allow(unknown_lints)] #![allow(clippy::module_name_repetitions, clippy::manual_let_else)] diff --git a/macros/src/util/mod.rs b/macros/src/util/mod.rs index 2244229..7ab2185 100644 --- a/macros/src/util/mod.rs +++ b/macros/src/util/mod.rs @@ -2,6 +2,7 @@ pub mod error; pub mod item_impl; pub mod iterator_ext; pub mod string; +pub mod syn_ext; pub mod syn_path; pub mod tokens; diff --git a/macros/src/util/syn_ext.rs b/macros/src/util/syn_ext.rs new file mode 100644 index 0000000..6201670 --- /dev/null +++ b/macros/src/util/syn_ext.rs @@ -0,0 +1,114 @@ +use proc_macro2::Ident; +use syn::punctuated::Punctuated; +use syn::token::Paren; +use syn::{ + Expr, + ExprCall, + ExprLit, + ExprMethodCall, + ExprPath, + GenericMethodArgument, + Lit, + MethodTurbofish, + Path, + Token, +}; + +pub trait ExprMethodCallExt +{ + fn new(receiver: Expr, method: Ident, args: impl IntoIterator) -> Self; + + fn with_turbofish(self, turbofish: MethodTurbofish) -> Self; +} + +impl ExprMethodCallExt for ExprMethodCall +{ + fn new(receiver: Expr, method: Ident, args: impl IntoIterator) -> Self + { + Self { + attrs: Vec::new(), + receiver: Box::new(receiver), + dot_token: ::default(), + method, + turbofish: None, + paren_token: Paren::default(), + args: Punctuated::from_iter(args), + } + } + + fn with_turbofish(mut self, turbofish: MethodTurbofish) -> Self + { + self.turbofish = Some(turbofish); + + self + } +} + +pub trait ExprPathExt +{ + fn new(path: Path) -> Self; +} + +impl ExprPathExt for ExprPath +{ + fn new(path: Path) -> Self + { + Self { + attrs: Vec::new(), + qself: None, + path, + } + } +} + +pub trait MethodTurbofishExt +{ + fn new(args: impl IntoIterator) -> Self; +} + +impl MethodTurbofishExt for MethodTurbofish +{ + fn new(args: impl IntoIterator) -> Self + { + Self { + colon2_token: ::default(), + lt_token: ::default(), + args: Punctuated::from_iter(args), + gt_token: ]>::default(), + } + } +} + +pub trait ExprCallExt +{ + fn new(function: Expr, args: impl IntoIterator) -> Self; +} + +impl ExprCallExt for ExprCall +{ + fn new(function: Expr, args: impl IntoIterator) -> Self + { + Self { + attrs: Vec::new(), + func: Box::new(function), + paren_token: Paren::default(), + args: Punctuated::from_iter(args), + } + } +} + +pub trait ExprLitExt +{ + fn new(lit: impl Into) -> Self; +} + +impl ExprLitExt for ExprLit +{ + fn new(lit: impl Into) -> Self + { + Self { + attrs: Vec::new(), + lit: lit.into(), + } + } +} diff --git a/macros/src/util/syn_path.rs b/macros/src/util/syn_path.rs index 88182cc..26e2597 100644 --- a/macros/src/util/syn_path.rs +++ b/macros/src/util/syn_path.rs @@ -32,3 +32,29 @@ impl SynPathExt for syn::Path ) } } + +macro_rules! syn_path { + ($first_segment: ident $(::$segment: ident)*) => { + ::syn::Path { + leading_colon: None, + segments: ::syn::punctuated::Punctuated::from_iter([ + $crate::util::syn_path::syn_path_segment!($first_segment), + $($crate::util::syn_path::syn_path_segment!($segment),)* + ]) + } + }; +} + +macro_rules! syn_path_segment { + ($segment: ident) => { + ::syn::PathSegment { + ident: ::proc_macro2::Ident::new( + stringify!($segment), + ::proc_macro2::Span::call_site(), + ), + arguments: ::syn::PathArguments::None, + } + }; +} + +pub(crate) use {syn_path, syn_path_segment}; -- cgit v1.2.3-18-g5258