view src/testdir/test_trycatch.vim @ 33776:9503dc55b5ed v9.0.2108

patch 9.0.2108: [security]: overflow with count for :s command Commit: https://github.com/vim/vim/commit/ac63787734fda2e294e477af52b3bd601517fa78 Author: Christian Brabandt <cb@256bit.org> Date: Tue Nov 14 20:45:48 2023 +0100 patch 9.0.2108: [security]: overflow with count for :s command Problem: [security]: overflow with count for :s command Solution: Abort the :s command if the count is too large If the count after the :s command is larger than what fits into a (signed) long variable, abort with e_value_too_large. Adds a test with INT_MAX as count and verify it correctly fails. It seems the return value on Windows using mingw compiler wraps around, so the initial test using :s/./b/9999999999999999999999999990 doesn't fail there, since the count is wrapping around several times and finally is no longer larger than 2147483647. So let's just use 2147483647 in the test, which hopefully will always cause a failure Signed-off-by: Christian Brabandt <cb@256bit.org>
author Christian Brabandt <cb@256bit.org>
date Thu, 16 Nov 2023 22:15:10 +0100
parents 695b50472e85
children
line wrap: on
line source

" Test try-catch-finally exception handling
" Most of this was formerly in test49.

source check.vim
source shared.vim
import './vim9.vim' as v9

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

com!		   XpathINIT  let g:Xpath = ''
com! -nargs=1 -bar Xpath      let g:Xpath = g:Xpath . <args>

" Test 25:  Executing :finally clauses on normal control flow		    {{{1
"
"	    Control flow in a :try conditional should always fall through to its
"	    :finally clause.  A :finally clause of a :try conditional inside an
"	    inactive conditional should never be executed.
"-------------------------------------------------------------------------------

func T25_F()
  let loops = 3
  while loops > 0
    Xpath 'a' . loops
    if loops >= 2
      try
        Xpath 'b' . loops
        if loops == 2
          try
            Xpath 'c' . loops
          finally
            Xpath 'd' . loops
          endtry
        endif
      finally
        Xpath 'e' . loops
        if loops == 2
          try
            Xpath 'f' . loops
          final
            Xpath 'g' . loops
          endtry
        endif
      endtry
    endif
    Xpath 'h' . loops
    let loops = loops - 1
  endwhile
  Xpath 'i'
endfunc

" Also try using "fina" and "final" and "finall" as abbreviations.
func T25_G()
  if 1
    try
      Xpath 'A'
      call T25_F()
      Xpath 'B'
    fina
      Xpath 'C'
    endtry
  else
    try
      Xpath 'D'
    finall
      Xpath 'E'
    endtry
  endif
endfunc

func Test_finally()
  XpathINIT
  call T25_G()
  call assert_equal('Aa3b3e3h3a2b2c2d2e2f2g2h2a1h1iBC', g:Xpath)
endfunc


"-------------------------------------------------------------------------------
" Test 26:  Executing :finally clauses after :continue or :break	    {{{1
"
"	    For a :continue or :break dynamically enclosed in a :try/:endtry
"	    region inside the next surrounding :while/:endwhile, if the
"	    :continue/:break is before the :finally, the :finally clause is
"	    executed first.  If the :continue/:break is after the :finally, the
"	    :finally clause is broken (like an :if/:endif region).
"-------------------------------------------------------------------------------

func T26_F()
  try
    let loops = 3
    while loops > 0
      try
        try
          if loops == 2
            Xpath 'a' . loops
            let loops = loops - 1
            continue
          elseif loops == 1
            Xpath 'b' . loops
            break
            finish
          endif
          Xpath 'c' . loops
        endtry
      finally
        Xpath 'd' . loops
      endtry
      Xpath 'e' . loops
      let loops = loops - 1
    endwhile
    Xpath 'f'
  finally
    Xpath 'g'
    let loops = 3
    while loops > 0
      try
      finally
        try
          if loops == 2
            Xpath 'h' . loops
            let loops = loops - 1
            continue
          elseif loops == 1
            Xpath 'i' . loops
            break
            finish
          endif
        endtry
        Xpath 'j' . loops
      endtry
      Xpath 'k' . loops
      let loops = loops - 1
    endwhile
    Xpath 'l'
  endtry
  Xpath 'm'
endfunc

func Test_finally_after_continue()
  XpathINIT
  call T26_F()
  call assert_equal('c3d3e3a2d1b1d1fgj3k3h2i1lm', g:Xpath)
endfunc


"-------------------------------------------------------------------------------
" Test 32:  Remembering the :return value on :finally			    {{{1
"
"	    If a :finally clause is executed due to a :return specifying
"	    a value, this is the value visible to the caller if not overwritten
"	    by a new :return in the :finally clause.  A :return without a value
"	    in the :finally clause overwrites with value 0.
"-------------------------------------------------------------------------------

func T32_F()
  try
    Xpath 'a'
    try
      Xpath 'b'
      return "ABCD"
      Xpath 'c'
    finally
      Xpath 'd'
    endtry
    Xpath 'e'
  finally
    Xpath 'f'
  endtry
  Xpath 'g'
endfunc

func T32_G()
  try
    Xpath 'h'
    return 8
    Xpath 'i'
  finally
    Xpath 'j'
    return 16 + strlen(T32_F())
    Xpath 'k'
  endtry
  Xpath 'l'
endfunc

func T32_H()
  try
    Xpath 'm'
    return 32
    Xpath 'n'
  finally
    Xpath 'o'
    return
    Xpath 'p'
  endtry
  Xpath 'q'
endfunc

func T32_I()
  try
    Xpath 'r'
  finally
    Xpath 's'
    return T32_G() + T32_H() + 64
    Xpath 't'
  endtry
  Xpath 'u'
endfunc

func Test_finally_return()
  XpathINIT
  call assert_equal(84, T32_I())
  call assert_equal('rshjabdfmo', g:Xpath)
endfunc

"-------------------------------------------------------------------------------
" Test 33:  :return under :execute or user command and :finally		    {{{1
"
"	    A :return command may be executed under an ":execute" or from
"	    a user command.  Executing of :finally clauses and passing through
"	    the return code works also then.
"-------------------------------------------------------------------------------

func T33_F()
  try
    RETURN 10
    Xpath 'a'
  finally
    Xpath 'b'
  endtry
  Xpath 'c'
endfunc

func T33_G()
  try
    RETURN 20
    Xpath 'd'
  finally
    Xpath 'e'
    RETURN 30
    Xpath 'f'
  endtry
  Xpath 'g'
endfunc

func T33_H()
  try
    execute "try | return 40 | finally | return 50 | endtry"
    Xpath 'h'
  finally
    Xpath 'i'
  endtry
  Xpath 'j'
endfunc

func T33_I()
  try
    execute "try | return 60 | finally | return 70 | endtry"
    Xpath 'k'
  finally
    Xpath 'l'
    execute "try | return 80 | finally | return 90 | endtry"
    Xpath 'm'
  endtry
  Xpath 'n'
endfunc

func T33_J()
  try
    RETURN 100
    Xpath 'o'
  finally
    Xpath 'p'
    return
    Xpath 'q'
  endtry
  Xpath 'r'
