Skip to main content

shannon_nu_protocol/errors/
parse_error.rs

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