Skip to main content

nu_parser/
parse_keywords.rs

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