Skip to content

Commit

Permalink
patch 8.2.3135: Vim9: builtin function arguments not checked at compi…
Browse files Browse the repository at this point in the history
…le time

Problem:    Vim9: builtin function arguments not checked at compile time.
Solution:   Add more type checks. (Yegappan Lakshmanan, closes #8539)
  • Loading branch information
yegappan authored and brammool committed Jul 10, 2021
1 parent 9da32e4 commit 5b73992
Show file tree
Hide file tree
Showing 12 changed files with 563 additions and 186 deletions.
5 changes: 5 additions & 0 deletions src/channel.c
Expand Up @@ -1311,6 +1311,11 @@ channel_open_func(typval_T *argvars)
jobopt_T opt;
channel_T *channel = NULL;

if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_dict_arg(argvars, 1) == FAIL))
return NULL;

address = tv_get_string(&argvars[0]);
if (argvars[1].v_type != VAR_UNKNOWN
&& (argvars[1].v_type != VAR_DICT || argvars[1].vval.v_dict == NULL))
Expand Down
2 changes: 2 additions & 0 deletions src/errors.h
Expand Up @@ -492,3 +492,5 @@ EXTERN char e_regexp_number_after_dot_pos_search[]
INIT(= N_("E1204: No Number allowed after .: '\\%%%c'"));
EXTERN char e_no_white_space_allowed_between_option_and[]
INIT(= N_("E1205: No white space allowed between option and"));
EXTERN char e_dict_required_for_argument_nr[]
INIT(= N_("E1206: Dictionary required for argument %d"));
164 changes: 94 additions & 70 deletions src/evalfunc.c

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/proto/typval.pro
Expand Up @@ -11,6 +11,7 @@ varnumber_T tv_get_bool_chk(typval_T *varp, int *denote);
float_T tv_get_float(typval_T *varp);
int check_for_string_arg(typval_T *args, int idx);
int check_for_nonempty_string_arg(typval_T *args, int idx);
int check_for_dict_arg(typval_T *args, int idx);
char_u *tv_get_string(typval_T *varp);
char_u *tv_get_string_strict(typval_T *varp);
char_u *tv_get_string_buf(typval_T *varp, char_u *buf);
Expand Down
5 changes: 5 additions & 0 deletions src/terminal.c
Expand Up @@ -5276,6 +5276,11 @@ term_load_dump(typval_T *argvars, typval_T *rettv, int do_diff)
FILE *fd2 = NULL;
char_u *textline = NULL;

if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_dict_arg(argvars, 1) == FAIL))
return;

// First open the files. If this fails bail out.
fname1 = tv_get_string_buf_chk(&argvars[0], buf1);
if (do_diff)
Expand Down
12 changes: 6 additions & 6 deletions src/testdir/test_search.vim
Expand Up @@ -373,9 +373,9 @@ func Test_searchpairpos()
endfunc

func Test_searchpair_errors()
call assert_fails("call searchpair([0], 'middle', 'end', 'bW', 'skip', 99, 100)", 'E730: using List as a String')
call assert_fails("call searchpair('start', {-> 0}, 'end', 'bW', 'skip', 99, 100)", 'E729: using Funcref as a String')
call assert_fails("call searchpair('start', 'middle', {'one': 1}, 'bW', 'skip', 99, 100)", 'E731: using Dictionary as a String')
call assert_fails("call searchpair([0], 'middle', 'end', 'bW', 'skip', 99, 100)", 'E730: Using a List as a String')
call assert_fails("call searchpair('start', {-> 0}, 'end', 'bW', 'skip', 99, 100)", 'E729: Using a Funcref as a String')
call assert_fails("call searchpair('start', 'middle', {'one': 1}, 'bW', 'skip', 99, 100)", 'E731: Using a Dictionary as a String')
call assert_fails("call searchpair('start', 'middle', 'end', 'flags', 'skip', 99, 100)", 'E475: Invalid argument: flags')
call assert_fails("call searchpair('start', 'middle', 'end', 'bW', 'func', -99, 100)", 'E475: Invalid argument: -99')
call assert_fails("call searchpair('start', 'middle', 'end', 'bW', 'func', 99, -100)", 'E475: Invalid argument: -100')
Expand All @@ -384,9 +384,9 @@ func Test_searchpair_errors()
endfunc

