# HG changeset patch # User Bram Moolenaar # Date 1597751104 -7200 # Node ID 3e5d0832a2e7512650453aa219b884c59a64f267 # Parent 10d05fa7d7a97d1a0f9d5aa9a5ce9494a380ac50 patch 8.2.1479: Vim9: error for list index uses wrong line number Commit: https://github.com/vim/vim/commit/1d634542cf5ebcd1d5d83bd124b3e1d5e7c96c58 Author: Bram Moolenaar Date: Tue Aug 18 13:41:50 2020 +0200 patch 8.2.1479: Vim9: error for list index uses wrong line number Problem: Vim9: error for list index uses wrong line number. Solution: Set source line number. (closes https://github.com/vim/vim/issues/6724) Add a way to assert the line number of the error with assert_fails(). diff --git a/runtime/doc/testing.txt b/runtime/doc/testing.txt --- a/runtime/doc/testing.txt +++ b/runtime/doc/testing.txt @@ -291,8 +291,9 @@ assert_exception({error} [, {msg}]) *a catch call assert_exception('E492:') endtry - -assert_fails({cmd} [, {error} [, {msg}]]) *assert_fails()* +< + *assert_fails()* +assert_fails({cmd} [, {error} [, {msg} [, {lnum}]]]) Run {cmd} and add an error message to |v:errors| if it does NOT produce an error or when {error} is not found in the error message. Also see |assert-return|. @@ -311,13 +312,21 @@ assert_fails({cmd} [, {error} [, {msg}]] string for the first error: > assert_fails('cmd', ['', 'E987:']) < + If {msg} is empty then it is not used. Do this to get the + default message when passing the {lnum} argument. + + When {lnum} is present and not negative, and the {error} + argument is present and matches, then this is compared with + the line number at which the error was reported. That can be + the line number in a function or in a script. + Note that beeping is not considered an error, and some failing commands only beep. Use |assert_beeps()| for those. Can also be used as a |method|: > GetCmd()->assert_fails('E99:') -assert_false({actual} [, {msg}]) *assert_false()* +assert_false({actual} [, {msg}]) *assert_false()* When {actual} is not false an error message is added to |v:errors|, like with |assert_equal()|. Also see |assert-return|. diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -494,7 +494,7 @@ static funcentry_T global_functions[] = {"assert_equal", 2, 3, FEARG_2, ret_number, f_assert_equal}, {"assert_equalfile", 2, 3, FEARG_1, ret_number, f_assert_equalfile}, {"assert_exception", 1, 2, 0, ret_number, f_assert_exception}, - {"assert_fails", 1, 3, FEARG_1, ret_number, f_assert_fails}, + {"assert_fails", 1, 4, FEARG_1, ret_number, f_assert_fails}, {"assert_false", 1, 2, FEARG_1, ret_number, f_assert_false}, {"assert_inrange", 3, 4, FEARG_3, ret_number, f_assert_inrange}, {"assert_match", 2, 3, FEARG_2, ret_number, f_assert_match}, diff --git a/src/globals.h b/src/globals.h --- a/src/globals.h +++ b/src/globals.h @@ -223,6 +223,7 @@ EXTERN int emsg_severe INIT(= FALSE); / // used by assert_fails() EXTERN int emsg_assert_fails_used INIT(= FALSE); EXTERN char_u *emsg_assert_fails_msg INIT(= NULL); +EXTERN long emsg_assert_fails_lnum INIT(= 0); EXTERN int did_endif INIT(= FALSE); // just had ":endif" #endif diff --git a/src/message.c b/src/message.c --- a/src/message.c +++ b/src/message.c @@ -655,7 +655,10 @@ emsg_core(char_u *s) } if (emsg_assert_fails_used && emsg_assert_fails_msg == NULL) + { emsg_assert_fails_msg = vim_strsave(s); + emsg_assert_fails_lnum = SOURCING_LNUM; + } // set "v:errmsg", also when using ":silent! cmd" set_vim_var_string(VV_ERRMSG, s, -1); diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -1464,16 +1464,18 @@ def Test_expr7_list() 4] call CheckDefFailure(["let x = 1234[3]"], 'E1107:') - call CheckDefExecFailure(["let x = g:anint[3]"], 'E1062:') + call CheckDefExecFailure(["let x = g:anint[3]"], 'E1062:', 1) call CheckDefFailure(["let x = g:list_mixed[xxx]"], 'E1001:') call CheckDefFailure(["let x = [1,2,3]"], 'E1069:') call CheckDefFailure(["let x = [1 ,2, 3]"], 'E1068:') - call CheckDefExecFailure(["let x = g:list_mixed['xx']"], 'E1029:') + call CheckDefExecFailure(["echo 1", "let x = [][0]", "echo 3"], 'E684:', 2) + + call CheckDefExecFailure(["let x = g:list_mixed['xx']"], 'E1029:', 1) call CheckDefFailure(["let x = g:list_mixed["], 'E1097:') - call CheckDefFailure(["let x = g:list_mixed[0"], 'E1097:') + call CheckDefFailure(["let x = g:list_mixed[0"], 'E1097:', 1) call CheckDefExecFailure(["let x = g:list_empty[3]"], 'E684:') call CheckDefFailure(["let l: list = [234, 'x']"], 'E1012:') call CheckDefFailure(["let l: list = ['x', 234]"], 'E1012:') diff --git a/src/testdir/vim9.vim b/src/testdir/vim9.vim --- a/src/testdir/vim9.vim +++ b/src/testdir/vim9.vim @@ -9,17 +9,19 @@ func CheckDefSuccess(lines) endfunc " Check that "lines" inside ":def" results in an "error" message. -func CheckDefFailure(lines, error) +" If "lnum" is given check that the error is reported for this line. +func CheckDefFailure(lines, error, lnum = -1) call writefile(['def Func()'] + a:lines + ['enddef', 'defcompile'], 'Xdef') - call assert_fails('so Xdef', a:error, a:lines) + call assert_fails('so Xdef', a:error, a:lines, a:lnum) call delete('Xdef') endfunc " Check that "lines" inside ":def" results in an "error" message when executed. -func CheckDefExecFailure(lines, error) +" If "lnum" is given check that the error is reported for this line. +func CheckDefExecFailure(lines, error, lnum = -1) call writefile(['def Func()'] + a:lines + ['enddef'], 'Xdef') so Xdef - call assert_fails('call Func()', a:error, a:lines) + call assert_fails('call Func()', a:error, a:lines, a:lnum) call delete('Xdef') endfunc diff --git a/src/testing.c b/src/testing.c --- a/src/testing.c +++ b/src/testing.c @@ -142,7 +142,10 @@ fill_assert_error( int did_copy = FALSE; int omitted = 0; - if (opt_msg_tv->v_type != VAR_UNKNOWN) + if (opt_msg_tv->v_type != VAR_UNKNOWN + && !(opt_msg_tv->v_type == VAR_STRING + && (opt_msg_tv->vval.v_string == NULL + || *opt_msg_tv->vval.v_string == NUL))) { ga_concat(gap, echo_string(opt_msg_tv, &tofree, numbuf, 0)); vim_free(tofree); @@ -570,6 +573,7 @@ f_assert_fails(typval_T *argvars, typval char_u buf[NUMBUFLEN]; char_u *expected; int error_found = FALSE; + int lnum_error_found = FALSE; char_u *actual = emsg_assert_fails_msg == NULL ? (char_u *)"[unknown]" : emsg_assert_fails_msg; @@ -611,14 +615,31 @@ f_assert_fails(typval_T *argvars, typval goto theend; } + if (!error_found && argvars[3].v_type == VAR_NUMBER + && argvars[3].vval.v_number >= 0 + && argvars[3].vval.v_number != emsg_assert_fails_lnum) + { + error_found = TRUE; + lnum_error_found = TRUE; + } + if (error_found) { typval_T actual_tv; prepare_assert_error(&ga); - actual_tv.v_type = VAR_STRING; - actual_tv.vval.v_string = actual; - fill_assert_error(&ga, &argvars[2], NULL, &argvars[1], + if (lnum_error_found) + { + actual_tv.v_type = VAR_NUMBER; + actual_tv.vval.v_number = emsg_assert_fails_lnum; + } + else + { + actual_tv.v_type = VAR_STRING; + actual_tv.vval.v_string = actual; + } + fill_assert_error(&ga, &argvars[2], NULL, + &argvars[lnum_error_found ? 3 : 1], &actual_tv, ASSERT_OTHER); ga_concat(&ga, (char_u *)": "); assert_append_cmd_or_arg(&ga, argvars, cmd); diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -755,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1479, +/**/ 1478, /**/ 1477, diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -2299,6 +2299,7 @@ call_def_function( ectx.ec_stack.ga_len -= is_slice ? 2 : 1; tv = STACK_TV_BOT(-1); + SOURCING_LNUM = iptr->isn_lnum; if (list_slice_or_index(list, is_slice, n1, n2, tv, TRUE) == FAIL) goto on_error;