endfunc

func T33_K()
  try
    execute "try | return 110 | finally | return 120 | endtry"
    Xpath 's'
  finally
    Xpath 't'
    execute "try | return 130 | finally | return | endtry"
    Xpath 'u'
  endtry
  Xpath 'v'
endfunc

func T33_L()
  try
    return
    Xpath 'w'
  finally
    Xpath 'x'
    RETURN 140
    Xpath 'y'
  endtry
  Xpath 'z'
endfunc

func T33_M()
  try
    return
    Xpath 'A'
  finally
    Xpath 'B'
    execute "try | return 150 | finally | return 160 | endtry"
    Xpath 'C'
  endtry
  Xpath 'D'
endfunc

func T33_N()
  RETURN 170
endfunc

func T33_O()
  execute "try | return 180 | finally | return 190 | endtry"
endfunc

func Test_finally_cmd_return()
  command! -nargs=? RETURN
        \ try | return <args> | finally | return <args> * 2 | endtry
  XpathINIT
  call assert_equal(20, T33_F())
  call assert_equal(60, T33_G())
  call assert_equal(50, T33_H())
  call assert_equal(90, T33_I())
  call assert_equal(0, T33_J())
  call assert_equal(0, T33_K())
  call assert_equal(280, T33_L())
  call assert_equal(160, T33_M())
  call assert_equal(340, T33_N())
  call assert_equal(190, T33_O())
  call assert_equal('beilptxB', g:Xpath)
  delcommand RETURN
endfunc


"-------------------------------------------------------------------------------
" Test 41:  Skipped :throw finding next command				    {{{1
"
"	    A :throw in an inactive conditional must not hide a following
"	    command.
"-------------------------------------------------------------------------------

func T41_F()
  Xpath 'a'
  if 0 | throw 'never' | endif | Xpath 'b'
  Xpath 'c'
endfunc

func T41_G()
  Xpath 'd'
  while 0 | throw 'never' | endwhile | Xpath 'e'
  Xpath 'f'
endfunc

func T41_H()
  Xpath 'g'
  if 0 | try | throw 'never' | endtry | endif | Xpath 'h'
  Xpath 'i'
endfunc

func Test_throw_inactive_cond()
  XpathINIT
  try
    Xpath 'j'
    call T41_F()
    Xpath 'k'
  catch /.*/
    Xpath 'l'
    call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint)
  endtry

  try
    Xpath 'm'
    call T41_G()
    Xpath 'n'
  catch /.*/
    Xpath 'o'
    call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint)
  endtry

  try
    Xpath 'p'
    call T41_H()
    Xpath 'q'
  catch /.*/
    Xpath 'r'
    call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint)
  endtry

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


"-------------------------------------------------------------------------------
" Test 42:  Catching number and string exceptions			    {{{1
"
"	    When a number is thrown, it is converted to a string exception.
"	    Numbers and strings may be caught by specifying a regular exception
"	    as argument to the :catch command.
"-------------------------------------------------------------------------------


func T42_F()
  try

    try
      Xpath 'a'
      throw 4711
      Xpath 'b'
    catch /4711/
      Xpath 'c'
    endtry

    try
      Xpath 'd'
      throw 4711
      Xpath 'e'
    catch /^4711$/
      Xpath 'f'
    endtry

    try
      Xpath 'g'
      throw 4711
      Xpath 'h'
    catch /\d/
      Xpath 'i'
    endtry

    try
      Xpath 'j'
      throw 4711
      Xpath 'k'
    catch /^\d\+$/
      Xpath 'l'
    endtry

    try
      Xpath 'm'
      throw "arrgh"
      Xpath 'n'
    catch /arrgh/
      Xpath 'o'
    endtry

    try
      Xpath 'p'
      throw "arrgh"
      Xpath 'q'
    catch /^arrgh$/
      Xpath 'r'
    endtry

    try
      Xpath 's'
      throw "arrgh"
      Xpath 't'
    catch /\l/
      Xpath 'u'
    endtry

    try
      Xpath 'v'
      throw "arrgh"
      Xpath 'w'
    catch /^\l\+$/
      Xpath 'x'
    endtry

    try
      try
        Xpath 'y'
        throw "ARRGH"
        Xpath 'z'
      catch /^arrgh$/
        Xpath 'A'
      endtry
    catch /^\carrgh$/
      Xpath 'B'
    endtry

    try
      Xpath 'C'
      throw ""
      Xpath 'D'
    catch /^$/
      Xpath 'E'
    endtry

  catch /.*/
    Xpath 'F'
    call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint)
  endtry
endfunc

func Test_catch_number_string()
  XpathINIT
  call T42_F()
  call assert_equal('acdfgijlmoprsuvxyBCE', g:Xpath)
endfunc


"-------------------------------------------------------------------------------
" Test 43:  Selecting the correct :catch clause				    {{{1
"
"	    When an exception is thrown and there are multiple :catch clauses,
"	    the first matching one is taken.
"-------------------------------------------------------------------------------

func T43_F()
  let loops = 3
  while loops > 0
    try
      if loops == 3
        Xpath 'a' . loops
        throw "a"
        Xpath 'b' . loops
      elseif loops == 2
        Xpath 'c' . loops
        throw "ab"
        Xpath 'd' . loops
      elseif loops == 1
        Xpath 'e' . loops
        throw "abc"
        Xpath 'f' . loops
      endif
    catch /abc/
      Xpath 'g' . loops
    catch /ab/
      Xpath 'h' . loops
    catch /.*/
      Xpath 'i' . loops
    catch /a/
      Xpath 'j' . loops
    endtry

    let loops = loops - 1
  endwhile
  Xpath 'k'
endfunc

func Test_multi_catch()
  XpathINIT
  call T43_F()
  call assert_equal('a3i3c2h2e1g1k', g:Xpath)
endfunc


"-------------------------------------------------------------------------------
" Test 44:  Missing or empty :catch patterns				    {{{1
"
"	    A missing or empty :catch pattern means the same as /.*/, that is,
"	    catches everything.  To catch only empty exceptions, /^$/ must be
"	    used.  A :catch with missing, empty, or /.*/ argument also works
"	    when followed by another command separated by a bar on the same
"	    line.  :catch patterns cannot be specified between ||.  But other
"	    pattern separators can be used instead of //.
"-------------------------------------------------------------------------------

