mirror of
https://github.com/tauri-apps/tauri.git
synced 2024-09-19 04:08:45 +03:00
Improve compile errors for async commands without Result
return type (#6124)
Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
This commit is contained in:
parent
154ccbe503
commit
d68a25e32e
5
.changes/improve-async-cmd-error-message.md
Normal file
5
.changes/improve-async-cmd-error-message.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"tauri-macros": 'patch:enhance'
|
||||
---
|
||||
|
||||
Improve compiler error message when generating an async command that has a reference input and don't return a Result.
|
@ -5,7 +5,7 @@
|
||||
use heck::{ToLowerCamelCase, ToSnakeCase};
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
use quote::{format_ident, quote, quote_spanned};
|
||||
use syn::{
|
||||
ext::IdentExt,
|
||||
parse::{Parse, ParseStream},
|
||||
@ -103,6 +103,63 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream {
|
||||
resolver: format_ident!("__tauri_resolver__"),
|
||||
};
|
||||
|
||||
// Tauri currently doesn't support async commands that take a reference as input and don't return
|
||||
// a result. See: https://github.com/tauri-apps/tauri/issues/2533
|
||||
//
|
||||
// For now, we provide an informative error message to the user in that case. Once #2533 is
|
||||
// resolved, this check can be removed.
|
||||
let mut async_command_check = TokenStream2::new();
|
||||
if function.sig.asyncness.is_some() {
|
||||
// This check won't catch all possible problems but it should catch the most common ones.
|
||||
let mut ref_argument_span = None;
|
||||
|
||||
for arg in &function.sig.inputs {
|
||||
if let syn::FnArg::Typed(pat) = arg {
|
||||
match &*pat.ty {
|
||||
syn::Type::Reference(_) => {
|
||||
ref_argument_span = Some(pat.span());
|
||||
}
|
||||
syn::Type::Path(path) => {
|
||||
// Check if the type contains a lifetime argument
|
||||
let last = path.path.segments.last().unwrap();
|
||||
if let syn::PathArguments::AngleBracketed(args) = &last.arguments {
|
||||
if args
|
||||
.args
|
||||
.iter()
|
||||
.any(|arg| matches!(arg, syn::GenericArgument::Lifetime(_)))
|
||||
{
|
||||
ref_argument_span = Some(pat.span());
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if let Some(span) = ref_argument_span {
|
||||
if let syn::ReturnType::Type(_, return_type) = &function.sig.output {
|
||||
// To check if the return type is `Result` we require it to check a trait that is
|
||||
// only implemented by `Result`. That way we don't exclude renamed result types
|
||||
// which we wouldn't otherwise be able to detect purely from the token stream.
|
||||
// The "error message" displayed to the user is simply the trait name.
|
||||
async_command_check = quote_spanned! {return_type.span() =>
|
||||
#[allow(unreachable_code, clippy::diverging_sub_expression)]
|
||||
const _: () = if false {
|
||||
trait AsyncCommandMustReturnResult {}
|
||||
impl<A, B> AsyncCommandMustReturnResult for Result<A, B> {}
|
||||
let _check: #return_type = unreachable!();
|
||||
let _: &dyn AsyncCommandMustReturnResult = &_check;
|
||||
};
|
||||
};
|
||||
} else {
|
||||
return quote_spanned! {
|
||||
span => compile_error!("async commands that contain references as inputs must return a `Result`");
|
||||
}.into();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// body to the command wrapper or a `compile_error!` of an error occurred while parsing it.
|
||||
let body = syn::parse::<WrapperAttributes>(attributes)
|
||||
.map(|mut attrs| {
|
||||
@ -121,6 +178,8 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream {
|
||||
|
||||
// Rely on rust 2018 edition to allow importing a macro from a path.
|
||||
quote!(
|
||||
#async_command_check
|
||||
|
||||
#function
|
||||
|
||||
#maybe_macro_export
|
||||
|
Loading…
Reference in New Issue
Block a user