mirror of
https://github.com/nushell/nushell
synced 2024-12-26 04:53:09 +00:00
disallow blocks as first-class values (#9587)
# Description This PR disallows blocks as first-class values by removing the ability to create them using the `block` syntax shape or type. Now, the parser will only ever be able to create closures as first-class values. This means that `{ 3 }` will always be treated as a closure, unless used in the specifically supported use case of the literal being given as an arg to `for`, `if` and other built-in block users. Note: first-class value here means "value that can be passed into commands and held in variables" # User-Facing Changes This may break some user scripts as `: block` is no longer allows as a type annotation. Note: these cases were not actually supported before, as, for example, passing a block that accessed a variable would have errored when the block was later evaluated. Closures do not have the restriction mentioned above and are the much safer value to pass as first-class, so now they are the only block-like value to be allowed to be passed. # Tests + Formatting <!-- Don't forget to add tests that cover your changes. Make sure you've run and fixed any issues with these commands: - `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes) - `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect -A clippy::result_large_err` to check that you're using the standard code style - `cargo test --workspace` to check that all tests pass - `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the standard library > **Note** > from `nushell` you can also use the `toolkit` as follows > ```bash > use toolkit.nu # or use an `env_change` hook to activate it automatically > toolkit check pr > ``` --> # After Submitting <!-- If your PR had any user-facing changes, update [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged, if necessary. This will help us keep the docs up to date. -->
This commit is contained in:
parent
4acf21cdcf
commit
b70cce47e2
2 changed files with 40 additions and 7 deletions
|
@ -2687,7 +2687,15 @@ pub fn parse_shape_name(
|
|||
let result = match bytes {
|
||||
b"any" => SyntaxShape::Any,
|
||||
b"binary" => SyntaxShape::Binary,
|
||||
b"block" => SyntaxShape::Block, //FIXME: Blocks should have known output types
|
||||
b"block" => {
|
||||
working_set.error(ParseError::LabeledErrorWithHelp {
|
||||
error: "Blocks are not support as first-class values".into(),
|
||||
label: "blocks are not supported as values".into(),
|
||||
help: "Use 'closure' instead of 'block'".into(),
|
||||
span,
|
||||
});
|
||||
SyntaxShape::Any
|
||||
}
|
||||
b"bool" => SyntaxShape::Boolean,
|
||||
b"cell-path" => SyntaxShape::CellPath,
|
||||
b"closure" => SyntaxShape::Closure(None), //FIXME: Blocks should have known output types
|
||||
|
@ -2919,10 +2927,19 @@ fn prepare_inner_span(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn parse_type(_working_set: &StateWorkingSet, bytes: &[u8]) -> Type {
|
||||
pub fn parse_type(working_set: &mut StateWorkingSet, bytes: &[u8], span: Span) -> Type {
|
||||
match bytes {
|
||||
b"binary" => Type::Binary,
|
||||
b"block" => Type::Block,
|
||||
b"block" => {
|
||||
working_set.error(ParseError::LabeledErrorWithHelp {
|
||||
error: "Blocks are not support as first-class values".into(),
|
||||
label: "blocks are not supported as values".into(),
|
||||
help: "Use 'closure' instead of 'block'".into(),
|
||||
span,
|
||||
});
|
||||
|
||||
Type::Any
|
||||
}
|
||||
b"bool" => Type::Bool,
|
||||
b"cellpath" => Type::CellPath,
|
||||
b"closure" => Type::Closure,
|
||||
|
@ -3080,18 +3097,18 @@ pub fn parse_var_with_opt_type(
|
|||
// We end with colon, so the next span should be the type
|
||||
if *spans_idx + 1 < spans.len() {
|
||||
*spans_idx += 1;
|
||||
let type_bytes = working_set.get_span_contents(spans[*spans_idx]);
|
||||
let type_bytes = working_set.get_span_contents(spans[*spans_idx]).to_vec();
|
||||
|
||||
let ty = parse_type(working_set, type_bytes);
|
||||
let ty = parse_type(working_set, &type_bytes, spans[*spans_idx]);
|
||||
|
||||
let var_name = bytes[0..(bytes.len() - 1)].to_vec();
|
||||
|
||||
if !is_variable(&var_name) {
|
||||
working_set.error(ParseError::Expected(
|
||||
"valid variable name",
|
||||
spans[*spans_idx],
|
||||
spans[*spans_idx - 1],
|
||||
));
|
||||
return (garbage(spans[*spans_idx]), None);
|
||||
return (garbage(spans[*spans_idx - 1]), None);
|
||||
}
|
||||
|
||||
let id = working_set.add_variable(var_name, spans[*spans_idx - 1], ty.clone(), mutable);
|
||||
|
|
|
@ -43,3 +43,19 @@ fn date_plus_duration() -> TestResult {
|
|||
let expected = "2023-04-20";
|
||||
run_test(input, expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_not_first_class_def() -> TestResult {
|
||||
fail_test(
|
||||
"def foo [x: block] { do $x }",
|
||||
"Blocks are not support as first-class values",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_not_first_class_let() -> TestResult {
|
||||
fail_test(
|
||||
"let x: block = { 3 }",
|
||||
"Blocks are not support as first-class values",
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue