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