diff --git a/src/builtin_complete.cpp b/src/builtin_complete.cpp index 8f33632bb..8ae0bb528 100644 --- a/src/builtin_complete.cpp +++ b/src/builtin_complete.cpp @@ -303,11 +303,10 @@ int builtin_complete(parser_t &parser, io_streams_t &streams, wchar_t **argv) { prefix.append(cmd); prefix.append(L": "); - wcstring err_text; - if (parser.detect_errors_in_argument_list(comp, &err_text, prefix.c_str())) { + if (maybe_t err_text = parse_util_detect_errors_in_argument_list(comp, prefix)) { streams.err.append_format(L"%ls: Completion '%ls' contained a syntax error\n", cmd, comp); - streams.err.append(err_text); + streams.err.append(*err_text); streams.err.push_back(L'\n'); return STATUS_CMD_ERROR; } diff --git a/src/parse_util.cpp b/src/parse_util.cpp index 7f5c14f05..b4348f7d3 100644 --- a/src/parse_util.cpp +++ b/src/parse_util.cpp @@ -1333,3 +1333,35 @@ parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, return res; } + +maybe_t parse_util_detect_errors_in_argument_list(const wcstring &arg_list_src, + const wcstring &prefix) { + // Helper to return a description of the first error. + auto get_error_text = [&](const parse_error_list_t &errors) { + assert(!errors.empty() && "Expected an error"); + return errors.at(0).describe_with_prefix(arg_list_src, prefix, false /* not interactive */, + false /* don't skip caret */); + }; + + // Parse the string as an argument list. + parse_error_list_t errors; + parse_node_tree_t tree; + if (!parse_tree_from_string(arg_list_src, parse_flag_none, &tree, &errors, + symbol_freestanding_argument_list)) { + // Failed to parse. + return get_error_text(errors); + } + + // Get the root argument list and extract arguments from it. + // Test each of these. + assert(!tree.empty() && "Should have parsed a tree"); + tnode_t arg_list(&tree, &tree.at(0)); + while (auto arg = arg_list.next_in_list()) { + const wcstring arg_src = arg.get_source(arg_list_src); + if (parse_util_detect_errors_in_argument(arg, arg_src, &errors)) { + return get_error_text(errors); + } + } + + return none(); +} diff --git a/src/parse_util.h b/src/parse_util.h index 74438544c..e862b796f 100644 --- a/src/parse_util.h +++ b/src/parse_util.h @@ -132,6 +132,11 @@ parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, bool allow_incomplete = true, parsed_source_ref_t *out_pstree = NULL); +/// Detect errors in the specified string when parsed as an argument list. Returns the text of an +/// error, or none if no error occurred. +maybe_t parse_util_detect_errors_in_argument_list(const wcstring &arg_list_src, + const wcstring &prefix = {}); + /// Test if this argument contains any errors. Detected errors include syntax errors in command /// substitutions, improperly escaped characters and improper use of the variable expansion /// operator. This does NOT currently detect unterminated quotes. diff --git a/src/parser.cpp b/src/parser.cpp index 73d7e10b7..bfaf9a003 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -680,41 +680,6 @@ template int parser_t::eval_node(parsed_source_ref_t, tnode_t const io_chain_t &, enum block_type_t, std::shared_ptr parent_job); -bool parser_t::detect_errors_in_argument_list(const wcstring &arg_list_src, wcstring *out, - const wchar_t *prefix) const { - bool errored = false; - parse_error_list_t errors; - - // Use empty string for the prefix if it's NULL. - if (!prefix) prefix = L""; //!OCLINT(parameter reassignment) - - // Parse the string as an argument list. - parse_node_tree_t tree; - if (!parse_tree_from_string(arg_list_src, parse_flag_none, &tree, &errors, - symbol_freestanding_argument_list)) { - // Failed to parse. - errored = true; - } - - if (!errored) { - // Get the root argument list and extract arguments from it. - assert(!tree.empty()); //!OCLINT(multiple unary operator) - tnode_t arg_list(&tree, &tree.at(0)); - while (auto arg = arg_list.next_in_list()) { - const wcstring arg_src = arg.get_source(arg_list_src); - if (parse_util_detect_errors_in_argument(arg, arg_src, &errors)) { - errored = true; - } - } - } - - if (!errors.empty() && out != NULL) { - out->assign(errors.at(0).describe_with_prefix( - arg_list_src, prefix, false /* not interactive */, false /* don't skip caret */)); - } - return errored; -} - void parser_t::get_backtrace(const wcstring &src, const parse_error_list_t &errors, wcstring &output) const { if (!errors.empty()) { diff --git a/src/parser.h b/src/parser.h index c522be72d..09842702d 100644 --- a/src/parser.h +++ b/src/parser.h @@ -335,11 +335,6 @@ class parser_t : public std::enable_shared_from_this { void get_backtrace(const wcstring &src, const parse_error_list_t &errors, wcstring &output) const; - /// Detect errors in the specified string when parsed as an argument list. Returns true if an - /// error occurred. - bool detect_errors_in_argument_list(const wcstring &arg_list_src, wcstring *out_err, - const wchar_t *prefix) const; - /// Tell the parser that the specified function may not be run if not inside of a conditional /// block. This is to remove some possibilities of infinite recursion. void forbid_function(const wcstring &function);