patch 9.0.2059: outstanding exceptions may be skipped

Problem:  outstanding exceptions may be skipped
Solution: When restoring exception state, process remaining outstanding
          exceptions

closes: #13386

Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
This commit is contained in:
Yegappan Lakshmanan
2023-10-21 11:59:42 +02:00
committed by Christian Brabandt
parent a36acb7ac4
commit 0ab500dede
9 changed files with 173 additions and 18 deletions

View File

@ -443,7 +443,8 @@ Any return value of the deferred function is discarded. The function cannot
be followed by anything, such as "->func" or ".member". Currently `:defer
GetArg()->TheFunc()` does not work, it may work in a later version.
Errors are reported but do not cause aborting execution of deferred functions.
Errors are reported but do not cause aborting execution of deferred functions
or altering execution outside of deferred functions.
No range is accepted. The function can be a partial with extra arguments, but
not with a dictionary. *E1300*

View File

@ -28,7 +28,8 @@ Vim9 classes, objects, interfaces, types and enums.
The fancy term is "object-oriented programming". You can find lots of study
material on this subject. Here we document what |Vim9| script provides,
assuming you know the basics already. Added are helpful hints about how to
use this functionality effectively.
use this functionality effectively. Vim9 classes and objects cannot be used
in legacy Vim scripts and legacy functions.
The basic item is an object:
- An object stores state. It contains one or more variables that can each

View File

