diff --git a/crates/nu-protocol/src/shell_error.rs b/crates/nu-protocol/src/shell_error.rs index f72a8a93e0..49aaeeb727 100644 --- a/crates/nu-protocol/src/shell_error.rs +++ b/crates/nu-protocol/src/shell_error.rs @@ -9,6 +9,11 @@ use crate::{ast::Operator, Span, Type}; /// and pass it into an error viewer to display to the user. #[derive(Debug, Clone, Error, Diagnostic, Serialize, Deserialize)] 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), url(docsrs))] OperatorMismatch { @@ -22,10 +27,22 @@ pub enum ShellError { rhs_span: Span, }, + /// An arithmetic operation's resulting value overflowed its possible size. + /// + /// ## Resolution + /// + /// 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), url(docsrs))] OperatorOverflow(String, #[label = "{0}"] 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), url(docsrs))] PipelineMismatch( @@ -34,27 +51,53 @@ pub enum ShellError { #[label("value originates from here")] Span, ), + /// 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), url(docsrs))] TypeMismatch(String, #[label = "needs {0}"] Span), + /// This value cannot be used with this operator. + /// + /// ## Resolution + /// + /// Not all values, for example custom values, can be used with all operators. Either + /// implement support for the operator on this type, or convert the type to a supported one. #[error("Unsupported operator: {0}.")] #[diagnostic(code(nu::shell::unsupported_operator), url(docsrs))] UnsupportedOperator(Operator, #[label = "unsupported operator"] Span), - #[error("Unsupported operator: {0}.")] + /// An operator was not recognized during evaluation. + /// + /// ## Resolution + /// + /// Did you write the correct operator? + #[error("Unknown operator: {0}.")] #[diagnostic(code(nu::shell::unknown_operator), url(docsrs))] - UnknownOperator(String, #[label = "unsupported operator"] Span), + UnknownOperator(String, #[label = "unknown operator"] Span), + /// An expected command parameter is missing. + /// + /// ## Resolution + /// + /// Add the expected parameter and try again. #[error("Missing parameter: {0}.")] #[diagnostic(code(nu::shell::missing_parameter), url(docsrs))] MissingParameter(String, #[label = "missing parameter: {0}"] Span), - // Be cautious, as flags can share the same span, resulting in a panic (ex: `rm -pt`) + /// Two parameters conflict with each other or are otherwise mutually exclusive. + /// + /// ## Resolution + /// + /// Remove one of the parameters/options and try again. #[error("Incompatible parameters.")] #[diagnostic(code(nu::shell::incompatible_parameters), url(docsrs))] IncompatibleParameters { left_message: String, + // Be cautious, as flags can share the same span, resulting in a panic (ex: `rm -pt`) #[label("{left_message}")] left_span: Span, right_message: String, @@ -62,59 +105,126 @@ pub enum ShellError { right_span: Span, }, + /// There's some issue with number or matching of delimiters in an expression. + /// + /// ## Resolution + /// + /// Check your syntax for mismatched braces, RegExp syntax errors, etc, based on the specific error message. #[error("Delimiter error")] #[diagnostic(code(nu::shell::delimiter_error), url(docsrs))] DelimiterError(String, #[label("{0}")] 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), url(docsrs))] IncompatibleParametersSingle(String, #[label = "{0}"] Span), + /// This build of nushell implements this feature, but it has not been enabled. + /// + /// ## Resolution + /// + /// Rebuild nushell with the appropriate feature enabled. #[error("Feature not enabled.")] #[diagnostic(code(nu::shell::feature_not_enabled), url(docsrs))] FeatureNotEnabled(#[label = "feature not enabled"] Span), + /// You're trying to run an unsupported external command. + /// + /// ## Resolution + /// + /// Make sure there's an appropriate `run-external` declaration for this external command. #[error("Running external commands not supported")] #[diagnostic(code(nu::shell::external_commands), url(docsrs))] ExternalNotSupported(#[label = "external not supported"] Span), + /// The given probability input is invalid. The probability must be between 0 and 1. + /// + /// ## Resolution + /// + /// Make sure the probability is between 0 and 1 and try again. #[error("Invalid Probability.")] #[diagnostic(code(nu::shell::invalid_probability), url(docsrs))] InvalidProbability(#[label = "invalid probability"] Span), + /// The first value in a `..` range must be compatible with the second one. + /// + /// ## Resolution + /// + /// Check to make sure both values are compatible, and that the values are enumerable in Nushell. #[error("Invalid range {0}..{1}")] #[diagnostic(code(nu::shell::invalid_range), url(docsrs))] InvalidRange(String, String, #[label = "expected a valid range"] Span), - // Only use this one if we Nushell completely falls over and hits a state that isn't possible or isn't recoverable + /// Catastrophic nushell failure. This reflects a completely unexpected or unrecoverable error. + /// + /// ## Resolution + /// + /// It is very likely that this is a bug. Please file an issue at https://github.com/nushell/nushell/issues with relevant information. #[error("Nushell failed: {0}.")] #[diagnostic(code(nu::shell::nushell_failed), url(docsrs))] + // Only use this one if we Nushell completely falls over and hits a state that isn't possible or isn't recoverable NushellFailed(String), - // Only use this one if we Nushell completely falls over and hits a state that isn't possible or isn't recoverable + /// Catastrophic nushell failure. This reflects a completely unexpected or unrecoverable error. + /// + /// ## Resolution + /// + /// It is very likely that this is a bug. Please file an issue at https://github.com/nushell/nushell/issues with relevant information. #[error("Nushell failed: {0}.")] #[diagnostic(code(nu::shell::nushell_failed), url(docsrs))] + // Only use this one if we Nushell completely falls over and hits a state that isn't possible or isn't recoverable NushellFailedSpanned(String, String, #[label = "{1}"] Span), + /// A referenced variable was not found at runtime. + /// + /// ## Resolution + /// + /// Check the variable name. Did you typo it? Did you forget to declare it? Is the casing right? #[error("Variable not found")] #[diagnostic(code(nu::shell::variable_not_found), url(docsrs))] VariableNotFoundAtRuntime(#[label = "variable not found"] Span), + /// A referenced environment variable was not found at runtime. + /// + /// ## Resolution + /// + /// Check the environment variable name. Did you typo it? Did you forget to declare it? Is the casing right? #[error("Environment variable '{0}' not found")] #[diagnostic(code(nu::shell::env_variable_not_found), url(docsrs))] EnvVarNotFoundAtRuntime(String, #[label = "environment variable not found"] Span), + /// The given item was not found. This is a fairly generic error that depends on context. + /// + /// ## Resolution + /// + /// This error is triggered in various places, and simply signals that "something" was not found. Refer to the specific error message for further details. #[error("Not found.")] #[diagnostic(code(nu::parser::not_found), url(docsrs))] NotFound(#[label = "did not find anything under this name"] 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 {0}.")] #[diagnostic(code(nu::shell::cant_convert), url(docsrs))] CantConvert(String, String, #[label("can't convert {1} to {0}")] Span), - // Identical to above, but with help + /// 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, referring to the help text in the specific error message. #[error("Can't convert to {0}.")] #[diagnostic(code(nu::shell::cant_convert), url(docsrs), help("{3}"))] + // Identical to above, but with help CantConvertWithHelp( String, String, @@ -122,6 +232,11 @@ pub enum ShellError { String, ), + /// An environment variable cannot be represented as a string. + /// + /// ## Resolution + /// + /// Not all types can be converted to environment variable values, which must be strings. Check the input type and try again. #[error("{0} is not representable as a string.")] #[diagnostic( code(nu::shell::env_var_not_a_string), @@ -133,26 +248,58 @@ Either make sure {0} is a string, or add a 'to_string' entry for it in ENV_CONVE )] EnvVarNotAString(String, #[label("value not representable as a string")] Span), + /// Division by zero is not a thing. + /// + /// ## Resolution + /// + /// Add a guard of some sort to check whether a denominator input to this division is zero, and branch off if that's the case. #[error("Division by zero.")] #[diagnostic(code(nu::shell::division_by_zero), url(docsrs))] DivisionByZero(#[label("division by zero")] Span), + /// 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), url(docsrs))] CannotCreateRange(#[label = "can't convert to countable values"] Span), + /// You attempted to access an index beyond the available length of a value. + /// + /// ## Resolution + /// + /// Check your lengths and try again. #[error("Row number too large (max: {0}).")] #[diagnostic(code(nu::shell::access_beyond_end), url(docsrs))] AccessBeyondEnd(usize, #[label = "index too large (max: {0})"] Span), + /// You attempted to access an index beyond the available length of a stream. + /// + /// ## Resolution + /// + /// Check your lengths and try again. #[error("Row number too large.")] #[diagnostic(code(nu::shell::access_beyond_end_of_stream), url(docsrs))] AccessBeyondEndOfStream(#[label = "index too large"] Span), + /// Tried to index into a type that does not support pathed access. + /// + /// ## Resolution + /// + /// Check your types. Only composite types can be pathed into. #[error("Data cannot be accessed with a cell path")] #[diagnostic(code(nu::shell::incompatible_path_access), url(docsrs))] IncompatiblePathAccess(String, #[label("{0} doesn't support cell paths")] Span), + /// The requested column does not exist. + /// + /// ## Resolution + /// + /// Check the spelling of your column name. Did you forget to rename a column somewhere? #[error("Cannot find column")] #[diagnostic(code(nu::shell::column_not_found), url(docsrs))] CantFindColumn( @@ -160,6 +307,11 @@ Either make sure {0} is a string, or add a 'to_string' entry for it in ENV_CONVE #[label = "value originates here"] Span, ), + /// Attempted to insert a column into a table, but a column with that name already exists. + /// + /// ## Resolution + /// + /// Drop or rename the existing column (check `rename -h`) and try again. #[error("Column already exists")] #[diagnostic(code(nu::shell::column_already_exists), url(docsrs))] ColumnAlreadyExists( @@ -167,6 +319,11 @@ Either make sure {0} is a string, or add a 'to_string' entry for it in ENV_CONVE #[label = "value originates here"] 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), url(docsrs))] NotAList( @@ -174,14 +331,38 @@ Either make sure {0} is a string, or add a 'to_string' entry for it in ENV_CONVE #[label = "value originates here"] Span, ), + /// An error happened while performing an external command. + /// + /// ## Resolution + /// + /// This error is fairly generic. Refer to the specific error message for further details. #[error("External command")] #[diagnostic(code(nu::shell::external_command), url(docsrs), help("{1}"))] ExternalCommand(String, String, #[label("{0}")] 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), url(docsrs))] UnsupportedInput(String, #[label("{0}")] Span), + /// Failed to parse an input into a datetime value. + /// + /// ## Resolution + /// + /// Make sure your datetime input format is correct. + /// + /// For example, these are some valid formats: + /// + /// * "5 pm" + /// * "2020/12/4" + /// * "2020.12.04 22:10 +2" + /// * "2020-04-12 22:10:57 +02:00" + /// * "2020-04-12T22:10:57.213231+02:00" + /// * "Tue, 1 Jul 2003 10:52:37 +0200""# #[error("Unable to parse datetime")] #[diagnostic( code(nu::shell::datetime_parse_error), @@ -198,58 +379,127 @@ Either make sure {0} is a string, or add a 'to_string' entry for it in ENV_CONVE )] DatetimeParseError(#[label("datetime parsing failed")] Span), + /// A network operation failed. + /// + /// ## Resolution + /// + /// It's always DNS. #[error("Network failure")] #[diagnostic(code(nu::shell::network_failure), url(docsrs))] NetworkFailure(String, #[label("{0}")] Span), + /// Help text for this command could not be found. + /// + /// ## Resolution + /// + /// Check the spelling for the requested command and try again. Are you sure it's defined and your configurations are loading correctly? Can you execute it? #[error("Command not found")] #[diagnostic(code(nu::shell::command_not_found), url(docsrs))] CommandNotFound(#[label("command not found")] Span), + /// A flag was not found. #[error("Flag not found")] #[diagnostic(code(nu::shell::flag_not_found), url(docsrs))] + // NOTE: Seems to be unused. Removable? FlagNotFound(String, #[label("{0} not found")] Span), + /// Failed to find a file during a nushell operation. + /// + /// ## Resolution + /// + /// Does the file in the error message exist? Is it readable and accessible? Is the casing right? #[error("File not found")] #[diagnostic(code(nu::shell::file_not_found), url(docsrs))] FileNotFound(#[label("file not found")] Span), + /// Failed to find a file during a nushell operation. + /// + /// ## Resolution + /// + /// Does the file in the error message exist? Is it readable and accessible? Is the casing right? #[error("File not found")] #[diagnostic(code(nu::shell::file_not_found), url(docsrs))] FileNotFoundCustom(String, #[label("{0}")] Span), + /// A plugin failed to load. + /// + /// ## Resolution + /// + /// This is a failry generic error. Refer to the specific error message for further details. #[error("Plugin failed to load: {0}")] #[diagnostic(code(nu::shell::plugin_failed_to_load), url(docsrs))] PluginFailedToLoad(String), + /// A message from a plugin failed to encode. + /// + /// ## Resolution + /// + /// This is likely a bug with the plugin itself. #[error("Plugin failed to encode: {0}")] #[diagnostic(code(nu::shell::plugin_failed_to_encode), url(docsrs))] PluginFailedToEncode(String), + /// A message to a plugin failed to decode. + /// + /// ## Resolution + /// + /// This is either an issue with the inputs to a plugin (bad JSON?) or a bug in the plugin itself. Fix or report as appropriate. #[error("Plugin failed to decode: {0}")] #[diagnostic(code(nu::shell::plugin_failed_to_decode), url(docsrs))] PluginFailedToDecode(String), + /// An I/O operation failed. + /// + /// ## Resolution + /// + /// This is a generic error. Refer to the specific error message for further details. #[error("I/O error")] #[diagnostic(code(nu::shell::io_error), url(docsrs), help("{0}"))] IOError(String), + /// Tried to `cd` to a path that isn't a directory. + /// + /// ## Resolution + /// + /// Make sure the path is a directory. It currently exists, but is of some other type, like a file. #[error("Cannot change to directory")] #[diagnostic(code(nu::shell::cannot_cd_to_directory), url(docsrs))] NotADirectory(#[label("is not a directory")] Span), + /// Attempted to perform an operation on a directory that doesn't exist. + /// + /// ## Resolution + /// + /// Make sure the directory in the error message actually exists before trying again. #[error("Directory not found")] #[diagnostic(code(nu::shell::directory_not_found), url(docsrs))] DirectoryNotFound(#[label("directory not found")] Span), + /// Attempted to perform an operation on a directory that doesn't exist. + /// + /// ## Resolution + /// + /// Make sure the directory in the error message actually exists before trying again. #[error("Directory not found")] #[diagnostic(code(nu::shell::directory_not_found_custom), url(docsrs))] DirectoryNotFoundCustom(String, #[label("{0}")] Span), + /// Attempted to perform an operation on a directory that doesn't exist. + /// + /// ## Resolution + /// + /// Make sure the directory in the error message actually exists before trying again. #[error("Directory not found")] #[diagnostic(code(nu::shell::directory_not_found_help), url(docsrs), help("{1}"))] DirectoryNotFoundHelp(#[label("directory not found")] Span, String), + /// The requested move operation cannot be completed. This is typically because both paths exist, + /// but are of different types. For example, you might be trying to overwrite an existing file with + /// a directory. + /// + /// ## Resolution + /// + /// Make sure the destination path does not exist before moving a directory. #[error("Move not possible")] #[diagnostic(code(nu::shell::move_not_possible), url(docsrs))] MoveNotPossible { @@ -261,26 +511,52 @@ Either make sure {0} is a string, or add a 'to_string' entry for it in ENV_CONVE destination_span: Span, }, + /// The requested move operation cannot be completed. This is typically because both paths exist, + /// but are of different types. For example, you might be trying to overwrite an existing file with + /// a directory. + /// + /// ## Resolution + /// + /// Make sure the destination path does not exist before moving a directory. #[error("Move not possible")] #[diagnostic(code(nu::shell::move_not_possible_single), url(docsrs))] + // NOTE: Currently not actively used. MoveNotPossibleSingle(String, #[label("{0}")] Span), + /// Failed to create either a file or directory. + /// + /// ## Resolution + /// + /// This is a fairly generic error. Refer to the specific error message for further details. #[error("Create not possible")] #[diagnostic(code(nu::shell::create_not_possible), url(docsrs))] CreateNotPossible(String, #[label("{0}")] Span), + /// Changing the access time ("atime") of this file is not possible. + /// + /// ## Resolution + /// + /// This can be for various reasons, such as your platform or permission flags. Refer to the specific error message for more details. #[error("Not possible to change the access time")] #[diagnostic(code(nu::shell::change_access_time_not_possible), url(docsrs))] ChangeAccessTimeNotPossible(String, #[label("{0}")] Span), + /// Changing the modification time ("mtime") of this file is not possible. + /// + /// ## Resolution + /// + /// This can be for various reasons, such as your platform or permission flags. Refer to the specific error message for more details. #[error("Not possible to change the modified time")] #[diagnostic(code(nu::shell::change_modified_time_not_possible), url(docsrs))] ChangeModifiedTimeNotPossible(String, #[label("{0}")] Span), + /// Unable to remove this item. #[error("Remove not possible")] #[diagnostic(code(nu::shell::remove_not_possible), url(docsrs))] + // NOTE: Currently unused. Remove? RemoveNotPossible(String, #[label("{0}")] Span), + // These three are unused. Remove? #[error("No file to be removed")] NoFileToBeRemoved(), #[error("No file to be moved")] @@ -288,38 +564,71 @@ Either make sure {0} is a string, or add a 'to_string' entry for it in ENV_CONVE #[error("No file to be copied")] NoFileToBeCopied(), + /// A name was not found. Did you mean a different name? + /// + /// ## Resolution + /// + /// The error message will suggest a possible match for what you meant. #[error("Name not found")] #[diagnostic(code(nu::shell::name_not_found), url(docsrs))] DidYouMean(String, #[label("did you mean '{0}'?")] Span), + /// The given input must be valid UTF-8 for further processing. + /// + /// ## Resolution + /// + /// Check your input's encoding. Are there any funny characters/bytes? #[error("Non-UTF8 string")] #[diagnostic(code(nu::parser::non_utf8), url(docsrs))] NonUtf8(#[label = "non-UTF8 string"] 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), url(docsrs))] DowncastNotPossible(String, #[label("{0}")] Span), + /// The value given for this configuration is not supported. + /// + /// ## Resolution + /// + /// Refer to the specific error message for details and convert values as needed. #[error("Unsupported config value")] #[diagnostic(code(nu::shell::unsupported_config_value), url(docsrs))] UnsupportedConfigValue(String, String, #[label = "expected {0}, got {1}"] Span), + /// An expected configuration value is not present. + /// + /// ## Resolution + /// + /// Refer to the specific error message and add the configuration value to your config file as needed. #[error("Missing config value")] #[diagnostic(code(nu::shell::missing_config_value), url(docsrs))] MissingConfigValue(String, #[label = "missing {0}"] Span), + /// Negative value passed when positive ons is required. + /// + /// ## Resolution + /// + /// Guard against negative values or check your inputs. #[error("Negative value passed when positive one is required")] #[diagnostic(code(nu::shell::needs_positive_value), url(docsrs))] NeedsPositiveValue(#[label = "use a positive value"] Span), + /// This is a generic error type used for different situations. #[error("{0}")] #[diagnostic()] SpannedLabeledError(String, String, #[label("{1}")] Span), + /// This is a generic error type used for different situations. #[error("{0}")] #[diagnostic(help("{3}"))] SpannedLabeledErrorHelp(String, String, #[label("{1}")] Span, String), + /// This is a generic error type used for different situations. #[error("{0}")] #[diagnostic()] SpannedLabeledErrorRelated( @@ -329,17 +638,25 @@ Either make sure {0} is a string, or add a 'to_string' entry for it in ENV_CONVE #[related] Vec, ), + /// This is a generic error type used for different situations. #[error("{0}")] #[diagnostic(help("{1}"))] LabeledError(String, String), + /// This is a generic error type used for different situations. #[error("{0}")] UnlabeledError(String), + /// This is a generic error type used for different situations. #[error("{1}")] #[diagnostic()] OutsideSpannedLabeledError(#[source_code] String, String, String, #[label("{2}")] Span), + /// Attempted to use a deprecated command. + /// + /// ## Resolution + /// + /// Check the help for the new suggested command and update your script accordingly. #[error("Deprecated command {0}")] #[diagnostic(code(nu::shell::deprecated_command), url(docsrs))] DeprecatedCommand(