func Test_searchpairpos_errors()
call assert_fails("call searchpairpos([0], 'middle', 'end', 'bW', 'skip', 99, 100)", 'E730: using List as a String')
call assert_fails("call searchpairpos('start', {-> 0}, 'end', 'bW', 'skip', 99, 100)", 'E729: using Funcref as a String')
call assert_fails("call searchpairpos('start', 'middle', {'one': 1}, 'bW', 'skip', 99, 100)", 'E731: using Dictionary as a String')
call assert_fails("call searchpairpos([0], 'middle', 'end', 'bW', 'skip', 99, 100)", 'E730: Using a List as a String')
call assert_fails("call searchpairpos('start', {-> 0}, 'end', 'bW', 'skip', 99, 100)", 'E729: Using a Funcref as a String')
call assert_fails("call searchpairpos('start', 'middle', {'one': 1}, 'bW', 'skip', 99, 100)", 'E731: Using a Dictionary as a String')
call assert_fails("call searchpairpos('start', 'middle', 'end', 'flags', 'skip', 99, 100)", 'E475: Invalid argument: flags')
call assert_fails("call searchpairpos('start', 'middle', 'end', 'bW', 'func', -99, 100)", 'E475: Invalid argument: -99')
call assert_fails("call searchpairpos('start', 'middle', 'end', 'bW', 'func', 99, -100)", 'E475: Invalid argument: -100')
Expand Down
2 changes: 1 addition & 1 deletion src/testdir/test_textprop.vim
Expand Up @@ -1340,7 +1340,7 @@ endfunc
func Test_prop_func_invalid_args()
call assert_fails('call prop_clear(1, 2, [])', 'E715:')
call assert_fails('call prop_clear(-1, 2)', 'E16:')
call assert_fails('call prop_find(test_null_dict())', 'E474:')
call assert_fails('call prop_find(test_null_dict())', 'E715:')
call assert_fails('call prop_find({"bufnr" : []})', 'E730:')
call assert_fails('call prop_find({})', 'E968:')
call assert_fails('call prop_find({}, "x")', 'E474:')
Expand Down
524 changes: 421 additions & 103 deletions src/testdir/test_vim9_builtin.vim

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion src/testing.c
Expand Up @@ -1096,7 +1096,10 @@ f_test_garbagecollect_soon(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
void
f_test_ignore_error(typval_T *argvars, typval_T *rettv UNUSED)
{
ignore_error_for_testing(tv_get_string(&argvars[0]));
if (argvars[0].v_type != VAR_STRING)
emsg(_(e_invarg));
else
ignore_error_for_testing(tv_get_string(&argvars[0]));
}

void
Expand Down
2 changes: 1 addition & 1 deletion src/textprop.c
Expand Up @@ -605,7 +605,7 @@ f_prop_find(typval_T *argvars, typval_T *rettv)

if (argvars[0].v_type != VAR_DICT || argvars[0].vval.v_dict == NULL)
{
emsg(_(e_invarg));
emsg(_(e_dictreq));
return;
}
dict = argvars[0].vval.v_dict;
Expand Down
25 changes: 21 additions & 4 deletions src/typval.c
Expand Up @@ -384,6 +384,23 @@ check_for_nonempty_string_arg(typval_T *args, int idx)
return OK;
}

/*
* Give an error and return FAIL unless "tv" is a dict.
*/
int
check_for_dict_arg(typval_T *args, int idx)
{
if (args[idx].v_type != VAR_DICT)
{
if (idx >= 0)
semsg(_(e_dict_required_for_argument_nr), idx + 1);
else
emsg(_(e_dictreq));
return FAIL;
}
return OK;
}

/*
* Get the string value of a variable.
* If it is a Number variable, the number is converted into a string.
Expand Down Expand Up @@ -456,13 +473,13 @@ tv_get_string_buf_chk_strict(typval_T *varp, char_u *buf, int strict)
return buf;
case VAR_FUNC:
case VAR_PARTIAL:
emsg(_("E729: using Funcref as a String"));
emsg(_("E729: Using a Funcref as a String"));
break;
case VAR_LIST:
emsg(_("E730: using List as a String"));
emsg(_("E730: Using a List as a String"));
break;
case VAR_DICT:
emsg(_("E731: using Dictionary as a String"));
emsg(_("E731: Using a Dictionary as a String"));
break;
case VAR_FLOAT:
#ifdef FEAT_FLOAT
Expand All @@ -483,7 +500,7 @@ tv_get_string_buf_chk_strict(typval_T *varp, char_u *buf, int strict)
STRCPY(buf, get_var_special_name(varp->vval.v_number));
return buf;
case VAR_BLOB:
emsg(_("E976: using Blob as a String"));
emsg(_("E976: Using a Blob as a String"));
break;
case VAR_JOB:
#ifdef FEAT_JOB_CHANNEL
Expand Down
2 changes: 2 additions & 0 deletions src/version.c
Expand Up @@ -755,6 +755,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
/**/
3135,
/**/
3134,
/**/
Expand Down

0 comments on commit 5b73992

Please sign in to comment.