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