From 1c2943bd8b099ef79b7893619acd801bedd02222 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Thu, 18 Jan 2018 11:27:23 -0800 Subject: [PATCH] Make statement_is_in_pipeline a free typesafe function --- src/parse_tree.cpp | 51 ++++++++++++++++++++-------------------------- src/parse_tree.h | 9 ++++---- src/parse_util.cpp | 22 +++++++++++--------- 3 files changed, 38 insertions(+), 44 deletions(-) diff --git a/src/parse_tree.cpp b/src/parse_tree.cpp index 64008d77c..8f74f575c 100644 --- a/src/parse_tree.cpp +++ b/src/parse_tree.cpp @@ -1315,35 +1315,6 @@ enum token_type redirection_type(tnode_t redirection, cons return result; } -bool parse_node_tree_t::statement_is_in_pipeline(const parse_node_t &node, - bool include_first) const { - // Moderately nasty hack! Walk up our ancestor chain and see if we are in a job_continuation. - // This checks if we are in the second or greater element in a pipeline; if we are the first - // element we treat this as false. This accepts a few statement types. - bool result = false; - const parse_node_t *ancestor = &node; - - // If we're given a plain statement, try to get its decorated statement parent. - if (ancestor && ancestor->type == symbol_plain_statement) - ancestor = this->get_parent(*ancestor, symbol_decorated_statement); - if (ancestor) ancestor = this->get_parent(*ancestor, symbol_statement); - if (ancestor) ancestor = this->get_parent(*ancestor); - - if (ancestor) { - if (ancestor->type == symbol_job_continuation) { - // Second or more in a pipeline. - result = true; - } else if (ancestor->type == symbol_job && include_first) { - // Check to see if we have a job continuation that's not empty. - const parse_node_t *continuation = - this->get_child(*ancestor, 1, symbol_job_continuation); - result = (continuation != NULL && continuation->child_count > 0); - } - } - - return result; -} - std::vector> parse_node_tree_t::comment_nodes_for_node( const parse_node_t &parent) const { std::vector> result; @@ -1420,3 +1391,25 @@ bool job_node_is_background(tnode_t job) { tnode_t bg = job.child<2>(); return bg.tag() == parse_background; } + +bool statement_is_in_pipeline(tnode_t st, bool include_first) { + using namespace grammar; + if (!st) { + return false; + } + + // If we're part of a job continuation, we're definitely in a pipeline. + if (st.try_get_parent()) { + return true; + } + + // If include_first is set, check if we're the beginning of a job, and if so, whether that job + // has a non-empty continuation. + if (include_first) { + tnode_t jc = st.try_get_parent().child<1>(); + if (jc.try_get_child()) { + return true; + } + } + return false; +} diff --git a/src/parse_tree.h b/src/parse_tree.h index 5855ec89a..59a2250a8 100644 --- a/src/parse_tree.h +++ b/src/parse_tree.h @@ -181,11 +181,6 @@ class parse_node_tree_t : public std::vector { const parse_node_t *parent) const; // Utilities - /// Given a plain statement, return true if the statement is part of a pipeline. If - /// include_first is set, the first command in a pipeline is considered part of it; otherwise - /// only the second or additional commands are. - bool statement_is_in_pipeline(const parse_node_t &node, bool include_first) const; - /// Given a node, return all of its comment nodes. std::vector> comment_nodes_for_node(const parse_node_t &node) const; @@ -429,6 +424,10 @@ arguments_node_list_t get_argument_nodes(tnode_t); +/// Return whether the statement is part of a pipeline. If include_first is set, the first command +/// in a pipeline is considered part of it; otherwise only the second or additional commands are. +bool statement_is_in_pipeline(tnode_t st, bool include_first); + /// Check whether an argument_list is a root list. inline bool argument_list_is_root(tnode_t list) { return !list.try_get_parent(); diff --git a/src/parse_util.cpp b/src/parse_util.cpp index 5cfada44f..ae54380ae 100644 --- a/src/parse_util.cpp +++ b/src/parse_util.cpp @@ -1142,9 +1142,11 @@ parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, has_unclosed_block = true; } else if (node.type == symbol_boolean_statement) { // 'or' and 'and' can be in a pipeline, as long as they're first. - parse_bool_statement_type_t type = bool_statement_type({&node_tree, &node}); + tnode_t gbs{&node_tree, &node}; + parse_bool_statement_type_t type = bool_statement_type(gbs); if ((type == parse_bool_and || type == parse_bool_or) && - node_tree.statement_is_in_pipeline(node, false /* don't count first */)) { + statement_is_in_pipeline(gbs.try_get_parent(), + false /* don't count first */)) { errored = append_syntax_error(&parse_errors, node.source_start, EXEC_ERR_MSG, (type == parse_bool_and) ? L"and" : L"or"); } @@ -1166,13 +1168,14 @@ parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, } } else if (node.type == symbol_plain_statement) { using namespace grammar; - tnode_t statement{&node_tree, &node}; + tnode_t pst{&node_tree, &node}; // In a few places below, we want to know if we are in a pipeline. - const bool is_in_pipeline = - node_tree.statement_is_in_pipeline(statement, true /* count first */); + tnode_t st = + pst.try_get_parent().try_get_parent(); + const bool is_in_pipeline = statement_is_in_pipeline(st, true /* count first */); // We need to know the decoration. - const enum parse_statement_decoration_t decoration = get_decoration(statement); + const enum parse_statement_decoration_t decoration = get_decoration(pst); // Check that we don't try to pipe through exec. if (is_in_pipeline && decoration == parse_statement_decoration_exec) { @@ -1180,8 +1183,7 @@ parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, L"exec"); } - if (maybe_t mcommand = - command_for_plain_statement({&node_tree, &node}, buff_src)) { + if (maybe_t mcommand = command_for_plain_statement(pst, buff_src)) { wcstring command = std::move(*mcommand); // Check that we can expand the command. if (!expand_one(command, @@ -1212,7 +1214,7 @@ parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, break; } } - if (!found_function && !first_argument_is_help(statement, buff_src)) { + if (!found_function && !first_argument_is_help(pst, buff_src)) { errored = append_syntax_error(&parse_errors, node.source_start, INVALID_RETURN_ERR_MSG); } @@ -1244,7 +1246,7 @@ parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, } } - if (!found_loop && !first_argument_is_help(statement, buff_src)) { + if (!found_loop && !first_argument_is_help(pst, buff_src)) { errored = append_syntax_error( &parse_errors, node.source_start, (command == L"break" ? INVALID_BREAK_ERR_MSG