Fix parse-time pipeline type checking to support multiple output types for same input type (#16111)

# Description
Fixes #15485

This PR changes pipeline checking to keep track of all possible output
types instead of only first type matching input type which appears in
the input/output types. For example, in this command:
```nushell
def foo []: [int -> string, int -> record] {
  # ...
}
```
An `int` input to the command may result in a string or a record to be
output. Before this PR, Nushell would always assume that an `int` input
would cause a `string` output because it's the first matching
input/output type pair. This would cause issues during type checking
where the parser would incorrectly determine the output type. After this
PR, Nushell considers the command to output either a string or a record.

# User-Facing Changes
* Parse-time pipeline type checking now properly supports commands with
multiple pipeline output types for the same pipeline input type

# Tests + Formatting
Added a couple tests

# After Submitting
N/A

---------

Co-authored-by: Bahex <Bahex@users.noreply.github.com>
This commit is contained in:
132ikl
2025-08-01 21:35:25 -04:00
committed by GitHub
parent eb8d2d3206
commit da9615f971
7 changed files with 164 additions and 83 deletions

View File

@ -1,4 +1,4 @@
use crate::repl::tests::{TestResult, fail_test, run_test};
use crate::repl::tests::{TestResult, fail_test, run_test, run_test_contains};
use rstest::rstest;
#[test]
@ -178,3 +178,62 @@ fn in_oneof_block_expected_type(#[case] input: &str) -> TestResult {
fn in_oneof_block_expected_block() -> TestResult {
fail_test("match 1 { 0 => { try 3 } }", "expected block")
}
#[test]
fn pipeline_multiple_types() -> TestResult {
// https://github.com/nushell/nushell/issues/15485
run_test_contains("{year: 2019} | into datetime | date humanize", "years ago")
}
const MULTIPLE_TYPES_DEFS: &str = "
def foo []: [int -> int, int -> string] {
if $in > 2 { 'hi' } else 4
}
def bar []: [int -> filesize, string -> string] {
if $in == 'hi' { 'meow' } else { into filesize }
}
";
#[test]
fn pipeline_multiple_types_custom() -> TestResult {
run_test(
&format!(
"{MULTIPLE_TYPES_DEFS}
5 | foo | str trim"
),
"hi",
)
}
#[test]
fn pipeline_multiple_types_propagate_string() -> TestResult {
run_test(
&format!(
"{MULTIPLE_TYPES_DEFS}
5 | foo | bar | str trim"
),
"meow",
)
}
#[test]
fn pipeline_multiple_types_propagate_int() -> TestResult {
run_test(
&format!(
"{MULTIPLE_TYPES_DEFS}
2 | foo | bar | format filesize B"
),
"4 B",
)
}
#[test]
fn pipeline_multiple_types_propagate_error() -> TestResult {
fail_test(
&format!(
"{MULTIPLE_TYPES_DEFS}
2 | foo | bar | values"
),
"parser::input_type_mismatch",
)
}