func T44_F()
  try
    try
      Xpath 'a'
      throw ""
    catch /^$/
      Xpath 'b'
    endtry

    try
      Xpath 'c'
      throw ""
    catch /.*/
      Xpath 'd'
    endtry

    try
      Xpath 'e'
      throw ""
    catch //
      Xpath 'f'
    endtry

    try
      Xpath 'g'
      throw ""
    catch
      Xpath 'h'
    endtry

    try
      Xpath 'i'
      throw "oops"
    catch /^$/
      Xpath 'j'
    catch /.*/
      Xpath 'k'
    endtry

    try
      Xpath 'l'
      throw "arrgh"
    catch /^$/
      Xpath 'm'
    catch //
      Xpath 'n'
    endtry

    try
      Xpath 'o'
      throw "brrr"
    catch /^$/
      Xpath 'p'
    catch
      Xpath 'q'
    endtry

    try | Xpath 'r' | throw "x" | catch /.*/ | Xpath 's' | endtry

    try | Xpath 't' | throw "y" | catch // | Xpath 'u' | endtry

    while 1
      try
        let caught = 0
        let v:errmsg = ""
        " Extra try level:  if ":catch" without arguments below raises
        " a syntax error because it misinterprets the "Xpath" as a pattern,
        " let it be caught by the ":catch /.*/" below.
        try
          try | Xpath 'v' | throw "z" | catch | Xpath 'w' | :
          endtry
        endtry
      catch /.*/
        let caught = 1
        call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint)
      finally
        if $VIMNOERRTHROW && v:errmsg != ""
          call assert_report(v:errmsg)
        endif
        if caught || $VIMNOERRTHROW && v:errmsg != ""
          Xpath 'x'
        endif
        break		" discard error for $VIMNOERRTHROW
      endtry
    endwhile

    let cologne = 4711
    try
      try
        Xpath 'y'
        throw "throw cologne"
        " Next lines catches all and throws 4711:
      catch |throw cologne|
        Xpath 'z'
      endtry
    catch /4711/
      Xpath 'A'
    endtry

    try
      Xpath 'B'
      throw "plus"
    catch +plus+
      Xpath 'C'
    endtry

    Xpath 'D'
  catch /.*/
    Xpath 'E'
    call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint)
  endtry
endfunc

func Test_empty_catch()
  XpathINIT
  call T44_F()
  call assert_equal('abcdefghiklnoqrstuvwyABCD', g:Xpath)
endfunc


"-------------------------------------------------------------------------------
" Test 45:  Catching exceptions from nested :try blocks			    {{{1
"
"	    When :try blocks are nested, an exception is caught by the innermost
"	    try conditional that has a matching :catch clause.
"-------------------------------------------------------------------------------

func T45_F()
  let loops = 3
  while loops > 0
    try
      try
        try
          try
            if loops == 3
              Xpath 'a' . loops
              throw "a"
              Xpath 'b' . loops
            elseif loops == 2
              Xpath 'c' . loops
              throw "ab"
              Xpath 'd' . loops
            elseif loops == 1
              Xpath 'e' . loops
              throw "abc"
              Xpath 'f' . loops
            endif
          catch /abc/
            Xpath 'g' . loops
          endtry
        catch /ab/
          Xpath 'h' . loops
        endtry
      catch /.*/
        Xpath 'i' . loops
      endtry
    catch /a/
      Xpath 'j' . loops
    endtry

    let loops = loops - 1
  endwhile
  Xpath 'k'
endfunc

func Test_catch_from_nested_try()
  XpathINIT
  call T45_F()
  call assert_equal('a3i3c2h2e1g1k', g:Xpath)
endfunc


"-------------------------------------------------------------------------------
" Test 46:  Executing :finally after a :throw in nested :try		    {{{1
"
"	    When an exception is thrown from within nested :try blocks, the
"	    :finally clauses of the non-catching try conditionals should be
"	    executed before the matching :catch of the next surrounding :try
"	    gets the control.  If this also has a :finally clause, it is
"	    executed afterwards.
"-------------------------------------------------------------------------------

func T46_F()
  let sum = 0

  try
    Xpath 'a'
    try
      Xpath 'b'
      try
        Xpath 'c'
        try
          Xpath 'd'
          throw "ABC"
          Xpath 'e'
        catch /xyz/
          Xpath 'f'
        finally
          Xpath 'g'
          if sum != 0
            Xpath 'h'
          endif
          let sum = sum + 1
        endtry
        Xpath 'i'
      catch /123/
        Xpath 'j'
      catch /321/
        Xpath 'k'
      finally
        Xpath 'l'
        if sum != 1
          Xpath 'm'
        endif
        let sum = sum + 2
      endtry
      Xpath 'n'
    finally
      Xpath 'o'
      if sum != 3
        Xpath 'p'
      endif
      let sum = sum + 4
    endtry
    Xpath 'q'
  catch /ABC/
    Xpath 'r'
    if sum != 7
      Xpath 's'
    endif
    let sum = sum + 8
  finally
    Xpath 't'
    if sum != 15
      Xpath 'u'
    endif
    let sum = sum + 16
  endtry
  Xpath 'v'
  if sum != 31
    Xpath 'w'
  endif
endfunc

func Test_finally_after_throw()
  XpathINIT
  call T46_F()
  call assert_equal('abcdglortv', g:Xpath)
endfunc


"-------------------------------------------------------------------------------
" Test 47:  Throwing exceptions from a :catch clause			    {{{1
"
"	    When an exception is thrown from a :catch clause, it should not be
"	    caught by a :catch of the same :try conditional.  After executing
"	    the :finally clause (if present), surrounding try conditionals
"	    should be checked for a matching :catch.
"-------------------------------------------------------------------------------

func T47_F()
  Xpath 'a'
  try
    Xpath 'b'
    try
      Xpath 'c'
      try
        Xpath 'd'
        throw "x1"
        Xpath 'e'
      catch /x1/
        Xpath 'f'
        try
          Xpath 'g'
          throw "x2"
          Xpath 'h'
        catch /x1/
          Xpath 'i'
        catch /x2/
          Xpath 'j'
          try
            Xpath 'k'
            throw "x3"
            Xpath 'l'
          catch /x1/
            Xpath 'm'
          catch /x2/
            Xpath 'n'
          finally
            Xpath 'o'
          endtry
          Xpath 'p'
        catch /x3/
          Xpath 'q'
        endtry
        Xpath 'r'
      catch /x1/
        Xpath 's'
      catch /x2/
        Xpath 't'
      catch /x3/
        Xpath 'u'
      finally
        Xpath 'v'
      endtry
      Xpath 'w'
    catch /x1/
      Xpath 'x'
    catch /x2/
      Xpath 'y'
    catch /x3/
      Xpath 'z'
    endtry
    Xpath 'A'
  catch /.*/
    Xpath 'B'
    call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint)
  endtry
  Xpath 'C'
endfunc

func Test_throw_from_catch()
  XpathINIT
  call T47_F()
  call assert_equal('abcdfgjkovzAC', g:Xpath)
endfunc


"-------------------------------------------------------------------------------
" Test 48:  Throwing exceptions from a :finally clause			    {{{1
"
"	    When an exception is thrown from a :finally clause, it should not be
"	    caught by a :catch of the same :try conditional.  Surrounding try
"	    conditionals should be checked for a matching :catch.  A previously
"	    thrown exception is discarded.
"-------------------------------------------------------------------------------

