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