Rework operator type errors (#14429)

# Description

This PR adds two new `ParseError` and `ShellError` cases for type errors
relating to operators.
- `OperatorUnsupportedType` is used when a type is not supported by an
operator in any way, shape, or form. E.g., `+` does not support `bool`.
- `OperatorIncompatibleTypes` is used when a operator is used with types
it supports, but the combination of types provided cannot be used
together. E.g., `filesize + duration` is not a valid combination.

The other preexisting error cases related to operators have been removed
and replaced with the new ones above. Namely:

- `ShellError::OperatorMismatch`
- `ShellError::UnsupportedOperator`
- `ParseError::UnsupportedOperationLHS`
- `ParseError::UnsupportedOperationRHS`
- `ParseError::UnsupportedOperationTernary`

# User-Facing Changes

- `help operators` now lists the precedence of `not` as 55 instead of 0
(above the other boolean operators). Fixes #13675.
- `math median` and `math mode` now ignore NaN values so that `[NaN NaN]
| math median` and `[NaN NaN] | math mode` no longer trigger a type
error. Instead, it's now an empty input error. Fixing this in earnest
can be left for a future PR.
- Comparisons with `nan` now return false instead of causing an error.
E.g., `1 == nan` is now `false`.
- All the operator type errors have been standardized and reworked. In
particular, they can now have a help message, which is currently used
for types errors relating to `++`.

```nu
[1] ++ 2
```
```
Error: nu::parser::operator_unsupported_type

  × The '++' operator does not work on values of type 'int'.
   ╭─[entry #1:1:5]
 1 │ [1] ++ 2
   ·     ─┬ ┬
   ·      │ ╰── int
   ·      ╰── does not support 'int'
   ╰────
  help: if you meant to append a value to a list or a record to a table, use the `append` command or wrap the value in a list. For example: `$list ++ $value` should be
        `$list ++ [$value]` or `$list | append $value`.
```
This commit is contained in:
Ian Manske
2025-02-13 04:03:40 +00:00
committed by GitHub
parent 2e1b6acc0e
commit 62e56d3581
32 changed files with 1684 additions and 1772 deletions

View File

@ -7,20 +7,21 @@ mod nu_schema;
mod nu_when;
pub mod utils;
use crate::{Cacheable, PolarsPlugin};
use nu_plugin::EngineInterface;
use nu_protocol::{
ast::Operator, CustomValue, PipelineData, ShellError, Span, Spanned, Type, Value,
};
use std::{cmp::Ordering, fmt};
use uuid::Uuid;
pub use file_type::PolarsFileType;
pub use nu_dataframe::{Axis, Column, NuDataFrame, NuDataFrameCustomValue};
pub use nu_expression::{NuExpression, NuExpressionCustomValue};
pub use nu_lazyframe::{NuLazyFrame, NuLazyFrameCustomValue};
pub use nu_lazygroupby::{NuLazyGroupBy, NuLazyGroupByCustomValue};
use nu_plugin::EngineInterface;
use nu_protocol::{ast::Operator, CustomValue, PipelineData, ShellError, Span, Spanned, Value};
pub use nu_schema::{str_to_dtype, NuSchema};
pub use nu_when::{NuWhen, NuWhenCustomValue, NuWhenType};
use uuid::Uuid;
use crate::{Cacheable, PolarsPlugin};
#[derive(Debug, Clone)]
pub enum PolarsPluginType {
@ -217,13 +218,16 @@ pub trait PolarsPluginCustomValue: CustomValue {
&self,
_plugin: &PolarsPlugin,
_engine: &EngineInterface,
_lhs_span: Span,
lhs_span: Span,
operator: Spanned<Operator>,
_right: Value,
) -> Result<Value, ShellError> {
Err(ShellError::UnsupportedOperator {
operator: operator.item,
span: operator.span,
Err(ShellError::OperatorUnsupportedType {
op: operator.item,
unsupported: Type::Custom(self.type_name().into()),
op_span: operator.span,
unsupported_span: lhs_span,
help: None,
})
}

View File

@ -18,15 +18,15 @@ pub(super) fn between_dataframes(
rhs: &NuDataFrame,
) -> Result<NuDataFrame, ShellError> {
match operator.item {
Operator::Math(Math::Plus) => {
Operator::Math(Math::Add) => {
lhs.append_df(rhs, Axis::Row, Span::merge(left.span(), right.span()))
}
_ => Err(ShellError::OperatorMismatch {
op => Err(ShellError::OperatorUnsupportedType {
op,
unsupported: left.get_type(),
op_span: operator.span,
lhs_ty: left.get_type().to_string(),
lhs_span: left.span(),
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
unsupported_span: left.span(),
help: None,
}),
}
}
@ -40,7 +40,7 @@ pub(super) fn compute_between_series(
) -> Result<NuDataFrame, ShellError> {
let operation_span = Span::merge(left.span(), right.span());
match operator.item {
Operator::Math(Math::Plus) => {
Operator::Math(Math::Add) => {
let mut res = (lhs + rhs).map_err(|e| ShellError::GenericError {
error: format!("Addition error: {e}"),
msg: "".into(),
@ -52,7 +52,7 @@ pub(super) fn compute_between_series(
res.rename(name.into());
NuDataFrame::try_from_series(res, operation_span)
}
Operator::Math(Math::Minus) => {
Operator::Math(Math::Subtract) => {
let mut res = (lhs - rhs).map_err(|e| ShellError::GenericError {
error: format!("Subtraction error: {e}"),
msg: "".into(),
@ -181,12 +181,12 @@ pub(super) fn compute_between_series(
span: operation_span,
}),
},
_ => Err(ShellError::OperatorMismatch {
op => Err(ShellError::OperatorUnsupportedType {
op,
unsupported: left.get_type(),
op_span: operator.span,
lhs_ty: left.get_type().to_string(),
lhs_span: left.span(),
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
unsupported_span: left.span(),
help: None,
}),
}
}
@ -222,12 +222,12 @@ pub(super) fn compute_series_single_value(
right: &Value,
) -> Result<NuDataFrame, ShellError> {
if !lhs.is_series() {
return Err(ShellError::OperatorMismatch {
return Err(ShellError::OperatorUnsupportedType {
op: operator.item,
unsupported: left.get_type(),
op_span: operator.span,
lhs_ty: left.get_type().to_string(),
lhs_span: left.span(),
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
unsupported_span: left.span(),
help: None,
});
}
@ -235,7 +235,7 @@ pub(super) fn compute_series_single_value(
let lhs = lhs.as_series(lhs_span)?;
match operator.item {
Operator::Math(Math::Plus) => match &right {
Operator::Math(Math::Add) => match &right {
Value::Int { val, .. } => {
compute_series_i64(&lhs, *val, <ChunkedArray<Int64Type>>::add, lhs_span)
}
@ -243,27 +243,27 @@ pub(super) fn compute_series_single_value(
compute_series_float(&lhs, *val, <ChunkedArray<Float64Type>>::add, lhs_span)
}
Value::String { val, .. } => add_string_to_series(&lhs, val, lhs_span),
_ => Err(ShellError::OperatorMismatch {
_ => Err(ShellError::OperatorUnsupportedType {
op: operator.item,
unsupported: right.get_type(),
op_span: operator.span,
lhs_ty: left.get_type().to_string(),
lhs_span: left.span(),
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
unsupported_span: right.span(),
help: None,
}),
},
Operator::Math(Math::Minus) => match &right {
Operator::Math(Math::Subtract) => match &right {
Value::Int { val, .. } => {
compute_series_i64(&lhs, *val, <ChunkedArray<Int64Type>>::sub, lhs_span)
}
Value::Float { val, .. } => {
compute_series_float(&lhs, *val, <ChunkedArray<Float64Type>>::sub, lhs_span)
}
_ => Err(ShellError::OperatorMismatch {
_ => Err(ShellError::OperatorUnsupportedType {
op: operator.item,
unsupported: right.get_type(),
op_span: operator.span,
lhs_ty: left.get_type().to_string(),
lhs_span: left.span(),
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
unsupported_span: right.span(),
help: None,
}),
},
Operator::Math(Math::Multiply) => match &right {
@ -273,12 +273,12 @@ pub(super) fn compute_series_single_value(
Value::Float { val, .. } => {
compute_series_float(&lhs, *val, <ChunkedArray<Float64Type>>::mul, lhs_span)
}
_ => Err(ShellError::OperatorMismatch {
_ => Err(ShellError::OperatorUnsupportedType {
op: operator.item,
unsupported: right.get_type(),
op_span: operator.span,
lhs_ty: left.get_type().to_string(),
lhs_span: left.span(),
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
unsupported_span: right.span(),
help: None,
}),
},
Operator::Math(Math::Divide) => {
@ -298,12 +298,12 @@ pub(super) fn compute_series_single_value(
compute_series_float(&lhs, *val, <ChunkedArray<Float64Type>>::div, lhs_span)
}
}
_ => Err(ShellError::OperatorMismatch {
_ => Err(ShellError::OperatorUnsupportedType {
op: operator.item,
unsupported: right.get_type(),
op_span: operator.span,
lhs_ty: left.get_type().to_string(),
lhs_span: left.span(),
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
unsupported_span: right.span(),
help: None,
}),
}
}
@ -319,12 +319,12 @@ pub(super) fn compute_series_single_value(
Value::Date { val, .. } => {
compare_series_i64(&lhs, val.timestamp_millis(), ChunkedArray::equal, lhs_span)
}
_ => Err(ShellError::OperatorMismatch {
_ => Err(ShellError::OperatorUnsupportedType {
op: operator.item,
unsupported: right.get_type(),
op_span: operator.span,
lhs_ty: left.get_type().to_string(),
lhs_span: left.span(),
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
unsupported_span: right.span(),
help: None,
}),
},
Operator::Comparison(Comparison::NotEqual) => match &right {
@ -344,12 +344,12 @@ pub(super) fn compute_series_single_value(
ChunkedArray::not_equal,
lhs_span,
),
_ => Err(ShellError::OperatorMismatch {
_ => Err(ShellError::OperatorUnsupportedType {
op: operator.item,
unsupported: right.get_type(),
op_span: operator.span,
lhs_ty: left.get_type().to_string(),
lhs_span: left.span(),
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
unsupported_span: right.span(),
help: None,
}),
},
Operator::Comparison(Comparison::LessThan) => match &right {
@ -360,12 +360,12 @@ pub(super) fn compute_series_single_value(
Value::Date { val, .. } => {
compare_series_i64(&lhs, val.timestamp_millis(), ChunkedArray::lt, lhs_span)
}
_ => Err(ShellError::OperatorMismatch {
_ => Err(ShellError::OperatorUnsupportedType {
op: operator.item,
unsupported: right.get_type(),
op_span: operator.span,
lhs_ty: left.get_type().to_string(),
lhs_span: left.span(),
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
unsupported_span: right.span(),
help: None,
}),
},
Operator::Comparison(Comparison::LessThanOrEqual) => match &right {
@ -376,12 +376,12 @@ pub(super) fn compute_series_single_value(
Value::Date { val, .. } => {
compare_series_i64(&lhs, val.timestamp_millis(), ChunkedArray::lt_eq, lhs_span)
}
_ => Err(ShellError::OperatorMismatch {
_ => Err(ShellError::OperatorUnsupportedType {
op: operator.item,
unsupported: right.get_type(),
op_span: operator.span,
lhs_ty: left.get_type().to_string(),
lhs_span: left.span(),
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
unsupported_span: right.span(),
help: None,
}),
},
Operator::Comparison(Comparison::GreaterThan) => match &right {
@ -392,12 +392,12 @@ pub(super) fn compute_series_single_value(
Value::Date { val, .. } => {
compare_series_i64(&lhs, val.timestamp_millis(), ChunkedArray::gt, lhs_span)
}
_ => Err(ShellError::OperatorMismatch {
_ => Err(ShellError::OperatorUnsupportedType {
op: operator.item,
unsupported: right.get_type(),
op_span: operator.span,
lhs_ty: left.get_type().to_string(),
lhs_span: left.span(),
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
unsupported_span: right.span(),
help: None,
}),
},
Operator::Comparison(Comparison::GreaterThanOrEqual) => match &right {
@ -408,23 +408,23 @@ pub(super) fn compute_series_single_value(
Value::Date { val, .. } => {
compare_series_i64(&lhs, val.timestamp_millis(), ChunkedArray::gt_eq, lhs_span)
}
_ => Err(ShellError::OperatorMismatch {
_ => Err(ShellError::OperatorUnsupportedType {
op: operator.item,
unsupported: right.get_type(),
op_span: operator.span,
lhs_ty: left.get_type().to_string(),
lhs_span: left.span(),
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
unsupported_span: right.span(),
help: None,
}),
},
// TODO: update this to do a regex match instead of a simple contains?
Operator::Comparison(Comparison::RegexMatch) => match &right {
Value::String { val, .. } => contains_series_pat(&lhs, val, lhs_span),
_ => Err(ShellError::OperatorMismatch {
_ => Err(ShellError::OperatorUnsupportedType {
op: operator.item,
unsupported: right.get_type(),
op_span: operator.span,
lhs_ty: left.get_type().to_string(),
lhs_span: left.span(),
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
unsupported_span: right.span(),
help: None,
}),
},
Operator::Comparison(Comparison::StartsWith) => match &right {
@ -432,12 +432,12 @@ pub(super) fn compute_series_single_value(
let starts_with_pattern = format!("^{}", fancy_regex::escape(val));
contains_series_pat(&lhs, &starts_with_pattern, lhs_span)
}
_ => Err(ShellError::OperatorMismatch {
_ => Err(ShellError::OperatorUnsupportedType {
op: operator.item,
unsupported: right.get_type(),
op_span: operator.span,
lhs_ty: left.get_type().to_string(),
lhs_span: left.span(),
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
unsupported_span: right.span(),
help: None,
}),
},
Operator::Comparison(Comparison::EndsWith) => match &right {
@ -445,20 +445,20 @@ pub(super) fn compute_series_single_value(
let ends_with_pattern = format!("{}$", fancy_regex::escape(val));
contains_series_pat(&lhs, &ends_with_pattern, lhs_span)
}
_ => Err(ShellError::OperatorMismatch {
_ => Err(ShellError::OperatorUnsupportedType {
op: operator.item,
unsupported: right.get_type(),
op_span: operator.span,
lhs_ty: left.get_type().to_string(),
lhs_span: left.span(),
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
unsupported_span: right.span(),
help: None,
}),
},
_ => Err(ShellError::OperatorMismatch {
_ => Err(ShellError::OperatorUnsupportedType {
op: operator.item,
unsupported: left.get_type(),
op_span: operator.span,
lhs_ty: left.get_type().to_string(),
lhs_span: left.span(),
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
unsupported_span: left.span(),
help: None,
}),
}
}

View File

@ -81,14 +81,14 @@ fn with_operator(
left: &NuExpression,
right: &NuExpression,
lhs_span: Span,
rhs_span: Span,
_rhs_span: Span,
op_span: Span,
) -> Result<Value, ShellError> {
match operator {
Operator::Math(Math::Plus) => {
Operator::Math(Math::Add) => {
apply_arithmetic(plugin, engine, left, right, lhs_span, Add::add)
}
Operator::Math(Math::Minus) => {
Operator::Math(Math::Subtract) => {
apply_arithmetic(plugin, engine, left, right, lhs_span, Sub::sub)
}
Operator::Math(Math::Multiply) => {
@ -100,7 +100,7 @@ fn with_operator(
Operator::Math(Math::Modulo) => {
apply_arithmetic(plugin, engine, left, right, lhs_span, Rem::rem)
}
Operator::Math(Math::FloorDivision) => {
Operator::Math(Math::FloorDivide) => {
apply_arithmetic(plugin, engine, left, right, lhs_span, Div::div)
}
Operator::Comparison(Comparison::Equal) => Ok(left
@ -133,12 +133,12 @@ fn with_operator(
.apply_with_expr(right.clone(), Expr::lt_eq)
.cache(plugin, engine, lhs_span)?
.into_value(lhs_span)),
_ => Err(ShellError::OperatorMismatch {
op => Err(ShellError::OperatorUnsupportedType {
op,
unsupported: Type::Custom(TYPE_NAME.into()),
op_span,
lhs_ty: Type::Custom(TYPE_NAME.into()).to_string(),
lhs_span,
rhs_ty: Type::Custom(TYPE_NAME.into()).to_string(),
rhs_span,
unsupported_span: lhs_span,
help: None,
}),
}
}