Mercurial > vim
diff src/testdir/test_trycatch.vim @ 32670:695b50472e85
Fix line endings issue
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Mon, 26 Jun 2023 13:13:12 +0200 |
parents | 448aef880252 |
children |
line wrap: on
line diff
--- a/src/testdir/test_trycatch.vim +++ b/src/testdir/test_trycatch.vim @@ -1,2381 +1,2381 @@ -" 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 +" 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