Fix try not working with let, etc. (#13885)

# Description
Partialy addresses #13868. `try` does not catch non-zero exit code
errors from the last command in a pipeline if the result is assigned to
a variable using `let` (or `mut`).

This was fixed by adding a new `OutDest::Value` case. This is used when
the pipeline is in a "value" position. I.e., it will be collected into a
value. This ended up replacing most of the usages of `OutDest::Capture`.
So, this PR also renames `OutDest::Capture` to `OutDest::PipeSeparate`
to better fit the few remaining use cases for it.

# User-Facing Changes
Bug fix.

# Tests + Formatting
Added two tests.
This commit is contained in:
Ian Manske
2024-09-23 04:44:25 -07:00
committed by GitHub
parent 2541a712e4
commit 03ee54a4df
32 changed files with 127 additions and 98 deletions

View File

@@ -1,7 +1,7 @@
use crate::{
engine::{
ArgumentStack, EngineState, ErrorHandlerStack, Redirection, StackCallArgGuard,
StackCaptureGuard, StackIoGuard, StackOutDest, DEFAULT_OVERLAY_NAME,
StackCollectValueGuard, StackIoGuard, StackOutDest, DEFAULT_OVERLAY_NAME,
},
Config, OutDest, ShellError, Span, Value, VarId, ENV_VARIABLE_ID, NU_VARIABLE_ID,
};
@@ -68,7 +68,7 @@ impl Stack {
/// stdout and stderr will be set to [`OutDest::Inherit`]. So, if the last command is an external command,
/// then its output will be forwarded to the terminal/stdio streams.
///
/// Use [`Stack::capture`] afterwards if you need to evaluate an expression to a [`Value`]
/// Use [`Stack::collect_value`] afterwards if you need to evaluate an expression to a [`Value`]
/// (as opposed to a [`PipelineData`](crate::PipelineData)).
pub fn new() -> Self {
Self {
@@ -299,7 +299,8 @@ impl Stack {
}
pub fn captures_to_stack(&self, captures: Vec<(VarId, Value)>) -> Stack {
self.captures_to_stack_preserve_out_dest(captures).capture()
self.captures_to_stack_preserve_out_dest(captures)
.collect_value()
}
pub fn captures_to_stack_preserve_out_dest(&self, captures: Vec<(VarId, Value)>) -> Stack {
@@ -589,11 +590,11 @@ impl Stack {
self.out_dest.pipe_stderr.as_ref()
}
/// Temporarily set the pipe stdout redirection to [`OutDest::Capture`].
/// Temporarily set the pipe stdout redirection to [`OutDest::Value`].
///
/// This is used before evaluating an expression into a `Value`.
pub fn start_capture(&mut self) -> StackCaptureGuard {
StackCaptureGuard::new(self)
pub fn start_collect_value(&mut self) -> StackCollectValueGuard {
StackCollectValueGuard::new(self)
}
/// Temporarily use the output redirections in the parent scope.
@@ -612,14 +613,14 @@ impl Stack {
StackIoGuard::new(self, stdout, stderr)
}
/// Mark stdout for the last command as [`OutDest::Capture`].
/// Mark stdout for the last command as [`OutDest::Value`].
///
/// This will irreversibly alter the output redirections, and so it only makes sense to use this on an owned `Stack`
/// (which is why this function does not take `&mut self`).
///
/// See [`Stack::start_capture`] which can temporarily set stdout as [`OutDest::Capture`] for a mutable `Stack` reference.
pub fn capture(mut self) -> Self {
self.out_dest.pipe_stdout = Some(OutDest::Capture);
/// See [`Stack::start_collect_value`] which can temporarily set stdout as [`OutDest::Value`] for a mutable `Stack` reference.
pub fn collect_value(mut self) -> Self {
self.out_dest.pipe_stdout = Some(OutDest::Value);
self.out_dest.pipe_stderr = None;
self
}

View File

@@ -184,15 +184,15 @@ impl Drop for StackIoGuard<'_> {
}
}
pub struct StackCaptureGuard<'a> {
pub struct StackCollectValueGuard<'a> {
stack: &'a mut Stack,
old_pipe_stdout: Option<OutDest>,
old_pipe_stderr: Option<OutDest>,
}
impl<'a> StackCaptureGuard<'a> {
impl<'a> StackCollectValueGuard<'a> {
pub(crate) fn new(stack: &'a mut Stack) -> Self {
let old_pipe_stdout = mem::replace(&mut stack.out_dest.pipe_stdout, Some(OutDest::Capture));
let old_pipe_stdout = mem::replace(&mut stack.out_dest.pipe_stdout, Some(OutDest::Value));
let old_pipe_stderr = stack.out_dest.pipe_stderr.take();
Self {
stack,
@@ -202,7 +202,7 @@ impl<'a> StackCaptureGuard<'a> {
}
}
impl<'a> Deref for StackCaptureGuard<'a> {
impl<'a> Deref for StackCollectValueGuard<'a> {
type Target = Stack;
fn deref(&self) -> &Self::Target {
@@ -210,13 +210,13 @@ impl<'a> Deref for StackCaptureGuard<'a> {
}
}
impl<'a> DerefMut for StackCaptureGuard<'a> {
impl<'a> DerefMut for StackCollectValueGuard<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.stack
}
}
impl Drop for StackCaptureGuard<'_> {
impl Drop for StackCollectValueGuard<'_> {
fn drop(&mut self) {
self.out_dest.pipe_stdout = self.old_pipe_stdout.take();
self.out_dest.pipe_stderr = self.old_pipe_stderr.take();
@@ -233,7 +233,7 @@ pub struct StackCallArgGuard<'a> {
impl<'a> StackCallArgGuard<'a> {
pub(crate) fn new(stack: &'a mut Stack) -> Self {
let old_pipe_stdout = mem::replace(&mut stack.out_dest.pipe_stdout, Some(OutDest::Capture));
let old_pipe_stdout = mem::replace(&mut stack.out_dest.pipe_stdout, Some(OutDest::Value));
let old_pipe_stderr = stack.out_dest.pipe_stderr.take();
let old_stdout = stack