diff --git a/CHANGELOG.md b/CHANGELOG.md index 01592c85..9e3954ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ Rhai Release Notes ================== +Version 1.9.1 +============= + +This is a bug-fix version that fixes a bug. + +Accessing properties in _Strict Variables Mode_ no longer generates a _variable not found_ error. + + Version 1.9.0 ============= diff --git a/src/parser.rs b/src/parser.rs index 754513c8..ca3d7850 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1295,6 +1295,7 @@ impl Engine { input: &mut TokenStream, state: &mut ParseState, lib: &mut FnLib, + is_property: bool, settings: ParseSettings, ) -> ParseResult { #[cfg(not(feature = "unchecked"))] @@ -1435,11 +1436,11 @@ impl Engine { |crate::ast::Ident { name, pos }| { let (index, is_func) = state.access_var(name, lib, *pos); - if settings.options.contains(LangOptions::STRICT_VAR) - && !settings.in_closure + if !is_func && index.is_none() + && !settings.in_closure + && settings.options.contains(LangOptions::STRICT_VAR) && !state.scope.contains(name) - && !is_func { // If the parent scope is not inside another capturing closure // then we can conclude that the captured variable doesn't exist. @@ -1572,20 +1573,18 @@ impl Engine { // Once the identifier consumed we must enable next variables capturing state.allow_capture = true; } - Expr::Variable( - (None, ns, 0, state.get_interned_string(s)).into(), - None, - settings.pos, - ) + let name = state.get_interned_string(s); + Expr::Variable((None, ns, 0, name).into(), None, settings.pos) } // Normal variable access _ => { let (index, is_func) = state.access_var(&s, lib, settings.pos); - if settings.options.contains(LangOptions::STRICT_VAR) - && index.is_none() - && !state.scope.contains(&s) + if !is_property && !is_func + && index.is_none() + && settings.options.contains(LangOptions::STRICT_VAR) + && !state.scope.contains(&s) { return Err( PERR::VariableUndefined(s.to_string()).into_err(settings.pos) @@ -1599,11 +1598,8 @@ impl Engine { None } }); - Expr::Variable( - (index, ns, 0, state.get_interned_string(s)).into(), - short_index, - settings.pos, - ) + let name = state.get_interned_string(s); + Expr::Variable((index, ns, 0, name).into(), short_index, settings.pos) } } } @@ -1790,7 +1786,7 @@ impl Engine { (.., pos) => return Err(PERR::PropertyExpected.into_err(*pos)), } - let rhs = self.parse_primary(input, state, lib, settings.level_up())?; + let rhs = self.parse_primary(input, state, lib, true, settings.level_up())?; let op_flags = match op { Token::Period => ASTFlags::NONE, Token::Elvis => ASTFlags::NEGATED, @@ -1960,7 +1956,7 @@ impl Engine { // Token::EOF => Err(PERR::UnexpectedEOF.into_err(settings.pos)), // All other tokens - _ => self.parse_primary(input, state, lib, settings.level_up()), + _ => self.parse_primary(input, state, lib, false, settings.level_up()), } } diff --git a/tests/options.rs b/tests/options.rs index 27ff3ca2..8bc27a1d 100644 --- a/tests/options.rs +++ b/tests/options.rs @@ -56,10 +56,6 @@ fn test_options_allow() -> Result<(), Box> { fn test_options_strict_var() -> Result<(), Box> { let mut engine = Engine::new(); - let mut scope = Scope::new(); - scope.push("x", 42 as INT); - scope.push_constant("y", 0 as INT); - engine.compile("let x = if y { z } else { w };")?; #[cfg(not(feature = "no_function"))] @@ -78,9 +74,16 @@ fn test_options_strict_var() -> Result<(), Box> { #[cfg(not(feature = "no_function"))] engine.compile("let f = |y| x * y;")?; + let mut scope = Scope::new(); + scope.push("x", 42 as INT); + scope.push_constant("y", 0 as INT); + engine.set_strict_variables(true); assert!(engine.compile("let x = if y { z } else { w };").is_err()); + + engine.compile_with_scope(&mut scope, "if x.abs() { y } else { x + y.len };")?; + engine.compile("let y = 42; let x = y;")?; assert_eq!(