nu_protocol/errors/
parse_error.rs

1use crate::{ast::RedirectionSource, did_you_mean, Span, Type};
2use miette::Diagnostic;
3use serde::{Deserialize, Serialize};
4use std::{
5    fmt::Display,
6    str::{from_utf8, Utf8Error},
7};
8use thiserror::Error;
9
10#[derive(Clone, Debug, Error, Diagnostic, Serialize, Deserialize)]
11pub enum ParseError {
12    /// The parser encountered unexpected tokens, when the code should have
13    /// finished. You should remove these or finish adding what you intended
14    /// to add.
15    #[error("Extra tokens in code.")]
16    #[diagnostic(code(nu::parser::extra_tokens), help("Try removing them."))]
17    ExtraTokens(#[label = "extra tokens"] Span),
18
19    #[error("Invalid characters after closing delimiter")]
20    #[diagnostic(
21        code(nu::parser::extra_token_after_closing_delimiter),
22        help("Try removing them.")
23    )]
24    ExtraTokensAfterClosingDelimiter(#[label = "invalid characters"] Span),
25
26    #[error("Extra positional argument.")]
27    #[diagnostic(code(nu::parser::extra_positional), help("Usage: {0}"))]
28    ExtraPositional(String, #[label = "extra positional argument"] Span),
29
30    #[error("Required positional parameter after optional parameter")]
31    #[diagnostic(code(nu::parser::required_after_optional))]
32    RequiredAfterOptional(
33        String,
34        #[label = "required parameter {0} after optional parameter"] Span,
35    ),
36
37    #[error("Unexpected end of code.")]
38    #[diagnostic(code(nu::parser::unexpected_eof))]
39    UnexpectedEof(String, #[label("expected closing {0}")] Span),
40
41    #[error("Unclosed delimiter.")]
42    #[diagnostic(code(nu::parser::unclosed_delimiter))]
43    Unclosed(String, #[label("unclosed {0}")] Span),
44
45    #[error("Unbalanced delimiter.")]
46    #[diagnostic(code(nu::parser::unbalanced_delimiter))]
47    Unbalanced(String, String, #[label("unbalanced {0} and {1}")] Span),
48
49    #[error("Parse mismatch during operation.")]
50    #[diagnostic(code(nu::parser::parse_mismatch))]
51    Expected(&'static str, #[label("expected {0}")] Span),
52
53    #[error("Parse mismatch during operation.")]
54    #[diagnostic(code(nu::parser::parse_mismatch_with_full_string_msg))]
55    ExpectedWithStringMsg(String, #[label("expected {0}")] Span),
56
57    #[error("Parse mismatch during operation.")]
58    #[diagnostic(code(nu::parser::parse_mismatch_with_did_you_mean))]
59    ExpectedWithDidYouMean(&'static str, DidYouMean, #[label("expected {0}. {1}")] Span),
60
61    #[error("Command does not support {0} input.")]
62    #[diagnostic(code(nu::parser::input_type_mismatch))]
63    InputMismatch(Type, #[label("command doesn't support {0} input")] Span),
64
65    #[error("Command output doesn't match {0}.")]
66    #[diagnostic(code(nu::parser::output_type_mismatch))]
67    OutputMismatch(
68        Type,
69        Type,
70        #[label("expected {0}, but command outputs {1}")] Span,
71    ),
72
73    #[error("Type mismatch during operation.")]
74    #[diagnostic(code(nu::parser::type_mismatch))]
75    Mismatch(String, String, #[label("expected {0}, found {1}")] Span), // expected, found, span
76
77    #[error("The '&&' operator is not supported in Nushell")]
78    #[diagnostic(
79        code(nu::parser::shell_andand),
80        help("use ';' instead of the shell '&&', or 'and' instead of the boolean '&&'")
81    )]
82    ShellAndAnd(#[label("instead of '&&', use ';' or 'and'")] Span),
83
84    #[error("The '||' operator is not supported in Nushell")]
85    #[diagnostic(
86        code(nu::parser::shell_oror),
87        help("use 'try' instead of the shell '||', or 'or' instead of the boolean '||'")
88    )]
89    ShellOrOr(#[label("instead of '||', use 'try' or 'or'")] Span),
90
91    #[error("The '2>' shell operation is 'err>' in Nushell.")]
92    #[diagnostic(code(nu::parser::shell_err))]
93    ShellErrRedirect(#[label("use 'err>' instead of '2>' in Nushell")] Span),
94
95    #[error("The '2>&1' shell operation is 'out+err>' in Nushell.")]
96    #[diagnostic(
97        code(nu::parser::shell_outerr),
98        help("Nushell redirection will write all of stdout before stderr.")
99    )]
100    ShellOutErrRedirect(#[label("use 'out+err>' instead of '2>&1' in Nushell")] Span),
101
102    #[error("Multiple redirections provided for {0}.")]
103    #[diagnostic(code(nu::parser::multiple_redirections))]
104    MultipleRedirections(
105        RedirectionSource,
106        #[label = "first redirection"] Span,
107        #[label = "second redirection"] Span,
108    ),
109
110    #[error("Unexpected redirection.")]
111    #[diagnostic(code(nu::parser::unexpected_redirection))]
112    UnexpectedRedirection {
113        #[label = "redirecting nothing"]
114        span: Span,
115    },
116
117    /// One or more of the values have types not supported by the operator.
118    #[error("The '{op}' operator does not work on values of type '{unsupported}'.")]
119    #[diagnostic(code(nu::parser::operator_unsupported_type))]
120    OperatorUnsupportedType {
121        op: &'static str,
122        unsupported: Type,
123        #[label = "does not support '{unsupported}'"]
124        op_span: Span,
125        #[label("{unsupported}")]
126        unsupported_span: Span,
127        #[help]
128        help: Option<&'static str>,
129    },
130
131    /// The operator supports the types of both values, but not the specific combination of their types.
132    #[error("Types '{lhs}' and '{rhs}' are not compatible for the '{op}' operator.")]
133    #[diagnostic(code(nu::parser::operator_incompatible_types))]
134    OperatorIncompatibleTypes {
135        op: &'static str,
136        lhs: Type,
137        rhs: Type,
138        #[label = "does not operate between '{lhs}' and '{rhs}'"]
139        op_span: Span,
140        #[label("{lhs}")]
141        lhs_span: Span,
142        #[label("{rhs}")]
143        rhs_span: Span,
144        #[help]
145        help: Option<&'static str>,
146    },
147
148    #[error("Capture of mutable variable.")]
149    #[diagnostic(code(nu::parser::expected_keyword))]
150    CaptureOfMutableVar(#[label("capture of mutable variable")] Span),
151
152    #[error("Expected keyword.")]
153    #[diagnostic(code(nu::parser::expected_keyword))]
154    ExpectedKeyword(String, #[label("expected {0}")] Span),
155
156    #[error("Unexpected keyword.")]
157    #[diagnostic(
158        code(nu::parser::unexpected_keyword),
159        help("'{0}' keyword is allowed only in a module.")
160    )]
161    UnexpectedKeyword(String, #[label("unexpected {0}")] Span),
162
163    #[error("Can't create alias to parser keyword.")]
164    #[diagnostic(
165        code(nu::parser::cant_alias_keyword),
166        help("Only the following keywords can be aliased: {0}.")
167    )]
168    CantAliasKeyword(String, #[label("not supported in alias")] Span),
169
170    #[error("Can't create alias to expression.")]
171    #[diagnostic(
172        code(nu::parser::cant_alias_expression),
173        help("Only command calls can be aliased.")
174    )]
175    CantAliasExpression(String, #[label("aliasing {0} is not supported")] Span),
176
177    #[error("Unknown operator")]
178    #[diagnostic(code(nu::parser::unknown_operator), help("{1}"))]
179    UnknownOperator(
180        &'static str,
181        &'static str,
182        #[label("Operator '{0}' not supported")] Span,
183    ),
184
185    #[error("Statement used in pipeline.")]
186    #[diagnostic(
187        code(nu::parser::unexpected_keyword),
188        help(
189            "'{0}' keyword is not allowed in pipeline. Use '{0}' by itself, outside of a pipeline."
190        )
191    )]
192    BuiltinCommandInPipeline(String, #[label("not allowed in pipeline")] Span),
193
194    #[error("{0} statement used in pipeline.")]
195    #[diagnostic(
196        code(nu::parser::unexpected_keyword),
197        help(
198            "Assigning '{1}' to '{2}' does not produce a value to be piped. If the pipeline result is meant to be assigned to '{2}', use '{0} {2} = ({1} | ...)'."
199        )
200    )]
201    AssignInPipeline(String, String, String, #[label("'{0}' in pipeline")] Span),
202
203    #[error("`{0}` used as variable name.")]
204    #[diagnostic(
205        code(nu::parser::name_is_builtin_var),
206        help(
207            "'{0}' is the name of a builtin Nushell variable and cannot be used as a variable name"
208        )
209    )]
210    NameIsBuiltinVar(String, #[label("already a builtin variable")] Span),
211
212    #[error("Incorrect value")]
213    #[diagnostic(code(nu::parser::incorrect_value), help("{2}"))]
214    IncorrectValue(String, #[label("unexpected {0}")] Span, String),
215
216    #[error("Multiple rest params.")]
217    #[diagnostic(code(nu::parser::multiple_rest_params))]
218    MultipleRestParams(#[label = "multiple rest params"] Span),
219
220    #[error("Variable not found.")]
221    #[diagnostic(code(nu::parser::variable_not_found))]
222    VariableNotFound(DidYouMean, #[label = "variable not found. {0}"] Span),
223
224    #[error("Use $env.{0} instead of ${0}.")]
225    #[diagnostic(code(nu::parser::env_var_not_var))]
226    EnvVarNotVar(String, #[label = "use $env.{0} instead of ${0}"] Span),
227
228    #[error("Variable name not supported.")]
229    #[diagnostic(code(nu::parser::variable_not_valid))]
230    VariableNotValid(#[label = "variable name can't contain spaces or quotes"] Span),
231
232    #[error("Alias name not supported.")]
233    #[diagnostic(code(nu::parser::variable_not_valid))]
234    AliasNotValid(
235        #[label = "alias name can't be a number, a filesize, or contain a hash # or caret ^"] Span,
236    ),
237
238    #[error("Command name not supported.")]
239    #[diagnostic(code(nu::parser::variable_not_valid))]
240    CommandDefNotValid(
241        #[label = "command name can't be a number, a filesize, or contain a hash # or caret ^"]
242        Span,
243    ),
244
245    #[error("Module not found.")]
246    #[diagnostic(
247        code(nu::parser::module_not_found),
248        help("module files and their paths must be available before your script is run as parsing occurs before anything is evaluated")
249    )]
250    ModuleNotFound(#[label = "module {1} not found"] Span, String),
251
252    #[error("Missing mod.nu file.")]
253    #[diagnostic(
254        code(nu::parser::module_missing_mod_nu_file),
255        help("Directory {0} is missing a mod.nu file.\n\nWhen importing a directory as a Nushell module, it needs to contain a mod.nu file (can be empty). Alternatively, you can use .nu files in the directory as modules individually.")
256    )]
257    ModuleMissingModNuFile(
258        String,
259        #[label = "module directory is missing a mod.nu file"] Span,
260    ),
261
262    #[error("Circular import.")]
263    #[diagnostic(code(nu::parser::circular_import), help("{0}"))]
264    CircularImport(String, #[label = "detected circular import"] Span),
265
266    #[error("Can't export {0} named same as the module.")]
267    #[diagnostic(
268        code(nu::parser::named_as_module),
269        help("Module {1} can't export {0} named the same as the module. Either change the module name, or export `{2}` {0}.")
270    )]
271    NamedAsModule(
272        String,
273        String,
274        String,
275        #[label = "can't export from module {1}"] Span,
276    ),
277
278    #[error("Module already contains 'main' command.")]
279    #[diagnostic(
280        code(nu::parser::module_double_main),
281        help("Tried to add 'main' command to module '{0}' but it has already been added.")
282    )]
283    ModuleDoubleMain(
284        String,
285        #[label = "module '{0}' already contains 'main'"] Span,
286    ),
287
288    #[error("Can't export alias defined as 'main'.")]
289    #[diagnostic(
290        code(nu::parser::export_main_alias_not_allowed),
291        help("Exporting aliases as 'main' is not allowed. Either rename the alias or convert it to a custom command.")
292    )]
293    ExportMainAliasNotAllowed(#[label = "can't export from module"] Span),
294
295    #[error("Active overlay not found.")]
296    #[diagnostic(code(nu::parser::active_overlay_not_found))]
297    ActiveOverlayNotFound(#[label = "not an active overlay"] Span),
298
299    #[error("Overlay prefix mismatch.")]
300    #[diagnostic(
301        code(nu::parser::overlay_prefix_mismatch),
302        help("Overlay {0} already exists {1} a prefix. To add it again, do it {1} the --prefix flag.")
303    )]
304    OverlayPrefixMismatch(
305        String,
306        String,
307        #[label = "already exists {1} a prefix"] Span,
308    ),
309
310    #[error("Module or overlay not found.")]
311    #[diagnostic(
312        code(nu::parser::module_or_overlay_not_found),
313        help("Requires either an existing overlay, a module, or an import pattern defining a module.")
314    )]
315    ModuleOrOverlayNotFound(#[label = "not a module or an overlay"] Span),
316
317    #[error("Cannot remove the last overlay.")]
318    #[diagnostic(
319        code(nu::parser::cant_remove_last_overlay),
320        help("At least one overlay must always be active.")
321    )]
322    CantRemoveLastOverlay(#[label = "this is the last overlay, can't remove it"] Span),
323
324    #[error("Cannot hide default overlay.")]
325    #[diagnostic(
326        code(nu::parser::cant_hide_default_overlay),
327        help("'{0}' is a default overlay. Default overlays cannot be hidden.")
328    )]
329    CantHideDefaultOverlay(String, #[label = "can't hide overlay"] Span),
330
331    #[error("Cannot add overlay.")]
332    #[diagnostic(code(nu::parser::cant_add_overlay_help), help("{0}"))]
333    CantAddOverlayHelp(String, #[label = "cannot add this overlay"] Span),
334
335    #[error("Duplicate command definition within a block.")]
336    #[diagnostic(code(nu::parser::duplicate_command_def))]
337    DuplicateCommandDef(#[label = "defined more than once"] Span),
338
339    #[error("Unknown command.")]
340    #[diagnostic(
341        code(nu::parser::unknown_command),
342        // TODO: actual suggestions like "Did you mean `foo`?"
343    )]
344    UnknownCommand(#[label = "unknown command"] Span),
345
346    #[error("Non-UTF8 string.")]
347    #[diagnostic(code(nu::parser::non_utf8))]
348    NonUtf8(#[label = "non-UTF8 string"] Span),
349
350    #[error("The `{0}` command doesn't have flag `{1}`.")]
351    #[diagnostic(code(nu::parser::unknown_flag), help("{3}"))]
352    UnknownFlag(String, String, #[label = "unknown flag"] Span, String),
353
354    #[error("Unknown type.")]
355    #[diagnostic(code(nu::parser::unknown_type))]
356    UnknownType(#[label = "unknown type"] Span),
357
358    #[error("Missing flag argument.")]
359    #[diagnostic(code(nu::parser::missing_flag_param))]
360    MissingFlagParam(String, #[label = "flag missing {0} argument"] Span),
361
362    #[error("Only the last flag in a short flag batch can take an argument.")]
363    #[diagnostic(code(nu::parser::only_last_flag_in_batch_can_take_arg))]
364    OnlyLastFlagInBatchCanTakeArg(#[label = "only the last flag can take args"] Span),
365
366    #[error("Missing required positional argument.")]
367    #[diagnostic(
368        code(nu::parser::missing_positional),
369        help("Usage: {2}. Use `--help` for more information.")
370    )]
371    MissingPositional(String, #[label("missing {0}")] Span, String),
372
373    #[error("Missing argument to `{1}`.")]
374    #[diagnostic(code(nu::parser::keyword_missing_arg))]
375    KeywordMissingArgument(
376        String,
377        String,
378        #[label("missing {0} value that follows {1}")] Span,
379    ),
380
381    #[error("Missing type.")]
382    #[diagnostic(code(nu::parser::missing_type))]
383    MissingType(#[label = "expected type"] Span),
384
385    #[error("Type mismatch.")]
386    #[diagnostic(code(nu::parser::type_mismatch))]
387    TypeMismatch(Type, Type, #[label("expected {0}, found {1}")] Span), // expected, found, span
388
389    #[error("Type mismatch.")]
390    #[diagnostic(code(nu::parser::type_mismatch_help), help("{3}"))]
391    TypeMismatchHelp(Type, Type, #[label("expected {0}, found {1}")] Span, String), // expected, found, span, help
392
393    #[error("Missing required flag.")]
394    #[diagnostic(code(nu::parser::missing_required_flag))]
395    MissingRequiredFlag(String, #[label("missing required flag {0}")] Span),
396
397    #[error("Incomplete math expression.")]
398    #[diagnostic(code(nu::parser::incomplete_math_expression))]
399    IncompleteMathExpression(#[label = "incomplete math expression"] Span),
400
401    #[error("Unknown state.")]
402    #[diagnostic(code(nu::parser::unknown_state))]
403    UnknownState(String, #[label("{0}")] Span),
404
405    #[error("Internal error.")]
406    #[diagnostic(code(nu::parser::unknown_state))]
407    InternalError(String, #[label("{0}")] Span),
408
409    #[error("Parser incomplete.")]
410    #[diagnostic(code(nu::parser::parser_incomplete))]
411    IncompleteParser(#[label = "parser support missing for this expression"] Span),
412
413    #[error("Rest parameter needs a name.")]
414    #[diagnostic(code(nu::parser::rest_needs_name))]
415    RestNeedsName(#[label = "needs a parameter name"] Span),
416
417    #[error("Parameter not correct type.")]
418    #[diagnostic(code(nu::parser::parameter_mismatch_type))]
419    ParameterMismatchType(
420        String,
421        String,
422        String,
423        #[label = "parameter {0} needs to be '{1}' instead of '{2}'"] Span,
424    ),
425
426    #[error("Default values should be constant expressions.")]
427    #[diagnostic(code(nu::parser::non_constant_default_value))]
428    NonConstantDefaultValue(#[label = "expected a constant value"] Span),
429
430    #[error("Extra columns.")]
431    #[diagnostic(code(nu::parser::extra_columns))]
432    ExtraColumns(
433        usize,
434        #[label("expected {0} column{}", if *.0 == 1 { "" } else { "s" })] Span,
435    ),
436
437    #[error("Missing columns.")]
438    #[diagnostic(code(nu::parser::missing_columns))]
439    MissingColumns(
440        usize,
441        #[label("expected {0} column{}", if *.0 == 1 { "" } else { "s" })] Span,
442    ),
443
444    #[error("{0}")]
445    #[diagnostic(code(nu::parser::assignment_mismatch))]
446    AssignmentMismatch(String, String, #[label("{1}")] Span),
447
448    #[error("Wrong import pattern structure.")]
449    #[diagnostic(code(nu::parser::wrong_import_pattern))]
450    WrongImportPattern(String, #[label = "{0}"] Span),
451
452    #[error("Export not found.")]
453    #[diagnostic(code(nu::parser::export_not_found))]
454    ExportNotFound(#[label = "could not find imports"] Span),
455
456    #[error("File not found")]
457    #[diagnostic(
458        code(nu::parser::sourced_file_not_found),
459        help("sourced files need to be available before your script is run")
460    )]
461    SourcedFileNotFound(String, #[label("File not found: {0}")] Span),
462
463    #[error("File not found")]
464    #[diagnostic(
465        code(nu::parser::registered_file_not_found),
466        help("registered files need to be available before your script is run")
467    )]
468    RegisteredFileNotFound(String, #[label("File not found: {0}")] Span),
469
470    #[error("File not found")]
471    #[diagnostic(code(nu::parser::file_not_found))]
472    FileNotFound(String, #[label("File not found: {0}")] Span),
473
474    #[error("Plugin not found")]
475    #[diagnostic(
476        code(nu::parser::plugin_not_found),
477        help("plugins need to be added to the plugin registry file before your script is run (see `plugin add`)"),
478    )]
479    PluginNotFound {
480        name: String,
481        #[label("Plugin not found: {name}")]
482        name_span: Span,
483        #[label("in this registry file")]
484        plugin_config_span: Option<Span>,
485    },
486
487    #[error("Invalid literal")] // <problem> in <entity>.
488    #[diagnostic()]
489    InvalidLiteral(String, String, #[label("{0} in {1}")] Span),
490
491    #[error("{0}")]
492    #[diagnostic()]
493    LabeledError(String, String, #[label("{1}")] Span),
494
495    #[error("{error}")]
496    #[diagnostic(help("{help}"))]
497    LabeledErrorWithHelp {
498        error: String,
499        label: String,
500        help: String,
501        #[label("{label}")]
502        span: Span,
503    },
504
505    #[error("Redirection can not be used with {0}.")]
506    #[diagnostic()]
507    RedirectingBuiltinCommand(
508        &'static str,
509        #[label("not allowed here")] Span,
510        #[label("...and here")] Option<Span>,
511    ),
512
513    #[error("This command does not have a ...rest parameter")]
514    #[diagnostic(
515        code(nu::parser::unexpected_spread_arg),
516        help("To spread arguments, the command needs to define a multi-positional parameter in its signature, such as ...rest")
517    )]
518    UnexpectedSpreadArg(String, #[label = "unexpected spread argument"] Span),
519
520    /// Invalid assignment left-hand side
521    ///
522    /// ## Resolution
523    ///
524    /// Assignment requires that you assign to a mutable variable or cell path.
525    #[error("Assignment to an immutable variable.")]
526    #[diagnostic(
527        code(nu::parser::assignment_requires_mutable_variable),
528        help("declare the variable with `mut`, or shadow it again with `let`")
529    )]
530    AssignmentRequiresMutableVar(#[label("needs to be a mutable variable")] Span),
531
532    /// Invalid assignment left-hand side
533    ///
534    /// ## Resolution
535    ///
536    /// Assignment requires that you assign to a variable or variable cell path.
537    #[error("Assignment operations require a variable.")]
538    #[diagnostic(
539        code(nu::parser::assignment_requires_variable),
540        help("try assigning to a variable or a cell path of a variable")
541    )]
542    AssignmentRequiresVar(#[label("needs to be a variable")] Span),
543
544    #[error("Attributes must be followed by a definition.")]
545    #[diagnostic(
546        code(nu::parser::attribute_requires_definition),
547        help("try following this line with a `def` or `extern` definition")
548    )]
549    AttributeRequiresDefinition(#[label("must be followed by a definition")] Span),
550}
551
552impl ParseError {
553    pub fn span(&self) -> Span {
554        match self {
555            ParseError::ExtraTokens(s) => *s,
556            ParseError::ExtraPositional(_, s) => *s,
557            ParseError::UnexpectedEof(_, s) => *s,
558            ParseError::Unclosed(_, s) => *s,
559            ParseError::Unbalanced(_, _, s) => *s,
560            ParseError::Expected(_, s) => *s,
561            ParseError::ExpectedWithStringMsg(_, s) => *s,
562            ParseError::ExpectedWithDidYouMean(_, _, s) => *s,
563            ParseError::Mismatch(_, _, s) => *s,
564            ParseError::OperatorUnsupportedType { op_span, .. } => *op_span,
565            ParseError::OperatorIncompatibleTypes { op_span, .. } => *op_span,
566            ParseError::ExpectedKeyword(_, s) => *s,
567            ParseError::UnexpectedKeyword(_, s) => *s,
568            ParseError::CantAliasKeyword(_, s) => *s,
569            ParseError::CantAliasExpression(_, s) => *s,
570            ParseError::BuiltinCommandInPipeline(_, s) => *s,
571            ParseError::AssignInPipeline(_, _, _, s) => *s,
572            ParseError::NameIsBuiltinVar(_, s) => *s,
573            ParseError::CaptureOfMutableVar(s) => *s,
574            ParseError::IncorrectValue(_, s, _) => *s,
575            ParseError::MultipleRestParams(s) => *s,
576            ParseError::VariableNotFound(_, s) => *s,
577            ParseError::EnvVarNotVar(_, s) => *s,
578            ParseError::VariableNotValid(s) => *s,
579            ParseError::AliasNotValid(s) => *s,
580            ParseError::CommandDefNotValid(s) => *s,
581            ParseError::ModuleNotFound(s, _) => *s,
582            ParseError::ModuleMissingModNuFile(_, s) => *s,
583            ParseError::NamedAsModule(_, _, _, s) => *s,
584            ParseError::ModuleDoubleMain(_, s) => *s,
585            ParseError::ExportMainAliasNotAllowed(s) => *s,
586            ParseError::CircularImport(_, s) => *s,
587            ParseError::ModuleOrOverlayNotFound(s) => *s,
588            ParseError::ActiveOverlayNotFound(s) => *s,
589            ParseError::OverlayPrefixMismatch(_, _, s) => *s,
590            ParseError::CantRemoveLastOverlay(s) => *s,
591            ParseError::CantHideDefaultOverlay(_, s) => *s,
592            ParseError::CantAddOverlayHelp(_, s) => *s,
593            ParseError::DuplicateCommandDef(s) => *s,
594            ParseError::UnknownCommand(s) => *s,
595            ParseError::NonUtf8(s) => *s,
596            ParseError::UnknownFlag(_, _, s, _) => *s,
597            ParseError::RequiredAfterOptional(_, s) => *s,
598            ParseError::UnknownType(s) => *s,
599            ParseError::MissingFlagParam(_, s) => *s,
600            ParseError::OnlyLastFlagInBatchCanTakeArg(s) => *s,
601            ParseError::MissingPositional(_, s, _) => *s,
602            ParseError::KeywordMissingArgument(_, _, s) => *s,
603            ParseError::MissingType(s) => *s,
604            ParseError::TypeMismatch(_, _, s) => *s,
605            ParseError::TypeMismatchHelp(_, _, s, _) => *s,
606            ParseError::InputMismatch(_, s) => *s,
607            ParseError::OutputMismatch(_, _, s) => *s,
608            ParseError::MissingRequiredFlag(_, s) => *s,
609            ParseError::IncompleteMathExpression(s) => *s,
610            ParseError::UnknownState(_, s) => *s,
611            ParseError::InternalError(_, s) => *s,
612            ParseError::IncompleteParser(s) => *s,
613            ParseError::RestNeedsName(s) => *s,
614            ParseError::ParameterMismatchType(_, _, _, s) => *s,
615            ParseError::NonConstantDefaultValue(s) => *s,
616            ParseError::ExtraColumns(_, s) => *s,
617            ParseError::MissingColumns(_, s) => *s,
618            ParseError::AssignmentMismatch(_, _, s) => *s,
619            ParseError::WrongImportPattern(_, s) => *s,
620            ParseError::ExportNotFound(s) => *s,
621            ParseError::SourcedFileNotFound(_, s) => *s,
622            ParseError::RegisteredFileNotFound(_, s) => *s,
623            ParseError::FileNotFound(_, s) => *s,
624            ParseError::PluginNotFound { name_span, .. } => *name_span,
625            ParseError::LabeledError(_, _, s) => *s,
626            ParseError::ShellAndAnd(s) => *s,
627            ParseError::ShellOrOr(s) => *s,
628            ParseError::ShellErrRedirect(s) => *s,
629            ParseError::ShellOutErrRedirect(s) => *s,
630            ParseError::MultipleRedirections(_, _, s) => *s,
631            ParseError::UnexpectedRedirection { span } => *span,
632            ParseError::UnknownOperator(_, _, s) => *s,
633            ParseError::InvalidLiteral(_, _, s) => *s,
634            ParseError::LabeledErrorWithHelp { span: s, .. } => *s,
635            ParseError::RedirectingBuiltinCommand(_, s, _) => *s,
636            ParseError::UnexpectedSpreadArg(_, s) => *s,
637            ParseError::ExtraTokensAfterClosingDelimiter(s) => *s,
638            ParseError::AssignmentRequiresVar(s) => *s,
639            ParseError::AssignmentRequiresMutableVar(s) => *s,
640            ParseError::AttributeRequiresDefinition(s) => *s,
641        }
642    }
643}
644
645#[derive(Clone, Debug, Serialize, Deserialize)]
646pub struct DidYouMean(Option<String>);
647
648fn did_you_mean_impl(possibilities_bytes: &[&[u8]], input_bytes: &[u8]) -> Option<String> {
649    let input = from_utf8(input_bytes).ok()?;
650    let possibilities = possibilities_bytes
651        .iter()
652        .map(|p| from_utf8(p))
653        .collect::<Result<Vec<&str>, Utf8Error>>()
654        .ok()?;
655    did_you_mean(&possibilities, input)
656}
657impl DidYouMean {
658    pub fn new(possibilities_bytes: &[&[u8]], input_bytes: &[u8]) -> DidYouMean {
659        DidYouMean(did_you_mean_impl(possibilities_bytes, input_bytes))
660    }
661}
662
663impl From<Option<String>> for DidYouMean {
664    fn from(value: Option<String>) -> Self {
665        Self(value)
666    }
667}
668
669impl Display for DidYouMean {
670    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
671        if let Some(suggestion) = &self.0 {
672            write!(f, "Did you mean '{}'?", suggestion)
673        } else {
674            write!(f, "")
675        }
676    }
677}