changeset 20899:e76bddcf3341 v8.2.1001

patch 8.2.1001: Vim9: crash with nested "if" and assignment Commit: https://github.com/vim/vim/commit/72abcf42d4b28719863c3bdf72b4c05d147a7d83 Author: Bram Moolenaar <Bram@vim.org> Date: Thu Jun 18 18:26:24 2020 +0200 patch 8.2.1001: Vim9: crash with nested "if" and assignment Problem: Vim9: crash with nested "if" and assignment. Solution: Skip more of the assignment. Do not set ctx_skip when code is reachable.
author Bram Moolenaar <Bram@vim.org>
date Thu, 18 Jun 2020 18:30:03 +0200
parents d3cd1a4d18e2
children b3ace9daf20b
files src/testdir/test_vim9_script.vim src/version.c src/vim9compile.c
diffstat 3 files changed, 72 insertions(+), 39 deletions(-) [+]
line wrap: on
line diff
--- a/src/testdir/test_vim9_script.vim
+++ b/src/testdir/test_vim9_script.vim
@@ -1162,6 +1162,26 @@ def Test_if_const_expr_fails()
   call CheckDefFailure(["if has('aaa') ? true false"], 'E109:')
 enddef
 
+def RunNested(i: number): number
+  let x: number = 0
+  if i % 2
+    if 1
+      " comment
+    else
+      " comment
+    endif
+    x += 1
+  else
+    x += 1000
+  endif
+  return x
+enddef
+
+def Test_nested_if()
+  assert_equal(1, RunNested(1))
+  assert_equal(1000, RunNested(2))
+enddef
+
 def Test_execute_cmd()
   new
   setline(1, 'default')
--- 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 */
 /**/
+    1001,
+/**/
     1000,
 /**/
     999,
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -5067,8 +5067,19 @@ compile_assignment(char_u *arg, exarg_T 
 
 	if (!heredoc)
 	{
-	    if (oplen > 0)
+	    if (cctx->ctx_skip == TRUE)
 	    {
+		if (oplen > 0 && var_count == 0)
+		{
+		    // skip over the "=" and the expression
+		    p = skipwhite(op + oplen);
+		    compile_expr0(&p, cctx);
+		}
+	    }
+	    else if (oplen > 0)
+	    {
+		type_T	*stacktype;
+
 		// For "var = expr" evaluate the expression.
 		if (var_count == 0)
 		{
@@ -5113,52 +5124,47 @@ compile_assignment(char_u *arg, exarg_T 
 			return FAIL;
 		}
 
-		if (cctx->ctx_skip != TRUE)
+		stacktype = stack->ga_len == 0 ? &t_void
+			  : ((type_T **)stack->ga_data)[stack->ga_len - 1];
+		if (lvar != NULL && (is_decl || !has_type))
 		{
-		    type_T	*stacktype;
-
-		    stacktype = stack->ga_len == 0 ? &t_void
-			      : ((type_T **)stack->ga_data)[stack->ga_len - 1];
-		    if (lvar != NULL && (is_decl || !has_type))
+		    if (new_local && !has_type)
 		    {
-			if (new_local && !has_type)
+			if (stacktype->tt_type == VAR_VOID)
 			{
-			    if (stacktype->tt_type == VAR_VOID)
-			    {
-				emsg(_(e_cannot_use_void));
-				goto theend;
-			    }
-			    else
-			    {
-				// An empty list or dict has a &t_void member,
-				// for a variable that implies &t_any.
-				if (stacktype == &t_list_empty)
-				    lvar->lv_type = &t_list_any;
-				else if (stacktype == &t_dict_empty)
-				    lvar->lv_type = &t_dict_any;
-				else
-				    lvar->lv_type = stacktype;
-			    }
+			    emsg(_(e_cannot_use_void));
+			    goto theend;
 			}
 			else
 			{
-			    type_T *use_type = lvar->lv_type;
-
-			    if (has_index)
-			    {
-				use_type = use_type->tt_member;
-				if (use_type == NULL)
-				    use_type = &t_void;
-			    }
-			    if (need_type(stacktype, use_type, -1, cctx)
-								       == FAIL)
-				goto theend;
+			    // An empty list or dict has a &t_void member,
+			    // for a variable that implies &t_any.
+			    if (stacktype == &t_list_empty)
+				lvar->lv_type = &t_list_any;
+			    else if (stacktype == &t_dict_empty)
+				lvar->lv_type = &t_dict_any;
+			    else
+				lvar->lv_type = stacktype;
 			}
 		    }
-		    else if (*p != '=' && need_type(stacktype, member_type, -1,
-								 cctx) == FAIL)
-			goto theend;
+		    else
+		    {
+			type_T *use_type = lvar->lv_type;
+
+			if (has_index)
+			{
+			    use_type = use_type->tt_member;
+			    if (use_type == NULL)
+				use_type = &t_void;
+			}
+			if (need_type(stacktype, use_type, -1, cctx)
+								   == FAIL)
+			    goto theend;
+		    }
 		}
+		else if (*p != '=' && need_type(stacktype, member_type, -1,
+							     cctx) == FAIL)
+		    goto theend;
 	    }
 	    else if (cmdidx == CMD_const)
 	    {
@@ -5220,6 +5226,10 @@ compile_assignment(char_u *arg, exarg_T 
 		end = p;
 	}
 
+	// no need to parse more when skipping
+	if (cctx->ctx_skip == TRUE)
+	    break;
+
 	if (oplen > 0 && *op != '=')
 	{
 	    type_T	    *expected = &t_number;
@@ -5806,7 +5816,8 @@ compile_endif(char_u *arg, cctx_T *cctx)
     }
     // Fill in the "end" label in jumps at the end of the blocks.
     compile_fill_jump_to_end(&ifscope->is_end_label, cctx);
-    cctx->ctx_skip = FALSE;
+    // TODO: this should restore the value from before the :if
+    cctx->ctx_skip = MAYBE;
 
     drop_scope(cctx);
     return arg;