Skip to main content

nu_parser/
parse_keywords.rs

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