changeset 21371:8e1081ede3b8 v8.2.1236

patch 8.2.1236: Vim9: a few errors not caught by try/catch Commit: https://github.com/vim/vim/commit/e859312e748297bde67a053fd3c486fc2c14b532 Author: Bram Moolenaar <Bram@vim.org> Date: Sat Jul 18 15:17:02 2020 +0200 patch 8.2.1236: Vim9: a few errors not caught by try/catch Problem: Vim9: a few errors not caught by try/catch. Solution: Do not bail out if an error is inside try/catch. Fix that a not matching catch doesn't jump to :endtry.
author Bram Moolenaar <Bram@vim.org>
date Sat, 18 Jul 2020 15:30:03 +0200
parents a652dd15819a
children 86c5173b29b1
files src/testdir/test_vim9_script.vim src/version.c src/vim9compile.c src/vim9execute.c
diffstat 4 files changed, 113 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- a/src/testdir/test_vim9_script.vim
+++ b/src/testdir/test_vim9_script.vim
@@ -511,6 +511,22 @@ def Test_try_catch()
   endtry # comment
   assert_equal(['1', 'wrong', '3'], l)
 
+  l = []
+  try
+    try
+      add(l, '1')
+      throw 'wrong'
+      add(l, '2')
+    catch /right/
+      add(l, v:exception)
+    endtry
+  catch /wrong/
+    add(l, 'caught')
+  finally
+    add(l, 'finally')
+  endtry
+  assert_equal(['1', 'caught', 'finally'], l)
+
   let n: number
   try
     n = l[3]
@@ -591,15 +607,63 @@ def Test_try_catch()
   endtry
   assert_equal(277, n)
 
-  #  TODO: make this work
-  #  try
-  #    &ts = g:astring
-  #  catch /E1093:/
-  #    n = 288
-  #  endtry
-  #  assert_equal(288, n)
+  try
+    &ts = g:astring
+  catch /E1029:/
+    n = 288
+  endtry
+  assert_equal(288, n)
+
+  try
+    &backspace = 'asdf'
+  catch /E474:/
+    n = 299
+  endtry
+  assert_equal(299, n)
+
+  l = [1]
+  try
+    l[3] = 3
+  catch /E684:/
+    n = 300
+  endtry
+  assert_equal(300, n)
+
+  try
+    d[''] = 3
+  catch /E713:/
+    n = 311
+  endtry
+  assert_equal(311, n)
+
+  try
+    unlet g:does_not_exist
+  catch /E108:/
+    n = 322
+  endtry
+  assert_equal(322, n)
+
+  try
+    d = {'text': 1, g:astring: 2}
+  catch /E721:/
+    n = 333
+  endtry
+  assert_equal(333, n)
+
+  try
+    l = DeletedFunc()
+  catch /E933:/
+    n = 344
+  endtry
+  assert_equal(344, n)
 enddef
 
+def DeletedFunc(): list<any>
+  return ['delete me']
+enddef
+defcompile
+delfunc DeletedFunc
+
 def ThrowFromDef()
   throw "getout" # comment
 enddef
--- 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 */
 /**/
+    1236,
+/**/
     1235,
 /**/
     1234,
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -4961,6 +4961,7 @@ compile_assignment(char_u *arg, exarg_T 
 	    }
 	    if (need_type(stacktype, &t_list_any, -1, cctx, FALSE) == FAIL)
 		goto theend;
+	    // TODO: check the length of a constant list here
 	    generate_CHECKLEN(cctx, semicolon ? var_count - 1 : var_count,
 								    semicolon);
 	}
@@ -6539,6 +6540,7 @@ compile_finally(char_u *arg, cctx_T *cct
 	// Previous catch without match jumps here
 	isn = ((isn_T *)instr->ga_data) + scope->se_u.se_try.ts_catch_label;
 	isn->isn_arg.jump.jump_where = instr->ga_len;
+	scope->se_u.se_try.ts_catch_label = 0;
     }
 
     // TODO: set index in ts_finally_label jumps
