summaryrefslogtreecommitdiffstats
path: root/src/testdir/test_trycatch.vim
diff options
context:
space:
mode:
Diffstat (limited to 'src/testdir/test_trycatch.vim')
-rw-r--r--src/testdir/test_trycatch.vim2351
1 files changed, 2351 insertions, 0 deletions
diff --git a/src/testdir/test_trycatch.vim b/src/testdir/test_trycatch.vim
new file mode 100644
index 0000000..9d62084
--- /dev/null
+++ b/src/testdir/test_trycatch.vim
@@ -0,0 +1,2351 @@
+" 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 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