diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 263b4eca..75d70dbc 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -401,6 +401,10 @@ pub enum Expr { /// func `(` expr `,` ... `)` FnCall(Box, Position), /// lhs `.` rhs + /// + /// ### Flags + /// + /// No flags are defined at this time. Use [`NONE`][ASTFlags::NONE]. Dot(Box, ASTFlags, Position), /// lhs `[` rhs `]` /// diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index a5426ca1..159a7a54 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -118,7 +118,7 @@ impl ChainArgument { impl Engine { /// Chain-evaluate a dot/index chain. - /// [`Position`] in [`EvalAltResult`] is [`NONE`][Position::NONE] and must be set afterwards. + /// [`Position`] in [`EvalAltResult`] may be [`NONE`][Position::NONE] and should be set afterwards. fn eval_dot_index_chain_helper( &self, global: &mut GlobalRuntimeState, @@ -620,11 +620,41 @@ impl Engine { let idx_values = &mut StaticVec::new_const(); - self.eval_dot_index_chain_arguments( - scope, global, caches, lib, this_ptr, rhs, options, chain_type, idx_values, 0, level, - )?; + match rhs { + // Short-circuit for simple property access: {expr}.prop + Expr::Property(..) if chain_type == ChainType::Dotting => { + idx_values.push(ChainArgument::Property(rhs.position())) + } + Expr::Property(..) => unreachable!("unexpected Expr::Property for indexing"), + // Short-circuit for simple method call: {expr}.func() + Expr::FnCall(x, ..) if chain_type == ChainType::Dotting && x.args.is_empty() => { + idx_values.push(ChainArgument::MethodCallArgs( + Default::default(), + Position::NONE, + )) + } + // Short-circuit for method call with all literal arguments: {expr}.func(1, 2, 3) + Expr::FnCall(x, ..) + if chain_type == ChainType::Dotting && x.args.iter().all(Expr::is_constant) => + { + let args: Vec<_> = x + .args + .iter() + .map(|expr| expr.get_literal_value().unwrap()) + .collect(); - let is_assignment = new_val.is_some(); + idx_values.push(ChainArgument::MethodCallArgs( + args.into_boxed_slice(), + x.args[0].position(), + )) + } + _ => { + self.eval_dot_index_chain_arguments( + scope, global, caches, lib, this_ptr, rhs, options, chain_type, idx_values, 0, + level, + )?; + } + } match lhs { // id.??? or id[???] @@ -645,11 +675,9 @@ impl Engine { global, caches, lib, &mut None, obj_ptr, root, expr, rhs, options, idx_values, chain_type, level, new_val, ) - .map(|(v, ..)| v) - .map_err(|err| err.fill_position(op_pos)) } // {expr}.??? = ??? or {expr}[???] = ??? - _ if is_assignment => unreachable!("cannot assign to an expression"), + _ if new_val.is_some() => unreachable!("cannot assign to an expression"), // {expr}.??? or {expr}[???] expr => { let value = self @@ -657,14 +685,15 @@ impl Engine { .flatten(); let obj_ptr = &mut value.into(); let root = ("", expr.start_position()); + self.eval_dot_index_chain_helper( global, caches, lib, this_ptr, obj_ptr, root, expr, rhs, options, idx_values, chain_type, level, new_val, ) - .map(|(v, ..)| if is_assignment { Dynamic::UNIT } else { v }) - .map_err(|err| err.fill_position(op_pos)) } } + .map(|(v, ..)| v) + .map_err(|err| err.fill_position(op_pos)) } /// Evaluate a chain of indexes and store the results in a [`StaticVec`]. @@ -798,7 +827,6 @@ impl Engine { } /// Call a get indexer. - /// [`Position`] in [`EvalAltResult`] may be [`NONE`][Position::NONE] and should be set afterwards. #[inline(always)] fn call_indexer_get( &self, @@ -822,7 +850,6 @@ impl Engine { } /// Call a set indexer. - /// [`Position`] in [`EvalAltResult`] may be [`NONE`][Position::NONE] and should be set afterwards. #[inline(always)] fn call_indexer_set( &self, diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index b8d11bd7..0ae9c2fb 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -184,9 +184,7 @@ impl Engine { *target.as_mut() = new_val; } - target - .propagate_changed_value() - .map_err(|err| err.fill_position(op_info.pos)) + target.propagate_changed_value(op_info.pos) } /// Evaluate a statement. diff --git a/src/eval/target.rs b/src/eval/target.rs index 69d67440..2e462677 100644 --- a/src/eval/target.rs +++ b/src/eval/target.rs @@ -1,7 +1,7 @@ //! Type to hold a mutable reference to the target of an evaluation. use crate::types::dynamic::Variant; -use crate::{Dynamic, RhaiResultOf}; +use crate::{Dynamic, Position, RhaiResultOf}; use std::ops::{Deref, DerefMut}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -272,10 +272,8 @@ impl<'a> Target<'a> { } /// Propagate a changed value back to the original source. /// This has no effect for direct references. - /// - /// [`Position`] in [`EvalAltResult`] is [`NONE`][Position::NONE] and should be set afterwards. #[inline] - pub fn propagate_changed_value(&mut self) -> RhaiResultOf<()> { + pub fn propagate_changed_value(&mut self, pos: Position) -> RhaiResultOf<()> { match self { Self::RefMut(..) | Self::TempValue(..) => (), #[cfg(not(feature = "no_closure"))] @@ -287,7 +285,7 @@ impl<'a> Target<'a> { Box::new(crate::ERR::ErrorMismatchDataType( "bool".to_string(), err.to_string(), - crate::Position::NONE, + pos, )) })?; @@ -317,7 +315,7 @@ impl<'a> Target<'a> { Box::new(crate::ERR::ErrorMismatchDataType( "integer".to_string(), err.to_string(), - crate::Position::NONE, + pos, )) })?; @@ -338,7 +336,7 @@ impl<'a> Target<'a> { Box::new(crate::ERR::ErrorMismatchDataType( "INT".to_string(), err.to_string(), - crate::Position::NONE, + pos, )) })?; @@ -363,7 +361,7 @@ impl<'a> Target<'a> { Box::new(crate::ERR::ErrorMismatchDataType( "char".to_string(), err.to_string(), - crate::Position::NONE, + pos, )) })?; diff --git a/src/func/call.rs b/src/func/call.rs index ea1355d4..9852d18b 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -920,9 +920,7 @@ impl Engine { // Propagate the changed value back to the source if necessary if updated { - target - .propagate_changed_value() - .map_err(|err| err.fill_position(pos))?; + target.propagate_changed_value(pos)?; } Ok((result, updated))