Document and critically review ShellError variants - Ep. 1 (#8229)

# Description

The `ShellError` enum at the moment is kind of messy. 

Many variants are basic tuple structs where you always have to reference
the implementation with its macro invocation to know which field serves
which purpose.
Furthermore we have both variants that are kind of redundant or either
overly broad to be useful for the user to match on or overly specific
with few uses.

So I set out to start fixing the lacking documentation and naming to
make it feasible to critically review the individual usages and fix
those.
Furthermore we can decide to join or split up variants that don't seem
to be fit for purpose.

Feel free to add review comments if you spot inconsistent use of
`ShellError` variants.

- Name fields on `ShellError::OperatorOverflow`
- Name fields on `ShellError::PipelineMismatch`
- Add doc to `ShellError::OnlySupportsThisInputType`
- Name `ShellError::OnlySupportsThisInputType`
- Name field on `ShellError::PipelineEmpty`
- Comment about issues with `TypeMismatch*`
- Fix a few `exp_input_type`s
- Name fields on `ShellError::InvalidRange`

# User-Facing Changes

(None now, end goal more explicit and consistent error messages)

# Tests + Formatting

(No additional tests needed so far)
This commit is contained in:
Stefan Holderbach
2023-03-01 20:34:48 +01:00
committed by GitHub
parent 0a1af85200
commit 438062d7fc
104 changed files with 749 additions and 752 deletions

View File

@ -228,20 +228,19 @@ impl PipelineData {
},
// Propagate errors by explicitly matching them before the final case.
Value::Error { error } => Err(error),
other => Err(ShellError::OnlySupportsThisInputType(
"list, binary, raw data or range".into(),
other.get_type().to_string(),
span,
// This line requires the Value::Error match above.
other.expect_span(),
)),
other => Err(ShellError::OnlySupportsThisInputType {
exp_input_type: "list, binary, raw data or range".into(),
wrong_type: other.get_type().to_string(),
dst_span: span,
src_span: other.expect_span(),
}),
},
PipelineData::Empty => Err(ShellError::OnlySupportsThisInputType(
"list, binary, raw data or range".into(),
"null".into(),
span,
span, // TODO: make PipelineData::Empty spanned, so that the span can be used here.
)),
PipelineData::Empty => Err(ShellError::OnlySupportsThisInputType {
exp_input_type: "list, binary, raw data or range".into(),
wrong_type: "null".into(),
dst_span: span,
src_span: span,
}),
other => Ok(PipelineIterator(other)),
}
}

View File