@ -757,6 +757,7 @@ exception_state_save(exception_state_T *estate)
estate->estate_did_throw = did_throw;
estate->estate_need_rethrow = need_rethrow;
estate->estate_trylevel = trylevel;
estate->estate_did_emsg = did_emsg;
}
/*
@ -765,11 +766,14 @@ exception_state_save(exception_state_T *estate)
void
exception_state_restore(exception_state_T *estate)
{
if (current_exception == NULL)
current_exception = estate->estate_current_exception;
did_throw |= estate->estate_did_throw;
need_rethrow |= estate->estate_need_rethrow;
trylevel |= estate->estate_trylevel;
// Handle any outstanding exceptions before restoring the state
if (did_throw)
handle_did_throw();
current_exception = estate->estate_current_exception;
did_throw = estate->estate_did_throw;
need_rethrow = estate->estate_need_rethrow;
trylevel = estate->estate_trylevel;
did_emsg = estate->estate_did_emsg;
}
/*
@ -782,6 +786,7 @@ exception_state_clear(void)
did_throw = FALSE;
need_rethrow = FALSE;
trylevel = 0;
did_emsg = 0;
}
/*

View File

@ -1099,6 +1099,7 @@ struct exception_state_S
int estate_did_throw;
int estate_need_rethrow;
int estate_trylevel;
int estate_did_emsg;
};
#ifdef FEAT_SYN_HL

View File

@ -904,7 +904,68 @@ func Test_defer_after_exception()
delfunc Defer
delfunc Foo
delfunc Bar
unlet g:callTrace
endfunc
" Test for multiple deferred function which throw exceptions.
" Exceptions thrown by deferred functions should result in error messages but
" not propagated into the calling functions.
func Test_multidefer_with_exception()
let g:callTrace = []
func Except()
let g:callTrace += [1]
throw 'InnerException'
let g:callTrace += [2]
endfunc
func FirstDefer()
let g:callTrace += [3]
let g:callTrace += [4]
endfunc
func SecondDeferWithExcept()
let g:callTrace += [5]
call Except()
let g:callTrace += [6]
endfunc
func ThirdDefer()
let g:callTrace += [7]
let g:callTrace += [8]
endfunc
func Foo()
let g:callTrace += [9]
defer FirstDefer()
defer SecondDeferWithExcept()
defer ThirdDefer()
let g:callTrace += [10]
endfunc
let v:errmsg = ''
try
let g:callTrace += [11]
call Foo()
let g:callTrace += [12]
catch /TestException/
let g:callTrace += [13]
catch
let g:callTrace += [14]
finally
let g:callTrace += [15]
endtry
let g:callTrace += [16]
call assert_equal('E605: Exception not caught: InnerException', v:errmsg)
call assert_equal([11, 9, 10, 7, 8, 5, 1, 3, 4, 12, 15, 16], g:callTrace)
unlet g:callTrace
delfunc Except
delfunc FirstDefer
delfunc SecondDeferWithExcept
delfunc ThirdDefer
delfunc Foo
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@ -8347,10 +8347,10 @@ def Test_class_variable_as_operands()
public static TruthyFn: func
static list: list<any> = []
static four: number = 4
static hello: string = 'hello'
static str: string = 'hello'
static def Hello(): string
return hello
static def Str(): string
return str
enddef
static def Four(): number
@ -8374,8 +8374,17 @@ def Test_class_variable_as_operands()
assert_equal(16, 1 << Tests.four)
assert_equal(8, Tests.four + four)
assert_equal(8, four + Tests.four)
assert_equal('hellohello', Tests.hello .. hello)
assert_equal('hellohello', hello .. Tests.hello)
assert_equal('hellohello', Tests.str .. str)
assert_equal('hellohello', str .. Tests.str)
# Using class variable for list indexing
var l = range(10)
assert_equal(4, l[Tests.four])
assert_equal([4, 5, 6], l[Tests.four : Tests.four + 2])
# Using class variable for Dict key
var d = {hello: 'abc'}
assert_equal('abc', d[Tests.str])
enddef
endclass
@ -8390,8 +8399,17 @@ def Test_class_variable_as_operands()
assert_equal(16, 1 << Tests.four)
assert_equal(8, Tests.four + Tests.Four())
assert_equal(8, Tests.Four() + Tests.four)
assert_equal('hellohello', Tests.hello .. Tests.Hello())
assert_equal('hellohello', Tests.Hello() .. Tests.hello)
assert_equal('hellohello', Tests.str .. Tests.Str())
assert_equal('hellohello', Tests.Str() .. Tests.str)
# Using class variable for list indexing
var l = range(10)
assert_equal(4, l[Tests.four])
assert_equal([4, 5, 6], l[Tests.four : Tests.four + 2])
# Using class variable for Dict key
var d = {hello: 'abc'}
assert_equal('abc', d[Tests.str])
enddef
Tests.TruthyFn = Tests.Truthy
@ -8409,8 +8427,17 @@ def Test_class_variable_as_operands()
assert_equal(16, 1 << Tests.four)
assert_equal(8, Tests.four + Tests.Four())
assert_equal(8, Tests.Four() + Tests.four)
assert_equal('hellohello', Tests.hello .. Tests.Hello())
assert_equal('hellohello', Tests.Hello() .. Tests.hello)
assert_equal('hellohello', Tests.str .. Tests.Str())
assert_equal('hellohello', Tests.Str() .. Tests.str)
# Using class variable for list indexing
var l = range(10)
assert_equal(4, l[Tests.four])
assert_equal([4, 5, 6], l[Tests.four : Tests.four + 2])
# Using class variable for Dict key
var d = {hello: 'abc'}
assert_equal('abc', d[Tests.str])
END
v9.CheckSourceSuccess(lines)
enddef

View File

@ -4725,6 +4725,64 @@ def Test_defer_after_exception()
v9.CheckScriptSuccess(lines)
enddef
" Test for multiple deferred function which throw exceptions.
" Exceptions thrown by deferred functions should result in error messages but
" not propagated into the calling functions.
def Test_multidefer_with_exception()
var lines =<< trim END
vim9script
var callTrace: list<number> = []
def Except()
callTrace += [1]
throw 'InnerException'
callTrace += [2]
enddef
def FirstDefer()
callTrace += [3]
callTrace += [4]
enddef
def SecondDeferWithExcept()
callTrace += [5]
Except()
callTrace += [6]
enddef
def ThirdDefer()
callTrace += [7]
callTrace += [8]
enddef
def Foo()
callTrace += [9]
defer FirstDefer()
defer SecondDeferWithExcept()
defer ThirdDefer()
callTrace += [10]
enddef
v:errmsg = ''
try
callTrace += [11]
Foo()
callTrace += [12]
catch /TestException/
callTrace += [13]
catch
callTrace += [14]
finally
callTrace += [15]
endtry
callTrace += [16]
assert_equal('E605: Exception not caught: InnerException', v:errmsg)
assert_equal([11, 9, 10, 7, 8, 5, 1, 3, 4, 12, 15, 16], callTrace)
END
v9.CheckScriptSuccess(lines)
enddef
" Keep this last, it messes up highlighting.
def Test_substitute_cmd()
new

View File

@ -704,6 +704,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
2059,
/**/
2058,
/**/

View File

@ -2340,8 +2340,7 @@ class_object_index(
}
if (did_emsg == did_emsg_save)
member_not_found_msg(cl, is_object ? VAR_OBJECT : VAR_CLASS, name,
len);
member_not_found_msg(cl, rettv->v_type, name, len);
}
return FAIL;