changeset 21859:3e5d0832a2e7 v8.2.1479

patch 8.2.1479: Vim9: error for list index uses wrong line number Commit: https://github.com/vim/vim/commit/1d634542cf5ebcd1d5d83bd124b3e1d5e7c96c58 Author: Bram Moolenaar <Bram@vim.org> 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().
author Bram Moolenaar <Bram@vim.org>
date Tue, 18 Aug 2020 13:45:04 +0200
parents 10d05fa7d7a9
children 2b26ea27117d
files runtime/doc/testing.txt src/evalfunc.c src/globals.h src/message.c src/testdir/test_vim9_expr.vim src/testdir/vim9.vim src/testing.c src/version.c src/vim9execute.c
diffstat 9 files changed, 56 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- 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|.
--- 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},
--- 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
--- 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);
--- 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<number> = [234, 'x']"], 'E1012:')
   call CheckDefFailure(["let l: list<number> = ['x', 234]"], 'E1012:')
--- 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
 
--- 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);
--- 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,
--- 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;