func T48_F()
  try

    try
      try
        Xpath 'a'
      catch /x1/
        Xpath 'b'
      finally
        Xpath 'c'
        throw "x1"
        Xpath 'd'
      endtry
      Xpath 'e'
    catch /x1/
      Xpath 'f'
    endtry
    Xpath 'g'

    try
      try
        Xpath 'h'
        throw "x2"
        Xpath 'i'
      catch /x2/
        Xpath 'j'
      catch /x3/
        Xpath 'k'
      finally
        Xpath 'l'
        throw "x3"
        Xpath 'm'
      endtry
      Xpath 'n'
    catch /x2/
      Xpath 'o'
    catch /x3/
      Xpath 'p'
    endtry
    Xpath 'q'

    try
      try
        try
          Xpath 'r'
          throw "x4"
          Xpath 's'
        catch /x5/
          Xpath 't'
        finally
          Xpath 'u'
          throw "x5"	" discards 'x4'
          Xpath 'v'
        endtry
        Xpath 'w'
      catch /x4/
        Xpath 'x'
      finally
        Xpath 'y'
      endtry
      Xpath 'z'
    catch /x5/
      Xpath 'A'
    endtry
    Xpath 'B'

  catch /.*/
    Xpath 'C'
    call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint)
  endtry
  Xpath 'D'
endfunc

func Test_throw_from_finally()
  XpathINIT
  call T48_F()
  call assert_equal('acfghjlpqruyABD', g:Xpath)
endfunc


"-------------------------------------------------------------------------------
" Test 51:  Throwing exceptions across :execute and user commands	    {{{1
"
"	    A :throw command may be executed under an ":execute" or from
"	    a user command.
"-------------------------------------------------------------------------------

func T51_F()
  command! -nargs=? THROW1    throw <args> | throw 1
  command! -nargs=? THROW2    try | throw <args> | endtry | throw 2
  command! -nargs=? THROW3    try | throw 3 | catch /3/ | throw <args> | endtry
  command! -nargs=? THROW4    try | throw 4 | finally   | throw <args> | endtry

  try

    try
      try
        Xpath 'a'
        THROW1 "A"
      catch /A/
        Xpath 'b'
      endtry
    catch /1/
      Xpath 'c'
    endtry

    try
      try
        Xpath 'd'
        THROW2 "B"
      catch /B/
        Xpath 'e'
      endtry
    catch /2/
      Xpath 'f'
    endtry

    try
      try
        Xpath 'g'
        THROW3 "C"
      catch /C/
        Xpath 'h'
      endtry
    catch /3/
      Xpath 'i'
    endtry

    try
      try
        Xpath 'j'
        THROW4 "D"
      catch /D/
        Xpath 'k'
      endtry
    catch /4/
      Xpath 'l'
    endtry

    try
      try
        Xpath 'm'
        execute 'throw "E" | throw 5'
      catch /E/
        Xpath 'n'
      endtry
    catch /5/
      Xpath 'o'
    endtry

    try
      try
        Xpath 'p'
        execute 'try | throw "F" | endtry | throw 6'
      catch /F/
        Xpath 'q'
      endtry
    catch /6/
      Xpath 'r'
    endtry

    try
      try
        Xpath 's'
        execute'try | throw 7 | catch /7/ | throw "G" | endtry'
      catch /G/
        Xpath 't'
      endtry
    catch /7/
      Xpath 'u'
    endtry

    try
      try
        Xpath 'v'
        execute 'try | throw 8 | finally   | throw "H" | endtry'
      catch /H/
        Xpath 'w'
      endtry
    catch /8/
      Xpath 'x'
    endtry

  catch /.*/
    Xpath 'y'
    call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint)
  endtry

  Xpath 'z'

  delcommand THROW1
  delcommand THROW2
  delcommand THROW3
  delcommand THROW4
endfunc

func Test_throw_across_commands()
  XpathINIT
  call T51_F()
  call assert_equal('abdeghjkmnpqstvwz', g:Xpath)
endfunc



"-------------------------------------------------------------------------------
" Test 69:  :throw across :if, :elseif, :while				    {{{1
"
"	    On an :if, :elseif, or :while command, an exception might be thrown
"	    during evaluation of the expression to test.  The exception can be
"	    caught by the script.
"-------------------------------------------------------------------------------

func T69_throw(x)
  Xpath 'x'
  throw a:x
endfunc

func Test_throw_ifelsewhile()
  XpathINIT

  try
    try
      Xpath 'a'
      if 111 == T69_throw("if") + 111
        Xpath 'b'
      else
        Xpath 'c'
      endif
      Xpath 'd'
    catch /^if$/
      Xpath 'e'
    catch /.*/
      Xpath 'f'
      call assert_report("if: " . v:exception . " in " . v:throwpoint)
    endtry

    try
      Xpath 'g'
      if v:false
        Xpath 'h'
      elseif 222 == T69_throw("elseif") + 222
        Xpath 'i'
      else
        Xpath 'j'
      endif
      Xpath 'k'
    catch /^elseif$/
      Xpath 'l'
    catch /.*/
      Xpath 'm'
      call assert_report("elseif: " . v:exception . " in " . v:throwpoint)
    endtry

    try
      Xpath 'n'
      while 333 == T69_throw("while") + 333
        Xpath 'o'
        break
      endwhile
      Xpath 'p'
    catch /^while$/
      Xpath 'q'
    catch /.*/
      Xpath 'r'
      call assert_report("while: " .. v:exception .. " in " .. v:throwpoint)
    endtry
  catch /^0$/	    " default return value
    Xpath 's'
    call assert_report(v:throwpoint)
  catch /.*/
    call assert_report(v:exception .. " in " .. v:throwpoint)
    Xpath 't'
  endtry

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


"-------------------------------------------------------------------------------
" Test 70:  :throw across :return or :throw				    {{{1
"
"	    On a :return or :throw command, an exception might be thrown during
"	    evaluation of the expression to return or throw, respectively.  The
"	    exception can be caught by the script.
"-------------------------------------------------------------------------------

let T70_taken = ""

func T70_throw(x, n)
    let g:T70_taken = g:T70_taken . "T" . a:n
    throw a:x
endfunc

func T70_F(x, y, n)
    let g:T70_taken = g:T70_taken . "F" . a:n
    return a:x + T70_throw(a:y, a:n)
endfunc

func T70_G(x, y, n)
    let g:T70_taken = g:T70_taken . "G" . a:n
    throw a:x . T70_throw(a:y, a:n)
    return a:x
endfunc

func Test_throwreturn()
  XpathINIT

  try
    try
      Xpath 'a'
      call T70_F(4711, "return", 1)
      Xpath 'b'
    catch /^return$/
      Xpath 'c'
    catch /.*/
      Xpath 'd'
      call assert_report("return: " .. v:exception .. " in " .. v:throwpoint)
    endtry

    try
      Xpath 'e'
      let var = T70_F(4712, "return-var", 2)
      Xpath 'f'
    catch /^return-var$/
      Xpath 'g'
    catch /.*/
      Xpath 'h'
      call assert_report("return-var: " . v:exception . " in " . v:throwpoint)
    finally
      unlet! var
    endtry

    try
      Xpath 'i'
      throw "except1" . T70_throw("throw1", 3)
      Xpath 'j'
    catch /^except1/
      Xpath 'k'
    catch /^throw1$/
      Xpath 'l'
    catch /.*/
      Xpath 'm'
      call assert_report("throw1: " .. v:exception .. " in " .. v:throwpoint)
    endtry

    try
      Xpath 'n'
      call T70_G("except2", "throw2", 4)
      Xpath 'o'
    catch /^except2/
      Xpath 'p'
    catch /^throw2$/
      Xpath 'q'
    catch /.*/
      Xpath 'r'
      call assert_report("throw2: " .. v:exception .. " in " .. v:throwpoint)
    endtry

    try
      Xpath 's'
      let var = T70_G("except3", "throw3", 5)
      Xpath 't'
    catch /^except3/
      Xpath 'u'
    catch /^throw3$/
      Xpath 'v'
    catch /.*/
      Xpath 'w'
      call assert_report("throw3: " .. v:exception .. " in " .. v:throwpoint)
    finally
      unlet! var
    endtry

    call assert_equal('F1T1F2T2T3G4T4G5T5', g:T70_taken)
    Xpath 'x'
  catch /^0$/	    " default return value
    Xpath 'y'
    call assert_report(v:throwpoint)
  catch /.*/
    Xpath 'z'
    call assert_report('Caught' .. v:exception .. ' in ' .. v:throwpoint)
  endtry

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

