Make statement_is_in_pipeline a free typesafe function

This commit is contained in:
ridiculousfish 2018-01-18 11:27:23 -08:00
parent 194f7f34d9
commit 1c2943bd8b
3 changed files with 38 additions and 44 deletions

View file

@ -1315,35 +1315,6 @@ enum token_type redirection_type(tnode_t<grammar::redirection> 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<tnode_t<grammar::comment>> parse_node_tree_t::comment_nodes_for_node(
const parse_node_t &parent) const {
std::vector<tnode_t<grammar::comment>> result;
@ -1420,3 +1391,25 @@ bool job_node_is_background(tnode_t<grammar::job> job) {
tnode_t<grammar::optional_background> bg = job.child<2>();
return bg.tag() == parse_background;
}
bool statement_is_in_pipeline(tnode_t<grammar::statement> 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<job_continuation>()) {
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<job_continuation> jc = st.try_get_parent<job>().child<1>();
if (jc.try_get_child<statement, 1>()) {
return true;
}
}
return false;
}

View file

@ -181,11 +181,6 @@ class parse_node_tree_t : public std::vector<parse_node_t> {
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<tnode_t<grammar::comment>> comment_nodes_for_node(const parse_node_t &node) const;
@ -429,6 +424,10 @@ arguments_node_list_t get_argument_nodes(tnode_t<grammar::arguments_or_redirecti
/// Return whether the given job is background because it has a & symbol.
bool job_node_is_background(tnode_t<grammar::job>);
/// 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<grammar::statement> st, bool include_first);
/// Check whether an argument_list is a root list.
inline bool argument_list_is_root(tnode_t<grammar::argument_list> list) {
return !list.try_get_parent<grammar::argument_list>();

View file

@ -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<grammar::boolean_statement> 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<grammar::statement>(),
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<plain_statement> statement{&node_tree, &node};
tnode_t<plain_statement> 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<statement> st =
pst.try_get_parent<decorated_statement>().try_get_parent<statement>();
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<wcstring> mcommand =
command_for_plain_statement({&node_tree, &node}, buff_src)) {
if (maybe_t<wcstring> 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