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
|
||||
=============
|
||||
|
||||
New features
|
||||
------------
|
||||
|
||||
* `#[cfg(...)]` attributes can now be put directly on plugin functions or function defined in a plugin module.
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rhai_codegen"
|
||||
version = "1.1.1"
|
||||
version = "1.2.0"
|
||||
edition = "2018"
|
||||
authors = ["jhwgh1968", "Stephen Chung"]
|
||||
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<()> {
|
||||
if let Some(cfg_attr) = attrs
|
||||
pub fn collect_cfg_attr(attrs: &[syn::Attribute]) -> Vec<syn::Attribute> {
|
||||
attrs
|
||||
.iter()
|
||||
.find(|a| a.path.get_ident().map(|i| *i == "cfg").unwrap_or(false))
|
||||
{
|
||||
Err(syn::Error::new(
|
||||
cfg_attr.span(),
|
||||
"cfg attributes not allowed on this item",
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
.filter(|&a| a.path.get_ident().map(|i| *i == "cfg").unwrap_or(false))
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
@ -281,6 +281,7 @@ pub struct ExportedFn {
|
||||
pass_context: bool,
|
||||
mut_receiver: bool,
|
||||
params: ExportedFnParams,
|
||||
cfg_attrs: Vec<syn::Attribute>,
|
||||
}
|
||||
|
||||
impl Parse for ExportedFn {
|
||||
@ -294,8 +295,7 @@ impl Parse for ExportedFn {
|
||||
syn::parse2::<syn::Path>(quote! { rhai::NativeCallContext }).unwrap();
|
||||
let mut pass_context = false;
|
||||
|
||||
// #[cfg] attributes are not allowed on functions due to what is generated for them
|
||||
crate::attrs::deny_cfg_attr(&fn_all.attrs)?;
|
||||
let cfg_attrs = crate::attrs::collect_cfg_attr(&fn_all.attrs);
|
||||
|
||||
let visibility = fn_all.vis;
|
||||
|
||||
@ -403,6 +403,7 @@ impl Parse for ExportedFn {
|
||||
pass_context,
|
||||
mut_receiver,
|
||||
params: Default::default(),
|
||||
cfg_attrs,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -414,6 +415,10 @@ impl ExportedFn {
|
||||
&self.params
|
||||
}
|
||||
|
||||
pub fn cfg_attrs(&self) -> &[syn::Attribute] {
|
||||
&self.cfg_attrs
|
||||
}
|
||||
|
||||
pub fn update_scope(&mut self, parent_scope: &ExportScope) {
|
||||
let keep = match (self.params.skip, parent_scope) {
|
||||
(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<()> {
|
||||
// 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"))]
|
||||
let param_names = quote! {};
|
||||
|
||||
let cfg_attrs: Vec<_> = self
|
||||
.cfg_attrs()
|
||||
.iter()
|
||||
.map(syn::Attribute::to_token_stream)
|
||||
.collect();
|
||||
|
||||
quote! {
|
||||
#(#cfg_attrs)*
|
||||
impl #type_name {
|
||||
#param_names
|
||||
#[inline(always)] pub fn param_types() -> [TypeId; #arg_count] { [#(#input_type_exprs),*] }
|
||||
}
|
||||
#(#cfg_attrs)*
|
||||
impl PluginFunction for #type_name {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
|
@ -88,7 +88,7 @@
|
||||
//!
|
||||
|
||||
use quote::quote;
|
||||
use syn::parse_macro_input;
|
||||
use syn::{parse_macro_input, spanned::Spanned};
|
||||
|
||||
mod attrs;
|
||||
mod function;
|
||||
@ -130,9 +130,19 @@ pub fn export_fn(
|
||||
|
||||
let parsed_params = match crate::attrs::outer_item_attributes(args.into(), "export_fn") {
|
||||
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);
|
||||
|
||||
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) {
|
||||
return e.to_compile_error().into();
|
||||
}
|
||||
@ -173,7 +183,7 @@ pub fn export_module(
|
||||
) -> proc_macro::TokenStream {
|
||||
let parsed_params = match crate::attrs::outer_item_attributes(args.into(), "export_module") {
|
||||
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);
|
||||
if let Err(e) = module_def.set_params(parsed_params) {
|
||||
|
@ -108,6 +108,7 @@ impl Parse for Module {
|
||||
let fns: Vec<_>;
|
||||
let mut consts = Vec::new();
|
||||
let mut sub_modules = Vec::new();
|
||||
|
||||
if let Some((_, ref mut content)) = mod_all.content {
|
||||
// Gather and parse functions.
|
||||
fns = content
|
||||
@ -117,15 +118,13 @@ impl Parse for Module {
|
||||
_ => None,
|
||||
})
|
||||
.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 =
|
||||
crate::attrs::inner_item_attributes(&mut item_fn.attrs, "rhai_fn")?;
|
||||
|
||||
syn::parse2::<ExportedFn>(item_fn.to_token_stream())
|
||||
.and_then(|mut f| {
|
||||
f.set_params(params)?;
|
||||
f.set_cfg_attrs(crate::attrs::collect_cfg_attr(&item_fn.attrs));
|
||||
Ok(f)
|
||||
})
|
||||
.map(|f| vec.push(f))
|
||||
@ -141,14 +140,12 @@ impl Parse for Module {
|
||||
attrs,
|
||||
ty,
|
||||
..
|
||||
}) => {
|
||||
// #[cfg] attributes are not allowed on const declarations
|
||||
crate::attrs::deny_cfg_attr(&attrs)?;
|
||||
|
||||
if matches!(vis, syn::Visibility::Public(_)) {
|
||||
consts.push((ident.to_string(), ty.clone(), expr.as_ref().clone()))
|
||||
}
|
||||
}
|
||||
}) if matches!(vis, syn::Visibility::Public(_)) => consts.push((
|
||||
ident.to_string(),
|
||||
ty.clone(),
|
||||
expr.as_ref().clone(),
|
||||
crate::attrs::collect_cfg_attr(&attrs),
|
||||
)),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::quote;
|
||||
use quote::{quote, ToTokens};
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
@ -10,7 +10,7 @@ use crate::function::{
|
||||
};
|
||||
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(
|
||||
fns: &mut [ExportedFn],
|
||||
@ -25,11 +25,18 @@ pub fn generate_body(
|
||||
let str_type_path = syn::parse2::<syn::Path>(quote! { str }).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_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(
|
||||
syn::parse2::<syn::Stmt>(quote! {
|
||||
#(#cfg_attrs)*
|
||||
m.set_var(#const_literal, #const_ref);
|
||||
})
|
||||
.unwrap(),
|
||||
@ -42,13 +49,8 @@ pub fn generate_body(
|
||||
continue;
|
||||
}
|
||||
let module_name = item_mod.module_name();
|
||||
let exported_name: syn::LitStr =
|
||||
syn::LitStr::new(item_mod.exported_name().as_ref(), Span::call_site());
|
||||
let cfg_attrs: Vec<_> = item_mod
|
||||
.attrs()
|
||||
.iter()
|
||||
.filter(|&a| a.path.get_ident().map(|i| *i == "cfg").unwrap_or(false))
|
||||
.collect();
|
||||
let exported_name = syn::LitStr::new(item_mod.exported_name().as_ref(), Span::call_site());
|
||||
let cfg_attrs = crate::attrs::collect_cfg_attr(item_mod.attrs());
|
||||
add_mod_blocks.push(
|
||||
syn::parse2::<syn::ExprBlock>(quote! {
|
||||
{
|
||||
@ -126,6 +128,12 @@ pub fn generate_body(
|
||||
})
|
||||
.collect();
|
||||
|
||||
let cfg_attrs: Vec<_> = function
|
||||
.cfg_attrs()
|
||||
.iter()
|
||||
.map(syn::Attribute::to_token_stream)
|
||||
.collect();
|
||||
|
||||
for fn_literal in reg_names {
|
||||
let mut namespace = FnNamespaceAccess::Internal;
|
||||
|
||||
@ -166,6 +174,7 @@ pub fn generate_body(
|
||||
|
||||
set_fn_statements.push(
|
||||
syn::parse2::<syn::Stmt>(quote! {
|
||||
#(#cfg_attrs)*
|
||||
m.set_fn(#fn_literal, FnNamespace::#ns_str, FnAccess::Public,
|
||||
#param_names, &[#(#fn_input_types),*], #fn_token_name().into());
|
||||
})
|
||||
@ -174,9 +183,11 @@ pub fn generate_body(
|
||||
}
|
||||
|
||||
gen_fn_tokens.push(quote! {
|
||||
#(#cfg_attrs)*
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct #fn_token_name();
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#[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]
|
||||
fn one_index_getter_and_rename_fn_module() {
|
||||
let input_tokens: TokenStream = quote! {
|
||||
@ -1811,6 +1877,7 @@ mod generate_tests {
|
||||
pub const MYSTIC_NUMBER: INT = 42;
|
||||
}
|
||||
pub mod second_is {
|
||||
#[cfg(hello)]
|
||||
pub const SPECIAL_CPU_NUMBER: INT = 68000;
|
||||
}
|
||||
}
|
||||
@ -1836,6 +1903,7 @@ mod generate_tests {
|
||||
}
|
||||
}
|
||||
pub mod second_is {
|
||||
#[cfg(hello)]
|
||||
pub const SPECIAL_CPU_NUMBER: INT = 68000;
|
||||
#[allow(unused_imports)]
|
||||
use super::*;
|
||||
@ -1848,6 +1916,7 @@ mod generate_tests {
|
||||
}
|
||||
#[allow(unused_mut)]
|
||||
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
|
||||
#[cfg(hello)]
|
||||
m.set_var("SPECIAL_CPU_NUMBER", SPECIAL_CPU_NUMBER);
|
||||
if flatten {} else {}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user