Skip to content

Type error variants in ShellError need harmonization. #10698

@sholderbach

Description

@sholderbach

Our ShellError contains many similar variants that generally serve the purpose to indicate that the provided type is not satisfying the requirements in a particular context. We inconsistently use some of those variants for the very same purpose which makes the quality of the error experience inconsistent. Furthermore folks have tried to make the error display better for some individual cases but assumptions how the data stored in the particular ShellError variant get's turned into a miette::Diagnostic might not hold if format strings are tweaked here and there or additional information is added in some cases. We thus have a lot of nonsensical errors lurking around.

With this issue I want to first focus on ShellError, but there are also typecheck like operations currently happening at parse-time. Thus ParseError has similar variants.
We should reconcile them in a follow up step.

Desired information for a type error

  • Observed type (this significantly simplifies the work for the user to track down what may be going wrong)
  • Expected type
    • This can potentially be more than a single type.
    • Some commands may have the desire to specify more than strictly the type (e.g. range of values, sign, encoding).
  • Span pointing to the thing that has the wrong type (if available)
    • Now every Value should have a span. In some cases we may still need to workshop what provides the most useful span in a particular situation.
  • Span pointing to the thing imposing the particular type requirement.
    • This could be a command, type annotation in the code (this is currently probably served by the ParseError side of things), operator, a particular flag with a restriction not expressable at parse time.

Current overlapping type-error-like errors

  • OperatorMismatch
    • Specific for operators (as it is ambiguous whether lhs or rhs takes precedence)
    • includes the types
    • fully spanned/labeled
  • PipelineMismatch
    • only expected
    • not sure if we need a specific version for pipelines (PipelineData does not provide a span for all its variants!)
  • OnlySupportsThisInputType
    • uses input type specific language (i.e. pipelining)
    • expected/observed type
    • spanned
    • label: "only {exp_input_type} input data is supported" (Check if someone overuses the variable field)
    • better than PipelineMismatch
    • pretty long name
  • PipelineEmpty
    • Not a full type error but similar to requiring that the input type is not nothing (this could be either from a Value::Nothing or a PipelineData::Empty/start position of a pipeline)
  • TypeMismatch
    • The prototypical name, but...
    • only free form message
    • single span with unclear responsibility
    • In the codebase used for things that are really not down to a type error!
  • NotAList
    • specifically only used to fail when numeric indexing fails because the Value is not a List(or also Table)
    • has both spans
    • misses observed type, mentions the explicit type as list
    • maybe could be simplified or additional context could be provided.
  • UnsupportedInput
    • Tuple struct variant: meaning of the fields is unclear at the construction site
    • second span gets labeled with: "input type: {1}"
      • so appears type specific
    • Currently also used for non type purposes as well
      • separate out the value specific errors
    • 2 strings, 2 spans

Closely related errors that are not strict type errors.

They probably can NOT be subsumed with one type error to rule them all but would benefit from verifying that we do not misuse them or should provide at least similarly helpful information as described above.

  • IncompatibleParametersSingle
    • only sounds similar, no type related misuse present
    • intended for incompatible usages of params/flags
  • CantConvert
    • Maybe worth separating when a type conversion fails because the type has no possible cast or if the cast failed because of a particular illegal value
    • mentions from and to type
    • details can be provided in the help
  • DowncastNotPossible
  • CannotCreateRange
    • in the creation of ranges we fail to that, we don't specify if this is down to the types or incompatibility of values.
    • span possibly a bit unspecific
Current source code for mentioned variants

This is only an excerpt of the actual ShellError enum

pub enum ShellError {
    /// An operator received two arguments of incompatible types.
    ///
    /// ## Resolution
    ///
    /// Check each argument's type and convert one or both as needed.
    #[error("Type mismatch during operation.")]
    #[diagnostic(code(nu::shell::type_mismatch))]
    OperatorMismatch {
        #[label = "type mismatch for operator"]
        op_span: Span,
        lhs_ty: String,
        #[label("{lhs_ty}")]
        lhs_span: Span,
        rhs_ty: String,
        #[label("{rhs_ty}")]
        rhs_span: Span,
    },

    /// 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.
    ///
    /// ## Resolution
    ///
    /// Check the relevant pipeline and extract or convert values as needed.
    #[error("Pipeline mismatch.")]
    #[diagnostic(code(nu::shell::pipeline_mismatch))]
    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 {
        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.
    ///
    /// ## Resolution
    ///
    /// 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")]
        dst_span: Span,
    },

    // TODO: remove non type error usages
    /// A command received an argument of the wrong type.
    ///
    /// ## Resolution
    ///
    /// Convert the argument type before passing it in, or change the command to accept the type.
    #[error("Type mismatch.")]
    #[diagnostic(code(nu::shell::type_mismatch))]
    TypeMismatch {
        err_message: String,
        #[label = "{err_message}"]
        span: Span,
    },
    /// An operation received parameters with some sort of incompatibility
    /// (for example, different number of rows in a table, incompatible column names, etc).
    ///
    /// ## Resolution
    ///
    /// Refer to the specific error message for details on what's incompatible and then fix your
    /// inputs to make sure they match that way.
    #[error("Incompatible parameters.")]
    #[diagnostic(code(nu::shell::incompatible_parameters))]
    IncompatibleParametersSingle {
        msg: String,
        #[label = "{msg}"]
        span: Span,
    },

    /// Failed to convert a value of one type into a different type.
    ///
    /// ## Resolution
    ///
    /// Not all values can be coerced this way. Check the supported type(s) and try again.
    #[error("Can't convert to {to_type}.")]
    #[diagnostic(code(nu::shell::cant_convert))]
    CantConvert {
        to_type: String,
        from_type: String,
        #[label("can't convert {from_type} to {to_type}")]
        span: Span,
        #[help]
        help: Option<String>,
    },

    /// An error happened while tryin to create a range.
    ///
    /// This can happen in various unexpected situations, for example if the range would loop forever (as would be the case with a 0-increment).
    ///
    /// ## Resolution
    ///
    /// Check your range values to make sure they're countable and would not loop forever.
    #[error("Can't convert range to countable values")]
    #[diagnostic(code(nu::shell::range_to_countable))]
    CannotCreateRange {
        #[label = "can't convert to countable values"]
        span: Span,
    },

    /// The given operation can only be performed on lists.
    ///
    /// ## Resolution
    ///
    /// Check the input type to this command. Are you sure it's a list?
    #[error("Not a list value")]
    #[diagnostic(code(nu::shell::not_a_list))]
    NotAList {
        #[label = "value not a list"]
        dst_span: Span,
        #[label = "value originates here"]
        src_span: Span,
    },

    /// An operation was attempted with an input unsupported for some reason.
    ///
    /// ## Resolution
    ///
    /// This error is fairly generic. Refer to the specific error message for further details.
    #[error("Unsupported input")]
    #[diagnostic(code(nu::shell::unsupported_input))]
    UnsupportedInput(
        String,
        String,
        #[label("{0}")] Span, // call head (the name of the command itself)
        #[label("input type: {1}")] Span,
    ),

    /// A custom value could not be converted to a Dataframe.
    ///
    /// ## Resolution
    ///
    /// Make sure conversion to a Dataframe is possible for this value or convert it to a type that does, first.
    #[error("Casting error")]
    #[diagnostic(code(nu::shell::downcast_not_possible))]
    DowncastNotPossible(String, #[label("{0}")] Span),
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    A:error-unhelpfulThe error message you observe is not helpful to identify the problemcategory:meta-issueAn issue that tracks other issueshelp wantedstatus: Extra attention is needed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions