Eliminate unnecessary data structures.

This commit is contained in:
Stephen Chung 2021-04-27 22:28:01 +08:00
parent a5d4a0abb9
commit b091113334
3 changed files with 105 additions and 59 deletions

View File

@ -2,8 +2,8 @@
name = "rhai_codegen" name = "rhai_codegen"
version = "0.3.5" version = "0.3.5"
edition = "2018" edition = "2018"
authors = ["jhwgh1968"] authors = ["jhwgh1968", "Stephen Chung"]
description = "Procedural macro support package for Rhai, a scripting language for Rust" description = "Procedural macros support package for Rhai, a scripting language and engine for Rust"
homepage = "https://rhai.rs/book/plugins/index.html" homepage = "https://rhai.rs/book/plugins/index.html"
repository = "https://github.com/rhaiscript/rhai" repository = "https://github.com/rhaiscript/rhai"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"

View File

@ -51,35 +51,42 @@ pub type Precedence = NonZeroU8;
// We cannot use Cow<str> here because `eval` may load a [module][Module] and // We cannot use Cow<str> here because `eval` may load a [module][Module] and
// the module name will live beyond the AST of the eval script text. // the module name will live beyond the AST of the eval script text.
// The best we can do is a shared reference. // The best we can do is a shared reference.
//
// This implementation splits the module names from the shared modules to improve data locality.
// Most usage will be looking up a particular key from the list and then getting the module that
// corresponds to that key.
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct Imports(StaticVec<Identifier>, StaticVec<Shared<Module>>); pub struct Imports {
keys: StaticVec<Identifier>,
modules: StaticVec<Shared<Module>>,
}
impl Imports { impl Imports {
/// Get the length of this stack of imported [modules][Module]. /// Get the length of this stack of imported [modules][Module].
#[inline(always)] #[inline(always)]
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.0.len() self.keys.len()
} }
/// Is this stack of imported [modules][Module] empty? /// Is this stack of imported [modules][Module] empty?
#[inline(always)] #[inline(always)]
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.0.is_empty() self.keys.is_empty()
} }
/// Get the imported [modules][Module] at a particular index. /// Get the imported [modules][Module] at a particular index.
#[inline(always)] #[inline(always)]
pub fn get(&self, index: usize) -> Option<Shared<Module>> { pub fn get(&self, index: usize) -> Option<Shared<Module>> {
self.1.get(index).cloned() self.modules.get(index).cloned()
} }
/// Get the imported [modules][Module] at a particular index. /// Get the imported [modules][Module] at a particular index.
#[allow(dead_code)] #[allow(dead_code)]
#[inline(always)] #[inline(always)]
pub(crate) fn get_mut(&mut self, index: usize) -> Option<&mut Shared<Module>> { pub(crate) fn get_mut(&mut self, index: usize) -> Option<&mut Shared<Module>> {
self.1.get_mut(index) self.modules.get_mut(index)
} }
/// Get the index of an imported [modules][Module] by name. /// Get the index of an imported [modules][Module] by name.
#[inline(always)] #[inline(always)]
pub fn find(&self, name: &str) -> Option<usize> { pub fn find(&self, name: &str) -> Option<usize> {
self.0 self.keys
.iter() .iter()
.enumerate() .enumerate()
.rev() .rev()
@ -88,22 +95,22 @@ impl Imports {
/// Push an imported [modules][Module] onto the stack. /// Push an imported [modules][Module] onto the stack.
#[inline(always)] #[inline(always)]
pub fn push(&mut self, name: impl Into<Identifier>, module: impl Into<Shared<Module>>) { pub fn push(&mut self, name: impl Into<Identifier>, module: impl Into<Shared<Module>>) {
self.0.push(name.into()); self.keys.push(name.into());
self.1.push(module.into()); self.modules.push(module.into());
} }
/// Truncate the stack of imported [modules][Module] to a particular length. /// Truncate the stack of imported [modules][Module] to a particular length.
#[inline(always)] #[inline(always)]
pub fn truncate(&mut self, size: usize) { pub fn truncate(&mut self, size: usize) {
self.0.truncate(size); self.keys.truncate(size);
self.1.truncate(size); self.modules.truncate(size);
} }
/// Get an iterator to this stack of imported [modules][Module] in reverse order. /// Get an iterator to this stack of imported [modules][Module] in reverse order.
#[allow(dead_code)] #[allow(dead_code)]
#[inline(always)] #[inline(always)]
pub fn iter(&self) -> impl Iterator<Item = (&str, &Module)> { pub fn iter(&self) -> impl Iterator<Item = (&str, &Module)> {
self.0 self.keys
.iter() .iter()
.zip(self.1.iter()) .zip(self.modules.iter())
.rev() .rev()
.map(|(name, module)| (name.as_str(), module.as_ref())) .map(|(name, module)| (name.as_str(), module.as_ref()))
} }
@ -111,29 +118,32 @@ impl Imports {
#[allow(dead_code)] #[allow(dead_code)]
#[inline(always)] #[inline(always)]
pub(crate) fn iter_raw(&self) -> impl Iterator<Item = (&Identifier, &Shared<Module>)> { pub(crate) fn iter_raw(&self) -> impl Iterator<Item = (&Identifier, &Shared<Module>)> {
self.0.iter().rev().zip(self.1.iter().rev()) self.keys.iter().rev().zip(self.modules.iter().rev())
} }
/// Get an iterator to this stack of imported [modules][Module] in forward order. /// Get an iterator to this stack of imported [modules][Module] in forward order.
#[allow(dead_code)] #[allow(dead_code)]
#[inline(always)] #[inline(always)]
pub(crate) fn scan_raw(&self) -> impl Iterator<Item = (&Identifier, &Shared<Module>)> { pub(crate) fn scan_raw(&self) -> impl Iterator<Item = (&Identifier, &Shared<Module>)> {
self.0.iter().zip(self.1.iter()) self.keys.iter().zip(self.modules.iter())
} }
/// Get a consuming iterator to this stack of imported [modules][Module] in reverse order. /// Get a consuming iterator to this stack of imported [modules][Module] in reverse order.
#[inline(always)] #[inline(always)]
pub fn into_iter(self) -> impl Iterator<Item = (Identifier, Shared<Module>)> { pub fn into_iter(self) -> impl Iterator<Item = (Identifier, Shared<Module>)> {
self.0.into_iter().rev().zip(self.1.into_iter().rev()) self.keys
.into_iter()
.rev()
.zip(self.modules.into_iter().rev())
} }
/// Does the specified function hash key exist in this stack of imported [modules][Module]? /// Does the specified function hash key exist in this stack of imported [modules][Module]?
#[allow(dead_code)] #[allow(dead_code)]
#[inline(always)] #[inline(always)]
pub fn contains_fn(&self, hash: u64) -> bool { pub fn contains_fn(&self, hash: u64) -> bool {
self.1.iter().any(|m| m.contains_qualified_fn(hash)) self.modules.iter().any(|m| m.contains_qualified_fn(hash))
} }
/// Get specified function via its hash key. /// Get specified function via its hash key.
#[inline(always)] #[inline(always)]
pub fn get_fn(&self, hash: u64) -> Option<(&CallableFunction, Option<&Identifier>)> { pub fn get_fn(&self, hash: u64) -> Option<(&CallableFunction, Option<&Identifier>)> {
self.1 self.modules
.iter() .iter()
.rev() .rev()
.find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw()))) .find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw())))
@ -143,12 +153,15 @@ impl Imports {
#[allow(dead_code)] #[allow(dead_code)]
#[inline(always)] #[inline(always)]
pub fn contains_iter(&self, id: TypeId) -> bool { pub fn contains_iter(&self, id: TypeId) -> bool {
self.1.iter().any(|m| m.contains_qualified_iter(id)) self.modules.iter().any(|m| m.contains_qualified_iter(id))
} }
/// Get the specified [`TypeId`][std::any::TypeId] iterator. /// Get the specified [`TypeId`][std::any::TypeId] iterator.
#[inline(always)] #[inline(always)]
pub fn get_iter(&self, id: TypeId) -> Option<IteratorFn> { pub fn get_iter(&self, id: TypeId) -> Option<IteratorFn> {
self.1.iter().rev().find_map(|m| m.get_qualified_iter(id)) self.modules
.iter()
.rev()
.find_map(|m| m.get_qualified_iter(id))
} }
} }
@ -160,7 +173,7 @@ impl fmt::Debug for Imports {
f.debug_map().finish() f.debug_map().finish()
} else { } else {
f.debug_map() f.debug_map()
.entries(self.0.iter().zip(self.1.iter())) .entries(self.keys.iter().zip(self.modules.iter()))
.finish() .finish()
} }
} }
@ -232,24 +245,27 @@ pub const TOKEN_OP_CONCAT: Token = Token::PlusAssign;
/// Method of chaining. /// Method of chaining.
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum ChainType { enum ChainType {
/// Not a chaining type.
NonChaining,
/// Indexing. /// Indexing.
#[cfg(not(feature = "no_index"))]
Index, Index,
/// Dotting. /// Dotting.
#[cfg(not(feature = "no_object"))]
Dot, Dot,
} }
/// Value of a chaining argument. /// Value of a chaining argument.
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
#[derive(Debug, Clone, Hash)] #[derive(Debug, Clone, Hash)]
pub enum ChainArgument { enum ChainArgument {
/// Dot-property access. /// Dot-property access.
#[cfg(not(feature = "no_object"))]
Property(Position), Property(Position),
/// Arguments to a dot-function call. /// Arguments to a dot method call.
FnCallArgs(StaticVec<Dynamic>, StaticVec<Position>), #[cfg(not(feature = "no_object"))]
MethodCallArgs(StaticVec<Dynamic>, StaticVec<Position>),
/// Index value. /// Index value.
#[cfg(not(feature = "no_index"))]
IndexValue(Dynamic, Position), IndexValue(Dynamic, Position),
} }
@ -264,7 +280,8 @@ impl ChainArgument {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
pub fn as_index_value(self) -> Dynamic { pub fn as_index_value(self) -> Dynamic {
match self { match self {
Self::Property(_) | Self::FnCallArgs(_, _) => { #[cfg(not(feature = "no_object"))]
Self::Property(_) | Self::MethodCallArgs(_, _) => {
panic!("expecting ChainArgument::IndexValue") panic!("expecting ChainArgument::IndexValue")
} }
Self::IndexValue(value, _) => value, Self::IndexValue(value, _) => value,
@ -274,28 +291,32 @@ impl ChainArgument {
/// ///
/// # Panics /// # Panics
/// ///
/// Panics if not `ChainArgument::FnCallArgs`. /// Panics if not `ChainArgument::MethodCallArgs`.
#[inline(always)] #[inline(always)]
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
pub fn as_fn_call_args(self) -> (StaticVec<Dynamic>, StaticVec<Position>) { pub fn as_fn_call_args(self) -> (StaticVec<Dynamic>, StaticVec<Position>) {
match self { match self {
Self::Property(_) | Self::IndexValue(_, _) => { Self::Property(_) => {
panic!("expecting ChainArgument::FnCallArgs") panic!("expecting ChainArgument::MethodCallArgs")
} }
Self::FnCallArgs(values, positions) => (values, positions), #[cfg(not(feature = "no_index"))]
Self::IndexValue(_, _) => {
panic!("expecting ChainArgument::MethodCallArgs")
}
Self::MethodCallArgs(values, positions) => (values, positions),
} }
} }
} }
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] #[cfg(not(feature = "no_object"))]
impl From<(StaticVec<Dynamic>, StaticVec<Position>)> for ChainArgument { impl From<(StaticVec<Dynamic>, StaticVec<Position>)> for ChainArgument {
#[inline(always)] #[inline(always)]
fn from((values, positions): (StaticVec<Dynamic>, StaticVec<Position>)) -> Self { fn from((values, positions): (StaticVec<Dynamic>, StaticVec<Position>)) -> Self {
Self::FnCallArgs(values, positions) Self::MethodCallArgs(values, positions)
} }
} }
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] #[cfg(not(feature = "no_index"))]
impl From<(Dynamic, Position)> for ChainArgument { impl From<(Dynamic, Position)> for ChainArgument {
#[inline(always)] #[inline(always)]
fn from((value, pos): (Dynamic, Position)) -> Self { fn from((value, pos): (Dynamic, Position)) -> Self {
@ -1129,14 +1150,14 @@ impl Engine {
level: usize, level: usize,
new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>, new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
) -> Result<(Dynamic, bool), Box<EvalAltResult>> { ) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
assert!(chain_type != ChainType::NonChaining);
let is_ref = target.is_ref(); let is_ref = target.is_ref();
let next_chain = match rhs { let rhs_chain = match rhs {
Expr::Index(_, _) => ChainType::Index, #[cfg(not(feature = "no_index"))]
Expr::Dot(_, _) => ChainType::Dot, Expr::Index(_, _) => Some(ChainType::Index),
_ => ChainType::NonChaining, #[cfg(not(feature = "no_object"))]
Expr::Dot(_, _) => Some(ChainType::Dot),
_ => None,
}; };
// Pop the last index value // Pop the last index value
@ -1155,9 +1176,10 @@ impl Engine {
let obj_ptr = &mut self.get_indexed_mut( let obj_ptr = &mut self.get_indexed_mut(
mods, state, lib, target, idx_val, idx_pos, false, is_ref, true, level, mods, state, lib, target, idx_val, idx_pos, false, is_ref, true, level,
)?; )?;
let rhs_chain = rhs_chain.unwrap();
self.eval_dot_index_chain_helper( self.eval_dot_index_chain_helper(
mods, state, lib, this_ptr, obj_ptr, &x.rhs, idx_values, next_chain, mods, state, lib, this_ptr, obj_ptr, &x.rhs, idx_values, rhs_chain,
level, new_val, level, new_val,
) )
.map_err(|err| err.fill_position(*x_pos)) .map_err(|err| err.fill_position(*x_pos))
@ -1340,9 +1362,10 @@ impl Engine {
// Others - syntax error // Others - syntax error
expr => unreachable!("invalid dot expression: {:?}", expr), expr => unreachable!("invalid dot expression: {:?}", expr),
}; };
let rhs_chain = rhs_chain.unwrap();
self.eval_dot_index_chain_helper( self.eval_dot_index_chain_helper(
mods, state, lib, this_ptr, &mut val, &x.rhs, idx_values, next_chain, mods, state, lib, this_ptr, &mut val, &x.rhs, idx_values, rhs_chain,
level, new_val, level, new_val,
) )
.map_err(|err| err.fill_position(*x_pos)) .map_err(|err| err.fill_position(*x_pos))
@ -1354,6 +1377,7 @@ impl Engine {
Expr::Property(p) => { Expr::Property(p) => {
let ((getter, hash_get), (setter, hash_set), Ident { pos, .. }) = let ((getter, hash_get), (setter, hash_set), Ident { pos, .. }) =
p.as_ref(); p.as_ref();
let rhs_chain = rhs_chain.unwrap();
let hash_get = FnCallHashes::from_native(*hash_get); let hash_get = FnCallHashes::from_native(*hash_get);
let hash_set = FnCallHashes::from_native(*hash_set); let hash_set = FnCallHashes::from_native(*hash_set);
let arg_values = &mut [target.as_mut(), &mut Default::default()]; let arg_values = &mut [target.as_mut(), &mut Default::default()];
@ -1374,7 +1398,7 @@ impl Engine {
&mut val.into(), &mut val.into(),
&x.rhs, &x.rhs,
idx_values, idx_values,
next_chain, rhs_chain,
level, level,
new_val, new_val,
) )
@ -1405,6 +1429,7 @@ impl Engine {
// xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr // xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr
Expr::FnCall(f, pos) if !f.is_qualified() => { Expr::FnCall(f, pos) if !f.is_qualified() => {
let FnCallExpr { name, hashes, .. } = f.as_ref(); let FnCallExpr { name, hashes, .. } = f.as_ref();
let rhs_chain = rhs_chain.unwrap();
let mut args = idx_val.as_fn_call_args(); let mut args = idx_val.as_fn_call_args();
let (mut val, _) = self.make_method_call( let (mut val, _) = self.make_method_call(
mods, state, lib, name, *hashes, target, &mut args, *pos, level, mods, state, lib, name, *hashes, target, &mut args, *pos, level,
@ -1414,7 +1439,7 @@ impl Engine {
self.eval_dot_index_chain_helper( self.eval_dot_index_chain_helper(
mods, state, lib, this_ptr, target, &x.rhs, idx_values, mods, state, lib, this_ptr, target, &x.rhs, idx_values,
next_chain, level, new_val, rhs_chain, level, new_val,
) )
.map_err(|err| err.fill_position(*pos)) .map_err(|err| err.fill_position(*pos))
} }
@ -1430,8 +1455,6 @@ impl Engine {
_ => EvalAltResult::ErrorDotExpr("".into(), rhs.position()).into(), _ => EvalAltResult::ErrorDotExpr("".into(), rhs.position()).into(),
} }
} }
chain_type => unreachable!("invalid ChainType: {:?}", chain_type),
} }
} }
@ -1449,7 +1472,9 @@ impl Engine {
new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>, new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
) -> RhaiResult { ) -> RhaiResult {
let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, op_pos) = match expr { let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, op_pos) = match expr {
#[cfg(not(feature = "no_index"))]
Expr::Index(x, pos) => (x.as_ref(), ChainType::Index, *pos), Expr::Index(x, pos) => (x.as_ref(), ChainType::Index, *pos),
#[cfg(not(feature = "no_object"))]
Expr::Dot(x, pos) => (x.as_ref(), ChainType::Dot, *pos), Expr::Dot(x, pos) => (x.as_ref(), ChainType::Dot, *pos),
_ => unreachable!("index or dot chain expected, but gets {:?}", expr), _ => unreachable!("index or dot chain expected, but gets {:?}", expr),
}; };
@ -1510,7 +1535,7 @@ impl Engine {
lib: &[&Module], lib: &[&Module],
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Option<&mut Dynamic>,
expr: &Expr, expr: &Expr,
parent_chain_type: ChainType, _parent_chain_type: ChainType,
idx_values: &mut StaticVec<ChainArgument>, idx_values: &mut StaticVec<ChainArgument>,
size: usize, size: usize,
level: usize, level: usize,
@ -1519,7 +1544,8 @@ impl Engine {
self.inc_operations(state, expr.position())?; self.inc_operations(state, expr.position())?;
match expr { match expr {
Expr::FnCall(x, _) if parent_chain_type == ChainType::Dot && !x.is_qualified() => { #[cfg(not(feature = "no_object"))]
Expr::FnCall(x, _) if _parent_chain_type == ChainType::Dot && !x.is_qualified() => {
let mut arg_positions: StaticVec<_> = Default::default(); let mut arg_positions: StaticVec<_> = Default::default();
let mut arg_values = x let mut arg_values = x
@ -1539,11 +1565,13 @@ impl Engine {
idx_values.push((arg_values, arg_positions).into()); idx_values.push((arg_values, arg_positions).into());
} }
Expr::FnCall(_, _) if parent_chain_type == ChainType::Dot => { #[cfg(not(feature = "no_object"))]
Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dot => {
unreachable!("function call in dot chain should not be namespace-qualified") unreachable!("function call in dot chain should not be namespace-qualified")
} }
Expr::Property(x) if parent_chain_type == ChainType::Dot => { #[cfg(not(feature = "no_object"))]
Expr::Property(x) if _parent_chain_type == ChainType::Dot => {
idx_values.push(ChainArgument::Property(x.2.pos)) idx_values.push(ChainArgument::Property(x.2.pos))
} }
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"), Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
@ -1553,12 +1581,15 @@ impl Engine {
// Evaluate in left-to-right order // Evaluate in left-to-right order
let lhs_val = match lhs { let lhs_val = match lhs {
Expr::Property(x) if parent_chain_type == ChainType::Dot => { #[cfg(not(feature = "no_object"))]
Expr::Property(x) if _parent_chain_type == ChainType::Dot => {
ChainArgument::Property(x.2.pos) ChainArgument::Property(x.2.pos)
} }
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"), Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
#[cfg(not(feature = "no_object"))]
Expr::FnCall(x, _) Expr::FnCall(x, _)
if parent_chain_type == ChainType::Dot && !x.is_qualified() => if _parent_chain_type == ChainType::Dot && !x.is_qualified() =>
{ {
let mut arg_positions: StaticVec<_> = Default::default(); let mut arg_positions: StaticVec<_> = Default::default();
@ -1579,17 +1610,26 @@ impl Engine {
(arg_values, arg_positions).into() (arg_values, arg_positions).into()
} }
Expr::FnCall(_, _) if parent_chain_type == ChainType::Dot => { #[cfg(not(feature = "no_object"))]
Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dot => {
unreachable!("function call in dot chain should not be namespace-qualified") unreachable!("function call in dot chain should not be namespace-qualified")
} }
_ => self #[cfg(not(feature = "no_object"))]
expr if _parent_chain_type == ChainType::Dot => {
unreachable!("invalid dot expression: {:?}", expr);
}
#[cfg(not(feature = "no_index"))]
_ if _parent_chain_type == ChainType::Index => self
.eval_expr(scope, mods, state, lib, this_ptr, lhs, level) .eval_expr(scope, mods, state, lib, this_ptr, lhs, level)
.map(|v| (v.flatten(), lhs.position()).into())?, .map(|v| (v.flatten(), lhs.position()).into())?,
expr => unreachable!("unknown chained expression: {:?}", expr),
}; };
// Push in reverse order // Push in reverse order
let chain_type = match expr { let chain_type = match expr {
#[cfg(not(feature = "no_index"))]
Expr::Index(_, _) => ChainType::Index, Expr::Index(_, _) => ChainType::Index,
#[cfg(not(feature = "no_object"))]
Expr::Dot(_, _) => ChainType::Dot, Expr::Dot(_, _) => ChainType::Dot,
_ => unreachable!("index or dot chain expected, but gets {:?}", expr), _ => unreachable!("index or dot chain expected, but gets {:?}", expr),
}; };
@ -1600,10 +1640,16 @@ impl Engine {
idx_values.push(lhs_val); idx_values.push(lhs_val);
} }
_ => idx_values.push( #[cfg(not(feature = "no_object"))]
_ if _parent_chain_type == ChainType::Dot => {
unreachable!("invalid dot expression: {:?}", expr);
}
#[cfg(not(feature = "no_index"))]
_ if _parent_chain_type == ChainType::Index => idx_values.push(
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level) self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
.map(|v| (v.flatten(), expr.position()).into())?, .map(|v| (v.flatten(), expr.position()).into())?,
), ),
_ => unreachable!("unknown chained expression: {:?}", expr),
} }
Ok(()) Ok(())

View File

@ -187,10 +187,10 @@ impl Position {
} }
/// Print this [`Position`] for debug purposes. /// Print this [`Position`] for debug purposes.
#[inline(always)] #[inline(always)]
pub(crate) fn debug_print(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { pub(crate) fn debug_print(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(not(feature = "no_position"))] #[cfg(not(feature = "no_position"))]
if !self.is_none() { if !self.is_none() {
write!(f, " @ {:?}", self)?; write!(_f, " @ {:?}", self)?;
} }
Ok(()) Ok(())