differentiating between --x and --x: bool (#10456)

# Description
Fixes: #10450 

This pr differentiating between `--x: bool` and `--x`

Here are examples which demostrate difference between them:
```nushell
def a [--x: bool] { $x };
a --x    # not allowed, you need to parse a value to the flag.
a        # it's allowed, and the value of `$x` is false, which behaves the same to `def a [--x] { $x }; a`
```

For boolean flag with default value, it works a little bit different to
#10450 mentioned:
```nushell
def foo [--option: bool = false] { $option }
foo                  # output false
foo --option         # not allowed, you need to parse a value to the flag.
foo --option true    # output true
```

# User-Facing Changes
After the pr, the following code is not allowed:
```nushell
def a [--x: bool] { $x }; a --x
```

Instead, you have to pass a value to flag `--x` like `a --x false`. But
bare flag works in the same way as before.

## Update: one more breaking change to help on #7260 
```
def foo [--option: bool] { $option == null }
foo
```
After the pr, if we don't use a boolean flag, the value will be `null`
instead of `true`. Because here `--option: bool` is treated as a flag
rather than a switch

---------

Co-authored-by: amtoine <stevan.antoine@gmail.com>
This commit is contained in:
WindSoilder 2023-09-23 16:20:48 +08:00 committed by GitHub
parent a26a01c8d0
commit d2c87ad4b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 51 additions and 60 deletions

View File

@ -180,3 +180,16 @@ fn def_default_value_should_restrict_implicit_type() {
let actual2 = nu!("def foo2 [--x = 3] { $x }; foo2 --x 3.0"); let actual2 = nu!("def foo2 [--x = 3] { $x }; foo2 --x 3.0");
assert!(actual2.err.contains("expected int")); assert!(actual2.err.contains("expected int"));
} }
#[test]
fn def_boolean_flags() {
let actual = nu!("def foo [--x: bool] { $x }; foo --x");
assert!(actual.err.contains("flag missing bool argument"));
let actual = nu!("def foo [--x: bool = false] { $x }; foo");
assert_eq!(actual.out, "false");
let actual = nu!("def foo [--x: bool = false] { $x }; foo --x");
assert!(actual.err.contains("flag missing bool argument"));
// boolean flags' default value should be null
let actual = nu!("def foo [--x: bool] { $x == null }; foo");
assert_eq!(actual.out, "true");
}

View File

@ -3720,11 +3720,8 @@ pub fn parse_signature_helper(working_set: &mut StateWorkingSet, span: Span) ->
*shape = syntax_shape; *shape = syntax_shape;
} }
Arg::Flag(Flag { arg, var_id, .. }) => { Arg::Flag(Flag { arg, var_id, .. }) => {
// Flags with a boolean type are just present/not-present switches working_set.set_variable_type(var_id.expect("internal error: all custom parameters must have var_ids"), syntax_shape.to_type());
if syntax_shape != SyntaxShape::Boolean { *arg = Some(syntax_shape);
working_set.set_variable_type(var_id.expect("internal error: all custom parameters must have var_ids"), syntax_shape.to_type());
*arg = Some(syntax_shape)
}
} }
} }
arg_explicit_type = true; arg_explicit_type = true;
@ -3818,31 +3815,28 @@ pub fn parse_signature_helper(working_set: &mut StateWorkingSet, span: Span) ->
let var_type = &working_set.get_variable(var_id).ty; let var_type = &working_set.get_variable(var_id).ty;
let expression_ty = expression.ty.clone(); let expression_ty = expression.ty.clone();
// Flags with a boolean type are just present/not-present switches // Flags with no TypeMode are just present/not-present switches
if var_type != &Type::Bool { // in the case, `var_type` is any.
match var_type { match var_type {
Type::Any => { Type::Any => {
if !arg_explicit_type { if !arg_explicit_type {
*arg = Some(expression_ty.to_shape()); *arg = Some(expression_ty.to_shape());
working_set.set_variable_type( working_set
var_id, .set_variable_type(var_id, expression_ty);
expression_ty,
);
}
} }
t => { }
if t != &expression_ty { t => {
working_set.error( if t != &expression_ty {
ParseError::AssignmentMismatch( working_set.error(
"Default value is the wrong type" ParseError::AssignmentMismatch(
.into(), "Default value is the wrong type"
format!( .into(),
format!(
"expected default value to be `{t}`" "expected default value to be `{t}`"
), ),
expression_span, expression_span,
), ),
) )
}
} }
} }
} }

View File

