view src/testdir/test_vimscript.vim @ 34074:1629cc65d78d v9.1.0006

patch 9.1.0006: is*() and to*() function may be unsafe Commit: https://github.com/vim/vim/commit/184f71cc6868a240dc872ed2852542bbc1d43e28 Author: Keith Thompson <Keith.S.Thompson@gmail.com> Date: Thu Jan 4 21:19:04 2024 +0100 patch 9.1.0006: is*() and to*() function may be unsafe Problem: is*() and to*() function may be unsafe Solution: Add SAFE_* macros and start using those instead (Keith Thompson) Use SAFE_() macros for is*() and to*() functions The standard is*() and to*() functions declared in <ctype.h> have undefined behavior for negative arguments other than EOF. If plain char is signed, passing an unchecked value from argv for from user input to one of these functions has undefined behavior. Solution: Add SAFE_*() macros that cast the argument to unsigned char. Most implementations behave sanely for negative arguments, and most character values in practice are non-negative, but it's still best to avoid undefined behavior. The change from #13347 has been omitted, as this has already been separately fixed in commit ac709e2fc0db6d31abb7da96f743c40956b60c3a (v9.0.2054) fixes: #13332 closes: #13347 Signed-off-by: Keith Thompson <Keith.S.Thompson@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
author Christian Brabandt <cb@256bit.org>
date Thu, 04 Jan 2024 21:30:04 +0100
parents def9fc5c92d1
children ddd5eaa2c0dc
line wrap: on
line source

" Test various aspects of the Vim script language.
" Most of this was formerly in test49.vim (developed by Servatius Brandt
" <Servatius.Brandt@fujitsu-siemens.com>)

source check.vim
source shared.vim
source script_util.vim

"-------------------------------------------------------------------------------
" Test environment							    {{{1
"-------------------------------------------------------------------------------

" Append a message to the "messages" file
func Xout(text)
    split messages
    $put =a:text
    wq
endfunc

com! -nargs=1	     Xout     call Xout(<args>)

" Create a new instance of Vim and run the commands in 'test' and then 'verify'
" The commands in 'test' are expected to store the test results in the Xtest.out
" file. If the test passes successfully, then Xtest.out should be empty.
func RunInNewVim(test, verify)
  let init =<< trim END
    set cpo-=C            " support line-continuation in sourced script
    source script_util.vim
    XpathINIT
    XloopINIT
  END
  let cleanup =<< trim END
    call writefile(v:errors, 'Xtest.out')
    qall
  END
  call writefile(init, 'Xtest.vim', 'D')
  call writefile(a:test, 'Xtest.vim', 'a')
  call writefile(a:verify, 'Xverify.vim', 'D')
  call writefile(cleanup, 'Xverify.vim', 'a')
  call RunVim([], [], "-S Xtest.vim -S Xverify.vim")
  call assert_equal([], readfile('Xtest.out'))
  call delete('Xtest.out')
endfunc

"-------------------------------------------------------------------------------
" Test 1:   :endwhile in function					    {{{1
"
"	    Detect if a broken loop is (incorrectly) reactivated by the
"	    :endwhile.  Use a :return to prevent an endless loop, and make
"	    this test first to get a meaningful result on an error before other
"	    tests will hang.
"-------------------------------------------------------------------------------

func T1_F()
    Xpath 'a'
    let first = 1
    while 1
	Xpath 'b'
	if first
	    Xpath 'c'
	    let first = 0
	    break
	else
	    Xpath 'd'
	    return
	endif
    endwhile
endfunc

func T1_G()
    Xpath 'h'
    let first = 1
    while 1
	Xpath 'i'
	if first
	    Xpath 'j'
	    let first = 0
	    break
	else
	    Xpath 'k'
	    return
	endif
	if 1	" unmatched :if
    endwhile
endfunc

func Test_endwhile_function()
  XpathINIT
  call T1_F()
  Xpath 'F'

  try
    call T1_G()
  catch
    " Catch missing :endif
    call assert_true(v:exception =~ 'E171:')
    Xpath 'x'
  endtry
  Xpath 'G'

  call assert_equal('abcFhijxG', g:Xpath)
endfunc

"-------------------------------------------------------------------------------
" Test 2:   :endwhile in script						    {{{1
"
"	    Detect if a broken loop is (incorrectly) reactivated by the
"	    :endwhile.  Use a :finish to prevent an endless loop, and place
"	    this test before others that might hang to get a meaningful result
"	    on an error.
"
"	    This test executes the bodies of the functions T1_F and T1_G from
"	    the previous test as script files (:return replaced by :finish).
"-------------------------------------------------------------------------------

func Test_endwhile_script()
  XpathINIT
  ExecAsScript T1_F
  Xpath 'F'
  call DeleteTheScript()

  try
    ExecAsScript T1_G
  catch
    " Catch missing :endif
    call assert_true(v:exception =~ 'E171:')
    Xpath 'x'
  endtry
  Xpath 'G'
  call DeleteTheScript()

  call assert_equal('abcFhijxG', g:Xpath)
endfunc

"-------------------------------------------------------------------------------
" Test 3:   :if, :elseif, :while, :continue, :break			    {{{1
"-------------------------------------------------------------------------------

func Test_if_while()
    XpathINIT
    if 1
	Xpath 'a'
	let loops = 3
	while loops > -1	    " main loop: loops == 3, 2, 1 (which breaks)
	    if loops <= 0
		let break_err = 1
		let loops = -1
	    else
		Xpath 'b' . loops
	    endif
	    if (loops == 2)
		while loops == 2 " dummy loop
		    Xpath 'c' . loops
		    let loops = loops - 1
		    continue    " stop dummy loop
		    Xpath 'd' . loops
		endwhile
		continue	    " continue main loop
		Xpath 'e' . loops
	    elseif (loops == 1)
		let p = 1
		while p	    " dummy loop
		    Xpath 'f' . loops
		    let p = 0
		    break	    " break dummy loop
		    Xpath 'g' . loops
		endwhile
		Xpath 'h' . loops
		unlet p
		break	    " break main loop
		Xpath 'i' . loops
	    endif
	    if (loops > 0)
		Xpath 'j' . loops
	    endif
	    while loops == 3    " dummy loop
		let loops = loops - 1
	    endwhile	    " end dummy loop
	endwhile		    " end main loop
	Xpath 'k'
    else
	Xpath 'l'
    endif
    Xpath 'm'
    if exists("break_err")
	Xpath 'm'
	unlet break_err
    endif

    unlet loops

    call assert_equal('ab3j3b2c2b1f1h1km', g:Xpath)
endfunc

" Check double quote after skipped "elseif" does not give error E15
func Test_skipped_elseif()
  if "foo" ==? "foo"
      let result = "first"
  elseif "foo" ==? "foo"
      let result = "second"
  endif
  call assert_equal('first', result)
endfunc

"-------------------------------------------------------------------------------
" Test 4:   :return							    {{{1
"-------------------------------------------------------------------------------

func T4_F()
    if 1
	Xpath 'a'
	let loops = 3
	while loops > 0				"    3:  2:     1:
	    Xpath 'b' . loops
	    if (loops == 2)
		Xpath 'c' . loops
		return
		Xpath 'd' . loops
	    endif
	    Xpath 'e' . loops
	    let loops = loops - 1
	endwhile
	Xpath 'f'
    else
	Xpath 'g'
    endif
endfunc

func Test_return()
    XpathINIT
    call T4_F()
    Xpath '4'

    call assert_equal('ab3e3b2c24', g:Xpath)
endfunc


"-------------------------------------------------------------------------------
" Test 5:   :finish							    {{{1
"
"	    This test executes the body of the function T4_F from the previous
"	    test as a script file (:return replaced by :finish).
"-------------------------------------------------------------------------------

func Test_finish()
    XpathINIT
    ExecAsScript T4_F
    Xpath '5'
    call DeleteTheScript()

    call assert_equal('ab3e3b2c25', g:Xpath)
endfunc



"-------------------------------------------------------------------------------
" Test 6:   Defining functions in :while loops				    {{{1
"
"	     Functions can be defined inside other functions.  An inner function
"	     gets defined when the outer function is executed.  Functions may
"	     also be defined inside while loops.  Expressions in braces for
"	     defining the function name are allowed.
"
"	     The functions are defined when sourcing the script, only the
"	     resulting path is checked in the test function.
"-------------------------------------------------------------------------------

XpathINIT

" The command CALL collects the argument of all its invocations in "calls"
" when used from a function (that is, when the global variable "calls" needs
" the "g:" prefix).  This is to check that the function code is skipped when
" the function is defined.  For inner functions, do so only if the outer
" function is not being executed.
"
let calls = ""
com! -nargs=1 CALL
	    \ if !exists("calls") && !exists("outer") |
	    \ let g:calls = g:calls . <args> |
	    \ endif

let i = 0
while i < 3
    let i = i + 1
    if i == 1
	Xpath 'a'
	function! F1(arg)
	    CALL a:arg
	    let outer = 1

	    let j = 0
	    while j < 1
		Xpath 'b'
		let j = j + 1
		function! G1(arg)
		    CALL a:arg
		endfunction
		Xpath 'c'
	    endwhile
	endfunction
	Xpath 'd'

	continue
    endif

    Xpath 'e' . i
    function! F{i}(i, arg)
	CALL a:arg
	let outer = 1

	if a:i == 3
	    Xpath 'f'
	endif
	let k = 0
	while k < 3
	    Xpath 'g' . k
	    let k = k + 1
	    function! G{a:i}{k}(arg)
		CALL a:arg
	    endfunction
	    Xpath 'h' . k
	endwhile
    endfunction
    Xpath 'i'

endwhile

if exists("*G1")
    Xpath 'j'
endif
if exists("*F1")
    call F1("F1")
    if exists("*G1")
       call G1("G1")
    endif
endif

if exists("G21") || exists("G22") || exists("G23")
    Xpath 'k'
endif
if exists("*F2")
    call F2(2, "F2")
    if exists("*G21")
       call G21("G21")
    endif
    if exists("*G22")
       call G22("G22")
    endif
    if exists("*G23")
       call G23("G23")
    endif
endif

if exists("G31") || exists("G32") || exists("G33")
    Xpath 'l'
endif
if exists("*F3")
    call F3(3, "F3")
    if exists("*G31")
       call G31("G31")
    endif
    if exists("*G32")
       call G32("G32")
    endif
    if exists("*G33")
       call G33("G33")
    endif
endif

Xpath 'm'

let g:test6_result = g:Xpath
let g:test6_calls = calls

unlet calls
delfunction F1
delfunction G1
delfunction F2
delfunction G21
delfunction G22
delfunction G23
delfunction G31
delfunction G32
delfunction G33

func Test_defining_functions()
    call assert_equal('ade2ie3ibcg0h1g1h2g2h3fg0h1g1h2g2h3m', g:test6_result)
    call assert_equal('F1G1F2G21G22G23F3G31G32G33', g:test6_calls)
endfunc

"-------------------------------------------------------------------------------
" Test 7:   Continuing on errors outside functions			    {{{1
"
"	    On an error outside a function, the script processing continues
"	    at the line following the outermost :endif or :endwhile.  When not
"	    inside an :if or :while, the script processing continues at the next
"	    line.
"-------------------------------------------------------------------------------

XpathINIT

if 1
    Xpath 'a'
    while 1
	Xpath 'b'
	asdf
	Xpath 'c'
	break
    endwhile | Xpath 'd'
    Xpath 'e'
endif | Xpath 'f'
Xpath 'g'

while 1
    Xpath 'h'
    if 1
	Xpath 'i'
	asdf
	Xpath 'j'
    endif | Xpath 'k'
    Xpath 'l'
    break
endwhile | Xpath 'm'
Xpath 'n'

asdf
Xpath 'o'

asdf | Xpath 'p'
Xpath 'q'

let g:test7_result = g:Xpath

func Test_error_in_script()
    call assert_equal('abghinoq', g:test7_result)
endfunc

"-------------------------------------------------------------------------------
" Test 8:   Aborting and continuing on errors inside functions		    {{{1
"
"	    On an error inside a function without the "abort" attribute, the
"	    script processing continues at the next line (unless the error was
"	    in a :return command).  On an error inside a function with the
"	    "abort" attribute, the function is aborted and the script processing
"	    continues after the function call; the value -1 is returned then.
"-------------------------------------------------------------------------------

XpathINIT

func T8_F()
    if 1
	Xpath 'a'
	while 1
	    Xpath 'b'
	    asdf
	    Xpath 'c'
	    asdf | Xpath 'd'
	    Xpath 'e'
	    break
	endwhile
	Xpath 'f'
    endif | Xpath 'g'
    Xpath 'h'

    while 1
	Xpath 'i'
	if 1
	    Xpath 'j'
	    asdf
	    Xpath 'k'
	    asdf | Xpath 'l'
	    Xpath 'm'
	endif
	Xpath 'n'
	break
    endwhile | Xpath 'o'
    Xpath 'p'

    return novar		" returns (default return value 0)
    Xpath 'q'
    return 1			" not reached
endfunc

func T8_G() abort
    if 1
	Xpath 'r'
	while 1
	    Xpath 's'
	    asdf		" returns -1
	    Xpath 't'
	    break
	endwhile
	Xpath 'v'
    endif | Xpath 'w'
    Xpath 'x'

    return -4			" not reached
endfunc

func T8_H() abort
    while 1
	Xpath 'A'
	if 1
	    Xpath 'B'
	    asdf		" returns -1
	    Xpath 'C'
	endif
	Xpath 'D'
	break
    endwhile | Xpath 'E'
    Xpath 'F'

    return -4			" not reached
endfunc

" Aborted functions (T8_G and T8_H) return -1.
let g:test8_sum = (T8_F() + 1) - 4 * T8_G() - 8 * T8_H()
Xpath 'X'
let g:test8_result = g:Xpath

func Test_error_in_function()
    call assert_equal(13, g:test8_sum)
    call assert_equal('abcefghijkmnoprsABX', g:test8_result)

    delfunction T8_F
    delfunction T8_G
    delfunction T8_H
endfunc


"-------------------------------------------------------------------------------
" Test 9:   Continuing after aborted functions				    {{{1
"
"	    When a function with the "abort" attribute is aborted due to an
"	    error, the next function back in the call hierarchy without an
"	    "abort" attribute continues; the value -1 is returned then.
"-------------------------------------------------------------------------------

XpathINIT

func F() abort
    Xpath 'a'
    let result = G()	" not aborted
    Xpath 'b'
    if result != 2
	Xpath 'c'
    endif
    return 1
endfunc

func G()		" no abort attribute
    Xpath 'd'
    if H() != -1	" aborted
	Xpath 'e'
    endif
    Xpath 'f'
    return 2
endfunc

func H() abort
    Xpath 'g'
    call I()		" aborted
    Xpath 'h'
    return 4
endfunc

func I() abort
    Xpath 'i'
    asdf		" error
    Xpath 'j'
    return 8
endfunc

if F() != 1
    Xpath 'k'
endif

let g:test9_result = g:Xpath

delfunction F
delfunction G
delfunction H
delfunction I

func Test_func_abort()
    call assert_equal('adgifb', g:test9_result)
endfunc


"-------------------------------------------------------------------------------
" Test 10:  :if, :elseif, :while argument parsing			    {{{1
"
"	    A '"' or '|' in an argument expression must not be mixed up with
"	    a comment or a next command after a bar.  Parsing errors should
"	    be recognized.
"-------------------------------------------------------------------------------

XpathINIT

func MSG(enr, emsg)
    let english = v:lang == "C" || v:lang =~ '^[Ee]n'
    if a:enr == ""
	Xout "TODO: Add message number for:" a:emsg
	let v:errmsg = ":" . v:errmsg
    endif
    let match = 1
    if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg)
	let match = 0
	if v:errmsg == ""
	    Xout "Message missing."
	else
	    let v:errmsg = v:errmsg->escape('"')
	    Xout "Unexpected message:" v:errmsg
	endif
    endif
    return match
endfunc

if 1 || strlen("\"") | Xpath 'a'
    Xpath 'b'
endif
Xpath 'c'

if 0
elseif 1 || strlen("\"") | Xpath 'd'
    Xpath 'e'
endif
Xpath 'f'

while 1 || strlen("\"") | Xpath 'g'
    Xpath 'h'
    break
endwhile
Xpath 'i'

let v:errmsg = ""
if 1 ||| strlen("\"") | Xpath 'j'
    Xpath 'k'
endif
Xpath 'l'
if !MSG('E15', "Invalid expression")
    Xpath 'm'
endif

let v:errmsg = ""
if 0
elseif 1 ||| strlen("\"") | Xpath 'n'
    Xpath 'o'
endif
Xpath 'p'
if !MSG('E15', "Invalid expression")
    Xpath 'q'
endif

let v:errmsg = ""
while 1 ||| strlen("\"") | Xpath 'r'
    Xpath 's'
    break
endwhile
Xpath 't'
if !MSG('E15', "Invalid expression")
    Xpath 'u'
endif

let g:test10_result = g:Xpath
delfunction MSG

func Test_expr_parsing()
    call assert_equal('abcdefghilpt', g:test10_result)
endfunc


"-------------------------------------------------------------------------------
" Test 11:  :if, :elseif, :while argument evaluation after abort	    {{{1
"
"	    When code is skipped over due to an error, the boolean argument to
"	    an :if, :elseif, or :while must not be evaluated.
"-------------------------------------------------------------------------------

XpathINIT

let calls = 0

func P(num)
    let g:calls = g:calls + a:num   " side effect on call
    return 0
endfunc

if 1
    Xpath 'a'
    asdf		" error
    Xpath 'b'
    if P(1)		" should not be called
	Xpath 'c'
    elseif !P(2)	" should not be called
	Xpath 'd'
    else
	Xpath 'e'
    endif
    Xpath 'f'
    while P(4)		" should not be called
	Xpath 'g'
    endwhile
    Xpath 'h'
endif
Xpath 'x'

let g:test11_calls = calls
let g:test11_result = g:Xpath

unlet calls
delfunction P

func Test_arg_abort()
    call assert_equal(0, g:test11_calls)
    call assert_equal('ax', g:test11_result)
endfunc


"-------------------------------------------------------------------------------
" Test 12:  Expressions in braces in skipped code			    {{{1
"
"	    In code skipped over due to an error or inactive conditional,
"	    an expression in braces as part of a variable or function name
"	    should not be evaluated.
"-------------------------------------------------------------------------------

XpathINIT

func NULL()
    Xpath 'a'
    return 0
endfunc

func ZERO()
    Xpath 'b'
    return 0
endfunc

func! F0()
    Xpath 'c'
endfunc

func! F1(arg)
    Xpath 'e'
endfunc

let V0 = 1

Xpath 'f'
echo 0 ? F{NULL() + V{ZERO()}}() : 1

Xpath 'g'
if 0
    Xpath 'h'
    call F{NULL() + V{ZERO()}}()
endif

Xpath 'i'
if 1
    asdf		" error
    Xpath 'j'
    call F1(F{NULL() + V{ZERO()}}())
endif

Xpath 'k'
if 1
    asdf		" error
    Xpath 'l'
    call F{NULL() + V{ZERO()}}()
endif

let g:test12_result = g:Xpath

func Test_braces_skipped()
    call assert_equal('fgik', g:test12_result)
endfunc


"-------------------------------------------------------------------------------
" Test 13:  Failure in argument evaluation for :while			    {{{1
"
"	    A failure in the expression evaluation for the condition of a :while
"	    causes the whole :while loop until the matching :endwhile being
"	    ignored.  Continuation is at the next following line.
"-------------------------------------------------------------------------------

XpathINIT

Xpath 'a'
while asdf
    Xpath 'b'
    while 1
	Xpath 'c'
	break
    endwhile
    Xpath 'd'
    break
endwhile
Xpath 'e'

while asdf | Xpath 'f' | endwhile | Xpath 'g'
Xpath 'h'
let g:test13_result = g:Xpath

func Test_while_fail()
    call assert_equal('aeh', g:test13_result)
endfunc


"-------------------------------------------------------------------------------
" Test 14:  Failure in argument evaluation for :if			    {{{1
"
"	    A failure in the expression evaluation for the condition of an :if
"	    does not cause the corresponding :else or :endif being matched to
"	    a previous :if/:elseif.  Neither of both branches of the failed :if
"	    are executed.
"-------------------------------------------------------------------------------

XpathINIT

function! F()
    Xpath 'a'
    let x = 0
    if x		" false
	Xpath 'b'
    elseif !x		" always true
	Xpath 'c'
	let x = 1
	if g:boolvar	" possibly undefined
	    Xpath 'd'
	else
	    Xpath 'e'
	endif
	Xpath 'f'
    elseif x		" never executed
	Xpath 'g'
    endif
    Xpath 'h'
endfunction

let boolvar = 1
call F()
Xpath '-'

unlet boolvar
call F()
let g:test14_result = g:Xpath

delfunction F

func Test_if_fail()
    call assert_equal('acdfh-acfh', g:test14_result)
endfunc


"-------------------------------------------------------------------------------
" Test 15:  Failure in argument evaluation for :if (bar)		    {{{1
"
"	    Like previous test, except that the failing :if ... | ... | :endif
"	    is in a single line.
"-------------------------------------------------------------------------------

XpathINIT

function! F()
    Xpath 'a'
    let x = 0
    if x		" false
	Xpath 'b'
    elseif !x		" always true
	Xpath 'c'
	let x = 1
	if g:boolvar | Xpath 'd' | else | Xpath 'e' | endif
	Xpath 'f'
    elseif x		" never executed
	Xpath 'g'
    endif
    Xpath 'h'
endfunction

let boolvar = 1
call F()
Xpath '-'

unlet boolvar
call F()
let g:test15_result = g:Xpath

delfunction F

func Test_if_bar_fail()
    call assert_equal('acdfh-acfh', g:test15_result)
endfunc

"-------------------------------------------------------------------------------
" Test 16:  Double :else or :elseif after :else				    {{{1
"
"	    Multiple :elses or an :elseif after an :else are forbidden.
"-------------------------------------------------------------------------------

func T16_F() abort
  if 0
    Xpath 'a'
  else
    Xpath 'b'
  else		" aborts function
    Xpath 'c'
  endif
  Xpath 'd'
endfunc

func T16_G() abort
  if 0
    Xpath 'a'
  else
    Xpath 'b'
  elseif 1		" aborts function
    Xpath 'c'
  else
    Xpath 'd'
  endif
  Xpath 'e'
