From ddd1afc57c906c019e864462dd51b572afa501f4 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 18 Feb 2018 13:13:58 -0800 Subject: [PATCH] Teach parse_util_detect_errors about unterminated pipelines Allow it to return PARSER_TEST_INCOMPLETE for code like `echo | ` --- src/fish_tests.cpp | 12 ++++++++++++ src/parse_util.cpp | 10 +++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index ef14f9ede..5252a2b3a 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -727,6 +727,18 @@ static void test_parser() { err(L"redirection after 'end' wrongly reported as error"); } + if (parse_util_detect_errors(L"true | ") != PARSER_TEST_INCOMPLETE) { + err(L"unterminated pipe not reported properly"); + } + + if (parse_util_detect_errors(L"begin ; true ; end | ") != PARSER_TEST_INCOMPLETE) { + err(L"unterminated pipe not reported properly"); + } + + if (parse_util_detect_errors(L" | true ") != PARSER_TEST_ERROR) { + err(L"leading pipe not reported properly"); + } + if (detect_argument_errors(L"foo")) { err(L"simple argument reported as error"); } diff --git a/src/parse_util.cpp b/src/parse_util.cpp index bf911af8d..507162d69 100644 --- a/src/parse_util.cpp +++ b/src/parse_util.cpp @@ -1198,6 +1198,10 @@ parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, // source. bool has_unclosed_block = false; + // Whether we encounter a missing statement, i.e. a newline after a pipe. This is found by + // detecting job_continuations that have source for pipes but not the statement. + bool has_unclosed_pipe = false; + // Whether there's an unclosed quote, and therefore unfinished. This is only set if // allow_incomplete is set. bool has_unclosed_quote = false; @@ -1243,6 +1247,9 @@ parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, if (node.type == symbol_end_command && !node.has_source()) { // An 'end' without source is an unclosed block. has_unclosed_block = true; + } else if (node.type == symbol_statement && !node.has_source()) { + // Check for a statement without source in a pipeline, i.e. unterminated pipeline. + has_unclosed_pipe |= statement_is_in_pipeline({&node_tree, &node}, false); } else if (node.type == symbol_boolean_statement) { // 'or' and 'and' can be in a pipeline, as long as they're first. tnode_t gbs{&node_tree, &node}; @@ -1290,7 +1297,8 @@ parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, if (errored) res |= PARSER_TEST_ERROR; - if (has_unclosed_block || has_unclosed_quote) res |= PARSER_TEST_INCOMPLETE; + if (has_unclosed_block || has_unclosed_quote || has_unclosed_pipe) + res |= PARSER_TEST_INCOMPLETE; if (out_errors != NULL) { *out_errors = std::move(parse_errors);