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    // `oneof` is usually flat, but yielded-type inference is recursive by
236    // definition: every union alternative may itself be an iterable.
237    fn yielded_type(ty: Type) -> Type {
238        match ty {
239            Type::List(item) => *item,
240            Type::Table(columns) => Type::Record(columns),
241            Type::Range => Type::Number,
242            Type::OneOf(types) => Type::one_of(types.into_iter().map(yielded_type)),
243            ty => ty,
244        }
245    }
246
247    // Infer the loop variable from yielded values, not from the iterable itself.
248    // Filter commands can return unions like `oneof<table, binary, list<any>>`,
249    // which yield records, binary chunks, or list items respectively.
250    let var_type = match iteration_expr.ty.clone() {
251        Type::OneOf(types) => Type::one_of(types.into_iter().map(yielded_type)),
252        ty => yielded_type(ty),
253    };
254
255    if let (Some(var_id), Some(block_id)) = (var_decl.as_var(), block_expr.as_block()) {
256        working_set.set_variable_type(var_id, var_type.clone());
257
258        let block = working_set.get_block_mut(block_id);
259        block.signature.required_positional.insert(
260            0,
261            PositionalArg {
262                name: String::new(),
263                desc: String::new(),
264                shape: var_type.to_shape(),
265                var_id: Some(var_id),
266                default_value: None,
267                completion: None,
268            },
269        );
270    }
271
272    Expression::new(working_set, Expr::Call(call), call_span, Type::Nothing)
273}
274
275pub fn parse_attribute_block(
276    working_set: &mut StateWorkingSet,
277    lite_command: &LiteCommand,
278) -> Pipeline {
279    let attributes = lite_command
280        .attribute_commands()
281        .map(|cmd| parse_attribute(working_set, &cmd).0)
282        .collect::<Vec<_>>();
283
284    let last_attr_span = attributes
285        .last()
286        .expect("Attribute block must contain at least one attribute")
287        .expr
288        .span;
289
290    working_set.error(ParseError::AttributeRequiresDefinition(last_attr_span));
291    let cmd_span = if lite_command.command_parts().is_empty() {
292        last_attr_span.past()
293    } else {
294        Span::concat(lite_command.command_parts())
295    };
296    let cmd_expr = garbage(working_set, cmd_span);
297    let ty = cmd_expr.ty.clone();
298
299    let attr_block_span = Span::merge_many(
300        attributes
301            .first()
302            .map(|x| x.expr.span)
303            .into_iter()
304            .chain(Some(cmd_span)),
305    );
306
307    Pipeline::from_vec(vec![Expression::new(
308        working_set,
309        Expr::AttributeBlock(AttributeBlock {
310            attributes,
311            item: Box::new(cmd_expr),
312        }),
313        attr_block_span,
314        ty,
315    )])
316}
317
318pub fn parse_def(
319    working_set: &mut StateWorkingSet,
320    lite_command: &LiteCommand,
321    module_name: Option<&[u8]>,
322) -> (Pipeline, Option<(Vec<u8>, DeclId)>) {
323    let mut attributes = vec![];
324    let mut attribute_vals = vec![];
325
326    for attr_cmd in lite_command.attribute_commands() {
327        let (attr, name) = parse_attribute(working_set, &attr_cmd);
328        if let Some(name) = name {
329            let val = eval_constant(working_set, &attr.expr);
330            match val {
331                Ok(val) => attribute_vals.push((name, val)),
332                Err(e) => working_set.error(e.wrap(working_set, attr.expr.span)),
333            }
334        }
335        attributes.push(attr);
336    }
337
338    let (expr, decl) = parse_def_inner(working_set, attribute_vals, lite_command, module_name);
339
340    let ty = expr.ty.clone();
341
342    let attr_block_span = Span::merge_many(
343        attributes
344            .first()
345            .map(|x| x.expr.span)
346            .into_iter()
347            .chain(Some(expr.span)),
348    );
349
350    let expr = if attributes.is_empty() {
351        expr
352    } else {
353        Expression::new(
354            working_set,
355            Expr::AttributeBlock(AttributeBlock {
356                attributes,
357                item: Box::new(expr),
358            }),
359            attr_block_span,
360            ty,
361        )
362    };
363
364    (Pipeline::from_vec(vec![expr]), decl)
365}
366
367pub fn parse_extern(
368    working_set: &mut StateWorkingSet,
369    lite_command: &LiteCommand,
370    module_name: Option<&[u8]>,
371) -> Pipeline {
372    let mut attributes = vec![];
373    let mut attribute_vals = vec![];
374
375    for attr_cmd in lite_command.attribute_commands() {
376        let (attr, name) = parse_attribute(working_set, &attr_cmd);
377        if let Some(name) = name {
378            let val = eval_constant(working_set, &attr.expr);
379            match val {
380                Ok(val) => attribute_vals.push((name, val)),
381                Err(e) => working_set.error(e.wrap(working_set, attr.expr.span)),
382            }
383        }
384        attributes.push(attr);
385    }
386
387    let expr = parse_extern_inner(working_set, attribute_vals, lite_command, module_name);
388
389    let ty = expr.ty.clone();
390
391    let attr_block_span = Span::merge_many(
392        attributes
393            .first()
394            .map(|x| x.expr.span)
395            .into_iter()
396            .chain(Some(expr.span)),
397    );
398
399    let expr = if attributes.is_empty() {
400        expr
401    } else {
402        Expression::new(
403            working_set,
404            Expr::AttributeBlock(AttributeBlock {
405                attributes,
406                item: Box::new(expr),
407            }),
408            attr_block_span,
409            ty,
410        )
411    };
412
413    Pipeline::from_vec(vec![expr])
414}
415
416fn parse_def_inner(
417    working_set: &mut StateWorkingSet,
418    attributes: Vec<(String, Value)>,
419    lite_command: &LiteCommand,
420    module_name: Option<&[u8]>,
421) -> (Expression, Option<(Vec<u8>, DeclId)>) {
422    let spans = lite_command.command_parts();
423
424    let (desc, extra_desc) = working_set.build_desc(&lite_command.comments);
425    let garbage_result =
426        |working_set: &mut StateWorkingSet<'_>| (garbage(working_set, Span::concat(spans)), None);
427
428    let (name_span, split_id) =
429        if spans.len() > 1 && working_set.get_span_contents(spans[0]) == b"export" {
430            (spans[1], 2)
431        } else {
432            (spans[0], 1)
433        };
434
435    let def_call = working_set.get_span_contents(name_span);
436    if def_call != b"def" {
437        working_set.error(ParseError::UnknownState(
438            "internal error: Wrong call name for def function".into(),
439            Span::concat(spans),
440        ));
441        return garbage_result(working_set);
442    }
443    if let Some(redirection) = lite_command.redirection.as_ref() {
444        working_set.error(redirecting_builtin_error("def", redirection));
445        return garbage_result(working_set);
446    }
447
448    let Some(decl_id) = working_set.permanent_state.find_decl(def_call, &[]) else {
449        working_set.error(ParseError::UnknownState(
450            "internal error: def declaration not found".into(),
451            Span::concat(spans),
452        ));
453        return garbage_result(working_set);
454    };
455
456    working_set.enter_scope();
457    let (command_spans, rest_spans) = spans.split_at(split_id);
458
459    let mut decl_name_span = None;
460
461    for span in rest_spans {
462        if !working_set.get_span_contents(*span).starts_with(b"-") {
463            decl_name_span = Some(*span);
464            break;
465        }
466    }
467
468    if let Some(name_span) = decl_name_span
469        && let Some(err) = detect_params_in_name(working_set, name_span, decl_id)
470    {
471        working_set.error(err);
472        return garbage_result(working_set);
473    }
474
475    let starting_error_count = working_set.parse_errors.len();
476    let ParsedInternalCall {
477        call,
478        output,
479        call_kind,
480    } = parse_internal_call(
481        working_set,
482        Span::concat(command_spans),
483        rest_spans,
484        decl_id,
485        ArgumentParsingLevel::Full,
486        None,
487    );
488
489    if working_set
490        .parse_errors
491        .get(starting_error_count..)
492        .is_none_or(|new_errors| {
493            new_errors
494                .iter()
495                .all(|e| !matches!(e, ParseError::Unclosed(token, _) if *token == "}"))
496        })
497    {
498        working_set.exit_scope();
499    }
500
501    let call_span = Span::concat(spans);
502    let decl = working_set.get_decl(decl_id);
503    let sig = decl.signature();
504
505    match call.positional_iter().nth(2) {
506        Some(Expression {
507            expr: Expr::Closure(block_id),
508            ..
509        }) => {
510            compile_block_with_id(working_set, *block_id);
511            *working_set.get_block_mut(*block_id).signature = sig.clone();
512        }
513        Some(arg) => working_set.error(ParseError::Expected(
514            "definition body closure { ... }",
515            arg.span,
516        )),
517        None => (),
518    }
519
520    if call_kind != CallKind::Valid {
521        return (
522            Expression::new(working_set, Expr::Call(call), call_span, output),
523            None,
524        );
525    }
526
527    let Ok(has_env) = has_flag_const(working_set, &call, "env") else {
528        return garbage_result(working_set);
529    };
530    let Ok(has_wrapped) = has_flag_const(working_set, &call, "wrapped") else {
531        return garbage_result(working_set);
532    };
533
534    let [name_expr, sig_expr, block_expr] = call
535        .positional_iter()
536        .next_array()
537        .expect("def call already checked");
538
539    let Some(name) = name_expr.as_string() else {
540        working_set.error(ParseError::UnknownState(
541            "Could not get string from string expression".into(),
542            name_expr.span,
543        ));
544        return garbage_result(working_set);
545    };
546
547    if let Some(mod_name) = module_name
548        && name.as_bytes() == mod_name
549    {
550        let name_expr_span = name_expr.span;
551
552        working_set.error(ParseError::NamedAsModule(
553            "command".to_string(),
554            name,
555            "main".to_string(),
556            name_expr_span,
557        ));
558        return (
559            Expression::new(working_set, Expr::Call(call), call_span, Type::Any),
560            None,
561        );
562    }
563
564    let mut result = None;
565
566    if let (Some(mut signature), Some(block_id)) = (sig_expr.as_signature(), block_expr.as_block())
567    {
568        if has_wrapped {
569            let Some(rest) = signature.rest_positional.as_mut() else {
570                working_set.error(ParseError::MissingPositional(
571                    "...rest-like positional argument".to_string(),
572                    name_expr.span,
573                    "def --wrapped must have a ...rest-like positional argument. \
574                            Add '...rest: string' to the command's signature."
575                        .to_string(),
576                ));
577
578                return (
579                    Expression::new(working_set, Expr::Call(call), call_span, Type::Any),
580                    result,
581                );
582            };
583
584            if !rest_param_is_type_annotated(
585                working_set.get_span_contents(sig_expr.span),
586                &rest.name,
587            ) {
588                rest.shape = SyntaxShape::ExternalArgument;
589            }
590
591            if let Some(var_id) = rest.var_id {
592                let rest_var = &working_set.get_variable(var_id);
593
594                if rest_var.ty != Type::Any && rest_var.ty != Type::List(Box::new(Type::String)) {
595                    working_set.error(ParseError::TypeMismatchHelp(
596                        Type::List(Box::new(Type::String)),
597                        rest_var.ty.clone(),
598                        rest_var.declaration_span,
599                        format!(
600                            "...rest-like positional argument used in 'def --wrapped' supports only strings. \
601                                Change the type annotation of ...{} to 'string'.",
602                            &rest.name
603                        ),
604                    ));
605
606                    return (
607                        Expression::new(working_set, Expr::Call(call), call_span, Type::Any),
608                        result,
609                    );
610                }
611            }
612        }
613
614        if let Some(decl_id) = working_set.find_predecl(name.as_bytes()) {
615            signature.name.clone_from(&name);
616            if !has_wrapped {
617                *signature = signature.add_help();
618            }
619            signature.description = desc;
620            signature.extra_description = extra_desc;
621            signature.allows_unknown_args = has_wrapped;
622
623            let (attribute_vals, examples) =
624                handle_special_attributes(attributes, working_set, &mut signature);
625
626            let declaration = working_set.get_decl_mut(decl_id);
627
628            *declaration = signature
629                .clone()
630                .into_block_command(block_id, attribute_vals, examples);
631
632            let block = working_set.get_block_mut(block_id);
633            block.signature = signature;
634            block.redirect_env = has_env;
635
636            if block.signature.input_output_types.is_empty() {
637                block
638                    .signature
639                    .input_output_types
640                    .push((Type::Any, Type::Any));
641            }
642
643            let block = working_set.get_block(block_id);
644
645            let typecheck_errors = check_block_input_output(working_set, block);
646
647            working_set
648                .parse_errors
649                .extend_from_slice(&typecheck_errors);
650
651            result = Some((name.as_bytes().to_vec(), decl_id));
652        } else {
653            working_set.error(ParseError::InternalError(
654                "Predeclaration failed to add declaration".into(),
655                name_expr.span,
656            ));
657        };
658    }
659
660    working_set.merge_predecl(name.as_bytes());
661
662    (
663        Expression::new(working_set, Expr::Call(call), call_span, Type::Any),
664        result,
665    )
666}
667
668fn parse_extern_inner(
669    working_set: &mut StateWorkingSet,
670    attributes: Vec<(String, Value)>,
671    lite_command: &LiteCommand,
672    module_name: Option<&[u8]>,
673) -> Expression {
674    let spans = lite_command.command_parts();
675
676    let (description, extra_description) = working_set.build_desc(&lite_command.comments);
677
678    let (name_span, split_id) =
679        if spans.len() > 1 && (working_set.get_span_contents(spans[0]) == b"export") {
680            (spans[1], 2)
681        } else {
682            (spans[0], 1)
683        };
684
685    let extern_call = working_set.get_span_contents(name_span);
686    if extern_call != b"extern" {
687        working_set.error(ParseError::UnknownState(
688            "internal error: Wrong call name for extern command".into(),
689            Span::concat(spans),
690        ));
691        return garbage(working_set, Span::concat(spans));
692    }
693    if let Some(redirection) = lite_command.redirection.as_ref() {
694        working_set.error(redirecting_builtin_error("extern", redirection));
695        return garbage(working_set, Span::concat(spans));
696    }
697
698    let (call, call_span) = match working_set.permanent().find_decl(extern_call, &[]) {
699        None => {
700            working_set.error(ParseError::UnknownState(
701                "internal error: def declaration not found".into(),
702                Span::concat(spans),
703            ));
704            return garbage(working_set, Span::concat(spans));
705        }
706        Some(decl_id) => {
707            working_set.enter_scope();
708
709            let (command_spans, rest_spans) = spans.split_at(split_id);
710
711            if let Some(name_span) = rest_spans.first()
712                && let Some(err) = detect_params_in_name(working_set, *name_span, decl_id)
713            {
714                working_set.error(err);
715                return garbage(working_set, Span::concat(spans));
716            }
717
718            let ParsedInternalCall { call, .. } = parse_internal_call(
719                working_set,
720                Span::concat(command_spans),
721                rest_spans,
722                decl_id,
723                ArgumentParsingLevel::Full,
724                None,
725            );
726            working_set.exit_scope();
727
728            let call_span = Span::concat(spans);
729
730            (call, call_span)
731        }
732    };
733
734    let (name_and_sig_exprs, body_expr) = {
735        let mut positional_iter = call.positional_iter();
736        (positional_iter.next_array::<2>(), positional_iter.next())
737    };
738
739    if let Some([name_expr, sig]) = name_and_sig_exprs {
740        if let (Some(name), Some(mut signature)) = (&name_expr.as_string(), sig.as_signature()) {
741            if let Some(mod_name) = module_name
742                && name.as_bytes() == mod_name
743            {
744                let name_expr_span = name_expr.span;
745                working_set.error(ParseError::NamedAsModule(
746                    "known external".to_string(),
747                    name.clone(),
748                    "main".to_string(),
749                    name_expr_span,
750                ));
751                return Expression::new(working_set, Expr::Call(call), call_span, Type::Any);
752            }
753
754            if let Some(decl_id) = working_set.find_predecl(name.as_bytes()) {
755                let external_name = if let Some(mod_name) = module_name {
756                    if name.as_bytes() == b"main" {
757                        String::from_utf8_lossy(mod_name).to_string()
758                    } else {
759                        name.clone()
760                    }
761                } else {
762                    name.clone()
763                };
764
765                signature.name = external_name;
766                signature.description = description;
767                signature.extra_description = extra_description;
768                signature.allows_unknown_args = true;
769
770                let (attribute_vals, examples) =
771                    handle_special_attributes(attributes, working_set, &mut signature);
772
773                let declaration = working_set.get_decl_mut(decl_id);
774
775                if let Some(block_id) = body_expr.and_then(|x| x.as_block()) {
776                    if signature.rest_positional.is_none() {
777                        working_set.error(ParseError::InternalError(
778                            "Extern block must have a rest positional argument".into(),
779                            name_expr.span,
780                        ));
781                    } else {
782                        *declaration = signature.clone().into_block_command(
783                            block_id,
784                            attribute_vals,
785                            examples,
786                        );
787
788                        working_set.get_block_mut(block_id).signature = signature;
789                    }
790                } else {
791                    if signature.rest_positional.is_none() {
792                        *signature = signature.rest(
793                            "args",
794                            SyntaxShape::ExternalArgument,
795                            "All other arguments to the command.",
796                        );
797                    }
798
799                    let decl = KnownExternal {
800                        signature,
801                        attributes: attribute_vals,
802                        examples,
803                        span: call_span,
804                    };
805
806                    *declaration = Box::new(decl);
807                }
808            } else {
809                working_set.error(ParseError::InternalError(
810                    "Predeclaration failed to add declaration".into(),
811                    spans[split_id],
812                ));
813            };
814        }
815        if let Some(name) = name_expr.as_string() {
816            working_set.merge_predecl(name.as_bytes());
817        } else {
818            working_set.error(ParseError::UnknownState(
819                "Could not get string from string expression".into(),
820                name_expr.span,
821            ));
822        }
823    }
824
825    Expression::new(working_set, Expr::Call(call), call_span, Type::Any)
826}
827
828fn handle_special_attributes(
829    attributes: Vec<(String, Value)>,
830    working_set: &mut StateWorkingSet<'_>,
831    signature: &mut Signature,
832) -> (Vec<(String, Value)>, Vec<CustomExample>) {
833    let mut attribute_vals = vec![];
834    let mut examples = vec![];
835    let mut search_terms = vec![];
836    let mut category = String::new();
837
838    for (name, value) in attributes {
839        let val_span = value.span();
840        match name.as_str() {
841            "example" => match CustomExample::from_value(value) {
842                Ok(example) => examples.push(example),
843                Err(_) => {
844                    let e = nu_protocol::ShellError::Generic(
845                        GenericError::new(
846                            "nu::shell::invalid_example",
847                            "Value couldn't be converted to an example",
848                            val_span,
849                        )
850                        .with_help("Is `attr example` shadowed?"),
851                    );
852                    working_set.error(e.wrap(working_set, val_span));
853                }
854            },
855            "search-terms" => match <Vec<String>>::from_value(value) {
856                Ok(mut terms) => {
857                    search_terms.append(&mut terms);
858                }
859                Err(_) => {
860                    let e = nu_protocol::ShellError::Generic(
861                        GenericError::new(
862                            "nu::shell::invalid_search_terms",
863                            "Value couldn't be converted to search-terms",
864                            val_span,
865                        )
866                        .with_help("Is `attr search-terms` shadowed?"),
867                    );
868                    working_set.error(e.wrap(working_set, val_span));
869                }
870            },
871            "category" => match <String>::from_value(value) {
872                Ok(term) => {
873                    category.push_str(&term);
874                }
875                Err(_) => {
876                    let e = nu_protocol::ShellError::Generic(
877                        GenericError::new(
878                            "nu::shell::invalid_category",
879                            "Value couldn't be converted to category",
880                            val_span,
881                        )
882                        .with_help("Is `attr category` shadowed?"),
883                    );
884                    working_set.error(e.wrap(working_set, val_span));
885                }
886            },
887            "complete" => match <Spanned<String>>::from_value(value) {
888                Ok(Spanned { item, span }) => {
889                    if let Some(decl) = working_set.find_decl(item.as_bytes()) {
890                        signature.complete = Some(CommandWideCompleter::Command(decl));
891                    } else {
892                        working_set.error(ParseError::UnknownCommand(span));
893                    }
894                }
895                Err(_) => {
896                    let e = nu_protocol::ShellError::Generic(
897                        GenericError::new(
898                            "nu::shell::invalid_completer",
899                            "Value couldn't be converted to a completer",
900                            val_span,
901                        )
902                        .with_help("Is `attr complete` shadowed?"),
903                    );
904                    working_set.error(e.wrap(working_set, val_span));
905                }
906            },
907            "complete external" => match value {
908                nu_protocol::Value::Nothing { .. } => {
909                    signature.complete = Some(CommandWideCompleter::External);
910                }
911                _ => {
912                    let e = nu_protocol::ShellError::Generic(
913                        GenericError::new(
914                            "nu::shell::invalid_completer",
915                            "This attribute shouldn't return anything",
916                            val_span,
917                        )
918                        .with_help("Is `attr complete` shadowed?"),
919                    );
920                    working_set.error(e.wrap(working_set, val_span));
921                }
922            },
923            _ => {
924                attribute_vals.push((name, value));
925            }
926        }
927    }
928
929    signature.search_terms = search_terms;
930    signature.category = category_from_string(&category);
931
932    (attribute_vals, examples)
933}
934
935fn detect_params_in_name(
936    working_set: &StateWorkingSet,
937    name_span: Span,
938    decl_id: DeclId,
939) -> Option<ParseError> {
940    let name = working_set.get_span_contents(name_span);
941    for (offset, char) in name.iter().enumerate() {
942        if *char == b'[' || *char == b'(' {
943            return Some(ParseError::LabeledErrorWithHelp {
944                error: "no space between name and parameters".into(),
945                label: "expected space".into(),
946                help: format!(
947                    "consider adding a space between the `{}` command's name and its parameters",
948                    working_set.get_decl(decl_id).name()
949                ),
950                span: Span::new(offset + name_span.start - 1, offset + name_span.start - 1),
951            });
952        }
953    }
954
955    None
956}
957
958pub(crate) fn has_flag_const(
959    working_set: &mut StateWorkingSet,
960    call: &Call,
961    name: &str,
962) -> Result<bool, ()> {
963    call.has_flag_const(working_set, name).map_err(|err| {
964        working_set.error(err.wrap(working_set, call.span()));
965    })
966}