nu_parser/
parse_keywords.rs

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