Skip to main content

nu_parser/
parse_module.rs

1use crate::{
2    Token, TokenContents,
3    exportable::Exportable,
4    lex,
5    lite_parser::{LiteCommand, lite_parse},
6    parse_helpers::{garbage_pipeline, trim_quotes},
7    parse_pipelines::redirecting_builtin_error,
8    parser::{
9        ArgumentParsingLevel, CallKind, ParsedInternalCall, compile_block_with_id,
10        parse_import_pattern, parse_internal_call, parse_string,
11    },
12    unescape_unquote_string,
13};
14
15use crate::parse_alias::parse_alias;
16use crate::parse_bindings::parse_const;
17use crate::parse_def::{
18    has_flag_const, parse_attribute_block, parse_def, parse_def_predecl, parse_extern,
19};
20
21use nu_protocol::{
22    BlockId, Module, ModuleId, ParseError, Span, Type,
23    ast::{
24        Argument, Block, Call, Expr, Expression, ImportPattern, ImportPatternHead,
25        ImportPatternMember, Pipeline,
26    },
27    engine::StateWorkingSet,
28    eval_const::eval_constant,
29};
30use std::{collections::HashSet, sync::Arc};
31
32use nu_protocol::parser_path::ParserPath;
33
34fn warp_export_call(
35    working_set: &mut StateWorkingSet,
36    pipeline: &mut Pipeline,
37    full_name: &str,
38    spans: &[Span],
39) -> bool {
40    let Some(export_decl_id) = working_set.find_decl(full_name.as_bytes()) else {
41        let error_span = spans.first().cloned().unwrap_or(Span::unknown());
42        working_set.error(ParseError::InternalError(
43            format!("missing '{full_name}' command"),
44            error_span,
45        ));
46        return false;
47    };
48    match pipeline.elements.first_mut().map(|e| {
49        e.expr.span = Span::concat(spans);
50        &mut e.expr.expr
51    }) {
52        Some(Expr::Call(def_call)) => {
53            def_call.head = Span::concat(&spans[0..=1]);
54            def_call.decl_id = export_decl_id;
55            return true;
56        }
57        Some(Expr::AttributeBlock(ab)) => {
58            if let Expr::Call(def_call) = &mut ab.item.expr {
59                def_call.decl_id = export_decl_id;
60                return true;
61            }
62        }
63        _ => {}
64    };
65    working_set.error(ParseError::InternalError(
66        "unexpected output from parsing a definition".into(),
67        Span::concat(&spans[1..]),
68    ));
69    true
70}
71
72pub fn parse_export_in_block(
73    working_set: &mut StateWorkingSet,
74    lite_command: &LiteCommand,
75) -> Pipeline {
76    let parts = lite_command.command_parts();
77    let full_name = if parts.len() > 1 {
78        let sub = working_set.get_span_contents(parts[1]);
79        match sub {
80            b"alias" => "export alias",
81            b"def" => "export def",
82            b"extern" => "export extern",
83            b"use" => "export use",
84            b"module" => "export module",
85            b"const" => "export const",
86            _ => "export",
87        }
88    } else {
89        "export"
90    };
91
92    if let Some(redirection) = lite_command.redirection.as_ref() {
93        working_set.error(redirecting_builtin_error(full_name, redirection));
94        return garbage_pipeline(working_set, &lite_command.parts);
95    }
96
97    let mut pipeline = match full_name {
98        "export def" => parse_def(working_set, lite_command, None).0,
99        "export extern" => parse_extern(working_set, lite_command, None),
100        _ if lite_command.has_attributes() => parse_attribute_block(working_set, lite_command),
101        "export alias" => parse_alias(working_set, lite_command, None),
102        "export const" => parse_const(working_set, &lite_command.parts[1..]).0,
103        "export use" => parse_use(working_set, lite_command, None).0,
104        "export module" => parse_module(working_set, lite_command, None).0,
105        _ => {
106            if let Some(decl_id) = working_set.find_decl(full_name.as_bytes()) {
107                let starting_error_count = working_set.parse_errors.len();
108                let ParsedInternalCall {
109                    call,
110                    output,
111                    call_kind,
112                } = parse_internal_call(
113                    working_set,
114                    parts[0],
115                    &parts[1..],
116                    decl_id,
117                    ArgumentParsingLevel::Full,
118                    None,
119                );
120
121                if call_kind != CallKind::Valid {
122                    return Pipeline::from_vec(vec![Expression::new(
123                        working_set,
124                        Expr::Call(call),
125                        Span::concat(&lite_command.parts),
126                        output,
127                    )]);
128                }
129                working_set.parse_errors.truncate(starting_error_count);
130                working_set.error(ParseError::UnexpectedKeyword(
131                    full_name.into(),
132                    lite_command.parts[0],
133                ));
134            } else {
135                working_set.error(ParseError::UnknownState(
136                    format!("internal error: '{full_name}' declaration not found",),
137                    Span::concat(&lite_command.parts),
138                ));
139            };
140            garbage_pipeline(working_set, &lite_command.parts)
141        }
142    };
143
144    warp_export_call(working_set, &mut pipeline, full_name, &lite_command.parts);
145    pipeline
146}
147
148pub fn parse_export_in_module(
149    working_set: &mut StateWorkingSet,
150    lite_command: &LiteCommand,
151    module_name: &[u8],
152    parent_module: &mut Module,
153) -> (Pipeline, Vec<Exportable>) {
154    let spans = lite_command.command_parts();
155
156    let export_span = if let Some(sp) = spans.first() {
157        if working_set.get_span_contents(*sp) != b"export" {
158            working_set.error(ParseError::UnknownState(
159                "expected export statement".into(),
160                Span::concat(spans),
161            ));
162            return (garbage_pipeline(working_set, spans), vec![]);
163        }
164
165        *sp
166    } else {
167        working_set.error(ParseError::UnknownState(
168            "got empty input for parsing export statement".into(),
169            Span::concat(spans),
170        ));
171        return (garbage_pipeline(working_set, spans), vec![]);
172    };
173
174    let (pipeline, exportables) = if let Some(kw_span) = spans.get(1) {
175        let kw_name = working_set.get_span_contents(*kw_span);
176        match kw_name {
177            b"def" => {
178                let (mut pipeline, cmd_result) =
179                    parse_def(working_set, lite_command, Some(module_name));
180
181                let mut result = vec![];
182
183                if let Some((decl_name, decl_id)) = cmd_result {
184                    result.push(Exportable::Decl {
185                        name: decl_name.to_vec(),
186                        id: decl_id,
187                    });
188                }
189
190                if !warp_export_call(working_set, &mut pipeline, "export def", spans) {
191                    return (garbage_pipeline(working_set, spans), vec![]);
192                }
193
194                (pipeline, result)
195            }
196            b"extern" => {
197                let mut pipeline = parse_extern(working_set, lite_command, Some(module_name));
198
199                if !warp_export_call(working_set, &mut pipeline, "export extern", spans) {
200                    return (garbage_pipeline(working_set, spans), vec![]);
201                }
202
203                let mut result = vec![];
204
205                let decl_name = match spans.get(2) {
206                    Some(span) => working_set.get_span_contents(*span),
207                    None => &[],
208                };
209                let decl_name = trim_quotes(decl_name);
210
211                if let Some(decl_id) = working_set.find_decl(decl_name) {
212                    result.push(Exportable::Decl {
213                        name: decl_name.to_vec(),
214                        id: decl_id,
215                    });
216                } else {
217                    working_set.error(ParseError::InternalError(
218                        "failed to find added declaration".into(),
219                        Span::concat(&spans[1..]),
220                    ));
221                }
222
223                (pipeline, result)
224            }
225            _ if lite_command.has_attributes() => {
226                (parse_attribute_block(working_set, lite_command), vec![])
227            }
228            b"alias" => {
229                let lite_command = LiteCommand {
230                    comments: lite_command.comments.clone(),
231                    parts: spans[1..].to_vec(),
232                    pipe: lite_command.pipe,
233                    redirection: lite_command.redirection.clone(),
234                    attribute_idx: vec![],
235                };
236                let mut pipeline = parse_alias(working_set, &lite_command, Some(module_name));
237
238                if !warp_export_call(working_set, &mut pipeline, "export alias", spans) {
239                    return (garbage_pipeline(working_set, spans), vec![]);
240                }
241
242                let mut result = vec![];
243
244                let alias_name = match spans.get(2) {
245                    Some(span) => working_set.get_span_contents(*span),
246                    None => &[],
247                };
248                let alias_name = trim_quotes(alias_name);
249
250                if let Some(alias_id) = working_set.find_decl(alias_name) {
251                    result.push(Exportable::Decl {
252                        name: alias_name.to_vec(),
253                        id: alias_id,
254                    });
255                } else {
256                    working_set.error(ParseError::InternalError(
257                        "failed to find added alias".into(),
258                        Span::concat(&spans[1..]),
259                    ));
260                }
261
262                (pipeline, result)
263            }
264            b"use" => {
265                let lite_command = LiteCommand {
266                    comments: lite_command.comments.clone(),
267                    parts: spans[1..].to_vec(),
268                    pipe: lite_command.pipe,
269                    redirection: lite_command.redirection.clone(),
270                    attribute_idx: vec![],
271                };
272                let (mut pipeline, exportables) =
273                    parse_use(working_set, &lite_command, Some(parent_module));
274
275                if !warp_export_call(working_set, &mut pipeline, "export use", spans) {
276                    return (garbage_pipeline(working_set, spans), vec![]);
277                }
278
279                (pipeline, exportables)
280            }
281            b"module" => {
282                let (mut pipeline, maybe_module_id) =
283                    parse_module(working_set, lite_command, Some(module_name));
284
285                if !warp_export_call(working_set, &mut pipeline, "export module", spans) {
286                    return (garbage_pipeline(working_set, spans), vec![]);
287                }
288
289                let mut result = vec![];
290
291                if let Some(module_name_span) = spans.get(2) {
292                    let module_name = working_set.get_span_contents(*module_name_span);
293                    let module_name = trim_quotes(module_name);
294
295                    if let Some(module_id) = maybe_module_id {
296                        result.push(Exportable::Module {
297                            name: working_set.get_module(module_id).name(),
298                            id: module_id,
299                        });
300                    } else {
301                        working_set.error(ParseError::InternalError(
302                            format!(
303                                "failed to find added module '{}'",
304                                String::from_utf8_lossy(module_name)
305                            ),
306                            Span::concat(&spans[1..]),
307                        ));
308                    }
309                }
310
311                (pipeline, result)
312            }
313            b"const" => {
314                let (mut pipeline, var_name_span) = parse_const(working_set, &spans[1..]);
315
316                if !warp_export_call(working_set, &mut pipeline, "export const", spans) {
317                    return (garbage_pipeline(working_set, spans), vec![]);
318                }
319
320                let mut result = vec![];
321
322                if let Some(var_name_span) = var_name_span {
323                    let var_name = working_set.get_span_contents(var_name_span);
324                    let var_name = trim_quotes(var_name);
325
326                    if let Some(var_id) = working_set.find_variable(var_name) {
327                        if let Err(err) = working_set.get_constant(var_id) {
328                            working_set.error(err);
329                        } else {
330                            result.push(Exportable::VarDecl {
331                                name: var_name.to_vec(),
332                                id: var_id,
333                            });
334                        }
335                    } else {
336                        working_set.error(ParseError::InternalError(
337                            "failed to find added variable".into(),
338                            Span::concat(&spans[1..]),
339                        ));
340                    }
341                }
342
343                (pipeline, result)
344            }
345            _ => {
346                working_set.error(ParseError::Expected(
347                    "def, alias, use, module, const or extern keyword",
348                    spans[1],
349                ));
350
351                (garbage_pipeline(working_set, spans), vec![])
352            }
353        }
354    } else {
355        working_set.error(ParseError::MissingPositional(
356            "def, alias, use, module, const or extern keyword".to_string(),
357            Span::new(export_span.end, export_span.end),
358            "def, alias, use, module, const or extern keyword".to_string(),
359        ));
360
361        (garbage_pipeline(working_set, spans), vec![])
362    };
363
364    (pipeline, exportables)
365}
366
367pub fn parse_export_env(
368    working_set: &mut StateWorkingSet,
369    spans: &[Span],
370) -> (Pipeline, Option<BlockId>) {
371    if !spans.is_empty() && working_set.get_span_contents(spans[0]) != b"export-env" {
372        working_set.error(ParseError::UnknownState(
373            "internal error: Wrong call name for 'export-env' command".into(),
374            Span::concat(spans),
375        ));
376        return (garbage_pipeline(working_set, spans), None);
377    }
378
379    if spans.len() < 2 {
380        working_set.error(ParseError::MissingPositional(
381            "block".into(),
382            Span::concat(spans),
383            "export-env <block>".into(),
384        ));
385        return (garbage_pipeline(working_set, spans), None);
386    }
387
388    let call = match working_set.find_decl(b"export-env") {
389        Some(decl_id) => {
390            let ParsedInternalCall {
391                call,
392                output,
393                call_kind,
394            } = parse_internal_call(
395                working_set,
396                spans[0],
397                &[spans[1]],
398                decl_id,
399                ArgumentParsingLevel::Full,
400                None,
401            );
402
403            if call_kind != CallKind::Valid {
404                return (
405                    Pipeline::from_vec(vec![Expression::new(
406                        working_set,
407                        Expr::Call(call),
408                        Span::concat(spans),
409                        output,
410                    )]),
411                    None,
412                );
413            }
414
415            call
416        }
417        None => {
418            working_set.error(ParseError::UnknownState(
419                "internal error: 'export-env' declaration not found".into(),
420                Span::concat(spans),
421            ));
422            return (garbage_pipeline(working_set, spans), None);
423        }
424    };
425
426    let block_id = if let Some(block) = call.positional_iter().next() {
427        if let Some(block_id) = block.as_block() {
428            block_id
429        } else {
430            working_set.error(ParseError::UnknownState(
431                "internal error: 'export-env' block is not a block".into(),
432                block.span,
433            ));
434            return (garbage_pipeline(working_set, spans), None);
435        }
436    } else {
437        working_set.error(ParseError::UnknownState(
438            "internal error: 'export-env' block is missing".into(),
439            Span::concat(spans),
440        ));
441        return (garbage_pipeline(working_set, spans), None);
442    };
443
444    let pipeline = Pipeline::from_vec(vec![Expression::new(
445        working_set,
446        Expr::Call(call),
447        Span::concat(spans),
448        Type::Any,
449    )]);
450
451    compile_block_with_id(working_set, block_id);
452
453    (pipeline, Some(block_id))
454}
455
456fn collect_first_comments(working_set: &StateWorkingSet, tokens: &[Token]) -> Vec<Span> {
457    let mut comments = vec![];
458
459    let mut tokens_iter = tokens.iter().peekable();
460    while let Some(token) = tokens_iter.next() {
461        match token.contents {
462            TokenContents::Comment => {
463                let comment = working_set.get_span_contents(token.span);
464
465                if comments.is_empty() && comment.starts_with(b"#!") {
466                    continue;
467                }
468
469                comments.push(token.span);
470            }
471            TokenContents::Eol => {
472                if let Some(Token {
473                    contents: TokenContents::Eol,
474                    ..
475                }) = tokens_iter.peek()
476                    && !comments.is_empty()
477                {
478                    break;
479                }
480            }
481            _ => {
482                comments.clear();
483                break;
484            }
485        }
486    }
487
488    comments
489}
490
491pub fn parse_module_block(
492    working_set: &mut StateWorkingSet,
493    span: Span,
494    module_name: &[u8],
495) -> (Block, Module, Vec<Span>) {
496    working_set.enter_scope();
497
498    let source = working_set.get_span_contents(span);
499
500    let (output, err) = lex(source, span.start, &[], &[], false);
501    if let Some(err) = err {
502        working_set.error(err)
503    }
504
505    let module_comments = collect_first_comments(working_set, &output);
506
507    let (output, err) = lite_parse(&output, working_set);
508    if let Some(err) = err {
509        working_set.error(err)
510    }
511
512    for pipeline in &output.block {
513        if pipeline.commands.len() == 1 {
514            parse_def_predecl(working_set, pipeline.commands[0].command_parts());
515        }
516    }
517
518    let mut module = Module::from_span(module_name.to_vec(), span);
519
520    let mut block = Block::new_with_capacity(output.block.len());
521    block.span = Some(span);
522
523    for pipeline in output.block.iter() {
524        if pipeline.commands.len() == 1 {
525            let command = &pipeline.commands[0];
526
527            let name = command
528                .command_parts()
529                .first()
530                .map(|s| working_set.get_span_contents(*s))
531                .unwrap_or(b"");
532
533            match name {
534                b"def" => block
535                    .pipelines
536                    .push(parse_def(working_set, command, None).0),
537                b"extern" => block
538                    .pipelines
539                    .push(parse_extern(working_set, command, None)),
540                b"export" => {
541                    let (pipe, exportables) =
542                        parse_export_in_module(working_set, command, module_name, &mut module);
543
544                    for exportable in exportables {
545                        match exportable {
546                            Exportable::Decl { name, id } => {
547                                if &name == b"main" {
548                                    if module.main.is_some() {
549                                        let err_span = if !pipe.elements.is_empty() {
550                                            if let Expr::Call(call) = &pipe.elements[0].expr.expr {
551                                                call.head
552                                            } else {
553                                                pipe.elements[0].expr.span
554                                            }
555                                        } else {
556                                            span
557                                        };
558                                        working_set.error(ParseError::ModuleDoubleMain(
559                                            String::from_utf8_lossy(module_name).to_string(),
560                                            err_span,
561                                        ));
562                                    } else {
563                                        module.main = Some(id);
564                                    }
565                                } else {
566                                    module.add_decl(name, id);
567                                }
568                            }
569                            Exportable::Module { name, id } => {
570                                if &name == b"mod" {
571                                    let (submodule_main, submodule_decls, submodule_submodules) = {
572                                        let submodule = working_set.get_module(id);
573                                        (submodule.main, submodule.decls(), submodule.submodules())
574                                    };
575
576                                    for (decl_name, decl_id) in submodule_decls {
577                                        module.add_decl(decl_name, decl_id);
578                                    }
579
580                                    if let Some(main_decl_id) = submodule_main {
581                                        if module.main.is_some() {
582                                            let err_span = if !pipe.elements.is_empty() {
583                                                if let Expr::Call(call) =
584                                                    &pipe.elements[0].expr.expr
585                                                {
586                                                    call.head
587                                                } else {
588                                                    pipe.elements[0].expr.span
589                                                }
590                                            } else {
591                                                span
592                                            };
593                                            working_set.error(ParseError::ModuleDoubleMain(
594                                                String::from_utf8_lossy(module_name).to_string(),
595                                                err_span,
596                                            ));
597                                        } else {
598                                            module.main = Some(main_decl_id);
599                                        }
600                                    }
601
602                                    for (submodule_name, submodule_id) in submodule_submodules {
603                                        module.add_submodule(submodule_name, submodule_id);
604                                    }
605                                } else {
606                                    module.add_submodule(name, id);
607                                }
608                            }
609                            Exportable::VarDecl { name, id } => {
610                                module.add_variable(name, id);
611                            }
612                        }
613                    }
614
615                    block.pipelines.push(pipe)
616                }
617                _ if command.has_attributes() => block
618                    .pipelines
619                    .push(parse_attribute_block(working_set, command)),
620                b"const" => block
621                    .pipelines
622                    .push(parse_const(working_set, &command.parts).0),
623                b"alias" => block
624                    .pipelines
625                    .push(parse_alias(working_set, command, None)),
626                b"use" => {
627                    let (pipeline, _) = parse_use(working_set, command, Some(&mut module));
628
629                    block.pipelines.push(pipeline)
630                }
631                b"module" => {
632                    let (pipeline, _) = parse_module(working_set, command, None);
633
634                    block.pipelines.push(pipeline)
635                }
636                b"export-env" => {
637                    let (pipe, maybe_env_block) = parse_export_env(working_set, &command.parts);
638
639                    if let Some(block_id) = maybe_env_block {
640                        module.add_env_block(block_id);
641                    }
642
643                    block.pipelines.push(pipe)
644                }
645                _ => {
646                    working_set.error(ParseError::ExpectedKeyword(
647                        "def, const, extern, alias, use, module, export or export-env keyword"
648                            .into(),
649                        command.parts[0],
650                    ));
651
652                    block
653                        .pipelines
654                        .push(garbage_pipeline(working_set, &command.parts))
655                }
656            }
657        } else {
658            working_set.error(ParseError::Expected("not a pipeline", span));
659            block.pipelines.push(garbage_pipeline(working_set, &[span]))
660        }
661    }
662
663    working_set.exit_scope();
664
665    (block, module, module_comments)
666}
667
668fn module_needs_reloading(working_set: &StateWorkingSet, module_id: ModuleId) -> bool {
669    let module = working_set.get_module(module_id);
670
671    fn submodule_need_reloading(working_set: &StateWorkingSet, submodule_id: ModuleId) -> bool {
672        let submodule = working_set.get_module(submodule_id);
673        let submodule_changed = if let Some((file_path, file_id)) = &submodule.file {
674            let existing_contents = working_set.get_contents_of_file(*file_id);
675            let file_contents = file_path.read(working_set);
676
677            if let (Some(existing), Some(new)) = (existing_contents, file_contents) {
678                existing != new
679            } else {
680                false
681            }
682        } else {
683            false
684        };
685
686        if submodule_changed {
687            true
688        } else {
689            module_needs_reloading(working_set, submodule_id)
690        }
691    }
692
693    let export_submodule_changed = module
694        .submodules
695        .iter()
696        .any(|(_, submodule_id)| submodule_need_reloading(working_set, *submodule_id));
697
698    if export_submodule_changed {
699        return true;
700    }
701
702    module
703        .imported_modules
704        .iter()
705        .any(|submodule_id| submodule_need_reloading(working_set, *submodule_id))
706}
707
708fn parse_module_file(
709    working_set: &mut StateWorkingSet,
710    path: ParserPath,
711    path_span: Span,
712    name_override: Option<String>,
713) -> Option<ModuleId> {
714    let module_name = if let Some(name) = name_override {
715        name
716    } else if let Some(stem) = path.file_stem() {
717        stem.to_string_lossy().to_string()
718    } else {
719        working_set.error(ParseError::ModuleNotFound(
720            path_span,
721            path.path().to_string_lossy().to_string(),
722        ));
723        return None;
724    };
725
726    let contents = if let Some(contents) = path.read(working_set) {
727        contents
728    } else {
729        working_set.error(ParseError::ModuleNotFound(
730            path_span,
731            path.path().to_string_lossy().to_string(),
732        ));
733        return None;
734    };
735
736    let file_id = working_set.add_file(&path.path().to_string_lossy(), &contents);
737    let new_span = working_set.get_span_for_file(file_id);
738
739    if let Some(module_id) = working_set.find_module_by_span(new_span)
740        && !module_needs_reloading(working_set, module_id)
741    {
742        return Some(module_id);
743    }
744
745    if let Err(e) = working_set.files.push(path.clone().path_buf(), path_span) {
746        working_set.error(e);
747        return None;
748    }
749
750    let (block, mut module, module_comments) =
751        parse_module_block(working_set, new_span, module_name.as_bytes());
752
753    working_set.files.pop();
754
755    let _ = working_set.add_block(Arc::new(block));
756    module.file = Some((path, file_id));
757    let module_id = working_set.add_module(&module_name, module, module_comments);
758
759    Some(module_id)
760}
761
762fn find_in_dirs(
763    filename: &str,
764    working_set: &StateWorkingSet,
765    cwd: &str,
766    dirs_var_name: Option<&str>,
767) -> Option<ParserPath> {
768    crate::parse_source::find_in_dirs(filename, working_set, cwd, dirs_var_name)
769}
770
771pub fn parse_module_file_or_dir(
772    working_set: &mut StateWorkingSet,
773    path: &[u8],
774    path_span: Span,
775    name_override: Option<String>,
776) -> Option<ModuleId> {
777    let (module_path_str, err) = unescape_unquote_string(path, path_span);
778    if let Some(err) = err {
779        working_set.error(err);
780        return None;
781    }
782
783    #[allow(deprecated)]
784    let cwd = working_set.get_cwd();
785
786    let module_path = if let Some(path) = find_in_dirs(
787        &module_path_str,
788        working_set,
789        &cwd,
790        Some(crate::parse_source::LIB_DIRS_VAR),
791    ) {
792        path
793    } else {
794        working_set.error(ParseError::ModuleNotFound(path_span, module_path_str));
795        return None;
796    };
797
798    if module_path.is_dir() {
799        if module_path.read_dir().is_none() {
800            working_set.error(ParseError::ModuleNotFound(
801                path_span,
802                module_path.path().to_string_lossy().to_string(),
803            ));
804            return None;
805        };
806
807        let module_name = if let Some(stem) = module_path.file_stem() {
808            stem.to_string_lossy().to_string()
809        } else {
810            working_set.error(ParseError::ModuleNotFound(
811                path_span,
812                module_path.path().to_string_lossy().to_string(),
813            ));
814            return None;
815        };
816
817        let mod_nu_path = module_path
818            .clone()
819            .join("mod.nu")
820            .normalize_slashes_forward();
821
822        if !(mod_nu_path.exists() && mod_nu_path.is_file()) {
823            working_set.error(ParseError::ModuleMissingModNuFile(
824                module_path.path().to_string_lossy().to_string(),
825                path_span,
826            ));
827            return None;
828        }
829
830        if let Some(module_id) = parse_module_file(
831            working_set,
832            mod_nu_path,
833            path_span,
834            name_override.or(Some(module_name)),
835        ) {
836            let module = working_set.get_module(module_id).clone();
837
838            let module_name = String::from_utf8_lossy(&module.name).to_string();
839
840            let module_comments = if let Some(comments) = working_set.get_module_comments(module_id)
841            {
842                comments.to_vec()
843            } else {
844                vec![]
845            };
846
847            let new_module_id = working_set.add_module(&module_name, module, module_comments);
848
849            Some(new_module_id)
850        } else {
851            None
852        }
853    } else if module_path.is_file() {
854        parse_module_file(working_set, module_path, path_span, name_override)
855    } else {
856        working_set.error(ParseError::ModuleNotFound(
857            path_span,
858            module_path.path().to_string_lossy().to_string(),
859        ));
860        None
861    }
862}
863
864pub fn parse_module(
865    working_set: &mut StateWorkingSet,
866    lite_command: &LiteCommand,
867    module_name: Option<&[u8]>,
868) -> (Pipeline, Option<ModuleId>) {
869    let spans = &lite_command.parts;
870
871    if let Some(redirection) = lite_command.redirection.as_ref() {
872        working_set.error(redirecting_builtin_error("module", redirection));
873        return (garbage_pipeline(working_set, spans), None);
874    }
875
876    let mut module_comments = lite_command.comments.clone();
877
878    let split_id = if spans.len() > 1 && working_set.get_span_contents(spans[0]) == b"export" {
879        2
880    } else {
881        1
882    };
883
884    let (mut call, call_span) = match working_set.find_decl(b"module") {
885        Some(decl_id) => {
886            let (command_spans, rest_spans) = spans.split_at(split_id);
887
888            let ParsedInternalCall {
889                call,
890                output,
891                call_kind,
892            } = parse_internal_call(
893                working_set,
894                Span::concat(command_spans),
895                rest_spans,
896                decl_id,
897                ArgumentParsingLevel::FirstK { k: 1 },
898                None,
899            );
900
901            let call_span = Span::concat(spans);
902            if call_kind != CallKind::Valid {
903                return (
904                    Pipeline::from_vec(vec![Expression::new(
905                        working_set,
906                        Expr::Call(call),
907                        call_span,
908                        output,
909                    )]),
910                    None,
911                );
912            }
913
914            (call, call_span)
915        }
916        None => {
917            working_set.error(ParseError::UnknownState(
918                "internal error: 'module' or 'export module' declaration not found".into(),
919                Span::concat(spans),
920            ));
921            return (garbage_pipeline(working_set, spans), None);
922        }
923    };
924
925    let Some(name_expr) = call.positional_iter().next() else {
926        working_set.error(ParseError::UnknownState(
927            "internal error: missing positional".into(),
928            Span::concat(spans),
929        ));
930        return (garbage_pipeline(working_set, spans), None);
931    };
932    let Some(name) = name_expr.as_string() else {
933        working_set.error(ParseError::UnknownState(
934            "internal error: name not a string".into(),
935            Span::concat(spans),
936        ));
937        return (garbage_pipeline(working_set, spans), None);
938    };
939
940    if module_name.is_some_and(|mod_name| mod_name == name.as_bytes()) {
941        working_set.error(ParseError::NamedAsModule(
942            "module".to_string(),
943            name,
944            "mod".to_string(),
945            name_expr.span,
946        ));
947        return (
948            Pipeline::from_vec(vec![Expression::new(
949                working_set,
950                Expr::Call(call),
951                call_span,
952                Type::Any,
953            )]),
954            None,
955        );
956    }
957    let (module_name_or_path, module_name_or_path_span) = (name, name_expr.span);
958
959    if spans.len() == split_id + 1 {
960        let pipeline = Pipeline::from_vec(vec![Expression::new(
961            working_set,
962            Expr::Call(call),
963            call_span,
964            Type::Any,
965        )]);
966
967        if let Some(module_id) = parse_module_file_or_dir(
968            working_set,
969            module_name_or_path.as_bytes(),
970            module_name_or_path_span,
971            None,
972        ) {
973            return (pipeline, Some(module_id));
974        } else {
975            working_set.error(ParseError::ModuleNotFound(
976                module_name_or_path_span,
977                module_name_or_path,
978            ));
979            return (pipeline, None);
980        }
981    }
982
983    if spans.len() < split_id + 2 {
984        working_set.error(ParseError::UnknownState(
985            "Expected structure: module <name> or module <name> <block>".into(),
986            Span::concat(spans),
987        ));
988
989        return (garbage_pipeline(working_set, spans), None);
990    }
991
992    let module_name = module_name_or_path;
993
994    let block_expr_span = spans[split_id + 1];
995    let block_bytes = working_set.get_span_contents(block_expr_span);
996    let mut start = block_expr_span.start;
997    let mut end = block_expr_span.end;
998
999    if block_bytes.starts_with(b"{") {
1000        start += 1;
1001    } else {
1002        working_set.error(ParseError::Expected("block", block_expr_span));
1003        return (garbage_pipeline(working_set, spans), None);
1004    }
1005
1006    if block_bytes.ends_with(b"}") {
1007        end -= 1;
1008    } else {
1009        let open = Span::new(
1010            block_expr_span.start,
1011            block_expr_span
1012                .start
1013                .saturating_add(1)
1014                .min(block_expr_span.end),
1015        );
1016        working_set.error(ParseError::unclosed("}", open, Span::new(end, end)));
1017    }
1018
1019    let block_content_span = Span::new(start, end);
1020
1021    let (block, module, inner_comments) =
1022        parse_module_block(working_set, block_content_span, module_name.as_bytes());
1023
1024    let block_id = working_set.add_block(Arc::new(block));
1025
1026    module_comments.extend(inner_comments);
1027    let module_id = working_set.add_module(&module_name, module, module_comments);
1028
1029    let block_expr = Expression::new(
1030        working_set,
1031        Expr::Block(block_id),
1032        block_expr_span,
1033        Type::Block,
1034    );
1035
1036    if !call.set_kth_argument(1, Argument::Positional(block_expr)) {
1037        working_set.error(ParseError::InternalError(
1038            "Failed to set the block argument".into(),
1039            block_expr_span,
1040        ));
1041    }
1042
1043    (
1044        Pipeline::from_vec(vec![Expression::new(
1045            working_set,
1046            Expr::Call(call),
1047            Span::concat(spans),
1048            Type::Any,
1049        )]),
1050        Some(module_id),
1051    )
1052}
1053
1054/// A module named after a parser keyword cannot have its `main` command invoked,
1055/// because `main` is the module's default entry point (called as `module_name`),
1056/// and parser keywords are intercepted by the parser before any command lookup.
1057///
1058/// Emits a `KeywordShadowModuleMain` error and returns `true` when this conflict is detected.
1059fn check_main_keyword_shadow(
1060    working_set: &mut StateWorkingSet,
1061    has_main: bool,
1062    name: &[u8],
1063    span: Span,
1064) -> bool {
1065    if has_main && crate::parse_keywords::is_parser_keyword(name) {
1066        working_set.error(ParseError::KeywordShadowModuleMain(
1067            String::from_utf8_lossy(name).to_string(),
1068            span,
1069        ));
1070        true
1071    } else {
1072        false
1073    }
1074}
1075
1076pub fn parse_use(
1077    working_set: &mut StateWorkingSet,
1078    lite_command: &LiteCommand,
1079    parent_module: Option<&mut Module>,
1080) -> (Pipeline, Vec<Exportable>) {
1081    let spans = &lite_command.parts;
1082
1083    let (name_span, split_id) =
1084        if spans.len() > 1 && working_set.get_span_contents(spans[0]) == b"export" {
1085            (spans[1], 2)
1086        } else {
1087            (spans[0], 1)
1088        };
1089
1090    let use_call = working_set.get_span_contents(name_span).to_vec();
1091    if use_call != b"use" {
1092        working_set.error(ParseError::UnknownState(
1093            "internal error: Wrong call name for 'use' command".into(),
1094            Span::concat(spans),
1095        ));
1096        return (garbage_pipeline(working_set, spans), vec![]);
1097    }
1098
1099    if working_set.get_span_contents(name_span) != b"use" {
1100        working_set.error(ParseError::UnknownState(
1101            "internal error: Wrong call name for 'use' command".into(),
1102            Span::concat(spans),
1103        ));
1104        return (garbage_pipeline(working_set, spans), vec![]);
1105    }
1106
1107    if let Some(redirection) = lite_command.redirection.as_ref() {
1108        working_set.error(redirecting_builtin_error("use", redirection));
1109        return (garbage_pipeline(working_set, spans), vec![]);
1110    }
1111
1112    let (call, call_span, args_spans) = match working_set.find_decl(b"use") {
1113        Some(decl_id) => {
1114            let (command_spans, rest_spans) = spans.split_at(split_id);
1115
1116            let ParsedInternalCall {
1117                call,
1118                output,
1119                call_kind,
1120            } = parse_internal_call(
1121                working_set,
1122                Span::concat(command_spans),
1123                rest_spans,
1124                decl_id,
1125                ArgumentParsingLevel::Full,
1126                None,
1127            );
1128
1129            let call_span = Span::concat(spans);
1130            if call_kind != CallKind::Valid {
1131                return (
1132                    Pipeline::from_vec(vec![Expression::new(
1133                        working_set,
1134                        Expr::Call(call),
1135                        call_span,
1136                        output,
1137                    )]),
1138                    vec![],
1139                );
1140            }
1141
1142            (call, call_span, rest_spans)
1143        }
1144        None => {
1145            working_set.error(ParseError::UnknownState(
1146                "internal error: 'use' declaration not found".into(),
1147                Span::concat(spans),
1148            ));
1149            return (garbage_pipeline(working_set, spans), vec![]);
1150        }
1151    };
1152
1153    let import_pattern_expr = parse_import_pattern(working_set, call.positional_iter(), args_spans);
1154
1155    let import_pattern = match &import_pattern_expr {
1156        Expression {
1157            expr: Expr::Nothing,
1158            ..
1159        } => {
1160            let mut call = call;
1161            call.set_parser_info(
1162                "noop".to_string(),
1163                Expression::new_unknown(Expr::Nothing, Span::unknown(), Type::Nothing),
1164            );
1165            return (
1166                Pipeline::from_vec(vec![Expression::new(
1167                    working_set,
1168                    Expr::Call(call),
1169                    Span::concat(spans),
1170                    Type::Any,
1171                )]),
1172                vec![],
1173            );
1174        }
1175        Expression {
1176            expr: Expr::ImportPattern(import_pattern),
1177            ..
1178        } => import_pattern.clone(),
1179        _ => {
1180            working_set.error(ParseError::UnknownState(
1181                "internal error: Import pattern positional is not import pattern".into(),
1182                import_pattern_expr.span,
1183            ));
1184            return (garbage_pipeline(working_set, spans), vec![]);
1185        }
1186    };
1187
1188    let (mut import_pattern, module, module_id) = if let Some(module_id) = import_pattern.head.id {
1189        let module = working_set.get_module(module_id).clone();
1190        (
1191            ImportPattern {
1192                head: ImportPatternHead {
1193                    name: module.name.clone(),
1194                    id: Some(module_id),
1195                    span: import_pattern.head.span,
1196                },
1197                members: import_pattern.members,
1198                hidden: HashSet::new(),
1199                constants: vec![],
1200            },
1201            module,
1202            module_id,
1203        )
1204    } else if let Some(module_id) = parse_module_file_or_dir(
1205        working_set,
1206        &import_pattern.head.name,
1207        import_pattern.head.span,
1208        None,
1209    ) {
1210        let module = working_set.get_module(module_id).clone();
1211        (
1212            ImportPattern {
1213                head: ImportPatternHead {
1214                    name: module.name.clone(),
1215                    id: Some(module_id),
1216                    span: import_pattern.head.span,
1217                },
1218                members: import_pattern.members,
1219                hidden: HashSet::new(),
1220                constants: vec![],
1221            },
1222            module,
1223            module_id,
1224        )
1225    } else {
1226        working_set.error(ParseError::ModuleNotFound(
1227            import_pattern.head.span,
1228            String::from_utf8_lossy(&import_pattern.head.name).to_string(),
1229        ));
1230        return (
1231            Pipeline::from_vec(vec![Expression::new(
1232                working_set,
1233                Expr::Call(call),
1234                call_span,
1235                Type::Any,
1236            )]),
1237            vec![],
1238        );
1239    };
1240
1241    if check_main_keyword_shadow(
1242        working_set,
1243        module.main.is_some(),
1244        &module.name,
1245        import_pattern.head.span,
1246    ) {
1247        return (garbage_pipeline(working_set, &[call_span]), vec![]);
1248    }
1249
1250    let mut imported_modules = vec![];
1251    let (definitions, errors) = module.resolve_import_pattern(
1252        working_set,
1253        module_id,
1254        &import_pattern.members,
1255        None,
1256        name_span,
1257        &mut imported_modules,
1258    );
1259
1260    working_set.parse_errors.extend(errors);
1261
1262    let mut constants = vec![];
1263
1264    for (name, const_vid) in definitions.constants {
1265        constants.push((name, const_vid));
1266    }
1267
1268    for (name, const_val) in definitions.constant_values {
1269        let const_var_id =
1270            working_set.add_variable(name.clone(), name_span, const_val.get_type(), false);
1271        working_set.set_variable_const_val(const_var_id, const_val);
1272        constants.push((name, const_var_id));
1273    }
1274
1275    let exportables = definitions
1276        .decls
1277        .iter()
1278        .map(|(name, decl_id)| Exportable::Decl {
1279            name: name.clone(),
1280            id: *decl_id,
1281        })
1282        .chain(
1283            definitions
1284                .modules
1285                .iter()
1286                .map(|(name, module_id)| Exportable::Module {
1287                    name: name.clone(),
1288                    id: *module_id,
1289                }),
1290        )
1291        .chain(
1292            constants
1293                .iter()
1294                .map(|(name, variable_id)| Exportable::VarDecl {
1295                    name: name.clone(),
1296                    id: *variable_id,
1297                }),
1298        )
1299        .collect();
1300
1301    import_pattern.constants = constants.iter().map(|(_, id)| *id).collect();
1302
1303    if let Some(m) = parent_module {
1304        m.track_imported_modules(&imported_modules)
1305    }
1306    working_set.use_decls(definitions.decls);
1307    working_set.use_modules(definitions.modules);
1308    working_set.use_variables(constants);
1309
1310    let import_pattern_expr = Expression::new(
1311        working_set,
1312        Expr::ImportPattern(Box::new(import_pattern)),
1313        Span::concat(args_spans),
1314        Type::Any,
1315    );
1316
1317    let mut call = call;
1318    call.set_parser_info("import_pattern".to_string(), import_pattern_expr);
1319
1320    (
1321        Pipeline::from_vec(vec![Expression::new(
1322            working_set,
1323            Expr::Call(call),
1324            Span::concat(spans),
1325            Type::Any,
1326        )]),
1327        exportables,
1328    )
1329}
1330
1331pub fn parse_hide(working_set: &mut StateWorkingSet, lite_command: &LiteCommand) -> Pipeline {
1332    let spans = &lite_command.parts;
1333
1334    if working_set.get_span_contents(spans[0]) != b"hide" {
1335        working_set.error(ParseError::UnknownState(
1336            "internal error: Wrong call name for 'hide' command".into(),
1337            Span::concat(spans),
1338        ));
1339        return garbage_pipeline(working_set, spans);
1340    }
1341    if let Some(redirection) = lite_command.redirection.as_ref() {
1342        working_set.error(redirecting_builtin_error("hide", redirection));
1343        return garbage_pipeline(working_set, spans);
1344    }
1345
1346    let (call, args_spans) = match working_set.find_decl(b"hide") {
1347        Some(decl_id) => {
1348            let ParsedInternalCall {
1349                call,
1350                output,
1351                call_kind,
1352            } = parse_internal_call(
1353                working_set,
1354                spans[0],
1355                &spans[1..],
1356                decl_id,
1357                ArgumentParsingLevel::Full,
1358                None,
1359            );
1360
1361            if call_kind != CallKind::Valid {
1362                return Pipeline::from_vec(vec![Expression::new(
1363                    working_set,
1364                    Expr::Call(call),
1365                    Span::concat(spans),
1366                    output,
1367                )]);
1368            }
1369
1370            (call, &spans[1..])
1371        }
1372        None => {
1373            working_set.error(ParseError::UnknownState(
1374                "internal error: 'hide' declaration not found".into(),
1375                Span::concat(spans),
1376            ));
1377            return garbage_pipeline(working_set, spans);
1378        }
1379    };
1380
1381    let import_pattern_expr = parse_import_pattern(working_set, call.positional_iter(), args_spans);
1382
1383    let import_pattern = if let Expression {
1384        expr: Expr::ImportPattern(import_pattern),
1385        ..
1386    } = &import_pattern_expr
1387    {
1388        import_pattern.clone()
1389    } else {
1390        working_set.error(ParseError::UnknownState(
1391            "internal error: Import pattern positional is not import pattern".into(),
1392            import_pattern_expr.span,
1393        ));
1394        return garbage_pipeline(working_set, spans);
1395    };
1396
1397    let bytes = working_set.get_span_contents(spans[0]);
1398
1399    if bytes == b"hide" && spans.len() >= 2 {
1400        for span in spans[1..].iter() {
1401            parse_string(working_set, *span);
1402        }
1403
1404        let (is_module, module) =
1405            if let Some(module_id) = working_set.find_module(&import_pattern.head.name) {
1406                (true, working_set.get_module(module_id).clone())
1407            } else if import_pattern.members.is_empty() {
1408                if let Some(id) = working_set.find_decl(&import_pattern.head.name) {
1409                    let mut module = Module::new(b"tmp".to_vec());
1410                    module.add_decl(import_pattern.head.name.clone(), id);
1411
1412                    (false, module)
1413                } else {
1414                    (false, Module::new(b"tmp".to_vec()))
1415                }
1416            } else {
1417                working_set.error(ParseError::ModuleNotFound(
1418                    spans[1],
1419                    String::from_utf8_lossy(&import_pattern.head.name).to_string(),
1420                ));
1421                return garbage_pipeline(working_set, spans);
1422            };
1423
1424        let decls_to_hide = if import_pattern.members.is_empty() {
1425            if is_module {
1426                module.decl_names_with_head(&import_pattern.head.name)
1427            } else {
1428                module.decl_names()
1429            }
1430        } else {
1431            match &import_pattern.members[0] {
1432                ImportPatternMember::Glob { .. } => module.decl_names(),
1433                ImportPatternMember::Name { name, span } => {
1434                    let mut decls = vec![];
1435
1436                    if name == b"main" {
1437                        if module.main.is_some() {
1438                            decls.push(import_pattern.head.name.clone());
1439                        } else {
1440                            working_set.error(ParseError::ExportNotFound(*span));
1441                        }
1442                    } else if let Some(item) =
1443                        module.decl_name_with_head(name, &import_pattern.head.name)
1444                    {
1445                        decls.push(item);
1446                    } else {
1447                        working_set.error(ParseError::ExportNotFound(*span));
1448                    }
1449
1450                    decls
1451                }
1452                ImportPatternMember::List { names } => {
1453                    let mut decls = vec![];
1454
1455                    for (name, span) in names {
1456                        if name == b"main" {
1457                            if module.main.is_some() {
1458                                decls.push(import_pattern.head.name.clone());
1459                            } else {
1460                                working_set.error(ParseError::ExportNotFound(*span));
1461                                break;
1462                            }
1463                        } else if let Some(item) =
1464                            module.decl_name_with_head(name, &import_pattern.head.name)
1465                        {
1466                            decls.push(item);
1467                        } else {
1468                            working_set.error(ParseError::ExportNotFound(*span));
1469                            break;
1470                        }
1471                    }
1472
1473                    decls
1474                }
1475            }
1476        };
1477
1478        let import_pattern = {
1479            let decls: HashSet<Vec<u8>> = decls_to_hide.iter().cloned().collect();
1480
1481            import_pattern.with_hidden(decls)
1482        };
1483
1484        working_set.hide_decls(&decls_to_hide);
1485
1486        let import_pattern_expr = Expression::new(
1487            working_set,
1488            Expr::ImportPattern(Box::new(import_pattern)),
1489            Span::concat(args_spans),
1490            Type::Any,
1491        );
1492
1493        let mut call = call;
1494        call.set_parser_info("import_pattern".to_string(), import_pattern_expr);
1495
1496        Pipeline::from_vec(vec![Expression::new(
1497            working_set,
1498            Expr::Call(call),
1499            Span::concat(spans),
1500            Type::Any,
1501        )])
1502    } else {
1503        working_set.error(ParseError::UnknownState(
1504            "Expected structure: hide <name>".into(),
1505            Span::concat(spans),
1506        ));
1507        garbage_pipeline(working_set, spans)
1508    }
1509}
1510
1511pub fn parse_overlay_new(working_set: &mut StateWorkingSet, call: Box<Call>) -> Pipeline {
1512    let call_span = call.span();
1513
1514    let Some(expr) = call.positional_iter().next() else {
1515        working_set.error(ParseError::UnknownState(
1516            "internal error: Missing required positional after call parsing".into(),
1517            call_span,
1518        ));
1519        return garbage_pipeline(working_set, &[call_span]);
1520    };
1521
1522    let (overlay_name, _) =
1523        match eval_constant(working_set, expr).and_then(|v| v.coerce_into_string()) {
1524            Ok(s) => (s, expr.span),
1525            Err(err) => {
1526                working_set.error(err.wrap(working_set, call_span));
1527                return garbage_pipeline(working_set, &[call_span]);
1528            }
1529        };
1530
1531    let pipeline = Pipeline::from_vec(vec![Expression::new(
1532        working_set,
1533        Expr::Call(call),
1534        call_span,
1535        Type::Any,
1536    )]);
1537
1538    let module_id = working_set.add_module(
1539        &overlay_name,
1540        Module::new(overlay_name.as_bytes().to_vec()),
1541        vec![],
1542    );
1543
1544    working_set.add_overlay(
1545        overlay_name.as_bytes().to_vec(),
1546        module_id,
1547        nu_protocol::ResolvedImportPattern::new(vec![], vec![], vec![], vec![]),
1548        false,
1549    );
1550
1551    pipeline
1552}
1553
1554pub fn parse_overlay_use(working_set: &mut StateWorkingSet, call: Box<Call>) -> Pipeline {
1555    let call_span = call.span();
1556
1557    let (overlay_name_expr, as_name_expr) = {
1558        let mut iter = call.positional_iter();
1559        (iter.next(), iter.next())
1560    };
1561
1562    let Some(overlay_name_expr) = overlay_name_expr else {
1563        working_set.error(ParseError::UnknownState(
1564            "internal error: Missing required positional after call parsing".into(),
1565            call_span,
1566        ));
1567        return garbage_pipeline(working_set, &[call_span]);
1568    };
1569
1570    let (overlay_name, overlay_name_span) = match eval_constant(working_set, overlay_name_expr) {
1571        Ok(nu_protocol::Value::Nothing { .. }) => {
1572            let mut call = call;
1573            call.set_parser_info(
1574                "noop".to_string(),
1575                Expression::new_unknown(Expr::Bool(true), Span::unknown(), Type::Bool),
1576            );
1577            return Pipeline::from_vec(vec![Expression::new(
1578                working_set,
1579                Expr::Call(call),
1580                call_span,
1581                Type::Any,
1582            )]);
1583        }
1584        result => match result.and_then(|v| v.coerce_into_string()) {
1585            Ok(s) => (s, overlay_name_expr.span),
1586            Err(err) => {
1587                working_set.error(err.wrap(working_set, call_span));
1588                return garbage_pipeline(working_set, &[call_span]);
1589            }
1590        },
1591    };
1592
1593    let new_name = if let Some(as_name_expr) = as_name_expr {
1594        let Some((b"as", new_name_expression)) = as_name_expr.as_keyword_with_name() else {
1595            working_set.error(ParseError::ExpectedKeyword(
1596                "as keyword".to_string(),
1597                as_name_expr.span,
1598            ));
1599            return garbage_pipeline(working_set, &[call_span]);
1600        };
1601
1602        match eval_constant(working_set, new_name_expression).and_then(|v| v.coerce_into_string()) {
1603            Ok(s) => Some(nu_protocol::Spanned {
1604                item: s,
1605                span: new_name_expression.span,
1606            }),
1607            Err(err) => {
1608                working_set.error(err.wrap(working_set, call_span));
1609                return garbage_pipeline(working_set, &[call_span]);
1610            }
1611        }
1612    } else {
1613        None
1614    };
1615
1616    let Ok(has_prefix) = has_flag_const(working_set, &call, "prefix") else {
1617        return garbage_pipeline(working_set, &[call_span]);
1618    };
1619    let Ok(do_reload) = has_flag_const(working_set, &call, "reload") else {
1620        return garbage_pipeline(working_set, &[call_span]);
1621    };
1622
1623    let pipeline = Pipeline::from_vec(vec![Expression::new(
1624        working_set,
1625        Expr::Call(call.clone()),
1626        call_span,
1627        Type::Any,
1628    )]);
1629
1630    let (final_overlay_name, origin_module, origin_module_id, is_module_updated) =
1631        if let Some(overlay_frame) = working_set.find_overlay(overlay_name.as_bytes()) {
1632            if has_prefix && !overlay_frame.prefixed {
1633                working_set.error(ParseError::OverlayPrefixMismatch(
1634                    overlay_name,
1635                    "without".to_string(),
1636                    overlay_name_span,
1637                ));
1638                return pipeline;
1639            }
1640
1641            if !has_prefix && overlay_frame.prefixed {
1642                working_set.error(ParseError::OverlayPrefixMismatch(
1643                    overlay_name,
1644                    "with".to_string(),
1645                    overlay_name_span,
1646                ));
1647                return pipeline;
1648            }
1649
1650            if let Some(new_name) = new_name
1651                && new_name.item != overlay_name
1652            {
1653                working_set.error(ParseError::CantAddOverlayHelp(
1654                    format!(
1655                        "Cannot add overlay as '{}' because it already exists under the name '{}'",
1656                        new_name.item, overlay_name
1657                    ),
1658                    new_name.span,
1659                ));
1660                return pipeline;
1661            }
1662
1663            let module_id = overlay_frame.origin;
1664
1665            if let Some(new_module_id) = working_set.find_module(overlay_name.as_bytes()) {
1666                if !do_reload && (module_id == new_module_id) {
1667                    (
1668                        overlay_name,
1669                        Module::new(working_set.get_module(module_id).name.clone()),
1670                        module_id,
1671                        false,
1672                    )
1673                } else {
1674                    (
1675                        overlay_name,
1676                        working_set.get_module(new_module_id).clone(),
1677                        new_module_id,
1678                        true,
1679                    )
1680                }
1681            } else {
1682                let module_name = overlay_name.as_bytes().to_vec();
1683                (overlay_name, Module::new(module_name), module_id, true)
1684            }
1685        } else {
1686            if let Some(module_id) = working_set.find_module(overlay_name.as_bytes()) {
1687                (
1688                    new_name.map(|spanned| spanned.item).unwrap_or(overlay_name),
1689                    working_set.get_module(module_id).clone(),
1690                    module_id,
1691                    true,
1692                )
1693            } else if let Some(module_id) = parse_module_file_or_dir(
1694                working_set,
1695                overlay_name.as_bytes(),
1696                overlay_name_span,
1697                new_name.as_ref().map(|spanned| spanned.item.clone()),
1698            ) {
1699                let new_module = working_set.get_module(module_id).clone();
1700                (
1701                    new_name
1702                        .map(|spanned| spanned.item)
1703                        .unwrap_or_else(|| String::from_utf8_lossy(&new_module.name).to_string()),
1704                    new_module,
1705                    module_id,
1706                    true,
1707                )
1708            } else {
1709                working_set.error(ParseError::ModuleOrOverlayNotFound(overlay_name_span));
1710                return pipeline;
1711            }
1712        };
1713
1714    if check_main_keyword_shadow(
1715        working_set,
1716        origin_module.main.is_some(),
1717        final_overlay_name.as_bytes(),
1718        overlay_name_span,
1719    ) {
1720        return pipeline;
1721    }
1722
1723    let (definitions, errors) = if is_module_updated {
1724        if has_prefix {
1725            origin_module.resolve_import_pattern(
1726                working_set,
1727                origin_module_id,
1728                &[],
1729                Some(final_overlay_name.as_bytes()),
1730                call.head,
1731                &mut vec![],
1732            )
1733        } else {
1734            origin_module.resolve_import_pattern(
1735                working_set,
1736                origin_module_id,
1737                &[ImportPatternMember::Glob {
1738                    span: overlay_name_span,
1739                }],
1740                Some(final_overlay_name.as_bytes()),
1741                call.head,
1742                &mut vec![],
1743            )
1744        }
1745    } else {
1746        (
1747            nu_protocol::ResolvedImportPattern::new(vec![], vec![], vec![], vec![]),
1748            vec![],
1749        )
1750    };
1751
1752    if errors.is_empty() {
1753        working_set.add_overlay(
1754            final_overlay_name.as_bytes().to_vec(),
1755            origin_module_id,
1756            definitions,
1757            has_prefix,
1758        );
1759    } else {
1760        working_set.parse_errors.extend(errors);
1761    }
1762
1763    let mut call = call;
1764    call.set_parser_info(
1765        "overlay_expr".to_string(),
1766        Expression::new(
1767            working_set,
1768            Expr::Overlay(if is_module_updated {
1769                Some(origin_module_id)
1770            } else {
1771                None
1772            }),
1773            overlay_name_span,
1774            Type::Any,
1775        ),
1776    );
1777
1778    Pipeline::from_vec(vec![Expression::new(
1779        working_set,
1780        Expr::Call(call),
1781        call_span,
1782        Type::Any,
1783    )])
1784}
1785
1786pub fn parse_overlay_hide(working_set: &mut StateWorkingSet, call: Box<Call>) -> Pipeline {
1787    let call_span = call.span();
1788
1789    let (overlay_name, overlay_name_span) = if let Some(expr) = call.positional_iter().next() {
1790        match eval_constant(working_set, expr) {
1791            Ok(val) => match val.coerce_into_string() {
1792                Ok(s) => (s, expr.span),
1793                Err(err) => {
1794                    working_set.error(err.wrap(working_set, call_span));
1795                    return garbage_pipeline(working_set, &[call_span]);
1796                }
1797            },
1798            Err(err) => {
1799                working_set.error(err.wrap(working_set, call_span));
1800                return garbage_pipeline(working_set, &[call_span]);
1801            }
1802        }
1803    } else {
1804        (
1805            String::from_utf8_lossy(working_set.last_overlay_name()).to_string(),
1806            call_span,
1807        )
1808    };
1809
1810    let Ok(keep_custom) = has_flag_const(working_set, &call, "keep-custom") else {
1811        return garbage_pipeline(working_set, &[call_span]);
1812    };
1813
1814    let pipeline = Pipeline::from_vec(vec![Expression::new(
1815        working_set,
1816        Expr::Call(call),
1817        call_span,
1818        Type::Any,
1819    )]);
1820
1821    if overlay_name == nu_protocol::engine::DEFAULT_OVERLAY_NAME {
1822        working_set.error(ParseError::CantHideDefaultOverlay(
1823            overlay_name,
1824            overlay_name_span,
1825        ));
1826
1827        return pipeline;
1828    }
1829
1830    if !working_set
1831        .unique_overlay_names()
1832        .contains(&overlay_name.as_bytes())
1833    {
1834        working_set.error(ParseError::ActiveOverlayNotFound(overlay_name_span));
1835        return pipeline;
1836    }
1837
1838    if working_set.num_overlays() < 2 {
1839        working_set.error(ParseError::CantRemoveLastOverlay(overlay_name_span));
1840        return pipeline;
1841    }
1842
1843    working_set.remove_overlay(overlay_name.as_bytes(), keep_custom);
1844
1845    pipeline
1846}