diff --git a/crates/nu-command/src/core_commands/if_.rs b/crates/nu-command/src/core_commands/if_.rs index 54e4629db..160e7a82f 100644 --- a/crates/nu-command/src/core_commands/if_.rs +++ b/crates/nu-command/src/core_commands/if_.rs @@ -28,7 +28,13 @@ impl Command for If { ) .optional( "else_expression", - SyntaxShape::Keyword(b"else".to_vec(), Box::new(SyntaxShape::Expression)), + SyntaxShape::Keyword( + b"else".to_vec(), + Box::new(SyntaxShape::OneOf(vec![ + SyntaxShape::Block, + SyntaxShape::Expression, + ])), + ), "expression or block to run if check fails", ) .category(Category::Core) diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index bf5d1f638..8dfd5d550 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -644,6 +644,27 @@ pub fn parse_multispan_value( (arg, error) } + SyntaxShape::OneOf(shapes) => { + for shape in shapes.iter() { + if let (s, None) = parse_multispan_value( + working_set, + spans, + spans_idx, + shape, + expand_aliases_denylist, + ) { + return (s, None); + } + } + let span = spans[*spans_idx]; + ( + Expression::garbage(span), + Some(ParseError::Expected( + format!("one of a list of accepted shapes: {:?}", shapes), + span, + )), + ) + } SyntaxShape::Expression => { trace!("parsing: expression"); @@ -4252,7 +4273,10 @@ pub fn parse_value( } else { return ( Expression::garbage(span), - Some(ParseError::Expected("non-block value".into(), span)), + Some(ParseError::Expected( + format!("non-block value: {}", shape), + span, + )), ); } } diff --git a/crates/nu-protocol/src/syntax_shape.rs b/crates/nu-protocol/src/syntax_shape.rs index 495ff9e5f..26a32fe46 100644 --- a/crates/nu-protocol/src/syntax_shape.rs +++ b/crates/nu-protocol/src/syntax_shape.rs @@ -101,6 +101,9 @@ pub enum SyntaxShape { /// A custom shape with custom completion logic Custom(Box, DeclId), + /// One of a list of possible items, checked in order + OneOf(Vec), + /// Nothing Nothing, } @@ -132,6 +135,7 @@ impl SyntaxShape { SyntaxShape::Keyword(_, expr) => expr.to_type(), SyntaxShape::MathExpression => Type::Any, SyntaxShape::Number => Type::Number, + SyntaxShape::OneOf(_) => Type::Any, SyntaxShape::Operator => Type::Any, SyntaxShape::Range => Type::Any, SyntaxShape::Record => Type::Record(vec![]), // FIXME: What role should fields play in the Record type? @@ -191,6 +195,11 @@ impl Display for SyntaxShape { SyntaxShape::Boolean => write!(f, "bool"), SyntaxShape::Error => write!(f, "error"), SyntaxShape::Custom(x, _) => write!(f, "custom<{}>", x), + SyntaxShape::OneOf(list) => { + let arg_vec: Vec<_> = list.iter().map(|x| x.to_string()).collect(); + let arg_string = arg_vec.join(", "); + write!(f, "one_of({})", arg_string) + } SyntaxShape::Nothing => write!(f, "nothing"), } } diff --git a/src/tests/test_conditionals.rs b/src/tests/test_conditionals.rs index 285485c92..20307ffe6 100644 --- a/src/tests/test_conditionals.rs +++ b/src/tests/test_conditionals.rs @@ -59,3 +59,19 @@ fn if_elseif3() -> TestResult { fn if_elseif4() -> TestResult { run_test("if 2 > 3 { 5 } else if 6 < 7 { 4 } else { 8 } ", "4") } + +#[test] +fn mutation_in_else() -> TestResult { + run_test( + "mut x = 100; if 2 > 3 { $x = 200 } else { $x = 300 }; $x ", + "300", + ) +} + +#[test] +fn mutation_in_else2() -> TestResult { + run_test( + "mut x = 100; if 2 > 3 { $x = 200 } else if true { $x = 400 } else { $x = 300 }; $x ", + "400", + ) +}