forked from extern/nushell
redirect should have a target (#10835)
# Description Fixes: #10830 The issue happened during lite-parsing, when we want to put a `LiteElement` to a `LitePipeline`, we do nothing if relative redirection target is empty. So the command `echo aaa o> | ignore` will be interpreted to `echo aaa | ignore`. This pr is going to check and return an error if redirection target is empty. # User-Facing Changes ## Before ``` ❯ echo aaa o> | ignore # nothing happened ``` ## After ```nushell ❯ echo aaa o> | ignore Error: nu::parser::parse_mismatch × Parse mismatch during operation. ╭─[entry #1:1:1] 1 │ echo aaa o> | ignore · ─┬ · ╰── expected redirection target ╰──── ```
This commit is contained in:
parent
c6016d7659
commit
f043a8a8ff
@ -264,3 +264,25 @@ fn separate_redirection_support_variable() {
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn redirection_should_have_a_target() {
|
||||
let scripts = [
|
||||
"echo asdf o+e>",
|
||||
"echo asdf o>",
|
||||
"echo asdf e>",
|
||||
"echo asdf o> e>",
|
||||
"echo asdf o> tmp.txt e>",
|
||||
"echo asdf o> e> tmp.txt",
|
||||
"echo asdf o> | ignore",
|
||||
"echo asdf o>; echo asdf",
|
||||
];
|
||||
for code in scripts {
|
||||
let actual = nu!(code);
|
||||
assert!(
|
||||
actual.err.contains("expected redirection target",),
|
||||
"should be error, code: {}",
|
||||
code
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -235,12 +235,14 @@ pub fn lite_parse(tokens: &[Token]) -> (LiteBlock, Option<ParseError>) {
|
||||
TokenContents::OutGreaterThan
|
||||
| TokenContents::ErrGreaterThan
|
||||
| TokenContents::OutErrGreaterThan => {
|
||||
push_command_to(
|
||||
if let Some(err) = push_command_to(
|
||||
&mut curr_pipeline,
|
||||
curr_command,
|
||||
last_connector,
|
||||
last_connector_span,
|
||||
);
|
||||
) {
|
||||
error = Some(err);
|
||||
}
|
||||
|
||||
curr_command = LiteCommand::new();
|
||||
last_token = token.contents;
|
||||
@ -248,12 +250,14 @@ pub fn lite_parse(tokens: &[Token]) -> (LiteBlock, Option<ParseError>) {
|
||||
last_connector_span = Some(token.span);
|
||||
}
|
||||
TokenContents::Pipe => {
|
||||
push_command_to(
|
||||
if let Some(err) = push_command_to(
|
||||
&mut curr_pipeline,
|
||||
curr_command,
|
||||
last_connector,
|
||||
last_connector_span,
|
||||
);
|
||||
) {
|
||||
error = Some(err);
|
||||
}
|
||||
|
||||
curr_command = LiteCommand::new();
|
||||
last_token = TokenContents::Pipe;
|
||||
@ -269,12 +273,14 @@ pub fn lite_parse(tokens: &[Token]) -> (LiteBlock, Option<ParseError>) {
|
||||
if actual_token != Some(TokenContents::Pipe)
|
||||
&& actual_token != Some(TokenContents::OutGreaterThan)
|
||||
{
|
||||
push_command_to(
|
||||
if let Some(err) = push_command_to(
|
||||
&mut curr_pipeline,
|
||||
curr_command,
|
||||
last_connector,
|
||||
last_connector_span,
|
||||
);
|
||||
) {
|
||||
error = Some(err);
|
||||
}
|
||||
|
||||
curr_command = LiteCommand::new();
|
||||
if !curr_pipeline.is_empty() {
|
||||
@ -294,12 +300,14 @@ pub fn lite_parse(tokens: &[Token]) -> (LiteBlock, Option<ParseError>) {
|
||||
last_token = TokenContents::Eol;
|
||||
}
|
||||
TokenContents::Semicolon => {
|
||||
push_command_to(
|
||||
if let Some(err) = push_command_to(
|
||||
&mut curr_pipeline,
|
||||
curr_command,
|
||||
last_connector,
|
||||
last_connector_span,
|
||||
);
|
||||
) {
|
||||
error = Some(err);
|
||||
}
|
||||
|
||||
curr_command = LiteCommand::new();
|
||||
if !curr_pipeline.is_empty() {
|
||||
@ -331,12 +339,14 @@ pub fn lite_parse(tokens: &[Token]) -> (LiteBlock, Option<ParseError>) {
|
||||
}
|
||||
}
|
||||
|
||||
push_command_to(
|
||||
if let Some(err) = push_command_to(
|
||||
&mut curr_pipeline,
|
||||
curr_command,
|
||||
last_connector,
|
||||
last_connector_span,
|
||||
);
|
||||
) {
|
||||
error = Some(err);
|
||||
}
|
||||
if !curr_pipeline.is_empty() {
|
||||
block.push(curr_pipeline);
|
||||
}
|
||||
@ -354,12 +364,16 @@ pub fn lite_parse(tokens: &[Token]) -> (LiteBlock, Option<ParseError>) {
|
||||
}
|
||||
}
|
||||
|
||||
/// push a `command` to `pipeline`
|
||||
///
|
||||
/// It will return Some(err) if `command` is empty and we want to push a
|
||||
/// redirection command.
|
||||
fn push_command_to(
|
||||
pipeline: &mut LitePipeline,
|
||||
command: LiteCommand,
|
||||
last_connector: TokenContents,
|
||||
last_connector_span: Option<Span>,
|
||||
) {
|
||||
) -> Option<ParseError> {
|
||||
if !command.is_empty() {
|
||||
match last_connector {
|
||||
TokenContents::OutGreaterThan => {
|
||||
@ -390,5 +404,16 @@ fn push_command_to(
|
||||
pipeline.push(LiteElement::Command(last_connector_span, command));
|
||||
}
|
||||
}
|
||||
None
|
||||
} else {
|
||||
match last_connector {
|
||||
TokenContents::OutGreaterThan
|
||||
| TokenContents::ErrGreaterThan
|
||||
| TokenContents::OutErrGreaterThan => Some(ParseError::Expected(
|
||||
"redirection target",
|
||||
last_connector_span.expect("internal error: redirection missing span information"),
|
||||
)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user