diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 6f6620394..a80883bb3 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -3401,11 +3401,9 @@ static bool test_1_parse_ll2(const wcstring &src, wcstring *out_cmd, wcstring *o } tnode_t stmt = stmts.at(0); - // Return its decoration. + // Return its decoration and command. *out_deco = tree.decoration_for_plain_statement(stmt); - - // Return its command. - tree.command_for_plain_statement(stmt, src, out_cmd); + *out_cmd = *command_for_plain_statement(stmt, src); // Return arguments separated by spaces. const parse_node_tree_t::parse_node_list_t arg_nodes = tree.find_nodes(stmt, symbol_argument); diff --git a/src/highlight.cpp b/src/highlight.cpp index 1cbff90b8..12abdb303 100644 --- a/src/highlight.cpp +++ b/src/highlight.cpp @@ -240,16 +240,14 @@ static bool is_potential_cd_path(const wcstring &path, const wcstring &working_d // Given a plain statement node in a parse tree, get the command and return it, expanded // appropriately for commands. If we succeed, return true. -bool plain_statement_get_expanded_command(const wcstring &src, const parse_node_tree_t &tree, - const parse_node_t &plain_statement, wcstring *out_cmd) { - assert(plain_statement.type == symbol_plain_statement); - +static bool plain_statement_get_expanded_command(const wcstring &src, + tnode_t stmt, + wcstring *out_cmd) { // Get the command. Try expanding it. If we cannot, it's an error. - wcstring cmd; - if (tree.command_for_plain_statement(plain_statement, src, &cmd) && - expand_one(cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES | EXPAND_SKIP_JOBS)) { + maybe_t cmd = command_for_plain_statement(stmt, src); + if (cmd && expand_one(*cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES | EXPAND_SKIP_JOBS)) { // Success, return the expanded string by reference. - *out_cmd = std::move(cmd); + *out_cmd = std::move(*cmd); return true; } return false; @@ -324,8 +322,8 @@ static bool autosuggest_parse_command(const wcstring &buff, wcstring *out_expand // Find the last statement. auto last_statement = parse_tree.find_last_node(); - if (last_statement && plain_statement_get_expanded_command(buff, parse_tree, *last_statement, - out_expanded_command)) { + if (last_statement && + plain_statement_get_expanded_command(buff, last_statement, out_expanded_command)) { // Find the last argument. If we don't get one, return an invalid node. if (auto last_arg = parse_tree.find_last_node(last_statement)) { *out_last_arg = last_arg.get_source(buff); @@ -803,8 +801,7 @@ void highlighter_t::color_arguments(const parse_node_t &list_node) { const parse_node_t *parent = this->parse_tree.get_parent(list_node, symbol_plain_statement); if (parent != NULL) { wcstring cmd_str; - if (plain_statement_get_expanded_command(this->buff, this->parse_tree, *parent, - &cmd_str)) { + if (plain_statement_get_expanded_command(this->buff, {&parse_tree, parent}, &cmd_str)) { cmd_is_cd = (cmd_str == L"cd"); } } diff --git a/src/history.cpp b/src/history.cpp index 9dd70968c..38649343e 100644 --- a/src/history.cpp +++ b/src/history.cpp @@ -1915,11 +1915,11 @@ void history_t::add_pending_with_file_detection(const wcstring &str) { impending_exit = true; } - wcstring command; - tree.command_for_plain_statement(node, str, &command); - unescape_string_in_place(&command, UNESCAPE_DEFAULT); - if (command == L"exit" || command == L"reboot") { - impending_exit = true; + if (maybe_t command = command_for_plain_statement({&tree, &node}, str)) { + unescape_string_in_place(&*command, UNESCAPE_DEFAULT); + if (*command == L"exit" || *command == L"reboot") { + impending_exit = true; + } } } } diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index c3c552816..e2a86851b 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -104,8 +104,9 @@ node_offset_t parse_execution_context_t::get_offset(const parse_node_t &node) co return offset; } -const parse_node_t *parse_execution_context_t::infinite_recursive_statement_in_job_list( - const parse_node_t &job_list, wcstring *out_func_name) const { +tnode_t +parse_execution_context_t::infinite_recursive_statement_in_job_list(const parse_node_t &job_list, + wcstring *out_func_name) const { assert(job_list.type == symbol_job_list); // This is a bit fragile. It is a test to see if we are inside of function call, but not inside // a block in that function call. If, in the future, the rules for what block scopes are pushed @@ -114,23 +115,23 @@ const parse_node_t *parse_execution_context_t::infinite_recursive_statement_in_j bool is_within_function_call = (current && parent && current->type() == TOP && parent->type() == FUNCTION_CALL); if (!is_within_function_call) { - return NULL; + return {}; } // Check to see which function call is forbidden. if (parser->forbidden_function.empty()) { - return NULL; + return {}; } const wcstring &forbidden_function_name = parser->forbidden_function.back(); // Get the first job in the job list. const parse_node_t *first_job = tree().next_node_in_node_list(job_list, symbol_job, NULL); if (first_job == NULL) { - return NULL; + return {}; } // Here's the statement node we find that's infinite recursive. - const parse_node_t *infinite_recursive_statement = NULL; + tnode_t infinite_recursive_statement; // Get the list of statements. const parse_node_tree_t::parse_node_list_t statements = @@ -144,8 +145,9 @@ const parse_node_t *parse_execution_context_t::infinite_recursive_statement_in_j if (statement.type != symbol_decorated_statement) { continue; } + tnode_t dec_statement(&tree(), &statement); - const parse_node_t &plain_statement = tree().find_child(statement, symbol_plain_statement); + auto plain_statement = tree().find_child(dec_statement); if (tree().decoration_for_plain_statement(plain_statement) != parse_statement_decoration_none) { // This statement has a decoration like 'builtin' or 'command', and therefore is not @@ -154,22 +156,17 @@ const parse_node_t *parse_execution_context_t::infinite_recursive_statement_in_j } // Ok, this is an undecorated plain statement. Get and expand its command. - wcstring cmd; - tree().command_for_plain_statement(plain_statement, pstree->src, &cmd); - - if (expand_one(cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES, NULL) && + maybe_t cmd = command_for_plain_statement(plain_statement, pstree->src); + if (cmd && expand_one(*cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES, NULL) && cmd == forbidden_function_name) { // This is it. - infinite_recursive_statement = &statement; + infinite_recursive_statement = plain_statement; if (out_func_name != NULL) { *out_func_name = forbidden_function_name; } break; } } - - assert(infinite_recursive_statement == NULL || - infinite_recursive_statement->type == symbol_decorated_statement); return infinite_recursive_statement; } @@ -829,9 +826,7 @@ parse_execution_result_t parse_execution_context_t::populate_plain_process( bool use_implicit_cd = false; // Get the command. We expect to always get it here. - wcstring cmd; - bool got_cmd = tree().command_for_plain_statement(statement, pstree->src, &cmd); - assert(got_cmd); + wcstring cmd = *command_for_plain_statement({&tree(), &statement}, pstree->src); // Expand it as a command. Return an error on failure. bool expanded = expand_one(cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES, NULL); @@ -1389,11 +1384,11 @@ parse_execution_result_t parse_execution_context_t::eval_node_at_offset( // execution (which does block statements, but never job lists). assert(offset == 0); wcstring func_name; - const parse_node_t *infinite_recursive_node = + auto infinite_recursive_node = this->infinite_recursive_statement_in_job_list(node, &func_name); - if (infinite_recursive_node != NULL) { + if (infinite_recursive_node) { // We have an infinite recursion. - this->report_error(*infinite_recursive_node, INFINITE_FUNC_RECURSION_ERR_MSG, + this->report_error(infinite_recursive_node, INFINITE_FUNC_RECURSION_ERR_MSG, func_name.c_str()); status = parse_execution_errored; } else { diff --git a/src/parse_execution.h b/src/parse_execution.h index f188524f8..c07265098 100644 --- a/src/parse_execution.h +++ b/src/parse_execution.h @@ -73,8 +73,8 @@ class parse_execution_context_t { const parse_node_t *get_child(const parse_node_t &parent, node_offset_t which, parse_token_type_t expected_type = token_type_invalid) const; node_offset_t get_offset(const parse_node_t &node) const; - const parse_node_t *infinite_recursive_statement_in_job_list(const parse_node_t &job_list, - wcstring *out_func_name) const; + tnode_t infinite_recursive_statement_in_job_list( + const parse_node_t &job_list, wcstring *out_func_name) const; bool is_function_context() const; /// Indicates whether a job is a simple block (one block, no redirections). diff --git a/src/parse_tree.cpp b/src/parse_tree.cpp index 53f706393..a44d2e9a2 100644 --- a/src/parse_tree.cpp +++ b/src/parse_tree.cpp @@ -1330,20 +1330,6 @@ enum parse_statement_decoration_t parse_node_tree_t::decoration_for_plain_statem return decoration; } -bool parse_node_tree_t::command_for_plain_statement(const parse_node_t &node, const wcstring &src, - wcstring *out_cmd) const { - bool result = false; - assert(node.type == symbol_plain_statement); - const parse_node_t *cmd_node = this->get_child(node, 0, parse_token_type_string); - if (cmd_node != NULL && cmd_node->has_source()) { - out_cmd->assign(src, cmd_node->source_start, cmd_node->source_length); - result = true; - } else { - out_cmd->clear(); - } - 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. @@ -1495,3 +1481,12 @@ const parse_node_t *parse_node_tree_t::next_node_in_node_list( if (out_list_tail != NULL) *out_list_tail = list_cursor; return list_entry; } + +maybe_t command_for_plain_statement(tnode_t stmt, + const wcstring &src) { + tnode_t cmd = stmt.child<0>(); + if (cmd && cmd.has_source()) { + return cmd.get_source(src); + } + return none(); +} diff --git a/src/parse_tree.h b/src/parse_tree.h index 6c04cc7ec..c9d0d4269 100644 --- a/src/parse_tree.h +++ b/src/parse_tree.h @@ -159,6 +159,9 @@ class parse_node_tree_t : public std::vector { // Find the first direct child of the given node of the given type. asserts on failure. const parse_node_t &find_child(const parse_node_t &parent, parse_token_type_t type) const; + template + tnode_t find_child(const parse_node_t &parent) const; + // Get the node corresponding to the parent of the given node, or NULL if there is no such // child. If expected_type is provided, only returns the parent if it is of that type. Note the // asymmetry: get_child asserts since the children are known, but get_parent does not, since the @@ -197,11 +200,6 @@ class parse_node_tree_t : public std::vector { enum parse_statement_decoration_t decoration_for_plain_statement( const parse_node_t &node) const; - /// Given a plain statement, get the command by reference (from the child node). Returns true if - /// successful. Clears the command on failure. - bool command_for_plain_statement(const parse_node_t &node, const wcstring &src, - wcstring *out_cmd) const; - /// 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. @@ -349,6 +347,11 @@ class tnode_t { } }; +template +tnode_t parse_node_tree_t::find_child(const parse_node_t &parent) const { + return tnode_t(this, &this->find_child(parent, Type::token)); +} + template tnode_t parse_node_tree_t::find_last_node(const parse_node_t *parent) const { return tnode_t(this, this->find_last_node_of_type(Type::token, parent)); @@ -366,6 +369,11 @@ std::vector> parse_node_tree_t::find_nodes(const parse_node_t &par return result; } +/// Given a plain statement, get the command from the child node. Returns the command string on +/// success, none on failure. +maybe_t command_for_plain_statement(tnode_t stmt, + const wcstring &src); + /// The big entry point. Parse a string, attempting to produce a tree for the given goal type. bool parse_tree_from_string(const wcstring &str, parse_tree_flags_t flags, parse_node_tree_t *output, parse_error_list_t *errors, diff --git a/src/parse_util.cpp b/src/parse_util.cpp index ed2f8e77f..dc89509dc 100644 --- a/src/parse_util.cpp +++ b/src/parse_util.cpp @@ -1188,8 +1188,9 @@ parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, L"exec"); } - wcstring command; - if (node_tree.command_for_plain_statement(node, buff_src, &command)) { + if (maybe_t mcommand = + command_for_plain_statement({&node_tree, &node}, buff_src)) { + wcstring command = std::move(*mcommand); // Check that we can expand the command. if (!expand_one(command, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES | EXPAND_SKIP_JOBS,