Excise use of parser_t's error() functionality. Thread a

parse_error_list_t through all of the expand functions, enabling them to
report errors more directly. Improve aspects of error reporting for
expansion failures.
This commit is contained in:
ridiculousfish 2014-03-21 17:13:33 -07:00
parent c71b168402
commit ad6367018b
12 changed files with 221 additions and 152 deletions

View file

@ -967,7 +967,7 @@ void completer_t::complete_strings(const wcstring &wc_escaped,
complete_flags_t flags) complete_flags_t flags)
{ {
wcstring tmp = wc_escaped; wcstring tmp = wc_escaped;
if (! expand_one(tmp, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_WILDCARDS | this->expand_flags())) if (! expand_one(tmp, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_WILDCARDS | this->expand_flags(), NULL))
return; return;
const wchar_t *wc = parse_util_unescape_wildcards(tmp.c_str()); const wchar_t *wc = parse_util_unescape_wildcards(tmp.c_str());
@ -1146,7 +1146,7 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool
if (use_command) if (use_command)
{ {
if (expand_string(str_cmd, this->completions, ACCEPT_INCOMPLETE | EXECUTABLES_ONLY | this->expand_flags()) != EXPAND_ERROR) if (expand_string(str_cmd, this->completions, ACCEPT_INCOMPLETE | EXECUTABLES_ONLY | this->expand_flags(), NULL) != EXPAND_ERROR)
{ {
if (this->wants_descriptions()) if (this->wants_descriptions())
{ {
@ -1179,7 +1179,7 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool
size_t prev_count = this->completions.size(); size_t prev_count = this->completions.size();
if (expand_string(nxt_completion, if (expand_string(nxt_completion,
this->completions, this->completions,
ACCEPT_INCOMPLETE | EXECUTABLES_ONLY | this->expand_flags()) != EXPAND_ERROR) ACCEPT_INCOMPLETE | EXECUTABLES_ONLY | this->expand_flags(), NULL) != EXPAND_ERROR)
{ {
/* For all new completions, if COMPLETE_NO_CASE is set, then use only the last path component */ /* For all new completions, if COMPLETE_NO_CASE is set, then use only the last path component */
for (size_t i=prev_count; i< this->completions.size(); i++) for (size_t i=prev_count; i< this->completions.size(); i++)
@ -1641,7 +1641,7 @@ void completer_t::complete_param_expand(const wcstring &sstr, bool do_file)
if (expand_string(comp_str, if (expand_string(comp_str,
this->completions, this->completions,
flags) == EXPAND_ERROR) flags, NULL) == EXPAND_ERROR)
{ {
debug(3, L"Error while expanding string '%ls'", comp_str); debug(3, L"Error while expanding string '%ls'", comp_str);
} }

View file

@ -131,6 +131,46 @@ int expand_is_clean(const wchar_t *in)
return 1; return 1;
} }
/* Append a syntax error to the given error list */
static void append_syntax_error(parse_error_list_t *errors, size_t source_start, const wchar_t *fmt, ...)
{
if (errors != NULL)
{
parse_error_t error;
error.source_start = source_start;
error.source_length = 0;
error.code = parse_error_syntax;
va_list va;
va_start(va, fmt);
error.text = vformat_string(fmt, va);
va_end(va);
errors->push_back(error);
}
}
/* Append a cmdsub error to the given error list */
static void append_cmdsub_error(parse_error_list_t *errors, size_t source_start, const wchar_t *fmt, ...)
{
if (errors != NULL)
{
parse_error_t error;
error.source_start = source_start;
error.source_length = 0;
error.code = parse_error_cmdsubst;
va_list va;
va_start(va, fmt);
error.text = vformat_string(fmt, va);
va_end(va);
errors->push_back(error);
}
}
/** /**
Return the environment variable value for the string starting at \c in. Return the environment variable value for the string starting at \c in.
*/ */
@ -834,7 +874,7 @@ static int expand_pid(const wcstring &instr_with_sep,
} }
void expand_variable_error(parser_t &parser, const wcstring &token, size_t token_pos, int error_pos) void expand_variable_error(parser_t &parser, const wcstring &token, size_t token_pos, int error_pos, parse_error_list_t *errors)
{ {
size_t stop_pos = token_pos+1; size_t stop_pos = token_pos+1;
@ -861,7 +901,7 @@ void expand_variable_error(parser_t &parser, const wcstring &token, size_t token
if (is_var) if (is_var)
{ {
parser.error(SYNTAX_ERROR, append_syntax_error(errors,
error_pos, error_pos,
COMPLETE_VAR_BRACKET_DESC, COMPLETE_VAR_BRACKET_DESC,
cpy, cpy,
@ -870,7 +910,7 @@ void expand_variable_error(parser_t &parser, const wcstring &token, size_t token
} }
else else
{ {
parser.error(SYNTAX_ERROR, append_syntax_error(errors,
error_pos, error_pos,
COMPLETE_VAR_BRACKET_DESC, COMPLETE_VAR_BRACKET_DESC,
L"", L"",
@ -884,7 +924,7 @@ void expand_variable_error(parser_t &parser, const wcstring &token, size_t token
case INTERNAL_SEPARATOR: case INTERNAL_SEPARATOR:
{ {
parser.error(SYNTAX_ERROR, append_syntax_error(errors,
error_pos, error_pos,
COMPLETE_VAR_PARAN_DESC); COMPLETE_VAR_PARAN_DESC);
break; break;
@ -892,7 +932,7 @@ void expand_variable_error(parser_t &parser, const wcstring &token, size_t token
case 0: case 0:
{ {
parser.error(SYNTAX_ERROR, append_syntax_error(errors,
error_pos, error_pos,
COMPLETE_VAR_NULL_DESC); COMPLETE_VAR_NULL_DESC);
break; break;
@ -907,7 +947,7 @@ void expand_variable_error(parser_t &parser, const wcstring &token, size_t token
else if (token_stop_char == ANY_STRING || token_stop_char == ANY_STRING_RECURSIVE) else if (token_stop_char == ANY_STRING || token_stop_char == ANY_STRING_RECURSIVE)
token_stop_char = L'*'; token_stop_char = L'*';
parser.error(SYNTAX_ERROR, append_syntax_error(errors,
error_pos, error_pos,
(token_stop_char == L'?' ? COMPLETE_YOU_WANT_STATUS : COMPLETE_VAR_DESC), (token_stop_char == L'?' ? COMPLETE_YOU_WANT_STATUS : COMPLETE_VAR_DESC),
token_stop_char); token_stop_char);
@ -919,7 +959,7 @@ void expand_variable_error(parser_t &parser, const wcstring &token, size_t token
/** /**
Parse an array slicing specification Parse an array slicing specification
*/ */
static int parse_slice(const wchar_t *in, wchar_t **end_ptr, std::vector<long> &idx, size_t array_size) static int parse_slice(const wchar_t *in, wchar_t **end_ptr, std::vector<long> &idx, std::vector<size_t> &source_positions, size_t array_size)
{ {
wchar_t *end; wchar_t *end;
@ -942,6 +982,7 @@ static int parse_slice(const wchar_t *in, wchar_t **end_ptr, std::vector<long> &
} }
errno=0; errno=0;
const size_t i1_src_pos = pos;
tmp = wcstol(&in[pos], &end, 10); tmp = wcstol(&in[pos], &end, 10);
if ((errno) || (end == &in[pos])) if ((errno) || (end == &in[pos]))
{ {
@ -958,6 +999,8 @@ static int parse_slice(const wchar_t *in, wchar_t **end_ptr, std::vector<long> &
pos+=2; pos+=2;
while (in[pos]==INTERNAL_SEPARATOR) while (in[pos]==INTERNAL_SEPARATOR)
pos++; pos++;
const size_t number_start = pos;
long tmp1 = wcstol(&in[pos], &end, 10); long tmp1 = wcstol(&in[pos], &end, 10);
if ((errno) || (end == &in[pos])) if ((errno) || (end == &in[pos]))
{ {
@ -973,12 +1016,14 @@ static int parse_slice(const wchar_t *in, wchar_t **end_ptr, std::vector<long> &
{ {
// debug(0, L"Expand range [subst]: %i\n", jjj); // debug(0, L"Expand range [subst]: %i\n", jjj);
idx.push_back(jjj); idx.push_back(jjj);
source_positions.push_back(number_start);
} }
continue; continue;
} }
// debug( 0, L"Push idx %d", tmp ); // debug( 0, L"Push idx %d", tmp );
idx.push_back(i1); idx.push_back(i1);
source_positions.push_back(i1_src_pos);
} }
if (end_ptr) if (end_ptr)
@ -993,7 +1038,6 @@ static int parse_slice(const wchar_t *in, wchar_t **end_ptr, std::vector<long> &
} }
/** /**
Expand all environment variables in the string *ptr. Expand all environment variables in the string *ptr.
@ -1008,24 +1052,29 @@ static int parse_slice(const wchar_t *in, wchar_t **end_ptr, std::vector<long> &
happens, don't edit it unless you know exactly what you are doing, happens, don't edit it unless you know exactly what you are doing,
and do proper testing afterwards. and do proper testing afterwards.
*/ */
static int expand_variables_internal(parser_t &parser, wchar_t * const in, std::vector<completion_t> &out, long last_idx); static int expand_variables_internal(parser_t &parser, wchar_t * const in, std::vector<completion_t> &out, long last_idx, parse_error_list_t *errors);
static int expand_variables2(parser_t &parser, const wcstring &instr, std::vector<completion_t> &out, long last_idx) static int expand_variables2(parser_t &parser, const wcstring &instr, std::vector<completion_t> &out, long last_idx, parse_error_list_t *errors)
{ {
wchar_t *in = wcsdup(instr.c_str()); wchar_t *in = wcsdup(instr.c_str());
int result = expand_variables_internal(parser, in, out, last_idx); int result = expand_variables_internal(parser, in, out, last_idx, errors);
free(in); free(in);
return result; return result;
} }
static int expand_variables_internal(parser_t &parser, wchar_t * const in, std::vector<completion_t> &out, long last_idx) static int expand_variables_internal(parser_t &parser, wchar_t * const in, std::vector<completion_t> &out, long last_idx, parse_error_list_t *errors)
{ {
int is_ok= 1; int is_ok= 1;
int empty=0; int empty=0;
wcstring var_tmp; wcstring var_tmp;
// list of indexes
std::vector<long> var_idx_list; std::vector<long> var_idx_list;
// parallel array of source positions of each index in the variable list
std::vector<size_t> var_pos_list;
// CHECK( out, 0 ); // CHECK( out, 0 );
for (long i=last_idx; (i>=0) && is_ok && !empty; i--) for (long i=last_idx; (i>=0) && is_ok && !empty; i--)
@ -1057,7 +1106,7 @@ static int expand_variables_internal(parser_t &parser, wchar_t * const in, std::
if (var_len == 0) if (var_len == 0)
{ {
expand_variable_error(parser, in, stop_pos-1, -1); expand_variable_error(parser, in, stop_pos-1, -1, errors);
is_ok = 0; is_ok = 0;
break; break;
@ -1075,15 +1124,16 @@ static int expand_variables_internal(parser_t &parser, wchar_t * const in, std::
{ {
tokenize_variable_array(var_val.c_str(), var_item_list); tokenize_variable_array(var_val.c_str(), var_item_list);
if (in[stop_pos] == L'[') const size_t slice_start = stop_pos;
if (in[slice_start] == L'[')
{ {
wchar_t *slice_end; wchar_t *slice_end;
all_vars=0; all_vars=0;
if (parse_slice(in + stop_pos, &slice_end, var_idx_list, var_item_list.size())) if (parse_slice(in + slice_start, &slice_end, var_idx_list, var_pos_list, var_item_list.size()))
{ {
parser.error(SYNTAX_ERROR, append_syntax_error(errors,
-1, stop_pos,
L"Invalid index value"); L"Invalid index value");
is_ok = 0; is_ok = 0;
break; break;
@ -1094,19 +1144,16 @@ static int expand_variables_internal(parser_t &parser, wchar_t * const in, std::
if (!all_vars) if (!all_vars)
{ {
wcstring_list_t string_values(var_idx_list.size()); wcstring_list_t string_values(var_idx_list.size());
for (size_t j=0; j<var_idx_list.size(); j++) for (size_t j=0; j<var_idx_list.size(); j++)
{ {
long tmp = var_idx_list.at(j); long tmp = var_idx_list.at(j);
/* size_t var_src_pos = var_pos_list.at(j);
Check that we are within array /* Check that we are within array bounds. If not, truncate the list to exit. */
bounds. If not, truncate the list to
exit.
*/
if (tmp < 1 || (size_t)tmp > var_item_list.size()) if (tmp < 1 || (size_t)tmp > var_item_list.size())
{ {
parser.error(SYNTAX_ERROR, /* The slice was parsed starting at stop_pos, so we have to add that to the error position */
-1, append_syntax_error(errors,
slice_start + var_src_pos,
ARRAY_BOUNDS_ERR); ARRAY_BOUNDS_ERR);
is_ok=0; is_ok=0;
var_idx_list.resize(j); var_idx_list.resize(j);
@ -1145,7 +1192,7 @@ static int expand_variables_internal(parser_t &parser, wchar_t * const in, std::
} }
} }
res.append(in + stop_pos); res.append(in + stop_pos);
is_ok &= expand_variables2(parser, res, out, i); is_ok &= expand_variables2(parser, res, out, i, errors);
} }
else else
{ {
@ -1173,7 +1220,7 @@ static int expand_variables_internal(parser_t &parser, wchar_t * const in, std::
} }
new_in.append(next); new_in.append(next);
new_in.append(in + stop_pos); new_in.append(in + stop_pos);
is_ok &= expand_variables2(parser, new_in, out, i); is_ok &= expand_variables2(parser, new_in, out, i, errors);
} }
} }
@ -1205,7 +1252,7 @@ static int expand_variables_internal(parser_t &parser, wchar_t * const in, std::
res.append(in); res.append(in);
res.append(in + stop_pos); res.append(in + stop_pos);
is_ok &= expand_variables2(parser, res, out, i); is_ok &= expand_variables2(parser, res, out, i, errors);
return is_ok; return is_ok;
} }
} }
@ -1225,7 +1272,7 @@ static int expand_variables_internal(parser_t &parser, wchar_t * const in, std::
/** /**
Perform bracket expansion Perform bracket expansion
*/ */
static int expand_brackets(parser_t &parser, const wcstring &instr, int flags, std::vector<completion_t> &out) static int expand_brackets(parser_t &parser, const wcstring &instr, int flags, std::vector<completion_t> &out, parse_error_list_t *errors)
{ {
bool syntax_error = false; bool syntax_error = false;
int bracket_count=0; int bracket_count=0;
@ -1295,14 +1342,14 @@ static int expand_brackets(parser_t &parser, const wcstring &instr, int flags, s
mod.push_back(BRACKET_END); mod.push_back(BRACKET_END);
} }
return expand_brackets(parser, mod, 1, out); return expand_brackets(parser, mod, 1, out, errors);
} }
} }
if (syntax_error) if (syntax_error)
{ {
parser.error(SYNTAX_ERROR, append_syntax_error(errors,
-1, SOURCE_LOCATION_UNKNOWN,
_(L"Mismatched brackets")); _(L"Mismatched brackets"));
return 0; return 0;
} }
@ -1331,7 +1378,7 @@ static int expand_brackets(parser_t &parser, const wcstring &instr, int flags, s
whole_item.append(in, length_preceding_brackets); whole_item.append(in, length_preceding_brackets);
whole_item.append(item_begin, item_len); whole_item.append(item_begin, item_len);
whole_item.append(bracket_end + 1); whole_item.append(bracket_end + 1);
expand_brackets(parser, whole_item, flags, out); expand_brackets(parser, whole_item, flags, out, errors);
item_begin = pos+1; item_begin = pos+1;
if (pos == bracket_end) if (pos == bracket_end)
@ -1355,7 +1402,7 @@ static int expand_brackets(parser_t &parser, const wcstring &instr, int flags, s
/** /**
Perform cmdsubst expansion Perform cmdsubst expansion
*/ */
static int expand_cmdsubst(parser_t &parser, const wcstring &input, std::vector<completion_t> &out_list) static int expand_cmdsubst(parser_t &parser, const wcstring &input, std::vector<completion_t> &out_list, parse_error_list_t *errors)
{ {
wchar_t *paran_begin=0, *paran_end=0; wchar_t *paran_begin=0, *paran_end=0;
std::vector<wcstring> sub_res; std::vector<wcstring> sub_res;
@ -1368,8 +1415,8 @@ static int expand_cmdsubst(parser_t &parser, const wcstring &input, std::vector<
switch (parse_ret = parse_util_locate_cmdsubst(in, &paran_begin, &paran_end, false)) switch (parse_ret = parse_util_locate_cmdsubst(in, &paran_begin, &paran_end, false))
{ {
case -1: case -1:
parser.error(SYNTAX_ERROR, append_syntax_error(errors,
-1, SOURCE_LOCATION_UNKNOWN,
L"Mismatched parenthesis"); L"Mismatched parenthesis");
return 0; return 0;
case 0: case 0:
@ -1384,7 +1431,7 @@ static int expand_cmdsubst(parser_t &parser, const wcstring &input, std::vector<
if (exec_subshell(subcmd, sub_res, true /* do apply exit status */) == -1) if (exec_subshell(subcmd, sub_res, true /* do apply exit status */) == -1)
{ {
parser.error(CMDSUBST_ERROR, -1, L"Unknown error while evaulating command substitution"); append_cmdsub_error(errors, SOURCE_LOCATION_UNKNOWN, L"Unknown error while evaulating command substitution");
return 0; return 0;
} }
@ -1392,24 +1439,25 @@ static int expand_cmdsubst(parser_t &parser, const wcstring &input, std::vector<
if (*tail_begin == L'[') if (*tail_begin == L'[')
{ {
std::vector<long> slice_idx; std::vector<long> slice_idx;
std::vector<size_t> slice_source_positions;
wchar_t *slice_end; wchar_t *slice_end;
if (parse_slice(tail_begin, &slice_end, slice_idx, sub_res.size())) if (parse_slice(tail_begin, &slice_end, slice_idx, slice_source_positions, sub_res.size()))
{ {
parser.error(SYNTAX_ERROR, -1, L"Invalid index value"); append_syntax_error(errors, SOURCE_LOCATION_UNKNOWN, L"Invalid index value");
return 0; return 0;
} }
else else
{ {
std::vector<wcstring> sub_res2; wcstring_list_t sub_res2;
tail_begin = slice_end; tail_begin = slice_end;
for (i=0; i < slice_idx.size(); i++) for (i=0; i < slice_idx.size(); i++)
{ {
long idx = slice_idx.at(i); long idx = slice_idx.at(i);
if (idx < 1 || (size_t)idx > sub_res.size()) if (idx < 1 || (size_t)idx > sub_res.size())
{ {
parser.error(SYNTAX_ERROR, append_syntax_error(errors,
-1, SOURCE_LOCATION_UNKNOWN,
ARRAY_BOUNDS_ERR); ARRAY_BOUNDS_ERR);
return 0; return 0;
} }
@ -1430,7 +1478,7 @@ static int expand_cmdsubst(parser_t &parser, const wcstring &input, std::vector<
of the string is inserted into the tail_expand array list of the string is inserted into the tail_expand array list
*/ */
std::vector<completion_t> tail_expand; std::vector<completion_t> tail_expand;
expand_cmdsubst(parser, tail_begin, tail_expand); expand_cmdsubst(parser, tail_begin, tail_expand, errors /* TODO: offset error locations */);
/* /*
Combine the result of the current command substitution with the Combine the result of the current command substitution with the
@ -1639,7 +1687,7 @@ static void remove_internal_separator(wcstring &str, bool conv)
} }
int expand_string(const wcstring &input, std::vector<completion_t> &output, expand_flags_t flags) int expand_string(const wcstring &input, std::vector<completion_t> &output, expand_flags_t flags, parse_error_list_t *errors)
{ {
parser_t parser(PARSER_TYPE_ERRORS_ONLY, true /* show errors */); parser_t parser(PARSER_TYPE_ERRORS_ONLY, true /* show errors */);
@ -1661,14 +1709,14 @@ int expand_string(const wcstring &input, std::vector<completion_t> &output, expa
if (parse_util_locate_cmdsubst(input.c_str(), &begin, &end, true) != 0) if (parse_util_locate_cmdsubst(input.c_str(), &begin, &end, true) != 0)
{ {
parser.error(CMDSUBST_ERROR, -1, L"Command substitutions not allowed"); append_cmdsub_error(errors, SOURCE_LOCATION_UNKNOWN, L"Command substitutions not allowed");
return EXPAND_ERROR; return EXPAND_ERROR;
} }
append_completion(*in, input); append_completion(*in, input);
} }
else else
{ {
int cmdsubst_ok = expand_cmdsubst(parser, input, *in); int cmdsubst_ok = expand_cmdsubst(parser, input, *in, errors);
if (! cmdsubst_ok) if (! cmdsubst_ok)
return EXPAND_ERROR; return EXPAND_ERROR;
} }
@ -1696,7 +1744,7 @@ int expand_string(const wcstring &input, std::vector<completion_t> &output, expa
} }
else else
{ {
if (!expand_variables2(parser, next, *out, next.size() - 1)) if (!expand_variables2(parser, next, *out, next.size() - 1, errors))
{ {
return EXPAND_ERROR; return EXPAND_ERROR;
} }
@ -1710,7 +1758,7 @@ int expand_string(const wcstring &input, std::vector<completion_t> &output, expa
{ {
const wcstring &next = in->at(i).completion; const wcstring &next = in->at(i).completion;
if (!expand_brackets(parser, next, flags, *out)) if (!expand_brackets(parser, next, flags, *out, errors))
{ {
return EXPAND_ERROR; return EXPAND_ERROR;
} }
@ -1840,7 +1888,7 @@ int expand_string(const wcstring &input, std::vector<completion_t> &output, expa
return res; return res;
} }
bool expand_one(wcstring &string, expand_flags_t flags) bool expand_one(wcstring &string, expand_flags_t flags, parse_error_list_t *errors)
{ {
std::vector<completion_t> completions; std::vector<completion_t> completions;
bool result = false; bool result = false;
@ -1850,7 +1898,7 @@ bool expand_one(wcstring &string, expand_flags_t flags)
return true; return true;
} }
if (expand_string(string, completions, flags | EXPAND_NO_DESCRIPTIONS)) if (expand_string(string, completions, flags | EXPAND_NO_DESCRIPTIONS, errors))
{ {
if (completions.size() == 1) if (completions.size() == 1)
{ {

View file

@ -19,6 +19,7 @@
#include "util.h" #include "util.h"
#include "common.h" #include "common.h"
#include "parse_constants.h"
#include <list> #include <list>
enum enum
@ -146,9 +147,10 @@ class parser_t;
\param input The parameter to expand \param input The parameter to expand
\param output The list to which the result will be appended. \param output The list to which the result will be appended.
\param flag Specifies if any expansion pass should be skipped. Legal values are any combination of EXPAND_SKIP_CMDSUBST EXPAND_SKIP_VARIABLES and EXPAND_SKIP_WILDCARDS \param flag Specifies if any expansion pass should be skipped. Legal values are any combination of EXPAND_SKIP_CMDSUBST EXPAND_SKIP_VARIABLES and EXPAND_SKIP_WILDCARDS
\param errors Resulting errors, or NULL to ignore
\return One of EXPAND_OK, EXPAND_ERROR, EXPAND_WILDCARD_MATCH and EXPAND_WILDCARD_NO_MATCH. EXPAND_WILDCARD_NO_MATCH and EXPAND_WILDCARD_MATCH are normal exit conditions used only on strings containing wildcards to tell if the wildcard produced any matches. \return One of EXPAND_OK, EXPAND_ERROR, EXPAND_WILDCARD_MATCH and EXPAND_WILDCARD_NO_MATCH. EXPAND_WILDCARD_NO_MATCH and EXPAND_WILDCARD_MATCH are normal exit conditions used only on strings containing wildcards to tell if the wildcard produced any matches.
*/ */
__warn_unused int expand_string(const wcstring &input, std::vector<completion_t> &output, expand_flags_t flags); __warn_unused int expand_string(const wcstring &input, std::vector<completion_t> &output, expand_flags_t flags, parse_error_list_t *errors);
/** /**
@ -158,9 +160,10 @@ __warn_unused int expand_string(const wcstring &input, std::vector<completion_t>
\param inout_str The parameter to expand in-place \param inout_str The parameter to expand in-place
\param flag Specifies if any expansion pass should be skipped. Legal values are any combination of EXPAND_SKIP_CMDSUBST EXPAND_SKIP_VARIABLES and EXPAND_SKIP_WILDCARDS \param flag Specifies if any expansion pass should be skipped. Legal values are any combination of EXPAND_SKIP_CMDSUBST EXPAND_SKIP_VARIABLES and EXPAND_SKIP_WILDCARDS
\param errors Resulting errors, or NULL to ignore
\return Whether expansion succeded \return Whether expansion succeded
*/ */
bool expand_one(wcstring &inout_str, expand_flags_t flags); bool expand_one(wcstring &inout_str, expand_flags_t flags, parse_error_list_t *errors = NULL);
/** /**
Convert the variable value to a human readable form, i.e. escape things, handle arrays, etc. Suitable for pretty-printing. The result must be free'd! Convert the variable value to a human readable form, i.e. escape things, handle arrays, etc. Suitable for pretty-printing. The result must be free'd!

View file

@ -997,7 +997,7 @@ static int expand_test(const wchar_t *in, int flags, ...)
int res=1; int res=1;
wchar_t *arg; wchar_t *arg;
if (expand_string(in, output, flags)) if (expand_string(in, output, flags, NULL))
{ {
} }

View file

@ -137,6 +137,31 @@ enum
}; };
typedef unsigned int parser_test_error_bits_t; typedef unsigned int parser_test_error_bits_t;
struct parse_error_t
{
/** Text of the error */
wcstring text;
/** Code for the error */
enum parse_error_code_t code;
/** Offset and length of the token in the source code that triggered this error */
size_t source_start;
size_t source_length;
/** Return a string describing the error, suitable for presentation to the user. If skip_caret is false, the offending line with a caret is printed as well */
wcstring describe(const wcstring &src) const;
/** Return a string describing the error, suitable for presentation to the user, with the given prefix. If skip_caret is false, the offending line with a caret is printed as well */
wcstring describe_with_prefix(const wcstring &src, const wcstring &prefix, bool is_interactive, bool skip_caret) const;
};
typedef std::vector<parse_error_t> parse_error_list_t;
/* Special source_start value that means unknown */
#define SOURCE_LOCATION_UNKNOWN (static_cast<size_t>(-1))
/* Helper function to offset error positions by the given amount. This is used when determining errors in a substring of a larger source buffer. */
void parse_error_offset_source_start(parse_error_list_t *errors, size_t amt);
/** Maximum number of function calls. */ /** Maximum number of function calls. */
#define FISH_MAX_STACK_DEPTH 128 #define FISH_MAX_STACK_DEPTH 128

View file

@ -135,7 +135,7 @@ 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 */ /* Ok, this is an undecorated plain statement. Get and expand its command */
wcstring cmd; wcstring cmd;
tree.command_for_plain_statement(plain_statement, src, &cmd); tree.command_for_plain_statement(plain_statement, src, &cmd);
expand_one(cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES); expand_one(cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES, NULL);
if (cmd == forbidden_function_name) if (cmd == forbidden_function_name)
{ {
@ -440,7 +440,7 @@ parse_execution_result_t parse_execution_context_t::run_for_statement(const pars
/* Get the variable name: `for var_name in ...`. We expand the variable name. It better result in just one. */ /* Get the variable name: `for var_name in ...`. We expand the variable name. It better result in just one. */
const parse_node_t &var_name_node = *get_child(header, 1, parse_token_type_string); const parse_node_t &var_name_node = *get_child(header, 1, parse_token_type_string);
wcstring for_var_name = get_source(var_name_node); wcstring for_var_name = get_source(var_name_node);
if (! expand_one(for_var_name, 0)) if (! expand_one(for_var_name, 0, NULL))
{ {
report_error(var_name_node, FAILED_EXPANSION_VARIABLE_NAME_ERR_MSG, for_var_name.c_str()); report_error(var_name_node, FAILED_EXPANSION_VARIABLE_NAME_ERR_MSG, for_var_name.c_str());
return parse_execution_errored; return parse_execution_errored;
@ -511,16 +511,17 @@ parse_execution_result_t parse_execution_context_t::run_switch_statement(const p
const parse_node_t &switch_value_node = *get_child(statement, 1, parse_token_type_string); const parse_node_t &switch_value_node = *get_child(statement, 1, parse_token_type_string);
const wcstring switch_value = get_source(switch_value_node); const wcstring switch_value = get_source(switch_value_node);
/* Expand it */ /* Expand it. We need to offset any errors by the position of the string */
std::vector<completion_t> switch_values_expanded; std::vector<completion_t> switch_values_expanded;
int expand_ret = expand_string(switch_value, switch_values_expanded, EXPAND_NO_DESCRIPTIONS); parse_error_list_t errors;
int expand_ret = expand_string(switch_value, switch_values_expanded, EXPAND_NO_DESCRIPTIONS, &errors);
parse_error_offset_source_start(&errors, switch_value_node.source_start);
switch (expand_ret) switch (expand_ret)
{ {
case EXPAND_ERROR: case EXPAND_ERROR:
{ {
result = report_error(switch_value_node, result = report_errors(errors);
_(L"Could not expand string '%ls'"),
switch_value.c_str());
break; break;
} }
@ -671,29 +672,43 @@ parse_execution_result_t parse_execution_context_t::run_while_statement(const pa
} }
/* Reports an error. Always returns parse_execution_errored, so you can assign the result to an 'errored' variable */ /* Reports an error. Always returns parse_execution_errored, so you can assign the result to an 'errored' variable */
parse_execution_result_t parse_execution_context_t::report_error(const parse_node_t &node, const wchar_t *fmt, ...) parse_execution_result_t parse_execution_context_t::report_error(const parse_node_t &node, const wchar_t *fmt, ...) const
{ {
if (parser->show_errors) if (parser->show_errors)
{ {
/* Create an error */ /* Create an error */
parse_error_t error; parse_error_list_t error_list = parse_error_list_t(1);
error.source_start = node.source_start; parse_error_t *error = &error_list.at(0);
error.source_length = node.source_length; error->source_start = node.source_start;
error.code = parse_error_syntax; //hackish error->source_length = node.source_length;
error->code = parse_error_syntax; //hackish
va_list va; va_list va;
va_start(va, fmt); va_start(va, fmt);
error.text = vformat_string(fmt, va); error->text = vformat_string(fmt, va);
va_end(va); va_end(va);
this->report_errors(error_list);
}
return parse_execution_errored;
}
parse_execution_result_t parse_execution_context_t::report_errors(const parse_error_list_t &error_list) const
{
if (parser->show_errors)
{
if (error_list.empty())
{
fprintf(stderr, "Bug: Error reported but no error text found.");
}
/* Get a backtrace */ /* Get a backtrace */
wcstring backtrace_and_desc; wcstring backtrace_and_desc;
const parse_error_list_t error_list = parse_error_list_t(1, error);
parser->get_backtrace(src, error_list, &backtrace_and_desc); parser->get_backtrace(src, error_list, &backtrace_and_desc);
/* Print it */
fprintf(stderr, "%ls", backtrace_and_desc.c_str()); fprintf(stderr, "%ls", backtrace_and_desc.c_str());
} }
return parse_execution_errored; return parse_execution_errored;
} }
@ -829,7 +844,7 @@ parse_execution_result_t parse_execution_context_t::populate_plain_process(job_t
assert(got_cmd); assert(got_cmd);
/* Expand it as a command. Return an error on failure. */ /* Expand it as a command. Return an error on failure. */
bool expanded = expand_one(cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES); bool expanded = expand_one(cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES, NULL);
if (! expanded) if (! expanded)
{ {
report_error(statement, ILLEGAL_CMD_ERR_MSG, cmd.c_str()); report_error(statement, ILLEGAL_CMD_ERR_MSG, cmd.c_str());
@ -947,14 +962,14 @@ wcstring_list_t parse_execution_context_t::determine_arguments(const parse_node_
/* Expand this string */ /* Expand this string */
std::vector<completion_t> arg_expanded; std::vector<completion_t> arg_expanded;
int expand_ret = expand_string(arg_str, arg_expanded, EXPAND_NO_DESCRIPTIONS); parse_error_list_t errors;
int expand_ret = expand_string(arg_str, arg_expanded, EXPAND_NO_DESCRIPTIONS, &errors);
parse_error_offset_source_start(&errors, arg_node.source_start);
switch (expand_ret) switch (expand_ret)
{ {
case EXPAND_ERROR: case EXPAND_ERROR:
{ {
this->report_error(arg_node, this->report_errors(errors);
_(L"Could not expand string '%ls'"),
arg_str.c_str());
break; break;
} }
@ -1016,7 +1031,7 @@ bool parse_execution_context_t::determine_io_chain(const parse_node_t &statement
enum token_type redirect_type = tree.type_for_redirection(redirect_node, src, &source_fd, &target); enum token_type redirect_type = tree.type_for_redirection(redirect_node, src, &source_fd, &target);
/* PCA: I can't justify this EXPAND_SKIP_VARIABLES flag. It was like this when I got here. */ /* PCA: I can't justify this EXPAND_SKIP_VARIABLES flag. It was like this when I got here. */
bool target_expanded = expand_one(target, no_exec ? EXPAND_SKIP_VARIABLES : 0); bool target_expanded = expand_one(target, no_exec ? EXPAND_SKIP_VARIABLES : 0, NULL);
if (! target_expanded || target.empty()) if (! target_expanded || target.empty())
{ {
/* Should improve this error message */ /* Should improve this error message */

View file

@ -65,7 +65,9 @@ private:
execution_cancellation_reason_t cancellation_reason(const block_t *block) const; execution_cancellation_reason_t cancellation_reason(const block_t *block) const;
/* Report an error. Always returns true. */ /* Report an error. Always returns true. */
parse_execution_result_t report_error(const parse_node_t &node, const wchar_t *fmt, ...); parse_execution_result_t report_error(const parse_node_t &node, const wchar_t *fmt, ...) const;
parse_execution_result_t report_errors(const parse_error_list_t &errors) const;
/* Wildcard error helper */ /* Wildcard error helper */
parse_execution_result_t report_unmatched_wildcard_error(const parse_node_t &unmatched_wildcard); parse_execution_result_t report_unmatched_wildcard_error(const parse_node_t &unmatched_wildcard);

View file

@ -113,6 +113,24 @@ wcstring parse_errors_description(const parse_error_list_t &errors, const wcstri
return target; return target;
} }
void parse_error_offset_source_start(parse_error_list_t *errors, size_t amt)
{
assert(errors != NULL);
if (amt > 0)
{
size_t i, max = errors->size();
for (i=0; i < max; i++)
{
parse_error_t *error = &errors->at(i);
/* preserve the special meaning of -1 as 'unknown' */
if (error->source_start != SOURCE_LOCATION_UNKNOWN)
{
error->source_start += amt;
}
}
}
}
/** Returns a string description of the given token type */ /** Returns a string description of the given token type */
wcstring token_type_description(parse_token_type_t type) wcstring token_type_description(parse_token_type_t type)
{ {
@ -487,7 +505,6 @@ class parse_ll_t
void parse_error(parse_token_t token, parse_error_code_t code, const wchar_t *format, ...); void parse_error(parse_token_t token, parse_error_code_t code, const wchar_t *format, ...);
void parse_error_failed_production(struct parse_stack_element_t &elem, parse_token_t token); void parse_error_failed_production(struct parse_stack_element_t &elem, parse_token_t token);
void parse_error_unbalancing_token(parse_token_t token); void parse_error_unbalancing_token(parse_token_t token);
void append_error_callout(wcstring &error_message, parse_token_t token);
void dump_stack(void) const; void dump_stack(void) const;

View file

@ -21,26 +21,6 @@ class parse_node_tree_t;
typedef size_t node_offset_t; typedef size_t node_offset_t;
#define NODE_OFFSET_INVALID (static_cast<node_offset_t>(-1)) #define NODE_OFFSET_INVALID (static_cast<node_offset_t>(-1))
struct parse_error_t
{
/** Text of the error */
wcstring text;
/** Code for the error */
enum parse_error_code_t code;
/** Offset and length of the token in the source code that triggered this error */
size_t source_start;
size_t source_length;
/** Return a string describing the error, suitable for presentation to the user. If skip_caret is false, the offending line with a caret is printed as well */
wcstring describe(const wcstring &src) const;
/** Return a string describing the error, suitable for presentation to the user, with the given prefix. If skip_caret is false, the offending line with a caret is printed as well */
wcstring describe_with_prefix(const wcstring &src, const wcstring &prefix, bool is_interactive, bool skip_caret) const;
};
typedef std::vector<parse_error_t> parse_error_list_t;
/* Returns a description of a list of parse errors */ /* Returns a description of a list of parse errors */
wcstring parse_errors_description(const parse_error_list_t &errors, const wcstring &src, const wchar_t *prefix = NULL); wcstring parse_errors_description(const parse_error_list_t &errors, const wcstring &src, const wchar_t *prefix = NULL);

View file

@ -1152,10 +1152,7 @@ parser_test_error_bits_t parse_util_detect_errors_in_argument(const parse_node_t
/* Our command substitution produced error offsets relative to its source. Tweak the offsets of the errors in the command substitution to account for both its offset within the string, and the offset of the node */ /* Our command substitution produced error offsets relative to its source. Tweak the offsets of the errors in the command substitution to account for both its offset within the string, and the offset of the node */
size_t error_offset = (paran_begin + 1 - arg_cpy) + node.source_start; size_t error_offset = (paran_begin + 1 - arg_cpy) + node.source_start;
for (size_t i=0; i < subst_errors.size(); i++) parse_error_offset_source_start(&subst_errors, error_offset);
{
subst_errors.at(i).source_start += error_offset;
}
if (out_errors != NULL) if (out_errors != NULL)
{ {
@ -1301,8 +1298,9 @@ parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, pars
if (node_tree.command_for_plain_statement(node, buff_src, &command)) if (node_tree.command_for_plain_statement(node, buff_src, &command))
{ {
// Check that we can expand the command // Check that we can expand the command
if (! expand_one(command, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES | EXPAND_SKIP_JOBS)) if (! expand_one(command, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES | EXPAND_SKIP_JOBS, NULL))
{ {
// TODO: leverage the resulting errors
errored = append_syntax_error(&parse_errors, node, ILLEGAL_CMD_ERR_MSG, command.c_str()); errored = append_syntax_error(&parse_errors, node, ILLEGAL_CMD_ERR_MSG, command.c_str());
} }

View file

@ -194,8 +194,6 @@ static wcstring user_presentable_path(const wcstring &path)
parser_t::parser_t(enum parser_type_t type, bool errors) : parser_t::parser_t(enum parser_type_t type, bool errors) :
parser_type(type), parser_type(type),
show_errors(errors), show_errors(errors),
error_code(0),
err_pos(0),
cancellation_requested(false), cancellation_requested(false),
is_within_fish_initialization(false) is_within_fish_initialization(false)
{ {
@ -382,22 +380,6 @@ void parser_t::allow_function()
forbidden_function.pop_back(); forbidden_function.pop_back();
} }
void parser_t::error(int ec, size_t p, const wchar_t *str, ...)
{
va_list va;
CHECK(str,);
error_code = ec;
// note : p may be -1
err_pos = static_cast<int>(p);
va_start(va, str);
err_buff = vformat_string(str, va);
va_end(va);
}
/** /**
Print profiling information to the specified stream Print profiling information to the specified stream
*/ */
@ -529,7 +511,7 @@ void parser_t::expand_argument_list(const wcstring &arg_list_src, std::vector<co
if (arg_node != NULL) if (arg_node != NULL)
{ {
const wcstring arg_src = arg_node->get_source(arg_list_src); const wcstring arg_src = arg_node->get_source(arg_list_src);
if (expand_string(arg_src, output_arg_list, eflags) == EXPAND_ERROR) if (expand_string(arg_src, output_arg_list, eflags, NULL) == EXPAND_ERROR)
{ {
/* Failed to expand a string */ /* Failed to expand a string */
break; break;
@ -1035,22 +1017,36 @@ void parser_t::get_backtrace(const wcstring &src, const parse_error_list_t &erro
{ {
const parse_error_t &err = errors.at(0); const parse_error_t &err = errors.at(0);
const bool is_interactive = get_is_interactive();
// Determine if we want to try to print a caret to point at the source error
// The err.source_start <= src.size() check is due to the nasty way that slices work,
// which is by rewriting the source (!)
size_t which_line = 0;
bool skip_caret = true;
if (err.source_start != SOURCE_LOCATION_UNKNOWN && err.source_start <= src.size())
{
// Determine which line we're on // Determine which line we're on
assert(err.source_start <= src.size()); which_line = 1 + std::count(src.begin(), src.begin() + err.source_start, L'\n');
size_t which_line = 1 + std::count(src.begin(), src.begin() + err.source_start, L'\n');
// Don't include the caret if we're interactive, this is the first line of text, and our source is at its beginning, because then it's obvious // Don't include the caret if we're interactive, this is the first line of text, and our source is at its beginning, because then it's obvious
bool is_interactive = get_is_interactive(); skip_caret = (is_interactive && which_line == 1 && err.source_start == 0);
bool skip_caret = (is_interactive && which_line == 1 && err.source_start == 0); }
wcstring prefix; wcstring prefix;
const wchar_t *filename = this->current_filename(); const wchar_t *filename = this->current_filename();
if (filename) if (filename)
{
if (which_line > 0)
{ {
prefix = format_string(_(L"%ls (line %lu): "), user_presentable_path(filename).c_str(), which_line); prefix = format_string(_(L"%ls (line %lu): "), user_presentable_path(filename).c_str(), which_line);
} }
else else
{
prefix = format_string(_(L"%ls: "), user_presentable_path(filename).c_str());
}
}
else
{ {
prefix = L"fish: "; prefix = L"fish: ";
} }

View file

@ -241,12 +241,6 @@ private:
/** Whether or not we output errors */ /** Whether or not we output errors */
const bool show_errors; const bool show_errors;
/** Last error code */
int error_code;
/** Position of last error */
int err_pos;
/** Indication that we should skip all blocks */ /** Indication that we should skip all blocks */
bool cancellation_requested; bool cancellation_requested;
@ -317,7 +311,7 @@ public:
\return 0 on success, 1 otherwise \return 0 on success, 1 otherwise
*/ */
int eval(const wcstring &cmd_str, const io_chain_t &io, enum block_type_t block_type); int eval(const wcstring &cmd, const io_chain_t &io, enum block_type_t block_type);
/** Evaluates a block node at the given node offset in the topmost execution context */ /** Evaluates a block node at the given node offset in the topmost execution context */
int eval_block_node(node_offset_t node_idx, const io_chain_t &io, enum block_type_t block_type); int eval_block_node(node_offset_t node_idx, const io_chain_t &io, enum block_type_t block_type);
@ -332,15 +326,6 @@ public:
*/ */
void expand_argument_list(const wcstring &arg_src, std::vector<completion_t> &output); void expand_argument_list(const wcstring &arg_src, std::vector<completion_t> &output);
/**
Sets the current evaluation error. This function should only be used by libraries that are called by
\param ec The new error code
\param p The character offset at which the error occured
\param str The printf-style error message filter
*/
void error(int ec, size_t p, const wchar_t *str, ...);
/** /**
Returns a string describing the current parser pisition in the format 'FILENAME (line LINE_NUMBER): LINE'. Returns a string describing the current parser pisition in the format 'FILENAME (line LINE_NUMBER): LINE'.
Example: Example: