diff --git a/src/eval.rs b/src/eval.rs index 6e5e0a7ccc..6b02c0e25c 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -9,6 +9,7 @@ pub enum ShellError { Mismatch(String, Span), Unsupported(Span), InternalError(String), + VariableNotFound(Span), } #[derive(Debug, Clone)] @@ -98,8 +99,11 @@ impl Stack { match this.vars.get(&var_id) { Some(v) => Ok(v.clone()), _ => { - println!("var_id: {}", var_id); - Err(ShellError::InternalError("variable not found".into())) + if let Some(parent) = &this.parent { + parent.get_var(var_id) + } else { + Err(ShellError::InternalError("variable not found".into())) + } } } } @@ -283,7 +287,9 @@ pub fn eval_expression( val: *i, span: expr.span, }), - Expr::Var(var_id) => stack.get_var(*var_id), + Expr::Var(var_id) => stack + .get_var(*var_id) + .map_err(move |_| ShellError::VariableNotFound(expr.span)), Expr::Call(call) => eval_call(state, stack, call), Expr::ExternalCall(_, _) => Err(ShellError::Unsupported(expr.span)), Expr::Operator(_) => Ok(Value::Unknown), diff --git a/src/main.rs b/src/main.rs index 624eb951a6..7aad256c4f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -121,7 +121,7 @@ fn main() -> std::io::Result<()> { let mut working_set = ParserWorkingSet::new(&*parser_state); let (output, err) = working_set.parse_file(&path, &file, false); if let Some(err) = err { - eprintln!("Error: {:?}", err); + eprintln!("Parse Error: {:?}", err); std::process::exit(1); } (output, working_set.render()) @@ -140,7 +140,7 @@ fn main() -> std::io::Result<()> { println!("{}", value); } Err(err) => { - eprintln!("Error: {:?}", err); + eprintln!("Eval Error: {:?}", err); std::process::exit(1); } } @@ -191,7 +191,7 @@ fn main() -> std::io::Result<()> { false, ); if let Some(err) = err { - println!("Error: {:?}", err); + eprintln!("Parse Error: {:?}", err); break; } (output, working_set.render()) @@ -208,7 +208,7 @@ fn main() -> std::io::Result<()> { println!("{}", value); } Err(err) => { - println!("Error: {:?}", err); + eprintln!("Eval Error: {:?}", err); } } } diff --git a/src/tests.rs b/src/tests.rs index 86048546ee..2cdd1d77dc 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -18,11 +18,15 @@ fn run_test(input: &str, expected: &str) -> TestResult { let output = cmd.output()?; + let stderr = String::from_utf8_lossy(&output.stderr).to_string(); + let stdout = String::from_utf8_lossy(&output.stdout).to_string(); + + println!("stdout: {}", stdout); + println!("stderr: {}", stderr); + assert!(output.status.success()); - let output = String::from_utf8_lossy(&output.stdout).to_string(); - - assert_eq!(output.trim(), expected); + assert_eq!(stdout.trim(), expected); Ok(()) } @@ -40,6 +44,7 @@ fn fail_test(input: &str, expected: &str) -> TestResult { let output = cmd.output()?; let output = String::from_utf8_lossy(&output.stderr).to_string(); + println!("{}", output); assert!(output.contains("Error:")); assert!(output.contains(expected)); @@ -71,3 +76,40 @@ fn if_test1() -> TestResult { fn if_test2() -> TestResult { run_test("if $false { 10 } else { 20 } ", "20") } + +#[test] +fn no_leak1() -> TestResult { + fail_test( + "if $false { let $x = 10 } else { let $x = 20 }; $x", + "VariableNotFound", + ) +} + +#[test] +fn no_leak2() -> TestResult { + fail_test( + "def foo [] { $x }; def bar [] { let $x = 10; foo }; bar", + "VariableNotFound", + ) +} + +#[test] +fn no_leak3() -> TestResult { + run_test( + "def foo [$x] { $x }; def bar [] { let $x = 10; foo 20}; bar", + "20", + ) +} + +#[test] +fn no_leak4() -> TestResult { + run_test( + "def foo [$x] { $x }; def bar [] { let $x = 10; (foo 20) + $x}; bar", + "30", + ) +} + +#[test] +fn simple_var_closing() -> TestResult { + run_test("let $x = 10; def foo [] { $x }; foo", "10") +}