"-------------------------------------------------------------------------------
" Test 71:  :throw across :echo variants and :execute			    {{{1
"
"	    On an :echo, :echon, :echomsg, :echoerr, or :execute command, an
"	    exception might be thrown during evaluation of the arguments to
"	    be displayed or executed as a command, respectively.  Any following
"	    arguments are not evaluated, then.  The exception can be caught by
"	    the script.
"-------------------------------------------------------------------------------

let T71_taken = ""

func T71_throw(x, n)
    let g:T71_taken = g:T71_taken . "T" . a:n
    throw a:x
endfunc

func T71_F(n)
    let g:T71_taken = g:T71_taken . "F" . a:n
    return "F" . a:n
endfunc

func Test_throw_echo()
  XpathINIT

  try
    try
      Xpath 'a'
      echo 'echo ' . T71_throw("echo-except", 1) . T71_F(1)
      Xpath 'b'
    catch /^echo-except$/
      Xpath 'c'
    catch /.*/
      Xpath 'd'
      call assert_report("echo: " .. v:exception .. " in " .. v:throwpoint)
    endtry

    try
      Xpath 'e'
      echon "echon " . T71_throw("echon-except", 2) . T71_F(2)
      Xpath 'f'
    catch /^echon-except$/
      Xpath 'g'
    catch /.*/
      Xpath 'h'
      call assert_report('echon: ' . v:exception . ' in ' . v:throwpoint)
    endtry

    try
      Xpath 'i'
      echomsg "echomsg " . T71_throw("echomsg-except", 3) . T71_F(3)
      Xpath 'j'
    catch /^echomsg-except$/
      Xpath 'k'
    catch /.*/
      Xpath 'l'
      call assert_report('echomsg: ' . v:exception . ' in ' . v:throwpoint)
    endtry

    try
      Xpath 'm'
      echoerr "echoerr " . T71_throw("echoerr-except", 4) . T71_F(4)
      Xpath 'n'
    catch /^echoerr-except$/
      Xpath 'o'
    catch /Vim/
      Xpath 'p'
    catch /echoerr/
      Xpath 'q'
    catch /.*/
      Xpath 'r'
      call assert_report('echoerr: ' . v:exception . ' in ' . v:throwpoint)
    endtry

    try
      Xpath 's'
      execute "echo 'execute " . T71_throw("execute-except", 5) . T71_F(5) "'"
      Xpath 't'
    catch /^execute-except$/
      Xpath 'u'
    catch /.*/
      Xpath 'v'
      call assert_report('execute: ' . v:exception . ' in ' . v:throwpoint)
    endtry

    call assert_equal('T1T2T3T4T5', g:T71_taken)
    Xpath 'w'
  catch /^0$/	    " default return value
    Xpath 'x'
    call assert_report(v:throwpoint)
  catch /.*/
    Xpath 'y'
    call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint)
  endtry

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


"-------------------------------------------------------------------------------
" Test 72:  :throw across :let or :unlet				    {{{1
"
"	    On a :let command, an exception might be thrown during evaluation
"	    of the expression to assign.  On an :let or :unlet command, the
"	    evaluation of the name of the variable to be assigned or list or
"	    deleted, respectively, may throw an exception.  Any following
"	    arguments are not evaluated, then.  The exception can be caught by
"	    the script.
"-------------------------------------------------------------------------------

let throwcount = 0

func T72_throw(x)
  let g:throwcount = g:throwcount + 1
  throw a:x
endfunc

let T72_addpath = ''

func T72_addpath(p)
  let g:T72_addpath = g:T72_addpath . a:p
endfunc

func Test_throw_let()
  XpathINIT

  try
    try
      let $VAR = 'old_value'
      Xpath 'a'
      let $VAR = 'let(' . T72_throw('var') . ')'
      Xpath 'b'
    catch /^var$/
      Xpath 'c'
    finally
      call assert_equal('old_value', $VAR)
    endtry

    try
      let @a = 'old_value'
      Xpath 'd'
      let @a = 'let(' . T72_throw('reg') . ')'
      Xpath 'e'
    catch /^reg$/
      try
        Xpath 'f'
        let @A = 'let(' . T72_throw('REG') . ')'
        Xpath 'g'
      catch /^REG$/
        Xpath 'h'
      endtry
    finally
      call assert_equal('old_value', @a)
      call assert_equal('old_value', @A)
    endtry

    try
      let saved_gpath = &g:path
      let saved_lpath = &l:path
      Xpath 'i'
      let &path = 'let(' . T72_throw('opt') . ')'
      Xpath 'j'
    catch /^opt$/
      try
        Xpath 'k'
        let &g:path = 'let(' . T72_throw('gopt') . ')'
        Xpath 'l'
      catch /^gopt$/
        try
          Xpath 'm'
          let &l:path = 'let(' . T72_throw('lopt') . ')'
          Xpath 'n'
        catch /^lopt$/
          Xpath 'o'
        endtry
      endtry
    finally
      call assert_equal(saved_gpath, &g:path)
      call assert_equal(saved_lpath, &l:path)
      let &g:path = saved_gpath
      let &l:path = saved_lpath
    endtry

    unlet! var1 var2 var3

    try
      Xpath 'p'
      let var1 = 'let(' . T72_throw('var1') . ')'
      Xpath 'q'
    catch /^var1$/
      Xpath 'r'
    finally
      call assert_true(!exists('var1'))
    endtry

    try
      let var2 = 'old_value'
      Xpath 's'
      let var2 = 'let(' . T72_throw('var2'). ')'
      Xpath 't'
    catch /^var2$/
      Xpath 'u'
    finally
      call assert_equal('old_value', var2)
    endtry

    try
      Xpath 'v'
      let var{T72_throw('var3')} = 4711
      Xpath 'w'
    catch /^var3$/
      Xpath 'x'
    endtry

    try
      call T72_addpath('T1')
      let var{T72_throw('var4')} var{T72_addpath('T2')} | call T72_addpath('T3')
      call T72_addpath('T4')
    catch /^var4$/
      call T72_addpath('T5')
    endtry

    try
      call T72_addpath('T6')
      unlet var{T72_throw('var5')} var{T72_addpath('T7')}
            \ | call T72_addpath('T8')
      call T72_addpath('T9')
    catch /^var5$/
      call T72_addpath('T10')
    endtry

    call assert_equal('T1T5T6T10', g:T72_addpath)
    call assert_equal(11, g:throwcount)
  catch /.*/
    call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint)
  endtry

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


