mirror of
https://github.com/fish-shell/fish-shell
synced 2025-01-12 21:18:53 +00:00
Support for error detection in arguments in new parser. Restores error
reporting for bad arguments (e.g. with bad variable names)
This commit is contained in:
parent
8d6b0c8d76
commit
79d14521db
4 changed files with 226 additions and 27 deletions
25
expand.cpp
25
expand.cpp
|
@ -53,31 +53,6 @@ parameter expansion.
|
|||
|
||||
#include "parse_util.h"
|
||||
|
||||
/**
|
||||
Error issued on invalid variable name
|
||||
*/
|
||||
#define COMPLETE_VAR_DESC _( L"The '$' character begins a variable name. The character '%lc', which directly followed a '$', is not allowed as a part of a variable name, and variable names may not be zero characters long. To learn more about variable expansion in fish, type 'help expand-variable'.")
|
||||
|
||||
/**
|
||||
Error issued on $?
|
||||
*/
|
||||
#define COMPLETE_YOU_WANT_STATUS _( L"$? is not a valid variable in fish. If you want the exit status of the last command, try $status.")
|
||||
|
||||
/**
|
||||
Error issued on invalid variable name
|
||||
*/
|
||||
#define COMPLETE_VAR_NULL_DESC _( L"The '$' begins a variable name. It was given at the end of an argument. Variable names may not be zero characters long. To learn more about variable expansion in fish, type 'help expand-variable'.")
|
||||
|
||||
/**
|
||||
Error issued on invalid variable name
|
||||
*/
|
||||
#define COMPLETE_VAR_BRACKET_DESC _( L"Did you mean %ls{$%ls}%ls? The '$' character begins a variable name. A bracket, which directly followed a '$', is not allowed as a part of a variable name, and variable names may not be zero characters long. To learn more about variable expansion in fish, type 'help expand-variable'." )
|
||||
|
||||
/**
|
||||
Error issued on invalid variable name
|
||||
*/
|
||||
#define COMPLETE_VAR_PARAN_DESC _( L"Did you mean (COMMAND)? In fish, the '$' character is only used for accessing variables. To learn more about command substitution in fish, type 'help expand-command-substitution'.")
|
||||
|
||||
/**
|
||||
Description for child process
|
||||
*/
|
||||
|
|
|
@ -178,6 +178,31 @@ typedef unsigned int parser_test_error_bits_t;
|
|||
/** Error message for Posix-style assignment: foo=bar */
|
||||
#define COMMAND_ASSIGN_ERR_MSG _( L"Unknown command '%ls'. Did you mean 'set %ls %ls'? See the help section on the set command by typing 'help set'.")
|
||||
|
||||
/**
|
||||
Error issued on invalid variable name
|
||||
*/
|
||||
#define COMPLETE_VAR_DESC _( L"The '$' character begins a variable name. The character '%lc', which directly followed a '$', is not allowed as a part of a variable name, and variable names may not be zero characters long. To learn more about variable expansion in fish, type 'help expand-variable'.")
|
||||
|
||||
/**
|
||||
Error issued on $?
|
||||
*/
|
||||
#define COMPLETE_YOU_WANT_STATUS _( L"$? is not a valid variable in fish. If you want the exit status of the last command, try $status.")
|
||||
|
||||
/**
|
||||
Error issued on invalid variable name
|
||||
*/
|
||||
#define COMPLETE_VAR_NULL_DESC _( L"The '$' begins a variable name. It was given at the end of an argument. Variable names may not be zero characters long. To learn more about variable expansion in fish, type 'help expand-variable'.")
|
||||
|
||||
/**
|
||||
Error issued on invalid variable name
|
||||
*/
|
||||
#define COMPLETE_VAR_BRACKET_DESC _( L"Did you mean %ls{$%ls}%ls? The '$' character begins a variable name. A bracket, which directly followed a '$', is not allowed as a part of a variable name, and variable names may not be zero characters long. To learn more about variable expansion in fish, type 'help expand-variable'." )
|
||||
|
||||
/**
|
||||
Error issued on invalid variable name
|
||||
*/
|
||||
#define COMPLETE_VAR_PARAN_DESC _( L"Did you mean (COMMAND)? In fish, the '$' character is only used for accessing variables. To learn more about command substitution in fish, type 'help expand-command-substitution'.")
|
||||
|
||||
/**
|
||||
While block description
|
||||
*/
|
||||
|
|
201
parse_util.cpp
201
parse_util.cpp
|
@ -1001,11 +1001,205 @@ static bool first_argument_is_help(const parse_node_tree_t &node_tree, const par
|
|||
return is_help;
|
||||
}
|
||||
|
||||
void parse_util_expand_variable_error(const parse_node_t &node, const wcstring &token, size_t token_pos, size_t error_pos, parse_error_list_t *out_errors)
|
||||
{
|
||||
size_t stop_pos = token_pos+1;
|
||||
|
||||
switch (token[stop_pos])
|
||||
{
|
||||
case BRACKET_BEGIN:
|
||||
{
|
||||
wchar_t *cpy = wcsdup(token.c_str());
|
||||
*(cpy+token_pos)=0;
|
||||
wchar_t *name = &cpy[stop_pos+1];
|
||||
wchar_t *end = wcschr(name, BRACKET_END);
|
||||
wchar_t *post;
|
||||
int is_var=0;
|
||||
if (end)
|
||||
{
|
||||
post = end+1;
|
||||
*end = 0;
|
||||
|
||||
if (!wcsvarname(name))
|
||||
{
|
||||
is_var = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_var)
|
||||
{
|
||||
append_syntax_error(out_errors,
|
||||
node,
|
||||
COMPLETE_VAR_BRACKET_DESC,
|
||||
cpy,
|
||||
name,
|
||||
post);
|
||||
}
|
||||
else
|
||||
{
|
||||
append_syntax_error(out_errors,
|
||||
node,
|
||||
COMPLETE_VAR_BRACKET_DESC,
|
||||
L"",
|
||||
L"VARIABLE",
|
||||
L"");
|
||||
}
|
||||
free(cpy);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case INTERNAL_SEPARATOR:
|
||||
{
|
||||
append_syntax_error(out_errors,
|
||||
node,
|
||||
COMPLETE_VAR_PARAN_DESC);
|
||||
break;
|
||||
}
|
||||
|
||||
case 0:
|
||||
{
|
||||
append_syntax_error(out_errors,
|
||||
node,
|
||||
COMPLETE_VAR_NULL_DESC);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
wchar_t token_stop_char = token[stop_pos];
|
||||
// Unescape (see http://github.com/fish-shell/fish-shell/issues/50)
|
||||
if (token_stop_char == ANY_CHAR)
|
||||
token_stop_char = L'?';
|
||||
else if (token_stop_char == ANY_STRING || token_stop_char == ANY_STRING_RECURSIVE)
|
||||
token_stop_char = L'*';
|
||||
|
||||
append_syntax_error(out_errors,
|
||||
node,
|
||||
(token_stop_char == L'?' ? COMPLETE_YOU_WANT_STATUS : COMPLETE_VAR_DESC),
|
||||
token_stop_char);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
parser_test_error_bits_t parse_util_detect_errors_in_argument(const parse_node_t &node, const wcstring &arg_src, parse_error_list_t *out_errors)
|
||||
{
|
||||
int err=0;
|
||||
|
||||
wchar_t *paran_begin, *paran_end;
|
||||
wchar_t *arg_cpy;
|
||||
int do_loop = 1;
|
||||
|
||||
arg_cpy = wcsdup(arg_src.c_str());
|
||||
|
||||
while (do_loop)
|
||||
{
|
||||
switch (parse_util_locate_cmdsubst(arg_cpy,
|
||||
¶n_begin,
|
||||
¶n_end,
|
||||
false))
|
||||
{
|
||||
case -1:
|
||||
{
|
||||
err=1;
|
||||
if (out_errors)
|
||||
{
|
||||
append_syntax_error(out_errors, node, L"Mismatched parenthesis");
|
||||
}
|
||||
free(arg_cpy);
|
||||
return err;
|
||||
}
|
||||
|
||||
case 0:
|
||||
{
|
||||
do_loop = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case 1:
|
||||
{
|
||||
|
||||
const wcstring subst(paran_begin + 1, paran_end);
|
||||
wcstring tmp;
|
||||
|
||||
tmp.append(arg_cpy, paran_begin - arg_cpy);
|
||||
tmp.push_back(INTERNAL_SEPARATOR);
|
||||
tmp.append(paran_end+1);
|
||||
|
||||
parse_error_list_t errors;
|
||||
err |= parse_util_detect_errors(subst, &errors);
|
||||
if (out_errors != NULL)
|
||||
{
|
||||
/* Todo: have to tweak the offsets of the errors in the command substitution */
|
||||
out_errors->insert(out_errors->end(), errors.begin(), errors.end());
|
||||
}
|
||||
|
||||
free(arg_cpy);
|
||||
arg_cpy = wcsdup(tmp.c_str());
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wcstring unesc;
|
||||
if (! unescape_string(arg_cpy, &unesc, UNESCAPE_SPECIAL))
|
||||
{
|
||||
if (out_errors)
|
||||
{
|
||||
append_syntax_error(out_errors, node, L"Invalid token '%ls'", arg_cpy);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Check for invalid variable expansions */
|
||||
const size_t unesc_size = unesc.size();
|
||||
for (size_t idx = 0; idx < unesc_size; idx++)
|
||||
{
|
||||
switch (unesc.at(idx))
|
||||
{
|
||||
case VARIABLE_EXPAND:
|
||||
case VARIABLE_EXPAND_SINGLE:
|
||||
{
|
||||
wchar_t next_char = (idx + 1 < unesc_size ? unesc.at(idx + 1) : L'\0');
|
||||
|
||||
if (next_char != VARIABLE_EXPAND &&
|
||||
next_char != VARIABLE_EXPAND_SINGLE &&
|
||||
! wcsvarchr(next_char))
|
||||
{
|
||||
err=1;
|
||||
if (out_errors)
|
||||
{
|
||||
parse_util_expand_variable_error(node, unesc, idx, node.source_start, out_errors);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(arg_cpy);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, parse_error_list_t *out_errors)
|
||||
{
|
||||
parse_node_tree_t node_tree;
|
||||
parse_error_list_t parse_errors;
|
||||
|
||||
parser_test_error_bits_t res = 0;
|
||||
|
||||
// Whether we encountered a parse error
|
||||
bool errored = false;
|
||||
|
||||
|
@ -1065,6 +1259,11 @@ parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, pars
|
|||
errored = append_syntax_error(&parse_errors, node, EXEC_ERR_MSG, is_and ? L"and" : L"or");
|
||||
}
|
||||
}
|
||||
else if (node.type == symbol_argument)
|
||||
{
|
||||
const wcstring arg_src = node.get_source(buff_src);
|
||||
res |= parse_util_detect_errors_in_argument(node, arg_src, &parse_errors);
|
||||
}
|
||||
else if (node.type == symbol_plain_statement)
|
||||
{
|
||||
// In a few places below, we want to know if we are in a pipeline
|
||||
|
@ -1159,8 +1358,6 @@ parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, pars
|
|||
}
|
||||
}
|
||||
|
||||
parser_test_error_bits_t res = 0;
|
||||
|
||||
if (errored)
|
||||
res |= PARSER_TEST_ERROR;
|
||||
|
||||
|
|
|
@ -171,4 +171,6 @@ std::vector<int> parse_util_compute_indents(const wcstring &src);
|
|||
|
||||
parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, parse_error_list_t *out_errors = NULL);
|
||||
|
||||
parser_test_error_bits_t parse_util_detect_errors_in_argument(const parse_node_t &node, const wcstring &arg_src, parse_error_list_t *out_errors = NULL);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue