From 786ba3bf91f94c7c9f02762847c04aec54dacae5 Mon Sep 17 00:00:00 2001 From: JT <547158+jntrnr@users.noreply.github.com> Date: Fri, 14 Jul 2023 15:20:35 +1200 Subject: [PATCH] Input output checking (#9680) # Description This PR tights input/output type-checking a bit more. There are a lot of commands that don't have correct input/output types, so part of the effort is updating them. This PR now contains updates to commands that had wrong input/output signatures. It doesn't add examples for these new signatures, but that can be follow-up work. # User-Facing Changes BREAKING CHANGE BREAKING CHANGE This work enforces many more checks on pipeline type correctness than previous nushell versions. This strictness may uncover incompatibilities in existing scripts or shortcomings in the type information for internal commands. # Tests + Formatting # After Submitting --- crates/nu-cmd-extra/src/extra/bits/and.rs | 8 +- crates/nu-cmd-extra/src/extra/bits/not.rs | 8 +- crates/nu-cmd-extra/src/extra/bits/or.rs | 8 +- .../src/extra/bits/rotate_left.rs | 8 +- .../src/extra/bits/rotate_right.rs | 8 +- .../nu-cmd-extra/src/extra/bits/shift_left.rs | 8 +- .../src/extra/bits/shift_right.rs | 8 +- crates/nu-cmd-extra/src/extra/bits/xor.rs | 8 +- crates/nu-cmd-extra/src/extra/bytes/length.rs | 8 +- crates/nu-cmd-extra/src/extra/bytes/remove.rs | 5 +- .../nu-cmd-extra/src/extra/bytes/replace.rs | 5 +- crates/nu-cmd-extra/src/extra/math/cos.rs | 8 +- crates/nu-cmd-extra/src/extra/math/sin.rs | 8 +- crates/nu-cmd-extra/src/extra/math/tan.rs | 8 +- .../tests/commands/bytes/starts_with.rs | 2 +- .../nu-cmd-lang/src/core_commands/collect.rs | 2 +- .../src/conversions/into/decimal.rs | 6 + crates/nu-command/src/conversions/into/int.rs | 7 + .../nu-command/src/conversions/into/string.rs | 4 + crates/nu-command/src/env/load_env.rs | 5 +- crates/nu-command/src/filters/append.rs | 12 +- crates/nu-command/src/filters/each.rs | 2 + crates/nu-command/src/filters/find.rs | 2 +- crates/nu-command/src/filters/first.rs | 2 + crates/nu-command/src/filters/get.rs | 2 + crates/nu-command/src/filters/insert.rs | 5 + crates/nu-command/src/filters/select.rs | 2 + crates/nu-command/src/filters/sort_by.rs | 9 +- crates/nu-command/src/filters/transpose.rs | 2 + crates/nu-command/src/filters/uniq_by.rs | 9 +- crates/nu-command/src/filters/update.rs | 5 + crates/nu-command/src/filters/upsert.rs | 5 + crates/nu-command/src/filters/where_.rs | 2 + crates/nu-command/src/filters/wrap.rs | 2 + crates/nu-command/src/math/abs.rs | 9 +- crates/nu-command/src/math/avg.rs | 7 +- crates/nu-command/src/math/ceil.rs | 9 +- crates/nu-command/src/math/floor.rs | 9 +- crates/nu-command/src/math/log.rs | 10 +- crates/nu-command/src/math/max.rs | 1 + crates/nu-command/src/math/median.rs | 1 + crates/nu-command/src/math/min.rs | 1 + crates/nu-command/src/math/mode.rs | 1 + crates/nu-command/src/math/round.rs | 8 +- crates/nu-command/src/math/sqrt.rs | 9 +- crates/nu-command/src/math/sum.rs | 7 +- crates/nu-command/src/network/url/encode.rs | 2 +- crates/nu-command/src/path/join.rs | 2 + crates/nu-command/src/platform/ansi/strip.rs | 3 +- crates/nu-command/src/strings/parse.rs | 6 +- crates/nu-command/src/strings/split/row.rs | 6 +- .../src/strings/str_/case/camel_case.rs | 5 +- .../src/strings/str_/case/capitalize.rs | 5 +- .../src/strings/str_/case/downcase.rs | 5 +- .../src/strings/str_/case/kebab_case.rs | 5 +- .../src/strings/str_/case/pascal_case.rs | 5 +- .../strings/str_/case/screaming_snake_case.rs | 5 +- .../src/strings/str_/case/snake_case.rs | 5 +- .../src/strings/str_/case/title_case.rs | 5 +- .../src/strings/str_/case/upcase.rs | 6 +- .../nu-command/src/strings/str_/contains.rs | 1 + crates/nu-command/src/strings/str_/join.rs | 6 +- crates/nu-command/src/strings/str_/length.rs | 2 +- crates/nu-command/src/strings/str_/replace.rs | 5 +- crates/nu-command/src/strings/str_/reverse.rs | 8 +- .../nu-command/src/strings/str_/substring.rs | 3 +- .../nu-command/src/strings/str_/trim/trim_.rs | 10 +- crates/nu-command/tests/commands/drop.rs | 2 +- crates/nu-command/tests/commands/last.rs | 2 +- .../nu-command/tests/commands/math/round.rs | 2 +- crates/nu-command/tests/commands/mkdir.rs | 1 - crates/nu-command/tests/commands/rename.rs | 2 +- crates/nu-command/tests/commands/reverse.rs | 2 +- .../nu-command/tests/commands/skip/skip_.rs | 2 +- .../nu-command/tests/commands/skip/until.rs | 2 +- .../nu-command/tests/commands/skip/while_.rs | 2 +- crates/nu-command/tests/commands/sort_by.rs | 2 +- crates/nu-command/tests/commands/take/rows.rs | 2 +- .../nu-command/tests/commands/take/until.rs | 2 +- .../nu-command/tests/commands/take/while_.rs | 2 +- crates/nu-command/tests/commands/where_.rs | 2 +- .../tests/format_conversions/csv.rs | 2 +- .../tests/format_conversions/toml.rs | 4 +- crates/nu-parser/src/parser.rs | 4 +- crates/nu-parser/src/type_check.rs | 135 +++++++++++++++++- crates/nu-protocol/src/ast/block.rs | 35 +---- crates/nu-protocol/src/parse_error.rs | 10 ++ src/tests/test_bits.rs | 2 +- src/tests/test_engine.rs | 2 +- 89 files changed, 480 insertions(+), 106 deletions(-) diff --git a/crates/nu-cmd-extra/src/extra/bits/and.rs b/crates/nu-cmd-extra/src/extra/bits/and.rs index e63b6f23e6..104cbe7de6 100644 --- a/crates/nu-cmd-extra/src/extra/bits/and.rs +++ b/crates/nu-cmd-extra/src/extra/bits/and.rs @@ -15,7 +15,13 @@ impl Command for BitsAnd { fn signature(&self) -> Signature { Signature::build("bits and") - .input_output_types(vec![(Type::Int, Type::Int)]) + .input_output_types(vec![ + (Type::Int, Type::Int), + ( + Type::List(Box::new(Type::Int)), + Type::List(Box::new(Type::Int)), + ), + ]) .vectorizes_over_list(true) .required( "target", diff --git a/crates/nu-cmd-extra/src/extra/bits/not.rs b/crates/nu-cmd-extra/src/extra/bits/not.rs index 4948a1c4a1..3e4e72529c 100644 --- a/crates/nu-cmd-extra/src/extra/bits/not.rs +++ b/crates/nu-cmd-extra/src/extra/bits/not.rs @@ -16,7 +16,13 @@ impl Command for BitsNot { fn signature(&self) -> Signature { Signature::build("bits not") - .input_output_types(vec![(Type::Int, Type::Int)]) + .input_output_types(vec![ + (Type::Int, Type::Int), + ( + Type::List(Box::new(Type::Int)), + Type::List(Box::new(Type::Int)), + ), + ]) .vectorizes_over_list(true) .switch( "signed", diff --git a/crates/nu-cmd-extra/src/extra/bits/or.rs b/crates/nu-cmd-extra/src/extra/bits/or.rs index 1c3c7243de..41110e17cd 100644 --- a/crates/nu-cmd-extra/src/extra/bits/or.rs +++ b/crates/nu-cmd-extra/src/extra/bits/or.rs @@ -15,7 +15,13 @@ impl Command for BitsOr { fn signature(&self) -> Signature { Signature::build("bits or") - .input_output_types(vec![(Type::Int, Type::Int)]) + .input_output_types(vec![ + (Type::Int, Type::Int), + ( + Type::List(Box::new(Type::Int)), + Type::List(Box::new(Type::Int)), + ), + ]) .vectorizes_over_list(true) .required( "target", diff --git a/crates/nu-cmd-extra/src/extra/bits/rotate_left.rs b/crates/nu-cmd-extra/src/extra/bits/rotate_left.rs index d299dd6dcc..363c010428 100644 --- a/crates/nu-cmd-extra/src/extra/bits/rotate_left.rs +++ b/crates/nu-cmd-extra/src/extra/bits/rotate_left.rs @@ -18,7 +18,13 @@ impl Command for BitsRol { fn signature(&self) -> Signature { Signature::build("bits rol") - .input_output_types(vec![(Type::Int, Type::Int)]) + .input_output_types(vec![ + (Type::Int, Type::Int), + ( + Type::List(Box::new(Type::Int)), + Type::List(Box::new(Type::Int)), + ), + ]) .vectorizes_over_list(true) .required("bits", SyntaxShape::Int, "number of bits to rotate left") .switch( diff --git a/crates/nu-cmd-extra/src/extra/bits/rotate_right.rs b/crates/nu-cmd-extra/src/extra/bits/rotate_right.rs index 9e521a30e7..c9f4f9ce9f 100644 --- a/crates/nu-cmd-extra/src/extra/bits/rotate_right.rs +++ b/crates/nu-cmd-extra/src/extra/bits/rotate_right.rs @@ -18,7 +18,13 @@ impl Command for BitsRor { fn signature(&self) -> Signature { Signature::build("bits ror") - .input_output_types(vec![(Type::Int, Type::Int)]) + .input_output_types(vec![ + (Type::Int, Type::Int), + ( + Type::List(Box::new(Type::Int)), + Type::List(Box::new(Type::Int)), + ), + ]) .vectorizes_over_list(true) .required("bits", SyntaxShape::Int, "number of bits to rotate right") .switch( diff --git a/crates/nu-cmd-extra/src/extra/bits/shift_left.rs b/crates/nu-cmd-extra/src/extra/bits/shift_left.rs index 802b29a04f..05289b282f 100644 --- a/crates/nu-cmd-extra/src/extra/bits/shift_left.rs +++ b/crates/nu-cmd-extra/src/extra/bits/shift_left.rs @@ -18,7 +18,13 @@ impl Command for BitsShl { fn signature(&self) -> Signature { Signature::build("bits shl") - .input_output_types(vec![(Type::Int, Type::Int)]) + .input_output_types(vec![ + (Type::Int, Type::Int), + ( + Type::List(Box::new(Type::Int)), + Type::List(Box::new(Type::Int)), + ), + ]) .vectorizes_over_list(true) .required("bits", SyntaxShape::Int, "number of bits to shift left") .switch( diff --git a/crates/nu-cmd-extra/src/extra/bits/shift_right.rs b/crates/nu-cmd-extra/src/extra/bits/shift_right.rs index f780ee6672..f94d81829d 100644 --- a/crates/nu-cmd-extra/src/extra/bits/shift_right.rs +++ b/crates/nu-cmd-extra/src/extra/bits/shift_right.rs @@ -18,7 +18,13 @@ impl Command for BitsShr { fn signature(&self) -> Signature { Signature::build("bits shr") - .input_output_types(vec![(Type::Int, Type::Int)]) + .input_output_types(vec![ + (Type::Int, Type::Int), + ( + Type::List(Box::new(Type::Int)), + Type::List(Box::new(Type::Int)), + ), + ]) .vectorizes_over_list(true) .required("bits", SyntaxShape::Int, "number of bits to shift right") .switch( diff --git a/crates/nu-cmd-extra/src/extra/bits/xor.rs b/crates/nu-cmd-extra/src/extra/bits/xor.rs index 691ea69079..c40d5ea587 100644 --- a/crates/nu-cmd-extra/src/extra/bits/xor.rs +++ b/crates/nu-cmd-extra/src/extra/bits/xor.rs @@ -15,7 +15,13 @@ impl Command for BitsXor { fn signature(&self) -> Signature { Signature::build("bits xor") - .input_output_types(vec![(Type::Int, Type::Int)]) + .input_output_types(vec![ + (Type::Int, Type::Int), + ( + Type::List(Box::new(Type::Int)), + Type::List(Box::new(Type::Int)), + ), + ]) .vectorizes_over_list(true) .required( "target", diff --git a/crates/nu-cmd-extra/src/extra/bytes/length.rs b/crates/nu-cmd-extra/src/extra/bytes/length.rs index bab8550a53..3f8caaf721 100644 --- a/crates/nu-cmd-extra/src/extra/bytes/length.rs +++ b/crates/nu-cmd-extra/src/extra/bytes/length.rs @@ -16,7 +16,13 @@ impl Command for BytesLen { fn signature(&self) -> Signature { Signature::build("bytes length") - .input_output_types(vec![(Type::Binary, Type::Int)]) + .input_output_types(vec![ + (Type::Binary, Type::Int), + ( + Type::List(Box::new(Type::Binary)), + Type::List(Box::new(Type::Int)), + ), + ]) .vectorizes_over_list(true) .rest( "rest", diff --git a/crates/nu-cmd-extra/src/extra/bytes/remove.rs b/crates/nu-cmd-extra/src/extra/bytes/remove.rs index 8780d14ece..8db1af62b5 100644 --- a/crates/nu-cmd-extra/src/extra/bytes/remove.rs +++ b/crates/nu-cmd-extra/src/extra/bytes/remove.rs @@ -30,7 +30,10 @@ impl Command for BytesRemove { fn signature(&self) -> Signature { Signature::build("bytes remove") - .input_output_types(vec![(Type::Binary, Type::Binary)]) + .input_output_types(vec![ + (Type::Binary, Type::Binary), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .required("pattern", SyntaxShape::Binary, "the pattern to find") .rest( "rest", diff --git a/crates/nu-cmd-extra/src/extra/bytes/replace.rs b/crates/nu-cmd-extra/src/extra/bytes/replace.rs index dc12d7e344..2370d13eb1 100644 --- a/crates/nu-cmd-extra/src/extra/bytes/replace.rs +++ b/crates/nu-cmd-extra/src/extra/bytes/replace.rs @@ -30,7 +30,10 @@ impl Command for BytesReplace { fn signature(&self) -> Signature { Signature::build("bytes replace") - .input_output_types(vec![(Type::Binary, Type::Binary)]) + .input_output_types(vec![ + (Type::Binary, Type::Binary), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .required("find", SyntaxShape::Binary, "the pattern to find") .required("replace", SyntaxShape::Binary, "the replacement pattern") .rest( diff --git a/crates/nu-cmd-extra/src/extra/math/cos.rs b/crates/nu-cmd-extra/src/extra/math/cos.rs index d2b94b7fd9..96486431c8 100644 --- a/crates/nu-cmd-extra/src/extra/math/cos.rs +++ b/crates/nu-cmd-extra/src/extra/math/cos.rs @@ -13,7 +13,13 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("math cos") .switch("degrees", "Use degrees instead of radians", Some('d')) - .input_output_types(vec![(Type::Number, Type::Float)]) + .input_output_types(vec![ + (Type::Number, Type::Float), + ( + Type::List(Box::new(Type::Number)), + Type::List(Box::new(Type::Float)), + ), + ]) .vectorizes_over_list(true) .category(Category::Math) } diff --git a/crates/nu-cmd-extra/src/extra/math/sin.rs b/crates/nu-cmd-extra/src/extra/math/sin.rs index 0db2dbc6da..276163eb06 100644 --- a/crates/nu-cmd-extra/src/extra/math/sin.rs +++ b/crates/nu-cmd-extra/src/extra/math/sin.rs @@ -13,7 +13,13 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("math sin") .switch("degrees", "Use degrees instead of radians", Some('d')) - .input_output_types(vec![(Type::Number, Type::Float)]) + .input_output_types(vec![ + (Type::Number, Type::Float), + ( + Type::List(Box::new(Type::Number)), + Type::List(Box::new(Type::Float)), + ), + ]) .vectorizes_over_list(true) .category(Category::Math) } diff --git a/crates/nu-cmd-extra/src/extra/math/tan.rs b/crates/nu-cmd-extra/src/extra/math/tan.rs index a6b3a8e64c..65b8b61388 100644 --- a/crates/nu-cmd-extra/src/extra/math/tan.rs +++ b/crates/nu-cmd-extra/src/extra/math/tan.rs @@ -13,7 +13,13 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("math tan") .switch("degrees", "Use degrees instead of radians", Some('d')) - .input_output_types(vec![(Type::Number, Type::Float)]) + .input_output_types(vec![ + (Type::Number, Type::Float), + ( + Type::List(Box::new(Type::Number)), + Type::List(Box::new(Type::Float)), + ), + ]) .vectorizes_over_list(true) .category(Category::Math) } diff --git a/crates/nu-cmd-extra/tests/commands/bytes/starts_with.rs b/crates/nu-cmd-extra/tests/commands/bytes/starts_with.rs index 797d5766e7..1448c0decd 100644 --- a/crates/nu-cmd-extra/tests/commands/bytes/starts_with.rs +++ b/crates/nu-cmd-extra/tests/commands/bytes/starts_with.rs @@ -21,7 +21,7 @@ fn basic_string_fails() { "# ); - assert!(actual.err.contains("Input type not supported")); + assert!(actual.err.contains("command doesn't support")); assert_eq!(actual.out, ""); } diff --git a/crates/nu-cmd-lang/src/core_commands/collect.rs b/crates/nu-cmd-lang/src/core_commands/collect.rs index ee827512e4..f8415e841a 100644 --- a/crates/nu-cmd-lang/src/core_commands/collect.rs +++ b/crates/nu-cmd-lang/src/core_commands/collect.rs @@ -16,7 +16,7 @@ impl Command for Collect { fn signature(&self) -> Signature { Signature::build("collect") - .input_output_types(vec![(Type::List(Box::new(Type::Any)), Type::Any)]) + .input_output_types(vec![(Type::Any, Type::Any)]) .required( "closure", SyntaxShape::Closure(Some(vec![SyntaxShape::Any])), diff --git a/crates/nu-command/src/conversions/into/decimal.rs b/crates/nu-command/src/conversions/into/decimal.rs index 11ceb0a902..66b03bd2f9 100644 --- a/crates/nu-command/src/conversions/into/decimal.rs +++ b/crates/nu-command/src/conversions/into/decimal.rs @@ -17,15 +17,21 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("into decimal") .input_output_types(vec![ + (Type::Int, Type::Number), (Type::String, Type::Number), (Type::Bool, Type::Number), (Type::Table(vec![]), Type::Table(vec![])), + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Number)), + ), ]) .rest( "rest", SyntaxShape::CellPath, "for a data structure input, convert data at the given cell paths", ) + .allow_variants_without_examples(true) .category(Category::Conversions) } diff --git a/crates/nu-command/src/conversions/into/int.rs b/crates/nu-command/src/conversions/into/int.rs index 7e5c8e7388..0d3e9dbdce 100644 --- a/crates/nu-command/src/conversions/into/int.rs +++ b/crates/nu-command/src/conversions/into/int.rs @@ -36,10 +36,17 @@ impl Command for SubCommand { (Type::Bool, Type::Int), // Unix timestamp in nanoseconds (Type::Date, Type::Int), + (Type::Duration, Type::Int), // TODO: Users should do this by dividing a Filesize by a Filesize explicitly (Type::Filesize, Type::Int), + (Type::Table(vec![]), Type::Table(vec![])), + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Int)), + ), ]) .vectorizes_over_list(true) + .allow_variants_without_examples(true) .named("radix", SyntaxShape::Number, "radix of integer", Some('r')) .switch("little-endian", "use little-endian byte decoding", None) .rest( diff --git a/crates/nu-command/src/conversions/into/string.rs b/crates/nu-command/src/conversions/into/string.rs index 8ede3f300f..699c74f8c8 100644 --- a/crates/nu-command/src/conversions/into/string.rs +++ b/crates/nu-command/src/conversions/into/string.rs @@ -40,6 +40,10 @@ impl Command for SubCommand { (Type::Bool, Type::String), (Type::Filesize, Type::String), (Type::Date, Type::String), + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::String)), + ), ]) .allow_variants_without_examples(true) // https://github.com/nushell/nushell/issues/7032 .rest( diff --git a/crates/nu-command/src/env/load_env.rs b/crates/nu-command/src/env/load_env.rs index 0a84fa100d..9f1102903a 100644 --- a/crates/nu-command/src/env/load_env.rs +++ b/crates/nu-command/src/env/load_env.rs @@ -19,7 +19,10 @@ impl Command for LoadEnv { fn signature(&self) -> nu_protocol::Signature { Signature::build("load-env") - .input_output_types(vec![(Type::Record(vec![]), Type::Nothing)]) + .input_output_types(vec![ + (Type::Record(vec![]), Type::Nothing), + (Type::Nothing, Type::Nothing), + ]) .allow_variants_without_examples(true) .optional( "update", diff --git a/crates/nu-command/src/filters/append.rs b/crates/nu-command/src/filters/append.rs index d7b529e300..d5af53b7d1 100644 --- a/crates/nu-command/src/filters/append.rs +++ b/crates/nu-command/src/filters/append.rs @@ -16,11 +16,15 @@ impl Command for Append { fn signature(&self) -> nu_protocol::Signature { Signature::build("append") - .input_output_types(vec![( - Type::List(Box::new(Type::Any)), - Type::List(Box::new(Type::Any)), - )]) + .input_output_types(vec![ + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + ), + (Type::Record(vec![]), Type::Table(vec![])), + ]) .required("row", SyntaxShape::Any, "the row, list, or table to append") + .allow_variants_without_examples(true) .category(Category::Filters) } diff --git a/crates/nu-command/src/filters/each.rs b/crates/nu-command/src/filters/each.rs index d627b2b018..02043c05c4 100644 --- a/crates/nu-command/src/filters/each.rs +++ b/crates/nu-command/src/filters/each.rs @@ -41,6 +41,7 @@ with 'transpose' first."# Type::List(Box::new(Type::Any)), ), (Type::Table(vec![]), Type::List(Box::new(Type::Any))), + (Type::Any, Type::Any), ]) .required( "closure", @@ -48,6 +49,7 @@ with 'transpose' first."# "the closure to run", ) .switch("keep-empty", "keep empty result cells", Some('k')) + .allow_variants_without_examples(true) .category(Category::Filters) } diff --git a/crates/nu-command/src/filters/find.rs b/crates/nu-command/src/filters/find.rs index 7a74221554..5123827a26 100644 --- a/crates/nu-command/src/filters/find.rs +++ b/crates/nu-command/src/filters/find.rs @@ -29,7 +29,7 @@ impl Command for Find { Type::List(Box::new(Type::Any)), Type::List(Box::new(Type::Any)), ), - (Type::String, Type::String), + (Type::String, Type::Any), ( // For find -p Type::Table(vec![]), diff --git a/crates/nu-command/src/filters/first.rs b/crates/nu-command/src/filters/first.rs index 153391509d..8a9660dac7 100644 --- a/crates/nu-command/src/filters/first.rs +++ b/crates/nu-command/src/filters/first.rs @@ -33,12 +33,14 @@ impl Command for First { Type::Any, ), (Type::Binary, Type::Binary), + (Type::Range, Type::Any), ]) .optional( "rows", SyntaxShape::Int, "starting from the front, the number of rows to return", ) + .allow_variants_without_examples(true) .category(Category::Filters) } diff --git a/crates/nu-command/src/filters/get.rs b/crates/nu-command/src/filters/get.rs index 1a5be9fb96..f9eb66e26e 100644 --- a/crates/nu-command/src/filters/get.rs +++ b/crates/nu-command/src/filters/get.rs @@ -34,6 +34,7 @@ If multiple cell paths are given, this will produce a list of values."# Type::Any, ), (Type::Table(vec![]), Type::Any), + (Type::Record(vec![]), Type::Any), ]) .required( "cell_path", @@ -51,6 +52,7 @@ If multiple cell paths are given, this will produce a list of values."# "get path in a case sensitive manner", Some('s'), ) + .allow_variants_without_examples(true) .category(Category::Filters) } diff --git a/crates/nu-command/src/filters/insert.rs b/crates/nu-command/src/filters/insert.rs index 43840cdb6a..14c4a65cad 100644 --- a/crates/nu-command/src/filters/insert.rs +++ b/crates/nu-command/src/filters/insert.rs @@ -19,6 +19,10 @@ impl Command for Insert { .input_output_types(vec![ (Type::Record(vec![]), Type::Record(vec![])), (Type::Table(vec![]), Type::Table(vec![])), + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + ), ]) .required( "field", @@ -30,6 +34,7 @@ impl Command for Insert { SyntaxShape::Any, "the new value to give the cell(s)", ) + .allow_variants_without_examples(true) .category(Category::Filters) } diff --git a/crates/nu-command/src/filters/select.rs b/crates/nu-command/src/filters/select.rs index d40d33709c..0828c3d209 100644 --- a/crates/nu-command/src/filters/select.rs +++ b/crates/nu-command/src/filters/select.rs @@ -21,6 +21,7 @@ impl Command for Select { .input_output_types(vec![ (Type::Record(vec![]), Type::Record(vec![])), (Type::Table(vec![]), Type::Table(vec![])), + (Type::List(Box::new(Type::Any)), Type::Any), ]) .switch( "ignore-errors", @@ -32,6 +33,7 @@ impl Command for Select { SyntaxShape::CellPath, "the columns to select from the table", ) + .allow_variants_without_examples(true) .category(Category::Filters) } diff --git a/crates/nu-command/src/filters/sort_by.rs b/crates/nu-command/src/filters/sort_by.rs index c606e03540..7e1b048300 100644 --- a/crates/nu-command/src/filters/sort_by.rs +++ b/crates/nu-command/src/filters/sort_by.rs @@ -16,7 +16,13 @@ impl Command for SortBy { fn signature(&self) -> nu_protocol::Signature { Signature::build("sort-by") - .input_output_types(vec![(Type::Table(vec![]), Type::Table(vec![]))]) + .input_output_types(vec![ + (Type::Table(vec![]), Type::Table(vec![])), + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + ), + ]) .rest("columns", SyntaxShape::Any, "the column(s) to sort by") .switch("reverse", "Sort in reverse order", Some('r')) .switch( @@ -29,6 +35,7 @@ impl Command for SortBy { "Sort alphanumeric string-based columns naturally (1, 9, 10, 99, 100, ...)", Some('n'), ) + .allow_variants_without_examples(true) .category(Category::Filters) } diff --git a/crates/nu-command/src/filters/transpose.rs b/crates/nu-command/src/filters/transpose.rs index 1092161a08..ede1b1c942 100644 --- a/crates/nu-command/src/filters/transpose.rs +++ b/crates/nu-command/src/filters/transpose.rs @@ -29,6 +29,7 @@ impl Command for Transpose { .input_output_types(vec![ (Type::Table(vec![]), Type::Table(vec![])), (Type::Table(vec![]), Type::Record(vec![])), + (Type::Record(vec![]), Type::Table(vec![])), ]) .switch( "header-row", @@ -55,6 +56,7 @@ impl Command for Transpose { "on repetition of record fields due to `header-row`, keep all the values obtained", Some('a'), ) + .allow_variants_without_examples(true) .rest( "rest", SyntaxShape::String, diff --git a/crates/nu-command/src/filters/uniq_by.rs b/crates/nu-command/src/filters/uniq_by.rs index c1e65e6a6a..16bbd3b497 100644 --- a/crates/nu-command/src/filters/uniq_by.rs +++ b/crates/nu-command/src/filters/uniq_by.rs @@ -17,7 +17,13 @@ impl Command for UniqBy { fn signature(&self) -> Signature { Signature::build("uniq-by") - .input_output_types(vec![(Type::Table(vec![]), Type::Table(vec![]))]) + .input_output_types(vec![ + (Type::Table(vec![]), Type::Table(vec![])), + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + ), + ]) .rest("columns", SyntaxShape::Any, "the column(s) to filter by") .switch( "count", @@ -39,6 +45,7 @@ impl Command for UniqBy { "Return the input values that occur once only", Some('u'), ) + .allow_variants_without_examples(true) .category(Category::Filters) } diff --git a/crates/nu-command/src/filters/update.rs b/crates/nu-command/src/filters/update.rs index c4daced13b..7743252f59 100644 --- a/crates/nu-command/src/filters/update.rs +++ b/crates/nu-command/src/filters/update.rs @@ -19,6 +19,10 @@ impl Command for Update { .input_output_types(vec![ (Type::Record(vec![]), Type::Record(vec![])), (Type::Table(vec![]), Type::Table(vec![])), + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + ), ]) .required( "field", @@ -30,6 +34,7 @@ impl Command for Update { SyntaxShape::Any, "the new value to give the cell(s), or a closure to create the value", ) + .allow_variants_without_examples(true) .category(Category::Filters) } diff --git a/crates/nu-command/src/filters/upsert.rs b/crates/nu-command/src/filters/upsert.rs index 1c0fcaac5d..cbabfe2807 100644 --- a/crates/nu-command/src/filters/upsert.rs +++ b/crates/nu-command/src/filters/upsert.rs @@ -19,6 +19,10 @@ impl Command for Upsert { .input_output_types(vec![ (Type::Record(vec![]), Type::Record(vec![])), (Type::Table(vec![]), Type::Table(vec![])), + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + ), ]) .required( "field", @@ -30,6 +34,7 @@ impl Command for Upsert { SyntaxShape::Any, "the new value to give the cell(s), or a closure to create the value", ) + .allow_variants_without_examples(true) .category(Category::Filters) } diff --git a/crates/nu-command/src/filters/where_.rs b/crates/nu-command/src/filters/where_.rs index a34651cc34..e8d9b0de7b 100644 --- a/crates/nu-command/src/filters/where_.rs +++ b/crates/nu-command/src/filters/where_.rs @@ -32,12 +32,14 @@ not supported."# Type::List(Box::new(Type::Any)), ), (Type::Table(vec![]), Type::Table(vec![])), + (Type::Range, Type::Any), ]) .required( "row_condition", SyntaxShape::RowCondition, "Filter condition", ) + .allow_variants_without_examples(true) .category(Category::Filters) } diff --git a/crates/nu-command/src/filters/wrap.rs b/crates/nu-command/src/filters/wrap.rs index 3cee9da095..64f5be8df4 100644 --- a/crates/nu-command/src/filters/wrap.rs +++ b/crates/nu-command/src/filters/wrap.rs @@ -23,8 +23,10 @@ impl Command for Wrap { .input_output_types(vec![ (Type::List(Box::new(Type::Any)), Type::Table(vec![])), (Type::Range, Type::Table(vec![])), + (Type::Any, Type::Record(vec![])), ]) .required("name", SyntaxShape::String, "the name of the column") + .allow_variants_without_examples(true) .category(Category::Filters) } diff --git a/crates/nu-command/src/math/abs.rs b/crates/nu-command/src/math/abs.rs index a4d1e8f623..2eeecb30b5 100644 --- a/crates/nu-command/src/math/abs.rs +++ b/crates/nu-command/src/math/abs.rs @@ -12,8 +12,15 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("math abs") - .input_output_types(vec![(Type::Number, Type::Number)]) + .input_output_types(vec![ + (Type::Number, Type::Number), + ( + Type::List(Box::new(Type::Number)), + Type::List(Box::new(Type::Number)), + ), + ]) .vectorizes_over_list(true) + .allow_variants_without_examples(true) .category(Category::Math) } diff --git a/crates/nu-command/src/math/avg.rs b/crates/nu-command/src/math/avg.rs index 95cce92306..1deae7ee4b 100644 --- a/crates/nu-command/src/math/avg.rs +++ b/crates/nu-command/src/math/avg.rs @@ -14,7 +14,12 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("math avg") - .input_output_types(vec![(Type::List(Box::new(Type::Number)), Type::Number)]) + .input_output_types(vec![ + (Type::List(Box::new(Type::Number)), Type::Number), + (Type::List(Box::new(Type::Duration)), Type::Duration), + (Type::List(Box::new(Type::Filesize)), Type::Filesize), + ]) + .allow_variants_without_examples(true) .category(Category::Math) } diff --git a/crates/nu-command/src/math/ceil.rs b/crates/nu-command/src/math/ceil.rs index 7398b957a7..ae7f181739 100644 --- a/crates/nu-command/src/math/ceil.rs +++ b/crates/nu-command/src/math/ceil.rs @@ -12,8 +12,15 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("math ceil") - .input_output_types(vec![(Type::Number, Type::Int)]) + .input_output_types(vec![ + (Type::Number, Type::Int), + ( + Type::List(Box::new(Type::Number)), + Type::List(Box::new(Type::Number)), + ), + ]) .vectorizes_over_list(true) + .allow_variants_without_examples(true) .category(Category::Math) } diff --git a/crates/nu-command/src/math/floor.rs b/crates/nu-command/src/math/floor.rs index e605dfa8ae..5625417bf1 100644 --- a/crates/nu-command/src/math/floor.rs +++ b/crates/nu-command/src/math/floor.rs @@ -12,8 +12,15 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("math floor") - .input_output_types(vec![(Type::Number, Type::Int)]) + .input_output_types(vec![ + (Type::Number, Type::Int), + ( + Type::List(Box::new(Type::Number)), + Type::List(Box::new(Type::Number)), + ), + ]) .vectorizes_over_list(true) + .allow_variants_without_examples(true) .category(Category::Math) } diff --git a/crates/nu-command/src/math/log.rs b/crates/nu-command/src/math/log.rs index 257857ce3d..1276b8d17d 100644 --- a/crates/nu-command/src/math/log.rs +++ b/crates/nu-command/src/math/log.rs @@ -20,7 +20,15 @@ impl Command for SubCommand { SyntaxShape::Number, "Base for which the logarithm should be computed", ) - .input_output_types(vec![(Type::Number, Type::Float)]) + .input_output_types(vec![ + (Type::Number, Type::Float), + (Type::Number, Type::Int), + ( + Type::List(Box::new(Type::Number)), + Type::List(Box::new(Type::Number)), + ), + ]) + .allow_variants_without_examples(true) .vectorizes_over_list(true) .category(Category::Math) } diff --git a/crates/nu-command/src/math/max.rs b/crates/nu-command/src/math/max.rs index 1d6e183583..fdfc993946 100644 --- a/crates/nu-command/src/math/max.rs +++ b/crates/nu-command/src/math/max.rs @@ -18,6 +18,7 @@ impl Command for SubCommand { (Type::List(Box::new(Type::Number)), Type::Number), (Type::Table(vec![]), Type::Record(vec![])), ]) + .allow_variants_without_examples(true) .category(Category::Math) } diff --git a/crates/nu-command/src/math/median.rs b/crates/nu-command/src/math/median.rs index 2fd925af53..02c5eb0d1d 100644 --- a/crates/nu-command/src/math/median.rs +++ b/crates/nu-command/src/math/median.rs @@ -20,6 +20,7 @@ impl Command for SubCommand { (Type::List(Box::new(Type::Number)), Type::Number), (Type::Table(vec![]), Type::Record(vec![])), ]) + .allow_variants_without_examples(true) .category(Category::Math) } diff --git a/crates/nu-command/src/math/min.rs b/crates/nu-command/src/math/min.rs index bfe10c6b31..2cb3458125 100644 --- a/crates/nu-command/src/math/min.rs +++ b/crates/nu-command/src/math/min.rs @@ -18,6 +18,7 @@ impl Command for SubCommand { (Type::List(Box::new(Type::Number)), Type::Number), (Type::Table(vec![]), Type::Record(vec![])), ]) + .allow_variants_without_examples(true) .category(Category::Math) } diff --git a/crates/nu-command/src/math/mode.rs b/crates/nu-command/src/math/mode.rs index ca0fd477f3..4f1de19d43 100644 --- a/crates/nu-command/src/math/mode.rs +++ b/crates/nu-command/src/math/mode.rs @@ -45,6 +45,7 @@ impl Command for SubCommand { ), (Type::Table(vec![]), Type::Record(vec![])), ]) + .allow_variants_without_examples(true) .category(Category::Math) } diff --git a/crates/nu-command/src/math/round.rs b/crates/nu-command/src/math/round.rs index ad4c378bec..b595688e11 100644 --- a/crates/nu-command/src/math/round.rs +++ b/crates/nu-command/src/math/round.rs @@ -15,7 +15,13 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("math round") - .input_output_types(vec![(Type::Number, Type::Number)]) + .input_output_types(vec![ + (Type::Number, Type::Number), + ( + Type::List(Box::new(Type::Number)), + Type::List(Box::new(Type::Number)), + ), + ]) .vectorizes_over_list(true) .named( "precision", diff --git a/crates/nu-command/src/math/sqrt.rs b/crates/nu-command/src/math/sqrt.rs index d341999151..f67f42b06c 100644 --- a/crates/nu-command/src/math/sqrt.rs +++ b/crates/nu-command/src/math/sqrt.rs @@ -12,8 +12,15 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("math sqrt") - .input_output_types(vec![(Type::Number, Type::Number)]) + .input_output_types(vec![ + (Type::Number, Type::Number), + ( + Type::List(Box::new(Type::Number)), + Type::List(Box::new(Type::Number)), + ), + ]) .vectorizes_over_list(true) + .allow_variants_without_examples(true) .category(Category::Math) } diff --git a/crates/nu-command/src/math/sum.rs b/crates/nu-command/src/math/sum.rs index 60d91a543f..ed9d27012c 100644 --- a/crates/nu-command/src/math/sum.rs +++ b/crates/nu-command/src/math/sum.rs @@ -14,7 +14,12 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("math sum") - .input_output_types(vec![(Type::List(Box::new(Type::Number)), Type::Number)]) + .input_output_types(vec![ + (Type::List(Box::new(Type::Number)), Type::Number), + (Type::Range, Type::Number), + (Type::Table(vec![]), Type::Table(vec![])), + ]) + .allow_variants_without_examples(true) .category(Category::Math) } diff --git a/crates/nu-command/src/network/url/encode.rs b/crates/nu-command/src/network/url/encode.rs index ca13ff5015..0555fabe44 100644 --- a/crates/nu-command/src/network/url/encode.rs +++ b/crates/nu-command/src/network/url/encode.rs @@ -17,7 +17,7 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("url encode") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![(Type::String, Type::String), (Type::List(Box::new(Type::String)), Type::List(Box::new(Type::String)))]) .vectorizes_over_list(true) .switch( "all", diff --git a/crates/nu-command/src/path/join.rs b/crates/nu-command/src/path/join.rs index 603a9a0c04..d5a622c313 100644 --- a/crates/nu-command/src/path/join.rs +++ b/crates/nu-command/src/path/join.rs @@ -35,6 +35,7 @@ impl Command for SubCommand { .input_output_types(vec![ (Type::String, Type::String), (Type::List(Box::new(Type::String)), Type::String), + (Type::Record(vec![]), Type::String), (Type::Table(vec![]), Type::List(Box::new(Type::String))), ]) .named( @@ -43,6 +44,7 @@ impl Command for SubCommand { "For a record or table input, join strings at the given columns", Some('c'), ) + .allow_variants_without_examples(true) .rest("append", SyntaxShape::String, "Path to append to the input") } diff --git a/crates/nu-command/src/platform/ansi/strip.rs b/crates/nu-command/src/platform/ansi/strip.rs index 54f2bdfabc..9c38e832a2 100644 --- a/crates/nu-command/src/platform/ansi/strip.rs +++ b/crates/nu-command/src/platform/ansi/strip.rs @@ -15,12 +15,13 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("ansi strip") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![(Type::String, Type::String), (Type::List(Box::new(Type::String)), Type::List(Box::new(Type::String)))]) .rest( "cell path", SyntaxShape::CellPath, "for a data structure input, remove ANSI sequences from strings at the given cell paths", ) + .allow_variants_without_examples(true) .category(Category::Platform) } diff --git a/crates/nu-command/src/strings/parse.rs b/crates/nu-command/src/strings/parse.rs index ebeeee8a71..f74fd42371 100644 --- a/crates/nu-command/src/strings/parse.rs +++ b/crates/nu-command/src/strings/parse.rs @@ -30,8 +30,12 @@ impl Command for Parse { SyntaxShape::String, "the pattern to match. Eg) \"{foo}: {bar}\"", ) - .input_output_types(vec![(Type::String, Type::Table(vec![]))]) + .input_output_types(vec![ + (Type::String, Type::Table(vec![])), + (Type::List(Box::new(Type::Any)), Type::Table(vec![])), + ]) .switch("regex", "use full regex syntax for patterns", Some('r')) + .allow_variants_without_examples(true) .category(Category::Strings) } diff --git a/crates/nu-command/src/strings/split/row.rs b/crates/nu-command/src/strings/split/row.rs index 74d5d322c7..2ee5c83fb4 100644 --- a/crates/nu-command/src/strings/split/row.rs +++ b/crates/nu-command/src/strings/split/row.rs @@ -16,8 +16,12 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("split row") - .input_output_types(vec![(Type::String, Type::List(Box::new(Type::String)))]) + .input_output_types(vec![ + (Type::String, Type::List(Box::new(Type::String))), + (Type::List(Box::new(Type::String)), Type::Table(vec![])), + ]) .vectorizes_over_list(true) + .allow_variants_without_examples(true) .required( "separator", SyntaxShape::String, diff --git a/crates/nu-command/src/strings/str_/case/camel_case.rs b/crates/nu-command/src/strings/str_/case/camel_case.rs index 051599add5..cee3e1993e 100644 --- a/crates/nu-command/src/strings/str_/case/camel_case.rs +++ b/crates/nu-command/src/strings/str_/case/camel_case.rs @@ -17,7 +17,10 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str camel-case") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .vectorizes_over_list(true) .rest( "rest", diff --git a/crates/nu-command/src/strings/str_/case/capitalize.rs b/crates/nu-command/src/strings/str_/case/capitalize.rs index 498b77f2bb..bad3ddb720 100644 --- a/crates/nu-command/src/strings/str_/case/capitalize.rs +++ b/crates/nu-command/src/strings/str_/case/capitalize.rs @@ -15,7 +15,10 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str capitalize") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .vectorizes_over_list(true) .rest( "rest", diff --git a/crates/nu-command/src/strings/str_/case/downcase.rs b/crates/nu-command/src/strings/str_/case/downcase.rs index 9c63e29b30..df16626f4b 100644 --- a/crates/nu-command/src/strings/str_/case/downcase.rs +++ b/crates/nu-command/src/strings/str_/case/downcase.rs @@ -15,7 +15,10 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str downcase") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .vectorizes_over_list(true) .rest( "rest", diff --git a/crates/nu-command/src/strings/str_/case/kebab_case.rs b/crates/nu-command/src/strings/str_/case/kebab_case.rs index 44c55d1245..f807862e85 100644 --- a/crates/nu-command/src/strings/str_/case/kebab_case.rs +++ b/crates/nu-command/src/strings/str_/case/kebab_case.rs @@ -17,7 +17,10 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str kebab-case") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .vectorizes_over_list(true) .rest( "rest", diff --git a/crates/nu-command/src/strings/str_/case/pascal_case.rs b/crates/nu-command/src/strings/str_/case/pascal_case.rs index 757aab1883..459dde3db1 100644 --- a/crates/nu-command/src/strings/str_/case/pascal_case.rs +++ b/crates/nu-command/src/strings/str_/case/pascal_case.rs @@ -17,7 +17,10 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str pascal-case") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .vectorizes_over_list(true) .rest( "rest", diff --git a/crates/nu-command/src/strings/str_/case/screaming_snake_case.rs b/crates/nu-command/src/strings/str_/case/screaming_snake_case.rs index 4e4d6f677e..840fce560c 100644 --- a/crates/nu-command/src/strings/str_/case/screaming_snake_case.rs +++ b/crates/nu-command/src/strings/str_/case/screaming_snake_case.rs @@ -16,7 +16,10 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str screaming-snake-case") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .vectorizes_over_list(true) .rest( "rest", diff --git a/crates/nu-command/src/strings/str_/case/snake_case.rs b/crates/nu-command/src/strings/str_/case/snake_case.rs index 137908421e..56e7bb2b09 100644 --- a/crates/nu-command/src/strings/str_/case/snake_case.rs +++ b/crates/nu-command/src/strings/str_/case/snake_case.rs @@ -16,7 +16,10 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str snake-case") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .vectorizes_over_list(true) .rest( "rest", diff --git a/crates/nu-command/src/strings/str_/case/title_case.rs b/crates/nu-command/src/strings/str_/case/title_case.rs index 1690c2fa3c..daab6fd47d 100644 --- a/crates/nu-command/src/strings/str_/case/title_case.rs +++ b/crates/nu-command/src/strings/str_/case/title_case.rs @@ -17,7 +17,10 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str title-case") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .vectorizes_over_list(true) .rest( "rest", diff --git a/crates/nu-command/src/strings/str_/case/upcase.rs b/crates/nu-command/src/strings/str_/case/upcase.rs index fd4fdca790..259026f56b 100644 --- a/crates/nu-command/src/strings/str_/case/upcase.rs +++ b/crates/nu-command/src/strings/str_/case/upcase.rs @@ -14,8 +14,12 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str upcase") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .vectorizes_over_list(true) + .allow_variants_without_examples(true) .rest( "rest", SyntaxShape::CellPath, diff --git a/crates/nu-command/src/strings/str_/contains.rs b/crates/nu-command/src/strings/str_/contains.rs index ef47588fb1..ce02b46b72 100644 --- a/crates/nu-command/src/strings/str_/contains.rs +++ b/crates/nu-command/src/strings/str_/contains.rs @@ -32,6 +32,7 @@ impl Command for SubCommand { .input_output_types(vec![ (Type::String, Type::Bool), (Type::Table(vec![]), Type::Table(vec![])), + (Type::List(Box::new(Type::String)), Type::List(Box::new(Type::Bool))) ]) .vectorizes_over_list(true) .required("string", SyntaxShape::String, "the substring to find") diff --git a/crates/nu-command/src/strings/str_/join.rs b/crates/nu-command/src/strings/str_/join.rs index e48adefacb..15e998c94d 100644 --- a/crates/nu-command/src/strings/str_/join.rs +++ b/crates/nu-command/src/strings/str_/join.rs @@ -16,12 +16,16 @@ impl Command for StrJoin { fn signature(&self) -> Signature { Signature::build("str join") - .input_output_types(vec![(Type::List(Box::new(Type::String)), Type::String)]) + .input_output_types(vec![ + (Type::List(Box::new(Type::Any)), Type::String), + (Type::String, Type::String), + ]) .optional( "separator", SyntaxShape::String, "optional separator to use when creating string", ) + .allow_variants_without_examples(true) .category(Category::Strings) } diff --git a/crates/nu-command/src/strings/str_/length.rs b/crates/nu-command/src/strings/str_/length.rs index 78a90fc528..8f6c7c6db9 100644 --- a/crates/nu-command/src/strings/str_/length.rs +++ b/crates/nu-command/src/strings/str_/length.rs @@ -29,7 +29,7 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str length") - .input_output_types(vec![(Type::String, Type::Int)]) + .input_output_types(vec![(Type::String, Type::Int), (Type::List(Box::new(Type::String)), Type::List(Box::new(Type::Int)))]) .vectorizes_over_list(true) .switch( "grapheme-clusters", diff --git a/crates/nu-command/src/strings/str_/replace.rs b/crates/nu-command/src/strings/str_/replace.rs index e6918fcd3b..7a4ece8e21 100644 --- a/crates/nu-command/src/strings/str_/replace.rs +++ b/crates/nu-command/src/strings/str_/replace.rs @@ -34,7 +34,10 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str replace") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .vectorizes_over_list(true) .required("find", SyntaxShape::String, "the pattern to find") .required("replace", SyntaxShape::String, "the replacement string") diff --git a/crates/nu-command/src/strings/str_/reverse.rs b/crates/nu-command/src/strings/str_/reverse.rs index 22586be55e..89209d5028 100644 --- a/crates/nu-command/src/strings/str_/reverse.rs +++ b/crates/nu-command/src/strings/str_/reverse.rs @@ -16,7 +16,13 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str reverse") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![ + (Type::String, Type::String), + ( + Type::List(Box::new(Type::String)), + Type::List(Box::new(Type::String)), + ), + ]) .vectorizes_over_list(true) .rest( "rest", diff --git a/crates/nu-command/src/strings/str_/substring.rs b/crates/nu-command/src/strings/str_/substring.rs index c94b0edd5f..6079f64cc6 100644 --- a/crates/nu-command/src/strings/str_/substring.rs +++ b/crates/nu-command/src/strings/str_/substring.rs @@ -42,8 +42,9 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str substring") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![(Type::String, Type::String), (Type::Table(vec![]), Type::Table(vec![]))]) .vectorizes_over_list(true) + .allow_variants_without_examples(true) .switch( "grapheme-clusters", "count indexes and split using grapheme clusters (all visible chars have length 1)", diff --git a/crates/nu-command/src/strings/str_/trim/trim_.rs b/crates/nu-command/src/strings/str_/trim/trim_.rs index 6b7c8888d2..e2b80ab88d 100644 --- a/crates/nu-command/src/strings/str_/trim/trim_.rs +++ b/crates/nu-command/src/strings/str_/trim/trim_.rs @@ -35,8 +35,16 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str trim") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![ + (Type::String, Type::String), + ( + Type::List(Box::new(Type::String)), + Type::List(Box::new(Type::String)), + ), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .vectorizes_over_list(true) + .allow_variants_without_examples(true) .rest( "rest", SyntaxShape::CellPath, diff --git a/crates/nu-command/tests/commands/drop.rs b/crates/nu-command/tests/commands/drop.rs index 73d7e0c519..ecba9177d2 100644 --- a/crates/nu-command/tests/commands/drop.rs +++ b/crates/nu-command/tests/commands/drop.rs @@ -91,5 +91,5 @@ fn nth_missing_first_argument() { fn fail_on_non_iterator() { let actual = nu!("1 | drop 50"); - assert!(actual.err.contains("only_supports_this_input_type")); + assert!(actual.err.contains("command doesn't support")); } diff --git a/crates/nu-command/tests/commands/last.rs b/crates/nu-command/tests/commands/last.rs index 2ff1254aff..f84225b950 100644 --- a/crates/nu-command/tests/commands/last.rs +++ b/crates/nu-command/tests/commands/last.rs @@ -78,7 +78,7 @@ fn last_errors_on_negative_index() { fn fail_on_non_iterator() { let actual = nu!("1 | last"); - assert!(actual.err.contains("only_supports_this_input_type")); + assert!(actual.err.contains("command doesn't support")); } #[test] diff --git a/crates/nu-command/tests/commands/math/round.rs b/crates/nu-command/tests/commands/math/round.rs index 6b93ebd083..5d2158a56f 100644 --- a/crates/nu-command/tests/commands/math/round.rs +++ b/crates/nu-command/tests/commands/math/round.rs @@ -32,5 +32,5 @@ fn can_round_float_with_negative_precision() { fn fails_with_wrong_input_type() { let actual = nu!("\"not_a_number\" | math round"); - assert!(actual.err.contains("Input type not supported")) + assert!(actual.err.contains("command doesn't support")) } diff --git a/crates/nu-command/tests/commands/mkdir.rs b/crates/nu-command/tests/commands/mkdir.rs index 250e7bdbce..7943ce7766 100644 --- a/crates/nu-command/tests/commands/mkdir.rs +++ b/crates/nu-command/tests/commands/mkdir.rs @@ -70,7 +70,6 @@ fn print_created_paths() { pipeline( r#" mkdir -v dir_1 dir_2 dir_3 - | length "# )); diff --git a/crates/nu-command/tests/commands/rename.rs b/crates/nu-command/tests/commands/rename.rs index 19d733f70c..50fc149458 100644 --- a/crates/nu-command/tests/commands/rename.rs +++ b/crates/nu-command/tests/commands/rename.rs @@ -83,7 +83,7 @@ fn errors_if_no_columns_present() { "# )); - assert!(actual.err.contains("only record input data is supported")); + assert!(actual.err.contains("command doesn't support")); }) } diff --git a/crates/nu-command/tests/commands/reverse.rs b/crates/nu-command/tests/commands/reverse.rs index e41459800f..936cf9f9e4 100644 --- a/crates/nu-command/tests/commands/reverse.rs +++ b/crates/nu-command/tests/commands/reverse.rs @@ -14,5 +14,5 @@ fn can_get_reverse_first() { fn fail_on_non_iterator() { let actual = nu!(cwd: ".", pipeline("1 | reverse")); - assert!(actual.err.contains("only_supports_this_input_type")); + assert!(actual.err.contains("command doesn't support")); } diff --git a/crates/nu-command/tests/commands/skip/skip_.rs b/crates/nu-command/tests/commands/skip/skip_.rs index ac461523b5..6b86843b22 100644 --- a/crates/nu-command/tests/commands/skip/skip_.rs +++ b/crates/nu-command/tests/commands/skip/skip_.rs @@ -19,5 +19,5 @@ fn binary_skip() { fn fail_on_non_iterator() { let actual = nu!(cwd: ".", pipeline("1 | skip 2")); - assert!(actual.err.contains("only_supports_this_input_type")); + assert!(actual.err.contains("command doesn't support")); } diff --git a/crates/nu-command/tests/commands/skip/until.rs b/crates/nu-command/tests/commands/skip/until.rs index e6acc9c5cd..7925cf4bbd 100644 --- a/crates/nu-command/tests/commands/skip/until.rs +++ b/crates/nu-command/tests/commands/skip/until.rs @@ -54,5 +54,5 @@ fn condition_is_met() { fn fail_on_non_iterator() { let actual = nu!(cwd: ".", pipeline("1 | skip until {|row| $row == 2}")); - assert!(actual.err.contains("only_supports_this_input_type")); + assert!(actual.err.contains("command doesn't support")); } diff --git a/crates/nu-command/tests/commands/skip/while_.rs b/crates/nu-command/tests/commands/skip/while_.rs index b008ba4dad..15f9b6d3e1 100644 --- a/crates/nu-command/tests/commands/skip/while_.rs +++ b/crates/nu-command/tests/commands/skip/while_.rs @@ -54,5 +54,5 @@ fn condition_is_met() { fn fail_on_non_iterator() { let actual = nu!(cwd: ".", pipeline("1 | skip while {|row| $row == 2}")); - assert!(actual.err.contains("only_supports_this_input_type")); + assert!(actual.err.contains("command doesn't support")); } diff --git a/crates/nu-command/tests/commands/sort_by.rs b/crates/nu-command/tests/commands/sort_by.rs index 0d7165b2fd..d1c48dd7bf 100644 --- a/crates/nu-command/tests/commands/sort_by.rs +++ b/crates/nu-command/tests/commands/sort_by.rs @@ -126,5 +126,5 @@ fn no_column_specified_fails() { fn fail_on_non_iterator() { let actual = nu!("1 | sort-by"); - assert!(actual.err.contains("only_supports_this_input_type")); + assert!(actual.err.contains("command doesn't support")); } diff --git a/crates/nu-command/tests/commands/take/rows.rs b/crates/nu-command/tests/commands/take/rows.rs index 3f57a5911c..a8099abf91 100644 --- a/crates/nu-command/tests/commands/take/rows.rs +++ b/crates/nu-command/tests/commands/take/rows.rs @@ -51,7 +51,7 @@ fn fails_on_string() { "# )); - assert!(actual.err.contains("only_supports_this_input_type")); + assert!(actual.err.contains("command doesn't support")); } #[test] diff --git a/crates/nu-command/tests/commands/take/until.rs b/crates/nu-command/tests/commands/take/until.rs index 5a6aaebc70..0859abe5fa 100644 --- a/crates/nu-command/tests/commands/take/until.rs +++ b/crates/nu-command/tests/commands/take/until.rs @@ -55,5 +55,5 @@ fn condition_is_met() { fn fail_on_non_iterator() { let actual = nu!(cwd: ".", pipeline("1 | take until {|row| $row == 2}")); - assert!(actual.err.contains("only_supports_this_input_type")); + assert!(actual.err.contains("command doesn't support")); } diff --git a/crates/nu-command/tests/commands/take/while_.rs b/crates/nu-command/tests/commands/take/while_.rs index 33e052ffdc..07ad8a0819 100644 --- a/crates/nu-command/tests/commands/take/while_.rs +++ b/crates/nu-command/tests/commands/take/while_.rs @@ -54,5 +54,5 @@ fn condition_is_met() { fn fail_on_non_iterator() { let actual = nu!(cwd: ".", pipeline("1 | take while {|row| $row == 2}")); - assert!(actual.err.contains("only_supports_this_input_type")); + assert!(actual.err.contains("command doesn't support")); } diff --git a/crates/nu-command/tests/commands/where_.rs b/crates/nu-command/tests/commands/where_.rs index 1f1ccc764b..d74d3f1dd3 100644 --- a/crates/nu-command/tests/commands/where_.rs +++ b/crates/nu-command/tests/commands/where_.rs @@ -180,7 +180,7 @@ fn contains_operator() { fn fail_on_non_iterator() { let actual = nu!(cwd: ".", pipeline(r#"{"name": "foo", "size": 3} | where name == "foo""#)); - assert!(actual.err.contains("only_supports_this_input_type")); + assert!(actual.err.contains("command doesn't support")); } // Test that filtering on columns that might be missing/null works diff --git a/crates/nu-command/tests/format_conversions/csv.rs b/crates/nu-command/tests/format_conversions/csv.rs index cacb796f29..b120d0a213 100644 --- a/crates/nu-command/tests/format_conversions/csv.rs +++ b/crates/nu-command/tests/format_conversions/csv.rs @@ -405,5 +405,5 @@ fn string_to_csv_error() { "# )); - assert!(actual.err.contains("can't convert")) + assert!(actual.err.contains("command doesn't support")) } diff --git a/crates/nu-command/tests/format_conversions/toml.rs b/crates/nu-command/tests/format_conversions/toml.rs index e0e220e31e..d69f77fbed 100644 --- a/crates/nu-command/tests/format_conversions/toml.rs +++ b/crates/nu-command/tests/format_conversions/toml.rs @@ -70,7 +70,7 @@ fn table_to_toml_fails() { "# )); - assert_eq!(actual.out, "true"); + assert!(actual.err.contains("command doesn't support")); } #[test] @@ -83,7 +83,7 @@ fn string_to_toml_fails() { "# )); - assert_eq!(actual.out, "true"); + assert!(actual.err.contains("command doesn't support")); } #[test] diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index 3a4a318624..d0e1edfa3b 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -4,7 +4,7 @@ use crate::{ lite_parser::{lite_parse, LiteCommand, LiteElement, LitePipeline}, parse_mut, parse_patterns::{parse_match_pattern, parse_pattern}, - type_check::{math_result_type, type_compatible}, + type_check::{self, math_result_type, type_compatible}, Token, TokenContents, }; @@ -5582,6 +5582,8 @@ pub fn parse_block( block.span = Some(span); + type_check::check_block_input_output(working_set, &block); + block } diff --git a/crates/nu-parser/src/type_check.rs b/crates/nu-parser/src/type_check.rs index bb4f4e3a2d..f09d1e3b04 100644 --- a/crates/nu-parser/src/type_check.rs +++ b/crates/nu-parser/src/type_check.rs @@ -1,5 +1,8 @@ use nu_protocol::{ - ast::{Bits, Boolean, Comparison, Expr, Expression, Math, Operator}, + ast::{ + Bits, Block, Boolean, Comparison, Expr, Expression, Math, Operator, Pipeline, + PipelineElement, + }, engine::StateWorkingSet, ParseError, Type, }; @@ -24,7 +27,30 @@ pub fn type_compatible(lhs: &Type, rhs: &Type) -> bool { match (lhs, rhs) { (Type::List(c), Type::List(d)) => type_compatible(c, d), - (Type::List(c), Type::Table(_)) => matches!(**c, Type::Any), + (Type::ListStream, Type::List(_)) => true, + (Type::List(_), Type::ListStream) => true, + (Type::List(c), Type::Table(table_fields)) => { + if matches!(**c, Type::Any) { + return true; + } + + if let Type::Record(fields) = &**c { + is_compatible(fields, table_fields) + } else { + false + } + } + (Type::Table(table_fields), Type::List(c)) => { + if matches!(**c, Type::Any) { + return true; + } + + if let Type::Record(fields) = &**c { + is_compatible(table_fields, fields) + } else { + false + } + } (Type::Number, Type::Int) => true, (Type::Int, Type::Number) => true, (Type::Number, Type::Float) => true, @@ -921,3 +947,108 @@ pub fn math_result_type( } } } + +pub fn check_pipeline_type( + working_set: &mut StateWorkingSet, + pipeline: &Pipeline, + input_type: Type, +) -> Type { + let mut current_type = input_type; + + 'elem: for elem in &pipeline.elements { + match elem { + PipelineElement::Expression( + _, + Expression { + expr: Expr::Call(call), + .. + }, + ) => { + let decl = working_set.get_decl(call.decl_id); + + if current_type == Type::Any { + let mut new_current_type = None; + for (_, call_output) in decl.signature().input_output_types { + if let Some(inner_current_type) = &new_current_type { + if inner_current_type == &Type::Any { + break; + } else if inner_current_type != &call_output { + // Union unequal types to Any for now + new_current_type = Some(Type::Any) + } + } else { + new_current_type = Some(call_output.clone()) + } + } + + if let Some(new_current_type) = new_current_type { + current_type = new_current_type + } else { + current_type = Type::Any; + } + continue 'elem; + } else { + for (call_input, call_output) in decl.signature().input_output_types { + if type_compatible(&call_input, ¤t_type) { + current_type = call_output.clone(); + continue 'elem; + } + } + } + + if !decl.signature().input_output_types.is_empty() { + working_set.error(ParseError::InputMismatch(current_type, call.head)) + } + current_type = Type::Any; + } + PipelineElement::Expression(_, Expression { ty, .. }) => { + current_type = ty.clone(); + } + _ => { + current_type = Type::Any; + } + } + } + + current_type +} + +pub fn check_block_input_output(working_set: &mut StateWorkingSet, block: &Block) { + // let inputs = block.input_types(); + + for (input_type, output_type) in &block.signature.input_output_types { + let mut current_type = input_type.clone(); + let mut current_output_type = Type::Nothing; + + for pipeline in &block.pipelines { + current_output_type = check_pipeline_type(working_set, pipeline, current_type); + current_type = Type::Nothing; + } + + if !type_compatible(output_type, ¤t_output_type) + && output_type != &Type::Any + && current_output_type != Type::Any + { + working_set.error(ParseError::OutputMismatch( + output_type.clone(), + block + .pipelines + .last() + .expect("internal error: we should have pipelines") + .elements + .last() + .expect("internal error: we should have elements") + .span(), + )) + } + } + + if block.signature.input_output_types.is_empty() { + let mut current_type = Type::Any; + + for pipeline in &block.pipelines { + let _ = check_pipeline_type(working_set, pipeline, current_type); + current_type = Type::Nothing; + } + } +} diff --git a/crates/nu-protocol/src/ast/block.rs b/crates/nu-protocol/src/ast/block.rs index e57171db0e..0840cf642c 100644 --- a/crates/nu-protocol/src/ast/block.rs +++ b/crates/nu-protocol/src/ast/block.rs @@ -1,5 +1,5 @@ -use super::{Expr, Expression, Pipeline}; -use crate::{ast::PipelineElement, engine::StateWorkingSet, Signature, Span, Type, VarId}; +use super::Pipeline; +use crate::{ast::PipelineElement, Signature, Span, Type, VarId}; use serde::{Deserialize, Serialize}; use std::ops::{Index, IndexMut}; @@ -66,37 +66,6 @@ impl Block { } } - pub fn input_type(&self, working_set: &StateWorkingSet) -> Type { - if let Some(first) = self.pipelines.first() { - if let Some(first) = first.elements.first() { - match first { - PipelineElement::Expression( - _, - Expression { - expr: Expr::Call(call), - .. - }, - ) => { - let decl = working_set.get_decl(call.decl_id); - - decl.signature().get_input_type() - } - PipelineElement::Expression( - _, - Expression { - expr: Expr::ExternalCall(..), - .. - }, - ) => Type::Any, - _ => Type::Nothing, - } - } else { - Type::Nothing - } - } else { - Type::Nothing - } - } pub fn output_type(&self) -> Type { if let Some(last) = self.pipelines.last() { if let Some(last) = last.elements.last() { diff --git a/crates/nu-protocol/src/parse_error.rs b/crates/nu-protocol/src/parse_error.rs index e7f3f834ef..610f513499 100644 --- a/crates/nu-protocol/src/parse_error.rs +++ b/crates/nu-protocol/src/parse_error.rs @@ -48,6 +48,14 @@ pub enum ParseError { #[diagnostic(code(nu::parser::parse_mismatch_with_full_string_msg))] ExpectedWithStringMsg(String, #[label("expected {0}")] Span), + #[error("Command does not support {0} input.")] + #[diagnostic(code(nu::parser::input_type_mismatch))] + InputMismatch(Type, #[label("command doesn't support {0} input")] Span), + + #[error("Command output doesn't match {0}.")] + #[diagnostic(code(nu::parser::output_type_mismatch))] + OutputMismatch(Type, #[label("command doesn't output {0}")] Span), + #[error("Type mismatch during operation.")] #[diagnostic(code(nu::parser::type_mismatch))] Mismatch(String, String, #[label("expected {0}, found {1}")] Span), // expected, found, span @@ -526,6 +534,8 @@ impl ParseError { ParseError::KeywordMissingArgument(_, _, s) => *s, ParseError::MissingType(s) => *s, ParseError::TypeMismatch(_, _, s) => *s, + ParseError::InputMismatch(_, s) => *s, + ParseError::OutputMismatch(_, s) => *s, ParseError::MissingRequiredFlag(_, s) => *s, ParseError::IncompleteMathExpression(s) => *s, ParseError::UnknownState(_, s) => *s, diff --git a/src/tests/test_bits.rs b/src/tests/test_bits.rs index 4399ea711a..ea4231786c 100644 --- a/src/tests/test_bits.rs +++ b/src/tests/test_bits.rs @@ -46,7 +46,7 @@ fn bits_xor_negative() -> TestResult { #[test] fn bits_xor_list() -> TestResult { run_test( - "[1 2 3 8 9 10] | bits xor 2 | str join '.'", + "[1 2 3 8 9 10] | bits xor 2 | into string | str join '.'", "3.0.1.10.11.8", ) } diff --git a/src/tests/test_engine.rs b/src/tests/test_engine.rs index 39ae57cf68..4aa2b7453b 100644 --- a/src/tests/test_engine.rs +++ b/src/tests/test_engine.rs @@ -55,7 +55,7 @@ fn in_and_if_else() -> TestResult { #[test] fn help_works_with_missing_requirements() -> TestResult { // `each while` is part of the *extra* feature and adds 3 lines - let expected_length = if cfg!(feature = "extra") { "65" } else { "62" }; + let expected_length = if cfg!(feature = "extra") { "66" } else { "63" }; run_test(r#"each --help | lines | length"#, expected_length) }