| " Test try-catch-finally exception handling |
| " Most of this was formerly in test49. |
| |
| import './util/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 |