Skip to main content

nu_parser/
parse_keywords.rs

1use crate::{
2    exportable::Exportable,
3    parse_block,
4    parser::{
5        ArgumentParsingLevel, CallKind, compile_block, compile_block_with_id, parse_attribute,
6        parse_redirection, redirecting_builtin_error,
7    },
8    type_check::{check_block_input_output, type_compatible},
9};
10
11use log::trace;
12use nu_path::absolute_with;
13use nu_path::is_windows_device_path;
14use nu_protocol::{
15    Alias, BlockId, CommandWideCompleter, CustomExample, DeclId, FromValue, Module, ModuleId,
16    ParseError, PositionalArg, ResolvedImportPattern, ShellError, Signature, Span, Spanned,
17    SyntaxShape, Type, Value, VarId,
18    ast::{
19        Argument, AttributeBlock, Block, Call, Expr, Expression, ImportPattern, ImportPatternHead,
20        ImportPatternMember, Pipeline, PipelineElement,
21    },
22    category_from_string,
23    engine::{DEFAULT_OVERLAY_NAME, StateWorkingSet},
24    eval_const::eval_constant,
25    parser_path::ParserPath,
26    shell_error::generic::GenericError,
27};
28use std::{
29    collections::{HashMap, HashSet},
30    path::{Path, PathBuf},
31    sync::Arc,
32};
33
34pub const LIB_DIRS_VAR: &str = "NU_LIB_DIRS";
35#[cfg(feature = "plugin")]
36pub const PLUGIN_DIRS_VAR: &str = "NU_PLUGIN_DIRS";
37
38use crate::{
39    Token, TokenContents, is_math_expression_like,
40    known_external::KnownExternal,
41    lex,
42    lite_parser::{LiteCommand, lite_parse},
43    parser::{
44        ParsedInternalCall, garbage, garbage_pipeline, parse, parse_call, parse_expression,
45        parse_full_signature, parse_import_pattern, parse_internal_call, parse_string,
46        parse_var_with_opt_type, trim_quotes,
47    },
48    unescape_unquote_string,
49};
50
51/// These parser keywords can be aliased
52pub const ALIASABLE_PARSER_KEYWORDS: &[&[u8]] = &[
53    b"if",
54    b"match",
55    b"try",
56    b"overlay",
57    b"overlay hide",
58    b"overlay new",
59    b"overlay use",
60];
61
62/// These parser keywords cannot be aliased (either not possible, or support not yet added)
63pub const UNALIASABLE_PARSER_KEYWORDS: &[&[u8]] = &[
64    b"alias",
65    b"const",
66    b"def",
67    b"extern",
68    b"module",
69    b"use",
70    b"export",
71    b"export alias",
72    b"export const",
73    b"export def",
74    b"export extern",
75    b"export module",
76    b"export use",
77    b"for",
78    b"loop",
79    b"while",
80    b"return",
81    b"break",
82    b"continue",
83    b"let",
84    b"mut",
85    b"hide",
86    b"export-env",
87    b"source-env",
88    b"source",
89    b"where",
90    b"plugin use",
91];
92
93/// Check whether spans start with a parser keyword that can be aliased
94pub fn is_unaliasable_parser_keyword(working_set: &StateWorkingSet, spans: &[Span]) -> bool {
95    // try two words
96    if let (Some(&span1), Some(&span2)) = (spans.first(), spans.get(1)) {
97        let cmd_name = working_set.get_span_contents(Span::append(span1, span2));
98        return UNALIASABLE_PARSER_KEYWORDS.contains(&cmd_name);
99    }
100
101    // try one word
102    if let Some(&span1) = spans.first() {
103        let cmd_name = working_set.get_span_contents(span1);
104        UNALIASABLE_PARSER_KEYWORDS.contains(&cmd_name)
105    } else {
106        false
107    }
108}
109
110/// This is a new more compact method of calling parse_xxx() functions without repeating the
111/// parse_call() in each function. Remaining keywords can be moved here.
112pub fn parse_keyword(working_set: &mut StateWorkingSet, lite_command: &LiteCommand) -> Pipeline {
113    let orig_parse_errors_len = working_set.parse_errors.len();
114
115    let call_expr = parse_call(working_set, &lite_command.parts, lite_command.parts[0]);
116
117    // If an error occurred, don't invoke the keyword-specific functionality
118    if working_set.parse_errors.len() > orig_parse_errors_len {
119        return Pipeline::from_vec(vec![call_expr]);
120    }
121
122    if let Expression {
123        expr: Expr::Call(call),
124        ..
125    } = call_expr.clone()
126    {
127        // Apply parse keyword side effects
128        let cmd = working_set.get_decl(call.decl_id);
129        // check help flag first.
130        if call.named_iter().any(|(flag, _, _)| flag.item == "help") {
131            let call_span = call.span();
132            return Pipeline::from_vec(vec![Expression::new(
133                working_set,
134                Expr::Call(call),
135                call_span,
136                Type::Any,
137            )]);
138        }
139
140        match cmd.name() {
141            "overlay hide" => parse_overlay_hide(working_set, call),
142            "overlay new" => parse_overlay_new(working_set, call),
143            "overlay use" => parse_overlay_use(working_set, call),
144            #[cfg(feature = "plugin")]
145            "plugin use" => parse_plugin_use(working_set, call),
146            _ => Pipeline::from_vec(vec![call_expr]),
147        }
148    } else {
149        Pipeline::from_vec(vec![call_expr])
150    }
151}
152
153pub fn parse_def_predecl(working_set: &mut StateWorkingSet, spans: &[Span]) {
154    let mut pos = 0;
155
156    let def_type_name = if spans.len() >= 3 {
157        // definition can't have only two spans, minimum is 3, e.g., 'extern spam []'
158        let first_word = working_set.get_span_contents(spans[0]);
159
160        if first_word == b"export" {
161            pos += 2;
162        } else {
163            pos += 1;
164        }
165
166        working_set.get_span_contents(spans[pos - 1]).to_vec()
167    } else {
168        return;
169    };
170
171    if def_type_name != b"def" && def_type_name != b"extern" {
172        return;
173    }
174
175    // Now, pos should point at the next span after the def-like call.
176    // Skip all potential flags, like --env, --wrapped or --help:
177    while pos < spans.len() && working_set.get_span_contents(spans[pos]).starts_with(b"-") {
178        pos += 1;
179    }
180
181    if pos >= spans.len() {
182        // This can happen if the call ends with a flag, e.g., 'def --help'
183        return;
184    }
185
186    // Now, pos should point at the command name.
187    let name_pos = pos;
188
189    let Some(name) = parse_string(working_set, spans[name_pos]).as_string() else {
190        return;
191    };
192
193    if name.contains('#')
194        || name.contains('^')
195        || name.contains('%')
196        || name.parse::<bytesize::ByteSize>().is_ok()
197        || name.parse::<f64>().is_ok()
198    {
199        working_set.error(ParseError::CommandDefNotValid(spans[name_pos]));
200        return;
201    }
202
203    // Find signature
204    let mut signature_pos = None;
205
206    while pos < spans.len() {
207        if working_set.get_span_contents(spans[pos]).starts_with(b"[")
208            || working_set.get_span_contents(spans[pos]).starts_with(b"(")
209        {
210            signature_pos = Some(pos);
211            break;
212        }
213
214        pos += 1;
215    }
216
217    let Some(signature_pos) = signature_pos else {
218        return;
219    };
220
221    let mut allow_unknown_args = false;
222
223    for span in spans {
224        if working_set.get_span_contents(*span) == b"--wrapped" && def_type_name == b"def" {
225            allow_unknown_args = true;
226        }
227    }
228
229    let starting_error_count = working_set.parse_errors.len();
230
231    working_set.enter_scope();
232    // FIXME: because parse_signature will update the scope with the variables it sees
233    // we end up parsing the signature twice per def. The first time is during the predecl
234    // so that we can see the types that are part of the signature, which we need for parsing.
235    // The second time is when we actually parse the body itworking_set.
236    // We can't reuse the first time because the variables that are created during parse_signature
237    // are lost when we exit the scope below.
238    let sig = parse_full_signature(
239        working_set,
240        &spans[signature_pos..],
241        def_type_name == b"extern",
242    );
243    working_set.parse_errors.truncate(starting_error_count);
244    working_set.exit_scope();
245
246    let Some(mut signature) = sig.as_signature() else {
247        return;
248    };
249
250    signature.name = name;
251
252    if allow_unknown_args {
253        signature.allows_unknown_args = true;
254    }
255
256    let decl = signature.predeclare();
257
258    if working_set.add_predecl(decl).is_some() {
259        working_set.error(ParseError::DuplicateCommandDef(spans[name_pos]));
260    }
261}
262
263pub fn parse_for(working_set: &mut StateWorkingSet, lite_command: &LiteCommand) -> Expression {
264    let spans = &lite_command.parts;
265    // Checking that the function is used with the correct name
266    // Maybe this is not necessary but it is a sanity check
267    if working_set.get_span_contents(spans[0]) != b"for" {
268        working_set.error(ParseError::UnknownState(
269            "internal error: Wrong call name for 'for' function".into(),
270            Span::concat(spans),
271        ));
272        return garbage(working_set, spans[0]);
273    }
274    if let Some(redirection) = lite_command.redirection.as_ref() {
275        working_set.error(redirecting_builtin_error("for", redirection));
276        return garbage(working_set, spans[0]);
277    }
278
279    // Parsing the spans and checking that they match the register signature
280    // Using a parsed call makes more sense than checking for how many spans are in the call
281    // Also, by creating a call, it can be checked if it matches the declaration signature
282    let (call, call_span) = match working_set.find_decl(b"for") {
283        None => {
284            working_set.error(ParseError::UnknownState(
285                "internal error: for declaration not found".into(),
286                Span::concat(spans),
287            ));
288            return garbage(working_set, spans[0]);
289        }
290        Some(decl_id) => {
291            let starting_error_count = working_set.parse_errors.len();
292            working_set.enter_scope();
293            let ParsedInternalCall {
294                call,
295                output,
296                call_kind,
297            } = parse_internal_call(
298                working_set,
299                spans[0],
300                &spans[1..],
301                decl_id,
302                ArgumentParsingLevel::Full,
303            );
304
305            if working_set
306                .parse_errors
307                .get(starting_error_count..)
308                .is_none_or(|new_errors| {
309                    new_errors
310                        .iter()
311                        .all(|e| !matches!(e, ParseError::Unclosed(token, _) if token == "}"))
312                })
313            {
314                working_set.exit_scope();
315            }
316
317            let call_span = Span::concat(spans);
318            let decl = working_set.get_decl(decl_id);
319            let sig = decl.signature();
320
321            if call_kind != CallKind::Valid {
322                return Expression::new(working_set, Expr::Call(call), call_span, output);
323            }
324
325            // Let's get our block and make sure it has the right signature
326            if let Some(
327                Expression {
328                    expr: Expr::Block(block_id),
329                    ..
330                }
331                | Expression {
332                    expr: Expr::RowCondition(block_id),
333                    ..
334                },
335            ) = call.positional_nth(2)
336            {
337                {
338                    let block = working_set.get_block_mut(*block_id);
339
340                    *block.signature = sig;
341                }
342            }
343
344            (call, call_span)
345        }
346    };
347
348    // All positional arguments must be in the call positional vector by this point
349    let var_decl = call.positional_nth(0).expect("for call already checked");
350    let iteration_expr = call.positional_nth(1).expect("for call already checked");
351    let block = call.positional_nth(2).expect("for call already checked");
352
353    let iteration_expr_ty = iteration_expr.ty.clone();
354
355    // Figure out the type of the variable the `for` uses for iteration
356    let var_type = match iteration_expr_ty {
357        Type::List(x) => *x,
358        Type::Table(x) => Type::Record(x),
359        Type::Range => Type::Number, // Range elements can be int or float
360        x => x,
361    };
362
363    if let (Some(var_id), Some(block_id)) = (&var_decl.as_var(), block.as_block()) {
364        working_set.set_variable_type(*var_id, var_type.clone());
365
366        let block = working_set.get_block_mut(block_id);
367
368        block.signature.required_positional.insert(
369            0,
370            PositionalArg {
371                name: String::new(),
372                desc: String::new(),
373                shape: var_type.to_shape(),
374                var_id: Some(*var_id),
375                default_value: None,
376                completion: None,
377            },
378        );
379    }
380
381    Expression::new(working_set, Expr::Call(call), call_span, Type::Nothing)
382}
383
384// This is meant for parsing attribute blocks without an accompanying `def` or `extern`. It's
385// necessary to provide consistent syntax highlighting, completions, and helpful errors
386//
387// There is no need to run the const evaluation here
388pub fn parse_attribute_block(
389    working_set: &mut StateWorkingSet,
390    lite_command: &LiteCommand,
391) -> Pipeline {
392    let attributes = lite_command
393        .attribute_commands()
394        .map(|cmd| parse_attribute(working_set, &cmd).0)
395        .collect::<Vec<_>>();
396
397    let last_attr_span = attributes
398        .last()
399        .expect("Attribute block must contain at least one attribute")
400        .expr
401        .span;
402
403    working_set.error(ParseError::AttributeRequiresDefinition(last_attr_span));
404    let cmd_span = if lite_command.command_parts().is_empty() {
405        last_attr_span.past()
406    } else {
407        Span::concat(lite_command.command_parts())
408    };
409    let cmd_expr = garbage(working_set, cmd_span);
410    let ty = cmd_expr.ty.clone();
411
412    let attr_block_span = Span::merge_many(
413        attributes
414            .first()
415            .map(|x| x.expr.span)
416            .into_iter()
417            .chain(Some(cmd_span)),
418    );
419
420    Pipeline::from_vec(vec![Expression::new(
421        working_set,
422        Expr::AttributeBlock(AttributeBlock {
423            attributes,
424            item: Box::new(cmd_expr),
425        }),
426        attr_block_span,
427        ty,
428    )])
429}
430
431// Returns also the parsed command name and ID
432pub fn parse_def(
433    working_set: &mut StateWorkingSet,
434    lite_command: &LiteCommand,
435    module_name: Option<&[u8]>,
436) -> (Pipeline, Option<(Vec<u8>, DeclId)>) {
437    let mut attributes = vec![];
438    let mut attribute_vals = vec![];
439
440    for attr_cmd in lite_command.attribute_commands() {
441        let (attr, name) = parse_attribute(working_set, &attr_cmd);
442        if let Some(name) = name {
443            let val = eval_constant(working_set, &attr.expr);
444            match val {
445                Ok(val) => attribute_vals.push((name, val)),
446                Err(e) => working_set.error(e.wrap(working_set, attr.expr.span)),
447            }
448        }
449        attributes.push(attr);
450    }
451
452    let (expr, decl) = parse_def_inner(working_set, attribute_vals, lite_command, module_name);
453
454    let ty = expr.ty.clone();
455
456    let attr_block_span = Span::merge_many(
457        attributes
458            .first()
459            .map(|x| x.expr.span)
460            .into_iter()
461            .chain(Some(expr.span)),
462    );
463
464    let expr = if attributes.is_empty() {
465        expr
466    } else {
467        Expression::new(
468            working_set,
469            Expr::AttributeBlock(AttributeBlock {
470                attributes,
471                item: Box::new(expr),
472            }),
473            attr_block_span,
474            ty,
475        )
476    };
477
478    (Pipeline::from_vec(vec![expr]), decl)
479}
480
481pub fn parse_extern(
482    working_set: &mut StateWorkingSet,
483    lite_command: &LiteCommand,
484    module_name: Option<&[u8]>,
485) -> Pipeline {
486    let mut attributes = vec![];
487    let mut attribute_vals = vec![];
488
489    for attr_cmd in lite_command.attribute_commands() {
490        let (attr, name) = parse_attribute(working_set, &attr_cmd);
491        if let Some(name) = name {
492            let val = eval_constant(working_set, &attr.expr);
493            match val {
494                Ok(val) => attribute_vals.push((name, val)),
495                Err(e) => working_set.error(e.wrap(working_set, attr.expr.span)),
496            }
497        }
498        attributes.push(attr);
499    }
500
501    let expr = parse_extern_inner(working_set, attribute_vals, lite_command, module_name);
502
503    let ty = expr.ty.clone();
504
505    let attr_block_span = Span::merge_many(
506        attributes
507            .first()
508            .map(|x| x.expr.span)
509            .into_iter()
510            .chain(Some(expr.span)),
511    );
512
513    let expr = if attributes.is_empty() {
514        expr
515    } else {
516        Expression::new(
517            working_set,
518            Expr::AttributeBlock(AttributeBlock {
519                attributes,
520                item: Box::new(expr),
521            }),
522            attr_block_span,
523            ty,
524        )
525    };
526
527    Pipeline::from_vec(vec![expr])
528}
529
530// Returns also the parsed command name and ID
531fn parse_def_inner(
532    working_set: &mut StateWorkingSet,
533    attributes: Vec<(String, Value)>,
534    lite_command: &LiteCommand,
535    module_name: Option<&[u8]>,
536) -> (Expression, Option<(Vec<u8>, DeclId)>) {
537    let spans = lite_command.command_parts();
538
539    let (desc, extra_desc) = working_set.build_desc(&lite_command.comments);
540    let garbage_result =
541        |working_set: &mut StateWorkingSet<'_>| (garbage(working_set, Span::concat(spans)), None);
542
543    // Checking that the function is used with the correct name
544    // Maybe this is not necessary but it is a sanity check
545    // Note: "export def" is treated the same as "def"
546
547    let (name_span, split_id) =
548        if spans.len() > 1 && working_set.get_span_contents(spans[0]) == b"export" {
549            (spans[1], 2)
550        } else {
551            (spans[0], 1)
552        };
553
554    let def_call = working_set.get_span_contents(name_span);
555    if def_call != b"def" {
556        working_set.error(ParseError::UnknownState(
557            "internal error: Wrong call name for def function".into(),
558            Span::concat(spans),
559        ));
560        return garbage_result(working_set);
561    }
562    if let Some(redirection) = lite_command.redirection.as_ref() {
563        working_set.error(redirecting_builtin_error("def", redirection));
564        return garbage_result(working_set);
565    }
566
567    // Parsing the spans and checking that they match the register signature
568    // Using a parsed call makes more sense than checking for how many spans are in the call
569    // Also, by creating a call, it can be checked if it matches the declaration signature
570    //
571    // NOTE: Here we only search for `def` in the permanent state,
572    // since recursively redefining `def` is dangerous,
573    // see https://github.com/nushell/nushell/issues/16586
574    let (call, call_span) = match working_set.permanent_state.find_decl(def_call, &[]) {
575        None => {
576            working_set.error(ParseError::UnknownState(
577                "internal error: def declaration not found".into(),
578                Span::concat(spans),
579            ));
580            return garbage_result(working_set);
581        }
582        Some(decl_id) => {
583            working_set.enter_scope();
584            let (command_spans, rest_spans) = spans.split_at(split_id);
585
586            // Find the first span that is not a flag
587            let mut decl_name_span = None;
588
589            for span in rest_spans {
590                if !working_set.get_span_contents(*span).starts_with(b"-") {
591                    decl_name_span = Some(*span);
592                    break;
593                }
594            }
595
596            if let Some(name_span) = decl_name_span {
597                // Check whether name contains [] or () -- possible missing space error
598                if let Some(err) = detect_params_in_name(working_set, name_span, decl_id) {
599                    working_set.error(err);
600                    return garbage_result(working_set);
601                }
602            }
603
604            let starting_error_count = working_set.parse_errors.len();
605            let ParsedInternalCall {
606                call,
607                output,
608                call_kind,
609            } = parse_internal_call(
610                working_set,
611                Span::concat(command_spans),
612                rest_spans,
613                decl_id,
614                ArgumentParsingLevel::Full,
615            );
616
617            if working_set
618                .parse_errors
619                .get(starting_error_count..)
620                .is_none_or(|new_errors| {
621                    new_errors
622                        .iter()
623                        .all(|e| !matches!(e, ParseError::Unclosed(token, _) if token == "}"))
624                })
625            {
626                working_set.exit_scope();
627            }
628
629            let call_span = Span::concat(spans);
630            let decl = working_set.get_decl(decl_id);
631            let sig = decl.signature();
632
633            // Let's get our block and make sure it has the right signature
634            if let Some(arg) = call.positional_nth(2) {
635                match arg {
636                    Expression {
637                        expr: Expr::Closure(block_id),
638                        ..
639                    } => {
640                        // Custom command bodies' are compiled eagerly
641                        // 1.  `module`s are not compiled, since they aren't ran/don't have any
642                        //     executable code. So `def`s inside modules have to be compiled by
643                        //     themselves.
644                        // 2.  `def` calls in scripts/runnable code don't *run* any code either,
645                        //     they are handled completely by the parser.
646                        compile_block_with_id(working_set, *block_id);
647                        *working_set.get_block_mut(*block_id).signature = sig.clone();
648                    }
649                    _ => working_set.error(ParseError::Expected(
650                        "definition body closure { ... }",
651                        arg.span,
652                    )),
653                }
654            }
655
656            if call_kind != CallKind::Valid {
657                return (
658                    Expression::new(working_set, Expr::Call(call), call_span, output),
659                    None,
660                );
661            }
662
663            (call, call_span)
664        }
665    };
666
667    let Ok(has_env) = has_flag_const(working_set, &call, "env") else {
668        return garbage_result(working_set);
669    };
670    let Ok(has_wrapped) = has_flag_const(working_set, &call, "wrapped") else {
671        return garbage_result(working_set);
672    };
673
674    // All positional arguments must be in the call positional vector by this point
675    let name_expr = call.positional_nth(0).expect("def call already checked");
676    let sig = call.positional_nth(1).expect("def call already checked");
677    let block = call.positional_nth(2).expect("def call already checked");
678
679    let name = if let Some(name) = name_expr.as_string() {
680        if let Some(mod_name) = module_name
681            && name.as_bytes() == mod_name
682        {
683            let name_expr_span = name_expr.span;
684
685            working_set.error(ParseError::NamedAsModule(
686                "command".to_string(),
687                name,
688                "main".to_string(),
689                name_expr_span,
690            ));
691            return (
692                Expression::new(working_set, Expr::Call(call), call_span, Type::Any),
693                None,
694            );
695        }
696
697        name
698    } else {
699        working_set.error(ParseError::UnknownState(
700            "Could not get string from string expression".into(),
701            name_expr.span,
702        ));
703        return garbage_result(working_set);
704    };
705
706    let mut result = None;
707
708    if let (Some(mut signature), Some(block_id)) = (sig.as_signature(), block.as_block()) {
709        if has_wrapped {
710            if let Some(rest) = &signature.rest_positional {
711                if let Some(var_id) = rest.var_id {
712                    let rest_var = &working_set.get_variable(var_id);
713
714                    if rest_var.ty != Type::Any && rest_var.ty != Type::List(Box::new(Type::String))
715                    {
716                        working_set.error(ParseError::TypeMismatchHelp(
717                            Type::List(Box::new(Type::String)),
718                            rest_var.ty.clone(),
719                            rest_var.declaration_span,
720                            format!("...rest-like positional argument used in 'def --wrapped' supports only strings. Change the type annotation of ...{} to 'string'.", &rest.name)));
721
722                        return (
723                            Expression::new(working_set, Expr::Call(call), call_span, Type::Any),
724                            result,
725                        );
726                    }
727                }
728            } else {
729                working_set.error(ParseError::MissingPositional("...rest-like positional argument".to_string(), name_expr.span, "def --wrapped must have a ...rest-like positional argument. Add '...rest: string' to the command's signature.".to_string()));
730
731                return (
732                    Expression::new(working_set, Expr::Call(call), call_span, Type::Any),
733                    result,
734                );
735            }
736        }
737
738        if let Some(decl_id) = working_set.find_predecl(name.as_bytes()) {
739            signature.name.clone_from(&name);
740            if !has_wrapped {
741                *signature = signature.add_help();
742            }
743            signature.description = desc;
744            signature.extra_description = extra_desc;
745            signature.allows_unknown_args = has_wrapped;
746
747            let (attribute_vals, examples) =
748                handle_special_attributes(attributes, working_set, &mut signature);
749
750            let declaration = working_set.get_decl_mut(decl_id);
751
752            *declaration = signature
753                .clone()
754                .into_block_command(block_id, attribute_vals, examples);
755
756            let block = working_set.get_block_mut(block_id);
757            block.signature = signature;
758            block.redirect_env = has_env;
759
760            if block.signature.input_output_types.is_empty() {
761                block
762                    .signature
763                    .input_output_types
764                    .push((Type::Any, Type::Any));
765            }
766
767            let block = working_set.get_block(block_id);
768
769            let typecheck_errors = check_block_input_output(working_set, block);
770
771            working_set
772                .parse_errors
773                .extend_from_slice(&typecheck_errors);
774
775            result = Some((name.as_bytes().to_vec(), decl_id));
776        } else {
777            working_set.error(ParseError::InternalError(
778                "Predeclaration failed to add declaration".into(),
779                name_expr.span,
780            ));
781        };
782    }
783
784    // It's OK if it returns None: The decl was already merged in previous parse pass.
785    working_set.merge_predecl(name.as_bytes());
786
787    (
788        Expression::new(working_set, Expr::Call(call), call_span, Type::Any),
789        result,
790    )
791}
792
793fn parse_extern_inner(
794    working_set: &mut StateWorkingSet,
795    attributes: Vec<(String, Value)>,
796    lite_command: &LiteCommand,
797    module_name: Option<&[u8]>,
798) -> Expression {
799    let spans = lite_command.command_parts();
800
801    let (description, extra_description) = working_set.build_desc(&lite_command.comments);
802
803    // Checking that the function is used with the correct name
804    // Maybe this is not necessary but it is a sanity check
805
806    let (name_span, split_id) =
807        if spans.len() > 1 && (working_set.get_span_contents(spans[0]) == b"export") {
808            (spans[1], 2)
809        } else {
810            (spans[0], 1)
811        };
812
813    let extern_call = working_set.get_span_contents(name_span);
814    if extern_call != b"extern" {
815        working_set.error(ParseError::UnknownState(
816            "internal error: Wrong call name for extern command".into(),
817            Span::concat(spans),
818        ));
819        return garbage(working_set, Span::concat(spans));
820    }
821    if let Some(redirection) = lite_command.redirection.as_ref() {
822        working_set.error(redirecting_builtin_error("extern", redirection));
823        return garbage(working_set, Span::concat(spans));
824    }
825
826    // Parsing the spans and checking that they match the register signature
827    // Using a parsed call makes more sense than checking for how many spans are in the call
828    // Also, by creating a call, it can be checked if it matches the declaration signature
829    //
830    // NOTE: Here we only search for `extern` in the permanent state,
831    // since recursively redefining `extern` is dangerous,
832    // see https://github.com/nushell/nushell/issues/16586
833    let (call, call_span) = match working_set.permanent().find_decl(extern_call, &[]) {
834        None => {
835            working_set.error(ParseError::UnknownState(
836                "internal error: def declaration not found".into(),
837                Span::concat(spans),
838            ));
839            return garbage(working_set, Span::concat(spans));
840        }
841        Some(decl_id) => {
842            working_set.enter_scope();
843
844            let (command_spans, rest_spans) = spans.split_at(split_id);
845
846            if let Some(name_span) = rest_spans.first()
847                && let Some(err) = detect_params_in_name(working_set, *name_span, decl_id)
848            {
849                working_set.error(err);
850                return garbage(working_set, Span::concat(spans));
851            }
852
853            let ParsedInternalCall { call, .. } = parse_internal_call(
854                working_set,
855                Span::concat(command_spans),
856                rest_spans,
857                decl_id,
858                ArgumentParsingLevel::Full,
859            );
860            working_set.exit_scope();
861
862            let call_span = Span::concat(spans);
863
864            (call, call_span)
865        }
866    };
867    let name_expr = call.positional_nth(0);
868    let sig = call.positional_nth(1);
869    let body = call.positional_nth(2);
870
871    if let (Some(name_expr), Some(sig)) = (name_expr, sig) {
872        if let (Some(name), Some(mut signature)) = (&name_expr.as_string(), sig.as_signature()) {
873            if let Some(mod_name) = module_name
874                && name.as_bytes() == mod_name
875            {
876                let name_expr_span = name_expr.span;
877                working_set.error(ParseError::NamedAsModule(
878                    "known external".to_string(),
879                    name.clone(),
880                    "main".to_string(),
881                    name_expr_span,
882                ));
883                return Expression::new(working_set, Expr::Call(call), call_span, Type::Any);
884            }
885
886            if let Some(decl_id) = working_set.find_predecl(name.as_bytes()) {
887                let external_name = if let Some(mod_name) = module_name {
888                    if name.as_bytes() == b"main" {
889                        String::from_utf8_lossy(mod_name).to_string()
890                    } else {
891                        name.clone()
892                    }
893                } else {
894                    name.clone()
895                };
896
897                signature.name = external_name;
898                signature.description = description;
899                signature.extra_description = extra_description;
900                signature.allows_unknown_args = true;
901
902                let (attribute_vals, examples) =
903                    handle_special_attributes(attributes, working_set, &mut signature);
904
905                let declaration = working_set.get_decl_mut(decl_id);
906
907                if let Some(block_id) = body.and_then(|x| x.as_block()) {
908                    if signature.rest_positional.is_none() {
909                        working_set.error(ParseError::InternalError(
910                            "Extern block must have a rest positional argument".into(),
911                            name_expr.span,
912                        ));
913                    } else {
914                        *declaration = signature.clone().into_block_command(
915                            block_id,
916                            attribute_vals,
917                            examples,
918                        );
919
920                        working_set.get_block_mut(block_id).signature = signature;
921                    }
922                } else {
923                    if signature.rest_positional.is_none() {
924                        // Make sure that a known external takes rest args with ExternalArgument
925                        // shape
926                        *signature = signature.rest(
927                            "args",
928                            SyntaxShape::ExternalArgument,
929                            "All other arguments to the command.",
930                        );
931                    }
932
933                    let decl = KnownExternal {
934                        signature,
935                        attributes: attribute_vals,
936                        examples,
937                        span: call_span,
938                    };
939
940                    *declaration = Box::new(decl);
941                }
942            } else {
943                working_set.error(ParseError::InternalError(
944                    "Predeclaration failed to add declaration".into(),
945                    spans[split_id],
946                ));
947            };
948        }
949        if let Some(name) = name_expr.as_string() {
950            // It's OK if it returns None: The decl was already merged in previous parse pass.
951            working_set.merge_predecl(name.as_bytes());
952        } else {
953            working_set.error(ParseError::UnknownState(
954                "Could not get string from string expression".into(),
955                name_expr.span,
956            ));
957        }
958    }
959
960    Expression::new(working_set, Expr::Call(call), call_span, Type::Any)
961}
962
963fn handle_special_attributes(
964    attributes: Vec<(String, Value)>,
965    working_set: &mut StateWorkingSet<'_>,
966    signature: &mut Signature,
967) -> (Vec<(String, Value)>, Vec<CustomExample>) {
968    let mut attribute_vals = vec![];
969    let mut examples = vec![];
970    let mut search_terms = vec![];
971    let mut category = String::new();
972
973    for (name, value) in attributes {
974        let val_span = value.span();
975        match name.as_str() {
976            "example" => match CustomExample::from_value(value) {
977                Ok(example) => examples.push(example),
978                Err(_) => {
979                    let e = ShellError::Generic(
980                        GenericError::new(
981                            "nu::shell::invalid_example",
982                            "Value couldn't be converted to an example",
983                            val_span,
984                        )
985                        .with_help("Is `attr example` shadowed?"),
986                    );
987                    working_set.error(e.wrap(working_set, val_span));
988                }
989            },
990            "search-terms" => match <Vec<String>>::from_value(value) {
991                Ok(mut terms) => {
992                    search_terms.append(&mut terms);
993                }
994                Err(_) => {
995                    let e = ShellError::Generic(
996                        GenericError::new(
997                            "nu::shell::invalid_search_terms",
998                            "Value couldn't be converted to search-terms",
999                            val_span,
1000                        )
1001                        .with_help("Is `attr search-terms` shadowed?"),
1002                    );
1003                    working_set.error(e.wrap(working_set, val_span));
1004                }
1005            },
1006            "category" => match <String>::from_value(value) {
1007                Ok(term) => {
1008                    category.push_str(&term);
1009                }
1010                Err(_) => {
1011                    let e = ShellError::Generic(
1012                        GenericError::new(
1013                            "nu::shell::invalid_category",
1014                            "Value couldn't be converted to category",
1015                            val_span,
1016                        )
1017                        .with_help("Is `attr category` shadowed?"),
1018                    );
1019                    working_set.error(e.wrap(working_set, val_span));
1020                }
1021            },
1022            "complete" => match <Spanned<String>>::from_value(value) {
1023                Ok(Spanned { item, span }) => {
1024                    if let Some(decl) = working_set.find_decl(item.as_bytes()) {
1025                        // TODO: Enforce command signature? Not before settling on a unified
1026                        // custom completion api
1027                        signature.complete = Some(CommandWideCompleter::Command(decl));
1028                    } else {
1029                        working_set.error(ParseError::UnknownCommand(span));
1030                    }
1031                }
1032                Err(_) => {
1033                    let e = ShellError::Generic(
1034                        GenericError::new(
1035                            "nu::shell::invalid_completer",
1036                            "Value couldn't be converted to a completer",
1037                            val_span,
1038                        )
1039                        .with_help("Is `attr complete` shadowed?"),
1040                    );
1041                    working_set.error(e.wrap(working_set, val_span));
1042                }
1043            },
1044            "complete external" => match value {
1045                Value::Nothing { .. } => {
1046                    signature.complete = Some(CommandWideCompleter::External);
1047                }
1048                _ => {
1049                    let e = ShellError::Generic(
1050                        GenericError::new(
1051                            "nu::shell::invalid_completer",
1052                            "This attribute shouldn't return anything",
1053                            val_span,
1054                        )
1055                        .with_help("Is `attr complete` shadowed?"),
1056                    );
1057                    working_set.error(e.wrap(working_set, val_span));
1058                }
1059            },
1060            _ => {
1061                attribute_vals.push((name, value));
1062            }
1063        }
1064    }
1065
1066    signature.search_terms = search_terms;
1067    signature.category = category_from_string(&category);
1068
1069    (attribute_vals, examples)
1070}
1071
1072fn check_alias_name<'a>(working_set: &mut StateWorkingSet, spans: &'a [Span]) -> Option<&'a Span> {
1073    let command_len = if !spans.is_empty() {
1074        if working_set.get_span_contents(spans[0]) == b"export" {
1075            2
1076        } else {
1077            1
1078        }
1079    } else {
1080        return None;
1081    };
1082
1083    if spans.len() == command_len {
1084        None
1085    } else if spans.len() < command_len + 3 {
1086        if working_set.get_span_contents(spans[command_len]) == b"=" {
1087            let name = String::from_utf8_lossy(
1088                working_set.get_span_contents(Span::concat(&spans[..command_len])),
1089            );
1090            working_set.error(ParseError::AssignmentMismatch(
1091                format!("{name} missing name"),
1092                "missing name".into(),
1093                spans[command_len],
1094            ));
1095            Some(&spans[command_len])
1096        } else {
1097            None
1098        }
1099    } else if working_set.get_span_contents(spans[command_len + 1]) != b"=" {
1100        let name = String::from_utf8_lossy(
1101            working_set.get_span_contents(Span::concat(&spans[..command_len])),
1102        );
1103        working_set.error(ParseError::AssignmentMismatch(
1104            format!("{name} missing sign"),
1105            "missing equal sign".into(),
1106            spans[command_len + 1],
1107        ));
1108        Some(&spans[command_len + 1])
1109    } else {
1110        None
1111    }
1112}
1113
1114pub fn parse_alias(
1115    working_set: &mut StateWorkingSet,
1116    lite_command: &LiteCommand,
1117    module_name: Option<&[u8]>,
1118) -> Pipeline {
1119    let spans = &lite_command.parts;
1120
1121    let (name_span, split_id) =
1122        if spans.len() > 1 && working_set.get_span_contents(spans[0]) == b"export" {
1123            (spans[1], 2)
1124        } else {
1125            (spans[0], 1)
1126        };
1127
1128    let name = working_set.get_span_contents(name_span);
1129
1130    if name != b"alias" {
1131        working_set.error(ParseError::InternalError(
1132            "Alias statement unparsable".into(),
1133            Span::concat(spans),
1134        ));
1135        return garbage_pipeline(working_set, spans);
1136    }
1137    if let Some(redirection) = lite_command.redirection.as_ref() {
1138        working_set.error(redirecting_builtin_error("alias", redirection));
1139        return garbage_pipeline(working_set, spans);
1140    }
1141
1142    if let Some(span) = check_alias_name(working_set, spans) {
1143        return Pipeline::from_vec(vec![garbage(working_set, *span)]);
1144    }
1145
1146    if let Some(decl_id) = working_set.find_decl(b"alias") {
1147        let (command_spans, rest_spans) = spans.split_at(split_id);
1148
1149        let original_starting_error_count = working_set.parse_errors.len();
1150
1151        let ParsedInternalCall {
1152            call: alias_call,
1153            output,
1154            call_kind,
1155        } = parse_internal_call(
1156            working_set,
1157            Span::concat(command_spans),
1158            rest_spans,
1159            decl_id,
1160            ArgumentParsingLevel::Full,
1161        );
1162
1163        working_set
1164            .parse_errors
1165            .truncate(original_starting_error_count);
1166
1167        let alias_pipeline = Pipeline::from_vec(vec![Expression::new(
1168            working_set,
1169            Expr::Call(alias_call.clone()),
1170            Span::concat(spans),
1171            output,
1172        )]);
1173
1174        if call_kind == CallKind::Help {
1175            return alias_pipeline;
1176        }
1177
1178        let Some(alias_name_expr) = alias_call.positional_nth(0) else {
1179            working_set.error(ParseError::UnknownState(
1180                "Missing positional after call check".to_string(),
1181                Span::concat(spans),
1182            ));
1183            return garbage_pipeline(working_set, spans);
1184        };
1185
1186        let alias_name = if let Some(name) = alias_name_expr.as_string() {
1187            if name.contains('#')
1188                || name.contains('^')
1189                || name.contains('%')
1190                || name.parse::<bytesize::ByteSize>().is_ok()
1191                || name.parse::<f64>().is_ok()
1192            {
1193                working_set.error(ParseError::AliasNotValid(alias_name_expr.span));
1194                return garbage_pipeline(working_set, spans);
1195            } else {
1196                name
1197            }
1198        } else {
1199            working_set.error(ParseError::AliasNotValid(alias_name_expr.span));
1200            return garbage_pipeline(working_set, spans);
1201        };
1202
1203        if spans.len() >= split_id + 3 {
1204            if let Some(mod_name) = module_name {
1205                if alias_name.as_bytes() == mod_name {
1206                    working_set.error(ParseError::NamedAsModule(
1207                        "alias".to_string(),
1208                        alias_name,
1209                        "main".to_string(),
1210                        spans[split_id],
1211                    ));
1212
1213                    return alias_pipeline;
1214                }
1215
1216                if alias_name == "main" {
1217                    working_set.error(ParseError::ExportMainAliasNotAllowed(spans[split_id]));
1218                    return alias_pipeline;
1219                }
1220            }
1221
1222            let _equals = working_set.get_span_contents(spans[split_id + 1]);
1223
1224            let replacement_spans = &spans[(split_id + 2)..];
1225            let first_bytes = working_set.get_span_contents(replacement_spans[0]);
1226
1227            if first_bytes != b"if"
1228                && first_bytes != b"match"
1229                && is_math_expression_like(working_set, replacement_spans[0])
1230            {
1231                let starting_error_count = working_set.parse_errors.len();
1232                let expr = parse_expression(working_set, replacement_spans);
1233                working_set.parse_errors.truncate(starting_error_count);
1234
1235                working_set.error(ParseError::CantAliasExpression(
1236                    expr.expr.description().to_string(),
1237                    replacement_spans[0],
1238                ));
1239                return alias_pipeline;
1240            }
1241
1242            let starting_error_count = working_set.parse_errors.len();
1243            working_set.search_predecls = false;
1244
1245            let expr = parse_call(working_set, replacement_spans, replacement_spans[0]);
1246
1247            working_set.search_predecls = true;
1248
1249            if starting_error_count != working_set.parse_errors.len()
1250                && let Some(e) = working_set.parse_errors.get(starting_error_count)
1251            {
1252                if let ParseError::MissingPositional(..)
1253                | ParseError::MissingRequiredFlag(..)
1254                | ParseError::MissingFlagParam(..) = e
1255                {
1256                    working_set
1257                        .parse_errors
1258                        .truncate(original_starting_error_count);
1259                    // ignore missing required positional
1260                } else {
1261                    return garbage_pipeline(working_set, replacement_spans);
1262                }
1263            }
1264
1265            let (command, wrapped_call) = match expr {
1266                Expression {
1267                    expr: Expr::Call(ref rhs_call),
1268                    ..
1269                } => {
1270                    let cmd = working_set.get_decl(rhs_call.decl_id);
1271
1272                    if cmd.is_keyword()
1273                        && !ALIASABLE_PARSER_KEYWORDS.contains(&cmd.name().as_bytes())
1274                    {
1275                        working_set.error(ParseError::CantAliasKeyword(
1276                            ALIASABLE_PARSER_KEYWORDS
1277                                .iter()
1278                                .map(|bytes| String::from_utf8_lossy(bytes).to_string())
1279                                .collect::<Vec<String>>()
1280                                .join(", "),
1281                            rhs_call.head,
1282                        ));
1283                        return alias_pipeline;
1284                    }
1285
1286                    (Some(cmd.clone_box()), expr)
1287                }
1288                Expression {
1289                    expr: Expr::ExternalCall(..),
1290                    ..
1291                } => (None, expr),
1292                _ => {
1293                    working_set.error(ParseError::InternalError(
1294                        "Parsed call not a call".into(),
1295                        expr.span,
1296                    ));
1297                    return alias_pipeline;
1298                }
1299            };
1300
1301            // Tries to build a useful description string
1302            let (description, extra_description) = match lite_command.comments.is_empty() {
1303                // First from comments, if any are present
1304                false => working_set.build_desc(&lite_command.comments),
1305                // Then from the command itself
1306                true => match alias_call.arguments.get(1) {
1307                    Some(Argument::Positional(Expression {
1308                        expr: Expr::Keyword(kw),
1309                        ..
1310                    })) => {
1311                        let aliased = working_set.get_span_contents(kw.expr.span);
1312                        (
1313                            format!("Alias for `{}`", String::from_utf8_lossy(aliased)),
1314                            String::new(),
1315                        )
1316                    }
1317                    // Then with a default.
1318                    _ => ("User declared alias".into(), String::new()),
1319                },
1320            };
1321
1322            let decl = Alias {
1323                name: alias_name,
1324                command,
1325                wrapped_call,
1326                description,
1327                extra_description,
1328            };
1329
1330            working_set.add_decl(Box::new(decl));
1331        }
1332
1333        // special case for `alias foo=bar`
1334        if spans.len() == 2 && working_set.get_span_contents(spans[1]).contains(&b'=') {
1335            let arg = String::from_utf8_lossy(working_set.get_span_contents(spans[1]));
1336
1337            // split at '='.  Note that the output must never be None, the
1338            // `unwrap` is just to avoid the possibility of panic, if the
1339            // invariant is broken.
1340            let (name, initial_value) = arg.split_once('=').unwrap_or((&arg, ""));
1341
1342            let name = if name.is_empty() { "{name}" } else { name };
1343            let initial_value = if initial_value.is_empty() {
1344                "{initial_value}"
1345            } else {
1346                initial_value
1347            };
1348
1349            working_set.error(ParseError::IncorrectValue(
1350                "alias argument".into(),
1351                spans[1],
1352                format!("Make sure to put spaces around '=': alias {name} = {initial_value}"),
1353            ))
1354        } else if spans.len() < 4 {
1355            working_set.error(ParseError::IncorrectValue(
1356                "Incomplete alias".into(),
1357                Span::concat(&spans[..split_id]),
1358                "incomplete alias".into(),
1359            ));
1360        }
1361
1362        return alias_pipeline;
1363    }
1364
1365    working_set.error(ParseError::InternalError(
1366        "Alias statement unparsable".into(),
1367        Span::concat(spans),
1368    ));
1369
1370    garbage_pipeline(working_set, spans)
1371}
1372
1373// Return false if command `export xxx` not found
1374// TODO: Rather than this, handle `export xxx` correctly in `parse_xxx`
1375fn warp_export_call(
1376    working_set: &mut StateWorkingSet,
1377    pipeline: &mut Pipeline,
1378    full_name: &str,
1379    spans: &[Span],
1380) -> bool {
1381    let Some(export_decl_id) = working_set.find_decl(full_name.as_bytes()) else {
1382        let error_span = spans.first().cloned().unwrap_or(Span::unknown());
1383        working_set.error(ParseError::InternalError(
1384            format!("missing '{full_name}' command"),
1385            error_span,
1386        ));
1387        return false;
1388    };
1389    match pipeline.elements.first_mut().map(|e| {
1390        e.expr.span = Span::concat(spans);
1391        &mut e.expr.expr
1392    }) {
1393        Some(Expr::Call(def_call)) => {
1394            def_call.head = Span::concat(&spans[0..=1]);
1395            def_call.decl_id = export_decl_id;
1396            return true;
1397        }
1398        Some(Expr::AttributeBlock(ab)) => {
1399            if let Expr::Call(def_call) = &mut ab.item.expr {
1400                def_call.decl_id = export_decl_id;
1401                return true;
1402            }
1403        }
1404        _ => {}
1405    };
1406    working_set.error(ParseError::InternalError(
1407        "unexpected output from parsing a definition".into(),
1408        Span::concat(&spans[1..]),
1409    ));
1410    true
1411}
1412
1413// This one will trigger if `export` appears during eval, e.g., in a script
1414pub fn parse_export_in_block(
1415    working_set: &mut StateWorkingSet,
1416    lite_command: &LiteCommand,
1417) -> Pipeline {
1418    let parts = lite_command.command_parts();
1419    let full_name = if parts.len() > 1 {
1420        let sub = working_set.get_span_contents(parts[1]);
1421        match sub {
1422            b"alias" => "export alias",
1423            b"def" => "export def",
1424            b"extern" => "export extern",
1425            b"use" => "export use",
1426            b"module" => "export module",
1427            b"const" => "export const",
1428            _ => "export",
1429        }
1430    } else {
1431        "export"
1432    };
1433
1434    if let Some(redirection) = lite_command.redirection.as_ref() {
1435        working_set.error(redirecting_builtin_error(full_name, redirection));
1436        return garbage_pipeline(working_set, &lite_command.parts);
1437    }
1438
1439    let mut pipeline = match full_name {
1440        // `parse_def` and `parse_extern` work both with and without attributes
1441        "export def" => parse_def(working_set, lite_command, None).0,
1442        "export extern" => parse_extern(working_set, lite_command, None),
1443        // Other definitions can't have attributes, so we handle attributes here with parse_attribute_block
1444        _ if lite_command.has_attributes() => parse_attribute_block(working_set, lite_command),
1445        "export alias" => parse_alias(working_set, lite_command, None),
1446        "export const" => parse_const(working_set, &lite_command.parts[1..]).0,
1447        "export use" => parse_use(working_set, lite_command, None).0,
1448        "export module" => parse_module(working_set, lite_command, None).0,
1449        _ => {
1450            if let Some(decl_id) = working_set.find_decl(full_name.as_bytes()) {
1451                let starting_error_count = working_set.parse_errors.len();
1452                let ParsedInternalCall {
1453                    call,
1454                    output,
1455                    call_kind,
1456                } = parse_internal_call(
1457                    working_set,
1458                    parts[0],
1459                    &parts[1..],
1460                    decl_id,
1461                    ArgumentParsingLevel::Full,
1462                );
1463
1464                if call_kind != CallKind::Valid {
1465                    return Pipeline::from_vec(vec![Expression::new(
1466                        working_set,
1467                        Expr::Call(call),
1468                        Span::concat(&lite_command.parts),
1469                        output,
1470                    )]);
1471                }
1472                // don't need errors generated by parse_internal_call
1473                working_set.parse_errors.truncate(starting_error_count);
1474                working_set.error(ParseError::UnexpectedKeyword(
1475                    full_name.into(),
1476                    lite_command.parts[0],
1477                ));
1478            } else {
1479                working_set.error(ParseError::UnknownState(
1480                    format!("internal error: '{full_name}' declaration not found",),
1481                    Span::concat(&lite_command.parts),
1482                ));
1483            };
1484            garbage_pipeline(working_set, &lite_command.parts)
1485        }
1486    };
1487
1488    // HACK: This is for different messages of e.g. `export def --help` and `def --help`,
1489    warp_export_call(working_set, &mut pipeline, full_name, &lite_command.parts);
1490    pipeline
1491}
1492
1493// This one will trigger only in a module
1494pub fn parse_export_in_module(
1495    working_set: &mut StateWorkingSet,
1496    lite_command: &LiteCommand,
1497    module_name: &[u8],
1498    parent_module: &mut Module,
1499) -> (Pipeline, Vec<Exportable>) {
1500    let spans = lite_command.command_parts();
1501
1502    let export_span = if let Some(sp) = spans.first() {
1503        if working_set.get_span_contents(*sp) != b"export" {
1504            working_set.error(ParseError::UnknownState(
1505                "expected export statement".into(),
1506                Span::concat(spans),
1507            ));
1508            return (garbage_pipeline(working_set, spans), vec![]);
1509        }
1510
1511        *sp
1512    } else {
1513        working_set.error(ParseError::UnknownState(
1514            "got empty input for parsing export statement".into(),
1515            Span::concat(spans),
1516        ));
1517        return (garbage_pipeline(working_set, spans), vec![]);
1518    };
1519
1520    let (pipeline, exportables) = if let Some(kw_span) = spans.get(1) {
1521        let kw_name = working_set.get_span_contents(*kw_span);
1522        match kw_name {
1523            // `parse_def` and `parse_extern` work both with and without attributes
1524            b"def" => {
1525                let (mut pipeline, cmd_result) =
1526                    parse_def(working_set, lite_command, Some(module_name));
1527
1528                let mut result = vec![];
1529
1530                if let Some((decl_name, decl_id)) = cmd_result {
1531                    result.push(Exportable::Decl {
1532                        name: decl_name.to_vec(),
1533                        id: decl_id,
1534                    });
1535                }
1536
1537                // Trying to warp the 'def' call into the 'export def' in a very clumsy way
1538                if !warp_export_call(working_set, &mut pipeline, "export def", spans) {
1539                    return (garbage_pipeline(working_set, spans), vec![]);
1540                }
1541
1542                (pipeline, result)
1543            }
1544            b"extern" => {
1545                let mut pipeline = parse_extern(working_set, lite_command, Some(module_name));
1546
1547                // Trying to warp the 'extern' call into the 'export extern' in a very clumsy way
1548                if !warp_export_call(working_set, &mut pipeline, "export extern", spans) {
1549                    return (garbage_pipeline(working_set, spans), vec![]);
1550                }
1551
1552                let mut result = vec![];
1553
1554                let decl_name = match spans.get(2) {
1555                    Some(span) => working_set.get_span_contents(*span),
1556                    None => &[],
1557                };
1558                let decl_name = trim_quotes(decl_name);
1559
1560                if let Some(decl_id) = working_set.find_decl(decl_name) {
1561                    result.push(Exportable::Decl {
1562                        name: decl_name.to_vec(),
1563                        id: decl_id,
1564                    });
1565                } else {
1566                    working_set.error(ParseError::InternalError(
1567                        "failed to find added declaration".into(),
1568                        Span::concat(&spans[1..]),
1569                    ));
1570                }
1571
1572                (pipeline, result)
1573            }
1574            // Other definitions can't have attributes, so we handle attributes here with parse_attribute_block
1575            _ if lite_command.has_attributes() => {
1576                (parse_attribute_block(working_set, lite_command), vec![])
1577            }
1578            b"alias" => {
1579                let lite_command = LiteCommand {
1580                    comments: lite_command.comments.clone(),
1581                    parts: spans[1..].to_vec(),
1582                    pipe: lite_command.pipe,
1583                    redirection: lite_command.redirection.clone(),
1584                    attribute_idx: vec![],
1585                };
1586                let mut pipeline = parse_alias(working_set, &lite_command, Some(module_name));
1587
1588                // Trying to warp the 'alias' call into the 'export alias' in a very clumsy way
1589                if !warp_export_call(working_set, &mut pipeline, "export alias", spans) {
1590                    return (garbage_pipeline(working_set, spans), vec![]);
1591                }
1592
1593                let mut result = vec![];
1594
1595                let alias_name = match spans.get(2) {
1596                    Some(span) => working_set.get_span_contents(*span),
1597                    None => &[],
1598                };
1599                let alias_name = trim_quotes(alias_name);
1600
1601                if let Some(alias_id) = working_set.find_decl(alias_name) {
1602                    result.push(Exportable::Decl {
1603                        name: alias_name.to_vec(),
1604                        id: alias_id,
1605                    });
1606                } else {
1607                    working_set.error(ParseError::InternalError(
1608                        "failed to find added alias".into(),
1609                        Span::concat(&spans[1..]),
1610                    ));
1611                }
1612
1613                (pipeline, result)
1614            }
1615            b"use" => {
1616                let lite_command = LiteCommand {
1617                    comments: lite_command.comments.clone(),
1618                    parts: spans[1..].to_vec(),
1619                    pipe: lite_command.pipe,
1620                    redirection: lite_command.redirection.clone(),
1621                    attribute_idx: vec![],
1622                };
1623                let (mut pipeline, exportables) =
1624                    parse_use(working_set, &lite_command, Some(parent_module));
1625
1626                // Trying to warp the 'use' call into the 'export use' in a very clumsy way
1627                if !warp_export_call(working_set, &mut pipeline, "export use", spans) {
1628                    return (garbage_pipeline(working_set, spans), vec![]);
1629                }
1630
1631                (pipeline, exportables)
1632            }
1633            b"module" => {
1634                let (mut pipeline, maybe_module_id) =
1635                    parse_module(working_set, lite_command, Some(module_name));
1636
1637                // Trying to warp the 'module' call into the 'export module' in a very clumsy way
1638                if !warp_export_call(working_set, &mut pipeline, "export module", spans) {
1639                    return (garbage_pipeline(working_set, spans), vec![]);
1640                }
1641
1642                let mut result = vec![];
1643
1644                if let Some(module_name_span) = spans.get(2) {
1645                    let module_name = working_set.get_span_contents(*module_name_span);
1646                    let module_name = trim_quotes(module_name);
1647
1648                    if let Some(module_id) = maybe_module_id {
1649                        result.push(Exportable::Module {
1650                            name: working_set.get_module(module_id).name(),
1651                            id: module_id,
1652                        });
1653                    } else {
1654                        working_set.error(ParseError::InternalError(
1655                            format!(
1656                                "failed to find added module '{}'",
1657                                String::from_utf8_lossy(module_name)
1658                            ),
1659                            Span::concat(&spans[1..]),
1660                        ));
1661                    }
1662                }
1663
1664                (pipeline, result)
1665            }
1666            b"const" => {
1667                let (mut pipeline, var_name_span) = parse_const(working_set, &spans[1..]);
1668
1669                // Trying to warp the 'const' call into the 'export const' in a very clumsy way
1670                if !warp_export_call(working_set, &mut pipeline, "export const", spans) {
1671                    return (garbage_pipeline(working_set, spans), vec![]);
1672                }
1673
1674                let mut result = vec![];
1675
1676                if let Some(var_name_span) = var_name_span {
1677                    let var_name = working_set.get_span_contents(var_name_span);
1678                    let var_name = trim_quotes(var_name);
1679
1680                    if let Some(var_id) = working_set.find_variable(var_name) {
1681                        if let Err(err) = working_set.get_constant(var_id) {
1682                            working_set.error(err);
1683                        } else {
1684                            result.push(Exportable::VarDecl {
1685                                name: var_name.to_vec(),
1686                                id: var_id,
1687                            });
1688                        }
1689                    } else {
1690                        working_set.error(ParseError::InternalError(
1691                            "failed to find added variable".into(),
1692                            Span::concat(&spans[1..]),
1693                        ));
1694                    }
1695                }
1696
1697                (pipeline, result)
1698            }
1699            _ => {
1700                working_set.error(ParseError::Expected(
1701                    "def, alias, use, module, const or extern keyword",
1702                    spans[1],
1703                ));
1704
1705                (garbage_pipeline(working_set, spans), vec![])
1706            }
1707        }
1708    } else {
1709        working_set.error(ParseError::MissingPositional(
1710            "def, alias, use, module, const or extern keyword".to_string(),
1711            Span::new(export_span.end, export_span.end),
1712            "def, alias, use, module, const or extern keyword".to_string(),
1713        ));
1714
1715        (garbage_pipeline(working_set, spans), vec![])
1716    };
1717
1718    (pipeline, exportables)
1719}
1720
1721pub fn parse_export_env(
1722    working_set: &mut StateWorkingSet,
1723    spans: &[Span],
1724) -> (Pipeline, Option<BlockId>) {
1725    if !spans.is_empty() && working_set.get_span_contents(spans[0]) != b"export-env" {
1726        working_set.error(ParseError::UnknownState(
1727            "internal error: Wrong call name for 'export-env' command".into(),
1728            Span::concat(spans),
1729        ));
1730        return (garbage_pipeline(working_set, spans), None);
1731    }
1732
1733    if spans.len() < 2 {
1734        working_set.error(ParseError::MissingPositional(
1735            "block".into(),
1736            Span::concat(spans),
1737            "export-env <block>".into(),
1738        ));
1739        return (garbage_pipeline(working_set, spans), None);
1740    }
1741
1742    let call = match working_set.find_decl(b"export-env") {
1743        Some(decl_id) => {
1744            let ParsedInternalCall {
1745                call,
1746                output,
1747                call_kind,
1748            } = parse_internal_call(
1749                working_set,
1750                spans[0],
1751                &[spans[1]],
1752                decl_id,
1753                ArgumentParsingLevel::Full,
1754            );
1755
1756            if call_kind != CallKind::Valid {
1757                return (
1758                    Pipeline::from_vec(vec![Expression::new(
1759                        working_set,
1760                        Expr::Call(call),
1761                        Span::concat(spans),
1762                        output,
1763                    )]),
1764                    None,
1765                );
1766            }
1767
1768            call
1769        }
1770        None => {
1771            working_set.error(ParseError::UnknownState(
1772                "internal error: 'export-env' declaration not found".into(),
1773                Span::concat(spans),
1774            ));
1775            return (garbage_pipeline(working_set, spans), None);
1776        }
1777    };
1778
1779    let block_id = if let Some(block) = call.positional_nth(0) {
1780        if let Some(block_id) = block.as_block() {
1781            block_id
1782        } else {
1783            working_set.error(ParseError::UnknownState(
1784                "internal error: 'export-env' block is not a block".into(),
1785                block.span,
1786            ));
1787            return (garbage_pipeline(working_set, spans), None);
1788        }
1789    } else {
1790        working_set.error(ParseError::UnknownState(
1791            "internal error: 'export-env' block is missing".into(),
1792            Span::concat(spans),
1793        ));
1794        return (garbage_pipeline(working_set, spans), None);
1795    };
1796
1797    let pipeline = Pipeline::from_vec(vec![Expression::new(
1798        working_set,
1799        Expr::Call(call),
1800        Span::concat(spans),
1801        Type::Any,
1802    )]);
1803
1804    // Since modules are parser constructs, `export-env` blocks don't have anything to drive their
1805    // compilation when they are inside modules
1806    //
1807    // When they appear not inside module definitions but inside runnable code (script, `def`
1808    // block, etc), their body is used more or less like a closure, which are also compiled eagerly
1809    //
1810    // This handles both of these cases
1811    compile_block_with_id(working_set, block_id);
1812
1813    (pipeline, Some(block_id))
1814}
1815
1816fn collect_first_comments(tokens: &[Token]) -> Vec<Span> {
1817    let mut comments = vec![];
1818
1819    let mut tokens_iter = tokens.iter().peekable();
1820    while let Some(token) = tokens_iter.next() {
1821        match token.contents {
1822            TokenContents::Comment => {
1823                comments.push(token.span);
1824            }
1825            TokenContents::Eol => {
1826                if let Some(Token {
1827                    contents: TokenContents::Eol,
1828                    ..
1829                }) = tokens_iter.peek()
1830                    && !comments.is_empty()
1831                {
1832                    break;
1833                }
1834            }
1835            _ => {
1836                comments.clear();
1837                break;
1838            }
1839        }
1840    }
1841
1842    comments
1843}
1844
1845pub fn parse_module_block(
1846    working_set: &mut StateWorkingSet,
1847    span: Span,
1848    module_name: &[u8],
1849) -> (Block, Module, Vec<Span>) {
1850    working_set.enter_scope();
1851
1852    let source = working_set.get_span_contents(span);
1853
1854    let (output, err) = lex(source, span.start, &[], &[], false);
1855    if let Some(err) = err {
1856        working_set.error(err)
1857    }
1858
1859    let module_comments = collect_first_comments(&output);
1860
1861    let (output, err) = lite_parse(&output, working_set);
1862    if let Some(err) = err {
1863        working_set.error(err)
1864    }
1865
1866    for pipeline in &output.block {
1867        if pipeline.commands.len() == 1 {
1868            parse_def_predecl(working_set, pipeline.commands[0].command_parts());
1869        }
1870    }
1871
1872    let mut module = Module::from_span(module_name.to_vec(), span);
1873
1874    let mut block = Block::new_with_capacity(output.block.len());
1875    block.span = Some(span);
1876
1877    for pipeline in output.block.iter() {
1878        if pipeline.commands.len() == 1 {
1879            let command = &pipeline.commands[0];
1880
1881            let name = command
1882                .command_parts()
1883                .first()
1884                .map(|s| working_set.get_span_contents(*s))
1885                .unwrap_or(b"");
1886
1887            match name {
1888                // `parse_def` and `parse_extern` work both with and without attributes
1889                b"def" => {
1890                    block.pipelines.push(
1891                        parse_def(
1892                            working_set,
1893                            command,
1894                            None, // using commands named as the module locally is OK
1895                        )
1896                        .0,
1897                    )
1898                }
1899                b"extern" => block
1900                    .pipelines
1901                    .push(parse_extern(working_set, command, None)),
1902                // `parse_export_in_module` also handles attributes by itself
1903                b"export" => {
1904                    let (pipe, exportables) =
1905                        parse_export_in_module(working_set, command, module_name, &mut module);
1906
1907                    for exportable in exportables {
1908                        match exportable {
1909                            Exportable::Decl { name, id } => {
1910                                if &name == b"main" {
1911                                    if module.main.is_some() {
1912                                        let err_span = if !pipe.elements.is_empty() {
1913                                            if let Expr::Call(call) = &pipe.elements[0].expr.expr {
1914                                                call.head
1915                                            } else {
1916                                                pipe.elements[0].expr.span
1917                                            }
1918                                        } else {
1919                                            span
1920                                        };
1921                                        working_set.error(ParseError::ModuleDoubleMain(
1922                                            String::from_utf8_lossy(module_name).to_string(),
1923                                            err_span,
1924                                        ));
1925                                    } else {
1926                                        module.main = Some(id);
1927                                    }
1928                                } else {
1929                                    module.add_decl(name, id);
1930                                }
1931                            }
1932                            Exportable::Module { name, id } => {
1933                                if &name == b"mod" {
1934                                    let (submodule_main, submodule_decls, submodule_submodules) = {
1935                                        let submodule = working_set.get_module(id);
1936                                        (submodule.main, submodule.decls(), submodule.submodules())
1937                                    };
1938
1939                                    // Add submodule's decls to the parent module
1940                                    for (decl_name, decl_id) in submodule_decls {
1941                                        module.add_decl(decl_name, decl_id);
1942                                    }
1943
1944                                    // Add submodule's main command to the parent module
1945                                    if let Some(main_decl_id) = submodule_main {
1946                                        if module.main.is_some() {
1947                                            let err_span = if !pipe.elements.is_empty() {
1948                                                if let Expr::Call(call) =
1949                                                    &pipe.elements[0].expr.expr
1950                                                {
1951                                                    call.head
1952                                                } else {
1953                                                    pipe.elements[0].expr.span
1954                                                }
1955                                            } else {
1956                                                span
1957                                            };
1958                                            working_set.error(ParseError::ModuleDoubleMain(
1959                                                String::from_utf8_lossy(module_name).to_string(),
1960                                                err_span,
1961                                            ));
1962                                        } else {
1963                                            module.main = Some(main_decl_id);
1964                                        }
1965                                    }
1966
1967                                    // Add submodule's submodules to the parent module
1968                                    for (submodule_name, submodule_id) in submodule_submodules {
1969                                        module.add_submodule(submodule_name, submodule_id);
1970                                    }
1971                                } else {
1972                                    module.add_submodule(name, id);
1973                                }
1974                            }
1975                            Exportable::VarDecl { name, id } => {
1976                                module.add_variable(name, id);
1977                            }
1978                        }
1979                    }
1980
1981                    block.pipelines.push(pipe)
1982                }
1983                // Other definitions can't have attributes, so we handle attributes here with parse_attribute_block
1984                _ if command.has_attributes() => block
1985                    .pipelines
1986                    .push(parse_attribute_block(working_set, command)),
1987                b"const" => block
1988                    .pipelines
1989                    .push(parse_const(working_set, &command.parts).0),
1990                b"alias" => {
1991                    block.pipelines.push(parse_alias(
1992                        working_set,
1993                        command,
1994                        None, // using aliases named as the module locally is OK
1995                    ))
1996                }
1997                b"use" => {
1998                    let (pipeline, _) = parse_use(working_set, command, Some(&mut module));
1999
2000                    block.pipelines.push(pipeline)
2001                }
2002                b"module" => {
2003                    let (pipeline, _) = parse_module(
2004                        working_set,
2005                        command,
2006                        None, // using modules named as the module locally is OK
2007                    );
2008
2009                    block.pipelines.push(pipeline)
2010                }
2011                b"export-env" => {
2012                    let (pipe, maybe_env_block) = parse_export_env(working_set, &command.parts);
2013
2014                    if let Some(block_id) = maybe_env_block {
2015                        module.add_env_block(block_id);
2016                    }
2017
2018                    block.pipelines.push(pipe)
2019                }
2020                _ => {
2021                    working_set.error(ParseError::ExpectedKeyword(
2022                        "def, const, extern, alias, use, module, export or export-env keyword"
2023                            .into(),
2024                        command.parts[0],
2025                    ));
2026
2027                    block
2028                        .pipelines
2029                        .push(garbage_pipeline(working_set, &command.parts))
2030                }
2031            }
2032        } else {
2033            working_set.error(ParseError::Expected("not a pipeline", span));
2034            block.pipelines.push(garbage_pipeline(working_set, &[span]))
2035        }
2036    }
2037
2038    working_set.exit_scope();
2039
2040    (block, module, module_comments)
2041}
2042
2043fn module_needs_reloading(working_set: &StateWorkingSet, module_id: ModuleId) -> bool {
2044    let module = working_set.get_module(module_id);
2045
2046    fn submodule_need_reloading(working_set: &StateWorkingSet, submodule_id: ModuleId) -> bool {
2047        let submodule = working_set.get_module(submodule_id);
2048        let submodule_changed = if let Some((file_path, file_id)) = &submodule.file {
2049            let existing_contents = working_set.get_contents_of_file(*file_id);
2050            let file_contents = file_path.read(working_set);
2051
2052            if let (Some(existing), Some(new)) = (existing_contents, file_contents) {
2053                existing != new
2054            } else {
2055                false
2056            }
2057        } else {
2058            false
2059        };
2060
2061        if submodule_changed {
2062            true
2063        } else {
2064            module_needs_reloading(working_set, submodule_id)
2065        }
2066    }
2067
2068    let export_submodule_changed = module
2069        .submodules
2070        .iter()
2071        .any(|(_, submodule_id)| submodule_need_reloading(working_set, *submodule_id));
2072
2073    if export_submodule_changed {
2074        return true;
2075    }
2076
2077    module
2078        .imported_modules
2079        .iter()
2080        .any(|submodule_id| submodule_need_reloading(working_set, *submodule_id))
2081}
2082
2083/// Parse a module from a file.
2084///
2085/// The module name is inferred from the stem of the file, unless specified in `name_override`.
2086fn parse_module_file(
2087    working_set: &mut StateWorkingSet,
2088    path: ParserPath,
2089    path_span: Span,
2090    name_override: Option<String>,
2091) -> Option<ModuleId> {
2092    // Infer the module name from the stem of the file, unless overridden.
2093    let module_name = if let Some(name) = name_override {
2094        name
2095    } else if let Some(stem) = path.file_stem() {
2096        stem.to_string_lossy().to_string()
2097    } else {
2098        working_set.error(ParseError::ModuleNotFound(
2099            path_span,
2100            path.path().to_string_lossy().to_string(),
2101        ));
2102        return None;
2103    };
2104
2105    // Read the content of the module.
2106    let contents = if let Some(contents) = path.read(working_set) {
2107        contents
2108    } else {
2109        working_set.error(ParseError::ModuleNotFound(
2110            path_span,
2111            path.path().to_string_lossy().to_string(),
2112        ));
2113        return None;
2114    };
2115
2116    let file_id = working_set.add_file(path.path().to_string_lossy().to_string(), &contents);
2117    let new_span = working_set.get_span_for_file(file_id);
2118
2119    // Check if we've parsed the module before.
2120    if let Some(module_id) = working_set.find_module_by_span(new_span)
2121        && !module_needs_reloading(working_set, module_id)
2122    {
2123        return Some(module_id);
2124    }
2125
2126    // Add the file to the stack of files being processed.
2127    if let Err(e) = working_set.files.push(path.clone().path_buf(), path_span) {
2128        working_set.error(e);
2129        return None;
2130    }
2131
2132    // Parse the module
2133    let (block, mut module, module_comments) =
2134        parse_module_block(working_set, new_span, module_name.as_bytes());
2135
2136    // Remove the file from the stack of files being processed.
2137    working_set.files.pop();
2138
2139    let _ = working_set.add_block(Arc::new(block));
2140    module.file = Some((path, file_id));
2141    let module_id = working_set.add_module(&module_name, module, module_comments);
2142
2143    Some(module_id)
2144}
2145
2146pub fn parse_module_file_or_dir(
2147    working_set: &mut StateWorkingSet,
2148    path: &[u8],
2149    path_span: Span,
2150    name_override: Option<String>,
2151) -> Option<ModuleId> {
2152    let (module_path_str, err) = unescape_unquote_string(path, path_span);
2153    if let Some(err) = err {
2154        working_set.error(err);
2155        return None;
2156    }
2157
2158    #[allow(deprecated)]
2159    let cwd = working_set.get_cwd();
2160
2161    let module_path =
2162        if let Some(path) = find_in_dirs(&module_path_str, working_set, &cwd, Some(LIB_DIRS_VAR)) {
2163            path
2164        } else {
2165            working_set.error(ParseError::ModuleNotFound(path_span, module_path_str));
2166            return None;
2167        };
2168
2169    if module_path.is_dir() {
2170        if module_path.read_dir().is_none() {
2171            working_set.error(ParseError::ModuleNotFound(
2172                path_span,
2173                module_path.path().to_string_lossy().to_string(),
2174            ));
2175            return None;
2176        };
2177
2178        let module_name = if let Some(stem) = module_path.file_stem() {
2179            stem.to_string_lossy().to_string()
2180        } else {
2181            working_set.error(ParseError::ModuleNotFound(
2182                path_span,
2183                module_path.path().to_string_lossy().to_string(),
2184            ));
2185            return None;
2186        };
2187
2188        let mod_nu_path = module_path
2189            .clone()
2190            .join("mod.nu")
2191            .normalize_slashes_forward();
2192
2193        if !(mod_nu_path.exists() && mod_nu_path.is_file()) {
2194            working_set.error(ParseError::ModuleMissingModNuFile(
2195                module_path.path().to_string_lossy().to_string(),
2196                path_span,
2197            ));
2198            return None;
2199        }
2200
2201        if let Some(module_id) = parse_module_file(
2202            working_set,
2203            mod_nu_path,
2204            path_span,
2205            name_override.or(Some(module_name)),
2206        ) {
2207            let module = working_set.get_module(module_id).clone();
2208
2209            let module_name = String::from_utf8_lossy(&module.name).to_string();
2210
2211            let module_comments = if let Some(comments) = working_set.get_module_comments(module_id)
2212            {
2213                comments.to_vec()
2214            } else {
2215                vec![]
2216            };
2217
2218            let new_module_id = working_set.add_module(&module_name, module, module_comments);
2219
2220            Some(new_module_id)
2221        } else {
2222            None
2223        }
2224    } else if module_path.is_file() {
2225        parse_module_file(working_set, module_path, path_span, name_override)
2226    } else {
2227        working_set.error(ParseError::ModuleNotFound(
2228            path_span,
2229            module_path.path().to_string_lossy().to_string(),
2230        ));
2231        None
2232    }
2233}
2234
2235pub fn parse_module(
2236    working_set: &mut StateWorkingSet,
2237    lite_command: &LiteCommand,
2238    module_name: Option<&[u8]>,
2239) -> (Pipeline, Option<ModuleId>) {
2240    // TODO: Currently, module is closing over its parent scope (i.e., defs in the parent scope are
2241    // visible and usable in this module's scope). We want to disable that for files.
2242
2243    let spans = &lite_command.parts;
2244
2245    if let Some(redirection) = lite_command.redirection.as_ref() {
2246        working_set.error(redirecting_builtin_error("module", redirection));
2247        return (garbage_pipeline(working_set, spans), None);
2248    }
2249
2250    let mut module_comments = lite_command.comments.clone();
2251
2252    let split_id = if spans.len() > 1 && working_set.get_span_contents(spans[0]) == b"export" {
2253        2
2254    } else {
2255        1
2256    };
2257
2258    let (mut call, call_span) = match working_set.find_decl(b"module") {
2259        Some(decl_id) => {
2260            let (command_spans, rest_spans) = spans.split_at(split_id);
2261
2262            let ParsedInternalCall {
2263                call,
2264                output,
2265                call_kind,
2266            } = parse_internal_call(
2267                working_set,
2268                Span::concat(command_spans),
2269                rest_spans,
2270                decl_id,
2271                ArgumentParsingLevel::FirstK { k: 1 },
2272            );
2273
2274            let call_span = Span::concat(spans);
2275            if call_kind != CallKind::Valid {
2276                return (
2277                    Pipeline::from_vec(vec![Expression::new(
2278                        working_set,
2279                        Expr::Call(call),
2280                        call_span,
2281                        output,
2282                    )]),
2283                    None,
2284                );
2285            }
2286
2287            (call, call_span)
2288        }
2289        None => {
2290            working_set.error(ParseError::UnknownState(
2291                "internal error: 'module' or 'export module' declaration not found".into(),
2292                Span::concat(spans),
2293            ));
2294            return (garbage_pipeline(working_set, spans), None);
2295        }
2296    };
2297
2298    let (module_name_or_path, module_name_or_path_span) = if let Some(name) = call.positional_nth(0)
2299    {
2300        if let Some(s) = name.as_string() {
2301            if let Some(mod_name) = module_name
2302                && s.as_bytes() == mod_name
2303            {
2304                working_set.error(ParseError::NamedAsModule(
2305                    "module".to_string(),
2306                    s,
2307                    "mod".to_string(),
2308                    name.span,
2309                ));
2310                return (
2311                    Pipeline::from_vec(vec![Expression::new(
2312                        working_set,
2313                        Expr::Call(call),
2314                        call_span,
2315                        Type::Any,
2316                    )]),
2317                    None,
2318                );
2319            }
2320            (s, name.span)
2321        } else {
2322            working_set.error(ParseError::UnknownState(
2323                "internal error: name not a string".into(),
2324                Span::concat(spans),
2325            ));
2326            return (garbage_pipeline(working_set, spans), None);
2327        }
2328    } else {
2329        working_set.error(ParseError::UnknownState(
2330            "internal error: missing positional".into(),
2331            Span::concat(spans),
2332        ));
2333        return (garbage_pipeline(working_set, spans), None);
2334    };
2335
2336    if spans.len() == split_id + 1 {
2337        let pipeline = Pipeline::from_vec(vec![Expression::new(
2338            working_set,
2339            Expr::Call(call),
2340            call_span,
2341            Type::Any,
2342        )]);
2343
2344        if let Some(module_id) = parse_module_file_or_dir(
2345            working_set,
2346            module_name_or_path.as_bytes(),
2347            module_name_or_path_span,
2348            None,
2349        ) {
2350            return (pipeline, Some(module_id));
2351        } else {
2352            working_set.error(ParseError::ModuleNotFound(
2353                module_name_or_path_span,
2354                module_name_or_path,
2355            ));
2356            return (pipeline, None);
2357        }
2358    }
2359
2360    if spans.len() < split_id + 2 {
2361        working_set.error(ParseError::UnknownState(
2362            "Expected structure: module <name> or module <name> <block>".into(),
2363            Span::concat(spans),
2364        ));
2365
2366        return (garbage_pipeline(working_set, spans), None);
2367    }
2368
2369    let module_name = module_name_or_path;
2370
2371    let block_expr_span = spans[split_id + 1];
2372    let block_bytes = working_set.get_span_contents(block_expr_span);
2373    let mut start = block_expr_span.start;
2374    let mut end = block_expr_span.end;
2375
2376    if block_bytes.starts_with(b"{") {
2377        start += 1;
2378    } else {
2379        working_set.error(ParseError::Expected("block", block_expr_span));
2380        return (garbage_pipeline(working_set, spans), None);
2381    }
2382
2383    if block_bytes.ends_with(b"}") {
2384        end -= 1;
2385    } else {
2386        working_set.error(ParseError::Unclosed("}".into(), Span::new(end, end)));
2387    }
2388
2389    let block_content_span = Span::new(start, end);
2390
2391    let (block, module, inner_comments) =
2392        parse_module_block(working_set, block_content_span, module_name.as_bytes());
2393
2394    let block_id = working_set.add_block(Arc::new(block));
2395
2396    module_comments.extend(inner_comments);
2397    let module_id = working_set.add_module(&module_name, module, module_comments);
2398
2399    let block_expr = Expression::new(
2400        working_set,
2401        Expr::Block(block_id),
2402        block_expr_span,
2403        Type::Block,
2404    );
2405
2406    // NOTE: Command `module`/`export module` has only 1 flag `-h/--help` which is handled outside of this function
2407    // if more flags added in the future, then this needs to be updated to something like `set_kth_positional`
2408    if !call.set_kth_argument(1, Argument::Positional(block_expr)) {
2409        working_set.error(ParseError::InternalError(
2410            "Failed to set the block argument".into(),
2411            block_expr_span,
2412        ));
2413    }
2414
2415    (
2416        Pipeline::from_vec(vec![Expression::new(
2417            working_set,
2418            Expr::Call(call),
2419            Span::concat(spans),
2420            Type::Any,
2421        )]),
2422        Some(module_id),
2423    )
2424}
2425
2426pub fn parse_use(
2427    working_set: &mut StateWorkingSet,
2428    lite_command: &LiteCommand,
2429    parent_module: Option<&mut Module>,
2430) -> (Pipeline, Vec<Exportable>) {
2431    let spans = &lite_command.parts;
2432
2433    let (name_span, split_id) =
2434        if spans.len() > 1 && working_set.get_span_contents(spans[0]) == b"export" {
2435            (spans[1], 2)
2436        } else {
2437            (spans[0], 1)
2438        };
2439
2440    let use_call = working_set.get_span_contents(name_span).to_vec();
2441    if use_call != b"use" {
2442        working_set.error(ParseError::UnknownState(
2443            "internal error: Wrong call name for 'use' command".into(),
2444            Span::concat(spans),
2445        ));
2446        return (garbage_pipeline(working_set, spans), vec![]);
2447    }
2448
2449    if working_set.get_span_contents(name_span) != b"use" {
2450        working_set.error(ParseError::UnknownState(
2451            "internal error: Wrong call name for 'use' command".into(),
2452            Span::concat(spans),
2453        ));
2454        return (garbage_pipeline(working_set, spans), vec![]);
2455    }
2456
2457    if let Some(redirection) = lite_command.redirection.as_ref() {
2458        working_set.error(redirecting_builtin_error("use", redirection));
2459        return (garbage_pipeline(working_set, spans), vec![]);
2460    }
2461
2462    let (call, call_span, args_spans) = match working_set.find_decl(b"use") {
2463        Some(decl_id) => {
2464            let (command_spans, rest_spans) = spans.split_at(split_id);
2465
2466            let ParsedInternalCall {
2467                call,
2468                output,
2469                call_kind,
2470            } = parse_internal_call(
2471                working_set,
2472                Span::concat(command_spans),
2473                rest_spans,
2474                decl_id,
2475                ArgumentParsingLevel::Full,
2476            );
2477
2478            let call_span = Span::concat(spans);
2479            if call_kind != CallKind::Valid {
2480                return (
2481                    Pipeline::from_vec(vec![Expression::new(
2482                        working_set,
2483                        Expr::Call(call),
2484                        call_span,
2485                        output,
2486                    )]),
2487                    vec![],
2488                );
2489            }
2490
2491            (call, call_span, rest_spans)
2492        }
2493        None => {
2494            working_set.error(ParseError::UnknownState(
2495                "internal error: 'use' declaration not found".into(),
2496                Span::concat(spans),
2497            ));
2498            return (garbage_pipeline(working_set, spans), vec![]);
2499        }
2500    };
2501
2502    let import_pattern_expr = parse_import_pattern(working_set, call.positional_iter(), args_spans);
2503
2504    let import_pattern = match &import_pattern_expr {
2505        Expression {
2506            expr: Expr::Nothing,
2507            ..
2508        } => {
2509            let mut call = call;
2510            call.set_parser_info(
2511                "noop".to_string(),
2512                Expression::new_unknown(Expr::Nothing, Span::unknown(), Type::Nothing),
2513            );
2514            return (
2515                Pipeline::from_vec(vec![Expression::new(
2516                    working_set,
2517                    Expr::Call(call),
2518                    Span::concat(spans),
2519                    Type::Any,
2520                )]),
2521                vec![],
2522            );
2523        }
2524        Expression {
2525            expr: Expr::ImportPattern(import_pattern),
2526            ..
2527        } => import_pattern.clone(),
2528        _ => {
2529            working_set.error(ParseError::UnknownState(
2530                "internal error: Import pattern positional is not import pattern".into(),
2531                import_pattern_expr.span,
2532            ));
2533            return (garbage_pipeline(working_set, spans), vec![]);
2534        }
2535    };
2536
2537    let (mut import_pattern, module, module_id) = if let Some(module_id) = import_pattern.head.id {
2538        let module = working_set.get_module(module_id).clone();
2539        (
2540            ImportPattern {
2541                head: ImportPatternHead {
2542                    name: module.name.clone(),
2543                    id: Some(module_id),
2544                    span: import_pattern.head.span,
2545                },
2546                members: import_pattern.members,
2547                hidden: HashSet::new(),
2548                constants: vec![],
2549            },
2550            module,
2551            module_id,
2552        )
2553    } else if let Some(module_id) = parse_module_file_or_dir(
2554        working_set,
2555        &import_pattern.head.name,
2556        import_pattern.head.span,
2557        None,
2558    ) {
2559        let module = working_set.get_module(module_id).clone();
2560        (
2561            ImportPattern {
2562                head: ImportPatternHead {
2563                    name: module.name.clone(),
2564                    id: Some(module_id),
2565                    span: import_pattern.head.span,
2566                },
2567                members: import_pattern.members,
2568                hidden: HashSet::new(),
2569                constants: vec![],
2570            },
2571            module,
2572            module_id,
2573        )
2574    } else {
2575        working_set.error(ParseError::ModuleNotFound(
2576            import_pattern.head.span,
2577            String::from_utf8_lossy(&import_pattern.head.name).to_string(),
2578        ));
2579        return (
2580            Pipeline::from_vec(vec![Expression::new(
2581                working_set,
2582                Expr::Call(call),
2583                call_span,
2584                Type::Any,
2585            )]),
2586            vec![],
2587        );
2588    };
2589
2590    let mut imported_modules = vec![];
2591    let (definitions, errors) = module.resolve_import_pattern(
2592        working_set,
2593        module_id,
2594        &import_pattern.members,
2595        None,
2596        name_span,
2597        &mut imported_modules,
2598    );
2599
2600    working_set.parse_errors.extend(errors);
2601
2602    let mut constants = vec![];
2603
2604    for (name, const_vid) in definitions.constants {
2605        constants.push((name, const_vid));
2606    }
2607
2608    for (name, const_val) in definitions.constant_values {
2609        let const_var_id =
2610            working_set.add_variable(name.clone(), name_span, const_val.get_type(), false);
2611        working_set.set_variable_const_val(const_var_id, const_val);
2612        constants.push((name, const_var_id));
2613    }
2614
2615    let exportables = definitions
2616        .decls
2617        .iter()
2618        .map(|(name, decl_id)| Exportable::Decl {
2619            name: name.clone(),
2620            id: *decl_id,
2621        })
2622        .chain(
2623            definitions
2624                .modules
2625                .iter()
2626                .map(|(name, module_id)| Exportable::Module {
2627                    name: name.clone(),
2628                    id: *module_id,
2629                }),
2630        )
2631        .chain(
2632            constants
2633                .iter()
2634                .map(|(name, variable_id)| Exportable::VarDecl {
2635                    name: name.clone(),
2636                    id: *variable_id,
2637                }),
2638        )
2639        .collect();
2640
2641    import_pattern.constants = constants.iter().map(|(_, id)| *id).collect();
2642
2643    if let Some(m) = parent_module {
2644        m.track_imported_modules(&imported_modules)
2645    }
2646    // Extend the current scope with the module's exportables
2647    working_set.use_decls(definitions.decls);
2648    working_set.use_modules(definitions.modules);
2649    working_set.use_variables(constants);
2650
2651    // Create a new Use command call to pass the import pattern as parser info
2652    let import_pattern_expr = Expression::new(
2653        working_set,
2654        Expr::ImportPattern(Box::new(import_pattern)),
2655        Span::concat(args_spans),
2656        Type::Any,
2657    );
2658
2659    let mut call = call;
2660    call.set_parser_info("import_pattern".to_string(), import_pattern_expr);
2661
2662    (
2663        Pipeline::from_vec(vec![Expression::new(
2664            working_set,
2665            Expr::Call(call),
2666            Span::concat(spans),
2667            Type::Any,
2668        )]),
2669        exportables,
2670    )
2671}
2672
2673pub fn parse_hide(working_set: &mut StateWorkingSet, lite_command: &LiteCommand) -> Pipeline {
2674    let spans = &lite_command.parts;
2675
2676    if working_set.get_span_contents(spans[0]) != b"hide" {
2677        working_set.error(ParseError::UnknownState(
2678            "internal error: Wrong call name for 'hide' command".into(),
2679            Span::concat(spans),
2680        ));
2681        return garbage_pipeline(working_set, spans);
2682    }
2683    if let Some(redirection) = lite_command.redirection.as_ref() {
2684        working_set.error(redirecting_builtin_error("hide", redirection));
2685        return garbage_pipeline(working_set, spans);
2686    }
2687
2688    let (call, args_spans) = match working_set.find_decl(b"hide") {
2689        Some(decl_id) => {
2690            let ParsedInternalCall {
2691                call,
2692                output,
2693                call_kind,
2694            } = parse_internal_call(
2695                working_set,
2696                spans[0],
2697                &spans[1..],
2698                decl_id,
2699                ArgumentParsingLevel::Full,
2700            );
2701
2702            if call_kind != CallKind::Valid {
2703                return Pipeline::from_vec(vec![Expression::new(
2704                    working_set,
2705                    Expr::Call(call),
2706                    Span::concat(spans),
2707                    output,
2708                )]);
2709            }
2710
2711            (call, &spans[1..])
2712        }
2713        None => {
2714            working_set.error(ParseError::UnknownState(
2715                "internal error: 'hide' declaration not found".into(),
2716                Span::concat(spans),
2717            ));
2718            return garbage_pipeline(working_set, spans);
2719        }
2720    };
2721
2722    let import_pattern_expr = parse_import_pattern(working_set, call.positional_iter(), args_spans);
2723
2724    let import_pattern = if let Expression {
2725        expr: Expr::ImportPattern(import_pattern),
2726        ..
2727    } = &import_pattern_expr
2728    {
2729        import_pattern.clone()
2730    } else {
2731        working_set.error(ParseError::UnknownState(
2732            "internal error: Import pattern positional is not import pattern".into(),
2733            import_pattern_expr.span,
2734        ));
2735        return garbage_pipeline(working_set, spans);
2736    };
2737
2738    let bytes = working_set.get_span_contents(spans[0]);
2739
2740    if bytes == b"hide" && spans.len() >= 2 {
2741        for span in spans[1..].iter() {
2742            parse_string(working_set, *span);
2743        }
2744
2745        // module used only internally, not saved anywhere
2746        let (is_module, module) =
2747            if let Some(module_id) = working_set.find_module(&import_pattern.head.name) {
2748                (true, working_set.get_module(module_id).clone())
2749            } else if import_pattern.members.is_empty() {
2750                // The pattern head can be:
2751                if let Some(id) = working_set.find_decl(&import_pattern.head.name) {
2752                    // a custom command,
2753                    let mut module = Module::new(b"tmp".to_vec());
2754                    module.add_decl(import_pattern.head.name.clone(), id);
2755
2756                    (false, module)
2757                } else {
2758                    // , or it could be an env var (handled by the engine)
2759                    (false, Module::new(b"tmp".to_vec()))
2760                }
2761            } else {
2762                working_set.error(ParseError::ModuleNotFound(
2763                    spans[1],
2764                    String::from_utf8_lossy(&import_pattern.head.name).to_string(),
2765                ));
2766                return garbage_pipeline(working_set, spans);
2767            };
2768
2769        // This kind of inverts the import pattern matching found in parse_use()
2770        let decls_to_hide = if import_pattern.members.is_empty() {
2771            if is_module {
2772                module.decl_names_with_head(&import_pattern.head.name)
2773            } else {
2774                module.decl_names()
2775            }
2776        } else {
2777            match &import_pattern.members[0] {
2778                ImportPatternMember::Glob { .. } => module.decl_names(),
2779                ImportPatternMember::Name { name, span } => {
2780                    let mut decls = vec![];
2781
2782                    if name == b"main" {
2783                        if module.main.is_some() {
2784                            decls.push(import_pattern.head.name.clone());
2785                        } else {
2786                            working_set.error(ParseError::ExportNotFound(*span));
2787                        }
2788                    } else if let Some(item) =
2789                        module.decl_name_with_head(name, &import_pattern.head.name)
2790                    {
2791                        decls.push(item);
2792                    } else {
2793                        working_set.error(ParseError::ExportNotFound(*span));
2794                    }
2795
2796                    decls
2797                }
2798                ImportPatternMember::List { names } => {
2799                    let mut decls = vec![];
2800
2801                    for (name, span) in names {
2802                        if name == b"main" {
2803                            if module.main.is_some() {
2804                                decls.push(import_pattern.head.name.clone());
2805                            } else {
2806                                working_set.error(ParseError::ExportNotFound(*span));
2807                                break;
2808                            }
2809                        } else if let Some(item) =
2810                            module.decl_name_with_head(name, &import_pattern.head.name)
2811                        {
2812                            decls.push(item);
2813                        } else {
2814                            working_set.error(ParseError::ExportNotFound(*span));
2815                            break;
2816                        }
2817                    }
2818
2819                    decls
2820                }
2821            }
2822        };
2823
2824        let import_pattern = {
2825            let decls: HashSet<Vec<u8>> = decls_to_hide.iter().cloned().collect();
2826
2827            import_pattern.with_hidden(decls)
2828        };
2829
2830        // TODO: `use spam; use spam foo; hide foo` will hide both `foo` and `spam foo` since
2831        // they point to the same DeclId. Do we want to keep it that way?
2832        working_set.hide_decls(&decls_to_hide);
2833
2834        // Create a new Use command call to pass the new import pattern
2835        let import_pattern_expr = Expression::new(
2836            working_set,
2837            Expr::ImportPattern(Box::new(import_pattern)),
2838            Span::concat(args_spans),
2839            Type::Any,
2840        );
2841
2842        let mut call = call;
2843        call.set_parser_info("import_pattern".to_string(), import_pattern_expr);
2844
2845        Pipeline::from_vec(vec![Expression::new(
2846            working_set,
2847            Expr::Call(call),
2848            Span::concat(spans),
2849            Type::Any,
2850        )])
2851    } else {
2852        working_set.error(ParseError::UnknownState(
2853            "Expected structure: hide <name>".into(),
2854            Span::concat(spans),
2855        ));
2856        garbage_pipeline(working_set, spans)
2857    }
2858}
2859
2860pub fn parse_overlay_new(working_set: &mut StateWorkingSet, call: Box<Call>) -> Pipeline {
2861    let call_span = call.span();
2862
2863    let (overlay_name, _) = if let Some(expr) = call.positional_nth(0) {
2864        match eval_constant(working_set, expr) {
2865            Ok(val) => match val.coerce_into_string() {
2866                Ok(s) => (s, expr.span),
2867                Err(err) => {
2868                    working_set.error(err.wrap(working_set, call_span));
2869                    return garbage_pipeline(working_set, &[call_span]);
2870                }
2871            },
2872            Err(err) => {
2873                working_set.error(err.wrap(working_set, call_span));
2874                return garbage_pipeline(working_set, &[call_span]);
2875            }
2876        }
2877    } else {
2878        working_set.error(ParseError::UnknownState(
2879            "internal error: Missing required positional after call parsing".into(),
2880            call_span,
2881        ));
2882        return garbage_pipeline(working_set, &[call_span]);
2883    };
2884
2885    let pipeline = Pipeline::from_vec(vec![Expression::new(
2886        working_set,
2887        Expr::Call(call),
2888        call_span,
2889        Type::Any,
2890    )]);
2891
2892    let module_id = working_set.add_module(
2893        &overlay_name,
2894        Module::new(overlay_name.as_bytes().to_vec()),
2895        vec![],
2896    );
2897
2898    working_set.add_overlay(
2899        overlay_name.as_bytes().to_vec(),
2900        module_id,
2901        ResolvedImportPattern::new(vec![], vec![], vec![], vec![]),
2902        false,
2903    );
2904
2905    pipeline
2906}
2907
2908pub fn parse_overlay_use(working_set: &mut StateWorkingSet, call: Box<Call>) -> Pipeline {
2909    let call_span = call.span();
2910
2911    let (overlay_name, overlay_name_span) = if let Some(expr) = call.positional_nth(0) {
2912        match eval_constant(working_set, expr) {
2913            Ok(Value::Nothing { .. }) => {
2914                let mut call = call;
2915                call.set_parser_info(
2916                    "noop".to_string(),
2917                    Expression::new_unknown(Expr::Bool(true), Span::unknown(), Type::Bool),
2918                );
2919                return Pipeline::from_vec(vec![Expression::new(
2920                    working_set,
2921                    Expr::Call(call),
2922                    call_span,
2923                    Type::Any,
2924                )]);
2925            }
2926            Ok(val) => match val.coerce_into_string() {
2927                Ok(s) => (s, expr.span),
2928                Err(err) => {
2929                    working_set.error(err.wrap(working_set, call_span));
2930                    return garbage_pipeline(working_set, &[call_span]);
2931                }
2932            },
2933            Err(err) => {
2934                working_set.error(err.wrap(working_set, call_span));
2935                return garbage_pipeline(working_set, &[call_span]);
2936            }
2937        }
2938    } else {
2939        working_set.error(ParseError::UnknownState(
2940            "internal error: Missing required positional after call parsing".into(),
2941            call_span,
2942        ));
2943        return garbage_pipeline(working_set, &[call_span]);
2944    };
2945
2946    let new_name = if let Some(kw_expression) = call.positional_nth(1) {
2947        if let Some(new_name_expression) = kw_expression.as_keyword() {
2948            match eval_constant(working_set, new_name_expression) {
2949                Ok(val) => match val.coerce_into_string() {
2950                    Ok(s) => Some(Spanned {
2951                        item: s,
2952                        span: new_name_expression.span,
2953                    }),
2954                    Err(err) => {
2955                        working_set.error(err.wrap(working_set, call_span));
2956                        return garbage_pipeline(working_set, &[call_span]);
2957                    }
2958                },
2959                Err(err) => {
2960                    working_set.error(err.wrap(working_set, call_span));
2961                    return garbage_pipeline(working_set, &[call_span]);
2962                }
2963            }
2964        } else {
2965            working_set.error(ParseError::ExpectedKeyword(
2966                "as keyword".to_string(),
2967                kw_expression.span,
2968            ));
2969            return garbage_pipeline(working_set, &[call_span]);
2970        }
2971    } else {
2972        None
2973    };
2974
2975    let Ok(has_prefix) = has_flag_const(working_set, &call, "prefix") else {
2976        return garbage_pipeline(working_set, &[call_span]);
2977    };
2978    let Ok(do_reload) = has_flag_const(working_set, &call, "reload") else {
2979        return garbage_pipeline(working_set, &[call_span]);
2980    };
2981
2982    let pipeline = Pipeline::from_vec(vec![Expression::new(
2983        working_set,
2984        Expr::Call(call.clone()),
2985        call_span,
2986        Type::Any,
2987    )]);
2988
2989    let (final_overlay_name, origin_module, origin_module_id, is_module_updated) =
2990        if let Some(overlay_frame) = working_set.find_overlay(overlay_name.as_bytes()) {
2991            // Activate existing overlay
2992
2993            // First, check for errors
2994            if has_prefix && !overlay_frame.prefixed {
2995                working_set.error(ParseError::OverlayPrefixMismatch(
2996                    overlay_name,
2997                    "without".to_string(),
2998                    overlay_name_span,
2999                ));
3000                return pipeline;
3001            }
3002
3003            if !has_prefix && overlay_frame.prefixed {
3004                working_set.error(ParseError::OverlayPrefixMismatch(
3005                    overlay_name,
3006                    "with".to_string(),
3007                    overlay_name_span,
3008                ));
3009                return pipeline;
3010            }
3011
3012            if let Some(new_name) = new_name
3013                && new_name.item != overlay_name
3014            {
3015                working_set.error(ParseError::CantAddOverlayHelp(
3016                    format!(
3017                        "Cannot add overlay as '{}' because it already exists under the name '{}'",
3018                        new_name.item, overlay_name
3019                    ),
3020                    new_name.span,
3021                ));
3022                return pipeline;
3023            }
3024
3025            let module_id = overlay_frame.origin;
3026
3027            if let Some(new_module_id) = working_set.find_module(overlay_name.as_bytes()) {
3028                if !do_reload && (module_id == new_module_id) {
3029                    (
3030                        overlay_name,
3031                        Module::new(working_set.get_module(module_id).name.clone()),
3032                        module_id,
3033                        false,
3034                    )
3035                } else {
3036                    // The origin module of an overlay changed => update it
3037                    (
3038                        overlay_name,
3039                        working_set.get_module(new_module_id).clone(),
3040                        new_module_id,
3041                        true,
3042                    )
3043                }
3044            } else {
3045                let module_name = overlay_name.as_bytes().to_vec();
3046                (overlay_name, Module::new(module_name), module_id, true)
3047            }
3048        } else {
3049            // Create a new overlay
3050            if let Some(module_id) =
3051                // the name is a module
3052                working_set.find_module(overlay_name.as_bytes())
3053            {
3054                (
3055                    new_name.map(|spanned| spanned.item).unwrap_or(overlay_name),
3056                    working_set.get_module(module_id).clone(),
3057                    module_id,
3058                    true,
3059                )
3060            } else if let Some(module_id) = parse_module_file_or_dir(
3061                working_set,
3062                overlay_name.as_bytes(),
3063                overlay_name_span,
3064                new_name.as_ref().map(|spanned| spanned.item.clone()),
3065            ) {
3066                // try file or directory
3067                let new_module = working_set.get_module(module_id).clone();
3068                (
3069                    new_name
3070                        .map(|spanned| spanned.item)
3071                        .unwrap_or_else(|| String::from_utf8_lossy(&new_module.name).to_string()),
3072                    new_module,
3073                    module_id,
3074                    true,
3075                )
3076            } else {
3077                working_set.error(ParseError::ModuleOrOverlayNotFound(overlay_name_span));
3078                return pipeline;
3079            }
3080        };
3081
3082    let (definitions, errors) = if is_module_updated {
3083        if has_prefix {
3084            origin_module.resolve_import_pattern(
3085                working_set,
3086                origin_module_id,
3087                &[],
3088                Some(final_overlay_name.as_bytes()),
3089                call.head,
3090                &mut vec![],
3091            )
3092        } else {
3093            origin_module.resolve_import_pattern(
3094                working_set,
3095                origin_module_id,
3096                &[ImportPatternMember::Glob {
3097                    span: overlay_name_span,
3098                }],
3099                Some(final_overlay_name.as_bytes()),
3100                call.head,
3101                &mut vec![],
3102            )
3103        }
3104    } else {
3105        (
3106            ResolvedImportPattern::new(vec![], vec![], vec![], vec![]),
3107            vec![],
3108        )
3109    };
3110
3111    if errors.is_empty() {
3112        working_set.add_overlay(
3113            final_overlay_name.as_bytes().to_vec(),
3114            origin_module_id,
3115            definitions,
3116            has_prefix,
3117        );
3118    } else {
3119        working_set.parse_errors.extend(errors);
3120    }
3121
3122    // Change the call argument to include the Overlay expression with the module ID
3123    let mut call = call;
3124    call.set_parser_info(
3125        "overlay_expr".to_string(),
3126        Expression::new(
3127            working_set,
3128            Expr::Overlay(if is_module_updated {
3129                Some(origin_module_id)
3130            } else {
3131                None
3132            }),
3133            overlay_name_span,
3134            Type::Any,
3135        ),
3136    );
3137
3138    Pipeline::from_vec(vec![Expression::new(
3139        working_set,
3140        Expr::Call(call),
3141        call_span,
3142        Type::Any,
3143    )])
3144}
3145
3146pub fn parse_overlay_hide(working_set: &mut StateWorkingSet, call: Box<Call>) -> Pipeline {
3147    let call_span = call.span();
3148
3149    let (overlay_name, overlay_name_span) = if let Some(expr) = call.positional_nth(0) {
3150        match eval_constant(working_set, expr) {
3151            Ok(val) => match val.coerce_into_string() {
3152                Ok(s) => (s, expr.span),
3153                Err(err) => {
3154                    working_set.error(err.wrap(working_set, call_span));
3155                    return garbage_pipeline(working_set, &[call_span]);
3156                }
3157            },
3158            Err(err) => {
3159                working_set.error(err.wrap(working_set, call_span));
3160                return garbage_pipeline(working_set, &[call_span]);
3161            }
3162        }
3163    } else {
3164        (
3165            String::from_utf8_lossy(working_set.last_overlay_name()).to_string(),
3166            call_span,
3167        )
3168    };
3169
3170    let Ok(keep_custom) = has_flag_const(working_set, &call, "keep-custom") else {
3171        return garbage_pipeline(working_set, &[call_span]);
3172    };
3173
3174    let pipeline = Pipeline::from_vec(vec![Expression::new(
3175        working_set,
3176        Expr::Call(call),
3177        call_span,
3178        Type::Any,
3179    )]);
3180
3181    if overlay_name == DEFAULT_OVERLAY_NAME {
3182        working_set.error(ParseError::CantHideDefaultOverlay(
3183            overlay_name,
3184            overlay_name_span,
3185        ));
3186
3187        return pipeline;
3188    }
3189
3190    if !working_set
3191        .unique_overlay_names()
3192        .contains(&overlay_name.as_bytes())
3193    {
3194        working_set.error(ParseError::ActiveOverlayNotFound(overlay_name_span));
3195        return pipeline;
3196    }
3197
3198    if working_set.num_overlays() < 2 {
3199        working_set.error(ParseError::CantRemoveLastOverlay(overlay_name_span));
3200        return pipeline;
3201    }
3202
3203    working_set.remove_overlay(overlay_name.as_bytes(), keep_custom);
3204
3205    pipeline
3206}
3207
3208pub fn parse_let(working_set: &mut StateWorkingSet, spans: &[Span]) -> Pipeline {
3209    trace!("parsing: let");
3210
3211    // JT: Disabling check_name because it doesn't work with optional types in the declaration
3212    // if let Some(span) = check_name(working_set, spans) {
3213    //     return Pipeline::from_vec(vec![garbage(*span)]);
3214    // }
3215
3216    if let Some(decl_id) = working_set.find_decl(b"let") {
3217        if spans.len() >= 4 {
3218            // This is a bit of by-hand parsing to get around the issue where we want to parse in the reverse order
3219            // so that the var-id created by the variable isn't visible in the expression that init it
3220            for span in spans.iter().enumerate() {
3221                let item = working_set.get_span_contents(*span.1);
3222                // https://github.com/nushell/nushell/issues/9596, let = if $
3223                // let x = 'f', = at least start from index 2
3224                if item == b"=" && spans.len() > (span.0 + 1) && span.0 > 1 {
3225                    let (tokens, parse_error) = lex(
3226                        working_set.get_span_contents(Span::concat(&spans[(span.0 + 1)..])),
3227                        spans[span.0 + 1].start,
3228                        &[],
3229                        &[],
3230                        false,
3231                    );
3232
3233                    if let Some(parse_error) = parse_error {
3234                        working_set.error(parse_error)
3235                    }
3236
3237                    let rvalue_span = Span::concat(&spans[(span.0 + 1)..]);
3238                    let rvalue_block = parse_block(working_set, &tokens, rvalue_span, false, true);
3239
3240                    let output_type = rvalue_block.output_type();
3241
3242                    let block_id = working_set.add_block(Arc::new(rvalue_block));
3243
3244                    let rvalue = Expression::new(
3245                        working_set,
3246                        Expr::Block(block_id),
3247                        rvalue_span,
3248                        output_type,
3249                    );
3250
3251                    let mut idx = 0;
3252                    let (lvalue, explicit_type) =
3253                        parse_var_with_opt_type(working_set, &spans[1..(span.0)], &mut idx, false);
3254                    // check for extra tokens after the identifier
3255                    if idx + 1 < span.0 - 1 {
3256                        working_set.error(ParseError::ExtraTokens(spans[idx + 2]));
3257                    }
3258
3259                    let var_id = lvalue.as_var();
3260                    let rhs_type = rvalue.ty.clone();
3261
3262                    if let Some(explicit_type) = &explicit_type
3263                        && !type_compatible(explicit_type, &rhs_type)
3264                    {
3265                        working_set.error(ParseError::TypeMismatch(
3266                            explicit_type.clone(),
3267                            rhs_type.clone(),
3268                            Span::concat(&spans[(span.0 + 1)..]),
3269                        ));
3270                    }
3271
3272                    if let Some(var_id) = var_id
3273                        && explicit_type.is_none()
3274                    {
3275                        working_set.set_variable_type(var_id, rhs_type);
3276                    }
3277
3278                    let call = Box::new(Call {
3279                        decl_id,
3280                        head: spans[0],
3281                        arguments: vec![Argument::Positional(lvalue), Argument::Positional(rvalue)],
3282                        parser_info: HashMap::new(),
3283                    });
3284
3285                    return Pipeline::from_vec(vec![Expression::new(
3286                        working_set,
3287                        Expr::Call(call),
3288                        Span::concat(spans),
3289                        Type::Any,
3290                    )]);
3291                }
3292            }
3293        }
3294        let ParsedInternalCall { call, output, .. } = parse_internal_call(
3295            working_set,
3296            spans[0],
3297            &spans[1..],
3298            decl_id,
3299            ArgumentParsingLevel::Full,
3300        );
3301
3302        return Pipeline::from_vec(vec![Expression::new(
3303            working_set,
3304            Expr::Call(call),
3305            Span::concat(spans),
3306            output,
3307        )]);
3308    } else {
3309        working_set.error(ParseError::UnknownState(
3310            "internal error: let or const statements not found in core language".into(),
3311            Span::concat(spans),
3312        ))
3313    }
3314
3315    working_set.error(ParseError::UnknownState(
3316        "internal error: let or const statement unparsable".into(),
3317        Span::concat(spans),
3318    ));
3319
3320    garbage_pipeline(working_set, spans)
3321}
3322
3323/// Additionally returns a span encompassing the variable name, if successful.
3324pub fn parse_const(working_set: &mut StateWorkingSet, spans: &[Span]) -> (Pipeline, Option<Span>) {
3325    trace!("parsing: const");
3326
3327    // JT: Disabling check_name because it doesn't work with optional types in the declaration
3328    // if let Some(span) = check_name(working_set, spans) {
3329    //     return Pipeline::from_vec(vec![garbage(working_set, *span)]);
3330    // }
3331
3332    if let Some(decl_id) = working_set.find_decl(b"const") {
3333        if spans.len() >= 4 {
3334            // This is a bit of by-hand parsing to get around the issue where we want to parse in the reverse order
3335            // so that the var-id created by the variable isn't visible in the expression that init it
3336            for span in spans.iter().enumerate() {
3337                let item = working_set.get_span_contents(*span.1);
3338                // const x = 'f', = at least start from index 2
3339                if item == b"=" && spans.len() > (span.0 + 1) && span.0 > 1 {
3340                    // Parse the rvalue as a subexpression
3341                    let rvalue_span = Span::concat(&spans[(span.0 + 1)..]);
3342
3343                    let (rvalue_tokens, rvalue_error) = lex(
3344                        working_set.get_span_contents(rvalue_span),
3345                        rvalue_span.start,
3346                        &[],
3347                        &[],
3348                        false,
3349                    );
3350                    working_set.parse_errors.extend(rvalue_error);
3351
3352                    trace!("parsing: const right-hand side subexpression");
3353                    let rvalue_block =
3354                        parse_block(working_set, &rvalue_tokens, rvalue_span, false, true);
3355                    let rvalue_ty = rvalue_block.output_type();
3356                    let rvalue_block_id = working_set.add_block(Arc::new(rvalue_block));
3357                    let rvalue = Expression::new(
3358                        working_set,
3359                        Expr::Subexpression(rvalue_block_id),
3360                        rvalue_span,
3361                        rvalue_ty,
3362                    );
3363
3364                    let mut idx = 0;
3365
3366                    let (lvalue, explicit_type) =
3367                        parse_var_with_opt_type(working_set, &spans[1..(span.0)], &mut idx, false);
3368                    // check for extra tokens after the identifier
3369                    if idx + 1 < span.0 - 1 {
3370                        working_set.error(ParseError::ExtraTokens(spans[idx + 2]));
3371                    }
3372
3373                    let var_id = lvalue.as_var();
3374                    let rhs_type = rvalue.ty.clone();
3375
3376                    if let Some(explicit_type) = &explicit_type
3377                        && !type_compatible(explicit_type, &rhs_type)
3378                    {
3379                        working_set.error(ParseError::TypeMismatch(
3380                            explicit_type.clone(),
3381                            rhs_type.clone(),
3382                            Span::concat(&spans[(span.0 + 1)..]),
3383                        ));
3384                    }
3385
3386                    if let Some(var_id) = var_id {
3387                        if explicit_type.is_none() {
3388                            working_set.set_variable_type(var_id, rhs_type);
3389                        }
3390
3391                        match eval_constant(working_set, &rvalue) {
3392                            Ok(mut value) => {
3393                                // In case rhs is parsed as 'any' but is evaluated to a concrete
3394                                // type:
3395                                let mut const_type = value.get_type();
3396
3397                                if let Some(explicit_type) = &explicit_type {
3398                                    if !type_compatible(explicit_type, &const_type) {
3399                                        working_set.error(ParseError::TypeMismatch(
3400                                            explicit_type.clone(),
3401                                            const_type.clone(),
3402                                            Span::concat(&spans[(span.0 + 1)..]),
3403                                        ));
3404                                    }
3405                                    let val_span = value.span();
3406
3407                                    // need to convert to Value::glob if rhs is string, and
3408                                    // the const variable is annotated with glob type.
3409                                    match value {
3410                                        Value::String { val, .. }
3411                                            if explicit_type == &Type::Glob =>
3412                                        {
3413                                            value = Value::glob(val, false, val_span);
3414                                            const_type = value.get_type();
3415                                        }
3416                                        _ => {}
3417                                    }
3418                                }
3419
3420                                working_set.set_variable_type(var_id, const_type);
3421
3422                                // Assign the constant value to the variable
3423                                working_set.set_variable_const_val(var_id, value);
3424                            }
3425                            Err(err) => working_set.error(err.wrap(working_set, rvalue.span)),
3426                        }
3427                    }
3428
3429                    let call = Box::new(Call {
3430                        decl_id,
3431                        head: spans[0],
3432                        arguments: vec![
3433                            Argument::Positional(lvalue.clone()),
3434                            Argument::Positional(rvalue),
3435                        ],
3436                        parser_info: HashMap::new(),
3437                    });
3438
3439                    return (
3440                        Pipeline::from_vec(vec![Expression::new(
3441                            working_set,
3442                            Expr::Call(call),
3443                            Span::concat(spans),
3444                            Type::Any,
3445                        )]),
3446                        Some(lvalue.span),
3447                    );
3448                }
3449            }
3450        }
3451        let ParsedInternalCall { call, output, .. } = parse_internal_call(
3452            working_set,
3453            spans[0],
3454            &spans[1..],
3455            decl_id,
3456            ArgumentParsingLevel::Full,
3457        );
3458
3459        return (
3460            Pipeline::from_vec(vec![Expression::new(
3461                working_set,
3462                Expr::Call(call),
3463                Span::concat(spans),
3464                output,
3465            )]),
3466            None,
3467        );
3468    } else {
3469        working_set.error(ParseError::UnknownState(
3470            "internal error: let or const statements not found in core language".into(),
3471            Span::concat(spans),
3472        ))
3473    }
3474
3475    working_set.error(ParseError::UnknownState(
3476        "internal error: let or const statement unparsable".into(),
3477        Span::concat(spans),
3478    ));
3479
3480    (garbage_pipeline(working_set, spans), None)
3481}
3482
3483pub fn parse_mut(working_set: &mut StateWorkingSet, spans: &[Span]) -> Pipeline {
3484    trace!("parsing: mut");
3485
3486    // JT: Disabling check_name because it doesn't work with optional types in the declaration
3487    // if let Some(span) = check_name(working_set, spans) {
3488    //     return Pipeline::from_vec(vec![garbage(working_set, *span)]);
3489    // }
3490
3491    if let Some(decl_id) = working_set.find_decl(b"mut") {
3492        if spans.len() >= 4 {
3493            // This is a bit of by-hand parsing to get around the issue where we want to parse in the reverse order
3494            // so that the var-id created by the variable isn't visible in the expression that init it
3495            for span in spans.iter().enumerate() {
3496                let item = working_set.get_span_contents(*span.1);
3497                // mut x = 'f', = at least start from index 2
3498                if item == b"=" && spans.len() > (span.0 + 1) && span.0 > 1 {
3499                    let (tokens, parse_error) = lex(
3500                        working_set.get_span_contents(Span::concat(&spans[(span.0 + 1)..])),
3501                        spans[span.0 + 1].start,
3502                        &[],
3503                        &[],
3504                        false,
3505                    );
3506
3507                    if let Some(parse_error) = parse_error {
3508                        working_set.error(parse_error);
3509                    }
3510
3511                    let rvalue_span = Span::concat(&spans[(span.0 + 1)..]);
3512                    let rvalue_block = parse_block(working_set, &tokens, rvalue_span, false, true);
3513
3514                    let output_type = rvalue_block.output_type();
3515
3516                    let block_id = working_set.add_block(Arc::new(rvalue_block));
3517
3518                    let rvalue = Expression::new(
3519                        working_set,
3520                        Expr::Block(block_id),
3521                        rvalue_span,
3522                        output_type,
3523                    );
3524
3525                    let mut idx = 0;
3526
3527                    let (lvalue, explicit_type) =
3528                        parse_var_with_opt_type(working_set, &spans[1..(span.0)], &mut idx, true);
3529                    // check for extra tokens after the identifier
3530                    if idx + 1 < span.0 - 1 {
3531                        working_set.error(ParseError::ExtraTokens(spans[idx + 2]));
3532                    }
3533
3534                    let var_id = lvalue.as_var();
3535                    let rhs_type = rvalue.ty.clone();
3536
3537                    if let Some(explicit_type) = &explicit_type
3538                        && !type_compatible(explicit_type, &rhs_type)
3539                    {
3540                        working_set.error(ParseError::TypeMismatch(
3541                            explicit_type.clone(),
3542                            rhs_type.clone(),
3543                            Span::concat(&spans[(span.0 + 1)..]),
3544                        ));
3545                    }
3546
3547                    if let Some(var_id) = var_id
3548                        && explicit_type.is_none()
3549                    {
3550                        working_set.set_variable_type(var_id, rhs_type);
3551                    }
3552
3553                    let call = Box::new(Call {
3554                        decl_id,
3555                        head: spans[0],
3556                        arguments: vec![Argument::Positional(lvalue), Argument::Positional(rvalue)],
3557                        parser_info: HashMap::new(),
3558                    });
3559
3560                    return Pipeline::from_vec(vec![Expression::new(
3561                        working_set,
3562                        Expr::Call(call),
3563                        Span::concat(spans),
3564                        Type::Any,
3565                    )]);
3566                }
3567            }
3568        }
3569        let ParsedInternalCall { call, output, .. } = parse_internal_call(
3570            working_set,
3571            spans[0],
3572            &spans[1..],
3573            decl_id,
3574            ArgumentParsingLevel::Full,
3575        );
3576
3577        return Pipeline::from_vec(vec![Expression::new(
3578            working_set,
3579            Expr::Call(call),
3580            Span::concat(spans),
3581            output,
3582        )]);
3583    } else {
3584        working_set.error(ParseError::UnknownState(
3585            "internal error: let or const statements not found in core language".into(),
3586            Span::concat(spans),
3587        ))
3588    }
3589
3590    working_set.error(ParseError::UnknownState(
3591        "internal error: let or const statement unparsable".into(),
3592        Span::concat(spans),
3593    ));
3594
3595    garbage_pipeline(working_set, spans)
3596}
3597
3598pub fn parse_source(working_set: &mut StateWorkingSet, lite_command: &LiteCommand) -> Pipeline {
3599    trace!("parsing source");
3600    let spans = &lite_command.parts;
3601    let name = working_set.get_span_contents(spans[0]);
3602
3603    if name == b"source" || name == b"source-env" {
3604        if let Some(redirection) = lite_command.redirection.as_ref() {
3605            let name = if name == b"source" {
3606                "source"
3607            } else {
3608                "source-env"
3609            };
3610            working_set.error(redirecting_builtin_error(name, redirection));
3611            return garbage_pipeline(working_set, spans);
3612        }
3613
3614        let scoped = name == b"source-env";
3615
3616        if let Some(decl_id) = working_set.find_decl(name) {
3617            #[allow(deprecated)]
3618            let cwd = working_set.get_cwd();
3619
3620            // Is this the right call to be using here?
3621            // Some of the others (`parse_let`) use it, some of them (`parse_hide`) don't.
3622            let ParsedInternalCall {
3623                call,
3624                output,
3625                call_kind,
3626            } = parse_internal_call(
3627                working_set,
3628                spans[0],
3629                &spans[1..],
3630                decl_id,
3631                ArgumentParsingLevel::Full,
3632            );
3633
3634            if call_kind == CallKind::Help {
3635                return Pipeline::from_vec(vec![Expression::new(
3636                    working_set,
3637                    Expr::Call(call),
3638                    Span::concat(spans),
3639                    output,
3640                )]);
3641            }
3642
3643            // Command and one file name
3644            if let Some(expr) = call.positional_nth(0) {
3645                let val = match eval_constant(working_set, expr) {
3646                    Ok(val) => val,
3647                    Err(err) => {
3648                        working_set.error(err.wrap(working_set, Span::concat(&spans[1..])));
3649                        return Pipeline::from_vec(vec![Expression::new(
3650                            working_set,
3651                            Expr::Call(call),
3652                            Span::concat(&spans[1..]),
3653                            Type::Any,
3654                        )]);
3655                    }
3656                };
3657
3658                if val.is_nothing() {
3659                    let mut call = call;
3660                    call.set_parser_info(
3661                        "noop".to_string(),
3662                        Expression::new_unknown(Expr::Nothing, Span::unknown(), Type::Nothing),
3663                    );
3664                    return Pipeline::from_vec(vec![Expression::new(
3665                        working_set,
3666                        Expr::Call(call),
3667                        Span::concat(spans),
3668                        Type::Any,
3669                    )]);
3670                }
3671
3672                let filename = match val.coerce_into_string() {
3673                    Ok(s) => s,
3674                    Err(err) => {
3675                        working_set.error(err.wrap(working_set, Span::concat(&spans[1..])));
3676                        return Pipeline::from_vec(vec![Expression::new(
3677                            working_set,
3678                            Expr::Call(call),
3679                            Span::concat(&spans[1..]),
3680                            Type::Any,
3681                        )]);
3682                    }
3683                };
3684
3685                if let Some(path) = find_in_dirs(&filename, working_set, &cwd, Some(LIB_DIRS_VAR)) {
3686                    if let Some(contents) = path.read(working_set) {
3687                        // Add the file to the stack of files being processed.
3688                        if let Err(e) = working_set.files.push(path.clone().path_buf(), spans[1]) {
3689                            working_set.error(e);
3690                            return garbage_pipeline(working_set, spans);
3691                        }
3692
3693                        // This will load the defs from the file into the
3694                        // working set, if it was a successful parse.
3695                        let mut block = parse(
3696                            working_set,
3697                            Some(&path.path().to_string_lossy()),
3698                            &contents,
3699                            scoped,
3700                        );
3701                        if block.ir_block.is_none() {
3702                            let block_mut = Arc::make_mut(&mut block);
3703                            compile_block(working_set, block_mut);
3704                        }
3705
3706                        // Remove the file from the stack of files being processed.
3707                        working_set.files.pop();
3708
3709                        // Save the block into the working set
3710                        let block_id = working_set.add_block(block);
3711
3712                        let mut call_with_block = call;
3713
3714                        // FIXME: Adding this expression to the positional creates a syntax highlighting error
3715                        // after writing `source example.nu`
3716                        call_with_block.set_parser_info(
3717                            "block_id".to_string(),
3718                            Expression::new(
3719                                working_set,
3720                                Expr::Int(block_id.get() as i64),
3721                                spans[1],
3722                                Type::Any,
3723                            ),
3724                        );
3725
3726                        // store the file path as a string to be gathered later
3727                        call_with_block.set_parser_info(
3728                            "block_id_name".to_string(),
3729                            Expression::new(
3730                                working_set,
3731                                Expr::Filepath(path.path_buf().display().to_string(), false),
3732                                spans[1],
3733                                Type::String,
3734                            ),
3735                        );
3736
3737                        return Pipeline::from_vec(vec![Expression::new(
3738                            working_set,
3739                            Expr::Call(call_with_block),
3740                            Span::concat(spans),
3741                            Type::Any,
3742                        )]);
3743                    }
3744                } else {
3745                    working_set.error(ParseError::SourcedFileNotFound(filename, spans[1]));
3746                }
3747            }
3748            return Pipeline::from_vec(vec![Expression::new(
3749                working_set,
3750                Expr::Call(call),
3751                Span::concat(spans),
3752                Type::Any,
3753            )]);
3754        }
3755    }
3756    working_set.error(ParseError::UnknownState(
3757        "internal error: source statement unparsable".into(),
3758        Span::concat(spans),
3759    ));
3760    garbage_pipeline(working_set, spans)
3761}
3762
3763pub fn parse_where_expr(working_set: &mut StateWorkingSet, spans: &[Span]) -> Expression {
3764    trace!("parsing: where");
3765
3766    if !spans.is_empty() && working_set.get_span_contents(spans[0]) != b"where" {
3767        working_set.error(ParseError::UnknownState(
3768            "internal error: Wrong call name for 'where' command".into(),
3769            Span::concat(spans),
3770        ));
3771        return garbage(working_set, Span::concat(spans));
3772    }
3773
3774    if spans.len() < 2 {
3775        working_set.error(ParseError::MissingPositional(
3776            "row condition".into(),
3777            Span::concat(spans),
3778            "where <row_condition>".into(),
3779        ));
3780        return garbage(working_set, Span::concat(spans));
3781    }
3782
3783    let call = match working_set.find_decl(b"where") {
3784        Some(decl_id) => {
3785            let ParsedInternalCall {
3786                call,
3787                output,
3788                call_kind,
3789            } = parse_internal_call(
3790                working_set,
3791                spans[0],
3792                &spans[1..],
3793                decl_id,
3794                ArgumentParsingLevel::Full,
3795            );
3796
3797            if call_kind != CallKind::Valid {
3798                return Expression::new(working_set, Expr::Call(call), Span::concat(spans), output);
3799            }
3800
3801            call
3802        }
3803        None => {
3804            working_set.error(ParseError::UnknownState(
3805                "internal error: 'where' declaration not found".into(),
3806                Span::concat(spans),
3807            ));
3808            return garbage(working_set, Span::concat(spans));
3809        }
3810    };
3811
3812    Expression::new(
3813        working_set,
3814        Expr::Call(call),
3815        Span::concat(spans),
3816        Type::Any,
3817    )
3818}
3819
3820pub fn parse_where(working_set: &mut StateWorkingSet, lite_command: &LiteCommand) -> Pipeline {
3821    let expr = parse_where_expr(working_set, &lite_command.parts);
3822    let redirection = lite_command
3823        .redirection
3824        .as_ref()
3825        .map(|r| parse_redirection(working_set, r));
3826
3827    let element = PipelineElement {
3828        pipe: None,
3829        expr,
3830        redirection,
3831    };
3832
3833    Pipeline {
3834        elements: vec![element],
3835    }
3836}
3837
3838#[cfg(feature = "plugin")]
3839pub fn parse_plugin_use(working_set: &mut StateWorkingSet, call: Box<Call>) -> Pipeline {
3840    use nu_protocol::{FromValue, PluginRegistryFile};
3841
3842    #[allow(deprecated)]
3843    let cwd = working_set.get_cwd();
3844
3845    if let Err(err) = (|| {
3846        let name = call
3847            .positional_nth(0)
3848            .map(|expr| {
3849                eval_constant(working_set, expr)
3850                    .and_then(Spanned::<String>::from_value)
3851                    .map_err(|err| err.wrap(working_set, call.head))
3852            })
3853            .expect("required positional should have been checked")?;
3854
3855        let plugin_config = call
3856            .named_iter()
3857            .find(|(arg_name, _, _)| arg_name.item == "plugin-config")
3858            .map(|(_, _, expr)| {
3859                let expr = expr
3860                    .as_ref()
3861                    .expect("--plugin-config arg should have been checked already");
3862                eval_constant(working_set, expr)
3863                    .and_then(Spanned::<String>::from_value)
3864                    .map_err(|err| err.wrap(working_set, call.head))
3865            })
3866            .transpose()?;
3867
3868        // The name could also be a filename, so try our best to expand it for that match.
3869        let filename_query = {
3870            let path = nu_path::expand_path_with(&name.item, &cwd, true);
3871            path.to_str()
3872                .and_then(|path_str| {
3873                    find_in_dirs(path_str, working_set, &cwd, Some("NU_PLUGIN_DIRS"))
3874                })
3875                .map(|parser_path| parser_path.path_buf())
3876                .unwrap_or(path)
3877        };
3878
3879        // Find the actual plugin config path location. We don't have a const/env variable for this,
3880        // it either lives in the current working directory or in the script's directory
3881        let plugin_config_path = if let Some(custom_path) = &plugin_config {
3882            find_in_dirs(&custom_path.item, working_set, &cwd, None).ok_or_else(|| {
3883                ParseError::FileNotFound(custom_path.item.clone(), custom_path.span)
3884            })?
3885        } else {
3886            ParserPath::RealPath(
3887                working_set
3888                    .permanent_state
3889                    .plugin_path
3890                    .as_ref()
3891                    .ok_or_else(|| ParseError::LabeledErrorWithHelp {
3892                        error: "Plugin registry file not set".into(),
3893                        label: "can't load plugin without registry file".into(),
3894                        span: call.head,
3895                        help:
3896                            "pass --plugin-config to `plugin use` when $nu.plugin-path is not set"
3897                                .into(),
3898                    })?
3899                    .to_owned(),
3900            )
3901        };
3902
3903        let file = plugin_config_path.open(working_set).map_err(|err| {
3904            ParseError::LabeledError(
3905                "Plugin registry file can't be opened".into(),
3906                err.to_string(),
3907                plugin_config.as_ref().map(|p| p.span).unwrap_or(call.head),
3908            )
3909        })?;
3910
3911        // The file is now open, so we just have to parse the contents and find the plugin
3912        let contents = PluginRegistryFile::read_from(file, Some(call.head))
3913            .map_err(|err| err.wrap(working_set, call.head))?;
3914
3915        let plugin_item = contents
3916            .plugins
3917            .iter()
3918            .find(|plugin| plugin.name == name.item || plugin.filename == filename_query)
3919            .ok_or_else(|| ParseError::PluginNotFound {
3920                name: name.item.clone(),
3921                name_span: name.span,
3922                plugin_config_span: plugin_config.as_ref().map(|p| p.span),
3923            })?;
3924
3925        // Now add the signatures to the working set
3926        nu_plugin_engine::load_plugin_registry_item(working_set, plugin_item, Some(call.head))
3927            .map_err(|err| err.wrap(working_set, call.head))?;
3928
3929        Ok(())
3930    })() {
3931        working_set.error(err);
3932    }
3933
3934    let call_span = call.span();
3935
3936    Pipeline::from_vec(vec![Expression::new(
3937        working_set,
3938        Expr::Call(call),
3939        call_span,
3940        Type::Nothing,
3941    )])
3942}
3943
3944pub fn find_dirs_var(working_set: &StateWorkingSet, var_name: &str) -> Option<VarId> {
3945    working_set
3946        .find_variable(format!("${var_name}").as_bytes())
3947        .filter(|var_id| working_set.get_variable(*var_id).const_val.is_some())
3948}
3949
3950/// This helper function is used to find files during parsing
3951///
3952/// First, the actual current working directory is selected as
3953///   a) the directory of a file currently being parsed
3954///   b) current working directory (PWD)
3955///
3956/// Then, if the file is not found in the actual cwd, dirs_var is checked.
3957/// For now, we first check for a const with the name of `dirs_var_name`,
3958/// and if that's not found, then we try to look for an environment variable of the same name.
3959/// If there is a relative path in dirs_var, it is assumed to be relative to the actual cwd
3960/// determined in the first step.
3961///
3962/// Always returns an absolute path
3963pub fn find_in_dirs(
3964    filename: &str,
3965    working_set: &StateWorkingSet,
3966    cwd: &str,
3967    dirs_var_name: Option<&str>,
3968) -> Option<ParserPath> {
3969    if is_windows_device_path(Path::new(&filename)) {
3970        return Some(ParserPath::RealPath(filename.into()));
3971    }
3972
3973    pub fn find_in_dirs_with_id(
3974        filename: &str,
3975        working_set: &StateWorkingSet,
3976        cwd: &str,
3977        dirs_var_name: Option<&str>,
3978    ) -> Option<ParserPath> {
3979        // Choose whether to use file-relative or PWD-relative path
3980        let actual_cwd = working_set
3981            .files
3982            .current_working_directory()
3983            .unwrap_or(Path::new(cwd));
3984
3985        // Try if we have an existing virtual path
3986        if let Some(virtual_path) = working_set.find_virtual_path(filename) {
3987            return Some(ParserPath::from_virtual_path(
3988                working_set,
3989                filename,
3990                virtual_path,
3991            ));
3992        } else {
3993            let abs_virtual_filename = actual_cwd.join(filename);
3994            let abs_virtual_filename = abs_virtual_filename.to_string_lossy();
3995
3996            if let Some(virtual_path) = working_set.find_virtual_path(&abs_virtual_filename) {
3997                return Some(ParserPath::from_virtual_path(
3998                    working_set,
3999                    &abs_virtual_filename,
4000                    virtual_path,
4001                ));
4002            }
4003        }
4004
4005        // Try if we have an existing filesystem path
4006        if let Ok(p) = absolute_with(filename, actual_cwd)
4007            && p.exists()
4008        {
4009            return Some(ParserPath::RealPath(p));
4010        }
4011
4012        // Early-exit if path is non-existent absolute path
4013        let path = Path::new(filename);
4014        if !path.is_relative() {
4015            return None;
4016        }
4017
4018        // Look up relative path from NU_LIB_DIRS
4019        dirs_var_name
4020            .as_ref()
4021            .and_then(|dirs_var_name| find_dirs_var(working_set, dirs_var_name))
4022            .map(|var_id| working_set.get_variable(var_id))?
4023            .const_val
4024            .as_ref()?
4025            .as_list()
4026            .ok()?
4027            .iter()
4028            .map(|lib_dir| -> Option<PathBuf> {
4029                let dir = lib_dir.to_path().ok()?;
4030                let dir_abs = absolute_with(dir, actual_cwd).ok()?;
4031                let path = absolute_with(filename, dir_abs).ok()?;
4032                path.exists().then_some(path)
4033            })
4034            .find(Option::is_some)
4035            .flatten()
4036            .map(ParserPath::RealPath)
4037    }
4038
4039    // TODO: remove (see #8310)
4040    // Same as find_in_dirs_with_id but using $env.NU_LIB_DIRS instead of constant
4041    pub fn find_in_dirs_old(
4042        filename: &str,
4043        working_set: &StateWorkingSet,
4044        cwd: &str,
4045        dirs_env: Option<&str>,
4046    ) -> Option<PathBuf> {
4047        // Choose whether to use file-relative or PWD-relative path
4048        let actual_cwd = working_set
4049            .files
4050            .current_working_directory()
4051            .unwrap_or(Path::new(cwd));
4052
4053        if let Ok(p) = absolute_with(filename, actual_cwd)
4054            && p.exists()
4055        {
4056            Some(p)
4057        } else {
4058            let path = Path::new(filename);
4059
4060            if path.is_relative() {
4061                if let Some(lib_dirs) =
4062                    dirs_env.and_then(|dirs_env| working_set.get_env_var(dirs_env))
4063                {
4064                    if let Ok(dirs) = lib_dirs.as_list() {
4065                        for lib_dir in dirs {
4066                            if let Ok(dir) = lib_dir.to_path() {
4067                                // make sure the dir is absolute path
4068                                if let Ok(dir_abs) = absolute_with(dir, actual_cwd)
4069                                    && let Ok(path) = absolute_with(filename, dir_abs)
4070                                    && path.exists()
4071                                {
4072                                    return Some(path);
4073                                }
4074                            }
4075                        }
4076
4077                        None
4078                    } else {
4079                        None
4080                    }
4081                } else {
4082                    None
4083                }
4084            } else {
4085                None
4086            }
4087        }
4088    }
4089
4090    find_in_dirs_with_id(filename, working_set, cwd, dirs_var_name).or_else(|| {
4091        find_in_dirs_old(filename, working_set, cwd, dirs_var_name).map(ParserPath::RealPath)
4092    })
4093}
4094
4095fn detect_params_in_name(
4096    working_set: &StateWorkingSet,
4097    name_span: Span,
4098    decl_id: DeclId,
4099) -> Option<ParseError> {
4100    let name = working_set.get_span_contents(name_span);
4101    for (offset, char) in name.iter().enumerate() {
4102        if *char == b'[' || *char == b'(' {
4103            return Some(ParseError::LabeledErrorWithHelp {
4104                error: "no space between name and parameters".into(),
4105                label: "expected space".into(),
4106                help: format!(
4107                    "consider adding a space between the `{}` command's name and its parameters",
4108                    working_set.get_decl(decl_id).name()
4109                ),
4110                span: Span::new(offset + name_span.start - 1, offset + name_span.start - 1),
4111            });
4112        }
4113    }
4114
4115    None
4116}
4117
4118/// Run has_flag_const and push possible error to working_set
4119fn has_flag_const(working_set: &mut StateWorkingSet, call: &Call, name: &str) -> Result<bool, ()> {
4120    call.has_flag_const(working_set, name).map_err(|err| {
4121        working_set.error(err.wrap(working_set, call.span()));
4122    })
4123}