changeset 21493:7449921216bc v8.2.1297

patch 8.2.1297: when a test fails it's often not easy to see where Commit: https://github.com/vim/vim/commit/a5d0423fa16f18b4576a2a07e50034e489587a7d Author: Bram Moolenaar <Bram@vim.org> Date: Sun Jul 26 15:37:02 2020 +0200 patch 8.2.1297: when a test fails it's often not easy to see where Problem: When a test fails it's often not easy to see what the call stack is. Solution: Add more entries from the call stack in the exception message.
author Bram Moolenaar <Bram@vim.org>
date Sun, 26 Jul 2020 15:45:04 +0200
parents fb81749507a4
children d26728613f3e
files runtime/doc/cmdline.txt src/debugger.c src/ex_docmd.c src/ex_eval.c src/message.c src/proto/scriptfile.pro src/scriptfile.c src/testdir/test_expand_func.vim src/testing.c src/version.c
diffstat 10 files changed, 103 insertions(+), 70 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/cmdline.txt
+++ b/runtime/doc/cmdline.txt
@@ -876,7 +876,7 @@ Also see |`=|.
 			       *:<cword>* *<cword>* *:<cWORD>* *<cWORD>*
 			       *:<cexpr>* *<cexpr>* *:<cfile>* *<cfile>*
 			       *:<afile>* *<afile>* *:<abuf>* *<abuf>*
-			       *:<amatch>* *<amatch>*
+			       *:<amatch>* *<amatch>* *:<stack>* *<stack>*
 			       *:<sfile>* *<sfile>* *:<slnum>* *<slnum>*
 			       *:<sflnum>* *<sflnum>* *E499* *E500*
 Note: these are typed literally, they are not special keys!
@@ -903,12 +903,16 @@ Note: these are typed literally, they ar
 		   events).
 	<sfile>    When executing a ":source" command, is replaced with the
 		   file name of the sourced file.  *E498*
-		   When executing a function, is replaced with:
-		   "function {function-name}[{lnum}]"
-		   function call nesting is indicated like this:
+		   When executing a function, is replaced with the call stack,
+		   as with <stack> (this is for backwards compatibility, using
+		   <stack> is preferred).
+		   Note that filename-modifiers are useless when <sfile> is
+		   not used inside a script.
+	<stack>	   is replaced with the call stack, using
+		   "function {function-name}[{lnum}]" for a function line
+		   and "script {file-name}[{lnum}]" for a script line, and
+		   ".." in between items.  E.g.:
 		   "function {function-name1}[{lnum}]..{function-name2}[{lnum}]"
-		   Note that filename-modifiers are useless when <sfile> is
-		   used inside a function.
 	<slnum>	   When executing a ":source" command, is replaced with the
 		   line number.  *E842*
 		   When executing a function it's the line number relative to
--- a/src/debugger.c
+++ b/src/debugger.c
@@ -105,7 +105,7 @@ do_debug(char_u *cmd)
 	vim_free(debug_newval);
 	debug_newval = NULL;
     }
-    sname = estack_sfile();
+    sname = estack_sfile(FALSE);
     if (sname != NULL)
 	msg((char *)sname);
     vim_free(sname);
@@ -344,7 +344,7 @@ do_checkbacktracelevel(void)
     }
     else
     {
-	char_u	*sname = estack_sfile();
+	char_u	*sname = estack_sfile(FALSE);
 	int	max = get_maxbacktrace_level(sname);
 
 	if (debug_backtrace_level > max)
@@ -365,7 +365,7 @@ do_showbacktrace(char_u *cmd)
     int	    i = 0;
     int	    max;
 
-    sname = estack_sfile();
+    sname = estack_sfile(FALSE);
     max = get_maxbacktrace_level(sname);
     if (sname != NULL)
     {
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -8268,8 +8268,10 @@ find_cmdline_var(char_u *src, int *usedl
 #define SPEC_SFILE  (SPEC_CFILE + 1)
 		    "<slnum>",		// ":so" file line number
 #define SPEC_SLNUM  (SPEC_SFILE + 1)
+		    "<stack>",		// call stack
+#define SPEC_STACK  (SPEC_SLNUM + 1)
 		    "<afile>",		// autocommand file name
-#define SPEC_AFILE  (SPEC_SLNUM + 1)
+#define SPEC_AFILE  (SPEC_STACK + 1)
 		    "<abuf>",		// autocommand buffer number
 #define SPEC_ABUF   (SPEC_AFILE + 1)
 		    "<amatch>",		// autocommand match name
@@ -8520,10 +8522,13 @@ eval_vars(
 		break;
 
 	case SPEC_SFILE:	// file name for ":so" command
-		result = estack_sfile();
+	case SPEC_STACK:	// call stack
+		result = estack_sfile(spec_idx == SPEC_SFILE);
 		if (result == NULL)
 		{
-		    *errormsg = _("E498: no :source file name to substitute for \"<sfile>\"");
+		    *errormsg = spec_idx == SPEC_SFILE
+			? _("E498: no :source file name to substitute for \"<sfile>\"")
+			: _("E489: no call stack to substitute for \"<stack>\"");
 		    return NULL;
 		}
 		resultbuf = result;	    // remember allocated string
--- a/src/ex_eval.c
+++ b/src/ex_eval.c
@@ -290,7 +290,7 @@ cause_errthrow(
 
 		    // Get the source name and lnum now, it may change before
 		    // reaching do_errthrow().
-		    elem->sfile = estack_sfile();
+		    elem->sfile = estack_sfile(FALSE);
 		    elem->slnum = SOURCING_LNUM;
 		}
 	    }
@@ -549,7 +549,7 @@ throw_exception(void *value, except_type
     }
     else
     {
-	excp->throw_name = estack_sfile();
+	excp->throw_name = estack_sfile(FALSE);
 	if (excp->throw_name == NULL)
 	    excp->throw_name = vim_strsave((char_u *)"");
 	if (excp->throw_name == NULL)
--- a/src/message.c
+++ b/src/message.c
@@ -461,7 +461,7 @@ get_emsg_source(void)
 
     if (SOURCING_NAME != NULL && other_sourcing_name())
     {
-	char_u	    *sname = estack_sfile();
+	char_u	    *sname = estack_sfile(FALSE);
 	char_u	    *tofree = sname;
 
 	if (sname == NULL)
--- a/src/proto/scriptfile.pro
+++ b/src/proto/scriptfile.pro
@@ -4,7 +4,7 @@ estack_T *estack_push(etype_T type, char
 estack_T *estack_push_ufunc(ufunc_T *ufunc, long lnum);
 int estack_top_is_ufunc(ufunc_T *ufunc, long lnum);
 estack_T *estack_pop(void);
-char_u *estack_sfile(void);
+char_u *estack_sfile(int is_sfile);
 void ex_runtime(exarg_T *eap);
 int do_in_path(char_u *path, char_u *name, int flags, void (*callback)(char_u *fname, void *ck), void *cookie);
 int do_in_runtimepath(char_u *name, int flags, void (*callback)(char_u *fname, void *ck), void *cookie);
--- a/src/scriptfile.c
+++ b/src/scriptfile.c
@@ -111,58 +111,68 @@ estack_pop(void)
 
 /*
  * Get the current value for <sfile> in allocated memory.
+ * "is_sfile" is TRUE for <sfile> itself.
  */
     char_u *
-estack_sfile(void)
+estack_sfile(int is_sfile)
 {
     estack_T	*entry;
 #ifdef FEAT_EVAL
+    garray_T	ga;
     size_t	len;
     int		idx;
-    char	*res;
-    size_t	done;
+    etype_T	last_type = ETYPE_SCRIPT;
+    char	*type_name;
 #endif
 
     entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
-    if (entry->es_name == NULL)
-	return NULL;
 #ifdef FEAT_EVAL
-    if (entry->es_info.ufunc == NULL)
+    if (is_sfile && entry->es_type != ETYPE_UFUNC)
 #endif
+    {
+	if (entry->es_name == NULL)
+	    return NULL;
 	return vim_strsave(entry->es_name);
-
+    }
 #ifdef FEAT_EVAL
+    // Give information about each stack entry up to the root.
     // For a function we compose the call stack, as it was done in the past:
     //   "function One[123]..Two[456]..Three"
-    len = STRLEN(entry->es_name) + 10;
-    for (idx = exestack.ga_len - 2; idx >= 0; --idx)
+    ga_init2(&ga, sizeof(char), 100);
+    for (idx = 0; idx < exestack.ga_len; ++idx)
     {
 	entry = ((estack_T *)exestack.ga_data) + idx;
-	if (entry->es_name == NULL || entry->es_info.ufunc == NULL)
+	if (entry->es_name != NULL)
 	{
-	    ++idx;
-	    break;
+	    len = STRLEN(entry->es_name) + 15;
+	    type_name = "";
+	    if (entry->es_type != last_type)
+	    {
+		switch (entry->es_type)
+		{
+		    case ETYPE_SCRIPT: type_name = "script "; break;
+		    case ETYPE_UFUNC: type_name = "function "; break;
+		    default: type_name = ""; break;
+		}
+		last_type = entry->es_type;
+	    }
+	    len += STRLEN(type_name);
+	    if (ga_grow(&ga, len) == FAIL)
+		break;
+	    if (idx == exestack.ga_len - 1 || entry->es_lnum == 0)
+		// For the bottom entry: do not add the line number, it is used
+		// in <slnum>.  Also leave it out when the number is not set.
+		vim_snprintf(ga.ga_data + ga.ga_len, len, "%s%s%s",
+				type_name, entry->es_name,
+				idx == exestack.ga_len - 1 ? "" : "..");
+	    else
+		vim_snprintf(ga.ga_data + ga.ga_len, len, "%s%s[%ld]..",
+				    type_name, entry->es_name, entry->es_lnum);
+	    ga.ga_len += STRLEN(ga.ga_data + ga.ga_len);
 	}
-	len += STRLEN(entry->es_name) + 15;
     }
 
-    res = (char *)alloc((int)len);
-    if (res != NULL)
-    {
-	STRCPY(res, "function ");
-	while (idx < exestack.ga_len - 1)
-	{
-	    done = STRLEN(res);
-	    entry = ((estack_T *)exestack.ga_data) + idx;
-	    vim_snprintf(res + done, len - done, "%s[%ld]..",
-					       entry->es_name, entry->es_lnum);
-	    ++idx;
-	}
-	done = STRLEN(res);
-	entry = ((estack_T *)exestack.ga_data) + idx;
-	vim_snprintf(res + done, len - done, "%s", entry->es_name);
-    }
-    return (char_u *)res;
+    return (char_u *)ga.ga_data;
 #endif
 }
 
--- a/src/testdir/test_expand_func.vim
+++ b/src/testdir/test_expand_func.vim
@@ -16,17 +16,47 @@ func s:expand_sflnum()
   return str2nr(expand('<sflnum>'))  
 endfunc
 
-func Test_expand_sfile()
-  call assert_match('test_expand_func\.vim$', s:sfile)
-  call assert_match('^function .*\.\.Test_expand_sfile$', expand('<sfile>'))
+" This test depends on the location in the test file, put it first.
+func Test_expand_sflnum()
+  call assert_equal(5, s:sflnum)
+  call assert_equal(22, str2nr(expand('<sflnum>')))
+
+  " Line-continuation
+  call assert_equal(
+        \ 25,
+        \ str2nr(expand('<sflnum>')))
 
   " Call in script-local function
-  call assert_match('^function .*\.\.Test_expand_sfile\[5\]\.\.<SNR>\d\+_expand_sfile$', s:expand_sfile())
+  call assert_equal(16, s:expand_sflnum())
+
+  " Call in command
+  command Flnum echo expand('<sflnum>')
+  call assert_equal(34, str2nr(trim(execute('Flnum'))))
+  delcommand Flnum
+endfunc
+
+func Test_expand_sfile_and_stack()
+  call assert_match('test_expand_func\.vim$', s:sfile)
+  let expected = 'script .*testdir/runtest.vim\[\d\+\]\.\.function RunTheTest\[\d\+\]\.\.Test_expand_sfile_and_stack$'
+  call assert_match(expected , expand('<sfile>'))
+  call assert_match(expected , expand('<stack>'))
+
+  " Call in script-local function
+  call assert_match('script .*testdir/runtest.vim\[\d\+\]\.\.function RunTheTest\[\d\+\]\.\.Test_expand_sfile_and_stack\[7\]\.\.<SNR>\d\+_expand_sfile$', s:expand_sfile())
 
   " Call in command
   command Sfile echo expand('<sfile>')
-  call assert_match('^function .*\.\.Test_expand_sfile$', trim(execute('Sfile')))
+  call assert_match('script .*testdir/runtest.vim\[\d\+\]\.\.function RunTheTest\[\d\+\]\.\.Test_expand_sfile_and_stack$', trim(execute('Sfile')))
   delcommand Sfile
+
+  " Use <stack> from sourced script.
+  let lines =<< trim END
+    let g:stack_value = expand('<stack>')
+  END
+  call writefile(lines, 'Xstack')
+  source Xstack
+  call assert_match('\<Xstack$', g:stack_value)
+  call delete('Xstack')
 endfunc
 
 func Test_expand_slnum()
@@ -47,24 +77,6 @@ func Test_expand_slnum()
   delcommand Slnum
 endfunc
 
-func Test_expand_sflnum()
-  call assert_equal(5, s:sflnum)
-  call assert_equal(52, str2nr(expand('<sflnum>')))
-
-  " Line-continuation
-  call assert_equal(
-        \ 55,
-        \ str2nr(expand('<sflnum>')))
-
-  " Call in script-local function
-  call assert_equal(16, s:expand_sflnum())
-
-  " Call in command
-  command Flnum echo expand('<sflnum>')
-  call assert_equal(64, str2nr(trim(execute('Flnum'))))
-  delcommand Flnum
-endfunc
-
 func Test_expand()
   new
   call assert_equal("",  expand('%:S'))
--- a/src/testing.c
+++ b/src/testing.c
@@ -22,7 +22,7 @@
 prepare_assert_error(garray_T *gap)
 {
     char    buf[NUMBUFLEN];
-    char_u  *sname = estack_sfile();
+    char_u  *sname = estack_sfile(FALSE);
 
     ga_init2(gap, 1, 100);
     if (sname != NULL)
--- 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 */
 /**/
+    1297,
+/**/
     1296,
 /**/
     1295,