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}