From cc3e2d79a5b2c7aef0544eda86d16a04fdba9ade Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 22 May 2021 19:14:24 +0800 Subject: [PATCH] Replace unwrap with expect. --- CHANGELOG.md | 8 +- src/ast.rs | 42 ++++-- src/dynamic.rs | 182 +++++++++++++++++++------ src/engine.rs | 183 ++++++++++++++++--------- src/engine_api.rs | 19 ++- src/fn_call.rs | 62 +++++---- src/fn_native.rs | 4 +- src/fn_register.rs | 18 ++- src/module/mod.rs | 5 +- src/packages/fn_basic.rs | 16 ++- src/packages/math_basic.rs | 8 +- src/packages/string_basic.rs | 6 +- src/parser.rs | 253 +++++++++++++++++++---------------- src/result.rs | 10 +- src/scope.rs | 38 ++++-- src/token.rs | 59 +++++--- tests/optimizer.rs | 8 +- tests/var_scope.rs | 2 +- 18 files changed, 600 insertions(+), 323 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e975ac1..40c5ae31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,16 @@ Rhai Release Notes Version 0.20.2 ============== +This version adds a number of convenience features: + +* Ability for a `Dynamic` to hold an `i32` _tag_ of arbitrary data + +* Simplifies dynamic properties access by falling back to an indexer (passing the name of the property as a string) when a property is not found. + Bug fixes --------- -* Constant propagation during optimization for constants held in a custom scope now works properly instead of always replacing by `()`. +* Propagation of constants held in a custom scope now works properly instead of always replacing by `()`. Breaking changes ---------------- diff --git a/src/ast.rs b/src/ast.rs index 0c8f5792..dce02ec4 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -909,6 +909,7 @@ impl DerefMut for StmtBlock { impl fmt::Debug for StmtBlock { #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Block")?; fmt::Debug::fmt(&self.0, f)?; self.1.debug_print(f) } @@ -1225,6 +1226,7 @@ impl Stmt { path: &mut Vec>, on_node: &mut impl FnMut(&[ASTNode]) -> bool, ) -> bool { + // Push the current node onto the path path.push(self.into()); if !on_node(path) { @@ -1340,7 +1342,8 @@ impl Stmt { _ => (), } - path.pop().unwrap(); + path.pop() + .expect("never fails because `path` always contains the current node"); true } @@ -1398,7 +1401,7 @@ impl OpAssignment { pub fn new(op: Token) -> Self { let op_raw = op .map_op_assignment() - .expect("token must be an op-assignment operator") + .expect("never fails because token must be an op-assignment operator") .keyword_syntax(); let op_assignment = op.keyword_syntax(); @@ -1492,13 +1495,9 @@ impl FnCallHashes { self.script.is_none() } /// Get the script function hash from this [`FnCallHashes`]. - /// - /// # Panics - /// - /// Panics if the [`FnCallHashes`] is native Rust only. #[inline(always)] - pub fn script_hash(&self) -> u64 { - self.script.unwrap() + pub fn script_hash(&self) -> Option { + self.script } /// Get the naive Rust function hash from this [`FnCallHashes`]. #[inline(always)] @@ -1787,7 +1786,7 @@ impl fmt::Debug for Expr { } Self::Property(x) => write!(f, "Property({})", (x.2).0), Self::Stmt(x) => { - f.write_str("Stmt")?; + f.write_str("ExprStmtBlock")?; f.debug_list().entries(x.0.iter()).finish() } Self::FnCall(x, _) => { @@ -1848,7 +1847,10 @@ impl Expr { #[cfg(not(feature = "no_index"))] Self::Array(x, _) if self.is_constant() => { let mut arr = Array::with_capacity(x.len()); - arr.extend(x.iter().map(|v| v.get_constant_value().unwrap())); + arr.extend(x.iter().map(|v| { + v.get_constant_value() + .expect("never fails because a constant array always has a constant value") + })); Dynamic::from_array(arr) } @@ -1856,7 +1858,10 @@ impl Expr { Self::Map(x, _) if self.is_constant() => { let mut map = x.1.clone(); x.0.iter().for_each(|(k, v)| { - *map.get_mut(k.name.as_str()).unwrap() = v.get_constant_value().unwrap() + *map.get_mut(k.name.as_str()) + .expect("never fails because the template should contain all the keys") = v + .get_constant_value() + .expect("never fails because a constant map always has a constant value") }); Dynamic::from_map(map) } @@ -1899,7 +1904,12 @@ impl Expr { | Self::FnCall(_, pos) | Self::Custom(_, pos) => *pos, - Self::InterpolatedString(x) => x.first().unwrap().position(), + Self::InterpolatedString(x) => x + .first() + .expect( + "never fails because an interpolated string always contains at least one item", + ) + .position(), Self::Property(x) => (x.2).1, Self::Stmt(x) => x.1, @@ -1933,7 +1943,9 @@ impl Expr { | Self::Custom(_, pos) => *pos = new_pos, Self::InterpolatedString(x) => { - x.first_mut().unwrap().set_position(new_pos); + x.first_mut() + .expect("never fails because an interpolated string always contains at least one item") + .set_position(new_pos); } Self::Property(x) => (x.2).1 = new_pos, @@ -2050,6 +2062,7 @@ impl Expr { path: &mut Vec>, on_node: &mut impl FnMut(&[ASTNode]) -> bool, ) -> bool { + // Push the current node onto the path path.push(self.into()); if !on_node(path) { @@ -2103,7 +2116,8 @@ impl Expr { _ => (), } - path.pop().unwrap(); + path.pop() + .expect("never fails because `path` always contains the current node"); true } diff --git a/src/dynamic.rs b/src/dynamic.rs index 8cf54178..e8e251e5 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -242,7 +242,9 @@ impl<'d, T: Any + Clone> Deref for DynamicReadLock<'d, T> { DynamicReadLockInner::Reference(reference) => *reference, // Unwrapping is safe because all checking is already done in its constructor #[cfg(not(feature = "no_closure"))] - DynamicReadLockInner::Guard(guard) => guard.downcast_ref().unwrap(), + DynamicReadLockInner::Guard(guard) => guard.downcast_ref().expect( + "never fails because the read guard was created after checking the data type", + ), } } } @@ -284,7 +286,9 @@ impl<'d, T: Any + Clone> Deref for DynamicWriteLock<'d, T> { DynamicWriteLockInner::Reference(reference) => *reference, // Unwrapping is safe because all checking is already done in its constructor #[cfg(not(feature = "no_closure"))] - DynamicWriteLockInner::Guard(guard) => guard.downcast_ref().unwrap(), + DynamicWriteLockInner::Guard(guard) => guard.downcast_ref().expect( + "never fails because the read guard was created after checking the data type", + ), } } } @@ -296,7 +300,9 @@ impl<'d, T: Any + Clone> DerefMut for DynamicWriteLock<'d, T> { DynamicWriteLockInner::Reference(reference) => *reference, // Unwrapping is safe because all checking is already done in its constructor #[cfg(not(feature = "no_closure"))] - DynamicWriteLockInner::Guard(guard) => guard.downcast_mut().unwrap(), + DynamicWriteLockInner::Guard(guard) => guard.downcast_mut().expect( + "never fails because the write guard was created after checking the data type", + ), } } } @@ -573,38 +579,88 @@ impl fmt::Display for Dynamic { Union::Variant(value, _, _) => { let _type_id = (***value).type_id(); + const CHECKED: &str = "never fails because the type was checked"; + #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] if _type_id == TypeId::of::() { - return write!(f, "{}", (**value).as_any().downcast_ref::().unwrap()); + return write!( + f, + "{}", + (**value).as_any().downcast_ref::().expect(CHECKED), + ); } else if _type_id == TypeId::of::() { - return write!(f, "{}", (**value).as_any().downcast_ref::().unwrap()); + return write!( + f, + "{}", + (**value).as_any().downcast_ref::().expect(CHECKED) + ); } else if _type_id == TypeId::of::() { - return write!(f, "{}", (**value).as_any().downcast_ref::().unwrap()); + return write!( + f, + "{}", + (**value).as_any().downcast_ref::().expect(CHECKED) + ); } else if _type_id == TypeId::of::() { - return write!(f, "{}", (**value).as_any().downcast_ref::().unwrap()); + return write!( + f, + "{}", + (**value).as_any().downcast_ref::().expect(CHECKED) + ); } else if _type_id == TypeId::of::() { - return write!(f, "{}", (**value).as_any().downcast_ref::().unwrap()); + return write!( + f, + "{}", + (**value).as_any().downcast_ref::().expect(CHECKED) + ); } else if _type_id == TypeId::of::() { - return write!(f, "{}", (**value).as_any().downcast_ref::().unwrap()); + return write!( + f, + "{}", + (**value).as_any().downcast_ref::().expect(CHECKED) + ); } else if _type_id == TypeId::of::() { - return write!(f, "{}", (**value).as_any().downcast_ref::().unwrap()); + return write!( + f, + "{}", + (**value).as_any().downcast_ref::().expect(CHECKED) + ); } else if _type_id == TypeId::of::() { - return write!(f, "{}", (**value).as_any().downcast_ref::().unwrap()); + return write!( + f, + "{}", + (**value).as_any().downcast_ref::().expect(CHECKED) + ); } #[cfg(not(feature = "no_float"))] if _type_id == TypeId::of::() { - return write!(f, "{}", (**value).as_any().downcast_ref::().unwrap()); + return write!( + f, + "{}", + (**value).as_any().downcast_ref::().expect(CHECKED) + ); } else if _type_id == TypeId::of::() { - return write!(f, "{}", (**value).as_any().downcast_ref::().unwrap()); + return write!( + f, + "{}", + (**value).as_any().downcast_ref::().expect(CHECKED) + ); } #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] if _type_id == TypeId::of::() { - return write!(f, "{}", (**value).as_any().downcast_ref::().unwrap()); + return write!( + f, + "{}", + (**value).as_any().downcast_ref::().expect(CHECKED) + ); } else if _type_id == TypeId::of::() { - return write!(f, "{}", (**value).as_any().downcast_ref::().unwrap()); + return write!( + f, + "{}", + (**value).as_any().downcast_ref::().expect(CHECKED) + ); } f.write_str((***value).type_name()) @@ -652,31 +708,73 @@ impl fmt::Debug for Dynamic { Union::Variant(value, _, _) => { let _type_id = (***value).type_id(); + const CHECKED: &str = "never fails because the type was checked"; + #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] if _type_id == TypeId::of::() { - return write!(f, "{:?}", (**value).as_any().downcast_ref::().unwrap()); + return write!( + f, + "{:?}", + (**value).as_any().downcast_ref::().expect(CHECKED) + ); } else if _type_id == TypeId::of::() { - return write!(f, "{:?}", (**value).as_any().downcast_ref::().unwrap()); + return write!( + f, + "{:?}", + (**value).as_any().downcast_ref::().expect(CHECKED) + ); } else if _type_id == TypeId::of::() { - return write!(f, "{:?}", (**value).as_any().downcast_ref::().unwrap()); + return write!( + f, + "{:?}", + (**value).as_any().downcast_ref::().expect(CHECKED) + ); } else if _type_id == TypeId::of::() { - return write!(f, "{:?}", (**value).as_any().downcast_ref::().unwrap()); + return write!( + f, + "{:?}", + (**value).as_any().downcast_ref::().expect(CHECKED) + ); } else if _type_id == TypeId::of::() { - return write!(f, "{:?}", (**value).as_any().downcast_ref::().unwrap()); + return write!( + f, + "{:?}", + (**value).as_any().downcast_ref::().expect(CHECKED) + ); } else if _type_id == TypeId::of::() { - return write!(f, "{:?}", (**value).as_any().downcast_ref::().unwrap()); + return write!( + f, + "{:?}", + (**value).as_any().downcast_ref::().expect(CHECKED) + ); } else if _type_id == TypeId::of::() { - return write!(f, "{:?}", (**value).as_any().downcast_ref::().unwrap()); + return write!( + f, + "{:?}", + (**value).as_any().downcast_ref::().expect(CHECKED) + ); } else if _type_id == TypeId::of::() { - return write!(f, "{:?}", (**value).as_any().downcast_ref::().unwrap()); + return write!( + f, + "{:?}", + (**value).as_any().downcast_ref::().expect(CHECKED) + ); } #[cfg(not(feature = "no_float"))] if _type_id == TypeId::of::() { - return write!(f, "{}", (**value).as_any().downcast_ref::().unwrap()); + return write!( + f, + "{}", + (**value).as_any().downcast_ref::().expect(CHECKED) + ); } else if _type_id == TypeId::of::() { - return write!(f, "{}", (**value).as_any().downcast_ref::().unwrap()); + return write!( + f, + "{}", + (**value).as_any().downcast_ref::().expect(CHECKED) + ); } #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] @@ -684,13 +782,13 @@ impl fmt::Debug for Dynamic { return write!( f, "{:?}", - (**value).as_any().downcast_ref::().unwrap() + (**value).as_any().downcast_ref::().expect(CHECKED) ); } else if _type_id == TypeId::of::() { return write!( f, "{:?}", - (**value).as_any().downcast_ref::().unwrap() + (**value).as_any().downcast_ref::().expect(CHECKED) ); } @@ -985,51 +1083,53 @@ impl Dynamic { pub fn from(mut value: T) -> Self { // Coded this way in order to maximally leverage potentials for dead-code removal. + const CHECKED: &str = "never fails because the type was checked"; + if TypeId::of::() == TypeId::of::() { - return unsafe_try_cast::<_, Dynamic>(value).ok().unwrap(); + return unsafe_try_cast::<_, Dynamic>(value).ok().expect(CHECKED); } if TypeId::of::() == TypeId::of::() { return ::downcast_ref::(&value) - .unwrap() + .expect(CHECKED) .clone() .into(); } #[cfg(not(feature = "no_float"))] if TypeId::of::() == TypeId::of::() { return ::downcast_ref::(&value) - .unwrap() + .expect(CHECKED) .clone() .into(); } #[cfg(feature = "decimal")] if TypeId::of::() == TypeId::of::() { return ::downcast_ref::(&value) - .unwrap() + .expect(CHECKED) .clone() .into(); } if TypeId::of::() == TypeId::of::() { return ::downcast_ref::(&value) - .unwrap() + .expect(CHECKED) .clone() .into(); } if TypeId::of::() == TypeId::of::() { return ::downcast_ref::(&value) - .unwrap() + .expect(CHECKED) .clone() .into(); } if TypeId::of::() == TypeId::of::() { return ::downcast_ref::(&value) - .unwrap() + .expect(CHECKED) .clone() .into(); } if TypeId::of::() == TypeId::of::<&str>() { return ::downcast_ref::<&str>(&value) - .unwrap() + .expect(CHECKED) .deref() .into(); } @@ -1455,9 +1555,9 @@ impl Dynamic { #[cfg(feature = "sync")] let value = cell.read().unwrap(); - let type_id = (*value).type_id(); - - if type_id != TypeId::of::() && TypeId::of::() != TypeId::of::() { + if (*value).type_id() != TypeId::of::() + && TypeId::of::() != TypeId::of::() + { return None; } else { return Some(DynamicReadLock(DynamicReadLockInner::Guard(value))); @@ -1488,9 +1588,9 @@ impl Dynamic { #[cfg(feature = "sync")] let value = cell.write().unwrap(); - let type_id = (*value).type_id(); - - if type_id != TypeId::of::() && TypeId::of::() != TypeId::of::() { + if (*value).type_id() != TypeId::of::() + && TypeId::of::() != TypeId::of::() + { return None; } else { return Some(DynamicWriteLock(DynamicWriteLockInner::Guard(value))); diff --git a/src/engine.rs b/src/engine.rs index e3e78934..dfff863b 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -429,15 +429,15 @@ impl<'a> Target<'a> { /// This has no effect except for string indexing. #[cfg(not(feature = "no_object"))] #[inline(always)] - pub fn propagate_changed_value(&mut self) { + pub fn propagate_changed_value(&mut self) -> Result<(), Box> { match self { - Self::Ref(_) | Self::Value(_) => (), + Self::Ref(_) | Self::Value(_) => Ok(()), #[cfg(not(feature = "no_closure"))] - Self::LockGuard(_) => (), + Self::LockGuard(_) => Ok(()), #[cfg(not(feature = "no_index"))] Self::StringChar(_, _, ch) => { let char_value = ch.clone(); - self.set_value(char_value, Position::NONE).unwrap(); + self.set_value(char_value, Position::NONE) } } } @@ -455,7 +455,9 @@ impl<'a> Target<'a> { Self::Value(_) => panic!("cannot update a value"), #[cfg(not(feature = "no_index"))] Self::StringChar(s, index, _) => { - let s = &mut *s.write_lock::().unwrap(); + let s = &mut *s + .write_lock::() + .expect("never fails because `StringChar` always holds an `ImmutableString`"); // Replace the character at the specified index position let new_ch = new_val.as_char().map_err(|err| { @@ -488,7 +490,12 @@ impl<'a> From<&'a mut Dynamic> for Target<'a> { if value.is_shared() { // Cloning is cheap for a shared value let container = value.clone(); - return Self::LockGuard((value.write_lock::().unwrap(), container)); + return Self::LockGuard(( + value + .write_lock::() + .expect("never fails when casting to `Dynamic`"), + container, + )); } Self::Ref(value) @@ -598,9 +605,12 @@ impl State { #[inline(always)] 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 self.fn_resolution_caches.0.push(BTreeMap::new()); } - self.fn_resolution_caches.0.last_mut().unwrap() + self.fn_resolution_caches.0.last_mut().expect( + "never fails because there is at least one function resolution cache by this point", + ) } /// Push an empty function resolution cache onto the stack and make it current. #[allow(dead_code)] @@ -617,7 +627,11 @@ impl State { /// Panics if there are no more function resolution cache in the stack. #[inline(always)] pub fn pop_fn_resolution_cache(&mut self) { - let mut cache = self.fn_resolution_caches.0.pop().unwrap(); + let mut cache = self + .fn_resolution_caches + .0 + .pop() + .expect("there should be at least one function resolution cache"); cache.clear(); self.fn_resolution_caches.1.push(cache); } @@ -996,10 +1010,16 @@ impl Engine { if let Some(index) = index { let offset = mods.len() - index.get(); - Some(mods.get(offset).expect("invalid index in Imports")) + Some( + mods.get(offset) + .expect("never fails because offset should be within range"), + ) } else { mods.find(root) - .map(|n| mods.get(n).expect("invalid index in Imports")) + .map(|n| { + mods.get(n) + .expect("never fails because the index came from `find`") + }) .or_else(|| self.global_sub_modules.get(root).cloned()) } } @@ -1051,6 +1071,10 @@ impl Engine { } /// Search for a variable within the scope + /// + /// # Panics + /// + /// Panics if `expr` is not [`Expr::Variable`]. pub(crate) fn search_scope_only<'s>( &self, scope: &'s mut Scope, @@ -1088,9 +1112,13 @@ impl Engine { this_ptr, level: 0, }; - if let Some(mut result) = - resolve_var(expr.get_variable_name(true).unwrap(), index, &context) - .map_err(|err| err.fill_position(var_pos))? + if let Some(mut result) = 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)); @@ -1101,7 +1129,9 @@ impl Engine { scope.len() - index } else { // Find the variable in the scope - let var_name = expr.get_variable_name(true).unwrap(); + let var_name = expr + .get_variable_name(true) + .expect("`expr` should be `Variable`"); scope .get_index(var_name) .ok_or_else(|| EvalAltResult::ErrorVariableNotFound(var_name.to_string(), var_pos))? @@ -1130,18 +1160,22 @@ impl Engine { level: usize, new_val: Option<((Dynamic, Position), (Option, Position))>, ) -> Result<(Dynamic, bool), Box> { + fn match_chain_type(expr: &Expr) -> ChainType { + match expr { + #[cfg(not(feature = "no_index"))] + Expr::Index(_, _) => ChainType::Index, + #[cfg(not(feature = "no_object"))] + Expr::Dot(_, _) => ChainType::Dot, + _ => unreachable!("`expr` should only be `Index` or `Dot`, but got {:?}", expr), + } + } + let is_ref = target.is_ref(); - let rhs_chain = match rhs { - #[cfg(not(feature = "no_index"))] - Expr::Index(_, _) => Some(ChainType::Index), - #[cfg(not(feature = "no_object"))] - Expr::Dot(_, _) => Some(ChainType::Dot), - _ => None, - }; - // Pop the last index value - let idx_val = idx_values.pop().unwrap(); + let idx_val = idx_values + .pop() + .expect("never fails because an index chain is never empty"); match chain_type { #[cfg(not(feature = "no_index"))] @@ -1156,7 +1190,7 @@ impl Engine { let obj_ptr = &mut self.get_indexed_mut( mods, state, lib, target, idx_val, idx_pos, false, true, level, )?; - let rhs_chain = rhs_chain.unwrap(); + let rhs_chain = match_chain_type(rhs); self.eval_dot_index_chain_helper( mods, state, lib, this_ptr, obj_ptr, root, &x.rhs, idx_values, @@ -1166,7 +1200,8 @@ impl Engine { } // xxx[rhs] op= new_val _ if new_val.is_some() => { - let ((mut new_val, new_pos), (op_info, op_pos)) = new_val.unwrap(); + let ((mut new_val, new_pos), (op_info, op_pos)) = + new_val.expect("never fails because `new_val` is `Some`"); let idx_val = idx_val.as_index_value(); #[cfg(not(feature = "no_index"))] @@ -1239,7 +1274,8 @@ impl Engine { let val = self.get_indexed_mut( mods, state, lib, target, index, *pos, true, false, level, )?; - let ((new_val, new_pos), (op_info, op_pos)) = new_val.unwrap(); + let ((new_val, new_pos), (op_info, op_pos)) = + new_val.expect("never fails because `new_val` is `Some`"); self.eval_op_assignment( mods, state, lib, op_info, op_pos, val, root, new_val, new_pos, )?; @@ -1258,7 +1294,8 @@ impl Engine { // xxx.id op= ??? Expr::Property(x) if new_val.is_some() => { let ((getter, hash_get), (setter, hash_set), (name, pos)) = x.as_ref(); - let ((mut new_val, new_pos), (op_info, op_pos)) = new_val.unwrap(); + let ((mut new_val, new_pos), (op_info, op_pos)) = + new_val.expect("never fails because `new_val` is `Some`"); if op_info.is_some() { let hash = FnCallHashes::from_native(*hash_get); @@ -1376,7 +1413,7 @@ impl Engine { // Others - syntax error expr => unreachable!("invalid dot expression: {:?}", expr), }; - let rhs_chain = rhs_chain.unwrap(); + 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, @@ -1391,7 +1428,7 @@ impl Engine { Expr::Property(p) => { let ((getter, hash_get), (setter, hash_set), (name, pos)) = p.as_ref(); - let rhs_chain = rhs_chain.unwrap(); + let rhs_chain = match_chain_type(rhs); let hash_get = FnCallHashes::from_native(*hash_get); let hash_set = FnCallHashes::from_native(*hash_set); let mut arg_values = [target.as_mut(), &mut Default::default()]; @@ -1479,7 +1516,7 @@ impl Engine { // xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr Expr::FnCall(f, pos) if !f.is_qualified() => { let FnCallExpr { name, hashes, .. } = f.as_ref(); - let rhs_chain = rhs_chain.unwrap(); + let rhs_chain = match_chain_type(rhs); let mut args = idx_val.as_fn_call_args(); let (mut val, _) = self.make_method_call( mods, state, lib, name, *hashes, target, &mut args, *pos, level, @@ -1924,7 +1961,10 @@ impl Engine { Expr::Map(x, _) => { let mut map = x.1.clone(); for (Ident { name: key, .. }, expr) in &x.0 { - *map.get_mut(key.as_str()).unwrap() = self + let value_ref = map + .get_mut(key.as_str()) + .expect("never fails because the template should contain all the keys"); + *value_ref = self .eval_expr(scope, mods, state, lib, this_ptr, expr, level)? .flatten(); } @@ -1941,7 +1981,9 @@ impl Engine { literal_args: c_args, .. } = x.as_ref(); - let namespace = namespace.as_ref(); + let namespace = namespace + .as_ref() + .expect("never fails because function call is qualified"); let hash = hashes.native_hash(); self.make_qualified_function_call( scope, mods, state, lib, this_ptr, namespace, name, args, c_args, hash, *pos, @@ -2000,10 +2042,12 @@ impl Engine { .iter() .map(Into::into) .collect::>(); + let key_token = custom.tokens.first().expect( + "never fails because a custom syntax stream must contain at least one token", + ); let custom_def = self - .custom_syntax - .get(custom.tokens.first().unwrap()) - .unwrap(); + .custom_syntax.get(key_token) + .expect("never fails because the custom syntax leading token should match with definition"); let mut context = EvalContext { engine: self, scope, @@ -2134,7 +2178,9 @@ impl Engine { let target_is_shared = false; if target_is_shared { - lock_guard = target.write_lock::().unwrap(); + lock_guard = target + .write_lock::() + .expect("never fails when casting to `Dynamic`"); lhs_ptr_inner = &mut *lock_guard; } else { lhs_ptr_inner = &mut *target; @@ -2204,12 +2250,13 @@ impl Engine { let (lhs_ptr, pos) = self.search_namespace(scope, mods, state, lib, this_ptr, lhs_expr)?; + let var_name = lhs_expr + .get_variable_name(false) + .expect("never fails because `lhs_ptr` is a `Variable`s"); + if !lhs_ptr.is_ref() { - return EvalAltResult::ErrorAssignmentToConstant( - lhs_expr.get_variable_name(false).unwrap().to_string(), - pos, - ) - .into(); + return EvalAltResult::ErrorAssignmentToConstant(var_name.to_string(), pos) + .into(); } #[cfg(not(feature = "unchecked"))] @@ -2222,7 +2269,7 @@ impl Engine { op_info.clone(), *op_pos, lhs_ptr, - (lhs_expr.get_variable_name(false).unwrap(), pos), + (var_name, pos), rhs_val, rhs_expr.position(), )?; @@ -2447,7 +2494,10 @@ impl Engine { let loop_var_is_shared = false; if loop_var_is_shared { - *loop_var.write_lock().unwrap() = value; + let mut value_ref = loop_var + .write_lock() + .expect("never fails when casting to `Dynamic`"); + *value_ref = value; } else { *loop_var = value; } @@ -2497,7 +2547,9 @@ impl Engine { literal_args: c_args, .. } = x.as_ref(); - let namespace = namespace.as_ref(); + let namespace = namespace + .as_ref() + .expect("never fails because function call is qualified"); let hash = hashes.native_hash(); self.make_qualified_function_call( scope, mods, state, lib, this_ptr, namespace, name, args, c_args, hash, *pos, @@ -2558,19 +2610,14 @@ impl Engine { if err_pos.is_none() { // No position info } else { - err_map.insert( - "line".into(), - (err_pos.line().unwrap() as INT).into(), - ); - err_map.insert( - "position".into(), - if err_pos.is_beginning_of_line() { - 0 - } else { - err_pos.position().unwrap() as INT - } - .into(), - ); + let line = err_pos.line().expect("never fails because a non-NONE `Position` always has a line number") as INT; + let position = if err_pos.is_beginning_of_line() { + 0 + } else { + err_pos.position().expect("never fails because a non-NONE `Position` always has a character position") + } as INT; + err_map.insert("line".into(), line.into()); + err_map.insert("position".into(), position.into()); } err.dump_fields(&mut err_map); @@ -2650,7 +2697,10 @@ impl Engine { #[cfg(not(feature = "no_function"))] if entry_type == AccessMode::ReadOnly && lib.iter().any(|&m| !m.is_empty()) { let global = if let Some(index) = mods.find(KEYWORD_GLOBAL) { - match mods.get_mut(index).unwrap() { + match mods + .get_mut(index) + .expect("never fails because the index came from `find`") + { m if m.internal => Some(m), _ => None, } @@ -2659,11 +2709,15 @@ impl Engine { let mut global = Module::new(); global.internal = true; mods.push(KEYWORD_GLOBAL, global); - Some(mods.get_mut(mods.len() - 1).unwrap()) + Some( + mods.get_mut(mods.len() - 1) + .expect("never fails because the global module was just added"), + ) }; if let Some(global) = global { - let global = Shared::get_mut(global).unwrap(); + let global = Shared::get_mut(global) + .expect("never fails because the global module is never shared"); global.set_var(name.clone(), value.clone()); } } @@ -2778,9 +2832,10 @@ impl Engine { /// Check a result to ensure that the data size is within allowable limit. #[cfg(not(feature = "unchecked"))] fn check_data_size(&self, result: &RhaiResult) -> Result<(), Box> { - if result.is_err() { - return Ok(()); - } + let result = match result { + Err(_) => return Ok(()), + Ok(r) => r, + }; // If no data size limits, just return let mut _has_limit = self.limits.max_string_size.is_some(); @@ -2849,7 +2904,7 @@ impl Engine { } } - let (_arr, _map, s) = calc_size(result.as_ref().unwrap()); + let (_arr, _map, s) = calc_size(result); if s > self .limits diff --git a/src/engine_api.rs b/src/engine_api.rs index ca0ad64b..6a847647 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -951,8 +951,14 @@ impl Engine { } } else { let mut iter = name.as_ref().splitn(2, separator.as_ref()); - let sub_module = iter.next().unwrap().trim(); - let remainder = iter.next().unwrap().trim(); + let sub_module = iter + .next() + .expect("never fails because the name contains a separator") + .trim(); + let remainder = iter + .next() + .expect("never fails because the name contains a separator") + .trim(); if !root.contains_key(sub_module) { let mut m: Module = Default::default(); @@ -960,7 +966,9 @@ impl Engine { m.build_index(); root.insert(sub_module.into(), m.into()); } else { - let m = root.remove(sub_module).unwrap(); + let m = root + .remove(sub_module) + .expect("never fails because the root contains the sub-module"); let mut m = crate::fn_native::shared_take_or_clone(m); register_static_module_raw(m.sub_modules_mut(), remainder, module); m.build_index(); @@ -1078,7 +1086,10 @@ impl Engine { resolver: &StaticModuleResolver, imports: &mut BTreeSet, ) { - ast.walk(&mut |path| match path.last().unwrap() { + ast.walk(&mut |path| match path + .last() + .expect("never fails because `path` always contains the current node") + { // Collect all `import` statements with a string constant path ASTNode::Stmt(Stmt::Import(Expr::StringConstant(s, _), _, _)) if !resolver.contains_path(s) && !imports.contains(s.as_str()) => diff --git a/src/fn_call.rs b/src/fn_call.rs index b47676be..996ff927 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -49,7 +49,12 @@ impl<'a> ArgBackup<'a> { /// /// This method blindly casts a reference to another lifetime, which saves allocation and string cloning. /// - /// If `restore_first_arg` is called before the end of the scope, the shorter lifetime will not leak. + /// As long as `restore_first_arg` is called before the end of the scope, the shorter lifetime + /// will not leak. + /// + /// # Panics + /// + /// Panics when `args` is empty. #[inline(always)] fn change_first_arg_to_copy(&mut self, args: &mut FnCallArgs<'a>) { // Clone the original value. @@ -239,7 +244,8 @@ impl Engine { FnResolutionCacheEntry { func, source: None } }) } else { - let (first, second) = args.split_first().unwrap(); + let (first, second) = args.split_first() + .expect("never fails because an op-assignment must have two arguments"); get_builtin_op_assignment_fn(fn_name, *first, second[0]).map( |f| { @@ -257,7 +263,9 @@ impl Engine { // Try all permutations with `Dynamic` wildcards None => { let hash_params = calc_fn_params_hash( - args.as_ref().unwrap().iter().enumerate().map(|(i, a)| { + args.as_ref().expect("never fails because there are no permutations if there are no arguments") + .iter().enumerate().map(|(i, a)| + { let mask = 1usize << (num_args - i - 1); if bitmask & mask != 0 { // Replace with `Dynamic` @@ -646,8 +654,12 @@ impl Engine { crate::engine::KEYWORD_IS_DEF_FN if args.len() == 2 && args[0].is::() && args[1].is::() => { - let fn_name = &*args[0].read_lock::().unwrap(); - let num_params = args[1].as_int().unwrap(); + let fn_name = &*args[0] + .read_lock::() + .expect("never fails because `args[0]` is `FnPtr`"); + let num_params = args[1] + .as_int() + .expect("never fails because `args[1]` is `INT`"); return Ok(( if num_params < 0 { @@ -704,11 +716,7 @@ impl Engine { // Scripted function call? #[cfg(not(feature = "no_function"))] - let hash_script = if hash.is_native_only() { - None - } else { - Some(hash.script_hash()) - }; + let hash_script = hash.script_hash(); #[cfg(not(feature = "no_function"))] if let Some(f) = hash_script.and_then(|hash| { @@ -743,7 +751,9 @@ impl Engine { let result = if _is_method { // Method call of script function - map first argument to `this` - let (first, rest) = args.split_first_mut().unwrap(); + let (first, rest) = args + .split_first_mut() + .expect("never fails because a method call always has a first parameter"); let orig_source = state.source.take(); state.source = source; @@ -906,7 +916,9 @@ impl Engine { let (result, updated) = match fn_name { KEYWORD_FN_PTR_CALL if obj.is::() => { // FnPtr call - let fn_ptr = obj.read_lock::().unwrap(); + let fn_ptr = obj + .read_lock::() + .expect("never fails because `obj` is `FnPtr`"); // Redirect function name let fn_name = fn_ptr.fn_name(); let args_len = call_args.len() + fn_ptr.curry().len(); @@ -970,7 +982,9 @@ impl Engine { )); } - let fn_ptr = obj.read_lock::().unwrap(); + let fn_ptr = obj + .read_lock::() + .expect("never fails because `obj` is `FnPtr`"); // Curry call Ok(( @@ -1041,7 +1055,10 @@ impl Engine { // Propagate the changed value back to the source if necessary if updated { - target.propagate_changed_value(); + target.propagate_changed_value().map_err(|mut err| { + err.set_position(pos); + err + })?; } Ok((result, updated)) @@ -1324,9 +1341,8 @@ impl Engine { } else { // Turn it into a method call only if the object is not shared and not a simple value is_ref = true; - once(target.take_ref().unwrap()) - .chain(arg_values.iter_mut()) - .collect() + let obj_ref = target.take_ref().expect("never fails because `target` is a reference if it is not a value and not shared"); + once(obj_ref).chain(arg_values.iter_mut()).collect() }; } else { // func(..., ...) @@ -1357,7 +1373,7 @@ impl Engine { state: &mut State, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, - namespace: Option<&NamespaceRef>, + namespace: &NamespaceRef, fn_name: &str, args_expr: &[Expr], literal_args: &[(Dynamic, Position)], @@ -1365,7 +1381,6 @@ impl Engine { pos: Position, level: usize, ) -> RhaiResult { - let namespace = namespace.unwrap(); let mut arg_values: StaticVec<_>; let mut first_arg_value = None; let mut args: StaticVec<_>; @@ -1410,11 +1425,12 @@ impl Engine { arg_values[0] = target.take_or_clone().flatten(); args = arg_values.iter_mut().collect(); } else { - let (first, rest) = arg_values.split_first_mut().unwrap(); + let (first, rest) = arg_values + .split_first_mut() + .expect("never fails because the arguments list is not empty"); first_arg_value = Some(first); - args = once(target.take_ref().unwrap()) - .chain(rest.iter_mut()) - .collect(); + let obj_ref = target.take_ref().expect("never fails because `target` is a reference if it is not a value and not shared"); + args = once(obj_ref).chain(rest.iter_mut()).collect(); } } else { // func(..., ...) or func(mod::x, ...) diff --git a/src/fn_native.rs b/src/fn_native.rs index 5d687735..0b22fe9b 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -253,7 +253,9 @@ pub fn shared_try_take(value: Shared) -> Result> { /// Panics if the resource is shared (i.e. has other outstanding references). #[inline(always)] pub fn shared_take(value: Shared) -> T { - shared_try_take(value).map_err(|_| ()).unwrap() + shared_try_take(value) + .ok() + .expect("resource should have no outstanding references") } /// Arguments to a function call, which is a list of [`&mut Dynamic`][Dynamic]. diff --git a/src/fn_register.rs b/src/fn_register.rs index e7a4c6b7..80cfb2cf 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -32,7 +32,8 @@ pub struct Mut(T); #[inline(always)] pub fn by_ref(data: &mut Dynamic) -> DynamicWriteLock { // Directly cast the &mut Dynamic into DynamicWriteLock to access the underlying data. - data.write_lock::().unwrap() + data.write_lock::() + .expect("never fails because the type was checked") } /// Dereference into value. @@ -43,12 +44,15 @@ pub fn by_value(data: &mut Dynamic) -> T { data.flatten_in_place(); let ref_str = data .as_str_ref() - .expect("argument passed by value should not be shared"); + .expect("never fails because argument passed by value should not be shared"); let ref_t = unsafe { mem::transmute::<_, &T>(&ref_str) }; ref_t.clone() } else if TypeId::of::() == TypeId::of::() { // If T is `String`, data must be `ImmutableString`, so map directly to it - unsafe_try_cast(mem::take(data).take_string().unwrap()).unwrap() + let value = mem::take(data) + .take_string() + .expect("never fails because the type was checked"); + unsafe_try_cast(value).expect("never fails because the type was checked") } else { // We consume the argument and then replace it with () - the argument is not supposed to be used again. // This way, we avoid having to clone the argument again, because it is already a clone when passed here. @@ -118,7 +122,7 @@ macro_rules! def_register { // The arguments are assumed to be of the correct number and types! let mut _drain = args.iter_mut(); - $($let $par = ($clone)(_drain.next().unwrap()); )* + $($let $par = ($clone)(_drain.next().expect("never fails because arguments list is fixed")); )* // Call the function with each argument value let r = self($($arg),*); @@ -146,7 +150,7 @@ macro_rules! def_register { // The arguments are assumed to be of the correct number and types! let mut _drain = args.iter_mut(); - $($let $par = ($clone)(_drain.next().unwrap()); )* + $($let $par = ($clone)(_drain.next().expect("never fails because arguments list is fixed")); )* // Call the function with each argument value let r = self(ctx, $($arg),*); @@ -174,7 +178,7 @@ macro_rules! def_register { // The arguments are assumed to be of the correct number and types! let mut _drain = args.iter_mut(); - $($let $par = ($clone)(_drain.next().unwrap()); )* + $($let $par = ($clone)(_drain.next().expect("never fails because arguments list is fixed")); )* // Call the function with each argument value self($($arg),*).map(Dynamic::from) @@ -199,7 +203,7 @@ macro_rules! def_register { // The arguments are assumed to be of the correct number and types! let mut _drain = args.iter_mut(); - $($let $par = ($clone)(_drain.next().unwrap()); )* + $($let $par = ($clone)(_drain.next().expect("never fails because arguments list is fixed")); )* // Call the function with each argument value self(ctx, $($arg),*).map(Dynamic::from) diff --git a/src/module/mod.rs b/src/module/mod.rs index b9320870..d21d8c46 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -1415,7 +1415,10 @@ impl Module { match aliases.len() { 0 => (), 1 => { - module.set_var(aliases.pop().unwrap(), value); + let alias = aliases + .pop() + .expect("never fails because the list has one item"); + module.set_var(alias, value); } _ => aliases.into_iter().for_each(|alias| { module.set_var(alias, value.clone()); diff --git a/src/packages/fn_basic.rs b/src/packages/fn_basic.rs index 63688c04..f88089c5 100644 --- a/src/packages/fn_basic.rs +++ b/src/packages/fn_basic.rs @@ -45,29 +45,31 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array { namespace: Option, f: &ScriptFnDef, ) -> Map { + const DICT: &str = "never fails because the dictionary is pre-filled with all the keys"; + let mut map = Map::new(); if let Some(ns) = namespace { - map.insert(dict.get("namespace").unwrap().clone().into(), ns.into()); + map.insert(dict.get("namespace").expect(DICT).clone().into(), ns.into()); } map.insert( - dict.get("name").unwrap().clone().into(), + dict.get("name").expect(DICT).clone().into(), f.name.clone().into(), ); map.insert( - dict.get("access").unwrap().clone().into(), + dict.get("access").expect(DICT).clone().into(), match f.access { - FnAccess::Public => dict.get("public").unwrap().clone(), - FnAccess::Private => dict.get("private").unwrap().clone(), + FnAccess::Public => dict.get("public").expect(DICT).clone(), + FnAccess::Private => dict.get("private").expect(DICT).clone(), } .into(), ); map.insert( - dict.get("is_anonymous").unwrap().clone().into(), + dict.get("is_anonymous").expect(DICT).clone().into(), f.name.starts_with(crate::engine::FN_ANONYMOUS).into(), ); map.insert( - dict.get("params").unwrap().clone().into(), + dict.get("params").expect(DICT).clone().into(), f.params .iter() .cloned() diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index e4d8b0b4..cd2c6091 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -311,12 +311,8 @@ mod decimal_functions { #[rhai_fn(return_raw)] pub fn sqrt(x: Decimal) -> Result> { - if cfg!(not(feature = "unchecked")) { - x.sqrt() - .ok_or_else(|| make_err(format!("Error taking the square root of {}", x,))) - } else { - Ok(x.sqrt().unwrap()) - } + x.sqrt() + .ok_or_else(|| make_err(format!("Error taking the square root of {}", x,))) } #[rhai_fn(return_raw)] pub fn exp(x: Decimal) -> Result> { diff --git a/src/packages/string_basic.rs b/src/packages/string_basic.rs index ea02a027..be07f1e0 100644 --- a/src/packages/string_basic.rs +++ b/src/packages/string_basic.rs @@ -27,9 +27,9 @@ pub fn print_with_func( value: &mut Dynamic, ) -> crate::ImmutableString { match ctx.call_fn_dynamic_raw(fn_name, true, &mut [value]) { - Ok(result) if result.is::() => { - result.take_immutable_string().unwrap() - } + Ok(result) if result.is::() => result + .take_immutable_string() + .expect("never fails as the result is `ImmutableString`"), Ok(result) => ctx.engine().map_type_name(result.type_name()).into(), Err(_) => ctx.engine().map_type_name(value.type_name()).into(), } diff --git a/src/parser.rs b/src/parser.rs index 3eec52e6..61a4b6ad 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -36,6 +36,8 @@ type PERR = ParseErrorType; type FunctionsLib = BTreeMap>; +const NEVER_ENDS: &str = "never fails because `TokenStream` never ends"; + /// A type that encapsulates the current state of the parser. #[derive(Debug)] pub struct ParseState<'e> { @@ -241,7 +243,7 @@ impl Expr { /// Consume a particular [token][Token], checking that it is the expected one. fn eat_token(input: &mut TokenStream, token: Token) -> Position { - let (t, pos) = input.next().unwrap(); + let (t, pos) = input.next().expect(NEVER_ENDS); if t != token { unreachable!( @@ -256,7 +258,7 @@ fn eat_token(input: &mut TokenStream, token: Token) -> Position { /// Match a particular [token][Token], consuming it if matched. fn match_token(input: &mut TokenStream, token: Token) -> (bool, Position) { - let (t, pos) = input.peek().unwrap(); + let (t, pos) = input.peek().expect(NEVER_ENDS); if *t == token { (true, eat_token(input, token)) } else { @@ -283,7 +285,7 @@ fn parse_paren_expr( let expr = parse_expr(input, state, lib, settings.level_up())?; - match input.next().unwrap() { + match input.next().expect(NEVER_ENDS) { // ( xxx ) (Token::RightParen, _) => Ok(expr), // ( @@ -310,7 +312,7 @@ fn parse_fn_call( #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; - let (token, token_pos) = input.peek().unwrap(); + let (token, token_pos) = input.peek().expect(NEVER_ENDS); let mut args = StaticVec::new(); @@ -365,13 +367,13 @@ fn parse_fn_call( let settings = settings.level_up(); loop { - match input.peek().unwrap() { + match input.peek().expect(NEVER_ENDS) { // id(...args, ) - handle trailing comma (Token::RightParen, _) => (), _ => args.push(parse_expr(input, state, lib, settings)?), } - match input.peek().unwrap() { + match input.peek().expect(NEVER_ENDS) { // id(...args) (Token::RightParen, _) => { eat_token(input, Token::RightParen); @@ -554,12 +556,12 @@ fn parse_index_chain( } // Check if there is a closing bracket - match input.peek().unwrap() { + match input.peek().expect(NEVER_ENDS) { (Token::RightBracket, _) => { eat_token(input, Token::RightBracket); // Any more indexing following? - match input.peek().unwrap() { + match input.peek().expect(NEVER_ENDS) { // If another indexing level, right-bind it (Token::LeftBracket, _) => { let prev_pos = settings.pos; @@ -614,10 +616,10 @@ fn parse_array_literal( "Size of array literal".to_string(), state.engine.max_array_size(), ) - .into_err(input.peek().unwrap().1)); + .into_err(input.peek().expect(NEVER_ENDS).1)); } - match input.peek().unwrap() { + match input.peek().expect(NEVER_ENDS) { (Token::RightBracket, _) => { eat_token(input, Token::RightBracket); break; @@ -634,7 +636,7 @@ fn parse_array_literal( } } - match input.peek().unwrap() { + match input.peek().expect(NEVER_ENDS) { (Token::Comma, _) => { eat_token(input, Token::Comma); } @@ -681,7 +683,7 @@ fn parse_map_literal( loop { const MISSING_RBRACE: &str = "to end this object map literal"; - match input.peek().unwrap() { + match input.peek().expect(NEVER_ENDS) { (Token::RightBrace, _) => { eat_token(input, Token::RightBrace); break; @@ -695,7 +697,7 @@ fn parse_map_literal( _ => (), } - let (name, pos) = match input.next().unwrap() { + let (name, pos) = match input.next().expect(NEVER_ENDS) { (Token::Identifier(s), pos) | (Token::StringConstant(s), pos) => { if map.iter().any(|(p, _)| p.name == s) { return Err(PERR::DuplicatedProperty(s).into_err(pos)); @@ -722,7 +724,7 @@ fn parse_map_literal( (_, pos) => return Err(PERR::PropertyExpected.into_err(pos)), }; - match input.next().unwrap() { + match input.next().expect(NEVER_ENDS) { (Token::Colon, _) => (), (Token::LexError(err), pos) => return Err(err.into_err(pos)), (_, pos) => { @@ -743,7 +745,7 @@ fn parse_map_literal( "Number of properties in object map literal".to_string(), state.engine.max_map_size(), ) - .into_err(input.peek().unwrap().1)); + .into_err(input.peek().expect(NEVER_ENDS).1)); } let expr = parse_expr(input, state, lib, settings.level_up())?; @@ -751,7 +753,7 @@ fn parse_map_literal( template.insert(name.clone().into(), Default::default()); map.push((Ident { name, pos }, expr)); - match input.peek().unwrap() { + match input.peek().expect(NEVER_ENDS) { (Token::Comma, _) => { eat_token(input, Token::Comma); } @@ -793,7 +795,7 @@ fn parse_switch( let item = parse_expr(input, state, lib, settings.level_up())?; - match input.next().unwrap() { + match input.next().expect(NEVER_ENDS) { (Token::LeftBrace, _) => (), (Token::LexError(err), pos) => return Err(err.into_err(pos)), (_, pos) => { @@ -812,7 +814,7 @@ fn parse_switch( loop { const MISSING_RBRACE: &str = "to end this switch block"; - let (expr, condition) = match input.peek().unwrap() { + let (expr, condition) = match input.peek().expect(NEVER_ENDS) { (Token::RightBrace, _) => { eat_token(input, Token::RightBrace); break; @@ -868,7 +870,7 @@ fn parse_switch( None }; - match input.next().unwrap() { + match input.next().expect(NEVER_ENDS) { (Token::DoubleArrow, _) => (), (Token::LexError(err), pos) => return Err(err.into_err(pos)), (_, pos) => { @@ -891,7 +893,7 @@ fn parse_switch( Some(stmt.into()) }; - match input.peek().unwrap() { + match input.peek().expect(NEVER_ENDS) { (Token::Comma, _) => { eat_token(input, Token::Comma); } @@ -934,7 +936,7 @@ fn parse_primary( #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; - let (token, token_pos) = input.peek().unwrap(); + let (token, token_pos) = input.peek().expect(NEVER_ENDS); settings.pos = *token_pos; let mut root_expr = match token { @@ -944,7 +946,7 @@ fn parse_primary( | Token::CharConstant(_) | Token::StringConstant(_) | Token::True - | Token::False => match input.next().unwrap().0 { + | Token::False => match input.next().expect(NEVER_ENDS).0 { Token::IntegerConstant(x) => Expr::IntegerConstant(x, settings.pos), Token::CharConstant(c) => Expr::CharConstant(c, settings.pos), Token::StringConstant(s) => { @@ -957,13 +959,13 @@ fn parse_primary( #[cfg(not(feature = "no_float"))] Token::FloatConstant(x) => { let x = (*x).into(); - input.next().unwrap(); + input.next().expect(NEVER_ENDS); Expr::FloatConstant(x, settings.pos) } #[cfg(feature = "decimal")] Token::DecimalConstant(x) => { let x = (*x).into(); - input.next().unwrap(); + input.next().expect(NEVER_ENDS); Expr::DynamicConstant(Box::new(x), settings.pos) } @@ -1025,7 +1027,7 @@ fn parse_primary( Token::InterpolatedString(_) => { let mut segments: StaticVec = Default::default(); - if let (Token::InterpolatedString(s), pos) = input.next().unwrap() { + if let (Token::InterpolatedString(s), pos) = input.next().expect(NEVER_ENDS) { segments.push(Expr::StringConstant(s.into(), pos)); } else { unreachable!(); @@ -1043,7 +1045,7 @@ fn parse_primary( control.is_within_text = true; state.tokenizer_control.set(control); - match input.next().unwrap() { + match input.next().expect(NEVER_ENDS) { (Token::StringConstant(s), pos) => { if !s.is_empty() { segments.push(Expr::StringConstant(s.into(), pos)); @@ -1080,12 +1082,12 @@ fn parse_primary( // Identifier Token::Identifier(_) => { - let s = match input.next().unwrap() { + let s = match input.next().expect(NEVER_ENDS) { (Token::Identifier(s), _) => s, _ => unreachable!(), }; - match input.peek().unwrap().0 { + match input.peek().expect(NEVER_ENDS).0 { // Function call Token::LeftParen | Token::Bang => { #[cfg(not(feature = "no_closure"))] @@ -1134,12 +1136,12 @@ fn parse_primary( // Reserved keyword or symbol Token::Reserved(_) => { - let s = match input.next().unwrap() { + let s = match input.next().expect(NEVER_ENDS) { (Token::Reserved(s), _) => s, _ => unreachable!(), }; - match input.peek().unwrap().0 { + match input.peek().expect(NEVER_ENDS).0 { // Function call is allowed to have reserved keyword Token::LeftParen | Token::Bang if is_keyword_function(&s) => Expr::Variable( None, @@ -1164,7 +1166,7 @@ fn parse_primary( } } - Token::LexError(_) => match input.next().unwrap() { + Token::LexError(_) => match input.next().expect(NEVER_ENDS) { (Token::LexError(err), _) => return Err(err.into_err(settings.pos)), _ => unreachable!(), }, @@ -1176,13 +1178,13 @@ fn parse_primary( // Tail processing all possible postfix operators loop { - let (tail_token, _) = input.peek().unwrap(); + let (tail_token, _) = input.peek().expect(NEVER_ENDS); if !root_expr.is_valid_postfix(tail_token) { break; } - let (tail_token, tail_pos) = input.next().unwrap(); + let (tail_token, tail_pos) = input.next().expect(NEVER_ENDS); settings.pos = tail_pos; root_expr = match (root_expr, tail_token) { @@ -1222,33 +1224,35 @@ fn parse_primary( parse_fn_call(input, state, lib, name, false, ns, settings.level_up())? } // module access - (Expr::Variable(_, var_pos, x), Token::DoubleColon) => match input.next().unwrap() { - (Token::Identifier(id2), pos2) => { - let (_, mut namespace, var_name) = *x; - let var_name_def = Ident { - name: var_name, - pos: var_pos, - }; + (Expr::Variable(_, var_pos, x), Token::DoubleColon) => { + match input.next().expect(NEVER_ENDS) { + (Token::Identifier(id2), pos2) => { + let (_, mut namespace, var_name) = *x; + let var_name_def = Ident { + name: var_name, + pos: var_pos, + }; - 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)); + 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)); + } + + Expr::Variable( + None, + pos2, + Box::new((None, namespace, state.get_identifier(id2))), + ) } - - Expr::Variable( - None, - pos2, - Box::new((None, namespace, state.get_identifier(id2))), - ) + (Token::Reserved(id2), pos2) if is_valid_identifier(id2.chars()) => { + return Err(PERR::Reserved(id2).into_err(pos2)); + } + (_, pos2) => return Err(PERR::VariableExpected.into_err(pos2)), } - (Token::Reserved(id2), pos2) if is_valid_identifier(id2.chars()) => { - return Err(PERR::Reserved(id2).into_err(pos2)); - } - (_, pos2) => return Err(PERR::VariableExpected.into_err(pos2)), - }, + } // Indexing #[cfg(not(feature = "no_index"))] (expr, Token::LeftBracket) => { @@ -1258,7 +1262,7 @@ fn parse_primary( #[cfg(not(feature = "no_object"))] (expr, Token::Period) => { // Expression after dot must start with an identifier - match input.peek().unwrap() { + match input.peek().expect(NEVER_ENDS) { (Token::Identifier(_), _) => { #[cfg(not(feature = "no_closure"))] { @@ -1313,7 +1317,7 @@ fn parse_unary( lib: &mut FunctionsLib, mut settings: ParseSettings, ) -> Result { - let (token, token_pos) = input.peek().unwrap(); + let (token, token_pos) = input.peek().expect(NEVER_ENDS); settings.pos = *token_pos; #[cfg(not(feature = "unchecked"))] @@ -1449,7 +1453,16 @@ fn make_assignment_stmt<'a>( // var (indexed) = rhs Expr::Variable(i, var_pos, x) => { let (index, _, name) = x.as_ref(); - let index = i.map_or_else(|| index.unwrap().get(), |n| n.get() as usize); + let index = i.map_or_else( + || { + index + .expect( + "never fails because the long index is `Some` when the short index is `None`", + ) + .get() + }, + |n| n.get() as usize, + ); match state.stack[state.stack.len() - index].1 { AccessMode::ReadWrite => { Ok(Stmt::Assignment(Box::new((lhs, op_info, rhs)), op_pos)) @@ -1498,12 +1511,15 @@ fn parse_op_assignment_stmt( #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; - let (token, token_pos) = input.peek().unwrap(); + let (token, token_pos) = input.peek().expect(NEVER_ENDS); settings.pos = *token_pos; let (op, pos) = match token { - Token::Equals => (None, input.next().unwrap().1), - _ if token.is_op_assignment() => input.next().map(|(op, pos)| (Some(op), pos)).unwrap(), + Token::Equals => (None, input.next().expect(NEVER_ENDS).1), + _ if token.is_op_assignment() => input + .next() + .map(|(op, pos)| (Some(op), pos)) + .expect(NEVER_ENDS), _ => return Ok(Stmt::Expr(lhs)), }; @@ -1544,7 +1560,8 @@ 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.unwrap().1[0].pos)) + return Err(PERR::PropertyExpected + .into_err(x.1.expect("never fails because the namespace is `Some`").1[0].pos)) } // lhs.prop (lhs, prop @ Expr::Property(_)) => { @@ -1649,7 +1666,7 @@ fn parse_binary_op( let mut root = lhs; loop { - let (current_op, current_pos) = input.peek().unwrap(); + let (current_op, current_pos) = input.peek().expect(NEVER_ENDS); let precedence = match current_op { Token::Custom(c) => state .engine @@ -1670,11 +1687,11 @@ fn parse_binary_op( return Ok(root); } - let (op_token, pos) = input.next().unwrap(); + let (op_token, pos) = input.next().expect(NEVER_ENDS); let rhs = parse_unary(input, state, lib, settings)?; - let (next_op, next_pos) = input.peek().unwrap(); + let (next_op, next_pos) = input.peek().expect(NEVER_ENDS); let next_precedence = match next_op { Token::Custom(c) => state .engine @@ -1744,8 +1761,12 @@ fn parse_binary_op( } Token::Or => { - let rhs = args.pop().unwrap(); - let current_lhs = args.pop().unwrap(); + let rhs = args + .pop() + .expect("never fails because `||` has two arguments"); + let current_lhs = args + .pop() + .expect("never fails because `||` has two arguments"); Expr::Or( Box::new(BinaryExpr { lhs: current_lhs, @@ -1755,8 +1776,12 @@ fn parse_binary_op( ) } Token::And => { - let rhs = args.pop().unwrap(); - let current_lhs = args.pop().unwrap(); + let rhs = args + .pop() + .expect("never fails because `&&` has two arguments"); + let current_lhs = args + .pop() + .expect("never fails because `&&` has two arguments"); Expr::And( Box::new(BinaryExpr { lhs: current_lhs, @@ -1841,7 +1866,7 @@ fn parse_custom_syntax( tokens.push(key.into()); loop { - let (fwd_token, fwd_pos) = input.peek().unwrap(); + let (fwd_token, fwd_pos) = input.peek().expect(NEVER_ENDS); settings.pos = *fwd_pos; let settings = settings.level_up(); @@ -1854,7 +1879,7 @@ fn parse_custom_syntax( }; match required_token.as_str() { - MARKER_IDENT => match input.next().unwrap() { + MARKER_IDENT => match input.next().expect(NEVER_ENDS) { (Token::Identifier(s), pos) => { let name = state.get_identifier(s); segments.push(name.clone().into()); @@ -1881,7 +1906,7 @@ fn parse_custom_syntax( } stmt => unreachable!("expecting Stmt::Block, but gets {:?}", stmt), }, - s => match input.next().unwrap() { + s => match input.next().expect(NEVER_ENDS) { (Token::LexError(err), pos) => return Err(err.into_err(pos)), (t, _) if t.syntax().as_ref() == s => { segments.push(required_token.clone()); @@ -1921,18 +1946,18 @@ fn parse_expr( #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; - settings.pos = input.peek().unwrap().1; + settings.pos = input.peek().expect(NEVER_ENDS).1; // Check if it is a custom syntax. if !state.engine.custom_syntax.is_empty() { - let (token, pos) = input.peek().unwrap(); + let (token, pos) = input.peek().expect(NEVER_ENDS); let token_pos = *pos; match token { Token::Custom(key) | Token::Reserved(key) | Token::Identifier(key) => { match state.engine.custom_syntax.get_key_value(key.as_str()) { Some((key, syntax)) => { - input.next().unwrap(); + input.next().expect(NEVER_ENDS); return parse_custom_syntax( input, state, lib, settings, key, syntax, token_pos, ); @@ -1958,7 +1983,7 @@ fn parse_expr( /// Make sure that the expression is not a statement expression (i.e. wrapped in `{}`). fn ensure_not_statement_expr(input: &mut TokenStream, type_name: &str) -> Result<(), ParseError> { - match input.peek().unwrap() { + match input.peek().expect(NEVER_ENDS) { // Disallow statement expressions (Token::LeftBrace, pos) | (Token::EOF, pos) => { Err(PERR::ExprExpected(type_name.to_string()).into_err(*pos)) @@ -1970,7 +1995,7 @@ fn ensure_not_statement_expr(input: &mut TokenStream, type_name: &str) -> Result /// Make sure that the expression is not a mis-typed assignment (i.e. `a = b` instead of `a == b`). fn ensure_not_assignment(input: &mut TokenStream) -> Result<(), ParseError> { - match input.peek().unwrap() { + match input.peek().expect(NEVER_ENDS) { (Token::Equals, pos) => Err(LexError::ImproperSymbol( "=".to_string(), "Possibly a typo of '=='?".to_string(), @@ -2017,7 +2042,7 @@ fn parse_if( // if guard { if_body } else ... let else_body = if match_token(input, Token::Else).0 { - if let (Token::If, _) = input.peek().unwrap() { + if let (Token::If, _) = input.peek().expect(NEVER_ENDS) { // if guard { if_body } else if ... parse_if(input, state, lib, settings.level_up())? } else { @@ -2046,7 +2071,7 @@ fn parse_while_loop( settings.ensure_level_within_max_limit(state.max_expr_depth)?; // while|loops ... - let (guard, token_pos) = match input.next().unwrap() { + let (guard, token_pos) = match input.next().expect(NEVER_ENDS) { (Token::While, pos) => { ensure_not_statement_expr(input, "a boolean")?; let expr = parse_expr(input, state, lib, settings.level_up())?; @@ -2081,7 +2106,7 @@ fn parse_do( settings.is_breakable = true; let body = parse_block(input, state, lib, settings.level_up())?; - let is_while = match input.next().unwrap() { + let is_while = match input.next().expect(NEVER_ENDS) { (Token::While, _) => true, (Token::Until, _) => false, (_, pos) => { @@ -2119,7 +2144,7 @@ fn parse_for( settings.pos = eat_token(input, Token::For); // for name ... - let (name, name_pos) = match input.next().unwrap() { + let (name, name_pos) = match input.next().expect(NEVER_ENDS) { // Variable name (Token::Identifier(s), pos) => (s, pos), // Reserved keyword @@ -2133,7 +2158,7 @@ fn parse_for( }; // for name in ... - match input.next().unwrap() { + match input.next().expect(NEVER_ENDS) { (Token::In, _) => (), (Token::LexError(err), pos) => return Err(err.into_err(pos)), (_, pos) => { @@ -2183,10 +2208,10 @@ fn parse_let( settings.ensure_level_within_max_limit(state.max_expr_depth)?; // let/const... (specified in `var_type`) - settings.pos = input.next().unwrap().1; + settings.pos = input.next().expect(NEVER_ENDS).1; // let name ... - let (name, pos) = match input.next().unwrap() { + let (name, pos) = match input.next().expect(NEVER_ENDS) { (Token::Identifier(s), pos) => (s, pos), (Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => { return Err(PERR::Reserved(s).into_err(pos)); @@ -2242,7 +2267,7 @@ fn parse_import( } // import expr as name ... - let (name, name_pos) = match input.next().unwrap() { + let (name, name_pos) = match input.next().expect(NEVER_ENDS) { (Token::Identifier(s), pos) => (s, pos), (Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => { return Err(PERR::Reserved(s).into_err(pos)); @@ -2280,7 +2305,7 @@ fn parse_export( settings.pos = eat_token(input, Token::Export); - match input.peek().unwrap() { + match input.peek().expect(NEVER_ENDS) { (Token::Let, pos) => { let pos = *pos; let mut stmt = parse_let(input, state, lib, AccessMode::ReadWrite, true, settings)?; @@ -2299,7 +2324,7 @@ fn parse_export( let mut exports = Vec::with_capacity(4); loop { - let (id, id_pos) = match input.next().unwrap() { + let (id, id_pos) = match input.next().expect(NEVER_ENDS) { (Token::Identifier(s), pos) => (s.clone(), pos), (Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => { return Err(PERR::Reserved(s).into_err(pos)); @@ -2309,7 +2334,7 @@ fn parse_export( }; let rename = if match_token(input, Token::As).0 { - match input.next().unwrap() { + match input.next().expect(NEVER_ENDS) { (Token::Identifier(s), pos) => Some(Ident { name: state.get_identifier(s), pos, @@ -2332,7 +2357,7 @@ fn parse_export( rename, )); - match input.peek().unwrap() { + match input.peek().expect(NEVER_ENDS) { (Token::Comma, _) => { eat_token(input, Token::Comma); } @@ -2358,7 +2383,7 @@ fn parse_block( mut settings: ParseSettings, ) -> Result { // Must start with { - settings.pos = match input.next().unwrap() { + settings.pos = match input.next().expect(NEVER_ENDS) { (Token::LeftBrace, pos) => pos, (Token::LexError(err), pos) => return Err(err.into_err(pos)), (_, pos) => { @@ -2383,7 +2408,7 @@ fn parse_block( loop { // Terminated? - match input.peek().unwrap() { + match input.peek().expect(NEVER_ENDS) { (Token::RightBrace, _) => { eat_token(input, Token::RightBrace); break; @@ -2412,7 +2437,7 @@ fn parse_block( statements.push(stmt); - match input.peek().unwrap() { + match input.peek().expect(NEVER_ENDS) { // { ... stmt } (Token::RightBrace, _) => { eat_token(input, Token::RightBrace); @@ -2461,7 +2486,7 @@ fn parse_expr_stmt( #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; - settings.pos = input.peek().unwrap().1; + settings.pos = input.peek().expect(NEVER_ENDS).1; let expr = parse_expr(input, state, lib, settings.level_up())?; let stmt = parse_op_assignment_stmt(input, state, lib, expr, settings.level_up())?; @@ -2484,7 +2509,7 @@ fn parse_stmt( let mut comments_pos = Position::NONE; // Handle doc-comments. - while let (Token::Comment(ref comment), pos) = input.peek().unwrap() { + while let (Token::Comment(ref comment), pos) = input.peek().expect(INPUT_NEVER_ENDS) { if comments_pos.is_none() { comments_pos = *pos; } @@ -2497,11 +2522,11 @@ fn parse_stmt( return Err(PERR::WrongDocComment.into_err(comments_pos)); } - match input.next().unwrap().0 { + match input.next().expect(NEVER_ENDS).0 { Token::Comment(comment) => { comments.push(comment); - match input.peek().unwrap() { + match input.peek().expect(INPUT_NEVER_ENDS) { (Token::Fn, _) | (Token::Private, _) => break, (Token::Comment(_), _) => (), _ => return Err(PERR::WrongDocComment.into_err(comments_pos)), @@ -2514,7 +2539,7 @@ fn parse_stmt( comments }; - let (token, token_pos) = match input.peek().unwrap() { + let (token, token_pos) = match input.peek().expect(NEVER_ENDS) { (Token::EOF, pos) => return Ok(Stmt::Noop(*pos)), x => x, }; @@ -2546,7 +2571,7 @@ fn parse_stmt( FnAccess::Public }; - match input.next().unwrap() { + match input.next().expect(NEVER_ENDS) { (Token::Fn, pos) => { let mut new_state = ParseState::new(state.engine, state.tokenizer_control.clone()); @@ -2631,9 +2656,9 @@ fn parse_stmt( pos, ) }) - .unwrap(); + .expect(NEVER_ENDS); - match input.peek().unwrap() { + match input.peek().expect(NEVER_ENDS) { // `return`/`throw` at (Token::EOF, _) => Ok(Stmt::Return(return_type, None, token_pos)), // `return;` or `throw;` @@ -2692,7 +2717,7 @@ fn parse_try_catch( // try { body } catch ( let var_def = if match_token(input, Token::LeftParen).0 { - let id = match input.next().unwrap() { + let id = match input.next().expect(NEVER_ENDS) { (Token::Identifier(s), pos) => Ident { name: state.get_identifier(s), pos, @@ -2740,7 +2765,7 @@ fn parse_fn( #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; - let (token, pos) = input.next().unwrap(); + let (token, pos) = input.next().expect(NEVER_ENDS); let name = token .into_function_name_for_override() @@ -2749,7 +2774,7 @@ fn parse_fn( _ => PERR::FnMissingName.into_err(pos), })?; - match input.peek().unwrap() { + match input.peek().expect(NEVER_ENDS) { (Token::LeftParen, _) => eat_token(input, Token::LeftParen), (_, pos) => return Err(PERR::FnMissingParams(name).into_err(*pos)), }; @@ -2760,7 +2785,7 @@ fn parse_fn( let sep_err = format!("to separate the parameters of function '{}'", name); loop { - match input.next().unwrap() { + match input.next().expect(NEVER_ENDS) { (Token::RightParen, _) => break, (Token::Identifier(s), pos) => { if params.iter().any(|(p, _)| p == &s) { @@ -2780,7 +2805,7 @@ fn parse_fn( } } - match input.next().unwrap() { + match input.next().expect(NEVER_ENDS) { (Token::RightParen, _) => break, (Token::Comma, _) => (), (Token::LexError(err), pos) => return Err(err.into_err(pos)), @@ -2792,7 +2817,7 @@ fn parse_fn( } // Parse function body - let body = match input.peek().unwrap() { + let body = match input.peek().expect(NEVER_ENDS) { (Token::LeftBrace, _) => { settings.is_breakable = false; parse_block(input, state, lib, settings.level_up())? @@ -2892,10 +2917,10 @@ fn parse_anon_fn( let mut params: StaticVec<_> = Default::default(); - if input.next().unwrap().0 != Token::Or { + if input.next().expect(NEVER_ENDS).0 != Token::Or { if !match_token(input, Token::Pipe).0 { loop { - match input.next().unwrap() { + match input.next().expect(NEVER_ENDS) { (Token::Pipe, _) => break, (Token::Identifier(s), pos) => { if params.iter().any(|(p, _)| p == &s) { @@ -2915,7 +2940,7 @@ fn parse_anon_fn( } } - match input.next().unwrap() { + match input.next().expect(NEVER_ENDS) { (Token::Pipe, _) => break, (Token::Comma, _) => (), (Token::LexError(err), pos) => return Err(err.into_err(pos)), @@ -3019,7 +3044,7 @@ impl Engine { assert!(functions.is_empty()); - match input.peek().unwrap() { + match input.peek().expect(NEVER_ENDS) { (Token::EOF, _) => (), // Return error if the expression doesn't end (token, pos) => { @@ -3044,7 +3069,7 @@ impl Engine { let mut statements = Vec::with_capacity(16); let mut functions = BTreeMap::new(); - while !input.peek().unwrap().0.is_eof() { + while !input.peek().expect(NEVER_ENDS).0.is_eof() { let settings = ParseSettings { allow_if_expr: true, allow_switch_expr: true, @@ -3067,7 +3092,7 @@ impl Engine { statements.push(stmt); - match input.peek().unwrap() { + match input.peek().expect(NEVER_ENDS) { // EOF (Token::EOF, _) => break, // stmt ; diff --git a/src/result.rs b/src/result.rs index 8a873f2a..172e9834 100644 --- a/src/result.rs +++ b/src/result.rs @@ -181,7 +181,9 @@ impl fmt::Display for EvalAltResult { | Self::ErrorTerminated(_, _) => f.write_str(desc)?, Self::ErrorRuntime(d, _) if d.is::() => { - let s = &*d.read_lock::().unwrap(); + let s = &*d + .read_lock::() + .expect("never fails because the type was checked"); write!(f, "{}: {}", desc, if s.is_empty() { desc } else { s })? } Self::ErrorRuntime(d, _) if d.is::<()>() => f.write_str(desc)?, @@ -336,7 +338,11 @@ impl EvalAltResult { pub(crate) fn dump_fields(&self, map: &mut crate::Map) { map.insert( "error".into(), - format!("{:?}", self).split('(').next().unwrap().into(), + format!("{:?}", self) + .split('(') + .next() + .expect("never fails because the debug format of an error is `ErrorXXX(...)`") + .into(), ); match self { diff --git a/src/scope.rs b/src/scope.rs index 3c34cb09..1ba7a2f3 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -373,7 +373,11 @@ impl<'a> Scope<'a> { } Some((_, AccessMode::ReadOnly)) => panic!("variable {} is constant", name), Some((index, AccessMode::ReadWrite)) => { - *self.values.get_mut(index).unwrap() = Dynamic::from(value); + let value_ref = self + .values + .get_mut(index) + .expect("never fails because the index is returned by `get_index`"); + *value_ref = Dynamic::from(value); } } self @@ -406,24 +410,38 @@ impl<'a> Scope<'a> { }) } /// Get a mutable reference to an entry in the [`Scope`] based on the index. + /// + /// # Panics + /// + /// Panics if the index is out of bounds. #[inline(always)] pub(crate) fn get_mut_by_index(&mut self, index: usize) -> &mut Dynamic { - self.values.get_mut(index).expect("invalid index in Scope") + self.values + .get_mut(index) + .expect("never fails unless the index is out of bounds") } /// Update the access type of an entry in the [`Scope`]. + /// + /// # Panics + /// + /// Panics if the index is out of bounds. #[cfg(not(feature = "no_module"))] #[inline(always)] - pub(crate) fn add_entry_alias( - &mut self, - index: usize, - alias: impl Into + PartialEq, - ) -> &mut Self { - let entry = self.names.get_mut(index).expect("invalid index in Scope"); + pub(crate) fn add_entry_alias(&mut self, index: usize, alias: Identifier) -> &mut Self { + let entry = self + .names + .get_mut(index) + .expect("never fails unless the index is out of bounds"); if entry.1.is_none() { + // Initialize the alias list if it is empty. entry.1 = Some(Default::default()); } - if !entry.1.as_ref().unwrap().iter().any(|a| &alias == a) { - entry.1.as_mut().unwrap().push(alias.into()); + let list = entry + .1 + .as_mut() + .expect("never fails because the list is initialized"); + if !list.iter().any(|a| &alias == a) { + list.push(alias); } self } diff --git a/src/token.rs b/src/token.rs index 44826444..c3fa5c44 100644 --- a/src/token.rs +++ b/src/token.rs @@ -1181,7 +1181,10 @@ pub fn parse_string_literal( #[cfg(not(feature = "no_position"))] { - skip_whitespace_until = start.position().unwrap() + 1; + let start_position = start + .position() + .expect("never fails because the string must have a starting position"); + skip_whitespace_until = start_position + 1; } } @@ -1201,7 +1204,11 @@ pub fn parse_string_literal( // Whitespace to skip #[cfg(not(feature = "no_position"))] - _ if next_char.is_whitespace() && pos.position().unwrap() < skip_whitespace_until => {} + _ if next_char.is_whitespace() + && pos + .position() + .expect("never fails because a character must have a position") + < skip_whitespace_until => {} // All other characters _ => { @@ -1237,12 +1244,14 @@ fn scan_block_comment( stream: &mut impl InputStream, mut level: usize, pos: &mut Position, - comment: &mut Option, + mut comment: Option<&mut String>, ) -> usize { + let comment = &mut comment; + while let Some(c) = stream.get_next() { pos.advance(); - if let Some(ref mut comment) = comment { + if let Some(comment) = comment { comment.push(c); } @@ -1251,7 +1260,7 @@ fn scan_block_comment( if let Some(c2) = stream.peek_next() { if c2 == '*' { eat_next(stream, pos); - if let Some(ref mut comment) = comment { + if let Some(comment) = comment { comment.push(c2); } level += 1; @@ -1262,7 +1271,7 @@ fn scan_block_comment( if let Some(c2) = stream.peek_next() { if c2 == '/' { eat_next(stream, pos); - if let Some(ref mut comment) = comment { + if let Some(comment) = comment { comment.push(c2); } level -= 1; @@ -1347,21 +1356,27 @@ fn get_next_token_inner( None }; - state.comment_level = scan_block_comment(stream, state.comment_level, pos, &mut comment); + state.comment_level = + scan_block_comment(stream, state.comment_level, pos, comment.as_mut()); - let include_comments = state.include_comments; + let return_comment = state.include_comments; #[cfg(not(feature = "no_function"))] #[cfg(feature = "metadata")] - let include_comments = if is_doc_comment(comment.as_ref().unwrap()) { - true - } else { - include_comments - }; + let return_comment = return_comment + || is_doc_comment( + comment + .as_ref() + .expect("never fails because `include_comments` is true"), + ); - if include_comments { - return Some((Token::Comment(comment.unwrap()), start_pos)); - } else if state.comment_level > 0 { + if return_comment { + return Some(( + Token::Comment(comment.expect("never fails because `return_comment` is true")), + start_pos, + )); + } + if state.comment_level > 0 { // Reached EOF without ending comment block return None; } @@ -1409,7 +1424,7 @@ fn get_next_token_inner( } #[cfg(any(not(feature = "no_float"), feature = "decimal"))] '.' => { - stream.get_next().unwrap(); + stream.get_next().expect("never fails because it is `.`"); // Check if followed by digits or something that cannot start a property name match stream.peek_next().unwrap_or('\0') { @@ -1443,7 +1458,7 @@ fn get_next_token_inner( } #[cfg(not(feature = "no_float"))] 'e' => { - stream.get_next().unwrap(); + stream.get_next().expect("never fails it is `e`"); // Check if followed by digits or +/- match stream.peek_next().unwrap_or('\0') { @@ -1456,7 +1471,11 @@ fn get_next_token_inner( '+' | '-' => { result.push(next_char); pos.advance(); - result.push(stream.get_next().unwrap()); + result.push( + stream + .get_next() + .expect("never fails because it is `+` or `-`"), + ); pos.advance(); } // Not a floating-point number @@ -1749,7 +1768,7 @@ fn get_next_token_inner( }; state.comment_level = - scan_block_comment(stream, state.comment_level, pos, &mut comment); + scan_block_comment(stream, state.comment_level, pos, comment.as_mut()); if let Some(comment) = comment { return Some((Token::Comment(comment), start_pos)); diff --git a/tests/optimizer.rs b/tests/optimizer.rs index 91b29a5c..844577b5 100644 --- a/tests/optimizer.rs +++ b/tests/optimizer.rs @@ -59,21 +59,21 @@ fn test_optimizer_parse() -> Result<(), Box> { assert_eq!( format!("{:?}", ast), - "AST { source: None, body: [Expr(123 @ 1:53)], functions: Module, resolver: None }" + "AST { source: None, body: Block[Expr(123 @ 1:53)], functions: Module, resolver: None }" ); let ast = engine.compile("const DECISION = false; if DECISION { 42 } else { 123 }")?; assert_eq!( format!("{:?}", ast), - r#"AST { source: None, body: [Const(false @ 1:18, "DECISION" @ 1:7, false, 1:1), Expr(123 @ 1:51)], functions: Module, resolver: None }"# + r#"AST { source: None, body: Block[Const(false @ 1:18, "DECISION" @ 1:7, false, 1:1), Expr(123 @ 1:51)], functions: Module, resolver: None }"# ); let ast = engine.compile("if 1 == 2 { 42 }")?; assert_eq!( format!("{:?}", ast), - "AST { source: None, body: [], functions: Module, resolver: None }" + "AST { source: None, body: Block[], functions: Module, resolver: None }" ); engine.set_optimization_level(OptimizationLevel::Full); @@ -82,7 +82,7 @@ fn test_optimizer_parse() -> Result<(), Box> { assert_eq!( format!("{:?}", ast), - "AST { source: None, body: [Expr(42 @ 1:1)], functions: Module, resolver: None }" + "AST { source: None, body: Block[Expr(42 @ 1:1)], functions: Module, resolver: None }" ); Ok(()) diff --git a/tests/var_scope.rs b/tests/var_scope.rs index 082a294b..8efbc5f4 100644 --- a/tests/var_scope.rs +++ b/tests/var_scope.rs @@ -61,7 +61,7 @@ fn test_scope_eval() -> Result<(), Box> { // First invocation engine .eval_with_scope::<()>(&mut scope, " let x = 4 + 5 - y + z; y = 1;") - .expect("y and z not found?"); + .expect("variables y and z should exist"); // Second invocation using the same state let result = engine.eval_with_scope::(&mut scope, "x")?;