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 = parse_multispan_value(
1303 working_set,
1304 &spans[..end],
1305 &mut spans_idx,
1306 &positional.shape,
1307 input_type.as_ref(),
1308 );
1309
1310 match special_cmd {
1311 Some(SpecialCmd::Match) if &positional.name == "match_block" => {
1312 output_override = Some(expr.ty.clone());
1313 }
1314 Some(SpecialCmd::If)
1315 if positional_idx == 1
1316 && let Expr::Block(block_id) = &expr.expr =>
1317 {
1318 let block = working_set.get_block(*block_id);
1319 let ty = match block.pipelines.is_empty() {
1320 false => block.output_type(),
1321 true => input_type.unwrap_or(Type::Any),
1322 };
1323
1324 output_override = Some(match output_override {
1325 Some(existing_ty) => existing_ty.union(ty),
1326 None => ty,
1327 });
1328 }
1329 Some(SpecialCmd::If)
1330 if positional_idx == 2
1331 && let Expr::Keyword(kw) = &expr.expr =>
1332 {
1333 let ty = match &kw.expr.expr {
1334 Expr::Block(block_id) => {
1335 let block = working_set.get_block(*block_id);
1336 match block.pipelines.is_empty() {
1337 false => block.output_type(),
1338 true => input_type.unwrap_or(Type::Any),
1339 }
1340 }
1341 _ => kw.expr.ty.clone(),
1342 };
1343
1344 output_override = Some(match output_override {
1345 Some(existing_ty) => existing_ty.union(ty),
1346 None => ty,
1347 });
1348 }
1349 _ => {}
1350 };
1351
1352 expr
1353 }
1354 };
1355
1356 if let SyntaxShape::OneOf(ref shapes) = positional.shape {
1364 for one_shape in shapes {
1365 if let SyntaxShape::Keyword(keyword, ..) = one_shape
1366 && keyword == b"catch"
1367 && let [nu_protocol::CompileError::NotInALoop { .. }] =
1368 &working_set.compile_errors[compile_error_count..]
1369 {
1370 working_set.compile_errors.truncate(compile_error_count);
1371 }
1372 }
1373 }
1374
1375 let arg = if !type_compatible(&positional.shape.to_type(), &arg.ty) {
1376 working_set.error(ParseError::TypeMismatch(
1377 positional.shape.to_type(),
1378 arg.ty,
1379 arg.span,
1380 ));
1381 Expression::garbage(working_set, arg.span)
1382 } else {
1383 arg
1384 };
1385
1386 call.add_positional(arg);
1387 positional_idx += 1;
1388 } else if signature.allows_unknown_args {
1389 let arg = parse_unknown_arg(working_set, arg_span, &signature);
1390
1391 call.add_unknown(arg);
1392 } else {
1393 call.add_positional(Expression::garbage(working_set, arg_span));
1394 working_set.error(ParseError::ExtraPositional(
1395 signature.call_signature(),
1396 arg_span,
1397 ))
1398 }
1399
1400 spans_idx += 1;
1401 }
1402
1403 let call_kind = check_call(working_set, command_span, &signature, &call);
1407
1408 deprecation
1409 .into_iter()
1410 .filter_map(|entry| entry.parse_warning(&signature.name, &call))
1411 .for_each(|warning| {
1412 working_set.warning(warning);
1416 });
1417
1418 if signature.creates_scope {
1419 working_set.exit_scope();
1420 }
1421
1422 match special_cmd {
1423 Some(SpecialCmd::If) if call.arguments.len() < 3 => {
1425 output_override = output_override.map(|ty| ty.union(Type::Nothing))
1426 }
1427 _ => {}
1428 }
1429
1430 let output = output_override.unwrap_or(output);
1431
1432 ParsedInternalCall {
1433 call: Box::new(call),
1434 output,
1435 call_kind,
1436 }
1437}
1438
1439pub fn parse_call(
1440 working_set: &mut StateWorkingSet,
1441 spans: &[Span],
1442 head: Span,
1443 input_type: Option<&Type>,
1444) -> Expression {
1445 trace!("parsing: call");
1446 let call_span = Span::concat(spans);
1447
1448 if spans.is_empty() {
1449 working_set.error(ParseError::UnknownState(
1450 "Encountered command with zero spans".into(),
1451 call_span,
1452 ));
1453 return garbage(working_set, head);
1454 }
1455
1456 let call_sigil = match working_set.get_span_contents(spans[0]).first() {
1457 Some(b'^') => Some(b'^'),
1458 Some(b'%') => Some(b'%'),
1459 _ => None,
1460 };
1461
1462 let mut adjusted_spans = Vec::new();
1463 let resolution_spans = match call_sigil {
1464 Some(b'^') | Some(b'%') => {
1465 adjusted_spans.reserve(spans.len());
1466 adjusted_spans.push(Span::new(spans[0].start + 1, spans[0].end));
1467 adjusted_spans.extend_from_slice(&spans[1..]);
1468 adjusted_spans.as_slice()
1469 }
1470 _ => spans,
1471 };
1472
1473 if call_sigil == Some(b'^') {
1476 trace!("parsing: forced external call");
1477 return parse_external_call(working_set, resolution_spans, call_span);
1478 }
1479
1480 if call_sigil == Some(b'%') && !resolution_spans.is_empty() {
1486 let (head_idx, head_span) = {
1488 let first = working_set.get_span_contents(resolution_spans[0]);
1489 if first.is_empty() && resolution_spans.len() > 1 {
1490 (1, resolution_spans[1])
1491 } else {
1492 (0, resolution_spans[0])
1493 }
1494 };
1495
1496 let dynamic_head_contents = working_set.get_span_contents(head_span);
1497 let is_dynamic_head = !dynamic_head_contents.is_empty()
1498 && (dynamic_head_contents[0] == b'$' || dynamic_head_contents[0] == b'(');
1499
1500 if is_dynamic_head {
1501 trace!("parsing: dynamic percent builtin dispatch");
1502
1503 let head_expr = crate::parser::parse_expression(working_set, &[head_span], input_type);
1504
1505 let mut call = Call::new(call_span);
1507 call.decl_id = DeclId::new(0);
1508
1509 call.set_parser_info(PERCENT_FORCED_BUILTIN_PARSER_INFO.to_string(), head_expr);
1511
1512 for arg_span in resolution_spans.iter().skip(head_idx + 1) {
1515 let contents = working_set.get_span_contents(*arg_span);
1516 if let Some(Spanned { span: arg_span, .. }) =
1517 extract_spread_list(contents.into_spanned(*arg_span))
1518 {
1519 let spread_expr = crate::parser::parse_value(
1520 working_set,
1521 arg_span,
1522 &SyntaxShape::List(Box::new(SyntaxShape::Any)),
1523 None,
1524 );
1525 call.arguments.push(Argument::Spread(spread_expr));
1526 } else {
1527 let arg_expr =
1528 crate::parser::parse_value(working_set, *arg_span, &SyntaxShape::Any, None);
1529 call.arguments.push(Argument::Positional(arg_expr));
1530 }
1531 }
1532
1533 return Expression::new(
1534 working_set,
1535 Expr::Call(Box::new(call)),
1536 call_span,
1537 Type::Any,
1538 );
1539 }
1540 }
1541
1542 let (cmd_start, pos, _name, maybe_decl_id) = if call_sigil == Some(b'%') {
1543 find_longest_decl_with_command_type(working_set, resolution_spans, CommandType::Builtin)
1544 } else {
1545 find_longest_decl(working_set, resolution_spans)
1546 };
1547
1548 if let Some(decl_id) = maybe_decl_id {
1549 if resolution_spans.len() > 1 {
1552 let test_equal = working_set.get_span_contents(resolution_spans[1]);
1553
1554 if test_equal == [b'='] {
1555 trace!("incomplete statement");
1556
1557 working_set.error(ParseError::UnknownState(
1558 "Incomplete statement".into(),
1559 call_span,
1560 ));
1561 return garbage(working_set, call_span);
1562 }
1563 }
1564
1565 let decl = working_set.get_decl(decl_id);
1566
1567 let parsed_call = if let Some(alias) = decl.as_alias() {
1568 if let Expression {
1569 expr: Expr::ExternalCall(head, args),
1570 span: _,
1571 span_id: _,
1572 ty,
1573 } = &alias.clone().wrapped_call
1574 {
1575 trace!("parsing: alias of external call");
1576
1577 let mut head = head.clone();
1578 head.span = Span::concat(&resolution_spans[cmd_start..pos]); let mut final_args = args.clone().into_vec();
1581 for arg_span in &resolution_spans[pos..] {
1582 let arg = parse_external_arg(working_set, *arg_span);
1583 final_args.push(arg);
1584 }
1585
1586 let expression = Expression::new(
1587 working_set,
1588 Expr::ExternalCall(head, final_args.into()),
1589 Span::concat(spans),
1590 ty.clone(),
1591 );
1592
1593 return expression;
1594 } else {
1595 trace!("parsing: alias of internal call");
1596 parse_internal_call(
1597 working_set,
1598 Span::concat(&resolution_spans[cmd_start..pos]),
1599 &resolution_spans[pos..],
1600 decl_id,
1601 ArgumentParsingLevel::Full,
1602 input_type,
1603 )
1604 }
1605 } else {
1606 trace!("parsing: internal call");
1607 parse_internal_call(
1608 working_set,
1609 Span::concat(&resolution_spans[cmd_start..pos]),
1610 &resolution_spans[pos..],
1611 decl_id,
1612 ArgumentParsingLevel::Full,
1613 input_type,
1614 )
1615 };
1616
1617 Expression::new(
1618 working_set,
1619 Expr::Call(parsed_call.call),
1620 call_span,
1621 parsed_call.output,
1622 )
1623 } else {
1624 if call_sigil == Some(b'%') {
1625 working_set.error(ParseError::LabeledErrorWithHelp {
1626 error: "percent sigil requires a built-in command".into(),
1627 label: "unknown built-in command".into(),
1628 help:
1629 "remove `%` to use normal resolution, or use `^` to run an external command explicitly".into(),
1630 span: resolution_spans[0],
1631 });
1632
1633 return parse_external_call(working_set, spans, call_span);
1635 }
1636
1637 let bytes = working_set.get_span_contents(spans[0]);
1639 trace!("parsing: range {bytes:?}");
1640 if let (Some(b'.'), Some(b'.')) = (bytes.first(), bytes.get(1)) {
1641 trace!("-- found leading range indicator");
1642 let starting_error_count = working_set.parse_errors.len();
1643
1644 if let Some(range_expr) = crate::parser::parse_range(working_set, spans[0]) {
1645 trace!("-- successfully parsed range");
1646 return range_expr;
1647 }
1648 working_set.parse_errors.truncate(starting_error_count);
1649 }
1650 trace!("parsing: external call");
1651
1652 parse_external_call(working_set, spans, call_span)
1654 }
1655}
1656
1657fn find_decl_with_command_type(
1658 working_set: &StateWorkingSet<'_>,
1659 name: &[u8],
1660 command_type: CommandType,
1661) -> Option<DeclId> {
1662 for idx in (0..working_set.num_decls()).rev() {
1665 let decl_id = DeclId::new(idx);
1666 let decl = working_set.get_decl(decl_id);
1667 if decl.command_type() == command_type && decl.name().as_bytes() == name {
1668 return Some(decl_id);
1669 }
1670 }
1671
1672 None
1673}
1674
1675fn command_name_from_spans(
1676 working_set: &StateWorkingSet<'_>,
1677 spans: &[Span],
1678 prefix: &[u8],
1679) -> Vec<u8> {
1680 let mut name = Vec::with_capacity(prefix.len() + spans.len() * 2);
1681 name.extend(prefix);
1682
1683 for span in spans {
1684 let name_part = working_set.get_span_contents(*span);
1685 if name.is_empty() {
1686 name.extend(name_part);
1687 } else {
1688 name.push(b' ');
1689 name.extend(name_part);
1690 }
1691 }
1692
1693 name
1694}
1695
1696fn find_longest_decl_with_command_type(
1697 working_set: &StateWorkingSet<'_>,
1698 spans: &[Span],
1699 command_type: CommandType,
1700) -> (
1701 usize,
1702 usize,
1703 Vec<u8>,
1704 Option<nu_protocol::Id<nu_protocol::marker::Decl>>,
1705) {
1706 let mut pos = spans.len();
1707 let cmd_start = 0;
1708 let mut name_spans = spans.to_vec();
1709
1710 let mut name = command_name_from_spans(working_set, &name_spans, b"");
1711
1712 let mut maybe_decl_id = find_decl_with_command_type(working_set, &name, command_type);
1713
1714 while maybe_decl_id.is_none() {
1715 if name_spans.len() <= 1 {
1716 break;
1717 }
1718
1719 name_spans.pop();
1720 pos -= 1;
1721
1722 name = command_name_from_spans(working_set, &name_spans, b"");
1723
1724 maybe_decl_id = find_decl_with_command_type(working_set, &name, command_type);
1725 }
1726
1727 (cmd_start, pos, name, maybe_decl_id)
1728}
1729
1730pub fn find_longest_decl(
1731 working_set: &mut StateWorkingSet<'_>,
1732 spans: &[Span],
1733) -> (
1734 usize,
1735 usize,
1736 Vec<u8>,
1737 Option<nu_protocol::Id<nu_protocol::marker::Decl>>,
1738) {
1739 find_longest_decl_with_prefix(working_set, spans, b"")
1740}
1741
1742pub fn find_longest_decl_with_prefix(
1743 working_set: &mut StateWorkingSet<'_>,
1744 spans: &[Span],
1745 prefix: &[u8],
1746) -> (
1747 usize,
1748 usize,
1749 Vec<u8>,
1750 Option<nu_protocol::Id<nu_protocol::marker::Decl>>,
1751) {
1752 let mut pos = 0;
1753 let cmd_start = pos;
1754 let mut name_spans = vec![];
1755
1756 for word_span in spans[cmd_start..].iter() {
1757 name_spans.push(*word_span);
1760
1761 pos += 1;
1762 }
1763
1764 let mut name = command_name_from_spans(working_set, &name_spans, prefix);
1765
1766 let mut maybe_decl_id = working_set.find_decl(&name);
1767
1768 while maybe_decl_id.is_none() {
1769 if name_spans.len() <= 1 {
1771 break;
1773 }
1774
1775 name_spans.pop();
1776 pos -= 1;
1777
1778 name = command_name_from_spans(working_set, &name_spans, prefix);
1779 maybe_decl_id = working_set.find_decl(&name);
1780 }
1781
1782 if let Some(decl_id) = maybe_decl_id
1785 && pos < spans.len()
1786 {
1787 let decl = working_set.get_decl(decl_id);
1788 if let Some(alias) = decl.as_alias() {
1789 if let Expression {
1792 expr: Expr::Call(call),
1793 ..
1794 } = &alias.wrapped_call
1795 {
1796 let aliased_decl_id = call.decl_id;
1797 let aliased_name = working_set.get_decl(aliased_decl_id).name().to_string();
1798
1799 let (_, new_pos, new_name, new_decl_id) = find_longest_decl_with_prefix(
1801 working_set,
1802 &spans[pos..],
1803 aliased_name.as_bytes(),
1804 );
1805
1806 if new_decl_id.is_some() && new_pos > 0 {
1808 let total_pos = pos + new_pos;
1809 return (cmd_start, total_pos, new_name, new_decl_id);
1810 }
1811 }
1812 }
1813 }
1814
1815 (cmd_start, pos, name, maybe_decl_id)
1816}
1817
1818pub fn parse_attribute(
1819 working_set: &mut StateWorkingSet,
1820 lite_command: &LiteCommand,
1821) -> (Attribute, Option<String>) {
1822 let _ = lite_command
1823 .parts
1824 .first()
1825 .filter(|s| working_set.get_span_contents(**s).starts_with(b"@"))
1826 .expect("Attributes always start with an `@`");
1827
1828 assert!(
1829 lite_command.attribute_idx.is_empty(),
1830 "attributes can't have attributes"
1831 );
1832
1833 let mut spans = lite_command.parts.clone();
1834 if let Some(first) = spans.first_mut() {
1835 first.start += 1;
1836 }
1837 let spans = spans.as_slice();
1838 let attr_span = Span::concat(spans);
1839
1840 let (cmd_start, cmd_end, mut name, decl_id) =
1841 find_longest_decl_with_prefix(working_set, spans, b"attr");
1842
1843 debug_assert!(name.starts_with(b"attr "));
1844 let _ = name.drain(..(b"attr ".len()));
1845
1846 let name_span = Span::concat(&spans[cmd_start..cmd_end]);
1847
1848 let Ok(name) = String::from_utf8(name) else {
1849 working_set.error(ParseError::NonUtf8(name_span));
1850 return (
1851 Attribute {
1852 expr: garbage(working_set, attr_span),
1853 },
1854 None,
1855 );
1856 };
1857
1858 let Some(decl_id) = decl_id else {
1859 working_set.error(ParseError::UnknownCommand(name_span));
1860 return (
1861 Attribute {
1862 expr: garbage(working_set, attr_span),
1863 },
1864 None,
1865 );
1866 };
1867
1868 let decl = working_set.get_decl(decl_id);
1869
1870 let parsed_call = match decl.as_alias() {
1871 Some(alias) => match &alias.clone().wrapped_call {
1874 Expression {
1875 expr: Expr::ExternalCall(..),
1876 ..
1877 } => {
1878 let shell_error = ShellError::NotAConstCommand { span: name_span };
1879 working_set.error(shell_error.wrap(working_set, attr_span));
1880 return (
1881 Attribute {
1882 expr: garbage(working_set, Span::concat(spans)),
1883 },
1884 None,
1885 );
1886 }
1887 _ => {
1888 trace!("parsing: alias of internal call");
1889 parse_internal_call(
1890 working_set,
1891 name_span,
1892 &spans[cmd_end..],
1893 decl_id,
1894 ArgumentParsingLevel::Full,
1895 None,
1896 )
1897 }
1898 },
1899 None => {
1900 trace!("parsing: internal call");
1901 parse_internal_call(
1902 working_set,
1903 name_span,
1904 &spans[cmd_end..],
1905 decl_id,
1906 ArgumentParsingLevel::Full,
1907 None,
1908 )
1909 }
1910 };
1911
1912 (
1913 Attribute {
1914 expr: Expression::new(
1915 working_set,
1916 Expr::Call(parsed_call.call),
1917 Span::concat(spans),
1918 parsed_call.output,
1919 ),
1920 },
1921 Some(name),
1922 )
1923}