endfunc

func T16_H() abort
  if 0
    Xpath 'a'
  elseif 0
    Xpath 'b'
  else
    Xpath 'c'
  else		" aborts function
    Xpath 'd'
  endif
  Xpath 'e'
endfunc

func T16_I() abort
  if 0
    Xpath 'a'
  elseif 0
    Xpath 'b'
  else
    Xpath 'c'
  elseif 1		" aborts function
    Xpath 'd'
  else
    Xpath 'e'
  endif
  Xpath 'f'
endfunc

func Test_Multi_Else()
  XpathINIT
  try
    call T16_F()
  catch /E583:/
    Xpath 'e'
  endtry
  call assert_equal('be', g:Xpath)

  XpathINIT
  try
    call T16_G()
  catch /E584:/
    Xpath 'f'
  endtry
  call assert_equal('bf', g:Xpath)

  XpathINIT
  try
    call T16_H()
  catch /E583:/
    Xpath 'f'
  endtry
  call assert_equal('cf', g:Xpath)

  XpathINIT
  try
    call T16_I()
  catch /E584:/
    Xpath 'g'
  endtry
  call assert_equal('cg', g:Xpath)
endfunc

"-------------------------------------------------------------------------------
" Test 17:  Nesting of unmatched :if or :endif inside a :while		    {{{1
"
"	    The :while/:endwhile takes precedence in nesting over an unclosed
"	    :if or an unopened :endif.
"-------------------------------------------------------------------------------

" While loops inside a function are continued on error.
func T17_F()
  let loops = 3
  while loops > 0
    let loops -= 1
    Xpath 'a' . loops
    if (loops == 1)
      Xpath 'b' . loops
      continue
    elseif (loops == 0)
      Xpath 'c' . loops
      break
    elseif 1
      Xpath 'd' . loops
    " endif missing!
  endwhile	" :endwhile after :if 1
  Xpath 'e'
endfunc

func T17_G()
  let loops = 2
  while loops > 0
    let loops -= 1
    Xpath 'a' . loops
    if 0
      Xpath 'b' . loops
    " endif missing
  endwhile	" :endwhile after :if 0
endfunc

func T17_H()
  let loops = 2
  while loops > 0
    let loops -= 1
    Xpath 'a' . loops
    " if missing!
    endif	" :endif without :if in while
    Xpath 'b' . loops
  endwhile
endfunc

" Error continuation outside a function is at the outermost :endwhile or :endif.
XpathINIT
let v:errmsg = ''
let loops = 2
while loops > 0
    let loops -= 1
    Xpath 'a' . loops
    if 0
	Xpath 'b' . loops
    " endif missing! Following :endwhile fails.
endwhile | Xpath 'c'
Xpath 'd'
call assert_match('E171:', v:errmsg)
call assert_equal('a1d', g:Xpath)

func Test_unmatched_if_in_while()
  XpathINIT
  call assert_fails('call T17_F()', 'E171:')
  call assert_equal('a2d2a1b1a0c0e', g:Xpath)

  XpathINIT
  call assert_fails('call T17_G()', 'E171:')
  call assert_equal('a1a0', g:Xpath)

  XpathINIT
  call assert_fails('call T17_H()', 'E580:')
  call assert_equal('a1b1a0b0', g:Xpath)
endfunc

"-------------------------------------------------------------------------------
" Test 18:  Interrupt (Ctrl-C pressed)					    {{{1
"
"	    On an interrupt, the script processing is terminated immediately.
"-------------------------------------------------------------------------------