"-------------------------------------------------------------------------------
" Test 73:  :throw across :function, :delfunction			    {{{1
"
"	    The :function and :delfunction commands may cause an expression
"	    specified in braces to be evaluated.  During evaluation, an
"	    exception might be thrown.  The exception can be caught by the
"	    script.
"-------------------------------------------------------------------------------

let T73_taken = ''

func T73_throw(x, n)
  let g:T73_taken = g:T73_taken . 'T' . a:n
  throw a:x
endfunc

func T73_expr(x, n)
  let g:T73_taken = g:T73_taken . 'E' . a:n
  if a:n % 2 == 0
    call T73_throw(a:x, a:n)
  endif
  return 2 - a:n % 2
endfunc

func Test_throw_func()
  XpathINIT

  try
    try
      " Define function.
      Xpath 'a'
      function! F0()
      endfunction
      Xpath 'b'
      function! F{T73_expr('function-def-ok', 1)}()
      endfunction
      Xpath 'c'
      function! F{T73_expr('function-def', 2)}()
      endfunction
      Xpath 'd'
    catch /^function-def-ok$/
      Xpath 'e'
    catch /^function-def$/
      Xpath 'f'
    catch /.*/
      call assert_report('def: ' . v:exception . ' in ' . v:throwpoint)
    endtry

    try
      " List function.
      Xpath 'g'
      function F0
      Xpath 'h'
      function F{T73_expr('function-lst-ok', 3)}
      Xpath 'i'
      function F{T73_expr('function-lst', 4)}
      Xpath 'j'
    catch /^function-lst-ok$/
      Xpath 'k'
    catch /^function-lst$/
      Xpath 'l'
    catch /.*/
      call assert_report('lst: ' . v:exception . ' in ' . v:throwpoint)
    endtry

    try
      " Delete function
      Xpath 'm'
      delfunction F0
      Xpath 'n'
      delfunction F{T73_expr('function-del-ok', 5)}
      Xpath 'o'
      delfunction F{T73_expr('function-del', 6)}
      Xpath 'p'
    catch /^function-del-ok$/
      Xpath 'q'
    catch /^function-del$/
      Xpath 'r'
    catch /.*/
      call assert_report('del: ' . v:exception . ' in ' . v:throwpoint)
    endtry
    call assert_equal('E1E2T2E3E4T4E5E6T6', g:T73_taken)
  catch /.*/
    call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint)
  endtry

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


"-------------------------------------------------------------------------------
" Test 74:  :throw across builtin functions and commands		    {{{1
"
"	    Some functions like exists(), searchpair() take expression
"	    arguments, other functions or commands like substitute() or
"	    :substitute cause an expression (specified in the regular
"	    expression) to be evaluated.  During evaluation an exception
"	    might be thrown.  The exception can be caught by the script.
"-------------------------------------------------------------------------------

let T74_taken = ""

func T74_throw(x, n)
  let g:T74_taken = g:T74_taken . "T" . a:n
  throw a:x
endfunc

func T74_expr(x, n)
  let g:T74_taken = g:T74_taken . "E" . a:n
  call T74_throw(a:x . a:n, a:n)
  return "EXPR"
endfunc

func T74_skip(x, n)
  let g:T74_taken = g:T74_taken . "S" . a:n . "(" . line(".")
  let theline = getline(".")
  if theline =~ "skip"
    let g:T74_taken = g:T74_taken . "s)"
    return 1
  elseif theline =~ "throw"
    let g:T74_taken = g:T74_taken . "t)"
    call T74_throw(a:x . a:n, a:n)
  else
    let g:T74_taken = g:T74_taken . ")"
    return 0
  endif
endfunc

func T74_subst(x, n)
  let g:T74_taken = g:T74_taken . "U" . a:n . "(" . line(".")
  let theline = getline(".")
  if theline =~ "not"       " T74_subst() should not be called for this line
    let g:T74_taken = g:T74_taken . "n)"
    call T74_throw(a:x . a:n, a:n)
  elseif theline =~ "throw"
    let g:T74_taken = g:T74_taken . "t)"
    call T74_throw(a:x . a:n, a:n)
  else
    let g:T74_taken = g:T74_taken . ")"
    return "replaced"
  endif
endfunc

func Test_throw_builtin_func()
  XpathINIT

  try
    try
      Xpath 'a'
      let result = exists('*{T74_expr("exists", 1)}')
      Xpath 'b'
    catch /^exists1$/
      Xpath 'c'
      try
        let result = exists('{T74_expr("exists", 2)}')
        Xpath 'd'
      catch /^exists2$/
        Xpath 'e'
      catch /.*/
        call assert_report('exists2: ' . v:exception . ' in ' . v:throwpoint)
      endtry
    catch /.*/
      call assert_report('exists1: ' . v:exception . ' in ' . v:throwpoint)
    endtry

    try
      let file = tempname()
      exec "edit" file
      call append(0, [
            \ 'begin',
            \ 'xx',
            \ 'middle 3',
            \ 'xx',
            \ 'middle 5 skip',
            \ 'xx',
            \ 'middle 7 throw',
            \ 'xx',
            \ 'end'])
      normal! gg
      Xpath 'f'
      let result = searchpair("begin", "middle", "end", '',
            \ 'T74_skip("searchpair", 3)')
      Xpath 'g'
      let result = searchpair("begin", "middle", "end", '',
            \ 'T74_skip("searchpair", 4)')
      Xpath 'h'
      let result = searchpair("begin", "middle", "end", '',
            \ 'T74_skip("searchpair", 5)')
      Xpath 'i'
    catch /^searchpair[35]$/
      Xpath 'j'
    catch /^searchpair4$/
      Xpath 'k'
    catch /.*/
      call assert_report('searchpair: ' . v:exception . ' in ' . v:throwpoint)
    finally
      bwipeout!
      call delete(file)
    endtry

    try
      let file = tempname()
      exec "edit" file
      call append(0, [
            \ 'subst 1',
            \ 'subst 2',
            \ 'not',
            \ 'subst 4',
            \ 'subst throw',
            \ 'subst 6'])
      normal! gg
      Xpath 'l'
      1,2substitute/subst/\=T74_subst("substitute", 6)/
      try
        Xpath 'm'
        try
          let v:errmsg = ""
          3substitute/subst/\=T74_subst("substitute", 7)/
        finally
          if v:errmsg != ""
            " If exceptions are not thrown on errors, fake the error
            " exception in order to get the same execution path.
            throw "faked Vim(substitute)"
          endif
        endtry
      catch /Vim(substitute)/	    " Pattern not found ('e' flag missing)
        Xpath 'n'
        3substitute/subst/\=T74_subst("substitute", 8)/e
        Xpath 'o'
      endtry
      Xpath 'p'
      4,6substitute/subst/\=T74_subst("substitute", 9)/
      Xpath 'q'
    catch /^substitute[678]/
      Xpath 'r'
    catch /^substitute9/
      Xpath 's'
    finally
      bwipeout!
      call delete(file)
    endtry

    try
      Xpath 't'
      let var = substitute("sub", "sub", '\=T74_throw("substitute()y", 10)', '')
      Xpath 'u'
    catch /substitute()y/
      Xpath 'v'
    catch /.*/
      call assert_report('substitute()y: ' . v:exception . ' in '
            \ . v:throwpoint)
    endtry

    try
      Xpath 'w'
      let var = substitute("not", "sub", '\=T74_throw("substitute()n", 11)', '')
      Xpath 'x'
    catch /substitute()n/
      Xpath 'y'
    catch /.*/
      call assert_report('substitute()n: ' . v:exception . ' in '
            \ . v:throwpoint)
    endtry

    call assert_equal('E1T1E2T2S3(3)S4(5s)S4(7t)T4U6(1)U6(2)U9(4)U9(5t)T9T10',
          \ g:T74_taken)

  catch /.*/
    call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint)
  endtry

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


