diff --git a/src/ast.rs b/src/ast.rs index 0a75dd9d..4414464e 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,10 +1,10 @@ //! Module defining the AST (abstract syntax tree). +use crate::calc_fn_hash; use crate::dynamic::Union; use crate::fn_native::shared_make_mut; use crate::module::NamespaceRef; use crate::token::Token; -use crate::utils::calc_fn_hash; use crate::{ Dynamic, FnNamespace, Identifier, ImmutableString, Module, Position, Shared, StaticVec, INT, }; @@ -216,6 +216,7 @@ impl Default for AST { impl AST { /// Create a new [`AST`]. #[inline(always)] + #[must_use] pub fn new( statements: impl IntoIterator, functions: impl Into>, @@ -230,6 +231,7 @@ impl AST { } /// Create a new [`AST`] with a source name. #[inline(always)] + #[must_use] pub fn new_with_source( statements: impl IntoIterator, functions: impl Into>, @@ -245,11 +247,13 @@ impl AST { } /// Get the source, if any. #[inline(always)] + #[must_use] pub fn source(&self) -> Option<&str> { self.source.as_ref().map(|s| s.as_str()) } /// Clone the source, if any. #[inline(always)] + #[must_use] pub(crate) fn clone_source(&self) -> Option { self.source.clone() } @@ -272,6 +276,7 @@ impl AST { /// Get the statements. #[cfg(not(feature = "internals"))] #[inline(always)] + #[must_use] pub(crate) fn statements(&self) -> &[Stmt] { &self.body.0 } @@ -280,12 +285,14 @@ impl AST { #[cfg(feature = "internals")] #[deprecated = "this method is volatile and may change"] #[inline(always)] + #[must_use] pub fn statements(&self) -> &[Stmt] { &self.body.0 } /// Get a mutable reference to the statements. #[cfg(not(feature = "no_optimize"))] #[inline(always)] + #[must_use] pub(crate) fn statements_mut(&mut self) -> &mut StaticVec { &mut self.body.0 } @@ -294,6 +301,7 @@ impl AST { #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_function"))] #[inline(always)] + #[must_use] pub(crate) fn shared_lib(&self) -> Shared { self.functions.clone() } @@ -306,12 +314,14 @@ impl AST { #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_function"))] #[inline(always)] + #[must_use] pub fn shared_lib(&self) -> Shared { self.functions.clone() } /// Get the internal [`Module`] containing all script-defined functions. #[cfg(not(feature = "internals"))] #[inline(always)] + #[must_use] pub(crate) fn lib(&self) -> &Module { &self.functions } @@ -322,6 +332,7 @@ impl AST { #[cfg(feature = "internals")] #[deprecated = "this method is volatile and may change"] #[inline(always)] + #[must_use] pub fn lib(&self) -> &Module { &self.functions } @@ -329,6 +340,7 @@ impl AST { #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "internals"))] #[inline(always)] + #[must_use] pub(crate) fn resolver( &self, ) -> Option> { @@ -341,6 +353,7 @@ impl AST { #[cfg(not(feature = "no_module"))] #[cfg(feature = "internals")] #[inline(always)] + #[must_use] pub fn resolver(&self) -> Option> { self.resolver.clone() } @@ -362,6 +375,7 @@ impl AST { /// This operation is cheap because functions are shared. #[cfg(not(feature = "no_function"))] #[inline(always)] + #[must_use] pub fn clone_functions_only(&self) -> Self { self.clone_functions_only_filtered(|_, _, _, _, _| true) } @@ -373,6 +387,7 @@ impl AST { /// This operation is cheap because functions are shared. #[cfg(not(feature = "no_function"))] #[inline(always)] + #[must_use] pub fn clone_functions_only_filtered( &self, filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool, @@ -390,6 +405,7 @@ impl AST { /// Clone the [`AST`]'s script statements into a new [`AST`]. /// No functions are cloned. #[inline(always)] + #[must_use] pub fn clone_statements_only(&self) -> Self { Self { source: self.source.clone(), @@ -449,6 +465,7 @@ impl AST { /// # } /// ``` #[inline(always)] + #[must_use] pub fn merge(&self, other: &Self) -> Self { self.merge_filtered(other, |_, _, _, _, _| true) } @@ -557,6 +574,7 @@ impl AST { /// # } /// ``` #[inline] + #[must_use] pub fn merge_filtered( &self, other: &Self, @@ -694,6 +712,7 @@ 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() @@ -704,6 +723,7 @@ 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() @@ -714,13 +734,15 @@ impl AST { /// Not available under `no_function`. #[cfg(not(feature = "no_function"))] #[inline(always)] - pub fn clear_functions(&mut self) { + pub fn clear_functions(&mut self) -> &mut Self { self.functions = Default::default(); + self } /// Clear all statements in the [`AST`], leaving only function definitions. #[inline(always)] - pub fn clear_statements(&mut self) { + pub fn clear_statements(&mut self) -> &mut Self { self.body = Default::default(); + self } /// Recursively walk the [`AST`], including function bodies (if any). /// Return `false` from the callback to terminate the walk. @@ -869,26 +891,33 @@ pub struct StmtBlock(StaticVec, Position); impl StmtBlock { /// Create a new [`StmtBlock`]. + #[must_use] pub fn new(mut statements: StaticVec, pos: Position) -> Self { statements.shrink_to_fit(); Self(statements, pos) } /// Is this statements block empty? #[inline(always)] + #[must_use] pub fn is_empty(&self) -> bool { self.0.is_empty() } /// Number of statements in this statements block. #[inline(always)] + #[must_use] pub fn len(&self) -> usize { self.0.len() } /// Get the position of this statements block. + #[inline(always)] + #[must_use] pub fn position(&self) -> Position { self.1 } /// Get the statements of this statements block. - pub fn statements(&mut self) -> &mut StaticVec { + #[inline(always)] + #[must_use] + pub fn statements_mut(&mut self) -> &mut StaticVec { &mut self.0 } } @@ -896,12 +925,14 @@ impl StmtBlock { impl Deref for StmtBlock { type Target = StaticVec; + #[inline(always)] fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for StmtBlock { + #[inline(always)] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } @@ -917,6 +948,7 @@ impl fmt::Debug for StmtBlock { } impl From for Stmt { + #[inline(always)] fn from(block: StmtBlock) -> Self { let block_pos = block.position(); Self::Block(block.0.into_boxed_slice(), block_pos) @@ -961,11 +993,7 @@ pub enum Stmt { /// `{` stmt`;` ... `}` Block(Box<[Stmt]>, Position), /// `try` `{` stmt; ... `}` `catch` `(` var `)` `{` stmt; ... `}` - TryCatch( - Box<(StmtBlock, Option, StmtBlock)>, - Position, - Position, - ), + TryCatch(Box<(StmtBlock, Option, StmtBlock)>, Position), /// [expression][Expr] Expr(Expr), /// `continue` @@ -983,7 +1011,7 @@ pub enum Stmt { /// /// Not available under `no_module`. #[cfg(not(feature = "no_module"))] - Export(Box<[(Ident, Option)]>, Position), + Export(Box<[(Ident, Ident)]>, Position), /// Convert a variable to shared. /// /// Not available under `no_closure`. @@ -1015,6 +1043,7 @@ impl From for StmtBlock { impl Stmt { /// Is this statement [`Noop`][Stmt::Noop]? #[inline(always)] + #[must_use] pub fn is_noop(&self) -> bool { match self { Self::Noop(_) => true, @@ -1022,6 +1051,7 @@ impl Stmt { } } /// Get the [position][Position] of this statement. + #[must_use] pub fn position(&self) -> Position { match self { Self::Noop(pos) @@ -1038,7 +1068,7 @@ impl Stmt { | Self::Return(_, _, pos) | Self::Let(_, _, _, pos) | Self::Const(_, _, _, pos) - | Self::TryCatch(_, pos, _) => *pos, + | Self::TryCatch(_, pos) => *pos, Self::Expr(x) => x.position(), @@ -1068,7 +1098,7 @@ impl Stmt { | Self::Return(_, _, pos) | Self::Let(_, _, _, pos) | Self::Const(_, _, _, pos) - | Self::TryCatch(_, pos, _) => *pos = new_pos, + | Self::TryCatch(_, pos) => *pos = new_pos, Self::Expr(x) => { x.set_position(new_pos); @@ -1086,6 +1116,7 @@ impl Stmt { self } /// Does this statement return a value? + #[must_use] pub fn returns_value(&self) -> bool { match self { Self::If(_, _, _) @@ -1098,7 +1129,7 @@ impl Stmt { | Self::While(_, _, _) | Self::Do(_, _, _, _) | Self::For(_, _, _) - | Self::TryCatch(_, _, _) => false, + | Self::TryCatch(_, _) => false, Self::Let(_, _, _, _) | Self::Const(_, _, _, _) @@ -1115,6 +1146,7 @@ impl Stmt { } } /// Is this statement self-terminated (i.e. no need for a semicolon terminator)? + #[must_use] pub fn is_self_terminated(&self) -> bool { match self { Self::If(_, _, _) @@ -1122,7 +1154,7 @@ impl Stmt { | Self::While(_, _, _) | Self::For(_, _, _) | Self::Block(_, _) - | Self::TryCatch(_, _, _) => true, + | Self::TryCatch(_, _) => true, // A No-op requires a semicolon in order to know it is an empty statement! Self::Noop(_) => false, @@ -1147,6 +1179,7 @@ impl Stmt { /// Is this statement _pure_? /// /// A pure statement has no side effects. + #[must_use] pub fn is_pure(&self) -> bool { match self { Self::Noop(_) => true, @@ -1174,7 +1207,7 @@ impl Stmt { | Self::FnCall(_, _) => false, Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()), Self::Continue(_) | Self::Break(_) | Self::Return(_, _, _) => false, - Self::TryCatch(x, _, _) => { + Self::TryCatch(x, _) => { (x.0).0.iter().all(Stmt::is_pure) && (x.2).0.iter().all(Stmt::is_pure) } @@ -1194,6 +1227,7 @@ impl Stmt { /// Only variable declarations (i.e. `let` and `const`) and `import`/`export` statements /// are internally pure. #[inline(always)] + #[must_use] pub fn is_internally_pure(&self) -> bool { match self { Self::Let(expr, _, _, _) | Self::Const(expr, _, _, _) => expr.is_pure(), @@ -1212,6 +1246,7 @@ impl Stmt { /// /// All statements following this statement will essentially be dead code. #[inline(always)] + #[must_use] pub fn is_control_flow_break(&self) -> bool { match self { Self::Return(_, _, _) | Self::Break(_) | Self::Continue(_) => true, @@ -1315,7 +1350,7 @@ impl Stmt { } } } - Self::TryCatch(x, _, _) => { + Self::TryCatch(x, _) => { for s in &(x.0).0 { if !s.walk(path, on_node) { return false; @@ -1364,6 +1399,15 @@ pub struct CustomExpr { pub tokens: StaticVec, } +impl CustomExpr { + /// Convert this into a [`Expr::Custom`]. + #[inline(always)] + #[must_use] + pub fn into_custom_syntax_expr(self, pos: Position) -> Expr { + Expr::Custom(self.into(), pos) + } +} + /// _(INTERNALS)_ A binary expression. /// Exported under the `internals` feature only. /// @@ -1397,6 +1441,7 @@ impl OpAssignment<'_> { /// # Panics /// /// Panics if the operator name is not an op-assignment operator. + #[must_use] pub fn new(op: Token) -> Self { let op_raw = op .map_op_assignment() @@ -1466,6 +1511,7 @@ impl fmt::Debug for FnCallHashes { impl FnCallHashes { /// Create a [`FnCallHashes`] with only the native Rust hash. #[inline(always)] + #[must_use] pub fn from_native(hash: u64) -> Self { Self { script: None, @@ -1474,6 +1520,7 @@ impl FnCallHashes { } /// Create a [`FnCallHashes`] with both native Rust and script function hashes set to the same value. #[inline(always)] + #[must_use] pub fn from_script(hash: u64) -> Self { Self { script: Some(hash), @@ -1482,6 +1529,7 @@ impl FnCallHashes { } /// Create a [`FnCallHashes`] with both native Rust and script function hashes. #[inline(always)] + #[must_use] pub fn from_script_and_native(script: u64, native: u64) -> Self { Self { script: Some(script), @@ -1490,19 +1538,10 @@ impl FnCallHashes { } /// Is this [`FnCallHashes`] native Rust only? #[inline(always)] + #[must_use] pub fn is_native_only(&self) -> bool { self.script.is_none() } - /// Get the script function hash from this [`FnCallHashes`]. - #[inline(always)] - pub fn script_hash(&self) -> Option { - self.script - } - /// Get the naive Rust function hash from this [`FnCallHashes`]. - #[inline(always)] - pub fn native_hash(&self) -> u64 { - self.native - } } /// _(INTERNALS)_ A function call. @@ -1530,9 +1569,16 @@ pub struct FnCallExpr { impl FnCallExpr { /// Does this function call contain a qualified namespace? #[inline(always)] + #[must_use] pub fn is_qualified(&self) -> bool { self.namespace.is_some() } + /// Convert this into a [`FnCall`][Expr::FnCall]. + #[inline(always)] + #[must_use] + pub fn into_fn_call_expr(self, pos: Position) -> Expr { + Expr::FnCall(self.into(), pos) + } } /// A type that wraps a floating-point number and implements [`Hash`]. @@ -1638,6 +1684,7 @@ impl FloatWrapper { pub const MIN_NATURAL_FLOAT_FOR_DISPLAY: f32 = 0.0000000000001; #[inline(always)] + #[must_use] pub fn new(value: F) -> Self { Self(value) } @@ -1646,6 +1693,7 @@ impl FloatWrapper { #[cfg(not(feature = "no_float"))] impl FloatWrapper { #[inline(always)] + #[must_use] pub(crate) const fn const_new(value: FLOAT) -> Self { Self(value) } @@ -1697,7 +1745,7 @@ pub enum Expr { Position, Box<( Option, - Option<(u64, NamespaceRef)>, + Option<(NamespaceRef, u64)>, Identifier, )>, ), @@ -1823,6 +1871,7 @@ impl Expr { /// /// Returns [`None`] if the expression is not a literal constant. #[inline] + #[must_use] pub fn get_literal_value(&self) -> Option { Some(match self { Self::DynamicConstant(x, _) => x.as_ref().clone(), @@ -1861,6 +1910,7 @@ impl Expr { } /// Create an [`Expr`] from a [`Dynamic`] value. #[inline] + #[must_use] pub fn from_dynamic(value: Dynamic, pos: Position) -> Self { match value.0 { Union::Unit(_, _, _) => Self::Unit(pos), @@ -1877,6 +1927,7 @@ impl Expr { } /// Is the expression a simple variable access? #[inline(always)] + #[must_use] pub(crate) fn is_variable_access(&self, non_qualified: bool) -> bool { match self { Self::Variable(_, _, x) => !non_qualified || x.1.is_none(), @@ -1885,6 +1936,7 @@ impl Expr { } /// Return the variable name if the expression a simple variable access. #[inline(always)] + #[must_use] pub(crate) fn get_variable_name(&self, non_qualified: bool) -> Option<&str> { match self { Self::Variable(_, _, x) if !non_qualified || x.1.is_none() => Some(x.2.as_str()), @@ -1893,6 +1945,7 @@ impl Expr { } /// Get the [position][Position] of the expression. #[inline] + #[must_use] pub fn position(&self) -> Position { match self { #[cfg(not(feature = "no_float"))] @@ -1966,6 +2019,7 @@ impl Expr { /// /// A pure expression has no side effects. #[inline] + #[must_use] pub fn is_pure(&self) -> bool { match self { Self::InterpolatedString(x) | Self::Array(x, _) => x.iter().all(Self::is_pure), @@ -1983,6 +2037,7 @@ impl Expr { } /// Is the expression the unit `()` literal? #[inline(always)] + #[must_use] pub fn is_unit(&self) -> bool { match self { Self::Unit(_) => true, @@ -1991,6 +2046,7 @@ impl Expr { } /// Is the expression a constant? #[inline] + #[must_use] pub fn is_constant(&self) -> bool { match self { #[cfg(not(feature = "no_float"))] @@ -2013,6 +2069,7 @@ impl Expr { } /// Is a particular [token][Token] allowed as a postfix operator to this expression? #[inline] + #[must_use] pub fn is_valid_postfix(&self, token: &Token) -> bool { match token { #[cfg(not(feature = "no_object"))] diff --git a/src/syntax.rs b/src/custom_syntax.rs similarity index 82% rename from src/syntax.rs rename to src/custom_syntax.rs index be664bcf..edc1b9b3 100644 --- a/src/syntax.rs +++ b/src/custom_syntax.rs @@ -1,13 +1,16 @@ //! Module implementing custom syntax for [`Engine`]. use crate::ast::Expr; +use crate::dynamic::Variant; use crate::engine::EvalContext; use crate::fn_native::SendSync; +use crate::r#unsafe::unsafe_try_cast; use crate::token::{is_valid_identifier, Token}; use crate::{ - Dynamic, Engine, Identifier, ImmutableString, LexError, ParseError, Position, RhaiResult, - Shared, StaticVec, + Engine, Identifier, ImmutableString, LexError, ParseError, Position, RhaiResult, Shared, + StaticVec, INT, }; +use std::any::TypeId; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -50,23 +53,70 @@ impl<'a> From<&'a Expr> for Expression<'a> { impl Expression<'_> { /// If this expression is a variable name, return it. Otherwise [`None`]. #[inline(always)] + #[must_use] pub fn get_variable_name(&self) -> Option<&str> { self.0.get_variable_name(true) } /// Get the expression. #[inline(always)] + #[must_use] pub(crate) fn expr(&self) -> &Expr { &self.0 } /// Get the position of this expression. #[inline(always)] + #[must_use] pub fn position(&self) -> Position { self.0.position() } /// Get the value of this expression if it is a literal constant. + /// Supports [`INT`][crate::INT], [`FLOAT`][crate::FLOAT], `()`, `char`, `bool` and + /// [`ImmutableString`][crate::ImmutableString]. + /// + /// Returns [`None`] also if the constant is not of the specified type. #[inline(always)] - pub fn get_literal_value(&self) -> Option { - self.0.get_literal_value() + #[must_use] + pub fn get_literal_value(&self) -> Option { + // Coded this way in order to maximally leverage potentials for dead-code removal. + + if TypeId::of::() == TypeId::of::() { + return match self.0 { + Expr::IntegerConstant(x, _) => unsafe_try_cast(*x).ok(), + _ => None, + }; + } + #[cfg(not(feature = "no_float"))] + if TypeId::of::() == TypeId::of::() { + return match self.0 { + Expr::FloatConstant(x, _) => unsafe_try_cast(*x).ok(), + _ => None, + }; + } + if TypeId::of::() == TypeId::of::() { + return match self.0 { + Expr::CharConstant(x, _) => unsafe_try_cast(*x).ok(), + _ => None, + }; + } + if TypeId::of::() == TypeId::of::() { + return match self.0 { + Expr::StringConstant(x, _) => unsafe_try_cast(x.clone()).ok(), + _ => None, + }; + } + if TypeId::of::() == TypeId::of::() { + return match self.0 { + Expr::BoolConstant(x, _) => unsafe_try_cast(*x).ok(), + _ => None, + }; + } + if TypeId::of::() == TypeId::of::<()>() { + return match self.0 { + Expr::Unit(_) => unsafe_try_cast(()).ok(), + _ => None, + }; + } + None } } @@ -77,6 +127,7 @@ 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, @@ -120,6 +171,7 @@ impl Engine { /// since they all go away at the end of the block. /// /// Variables in parent blocks should be left untouched as they persist beyond the current block. + #[must_use] pub fn register_custom_syntax + Into>( &mut self, keywords: &[S], diff --git a/src/dynamic.rs b/src/dynamic.rs index a95cab1f..7c876431 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -55,21 +55,27 @@ mod private { #[cfg(not(feature = "sync"))] pub trait Variant: Any + private::Sealed { /// Convert this [`Variant`] trait object to [`&dyn Any`][Any]. + #[must_use] fn as_any(&self) -> &dyn Any; /// Convert this [`Variant`] trait object to [`&mut dyn Any`][Any]. + #[must_use] fn as_mut_any(&mut self) -> &mut dyn Any; /// Convert this [`Variant`] trait object to an [`Any`] trait object. + #[must_use] fn as_box_any(self: Box) -> Box; /// Get the name of this type. + #[must_use] fn type_name(&self) -> &'static str; /// Convert into [`Dynamic`]. + #[must_use] fn into_dynamic(self) -> Dynamic; /// Clone into [`Dynamic`]. + #[must_use] fn clone_into_dynamic(&self) -> Dynamic; } @@ -80,21 +86,27 @@ pub trait Variant: Any + private::Sealed { #[cfg(feature = "sync")] pub trait Variant: Any + Send + Sync + private::Sealed { /// Convert this [`Variant`] trait object to [`&dyn Any`][Any]. + #[must_use] fn as_any(&self) -> &dyn Any; /// Convert this [`Variant`] trait object to [`&mut dyn Any`][Any]. + #[must_use] fn as_mut_any(&mut self) -> &mut dyn Any; /// Convert this [`Variant`] trait object to an [`Any`] trait object. + #[must_use] fn as_box_any(self: Box) -> Box; /// Get the name of this type. + #[must_use] fn type_name(&self) -> &'static str; /// Convert into [`Dynamic`]. + #[must_use] fn into_dynamic(self) -> Dynamic; /// Clone into [`Dynamic`]. + #[must_use] fn clone_into_dynamic(&self) -> Dynamic; } @@ -128,6 +140,7 @@ impl Variant for T { impl dyn Variant { /// Is this [`Variant`] a specific type? #[inline(always)] + #[must_use] pub fn is(&self) -> bool { TypeId::of::() == self.type_id() } @@ -240,10 +253,10 @@ impl<'d, T: Any + Clone> Deref for DynamicReadLock<'d, T> { #[inline(always)] fn deref(&self) -> &Self::Target { - match &self.0 { - DynamicReadLockInner::Reference(reference) => *reference, + match self.0 { + DynamicReadLockInner::Reference(ref reference) => *reference, #[cfg(not(feature = "no_closure"))] - DynamicReadLockInner::Guard(guard) => guard.downcast_ref().expect( + DynamicReadLockInner::Guard(ref guard) => guard.downcast_ref().expect( "never fails because the read guard was created after checking the data type", ), } @@ -283,10 +296,10 @@ impl<'d, T: Any + Clone> Deref for DynamicWriteLock<'d, T> { #[inline(always)] fn deref(&self) -> &Self::Target { - match &self.0 { - DynamicWriteLockInner::Reference(reference) => *reference, + match self.0 { + DynamicWriteLockInner::Reference(ref reference) => *reference, #[cfg(not(feature = "no_closure"))] - DynamicWriteLockInner::Guard(guard) => guard.downcast_ref().expect( + DynamicWriteLockInner::Guard(ref guard) => guard.downcast_ref().expect( "never fails because the write guard was created after checking the data type", ), } @@ -296,10 +309,10 @@ impl<'d, T: Any + Clone> Deref for DynamicWriteLock<'d, T> { impl<'d, T: Any + Clone> DerefMut for DynamicWriteLock<'d, T> { #[inline(always)] fn deref_mut(&mut self) -> &mut Self::Target { - match &mut self.0 { - DynamicWriteLockInner::Reference(reference) => *reference, + match self.0 { + DynamicWriteLockInner::Reference(ref mut reference) => *reference, #[cfg(not(feature = "no_closure"))] - DynamicWriteLockInner::Guard(guard) => guard.downcast_mut().expect( + DynamicWriteLockInner::Guard(ref mut guard) => guard.downcast_mut().expect( "never fails because the write guard was created after checking the data type", ), } @@ -308,6 +321,7 @@ impl<'d, T: Any + Clone> DerefMut for DynamicWriteLock<'d, T> { impl Dynamic { /// Get the arbitrary data attached to this [`Dynamic`]. + #[must_use] pub const fn tag(&self) -> Tag { match self.0 { Union::Unit(_, tag, _) @@ -333,33 +347,35 @@ impl Dynamic { } } /// Attach arbitrary data to this [`Dynamic`]. - pub fn set_tag(&mut self, value: Tag) { - match &mut self.0 { - Union::Unit(_, tag, _) - | Union::Bool(_, tag, _) - | Union::Str(_, tag, _) - | Union::Char(_, tag, _) - | Union::Int(_, tag, _) - | Union::FnPtr(_, tag, _) - | Union::Variant(_, tag, _) => *tag = value, + pub fn set_tag(&mut self, value: Tag) -> &mut Self { + match self.0 { + Union::Unit(_, ref mut tag, _) + | Union::Bool(_, ref mut tag, _) + | Union::Str(_, ref mut tag, _) + | Union::Char(_, ref mut tag, _) + | Union::Int(_, ref mut tag, _) + | Union::FnPtr(_, ref mut tag, _) + | Union::Variant(_, ref mut tag, _) => *tag = value, #[cfg(not(feature = "no_float"))] - Union::Float(_, tag, _) => *tag = value, + Union::Float(_, ref mut tag, _) => *tag = value, #[cfg(feature = "decimal")] - Union::Decimal(_, tag, _) => *tag = value, + Union::Decimal(_, ref mut tag, _) => *tag = value, #[cfg(not(feature = "no_index"))] - Union::Array(_, tag, _) => *tag = value, + Union::Array(_, ref mut tag, _) => *tag = value, #[cfg(not(feature = "no_object"))] - Union::Map(_, tag, _) => *tag = value, + Union::Map(_, ref mut tag, _) => *tag = value, #[cfg(not(feature = "no_std"))] - Union::TimeStamp(_, tag, _) => *tag = value, + Union::TimeStamp(_, ref mut tag, _) => *tag = value, #[cfg(not(feature = "no_closure"))] - Union::Shared(_, tag, _) => *tag = value, + Union::Shared(_, ref mut tag, _) => *tag = value, } + self } /// Does this [`Dynamic`] hold a variant data type /// instead of one of the supported system primitive types? #[inline(always)] + #[must_use] pub const fn is_variant(&self) -> bool { match self.0 { Union::Variant(_, _, _) => true, @@ -371,6 +387,7 @@ impl Dynamic { /// Not available under `no_closure`. #[cfg(not(feature = "no_closure"))] #[inline(always)] + #[must_use] pub const fn is_shared(&self) -> bool { #[cfg(not(feature = "no_closure"))] match self.0 { @@ -385,6 +402,7 @@ impl Dynamic { /// If the [`Dynamic`] is a shared variant checking is performed on /// top of its internal value. #[inline(always)] + #[must_use] pub fn is(&self) -> bool { let mut target_type_id = TypeId::of::(); @@ -400,8 +418,9 @@ 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. + #[must_use] pub fn type_id(&self) -> TypeId { - match &self.0 { + match self.0 { Union::Unit(_, _, _) => TypeId::of::<()>(), Union::Bool(_, _, _) => TypeId::of::(), Union::Str(_, _, _) => TypeId::of::(), @@ -419,17 +438,15 @@ impl Dynamic { #[cfg(not(feature = "no_std"))] Union::TimeStamp(_, _, _) => TypeId::of::(), - Union::Variant(value, _, _) => value.as_ref().as_ref().type_id(), + Union::Variant(ref value, _, _) => (***value).type_id(), #[cfg(not(feature = "no_closure"))] - Union::Shared(cell, _, _) => { - #[cfg(not(feature = "sync"))] - let value = cell.borrow(); - #[cfg(feature = "sync")] - let value = cell.read().unwrap(); + #[cfg(not(feature = "sync"))] + Union::Shared(ref cell, _, _) => (*cell.borrow()).type_id(), - (*value).type_id() - } + #[cfg(not(feature = "no_closure"))] + #[cfg(feature = "sync")] + Union::Shared(ref cell, _, _) => (*cell.read().unwrap()).type_id(), } } /// Get the name of the type of the value held by this [`Dynamic`]. @@ -438,8 +455,9 @@ 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. + #[must_use] pub fn type_name(&self) -> &'static str { - match &self.0 { + match self.0 { Union::Unit(_, _, _) => "()", Union::Bool(_, _, _) => "bool", Union::Str(_, _, _) => "string", @@ -457,17 +475,17 @@ impl Dynamic { #[cfg(not(feature = "no_std"))] Union::TimeStamp(_, _, _) => "timestamp", - Union::Variant(value, _, _) => value.as_ref().as_ref().type_name(), + Union::Variant(ref value, _, _) => (***value).type_name(), #[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "sync"))] - Union::Shared(cell, _, _) => cell + Union::Shared(ref cell, _, _) => cell .try_borrow() .map(|v| (*v).type_name()) .unwrap_or(""), #[cfg(not(feature = "no_closure"))] #[cfg(feature = "sync")] - Union::Shared(cell, _, _) => (*cell.read().unwrap()).type_name(), + Union::Shared(ref cell, _, _) => (*cell.read().unwrap()).type_name(), } } } @@ -481,76 +499,67 @@ impl Hash for Dynamic { fn hash(&self, state: &mut H) { std::mem::discriminant(&self.0).hash(state); - match &self.0 { + match self.0 { Union::Unit(_, _, _) => ().hash(state), - Union::Bool(b, _, _) => b.hash(state), - Union::Str(s, _, _) => s.hash(state), - Union::Char(c, _, _) => c.hash(state), - Union::Int(i, _, _) => i.hash(state), + Union::Bool(ref b, _, _) => b.hash(state), + Union::Str(ref s, _, _) => s.hash(state), + Union::Char(ref c, _, _) => c.hash(state), + Union::Int(ref i, _, _) => i.hash(state), #[cfg(not(feature = "no_float"))] - Union::Float(f, _, _) => f.hash(state), + Union::Float(ref f, _, _) => f.hash(state), #[cfg(not(feature = "no_index"))] - Union::Array(a, _, _) => a.as_ref().hash(state), + Union::Array(ref a, _, _) => a.as_ref().hash(state), #[cfg(not(feature = "no_object"))] - Union::Map(m, _, _) => m.as_ref().hash(state), - Union::FnPtr(f, _, _) => f.hash(state), + Union::Map(ref m, _, _) => m.as_ref().hash(state), + Union::FnPtr(ref f, _, _) => f.hash(state), #[cfg(not(feature = "no_closure"))] - Union::Shared(cell, _, _) => { - #[cfg(not(feature = "sync"))] - let value = cell.borrow(); - #[cfg(feature = "sync")] - let value = cell.read().unwrap(); + #[cfg(not(feature = "sync"))] + Union::Shared(ref cell, _, _) => (*cell.borrow()).hash(state), - (*value).hash(state) - } + #[cfg(not(feature = "no_closure"))] + #[cfg(feature = "sync")] + Union::Shared(ref cell, _, _) => (*cell.read().unwrap()).hash(state), #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] - Union::Variant(value, _, _) => { - let value = value.as_ref().as_ref(); - let _type_id = value.type_id(); - let _value_any = value.as_any(); + Union::Variant(ref value, _, _) => { + let value_any = (***value).as_any(); + let type_id = value_any.type_id(); - if _type_id == TypeId::of::() { + if type_id == TypeId::of::() { TypeId::of::().hash(state); - _value_any.downcast_ref::().expect(CHECKED).hash(state); - } else if _type_id == TypeId::of::() { + value_any.downcast_ref::().expect(CHECKED).hash(state); + } else if type_id == TypeId::of::() { TypeId::of::().hash(state); - _value_any.downcast_ref::().expect(CHECKED).hash(state); - } else if _type_id == TypeId::of::() { + value_any.downcast_ref::().expect(CHECKED).hash(state); + } else if type_id == TypeId::of::() { TypeId::of::().hash(state); - _value_any.downcast_ref::().expect(CHECKED).hash(state); - } else if _type_id == TypeId::of::() { + value_any.downcast_ref::().expect(CHECKED).hash(state); + } else if type_id == TypeId::of::() { TypeId::of::().hash(state); - _value_any.downcast_ref::().expect(CHECKED).hash(state); - } else if _type_id == TypeId::of::() { + value_any.downcast_ref::().expect(CHECKED).hash(state); + } else if type_id == TypeId::of::() { TypeId::of::().hash(state); - _value_any.downcast_ref::().expect(CHECKED).hash(state); - } else if _type_id == TypeId::of::() { + value_any.downcast_ref::().expect(CHECKED).hash(state); + } else if type_id == TypeId::of::() { TypeId::of::().hash(state); - _value_any.downcast_ref::().expect(CHECKED).hash(state); - } else if _type_id == TypeId::of::() { + value_any.downcast_ref::().expect(CHECKED).hash(state); + } else if type_id == TypeId::of::() { TypeId::of::().hash(state); - _value_any.downcast_ref::().expect(CHECKED).hash(state); - } else if _type_id == TypeId::of::() { + value_any.downcast_ref::().expect(CHECKED).hash(state); + } else if type_id == TypeId::of::() { TypeId::of::().hash(state); - _value_any.downcast_ref::().expect(CHECKED).hash(state); + value_any.downcast_ref::().expect(CHECKED).hash(state); } #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] - if _type_id == TypeId::of::() { + if type_id == TypeId::of::() { TypeId::of::().hash(state); - _value_any - .downcast_ref::() - .expect(CHECKED) - .hash(state); - } else if _type_id == TypeId::of::() { + value_any.downcast_ref::().expect(CHECKED).hash(state); + } else if type_id == TypeId::of::() { TypeId::of::().hash(state); - _value_any - .downcast_ref::() - .expect(CHECKED) - .hash(state); + value_any.downcast_ref::().expect(CHECKED).hash(state); } } @@ -561,6 +570,7 @@ impl Hash for Dynamic { /// Map the name of a standard type into a friendly form. #[inline(always)] +#[must_use] pub(crate) fn map_std_type_name(name: &str) -> &str { if name == type_name::() { return "string"; @@ -596,31 +606,30 @@ pub(crate) fn map_std_type_name(name: &str) -> &str { impl fmt::Display for Dynamic { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &self.0 { + match self.0 { Union::Unit(_, _, _) => write!(f, ""), - Union::Bool(value, _, _) => fmt::Display::fmt(value, f), - Union::Str(value, _, _) => fmt::Display::fmt(value, f), - Union::Char(value, _, _) => fmt::Display::fmt(value, f), - Union::Int(value, _, _) => fmt::Display::fmt(value, f), + Union::Bool(ref value, _, _) => fmt::Display::fmt(value, f), + Union::Str(ref value, _, _) => fmt::Display::fmt(value, f), + Union::Char(ref value, _, _) => fmt::Display::fmt(value, f), + Union::Int(ref value, _, _) => fmt::Display::fmt(value, f), #[cfg(not(feature = "no_float"))] - Union::Float(value, _, _) => fmt::Display::fmt(value, f), + Union::Float(ref value, _, _) => fmt::Display::fmt(value, f), #[cfg(feature = "decimal")] - Union::Decimal(value, _, _) => fmt::Display::fmt(value, f), + Union::Decimal(ref value, _, _) => fmt::Display::fmt(value, f), #[cfg(not(feature = "no_index"))] - Union::Array(value, _, _) => fmt::Debug::fmt(value, f), + Union::Array(ref value, _, _) => fmt::Debug::fmt(value, f), #[cfg(not(feature = "no_object"))] - Union::Map(value, _, _) => { + Union::Map(ref value, _, _) => { f.write_str("#")?; fmt::Debug::fmt(value, f) } - Union::FnPtr(value, _, _) => fmt::Display::fmt(value, f), + Union::FnPtr(ref value, _, _) => fmt::Display::fmt(value, f), #[cfg(not(feature = "no_std"))] Union::TimeStamp(_, _, _) => f.write_str(""), - Union::Variant(value, _, _) => { - let value = value.as_ref().as_ref(); - let _type_id = value.type_id(); - let _value_any = value.as_any(); + Union::Variant(ref value, _, _) => { + let _value_any = (***value).as_any(); + let _type_id = _value_any.type_id(); #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] @@ -656,12 +665,12 @@ impl fmt::Display for Dynamic { return fmt::Display::fmt(_value_any.downcast_ref::().expect(CHECKED), f); } - f.write_str(value.type_name()) + f.write_str((***value).type_name()) } #[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "sync"))] - Union::Shared(cell, _, _) => { + Union::Shared(ref cell, _, _) => { if let Ok(v) = cell.try_borrow() { fmt::Display::fmt(&*v, f) } else { @@ -670,38 +679,37 @@ impl fmt::Display for Dynamic { } #[cfg(not(feature = "no_closure"))] #[cfg(feature = "sync")] - Union::Shared(cell, _, _) => fmt::Display::fmt(&*cell.read().unwrap(), f), + Union::Shared(ref cell, _, _) => fmt::Display::fmt(&*cell.read().unwrap(), f), } } } impl fmt::Debug for Dynamic { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &self.0 { - Union::Unit(value, _, _) => fmt::Debug::fmt(value, f), - Union::Bool(value, _, _) => fmt::Debug::fmt(value, f), - Union::Str(value, _, _) => fmt::Debug::fmt(value, f), - Union::Char(value, _, _) => fmt::Debug::fmt(value, f), - Union::Int(value, _, _) => fmt::Debug::fmt(value, f), + match self.0 { + Union::Unit(ref value, _, _) => fmt::Debug::fmt(value, f), + Union::Bool(ref value, _, _) => fmt::Debug::fmt(value, f), + Union::Str(ref value, _, _) => fmt::Debug::fmt(value, f), + Union::Char(ref value, _, _) => fmt::Debug::fmt(value, f), + Union::Int(ref value, _, _) => fmt::Debug::fmt(value, f), #[cfg(not(feature = "no_float"))] - Union::Float(value, _, _) => fmt::Debug::fmt(value, f), + Union::Float(ref value, _, _) => fmt::Debug::fmt(value, f), #[cfg(feature = "decimal")] - Union::Decimal(value, _, _) => fmt::Debug::fmt(value, f), + Union::Decimal(ref value, _, _) => fmt::Debug::fmt(value, f), #[cfg(not(feature = "no_index"))] - Union::Array(value, _, _) => fmt::Debug::fmt(value, f), + Union::Array(ref value, _, _) => fmt::Debug::fmt(value, f), #[cfg(not(feature = "no_object"))] - Union::Map(value, _, _) => { + Union::Map(ref value, _, _) => { f.write_str("#")?; fmt::Debug::fmt(value, f) } - Union::FnPtr(value, _, _) => fmt::Debug::fmt(value, f), + Union::FnPtr(ref value, _, _) => fmt::Debug::fmt(value, f), #[cfg(not(feature = "no_std"))] Union::TimeStamp(_, _, _) => write!(f, ""), - Union::Variant(value, _, _) => { - let value = value.as_ref().as_ref(); - let _type_id = value.type_id(); - let _value_any = value.as_any(); + Union::Variant(ref value, _, _) => { + let _value_any = (***value).as_any(); + let _type_id = _value_any.type_id(); const CHECKED: &str = "never fails because the type was checked"; @@ -739,12 +747,12 @@ impl fmt::Debug for Dynamic { return fmt::Debug::fmt(_value_any.downcast_ref::().expect(CHECKED), f); } - f.write_str(value.type_name()) + f.write_str((***value).type_name()) } #[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "sync"))] - Union::Shared(cell, _, _) => { + Union::Shared(ref cell, _, _) => { if let Ok(v) = cell.try_borrow() { write!(f, "{:?} (shared)", *v) } else { @@ -753,7 +761,7 @@ impl fmt::Debug for Dynamic { } #[cfg(not(feature = "no_closure"))] #[cfg(feature = "sync")] - Union::Shared(cell, _, _) => fmt::Debug::fmt(&*cell.read().unwrap(), f), + Union::Shared(ref cell, _, _) => fmt::Debug::fmt(&*cell.read().unwrap(), f), } } } @@ -803,6 +811,7 @@ impl Clone for Dynamic { impl Default for Dynamic { #[inline(always)] + #[must_use] fn default() -> Self { Self::UNIT } @@ -872,6 +881,7 @@ impl Dynamic { )); /// Get the [`AccessMode`] for this [`Dynamic`]. + #[must_use] pub(crate) const fn access_mode(&self) -> AccessMode { match self.0 { Union::Unit(_, _, access) @@ -897,35 +907,40 @@ impl Dynamic { } } /// Set the [`AccessMode`] for this [`Dynamic`]. - pub(crate) fn set_access_mode(&mut self, typ: AccessMode) { - match &mut self.0 { - Union::Unit(_, _, access) - | Union::Bool(_, _, access) - | Union::Str(_, _, access) - | Union::Char(_, _, access) - | Union::Int(_, _, access) - | Union::FnPtr(_, _, access) - | Union::Variant(_, _, access) => *access = typ, + pub(crate) fn set_access_mode(&mut self, typ: AccessMode) -> &mut Self { + match self.0 { + Union::Unit(_, _, ref mut access) + | Union::Bool(_, _, ref mut access) + | Union::Str(_, _, ref mut access) + | Union::Char(_, _, ref mut access) + | Union::Int(_, _, ref mut access) + | Union::FnPtr(_, _, ref mut access) + | Union::Variant(_, _, ref mut access) => *access = typ, #[cfg(not(feature = "no_float"))] - Union::Float(_, _, access) => *access = typ, + Union::Float(_, _, ref mut access) => *access = typ, #[cfg(feature = "decimal")] - Union::Decimal(_, _, access) => *access = typ, + Union::Decimal(_, _, ref mut access) => *access = typ, #[cfg(not(feature = "no_index"))] - Union::Array(a, _, access) => { + Union::Array(ref mut a, _, ref mut access) => { *access = typ; - a.iter_mut().for_each(|v| v.set_access_mode(typ)); + a.iter_mut().for_each(|v| { + v.set_access_mode(typ); + }); } #[cfg(not(feature = "no_object"))] - Union::Map(m, _, access) => { + Union::Map(ref mut m, _, ref mut access) => { *access = typ; - m.values_mut().for_each(|v| v.set_access_mode(typ)); + m.values_mut().for_each(|v| { + v.set_access_mode(typ); + }); } #[cfg(not(feature = "no_std"))] - Union::TimeStamp(_, _, access) => *access = typ, + Union::TimeStamp(_, _, ref mut access) => *access = typ, #[cfg(not(feature = "no_closure"))] - Union::Shared(_, _, access) => *access = typ, + Union::Shared(_, _, ref mut access) => *access = typ, } + self } /// Is this [`Dynamic`] read-only? /// @@ -934,21 +949,27 @@ impl Dynamic { /// [`ErrorAssignmentToConstant`][crate::EvalAltResult::ErrorAssignmentToConstant] /// if its value is going to be modified. This safe-guards constant values from being modified /// from within Rust functions. + #[must_use] pub fn is_read_only(&self) -> bool { #[cfg(not(feature = "no_closure"))] match self.0 { Union::Shared(_, _, ReadOnly) => return true, - Union::Shared(ref cell, _, _) => { - #[cfg(not(feature = "sync"))] - let value = cell.borrow(); - #[cfg(feature = "sync")] - let value = cell.read().unwrap(); - return match value.access_mode() { + #[cfg(not(feature = "sync"))] + Union::Shared(ref cell, _, _) => { + return match cell.borrow().access_mode() { ReadWrite => false, ReadOnly => true, - }; + } } + #[cfg(feature = "sync")] + Union::Shared(ref cell, _, _) => { + return match cell.read().unwrap().access_mode() { + ReadWrite => false, + ReadOnly => true, + } + } + _ => (), } @@ -958,8 +979,9 @@ impl Dynamic { } } /// Can this [`Dynamic`] be hashed? + #[must_use] pub(crate) fn is_hashable(&self) -> bool { - match &self.0 { + match self.0 { Union::Unit(_, _, _) | Union::Bool(_, _, _) | Union::Str(_, _, _) @@ -974,14 +996,12 @@ impl Dynamic { Union::Map(_, _, _) => true, #[cfg(not(feature = "no_closure"))] - Union::Shared(cell, _, _) => { - #[cfg(not(feature = "sync"))] - let value = cell.borrow(); - #[cfg(feature = "sync")] - let value = cell.read().unwrap(); + #[cfg(not(feature = "sync"))] + Union::Shared(ref cell, _, _) => cell.borrow().is_hashable(), - value.is_hashable() - } + #[cfg(not(feature = "no_closure"))] + #[cfg(feature = "sync")] + Union::Shared(ref cell, _, _) => cell.read().unwrap().is_hashable(), _ => false, } @@ -1019,6 +1039,7 @@ impl Dynamic { /// assert_eq!(new_result.to_string(), "hello"); /// ``` #[inline(always)] + #[must_use] pub fn from(mut value: T) -> Self { // Coded this way in order to maximally leverage potentials for dead-code removal. @@ -1124,6 +1145,7 @@ impl Dynamic { /// If the [`Dynamic`] value is already shared, this method returns itself. #[cfg(not(feature = "no_closure"))] #[inline(always)] + #[must_use] pub fn into_shared(self) -> Self { let _access = self.access_mode(); @@ -1160,6 +1182,7 @@ impl Dynamic { /// assert_eq!(x.try_cast::().unwrap(), 42); /// ``` #[inline(always)] + #[must_use] pub fn try_cast(self) -> Option { // Coded this way in order to maximally leverage potentials for dead-code removal. @@ -1295,6 +1318,7 @@ impl Dynamic { /// assert_eq!(x.cast::(), 42); /// ``` #[inline(always)] + #[must_use] pub fn cast(self) -> T { #[cfg(not(feature = "no_closure"))] let self_type_name = if self.is_shared() { @@ -1342,6 +1366,7 @@ impl Dynamic { /// assert_eq!(y.clone_cast::(), 42); /// ``` #[inline(always)] + #[must_use] pub fn clone_cast(&self) -> T { self.flatten_clone().cast::() } @@ -1351,21 +1376,17 @@ impl Dynamic { /// /// If the [`Dynamic`] is a shared value, it returns a cloned copy of the shared value. #[inline(always)] + #[must_use] pub fn flatten_clone(&self) -> Self { - #[cfg(not(feature = "no_closure"))] - match &self.0 { - Union::Shared(cell, _, _) => { - #[cfg(not(feature = "sync"))] - let value = cell.borrow(); - #[cfg(feature = "sync")] - let value = cell.read().unwrap(); - - return value.clone(); - } - _ => (), + match self.0 { + #[cfg(not(feature = "no_closure"))] + #[cfg(not(feature = "sync"))] + Union::Shared(ref cell, _, _) => cell.borrow().clone(), + #[cfg(not(feature = "no_closure"))] + #[cfg(feature = "sync")] + Union::Shared(ref cell, _, _) => cell.read().unwrap().clone(), + _ => self.clone(), } - - self.clone() } /// Flatten the [`Dynamic`]. /// @@ -1374,31 +1395,22 @@ impl Dynamic { /// If the [`Dynamic`] is a shared value, it returns the shared value if there are no /// outstanding references, or a cloned copy. #[inline(always)] + #[must_use] pub fn flatten(self) -> Self { - #[cfg(not(feature = "no_closure"))] match self.0 { - Union::Shared(cell, _, _) => { - return crate::fn_native::shared_try_take(cell).map_or_else( - |cell| { - #[cfg(not(feature = "sync"))] - let value = cell.borrow(); - #[cfg(feature = "sync")] - let value = cell.read().unwrap(); - - value.clone() - }, - |value| { - #[cfg(not(feature = "sync"))] - return value.into_inner(); - #[cfg(feature = "sync")] - return value.into_inner().unwrap(); - }, - ) - } - _ => (), + #[cfg(not(feature = "no_closure"))] + Union::Shared(cell, _, _) => crate::fn_native::shared_try_take(cell).map_or_else( + #[cfg(not(feature = "sync"))] + |cell| cell.borrow().clone(), + #[cfg(feature = "sync")] + |cell| cell.read().unwrap().clone(), + #[cfg(not(feature = "sync"))] + |value| value.into_inner(), + #[cfg(feature = "sync")] + |value| value.into_inner().unwrap(), + ), + _ => self, } - - self } /// Flatten the [`Dynamic`] in place. /// @@ -1407,32 +1419,27 @@ impl Dynamic { /// If the [`Dynamic`] is a shared value, it is set to the shared value if there are no /// outstanding references, or a cloned copy otherwise. #[inline(always)] - pub(crate) fn flatten_in_place(&mut self) { - #[cfg(not(feature = "no_closure"))] + pub(crate) fn flatten_in_place(&mut self) -> &mut Self { match self.0 { + #[cfg(not(feature = "no_closure"))] Union::Shared(_, _, _) => match std::mem::take(self).0 { Union::Shared(cell, _, _) => { *self = crate::fn_native::shared_try_take(cell).map_or_else( - |cell| { - #[cfg(not(feature = "sync"))] - let value = cell.borrow(); - #[cfg(feature = "sync")] - let value = cell.read().unwrap(); - - value.clone() - }, - |value| { - #[cfg(not(feature = "sync"))] - return value.into_inner(); - #[cfg(feature = "sync")] - return value.into_inner().unwrap(); - }, - ) + #[cfg(not(feature = "sync"))] + |cell| cell.borrow().clone(), + #[cfg(feature = "sync")] + |cell| cell.read().unwrap().clone(), + #[cfg(not(feature = "sync"))] + |value| value.into_inner(), + #[cfg(feature = "sync")] + |value| value.into_inner().unwrap(), + ); } _ => unreachable!(), }, _ => (), } + self } /// Is the [`Dynamic`] a shared value that is locked? /// @@ -1445,6 +1452,7 @@ impl Dynamic { /// So this method always returns [`false`] under [`Sync`]. #[cfg(not(feature = "no_closure"))] #[inline(always)] + #[must_use] pub fn is_locked(&self) -> bool { #[cfg(not(feature = "no_closure"))] match self.0 { @@ -1470,6 +1478,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)] + #[must_use] pub fn read_lock(&self) -> Option> { match self.0 { #[cfg(not(feature = "no_closure"))] @@ -1503,6 +1512,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)] + #[must_use] pub fn write_lock(&mut self) -> Option> { match self.0 { #[cfg(not(feature = "no_closure"))] @@ -1531,77 +1541,78 @@ impl Dynamic { /// /// Returns [`None`] if the cast fails, or if the value is shared. #[inline(always)] + #[must_use] pub(crate) fn downcast_ref(&self) -> Option<&T> { // Coded this way in order to maximally leverage potentials for dead-code removal. if TypeId::of::() == TypeId::of::() { - return match &self.0 { - Union::Int(value, _, _) => value.as_any().downcast_ref::(), + return match self.0 { + Union::Int(ref value, _, _) => value.as_any().downcast_ref::(), _ => None, }; } #[cfg(not(feature = "no_float"))] if TypeId::of::() == TypeId::of::() { - return match &self.0 { - Union::Float(value, _, _) => value.as_ref().as_any().downcast_ref::(), + return match self.0 { + Union::Float(ref value, _, _) => value.as_ref().as_any().downcast_ref::(), _ => None, }; } #[cfg(feature = "decimal")] if TypeId::of::() == TypeId::of::() { - return match &self.0 { - Union::Decimal(value, _, _) => value.as_ref().as_any().downcast_ref::(), + return match self.0 { + Union::Decimal(ref value, _, _) => value.as_ref().as_any().downcast_ref::(), _ => None, }; } if TypeId::of::() == TypeId::of::() { - return match &self.0 { - Union::Bool(value, _, _) => value.as_any().downcast_ref::(), + return match self.0 { + Union::Bool(ref value, _, _) => value.as_any().downcast_ref::(), _ => None, }; } if TypeId::of::() == TypeId::of::() { - return match &self.0 { - Union::Str(value, _, _) => value.as_any().downcast_ref::(), + return match self.0 { + Union::Str(ref value, _, _) => value.as_any().downcast_ref::(), _ => None, }; } if TypeId::of::() == TypeId::of::() { - return match &self.0 { - Union::Char(value, _, _) => value.as_any().downcast_ref::(), + return match self.0 { + Union::Char(ref value, _, _) => value.as_any().downcast_ref::(), _ => None, }; } #[cfg(not(feature = "no_index"))] if TypeId::of::() == TypeId::of::() { - return match &self.0 { - Union::Array(value, _, _) => value.as_ref().as_any().downcast_ref::(), + return match self.0 { + Union::Array(ref value, _, _) => value.as_ref().as_any().downcast_ref::(), _ => None, }; } #[cfg(not(feature = "no_object"))] if TypeId::of::() == TypeId::of::() { - return match &self.0 { - Union::Map(value, _, _) => value.as_ref().as_any().downcast_ref::(), + return match self.0 { + Union::Map(ref value, _, _) => value.as_ref().as_any().downcast_ref::(), _ => None, }; } if TypeId::of::() == TypeId::of::() { - return match &self.0 { - Union::FnPtr(value, _, _) => value.as_ref().as_any().downcast_ref::(), + return match self.0 { + Union::FnPtr(ref value, _, _) => value.as_ref().as_any().downcast_ref::(), _ => None, }; } #[cfg(not(feature = "no_std"))] if TypeId::of::() == TypeId::of::() { - return match &self.0 { - Union::TimeStamp(value, _, _) => value.as_ref().as_any().downcast_ref::(), + return match self.0 { + Union::TimeStamp(ref value, _, _) => value.as_ref().as_any().downcast_ref::(), _ => None, }; } if TypeId::of::() == TypeId::of::<()>() { - return match &self.0 { - Union::Unit(value, _, _) => value.as_any().downcast_ref::(), + return match self.0 { + Union::Unit(ref value, _, _) => value.as_any().downcast_ref::(), _ => None, }; } @@ -1609,8 +1620,8 @@ impl Dynamic { return self.as_any().downcast_ref::(); } - match &self.0 { - Union::Variant(value, _, _) => value.as_ref().as_ref().as_any().downcast_ref::(), + match self.0 { + Union::Variant(ref value, _, _) => (***value).as_any().downcast_ref::(), #[cfg(not(feature = "no_closure"))] Union::Shared(_, _, _) => None, _ => None, @@ -1621,77 +1632,88 @@ impl Dynamic { /// /// Returns [`None`] if the cast fails, or if the value is shared. #[inline(always)] + #[must_use] pub(crate) fn downcast_mut(&mut self) -> Option<&mut T> { // Coded this way in order to maximally leverage potentials for dead-code removal. if TypeId::of::() == TypeId::of::() { - return match &mut self.0 { - Union::Int(value, _, _) => value.as_mut_any().downcast_mut::(), + return match self.0 { + Union::Int(ref mut value, _, _) => value.as_mut_any().downcast_mut::(), _ => None, }; } #[cfg(not(feature = "no_float"))] if TypeId::of::() == TypeId::of::() { - return match &mut self.0 { - Union::Float(value, _, _) => value.as_mut().as_mut_any().downcast_mut::(), + return match self.0 { + Union::Float(ref mut value, _, _) => { + value.as_mut().as_mut_any().downcast_mut::() + } _ => None, }; } #[cfg(feature = "decimal")] if TypeId::of::() == TypeId::of::() { - return match &mut self.0 { - Union::Decimal(value, _, _) => value.as_mut().as_mut_any().downcast_mut::(), + return match self.0 { + Union::Decimal(ref mut value, _, _) => { + value.as_mut().as_mut_any().downcast_mut::() + } _ => None, }; } if TypeId::of::() == TypeId::of::() { - return match &mut self.0 { - Union::Bool(value, _, _) => value.as_mut_any().downcast_mut::(), + return match self.0 { + Union::Bool(ref mut value, _, _) => value.as_mut_any().downcast_mut::(), _ => None, }; } if TypeId::of::() == TypeId::of::() { - return match &mut self.0 { - Union::Str(value, _, _) => value.as_mut_any().downcast_mut::(), + return match self.0 { + Union::Str(ref mut value, _, _) => value.as_mut_any().downcast_mut::(), _ => None, }; } if TypeId::of::() == TypeId::of::() { - return match &mut self.0 { - Union::Char(value, _, _) => value.as_mut_any().downcast_mut::(), + return match self.0 { + Union::Char(ref mut value, _, _) => value.as_mut_any().downcast_mut::(), _ => None, }; } #[cfg(not(feature = "no_index"))] if TypeId::of::() == TypeId::of::() { - return match &mut self.0 { - Union::Array(value, _, _) => value.as_mut().as_mut_any().downcast_mut::(), + return match self.0 { + Union::Array(ref mut value, _, _) => { + value.as_mut().as_mut_any().downcast_mut::() + } _ => None, }; } #[cfg(not(feature = "no_object"))] if TypeId::of::() == TypeId::of::() { - return match &mut self.0 { - Union::Map(value, _, _) => value.as_mut().as_mut_any().downcast_mut::(), + return match self.0 { + Union::Map(ref mut value, _, _) => value.as_mut().as_mut_any().downcast_mut::(), _ => None, }; } if TypeId::of::() == TypeId::of::() { - return match &mut self.0 { - Union::FnPtr(value, _, _) => value.as_mut().as_mut_any().downcast_mut::(), + return match self.0 { + Union::FnPtr(ref mut value, _, _) => { + value.as_mut().as_mut_any().downcast_mut::() + } _ => None, }; } #[cfg(not(feature = "no_std"))] if TypeId::of::() == TypeId::of::() { - return match &mut self.0 { - Union::TimeStamp(value, _, _) => value.as_mut().as_mut_any().downcast_mut::(), + return match self.0 { + Union::TimeStamp(ref mut value, _, _) => { + value.as_mut().as_mut_any().downcast_mut::() + } _ => None, }; } if TypeId::of::() == TypeId::of::<()>() { - return match &mut self.0 { - Union::Unit(value, _, _) => value.as_mut_any().downcast_mut::(), + return match self.0 { + Union::Unit(ref mut value, _, _) => value.as_mut_any().downcast_mut::(), _ => None, }; } @@ -1699,8 +1721,8 @@ impl Dynamic { return self.as_mut_any().downcast_mut::(); } - match &mut self.0 { - Union::Variant(value, _, _) => value.as_mut().as_mut_any().downcast_mut::(), + match self.0 { + Union::Variant(ref mut value, _, _) => (***value).as_mut_any().downcast_mut::(), #[cfg(not(feature = "no_closure"))] Union::Shared(_, _, _) => None, _ => None, @@ -1709,6 +1731,7 @@ 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), @@ -1720,6 +1743,7 @@ 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), @@ -1734,6 +1758,7 @@ 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), @@ -1748,6 +1773,7 @@ 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), @@ -1759,6 +1785,7 @@ 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), @@ -1770,6 +1797,7 @@ 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), @@ -1785,6 +1813,7 @@ 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), @@ -1802,6 +1831,7 @@ impl Dynamic { /// This method is deprecated and will be removed in the future. /// Use [`as_string`][Dynamic::as_string] instead. #[inline(always)] + #[must_use] #[deprecated( since = "0.20.3", note = "this method is deprecated and will be removed in the future" @@ -1813,6 +1843,7 @@ 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) } @@ -1824,6 +1855,7 @@ impl Dynamic { /// This method is deprecated and will be removed in the future. /// Use [`as_immutable_string`][Dynamic::as_immutable_string] instead. #[inline(always)] + #[must_use] #[deprecated( since = "0.20.3", note = "this method is deprecated and will be removed in the future" @@ -1834,6 +1866,7 @@ impl Dynamic { /// Convert the [`Dynamic`] into an [`ImmutableString`] and return it. /// Returns the name of the actual type if the cast fails. #[inline(always)] + #[must_use] pub fn as_immutable_string(self) -> Result { match self.0 { Union::Str(s, _, _) => Ok(s), @@ -1844,8 +1877,8 @@ impl Dynamic { #[cfg(feature = "sync")] let value = cell.read().unwrap(); - match &value.0 { - Union::Str(s, _, _) => Ok(s.clone()), + match value.0 { + Union::Str(ref s, _, _) => Ok(s.clone()), _ => Err((*value).type_name()), } } diff --git a/src/engine.rs b/src/engine.rs index 905d7e5f..205a8c37 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,7 +1,9 @@ //! Main module defining the script evaluation [`Engine`]. use crate::ast::{Expr, FnCallExpr, Ident, OpAssignment, ReturnType, Stmt}; +use crate::custom_syntax::CustomSyntax; use crate::dynamic::{map_std_type_name, AccessMode, Union, Variant}; +use crate::fn_hash::get_hasher; use crate::fn_native::{ CallableFunction, IteratorFn, OnDebugCallback, OnPrintCallback, OnVarCallback, }; @@ -9,9 +11,7 @@ use crate::module::NamespaceRef; use crate::optimize::OptimizationLevel; use crate::packages::{Package, StandardPackage}; use crate::r#unsafe::unsafe_cast_var_name_to_lifetime; -use crate::syntax::CustomSyntax; use crate::token::Token; -use crate::utils::get_hasher; use crate::{ Dynamic, EvalAltResult, Identifier, ImmutableString, Module, Position, RhaiResult, Scope, Shared, StaticVec, INT, @@ -64,27 +64,32 @@ pub struct Imports { impl Imports { /// Get the length of this stack of imported [modules][Module]. #[inline(always)] + #[must_use] pub fn len(&self) -> usize { self.keys.len() } /// Is this stack of imported [modules][Module] empty? #[inline(always)] + #[must_use] pub fn is_empty(&self) -> bool { self.keys.is_empty() } /// Get the imported [modules][Module] at a particular index. #[inline(always)] + #[must_use] pub fn get(&self, index: usize) -> Option> { self.modules.get(index).cloned() } /// Get the imported [modules][Module] at a particular index. #[allow(dead_code)] #[inline(always)] + #[must_use] pub(crate) fn get_mut(&mut self, index: usize) -> Option<&mut Shared> { self.modules.get_mut(index) } /// Get the index of an imported [modules][Module] by name. #[inline(always)] + #[must_use] pub fn find(&self, name: &str) -> Option { self.keys .iter() @@ -107,6 +112,7 @@ 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() @@ -117,17 +123,20 @@ 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()) } /// Get a consuming iterator to this stack of imported [modules][Module] in reverse order. #[inline(always)] + #[must_use] pub fn into_iter(self) -> impl Iterator)> { self.keys .into_iter() @@ -137,11 +146,13 @@ impl Imports { /// Does the specified function hash key exist in this stack of imported [modules][Module]? #[allow(dead_code)] #[inline(always)] + #[must_use] pub fn contains_fn(&self, hash: u64) -> bool { self.modules.iter().any(|m| m.contains_qualified_fn(hash)) } /// Get specified function via its hash key. #[inline(always)] + #[must_use] pub fn get_fn(&self, hash: u64) -> Option<(&CallableFunction, Option<&Identifier>)> { self.modules .iter() @@ -152,11 +163,13 @@ impl Imports { /// imported [modules][Module]? #[allow(dead_code)] #[inline(always)] + #[must_use] pub fn contains_iter(&self, id: TypeId) -> bool { self.modules.iter().any(|m| m.contains_qualified_iter(id)) } /// Get the specified [`TypeId`][std::any::TypeId] iterator. #[inline(always)] + #[must_use] pub fn get_iter(&self, id: TypeId) -> Option { self.modules .iter() @@ -278,6 +291,7 @@ impl ChainArgument { /// Panics if not `ChainArgument::IndexValue`. #[inline(always)] #[cfg(not(feature = "no_index"))] + #[must_use] pub fn as_index_value(self) -> Dynamic { match self { #[cfg(not(feature = "no_object"))] @@ -294,6 +308,7 @@ impl ChainArgument { /// Panics if not `ChainArgument::MethodCallArgs`. #[inline(always)] #[cfg(not(feature = "no_object"))] + #[must_use] pub fn as_fn_call_args(self) -> (StaticVec, Position) { match self { Self::Property(_) => { @@ -349,6 +364,7 @@ impl<'a> Target<'a> { /// Is the `Target` a reference pointing to other data? #[allow(dead_code)] #[inline(always)] + #[must_use] pub fn is_ref(&self) -> bool { match self { Self::RefMut(_) => true, @@ -363,6 +379,7 @@ impl<'a> Target<'a> { } /// Is the `Target` a temp value? #[inline(always)] + #[must_use] pub fn is_temp_value(&self) -> bool { match self { Self::RefMut(_) => false, @@ -378,6 +395,7 @@ impl<'a> Target<'a> { /// Is the `Target` a shared value? #[cfg(not(feature = "no_closure"))] #[inline(always)] + #[must_use] pub fn is_shared(&self) -> bool { match self { Self::RefMut(r) => r.is_shared(), @@ -393,6 +411,7 @@ impl<'a> Target<'a> { /// Is the `Target` a specific type? #[allow(dead_code)] #[inline(always)] + #[must_use] pub fn is(&self) -> bool { match self { Self::RefMut(r) => r.is::(), @@ -407,6 +426,7 @@ impl<'a> Target<'a> { } /// Get the value of the `Target` as a `Dynamic`, cloning a referenced value if necessary. #[inline(always)] + #[must_use] pub fn take_or_clone(self) -> Dynamic { match self { Self::RefMut(r) => r.clone(), // Referenced value is cloned @@ -421,6 +441,7 @@ impl<'a> Target<'a> { } /// Take a `&mut Dynamic` reference from the `Target`. #[inline(always)] + #[must_use] pub fn take_ref(self) -> Option<&'a mut Dynamic> { match self { Self::RefMut(r) => Some(r), @@ -429,12 +450,14 @@ impl<'a> Target<'a> { } /// Convert a shared or reference `Target` into a target with an owned value. #[inline(always)] + #[must_use] pub fn into_owned(self) -> Target<'static> { self.take_or_clone().into() } /// Propagate a changed value back to the original source. /// This has no effect except for string indexing. #[inline(always)] + #[must_use] pub fn propagate_changed_value(&mut self) -> Result<(), Box> { match self { Self::RefMut(_) | Self::TempValue(_) => (), @@ -566,6 +589,7 @@ impl AsMut for Target<'_> { impl> From for Target<'_> { #[inline(always)] + #[must_use] fn from(value: T) -> Self { Self::TempValue(value.into()) } @@ -614,11 +638,13 @@ pub struct State { impl State { /// Is the state currently at global (root) level? #[inline(always)] + #[must_use] pub fn is_global(&self) -> bool { self.scope_level == 0 } /// Get a mutable reference to the current function resolution cache. #[inline(always)] + #[must_use] pub fn fn_resolution_cache_mut(&mut self) -> &mut FnResolutionCache { if self.fn_resolution_caches.0.is_empty() { // Push a new function resolution cache if the stack is empty @@ -714,27 +740,32 @@ pub struct EvalContext<'a, 'x, 'px, 'm, 's, 't, 'pt> { impl<'x, 'px> EvalContext<'_, 'x, 'px, '_, '_, '_, '_> { /// The current [`Engine`]. #[inline(always)] + #[must_use] pub fn engine(&self) -> &Engine { self.engine } /// The current source. #[inline(always)] + #[must_use] pub fn source(&self) -> Option<&str> { self.state.source.as_ref().map(|s| s.as_str()) } /// The current [`Scope`]. #[inline(always)] + #[must_use] pub fn scope(&self) -> &Scope { self.scope } /// Mutable reference to the current [`Scope`]. #[inline(always)] + #[must_use] pub fn scope_mut(&mut self) -> &mut &'x mut Scope<'px> { &mut self.scope } /// 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() } @@ -743,11 +774,13 @@ impl<'x, 'px> EvalContext<'_, 'x, 'px, '_, '_, '_, '_> { #[cfg(feature = "internals")] #[cfg(not(feature = "no_module"))] #[inline(always)] + #[must_use] pub fn imports(&self) -> &Imports { self.mods } /// 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() } @@ -755,16 +788,19 @@ impl<'x, 'px> EvalContext<'_, 'x, 'px, '_, '_, '_, '_> { /// Exported under the `internals` feature only. #[cfg(feature = "internals")] #[inline(always)] + #[must_use] pub fn namespaces(&self) -> &[&Module] { self.lib } /// The current bound `this` pointer, if any. #[inline(always)] + #[must_use] pub fn this_ptr(&self) -> Option<&Dynamic> { self.this_ptr.as_ref().map(|v| &**v) } /// The current nesting level of function calls. #[inline(always)] + #[must_use] pub fn call_level(&self) -> usize { self.level } @@ -853,6 +889,7 @@ impl Default for Engine { /// Make getter function #[cfg(not(feature = "no_object"))] #[inline(always)] +#[must_use] pub fn make_getter(id: &str) -> String { format!("{}{}", FN_GET, id) } @@ -860,6 +897,7 @@ pub fn make_getter(id: &str) -> String { /// Make setter function #[cfg(not(feature = "no_object"))] #[inline(always)] +#[must_use] pub fn make_setter(id: &str) -> String { format!("{}{}", FN_SET, id) } @@ -867,6 +905,7 @@ pub fn make_setter(id: &str) -> String { /// Is this function an anonymous function? #[cfg(not(feature = "no_function"))] #[inline(always)] +#[must_use] pub fn is_anonymous_fn(fn_name: &str) -> bool { fn_name.starts_with(FN_ANONYMOUS) } @@ -896,6 +935,7 @@ fn default_debug(_s: &str, _source: Option<&str>, _pos: Position) { impl Engine { /// Create a new [`Engine`] #[inline] + #[must_use] pub fn new() -> Self { // Create the new scripting Engine let mut engine = Self { @@ -959,6 +999,7 @@ impl Engine { /// /// Use [`register_global_module`][Engine::register_global_module] to add packages of functions. #[inline(always)] + #[must_use] pub fn new_raw() -> Self { let mut engine = Self { global_namespace: Default::default(), @@ -1009,6 +1050,7 @@ impl Engine { /// Search for a module within an imports stack. #[inline] + #[must_use] pub(crate) fn search_imports( &self, mods: &Imports, @@ -1042,6 +1084,7 @@ 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, @@ -1059,17 +1102,17 @@ impl Engine { // Normal variable access (_, None, _) => self.search_scope_only(scope, mods, state, lib, this_ptr, expr), // Qualified variable - (_, Some((hash_var, modules)), var_name) => { - let module = self.search_imports(mods, state, modules).ok_or_else(|| { + (_, Some((namespace, hash_var)), var_name) => { + let module = self.search_imports(mods, state, namespace).ok_or_else(|| { EvalAltResult::ErrorModuleNotFound( - modules[0].name.to_string(), - modules[0].pos, + namespace[0].name.to_string(), + namespace[0].pos, ) })?; let target = module.get_qualified_var(*hash_var).map_err(|mut err| { match *err { EvalAltResult::ErrorVariableNotFound(ref mut err_name, _) => { - *err_name = format!("{}{}", modules, var_name); + *err_name = format!("{}{}", namespace, var_name); } _ => (), } @@ -1091,6 +1134,7 @@ impl Engine { /// # Panics /// /// Panics if `expr` is not [`Expr::Variable`]. + #[must_use] pub(crate) fn search_scope_only<'s>( &self, scope: &'s mut Scope, @@ -1128,16 +1172,18 @@ impl Engine { this_ptr, level: 0, }; - if let Some(mut result) = resolve_var( + match resolve_var( expr.get_variable_name(true) .expect("`expr` should be `Variable`"), index, &context, - ) - .map_err(|err| err.fill_position(var_pos))? - { - result.set_access_mode(AccessMode::ReadOnly); - return Ok((result.into(), var_pos)); + ) { + Ok(Some(mut result)) => { + result.set_access_mode(AccessMode::ReadOnly); + return Ok((result.into(), var_pos)); + } + Ok(None) => (), + Err(err) => return Err(err.fill_position(var_pos)), } } @@ -1162,6 +1208,7 @@ 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, @@ -1275,9 +1322,9 @@ impl Engine { // 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 mut args = idx_val.as_fn_call_args(); + let args = &mut idx_val.as_fn_call_args(); self.make_method_call( - mods, state, lib, name, *hashes, target, &mut args, *pos, level, + mods, state, lib, name, *hashes, target, args, *pos, level, ) } // xxx.fn_name(...) = ??? @@ -1295,11 +1342,11 @@ impl Engine { new_val.expect("never fails because `new_val` is `Some`"); let index = name.into(); { - let mut val = self.get_indexed_mut( + let val_target = &mut self.get_indexed_mut( mods, state, lib, target, index, *pos, true, false, level, )?; self.eval_op_assignment( - mods, state, lib, op_info, op_pos, &mut val, root, new_val, + mods, state, lib, op_info, op_pos, val_target, root, new_val, ) .map_err(|err| err.fill_position(new_pos))?; } @@ -1324,11 +1371,11 @@ impl Engine { if op_info.is_some() { let hash = FnCallHashes::from_native(*hash_get); - let mut args = [target.as_mut()]; + let args = &mut [target.as_mut()]; let (mut orig_val, _) = self .exec_fn_call( - mods, state, lib, getter, hash, &mut args, is_ref, true, *pos, - None, level, + mods, state, lib, getter, hash, args, is_ref, true, *pos, None, + level, ) .or_else(|err| match *err { // Try an indexer if property does not exist @@ -1368,18 +1415,17 @@ impl Engine { } let hash = FnCallHashes::from_native(*hash_set); - let mut args = [target.as_mut(), &mut new_val]; + let args = &mut [target.as_mut(), &mut new_val]; self.exec_fn_call( - mods, state, lib, setter, hash, &mut args, is_ref, true, *pos, None, - level, + mods, state, lib, setter, hash, args, is_ref, true, *pos, None, level, ) .or_else(|err| match *err { // Try an indexer if property does not exist EvalAltResult::ErrorDotExpr(_, _) => { - let mut prop = name.into(); - let args = &mut [target, &mut prop, &mut new_val]; + let args = &mut [target, &mut name.into(), &mut new_val]; let hash_set = FnCallHashes::from_native(crate::calc_fn_hash(FN_IDX_SET, 3)); + self.exec_fn_call( mods, state, lib, FN_IDX_SET, hash_set, args, is_ref, true, *pos, None, level, @@ -1398,10 +1444,9 @@ impl Engine { Expr::Property(x) => { let ((getter, hash_get), _, (name, pos)) = x.as_ref(); let hash = FnCallHashes::from_native(*hash_get); - let mut args = [target.as_mut()]; + let args = &mut [target.as_mut()]; self.exec_fn_call( - mods, state, lib, getter, hash, &mut args, is_ref, true, *pos, None, - level, + mods, state, lib, getter, hash, args, is_ref, true, *pos, None, level, ) .map_or_else( |err| match *err { @@ -1426,8 +1471,8 @@ 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::() => { - let mut val = match &x.lhs { - Expr::Property(p) => { + let val_target = &mut match x.lhs { + Expr::Property(ref p) => { let (name, pos) = &p.2; let index = name.into(); self.get_indexed_mut( @@ -1435,11 +1480,11 @@ impl Engine { )? } // {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr - Expr::FnCall(x, pos) if !x.is_qualified() => { + Expr::FnCall(ref x, pos) if !x.is_qualified() => { let FnCallExpr { name, hashes, .. } = x.as_ref(); - let mut args = idx_val.as_fn_call_args(); + let args = &mut idx_val.as_fn_call_args(); let (val, _) = self.make_method_call( - mods, state, lib, name, *hashes, target, &mut args, *pos, level, + mods, state, lib, name, *hashes, target, args, pos, level, )?; val.into() } @@ -1448,21 +1493,21 @@ impl Engine { "function call in dot chain should not be namespace-qualified" ), // Others - syntax error - expr => unreachable!("invalid dot expression: {:?}", expr), + ref expr => unreachable!("invalid dot expression: {:?}", expr), }; let rhs_chain = match_chain_type(rhs); self.eval_dot_index_chain_helper( - mods, state, lib, this_ptr, &mut val, root, &x.rhs, idx_values, + mods, state, lib, this_ptr, val_target, root, &x.rhs, 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) => { - match &x.lhs { + match x.lhs { // xxx.prop[expr] | xxx.prop.expr - Expr::Property(p) => { + Expr::Property(ref p) => { let ((getter, hash_get), (setter, hash_set), (name, pos)) = p.as_ref(); let rhs_chain = match_chain_type(rhs); @@ -1525,8 +1570,8 @@ impl Engine { |err| match *err { // Try an indexer if property does not exist EvalAltResult::ErrorDotExpr(_, _) => { - let mut prop = name.into(); - let args = &mut [target.as_mut(), &mut prop, val]; + let args = + &mut [target.as_mut(), &mut name.into(), val]; let hash_set = FnCallHashes::from_native( crate::calc_fn_hash(FN_IDX_SET, 3), ); @@ -1553,12 +1598,12 @@ impl Engine { Ok((result, may_be_changed)) } // xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr - Expr::FnCall(f, pos) if !f.is_qualified() => { + Expr::FnCall(ref f, pos) if !f.is_qualified() => { let FnCallExpr { name, hashes, .. } = f.as_ref(); let rhs_chain = match_chain_type(rhs); - let mut args = idx_val.as_fn_call_args(); + let args = &mut idx_val.as_fn_call_args(); let (mut val, _) = self.make_method_call( - mods, state, lib, name, *hashes, target, &mut args, *pos, level, + mods, state, lib, name, *hashes, target, args, pos, level, )?; let val = &mut val; let target = &mut val.into(); @@ -1567,14 +1612,14 @@ impl Engine { mods, state, lib, this_ptr, target, root, &x.rhs, idx_values, rhs_chain, level, new_val, ) - .map_err(|err| err.fill_position(*pos)) + .map_err(|err| err.fill_position(pos)) } // xxx.module::fn_name(...) - syntax error Expr::FnCall(_, _) => unreachable!( "function call in dot chain should not be namespace-qualified" ), // Others - syntax error - expr => unreachable!("invalid dot expression: {:?}", expr), + ref expr => unreachable!("invalid dot expression: {:?}", expr), } } // Syntax error @@ -1586,6 +1631,8 @@ 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, @@ -1649,6 +1696,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( &self, scope: &mut Scope, @@ -1774,6 +1822,7 @@ 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, @@ -1795,7 +1844,7 @@ impl Engine { // val_array[idx] let index = idx .as_int() - .map_err(|err| self.make_type_mismatch_err::(err, idx_pos))?; + .map_err(|typ| self.make_type_mismatch_err::(typ, idx_pos))?; let arr_len = arr.len(); @@ -1854,7 +1903,7 @@ impl Engine { // val_int[idx] let index = idx .as_int() - .map_err(|err| self.make_type_mismatch_err::(err, idx_pos))?; + .map_err(|typ| self.make_type_mismatch_err::(typ, idx_pos))?; let bits = std::mem::size_of_val(value) * 8; @@ -1891,7 +1940,7 @@ impl Engine { // val_string[idx] let index = idx .as_int() - .map_err(|err| self.make_type_mismatch_err::(err, idx_pos))?; + .map_err(|typ| self.make_type_mismatch_err::(typ, idx_pos))?; let (ch, offset) = if index >= 0 { let offset = index as usize; @@ -1939,6 +1988,7 @@ impl Engine { } /// Evaluate an expression. + #[must_use] pub(crate) fn eval_expr( &self, scope: &mut Scope, @@ -2059,7 +2109,7 @@ impl Engine { let namespace = namespace .as_ref() .expect("never fails because function call is qualified"); - let hash = hashes.native_hash(); + let hash = hashes.native; self.make_qualified_function_call( scope, mods, state, lib, this_ptr, namespace, name, args, constants, hash, *pos, level, @@ -2086,12 +2136,12 @@ impl Engine { Ok((self .eval_expr(scope, mods, state, lib, this_ptr, &x.lhs, level)? .as_bool() - .map_err(|err| self.make_type_mismatch_err::(err, x.lhs.position()))? + .map_err(|typ| self.make_type_mismatch_err::(typ, x.lhs.position()))? && // Short-circuit using && self .eval_expr(scope, mods, state, lib, this_ptr, &x.rhs, level)? .as_bool() - .map_err(|err| self.make_type_mismatch_err::(err, x.rhs.position()))?) + .map_err(|typ| self.make_type_mismatch_err::(typ, x.rhs.position()))?) .into()) } @@ -2099,12 +2149,12 @@ impl Engine { Ok((self .eval_expr(scope, mods, state, lib, this_ptr, &x.lhs, level)? .as_bool() - .map_err(|err| self.make_type_mismatch_err::(err, x.lhs.position()))? + .map_err(|typ| self.make_type_mismatch_err::(typ, x.lhs.position()))? || // Short-circuit using || self .eval_expr(scope, mods, state, lib, this_ptr, &x.rhs, level)? .as_bool() - .map_err(|err| self.make_type_mismatch_err::(err, x.rhs.position()))?) + .map_err(|typ| self.make_type_mismatch_err::(typ, x.rhs.position()))?) .into()) } @@ -2139,6 +2189,7 @@ impl Engine { } /// Evaluate a statements block. + #[must_use] pub(crate) fn eval_stmt_block( &self, scope: &mut Scope, @@ -2216,6 +2267,7 @@ 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, @@ -2289,6 +2341,7 @@ 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, @@ -2397,7 +2450,7 @@ impl Engine { let guard_val = self .eval_expr(scope, mods, state, lib, this_ptr, expr, level)? .as_bool() - .map_err(|err| self.make_type_mismatch_err::(err, expr.position()))?; + .map_err(|typ| self.make_type_mismatch_err::(typ, expr.position()))?; if guard_val { if !x.0.is_empty() { @@ -2430,9 +2483,9 @@ impl Engine { match self .eval_expr(scope, mods, state, lib, this_ptr, &condition, level) .and_then(|v| { - v.as_bool().map_err(|err| { + v.as_bool().map_err(|typ| { self.make_type_mismatch_err::( - err, + typ, condition.position(), ) }) @@ -2474,7 +2527,7 @@ impl Engine { let condition = if !expr.is_unit() { self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? .as_bool() - .map_err(|err| self.make_type_mismatch_err::(err, expr.position()))? + .map_err(|typ| self.make_type_mismatch_err::(typ, expr.position()))? } else { true }; @@ -2512,7 +2565,7 @@ impl Engine { let condition = self .eval_expr(scope, mods, state, lib, this_ptr, expr, level)? .as_bool() - .map_err(|err| self.make_type_mismatch_err::(err, expr.position()))?; + .map_err(|typ| self.make_type_mismatch_err::(typ, expr.position()))?; if condition ^ *is_while { return Ok(Dynamic::UNIT); @@ -2649,7 +2702,7 @@ impl Engine { let namespace = namespace .as_ref() .expect("never fails because function call is qualified"); - let hash = hashes.native_hash(); + let hash = hashes.native; self.make_qualified_function_call( scope, mods, state, lib, this_ptr, namespace, name, args, constants, hash, *pos, level, @@ -2673,7 +2726,7 @@ impl Engine { } // Try/Catch statement - Stmt::TryCatch(x, _, _) => { + Stmt::TryCatch(x, _) => { let (try_stmt, err_var, catch_stmt) = x.as_ref(); let result = self @@ -2898,11 +2951,13 @@ impl Engine { // Export statement #[cfg(not(feature = "no_module"))] Stmt::Export(list, _) => { - for (Ident { name, pos, .. }, rename) in list.iter() { + for (Ident { name, pos, .. }, Ident { name: rename, .. }) in list.as_ref() { // Mark scope variables as public - if let Some(index) = scope.get_index(name).map(|(i, _)| i) { - let alias = rename.as_ref().map(|x| &x.name).unwrap_or_else(|| name); - scope.add_entry_alias(index, alias.clone()); + if let Some((index, _)) = scope.get_index(name) { + scope.add_entry_alias( + index, + if rename.is_empty() { name } else { rename }.clone(), + ); } else { return EvalAltResult::ErrorVariableNotFound(name.to_string(), *pos).into(); } @@ -2932,6 +2987,7 @@ 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 } @@ -2939,17 +2995,20 @@ 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) { @@ -3052,6 +3111,7 @@ 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 State, @@ -3080,6 +3140,7 @@ impl Engine { /// If a type is registered via [`register_type_with_name`][Engine::register_type_with_name], /// the type name provided for the registration will be used. #[inline(always)] + #[must_use] pub fn map_type_name<'a>(&'a self, name: &'a str) -> &'a str { self.type_names .get(name) @@ -3089,6 +3150,7 @@ impl Engine { /// Make a `Box<`[`EvalAltResult`][EvalAltResult::ErrorMismatchDataType]`>`. #[inline(always)] + #[must_use] pub(crate) fn make_type_mismatch_err(&self, typ: &str, pos: Position) -> Box { EvalAltResult::ErrorMismatchDataType( self.map_type_name(type_name::()).into(), diff --git a/src/engine_api.rs b/src/engine_api.rs index 7d97e884..9aba5f05 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -5,7 +5,7 @@ use crate::engine::{EvalContext, Imports, State}; use crate::fn_native::{FnCallArgs, SendSync}; use crate::fn_register::RegisterNativeFunction; use crate::optimize::OptimizationLevel; -use crate::parser::ParseState; +use crate::parse::ParseState; use crate::{ scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, Identifier, Module, NativeCallContext, ParseError, Position, RhaiResult, Shared, AST, @@ -1031,6 +1031,7 @@ impl Engine { /// # } /// ``` #[inline(always)] + #[must_use] pub fn compile(&self, script: &str) -> Result { self.compile_with_scope(&Default::default(), script) } @@ -1073,6 +1074,7 @@ impl Engine { /// # } /// ``` #[inline(always)] + #[must_use] pub fn compile_with_scope(&self, scope: &Scope, script: &str) -> Result { self.compile_scripts_with_scope(scope, &[script]) } @@ -1086,6 +1088,7 @@ 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, @@ -1197,6 +1200,7 @@ impl Engine { /// # } /// ``` #[inline(always)] + #[must_use] pub fn compile_scripts_with_scope( &self, scope: &Scope, @@ -1206,6 +1210,7 @@ impl Engine { } /// Join a list of strings and compile into an [`AST`] using own scope at a specific optimization level. #[inline(always)] + #[must_use] pub(crate) fn compile_with_scope_and_optimization_level( &self, scope: &Scope, @@ -1224,6 +1229,7 @@ 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; @@ -1279,6 +1285,7 @@ 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) } @@ -1318,6 +1325,7 @@ 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, @@ -1368,6 +1376,7 @@ impl Engine { /// # } /// ``` #[cfg(not(feature = "no_object"))] + #[must_use] pub fn parse_json( &self, json: impl AsRef, @@ -1443,6 +1452,7 @@ impl Engine { /// # } /// ``` #[inline(always)] + #[must_use] pub fn compile_expression(&self, script: &str) -> Result { self.compile_expression_with_scope(&Default::default(), script) } @@ -1486,6 +1496,7 @@ impl Engine { /// # } /// ``` #[inline(always)] + #[must_use] pub fn compile_expression_with_scope( &self, scope: &Scope, @@ -1518,6 +1529,7 @@ 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, @@ -1548,6 +1560,7 @@ 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, @@ -1570,6 +1583,7 @@ impl Engine { /// # } /// ``` #[inline(always)] + #[must_use] pub fn eval(&self, script: &str) -> Result> { self.eval_with_scope(&mut Default::default(), script) } @@ -1596,6 +1610,7 @@ impl Engine { /// # } /// ``` #[inline(always)] + #[must_use] pub fn eval_with_scope( &self, scope: &mut Scope, @@ -1623,6 +1638,7 @@ impl Engine { /// # } /// ``` #[inline(always)] + #[must_use] pub fn eval_expression( &self, script: &str, @@ -1648,6 +1664,7 @@ impl Engine { /// # } /// ``` #[inline(always)] + #[must_use] pub fn eval_expression_with_scope( &self, scope: &mut Scope, @@ -1686,6 +1703,7 @@ impl Engine { /// # } /// ``` #[inline(always)] + #[must_use] pub fn eval_ast(&self, ast: &AST) -> Result> { self.eval_ast_with_scope(&mut Default::default(), ast) } @@ -1719,6 +1737,7 @@ impl Engine { /// # } /// ``` #[inline(always)] + #[must_use] pub fn eval_ast_with_scope( &self, scope: &mut Scope, @@ -1741,6 +1760,7 @@ impl Engine { } /// Evaluate an [`AST`] with own scope. #[inline(always)] + #[must_use] pub(crate) fn eval_ast_with_scope_raw<'a>( &self, scope: &mut Scope, @@ -1889,6 +1909,7 @@ impl Engine { /// ``` #[cfg(not(feature = "no_function"))] #[inline(always)] + #[must_use] pub fn call_fn( &self, scope: &mut Scope, @@ -1968,6 +1989,7 @@ impl Engine { /// ``` #[cfg(not(feature = "no_function"))] #[inline(always)] + #[must_use] pub fn call_fn_dynamic( &self, scope: &mut Scope, @@ -1991,6 +2013,7 @@ impl Engine { /// clone them _before_ calling this function. #[cfg(not(feature = "no_function"))] #[inline(always)] + #[must_use] pub(crate) fn call_fn_dynamic_raw( &self, scope: &mut Scope, @@ -2048,6 +2071,7 @@ impl Engine { /// Then, the [`AST`] is cloned and the copy re-optimized before running. #[cfg(not(feature = "no_optimize"))] #[inline(always)] + #[must_use] pub fn optimize_ast( &self, scope: &Scope, @@ -2076,6 +2100,7 @@ impl Engine { /// 2) Functions in registered sub-modules /// 3) Functions in packages (optional) #[cfg(feature = "metadata")] + #[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 abce7e33..fb16e9fd 100644 --- a/src/engine_settings.rs +++ b/src/engine_settings.rs @@ -28,6 +28,7 @@ impl Engine { /// Not available under `no_optimize`. #[cfg(not(feature = "no_optimize"))] #[inline(always)] + #[must_use] pub fn optimization_level(&self) -> crate::OptimizationLevel { self.optimization_level } @@ -48,6 +49,7 @@ impl Engine { #[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "no_function"))] #[inline(always)] + #[must_use] pub fn max_call_levels(&self) -> usize { self.limits.max_call_stack_depth } @@ -66,6 +68,7 @@ impl Engine { /// Not available under `unchecked`. #[cfg(not(feature = "unchecked"))] #[inline(always)] + #[must_use] pub fn max_operations(&self) -> u64 { self.limits.max_operations.map_or(0, NonZeroU64::get) } @@ -85,6 +88,7 @@ impl Engine { #[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "no_module"))] #[inline(always)] + #[must_use] pub fn max_modules(&self) -> usize { self.limits.max_modules } @@ -110,6 +114,7 @@ impl Engine { /// Not available under `unchecked`. #[cfg(not(feature = "unchecked"))] #[inline(always)] + #[must_use] pub fn max_expr_depth(&self) -> usize { self.limits.max_expr_depth.map_or(0, NonZeroUsize::get) } @@ -119,6 +124,7 @@ impl Engine { #[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "no_function"))] #[inline(always)] + #[must_use] pub fn max_function_expr_depth(&self) -> usize { self.limits .max_function_expr_depth @@ -138,6 +144,7 @@ impl Engine { /// Not available under `unchecked`. #[cfg(not(feature = "unchecked"))] #[inline(always)] + #[must_use] pub fn max_string_size(&self) -> usize { self.limits.max_string_size.map_or(0, NonZeroUsize::get) } @@ -157,6 +164,7 @@ impl Engine { #[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "no_index"))] #[inline(always)] + #[must_use] pub fn max_array_size(&self) -> usize { self.limits.max_array_size.map_or(0, NonZeroUsize::get) } @@ -176,6 +184,7 @@ impl Engine { #[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "no_object"))] #[inline(always)] + #[must_use] pub fn max_map_size(&self) -> usize { self.limits.max_map_size.map_or(0, NonZeroUsize::get) } @@ -260,6 +269,7 @@ impl Engine { /// # Ok(()) /// # } /// ``` + #[must_use] pub fn register_custom_operator( &mut self, keyword: impl AsRef + Into, diff --git a/src/result.rs b/src/error.rs similarity index 97% rename from src/result.rs rename to src/error.rs index 3b3f08f7..ea518647 100644 --- a/src/result.rs +++ b/src/error.rs @@ -95,6 +95,7 @@ pub enum EvalAltResult { } impl EvalAltResult { + #[must_use] pub(crate) fn desc(&self) -> &str { match self { #[allow(deprecated)] @@ -275,6 +276,7 @@ impl EvalAltResult { /// Is this a pseudo error? A pseudo error is one that does not occur naturally. /// /// [`LoopBreak`][EvalAltResult::LoopBreak] and [`Return`][EvalAltResult::Return] are pseudo errors. + #[must_use] pub fn is_pseudo_error(&self) -> bool { match self { Self::LoopBreak(_, _) | Self::Return(_, _) => true, @@ -286,6 +288,7 @@ impl EvalAltResult { /// # Panics /// /// Panics when [`LoopBreak`][EvalAltResult::LoopBreak] or [`Return`][EvalAltResult::Return]. + #[must_use] pub fn is_catchable(&self) -> bool { match self { Self::ErrorSystem(_, _) => false, @@ -325,6 +328,7 @@ impl EvalAltResult { /// # Panics /// /// Panics when [`LoopBreak`][EvalAltResult::LoopBreak] or [`Return`][EvalAltResult::Return]. + #[must_use] pub fn is_system_exception(&self) -> bool { match self { Self::ErrorSystem(_, _) => true, @@ -412,6 +416,7 @@ impl EvalAltResult { }; } /// Get the [position][Position] of this error. + #[must_use] pub fn position(&self) -> Position { match self { Self::ErrorSystem(_, _) => Position::NONE, @@ -444,6 +449,12 @@ impl EvalAltResult { | Self::Return(_, pos) => *pos, } } + /// Remove the [position][Position] information from this error. + /// + /// The [position][Position] of this error is set to [`NONE`][Position::NONE] afterwards. + pub fn clear_position(&mut self) -> &mut Self { + self.set_position(Position::NONE) + } /// Remove the [position][Position] information from this error and return it. /// /// The [position][Position] of this error is set to [`NONE`][Position::NONE] afterwards. @@ -453,7 +464,7 @@ impl EvalAltResult { pos } /// Override the [position][Position] of this error. - pub fn set_position(&mut self, new_position: Position) { + pub fn set_position(&mut self, new_position: Position) -> &mut Self { match self { Self::ErrorSystem(_, _) => (), @@ -484,10 +495,12 @@ impl EvalAltResult { | Self::LoopBreak(_, pos) | Self::Return(_, pos) => *pos = new_position, } + self } /// Consume the current [`EvalAltResult`] and return a new one with the specified [`Position`] /// if the current position is [`Position::None`]. #[inline(always)] + #[must_use] pub(crate) fn fill_position(mut self: Box, new_position: Position) -> Box { if self.position().is_none() { self.set_position(new_position); diff --git a/src/parse_error.rs b/src/error_parsing.rs similarity index 99% rename from src/parse_error.rs rename to src/error_parsing.rs index 468cbd79..795b85f3 100644 --- a/src/parse_error.rs +++ b/src/error_parsing.rs @@ -58,6 +58,7 @@ impl fmt::Display for LexError { } impl LexError { + #[must_use] pub(crate) fn desc(&self) -> &str { match self { Self::UnexpectedInput(_) => "Unexpected character encountered", @@ -72,6 +73,7 @@ impl LexError { } /// Convert a [`LexError`] into a [`ParseError`]. #[inline(always)] + #[must_use] pub fn into_err(self, pos: Position) -> ParseError { ParseError(Box::new(self.into()), pos) } @@ -188,10 +190,12 @@ pub enum ParseErrorType { impl ParseErrorType { /// Make a [`ParseError`] using the current type and position. #[inline(always)] + #[must_use] pub(crate) fn into_err(self, pos: Position) -> ParseError { ParseError(Box::new(self), pos) } + #[must_use] pub(crate) fn desc(&self) -> &str { match self { Self::UnexpectedEOF => "Script is incomplete", diff --git a/src/fn_builtin.rs b/src/fn_builtin.rs index 4c86c32b..ed5a5bda 100644 --- a/src/fn_builtin.rs +++ b/src/fn_builtin.rs @@ -17,6 +17,7 @@ const BUILTIN: &str = "never fails because this is built-in code and the type is /// Is the type a numeric type? #[inline(always)] +#[must_use] fn is_numeric(type_id: TypeId) -> bool { let result = type_id == TypeId::of::() || type_id == TypeId::of::() @@ -40,6 +41,7 @@ fn is_numeric(type_id: TypeId) -> bool { } /// Build in common binary operator implementations to avoid the cost of calling a registered function. +#[must_use] pub fn get_builtin_binary_op_fn( op: &str, x: &Dynamic, @@ -54,91 +56,89 @@ pub fn get_builtin_binary_op_fn( // One of the operands is a custom type, so it is never built-in if x.is_variant() || y.is_variant() { - if is_numeric(type1) && is_numeric(type2) { + return if is_numeric(type1) && is_numeric(type2) { // Disallow comparisons between different numeric types - return None; - } - - // If the types are not the same, default to not compare - if type1 != type2 { - return match op { + None + } else if type1 != type2 { + // If the types are not the same, default to not compare + match op { "!=" => Some(|_, _| Ok(Dynamic::TRUE)), "==" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)), _ => None, - }; - } - - // Disallow comparisons between the same type - return None; + } + } else { + // Disallow comparisons between the same type + None + }; } let types_pair = (type1, type2); macro_rules! impl_op { ($xx:ident $op:tt $yy:ident) => { - return Some(|_, args| { + Some(|_, args| { let x = &*args[0].read_lock::<$xx>().expect(BUILTIN); let y = &*args[1].read_lock::<$yy>().expect(BUILTIN); Ok((x $op y).into()) }) }; ($xx:ident . $func:ident ( $yy:ty )) => { - return Some(|_, args| { + Some(|_, args| { let x = &*args[0].read_lock::<$xx>().expect(BUILTIN); let y = &*args[1].read_lock::<$yy>().expect(BUILTIN); Ok(x.$func(y).into()) }) }; ($xx:ident . $func:ident ( $yy:ident . $yyy:ident () )) => { - return Some(|_, args| { + Some(|_, args| { let x = &*args[0].read_lock::<$xx>().expect(BUILTIN); let y = &*args[1].read_lock::<$yy>().expect(BUILTIN); Ok(x.$func(y.$yyy()).into()) }) }; ($func:ident ( $op:tt )) => { - return Some(|_, args| { + Some(|_, args| { let (x, y) = $func(args); Ok((x $op y).into()) }) }; ($base:ty => $xx:ident $op:tt $yy:ident) => { - return Some(|_, args| { + Some(|_, args| { let x = args[0].$xx().expect(BUILTIN) as $base; let y = args[1].$yy().expect(BUILTIN) as $base; Ok((x $op y).into()) }) }; ($base:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty)) => { - return Some(|_, args| { + Some(|_, args| { let x = args[0].$xx().expect(BUILTIN) as $base; let y = args[1].$yy().expect(BUILTIN) as $base; Ok(x.$func(y as $yyy).into()) }) }; ($base:ty => $func:ident ( $xx:ident, $yy:ident )) => { - return Some(|_, args| { + Some(|_, args| { let x = args[0].$xx().expect(BUILTIN) as $base; let y = args[1].$yy().expect(BUILTIN) as $base; $func(x, y).map(Into::::into) }) }; (from $base:ty => $xx:ident $op:tt $yy:ident) => { - return Some(|_, args| { + Some(|_, args| { let x = <$base>::from(args[0].$xx().expect(BUILTIN)); let y = <$base>::from(args[1].$yy().expect(BUILTIN)); Ok((x $op y).into()) }) }; (from $base:ty => $xx:ident . $func:ident ( $yy:ident )) => { - return Some(|_, args| { + Some(|_, args| { let x = <$base>::from(args[0].$xx().expect(BUILTIN)); let y = <$base>::from(args[1].$yy().expect(BUILTIN)); Ok(x.$func(y).into()) }) }; (from $base:ty => $func:ident ( $xx:ident, $yy:ident )) => { - return Some(|_, args| { + Some(|_, args| { let x = <$base>::from(args[0].$xx().expect(BUILTIN)); let y = <$base>::from(args[1].$yy().expect(BUILTIN)); $func(x, y).map(Into::::into) @@ -150,7 +150,7 @@ pub fn get_builtin_binary_op_fn( ($x:ty, $xx:ident, $y:ty, $yy:ident) => { #[cfg(not(feature = "no_float"))] if types_pair == (TypeId::of::<$x>(), TypeId::of::<$y>()) { - match op { + return match op { "+" => impl_op!(FLOAT => $xx + $yy), "-" => impl_op!(FLOAT => $xx - $yy), "*" => impl_op!(FLOAT => $xx * $yy), @@ -163,8 +163,8 @@ pub fn get_builtin_binary_op_fn( ">=" => impl_op!(FLOAT => $xx >= $yy), "<" => impl_op!(FLOAT => $xx < $yy), "<=" => impl_op!(FLOAT => $xx <= $yy), - _ => return None, - } + _ => None, + }; } }; } @@ -177,41 +177,43 @@ pub fn get_builtin_binary_op_fn( ($x:ty, $xx:ident, $y:ty, $yy:ident) => { #[cfg(feature = "decimal")] if types_pair == (TypeId::of::<$x>(), TypeId::of::<$y>()) { - if cfg!(not(feature = "unBUILTIN")) { - use crate::packages::arithmetic::decimal_functions::*; + #[cfg(not(feature = "unchecked"))] + use crate::packages::arithmetic::decimal_functions::*; - match op { - "+" => impl_op!(from Decimal => add($xx, $yy)), - "-" => impl_op!(from Decimal => subtract($xx, $yy)), - "*" => impl_op!(from Decimal => multiply($xx, $yy)), - "/" => impl_op!(from Decimal => divide($xx, $yy)), - "%" => impl_op!(from Decimal => modulo($xx, $yy)), - "**" => impl_op!(from Decimal => power($xx, $yy)), - _ => () - } - } else { - use rust_decimal::MathematicalOps; - - match op { - "+" => impl_op!(from Decimal => $xx + $yy), - "-" => impl_op!(from Decimal => $xx - $yy), - "*" => impl_op!(from Decimal => $xx * $yy), - "/" => impl_op!(from Decimal => $xx / $yy), - "%" => impl_op!(from Decimal => $xx % $yy), - "**" => impl_op!(from Decimal => $xx.powd($yy)), - _ => () - } + #[cfg(not(feature = "unchecked"))] + match op { + "+" => return impl_op!(from Decimal => add($xx, $yy)), + "-" => return impl_op!(from Decimal => subtract($xx, $yy)), + "*" => return impl_op!(from Decimal => multiply($xx, $yy)), + "/" => return impl_op!(from Decimal => divide($xx, $yy)), + "%" => return impl_op!(from Decimal => modulo($xx, $yy)), + "**" => return impl_op!(from Decimal => power($xx, $yy)), + _ => () } + #[cfg(feature = "unchecked")] + use rust_decimal::MathematicalOps; + + #[cfg(feature = "unchecked")] match op { + "+" => return impl_op!(from Decimal => $xx + $yy), + "-" => return impl_op!(from Decimal => $xx - $yy), + "*" => return impl_op!(from Decimal => $xx * $yy), + "/" => return impl_op!(from Decimal => $xx / $yy), + "%" => return impl_op!(from Decimal => $xx % $yy), + "**" => return impl_op!(from Decimal => $xx.powd($yy)), + _ => () + } + + return match op { "==" => impl_op!(from Decimal => $xx == $yy), "!=" => impl_op!(from Decimal => $xx != $yy), ">" => impl_op!(from Decimal => $xx > $yy), ">=" => impl_op!(from Decimal => $xx >= $yy), "<" => impl_op!(from Decimal => $xx < $yy), "<=" => impl_op!(from Decimal => $xx <= $yy), - _ => return None - } + _ => None + }; } }; } @@ -232,22 +234,20 @@ pub fn get_builtin_binary_op_fn( (s1, s2) } - match op { - "+" => { - return Some(|_, args| { - let x = args[0].as_char().expect(BUILTIN); - let y = &*args[1].read_lock::().expect(BUILTIN); - Ok(format!("{}{}", x, y).into()) - }) - } + return match op { + "+" => Some(|_, args| { + let x = args[0].as_char().expect(BUILTIN); + let y = &*args[1].read_lock::().expect(BUILTIN); + Ok(format!("{}{}", x, y).into()) + }), "==" => impl_op!(get_s1s2(==)), "!=" => impl_op!(get_s1s2(!=)), ">" => impl_op!(get_s1s2(>)), ">=" => impl_op!(get_s1s2(>=)), "<" => impl_op!(get_s1s2(<)), "<=" => impl_op!(get_s1s2(<=)), - _ => return None, - } + _ => None, + }; } // string op char if types_pair == (TypeId::of::(), TypeId::of::()) { @@ -261,36 +261,30 @@ pub fn get_builtin_binary_op_fn( (s1, s2) } - match op { - "+" => { - return Some(|_, args| { - let x = &*args[0].read_lock::().expect(BUILTIN); - let y = args[1].as_char().expect(BUILTIN); - Ok((x + y).into()) - }) - } - "-" => { - return Some(|_, args| { - let x = &*args[0].read_lock::().expect(BUILTIN); - let y = args[1].as_char().expect(BUILTIN); - Ok((x - y).into()) - }) - } + return match op { + "+" => Some(|_, args| { + let x = &*args[0].read_lock::().expect(BUILTIN); + let y = args[1].as_char().expect(BUILTIN); + Ok((x + y).into()) + }), + "-" => Some(|_, args| { + let x = &*args[0].read_lock::().expect(BUILTIN); + let y = args[1].as_char().expect(BUILTIN); + Ok((x - y).into()) + }), "==" => impl_op!(get_s1s2(==)), "!=" => impl_op!(get_s1s2(!=)), ">" => impl_op!(get_s1s2(>)), ">=" => impl_op!(get_s1s2(>=)), "<" => impl_op!(get_s1s2(<)), "<=" => impl_op!(get_s1s2(<=)), - OP_CONTAINS => { - return Some(|_, args| { - let s = &*args[0].read_lock::().expect(BUILTIN); - let c = args[1].as_char().expect(BUILTIN); - Ok((s.contains(c)).into()) - }) - } - _ => return None, - } + OP_CONTAINS => Some(|_, args| { + let s = &*args[0].read_lock::().expect(BUILTIN); + let c = args[1].as_char().expect(BUILTIN); + Ok((s.contains(c)).into()) + }), + _ => None, + }; } // map op string @@ -298,10 +292,10 @@ pub fn get_builtin_binary_op_fn( if types_pair == (TypeId::of::(), TypeId::of::()) { use crate::Map; - match op { + return match op { OP_CONTAINS => impl_op!(Map.contains_key(ImmutableString.as_str())), - _ => return None, - } + _ => None, + }; } // Default comparison operators for different types @@ -316,35 +310,36 @@ pub fn get_builtin_binary_op_fn( // Beyond here, type1 == type2 if type1 == TypeId::of::() { - if cfg!(not(feature = "unBUILTIN")) { - use crate::packages::arithmetic::arith_basic::INT::functions::*; + #[cfg(not(feature = "unchecked"))] + use crate::packages::arithmetic::arith_basic::INT::functions::*; - match op { - "+" => impl_op!(INT => add(as_int, as_int)), - "-" => impl_op!(INT => subtract(as_int, as_int)), - "*" => impl_op!(INT => multiply(as_int, as_int)), - "/" => impl_op!(INT => divide(as_int, as_int)), - "%" => impl_op!(INT => modulo(as_int, as_int)), - "**" => impl_op!(INT => power(as_int, as_int)), - ">>" => impl_op!(INT => shift_right(as_int, as_int)), - "<<" => impl_op!(INT => shift_left(as_int, as_int)), - _ => (), - } - } else { - match op { - "+" => impl_op!(INT => as_int + as_int), - "-" => impl_op!(INT => as_int - as_int), - "*" => impl_op!(INT => as_int * as_int), - "/" => impl_op!(INT => as_int / as_int), - "%" => impl_op!(INT => as_int % as_int), - "**" => impl_op!(INT => as_int.pow(as_int as u32)), - ">>" => impl_op!(INT => as_int >> as_int), - "<<" => impl_op!(INT => as_int << as_int), - _ => (), - } + #[cfg(not(feature = "unchecked"))] + match op { + "+" => return impl_op!(INT => add(as_int, as_int)), + "-" => return impl_op!(INT => subtract(as_int, as_int)), + "*" => return impl_op!(INT => multiply(as_int, as_int)), + "/" => return impl_op!(INT => divide(as_int, as_int)), + "%" => return impl_op!(INT => modulo(as_int, as_int)), + "**" => return impl_op!(INT => power(as_int, as_int)), + ">>" => return impl_op!(INT => shift_right(as_int, as_int)), + "<<" => return impl_op!(INT => shift_left(as_int, as_int)), + _ => (), } + #[cfg(feature = "unchecked")] match op { + "+" => return impl_op!(INT => as_int + as_int), + "-" => return impl_op!(INT => as_int - as_int), + "*" => return impl_op!(INT => as_int * as_int), + "/" => return impl_op!(INT => as_int / as_int), + "%" => return impl_op!(INT => as_int % as_int), + "**" => return impl_op!(INT => as_int.pow(as_int as u32)), + ">>" => return impl_op!(INT => as_int >> as_int), + "<<" => return impl_op!(INT => as_int << as_int), + _ => (), + } + + return match op { "==" => impl_op!(INT => as_int == as_int), "!=" => impl_op!(INT => as_int != as_int), ">" => impl_op!(INT => as_int > as_int), @@ -354,12 +349,12 @@ pub fn get_builtin_binary_op_fn( "&" => impl_op!(INT => as_int & as_int), "|" => impl_op!(INT => as_int | as_int), "^" => impl_op!(INT => as_int ^ as_int), - _ => return None, - } + _ => None, + }; } if type1 == TypeId::of::() { - match op { + return match op { "==" => impl_op!(bool => as_bool == as_bool), "!=" => impl_op!(bool => as_bool != as_bool), ">" => impl_op!(bool => as_bool > as_bool), @@ -369,12 +364,12 @@ pub fn get_builtin_binary_op_fn( "&" => impl_op!(bool => as_bool & as_bool), "|" => impl_op!(bool => as_bool | as_bool), "^" => impl_op!(bool => as_bool ^ as_bool), - _ => return None, - } + _ => None, + }; } if type1 == TypeId::of::() { - match op { + return match op { "+" => impl_op!(ImmutableString + ImmutableString), "-" => impl_op!(ImmutableString - ImmutableString), "==" => impl_op!(ImmutableString == ImmutableString), @@ -383,48 +378,45 @@ pub fn get_builtin_binary_op_fn( ">=" => impl_op!(ImmutableString >= ImmutableString), "<" => impl_op!(ImmutableString < ImmutableString), "<=" => impl_op!(ImmutableString <= ImmutableString), - OP_CONTAINS => { - return Some(|_, args| { - let s1 = &*args[0].read_lock::().expect(BUILTIN); - let s2 = &*args[1].read_lock::().expect(BUILTIN); - Ok((s1.contains(s2.as_str())).into()) - }) - } - _ => return None, - } + OP_CONTAINS => Some(|_, args| { + let s1 = &*args[0].read_lock::().expect(BUILTIN); + let s2 = &*args[1].read_lock::().expect(BUILTIN); + Ok((s1.contains(s2.as_str())).into()) + }), + _ => None, + }; } if type1 == TypeId::of::() { - match op { - "+" => { - return Some(|_, args| { - let x = args[0].as_char().expect(BUILTIN); - let y = args[1].as_char().expect(BUILTIN); - Ok(format!("{}{}", x, y).into()) - }) - } + return match op { + "+" => Some(|_, args| { + let x = args[0].as_char().expect(BUILTIN); + let y = args[1].as_char().expect(BUILTIN); + Ok(format!("{}{}", x, y).into()) + }), "==" => impl_op!(char => as_char == as_char), "!=" => impl_op!(char => as_char != as_char), ">" => impl_op!(char => as_char > as_char), ">=" => impl_op!(char => as_char >= as_char), "<" => impl_op!(char => as_char < as_char), "<=" => impl_op!(char => as_char <= as_char), - _ => return None, - } + _ => None, + }; } if type1 == TypeId::of::<()>() { - match op { - "==" => return Some(|_, _| Ok(Dynamic::TRUE)), - "!=" | ">" | ">=" | "<" | "<=" => return Some(|_, _| Ok(Dynamic::FALSE)), - _ => return None, - } + return match op { + "==" => Some(|_, _| Ok(Dynamic::TRUE)), + "!=" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)), + _ => None, + }; } None } /// Build in common operator assignment implementations to avoid the cost of calling a registered function. +#[must_use] pub fn get_builtin_op_assignment_fn( op: &str, x: &Dynamic, @@ -441,53 +433,53 @@ pub fn get_builtin_op_assignment_fn( macro_rules! impl_op { ($x:ty = x $op:tt $yy:ident) => { - return Some(|_, args| { + Some(|_, args| { let x = args[0].$yy().expect(BUILTIN); let y = args[1].$yy().expect(BUILTIN) as $x; Ok((*args[0].write_lock::<$x>().expect(BUILTIN) = x $op y).into()) }) }; ($x:ident $op:tt $yy:ident) => { - return Some(|_, args| { + Some(|_, args| { let y = args[1].$yy().expect(BUILTIN) as $x; Ok((*args[0].write_lock::<$x>().expect(BUILTIN) $op y).into()) }) }; ($x:ident $op:tt $yy:ident as $yyy:ty) => { - return Some(|_, args| { + Some(|_, args| { let y = args[1].$yy().expect(BUILTIN) as $yyy; Ok((*args[0].write_lock::<$x>().expect(BUILTIN) $op y).into()) }) }; ($x:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty )) => { - return Some(|_, args| { + Some(|_, args| { let x = args[0].$xx().expect(BUILTIN); let y = args[1].$yy().expect(BUILTIN) as $x; Ok((*args[0].write_lock::<$x>().expect(BUILTIN) = x.$func(y as $yyy)).into()) }) }; ($x:ty => $func:ident ( $xx:ident, $yy:ident )) => { - return Some(|_, args| { + Some(|_, args| { let x = args[0].$xx().expect(BUILTIN); let y = args[1].$yy().expect(BUILTIN) as $x; Ok((*args[0].write_lock().expect(BUILTIN) = $func(x, y)?).into()) }) }; (from $x:ident $op:tt $yy:ident) => { - return Some(|_, args| { + Some(|_, args| { let y = <$x>::from(args[1].$yy().expect(BUILTIN)); Ok((*args[0].write_lock::<$x>().expect(BUILTIN) $op y).into()) }) }; (from $x:ty => $xx:ident . $func:ident ( $yy:ident )) => { - return Some(|_, args| { + Some(|_, args| { let x = args[0].$xx().expect(BUILTIN); let y = <$x>::from(args[1].$yy().expect(BUILTIN)); Ok((*args[0].write_lock::<$x>().expect(BUILTIN) = x.$func(y)).into()) }) }; (from $x:ty => $func:ident ( $xx:ident, $yy:ident )) => { - return Some(|_, args| { + Some(|_, args| { let x = args[0].$xx().expect(BUILTIN); let y = <$x>::from(args[1].$yy().expect(BUILTIN)); Ok((*args[0].write_lock().expect(BUILTIN) = $func(x, y)?).into()) @@ -499,15 +491,15 @@ pub fn get_builtin_op_assignment_fn( ($x:ident, $xx:ident, $y:ty, $yy:ident) => { #[cfg(not(feature = "no_float"))] if types_pair == (TypeId::of::<$x>(), TypeId::of::<$y>()) { - match op { + return match op { "+=" => impl_op!($x += $yy), "-=" => impl_op!($x -= $yy), "*=" => impl_op!($x *= $yy), "/=" => impl_op!($x /= $yy), "%=" => impl_op!($x %= $yy), "**=" => impl_op!($x => $xx.powf($yy as $x)), - _ => return None, - } + _ => None, + }; } } } @@ -519,31 +511,33 @@ pub fn get_builtin_op_assignment_fn( ($x:ident, $xx:ident, $y:ty, $yy:ident) => { #[cfg(feature = "decimal")] if types_pair == (TypeId::of::<$x>(), TypeId::of::<$y>()) { - if cfg!(not(feature = "unBUILTIN")) { - use crate::packages::arithmetic::decimal_functions::*; + #[cfg(not(feature = "unchecked"))] + use crate::packages::arithmetic::decimal_functions::*; - match op { - "+=" => impl_op!(from $x => add($xx, $yy)), - "-=" => impl_op!(from $x => subtract($xx, $yy)), - "*=" => impl_op!(from $x => multiply($xx, $yy)), - "/=" => impl_op!(from $x => divide($xx, $yy)), - "%=" => impl_op!(from $x => modulo($xx, $yy)), - "**=" => impl_op!(from $x => power($xx, $yy)), - _ => return None, - } - } else { - use rust_decimal::MathematicalOps; + #[cfg(not(feature = "unchecked"))] + return match op { + "+=" => impl_op!(from $x => add($xx, $yy)), + "-=" => impl_op!(from $x => subtract($xx, $yy)), + "*=" => impl_op!(from $x => multiply($xx, $yy)), + "/=" => impl_op!(from $x => divide($xx, $yy)), + "%=" => impl_op!(from $x => modulo($xx, $yy)), + "**=" => impl_op!(from $x => power($xx, $yy)), + _ => None, + }; - match op { - "+=" => impl_op!(from $x += $yy), - "-=" => impl_op!(from $x -= $yy), - "*=" => impl_op!(from $x *= $yy), - "/=" => impl_op!(from $x /= $yy), - "%=" => impl_op!(from $x %= $yy), - "**=" => impl_op!(from $x => $xx.powd($yy)), - _ => return None, - } - } + #[cfg(feature = "unchecked")] + use rust_decimal::MathematicalOps; + + #[cfg(feature = "unchecked")] + return match op { + "+=" => impl_op!(from $x += $yy), + "-=" => impl_op!(from $x -= $yy), + "*=" => impl_op!(from $x *= $yy), + "/=" => impl_op!(from $x /= $yy), + "%=" => impl_op!(from $x %= $yy), + "**=" => impl_op!(from $x => $xx.powd($yy)), + _ => None, + }; } }; } @@ -553,31 +547,29 @@ pub fn get_builtin_op_assignment_fn( // string op= char if types_pair == (TypeId::of::(), TypeId::of::()) { - match op { + return match op { "+=" => impl_op!(ImmutableString += as_char as char), "-=" => impl_op!(ImmutableString -= as_char as char), - _ => return None, - } + _ => None, + }; } // char op= string if types_pair == (TypeId::of::(), TypeId::of::()) { - match op { - "+=" => { - return Some(|_, args| { - let mut ch = args[0].as_char().expect(BUILTIN).to_string(); - ch.push_str( - args[1] - .read_lock::() - .expect(BUILTIN) - .as_str(), - ); + return match op { + "+=" => Some(|_, args| { + let mut ch = args[0].as_char().expect(BUILTIN).to_string(); + ch.push_str( + args[1] + .read_lock::() + .expect(BUILTIN) + .as_str(), + ); - let mut x = args[0].write_lock::().expect(BUILTIN); - Ok((*x = ch.into()).into()) - }) - } - _ => return None, - } + let mut x = args[0].write_lock::().expect(BUILTIN); + Ok((*x = ch.into()).into()) + }), + _ => None, + }; } // No built-in op-assignments for different types. @@ -587,83 +579,78 @@ pub fn get_builtin_op_assignment_fn( // Beyond here, type1 == type2 if type1 == TypeId::of::() { - if cfg!(not(feature = "unBUILTIN")) { - use crate::packages::arithmetic::arith_basic::INT::functions::*; + #[cfg(not(feature = "unchecked"))] + use crate::packages::arithmetic::arith_basic::INT::functions::*; - match op { - "+=" => impl_op!(INT => add(as_int, as_int)), - "-=" => impl_op!(INT => subtract(as_int, as_int)), - "*=" => impl_op!(INT => multiply(as_int, as_int)), - "/=" => impl_op!(INT => divide(as_int, as_int)), - "%=" => impl_op!(INT => modulo(as_int, as_int)), - "**=" => impl_op!(INT => power(as_int, as_int)), - ">>=" => impl_op!(INT => shift_right(as_int, as_int)), - "<<=" => impl_op!(INT => shift_left(as_int, as_int)), - _ => (), - } - } else { - match op { - "+=" => impl_op!(INT += as_int), - "-=" => impl_op!(INT -= as_int), - "*=" => impl_op!(INT *= as_int), - "/=" => impl_op!(INT /= as_int), - "%=" => impl_op!(INT %= as_int), - "**=" => impl_op!(INT => as_int.pow(as_int as u32)), - ">>=" => impl_op!(INT >>= as_int), - "<<=" => impl_op!(INT <<= as_int), - _ => (), - } + #[cfg(not(feature = "unchecked"))] + match op { + "+=" => return impl_op!(INT => add(as_int, as_int)), + "-=" => return impl_op!(INT => subtract(as_int, as_int)), + "*=" => return impl_op!(INT => multiply(as_int, as_int)), + "/=" => return impl_op!(INT => divide(as_int, as_int)), + "%=" => return impl_op!(INT => modulo(as_int, as_int)), + "**=" => return impl_op!(INT => power(as_int, as_int)), + ">>=" => return impl_op!(INT => shift_right(as_int, as_int)), + "<<=" => return impl_op!(INT => shift_left(as_int, as_int)), + _ => (), } + #[cfg(feature = "unchecked")] match op { + "+=" => return impl_op!(INT += as_int), + "-=" => return impl_op!(INT -= as_int), + "*=" => return impl_op!(INT *= as_int), + "/=" => return impl_op!(INT /= as_int), + "%=" => return impl_op!(INT %= as_int), + "**=" => return impl_op!(INT => as_int.pow(as_int as u32)), + ">>=" => return impl_op!(INT >>= as_int), + "<<=" => return impl_op!(INT <<= as_int), + _ => (), + } + + return match op { "&=" => impl_op!(INT &= as_int), "|=" => impl_op!(INT |= as_int), "^=" => impl_op!(INT ^= as_int), - _ => (), - } + _ => None, + }; } if type1 == TypeId::of::() { - match op { + return match op { "&=" => impl_op!(bool = x && as_bool), "|=" => impl_op!(bool = x || as_bool), - _ => return None, - } + _ => None, + }; } if type1 == TypeId::of::() { - match op { - "+=" => { - return Some(|_, args| { - let y = args[1].as_char().expect(BUILTIN); - let mut x = args[0].write_lock::().expect(BUILTIN); - Ok((*x = format!("{}{}", *x, y).into()).into()) - }) - } - _ => return None, - } + return match op { + "+=" => Some(|_, args| { + let y = args[1].as_char().expect(BUILTIN); + let mut x = args[0].write_lock::().expect(BUILTIN); + Ok((*x = format!("{}{}", *x, y).into()).into()) + }), + _ => None, + }; } if type1 == TypeId::of::() { - match op { - "+=" => { - return Some(|_, args| { - let (first, second) = args.split_first_mut().expect(BUILTIN); - let mut x = first.write_lock::().expect(BUILTIN); - let y = &*second[0].read_lock::().expect(BUILTIN); - Ok((*x += y).into()) - }) - } - "-=" => { - return Some(|_, args| { - let (first, second) = args.split_first_mut().expect(BUILTIN); - let mut x = first.write_lock::().expect(BUILTIN); - let y = &*second[0].read_lock::().expect(BUILTIN); - Ok((*x -= y).into()) - }) - } - _ => return None, - } + return match op { + "+=" => Some(|_, args| { + let (first, second) = args.split_first_mut().expect(BUILTIN); + let mut x = first.write_lock::().expect(BUILTIN); + let y = &*second[0].read_lock::().expect(BUILTIN); + Ok((*x += y).into()) + }), + "-=" => Some(|_, args| { + let (first, second) = args.split_first_mut().expect(BUILTIN); + let mut x = first.write_lock::().expect(BUILTIN); + let y = &*second[0].read_lock::().expect(BUILTIN); + Ok((*x -= y).into()) + }), + _ => None, + }; } None diff --git a/src/fn_call.rs b/src/fn_call.rs index be0fe520..78c32f09 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -98,6 +98,7 @@ impl Drop for ArgBackup<'_> { #[cfg(not(feature = "no_closure"))] #[inline(always)] +#[must_use] pub fn ensure_no_data_race( fn_name: &str, args: &FnCallArgs, @@ -123,6 +124,7 @@ pub fn ensure_no_data_race( impl Engine { /// Generate the signature for a function call. #[inline] + #[must_use] fn gen_call_signature( &self, namespace: Option<&NamespaceRef>, @@ -153,7 +155,8 @@ impl Engine { /// 4) Imported modules - functions marked with global namespace /// 5) Global sub-modules - functions marked with global namespace #[inline(always)] - fn resolve_function<'s>( + #[must_use] + fn resolve_fn<'s>( &self, mods: &Imports, state: &'s mut State, @@ -286,16 +289,17 @@ 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, state: &mut State, lib: &[&Module], - fn_name: &str, - hash_native: u64, + name: &str, + hash: u64, args: &mut FnCallArgs, is_ref: bool, - is_op_assignment: bool, + is_op_assign: bool, pos: Position, ) -> Result<(Dynamic, bool), Box> { #[cfg(not(feature = "unchecked"))] @@ -304,16 +308,7 @@ impl Engine { let state_source = state.source.clone(); // Check if function access already in the cache - let func = self.resolve_function( - mods, - state, - lib, - fn_name, - hash_native, - Some(args), - true, - is_op_assignment, - ); + let func = self.resolve_fn(mods, state, lib, name, hash, Some(args), true, is_op_assign); if let Some(f) = func { let FnResolutionCacheEntry { func, source } = f.as_ref(); @@ -334,9 +329,9 @@ impl Engine { let result = if func.is_plugin_fn() { func.get_plugin_fn() - .call((self, fn_name, source, mods, lib).into(), args) + .call((self, name, source, mods, lib).into(), args) } else { - func.get_native_fn()((self, fn_name, source, mods, lib).into(), args) + func.get_native_fn()((self, name, source, mods, lib).into(), args) }; // Restore the original reference @@ -345,7 +340,7 @@ impl Engine { let result = result.map_err(|err| err.fill_position(pos))?; // See if the function match print/debug (which requires special processing) - return Ok(match fn_name { + return Ok(match name { KEYWORD_PRINT => { let text = result.as_immutable_string().map_err(|typ| { EvalAltResult::ErrorMismatchOutputType( @@ -371,7 +366,7 @@ impl Engine { }); } - match fn_name { + match name { // index getter function not found? #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] crate::engine::FN_IDX_GET => { @@ -398,13 +393,13 @@ impl Engine { // Getter function not found? #[cfg(not(feature = "no_object"))] - _ if fn_name.starts_with(crate::engine::FN_GET) => { + _ if name.starts_with(crate::engine::FN_GET) => { assert!(args.len() == 1); EvalAltResult::ErrorDotExpr( format!( "Unknown property '{}' - a getter is not registered for type '{}'", - &fn_name[crate::engine::FN_GET.len()..], + &name[crate::engine::FN_GET.len()..], self.map_type_name(args[0].type_name()) ), pos, @@ -414,13 +409,13 @@ impl Engine { // Setter function not found? #[cfg(not(feature = "no_object"))] - _ if fn_name.starts_with(crate::engine::FN_SET) => { + _ if name.starts_with(crate::engine::FN_SET) => { assert!(args.len() == 2); EvalAltResult::ErrorDotExpr( format!( "No writable property '{}' - a setter is not registered for type '{}' to handle '{}'", - &fn_name[crate::engine::FN_SET.len()..], + &name[crate::engine::FN_SET.len()..], self.map_type_name(args[0].type_name()), self.map_type_name(args[1].type_name()), ), @@ -431,7 +426,7 @@ impl Engine { // Raise error _ => EvalAltResult::ErrorFunctionNotFound( - self.gen_call_signature(None, fn_name, args.as_ref()), + self.gen_call_signature(None, name, args.as_ref()), pos, ) .into(), @@ -446,6 +441,7 @@ 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, @@ -576,6 +572,7 @@ impl Engine { // Does a scripted function exist? #[cfg(not(feature = "no_function"))] #[inline(always)] + #[must_use] pub(crate) fn has_script_fn( &self, mods: Option<&Imports>, @@ -614,13 +611,14 @@ 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, state: &mut State, lib: &[&Module], fn_name: &str, - hash: FnCallHashes, + hashes: FnCallHashes, args: &mut FnCallArgs, is_ref: bool, _is_method: bool, @@ -691,11 +689,11 @@ impl Engine { // Scripted function call? #[cfg(not(feature = "no_function"))] - let hash_script = hash.script_hash(); + let hash_script = hashes.script; #[cfg(not(feature = "no_function"))] if let Some(f) = hash_script.and_then(|hash| { - self.resolve_function(mods, state, lib, fn_name, hash, None, false, false) + self.resolve_fn(mods, state, lib, fn_name, hash, None, false, false) .clone() }) { let FnResolutionCacheEntry { func, source } = *f; @@ -781,13 +779,14 @@ impl Engine { } // Native function call - let hash = hash.native_hash(); + let hash = hashes.native; self.call_native_fn(mods, state, lib, fn_name, hash, args, is_ref, false, pos) } /// 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] pub(crate) fn eval_global_statements( &self, scope: &mut Scope, @@ -808,6 +807,7 @@ impl Engine { } /// Evaluate a text script in place - used primarily for 'eval'. + #[must_use] fn eval_script_expr_in_place( &self, scope: &mut Scope, @@ -859,6 +859,7 @@ impl Engine { /// Call a dot method. #[cfg(not(feature = "no_object"))] + #[must_use] pub(crate) fn make_method_call( &self, mods: &mut Imports, @@ -900,7 +901,7 @@ impl Engine { if call_args.len() > 0 { if !call_args[0].is::() { return Err(self.make_type_mismatch_err::( - self.map_type_name(target.type_name()), + self.map_type_name(call_args[0].type_name()), *call_arg_pos, )); } @@ -1020,6 +1021,7 @@ impl Engine { /// Evaluate an argument. #[inline(always)] + #[must_use] pub(crate) fn get_arg_value( &self, scope: &mut Scope, @@ -1041,6 +1043,7 @@ impl Engine { } /// Call a function in normal function-call style. + #[must_use] pub(crate) fn make_function_call( &self, scope: &mut Scope, @@ -1155,7 +1158,7 @@ impl Engine { let fn_name = arg .as_immutable_string() - .map_err(|err| self.make_type_mismatch_err::(err, arg_pos))?; + .map_err(|typ| self.make_type_mismatch_err::(typ, arg_pos))?; let (arg, arg_pos) = self.get_arg_value( scope, mods, state, lib, this_ptr, level, args_expr, constants, 1, @@ -1163,7 +1166,7 @@ impl Engine { let num_params = arg .as_int() - .map_err(|err| self.make_type_mismatch_err::(err, arg_pos))?; + .map_err(|typ| self.make_type_mismatch_err::(typ, arg_pos))?; return Ok(if num_params < 0 { Dynamic::FALSE @@ -1181,7 +1184,7 @@ impl Engine { )?; let var_name = arg .as_immutable_string() - .map_err(|err| self.make_type_mismatch_err::(err, arg_pos))?; + .map_err(|typ| self.make_type_mismatch_err::(typ, arg_pos))?; return Ok(scope.contains(&var_name).into()); } @@ -1291,6 +1294,7 @@ 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, diff --git a/src/fn_hash.rs b/src/fn_hash.rs new file mode 100644 index 00000000..e74d0a9b --- /dev/null +++ b/src/fn_hash.rs @@ -0,0 +1,113 @@ +//! Module containing utilities to hash functions and function calls. + +#[cfg(feature = "no_std")] +use std::prelude::v1::*; +use std::{ + any::TypeId, + hash::{BuildHasher, Hash, Hasher}, + iter::empty, +}; + +/// A hasher that only takes one single [`u64`] and returns it as a hash key. +/// +/// # Panics +/// +/// Panics when hashing any data type other than a [`u64`]. +#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct StraightHasher(u64); + +impl Hasher for StraightHasher { + #[inline(always)] + fn finish(&self) -> u64 { + self.0 + } + #[inline(always)] + fn write(&mut self, bytes: &[u8]) { + assert_eq!(bytes.len(), 8, "StraightHasher can only hash u64 values"); + + let mut key = [0_u8; 8]; + key.copy_from_slice(bytes); + + self.0 = u64::from_ne_bytes(key); + } +} + +/// A hash builder for `StraightHasher`. +#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Default)] +pub struct StraightHasherBuilder; + +impl BuildHasher for StraightHasherBuilder { + type Hasher = StraightHasher; + + #[inline(always)] + fn build_hasher(&self) -> Self::Hasher { + StraightHasher(42) + } +} + +/// Create an instance of the default hasher. +#[inline(always)] +#[must_use] +pub fn get_hasher() -> ahash::AHasher { + Default::default() +} + +/// Calculate a [`u64`] hash key from a namespace-qualified function name +/// and the number of parameters, but no parameter types. +/// +/// Module names are passed in via `&str` references from an iterator. +/// Parameter types are passed in via [`TypeId`] values from an iterator. +/// +/// # Note +/// +/// The first module name is skipped. Hashing starts from the _second_ module in the chain. +#[inline] +#[must_use] +pub fn calc_qualified_fn_hash<'a>( + modules: impl Iterator, + fn_name: impl AsRef, + num: usize, +) -> u64 { + let s = &mut get_hasher(); + + // We always skip the first module + let mut len = 0; + modules + .inspect(|_| len += 1) + .skip(1) + .for_each(|m| m.hash(s)); + len.hash(s); + fn_name.as_ref().hash(s); + num.hash(s); + s.finish() +} + +/// Calculate a [`u64`] hash key from a non-namespace-qualified function name +/// and the number of parameters, but no parameter types. +/// +/// 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 { + calc_qualified_fn_hash(empty(), fn_name, num) +} + +/// Calculate a [`u64`] hash key from a list of parameter types. +/// +/// Parameter types are passed in via [`TypeId`] values from an iterator. +#[inline] +#[must_use] +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)); + len.hash(s); + s.finish() +} + +/// Combine two [`u64`] hashes by taking the XOR of them. +#[inline(always)] +#[must_use] +pub(crate) fn combine_hashes(a: u64, b: u64) -> u64 { + a ^ b +} diff --git a/src/fn_native.rs b/src/fn_native.rs index 4fd1acb1..9798ebf5 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -92,6 +92,7 @@ impl<'a, M: AsRef<[&'a Module]> + ?Sized> From<(&'a Engine, &'a str, &'a M)> impl<'a> NativeCallContext<'a> { /// Create a new [`NativeCallContext`]. #[inline(always)] + #[must_use] pub fn new(engine: &'a Engine, fn_name: &'a str, lib: &'a [&Module]) -> Self { Self { engine, @@ -108,6 +109,7 @@ impl<'a> NativeCallContext<'a> { #[cfg(feature = "internals")] #[cfg(not(feature = "no_module"))] #[inline(always)] + #[must_use] pub fn new_with_all_fields( engine: &'a Engine, fn_name: &'a str, @@ -125,16 +127,19 @@ impl<'a> NativeCallContext<'a> { } /// The current [`Engine`]. #[inline(always)] + #[must_use] pub fn engine(&self) -> &Engine { self.engine } /// Name of the function called. #[inline(always)] + #[must_use] pub fn fn_name(&self) -> &str { self.fn_name } /// The current source. #[inline(always)] + #[must_use] pub fn source(&self) -> Option<&str> { self.source } @@ -143,6 +148,7 @@ 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()) } @@ -150,6 +156,7 @@ 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)> { @@ -162,11 +169,13 @@ impl<'a> NativeCallContext<'a> { #[cfg(feature = "internals")] #[cfg(not(feature = "no_module"))] #[inline(always)] + #[must_use] pub fn imports(&self) -> Option<&Imports> { self.mods } /// 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() } @@ -174,6 +183,7 @@ impl<'a> NativeCallContext<'a> { /// Exported under the `internals` feature only. #[cfg(feature = "internals")] #[inline(always)] + #[must_use] pub fn namespaces(&self) -> &[&Module] { self.lib } @@ -190,6 +200,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] pub fn call_fn_dynamic_raw( &self, fn_name: impl AsRef, @@ -228,18 +239,21 @@ impl<'a> NativeCallContext<'a> { /// Consume a [`Shared`] resource and return a mutable reference to the wrapped value. /// If the resource is shared (i.e. has other outstanding references), a cloned copy is used. #[inline(always)] +#[must_use] pub fn shared_make_mut(value: &mut Shared) -> &mut T { Shared::make_mut(value) } /// Consume a [`Shared`] resource if is unique (i.e. not shared), or clone it otherwise. #[inline(always)] +#[must_use] pub fn shared_take_or_clone(value: Shared) -> T { shared_try_take(value).unwrap_or_else(|v| v.as_ref().clone()) } /// 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) } @@ -250,6 +264,7 @@ pub fn shared_try_take(value: Shared) -> Result> { /// /// Panics if the resource is shared (i.e. has other outstanding references). #[inline(always)] +#[must_use] pub fn shared_take(value: Shared) -> T { shared_try_take(value) .ok() @@ -267,31 +282,37 @@ 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() } /// Create a new function pointer without checking its parameters. #[inline(always)] + #[must_use] pub(crate) fn new_unchecked(name: Identifier, curry: StaticVec) -> Self { Self(name.into(), curry) } /// Get the name of the function. #[inline(always)] + #[must_use] pub fn fn_name(&self) -> &str { self.get_fn_name().as_ref() } /// Get the name of the function. #[inline(always)] + #[must_use] pub(crate) fn get_fn_name(&self) -> &Identifier { &self.0 } /// Get the underlying data of the function pointer. #[inline(always)] + #[must_use] pub(crate) fn take_data(self) -> (Identifier, StaticVec) { (self.0, self.1) } /// Get the curried arguments. #[inline(always)] + #[must_use] pub fn curry(&self) -> &[Dynamic] { self.1.as_ref() } @@ -309,11 +330,13 @@ impl FnPtr { } /// Is the function pointer curried? #[inline(always)] + #[must_use] pub fn is_curried(&self) -> bool { !self.1.is_empty() } /// Get the number of curried arguments. #[inline(always)] + #[must_use] pub fn num_curried(&self) -> usize { self.1.len() } @@ -322,6 +345,7 @@ impl FnPtr { /// Not available under `no_function`. #[cfg(not(feature = "no_function"))] #[inline(always)] + #[must_use] pub fn is_anonymous(&self) -> bool { self.0.starts_with(crate::engine::FN_ANONYMOUS) } @@ -336,6 +360,7 @@ impl FnPtr { /// Do not use the arguments after this call. If they are needed afterwards, /// clone them _before_ calling this function. #[inline(always)] + #[must_use] pub fn call_dynamic( &self, ctx: &NativeCallContext, @@ -518,6 +543,7 @@ impl fmt::Display for CallableFunction { impl CallableFunction { /// Is this a pure native Rust function? #[inline(always)] + #[must_use] pub fn is_pure(&self) -> bool { match self { Self::Pure(_) => true, @@ -531,6 +557,7 @@ impl CallableFunction { } /// Is this a native Rust method function? #[inline(always)] + #[must_use] pub fn is_method(&self) -> bool { match self { Self::Method(_) => true, @@ -544,6 +571,7 @@ impl CallableFunction { } /// Is this an iterator function? #[inline(always)] + #[must_use] pub fn is_iter(&self) -> bool { match self { Self::Iterator(_) => true, @@ -555,6 +583,7 @@ impl CallableFunction { } /// Is this a Rhai-scripted function? #[inline(always)] + #[must_use] pub fn is_script(&self) -> bool { match self { #[cfg(not(feature = "no_function"))] @@ -565,6 +594,7 @@ impl CallableFunction { } /// Is this a plugin function? #[inline(always)] + #[must_use] pub fn is_plugin_fn(&self) -> bool { match self { Self::Plugin(_) => true, @@ -576,6 +606,7 @@ impl CallableFunction { } /// Is this a native Rust function? #[inline(always)] + #[must_use] pub fn is_native(&self) -> bool { match self { Self::Pure(_) | Self::Method(_) => true, @@ -588,6 +619,7 @@ impl CallableFunction { } /// Get the access mode. #[inline(always)] + #[must_use] pub fn access(&self) -> FnAccess { match self { Self::Plugin(_) => FnAccess::Public, @@ -604,6 +636,7 @@ impl CallableFunction { /// Panics if the [`CallableFunction`] is not [`Pure`][CallableFunction::Pure] or /// [`Method`][CallableFunction::Method]. #[inline(always)] + #[must_use] pub fn get_native_fn(&self) -> &Shared { match self { Self::Pure(f) | Self::Method(f) => f, @@ -622,6 +655,7 @@ impl CallableFunction { /// Panics if the [`CallableFunction`] is not [`Script`][CallableFunction::Script]. #[cfg(not(feature = "no_function"))] #[inline(always)] + #[must_use] pub fn get_fn_def(&self) -> &Shared { match self { Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Plugin(_) => { @@ -636,6 +670,7 @@ impl CallableFunction { /// /// Panics if the [`CallableFunction`] is not [`Iterator`][CallableFunction::Iterator]. #[inline(always)] + #[must_use] pub fn get_iter_fn(&self) -> IteratorFn { match self { Self::Iterator(f) => *f, @@ -653,6 +688,7 @@ impl CallableFunction { /// /// Panics if the [`CallableFunction`] is not [`Plugin`][CallableFunction::Plugin]. #[inline(always)] + #[must_use] pub fn get_plugin_fn<'s>(&'s self) -> &Shared { match self { Self::Plugin(f) => f, @@ -666,16 +702,19 @@ impl CallableFunction { } /// Create a new [`CallableFunction::Pure`]. #[inline(always)] + #[must_use] pub fn from_pure(func: Box) -> Self { Self::Pure(func.into()) } /// Create a new [`CallableFunction::Method`]. #[inline(always)] + #[must_use] pub fn from_method(func: Box) -> Self { Self::Method(func.into()) } /// Create a new [`CallableFunction::Plugin`]. #[inline(always)] + #[must_use] pub fn from_plugin(func: impl PluginFunction + 'static + SendSync) -> Self { Self::Plugin((Box::new(func) as Box).into()) } diff --git a/src/fn_register.rs b/src/fn_register.rs index 28737808..2178e56b 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -30,6 +30,7 @@ pub struct Mut(T); /// Dereference into DynamicWriteLock #[inline(always)] +#[must_use] pub fn by_ref(data: &mut Dynamic) -> DynamicWriteLock { // Directly cast the &mut Dynamic into DynamicWriteLock to access the underlying data. data.write_lock::() @@ -38,6 +39,7 @@ pub fn by_ref(data: &mut Dynamic) -> DynamicWriteLock { /// Dereference into value. #[inline(always)] +#[must_use] pub fn by_value(data: &mut Dynamic) -> T { if TypeId::of::() == TypeId::of::<&str>() { // If T is `&str`, data must be `ImmutableString`, so map directly to it @@ -63,24 +65,30 @@ pub fn by_value(data: &mut Dynamic) -> T { /// Trait to register custom Rust functions. pub trait RegisterNativeFunction { /// Convert this function into a [`CallableFunction`]. + #[must_use] fn into_callable_function(self) -> CallableFunction; /// Get the type ID's of this function's parameters. + #[must_use] fn param_types() -> Box<[TypeId]>; /// _(METADATA)_ Get the type names of this function's parameters. /// Exported under the `metadata` feature only. #[cfg(feature = "metadata")] + #[must_use] fn param_names() -> Box<[&'static str]>; /// _(METADATA)_ Get the type ID of this function's return value. /// Exported under the `metadata` feature only. #[cfg(feature = "metadata")] + #[must_use] fn return_type() -> TypeId; /// _(METADATA)_ Get the type name of this function's return value. /// Exported under the `metadata` feature only. #[cfg(feature = "metadata")] + #[must_use] fn return_type_name() -> &'static str; } #[inline(always)] +#[must_use] fn is_setter(_fn_name: &str) -> bool { #[cfg(not(feature = "no_object"))] if _fn_name.starts_with(crate::engine::FN_SET) { diff --git a/src/utils.rs b/src/immutable_string.rs similarity index 75% rename from src/utils.rs rename to src/immutable_string.rs index 49012ea3..212ecc24 100644 --- a/src/utils.rs +++ b/src/immutable_string.rs @@ -1,119 +1,19 @@ -//! Module containing various utility types and functions. +//! The `ImmutableString` type. use crate::fn_native::{shared_make_mut, shared_take}; -use crate::{Identifier, Shared, SmartString}; +use crate::{Shared, SmartString}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ - any::TypeId, borrow::Borrow, cmp::Ordering, fmt, - hash::{BuildHasher, Hash, Hasher}, - iter::{empty, FromIterator}, + hash::Hash, + iter::FromIterator, ops::{Add, AddAssign, Deref, Sub, SubAssign}, str::FromStr, }; -/// A hasher that only takes one single [`u64`] and returns it as a hash key. -/// -/// # Panics -/// -/// Panics when hashing any data type other than a [`u64`]. -#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub struct StraightHasher(u64); - -impl Hasher for StraightHasher { - #[inline(always)] - fn finish(&self) -> u64 { - self.0 - } - #[inline(always)] - fn write(&mut self, bytes: &[u8]) { - assert_eq!(bytes.len(), 8, "StraightHasher can only hash u64 values"); - - let mut key = [0_u8; 8]; - key.copy_from_slice(bytes); - - self.0 = u64::from_ne_bytes(key); - } -} - -/// A hash builder for `StraightHasher`. -#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Default)] -pub struct StraightHasherBuilder; - -impl BuildHasher for StraightHasherBuilder { - type Hasher = StraightHasher; - - #[inline(always)] - fn build_hasher(&self) -> Self::Hasher { - StraightHasher(42) - } -} - -/// Create an instance of the default hasher. -#[inline(always)] -pub fn get_hasher() -> ahash::AHasher { - Default::default() -} - -/// Calculate a [`u64`] hash key from a namespace-qualified function name -/// and the number of parameters, but no parameter types. -/// -/// Module names are passed in via `&str` references from an iterator. -/// Parameter types are passed in via [`TypeId`] values from an iterator. -/// -/// # Note -/// -/// The first module name is skipped. Hashing starts from the _second_ module in the chain. -#[inline] -pub fn calc_qualified_fn_hash<'a>( - modules: impl Iterator, - fn_name: impl AsRef, - num: usize, -) -> u64 { - let s = &mut get_hasher(); - - // We always skip the first module - let mut len = 0; - modules - .inspect(|_| len += 1) - .skip(1) - .for_each(|m| m.hash(s)); - len.hash(s); - fn_name.as_ref().hash(s); - num.hash(s); - s.finish() -} - -/// Calculate a [`u64`] hash key from a non-namespace-qualified function name -/// and the number of parameters, but no parameter types. -/// -/// Parameter types are passed in via [`TypeId`] values from an iterator. -#[inline(always)] -pub fn calc_fn_hash(fn_name: impl AsRef, num: usize) -> u64 { - calc_qualified_fn_hash(empty(), fn_name, num) -} - -/// Calculate a [`u64`] hash key from a list of parameter types. -/// -/// Parameter types are passed in via [`TypeId`] values from an iterator. -#[inline] -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)); - len.hash(s); - s.finish() -} - -/// Combine two [`u64`] hashes by taking the XOR of them. -#[inline(always)] -pub(crate) fn combine_hashes(a: u64, b: u64) -> u64 { - a ^ b -} - /// The system immutable string type. /// /// An [`ImmutableString`] wraps an [`Rc`][std::rc::Rc]`<`[`String`]`>` @@ -623,32 +523,3 @@ impl ImmutableString { shared_make_mut(&mut self.0) } } - -/// A factory of identifiers from text strings. -/// -/// When [`SmartString`](https://crates.io/crates/smartstring) is used as [`Identifier`], -/// this just returns one because most identifiers in Rhai are short and ASCII-based. -/// -/// When [`ImmutableString`] is used as [`Identifier`], this type acts as an interner which keeps a -/// collection of strings and returns shared instances, only creating a new string when it is not -/// yet interned. -#[derive(Debug, Clone, Default, Hash)] -pub struct IdentifierBuilder( - #[cfg(feature = "no_smartstring")] std::collections::BTreeSet, -); - -impl IdentifierBuilder { - /// Get an identifier from a text string. - #[inline(always)] - pub fn get(&mut self, text: impl AsRef + Into) -> Identifier { - #[cfg(not(feature = "no_smartstring"))] - return text.as_ref().into(); - - #[cfg(feature = "no_smartstring")] - return self.0.get(text.as_ref()).cloned().unwrap_or_else(|| { - let s: Identifier = text.into(); - self.0.insert(s.clone()); - s - }); - } -} diff --git a/src/lib.rs b/src/lib.rs index 2db92c3e..a193bf30 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,28 +70,29 @@ use std::prelude::v1::*; // Internal modules mod ast; +mod custom_syntax; mod dynamic; mod engine; mod engine_api; mod engine_settings; +mod error; +mod error_parsing; mod fn_args; mod fn_builtin; mod fn_call; mod fn_func; +mod fn_hash; mod fn_native; mod fn_register; +mod immutable_string; mod module; mod optimize; pub mod packages; -mod parse_error; -mod parser; +mod parse; pub mod plugin; -mod result; mod scope; -mod syntax; mod token; mod r#unsafe; -mod utils; type RhaiResult = Result>; @@ -126,17 +127,17 @@ pub type FLOAT = f64; pub type FLOAT = f32; pub use ast::{FnAccess, AST}; +pub use custom_syntax::Expression; pub use dynamic::Dynamic; pub use engine::{Engine, EvalContext, OP_CONTAINS, OP_EQUALS}; +pub use error::EvalAltResult; +pub use error_parsing::{LexError, ParseError, ParseErrorType}; pub use fn_native::{FnPtr, NativeCallContext}; pub use fn_register::RegisterNativeFunction; +pub use immutable_string::ImmutableString; pub use module::{FnNamespace, Module}; -pub use parse_error::{LexError, ParseError, ParseErrorType}; -pub use result::EvalAltResult; pub use scope::Scope; -pub use syntax::Expression; pub use token::Position; -pub use utils::ImmutableString; /// An identifier in Rhai. [`SmartString`](https://crates.io/crates/smartstring) is used because most /// identifiers are ASCII and short, fewer than 23 characters, so they can be stored inline. @@ -169,7 +170,9 @@ pub use fn_native::Shared; #[cfg(not(feature = "no_closure"))] use fn_native::Locked; -pub(crate) use utils::{calc_fn_hash, calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes}; +pub(crate) use fn_hash::{ + calc_fn_hash, calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes, +}; pub use rhai_codegen::*; diff --git a/src/module/mod.rs b/src/module/mod.rs index 63052d6b..c29fd06f 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -4,8 +4,8 @@ use crate::ast::{FnAccess, Ident}; use crate::dynamic::Variant; use crate::fn_native::{shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, SendSync}; use crate::fn_register::RegisterNativeFunction; +use crate::parse::IdentifierBuilder; use crate::token::Token; -use crate::utils::IdentifierBuilder; use crate::{ calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes, Dynamic, EvalAltResult, Identifier, ImmutableString, NativeCallContext, Position, Shared, StaticVec, @@ -67,6 +67,7 @@ impl FuncInfo { /// Generate a signature of the function. /// Exported under the `metadata` feature only. #[cfg(feature = "metadata")] + #[must_use] pub fn gen_signature(&self) -> String { let mut sig = format!("{}(", self.name); @@ -240,6 +241,7 @@ impl Module { /// assert_eq!(module.get_var_value::("answer").unwrap(), 42); /// ``` #[inline(always)] + #[must_use] pub fn new() -> Self { Self { id: None, @@ -269,6 +271,7 @@ impl Module { /// assert_eq!(module.id(), Some("hello")); /// ``` #[inline(always)] + #[must_use] pub fn id(&self) -> Option<&str> { self.id_raw().map(|s| s.as_str()) } @@ -285,6 +288,7 @@ impl Module { /// assert_eq!(module.id_raw().map(|s| s.as_str()), Some("hello")); /// ``` #[inline(always)] + #[must_use] pub fn id_raw(&self) -> Option<&Identifier> { self.id.as_ref() } @@ -317,6 +321,7 @@ impl Module { /// assert!(module.is_empty()); /// ``` #[inline(always)] + #[must_use] pub fn is_empty(&self) -> bool { self.functions.is_empty() && self.all_functions.is_empty() @@ -349,6 +354,7 @@ impl Module { /// # } /// ``` #[inline(always)] + #[must_use] pub fn is_indexed(&self) -> bool { self.indexed } @@ -357,6 +363,7 @@ impl Module { /// Exported under the `metadata` feature only. #[cfg(feature = "metadata")] #[inline(always)] + #[must_use] pub fn gen_fn_signatures(&self) -> impl Iterator + '_ { self.functions .values() @@ -379,6 +386,7 @@ impl Module { /// assert!(module.contains_var("answer")); /// ``` #[inline(always)] + #[must_use] pub fn contains_var(&self, name: &str) -> bool { self.variables.contains_key(name) } @@ -395,6 +403,7 @@ impl Module { /// assert_eq!(module.get_var_value::("answer").unwrap(), 42); /// ``` #[inline(always)] + #[must_use] pub fn get_var_value(&self, name: &str) -> Option { self.get_var(name).and_then(Dynamic::try_cast::) } @@ -411,6 +420,7 @@ impl Module { /// assert_eq!(module.get_var("answer").unwrap().cast::(), 42); /// ``` #[inline(always)] + #[must_use] pub fn get_var(&self, name: &str) -> Option { self.variables.get(name).cloned() } @@ -448,6 +458,7 @@ 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() @@ -489,6 +500,7 @@ impl Module { /// and number of parameters. #[cfg(not(feature = "no_function"))] #[inline(always)] + #[must_use] pub fn get_script_fn( &self, name: &str, @@ -508,6 +520,7 @@ impl Module { /// Thus the [`Module`] is automatically set to be non-indexed. #[cfg(not(feature = "no_module"))] #[inline(always)] + #[must_use] pub(crate) fn sub_modules_mut(&mut self) -> &mut BTreeMap> { // We must assume that the user has changed the sub-modules // (otherwise why take a mutable reference?) @@ -533,6 +546,7 @@ impl Module { /// assert!(module.contains_sub_module("question")); /// ``` #[inline(always)] + #[must_use] pub fn contains_sub_module(&self, name: &str) -> bool { self.modules.contains_key(name) } @@ -550,6 +564,7 @@ impl Module { /// assert!(module.get_sub_module("question").is_some()); /// ``` #[inline(always)] + #[must_use] pub fn get_sub_module(&self, name: &str) -> Option<&Module> { self.modules.get(name).map(|m| m.as_ref()) } @@ -594,6 +609,7 @@ impl Module { /// assert!(module.contains_fn(hash)); /// ``` #[inline(always)] + #[must_use] pub fn contains_fn(&self, hash_fn: u64) -> bool { self.functions.contains_key(&hash_fn) } @@ -641,6 +657,7 @@ impl Module { /// Remap type ID. #[inline(always)] + #[must_use] fn map_type(map: bool, type_id: TypeId) -> TypeId { if !map { return type_id; @@ -1104,6 +1121,7 @@ impl Module { /// /// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call. #[inline(always)] + #[must_use] pub(crate) fn get_fn(&self, hash_fn: u64) -> Option<&CallableFunction> { self.functions.get(&hash_fn).map(|f| f.func.as_ref()) } @@ -1112,6 +1130,7 @@ impl Module { /// /// The [`u64`] hash is calculated by [`build_index`][Module::build_index]. #[inline(always)] + #[must_use] pub fn contains_qualified_fn(&self, hash_fn: u64) -> bool { self.all_functions.contains_key(&hash_fn) } @@ -1120,6 +1139,7 @@ impl Module { /// /// The [`u64`] hash is calculated by [`build_index`][Module::build_index]. #[inline(always)] + #[must_use] pub(crate) fn get_qualified_fn(&self, hash_qualified_fn: u64) -> Option<&CallableFunction> { self.all_functions .get(&hash_qualified_fn) @@ -1266,6 +1286,7 @@ impl Module { /// Get the number of variables, functions and type iterators in the [`Module`]. #[inline(always)] + #[must_use] pub fn count(&self) -> (usize, usize, usize) { ( self.variables.len(), @@ -1276,12 +1297,14 @@ 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)) } @@ -1289,6 +1312,7 @@ 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) } @@ -1303,6 +1327,7 @@ 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< @@ -1338,6 +1363,7 @@ impl Module { #[cfg(not(feature = "no_function"))] #[cfg(not(feature = "internals"))] #[inline(always)] + #[must_use] pub fn iter_script_fn_info( &self, ) -> impl Iterator { @@ -1359,6 +1385,7 @@ impl Module { #[cfg(not(feature = "no_function"))] #[cfg(feature = "internals")] #[inline(always)] + #[must_use] pub fn iter_script_fn_info( &self, ) -> impl Iterator< @@ -1395,6 +1422,7 @@ impl Module { /// # } /// ``` #[cfg(not(feature = "no_module"))] + #[must_use] pub fn eval_ast_as_new( mut scope: crate::Scope, ast: &crate::AST, @@ -1463,6 +1491,7 @@ impl Module { /// /// Panics if the [`Module`] is not yet indexed via [`build_index`][Module::build_index]. #[inline(always)] + #[must_use] pub fn contains_indexed_global_functions(&self) -> bool { self.contains_indexed_global_functions } @@ -1562,12 +1591,14 @@ impl Module { /// Does a type iterator exist in the entire module tree? #[inline(always)] + #[must_use] pub fn contains_qualified_iter(&self, id: TypeId) -> bool { self.all_type_iterators.contains_key(&id) } /// Does a type iterator exist in the module? #[inline(always)] + #[must_use] pub fn contains_iter(&self, id: TypeId) -> bool { self.type_iterators.contains_key(&id) } @@ -1609,12 +1640,14 @@ impl Module { /// Get the specified type iterator. #[inline(always)] + #[must_use] pub(crate) fn get_qualified_iter(&self, id: TypeId) -> Option { self.all_type_iterators.get(&id).cloned() } /// Get the specified type iterator. #[inline(always)] + #[must_use] pub(crate) fn get_iter(&self, id: TypeId) -> Option { self.type_iterators.get(&id).cloned() } @@ -1693,6 +1726,7 @@ impl From> for NamespaceRef { impl NamespaceRef { /// Get the [`Scope`][crate::Scope] index offset. #[inline(always)] + #[must_use] pub(crate) fn index(&self) -> Option { self.index } diff --git a/src/module/resolvers/collection.rs b/src/module/resolvers/collection.rs index 7ee036f0..81022aa4 100644 --- a/src/module/resolvers/collection.rs +++ b/src/module/resolvers/collection.rs @@ -41,6 +41,7 @@ impl ModuleResolversCollection { /// engine.set_module_resolver(collection); /// ``` #[inline(always)] + #[must_use] pub fn new() -> Self { Default::default() } @@ -76,11 +77,13 @@ 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() } @@ -92,11 +95,13 @@ impl ModuleResolversCollection { } /// Is this [`ModuleResolversCollection`] empty? #[inline(always)] + #[must_use] pub fn is_empty(&self) -> bool { self.0.is_empty() } /// Get the number of [module resolvers][ModuleResolver] in this [`ModuleResolversCollection`]. #[inline(always)] + #[must_use] pub fn len(&self) -> usize { self.0.len() } diff --git a/src/module/resolvers/file.rs b/src/module/resolvers/file.rs index d933b028..25f196f2 100644 --- a/src/module/resolvers/file.rs +++ b/src/module/resolvers/file.rs @@ -77,6 +77,7 @@ impl FileModuleResolver { /// engine.set_module_resolver(resolver); /// ``` #[inline(always)] + #[must_use] pub fn new() -> Self { Self::new_with_extension(RHAI_SCRIPT_EXTENSION) } @@ -99,6 +100,7 @@ impl FileModuleResolver { /// engine.set_module_resolver(resolver); /// ``` #[inline(always)] + #[must_use] pub fn new_with_path(path: impl Into) -> Self { Self::new_with_path_and_extension(path, RHAI_SCRIPT_EXTENSION) } @@ -118,6 +120,7 @@ impl FileModuleResolver { /// engine.set_module_resolver(resolver); /// ``` #[inline(always)] + #[must_use] pub fn new_with_extension(extension: impl Into) -> Self { Self { base_path: None, @@ -143,6 +146,7 @@ impl FileModuleResolver { /// engine.set_module_resolver(resolver); /// ``` #[inline(always)] + #[must_use] pub fn new_with_path_and_extension( path: impl Into, extension: impl Into, @@ -157,6 +161,7 @@ impl FileModuleResolver { /// Get the base path for script files. #[inline(always)] + #[must_use] pub fn base_path(&self) -> Option<&Path> { self.base_path.as_ref().map(PathBuf::as_ref) } @@ -169,6 +174,7 @@ impl FileModuleResolver { /// Get the script file extension. #[inline(always)] + #[must_use] pub fn extension(&self) -> &str { &self.extension } @@ -188,12 +194,14 @@ impl FileModuleResolver { } /// Is the cache enabled? #[inline(always)] + #[must_use] pub fn is_cache_enabled(&self) -> bool { self.cache_enabled } /// Is a particular path cached? #[inline(always)] + #[must_use] pub fn is_cached(&self, path: &str, source_path: Option<&str>) -> bool { if !self.cache_enabled { return false; @@ -208,16 +216,19 @@ impl FileModuleResolver { } /// Empty the internal cache. #[inline(always)] - pub fn clear_cache(&mut self) { + pub fn clear_cache(&mut self) -> &mut Self { #[cfg(not(feature = "sync"))] self.cache.borrow_mut().clear(); #[cfg(feature = "sync")] self.cache.write().unwrap().clear(); + + self } /// Remove the specified path from internal cache. /// /// The next time this path is resolved, the script file will be loaded once again. #[inline(always)] + #[must_use] pub fn clear_cache_for_path( &mut self, path: &str, @@ -240,6 +251,8 @@ impl FileModuleResolver { .map(|(_, v)| v); } /// 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 6014d23e..8303bc9b 100644 --- a/src/module/resolvers/mod.rs +++ b/src/module/resolvers/mod.rs @@ -23,6 +23,7 @@ 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, @@ -41,6 +42,7 @@ pub trait ModuleResolver: SendSync { /// Override the default implementation of this method if the module resolver /// serves modules based on compiled Rhai scripts. #[allow(unused_variables)] + #[must_use] fn resolve_ast( &self, engine: &Engine, diff --git a/src/module/resolvers/stat.rs b/src/module/resolvers/stat.rs index 5e905926..f1fcc3b4 100644 --- a/src/module/resolvers/stat.rs +++ b/src/module/resolvers/stat.rs @@ -41,6 +41,7 @@ impl StaticModuleResolver { /// engine.set_module_resolver(resolver); /// ``` #[inline(always)] + #[must_use] pub fn new() -> Self { Default::default() } @@ -57,38 +58,45 @@ impl StaticModuleResolver { } /// Does the path exist? #[inline(always)] + #[must_use] pub fn contains_path(&self, path: &str) -> bool { self.0.contains_key(path) } /// 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) } /// Remove all [modules][Module]. #[inline(always)] - pub fn clear(&mut self) { + pub fn clear(&mut self) -> &mut Self { self.0.clear(); + self } /// Is this [`StaticModuleResolver`] empty? #[inline(always)] @@ -97,6 +105,7 @@ impl StaticModuleResolver { } /// Get the number of [modules][Module] in this [`StaticModuleResolver`]. #[inline(always)] + #[must_use] pub fn len(&self) -> usize { self.0.len() } @@ -105,10 +114,11 @@ impl StaticModuleResolver { /// /// Existing modules of the same path name are overwritten. #[inline(always)] - pub fn merge(&mut self, other: Self) { + pub fn merge(&mut self, other: Self) -> &mut Self { if !other.is_empty() { self.0.extend(other.0.into_iter()); } + self } } diff --git a/src/optimize.rs b/src/optimize.rs index da14b1d2..8d3054bf 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -4,8 +4,8 @@ use crate::ast::{Expr, OpAssignment, Stmt}; use crate::dynamic::AccessMode; use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF}; use crate::fn_builtin::get_builtin_binary_op_fn; +use crate::fn_hash::get_hasher; use crate::token::Token; -use crate::utils::get_hasher; use crate::{ calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, ImmutableString, Module, Position, Scope, StaticVec, AST, @@ -248,12 +248,12 @@ fn optimize_stmt_block( if preserve_result && index >= statements.len() - 1 { break; } else { - match &statements[index] { - stmt if is_pure(stmt) && index >= first_non_constant => { + match statements[index] { + ref stmt if is_pure(stmt) && index >= first_non_constant => { state.set_dirty(); statements.remove(index); } - stmt if stmt.is_pure() => { + ref stmt if stmt.is_pure() => { state.set_dirty(); if index < first_non_constant { first_non_constant -= 1; @@ -269,36 +269,37 @@ fn optimize_stmt_block( // We cannot remove anything for non-pure statements due to potential side-effects. if preserve_result { loop { - match &mut statements[..] { + match statements[..] { // { return; } -> {} [Stmt::Return(crate::ast::ReturnType::Return, None, _)] if reduce_return => { state.set_dirty(); statements.clear(); } - [stmt] if !stmt.returns_value() && is_pure(stmt) => { + [ref stmt] if !stmt.returns_value() && is_pure(stmt) => { state.set_dirty(); statements.clear(); } // { ...; return; } -> { ... } - [.., last_stmt, Stmt::Return(crate::ast::ReturnType::Return, None, _)] + [.., ref last_stmt, Stmt::Return(crate::ast::ReturnType::Return, None, _)] if reduce_return && !last_stmt.returns_value() => { state.set_dirty(); statements.pop().unwrap(); } // { ...; return val; } -> { ...; val } - [.., Stmt::Return(crate::ast::ReturnType::Return, expr, pos)] + [.., Stmt::Return(crate::ast::ReturnType::Return, ref mut expr, pos)] if reduce_return => { state.set_dirty(); *statements.last_mut().unwrap() = if let Some(expr) = expr { Stmt::Expr(mem::take(expr)) } else { - Stmt::Noop(*pos) + Stmt::Noop(pos) }; } - [.., second_last_stmt, Stmt::Noop(_)] if second_last_stmt.returns_value() => (), - [.., second_last_stmt, last_stmt] + [.., ref second_last_stmt, Stmt::Noop(_)] + if second_last_stmt.returns_value() => {} + [.., ref second_last_stmt, ref last_stmt] if !last_stmt.returns_value() && is_pure(last_stmt) => { state.set_dirty(); @@ -313,8 +314,8 @@ fn optimize_stmt_block( } } else { loop { - match &statements[..] { - [stmt] if is_pure(stmt) => { + match statements[..] { + [ref stmt] if is_pure(stmt) => { state.set_dirty(); statements.clear(); } @@ -326,13 +327,13 @@ fn optimize_stmt_block( statements.pop().unwrap(); } // { ...; return pure_val; } -> { ... } - [.., Stmt::Return(crate::ast::ReturnType::Return, Some(expr), _)] + [.., Stmt::Return(crate::ast::ReturnType::Return, Some(ref expr), _)] if reduce_return && expr.is_pure() => { state.set_dirty(); statements.pop().unwrap(); } - [.., last_stmt] if is_pure(last_stmt) => { + [.., ref last_stmt] if is_pure(last_stmt) => { state.set_dirty(); statements.pop().unwrap(); } @@ -373,8 +374,8 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { && x2.args[0].get_variable_name(true) == x.0.get_variable_name(true) ) => { - match &mut x.2 { - Expr::FnCall(x2, _) => { + match x.2 { + Expr::FnCall(ref mut x2, _) => { state.set_dirty(); let op = Token::lookup_from_syntax(&x2.name).unwrap(); let op_assignment = op.make_op_assignment().unwrap(); @@ -444,11 +445,11 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { // if expr { if_block } else { else_block } Stmt::If(condition, x, _) => { optimize_expr(condition, state, false); - let if_block = mem::take(x.0.statements()).into_vec(); - *x.0.statements() = + let if_block = mem::take(x.0.statements_mut()).into_vec(); + *x.0.statements_mut() = optimize_stmt_block(if_block, state, preserve_result, true, false).into(); - let else_block = mem::take(x.1.statements()).into_vec(); - *x.1.statements() = + let else_block = mem::take(x.1.statements_mut()).into_vec(); + *x.1.statements_mut() = optimize_stmt_block(else_block, state, preserve_result, true, false).into(); } @@ -520,8 +521,8 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { _ => { block.0 = Some(condition); - *block.1.statements() = optimize_stmt_block( - mem::take(block.1.statements()).into_vec(), + *block.1.statements_mut() = optimize_stmt_block( + mem::take(block.1.statements_mut()).into_vec(), state, preserve_result, true, @@ -541,8 +542,8 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { x.0.remove(&key); } - let def_block = mem::take(x.1.statements()).into_vec(); - *x.1.statements() = + let def_block = mem::take(x.1.statements_mut()).into_vec(); + *x.1.statements_mut() = optimize_stmt_block(def_block, state, preserve_result, true, false).into(); } @@ -554,8 +555,8 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { // while expr { block } Stmt::While(condition, body, _) => { optimize_expr(condition, state, false); - let block = mem::take(body.statements()).into_vec(); - *body.statements() = optimize_stmt_block(block, state, false, true, false).into(); + let block = mem::take(body.statements_mut()).into_vec(); + *body.statements_mut() = optimize_stmt_block(block, state, false, true, false).into(); if body.len() == 1 { match body[0] { @@ -582,7 +583,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { | Stmt::Do(body, Expr::BoolConstant(false, _), true, _) => { state.set_dirty(); let block_pos = body.position(); - let block = mem::take(body.statements()).into_vec(); + let block = mem::take(body.statements_mut()).into_vec(); *stmt = Stmt::Block( optimize_stmt_block(block, state, false, true, false).into_boxed_slice(), block_pos, @@ -591,14 +592,14 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { // do { block } while|until expr Stmt::Do(body, condition, _, _) => { optimize_expr(condition, state, false); - let block = mem::take(body.statements()).into_vec(); - *body.statements() = optimize_stmt_block(block, state, false, true, false).into(); + let block = mem::take(body.statements_mut()).into_vec(); + *body.statements_mut() = optimize_stmt_block(block, state, false, true, false).into(); } // for id in expr { block } Stmt::For(iterable, x, _) => { optimize_expr(iterable, state, false); - let body = mem::take(x.2.statements()).into_vec(); - *x.2.statements() = optimize_stmt_block(body, state, false, true, false).into(); + let body = mem::take(x.2.statements_mut()).into_vec(); + *x.2.statements_mut() = optimize_stmt_block(body, state, false, true, false).into(); } // let id = expr; Stmt::Let(expr, _, _, _) => optimize_expr(expr, state, false), @@ -624,7 +625,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { } } // try { pure try_block } catch ( var ) { catch_block } -> try_block - Stmt::TryCatch(x, _, _) if x.0.iter().all(Stmt::is_pure) => { + Stmt::TryCatch(x, _) if x.0.iter().all(Stmt::is_pure) => { // If try block is pure, there will never be any exceptions state.set_dirty(); let try_pos = x.0.position(); @@ -635,11 +636,13 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { ); } // try { try_block } catch ( var ) { catch_block } - Stmt::TryCatch(x, _, _) => { - let try_block = mem::take(x.0.statements()).into_vec(); - *x.0.statements() = optimize_stmt_block(try_block, state, false, true, false).into(); - let catch_block = mem::take(x.2.statements()).into_vec(); - *x.2.statements() = optimize_stmt_block(catch_block, state, false, true, false).into(); + Stmt::TryCatch(x, _) => { + let try_block = mem::take(x.0.statements_mut()).into_vec(); + *x.0.statements_mut() = + optimize_stmt_block(try_block, state, false, true, false).into(); + let catch_block = mem::take(x.2.statements_mut()).into_vec(); + *x.2.statements_mut() = + optimize_stmt_block(catch_block, state, false, true, false).into(); } // func(...) Stmt::Expr(expr @ Expr::FnCall(_, _)) => { @@ -686,7 +689,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) { Expr::Stmt(x) if x.is_empty() => { state.set_dirty(); *expr = Expr::Unit(x.position()) } // { stmt; ... } - do not count promotion as dirty because it gets turned back into an array Expr::Stmt(x) => { - *x.statements() = optimize_stmt_block(mem::take(x.statements()).into_vec(), state, true, true, false).into(); + *x.statements_mut() = optimize_stmt_block(mem::take(x.statements_mut()).into_vec(), state, true, true, false).into(); // { Stmt(Expr) } - promote match x.as_mut().as_mut() { @@ -726,7 +729,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) { // Array literal where everything is pure - promote the indexed item. // All other items can be thrown away. state.set_dirty(); - let mut result = a.remove(*i as usize); + let mut result = mem::take(&mut a[*i as usize]); result.set_position(*pos); *expr = result; } @@ -737,7 +740,8 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) { // Array literal where everything is pure - promote the indexed item. // All other items can be thrown away. state.set_dirty(); - let mut result = a.remove(a.len() - i.abs() as usize); + let index = a.len() - i.abs() as usize; + let mut result = mem::take(&mut a[index]); result.set_position(*pos); *expr = result; } @@ -794,9 +798,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) { } // `... ${ ... } ...` Expr::InterpolatedString(x) => { - x.iter_mut().for_each(|expr| optimize_expr(expr, state, false)); - - let mut n= 0; + let mut n = 0; // Merge consecutive strings while n < x.len()-1 { @@ -948,7 +950,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) { 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_hash(), arg_types.as_ref()) { + 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(); @@ -1136,9 +1138,9 @@ pub fn optimize_into_ast( // Optimize the function body let state = &mut State::new(engine, lib2, level); - let body = mem::take(fn_def.body.statements()).into_vec(); + let body = mem::take(fn_def.body.statements_mut()).into_vec(); - *fn_def.body.statements() = + *fn_def.body.statements_mut() = optimize_stmt_block(body, state, true, true, true).into(); fn_def diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index 4e60e986..c4b51b1a 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -287,9 +287,8 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, { { reg_range!(lib | "range" => i8, u8, i16, u16, i32, u32, i64, u64); - if cfg!(not(target_arch = "wasm32")) { - reg_range!(lib | "range" => i128, u128); - } + #[cfg(not(target_arch = "wasm32"))] + reg_range!(lib | "range" => i128, u128); } reg_range!(lib | step "range" => INT); @@ -299,9 +298,8 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, { { reg_range!(lib | step "range" => i8, u8, i16, u16, i32, u32, i64, u64); - if cfg!(not(target_arch = "wasm32")) { - reg_range!(lib | step "range" => i128, u128); - } + #[cfg(not(target_arch = "wasm32"))] + reg_range!(lib | step "range" => i128, u128); } #[cfg(not(feature = "no_float"))] diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index 1e790000..efa02f1e 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -9,7 +9,7 @@ use std::prelude::v1::*; use crate::FLOAT; #[cfg(not(feature = "no_float"))] -use crate::result::EvalAltResult; +use crate::error::EvalAltResult; #[cfg(feature = "no_std")] #[cfg(not(feature = "no_float"))] diff --git a/src/packages/mod.rs b/src/packages/mod.rs index 3e3975e3..5e9b335d 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -38,11 +38,13 @@ pub trait Package { fn init(lib: &mut Module); /// Retrieve the generic package library from this package. + #[must_use] fn as_shared_module(&self) -> Shared; /// Retrieve the generic package library from this package. /// This method is deprecated and will be removed in the future. /// Use [`as_shared_module`][Package::as_shared_module] instead. + #[must_use] #[deprecated(since = "0.19.9", note = "use `as_shared_module` instead")] fn get(&self) -> Shared { self.as_shared_module() diff --git a/src/parser.rs b/src/parse.rs similarity index 93% rename from src/parser.rs rename to src/parse.rs index 998dec80..6b443fa4 100644 --- a/src/parser.rs +++ b/src/parse.rs @@ -4,19 +4,18 @@ use crate::ast::{ BinaryExpr, CustomExpr, Expr, FnCallExpr, FnCallHashes, Ident, OpAssignment, ReturnType, ScriptFnDef, Stmt, StmtBlock, }; +use crate::custom_syntax::{ + CustomSyntax, MARKER_BLOCK, MARKER_BOOL, MARKER_EXPR, MARKER_IDENT, MARKER_INT, MARKER_STRING, +}; use crate::dynamic::{AccessMode, Union}; use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS}; use crate::module::NamespaceRef; -use crate::optimize::optimize_into_ast; -use crate::optimize::OptimizationLevel; -use crate::syntax::{ - CustomSyntax, MARKER_BLOCK, MARKER_BOOL, MARKER_EXPR, MARKER_IDENT, MARKER_INT, MARKER_STRING, -}; +use crate::optimize::{optimize_into_ast, OptimizationLevel}; +use crate::fn_hash::get_hasher; use crate::token::{ is_keyword_function, is_valid_identifier, Token, TokenStream, TokenizerControl, }; -use crate::utils::{get_hasher, IdentifierBuilder}; use crate::{ calc_fn_hash, calc_qualified_fn_hash, Dynamic, Engine, Identifier, LexError, ParseError, ParseErrorType, Position, Scope, Shared, StaticVec, AST, @@ -30,7 +29,7 @@ use std::{ }; #[cfg(not(feature = "no_float"))] -use crate::{syntax::MARKER_FLOAT, FLOAT}; +use crate::{custom_syntax::MARKER_FLOAT, FLOAT}; #[cfg(not(feature = "no_function"))] use crate::FnAccess; @@ -41,6 +40,36 @@ type FunctionsLib = BTreeMap>; const NEVER_ENDS: &str = "never fails because `TokenStream` never ends"; +/// A factory of identifiers from text strings. +/// +/// When [`SmartString`](https://crates.io/crates/smartstring) is used as [`Identifier`], +/// this just returns a copy because most identifiers in Rhai are short and ASCII-based. +/// +/// When [`ImmutableString`] is used as [`Identifier`], this type acts as an interner which keeps a +/// collection of strings and returns shared instances, only creating a new string when it is not +/// yet interned. +#[derive(Debug, Clone, Default, Hash)] +pub struct IdentifierBuilder( + #[cfg(feature = "no_smartstring")] std::collections::BTreeSet, +); + +impl IdentifierBuilder { + /// Get an identifier from a text string. + #[inline(always)] + #[must_use] + pub fn get(&mut self, text: impl AsRef + Into) -> Identifier { + #[cfg(not(feature = "no_smartstring"))] + return text.into(); + + #[cfg(feature = "no_smartstring")] + return self.0.get(text.as_ref()).cloned().unwrap_or_else(|| { + let s: Identifier = text.into(); + self.0.insert(s.clone()); + s + }); + } +} + /// A type that encapsulates the current state of the parser. #[derive(Debug)] pub struct ParseState<'e> { @@ -59,8 +88,8 @@ pub struct ParseState<'e> { external_vars: BTreeMap, /// An indicator that disables variable capturing into externals one single time /// up until the nearest consumed Identifier token. - /// If set to false the next call to `access_var` will not capture the variable. - /// All consequent calls to `access_var` will not be affected + /// If set to false the next call to [`access_var`][ParseState::access_var] will not capture the variable. + /// All consequent calls to [`access_var`][ParseState::access_var] will not be affected #[cfg(not(feature = "no_closure"))] allow_capture: bool, /// Encapsulates a local stack with imported [module][crate::Module] names. @@ -78,6 +107,7 @@ pub struct ParseState<'e> { impl<'e> ParseState<'e> { /// Create a new [`ParseState`]. #[inline(always)] + #[must_use] pub fn new(engine: &'e Engine, tokenizer_control: TokenizerControl) -> Self { Self { engine, @@ -103,8 +133,8 @@ impl<'e> ParseState<'e> { /// /// If the variable is not present in the scope adds it to the list of external variables /// - /// The return value is the offset to be deducted from `Stack::len`, - /// i.e. the top element of the [`ParseState`] is offset 1. + /// The return value is the offset to be deducted from `ParseState::stack::len`, + /// 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)] @@ -155,6 +185,7 @@ impl<'e> ParseState<'e> { /// Panics when called under `no_module`. #[cfg(not(feature = "no_module"))] #[inline(always)] + #[must_use] pub fn find_module(&self, name: &str) -> Option { self.modules .iter() @@ -166,6 +197,7 @@ impl<'e> ParseState<'e> { /// Get an interned string, creating one if it is not yet interned. #[inline(always)] + #[must_use] pub fn get_identifier(&mut self, text: impl AsRef + Into) -> Identifier { self.interned_strings.get(text) } @@ -197,6 +229,7 @@ struct ParseSettings { impl ParseSettings { /// Create a new `ParseSettings` with one higher expression level. #[inline(always)] + #[must_use] pub fn level_up(&self) -> Self { Self { level: self.level + 1, @@ -206,6 +239,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] pub fn ensure_level_within_max_limit( &self, limit: Option, @@ -222,6 +256,7 @@ impl Expr { /// All other variants are untouched. #[cfg(not(feature = "no_object"))] #[inline(always)] + #[must_use] fn into_property(self, state: &mut ParseState) -> Self { match self { Self::Variable(_, pos, x) if x.1.is_none() => { @@ -366,17 +401,15 @@ fn parse_fn_call( args.shrink_to_fit(); - return Ok(Expr::FnCall( - Box::new(FnCallExpr { - name: state.get_identifier(id), - capture, - namespace, - hashes, - args, - ..Default::default() - }), - settings.pos, - )); + return Ok(FnCallExpr { + name: state.get_identifier(id), + capture, + namespace, + hashes, + args, + ..Default::default() + } + .into_fn_call_expr(settings.pos)); } // id... _ => (), @@ -418,17 +451,15 @@ fn parse_fn_call( args.shrink_to_fit(); - return Ok(Expr::FnCall( - Box::new(FnCallExpr { - name: state.get_identifier(id), - capture, - namespace, - hashes, - args, - ..Default::default() - }), - settings.pos, - )); + return Ok(FnCallExpr { + name: state.get_identifier(id), + capture, + namespace, + hashes, + args, + ..Default::default() + } + .into_fn_call_expr(settings.pos)); } // id(...args, (Token::Comma, _) => { @@ -472,7 +503,7 @@ fn parse_index_chain( let idx_expr = parse_expr(input, state, lib, settings.level_up())?; // Check type of indexing - must be integer or string - match &idx_expr { + match idx_expr { Expr::IntegerConstant(_, pos) => match lhs { Expr::IntegerConstant(_, _) | Expr::Array(_, _) @@ -483,7 +514,7 @@ fn parse_index_chain( return Err(PERR::MalformedIndexExpr( "Object map access expects string index, not a number".into(), ) - .into_err(*pos)) + .into_err(pos)) } #[cfg(not(feature = "no_float"))] @@ -686,7 +717,7 @@ fn parse_array_literal( arr.shrink_to_fit(); - Ok(Expr::Array(Box::new(arr), settings.pos)) + Ok(Expr::Array(arr.into(), settings.pos)) } /// Parse a map literal. @@ -1095,7 +1126,7 @@ fn parse_primary( } segments.shrink_to_fit(); - Expr::InterpolatedString(Box::new(segments)) + Expr::InterpolatedString(segments.into()) } // Array literal @@ -1239,14 +1270,14 @@ fn parse_primary( let (_, namespace, name) = *x; settings.pos = var_pos; - let ns = namespace.map(|(_, ns)| ns); + let ns = namespace.map(|(ns, _)| ns); parse_fn_call(input, state, lib, name, true, ns, settings.level_up())? } // Function call (Expr::Variable(_, var_pos, x), Token::LeftParen) => { let (_, namespace, name) = *x; settings.pos = var_pos; - let ns = namespace.map(|(_, ns)| ns); + let ns = namespace.map(|(ns, _)| ns); parse_fn_call(input, state, lib, name, false, ns, settings.level_up())? } // module access @@ -1258,12 +1289,12 @@ fn parse_primary( pos: var_pos, }; - if let Some((_, ref mut namespace)) = namespace { + if let Some((ref mut namespace, _)) = namespace { namespace.push(var_name_def); } else { let mut ns: NamespaceRef = Default::default(); ns.push(var_name_def); - namespace = Some((42, ns)); + namespace = Some((ns, 42)); } Expr::Variable( @@ -1308,15 +1339,15 @@ fn parse_primary( // Cache the hash key for namespace-qualified variables match root_expr { - Expr::Variable(_, _, ref mut x) if x.1.is_some() => Some(x), - Expr::Index(ref mut x, _) | Expr::Dot(ref mut x, _) => match &mut x.lhs { - Expr::Variable(_, _, x) if x.1.is_some() => Some(x), + 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::Variable(_, _, ref mut x) if x.1.is_some() => Some(x.as_mut()), _ => None, }, _ => None, } - .map(|x| match x.as_mut() { - (_, Some((hash, namespace)), name) => { + .map(|x| match x { + (_, Some((namespace, hash)), name) => { *hash = calc_qualified_fn_hash(namespace.iter().map(|v| v.name.as_str()), name, 0); #[cfg(not(feature = "no_module"))] @@ -1370,15 +1401,13 @@ fn parse_unary( args.push(expr); args.shrink_to_fit(); - Ok(Expr::FnCall( - Box::new(FnCallExpr { - name: state.get_identifier("-"), - hashes: FnCallHashes::from_native(calc_fn_hash("-", 1)), - args, - ..Default::default() - }), - pos, - )) + Ok(FnCallExpr { + name: state.get_identifier("-"), + hashes: FnCallHashes::from_native(calc_fn_hash("-", 1)), + args, + ..Default::default() + } + .into_fn_call_expr(pos)) } } } @@ -1397,15 +1426,13 @@ fn parse_unary( args.push(expr); args.shrink_to_fit(); - Ok(Expr::FnCall( - Box::new(FnCallExpr { - name: state.get_identifier("+"), - hashes: FnCallHashes::from_native(calc_fn_hash("+", 1)), - args, - ..Default::default() - }), - pos, - )) + Ok(FnCallExpr { + name: state.get_identifier("+"), + hashes: FnCallHashes::from_native(calc_fn_hash("+", 1)), + args, + ..Default::default() + } + .into_fn_call_expr(pos)) } } } @@ -1416,15 +1443,13 @@ fn parse_unary( args.push(parse_unary(input, state, lib, settings.level_up())?); args.shrink_to_fit(); - Ok(Expr::FnCall( - Box::new(FnCallExpr { - name: state.get_identifier("!"), - hashes: FnCallHashes::from_native(calc_fn_hash("!", 1)), - args, - ..Default::default() - }), - pos, - )) + Ok(FnCallExpr { + name: state.get_identifier("!"), + hashes: FnCallHashes::from_native(calc_fn_hash("!", 1)), + args, + ..Default::default() + } + .into_fn_call_expr(pos)) } // Token::EOF => Err(PERR::UnexpectedEOF.into_err(settings.pos)), @@ -1441,6 +1466,7 @@ fn make_assignment_stmt<'a>( rhs: Expr, op_pos: Position, ) -> Result { + #[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 { @@ -1460,17 +1486,17 @@ fn make_assignment_stmt<'a>( let op_info = op.map(|v| OpAssignment::new(v)); - match &lhs { + match lhs { // const_expr = rhs - expr if expr.is_constant() => { + ref expr if expr.is_constant() => { Err(PERR::AssignmentToConstant("".into()).into_err(lhs.position())) } // var (non-indexed) = rhs - Expr::Variable(None, _, x) if x.0.is_none() => { + Expr::Variable(None, _, ref x) if x.0.is_none() => { Ok(Stmt::Assignment(Box::new((lhs, op_info, rhs)), op_pos)) } // var (indexed) = rhs - Expr::Variable(i, var_pos, x) => { + Expr::Variable(i, var_pos, ref x) => { let (index, _, name) = x.as_ref(); let index = i.map_or_else( || { @@ -1488,20 +1514,20 @@ fn make_assignment_stmt<'a>( } // Constant values cannot be assigned to AccessMode::ReadOnly => { - Err(PERR::AssignmentToConstant(name.to_string()).into_err(*var_pos)) + Err(PERR::AssignmentToConstant(name.to_string()).into_err(var_pos)) } } } // xxx[???]... = rhs, xxx.prop... = rhs - Expr::Index(x, _) | Expr::Dot(x, _) => { + Expr::Index(ref x, _) | Expr::Dot(ref x, _) => { match check_lvalue(&x.rhs, matches!(lhs, Expr::Dot(_, _))) { - None => match &x.lhs { + None => match x.lhs { // var[???] = rhs, var.??? = rhs Expr::Variable(_, _, _) => { Ok(Stmt::Assignment(Box::new((lhs, op_info, rhs)), op_pos)) } // expr[???] = rhs, expr.??? = rhs - expr => { + ref expr => { Err(PERR::AssignmentToInvalidLHS("".to_string()).into_err(expr.position())) } }, @@ -1580,7 +1606,7 @@ fn make_dot_expr( // lhs.module::id - syntax error (_, Expr::Variable(_, _, x)) if x.1.is_some() => { return Err(PERR::PropertyExpected - .into_err(x.1.expect("never fails because the namespace is `Some`").1[0].pos)) + .into_err(x.1.expect("never fails because the namespace is `Some`").0[0].pos)) } // lhs.prop (lhs, prop @ Expr::Property(_)) => { @@ -1765,19 +1791,17 @@ fn parse_binary_op( | Token::PowerOf | Token::Ampersand | Token::Pipe - | Token::XOr => Expr::FnCall(Box::new(FnCallExpr { args, ..op_base }), pos), + | Token::XOr => FnCallExpr { args, ..op_base }.into_fn_call_expr(pos), // '!=' defaults to true when passed invalid operands - Token::NotEqualsTo => Expr::FnCall(Box::new(FnCallExpr { args, ..op_base }), pos), + Token::NotEqualsTo => FnCallExpr { args, ..op_base }.into_fn_call_expr(pos), // Comparison operators default to false when passed invalid operands Token::EqualsTo | Token::LessThan | Token::LessThanEqualsTo | Token::GreaterThan - | Token::GreaterThanEqualsTo => { - Expr::FnCall(Box::new(FnCallExpr { args, ..op_base }), pos) - } + | Token::GreaterThanEqualsTo => FnCallExpr { args, ..op_base }.into_fn_call_expr(pos), Token::Or => { let rhs = args @@ -1816,16 +1840,13 @@ fn parse_binary_op( args.shrink_to_fit(); // Convert into a call to `contains` - let hash = calc_fn_hash(OP_CONTAINS, 2); - Expr::FnCall( - Box::new(FnCallExpr { - hashes: FnCallHashes::from_script(hash), - args, - name: state.get_identifier(OP_CONTAINS), - ..op_base - }), - pos, - ) + FnCallExpr { + hashes: FnCallHashes::from_script(calc_fn_hash(OP_CONTAINS, 2)), + args, + name: state.get_identifier(OP_CONTAINS), + ..op_base + } + .into_fn_call_expr(pos) } Token::Custom(s) @@ -1837,18 +1858,16 @@ fn parse_binary_op( { let hash = calc_fn_hash(&s, 2); - Expr::FnCall( - Box::new(FnCallExpr { - hashes: if is_valid_identifier(s.chars()) { - FnCallHashes::from_script(hash) - } else { - FnCallHashes::from_native(hash) - }, - args, - ..op_base - }), - pos, - ) + FnCallExpr { + hashes: if is_valid_identifier(s.chars()) { + FnCallHashes::from_script(hash) + } else { + FnCallHashes::from_native(hash) + }, + args, + ..op_base + } + .into_fn_call_expr(pos) } op_token => return Err(PERR::UnknownOperator(op_token.into()).into_err(pos)), @@ -1889,12 +1908,10 @@ fn parse_custom_syntax( settings.pos = *fwd_pos; let settings = settings.level_up(); - let required_token = if let Some(seg) = parse_func(&segments, fwd_token.syntax().as_ref()) - .map_err(|err| err.0.into_err(settings.pos))? - { - seg - } else { - break; + let required_token = match parse_func(&segments, fwd_token.syntax().as_ref()) { + Ok(Some(seg)) => seg, + Ok(None) => break, + Err(err) => return Err(err.0.into_err(settings.pos)), }; match required_token.as_str() { @@ -1994,14 +2011,12 @@ fn parse_custom_syntax( keywords.shrink_to_fit(); tokens.shrink_to_fit(); - Ok(Expr::Custom( - Box::new(CustomExpr { - keywords, - tokens, - scope_changed: syntax.scope_changed, - }), - pos, - )) + Ok(CustomExpr { + keywords, + tokens, + scope_changed: syntax.scope_changed, + } + .into_custom_syntax_expr(pos)) } /// Parse an expression. @@ -2407,23 +2422,19 @@ fn parse_export( _ => (), } - let mut exports = Vec::with_capacity(4); + let mut exports = Vec::<(Ident, Ident)>::with_capacity(4); loop { let (id, id_pos) = parse_var_name(input)?; - let rename = if match_token(input, Token::As).0 { + let (rename, rename_pos) = if match_token(input, Token::As).0 { let (name, pos) = parse_var_name(input)?; - if exports.iter().any(|(_, alias)| match alias { - Some(Ident { name: alias, .. }) if alias == &name => true, - _ => false, - }) { + if exports.iter().any(|(_, alias)| alias.name == name) { return Err(PERR::DuplicatedVariable(name).into_err(pos)); } - let name = state.get_identifier(name); - Some(Ident { name, pos }) + (name, pos) } else { - None + (Default::default(), Position::NONE) }; exports.push(( @@ -2431,7 +2442,10 @@ fn parse_export( name: state.get_identifier(id), pos: id_pos, }, - rename, + Ident { + name: state.get_identifier(rename), + pos: rename_pos, + }, )); match input.peek().expect(NEVER_ENDS) { @@ -2817,7 +2831,6 @@ fn parse_try_catch( Ok(Stmt::TryCatch( Box::new((body.into(), var_def, catch_body.into())), settings.pos, - catch_pos, )) } @@ -2838,12 +2851,11 @@ fn parse_fn( let (token, pos) = input.next().expect(NEVER_ENDS); - let name = token - .into_function_name_for_override() - .map_err(|t| match t { - Token::Reserved(s) => PERR::Reserved(s).into_err(pos), - _ => PERR::FnMissingName.into_err(pos), - })?; + let name = match token.into_function_name_for_override() { + Ok(r) => r, + Err(Token::Reserved(s)) => return Err(PERR::Reserved(s).into_err(pos)), + Err(_) => return Err(PERR::FnMissingName.into_err(pos)), + }; match input.peek().expect(NEVER_ENDS) { (Token::LeftParen, _) => eat_token(input, Token::LeftParen), @@ -2940,36 +2952,31 @@ fn make_curry_from_externals( } let num_externals = externals.len(); - let mut args: StaticVec<_> = Default::default(); + let mut args = StaticVec::with_capacity(externals.len() + 1); args.push(fn_expr); - externals.iter().for_each(|x| { - args.push(Expr::Variable( - None, - Position::NONE, - Box::new((None, None, x.clone())), - )); - }); - - args.shrink_to_fit(); - - let expr = Expr::FnCall( - Box::new(FnCallExpr { - name: state.get_identifier(crate::engine::KEYWORD_FN_PTR_CURRY), - hashes: FnCallHashes::from_native(calc_fn_hash( - crate::engine::KEYWORD_FN_PTR_CURRY, - num_externals + 1, - )), - args, - ..Default::default() - }), - pos, + args.extend( + externals + .iter() + .cloned() + .map(|x| Expr::Variable(None, Position::NONE, Box::new((None, None, x)))), ); + let expr = FnCallExpr { + name: state.get_identifier(crate::engine::KEYWORD_FN_PTR_CURRY), + hashes: FnCallHashes::from_native(calc_fn_hash( + crate::engine::KEYWORD_FN_PTR_CURRY, + num_externals + 1, + )), + args, + ..Default::default() + } + .into_fn_call_expr(pos); + // Convert the entire expression into a statement block, then insert the relevant // [`Share`][Stmt::Share] statements. - let mut statements: StaticVec<_> = Default::default(); + let mut statements = StaticVec::with_capacity(externals.len() + 1); statements.extend(externals.into_iter().map(Stmt::Share)); statements.push(Stmt::Expr(expr)); Expr::Stmt(Box::new(StmtBlock::new(statements, pos))) @@ -2986,7 +2993,7 @@ fn parse_anon_fn( #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; - let mut params: StaticVec<_> = Default::default(); + let mut params_list: StaticVec<_> = Default::default(); if input.next().expect(NEVER_ENDS).0 != Token::Or { if !match_token(input, Token::Pipe).0 { @@ -2994,12 +3001,12 @@ fn parse_anon_fn( match input.next().expect(NEVER_ENDS) { (Token::Pipe, _) => break, (Token::Identifier(s), pos) => { - if params.iter().any(|(p, _)| p == &s) { + 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.push((s, pos)) + params_list.push(s) } (Token::LexError(err), pos) => return Err(err.into_err(pos)), (_, pos) => { @@ -3033,29 +3040,22 @@ fn parse_anon_fn( // External variables may need to be processed in a consistent order, // so extract them into a list. - let externals: StaticVec = { - #[cfg(not(feature = "no_closure"))] - { - state - .external_vars - .iter() - .map(|(name, _)| name.clone()) - .collect() - } - #[cfg(feature = "no_closure")] - Default::default() - }; + #[cfg(not(feature = "no_closure"))] + let externals: StaticVec = state + .external_vars + .iter() + .map(|(name, _)| name.clone()) + .collect(); - let mut params: StaticVec<_> = if cfg!(not(feature = "no_closure")) { - externals - .iter() - .cloned() - .chain(params.into_iter().map(|(v, _)| v)) - .collect() - } else { - params.into_iter().map(|(v, _)| v).collect() - }; - params.shrink_to_fit(); + #[cfg(not(feature = "no_closure"))] + let mut params = StaticVec::with_capacity(params_list.len() + externals.len()); + #[cfg(feature = "no_closure")] + let mut params = StaticVec::with_capacity(params_list.len()); + + #[cfg(not(feature = "no_closure"))] + params.extend(externals.iter().cloned()); + + params.append(&mut params_list); // Create unique function name by hashing the script body plus the parameters. let hasher = &mut get_hasher(); @@ -3091,6 +3091,8 @@ fn parse_anon_fn( } impl Engine { + /// Parse a global level expression. + #[must_use] pub(crate) fn parse_global_expr( &self, input: &mut TokenStream, @@ -3132,6 +3134,7 @@ impl Engine { } /// Parse the global level statements. + #[must_use] fn parse_global_level( &self, input: &mut TokenStream, @@ -3193,6 +3196,7 @@ impl Engine { /// Run the parser on an input stream, returning an AST. #[inline(always)] + #[must_use] pub(crate) fn parse( &self, input: &mut TokenStream, diff --git a/src/plugin.rs b/src/plugin.rs index 73c2bb1d..6e9e88ec 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -21,8 +21,10 @@ 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? + #[must_use] fn is_method_call(&self) -> bool; } diff --git a/src/scope.rs b/src/scope.rs index 1ba7a2f3..b1d7c749 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -95,6 +95,7 @@ impl<'a> Scope<'a> { /// assert_eq!(my_scope.get_value::("x").unwrap(), 42); /// ``` #[inline(always)] + #[must_use] pub fn new() -> Self { Self { values: Default::default(), @@ -121,6 +122,7 @@ impl<'a> Scope<'a> { /// assert!(my_scope.is_empty()); /// ``` #[inline(always)] + #[must_use] pub fn clear(&mut self) -> &mut Self { self.names.clear(); self.values.clear(); @@ -140,6 +142,7 @@ impl<'a> Scope<'a> { /// assert_eq!(my_scope.len(), 1); /// ``` #[inline(always)] + #[must_use] pub fn len(&self) -> usize { self.values.len() } @@ -157,6 +160,7 @@ impl<'a> Scope<'a> { /// assert!(!my_scope.is_empty()); /// ``` #[inline(always)] + #[must_use] pub fn is_empty(&self) -> bool { self.values.len() == 0 } @@ -301,6 +305,7 @@ impl<'a> Scope<'a> { /// assert!(!my_scope.contains("y")); /// ``` #[inline(always)] + #[must_use] pub fn contains(&self, name: &str) -> bool { self.names .iter() @@ -309,6 +314,7 @@ impl<'a> Scope<'a> { } /// Find an entry in the [`Scope`], starting from the last. #[inline(always)] + #[must_use] pub(crate) fn get_index(&self, name: &str) -> Option<(usize, AccessMode)> { self.names .iter() @@ -335,6 +341,7 @@ impl<'a> Scope<'a> { /// assert_eq!(my_scope.get_value::("x").unwrap(), 42); /// ``` #[inline(always)] + #[must_use] pub fn get_value(&self, name: &str) -> Option { self.names .iter() @@ -402,6 +409,7 @@ impl<'a> Scope<'a> { /// /// assert_eq!(my_scope.get_value::("x").unwrap(), 123); /// ``` + #[must_use] pub fn get_mut(&mut self, name: &str) -> Option<&mut Dynamic> { self.get_index(name) .and_then(move |(index, access)| match access { @@ -415,6 +423,7 @@ impl<'a> Scope<'a> { /// /// Panics if the index is out of bounds. #[inline(always)] + #[must_use] pub(crate) fn get_mut_by_index(&mut self, index: usize) -> &mut Dynamic { self.values .get_mut(index) @@ -448,6 +457,7 @@ impl<'a> Scope<'a> { /// Clone the [`Scope`], keeping only the last instances of each variable name. /// Shadowed variables are omitted in the copy. #[inline(always)] + #[must_use] pub(crate) fn clone_visible(&self) -> Self { let mut entries: Self = Default::default(); @@ -467,6 +477,7 @@ 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)> { @@ -503,6 +514,7 @@ 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())) @@ -510,6 +522,7 @@ 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/de.rs b/src/serde/de.rs index d6e93d20..48e7a15f 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -28,14 +28,17 @@ impl<'de> DynamicDeserializer<'de> { /// /// The reference is necessary because the deserialized type may hold references /// (especially `&str`) to the source [`Dynamic`][crate::Dynamic]. + #[must_use] pub fn from_dynamic(value: &'de Dynamic) -> Self { Self { value } } /// Shortcut for a type conversion error. + #[must_use] fn type_error(&self) -> Result> { self.type_error_str(type_name::()) } /// Shortcut for a type conversion error. + #[must_use] fn type_error_str(&self, error: &str) -> Result> { EvalAltResult::ErrorMismatchOutputType( error.into(), @@ -44,6 +47,7 @@ impl<'de> DynamicDeserializer<'de> { ) .into() } + #[must_use] fn deserialize_int>( &mut self, v: crate::INT, @@ -107,6 +111,7 @@ impl<'de> DynamicDeserializer<'de> { /// # Ok(()) /// # } /// ``` +#[must_use] pub fn from_dynamic<'de, T: Deserialize<'de>>( value: &'de Dynamic, ) -> Result> { @@ -125,7 +130,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { type Error = Box; fn deserialize_any>(self, visitor: V) -> Result> { - match &self.value.0 { + match self.value.0 { Union::Unit(_, _, _) => self.deserialize_unit(visitor), Union::Bool(_, _, _) => self.deserialize_bool(visitor), Union::Str(_, _, _) => self.deserialize_str(visitor), @@ -158,16 +163,16 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { #[cfg(not(feature = "no_std"))] Union::TimeStamp(_, _, _) => self.type_error(), - Union::Variant(value, _, _) if value.is::() => self.deserialize_i8(visitor), - Union::Variant(value, _, _) if value.is::() => self.deserialize_i16(visitor), - Union::Variant(value, _, _) if value.is::() => self.deserialize_i32(visitor), - Union::Variant(value, _, _) if value.is::() => self.deserialize_i64(visitor), - Union::Variant(value, _, _) if value.is::() => self.deserialize_i128(visitor), - Union::Variant(value, _, _) if value.is::() => self.deserialize_u8(visitor), - Union::Variant(value, _, _) if value.is::() => self.deserialize_u16(visitor), - Union::Variant(value, _, _) if value.is::() => self.deserialize_u32(visitor), - Union::Variant(value, _, _) if value.is::() => self.deserialize_u64(visitor), - Union::Variant(value, _, _) if value.is::() => self.deserialize_u128(visitor), + Union::Variant(ref value, _, _) if value.is::() => self.deserialize_i8(visitor), + Union::Variant(ref value, _, _) if value.is::() => self.deserialize_i16(visitor), + Union::Variant(ref value, _, _) if value.is::() => self.deserialize_i32(visitor), + Union::Variant(ref value, _, _) if value.is::() => self.deserialize_i64(visitor), + Union::Variant(ref value, _, _) if value.is::() => self.deserialize_i128(visitor), + Union::Variant(ref value, _, _) if value.is::() => self.deserialize_u8(visitor), + Union::Variant(ref value, _, _) if value.is::() => self.deserialize_u16(visitor), + Union::Variant(ref value, _, _) if value.is::() => self.deserialize_u32(visitor), + Union::Variant(ref value, _, _) if value.is::() => self.deserialize_u64(visitor), + Union::Variant(ref value, _, _) if value.is::() => self.deserialize_u128(visitor), Union::Variant(_, _, _) => self.type_error(), @@ -494,6 +499,7 @@ struct IterateArray<'a, ITER: Iterator> { #[cfg(not(feature = "no_index"))] impl<'a, ITER: Iterator> IterateArray<'a, ITER> { + #[must_use] pub fn new(iter: ITER) -> Self { Self { iter } } @@ -534,6 +540,7 @@ where KEYS: Iterator, VALUES: Iterator, { + #[must_use] pub fn new(keys: KEYS, values: VALUES) -> Self { Self { keys, values } } diff --git a/src/serde/metadata.rs b/src/serde/metadata.rs index 79fe947e..985e2dda 100644 --- a/src/serde/metadata.rs +++ b/src/serde/metadata.rs @@ -214,6 +214,7 @@ impl Engine { /// 2) Functions registered into the global namespace /// 3) Functions in static modules /// 4) Functions in global modules (optional) + #[must_use] pub fn gen_fn_metadata_with_ast_to_json( &self, ast: &AST, @@ -253,6 +254,7 @@ impl Engine { /// 1) Functions registered into the global namespace /// 2) Functions in static modules /// 3) Functions in global modules (optional) + #[must_use] pub fn gen_fn_metadata_to_json(&self, include_global: bool) -> serde_json::Result { self.gen_fn_metadata_with_ast_to_json(&Default::default(), include_global) } diff --git a/src/serde/ser.rs b/src/serde/ser.rs index 779a5487..79483c06 100644 --- a/src/serde/ser.rs +++ b/src/serde/ser.rs @@ -25,6 +25,7 @@ struct DynamicSerializer { impl DynamicSerializer { /// Create a [`DynamicSerializer`] from a [`Dynamic`][crate::Dynamic] value. + #[must_use] pub fn new(_value: Dynamic) -> Self { Self { _key: Default::default(), @@ -81,6 +82,7 @@ impl DynamicSerializer { /// # Ok(()) /// # } /// ``` +#[must_use] pub fn to_dynamic(value: T) -> RhaiResult { let mut s = DynamicSerializer::new(Default::default()); value.serialize(&mut s) diff --git a/src/serde/serialize.rs b/src/serde/serialize.rs index be3625ac..ee0f3996 100644 --- a/src/serde/serialize.rs +++ b/src/serde/serialize.rs @@ -11,23 +11,23 @@ use serde::ser::SerializeMap; impl Serialize for Dynamic { fn serialize(&self, ser: S) -> Result { - match &self.0 { + match self.0 { Union::Unit(_, _, _) => ser.serialize_unit(), - Union::Bool(x, _, _) => ser.serialize_bool(*x), - Union::Str(s, _, _) => ser.serialize_str(s.as_str()), + Union::Bool(x, _, _) => ser.serialize_bool(x), + Union::Str(ref s, _, _) => ser.serialize_str(s.as_str()), Union::Char(c, _, _) => ser.serialize_str(&c.to_string()), #[cfg(not(feature = "only_i32"))] - Union::Int(x, _, _) => ser.serialize_i64(*x), + Union::Int(x, _, _) => ser.serialize_i64(x), #[cfg(feature = "only_i32")] - Union::Int(x, _, _) => ser.serialize_i32(*x), + Union::Int(x, _, _) => ser.serialize_i32(x), #[cfg(not(feature = "no_float"))] #[cfg(not(feature = "f32_float"))] - Union::Float(x, _, _) => ser.serialize_f64(**x), + Union::Float(x, _, _) => ser.serialize_f64(*x), #[cfg(not(feature = "no_float"))] #[cfg(feature = "f32_float")] - Union::Float(x, _, _) => ser.serialize_f32(**x), + Union::Float(x, _, _) => ser.serialize_f32(*x), #[cfg(feature = "decimal")] #[cfg(not(feature = "f32_float"))] @@ -53,27 +53,27 @@ impl Serialize for Dynamic { } #[cfg(not(feature = "no_index"))] - Union::Array(a, _, _) => (**a).serialize(ser), + Union::Array(ref a, _, _) => (**a).serialize(ser), #[cfg(not(feature = "no_object"))] - Union::Map(m, _, _) => { + Union::Map(ref m, _, _) => { let mut map = ser.serialize_map(Some(m.len()))?; for (k, v) in m.iter() { map.serialize_entry(k.as_str(), v)?; } map.end() } - Union::FnPtr(f, _, _) => ser.serialize_str(f.fn_name()), + Union::FnPtr(ref f, _, _) => ser.serialize_str(f.fn_name()), #[cfg(not(feature = "no_std"))] - Union::TimeStamp(x, _, _) => ser.serialize_str(x.as_ref().type_name()), + Union::TimeStamp(ref x, _, _) => ser.serialize_str(x.as_ref().type_name()), - Union::Variant(v, _, _) => ser.serialize_str((***v).type_name()), + Union::Variant(ref v, _, _) => ser.serialize_str((***v).type_name()), #[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "sync"))] - Union::Shared(cell, _, _) => cell.borrow().serialize(ser), + Union::Shared(ref cell, _, _) => cell.borrow().serialize(ser), #[cfg(not(feature = "no_closure"))] #[cfg(feature = "sync")] - Union::Shared(cell, _, _) => cell.read().unwrap().serialize(ser), + Union::Shared(ref cell, _, _) => cell.read().unwrap().serialize(ser), } } } diff --git a/src/serde/str.rs b/src/serde/str.rs index 1063ffd4..5a275d90 100644 --- a/src/serde/str.rs +++ b/src/serde/str.rs @@ -13,10 +13,12 @@ pub struct StringSliceDeserializer<'a> { impl<'a> StringSliceDeserializer<'a> { /// Create an `ImmutableStringDeserializer` from an `&str` reference. + #[must_use] pub fn from_str(value: &'a str) -> Self { Self { value } } /// Shortcut for a type conversion error. + #[must_use] fn type_error(&self) -> Result> { EvalAltResult::ErrorMismatchOutputType( type_name::().into(), diff --git a/src/token.rs b/src/token.rs index ad72253c..aaac31c7 100644 --- a/src/token.rs +++ b/src/token.rs @@ -89,6 +89,7 @@ impl Position { /// /// Panics if `line` is zero. #[inline(always)] + #[must_use] pub fn new(line: u16, _position: u16) -> Self { assert!(line != 0, "line cannot be zero"); @@ -101,6 +102,7 @@ impl Position { } /// 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() { None @@ -113,6 +115,7 @@ impl Position { } /// 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() { None @@ -171,6 +174,7 @@ impl Position { } /// Is this [`Position`] at the beginning of a line? #[inline(always)] + #[must_use] pub fn is_beginning_of_line(self) -> bool { #[cfg(not(feature = "no_position"))] return self.pos == 0 && !self.is_none(); @@ -179,6 +183,7 @@ impl Position { } /// Is there no [`Position`]? #[inline(always)] + #[must_use] pub fn is_none(self) -> bool { #[cfg(not(feature = "no_position"))] return self == Self::NONE; @@ -187,6 +192,7 @@ 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() { @@ -465,6 +471,7 @@ impl Token { /// # Panics /// /// Panics if the token is not a keyword. + #[must_use] pub fn keyword_syntax(&self) -> &'static str { use Token::*; @@ -554,6 +561,7 @@ impl Token { } /// Get the syntax of the token. + #[must_use] pub fn syntax(&self) -> Cow<'static, str> { use Token::*; @@ -580,6 +588,7 @@ impl Token { /// Is this token an op-assignment operator? #[inline] + #[must_use] pub fn is_op_assignment(&self) -> bool { match self { Self::PlusAssign @@ -598,6 +607,7 @@ impl Token { } /// Get the corresponding operator of the token if it is an op-assignment operator. + #[must_use] pub fn map_op_assignment(&self) -> Option { Some(match self { Self::PlusAssign => Self::Plus, @@ -617,6 +627,7 @@ impl Token { /// Has this token a corresponding op-assignment operator? #[inline] + #[must_use] pub fn has_op_assignment(&self) -> bool { match self { Self::Plus @@ -635,6 +646,7 @@ impl Token { } /// Get the corresponding op-assignment operator of the token. + #[must_use] pub fn make_op_assignment(&self) -> Option { Some(match self { Self::Plus => Self::PlusAssign, @@ -653,6 +665,7 @@ impl Token { } /// Reverse lookup a token from a piece of syntax. + #[must_use] pub fn lookup_from_syntax(syntax: &str) -> Option { use Token::*; @@ -763,6 +776,7 @@ impl Token { // Is this token [`EOF`][Token::EOF]? #[inline(always)] + #[must_use] pub fn is_eof(&self) -> bool { use Token::*; @@ -774,6 +788,7 @@ impl Token { // If another operator is after these, it's probably an unary operator // (not sure about `fn` name). + #[must_use] pub fn is_next_unary(&self) -> bool { use Token::*; @@ -834,6 +849,7 @@ impl Token { } /// Get the precedence number of the token. + #[must_use] pub fn precedence(&self) -> Option { use Token::*; @@ -868,6 +884,7 @@ impl Token { } /// Does an expression bind to the right (instead of left)? + #[must_use] pub fn is_bind_right(&self) -> bool { use Token::*; @@ -888,6 +905,7 @@ impl Token { } /// Is this token a standard symbol used in the language? + #[must_use] pub fn is_symbol(&self) -> bool { use Token::*; @@ -905,6 +923,7 @@ impl Token { } /// Is this token an active standard keyword? + #[must_use] pub fn is_keyword(&self) -> bool { use Token::*; @@ -924,6 +943,7 @@ impl Token { /// Is this token a reserved symbol? #[inline(always)] + #[must_use] pub fn is_reserved(&self) -> bool { match self { Self::Reserved(_) => true, @@ -933,6 +953,7 @@ impl Token { /// Convert a token into a function name, if possible. #[cfg(not(feature = "no_function"))] + #[must_use] 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), @@ -942,6 +963,7 @@ impl Token { /// Is this token a custom keyword? #[inline(always)] + #[must_use] pub fn is_custom(&self) -> bool { match self { Self::Custom(_) => true, @@ -991,6 +1013,7 @@ pub trait InputStream { /// Get the next character from the `InputStream`. fn get_next(&mut self) -> Option; /// Peek the next character in the `InputStream`. + #[must_use] fn peek_next(&mut self) -> Option; } @@ -1028,6 +1051,7 @@ 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, @@ -1287,6 +1311,7 @@ fn scan_block_comment( /// /// This function is volatile and may change. #[inline(always)] +#[must_use] pub fn get_next_token( stream: &mut impl InputStream, state: &mut TokenizeState, @@ -1326,12 +1351,14 @@ fn is_numeric_digit(c: char) -> bool { #[cfg(not(feature = "no_function"))] #[cfg(feature = "metadata")] #[inline(always)] +#[must_use] pub fn is_doc_comment(comment: &str) -> bool { (comment.starts_with("///") && !comment.starts_with("////")) || (comment.starts_with("/**") && !comment.starts_with("/***")) } /// Get the next token. +#[must_use] fn get_next_token_inner( stream: &mut impl InputStream, state: &mut TokenizeState, @@ -1962,6 +1989,7 @@ fn get_identifier( /// Is this keyword allowed as a function? #[inline(always)] +#[must_use] pub fn is_keyword_function(name: &str) -> bool { match name { KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR @@ -1975,6 +2003,7 @@ pub fn is_keyword_function(name: &str) -> bool { } /// Is a text string a valid identifier? +#[must_use] pub fn is_valid_identifier(name: impl Iterator) -> bool { let mut first_alphabetic = false; @@ -1994,6 +2023,7 @@ pub fn is_valid_identifier(name: impl Iterator) -> bool { /// Is a character valid to start an identifier? #[cfg(feature = "unicode-xid-ident")] #[inline(always)] +#[must_use] pub fn is_id_first_alphabetic(x: char) -> bool { unicode_xid::UnicodeXID::is_xid_start(x) } @@ -2001,6 +2031,7 @@ pub fn is_id_first_alphabetic(x: char) -> bool { /// Is a character valid for an identifier? #[cfg(feature = "unicode-xid-ident")] #[inline(always)] +#[must_use] pub fn is_id_continue(x: char) -> bool { unicode_xid::UnicodeXID::is_xid_continue(x) } @@ -2008,6 +2039,7 @@ pub fn is_id_continue(x: char) -> bool { /// Is a character valid to start an identifier? #[cfg(not(feature = "unicode-xid-ident"))] #[inline(always)] +#[must_use] pub fn is_id_first_alphabetic(x: char) -> bool { x.is_ascii_alphabetic() } @@ -2015,6 +2047,7 @@ pub fn is_id_first_alphabetic(x: char) -> bool { /// Is a character valid for an identifier? #[cfg(not(feature = "unicode-xid-ident"))] #[inline(always)] +#[must_use] pub fn is_id_continue(x: char) -> bool { x.is_ascii_alphanumeric() || x == '_' } @@ -2201,6 +2234,7 @@ impl Engine { /// Exported under the `internals` feature only. #[cfg(feature = "internals")] #[inline(always)] + #[must_use] pub fn lex<'a>( &'a self, input: impl IntoIterator, @@ -2211,6 +2245,7 @@ impl Engine { /// Exported under the `internals` feature only. #[cfg(feature = "internals")] #[inline(always)] + #[must_use] pub fn lex_with_map<'a>( &'a self, input: impl IntoIterator, @@ -2220,6 +2255,7 @@ impl Engine { } /// Tokenize an input text stream with an optional mapping function. #[inline(always)] + #[must_use] pub(crate) fn lex_raw<'a>( &'a self, input: impl IntoIterator, diff --git a/src/unsafe.rs b/src/unsafe.rs index 01e663aa..a40d9819 100644 --- a/src/unsafe.rs +++ b/src/unsafe.rs @@ -9,6 +9,7 @@ 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 @@ -26,6 +27,7 @@ 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::() { @@ -50,6 +52,7 @@ pub fn unsafe_cast_box(item: Box) -> Result, Box> { /// Force-casting a local variable's lifetime to the current [`Scope`][crate::Scope]'s larger lifetime saves /// on allocations and string cloning, thus avoids us having to maintain a chain of [`Scope`][crate::Scope]'s. #[inline(always)] +#[must_use] pub fn unsafe_cast_var_name_to_lifetime<'s>(name: &str) -> &'s str { // WARNING - force-cast the variable name into the scope's lifetime to avoid cloning it // this is safe because all local variables are cleared at the end of the block diff --git a/tests/syntax.rs b/tests/syntax.rs index 8fbeedb5..a0ce0ab8 100644 --- a/tests/syntax.rs +++ b/tests/syntax.rs @@ -24,7 +24,7 @@ fn test_custom_syntax() -> Result<(), Box> { true, |context, inputs| { let var_name = inputs[0].get_variable_name().unwrap().to_string(); - let max = inputs[1].get_literal_value().unwrap().as_int().unwrap(); + let max = inputs[1].get_literal_value::().unwrap(); let stmt = inputs.get(2).unwrap(); let condition = inputs.get(3).unwrap();