nu_parser/
parse_keywords.rs

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