1use crate::{
2 lite_parser::LiteCommand,
3 parse_helpers::{PERCENT_FORCED_BUILTIN_PARSER_INFO, extract_spread_list, garbage},
4 parse_source::find_dirs_var,
5 type_check::type_compatible,
6};
7use log::trace;
8use nu_engine::DIR_VAR_PARSER_INFO;
9use nu_protocol::{
10 DeclId, Flag, IntoSpanned, ParseError, PositionalArg, ShellError, Signature, Span, Spanned,
11 SyntaxShape, Type, TypeSet,
12 ast::*,
13 did_you_mean,
14 engine::{CommandType, StateWorkingSet},
15};
16use std::str;
17
18#[derive(Debug, PartialEq, Eq)]
20pub(crate) enum CallKind {
21 Help,
22 Valid,
23 Invalid,
24}
25
26pub(crate) fn check_call(
27 working_set: &mut StateWorkingSet,
28 command: Span,
29 sig: &Signature,
30 call: &Call,
31) -> CallKind {
32 if call.named_iter().any(|(n, _, _)| n.item == "help") {
34 return CallKind::Help;
35 }
36
37 if call.positional_iter().count() < sig.required_positional.len() {
38 let end_offset = call
39 .positional_iter()
40 .last()
41 .map(|last| last.span.end)
42 .unwrap_or(command.end);
43 for argument in &sig.required_positional {
47 let found = call.positional_iter().fold(false, |ac, expr| {
48 if argument.shape.to_type() == expr.ty || argument.shape == SyntaxShape::Any {
49 true
50 } else {
51 ac
52 }
53 });
54 if !found {
55 working_set.error(ParseError::MissingPositional(
56 argument.name.clone(),
57 Span::new(end_offset, end_offset),
58 sig.call_signature(),
59 ));
60 return CallKind::Invalid;
61 }
62 }
63
64 let missing = &sig.required_positional[call.positional_iter().count()];
65 working_set.error(ParseError::MissingPositional(
66 missing.name.clone(),
67 Span::new(end_offset, end_offset),
68 sig.call_signature(),
69 ));
70 return CallKind::Invalid;
71 } else {
72 for req_flag in sig.named.iter().filter(|x| x.required) {
73 if call.named_iter().all(|(n, _, _)| n.item != req_flag.long) {
74 working_set.error(ParseError::MissingRequiredFlag(
75 req_flag.long.clone(),
76 command,
77 ));
78 return CallKind::Invalid;
79 }
80 }
81 }
82 CallKind::Valid
83}
84
85fn parse_unknown_arg(
86 working_set: &mut StateWorkingSet,
87 span: Span,
88 signature: &Signature,
89) -> Expression {
90 let shape = signature
91 .rest_positional
92 .as_ref()
93 .map(|arg| arg.shape.clone())
94 .unwrap_or(SyntaxShape::Any);
95
96 crate::parser::parse_value(working_set, span, &shape, None)
97}
98
99fn parse_external_string(working_set: &mut StateWorkingSet, span: Span) -> Expression {
100 let contents = working_set.get_span_contents(span);
101
102 if contents.starts_with(b"r#") {
103 crate::parser::parse_raw_string(working_set, span)
104 } else if contents
105 .iter()
106 .any(|b| matches!(b, b'"' | b'\'' | b'(' | b')' | b'`'))
107 {
108 enum State {
109 Bare {
110 from: usize,
111 },
112 BackTickQuote {
113 from: usize,
114 },
115 Quote {
116 from: usize,
117 quote_char: u8,
118 escaped: bool,
119 },
120 Parenthesized {
121 from: usize,
122 depth: usize,
123 },
124 }
125 let make_span = |from: usize, index: usize| Span {
131 start: span.start + from,
132 end: span.start + index,
133 };
134 let mut spans = vec![];
135 let mut state = State::Bare { from: 0 };
136 let mut index = 0;
137 while index < contents.len() {
138 let ch = contents[index];
139 match &mut state {
140 State::Bare { from } => match ch {
141 b'"' | b'\'' => {
142 if index != *from {
144 spans.push(make_span(*from, index));
145 }
146 state = State::Quote {
148 from: index,
149 quote_char: ch,
150 escaped: false,
151 };
152 }
153 b'$' => {
154 if let Some("e_char @ (b'"' | b'\'')) = contents.get(index + 1) {
155 if index != *from {
157 spans.push(make_span(*from, index));
158 }
159 state = State::Quote {
160 from: index,
161 quote_char,
162 escaped: false,
163 };
164 index += 2;
166 continue;
167 }
168 }
169 b'`' => {
170 if index != *from {
171 spans.push(make_span(*from, index))
172 }
173 state = State::BackTickQuote { from: index }
174 }
175 b'(' => {
176 if index != *from {
177 spans.push(make_span(*from, index))
178 }
179 state = State::Parenthesized {
180 from: index,
181 depth: 1,
182 }
183 }
184 _ => (),
186 },
187 State::Quote {
188 from,
189 quote_char,
190 escaped,
191 } => match ch {
192 ch if ch == *quote_char && !*escaped => {
193 spans.push(make_span(*from, index + 1));
195 state = State::Bare { from: index + 1 };
197 }
198 b'\\' if !*escaped && *quote_char == b'"' => {
199 *escaped = true;
201 }
202 _ => {
203 *escaped = false;
204 }
205 },
206 State::BackTickQuote { from } => {
207 if ch == b'`' {
208 spans.push(make_span(*from, index + 1));
209 state = State::Bare { from: index + 1 };
210 }
211 }
212 State::Parenthesized { from, depth } => {
213 if ch == b')' {
214 if *depth == 1 {
215 spans.push(make_span(*from, index + 1));
216 state = State::Bare { from: index + 1 };
217 } else {
218 *depth -= 1;
219 }
220 } else if ch == b'(' {
221 *depth += 1;
222 }
223 }
224 }
225 index += 1;
226 }
227
228 match state {
230 State::Bare { from }
231 | State::Quote { from, .. }
232 | State::Parenthesized { from, .. }
233 | State::BackTickQuote { from, .. } => {
234 if from < contents.len() {
235 spans.push(make_span(from, contents.len()));
236 }
237 }
238 }
239
240 if log::log_enabled!(log::Level::Trace) {
242 let contents = spans
243 .iter()
244 .map(|span| String::from_utf8_lossy(working_set.get_span_contents(*span)))
245 .collect::<Vec<_>>();
246
247 trace!("parsing: external string, parts: {contents:?}")
248 }
249
250 let quoted =
252 (contents.len() >= 3 && contents.starts_with(b"$\"") && contents.ends_with(b"\""))
253 || is_quoted(contents);
254
255 let exprs: Vec<Expression> = spans
257 .into_iter()
258 .map(|span| crate::parser::parse_string(working_set, span))
259 .collect();
260
261 if exprs
262 .iter()
263 .all(|expr| matches!(expr.expr, Expr::String(..)))
264 {
265 let string = exprs
267 .into_iter()
268 .map(|expr| {
269 let Expr::String(contents) = expr.expr else {
270 unreachable!("already checked that this was a String")
271 };
272 contents
273 })
274 .collect::<String>();
275 if quoted {
276 Expression::new(working_set, Expr::String(string), span, Type::String)
277 } else {
278 Expression::new(
279 working_set,
280 Expr::GlobPattern(string, false),
281 span,
282 Type::Glob,
283 )
284 }
285 } else {
286 let exprs = exprs
288 .into_iter()
289 .flat_map(|expr| match expr.expr {
290 Expr::StringInterpolation(subexprs) => subexprs,
291 _ => vec![expr],
292 })
293 .collect();
294 if quoted {
297 Expression::new(
298 working_set,
299 Expr::StringInterpolation(exprs),
300 span,
301 Type::String,
302 )
303 } else {
304 Expression::new(
305 working_set,
306 Expr::GlobInterpolation(exprs, false),
307 span,
308 Type::Glob,
309 )
310 }
311 }
312 } else {
313 crate::parser::parse_glob_pattern(working_set, span)
314 }
315}
316
317fn is_quoted(bytes: &[u8]) -> bool {
318 matches!(bytes, [b'\'', .., b'\''] | [b'"', .., b'"'])
319}
320
321fn parse_external_arg(working_set: &mut StateWorkingSet, span: Span) -> ExternalArgument {
322 let contents = working_set.get_span_contents(span);
323
324 if let Some(Spanned { item: _, span }) = extract_spread_list(contents.into_spanned(span)) {
325 ExternalArgument::Spread(crate::parser::parse_value(
326 working_set,
327 span,
328 &SyntaxShape::List(Box::new(SyntaxShape::Any)),
329 None,
330 ))
331 } else {
332 ExternalArgument::Regular(parse_regular_external_arg(working_set, span))
333 }
334}
335
336pub(crate) fn parse_regular_external_arg(
337 working_set: &mut StateWorkingSet,
338 span: Span,
339) -> Expression {
340 match working_set.get_span_contents(span) {
341 [b'$', ..] => crate::parser::parse_dollar_expr(working_set, span, &SyntaxShape::Any, None),
342 [b'(', ..] => crate::parser::parse_paren_expr(working_set, span, &SyntaxShape::Any),
343 [b'[', ..] => crate::parser::parse_list_expression(working_set, span, &SyntaxShape::Any),
344 _ => parse_external_string(working_set, span),
345 }
346}
347
348pub fn parse_external_call(
349 working_set: &mut StateWorkingSet,
350 spans: &[Span],
351 call_span: Span,
352) -> Expression {
353 trace!("parse external");
354
355 let head_span = spans[0];
356
357 let head_contents = working_set.get_span_contents(head_span);
358
359 let head = if let [b'$' | b'(', ..] = head_contents {
360 let arg = crate::parser::parse_expression(working_set, &[head_span], None);
362 Box::new(arg)
363 } else {
364 Box::new(parse_external_string(working_set, head_span))
365 };
366
367 let args = spans[1..]
368 .iter()
369 .map(|&span| parse_external_arg(working_set, span))
370 .collect();
371
372 Expression::new(
373 working_set,
374 Expr::ExternalCall(head, args),
375 call_span,
376 Type::Any,
377 )
378}
379
380fn ensure_flag_arg_type(
381 working_set: &mut StateWorkingSet,
382 arg_name: String,
383 arg: Expression,
384 arg_shape: &SyntaxShape,
385 long_name_span: Span,
386) -> (Spanned<String>, Expression) {
387 if !type_compatible(&arg_shape.to_type(), &arg.ty) {
388 working_set.error(ParseError::TypeMismatch(
389 arg_shape.to_type(),
390 arg.ty,
391 arg.span,
392 ));
393 (
394 Spanned {
395 item: arg_name,
396 span: long_name_span,
397 },
398 Expression::garbage(working_set, arg.span),
399 )
400 } else {
401 (
402 Spanned {
403 item: arg_name,
404 span: long_name_span,
405 },
406 arg,
407 )
408 }
409}
410
411enum LongFlagParseResult {
416 FoundFlag(Spanned<String>, Option<Expression>),
418 NoFlag,
420 EndOfOptions,
422}
423
424fn parse_long_flag(
425 working_set: &mut StateWorkingSet,
426 spans: &[Span],
427 spans_idx: &mut usize,
428 sig: &Signature,
429) -> LongFlagParseResult {
430 let arg_span = spans[*spans_idx];
431 let arg_contents = working_set.get_span_contents(arg_span);
432
433 if arg_contents.starts_with(b"--") {
434 if arg_contents == b"--" {
436 return LongFlagParseResult::EndOfOptions;
437 }
438
439 let split: Vec<_> = arg_contents.split(|x| *x == b'=').collect();
441 let long_name = String::from_utf8(split[0].into());
442 if let Ok(long_name) = long_name {
443 let long_name = long_name[2..].to_string();
444 if let Some(flag) = sig.get_long_flag(&long_name) {
445 if let Some(arg_shape) = &flag.arg {
446 if split.len() > 1 {
447 let long_name_len = long_name.len();
449 let mut span = arg_span;
450 span.start += long_name_len + 3; let arg = crate::parser::parse_value(working_set, span, arg_shape, None);
453 let (arg_name, val_expression) = ensure_flag_arg_type(
454 working_set,
455 long_name,
456 arg,
457 arg_shape,
458 Span::new(arg_span.start, arg_span.start + long_name_len + 2),
459 );
460 LongFlagParseResult::FoundFlag(arg_name, Some(val_expression))
461 } else if let Some(arg) = spans.get(*spans_idx + 1) {
462 let arg = crate::parser::parse_value(working_set, *arg, arg_shape, None);
463
464 *spans_idx += 1;
465 let (arg_name, val_expression) =
466 ensure_flag_arg_type(working_set, long_name, arg, arg_shape, arg_span);
467 LongFlagParseResult::FoundFlag(arg_name, Some(val_expression))
468 } else {
469 working_set.error(ParseError::MissingFlagParam(
470 arg_shape.to_string(),
471 arg_span,
472 ));
473 LongFlagParseResult::FoundFlag(
476 Spanned {
477 item: long_name,
478 span: arg_span,
479 },
480 None,
481 )
482 }
483 } else {
484 if split.len() > 1 {
487 let long_name_len = long_name.len();
489 let mut span = arg_span;
490 span.start += long_name_len + 3; let arg = crate::parser::parse_value(
493 working_set,
494 span,
495 &SyntaxShape::Boolean,
496 None,
497 );
498
499 let (arg_name, val_expression) = ensure_flag_arg_type(
500 working_set,
501 long_name,
502 arg,
503 &SyntaxShape::Boolean,
504 Span::new(arg_span.start, arg_span.start + long_name_len + 2),
505 );
506 LongFlagParseResult::FoundFlag(arg_name, Some(val_expression))
507 } else {
508 LongFlagParseResult::FoundFlag(
509 Spanned {
510 item: long_name,
511 span: arg_span,
512 },
513 None,
514 )
515 }
516 }
517 } else {
518 let suggestion = did_you_mean(sig.get_names(), &long_name)
519 .map(|name| format!("Did you mean: `--{name}`?"))
520 .unwrap_or("Use `--help` to see available flags".to_owned());
521 working_set.error(ParseError::UnknownFlag(
522 sig.name.clone(),
523 long_name.clone(),
524 arg_span,
525 suggestion,
526 ));
527 LongFlagParseResult::FoundFlag(
528 Spanned {
529 item: long_name.clone(),
530 span: arg_span,
531 },
532 None,
533 )
534 }
535 } else {
536 working_set.error(ParseError::NonUtf8(arg_span));
537 LongFlagParseResult::FoundFlag(
538 Spanned {
539 item: "--".into(),
540 span: arg_span,
541 },
542 None,
543 )
544 }
545 } else {
546 LongFlagParseResult::NoFlag
547 }
548}
549
550fn parse_short_flags(
551 working_set: &mut StateWorkingSet,
552 spans: &[Span],
553 spans_idx: &mut usize,
554 positional_idx: usize,
555 sig: &Signature,
556) -> Option<Vec<Flag>> {
557 let arg_span = spans[*spans_idx];
558
559 let arg_contents = working_set.get_span_contents(arg_span);
560
561 if let Ok(arg_contents_uft8_ref) = str::from_utf8(arg_contents) {
562 if arg_contents_uft8_ref.starts_with('-') && arg_contents_uft8_ref.len() > 1 {
563 let short_flags = &arg_contents_uft8_ref[1..];
564 let num_chars = short_flags.chars().count();
565 let mut found_short_flags = vec![];
566 let mut unmatched_short_flags = vec![];
567 for (offset, short_flag) in short_flags.char_indices() {
568 let short_flag_span = Span::new(
569 arg_span.start + 1 + offset,
570 arg_span.start + 1 + offset + short_flag.len_utf8(),
571 );
572 if let Some(flag) = sig.get_short_flag(short_flag) {
573 if flag.arg.is_some() && offset < num_chars - 1 {
575 working_set
576 .error(ParseError::OnlyLastFlagInBatchCanTakeArg(short_flag_span));
577 break;
578 }
579 found_short_flags.push(flag);
580 } else {
581 unmatched_short_flags.push(short_flag_span);
582 }
583 }
584
585 if found_short_flags.is_empty()
586 && matches!(
588 sig.get_positional(positional_idx),
589 Some(PositionalArg {
590 shape: SyntaxShape::Int | SyntaxShape::Number | SyntaxShape::Float,
591 ..
592 })
593 )
594 && String::from_utf8_lossy(working_set.get_span_contents(arg_span))
595 .parse::<f64>()
596 .is_ok()
597 {
598 return None;
599 } else if let Some(first) = unmatched_short_flags.first() {
600 let contents = working_set.get_span_contents(*first);
601 working_set.error(ParseError::UnknownFlag(
602 sig.name.clone(),
603 format!("-{}", String::from_utf8_lossy(contents)),
604 *first,
605 "Use `--help` to see available flags".to_owned(),
606 ));
607 }
608
609 Some(found_short_flags)
610 } else {
611 None
612 }
613 } else {
614 working_set.error(ParseError::NonUtf8(arg_span));
615 None
616 }
617}
618
619fn first_kw_idx(
620 working_set: &StateWorkingSet,
621 signature: &Signature,
622 spans: &[Span],
623 spans_idx: usize,
624 positional_idx: usize,
625) -> (Option<usize>, usize) {
626 for idx in (positional_idx + 1)..signature.num_positionals() {
627 if let Some(PositionalArg {
628 shape: SyntaxShape::Keyword(kw, ..),
629 ..
630 }) = signature.get_positional(idx)
631 {
632 for (span_idx, &span) in spans.iter().enumerate().skip(spans_idx) {
633 let contents = working_set.get_span_contents(span);
634
635 if contents == kw {
636 return (Some(idx), span_idx);
637 }
638 }
639 }
640 }
641 (None, spans.len())
642}
643
644fn calculate_end_span(
645 working_set: &StateWorkingSet,
646 signature: &Signature,
647 spans: &[Span],
648 spans_idx: usize,
649 positional_idx: usize,
650) -> usize {
651 if signature.rest_positional.is_some() {
652 spans.len()
653 } else {
654 let (kw_pos, kw_idx) =
655 first_kw_idx(working_set, signature, spans, spans_idx, positional_idx);
656
657 if let Some(kw_pos) = kw_pos {
658 let positionals_between = kw_pos - positional_idx - 1;
663 if positionals_between >= (kw_idx - spans_idx) {
664 kw_idx
665 } else {
666 kw_idx - positionals_between
667 }
668 } else {
669 let remaining_spans = spans.len() - (spans_idx + 1);
672 let remaining_positional = signature
674 .required_positional
675 .len()
676 .saturating_sub(positional_idx + 1);
677 let extra_spans = remaining_spans.saturating_sub(remaining_positional);
679 spans_idx + 1 + extra_spans
680 }
681 }
682}
683
684pub(crate) fn parse_oneof(
685 working_set: &mut StateWorkingSet,
686 spans: &[Span],
687 spans_idx: &mut usize,
688 possible_shapes: &Vec<SyntaxShape>,
689 multispan: bool,
690 input_type: Option<&Type>,
691) -> Expression {
692 let starting_spans_idx = *spans_idx;
693 let mut best_guess = None;
694 let mut best_guess_errors = Vec::new();
695 let mut max_first_error_offset = 0;
696 let mut propagate_error = false;
697 for shape in possible_shapes {
698 let starting_error_count = working_set.parse_errors.len();
699 *spans_idx = starting_spans_idx;
700 let value = match multispan {
701 true => parse_multispan_value(working_set, spans, spans_idx, shape, input_type),
702 false => crate::parser::parse_value(working_set, spans[*spans_idx], shape, input_type),
703 };
704
705 let new_errors = &working_set.parse_errors[starting_error_count..];
706 let Some(first_error_offset) = new_errors.iter().map(|e| e.span().start).min() else {
708 return value;
709 };
710
711 if first_error_offset > max_first_error_offset {
712 propagate_error = match working_set.parse_errors.last() {
715 Some(ParseError::Expected(_, error_span))
716 | Some(ParseError::ExpectedWithStringMsg(_, error_span)) => {
717 matches!(
718 shape,
719 SyntaxShape::Block | SyntaxShape::Closure(_) | SyntaxShape::Expression
720 ) && *error_span != spans[*spans_idx]
721 }
722 _ => true,
723 };
724 max_first_error_offset = first_error_offset;
725 best_guess = Some(value);
726 best_guess_errors.clear();
727 best_guess_errors.extend_from_slice(new_errors);
728 }
729 working_set.parse_errors.truncate(starting_error_count);
730 }
731
732 if max_first_error_offset > spans[starting_spans_idx].start || propagate_error {
735 working_set.parse_errors.extend(best_guess_errors);
736 best_guess.expect("best_guess should not be None here!")
737 } else {
738 working_set.error(ParseError::ExpectedWithStringMsg(
739 format!("one of a list of accepted shapes: {possible_shapes:?}"),
740 spans[starting_spans_idx],
741 ));
742 Expression::garbage(working_set, spans[starting_spans_idx])
743 }
744}
745
746pub fn parse_multispan_value(
747 working_set: &mut StateWorkingSet,
748 spans: &[Span],
749 spans_idx: &mut usize,
750 shape: &SyntaxShape,
751 input_type: Option<&Type>,
752) -> Expression {
753 trace!("parse multispan value");
754 match shape {
755 SyntaxShape::VarWithOptType => {
756 trace!("parsing: var with opt type");
757
758 crate::parser::parse_var_with_opt_type(working_set, spans, spans_idx, false, input_type)
759 .0
760 }
761 SyntaxShape::RowCondition => {
762 trace!("parsing: row condition");
763 let arg = crate::parser::parse_row_condition(working_set, &spans[*spans_idx..]);
764 *spans_idx = spans.len() - 1;
765
766 arg
767 }
768 SyntaxShape::MathExpression => {
769 trace!("parsing: math expression");
770
771 let arg = crate::parser::parse_math_expression(
772 working_set,
773 &spans[*spans_idx..],
774 None,
775 input_type,
776 );
777 *spans_idx = spans.len() - 1;
778
779 arg
780 }
781 SyntaxShape::OneOf(possible_shapes) => parse_oneof(
782 working_set,
783 spans,
784 spans_idx,
785 possible_shapes,
786 true,
787 input_type,
788 ),
789
790 SyntaxShape::Expression => {
791 trace!("parsing: expression");
792
793 let arg =
796 crate::parser::parse_expression(working_set, &spans[*spans_idx..], input_type);
797 *spans_idx = spans.len().saturating_sub(1);
798
799 arg
800 }
801 SyntaxShape::Signature => {
802 trace!("parsing: signature");
803
804 let sig = crate::parser::parse_full_signature(working_set, &spans[*spans_idx..], false);
805 *spans_idx = spans.len().saturating_sub(1);
806
807 sig
808 }
809 SyntaxShape::ExternalSignature => {
810 trace!("parsing: external signature");
811
812 let sig = crate::parser::parse_full_signature(working_set, &spans[*spans_idx..], true);
813 *spans_idx = spans.len().saturating_sub(1);
814
815 sig
816 }
817 SyntaxShape::Keyword(keyword, arg) => {
818 trace!(
819 "parsing: keyword({}) {:?}",
820 String::from_utf8_lossy(keyword),
821 arg
822 );
823 let arg_span = spans[*spans_idx];
824
825 let arg_contents = working_set.get_span_contents(arg_span);
826
827 if arg_contents != keyword {
828 working_set.error(ParseError::ExpectedKeyword(
833 String::from_utf8_lossy(keyword).into(),
834 arg_span,
835 ))
836 }
837
838 *spans_idx += 1;
839 if *spans_idx >= spans.len() {
840 working_set.error(ParseError::KeywordMissingArgument(
841 arg.to_string(),
842 String::from_utf8_lossy(keyword).into(),
843 Span::new(spans[*spans_idx - 1].end, spans[*spans_idx - 1].end),
844 ));
845 let keyword = Keyword {
846 keyword: keyword.as_slice().into(),
847 span: spans[*spans_idx - 1],
848 expr: Expression::garbage(working_set, arg_span),
849 };
850 return Expression::new(
851 working_set,
852 Expr::Keyword(Box::new(keyword)),
853 arg_span,
854 Type::Any,
855 );
856 }
857
858 let keyword = Keyword {
859 keyword: keyword.as_slice().into(),
860 span: spans[*spans_idx - 1],
861 expr: parse_multispan_value(working_set, spans, spans_idx, arg, input_type),
862 };
863
864 Expression::new(
865 working_set,
866 Expr::Keyword(Box::new(keyword.clone())),
867 keyword.span.merge(keyword.expr.span),
868 keyword.expr.ty,
869 )
870 }
871 _ => {
872 let arg_span = spans[*spans_idx];
874
875 crate::parser::parse_value(working_set, arg_span, shape, input_type)
876 }
877 }
878}
879
880pub struct ParsedInternalCall {
881 pub call: Box<Call>,
882 pub output: Type,
883 pub call_kind: CallKind,
884}
885
886#[derive(Default)]
891pub enum ArgumentParsingLevel {
892 #[default]
893 Full,
894 FirstK { k: usize },
896}
897
898pub fn parse_internal_call(
899 working_set: &mut StateWorkingSet,
900 command_span: Span,
901 spans: &[Span],
902 decl_id: DeclId,
903 arg_parsing_level: ArgumentParsingLevel,
904 input_type: Option<&Type>,
905) -> ParsedInternalCall {
906 trace!("parsing: internal call (decl id: {})", decl_id.get());
907
908 let mut call = Call::new(command_span);
909 call.decl_id = decl_id;
910 call.head = command_span;
911 let _ = working_set.add_span(call.head);
912
913 let decl = working_set.get_decl(decl_id);
914 let signature = working_set.get_signature(decl);
915
916 enum SpecialCmd {
917 Let,
918 Def,
919 Match,
920 If,
921 }
922
923 impl SpecialCmd {
924 fn from_str(s: &str) -> Option<Self> {
925 Some(match s {
926 "let" => Self::Let,
927 "def" => Self::Def,
928 "match" => Self::Match,
929 "if" => Self::If,
930 _ => return None,
931 })
932 }
933 }
934
935 let special_cmd = decl
936 .is_keyword()
937 .then(|| decl.name())
938 .and_then(SpecialCmd::from_str);
939
940 let output = signature
948 .get_output_type(
949 input_type
950 .map(|ty| ty.clone().union(Type::Nothing))
951 .as_ref(),
952 )
953 .unwrap_or(Type::Error);
954
955 let mut output_override = None;
960
961 let deprecation = decl.deprecation_info();
962
963 let lib_dirs_var_id = match decl.name() {
965 "use" | "overlay use" | "source-env" if decl.is_keyword() => {
966 find_dirs_var(working_set, crate::parse_source::LIB_DIRS_VAR)
967 }
968 "nu-check" if decl.is_builtin() => {
969 find_dirs_var(working_set, crate::parse_source::LIB_DIRS_VAR)
970 }
971 _ => None,
972 };
973
974 let mut positional_idx = 0;
976
977 let mut spans_idx = 0;
980
981 if let Some(alias) = decl.as_alias() {
982 if let Expression {
983 expr: Expr::Call(wrapped_call),
984 ..
985 } = &alias.wrapped_call
986 {
987 call = *wrapped_call.clone();
989 call.head = command_span;
990 positional_idx = call.positional_iter().count();
992 } else {
993 working_set.error(ParseError::UnknownState(
994 "Alias does not point to internal call.".to_string(),
995 command_span,
996 ));
997 return ParsedInternalCall {
998 call: Box::new(call),
999 output: Type::Any,
1000 call_kind: CallKind::Invalid,
1001 };
1002 }
1003 }
1004
1005 if let Some(var_id) = lib_dirs_var_id {
1006 call.set_parser_info(
1007 DIR_VAR_PARSER_INFO.to_owned(),
1008 Expression::new(working_set, Expr::Var(var_id), call.head, Type::Any),
1009 );
1010 }
1011
1012 if signature.creates_scope {
1013 working_set.enter_scope();
1014 }
1015
1016 let mut end_of_options = false;
1017
1018 while spans_idx < spans.len() {
1019 let arg_span = spans[spans_idx];
1020
1021 let starting_error_count = working_set.parse_errors.len();
1022
1023 if !end_of_options {
1025 let flag_parse_result = parse_long_flag(working_set, spans, &mut spans_idx, &signature);
1027
1028 match flag_parse_result {
1029 LongFlagParseResult::EndOfOptions => {
1030 end_of_options = true;
1032
1033 if signature.allows_unknown_args {
1034 let arg = parse_unknown_arg(working_set, arg_span, &signature);
1037 call.add_unknown(arg);
1038 }
1039
1040 spans_idx += 1;
1041 continue;
1042 }
1043 LongFlagParseResult::FoundFlag(long_name, arg) => {
1044 if working_set.parse_errors[starting_error_count..]
1046 .iter()
1047 .any(|x| matches!(x, ParseError::UnknownFlag(_, _, _, _)))
1048 && signature.allows_unknown_args
1049 {
1050 working_set.parse_errors.truncate(starting_error_count);
1051 let arg = parse_unknown_arg(working_set, arg_span, &signature);
1052
1053 call.add_unknown(arg);
1054 } else {
1055 call.add_named((long_name, None, arg));
1056 }
1057
1058 spans_idx += 1;
1059 continue;
1060 }
1061 LongFlagParseResult::NoFlag => {
1062 }
1064 }
1065 }
1066
1067 if !end_of_options {
1069 let starting_error_count = working_set.parse_errors.len();
1070
1071 let short_flags = parse_short_flags(
1073 working_set,
1074 spans,
1075 &mut spans_idx,
1076 positional_idx,
1077 &signature,
1078 );
1079
1080 if let Some(mut short_flags) = short_flags {
1081 if short_flags.is_empty() {
1082 short_flags.push(Flag {
1084 long: "".to_string(),
1085 short: Some('a'),
1086 arg: None,
1087 required: false,
1088 desc: "".to_string(),
1089 var_id: None,
1090 default_value: None,
1091 completion: None,
1092 })
1093 }
1094
1095 if working_set.parse_errors[starting_error_count..]
1096 .iter()
1097 .any(|x| matches!(x, ParseError::UnknownFlag(_, _, _, _)))
1098 && signature.allows_unknown_args
1099 {
1100 working_set.parse_errors.truncate(starting_error_count);
1101 let arg = parse_unknown_arg(working_set, arg_span, &signature);
1102
1103 call.add_unknown(arg);
1104 } else {
1105 for flag in short_flags {
1106 let _ = working_set.add_span(spans[spans_idx]);
1107
1108 if let Some(arg_shape) = flag.arg {
1109 if let Some(arg) = spans.get(spans_idx + 1) {
1110 let arg =
1111 crate::parser::parse_value(working_set, *arg, &arg_shape, None);
1112 let (arg_name, val_expression) = ensure_flag_arg_type(
1113 working_set,
1114 flag.long.clone(),
1115 arg.clone(),
1116 &arg_shape,
1117 spans[spans_idx],
1118 );
1119
1120 if flag.long.is_empty() {
1121 if let Some(short) = flag.short {
1122 call.add_named((
1123 arg_name,
1124 Some(Spanned {
1125 item: short.to_string(),
1126 span: spans[spans_idx],
1127 }),
1128 Some(val_expression),
1129 ));
1130 }
1131 } else {
1132 call.add_named((arg_name, None, Some(val_expression)));
1133 }
1134 spans_idx += 1;
1135 } else {
1136 working_set.error(ParseError::MissingFlagParam(
1137 arg_shape.to_string(),
1138 arg_span,
1139 ));
1140 call.add_named((
1143 Spanned {
1144 item: String::new(),
1145 span: spans[spans_idx],
1146 },
1147 None,
1148 None,
1149 ));
1150 }
1151 } else if flag.long.is_empty() {
1152 if let Some(short) = flag.short {
1153 call.add_named((
1154 Spanned {
1155 item: String::new(),
1156 span: spans[spans_idx],
1157 },
1158 Some(Spanned {
1159 item: short.to_string(),
1160 span: spans[spans_idx],
1161 }),
1162 None,
1163 ));
1164 }
1165 } else {
1166 call.add_named((
1167 Spanned {
1168 item: flag.long.clone(),
1169 span: spans[spans_idx],
1170 },
1171 None,
1172 None,
1173 ));
1174 }
1175 }
1176 }
1177
1178 spans_idx += 1;
1179 continue;
1180 }
1181 } {
1184 let contents = working_set.get_span_contents(spans[spans_idx]);
1185
1186 if let Some(Spanned {
1187 span: spread_arg_span,
1188 ..
1189 }) = extract_spread_list(contents.into_spanned(spans[spans_idx]))
1190 {
1191 if signature.rest_positional.is_none() && !signature.allows_unknown_args {
1192 working_set.error(ParseError::UnexpectedSpreadArg(
1193 signature.call_signature(),
1194 arg_span,
1195 ));
1196 call.add_positional(Expression::garbage(working_set, arg_span));
1197 } else if positional_idx < signature.required_positional.len() {
1198 working_set.error(ParseError::MissingPositional(
1199 signature.required_positional[positional_idx].name.clone(),
1200 Span::new(spans[spans_idx].start, spans[spans_idx].start),
1201 signature.call_signature(),
1202 ));
1203 call.add_positional(Expression::garbage(working_set, arg_span));
1204 } else {
1205 let rest_shape = match &signature.rest_positional {
1206 Some(arg) if matches!(arg.shape, SyntaxShape::ExternalArgument) => {
1207 SyntaxShape::Any
1209 }
1210 Some(arg) => arg.shape.clone(),
1211 None => SyntaxShape::Any,
1212 };
1213 let args = crate::parser::parse_value(
1215 working_set,
1216 spread_arg_span,
1217 &SyntaxShape::List(Box::new(rest_shape)),
1218 None,
1219 );
1220
1221 call.add_spread(args);
1222 positional_idx =
1224 signature.required_positional.len() + signature.optional_positional.len();
1225 }
1226
1227 spans_idx += 1;
1228 continue;
1229 }
1230 }
1231
1232 if let Some(positional) = signature.get_positional(positional_idx) {
1234 let end = calculate_end_span(working_set, &signature, spans, spans_idx, positional_idx);
1235
1236 if end == spans_idx {
1238 let prev_span = if spans_idx == 0 {
1239 command_span
1240 } else {
1241 spans[spans_idx - 1]
1242 };
1243 let whitespace_span = Span::new(prev_span.end, spans[spans_idx].start);
1244 working_set.error(ParseError::MissingPositional(
1245 positional.name.clone(),
1246 whitespace_span,
1247 signature.call_signature(),
1248 ));
1249 call.add_positional(Expression::garbage(working_set, whitespace_span));
1250 positional_idx += 1;
1251 continue;
1252 }
1253 debug_assert!(end <= spans.len());
1254
1255 if spans[..end].is_empty() || spans_idx == end {
1256 working_set.error(ParseError::MissingPositional(
1257 positional.name.clone(),
1258 Span::new(spans[spans_idx].end, spans[spans_idx].end),
1259 signature.call_signature(),
1260 ));
1261 positional_idx += 1;
1262 continue;
1263 }
1264
1265 let compile_error_count = working_set.compile_errors.len();
1266
1267 let arg = match arg_parsing_level {
1270 ArgumentParsingLevel::FirstK { k } if k <= positional_idx => {
1271 Expression::garbage(working_set, spans[spans_idx])
1272 }
1273 _ => {
1274 let input_type: Option<Type> = match special_cmd {
1275 Some(SpecialCmd::Let)
1278 if let SyntaxShape::VarWithOptType = &positional.shape =>
1279 {
1280 output_override = input_type.cloned();
1281 input_type.cloned()
1282 }
1283 Some(SpecialCmd::Def) if &positional.name == "block" => {
1286 match call.arguments.last() {
1288 Some(Argument::Positional(Expression {
1289 expr: Expr::Signature(sig),
1290 ..
1291 })) => Some(sig.get_input_type()),
1292 _ => None,
1293 }
1294 }
1295 Some(SpecialCmd::If) if positional_idx >= 1 => input_type.cloned(),
1296 Some(SpecialCmd::Match) if &positional.name == "match_block" => {
1297 input_type.cloned()
1298 }
1299 _ => None,
1300 };
1301
1302 let expr = match special_cmd {
1313 Some(SpecialCmd::Def) if &positional.name == "block" => {
1314 let starting_error_count = working_set.parse_errors.len();
1315
1316 let out = crate::parse_expressions::parse_closure_expression(
1317 working_set,
1318 &positional.shape,
1319 spans[spans_idx],
1320 input_type.as_ref(),
1321 );
1322
1323 if let Expr::Closure(_) = out.expr {
1324 out
1325 } else {
1326 working_set.parse_errors.truncate(starting_error_count);
1329 parse_multispan_value(
1330 working_set,
1331 &spans[..end],
1332 &mut spans_idx,
1333 &positional.shape,
1334 input_type.as_ref(),
1335 )
1336 }
1337 }
1338 _ => parse_multispan_value(
1339 working_set,
1340 &spans[..end],
1341 &mut spans_idx,
1342 &positional.shape,
1343 input_type.as_ref(),
1344 ),
1345 };
1346
1347 match special_cmd {
1348 Some(SpecialCmd::Match) if &positional.name == "match_block" => {
1349 output_override = Some(expr.ty.clone());
1350 }
1351 Some(SpecialCmd::If)
1352 if positional_idx == 1
1353 && let Expr::Block(block_id) = &expr.expr =>
1354 {
1355 let block = working_set.get_block(*block_id);
1356 let ty = match block.pipelines.is_empty() {
1357 false => block.output_type(),
1358 true => input_type.unwrap_or(Type::Any),
1359 };
1360
1361 output_override = Some(match output_override {
1362 Some(existing_ty) => existing_ty.union(ty),
1363 None => ty,
1364 });
1365 }
1366 Some(SpecialCmd::If)
1367 if positional_idx == 2
1368 && let Expr::Keyword(kw) = &expr.expr =>
1369 {
1370 let ty = match &kw.expr.expr {
1371 Expr::Block(block_id) => {
1372 let block = working_set.get_block(*block_id);
1373 match block.pipelines.is_empty() {
1374 false => block.output_type(),
1375 true => input_type.unwrap_or(Type::Any),
1376 }
1377 }
1378 _ => kw.expr.ty.clone(),
1379 };
1380
1381 output_override = Some(match output_override {
1382 Some(existing_ty) => existing_ty.union(ty),
1383 None => ty,
1384 });
1385 }
1386 _ => {}
1387 };
1388
1389 expr
1390 }
1391 };
1392
1393 if let SyntaxShape::OneOf(ref shapes) = positional.shape {
1401 for one_shape in shapes {
1402 if let SyntaxShape::Keyword(keyword, ..) = one_shape
1403 && keyword == b"catch"
1404 && let [nu_protocol::CompileError::NotInALoop { .. }] =
1405 &working_set.compile_errors[compile_error_count..]
1406 {
1407 working_set.compile_errors.truncate(compile_error_count);
1408 }
1409 }
1410 }
1411
1412 let arg = if !type_compatible(&positional.shape.to_type(), &arg.ty) {
1413 working_set.error(ParseError::TypeMismatch(
1414 positional.shape.to_type(),
1415 arg.ty,
1416 arg.span,
1417 ));
1418 Expression::garbage(working_set, arg.span)
1419 } else {
1420 arg
1421 };
1422
1423 call.add_positional(arg);
1424 positional_idx += 1;
1425 } else if signature.allows_unknown_args {
1426 let arg = parse_unknown_arg(working_set, arg_span, &signature);
1427
1428 call.add_unknown(arg);
1429 } else {
1430 call.add_positional(Expression::garbage(working_set, arg_span));
1431 working_set.error(ParseError::ExtraPositional(
1432 signature.call_signature(),
1433 arg_span,
1434 ))
1435 }
1436
1437 spans_idx += 1;
1438 }
1439
1440 let call_kind = check_call(working_set, command_span, &signature, &call);
1444
1445 deprecation
1446 .into_iter()
1447 .filter_map(|entry| entry.parse_warning(&signature.name, &call))
1448 .for_each(|warning| {
1449 working_set.warning(warning);
1453 });
1454
1455 if signature.creates_scope {
1456 working_set.exit_scope();
1457 }
1458
1459 match special_cmd {
1460 Some(SpecialCmd::If) if call.arguments.len() < 3 => {
1462 output_override = output_override.map(|ty| ty.union(Type::Nothing))
1463 }
1464 _ => {}
1465 }
1466
1467 let output = output_override.unwrap_or(output);
1468
1469 ParsedInternalCall {
1470 call: Box::new(call),
1471 output,
1472 call_kind,
1473 }
1474}
1475
1476pub fn parse_call(
1477 working_set: &mut StateWorkingSet,
1478 spans: &[Span],
1479 head: Span,
1480 input_type: Option<&Type>,
1481) -> Expression {
1482 trace!("parsing: call");
1483 let call_span = Span::concat(spans);
1484
1485 if spans.is_empty() {
1486 working_set.error(ParseError::UnknownState(
1487 "Encountered command with zero spans".into(),
1488 call_span,
1489 ));
1490 return garbage(working_set, head);
1491 }
1492
1493 let call_sigil = match working_set.get_span_contents(spans[0]).first() {
1494 Some(b'^') => Some(b'^'),
1495 Some(b'%') => Some(b'%'),
1496 _ => None,
1497 };
1498
1499 let mut adjusted_spans = Vec::new();
1500 let resolution_spans = match call_sigil {
1501 Some(b'^') | Some(b'%') => {
1502 adjusted_spans.reserve(spans.len());
1503 adjusted_spans.push(Span::new(spans[0].start + 1, spans[0].end));
1504 adjusted_spans.extend_from_slice(&spans[1..]);
1505 adjusted_spans.as_slice()
1506 }
1507 _ => spans,
1508 };
1509
1510 if call_sigil == Some(b'^') {
1513 trace!("parsing: forced external call");
1514 return parse_external_call(working_set, resolution_spans, call_span);
1515 }
1516
1517 if call_sigil == Some(b'%') && !resolution_spans.is_empty() {
1523 let (head_idx, head_span) = {
1525 let first = working_set.get_span_contents(resolution_spans[0]);
1526 if first.is_empty() && resolution_spans.len() > 1 {
1527 (1, resolution_spans[1])
1528 } else {
1529 (0, resolution_spans[0])
1530 }
1531 };
1532
1533 let dynamic_head_contents = working_set.get_span_contents(head_span);
1534 let is_dynamic_head = !dynamic_head_contents.is_empty()
1535 && (dynamic_head_contents[0] == b'$' || dynamic_head_contents[0] == b'(');
1536
1537 if is_dynamic_head {
1538 trace!("parsing: dynamic percent builtin dispatch");
1539
1540 let head_expr = crate::parser::parse_expression(working_set, &[head_span], input_type);
1541
1542 let mut call = Call::new(call_span);
1544 call.decl_id = DeclId::new(0);
1545
1546 call.set_parser_info(PERCENT_FORCED_BUILTIN_PARSER_INFO.to_string(), head_expr);
1548
1549 for arg_span in resolution_spans.iter().skip(head_idx + 1) {
1552 let contents = working_set.get_span_contents(*arg_span);
1553 if let Some(Spanned { span: arg_span, .. }) =
1554 extract_spread_list(contents.into_spanned(*arg_span))
1555 {
1556 let spread_expr = crate::parser::parse_value(
1557 working_set,
1558 arg_span,
1559 &SyntaxShape::List(Box::new(SyntaxShape::Any)),
1560 None,
1561 );
1562 call.arguments.push(Argument::Spread(spread_expr));
1563 } else {
1564 let arg_expr =
1565 crate::parser::parse_value(working_set, *arg_span, &SyntaxShape::Any, None);
1566 call.arguments.push(Argument::Positional(arg_expr));
1567 }
1568 }
1569
1570 return Expression::new(
1571 working_set,
1572 Expr::Call(Box::new(call)),
1573 call_span,
1574 Type::Any,
1575 );
1576 }
1577 }
1578
1579 let (cmd_start, pos, _name, maybe_decl_id) = if call_sigil == Some(b'%') {
1580 find_longest_decl_with_command_type(working_set, resolution_spans, CommandType::Builtin)
1581 } else {
1582 find_longest_decl(working_set, resolution_spans)
1583 };
1584
1585 if let Some(decl_id) = maybe_decl_id {
1586 if resolution_spans.len() > 1 {
1589 let test_equal = working_set.get_span_contents(resolution_spans[1]);
1590
1591 if test_equal == [b'='] {
1592 trace!("incomplete statement");
1593
1594 working_set.error(ParseError::UnknownState(
1595 "Incomplete statement".into(),
1596 call_span,
1597 ));
1598 return garbage(working_set, call_span);
1599 }
1600 }
1601
1602 let decl = working_set.get_decl(decl_id);
1603
1604 let parsed_call = if let Some(alias) = decl.as_alias() {
1605 if let Expression {
1606 expr: Expr::ExternalCall(head, args),
1607 span: _,
1608 span_id: _,
1609 ty,
1610 } = &alias.clone().wrapped_call
1611 {
1612 trace!("parsing: alias of external call");
1613
1614 let mut head = head.clone();
1615 head.span = Span::concat(&resolution_spans[cmd_start..pos]); let mut final_args = args.clone().into_vec();
1618 for arg_span in &resolution_spans[pos..] {
1619 let arg = parse_external_arg(working_set, *arg_span);
1620 final_args.push(arg);
1621 }
1622
1623 let expression = Expression::new(
1624 working_set,
1625 Expr::ExternalCall(head, final_args.into()),
1626 Span::concat(spans),
1627 ty.clone(),
1628 );
1629
1630 return expression;
1631 } else {
1632 trace!("parsing: alias of internal call");
1633 parse_internal_call(
1634 working_set,
1635 Span::concat(&resolution_spans[cmd_start..pos]),
1636 &resolution_spans[pos..],
1637 decl_id,
1638 ArgumentParsingLevel::Full,
1639 input_type,
1640 )
1641 }
1642 } else {
1643 trace!("parsing: internal call");
1644 parse_internal_call(
1645 working_set,
1646 Span::concat(&resolution_spans[cmd_start..pos]),
1647 &resolution_spans[pos..],
1648 decl_id,
1649 ArgumentParsingLevel::Full,
1650 input_type,
1651 )
1652 };
1653
1654 Expression::new(
1655 working_set,
1656 Expr::Call(parsed_call.call),
1657 call_span,
1658 parsed_call.output,
1659 )
1660 } else {
1661 if call_sigil == Some(b'%') {
1662 working_set.error(ParseError::LabeledErrorWithHelp {
1663 error: "percent sigil requires a built-in command".into(),
1664 label: "unknown built-in command".into(),
1665 help:
1666 "remove `%` to use normal resolution, or use `^` to run an external command explicitly".into(),
1667 span: resolution_spans[0],
1668 });
1669
1670 return parse_external_call(working_set, spans, call_span);
1672 }
1673
1674 let bytes = working_set.get_span_contents(spans[0]);
1676 trace!("parsing: range {bytes:?}");
1677 if let (Some(b'.'), Some(b'.')) = (bytes.first(), bytes.get(1)) {
1678 trace!("-- found leading range indicator");
1679 let starting_error_count = working_set.parse_errors.len();
1680
1681 if let Some(range_expr) = crate::parser::parse_range(working_set, spans[0]) {
1682 trace!("-- successfully parsed range");
1683 return range_expr;
1684 }
1685 working_set.parse_errors.truncate(starting_error_count);
1686 }
1687 trace!("parsing: external call");
1688
1689 parse_external_call(working_set, spans, call_span)
1691 }
1692}
1693
1694fn find_decl_with_command_type(
1695 working_set: &StateWorkingSet<'_>,
1696 name: &[u8],
1697 command_type: CommandType,
1698) -> Option<DeclId> {
1699 for idx in (0..working_set.num_decls()).rev() {
1702 let decl_id = DeclId::new(idx);
1703 let decl = working_set.get_decl(decl_id);
1704 if decl.command_type() == command_type && decl.name().as_bytes() == name {
1705 return Some(decl_id);
1706 }
1707 }
1708
1709 None
1710}
1711
1712fn command_name_from_spans(
1713 working_set: &StateWorkingSet<'_>,
1714 spans: &[Span],
1715 prefix: &[u8],
1716) -> Vec<u8> {
1717 let mut name = Vec::with_capacity(prefix.len() + spans.len() * 2);
1718 name.extend(prefix);
1719
1720 for span in spans {
1721 let name_part = working_set.get_span_contents(*span);
1722 if name.is_empty() {
1723 name.extend(name_part);
1724 } else {
1725 name.push(b' ');
1726 name.extend(name_part);
1727 }
1728 }
1729
1730 name
1731}
1732
1733fn find_longest_decl_with_command_type(
1734 working_set: &StateWorkingSet<'_>,
1735 spans: &[Span],
1736 command_type: CommandType,
1737) -> (
1738 usize,
1739 usize,
1740 Vec<u8>,
1741 Option<nu_protocol::Id<nu_protocol::marker::Decl>>,
1742) {
1743 let mut pos = spans.len();
1744 let cmd_start = 0;
1745 let mut name_spans = spans.to_vec();
1746
1747 let mut name = command_name_from_spans(working_set, &name_spans, b"");
1748
1749 let mut maybe_decl_id = find_decl_with_command_type(working_set, &name, command_type);
1750
1751 while maybe_decl_id.is_none() {
1752 if name_spans.len() <= 1 {
1753 break;
1754 }
1755
1756 name_spans.pop();
1757 pos -= 1;
1758
1759 name = command_name_from_spans(working_set, &name_spans, b"");
1760
1761 maybe_decl_id = find_decl_with_command_type(working_set, &name, command_type);
1762 }
1763
1764 (cmd_start, pos, name, maybe_decl_id)
1765}
1766
1767pub fn find_longest_decl(
1768 working_set: &mut StateWorkingSet<'_>,
1769 spans: &[Span],
1770) -> (
1771 usize,
1772 usize,
1773 Vec<u8>,
1774 Option<nu_protocol::Id<nu_protocol::marker::Decl>>,
1775) {
1776 find_longest_decl_with_prefix(working_set, spans, b"")
1777}
1778
1779pub fn find_longest_decl_with_prefix(
1780 working_set: &mut StateWorkingSet<'_>,
1781 spans: &[Span],
1782 prefix: &[u8],
1783) -> (
1784 usize,
1785 usize,
1786 Vec<u8>,
1787 Option<nu_protocol::Id<nu_protocol::marker::Decl>>,
1788) {
1789 let mut pos = 0;
1790 let cmd_start = pos;
1791 let mut name_spans = vec![];
1792
1793 for word_span in spans[cmd_start..].iter() {
1794 name_spans.push(*word_span);
1797
1798 pos += 1;
1799 }
1800
1801 let mut name = command_name_from_spans(working_set, &name_spans, prefix);
1802
1803 let mut maybe_decl_id = working_set.find_decl(&name);
1804
1805 while maybe_decl_id.is_none() {
1806 if name_spans.len() <= 1 {
1808 break;
1810 }
1811
1812 name_spans.pop();
1813 pos -= 1;
1814
1815 name = command_name_from_spans(working_set, &name_spans, prefix);
1816 maybe_decl_id = working_set.find_decl(&name);
1817 }
1818
1819 if let Some(decl_id) = maybe_decl_id
1822 && pos < spans.len()
1823 {
1824 let decl = working_set.get_decl(decl_id);
1825 if let Some(alias) = decl.as_alias() {
1826 if let Expression {
1829 expr: Expr::Call(call),
1830 ..
1831 } = &alias.wrapped_call
1832 {
1833 let aliased_decl_id = call.decl_id;
1834 let aliased_name = working_set.get_decl(aliased_decl_id).name().to_string();
1835
1836 let (_, new_pos, new_name, new_decl_id) = find_longest_decl_with_prefix(
1838 working_set,
1839 &spans[pos..],
1840 aliased_name.as_bytes(),
1841 );
1842
1843 if new_decl_id.is_some() && new_pos > 0 {
1845 let total_pos = pos + new_pos;
1846 return (cmd_start, total_pos, new_name, new_decl_id);
1847 }
1848 }
1849 }
1850 }
1851
1852 (cmd_start, pos, name, maybe_decl_id)
1853}
1854
1855pub fn parse_attribute(
1856 working_set: &mut StateWorkingSet,
1857 lite_command: &LiteCommand,
1858) -> (Attribute, Option<String>) {
1859 let _ = lite_command
1860 .parts
1861 .first()
1862 .filter(|s| working_set.get_span_contents(**s).starts_with(b"@"))
1863 .expect("Attributes always start with an `@`");
1864
1865 assert!(
1866 lite_command.attribute_idx.is_empty(),
1867 "attributes can't have attributes"
1868 );
1869
1870 let mut spans = lite_command.parts.clone();
1871 if let Some(first) = spans.first_mut() {
1872 first.start += 1;
1873 }
1874 let spans = spans.as_slice();
1875 let attr_span = Span::concat(spans);
1876
1877 let (cmd_start, cmd_end, mut name, decl_id) =
1878 find_longest_decl_with_prefix(working_set, spans, b"attr");
1879
1880 debug_assert!(name.starts_with(b"attr "));
1881 let _ = name.drain(..(b"attr ".len()));
1882
1883 let name_span = Span::concat(&spans[cmd_start..cmd_end]);
1884
1885 let Ok(name) = String::from_utf8(name) else {
1886 working_set.error(ParseError::NonUtf8(name_span));
1887 return (
1888 Attribute {
1889 expr: garbage(working_set, attr_span),
1890 },
1891 None,
1892 );
1893 };
1894
1895 let Some(decl_id) = decl_id else {
1896 working_set.error(ParseError::UnknownCommand(name_span));
1897 return (
1898 Attribute {
1899 expr: garbage(working_set, attr_span),
1900 },
1901 None,
1902 );
1903 };
1904
1905 let decl = working_set.get_decl(decl_id);
1906
1907 let parsed_call = match decl.as_alias() {
1908 Some(alias) => match &alias.clone().wrapped_call {
1911 Expression {
1912 expr: Expr::ExternalCall(..),
1913 ..
1914 } => {
1915 let shell_error = ShellError::NotAConstCommand { span: name_span };
1916 working_set.error(shell_error.wrap(working_set, attr_span));
1917 return (
1918 Attribute {
1919 expr: garbage(working_set, Span::concat(spans)),
1920 },
1921 None,
1922 );
1923 }
1924 _ => {
1925 trace!("parsing: alias of internal call");
1926 parse_internal_call(
1927 working_set,
1928 name_span,
1929 &spans[cmd_end..],
1930 decl_id,
1931 ArgumentParsingLevel::Full,
1932 None,
1933 )
1934 }
1935 },
1936 None => {
1937 trace!("parsing: internal call");
1938 parse_internal_call(
1939 working_set,
1940 name_span,
1941 &spans[cmd_end..],
1942 decl_id,
1943 ArgumentParsingLevel::Full,
1944 None,
1945 )
1946 }
1947 };
1948
1949 (
1950 Attribute {
1951 expr: Expression::new(
1952 working_set,
1953 Expr::Call(parsed_call.call),
1954 Span::concat(spans),
1955 parsed_call.output,
1956 ),
1957 },
1958 Some(name),
1959 )
1960}