Fix for issue where unterminated quotes would attempt to be executed,

instead of continuing edit onto the next line.
This commit is contained in:
ridiculousfish 2014-01-14 00:01:26 -08:00
parent b9394b9599
commit dc8014562b
6 changed files with 55 additions and 11 deletions

View File

@ -4298,7 +4298,7 @@ int builtin_parse(parser_t &parser, wchar_t **argv)
const wcstring src = str2wcstring(&txt.at(0), txt.size());
parse_node_tree_t parse_tree;
parse_error_list_t errors;
bool success = parse_tree_from_string(src, parse_flag_none, &parse_tree, &errors, true);
bool success = parse_tree_from_string(src, parse_flag_none, &parse_tree, &errors);
if (! success)
{
stdout_buffer.append(L"Parsing failed:\n");
@ -4311,7 +4311,7 @@ int builtin_parse(parser_t &parser, wchar_t **argv)
stdout_buffer.append(L"(Reparsed with continue after error)\n");
parse_tree.clear();
errors.clear();
parse_tree_from_string(src, parse_flag_continue_after_error, &parse_tree, &errors, true);
parse_tree_from_string(src, parse_flag_continue_after_error, &parse_tree, &errors);
}
const wcstring dump = parse_dump_tree(parse_tree, src);
stdout_buffer.append(dump);

View File

@ -2452,7 +2452,8 @@ static void test_new_parser_errors(void)
}
tests[] =
{
{L"echo (abc", parse_error_tokenizer},
{L"echo 'abc", parse_error_tokenizer_unterminated_quote},
{L"echo (abc", parse_error_tokenizer_unterminated_subshell},
{L"end", parse_error_unbalancing_end},
{L"echo hi ; end", parse_error_unbalancing_end},

View File

@ -114,7 +114,11 @@ enum parse_error_code_t
parse_error_generic, // unclassified error types
parse_error_tokenizer, //tokenizer error
//tokenizer errors
parse_error_tokenizer_unterminated_quote,
parse_error_tokenizer_unterminated_subshell,
parse_error_tokenizer_unterminated_escape,
parse_error_tokenizer_other,
parse_error_unbalancing_end, //end outside of block
parse_error_unbalancing_else, //else outside of if

View File

@ -583,7 +583,7 @@ class parse_ll_t
void accept_tokens(parse_token_t token1, parse_token_t token2);
/* Report tokenizer errors */
void report_tokenizer_error(parse_token_t token, const wchar_t *tok_error);
void report_tokenizer_error(parse_token_t token, int tok_err, const wchar_t *tok_error);
/* Indicate if we hit a fatal error */
bool has_fatal_error(void) const
@ -786,10 +786,31 @@ void parse_ll_t::parse_error_failed_production(struct parse_stack_element_t &sta
}
}
void parse_ll_t::report_tokenizer_error(parse_token_t token, const wchar_t *tok_error)
void parse_ll_t::report_tokenizer_error(parse_token_t token, int tok_err_code, const wchar_t *tok_error)
{
assert(tok_error != NULL);
this->parse_error(token, parse_error_tokenizer, L"%ls", tok_error);
parse_error_code_t parse_error_code;
switch (tok_err_code)
{
case TOK_UNTERMINATED_QUOTE:
parse_error_code = parse_error_tokenizer_unterminated_quote;
break;
case TOK_UNTERMINATED_SUBSHELL:
parse_error_code = parse_error_tokenizer_unterminated_subshell;
break;
case TOK_UNTERMINATED_ESCAPE:
parse_error_code = parse_error_tokenizer_unterminated_escape;
break;
case TOK_OTHER:
default:
parse_error_code = parse_error_tokenizer_other;
break;
}
this->parse_error(token, parse_error_code, L"%ls", tok_error);
}
void parse_ll_t::parse_error(const wchar_t *expected, parse_token_t token)
@ -1076,7 +1097,7 @@ static inline parse_token_t next_parse_token(tokenizer_t *tok)
return result;
}
bool parse_tree_from_string(const wcstring &str, parse_tree_flags_t parse_flags, parse_node_tree_t *output, parse_error_list_t *errors, bool log_it)
bool parse_tree_from_string(const wcstring &str, parse_tree_flags_t parse_flags, parse_node_tree_t *output, parse_error_list_t *errors)
{
parse_ll_t parser;
parser.set_should_generate_error_messages(errors != NULL);
@ -1116,7 +1137,7 @@ bool parse_tree_from_string(const wcstring &str, parse_tree_flags_t parse_flags,
/* Handle tokenizer errors. This is a hack because really the parser should report this for itself; but it has no way of getting the tokenizer message */
if (queue[1].type == parse_special_type_tokenizer_error)
{
parser.report_tokenizer_error(queue[1], tok_last(&tok));
parser.report_tokenizer_error(queue[1], tok_get_error(&tok), tok_last(&tok));
}
/* Handle errors */

View File

@ -197,7 +197,7 @@ public:
};
/* The big entry point. Parse a string! */
bool parse_tree_from_string(const wcstring &str, parse_tree_flags_t flags, parse_node_tree_t *output, parse_error_list_t *errors, bool log_it = false);
bool parse_tree_from_string(const wcstring &str, parse_tree_flags_t flags, parse_node_tree_t *output, parse_error_list_t *errors);
/* Fish grammar:

View File

@ -992,9 +992,27 @@ parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, pars
// We detect this via an 'end_command' block without source
bool has_unclosed_block = false;
// Whether there's an unclosed quote, and therefore unfinished
bool has_unclosed_quote = false;
// Parse the input string into a parse tree
// Some errors are detected here
bool parsed = parse_tree_from_string(buff_src, parse_flag_leave_unterminated, &node_tree, &parse_errors);
for (size_t i=0; i < parse_errors.size(); i++)
{
if (parse_errors.at(i).code == parse_error_tokenizer_unterminated_quote)
{
// Remove this error, since we don't consider it a real error
has_unclosed_quote = true;
parse_errors.erase(parse_errors.begin() + i);
i--;
}
}
// #1238: If the only error was unterminated quote, then consider this to have parsed successfully. A better fix would be to have parse_tree_from_string return this information directly (but it would be a shame to munge up its nice bool return).
if (parse_errors.empty() && has_unclosed_quote)
parsed = true;
if (! parsed)
{
errored = true;
@ -1120,7 +1138,7 @@ parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, pars
if (errored)
res |= PARSER_TEST_ERROR;
if (has_unclosed_block)
if (has_unclosed_block || has_unclosed_quote)
res |= PARSER_TEST_INCOMPLETE;
if (out_errors)