Raise error when using o>| pipe (#13323)

# Description
From the feedbacks from @amtoine , it's good to make nushell shows error
for `o>|` syntax.

# User-Facing Changes
## Before
```nushell
'foo' o>| print                                                                                                                                                                                                                     07/09/2024 06:44:23 AM
Error: nu::parser::parse_mismatch

  × Parse mismatch during operation.
   ╭─[entry #6:1:9]
 1 │ 'foo' o>| print
   ·         ┬
   ·         ╰── expected redirection target
```

## After
```nushell
'foo' o>| print                                                                                                                                                                                                                     07/09/2024 06:47:26 AM
Error: nu::parser::parse_mismatch

  × Parse mismatch during operation.
   ╭─[entry #1:1:7]
 1 │ 'foo' o>| print
   ·       ─┬─
   ·        ╰── expected `|`.  Redirection stdout to pipe is the same as piping directly.
   ╰────
```

# Tests + Formatting
Added one test

---------

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
This commit is contained in:
Wind 2024-07-09 20:11:25 +08:00 committed by GitHub
parent e98b2ceb8c
commit 1964dacaef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 46 additions and 15 deletions

View File

@ -580,7 +580,10 @@ fn lex_internal(
// If the next character is non-newline whitespace, skip it. // If the next character is non-newline whitespace, skip it.
curr_offset += 1; curr_offset += 1;
} else { } else {
let token = try_lex_special_piped_item(input, &mut curr_offset, span_offset); let (token, err) = try_lex_special_piped_item(input, &mut curr_offset, span_offset);
if error.is_none() {
error = err;
}
if let Some(token) = token { if let Some(token) = token {
output.push(token); output.push(token);
is_complete = false; is_complete = false;
@ -614,40 +617,60 @@ fn try_lex_special_piped_item(
input: &[u8], input: &[u8],
curr_offset: &mut usize, curr_offset: &mut usize,
span_offset: usize, span_offset: usize,
) -> Option<Token> { ) -> (Option<Token>, Option<ParseError>) {
let c = input[*curr_offset]; let c = input[*curr_offset];
let e_pipe_len = 3; let e_pipe_len = 3;
let eo_pipe_len = 5; let eo_pipe_len = 5;
let o_pipe_len = 3;
let offset = *curr_offset; let offset = *curr_offset;
if c == b'e' { if c == b'e' {
// expect `e>|` // expect `e>|`
if (offset + e_pipe_len <= input.len()) && (&input[offset..offset + e_pipe_len] == b"e>|") { if (offset + e_pipe_len <= input.len()) && (&input[offset..offset + e_pipe_len] == b"e>|") {
*curr_offset += e_pipe_len; *curr_offset += e_pipe_len;
return Some(Token::new( return (
Some(Token::new(
TokenContents::ErrGreaterPipe, TokenContents::ErrGreaterPipe,
Span::new(span_offset + offset, span_offset + offset + e_pipe_len), Span::new(span_offset + offset, span_offset + offset + e_pipe_len),
)); )),
None,
);
} }
if (offset + eo_pipe_len <= input.len()) if (offset + eo_pipe_len <= input.len())
&& (&input[offset..offset + eo_pipe_len] == b"e+o>|") && (&input[offset..offset + eo_pipe_len] == b"e+o>|")
{ {
*curr_offset += eo_pipe_len; *curr_offset += eo_pipe_len;
return Some(Token::new( return (
Some(Token::new(
TokenContents::OutErrGreaterPipe, TokenContents::OutErrGreaterPipe,
Span::new(span_offset + offset, span_offset + offset + eo_pipe_len), Span::new(span_offset + offset, span_offset + offset + eo_pipe_len),
)); )),
None,
);
} }
} else if c == b'o' { } else if c == b'o' {
// indicates an error if user happened to type `o>|`
if offset + o_pipe_len <= input.len() && (&input[offset..offset + o_pipe_len] == b"o>|") {
return (
None,
Some(ParseError::Expected(
"`|`. Redirecting stdout to a pipe is the same as normal piping.",
Span::new(span_offset + offset, span_offset + offset + o_pipe_len),
)),
);
}
// it can be the following case: `o+e>|` // it can be the following case: `o+e>|`
if (offset + eo_pipe_len <= input.len()) if (offset + eo_pipe_len <= input.len())
&& (&input[offset..offset + eo_pipe_len] == b"o+e>|") && (&input[offset..offset + eo_pipe_len] == b"o+e>|")
{ {
*curr_offset += eo_pipe_len; *curr_offset += eo_pipe_len;
return Some(Token::new( return (
Some(Token::new(
TokenContents::OutErrGreaterPipe, TokenContents::OutErrGreaterPipe,
Span::new(span_offset + offset, span_offset + offset + eo_pipe_len), Span::new(span_offset + offset, span_offset + offset + eo_pipe_len),
)); )),
None,
);
} }
} }
None (None, None)
} }

View File

@ -1161,3 +1161,11 @@ fn command_not_found_error_shows_not_found_2() {
&& actual.err.contains("Did you mean `for`?") && actual.err.contains("Did you mean `for`?")
); );
} }
#[test]
fn error_on_out_greater_pipe() {
let actual = nu!(r#""foo" o>| print"#);
assert!(actual
.err
.contains("Redirecting stdout to a pipe is the same as normal piping"))
}