Allow #[cfg(...)] in plugin functions.
This commit is contained in:
parent
0265af415d
commit
3f2dd23e6e
@ -4,6 +4,11 @@ Rhai Release Notes
|
|||||||
Version 1.2.0
|
Version 1.2.0
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
New features
|
||||||
|
------------
|
||||||
|
|
||||||
|
* `#[cfg(...)]` attributes can now be put directly on plugin functions or function defined in a plugin module.
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rhai_codegen"
|
name = "rhai_codegen"
|
||||||
version = "1.1.1"
|
version = "1.2.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
authors = ["jhwgh1968", "Stephen Chung"]
|
authors = ["jhwgh1968", "Stephen Chung"]
|
||||||
description = "Procedural macros support package for Rhai, a scripting language and engine for Rust"
|
description = "Procedural macros support package for Rhai, a scripting language and engine for Rust"
|
||||||
|
@ -129,16 +129,10 @@ pub fn inner_item_attributes<T: ExportedParams>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deny_cfg_attr(attrs: &[syn::Attribute]) -> syn::Result<()> {
|
pub fn collect_cfg_attr(attrs: &[syn::Attribute]) -> Vec<syn::Attribute> {
|
||||||
if let Some(cfg_attr) = attrs
|
attrs
|
||||||
.iter()
|
.iter()
|
||||||
.find(|a| a.path.get_ident().map(|i| *i == "cfg").unwrap_or(false))
|
.filter(|&a| a.path.get_ident().map(|i| *i == "cfg").unwrap_or(false))
|
||||||
{
|
.cloned()
|
||||||
Err(syn::Error::new(
|
.collect()
|
||||||
cfg_attr.span(),
|
|
||||||
"cfg attributes not allowed on this item",
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -281,6 +281,7 @@ pub struct ExportedFn {
|
|||||||
pass_context: bool,
|
pass_context: bool,
|
||||||
mut_receiver: bool,
|
mut_receiver: bool,
|
||||||
params: ExportedFnParams,
|
params: ExportedFnParams,
|
||||||
|
cfg_attrs: Vec<syn::Attribute>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for ExportedFn {
|
impl Parse for ExportedFn {
|
||||||
@ -294,8 +295,7 @@ impl Parse for ExportedFn {
|
|||||||
syn::parse2::<syn::Path>(quote! { rhai::NativeCallContext }).unwrap();
|
syn::parse2::<syn::Path>(quote! { rhai::NativeCallContext }).unwrap();
|
||||||
let mut pass_context = false;
|
let mut pass_context = false;
|
||||||
|
|
||||||
// #[cfg] attributes are not allowed on functions due to what is generated for them
|
let cfg_attrs = crate::attrs::collect_cfg_attr(&fn_all.attrs);
|
||||||
crate::attrs::deny_cfg_attr(&fn_all.attrs)?;
|
|
||||||
|
|
||||||
let visibility = fn_all.vis;
|
let visibility = fn_all.vis;
|
||||||
|
|
||||||
@ -403,6 +403,7 @@ impl Parse for ExportedFn {
|
|||||||
pass_context,
|
pass_context,
|
||||||
mut_receiver,
|
mut_receiver,
|
||||||
params: Default::default(),
|
params: Default::default(),
|
||||||
|
cfg_attrs,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -414,6 +415,10 @@ impl ExportedFn {
|
|||||||
&self.params
|
&self.params
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn cfg_attrs(&self) -> &[syn::Attribute] {
|
||||||
|
&self.cfg_attrs
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update_scope(&mut self, parent_scope: &ExportScope) {
|
pub fn update_scope(&mut self, parent_scope: &ExportScope) {
|
||||||
let keep = match (self.params.skip, parent_scope) {
|
let keep = match (self.params.skip, parent_scope) {
|
||||||
(true, _) => false,
|
(true, _) => false,
|
||||||
@ -498,6 +503,10 @@ impl ExportedFn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_cfg_attrs(&mut self, cfg_attrs: Vec<syn::Attribute>) {
|
||||||
|
self.cfg_attrs = cfg_attrs
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_params(&mut self, mut params: ExportedFnParams) -> syn::Result<()> {
|
pub fn set_params(&mut self, mut params: ExportedFnParams) -> syn::Result<()> {
|
||||||
// Several issues are checked here to avoid issues with diagnostics caused by raising them later.
|
// Several issues are checked here to avoid issues with diagnostics caused by raising them later.
|
||||||
//
|
//
|
||||||
@ -831,11 +840,19 @@ impl ExportedFn {
|
|||||||
#[cfg(not(feature = "metadata"))]
|
#[cfg(not(feature = "metadata"))]
|
||||||
let param_names = quote! {};
|
let param_names = quote! {};
|
||||||
|
|
||||||
|
let cfg_attrs: Vec<_> = self
|
||||||
|
.cfg_attrs()
|
||||||
|
.iter()
|
||||||
|
.map(syn::Attribute::to_token_stream)
|
||||||
|
.collect();
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
|
#(#cfg_attrs)*
|
||||||
impl #type_name {
|
impl #type_name {
|
||||||
#param_names
|
#param_names
|
||||||
#[inline(always)] pub fn param_types() -> [TypeId; #arg_count] { [#(#input_type_exprs),*] }
|
#[inline(always)] pub fn param_types() -> [TypeId; #arg_count] { [#(#input_type_exprs),*] }
|
||||||
}
|
}
|
||||||
|
#(#cfg_attrs)*
|
||||||
impl PluginFunction for #type_name {
|
impl PluginFunction for #type_name {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||||
|
@ -88,7 +88,7 @@
|
|||||||
//!
|
//!
|
||||||
|
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::parse_macro_input;
|
use syn::{parse_macro_input, spanned::Spanned};
|
||||||
|
|
||||||
mod attrs;
|
mod attrs;
|
||||||
mod function;
|
mod function;
|
||||||
@ -130,9 +130,19 @@ pub fn export_fn(
|
|||||||
|
|
||||||
let parsed_params = match crate::attrs::outer_item_attributes(args.into(), "export_fn") {
|
let parsed_params = match crate::attrs::outer_item_attributes(args.into(), "export_fn") {
|
||||||
Ok(args) => args,
|
Ok(args) => args,
|
||||||
Err(err) => return proc_macro::TokenStream::from(err.to_compile_error()),
|
Err(err) => return err.to_compile_error().into(),
|
||||||
};
|
};
|
||||||
let mut function_def = parse_macro_input!(input as function::ExportedFn);
|
let mut function_def = parse_macro_input!(input as function::ExportedFn);
|
||||||
|
|
||||||
|
if !function_def.cfg_attrs().is_empty() {
|
||||||
|
return syn::Error::new(
|
||||||
|
function_def.cfg_attrs()[0].span(),
|
||||||
|
"`cfg` attributes are not allowed for `export_fn`",
|
||||||
|
)
|
||||||
|
.to_compile_error()
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
|
||||||
if let Err(e) = function_def.set_params(parsed_params) {
|
if let Err(e) = function_def.set_params(parsed_params) {
|
||||||
return e.to_compile_error().into();
|
return e.to_compile_error().into();
|
||||||
}
|
}
|
||||||
@ -173,7 +183,7 @@ pub fn export_module(
|
|||||||
) -> proc_macro::TokenStream {
|
) -> proc_macro::TokenStream {
|
||||||
let parsed_params = match crate::attrs::outer_item_attributes(args.into(), "export_module") {
|
let parsed_params = match crate::attrs::outer_item_attributes(args.into(), "export_module") {
|
||||||
Ok(args) => args,
|
Ok(args) => args,
|
||||||
Err(err) => return proc_macro::TokenStream::from(err.to_compile_error()),
|
Err(err) => return err.to_compile_error().into(),
|
||||||
};
|
};
|
||||||
let mut module_def = parse_macro_input!(input as module::Module);
|
let mut module_def = parse_macro_input!(input as module::Module);
|
||||||
if let Err(e) = module_def.set_params(parsed_params) {
|
if let Err(e) = module_def.set_params(parsed_params) {
|
||||||
|
@ -108,6 +108,7 @@ impl Parse for Module {
|
|||||||
let fns: Vec<_>;
|
let fns: Vec<_>;
|
||||||
let mut consts = Vec::new();
|
let mut consts = Vec::new();
|
||||||
let mut sub_modules = Vec::new();
|
let mut sub_modules = Vec::new();
|
||||||
|
|
||||||
if let Some((_, ref mut content)) = mod_all.content {
|
if let Some((_, ref mut content)) = mod_all.content {
|
||||||
// Gather and parse functions.
|
// Gather and parse functions.
|
||||||
fns = content
|
fns = content
|
||||||
@ -117,15 +118,13 @@ impl Parse for Module {
|
|||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.try_fold(Vec::new(), |mut vec, item_fn| {
|
.try_fold(Vec::new(), |mut vec, item_fn| {
|
||||||
// #[cfg] attributes are not allowed on functions
|
|
||||||
crate::attrs::deny_cfg_attr(&item_fn.attrs)?;
|
|
||||||
|
|
||||||
let params =
|
let params =
|
||||||
crate::attrs::inner_item_attributes(&mut item_fn.attrs, "rhai_fn")?;
|
crate::attrs::inner_item_attributes(&mut item_fn.attrs, "rhai_fn")?;
|
||||||
|
|
||||||
syn::parse2::<ExportedFn>(item_fn.to_token_stream())
|
syn::parse2::<ExportedFn>(item_fn.to_token_stream())
|
||||||
.and_then(|mut f| {
|
.and_then(|mut f| {
|
||||||
f.set_params(params)?;
|
f.set_params(params)?;
|
||||||
|
f.set_cfg_attrs(crate::attrs::collect_cfg_attr(&item_fn.attrs));
|
||||||
Ok(f)
|
Ok(f)
|
||||||
})
|
})
|
||||||
.map(|f| vec.push(f))
|
.map(|f| vec.push(f))
|
||||||
@ -141,14 +140,12 @@ impl Parse for Module {
|
|||||||
attrs,
|
attrs,
|
||||||
ty,
|
ty,
|
||||||
..
|
..
|
||||||
}) => {
|
}) if matches!(vis, syn::Visibility::Public(_)) => consts.push((
|
||||||
// #[cfg] attributes are not allowed on const declarations
|
ident.to_string(),
|
||||||
crate::attrs::deny_cfg_attr(&attrs)?;
|
ty.clone(),
|
||||||
|
expr.as_ref().clone(),
|
||||||
if matches!(vis, syn::Visibility::Public(_)) {
|
crate::attrs::collect_cfg_attr(&attrs),
|
||||||
consts.push((ident.to_string(), ty.clone(), expr.as_ref().clone()))
|
)),
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use proc_macro2::{Span, TokenStream};
|
use proc_macro2::{Span, TokenStream};
|
||||||
use quote::quote;
|
use quote::{quote, ToTokens};
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ use crate::function::{
|
|||||||
};
|
};
|
||||||
use crate::module::Module;
|
use crate::module::Module;
|
||||||
|
|
||||||
pub type ExportedConst = (String, Box<syn::Type>, syn::Expr);
|
pub type ExportedConst = (String, Box<syn::Type>, syn::Expr, Vec<syn::Attribute>);
|
||||||
|
|
||||||
pub fn generate_body(
|
pub fn generate_body(
|
||||||
fns: &mut [ExportedFn],
|
fns: &mut [ExportedFn],
|
||||||
@ -25,11 +25,18 @@ pub fn generate_body(
|
|||||||
let str_type_path = syn::parse2::<syn::Path>(quote! { str }).unwrap();
|
let str_type_path = syn::parse2::<syn::Path>(quote! { str }).unwrap();
|
||||||
let string_type_path = syn::parse2::<syn::Path>(quote! { String }).unwrap();
|
let string_type_path = syn::parse2::<syn::Path>(quote! { String }).unwrap();
|
||||||
|
|
||||||
for (const_name, _, _) in consts {
|
for (const_name, _, _, cfg_attrs) in consts {
|
||||||
let const_literal = syn::LitStr::new(&const_name, Span::call_site());
|
let const_literal = syn::LitStr::new(&const_name, Span::call_site());
|
||||||
let const_ref = syn::Ident::new(&const_name, Span::call_site());
|
let const_ref = syn::Ident::new(&const_name, Span::call_site());
|
||||||
|
|
||||||
|
let cfg_attrs: Vec<_> = cfg_attrs
|
||||||
|
.iter()
|
||||||
|
.map(syn::Attribute::to_token_stream)
|
||||||
|
.collect();
|
||||||
|
|
||||||
set_const_statements.push(
|
set_const_statements.push(
|
||||||
syn::parse2::<syn::Stmt>(quote! {
|
syn::parse2::<syn::Stmt>(quote! {
|
||||||
|
#(#cfg_attrs)*
|
||||||
m.set_var(#const_literal, #const_ref);
|
m.set_var(#const_literal, #const_ref);
|
||||||
})
|
})
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
@ -42,13 +49,8 @@ pub fn generate_body(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let module_name = item_mod.module_name();
|
let module_name = item_mod.module_name();
|
||||||
let exported_name: syn::LitStr =
|
let exported_name = syn::LitStr::new(item_mod.exported_name().as_ref(), Span::call_site());
|
||||||
syn::LitStr::new(item_mod.exported_name().as_ref(), Span::call_site());
|
let cfg_attrs = crate::attrs::collect_cfg_attr(item_mod.attrs());
|
||||||
let cfg_attrs: Vec<_> = item_mod
|
|
||||||
.attrs()
|
|
||||||
.iter()
|
|
||||||
.filter(|&a| a.path.get_ident().map(|i| *i == "cfg").unwrap_or(false))
|
|
||||||
.collect();
|
|
||||||
add_mod_blocks.push(
|
add_mod_blocks.push(
|
||||||
syn::parse2::<syn::ExprBlock>(quote! {
|
syn::parse2::<syn::ExprBlock>(quote! {
|
||||||
{
|
{
|
||||||
@ -126,6 +128,12 @@ pub fn generate_body(
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let cfg_attrs: Vec<_> = function
|
||||||
|
.cfg_attrs()
|
||||||
|
.iter()
|
||||||
|
.map(syn::Attribute::to_token_stream)
|
||||||
|
.collect();
|
||||||
|
|
||||||
for fn_literal in reg_names {
|
for fn_literal in reg_names {
|
||||||
let mut namespace = FnNamespaceAccess::Internal;
|
let mut namespace = FnNamespaceAccess::Internal;
|
||||||
|
|
||||||
@ -166,6 +174,7 @@ pub fn generate_body(
|
|||||||
|
|
||||||
set_fn_statements.push(
|
set_fn_statements.push(
|
||||||
syn::parse2::<syn::Stmt>(quote! {
|
syn::parse2::<syn::Stmt>(quote! {
|
||||||
|
#(#cfg_attrs)*
|
||||||
m.set_fn(#fn_literal, FnNamespace::#ns_str, FnAccess::Public,
|
m.set_fn(#fn_literal, FnNamespace::#ns_str, FnAccess::Public,
|
||||||
#param_names, &[#(#fn_input_types),*], #fn_token_name().into());
|
#param_names, &[#(#fn_input_types),*], #fn_token_name().into());
|
||||||
})
|
})
|
||||||
@ -174,9 +183,11 @@ pub fn generate_body(
|
|||||||
}
|
}
|
||||||
|
|
||||||
gen_fn_tokens.push(quote! {
|
gen_fn_tokens.push(quote! {
|
||||||
|
#(#cfg_attrs)*
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
pub struct #fn_token_name();
|
pub struct #fn_token_name();
|
||||||
});
|
});
|
||||||
|
|
||||||
gen_fn_tokens.push(function.generate_impl(&fn_token_name.to_string()));
|
gen_fn_tokens.push(function.generate_impl(&fn_token_name.to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1568,6 +1568,72 @@ mod generate_tests {
|
|||||||
assert_streams_eq(item_mod.generate(), expected_tokens);
|
assert_streams_eq(item_mod.generate(), expected_tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn one_index_getter_fn_with_cfg_attr_module() {
|
||||||
|
let input_tokens: TokenStream = quote! {
|
||||||
|
pub mod one_index_fn {
|
||||||
|
#[cfg(hello)]
|
||||||
|
#[rhai_fn(index_get)]
|
||||||
|
#[some_other_attr]
|
||||||
|
pub fn get_by_index(x: &mut MyCollection, i: u64) -> FLOAT {
|
||||||
|
x.get(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let expected_tokens = quote! {
|
||||||
|
pub mod one_index_fn {
|
||||||
|
#[cfg(hello)]
|
||||||
|
#[some_other_attr]
|
||||||
|
pub fn get_by_index(x: &mut MyCollection, i: u64) -> FLOAT {
|
||||||
|
x.get(i)
|
||||||
|
}
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub fn rhai_module_generate() -> Module {
|
||||||
|
let mut m = Module::new();
|
||||||
|
rhai_generate_into_module(&mut m, false);
|
||||||
|
m.build_index();
|
||||||
|
m
|
||||||
|
}
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
|
||||||
|
#[cfg(hello)]
|
||||||
|
m.set_fn("index$get$", FnNamespace::Global, FnAccess::Public, Some(get_by_index_token::PARAM_NAMES),
|
||||||
|
&[TypeId::of::<MyCollection>(), TypeId::of::<u64>()],
|
||||||
|
get_by_index_token().into());
|
||||||
|
if flatten {} else {}
|
||||||
|
}
|
||||||
|
#[cfg(hello)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub struct get_by_index_token();
|
||||||
|
#[cfg(hello)]
|
||||||
|
impl get_by_index_token {
|
||||||
|
pub const PARAM_NAMES: &'static [&'static str] = &["x: &mut MyCollection", "i: u64", "FLOAT"];
|
||||||
|
#[inline(always)] pub fn param_types() -> [TypeId; 2usize] { [TypeId::of::<MyCollection>(), TypeId::of::<u64>()] }
|
||||||
|
}
|
||||||
|
#[cfg(hello)]
|
||||||
|
impl PluginFunction for get_by_index_token {
|
||||||
|
#[inline(always)]
|
||||||
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||||
|
if args[0usize].is_read_only() {
|
||||||
|
return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into());
|
||||||
|
}
|
||||||
|
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
|
let arg0 = &mut args[0usize].write_lock::<MyCollection>().unwrap();
|
||||||
|
Ok(Dynamic::from(get_by_index(arg0, arg1)))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let item_mod = syn::parse2::<Module>(input_tokens).unwrap();
|
||||||
|
assert_streams_eq(item_mod.generate(), expected_tokens);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn one_index_getter_and_rename_fn_module() {
|
fn one_index_getter_and_rename_fn_module() {
|
||||||
let input_tokens: TokenStream = quote! {
|
let input_tokens: TokenStream = quote! {
|
||||||
@ -1811,6 +1877,7 @@ mod generate_tests {
|
|||||||
pub const MYSTIC_NUMBER: INT = 42;
|
pub const MYSTIC_NUMBER: INT = 42;
|
||||||
}
|
}
|
||||||
pub mod second_is {
|
pub mod second_is {
|
||||||
|
#[cfg(hello)]
|
||||||
pub const SPECIAL_CPU_NUMBER: INT = 68000;
|
pub const SPECIAL_CPU_NUMBER: INT = 68000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1836,6 +1903,7 @@ mod generate_tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub mod second_is {
|
pub mod second_is {
|
||||||
|
#[cfg(hello)]
|
||||||
pub const SPECIAL_CPU_NUMBER: INT = 68000;
|
pub const SPECIAL_CPU_NUMBER: INT = 68000;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -1848,6 +1916,7 @@ mod generate_tests {
|
|||||||
}
|
}
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
|
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
|
||||||
|
#[cfg(hello)]
|
||||||
m.set_var("SPECIAL_CPU_NUMBER", SPECIAL_CPU_NUMBER);
|
m.set_var("SPECIAL_CPU_NUMBER", SPECIAL_CPU_NUMBER);
|
||||||
if flatten {} else {}
|
if flatten {} else {}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user