func Test_interrupt_while_if()
  let test =<< trim [CODE]
    try
      if 1
        Xpath 'a'
        while 1
          Xpath 'b'
          if 1
            Xpath 'c'
            call interrupt()
            call assert_report('should not get here')
            break
            finish
          endif | call assert_report('should not get here')
          call assert_report('should not get here')
        endwhile | call assert_report('should not get here')
        call assert_report('should not get here')
      endif | call assert_report('should not get here')
      call assert_report('should not get here')
    catch /^Vim:Interrupt$/
      Xpath 'd'
    endtry | Xpath 'e'
    Xpath 'f'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcdef', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_interrupt_try()
  let test =<< trim [CODE]
    try
      try
        Xpath 'a'
        call interrupt()
        call assert_report('should not get here')
      endtry | call assert_report('should not get here')
      call assert_report('should not get here')
    catch /^Vim:Interrupt$/
      Xpath 'b'
    endtry | Xpath 'c'
    Xpath 'd'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcd', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_interrupt_func_while_if()
  let test =<< trim [CODE]
    func F()
      if 1
        Xpath 'a'
        while 1
          Xpath 'b'
          if 1
            Xpath 'c'
            call interrupt()
            call assert_report('should not get here')
            break
            return
          endif | call assert_report('should not get here')
          call assert_report('should not get here')
        endwhile | call assert_report('should not get here')
        call assert_report('should not get here')
      endif | call assert_report('should not get here')
      call assert_report('should not get here')
    endfunc

    Xpath 'd'
    try
      call F() | call assert_report('should not get here')
    catch /^Vim:Interrupt$/
      Xpath 'e'
    endtry | Xpath 'f'
    Xpath 'g'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('dabcefg', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_interrupt_func_try()
  let test =<< trim [CODE]
    func G()
      try
        Xpath 'a'
        call interrupt()
        call assert_report('should not get here')
      endtry | call assert_report('should not get here')
      call assert_report('should not get here')
    endfunc

    Xpath 'b'
    try
      call G() | call assert_report('should not get here')
    catch /^Vim:Interrupt$/
      Xpath 'c'
    endtry | Xpath 'd'
    Xpath 'e'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('bacde', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 19:  Aborting on errors inside :try/:endtry			    {{{1
"
"	    An error in a command dynamically enclosed in a :try/:endtry region
"	    aborts script processing immediately.  It does not matter whether
"	    the failing command is outside or inside a function and whether a
"	    function has an "abort" attribute.
"-------------------------------------------------------------------------------

func Test_try_error_abort_1()
  let test =<< trim [CODE]
    func F() abort
      Xpath 'a'
      asdf
      call assert_report('should not get here')
    endfunc

    try
      Xpath 'b'
      call F()
      call assert_report('should not get here')
    endtry | call assert_report('should not get here')
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('ba', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_try_error_abort_2()
  let test =<< trim [CODE]
    func G()
      Xpath 'a'
      asdf
      call assert_report('should not get here')
    endfunc

    try
      Xpath 'b'
      call G()
      call assert_report('should not get here')
    endtry | call assert_report('should not get here')
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('ba', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_try_error_abort_3()
  let test =<< trim [CODE]
    try
      Xpath 'a'
      asdf
      call assert_report('should not get here')
    endtry | call assert_report('should not get here')
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('a', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_try_error_abort_4()
  let test =<< trim [CODE]
    if 1
      try
        Xpath 'a'
        asdf
        call assert_report('should not get here')
      endtry | call assert_report('should not get here')
    endif | call assert_report('should not get here')
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('a', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_try_error_abort_5()
  let test =<< trim [CODE]
    let p = 1
    while p
      let p = 0
      try
        Xpath 'a'
        asdf
        call assert_report('should not get here')
      endtry | call assert_report('should not get here')
    endwhile | call assert_report('should not get here')
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('a', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_try_error_abort_6()
  let test =<< trim [CODE]
    let p = 1
    Xpath 'a'
    while p
      Xpath 'b'
      let p = 0
      try
        Xpath 'c'
    endwhile | call assert_report('should not get here')
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abc', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 20:  Aborting on errors after :try/:endtry			    {{{1
"
"	    When an error occurs after the last active :try/:endtry region has
"	    been left, termination behavior is as if no :try/:endtry has been
"	    seen.
"-------------------------------------------------------------------------------

func Test_error_after_try_1()
  let test =<< trim [CODE]
    let p = 1
    while p
      let p = 0
      Xpath 'a'
      try
        Xpath 'b'
      endtry
      asdf
      call assert_report('should not get here')
    endwhile | call assert_report('should not get here')
    Xpath 'c'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abc', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_error_after_try_2()
  let test =<< trim [CODE]
    while 1
      try
        Xpath 'a'
        break
        call assert_report('should not get here')
      endtry
    endwhile
    Xpath 'b'
    asdf
    Xpath 'c'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abc', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_error_after_try_3()
  let test =<< trim [CODE]
    while 1
      try
        Xpath 'a'
        break
        call assert_report('should not get here')
      finally
        Xpath 'b'
      endtry
    endwhile
    Xpath 'c'
    asdf
    Xpath 'd'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcd', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_error_after_try_4()
  let test =<< trim [CODE]
    while 1
      try
        Xpath 'a'
      finally
        Xpath 'b'
        break
        call assert_report('should not get here')
      endtry
    endwhile
    Xpath 'c'
    asdf
    Xpath 'd'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcd', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_error_after_try_5()
  let test =<< trim [CODE]
    let p = 1
    while p
      let p = 0
      try
        Xpath 'a'
        continue
        call assert_report('should not get here')
      endtry
    endwhile
    Xpath 'b'
    asdf
    Xpath 'c'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abc', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_error_after_try_6()
  let test =<< trim [CODE]
    let p = 1
    while p
      let p = 0
      try
        Xpath 'a'
        continue
        call assert_report('should not get here')
      finally
        Xpath 'b'
      endtry
    endwhile
    Xpath 'c'
    asdf
    Xpath 'd'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcd', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_error_after_try_7()
  let test =<< trim [CODE]
    let p = 1
    while p
      let p = 0
      try
        Xpath 'a'
      finally
        Xpath 'b'
        continue
        call assert_report('should not get here')
      endtry
    endwhile
    Xpath 'c'
    asdf
    Xpath 'd'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcd', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 21:  :finally for :try after :continue/:break/:return/:finish	    {{{1
"
"	    If a :try conditional stays inactive due to a preceding :continue,
"	    :break, :return, or :finish, its :finally clause should not be
"	    executed.
"-------------------------------------------------------------------------------

func Test_finally_after_loop_ctrl_statement()
  let test =<< trim [CODE]
    func F()
      let loops = 2
      while loops > 0
        XloopNEXT
        let loops = loops - 1
        try
          if loops == 1
            Xloop 'a'
            continue
            call assert_report('should not get here')
          elseif loops == 0
            Xloop 'b'
            break
            call assert_report('should not get here')
          endif

          try		" inactive
            call assert_report('should not get here')
          finally
            call assert_report('should not get here')
          endtry
        finally
          Xloop 'c'
        endtry
        call assert_report('should not get here')
      endwhile

      try
        Xpath 'd'
        return
        call assert_report('should not get here')
        try		    " inactive
          call assert_report('should not get here')
        finally
          call assert_report('should not get here')
        endtry
      finally
        Xpath 'e'
      endtry
      call assert_report('should not get here')
    endfunc

    try
      Xpath 'f'
      call F()
      Xpath 'g'
      finish
      call assert_report('should not get here')
      try		" inactive
        call assert_report('should not get here')
      finally
        call assert_report('should not get here')
      endtry
    finally
      Xpath 'h'
    endtry
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('fa2c2b3c3degh', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 22:  :finally for a :try after an error/interrupt/:throw		    {{{1
"
"	    If a :try conditional stays inactive due to a preceding error or
"	    interrupt or :throw, its :finally clause should not be executed.
"-------------------------------------------------------------------------------

func Test_finally_after_error_in_func()
  let test =<< trim [CODE]
    func Error()
      try
        Xpath 'b'
        asdf    " aborting error, triggering error exception
        call assert_report('should not get here')
      endtry
      call assert_report('should not get here')
    endfunc

    Xpath 'a'
    call Error()
    call assert_report('should not get here')

    if 1	" not active due to error
      try	" not active since :if inactive
        call assert_report('should not get here')
      finally
        call assert_report('should not get here')
      endtry
    endif

    try		" not active due to error
      call assert_report('should not get here')
    finally
      call assert_report('should not get here')
    endtry
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('ab', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_finally_after_interrupt()
  let test =<< trim [CODE]
    func Interrupt()
      try
        Xpath 'a'
        call interrupt()            " triggering interrupt exception
        call assert_report('should not get here')
      endtry
    endfunc

    Xpath 'b'
    try
      call Interrupt()
    catch /^Vim:Interrupt$/
      Xpath 'c'
      finish
    endtry
    call assert_report('should not get here')

    if 1	" not active due to interrupt
      try	" not active since :if inactive
        call assert_report('should not get here')
      finally
        call assert_report('should not get here')
      endtry
    endif

    try		" not active due to interrupt
      call assert_report('should not get here')
    finally
      call assert_report('should not get here')
    endtry
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('bac', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_finally_after_throw()
  let test =<< trim [CODE]
    func Throw()
      Xpath 'a'
      throw 'xyz'
    endfunc

    Xpath 'b'
    call Throw()
    call assert_report('should not get here')

    if 1	" not active due to :throw
      try	" not active since :if inactive
        call assert_report('should not get here')
      finally
        call assert_report('should not get here')
      endtry
    endif

    try		" not active due to :throw
      call assert_report('should not get here')
    finally
      call assert_report('should not get here')
    endtry
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('ba', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 23:  :catch clauses for a :try after a :throw			    {{{1
"
"	    If a :try conditional stays inactive due to a preceding :throw,
"	    none of its :catch clauses should be executed.
"-------------------------------------------------------------------------------

func Test_catch_after_throw()
  let test =<< trim [CODE]
    try
      Xpath 'a'
      throw "xyz"
      call assert_report('should not get here')

      if 1	" not active due to :throw
        try	" not active since :if inactive
          call assert_report('should not get here')
        catch /xyz/
          call assert_report('should not get here')
        endtry
      endif
    catch /xyz/
      Xpath 'b'
    endtry

    Xpath 'c'
    throw "abc"
    call assert_report('should not get here')

    try		" not active due to :throw
      call assert_report('should not get here')
    catch /abc/
      call assert_report('should not get here')
    endtry
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abc', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 24:  :endtry for a :try after a :throw				    {{{1
"
"	    If a :try conditional stays inactive due to a preceding :throw,
"	    its :endtry should not rethrow the exception to the next surrounding
"	    active :try conditional.
"-------------------------------------------------------------------------------

func Test_endtry_after_throw()
  let test =<< trim [CODE]
    try			" try 1
      try		" try 2
        Xpath 'a'
        throw "xyz"	" makes try 2 inactive
        call assert_report('should not get here')

        try		" try 3
          call assert_report('should not get here')
        endtry	" no rethrow to try 1
      catch /xyz/	" should catch although try 2 inactive
        Xpath 'b'
      endtry
    catch /xyz/		" try 1 active, but exception already caught
      call assert_report('should not get here')
    endtry
    Xpath 'c'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abc', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 27:  Executing :finally clauses after :return			    {{{1
"
"	    For a :return command dynamically enclosed in a :try/:endtry region,
"	    :finally clauses are executed and the called function is ended.
"-------------------------------------------------------------------------------

func T27_F()
  try
    Xpath 'a'
    try
      Xpath 'b'
      return
      call assert_report('should not get here')
    finally
      Xpath 'c'
    endtry
    Xpath 'd'
  finally
    Xpath 'e'
  endtry
  call assert_report('should not get here')
endfunc

func T27_G()
  try
    Xpath 'f'
    return
    call assert_report('should not get here')
  finally
    Xpath 'g'
    call T27_F()
    Xpath 'h'
  endtry
  call assert_report('should not get here')
endfunc

func T27_H()
  try
    Xpath 'i'
    call T27_G()
    Xpath 'j'
  finally
    Xpath 'k'
    return
    call assert_report('should not get here')
  endtry
  call assert_report('should not get here')
endfunction

func Test_finally_after_return()
  XpathINIT
  try
      Xpath 'l'
      call T27_H()
      Xpath 'm'
  finally
      Xpath 'n'
  endtry
  call assert_equal('lifgabcehjkmn', g:Xpath)
endfunc

"-------------------------------------------------------------------------------
" Test 28:  Executing :finally clauses after :finish			    {{{1
"
"	    For a :finish command dynamically enclosed in a :try/:endtry region,
"	    :finally clauses are executed and the sourced file is finished.
"
"	    This test executes the bodies of the functions F, G, and H from the
"	    previous test as script files (:return replaced by :finish).
"-------------------------------------------------------------------------------

func Test_finally_after_finish()
  XpathINIT

  let scriptF = MakeScript("T27_F")
  let scriptG = MakeScript("T27_G", scriptF)
  let scriptH = MakeScript("T27_H", scriptG)

  try
    Xpath 'A'
    exec "source" scriptH
    Xpath 'B'
  finally
    Xpath 'C'
  endtry
  Xpath 'D'
  call assert_equal('AifgabcehjkBCD', g:Xpath)
  call delete(scriptF)
  call delete(scriptG)
  call delete(scriptH)
endfunc

"-------------------------------------------------------------------------------
" Test 29:  Executing :finally clauses on errors			    {{{1
"
"	    After an error in a command dynamically enclosed in a :try/:endtry
"	    region, :finally clauses are executed and the script processing is
"	    terminated.
"-------------------------------------------------------------------------------

func Test_finally_after_error_1()
  let test =<< trim [CODE]
    func F()
      while 1
        try
          Xpath 'a'
          while 1
            try
              Xpath 'b'
              asdf	    " error
              call assert_report('should not get here')
            finally
              Xpath 'c'
            endtry | call assert_report('should not get here')
            call assert_report('should not get here')
            break
          endwhile
          call assert_report('should not get here')
        finally
          Xpath 'd'
        endtry | call assert_report('should not get here')
        call assert_report('should not get here')
        break
      endwhile
      call assert_report('should not get here')
    endfunc

    while 1
      try
        Xpath 'e'
        while 1
          call F()
          call assert_report('should not get here')
          break
        endwhile  | call assert_report('should not get here')
        call assert_report('should not get here')
      finally
        Xpath 'f'
      endtry | call assert_report('should not get here')
    endwhile | call assert_report('should not get here')
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('eabcdf', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_finally_after_error_2()
  let test =<< trim [CODE]
    func G() abort
      if 1
        try
          Xpath 'a'
          asdf	    " error
          call assert_report('should not get here')
        finally
          Xpath 'b'
        endtry | Xpath 'c'
      endif | Xpath 'd'
      call assert_report('should not get here')
    endfunc

    if 1
      try
        Xpath 'e'
        call G()
        call assert_report('should not get here')
      finally
        Xpath 'f'
      endtry | call assert_report('should not get here')
    endif | call assert_report('should not get here')
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('eabf', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 30:  Executing :finally clauses on interrupt			    {{{1
"
"	    After an interrupt in a command dynamically enclosed in
"	    a :try/:endtry region, :finally clauses are executed and the
"	    script processing is terminated.
"-------------------------------------------------------------------------------

func Test_finally_on_interrupt()
  let test =<< trim [CODE]
    func F()
      try
        Xloop 'a'
        call interrupt()
        call assert_report('should not get here')
      finally
        Xloop 'b'
      endtry
      call assert_report('should not get here')
    endfunc

    try
      try
        Xpath 'c'
        try
          Xpath 'd'
          call interrupt()
          call assert_report('should not get here')
        finally
          Xpath 'e'
          try
            Xpath 'f'
            try
              Xpath 'g'
            finally
              Xpath 'h'
              try
                Xpath 'i'
                call interrupt()
                call assert_report('should not get here')
              endtry
              call assert_report('should not get here')
            endtry
            call assert_report('should not get here')
          endtry
          call assert_report('should not get here')
        endtry
        call assert_report('should not get here')
      finally
        Xpath 'j'
        try
          Xpath 'k'
          call F()
          call assert_report('should not get here')
        finally
          Xpath 'l'
          try
            Xpath 'm'
            XloopNEXT
            ExecAsScript F
            call assert_report('should not get here')
          finally
            Xpath 'n'
          endtry
          call assert_report('should not get here')
        endtry
        call assert_report('should not get here')
      endtry
      call assert_report('should not get here')
    catch /^Vim:Interrupt$/
      Xpath 'o'
    endtry
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('cdefghijka1b1lma2b2no', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 31:  Executing :finally clauses after :throw			    {{{1
"
"	    After a :throw dynamically enclosed in a :try/:endtry region,
"	    :finally clauses are executed and the script processing is
"	    terminated.
"-------------------------------------------------------------------------------

func Test_finally_after_throw_2()
  let test =<< trim [CODE]
    func F()
      try
        Xloop 'a'
        throw "exception"
        call assert_report('should not get here')
      finally
        Xloop 'b'
      endtry
      call assert_report('should not get here')
    endfunc

    try
      Xpath 'c'
      try
        Xpath 'd'
        throw "exception"
        call assert_report('should not get here')
      finally
        Xpath 'e'
        try
          Xpath 'f'
          try
            Xpath 'g'
          finally
            Xpath 'h'
            try
              Xpath 'i'
              throw "exception"
              call assert_report('should not get here')
            endtry
            call assert_report('should not get here')
          endtry
          call assert_report('should not get here')
        endtry
        call assert_report('should not get here')
      endtry
      call assert_report('should not get here')
    finally
      Xpath 'j'
      try
        Xpath 'k'
        call F()
        call assert_report('should not get here')
      finally
        Xpath 'l'
        try
          Xpath 'm'
          XloopNEXT
          ExecAsScript F
          call assert_report('should not get here')
        finally
          Xpath 'n'
        endtry
        call assert_report('should not get here')
      endtry
      call assert_report('should not get here')
    endtry
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('cdefghijka1b1lma2b2n', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 34:  :finally reason discarded by :continue			    {{{1
"
"	    When a :finally clause is executed due to a :continue, :break,
"	    :return, :finish, error, interrupt or :throw, the jump reason is
"	    discarded by a :continue in the finally clause.
"-------------------------------------------------------------------------------

func Test_finally_after_continue()
  let test =<< trim [CODE]
    func C(jump)
      XloopNEXT
      let loop = 0
      while loop < 2
        let loop = loop + 1
        if loop == 1
          try
            if a:jump == "continue"
              continue
            elseif a:jump == "break"
              break
            elseif a:jump == "return" || a:jump == "finish"
              return
            elseif a:jump == "error"
              asdf
            elseif a:jump == "interrupt"
              call interrupt()
              let dummy = 0
            elseif a:jump == "throw"
              throw "abc"
            endif
          finally
            continue	" discards jump that caused the :finally
            call assert_report('should not get here')
          endtry
          call assert_report('should not get here')
        elseif loop == 2
          Xloop 'a'
        endif
      endwhile
    endfunc

    call C("continue")
    Xpath 'b'
    call C("break")
    Xpath 'c'
    call C("return")
    Xpath 'd'
    let g:jump = "finish"
    ExecAsScript C
    unlet g:jump
    Xpath 'e'
    try
      call C("error")
      Xpath 'f'
    finally
      Xpath 'g'
      try
        call C("interrupt")
        Xpath 'h'
      finally
        Xpath 'i'
        call C("throw")
        Xpath 'j'
      endtry
    endtry
    Xpath 'k'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('a2ba3ca4da5ea6fga7hia8jk', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 35:  :finally reason discarded by :break				    {{{1
"
"	    When a :finally clause is executed due to a :continue, :break,
"	    :return, :finish, error, interrupt or :throw, the jump reason is
"	    discarded by a :break in the finally clause.
"-------------------------------------------------------------------------------

func Test_finally_discard_by_break()
  let test =<< trim [CODE]
    func B(jump)
      XloopNEXT
      let loop = 0
      while loop < 2
        let loop = loop + 1
        if loop == 1
          try
            if a:jump == "continue"
              continue
            elseif a:jump == "break"
              break
            elseif a:jump == "return" || a:jump == "finish"
              return
            elseif a:jump == "error"
              asdf
            elseif a:jump == "interrupt"
              call interrupt()
              let dummy = 0
            elseif a:jump == "throw"
              throw "abc"
            endif
          finally
            break	" discards jump that caused the :finally
            call assert_report('should not get here')
          endtry
        elseif loop == 2
          call assert_report('should not get here')
        endif
      endwhile
      Xloop 'a'
    endfunc

    call B("continue")
    Xpath 'b'
    call B("break")
    Xpath 'c'
    call B("return")
    Xpath 'd'
    let g:jump = "finish"
    ExecAsScript B
    unlet g:jump
    Xpath 'e'
    try
      call B("error")
      Xpath 'f'
    finally
      Xpath 'g'
      try
        call B("interrupt")
        Xpath 'h'
      finally
        Xpath 'i'
        call B("throw")
        Xpath 'j'
      endtry
    endtry
    Xpath 'k'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('a2ba3ca4da5ea6fga7hia8jk', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 36:  :finally reason discarded by :return			    {{{1
"
"	    When a :finally clause is executed due to a :continue, :break,
"	    :return, :finish, error, interrupt or :throw, the jump reason is
"	    discarded by a :return in the finally clause.
"-------------------------------------------------------------------------------

func Test_finally_discard_by_return()
  let test =<< trim [CODE]
    func R(jump, retval) abort
      let loop = 0
      while loop < 2
        let loop = loop + 1
        if loop == 1
          try
            if a:jump == "continue"
              continue
            elseif a:jump == "break"
              break
            elseif a:jump == "return"
              return
            elseif a:jump == "error"
              asdf
            elseif a:jump == "interrupt"
              call interrupt()
              let dummy = 0
            elseif a:jump == "throw"
              throw "abc"
            endif
          finally
            return a:retval	" discards jump that caused the :finally
            call assert_report('should not get here')
          endtry
        elseif loop == 2
          call assert_report('should not get here')
        endif
      endwhile
      call assert_report('should not get here')
    endfunc

    let sum =  -R("continue", -8)
    Xpath 'a'
    let sum = sum - R("break", -16)
    Xpath 'b'
    let sum = sum - R("return", -32)
    Xpath 'c'
    try
      let sum = sum - R("error", -64)
      Xpath 'd'
    finally
      Xpath 'e'
      try
        let sum = sum - R("interrupt", -128)
        Xpath 'f'
      finally
        Xpath 'g'
        let sum = sum - R("throw", -256)
        Xpath 'h'
      endtry
    endtry
    Xpath 'i'

    let expected = 8 + 16 + 32 + 64 + 128 + 256
    call assert_equal(sum, expected)
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcdefghi', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 37:  :finally reason discarded by :finish			    {{{1
"
"	    When a :finally clause is executed due to a :continue, :break,
"	    :return, :finish, error, interrupt or :throw, the jump reason is
"	    discarded by a :finish in the finally clause.
"-------------------------------------------------------------------------------

func Test_finally_discard_by_finish()
  let test =<< trim [CODE]
    func F(jump)	" not executed as function, transformed to a script
      let loop = 0
      while loop < 2
        let loop = loop + 1
        if loop == 1
          try
            if a:jump == "continue"
              continue
            elseif a:jump == "break"
              break
            elseif a:jump == "finish"
              finish
            elseif a:jump == "error"
              asdf
            elseif a:jump == "interrupt"
              call interrupt()
              let dummy = 0
            elseif a:jump == "throw"
              throw "abc"
            endif
          finally
            finish	" discards jump that caused the :finally
            call assert_report('should not get here')
          endtry
        elseif loop == 2
          call assert_report('should not get here')
        endif
      endwhile
      call assert_report('should not get here')
    endfunc

    let scriptF = MakeScript("F")
    delfunction F

    let g:jump = "continue"
    exec "source" scriptF
    Xpath 'a'
    let g:jump = "break"
    exec "source" scriptF
    Xpath 'b'
    let g:jump = "finish"
    exec "source" scriptF
    Xpath 'c'
    try
      let g:jump = "error"
      exec "source" scriptF
      Xpath 'd'
    finally
      Xpath 'e'
      try
        let g:jump = "interrupt"
        exec "source" scriptF
        Xpath 'f'
      finally
        Xpath 'g'
        try
          let g:jump = "throw"
          exec "source" scriptF
          Xpath 'h'
        finally
          Xpath 'i'
        endtry
      endtry
    endtry
    unlet g:jump
    call delete(scriptF)
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcdefghi', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 38:  :finally reason discarded by an error			    {{{1
"
"	    When a :finally clause is executed due to a :continue, :break,
"	    :return, :finish, error, interrupt or :throw, the jump reason is
"	    discarded by an error in the finally clause.
"-------------------------------------------------------------------------------

func Test_finally_discard_by_error()
  let test =<< trim [CODE]
    func E(jump)
      let loop = 0
      while loop < 2
        let loop = loop + 1
        if loop == 1
          try
            if a:jump == "continue"
              continue
            elseif a:jump == "break"
              break
            elseif a:jump == "return" || a:jump == "finish"
              return
            elseif a:jump == "error"
              asdf
            elseif a:jump == "interrupt"
              call interrupt()
              let dummy = 0
            elseif a:jump == "throw"
              throw "abc"
            endif
          finally
            asdf	" error; discards jump that caused the :finally
          endtry
        elseif loop == 2
          call assert_report('should not get here')
        endif
      endwhile
      call assert_report('should not get here')
    endfunc

    try
      Xpath 'a'
      call E("continue")
      call assert_report('should not get here')
    finally
      try
        Xpath 'b'
        call E("break")
        call assert_report('should not get here')
      finally
        try
          Xpath 'c'
          call E("return")
          call assert_report('should not get here')
        finally
          try
            Xpath 'd'
            let g:jump = "finish"
            ExecAsScript E
            call assert_report('should not get here')
          finally
            unlet g:jump
            try
              Xpath 'e'
              call E("error")
              call assert_report('should not get here')
            finally
              try
                Xpath 'f'
                call E("interrupt")
                call assert_report('should not get here')
              finally
                try
                  Xpath 'g'
                  call E("throw")
                  call assert_report('should not get here')
                finally
                  Xpath 'h'
                  delfunction E
                endtry
              endtry
            endtry
          endtry
        endtry
      endtry
    endtry
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcdefgh', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 39:  :finally reason discarded by an interrupt			    {{{1
"
"	    When a :finally clause is executed due to a :continue, :break,
"	    :return, :finish, error, interrupt or :throw, the jump reason is
"	    discarded by an interrupt in the finally clause.
"-------------------------------------------------------------------------------

func Test_finally_discarded_by_interrupt()
  let test =<< trim [CODE]
    func I(jump)
      let loop = 0
      while loop < 2
        let loop = loop + 1
        if loop == 1
          try
            if a:jump == "continue"
              continue
            elseif a:jump == "break"
              break
            elseif a:jump == "return" || a:jump == "finish"
              return
            elseif a:jump == "error"
              asdf
            elseif a:jump == "interrupt"
              call interrupt()
              let dummy = 0
            elseif a:jump == "throw"
              throw "abc"
            endif
          finally
            call interrupt()
            let dummy = 0
          endtry
        elseif loop == 2
          call assert_report('should not get here')
        endif
      endwhile
      call assert_report('should not get here')
    endfunc

    try
      try
        Xpath 'a'
        call I("continue")
        call assert_report('should not get here')
      finally
        try
          Xpath 'b'
          call I("break")
          call assert_report('should not get here')
        finally
          try
            Xpath 'c'
            call I("return")
            call assert_report('should not get here')
          finally
            try
              Xpath 'd'
              let g:jump = "finish"
              ExecAsScript I
              call assert_report('should not get here')
            finally
              unlet g:jump
              try
                Xpath 'e'
                call I("error")
                call assert_report('should not get here')
              finally
                try
                  Xpath 'f'
                  call I("interrupt")
                  call assert_report('should not get here')
                finally
                  try
                    Xpath 'g'
                    call I("throw")
                    call assert_report('should not get here')
                  finally
                    Xpath 'h'
                    delfunction I
                  endtry
                endtry
              endtry
            endtry
          endtry
        endtry
      endtry
      call assert_report('should not get here')
    catch /^Vim:Interrupt$/
      Xpath 'A'
    endtry
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcdefghA', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 40:  :finally reason discarded by :throw				    {{{1
"
"	    When a :finally clause is executed due to a :continue, :break,
"	    :return, :finish, error, interrupt or :throw, the jump reason is
"	    discarded by a :throw in the finally clause.
"-------------------------------------------------------------------------------

func Test_finally_discard_by_throw()
  let test =<< trim [CODE]
    func T(jump)
      let loop = 0
      while loop < 2
        let loop = loop + 1
        if loop == 1
          try
            if a:jump == "continue"
              continue
            elseif a:jump == "break"
              break
            elseif a:jump == "return" || a:jump == "finish"
              return
            elseif a:jump == "error"
              asdf
            elseif a:jump == "interrupt"
              call interrupt()
              let dummy = 0
            elseif a:jump == "throw"
              throw "abc"
            endif
          finally
            throw "xyz"	" discards jump that caused the :finally
          endtry
        elseif loop == 2
          call assert_report('should not get here')
        endif
      endwhile
      call assert_report('should not get here')
    endfunc

    try
      Xpath 'a'
      call T("continue")
      call assert_report('should not get here')
    finally
      try
        Xpath 'b'
        call T("break")
        call assert_report('should not get here')
      finally
        try
          Xpath 'c'
          call T("return")
          call assert_report('should not get here')
        finally
          try
            Xpath 'd'
            let g:jump = "finish"
            ExecAsScript T
            call assert_report('should not get here')
          finally
            unlet g:jump
            try
              Xpath 'e'
              call T("error")
              call assert_report('should not get here')
            finally
              try
                Xpath 'f'
                call T("interrupt")
                call assert_report('should not get here')
              finally
                try
                  Xpath 'g'
                  call T("throw")
                  call assert_report('should not get here')
                finally
                  Xpath 'h'
                  delfunction T
                endtry
              endtry
            endtry
          endtry
        endtry
      endtry
    endtry
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcdefgh', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 49:  Throwing exceptions across functions			    {{{1
"
"	    When an exception is thrown but not caught inside a function, the
"	    caller is checked for a matching :catch clause.
"-------------------------------------------------------------------------------

func T49_C()
  try
    Xpath 'a'
    throw "arrgh"
    call assert_report('should not get here')
  catch /arrgh/
    Xpath 'b'
  endtry
  Xpath 'c'
endfunc

func T49_T1()
  XloopNEXT
  try
    Xloop 'd'
    throw "arrgh"
    call assert_report('should not get here')
  finally
    Xloop 'e'
  endtry
  Xloop 'f'
endfunc

func T49_T2()
  try
    Xpath 'g'
    call T49_T1()
    call assert_report('should not get here')
  finally
    Xpath 'h'
  endtry
  call assert_report('should not get here')
endfunc

func Test_throw_exception_across_funcs()
  XpathINIT
  XloopINIT
  try
    Xpath 'i'
    call T49_C()            " throw and catch
    Xpath 'j'
  catch /.*/
    call assert_report('should not get here')
  endtry

  try
    Xpath 'k'
    call T49_T1()  " throw, one level
    call assert_report('should not get here')
  catch /arrgh/
    Xpath 'l'
  catch /.*/
    call assert_report('should not get here')
  endtry

  try
    Xpath 'm'
    call T49_T2()	" throw, two levels
    call assert_report('should not get here')
  catch /arrgh/
    Xpath 'n'
  catch /.*/
    call assert_report('should not get here')
  endtry
  Xpath 'o'

  call assert_equal('iabcjkd2e2lmgd3e3hno', g:Xpath)
endfunc

"-------------------------------------------------------------------------------
" Test 50:  Throwing exceptions across script files			    {{{1
"
"	    When an exception is thrown but not caught inside a script file,
"	    the sourcing script or function is checked for a matching :catch
"	    clause.
"
"	    This test executes the bodies of the functions C, T1, and T2 from
"	    the previous test as script files (:return replaced by :finish).
"-------------------------------------------------------------------------------

func T50_F()
  try
    Xpath 'A'
    exec "source" g:scriptC
    Xpath 'B'
  catch /.*/
    call assert_report('should not get here')
  endtry

  try
    Xpath 'C'
    exec "source" g:scriptT1
    call assert_report('should not get here')
  catch /arrgh/
    Xpath 'D'
  catch /.*/
    call assert_report('should not get here')
  endtry
endfunc

func Test_throw_across_script()
  XpathINIT
  XloopINIT
  let g:scriptC = MakeScript("T49_C")
  let g:scriptT1 = MakeScript("T49_T1")
  let scriptT2 = MakeScript("T49_T2", g:scriptT1)

  try
    Xpath 'E'
    call T50_F()
    Xpath 'F'
    exec "source" scriptT2
    call assert_report('should not get here')
  catch /arrgh/
    Xpath 'G'
  catch /.*/
    call assert_report('should not get here')
  endtry
  Xpath 'H'
  call assert_equal('EAabcBCd2e2DFgd3e3hGH', g:Xpath)

  call delete(g:scriptC)
  call delete(g:scriptT1)
  call delete(scriptT2)
  unlet g:scriptC g:scriptT1 scriptT2
endfunc

"-------------------------------------------------------------------------------
" Test 52:  Uncaught exceptions						    {{{1
"
"	    When an exception is thrown but not caught, an error message is
"	    displayed when the script is terminated.  In case of an interrupt
"	    or error exception, the normal interrupt or error message(s) are
"	    displayed.
"-------------------------------------------------------------------------------

func Test_uncaught_exception_1()
  CheckEnglish

  let test =<< trim [CODE]
    Xpath 'a'
    throw "arrgh"
    call assert_report('should not get here')`
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('E605: Exception not caught: arrgh', v:errmsg)
    call assert_equal('a', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_uncaught_exception_2()
  CheckEnglish

  let test =<< trim [CODE]
    try
      Xpath 'a'
      throw "oops"
      call assert_report('should not get here')`
    catch /arrgh/
      call assert_report('should not get here')`
    endtry
    call assert_report('should not get here')`
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('E605: Exception not caught: oops', v:errmsg)
    call assert_equal('a', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_uncaught_exception_3()
  CheckEnglish

  let test =<< trim [CODE]
    func T()
      Xpath 'c'
      throw "brrr"
      call assert_report('should not get here')`
    endfunc

    try
      Xpath 'a'
      throw "arrgh"
      call assert_report('should not get here')`
    catch /.*/
      Xpath 'b'
      call T()
      call assert_report('should not get here')`
    endtry
    call assert_report('should not get here')`
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('E605: Exception not caught: brrr', v:errmsg)
    call assert_equal('abc', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_uncaught_exception_4()
  CheckEnglish

  let test =<< trim [CODE]
    try
      Xpath 'a'
      throw "arrgh"
      call assert_report('should not get here')`
    finally
      Xpath 'b'
      throw "brrr"
      call assert_report('should not get here')`
    endtry
    call assert_report('should not get here')`
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('E605: Exception not caught: brrr', v:errmsg)
    call assert_equal('ab', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_uncaught_exception_5()
  CheckEnglish

  " Need to catch and handle interrupt, otherwise the test will wait for the
  " user to press <Enter> to continue
  let test =<< trim [CODE]
    try
      try
        Xpath 'a'
        call interrupt()
        call assert_report('should not get here')
      endtry
      call assert_report('should not get here')
    catch /^Vim:Interrupt$/
      Xpath 'b'
    endtry
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('ab', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_uncaught_exception_6()
  CheckEnglish

  let test =<< trim [CODE]
    try
      Xpath 'a'
      let x = novar	" error E121; exception: E121
    catch /E15:/	" should not catch
      call assert_report('should not get here')
    endtry
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('a', g:Xpath)
    call assert_equal('E121: Undefined variable: novar', v:errmsg)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_uncaught_exception_7()
  CheckEnglish

  let test =<< trim [CODE]
    try
      Xpath 'a'
      " error E108/E488; exception: E488
      unlet novar #
    catch /E108:/       " should not catch
      call assert_report('should not get here')
    endtry
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('a', g:Xpath)
    call assert_equal('E488: Trailing characters: #', v:errmsg)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 53:  Nesting errors: :endif/:else/:elseif			    {{{1
"
"	    For nesting errors of :if conditionals the correct error messages
"	    should be given.
"-------------------------------------------------------------------------------

func Test_nested_if_else_errors()
  CheckEnglish

  " :endif without :if
  let code =<< trim END
    endif
  END
  call writefile(code, 'Xtest', 'D')
  call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if')

  " :endif without :if
  let code =<< trim END
    while 1
      endif
    endwhile
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if')

  " :endif without :if
  let code =<< trim END
    try
    finally
      endif
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if')

  " :endif without :if
  let code =<< trim END
    try
      endif
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if')

  " :endif without :if
  let code =<< trim END
    try
      throw "a"
    catch /a/
      endif
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if')

  " :else without :if
  let code =<< trim END
    else
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if')

  " :else without :if
  let code =<< trim END
    while 1
      else
    endwhile
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if')

  " :else without :if
  let code =<< trim END
    try
    finally
      else
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if')

  " :else without :if
  let code =<< trim END
    try
      else
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if')

  " :else without :if
  let code =<< trim END
    try
      throw "a"
    catch /a/
      else
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if')

  " :elseif without :if
  let code =<< trim END
    elseif 1
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if')

  " :elseif without :if
  let code =<< trim END
    while 1
      elseif 1
    endwhile
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if')

  " :elseif without :if
  let code =<< trim END
    try
    finally
      elseif 1
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if')

  " :elseif without :if
  let code =<< trim END
    try
      elseif 1
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if')

  " :elseif without :if
  let code =<< trim END
    try
      throw "a"
    catch /a/
      elseif 1
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if')

  " multiple :else
  let code =<< trim END
    if 1
    else
    else
    endif
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(else):E583: Multiple :else')

  " :elseif after :else
  let code =<< trim END
    if 1
    else
    elseif 1
    endif
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(elseif):E584: :elseif after :else')
endfunc

"-------------------------------------------------------------------------------
" Test 54:  Nesting errors: :while/:endwhile				    {{{1
"
"	    For nesting errors of :while conditionals the correct error messages
"	    should be given.
"
"	    This test reuses the function MESSAGES() from the previous test.
"	    This function checks the messages in g:msgfile.
"-------------------------------------------------------------------------------

func Test_nested_while_error()
  CheckEnglish

  " :endwhile without :while
  let code =<< trim END
    endwhile
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')

  " :endwhile without :while
  let code =<< trim END
    if 1
      endwhile
    endif
  END
  call writefile(code, 'Xtest', 'D')
  call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')

  " Missing :endif
  let code =<< trim END
    while 1
      if 1
    endwhile
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endwhile):E171: Missing :endif')

  " :endwhile without :while
  let code =<< trim END
    try
    finally
      endwhile
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')

  " Missing :endtry
  let code =<< trim END
    while 1
      try
      finally
    endwhile
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endwhile):E600: Missing :endtry')

  " Missing :endtry
  let code =<< trim END
    while 1
      if 1
        try
        finally
    endwhile
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endwhile):E600: Missing :endtry')

  " Missing :endif
  let code =<< trim END
    while 1
      try
      finally
        if 1
    endwhile
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endwhile):E171: Missing :endif')

  " :endwhile without :while
  let code =<< trim END
    try
      endwhile
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')

  " :endwhile without :while
  let code =<< trim END
    while 1
      try
        endwhile
      endtry
    endwhile
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')

  " :endwhile without :while
  let code =<< trim END
    try
      throw "a"
    catch /a/
      endwhile
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')

  " :endwhile without :while
  let code =<< trim END
    while 1
      try
        throw "a"
      catch /a/
        endwhile
      endtry
    endwhile
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')
endfunc

"-------------------------------------------------------------------------------
" Test 55:  Nesting errors: :continue/:break				    {{{1
"
"	    For nesting errors of :continue and :break commands the correct
"	    error messages should be given.
"
"	    This test reuses the function MESSAGES() from the previous test.
"	    This function checks the messages in g:msgfile.
"-------------------------------------------------------------------------------

func Test_nested_cont_break_error()
  CheckEnglish

  " :continue without :while
  let code =<< trim END
    continue
  END
  call writefile(code, 'Xtest', 'D')
  call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for')

  " :continue without :while
  let code =<< trim END
    if 1
      continue
    endif
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for')

  " :continue without :while
  let code =<< trim END
    try
    finally
      continue
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for')

  " :continue without :while
  let code =<< trim END
    try
      continue
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for')

  " :continue without :while
  let code =<< trim END
    try
      throw "a"
    catch /a/
      continue
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for')

  " :break without :while
  let code =<< trim END
    break
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for')

  " :break without :while
  let code =<< trim END
    if 1
      break
    endif
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for')

  " :break without :while
  let code =<< trim END
    try
    finally
      break
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for')

  " :break without :while
  let code =<< trim END
    try
      break
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for')

  " :break without :while
  let code =<< trim END
    try
      throw "a"
    catch /a/
      break
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for')
endfunc

"-------------------------------------------------------------------------------
" Test 56:  Nesting errors: :endtry					    {{{1
"
"	    For nesting errors of :try conditionals the correct error messages
"	    should be given.
"
"	    This test reuses the function MESSAGES() from the previous test.
"	    This function check the messages in g:msgfile.
"-------------------------------------------------------------------------------

func Test_nested_endtry_error()
  CheckEnglish

  " :endtry without :try
  let code =<< trim END
    endtry
  END
  call writefile(code, 'Xtest', 'D')
  call AssertException(['source Xtest'], 'Vim(endtry):E602: :endtry without :try')

  " :endtry without :try
  let code =<< trim END
    if 1
      endtry
    endif
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endtry):E602: :endtry without :try')

  " :endtry without :try
  let code =<< trim END
    while 1
      endtry
    endwhile
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endtry):E602: :endtry without :try')

  " Missing :endif
  let code =<< trim END
    try
        if 1
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endtry):E171: Missing :endif')

  " Missing :endwhile
  let code =<< trim END
    try
      while 1
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endtry):E170: Missing :endwhile')

  " Missing :endif
  let code =<< trim END
    try
    finally
      if 1
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endtry):E171: Missing :endif')

  " Missing :endwhile
  let code =<< trim END
    try
    finally
      while 1
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endtry):E170: Missing :endwhile')

  " Missing :endif
  let code =<< trim END
    try
      throw "a"
    catch /a/
      if 1
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endtry):E171: Missing :endif')

  " Missing :endwhile
  let code =<< trim END
    try
      throw "a"
    catch /a/
      while 1
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endtry):E170: Missing :endwhile')
endfunc

"-------------------------------------------------------------------------------
" Test 57:  v:exception and v:throwpoint for user exceptions		    {{{1
"
"	    v:exception evaluates to the value of the exception that was caught
"	    most recently and is not finished.  (A caught exception is finished
"	    when the next ":catch", ":finally", or ":endtry" is reached.)
"	    v:throwpoint evaluates to the script/function name and line number
"	    where that exception has been thrown.
"-------------------------------------------------------------------------------

func Test_user_exception_info()
  CheckEnglish

  XpathINIT
  XloopINIT

  func FuncException()
    let g:exception = v:exception
  endfunc

  func FuncThrowpoint()
    let g:throwpoint = v:throwpoint
  endfunc

  let scriptException  = MakeScript("FuncException")
  let scriptThrowPoint = MakeScript("FuncThrowpoint")

  command! CmdException  let g:exception  = v:exception
  command! CmdThrowpoint let g:throwpoint = v:throwpoint

  func T(arg, line)
    if a:line == 2
      throw a:arg		" in line 2
    elseif a:line == 4
      throw a:arg		" in line 4
    elseif a:line == 6
      throw a:arg		" in line 6
    elseif a:line == 8
      throw a:arg		" in line 8
    endif
  endfunc

  func G(arg, line)
    call T(a:arg, a:line)
  endfunc

  func F(arg, line)
    call G(a:arg, a:line)
  endfunc

  let scriptT = MakeScript("T")
  let scriptG = MakeScript("G", scriptT)
  let scriptF = MakeScript("F", scriptG)

  try
    Xpath 'a'
    call F("oops", 2)
  catch /.*/
    Xpath 'b'
    let exception  = v:exception
    let throwpoint = v:throwpoint
    call assert_equal("oops", v:exception)
    call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint)
    call assert_match('\<2\>', v:throwpoint)

    exec "let exception  = v:exception"
    exec "let throwpoint = v:throwpoint"
    call assert_equal("oops", v:exception)
    call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint)
    call assert_match('\<2\>', v:throwpoint)

    CmdException
    CmdThrowpoint
    call assert_equal("oops", v:exception)
    call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint)
    call assert_match('\<2\>', v:throwpoint)

    call FuncException()
    call FuncThrowpoint()
    call assert_equal("oops", v:exception)
    call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint)
    call assert_match('\<2\>', v:throwpoint)

    exec "source" scriptException
    exec "source" scriptThrowPoint
    call assert_equal("oops", v:exception)
    call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint)
    call assert_match('\<2\>', v:throwpoint)

    try
      Xpath 'c'
      call G("arrgh", 4)
    catch /.*/
      Xpath 'd'
      let exception  = v:exception
      let throwpoint = v:throwpoint
      call assert_equal("arrgh", v:exception)
      call assert_match('\<G\[1]\.\.T\>', v:throwpoint)
      call assert_match('\<4\>', v:throwpoint)

      try
        Xpath 'e'
        let g:arg = "autsch"
        let g:line = 6
        exec "source" scriptF
      catch /.*/
        Xpath 'f'
        let exception  = v:exception
        let throwpoint = v:throwpoint
        call assert_equal("autsch", v:exception)
        call assert_match(fnamemodify(scriptT, ':t'), v:throwpoint)
        call assert_match('\<6\>', v:throwpoint)
      finally
        Xpath 'g'
        let exception  = v:exception
        let throwpoint = v:throwpoint
        call assert_equal("arrgh", v:exception)
        call assert_match('\<G\[1]\.\.T\>', v:throwpoint)
        call assert_match('\<4\>', v:throwpoint)
        try
          Xpath 'h'
          let g:arg = "brrrr"
          let g:line = 8
          exec "source" scriptG
        catch /.*/
          Xpath 'i'
          let exception  = v:exception
          let throwpoint = v:throwpoint
          " Resolve scriptT for matching it against v:throwpoint.
          call assert_equal("brrrr", v:exception)
          call assert_match(fnamemodify(scriptT, ':t'), v:throwpoint)
          call assert_match('\<8\>', v:throwpoint)
        finally
          Xpath 'j'
          let exception  = v:exception
          let throwpoint = v:throwpoint
          call assert_equal("arrgh", v:exception)
          call assert_match('\<G\[1]\.\.T\>', v:throwpoint)
          call assert_match('\<4\>', v:throwpoint)
        endtry
        Xpath 'k'
        let exception  = v:exception
        let throwpoint = v:throwpoint
        call assert_equal("arrgh", v:exception)
        call assert_match('\<G\[1]\.\.T\>', v:throwpoint)
        call assert_match('\<4\>', v:throwpoint)
      endtry
      Xpath 'l'
      let exception  = v:exception
      let throwpoint = v:throwpoint
      call assert_equal("arrgh", v:exception)
      call assert_match('\<G\[1]\.\.T\>', v:throwpoint)
      call assert_match('\<4\>', v:throwpoint)
    finally
      Xpath 'm'
      let exception  = v:exception
      let throwpoint = v:throwpoint
      call assert_equal("oops", v:exception)
      call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint)
      call assert_match('\<2\>', v:throwpoint)
    endtry
    Xpath 'n'
    let exception  = v:exception
    let throwpoint = v:throwpoint
    call assert_equal("oops", v:exception)
    call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint)
    call assert_match('\<2\>', v:throwpoint)
  finally
    Xpath 'o'
    let exception  = v:exception
    let throwpoint = v:throwpoint
    call assert_equal("", v:exception)
    call assert_match('^$', v:throwpoint)
    call assert_match('^$', v:throwpoint)
  endtry

  call assert_equal('abcdefghijklmno', g:Xpath)

  unlet exception throwpoint
  delfunction FuncException
  delfunction FuncThrowpoint
  call delete(scriptException)
  call delete(scriptThrowPoint)
  unlet scriptException scriptThrowPoint
  delcommand CmdException
  delcommand CmdThrowpoint
  delfunction T
  delfunction G
  delfunction F
  call delete(scriptT)
  call delete(scriptG)
  call delete(scriptF)
  unlet scriptT scriptG scriptF
endfunc

"-------------------------------------------------------------------------------
"
" Test 58:  v:exception and v:throwpoint for error/interrupt exceptions	    {{{1
"
"	    v:exception and v:throwpoint work also for error and interrupt
"	    exceptions.
"-------------------------------------------------------------------------------

func Test_exception_info_for_error()
  CheckEnglish

  let test =<< trim [CODE]
    func T(line)
      if a:line == 2
        delfunction T		" error (function in use) in line 2
      elseif a:line == 4
        call interrupt()
      endif
    endfunc

    while 1
      try
        Xpath 'a'
        call T(2)
        call assert_report('should not get here')
      catch /.*/
        Xpath 'b'
        if v:exception !~ 'Vim(delfunction):'
          call assert_report('should not get here')
        endif
        if v:throwpoint !~ '\<T\>'
          call assert_report('should not get here')
        endif
        if v:throwpoint !~ '\<2\>'
          call assert_report('should not get here')
        endif
      finally
        Xpath 'c'
        if v:exception != ""
          call assert_report('should not get here')
        endif
        if v:throwpoint != ""
          call assert_report('should not get here')
        endif
        break
      endtry
    endwhile

    Xpath 'd'
    if v:exception != ""
      call assert_report('should not get here')
    endif
    if v:throwpoint != ""
      call assert_report('should not get here')
    endif

    while 1
      try
        Xpath 'e'
        call T(4)
        call assert_report('should not get here')
      catch /.*/
        Xpath 'f'
        if v:exception != 'Vim:Interrupt'
          call assert_report('should not get here')
        endif
        if v:throwpoint !~ 'function T'
          call assert_report('should not get here')
        endif
        if v:throwpoint !~ '\<4\>'
          call assert_report('should not get here')
        endif
      finally
        Xpath 'g'
        if v:exception != ""
          call assert_report('should not get here')
        endif
        if v:throwpoint != ""
          call assert_report('should not get here')
        endif
        break
      endtry
    endwhile

    Xpath 'h'
    if v:exception != ""
      call assert_report('should not get here')
    endif
    if v:throwpoint != ""
      call assert_report('should not get here')
    endif
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcdefgh', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
"
" Test 59:  v:exception and v:throwpoint when discarding exceptions	    {{{1
"
"	    When a :catch clause is left by a ":break" etc or an error or
"	    interrupt exception, v:exception and v:throwpoint are reset.  They
"	    are not affected by an exception that is discarded before being
"	    caught.
"-------------------------------------------------------------------------------
func Test_exception_info_on_discard()
  CheckEnglish

  let test =<< trim [CODE]
    let sfile = expand("<sfile>")

    while 1
      try
        throw "x1"
      catch /.*/
        break
      endtry
    endwhile
    call assert_equal('', v:exception)
    call assert_equal('', v:throwpoint)

    while 1
      try
        throw "x2"
      catch /.*/
        break
      finally
        call assert_equal('', v:exception)
        call assert_equal('', v:throwpoint)
      endtry
      break
    endwhile
    call assert_equal('', v:exception)
    call assert_equal('', v:throwpoint)

    while 1
      try
        let errcaught = 0
        try
          try
            throw "x3"
          catch /.*/
            let lnum = expand("<sflnum>")
            asdf
          endtry
        catch /.*/
          let errcaught = 1
          call assert_match('Vim:E492: Not an editor command:', v:exception)
          call assert_match('line ' .. (lnum + 1), v:throwpoint)
        endtry
      finally
        call assert_equal(1, errcaught)
        break
      endtry
    endwhile
    call assert_equal('', v:exception)
    call assert_equal('', v:throwpoint)

    Xpath 'a'

    while 1
      try
        let intcaught = 0
        try
          try
            throw "x4"
          catch /.*/
            let lnum = expand("<sflnum>")
            call interrupt()
          endtry
        catch /.*/
          let intcaught = 1
          call assert_match('Vim:Interrupt', v:exception)
          call assert_match('line ' .. (lnum + 1), v:throwpoint)
        endtry
      finally
        call assert_equal(1, intcaught)
        break
      endtry
    endwhile
    call assert_equal('', v:exception)
    call assert_equal('', v:throwpoint)

    Xpath 'b'

    while 1
      try
        let errcaught = 0
        try
          try
            if 1
              let lnum = expand("<sflnum>")
              throw "x5"
            " missing endif
          catch /.*/
            call assert_report('should not get here')
          endtry
        catch /.*/
          let errcaught = 1
          call assert_match('Vim(catch):E171: Missing :endif:', v:exception)
          call assert_match('line ' .. (lnum + 3), v:throwpoint)
        endtry
      finally
        call assert_equal(1, errcaught)
        break
      endtry
    endwhile
    call assert_equal('', v:exception)
    call assert_equal('', v:throwpoint)

    Xpath 'c'

    try
      while 1
        try
          throw "x6"
        finally
          break
        endtry
        break
      endwhile
    catch /.*/
      call assert_report('should not get here')
    endtry
    call assert_equal('', v:exception)
    call assert_equal('', v:throwpoint)

    try
      while 1
        try
          throw "x7"
        finally
          break
        endtry
        break
      endwhile
    catch /.*/
      call assert_report('should not get here')
    finally
      call assert_equal('', v:exception)
      call assert_equal('', v:throwpoint)
    endtry
    call assert_equal('', v:exception)
    call assert_equal('', v:throwpoint)

    while 1
      try
        let errcaught = 0
        try
          try
            throw "x8"
          finally
            let lnum = expand("<sflnum>")
            asdf
          endtry
        catch /.*/
          let errcaught = 1
          call assert_match('Vim:E492: Not an editor command:', v:exception)
          call assert_match('line ' .. (lnum + 1), v:throwpoint)
        endtry
      finally
        call assert_equal(1, errcaught)
        break
      endtry
    endwhile
    call assert_equal('', v:exception)
    call assert_equal('', v:throwpoint)

    Xpath 'd'

    while 1
      try
        let intcaught = 0
        try
          try
            throw "x9"
          finally
            let lnum = expand("<sflnum>")
            call interrupt()
          endtry
        catch /.*/
          let intcaught = 1
          call assert_match('Vim:Interrupt', v:exception)
          call assert_match('line ' .. (lnum + 1), v:throwpoint)
        endtry
      finally
        call assert_equal(1, intcaught)
        break
      endtry
    endwhile
    call assert_equal('', v:exception)
    call assert_equal('', v:throwpoint)

    Xpath 'e'

    while 1
      try
        let errcaught = 0
        try
          try
            if 1
              let lnum = expand("<sflnum>")
              throw "x10"
            " missing endif
          finally
            call assert_equal('', v:exception)
            call assert_equal('', v:throwpoint)
          endtry
        catch /.*/
          let errcaught = 1
          call assert_match('Vim(finally):E171: Missing :endif:', v:exception)
          call assert_match('line ' .. (lnum + 3), v:throwpoint)
        endtry
      finally
        call assert_equal(1, errcaught)
        break
      endtry
    endwhile
    call assert_equal('', v:exception)
    call assert_equal('', v:throwpoint)

    Xpath 'f'

    while 1
      try
        let errcaught = 0
        try
          try
            if 1
              let lnum = expand("<sflnum>")
              throw "x11"
            " missing endif
          endtry
        catch /.*/
          let errcaught = 1
          call assert_match('Vim(endtry):E171: Missing :endif:', v:exception)
          call assert_match('line ' .. (lnum + 3), v:throwpoint)
        endtry
      finally
        call assert_equal(1, errcaught)
        break
      endtry
    endwhile
    call assert_equal('', v:exception)
    call assert_equal('', v:throwpoint)

    Xpath 'g'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcdefg', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
"
" Test 60:  (Re)throwing v:exception; :echoerr.				    {{{1
"
"	    A user exception can be rethrown after catching by throwing
"	    v:exception.  An error or interrupt exception cannot be rethrown
"	    because Vim exceptions cannot be faked.  A Vim exception using the
"	    value of v:exception can, however, be triggered by the :echoerr
"	    command.
"-------------------------------------------------------------------------------

func Test_rethrow_exception_1()
  XpathINIT
  try
    try
      Xpath 'a'
      throw "oops"
    catch /oops/
      Xpath 'b'
      throw v:exception	" rethrow user exception
    catch /.*/
      call assert_report('should not get here')
    endtry
  catch /^oops$/			" catches rethrown user exception
    Xpath 'c'
  catch /.*/
    call assert_report('should not get here')
  endtry
  call assert_equal('abc', g:Xpath)
endfunc

func Test_rethrow_exception_2()
  XpathINIT
  try
    let caught = 0
    try
      Xpath 'a'
      write /n/o/n/w/r/i/t/a/b/l/e/_/f/i/l/e
      call assert_report('should not get here')
    catch /^Vim(write):/
      let caught = 1
      throw v:exception	" throw error: cannot fake Vim exception
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'b'
      call assert_equal(1, caught)
    endtry
  catch /^Vim(throw):/	" catches throw error
    let caught = caught + 1
  catch /.*/
    call assert_report('should not get here')
  finally
    Xpath 'c'
    call assert_equal(2, caught)
  endtry
  call assert_equal('abc', g:Xpath)
endfunc

func Test_rethrow_exception_3()
  XpathINIT
  try
    let caught = 0
    try
      Xpath 'a'
      asdf
    catch /^Vim/		" catch error exception
      let caught = 1
      " Trigger Vim error exception with value specified after :echoerr
      let value = substitute(v:exception, '^Vim\((.*)\)\=:', '', "")
      echoerr value
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'b'
      call assert_equal(1, caught)
    endtry
  catch /^Vim(echoerr):/
    let caught = caught + 1
    call assert_match(value, v:exception)
  catch /.*/
    call assert_report('should not get here')
  finally
    Xpath 'c'
    call assert_equal(2, caught)
  endtry
  call assert_equal('abc', g:Xpath)
endfunc

func Test_rethrow_exception_3()
  XpathINIT
  try
    let errcaught = 0
    try
      Xpath 'a'
      let intcaught = 0
      call interrupt()
    catch /^Vim:/		" catch interrupt exception
      let intcaught = 1
      " Trigger Vim error exception with value specified after :echoerr
      echoerr substitute(v:exception, '^Vim\((.*)\)\=:', '', "")
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'b'
      call assert_equal(1, intcaught)
    endtry
  catch /^Vim(echoerr):/
    let errcaught = 1
    call assert_match('Interrupt', v:exception)
  finally
    Xpath 'c'
    call assert_equal(1, errcaught)
  endtry
  call assert_equal('abc', g:Xpath)
endfunc

"-------------------------------------------------------------------------------
" Test 61:  Catching interrupt exceptions				    {{{1
"
"	    When an interrupt occurs inside a :try/:endtry region, an
"	    interrupt exception is thrown and can be caught.  Its value is
"	    "Vim:Interrupt".  If the interrupt occurs after an error or a :throw
"	    but before a matching :catch is reached, all following :catches of
"	    that try block are ignored, but the interrupt exception can be
"	    caught by the next surrounding try conditional.  An interrupt is
"	    ignored when there is a previous interrupt that has not been caught
"	    or causes a :finally clause to be executed.
"-------------------------------------------------------------------------------

func Test_catch_intr_exception()
  let test =<< trim [CODE]
    while 1
      try
        try
          Xpath 'a'
          call interrupt()
          call assert_report('should not get here')
        catch /^Vim:Interrupt$/
          Xpath 'b'
        finally
          Xpath 'c'
        endtry
      catch /.*/
        call assert_report('should not get here')
      finally
        Xpath 'd'
        break
      endtry
    endwhile

    while 1
      try
        try
          try
            Xpath 'e'
            asdf
            call assert_report('should not get here')
          catch /do_not_catch/
            call assert_report('should not get here')
          catch /.*/
            Xpath 'f'
            call interrupt()
            call assert_report('should not get here')
          catch /.*/
            call assert_report('should not get here')
          finally
            Xpath 'g'
            call interrupt()
            call assert_report('should not get here')
          endtry
        catch /^Vim:Interrupt$/
          Xpath 'h'
        finally
          Xpath 'i'
        endtry
      catch /.*/
        call assert_report('should not get here')
      finally
        Xpath 'j'
        break
      endtry
    endwhile

    while 1
      try
        try
          try
            Xpath 'k'
            throw "x"
            call assert_report('should not get here')
          catch /do_not_catch/
            call assert_report('should not get here')
          catch /x/
            Xpath 'l'
            call interrupt()
            call assert_report('should not get here')
          catch /.*/
            call assert_report('should not get here')
          endtry
        catch /^Vim:Interrupt$/
          Xpath 'm'
        finally
          Xpath 'n'
        endtry
      catch /.*/
        call assert_report('should not get here')
      finally
        Xpath 'o'
        break
      endtry
    endwhile

    while 1
      try
        try
          Xpath 'p'
          call interrupt()
          call assert_report('should not get here')
        catch /do_not_catch/
          call interrupt()
          call assert_report('should not get here')
        catch /^Vim:Interrupt$/
          Xpath 'q'
        finally
          Xpath 'r'
        endtry
      catch /.*/
        call assert_report('should not get here')
      finally
        Xpath 's'
        break
      endtry
    endwhile

    Xpath 't'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcdefghijklmnopqrst', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 62:  Catching error exceptions					    {{{1
"
"	    An error inside a :try/:endtry region is converted to an exception
"	    and can be caught.  The error exception has a "Vim(cmdname):" prefix
"	    where cmdname is the name of the failing command, or a "Vim:" prefix
"	    if no command name is known.  The "Vim" prefixes cannot be faked.
"-------------------------------------------------------------------------------

func Test_catch_err_exception_1()
  XpathINIT
  while 1
    try
      try
        let caught = 0
        unlet novar
      catch /^Vim(unlet):/
        Xpath 'a'
        let caught = 1
        let v:errmsg = substitute(v:exception, '^Vim(unlet):', '', "")
      finally
        Xpath 'b'
        call assert_equal(1, caught)
        call assert_match('E108: No such variable: "novar"', v:errmsg)
      endtry
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'c'
      break
    endtry
    call assert_report('should not get here')
  endwhile
  call assert_equal('abc', g:Xpath)
endfunc

func Test_catch_err_exception_2()
  XpathINIT
  while 1
    try
      try
        let caught = 0
        throw novar			" error in :throw
      catch /^Vim(throw):/
        Xpath 'a'
        let caught = 1
        let v:errmsg = substitute(v:exception, '^Vim(throw):', '', "")
      finally
        Xpath 'b'
        call assert_equal(1, caught)
        call assert_match('E121: Undefined variable: novar', v:errmsg)
      endtry
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'c'
      break
    endtry
    call assert_report('should not get here')
  endwhile
  call assert_equal('abc', g:Xpath)
endfunc

func Test_catch_err_exception_3()
  XpathINIT
  while 1
    try
      try
        let caught = 0
        throw "Vim:faked"		" error: cannot fake Vim exception
      catch /^Vim(throw):/
        Xpath 'a'
        let caught = 1
        let v:errmsg = substitute(v:exception, '^Vim(throw):', '', "")
      finally
        Xpath 'b'
        call assert_equal(1, caught)
        call assert_match("E608: Cannot :throw exceptions with 'Vim' prefix",
              \ v:errmsg)
      endtry
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'c'
      break
    endtry
    call assert_report('should not get here')
  endwhile
  call assert_equal('abc', g:Xpath)
endfunc

func Test_catch_err_exception_4()
  XpathINIT
  func F()
    while 1
    " Missing :endwhile
  endfunc

  while 1
    try
      try
        let caught = 0
        call F()
      catch /^Vim(endfunction):/
        Xpath 'a'
        let caught = 1
        let v:errmsg = substitute(v:exception, '^Vim(endfunction):', '', "")
      finally
        Xpath 'b'
        call assert_equal(1, caught)
        call assert_match("E170: Missing :endwhile", v:errmsg)
      endtry
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'c'
      break
    endtry
    call assert_report('should not get here')
  endwhile
  call assert_equal('abc', g:Xpath)
  delfunc F
endfunc

func Test_catch_err_exception_5()
  XpathINIT
  func F()
    while 1
    " Missing :endwhile
  endfunc

  while 1
    try
      try
        let caught = 0
        ExecAsScript F
      catch /^Vim:/
        Xpath 'a'
        let caught = 1
        let v:errmsg = substitute(v:exception, '^Vim:', '', "")
      finally
        Xpath 'b'
        call assert_equal(1, caught)
        call assert_match("E170: Missing :endwhile", v:errmsg)
      endtry
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'c'
      break
    endtry
    call assert_report('should not get here')
  endwhile
  call assert_equal('abc', g:Xpath)
  delfunc F
endfunc

func Test_catch_err_exception_6()
  XpathINIT
  func G()
    call G()
  endfunc

  while 1
    try
      let mfd_save = &mfd
      set mfd=3
      try
        let caught = 0
        call G()
      catch /^Vim(call):/
        Xpath 'a'
        let caught = 1
        let v:errmsg = substitute(v:exception, '^Vim(call):', '', "")
      finally
        Xpath 'b'
        call assert_equal(1, caught)
        call assert_match("E132: Function call depth is higher than 'maxfuncdepth'", v:errmsg)
      endtry
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'c'
      let &mfd = mfd_save
      break
    endtry
    call assert_report('should not get here')
  endwhile
  call assert_equal('abc', g:Xpath)
  delfunc G
endfunc

func Test_catch_err_exception_7()
  XpathINIT
  func H()
    return H()
  endfunc

  while 1
    try
      let mfd_save = &mfd
      set mfd=3
      try
        let caught = 0
        call H()
      catch /^Vim(return):/
        Xpath 'a'
        let caught = 1
        let v:errmsg = substitute(v:exception, '^Vim(return):', '', "")
      finally
        Xpath 'b'
        call assert_equal(1, caught)
        call assert_match("E132: Function call depth is higher than 'maxfuncdepth'", v:errmsg)
      endtry
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'c'
      let &mfd = mfd_save
      break		" discard error for $VIMNOERRTHROW
    endtry
    call assert_report('should not get here')
  endwhile

  call assert_equal('abc', g:Xpath)
  delfunc H
endfunc

"-------------------------------------------------------------------------------
" Test 63:  Suppressing error exceptions by :silent!.			    {{{1
"
"	    A :silent! command inside a :try/:endtry region suppresses the
"	    conversion of errors to an exception and the immediate abortion on
"	    error.  When the commands executed by the :silent! themselves open
"	    a new :try/:endtry region, conversion of errors to exception and
"	    immediate abortion is switched on again - until the next :silent!
"	    etc.  The :silent! has the effect of setting v:errmsg to the error
"	    message text (without displaying it) and continuing with the next
"	    script line.
"
"	    When a command triggering autocommands is executed by :silent!
"	    inside a :try/:endtry, the autocommand execution is not suppressed
"	    on error.
"
"	    This test reuses the function MSG() from the previous test.
"-------------------------------------------------------------------------------

func Test_silent_exception()
  XpathINIT
  XloopINIT
  let g:taken = ""

  func S(n) abort
    XloopNEXT
    let g:taken = g:taken . "E" . a:n
    let v:errmsg = ""
    exec "asdf" . a:n

    " Check that ":silent!" continues:
    Xloop 'a'

    " Check that ":silent!" sets "v:errmsg":
    call assert_match("E492: Not an editor command", v:errmsg)
  endfunc

  func Foo()
    while 1
      try
        try
          let caught = 0
          " This is not silent:
          call S(3)
        catch /^Vim:/
          Xpath 'b'
          let caught = 1
          let errmsg3 = substitute(v:exception, '^Vim:', '', "")
          silent! call S(4)
        finally
          call assert_equal(1, caught)
          Xpath 'c'
          call assert_match("E492: Not an editor command", errmsg3)
          silent! call S(5)
          " Break out of try conditionals that cover ":silent!".  This also
          " discards the aborting error when $VIMNOERRTHROW is non-zero.
          break
        endtry
      catch /.*/
        call assert_report('should not get here')
      endtry
    endwhile
    " This is a double ":silent!" (see caller).
    silent! call S(6)
  endfunc

  func Bar()
    try
      silent! call S(2)
      silent! execute "call Foo() | call S(7)"
      silent! call S(8)
    endtry	" normal end of try cond that covers ":silent!"
    " This has a ":silent!" from the caller:
    call S(9)
  endfunc

  silent! call S(1)
  silent! call Bar()
  silent! call S(10)

  call assert_equal("E1E2E3E4E5E6E7E8E9E10", g:taken)

  augroup TMP
    au!
    autocmd BufWritePost * Xpath 'd'
  augroup END

  Xpath 'e'
  silent! write /i/m/p/o/s/s/i/b/l/e
  Xpath 'f'

  call assert_equal('a2a3ba5ca6a7a8a9a10a11edf', g:Xpath)

  augroup TMP
    au!
  augroup END
  augroup! TMP
  delfunction S
  delfunction Foo
  delfunction Bar
endfunc

"-------------------------------------------------------------------------------
" Test 64:  Error exceptions after error, interrupt or :throw		    {{{1
"
"	    When an error occurs after an interrupt or a :throw but before
"	    a matching :catch is reached, all following :catches of that try
"	    block are ignored, but the error exception can be caught by the next
"	    surrounding try conditional.  Any previous error exception is
"	    discarded.  An error is ignored when there is a previous error that
"	    has not been caught.
"-------------------------------------------------------------------------------

func Test_exception_after_error_1()
  XpathINIT
  while 1
    try
      try
        Xpath 'a'
        let caught = 0
        while 1
          if 1
          " Missing :endif
        endwhile	" throw error exception
      catch /^Vim(/
        Xpath 'b'
        let caught = 1
      finally
        Xpath 'c'
        call assert_equal(1, caught)
      endtry
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'd'
      break
    endtry
    call assert_report('should not get here')
  endwhile
  call assert_equal('abcd', g:Xpath)
endfunc

func Test_exception_after_error_2()
  XpathINIT
  while 1
    try
      try
        Xpath 'a'
        let caught = 0
        try
          if 1
          " Missing :endif
        catch /.*/	" throw error exception
          call assert_report('should not get here')
        catch /.*/
          call assert_report('should not get here')
        endtry
      catch /^Vim(/
        Xpath 'b'
        let caught = 1
      finally
        Xpath 'c'
        call assert_equal(1, caught)
      endtry
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'd'
      break
    endtry
    call assert_report('should not get here')
  endwhile
  call assert_equal('abcd', g:Xpath)
endfunc

func Test_exception_after_error_3()
  XpathINIT
  while 1
    try
      try
        let caught = 0
        try
          Xpath 'a'
          call interrupt()
        catch /do_not_catch/
          call assert_report('should not get here')
          if 1
          " Missing :endif
        catch /.*/	" throw error exception
          call assert_report('should not get here')
        catch /.*/
          call assert_report('should not get here')
        endtry
      catch /^Vim(/
        Xpath 'b'
        let caught = 1
      finally
        Xpath 'c'
        call assert_equal(1, caught)
      endtry
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'd'
      break
    endtry
    call assert_report('should not get here')
  endwhile
  call assert_equal('abcd', g:Xpath)
endfunc

func Test_exception_after_error_4()
  XpathINIT
  while 1
    try
      try
        let caught = 0
        try
          Xpath 'a'
          throw "x"
        catch /do_not_catch/
          call assert_report('should not get here')
          if 1
          " Missing :endif
        catch /x/	" throw error exception
          call assert_report('should not get here')
        catch /.*/
          call assert_report('should not get here')
        endtry
      catch /^Vim(/
        Xpath 'b'
        let caught = 1
      finally
        Xpath 'c'
        call assert_equal(1, caught)
      endtry
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'd'
      break
    endtry
    call assert_report('should not get here')
  endwhile
  call assert_equal('abcd', g:Xpath)
endfunc

func Test_exception_after_error_5()
  XpathINIT
  while 1
    try
      try
        let caught = 0
        Xpath 'a'
        endif		" :endif without :if; throw error exception
        if 1
        " Missing :endif
      catch /do_not_catch/ " ignore new error
        call assert_report('should not get here')
      catch /^Vim(endif):/
        Xpath 'b'
        let caught = 1
      catch /^Vim(/
        call assert_report('should not get here')
      finally
        Xpath 'c'
        call assert_equal(1, caught)
      endtry
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'd'
      break
    endtry
    call assert_report('should not get here')
  endwhile
  call assert_equal('abcd', g:Xpath)
endfunc

"-------------------------------------------------------------------------------
" Test 65:  Errors in the /pattern/ argument of a :catch		    {{{1
"
"	    On an error in the /pattern/ argument of a :catch, the :catch does
"	    not match.  Any following :catches of the same :try/:endtry don't
"	    match either.  Finally clauses are executed.
"-------------------------------------------------------------------------------

func Test_catch_pattern_error()
  CheckEnglish
  XpathINIT

  try
    try
      Xpath 'a'
      throw "oops"
    catch /^oops$/
      Xpath 'b'
    catch /\)/		" not checked; exception has already been caught
      call assert_report('should not get here')
    endtry
    Xpath 'c'
  catch /.*/
    call assert_report('should not get here')
  endtry
  call assert_equal('abc', g:Xpath)

  XpathINIT
  func F()
    try
      try
        try
          Xpath 'a'
          throw "ab"
        catch /abc/	" does not catch
          call assert_report('should not get here')
        catch /\)/	" error; discards exception
          call assert_report('should not get here')
        catch /.*/	" not checked
          call assert_report('should not get here')
        finally
          Xpath 'b'
        endtry
        call assert_report('should not get here')
      catch /^ab$/	" checked, but original exception is discarded
        call assert_report('should not get here')
      catch /^Vim(catch):/
        Xpath 'c'
        call assert_match('Vim(catch):E475: Invalid argument:', v:exception)
      finally
        Xpath 'd'
      endtry
      Xpath 'e'
    catch /.*/
      call assert_report('should not get here')
    endtry
    Xpath 'f'
  endfunc

  call F()
  call assert_equal('abcdef', g:Xpath)

  delfunc F
endfunc

"-------------------------------------------------------------------------------
" Test 66:  Stop range :call on error, interrupt, or :throw		    {{{1
"
"	    When a function which is multiply called for a range since it
"	    doesn't handle the range itself has an error in a command
"	    dynamically enclosed by :try/:endtry or gets an interrupt or
"	    executes a :throw, no more calls for the remaining lines in the
"	    range are made.  On an error in a command not dynamically enclosed
"	    by :try/:endtry, the function is executed again for the remaining
"	    lines in the range.
"-------------------------------------------------------------------------------

func Test_stop_range_on_error()
  let test =<< trim [CODE]
    let file = tempname()
    exec "edit" file
    call setline(1, ['line 1', 'line 2', 'line 3'])
    let taken = ""
    let expected = "G1EF1E(1)F1E(2)F1E(3)G2EF2E(1)G3IF3I(1)G4TF4T(1)G5AF5A(1)"

    func F(reason, n) abort
      let g:taken = g:taken .. "F" .. a:n ..
                          \ substitute(a:reason, '\(\l\).*', '\u\1', "") ..
                          \ "(" .. line(".") .. ")"

      if a:reason == "error"
        asdf
      elseif a:reason == "interrupt"
        call interrupt()
      elseif a:reason == "throw"
        throw "xyz"
      elseif a:reason == "aborting error"
        XloopNEXT
        call assert_equal(g:taken, g:expected)
        try
          bwipeout!
          call delete(g:file)
          asdf
        endtry
      endif
    endfunc

    func G(reason, n)
      let g:taken = g:taken .. "G" .. a:n ..
                              \ substitute(a:reason, '\(\l\).*', '\u\1', "")
      1,3call F(a:reason, a:n)
    endfunc

    Xpath 'a'
    call G("error", 1)
    try
      Xpath 'b'
      try
        call G("error", 2)
        call assert_report('should not get here')
      finally
        Xpath 'c'
        try
          call G("interrupt", 3)
          call assert_report('should not get here')
        finally
          Xpath 'd'
          try
            call G("throw", 4)
            call assert_report('should not get here')
          endtry
        endtry
      endtry
    catch /xyz/
      Xpath 'e'
    catch /.*/
      call assert_report('should not get here')
    endtry
    Xpath 'f'
    call G("aborting error", 5)
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcdef', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 67:  :throw across :call command					    {{{1
"
"	    On a call command, an exception might be thrown when evaluating the
"	    function name, during evaluation of the arguments, or when the
"	    function is being executed.  The exception can be caught by the
"	    caller.
"-------------------------------------------------------------------------------

func THROW(x, n)
  if a:n == 1
    Xpath 'A'
  elseif a:n == 2
    Xpath 'B'
  elseif a:n == 3
    Xpath 'C'
  endif
  throw a:x
endfunc

func NAME(x, n)
  if a:n == 1
    call assert_report('should not get here')
  elseif a:n == 2
    Xpath 'D'
  elseif a:n == 3
    Xpath 'E'
  elseif a:n == 4
    Xpath 'F'
  endif
  return a:x
endfunc

func ARG(x, n)
  if a:n == 1
    call assert_report('should not get here')
  elseif a:n == 2
    call assert_report('should not get here')
  elseif a:n == 3
    Xpath 'G'
  elseif a:n == 4
    Xpath 'I'
  endif
  return a:x
endfunc

func Test_throw_across_call_cmd()
  XpathINIT

  func F(x, n)
    if a:n == 2
      call assert_report('should not get here')
    elseif a:n == 4
      Xpath 'a'
    endif
  endfunc

  while 1
    try
      let v:errmsg = ""

      while 1
        try
          Xpath 'b'
          call {NAME(THROW("name", 1), 1)}(ARG(4711, 1), 1)
          call assert_report('should not get here')
        catch /^name$/
          Xpath 'c'
        catch /.*/
          call assert_report('should not get here')
        finally
          call assert_equal("", v:errmsg)
          let v:errmsg = ""
          break
        endtry
      endwhile

      while 1
        try
          Xpath 'd'
          call {NAME("F", 2)}(ARG(THROW("arg", 2), 2), 2)
          call assert_report('should not get here')
        catch /^arg$/
          Xpath 'e'
        catch /.*/
          call assert_report('should not get here')
        finally
          call assert_equal("", v:errmsg)
          let v:errmsg = ""
          break
        endtry
      endwhile

      while 1
        try
          Xpath 'f'
          call {NAME("THROW", 3)}(ARG("call", 3), 3)
          call assert_report('should not get here')
        catch /^call$/
          Xpath 'g'
        catch /^0$/	    " default return value
          call assert_report('should not get here')
        catch /.*/
          call assert_report('should not get here')
        finally
          call assert_equal("", v:errmsg)
          let v:errmsg = ""
          break
        endtry
      endwhile

      while 1
        try
          Xpath 'h'
          call {NAME("F", 4)}(ARG(4711, 4), 4)
          Xpath 'i'
        catch /.*/
          call assert_report('should not get here')
        finally
          call assert_equal("", v:errmsg)
          let v:errmsg = ""
          break
        endtry
      endwhile

    catch /^0$/	    " default return value
      call assert_report('should not get here')
    catch /.*/
      call assert_report('should not get here')
    finally
      call assert_equal("", v:errmsg)
      let v:errmsg = ""
      break
    endtry
  endwhile

  call assert_equal('bAcdDBefEGCghFIai', g:Xpath)
  delfunction F
endfunc

"-------------------------------------------------------------------------------
" Test 68:  :throw across function calls in expressions			    {{{1
"
"	    On a function call within an expression, an exception might be
"	    thrown when evaluating the function name, during evaluation of the
"	    arguments, or when the function is being executed.  The exception
"	    can be caught by the caller.
"
"	    This test reuses the functions THROW(), NAME(), and ARG() from the
"	    previous test.
"-------------------------------------------------------------------------------

func Test_throw_across_call_expr()
  XpathINIT

  func F(x, n)
    if a:n == 2
      call assert_report('should not get here')
    elseif a:n == 4
      Xpath 'a'
    endif
    return a:x
  endfunction

  while 1
    try
      let error = 0
      let v:errmsg = ""

      while 1
        try
          Xpath 'b'
          let var1 = {NAME(THROW("name", 1), 1)}(ARG(4711, 1), 1)
          call assert_report('should not get here')
        catch /^name$/
          Xpath 'c'
        catch /.*/
          call assert_report('should not get here')
        finally
          call assert_equal("", v:errmsg)
          let v:errmsg = ""
          break
        endtry
      endwhile
      call assert_true(!exists('var1'))

      while 1
        try
          Xpath 'd'
          let var2 = {NAME("F", 2)}(ARG(THROW("arg", 2), 2), 2)
          call assert_report('should not get here')
        catch /^arg$/
          Xpath 'e'
        catch /.*/
          call assert_report('should not get here')
        finally
          call assert_equal("", v:errmsg)
          let v:errmsg = ""
          break
        endtry
      endwhile
      call assert_true(!exists('var2'))

      while 1
        try
          Xpath 'f'
          let var3 = {NAME("THROW", 3)}(ARG("call", 3), 3)
          call assert_report('should not get here')
        catch /^call$/
          Xpath 'g'
        catch /^0$/	    " default return value
          call assert_report('should not get here')
        catch /.*/
          call assert_report('should not get here')
        finally
          call assert_equal("", v:errmsg)
          let v:errmsg = ""
          break
        endtry
      endwhile
      call assert_true(!exists('var3'))

      while 1
        try
          Xpath 'h'
          let var4 = {NAME("F", 4)}(ARG(4711, 4), 4)
          Xpath 'i'
        catch /.*/
          call assert_report('should not get here')
        finally
          call assert_equal("", v:errmsg)
          let v:errmsg = ""
          break
        endtry
      endwhile
      call assert_true(exists('var4') && var4 == 4711)

    catch /^0$/	    " default return value
      call assert_report('should not get here')
    catch /.*/
      call assert_report('should not get here')
    finally
      call assert_equal("", v:errmsg)
      break
    endtry
  endwhile

  call assert_equal('bAcdDBefEGCghFIai', g:Xpath)
  delfunc F
endfunc

"-------------------------------------------------------------------------------
" Test 76:  Errors, interrupts, :throw during expression evaluation	    {{{1
"
"	    When a function call made during expression evaluation is aborted
"	    due to an error inside a :try/:endtry region or due to an interrupt
"	    or a :throw, the expression evaluation is aborted as well.	No
"	    message is displayed for the cancelled expression evaluation.  On an
"	    error not inside :try/:endtry, the expression evaluation continues.
"-------------------------------------------------------------------------------

func Test_expr_eval_error()
  let test =<< trim [CODE]
    let taken = ""

    func ERR(n)
      let g:taken = g:taken .. "E" .. a:n
      asdf
    endfunc

    func ERRabort(n) abort
      let g:taken = g:taken .. "A" .. a:n
      asdf
    endfunc	" returns -1; may cause follow-up msg for illegal var/func name

    func WRAP(n, arg)
      let g:taken = g:taken .. "W" .. a:n
      let g:saved_errmsg = v:errmsg
      return arg
    endfunc

    func INT(n)
      let g:taken = g:taken .. "I" .. a:n
      call interrupt()
    endfunc

    func THR(n)
      let g:taken = g:taken .. "T" .. a:n
      throw "should not be caught"
    endfunc

    func CONT(n)
      let g:taken = g:taken .. "C" .. a:n
    endfunc

    func MSG(n)
      let g:taken = g:taken .. "M" .. a:n
      let errmsg = (a:n >= 37 && a:n <= 44) ? g:saved_errmsg : v:errmsg
      let msgptn = (a:n >= 10 && a:n <= 27) ? "^$" : "asdf"
      call assert_match(msgptn, errmsg)
      let v:errmsg = ""
      let g:saved_errmsg = ""
    endfunc

    let v:errmsg = ""

    try
      let t = 1
      while t <= 9
        Xloop 'a'
        try
          if t == 1
            let v{ERR(t) + CONT(t)} = 0
          elseif t == 2
            let v{ERR(t) + CONT(t)}
          elseif t == 3
            let var = exists('v{ERR(t) + CONT(t)}')
          elseif t == 4
            unlet v{ERR(t) + CONT(t)}
          elseif t == 5
            function F{ERR(t) + CONT(t)}()
            endfunction
          elseif t == 6
            function F{ERR(t) + CONT(t)}
          elseif t == 7
            let var = exists('*F{ERR(t) + CONT(t)}')
          elseif t == 8
            delfunction F{ERR(t) + CONT(t)}
          elseif t == 9
            let var = ERR(t) + CONT(t)
          endif
        catch /asdf/
          " v:errmsg is not set when the error message is converted to an
          " exception.  Set it to the original error message.
          let v:errmsg = substitute(v:exception, '^Vim:', '', "")
        catch /^Vim\((\a\+)\)\=:/
          " An error exception has been thrown after the original error.
          let v:errmsg = ""
        finally
          call MSG(t)
          let t = t + 1
          XloopNEXT
          continue	" discard an aborting error
        endtry
      endwhile
    catch /.*/
      call assert_report('should not get here')
    endtry

    try
      let t = 10
      while t <= 18
        Xloop 'b'
        try
          if t == 10
            let v{INT(t) + CONT(t)} = 0
          elseif t == 11
            let v{INT(t) + CONT(t)}
          elseif t == 12
            let var = exists('v{INT(t) + CONT(t)}')
          elseif t == 13
            unlet v{INT(t) + CONT(t)}
          elseif t == 14
            function F{INT(t) + CONT(t)}()
            endfunction
          elseif t == 15
            function F{INT(t) + CONT(t)}
          elseif t == 16
            let var = exists('*F{INT(t) + CONT(t)}')
          elseif t == 17
            delfunction F{INT(t) + CONT(t)}
          elseif t == 18
            let var = INT(t) + CONT(t)
          endif
        catch /^Vim\((\a\+)\)\=:\(Interrupt\)\@!/
          " An error exception has been triggered after the interrupt.
          let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
        finally
          call MSG(t)
          let t = t + 1
          XloopNEXT
          continue	" discard interrupt
        endtry
      endwhile
    catch /.*/
      call assert_report('should not get here')
    endtry

    try
      let t = 19
      while t <= 27
        Xloop 'c'
        try
          if t == 19
            let v{THR(t) + CONT(t)} = 0
          elseif t == 20
            let v{THR(t) + CONT(t)}
          elseif t == 21
            let var = exists('v{THR(t) + CONT(t)}')
          elseif t == 22
            unlet v{THR(t) + CONT(t)}
          elseif t == 23
            function F{THR(t) + CONT(t)}()
            endfunction
          elseif t == 24
            function F{THR(t) + CONT(t)}
          elseif t == 25
            let var = exists('*F{THR(t) + CONT(t)}')
          elseif t == 26
            delfunction F{THR(t) + CONT(t)}
          elseif t == 27
            let var = THR(t) + CONT(t)
          endif
        catch /^Vim\((\a\+)\)\=:/
          " An error exception has been triggered after the :throw.
          let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
        finally
          call MSG(t)
          let t = t + 1
          XloopNEXT
          continue	" discard exception
        endtry
      endwhile
    catch /.*/
      call assert_report('should not get here')
    endtry

    let v{ERR(28) + CONT(28)} = 0
    call MSG(28)
    let v{ERR(29) + CONT(29)}
    call MSG(29)
    let var = exists('v{ERR(30) + CONT(30)}')
    call MSG(30)
    unlet v{ERR(31) + CONT(31)}
    call MSG(31)
    function F{ERR(32) + CONT(32)}()
    endfunction
    call MSG(32)
    function F{ERR(33) + CONT(33)}
    call MSG(33)
    let var = exists('*F{ERR(34) + CONT(34)}')
    call MSG(34)
    delfunction F{ERR(35) + CONT(35)}
    call MSG(35)
    let var = ERR(36) + CONT(36)
    call MSG(36)

    let saved_errmsg = ""

    let v{WRAP(37, ERRabort(37)) + CONT(37)} = 0
    call MSG(37)
    let v{WRAP(38, ERRabort(38)) + CONT(38)}
    call MSG(38)
    let var = exists('v{WRAP(39, ERRabort(39)) + CONT(39)}')
    call MSG(39)
    unlet v{WRAP(40, ERRabort(40)) + CONT(40)}
    call MSG(40)
    function F{WRAP(41, ERRabort(41)) + CONT(41)}()
    endfunction
    call MSG(41)
    function F{WRAP(42, ERRabort(42)) + CONT(42)}
    call MSG(42)
    let var = exists('*F{WRAP(43, ERRabort(43)) + CONT(43)}')
    call MSG(43)
    delfunction F{WRAP(44, ERRabort(44)) + CONT(44)}
    call MSG(44)
    let var = ERRabort(45) + CONT(45)
    call MSG(45)
    Xpath 'd'

    let expected = ""
          \ .. "E1M1E2M2E3M3E4M4E5M5E6M6E7M7E8M8E9M9"
          \ .. "I10M10I11M11I12M12I13M13I14M14I15M15I16M16I17M17I18M18"
          \ .. "T19M19T20M20T21M21T22M22T23M23T24M24T25M25T26M26T27M27"
          \ .. "E28C28M28E29C29M29E30C30M30E31C31M31E32C32M32E33C33M33"
          \ .. "E34C34M34E35C35M35E36C36M36"
          \ .. "A37W37C37M37A38W38C38M38A39W39C39M39A40W40C40M40A41W41C41M41"
          \ .. "A42W42C42M42A43W43C43M43A44W44C44M44A45C45M45"
    call assert_equal(expected, taken)
  [CODE]
  let verify =<< trim [CODE]
    let expected = "a1a2a3a4a5a6a7a8a9"
                      \ .. "b10b11b12b13b14b15b16b17b18"
                      \ .. "c19c20c21c22c23c24c25c26c27d"
    call assert_equal(expected, g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 77:  Errors, interrupts, :throw in name{brace-expression}	    {{{1
"
"	    When a function call made during evaluation of an expression in
"	    braces as part of a function name after ":function" is aborted due
"	    to an error inside a :try/:endtry region or due to an interrupt or
"	    a :throw, the expression evaluation is aborted as well, and the
"	    function definition is ignored, skipping all commands to the
"	    ":endfunction".  On an error not inside :try/:endtry, the expression
"	    evaluation continues and the function gets defined, and can be
"	    called and deleted.
"-------------------------------------------------------------------------------
func Test_brace_expr_error()
  let test =<< trim [CODE]
    func ERR() abort
      Xloop 'a'
      asdf
    endfunc					" returns -1

    func OK()
      Xloop 'b'
      let v:errmsg = ""
      return 0
    endfunc

    let v:errmsg = ""

    Xpath 'c'
    func F{1 + ERR() + OK()}(arg)
      " F0 should be defined.
      if exists("a:arg") && a:arg == "calling"
        Xpath 'd'
      else
        call assert_report('should not get here')
      endif
    endfunction
    call assert_equal("", v:errmsg)
    XloopNEXT

    Xpath 'e'
    call F{1 + ERR() + OK()}("calling")
    call assert_equal("", v:errmsg)
    XloopNEXT

    Xpath 'f'
    delfunction F{1 + ERR() + OK()}
    call assert_equal("", v:errmsg)
    XloopNEXT

    try
      while 1
        try
          Xpath 'g'
          func G{1 + ERR() + OK()}(arg)
            " G0 should not be defined, and the function body should be
            " skipped.
            call assert_report('should not get here')
            " Use an unmatched ":finally" to check whether the body is
            " skipped when an error occurs in ERR().  This works whether or
            " not the exception is converted to an exception.
            finally
              call assert_report('should not get here')
            endtry
          try
            call assert_report('should not get here')
          endfunction

          call assert_report('should not get here')
        catch /asdf/
          " Jumped to when the function is not defined and the body is
          " skipped.
          Xpath 'h'
        catch /.*/
          call assert_report('should not get here')
        finally
          Xpath 'i'
          break
        endtry			" jumped to when the body is not skipped
      endwhile
    catch /.*/
      call assert_report('should not get here')
    endtry
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('ca1b1ea2b2dfa3b3ga4hi', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 78:  Messages on parsing errors in expression evaluation		    {{{1
"
"	    When an expression evaluation detects a parsing error, an error
"	    message is given and converted to an exception, and the expression
"	    evaluation is aborted.
"-------------------------------------------------------------------------------
func Test_expr_eval_error_msg()
  CheckEnglish

  let test =<< trim [CODE]
    let taken = ""

    func F(n)
      let g:taken = g:taken . "F" . a:n
    endfunc

    func MSG(n, enr, emsg)
      let g:taken = g:taken . "M" . a:n
      call assert_match('^' .. a:enr .. ':', v:errmsg)
      call assert_match(a:emsg, v:errmsg)
    endfunc

    func CONT(n)
      let g:taken = g:taken . "C" . a:n
    endfunc

    let v:errmsg = ""
    try
      let t = 1
      while t <= 14
        let g:taken = g:taken . "T" . t
        let v:errmsg = ""
        try
          if t == 1
            let v{novar + CONT(t)} = 0
          elseif t == 2
            let v{novar + CONT(t)}
          elseif t == 3
            let var = exists('v{novar + CONT(t)}')
          elseif t == 4
            unlet v{novar + CONT(t)}
          elseif t == 5
            function F{novar + CONT(t)}()
            endfunction
          elseif t == 6
            function F{novar + CONT(t)}
          elseif t == 7
            let var = exists('*F{novar + CONT(t)}')
          elseif t == 8
            delfunction F{novar + CONT(t)}
          elseif t == 9
            echo novar + CONT(t)
          elseif t == 10
            echo v{novar + CONT(t)}
          elseif t == 11
            echo F{novar + CONT(t)}
          elseif t == 12
            let var = novar + CONT(t)
          elseif t == 13
            let var = v{novar + CONT(t)}
          elseif t == 14
            let var = F{novar + CONT(t)}()
          endif
        catch /^Vim\((\a\+)\)\=:/
          Xloop 'a'
          " v:errmsg is not set when the error message is converted to an
          " exception.  Set it to the original error message.
          let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
        finally
          Xloop 'b'
          if t <= 8 && t != 3 && t != 7
            call MSG(t, 'E475', 'Invalid argument\>')
          else
            call MSG(t, 'E121', "Undefined variable")
          endif
          let t = t + 1
          XloopNEXT
          continue	" discard an aborting error
        endtry
      endwhile
    catch /.*/
      call assert_report('should not get here')
    endtry

    func T(n, expr, enr, emsg)
      try
        let g:taken = g:taken . "T" . a:n
        let v:errmsg = ""
        try
          execute "let var = " . a:expr
        catch /^Vim\((\a\+)\)\=:/
          Xloop 'c'
          " v:errmsg is not set when the error message is converted to an
          " exception.  Set it to the original error message.
          let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
        finally
          Xloop 'd'
          call MSG(a:n, a:enr, a:emsg)
          XloopNEXT
          " Discard an aborting error:
          return
        endtry
      catch /.*/
        call assert_report('should not get here')
      endtry
    endfunc

    call T(15, 'Nofunc() + CONT(15)',	'E117',	"Unknown function")
    call T(16, 'F(1 2 + CONT(16))',	'E116',	"Invalid arguments")
    call T(17, 'F(1, 2) + CONT(17)',	'E118',	"Too many arguments")
    call T(18, 'F() + CONT(18)',	'E119',	"Not enough arguments")
    call T(19, '{(1} + CONT(19)',	'E110',	"Missing ')'")
    call T(20, '("abc"[1) + CONT(20)',	'E111',	"Missing ']'")
    call T(21, '(1 +) + CONT(21)',	'E15',	"Invalid expression")
    call T(22, '1 2 + CONT(22)',	'E488',	"Trailing characters: 2 +")
    call T(23, '(1 ? 2) + CONT(23)',	'E109',	"Missing ':' after '?'")
    call T(24, '("abc) + CONT(24)',	'E114',	"Missing double quote")
    call T(25, "('abc) + CONT(25)",	'E115',	"Missing single quote")
    call T(26, '& + CONT(26)',		'E112', "Option name missing")
    call T(27, '&asdf + CONT(27)',	'E113', "Unknown option")

    let expected = ""
      \ .. "T1M1T2M2T3M3T4M4T5M5T6M6T7M7T8M8T9M9T10M10T11M11T12M12T13M13T14M14"
      \ .. "T15M15T16M16T17M17T18M18T19M19T20M20T21M21T22M22T23M23T24M24T25M25"
      \ .. "T26M26T27M27"

    call assert_equal(expected, taken)
  [CODE]
  let verify =<< trim [CODE]
    let expected = "a1b1a2b2a3b3a4b4a5b5a6b6a7b7a8b8a9b9a10b10a11b11a12b12"
                  \ .. "a13b13a14b14c15d15c16d16c17d17c18d18c19d19c20d20"
                  \ .. "c21d21c22d22c23d23c24d24c25d25c26d26c27d27"
    call assert_equal(expected, g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 79:  Throwing one of several errors for the same command		    {{{1
"
"	    When several errors appear in a row (for instance during expression
"	    evaluation), the first as the most specific one is used when
"	    throwing an error exception.  If, however, a syntax error is
"	    detected afterwards, this one is used for the error exception.
"	    On a syntax error, the next command is not executed, on a normal
"	    error, however, it is (relevant only in a function without the
"	    "abort" flag).  v:errmsg is not set.
"
"	    If throwing error exceptions is configured off, v:errmsg is always
"	    set to the latest error message, that is, to the more general
"	    message or the syntax error, respectively.
"-------------------------------------------------------------------------------
func Test_throw_multi_error()
  CheckEnglish

  let test =<< trim [CODE]
    func NEXT(cmd)
      exec a:cmd . " | Xloop 'a'"
    endfun

    call NEXT('echo novar')			" (checks nextcmd)
    XloopNEXT
    call NEXT('let novar #')			" (skips nextcmd)
    XloopNEXT
    call NEXT('unlet novar #')			" (skips nextcmd)
    XloopNEXT
    call NEXT('let {novar}')			" (skips nextcmd)
    XloopNEXT
    call NEXT('unlet{ novar}')			" (skips nextcmd)

    call assert_equal('a1', g:Xpath)
    XpathINIT
    XloopINIT

    func EXEC(cmd)
      exec a:cmd
    endfunc

    try
      while 1				" dummy loop
        try
          let v:errmsg = ""
          call EXEC('echo novar')	" normal error
        catch /^Vim\((\a\+)\)\=:/
          Xpath 'b'
          call assert_match('E121: Undefined variable: novar', v:exception)
        finally
          Xpath 'c'
          call assert_equal("", v:errmsg)
          break
        endtry
      endwhile

      Xpath 'd'
      let cmd = "let"
      while cmd != ""
        try
          let v:errmsg = ""
          call EXEC(cmd . ' novar #')		" normal plus syntax error
        catch /^Vim\((\a\+)\)\=:/
          Xloop 'e'
          if cmd =~ 'unlet'
            " TODO: should get error for 'novar'
            call assert_match('E488: Trailing characters', v:exception)
          else
            call assert_match('E121: Undefined variable: novar', v:exception)
          endif
        finally
          Xloop 'f'
          call assert_equal("", v:errmsg)
          if cmd == "let"
            let cmd = "unlet"
          else
            let cmd = ""
          endif
          XloopNEXT
          continue
        endtry
      endwhile

      Xpath 'g'
      let cmd = "let"
      while cmd != ""
        try
          let v:errmsg = ""
          call EXEC(cmd . ' {novar}')		" normal plus syntax error
        catch /^Vim\((\a\+)\)\=:/
          Xloop 'h'
          call assert_match('E475: Invalid argument: {novar}', v:exception)
        finally
          Xloop 'i'
          call assert_equal("", v:errmsg)
          if cmd == "let"
            let cmd = "unlet"
          else
            let cmd = ""
          endif
          XloopNEXT
          continue
        endtry
      endwhile
    catch /.*/
      call assert_report('should not get here')
    endtry
    Xpath 'j'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('bcde1f1e2f2gh3i3h4i4j', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 80:  Syntax error in expression for illegal :elseif		    {{{1
"
"	    If there is a syntax error in the expression after an illegal
"	    :elseif, an error message is given (or an error exception thrown)
"	    for the illegal :elseif rather than the expression error.
"-------------------------------------------------------------------------------
func Test_if_syntax_error()
  CheckEnglish

  let test =<< trim [CODE]
    let v:errmsg = ""
    if 0
    else
    elseif 1 ||| 2
    endif
    Xpath 'a'
    call assert_match('E584: :elseif after :else', v:errmsg)

    let v:errmsg = ""
    if 1
    else
    elseif 1 ||| 2
    endif
    Xpath 'b'
    call assert_match('E584: :elseif after :else', v:errmsg)

    let v:errmsg = ""
    elseif 1 ||| 2
    Xpath 'c'
    call assert_match('E582: :elseif without :if', v:errmsg)

    let v:errmsg = ""
    while 1
      elseif 1 ||| 2
    endwhile
    Xpath 'd'
    call assert_match('E582: :elseif without :if', v:errmsg)

    while 1
      try
        try
          let v:errmsg = ""
          if 0
          else
          elseif 1 ||| 2
          endif
        catch /^Vim\((\a\+)\)\=:/
          Xpath 'e'
          call assert_match('E584: :elseif after :else', v:exception)
        finally
          Xpath 'f'
          call assert_equal("", v:errmsg)
        endtry
      catch /.*/
      call assert_report('should not get here')
      finally
        Xpath 'g'
        break
      endtry
    endwhile

    while 1
      try
        try
          let v:errmsg = ""
          if 1
          else
          elseif 1 ||| 2
          endif
        catch /^Vim\((\a\+)\)\=:/
          Xpath 'h'
          call assert_match('E584: :elseif after :else', v:exception)
        finally
          Xpath 'i'
          call assert_equal("", v:errmsg)
        endtry
      catch /.*/
        call assert_report('should not get here')
      finally
        Xpath 'j'
        break
      endtry
    endwhile

    while 1
      try
        try
          let v:errmsg = ""
          elseif 1 ||| 2
        catch /^Vim\((\a\+)\)\=:/
          Xpath 'k'
          call assert_match('E582: :elseif without :if', v:exception)
        finally
          Xpath 'l'
          call assert_equal("", v:errmsg)
        endtry
      catch /.*/
        call assert_report('should not get here')
      finally
        Xpath 'm'
        break
      endtry
    endwhile

    while 1
      try
        try
          let v:errmsg = ""
          while 1
              elseif 1 ||| 2
          endwhile
        catch /^Vim\((\a\+)\)\=:/
          Xpath 'n'
          call assert_match('E582: :elseif without :if', v:exception)
        finally
          Xpath 'o'
          call assert_equal("", v:errmsg)
        endtry
      catch /.*/
        call assert_report('should not get here')
      finally
        Xpath 'p'
        break
      endtry
    endwhile
    Xpath 'q'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcdefghijklmnopq', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 81:  Discarding exceptions after an error or interrupt		    {{{1
"
"	    When an exception is thrown from inside a :try conditional without
"	    :catch and :finally clauses and an error or interrupt occurs before
"	    the :endtry is reached, the exception is discarded.
"-------------------------------------------------------------------------------

func Test_discard_exception_after_error_1()
  let test =<< trim [CODE]
    try
      Xpath 'a'
      try
        Xpath 'b'
        throw "arrgh"
        call assert_report('should not get here')
        if 1
        call assert_report('should not get here')
        " error after :throw: missing :endif
      endtry
      call assert_report('should not get here')
    catch /arrgh/
      call assert_report('should not get here')
    endtry
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('ab', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

" interrupt the code before the endtry is invoked
func Test_discard_exception_after_error_2()
  XpathINIT
  let lines =<< trim [CODE]
    try
      Xpath 'a'
      try
        Xpath 'b'
        throw "arrgh"
        call assert_report('should not get here')
      endtry                      " interrupt here
      call assert_report('should not get here')
    catch /arrgh/
      call assert_report('should not get here')
    endtry
    call assert_report('should not get here')
  [CODE]
  call writefile(lines, 'Xscript', 'D')

  breakadd file 7 Xscript
  try
    let caught_intr = 0
    debuggreedy
    call feedkeys(":source Xscript\<CR>quit\<CR>", "xt")
  catch /^Vim:Interrupt$/
    call assert_match('Xscript, line 7', v:throwpoint)
    let caught_intr = 1
  endtry
  0debuggreedy
  call assert_equal(1, caught_intr)
  call assert_equal('ab', g:Xpath)
  breakdel *
endfunc

"-------------------------------------------------------------------------------
" Test 82:  Ignoring :catch clauses after an error or interrupt		    {{{1
"
"	    When an exception is thrown and an error or interrupt occurs before
"	    the matching :catch clause is reached, the exception is discarded
"	    and the :catch clause is ignored (also for the error or interrupt
"	    exception being thrown then).
"-------------------------------------------------------------------------------

func Test_ignore_catch_after_error_1()
  let test =<< trim [CODE]
    try
      try
        Xpath 'a'
        throw "arrgh"
        call assert_report('should not get here')
        if 1
        call assert_report('should not get here')
        " error after :throw: missing :endif
      catch /.*/
        call assert_report('should not get here')
      catch /.*/
        call assert_report('should not get here')
      endtry
      call assert_report('should not get here')
    catch /arrgh/
      call assert_report('should not get here')
    endtry
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('a', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_ignore_catch_after_error_2()
  let test =<< trim [CODE]
    func E()
      try
        try
          Xpath 'a'
          throw "arrgh"
          call assert_report('should not get here')
          if 1
          call assert_report('should not get here')
          " error after :throw: missing :endif
        catch /.*/
          call assert_report('should not get here')
        catch /.*/
          call assert_report('should not get here')
        endtry
        call assert_report('should not get here')
      catch /arrgh/
        call assert_report('should not get here')
      endtry
    endfunc

    call E()
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('a', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

" interrupt right before a catch is invoked in a script
func Test_ignore_catch_after_intr_1()
  " for unknown reasons this test sometimes fails on MS-Windows.
  let g:test_is_flaky = 1

  XpathINIT
  let lines =<< trim [CODE]
    try
      try
        Xpath 'a'
        throw "arrgh"
        call assert_report('should not get here')
      catch /.*/              " interrupt here
        call assert_report('should not get here')
      catch /.*/
        call assert_report('should not get here')
      endtry
      call assert_report('should not get here')
    catch /arrgh/
      call assert_report('should not get here')
    endtry
    call assert_report('should not get here')
  [CODE]
  call writefile(lines, 'Xscript', 'D')

  breakadd file 6 Xscript
  try
    let caught_intr = 0
    debuggreedy
    call feedkeys(":source Xscript\<CR>quit\<CR>", "xt")
  catch /^Vim:Interrupt$/
    call assert_match('Xscript, line 6', v:throwpoint)
    let caught_intr = 1
  endtry
  0debuggreedy
  call assert_equal(1, caught_intr)
  call assert_equal('a', g:Xpath)
  breakdel *
endfunc

" interrupt right before a catch is invoked inside a function.
func Test_ignore_catch_after_intr_2()
  " for unknown reasons this test sometimes fails on MS-Windows.
  let g:test_is_flaky = 1

  XpathINIT
  func F()
    try
      try
        Xpath 'a'
        throw "arrgh"
        call assert_report('should not get here')
      catch /.*/              " interrupt here
        call assert_report('should not get here')
      catch /.*/
        call assert_report('should not get here')
      endtry
      call assert_report('should not get here')
    catch /arrgh/
      call assert_report('should not get here')
    endtry
    call assert_report('should not get here')
  endfunc

  breakadd func 6 F
  try
    let caught_intr = 0
    debuggreedy
    call feedkeys(":call F()\<CR>quit\<CR>", "xt")
  catch /^Vim:Interrupt$/
    call assert_match('\.F, line 6', v:throwpoint)
    let caught_intr = 1
  endtry
  0debuggreedy
  call assert_equal(1, caught_intr)
  call assert_equal('a', g:Xpath)
  breakdel *
  delfunc F
endfunc

"-------------------------------------------------------------------------------
" Test 83:  Executing :finally clauses after an error or interrupt	    {{{1
"
"	    When an exception is thrown and an error or interrupt occurs before
"	    the :finally of the innermost :try is reached, the exception is
"	    discarded and the :finally clause is executed.
"-------------------------------------------------------------------------------

func Test_finally_after_error()
  let test =<< trim [CODE]
    try
      Xpath 'a'
      try
        Xpath 'b'
        throw "arrgh"
        call assert_report('should not get here')
        if 1
        call assert_report('should not get here')
        " error after :throw: missing :endif
      finally
        Xpath 'c'
      endtry
      call assert_report('should not get here')
    catch /arrgh/
      call assert_report('should not get here')
    endtry
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abc', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

" interrupt the code right before the finally is invoked
func Test_finally_after_intr()
  XpathINIT
  let lines =<< trim [CODE]
    try
      Xpath 'a'
      try
        Xpath 'b'
        throw "arrgh"
        call assert_report('should not get here')
      finally		" interrupt here
        Xpath 'c'
      endtry
      call assert_report('should not get here')
    catch /arrgh/
      call assert_report('should not get here')
    endtry
    call assert_report('should not get here')
  [CODE]
  call writefile(lines, 'Xscript', 'D')

  breakadd file 7 Xscript
  try
    let caught_intr = 0
    debuggreedy
    call feedkeys(":source Xscript\<CR>quit\<CR>", "xt")
  catch /^Vim:Interrupt$/
    call assert_match('Xscript, line 7', v:throwpoint)
    let caught_intr = 1
  endtry
  0debuggreedy
  call assert_equal(1, caught_intr)
  call assert_equal('abc', g:Xpath)
  breakdel *
endfunc

"-------------------------------------------------------------------------------
" Test 84:  Exceptions in autocommand sequences.			    {{{1
"
"	    When an exception occurs in a sequence of autocommands for
"	    a specific event, the rest of the sequence is not executed.  The
"	    command that triggered the autocommand execution aborts, and the
"	    exception is propagated to the caller.
"
"	    For the FuncUndefined event under a function call expression or
"	    :call command, the function is not executed, even when it has
"	    been defined by the autocommands before the exception occurred.
"-------------------------------------------------------------------------------

func Test_autocmd_exception()
  let test =<< trim [CODE]
    func INT()
      call interrupt()
    endfunc

    aug TMP
      autocmd!

      autocmd User x1 Xpath 'a'
      autocmd User x1 throw "x1"
      autocmd User x1 call assert_report('should not get here')

      autocmd User x2 Xpath 'b'
      autocmd User x2 asdf
      autocmd User x2 call assert_report('should not get here')

      autocmd User x3 Xpath 'c'
      autocmd User x3 call INT()
      autocmd User x3 call assert_report('should not get here')

      autocmd FuncUndefined U1 func U1()
      autocmd FuncUndefined U1   call assert_report('should not get here')
      autocmd FuncUndefined U1 endfunc
      autocmd FuncUndefined U1 Xpath 'd'
      autocmd FuncUndefined U1 throw "U1"
      autocmd FuncUndefined U1 call assert_report('should not get here')

      autocmd FuncUndefined U2 func U2()
      autocmd FuncUndefined U2   call assert_report('should not get here')
      autocmd FuncUndefined U2 endfunc
      autocmd FuncUndefined U2 Xpath 'e'
      autocmd FuncUndefined U2 ASDF
      autocmd FuncUndefined U2 call assert_report('should not get here')

      autocmd FuncUndefined U3 func U3()
      autocmd FuncUndefined U3   call assert_report('should not get here')
      autocmd FuncUndefined U3 endfunc
      autocmd FuncUndefined U3 Xpath 'f'
      autocmd FuncUndefined U3 call INT()
      autocmd FuncUndefined U3 call assert_report('should not get here')
    aug END

    try
      try
        Xpath 'g'
        doautocmd User x1
      catch /x1/
        Xpath 'h'
      endtry

      while 1
        try
          Xpath 'i'
          doautocmd User x2
        catch /asdf/
          Xpath 'j'
        finally
          Xpath 'k'
          break
        endtry
      endwhile

      while 1
        try
          Xpath 'l'
          doautocmd User x3
        catch /Vim:Interrupt/
          Xpath 'm'
        finally
          Xpath 'n'
          " ... but break loop for caught interrupt exception,
          " or discard interrupt and break loop if $VIMNOINTTHROW
          break
        endtry
      endwhile

      if exists("*U1") | delfunction U1 | endif
      if exists("*U2") | delfunction U2 | endif
      if exists("*U3") | delfunction U3 | endif

      try
        Xpath 'o'
        call U1()
      catch /U1/
        Xpath 'p'
      endtry

      while 1
        try
          Xpath 'q'
          call U2()
        catch /ASDF/
          Xpath 'r'
        finally
          Xpath 's'
          " ... but break loop for caught error exception,
          " or discard error and break loop if $VIMNOERRTHROW
          break
        endtry
      endwhile

      while 1
        try
          Xpath 't'
          call U3()
        catch /Vim:Interrupt/
          Xpath 'u'
        finally
          Xpath 'v'
          " ... but break loop for caught interrupt exception,
          " or discard interrupt and break loop if $VIMNOINTTHROW
          break
        endtry
      endwhile
    catch /.*/
      call assert_report('should not get here')
    endtry
    Xpath 'w'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('gahibjklcmnodpqerstfuvw', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 85:  Error exceptions in autocommands for I/O command events	    {{{1
"
"	    When an I/O command is inside :try/:endtry, autocommands to be
"	    executed after it should be skipped on an error (exception) in the
"	    command itself or in autocommands to be executed before the command.
"	    In the latter case, the I/O command should not be executed either.
"	    Example 1: BufWritePre, :write, BufWritePost
"	    Example 2: FileReadPre, :read, FileReadPost.
"-------------------------------------------------------------------------------

func Test_autocmd_error_io_exception()
  let test =<< trim [CODE]
    " Remove the autocommands for the events specified as arguments in all used
    " autogroups.
    func Delete_autocommands(...)
      let augfile = tempname()
      while 1
        try
          exec "redir >" . augfile
          aug
          redir END
          exec "edit" augfile
          g/^$/d
          norm G$
          let wrap = "w"
          while search('\%(  \|^\)\@<=.\{-}\%(  \)\@=', wrap) > 0
            let wrap = "W"
            exec "norm y/  \n"
            let argno = 1
            while argno <= a:0
              exec "au!" escape(@", " ") a:{argno}
              let argno = argno + 1
            endwhile
          endwhile
        catch /.*/
        finally
          bwipeout!
          call delete(augfile)
          break
        endtry
      endwhile
    endfunc

    call Delete_autocommands("BufWritePre", "BufWritePost")

    while 1
      try
        try
          let post = 0
          aug TMP
            au! BufWritePost * let post = 1
          aug END
          write /n/o/n/e/x/i/s/t/e/n/t
        catch /^Vim(write):/
          Xpath 'a'
          call assert_match("E212: Can't open file for writing", v:exception)
        finally
          Xpath 'b'
          call assert_equal(0, post)
          au! TMP
          aug! TMP
        endtry
      catch /.*/
        call assert_report('should not get here')
      finally
        Xpath 'c'
        break
      endtry
    endwhile

    while 1
      try
        try
          let post = 0
          aug TMP
            au! BufWritePre  * asdf
            au! BufWritePost * let post = 1
          aug END
          let tmpfile = tempname()
          exec "write" tmpfile
        catch /^Vim\((write)\)\=:/
          Xpath 'd'
          call assert_match('E492: Not an editor command', v:exception)
        finally
          Xpath 'e'
          if filereadable(tmpfile)
            call assert_report('should not get here')
          endif
          call assert_equal(0, post)
          au! TMP
          aug! TMP
        endtry
      catch /.*/
        call assert_report('should not get here')
      finally
        Xpath 'f'
        break
      endtry
    endwhile

    call delete(tmpfile)

    call Delete_autocommands("BufWritePre", "BufWritePost",
          \ "BufReadPre", "BufReadPost", "FileReadPre", "FileReadPost")

    while 1
      try
        try
          let post = 0
          aug TMP
            au! FileReadPost * let post = 1
          aug END
          let caught = 0
          read /n/o/n/e/x/i/s/t/e/n/t
        catch /^Vim(read):/
          Xpath 'g'
          call assert_match("E484: Can't open file", v:exception)
        finally
          Xpath 'h'
          call assert_equal(0, post)
          au! TMP
          aug! TMP
        endtry
      catch /.*/
        call assert_report('should not get here')
      finally
        Xpath 'i'
        break
      endtry
    endwhile

    while 1
      try
        let infile = tempname()
        let tmpfile = tempname()
        call writefile(["XYZ"], infile)
        exec "edit" tmpfile
        try
          Xpath 'j'
          try
            let post = 0
            aug TMP
              au! FileReadPre  * asdf
              au! FileReadPost * let post = 1
            aug END
            exec "0read" infile
          catch /^Vim\((read)\)\=:/
            Xpath 'k'
            call assert_match('E492: Not an editor command', v:exception)
          finally
            Xpath 'l'
            if getline("1") == "XYZ"
              call assert_report('should not get here')
            endif
            call assert_equal(0, post)
            au! TMP
            aug! TMP
          endtry
        finally
          Xpath 'm'
          bwipeout!
        endtry
      catch /.*/
        call assert_report('should not get here')
      finally
        Xpath 'n'
        break
      endtry
    endwhile

    call delete(infile)
    call delete(tmpfile)
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcdefghijklmn', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 87   using (expr) ? funcref : funcref				    {{{1
"
"	    Vim needs to correctly parse the funcref and even when it does
"	    not execute the funcref, it needs to consume the trailing ()
"-------------------------------------------------------------------------------

func Add2(x1, x2)
  return a:x1 + a:x2
endfu

func GetStr()
  return "abcdefghijklmnopqrstuvwxyp"
endfu

func Test_funcref_with_condexpr()
  call assert_equal(5, function('Add2')(2,3))

  call assert_equal(3, 1 ? function('Add2')(1,2) : function('Add2')(2,3))
  call assert_equal(5, 0 ? function('Add2')(1,2) : function('Add2')(2,3))
  " Make sure, GetStr() still works.
  call assert_equal('abcdefghijk', GetStr()[0:10])
endfunc

" Test 90:  Recognizing {} in variable name.			    {{{1
"-------------------------------------------------------------------------------

func Test_curlies()
    let s:var = 66
    let ns = 's'
    call assert_equal(66, {ns}:var)

    let g:a = {}
    let g:b = 't'
    let g:a[g:b] = 77
    call assert_equal(77, g:a['t'])
endfunc

"-------------------------------------------------------------------------------
" Test 91:  using type().					    {{{1
"-------------------------------------------------------------------------------

func Test_type()
    call assert_equal(0, type(0))
    call assert_equal(1, type(""))
    call assert_equal(2, type(function("tr")))
    call assert_equal(2, type(function("tr", [8])))
    call assert_equal(3, type([]))
    call assert_equal(4, type({}))
    call assert_equal(5, type(0.0))
    call assert_equal(6, type(v:false))
    call assert_equal(6, type(v:true))
    call assert_equal(7, type(v:none))
    call assert_equal(7, type(v:null))
    call assert_equal(8, v:t_job)
    call assert_equal(9, v:t_channel)
    call assert_equal(v:t_number, type(0))
    call assert_equal(v:t_string, type(""))
    call assert_equal(v:t_func, type(function("tr")))
    call assert_equal(v:t_func, type(function("tr", [8])))
    call assert_equal(v:t_list, type([]))
    call assert_equal(v:t_dict, type({}))
    call assert_equal(v:t_float, type(0.0))
    call assert_equal(v:t_bool, type(v:false))
    call assert_equal(v:t_bool, type(v:true))
    call assert_equal(v:t_none, type(v:none))
    call assert_equal(v:t_none, type(v:null))
    call assert_equal(v:t_string, type(test_null_string()))
    call assert_equal(v:t_func, type(test_null_function()))
    call assert_equal(v:t_func, type(test_null_partial()))
    call assert_equal(v:t_list, type(test_null_list()))
    call assert_equal(v:t_dict, type(test_null_dict()))
    if has('job')
      call assert_equal(v:t_job, type(test_null_job()))
    endif
    if has('channel')
      call assert_equal(v:t_channel, type(test_null_channel()))
    endif
    call assert_equal(v:t_blob, type(test_null_blob()))

    call assert_fails("call type(test_void())", ['E340:', 'E685:'])
    call assert_fails("call type(test_unknown())", ['E340:', 'E685:'])

    call assert_equal(0, 0 + v:false)
    call assert_equal(1, 0 + v:true)
    call assert_equal(0, 0 + v:none)
    call assert_equal(0, 0 + v:null)

    call assert_equal('v:false', '' . v:false)
    call assert_equal('v:true', '' . v:true)
    call assert_equal('v:none', '' . v:none)
    call assert_equal('v:null', '' . v:null)

    call assert_true(v:false == 0)
    call assert_false(v:false != 0)
    call assert_true(v:true == 1)
    call assert_false(v:true != 1)
    call assert_false(v:true == v:false)
    call assert_true(v:true != v:false)

    call assert_true(v:null == 0)
    call assert_false(v:null == 1)
    call assert_false(v:null != 0)
    call assert_true(v:none == 0)
    call assert_false(v:none == 1)
    call assert_false(v:none != 0)
    call assert_true(v:null == 0.0)
    call assert_false(v:null == 0.1)
    call assert_false(v:null != 0.0)

    call assert_true(v:false is v:false)
    call assert_true(v:true is v:true)
    call assert_true(v:none is v:none)
    call assert_true(v:null is v:null)

    call assert_false(v:false isnot v:false)
    call assert_false(v:true isnot v:true)
    call assert_false(v:none isnot v:none)
    call assert_false(v:null isnot v:null)

    call assert_false(v:false is 0)
    call assert_false(v:true is 1)
    call assert_false(v:true is v:false)
    call assert_false(v:none is 0)
    call assert_false(v:none is [])
    call assert_false(v:none is {})
    call assert_false(v:none is 'text')
    call assert_false(v:null is 0)
    call assert_false(v:null is v:none)

    call assert_true(v:false isnot 0)
    call assert_true(v:true isnot 1)
    call assert_true(v:true isnot v:false)
    call assert_true(v:none isnot 0)
    call assert_true(v:null isnot 0)
    call assert_true(v:null isnot v:none)

    call assert_equal(v:false, eval(string(v:false)))
    call assert_equal(v:true, eval(string(v:true)))
    call assert_equal(v:none, eval(string(v:none)))
    call assert_equal(v:null, eval(string(v:null)))

    call assert_equal(v:false, copy(v:false))
    call assert_equal(v:true, copy(v:true))
    call assert_equal(v:none, copy(v:none))
    call assert_equal(v:null, copy(v:null))

    call assert_equal([v:false], deepcopy([v:false]))
    call assert_equal([v:true], deepcopy([v:true]))
    call assert_equal([v:none], deepcopy([v:none]))
    call assert_equal([v:null], deepcopy([v:null]))

    call assert_true(empty(v:false))
    call assert_false(empty(v:true))
    call assert_true(empty(v:null))
    call assert_true(empty(v:none))

    func ChangeYourMind()
	try
	    return v:true
	finally
	    return 'something else'
	endtry
    endfunc

    call ChangeYourMind()
endfunc

func Test_typename()
  call assert_equal('number', typename(123))
  call assert_equal('string', typename('x'))
  call assert_equal('list<number>', typename([123]))
  call assert_equal('dict<number>', typename(#{key: 123}))
  call assert_equal('list<dict<number>>', typename([#{key: 123}]))

  let l = []
  let d = #{a: 0}
  let l = [d]
  let l[0].e = #{b: l}
  call assert_equal('list<dict<any>>', typename(l))
  call assert_equal('dict<any>', typename(d))
endfunc

"-------------------------------------------------------------------------------
" Test 92:  skipping code					    {{{1
"-------------------------------------------------------------------------------

func Test_skip()
    let Fn = function('Test_type')
    call assert_false(0 && Fn[1])
    call assert_false(0 && string(Fn))
    call assert_false(0 && len(Fn))
    let l = []
    call assert_false(0 && l[1])
    call assert_false(0 && string(l))
    call assert_false(0 && len(l))
    let f = 1.0
    call assert_false(0 && f[1])
    call assert_false(0 && string(f))
    call assert_false(0 && len(f))
    let sp = v:null
    call assert_false(0 && sp[1])
    call assert_false(0 && string(sp))
    call assert_false(0 && len(sp))

endfunc

"-------------------------------------------------------------------------------
" Test 93:  :echo and string()					    {{{1
"-------------------------------------------------------------------------------

func Test_echo_and_string()
    " String
    let a = 'foo bar'
    redir => result
    echo a
    echo string(a)
    redir END
    let l = split(result, "\n")
    call assert_equal(["foo bar",
		     \ "'foo bar'"], l)

    " Float
    let a = -1.2e0
    redir => result
    echo a
    echo string(a)
    redir END
    let l = split(result, "\n")
    call assert_equal(["-1.2",
                     \ "-1.2"], l)

    " Funcref
    redir => result
    echo function('string')
    echo string(function('string'))
    redir END
    let l = split(result, "\n")
    call assert_equal(["string",
		     \ "function('string')"], l)

    " Recursive dictionary
    let a = {}
    let a["a"] = a
    redir => result
    echo a
    echo string(a)
    redir END
    let l = split(result, "\n")
    call assert_equal(["{'a': {...}}",
		     \ "{'a': {...}}"], l)

    " Recursive list
    let a = [0]
    let a[0] = a
    redir => result
    echo a
    echo string(a)
    redir END
    let l = split(result, "\n")
    call assert_equal(["[[...]]",
		     \ "[[...]]"], l)

    " Empty dictionaries in a list
    let a = {}
    redir => result
    echo [a, a, a]
    echo string([a, a, a])
    redir END
    let l = split(result, "\n")
    call assert_equal(["[{}, {}, {}]",
		     \ "[{}, {}, {}]"], l)

    " Empty dictionaries in a dictionary
    let a = {}
    let b = {"a": a, "b": a}
    redir => result
    echo b
    echo string(b)
    redir END
    let l = split(result, "\n")
    call assert_equal(["{'a': {}, 'b': {}}",
		     \ "{'a': {}, 'b': {}}"], l)

    " Empty lists in a list
    let a = []
    redir => result
    echo [a, a, a]
    echo string([a, a, a])
    redir END
    let l = split(result, "\n")
    call assert_equal(["[[], [], []]",
		     \ "[[], [], []]"], l)

    " Empty lists in a dictionary
    let a = []
    let b = {"a": a, "b": a}
    redir => result
    echo b
    echo string(b)
    redir END
    let l = split(result, "\n")
    call assert_equal(["{'a': [], 'b': []}",
		     \ "{'a': [], 'b': []}"], l)

    " Dictionaries in a list
    let a = {"one": "yes", "two": "yes", "three": "yes"}
    redir => result
    echo [a, a, a]
    echo string([a, a, a])
    redir END
    let l = split(result, "\n")
    call assert_equal(["[{'one': 'yes', 'two': 'yes', 'three': 'yes'}, {...}, {...}]",
		     \ "[{'one': 'yes', 'two': 'yes', 'three': 'yes'}, {'one': 'yes', 'two': 'yes', 'three': 'yes'}, {'one': 'yes', 'two': 'yes', 'three': 'yes'}]"], l)

    " Dictionaries in a dictionary
    let a = {"one": "yes", "two": "yes", "three": "yes"}
    let b = {"a": a, "b": a}
    redir => result
    echo b
    echo string(b)
    redir END
    let l = split(result, "\n")
    call assert_equal(["{'a': {'one': 'yes', 'two': 'yes', 'three': 'yes'}, 'b': {...}}",
		     \ "{'a': {'one': 'yes', 'two': 'yes', 'three': 'yes'}, 'b': {'one': 'yes', 'two': 'yes', 'three': 'yes'}}"], l)

    " Lists in a list
    let a = [1, 2, 3]
    redir => result
    echo [a, a, a]
    echo string([a, a, a])
    redir END
    let l = split(result, "\n")
    call assert_equal(["[[1, 2, 3], [...], [...]]",
		     \ "[[1, 2, 3], [1, 2, 3], [1, 2, 3]]"], l)

    " Lists in a dictionary
    let a = [1, 2, 3]
    let b = {"a": a, "b": a}
    redir => result
    echo b
    echo string(b)
    redir END
    let l = split(result, "\n")
    call assert_equal(["{'a': [1, 2, 3], 'b': [...]}",
		     \ "{'a': [1, 2, 3], 'b': [1, 2, 3]}"], l)

    call assert_fails('echo &:', 'E112:')
    call assert_fails('echo &g:', 'E112:')
    call assert_fails('echo &l:', 'E112:')

endfunc

"-------------------------------------------------------------------------------
" Test 94:  64-bit Numbers					    {{{1
"-------------------------------------------------------------------------------

func Test_num64()
    call assert_notequal( 4294967296, 0)
    call assert_notequal(-4294967296, 0)
    call assert_equal( 4294967296,  0xFFFFffff + 1)
    call assert_equal(-4294967296, -0xFFFFffff - 1)

    call assert_equal( 9223372036854775807,  1 / 0)
    call assert_equal(-9223372036854775807, -1 / 0)
    call assert_equal(-9223372036854775807 - 1,  0 / 0)

    call assert_equal( 0x7FFFffffFFFFffff, float2nr( 1.0e150))
    call assert_equal(-0x7FFFffffFFFFffff, float2nr(-1.0e150))

    let rng = range(0xFFFFffff, 0x100000001)
    call assert_equal([0xFFFFffff, 0x100000000, 0x100000001], rng)
    call assert_equal(0x100000001, max(rng))
    call assert_equal(0xFFFFffff, min(rng))
    call assert_equal(rng, sort(range(0x100000001, 0xFFFFffff, -1), 'N'))
endfunc

"-------------------------------------------------------------------------------
" Test 95:  lines of :append, :change, :insert			    {{{1
"-------------------------------------------------------------------------------

func DefineFunction(name, body)
    let func = join(['function! ' . a:name . '()'] + a:body + ['endfunction'], "\n")
    exec func
endfunc

func Test_script_lines()
    " :append
    try
	call DefineFunction('T_Append', [
		    \ 'append',
		    \ 'py <<EOS',
		    \ '.',
		    \ ])
    catch
	call assert_report("Can't define function")
    endtry
    try
	call DefineFunction('T_Append', [
		    \ 'append',
		    \ 'abc',
		    \ ])
	call assert_report("Shouldn't be able to define function")
    catch
	call assert_exception('Vim(function):E1145: Missing heredoc end marker: .')
    endtry

    " :change
    try
	call DefineFunction('T_Change', [
		    \ 'change',
		    \ 'py <<EOS',
		    \ '.',
		    \ ])
    catch
	call assert_report("Can't define function")
    endtry
    try
	call DefineFunction('T_Change', [
		    \ 'change',
		    \ 'abc',
		    \ ])
	call assert_report("Shouldn't be able to define function")
    catch
	call assert_exception('Vim(function):E1145: Missing heredoc end marker: .')
    endtry

    " :insert
    try
	call DefineFunction('T_Insert', [
		    \ 'insert',
		    \ 'py <<EOS',
		    \ '.',
		    \ ])
    catch
	call assert_report("Can't define function")
    endtry
    try
	call DefineFunction('T_Insert', [
		    \ 'insert',
		    \ 'abc',
		    \ ])
	call assert_report("Shouldn't be able to define function")
    catch
	call assert_exception('Vim(function):E1145: Missing heredoc end marker: .')
    endtry
endfunc

"-------------------------------------------------------------------------------
" Test 96:  line continuation						    {{{1
"
"	    Undefined behavior was detected by ubsan with line continuation
"	    after an empty line.
"-------------------------------------------------------------------------------
func Test_script_empty_line_continuation()

    \
endfunc

"-------------------------------------------------------------------------------
" Test 97:  bitwise functions						    {{{1
"-------------------------------------------------------------------------------
func Test_bitwise_functions()
    " and
    call assert_equal(127, and(127, 127))
    call assert_equal(16, and(127, 16))
    eval 127->and(16)->assert_equal(16)
    call assert_equal(0, and(127, 128))
    call assert_fails("call and([], 1)", 'E745:')
    call assert_fails("call and({}, 1)", 'E728:')
    call assert_fails("call and(1.0, 1)", 'E805:')
    call assert_fails("call and(1, 1.0)", 'E805:')
    call assert_fails("call and(1, [])", 'E745:')
    call assert_fails("call and(1, {})", 'E728:')
    " or
    call assert_equal(23, or(16, 7))
    call assert_equal(15, or(8, 7))
    eval 8->or(7)->assert_equal(15)
    call assert_equal(123, or(0, 123))
    call assert_fails("call or([], 1)", 'E745:')
    call assert_fails("call or({}, 1)", 'E728:')
    call assert_fails("call or(1.0, 1)", 'E805:')
    call assert_fails("call or(1, 1.0)", 'E805:')
    call assert_fails("call or(1, [])", 'E745:')
    call assert_fails("call or(1, {})", 'E728:')
    " xor
    call assert_equal(0, xor(127, 127))
    call assert_equal(111, xor(127, 16))
    eval 127->xor(16)->assert_equal(111)
    call assert_equal(255, xor(127, 128))
    call assert_fails("call xor(1.0, 1)", 'E805:')
    call assert_fails("call xor(1, 1.0)", 'E805:')
    call assert_fails("call xor([], 1)", 'E745:')
    call assert_fails("call xor({}, 1)", 'E728:')
    call assert_fails("call xor(1, [])", 'E745:')
    call assert_fails("call xor(1, {})", 'E728:')
    " invert
    call assert_equal(65408, and(invert(127), 65535))
    eval 127->invert()->and(65535)->assert_equal(65408)
    call assert_equal(65519, and(invert(16), 65535))
    call assert_equal(65407, and(invert(128), 65535))
    call assert_fails("call invert(1.0)", 'E805:')
    call assert_fails("call invert([])", 'E745:')
    call assert_fails("call invert({})", 'E728:')
endfunc

" Test using bang after user command				    {{{1
func Test_user_command_with_bang()
    command -bang Nieuw let nieuw = 1
    Ni!
    call assert_equal(1, nieuw)
    unlet nieuw
    delcommand Nieuw
endfunc

func Test_script_expand_sfile()
  let lines =<< trim END
    func s:snr()
      return expand('<sfile>')
    endfunc
    let g:result = s:snr()
  END
  call writefile(lines, 'Xexpand', 'D')
  source Xexpand
  call assert_match('<SNR>\d\+_snr', g:result)
  source Xexpand
  call assert_match('<SNR>\d\+_snr', g:result)

  unlet g:result
endfunc

func Test_compound_assignment_operators()
    " Test for number
    let x = 1
    let x += 10
    call assert_equal(11, x)
    let x -= 5
    call assert_equal(6, x)
    let x *= 4
    call assert_equal(24, x)
    let x /= 3
    call assert_equal(8, x)
    let x %= 3
    call assert_equal(2, x)
    let x .= 'n'
    call assert_equal('2n', x)

    " Test special cases: division or modulus with 0.
    let x = 1
    let x /= 0
    call assert_equal(0x7FFFFFFFFFFFFFFF, x)

    let x = -1
    let x /= 0
    call assert_equal(-0x7FFFFFFFFFFFFFFF, x)

    let x = 0
    let x /= 0
    call assert_equal(-0x7FFFFFFFFFFFFFFF - 1, x)

    let x = 1
    let x %= 0
    call assert_equal(0, x)

    let x = -1
    let x %= 0
    call assert_equal(0, x)

    let x = 0
    let x %= 0
    call assert_equal(0, x)

    " Test for string
    let x = 'str'
    let x .= 'ing'
    call assert_equal('string', x)
    let x += 1
    call assert_equal(1, x)

    " Test for float
    let x -= 1.5
    call assert_equal(-0.5, x)
    let x = 0.5
    let x += 4.5
    call assert_equal(5.0, x)
    let x -= 1.5
    call assert_equal(3.5, x)
    let x *= 3.0
    call assert_equal(10.5, x)
    let x /= 2.5
    call assert_equal(4.2, x)
    call assert_fails('let x %= 0.5', 'E734:')
    call assert_fails('let x .= "f"', 'E734:')
    let x = !3.14
    call assert_equal(0.0, x)

    " integer and float operations
    let x = 1
    let x *= 2.1
    call assert_equal(2.1, x)
    let x = 1
    let x /= 0.25
    call assert_equal(4.0, x)
    let x = 1
    call assert_fails('let x %= 0.25', 'E734:')
    let x = 1
    call assert_fails('let x .= 0.25', 'E734:')
    let x = 1.0
    call assert_fails('let x += [1.1]', 'E734:')

    " Test for environment variable
    let $FOO = 1
    call assert_fails('let $FOO += 1', 'E734:')
    call assert_fails('let $FOO -= 1', 'E734:')
    call assert_fails('let $FOO *= 1', 'E734:')
    call assert_fails('let $FOO /= 1', 'E734:')
    call assert_fails('let $FOO %= 1', 'E734:')
    let $FOO .= 's'
    call assert_equal('1s', $FOO)
    unlet $FOO

    " Test for option variable (type: number)
    let &scrolljump = 1
    let &scrolljump += 5
    call assert_equal(6, &scrolljump)
    let &scrolljump -= 2
    call assert_equal(4, &scrolljump)
    let &scrolljump *= 3
    call assert_equal(12, &scrolljump)
    let &scrolljump /= 2
    call assert_equal(6, &scrolljump)
    let &scrolljump %= 5
    call assert_equal(1, &scrolljump)
    call assert_fails('let &scrolljump .= "j"', ['E734:', 'E734:'])
    set scrolljump&vim

    let &foldlevelstart = 2
    let &foldlevelstart -= 1
    call assert_equal(1, &foldlevelstart)
    let &foldlevelstart -= 1
    call assert_equal(0, &foldlevelstart)
    let &foldlevelstart = 2
    let &foldlevelstart -= 2
    call assert_equal(0, &foldlevelstart)

    " Test for register
    let @/ = 1
    call assert_fails('let @/ += 1', 'E734:')
    call assert_fails('let @/ -= 1', 'E734:')
    call assert_fails('let @/ *= 1', 'E734:')
    call assert_fails('let @/ /= 1', 'E734:')
    call assert_fails('let @/ %= 1', 'E734:')
    let @/ .= 's'
    call assert_equal('1s', @/)
    let @/ = ''
endfunc

func Test_unlet_env()
  let $TESTVAR = 'yes'
  call assert_equal('yes', $TESTVAR)
  call assert_fails('lockvar $TESTVAR', 'E940:')
  call assert_fails('unlockvar $TESTVAR', 'E940:')
  call assert_equal('yes', $TESTVAR)
  if 0
    unlet $TESTVAR
  endif
  call assert_equal('yes', $TESTVAR)
  unlet $TESTVAR
  call assert_equal('', $TESTVAR)
endfunc

func Test_refcount()
    " Immediate values
    call assert_equal(-1, test_refcount(1))
    call assert_equal(-1, test_refcount('s'))
    call assert_equal(-1, test_refcount(v:true))
    call assert_equal(0, test_refcount([]))
    call assert_equal(0, test_refcount({}))
    call assert_equal(0, test_refcount(0zff))
    call assert_equal(0, test_refcount({-> line('.')}))
    call assert_equal(-1, test_refcount(0.1))
    if has('job')
        call assert_equal(0, test_refcount(job_start([&shell, &shellcmdflag, 'echo .'])))
    endif

    " No refcount types
    let x = 1
    call assert_equal(-1, test_refcount(x))
    let x = 's'
    call assert_equal(-1, test_refcount(x))
    let x = v:true
    call assert_equal(-1, test_refcount(x))
    let x = 0.1
    call assert_equal(-1, test_refcount(x))

    " Check refcount
    let x = []
    call assert_equal(1, test_refcount(x))

    let x = {}
    call assert_equal(1, x->test_refcount())

    let x = 0zff
    call assert_equal(1, test_refcount(x))

    let X = {-> line('.')}
    call assert_equal(1, test_refcount(X))
    let Y = X
    call assert_equal(2, test_refcount(X))

    if has('job')
        let job = job_start([&shell, &shellcmdflag, 'echo .'])
        call assert_equal(1, test_refcount(job))
        call assert_equal(1, test_refcount(job_getchannel(job)))
        call assert_equal(1, test_refcount(job))
    endif

    " Function arguments, copying and unassigning
    func ExprCheck(x, i)
        let i = a:i + 1
        call assert_equal(i, test_refcount(a:x))
        let Y = a:x
        call assert_equal(i + 1, test_refcount(a:x))
        call assert_equal(test_refcount(a:x), test_refcount(Y))
        let Y = 0
        call assert_equal(i, test_refcount(a:x))
    endfunc
    call ExprCheck([], 0)
    call ExprCheck({}, 0)
    call ExprCheck(0zff, 0)
    call ExprCheck({-> line('.')}, 0)
    if has('job')
	call ExprCheck(job, 1)
	call ExprCheck(job_getchannel(job), 1)
	call job_stop(job)
    endif
    delfunc ExprCheck

    " Regarding function
    func Func(x) abort
        call assert_equal(2, test_refcount(function('Func')))
        call assert_equal(0, test_refcount(funcref('Func')))
    endfunc
    call assert_equal(1, test_refcount(function('Func')))
    call assert_equal(0, test_refcount(function('Func', [1])))
    call assert_equal(0, test_refcount(funcref('Func')))
    call assert_equal(0, test_refcount(funcref('Func', [1])))
    let X = function('Func')
    let Y = X
    call assert_equal(1, test_refcount(X))
    let X = function('Func', [1])
    let Y = X
    call assert_equal(2, test_refcount(X))
    let X = funcref('Func')
    let Y = X
    call assert_equal(2, test_refcount(X))
    let X = funcref('Func', [1])
    let Y = X
    call assert_equal(2, test_refcount(X))
    unlet X
    unlet Y
    call Func(1)
    delfunc Func

    " Function with dict
    func DictFunc() dict
        call assert_equal(3, test_refcount(self))
    endfunc
    let d = {'Func': function('DictFunc')}
    call assert_equal(1, test_refcount(d))
    call assert_equal(0, test_refcount(d.Func))
    call d.Func()
    unlet d
    delfunc DictFunc

    if has('channel')
      call assert_equal(-1, test_refcount(test_null_job()))
      call assert_equal(-1, test_refcount(test_null_channel()))
    endif
    call assert_equal(-1, test_refcount(test_null_function()))
    call assert_equal(-1, test_refcount(test_null_partial()))
    call assert_equal(-1, test_refcount(test_null_blob()))
    call assert_equal(-1, test_refcount(test_null_list()))
    call assert_equal(-1, test_refcount(test_null_dict()))
endfunc

" Test for missing :endif, :endfor, :endwhile and :endtry           {{{1
func Test_missing_end()
  call writefile(['if 2 > 1', 'echo ">"'], 'Xscript', 'D')
  call assert_fails('source Xscript', 'E171:')
  call writefile(['for i in range(5)', 'echo i'], 'Xscript')
  call assert_fails('source Xscript', 'E170:')
  call writefile(['while v:true', 'echo "."'], 'Xscript')
  call assert_fails('source Xscript', 'E170:')
  call writefile(['try', 'echo "."'], 'Xscript')
  call assert_fails('source Xscript', 'E600:')

  " Using endfor with :while
  let caught_e732 = 0
  try
    while v:true
    endfor
  catch /E732:/
    let caught_e732 = 1
  endtry
  call assert_equal(1, caught_e732)

  " Using endwhile with :for
  let caught_e733 = 0
  try
    for i in range(1)
    endwhile
  catch /E733:/
    let caught_e733 = 1
  endtry
  call assert_equal(1, caught_e733)

  " Using endfunc with :if
  call assert_fails('exe "if 1 | endfunc | endif"', 'E193:')

  " Missing 'in' in a :for statement
  call assert_fails('for i range(1) | endfor', 'E690:')

  " Incorrect number of variables in for
  call assert_fails('for [i,] in range(3) | endfor', 'E475:')
endfunc

" Test for deep nesting of if/for/while/try statements              {{{1
func Test_deep_nest()
  CheckRunVimInTerminal

  let lines =<< trim [SCRIPT]
    " Deep nesting of if ... endif
    func Test1()
      let @a = join(repeat(['if v:true'], 51), "\n")
      let @a ..= "\n"
      let @a ..= join(repeat(['endif'], 51), "\n")
      @a
      let @a = ''
    endfunc

    " Deep nesting of for ... endfor
    func Test2()
      let @a = join(repeat(['for i in [1]'], 51), "\n")
      let @a ..= "\n"
      let @a ..= join(repeat(['endfor'], 51), "\n")
      @a
      let @a = ''
    endfunc

    " Deep nesting of while ... endwhile
    func Test3()
      let @a = join(repeat(['while v:true'], 51), "\n")
      let @a ..= "\n"
      let @a ..= join(repeat(['endwhile'], 51), "\n")
      @a
      let @a = ''
    endfunc

    " Deep nesting of try ... endtry
    func Test4()
      let @a = join(repeat(['try'], 51), "\n")
      let @a ..= "\necho v:true\n"
      let @a ..= join(repeat(['endtry'], 51), "\n")
      @a
      let @a = ''
    endfunc

    " Deep nesting of function ... endfunction
    func Test5()
      let @a = join(repeat(['function X()'], 51), "\n")
      let @a ..= "\necho v:true\n"
      let @a ..= join(repeat(['endfunction'], 51), "\n")
      @a
      let @a = ''
    endfunc
  [SCRIPT]
  call writefile(lines, 'Xscript', 'D')

  let buf = RunVimInTerminal('-S Xscript', {'rows': 6})

  " Deep nesting of if ... endif
  call term_sendkeys(buf, ":call Test1()\n")
  call TermWait(buf)
  call WaitForAssert({-> assert_match('^E579:', term_getline(buf, 5))})

  " Deep nesting of for ... endfor
  call term_sendkeys(buf, ":call Test2()\n")
  call TermWait(buf)
  call WaitForAssert({-> assert_match('^E585:', term_getline(buf, 5))})

  " Deep nesting of while ... endwhile
  call term_sendkeys(buf, ":call Test3()\n")
  call TermWait(buf)
  call WaitForAssert({-> assert_match('^E585:', term_getline(buf, 5))})

  " Deep nesting of try ... endtry
  call term_sendkeys(buf, ":call Test4()\n")
  call TermWait(buf)
  call WaitForAssert({-> assert_match('^E601:', term_getline(buf, 5))})

  " Deep nesting of function ... endfunction
  call term_sendkeys(buf, ":call Test5()\n")
  call TermWait(buf)
  call WaitForAssert({-> assert_match('^E1058:', term_getline(buf, 4))})
  call term_sendkeys(buf, "\<C-C>\n")
  call TermWait(buf)

  "let l = ''
  "for i in range(1, 6)
  "  let l ..= term_getline(buf, i) . "\n"
  "endfor
  "call assert_report(l)

  call StopVimInTerminal(buf)
endfunc

" Test for errors in converting to float from various types         {{{1
func Test_float_conversion_errors()
  call assert_fails('let x = 4.0 % 2.0', 'E804:')
  call assert_fails('echo 1.1[0]', 'E806:')
  call assert_fails('echo sort([function("min"), 1], "f")', 'E891:')
  call assert_fails('echo 3.2 == "vim"', 'E892:')
  call assert_fails('echo sort([[], 1], "f")', 'E893:')
  call assert_fails('echo sort([{}, 1], "f")', 'E894:')
  call assert_fails('echo 3.2 == v:true', 'E362:')
  call assert_fails('echo 3.2 == v:none', 'E907:')
endfunc

" invalid function names               {{{1
func Test_invalid_function_names()
  " function name not starting with capital
  let caught_e128 = 0
  try
    func! g:test()
      echo "test"
    endfunc
  catch /E128:/
    let caught_e128 = 1
  endtry
  call assert_equal(1, caught_e128)

  " function name includes a colon
  let caught_e884 = 0
  try
    func! b:test()
      echo "test"
    endfunc
  catch /E884:/
    let caught_e884 = 1
  endtry
  call assert_equal(1, caught_e884)

  " function name followed by #
  let caught_e128 = 0
  try
    func! test2() "#
      echo "test2"
    endfunc
  catch /E128:/
    let caught_e128 = 1
  endtry
  call assert_equal(1, caught_e128)

  " function name starting with/without "g:", buffer-local funcref.
  function! g:Foo(n)
    return 'called Foo(' . a:n . ')'
  endfunction
  let b:my_func = function('Foo')
  call assert_equal('called Foo(1)', b:my_func(1))
  call assert_equal('called Foo(2)', g:Foo(2))
  call assert_equal('called Foo(3)', Foo(3))
  delfunc g:Foo

  " script-local function used in Funcref must exist.
  let lines =<< trim END
    func s:Testje()
      return "foo"
    endfunc
    let Bar = function('s:Testje')
    call assert_equal(0, exists('s:Testje'))
    call assert_equal(1, exists('*s:Testje'))
    call assert_equal(1, exists('Bar'))
    call assert_equal(1, exists('*Bar'))
  END
  call writefile(lines, 'Xscript', 'D')
  source Xscript
endfunc

" substring and variable name              {{{1
func Test_substring_var()
  let str = 'abcdef'
  let n = 3
  call assert_equal('def', str[n:])
  call assert_equal('abcd', str[:n])
  call assert_equal('d', str[n:n])
  unlet n
  let nn = 3
  call assert_equal('def', str[nn:])
  call assert_equal('abcd', str[:nn])
  call assert_equal('d', str[nn:nn])
  unlet nn
  let b:nn = 4
  call assert_equal('ef', str[b:nn:])
  call assert_equal('abcde', str[:b:nn])
  call assert_equal('e', str[b:nn:b:nn])
  unlet b:nn
endfunc

" Test using s: with a typed command              {{{1
func Test_typed_script_var()
  CheckRunVimInTerminal

  let buf = RunVimInTerminal('', {'rows': 6})

  " Deep nesting of if ... endif
  call term_sendkeys(buf, ":echo get(s:, 'foo', 'x')\n")
  call TermWait(buf)
  call WaitForAssert({-> assert_match('^E116:', term_getline(buf, 5))})

  call StopVimInTerminal(buf)
endfunc

" Test for issue6776              {{{1
func Test_ternary_expression()
  try
    call eval('0 ? 0')
  catch
  endtry
  " previous failure should not cause next expression to fail
  call assert_equal(v:false, eval(string(v:false)))

  try
    call eval('0 ? "burp')
  catch
  endtry
  " previous failure should not cause next expression to fail
  call assert_equal(v:false, eval(string(v:false)))

  try
    call eval('1 ? 0 : "burp')
  catch
  endtry
  " previous failure should not cause next expression to fail
  call assert_equal(v:false, eval(string(v:false)))
endfunction

func Test_for_over_string()
  let res = ''
  for c in 'aéc̀d'
    let res ..= c .. '-'
  endfor
  call assert_equal('a-é-c̀-d-', res)

  let res = ''
  for c in ''
    let res ..= c .. '-'
  endfor
  call assert_equal('', res)

  let res = ''
  for c in test_null_string()
    let res ..= c .. '-'
  endfor
  call assert_equal('', res)
endfunc

" Test for deeply nested :source command  {{{1
func Test_deeply_nested_source()
  let lines =<< trim END

      so
      sil 0scr
      delete
      so
      0
  END
  call writefile(["vim9 silent! @0 \n/"] + lines, 'Xnested.vim', 'D')

  " this must not crash
  let cmd = GetVimCommand() .. " -e -s -S Xnested.vim -c qa!"
  call system(cmd)
endfunc

"-------------------------------------------------------------------------------
" Modelines								    {{{1
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
"-------------------------------------------------------------------------------