From 823ecef1f1245feb5832aa8ad02898af2d8b4882 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 26 Feb 2021 17:19:40 +0800 Subject: [PATCH 1/4] #[rhai_fn(pure)] must have &mut parameter. --- codegen/src/function.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/codegen/src/function.rs b/codegen/src/function.rs index 8738b410..ceb4cb89 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -503,10 +503,9 @@ 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() { return Err(syn::Error::new( @@ -515,6 +514,15 @@ impl ExportedFn { )); } + // 1b. Do not allow non-method pure functions. + // + if params.pure && !self.mutable_receiver() { + return Err(syn::Error::new( + self.signature.span(), + "functions marked with 'pure' must have 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 => { From 37fe14ba16ce74af9ab918c92521e3b6a9d50567 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 26 Feb 2021 18:28:21 +0800 Subject: [PATCH 2/4] Raise error if pure misuse. --- codegen/src/function.rs | 26 +++++++++---------- .../ui_tests/export_fn_raw_noreturn.stderr | 8 +++--- .../ui_tests/export_mod_raw_noreturn.stderr | 6 ++--- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/codegen/src/function.rs b/codegen/src/function.rs index ceb4cb89..f45028f4 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,8 +194,8 @@ 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, @@ -507,18 +507,18 @@ impl ExportedFn { // // 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 && !self.mutable_receiver() { + if params.pure.is_some() && !self.mutable_receiver() { return Err(syn::Error::new( - self.signature.span(), + params.pure.unwrap(), "functions marked with 'pure' must have a &mut first parameter", )); } @@ -643,7 +643,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),*) @@ -758,7 +758,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( @@ -879,7 +879,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/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 From 33fa57d95b36ce45337b18e9bea5e40babfdd574 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 26 Feb 2021 20:00:25 +0800 Subject: [PATCH 3/4] Refine codegen error positions. --- CHANGELOG.md | 2 + codegen/src/function.rs | 44 ++++++++++++------- codegen/src/module.rs | 2 +- .../ui_tests/rhai_fn_getter_signature.stderr | 2 +- .../ui_tests/rhai_fn_global_multiple.stderr | 2 +- .../rhai_fn_index_getter_signature.stderr | 2 +- .../rhai_fn_setter_index_signature.stderr | 2 +- codegen/ui_tests/rhai_fn_setter_return.stderr | 2 +- .../ui_tests/rhai_fn_setter_signature.stderr | 2 +- 9 files changed, 36 insertions(+), 24 deletions(-) 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 f45028f4..d35134db 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -200,12 +200,22 @@ impl ExportedParams for ExportedFnParams { ("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 { @@ -519,7 +529,7 @@ impl ExportedFn { if params.pure.is_some() && !self.mutable_receiver() { return Err(syn::Error::new( params.pure.unwrap(), - "functions marked with 'pure' must have a &mut first parameter", + "'pure' is not necessary on functions without a &mut first parameter", )); } @@ -541,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", )) } @@ -569,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", )) } _ => {} @@ -667,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! { @@ -680,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! { @@ -693,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! { @@ -706,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! { 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/rhai_fn_getter_signature.stderr b/codegen/ui_tests/rhai_fn_getter_signature.stderr index 70b42738..a5b6aaf1 100644 --- a/codegen/ui_tests/rhai_fn_getter_signature.stderr +++ b/codegen/ui_tests/rhai_fn_getter_signature.stderr @@ -2,7 +2,7 @@ error: property getter requires exactly 1 parameter --> $DIR/rhai_fn_getter_signature.rs:13:9 | 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..bed303c6 100644 --- a/codegen/ui_tests/rhai_fn_index_getter_signature.stderr +++ b/codegen/ui_tests/rhai_fn_index_getter_signature.stderr @@ -2,7 +2,7 @@ error: index getter requires exactly 2 parameters --> $DIR/rhai_fn_index_getter_signature.rs:13:9 | 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..4f778770 100644 --- a/codegen/ui_tests/rhai_fn_setter_index_signature.stderr +++ b/codegen/ui_tests/rhai_fn_setter_index_signature.stderr @@ -2,7 +2,7 @@ error: index setter requires exactly 3 parameters --> $DIR/rhai_fn_setter_index_signature.rs:13:9 | 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..2001e7e5 100644 --- a/codegen/ui_tests/rhai_fn_setter_return.stderr +++ b/codegen/ui_tests/rhai_fn_setter_return.stderr @@ -2,7 +2,7 @@ error: property setter cannot return any value --> $DIR/rhai_fn_setter_return.rs:13:9 | 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..a6d0f947 100644 --- a/codegen/ui_tests/rhai_fn_setter_signature.stderr +++ b/codegen/ui_tests/rhai_fn_setter_signature.stderr @@ -2,7 +2,7 @@ error: property setter requires exactly 2 parameters --> $DIR/rhai_fn_setter_signature.rs:13:9 | 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 From f3b5df09af16b0a9a9c049e8158e4e9f5cc2b073 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 26 Feb 2021 20:14:23 +0800 Subject: [PATCH 4/4] Fix codegen test results. --- codegen/src/function.rs | 2 +- codegen/ui_tests/rhai_fn_getter_signature.stderr | 2 +- codegen/ui_tests/rhai_fn_index_getter_signature.stderr | 2 +- codegen/ui_tests/rhai_fn_setter_index_signature.stderr | 2 +- codegen/ui_tests/rhai_fn_setter_return.stderr | 2 +- codegen/ui_tests/rhai_fn_setter_signature.stderr | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/codegen/src/function.rs b/codegen/src/function.rs index d35134db..7fef7f42 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -537,7 +537,7 @@ impl ExportedFn { // 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", )) } diff --git a/codegen/ui_tests/rhai_fn_getter_signature.stderr b/codegen/ui_tests/rhai_fn_getter_signature.stderr index a5b6aaf1..0a7742e6 100644 --- a/codegen/ui_tests/rhai_fn_getter_signature.stderr +++ b/codegen/ui_tests/rhai_fn_getter_signature.stderr @@ -1,5 +1,5 @@ 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 { | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/codegen/ui_tests/rhai_fn_index_getter_signature.stderr b/codegen/ui_tests/rhai_fn_index_getter_signature.stderr index bed303c6..8d567a97 100644 --- a/codegen/ui_tests/rhai_fn_index_getter_signature.stderr +++ b/codegen/ui_tests/rhai_fn_index_getter_signature.stderr @@ -1,5 +1,5 @@ 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 { | ^^^^^^^^^^^^ diff --git a/codegen/ui_tests/rhai_fn_setter_index_signature.stderr b/codegen/ui_tests/rhai_fn_setter_index_signature.stderr index 4f778770..aa9548e1 100644 --- a/codegen/ui_tests/rhai_fn_setter_index_signature.stderr +++ b/codegen/ui_tests/rhai_fn_setter_index_signature.stderr @@ -1,5 +1,5 @@ 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 { | ^^^^^^^^^^^^ diff --git a/codegen/ui_tests/rhai_fn_setter_return.stderr b/codegen/ui_tests/rhai_fn_setter_return.stderr index 2001e7e5..8c0b21ab 100644 --- a/codegen/ui_tests/rhai_fn_setter_return.stderr +++ b/codegen/ui_tests/rhai_fn_setter_return.stderr @@ -1,5 +1,5 @@ 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 { | ^^^^^^^ diff --git a/codegen/ui_tests/rhai_fn_setter_signature.stderr b/codegen/ui_tests/rhai_fn_setter_signature.stderr index a6d0f947..d5f3c9d3 100644 --- a/codegen/ui_tests/rhai_fn_setter_signature.stderr +++ b/codegen/ui_tests/rhai_fn_setter_signature.stderr @@ -1,5 +1,5 @@ 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 { | ^^^^^^^^^^^^