@ -100,7 +100,7 @@ def "nu-complete list-externs" [] {
def build-help-header [ def build-help-header [
text: string text: string
--no-newline (-n): bool --no-newline (-n)
] { ] {
let header = $"(ansi green)($text)(ansi reset):" let header = $"(ansi green)($text)(ansi reset):"

View File

@ -67,22 +67,6 @@ fn do_rest_args() -> TestResult {
#[test] #[test]
fn custom_switch1() -> TestResult { fn custom_switch1() -> TestResult {
run_test(
r#"def florb [ --dry-run: bool ] { if ($dry_run) { "foo" } else { "bar" } }; florb --dry-run"#,
"foo",
)
}
#[test]
fn custom_switch2() -> TestResult {
run_test(
r#"def florb [ --dry-run: bool ] { if ($dry_run) { "foo" } else { "bar" } }; florb"#,
"bar",
)
}
#[test]
fn custom_switch3() -> TestResult {
run_test( run_test(
r#"def florb [ --dry-run ] { if ($dry_run) { "foo" } else { "bar" } }; florb --dry-run"#, r#"def florb [ --dry-run ] { if ($dry_run) { "foo" } else { "bar" } }; florb --dry-run"#,
"foo", "foo",
@ -90,7 +74,7 @@ fn custom_switch3() -> TestResult {
} }
#[test] #[test]
fn custom_switch4() -> TestResult { fn custom_switch2() -> TestResult {
run_test( run_test(
r#"def florb [ --dry-run ] { if ($dry_run) { "foo" } else { "bar" } }; florb"#, r#"def florb [ --dry-run ] { if ($dry_run) { "foo" } else { "bar" } }; florb"#,
"bar", "bar",

View File

@ -440,7 +440,7 @@ fn string_escape_interpolation2() -> TestResult {
#[test] #[test]
fn proper_rest_types() -> TestResult { fn proper_rest_types() -> TestResult {
run_test( run_test(
r#"def foo [--verbose(-v): bool, # my test flag r#"def foo [--verbose(-v), # my test flag
...rest: int # my rest comment ...rest: int # my rest comment
] { if $verbose { print "verbose!" } else { print "not verbose!" } }; foo"#, ] { if $verbose { print "verbose!" } else { print "not verbose!" } }; foo"#,
"not verbose!", "not verbose!",

View File

@ -8,8 +8,8 @@
# check standard code formatting and apply the changes # check standard code formatting and apply the changes
export def fmt [ export def fmt [
--check: bool # do not apply the format changes, only check the syntax --check # do not apply the format changes, only check the syntax
--verbose: bool # print extra information about the command's progress --verbose # print extra information about the command's progress
] { ] {
if $verbose { if $verbose {
print $"running ('toolkit fmt' | pretty-format-command)" print $"running ('toolkit fmt' | pretty-format-command)"
@ -32,9 +32,9 @@ export def fmt [
# #
# > it is important to make `clippy` happy :relieved: # > it is important to make `clippy` happy :relieved:
export def clippy [ export def clippy [
--verbose: bool # print extra information about the command's progress --verbose # print extra information about the command's progress
--features: list<string> # the list of features to run *Clippy* on --features: list<string> # the list of features to run *Clippy* on
--workspace: bool # run the *Clippy* command on the whole workspace (overrides `--features`) --workspace # run the *Clippy* command on the whole workspace (overrides `--features`)
] { ] {
if $verbose { if $verbose {
print $"running ('toolkit clippy' | pretty-format-command)" print $"running ('toolkit clippy' | pretty-format-command)"
@ -63,9 +63,9 @@ export def clippy [
# check that all the tests pass # check that all the tests pass
export def test [ export def test [
--fast: bool # use the "nextext" `cargo` subcommand to speed up the tests (see [`cargo-nextest`](https://nexte.st/) and [`nextest-rs/nextest`](https://github.com/nextest-rs/nextest)) --fast # use the "nextext" `cargo` subcommand to speed up the tests (see [`cargo-nextest`](https://nexte.st/) and [`nextest-rs/nextest`](https://github.com/nextest-rs/nextest))
--features: list<string> # the list of features to run the tests on --features: list<string> # the list of features to run the tests on
--workspace: bool # run the *Clippy* command on the whole workspace (overrides `--features`) --workspace # run the *Clippy* command on the whole workspace (overrides `--features`)
] { ] {
if $fast { if $fast {
if $workspace { if $workspace {
@ -104,11 +104,11 @@ def pretty-format-command [] {
# otherwise, the truth values will be incremental, following # otherwise, the truth values will be incremental, following
# the order above. # the order above.
def report [ def report [
--fail-fmt: bool --fail-fmt
--fail-clippy: bool --fail-clippy
--fail-test: bool --fail-test
--fail-test-stdlib: bool --fail-test-stdlib
--no-fail: bool --no-fail
] { ] {
[fmt clippy test "test stdlib"] [fmt clippy test "test stdlib"]
| wrap stage | wrap stage
@ -227,7 +227,7 @@ def report [
# #
# now the whole `toolkit check pr` passes! :tada: # now the whole `toolkit check pr` passes! :tada:
export def "check pr" [ export def "check pr" [
--fast: bool # use the "nextext" `cargo` subcommand to speed up the tests (see [`cargo-nextest`](https://nexte.st/) and [`nextest-rs/nextest`](https://github.com/nextest-rs/nextest)) --fast # use the "nextext" `cargo` subcommand to speed up the tests (see [`cargo-nextest`](https://nexte.st/) and [`nextest-rs/nextest`](https://github.com/nextest-rs/nextest))
--features: list<string> # the list of features to check the current PR on --features: list<string> # the list of features to check the current PR on
] { ] {
$env.NU_TEST_LOCALE_OVERRIDE = 'en_US.utf8'; $env.NU_TEST_LOCALE_OVERRIDE = 'en_US.utf8';
@ -297,7 +297,7 @@ def build-plugin [] {
# build Nushell and plugins with some features # build Nushell and plugins with some features
export def build [ export def build [
...features: string@"nu-complete list features" # a space-separated list of feature to install with Nushell ...features: string@"nu-complete list features" # a space-separated list of feature to install with Nushell
--all: bool # build all plugins with Nushell --all # build all plugins with Nushell
] { ] {
build-nushell ($features | str join ",") build-nushell ($features | str join ",")
@ -335,7 +335,7 @@ def install-plugin [] {
# install Nushell and features you want # install Nushell and features you want
export def install [ export def install [
...features: string@"nu-complete list features" # a space-separated list of feature to install with Nushell ...features: string@"nu-complete list features" # a space-separated list of feature to install with Nushell
--all: bool # install all plugins with Nushell --all # install all plugins with Nushell
] { ] {
touch crates/nu-cmd-lang/build.rs # needed to make sure `version` has the correct `commit_hash` touch crates/nu-cmd-lang/build.rs # needed to make sure `version` has the correct `commit_hash`
cargo install --path . --features ($features | str join ",") --locked --force cargo install --path . --features ($features | str join ",") --locked --force