diff --git a/CHANGELOG.md b/CHANGELOG.md index bbdbf134..4f06be45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,15 @@ Rhai Release Notes ================== +Version 1.0.1 +============= + +Bug fixes +--------- + +* Fixed bug in using indexing/dotting inside index bracket. + + Version 1.0.0 ============= diff --git a/Cargo.toml b/Cargo.toml index 834c4e29..e93cc307 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,11 +19,11 @@ categories = ["no-std", "embedded", "wasm", "parser-implementations"] smallvec = { version = "1.6", default-features = false, features = ["union"] } ahash = { version = "0.7", default-features = false } num-traits = { version = "0.2", default-features = false } -smartstring = { version = "0.2.6", default-features = false } -rhai_codegen = { version = ">=0.4.0", path = "codegen", default-features = false } +smartstring = { version = "0.2.7", default-features = false } +rhai_codegen = { version = "1.0", path = "codegen", default-features = false } [features] -default = ["smartstring/std", "ahash/std", "num-traits/std"] # remove 'smartstring/std' when smartstring is updated to support no-std +default = ["ahash/std", "num-traits/std"] unchecked = [] # unchecked arithmetic sync = [] # restrict to only types that implement Send + Sync no_position = [] # do not track position in the parser @@ -32,7 +32,7 @@ no_float = [] # no floating-point f32_float = [] # set FLOAT=f32 only_i32 = [] # set INT=i32 (useful for 32-bit systems) only_i64 = [] # set INT=i64 (default) and disable support for all other integer types -decimal = ["rust_decimal/std"] # add the Decimal number type +decimal = ["rust_decimal"] # add the Decimal number type no_index = [] # no arrays and indexing no_object = [] # no custom objects no_function = ["no_closure"] # no script-defined functions (meaning no closures) @@ -40,7 +40,7 @@ no_closure = [] # no automatic sharing and capture of anonymous no_module = [] # no modules internals = [] # expose internal data structures unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for identifiers. -metadata = ["serde_json", "rhai_codegen/metadata"] # enable exporting functions metadata +metadata = ["serde", "serde_json", "rhai_codegen/metadata"] # enable exporting functions metadata no_std = ["no-std-compat", "num-traits/libm", "core-error", "libm", "ahash/compile-time-rng"] diff --git a/codegen/src/attrs.rs b/codegen/src/attrs.rs index 48578518..7ffdae87 100644 --- a/codegen/src/attrs.rs +++ b/codegen/src/attrs.rs @@ -132,7 +132,7 @@ pub fn inner_item_attributes( } } -pub fn deny_cfg_attr(attrs: &Vec) -> syn::Result<()> { +pub fn deny_cfg_attr(attrs: &[syn::Attribute]) -> syn::Result<()> { if let Some(cfg_attr) = attrs .iter() .find(|a| a.path.get_ident().map(|i| *i == "cfg").unwrap_or(false)) diff --git a/codegen/src/function.rs b/codegen/src/function.rs index 8b4c106a..8694f266 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -152,13 +152,13 @@ impl ExportedParams for ExportedFnParams { ("get", None) | ("set", None) | ("name", None) => { return Err(syn::Error::new(key.span(), "requires value")) } - ("name", Some(s)) if &s.value() == FN_IDX_GET => { + ("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 => { + ("name", Some(s)) if s.value() == FN_IDX_SET => { return Err(syn::Error::new( item_span, "use attribute 'index_set' instead", @@ -268,7 +268,6 @@ impl ExportedParams for ExportedFnParams { special, namespace, span: Some(span), - ..Default::default() }) } } @@ -317,7 +316,7 @@ impl Parse for ExportedFn { let skip_slots = if pass_context { 1 } else { 0 }; // Determine whether function generates a special calling convention for a mutable receiver. - let mut_receiver = match fn_all.sig.inputs.iter().skip(skip_slots).next() { + let mut_receiver = match fn_all.sig.inputs.iter().nth(skip_slots) { Some(syn::FnArg::Receiver(syn::Receiver { reference: Some(_), .. })) => true, @@ -474,7 +473,7 @@ impl ExportedFn { literals } - pub fn exported_name<'n>(&'n self) -> Cow<'n, str> { + pub fn exported_name(&self) -> Cow { self.params .name .last() @@ -629,7 +628,7 @@ impl ExportedFn { let return_span = self .return_type() .map(|r| r.span()) - .unwrap_or_else(|| proc_macro2::Span::call_site()); + .unwrap_or_else(proc_macro2::Span::call_site); if self.params.return_raw.is_some() { quote_spanned! { return_span => pub #dynamic_signature { @@ -691,7 +690,7 @@ impl ExportedFn { }) .unwrap(), ); - if !self.params().pure.is_some() { + if self.params().pure.is_none() { let arg_lit_str = syn::LitStr::new(&pat.to_token_stream().to_string(), pat.span()); unpack_statements.push( @@ -812,8 +811,8 @@ impl ExportedFn { let return_span = self .return_type() .map(|r| r.span()) - .unwrap_or_else(|| proc_macro2::Span::call_site()); - let return_expr = if !self.params.return_raw.is_some() { + .unwrap_or_else(proc_macro2::Span::call_site); + let return_expr = if self.params.return_raw.is_none() { quote_spanned! { return_span => Ok(Dynamic::from(#sig_name(#(#unpack_exprs),*))) } diff --git a/codegen/src/module.rs b/codegen/src/module.rs index f78dad1f..7e7421a5 100644 --- a/codegen/src/module.rs +++ b/codegen/src/module.rs @@ -91,7 +91,6 @@ impl ExportedParams for ExportedModParams { name, skip, scope: scope.unwrap_or_default(), - ..Default::default() }) } } diff --git a/codegen/src/rhai_module.rs b/codegen/src/rhai_module.rs index 17a29fd6..f3137f78 100644 --- a/codegen/src/rhai_module.rs +++ b/codegen/src/rhai_module.rs @@ -214,7 +214,7 @@ pub fn generate_body( } } -pub fn check_rename_collisions(fns: &Vec) -> Result<(), syn::Error> { +pub fn check_rename_collisions(fns: &[ExportedFn]) -> Result<(), syn::Error> { fn make_key(name: impl ToString, item_fn: &ExportedFn) -> String { item_fn .arg_list() diff --git a/no_std/no_std_repl/Cargo.toml b/no_std/no_std_repl/Cargo.toml new file mode 100644 index 00000000..36e9c2f0 --- /dev/null +++ b/no_std/no_std_repl/Cargo.toml @@ -0,0 +1,38 @@ +cargo-features = ["named-profiles"] + +[workspace] + +[package] +name = "no_std_repl" +version = "0.1.0" +edition = "2018" +authors = ["Stephen Chung"] +description = "no-std REPL application" +homepage = "https://github.com/rhaiscript/rhai/tree/no_std/no_std_repl" +repository = "https://github.com/rhaiscript/rhai" + +[dependencies] +rhai = { path = "../../", default_features = false, features = [ "no_std", "decimal", "metadata" ] } +wee_alloc = { version = "0.4.5", default_features = false } + +[profile.dev] +panic = "abort" + +[profile.release] +opt-level = "z" # optimize for size +debug = false +rpath = false +debug-assertions = false +codegen-units = 1 +panic = "abort" + +[profile.unix] +inherits = "release" +lto = true + +[profile.windows] +inherits = "release" + +[profile.macos] +inherits = "release" +lto = "fat" diff --git a/no_std/no_std_repl/README.md b/no_std/no_std_repl/README.md new file mode 100644 index 00000000..c7ea3ce5 --- /dev/null +++ b/no_std/no_std_repl/README.md @@ -0,0 +1,26 @@ +`no-std` REPL Sample +==================== + +This sample application is the same version as the `rhai-repl` tool, compiled for `no-std`. + +[`wee_alloc`](https://crates.io/crates/wee_alloc) is used as the allocator. + + +To Build +-------- + +The nightly compiler is required: + +```bash +cargo +nightly build --release +``` + +A specific profile can also be used: + +```bash +cargo +nightly build --profile unix -Z unstable-options +``` + +Three profiles are defined: `unix`, `windows` and `macos`. + +The release build is optimized for size. It can be changed to optimize on speed instead. diff --git a/no_std/no_std_repl/src/main.rs b/no_std/no_std_repl/src/main.rs new file mode 100644 index 00000000..9a4b5e7f --- /dev/null +++ b/no_std/no_std_repl/src/main.rs @@ -0,0 +1,214 @@ +use rhai::{Dynamic, Engine, EvalAltResult, Scope, AST}; + +use std::{ + env, + io::{stdin, stdout, Write}, +}; + +/// Pretty-print error. +fn print_error(input: &str, mut err: EvalAltResult) { + let lines: Vec<_> = input.trim().split('\n').collect(); + let pos = err.take_position(); + + let line_no = if lines.len() > 1 { + if pos.is_none() { + "".to_string() + } else { + format!("{}: ", pos.line().unwrap()) + } + } else { + "".to_string() + }; + + // Print error position + if pos.is_none() { + // No position + println!("{}", err); + } else { + // Specific position - print line text + println!("{}{}", line_no, lines[pos.line().unwrap() - 1]); + + // Display position marker + println!( + "{0:>1$} {2}", + "^", + line_no.len() + pos.position().unwrap(), + err + ); + } +} + +/// Print help text. +fn print_help() { + println!("help => print this help"); + println!("quit, exit => quit"); + println!("scope => print all variables in the scope"); + println!("functions => print all functions defined"); + println!("json => output all functions in JSON format"); + println!("ast => print the last AST (optimized)"); + println!("astu => print the last raw, un-optimized AST"); + println!(r"end a line with '\' to continue to the next line."); + println!(); +} + +fn main() { + let title = format!("Rhai REPL tool (version {})", env!("CARGO_PKG_VERSION")); + println!("{}", title); + println!("{0:=<1$}", "", title.len()); + print_help(); + + // Initialize scripting engine + let mut engine = Engine::new(); + + // Setup Engine + #[cfg(not(feature = "no_optimize"))] + engine.set_optimization_level(rhai::OptimizationLevel::None); + + // Make Engine immutable + let engine = engine; + + // Create scope + let mut scope = Scope::new(); + + // REPL loop + let mut input = String::new(); + let mut main_ast: AST = Default::default(); + let mut ast_u: AST = Default::default(); + let mut ast: AST = Default::default(); + + 'main_loop: loop { + print!("rhai-repl> "); + stdout().flush().expect("couldn't flush stdout"); + + input.clear(); + + loop { + match stdin().read_line(&mut input) { + Ok(0) => break 'main_loop, + Ok(_) => (), + Err(err) => panic!("input error: {}", err), + } + + let line = input.as_str().trim_end(); + + // Allow line continuation + if line.ends_with('\\') { + let len = line.len(); + input.truncate(len - 1); + input.push('\n'); + } else { + break; + } + + print!("> "); + stdout().flush().expect("couldn't flush stdout"); + } + + let script = input.trim(); + + if script.is_empty() { + continue; + } + + // Implement standard commands + match script { + "help" => { + print_help(); + continue; + } + "exit" | "quit" => break, // quit + "scope" => { + scope + .iter_raw() + .enumerate() + .for_each(|(i, (name, constant, value))| { + #[cfg(not(feature = "no_closure"))] + let value_is_shared = if value.is_shared() { " (shared" } else { "" }; + #[cfg(feature = "no_closure")] + let value_is_shared = ""; + + println!( + "[{}] {}{}{} = {:?}", + i + 1, + if constant { "const " } else { "" }, + name, + value_is_shared, + *value.read_lock::().unwrap(), + ) + }); + println!(); + continue; + } + "astu" => { + // print the last un-optimized AST + println!("{:#?}\n", ast_u); + continue; + } + "ast" => { + // print the last AST + println!("{:#?}\n", ast); + continue; + } + "functions" => { + // print a list of all registered functions + engine + .gen_fn_signatures(false) + .into_iter() + .for_each(|f| println!("{}", f)); + + #[cfg(not(feature = "no_function"))] + main_ast.iter_functions().for_each(|f| println!("{}", f)); + + println!(); + continue; + } + "json" => { + println!( + "{}", + engine + .gen_fn_metadata_with_ast_to_json(&main_ast, true) + .unwrap() + ); + continue; + } + _ => (), + } + + match engine + .compile_with_scope(&scope, &script) + .map_err(Into::into) + .and_then(|r| { + ast_u = r.clone(); + + #[cfg(not(feature = "no_optimize"))] + { + ast = engine.optimize_ast(&scope, r, rhai::OptimizationLevel::Simple); + } + + #[cfg(feature = "no_optimize")] + { + ast = r; + } + + // Merge the AST into the main + main_ast += ast.clone(); + + // Evaluate + engine.eval_ast_with_scope::(&mut scope, &main_ast) + }) { + Ok(result) if !result.is::<()>() => { + println!("=> {:?}", result); + println!(); + } + Ok(_) => (), + Err(err) => { + println!(); + print_error(&input, *err); + println!(); + } + } + + // Throw away all the statements, leaving only the functions + main_ast.clear_statements(); + } +} diff --git a/no_std/no_std_test/README.md b/no_std/no_std_test/README.md index 1c89b11f..042e15c0 100644 --- a/no_std/no_std_test/README.md +++ b/no_std/no_std_test/README.md @@ -6,15 +6,21 @@ This sample application is a bare-bones `no-std` build for testing. [`wee_alloc`](https://crates.io/crates/wee_alloc) is used as the allocator. -To Compile ----------- +To Build +-------- The nightly compiler is required: +```bash +cargo +nightly build --release +``` + +A specific profile can also be used: + ```bash cargo +nightly build --profile unix -Z unstable-options ``` -Available profiles are: `unix`, `windows` and `macos`. +Three profiles are defined: `unix`, `windows` and `macos`. The release build is optimized for size. It can be changed to optimize on speed instead. diff --git a/src/ast.rs b/src/ast.rs index eb992f6a..c9ce1d3f 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -78,7 +78,6 @@ pub struct ScriptFnDef { } impl fmt::Display for ScriptFnDef { - #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, @@ -129,7 +128,6 @@ pub struct ScriptFnMetadata<'a> { #[cfg(not(feature = "no_function"))] impl fmt::Display for ScriptFnMetadata<'_> { - #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, @@ -149,16 +147,16 @@ impl fmt::Display for ScriptFnMetadata<'_> { } #[cfg(not(feature = "no_function"))] -impl<'a> Into> for &'a ScriptFnDef { - #[inline(always)] - fn into(self) -> ScriptFnMetadata<'a> { - ScriptFnMetadata { +impl<'a> From<&'a ScriptFnDef> for ScriptFnMetadata<'a> { + #[inline] + fn from(value: &'a ScriptFnDef) -> Self { + Self { #[cfg(not(feature = "no_function"))] #[cfg(feature = "metadata")] - comments: self.comments.iter().map(|s| s.as_str()).collect(), - access: self.access, - name: &self.name, - params: self.params.iter().map(|s| s.as_str()).collect(), + comments: value.comments.iter().map(|s| s.as_str()).collect(), + access: value.access, + name: &value.name, + params: value.params.iter().map(|s| s.as_str()).collect(), } } } @@ -215,7 +213,7 @@ impl Default for AST { impl AST { /// Create a new [`AST`]. - #[inline(always)] + #[inline] #[must_use] pub fn new( statements: impl IntoIterator, @@ -223,7 +221,7 @@ impl AST { ) -> Self { Self { source: None, - body: StmtBlock::new(statements.into_iter().collect(), Position::NONE), + body: StmtBlock::new(statements, Position::NONE), functions: functions.into(), #[cfg(not(feature = "no_module"))] resolver: None, @@ -239,7 +237,7 @@ impl AST { ) -> Self { Self { source: Some(source.into()), - body: StmtBlock::new(statements.into_iter().collect(), Position::NONE), + body: StmtBlock::new(statements, Position::NONE), functions: functions.into(), #[cfg(not(feature = "no_module"))] resolver: None, @@ -258,7 +256,7 @@ impl AST { self.source.as_ref() } /// Set the source. - #[inline(always)] + #[inline] pub fn set_source(&mut self, source: impl Into) -> &mut Self { let source = source.into(); Shared::get_mut(&mut self.functions) @@ -386,7 +384,7 @@ impl AST { /// /// This operation is cheap because functions are shared. #[cfg(not(feature = "no_function"))] - #[inline(always)] + #[inline] #[must_use] pub fn clone_functions_only_filtered( &self, @@ -657,7 +655,7 @@ impl AST { /// # Ok(()) /// # } /// ``` - #[inline(always)] + #[inline] pub fn combine_filtered( &mut self, other: Self, @@ -696,7 +694,7 @@ impl AST { /// # } /// ``` #[cfg(not(feature = "no_function"))] - #[inline(always)] + #[inline] pub fn retain_functions( &mut self, filter: impl Fn(FnNamespace, FnAccess, &str, usize) -> bool, @@ -712,7 +710,6 @@ impl AST { #[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_module"))] #[inline(always)] - #[must_use] pub(crate) fn iter_fn_def(&self) -> impl Iterator { self.functions .iter_script_fn() @@ -723,7 +720,6 @@ impl AST { /// Not available under `no_function`. #[cfg(not(feature = "no_function"))] #[inline(always)] - #[must_use] pub fn iter_functions<'a>(&'a self) -> impl Iterator + 'a { self.functions .iter_script_fn() @@ -748,7 +744,7 @@ impl AST { /// Return `false` from the callback to terminate the walk. #[cfg(not(feature = "internals"))] #[cfg(not(feature = "no_module"))] - #[inline(always)] + #[inline] pub(crate) fn walk(&self, on_node: &mut impl FnMut(&[ASTNode]) -> bool) -> bool { let path = &mut Default::default(); @@ -770,7 +766,7 @@ impl AST { /// Return `false` from the callback to terminate the walk. /// Exported under the `internals` feature only. #[cfg(feature = "internals")] - #[inline(always)] + #[inline] pub fn walk(&self, on_node: &mut impl FnMut(&[ASTNode]) -> bool) -> bool { let path = &mut Default::default(); @@ -835,7 +831,6 @@ pub struct Ident { } impl fmt::Debug for Ident { - #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self.name)?; self.pos.debug_print(f) @@ -892,7 +887,8 @@ pub struct StmtBlock(StaticVec, Position); impl StmtBlock { /// Create a new [`StmtBlock`]. #[must_use] - pub fn new(mut statements: StaticVec, pos: Position) -> Self { + pub fn new(statements: impl IntoIterator, pos: Position) -> Self { + let mut statements: StaticVec<_> = statements.into_iter().collect(); statements.shrink_to_fit(); Self(statements, pos) } @@ -939,7 +935,6 @@ impl DerefMut for StmtBlock { } impl fmt::Debug for StmtBlock { - #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("Block")?; fmt::Debug::fmt(&self.0, f)?; @@ -1027,7 +1022,7 @@ impl Default for Stmt { } impl From for StmtBlock { - #[inline(always)] + #[inline] fn from(stmt: Stmt) -> Self { match stmt { Stmt::Block(mut block, pos) => Self(block.iter_mut().map(mem::take).collect(), pos), @@ -1045,10 +1040,7 @@ impl Stmt { #[inline(always)] #[must_use] pub const fn is_noop(&self) -> bool { - match self { - Self::Noop(_) => true, - _ => false, - } + matches!(self, Self::Noop(_)) } /// Get the [position][Position] of this statement. #[must_use] @@ -1226,7 +1218,7 @@ impl Stmt { /// /// Only variable declarations (i.e. `let` and `const`) and `import`/`export` statements /// are internally pure. - #[inline(always)] + #[inline] #[must_use] pub fn is_internally_pure(&self) -> bool { match self { @@ -1245,7 +1237,7 @@ impl Stmt { /// Currently this is only true for `return`, `throw`, `break` and `continue`. /// /// All statements following this statement will essentially be dead code. - #[inline(always)] + #[inline] #[must_use] pub const fn is_control_flow_break(&self) -> bool { match self { @@ -1634,7 +1626,6 @@ impl fmt::Debug for FloatWrapper { #[cfg(not(feature = "no_float"))] impl> fmt::Display for FloatWrapper { - #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let abs = self.0.abs(); if abs > Self::MAX_NATURAL_FLOAT_FOR_DISPLAY.into() @@ -1690,7 +1681,7 @@ impl FloatWrapper { /// Create a new [`FloatWrapper`]. #[inline(always)] #[must_use] - pub(crate) const fn const_new(value: FLOAT) -> Self { + pub const fn new_const(value: FLOAT) -> Self { Self(value) } } @@ -1759,10 +1750,10 @@ pub enum Expr { Stmt(Box), /// func `(` expr `,` ... `)` FnCall(Box, Position), - /// lhs `.` rhs - Dot(Box, Position), - /// expr `[` expr `]` - Index(Box, Position), + /// lhs `.` rhs - bool variable is a dummy + Dot(Box, bool, Position), + /// expr `[` expr `]` - boolean indicates whether the dotting/indexing chain stops + Index(Box, bool, Position), /// lhs `&&` rhs And(Box, Position), /// lhs `||` rhs @@ -1839,10 +1830,18 @@ impl fmt::Debug for Expr { } ff.finish() } - Self::Dot(x, pos) | Self::Index(x, pos) | Self::And(x, pos) | Self::Or(x, pos) => { + Self::Index(x, term, pos) => { + display_pos = *pos; + + f.debug_struct("Index") + .field("lhs", &x.lhs) + .field("rhs", &x.rhs) + .field("terminate", term) + .finish() + } + Self::Dot(x, _, pos) | Self::And(x, pos) | Self::Or(x, pos) => { let op_name = match self { - Self::Dot(_, _) => "Dot", - Self::Index(_, _) => "Index", + Self::Dot(_, _, _) => "Dot", Self::And(_, _) => "And", Self::Or(_, _) => "Or", _ => unreachable!(), @@ -1915,9 +1914,18 @@ impl Expr { Union::Char(c, _, _) => Self::CharConstant(c, pos), Union::Int(i, _, _) => Self::IntegerConstant(i, pos), + #[cfg(feature = "decimal")] + Union::Decimal(value, _, _) => Self::DynamicConstant(Box::new((*value).into()), pos), + #[cfg(not(feature = "no_float"))] Union::Float(f, _, _) => Self::FloatConstant(f, pos), + #[cfg(not(feature = "no_index"))] + Union::Array(a, _, _) => Self::DynamicConstant(Box::new((*a).into()), pos), + + #[cfg(not(feature = "no_object"))] + Union::Map(m, _, _) => Self::DynamicConstant(Box::new((*m).into()), pos), + _ => Self::DynamicConstant(value.into(), pos), } } @@ -1964,7 +1972,7 @@ impl Expr { Self::Property(x) => (x.2).1, Self::Stmt(x) => x.1, - Self::And(x, _) | Self::Or(x, _) | Self::Dot(x, _) | Self::Index(x, _) => { + Self::And(x, _) | Self::Or(x, _) | Self::Dot(x, _, _) | Self::Index(x, _, _) => { x.lhs.position() } } @@ -1986,8 +1994,8 @@ impl Expr { | Self::Map(_, pos) | Self::And(_, pos) | Self::Or(_, pos) - | Self::Dot(_, pos) - | Self::Index(_, pos) + | Self::Dot(_, _, pos) + | Self::Index(_, _, pos) | Self::Variable(_, pos, _) | Self::Stack(_, pos) | Self::FnCall(_, pos) @@ -2024,10 +2032,7 @@ impl Expr { #[inline(always)] #[must_use] pub const fn is_unit(&self) -> bool { - match self { - Self::Unit(_) => true, - _ => false, - } + matches!(self, Self::Unit(_)) } /// Is the expression a constant? #[inline] @@ -2078,8 +2083,8 @@ impl Expr { | Self::InterpolatedString(_, _) | Self::FnCall(_, _) | Self::Stmt(_) - | Self::Dot(_, _) - | Self::Index(_, _) + | Self::Dot(_, _, _) + | Self::Index(_, _, _) | Self::Array(_, _) | Self::Map(_, _) => match token { #[cfg(not(feature = "no_index"))] @@ -2144,7 +2149,7 @@ impl Expr { } } } - Self::Index(x, _) | Self::Dot(x, _) | Expr::And(x, _) | Expr::Or(x, _) => { + Self::Index(x, _, _) | Self::Dot(x, _, _) | Expr::And(x, _) | Expr::Or(x, _) => { if !x.lhs.walk(path, on_node) { return false; } diff --git a/src/bin/rhai-repl.rs b/src/bin/rhai-repl.rs index a4af261f..62dc3869 100644 --- a/src/bin/rhai-repl.rs +++ b/src/bin/rhai-repl.rs @@ -48,6 +48,8 @@ fn print_help() { println!("scope => print all variables in the scope"); #[cfg(feature = "metadata")] println!("functions => print all functions defined"); + #[cfg(feature = "metadata")] + println!("json => output all functions in JSON format"); println!("ast => print the last AST (optimized)"); println!("astu => print the last raw, un-optimized AST"); println!(r"end a line with '\' to continue to the next line."); @@ -76,7 +78,12 @@ fn main() { eprintln!("Error script file path: {}\n{}", filename, err); exit(1); } - Ok(f) => f, + Ok(f) => { + match f.strip_prefix(std::env::current_dir().unwrap().canonicalize().unwrap()) { + Ok(f) => f.into(), + _ => f, + } + } }; contents.clear(); @@ -207,7 +214,7 @@ fn main() { .enumerate() .for_each(|(i, (name, constant, value))| { #[cfg(not(feature = "no_closure"))] - let value_is_shared = if value.is_shared() { " (shared" } else { "" }; + let value_is_shared = if value.is_shared() { " (shared)" } else { "" }; #[cfg(feature = "no_closure")] let value_is_shared = ""; @@ -247,15 +254,16 @@ fn main() { println!(); continue; } - // "json" => { - // println!( - // "{}", - // engine - // .gen_fn_metadata_with_ast_to_json(&main_ast, true) - // .unwrap() - // ); - // continue; - // } + #[cfg(feature = "metadata")] + "json" => { + println!( + "{}", + engine + .gen_fn_metadata_with_ast_to_json(&main_ast, true) + .unwrap() + ); + continue; + } _ => (), } diff --git a/src/bin/rhai-run.rs b/src/bin/rhai-run.rs index e2867515..4d1749df 100644 --- a/src/bin/rhai-run.rs +++ b/src/bin/rhai-run.rs @@ -43,7 +43,11 @@ fn main() { eprintln!("Error script file path: {}\n{}", filename, err); exit(1); } - Ok(f) => f, + Ok(f) => match f.strip_prefix(std::env::current_dir().unwrap().canonicalize().unwrap()) + { + Ok(f) => f.into(), + _ => f, + }, }; let mut engine = Engine::new(); diff --git a/src/custom_syntax.rs b/src/custom_syntax.rs index 7e1aa614..e77bee10 100644 --- a/src/custom_syntax.rs +++ b/src/custom_syntax.rs @@ -134,7 +134,6 @@ impl EvalContext<'_, '_, '_, '_, '_, '_, '_, '_> { /// /// This function is very low level. It evaluates an expression from an [`AST`][crate::AST]. #[inline(always)] - #[must_use] pub fn eval_expression_tree(&mut self, expr: &Expression) -> RhaiResult { self.engine.eval_expr( self.scope, @@ -179,15 +178,12 @@ impl Engine { /// Replacing one variable with another (i.e. adding a new variable and removing one variable at /// the same time so that the total _size_ of the [`Scope`][crate::Scope] is unchanged) also /// does NOT count, so `false` should be passed. - #[must_use] pub fn register_custom_syntax + Into>( &mut self, keywords: &[S], scope_may_be_changed: bool, func: impl Fn(&mut EvalContext, &[Expression]) -> RhaiResult + SendSync + 'static, ) -> Result<&mut Self, ParseError> { - let keywords = keywords.as_ref(); - let mut segments: StaticVec = Default::default(); for s in keywords { @@ -240,8 +236,7 @@ impl Engine { s ), ) - .into_err(Position::NONE) - .into()); + .into_err(Position::NONE)); } // Identifier in first position s if segments.is_empty() && is_valid_identifier(s.chars()) => { @@ -264,8 +259,7 @@ impl Engine { s ), ) - .into_err(Position::NONE) - .into()); + .into_err(Position::NONE)); } }; diff --git a/src/dynamic.rs b/src/dynamic.rs index d4c0732e..512e062d 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -212,6 +212,7 @@ pub enum Union { TimeStamp(Box, Tag, AccessMode), /// Any type as a trait object. + #[allow(clippy::redundant_allocation)] Variant(Box>, Tag, AccessMode), /// A _shared_ value of any type. @@ -378,10 +379,7 @@ impl Dynamic { #[inline(always)] #[must_use] pub const fn is_variant(&self) -> bool { - match self.0 { - Union::Variant(_, _, _) => true, - _ => false, - } + matches!(self.0, Union::Variant(_, _, _)) } /// Is the value held by this [`Dynamic`] shared? /// @@ -390,13 +388,11 @@ impl Dynamic { #[inline(always)] #[must_use] pub const fn is_shared(&self) -> bool { - #[cfg(not(feature = "no_closure"))] match self.0 { - Union::Shared(_, _, _) => return true, - _ => (), + #[cfg(not(feature = "no_closure"))] + Union::Shared(_, _, _) => true, + _ => false, } - - false } /// Is the value held by this [`Dynamic`] a particular type? /// @@ -405,13 +401,11 @@ impl Dynamic { #[inline(always)] #[must_use] pub fn is(&self) -> bool { - let mut target_type_id = TypeId::of::(); - - if target_type_id == TypeId::of::() { - target_type_id = TypeId::of::(); + if TypeId::of::() == TypeId::of::() { + self.type_id() == TypeId::of::() + } else { + self.type_id() == TypeId::of::() } - - self.type_id() == target_type_id } /// Get the [`TypeId`] of the value held by this [`Dynamic`]. /// @@ -577,7 +571,7 @@ impl Hash for Dynamic { } /// Map the name of a standard type into a friendly form. -#[inline(always)] +#[inline] #[must_use] pub(crate) fn map_std_type_name(name: &str) -> &str { if name == type_name::() { @@ -845,7 +839,7 @@ impl Dynamic { /// Not available under `no_float`. #[cfg(not(feature = "no_float"))] pub const FLOAT_ZERO: Dynamic = Self(Union::Float( - FloatWrapper::const_new(0.0), + FloatWrapper::new_const(0.0), DEFAULT_TAG_VALUE, ReadWrite, )); @@ -854,7 +848,7 @@ impl Dynamic { /// Not available under `no_float`. #[cfg(not(feature = "no_float"))] pub const FLOAT_ONE: Dynamic = Self(Union::Float( - FloatWrapper::const_new(1.0), + FloatWrapper::new_const(1.0), DEFAULT_TAG_VALUE, ReadWrite, )); @@ -863,7 +857,7 @@ impl Dynamic { /// Not available under `no_float`. #[cfg(not(feature = "no_float"))] pub const FLOAT_TWO: Dynamic = Self(Union::Float( - FloatWrapper::const_new(2.0), + FloatWrapper::new_const(2.0), DEFAULT_TAG_VALUE, ReadWrite, )); @@ -872,7 +866,7 @@ impl Dynamic { /// Not available under `no_float`. #[cfg(not(feature = "no_float"))] pub const FLOAT_TEN: Dynamic = Self(Union::Float( - FloatWrapper::const_new(10.0), + FloatWrapper::new_const(10.0), DEFAULT_TAG_VALUE, ReadWrite, )); @@ -881,7 +875,7 @@ impl Dynamic { /// Not available under `no_float`. #[cfg(not(feature = "no_float"))] pub const FLOAT_NEGATIVE_ONE: Dynamic = Self(Union::Float( - FloatWrapper::const_new(-1.0), + FloatWrapper::new_const(-1.0), DEFAULT_TAG_VALUE, ReadWrite, )); @@ -1056,21 +1050,21 @@ impl Dynamic { let val = value.as_any(); if TypeId::of::() == TypeId::of::() { - return val.downcast_ref::().expect(CHECKED).clone().into(); + return (*val.downcast_ref::().expect(CHECKED)).into(); } #[cfg(not(feature = "no_float"))] if TypeId::of::() == TypeId::of::() { - return val.downcast_ref::().expect(CHECKED).clone().into(); + return (*val.downcast_ref::().expect(CHECKED)).into(); } #[cfg(feature = "decimal")] if TypeId::of::() == TypeId::of::() { - return val.downcast_ref::().expect(CHECKED).clone().into(); + return (*val.downcast_ref::().expect(CHECKED)).into(); } if TypeId::of::() == TypeId::of::() { - return val.downcast_ref::().expect(CHECKED).clone().into(); + return (*val.downcast_ref::().expect(CHECKED)).into(); } if TypeId::of::() == TypeId::of::() { - return val.downcast_ref::().expect(CHECKED).clone().into(); + return (*val.downcast_ref::().expect(CHECKED)).into(); } if TypeId::of::() == TypeId::of::() { return val @@ -1321,7 +1315,7 @@ impl Dynamic { /// /// assert_eq!(x.cast::(), 42); /// ``` - #[inline(always)] + #[inline] #[must_use] pub fn cast(self) -> T { #[cfg(not(feature = "no_closure"))] @@ -1481,7 +1475,7 @@ impl Dynamic { /// /// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1). /// Otherwise, this call panics if the data is currently borrowed for write. - #[inline(always)] + #[inline] #[must_use] pub fn read_lock(&self) -> Option> { match self.0 { @@ -1515,7 +1509,7 @@ impl Dynamic { /// /// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1). /// Otherwise, this call panics if the data is currently borrowed for write. - #[inline(always)] + #[inline] #[must_use] pub fn write_lock(&mut self) -> Option> { match self.0 { @@ -1735,7 +1729,6 @@ impl Dynamic { /// Cast the [`Dynamic`] as a unit `()` and return it. /// Returns the name of the actual type if the cast fails. #[inline(always)] - #[must_use] pub fn as_unit(&self) -> Result<(), &'static str> { match self.0 { Union::Unit(value, _, _) => Ok(value), @@ -1747,7 +1740,6 @@ impl Dynamic { /// Cast the [`Dynamic`] as the system integer type [`INT`] and return it. /// Returns the name of the actual type if the cast fails. #[inline(always)] - #[must_use] pub fn as_int(&self) -> Result { match self.0 { Union::Int(n, _, _) => Ok(n), @@ -1762,7 +1754,6 @@ impl Dynamic { /// Not available under `no_float`. #[cfg(not(feature = "no_float"))] #[inline(always)] - #[must_use] pub fn as_float(&self) -> Result { match self.0 { Union::Float(n, _, _) => Ok(*n), @@ -1777,7 +1768,6 @@ impl Dynamic { /// Exported under the `decimal` feature only. #[cfg(feature = "decimal")] #[inline(always)] - #[must_use] pub fn as_decimal(&self) -> Result { match self.0 { Union::Decimal(ref n, _, _) => Ok(**n), @@ -1789,7 +1779,6 @@ impl Dynamic { /// Cast the [`Dynamic`] as a [`bool`] and return it. /// Returns the name of the actual type if the cast fails. #[inline(always)] - #[must_use] pub fn as_bool(&self) -> Result { match self.0 { Union::Bool(b, _, _) => Ok(b), @@ -1801,7 +1790,6 @@ impl Dynamic { /// Cast the [`Dynamic`] as a [`char`] and return it. /// Returns the name of the actual type if the cast fails. #[inline(always)] - #[must_use] pub fn as_char(&self) -> Result { match self.0 { Union::Char(n, _, _) => Ok(n), @@ -1817,7 +1805,6 @@ impl Dynamic { /// /// Panics if the value is shared. #[inline(always)] - #[must_use] pub(crate) fn as_str_ref(&self) -> Result<&str, &'static str> { match self.0 { Union::Str(ref s, _, _) => Ok(s), @@ -1830,14 +1817,12 @@ impl Dynamic { /// If there are other references to the same string, a cloned copy is returned. /// Returns the name of the actual type if the cast fails. #[inline(always)] - #[must_use] pub fn as_string(self) -> Result { self.as_immutable_string().map(ImmutableString::into_owned) } /// Convert the [`Dynamic`] into an [`ImmutableString`] and return it. /// Returns the name of the actual type if the cast fails. - #[inline(always)] - #[must_use] + #[inline] pub fn as_immutable_string(self) -> Result { match self.0 { Union::Str(s, _, _) => Ok(s), @@ -2057,6 +2042,6 @@ impl From for Dynamic { impl From>> for Dynamic { #[inline(always)] fn from(value: crate::Shared>) -> Self { - Self(Union::Shared(value.into(), DEFAULT_TAG_VALUE, ReadWrite)) + Self(Union::Shared(value, DEFAULT_TAG_VALUE, ReadWrite)) } } diff --git a/src/engine.rs b/src/engine.rs index 91b20d8b..75924308 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -67,7 +67,10 @@ impl Imports { #[inline(always)] #[must_use] pub fn new() -> Self { - Default::default() + Self { + keys: StaticVec::new(), + modules: StaticVec::new(), + } } /// Get the length of this stack of imported [modules][Module]. #[inline(always)] @@ -119,7 +122,6 @@ impl Imports { /// Get an iterator to this stack of imported [modules][Module] in reverse order. #[allow(dead_code)] #[inline(always)] - #[must_use] pub fn iter(&self) -> impl Iterator { self.keys .iter() @@ -130,14 +132,12 @@ impl Imports { /// Get an iterator to this stack of imported [modules][Module] in reverse order. #[allow(dead_code)] #[inline(always)] - #[must_use] pub(crate) fn iter_raw(&self) -> impl Iterator)> { self.keys.iter().rev().zip(self.modules.iter().rev()) } /// Get an iterator to this stack of imported [modules][Module] in forward order. #[allow(dead_code)] #[inline(always)] - #[must_use] pub(crate) fn scan_raw(&self) -> impl Iterator)> { self.keys.iter().zip(self.modules.iter()) } @@ -290,10 +290,10 @@ pub const TOKEN_OP_CONCAT: Token = Token::PlusAssign; enum ChainType { /// Indexing. #[cfg(not(feature = "no_index"))] - Index, + Indexing, /// Dotting. #[cfg(not(feature = "no_object"))] - Dot, + Dotting, } /// Value of a chaining argument. @@ -318,9 +318,10 @@ impl ChainArgument { #[inline(always)] #[cfg(not(feature = "no_index"))] #[must_use] - pub fn as_index_value(self) -> Option { + pub fn into_index_value(self) -> Option { match self { Self::IndexValue(value, _) => Some(value), + #[cfg(not(feature = "no_object"))] _ => None, } } @@ -328,7 +329,7 @@ impl ChainArgument { #[inline(always)] #[cfg(not(feature = "no_object"))] #[must_use] - pub fn as_fn_call_args(self) -> Option<(StaticVec, Position)> { + pub fn into_fn_call_args(self) -> Option<(StaticVec, Position)> { match self { Self::MethodCallArgs(values, pos) => Some((values, pos)), _ => None, @@ -352,6 +353,18 @@ impl From<(Dynamic, Position)> for ChainArgument { } } +/// Get the chaining type for an [`Expr`]. +#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] +fn match_chaining_type(expr: &Expr) -> ChainType { + match expr { + #[cfg(not(feature = "no_index"))] + Expr::Index(_, _, _) => ChainType::Indexing, + #[cfg(not(feature = "no_object"))] + Expr::Dot(_, _, _) => ChainType::Dotting, + _ => unreachable!("`expr` should only be `Index` or `Dot`, but got {:?}", expr), + } +} + /// A type that encapsulates a mutation target for an expression with side effects. #[derive(Debug)] pub enum Target<'a> { @@ -376,7 +389,7 @@ pub enum Target<'a> { impl<'a> Target<'a> { /// Is the `Target` a reference pointing to other data? #[allow(dead_code)] - #[inline(always)] + #[inline] #[must_use] pub const fn is_ref(&self) -> bool { match self { @@ -391,7 +404,7 @@ impl<'a> Target<'a> { } } /// Is the `Target` a temp value? - #[inline(always)] + #[inline] #[must_use] pub const fn is_temp_value(&self) -> bool { match self { @@ -407,7 +420,7 @@ impl<'a> Target<'a> { } /// Is the `Target` a shared value? #[cfg(not(feature = "no_closure"))] - #[inline(always)] + #[inline] #[must_use] pub fn is_shared(&self) -> bool { match self { @@ -423,7 +436,7 @@ impl<'a> Target<'a> { } /// Is the `Target` a specific type? #[allow(dead_code)] - #[inline(always)] + #[inline] #[must_use] pub fn is(&self) -> bool { match self { @@ -438,7 +451,7 @@ impl<'a> Target<'a> { } } /// Get the value of the `Target` as a `Dynamic`, cloning a referenced value if necessary. - #[inline(always)] + #[inline] #[must_use] pub fn take_or_clone(self) -> Dynamic { match self { @@ -469,8 +482,7 @@ impl<'a> Target<'a> { } /// Propagate a changed value back to the original source. /// This has no effect except for string indexing. - #[inline(always)] - #[must_use] + #[inline] pub fn propagate_changed_value(&mut self) -> Result<(), Box> { match self { Self::RefMut(_) | Self::TempValue(_) => (), @@ -534,7 +546,7 @@ impl<'a> Target<'a> { } impl<'a> From<&'a mut Dynamic> for Target<'a> { - #[inline(always)] + #[inline] fn from(value: &'a mut Dynamic) -> Self { #[cfg(not(feature = "no_closure"))] if value.is_shared() { @@ -555,7 +567,7 @@ impl<'a> From<&'a mut Dynamic> for Target<'a> { impl Deref for Target<'_> { type Target = Dynamic; - #[inline(always)] + #[inline] fn deref(&self) -> &Dynamic { match self { Self::RefMut(r) => *r, @@ -578,7 +590,7 @@ impl AsRef for Target<'_> { } impl DerefMut for Target<'_> { - #[inline(always)] + #[inline] fn deref_mut(&mut self) -> &mut Dynamic { match self { Self::RefMut(r) => *r, @@ -818,7 +830,6 @@ impl<'x, 'px, 'pt> EvalContext<'_, 'x, 'px, '_, '_, '_, '_, 'pt> { /// Get an iterator over the current set of modules imported via `import` statements. #[cfg(not(feature = "no_module"))] #[inline(always)] - #[must_use] pub fn iter_imports(&self) -> impl Iterator { self.mods.iter() } @@ -833,7 +844,6 @@ impl<'x, 'px, 'pt> EvalContext<'_, 'x, 'px, '_, '_, '_, '_, 'pt> { } /// Get an iterator over the namespaces containing definition of all script-defined functions. #[inline(always)] - #[must_use] pub fn iter_namespaces(&self) -> impl Iterator { self.lib.iter().cloned() } @@ -947,7 +957,7 @@ impl Default for Engine { /// Make getter function #[cfg(not(feature = "no_object"))] -#[inline(always)] +#[inline] #[must_use] pub fn make_getter(id: &str) -> String { format!("{}{}", FN_GET, id) @@ -955,7 +965,7 @@ pub fn make_getter(id: &str) -> String { /// Make setter function #[cfg(not(feature = "no_object"))] -#[inline(always)] +#[inline] #[must_use] pub fn make_setter(id: &str) -> String { format!("{}{}", FN_SET, id) @@ -970,7 +980,7 @@ pub fn is_anonymous_fn(fn_name: &str) -> bool { } /// Print to `stdout` -#[inline(always)] +#[inline] #[allow(unused_variables)] fn print_to_stdout(s: &str) { #[cfg(not(feature = "no_std"))] @@ -979,7 +989,7 @@ fn print_to_stdout(s: &str) { } /// Debug to `stdout` -#[inline(always)] +#[inline] #[allow(unused_variables)] fn debug_to_stdout(s: &str, source: Option<&str>, pos: Position) { #[cfg(not(feature = "no_std"))] @@ -1021,7 +1031,7 @@ impl Engine { /// Create a new [`Engine`] with minimal built-in functions. /// /// Use [`register_global_module`][Engine::register_global_module] to add packages of functions. - #[inline(always)] + #[inline] #[must_use] pub fn new_raw() -> Self { let mut engine = Self { @@ -1093,7 +1103,6 @@ impl Engine { /// Search for a variable within the scope or within imports, /// depending on whether the variable name is namespace-qualified. - #[must_use] pub(crate) fn search_namespace<'s>( &self, scope: &'s mut Scope, @@ -1143,7 +1152,6 @@ impl Engine { /// # Panics /// /// Panics if `expr` is not [`Expr::Variable`]. - #[must_use] pub(crate) fn search_scope_only<'s>( &self, scope: &'s mut Scope, @@ -1217,7 +1225,6 @@ impl Engine { /// Chain-evaluate a dot/index chain. /// [`Position`] in [`EvalAltResult`] is [`NONE`][Position::NONE] and must be set afterwards. #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] - #[must_use] fn eval_dot_index_chain_helper( &self, mods: &mut Imports, @@ -1227,21 +1234,12 @@ impl Engine { target: &mut Target, root: (&str, Position), rhs: &Expr, + _terminate_chaining: bool, idx_values: &mut StaticVec, chain_type: ChainType, level: usize, new_val: Option<((Dynamic, Position), (Option, Position))>, ) -> Result<(Dynamic, bool), Box> { - fn match_chain_type(expr: &Expr) -> ChainType { - match expr { - #[cfg(not(feature = "no_index"))] - Expr::Index(_, _) => ChainType::Index, - #[cfg(not(feature = "no_object"))] - Expr::Dot(_, _) => ChainType::Dot, - _ => unreachable!("`expr` should only be `Index` or `Dot`, but got {:?}", expr), - } - } - let is_ref_mut = target.is_ref(); // Pop the last index value @@ -1251,23 +1249,25 @@ impl Engine { match chain_type { #[cfg(not(feature = "no_index"))] - ChainType::Index => { + ChainType::Indexing => { let pos = rhs.position(); let idx_val = idx_val - .as_index_value() + .into_index_value() .expect("never fails because `chain_type` is `ChainType::Index`"); match rhs { // xxx[idx].expr... | xxx[idx][expr]... - Expr::Dot(x, x_pos) | Expr::Index(x, x_pos) => { + Expr::Dot(x, term, x_pos) | Expr::Index(x, term, x_pos) + if !_terminate_chaining => + { let idx_pos = x.lhs.position(); let obj_ptr = &mut self.get_indexed_mut( mods, state, lib, target, idx_val, idx_pos, false, true, level, )?; - let rhs_chain = match_chain_type(rhs); + let rhs_chain = match_chaining_type(rhs); self.eval_dot_index_chain_helper( - mods, state, lib, this_ptr, obj_ptr, root, &x.rhs, idx_values, + mods, state, lib, this_ptr, obj_ptr, root, &x.rhs, *term, idx_values, rhs_chain, level, new_val, ) .map_err(|err| err.fill_position(*x_pos)) @@ -1324,13 +1324,13 @@ impl Engine { } #[cfg(not(feature = "no_object"))] - ChainType::Dot => { + ChainType::Dotting => { match rhs { // xxx.fn_name(arg_expr_list) Expr::FnCall(x, pos) if !x.is_qualified() && new_val.is_none() => { let FnCallExpr { name, hashes, .. } = x.as_ref(); let call_args = &mut idx_val - .as_fn_call_args() + .into_fn_call_args() .expect("never fails because `chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`"); self.make_method_call( mods, state, lib, name, *hashes, target, call_args, *pos, level, @@ -1482,7 +1482,9 @@ impl Engine { ) } // {xxx:map}.sub_lhs[expr] | {xxx:map}.sub_lhs.expr - Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target.is::() => { + Expr::Index(x, term, x_pos) | Expr::Dot(x, term, x_pos) + if target.is::() => + { let val_target = &mut match x.lhs { Expr::Property(ref p) => { let (name, pos) = &p.2; @@ -1495,7 +1497,7 @@ impl Engine { Expr::FnCall(ref x, pos) if !x.is_qualified() => { let FnCallExpr { name, hashes, .. } = x.as_ref(); let call_args = &mut idx_val - .as_fn_call_args() + .into_fn_call_args() .expect("never fails because `chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`"); let (val, _) = self.make_method_call( mods, state, lib, name, *hashes, target, call_args, pos, level, @@ -1509,22 +1511,22 @@ impl Engine { // Others - syntax error ref expr => unreachable!("invalid dot expression: {:?}", expr), }; - let rhs_chain = match_chain_type(rhs); + let rhs_chain = match_chaining_type(rhs); self.eval_dot_index_chain_helper( - mods, state, lib, this_ptr, val_target, root, &x.rhs, idx_values, - rhs_chain, level, new_val, + mods, state, lib, this_ptr, val_target, root, &x.rhs, *term, + idx_values, rhs_chain, level, new_val, ) .map_err(|err| err.fill_position(*x_pos)) } // xxx.sub_lhs[expr] | xxx.sub_lhs.expr - Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) => { + Expr::Index(x, term, x_pos) | Expr::Dot(x, term, x_pos) => { match x.lhs { // xxx.prop[expr] | xxx.prop.expr Expr::Property(ref p) => { let ((getter, hash_get), (setter, hash_set), (name, pos)) = p.as_ref(); - let rhs_chain = match_chain_type(rhs); + let rhs_chain = match_chaining_type(rhs); let hash_get = FnCallHashes::from_native(*hash_get); let hash_set = FnCallHashes::from_native(*hash_set); let mut arg_values = [target.as_mut(), &mut Default::default()]; @@ -1564,6 +1566,7 @@ impl Engine { &mut val.into(), root, &x.rhs, + *term, idx_values, rhs_chain, level, @@ -1614,9 +1617,9 @@ impl Engine { // xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr Expr::FnCall(ref f, pos) if !f.is_qualified() => { let FnCallExpr { name, hashes, .. } = f.as_ref(); - let rhs_chain = match_chain_type(rhs); + let rhs_chain = match_chaining_type(rhs); let args = &mut idx_val - .as_fn_call_args() + .into_fn_call_args() .expect("never fails because `chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`"); let (mut val, _) = self.make_method_call( mods, state, lib, name, *hashes, target, args, pos, level, @@ -1625,8 +1628,8 @@ impl Engine { let target = &mut val.into(); self.eval_dot_index_chain_helper( - mods, state, lib, this_ptr, target, root, &x.rhs, idx_values, - rhs_chain, level, new_val, + mods, state, lib, this_ptr, target, root, &x.rhs, *term, + idx_values, rhs_chain, level, new_val, ) .map_err(|err| err.fill_position(pos)) } @@ -1647,8 +1650,6 @@ impl Engine { /// Evaluate a dot/index chain. #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] - #[must_use] - #[must_use] fn eval_dot_index_chain( &self, scope: &mut Scope, @@ -1660,18 +1661,18 @@ impl Engine { level: usize, new_val: Option<((Dynamic, Position), (Option, Position))>, ) -> RhaiResult { - let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, op_pos) = match expr { + let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, term, op_pos) = match expr { #[cfg(not(feature = "no_index"))] - Expr::Index(x, pos) => (x.as_ref(), ChainType::Index, *pos), + Expr::Index(x, term, pos) => (x.as_ref(), ChainType::Indexing, *term, *pos), #[cfg(not(feature = "no_object"))] - Expr::Dot(x, pos) => (x.as_ref(), ChainType::Dot, *pos), + Expr::Dot(x, term, pos) => (x.as_ref(), ChainType::Dotting, *term, *pos), _ => unreachable!("index or dot chain expected, but gets {:?}", expr), }; let idx_values = &mut Default::default(); - self.eval_indexed_chain( - scope, mods, state, lib, this_ptr, rhs, chain_type, idx_values, 0, level, + self.eval_dot_index_chain_arguments( + scope, mods, state, lib, this_ptr, rhs, term, chain_type, idx_values, 0, level, )?; match lhs { @@ -1680,13 +1681,15 @@ impl Engine { #[cfg(not(feature = "unchecked"))] self.inc_operations(state, *var_pos)?; - let (target, _) = self.search_namespace(scope, mods, state, lib, this_ptr, lhs)?; + let (mut target, _) = + self.search_namespace(scope, mods, state, lib, this_ptr, lhs)?; - let obj_ptr = &mut target.into(); + let obj_ptr = &mut target; let root = (x.2.as_str(), *var_pos); + self.eval_dot_index_chain_helper( - mods, state, lib, &mut None, obj_ptr, root, rhs, idx_values, chain_type, level, - new_val, + mods, state, lib, &mut None, obj_ptr, root, rhs, term, idx_values, chain_type, + level, new_val, ) .map(|(v, _)| v) .map_err(|err| err.fill_position(op_pos)) @@ -1699,8 +1702,8 @@ impl Engine { let obj_ptr = &mut value.into(); let root = ("", expr.position()); self.eval_dot_index_chain_helper( - mods, state, lib, this_ptr, obj_ptr, root, rhs, idx_values, chain_type, level, - new_val, + mods, state, lib, this_ptr, obj_ptr, root, rhs, term, idx_values, chain_type, + level, new_val, ) .map(|(v, _)| v) .map_err(|err| err.fill_position(op_pos)) @@ -1712,8 +1715,7 @@ impl Engine { /// [`StaticVec`] is used to avoid an allocation in the overwhelming cases of /// just a few levels of indexing. #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] - #[must_use] - fn eval_indexed_chain( + fn eval_dot_index_chain_arguments( &self, scope: &mut Scope, mods: &mut Imports, @@ -1721,6 +1723,7 @@ impl Engine { lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, expr: &Expr, + terminate_chaining: bool, _parent_chain_type: ChainType, idx_values: &mut StaticVec, size: usize, @@ -1731,7 +1734,7 @@ impl Engine { match expr { #[cfg(not(feature = "no_object"))] - Expr::FnCall(x, _) if _parent_chain_type == ChainType::Dot && !x.is_qualified() => { + Expr::FnCall(x, _) if _parent_chain_type == ChainType::Dotting && !x.is_qualified() => { let crate::ast::FnCallExpr { args, constants, .. } = x.as_ref(); @@ -1751,30 +1754,30 @@ impl Engine { idx_values.push((arg_values, first_arg_pos).into()); } #[cfg(not(feature = "no_object"))] - Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dot => { + Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dotting => { unreachable!("function call in dot chain should not be namespace-qualified") } #[cfg(not(feature = "no_object"))] - Expr::Property(x) if _parent_chain_type == ChainType::Dot => { + Expr::Property(x) if _parent_chain_type == ChainType::Dotting => { idx_values.push(ChainArgument::Property((x.2).1)) } Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"), - Expr::Index(x, _) | Expr::Dot(x, _) => { + Expr::Index(x, term, _) | Expr::Dot(x, term, _) if !terminate_chaining => { let crate::ast::BinaryExpr { lhs, rhs, .. } = x.as_ref(); // Evaluate in left-to-right order let lhs_val = match lhs { #[cfg(not(feature = "no_object"))] - Expr::Property(x) if _parent_chain_type == ChainType::Dot => { + Expr::Property(x) if _parent_chain_type == ChainType::Dotting => { ChainArgument::Property((x.2).1) } Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"), #[cfg(not(feature = "no_object"))] Expr::FnCall(x, _) - if _parent_chain_type == ChainType::Dot && !x.is_qualified() => + if _parent_chain_type == ChainType::Dotting && !x.is_qualified() => { let crate::ast::FnCallExpr { args, constants, .. @@ -1795,41 +1798,37 @@ impl Engine { (arg_values, first_arg_pos).into() } #[cfg(not(feature = "no_object"))] - Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dot => { + Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dotting => { unreachable!("function call in dot chain should not be namespace-qualified") } #[cfg(not(feature = "no_object"))] - expr if _parent_chain_type == ChainType::Dot => { + expr if _parent_chain_type == ChainType::Dotting => { unreachable!("invalid dot expression: {:?}", expr); } #[cfg(not(feature = "no_index"))] - _ if _parent_chain_type == ChainType::Index => self + _ if _parent_chain_type == ChainType::Indexing => self .eval_expr(scope, mods, state, lib, this_ptr, lhs, level) .map(|v| (v.flatten(), lhs.position()).into())?, expr => unreachable!("unknown chained expression: {:?}", expr), }; // Push in reverse order - let chain_type = match expr { - #[cfg(not(feature = "no_index"))] - Expr::Index(_, _) => ChainType::Index, - #[cfg(not(feature = "no_object"))] - Expr::Dot(_, _) => ChainType::Dot, - _ => unreachable!("index or dot chain expected, but gets {:?}", expr), - }; - self.eval_indexed_chain( - scope, mods, state, lib, this_ptr, rhs, chain_type, idx_values, size, level, + let chain_type = match_chaining_type(expr); + + self.eval_dot_index_chain_arguments( + scope, mods, state, lib, this_ptr, rhs, *term, chain_type, idx_values, size, + level, )?; idx_values.push(lhs_val); } #[cfg(not(feature = "no_object"))] - _ if _parent_chain_type == ChainType::Dot => { + _ if _parent_chain_type == ChainType::Dotting => { unreachable!("invalid dot expression: {:?}", expr); } #[cfg(not(feature = "no_index"))] - _ if _parent_chain_type == ChainType::Index => idx_values.push( + _ if _parent_chain_type == ChainType::Indexing => idx_values.push( self.eval_expr(scope, mods, state, lib, this_ptr, expr, level) .map(|v| (v.flatten(), expr.position()).into())?, ), @@ -1842,7 +1841,6 @@ impl Engine { /// Get the value at the indexed position of a base type. /// [`Position`] in [`EvalAltResult`] may be [`NONE`][Position::NONE] and should be set afterwards. #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] - #[must_use] fn get_indexed_mut<'t>( &self, mods: &mut Imports, @@ -2013,7 +2011,6 @@ impl Engine { } /// Evaluate an expression. - #[must_use] pub(crate) fn eval_expr( &self, scope: &mut Scope, @@ -2051,13 +2048,13 @@ impl Engine { // lhs[idx_expr] #[cfg(not(feature = "no_index"))] - Expr::Index(_, _) => { + Expr::Index(_, _, _) => { self.eval_dot_index_chain(scope, mods, state, lib, this_ptr, expr, level, None) } // lhs.dot_rhs #[cfg(not(feature = "no_object"))] - Expr::Dot(_, _) => { + Expr::Dot(_, _, _) => { self.eval_dot_index_chain(scope, mods, state, lib, this_ptr, expr, level, None) } @@ -2214,7 +2211,6 @@ impl Engine { } /// Evaluate a statements block. - #[must_use] pub(crate) fn eval_stmt_block( &self, scope: &mut Scope, @@ -2292,7 +2288,6 @@ impl Engine { /// Evaluate an op-assignment statement. /// [`Position`] in [`EvalAltResult`] is [`NONE`][Position::NONE] and should be set afterwards. - #[must_use] pub(crate) fn eval_op_assignment( &self, mods: &mut Imports, @@ -2366,7 +2361,6 @@ impl Engine { /// /// This method uses some unsafe code, mainly for avoiding cloning of local variable names via /// direct lifetime casting. - #[must_use] pub(crate) fn eval_stmt( &self, scope: &mut Scope, @@ -2436,7 +2430,7 @@ impl Engine { let rhs_val = self .eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)? .flatten(); - let _new_val = Some(((rhs_val, rhs_expr.position()), (op_info.clone(), *op_pos))); + let _new_val = Some(((rhs_val, rhs_expr.position()), (*op_info, *op_pos))); // Must be either `var[index] op= val` or `var.prop op= val` match lhs_expr { @@ -2446,7 +2440,7 @@ impl Engine { } // idx_lhs[idx_expr] op= rhs #[cfg(not(feature = "no_index"))] - Expr::Index(_, _) => { + Expr::Index(_, _, _) => { self.eval_dot_index_chain( scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val, )?; @@ -2454,7 +2448,7 @@ impl Engine { } // dot_lhs.dot_rhs op= rhs #[cfg(not(feature = "no_object"))] - Expr::Dot(_, _) => { + Expr::Dot(_, _, _) => { self.eval_dot_index_chain( scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val, )?; @@ -2953,7 +2947,7 @@ impl Engine { EvalAltResult::ErrorModuleNotFound(path.to_string(), path_pos).into() })?; - export.as_ref().map(|x| x.name.clone()).map(|name| { + if let Some(name) = export.as_ref().map(|x| x.name.clone()) { if !module.is_indexed() { // Index the module (making a clone copy if necessary) if it is not indexed let mut module = crate::fn_native::shared_take_or_clone(module); @@ -2962,7 +2956,7 @@ impl Engine { } else { mods.push(name, module); } - }); + } state.modules += 1; @@ -2992,14 +2986,14 @@ impl Engine { // Share statement #[cfg(not(feature = "no_closure"))] Stmt::Share(name) => { - scope.get_index(name).map(|(index, _)| { + if let Some((index, _)) = scope.get_index(name) { let val = scope.get_mut_by_index(index); if !val.is_shared() { // Replace the variable with a shared value. *val = std::mem::take(val).into_shared(); } - }); + } Ok(Dynamic::UNIT) } }; @@ -3011,7 +3005,6 @@ impl Engine { /// Check a result to ensure that the data size is within allowable limit. #[cfg(feature = "unchecked")] #[inline(always)] - #[must_use] fn check_return_value(&self, result: RhaiResult) -> RhaiResult { result } @@ -3019,20 +3012,17 @@ impl Engine { /// Check a result to ensure that the data size is within allowable limit. #[cfg(not(feature = "unchecked"))] #[inline(always)] - #[must_use] fn check_return_value(&self, result: RhaiResult) -> RhaiResult { result.and_then(|r| self.check_data_size(&r).map(|_| r)) } #[cfg(feature = "unchecked")] #[inline(always)] - #[must_use] fn check_data_size(&self, _value: &Dynamic) -> Result<(), Box> { Ok(()) } #[cfg(not(feature = "unchecked"))] - #[must_use] fn check_data_size(&self, value: &Dynamic) -> Result<(), Box> { // Recursively calculate the size of a value (especially `Array` and `Map`) fn calc_size(value: &Dynamic) -> (usize, usize, usize) { @@ -3135,7 +3125,6 @@ impl Engine { /// Check if the number of operations stay within limit. #[cfg(not(feature = "unchecked"))] - #[must_use] pub(crate) fn inc_operations( &self, state: &mut EvalState, diff --git a/src/engine_api.rs b/src/engine_api.rs index ab896a2a..d61c4074 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -329,8 +329,7 @@ impl Engine { name: &str, get_fn: impl Fn(&mut T) -> V + SendSync + 'static, ) -> &mut Self { - use crate::engine::make_getter; - self.register_fn(&make_getter(name), get_fn) + self.register_fn(&crate::engine::make_getter(name), get_fn) } /// Register a getter function for a member of a registered type with the [`Engine`]. /// @@ -378,8 +377,7 @@ impl Engine { name: &str, get_fn: impl Fn(&mut T) -> Result> + SendSync + 'static, ) -> &mut Self { - use crate::engine::make_getter; - self.register_result_fn(&make_getter(name), get_fn) + self.register_result_fn(&crate::engine::make_getter(name), get_fn) } /// Register a setter function for a member of a registered type with the [`Engine`]. /// @@ -426,8 +424,7 @@ impl Engine { name: &str, set_fn: impl Fn(&mut T, V) + SendSync + 'static, ) -> &mut Self { - use crate::engine::make_setter; - self.register_fn(&make_setter(name), set_fn) + self.register_fn(&crate::engine::make_setter(name), set_fn) } /// Register a setter function for a member of a registered type with the [`Engine`]. /// @@ -477,8 +474,7 @@ impl Engine { name: &str, set_fn: impl Fn(&mut T, V) -> Result<(), Box> + SendSync + 'static, ) -> &mut Self { - use crate::engine::make_setter; - self.register_result_fn(&make_setter(name), set_fn) + self.register_result_fn(&crate::engine::make_setter(name), set_fn) } /// Short-hand for registering both getter and setter functions /// of a registered type with the [`Engine`]. @@ -883,7 +879,7 @@ impl Engine { pub fn register_indexer_get_set( &mut self, get_fn: impl Fn(&mut T, X) -> V + SendSync + 'static, - set_fn: impl Fn(&mut T, X, V) -> () + SendSync + 'static, + set_fn: impl Fn(&mut T, X, V) + SendSync + 'static, ) -> &mut Self { self.register_indexer_get(get_fn) .register_indexer_set(set_fn) @@ -1011,7 +1007,6 @@ impl Engine { /// # } /// ``` #[inline(always)] - #[must_use] pub fn compile(&self, script: &str) -> Result { self.compile_with_scope(&Default::default(), script) } @@ -1054,7 +1049,6 @@ impl Engine { /// # } /// ``` #[inline(always)] - #[must_use] pub fn compile_with_scope(&self, scope: &Scope, script: &str) -> Result { self.compile_scripts_with_scope(scope, &[script]) } @@ -1068,7 +1062,6 @@ impl Engine { /// [`AST`]. When it is evaluated later, `import` statement directly recall pre-resolved /// [modules][Module] and the resolution process is not performed again. #[cfg(not(feature = "no_module"))] - #[must_use] pub fn compile_into_self_contained( &self, scope: &Scope, @@ -1107,7 +1100,7 @@ impl Engine { let mut resolver = StaticModuleResolver::new(); let mut imports = Default::default(); - collect_imports(&ast, &mut resolver, &mut imports); + collect_imports(&ast, &resolver, &mut imports); if !imports.is_empty() { while let Some(path) = imports.iter().next() { @@ -1115,7 +1108,7 @@ impl Engine { match module_resolver.resolve_ast(self, None, &path, Position::NONE) { Some(Ok(module_ast)) => { - collect_imports(&module_ast, &mut resolver, &mut imports) + collect_imports(&module_ast, &resolver, &mut imports) } Some(err) => return err, None => (), @@ -1180,7 +1173,6 @@ impl Engine { /// # } /// ``` #[inline(always)] - #[must_use] pub fn compile_scripts_with_scope( &self, scope: &Scope, @@ -1189,8 +1181,7 @@ impl Engine { self.compile_with_scope_and_optimization_level(scope, scripts, self.optimization_level) } /// Join a list of strings and compile into an [`AST`] using own scope at a specific optimization level. - #[inline(always)] - #[must_use] + #[inline] pub(crate) fn compile_with_scope_and_optimization_level( &self, scope: &Scope, @@ -1209,7 +1200,6 @@ impl Engine { /// Read the contents of a file into a string. #[cfg(not(feature = "no_std"))] #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] - #[must_use] fn read_file(path: std::path::PathBuf) -> Result> { use std::io::Read; @@ -1265,7 +1255,6 @@ impl Engine { #[cfg(not(feature = "no_std"))] #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #[inline(always)] - #[must_use] pub fn compile_file(&self, path: std::path::PathBuf) -> Result> { self.compile_file_with_scope(&Default::default(), path) } @@ -1305,7 +1294,6 @@ impl Engine { #[cfg(not(feature = "no_std"))] #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #[inline(always)] - #[must_use] pub fn compile_file_with_scope( &self, scope: &Scope, @@ -1357,7 +1345,6 @@ impl Engine { /// # } /// ``` #[cfg(not(feature = "no_object"))] - #[must_use] pub fn parse_json( &self, json: impl AsRef, @@ -1433,7 +1420,6 @@ impl Engine { /// # } /// ``` #[inline(always)] - #[must_use] pub fn compile_expression(&self, script: &str) -> Result { self.compile_expression_with_scope(&Default::default(), script) } @@ -1476,8 +1462,7 @@ impl Engine { /// # Ok(()) /// # } /// ``` - #[inline(always)] - #[must_use] + #[inline] pub fn compile_expression_with_scope( &self, scope: &Scope, @@ -1510,7 +1495,6 @@ impl Engine { #[cfg(not(feature = "no_std"))] #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #[inline(always)] - #[must_use] pub fn eval_file( &self, path: std::path::PathBuf, @@ -1541,7 +1525,6 @@ impl Engine { #[cfg(not(feature = "no_std"))] #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #[inline(always)] - #[must_use] pub fn eval_file_with_scope( &self, scope: &mut Scope, @@ -1564,7 +1547,6 @@ impl Engine { /// # } /// ``` #[inline(always)] - #[must_use] pub fn eval(&self, script: &str) -> Result> { self.eval_with_scope(&mut Default::default(), script) } @@ -1591,7 +1573,6 @@ impl Engine { /// # } /// ``` #[inline(always)] - #[must_use] pub fn eval_with_scope( &self, scope: &mut Scope, @@ -1619,7 +1600,6 @@ impl Engine { /// # } /// ``` #[inline(always)] - #[must_use] pub fn eval_expression( &self, script: &str, @@ -1644,8 +1624,7 @@ impl Engine { /// # Ok(()) /// # } /// ``` - #[inline(always)] - #[must_use] + #[inline] pub fn eval_expression_with_scope( &self, scope: &mut Scope, @@ -1684,7 +1663,6 @@ impl Engine { /// # } /// ``` #[inline(always)] - #[must_use] pub fn eval_ast(&self, ast: &AST) -> Result> { self.eval_ast_with_scope(&mut Default::default(), ast) } @@ -1717,8 +1695,7 @@ impl Engine { /// # Ok(()) /// # } /// ``` - #[inline(always)] - #[must_use] + #[inline] pub fn eval_ast_with_scope( &self, scope: &mut Scope, @@ -1730,18 +1707,17 @@ impl Engine { let typ = self.map_type_name(result.type_name()); - return result.try_cast::().ok_or_else(|| { + result.try_cast::().ok_or_else(|| { EvalAltResult::ErrorMismatchOutputType( self.map_type_name(type_name::()).into(), typ.into(), Position::NONE, ) .into() - }); + }) } /// Evaluate an [`AST`] with own scope. - #[inline(always)] - #[must_use] + #[inline] pub(crate) fn eval_ast_with_scope_raw<'a>( &self, scope: &mut Scope, @@ -1797,7 +1773,7 @@ impl Engine { } /// Evaluate a string with own scope, but throw away the result and only return error (if any). /// Useful for when you don't need the result, but still need to keep track of possible errors. - #[inline(always)] + #[inline] pub fn consume_with_scope( &self, scope: &mut Scope, @@ -1824,7 +1800,7 @@ impl Engine { } /// Evaluate an [`AST`] with own scope, but throw away the result and only return error (if any). /// Useful for when you don't need the result, but still need to keep track of possible errors. - #[inline(always)] + #[inline] pub fn consume_ast_with_scope( &self, scope: &mut Scope, @@ -1889,8 +1865,7 @@ impl Engine { /// # } /// ``` #[cfg(not(feature = "no_function"))] - #[inline(always)] - #[must_use] + #[inline] pub fn call_fn( &self, scope: &mut Scope, @@ -1901,6 +1876,7 @@ impl Engine { let mut arg_values: crate::StaticVec<_> = Default::default(); args.parse(&mut arg_values); let mut args: crate::StaticVec<_> = arg_values.iter_mut().collect(); + let name = name.as_ref(); let result = self.call_fn_dynamic_raw(scope, ast, true, name, &mut None, &mut args)?; @@ -1969,8 +1945,7 @@ impl Engine { /// # } /// ``` #[cfg(not(feature = "no_function"))] - #[inline(always)] - #[must_use] + #[inline] pub fn call_fn_dynamic( &self, scope: &mut Scope, @@ -1980,6 +1955,7 @@ impl Engine { mut this_ptr: Option<&mut Dynamic>, mut arg_values: impl AsMut<[Dynamic]>, ) -> RhaiResult { + let name = name.as_ref(); let mut args: crate::StaticVec<_> = arg_values.as_mut().iter_mut().collect(); self.call_fn_dynamic_raw(scope, ast, eval_ast, name, &mut this_ptr, &mut args) @@ -1993,14 +1969,13 @@ impl Engine { /// Do not use the arguments after this call. If they are needed afterwards, /// clone them _before_ calling this function. #[cfg(not(feature = "no_function"))] - #[inline(always)] - #[must_use] + #[inline] pub(crate) fn call_fn_dynamic_raw( &self, scope: &mut Scope, ast: &AST, eval_ast: bool, - name: impl AsRef, + name: &str, this_ptr: &mut Option<&mut Dynamic>, args: &mut FnCallArgs, ) -> RhaiResult { @@ -2008,7 +1983,6 @@ impl Engine { let mods = &mut Default::default(); let lib = &[ast.lib()]; let statements = ast.statements(); - let name = name.as_ref(); if eval_ast && !statements.is_empty() { self.eval_global_statements(scope, mods, state, statements, lib, 0)?; @@ -2051,7 +2025,7 @@ impl Engine { /// (i.e. with [`Scope::push_constant`]). /// Then, the [`AST`] is cloned and the copy re-optimized before running. #[cfg(not(feature = "no_optimize"))] - #[inline(always)] + #[inline] #[must_use] pub fn optimize_ast( &self, @@ -2086,6 +2060,7 @@ impl Engine { /// 2) Functions in registered sub-modules /// 3) Functions in packages (optional) #[cfg(feature = "metadata")] + #[inline] #[must_use] pub fn gen_fn_signatures(&self, include_packages: bool) -> Vec { let mut signatures: Vec<_> = Default::default(); diff --git a/src/engine_settings.rs b/src/engine_settings.rs index e4176e21..06dd4f7b 100644 --- a/src/engine_settings.rs +++ b/src/engine_settings.rs @@ -291,7 +291,6 @@ impl Engine { /// # Ok(()) /// # } /// ``` - #[must_use] pub fn register_custom_operator( &mut self, keyword: impl AsRef + Into, @@ -310,18 +309,18 @@ impl Engine { // Disabled keywords are OK Some(token) if token.is_keyword() => { if !self.disabled_symbols.contains(token.syntax().as_ref()) { - return Err(format!("'{}' is a reserved keyword", keyword.as_ref()).into()); + return Err(format!("'{}' is a reserved keyword", keyword.as_ref())); } } // Active standard symbols cannot be made custom Some(token) if token.is_symbol() => { if !self.disabled_symbols.contains(token.syntax().as_ref()) { - return Err(format!("'{}' is a reserved operator", keyword.as_ref()).into()); + return Err(format!("'{}' is a reserved operator", keyword.as_ref())); } } // Active standard symbols cannot be made custom Some(token) if !self.disabled_symbols.contains(token.syntax().as_ref()) => { - return Err(format!("'{}' is a reserved symbol", keyword.as_ref()).into()) + return Err(format!("'{}' is a reserved symbol", keyword.as_ref())) } // Disabled symbols are OK Some(_) => (), diff --git a/src/error_parsing.rs b/src/error_parsing.rs index d89ca226..c65b9b07 100644 --- a/src/error_parsing.rs +++ b/src/error_parsing.rs @@ -39,7 +39,6 @@ pub enum LexError { impl Error for LexError {} impl fmt::Display for LexError { - #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::UnexpectedInput(s) => write!(f, "Unexpected '{}'", s), @@ -283,7 +282,6 @@ pub struct ParseError(pub Box, pub Position); impl Error for ParseError {} impl fmt::Display for ParseError { - #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.0, f)?; diff --git a/src/fn_builtin.rs b/src/fn_builtin.rs index 3d4346bf..2230804a 100644 --- a/src/fn_builtin.rs +++ b/src/fn_builtin.rs @@ -18,7 +18,7 @@ use rust_decimal::Decimal; const BUILTIN: &str = "never fails because this is built-in code and the type is already checked"; /// Is the type a numeric type? -#[inline(always)] +#[inline] #[must_use] fn is_numeric(type_id: TypeId) -> bool { let result = type_id == TypeId::of::() @@ -206,7 +206,6 @@ pub fn get_builtin_binary_op_fn( // char op string if types_pair == (TypeId::of::(), TypeId::of::()) { - #[inline(always)] fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) { let x = args[0].as_char().expect(BUILTIN); let y = &*args[1].read_lock::().expect(BUILTIN); @@ -233,7 +232,6 @@ pub fn get_builtin_binary_op_fn( } // string op char if types_pair == (TypeId::of::(), TypeId::of::()) { - #[inline(always)] fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) { let x = &*args[0].read_lock::().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN); diff --git a/src/fn_call.rs b/src/fn_call.rs index 45cc65e6..23bc447a 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -84,7 +84,9 @@ impl<'a> ArgBackup<'a> { /// the current scope. Otherwise it is undefined behavior as the shorter lifetime will leak. #[inline(always)] fn restore_first_arg(mut self, args: &mut FnCallArgs<'a>) { - self.orig_mut.take().map(|p| args[0] = p); + if let Some(p) = self.orig_mut.take() { + args[0] = p; + } } } @@ -100,8 +102,7 @@ impl Drop for ArgBackup<'_> { } #[cfg(not(feature = "no_closure"))] -#[inline(always)] -#[must_use] +#[inline] pub fn ensure_no_data_race( fn_name: &str, args: &FnCallArgs, @@ -157,7 +158,6 @@ impl Engine { /// 3) Global modules - packages /// 4) Imported modules - functions marked with global namespace /// 5) Global sub-modules - functions marked with global namespace - #[inline(always)] #[must_use] fn resolve_fn<'s>( &self, @@ -262,7 +262,6 @@ impl Engine { /// Function call arguments be _consumed_ when the function requires them to be passed by value. /// All function arguments not in the first position are always passed by value and thus consumed. /// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`! - #[must_use] pub(crate) fn call_native_fn( &self, mods: &Imports, @@ -291,7 +290,7 @@ impl Engine { let mut backup: Option = None; if is_method_call && func.is_pure() && !args.is_empty() { backup = Some(Default::default()); - backup.as_mut().map(|bk| bk.change_first_arg_to_copy(args)); + backup.as_mut().unwrap().change_first_arg_to_copy(args); } // Run external function @@ -312,7 +311,9 @@ impl Engine { }; // Restore the original reference - backup.map(|bk| bk.restore_first_arg(args)); + if let Some(bk) = backup { + bk.restore_first_arg(args) + } let result = result.map_err(|err| err.fill_position(pos))?; @@ -434,7 +435,6 @@ impl Engine { /// All function arguments not in the first position are always passed by value and thus consumed. /// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`! #[cfg(not(feature = "no_function"))] - #[must_use] pub(crate) fn call_script_fn( &self, scope: &mut Scope, @@ -447,7 +447,6 @@ impl Engine { pos: Position, level: usize, ) -> RhaiResult { - #[inline(always)] fn make_error( name: String, fn_def: &crate::ast::ScriptFnDef, @@ -564,7 +563,6 @@ impl Engine { // Does a scripted function exist? #[cfg(not(feature = "no_function"))] - #[inline(always)] #[must_use] pub(crate) fn has_script_fn( &self, @@ -604,7 +602,6 @@ impl Engine { /// Function call arguments may be _consumed_ when the function requires them to be passed by value. /// All function arguments not in the first position are always passed by value and thus consumed. /// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`! - #[must_use] pub(crate) fn exec_fn_call( &self, mods: &mut Imports, @@ -619,7 +616,6 @@ impl Engine { _capture_scope: Option, _level: usize, ) -> Result<(Dynamic, bool), Box> { - #[inline(always)] fn no_method_err(name: &str, pos: Position) -> Result<(Dynamic, bool), Box> { let msg = format!("'{0}' should not be called this way. Try {0}(...);", name); EvalAltResult::ErrorRuntime(msg.into(), pos).into() @@ -706,7 +702,7 @@ impl Engine { // Move captured variables into scope #[cfg(not(feature = "no_closure"))] if !func.externals.is_empty() { - _capture_scope.map(|captured| { + if let Some(captured) = _capture_scope { captured .into_iter() .filter(|(name, _, _)| func.externals.contains(name.as_ref())) @@ -714,7 +710,7 @@ impl Engine { // Consume the scope values. scope.push_dynamic(name, value); }) - }); + } } let result = if _is_method_call { @@ -750,7 +746,7 @@ impl Engine { let mut backup: Option = None; if is_ref_mut && !args.is_empty() { backup = Some(Default::default()); - backup.as_mut().map(|bk| bk.change_first_arg_to_copy(args)); + backup.as_mut().unwrap().change_first_arg_to_copy(args); } let orig_source = state.source.take(); @@ -765,7 +761,9 @@ impl Engine { state.source = orig_source; // Restore the original reference - backup.map(|bk| bk.restore_first_arg(args)); + if let Some(bk) = backup { + bk.restore_first_arg(args) + } result? }; @@ -782,8 +780,7 @@ impl Engine { /// Evaluate a list of statements with no `this` pointer. /// This is commonly used to evaluate a list of statements in an [`AST`] or a script function body. - #[inline(always)] - #[must_use] + #[inline] pub(crate) fn eval_global_statements( &self, scope: &mut Scope, @@ -804,7 +801,6 @@ impl Engine { } /// Evaluate a text script in place - used primarily for 'eval'. - #[must_use] fn eval_script_expr_in_place( &self, scope: &mut Scope, @@ -856,7 +852,6 @@ impl Engine { /// Call a dot method. #[cfg(not(feature = "no_object"))] - #[must_use] pub(crate) fn make_method_call( &self, mods: &mut Imports, @@ -895,7 +890,7 @@ impl Engine { ) } KEYWORD_FN_PTR_CALL => { - if call_args.len() > 0 { + if !call_args.is_empty() { if !call_args[0].is::() { return Err(self.make_type_mismatch_err::( self.map_type_name(call_args[0].type_name()), @@ -1018,8 +1013,7 @@ impl Engine { } /// Evaluate an argument. - #[inline(always)] - #[must_use] + #[inline] pub(crate) fn get_arg_value( &self, scope: &mut Scope, @@ -1041,7 +1035,6 @@ impl Engine { } /// Call a function in normal function-call style. - #[must_use] pub(crate) fn make_function_call( &self, scope: &mut Scope, @@ -1107,7 +1100,7 @@ impl Engine { return arg .as_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, arg_pos)) - .and_then(|s| FnPtr::try_from(s)) + .and_then(FnPtr::try_from) .map(Into::::into) .map_err(|err| err.fill_position(arg_pos)); } @@ -1293,7 +1286,6 @@ impl Engine { } /// Call a namespace-qualified function in normal function-call style. - #[must_use] pub(crate) fn make_qualified_function_call( &self, scope: &mut Scope, @@ -1390,10 +1382,10 @@ impl Engine { // Clone first argument if the function is not a method after-all if !func.map(|f| f.is_method()).unwrap_or(true) { - first_arg_value.map(|first| { + if let Some(first) = first_arg_value { *first = args[0].clone(); args[0] = first; - }); + } } match func { diff --git a/src/fn_hash.rs b/src/fn_hash.rs index 887ebceb..fcb66e76 100644 --- a/src/fn_hash.rs +++ b/src/fn_hash.rs @@ -21,7 +21,7 @@ impl Hasher for StraightHasher { fn finish(&self) -> u64 { self.0 } - #[inline(always)] + #[inline] fn write(&mut self, bytes: &[u8]) { assert_eq!(bytes.len(), 8, "StraightHasher can only hash u64 values"); @@ -62,10 +62,7 @@ pub fn get_hasher() -> ahash::AHasher { /// The first module name is skipped. Hashing starts from the _second_ module in the chain. #[inline] #[must_use] -pub fn calc_qualified_var_hash<'a>( - modules: impl Iterator, - var_name: impl AsRef, -) -> u64 { +pub fn calc_qualified_var_hash<'a>(modules: impl Iterator, var_name: &str) -> u64 { let s = &mut get_hasher(); // We always skip the first module @@ -75,7 +72,7 @@ pub fn calc_qualified_var_hash<'a>( .skip(1) .for_each(|m| m.hash(s)); len.hash(s); - var_name.as_ref().hash(s); + var_name.hash(s); s.finish() } @@ -92,7 +89,7 @@ pub fn calc_qualified_var_hash<'a>( #[must_use] pub fn calc_qualified_fn_hash<'a>( modules: impl Iterator, - fn_name: impl AsRef, + fn_name: &str, num: usize, ) -> u64 { let s = &mut get_hasher(); @@ -104,7 +101,7 @@ pub fn calc_qualified_fn_hash<'a>( .skip(1) .for_each(|m| m.hash(s)); len.hash(s); - fn_name.as_ref().hash(s); + fn_name.hash(s); num.hash(s); s.finish() } @@ -115,7 +112,7 @@ pub fn calc_qualified_fn_hash<'a>( /// Parameter types are passed in via [`TypeId`] values from an iterator. #[inline(always)] #[must_use] -pub fn calc_fn_hash(fn_name: impl AsRef, num: usize) -> u64 { +pub fn calc_fn_hash(fn_name: &str, num: usize) -> u64 { calc_qualified_fn_hash(empty(), fn_name, num) } @@ -127,7 +124,10 @@ pub fn calc_fn_hash(fn_name: impl AsRef, num: usize) -> u64 { pub fn calc_fn_params_hash(params: impl Iterator) -> u64 { let s = &mut get_hasher(); let mut len = 0; - params.inspect(|_| len += 1).for_each(|t| t.hash(s)); + params.for_each(|t| { + len += 1; + t.hash(s); + }); len.hash(s); s.finish() } diff --git a/src/fn_native.rs b/src/fn_native.rs index 82670f89..9930fec4 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -144,7 +144,6 @@ impl<'a> NativeCallContext<'a> { /// Not available under `no_module`. #[cfg(not(feature = "no_module"))] #[inline(always)] - #[must_use] pub fn iter_imports(&self) -> impl Iterator { self.mods.iter().flat_map(|&m| m.iter()) } @@ -152,7 +151,6 @@ impl<'a> NativeCallContext<'a> { #[cfg(not(feature = "no_module"))] #[allow(dead_code)] #[inline(always)] - #[must_use] pub(crate) fn iter_imports_raw( &self, ) -> impl Iterator)> { @@ -171,7 +169,6 @@ impl<'a> NativeCallContext<'a> { } /// Get an iterator over the namespaces containing definitions of all script-defined functions. #[inline(always)] - #[must_use] pub fn iter_namespaces(&self) -> impl Iterator { self.lib.iter().cloned() } @@ -195,8 +192,7 @@ impl<'a> NativeCallContext<'a> { /// /// If `is_method` is [`true`], the first argument is assumed to be passed /// by reference and is not consumed. - #[inline(always)] - #[must_use] + #[inline] pub fn call_fn_dynamic_raw( &self, fn_name: impl AsRef, @@ -249,7 +245,6 @@ pub fn shared_take_or_clone(value: Shared) -> T { /// Consume a [`Shared`] resource if is unique (i.e. not shared). #[inline(always)] -#[must_use] pub fn shared_try_take(value: Shared) -> Result> { Shared::try_unwrap(value) } @@ -338,7 +333,6 @@ pub enum CallableFunction { } impl fmt::Debug for CallableFunction { - #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Pure(_) => write!(f, "NativePureFunction"), @@ -353,7 +347,6 @@ impl fmt::Debug for CallableFunction { } impl fmt::Display for CallableFunction { - #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Pure(_) => write!(f, "NativePureFunction"), @@ -369,7 +362,7 @@ impl fmt::Display for CallableFunction { impl CallableFunction { /// Is this a pure native Rust function? - #[inline(always)] + #[inline] #[must_use] pub fn is_pure(&self) -> bool { match self { @@ -383,7 +376,7 @@ impl CallableFunction { } } /// Is this a native Rust method function? - #[inline(always)] + #[inline] #[must_use] pub fn is_method(&self) -> bool { match self { @@ -397,7 +390,7 @@ impl CallableFunction { } } /// Is this an iterator function? - #[inline(always)] + #[inline] #[must_use] pub const fn is_iter(&self) -> bool { match self { @@ -409,7 +402,7 @@ impl CallableFunction { } } /// Is this a Rhai-scripted function? - #[inline(always)] + #[inline] #[must_use] pub const fn is_script(&self) -> bool { match self { @@ -420,7 +413,7 @@ impl CallableFunction { } } /// Is this a plugin function? - #[inline(always)] + #[inline] #[must_use] pub const fn is_plugin_fn(&self) -> bool { match self { @@ -432,7 +425,7 @@ impl CallableFunction { } } /// Is this a native Rust function? - #[inline(always)] + #[inline] #[must_use] pub const fn is_native(&self) -> bool { match self { @@ -445,7 +438,7 @@ impl CallableFunction { } } /// Get the access mode. - #[inline(always)] + #[inline] #[must_use] pub fn access(&self) -> FnAccess { match self { @@ -457,7 +450,7 @@ impl CallableFunction { } } /// Get a shared reference to a native Rust function. - #[inline(always)] + #[inline] #[must_use] pub fn get_native_fn(&self) -> Option<&Shared> { match self { @@ -472,7 +465,7 @@ impl CallableFunction { /// /// Not available under `no_function`. #[cfg(not(feature = "no_function"))] - #[inline(always)] + #[inline] #[must_use] pub const fn get_script_fn_def(&self) -> Option<&Shared> { match self { @@ -481,7 +474,7 @@ impl CallableFunction { } } /// Get a reference to an iterator function. - #[inline(always)] + #[inline] #[must_use] pub fn get_iter_fn(&self) -> Option { match self { @@ -493,9 +486,9 @@ impl CallableFunction { } } /// Get a shared reference to a plugin function. - #[inline(always)] + #[inline] #[must_use] - pub fn get_plugin_fn<'s>(&'s self) -> Option<&Shared> { + pub fn get_plugin_fn(&self) -> Option<&Shared> { match self { Self::Plugin(f) => Some(f), Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => None, @@ -557,6 +550,6 @@ impl From for CallableFunction { impl From> for CallableFunction { #[inline(always)] fn from(func: Shared) -> Self { - Self::Plugin(func.into()) + Self::Plugin(func) } } diff --git a/src/fn_ptr.rs b/src/fn_ptr.rs index 5c22208d..6815ce0c 100644 --- a/src/fn_ptr.rs +++ b/src/fn_ptr.rs @@ -19,7 +19,6 @@ pub struct FnPtr(Identifier, StaticVec); impl FnPtr { /// Create a new function pointer. #[inline(always)] - #[must_use] pub fn new(name: impl Into) -> Result> { name.into().try_into() } @@ -27,7 +26,7 @@ impl FnPtr { #[inline(always)] #[must_use] pub(crate) fn new_unchecked(name: Identifier, curry: StaticVec) -> Self { - Self(name.into(), curry) + Self(name, curry) } /// Get the name of the function. #[inline(always)] @@ -96,8 +95,7 @@ impl FnPtr { /// This is to avoid unnecessarily cloning the arguments. /// Do not use the arguments after this call. If they are needed afterwards, /// clone them _before_ calling this function. - #[inline(always)] - #[must_use] + #[inline] pub fn call_dynamic( &self, ctx: &NativeCallContext, @@ -127,7 +125,6 @@ impl FnPtr { } impl fmt::Display for FnPtr { - #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Fn({})", self.0) } @@ -136,7 +133,7 @@ impl fmt::Display for FnPtr { impl TryFrom for FnPtr { type Error = Box; - #[inline(always)] + #[inline] fn try_from(value: Identifier) -> Result { if is_valid_identifier(value.chars()) { Ok(Self(value, Default::default())) diff --git a/src/fn_register.rs b/src/fn_register.rs index 0e978142..b74a93ce 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -88,7 +88,7 @@ pub trait RegisterNativeFunction { fn return_type_name() -> &'static str; } -#[inline(always)] +#[inline] #[must_use] fn is_setter(_fn_name: &str) -> bool { #[cfg(not(feature = "no_object"))] diff --git a/src/immutable_string.rs b/src/immutable_string.rs index d6555698..54e9bf88 100644 --- a/src/immutable_string.rs +++ b/src/immutable_string.rs @@ -16,8 +16,8 @@ use std::{ /// The system immutable string type. /// -/// An [`ImmutableString`] wraps an [`Rc`][std::rc::Rc]`<`[`String`]`>` -/// (or [`Arc`][std::sync::Arc]`<`[`String`]`>` under the `sync` feature) +/// An [`ImmutableString`] wraps an [`Rc`][std::rc::Rc]`<`[`SmartString`][smartstring::SmartString]`>` +/// (or [`Arc`][std::sync::Arc]`<`[`SmartString`][smartstring::SmartString]`>` under the `sync` feature) /// so that it can be simply shared and not cloned. /// /// # Example @@ -255,12 +255,10 @@ impl Add<&str> for ImmutableString { #[inline] fn add(mut self, rhs: &str) -> Self::Output { - if rhs.is_empty() { - self - } else { + if !rhs.is_empty() { self.make_mut().push_str(rhs); - self } + self } } @@ -322,7 +320,7 @@ impl Add for &ImmutableString { } impl AddAssign for ImmutableString { - #[inline(always)] + #[inline] fn add_assign(&mut self, rhs: String) { if !rhs.is_empty() { if self.is_empty() { diff --git a/src/module/mod.rs b/src/module/mod.rs index 1ff6e4a8..413fad22 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -112,10 +112,10 @@ impl FuncInfo { /// # Note /// /// The first module name is skipped. Hashing starts from the _second_ module in the chain. -#[inline(always)] +#[inline] fn calc_native_fn_hash<'a>( modules: impl Iterator, - fn_name: impl AsRef, + fn_name: &str, params: &[TypeId], ) -> u64 { let hash_script = calc_qualified_fn_hash(modules, fn_name, params.len()); @@ -324,7 +324,7 @@ impl Module { /// let module = Module::new(); /// assert!(module.is_empty()); /// ``` - #[inline(always)] + #[inline] #[must_use] pub fn is_empty(&self) -> bool { self.functions.is_empty() @@ -437,7 +437,7 @@ impl Module { /// module.set_var("answer", 42_i64); /// assert_eq!(module.get_var_value::("answer").unwrap(), 42); /// ``` - #[inline(always)] + #[inline] pub fn set_var( &mut self, name: impl Into, @@ -457,7 +457,6 @@ impl Module { /// Get a reference to a namespace-qualified variable. /// Name and Position in [`EvalAltResult`] are [`None`] and [`NONE`][Position::NONE] and must be set afterwards. #[inline(always)] - #[must_use] pub(crate) fn get_qualified_var(&self, hash_var: u64) -> Result<&Dynamic, Box> { self.all_variables.get(&hash_var).ok_or_else(|| { EvalAltResult::ErrorVariableNotFound(String::new(), Position::NONE).into() @@ -519,7 +518,7 @@ impl Module { /// By taking a mutable reference, it is assumed that some sub-modules will be modified. /// Thus the [`Module`] is automatically set to be non-indexed. #[cfg(not(feature = "no_module"))] - #[inline(always)] + #[inline] #[must_use] pub(crate) fn sub_modules_mut(&mut self) -> &mut BTreeMap> { // We must assume that the user has changed the sub-modules @@ -580,7 +579,7 @@ impl Module { /// module.set_sub_module("question", sub_module); /// assert!(module.get_sub_module("question").is_some()); /// ``` - #[inline(always)] + #[inline] pub fn set_sub_module( &mut self, name: impl Into, @@ -624,7 +623,7 @@ impl Module { /// The _last entry_ in the list should be the _return type_ of the function. /// In other words, the number of entries should be one larger than the number of parameters. #[cfg(feature = "metadata")] - #[inline(always)] + #[inline] pub fn update_fn_metadata(&mut self, hash_fn: u64, arg_names: &[&str]) -> &mut Self { let param_names = arg_names .iter() @@ -641,7 +640,7 @@ impl Module { /// Update the namespace of a registered function. /// /// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call. - #[inline(always)] + #[inline] pub fn update_fn_namespace(&mut self, hash_fn: u64, namespace: FnNamespace) -> &mut Self { if let Some(f) = self.functions.get_mut(&hash_fn) { f.namespace = namespace; @@ -652,7 +651,7 @@ impl Module { } /// Remap type ID. - #[inline(always)] + #[inline] #[must_use] fn map_type(map: bool, type_id: TypeId) -> TypeId { if !map { @@ -706,7 +705,7 @@ impl Module { #[cfg(feature = "metadata")] param_names.shrink_to_fit(); - let hash_fn = calc_native_fn_hash(empty(), &name, ¶m_types); + let hash_fn = calc_native_fn_hash(empty(), name.as_ref(), ¶m_types); self.functions.insert( hash_fn, @@ -1207,7 +1206,7 @@ impl Module { /// Merge another [`Module`] into this [`Module`]. #[inline(always)] pub fn merge(&mut self, other: &Self) -> &mut Self { - self.merge_filtered(other, &mut |_, _, _, _, _| true) + self.merge_filtered(other, &|_, _, _, _, _| true) } /// Merge another [`Module`] into this [`Module`] based on a filter predicate. @@ -1292,14 +1291,12 @@ impl Module { /// Get an iterator to the sub-modules in the [`Module`]. #[inline(always)] - #[must_use] pub fn iter_sub_modules(&self) -> impl Iterator)> { self.modules.iter().map(|(k, m)| (k.as_str(), m.clone())) } /// Get an iterator to the variables in the [`Module`]. #[inline(always)] - #[must_use] pub fn iter_var(&self) -> impl Iterator { self.variables.iter().map(|(k, v)| (k.as_str(), v)) } @@ -1307,7 +1304,6 @@ impl Module { /// Get an iterator to the functions in the [`Module`]. #[inline(always)] #[allow(dead_code)] - #[must_use] pub(crate) fn iter_fn(&self) -> impl Iterator { self.functions.values().map(Box::as_ref) } @@ -1322,7 +1318,6 @@ impl Module { /// 5) Shared reference to function definition [`ScriptFnDef`][crate::ast::ScriptFnDef]. #[cfg(not(feature = "no_function"))] #[inline(always)] - #[must_use] pub(crate) fn iter_script_fn( &self, ) -> impl Iterator< @@ -1360,7 +1355,6 @@ impl Module { #[cfg(not(feature = "no_function"))] #[cfg(not(feature = "internals"))] #[inline(always)] - #[must_use] pub fn iter_script_fn_info( &self, ) -> impl Iterator { @@ -1419,7 +1413,6 @@ impl Module { /// # } /// ``` #[cfg(not(feature = "no_module"))] - #[must_use] pub fn eval_ast_as_new( mut scope: crate::Scope, ast: &crate::AST, @@ -1529,13 +1522,13 @@ impl Module { // Index all variables module.variables.iter().for_each(|(var_name, value)| { - let hash_var = crate::calc_qualified_var_hash(path.iter().map(|&v| v), var_name); + let hash_var = crate::calc_qualified_var_hash(path.iter().copied(), var_name); variables.insert(hash_var, value.clone()); }); // Index type iterators module.type_iterators.iter().for_each(|(&type_id, func)| { - type_iterators.insert(type_id, func.clone()); + type_iterators.insert(type_id, *func); contains_indexed_global_functions = true; }); @@ -1611,10 +1604,10 @@ impl Module { } /// Set a type iterator into the [`Module`]. - #[inline(always)] + #[inline] pub fn set_iter(&mut self, type_id: TypeId, func: IteratorFn) -> &mut Self { if self.indexed { - self.all_type_iterators.insert(type_id, func.clone()); + self.all_type_iterators.insert(type_id, func); self.contains_indexed_global_functions = true; } self.type_iterators.insert(type_id, func); @@ -1678,7 +1671,6 @@ pub struct NamespaceRef { } impl fmt::Debug for NamespaceRef { - #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(index) = self.index { write!(f, "{} -> ", index)?; @@ -1696,7 +1688,6 @@ impl fmt::Debug for NamespaceRef { } impl fmt::Display for NamespaceRef { - #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for Ident { name, .. } in self.path.iter() { write!(f, "{}{}", name, Token::DoubleColon.syntax())?; @@ -1735,7 +1726,10 @@ impl NamespaceRef { #[inline(always)] #[must_use] pub fn new(&self) -> Self { - Default::default() + Self { + index: None, + path: StaticVec::new(), + } } /// Get the [`Scope`][crate::Scope] index offset. #[inline(always)] diff --git a/src/module/resolvers/collection.rs b/src/module/resolvers/collection.rs index 81022aa4..55087afe 100644 --- a/src/module/resolvers/collection.rs +++ b/src/module/resolvers/collection.rs @@ -77,16 +77,9 @@ impl ModuleResolversCollection { } /// Get an iterator of all the [module resolvers][ModuleResolver]. #[inline(always)] - #[must_use] pub fn iter(&self) -> impl Iterator { self.0.iter().map(|v| v.as_ref()) } - /// Get a mutable iterator of all the [module resolvers][ModuleResolver]. - #[inline(always)] - #[must_use] - pub fn into_iter(self) -> impl Iterator> { - self.0.into_iter() - } /// Remove all [module resolvers][ModuleResolver]. #[inline(always)] pub fn clear(&mut self) -> &mut Self { @@ -114,6 +107,16 @@ impl ModuleResolversCollection { } } +impl IntoIterator for ModuleResolversCollection { + type Item = Box; + type IntoIter = std::vec::IntoIter>; + + #[inline(always)] + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + impl ModuleResolver for ModuleResolversCollection { fn resolve( &self, diff --git a/src/module/resolvers/file.rs b/src/module/resolvers/file.rs index 441114f3..f3b06113 100644 --- a/src/module/resolvers/file.rs +++ b/src/module/resolvers/file.rs @@ -200,7 +200,7 @@ impl FileModuleResolver { } /// Is a particular path cached? - #[inline(always)] + #[inline] #[must_use] pub fn is_cached(&self, path: &str, source_path: Option<&str>) -> bool { if !self.cache_enabled { @@ -227,7 +227,7 @@ impl FileModuleResolver { /// Remove the specified path from internal cache. /// /// The next time this path is resolved, the script file will be loaded once again. - #[inline(always)] + #[inline] #[must_use] pub fn clear_cache_for_path( &mut self, @@ -252,7 +252,6 @@ impl FileModuleResolver { } /// Construct a full file path. #[must_use] - #[must_use] fn get_file_path(&self, path: &str, source_path: Option<&str>) -> PathBuf { let path = Path::new(path); diff --git a/src/module/resolvers/mod.rs b/src/module/resolvers/mod.rs index 8303bc9b..13030ad7 100644 --- a/src/module/resolvers/mod.rs +++ b/src/module/resolvers/mod.rs @@ -23,7 +23,6 @@ pub use stat::StaticModuleResolver; /// Trait that encapsulates a module resolution service. pub trait ModuleResolver: SendSync { /// Resolve a module based on a path string. - #[must_use] fn resolve( &self, engine: &Engine, diff --git a/src/module/resolvers/stat.rs b/src/module/resolvers/stat.rs index f1fcc3b4..0b43bd28 100644 --- a/src/module/resolvers/stat.rs +++ b/src/module/resolvers/stat.rs @@ -64,33 +64,28 @@ impl StaticModuleResolver { } /// Get an iterator of all the [modules][Module]. #[inline(always)] - #[must_use] pub fn iter(&self) -> impl Iterator)> { self.0.iter().map(|(k, v)| (k.as_str(), v)) } /// Get a mutable iterator of all the [modules][Module]. #[inline(always)] - #[must_use] pub fn iter_mut(&mut self) -> impl Iterator)> { self.0.iter_mut().map(|(k, v)| (k.as_str(), v)) } /// Get a mutable iterator of all the modules. #[inline(always)] - #[must_use] pub fn into_iter(self) -> impl Iterator)> { self.0.into_iter() } /// Get an iterator of all the [module][Module] paths. #[inline(always)] - #[must_use] pub fn paths(&self) -> impl Iterator { self.0.keys().map(|s| s.as_str()) } /// Get an iterator of all the [modules][Module]. #[inline(always)] - #[must_use] pub fn values(&self) -> impl Iterator> { - self.0.values().map(|m| m) + self.0.values() } /// Remove all [modules][Module]. #[inline(always)] diff --git a/src/optimize.rs b/src/optimize.rs index 17468495..b5d25a9b 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -18,6 +18,9 @@ use std::{ mem, }; +#[cfg(not(feature = "no_closure"))] +use crate::engine::KEYWORD_IS_SHARED; + /// Level of optimization performed. #[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)] pub enum OptimizationLevel { @@ -118,42 +121,40 @@ impl<'a> OptimizerState<'a> { } }) } -} + /// Call a registered function + #[inline(always)] + pub fn call_fn_with_constant_arguments( + &self, + fn_name: &str, + arg_values: &mut [Dynamic], + ) -> Option { + self.engine + .call_native_fn( + &Default::default(), + &mut Default::default(), + self.lib, + fn_name, + calc_fn_hash(fn_name, arg_values.len()), + &mut arg_values.iter_mut().collect::>(), + false, + false, + Position::NONE, + ) + .ok() + .map(|(v, _)| v) + } + // Has a system function a Rust-native override? + pub fn has_native_fn(&self, hash_script: u64, arg_types: &[TypeId]) -> bool { + let hash_params = calc_fn_params_hash(arg_types.iter().cloned()); + let hash = combine_hashes(hash_script, hash_params); -// Has a system function a Rust-native override? -fn has_native_fn(state: &OptimizerState, hash_script: u64, arg_types: &[TypeId]) -> bool { - let hash_params = calc_fn_params_hash(arg_types.iter().cloned()); - let hash = combine_hashes(hash_script, hash_params); - - // First check registered functions - state.engine.global_namespace.contains_fn(hash) + // First check registered functions + self.engine.global_namespace.contains_fn(hash) // Then check packages - || state.engine.global_modules.iter().any(|m| m.contains_fn(hash)) + || self.engine.global_modules.iter().any(|m| m.contains_fn(hash)) // Then check sub-modules - || state.engine.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash)) -} - -/// Call a registered function -fn call_fn_with_constant_arguments( - state: &OptimizerState, - fn_name: &str, - arg_values: &mut [Dynamic], -) -> Option { - state - .engine - .call_native_fn( - &mut Default::default(), - &mut Default::default(), - state.lib, - fn_name, - calc_fn_hash(fn_name, arg_values.len()), - &mut arg_values.iter_mut().collect::>(), - false, - false, - Position::NONE, - ) - .ok() - .map(|(v, _)| v) + || self.engine.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash)) + } } /// Optimize a block of [statements][Stmt]. @@ -705,14 +706,14 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { } // lhs.rhs #[cfg(not(feature = "no_object"))] - Expr::Dot(x, _) if !_chaining => match (&mut x.lhs, &mut x.rhs) { + Expr::Dot(x,_, _) if !_chaining => match (&mut x.lhs, &mut x.rhs) { // map.string (Expr::Map(m, pos), Expr::Property(p)) if m.0.iter().all(|(_, x)| x.is_pure()) => { let prop = p.2.0.as_str(); // Map literal where everything is pure - promote the indexed item. // All other items can be thrown away. state.set_dirty(); - *expr = mem::take(&mut m.0).into_iter().find(|(x, _)| &x.name == prop) + *expr = mem::take(&mut m.0).into_iter().find(|(x, _)| x.name == prop) .map(|(_, mut expr)| { expr.set_position(*pos); expr }) .unwrap_or_else(|| Expr::Unit(*pos)); } @@ -723,11 +724,11 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { } // ....lhs.rhs #[cfg(not(feature = "no_object"))] - Expr::Dot(x, _) => { optimize_expr(&mut x.lhs, state, false); optimize_expr(&mut x.rhs, state, _chaining); } + Expr::Dot(x,_, _) => { optimize_expr(&mut x.lhs, state, false); optimize_expr(&mut x.rhs, state, _chaining); } // lhs[rhs] #[cfg(not(feature = "no_index"))] - Expr::Index(x, _) if !_chaining => match (&mut x.lhs, &mut x.rhs) { + Expr::Index(x, _, _) if !_chaining => match (&mut x.lhs, &mut x.rhs) { // array[int] (Expr::Array(a, pos), Expr::IntegerConstant(i, _)) if *i >= 0 && (*i as usize) < a.len() && a.iter().all(Expr::is_pure) => @@ -791,7 +792,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { }, // ...[lhs][rhs] #[cfg(not(feature = "no_index"))] - Expr::Index(x, _) => { optimize_expr(&mut x.lhs, state, false); optimize_expr(&mut x.rhs, state, _chaining); } + Expr::Index(x, _, _) => { optimize_expr(&mut x.lhs, state, false); optimize_expr(&mut x.rhs, state, _chaining); } // `` Expr::InterpolatedString(x, pos) if x.is_empty() => { state.set_dirty(); @@ -944,32 +945,36 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { Expr::FnCall(x, pos) if !x.is_qualified() // Non-qualified && state.optimization_level == OptimizationLevel::Simple // simple optimizations - && x.args.len() == 2 // binary call && x.args.iter().all(Expr::is_constant) // all arguments are constants //&& !is_valid_identifier(x.name.chars()) // cannot be scripted => { - let mut arg_values: StaticVec<_> = x.args.iter().map(|e| match e { - Expr::Stack(slot, _) => x.constants[*slot].clone(), - _ => e.get_literal_value().unwrap() - }).collect(); + let arg_values = &mut x.args.iter().map(|e| match e { + Expr::Stack(slot, _) => x.constants[*slot].clone(), + _ => e.get_literal_value().unwrap() + }).collect::>(); let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect(); - // Search for overloaded operators (can override built-in). - if !has_native_fn(state, x.hashes.native, arg_types.as_ref()) { - if let Some(mut result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1]) - .and_then(|f| { - let ctx = (state.engine, x.name.as_ref(), state.lib).into(); - let (first, second) = arg_values.split_first_mut().unwrap(); - (f)(ctx, &mut [ first, &mut second[0] ]).ok() - }) - .map(Expr::from) - { - state.set_dirty(); - result.set_position(*pos); - *expr = result; - return; + let result = match x.name.as_str() { + KEYWORD_TYPE_OF if arg_values.len() == 1 => Some(state.engine.map_type_name(arg_values[0].type_name()).into()), + #[cfg(not(feature = "no_closure"))] + KEYWORD_IS_SHARED if arg_values.len() == 1 => Some(Dynamic::FALSE), + // Overloaded operators can override built-in. + _ if x.args.len() == 2 && !state.has_native_fn(x.hashes.native, arg_types.as_ref()) => { + get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1]) + .and_then(|f| { + let ctx = (state.engine, x.name.as_ref(), state.lib).into(); + let (first, second) = arg_values.split_first_mut().unwrap(); + (f)(ctx, &mut [ first, &mut second[0] ]).ok() + }) } + _ => None + }; + + if let Some(result) = result { + state.set_dirty(); + *expr = Expr::from_dynamic(result, *pos); + return; } x.args.iter_mut().for_each(|a| optimize_expr(a, state, false)); @@ -997,30 +1002,21 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { let has_script_fn = false; if !has_script_fn { - let mut arg_values: StaticVec<_> = x.args.iter().map(|e| match e { - Expr::Stack(slot, _) => x.constants[*slot].clone(), - _ => e.get_literal_value().unwrap() - }).collect(); + let arg_values = &mut x.args.iter().map(|e| match e { + Expr::Stack(slot, _) => x.constants[*slot].clone(), + _ => e.get_literal_value().unwrap() + }).collect::>(); - // Save the typename of the first argument if it is `type_of()` - // This is to avoid `call_args` being passed into the closure - let arg_for_type_of = if x.name == KEYWORD_TYPE_OF && arg_values.len() == 1 { - state.engine.map_type_name(arg_values[0].type_name()) - } else { - "" + let result = match x.name.as_str() { + KEYWORD_TYPE_OF if arg_values.len() == 1 => Some(state.engine.map_type_name(arg_values[0].type_name()).into()), + #[cfg(not(feature = "no_closure"))] + KEYWORD_IS_SHARED if arg_values.len() == 1 => Some(Dynamic::FALSE), + _ => state.call_fn_with_constant_arguments(x.name.as_ref(), arg_values) }; - if let Some(mut result) = call_fn_with_constant_arguments(&state, x.name.as_ref(), &mut arg_values) - .or_else(|| if !arg_for_type_of.is_empty() { - // Handle `type_of()` - Some(arg_for_type_of.to_string().into()) - } else { - None - }).map(Expr::from) - { + if let Some(result) = result { state.set_dirty(); - result.set_position(*pos); - *expr = result; + *expr = Expr::from_dynamic(result, *pos); return; } } @@ -1043,9 +1039,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { // constant-name Expr::Variable(_, pos, x) if x.1.is_none() && state.find_constant(&x.2).is_some() => { // Replace constant with value - let pos = *pos; - *expr = Expr::from(state.find_constant(&x.2).unwrap().clone()); - expr.set_position(pos); + *expr = Expr::from_dynamic(state.find_constant(&x.2).unwrap().clone(), *pos); state.set_dirty(); } diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 599a7540..3400f1d1 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -155,7 +155,7 @@ mod array_functions { len as usize }; - array[start..start + len].iter().cloned().collect() + array[start..start + len].to_vec() } #[rhai_fn(name = "extract")] pub fn extract_tail(array: &mut Array, start: INT) -> Array { @@ -170,7 +170,7 @@ mod array_functions { start as usize }; - array[start..].iter().cloned().collect() + array[start..].to_vec() } #[rhai_fn(name = "split")] pub fn split_at(array: &mut Array, start: INT) -> Array { diff --git a/src/packages/fn_basic.rs b/src/packages/fn_basic.rs index 76e9d52d..063bd42c 100644 --- a/src/packages/fn_basic.rs +++ b/src/packages/fn_basic.rs @@ -50,14 +50,11 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array { let mut map = Map::new(); if let Some(ns) = namespace { - map.insert(dict.get("namespace").expect(DICT).clone().into(), ns.into()); + map.insert(dict.get("namespace").expect(DICT).clone(), ns.into()); } + map.insert(dict.get("name").expect(DICT).clone(), f.name.clone().into()); map.insert( - dict.get("name").expect(DICT).clone().into(), - f.name.clone().into(), - ); - map.insert( - dict.get("access").expect(DICT).clone().into(), + dict.get("access").expect(DICT).clone(), match f.access { FnAccess::Public => dict.get("public").expect(DICT).clone(), FnAccess::Private => dict.get("private").expect(DICT).clone(), @@ -65,11 +62,11 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array { .into(), ); map.insert( - dict.get("is_anonymous").expect(DICT).clone().into(), + dict.get("is_anonymous").expect(DICT).clone(), f.name.starts_with(crate::engine::FN_ANONYMOUS).into(), ); map.insert( - dict.get("params").expect(DICT).clone().into(), + dict.get("params").expect(DICT).clone(), f.params .iter() .cloned() @@ -78,7 +75,7 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array { .into(), ); - map.into() + map } // Intern strings diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index 835030fe..65fb6484 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -1,5 +1,6 @@ use crate::dynamic::Variant; use crate::{def_package, EvalAltResult, INT}; +use std::iter::{ExactSizeIterator, FusedIterator}; use std::ops::Range; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -53,31 +54,19 @@ where None } else if self.0 < self.1 { #[cfg(not(feature = "unchecked"))] - let diff1 = if let Some(diff) = self.1.checked_sub(&self.0) { - diff - } else { - return None; - }; + let diff1 = self.1.checked_sub(&self.0)?; #[cfg(feature = "unchecked")] let diff1 = self.1 - self.0; let v = self.0; #[cfg(not(feature = "unchecked"))] - let n = if let Some(num) = self.0.checked_add(&self.2) { - num - } else { - return None; - }; + let n = self.0.checked_add(&self.2)?; #[cfg(feature = "unchecked")] let n = self.0 + self.2; #[cfg(not(feature = "unchecked"))] - let diff2 = if let Some(diff) = self.1.checked_sub(&n) { - diff - } else { - return None; - }; + let diff2 = self.1.checked_sub(&n)?; #[cfg(feature = "unchecked")] let diff2 = self.1 - n; @@ -89,31 +78,19 @@ where } } else { #[cfg(not(feature = "unchecked"))] - let diff1 = if let Some(diff) = self.0.checked_sub(&self.1) { - diff - } else { - return None; - }; + let diff1 = self.0.checked_sub(&self.1)?; #[cfg(feature = "unchecked")] let diff1 = self.0 - self.1; let v = self.0; #[cfg(not(feature = "unchecked"))] - let n = if let Some(num) = self.0.checked_add(&self.2) { - num - } else { - return None; - }; + let n = self.0.checked_add(&self.2)?; #[cfg(feature = "unchecked")] let n = self.0 + self.2; #[cfg(not(feature = "unchecked"))] - let diff2 = if let Some(diff) = n.checked_sub(&self.1) { - diff - } else { - return None; - }; + let diff2 = n.checked_sub(&self.1)?; #[cfg(feature = "unchecked")] let diff2 = n - self.1; @@ -127,6 +104,11 @@ where } } +impl FusedIterator for StepRange where + T: Variant + Copy + PartialOrd + Add + Sub +{ +} + // Bit-field iterator with step #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] struct BitRange(INT, INT, usize); @@ -190,6 +172,20 @@ impl Iterator for BitRange { Some(r) } } + + #[inline(always)] + fn size_hint(&self) -> (usize, Option) { + (self.2, Some(self.2)) + } +} + +impl FusedIterator for BitRange {} + +impl ExactSizeIterator for BitRange { + #[inline(always)] + fn len(&self) -> usize { + self.2 + } } // String iterator over characters @@ -248,6 +244,21 @@ impl Iterator for CharsStream { Some(ch) } } + + #[inline(always)] + fn size_hint(&self) -> (usize, Option) { + let remaining = self.0.len() - self.1; + (remaining, Some(remaining)) + } +} + +impl FusedIterator for CharsStream {} + +impl ExactSizeIterator for CharsStream { + #[inline(always)] + fn len(&self) -> usize { + self.0.len() - self.1 + } } macro_rules! reg_range { @@ -356,11 +367,11 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, { } } - impl std::iter::FusedIterator for StepFloatRange {} + impl FusedIterator for StepFloatRange {} lib.set_iterator::(); - let _hash = lib.set_native_fn("range", |from, to, step| StepFloatRange::new(from, to, step)); + let _hash = lib.set_native_fn("range", StepFloatRange::new); #[cfg(feature = "metadata")] lib.update_fn_metadata(_hash, &["from: FLOAT", "to: FLOAT", "step: FLOAT", "Iterator"]); } @@ -418,11 +429,11 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, { } } - impl std::iter::FusedIterator for StepDecimalRange {} + impl FusedIterator for StepDecimalRange {} lib.set_iterator::(); - let _hash = lib.set_native_fn("range", |from, to, step| StepDecimalRange::new(from, to, step)); + let _hash = lib.set_native_fn("range", StepDecimalRange::new); #[cfg(feature = "metadata")] lib.update_fn_metadata(_hash, &["from: Decimal", "to: Decimal", "step: Decimal", "Iterator"]); } @@ -430,7 +441,7 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, { // Register string iterator lib.set_iterator::(); - let _hash = lib.set_native_fn("chars", |string, from,len| Ok(CharsStream::new(string, from, len))); + let _hash = lib.set_native_fn("chars", |string, from, len| Ok(CharsStream::new(string, from, len))); #[cfg(feature = "metadata")] lib.update_fn_metadata(_hash, &["string: &str", "from: INT", "len: INT", "Iterator"]); @@ -445,7 +456,7 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, { // Register bit-field iterator lib.set_iterator::(); - let _hash = lib.set_native_fn("bits", |value, from, len| BitRange::new(value, from, len)); + let _hash = lib.set_native_fn("bits", BitRange::new); #[cfg(feature = "metadata")] lib.update_fn_metadata(_hash, &["value: INT", "from: INT", "len: INT", "Iterator"]); diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index efa02f1e..674a6a2e 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -113,7 +113,7 @@ def_package!(crate:BasicMathPackage:"Basic mathematic functions.", lib, { mod int_functions { #[rhai_fn(name = "parse_int", return_raw)] pub fn parse_int_radix(s: &str, radix: INT) -> Result> { - if radix < 2 || radix > 36 { + if !(2..=36).contains(&radix) { return EvalAltResult::ErrorArithmetic( format!("Invalid radix: '{}'", radix), Position::NONE, diff --git a/src/packages/mod.rs b/src/packages/mod.rs index f3705088..7b89ccdf 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -79,8 +79,13 @@ macro_rules! def_package { } } + impl Default for $package { + fn default() -> Self { + Self::new() + } + } + impl $package { - #[allow(dead_code)] pub fn new() -> Self { let mut module = $root::Module::new(); ::init(&mut module); diff --git a/src/packages/string_basic.rs b/src/packages/string_basic.rs index c361863f..1bb901c8 100644 --- a/src/packages/string_basic.rs +++ b/src/packages/string_basic.rs @@ -12,8 +12,8 @@ use crate::Array; #[cfg(not(feature = "no_object"))] use crate::Map; -pub const FUNC_TO_STRING: &'static str = "to_string"; -pub const FUNC_TO_DEBUG: &'static str = "to_debug"; +pub const FUNC_TO_STRING: &str = "to_string"; +pub const FUNC_TO_DEBUG: &str = "to_debug"; def_package!(crate:BasicStringPackage:"Basic string utilities, including printing.", lib, { combine_with_exported_module!(lib, "print_debug", print_debug_functions); @@ -22,7 +22,7 @@ def_package!(crate:BasicStringPackage:"Basic string utilities, including printin // Register print and debug -#[inline(always)] +#[inline] pub fn print_with_func( fn_name: &str, ctx: &NativeCallContext, @@ -106,7 +106,7 @@ mod print_debug_functions { pub fn format_array(ctx: NativeCallContext, array: &mut Array) -> ImmutableString { let len = array.len(); let mut result = String::with_capacity(len * 5 + 2); - result.push_str("["); + result.push('['); array.iter_mut().enumerate().for_each(|(i, x)| { result.push_str(&print_with_func(FUNC_TO_DEBUG, &ctx, x)); @@ -115,7 +115,7 @@ mod print_debug_functions { } }); - result.push_str("]"); + result.push(']'); result.into() } } @@ -144,7 +144,7 @@ mod print_debug_functions { )); }); - result.push_str("}"); + result.push('}'); result.into() } } diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index 2dc9cfa3..22c9074d 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -38,12 +38,11 @@ mod string_functions { ) -> ImmutableString { let mut s = print_with_func(FUNC_TO_STRING, &ctx, item); - if string.is_empty() { - s - } else { + if !string.is_empty() { s.make_mut().push_str(string); - s.into() } + + s } #[rhai_fn(name = "+", name = "append")] @@ -240,7 +239,7 @@ mod string_functions { let mut chars = StaticVec::with_capacity(string.len()); let offset = if string.is_empty() || len <= 0 { - return ctx.engine().empty_string.clone().into(); + return ctx.engine().empty_string.clone(); } else if start < 0 { if let Some(n) = start.checked_abs() { chars.extend(string.chars()); @@ -253,7 +252,7 @@ mod string_functions { 0 } } else if start as usize >= string.chars().count() { - return ctx.engine().empty_string.clone().into(); + return ctx.engine().empty_string.clone(); } else { start as usize }; diff --git a/src/parse.rs b/src/parse.rs index 9d00409f..073a222f 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -8,7 +8,7 @@ use crate::custom_syntax::{ CustomSyntax, CUSTOM_SYNTAX_MARKER_BLOCK, CUSTOM_SYNTAX_MARKER_BOOL, CUSTOM_SYNTAX_MARKER_EXPR, CUSTOM_SYNTAX_MARKER_IDENT, CUSTOM_SYNTAX_MARKER_INT, CUSTOM_SYNTAX_MARKER_STRING, }; -use crate::dynamic::{AccessMode, Union}; +use crate::dynamic::AccessMode; use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS}; use crate::fn_hash::get_hasher; use crate::module::NamespaceRef; @@ -17,8 +17,8 @@ use crate::token::{ is_keyword_function, is_valid_identifier, Token, TokenStream, TokenizerControl, }; use crate::{ - calc_fn_hash, calc_qualified_fn_hash, calc_qualified_var_hash, Dynamic, Engine, Identifier, - LexError, ParseError, ParseErrorType, Position, Scope, Shared, StaticVec, AST, + calc_fn_hash, calc_qualified_fn_hash, calc_qualified_var_hash, Engine, Identifier, LexError, + ParseError, ParseErrorType, Position, Scope, Shared, StaticVec, AST, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -59,7 +59,7 @@ pub struct IdentifierBuilder( impl IdentifierBuilder { /// Get an identifier from a text string. - #[inline(always)] + #[inline] #[must_use] pub fn get(&mut self, text: impl AsRef + Into) -> Identifier { #[cfg(not(feature = "no_smartstring"))] @@ -141,7 +141,7 @@ impl<'e> ParseState<'e> { /// i.e. the top element of [`ParseState`]'s variables stack is offset 1. /// /// Return `None` when the variable name is not found in the `stack`. - #[inline(always)] + #[inline] pub fn access_var(&mut self, name: &str, _pos: Position) -> Option { let mut barrier = false; @@ -242,8 +242,7 @@ impl ParseSettings { } /// Make sure that the current level of expression nesting is within the maximum limit. #[cfg(not(feature = "unchecked"))] - #[inline(always)] - #[must_use] + #[inline] pub fn ensure_level_within_max_limit( &self, limit: Option, @@ -261,7 +260,7 @@ impl Expr { /// Convert a [`Variable`][Expr::Variable] into a [`Property`][Expr::Property]. /// All other variants are untouched. #[cfg(not(feature = "no_object"))] - #[inline(always)] + #[inline] #[must_use] fn into_property(self, state: &mut ParseState) -> Self { match self { @@ -412,7 +411,7 @@ fn parse_paren_expr( // ( xxx ) (Token::RightParen, _) => Ok(expr), // ( - (Token::LexError(err), pos) => return Err(err.into_err(pos)), + (Token::LexError(err), pos) => Err(err.into_err(pos)), // ( xxx ??? (_, pos) => Err(PERR::MissingToken( Token::RightParen.into(), @@ -700,17 +699,19 @@ fn parse_index_chain( // Indexing binds to right Ok(Expr::Index( BinaryExpr { lhs, rhs: idx_expr }.into(), + false, prev_pos, )) } // Otherwise terminate the indexing chain _ => Ok(Expr::Index( BinaryExpr { lhs, rhs: idx_expr }.into(), + true, settings.pos, )), } } - (Token::LexError(err), pos) => return Err(err.clone().into_err(*pos)), + (Token::LexError(err), pos) => Err(err.clone().into_err(*pos)), (_, pos) => Err(PERR::MissingToken( Token::RightBracket.into(), "for a matching [ in this index expression".into(), @@ -806,7 +807,7 @@ fn parse_map_literal( settings.pos = eat_token(input, Token::MapStart); let mut map: StaticVec<(Ident, Expr)> = Default::default(); - let mut template: BTreeMap = Default::default(); + let mut template: BTreeMap = Default::default(); loop { const MISSING_RBRACE: &str = "to end this object map literal"; @@ -878,7 +879,7 @@ fn parse_map_literal( let expr = parse_expr(input, state, lib, settings.level_up())?; let name = state.get_identifier(name); - template.insert(name.clone().into(), Default::default()); + template.insert(name.clone(), Default::default()); map.push((Ident { name, pos }, expr)); match input.peek().expect(NEVER_ENDS) { @@ -1085,7 +1086,7 @@ fn parse_primary( }, #[cfg(not(feature = "no_float"))] Token::FloatConstant(x) => { - let x = (*x).into(); + let x = *x; input.next().expect(NEVER_ENDS); Expr::FloatConstant(x, settings.pos) } @@ -1396,7 +1397,7 @@ fn parse_primary( let rhs = parse_primary(input, state, lib, settings.level_up())?; - make_dot_expr(state, expr, rhs, tail_pos)? + make_dot_expr(state, expr, false, rhs, tail_pos)? } // Unknown postfix operator (expr, token) => unreachable!( @@ -1408,23 +1409,26 @@ fn parse_primary( } // Cache the hash key for namespace-qualified variables - match root_expr { + let namespaced_variable = match root_expr { Expr::Variable(_, _, ref mut x) if x.1.is_some() => Some(x.as_mut()), - Expr::Index(ref mut x, _) | Expr::Dot(ref mut x, _) => match x.lhs { + Expr::Index(ref mut x, _, _) | Expr::Dot(ref mut x, _, _) => match x.lhs { Expr::Variable(_, _, ref mut x) if x.1.is_some() => Some(x.as_mut()), _ => None, }, _ => None, - } - .map(|x| match x { - (_, Some((namespace, hash)), name) => { - *hash = calc_qualified_var_hash(namespace.iter().map(|v| v.name.as_str()), name); + }; - #[cfg(not(feature = "no_module"))] - namespace.set_index(state.find_module(&namespace[0].name)); + if let Some(x) = namespaced_variable { + match x { + (_, Some((namespace, hash)), name) => { + *hash = calc_qualified_var_hash(namespace.iter().map(|v| v.name.as_str()), name); + + #[cfg(not(feature = "no_module"))] + namespace.set_index(state.find_module(&namespace[0].name)); + } + _ => unreachable!("expecting namespace-qualified variable access"), } - _ => unreachable!("expecting namespace-qualified variable access"), - }); + } // Make sure identifiers are valid Ok(root_expr) @@ -1529,7 +1533,7 @@ fn parse_unary( } /// Make an assignment statement. -fn make_assignment_stmt<'a>( +fn make_assignment_stmt( op: Option, state: &mut ParseState, lhs: Expr, @@ -1539,13 +1543,13 @@ fn make_assignment_stmt<'a>( #[must_use] fn check_lvalue(expr: &Expr, parent_is_dot: bool) -> Option { match expr { - Expr::Index(x, _) | Expr::Dot(x, _) if parent_is_dot => match x.lhs { - Expr::Property(_) => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _))), + Expr::Index(x, _, _) | Expr::Dot(x, _, _) if parent_is_dot => match x.lhs { + Expr::Property(_) => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _, _))), ref e => Some(e.position()), }, - Expr::Index(x, _) | Expr::Dot(x, _) => match x.lhs { + Expr::Index(x, _, _) | Expr::Dot(x, _, _) => match x.lhs { Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"), - _ => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _))), + _ => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _, _))), }, Expr::Property(_) if parent_is_dot => None, Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"), @@ -1554,7 +1558,7 @@ fn make_assignment_stmt<'a>( } } - let op_info = op.map(|v| OpAssignment::new(v)); + let op_info = op.map(OpAssignment::new); match lhs { // const_expr = rhs @@ -1587,8 +1591,8 @@ fn make_assignment_stmt<'a>( } } // xxx[???]... = rhs, xxx.prop... = rhs - Expr::Index(ref x, _) | Expr::Dot(ref x, _) => { - match check_lvalue(&x.rhs, matches!(lhs, Expr::Dot(_, _))) { + Expr::Index(ref x, _, _) | Expr::Dot(ref x, _, _) => { + match check_lvalue(&x.rhs, matches!(lhs, Expr::Dot(_, _, _))) { None => match x.lhs { // var[???] = rhs, var.??? = rhs Expr::Variable(_, _, _) => { @@ -1645,41 +1649,38 @@ fn parse_op_assignment_stmt( fn make_dot_expr( state: &mut ParseState, lhs: Expr, + terminate_chaining: bool, rhs: Expr, op_pos: Position, ) -> Result { Ok(match (lhs, rhs) { - // idx_lhs[idx_expr].rhs - // Attach dot chain to the bottom level of indexing chain - (Expr::Index(mut x, pos), rhs) => { - x.rhs = make_dot_expr(state, x.rhs, rhs, op_pos)?; - Expr::Index(x, pos) + // lhs[???]...[???].rhs + (Expr::Index(mut x, false, pos), rhs) if !terminate_chaining => { + // Attach dot chain to the bottom level of indexing chain + x.rhs = make_dot_expr(state, x.rhs, false, rhs, op_pos)?; + Expr::Index(x, false, pos) + } + // lhs[idx_expr].rhs + (Expr::Index(mut x, _, pos), rhs) => { + x.rhs = make_dot_expr(state, x.rhs, true, rhs, op_pos)?; + Expr::Index(x, false, pos) } // lhs.id - (lhs, Expr::Variable(_, var_pos, x)) if x.1.is_none() => { - let ident = x.2; - let getter = state.get_identifier(crate::engine::make_getter(&ident)); - let hash_get = calc_fn_hash(&getter, 1); - let setter = state.get_identifier(crate::engine::make_setter(&ident)); - let hash_set = calc_fn_hash(&setter, 2); - - let rhs = Expr::Property(Box::new(( - (getter, hash_get), - (setter, hash_set), - (state.get_identifier(ident).into(), var_pos), - ))); - - Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos) + (lhs, var_expr @ Expr::Variable(_, _, _)) if var_expr.is_variable_access(true) => { + let rhs = var_expr.into_property(state); + Expr::Dot(BinaryExpr { lhs, rhs }.into(), false, op_pos) } // lhs.module::id - syntax error - (_, Expr::Variable(_, _, x)) if x.1.is_some() => { + (_, Expr::Variable(_, _, x)) => { return Err(PERR::PropertyExpected .into_err(x.1.expect("never fails because the namespace is `Some`").0[0].pos)) } // lhs.prop - (lhs, prop @ Expr::Property(_)) => Expr::Dot(BinaryExpr { lhs, rhs: prop }.into(), op_pos), + (lhs, prop @ Expr::Property(_)) => { + Expr::Dot(BinaryExpr { lhs, rhs: prop }.into(), false, op_pos) + } // lhs.dot_lhs.dot_rhs - (lhs, Expr::Dot(x, pos)) => match x.lhs { + (lhs, Expr::Dot(x, _, pos)) => match x.lhs { Expr::Variable(_, _, _) | Expr::Property(_) => { let rhs = Expr::Dot( BinaryExpr { @@ -1687,9 +1688,10 @@ fn make_dot_expr( rhs: x.rhs, } .into(), + false, pos, ); - Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos) + Expr::Dot(BinaryExpr { lhs, rhs }.into(), false, op_pos) } Expr::FnCall(mut func, func_pos) => { // Recalculate hash @@ -1704,23 +1706,25 @@ fn make_dot_expr( rhs: x.rhs, } .into(), + false, pos, ); - Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos) + Expr::Dot(BinaryExpr { lhs, rhs }.into(), false, op_pos) } _ => unreachable!("invalid dot expression: {:?}", x.lhs), }, // lhs.idx_lhs[idx_rhs] - (lhs, Expr::Index(x, pos)) => { + (lhs, Expr::Index(x, term, pos)) => { let rhs = Expr::Index( BinaryExpr { lhs: x.lhs.into_property(state), rhs: x.rhs, } .into(), + term, pos, ); - Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos) + Expr::Dot(BinaryExpr { lhs, rhs }.into(), false, op_pos) } // lhs.nnn::func(...) (_, Expr::FnCall(x, _)) if x.is_qualified() => { @@ -1756,7 +1760,7 @@ fn make_dot_expr( calc_fn_hash(&func.name, func.args.len() + 1), ); let rhs = Expr::FnCall(func, func_pos); - Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos) + Expr::Dot(BinaryExpr { lhs, rhs }.into(), false, op_pos) } // lhs.rhs (_, rhs) => return Err(PERR::PropertyExpected.into_err(rhs.position())), @@ -2715,7 +2719,7 @@ fn parse_stmt( is_function_scope: true, is_breakable: false, level: 0, - pos: pos, + pos, }; let func = parse_fn( @@ -3033,40 +3037,38 @@ fn parse_anon_fn( let mut params_list: StaticVec<_> = Default::default(); - if input.next().expect(NEVER_ENDS).0 != Token::Or { - if !match_token(input, Token::Pipe).0 { - loop { - match input.next().expect(NEVER_ENDS) { - (Token::Pipe, _) => break, - (Token::Identifier(s), pos) => { - if params_list.iter().any(|p| p == &s) { - return Err(PERR::FnDuplicatedParam("".to_string(), s).into_err(pos)); - } - let s = state.get_identifier(s); - state.stack.push((s.clone(), AccessMode::ReadWrite)); - params_list.push(s) - } - (Token::LexError(err), pos) => return Err(err.into_err(pos)), - (_, pos) => { - return Err(PERR::MissingToken( - Token::Pipe.into(), - "to close the parameters list of anonymous function".into(), - ) - .into_err(pos)) + if input.next().expect(NEVER_ENDS).0 != Token::Or && !match_token(input, Token::Pipe).0 { + loop { + match input.next().expect(NEVER_ENDS) { + (Token::Pipe, _) => break, + (Token::Identifier(s), pos) => { + if params_list.iter().any(|p| p == &s) { + return Err(PERR::FnDuplicatedParam("".to_string(), s).into_err(pos)); } + let s = state.get_identifier(s); + state.stack.push((s.clone(), AccessMode::ReadWrite)); + params_list.push(s) } + (Token::LexError(err), pos) => return Err(err.into_err(pos)), + (_, pos) => { + return Err(PERR::MissingToken( + Token::Pipe.into(), + "to close the parameters list of anonymous function".into(), + ) + .into_err(pos)) + } + } - match input.next().expect(NEVER_ENDS) { - (Token::Pipe, _) => break, - (Token::Comma, _) => (), - (Token::LexError(err), pos) => return Err(err.into_err(pos)), - (_, pos) => { - return Err(PERR::MissingToken( - Token::Comma.into(), - "to separate the parameters of anonymous function".into(), - ) - .into_err(pos)) - } + match input.next().expect(NEVER_ENDS) { + (Token::Pipe, _) => break, + (Token::Comma, _) => (), + (Token::LexError(err), pos) => return Err(err.into_err(pos)), + (_, pos) => { + return Err(PERR::MissingToken( + Token::Comma.into(), + "to separate the parameters of anonymous function".into(), + ) + .into_err(pos)) } } } @@ -3119,7 +3121,7 @@ fn parse_anon_fn( comments: Default::default(), }; - let fn_ptr = crate::FnPtr::new_unchecked(fn_name.into(), Default::default()); + let fn_ptr = crate::FnPtr::new_unchecked(fn_name, Default::default()); let expr = Expr::DynamicConstant(Box::new(fn_ptr.into()), settings.pos); #[cfg(not(feature = "no_closure"))] @@ -3130,7 +3132,6 @@ fn parse_anon_fn( impl Engine { /// Parse a global level expression. - #[must_use] pub(crate) fn parse_global_expr( &self, input: &mut TokenStream, @@ -3172,7 +3173,6 @@ impl Engine { } /// Parse the global level statements. - #[must_use] fn parse_global_level( &self, input: &mut TokenStream, @@ -3234,7 +3234,6 @@ impl Engine { /// Run the parser on an input stream, returning an AST. #[inline(always)] - #[must_use] pub(crate) fn parse( &self, input: &mut TokenStream, @@ -3250,33 +3249,3 @@ impl Engine { ) } } - -impl From for Expr { - fn from(value: Dynamic) -> Self { - match value.0 { - #[cfg(not(feature = "no_float"))] - Union::Float(value, _, _) => Self::FloatConstant(value, Position::NONE), - - #[cfg(feature = "decimal")] - Union::Decimal(value, _, _) => { - Self::DynamicConstant(Box::new((*value).into()), Position::NONE) - } - - Union::Unit(_, _, _) => Self::Unit(Position::NONE), - Union::Int(value, _, _) => Self::IntegerConstant(value, Position::NONE), - Union::Char(value, _, _) => Self::CharConstant(value, Position::NONE), - Union::Str(value, _, _) => Self::StringConstant(value, Position::NONE), - Union::Bool(value, _, _) => Self::BoolConstant(value, Position::NONE), - - #[cfg(not(feature = "no_index"))] - Union::Array(array, _, _) => { - Self::DynamicConstant(Box::new((*array).into()), Position::NONE) - } - - #[cfg(not(feature = "no_object"))] - Union::Map(map, _, _) => Self::DynamicConstant(Box::new((*map).into()), Position::NONE), - - _ => Self::DynamicConstant(Box::new(value.into()), Position::NONE), - } - } -} diff --git a/src/plugin.rs b/src/plugin.rs index 24bf9e3a..779f3fd7 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -22,7 +22,6 @@ pub use rhai_codegen::{export_fn, register_exported_fn}; /// Use the `#[export_module]` and `#[export_fn]` procedural attributes instead. pub trait PluginFunction { /// Call the plugin function with the arguments provided. - #[must_use] fn call(&self, context: NativeCallContext, args: &mut FnCallArgs) -> RhaiResult; /// Is this plugin function a method? diff --git a/src/scope.rs b/src/scope.rs index 9fa87088..bd99207a 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -49,7 +49,7 @@ const SCOPE_ENTRIES_INLINED: usize = 8; // look up a variable. Variable lookup is usually via direct indexing, by-passing the name altogether. // // Since [`Dynamic`] is reasonably small, packing it tightly improves cache locality when variables are accessed. -#[derive(Debug, Clone, Hash)] +#[derive(Debug, Clone, Hash, Default)] pub struct Scope<'a> { /// Current value of the entry. values: smallvec::SmallVec<[Dynamic; SCOPE_ENTRIES_INLINED]>, @@ -59,13 +59,6 @@ pub struct Scope<'a> { >, } -impl Default for Scope<'_> { - #[inline(always)] - fn default() -> Self { - Self::new() - } -} - impl<'a> IntoIterator for Scope<'a> { type Item = (Cow<'a, str>, Dynamic); type IntoIter = Box + 'a>; @@ -97,10 +90,7 @@ impl<'a> Scope<'a> { #[inline(always)] #[must_use] pub fn new() -> Self { - Self { - values: Default::default(), - names: Default::default(), - } + Default::default() } /// Empty the [`Scope`]. /// @@ -246,7 +236,7 @@ impl<'a> Scope<'a> { self.push_dynamic_value(name, AccessMode::ReadOnly, value) } /// Add (push) a new entry with a [`Dynamic`] value to the [`Scope`]. - #[inline(always)] + #[inline] pub(crate) fn push_dynamic_value( &mut self, name: impl Into>, @@ -255,7 +245,7 @@ impl<'a> Scope<'a> { ) -> &mut Self { self.names.push((name.into(), Default::default())); value.set_access_mode(access); - self.values.push(value.into()); + self.values.push(value); self } /// Truncate (rewind) the [`Scope`] to a previous size. @@ -371,7 +361,7 @@ impl<'a> Scope<'a> { /// my_scope.set_value("x", 0_i64); /// assert_eq!(my_scope.get_value::("x").unwrap(), 0); /// ``` - #[inline(always)] + #[inline] pub fn set_value(&mut self, name: &'a str, value: impl Variant + Clone) -> &mut Self { match self.get_index(name) { None => { @@ -408,6 +398,7 @@ impl<'a> Scope<'a> { /// /// assert_eq!(my_scope.get_value::("x").unwrap(), 123); /// ``` + #[inline(always)] #[must_use] pub fn get_mut(&mut self, name: &str) -> Option<&mut Dynamic> { self.get_index(name) @@ -434,7 +425,7 @@ impl<'a> Scope<'a> { /// /// Panics if the index is out of bounds. #[cfg(not(feature = "no_module"))] - #[inline(always)] + #[inline] pub(crate) fn add_entry_alias(&mut self, index: usize, alias: Identifier) -> &mut Self { let (_, aliases) = self .names @@ -456,7 +447,7 @@ impl<'a> Scope<'a> { #[inline(always)] #[must_use] pub(crate) fn clone_visible(&self) -> Self { - let mut entries: Self = Default::default(); + let mut entries = Self::new(); self.names .iter() @@ -474,7 +465,6 @@ impl<'a> Scope<'a> { /// Get an iterator to entries in the [`Scope`]. #[inline(always)] #[allow(dead_code)] - #[must_use] pub(crate) fn into_iter( self, ) -> impl Iterator, Dynamic, Vec)> { @@ -511,7 +501,6 @@ impl<'a> Scope<'a> { /// assert_eq!(value.cast::(), "hello"); /// ``` #[inline(always)] - #[must_use] pub fn iter(&self) -> impl Iterator { self.iter_raw() .map(|(name, constant, value)| (name, constant, value.flatten_clone())) @@ -519,7 +508,6 @@ impl<'a> Scope<'a> { /// Get an iterator to entries in the [`Scope`]. /// Shared values are not expanded. #[inline(always)] - #[must_use] pub fn iter_raw(&self) -> impl Iterator { self.names .iter() diff --git a/src/serde/serialize.rs b/src/serde/serialize.rs index 1bf93383..560d489d 100644 --- a/src/serde/serialize.rs +++ b/src/serde/serialize.rs @@ -1,6 +1,6 @@ //! Implementations of [`serde::Serialize`]. -use crate::dynamic::{Union, Variant}; +use crate::dynamic::Union; use crate::{Dynamic, ImmutableString}; use serde::ser::{Serialize, Serializer}; #[cfg(feature = "no_std")] @@ -9,6 +9,9 @@ use std::prelude::v1::*; #[cfg(not(feature = "no_object"))] use serde::ser::SerializeMap; +#[cfg(not(feature = "no_std"))] +use crate::dynamic::Variant; + impl Serialize for Dynamic { fn serialize(&self, ser: S) -> Result { match self.0 { diff --git a/src/token.rs b/src/token.rs index 86ad18fc..4dcaec9a 100644 --- a/src/token.rs +++ b/src/token.rs @@ -83,53 +83,73 @@ impl Position { /// Create a new [`Position`]. /// /// `line` must not be zero. - /// If [`Position`] is zero, then it is at the beginning of a line. + /// + /// If `position` is zero, then it is at the beginning of a line. /// /// # Panics /// /// Panics if `line` is zero. #[inline(always)] #[must_use] - pub fn new(line: u16, _position: u16) -> Self { + pub fn new(line: u16, position: u16) -> Self { assert!(line != 0, "line cannot be zero"); + let _pos = position; + Self { #[cfg(not(feature = "no_position"))] line, #[cfg(not(feature = "no_position"))] - pos: _position, + pos: _pos, } } + /// Create a new [`Position`]. + /// + /// If `line` is zero, then [`None`] is returned. + /// + /// If `position` is zero, then it is at the beginning of a line. + #[inline(always)] + #[must_use] + pub const fn new_const(line: u16, position: u16) -> Option { + if line == 0 { + return None; + } + let _pos = position; + + Some(Self { + #[cfg(not(feature = "no_position"))] + line, + #[cfg(not(feature = "no_position"))] + pos: _pos, + }) + } /// Get the line number (1-based), or [`None`] if there is no position. #[inline(always)] #[must_use] - pub fn line(self) -> Option { - if self.is_none() { + pub const fn line(self) -> Option { + #[cfg(not(feature = "no_position"))] + return if self.is_none() { None } else { - #[cfg(not(feature = "no_position"))] - return Some(self.line as usize); - #[cfg(feature = "no_position")] - unreachable!("there is no Position"); - } + Some(self.line as usize) + }; + + #[cfg(feature = "no_position")] + return None; } /// Get the character position (1-based), or [`None`] if at beginning of a line. #[inline(always)] #[must_use] - pub fn position(self) -> Option { - if self.is_none() { + pub const fn position(self) -> Option { + #[cfg(not(feature = "no_position"))] + return if self.is_none() || self.pos == 0 { None } else { - #[cfg(not(feature = "no_position"))] - return if self.pos == 0 { - None - } else { - Some(self.pos as usize) - }; + Some(self.pos as usize) + }; - #[cfg(feature = "no_position")] - unreachable!("there is no Position"); - } + #[cfg(feature = "no_position")] + return None; } /// Advance by one character position. #[inline(always)] @@ -192,7 +212,6 @@ impl Position { } /// Print this [`Position`] for debug purposes. #[inline(always)] - #[must_use] pub(crate) fn debug_print(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { #[cfg(not(feature = "no_position"))] if !self.is_none() { @@ -211,7 +230,6 @@ impl Default for Position { } impl fmt::Display for Position { - #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.is_none() { write!(f, "none")?; @@ -227,7 +245,6 @@ impl fmt::Display for Position { } impl fmt::Debug for Position { - #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[cfg(not(feature = "no_position"))] write!(f, "{}:{}", self.line, self.pos)?; @@ -586,20 +603,20 @@ impl Token { #[inline] #[must_use] pub const fn is_op_assignment(&self) -> bool { - match self { + matches!( + self, Self::PlusAssign - | Self::MinusAssign - | Self::MultiplyAssign - | Self::DivideAssign - | Self::LeftShiftAssign - | Self::RightShiftAssign - | Self::ModuloAssign - | Self::PowerOfAssign - | Self::AndAssign - | Self::OrAssign - | Self::XOrAssign => true, - _ => false, - } + | Self::MinusAssign + | Self::MultiplyAssign + | Self::DivideAssign + | Self::LeftShiftAssign + | Self::RightShiftAssign + | Self::ModuloAssign + | Self::PowerOfAssign + | Self::AndAssign + | Self::OrAssign + | Self::XOrAssign + ) } /// Get the corresponding operator of the token if it is an op-assignment operator. @@ -625,20 +642,20 @@ impl Token { #[inline] #[must_use] pub const fn has_op_assignment(&self) -> bool { - match self { + matches!( + self, Self::Plus - | Self::Minus - | Self::Multiply - | Self::Divide - | Self::LeftShift - | Self::RightShift - | Self::Modulo - | Self::PowerOf - | Self::Ampersand - | Self::Pipe - | Self::XOr => true, - _ => false, - } + | Self::Minus + | Self::Multiply + | Self::Divide + | Self::LeftShift + | Self::RightShift + | Self::Modulo + | Self::PowerOf + | Self::Ampersand + | Self::Pipe + | Self::XOr + ) } /// Get the corresponding op-assignment operator of the token. @@ -772,12 +789,7 @@ impl Token { #[inline(always)] #[must_use] pub const fn is_eof(&self) -> bool { - use Token::*; - - match self { - EOF => true, - _ => false, - } + matches!(self, Self::EOF) } // If another operator is after these, it's probably an unary operator @@ -917,6 +929,7 @@ impl Token { } /// Is this token an active standard keyword? + #[inline] #[must_use] pub const fn is_keyword(&self) -> bool { use Token::*; @@ -939,15 +952,12 @@ impl Token { #[inline(always)] #[must_use] pub const fn is_reserved(&self) -> bool { - match self { - Self::Reserved(_) => true, - _ => false, - } + matches!(self, Self::Reserved(_)) } /// Convert a token into a function name, if possible. #[cfg(not(feature = "no_function"))] - #[must_use] + #[inline] pub(crate) fn into_function_name_for_override(self) -> Result { match self { Self::Custom(s) | Self::Identifier(s) if is_valid_identifier(s.chars()) => Ok(s), @@ -959,10 +969,7 @@ impl Token { #[inline(always)] #[must_use] pub const fn is_custom(&self) -> bool { - match self { - Self::Custom(_) => true, - _ => false, - } + matches!(self, Self::Custom(_)) } } @@ -1045,7 +1052,6 @@ pub trait InputStream { /// # Volatile API /// /// This function is volatile and may change. -#[must_use] pub fn parse_string_literal( stream: &mut impl InputStream, state: &mut TokenizeState, @@ -1269,20 +1275,26 @@ fn scan_block_comment( while let Some(c) = stream.get_next() { pos.advance(); - comment.as_mut().map(|comment| comment.push(c)); + if let Some(comment) = comment.as_mut() { + comment.push(c); + } match c { '/' => { stream.peek_next().filter(|&c2| c2 == '*').map(|c2| { eat_next(stream, pos); - comment.as_mut().map(|comment| comment.push(c2)); + if let Some(comment) = comment.as_mut() { + comment.push(c2); + } level += 1; }); } '*' => { stream.peek_next().filter(|&c2| c2 == '/').map(|c2| { eat_next(stream, pos); - comment.as_mut().map(|comment| comment.push(c2)); + if let Some(comment) = comment.as_mut() { + comment.push(c2); + } level -= 1; }); } @@ -1304,7 +1316,7 @@ fn scan_block_comment( /// # Volatile API /// /// This function is volatile and may change. -#[inline(always)] +#[inline] #[must_use] pub fn get_next_token( stream: &mut impl InputStream, @@ -1324,21 +1336,13 @@ pub fn get_next_token( /// Test if the given character is a hex character. #[inline(always)] fn is_hex_digit(c: char) -> bool { - match c { - 'a'..='f' => true, - 'A'..='F' => true, - '0'..='9' => true, - _ => false, - } + matches!(c, 'a'..='f' | 'A'..='F' | '0'..='9') } /// Test if the given character is a numeric digit. #[inline(always)] fn is_numeric_digit(c: char) -> bool { - match c { - '0'..='9' => true, - _ => false, - } + matches!(c, '0'..='9') } /// Test if the comment block is a doc-comment. @@ -1455,7 +1459,7 @@ fn get_next_token_inner( break; } // symbol after period - probably a float - ch @ _ if !is_id_first_alphabetic(ch) => { + ch if !is_id_first_alphabetic(ch) => { result.push(next_char); pos.advance(); result.push('0'); @@ -1750,7 +1754,9 @@ fn get_next_token_inner( pos.new_line(); break; } - comment.as_mut().map(|comment| comment.push(c)); + if let Some(comment) = comment.as_mut() { + comment.push(c); + } pos.advance(); } @@ -1982,11 +1988,11 @@ fn get_identifier( )); } - return Some((Token::Identifier(identifier), start_pos)); + Some((Token::Identifier(identifier), start_pos)) } /// Is this keyword allowed as a function? -#[inline(always)] +#[inline] #[must_use] pub fn is_keyword_function(name: &str) -> bool { match name { @@ -2062,7 +2068,7 @@ pub struct MultiInputsStream<'a> { } impl InputStream for MultiInputsStream<'_> { - #[inline(always)] + #[inline] fn unget(&mut self, ch: char) { if self.buf.is_some() { panic!("cannot unget two characters in a row"); @@ -2252,7 +2258,7 @@ impl Engine { self.lex_raw(input, Some(map)) } /// Tokenize an input text stream with an optional mapping function. - #[inline(always)] + #[inline] #[must_use] pub(crate) fn lex_raw<'a>( &'a self, diff --git a/src/unsafe.rs b/src/unsafe.rs index a40d9819..a6fe76f8 100644 --- a/src/unsafe.rs +++ b/src/unsafe.rs @@ -9,7 +9,6 @@ use std::{ /// Cast a type into another type. #[inline(always)] -#[must_use] pub fn unsafe_try_cast(a: A) -> Result { if TypeId::of::() == a.type_id() { // SAFETY: Just checked we have the right type. We explicitly forget the @@ -27,7 +26,6 @@ pub fn unsafe_try_cast(a: A) -> Result { /// Cast a Boxed type into another type. #[inline(always)] -#[must_use] pub fn unsafe_cast_box(item: Box) -> Result, Box> { // Only allow casting to the exact same type if TypeId::of::() == TypeId::of::() { diff --git a/tests/arrays.rs b/tests/arrays.rs index eb17d0a9..486bfbe8 100644 --- a/tests/arrays.rs +++ b/tests/arrays.rs @@ -53,6 +53,18 @@ fn test_arrays() -> Result<(), Box> { convert_to_vec::(engine.eval("let y = [1, 2, 3]; y.insert(-999, 4); y")?), [4, 1, 2, 3] ); + assert_eq!( + engine.eval::("let y = [1, 2, 3]; let z = [42]; y[z.len]")?, + 2 + ); + assert_eq!( + engine.eval::("let y = [1, 2, [3, 4, 5, 6]]; let z = [42]; y[2][z.len]")?, + 4 + ); + assert_eq!( + engine.eval::("let y = [1, 2, 3]; let z = [2]; y[z[0]]")?, + 3 + ); assert_eq!( convert_to_vec::(engine.eval(