patch 8.2.4071: Vim9: no detection of return in try/endtry
Problem: Vim9: no detection of return in try/endtry. (Dominique Pellé)
Solution: Check if any of the blocks inside try/endtry did not end in
return.
diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim
index 04890bc..8bf2e9e 100644
--- a/src/testdir/test_vim9_script.vim
+++ b/src/testdir/test_vim9_script.vim
@@ -667,7 +667,6 @@
finally
return 6
endtry
- return -1
enddef
assert_equal(6, ReturnInFinally())
@@ -708,6 +707,64 @@
CheckDefAndScriptSuccess(lines)
enddef
+def Test_try_ends_in_return()
+ var lines =<< trim END
+ vim9script
+ def Foo(): string
+ try
+ return 'foo'
+ catch
+ return 'caught'
+ endtry
+ enddef
+ assert_equal('foo', Foo())
+ END
+ CheckScriptSuccess(lines)
+
+ lines =<< trim END
+ vim9script
+ def Foo(): string
+ try
+ return 'foo'
+ catch
+ return 'caught'
+ endtry
+ echo 'notreached'
+ enddef
+ assert_equal('foo', Foo())
+ END
+ CheckScriptFailure(lines, 'E1095:')
+
+ lines =<< trim END
+ vim9script
+ def Foo(): string
+ try
+ return 'foo'
+ catch /x/
+ return 'caught'
+ endtry
+ enddef
+ assert_equal('foo', Foo())
+ END
+ CheckScriptFailure(lines, 'E1027:')
+
+ lines =<< trim END
+ vim9script
+ def Foo(): string
+ try
+ echo 'foo'
+ catch
+ echo 'caught'
+ finally
+ return 'done'
+ endtry
+ enddef
+ assert_equal('done', Foo())
+ END
+ CheckScriptSuccess(lines)
+
+enddef
+
def Test_try_in_catch()
var lines =<< trim END
vim9script
diff --git a/src/version.c b/src/version.c
index 8c77e2e..2a1b4ae 100644
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 4071,
+/**/
4070,
/**/
4069,
diff --git a/src/vim9.h b/src/vim9.h
index 2ae3f42..912af1f 100644
--- a/src/vim9.h
+++ b/src/vim9.h
@@ -594,6 +594,8 @@
endlabel_T *ts_end_label; // jump to :finally or :endtry
int ts_catch_label; // instruction idx of last CATCH
int ts_caught_all; // "catch" without argument encountered
+ int ts_has_finally; // "finally" encountered
+ int ts_no_return; // one of the blocks did not end in return
} tryscope_T;
typedef enum {
diff --git a/src/vim9cmds.c b/src/vim9cmds.c
index 3372ad4..5beeab8 100644
--- a/src/vim9cmds.c
+++ b/src/vim9cmds.c
@@ -1343,6 +1343,8 @@
emsg(_(e_catch_unreachable_after_catch_all));
return NULL;
}
+ if (!cctx->ctx_had_return)
+ scope->se_u.se_try.ts_no_return = TRUE;
if (cctx->ctx_skip != SKIP_YES)
{
@@ -1498,6 +1500,7 @@
isn->isn_arg.jump.jump_where = this_instr;
scope->se_u.se_try.ts_catch_label = 0;
}
+ scope->se_u.se_try.ts_has_finally = TRUE;
if (generate_instr(cctx, ISN_FINALLY) == NULL)
return NULL;
}
@@ -1567,6 +1570,14 @@
}
}
+ // If there is a finally clause that ends in return then we will return.
+ // If one of the blocks didn't end in "return" or we did not catch all
+ // exceptions reset the had_return flag.
+ if (!(scope->se_u.se_try.ts_has_finally && cctx->ctx_had_return)
+ && (scope->se_u.se_try.ts_no_return
+ || !scope->se_u.se_try.ts_caught_all))
+ cctx->ctx_had_return = FALSE;
+
compile_endblock(cctx);
if (cctx->ctx_skip != SKIP_YES)
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 90cb2b4..7e4f99f 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -3041,7 +3041,6 @@
break;
case CMD_endtry:
line = compile_endtry(p, &cctx);
- cctx.ctx_had_return = FALSE;
break;
case CMD_throw:
line = compile_throw(p, &cctx);