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
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");
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;
}
Arg::Flag(Flag { arg, var_id, .. }) => {
// Flags with a boolean type are just present/not-present switches
if syntax_shape != SyntaxShape::Boolean {
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)
}
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;
@ -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 expression_ty = expression.ty.clone();
// Flags with a boolean type are just present/not-present switches
if var_type != &Type::Bool {
match var_type {
Type::Any => {
if !arg_explicit_type {
*arg = Some(expression_ty.to_shape());
working_set.set_variable_type(
var_id,
expression_ty,
);
}
// Flags with no TypeMode are just present/not-present switches
// in the case, `var_type` is any.
match var_type {
Type::Any => {
if !arg_explicit_type {
*arg = Some(expression_ty.to_shape());
working_set
.set_variable_type(var_id, expression_ty);
}
t => {
if t != &expression_ty {
working_set.error(
ParseError::AssignmentMismatch(
"Default value is the wrong type"
.into(),
format!(
}
t => {
if t != &expression_ty {
working_set.error(
ParseError::AssignmentMismatch(
"Default value is the wrong type"
.into(),
format!(
"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 [
text: string
--no-newline (-n): bool
--no-newline (-n)
] {
let header = $"(ansi green)($text)(ansi reset):"