diff --git a/crates/nu-cmd-lang/src/core_commands/if_.rs b/crates/nu-cmd-lang/src/core_commands/if_.rs index 7c54ca432f..e8b192da04 100644 --- a/crates/nu-cmd-lang/src/core_commands/if_.rs +++ b/crates/nu-cmd-lang/src/core_commands/if_.rs @@ -60,11 +60,13 @@ impl Command for If { ) -> Result { let call = call.assert_ast_call()?; let cond = call.positional_nth(0).expect("checked through parser"); - let then_block = call - .positional_nth(1) - .expect("checked through parser") + let then_expr = call.positional_nth(1).expect("checked through parser"); + let then_block = then_expr .as_block() - .expect("internal error: missing block"); + .ok_or_else(|| ShellError::TypeMismatch { + err_message: "expected block".into(), + span: then_expr.span, + })?; let else_case = call.positional_nth(2); if eval_constant(working_set, cond)?.as_bool()? { diff --git a/crates/nu-command/src/database/commands/into_sqlite.rs b/crates/nu-command/src/database/commands/into_sqlite.rs index ee7e8bb3c8..27417fca2f 100644 --- a/crates/nu-command/src/database/commands/into_sqlite.rs +++ b/crates/nu-command/src/database/commands/into_sqlite.rs @@ -353,6 +353,7 @@ fn nu_value_to_sqlite_type(val: &Value) -> Result<&'static str, ShellError> { // intentionally enumerated so that any future types get handled Type::Any + | Type::Block | Type::CellPath | Type::Closure | Type::Custom(_) diff --git a/crates/nu-parser/src/parse_keywords.rs b/crates/nu-parser/src/parse_keywords.rs index 476ec803c8..e79edda066 100644 --- a/crates/nu-parser/src/parse_keywords.rs +++ b/crates/nu-parser/src/parse_keywords.rs @@ -2487,7 +2487,7 @@ pub fn parse_module( working_set, Expr::Block(block_id), block_expr_span, - Type::Any, + Type::Block, ); let module_decl_id = working_set diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index bd63ea37fa..140964f619 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -4768,7 +4768,7 @@ pub fn parse_block_expression(working_set: &mut StateWorkingSet, span: Span) -> let block_id = working_set.add_block(Arc::new(output)); - Expression::new(working_set, Expr::Block(block_id), span, Type::Any) + Expression::new(working_set, Expr::Block(block_id), span, Type::Block) } pub fn parse_match_block_expression(working_set: &mut StateWorkingSet, span: Span) -> Expression { diff --git a/crates/nu-parser/src/type_check.rs b/crates/nu-parser/src/type_check.rs index ca964798ef..a532919394 100644 --- a/crates/nu-parser/src/type_check.rs +++ b/crates/nu-parser/src/type_check.rs @@ -88,6 +88,7 @@ pub fn type_compatible(lhs: &Type, rhs: &Type) -> bool { (Type::Int, Type::Number) => true, (Type::Number, Type::Float) => true, (Type::Float, Type::Number) => true, + (Type::Closure, Type::Block) => true, (Type::Any, _) => true, (_, Type::Any) => true, (Type::Record(lhs), Type::Record(rhs)) | (Type::Table(lhs), Type::Table(rhs)) => { diff --git a/crates/nu-protocol/src/syntax_shape.rs b/crates/nu-protocol/src/syntax_shape.rs index 786a00982e..44dd4903d3 100644 --- a/crates/nu-protocol/src/syntax_shape.rs +++ b/crates/nu-protocol/src/syntax_shape.rs @@ -143,7 +143,7 @@ impl SyntaxShape { match self { SyntaxShape::Any => Type::Any, - SyntaxShape::Block => Type::Any, + SyntaxShape::Block => Type::Block, SyntaxShape::Closure(_) => Type::Closure, SyntaxShape::Binary => Type::Binary, SyntaxShape::CellPath => Type::Any, diff --git a/crates/nu-protocol/src/ty.rs b/crates/nu-protocol/src/ty.rs index 72f5dbf905..617606d238 100644 --- a/crates/nu-protocol/src/ty.rs +++ b/crates/nu-protocol/src/ty.rs @@ -9,6 +9,7 @@ use strum_macros::EnumIter; pub enum Type { Any, Binary, + Block, Bool, CellPath, Closure, @@ -110,6 +111,7 @@ impl Type { Type::Range => SyntaxShape::Range, Type::Bool => SyntaxShape::Boolean, Type::String => SyntaxShape::String, + Type::Block => SyntaxShape::Block, // FIXME needs more accuracy Type::Closure => SyntaxShape::Closure(None), // FIXME needs more accuracy Type::CellPath => SyntaxShape::CellPath, Type::Duration => SyntaxShape::Duration, @@ -134,6 +136,7 @@ impl Type { match self { Type::Closure => String::from("closure"), Type::Bool => String::from("bool"), + Type::Block => String::from("block"), Type::CellPath => String::from("cell-path"), Type::Date => String::from("datetime"), Type::Duration => String::from("duration"), @@ -159,6 +162,7 @@ impl Type { impl Display for Type { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { + Type::Block => write!(f, "block"), Type::Closure => write!(f, "closure"), Type::Bool => write!(f, "bool"), Type::CellPath => write!(f, "cell-path"), diff --git a/tests/const_/mod.rs b/tests/const_/mod.rs index 76dc712489..e99508b83d 100644 --- a/tests/const_/mod.rs +++ b/tests/const_/mod.rs @@ -410,6 +410,15 @@ fn if_const() { assert_eq!(actual.out, "no!"); } +#[rstest] +#[case(&"const x = if true ()", "expected block, found nothing")] +#[case(&"const x = if true {foo: bar}", "expected block, found record")] +#[case(&"const x = if true {1: 2}", "expected block")] +fn if_const_error(#[case] inp: &str, #[case] expect: &str) { + let actual = nu!(inp); + assert!(actual.err.contains(expect)); +} + #[test] fn const_glob_type() { let actual = nu!("const x: glob = 'aa'; $x | describe");