nu_parser/
parse_keywords.rs

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