@@ -6584,8 +6586,18 @@ compile_endtry(char_u *arg, cctx_T *cctx
     compile_fill_jump_to_end(&scope->se_u.se_try.ts_end_label, cctx);
 
     // End :catch or :finally scope: set value in ISN_TRY instruction
+    if (isn->isn_arg.try.try_catch == 0)
+	isn->isn_arg.try.try_catch = instr->ga_len;
     if (isn->isn_arg.try.try_finally == 0)
 	isn->isn_arg.try.try_finally = instr->ga_len;
+
+    if (scope->se_u.se_try.ts_catch_label != 0)
+    {
+	// Last catch without match jumps here
+	isn = ((isn_T *)instr->ga_data) + scope->se_u.se_try.ts_catch_label;
+	isn->isn_arg.jump.jump_where = instr->ga_len;
+    }
+
     compile_endblock(cctx);
 
     if (generate_instr(cctx, ISN_ENDTRY) == NULL)
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -1241,7 +1241,7 @@ call_def_function(
 		    if (msg != NULL)
 		    {
 			emsg(_(msg));
-			goto failed;
+			goto on_error;
 		    }
 		}
 		break;
@@ -1272,7 +1272,8 @@ call_def_function(
 		--ectx.ec_stack.ga_len;
 		if (set_vim_var_tv(iptr->isn_arg.number, STACK_TV_BOT(0))
 								       == FAIL)
-		    goto failed;
+		    // should not happen, type is checked when compiling
+		    goto on_error;
 		break;
 
 	    // store g:/b:/w:/t: variable
@@ -1335,7 +1336,7 @@ call_def_function(
 		    if (lidx < 0 || lidx > list->lv_len)
 		    {
 			semsg(_(e_listidx), lidx);
-			goto failed;
+			goto on_error;
 		    }
 		    tv = STACK_TV_BOT(-3);
 		    if (lidx < list->lv_len)
@@ -1348,7 +1349,7 @@ call_def_function(
 		    }
 		    else
 		    {
-			// append to list
+			// append to list, only fails when out of memory
 			if (list_append_tv(list, tv) == FAIL)
 			    goto failed;
 			clear_tv(tv);
@@ -1371,18 +1372,19 @@ call_def_function(
 		    if (key == NULL || *key == NUL)
 		    {
 			emsg(_(e_emptykey));
-			goto failed;
+			goto on_error;
 		    }
 		    tv = STACK_TV_BOT(-3);
 		    di = dict_find(dict, key, -1);
 		    if (di != NULL)
 		    {
+			// overwrite existing value
 			clear_tv(&di->di_tv);
 			di->di_tv = *tv;
 		    }
 		    else
 		    {
-			// add to dict
+			// add to dict, only fails when out of memory
 			if (dict_add_tv(dict, (char *)key, tv) == FAIL)
 			    goto failed;
 			clear_tv(tv);
@@ -1465,7 +1467,7 @@ call_def_function(
 	    case ISN_UNLET:
 		if (do_unlet(iptr->isn_arg.unlet.ul_name,
 				       iptr->isn_arg.unlet.ul_forceit) == FAIL)
-		    goto failed;
+		    goto on_error;
 		break;
 	    case ISN_UNLETENV:
 		vim_unsetenv(iptr->isn_arg.unlet.ul_name);
@@ -1481,24 +1483,38 @@ call_def_function(
 	    // create a dict from items on the stack
 	    case ISN_NEWDICT:
 		{
-		    int	    count = iptr->isn_arg.number;
-		    dict_T  *dict = dict_alloc();
-		    dictitem_T *item;
+		    int		count = iptr->isn_arg.number;
+		    dict_T	*dict = dict_alloc();
+		    dictitem_T	*item;
 
 		    if (dict == NULL)
 			goto failed;
 		    for (idx = 0; idx < count; ++idx)
 		    {
-			// check key type is VAR_STRING
+			// have already checked key type is VAR_STRING
 			tv = STACK_TV_BOT(2 * (idx - count));
+			// check key is unique
+			item = dict_find(dict, tv->vval.v_string, -1);
+			if (item != NULL)
+			{
+			    semsg(_(e_duplicate_key), tv->vval.v_string);
+			    dict_unref(dict);
+			    goto on_error;
+			}
 			item = dictitem_alloc(tv->vval.v_string);
 			clear_tv(tv);
 			if (item == NULL)
+			{
+			    dict_unref(dict);
 			    goto failed;
+			}
 			item->di_tv = *STACK_TV_BOT(2 * (idx - count) + 1);
 			item->di_tv.v_lock = 0;
 			if (dict_add(dict, item) == FAIL)
+			{
+			    dict_unref(dict);
 			    goto failed;
+			}
 		    }
 
 		    if (count > 0)
@@ -1519,7 +1535,7 @@ call_def_function(
 		if (call_dfunc(iptr->isn_arg.dfunc.cdf_idx,
 			      iptr->isn_arg.dfunc.cdf_argcount,
 			      &ectx) == FAIL)
-		    goto failed;
+		    goto on_error;
 		break;
 
 	    // call a builtin function