add type check during eval time (#11475)

# Description
Fixes: #11438 

Take the following as example:
```nushell
def spam [foo: string] {
    $'foo: ($foo | describe)'
}
def outer [--foo: string] {
    spam $foo
}

outer
```
When we call `outer`, type checker only check the all for `outer`, but
doesn't check inside the body of `outer`. This pr is trying to introduce
a type checking process through `Type::is_subtype()` during eval time.

## NOTE
I'm not really sure if it's easy to make a check inside the body of
`outer`. Adding an eval time type checker seems like an easier solution.
As a result: `outer` will be caught by runtime, not parse time type
checker

cc @kubouch 

# User-Facing Changes
After this pr the following call will failed:
```nushell
> outer
Error: nu:🐚:cant_convert

  × Can't convert to string.
   ╭─[entry #27:1:1]
 1 │ def outer [--foo: any] {
 2 │     spam $foo
   ·          ──┬─
   ·            ╰── can't convert nothing to string
 3 │ }
   ╰────
```

# Tests + Formatting
Done

# After Submitting
NaN
This commit is contained in:
WindSoilder
2024-01-12 23:48:53 +08:00
committed by GitHub
parent 8cad12a05c
commit 724818030d
5 changed files with 47 additions and 5 deletions

View File

@ -42,11 +42,17 @@ pub fn eval_call(
let mut callee_stack = caller_stack.gather_captures(engine_state, &block.captures);
for (param_idx, param) in decl
for (param_idx, (param, required)) in decl
.signature()
.required_positional
.iter()
.chain(decl.signature().optional_positional.iter())
.map(|p| (p, true))
.chain(
decl.signature()
.optional_positional
.iter()
.map(|p| (p, false)),
)
.enumerate()
{
let var_id = param
@ -55,6 +61,14 @@ pub fn eval_call(
if let Some(arg) = call.positional_nth(param_idx) {
let result = eval_expression(engine_state, caller_stack, arg)?;
if required && !result.get_type().is_subtype(&param.shape.to_type()) {
return Err(ShellError::CantConvert {
to_type: param.shape.to_type().to_string(),
from_type: result.get_type().to_string(),
span: result.span(),
help: None,
});
}
callee_stack.add_var(var_id, result);
} else if let Some(value) = &param.default_value {
callee_stack.add_var(var_id, value.to_owned());