diff --git a/CHANGELOG.md b/CHANGELOG.md index 97b799ba..07ee0936 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ Rhai Release Notes ================== +This version introduces functions with `Dynamic` parameters acting as wildcards. + Version 0.19.13 =============== diff --git a/codegen/src/function.rs b/codegen/src/function.rs index 8738b410..7fef7f42 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -98,8 +98,8 @@ pub fn print_type(ty: &syn::Type) -> String { #[derive(Debug, Default)] pub struct ExportedFnParams { pub name: Vec, - pub return_raw: bool, - pub pure: bool, + pub return_raw: Option, + pub pure: Option, pub skip: bool, pub special: FnSpecialAccess, pub namespace: FnNamespaceAccess, @@ -137,8 +137,8 @@ impl ExportedParams for ExportedFnParams { items: attrs, } = info; let mut name = Vec::new(); - let mut return_raw = false; - let mut pure = false; + let mut return_raw = None; + let mut pure = None; let mut skip = false; let mut namespace = FnNamespaceAccess::Unset; let mut special = FnSpecialAccess::None; @@ -194,18 +194,28 @@ impl ExportedParams for ExportedFnParams { return Err(syn::Error::new(s.span(), "extraneous value")) } - ("pure", None) => pure = true, - ("return_raw", None) => return_raw = true, + ("pure", None) => pure = Some(item_span), + ("return_raw", None) => return_raw = Some(item_span), ("skip", None) => skip = true, ("global", None) => match namespace { FnNamespaceAccess::Unset => namespace = FnNamespaceAccess::Global, FnNamespaceAccess::Global => (), - _ => return Err(syn::Error::new(key.span(), "conflicting namespace")), + FnNamespaceAccess::Internal => { + return Err(syn::Error::new( + key.span(), + "namespace is already set to 'internal'", + )) + } }, ("internal", None) => match namespace { FnNamespaceAccess::Unset => namespace = FnNamespaceAccess::Internal, FnNamespaceAccess::Internal => (), - _ => return Err(syn::Error::new(key.span(), "conflicting namespace")), + FnNamespaceAccess::Global => { + return Err(syn::Error::new( + key.span(), + "namespace is already set to 'global'", + )) + } }, ("get", Some(s)) => { @@ -478,10 +488,10 @@ impl ExportedFn { } pub fn exported_name<'n>(&'n self) -> Cow<'n, str> { - self.params.name.last().map_or_else( - || self.signature.ident.to_string().into(), - |s| s.as_str().into(), - ) + self.params + .name + .last() + .map_or_else(|| self.signature.ident.to_string().into(), |s| s.into()) } pub fn arg_list(&self) -> impl Iterator { @@ -503,23 +513,31 @@ impl ExportedFn { } pub fn set_params(&mut self, mut params: ExportedFnParams) -> syn::Result<()> { - // Several issues are checked here to avoid issues with diagnostics caused by raising them - // later. + // Several issues are checked here to avoid issues with diagnostics caused by raising them later. // - // 1. Do not allow non-returning raw functions. + // 1a. Do not allow non-returning raw functions. // - if params.return_raw && self.return_type().is_none() { + if params.return_raw.is_some() && self.return_type().is_none() { return Err(syn::Error::new( - self.signature.span(), + params.return_raw.unwrap(), "functions marked with 'return_raw' must return Result>", )); } + // 1b. Do not allow non-method pure functions. + // + if params.pure.is_some() && !self.mutable_receiver() { + return Err(syn::Error::new( + params.pure.unwrap(), + "'pure' is not necessary on functions without a &mut first parameter", + )); + } + match params.special { // 2a. Property getters must take only the subject as an argument. FnSpecialAccess::Property(Property::Get(_)) if self.arg_count() != 1 => { return Err(syn::Error::new( - self.signature.span(), + self.signature.inputs.span(), "property getter requires exactly 1 parameter", )) } @@ -533,21 +551,21 @@ impl ExportedFn { // 3a. Property setters must take the subject and a new value as arguments. FnSpecialAccess::Property(Property::Set(_)) if self.arg_count() != 2 => { return Err(syn::Error::new( - self.signature.span(), + self.signature.inputs.span(), "property setter requires exactly 2 parameters", )) } // 3b. Property setters must return nothing. FnSpecialAccess::Property(Property::Set(_)) if self.return_type().is_some() => { return Err(syn::Error::new( - self.signature.span(), + self.signature.output.span(), "property setter cannot return any value", )) } // 4a. Index getters must take the subject and the accessed "index" as arguments. FnSpecialAccess::Index(Index::Get) if self.arg_count() != 2 => { return Err(syn::Error::new( - self.signature.span(), + self.signature.inputs.span(), "index getter requires exactly 2 parameters", )) } @@ -561,15 +579,15 @@ impl ExportedFn { // 5a. Index setters must take the subject, "index", and new value as arguments. FnSpecialAccess::Index(Index::Set) if self.arg_count() != 3 => { return Err(syn::Error::new( - self.signature.span(), + self.signature.inputs.span(), "index setter requires exactly 3 parameters", )) } // 5b. Index setters must return nothing. FnSpecialAccess::Index(Index::Set) if self.return_type().is_some() => { return Err(syn::Error::new( - self.signature.span(), - "index setter cannot return a value", + self.signature.output.span(), + "index setter cannot return any value", )) } _ => {} @@ -635,7 +653,7 @@ impl ExportedFn { .return_type() .map(|r| r.span()) .unwrap_or_else(|| proc_macro2::Span::call_site()); - if self.params.return_raw { + if self.params.return_raw.is_some() { quote_spanned! { return_span => pub #dynamic_signature { #name(#(#arguments),*) @@ -659,7 +677,7 @@ impl ExportedFn { pub fn generate_callable(&self, on_type_name: &str) -> proc_macro2::TokenStream { let token_name: syn::Ident = syn::Ident::new(on_type_name, self.name().span()); let callable_fn_name: syn::Ident = syn::Ident::new( - format!("{}_callable", on_type_name.to_lowercase()).as_str(), + &format!("{}_callable", on_type_name.to_lowercase()), self.name().span(), ); quote! { @@ -672,7 +690,7 @@ impl ExportedFn { pub fn generate_input_names(&self, on_type_name: &str) -> proc_macro2::TokenStream { let token_name: syn::Ident = syn::Ident::new(on_type_name, self.name().span()); let input_names_fn_name: syn::Ident = syn::Ident::new( - format!("{}_input_names", on_type_name.to_lowercase()).as_str(), + &format!("{}_input_names", on_type_name.to_lowercase()), self.name().span(), ); quote! { @@ -685,7 +703,7 @@ impl ExportedFn { pub fn generate_input_types(&self, on_type_name: &str) -> proc_macro2::TokenStream { let token_name: syn::Ident = syn::Ident::new(on_type_name, self.name().span()); let input_types_fn_name: syn::Ident = syn::Ident::new( - format!("{}_input_types", on_type_name.to_lowercase()).as_str(), + &format!("{}_input_types", on_type_name.to_lowercase()), self.name().span(), ); quote! { @@ -698,7 +716,7 @@ impl ExportedFn { pub fn generate_return_type(&self, on_type_name: &str) -> proc_macro2::TokenStream { let token_name: syn::Ident = syn::Ident::new(on_type_name, self.name().span()); let return_type_fn_name: syn::Ident = syn::Ident::new( - format!("{}_return_type", on_type_name.to_lowercase()).as_str(), + &format!("{}_return_type", on_type_name.to_lowercase()), self.name().span(), ); quote! { @@ -750,7 +768,7 @@ impl ExportedFn { }) .unwrap(), ); - if !self.params().pure { + if !self.params().pure.is_some() { let arg_lit_str = syn::LitStr::new(&pat.to_token_stream().to_string(), pat.span()); unpack_statements.push( @@ -871,7 +889,7 @@ impl ExportedFn { .return_type() .map(|r| r.span()) .unwrap_or_else(|| proc_macro2::Span::call_site()); - let return_expr = if !self.params.return_raw { + let return_expr = if !self.params.return_raw.is_some() { if self.return_dynamic { quote_spanned! { return_span => Ok(#sig_name(#(#unpack_exprs),*)) diff --git a/codegen/src/module.rs b/codegen/src/module.rs index dc0e8571..ca740de1 100644 --- a/codegen/src/module.rs +++ b/codegen/src/module.rs @@ -217,7 +217,7 @@ impl Module { pub fn exported_name(&self) -> Cow { if !self.params.name.is_empty() { - self.params.name.as_str().into() + (&self.params.name).into() } else { self.module_name().to_string().into() } diff --git a/codegen/ui_tests/export_fn_raw_noreturn.stderr b/codegen/ui_tests/export_fn_raw_noreturn.stderr index b2574e9c..d4a992c0 100644 --- a/codegen/ui_tests/export_fn_raw_noreturn.stderr +++ b/codegen/ui_tests/export_fn_raw_noreturn.stderr @@ -1,8 +1,8 @@ error: functions marked with 'return_raw' must return Result> - --> $DIR/export_fn_raw_noreturn.rs:10:5 - | -10 | pub fn test_fn(input: &mut Point) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + --> $DIR/export_fn_raw_noreturn.rs:9:13 + | +9 | #[export_fn(return_raw)] + | ^^^^^^^^^^ error[E0425]: cannot find function `test_fn` in this scope --> $DIR/export_fn_raw_noreturn.rs:19:5 diff --git a/codegen/ui_tests/export_mod_raw_noreturn.stderr b/codegen/ui_tests/export_mod_raw_noreturn.stderr index d840295f..d59be0f8 100644 --- a/codegen/ui_tests/export_mod_raw_noreturn.stderr +++ b/codegen/ui_tests/export_mod_raw_noreturn.stderr @@ -1,8 +1,8 @@ error: functions marked with 'return_raw' must return Result> - --> $DIR/export_mod_raw_noreturn.rs:12:5 + --> $DIR/export_mod_raw_noreturn.rs:11:11 | -12 | pub fn test_fn(input: &mut Point) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +11 | #[rhai_fn(return_raw)] + | ^^^^^^^^^^ error[E0433]: failed to resolve: use of undeclared crate or module `test_mod` --> $DIR/export_mod_raw_noreturn.rs:22:5 diff --git a/codegen/ui_tests/rhai_fn_getter_signature.stderr b/codegen/ui_tests/rhai_fn_getter_signature.stderr index 70b42738..0a7742e6 100644 --- a/codegen/ui_tests/rhai_fn_getter_signature.stderr +++ b/codegen/ui_tests/rhai_fn_getter_signature.stderr @@ -1,8 +1,8 @@ error: property getter requires exactly 1 parameter - --> $DIR/rhai_fn_getter_signature.rs:13:9 + --> $DIR/rhai_fn_getter_signature.rs:13:20 | 13 | pub fn test_fn(input: Point, value: bool) -> bool { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0433]: failed to resolve: use of undeclared crate or module `test_module` --> $DIR/rhai_fn_getter_signature.rs:23:8 diff --git a/codegen/ui_tests/rhai_fn_global_multiple.stderr b/codegen/ui_tests/rhai_fn_global_multiple.stderr index 93da770d..f36c296d 100644 --- a/codegen/ui_tests/rhai_fn_global_multiple.stderr +++ b/codegen/ui_tests/rhai_fn_global_multiple.stderr @@ -1,4 +1,4 @@ -error: conflicting namespace +error: namespace is already set to 'global' --> $DIR/rhai_fn_global_multiple.rs:12:23 | 12 | #[rhai_fn(global, internal)] diff --git a/codegen/ui_tests/rhai_fn_index_getter_signature.stderr b/codegen/ui_tests/rhai_fn_index_getter_signature.stderr index 3bdffb28..8d567a97 100644 --- a/codegen/ui_tests/rhai_fn_index_getter_signature.stderr +++ b/codegen/ui_tests/rhai_fn_index_getter_signature.stderr @@ -1,8 +1,8 @@ error: index getter requires exactly 2 parameters - --> $DIR/rhai_fn_index_getter_signature.rs:13:9 + --> $DIR/rhai_fn_index_getter_signature.rs:13:20 | 13 | pub fn test_fn(input: Point) -> bool { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^ error[E0433]: failed to resolve: use of undeclared crate or module `test_module` --> $DIR/rhai_fn_index_getter_signature.rs:23:8 diff --git a/codegen/ui_tests/rhai_fn_setter_index_signature.stderr b/codegen/ui_tests/rhai_fn_setter_index_signature.stderr index b7ae7dad..aa9548e1 100644 --- a/codegen/ui_tests/rhai_fn_setter_index_signature.stderr +++ b/codegen/ui_tests/rhai_fn_setter_index_signature.stderr @@ -1,8 +1,8 @@ error: index setter requires exactly 3 parameters - --> $DIR/rhai_fn_setter_index_signature.rs:13:9 + --> $DIR/rhai_fn_setter_index_signature.rs:13:20 | 13 | pub fn test_fn(input: Point) -> bool { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^ error[E0433]: failed to resolve: use of undeclared crate or module `test_module` --> $DIR/rhai_fn_setter_index_signature.rs:23:8 diff --git a/codegen/ui_tests/rhai_fn_setter_return.stderr b/codegen/ui_tests/rhai_fn_setter_return.stderr index d55e2073..8c0b21ab 100644 --- a/codegen/ui_tests/rhai_fn_setter_return.stderr +++ b/codegen/ui_tests/rhai_fn_setter_return.stderr @@ -1,8 +1,8 @@ error: property setter cannot return any value - --> $DIR/rhai_fn_setter_return.rs:13:9 + --> $DIR/rhai_fn_setter_return.rs:13:51 | 13 | pub fn test_fn(input: &mut Point, value: f32) -> bool { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ error[E0433]: failed to resolve: use of undeclared crate or module `test_module` --> $DIR/rhai_fn_setter_return.rs:24:8 diff --git a/codegen/ui_tests/rhai_fn_setter_signature.stderr b/codegen/ui_tests/rhai_fn_setter_signature.stderr index 0e788c9a..d5f3c9d3 100644 --- a/codegen/ui_tests/rhai_fn_setter_signature.stderr +++ b/codegen/ui_tests/rhai_fn_setter_signature.stderr @@ -1,8 +1,8 @@ error: property setter requires exactly 2 parameters - --> $DIR/rhai_fn_setter_signature.rs:13:9 + --> $DIR/rhai_fn_setter_signature.rs:13:20 | 13 | pub fn test_fn(input: Point) -> bool { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^ error[E0433]: failed to resolve: use of undeclared crate or module `test_module` --> $DIR/rhai_fn_setter_signature.rs:23:8