codegen: prevent name duplication in Rust or Rhai
This commit is contained in:
parent
8efde3c7ce
commit
c87da31328
@ -18,6 +18,7 @@ pub(crate) struct ExportedFnParams {
|
||||
pub name: Option<String>,
|
||||
pub return_raw: bool,
|
||||
pub skip: bool,
|
||||
pub span: Option<proc_macro2::Span>,
|
||||
}
|
||||
|
||||
impl ExportedFnParams {
|
||||
@ -47,6 +48,7 @@ impl Parse for ExportedFnParams {
|
||||
let arg_list = args.call(
|
||||
syn::punctuated::Punctuated::<syn::Expr, syn::Token![,]>::parse_separated_nonempty,
|
||||
)?;
|
||||
let span = arg_list.span();
|
||||
|
||||
let mut attrs: HashMap<proc_macro2::Ident, Option<syn::LitStr>> = HashMap::new();
|
||||
for arg in arg_list {
|
||||
@ -125,6 +127,7 @@ impl Parse for ExportedFnParams {
|
||||
name,
|
||||
return_raw,
|
||||
skip,
|
||||
span: Some(span),
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ use std::vec as new_vec;
|
||||
#[cfg(no_std)]
|
||||
use core::mem;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
fn inner_fn_attributes(f: &mut syn::ItemFn) -> syn::Result<ExportedFnParams> {
|
||||
if let Some(rhai_fn_idx) = f.attrs.iter().position(|a| {
|
||||
a.path
|
||||
@ -28,6 +30,48 @@ fn inner_fn_attributes(f: &mut syn::ItemFn) -> syn::Result<ExportedFnParams> {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_rename_collisions(fns: &Vec<ExportedFn>) -> Result<(), syn::Error> {
|
||||
let mut renames = HashMap::<String, proc_macro2::Span>::new();
|
||||
let mut names = HashMap::<String, proc_macro2::Span>::new();
|
||||
for itemfn in fns.iter() {
|
||||
if let Some(ref name) = itemfn.params.name {
|
||||
let current_span = itemfn.params.span.as_ref().unwrap();
|
||||
let key = itemfn.arg_list().fold(name.clone(), |mut argstr, fnarg| {
|
||||
let type_string: String = match fnarg {
|
||||
syn::FnArg::Receiver(_) => unimplemented!("receiver rhai_fns not implemented"),
|
||||
syn::FnArg::Typed(syn::PatType { ref ty, .. }) =>
|
||||
ty.as_ref().to_token_stream().to_string(),
|
||||
};
|
||||
argstr.push('.');
|
||||
argstr.extend(type_string.chars());
|
||||
argstr
|
||||
});
|
||||
if let Some(other_span) = renames.insert(key,
|
||||
current_span.clone()) {
|
||||
let mut err = syn::Error::new(current_span.clone(),
|
||||
format!("duplicate Rhai signature for '{}'", &name));
|
||||
err.combine(syn::Error::new(other_span,
|
||||
format!("duplicated function renamed '{}'", &name)));
|
||||
return Err(err);
|
||||
}
|
||||
} else {
|
||||
let ident = itemfn.name();
|
||||
names.insert(ident.to_string(), ident.span());
|
||||
}
|
||||
}
|
||||
for (new_name, attr_span) in renames.drain() {
|
||||
let new_name = new_name.split('.').next().unwrap();
|
||||
if let Some(fn_span) = names.get(new_name) {
|
||||
let mut err = syn::Error::new(attr_span,
|
||||
format!("duplicate Rhai signature for '{}'", &new_name));
|
||||
err.combine(syn::Error::new(fn_span.clone(),
|
||||
format!("duplicated function '{}'", &new_name)));
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Module {
|
||||
mod_all: Option<syn::ItemMod>,
|
||||
@ -92,7 +136,15 @@ impl Parse for Module {
|
||||
|
||||
impl Module {
|
||||
pub fn generate(self) -> proc_macro2::TokenStream {
|
||||
// Check for collisions if the "name" attribute was used on inner functions.
|
||||
if let Err(e) = check_rename_collisions(&self.fns) {
|
||||
return e.to_compile_error();
|
||||
}
|
||||
|
||||
// Perform the generation of new module items.
|
||||
let mod_gen = crate::rhai_module::generate_body(&self.fns, &self.consts);
|
||||
|
||||
// Rebuild the structure of the module, with the new content added.
|
||||
let Module { mod_all, .. } = self;
|
||||
let mut mod_all = mod_all.unwrap();
|
||||
let mod_name = mod_all.ident.clone();
|
||||
|
@ -187,12 +187,12 @@ mod duplicate_fn_rename {
|
||||
pub mod my_adds {
|
||||
use rhai::{FLOAT, INT};
|
||||
|
||||
#[rhai_fn(name = "add_f")]
|
||||
#[rhai_fn(name = "add")]
|
||||
pub fn add_float(f1: FLOAT, f2: FLOAT) -> FLOAT {
|
||||
f1 + f2
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "add_i")]
|
||||
#[rhai_fn(name = "add")]
|
||||
pub fn add_int(i1: INT, i2: INT) -> INT {
|
||||
i1 + i2
|
||||
}
|
||||
@ -211,9 +211,9 @@ fn duplicate_fn_rename_test() -> Result<(), Box<EvalAltResult>> {
|
||||
let output_array = engine.eval::<Array>(
|
||||
r#"import "Math::Advanced" as math;
|
||||
let fx = get_mystic_number();
|
||||
let fy = math::add_f(fx, 1.0);
|
||||
let fy = math::add(fx, 1.0);
|
||||
let ix = 42;
|
||||
let iy = math::add_i(ix, 1);
|
||||
let iy = math::add(ix, 1);
|
||||
[fy, iy]
|
||||
"#,
|
||||
)?;
|
||||
|
33
codegen/ui_tests/rhai_fn_rename_collision.rs
Normal file
33
codegen/ui_tests/rhai_fn_rename_collision.rs
Normal file
@ -0,0 +1,33 @@
|
||||
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")]
|
||||
pub fn test_fn(input: Point) -> bool {
|
||||
input.x > input.y
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "foo")]
|
||||
pub fn test_fn_2(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");
|
||||
}
|
||||
}
|
17
codegen/ui_tests/rhai_fn_rename_collision.stderr
Normal file
17
codegen/ui_tests/rhai_fn_rename_collision.stderr
Normal file
@ -0,0 +1,17 @@
|
||||
error: duplicate Rhai signature for 'foo'
|
||||
--> $DIR/rhai_fn_rename_collision.rs:17:15
|
||||
|
|
||||
17 | #[rhai_fn(name = "foo")]
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: duplicated function renamed 'foo'
|
||||
--> $DIR/rhai_fn_rename_collision.rs:12:15
|
||||
|
|
||||
12 | #[rhai_fn(name = "foo")]
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared type 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`
|
32
codegen/ui_tests/rhai_fn_rename_collision_oneattr.rs
Normal file
32
codegen/ui_tests/rhai_fn_rename_collision_oneattr.rs
Normal file
@ -0,0 +1,32 @@
|
||||
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")]
|
||||
pub fn test_fn(input: Point) -> bool {
|
||||
input.x > input.y
|
||||
}
|
||||
|
||||
pub fn foo(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");
|
||||
}
|
||||
}
|
17
codegen/ui_tests/rhai_fn_rename_collision_oneattr.stderr
Normal file
17
codegen/ui_tests/rhai_fn_rename_collision_oneattr.stderr
Normal file
@ -0,0 +1,17 @@
|
||||
error: duplicate Rhai signature for 'foo'
|
||||
--> $DIR/rhai_fn_rename_collision_oneattr.rs:12:15
|
||||
|
|
||||
12 | #[rhai_fn(name = "foo")]
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: duplicated function 'foo'
|
||||
--> $DIR/rhai_fn_rename_collision_oneattr.rs:17:12
|
||||
|
|
||||
17 | pub fn foo(input: Point) -> bool {
|
||||
| ^^^
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared type 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`
|
Loading…
Reference in New Issue
Block a user