From bcf14025a793b9de2e505ac8698d1152f320e713 Mon Sep 17 00:00:00 2001 From: J Henry Waugh Date: Sat, 15 Aug 2020 21:51:14 -0500 Subject: [PATCH] Add rhai_fn nested attribute and skip fn parameter --- codegen/src/function.rs | 18 +++- codegen/src/module.rs | 90 ++++++++++++++++--- codegen/tests/test_nested.rs | 36 ++++++++ codegen/ui_tests/rhai_fn_bad_attr.rs | 27 ++++++ codegen/ui_tests/rhai_fn_bad_attr.stderr | 11 +++ codegen/ui_tests/rhai_fn_bad_value.rs | 27 ++++++ codegen/ui_tests/rhai_fn_bad_value.stderr | 11 +++ codegen/ui_tests/rhai_fn_extra_value.rs | 27 ++++++ codegen/ui_tests/rhai_fn_extra_value.stderr | 11 +++ codegen/ui_tests/rhai_fn_junk_arg.rs | 27 ++++++ codegen/ui_tests/rhai_fn_junk_arg.stderr | 11 +++ codegen/ui_tests/rhai_fn_missing_value.rs | 27 ++++++ codegen/ui_tests/rhai_fn_missing_value.stderr | 11 +++ codegen/ui_tests/rhai_fn_path_attr.rs | 27 ++++++ codegen/ui_tests/rhai_fn_path_attr.stderr | 11 +++ 15 files changed, 357 insertions(+), 15 deletions(-) create mode 100644 codegen/tests/test_nested.rs create mode 100644 codegen/ui_tests/rhai_fn_bad_attr.rs create mode 100644 codegen/ui_tests/rhai_fn_bad_attr.stderr create mode 100644 codegen/ui_tests/rhai_fn_bad_value.rs create mode 100644 codegen/ui_tests/rhai_fn_bad_value.stderr create mode 100644 codegen/ui_tests/rhai_fn_extra_value.rs create mode 100644 codegen/ui_tests/rhai_fn_extra_value.stderr create mode 100644 codegen/ui_tests/rhai_fn_junk_arg.rs create mode 100644 codegen/ui_tests/rhai_fn_junk_arg.stderr create mode 100644 codegen/ui_tests/rhai_fn_missing_value.rs create mode 100644 codegen/ui_tests/rhai_fn_missing_value.stderr create mode 100644 codegen/ui_tests/rhai_fn_path_attr.rs create mode 100644 codegen/ui_tests/rhai_fn_path_attr.stderr diff --git a/codegen/src/function.rs b/codegen/src/function.rs index 0e4f1260..1724e308 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -17,6 +17,15 @@ use syn::{parse::Parse, parse::ParseStream, parse::Parser, spanned::Spanned}; pub(crate) struct ExportedFnParams { pub name: Option, pub return_raw: bool, + pub skip: bool, +} + +impl ExportedFnParams { + pub fn skip() -> ExportedFnParams { + let mut skip = ExportedFnParams::default(); + skip.skip = true; + skip + } } impl Parse for ExportedFnParams { @@ -68,6 +77,7 @@ impl Parse for ExportedFnParams { let mut name = None; let mut return_raw = false; + let mut skip = false; for (ident, value) in attrs.drain() { match (ident.to_string().as_ref(), value) { ("name", Some(s)) => name = Some(s.value()), @@ -76,6 +86,10 @@ impl Parse for ExportedFnParams { ("return_raw", Some(s)) => { return Err(syn::Error::new(s.span(), "extraneous value")) } + ("skip", None) => skip = true, + ("skip", Some(s)) => { + return Err(syn::Error::new(s.span(), "extraneous value")) + } (attr, _) => { return Err(syn::Error::new( ident.span(), @@ -85,7 +99,7 @@ impl Parse for ExportedFnParams { } } - Ok(ExportedFnParams { name, return_raw }) + Ok(ExportedFnParams { name, return_raw, skip, ..Default::default() }) } } @@ -95,7 +109,7 @@ pub(crate) struct ExportedFn { signature: syn::Signature, is_public: bool, mut_receiver: bool, - params: ExportedFnParams, + pub params: ExportedFnParams, } impl Parse for ExportedFn { diff --git a/codegen/src/module.rs b/codegen/src/module.rs index 9e042bb9..827607c8 100644 --- a/codegen/src/module.rs +++ b/codegen/src/module.rs @@ -1,7 +1,7 @@ use quote::{quote, ToTokens}; use syn::{parse::Parse, parse::ParseStream}; -use crate::function::ExportedFn; +use crate::function::{ExportedFn, ExportedFnParams}; use crate::rhai_module::ExportedConst; #[cfg(no_std)] @@ -12,6 +12,22 @@ use std::vec as new_vec; #[cfg(no_std)] use core::mem; +fn inner_fn_attributes(f: &mut syn::ItemFn) -> syn::Result { + if let Some(rhai_fn_idx) = f.attrs.iter().position(|a| { + a.path + .get_ident() + .map(|i| i.to_string() == "rhai_fn") + .unwrap_or(false) + }) { + let rhai_fn_attr = f.attrs.remove(rhai_fn_idx); + rhai_fn_attr.parse_args() + } else if let syn::Visibility::Public(_) = f.vis { + Ok(ExportedFnParams::default()) + } else { + Ok(ExportedFnParams::skip()) + } +} + #[derive(Debug)] pub(crate) struct Module { mod_all: Option, @@ -21,25 +37,27 @@ pub(crate) struct Module { impl Parse for Module { fn parse(input: ParseStream) -> syn::Result { - let mod_all: syn::ItemMod = input.parse()?; + let mut mod_all: syn::ItemMod = input.parse()?; let fns: Vec<_>; let consts: Vec<_>; - if let Some((_, ref content)) = mod_all.content { + if let Some((_, ref mut content)) = mod_all.content { fns = content - .iter() + .iter_mut() .filter_map(|item| match item { - syn::Item::Fn(f) => { - if let syn::Visibility::Public(_) = f.vis { - Some(f) - } else { - None - } - } + syn::Item::Fn(f) => Some(f), _ => None, }) - .try_fold(Vec::new(), |mut vec, itemfn| { + .try_fold(Vec::new(), |mut vec, mut itemfn| { + let params = match inner_fn_attributes(&mut itemfn) { + Ok(p) => p, + Err(e) => return Err(e), + }; syn::parse2::(itemfn.to_token_stream()) - .map(|f| vec.push(f)) + .map(|mut f| { + f.params = params; + f + }) + .map(|f| if !f.params.skip { vec.push(f) }) .map(|_| vec) })?; consts = content @@ -217,6 +235,22 @@ mod module_tests { assert!(item_mod.consts.is_empty()); } + #[test] + fn one_skipped_fn_module() { + let input_tokens: TokenStream = quote! { + pub mod one_fn { + #[rhai_fn(skip)] + pub fn get_mystic_number() -> INT { + 42 + } + } + }; + + let item_mod = syn::parse2::(input_tokens).unwrap(); + assert!(item_mod.fns.is_empty()); + assert!(item_mod.consts.is_empty()); + } + #[test] fn one_private_constant_module() { let input_tokens: TokenStream = quote! { @@ -552,6 +586,36 @@ mod generate_tests { assert_streams_eq(item_mod.generate(), expected_tokens); } + #[test] + fn one_skipped_fn_module() { + let input_tokens: TokenStream = quote! { + pub mod one_fn { + #[rhai_fn(skip)] + pub fn get_mystic_number() -> INT { + 42 + } + } + }; + + let expected_tokens = quote! { + pub mod one_fn { + pub fn get_mystic_number() -> INT { + 42 + } + #[allow(unused_imports)] + use super::*; + #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { + let mut m = Module::new(); + m + } + } + }; + + let item_mod = syn::parse2::(input_tokens).unwrap(); + assert_streams_eq(item_mod.generate(), expected_tokens); + } + #[test] fn one_private_constant_module() { let input_tokens: TokenStream = quote! { diff --git a/codegen/tests/test_nested.rs b/codegen/tests/test_nested.rs new file mode 100644 index 00000000..4881b927 --- /dev/null +++ b/codegen/tests/test_nested.rs @@ -0,0 +1,36 @@ +use rhai::module_resolvers::*; +use rhai::{Engine, EvalAltResult, RegisterFn, FLOAT, INT}; + +pub mod one_fn_module_nested_attr { + use rhai::plugin::*; + + #[export_module] + pub mod advanced_math { + use rhai::plugin::*; + use rhai::FLOAT; + + #[rhai_fn(return_raw)] + pub fn get_mystic_number() -> Result> { + Ok(Dynamic::from(42.0 as FLOAT)) + } + } +} + +#[test] +fn one_fn_module_nested_attr_test() -> Result<(), Box> { + let mut engine = Engine::new(); + let m = rhai::exported_module!(crate::one_fn_module_nested_attr::advanced_math); + let mut r = StaticModuleResolver::new(); + r.insert("Math::Advanced".to_string(), m); + engine.set_module_resolver(Some(r)); + + assert_eq!( + engine.eval::( + r#"import "Math::Advanced" as math; + let m = math::get_mystic_number(); + m"# + )?, + 42.0 + ); + Ok(()) +} diff --git a/codegen/ui_tests/rhai_fn_bad_attr.rs b/codegen/ui_tests/rhai_fn_bad_attr.rs new file mode 100644 index 00000000..119efdb3 --- /dev/null +++ b/codegen/ui_tests/rhai_fn_bad_attr.rs @@ -0,0 +1,27 @@ +use rhai::plugin::*; + +#[derive(Clone)] +struct Point { + x: f32, + y: f32, +} + +#[export_module] +pub mod test_module { +#[rhai_fn(unknown = "thing")] +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_bad_attr.stderr b/codegen/ui_tests/rhai_fn_bad_attr.stderr new file mode 100644 index 00000000..3fe059dd --- /dev/null +++ b/codegen/ui_tests/rhai_fn_bad_attr.stderr @@ -0,0 +1,11 @@ +error: unknown attribute 'unknown' + --> $DIR/rhai_fn_bad_attr.rs:11:11 + | +11 | #[rhai_fn(unknown = "thing")] + | ^^^^^^^ + +error[E0433]: failed to resolve: use of undeclared type 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` diff --git a/codegen/ui_tests/rhai_fn_bad_value.rs b/codegen/ui_tests/rhai_fn_bad_value.rs new file mode 100644 index 00000000..307e3c0e --- /dev/null +++ b/codegen/ui_tests/rhai_fn_bad_value.rs @@ -0,0 +1,27 @@ +use rhai::plugin::*; + +#[derive(Clone)] +struct Point { + x: f32, + y: f32, +} + +#[export_module] +pub mod test_module { +#[rhai_fn(name = true)] +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_bad_value.stderr b/codegen/ui_tests/rhai_fn_bad_value.stderr new file mode 100644 index 00000000..fedaed9b --- /dev/null +++ b/codegen/ui_tests/rhai_fn_bad_value.stderr @@ -0,0 +1,11 @@ +error: expecting string literal + --> $DIR/rhai_fn_bad_value.rs:11:18 + | +11 | #[rhai_fn(name = true)] + | ^^^^ + +error[E0433]: failed to resolve: use of undeclared type 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` diff --git a/codegen/ui_tests/rhai_fn_extra_value.rs b/codegen/ui_tests/rhai_fn_extra_value.rs new file mode 100644 index 00000000..f86b2df3 --- /dev/null +++ b/codegen/ui_tests/rhai_fn_extra_value.rs @@ -0,0 +1,27 @@ +use rhai::plugin::*; + +#[derive(Clone)] +struct Point { + x: f32, + y: f32, +} + +#[export_module] +pub mod test_module { +#[rhai_fn(return_raw = "yes")] +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_extra_value.stderr b/codegen/ui_tests/rhai_fn_extra_value.stderr new file mode 100644 index 00000000..0597f4c2 --- /dev/null +++ b/codegen/ui_tests/rhai_fn_extra_value.stderr @@ -0,0 +1,11 @@ +error: extraneous value + --> $DIR/rhai_fn_extra_value.rs:11:24 + | +11 | #[rhai_fn(return_raw = "yes")] + | ^^^^^ + +error[E0433]: failed to resolve: use of undeclared type 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` diff --git a/codegen/ui_tests/rhai_fn_junk_arg.rs b/codegen/ui_tests/rhai_fn_junk_arg.rs new file mode 100644 index 00000000..b84424ae --- /dev/null +++ b/codegen/ui_tests/rhai_fn_junk_arg.rs @@ -0,0 +1,27 @@ +use rhai::plugin::*; + +#[derive(Clone)] +struct Point { + x: f32, + y: f32, +} + +#[export_module] +pub mod test_module { +#[rhai_fn("wheeeee")] +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_junk_arg.stderr b/codegen/ui_tests/rhai_fn_junk_arg.stderr new file mode 100644 index 00000000..e2054eef --- /dev/null +++ b/codegen/ui_tests/rhai_fn_junk_arg.stderr @@ -0,0 +1,11 @@ +error: expecting identifier + --> $DIR/rhai_fn_junk_arg.rs:11:11 + | +11 | #[rhai_fn("wheeeee")] + | ^^^^^^^^^ + +error[E0433]: failed to resolve: use of undeclared type 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` diff --git a/codegen/ui_tests/rhai_fn_missing_value.rs b/codegen/ui_tests/rhai_fn_missing_value.rs new file mode 100644 index 00000000..25349530 --- /dev/null +++ b/codegen/ui_tests/rhai_fn_missing_value.rs @@ -0,0 +1,27 @@ +use rhai::plugin::*; + +#[derive(Clone)] +struct Point { + x: f32, + y: f32, +} + +#[export_module] +pub mod test_module { +#[rhai_fn(name)] +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_missing_value.stderr b/codegen/ui_tests/rhai_fn_missing_value.stderr new file mode 100644 index 00000000..6ea8040a --- /dev/null +++ b/codegen/ui_tests/rhai_fn_missing_value.stderr @@ -0,0 +1,11 @@ +error: requires value + --> $DIR/rhai_fn_missing_value.rs:11:11 + | +11 | #[rhai_fn(name)] + | ^^^^ + +error[E0433]: failed to resolve: use of undeclared type 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` diff --git a/codegen/ui_tests/rhai_fn_path_attr.rs b/codegen/ui_tests/rhai_fn_path_attr.rs new file mode 100644 index 00000000..5c2174a8 --- /dev/null +++ b/codegen/ui_tests/rhai_fn_path_attr.rs @@ -0,0 +1,27 @@ +use rhai::plugin::*; + +#[derive(Clone)] +struct Point { + x: f32, + y: f32, +} + +#[export_module] +pub mod test_module { +#[rhai_fn(rhai::name = "thing")] +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_path_attr.stderr b/codegen/ui_tests/rhai_fn_path_attr.stderr new file mode 100644 index 00000000..5b471f16 --- /dev/null +++ b/codegen/ui_tests/rhai_fn_path_attr.stderr @@ -0,0 +1,11 @@ +error: expecting attribute name + --> $DIR/rhai_fn_path_attr.rs:11:11 + | +11 | #[rhai_fn(rhai::name = "thing")] + | ^^^^^^^^^^ + +error[E0433]: failed to resolve: use of undeclared type 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`