Make $in work in catch closures (#7458)

# Description

This now works:
```
try { 'x' | math abs } catch { $in }
```

# User-Facing Changes

See above.

# Tests + Formatting

Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
This commit is contained in:
Leon 2022-12-23 01:35:41 +10:00 committed by GitHub
parent 216d7d035f
commit 9d1cb1bfaf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 43 additions and 35 deletions

View File

@ -1,7 +1,9 @@
use nu_engine::{eval_block, CallExt}; use nu_engine::{eval_block, CallExt};
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Block, Closure, Command, EngineState, Stack}; use nu_protocol::engine::{Block, Closure, Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type, Value}; use nu_protocol::{
Category, Example, IntoPipelineData, PipelineData, Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)] #[derive(Clone)]
pub struct Try; pub struct Try;
@ -57,11 +59,11 @@ impl Command for Try {
Err(error) | Ok(PipelineData::Value(Value::Error { error }, ..)) => { Err(error) | Ok(PipelineData::Value(Value::Error { error }, ..)) => {
if let Some(catch_block) = catch_block { if let Some(catch_block) = catch_block {
let catch_block = engine_state.get_block(catch_block.block_id); let catch_block = engine_state.get_block(catch_block.block_id);
let err_value = Value::Error { error };
// Put the error value in the positional closure var
if let Some(var) = catch_block.signature.get_positional(0) { if let Some(var) = catch_block.signature.get_positional(0) {
if let Some(var_id) = &var.var_id { if let Some(var_id) = &var.var_id {
let err_value = Value::Error { error }; stack.add_var(*var_id, err_value.clone());
stack.add_var(*var_id, err_value);
} }
} }
@ -69,7 +71,8 @@ impl Command for Try {
engine_state, engine_state,
stack, stack,
catch_block, catch_block,
PipelineData::empty(), // Make the error accessible with $in, too
err_value.into_pipeline_data(),
false, false,
false, false,
) )
@ -86,6 +89,9 @@ impl Command for Try {
if let Some(var) = catch_block.signature.get_positional(0) { if let Some(var) = catch_block.signature.get_positional(0) {
if let Some(var_id) = &var.var_id { if let Some(var_id) = &var.var_id {
// Because external command errors aren't "real" errors,
// (unless do -c is in effect)
// they can't be passed in as Nushell values.
let err_value = Value::nothing(call.head); let err_value = Value::nothing(call.head);
stack.add_var(*var_id, err_value); stack.add_var(*var_id, err_value);
} }
@ -95,7 +101,8 @@ impl Command for Try {
engine_state, engine_state,
stack, stack,
catch_block, catch_block,
PipelineData::empty(), // The same null as in the above block is set as the $in value.
Value::nothing(call.head).into_pipeline_data(),
false, false,
false, false,
) )

View File

@ -1,50 +1,51 @@
use nu_test_support::nu; use nu_test_support::nu;
use nu_test_support::playground::Playground;
#[test] #[test]
fn try_succeed() { fn try_succeed() {
Playground::setup("try_succeed_test", |dirs, _sandbox| { let output = nu!(
let output = nu!( cwd: ".",
cwd: dirs.test(), "try { 345 } catch { echo 'hello' }"
"try { 345 } catch { echo 'hello' }" );
);
assert!(output.out.contains("345")); assert!(output.out.contains("345"));
})
} }
#[test] #[test]
fn try_catch() { fn try_catch() {
Playground::setup("try_catch_test", |dirs, _sandbox| { let output = nu!(
let output = nu!( cwd: ".",
cwd: dirs.test(), "try { foobarbaz } catch { echo 'hello' }"
"try { foobarbaz } catch { echo 'hello' }" );
);
assert!(output.out.contains("hello")); assert!(output.out.contains("hello"));
})
} }
#[test] #[test]
fn catch_can_access_error() { fn catch_can_access_error() {
Playground::setup("try_catch_test", |dirs, _sandbox| { let output = nu!(
let output = nu!( cwd: ".",
cwd: dirs.test(), "try { foobarbaz } catch { |err| $err }"
"try { foobarbaz } catch { |err| $err }" );
);
assert!(output.err.contains("External command failed")); assert!(output.err.contains("External command failed"));
}) }
#[test]
fn catch_can_access_error_as_dollar_in() {
let output = nu!(
cwd: ".",
"try { foobarbaz } catch { $in }"
);
assert!(output.err.contains("External command failed"));
} }
#[test] #[test]
fn external_failed_should_be_catched() { fn external_failed_should_be_catched() {
Playground::setup("try_catch_test", |dirs, _sandbox| { let output = nu!(
let output = nu!( cwd: ".",
cwd: dirs.test(), "try { nu --testbin fail; echo 'success' } catch { echo 'fail' }"
"try { nu --testbin fail; echo 'success' } catch { echo 'fail' }" );
);
assert!(output.out.contains("fail")); assert!(output.out.contains("fail"));
})
} }