Skip to main content

nu_parser/
parse_def.rs

1use crate::{
2    known_external::KnownExternal,
3    lite_parser::LiteCommand,
4    parse_helpers::{SPREAD_OPERATOR, garbage},
5    parse_keywords::{find_keyword_decl, reject_parser_keyword_name},
6    parse_pipelines::redirecting_builtin_error,
7    parser::{
8        ArgumentParsingLevel, CallKind, ParsedInternalCall, compile_block_with_id, parse_attribute,
9        parse_full_signature, parse_internal_call, parse_string,
10    },
11    type_check::check_block_input_output,
12};
13
14use itertools::Itertools;
15use nu_protocol::{
16    CommandWideCompleter, CustomExample, DeclId, FromValue, ParseError, PositionalArg, Signature,
17    Span, Spanned, SyntaxShape, Type, Value,
18    ast::{AttributeBlock, Call, Expr, Expression, Pipeline},
19    category_from_string,
20    engine::{CommandType, StateWorkingSet},
21    eval_const::eval_constant,
22    shell_error::generic::GenericError,
23};
24
25fn rest_param_is_type_annotated(signature_source: &[u8], rest_name: &str) -> bool {
26    let mut needle = Vec::with_capacity(rest_name.len() + 3);
27    needle.extend_from_slice(SPREAD_OPERATOR);
28    needle.extend_from_slice(rest_name.as_bytes());
29
30    if signature_source.len() < needle.len() {
31        return false;
32    }
33
34    for start in 0..=(signature_source.len() - needle.len()) {
35        if signature_source[start..start + needle.len()] != needle {
36            continue;
37        }
38
39        let mut idx = start + needle.len();
40        while idx < signature_source.len() && signature_source[idx].is_ascii_whitespace() {
41            idx += 1;
42        }
43
44        if idx < signature_source.len() && signature_source[idx] == b':' {
45            return true;
46        }
47    }
48
49    false
50}
51
52pub fn parse_def_predecl(working_set: &mut StateWorkingSet, spans: &[Span]) {
53    let mut pos = 0;
54
55    let def_type_name = if spans.len() >= 3 {
56        let first_word = working_set.get_span_contents(spans[0]);
57
58        if first_word == b"export" {
59            pos += 2;
60        } else {
61            pos += 1;
62        }
63
64        working_set.get_span_contents(spans[pos - 1]).to_vec()
65    } else {
66        return;
67    };
68
69    if def_type_name != b"def" && def_type_name != b"extern" {
70        return;
71    }
72
73    while pos < spans.len() && working_set.get_span_contents(spans[pos]).starts_with(b"-") {
74        pos += 1;
75    }
76
77    if pos >= spans.len() {
78        return;
79    }
80
81    let name_pos = pos;
82
83    let Some(name) = parse_string(working_set, spans[name_pos]).as_string() else {
84        return;
85    };
86
87    if name.contains('#')
88        || name.contains('^')
89        || name.contains('%')
90        || name.parse::<bytesize::ByteSize>().is_ok()
91        || name.parse::<f64>().is_ok()
92    {
93        working_set.error(ParseError::CommandDefNotValid(spans[name_pos]));
94        return;
95    }
96
97    if reject_parser_keyword_name(working_set, &name, "command", spans[name_pos]) {
98        return;
99    }
100
101    let mut signature_pos = None;
102
103    while pos < spans.len() {
104        if working_set.get_span_contents(spans[pos]).starts_with(b"[")
105            || working_set.get_span_contents(spans[pos]).starts_with(b"(")
106        {
107            signature_pos = Some(pos);
108            break;
109        }
110
111        pos += 1;
112    }
113
114    let Some(signature_pos) = signature_pos else {
115        return;
116    };
117
118    let mut allow_unknown_args = false;
119
120    for span in spans {
121        if working_set.get_span_contents(*span) == b"--wrapped" && def_type_name == b"def" {
122            allow_unknown_args = true;
123        }
124    }
125
126    let starting_error_count = working_set.parse_errors.len();
127
128    working_set.enter_scope();
129    let sig = parse_full_signature(
130        working_set,
131        &spans[signature_pos..],
132        def_type_name == b"extern",
133    );
134    working_set.parse_errors.truncate(starting_error_count);
135    working_set.exit_scope();
136
137    let Some(mut signature) = sig.as_signature() else {
138        return;
139    };
140
141    signature.name = name;
142
143    if allow_unknown_args {
144        if let Some(rest) = &mut signature.rest_positional
145            && !rest_param_is_type_annotated(
146                working_set.get_span_contents(spans[signature_pos]),
147                &rest.name,
148            )
149        {
150            rest.shape = SyntaxShape::ExternalArgument;
151        }
152        signature.allows_unknown_args = true;
153    }
154
155    let command_type = if def_type_name == b"extern" {
156        CommandType::External
157    } else {
158        CommandType::Custom
159    };
160
161    let decl = signature.predeclare_with_command_type(command_type);
162
163    if working_set.add_predecl(decl).is_some() {
164        working_set.error(ParseError::DuplicateCommandDef(spans[name_pos]));
165    }
166}
167
168pub fn parse_for(working_set: &mut StateWorkingSet, lite_command: &LiteCommand) -> Expression {
169    let spans = &lite_command.parts;
170    if working_set.get_span_contents(spans[0]) != b"for" {
171        working_set.error(ParseError::UnknownState(
172            "internal error: Wrong call name for 'for' function".into(),
173            Span::concat(spans),
174        ));
175        return garbage(working_set, spans[0]);
176    }
177    if let Some(redirection) = lite_command.redirection.as_ref() {
178        working_set.error(redirecting_builtin_error("for", redirection));
179        return garbage(working_set, spans[0]);
180    }
181
182    let Some(decl_id) = working_set.find_decl(b"for") else {
183        working_set.error(ParseError::UnknownState(
184            "internal error: for declaration not found".into(),
185            Span::concat(spans),
186        ));
187        return garbage(working_set, spans[0]);
188    };
189
190    let starting_error_count = working_set.parse_errors.len();
191    working_set.enter_scope();
192    let ParsedInternalCall {
193        call,
194        output,
195        call_kind,
196    } = parse_internal_call(
197        working_set,
198        spans[0],
199        &spans[1..],
200        decl_id,
201        ArgumentParsingLevel::Full,
202        None,
203    );
204
205    if working_set
206        .parse_errors
207        .get(starting_error_count..)
208        .is_none_or(|new_errors| {
209            new_errors
210                .iter()
211                .all(|e| !matches!(e, ParseError::Unclosed(token, ..) if *token == "}"))
212        })
213    {
214        working_set.exit_scope();
215    }
216
217    let call_span = Span::concat(spans);
218    let decl = working_set.get_decl(decl_id);
219    let sig = decl.signature();
220
221    if call_kind != CallKind::Valid {
222        return Expression::new(working_set, Expr::Call(call), call_span, output);
223    }
224
225    let [var_decl, iteration_expr, block_expr] = call
226        .positional_iter()
227        .next_array()
228        .expect("for call already checked");
229
230    if let Expression {
231        expr: Expr::Block(block_id) | Expr::RowCondition(block_id),
232        ..
233    } = block_expr
234    {
235        let block = working_set.get_block_mut(*block_id);
236
237        *block.signature = sig;
238    };
239
240    // `oneof` is usually flat, but yielded-type inference is recursive by
241    // definition: every union alternative may itself be an iterable.
242    fn yielded_type(ty: Type) -> Type {
243        match ty {
244            Type::List(item) => *item,
245            Type::Table(columns) => Type::Record(columns),
246            Type::Range => Type::Number,
247            Type::OneOf(types) => Type::one_of(types.into_iter().map(yielded_type)),
248            ty => ty,
249        }
250    }
251
252    // Infer the loop variable from yielded values, not from the iterable itself.
253    // Filter commands can return unions like `oneof<table, binary, list<any>>`,
254    // which yield records, binary chunks, or list items respectively.
255    let var_type = match iteration_expr.ty.clone() {
256        Type::OneOf(types) => Type::one_of(types.into_iter().map(yielded_type)),
257        ty => yielded_type(ty),
258    };
259
260    if let (Some(var_id), Some(block_id)) = (var_decl.as_var(), block_expr.as_block()) {
261        working_set.set_variable_type(var_id, var_type.clone());
262
263        let block = working_set.get_block_mut(block_id);
264        block.signature.required_positional.insert(
265            0,
266            PositionalArg {
267                name: String::new(),
268                desc: String::new(),
269                shape: var_type.to_shape(),
270                var_id: Some(var_id),
271                default_value: None,
272                completion: None,
273            },
274        );
275    }
276
277    Expression::new(working_set, Expr::Call(call), call_span, Type::Nothing)
278}
279
280pub fn parse_attribute_block(
281    working_set: &mut StateWorkingSet,
282    lite_command: &LiteCommand,
283) -> Pipeline {
284    let attributes = lite_command
285        .attribute_commands()
286        .map(|cmd| parse_attribute(working_set, &cmd).0)
287        .collect::<Vec<_>>();
288
289    let last_attr_span = attributes
290        .last()
291        .expect("Attribute block must contain at least one attribute")
292        .expr
293        .span;
294
295    working_set.error(ParseError::AttributeRequiresDefinition(last_attr_span));
296    let cmd_span = if lite_command.command_parts().is_empty() {
297        last_attr_span.past()
298    } else {
299        Span::concat(lite_command.command_parts())
300    };
301    let cmd_expr = garbage(working_set, cmd_span);
302    let ty = cmd_expr.ty.clone();
303
304    let attr_block_span = Span::merge_many(
305        attributes
306            .first()
307            .map(|x| x.expr.span)
308            .into_iter()
309            .chain(Some(cmd_span)),
310    );
311
312    Pipeline::from_vec(vec![Expression::new(
313        working_set,
314        Expr::AttributeBlock(AttributeBlock {
315            attributes,
316            item: Box::new(cmd_expr),
317        }),
318        attr_block_span,
319        ty,
320    )])
321}
322
323pub fn parse_def(
324    working_set: &mut StateWorkingSet,
325    lite_command: &LiteCommand,
326    module_name: Option<&[u8]>,
327) -> (Pipeline, Option<(Vec<u8>, DeclId)>) {
328    let mut attributes = vec![];
329    let mut attribute_vals = vec![];
330
331    for attr_cmd in lite_command.attribute_commands() {
332        let (attr, name) = parse_attribute(working_set, &attr_cmd);
333        if let Some(name) = name {
334            let val = eval_constant(working_set, &attr.expr);
335            match val {
336                Ok(val) => attribute_vals.push((name, val)),
337                Err(e) => working_set.error(e.wrap(working_set, attr.expr.span)),
338            }
339        }
340        attributes.push(attr);
341    }
342
343    let (expr, decl) = parse_def_inner(working_set, attribute_vals, lite_command, module_name);
344
345    let ty = expr.ty.clone();
346
347    let attr_block_span = Span::merge_many(
348        attributes
349            .first()
350            .map(|x| x.expr.span)
351            .into_iter()
352            .chain(Some(expr.span)),
353    );
354
355    let expr = if attributes.is_empty() {
356        expr
357    } else {
358        Expression::new(
359            working_set,
360            Expr::AttributeBlock(AttributeBlock {
361                attributes,
362                item: Box::new(expr),
363            }),
364            attr_block_span,
365            ty,
366        )
367    };
368
369    (Pipeline::from_vec(vec![expr]), decl)
370}
371
372pub fn parse_extern(
373    working_set: &mut StateWorkingSet,
374    lite_command: &LiteCommand,
375    module_name: Option<&[u8]>,
376) -> Pipeline {
377    let mut attributes = vec![];
378    let mut attribute_vals = vec![];
379
380    for attr_cmd in lite_command.attribute_commands() {
381        let (attr, name) = parse_attribute(working_set, &attr_cmd);
382        if let Some(name) = name {
383            let val = eval_constant(working_set, &attr.expr);
384            match val {
385                Ok(val) => attribute_vals.push((name, val)),
386                Err(e) => working_set.error(e.wrap(working_set, attr.expr.span)),
387            }
388        }
389        attributes.push(attr);
390    }
391
392    let expr = parse_extern_inner(working_set, attribute_vals, lite_command, module_name);
393
394    let ty = expr.ty.clone();
395
396    let attr_block_span = Span::merge_many(
397        attributes
398            .first()
399            .map(|x| x.expr.span)
400            .into_iter()
401            .chain(Some(expr.span)),
402    );
403
404    let expr = if attributes.is_empty() {
405        expr
406    } else {
407        Expression::new(
408            working_set,
409            Expr::AttributeBlock(AttributeBlock {
410                attributes,
411                item: Box::new(expr),
412            }),
413            attr_block_span,
414            ty,
415        )
416    };
417
418    Pipeline::from_vec(vec![expr])
419}
420
421fn parse_def_inner(
422    working_set: &mut StateWorkingSet,
423    attributes: Vec<(String, Value)>,
424    lite_command: &LiteCommand,
425    module_name: Option<&[u8]>,
426) -> (Expression, Option<(Vec<u8>, DeclId)>) {
427    let spans = lite_command.command_parts();
428
429    let (desc, extra_desc) = working_set.build_desc(&lite_command.comments);
430    let garbage_result =
431        |working_set: &mut StateWorkingSet<'_>| (garbage(working_set, Span::concat(spans)), None);
432
433    let (name_span, split_id) =
434        if spans.len() > 1 && working_set.get_span_contents(spans[0]) == b"export" {
435            (spans[1], 2)
436        } else {
437            (spans[0], 1)
438        };
439
440    let def_call = working_set.get_span_contents(name_span);
441    if def_call != b"def" {
442        working_set.error(ParseError::UnknownState(
443            "internal error: Wrong call name for def function".into(),
444            Span::concat(spans),
445        ));
446        return garbage_result(working_set);
447    }
448    if let Some(redirection) = lite_command.redirection.as_ref() {
449        working_set.error(redirecting_builtin_error("def", redirection));
450        return garbage_result(working_set);
451    }
452
453    // Prefer the keyword declaration so a previously-shadowed `def` command cannot
454    // hijack parsing (which previously panicked on incomplete input in the REPL).
455    let Some(decl_id) = find_keyword_decl(working_set, def_call)
456        .or_else(|| working_set.permanent_state.find_decl(def_call, &[]))
457    else {
458        working_set.error(ParseError::UnknownState(
459            "internal error: def declaration not found".into(),
460            Span::concat(spans),
461        ));
462        return garbage_result(working_set);
463    };
464
465    working_set.enter_scope();
466    let (command_spans, rest_spans) = spans.split_at(split_id);
467
468    let mut decl_name_span = None;
469
470    for span in rest_spans {
471        if !working_set.get_span_contents(*span).starts_with(b"-") {
472            decl_name_span = Some(*span);
473            break;
474        }
475    }
476
477    if let Some(name_span) = decl_name_span
478        && let Some(err) = detect_params_in_name(working_set, name_span, decl_id)
479    {
480        working_set.error(err);
481        return garbage_result(working_set);
482    }
483
484    let starting_error_count = working_set.parse_errors.len();
485    let ParsedInternalCall {
486        call,
487        output,
488        call_kind,
489    } = parse_internal_call(
490        working_set,
491        Span::concat(command_spans),
492        rest_spans,
493        decl_id,
494        ArgumentParsingLevel::Full,
495        None,
496    );
497
498    if working_set
499        .parse_errors
500        .get(starting_error_count..)
501        .is_none_or(|new_errors| {
502            new_errors
503                .iter()
504                .all(|e| !matches!(e, ParseError::Unclosed(token, ..) if *token == "}"))
505        })
506    {
507        working_set.exit_scope();
508    }
509
510    let call_span = Span::concat(spans);
511    let decl = working_set.get_decl(decl_id);
512    let sig = decl.signature();
513
514    match call.positional_iter().nth(2) {
515        Some(Expression {
516            expr: Expr::Closure(block_id),
517            ..
518        }) => {
519            compile_block_with_id(working_set, *block_id);
520            *working_set.get_block_mut(*block_id).signature = sig.clone();
521        }
522        Some(arg) => working_set.error(ParseError::Expected(
523            "definition body closure { ... }",
524            arg.span,
525        )),
526        None => (),
527    }
528
529    if call_kind != CallKind::Valid {
530        return (
531            Expression::new(working_set, Expr::Call(call), call_span, output),
532            None,
533        );
534    }
535
536    let Ok(has_env) = has_flag_const(working_set, &call, "env") else {
537        return garbage_result(working_set);
538    };
539    let Ok(has_wrapped) = has_flag_const(working_set, &call, "wrapped") else {
540        return garbage_result(working_set);
541    };
542
543    let Some([name_expr, sig_expr, block_expr]) = call.positional_iter().next_array() else {
544        working_set.error(ParseError::UnknownState(
545            "internal error: def call missing required positionals".into(),
546            call_span,
547        ));
548        return garbage_result(working_set);
549    };
550
551    let Some(name) = name_expr.as_string() else {
552        working_set.error(ParseError::UnknownState(
553            "Could not get string from string expression".into(),
554            name_expr.span,
555        ));
556        return garbage_result(working_set);
557    };
558
559    if reject_parser_keyword_name(working_set, &name, "command", name_expr.span) {
560        return (
561            Expression::new(working_set, Expr::Call(call), call_span, Type::Any),
562            None,
563        );
564    }
565
566    if let Some(mod_name) = module_name
567        && name.as_bytes() == mod_name
568    {
569        let name_expr_span = name_expr.span;
570
571        working_set.error(ParseError::NamedAsModule(
572            "command".to_string(),
573            name,
574            "main".to_string(),
575            name_expr_span,
576        ));
577        return (
578            Expression::new(working_set, Expr::Call(call), call_span, Type::Any),
579            None,
580        );
581    }
582
583    let mut result = None;
584
585    if let (Some(mut signature), Some(block_id)) = (sig_expr.as_signature(), block_expr.as_block())
586    {
587        if has_wrapped {
588            let Some(rest) = signature.rest_positional.as_mut() else {
589                working_set.error(ParseError::MissingPositional(
590                    "...rest-like positional argument".to_string(),
591                    name_expr.span,
592                    "def --wrapped must have a ...rest-like positional argument. \
593                            Add '...rest: string' to the command's signature."
594                        .to_string(),
595                ));
596
597                return (
598                    Expression::new(working_set, Expr::Call(call), call_span, Type::Any),
599                    result,
600                );
601            };
602
603            if !rest_param_is_type_annotated(
604                working_set.get_span_contents(sig_expr.span),
605                &rest.name,
606            ) {
607                rest.shape = SyntaxShape::ExternalArgument;
608            }
609
610            if let Some(var_id) = rest.var_id {
611                let rest_var = &working_set.get_variable(var_id);
612
613                if rest_var.ty != Type::Any && rest_var.ty != Type::List(Box::new(Type::String)) {
614                    working_set.error(ParseError::TypeMismatchHelp(
615                        Type::List(Box::new(Type::String)),
616                        rest_var.ty.clone(),
617                        rest_var.declaration_span,
618                        format!(
619                            "...rest-like positional argument used in 'def --wrapped' supports only strings. \
620                                Change the type annotation of ...{} to 'string'.",
621rest.name
622                        ),
623                    ));
624
625                    return (
626                        Expression::new(working_set, Expr::Call(call), call_span, Type::Any),
627                        result,
628                    );
629                }
630            }
631        }
632
633        if let Some(decl_id) = working_set.find_predecl(name.as_bytes()) {
634            signature.name.clone_from(&name);
635            if !has_wrapped {
636                *signature = signature.add_help();
637            }
638            signature.description = desc;
639            signature.extra_description = extra_desc;
640            signature.allows_unknown_args = has_wrapped;
641
642            let (attribute_vals, examples) =
643                handle_special_attributes(attributes, working_set, &mut signature);
644
645            let declaration = working_set.get_decl_mut(decl_id);
646
647            *declaration = signature
648                .clone()
649                .into_block_command(block_id, attribute_vals, examples);
650
651            let block = working_set.get_block_mut(block_id);
652            block.signature = signature;
653            block.redirect_env = has_env;
654
655            if block.signature.input_output_types.is_empty() {
656                block
657                    .signature
658                    .input_output_types
659                    .push((Type::Any, Type::Any));
660            }
661
662            let block = working_set.get_block(block_id);
663
664            let typecheck_errors = check_block_input_output(working_set, block);
665
666            working_set
667                .parse_errors
668                .extend_from_slice(&typecheck_errors);
669
670            result = Some((name.as_bytes().to_vec(), decl_id));
671        } else {
672            working_set.error(ParseError::InternalError(
673                "Predeclaration failed to add declaration".into(),
674                name_expr.span,
675            ));
676        };
677    }
678
679    working_set.merge_predecl(name.as_bytes());
680
681    (
682        Expression::new(working_set, Expr::Call(call), call_span, Type::Any),
683        result,
684    )
685}
686
687fn parse_extern_inner(
688    working_set: &mut StateWorkingSet,
689    attributes: Vec<(String, Value)>,
690    lite_command: &LiteCommand,
691    module_name: Option<&[u8]>,
692) -> Expression {
693    let spans = lite_command.command_parts();
694
695    let (description, extra_description) = working_set.build_desc(&lite_command.comments);
696
697    let (name_span, split_id) =
698        if spans.len() > 1 && (working_set.get_span_contents(spans[0]) == b"export") {
699            (spans[1], 2)
700        } else {
701            (spans[0], 1)
702        };
703
704    let extern_call = working_set.get_span_contents(name_span);
705    if extern_call != b"extern" {
706        working_set.error(ParseError::UnknownState(
707            "internal error: Wrong call name for extern command".into(),
708            Span::concat(spans),
709        ));
710        return garbage(working_set, Span::concat(spans));
711    }
712    if let Some(redirection) = lite_command.redirection.as_ref() {
713        working_set.error(redirecting_builtin_error("extern", redirection));
714        return garbage(working_set, Span::concat(spans));
715    }
716
717    let (call, call_span) = match find_keyword_decl(working_set, extern_call)
718        .or_else(|| working_set.permanent().find_decl(extern_call, &[]))
719    {
720        None => {
721            working_set.error(ParseError::UnknownState(
722                "internal error: extern declaration not found".into(),
723                Span::concat(spans),
724            ));
725            return garbage(working_set, Span::concat(spans));
726        }
727        Some(decl_id) => {
728            working_set.enter_scope();
729
730            let (command_spans, rest_spans) = spans.split_at(split_id);
731
732            if let Some(name_span) = rest_spans.first()
733                && let Some(err) = detect_params_in_name(working_set, *name_span, decl_id)
734            {
735                working_set.error(err);
736                return garbage(working_set, Span::concat(spans));
737            }
738
739            let ParsedInternalCall { call, .. } = parse_internal_call(
740                working_set,
741                Span::concat(command_spans),
742                rest_spans,
743                decl_id,
744                ArgumentParsingLevel::Full,
745                None,
746            );
747            working_set.exit_scope();
748
749            let call_span = Span::concat(spans);
750
751            (call, call_span)
752        }
753    };
754
755    let (name_and_sig_exprs, body_expr) = {
756        let mut positional_iter = call.positional_iter();
757        (positional_iter.next_array::<2>(), positional_iter.next())
758    };
759
760    if let Some([name_expr, sig]) = name_and_sig_exprs {
761        if let (Some(name), Some(mut signature)) = (&name_expr.as_string(), sig.as_signature()) {
762            if reject_parser_keyword_name(working_set, name, "command", name_expr.span) {
763                return Expression::new(working_set, Expr::Call(call), call_span, Type::Any);
764            }
765
766            if let Some(mod_name) = module_name
767                && name.as_bytes() == mod_name
768            {
769                let name_expr_span = name_expr.span;
770                working_set.error(ParseError::NamedAsModule(
771                    "known external".to_string(),
772                    name.clone(),
773                    "main".to_string(),
774                    name_expr_span,
775                ));
776                return Expression::new(working_set, Expr::Call(call), call_span, Type::Any);
777            }
778
779            if let Some(decl_id) = working_set.find_predecl(name.as_bytes()) {
780                let external_name = if let Some(mod_name) = module_name {
781                    if name.as_bytes() == b"main" {
782                        String::from_utf8_lossy(mod_name).to_string()
783                    } else {
784                        name.clone()
785                    }
786                } else {
787                    name.clone()
788                };
789
790                signature.name = external_name;
791                signature.description = description;
792                signature.extra_description = extra_description;
793                signature.allows_unknown_args = true;
794
795                let (attribute_vals, examples) =
796                    handle_special_attributes(attributes, working_set, &mut signature);
797
798                let declaration = working_set.get_decl_mut(decl_id);
799
800                if let Some(block_id) = body_expr.and_then(|x| x.as_block()) {
801                    if signature.rest_positional.is_none() {
802                        working_set.error(ParseError::InternalError(
803                            "Extern block must have a rest positional argument".into(),
804                            name_expr.span,
805                        ));
806                    } else {
807                        *declaration = signature.clone().into_block_command(
808                            block_id,
809                            attribute_vals,
810                            examples,
811                        );
812
813                        working_set.get_block_mut(block_id).signature = signature;
814                    }
815                } else {
816                    if signature.rest_positional.is_none() {
817                        *signature = signature.rest(
818                            "args",
819                            SyntaxShape::ExternalArgument,
820                            "All other arguments to the command.",
821                        );
822                    }
823
824                    let decl = KnownExternal {
825                        signature,
826                        attributes: attribute_vals,
827                        examples,
828                        span: call_span,
829                    };
830
831                    *declaration = Box::new(decl);
832                }
833            } else {
834                working_set.error(ParseError::InternalError(
835                    "Predeclaration failed to add declaration".into(),
836                    spans[split_id],
837                ));
838            };
839        }
840        if let Some(name) = name_expr.as_string() {
841            working_set.merge_predecl(name.as_bytes());
842        } else {
843            working_set.error(ParseError::UnknownState(
844                "Could not get string from string expression".into(),
845                name_expr.span,
846            ));
847        }
848    }
849
850    Expression::new(working_set, Expr::Call(call), call_span, Type::Any)
851}
852
853fn handle_special_attributes(
854    attributes: Vec<(String, Value)>,
855    working_set: &mut StateWorkingSet<'_>,
856    signature: &mut Signature,
857) -> (Vec<(String, Value)>, Vec<CustomExample>) {
858    let mut attribute_vals = vec![];
859    let mut examples = vec![];
860    let mut search_terms = vec![];
861    let mut category = String::new();
862
863    for (name, value) in attributes {
864        let val_span = value.span();
865        match name.as_str() {
866            "example" => match CustomExample::from_value(value) {
867                Ok(example) => examples.push(example),
868                Err(_) => {
869                    let e = nu_protocol::ShellError::Generic(
870                        GenericError::new(
871                            "nu::shell::invalid_example",
872                            "Value couldn't be converted to an example",
873                            val_span,
874                        )
875                        .with_help("Is `attr example` shadowed?"),
876                    );
877                    working_set.error(e.wrap(working_set, val_span));
878                }
879            },
880            "search-terms" => match <Vec<String>>::from_value(value) {
881                Ok(mut terms) => {
882                    search_terms.append(&mut terms);
883                }
884                Err(_) => {
885                    let e = nu_protocol::ShellError::Generic(
886                        GenericError::new(
887                            "nu::shell::invalid_search_terms",
888                            "Value couldn't be converted to search-terms",
889                            val_span,
890                        )
891                        .with_help("Is `attr search-terms` shadowed?"),
892                    );
893                    working_set.error(e.wrap(working_set, val_span));
894                }
895            },
896            "category" => match <String>::from_value(value) {
897                Ok(term) => {
898                    category.push_str(&term);
899                }
900                Err(_) => {
901                    let e = nu_protocol::ShellError::Generic(
902                        GenericError::new(
903                            "nu::shell::invalid_category",
904                            "Value couldn't be converted to category",
905                            val_span,
906                        )
907                        .with_help("Is `attr category` shadowed?"),
908                    );
909                    working_set.error(e.wrap(working_set, val_span));
910                }
911            },
912            "complete" => match <Spanned<String>>::from_value(value) {
913                Ok(Spanned { item, span }) => {
914                    if let Some(decl) = working_set.find_decl(item.as_bytes()) {
915                        signature.complete = Some(CommandWideCompleter::Command(decl));
916                    } else {
917                        working_set.error(ParseError::UnknownCommand(span));
918                    }
919                }
920                Err(_) => {
921                    let e = nu_protocol::ShellError::Generic(
922                        GenericError::new(
923                            "nu::shell::invalid_completer",
924                            "Value couldn't be converted to a completer",
925                            val_span,
926                        )
927                        .with_help("Is `attr complete` shadowed?"),
928                    );
929                    working_set.error(e.wrap(working_set, val_span));
930                }
931            },
932            "complete external" => match value {
933                nu_protocol::Value::Nothing { .. } => {
934                    signature.complete = Some(CommandWideCompleter::External);
935                }
936                _ => {
937                    let e = nu_protocol::ShellError::Generic(
938                        GenericError::new(
939                            "nu::shell::invalid_completer",
940                            "This attribute shouldn't return anything",
941                            val_span,
942                        )
943                        .with_help("Is `attr complete` shadowed?"),
944                    );
945                    working_set.error(e.wrap(working_set, val_span));
946                }
947            },
948            _ => {
949                attribute_vals.push((name, value));
950            }
951        }
952    }
953
954    signature.search_terms = search_terms;
955    signature.category = category_from_string(&category);
956
957    (attribute_vals, examples)
958}
959
960fn detect_params_in_name(
961    working_set: &StateWorkingSet,
962    name_span: Span,
963    decl_id: DeclId,
964) -> Option<ParseError> {
965    let name = working_set.get_span_contents(name_span);
966    for (offset, char) in name.iter().enumerate() {
967        if *char == b'[' || *char == b'(' {
968            return Some(ParseError::LabeledErrorWithHelp {
969                error: "no space between name and parameters".into(),
970                label: "expected space".into(),
971                help: format!(
972                    "consider adding a space between the `{}` command's name and its parameters",
973                    working_set.get_decl(decl_id).name()
974                ),
975                span: Span::new(offset + name_span.start - 1, offset + name_span.start - 1),
976            });
977        }
978    }
979
980    None
981}
982
983pub(crate) fn has_flag_const(
984    working_set: &mut StateWorkingSet,
985    call: &Call,
986    name: &str,
987) -> Result<bool, ()> {
988    call.has_flag_const(working_set, name).map_err(|err| {
989        working_set.error(err.wrap(working_set, call.span()));
990    })
991}