diff --git a/RELEASES.md b/RELEASES.md index 69044fc5..902cd855 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -8,6 +8,7 @@ New features ------------ * Plugins support via procedural macros. +* Scripted functions are allowed in packages. Version 0.18.3 diff --git a/codegen/src/attrs.rs b/codegen/src/attrs.rs index 5d1e42d7..32d258f7 100644 --- a/codegen/src/attrs.rs +++ b/codegen/src/attrs.rs @@ -22,6 +22,7 @@ pub trait ExportedParams: Sized { pub struct AttrItem { pub key: proc_macro2::Ident, pub value: Option, + pub span: proc_macro2::Span, } pub struct ExportInfo { @@ -46,6 +47,7 @@ pub fn parse_punctuated_items( let mut attrs: Vec = Vec::new(); for arg in arg_list { + let arg_span = arg.span(); let (key, value) = match arg { syn::Expr::Assign(syn::ExprAssign { ref left, @@ -78,7 +80,7 @@ pub fn parse_punctuated_items( .ok_or_else(|| syn::Error::new(attr_path.span(), "expecting attribute name"))?, x => return Err(syn::Error::new(x.span(), "expecting identifier")), }; - attrs.push(AttrItem { key, value }); + attrs.push(AttrItem { key, value, span: arg_span }); } Ok(ExportInfo { item_span: list_span, items: attrs }) diff --git a/codegen/src/function.rs b/codegen/src/function.rs index aa4001f5..75812dbd 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -17,24 +17,43 @@ use syn::{parse::Parse, parse::ParseStream, parse::Parser, spanned::Spanned}; use crate::attrs::{ExportInfo, ExportScope, ExportedParams}; +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum Index { + Get, + Set, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum Property { + Get(syn::Ident), + Set(syn::Ident), +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum FnSpecialAccess { + None, + Index(Index), + Property(Property), +} + +impl Default for FnSpecialAccess { + fn default() -> FnSpecialAccess { + FnSpecialAccess::None + } +} + #[derive(Debug, Default)] pub(crate) struct ExportedFnParams { pub name: Option>, pub return_raw: bool, pub skip: bool, pub span: Option, + pub special: FnSpecialAccess, } pub const FN_IDX_GET: &str = "index$get$"; pub const FN_IDX_SET: &str = "index$set$"; -pub fn make_getter(id: &str) -> String { - format!("get${}", id) -} -pub fn make_setter(id: &str) -> String { - format!("set${}", id) -} - impl Parse for ExportedFnParams { fn parse(args: ParseStream) -> syn::Result { if args.is_empty() { @@ -63,26 +82,73 @@ impl ExportedParams for ExportedFnParams { let mut name = Vec::new(); let mut return_raw = false; let mut skip = false; + let mut special = FnSpecialAccess::None; for attr in attrs { - let crate::attrs::AttrItem { key, value } = attr; + let crate::attrs::AttrItem { key, value, span: item_span } = attr; match (key.to_string().as_ref(), value) { - ("name", Some(s)) => { - // check validity of name - if s.value().contains('.') { - return Err(syn::Error::new( - s.span(), - "Rhai function names may not contain dot", - )); - } - name.push(s.value()) - } - ("get", Some(s)) => name.push(make_getter(&s.value())), - ("set", Some(s)) => name.push(make_setter(&s.value())), ("get", None) | ("set", None) | ("name", None) => { return Err(syn::Error::new(key.span(), "requires value")) - } - ("index_get", None) => name.push(FN_IDX_GET.to_string()), - ("index_set", None) => name.push(FN_IDX_SET.to_string()), + }, + ("name", Some(s)) if &s.value() == FN_IDX_GET => { + return Err(syn::Error::new(item_span, + "use attribute 'index_get' instead")) + }, + ("name", Some(s)) if &s.value() == FN_IDX_SET => { + return Err(syn::Error::new(item_span, + "use attribute 'index_set' instead")) + }, + ("name", Some(s)) if s.value().starts_with("get$") => { + return Err(syn::Error::new(item_span, + format!("use attribute 'getter = \"{}\"' instead", + &s.value()["get$".len()..]))) + }, + ("name", Some(s)) if s.value().starts_with("set$") => { + return Err(syn::Error::new(item_span, + format!("use attribute 'setter = \"{}\"' instead", + &s.value()["set$".len()..]))) + }, + ("name", Some(s)) if s.value().contains('$') => { + return Err(syn::Error::new(s.span(), + "Rhai function names may not contain dollar sign")) + }, + ("name", Some(s)) if s.value().contains('.') => { + return Err(syn::Error::new(s.span(), + "Rhai function names may not contain dot")) + }, + ("name", Some(s)) => { + name.push(s.value()) + }, + ("set", Some(s)) => special = match special { + FnSpecialAccess::None => + FnSpecialAccess::Property(Property::Set(syn::Ident::new(&s.value(), + s.span()))), + _ => { + return Err(syn::Error::new(item_span.span(), "conflicting setter")) + } + }, + ("get", Some(s)) => special = match special { + FnSpecialAccess::None => + FnSpecialAccess::Property(Property::Get(syn::Ident::new(&s.value(), + s.span()))), + _ => { + return Err(syn::Error::new(item_span.span(), "conflicting getter")) + } + }, + ("index_get", None) => special = match special { + FnSpecialAccess::None => + FnSpecialAccess::Index(Index::Get), + _ => { + return Err(syn::Error::new(item_span.span(), "conflicting index_get")) + } + }, + + ("index_set", None) => special = match special { + FnSpecialAccess::None => + FnSpecialAccess::Index(Index::Set), + _ => { + return Err(syn::Error::new(item_span.span(), "conflicting index_set")) + } + }, ("return_raw", None) => return_raw = true, ("index_get", Some(s)) | ("index_set", Some(s)) | ("return_raw", Some(s)) => { return Err(syn::Error::new(s.span(), "extraneous value")) @@ -102,6 +168,7 @@ impl ExportedParams for ExportedFnParams { name: if name.is_empty() { None } else { Some(name) }, return_raw, skip, + special, span: Some(span), ..Default::default() }) @@ -259,6 +326,32 @@ impl ExportedFn { &self.signature.ident } + pub(crate) fn exported_names(&self) -> Vec { + let mut literals = self.params.name.as_ref() + .map(|v| v.iter() + .map(|s| syn::LitStr::new(s, proc_macro2::Span::call_site())).collect()) + .unwrap_or_else(|| Vec::new()); + + match self.params.special { + FnSpecialAccess::None => {}, + FnSpecialAccess::Property(Property::Get(ref g)) => + literals.push(syn::LitStr::new(&format!("get${}", g.to_string()), g.span())), + FnSpecialAccess::Property(Property::Set(ref s)) => + literals.push(syn::LitStr::new(&format!("set${}", s.to_string()), s.span())), + FnSpecialAccess::Index(Index::Get) => + literals.push(syn::LitStr::new(FN_IDX_GET, proc_macro2::Span::call_site())), + FnSpecialAccess::Index(Index::Set) => + literals.push(syn::LitStr::new(FN_IDX_SET, proc_macro2::Span::call_site())), + } + + if literals.is_empty() { + literals.push(syn::LitStr::new(&self.signature.ident.to_string(), + self.signature.ident.span())); + } + + literals + } + pub(crate) fn exported_name<'n>(&'n self) -> Cow<'n, str> { if let Some(ref name) = self.params.name { Cow::Borrowed(name.last().unwrap().as_str()) @@ -284,9 +377,11 @@ impl ExportedFn { } pub fn set_params(&mut self, mut params: ExportedFnParams) -> syn::Result<()> { - // Do not allow non-returning raw functions. + // Several issues are checked here to avoid issues with diagnostics caused by raising them + // later. + // + // 1. Do not allow non-returning raw functions. // - // This is caught now to avoid issues with diagnostics later. if params.return_raw && mem::discriminant(&self.signature.output) == mem::discriminant(&syn::ReturnType::Default) @@ -297,6 +392,58 @@ impl ExportedFn { )); } + 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(), + "property getter requires exactly 1 argument", + )), + // 2b. Property getters must return a value. + FnSpecialAccess::Property(Property::Get(_)) if self.return_type().is_none() => + return Err(syn::Error::new( + self.signature.span(), + "property getter must return a value" + )), + // 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(), + "property setter requires exactly 2 arguments", + )), + // 3b. Property setters must return nothing. + FnSpecialAccess::Property(Property::Set(_)) if self.return_type().is_some() => + return Err(syn::Error::new( + self.signature.span(), + "property setter must return no 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(), + "index getter requires exactly 2 arguments", + )), + // 4b. Index getters must return a value. + FnSpecialAccess::Index(Index::Get) if self.return_type().is_none() => + return Err(syn::Error::new( + self.signature.span(), + "index getter must return a value" + )), + // 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(), + "index setter requires exactly 3 arguments", + )), + // 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 must return no value" + )), + _ => {} + } + self.params = params; Ok(()) } diff --git a/codegen/src/module.rs b/codegen/src/module.rs index 07ae54da..1ebc25b8 100644 --- a/codegen/src/module.rs +++ b/codegen/src/module.rs @@ -53,7 +53,7 @@ impl ExportedParams for ExportedModParams { let mut skip = false; let mut scope = ExportScope::default(); for attr in attrs { - let AttrItem { key, value } = attr; + let AttrItem { key, value, .. } = attr; match (key.to_string().as_ref(), value) { ("name", Some(s)) => name = Some(s.value()), ("name", None) => return Err(syn::Error::new(key.span(), "requires value")), diff --git a/codegen/src/rhai_module.rs b/codegen/src/rhai_module.rs index 57ec78a8..d318f351 100644 --- a/codegen/src/rhai_module.rs +++ b/codegen/src/rhai_module.rs @@ -67,11 +67,7 @@ pub(crate) fn generate_body( &format!("{}_token", function.name().to_string()), function.name().span(), ); - let reg_names = function - .params() - .name - .clone() - .unwrap_or_else(|| vec![function.name().to_string()]); + let reg_names = function.exported_names(); let fn_input_types: Vec = function .arg_list() @@ -110,9 +106,7 @@ pub(crate) fn generate_body( }) .collect(); - for reg_name in reg_names { - let fn_literal = syn::LitStr::new(®_name, proc_macro2::Span::call_site()); - + for fn_literal in reg_names { set_fn_stmts.push( syn::parse2::(quote! { m.set_fn(#fn_literal, FnAccess::Public, &[#(#fn_input_types),*], diff --git a/codegen/src/test/module.rs b/codegen/src/test/module.rs index 5d4e6186..2bf48752 100644 --- a/codegen/src/test/module.rs +++ b/codegen/src/test/module.rs @@ -538,6 +538,74 @@ mod generate_tests { assert_streams_eq(item_mod.generate(), expected_tokens); } + #[test] + fn one_double_rename_fn_module() { + let input_tokens: TokenStream = quote! { + pub mod one_fn { + #[rhai_fn(name = "add", name = "+", name = "add_together")] + pub fn add_together(x: INT, y: INT) -> INT { + x + y + } + } + }; + + let expected_tokens = quote! { + pub mod one_fn { + pub fn add_together(x: INT, y: INT) -> INT { + x + y + } + #[allow(unused_imports)] + use super::*; + #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { + let mut m = Module::new(); + m.set_fn("add", FnAccess::Public, &[core::any::TypeId::of::(), + core::any::TypeId::of::()], + CallableFunction::from_plugin(add_together_token())); + m.set_fn("+", FnAccess::Public, &[core::any::TypeId::of::(), + core::any::TypeId::of::()], + CallableFunction::from_plugin(add_together_token())); + m.set_fn("add_together", FnAccess::Public, &[core::any::TypeId::of::(), + core::any::TypeId::of::()], + CallableFunction::from_plugin(add_together_token())); + m + } + #[allow(non_camel_case_types)] + struct add_together_token(); + impl PluginFunction for add_together_token { + fn call(&self, + args: &mut [&mut Dynamic], pos: Position + ) -> Result> { + debug_assert_eq!(args.len(), 2usize, + "wrong arg count: {} != {}", args.len(), 2usize); + let arg0 = mem::take(args[0usize]).clone().cast::(); + let arg1 = mem::take(args[1usize]).clone().cast::(); + Ok(Dynamic::from(add_together(arg0, arg1))) + } + + fn is_method_call(&self) -> bool { false } + fn is_varadic(&self) -> bool { false } + fn clone_boxed(&self) -> Box { + Box::new(add_together_token()) + } + fn input_types(&self) -> Box<[TypeId]> { + new_vec![TypeId::of::(), + TypeId::of::()].into_boxed_slice() + } + } + pub fn add_together_token_callable() -> CallableFunction { + CallableFunction::from_plugin(add_together_token()) + } + pub fn add_together_token_input_types() -> Box<[TypeId]> { + add_together_token().input_types() + } + } + }; + + let item_mod = syn::parse2::(input_tokens).unwrap(); + assert_streams_eq(item_mod.generate(), expected_tokens); + } + #[test] fn one_constant_module() { let input_tokens: TokenStream = quote! { @@ -1004,6 +1072,521 @@ mod generate_tests { assert_streams_eq(item_mod.generate(), expected_tokens); } + #[test] + fn one_getter_fn_module() { + let input_tokens: TokenStream = quote! { + pub mod one_fn { + #[rhai_fn(get = "square")] + pub fn int_foo(x: &mut u64) -> u64 { + (*x) * (*x) + } + } + }; + + let expected_tokens = quote! { + pub mod one_fn { + pub fn int_foo(x: &mut u64) -> u64 { + (*x) * (*x) + } + #[allow(unused_imports)] + use super::*; + #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { + let mut m = Module::new(); + m.set_fn("get$square", FnAccess::Public, &[core::any::TypeId::of::()], + CallableFunction::from_plugin(int_foo_token())); + m + } + #[allow(non_camel_case_types)] + struct int_foo_token(); + impl PluginFunction for int_foo_token { + fn call(&self, + args: &mut [&mut Dynamic], pos: Position + ) -> Result> { + debug_assert_eq!(args.len(), 1usize, + "wrong arg count: {} != {}", args.len(), 1usize); + let arg0: &mut _ = &mut args[0usize].write_lock::().unwrap(); + Ok(Dynamic::from(int_foo(arg0))) + } + + fn is_method_call(&self) -> bool { true } + fn is_varadic(&self) -> bool { false } + fn clone_boxed(&self) -> Box { + Box::new(int_foo_token()) + } + fn input_types(&self) -> Box<[TypeId]> { + new_vec![TypeId::of::()].into_boxed_slice() + } + } + pub fn int_foo_token_callable() -> CallableFunction { + CallableFunction::from_plugin(int_foo_token()) + } + pub fn int_foo_token_input_types() -> Box<[TypeId]> { + int_foo_token().input_types() + } + } + }; + + let item_mod = syn::parse2::(input_tokens).unwrap(); + assert_streams_eq(item_mod.generate(), expected_tokens); + } + + #[test] + fn one_getter_and_rename_fn_module() { + let input_tokens: TokenStream = quote! { + pub mod one_fn { + #[rhai_fn(name = "square", get = "square")] + pub fn int_foo(x: &mut u64) -> u64 { + (*x) * (*x) + } + } + }; + + let expected_tokens = quote! { + pub mod one_fn { + pub fn int_foo(x: &mut u64) -> u64 { + (*x) * (*x) + } + #[allow(unused_imports)] + use super::*; + #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { + let mut m = Module::new(); + m.set_fn("square", FnAccess::Public, &[core::any::TypeId::of::()], + CallableFunction::from_plugin(int_foo_token())); + m.set_fn("get$square", FnAccess::Public, &[core::any::TypeId::of::()], + CallableFunction::from_plugin(int_foo_token())); + m + } + #[allow(non_camel_case_types)] + struct int_foo_token(); + impl PluginFunction for int_foo_token { + fn call(&self, + args: &mut [&mut Dynamic], pos: Position + ) -> Result> { + debug_assert_eq!(args.len(), 1usize, + "wrong arg count: {} != {}", args.len(), 1usize); + let arg0: &mut _ = &mut args[0usize].write_lock::().unwrap(); + Ok(Dynamic::from(int_foo(arg0))) + } + + fn is_method_call(&self) -> bool { true } + fn is_varadic(&self) -> bool { false } + fn clone_boxed(&self) -> Box { + Box::new(int_foo_token()) + } + fn input_types(&self) -> Box<[TypeId]> { + new_vec![TypeId::of::()].into_boxed_slice() + } + } + pub fn int_foo_token_callable() -> CallableFunction { + CallableFunction::from_plugin(int_foo_token()) + } + pub fn int_foo_token_input_types() -> Box<[TypeId]> { + int_foo_token().input_types() + } + } + }; + + let item_mod = syn::parse2::(input_tokens).unwrap(); + assert_streams_eq(item_mod.generate(), expected_tokens); + } + + #[test] + fn one_setter_fn_module() { + let input_tokens: TokenStream = quote! { + pub mod one_fn { + #[rhai_fn(set = "squared")] + pub fn int_foo(x: &mut u64, y: u64) { + *x = y * y + } + } + }; + + let expected_tokens = quote! { + pub mod one_fn { + pub fn int_foo(x: &mut u64, y: u64) { + *x = y * y + } + #[allow(unused_imports)] + use super::*; + #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { + let mut m = Module::new(); + m.set_fn("set$squared", FnAccess::Public, + &[core::any::TypeId::of::(), + core::any::TypeId::of::()], + CallableFunction::from_plugin(int_foo_token())); + m + } + #[allow(non_camel_case_types)] + struct int_foo_token(); + impl PluginFunction for int_foo_token { + fn call(&self, + args: &mut [&mut Dynamic], pos: Position + ) -> Result> { + debug_assert_eq!(args.len(), 2usize, + "wrong arg count: {} != {}", args.len(), 2usize); + let arg1 = mem::take(args[1usize]).clone().cast::(); + let arg0: &mut _ = &mut args[0usize].write_lock::().unwrap(); + Ok(Dynamic::from(int_foo(arg0, arg1))) + } + + fn is_method_call(&self) -> bool { true } + fn is_varadic(&self) -> bool { false } + fn clone_boxed(&self) -> Box { + Box::new(int_foo_token()) + } + fn input_types(&self) -> Box<[TypeId]> { + new_vec![TypeId::of::(), TypeId::of::()].into_boxed_slice() + } + } + pub fn int_foo_token_callable() -> CallableFunction { + CallableFunction::from_plugin(int_foo_token()) + } + pub fn int_foo_token_input_types() -> Box<[TypeId]> { + int_foo_token().input_types() + } + } + }; + + let item_mod = syn::parse2::(input_tokens).unwrap(); + assert_streams_eq(item_mod.generate(), expected_tokens); + } + + #[test] + fn one_setter_and_rename_fn_module() { + let input_tokens: TokenStream = quote! { + pub mod one_fn { + #[rhai_fn(name = "set_sq", set = "squared")] + pub fn int_foo(x: &mut u64, y: u64) { + *x = y * y + } + } + }; + + let expected_tokens = quote! { + pub mod one_fn { + pub fn int_foo(x: &mut u64, y: u64) { + *x = y * y + } + #[allow(unused_imports)] + use super::*; + #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { + let mut m = Module::new(); + m.set_fn("set_sq", FnAccess::Public, + &[core::any::TypeId::of::(), + core::any::TypeId::of::()], + CallableFunction::from_plugin(int_foo_token())); + m.set_fn("set$squared", FnAccess::Public, + &[core::any::TypeId::of::(), + core::any::TypeId::of::()], + CallableFunction::from_plugin(int_foo_token())); + m + } + #[allow(non_camel_case_types)] + struct int_foo_token(); + impl PluginFunction for int_foo_token { + fn call(&self, + args: &mut [&mut Dynamic], pos: Position + ) -> Result> { + debug_assert_eq!(args.len(), 2usize, + "wrong arg count: {} != {}", args.len(), 2usize); + let arg1 = mem::take(args[1usize]).clone().cast::(); + let arg0: &mut _ = &mut args[0usize].write_lock::().unwrap(); + Ok(Dynamic::from(int_foo(arg0, arg1))) + } + + fn is_method_call(&self) -> bool { true } + fn is_varadic(&self) -> bool { false } + fn clone_boxed(&self) -> Box { + Box::new(int_foo_token()) + } + fn input_types(&self) -> Box<[TypeId]> { + new_vec![TypeId::of::(), TypeId::of::()].into_boxed_slice() + } + } + pub fn int_foo_token_callable() -> CallableFunction { + CallableFunction::from_plugin(int_foo_token()) + } + pub fn int_foo_token_input_types() -> Box<[TypeId]> { + int_foo_token().input_types() + } + } + }; + + let item_mod = syn::parse2::(input_tokens).unwrap(); + assert_streams_eq(item_mod.generate(), expected_tokens); + } + + #[test] + fn one_index_getter_fn_module() { + let input_tokens: TokenStream = quote! { + pub mod one_index_fn { + #[rhai_fn(index_get)] + pub fn get_by_index(x: &mut MyCollection, i: u64) -> FLOAT { + x.get(i) + } + } + }; + + let expected_tokens = quote! { + pub mod one_index_fn { + pub fn get_by_index(x: &mut MyCollection, i: u64) -> FLOAT { + x.get(i) + } + #[allow(unused_imports)] + use super::*; + #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { + let mut m = Module::new(); + m.set_fn("index$get$", FnAccess::Public, + &[core::any::TypeId::of::(), + core::any::TypeId::of::()], + CallableFunction::from_plugin(get_by_index_token())); + m + } + #[allow(non_camel_case_types)] + struct get_by_index_token(); + impl PluginFunction for get_by_index_token { + fn call(&self, + args: &mut [&mut Dynamic], pos: Position + ) -> Result> { + debug_assert_eq!(args.len(), 2usize, + "wrong arg count: {} != {}", args.len(), 2usize); + let arg1 = mem::take(args[1usize]).clone().cast::(); + let arg0: &mut _ = &mut args[0usize].write_lock::().unwrap(); + Ok(Dynamic::from(get_by_index(arg0, arg1))) + } + + fn is_method_call(&self) -> bool { true } + fn is_varadic(&self) -> bool { false } + fn clone_boxed(&self) -> Box { + Box::new(get_by_index_token()) + } + fn input_types(&self) -> Box<[TypeId]> { + new_vec![TypeId::of::(), + TypeId::of::()].into_boxed_slice() + } + } + pub fn get_by_index_token_callable() -> CallableFunction { + CallableFunction::from_plugin(get_by_index_token()) + } + pub fn get_by_index_token_input_types() -> Box<[TypeId]> { + get_by_index_token().input_types() + } + } + }; + + let item_mod = syn::parse2::(input_tokens).unwrap(); + assert_streams_eq(item_mod.generate(), expected_tokens); + } + + #[test] + fn one_index_getter_and_rename_fn_module() { + let input_tokens: TokenStream = quote! { + pub mod one_index_fn { + #[rhai_fn(name = "get", index_get)] + pub fn get_by_index(x: &mut MyCollection, i: u64) -> FLOAT { + x.get(i) + } + } + }; + + let expected_tokens = quote! { + pub mod one_index_fn { + pub fn get_by_index(x: &mut MyCollection, i: u64) -> FLOAT { + x.get(i) + } + #[allow(unused_imports)] + use super::*; + #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { + let mut m = Module::new(); + m.set_fn("get", FnAccess::Public, + &[core::any::TypeId::of::(), + core::any::TypeId::of::()], + CallableFunction::from_plugin(get_by_index_token())); + m.set_fn("index$get$", FnAccess::Public, + &[core::any::TypeId::of::(), + core::any::TypeId::of::()], + CallableFunction::from_plugin(get_by_index_token())); + m + } + #[allow(non_camel_case_types)] + struct get_by_index_token(); + impl PluginFunction for get_by_index_token { + fn call(&self, + args: &mut [&mut Dynamic], pos: Position + ) -> Result> { + debug_assert_eq!(args.len(), 2usize, + "wrong arg count: {} != {}", args.len(), 2usize); + let arg1 = mem::take(args[1usize]).clone().cast::(); + let arg0: &mut _ = &mut args[0usize].write_lock::().unwrap(); + Ok(Dynamic::from(get_by_index(arg0, arg1))) + } + + fn is_method_call(&self) -> bool { true } + fn is_varadic(&self) -> bool { false } + fn clone_boxed(&self) -> Box { + Box::new(get_by_index_token()) + } + fn input_types(&self) -> Box<[TypeId]> { + new_vec![TypeId::of::(), + TypeId::of::()].into_boxed_slice() + } + } + pub fn get_by_index_token_callable() -> CallableFunction { + CallableFunction::from_plugin(get_by_index_token()) + } + pub fn get_by_index_token_input_types() -> Box<[TypeId]> { + get_by_index_token().input_types() + } + } + }; + + let item_mod = syn::parse2::(input_tokens).unwrap(); + assert_streams_eq(item_mod.generate(), expected_tokens); + } + + #[test] + fn one_index_setter_fn_module() { + let input_tokens: TokenStream = quote! { + pub mod one_index_fn { + #[rhai_fn(index_set)] + pub fn set_by_index(x: &mut MyCollection, i: u64, item: FLOAT) { + x.entry(i).set(item) + } + } + }; + + let expected_tokens = quote! { + pub mod one_index_fn { + pub fn set_by_index(x: &mut MyCollection, i: u64, item: FLOAT) { + x.entry(i).set(item) + } + #[allow(unused_imports)] + use super::*; + #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { + let mut m = Module::new(); + m.set_fn("index$set$", FnAccess::Public, + &[core::any::TypeId::of::(), + core::any::TypeId::of::(), + core::any::TypeId::of::()], + CallableFunction::from_plugin(set_by_index_token())); + m + } + #[allow(non_camel_case_types)] + struct set_by_index_token(); + impl PluginFunction for set_by_index_token { + fn call(&self, + args: &mut [&mut Dynamic], pos: Position + ) -> Result> { + debug_assert_eq!(args.len(), 3usize, + "wrong arg count: {} != {}", args.len(), 3usize); + let arg1 = mem::take(args[1usize]).clone().cast::(); + let arg2 = mem::take(args[2usize]).clone().cast::(); + let arg0: &mut _ = &mut args[0usize].write_lock::().unwrap(); + Ok(Dynamic::from(set_by_index(arg0, arg1, arg2))) + } + + fn is_method_call(&self) -> bool { true } + fn is_varadic(&self) -> bool { false } + fn clone_boxed(&self) -> Box { + Box::new(set_by_index_token()) + } + fn input_types(&self) -> Box<[TypeId]> { + new_vec![TypeId::of::(), + TypeId::of::(), + TypeId::of::()].into_boxed_slice() + } + } + pub fn set_by_index_token_callable() -> CallableFunction { + CallableFunction::from_plugin(set_by_index_token()) + } + pub fn set_by_index_token_input_types() -> Box<[TypeId]> { + set_by_index_token().input_types() + } + } + }; + + let item_mod = syn::parse2::(input_tokens).unwrap(); + assert_streams_eq(item_mod.generate(), expected_tokens); + } + + #[test] + fn one_index_setter_and_rename_fn_module() { + let input_tokens: TokenStream = quote! { + pub mod one_index_fn { + #[rhai_fn(name = "set", index_set)] + pub fn set_by_index(x: &mut MyCollection, i: u64, item: FLOAT) { + x.entry(i).set(item) + } + } + }; + + let expected_tokens = quote! { + pub mod one_index_fn { + pub fn set_by_index(x: &mut MyCollection, i: u64, item: FLOAT) { + x.entry(i).set(item) + } + #[allow(unused_imports)] + use super::*; + #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { + let mut m = Module::new(); + m.set_fn("set", FnAccess::Public, + &[core::any::TypeId::of::(), + core::any::TypeId::of::(), + core::any::TypeId::of::()], + CallableFunction::from_plugin(set_by_index_token())); + m.set_fn("index$set$", FnAccess::Public, + &[core::any::TypeId::of::(), + core::any::TypeId::of::(), + core::any::TypeId::of::()], + CallableFunction::from_plugin(set_by_index_token())); + m + } + #[allow(non_camel_case_types)] + struct set_by_index_token(); + impl PluginFunction for set_by_index_token { + fn call(&self, + args: &mut [&mut Dynamic], pos: Position + ) -> Result> { + debug_assert_eq!(args.len(), 3usize, + "wrong arg count: {} != {}", args.len(), 3usize); + let arg1 = mem::take(args[1usize]).clone().cast::(); + let arg2 = mem::take(args[2usize]).clone().cast::(); + let arg0: &mut _ = &mut args[0usize].write_lock::().unwrap(); + Ok(Dynamic::from(set_by_index(arg0, arg1, arg2))) + } + + fn is_method_call(&self) -> bool { true } + fn is_varadic(&self) -> bool { false } + fn clone_boxed(&self) -> Box { + Box::new(set_by_index_token()) + } + fn input_types(&self) -> Box<[TypeId]> { + new_vec![TypeId::of::(), + TypeId::of::(), + TypeId::of::()].into_boxed_slice() + } + } + pub fn set_by_index_token_callable() -> CallableFunction { + CallableFunction::from_plugin(set_by_index_token()) + } + pub fn set_by_index_token_input_types() -> Box<[TypeId]> { + set_by_index_token().input_types() + } + } + }; + + let item_mod = syn::parse2::(input_tokens).unwrap(); + assert_streams_eq(item_mod.generate(), expected_tokens); + } + #[test] fn one_constant_nested_module() { let input_tokens: TokenStream = quote! { diff --git a/codegen/ui_tests/export_mod_bad_attr.stderr b/codegen/ui_tests/export_mod_bad_attr.stderr index 9704d74d..c9318637 100644 --- a/codegen/ui_tests/export_mod_bad_attr.stderr +++ b/codegen/ui_tests/export_mod_bad_attr.stderr @@ -4,8 +4,8 @@ error: unknown attribute 'unknown' 11 | #[rhai_fn(unknown = "thing")] | ^^^^^^^ -error[E0433]: failed to resolve: use of undeclared type or module `test_mod` +error[E0433]: failed to resolve: use of undeclared crate or module `test_mod` --> $DIR/export_mod_bad_attr.rs:22:8 | 22 | if test_mod::test_fn(n) { - | ^^^^^^^^ use of undeclared type or module `test_mod` + | ^^^^^^^^ use of undeclared crate or module `test_mod` diff --git a/codegen/ui_tests/export_mod_bad_value.stderr b/codegen/ui_tests/export_mod_bad_value.stderr index 63b22e4e..683e689d 100644 --- a/codegen/ui_tests/export_mod_bad_value.stderr +++ b/codegen/ui_tests/export_mod_bad_value.stderr @@ -4,8 +4,8 @@ error: expecting string literal 11 | #[rhai_fn(name = true)] | ^^^^ -error[E0433]: failed to resolve: use of undeclared type or module `test_mod` +error[E0433]: failed to resolve: use of undeclared crate or module `test_mod` --> $DIR/export_mod_bad_value.rs:22:8 | 22 | if test_mod::test_fn(n) { - | ^^^^^^^^ use of undeclared type or module `test_mod` + | ^^^^^^^^ use of undeclared crate or module `test_mod` diff --git a/codegen/ui_tests/export_mod_cfg.stderr b/codegen/ui_tests/export_mod_cfg.stderr index b932ec86..a5b9c027 100644 --- a/codegen/ui_tests/export_mod_cfg.stderr +++ b/codegen/ui_tests/export_mod_cfg.stderr @@ -4,8 +4,8 @@ error: cfg attributes not allowed on this item 11 | #[cfg(not(feature = "foo"))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0433]: failed to resolve: use of undeclared type or module `test_mod` +error[E0433]: failed to resolve: use of undeclared crate or module `test_mod` --> $DIR/export_mod_cfg.rs:23:8 | 23 | if test_mod::test_fn(n) { - | ^^^^^^^^ use of undeclared type or module `test_mod` + | ^^^^^^^^ use of undeclared crate or module `test_mod` diff --git a/codegen/ui_tests/export_mod_extra_value.stderr b/codegen/ui_tests/export_mod_extra_value.stderr index 0e3c65d6..9ad71395 100644 --- a/codegen/ui_tests/export_mod_extra_value.stderr +++ b/codegen/ui_tests/export_mod_extra_value.stderr @@ -4,8 +4,8 @@ error: extraneous value 11 | #[rhai_fn(return_raw = "yes")] | ^^^^^ -error[E0433]: failed to resolve: use of undeclared type or module `test_mod` +error[E0433]: failed to resolve: use of undeclared crate or module `test_mod` --> $DIR/export_mod_extra_value.rs:22:8 | 22 | if test_mod::test_fn(n) { - | ^^^^^^^^ use of undeclared type or module `test_mod` + | ^^^^^^^^ use of undeclared crate or module `test_mod` diff --git a/codegen/ui_tests/export_mod_junk_arg.stderr b/codegen/ui_tests/export_mod_junk_arg.stderr index f4505bae..8d0a9ef9 100644 --- a/codegen/ui_tests/export_mod_junk_arg.stderr +++ b/codegen/ui_tests/export_mod_junk_arg.stderr @@ -4,8 +4,8 @@ error: expecting identifier 11 | #[rhai_fn("wheeeee")] | ^^^^^^^^^ -error[E0433]: failed to resolve: use of undeclared type or module `test_mod` +error[E0433]: failed to resolve: use of undeclared crate or module `test_mod` --> $DIR/export_mod_junk_arg.rs:22:8 | 22 | if test_mod::test_fn(n) { - | ^^^^^^^^ use of undeclared type or module `test_mod` + | ^^^^^^^^ use of undeclared crate or module `test_mod` diff --git a/codegen/ui_tests/export_mod_missing_value.stderr b/codegen/ui_tests/export_mod_missing_value.stderr index f479f5fa..b4819fce 100644 --- a/codegen/ui_tests/export_mod_missing_value.stderr +++ b/codegen/ui_tests/export_mod_missing_value.stderr @@ -4,8 +4,8 @@ error: requires value 11 | #[rhai_fn(name)] | ^^^^ -error[E0433]: failed to resolve: use of undeclared type or module `test_mod` +error[E0433]: failed to resolve: use of undeclared crate or module `test_mod` --> $DIR/export_mod_missing_value.rs:22:8 | 22 | if test_mod::test_fn(n) { - | ^^^^^^^^ use of undeclared type or module `test_mod` + | ^^^^^^^^ use of undeclared crate or module `test_mod` diff --git a/codegen/ui_tests/export_mod_path_attr.stderr b/codegen/ui_tests/export_mod_path_attr.stderr index d96d7c8c..55ca0b2c 100644 --- a/codegen/ui_tests/export_mod_path_attr.stderr +++ b/codegen/ui_tests/export_mod_path_attr.stderr @@ -4,8 +4,8 @@ error: expecting attribute name 11 | #[rhai_fn(rhai::name = "thing")] | ^^^^^^^^^^ -error[E0433]: failed to resolve: use of undeclared type or module `test_mod` +error[E0433]: failed to resolve: use of undeclared crate or module `test_mod` --> $DIR/export_mod_path_attr.rs:22:8 | 22 | if test_mod::test_fn(n) { - | ^^^^^^^^ use of undeclared type or module `test_mod` + | ^^^^^^^^ use of undeclared crate or module `test_mod` diff --git a/codegen/ui_tests/export_mod_raw_noreturn.stderr b/codegen/ui_tests/export_mod_raw_noreturn.stderr index a548f339..74fd381f 100644 --- a/codegen/ui_tests/export_mod_raw_noreturn.stderr +++ b/codegen/ui_tests/export_mod_raw_noreturn.stderr @@ -4,8 +4,8 @@ error: return_raw functions must return Result 12 | pub fn test_fn(input: &mut Point) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0433]: failed to resolve: use of undeclared type or module `test_mod` +error[E0433]: failed to resolve: use of undeclared crate or module `test_mod` --> $DIR/export_mod_raw_noreturn.rs:22:5 | 22 | test_mod::test_fn(&mut n); - | ^^^^^^^^ use of undeclared type or module `test_mod` + | ^^^^^^^^ use of undeclared crate or module `test_mod` diff --git a/codegen/ui_tests/module_cfg_const.stderr b/codegen/ui_tests/module_cfg_const.stderr index 4eaf1b06..9d3bd14d 100644 --- a/codegen/ui_tests/module_cfg_const.stderr +++ b/codegen/ui_tests/module_cfg_const.stderr @@ -4,8 +4,8 @@ error: cfg attributes not allowed on this item 13 | #[cfg(feature = "foo")] | ^^^^^^^^^^^^^^^^^^^^^^^ -error[E0433]: failed to resolve: use of undeclared type or module `test_module` +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` --> $DIR/module_cfg_const.rs:26:8 | 26 | if test_module::test_fn(n) { - | ^^^^^^^^^^^ use of undeclared type or module `test_module` + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/module_cfg_fn.stderr b/codegen/ui_tests/module_cfg_fn.stderr index 80ac12f8..f2529cbd 100644 --- a/codegen/ui_tests/module_cfg_fn.stderr +++ b/codegen/ui_tests/module_cfg_fn.stderr @@ -4,8 +4,8 @@ error: cfg attributes not allowed on this item 11 | #[cfg(not(feature = "foo"))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0433]: failed to resolve: use of undeclared type or module `test_module` +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` --> $DIR/module_cfg_fn.rs:22:8 | 22 | if test_module::test_fn(n) { - | ^^^^^^^^^^^ use of undeclared type or module `test_module` + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_fn_bad_attr.stderr b/codegen/ui_tests/rhai_fn_bad_attr.stderr index 3fe059dd..0f7fa449 100644 --- a/codegen/ui_tests/rhai_fn_bad_attr.stderr +++ b/codegen/ui_tests/rhai_fn_bad_attr.stderr @@ -4,8 +4,8 @@ error: unknown attribute 'unknown' 11 | #[rhai_fn(unknown = "thing")] | ^^^^^^^ -error[E0433]: failed to resolve: use of undeclared type or module `test_module` +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` --> $DIR/rhai_fn_bad_attr.rs:22:8 | 22 | if test_module::test_fn(n) { - | ^^^^^^^^^^^ use of undeclared type or module `test_module` + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_fn_bad_value.stderr b/codegen/ui_tests/rhai_fn_bad_value.stderr index fedaed9b..f899ddad 100644 --- a/codegen/ui_tests/rhai_fn_bad_value.stderr +++ b/codegen/ui_tests/rhai_fn_bad_value.stderr @@ -4,8 +4,8 @@ error: expecting string literal 11 | #[rhai_fn(name = true)] | ^^^^ -error[E0433]: failed to resolve: use of undeclared type or module `test_module` +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` --> $DIR/rhai_fn_bad_value.rs:22:8 | 22 | if test_module::test_fn(n) { - | ^^^^^^^^^^^ use of undeclared type or module `test_module` + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_fn_extra_value.stderr b/codegen/ui_tests/rhai_fn_extra_value.stderr index 0597f4c2..44ecfc0c 100644 --- a/codegen/ui_tests/rhai_fn_extra_value.stderr +++ b/codegen/ui_tests/rhai_fn_extra_value.stderr @@ -4,8 +4,8 @@ error: extraneous value 11 | #[rhai_fn(return_raw = "yes")] | ^^^^^ -error[E0433]: failed to resolve: use of undeclared type or module `test_module` +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` --> $DIR/rhai_fn_extra_value.rs:22:8 | 22 | if test_module::test_fn(n) { - | ^^^^^^^^^^^ use of undeclared type or module `test_module` + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_fn_getter_conflict.rs b/codegen/ui_tests/rhai_fn_getter_conflict.rs new file mode 100644 index 00000000..66cdb9d0 --- /dev/null +++ b/codegen/ui_tests/rhai_fn_getter_conflict.rs @@ -0,0 +1,28 @@ +use rhai::plugin::*; + +#[derive(Clone)] +pub struct Point { + x: f32, + y: f32, +} + +#[export_module] +pub mod test_module { + pub use super::Point; + #[rhai_fn(name = "foo", get = "foo", set = "bar")] + pub fn test_fn(input: Point) -> bool { + input.x > input.y + } +} + +fn main() { + let n = Point { + x: 0.0, + y: 10.0, + }; + if test_module::test_fn(n) { + println!("yes"); + } else { + println!("no"); + } +} diff --git a/codegen/ui_tests/rhai_fn_getter_conflict.stderr b/codegen/ui_tests/rhai_fn_getter_conflict.stderr new file mode 100644 index 00000000..b1011bc6 --- /dev/null +++ b/codegen/ui_tests/rhai_fn_getter_conflict.stderr @@ -0,0 +1,11 @@ +error: conflicting setter + --> $DIR/rhai_fn_getter_conflict.rs:12:42 + | +12 | #[rhai_fn(name = "foo", get = "foo", set = "bar")] + | ^^^^^^^^^^^ + +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` + --> $DIR/rhai_fn_getter_conflict.rs:23:8 + | +23 | if test_module::test_fn(n) { + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_fn_getter_multiple.rs b/codegen/ui_tests/rhai_fn_getter_multiple.rs new file mode 100644 index 00000000..d60a11ba --- /dev/null +++ b/codegen/ui_tests/rhai_fn_getter_multiple.rs @@ -0,0 +1,28 @@ +use rhai::plugin::*; + +#[derive(Clone)] +pub struct Point { + x: f32, + y: f32, +} + +#[export_module] +pub mod test_module { + pub use super::Point; + #[rhai_fn(name = "foo", get = "foo", get = "bar")] + pub fn test_fn(input: Point) -> bool { + input.x > input.y + } +} + +fn main() { + let n = Point { + x: 0.0, + y: 10.0, + }; + if test_module::test_fn(n) { + println!("yes"); + } else { + println!("no"); + } +} diff --git a/codegen/ui_tests/rhai_fn_getter_multiple.stderr b/codegen/ui_tests/rhai_fn_getter_multiple.stderr new file mode 100644 index 00000000..b37aca65 --- /dev/null +++ b/codegen/ui_tests/rhai_fn_getter_multiple.stderr @@ -0,0 +1,11 @@ +error: conflicting getter + --> $DIR/rhai_fn_getter_multiple.rs:12:42 + | +12 | #[rhai_fn(name = "foo", get = "foo", get = "bar")] + | ^^^^^^^^^^^ + +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` + --> $DIR/rhai_fn_getter_multiple.rs:23:8 + | +23 | if test_module::test_fn(n) { + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_fn_getter_return.rs b/codegen/ui_tests/rhai_fn_getter_return.rs new file mode 100644 index 00000000..da7c8454 --- /dev/null +++ b/codegen/ui_tests/rhai_fn_getter_return.rs @@ -0,0 +1,29 @@ +use rhai::plugin::*; + +#[derive(Clone)] +pub struct Point { + x: f32, + y: f32, +} + +#[export_module] +pub mod test_module { + pub use super::Point; + #[rhai_fn(get = "foo")] + pub fn test_fn(input: &mut Point) { + input.x *= 2.0; + } +} + +fn main() { + let mut n = Point { + x: 0.0, + y: 10.0, + }; + test_module::test_fn(&mut n); + if n.x > 10.0 { + println!("yes"); + } else { + println!("no"); + } +} diff --git a/codegen/ui_tests/rhai_fn_getter_return.stderr b/codegen/ui_tests/rhai_fn_getter_return.stderr new file mode 100644 index 00000000..9dad293b --- /dev/null +++ b/codegen/ui_tests/rhai_fn_getter_return.stderr @@ -0,0 +1,11 @@ +error: property getter must return a value + --> $DIR/rhai_fn_getter_return.rs:13:9 + | +13 | pub fn test_fn(input: &mut Point) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` + --> $DIR/rhai_fn_getter_return.rs:23:5 + | +23 | test_module::test_fn(&mut n); + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_fn_getter_signature.rs b/codegen/ui_tests/rhai_fn_getter_signature.rs new file mode 100644 index 00000000..c4f1952d --- /dev/null +++ b/codegen/ui_tests/rhai_fn_getter_signature.rs @@ -0,0 +1,28 @@ +use rhai::plugin::*; + +#[derive(Clone)] +pub struct Point { + x: f32, + y: f32, +} + +#[export_module] +pub mod test_module { + pub use super::Point; + #[rhai_fn(get = "foo")] + pub fn test_fn(input: Point, value: bool) -> bool { + value && input.x > input.y + } +} + +fn main() { + let n = Point { + x: 0.0, + y: 10.0, + }; + if test_module::test_fn(n, true) { + println!("yes"); + } else { + println!("no"); + } +} diff --git a/codegen/ui_tests/rhai_fn_getter_signature.stderr b/codegen/ui_tests/rhai_fn_getter_signature.stderr new file mode 100644 index 00000000..0689c83f --- /dev/null +++ b/codegen/ui_tests/rhai_fn_getter_signature.stderr @@ -0,0 +1,11 @@ +error: property getter requires exactly 1 argument + --> $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 + | +23 | if test_module::test_fn(n, true) { + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_fn_index_getter_multiple.rs b/codegen/ui_tests/rhai_fn_index_getter_multiple.rs new file mode 100644 index 00000000..450c8aa5 --- /dev/null +++ b/codegen/ui_tests/rhai_fn_index_getter_multiple.rs @@ -0,0 +1,28 @@ +use rhai::plugin::*; + +#[derive(Clone)] +pub struct Point { + x: f32, + y: f32, +} + +#[export_module] +pub mod test_module { + pub use super::Point; + #[rhai_fn(name = "foo", index_get, index_get)] + pub fn test_fn(input: Point) -> bool { + input.x > input.y + } +} + +fn main() { + let n = Point { + x: 0.0, + y: 10.0, + }; + if test_module::test_fn(n) { + println!("yes"); + } else { + println!("no"); + } +} diff --git a/codegen/ui_tests/rhai_fn_index_getter_multiple.stderr b/codegen/ui_tests/rhai_fn_index_getter_multiple.stderr new file mode 100644 index 00000000..30cc273d --- /dev/null +++ b/codegen/ui_tests/rhai_fn_index_getter_multiple.stderr @@ -0,0 +1,11 @@ +error: conflicting index_get + --> $DIR/rhai_fn_index_getter_multiple.rs:12:40 + | +12 | #[rhai_fn(name = "foo", index_get, index_get)] + | ^^^^^^^^^ + +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` + --> $DIR/rhai_fn_index_getter_multiple.rs:23:8 + | +23 | if test_module::test_fn(n) { + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_fn_index_getter_return.rs b/codegen/ui_tests/rhai_fn_index_getter_return.rs new file mode 100644 index 00000000..32b8c3ee --- /dev/null +++ b/codegen/ui_tests/rhai_fn_index_getter_return.rs @@ -0,0 +1,28 @@ +use rhai::plugin::*; + +#[derive(Clone)] +pub struct Point { + x: f32, + y: f32, +} + +#[export_module] +pub mod test_module { + pub use super::Point; + #[rhai_fn(index_get)] + pub fn test_fn(input: &mut Point, i: f32) { + input.x *= 2.0; + } +} + +fn main() { + let mut n = Point { + x: 0.0, + y: 10.0, + }; + if test_module::test_fn(&mut n, 5.0) { + println!("yes"); + } else { + println!("no"); + } +} diff --git a/codegen/ui_tests/rhai_fn_index_getter_return.stderr b/codegen/ui_tests/rhai_fn_index_getter_return.stderr new file mode 100644 index 00000000..b8da178e --- /dev/null +++ b/codegen/ui_tests/rhai_fn_index_getter_return.stderr @@ -0,0 +1,11 @@ +error: index getter must return a value + --> $DIR/rhai_fn_index_getter_return.rs:13:9 + | +13 | pub fn test_fn(input: &mut Point, i: f32) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` + --> $DIR/rhai_fn_index_getter_return.rs:23:8 + | +23 | if test_module::test_fn(&mut n, 5.0) { + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_fn_index_getter_signature.rs b/codegen/ui_tests/rhai_fn_index_getter_signature.rs new file mode 100644 index 00000000..e6fe68f5 --- /dev/null +++ b/codegen/ui_tests/rhai_fn_index_getter_signature.rs @@ -0,0 +1,28 @@ +use rhai::plugin::*; + +#[derive(Clone)] +pub struct Point { + x: f32, + y: f32, +} + +#[export_module] +pub mod test_module { + pub use super::Point; + #[rhai_fn(index_get)] + pub fn test_fn(input: Point) -> bool { + input.x > input.y + } +} + +fn main() { + let n = Point { + x: 0.0, + y: 10.0, + }; + if test_module::test_fn(n) { + println!("yes"); + } else { + println!("no"); + } +} diff --git a/codegen/ui_tests/rhai_fn_index_getter_signature.stderr b/codegen/ui_tests/rhai_fn_index_getter_signature.stderr new file mode 100644 index 00000000..7e86413e --- /dev/null +++ b/codegen/ui_tests/rhai_fn_index_getter_signature.stderr @@ -0,0 +1,11 @@ +error: index getter requires exactly 2 arguments + --> $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 + | +23 | if test_module::test_fn(n) { + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_fn_index_setter_multiple.rs b/codegen/ui_tests/rhai_fn_index_setter_multiple.rs new file mode 100644 index 00000000..77d8cf3e --- /dev/null +++ b/codegen/ui_tests/rhai_fn_index_setter_multiple.rs @@ -0,0 +1,28 @@ +use rhai::plugin::*; + +#[derive(Clone)] +pub struct Point { + x: f32, + y: f32, +} + +#[export_module] +pub mod test_module { + pub use super::Point; + #[rhai_fn(name = "foo", index_set, index_set)] + pub fn test_fn(input: Point) -> bool { + input.x > input.y + } +} + +fn main() { + let n = Point { + x: 0.0, + y: 10.0, + }; + if test_module::test_fn(n) { + println!("yes"); + } else { + println!("no"); + } +} diff --git a/codegen/ui_tests/rhai_fn_index_setter_multiple.stderr b/codegen/ui_tests/rhai_fn_index_setter_multiple.stderr new file mode 100644 index 00000000..1e45346d --- /dev/null +++ b/codegen/ui_tests/rhai_fn_index_setter_multiple.stderr @@ -0,0 +1,11 @@ +error: conflicting index_set + --> $DIR/rhai_fn_index_setter_multiple.rs:12:40 + | +12 | #[rhai_fn(name = "foo", index_set, index_set)] + | ^^^^^^^^^ + +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` + --> $DIR/rhai_fn_index_setter_multiple.rs:23:8 + | +23 | if test_module::test_fn(n) { + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_fn_junk_arg.stderr b/codegen/ui_tests/rhai_fn_junk_arg.stderr index e2054eef..2fa0be8d 100644 --- a/codegen/ui_tests/rhai_fn_junk_arg.stderr +++ b/codegen/ui_tests/rhai_fn_junk_arg.stderr @@ -4,8 +4,8 @@ error: expecting identifier 11 | #[rhai_fn("wheeeee")] | ^^^^^^^^^ -error[E0433]: failed to resolve: use of undeclared type or module `test_module` +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` --> $DIR/rhai_fn_junk_arg.rs:22:8 | 22 | if test_module::test_fn(n) { - | ^^^^^^^^^^^ use of undeclared type or module `test_module` + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_fn_missing_value.stderr b/codegen/ui_tests/rhai_fn_missing_value.stderr index 6ea8040a..1cd4b241 100644 --- a/codegen/ui_tests/rhai_fn_missing_value.stderr +++ b/codegen/ui_tests/rhai_fn_missing_value.stderr @@ -4,8 +4,8 @@ error: requires value 11 | #[rhai_fn(name)] | ^^^^ -error[E0433]: failed to resolve: use of undeclared type or module `test_module` +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` --> $DIR/rhai_fn_missing_value.rs:22:8 | 22 | if test_module::test_fn(n) { - | ^^^^^^^^^^^ use of undeclared type or module `test_module` + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_fn_path_attr.stderr b/codegen/ui_tests/rhai_fn_path_attr.stderr index 5b471f16..70c3b155 100644 --- a/codegen/ui_tests/rhai_fn_path_attr.stderr +++ b/codegen/ui_tests/rhai_fn_path_attr.stderr @@ -4,8 +4,8 @@ error: expecting attribute name 11 | #[rhai_fn(rhai::name = "thing")] | ^^^^^^^^^^ -error[E0433]: failed to resolve: use of undeclared type or module `test_module` +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` --> $DIR/rhai_fn_path_attr.rs:22:8 | 22 | if test_module::test_fn(n) { - | ^^^^^^^^^^^ use of undeclared type or module `test_module` + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_fn_rename_collision.stderr b/codegen/ui_tests/rhai_fn_rename_collision.stderr index 19ddfd35..fcbecd8a 100644 --- a/codegen/ui_tests/rhai_fn_rename_collision.stderr +++ b/codegen/ui_tests/rhai_fn_rename_collision.stderr @@ -10,8 +10,8 @@ error: duplicated function renamed 'foo' 12 | #[rhai_fn(name = "foo")] | ^^^^^^^^^^^^ -error[E0433]: failed to resolve: use of undeclared type or module `test_module` +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` --> $DIR/rhai_fn_rename_collision.rs:28:8 | 28 | if test_module::test_fn(n) { - | ^^^^^^^^^^^ use of undeclared type or module `test_module` + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_fn_rename_collision_oneattr.stderr b/codegen/ui_tests/rhai_fn_rename_collision_oneattr.stderr index 6702b43a..b10e434c 100644 --- a/codegen/ui_tests/rhai_fn_rename_collision_oneattr.stderr +++ b/codegen/ui_tests/rhai_fn_rename_collision_oneattr.stderr @@ -10,8 +10,8 @@ error: duplicated function 'foo' 17 | pub fn foo(input: Point) -> bool { | ^^^ -error[E0433]: failed to resolve: use of undeclared type or module `test_module` +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` --> $DIR/rhai_fn_rename_collision_oneattr.rs:27:8 | 27 | if test_module::test_fn(n) { - | ^^^^^^^^^^^ use of undeclared type or module `test_module` + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_fn_rename_collision_oneattr_multiple.stderr b/codegen/ui_tests/rhai_fn_rename_collision_oneattr_multiple.stderr index 091a5893..77375279 100644 --- a/codegen/ui_tests/rhai_fn_rename_collision_oneattr_multiple.stderr +++ b/codegen/ui_tests/rhai_fn_rename_collision_oneattr_multiple.stderr @@ -1,17 +1,17 @@ -error: duplicate Rhai signature for 'get$bar' - --> $DIR/rhai_fn_rename_collision_oneattr_multiple.rs:17:15 - | -17 | #[rhai_fn(get = "bar")] - | ^^^^^^^^^^^ - -error: duplicated function renamed 'get$bar' +error: duplicate Rhai signature for 'foo' --> $DIR/rhai_fn_rename_collision_oneattr_multiple.rs:12:15 | 12 | #[rhai_fn(name = "foo", get = "bar")] | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0433]: failed to resolve: use of undeclared type or module `test_module` +error: duplicated function 'foo' + --> $DIR/rhai_fn_rename_collision_oneattr_multiple.rs:18:12 + | +18 | pub fn foo(input: Point) -> bool { + | ^^^ + +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` --> $DIR/rhai_fn_rename_collision_oneattr_multiple.rs:25:8 | 25 | if test_module::test_fn(n) { - | ^^^^^^^^^^^ use of undeclared type or module `test_module` + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_fn_rename_collision_with_itself.stderr b/codegen/ui_tests/rhai_fn_rename_collision_with_itself.stderr index ffbca90b..5e27be60 100644 --- a/codegen/ui_tests/rhai_fn_rename_collision_with_itself.stderr +++ b/codegen/ui_tests/rhai_fn_rename_collision_with_itself.stderr @@ -10,8 +10,8 @@ error: duplicated function renamed 'foo' 12 | #[rhai_fn(name = "foo", name = "bar", name = "foo")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0433]: failed to resolve: use of undeclared type or module `test_module` +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` --> $DIR/rhai_fn_rename_collision_with_itself.rs:20:8 | 20 | if test_module::test_fn(n) { - | ^^^^^^^^^^^ use of undeclared type or module `test_module` + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_fn_rename_dollar_sign.rs b/codegen/ui_tests/rhai_fn_rename_dollar_sign.rs new file mode 100644 index 00000000..69af645c --- /dev/null +++ b/codegen/ui_tests/rhai_fn_rename_dollar_sign.rs @@ -0,0 +1,28 @@ +use rhai::plugin::*; + +#[derive(Clone)] +pub struct Point { + x: f32, + y: f32, +} + +#[export_module] +pub mod test_module { + pub use super::Point; + #[rhai_fn(name = "big$caching")] + pub fn test_fn(input: Point) -> bool { + input.x > input.y + } +} + +fn main() { + let n = Point { + x: 0.0, + y: 10.0, + }; + if test_module::test_fn(n) { + println!("yes"); + } else { + println!("no"); + } +} diff --git a/codegen/ui_tests/rhai_fn_rename_dollar_sign.stderr b/codegen/ui_tests/rhai_fn_rename_dollar_sign.stderr new file mode 100644 index 00000000..3438bdfc --- /dev/null +++ b/codegen/ui_tests/rhai_fn_rename_dollar_sign.stderr @@ -0,0 +1,11 @@ +error: Rhai function names may not contain dollar sign + --> $DIR/rhai_fn_rename_dollar_sign.rs:12:22 + | +12 | #[rhai_fn(name = "big$caching")] + | ^^^^^^^^^^^^^ + +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` + --> $DIR/rhai_fn_rename_dollar_sign.rs:23:8 + | +23 | if test_module::test_fn(n) { + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_fn_rename_dot.stderr b/codegen/ui_tests/rhai_fn_rename_dot.stderr index 61299e8b..964e1b34 100644 --- a/codegen/ui_tests/rhai_fn_rename_dot.stderr +++ b/codegen/ui_tests/rhai_fn_rename_dot.stderr @@ -4,8 +4,8 @@ error: Rhai function names may not contain dot 12 | #[rhai_fn(name = "foo.bar")] | ^^^^^^^^^ -error[E0433]: failed to resolve: use of undeclared type or module `test_module` +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` --> $DIR/rhai_fn_rename_dot.rs:23:8 | 23 | if test_module::test_fn(n) { - | ^^^^^^^^^^^ use of undeclared type or module `test_module` + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_fn_rename_to_index_getter.rs b/codegen/ui_tests/rhai_fn_rename_to_index_getter.rs new file mode 100644 index 00000000..394113b4 --- /dev/null +++ b/codegen/ui_tests/rhai_fn_rename_to_index_getter.rs @@ -0,0 +1,28 @@ +use rhai::plugin::*; + +#[derive(Clone)] +pub struct Point { + x: f32, + y: f32, +} + +#[export_module] +pub mod test_module { + pub use super::Point; + #[rhai_fn(name = "index$get$")] + pub fn test_fn(input: Point) -> bool { + input.x > input.y + } +} + +fn main() { + let n = Point { + x: 0.0, + y: 10.0, + }; + if test_module::test_fn(n) { + println!("yes"); + } else { + println!("no"); + } +} diff --git a/codegen/ui_tests/rhai_fn_rename_to_index_getter.stderr b/codegen/ui_tests/rhai_fn_rename_to_index_getter.stderr new file mode 100644 index 00000000..64c11d92 --- /dev/null +++ b/codegen/ui_tests/rhai_fn_rename_to_index_getter.stderr @@ -0,0 +1,11 @@ +error: use attribute 'index_get' instead + --> $DIR/rhai_fn_rename_to_index_getter.rs:12:15 + | +12 | #[rhai_fn(name = "index$get$")] + | ^^^^^^^^^^^^^^^^^^^ + +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` + --> $DIR/rhai_fn_rename_to_index_getter.rs:23:8 + | +23 | if test_module::test_fn(n) { + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_fn_rename_to_index_setter.rs b/codegen/ui_tests/rhai_fn_rename_to_index_setter.rs new file mode 100644 index 00000000..cb5d7c8e --- /dev/null +++ b/codegen/ui_tests/rhai_fn_rename_to_index_setter.rs @@ -0,0 +1,28 @@ +use rhai::plugin::*; + +#[derive(Clone)] +pub struct Point { + x: f32, + y: f32, +} + +#[export_module] +pub mod test_module { + pub use super::Point; + #[rhai_fn(name = "index$set$")] + pub fn test_fn(input: Point) -> bool { + input.x > input.y + } +} + +fn main() { + let n = Point { + x: 0.0, + y: 10.0, + }; + if test_module::test_fn(n) { + println!("yes"); + } else { + println!("no"); + } +} diff --git a/codegen/ui_tests/rhai_fn_rename_to_index_setter.stderr b/codegen/ui_tests/rhai_fn_rename_to_index_setter.stderr new file mode 100644 index 00000000..0d9c9f3d --- /dev/null +++ b/codegen/ui_tests/rhai_fn_rename_to_index_setter.stderr @@ -0,0 +1,11 @@ +error: use attribute 'index_set' instead + --> $DIR/rhai_fn_rename_to_index_setter.rs:12:15 + | +12 | #[rhai_fn(name = "index$set$")] + | ^^^^^^^^^^^^^^^^^^^ + +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` + --> $DIR/rhai_fn_rename_to_index_setter.rs:23:8 + | +23 | if test_module::test_fn(n) { + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_fn_rename_to_prop_getter.rs b/codegen/ui_tests/rhai_fn_rename_to_prop_getter.rs new file mode 100644 index 00000000..d81aa5d5 --- /dev/null +++ b/codegen/ui_tests/rhai_fn_rename_to_prop_getter.rs @@ -0,0 +1,28 @@ +use rhai::plugin::*; + +#[derive(Clone)] +pub struct Point { + x: f32, + y: f32, +} + +#[export_module] +pub mod test_module { + pub use super::Point; + #[rhai_fn(name = "get$foo")] + pub fn test_fn(input: Point) -> bool { + input.x > input.y + } +} + +fn main() { + let n = Point { + x: 0.0, + y: 10.0, + }; + if test_module::test_fn(n) { + println!("yes"); + } else { + println!("no"); + } +} diff --git a/codegen/ui_tests/rhai_fn_rename_to_prop_getter.stderr b/codegen/ui_tests/rhai_fn_rename_to_prop_getter.stderr new file mode 100644 index 00000000..cee9c66b --- /dev/null +++ b/codegen/ui_tests/rhai_fn_rename_to_prop_getter.stderr @@ -0,0 +1,11 @@ +error: use attribute 'getter = "foo"' instead + --> $DIR/rhai_fn_rename_to_prop_getter.rs:12:15 + | +12 | #[rhai_fn(name = "get$foo")] + | ^^^^^^^^^^^^^^^^ + +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` + --> $DIR/rhai_fn_rename_to_prop_getter.rs:23:8 + | +23 | if test_module::test_fn(n) { + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_fn_rename_to_prop_setter.rs b/codegen/ui_tests/rhai_fn_rename_to_prop_setter.rs new file mode 100644 index 00000000..d81aa5d5 --- /dev/null +++ b/codegen/ui_tests/rhai_fn_rename_to_prop_setter.rs @@ -0,0 +1,28 @@ +use rhai::plugin::*; + +#[derive(Clone)] +pub struct Point { + x: f32, + y: f32, +} + +#[export_module] +pub mod test_module { + pub use super::Point; + #[rhai_fn(name = "get$foo")] + pub fn test_fn(input: Point) -> bool { + input.x > input.y + } +} + +fn main() { + let n = Point { + x: 0.0, + y: 10.0, + }; + if test_module::test_fn(n) { + println!("yes"); + } else { + println!("no"); + } +} diff --git a/codegen/ui_tests/rhai_fn_rename_to_prop_setter.stderr b/codegen/ui_tests/rhai_fn_rename_to_prop_setter.stderr new file mode 100644 index 00000000..9544ce93 --- /dev/null +++ b/codegen/ui_tests/rhai_fn_rename_to_prop_setter.stderr @@ -0,0 +1,11 @@ +error: use attribute 'getter = "foo"' instead + --> $DIR/rhai_fn_rename_to_prop_setter.rs:12:15 + | +12 | #[rhai_fn(name = "get$foo")] + | ^^^^^^^^^^^^^^^^ + +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` + --> $DIR/rhai_fn_rename_to_prop_setter.rs:23:8 + | +23 | if test_module::test_fn(n) { + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_fn_setter_index_signature.rs b/codegen/ui_tests/rhai_fn_setter_index_signature.rs new file mode 100644 index 00000000..21a71846 --- /dev/null +++ b/codegen/ui_tests/rhai_fn_setter_index_signature.rs @@ -0,0 +1,28 @@ +use rhai::plugin::*; + +#[derive(Clone)] +pub struct Point { + x: f32, + y: f32, +} + +#[export_module] +pub mod test_module { + pub use super::Point; + #[rhai_fn(index_set)] + pub fn test_fn(input: Point) -> bool { + input.x > input.y + } +} + +fn main() { + let n = Point { + x: 0.0, + y: 10.0, + }; + if test_module::test_fn(n) { + println!("yes"); + } else { + println!("no"); + } +} diff --git a/codegen/ui_tests/rhai_fn_setter_index_signature.stderr b/codegen/ui_tests/rhai_fn_setter_index_signature.stderr new file mode 100644 index 00000000..80cfc5fb --- /dev/null +++ b/codegen/ui_tests/rhai_fn_setter_index_signature.stderr @@ -0,0 +1,11 @@ +error: index setter requires exactly 3 arguments + --> $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 + | +23 | if test_module::test_fn(n) { + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_fn_setter_multiple.rs b/codegen/ui_tests/rhai_fn_setter_multiple.rs new file mode 100644 index 00000000..170a373c --- /dev/null +++ b/codegen/ui_tests/rhai_fn_setter_multiple.rs @@ -0,0 +1,28 @@ +use rhai::plugin::*; + +#[derive(Clone)] +pub struct Point { + x: f32, + y: f32, +} + +#[export_module] +pub mod test_module { + pub use super::Point; + #[rhai_fn(name = "foo", set = "foo", set = "bar")] + pub fn test_fn(input: Point) -> bool { + input.x > input.y + } +} + +fn main() { + let n = Point { + x: 0.0, + y: 10.0, + }; + if test_module::test_fn(n) { + println!("yes"); + } else { + println!("no"); + } +} diff --git a/codegen/ui_tests/rhai_fn_setter_multiple.stderr b/codegen/ui_tests/rhai_fn_setter_multiple.stderr new file mode 100644 index 00000000..6783fb1a --- /dev/null +++ b/codegen/ui_tests/rhai_fn_setter_multiple.stderr @@ -0,0 +1,11 @@ +error: conflicting setter + --> $DIR/rhai_fn_setter_multiple.rs:12:42 + | +12 | #[rhai_fn(name = "foo", set = "foo", set = "bar")] + | ^^^^^^^^^^^ + +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` + --> $DIR/rhai_fn_setter_multiple.rs:23:8 + | +23 | if test_module::test_fn(n) { + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_fn_setter_return.rs b/codegen/ui_tests/rhai_fn_setter_return.rs new file mode 100644 index 00000000..5a2542a8 --- /dev/null +++ b/codegen/ui_tests/rhai_fn_setter_return.rs @@ -0,0 +1,29 @@ +use rhai::plugin::*; + +#[derive(Clone)] +pub struct Point { + x: f32, + y: f32, +} + +#[export_module] +pub mod test_module { + pub use super::Point; + #[rhai_fn(set = "foo")] + pub fn test_fn(input: &mut Point, value: f32) -> bool { + let z = if value % 2 { input.x } else { input.y }; + *input.x = z; + } +} + +fn main() { + let mut n = Point { + x: 0.0, + y: 10.0, + }; + if test_module::test_fn(&mut n, 5.0) { + println!("yes"); + } else { + println!("no"); + } +} diff --git a/codegen/ui_tests/rhai_fn_setter_return.stderr b/codegen/ui_tests/rhai_fn_setter_return.stderr new file mode 100644 index 00000000..db37a80f --- /dev/null +++ b/codegen/ui_tests/rhai_fn_setter_return.stderr @@ -0,0 +1,11 @@ +error: property setter must return no 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 + | +24 | if test_module::test_fn(&mut n, 5.0) { + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_fn_setter_signature.rs b/codegen/ui_tests/rhai_fn_setter_signature.rs new file mode 100644 index 00000000..06e401a6 --- /dev/null +++ b/codegen/ui_tests/rhai_fn_setter_signature.rs @@ -0,0 +1,28 @@ +use rhai::plugin::*; + +#[derive(Clone)] +pub struct Point { + x: f32, + y: f32, +} + +#[export_module] +pub mod test_module { + pub use super::Point; + #[rhai_fn(set = "foo")] + pub fn test_fn(input: Point) -> bool { + input.x > input.y + } +} + +fn main() { + let n = Point { + x: 0.0, + y: 10.0, + }; + if test_module::test_fn(n) { + println!("yes"); + } else { + println!("no"); + } +} diff --git a/codegen/ui_tests/rhai_fn_setter_signature.stderr b/codegen/ui_tests/rhai_fn_setter_signature.stderr new file mode 100644 index 00000000..55127a25 --- /dev/null +++ b/codegen/ui_tests/rhai_fn_setter_signature.stderr @@ -0,0 +1,11 @@ +error: property setter requires exactly 2 arguments + --> $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 + | +23 | if test_module::test_fn(n) { + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_mod_bad_attr.stderr b/codegen/ui_tests/rhai_mod_bad_attr.stderr index 87209038..8ac71f6c 100644 --- a/codegen/ui_tests/rhai_mod_bad_attr.stderr +++ b/codegen/ui_tests/rhai_mod_bad_attr.stderr @@ -4,8 +4,8 @@ error: unknown attribute 'unknown' 11 | #[rhai_mod(unknown = "thing")] | ^^^^^^^ -error[E0433]: failed to resolve: use of undeclared type or module `test_module` +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` --> $DIR/rhai_mod_bad_attr.rs:24:8 | 24 | if test_module::test_fn(n) { - | ^^^^^^^^^^^ use of undeclared type or module `test_module` + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_mod_bad_value.stderr b/codegen/ui_tests/rhai_mod_bad_value.stderr index 765f8a1f..76498300 100644 --- a/codegen/ui_tests/rhai_mod_bad_value.stderr +++ b/codegen/ui_tests/rhai_mod_bad_value.stderr @@ -4,8 +4,8 @@ error: expecting string literal 11 | #[rhai_mod(name = true)] | ^^^^ -error[E0433]: failed to resolve: use of undeclared type or module `test_module` +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` --> $DIR/rhai_mod_bad_value.rs:24:8 | 24 | if test_module::test_fn(n) { - | ^^^^^^^^^^^ use of undeclared type or module `test_module` + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_mod_junk_arg.stderr b/codegen/ui_tests/rhai_mod_junk_arg.stderr index c2dcc28a..a8c733a0 100644 --- a/codegen/ui_tests/rhai_mod_junk_arg.stderr +++ b/codegen/ui_tests/rhai_mod_junk_arg.stderr @@ -4,8 +4,8 @@ error: expecting identifier 11 | #[rhai_mod("wheeeee")] | ^^^^^^^^^ -error[E0433]: failed to resolve: use of undeclared type or module `test_module` +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` --> $DIR/rhai_mod_junk_arg.rs:24:8 | 24 | if test_module::test_fn(n) { - | ^^^^^^^^^^^ use of undeclared type or module `test_module` + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_mod_missing_value.stderr b/codegen/ui_tests/rhai_mod_missing_value.stderr index 62e0502f..ed452923 100644 --- a/codegen/ui_tests/rhai_mod_missing_value.stderr +++ b/codegen/ui_tests/rhai_mod_missing_value.stderr @@ -4,8 +4,8 @@ error: requires value 11 | #[rhai_mod(name)] | ^^^^ -error[E0433]: failed to resolve: use of undeclared type or module `test_module` +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` --> $DIR/rhai_mod_missing_value.rs:24:8 | 24 | if test_module::test_fn(n) { - | ^^^^^^^^^^^ use of undeclared type or module `test_module` + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_mod_name_collisions.stderr b/codegen/ui_tests/rhai_mod_name_collisions.stderr index 539bd3eb..53c209e1 100644 --- a/codegen/ui_tests/rhai_mod_name_collisions.stderr +++ b/codegen/ui_tests/rhai_mod_name_collisions.stderr @@ -10,8 +10,8 @@ error: duplicated function 'test_fn' 12 | pub fn test_fn(input: Point) -> bool { | ^^^^^^^ -error[E0433]: failed to resolve: use of undeclared type or module `test_module` +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` --> $DIR/rhai_mod_name_collisions.rs:26:8 | 26 | if test_module::test_fn(n) { - | ^^^^^^^^^^^ use of undeclared type or module `test_module` + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_mod_path_attr.stderr b/codegen/ui_tests/rhai_mod_path_attr.stderr index bd165324..cb99205a 100644 --- a/codegen/ui_tests/rhai_mod_path_attr.stderr +++ b/codegen/ui_tests/rhai_mod_path_attr.stderr @@ -4,8 +4,8 @@ error: expecting attribute name 11 | #[rhai_mod(rhai::name = "thing")] | ^^^^^^^^^^ -error[E0433]: failed to resolve: use of undeclared type or module `test_module` +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` --> $DIR/rhai_mod_path_attr.rs:24:8 | 24 | if test_module::test_fn(n) { - | ^^^^^^^^^^^ use of undeclared type or module `test_module` + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_mod_return_raw.stderr b/codegen/ui_tests/rhai_mod_return_raw.stderr index f767dce0..50ab3bef 100644 --- a/codegen/ui_tests/rhai_mod_return_raw.stderr +++ b/codegen/ui_tests/rhai_mod_return_raw.stderr @@ -4,8 +4,8 @@ error: unknown attribute 'return_raw' 11 | #[rhai_mod(return_raw = "yes")] | ^^^^^^^^^^ -error[E0433]: failed to resolve: use of undeclared type or module `test_module` +error[E0433]: failed to resolve: use of undeclared crate or module `test_module` --> $DIR/rhai_mod_return_raw.rs:24:8 | 24 | if test_module::test_fn(n) { - | ^^^^^^^^^^^ use of undeclared type or module `test_module` + | ^^^^^^^^^^^ use of undeclared crate or module `test_module` diff --git a/codegen/ui_tests/rhai_mod_unknown_type.stderr b/codegen/ui_tests/rhai_mod_unknown_type.stderr index 392f90a9..87360967 100644 --- a/codegen/ui_tests/rhai_mod_unknown_type.stderr +++ b/codegen/ui_tests/rhai_mod_unknown_type.stderr @@ -15,7 +15,7 @@ help: consider importing one of these items | 11 | use core::fmt::Pointer; | -11 | use crate::mem::fmt::Pointer; +11 | use crate::new_vec::fmt::Pointer; | 11 | use std::fmt::Pointer; | diff --git a/doc/src/language/modules/export.md b/doc/src/language/modules/export.md index f05e008c..2152dc74 100644 --- a/doc/src/language/modules/export.md +++ b/doc/src/language/modules/export.md @@ -43,6 +43,9 @@ export x as answer; // the variable 'x' is exported under the alias 'answer' } ``` +[`private`] variables are used to initialize the module. +They cannot be used apart from this. + Functions --------- @@ -59,6 +62,9 @@ fn inc(x) { x + 1 } // script-defined function - default public private fn foo() {} // private function - hidden ``` +[`private`] functions are commonly called to initialize the module. +They cannot be called apart from this. + Sub-Modules ----------- diff --git a/doc/src/plugins/module.md b/doc/src/plugins/module.md index 9f510998..40232140 100644 --- a/doc/src/plugins/module.md +++ b/doc/src/plugins/module.md @@ -13,8 +13,8 @@ This Rust module can then either be loaded into an [`Engine`] as a normal [modul registered as a [custom package]. This is done by using the `exported_module!` macro. -Using`#[export_module]` and `exported_module!` ---------------------------------------------- +`#[export_module]` and `exported_module!` +---------------------------------------- Apply `#[export_module]` onto a Rust module to convert all `pub` functions into Rhai plugin functions. @@ -92,6 +92,8 @@ With `#[rhai_fn(name = "...")]`, multiple functions may be registered under the Operators (which require function names that are not valid for Rust) can also be registered this way. +Registering the same function name with the same parameter types will cause a parsing error. + ```rust use rhai::plugins::*; // a "prelude" import for macros @@ -155,6 +157,38 @@ mod my_module { ``` +Multiple Registrations +---------------------- + +Parameters to the `#[rhai_fn(...)]` attribute can be applied multiple times. + +This is especially useful for the `name = "..."`, `get = "..."` and `set = "..."` parameters +to give multiple alternative names to the same function. + +```rust +use rhai::plugins::*; // a "prelude" import for macros + +#[export_module] +mod my_module { + // This function can be called in five ways + #[rhai_fn(name = "get_prop_value", name = "prop", name = "+", set = "prop", index_get)] + pub fn prop_function(obj: &mut MyType, index: i64) -> i64 { + obj.prop[index] + } +} +``` + +The above function can be called in five ways: + +| Parameter for `#[rhai_fn(...)]` | Type | Call style | +| ------------------------------- | :-------------: | --------------------------------------------- | +| `name = "get_prop_value"` | Method function | `get_prop_value(x, 0)`, `x.get_prop_value(0)` | +| `name = "prop"` | Method function | `prop(x, 0)`, `x.prop(0)` | +| `name = "+"` | Operator | `x + 42` | +| `set = "prop"` | Setter | `x.prop = 42` | +| `index_get` | Index getter | `x[0]` | + + Fallible Functions ------------------ diff --git a/src/fn_call.rs b/src/fn_call.rs index e7c5fdc7..3728d4e8 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -444,7 +444,7 @@ impl Engine { //|| self.global_module.contains_fn(hash_script, pub_only) || self.global_module.contains_fn(hash_fn, pub_only) // Then check packages - //|| self.packages.contains_fn(hash_script, pub_only) + || self.packages.contains_fn(hash_script, pub_only) || self.packages.contains_fn(hash_fn, pub_only) } @@ -477,7 +477,17 @@ impl Engine { // Qualifiers (none) + function name + number of arguments + argument `TypeId`'s. let arg_types = args.iter().map(|a| a.type_id()); - let hash_fn = calc_fn_hash(empty(), fn_name, args.len(), arg_types); + let hash_fn = calc_fn_hash( + empty(), + fn_name, + if args.is_empty() { + // Distinguish between a script function and a native function with no parameters + usize::MAX + } else { + args.len() + }, + arg_types, + ); match fn_name { // type_of @@ -514,9 +524,15 @@ impl Engine { // Normal script function call #[cfg(not(feature = "no_function"))] - _ if hash_script > 0 && lib.contains_fn(hash_script, pub_only) => { + _ if lib.contains_fn(hash_script, pub_only) + || self.packages.contains_fn(hash_script, pub_only) => + { // Get scripted function - let func = lib.get_fn(hash_script, pub_only).unwrap().get_fn_def(); + let func = lib + .get_fn(hash_script, pub_only) + .or_else(|| self.packages.get_fn(hash_script, pub_only)) + .unwrap() + .get_fn_def(); let scope = &mut Scope::new(); let mods = &mut Imports::new(); @@ -559,6 +575,7 @@ impl Engine { Ok((result, false)) } + // Normal native function call _ => self.call_native_fn( state, lib, fn_name, hash_fn, args, is_ref, pub_only, def_val, diff --git a/src/lib.rs b/src/lib.rs index 4939e119..c0d00fab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -93,7 +93,7 @@ pub use result::EvalAltResult; pub use scope::Scope; pub use syntax::{EvalContext, Expression}; pub use token::Position; -pub use utils::calc_fn_spec as calc_fn_hash; +pub use utils::calc_fn_hash; pub use rhai_codegen::*; diff --git a/src/module.rs b/src/module.rs index c6e161b6..3513dd7f 100644 --- a/src/module.rs +++ b/src/module.rs @@ -247,9 +247,13 @@ impl Module { &mut self, hash_var: u64, ) -> Result<&mut Dynamic, Box> { - self.all_variables.get_mut(&hash_var).ok_or_else(|| { - EvalAltResult::ErrorVariableNotFound(String::new(), Position::none()).into() - }) + if hash_var == 0 { + Err(EvalAltResult::ErrorVariableNotFound(String::new(), Position::none()).into()) + } else { + self.all_variables.get_mut(&hash_var).ok_or_else(|| { + EvalAltResult::ErrorVariableNotFound(String::new(), Position::none()).into() + }) + } } /// Set a script-defined function into the module. @@ -354,7 +358,9 @@ impl Module { /// assert!(module.contains_fn(hash, true)); /// ``` pub fn contains_fn(&self, hash_fn: u64, public_only: bool) -> bool { - if public_only { + if hash_fn == 0 { + false + } else if public_only { self.functions .get(&hash_fn) .map(|(_, access, _, _)| match access { @@ -383,7 +389,14 @@ impl Module { ) -> u64 { let name = name.into(); - let hash_fn = calc_fn_hash(empty(), &name, arg_types.len(), arg_types.iter().cloned()); + let args_len = if arg_types.is_empty() { + // Distinguish between a script function and a function with no parameters + usize::MAX + } else { + arg_types.len() + }; + + let hash_fn = calc_fn_hash(empty(), &name, args_len, arg_types.iter().cloned()); let params = arg_types.into_iter().cloned().collect(); @@ -910,13 +923,17 @@ impl Module { /// The `u64` hash is calculated by the function `crate::calc_fn_hash`. /// It is also returned by the `set_fn_XXX` calls. pub(crate) fn get_fn(&self, hash_fn: u64, public_only: bool) -> Option<&Func> { - self.functions - .get(&hash_fn) - .and_then(|(_, access, _, f)| match access { - _ if !public_only => Some(f), - FnAccess::Public => Some(f), - FnAccess::Private => None, - }) + if hash_fn == 0 { + None + } else { + self.functions + .get(&hash_fn) + .and_then(|(_, access, _, f)| match access { + _ if !public_only => Some(f), + FnAccess::Public => Some(f), + FnAccess::Private => None, + }) + } } /// Get a modules-qualified function. diff --git a/src/optimize.rs b/src/optimize.rs index ab4147c3..83f828f4 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -149,7 +149,7 @@ fn call_fn_with_constant_arguments( fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt { match stmt { // if expr { Noop } - Stmt::IfThenElse(x) if matches!(x.1, Stmt::Noop(_)) => { + Stmt::IfThenElse(x) if matches!(x.1, Stmt::Noop(_)) && x.2.is_none() => { state.set_dirty(); let pos = x.0.position(); diff --git a/src/parser.rs b/src/parser.rs index a3232247..d598c7a6 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1838,7 +1838,7 @@ fn parse_unary( // Call negative function expr => { let op = "-"; - let hash = calc_fn_hash(empty(), op, 2, empty()); + let hash = calc_fn_hash(empty(), op, 1, empty()); let mut args = StaticVec::new(); args.push(expr); @@ -1865,7 +1865,7 @@ fn parse_unary( args.push(expr); let op = "!"; - let hash = calc_fn_hash(empty(), op, 2, empty()); + let hash = calc_fn_hash(empty(), op, 1, empty()); Ok(Expr::FnCall(Box::new(( (op.into(), true, false, pos), diff --git a/src/plugin.rs b/src/plugin.rs index b03df99c..ef4a6590 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -1,28 +1,15 @@ -//! Module defining plugins in Rhai. Is exported for use by plugin authors. +//! Module defining plugins in Rhai for use by plugin authors. pub use crate::{ - stdlib::any::TypeId, - stdlib::boxed::Box, - stdlib::format, - stdlib::string::ToString, - stdlib::vec::Vec, - stdlib::vec as new_vec, - stdlib::mem, - fn_native::CallableFunction, - Dynamic, - Engine, - EvalAltResult, - FnAccess, - ImmutableString, - Module, - Position, - RegisterResultFn, + fn_native::CallableFunction, stdlib::any::TypeId, stdlib::boxed::Box, stdlib::format, + stdlib::mem, stdlib::string::ToString, stdlib::vec as new_vec, stdlib::vec::Vec, Dynamic, + Engine, EvalAltResult, FnAccess, ImmutableString, Module, Position, RegisterResultFn, }; -#[cfg(features = "no_module")] -pub use rhai_codegen::{export_fn, register_exported_fn}; #[cfg(not(features = "no_module"))] pub use rhai_codegen::*; +#[cfg(features = "no_module")] +pub use rhai_codegen::{export_fn, register_exported_fn}; #[cfg(features = "sync")] /// Represents an externally-written plugin for the Rhai interpreter. diff --git a/src/utils.rs b/src/utils.rs index ee448260..2587262b 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -69,7 +69,7 @@ impl BuildHasher for StraightHasherBuilder { /// # Note /// /// The first module name is skipped. Hashing starts from the _second_ module in the chain. -pub fn calc_fn_spec<'a>( +pub fn calc_fn_hash<'a>( modules: impl Iterator, fn_name: &str, num: usize, diff --git a/tests/packages.rs b/tests/packages.rs index c08234fe..6e871496 100644 --- a/tests/packages.rs +++ b/tests/packages.rs @@ -1,10 +1,10 @@ -use rhai::{Engine, EvalAltResult, INT, Scope}; use rhai::packages::{Package, StandardPackage}; +use rhai::{Engine, EvalAltResult, Module, Scope, INT}; #[test] fn test_packages() -> Result<(), Box> { - let e = Engine::new(); - let ast = e.compile("x")?; + let engine = Engine::new(); + let ast = engine.compile("x")?; let std_pkg = StandardPackage::new(); let make_call = |x: INT| -> Result> { @@ -24,10 +24,22 @@ fn test_packages() -> Result<(), Box> { engine.eval_ast_with_scope::(&mut scope, &ast) }; - // The following loop creates 10,000 Engine instances! - for x in 0..10_000 { - assert_eq!(make_call(x)?, x); - } + assert_eq!(make_call(42)?, 42); + + Ok(()) +} + +#[cfg(not(feature = "no_function"))] +#[cfg(not(feature = "no_module"))] +#[test] +fn test_packages_with_script() -> Result<(), Box> { + let mut engine = Engine::new(); + let ast = engine.compile("fn foo(x) { x + 1 }")?; + let module = Module::eval_ast_as_new(Scope::new(), &ast, &engine)?; + + engine.load_package(module); + + assert_eq!(engine.eval::("foo(41)")?, 42); Ok(()) }