fix: panic of if command as a constant expr by bringing back Type::Block (#16122)

Fixes #16110. Alternative to #16120 

# Description

# User-Facing Changes

no more panic

# Tests + Formatting

+1

# After Submitting
This commit is contained in:
zc he
2025-07-08 20:45:35 +08:00
committed by GitHub
parent a674ce2dbc
commit 4da755895d
8 changed files with 24 additions and 7 deletions

View File

@ -60,11 +60,13 @@ impl Command for If {
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let call = call.assert_ast_call()?; let call = call.assert_ast_call()?;
let cond = call.positional_nth(0).expect("checked through parser"); let cond = call.positional_nth(0).expect("checked through parser");
let then_block = call let then_expr = call.positional_nth(1).expect("checked through parser");
.positional_nth(1) let then_block = then_expr
.expect("checked through parser")
.as_block() .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); let else_case = call.positional_nth(2);
if eval_constant(working_set, cond)?.as_bool()? { if eval_constant(working_set, cond)?.as_bool()? {

View File

@ -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 // intentionally enumerated so that any future types get handled
Type::Any Type::Any
| Type::Block
| Type::CellPath | Type::CellPath
| Type::Closure | Type::Closure
| Type::Custom(_) | Type::Custom(_)

View File

@ -2487,7 +2487,7 @@ pub fn parse_module(
working_set, working_set,
Expr::Block(block_id), Expr::Block(block_id),
block_expr_span, block_expr_span,
Type::Any, Type::Block,
); );
let module_decl_id = working_set let module_decl_id = working_set

View File

@ -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)); 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 { pub fn parse_match_block_expression(working_set: &mut StateWorkingSet, span: Span) -> Expression {

View File

@ -88,6 +88,7 @@ pub fn type_compatible(lhs: &Type, rhs: &Type) -> bool {
(Type::Int, Type::Number) => true, (Type::Int, Type::Number) => true,
(Type::Number, Type::Float) => true, (Type::Number, Type::Float) => true,
(Type::Float, Type::Number) => true, (Type::Float, Type::Number) => true,
(Type::Closure, Type::Block) => true,
(Type::Any, _) => true, (Type::Any, _) => true,
(_, Type::Any) => true, (_, Type::Any) => true,
(Type::Record(lhs), Type::Record(rhs)) | (Type::Table(lhs), Type::Table(rhs)) => { (Type::Record(lhs), Type::Record(rhs)) | (Type::Table(lhs), Type::Table(rhs)) => {

View File

@ -143,7 +143,7 @@ impl SyntaxShape {
match self { match self {
SyntaxShape::Any => Type::Any, SyntaxShape::Any => Type::Any,
SyntaxShape::Block => Type::Any, SyntaxShape::Block => Type::Block,
SyntaxShape::Closure(_) => Type::Closure, SyntaxShape::Closure(_) => Type::Closure,
SyntaxShape::Binary => Type::Binary, SyntaxShape::Binary => Type::Binary,
SyntaxShape::CellPath => Type::Any, SyntaxShape::CellPath => Type::Any,

View File

@ -9,6 +9,7 @@ use strum_macros::EnumIter;
pub enum Type { pub enum Type {
Any, Any,
Binary, Binary,
Block,
Bool, Bool,
CellPath, CellPath,
Closure, Closure,
@ -110,6 +111,7 @@ impl Type {
Type::Range => SyntaxShape::Range, Type::Range => SyntaxShape::Range,
Type::Bool => SyntaxShape::Boolean, Type::Bool => SyntaxShape::Boolean,
Type::String => SyntaxShape::String, Type::String => SyntaxShape::String,
Type::Block => SyntaxShape::Block, // FIXME needs more accuracy
Type::Closure => SyntaxShape::Closure(None), // FIXME needs more accuracy Type::Closure => SyntaxShape::Closure(None), // FIXME needs more accuracy
Type::CellPath => SyntaxShape::CellPath, Type::CellPath => SyntaxShape::CellPath,
Type::Duration => SyntaxShape::Duration, Type::Duration => SyntaxShape::Duration,
@ -134,6 +136,7 @@ impl Type {
match self { match self {
Type::Closure => String::from("closure"), Type::Closure => String::from("closure"),
Type::Bool => String::from("bool"), Type::Bool => String::from("bool"),
Type::Block => String::from("block"),
Type::CellPath => String::from("cell-path"), Type::CellPath => String::from("cell-path"),
Type::Date => String::from("datetime"), Type::Date => String::from("datetime"),
Type::Duration => String::from("duration"), Type::Duration => String::from("duration"),
@ -159,6 +162,7 @@ impl Type {
impl Display for Type { impl Display for Type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Type::Block => write!(f, "block"),
Type::Closure => write!(f, "closure"), Type::Closure => write!(f, "closure"),
Type::Bool => write!(f, "bool"), Type::Bool => write!(f, "bool"),
Type::CellPath => write!(f, "cell-path"), Type::CellPath => write!(f, "cell-path"),

View File

@ -410,6 +410,15 @@ fn if_const() {
assert_eq!(actual.out, "no!"); 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] #[test]
fn const_glob_type() { fn const_glob_type() {
let actual = nu!("const x: glob = 'aa'; $x | describe"); let actual = nu!("const x: glob = 'aa'; $x | describe");