@ -34,8 +34,13 @@ pub enum ShellError {
/// Check the inputs to the operation and add guards for their sizes.
/// Integers are generally of size i64, floats are generally f64.
#[error("Operator overflow.")]
#[diagnostic(code(nu::shell::operator_overflow), help("{2}"))]
OperatorOverflow(String, #[label = "{0}"] Span, String),
#[diagnostic(code(nu::shell::operator_overflow), help("{help}"))]
OperatorOverflow {
msg: String,
#[label = "{msg}"]
span: Span,
help: String,
},
/// The pipelined input into a command was not of the expected type. For example, it might
/// expect a string input, but received a table instead.
@ -45,20 +50,33 @@ pub enum ShellError {
/// Check the relevant pipeline and extract or convert values as needed.
#[error("Pipeline mismatch.")]
#[diagnostic(code(nu::shell::pipeline_mismatch))]
PipelineMismatch(
String,
#[label("expected: {0}")] Span,
#[label("value originates from here")] Span,
),
PipelineMismatch {
exp_input_type: String,
#[label("expected: {exp_input_type}")]
dst_span: Span,
#[label("value originates from here")]
src_span: Span,
},
// TODO: properly unify
/// The pipelined input into a command was not of the expected type. For example, it might
/// expect a string input, but received a table instead.
///
/// (duplicate of [`ShellError::PipelineMismatch`] that reports the observed type)
///
/// ## Resolution
///
/// Check the relevant pipeline and extract or convert values as needed.
#[error("Input type not supported.")]
#[diagnostic(code(nu::shell::only_supports_this_input_type))]
OnlySupportsThisInputType(
String,
String,
#[label("only {0} input data is supported")] Span,
#[label("input type: {1}")] Span,
),
OnlySupportsThisInputType {
exp_input_type: String,
wrong_type: String,
#[label("only {exp_input_type} input data is supported")]
dst_span: Span,
#[label("input type: {wrong_type}")]
src_span: Span,
},
/// No input value was piped into the command.
///
@ -67,8 +85,12 @@ pub enum ShellError {
/// Only use this command to process values from a previous expression.
#[error("Pipeline empty.")]
#[diagnostic(code(nu::shell::pipeline_mismatch))]
PipelineEmpty(#[label("no input value was piped in")] Span),
PipelineEmpty {
#[label("no input value was piped in")]
dst_span: Span,
},
// TODO: remove non type error usages
/// A command received an argument of the wrong type.
///
/// ## Resolution
@ -78,6 +100,7 @@ pub enum ShellError {
#[diagnostic(code(nu::shell::type_mismatch))]
TypeMismatch(String, #[label = "{0}"] Span),
// TODO: merge with `TypeMismatch` as they are currently identical in capability
/// A command received an argument of the wrong type.
///
/// ## Resolution
@ -215,9 +238,14 @@ pub enum ShellError {
/// ## Resolution
///
/// Check to make sure both values are compatible, and that the values are enumerable in Nushell.
#[error("Invalid range {0}..{1}")]
#[error("Invalid range {left_flank}..{right_flank}")]
#[diagnostic(code(nu::shell::invalid_range))]
InvalidRange(String, String, #[label = "expected a valid range"] Span),
InvalidRange {
left_flank: String,
right_flank: String,
#[label = "expected a valid range"]
span: Span,
},
/// Catastrophic nushell failure. This reflects a completely unexpected or unrecoverable error.
///

View File

@ -1996,11 +1996,7 @@ impl Value {
if let Some(val) = lhs.checked_add(*rhs) {
Ok(Value::Int { val, span })
} else {
Err(ShellError::OperatorOverflow(
"add operation overflowed".into(),
span,
"Consider using floating point values for increased range by promoting operand with 'into decimal'. Note: float has reduced precision!".into()
))
Err(ShellError::OperatorOverflow { msg: "add operation overflowed".into(), span, help: "Consider using floating point values for increased range by promoting operand with 'into decimal'. Note: float has reduced precision!".into() })
}
}
(Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Float {
@ -2023,33 +2019,33 @@ impl Value {
if let Some(val) = lhs.checked_add_signed(chrono::Duration::nanoseconds(*rhs)) {
Ok(Value::Date { val, span })
} else {
Err(ShellError::OperatorOverflow(
"addition operation overflowed".into(),
Err(ShellError::OperatorOverflow {
msg: "addition operation overflowed".into(),
span,
"".into(),
))
help: "".into(),
})
}
}
(Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
if let Some(val) = lhs.checked_add(*rhs) {
Ok(Value::Duration { val, span })
} else {
Err(ShellError::OperatorOverflow(
"add operation overflowed".into(),
Err(ShellError::OperatorOverflow {
msg: "add operation overflowed".into(),
span,
"".into(),
))
help: "".into(),
})
}
}
(Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
if let Some(val) = lhs.checked_add(*rhs) {
Ok(Value::Filesize { val, span })
} else {
Err(ShellError::OperatorOverflow(
"add operation overflowed".into(),
Err(ShellError::OperatorOverflow {
msg: "add operation overflowed".into(),
span,
"".into(),
))
help: "".into(),
})
}
}
@ -2110,11 +2106,7 @@ impl Value {
if let Some(val) = lhs.checked_sub(*rhs) {
Ok(Value::Int { val, span })
} else {
Err(ShellError::OperatorOverflow(
"subtraction operation overflowed".into(),
span,
"Consider using floating point values for increased range by promoting operand with 'into decimal'. Note: float has reduced precision!".into()
))
Err(ShellError::OperatorOverflow { msg: "subtraction operation overflowed".into(), span, help: "Consider using floating point values for increased range by promoting operand with 'into decimal'. Note: float has reduced precision!".into() })
}
}
(Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Float {
@ -2135,43 +2127,43 @@ impl Value {
if let Some(v) = result.num_nanoseconds() {
Ok(Value::Duration { val: v, span })
} else {
Err(ShellError::OperatorOverflow(
"subtraction operation overflowed".into(),
Err(ShellError::OperatorOverflow {
msg: "subtraction operation overflowed".into(),
span,
"".into(),
))
help: "".into(),
})
}
}
(Value::Date { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
match lhs.checked_sub_signed(chrono::Duration::nanoseconds(*rhs)) {
Some(val) => Ok(Value::Date { val, span }),
_ => Err(ShellError::OperatorOverflow(
"subtraction operation overflowed".into(),
_ => Err(ShellError::OperatorOverflow {
msg: "subtraction operation overflowed".into(),
span,
"".into(),
)),
help: "".into(),
}),
}
}
(Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
if let Some(val) = lhs.checked_sub(*rhs) {
Ok(Value::Duration { val, span })
} else {
Err(ShellError::OperatorOverflow(
"subtraction operation overflowed".into(),
Err(ShellError::OperatorOverflow {
msg: "subtraction operation overflowed".into(),
span,
"".into(),
))
help: "".into(),
})
}
}
(Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
if let Some(val) = lhs.checked_sub(*rhs) {
Ok(Value::Filesize { val, span })
} else {
Err(ShellError::OperatorOverflow(
"add operation overflowed".into(),
Err(ShellError::OperatorOverflow {
msg: "add operation overflowed".into(),
span,
"".into(),
))
help: "".into(),
})
}
}
@ -2195,11 +2187,7 @@ impl Value {
if let Some(val) = lhs.checked_mul(*rhs) {
Ok(Value::Int { val, span })
} else {
Err(ShellError::OperatorOverflow(
"multiply operation overflowed".into(),
span,
"Consider using floating point values for increased range by promoting operand with 'into decimal'. Note: float has reduced precision!".into()
))
Err(ShellError::OperatorOverflow { msg: "multiply operation overflowed".into(), span, help: "Consider using floating point values for increased range by promoting operand with 'into decimal'. Note: float has reduced precision!".into() })
}
}
(Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Float {
@ -3192,11 +3180,7 @@ impl Value {
if let Some(val) = lhs.checked_pow(*rhs as u32) {
Ok(Value::Int { val, span })
} else {
Err(ShellError::OperatorOverflow(
"pow operation overflowed".into(),
span,
"Consider using floating point values for increased range by promoting operand with 'into decimal'. Note: float has reduced precision!".into()
))
Err(ShellError::OperatorOverflow { msg: "pow operation overflowed".into(), span, help: "Consider using floating point values for increased range by promoting operand with 'into decimal'. Note: float has reduced precision!".into() })
}
}
(Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Float {