"-------------------------------------------------------------------------------
" Test 75:  Errors in builtin functions.				    {{{1
"
"	    On an error in a builtin function called inside a :try/:endtry
"	    region, the evaluation of the expression calling that function and
"	    the command containing that expression are abandoned.  The error can
"	    be caught as an exception.
"
"	    A simple :call of the builtin function is a trivial case.  If the
"	    builtin function is called in the argument list of another function,
"	    no further arguments are evaluated, and the other function is not
"	    executed.  If the builtin function is called from the argument of
"	    a :return command, the :return command is not executed.  If the
"	    builtin function is called from the argument of a :throw command,
"	    the :throw command is not executed.  The evaluation of the
"	    expression calling the builtin function is abandoned.
"-------------------------------------------------------------------------------

func T75_F1(arg1)
  Xpath 'a'
endfunc

func T75_F2(arg1, arg2)
  Xpath 'b'
endfunc

func T75_G()
  Xpath 'c'
endfunc

func T75_H()
  Xpath 'd'
endfunc

func T75_R()
  while 1
    try
      let caught = 0
      let v:errmsg = ""
      Xpath 'e'
      return append(1, "s")
    catch /E21/
      let caught = 1
    catch /.*/
      Xpath 'f'
    finally
      Xpath 'g'
      if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21'
        Xpath 'h'
      endif
      break		" discard error for $VIMNOERRTHROW
    endtry
  endwhile
  Xpath 'i'
endfunc

func Test_builtin_func_error()
  XpathINIT

  try
    set noma	" let append() fail with "E21"

    while 1
      try
        let caught = 0
        let v:errmsg = ""
        Xpath 'j'
        call append(1, "s")
      catch /E21/
        let caught = 1
      catch /.*/
        Xpath 'k'
      finally
        Xpath 'l'
        if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21'
          Xpath 'm'
        endif
        break		" discard error for $VIMNOERRTHROW
      endtry
    endwhile

    while 1
      try
        let caught = 0
        let v:errmsg = ""
        Xpath 'n'
        call T75_F1('x' . append(1, "s"))
      catch /E21/
        let caught = 1
      catch /.*/
        Xpath 'o'
      finally
        Xpath 'p'
        if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21'
          Xpath 'q'
        endif
        break		" discard error for $VIMNOERRTHROW
      endtry
    endwhile

    while 1
      try
        let caught = 0
        let v:errmsg = ""
        Xpath 'r'
        call T75_F2('x' . append(1, "s"), T75_G())
      catch /E21/
        let caught = 1
      catch /.*/
        Xpath 's'
      finally
        Xpath 't'
        if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21'
          Xpath 'u'
        endif
        break		" discard error for $VIMNOERRTHROW
      endtry
    endwhile

    call T75_R()

    while 1
      try
        let caught = 0
        let v:errmsg = ""
        Xpath 'v'
        throw "T" . append(1, "s")
      catch /E21/
        let caught = 1
      catch /^T.*/
        Xpath 'w'
      catch /.*/
        Xpath 'x'
      finally
        Xpath 'y'
        if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21'
          Xpath 'z'
        endif
        break		" discard error for $VIMNOERRTHROW
      endtry
    endwhile

    while 1
      try
        let caught = 0
        let v:errmsg = ""
        Xpath 'A'
        let x = "a"
        let x = x . "b" . append(1, "s") . T75_H()
      catch /E21/
        let caught = 1
      catch /.*/
        Xpath 'B'
      finally
        Xpath 'C'
        if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21'
          Xpath 'D'
        endif
        call assert_equal('a', x)
        break		" discard error for $VIMNOERRTHROW
      endtry
    endwhile
  catch /.*/
    call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint)
  finally
    set ma&
  endtry

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

func Test_reload_in_try_catch()
  call writefile(['x'], 'Xreload', 'D')
  set autoread
  edit Xreload
  tabnew
  call writefile(['xx'], 'Xreload')
  augroup ReLoad
    au FileReadPost Xreload let x = doesnotexist
    au BufReadPost Xreload let x = doesnotexist
  augroup END
  try
    edit Xreload
  catch
  endtry
  tabnew

  tabclose
  tabclose
  autocmd! ReLoad
  set noautoread
  bwipe! Xreload
endfunc

" Test for errors with :catch, :throw, :finally                            {{{1
func Test_try_catch_errors()
  call assert_fails('throw |', 'E471:')
  call assert_fails("throw \n ", 'E471:')
  call assert_fails('catch abc', 'E654:')
  call assert_fails('try | let i = 1| finally | catch | endtry', 'E604:')
  call assert_fails('finally', 'E606:')
  call assert_fails('try | finally | finally | endtry', 'E607:')
  call assert_fails('try | for i in range(5) | endif | endtry', 'E580:')
  call assert_fails('try | while v:true | endtry', 'E170:')
  call assert_fails('try | if v:true | endtry', 'E171:')

  " this was using a negative index in cstack[]
  let lines =<< trim END
      try
      for
      if
      endwhile
      if
      finally
  END
  call v9.CheckScriptFailure(lines, 'E690:')

  let lines =<< trim END
      try
      for
      if
      endwhile
      if
      endtry
  END
  call v9.CheckScriptFailure(lines, 'E690:')
endfunc

