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> {
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()? {

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
Type::Any
| Type::Block
| Type::CellPath
| Type::Closure
| Type::Custom(_)

View File

@ -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

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));
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 {

View File

@ -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)) => {

View File

@ -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,

View File

@ -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"),

View File

@ -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");