" Test for verbose messages with :try :catch, and :finally                 {{{1
func Test_try_catch_verbose()
  " This test works only when the language is English
  CheckEnglish

  set verbose=14

  " Test for verbose messages displayed when an exception is caught
  redir => msg
  try
    echo i
  catch /E121:/
  finally
  endtry
  redir END
  let expected = [
        \ 'Exception thrown: Vim(echo):E121: Undefined variable: i', '',
        \ 'Exception caught: Vim(echo):E121: Undefined variable: i', '',
        \ 'Exception finished: Vim(echo):E121: Undefined variable: i']
  call assert_equal(expected, split(msg, "\n"))

  " Test for verbose messages displayed when an exception is discarded
  redir => msg
  try
    try
      throw 'abc'
    finally
      throw 'xyz'
    endtry
  catch
  endtry
  redir END
  let expected = [
        \ 'Exception thrown: abc', '',
        \ 'Exception made pending: abc', '',
        \ 'Exception thrown: xyz', '',
        \ 'Exception discarded: abc', '',
        \ 'Exception caught: xyz', '',
        \ 'Exception finished: xyz']
  call assert_equal(expected, split(msg, "\n"))

  " Test for messages displayed when :throw is resumed after :finally
  redir => msg
  try
    try
      throw 'abc'
    finally
    endtry
  catch
  endtry
  redir END
  let expected = [
        \ 'Exception thrown: abc', '',
        \ 'Exception made pending: abc', '',
        \ 'Exception resumed: abc', '',
        \ 'Exception caught: abc', '',
        \ 'Exception finished: abc']
  call assert_equal(expected, split(msg, "\n"))

  " Test for messages displayed when :break is resumed after :finally
  redir => msg
  for i in range(1)
    try
      break
    finally
    endtry
  endfor
  redir END
  let expected = [':break made pending', '', ':break resumed']
  call assert_equal(expected, split(msg, "\n"))

  " Test for messages displayed when :continue is resumed after :finally
  redir => msg
  for i in range(1)
    try
      continue
    finally
    endtry
  endfor
  redir END
  let expected = [':continue made pending', '', ':continue resumed']
  call assert_equal(expected, split(msg, "\n"))

  " Test for messages displayed when :return is resumed after :finally
  func Xtest()
    try
      return 'vim'
    finally
    endtry
  endfunc
  redir => msg
  call Xtest()
  redir END
  let expected = [
        \ 'calling Xtest()', '',
        \ ':return vim made pending', '',
        \ ':return vim resumed', '',
        \ 'Xtest returning ''vim''', '',
        \ 'continuing in Test_try_catch_verbose']
  call assert_equal(expected, split(msg, "\n"))
  delfunc Xtest

  " Test for messages displayed when :finish is resumed after :finally
  call writefile(['try', 'finish', 'finally', 'endtry'], 'Xscript')
  redir => msg
  source Xscript
  redir END
  let expected = [
        \ ':finish made pending', '',
        \ ':finish resumed', '',
        \ 'finished sourcing Xscript',
        \ 'continuing in Test_try_catch_verbose']
  call assert_equal(expected, split(msg, "\n")[1:])
  call delete('Xscript')

  " Test for messages displayed when a pending :continue is discarded by an
  " exception in a finally handler
  redir => msg
  try
    for i in range(1)
      try
        continue
      finally
        throw 'abc'
      endtry
    endfor
  catch
  endtry
  redir END
  let expected = [
        \ ':continue made pending', '',
        \ 'Exception thrown: abc', '',
        \ ':continue discarded', '',
        \ 'Exception caught: abc', '',
        \ 'Exception finished: abc']
  call assert_equal(expected, split(msg, "\n"))

  set verbose&
endfunc

" Test for throwing an exception from a BufEnter autocmd                   {{{1
func Test_BufEnter_exception()
  augroup bufenter_exception
    au!
    autocmd BufEnter Xfile1 throw 'abc'
  augroup END

  let caught_abc = 0
  try
    sp Xfile1
  catch /^abc/
    let caught_abc = 1
  endtry
  call assert_equal(1, caught_abc)
  call assert_equal(1, winnr('$'))

  augroup bufenter_exception
    au!
  augroup END
  augroup! bufenter_exception
  %bwipe!

  " Test for recursively throwing exceptions in autocmds
  augroup bufenter_exception
    au!
    autocmd BufEnter Xfile1 throw 'bufenter'
    autocmd BufLeave Xfile1 throw 'bufleave'
  augroup END

  let ex_count = 0
  try
    try
      sp Xfile1
    catch /^bufenter/
      let ex_count += 1
    endtry
  catch /^bufleave/
      let ex_count += 10
  endtry
  call assert_equal(10, ex_count)
  call assert_equal(2, winnr('$'))

  augroup bufenter_exception
    au!
  augroup END
  augroup! bufenter_exception
  %bwipe!
endfunc

" Test for using try/catch when lines are joined by "|" or "\n"           {{{1
func Test_try_catch_nextcmd()
  func Throw()
    throw "Failure"
  endfunc

  let lines =<< trim END
    try
      let s:x = Throw()
    catch
      let g:caught = 1
    endtry
  END

  let g:caught = 0
  call execute(lines)
  call assert_equal(1, g:caught)

  let g:caught = 0
  call execute(join(lines, '|'))
  call assert_equal(1, g:caught)

  let g:caught = 0
  call execute(join(lines, "\n"))
  call assert_equal(1, g:caught)

  unlet g:caught
  delfunc Throw
endfunc

" Test for using try/catch in a user command with a failing expression    {{{1
func Test_user_command_try_catch()
  let lines =<< trim END
      function s:throw() abort
        throw 'error'
      endfunction

      command! Execute
      \   try
      \ |   let s:x = s:throw()
      \ | catch
      \ |   let g:caught = 'caught'
      \ | endtry

      let g:caught = 'no'
      Execute
      call assert_equal('caught', g:caught)
  END
  call writefile(lines, 'XtestTryCatch')
  source XtestTryCatch

  call delete('XtestTryCatch')
  unlet g:caught
endfunc

" Test for using throw in a called function with following error    {{{1
func Test_user_command_throw_in_function_call()
  let lines =<< trim END
      function s:get_dict() abort
        throw 'my_error'
      endfunction

      try
        call s:get_dict().foo()
      catch /my_error/
        let caught = 'yes'
      catch
        let caught = v:exception
      endtry
      call assert_equal('yes', caught)
  END
  call writefile(lines, 'XtestThrow')
  source XtestThrow

  call delete('XtestThrow')
  unlet g:caught
endfunc

" Test that after reporting an uncaught exception there is no error for a
" missing :endif
func Test_after_exception_no_endif_error()
  function Throw()
    throw "Failure"
  endfunction

  function Foo()
    if 1
      call Throw()
    endif
  endfunction
  call assert_fails('call Foo()', ['E605:', 'E605:'])
  delfunc Throw
  delfunc Foo
endfunc

" Test for using throw in a called function with following endtry    {{{1
func Test_user_command_function_call_with_endtry()
  let lines =<< trim END
      funct s:throw(msg) abort
        throw a:msg
      endfunc
      func s:main() abort
        try
          try
            throw 'err1'
          catch
            call s:throw('err2') | endtry
          catch
            let s:caught = 'yes'
        endtry
      endfunc

      call s:main()
      call assert_equal('yes', s:caught)
  END
  call writefile(lines, 'XtestThrow', 'D')
  source XtestThrow
endfunc

func ThisWillFail()

endfunc

" This was crashing prior to the fix in 8.2.3478.
func Test_error_in_catch_and_finally()
  let lines =<< trim END
    try
      echo x
    catch
      for l in []
    finally
  END
  call writefile(lines, 'XtestCatchAndFinally', 'D')
  try
    source XtestCatchAndFinally
  catch /E600:/
  endtry
endfunc

" This was causing an illegal memory access
func Test_leave_block_in_endtry_not_called()
  let lines =<< trim END
      vim9script
      try #
      for x in []
      if
      endwhile
      if
      endtry
  END
  call writefile(lines, 'XtestEndtry', 'D')
  try
    source XtestEndtry
  catch /E171:/
  endtry
endfunc

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