Mercurial > vim
diff src/testdir/test_vimscript.vim @ 32669:448aef880252
normalize line endings
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Mon, 26 Jun 2023 09:54:34 +0200 |
parents | f35ea6c38a20 |
children | 695b50472e85 |
line wrap: on
line diff
--- a/src/testdir/test_vimscript.vim +++ b/src/testdir/test_vimscript.vim @@ -1,7534 +1,7534 @@ -" Test various aspects of the Vim script language. -" Most of this was formerly in test49.vim (developed by Servatius Brandt -" <Servatius.Brandt@fujitsu-siemens.com>) - -source check.vim -source shared.vim -source script_util.vim - -"------------------------------------------------------------------------------- -" Test environment {{{1 -"------------------------------------------------------------------------------- - -" Append a message to the "messages" file -func Xout(text) - split messages - $put =a:text - wq -endfunc - -com! -nargs=1 Xout call Xout(<args>) - -" Create a new instance of Vim and run the commands in 'test' and then 'verify' -" The commands in 'test' are expected to store the test results in the Xtest.out -" file. If the test passes successfully, then Xtest.out should be empty. -func RunInNewVim(test, verify) - let init =<< trim END - set cpo-=C " support line-continuation in sourced script - source script_util.vim - XpathINIT - XloopINIT - END - let cleanup =<< trim END - call writefile(v:errors, 'Xtest.out') - qall - END - call writefile(init, 'Xtest.vim', 'D') - call writefile(a:test, 'Xtest.vim', 'a') - call writefile(a:verify, 'Xverify.vim', 'D') - call writefile(cleanup, 'Xverify.vim', 'a') - call RunVim([], [], "-S Xtest.vim -S Xverify.vim") - call assert_equal([], readfile('Xtest.out')) - call delete('Xtest.out') -endfunc - -"------------------------------------------------------------------------------- -" Test 1: :endwhile in function {{{1 -" -" Detect if a broken loop is (incorrectly) reactivated by the -" :endwhile. Use a :return to prevent an endless loop, and make -" this test first to get a meaningful result on an error before other -" tests will hang. -"------------------------------------------------------------------------------- - -func T1_F() - Xpath 'a' - let first = 1 - while 1 - Xpath 'b' - if first - Xpath 'c' - let first = 0 - break - else - Xpath 'd' - return - endif - endwhile -endfunc - -func T1_G() - Xpath 'h' - let first = 1 - while 1 - Xpath 'i' - if first - Xpath 'j' - let first = 0 - break - else - Xpath 'k' - return - endif - if 1 " unmatched :if - endwhile -endfunc - -func Test_endwhile_function() - XpathINIT - call T1_F() - Xpath 'F' - - try - call T1_G() - catch - " Catch missing :endif - call assert_true(v:exception =~ 'E171:') - Xpath 'x' - endtry - Xpath 'G' - - call assert_equal('abcFhijxG', g:Xpath) -endfunc - -"------------------------------------------------------------------------------- -" Test 2: :endwhile in script {{{1 -" -" Detect if a broken loop is (incorrectly) reactivated by the -" :endwhile. Use a :finish to prevent an endless loop, and place -" this test before others that might hang to get a meaningful result -" on an error. -" -" This test executes the bodies of the functions T1_F and T1_G from -" the previous test as script files (:return replaced by :finish). -"------------------------------------------------------------------------------- - -func Test_endwhile_script() - XpathINIT - ExecAsScript T1_F - Xpath 'F' - call DeleteTheScript() - - try - ExecAsScript T1_G - catch - " Catch missing :endif - call assert_true(v:exception =~ 'E171:') - Xpath 'x' - endtry - Xpath 'G' - call DeleteTheScript() - - call assert_equal('abcFhijxG', g:Xpath) -endfunc - -"------------------------------------------------------------------------------- -" Test 3: :if, :elseif, :while, :continue, :break {{{1 -"------------------------------------------------------------------------------- - -func Test_if_while() - XpathINIT - if 1 - Xpath 'a' - let loops = 3 - while loops > -1 " main loop: loops == 3, 2, 1 (which breaks) - if loops <= 0 - let break_err = 1 - let loops = -1 - else - Xpath 'b' . loops - endif - if (loops == 2) - while loops == 2 " dummy loop - Xpath 'c' . loops - let loops = loops - 1 - continue " stop dummy loop - Xpath 'd' . loops - endwhile - continue " continue main loop - Xpath 'e' . loops - elseif (loops == 1) - let p = 1 - while p " dummy loop - Xpath 'f' . loops - let p = 0 - break " break dummy loop - Xpath 'g' . loops - endwhile - Xpath 'h' . loops - unlet p - break " break main loop - Xpath 'i' . loops - endif - if (loops > 0) - Xpath 'j' . loops - endif - while loops == 3 " dummy loop - let loops = loops - 1 - endwhile " end dummy loop - endwhile " end main loop - Xpath 'k' - else - Xpath 'l' - endif - Xpath 'm' - if exists("break_err") - Xpath 'm' - unlet break_err - endif - - unlet loops - - call assert_equal('ab3j3b2c2b1f1h1km', g:Xpath) -endfunc - -" Check double quote after skipped "elseif" does not give error E15 -func Test_skipped_elseif() - if "foo" ==? "foo" - let result = "first" - elseif "foo" ==? "foo" - let result = "second" - endif - call assert_equal('first', result) -endfunc - -"------------------------------------------------------------------------------- -" Test 4: :return {{{1 -"------------------------------------------------------------------------------- - -func T4_F() - if 1 - Xpath 'a' - let loops = 3 - while loops > 0 " 3: 2: 1: - Xpath 'b' . loops - if (loops == 2) - Xpath 'c' . loops - return - Xpath 'd' . loops - endif - Xpath 'e' . loops - let loops = loops - 1 - endwhile - Xpath 'f' - else - Xpath 'g' - endif -endfunc - -func Test_return() - XpathINIT - call T4_F() - Xpath '4' - - call assert_equal('ab3e3b2c24', g:Xpath) -endfunc - - -"------------------------------------------------------------------------------- -" Test 5: :finish {{{1 -" -" This test executes the body of the function T4_F from the previous -" test as a script file (:return replaced by :finish). -"------------------------------------------------------------------------------- - -func Test_finish() - XpathINIT - ExecAsScript T4_F - Xpath '5' - call DeleteTheScript() - - call assert_equal('ab3e3b2c25', g:Xpath) -endfunc - - - -"------------------------------------------------------------------------------- -" Test 6: Defining functions in :while loops {{{1 -" -" Functions can be defined inside other functions. An inner function -" gets defined when the outer function is executed. Functions may -" also be defined inside while loops. Expressions in braces for -" defining the function name are allowed. -" -" The functions are defined when sourcing the script, only the -" resulting path is checked in the test function. -"------------------------------------------------------------------------------- - -XpathINIT - -" The command CALL collects the argument of all its invocations in "calls" -" when used from a function (that is, when the global variable "calls" needs -" the "g:" prefix). This is to check that the function code is skipped when -" the function is defined. For inner functions, do so only if the outer -" function is not being executed. -" -let calls = "" -com! -nargs=1 CALL - \ if !exists("calls") && !exists("outer") | - \ let g:calls = g:calls . <args> | - \ endif - -let i = 0 -while i < 3 - let i = i + 1 - if i == 1 - Xpath 'a' - function! F1(arg) - CALL a:arg - let outer = 1 - - let j = 0 - while j < 1 - Xpath 'b' - let j = j + 1 - function! G1(arg) - CALL a:arg - endfunction - Xpath 'c' - endwhile - endfunction - Xpath 'd' - - continue - endif - - Xpath 'e' . i - function! F{i}(i, arg) - CALL a:arg - let outer = 1 - - if a:i == 3 - Xpath 'f' - endif - let k = 0 - while k < 3 - Xpath 'g' . k - let k = k + 1 - function! G{a:i}{k}(arg) - CALL a:arg - endfunction - Xpath 'h' . k - endwhile - endfunction - Xpath 'i' - -endwhile - -if exists("*G1") - Xpath 'j' -endif -if exists("*F1") - call F1("F1") - if exists("*G1") - call G1("G1") - endif -endif - -if exists("G21") || exists("G22") || exists("G23") - Xpath 'k' -endif -if exists("*F2") - call F2(2, "F2") - if exists("*G21") - call G21("G21") - endif - if exists("*G22") - call G22("G22") - endif - if exists("*G23") - call G23("G23") - endif -endif - -if exists("G31") || exists("G32") || exists("G33") - Xpath 'l' -endif -if exists("*F3") - call F3(3, "F3") - if exists("*G31") - call G31("G31") - endif - if exists("*G32") - call G32("G32") - endif - if exists("*G33") - call G33("G33") - endif -endif - -Xpath 'm' - -let g:test6_result = g:Xpath -let g:test6_calls = calls - -unlet calls -delfunction F1 -delfunction G1 -delfunction F2 -delfunction G21 -delfunction G22 -delfunction G23 -delfunction G31 -delfunction G32 -delfunction G33 - -func Test_defining_functions() - call assert_equal('ade2ie3ibcg0h1g1h2g2h3fg0h1g1h2g2h3m', g:test6_result) - call assert_equal('F1G1F2G21G22G23F3G31G32G33', g:test6_calls) -endfunc - -"------------------------------------------------------------------------------- -" Test 7: Continuing on errors outside functions {{{1 -" -" On an error outside a function, the script processing continues -" at the line following the outermost :endif or :endwhile. When not -" inside an :if or :while, the script processing continues at the next -" line. -"------------------------------------------------------------------------------- - -XpathINIT - -if 1 - Xpath 'a' - while 1 - Xpath 'b' - asdf - Xpath 'c' - break - endwhile | Xpath 'd' - Xpath 'e' -endif | Xpath 'f' -Xpath 'g' - -while 1 - Xpath 'h' - if 1 - Xpath 'i' - asdf - Xpath 'j' - endif | Xpath 'k' - Xpath 'l' - break -endwhile | Xpath 'm' -Xpath 'n' - -asdf -Xpath 'o' - -asdf | Xpath 'p' -Xpath 'q' - -let g:test7_result = g:Xpath - -func Test_error_in_script() - call assert_equal('abghinoq', g:test7_result) -endfunc - -"------------------------------------------------------------------------------- -" Test 8: Aborting and continuing on errors inside functions {{{1 -" -" On an error inside a function without the "abort" attribute, the -" script processing continues at the next line (unless the error was -" in a :return command). On an error inside a function with the -" "abort" attribute, the function is aborted and the script processing -" continues after the function call; the value -1 is returned then. -"------------------------------------------------------------------------------- - -XpathINIT - -func T8_F() - if 1 - Xpath 'a' - while 1 - Xpath 'b' - asdf - Xpath 'c' - asdf | Xpath 'd' - Xpath 'e' - break - endwhile - Xpath 'f' - endif | Xpath 'g' - Xpath 'h' - - while 1 - Xpath 'i' - if 1 - Xpath 'j' - asdf - Xpath 'k' - asdf | Xpath 'l' - Xpath 'm' - endif - Xpath 'n' - break - endwhile | Xpath 'o' - Xpath 'p' - - return novar " returns (default return value 0) - Xpath 'q' - return 1 " not reached -endfunc - -func T8_G() abort - if 1 - Xpath 'r' - while 1 - Xpath 's' - asdf " returns -1 - Xpath 't' - break - endwhile - Xpath 'v' - endif | Xpath 'w' - Xpath 'x' - - return -4 " not reached -endfunc - -func T8_H() abort - while 1 - Xpath 'A' - if 1 - Xpath 'B' - asdf " returns -1 - Xpath 'C' - endif - Xpath 'D' - break - endwhile | Xpath 'E' - Xpath 'F' - - return -4 " not reached -endfunc - -" Aborted functions (T8_G and T8_H) return -1. -let g:test8_sum = (T8_F() + 1) - 4 * T8_G() - 8 * T8_H() -Xpath 'X' -let g:test8_result = g:Xpath - -func Test_error_in_function() - call assert_equal(13, g:test8_sum) - call assert_equal('abcefghijkmnoprsABX', g:test8_result) - - delfunction T8_F - delfunction T8_G - delfunction T8_H -endfunc - - -"------------------------------------------------------------------------------- -" Test 9: Continuing after aborted functions {{{1 -" -" When a function with the "abort" attribute is aborted due to an -" error, the next function back in the call hierarchy without an -" "abort" attribute continues; the value -1 is returned then. -"------------------------------------------------------------------------------- - -XpathINIT - -func F() abort - Xpath 'a' - let result = G() " not aborted - Xpath 'b' - if result != 2 - Xpath 'c' - endif - return 1 -endfunc - -func G() " no abort attribute - Xpath 'd' - if H() != -1 " aborted - Xpath 'e' - endif - Xpath 'f' - return 2 -endfunc - -func H() abort - Xpath 'g' - call I() " aborted - Xpath 'h' - return 4 -endfunc - -func I() abort - Xpath 'i' - asdf " error - Xpath 'j' - return 8 -endfunc - -if F() != 1 - Xpath 'k' -endif - -let g:test9_result = g:Xpath - -delfunction F -delfunction G -delfunction H -delfunction I - -func Test_func_abort() - call assert_equal('adgifb', g:test9_result) -endfunc - - -"------------------------------------------------------------------------------- -" Test 10: :if, :elseif, :while argument parsing {{{1 -" -" A '"' or '|' in an argument expression must not be mixed up with -" a comment or a next command after a bar. Parsing errors should -" be recognized. -"------------------------------------------------------------------------------- - -XpathINIT - -func MSG(enr, emsg) - let english = v:lang == "C" || v:lang =~ '^[Ee]n' - if a:enr == "" - Xout "TODO: Add message number for:" a:emsg - let v:errmsg = ":" . v:errmsg - endif - let match = 1 - if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg) - let match = 0 - if v:errmsg == "" - Xout "Message missing." - else - let v:errmsg = v:errmsg->escape('"') - Xout "Unexpected message:" v:errmsg - endif - endif - return match -endfunc - -if 1 || strlen("\"") | Xpath 'a' - Xpath 'b' -endif -Xpath 'c' - -if 0 -elseif 1 || strlen("\"") | Xpath 'd' - Xpath 'e' -endif -Xpath 'f' - -while 1 || strlen("\"") | Xpath 'g' - Xpath 'h' - break -endwhile -Xpath 'i' - -let v:errmsg = "" -if 1 ||| strlen("\"") | Xpath 'j' - Xpath 'k' -endif -Xpath 'l' -if !MSG('E15', "Invalid expression") - Xpath 'm' -endif - -let v:errmsg = "" -if 0 -elseif 1 ||| strlen("\"") | Xpath 'n' - Xpath 'o' -endif -Xpath 'p' -if !MSG('E15', "Invalid expression") - Xpath 'q' -endif - -let v:errmsg = "" -while 1 ||| strlen("\"") | Xpath 'r' - Xpath 's' - break -endwhile -Xpath 't' -if !MSG('E15', "Invalid expression") - Xpath 'u' -endif - -let g:test10_result = g:Xpath -delfunction MSG - -func Test_expr_parsing() - call assert_equal('abcdefghilpt', g:test10_result) -endfunc - - -"------------------------------------------------------------------------------- -" Test 11: :if, :elseif, :while argument evaluation after abort {{{1 -" -" When code is skipped over due to an error, the boolean argument to -" an :if, :elseif, or :while must not be evaluated. -"------------------------------------------------------------------------------- - -XpathINIT - -let calls = 0 - -func P(num) - let g:calls = g:calls + a:num " side effect on call - return 0 -endfunc - -if 1 - Xpath 'a' - asdf " error - Xpath 'b' - if P(1) " should not be called - Xpath 'c' - elseif !P(2) " should not be called - Xpath 'd' - else - Xpath 'e' - endif - Xpath 'f' - while P(4) " should not be called - Xpath 'g' - endwhile - Xpath 'h' -endif -Xpath 'x' - -let g:test11_calls = calls -let g:test11_result = g:Xpath - -unlet calls -delfunction P - -func Test_arg_abort() - call assert_equal(0, g:test11_calls) - call assert_equal('ax', g:test11_result) -endfunc - - -"------------------------------------------------------------------------------- -" Test 12: Expressions in braces in skipped code {{{1 -" -" In code skipped over due to an error or inactive conditional, -" an expression in braces as part of a variable or function name -" should not be evaluated. -"------------------------------------------------------------------------------- - -XpathINIT - -func NULL() - Xpath 'a' - return 0 -endfunc - -func ZERO() - Xpath 'b' - return 0 -endfunc - -func! F0() - Xpath 'c' -endfunc - -func! F1(arg) - Xpath 'e' -endfunc - -let V0 = 1 - -Xpath 'f' -echo 0 ? F{NULL() + V{ZERO()}}() : 1 - -Xpath 'g' -if 0 - Xpath 'h' - call F{NULL() + V{ZERO()}}() -endif - -Xpath 'i' -if 1 - asdf " error - Xpath 'j' - call F1(F{NULL() + V{ZERO()}}()) -endif - -Xpath 'k' -if 1 - asdf " error - Xpath 'l' - call F{NULL() + V{ZERO()}}() -endif - -let g:test12_result = g:Xpath - -func Test_braces_skipped() - call assert_equal('fgik', g:test12_result) -endfunc - - -"------------------------------------------------------------------------------- -" Test 13: Failure in argument evaluation for :while {{{1 -" -" A failure in the expression evaluation for the condition of a :while -" causes the whole :while loop until the matching :endwhile being -" ignored. Continuation is at the next following line. -"------------------------------------------------------------------------------- - -XpathINIT - -Xpath 'a' -while asdf - Xpath 'b' - while 1 - Xpath 'c' - break - endwhile - Xpath 'd' - break -endwhile -Xpath 'e' - -while asdf | Xpath 'f' | endwhile | Xpath 'g' -Xpath 'h' -let g:test13_result = g:Xpath - -func Test_while_fail() - call assert_equal('aeh', g:test13_result) -endfunc - - -"------------------------------------------------------------------------------- -" Test 14: Failure in argument evaluation for :if {{{1 -" -" A failure in the expression evaluation for the condition of an :if -" does not cause the corresponding :else or :endif being matched to -" a previous :if/:elseif. Neither of both branches of the failed :if -" are executed. -"------------------------------------------------------------------------------- - -XpathINIT - -function! F() - Xpath 'a' - let x = 0 - if x " false - Xpath 'b' - elseif !x " always true - Xpath 'c' - let x = 1 - if g:boolvar " possibly undefined - Xpath 'd' - else - Xpath 'e' - endif - Xpath 'f' - elseif x " never executed - Xpath 'g' - endif - Xpath 'h' -endfunction - -let boolvar = 1 -call F() -Xpath '-' - -unlet boolvar -call F() -let g:test14_result = g:Xpath - -delfunction F - -func Test_if_fail() - call assert_equal('acdfh-acfh', g:test14_result) -endfunc - - -"------------------------------------------------------------------------------- -" Test 15: Failure in argument evaluation for :if (bar) {{{1 -" -" Like previous test, except that the failing :if ... | ... | :endif -" is in a single line. -"------------------------------------------------------------------------------- - -XpathINIT - -function! F() - Xpath 'a' - let x = 0 - if x " false - Xpath 'b' - elseif !x " always true - Xpath 'c' - let x = 1 - if g:boolvar | Xpath 'd' | else | Xpath 'e' | endif - Xpath 'f' - elseif x " never executed - Xpath 'g' - endif - Xpath 'h' -endfunction - -let boolvar = 1 -call F() -Xpath '-' - -unlet boolvar -call F() -let g:test15_result = g:Xpath - -delfunction F - -func Test_if_bar_fail() - call assert_equal('acdfh-acfh', g:test15_result) -endfunc - -"------------------------------------------------------------------------------- -" Test 16: Double :else or :elseif after :else {{{1 -" -" Multiple :elses or an :elseif after an :else are forbidden. -"------------------------------------------------------------------------------- - -func T16_F() abort - if 0 - Xpath 'a' - else - Xpath 'b' - else " aborts function - Xpath 'c' - endif - Xpath 'd' -endfunc - -func T16_G() abort - if 0 - Xpath 'a' - else - Xpath 'b' - elseif 1 " aborts function - Xpath 'c' - else - Xpath 'd' - endif - Xpath 'e' -endfunc - -func T16_H() abort - if 0 - Xpath 'a' - elseif 0 - Xpath 'b' - else - Xpath 'c' - else " aborts function - Xpath 'd' - endif - Xpath 'e' -endfunc - -func T16_I() abort - if 0 - Xpath 'a' - elseif 0 - Xpath 'b' - else - Xpath 'c' - elseif 1 " aborts function - Xpath 'd' - else - Xpath 'e' - endif - Xpath 'f' -endfunc - -func Test_Multi_Else() - XpathINIT - try - call T16_F() - catch /E583:/ - Xpath 'e' - endtry - call assert_equal('be', g:Xpath) - - XpathINIT - try - call T16_G() - catch /E584:/ - Xpath 'f' - endtry - call assert_equal('bf', g:Xpath) - - XpathINIT - try - call T16_H() - catch /E583:/ - Xpath 'f' - endtry - call assert_equal('cf', g:Xpath) - - XpathINIT - try - call T16_I() - catch /E584:/ - Xpath 'g' - endtry - call assert_equal('cg', g:Xpath) -endfunc - -"------------------------------------------------------------------------------- -" Test 17: Nesting of unmatched :if or :endif inside a :while {{{1 -" -" The :while/:endwhile takes precedence in nesting over an unclosed -" :if or an unopened :endif. -"------------------------------------------------------------------------------- - -" While loops inside a function are continued on error. -func T17_F() - let loops = 3 - while loops > 0 - let loops -= 1 - Xpath 'a' . loops - if (loops == 1) - Xpath 'b' . loops - continue - elseif (loops == 0) - Xpath 'c' . loops - break - elseif 1 - Xpath 'd' . loops - " endif missing! - endwhile " :endwhile after :if 1 - Xpath 'e' -endfunc - -func T17_G() - let loops = 2 - while loops > 0 - let loops -= 1 - Xpath 'a' . loops - if 0 - Xpath 'b' . loops - " endif missing - endwhile " :endwhile after :if 0 -endfunc - -func T17_H() - let loops = 2 - while loops > 0 - let loops -= 1 - Xpath 'a' . loops - " if missing! - endif " :endif without :if in while - Xpath 'b' . loops - endwhile -endfunc - -" Error continuation outside a function is at the outermost :endwhile or :endif. -XpathINIT -let v:errmsg = '' -let loops = 2 -while loops > 0 - let loops -= 1 - Xpath 'a' . loops - if 0 - Xpath 'b' . loops - " endif missing! Following :endwhile fails. -endwhile | Xpath 'c' -Xpath 'd' -call assert_match('E171:', v:errmsg) -call assert_equal('a1d', g:Xpath) - -func Test_unmatched_if_in_while() - XpathINIT - call assert_fails('call T17_F()', 'E171:') - call assert_equal('a2d2a1b1a0c0e', g:Xpath) - - XpathINIT - call assert_fails('call T17_G()', 'E171:') - call assert_equal('a1a0', g:Xpath) - - XpathINIT - call assert_fails('call T17_H()', 'E580:') - call assert_equal('a1b1a0b0', g:Xpath) -endfunc - -"------------------------------------------------------------------------------- -" Test 18: Interrupt (Ctrl-C pressed) {{{1 -" -" On an interrupt, the script processing is terminated immediately. -"------------------------------------------------------------------------------- - -func Test_interrupt_while_if() - let test =<< trim [CODE] - try - if 1 - Xpath 'a' - while 1 - Xpath 'b' - if 1 - Xpath 'c' - call interrupt() - call assert_report('should not get here') - break - finish - endif | call assert_report('should not get here') - call assert_report('should not get here') - endwhile | call assert_report('should not get here') - call assert_report('should not get here') - endif | call assert_report('should not get here') - call assert_report('should not get here') - catch /^Vim:Interrupt$/ - Xpath 'd' - endtry | Xpath 'e' - Xpath 'f' - [CODE] - let verify =<< trim [CODE] - call assert_equal('abcdef', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -func Test_interrupt_try() - let test =<< trim [CODE] - try - try - Xpath 'a' - call interrupt() - call assert_report('should not get here') - endtry | call assert_report('should not get here') - call assert_report('should not get here') - catch /^Vim:Interrupt$/ - Xpath 'b' - endtry | Xpath 'c' - Xpath 'd' - [CODE] - let verify =<< trim [CODE] - call assert_equal('abcd', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -func Test_interrupt_func_while_if() - let test =<< trim [CODE] - func F() - if 1 - Xpath 'a' - while 1 - Xpath 'b' - if 1 - Xpath 'c' - call interrupt() - call assert_report('should not get here') - break - return - endif | call assert_report('should not get here') - call assert_report('should not get here') - endwhile | call assert_report('should not get here') - call assert_report('should not get here') - endif | call assert_report('should not get here') - call assert_report('should not get here') - endfunc - - Xpath 'd' - try - call F() | call assert_report('should not get here') - catch /^Vim:Interrupt$/ - Xpath 'e' - endtry | Xpath 'f' - Xpath 'g' - [CODE] - let verify =<< trim [CODE] - call assert_equal('dabcefg', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -func Test_interrupt_func_try() - let test =<< trim [CODE] - func G() - try - Xpath 'a' - call interrupt() - call assert_report('should not get here') - endtry | call assert_report('should not get here') - call assert_report('should not get here') - endfunc - - Xpath 'b' - try - call G() | call assert_report('should not get here') - catch /^Vim:Interrupt$/ - Xpath 'c' - endtry | Xpath 'd' - Xpath 'e' - [CODE] - let verify =<< trim [CODE] - call assert_equal('bacde', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" Test 19: Aborting on errors inside :try/:endtry {{{1 -" -" An error in a command dynamically enclosed in a :try/:endtry region -" aborts script processing immediately. It does not matter whether -" the failing command is outside or inside a function and whether a -" function has an "abort" attribute. -"------------------------------------------------------------------------------- - -func Test_try_error_abort_1() - let test =<< trim [CODE] - func F() abort - Xpath 'a' - asdf - call assert_report('should not get here') - endfunc - - try - Xpath 'b' - call F() - call assert_report('should not get here') - endtry | call assert_report('should not get here') - call assert_report('should not get here') - [CODE] - let verify =<< trim [CODE] - call assert_equal('ba', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -func Test_try_error_abort_2() - let test =<< trim [CODE] - func G() - Xpath 'a' - asdf - call assert_report('should not get here') - endfunc - - try - Xpath 'b' - call G() - call assert_report('should not get here') - endtry | call assert_report('should not get here') - call assert_report('should not get here') - [CODE] - let verify =<< trim [CODE] - call assert_equal('ba', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -func Test_try_error_abort_3() - let test =<< trim [CODE] - try - Xpath 'a' - asdf - call assert_report('should not get here') - endtry | call assert_report('should not get here') - call assert_report('should not get here') - [CODE] - let verify =<< trim [CODE] - call assert_equal('a', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -func Test_try_error_abort_4() - let test =<< trim [CODE] - if 1 - try - Xpath 'a' - asdf - call assert_report('should not get here') - endtry | call assert_report('should not get here') - endif | call assert_report('should not get here') - call assert_report('should not get here') - [CODE] - let verify =<< trim [CODE] - call assert_equal('a', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -func Test_try_error_abort_5() - let test =<< trim [CODE] - let p = 1 - while p - let p = 0 - try - Xpath 'a' - asdf - call assert_report('should not get here') - endtry | call assert_report('should not get here') - endwhile | call assert_report('should not get here') - call assert_report('should not get here') - [CODE] - let verify =<< trim [CODE] - call assert_equal('a', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -func Test_try_error_abort_6() - let test =<< trim [CODE] - let p = 1 - Xpath 'a' - while p - Xpath 'b' - let p = 0 - try - Xpath 'c' - endwhile | call assert_report('should not get here') - call assert_report('should not get here') - [CODE] - let verify =<< trim [CODE] - call assert_equal('abc', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" Test 20: Aborting on errors after :try/:endtry {{{1 -" -" When an error occurs after the last active :try/:endtry region has -" been left, termination behavior is as if no :try/:endtry has been -" seen. -"------------------------------------------------------------------------------- - -func Test_error_after_try_1() - let test =<< trim [CODE] - let p = 1 - while p - let p = 0 - Xpath 'a' - try - Xpath 'b' - endtry - asdf - call assert_report('should not get here') - endwhile | call assert_report('should not get here') - Xpath 'c' - [CODE] - let verify =<< trim [CODE] - call assert_equal('abc', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -func Test_error_after_try_2() - let test =<< trim [CODE] - while 1 - try - Xpath 'a' - break - call assert_report('should not get here') - endtry - endwhile - Xpath 'b' - asdf - Xpath 'c' - [CODE] - let verify =<< trim [CODE] - call assert_equal('abc', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -func Test_error_after_try_3() - let test =<< trim [CODE] - while 1 - try - Xpath 'a' - break - call assert_report('should not get here') - finally - Xpath 'b' - endtry - endwhile - Xpath 'c' - asdf - Xpath 'd' - [CODE] - let verify =<< trim [CODE] - call assert_equal('abcd', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -func Test_error_after_try_4() - let test =<< trim [CODE] - while 1 - try - Xpath 'a' - finally - Xpath 'b' - break - call assert_report('should not get here') - endtry - endwhile - Xpath 'c' - asdf - Xpath 'd' - [CODE] - let verify =<< trim [CODE] - call assert_equal('abcd', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -func Test_error_after_try_5() - let test =<< trim [CODE] - let p = 1 - while p - let p = 0 - try - Xpath 'a' - continue - call assert_report('should not get here') - endtry - endwhile - Xpath 'b' - asdf - Xpath 'c' - [CODE] - let verify =<< trim [CODE] - call assert_equal('abc', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -func Test_error_after_try_6() - let test =<< trim [CODE] - let p = 1 - while p - let p = 0 - try - Xpath 'a' - continue - call assert_report('should not get here') - finally - Xpath 'b' - endtry - endwhile - Xpath 'c' - asdf - Xpath 'd' - [CODE] - let verify =<< trim [CODE] - call assert_equal('abcd', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -func Test_error_after_try_7() - let test =<< trim [CODE] - let p = 1 - while p - let p = 0 - try - Xpath 'a' - finally - Xpath 'b' - continue - call assert_report('should not get here') - endtry - endwhile - Xpath 'c' - asdf - Xpath 'd' - [CODE] - let verify =<< trim [CODE] - call assert_equal('abcd', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" Test 21: :finally for :try after :continue/:break/:return/:finish {{{1 -" -" If a :try conditional stays inactive due to a preceding :continue, -" :break, :return, or :finish, its :finally clause should not be -" executed. -"------------------------------------------------------------------------------- - -func Test_finally_after_loop_ctrl_statement() - let test =<< trim [CODE] - func F() - let loops = 2 - while loops > 0 - XloopNEXT - let loops = loops - 1 - try - if loops == 1 - Xloop 'a' - continue - call assert_report('should not get here') - elseif loops == 0 - Xloop 'b' - break - call assert_report('should not get here') - endif - - try " inactive - call assert_report('should not get here') - finally - call assert_report('should not get here') - endtry - finally - Xloop 'c' - endtry - call assert_report('should not get here') - endwhile - - try - Xpath 'd' - return - call assert_report('should not get here') - try " inactive - call assert_report('should not get here') - finally - call assert_report('should not get here') - endtry - finally - Xpath 'e' - endtry - call assert_report('should not get here') - endfunc - - try - Xpath 'f' - call F() - Xpath 'g' - finish - call assert_report('should not get here') - try " inactive - call assert_report('should not get here') - finally - call assert_report('should not get here') - endtry - finally - Xpath 'h' - endtry - call assert_report('should not get here') - [CODE] - let verify =<< trim [CODE] - call assert_equal('fa2c2b3c3degh', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" Test 22: :finally for a :try after an error/interrupt/:throw {{{1 -" -" If a :try conditional stays inactive due to a preceding error or -" interrupt or :throw, its :finally clause should not be executed. -"------------------------------------------------------------------------------- - -func Test_finally_after_error_in_func() - let test =<< trim [CODE] - func Error() - try - Xpath 'b' - asdf " aborting error, triggering error exception - call assert_report('should not get here') - endtry - call assert_report('should not get here') - endfunc - - Xpath 'a' - call Error() - call assert_report('should not get here') - - if 1 " not active due to error - try " not active since :if inactive - call assert_report('should not get here') - finally - call assert_report('should not get here') - endtry - endif - - try " not active due to error - call assert_report('should not get here') - finally - call assert_report('should not get here') - endtry - [CODE] - let verify =<< trim [CODE] - call assert_equal('ab', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -func Test_finally_after_interrupt() - let test =<< trim [CODE] - func Interrupt() - try - Xpath 'a' - call interrupt() " triggering interrupt exception - call assert_report('should not get here') - endtry - endfunc - - Xpath 'b' - try - call Interrupt() - catch /^Vim:Interrupt$/ - Xpath 'c' - finish - endtry - call assert_report('should not get here') - - if 1 " not active due to interrupt - try " not active since :if inactive - call assert_report('should not get here') - finally - call assert_report('should not get here') - endtry - endif - - try " not active due to interrupt - call assert_report('should not get here') - finally - call assert_report('should not get here') - endtry - [CODE] - let verify =<< trim [CODE] - call assert_equal('bac', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -func Test_finally_after_throw() - let test =<< trim [CODE] - func Throw() - Xpath 'a' - throw 'xyz' - endfunc - - Xpath 'b' - call Throw() - call assert_report('should not get here') - - if 1 " not active due to :throw - try " not active since :if inactive - call assert_report('should not get here') - finally - call assert_report('should not get here') - endtry - endif - - try " not active due to :throw - call assert_report('should not get here') - finally - call assert_report('should not get here') - endtry - [CODE] - let verify =<< trim [CODE] - call assert_equal('ba', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" Test 23: :catch clauses for a :try after a :throw {{{1 -" -" If a :try conditional stays inactive due to a preceding :throw, -" none of its :catch clauses should be executed. -"------------------------------------------------------------------------------- - -func Test_catch_after_throw() - let test =<< trim [CODE] - try - Xpath 'a' - throw "xyz" - call assert_report('should not get here') - - if 1 " not active due to :throw - try " not active since :if inactive - call assert_report('should not get here') - catch /xyz/ - call assert_report('should not get here') - endtry - endif - catch /xyz/ - Xpath 'b' - endtry - - Xpath 'c' - throw "abc" - call assert_report('should not get here') - - try " not active due to :throw - call assert_report('should not get here') - catch /abc/ - call assert_report('should not get here') - endtry - [CODE] - let verify =<< trim [CODE] - call assert_equal('abc', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" Test 24: :endtry for a :try after a :throw {{{1 -" -" If a :try conditional stays inactive due to a preceding :throw, -" its :endtry should not rethrow the exception to the next surrounding -" active :try conditional. -"------------------------------------------------------------------------------- - -func Test_endtry_after_throw() - let test =<< trim [CODE] - try " try 1 - try " try 2 - Xpath 'a' - throw "xyz" " makes try 2 inactive - call assert_report('should not get here') - - try " try 3 - call assert_report('should not get here') - endtry " no rethrow to try 1 - catch /xyz/ " should catch although try 2 inactive - Xpath 'b' - endtry - catch /xyz/ " try 1 active, but exception already caught - call assert_report('should not get here') - endtry - Xpath 'c' - [CODE] - let verify =<< trim [CODE] - call assert_equal('abc', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" Test 27: Executing :finally clauses after :return {{{1 -" -" For a :return command dynamically enclosed in a :try/:endtry region, -" :finally clauses are executed and the called function is ended. -"------------------------------------------------------------------------------- - -func T27_F() - try - Xpath 'a' - try - Xpath 'b' - return - call assert_report('should not get here') - finally - Xpath 'c' - endtry - Xpath 'd' - finally - Xpath 'e' - endtry - call assert_report('should not get here') -endfunc - -func T27_G() - try - Xpath 'f' - return - call assert_report('should not get here') - finally - Xpath 'g' - call T27_F() - Xpath 'h' - endtry - call assert_report('should not get here') -endfunc - -func T27_H() - try - Xpath 'i' - call T27_G() - Xpath 'j' - finally - Xpath 'k' - return - call assert_report('should not get here') - endtry - call assert_report('should not get here') -endfunction - -func Test_finally_after_return() - XpathINIT - try - Xpath 'l' - call T27_H() - Xpath 'm' - finally - Xpath 'n' - endtry - call assert_equal('lifgabcehjkmn', g:Xpath) -endfunc - -"------------------------------------------------------------------------------- -" Test 28: Executing :finally clauses after :finish {{{1 -" -" For a :finish command dynamically enclosed in a :try/:endtry region, -" :finally clauses are executed and the sourced file is finished. -" -" This test executes the bodies of the functions F, G, and H from the -" previous test as script files (:return replaced by :finish). -"------------------------------------------------------------------------------- - -func Test_finally_after_finish() - XpathINIT - - let scriptF = MakeScript("T27_F") - let scriptG = MakeScript("T27_G", scriptF) - let scriptH = MakeScript("T27_H", scriptG) - - try - Xpath 'A' - exec "source" scriptH - Xpath 'B' - finally - Xpath 'C' - endtry - Xpath 'D' - call assert_equal('AifgabcehjkBCD', g:Xpath) - call delete(scriptF) - call delete(scriptG) - call delete(scriptH) -endfunc - -"------------------------------------------------------------------------------- -" Test 29: Executing :finally clauses on errors {{{1 -" -" After an error in a command dynamically enclosed in a :try/:endtry -" region, :finally clauses are executed and the script processing is -" terminated. -"------------------------------------------------------------------------------- - -func Test_finally_after_error_1() - let test =<< trim [CODE] - func F() - while 1 - try - Xpath 'a' - while 1 - try - Xpath 'b' - asdf " error - call assert_report('should not get here') - finally - Xpath 'c' - endtry | call assert_report('should not get here') - call assert_report('should not get here') - break - endwhile - call assert_report('should not get here') - finally - Xpath 'd' - endtry | call assert_report('should not get here') - call assert_report('should not get here') - break - endwhile - call assert_report('should not get here') - endfunc - - while 1 - try - Xpath 'e' - while 1 - call F() - call assert_report('should not get here') - break - endwhile | call assert_report('should not get here') - call assert_report('should not get here') - finally - Xpath 'f' - endtry | call assert_report('should not get here') - endwhile | call assert_report('should not get here') - call assert_report('should not get here') - [CODE] - let verify =<< trim [CODE] - call assert_equal('eabcdf', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -func Test_finally_after_error_2() - let test =<< trim [CODE] - func G() abort - if 1 - try - Xpath 'a' - asdf " error - call assert_report('should not get here') - finally - Xpath 'b' - endtry | Xpath 'c' - endif | Xpath 'd' - call assert_report('should not get here') - endfunc - - if 1 - try - Xpath 'e' - call G() - call assert_report('should not get here') - finally - Xpath 'f' - endtry | call assert_report('should not get here') - endif | call assert_report('should not get here') - call assert_report('should not get here') - [CODE] - let verify =<< trim [CODE] - call assert_equal('eabf', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" Test 30: Executing :finally clauses on interrupt {{{1 -" -" After an interrupt in a command dynamically enclosed in -" a :try/:endtry region, :finally clauses are executed and the -" script processing is terminated. -"------------------------------------------------------------------------------- - -func Test_finally_on_interrupt() - let test =<< trim [CODE] - func F() - try - Xloop 'a' - call interrupt() - call assert_report('should not get here') - finally - Xloop 'b' - endtry - call assert_report('should not get here') - endfunc - - try - try - Xpath 'c' - try - Xpath 'd' - call interrupt() - call assert_report('should not get here') - finally - Xpath 'e' - try - Xpath 'f' - try - Xpath 'g' - finally - Xpath 'h' - try - Xpath 'i' - call interrupt() - call assert_report('should not get here') - endtry - call assert_report('should not get here') - endtry - call assert_report('should not get here') - endtry - call assert_report('should not get here') - endtry - call assert_report('should not get here') - finally - Xpath 'j' - try - Xpath 'k' - call F() - call assert_report('should not get here') - finally - Xpath 'l' - try - Xpath 'm' - XloopNEXT - ExecAsScript F - call assert_report('should not get here') - finally - Xpath 'n' - endtry - call assert_report('should not get here') - endtry - call assert_report('should not get here') - endtry - call assert_report('should not get here') - catch /^Vim:Interrupt$/ - Xpath 'o' - endtry - [CODE] - let verify =<< trim [CODE] - call assert_equal('cdefghijka1b1lma2b2no', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" Test 31: Executing :finally clauses after :throw {{{1 -" -" After a :throw dynamically enclosed in a :try/:endtry region, -" :finally clauses are executed and the script processing is -" terminated. -"------------------------------------------------------------------------------- - -func Test_finally_after_throw_2() - let test =<< trim [CODE] - func F() - try - Xloop 'a' - throw "exception" - call assert_report('should not get here') - finally - Xloop 'b' - endtry - call assert_report('should not get here') - endfunc - - try - Xpath 'c' - try - Xpath 'd' - throw "exception" - call assert_report('should not get here') - finally - Xpath 'e' - try - Xpath 'f' - try - Xpath 'g' - finally - Xpath 'h' - try - Xpath 'i' - throw "exception" - call assert_report('should not get here') - endtry - call assert_report('should not get here') - endtry - call assert_report('should not get here') - endtry - call assert_report('should not get here') - endtry - call assert_report('should not get here') - finally - Xpath 'j' - try - Xpath 'k' - call F() - call assert_report('should not get here') - finally - Xpath 'l' - try - Xpath 'm' - XloopNEXT - ExecAsScript F - call assert_report('should not get here') - finally - Xpath 'n' - endtry - call assert_report('should not get here') - endtry - call assert_report('should not get here') - endtry - call assert_report('should not get here') - [CODE] - let verify =<< trim [CODE] - call assert_equal('cdefghijka1b1lma2b2n', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" Test 34: :finally reason discarded by :continue {{{1 -" -" When a :finally clause is executed due to a :continue, :break, -" :return, :finish, error, interrupt or :throw, the jump reason is -" discarded by a :continue in the finally clause. -"------------------------------------------------------------------------------- - -func Test_finally_after_continue() - let test =<< trim [CODE] - func C(jump) - XloopNEXT - let loop = 0 - while loop < 2 - let loop = loop + 1 - if loop == 1 - try - if a:jump == "continue" - continue - elseif a:jump == "break" - break - elseif a:jump == "return" || a:jump == "finish" - return - elseif a:jump == "error" - asdf - elseif a:jump == "interrupt" - call interrupt() - let dummy = 0 - elseif a:jump == "throw" - throw "abc" - endif - finally - continue " discards jump that caused the :finally - call assert_report('should not get here') - endtry - call assert_report('should not get here') - elseif loop == 2 - Xloop 'a' - endif - endwhile - endfunc - - call C("continue") - Xpath 'b' - call C("break") - Xpath 'c' - call C("return") - Xpath 'd' - let g:jump = "finish" - ExecAsScript C - unlet g:jump - Xpath 'e' - try - call C("error") - Xpath 'f' - finally - Xpath 'g' - try - call C("interrupt") - Xpath 'h' - finally - Xpath 'i' - call C("throw") - Xpath 'j' - endtry - endtry - Xpath 'k' - [CODE] - let verify =<< trim [CODE] - call assert_equal('a2ba3ca4da5ea6fga7hia8jk', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" Test 35: :finally reason discarded by :break {{{1 -" -" When a :finally clause is executed due to a :continue, :break, -" :return, :finish, error, interrupt or :throw, the jump reason is -" discarded by a :break in the finally clause. -"------------------------------------------------------------------------------- - -func Test_finally_discard_by_break() - let test =<< trim [CODE] - func B(jump) - XloopNEXT - let loop = 0 - while loop < 2 - let loop = loop + 1 - if loop == 1 - try - if a:jump == "continue" - continue - elseif a:jump == "break" - break - elseif a:jump == "return" || a:jump == "finish" - return - elseif a:jump == "error" - asdf - elseif a:jump == "interrupt" - call interrupt() - let dummy = 0 - elseif a:jump == "throw" - throw "abc" - endif - finally - break " discards jump that caused the :finally - call assert_report('should not get here') - endtry - elseif loop == 2 - call assert_report('should not get here') - endif - endwhile - Xloop 'a' - endfunc - - call B("continue") - Xpath 'b' - call B("break") - Xpath 'c' - call B("return") - Xpath 'd' - let g:jump = "finish" - ExecAsScript B - unlet g:jump - Xpath 'e' - try - call B("error") - Xpath 'f' - finally - Xpath 'g' - try - call B("interrupt") - Xpath 'h' - finally - Xpath 'i' - call B("throw") - Xpath 'j' - endtry - endtry - Xpath 'k' - [CODE] - let verify =<< trim [CODE] - call assert_equal('a2ba3ca4da5ea6fga7hia8jk', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" Test 36: :finally reason discarded by :return {{{1 -" -" When a :finally clause is executed due to a :continue, :break, -" :return, :finish, error, interrupt or :throw, the jump reason is -" discarded by a :return in the finally clause. -"------------------------------------------------------------------------------- - -func Test_finally_discard_by_return() - let test =<< trim [CODE] - func R(jump, retval) abort - let loop = 0 - while loop < 2 - let loop = loop + 1 - if loop == 1 - try - if a:jump == "continue" - continue - elseif a:jump == "break" - break - elseif a:jump == "return" - return - elseif a:jump == "error" - asdf - elseif a:jump == "interrupt" - call interrupt() - let dummy = 0 - elseif a:jump == "throw" - throw "abc" - endif - finally - return a:retval " discards jump that caused the :finally - call assert_report('should not get here') - endtry - elseif loop == 2 - call assert_report('should not get here') - endif - endwhile - call assert_report('should not get here') - endfunc - - let sum = -R("continue", -8) - Xpath 'a' - let sum = sum - R("break", -16) - Xpath 'b' - let sum = sum - R("return", -32) - Xpath 'c' - try - let sum = sum - R("error", -64) - Xpath 'd' - finally - Xpath 'e' - try - let sum = sum - R("interrupt", -128) - Xpath 'f' - finally - Xpath 'g' - let sum = sum - R("throw", -256) - Xpath 'h' - endtry - endtry - Xpath 'i' - - let expected = 8 + 16 + 32 + 64 + 128 + 256 - call assert_equal(sum, expected) - [CODE] - let verify =<< trim [CODE] - call assert_equal('abcdefghi', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" Test 37: :finally reason discarded by :finish {{{1 -" -" When a :finally clause is executed due to a :continue, :break, -" :return, :finish, error, interrupt or :throw, the jump reason is -" discarded by a :finish in the finally clause. -"------------------------------------------------------------------------------- - -func Test_finally_discard_by_finish() - let test =<< trim [CODE] - func F(jump) " not executed as function, transformed to a script - let loop = 0 - while loop < 2 - let loop = loop + 1 - if loop == 1 - try - if a:jump == "continue" - continue - elseif a:jump == "break" - break - elseif a:jump == "finish" - finish - elseif a:jump == "error" - asdf - elseif a:jump == "interrupt" - call interrupt() - let dummy = 0 - elseif a:jump == "throw" - throw "abc" - endif - finally - finish " discards jump that caused the :finally - call assert_report('should not get here') - endtry - elseif loop == 2 - call assert_report('should not get here') - endif - endwhile - call assert_report('should not get here') - endfunc - - let scriptF = MakeScript("F") - delfunction F - - let g:jump = "continue" - exec "source" scriptF - Xpath 'a' - let g:jump = "break" - exec "source" scriptF - Xpath 'b' - let g:jump = "finish" - exec "source" scriptF - Xpath 'c' - try - let g:jump = "error" - exec "source" scriptF - Xpath 'd' - finally - Xpath 'e' - try - let g:jump = "interrupt" - exec "source" scriptF - Xpath 'f' - finally - Xpath 'g' - try - let g:jump = "throw" - exec "source" scriptF - Xpath 'h' - finally - Xpath 'i' - endtry - endtry - endtry - unlet g:jump - call delete(scriptF) - [CODE] - let verify =<< trim [CODE] - call assert_equal('abcdefghi', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" Test 38: :finally reason discarded by an error {{{1 -" -" When a :finally clause is executed due to a :continue, :break, -" :return, :finish, error, interrupt or :throw, the jump reason is -" discarded by an error in the finally clause. -"------------------------------------------------------------------------------- - -func Test_finally_discard_by_error() - let test =<< trim [CODE] - func E(jump) - let loop = 0 - while loop < 2 - let loop = loop + 1 - if loop == 1 - try - if a:jump == "continue" - continue - elseif a:jump == "break" - break - elseif a:jump == "return" || a:jump == "finish" - return - elseif a:jump == "error" - asdf - elseif a:jump == "interrupt" - call interrupt() - let dummy = 0 - elseif a:jump == "throw" - throw "abc" - endif - finally - asdf " error; discards jump that caused the :finally - endtry - elseif loop == 2 - call assert_report('should not get here') - endif - endwhile - call assert_report('should not get here') - endfunc - - try - Xpath 'a' - call E("continue") - call assert_report('should not get here') - finally - try - Xpath 'b' - call E("break") - call assert_report('should not get here') - finally - try - Xpath 'c' - call E("return") - call assert_report('should not get here') - finally - try - Xpath 'd' - let g:jump = "finish" - ExecAsScript E - call assert_report('should not get here') - finally - unlet g:jump - try - Xpath 'e' - call E("error") - call assert_report('should not get here') - finally - try - Xpath 'f' - call E("interrupt") - call assert_report('should not get here') - finally - try - Xpath 'g' - call E("throw") - call assert_report('should not get here') - finally - Xpath 'h' - delfunction E - endtry - endtry - endtry - endtry - endtry - endtry - endtry - call assert_report('should not get here') - [CODE] - let verify =<< trim [CODE] - call assert_equal('abcdefgh', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" Test 39: :finally reason discarded by an interrupt {{{1 -" -" When a :finally clause is executed due to a :continue, :break, -" :return, :finish, error, interrupt or :throw, the jump reason is -" discarded by an interrupt in the finally clause. -"------------------------------------------------------------------------------- - -func Test_finally_discarded_by_interrupt() - let test =<< trim [CODE] - func I(jump) - let loop = 0 - while loop < 2 - let loop = loop + 1 - if loop == 1 - try - if a:jump == "continue" - continue - elseif a:jump == "break" - break - elseif a:jump == "return" || a:jump == "finish" - return - elseif a:jump == "error" - asdf - elseif a:jump == "interrupt" - call interrupt() - let dummy = 0 - elseif a:jump == "throw" - throw "abc" - endif - finally - call interrupt() - let dummy = 0 - endtry - elseif loop == 2 - call assert_report('should not get here') - endif - endwhile - call assert_report('should not get here') - endfunc - - try - try - Xpath 'a' - call I("continue") - call assert_report('should not get here') - finally - try - Xpath 'b' - call I("break") - call assert_report('should not get here') - finally - try - Xpath 'c' - call I("return") - call assert_report('should not get here') - finally - try - Xpath 'd' - let g:jump = "finish" - ExecAsScript I - call assert_report('should not get here') - finally - unlet g:jump - try - Xpath 'e' - call I("error") - call assert_report('should not get here') - finally - try - Xpath 'f' - call I("interrupt") - call assert_report('should not get here') - finally - try - Xpath 'g' - call I("throw") - call assert_report('should not get here') - finally - Xpath 'h' - delfunction I - endtry - endtry - endtry - endtry - endtry - endtry - endtry - call assert_report('should not get here') - catch /^Vim:Interrupt$/ - Xpath 'A' - endtry - [CODE] - let verify =<< trim [CODE] - call assert_equal('abcdefghA', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" Test 40: :finally reason discarded by :throw {{{1 -" -" When a :finally clause is executed due to a :continue, :break, -" :return, :finish, error, interrupt or :throw, the jump reason is -" discarded by a :throw in the finally clause. -"------------------------------------------------------------------------------- - -func Test_finally_discard_by_throw() - let test =<< trim [CODE] - func T(jump) - let loop = 0 - while loop < 2 - let loop = loop + 1 - if loop == 1 - try - if a:jump == "continue" - continue - elseif a:jump == "break" - break - elseif a:jump == "return" || a:jump == "finish" - return - elseif a:jump == "error" - asdf - elseif a:jump == "interrupt" - call interrupt() - let dummy = 0 - elseif a:jump == "throw" - throw "abc" - endif - finally - throw "xyz" " discards jump that caused the :finally - endtry - elseif loop == 2 - call assert_report('should not get here') - endif - endwhile - call assert_report('should not get here') - endfunc - - try - Xpath 'a' - call T("continue") - call assert_report('should not get here') - finally - try - Xpath 'b' - call T("break") - call assert_report('should not get here') - finally - try - Xpath 'c' - call T("return") - call assert_report('should not get here') - finally - try - Xpath 'd' - let g:jump = "finish" - ExecAsScript T - call assert_report('should not get here') - finally - unlet g:jump - try - Xpath 'e' - call T("error") - call assert_report('should not get here') - finally - try - Xpath 'f' - call T("interrupt") - call assert_report('should not get here') - finally - try - Xpath 'g' - call T("throw") - call assert_report('should not get here') - finally - Xpath 'h' - delfunction T - endtry - endtry - endtry - endtry - endtry - endtry - endtry - call assert_report('should not get here') - [CODE] - let verify =<< trim [CODE] - call assert_equal('abcdefgh', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" Test 49: Throwing exceptions across functions {{{1 -" -" When an exception is thrown but not caught inside a function, the -" caller is checked for a matching :catch clause. -"------------------------------------------------------------------------------- - -func T49_C() - try - Xpath 'a' - throw "arrgh" - call assert_report('should not get here') - catch /arrgh/ - Xpath 'b' - endtry - Xpath 'c' -endfunc - -func T49_T1() - XloopNEXT - try - Xloop 'd' - throw "arrgh" - call assert_report('should not get here') - finally - Xloop 'e' - endtry - Xloop 'f' -endfunc - -func T49_T2() - try - Xpath 'g' - call T49_T1() - call assert_report('should not get here') - finally - Xpath 'h' - endtry - call assert_report('should not get here') -endfunc - -func Test_throw_exception_across_funcs() - XpathINIT - XloopINIT - try - Xpath 'i' - call T49_C() " throw and catch - Xpath 'j' - catch /.*/ - call assert_report('should not get here') - endtry - - try - Xpath 'k' - call T49_T1() " throw, one level - call assert_report('should not get here') - catch /arrgh/ - Xpath 'l' - catch /.*/ - call assert_report('should not get here') - endtry - - try - Xpath 'm' - call T49_T2() " throw, two levels - call assert_report('should not get here') - catch /arrgh/ - Xpath 'n' - catch /.*/ - call assert_report('should not get here') - endtry - Xpath 'o' - - call assert_equal('iabcjkd2e2lmgd3e3hno', g:Xpath) -endfunc - -"------------------------------------------------------------------------------- -" Test 50: Throwing exceptions across script files {{{1 -" -" When an exception is thrown but not caught inside a script file, -" the sourcing script or function is checked for a matching :catch -" clause. -" -" This test executes the bodies of the functions C, T1, and T2 from -" the previous test as script files (:return replaced by :finish). -"------------------------------------------------------------------------------- - -func T50_F() - try - Xpath 'A' - exec "source" g:scriptC - Xpath 'B' - catch /.*/ - call assert_report('should not get here') - endtry - - try - Xpath 'C' - exec "source" g:scriptT1 - call assert_report('should not get here') - catch /arrgh/ - Xpath 'D' - catch /.*/ - call assert_report('should not get here') - endtry -endfunc - -func Test_throw_across_script() - XpathINIT - XloopINIT - let g:scriptC = MakeScript("T49_C") - let g:scriptT1 = MakeScript("T49_T1") - let scriptT2 = MakeScript("T49_T2", g:scriptT1) - - try - Xpath 'E' - call T50_F() - Xpath 'F' - exec "source" scriptT2 - call assert_report('should not get here') - catch /arrgh/ - Xpath 'G' - catch /.*/ - call assert_report('should not get here') - endtry - Xpath 'H' - call assert_equal('EAabcBCd2e2DFgd3e3hGH', g:Xpath) - - call delete(g:scriptC) - call delete(g:scriptT1) - call delete(scriptT2) - unlet g:scriptC g:scriptT1 scriptT2 -endfunc - -"------------------------------------------------------------------------------- -" Test 52: Uncaught exceptions {{{1 -" -" When an exception is thrown but not caught, an error message is -" displayed when the script is terminated. In case of an interrupt -" or error exception, the normal interrupt or error message(s) are -" displayed. -"------------------------------------------------------------------------------- - -func Test_uncaught_exception_1() - CheckEnglish - - let test =<< trim [CODE] - Xpath 'a' - throw "arrgh" - call assert_report('should not get here')` - [CODE] - let verify =<< trim [CODE] - call assert_equal('E605: Exception not caught: arrgh', v:errmsg) - call assert_equal('a', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -func Test_uncaught_exception_2() - CheckEnglish - - let test =<< trim [CODE] - try - Xpath 'a' - throw "oops" - call assert_report('should not get here')` - catch /arrgh/ - call assert_report('should not get here')` - endtry - call assert_report('should not get here')` - [CODE] - let verify =<< trim [CODE] - call assert_equal('E605: Exception not caught: oops', v:errmsg) - call assert_equal('a', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -func Test_uncaught_exception_3() - CheckEnglish - - let test =<< trim [CODE] - func T() - Xpath 'c' - throw "brrr" - call assert_report('should not get here')` - endfunc - - try - Xpath 'a' - throw "arrgh" - call assert_report('should not get here')` - catch /.*/ - Xpath 'b' - call T() - call assert_report('should not get here')` - endtry - call assert_report('should not get here')` - [CODE] - let verify =<< trim [CODE] - call assert_equal('E605: Exception not caught: brrr', v:errmsg) - call assert_equal('abc', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -func Test_uncaught_exception_4() - CheckEnglish - - let test =<< trim [CODE] - try - Xpath 'a' - throw "arrgh" - call assert_report('should not get here')` - finally - Xpath 'b' - throw "brrr" - call assert_report('should not get here')` - endtry - call assert_report('should not get here')` - [CODE] - let verify =<< trim [CODE] - call assert_equal('E605: Exception not caught: brrr', v:errmsg) - call assert_equal('ab', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -func Test_uncaught_exception_5() - CheckEnglish - - " Need to catch and handle interrupt, otherwise the test will wait for the - " user to press <Enter> to continue - let test =<< trim [CODE] - try - try - Xpath 'a' - call interrupt() - call assert_report('should not get here') - endtry - call assert_report('should not get here') - catch /^Vim:Interrupt$/ - Xpath 'b' - endtry - [CODE] - let verify =<< trim [CODE] - call assert_equal('ab', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -func Test_uncaught_exception_6() - CheckEnglish - - let test =<< trim [CODE] - try - Xpath 'a' - let x = novar " error E121; exception: E121 - catch /E15:/ " should not catch - call assert_report('should not get here') - endtry - call assert_report('should not get here') - [CODE] - let verify =<< trim [CODE] - call assert_equal('a', g:Xpath) - call assert_equal('E121: Undefined variable: novar', v:errmsg) - [CODE] - call RunInNewVim(test, verify) -endfunc - -func Test_uncaught_exception_7() - CheckEnglish - - let test =<< trim [CODE] - try - Xpath 'a' - " error E108/E488; exception: E488 - unlet novar # - catch /E108:/ " should not catch - call assert_report('should not get here') - endtry - call assert_report('should not get here') - [CODE] - let verify =<< trim [CODE] - call assert_equal('a', g:Xpath) - call assert_equal('E488: Trailing characters: #', v:errmsg) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" Test 53: Nesting errors: :endif/:else/:elseif {{{1 -" -" For nesting errors of :if conditionals the correct error messages -" should be given. -"------------------------------------------------------------------------------- - -func Test_nested_if_else_errors() - CheckEnglish - - " :endif without :if - let code =<< trim END - endif - END - call writefile(code, 'Xtest', 'D') - call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if') - - " :endif without :if - let code =<< trim END - while 1 - endif - endwhile - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if') - - " :endif without :if - let code =<< trim END - try - finally - endif - endtry - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if') - - " :endif without :if - let code =<< trim END - try - endif - endtry - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if') - - " :endif without :if - let code =<< trim END - try - throw "a" - catch /a/ - endif - endtry - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if') - - " :else without :if - let code =<< trim END - else - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if') - - " :else without :if - let code =<< trim END - while 1 - else - endwhile - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if') - - " :else without :if - let code =<< trim END - try - finally - else - endtry - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if') - - " :else without :if - let code =<< trim END - try - else - endtry - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if') - - " :else without :if - let code =<< trim END - try - throw "a" - catch /a/ - else - endtry - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if') - - " :elseif without :if - let code =<< trim END - elseif 1 - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if') - - " :elseif without :if - let code =<< trim END - while 1 - elseif 1 - endwhile - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if') - - " :elseif without :if - let code =<< trim END - try - finally - elseif 1 - endtry - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if') - - " :elseif without :if - let code =<< trim END - try - elseif 1 - endtry - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if') - - " :elseif without :if - let code =<< trim END - try - throw "a" - catch /a/ - elseif 1 - endtry - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if') - - " multiple :else - let code =<< trim END - if 1 - else - else - endif - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(else):E583: Multiple :else') - - " :elseif after :else - let code =<< trim END - if 1 - else - elseif 1 - endif - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(elseif):E584: :elseif after :else') -endfunc - -"------------------------------------------------------------------------------- -" Test 54: Nesting errors: :while/:endwhile {{{1 -" -" For nesting errors of :while conditionals the correct error messages -" should be given. -" -" This test reuses the function MESSAGES() from the previous test. -" This functions checks the messages in g:msgfile. -"------------------------------------------------------------------------------- - -func Test_nested_while_error() - CheckEnglish - - " :endwhile without :while - let code =<< trim END - endwhile - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while') - - " :endwhile without :while - let code =<< trim END - if 1 - endwhile - endif - END - call writefile(code, 'Xtest', 'D') - call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while') - - " Missing :endif - let code =<< trim END - while 1 - if 1 - endwhile - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(endwhile):E171: Missing :endif') - - " :endwhile without :while - let code =<< trim END - try - finally - endwhile - endtry - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while') - - " Missing :endtry - let code =<< trim END - while 1 - try - finally - endwhile - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(endwhile):E600: Missing :endtry') - - " Missing :endtry - let code =<< trim END - while 1 - if 1 - try - finally - endwhile - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(endwhile):E600: Missing :endtry') - - " Missing :endif - let code =<< trim END - while 1 - try - finally - if 1 - endwhile - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(endwhile):E171: Missing :endif') - - " :endwhile without :while - let code =<< trim END - try - endwhile - endtry - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while') - - " :endwhile without :while - let code =<< trim END - while 1 - try - endwhile - endtry - endwhile - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while') - - " :endwhile without :while - let code =<< trim END - try - throw "a" - catch /a/ - endwhile - endtry - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while') - - " :endwhile without :while - let code =<< trim END - while 1 - try - throw "a" - catch /a/ - endwhile - endtry - endwhile - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while') -endfunc - -"------------------------------------------------------------------------------- -" Test 55: Nesting errors: :continue/:break {{{1 -" -" For nesting errors of :continue and :break commands the correct -" error messages should be given. -" -" This test reuses the function MESSAGES() from the previous test. -" This functions checks the messages in g:msgfile. -"------------------------------------------------------------------------------- - -func Test_nested_cont_break_error() - CheckEnglish - - " :continue without :while - let code =<< trim END - continue - END - call writefile(code, 'Xtest', 'D') - call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for') - - " :continue without :while - let code =<< trim END - if 1 - continue - endif - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for') - - " :continue without :while - let code =<< trim END - try - finally - continue - endtry - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for') - - " :continue without :while - let code =<< trim END - try - continue - endtry - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for') - - " :continue without :while - let code =<< trim END - try - throw "a" - catch /a/ - continue - endtry - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for') - - " :break without :while - let code =<< trim END - break - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for') - - " :break without :while - let code =<< trim END - if 1 - break - endif - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for') - - " :break without :while - let code =<< trim END - try - finally - break - endtry - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for') - - " :break without :while - let code =<< trim END - try - break - endtry - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for') - - " :break without :while - let code =<< trim END - try - throw "a" - catch /a/ - break - endtry - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for') -endfunc - -"------------------------------------------------------------------------------- -" Test 56: Nesting errors: :endtry {{{1 -" -" For nesting errors of :try conditionals the correct error messages -" should be given. -" -" This test reuses the function MESSAGES() from the previous test. -" This functions checks the messages in g:msgfile. -"------------------------------------------------------------------------------- - -func Test_nested_endtry_error() - CheckEnglish - - " :endtry without :try - let code =<< trim END - endtry - END - call writefile(code, 'Xtest', 'D') - call AssertException(['source Xtest'], 'Vim(endtry):E602: :endtry without :try') - - " :endtry without :try - let code =<< trim END - if 1 - endtry - endif - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(endtry):E602: :endtry without :try') - - " :endtry without :try - let code =<< trim END - while 1 - endtry - endwhile - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(endtry):E602: :endtry without :try') - - " Missing :endif - let code =<< trim END - try - if 1 - endtry - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(endtry):E171: Missing :endif') - - " Missing :endwhile - let code =<< trim END - try - while 1 - endtry - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(endtry):E170: Missing :endwhile') - - " Missing :endif - let code =<< trim END - try - finally - if 1 - endtry - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(endtry):E171: Missing :endif') - - " Missing :endwhile - let code =<< trim END - try - finally - while 1 - endtry - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(endtry):E170: Missing :endwhile') - - " Missing :endif - let code =<< trim END - try - throw "a" - catch /a/ - if 1 - endtry - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(endtry):E171: Missing :endif') - - " Missing :endwhile - let code =<< trim END - try - throw "a" - catch /a/ - while 1 - endtry - END - call writefile(code, 'Xtest') - call AssertException(['source Xtest'], 'Vim(endtry):E170: Missing :endwhile') -endfunc - -"------------------------------------------------------------------------------- -" Test 57: v:exception and v:throwpoint for user exceptions {{{1 -" -" v:exception evaluates to the value of the exception that was caught -" most recently and is not finished. (A caught exception is finished -" when the next ":catch", ":finally", or ":endtry" is reached.) -" v:throwpoint evaluates to the script/function name and line number -" where that exception has been thrown. -"------------------------------------------------------------------------------- - -func Test_user_exception_info() - CheckEnglish - - XpathINIT - XloopINIT - - func FuncException() - let g:exception = v:exception - endfunc - - func FuncThrowpoint() - let g:throwpoint = v:throwpoint - endfunc - - let scriptException = MakeScript("FuncException") - let scriptThrowPoint = MakeScript("FuncThrowpoint") - - command! CmdException let g:exception = v:exception - command! CmdThrowpoint let g:throwpoint = v:throwpoint - - func T(arg, line) - if a:line == 2 - throw a:arg " in line 2 - elseif a:line == 4 - throw a:arg " in line 4 - elseif a:line == 6 - throw a:arg " in line 6 - elseif a:line == 8 - throw a:arg " in line 8 - endif - endfunc - - func G(arg, line) - call T(a:arg, a:line) - endfunc - - func F(arg, line) - call G(a:arg, a:line) - endfunc - - let scriptT = MakeScript("T") - let scriptG = MakeScript("G", scriptT) - let scriptF = MakeScript("F", scriptG) - - try - Xpath 'a' - call F("oops", 2) - catch /.*/ - Xpath 'b' - let exception = v:exception - let throwpoint = v:throwpoint - call assert_equal("oops", v:exception) - call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint) - call assert_match('\<2\>', v:throwpoint) - - exec "let exception = v:exception" - exec "let throwpoint = v:throwpoint" - call assert_equal("oops", v:exception) - call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint) - call assert_match('\<2\>', v:throwpoint) - - CmdException - CmdThrowpoint - call assert_equal("oops", v:exception) - call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint) - call assert_match('\<2\>', v:throwpoint) - - call FuncException() - call FuncThrowpoint() - call assert_equal("oops", v:exception) - call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint) - call assert_match('\<2\>', v:throwpoint) - - exec "source" scriptException - exec "source" scriptThrowPoint - call assert_equal("oops", v:exception) - call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint) - call assert_match('\<2\>', v:throwpoint) - - try - Xpath 'c' - call G("arrgh", 4) - catch /.*/ - Xpath 'd' - let exception = v:exception - let throwpoint = v:throwpoint - call assert_equal("arrgh", v:exception) - call assert_match('\<G\[1]\.\.T\>', v:throwpoint) - call assert_match('\<4\>', v:throwpoint) - - try - Xpath 'e' - let g:arg = "autsch" - let g:line = 6 - exec "source" scriptF - catch /.*/ - Xpath 'f' - let exception = v:exception - let throwpoint = v:throwpoint - call assert_equal("autsch", v:exception) - call assert_match(fnamemodify(scriptT, ':t'), v:throwpoint) - call assert_match('\<6\>', v:throwpoint) - finally - Xpath 'g' - let exception = v:exception - let throwpoint = v:throwpoint - call assert_equal("arrgh", v:exception) - call assert_match('\<G\[1]\.\.T\>', v:throwpoint) - call assert_match('\<4\>', v:throwpoint) - try - Xpath 'h' - let g:arg = "brrrr" - let g:line = 8 - exec "source" scriptG - catch /.*/ - Xpath 'i' - let exception = v:exception - let throwpoint = v:throwpoint - " Resolve scriptT for matching it against v:throwpoint. - call assert_equal("brrrr", v:exception) - call assert_match(fnamemodify(scriptT, ':t'), v:throwpoint) - call assert_match('\<8\>', v:throwpoint) - finally - Xpath 'j' - let exception = v:exception - let throwpoint = v:throwpoint - call assert_equal("arrgh", v:exception) - call assert_match('\<G\[1]\.\.T\>', v:throwpoint) - call assert_match('\<4\>', v:throwpoint) - endtry - Xpath 'k' - let exception = v:exception - let throwpoint = v:throwpoint - call assert_equal("arrgh", v:exception) - call assert_match('\<G\[1]\.\.T\>', v:throwpoint) - call assert_match('\<4\>', v:throwpoint) - endtry - Xpath 'l' - let exception = v:exception - let throwpoint = v:throwpoint - call assert_equal("arrgh", v:exception) - call assert_match('\<G\[1]\.\.T\>', v:throwpoint) - call assert_match('\<4\>', v:throwpoint) - finally - Xpath 'm' - let exception = v:exception - let throwpoint = v:throwpoint - call assert_equal("oops", v:exception) - call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint) - call assert_match('\<2\>', v:throwpoint) - endtry - Xpath 'n' - let exception = v:exception - let throwpoint = v:throwpoint - call assert_equal("oops", v:exception) - call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint) - call assert_match('\<2\>', v:throwpoint) - finally - Xpath 'o' - let exception = v:exception - let throwpoint = v:throwpoint - call assert_equal("", v:exception) - call assert_match('^$', v:throwpoint) - call assert_match('^$', v:throwpoint) - endtry - - call assert_equal('abcdefghijklmno', g:Xpath) - - unlet exception throwpoint - delfunction FuncException - delfunction FuncThrowpoint - call delete(scriptException) - call delete(scriptThrowPoint) - unlet scriptException scriptThrowPoint - delcommand CmdException - delcommand CmdThrowpoint - delfunction T - delfunction G - delfunction F - call delete(scriptT) - call delete(scriptG) - call delete(scriptF) - unlet scriptT scriptG scriptF -endfunc - -"------------------------------------------------------------------------------- -" -" Test 58: v:exception and v:throwpoint for error/interrupt exceptions {{{1 -" -" v:exception and v:throwpoint work also for error and interrupt -" exceptions. -"------------------------------------------------------------------------------- - -func Test_exception_info_for_error() - CheckEnglish - - let test =<< trim [CODE] - func T(line) - if a:line == 2 - delfunction T " error (function in use) in line 2 - elseif a:line == 4 - call interrupt() - endif - endfunc - - while 1 - try - Xpath 'a' - call T(2) - call assert_report('should not get here') - catch /.*/ - Xpath 'b' - if v:exception !~ 'Vim(delfunction):' - call assert_report('should not get here') - endif - if v:throwpoint !~ '\<T\>' - call assert_report('should not get here') - endif - if v:throwpoint !~ '\<2\>' - call assert_report('should not get here') - endif - finally - Xpath 'c' - if v:exception != "" - call assert_report('should not get here') - endif - if v:throwpoint != "" - call assert_report('should not get here') - endif - break - endtry - endwhile - - Xpath 'd' - if v:exception != "" - call assert_report('should not get here') - endif - if v:throwpoint != "" - call assert_report('should not get here') - endif - - while 1 - try - Xpath 'e' - call T(4) - call assert_report('should not get here') - catch /.*/ - Xpath 'f' - if v:exception != 'Vim:Interrupt' - call assert_report('should not get here') - endif - if v:throwpoint !~ 'function T' - call assert_report('should not get here') - endif - if v:throwpoint !~ '\<4\>' - call assert_report('should not get here') - endif - finally - Xpath 'g' - if v:exception != "" - call assert_report('should not get here') - endif - if v:throwpoint != "" - call assert_report('should not get here') - endif - break - endtry - endwhile - - Xpath 'h' - if v:exception != "" - call assert_report('should not get here') - endif - if v:throwpoint != "" - call assert_report('should not get here') - endif - [CODE] - let verify =<< trim [CODE] - call assert_equal('abcdefgh', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" -" Test 59: v:exception and v:throwpoint when discarding exceptions {{{1 -" -" When a :catch clause is left by a ":break" etc or an error or -" interrupt exception, v:exception and v:throwpoint are reset. They -" are not affected by an exception that is discarded before being -" caught. -"------------------------------------------------------------------------------- -func Test_exception_info_on_discard() - CheckEnglish - - let test =<< trim [CODE] - let sfile = expand("<sfile>") - - while 1 - try - throw "x1" - catch /.*/ - break - endtry - endwhile - call assert_equal('', v:exception) - call assert_equal('', v:throwpoint) - - while 1 - try - throw "x2" - catch /.*/ - break - finally - call assert_equal('', v:exception) - call assert_equal('', v:throwpoint) - endtry - break - endwhile - call assert_equal('', v:exception) - call assert_equal('', v:throwpoint) - - while 1 - try - let errcaught = 0 - try - try - throw "x3" - catch /.*/ - let lnum = expand("<sflnum>") - asdf - endtry - catch /.*/ - let errcaught = 1 - call assert_match('Vim:E492: Not an editor command:', v:exception) - call assert_match('line ' .. (lnum + 1), v:throwpoint) - endtry - finally - call assert_equal(1, errcaught) - break - endtry - endwhile - call assert_equal('', v:exception) - call assert_equal('', v:throwpoint) - - Xpath 'a' - - while 1 - try - let intcaught = 0 - try - try - throw "x4" - catch /.*/ - let lnum = expand("<sflnum>") - call interrupt() - endtry - catch /.*/ - let intcaught = 1 - call assert_match('Vim:Interrupt', v:exception) - call assert_match('line ' .. (lnum + 1), v:throwpoint) - endtry - finally - call assert_equal(1, intcaught) - break - endtry - endwhile - call assert_equal('', v:exception) - call assert_equal('', v:throwpoint) - - Xpath 'b' - - while 1 - try - let errcaught = 0 - try - try - if 1 - let lnum = expand("<sflnum>") - throw "x5" - " missing endif - catch /.*/ - call assert_report('should not get here') - endtry - catch /.*/ - let errcaught = 1 - call assert_match('Vim(catch):E171: Missing :endif:', v:exception) - call assert_match('line ' .. (lnum + 3), v:throwpoint) - endtry - finally - call assert_equal(1, errcaught) - break - endtry - endwhile - call assert_equal('', v:exception) - call assert_equal('', v:throwpoint) - - Xpath 'c' - - try - while 1 - try - throw "x6" - finally - break - endtry - break - endwhile - catch /.*/ - call assert_report('should not get here') - endtry - call assert_equal('', v:exception) - call assert_equal('', v:throwpoint) - - try - while 1 - try - throw "x7" - finally - break - endtry - break - endwhile - catch /.*/ - call assert_report('should not get here') - finally - call assert_equal('', v:exception) - call assert_equal('', v:throwpoint) - endtry - call assert_equal('', v:exception) - call assert_equal('', v:throwpoint) - - while 1 - try - let errcaught = 0 - try - try - throw "x8" - finally - let lnum = expand("<sflnum>") - asdf - endtry - catch /.*/ - let errcaught = 1 - call assert_match('Vim:E492: Not an editor command:', v:exception) - call assert_match('line ' .. (lnum + 1), v:throwpoint) - endtry - finally - call assert_equal(1, errcaught) - break - endtry - endwhile - call assert_equal('', v:exception) - call assert_equal('', v:throwpoint) - - Xpath 'd' - - while 1 - try - let intcaught = 0 - try - try - throw "x9" - finally - let lnum = expand("<sflnum>") - call interrupt() - endtry - catch /.*/ - let intcaught = 1 - call assert_match('Vim:Interrupt', v:exception) - call assert_match('line ' .. (lnum + 1), v:throwpoint) - endtry - finally - call assert_equal(1, intcaught) - break - endtry - endwhile - call assert_equal('', v:exception) - call assert_equal('', v:throwpoint) - - Xpath 'e' - - while 1 - try - let errcaught = 0 - try - try - if 1 - let lnum = expand("<sflnum>") - throw "x10" - " missing endif - finally - call assert_equal('', v:exception) - call assert_equal('', v:throwpoint) - endtry - catch /.*/ - let errcaught = 1 - call assert_match('Vim(finally):E171: Missing :endif:', v:exception) - call assert_match('line ' .. (lnum + 3), v:throwpoint) - endtry - finally - call assert_equal(1, errcaught) - break - endtry - endwhile - call assert_equal('', v:exception) - call assert_equal('', v:throwpoint) - - Xpath 'f' - - while 1 - try - let errcaught = 0 - try - try - if 1 - let lnum = expand("<sflnum>") - throw "x11" - " missing endif - endtry - catch /.*/ - let errcaught = 1 - call assert_match('Vim(endtry):E171: Missing :endif:', v:exception) - call assert_match('line ' .. (lnum + 3), v:throwpoint) - endtry - finally - call assert_equal(1, errcaught) - break - endtry - endwhile - call assert_equal('', v:exception) - call assert_equal('', v:throwpoint) - - Xpath 'g' - [CODE] - let verify =<< trim [CODE] - call assert_equal('abcdefg', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" -" Test 60: (Re)throwing v:exception; :echoerr. {{{1 -" -" A user exception can be rethrown after catching by throwing -" v:exception. An error or interrupt exception cannot be rethrown -" because Vim exceptions cannot be faked. A Vim exception using the -" value of v:exception can, however, be triggered by the :echoerr -" command. -"------------------------------------------------------------------------------- - -func Test_rethrow_exception_1() - XpathINIT - try - try - Xpath 'a' - throw "oops" - catch /oops/ - Xpath 'b' - throw v:exception " rethrow user exception - catch /.*/ - call assert_report('should not get here') - endtry - catch /^oops$/ " catches rethrown user exception - Xpath 'c' - catch /.*/ - call assert_report('should not get here') - endtry - call assert_equal('abc', g:Xpath) -endfunc - -func Test_rethrow_exception_2() - XpathINIT - try - let caught = 0 - try - Xpath 'a' - write /n/o/n/w/r/i/t/a/b/l/e/_/f/i/l/e - call assert_report('should not get here') - catch /^Vim(write):/ - let caught = 1 - throw v:exception " throw error: cannot fake Vim exception - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'b' - call assert_equal(1, caught) - endtry - catch /^Vim(throw):/ " catches throw error - let caught = caught + 1 - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'c' - call assert_equal(2, caught) - endtry - call assert_equal('abc', g:Xpath) -endfunc - -func Test_rethrow_exception_3() - XpathINIT - try - let caught = 0 - try - Xpath 'a' - asdf - catch /^Vim/ " catch error exception - let caught = 1 - " Trigger Vim error exception with value specified after :echoerr - let value = substitute(v:exception, '^Vim\((.*)\)\=:', '', "") - echoerr value - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'b' - call assert_equal(1, caught) - endtry - catch /^Vim(echoerr):/ - let caught = caught + 1 - call assert_match(value, v:exception) - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'c' - call assert_equal(2, caught) - endtry - call assert_equal('abc', g:Xpath) -endfunc - -func Test_rethrow_exception_3() - XpathINIT - try - let errcaught = 0 - try - Xpath 'a' - let intcaught = 0 - call interrupt() - catch /^Vim:/ " catch interrupt exception - let intcaught = 1 - " Trigger Vim error exception with value specified after :echoerr - echoerr substitute(v:exception, '^Vim\((.*)\)\=:', '', "") - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'b' - call assert_equal(1, intcaught) - endtry - catch /^Vim(echoerr):/ - let errcaught = 1 - call assert_match('Interrupt', v:exception) - finally - Xpath 'c' - call assert_equal(1, errcaught) - endtry - call assert_equal('abc', g:Xpath) -endfunc - -"------------------------------------------------------------------------------- -" Test 61: Catching interrupt exceptions {{{1 -" -" When an interrupt occurs inside a :try/:endtry region, an -" interrupt exception is thrown and can be caught. Its value is -" "Vim:Interrupt". If the interrupt occurs after an error or a :throw -" but before a matching :catch is reached, all following :catches of -" that try block are ignored, but the interrupt exception can be -" caught by the next surrounding try conditional. An interrupt is -" ignored when there is a previous interrupt that has not been caught -" or causes a :finally clause to be executed. -"------------------------------------------------------------------------------- - -func Test_catch_intr_exception() - let test =<< trim [CODE] - while 1 - try - try - Xpath 'a' - call interrupt() - call assert_report('should not get here') - catch /^Vim:Interrupt$/ - Xpath 'b' - finally - Xpath 'c' - endtry - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'd' - break - endtry - endwhile - - while 1 - try - try - try - Xpath 'e' - asdf - call assert_report('should not get here') - catch /do_not_catch/ - call assert_report('should not get here') - catch /.*/ - Xpath 'f' - call interrupt() - call assert_report('should not get here') - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'g' - call interrupt() - call assert_report('should not get here') - endtry - catch /^Vim:Interrupt$/ - Xpath 'h' - finally - Xpath 'i' - endtry - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'j' - break - endtry - endwhile - - while 1 - try - try - try - Xpath 'k' - throw "x" - call assert_report('should not get here') - catch /do_not_catch/ - call assert_report('should not get here') - catch /x/ - Xpath 'l' - call interrupt() - call assert_report('should not get here') - catch /.*/ - call assert_report('should not get here') - endtry - catch /^Vim:Interrupt$/ - Xpath 'm' - finally - Xpath 'n' - endtry - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'o' - break - endtry - endwhile - - while 1 - try - try - Xpath 'p' - call interrupt() - call assert_report('should not get here') - catch /do_not_catch/ - call interrupt() - call assert_report('should not get here') - catch /^Vim:Interrupt$/ - Xpath 'q' - finally - Xpath 'r' - endtry - catch /.*/ - call assert_report('should not get here') - finally - Xpath 's' - break - endtry - endwhile - - Xpath 't' - [CODE] - let verify =<< trim [CODE] - call assert_equal('abcdefghijklmnopqrst', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" Test 62: Catching error exceptions {{{1 -" -" An error inside a :try/:endtry region is converted to an exception -" and can be caught. The error exception has a "Vim(cmdname):" prefix -" where cmdname is the name of the failing command, or a "Vim:" prefix -" if no command name is known. The "Vim" prefixes cannot be faked. -"------------------------------------------------------------------------------- - -func Test_catch_err_exception_1() - XpathINIT - while 1 - try - try - let caught = 0 - unlet novar - catch /^Vim(unlet):/ - Xpath 'a' - let caught = 1 - let v:errmsg = substitute(v:exception, '^Vim(unlet):', '', "") - finally - Xpath 'b' - call assert_equal(1, caught) - call assert_match('E108: No such variable: "novar"', v:errmsg) - endtry - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'c' - break - endtry - call assert_report('should not get here') - endwhile - call assert_equal('abc', g:Xpath) -endfunc - -func Test_catch_err_exception_2() - XpathINIT - while 1 - try - try - let caught = 0 - throw novar " error in :throw - catch /^Vim(throw):/ - Xpath 'a' - let caught = 1 - let v:errmsg = substitute(v:exception, '^Vim(throw):', '', "") - finally - Xpath 'b' - call assert_equal(1, caught) - call assert_match('E121: Undefined variable: novar', v:errmsg) - endtry - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'c' - break - endtry - call assert_report('should not get here') - endwhile - call assert_equal('abc', g:Xpath) -endfunc - -func Test_catch_err_exception_3() - XpathINIT - while 1 - try - try - let caught = 0 - throw "Vim:faked" " error: cannot fake Vim exception - catch /^Vim(throw):/ - Xpath 'a' - let caught = 1 - let v:errmsg = substitute(v:exception, '^Vim(throw):', '', "") - finally - Xpath 'b' - call assert_equal(1, caught) - call assert_match("E608: Cannot :throw exceptions with 'Vim' prefix", - \ v:errmsg) - endtry - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'c' - break - endtry - call assert_report('should not get here') - endwhile - call assert_equal('abc', g:Xpath) -endfunc - -func Test_catch_err_exception_4() - XpathINIT - func F() - while 1 - " Missing :endwhile - endfunc - - while 1 - try - try - let caught = 0 - call F() - catch /^Vim(endfunction):/ - Xpath 'a' - let caught = 1 - let v:errmsg = substitute(v:exception, '^Vim(endfunction):', '', "") - finally - Xpath 'b' - call assert_equal(1, caught) - call assert_match("E170: Missing :endwhile", v:errmsg) - endtry - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'c' - break - endtry - call assert_report('should not get here') - endwhile - call assert_equal('abc', g:Xpath) - delfunc F -endfunc - -func Test_catch_err_exception_5() - XpathINIT - func F() - while 1 - " Missing :endwhile - endfunc - - while 1 - try - try - let caught = 0 - ExecAsScript F - catch /^Vim:/ - Xpath 'a' - let caught = 1 - let v:errmsg = substitute(v:exception, '^Vim:', '', "") - finally - Xpath 'b' - call assert_equal(1, caught) - call assert_match("E170: Missing :endwhile", v:errmsg) - endtry - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'c' - break - endtry - call assert_report('should not get here') - endwhile - call assert_equal('abc', g:Xpath) - delfunc F -endfunc - -func Test_catch_err_exception_6() - XpathINIT - func G() - call G() - endfunc - - while 1 - try - let mfd_save = &mfd - set mfd=3 - try - let caught = 0 - call G() - catch /^Vim(call):/ - Xpath 'a' - let caught = 1 - let v:errmsg = substitute(v:exception, '^Vim(call):', '', "") - finally - Xpath 'b' - call assert_equal(1, caught) - call assert_match("E132: Function call depth is higher than 'maxfuncdepth'", v:errmsg) - endtry - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'c' - let &mfd = mfd_save - break - endtry - call assert_report('should not get here') - endwhile - call assert_equal('abc', g:Xpath) - delfunc G -endfunc - -func Test_catch_err_exception_7() - XpathINIT - func H() - return H() - endfunc - - while 1 - try - let mfd_save = &mfd - set mfd=3 - try - let caught = 0 - call H() - catch /^Vim(return):/ - Xpath 'a' - let caught = 1 - let v:errmsg = substitute(v:exception, '^Vim(return):', '', "") - finally - Xpath 'b' - call assert_equal(1, caught) - call assert_match("E132: Function call depth is higher than 'maxfuncdepth'", v:errmsg) - endtry - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'c' - let &mfd = mfd_save - break " discard error for $VIMNOERRTHROW - endtry - call assert_report('should not get here') - endwhile - - call assert_equal('abc', g:Xpath) - delfunc H -endfunc - -"------------------------------------------------------------------------------- -" Test 63: Suppressing error exceptions by :silent!. {{{1 -" -" A :silent! command inside a :try/:endtry region suppresses the -" conversion of errors to an exception and the immediate abortion on -" error. When the commands executed by the :silent! themselves open -" a new :try/:endtry region, conversion of errors to exception and -" immediate abortion is switched on again - until the next :silent! -" etc. The :silent! has the effect of setting v:errmsg to the error -" message text (without displaying it) and continuing with the next -" script line. -" -" When a command triggering autocommands is executed by :silent! -" inside a :try/:endtry, the autocommand execution is not suppressed -" on error. -" -" This test reuses the function MSG() from the previous test. -"------------------------------------------------------------------------------- - -func Test_silent_exception() - XpathINIT - XloopINIT - let g:taken = "" - - func S(n) abort - XloopNEXT - let g:taken = g:taken . "E" . a:n - let v:errmsg = "" - exec "asdf" . a:n - - " Check that ":silent!" continues: - Xloop 'a' - - " Check that ":silent!" sets "v:errmsg": - call assert_match("E492: Not an editor command", v:errmsg) - endfunc - - func Foo() - while 1 - try - try - let caught = 0 - " This is not silent: - call S(3) - catch /^Vim:/ - Xpath 'b' - let caught = 1 - let errmsg3 = substitute(v:exception, '^Vim:', '', "") - silent! call S(4) - finally - call assert_equal(1, caught) - Xpath 'c' - call assert_match("E492: Not an editor command", errmsg3) - silent! call S(5) - " Break out of try conditionals that cover ":silent!". This also - " discards the aborting error when $VIMNOERRTHROW is non-zero. - break - endtry - catch /.*/ - call assert_report('should not get here') - endtry - endwhile - " This is a double ":silent!" (see caller). - silent! call S(6) - endfunc - - func Bar() - try - silent! call S(2) - silent! execute "call Foo() | call S(7)" - silent! call S(8) - endtry " normal end of try cond that covers ":silent!" - " This has a ":silent!" from the caller: - call S(9) - endfunc - - silent! call S(1) - silent! call Bar() - silent! call S(10) - - call assert_equal("E1E2E3E4E5E6E7E8E9E10", g:taken) - - augroup TMP - au! - autocmd BufWritePost * Xpath 'd' - augroup END - - Xpath 'e' - silent! write /i/m/p/o/s/s/i/b/l/e - Xpath 'f' - - call assert_equal('a2a3ba5ca6a7a8a9a10a11edf', g:Xpath) - - augroup TMP - au! - augroup END - augroup! TMP - delfunction S - delfunction Foo - delfunction Bar -endfunc - -"------------------------------------------------------------------------------- -" Test 64: Error exceptions after error, interrupt or :throw {{{1 -" -" When an error occurs after an interrupt or a :throw but before -" a matching :catch is reached, all following :catches of that try -" block are ignored, but the error exception can be caught by the next -" surrounding try conditional. Any previous error exception is -" discarded. An error is ignored when there is a previous error that -" has not been caught. -"------------------------------------------------------------------------------- - -func Test_exception_after_error_1() - XpathINIT - while 1 - try - try - Xpath 'a' - let caught = 0 - while 1 - if 1 - " Missing :endif - endwhile " throw error exception - catch /^Vim(/ - Xpath 'b' - let caught = 1 - finally - Xpath 'c' - call assert_equal(1, caught) - endtry - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'd' - break - endtry - call assert_report('should not get here') - endwhile - call assert_equal('abcd', g:Xpath) -endfunc - -func Test_exception_after_error_2() - XpathINIT - while 1 - try - try - Xpath 'a' - let caught = 0 - try - if 1 - " Missing :endif - catch /.*/ " throw error exception - call assert_report('should not get here') - catch /.*/ - call assert_report('should not get here') - endtry - catch /^Vim(/ - Xpath 'b' - let caught = 1 - finally - Xpath 'c' - call assert_equal(1, caught) - endtry - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'd' - break - endtry - call assert_report('should not get here') - endwhile - call assert_equal('abcd', g:Xpath) -endfunc - -func Test_exception_after_error_3() - XpathINIT - while 1 - try - try - let caught = 0 - try - Xpath 'a' - call interrupt() - catch /do_not_catch/ - call assert_report('should not get here') - if 1 - " Missing :endif - catch /.*/ " throw error exception - call assert_report('should not get here') - catch /.*/ - call assert_report('should not get here') - endtry - catch /^Vim(/ - Xpath 'b' - let caught = 1 - finally - Xpath 'c' - call assert_equal(1, caught) - endtry - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'd' - break - endtry - call assert_report('should not get here') - endwhile - call assert_equal('abcd', g:Xpath) -endfunc - -func Test_exception_after_error_4() - XpathINIT - while 1 - try - try - let caught = 0 - try - Xpath 'a' - throw "x" - catch /do_not_catch/ - call assert_report('should not get here') - if 1 - " Missing :endif - catch /x/ " throw error exception - call assert_report('should not get here') - catch /.*/ - call assert_report('should not get here') - endtry - catch /^Vim(/ - Xpath 'b' - let caught = 1 - finally - Xpath 'c' - call assert_equal(1, caught) - endtry - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'd' - break - endtry - call assert_report('should not get here') - endwhile - call assert_equal('abcd', g:Xpath) -endfunc - -func Test_exception_after_error_5() - XpathINIT - while 1 - try - try - let caught = 0 - Xpath 'a' - endif " :endif without :if; throw error exception - if 1 - " Missing :endif - catch /do_not_catch/ " ignore new error - call assert_report('should not get here') - catch /^Vim(endif):/ - Xpath 'b' - let caught = 1 - catch /^Vim(/ - call assert_report('should not get here') - finally - Xpath 'c' - call assert_equal(1, caught) - endtry - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'd' - break - endtry - call assert_report('should not get here') - endwhile - call assert_equal('abcd', g:Xpath) -endfunc - -"------------------------------------------------------------------------------- -" Test 65: Errors in the /pattern/ argument of a :catch {{{1 -" -" On an error in the /pattern/ argument of a :catch, the :catch does -" not match. Any following :catches of the same :try/:endtry don't -" match either. Finally clauses are executed. -"------------------------------------------------------------------------------- - -func Test_catch_pattern_error() - CheckEnglish - XpathINIT - - try - try - Xpath 'a' - throw "oops" - catch /^oops$/ - Xpath 'b' - catch /\)/ " not checked; exception has already been caught - call assert_report('should not get here') - endtry - Xpath 'c' - catch /.*/ - call assert_report('should not get here') - endtry - call assert_equal('abc', g:Xpath) - - XpathINIT - func F() - try - try - try - Xpath 'a' - throw "ab" - catch /abc/ " does not catch - call assert_report('should not get here') - catch /\)/ " error; discards exception - call assert_report('should not get here') - catch /.*/ " not checked - call assert_report('should not get here') - finally - Xpath 'b' - endtry - call assert_report('should not get here') - catch /^ab$/ " checked, but original exception is discarded - call assert_report('should not get here') - catch /^Vim(catch):/ - Xpath 'c' - call assert_match('Vim(catch):E475: Invalid argument:', v:exception) - finally - Xpath 'd' - endtry - Xpath 'e' - catch /.*/ - call assert_report('should not get here') - endtry - Xpath 'f' - endfunc - - call F() - call assert_equal('abcdef', g:Xpath) - - delfunc F -endfunc - -"------------------------------------------------------------------------------- -" Test 66: Stop range :call on error, interrupt, or :throw {{{1 -" -" When a function which is multiply called for a range since it -" doesn't handle the range itself has an error in a command -" dynamically enclosed by :try/:endtry or gets an interrupt or -" executes a :throw, no more calls for the remaining lines in the -" range are made. On an error in a command not dynamically enclosed -" by :try/:endtry, the function is executed again for the remaining -" lines in the range. -"------------------------------------------------------------------------------- - -func Test_stop_range_on_error() - let test =<< trim [CODE] - let file = tempname() - exec "edit" file - call setline(1, ['line 1', 'line 2', 'line 3']) - let taken = "" - let expected = "G1EF1E(1)F1E(2)F1E(3)G2EF2E(1)G3IF3I(1)G4TF4T(1)G5AF5A(1)" - - func F(reason, n) abort - let g:taken = g:taken .. "F" .. a:n .. - \ substitute(a:reason, '\(\l\).*', '\u\1', "") .. - \ "(" .. line(".") .. ")" - - if a:reason == "error" - asdf - elseif a:reason == "interrupt" - call interrupt() - elseif a:reason == "throw" - throw "xyz" - elseif a:reason == "aborting error" - XloopNEXT - call assert_equal(g:taken, g:expected) - try - bwipeout! - call delete(g:file) - asdf - endtry - endif - endfunc - - func G(reason, n) - let g:taken = g:taken .. "G" .. a:n .. - \ substitute(a:reason, '\(\l\).*', '\u\1', "") - 1,3call F(a:reason, a:n) - endfunc - - Xpath 'a' - call G("error", 1) - try - Xpath 'b' - try - call G("error", 2) - call assert_report('should not get here') - finally - Xpath 'c' - try - call G("interrupt", 3) - call assert_report('should not get here') - finally - Xpath 'd' - try - call G("throw", 4) - call assert_report('should not get here') - endtry - endtry - endtry - catch /xyz/ - Xpath 'e' - catch /.*/ - call assert_report('should not get here') - endtry - Xpath 'f' - call G("aborting error", 5) - call assert_report('should not get here') - [CODE] - let verify =<< trim [CODE] - call assert_equal('abcdef', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" Test 67: :throw across :call command {{{1 -" -" On a call command, an exception might be thrown when evaluating the -" function name, during evaluation of the arguments, or when the -" function is being executed. The exception can be caught by the -" caller. -"------------------------------------------------------------------------------- - -func THROW(x, n) - if a:n == 1 - Xpath 'A' - elseif a:n == 2 - Xpath 'B' - elseif a:n == 3 - Xpath 'C' - endif - throw a:x -endfunc - -func NAME(x, n) - if a:n == 1 - call assert_report('should not get here') - elseif a:n == 2 - Xpath 'D' - elseif a:n == 3 - Xpath 'E' - elseif a:n == 4 - Xpath 'F' - endif - return a:x -endfunc - -func ARG(x, n) - if a:n == 1 - call assert_report('should not get here') - elseif a:n == 2 - call assert_report('should not get here') - elseif a:n == 3 - Xpath 'G' - elseif a:n == 4 - Xpath 'I' - endif - return a:x -endfunc - -func Test_throw_across_call_cmd() - XpathINIT - - func F(x, n) - if a:n == 2 - call assert_report('should not get here') - elseif a:n == 4 - Xpath 'a' - endif - endfunc - - while 1 - try - let v:errmsg = "" - - while 1 - try - Xpath 'b' - call {NAME(THROW("name", 1), 1)}(ARG(4711, 1), 1) - call assert_report('should not get here') - catch /^name$/ - Xpath 'c' - catch /.*/ - call assert_report('should not get here') - finally - call assert_equal("", v:errmsg) - let v:errmsg = "" - break - endtry - endwhile - - while 1 - try - Xpath 'd' - call {NAME("F", 2)}(ARG(THROW("arg", 2), 2), 2) - call assert_report('should not get here') - catch /^arg$/ - Xpath 'e' - catch /.*/ - call assert_report('should not get here') - finally - call assert_equal("", v:errmsg) - let v:errmsg = "" - break - endtry - endwhile - - while 1 - try - Xpath 'f' - call {NAME("THROW", 3)}(ARG("call", 3), 3) - call assert_report('should not get here') - catch /^call$/ - Xpath 'g' - catch /^0$/ " default return value - call assert_report('should not get here') - catch /.*/ - call assert_report('should not get here') - finally - call assert_equal("", v:errmsg) - let v:errmsg = "" - break - endtry - endwhile - - while 1 - try - Xpath 'h' - call {NAME("F", 4)}(ARG(4711, 4), 4) - Xpath 'i' - catch /.*/ - call assert_report('should not get here') - finally - call assert_equal("", v:errmsg) - let v:errmsg = "" - break - endtry - endwhile - - catch /^0$/ " default return value - call assert_report('should not get here') - catch /.*/ - call assert_report('should not get here') - finally - call assert_equal("", v:errmsg) - let v:errmsg = "" - break - endtry - endwhile - - call assert_equal('bAcdDBefEGCghFIai', g:Xpath) - delfunction F -endfunc - -"------------------------------------------------------------------------------- -" Test 68: :throw across function calls in expressions {{{1 -" -" On a function call within an expression, an exception might be -" thrown when evaluating the function name, during evaluation of the -" arguments, or when the function is being executed. The exception -" can be caught by the caller. -" -" This test reuses the functions THROW(), NAME(), and ARG() from the -" previous test. -"------------------------------------------------------------------------------- - -func Test_throw_across_call_expr() - XpathINIT - - func F(x, n) - if a:n == 2 - call assert_report('should not get here') - elseif a:n == 4 - Xpath 'a' - endif - return a:x - endfunction - - while 1 - try - let error = 0 - let v:errmsg = "" - - while 1 - try - Xpath 'b' - let var1 = {NAME(THROW("name", 1), 1)}(ARG(4711, 1), 1) - call assert_report('should not get here') - catch /^name$/ - Xpath 'c' - catch /.*/ - call assert_report('should not get here') - finally - call assert_equal("", v:errmsg) - let v:errmsg = "" - break - endtry - endwhile - call assert_true(!exists('var1')) - - while 1 - try - Xpath 'd' - let var2 = {NAME("F", 2)}(ARG(THROW("arg", 2), 2), 2) - call assert_report('should not get here') - catch /^arg$/ - Xpath 'e' - catch /.*/ - call assert_report('should not get here') - finally - call assert_equal("", v:errmsg) - let v:errmsg = "" - break - endtry - endwhile - call assert_true(!exists('var2')) - - while 1 - try - Xpath 'f' - let var3 = {NAME("THROW", 3)}(ARG("call", 3), 3) - call assert_report('should not get here') - catch /^call$/ - Xpath 'g' - catch /^0$/ " default return value - call assert_report('should not get here') - catch /.*/ - call assert_report('should not get here') - finally - call assert_equal("", v:errmsg) - let v:errmsg = "" - break - endtry - endwhile - call assert_true(!exists('var3')) - - while 1 - try - Xpath 'h' - let var4 = {NAME("F", 4)}(ARG(4711, 4), 4) - Xpath 'i' - catch /.*/ - call assert_report('should not get here') - finally - call assert_equal("", v:errmsg) - let v:errmsg = "" - break - endtry - endwhile - call assert_true(exists('var4') && var4 == 4711) - - catch /^0$/ " default return value - call assert_report('should not get here') - catch /.*/ - call assert_report('should not get here') - finally - call assert_equal("", v:errmsg) - break - endtry - endwhile - - call assert_equal('bAcdDBefEGCghFIai', g:Xpath) - delfunc F -endfunc - -"------------------------------------------------------------------------------- -" Test 76: Errors, interrupts, :throw during expression evaluation {{{1 -" -" When a function call made during expression evaluation is aborted -" due to an error inside a :try/:endtry region or due to an interrupt -" or a :throw, the expression evaluation is aborted as well. No -" message is displayed for the cancelled expression evaluation. On an -" error not inside :try/:endtry, the expression evaluation continues. -"------------------------------------------------------------------------------- - -func Test_expr_eval_error() - let test =<< trim [CODE] - let taken = "" - - func ERR(n) - let g:taken = g:taken .. "E" .. a:n - asdf - endfunc - - func ERRabort(n) abort - let g:taken = g:taken .. "A" .. a:n - asdf - endfunc " returns -1; may cause follow-up msg for illegal var/func name - - func WRAP(n, arg) - let g:taken = g:taken .. "W" .. a:n - let g:saved_errmsg = v:errmsg - return arg - endfunc - - func INT(n) - let g:taken = g:taken .. "I" .. a:n - call interrupt() - endfunc - - func THR(n) - let g:taken = g:taken .. "T" .. a:n - throw "should not be caught" - endfunc - - func CONT(n) - let g:taken = g:taken .. "C" .. a:n - endfunc - - func MSG(n) - let g:taken = g:taken .. "M" .. a:n - let errmsg = (a:n >= 37 && a:n <= 44) ? g:saved_errmsg : v:errmsg - let msgptn = (a:n >= 10 && a:n <= 27) ? "^$" : "asdf" - call assert_match(msgptn, errmsg) - let v:errmsg = "" - let g:saved_errmsg = "" - endfunc - - let v:errmsg = "" - - try - let t = 1 - while t <= 9 - Xloop 'a' - try - if t == 1 - let v{ERR(t) + CONT(t)} = 0 - elseif t == 2 - let v{ERR(t) + CONT(t)} - elseif t == 3 - let var = exists('v{ERR(t) + CONT(t)}') - elseif t == 4 - unlet v{ERR(t) + CONT(t)} - elseif t == 5 - function F{ERR(t) + CONT(t)}() - endfunction - elseif t == 6 - function F{ERR(t) + CONT(t)} - elseif t == 7 - let var = exists('*F{ERR(t) + CONT(t)}') - elseif t == 8 - delfunction F{ERR(t) + CONT(t)} - elseif t == 9 - let var = ERR(t) + CONT(t) - endif - catch /asdf/ - " v:errmsg is not set when the error message is converted to an - " exception. Set it to the original error message. - let v:errmsg = substitute(v:exception, '^Vim:', '', "") - catch /^Vim\((\a\+)\)\=:/ - " An error exception has been thrown after the original error. - let v:errmsg = "" - finally - call MSG(t) - let t = t + 1 - XloopNEXT - continue " discard an aborting error - endtry - endwhile - catch /.*/ - call assert_report('should not get here') - endtry - - try - let t = 10 - while t <= 18 - Xloop 'b' - try - if t == 10 - let v{INT(t) + CONT(t)} = 0 - elseif t == 11 - let v{INT(t) + CONT(t)} - elseif t == 12 - let var = exists('v{INT(t) + CONT(t)}') - elseif t == 13 - unlet v{INT(t) + CONT(t)} - elseif t == 14 - function F{INT(t) + CONT(t)}() - endfunction - elseif t == 15 - function F{INT(t) + CONT(t)} - elseif t == 16 - let var = exists('*F{INT(t) + CONT(t)}') - elseif t == 17 - delfunction F{INT(t) + CONT(t)} - elseif t == 18 - let var = INT(t) + CONT(t) - endif - catch /^Vim\((\a\+)\)\=:\(Interrupt\)\@!/ - " An error exception has been triggered after the interrupt. - let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "") - finally - call MSG(t) - let t = t + 1 - XloopNEXT - continue " discard interrupt - endtry - endwhile - catch /.*/ - call assert_report('should not get here') - endtry - - try - let t = 19 - while t <= 27 - Xloop 'c' - try - if t == 19 - let v{THR(t) + CONT(t)} = 0 - elseif t == 20 - let v{THR(t) + CONT(t)} - elseif t == 21 - let var = exists('v{THR(t) + CONT(t)}') - elseif t == 22 - unlet v{THR(t) + CONT(t)} - elseif t == 23 - function F{THR(t) + CONT(t)}() - endfunction - elseif t == 24 - function F{THR(t) + CONT(t)} - elseif t == 25 - let var = exists('*F{THR(t) + CONT(t)}') - elseif t == 26 - delfunction F{THR(t) + CONT(t)} - elseif t == 27 - let var = THR(t) + CONT(t) - endif - catch /^Vim\((\a\+)\)\=:/ - " An error exception has been triggered after the :throw. - let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "") - finally - call MSG(t) - let t = t + 1 - XloopNEXT - continue " discard exception - endtry - endwhile - catch /.*/ - call assert_report('should not get here') - endtry - - let v{ERR(28) + CONT(28)} = 0 - call MSG(28) - let v{ERR(29) + CONT(29)} - call MSG(29) - let var = exists('v{ERR(30) + CONT(30)}') - call MSG(30) - unlet v{ERR(31) + CONT(31)} - call MSG(31) - function F{ERR(32) + CONT(32)}() - endfunction - call MSG(32) - function F{ERR(33) + CONT(33)} - call MSG(33) - let var = exists('*F{ERR(34) + CONT(34)}') - call MSG(34) - delfunction F{ERR(35) + CONT(35)} - call MSG(35) - let var = ERR(36) + CONT(36) - call MSG(36) - - let saved_errmsg = "" - - let v{WRAP(37, ERRabort(37)) + CONT(37)} = 0 - call MSG(37) - let v{WRAP(38, ERRabort(38)) + CONT(38)} - call MSG(38) - let var = exists('v{WRAP(39, ERRabort(39)) + CONT(39)}') - call MSG(39) - unlet v{WRAP(40, ERRabort(40)) + CONT(40)} - call MSG(40) - function F{WRAP(41, ERRabort(41)) + CONT(41)}() - endfunction - call MSG(41) - function F{WRAP(42, ERRabort(42)) + CONT(42)} - call MSG(42) - let var = exists('*F{WRAP(43, ERRabort(43)) + CONT(43)}') - call MSG(43) - delfunction F{WRAP(44, ERRabort(44)) + CONT(44)} - call MSG(44) - let var = ERRabort(45) + CONT(45) - call MSG(45) - Xpath 'd' - - let expected = "" - \ .. "E1M1E2M2E3M3E4M4E5M5E6M6E7M7E8M8E9M9" - \ .. "I10M10I11M11I12M12I13M13I14M14I15M15I16M16I17M17I18M18" - \ .. "T19M19T20M20T21M21T22M22T23M23T24M24T25M25T26M26T27M27" - \ .. "E28C28M28E29C29M29E30C30M30E31C31M31E32C32M32E33C33M33" - \ .. "E34C34M34E35C35M35E36C36M36" - \ .. "A37W37C37M37A38W38C38M38A39W39C39M39A40W40C40M40A41W41C41M41" - \ .. "A42W42C42M42A43W43C43M43A44W44C44M44A45C45M45" - call assert_equal(expected, taken) - [CODE] - let verify =<< trim [CODE] - let expected = "a1a2a3a4a5a6a7a8a9" - \ .. "b10b11b12b13b14b15b16b17b18" - \ .. "c19c20c21c22c23c24c25c26c27d" - call assert_equal(expected, g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" Test 77: Errors, interrupts, :throw in name{brace-expression} {{{1 -" -" When a function call made during evaluation of an expression in -" braces as part of a function name after ":function" is aborted due -" to an error inside a :try/:endtry region or due to an interrupt or -" a :throw, the expression evaluation is aborted as well, and the -" function definition is ignored, skipping all commands to the -" ":endfunction". On an error not inside :try/:endtry, the expression -" evaluation continues and the function gets defined, and can be -" called and deleted. -"------------------------------------------------------------------------------- -func Test_brace_expr_error() - let test =<< trim [CODE] - func ERR() abort - Xloop 'a' - asdf - endfunc " returns -1 - - func OK() - Xloop 'b' - let v:errmsg = "" - return 0 - endfunc - - let v:errmsg = "" - - Xpath 'c' - func F{1 + ERR() + OK()}(arg) - " F0 should be defined. - if exists("a:arg") && a:arg == "calling" - Xpath 'd' - else - call assert_report('should not get here') - endif - endfunction - call assert_equal("", v:errmsg) - XloopNEXT - - Xpath 'e' - call F{1 + ERR() + OK()}("calling") - call assert_equal("", v:errmsg) - XloopNEXT - - Xpath 'f' - delfunction F{1 + ERR() + OK()} - call assert_equal("", v:errmsg) - XloopNEXT - - try - while 1 - try - Xpath 'g' - func G{1 + ERR() + OK()}(arg) - " G0 should not be defined, and the function body should be - " skipped. - call assert_report('should not get here') - " Use an unmatched ":finally" to check whether the body is - " skipped when an error occurs in ERR(). This works whether or - " not the exception is converted to an exception. - finally - call assert_report('should not get here') - endtry - try - call assert_report('should not get here') - endfunction - - call assert_report('should not get here') - catch /asdf/ - " Jumped to when the function is not defined and the body is - " skipped. - Xpath 'h' - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'i' - break - endtry " jumped to when the body is not skipped - endwhile - catch /.*/ - call assert_report('should not get here') - endtry - [CODE] - let verify =<< trim [CODE] - call assert_equal('ca1b1ea2b2dfa3b3ga4hi', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" Test 78: Messages on parsing errors in expression evaluation {{{1 -" -" When an expression evaluation detects a parsing error, an error -" message is given and converted to an exception, and the expression -" evaluation is aborted. -"------------------------------------------------------------------------------- -func Test_expr_eval_error_msg() - CheckEnglish - - let test =<< trim [CODE] - let taken = "" - - func F(n) - let g:taken = g:taken . "F" . a:n - endfunc - - func MSG(n, enr, emsg) - let g:taken = g:taken . "M" . a:n - call assert_match('^' .. a:enr .. ':', v:errmsg) - call assert_match(a:emsg, v:errmsg) - endfunc - - func CONT(n) - let g:taken = g:taken . "C" . a:n - endfunc - - let v:errmsg = "" - try - let t = 1 - while t <= 14 - let g:taken = g:taken . "T" . t - let v:errmsg = "" - try - if t == 1 - let v{novar + CONT(t)} = 0 - elseif t == 2 - let v{novar + CONT(t)} - elseif t == 3 - let var = exists('v{novar + CONT(t)}') - elseif t == 4 - unlet v{novar + CONT(t)} - elseif t == 5 - function F{novar + CONT(t)}() - endfunction - elseif t == 6 - function F{novar + CONT(t)} - elseif t == 7 - let var = exists('*F{novar + CONT(t)}') - elseif t == 8 - delfunction F{novar + CONT(t)} - elseif t == 9 - echo novar + CONT(t) - elseif t == 10 - echo v{novar + CONT(t)} - elseif t == 11 - echo F{novar + CONT(t)} - elseif t == 12 - let var = novar + CONT(t) - elseif t == 13 - let var = v{novar + CONT(t)} - elseif t == 14 - let var = F{novar + CONT(t)}() - endif - catch /^Vim\((\a\+)\)\=:/ - Xloop 'a' - " v:errmsg is not set when the error message is converted to an - " exception. Set it to the original error message. - let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "") - finally - Xloop 'b' - if t <= 8 && t != 3 && t != 7 - call MSG(t, 'E475', 'Invalid argument\>') - else - call MSG(t, 'E121', "Undefined variable") - endif - let t = t + 1 - XloopNEXT - continue " discard an aborting error - endtry - endwhile - catch /.*/ - call assert_report('should not get here') - endtry - - func T(n, expr, enr, emsg) - try - let g:taken = g:taken . "T" . a:n - let v:errmsg = "" - try - execute "let var = " . a:expr - catch /^Vim\((\a\+)\)\=:/ - Xloop 'c' - " v:errmsg is not set when the error message is converted to an - " exception. Set it to the original error message. - let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "") - finally - Xloop 'd' - call MSG(a:n, a:enr, a:emsg) - XloopNEXT - " Discard an aborting error: - return - endtry - catch /.*/ - call assert_report('should not get here') - endtry - endfunc - - call T(15, 'Nofunc() + CONT(15)', 'E117', "Unknown function") - call T(16, 'F(1 2 + CONT(16))', 'E116', "Invalid arguments") - call T(17, 'F(1, 2) + CONT(17)', 'E118', "Too many arguments") - call T(18, 'F() + CONT(18)', 'E119', "Not enough arguments") - call T(19, '{(1} + CONT(19)', 'E110', "Missing ')'") - call T(20, '("abc"[1) + CONT(20)', 'E111', "Missing ']'") - call T(21, '(1 +) + CONT(21)', 'E15', "Invalid expression") - call T(22, '1 2 + CONT(22)', 'E488', "Trailing characters: 2 +") - call T(23, '(1 ? 2) + CONT(23)', 'E109', "Missing ':' after '?'") - call T(24, '("abc) + CONT(24)', 'E114', "Missing double quote") - call T(25, "('abc) + CONT(25)", 'E115', "Missing single quote") - call T(26, '& + CONT(26)', 'E112', "Option name missing") - call T(27, '&asdf + CONT(27)', 'E113', "Unknown option") - - let expected = "" - \ .. "T1M1T2M2T3M3T4M4T5M5T6M6T7M7T8M8T9M9T10M10T11M11T12M12T13M13T14M14" - \ .. "T15M15T16M16T17M17T18M18T19M19T20M20T21M21T22M22T23M23T24M24T25M25" - \ .. "T26M26T27M27" - - call assert_equal(expected, taken) - [CODE] - let verify =<< trim [CODE] - let expected = "a1b1a2b2a3b3a4b4a5b5a6b6a7b7a8b8a9b9a10b10a11b11a12b12" - \ .. "a13b13a14b14c15d15c16d16c17d17c18d18c19d19c20d20" - \ .. "c21d21c22d22c23d23c24d24c25d25c26d26c27d27" - call assert_equal(expected, g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" Test 79: Throwing one of several errors for the same command {{{1 -" -" When several errors appear in a row (for instance during expression -" evaluation), the first as the most specific one is used when -" throwing an error exception. If, however, a syntax error is -" detected afterwards, this one is used for the error exception. -" On a syntax error, the next command is not executed, on a normal -" error, however, it is (relevant only in a function without the -" "abort" flag). v:errmsg is not set. -" -" If throwing error exceptions is configured off, v:errmsg is always -" set to the latest error message, that is, to the more general -" message or the syntax error, respectively. -"------------------------------------------------------------------------------- -func Test_throw_multi_error() - CheckEnglish - - let test =<< trim [CODE] - func NEXT(cmd) - exec a:cmd . " | Xloop 'a'" - endfun - - call NEXT('echo novar') " (checks nextcmd) - XloopNEXT - call NEXT('let novar #') " (skips nextcmd) - XloopNEXT - call NEXT('unlet novar #') " (skips nextcmd) - XloopNEXT - call NEXT('let {novar}') " (skips nextcmd) - XloopNEXT - call NEXT('unlet{ novar}') " (skips nextcmd) - - call assert_equal('a1', g:Xpath) - XpathINIT - XloopINIT - - func EXEC(cmd) - exec a:cmd - endfunc - - try - while 1 " dummy loop - try - let v:errmsg = "" - call EXEC('echo novar') " normal error - catch /^Vim\((\a\+)\)\=:/ - Xpath 'b' - call assert_match('E121: Undefined variable: novar', v:exception) - finally - Xpath 'c' - call assert_equal("", v:errmsg) - break - endtry - endwhile - - Xpath 'd' - let cmd = "let" - while cmd != "" - try - let v:errmsg = "" - call EXEC(cmd . ' novar #') " normal plus syntax error - catch /^Vim\((\a\+)\)\=:/ - Xloop 'e' - if cmd =~ 'unlet' - " TODO: should get error for 'novar' - call assert_match('E488: Trailing characters', v:exception) - else - call assert_match('E121: Undefined variable: novar', v:exception) - endif - finally - Xloop 'f' - call assert_equal("", v:errmsg) - if cmd == "let" - let cmd = "unlet" - else - let cmd = "" - endif - XloopNEXT - continue - endtry - endwhile - - Xpath 'g' - let cmd = "let" - while cmd != "" - try - let v:errmsg = "" - call EXEC(cmd . ' {novar}') " normal plus syntax error - catch /^Vim\((\a\+)\)\=:/ - Xloop 'h' - call assert_match('E475: Invalid argument: {novar}', v:exception) - finally - Xloop 'i' - call assert_equal("", v:errmsg) - if cmd == "let" - let cmd = "unlet" - else - let cmd = "" - endif - XloopNEXT - continue - endtry - endwhile - catch /.*/ - call assert_report('should not get here') - endtry - Xpath 'j' - [CODE] - let verify =<< trim [CODE] - call assert_equal('bcde1f1e2f2gh3i3h4i4j', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" Test 80: Syntax error in expression for illegal :elseif {{{1 -" -" If there is a syntax error in the expression after an illegal -" :elseif, an error message is given (or an error exception thrown) -" for the illegal :elseif rather than the expression error. -"------------------------------------------------------------------------------- -func Test_if_syntax_error() - CheckEnglish - - let test =<< trim [CODE] - let v:errmsg = "" - if 0 - else - elseif 1 ||| 2 - endif - Xpath 'a' - call assert_match('E584: :elseif after :else', v:errmsg) - - let v:errmsg = "" - if 1 - else - elseif 1 ||| 2 - endif - Xpath 'b' - call assert_match('E584: :elseif after :else', v:errmsg) - - let v:errmsg = "" - elseif 1 ||| 2 - Xpath 'c' - call assert_match('E582: :elseif without :if', v:errmsg) - - let v:errmsg = "" - while 1 - elseif 1 ||| 2 - endwhile - Xpath 'd' - call assert_match('E582: :elseif without :if', v:errmsg) - - while 1 - try - try - let v:errmsg = "" - if 0 - else - elseif 1 ||| 2 - endif - catch /^Vim\((\a\+)\)\=:/ - Xpath 'e' - call assert_match('E584: :elseif after :else', v:exception) - finally - Xpath 'f' - call assert_equal("", v:errmsg) - endtry - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'g' - break - endtry - endwhile - - while 1 - try - try - let v:errmsg = "" - if 1 - else - elseif 1 ||| 2 - endif - catch /^Vim\((\a\+)\)\=:/ - Xpath 'h' - call assert_match('E584: :elseif after :else', v:exception) - finally - Xpath 'i' - call assert_equal("", v:errmsg) - endtry - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'j' - break - endtry - endwhile - - while 1 - try - try - let v:errmsg = "" - elseif 1 ||| 2 - catch /^Vim\((\a\+)\)\=:/ - Xpath 'k' - call assert_match('E582: :elseif without :if', v:exception) - finally - Xpath 'l' - call assert_equal("", v:errmsg) - endtry - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'm' - break - endtry - endwhile - - while 1 - try - try - let v:errmsg = "" - while 1 - elseif 1 ||| 2 - endwhile - catch /^Vim\((\a\+)\)\=:/ - Xpath 'n' - call assert_match('E582: :elseif without :if', v:exception) - finally - Xpath 'o' - call assert_equal("", v:errmsg) - endtry - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'p' - break - endtry - endwhile - Xpath 'q' - [CODE] - let verify =<< trim [CODE] - call assert_equal('abcdefghijklmnopq', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" Test 81: Discarding exceptions after an error or interrupt {{{1 -" -" When an exception is thrown from inside a :try conditional without -" :catch and :finally clauses and an error or interrupt occurs before -" the :endtry is reached, the exception is discarded. -"------------------------------------------------------------------------------- - -func Test_discard_exception_after_error_1() - let test =<< trim [CODE] - try - Xpath 'a' - try - Xpath 'b' - throw "arrgh" - call assert_report('should not get here') - if 1 - call assert_report('should not get here') - " error after :throw: missing :endif - endtry - call assert_report('should not get here') - catch /arrgh/ - call assert_report('should not get here') - endtry - call assert_report('should not get here') - [CODE] - let verify =<< trim [CODE] - call assert_equal('ab', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -" interrupt the code before the endtry is invoked -func Test_discard_exception_after_error_2() - XpathINIT - let lines =<< trim [CODE] - try - Xpath 'a' - try - Xpath 'b' - throw "arrgh" - call assert_report('should not get here') - endtry " interrupt here - call assert_report('should not get here') - catch /arrgh/ - call assert_report('should not get here') - endtry - call assert_report('should not get here') - [CODE] - call writefile(lines, 'Xscript', 'D') - - breakadd file 7 Xscript - try - let caught_intr = 0 - debuggreedy - call feedkeys(":source Xscript\<CR>quit\<CR>", "xt") - catch /^Vim:Interrupt$/ - call assert_match('Xscript, line 7', v:throwpoint) - let caught_intr = 1 - endtry - 0debuggreedy - call assert_equal(1, caught_intr) - call assert_equal('ab', g:Xpath) - breakdel * -endfunc - -"------------------------------------------------------------------------------- -" Test 82: Ignoring :catch clauses after an error or interrupt {{{1 -" -" When an exception is thrown and an error or interrupt occurs before -" the matching :catch clause is reached, the exception is discarded -" and the :catch clause is ignored (also for the error or interrupt -" exception being thrown then). -"------------------------------------------------------------------------------- - -func Test_ignore_catch_after_error_1() - let test =<< trim [CODE] - try - try - Xpath 'a' - throw "arrgh" - call assert_report('should not get here') - if 1 - call assert_report('should not get here') - " error after :throw: missing :endif - catch /.*/ - call assert_report('should not get here') - catch /.*/ - call assert_report('should not get here') - endtry - call assert_report('should not get here') - catch /arrgh/ - call assert_report('should not get here') - endtry - call assert_report('should not get here') - [CODE] - let verify =<< trim [CODE] - call assert_equal('a', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -func Test_ignore_catch_after_error_2() - let test =<< trim [CODE] - func E() - try - try - Xpath 'a' - throw "arrgh" - call assert_report('should not get here') - if 1 - call assert_report('should not get here') - " error after :throw: missing :endif - catch /.*/ - call assert_report('should not get here') - catch /.*/ - call assert_report('should not get here') - endtry - call assert_report('should not get here') - catch /arrgh/ - call assert_report('should not get here') - endtry - endfunc - - call E() - call assert_report('should not get here') - [CODE] - let verify =<< trim [CODE] - call assert_equal('a', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -" interrupt right before a catch is invoked in a script -func Test_ignore_catch_after_intr_1() - " for unknown reasons this test sometimes fails on MS-Windows. - let g:test_is_flaky = 1 - - XpathINIT - let lines =<< trim [CODE] - try - try - Xpath 'a' - throw "arrgh" - call assert_report('should not get here') - catch /.*/ " interrupt here - call assert_report('should not get here') - catch /.*/ - call assert_report('should not get here') - endtry - call assert_report('should not get here') - catch /arrgh/ - call assert_report('should not get here') - endtry - call assert_report('should not get here') - [CODE] - call writefile(lines, 'Xscript', 'D') - - breakadd file 6 Xscript - try - let caught_intr = 0 - debuggreedy - call feedkeys(":source Xscript\<CR>quit\<CR>", "xt") - catch /^Vim:Interrupt$/ - call assert_match('Xscript, line 6', v:throwpoint) - let caught_intr = 1 - endtry - 0debuggreedy - call assert_equal(1, caught_intr) - call assert_equal('a', g:Xpath) - breakdel * -endfunc - -" interrupt right before a catch is invoked inside a function. -func Test_ignore_catch_after_intr_2() - " for unknown reasons this test sometimes fails on MS-Windows. - let g:test_is_flaky = 1 - - XpathINIT - func F() - try - try - Xpath 'a' - throw "arrgh" - call assert_report('should not get here') - catch /.*/ " interrupt here - call assert_report('should not get here') - catch /.*/ - call assert_report('should not get here') - endtry - call assert_report('should not get here') - catch /arrgh/ - call assert_report('should not get here') - endtry - call assert_report('should not get here') - endfunc - - breakadd func 6 F - try - let caught_intr = 0 - debuggreedy - call feedkeys(":call F()\<CR>quit\<CR>", "xt") - catch /^Vim:Interrupt$/ - call assert_match('\.F, line 6', v:throwpoint) - let caught_intr = 1 - endtry - 0debuggreedy - call assert_equal(1, caught_intr) - call assert_equal('a', g:Xpath) - breakdel * - delfunc F -endfunc - -"------------------------------------------------------------------------------- -" Test 83: Executing :finally clauses after an error or interrupt {{{1 -" -" When an exception is thrown and an error or interrupt occurs before -" the :finally of the innermost :try is reached, the exception is -" discarded and the :finally clause is executed. -"------------------------------------------------------------------------------- - -func Test_finally_after_error() - let test =<< trim [CODE] - try - Xpath 'a' - try - Xpath 'b' - throw "arrgh" - call assert_report('should not get here') - if 1 - call assert_report('should not get here') - " error after :throw: missing :endif - finally - Xpath 'c' - endtry - call assert_report('should not get here') - catch /arrgh/ - call assert_report('should not get here') - endtry - call assert_report('should not get here') - [CODE] - let verify =<< trim [CODE] - call assert_equal('abc', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -" interrupt the code right before the finally is invoked -func Test_finally_after_intr() - XpathINIT - let lines =<< trim [CODE] - try - Xpath 'a' - try - Xpath 'b' - throw "arrgh" - call assert_report('should not get here') - finally " interrupt here - Xpath 'c' - endtry - call assert_report('should not get here') - catch /arrgh/ - call assert_report('should not get here') - endtry - call assert_report('should not get here') - [CODE] - call writefile(lines, 'Xscript', 'D') - - breakadd file 7 Xscript - try - let caught_intr = 0 - debuggreedy - call feedkeys(":source Xscript\<CR>quit\<CR>", "xt") - catch /^Vim:Interrupt$/ - call assert_match('Xscript, line 7', v:throwpoint) - let caught_intr = 1 - endtry - 0debuggreedy - call assert_equal(1, caught_intr) - call assert_equal('abc', g:Xpath) - breakdel * -endfunc - -"------------------------------------------------------------------------------- -" Test 84: Exceptions in autocommand sequences. {{{1 -" -" When an exception occurs in a sequence of autocommands for -" a specific event, the rest of the sequence is not executed. The -" command that triggered the autocommand execution aborts, and the -" exception is propagated to the caller. -" -" For the FuncUndefined event under a function call expression or -" :call command, the function is not executed, even when it has -" been defined by the autocommands before the exception occurred. -"------------------------------------------------------------------------------- - -func Test_autocmd_exception() - let test =<< trim [CODE] - func INT() - call interrupt() - endfunc - - aug TMP - autocmd! - - autocmd User x1 Xpath 'a' - autocmd User x1 throw "x1" - autocmd User x1 call assert_report('should not get here') - - autocmd User x2 Xpath 'b' - autocmd User x2 asdf - autocmd User x2 call assert_report('should not get here') - - autocmd User x3 Xpath 'c' - autocmd User x3 call INT() - autocmd User x3 call assert_report('should not get here') - - autocmd FuncUndefined U1 func U1() - autocmd FuncUndefined U1 call assert_report('should not get here') - autocmd FuncUndefined U1 endfunc - autocmd FuncUndefined U1 Xpath 'd' - autocmd FuncUndefined U1 throw "U1" - autocmd FuncUndefined U1 call assert_report('should not get here') - - autocmd FuncUndefined U2 func U2() - autocmd FuncUndefined U2 call assert_report('should not get here') - autocmd FuncUndefined U2 endfunc - autocmd FuncUndefined U2 Xpath 'e' - autocmd FuncUndefined U2 ASDF - autocmd FuncUndefined U2 call assert_report('should not get here') - - autocmd FuncUndefined U3 func U3() - autocmd FuncUndefined U3 call assert_report('should not get here') - autocmd FuncUndefined U3 endfunc - autocmd FuncUndefined U3 Xpath 'f' - autocmd FuncUndefined U3 call INT() - autocmd FuncUndefined U3 call assert_report('should not get here') - aug END - - try - try - Xpath 'g' - doautocmd User x1 - catch /x1/ - Xpath 'h' - endtry - - while 1 - try - Xpath 'i' - doautocmd User x2 - catch /asdf/ - Xpath 'j' - finally - Xpath 'k' - break - endtry - endwhile - - while 1 - try - Xpath 'l' - doautocmd User x3 - catch /Vim:Interrupt/ - Xpath 'm' - finally - Xpath 'n' - " ... but break loop for caught interrupt exception, - " or discard interrupt and break loop if $VIMNOINTTHROW - break - endtry - endwhile - - if exists("*U1") | delfunction U1 | endif - if exists("*U2") | delfunction U2 | endif - if exists("*U3") | delfunction U3 | endif - - try - Xpath 'o' - call U1() - catch /U1/ - Xpath 'p' - endtry - - while 1 - try - Xpath 'q' - call U2() - catch /ASDF/ - Xpath 'r' - finally - Xpath 's' - " ... but break loop for caught error exception, - " or discard error and break loop if $VIMNOERRTHROW - break - endtry - endwhile - - while 1 - try - Xpath 't' - call U3() - catch /Vim:Interrupt/ - Xpath 'u' - finally - Xpath 'v' - " ... but break loop for caught interrupt exception, - " or discard interrupt and break loop if $VIMNOINTTHROW - break - endtry - endwhile - catch /.*/ - call assert_report('should not get here') - endtry - Xpath 'w' - [CODE] - let verify =<< trim [CODE] - call assert_equal('gahibjklcmnodpqerstfuvw', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" Test 85: Error exceptions in autocommands for I/O command events {{{1 -" -" When an I/O command is inside :try/:endtry, autocommands to be -" executed after it should be skipped on an error (exception) in the -" command itself or in autocommands to be executed before the command. -" In the latter case, the I/O command should not be executed either. -" Example 1: BufWritePre, :write, BufWritePost -" Example 2: FileReadPre, :read, FileReadPost. -"------------------------------------------------------------------------------- - -func Test_autocmd_error_io_exception() - let test =<< trim [CODE] - " Remove the autocommands for the events specified as arguments in all used - " autogroups. - func Delete_autocommands(...) - let augfile = tempname() - while 1 - try - exec "redir >" . augfile - aug - redir END - exec "edit" augfile - g/^$/d - norm G$ - let wrap = "w" - while search('\%( \|^\)\@<=.\{-}\%( \)\@=', wrap) > 0 - let wrap = "W" - exec "norm y/ \n" - let argno = 1 - while argno <= a:0 - exec "au!" escape(@", " ") a:{argno} - let argno = argno + 1 - endwhile - endwhile - catch /.*/ - finally - bwipeout! - call delete(augfile) - break - endtry - endwhile - endfunc - - call Delete_autocommands("BufWritePre", "BufWritePost") - - while 1 - try - try - let post = 0 - aug TMP - au! BufWritePost * let post = 1 - aug END - write /n/o/n/e/x/i/s/t/e/n/t - catch /^Vim(write):/ - Xpath 'a' - call assert_match("E212: Can't open file for writing", v:exception) - finally - Xpath 'b' - call assert_equal(0, post) - au! TMP - aug! TMP - endtry - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'c' - break - endtry - endwhile - - while 1 - try - try - let post = 0 - aug TMP - au! BufWritePre * asdf - au! BufWritePost * let post = 1 - aug END - let tmpfile = tempname() - exec "write" tmpfile - catch /^Vim\((write)\)\=:/ - Xpath 'd' - call assert_match('E492: Not an editor command', v:exception) - finally - Xpath 'e' - if filereadable(tmpfile) - call assert_report('should not get here') - endif - call assert_equal(0, post) - au! TMP - aug! TMP - endtry - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'f' - break - endtry - endwhile - - call delete(tmpfile) - - call Delete_autocommands("BufWritePre", "BufWritePost", - \ "BufReadPre", "BufReadPost", "FileReadPre", "FileReadPost") - - while 1 - try - try - let post = 0 - aug TMP - au! FileReadPost * let post = 1 - aug END - let caught = 0 - read /n/o/n/e/x/i/s/t/e/n/t - catch /^Vim(read):/ - Xpath 'g' - call assert_match("E484: Can't open file", v:exception) - finally - Xpath 'h' - call assert_equal(0, post) - au! TMP - aug! TMP - endtry - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'i' - break - endtry - endwhile - - while 1 - try - let infile = tempname() - let tmpfile = tempname() - call writefile(["XYZ"], infile) - exec "edit" tmpfile - try - Xpath 'j' - try - let post = 0 - aug TMP - au! FileReadPre * asdf - au! FileReadPost * let post = 1 - aug END - exec "0read" infile - catch /^Vim\((read)\)\=:/ - Xpath 'k' - call assert_match('E492: Not an editor command', v:exception) - finally - Xpath 'l' - if getline("1") == "XYZ" - call assert_report('should not get here') - endif - call assert_equal(0, post) - au! TMP - aug! TMP - endtry - finally - Xpath 'm' - bwipeout! - endtry - catch /.*/ - call assert_report('should not get here') - finally - Xpath 'n' - break - endtry - endwhile - - call delete(infile) - call delete(tmpfile) - [CODE] - let verify =<< trim [CODE] - call assert_equal('abcdefghijklmn', g:Xpath) - [CODE] - call RunInNewVim(test, verify) -endfunc - -"------------------------------------------------------------------------------- -" Test 87 using (expr) ? funcref : funcref {{{1 -" -" Vim needs to correctly parse the funcref and even when it does -" not execute the funcref, it needs to consume the trailing () -"------------------------------------------------------------------------------- - -func Add2(x1, x2) - return a:x1 + a:x2 -endfu - -func GetStr() - return "abcdefghijklmnopqrstuvwxyp" -endfu - -func Test_funcref_with_condexpr() - call assert_equal(5, function('Add2')(2,3)) - - call assert_equal(3, 1 ? function('Add2')(1,2) : function('Add2')(2,3)) - call assert_equal(5, 0 ? function('Add2')(1,2) : function('Add2')(2,3)) - " Make sure, GetStr() still works. - call assert_equal('abcdefghijk', GetStr()[0:10]) -endfunc - -" Test 90: Recognizing {} in variable name. {{{1 -"------------------------------------------------------------------------------- - -func Test_curlies() - let s:var = 66 - let ns = 's' - call assert_equal(66, {ns}:var) - - let g:a = {} - let g:b = 't' - let g:a[g:b] = 77 - call assert_equal(77, g:a['t']) -endfunc - -"------------------------------------------------------------------------------- -" Test 91: using type(). {{{1 -"------------------------------------------------------------------------------- - -func Test_type() - call assert_equal(0, type(0)) - call assert_equal(1, type("")) - call assert_equal(2, type(function("tr"))) - call assert_equal(2, type(function("tr", [8]))) - call assert_equal(3, type([])) - call assert_equal(4, type({})) - call assert_equal(5, type(0.0)) - call assert_equal(6, type(v:false)) - call assert_equal(6, type(v:true)) - call assert_equal(7, type(v:none)) - call assert_equal(7, type(v:null)) - call assert_equal(8, v:t_job) - call assert_equal(9, v:t_channel) - call assert_equal(v:t_number, type(0)) - call assert_equal(v:t_string, type("")) - call assert_equal(v:t_func, type(function("tr"))) - call assert_equal(v:t_func, type(function("tr", [8]))) - call assert_equal(v:t_list, type([])) - call assert_equal(v:t_dict, type({})) - call assert_equal(v:t_float, type(0.0)) - call assert_equal(v:t_bool, type(v:false)) - call assert_equal(v:t_bool, type(v:true)) - call assert_equal(v:t_none, type(v:none)) - call assert_equal(v:t_none, type(v:null)) - call assert_equal(v:t_string, type(test_null_string())) - call assert_equal(v:t_func, type(test_null_function())) - call assert_equal(v:t_func, type(test_null_partial())) - call assert_equal(v:t_list, type(test_null_list())) - call assert_equal(v:t_dict, type(test_null_dict())) - if has('job') - call assert_equal(v:t_job, type(test_null_job())) - endif - if has('channel') - call assert_equal(v:t_channel, type(test_null_channel())) - endif - call assert_equal(v:t_blob, type(test_null_blob())) - - call assert_fails("call type(test_void())", ['E340:', 'E685:']) - call assert_fails("call type(test_unknown())", ['E340:', 'E685:']) - - call assert_equal(0, 0 + v:false) - call assert_equal(1, 0 + v:true) - call assert_equal(0, 0 + v:none) - call assert_equal(0, 0 + v:null) - - call assert_equal('v:false', '' . v:false) - call assert_equal('v:true', '' . v:true) - call assert_equal('v:none', '' . v:none) - call assert_equal('v:null', '' . v:null) - - call assert_true(v:false == 0) - call assert_false(v:false != 0) - call assert_true(v:true == 1) - call assert_false(v:true != 1) - call assert_false(v:true == v:false) - call assert_true(v:true != v:false) - - call assert_true(v:null == 0) - call assert_false(v:null == 1) - call assert_false(v:null != 0) - call assert_true(v:none == 0) - call assert_false(v:none == 1) - call assert_false(v:none != 0) - call assert_true(v:null == 0.0) - call assert_false(v:null == 0.1) - call assert_false(v:null != 0.0) - - call assert_true(v:false is v:false) - call assert_true(v:true is v:true) - call assert_true(v:none is v:none) - call assert_true(v:null is v:null) - - call assert_false(v:false isnot v:false) - call assert_false(v:true isnot v:true) - call assert_false(v:none isnot v:none) - call assert_false(v:null isnot v:null) - - call assert_false(v:false is 0) - call assert_false(v:true is 1) - call assert_false(v:true is v:false) - call assert_false(v:none is 0) - call assert_false(v:none is []) - call assert_false(v:none is {}) - call assert_false(v:none is 'text') - call assert_false(v:null is 0) - call assert_false(v:null is v:none) - - call assert_true(v:false isnot 0) - call assert_true(v:true isnot 1) - call assert_true(v:true isnot v:false) - call assert_true(v:none isnot 0) - call assert_true(v:null isnot 0) - call assert_true(v:null isnot v:none) - - call assert_equal(v:false, eval(string(v:false))) - call assert_equal(v:true, eval(string(v:true))) - call assert_equal(v:none, eval(string(v:none))) - call assert_equal(v:null, eval(string(v:null))) - - call assert_equal(v:false, copy(v:false)) - call assert_equal(v:true, copy(v:true)) - call assert_equal(v:none, copy(v:none)) - call assert_equal(v:null, copy(v:null)) - - call assert_equal([v:false], deepcopy([v:false])) - call assert_equal([v:true], deepcopy([v:true])) - call assert_equal([v:none], deepcopy([v:none])) - call assert_equal([v:null], deepcopy([v:null])) - - call assert_true(empty(v:false)) - call assert_false(empty(v:true)) - call assert_true(empty(v:null)) - call assert_true(empty(v:none)) - - func ChangeYourMind() - try - return v:true - finally - return 'something else' - endtry - endfunc - - call ChangeYourMind() -endfunc - -func Test_typename() - call assert_equal('number', typename(123)) - call assert_equal('string', typename('x')) - call assert_equal('list<number>', typename([123])) - call assert_equal('dict<number>', typename(#{key: 123})) - call assert_equal('list<dict<number>>', typename([#{key: 123}])) - - let l = [] - let d = #{a: 0} - let l = [d] - let l[0].e = #{b: l} - call assert_equal('list<dict<any>>', typename(l)) - call assert_equal('dict<any>', typename(d)) -endfunc - -"------------------------------------------------------------------------------- -" Test 92: skipping code {{{1 -"------------------------------------------------------------------------------- - -func Test_skip() - let Fn = function('Test_type') - call assert_false(0 && Fn[1]) - call assert_false(0 && string(Fn)) - call assert_false(0 && len(Fn)) - let l = [] - call assert_false(0 && l[1]) - call assert_false(0 && string(l)) - call assert_false(0 && len(l)) - let f = 1.0 - call assert_false(0 && f[1]) - call assert_false(0 && string(f)) - call assert_false(0 && len(f)) - let sp = v:null - call assert_false(0 && sp[1]) - call assert_false(0 && string(sp)) - call assert_false(0 && len(sp)) - -endfunc - -"------------------------------------------------------------------------------- -" Test 93: :echo and string() {{{1 -"------------------------------------------------------------------------------- - -func Test_echo_and_string() - " String - let a = 'foo bar' - redir => result - echo a - echo string(a) - redir END - let l = split(result, "\n") - call assert_equal(["foo bar", - \ "'foo bar'"], l) - - " Float - let a = -1.2e0 - redir => result - echo a - echo string(a) - redir END - let l = split(result, "\n") - call assert_equal(["-1.2", - \ "-1.2"], l) - - " Funcref - redir => result - echo function('string') - echo string(function('string')) - redir END - let l = split(result, "\n") - call assert_equal(["string", - \ "function('string')"], l) - - " Recursive dictionary - let a = {} - let a["a"] = a - redir => result - echo a - echo string(a) - redir END - let l = split(result, "\n") - call assert_equal(["{'a': {...}}", - \ "{'a': {...}}"], l) - - " Recursive list - let a = [0] - let a[0] = a - redir => result - echo a - echo string(a) - redir END - let l = split(result, "\n") - call assert_equal(["[[...]]", - \ "[[...]]"], l) - - " Empty dictionaries in a list - let a = {} - redir => result - echo [a, a, a] - echo string([a, a, a]) - redir END - let l = split(result, "\n") - call assert_equal(["[{}, {}, {}]", - \ "[{}, {}, {}]"], l) - - " Empty dictionaries in a dictionary - let a = {} - let b = {"a": a, "b": a} - redir => result - echo b - echo string(b) - redir END - let l = split(result, "\n") - call assert_equal(["{'a': {}, 'b': {}}", - \ "{'a': {}, 'b': {}}"], l) - - " Empty lists in a list - let a = [] - redir => result - echo [a, a, a] - echo string([a, a, a]) - redir END - let l = split(result, "\n") - call assert_equal(["[[], [], []]", - \ "[[], [], []]"], l) - - " Empty lists in a dictionary - let a = [] - let b = {"a": a, "b": a} - redir => result - echo b - echo string(b) - redir END - let l = split(result, "\n") - call assert_equal(["{'a': [], 'b': []}", - \ "{'a': [], 'b': []}"], l) - - " Dictionaries in a list - let a = {"one": "yes", "two": "yes", "three": "yes"} - redir => result - echo [a, a, a] - echo string([a, a, a]) - redir END - let l = split(result, "\n") - call assert_equal(["[{'one': 'yes', 'two': 'yes', 'three': 'yes'}, {...}, {...}]", - \ "[{'one': 'yes', 'two': 'yes', 'three': 'yes'}, {'one': 'yes', 'two': 'yes', 'three': 'yes'}, {'one': 'yes', 'two': 'yes', 'three': 'yes'}]"], l) - - " Dictionaries in a dictionary - let a = {"one": "yes", "two": "yes", "three": "yes"} - let b = {"a": a, "b": a} - redir => result - echo b - echo string(b) - redir END - let l = split(result, "\n") - call assert_equal(["{'a': {'one': 'yes', 'two': 'yes', 'three': 'yes'}, 'b': {...}}", - \ "{'a': {'one': 'yes', 'two': 'yes', 'three': 'yes'}, 'b': {'one': 'yes', 'two': 'yes', 'three': 'yes'}}"], l) - - " Lists in a list - let a = [1, 2, 3] - redir => result - echo [a, a, a] - echo string([a, a, a]) - redir END - let l = split(result, "\n") - call assert_equal(["[[1, 2, 3], [...], [...]]", - \ "[[1, 2, 3], [1, 2, 3], [1, 2, 3]]"], l) - - " Lists in a dictionary - let a = [1, 2, 3] - let b = {"a": a, "b": a} - redir => result - echo b - echo string(b) - redir END - let l = split(result, "\n") - call assert_equal(["{'a': [1, 2, 3], 'b': [...]}", - \ "{'a': [1, 2, 3], 'b': [1, 2, 3]}"], l) - - call assert_fails('echo &:', 'E112:') - call assert_fails('echo &g:', 'E112:') - call assert_fails('echo &l:', 'E112:') - -endfunc - -"------------------------------------------------------------------------------- -" Test 94: 64-bit Numbers {{{1 -"------------------------------------------------------------------------------- - -func Test_num64() - call assert_notequal( 4294967296, 0) - call assert_notequal(-4294967296, 0) - call assert_equal( 4294967296, 0xFFFFffff + 1) - call assert_equal(-4294967296, -0xFFFFffff - 1) - - call assert_equal( 9223372036854775807, 1 / 0) - call assert_equal(-9223372036854775807, -1 / 0) - call assert_equal(-9223372036854775807 - 1, 0 / 0) - - call assert_equal( 0x7FFFffffFFFFffff, float2nr( 1.0e150)) - call assert_equal(-0x7FFFffffFFFFffff, float2nr(-1.0e150)) - - let rng = range(0xFFFFffff, 0x100000001) - call assert_equal([0xFFFFffff, 0x100000000, 0x100000001], rng) - call assert_equal(0x100000001, max(rng)) - call assert_equal(0xFFFFffff, min(rng)) - call assert_equal(rng, sort(range(0x100000001, 0xFFFFffff, -1), 'N')) -endfunc - -"------------------------------------------------------------------------------- -" Test 95: lines of :append, :change, :insert {{{1 -"------------------------------------------------------------------------------- - -func DefineFunction(name, body) - let func = join(['function! ' . a:name . '()'] + a:body + ['endfunction'], "\n") - exec func -endfunc - -func Test_script_lines() - " :append - try - call DefineFunction('T_Append', [ - \ 'append', - \ 'py <<EOS', - \ '.', - \ ]) - catch - call assert_report("Can't define function") - endtry - try - call DefineFunction('T_Append', [ - \ 'append', - \ 'abc', - \ ]) - call assert_report("Shouldn't be able to define function") - catch - call assert_exception('Vim(function):E1145: Missing heredoc end marker: .') - endtry - - " :change - try - call DefineFunction('T_Change', [ - \ 'change', - \ 'py <<EOS', - \ '.', - \ ]) - catch - call assert_report("Can't define function") - endtry - try - call DefineFunction('T_Change', [ - \ 'change', - \ 'abc', - \ ]) - call assert_report("Shouldn't be able to define function") - catch - call assert_exception('Vim(function):E1145: Missing heredoc end marker: .') - endtry - - " :insert - try - call DefineFunction('T_Insert', [ - \ 'insert', - \ 'py <<EOS', - \ '.', - \ ]) - catch - call assert_report("Can't define function") - endtry - try - call DefineFunction('T_Insert', [ - \ 'insert', - \ 'abc', - \ ]) - call assert_report("Shouldn't be able to define function") - catch - call assert_exception('Vim(function):E1145: Missing heredoc end marker: .') - endtry -endfunc - -"------------------------------------------------------------------------------- -" Test 96: line continuation {{{1 -" -" Undefined behavior was detected by ubsan with line continuation -" after an empty line. -"------------------------------------------------------------------------------- -func Test_script_empty_line_continuation() - - \ -endfunc - -"------------------------------------------------------------------------------- -" Test 97: bitwise functions {{{1 -"------------------------------------------------------------------------------- -func Test_bitwise_functions() - " and - call assert_equal(127, and(127, 127)) - call assert_equal(16, and(127, 16)) - eval 127->and(16)->assert_equal(16) - call assert_equal(0, and(127, 128)) - call assert_fails("call and([], 1)", 'E745:') - call assert_fails("call and({}, 1)", 'E728:') - call assert_fails("call and(1.0, 1)", 'E805:') - call assert_fails("call and(1, 1.0)", 'E805:') - call assert_fails("call and(1, [])", 'E745:') - call assert_fails("call and(1, {})", 'E728:') - " or - call assert_equal(23, or(16, 7)) - call assert_equal(15, or(8, 7)) - eval 8->or(7)->assert_equal(15) - call assert_equal(123, or(0, 123)) - call assert_fails("call or([], 1)", 'E745:') - call assert_fails("call or({}, 1)", 'E728:') - call assert_fails("call or(1.0, 1)", 'E805:') - call assert_fails("call or(1, 1.0)", 'E805:') - call assert_fails("call or(1, [])", 'E745:') - call assert_fails("call or(1, {})", 'E728:') - " xor - call assert_equal(0, xor(127, 127)) - call assert_equal(111, xor(127, 16)) - eval 127->xor(16)->assert_equal(111) - call assert_equal(255, xor(127, 128)) - call assert_fails("call xor(1.0, 1)", 'E805:') - call assert_fails("call xor(1, 1.0)", 'E805:') - call assert_fails("call xor([], 1)", 'E745:') - call assert_fails("call xor({}, 1)", 'E728:') - call assert_fails("call xor(1, [])", 'E745:') - call assert_fails("call xor(1, {})", 'E728:') - " invert - call assert_equal(65408, and(invert(127), 65535)) - eval 127->invert()->and(65535)->assert_equal(65408) - call assert_equal(65519, and(invert(16), 65535)) - call assert_equal(65407, and(invert(128), 65535)) - call assert_fails("call invert(1.0)", 'E805:') - call assert_fails("call invert([])", 'E745:') - call assert_fails("call invert({})", 'E728:') -endfunc - -" Test using bang after user command {{{1 -func Test_user_command_with_bang() - command -bang Nieuw let nieuw = 1 - Ni! - call assert_equal(1, nieuw) - unlet nieuw - delcommand Nieuw -endfunc - -func Test_script_expand_sfile() - let lines =<< trim END - func s:snr() - return expand('<sfile>') - endfunc - let g:result = s:snr() - END - call writefile(lines, 'Xexpand', 'D') - source Xexpand - call assert_match('<SNR>\d\+_snr', g:result) - source Xexpand - call assert_match('<SNR>\d\+_snr', g:result) - - unlet g:result -endfunc - -func Test_compound_assignment_operators() - " Test for number - let x = 1 - let x += 10 - call assert_equal(11, x) - let x -= 5 - call assert_equal(6, x) - let x *= 4 - call assert_equal(24, x) - let x /= 3 - call assert_equal(8, x) - let x %= 3 - call assert_equal(2, x) - let x .= 'n' - call assert_equal('2n', x) - - " Test special cases: division or modulus with 0. - let x = 1 - let x /= 0 - call assert_equal(0x7FFFFFFFFFFFFFFF, x) - - let x = -1 - let x /= 0 - call assert_equal(-0x7FFFFFFFFFFFFFFF, x) - - let x = 0 - let x /= 0 - call assert_equal(-0x7FFFFFFFFFFFFFFF - 1, x) - - let x = 1 - let x %= 0 - call assert_equal(0, x) - - let x = -1 - let x %= 0 - call assert_equal(0, x) - - let x = 0 - let x %= 0 - call assert_equal(0, x) - - " Test for string - let x = 'str' - let x .= 'ing' - call assert_equal('string', x) - let x += 1 - call assert_equal(1, x) - - " Test for float - let x -= 1.5 - call assert_equal(-0.5, x) - let x = 0.5 - let x += 4.5 - call assert_equal(5.0, x) - let x -= 1.5 - call assert_equal(3.5, x) - let x *= 3.0 - call assert_equal(10.5, x) - let x /= 2.5 - call assert_equal(4.2, x) - call assert_fails('let x %= 0.5', 'E734:') - call assert_fails('let x .= "f"', 'E734:') - let x = !3.14 - call assert_equal(0.0, x) - - " integer and float operations - let x = 1 - let x *= 2.1 - call assert_equal(2.1, x) - let x = 1 - let x /= 0.25 - call assert_equal(4.0, x) - let x = 1 - call assert_fails('let x %= 0.25', 'E734:') - let x = 1 - call assert_fails('let x .= 0.25', 'E734:') - let x = 1.0 - call assert_fails('let x += [1.1]', 'E734:') - - " Test for environment variable - let $FOO = 1 - call assert_fails('let $FOO += 1', 'E734:') - call assert_fails('let $FOO -= 1', 'E734:') - call assert_fails('let $FOO *= 1', 'E734:') - call assert_fails('let $FOO /= 1', 'E734:') - call assert_fails('let $FOO %= 1', 'E734:') - let $FOO .= 's' - call assert_equal('1s', $FOO) - unlet $FOO - - " Test for option variable (type: number) - let &scrolljump = 1 - let &scrolljump += 5 - call assert_equal(6, &scrolljump) - let &scrolljump -= 2 - call assert_equal(4, &scrolljump) - let &scrolljump *= 3 - call assert_equal(12, &scrolljump) - let &scrolljump /= 2 - call assert_equal(6, &scrolljump) - let &scrolljump %= 5 - call assert_equal(1, &scrolljump) - call assert_fails('let &scrolljump .= "j"', ['E734:', 'E734:']) - set scrolljump&vim - - let &foldlevelstart = 2 - let &foldlevelstart -= 1 - call assert_equal(1, &foldlevelstart) - let &foldlevelstart -= 1 - call assert_equal(0, &foldlevelstart) - let &foldlevelstart = 2 - let &foldlevelstart -= 2 - call assert_equal(0, &foldlevelstart) - - " Test for register - let @/ = 1 - call assert_fails('let @/ += 1', 'E734:') - call assert_fails('let @/ -= 1', 'E734:') - call assert_fails('let @/ *= 1', 'E734:') - call assert_fails('let @/ /= 1', 'E734:') - call assert_fails('let @/ %= 1', 'E734:') - let @/ .= 's' - call assert_equal('1s', @/) - let @/ = '' -endfunc - -func Test_unlet_env() - let $TESTVAR = 'yes' - call assert_equal('yes', $TESTVAR) - call assert_fails('lockvar $TESTVAR', 'E940:') - call assert_fails('unlockvar $TESTVAR', 'E940:') - call assert_equal('yes', $TESTVAR) - if 0 - unlet $TESTVAR - endif - call assert_equal('yes', $TESTVAR) - unlet $TESTVAR - call assert_equal('', $TESTVAR) -endfunc - -func Test_refcount() - " Immediate values - call assert_equal(-1, test_refcount(1)) - call assert_equal(-1, test_refcount('s')) - call assert_equal(-1, test_refcount(v:true)) - call assert_equal(0, test_refcount([])) - call assert_equal(0, test_refcount({})) - call assert_equal(0, test_refcount(0zff)) - call assert_equal(0, test_refcount({-> line('.')})) - call assert_equal(-1, test_refcount(0.1)) - if has('job') - call assert_equal(0, test_refcount(job_start([&shell, &shellcmdflag, 'echo .']))) - endif - - " No refcount types - let x = 1 - call assert_equal(-1, test_refcount(x)) - let x = 's' - call assert_equal(-1, test_refcount(x)) - let x = v:true - call assert_equal(-1, test_refcount(x)) - let x = 0.1 - call assert_equal(-1, test_refcount(x)) - - " Check refcount - let x = [] - call assert_equal(1, test_refcount(x)) - - let x = {} - call assert_equal(1, x->test_refcount()) - - let x = 0zff - call assert_equal(1, test_refcount(x)) - - let X = {-> line('.')} - call assert_equal(1, test_refcount(X)) - let Y = X - call assert_equal(2, test_refcount(X)) - - if has('job') - let job = job_start([&shell, &shellcmdflag, 'echo .']) - call assert_equal(1, test_refcount(job)) - call assert_equal(1, test_refcount(job_getchannel(job))) - call assert_equal(1, test_refcount(job)) - endif - - " Function arguments, copying and unassigning - func ExprCheck(x, i) - let i = a:i + 1 - call assert_equal(i, test_refcount(a:x)) - let Y = a:x - call assert_equal(i + 1, test_refcount(a:x)) - call assert_equal(test_refcount(a:x), test_refcount(Y)) - let Y = 0 - call assert_equal(i, test_refcount(a:x)) - endfunc - call ExprCheck([], 0) - call ExprCheck({}, 0) - call ExprCheck(0zff, 0) - call ExprCheck({-> line('.')}, 0) - if has('job') - call ExprCheck(job, 1) - call ExprCheck(job_getchannel(job), 1) - call job_stop(job) - endif - delfunc ExprCheck - - " Regarding function - func Func(x) abort - call assert_equal(2, test_refcount(function('Func'))) - call assert_equal(0, test_refcount(funcref('Func'))) - endfunc - call assert_equal(1, test_refcount(function('Func'))) - call assert_equal(0, test_refcount(function('Func', [1]))) - call assert_equal(0, test_refcount(funcref('Func'))) - call assert_equal(0, test_refcount(funcref('Func', [1]))) - let X = function('Func') - let Y = X - call assert_equal(1, test_refcount(X)) - let X = function('Func', [1]) - let Y = X - call assert_equal(2, test_refcount(X)) - let X = funcref('Func') - let Y = X - call assert_equal(2, test_refcount(X)) - let X = funcref('Func', [1]) - let Y = X - call assert_equal(2, test_refcount(X)) - unlet X - unlet Y - call Func(1) - delfunc Func - - " Function with dict - func DictFunc() dict - call assert_equal(3, test_refcount(self)) - endfunc - let d = {'Func': function('DictFunc')} - call assert_equal(1, test_refcount(d)) - call assert_equal(0, test_refcount(d.Func)) - call d.Func() - unlet d - delfunc DictFunc - - if has('channel') - call assert_equal(-1, test_refcount(test_null_job())) - call assert_equal(-1, test_refcount(test_null_channel())) - endif - call assert_equal(-1, test_refcount(test_null_function())) - call assert_equal(-1, test_refcount(test_null_partial())) - call assert_equal(-1, test_refcount(test_null_blob())) - call assert_equal(-1, test_refcount(test_null_list())) - call assert_equal(-1, test_refcount(test_null_dict())) -endfunc - -" Test for missing :endif, :endfor, :endwhile and :endtry {{{1 -func Test_missing_end() - call writefile(['if 2 > 1', 'echo ">"'], 'Xscript', 'D') - call assert_fails('source Xscript', 'E171:') - call writefile(['for i in range(5)', 'echo i'], 'Xscript') - call assert_fails('source Xscript', 'E170:') - call writefile(['while v:true', 'echo "."'], 'Xscript') - call assert_fails('source Xscript', 'E170:') - call writefile(['try', 'echo "."'], 'Xscript') - call assert_fails('source Xscript', 'E600:') - - " Using endfor with :while - let caught_e732 = 0 - try - while v:true - endfor - catch /E732:/ - let caught_e732 = 1 - endtry - call assert_equal(1, caught_e732) - - " Using endwhile with :for - let caught_e733 = 0 - try - for i in range(1) - endwhile - catch /E733:/ - let caught_e733 = 1 - endtry - call assert_equal(1, caught_e733) - - " Using endfunc with :if - call assert_fails('exe "if 1 | endfunc | endif"', 'E193:') - - " Missing 'in' in a :for statement - call assert_fails('for i range(1) | endfor', 'E690:') - - " Incorrect number of variables in for - call assert_fails('for [i,] in range(3) | endfor', 'E475:') -endfunc - -" Test for deep nesting of if/for/while/try statements {{{1 -func Test_deep_nest() - CheckRunVimInTerminal - - let lines =<< trim [SCRIPT] - " Deep nesting of if ... endif - func Test1() - let @a = join(repeat(['if v:true'], 51), "\n") - let @a ..= "\n" - let @a ..= join(repeat(['endif'], 51), "\n") - @a - let @a = '' - endfunc - - " Deep nesting of for ... endfor - func Test2() - let @a = join(repeat(['for i in [1]'], 51), "\n") - let @a ..= "\n" - let @a ..= join(repeat(['endfor'], 51), "\n") - @a - let @a = '' - endfunc - - " Deep nesting of while ... endwhile - func Test3() - let @a = join(repeat(['while v:true'], 51), "\n") - let @a ..= "\n" - let @a ..= join(repeat(['endwhile'], 51), "\n") - @a - let @a = '' - endfunc - - " Deep nesting of try ... endtry - func Test4() - let @a = join(repeat(['try'], 51), "\n") - let @a ..= "\necho v:true\n" - let @a ..= join(repeat(['endtry'], 51), "\n") - @a - let @a = '' - endfunc - - " Deep nesting of function ... endfunction - func Test5() - let @a = join(repeat(['function X()'], 51), "\n") - let @a ..= "\necho v:true\n" - let @a ..= join(repeat(['endfunction'], 51), "\n") - @a - let @a = '' - endfunc - [SCRIPT] - call writefile(lines, 'Xscript', 'D') - - let buf = RunVimInTerminal('-S Xscript', {'rows': 6}) - - " Deep nesting of if ... endif - call term_sendkeys(buf, ":call Test1()\n") - call TermWait(buf) - call WaitForAssert({-> assert_match('^E579:', term_getline(buf, 5))}) - - " Deep nesting of for ... endfor - call term_sendkeys(buf, ":call Test2()\n") - call TermWait(buf) - call WaitForAssert({-> assert_match('^E585:', term_getline(buf, 5))}) - - " Deep nesting of while ... endwhile - call term_sendkeys(buf, ":call Test3()\n") - call TermWait(buf) - call WaitForAssert({-> assert_match('^E585:', term_getline(buf, 5))}) - - " Deep nesting of try ... endtry - call term_sendkeys(buf, ":call Test4()\n") - call TermWait(buf) - call WaitForAssert({-> assert_match('^E601:', term_getline(buf, 5))}) - - " Deep nesting of function ... endfunction - call term_sendkeys(buf, ":call Test5()\n") - call TermWait(buf) - call WaitForAssert({-> assert_match('^E1058:', term_getline(buf, 4))}) - call term_sendkeys(buf, "\<C-C>\n") - call TermWait(buf) - - "let l = '' - "for i in range(1, 6) - " let l ..= term_getline(buf, i) . "\n" - "endfor - "call assert_report(l) - - call StopVimInTerminal(buf) -endfunc - -" Test for errors in converting to float from various types {{{1 -func Test_float_conversion_errors() - call assert_fails('let x = 4.0 % 2.0', 'E804:') - call assert_fails('echo 1.1[0]', 'E806:') - call assert_fails('echo sort([function("min"), 1], "f")', 'E891:') - call assert_fails('echo 3.2 == "vim"', 'E892:') - call assert_fails('echo sort([[], 1], "f")', 'E893:') - call assert_fails('echo sort([{}, 1], "f")', 'E894:') - call assert_fails('echo 3.2 == v:true', 'E362:') - call assert_fails('echo 3.2 == v:none', 'E907:') -endfunc - -" invalid function names {{{1 -func Test_invalid_function_names() - " function name not starting with capital - let caught_e128 = 0 - try - func! g:test() - echo "test" - endfunc - catch /E128:/ - let caught_e128 = 1 - endtry - call assert_equal(1, caught_e128) - - " function name includes a colon - let caught_e884 = 0 - try - func! b:test() - echo "test" - endfunc - catch /E884:/ - let caught_e884 = 1 - endtry - call assert_equal(1, caught_e884) - - " function name followed by # - let caught_e128 = 0 - try - func! test2() "# - echo "test2" - endfunc - catch /E128:/ - let caught_e128 = 1 - endtry - call assert_equal(1, caught_e128) - - " function name starting with/without "g:", buffer-local funcref. - function! g:Foo(n) - return 'called Foo(' . a:n . ')' - endfunction - let b:my_func = function('Foo') - call assert_equal('called Foo(1)', b:my_func(1)) - call assert_equal('called Foo(2)', g:Foo(2)) - call assert_equal('called Foo(3)', Foo(3)) - delfunc g:Foo - - " script-local function used in Funcref must exist. - let lines =<< trim END - func s:Testje() - return "foo" - endfunc - let Bar = function('s:Testje') - call assert_equal(0, exists('s:Testje')) - call assert_equal(1, exists('*s:Testje')) - call assert_equal(1, exists('Bar')) - call assert_equal(1, exists('*Bar')) - END - call writefile(lines, 'Xscript', 'D') - source Xscript -endfunc - -" substring and variable name {{{1 -func Test_substring_var() - let str = 'abcdef' - let n = 3 - call assert_equal('def', str[n:]) - call assert_equal('abcd', str[:n]) - call assert_equal('d', str[n:n]) - unlet n - let nn = 3 - call assert_equal('def', str[nn:]) - call assert_equal('abcd', str[:nn]) - call assert_equal('d', str[nn:nn]) - unlet nn - let b:nn = 4 - call assert_equal('ef', str[b:nn:]) - call assert_equal('abcde', str[:b:nn]) - call assert_equal('e', str[b:nn:b:nn]) - unlet b:nn -endfunc - -" Test using s: with a typed command {{{1 -func Test_typed_script_var() - CheckRunVimInTerminal - - let buf = RunVimInTerminal('', {'rows': 6}) - - " Deep nesting of if ... endif - call term_sendkeys(buf, ":echo get(s:, 'foo', 'x')\n") - call TermWait(buf) - call WaitForAssert({-> assert_match('^E116:', term_getline(buf, 5))}) - - call StopVimInTerminal(buf) -endfunc - -" Test for issue6776 {{{1 -func Test_ternary_expression() - try - call eval('0 ? 0') - catch - endtry - " previous failure should not cause next expression to fail - call assert_equal(v:false, eval(string(v:false))) - - try - call eval('0 ? "burp') - catch - endtry - " previous failure should not cause next expression to fail - call assert_equal(v:false, eval(string(v:false))) - - try - call eval('1 ? 0 : "burp') - catch - endtry - " previous failure should not cause next expression to fail - call assert_equal(v:false, eval(string(v:false))) -endfunction - -func Test_for_over_string() - let res = '' - for c in 'aéc̀d' - let res ..= c .. '-' - endfor - call assert_equal('a-é-c̀-d-', res) - - let res = '' - for c in '' - let res ..= c .. '-' - endfor - call assert_equal('', res) - - let res = '' - for c in test_null_string() - let res ..= c .. '-' - endfor - call assert_equal('', res) -endfunc - -" Test for deeply nested :source command {{{1 -func Test_deeply_nested_source() - let lines =<< trim END - - so - sil 0scr - delete - so - 0 - END - call writefile(["vim9 silent! @0 \n/"] + lines, 'Xnested.vim', 'D') - - " this must not crash - let cmd = GetVimCommand() .. " -e -s -S Xnested.vim -c qa!" - call system(cmd) -endfunc - -"------------------------------------------------------------------------------- -" Modelines {{{1 -" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker -"------------------------------------------------------------------------------- +" Test various aspects of the Vim script language. +" Most of this was formerly in test49.vim (developed by Servatius Brandt +" <Servatius.Brandt@fujitsu-siemens.com>) + +source check.vim +source shared.vim +source script_util.vim + +"------------------------------------------------------------------------------- +" Test environment {{{1 +"------------------------------------------------------------------------------- + +" Append a message to the "messages" file +func Xout(text) + split messages + $put =a:text + wq +endfunc + +com! -nargs=1 Xout call Xout(<args>) + +" Create a new instance of Vim and run the commands in 'test' and then 'verify' +" The commands in 'test' are expected to store the test results in the Xtest.out +" file. If the test passes successfully, then Xtest.out should be empty. +func RunInNewVim(test, verify) + let init =<< trim END + set cpo-=C " support line-continuation in sourced script + source script_util.vim + XpathINIT + XloopINIT + END + let cleanup =<< trim END + call writefile(v:errors, 'Xtest.out') + qall + END + call writefile(init, 'Xtest.vim', 'D') + call writefile(a:test, 'Xtest.vim', 'a') + call writefile(a:verify, 'Xverify.vim', 'D') + call writefile(cleanup, 'Xverify.vim', 'a') + call RunVim([], [], "-S Xtest.vim -S Xverify.vim") + call assert_equal([], readfile('Xtest.out')) + call delete('Xtest.out') +endfunc + +"------------------------------------------------------------------------------- +" Test 1: :endwhile in function {{{1 +" +" Detect if a broken loop is (incorrectly) reactivated by the +" :endwhile. Use a :return to prevent an endless loop, and make +" this test first to get a meaningful result on an error before other +" tests will hang. +"------------------------------------------------------------------------------- + +func T1_F() + Xpath 'a' + let first = 1 + while 1 + Xpath 'b' + if first + Xpath 'c' + let first = 0 + break + else + Xpath 'd' + return + endif + endwhile +endfunc + +func T1_G() + Xpath 'h' + let first = 1 + while 1 + Xpath 'i' + if first + Xpath 'j' + let first = 0 + break + else + Xpath 'k' + return + endif + if 1 " unmatched :if + endwhile +endfunc + +func Test_endwhile_function() + XpathINIT + call T1_F() + Xpath 'F' + + try + call T1_G() + catch + " Catch missing :endif + call assert_true(v:exception =~ 'E171:') + Xpath 'x' + endtry + Xpath 'G' + + call assert_equal('abcFhijxG', g:Xpath) +endfunc + +"------------------------------------------------------------------------------- +" Test 2: :endwhile in script {{{1 +" +" Detect if a broken loop is (incorrectly) reactivated by the +" :endwhile. Use a :finish to prevent an endless loop, and place +" this test before others that might hang to get a meaningful result +" on an error. +" +" This test executes the bodies of the functions T1_F and T1_G from +" the previous test as script files (:return replaced by :finish). +"------------------------------------------------------------------------------- + +func Test_endwhile_script() + XpathINIT + ExecAsScript T1_F + Xpath 'F' + call DeleteTheScript() + + try + ExecAsScript T1_G + catch + " Catch missing :endif + call assert_true(v:exception =~ 'E171:') + Xpath 'x' + endtry + Xpath 'G' + call DeleteTheScript() + + call assert_equal('abcFhijxG', g:Xpath) +endfunc + +"------------------------------------------------------------------------------- +" Test 3: :if, :elseif, :while, :continue, :break {{{1 +"------------------------------------------------------------------------------- + +func Test_if_while() + XpathINIT + if 1 + Xpath 'a' + let loops = 3 + while loops > -1 " main loop: loops == 3, 2, 1 (which breaks) + if loops <= 0 + let break_err = 1 + let loops = -1 + else + Xpath 'b' . loops + endif + if (loops == 2) + while loops == 2 " dummy loop + Xpath 'c' . loops + let loops = loops - 1 + continue " stop dummy loop + Xpath 'd' . loops + endwhile + continue " continue main loop + Xpath 'e' . loops + elseif (loops == 1) + let p = 1 + while p " dummy loop + Xpath 'f' . loops + let p = 0 + break " break dummy loop + Xpath 'g' . loops + endwhile + Xpath 'h' . loops + unlet p + break " break main loop + Xpath 'i' . loops + endif + if (loops > 0) + Xpath 'j' . loops + endif + while loops == 3 " dummy loop + let loops = loops - 1 + endwhile " end dummy loop + endwhile " end main loop + Xpath 'k' + else + Xpath 'l' + endif + Xpath 'm' + if exists("break_err") + Xpath 'm' + unlet break_err + endif + + unlet loops + + call assert_equal('ab3j3b2c2b1f1h1km', g:Xpath) +endfunc + +" Check double quote after skipped "elseif" does not give error E15 +func Test_skipped_elseif() + if "foo" ==? "foo" + let result = "first" + elseif "foo" ==? "foo" + let result = "second" + endif + call assert_equal('first', result) +endfunc + +"------------------------------------------------------------------------------- +" Test 4: :return {{{1 +"------------------------------------------------------------------------------- + +func T4_F() + if 1 + Xpath 'a' + let loops = 3 + while loops > 0 " 3: 2: 1: + Xpath 'b' . loops + if (loops == 2) + Xpath 'c' . loops + return + Xpath 'd' . loops + endif + Xpath 'e' . loops + let loops = loops - 1 + endwhile + Xpath 'f' + else + Xpath 'g' + endif +endfunc + +func Test_return() + XpathINIT + call T4_F() + Xpath '4' + + call assert_equal('ab3e3b2c24', g:Xpath) +endfunc + + +"------------------------------------------------------------------------------- +" Test 5: :finish {{{1 +" +" This test executes the body of the function T4_F from the previous +" test as a script file (:return replaced by :finish). +"------------------------------------------------------------------------------- + +func Test_finish() + XpathINIT + ExecAsScript T4_F + Xpath '5' + call DeleteTheScript() + + call assert_equal('ab3e3b2c25', g:Xpath) +endfunc + + + +"------------------------------------------------------------------------------- +" Test 6: Defining functions in :while loops {{{1 +" +" Functions can be defined inside other functions. An inner function +" gets defined when the outer function is executed. Functions may +" also be defined inside while loops. Expressions in braces for +" defining the function name are allowed. +" +" The functions are defined when sourcing the script, only the +" resulting path is checked in the test function. +"------------------------------------------------------------------------------- + +XpathINIT + +" The command CALL collects the argument of all its invocations in "calls" +" when used from a function (that is, when the global variable "calls" needs +" the "g:" prefix). This is to check that the function code is skipped when +" the function is defined. For inner functions, do so only if the outer +" function is not being executed. +" +let calls = "" +com! -nargs=1 CALL + \ if !exists("calls") && !exists("outer") | + \ let g:calls = g:calls . <args> | + \ endif + +let i = 0 +while i < 3 + let i = i + 1 + if i == 1 + Xpath 'a' + function! F1(arg) + CALL a:arg + let outer = 1 + + let j = 0 + while j < 1 + Xpath 'b' + let j = j + 1 + function! G1(arg) + CALL a:arg + endfunction + Xpath 'c' + endwhile + endfunction + Xpath 'd' + + continue + endif + + Xpath 'e' . i + function! F{i}(i, arg) + CALL a:arg + let outer = 1 + + if a:i == 3 + Xpath 'f' + endif + let k = 0 + while k < 3 + Xpath 'g' . k + let k = k + 1 + function! G{a:i}{k}(arg) + CALL a:arg + endfunction + Xpath 'h' . k + endwhile + endfunction + Xpath 'i' + +endwhile + +if exists("*G1") + Xpath 'j' +endif +if exists("*F1") + call F1("F1") + if exists("*G1") + call G1("G1") + endif +endif + +if exists("G21") || exists("G22") || exists("G23") + Xpath 'k' +endif +if exists("*F2") + call F2(2, "F2") + if exists("*G21") + call G21("G21") + endif + if exists("*G22") + call G22("G22") + endif + if exists("*G23") + call G23("G23") + endif +endif + +if exists("G31") || exists("G32") || exists("G33") + Xpath 'l' +endif +if exists("*F3") + call F3(3, "F3") + if exists("*G31") + call G31("G31") + endif + if exists("*G32") + call G32("G32") + endif + if exists("*G33") + call G33("G33") + endif +endif + +Xpath 'm' + +let g:test6_result = g:Xpath +let g:test6_calls = calls + +unlet calls +delfunction F1 +delfunction G1 +delfunction F2 +delfunction G21 +delfunction G22 +delfunction G23 +delfunction G31 +delfunction G32 +delfunction G33 + +func Test_defining_functions() + call assert_equal('ade2ie3ibcg0h1g1h2g2h3fg0h1g1h2g2h3m', g:test6_result) + call assert_equal('F1G1F2G21G22G23F3G31G32G33', g:test6_calls) +endfunc + +"------------------------------------------------------------------------------- +" Test 7: Continuing on errors outside functions {{{1 +" +" On an error outside a function, the script processing continues +" at the line following the outermost :endif or :endwhile. When not +" inside an :if or :while, the script processing continues at the next +" line. +"------------------------------------------------------------------------------- + +XpathINIT + +if 1 + Xpath 'a' + while 1 + Xpath 'b' + asdf + Xpath 'c' + break + endwhile | Xpath 'd' + Xpath 'e' +endif | Xpath 'f' +Xpath 'g' + +while 1 + Xpath 'h' + if 1 + Xpath 'i' + asdf + Xpath 'j' + endif | Xpath 'k' + Xpath 'l' + break +endwhile | Xpath 'm' +Xpath 'n' + +asdf +Xpath 'o' + +asdf | Xpath 'p' +Xpath 'q' + +let g:test7_result = g:Xpath + +func Test_error_in_script() + call assert_equal('abghinoq', g:test7_result) +endfunc + +"------------------------------------------------------------------------------- +" Test 8: Aborting and continuing on errors inside functions {{{1 +" +" On an error inside a function without the "abort" attribute, the +" script processing continues at the next line (unless the error was +" in a :return command). On an error inside a function with the +" "abort" attribute, the function is aborted and the script processing +" continues after the function call; the value -1 is returned then. +"------------------------------------------------------------------------------- + +XpathINIT + +func T8_F() + if 1 + Xpath 'a' + while 1 + Xpath 'b' + asdf + Xpath 'c' + asdf | Xpath 'd' + Xpath 'e' + break + endwhile + Xpath 'f' + endif | Xpath 'g' + Xpath 'h' + + while 1 + Xpath 'i' + if 1 + Xpath 'j' + asdf + Xpath 'k' + asdf | Xpath 'l' + Xpath 'm' + endif + Xpath 'n' + break + endwhile | Xpath 'o' + Xpath 'p' + + return novar " returns (default return value 0) + Xpath 'q' + return 1 " not reached +endfunc + +func T8_G() abort + if 1 + Xpath 'r' + while 1 + Xpath 's' + asdf " returns -1 + Xpath 't' + break + endwhile + Xpath 'v' + endif | Xpath 'w' + Xpath 'x' + + return -4 " not reached +endfunc + +func T8_H() abort + while 1 + Xpath 'A' + if 1 + Xpath 'B' + asdf " returns -1 + Xpath 'C' + endif + Xpath 'D' + break + endwhile | Xpath 'E' + Xpath 'F' + + return -4 " not reached +endfunc + +" Aborted functions (T8_G and T8_H) return -1. +let g:test8_sum = (T8_F() + 1) - 4 * T8_G() - 8 * T8_H() +Xpath 'X' +let g:test8_result = g:Xpath + +func Test_error_in_function() + call assert_equal(13, g:test8_sum) + call assert_equal('abcefghijkmnoprsABX', g:test8_result) + + delfunction T8_F + delfunction T8_G + delfunction T8_H +endfunc + + +"------------------------------------------------------------------------------- +" Test 9: Continuing after aborted functions {{{1 +" +" When a function with the "abort" attribute is aborted due to an +" error, the next function back in the call hierarchy without an +" "abort" attribute continues; the value -1 is returned then. +"------------------------------------------------------------------------------- + +XpathINIT + +func F() abort + Xpath 'a' + let result = G() " not aborted + Xpath 'b' + if result != 2 + Xpath 'c' + endif + return 1 +endfunc + +func G() " no abort attribute + Xpath 'd' + if H() != -1 " aborted + Xpath 'e' + endif + Xpath 'f' + return 2 +endfunc + +func H() abort + Xpath 'g' + call I() " aborted + Xpath 'h' + return 4 +endfunc + +func I() abort + Xpath 'i' + asdf " error + Xpath 'j' + return 8 +endfunc + +if F() != 1 + Xpath 'k' +endif + +let g:test9_result = g:Xpath + +delfunction F +delfunction G +delfunction H +delfunction I + +func Test_func_abort() + call assert_equal('adgifb', g:test9_result) +endfunc + + +"------------------------------------------------------------------------------- +" Test 10: :if, :elseif, :while argument parsing {{{1 +" +" A '"' or '|' in an argument expression must not be mixed up with +" a comment or a next command after a bar. Parsing errors should +" be recognized. +"------------------------------------------------------------------------------- + +XpathINIT + +func MSG(enr, emsg) + let english = v:lang == "C" || v:lang =~ '^[Ee]n' + if a:enr == "" + Xout "TODO: Add message number for:" a:emsg + let v:errmsg = ":" . v:errmsg + endif + let match = 1 + if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg) + let match = 0 + if v:errmsg == "" + Xout "Message missing." + else + let v:errmsg = v:errmsg->escape('"') + Xout "Unexpected message:" v:errmsg + endif + endif + return match +endfunc + +if 1 || strlen("\"") | Xpath 'a' + Xpath 'b' +endif +Xpath 'c' + +if 0 +elseif 1 || strlen("\"") | Xpath 'd' + Xpath 'e' +endif +Xpath 'f' + +while 1 || strlen("\"") | Xpath 'g' + Xpath 'h' + break +endwhile +Xpath 'i' + +let v:errmsg = "" +if 1 ||| strlen("\"") | Xpath 'j' + Xpath 'k' +endif +Xpath 'l' +if !MSG('E15', "Invalid expression") + Xpath 'm' +endif + +let v:errmsg = "" +if 0 +elseif 1 ||| strlen("\"") | Xpath 'n' + Xpath 'o' +endif +Xpath 'p' +if !MSG('E15', "Invalid expression") + Xpath 'q' +endif + +let v:errmsg = "" +while 1 ||| strlen("\"") | Xpath 'r' + Xpath 's' + break +endwhile +Xpath 't' +if !MSG('E15', "Invalid expression") + Xpath 'u' +endif + +let g:test10_result = g:Xpath +delfunction MSG + +func Test_expr_parsing() + call assert_equal('abcdefghilpt', g:test10_result) +endfunc + + +"------------------------------------------------------------------------------- +" Test 11: :if, :elseif, :while argument evaluation after abort {{{1 +" +" When code is skipped over due to an error, the boolean argument to +" an :if, :elseif, or :while must not be evaluated. +"------------------------------------------------------------------------------- + +XpathINIT + +let calls = 0 + +func P(num) + let g:calls = g:calls + a:num " side effect on call + return 0 +endfunc + +if 1 + Xpath 'a' + asdf " error + Xpath 'b' + if P(1) " should not be called + Xpath 'c' + elseif !P(2) " should not be called + Xpath 'd' + else + Xpath 'e' + endif + Xpath 'f' + while P(4) " should not be called + Xpath 'g' + endwhile + Xpath 'h' +endif +Xpath 'x' + +let g:test11_calls = calls +let g:test11_result = g:Xpath + +unlet calls +delfunction P + +func Test_arg_abort() + call assert_equal(0, g:test11_calls) + call assert_equal('ax', g:test11_result) +endfunc + + +"------------------------------------------------------------------------------- +" Test 12: Expressions in braces in skipped code {{{1 +" +" In code skipped over due to an error or inactive conditional, +" an expression in braces as part of a variable or function name +" should not be evaluated. +"------------------------------------------------------------------------------- + +XpathINIT + +func NULL() + Xpath 'a' + return 0 +endfunc + +func ZERO() + Xpath 'b' + return 0 +endfunc + +func! F0() + Xpath 'c' +endfunc + +func! F1(arg) + Xpath 'e' +endfunc + +let V0 = 1 + +Xpath 'f' +echo 0 ? F{NULL() + V{ZERO()}}() : 1 + +Xpath 'g' +if 0 + Xpath 'h' + call F{NULL() + V{ZERO()}}() +endif + +Xpath 'i' +if 1 + asdf " error + Xpath 'j' + call F1(F{NULL() + V{ZERO()}}()) +endif + +Xpath 'k' +if 1 + asdf " error + Xpath 'l' + call F{NULL() + V{ZERO()}}() +endif + +let g:test12_result = g:Xpath + +func Test_braces_skipped() + call assert_equal('fgik', g:test12_result) +endfunc + + +"------------------------------------------------------------------------------- +" Test 13: Failure in argument evaluation for :while {{{1 +" +" A failure in the expression evaluation for the condition of a :while +" causes the whole :while loop until the matching :endwhile being +" ignored. Continuation is at the next following line. +"------------------------------------------------------------------------------- + +XpathINIT + +Xpath 'a' +while asdf + Xpath 'b' + while 1 + Xpath 'c' + break + endwhile + Xpath 'd' + break +endwhile +Xpath 'e' + +while asdf | Xpath 'f' | endwhile | Xpath 'g' +Xpath 'h' +let g:test13_result = g:Xpath + +func Test_while_fail() + call assert_equal('aeh', g:test13_result) +endfunc + + +"------------------------------------------------------------------------------- +" Test 14: Failure in argument evaluation for :if {{{1 +" +" A failure in the expression evaluation for the condition of an :if +" does not cause the corresponding :else or :endif being matched to +" a previous :if/:elseif. Neither of both branches of the failed :if +" are executed. +"------------------------------------------------------------------------------- + +XpathINIT + +function! F() + Xpath 'a' + let x = 0 + if x " false + Xpath 'b' + elseif !x " always true + Xpath 'c' + let x = 1 + if g:boolvar " possibly undefined + Xpath 'd' + else + Xpath 'e' + endif + Xpath 'f' + elseif x " never executed + Xpath 'g' + endif + Xpath 'h' +endfunction + +let boolvar = 1 +call F() +Xpath '-' + +unlet boolvar +call F() +let g:test14_result = g:Xpath + +delfunction F + +func Test_if_fail() + call assert_equal('acdfh-acfh', g:test14_result) +endfunc + + +"------------------------------------------------------------------------------- +" Test 15: Failure in argument evaluation for :if (bar) {{{1 +" +" Like previous test, except that the failing :if ... | ... | :endif +" is in a single line. +"------------------------------------------------------------------------------- + +XpathINIT + +function! F() + Xpath 'a' + let x = 0 + if x " false + Xpath 'b' + elseif !x " always true + Xpath 'c' + let x = 1 + if g:boolvar | Xpath 'd' | else | Xpath 'e' | endif + Xpath 'f' + elseif x " never executed + Xpath 'g' + endif + Xpath 'h' +endfunction + +let boolvar = 1 +call F() +Xpath '-' + +unlet boolvar +call F() +let g:test15_result = g:Xpath + +delfunction F + +func Test_if_bar_fail() + call assert_equal('acdfh-acfh', g:test15_result) +endfunc + +"------------------------------------------------------------------------------- +" Test 16: Double :else or :elseif after :else {{{1 +" +" Multiple :elses or an :elseif after an :else are forbidden. +"------------------------------------------------------------------------------- + +func T16_F() abort + if 0 + Xpath 'a' + else + Xpath 'b' + else " aborts function + Xpath 'c' + endif + Xpath 'd' +endfunc + +func T16_G() abort + if 0 + Xpath 'a' + else + Xpath 'b' + elseif 1 " aborts function + Xpath 'c' + else + Xpath 'd' + endif + Xpath 'e' +endfunc + +func T16_H() abort + if 0 + Xpath 'a' + elseif 0 + Xpath 'b' + else + Xpath 'c' + else " aborts function + Xpath 'd' + endif + Xpath 'e' +endfunc + +func T16_I() abort + if 0 + Xpath 'a' + elseif 0 + Xpath 'b' + else + Xpath 'c' + elseif 1 " aborts function + Xpath 'd' + else + Xpath 'e' + endif + Xpath 'f' +endfunc + +func Test_Multi_Else() + XpathINIT + try + call T16_F() + catch /E583:/ + Xpath 'e' + endtry + call assert_equal('be', g:Xpath) + + XpathINIT + try + call T16_G() + catch /E584:/ + Xpath 'f' + endtry + call assert_equal('bf', g:Xpath) + + XpathINIT + try + call T16_H() + catch /E583:/ + Xpath 'f' + endtry + call assert_equal('cf', g:Xpath) + + XpathINIT + try + call T16_I() + catch /E584:/ + Xpath 'g' + endtry + call assert_equal('cg', g:Xpath) +endfunc + +"------------------------------------------------------------------------------- +" Test 17: Nesting of unmatched :if or :endif inside a :while {{{1 +" +" The :while/:endwhile takes precedence in nesting over an unclosed +" :if or an unopened :endif. +"------------------------------------------------------------------------------- + +" While loops inside a function are continued on error. +func T17_F() + let loops = 3 + while loops > 0 + let loops -= 1 + Xpath 'a' . loops + if (loops == 1) + Xpath 'b' . loops + continue + elseif (loops == 0) + Xpath 'c' . loops + break + elseif 1 + Xpath 'd' . loops + " endif missing! + endwhile " :endwhile after :if 1 + Xpath 'e' +endfunc + +func T17_G() + let loops = 2 + while loops > 0 + let loops -= 1 + Xpath 'a' . loops + if 0 + Xpath 'b' . loops + " endif missing + endwhile " :endwhile after :if 0 +endfunc + +func T17_H() + let loops = 2 + while loops > 0 + let loops -= 1 + Xpath 'a' . loops + " if missing! + endif " :endif without :if in while + Xpath 'b' . loops + endwhile +endfunc + +" Error continuation outside a function is at the outermost :endwhile or :endif. +XpathINIT +let v:errmsg = '' +let loops = 2 +while loops > 0 + let loops -= 1 + Xpath 'a' . loops + if 0 + Xpath 'b' . loops + " endif missing! Following :endwhile fails. +endwhile | Xpath 'c' +Xpath 'd' +call assert_match('E171:', v:errmsg) +call assert_equal('a1d', g:Xpath) + +func Test_unmatched_if_in_while() + XpathINIT + call assert_fails('call T17_F()', 'E171:') + call assert_equal('a2d2a1b1a0c0e', g:Xpath) + + XpathINIT + call assert_fails('call T17_G()', 'E171:') + call assert_equal('a1a0', g:Xpath) + + XpathINIT + call assert_fails('call T17_H()', 'E580:') + call assert_equal('a1b1a0b0', g:Xpath) +endfunc + +"------------------------------------------------------------------------------- +" Test 18: Interrupt (Ctrl-C pressed) {{{1 +" +" On an interrupt, the script processing is terminated immediately. +"------------------------------------------------------------------------------- + +func Test_interrupt_while_if() + let test =<< trim [CODE] + try + if 1 + Xpath 'a' + while 1 + Xpath 'b' + if 1 + Xpath 'c' + call interrupt() + call assert_report('should not get here') + break + finish + endif | call assert_report('should not get here') + call assert_report('should not get here') + endwhile | call assert_report('should not get here') + call assert_report('should not get here') + endif | call assert_report('should not get here') + call assert_report('should not get here') + catch /^Vim:Interrupt$/ + Xpath 'd' + endtry | Xpath 'e' + Xpath 'f' + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcdef', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_interrupt_try() + let test =<< trim [CODE] + try + try + Xpath 'a' + call interrupt() + call assert_report('should not get here') + endtry | call assert_report('should not get here') + call assert_report('should not get here') + catch /^Vim:Interrupt$/ + Xpath 'b' + endtry | Xpath 'c' + Xpath 'd' + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcd', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_interrupt_func_while_if() + let test =<< trim [CODE] + func F() + if 1 + Xpath 'a' + while 1 + Xpath 'b' + if 1 + Xpath 'c' + call interrupt() + call assert_report('should not get here') + break + return + endif | call assert_report('should not get here') + call assert_report('should not get here') + endwhile | call assert_report('should not get here') + call assert_report('should not get here') + endif | call assert_report('should not get here') + call assert_report('should not get here') + endfunc + + Xpath 'd' + try + call F() | call assert_report('should not get here') + catch /^Vim:Interrupt$/ + Xpath 'e' + endtry | Xpath 'f' + Xpath 'g' + [CODE] + let verify =<< trim [CODE] + call assert_equal('dabcefg', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_interrupt_func_try() + let test =<< trim [CODE] + func G() + try + Xpath 'a' + call interrupt() + call assert_report('should not get here') + endtry | call assert_report('should not get here') + call assert_report('should not get here') + endfunc + + Xpath 'b' + try + call G() | call assert_report('should not get here') + catch /^Vim:Interrupt$/ + Xpath 'c' + endtry | Xpath 'd' + Xpath 'e' + [CODE] + let verify =<< trim [CODE] + call assert_equal('bacde', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 19: Aborting on errors inside :try/:endtry {{{1 +" +" An error in a command dynamically enclosed in a :try/:endtry region +" aborts script processing immediately. It does not matter whether +" the failing command is outside or inside a function and whether a +" function has an "abort" attribute. +"------------------------------------------------------------------------------- + +func Test_try_error_abort_1() + let test =<< trim [CODE] + func F() abort + Xpath 'a' + asdf + call assert_report('should not get here') + endfunc + + try + Xpath 'b' + call F() + call assert_report('should not get here') + endtry | call assert_report('should not get here') + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('ba', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_try_error_abort_2() + let test =<< trim [CODE] + func G() + Xpath 'a' + asdf + call assert_report('should not get here') + endfunc + + try + Xpath 'b' + call G() + call assert_report('should not get here') + endtry | call assert_report('should not get here') + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('ba', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_try_error_abort_3() + let test =<< trim [CODE] + try + Xpath 'a' + asdf + call assert_report('should not get here') + endtry | call assert_report('should not get here') + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('a', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_try_error_abort_4() + let test =<< trim [CODE] + if 1 + try + Xpath 'a' + asdf + call assert_report('should not get here') + endtry | call assert_report('should not get here') + endif | call assert_report('should not get here') + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('a', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_try_error_abort_5() + let test =<< trim [CODE] + let p = 1 + while p + let p = 0 + try + Xpath 'a' + asdf + call assert_report('should not get here') + endtry | call assert_report('should not get here') + endwhile | call assert_report('should not get here') + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('a', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_try_error_abort_6() + let test =<< trim [CODE] + let p = 1 + Xpath 'a' + while p + Xpath 'b' + let p = 0 + try + Xpath 'c' + endwhile | call assert_report('should not get here') + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('abc', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 20: Aborting on errors after :try/:endtry {{{1 +" +" When an error occurs after the last active :try/:endtry region has +" been left, termination behavior is as if no :try/:endtry has been +" seen. +"------------------------------------------------------------------------------- + +func Test_error_after_try_1() + let test =<< trim [CODE] + let p = 1 + while p + let p = 0 + Xpath 'a' + try + Xpath 'b' + endtry + asdf + call assert_report('should not get here') + endwhile | call assert_report('should not get here') + Xpath 'c' + [CODE] + let verify =<< trim [CODE] + call assert_equal('abc', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_error_after_try_2() + let test =<< trim [CODE] + while 1 + try + Xpath 'a' + break + call assert_report('should not get here') + endtry + endwhile + Xpath 'b' + asdf + Xpath 'c' + [CODE] + let verify =<< trim [CODE] + call assert_equal('abc', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_error_after_try_3() + let test =<< trim [CODE] + while 1 + try + Xpath 'a' + break + call assert_report('should not get here') + finally + Xpath 'b' + endtry + endwhile + Xpath 'c' + asdf + Xpath 'd' + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcd', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_error_after_try_4() + let test =<< trim [CODE] + while 1 + try + Xpath 'a' + finally + Xpath 'b' + break + call assert_report('should not get here') + endtry + endwhile + Xpath 'c' + asdf + Xpath 'd' + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcd', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_error_after_try_5() + let test =<< trim [CODE] + let p = 1 + while p + let p = 0 + try + Xpath 'a' + continue + call assert_report('should not get here') + endtry + endwhile + Xpath 'b' + asdf + Xpath 'c' + [CODE] + let verify =<< trim [CODE] + call assert_equal('abc', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_error_after_try_6() + let test =<< trim [CODE] + let p = 1 + while p + let p = 0 + try + Xpath 'a' + continue + call assert_report('should not get here') + finally + Xpath 'b' + endtry + endwhile + Xpath 'c' + asdf + Xpath 'd' + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcd', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_error_after_try_7() + let test =<< trim [CODE] + let p = 1 + while p + let p = 0 + try + Xpath 'a' + finally + Xpath 'b' + continue + call assert_report('should not get here') + endtry + endwhile + Xpath 'c' + asdf + Xpath 'd' + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcd', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 21: :finally for :try after :continue/:break/:return/:finish {{{1 +" +" If a :try conditional stays inactive due to a preceding :continue, +" :break, :return, or :finish, its :finally clause should not be +" executed. +"------------------------------------------------------------------------------- + +func Test_finally_after_loop_ctrl_statement() + let test =<< trim [CODE] + func F() + let loops = 2 + while loops > 0 + XloopNEXT + let loops = loops - 1 + try + if loops == 1 + Xloop 'a' + continue + call assert_report('should not get here') + elseif loops == 0 + Xloop 'b' + break + call assert_report('should not get here') + endif + + try " inactive + call assert_report('should not get here') + finally + call assert_report('should not get here') + endtry + finally + Xloop 'c' + endtry + call assert_report('should not get here') + endwhile + + try + Xpath 'd' + return + call assert_report('should not get here') + try " inactive + call assert_report('should not get here') + finally + call assert_report('should not get here') + endtry + finally + Xpath 'e' + endtry + call assert_report('should not get here') + endfunc + + try + Xpath 'f' + call F() + Xpath 'g' + finish + call assert_report('should not get here') + try " inactive + call assert_report('should not get here') + finally + call assert_report('should not get here') + endtry + finally + Xpath 'h' + endtry + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('fa2c2b3c3degh', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 22: :finally for a :try after an error/interrupt/:throw {{{1 +" +" If a :try conditional stays inactive due to a preceding error or +" interrupt or :throw, its :finally clause should not be executed. +"------------------------------------------------------------------------------- + +func Test_finally_after_error_in_func() + let test =<< trim [CODE] + func Error() + try + Xpath 'b' + asdf " aborting error, triggering error exception + call assert_report('should not get here') + endtry + call assert_report('should not get here') + endfunc + + Xpath 'a' + call Error() + call assert_report('should not get here') + + if 1 " not active due to error + try " not active since :if inactive + call assert_report('should not get here') + finally + call assert_report('should not get here') + endtry + endif + + try " not active due to error + call assert_report('should not get here') + finally + call assert_report('should not get here') + endtry + [CODE] + let verify =<< trim [CODE] + call assert_equal('ab', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_finally_after_interrupt() + let test =<< trim [CODE] + func Interrupt() + try + Xpath 'a' + call interrupt() " triggering interrupt exception + call assert_report('should not get here') + endtry + endfunc + + Xpath 'b' + try + call Interrupt() + catch /^Vim:Interrupt$/ + Xpath 'c' + finish + endtry + call assert_report('should not get here') + + if 1 " not active due to interrupt + try " not active since :if inactive + call assert_report('should not get here') + finally + call assert_report('should not get here') + endtry + endif + + try " not active due to interrupt + call assert_report('should not get here') + finally + call assert_report('should not get here') + endtry + [CODE] + let verify =<< trim [CODE] + call assert_equal('bac', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_finally_after_throw() + let test =<< trim [CODE] + func Throw() + Xpath 'a' + throw 'xyz' + endfunc + + Xpath 'b' + call Throw() + call assert_report('should not get here') + + if 1 " not active due to :throw + try " not active since :if inactive + call assert_report('should not get here') + finally + call assert_report('should not get here') + endtry + endif + + try " not active due to :throw + call assert_report('should not get here') + finally + call assert_report('should not get here') + endtry + [CODE] + let verify =<< trim [CODE] + call assert_equal('ba', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 23: :catch clauses for a :try after a :throw {{{1 +" +" If a :try conditional stays inactive due to a preceding :throw, +" none of its :catch clauses should be executed. +"------------------------------------------------------------------------------- + +func Test_catch_after_throw() + let test =<< trim [CODE] + try + Xpath 'a' + throw "xyz" + call assert_report('should not get here') + + if 1 " not active due to :throw + try " not active since :if inactive + call assert_report('should not get here') + catch /xyz/ + call assert_report('should not get here') + endtry + endif + catch /xyz/ + Xpath 'b' + endtry + + Xpath 'c' + throw "abc" + call assert_report('should not get here') + + try " not active due to :throw + call assert_report('should not get here') + catch /abc/ + call assert_report('should not get here') + endtry + [CODE] + let verify =<< trim [CODE] + call assert_equal('abc', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 24: :endtry for a :try after a :throw {{{1 +" +" If a :try conditional stays inactive due to a preceding :throw, +" its :endtry should not rethrow the exception to the next surrounding +" active :try conditional. +"------------------------------------------------------------------------------- + +func Test_endtry_after_throw() + let test =<< trim [CODE] + try " try 1 + try " try 2 + Xpath 'a' + throw "xyz" " makes try 2 inactive + call assert_report('should not get here') + + try " try 3 + call assert_report('should not get here') + endtry " no rethrow to try 1 + catch /xyz/ " should catch although try 2 inactive + Xpath 'b' + endtry + catch /xyz/ " try 1 active, but exception already caught + call assert_report('should not get here') + endtry + Xpath 'c' + [CODE] + let verify =<< trim [CODE] + call assert_equal('abc', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 27: Executing :finally clauses after :return {{{1 +" +" For a :return command dynamically enclosed in a :try/:endtry region, +" :finally clauses are executed and the called function is ended. +"------------------------------------------------------------------------------- + +func T27_F() + try + Xpath 'a' + try + Xpath 'b' + return + call assert_report('should not get here') + finally + Xpath 'c' + endtry + Xpath 'd' + finally + Xpath 'e' + endtry + call assert_report('should not get here') +endfunc + +func T27_G() + try + Xpath 'f' + return + call assert_report('should not get here') + finally + Xpath 'g' + call T27_F() + Xpath 'h' + endtry + call assert_report('should not get here') +endfunc + +func T27_H() + try + Xpath 'i' + call T27_G() + Xpath 'j' + finally + Xpath 'k' + return + call assert_report('should not get here') + endtry + call assert_report('should not get here') +endfunction + +func Test_finally_after_return() + XpathINIT + try + Xpath 'l' + call T27_H() + Xpath 'm' + finally + Xpath 'n' + endtry + call assert_equal('lifgabcehjkmn', g:Xpath) +endfunc + +"------------------------------------------------------------------------------- +" Test 28: Executing :finally clauses after :finish {{{1 +" +" For a :finish command dynamically enclosed in a :try/:endtry region, +" :finally clauses are executed and the sourced file is finished. +" +" This test executes the bodies of the functions F, G, and H from the +" previous test as script files (:return replaced by :finish). +"------------------------------------------------------------------------------- + +func Test_finally_after_finish() + XpathINIT + + let scriptF = MakeScript("T27_F") + let scriptG = MakeScript("T27_G", scriptF) + let scriptH = MakeScript("T27_H", scriptG) + + try + Xpath 'A' + exec "source" scriptH + Xpath 'B' + finally + Xpath 'C' + endtry + Xpath 'D' + call assert_equal('AifgabcehjkBCD', g:Xpath) + call delete(scriptF) + call delete(scriptG) + call delete(scriptH) +endfunc + +"------------------------------------------------------------------------------- +" Test 29: Executing :finally clauses on errors {{{1 +" +" After an error in a command dynamically enclosed in a :try/:endtry +" region, :finally clauses are executed and the script processing is +" terminated. +"------------------------------------------------------------------------------- + +func Test_finally_after_error_1() + let test =<< trim [CODE] + func F() + while 1 + try + Xpath 'a' + while 1 + try + Xpath 'b' + asdf " error + call assert_report('should not get here') + finally + Xpath 'c' + endtry | call assert_report('should not get here') + call assert_report('should not get here') + break + endwhile + call assert_report('should not get here') + finally + Xpath 'd' + endtry | call assert_report('should not get here') + call assert_report('should not get here') + break + endwhile + call assert_report('should not get here') + endfunc + + while 1 + try + Xpath 'e' + while 1 + call F() + call assert_report('should not get here') + break + endwhile | call assert_report('should not get here') + call assert_report('should not get here') + finally + Xpath 'f' + endtry | call assert_report('should not get here') + endwhile | call assert_report('should not get here') + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('eabcdf', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_finally_after_error_2() + let test =<< trim [CODE] + func G() abort + if 1 + try + Xpath 'a' + asdf " error + call assert_report('should not get here') + finally + Xpath 'b' + endtry | Xpath 'c' + endif | Xpath 'd' + call assert_report('should not get here') + endfunc + + if 1 + try + Xpath 'e' + call G() + call assert_report('should not get here') + finally + Xpath 'f' + endtry | call assert_report('should not get here') + endif | call assert_report('should not get here') + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('eabf', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 30: Executing :finally clauses on interrupt {{{1 +" +" After an interrupt in a command dynamically enclosed in +" a :try/:endtry region, :finally clauses are executed and the +" script processing is terminated. +"------------------------------------------------------------------------------- + +func Test_finally_on_interrupt() + let test =<< trim [CODE] + func F() + try + Xloop 'a' + call interrupt() + call assert_report('should not get here') + finally + Xloop 'b' + endtry + call assert_report('should not get here') + endfunc + + try + try + Xpath 'c' + try + Xpath 'd' + call interrupt() + call assert_report('should not get here') + finally + Xpath 'e' + try + Xpath 'f' + try + Xpath 'g' + finally + Xpath 'h' + try + Xpath 'i' + call interrupt() + call assert_report('should not get here') + endtry + call assert_report('should not get here') + endtry + call assert_report('should not get here') + endtry + call assert_report('should not get here') + endtry + call assert_report('should not get here') + finally + Xpath 'j' + try + Xpath 'k' + call F() + call assert_report('should not get here') + finally + Xpath 'l' + try + Xpath 'm' + XloopNEXT + ExecAsScript F + call assert_report('should not get here') + finally + Xpath 'n' + endtry + call assert_report('should not get here') + endtry + call assert_report('should not get here') + endtry + call assert_report('should not get here') + catch /^Vim:Interrupt$/ + Xpath 'o' + endtry + [CODE] + let verify =<< trim [CODE] + call assert_equal('cdefghijka1b1lma2b2no', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 31: Executing :finally clauses after :throw {{{1 +" +" After a :throw dynamically enclosed in a :try/:endtry region, +" :finally clauses are executed and the script processing is +" terminated. +"------------------------------------------------------------------------------- + +func Test_finally_after_throw_2() + let test =<< trim [CODE] + func F() + try + Xloop 'a' + throw "exception" + call assert_report('should not get here') + finally + Xloop 'b' + endtry + call assert_report('should not get here') + endfunc + + try + Xpath 'c' + try + Xpath 'd' + throw "exception" + call assert_report('should not get here') + finally + Xpath 'e' + try + Xpath 'f' + try + Xpath 'g' + finally + Xpath 'h' + try + Xpath 'i' + throw "exception" + call assert_report('should not get here') + endtry + call assert_report('should not get here') + endtry + call assert_report('should not get here') + endtry + call assert_report('should not get here') + endtry + call assert_report('should not get here') + finally + Xpath 'j' + try + Xpath 'k' + call F() + call assert_report('should not get here') + finally + Xpath 'l' + try + Xpath 'm' + XloopNEXT + ExecAsScript F + call assert_report('should not get here') + finally + Xpath 'n' + endtry + call assert_report('should not get here') + endtry + call assert_report('should not get here') + endtry + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('cdefghijka1b1lma2b2n', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 34: :finally reason discarded by :continue {{{1 +" +" When a :finally clause is executed due to a :continue, :break, +" :return, :finish, error, interrupt or :throw, the jump reason is +" discarded by a :continue in the finally clause. +"------------------------------------------------------------------------------- + +func Test_finally_after_continue() + let test =<< trim [CODE] + func C(jump) + XloopNEXT + let loop = 0 + while loop < 2 + let loop = loop + 1 + if loop == 1 + try + if a:jump == "continue" + continue + elseif a:jump == "break" + break + elseif a:jump == "return" || a:jump == "finish" + return + elseif a:jump == "error" + asdf + elseif a:jump == "interrupt" + call interrupt() + let dummy = 0 + elseif a:jump == "throw" + throw "abc" + endif + finally + continue " discards jump that caused the :finally + call assert_report('should not get here') + endtry + call assert_report('should not get here') + elseif loop == 2 + Xloop 'a' + endif + endwhile + endfunc + + call C("continue") + Xpath 'b' + call C("break") + Xpath 'c' + call C("return") + Xpath 'd' + let g:jump = "finish" + ExecAsScript C + unlet g:jump + Xpath 'e' + try + call C("error") + Xpath 'f' + finally + Xpath 'g' + try + call C("interrupt") + Xpath 'h' + finally + Xpath 'i' + call C("throw") + Xpath 'j' + endtry + endtry + Xpath 'k' + [CODE] + let verify =<< trim [CODE] + call assert_equal('a2ba3ca4da5ea6fga7hia8jk', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 35: :finally reason discarded by :break {{{1 +" +" When a :finally clause is executed due to a :continue, :break, +" :return, :finish, error, interrupt or :throw, the jump reason is +" discarded by a :break in the finally clause. +"------------------------------------------------------------------------------- + +func Test_finally_discard_by_break() + let test =<< trim [CODE] + func B(jump) + XloopNEXT + let loop = 0 + while loop < 2 + let loop = loop + 1 + if loop == 1 + try + if a:jump == "continue" + continue + elseif a:jump == "break" + break + elseif a:jump == "return" || a:jump == "finish" + return + elseif a:jump == "error" + asdf + elseif a:jump == "interrupt" + call interrupt() + let dummy = 0 + elseif a:jump == "throw" + throw "abc" + endif + finally + break " discards jump that caused the :finally + call assert_report('should not get here') + endtry + elseif loop == 2 + call assert_report('should not get here') + endif + endwhile + Xloop 'a' + endfunc + + call B("continue") + Xpath 'b' + call B("break") + Xpath 'c' + call B("return") + Xpath 'd' + let g:jump = "finish" + ExecAsScript B + unlet g:jump + Xpath 'e' + try + call B("error") + Xpath 'f' + finally + Xpath 'g' + try + call B("interrupt") + Xpath 'h' + finally + Xpath 'i' + call B("throw") + Xpath 'j' + endtry + endtry + Xpath 'k' + [CODE] + let verify =<< trim [CODE] + call assert_equal('a2ba3ca4da5ea6fga7hia8jk', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 36: :finally reason discarded by :return {{{1 +" +" When a :finally clause is executed due to a :continue, :break, +" :return, :finish, error, interrupt or :throw, the jump reason is +" discarded by a :return in the finally clause. +"------------------------------------------------------------------------------- + +func Test_finally_discard_by_return() + let test =<< trim [CODE] + func R(jump, retval) abort + let loop = 0 + while loop < 2 + let loop = loop + 1 + if loop == 1 + try + if a:jump == "continue" + continue + elseif a:jump == "break" + break + elseif a:jump == "return" + return + elseif a:jump == "error" + asdf + elseif a:jump == "interrupt" + call interrupt() + let dummy = 0 + elseif a:jump == "throw" + throw "abc" + endif + finally + return a:retval " discards jump that caused the :finally + call assert_report('should not get here') + endtry + elseif loop == 2 + call assert_report('should not get here') + endif + endwhile + call assert_report('should not get here') + endfunc + + let sum = -R("continue", -8) + Xpath 'a' + let sum = sum - R("break", -16) + Xpath 'b' + let sum = sum - R("return", -32) + Xpath 'c' + try + let sum = sum - R("error", -64) + Xpath 'd' + finally + Xpath 'e' + try + let sum = sum - R("interrupt", -128) + Xpath 'f' + finally + Xpath 'g' + let sum = sum - R("throw", -256) + Xpath 'h' + endtry + endtry + Xpath 'i' + + let expected = 8 + 16 + 32 + 64 + 128 + 256 + call assert_equal(sum, expected) + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcdefghi', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 37: :finally reason discarded by :finish {{{1 +" +" When a :finally clause is executed due to a :continue, :break, +" :return, :finish, error, interrupt or :throw, the jump reason is +" discarded by a :finish in the finally clause. +"------------------------------------------------------------------------------- + +func Test_finally_discard_by_finish() + let test =<< trim [CODE] + func F(jump) " not executed as function, transformed to a script + let loop = 0 + while loop < 2 + let loop = loop + 1 + if loop == 1 + try + if a:jump == "continue" + continue + elseif a:jump == "break" + break + elseif a:jump == "finish" + finish + elseif a:jump == "error" + asdf + elseif a:jump == "interrupt" + call interrupt() + let dummy = 0 + elseif a:jump == "throw" + throw "abc" + endif + finally + finish " discards jump that caused the :finally + call assert_report('should not get here') + endtry + elseif loop == 2 + call assert_report('should not get here') + endif + endwhile + call assert_report('should not get here') + endfunc + + let scriptF = MakeScript("F") + delfunction F + + let g:jump = "continue" + exec "source" scriptF + Xpath 'a' + let g:jump = "break" + exec "source" scriptF + Xpath 'b' + let g:jump = "finish" + exec "source" scriptF + Xpath 'c' + try + let g:jump = "error" + exec "source" scriptF + Xpath 'd' + finally + Xpath 'e' + try + let g:jump = "interrupt" + exec "source" scriptF + Xpath 'f' + finally + Xpath 'g' + try + let g:jump = "throw" + exec "source" scriptF + Xpath 'h' + finally + Xpath 'i' + endtry + endtry + endtry + unlet g:jump + call delete(scriptF) + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcdefghi', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 38: :finally reason discarded by an error {{{1 +" +" When a :finally clause is executed due to a :continue, :break, +" :return, :finish, error, interrupt or :throw, the jump reason is +" discarded by an error in the finally clause. +"------------------------------------------------------------------------------- + +func Test_finally_discard_by_error() + let test =<< trim [CODE] + func E(jump) + let loop = 0 + while loop < 2 + let loop = loop + 1 + if loop == 1 + try + if a:jump == "continue" + continue + elseif a:jump == "break" + break + elseif a:jump == "return" || a:jump == "finish" + return + elseif a:jump == "error" + asdf + elseif a:jump == "interrupt" + call interrupt() + let dummy = 0 + elseif a:jump == "throw" + throw "abc" + endif + finally + asdf " error; discards jump that caused the :finally + endtry + elseif loop == 2 + call assert_report('should not get here') + endif + endwhile + call assert_report('should not get here') + endfunc + + try + Xpath 'a' + call E("continue") + call assert_report('should not get here') + finally + try + Xpath 'b' + call E("break") + call assert_report('should not get here') + finally + try + Xpath 'c' + call E("return") + call assert_report('should not get here') + finally + try + Xpath 'd' + let g:jump = "finish" + ExecAsScript E + call assert_report('should not get here') + finally + unlet g:jump + try + Xpath 'e' + call E("error") + call assert_report('should not get here') + finally + try + Xpath 'f' + call E("interrupt") + call assert_report('should not get here') + finally + try + Xpath 'g' + call E("throw") + call assert_report('should not get here') + finally + Xpath 'h' + delfunction E + endtry + endtry + endtry + endtry + endtry + endtry + endtry + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcdefgh', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 39: :finally reason discarded by an interrupt {{{1 +" +" When a :finally clause is executed due to a :continue, :break, +" :return, :finish, error, interrupt or :throw, the jump reason is +" discarded by an interrupt in the finally clause. +"------------------------------------------------------------------------------- + +func Test_finally_discarded_by_interrupt() + let test =<< trim [CODE] + func I(jump) + let loop = 0 + while loop < 2 + let loop = loop + 1 + if loop == 1 + try + if a:jump == "continue" + continue + elseif a:jump == "break" + break + elseif a:jump == "return" || a:jump == "finish" + return + elseif a:jump == "error" + asdf + elseif a:jump == "interrupt" + call interrupt() + let dummy = 0 + elseif a:jump == "throw" + throw "abc" + endif + finally + call interrupt() + let dummy = 0 + endtry + elseif loop == 2 + call assert_report('should not get here') + endif + endwhile + call assert_report('should not get here') + endfunc + + try + try + Xpath 'a' + call I("continue") + call assert_report('should not get here') + finally + try + Xpath 'b' + call I("break") + call assert_report('should not get here') + finally + try + Xpath 'c' + call I("return") + call assert_report('should not get here') + finally + try + Xpath 'd' + let g:jump = "finish" + ExecAsScript I + call assert_report('should not get here') + finally + unlet g:jump + try + Xpath 'e' + call I("error") + call assert_report('should not get here') + finally + try + Xpath 'f' + call I("interrupt") + call assert_report('should not get here') + finally + try + Xpath 'g' + call I("throw") + call assert_report('should not get here') + finally + Xpath 'h' + delfunction I + endtry + endtry + endtry + endtry + endtry + endtry + endtry + call assert_report('should not get here') + catch /^Vim:Interrupt$/ + Xpath 'A' + endtry + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcdefghA', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 40: :finally reason discarded by :throw {{{1 +" +" When a :finally clause is executed due to a :continue, :break, +" :return, :finish, error, interrupt or :throw, the jump reason is +" discarded by a :throw in the finally clause. +"------------------------------------------------------------------------------- + +func Test_finally_discard_by_throw() + let test =<< trim [CODE] + func T(jump) + let loop = 0 + while loop < 2 + let loop = loop + 1 + if loop == 1 + try + if a:jump == "continue" + continue + elseif a:jump == "break" + break + elseif a:jump == "return" || a:jump == "finish" + return + elseif a:jump == "error" + asdf + elseif a:jump == "interrupt" + call interrupt() + let dummy = 0 + elseif a:jump == "throw" + throw "abc" + endif + finally + throw "xyz" " discards jump that caused the :finally + endtry + elseif loop == 2 + call assert_report('should not get here') + endif + endwhile + call assert_report('should not get here') + endfunc + + try + Xpath 'a' + call T("continue") + call assert_report('should not get here') + finally + try + Xpath 'b' + call T("break") + call assert_report('should not get here') + finally + try + Xpath 'c' + call T("return") + call assert_report('should not get here') + finally + try + Xpath 'd' + let g:jump = "finish" + ExecAsScript T + call assert_report('should not get here') + finally + unlet g:jump + try + Xpath 'e' + call T("error") + call assert_report('should not get here') + finally + try + Xpath 'f' + call T("interrupt") + call assert_report('should not get here') + finally + try + Xpath 'g' + call T("throw") + call assert_report('should not get here') + finally + Xpath 'h' + delfunction T + endtry + endtry + endtry + endtry + endtry + endtry + endtry + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcdefgh', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 49: Throwing exceptions across functions {{{1 +" +" When an exception is thrown but not caught inside a function, the +" caller is checked for a matching :catch clause. +"------------------------------------------------------------------------------- + +func T49_C() + try + Xpath 'a' + throw "arrgh" + call assert_report('should not get here') + catch /arrgh/ + Xpath 'b' + endtry + Xpath 'c' +endfunc + +func T49_T1() + XloopNEXT + try + Xloop 'd' + throw "arrgh" + call assert_report('should not get here') + finally + Xloop 'e' + endtry + Xloop 'f' +endfunc + +func T49_T2() + try + Xpath 'g' + call T49_T1() + call assert_report('should not get here') + finally + Xpath 'h' + endtry + call assert_report('should not get here') +endfunc + +func Test_throw_exception_across_funcs() + XpathINIT + XloopINIT + try + Xpath 'i' + call T49_C() " throw and catch + Xpath 'j' + catch /.*/ + call assert_report('should not get here') + endtry + + try + Xpath 'k' + call T49_T1() " throw, one level + call assert_report('should not get here') + catch /arrgh/ + Xpath 'l' + catch /.*/ + call assert_report('should not get here') + endtry + + try + Xpath 'm' + call T49_T2() " throw, two levels + call assert_report('should not get here') + catch /arrgh/ + Xpath 'n' + catch /.*/ + call assert_report('should not get here') + endtry + Xpath 'o' + + call assert_equal('iabcjkd2e2lmgd3e3hno', g:Xpath) +endfunc + +"------------------------------------------------------------------------------- +" Test 50: Throwing exceptions across script files {{{1 +" +" When an exception is thrown but not caught inside a script file, +" the sourcing script or function is checked for a matching :catch +" clause. +" +" This test executes the bodies of the functions C, T1, and T2 from +" the previous test as script files (:return replaced by :finish). +"------------------------------------------------------------------------------- + +func T50_F() + try + Xpath 'A' + exec "source" g:scriptC + Xpath 'B' + catch /.*/ + call assert_report('should not get here') + endtry + + try + Xpath 'C' + exec "source" g:scriptT1 + call assert_report('should not get here') + catch /arrgh/ + Xpath 'D' + catch /.*/ + call assert_report('should not get here') + endtry +endfunc + +func Test_throw_across_script() + XpathINIT + XloopINIT + let g:scriptC = MakeScript("T49_C") + let g:scriptT1 = MakeScript("T49_T1") + let scriptT2 = MakeScript("T49_T2", g:scriptT1) + + try + Xpath 'E' + call T50_F() + Xpath 'F' + exec "source" scriptT2 + call assert_report('should not get here') + catch /arrgh/ + Xpath 'G' + catch /.*/ + call assert_report('should not get here') + endtry + Xpath 'H' + call assert_equal('EAabcBCd2e2DFgd3e3hGH', g:Xpath) + + call delete(g:scriptC) + call delete(g:scriptT1) + call delete(scriptT2) + unlet g:scriptC g:scriptT1 scriptT2 +endfunc + +"------------------------------------------------------------------------------- +" Test 52: Uncaught exceptions {{{1 +" +" When an exception is thrown but not caught, an error message is +" displayed when the script is terminated. In case of an interrupt +" or error exception, the normal interrupt or error message(s) are +" displayed. +"------------------------------------------------------------------------------- + +func Test_uncaught_exception_1() + CheckEnglish + + let test =<< trim [CODE] + Xpath 'a' + throw "arrgh" + call assert_report('should not get here')` + [CODE] + let verify =<< trim [CODE] + call assert_equal('E605: Exception not caught: arrgh', v:errmsg) + call assert_equal('a', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_uncaught_exception_2() + CheckEnglish + + let test =<< trim [CODE] + try + Xpath 'a' + throw "oops" + call assert_report('should not get here')` + catch /arrgh/ + call assert_report('should not get here')` + endtry + call assert_report('should not get here')` + [CODE] + let verify =<< trim [CODE] + call assert_equal('E605: Exception not caught: oops', v:errmsg) + call assert_equal('a', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_uncaught_exception_3() + CheckEnglish + + let test =<< trim [CODE] + func T() + Xpath 'c' + throw "brrr" + call assert_report('should not get here')` + endfunc + + try + Xpath 'a' + throw "arrgh" + call assert_report('should not get here')` + catch /.*/ + Xpath 'b' + call T() + call assert_report('should not get here')` + endtry + call assert_report('should not get here')` + [CODE] + let verify =<< trim [CODE] + call assert_equal('E605: Exception not caught: brrr', v:errmsg) + call assert_equal('abc', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_uncaught_exception_4() + CheckEnglish + + let test =<< trim [CODE] + try + Xpath 'a' + throw "arrgh" + call assert_report('should not get here')` + finally + Xpath 'b' + throw "brrr" + call assert_report('should not get here')` + endtry + call assert_report('should not get here')` + [CODE] + let verify =<< trim [CODE] + call assert_equal('E605: Exception not caught: brrr', v:errmsg) + call assert_equal('ab', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_uncaught_exception_5() + CheckEnglish + + " Need to catch and handle interrupt, otherwise the test will wait for the + " user to press <Enter> to continue + let test =<< trim [CODE] + try + try + Xpath 'a' + call interrupt() + call assert_report('should not get here') + endtry + call assert_report('should not get here') + catch /^Vim:Interrupt$/ + Xpath 'b' + endtry + [CODE] + let verify =<< trim [CODE] + call assert_equal('ab', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_uncaught_exception_6() + CheckEnglish + + let test =<< trim [CODE] + try + Xpath 'a' + let x = novar " error E121; exception: E121 + catch /E15:/ " should not catch + call assert_report('should not get here') + endtry + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('a', g:Xpath) + call assert_equal('E121: Undefined variable: novar', v:errmsg) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_uncaught_exception_7() + CheckEnglish + + let test =<< trim [CODE] + try + Xpath 'a' + " error E108/E488; exception: E488 + unlet novar # + catch /E108:/ " should not catch + call assert_report('should not get here') + endtry + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('a', g:Xpath) + call assert_equal('E488: Trailing characters: #', v:errmsg) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 53: Nesting errors: :endif/:else/:elseif {{{1 +" +" For nesting errors of :if conditionals the correct error messages +" should be given. +"------------------------------------------------------------------------------- + +func Test_nested_if_else_errors() + CheckEnglish + + " :endif without :if + let code =<< trim END + endif + END + call writefile(code, 'Xtest', 'D') + call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if') + + " :endif without :if + let code =<< trim END + while 1 + endif + endwhile + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if') + + " :endif without :if + let code =<< trim END + try + finally + endif + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if') + + " :endif without :if + let code =<< trim END + try + endif + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if') + + " :endif without :if + let code =<< trim END + try + throw "a" + catch /a/ + endif + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if') + + " :else without :if + let code =<< trim END + else + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if') + + " :else without :if + let code =<< trim END + while 1 + else + endwhile + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if') + + " :else without :if + let code =<< trim END + try + finally + else + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if') + + " :else without :if + let code =<< trim END + try + else + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if') + + " :else without :if + let code =<< trim END + try + throw "a" + catch /a/ + else + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if') + + " :elseif without :if + let code =<< trim END + elseif 1 + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if') + + " :elseif without :if + let code =<< trim END + while 1 + elseif 1 + endwhile + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if') + + " :elseif without :if + let code =<< trim END + try + finally + elseif 1 + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if') + + " :elseif without :if + let code =<< trim END + try + elseif 1 + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if') + + " :elseif without :if + let code =<< trim END + try + throw "a" + catch /a/ + elseif 1 + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if') + + " multiple :else + let code =<< trim END + if 1 + else + else + endif + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(else):E583: Multiple :else') + + " :elseif after :else + let code =<< trim END + if 1 + else + elseif 1 + endif + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(elseif):E584: :elseif after :else') +endfunc + +"------------------------------------------------------------------------------- +" Test 54: Nesting errors: :while/:endwhile {{{1 +" +" For nesting errors of :while conditionals the correct error messages +" should be given. +" +" This test reuses the function MESSAGES() from the previous test. +" This functions checks the messages in g:msgfile. +"------------------------------------------------------------------------------- + +func Test_nested_while_error() + CheckEnglish + + " :endwhile without :while + let code =<< trim END + endwhile + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while') + + " :endwhile without :while + let code =<< trim END + if 1 + endwhile + endif + END + call writefile(code, 'Xtest', 'D') + call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while') + + " Missing :endif + let code =<< trim END + while 1 + if 1 + endwhile + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endwhile):E171: Missing :endif') + + " :endwhile without :while + let code =<< trim END + try + finally + endwhile + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while') + + " Missing :endtry + let code =<< trim END + while 1 + try + finally + endwhile + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endwhile):E600: Missing :endtry') + + " Missing :endtry + let code =<< trim END + while 1 + if 1 + try + finally + endwhile + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endwhile):E600: Missing :endtry') + + " Missing :endif + let code =<< trim END + while 1 + try + finally + if 1 + endwhile + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endwhile):E171: Missing :endif') + + " :endwhile without :while + let code =<< trim END + try + endwhile + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while') + + " :endwhile without :while + let code =<< trim END + while 1 + try + endwhile + endtry + endwhile + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while') + + " :endwhile without :while + let code =<< trim END + try + throw "a" + catch /a/ + endwhile + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while') + + " :endwhile without :while + let code =<< trim END + while 1 + try + throw "a" + catch /a/ + endwhile + endtry + endwhile + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while') +endfunc + +"------------------------------------------------------------------------------- +" Test 55: Nesting errors: :continue/:break {{{1 +" +" For nesting errors of :continue and :break commands the correct +" error messages should be given. +" +" This test reuses the function MESSAGES() from the previous test. +" This functions checks the messages in g:msgfile. +"------------------------------------------------------------------------------- + +func Test_nested_cont_break_error() + CheckEnglish + + " :continue without :while + let code =<< trim END + continue + END + call writefile(code, 'Xtest', 'D') + call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for') + + " :continue without :while + let code =<< trim END + if 1 + continue + endif + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for') + + " :continue without :while + let code =<< trim END + try + finally + continue + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for') + + " :continue without :while + let code =<< trim END + try + continue + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for') + + " :continue without :while + let code =<< trim END + try + throw "a" + catch /a/ + continue + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for') + + " :break without :while + let code =<< trim END + break + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for') + + " :break without :while + let code =<< trim END + if 1 + break + endif + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for') + + " :break without :while + let code =<< trim END + try + finally + break + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for') + + " :break without :while + let code =<< trim END + try + break + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for') + + " :break without :while + let code =<< trim END + try + throw "a" + catch /a/ + break + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for') +endfunc + +"------------------------------------------------------------------------------- +" Test 56: Nesting errors: :endtry {{{1 +" +" For nesting errors of :try conditionals the correct error messages +" should be given. +" +" This test reuses the function MESSAGES() from the previous test. +" This functions checks the messages in g:msgfile. +"------------------------------------------------------------------------------- + +func Test_nested_endtry_error() + CheckEnglish + + " :endtry without :try + let code =<< trim END + endtry + END + call writefile(code, 'Xtest', 'D') + call AssertException(['source Xtest'], 'Vim(endtry):E602: :endtry without :try') + + " :endtry without :try + let code =<< trim END + if 1 + endtry + endif + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endtry):E602: :endtry without :try') + + " :endtry without :try + let code =<< trim END + while 1 + endtry + endwhile + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endtry):E602: :endtry without :try') + + " Missing :endif + let code =<< trim END + try + if 1 + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endtry):E171: Missing :endif') + + " Missing :endwhile + let code =<< trim END + try + while 1 + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endtry):E170: Missing :endwhile') + + " Missing :endif + let code =<< trim END + try + finally + if 1 + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endtry):E171: Missing :endif') + + " Missing :endwhile + let code =<< trim END + try + finally + while 1 + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endtry):E170: Missing :endwhile') + + " Missing :endif + let code =<< trim END + try + throw "a" + catch /a/ + if 1 + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endtry):E171: Missing :endif') + + " Missing :endwhile + let code =<< trim END + try + throw "a" + catch /a/ + while 1 + endtry + END + call writefile(code, 'Xtest') + call AssertException(['source Xtest'], 'Vim(endtry):E170: Missing :endwhile') +endfunc + +"------------------------------------------------------------------------------- +" Test 57: v:exception and v:throwpoint for user exceptions {{{1 +" +" v:exception evaluates to the value of the exception that was caught +" most recently and is not finished. (A caught exception is finished +" when the next ":catch", ":finally", or ":endtry" is reached.) +" v:throwpoint evaluates to the script/function name and line number +" where that exception has been thrown. +"------------------------------------------------------------------------------- + +func Test_user_exception_info() + CheckEnglish + + XpathINIT + XloopINIT + + func FuncException() + let g:exception = v:exception + endfunc + + func FuncThrowpoint() + let g:throwpoint = v:throwpoint + endfunc + + let scriptException = MakeScript("FuncException") + let scriptThrowPoint = MakeScript("FuncThrowpoint") + + command! CmdException let g:exception = v:exception + command! CmdThrowpoint let g:throwpoint = v:throwpoint + + func T(arg, line) + if a:line == 2 + throw a:arg " in line 2 + elseif a:line == 4 + throw a:arg " in line 4 + elseif a:line == 6 + throw a:arg " in line 6 + elseif a:line == 8 + throw a:arg " in line 8 + endif + endfunc + + func G(arg, line) + call T(a:arg, a:line) + endfunc + + func F(arg, line) + call G(a:arg, a:line) + endfunc + + let scriptT = MakeScript("T") + let scriptG = MakeScript("G", scriptT) + let scriptF = MakeScript("F", scriptG) + + try + Xpath 'a' + call F("oops", 2) + catch /.*/ + Xpath 'b' + let exception = v:exception + let throwpoint = v:throwpoint + call assert_equal("oops", v:exception) + call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint) + call assert_match('\<2\>', v:throwpoint) + + exec "let exception = v:exception" + exec "let throwpoint = v:throwpoint" + call assert_equal("oops", v:exception) + call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint) + call assert_match('\<2\>', v:throwpoint) + + CmdException + CmdThrowpoint + call assert_equal("oops", v:exception) + call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint) + call assert_match('\<2\>', v:throwpoint) + + call FuncException() + call FuncThrowpoint() + call assert_equal("oops", v:exception) + call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint) + call assert_match('\<2\>', v:throwpoint) + + exec "source" scriptException + exec "source" scriptThrowPoint + call assert_equal("oops", v:exception) + call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint) + call assert_match('\<2\>', v:throwpoint) + + try + Xpath 'c' + call G("arrgh", 4) + catch /.*/ + Xpath 'd' + let exception = v:exception + let throwpoint = v:throwpoint + call assert_equal("arrgh", v:exception) + call assert_match('\<G\[1]\.\.T\>', v:throwpoint) + call assert_match('\<4\>', v:throwpoint) + + try + Xpath 'e' + let g:arg = "autsch" + let g:line = 6 + exec "source" scriptF + catch /.*/ + Xpath 'f' + let exception = v:exception + let throwpoint = v:throwpoint + call assert_equal("autsch", v:exception) + call assert_match(fnamemodify(scriptT, ':t'), v:throwpoint) + call assert_match('\<6\>', v:throwpoint) + finally + Xpath 'g' + let exception = v:exception + let throwpoint = v:throwpoint + call assert_equal("arrgh", v:exception) + call assert_match('\<G\[1]\.\.T\>', v:throwpoint) + call assert_match('\<4\>', v:throwpoint) + try + Xpath 'h' + let g:arg = "brrrr" + let g:line = 8 + exec "source" scriptG + catch /.*/ + Xpath 'i' + let exception = v:exception + let throwpoint = v:throwpoint + " Resolve scriptT for matching it against v:throwpoint. + call assert_equal("brrrr", v:exception) + call assert_match(fnamemodify(scriptT, ':t'), v:throwpoint) + call assert_match('\<8\>', v:throwpoint) + finally + Xpath 'j' + let exception = v:exception + let throwpoint = v:throwpoint + call assert_equal("arrgh", v:exception) + call assert_match('\<G\[1]\.\.T\>', v:throwpoint) + call assert_match('\<4\>', v:throwpoint) + endtry + Xpath 'k' + let exception = v:exception + let throwpoint = v:throwpoint + call assert_equal("arrgh", v:exception) + call assert_match('\<G\[1]\.\.T\>', v:throwpoint) + call assert_match('\<4\>', v:throwpoint) + endtry + Xpath 'l' + let exception = v:exception + let throwpoint = v:throwpoint + call assert_equal("arrgh", v:exception) + call assert_match('\<G\[1]\.\.T\>', v:throwpoint) + call assert_match('\<4\>', v:throwpoint) + finally + Xpath 'm' + let exception = v:exception + let throwpoint = v:throwpoint + call assert_equal("oops", v:exception) + call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint) + call assert_match('\<2\>', v:throwpoint) + endtry + Xpath 'n' + let exception = v:exception + let throwpoint = v:throwpoint + call assert_equal("oops", v:exception) + call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint) + call assert_match('\<2\>', v:throwpoint) + finally + Xpath 'o' + let exception = v:exception + let throwpoint = v:throwpoint + call assert_equal("", v:exception) + call assert_match('^$', v:throwpoint) + call assert_match('^$', v:throwpoint) + endtry + + call assert_equal('abcdefghijklmno', g:Xpath) + + unlet exception throwpoint + delfunction FuncException + delfunction FuncThrowpoint + call delete(scriptException) + call delete(scriptThrowPoint) + unlet scriptException scriptThrowPoint + delcommand CmdException + delcommand CmdThrowpoint + delfunction T + delfunction G + delfunction F + call delete(scriptT) + call delete(scriptG) + call delete(scriptF) + unlet scriptT scriptG scriptF +endfunc + +"------------------------------------------------------------------------------- +" +" Test 58: v:exception and v:throwpoint for error/interrupt exceptions {{{1 +" +" v:exception and v:throwpoint work also for error and interrupt +" exceptions. +"------------------------------------------------------------------------------- + +func Test_exception_info_for_error() + CheckEnglish + + let test =<< trim [CODE] + func T(line) + if a:line == 2 + delfunction T " error (function in use) in line 2 + elseif a:line == 4 + call interrupt() + endif + endfunc + + while 1 + try + Xpath 'a' + call T(2) + call assert_report('should not get here') + catch /.*/ + Xpath 'b' + if v:exception !~ 'Vim(delfunction):' + call assert_report('should not get here') + endif + if v:throwpoint !~ '\<T\>' + call assert_report('should not get here') + endif + if v:throwpoint !~ '\<2\>' + call assert_report('should not get here') + endif + finally + Xpath 'c' + if v:exception != "" + call assert_report('should not get here') + endif + if v:throwpoint != "" + call assert_report('should not get here') + endif + break + endtry + endwhile + + Xpath 'd' + if v:exception != "" + call assert_report('should not get here') + endif + if v:throwpoint != "" + call assert_report('should not get here') + endif + + while 1 + try + Xpath 'e' + call T(4) + call assert_report('should not get here') + catch /.*/ + Xpath 'f' + if v:exception != 'Vim:Interrupt' + call assert_report('should not get here') + endif + if v:throwpoint !~ 'function T' + call assert_report('should not get here') + endif + if v:throwpoint !~ '\<4\>' + call assert_report('should not get here') + endif + finally + Xpath 'g' + if v:exception != "" + call assert_report('should not get here') + endif + if v:throwpoint != "" + call assert_report('should not get here') + endif + break + endtry + endwhile + + Xpath 'h' + if v:exception != "" + call assert_report('should not get here') + endif + if v:throwpoint != "" + call assert_report('should not get here') + endif + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcdefgh', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" +" Test 59: v:exception and v:throwpoint when discarding exceptions {{{1 +" +" When a :catch clause is left by a ":break" etc or an error or +" interrupt exception, v:exception and v:throwpoint are reset. They +" are not affected by an exception that is discarded before being +" caught. +"------------------------------------------------------------------------------- +func Test_exception_info_on_discard() + CheckEnglish + + let test =<< trim [CODE] + let sfile = expand("<sfile>") + + while 1 + try + throw "x1" + catch /.*/ + break + endtry + endwhile + call assert_equal('', v:exception) + call assert_equal('', v:throwpoint) + + while 1 + try + throw "x2" + catch /.*/ + break + finally + call assert_equal('', v:exception) + call assert_equal('', v:throwpoint) + endtry + break + endwhile + call assert_equal('', v:exception) + call assert_equal('', v:throwpoint) + + while 1 + try + let errcaught = 0 + try + try + throw "x3" + catch /.*/ + let lnum = expand("<sflnum>") + asdf + endtry + catch /.*/ + let errcaught = 1 + call assert_match('Vim:E492: Not an editor command:', v:exception) + call assert_match('line ' .. (lnum + 1), v:throwpoint) + endtry + finally + call assert_equal(1, errcaught) + break + endtry + endwhile + call assert_equal('', v:exception) + call assert_equal('', v:throwpoint) + + Xpath 'a' + + while 1 + try + let intcaught = 0 + try + try + throw "x4" + catch /.*/ + let lnum = expand("<sflnum>") + call interrupt() + endtry + catch /.*/ + let intcaught = 1 + call assert_match('Vim:Interrupt', v:exception) + call assert_match('line ' .. (lnum + 1), v:throwpoint) + endtry + finally + call assert_equal(1, intcaught) + break + endtry + endwhile + call assert_equal('', v:exception) + call assert_equal('', v:throwpoint) + + Xpath 'b' + + while 1 + try + let errcaught = 0 + try + try + if 1 + let lnum = expand("<sflnum>") + throw "x5" + " missing endif + catch /.*/ + call assert_report('should not get here') + endtry + catch /.*/ + let errcaught = 1 + call assert_match('Vim(catch):E171: Missing :endif:', v:exception) + call assert_match('line ' .. (lnum + 3), v:throwpoint) + endtry + finally + call assert_equal(1, errcaught) + break + endtry + endwhile + call assert_equal('', v:exception) + call assert_equal('', v:throwpoint) + + Xpath 'c' + + try + while 1 + try + throw "x6" + finally + break + endtry + break + endwhile + catch /.*/ + call assert_report('should not get here') + endtry + call assert_equal('', v:exception) + call assert_equal('', v:throwpoint) + + try + while 1 + try + throw "x7" + finally + break + endtry + break + endwhile + catch /.*/ + call assert_report('should not get here') + finally + call assert_equal('', v:exception) + call assert_equal('', v:throwpoint) + endtry + call assert_equal('', v:exception) + call assert_equal('', v:throwpoint) + + while 1 + try + let errcaught = 0 + try + try + throw "x8" + finally + let lnum = expand("<sflnum>") + asdf + endtry + catch /.*/ + let errcaught = 1 + call assert_match('Vim:E492: Not an editor command:', v:exception) + call assert_match('line ' .. (lnum + 1), v:throwpoint) + endtry + finally + call assert_equal(1, errcaught) + break + endtry + endwhile + call assert_equal('', v:exception) + call assert_equal('', v:throwpoint) + + Xpath 'd' + + while 1 + try + let intcaught = 0 + try + try + throw "x9" + finally + let lnum = expand("<sflnum>") + call interrupt() + endtry + catch /.*/ + let intcaught = 1 + call assert_match('Vim:Interrupt', v:exception) + call assert_match('line ' .. (lnum + 1), v:throwpoint) + endtry + finally + call assert_equal(1, intcaught) + break + endtry + endwhile + call assert_equal('', v:exception) + call assert_equal('', v:throwpoint) + + Xpath 'e' + + while 1 + try + let errcaught = 0 + try + try + if 1 + let lnum = expand("<sflnum>") + throw "x10" + " missing endif + finally + call assert_equal('', v:exception) + call assert_equal('', v:throwpoint) + endtry + catch /.*/ + let errcaught = 1 + call assert_match('Vim(finally):E171: Missing :endif:', v:exception) + call assert_match('line ' .. (lnum + 3), v:throwpoint) + endtry + finally + call assert_equal(1, errcaught) + break + endtry + endwhile + call assert_equal('', v:exception) + call assert_equal('', v:throwpoint) + + Xpath 'f' + + while 1 + try + let errcaught = 0 + try + try + if 1 + let lnum = expand("<sflnum>") + throw "x11" + " missing endif + endtry + catch /.*/ + let errcaught = 1 + call assert_match('Vim(endtry):E171: Missing :endif:', v:exception) + call assert_match('line ' .. (lnum + 3), v:throwpoint) + endtry + finally + call assert_equal(1, errcaught) + break + endtry + endwhile + call assert_equal('', v:exception) + call assert_equal('', v:throwpoint) + + Xpath 'g' + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcdefg', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" +" Test 60: (Re)throwing v:exception; :echoerr. {{{1 +" +" A user exception can be rethrown after catching by throwing +" v:exception. An error or interrupt exception cannot be rethrown +" because Vim exceptions cannot be faked. A Vim exception using the +" value of v:exception can, however, be triggered by the :echoerr +" command. +"------------------------------------------------------------------------------- + +func Test_rethrow_exception_1() + XpathINIT + try + try + Xpath 'a' + throw "oops" + catch /oops/ + Xpath 'b' + throw v:exception " rethrow user exception + catch /.*/ + call assert_report('should not get here') + endtry + catch /^oops$/ " catches rethrown user exception + Xpath 'c' + catch /.*/ + call assert_report('should not get here') + endtry + call assert_equal('abc', g:Xpath) +endfunc + +func Test_rethrow_exception_2() + XpathINIT + try + let caught = 0 + try + Xpath 'a' + write /n/o/n/w/r/i/t/a/b/l/e/_/f/i/l/e + call assert_report('should not get here') + catch /^Vim(write):/ + let caught = 1 + throw v:exception " throw error: cannot fake Vim exception + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'b' + call assert_equal(1, caught) + endtry + catch /^Vim(throw):/ " catches throw error + let caught = caught + 1 + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'c' + call assert_equal(2, caught) + endtry + call assert_equal('abc', g:Xpath) +endfunc + +func Test_rethrow_exception_3() + XpathINIT + try + let caught = 0 + try + Xpath 'a' + asdf + catch /^Vim/ " catch error exception + let caught = 1 + " Trigger Vim error exception with value specified after :echoerr + let value = substitute(v:exception, '^Vim\((.*)\)\=:', '', "") + echoerr value + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'b' + call assert_equal(1, caught) + endtry + catch /^Vim(echoerr):/ + let caught = caught + 1 + call assert_match(value, v:exception) + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'c' + call assert_equal(2, caught) + endtry + call assert_equal('abc', g:Xpath) +endfunc + +func Test_rethrow_exception_3() + XpathINIT + try + let errcaught = 0 + try + Xpath 'a' + let intcaught = 0 + call interrupt() + catch /^Vim:/ " catch interrupt exception + let intcaught = 1 + " Trigger Vim error exception with value specified after :echoerr + echoerr substitute(v:exception, '^Vim\((.*)\)\=:', '', "") + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'b' + call assert_equal(1, intcaught) + endtry + catch /^Vim(echoerr):/ + let errcaught = 1 + call assert_match('Interrupt', v:exception) + finally + Xpath 'c' + call assert_equal(1, errcaught) + endtry + call assert_equal('abc', g:Xpath) +endfunc + +"------------------------------------------------------------------------------- +" Test 61: Catching interrupt exceptions {{{1 +" +" When an interrupt occurs inside a :try/:endtry region, an +" interrupt exception is thrown and can be caught. Its value is +" "Vim:Interrupt". If the interrupt occurs after an error or a :throw +" but before a matching :catch is reached, all following :catches of +" that try block are ignored, but the interrupt exception can be +" caught by the next surrounding try conditional. An interrupt is +" ignored when there is a previous interrupt that has not been caught +" or causes a :finally clause to be executed. +"------------------------------------------------------------------------------- + +func Test_catch_intr_exception() + let test =<< trim [CODE] + while 1 + try + try + Xpath 'a' + call interrupt() + call assert_report('should not get here') + catch /^Vim:Interrupt$/ + Xpath 'b' + finally + Xpath 'c' + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'd' + break + endtry + endwhile + + while 1 + try + try + try + Xpath 'e' + asdf + call assert_report('should not get here') + catch /do_not_catch/ + call assert_report('should not get here') + catch /.*/ + Xpath 'f' + call interrupt() + call assert_report('should not get here') + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'g' + call interrupt() + call assert_report('should not get here') + endtry + catch /^Vim:Interrupt$/ + Xpath 'h' + finally + Xpath 'i' + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'j' + break + endtry + endwhile + + while 1 + try + try + try + Xpath 'k' + throw "x" + call assert_report('should not get here') + catch /do_not_catch/ + call assert_report('should not get here') + catch /x/ + Xpath 'l' + call interrupt() + call assert_report('should not get here') + catch /.*/ + call assert_report('should not get here') + endtry + catch /^Vim:Interrupt$/ + Xpath 'm' + finally + Xpath 'n' + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'o' + break + endtry + endwhile + + while 1 + try + try + Xpath 'p' + call interrupt() + call assert_report('should not get here') + catch /do_not_catch/ + call interrupt() + call assert_report('should not get here') + catch /^Vim:Interrupt$/ + Xpath 'q' + finally + Xpath 'r' + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 's' + break + endtry + endwhile + + Xpath 't' + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcdefghijklmnopqrst', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 62: Catching error exceptions {{{1 +" +" An error inside a :try/:endtry region is converted to an exception +" and can be caught. The error exception has a "Vim(cmdname):" prefix +" where cmdname is the name of the failing command, or a "Vim:" prefix +" if no command name is known. The "Vim" prefixes cannot be faked. +"------------------------------------------------------------------------------- + +func Test_catch_err_exception_1() + XpathINIT + while 1 + try + try + let caught = 0 + unlet novar + catch /^Vim(unlet):/ + Xpath 'a' + let caught = 1 + let v:errmsg = substitute(v:exception, '^Vim(unlet):', '', "") + finally + Xpath 'b' + call assert_equal(1, caught) + call assert_match('E108: No such variable: "novar"', v:errmsg) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'c' + break + endtry + call assert_report('should not get here') + endwhile + call assert_equal('abc', g:Xpath) +endfunc + +func Test_catch_err_exception_2() + XpathINIT + while 1 + try + try + let caught = 0 + throw novar " error in :throw + catch /^Vim(throw):/ + Xpath 'a' + let caught = 1 + let v:errmsg = substitute(v:exception, '^Vim(throw):', '', "") + finally + Xpath 'b' + call assert_equal(1, caught) + call assert_match('E121: Undefined variable: novar', v:errmsg) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'c' + break + endtry + call assert_report('should not get here') + endwhile + call assert_equal('abc', g:Xpath) +endfunc + +func Test_catch_err_exception_3() + XpathINIT + while 1 + try + try + let caught = 0 + throw "Vim:faked" " error: cannot fake Vim exception + catch /^Vim(throw):/ + Xpath 'a' + let caught = 1 + let v:errmsg = substitute(v:exception, '^Vim(throw):', '', "") + finally + Xpath 'b' + call assert_equal(1, caught) + call assert_match("E608: Cannot :throw exceptions with 'Vim' prefix", + \ v:errmsg) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'c' + break + endtry + call assert_report('should not get here') + endwhile + call assert_equal('abc', g:Xpath) +endfunc + +func Test_catch_err_exception_4() + XpathINIT + func F() + while 1 + " Missing :endwhile + endfunc + + while 1 + try + try + let caught = 0 + call F() + catch /^Vim(endfunction):/ + Xpath 'a' + let caught = 1 + let v:errmsg = substitute(v:exception, '^Vim(endfunction):', '', "") + finally + Xpath 'b' + call assert_equal(1, caught) + call assert_match("E170: Missing :endwhile", v:errmsg) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'c' + break + endtry + call assert_report('should not get here') + endwhile + call assert_equal('abc', g:Xpath) + delfunc F +endfunc + +func Test_catch_err_exception_5() + XpathINIT + func F() + while 1 + " Missing :endwhile + endfunc + + while 1 + try + try + let caught = 0 + ExecAsScript F + catch /^Vim:/ + Xpath 'a' + let caught = 1 + let v:errmsg = substitute(v:exception, '^Vim:', '', "") + finally + Xpath 'b' + call assert_equal(1, caught) + call assert_match("E170: Missing :endwhile", v:errmsg) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'c' + break + endtry + call assert_report('should not get here') + endwhile + call assert_equal('abc', g:Xpath) + delfunc F +endfunc + +func Test_catch_err_exception_6() + XpathINIT + func G() + call G() + endfunc + + while 1 + try + let mfd_save = &mfd + set mfd=3 + try + let caught = 0 + call G() + catch /^Vim(call):/ + Xpath 'a' + let caught = 1 + let v:errmsg = substitute(v:exception, '^Vim(call):', '', "") + finally + Xpath 'b' + call assert_equal(1, caught) + call assert_match("E132: Function call depth is higher than 'maxfuncdepth'", v:errmsg) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'c' + let &mfd = mfd_save + break + endtry + call assert_report('should not get here') + endwhile + call assert_equal('abc', g:Xpath) + delfunc G +endfunc + +func Test_catch_err_exception_7() + XpathINIT + func H() + return H() + endfunc + + while 1 + try + let mfd_save = &mfd + set mfd=3 + try + let caught = 0 + call H() + catch /^Vim(return):/ + Xpath 'a' + let caught = 1 + let v:errmsg = substitute(v:exception, '^Vim(return):', '', "") + finally + Xpath 'b' + call assert_equal(1, caught) + call assert_match("E132: Function call depth is higher than 'maxfuncdepth'", v:errmsg) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'c' + let &mfd = mfd_save + break " discard error for $VIMNOERRTHROW + endtry + call assert_report('should not get here') + endwhile + + call assert_equal('abc', g:Xpath) + delfunc H +endfunc + +"------------------------------------------------------------------------------- +" Test 63: Suppressing error exceptions by :silent!. {{{1 +" +" A :silent! command inside a :try/:endtry region suppresses the +" conversion of errors to an exception and the immediate abortion on +" error. When the commands executed by the :silent! themselves open +" a new :try/:endtry region, conversion of errors to exception and +" immediate abortion is switched on again - until the next :silent! +" etc. The :silent! has the effect of setting v:errmsg to the error +" message text (without displaying it) and continuing with the next +" script line. +" +" When a command triggering autocommands is executed by :silent! +" inside a :try/:endtry, the autocommand execution is not suppressed +" on error. +" +" This test reuses the function MSG() from the previous test. +"------------------------------------------------------------------------------- + +func Test_silent_exception() + XpathINIT + XloopINIT + let g:taken = "" + + func S(n) abort + XloopNEXT + let g:taken = g:taken . "E" . a:n + let v:errmsg = "" + exec "asdf" . a:n + + " Check that ":silent!" continues: + Xloop 'a' + + " Check that ":silent!" sets "v:errmsg": + call assert_match("E492: Not an editor command", v:errmsg) + endfunc + + func Foo() + while 1 + try + try + let caught = 0 + " This is not silent: + call S(3) + catch /^Vim:/ + Xpath 'b' + let caught = 1 + let errmsg3 = substitute(v:exception, '^Vim:', '', "") + silent! call S(4) + finally + call assert_equal(1, caught) + Xpath 'c' + call assert_match("E492: Not an editor command", errmsg3) + silent! call S(5) + " Break out of try conditionals that cover ":silent!". This also + " discards the aborting error when $VIMNOERRTHROW is non-zero. + break + endtry + catch /.*/ + call assert_report('should not get here') + endtry + endwhile + " This is a double ":silent!" (see caller). + silent! call S(6) + endfunc + + func Bar() + try + silent! call S(2) + silent! execute "call Foo() | call S(7)" + silent! call S(8) + endtry " normal end of try cond that covers ":silent!" + " This has a ":silent!" from the caller: + call S(9) + endfunc + + silent! call S(1) + silent! call Bar() + silent! call S(10) + + call assert_equal("E1E2E3E4E5E6E7E8E9E10", g:taken) + + augroup TMP + au! + autocmd BufWritePost * Xpath 'd' + augroup END + + Xpath 'e' + silent! write /i/m/p/o/s/s/i/b/l/e + Xpath 'f' + + call assert_equal('a2a3ba5ca6a7a8a9a10a11edf', g:Xpath) + + augroup TMP + au! + augroup END + augroup! TMP + delfunction S + delfunction Foo + delfunction Bar +endfunc + +"------------------------------------------------------------------------------- +" Test 64: Error exceptions after error, interrupt or :throw {{{1 +" +" When an error occurs after an interrupt or a :throw but before +" a matching :catch is reached, all following :catches of that try +" block are ignored, but the error exception can be caught by the next +" surrounding try conditional. Any previous error exception is +" discarded. An error is ignored when there is a previous error that +" has not been caught. +"------------------------------------------------------------------------------- + +func Test_exception_after_error_1() + XpathINIT + while 1 + try + try + Xpath 'a' + let caught = 0 + while 1 + if 1 + " Missing :endif + endwhile " throw error exception + catch /^Vim(/ + Xpath 'b' + let caught = 1 + finally + Xpath 'c' + call assert_equal(1, caught) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'd' + break + endtry + call assert_report('should not get here') + endwhile + call assert_equal('abcd', g:Xpath) +endfunc + +func Test_exception_after_error_2() + XpathINIT + while 1 + try + try + Xpath 'a' + let caught = 0 + try + if 1 + " Missing :endif + catch /.*/ " throw error exception + call assert_report('should not get here') + catch /.*/ + call assert_report('should not get here') + endtry + catch /^Vim(/ + Xpath 'b' + let caught = 1 + finally + Xpath 'c' + call assert_equal(1, caught) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'd' + break + endtry + call assert_report('should not get here') + endwhile + call assert_equal('abcd', g:Xpath) +endfunc + +func Test_exception_after_error_3() + XpathINIT + while 1 + try + try + let caught = 0 + try + Xpath 'a' + call interrupt() + catch /do_not_catch/ + call assert_report('should not get here') + if 1 + " Missing :endif + catch /.*/ " throw error exception + call assert_report('should not get here') + catch /.*/ + call assert_report('should not get here') + endtry + catch /^Vim(/ + Xpath 'b' + let caught = 1 + finally + Xpath 'c' + call assert_equal(1, caught) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'd' + break + endtry + call assert_report('should not get here') + endwhile + call assert_equal('abcd', g:Xpath) +endfunc + +func Test_exception_after_error_4() + XpathINIT + while 1 + try + try + let caught = 0 + try + Xpath 'a' + throw "x" + catch /do_not_catch/ + call assert_report('should not get here') + if 1 + " Missing :endif + catch /x/ " throw error exception + call assert_report('should not get here') + catch /.*/ + call assert_report('should not get here') + endtry + catch /^Vim(/ + Xpath 'b' + let caught = 1 + finally + Xpath 'c' + call assert_equal(1, caught) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'd' + break + endtry + call assert_report('should not get here') + endwhile + call assert_equal('abcd', g:Xpath) +endfunc + +func Test_exception_after_error_5() + XpathINIT + while 1 + try + try + let caught = 0 + Xpath 'a' + endif " :endif without :if; throw error exception + if 1 + " Missing :endif + catch /do_not_catch/ " ignore new error + call assert_report('should not get here') + catch /^Vim(endif):/ + Xpath 'b' + let caught = 1 + catch /^Vim(/ + call assert_report('should not get here') + finally + Xpath 'c' + call assert_equal(1, caught) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'd' + break + endtry + call assert_report('should not get here') + endwhile + call assert_equal('abcd', g:Xpath) +endfunc + +"------------------------------------------------------------------------------- +" Test 65: Errors in the /pattern/ argument of a :catch {{{1 +" +" On an error in the /pattern/ argument of a :catch, the :catch does +" not match. Any following :catches of the same :try/:endtry don't +" match either. Finally clauses are executed. +"------------------------------------------------------------------------------- + +func Test_catch_pattern_error() + CheckEnglish + XpathINIT + + try + try + Xpath 'a' + throw "oops" + catch /^oops$/ + Xpath 'b' + catch /\)/ " not checked; exception has already been caught + call assert_report('should not get here') + endtry + Xpath 'c' + catch /.*/ + call assert_report('should not get here') + endtry + call assert_equal('abc', g:Xpath) + + XpathINIT + func F() + try + try + try + Xpath 'a' + throw "ab" + catch /abc/ " does not catch + call assert_report('should not get here') + catch /\)/ " error; discards exception + call assert_report('should not get here') + catch /.*/ " not checked + call assert_report('should not get here') + finally + Xpath 'b' + endtry + call assert_report('should not get here') + catch /^ab$/ " checked, but original exception is discarded + call assert_report('should not get here') + catch /^Vim(catch):/ + Xpath 'c' + call assert_match('Vim(catch):E475: Invalid argument:', v:exception) + finally + Xpath 'd' + endtry + Xpath 'e' + catch /.*/ + call assert_report('should not get here') + endtry + Xpath 'f' + endfunc + + call F() + call assert_equal('abcdef', g:Xpath) + + delfunc F +endfunc + +"------------------------------------------------------------------------------- +" Test 66: Stop range :call on error, interrupt, or :throw {{{1 +" +" When a function which is multiply called for a range since it +" doesn't handle the range itself has an error in a command +" dynamically enclosed by :try/:endtry or gets an interrupt or +" executes a :throw, no more calls for the remaining lines in the +" range are made. On an error in a command not dynamically enclosed +" by :try/:endtry, the function is executed again for the remaining +" lines in the range. +"------------------------------------------------------------------------------- + +func Test_stop_range_on_error() + let test =<< trim [CODE] + let file = tempname() + exec "edit" file + call setline(1, ['line 1', 'line 2', 'line 3']) + let taken = "" + let expected = "G1EF1E(1)F1E(2)F1E(3)G2EF2E(1)G3IF3I(1)G4TF4T(1)G5AF5A(1)" + + func F(reason, n) abort + let g:taken = g:taken .. "F" .. a:n .. + \ substitute(a:reason, '\(\l\).*', '\u\1', "") .. + \ "(" .. line(".") .. ")" + + if a:reason == "error" + asdf + elseif a:reason == "interrupt" + call interrupt() + elseif a:reason == "throw" + throw "xyz" + elseif a:reason == "aborting error" + XloopNEXT + call assert_equal(g:taken, g:expected) + try + bwipeout! + call delete(g:file) + asdf + endtry + endif + endfunc + + func G(reason, n) + let g:taken = g:taken .. "G" .. a:n .. + \ substitute(a:reason, '\(\l\).*', '\u\1', "") + 1,3call F(a:reason, a:n) + endfunc + + Xpath 'a' + call G("error", 1) + try + Xpath 'b' + try + call G("error", 2) + call assert_report('should not get here') + finally + Xpath 'c' + try + call G("interrupt", 3) + call assert_report('should not get here') + finally + Xpath 'd' + try + call G("throw", 4) + call assert_report('should not get here') + endtry + endtry + endtry + catch /xyz/ + Xpath 'e' + catch /.*/ + call assert_report('should not get here') + endtry + Xpath 'f' + call G("aborting error", 5) + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcdef', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 67: :throw across :call command {{{1 +" +" On a call command, an exception might be thrown when evaluating the +" function name, during evaluation of the arguments, or when the +" function is being executed. The exception can be caught by the +" caller. +"------------------------------------------------------------------------------- + +func THROW(x, n) + if a:n == 1 + Xpath 'A' + elseif a:n == 2 + Xpath 'B' + elseif a:n == 3 + Xpath 'C' + endif + throw a:x +endfunc + +func NAME(x, n) + if a:n == 1 + call assert_report('should not get here') + elseif a:n == 2 + Xpath 'D' + elseif a:n == 3 + Xpath 'E' + elseif a:n == 4 + Xpath 'F' + endif + return a:x +endfunc + +func ARG(x, n) + if a:n == 1 + call assert_report('should not get here') + elseif a:n == 2 + call assert_report('should not get here') + elseif a:n == 3 + Xpath 'G' + elseif a:n == 4 + Xpath 'I' + endif + return a:x +endfunc + +func Test_throw_across_call_cmd() + XpathINIT + + func F(x, n) + if a:n == 2 + call assert_report('should not get here') + elseif a:n == 4 + Xpath 'a' + endif + endfunc + + while 1 + try + let v:errmsg = "" + + while 1 + try + Xpath 'b' + call {NAME(THROW("name", 1), 1)}(ARG(4711, 1), 1) + call assert_report('should not get here') + catch /^name$/ + Xpath 'c' + catch /.*/ + call assert_report('should not get here') + finally + call assert_equal("", v:errmsg) + let v:errmsg = "" + break + endtry + endwhile + + while 1 + try + Xpath 'd' + call {NAME("F", 2)}(ARG(THROW("arg", 2), 2), 2) + call assert_report('should not get here') + catch /^arg$/ + Xpath 'e' + catch /.*/ + call assert_report('should not get here') + finally + call assert_equal("", v:errmsg) + let v:errmsg = "" + break + endtry + endwhile + + while 1 + try + Xpath 'f' + call {NAME("THROW", 3)}(ARG("call", 3), 3) + call assert_report('should not get here') + catch /^call$/ + Xpath 'g' + catch /^0$/ " default return value + call assert_report('should not get here') + catch /.*/ + call assert_report('should not get here') + finally + call assert_equal("", v:errmsg) + let v:errmsg = "" + break + endtry + endwhile + + while 1 + try + Xpath 'h' + call {NAME("F", 4)}(ARG(4711, 4), 4) + Xpath 'i' + catch /.*/ + call assert_report('should not get here') + finally + call assert_equal("", v:errmsg) + let v:errmsg = "" + break + endtry + endwhile + + catch /^0$/ " default return value + call assert_report('should not get here') + catch /.*/ + call assert_report('should not get here') + finally + call assert_equal("", v:errmsg) + let v:errmsg = "" + break + endtry + endwhile + + call assert_equal('bAcdDBefEGCghFIai', g:Xpath) + delfunction F +endfunc + +"------------------------------------------------------------------------------- +" Test 68: :throw across function calls in expressions {{{1 +" +" On a function call within an expression, an exception might be +" thrown when evaluating the function name, during evaluation of the +" arguments, or when the function is being executed. The exception +" can be caught by the caller. +" +" This test reuses the functions THROW(), NAME(), and ARG() from the +" previous test. +"------------------------------------------------------------------------------- + +func Test_throw_across_call_expr() + XpathINIT + + func F(x, n) + if a:n == 2 + call assert_report('should not get here') + elseif a:n == 4 + Xpath 'a' + endif + return a:x + endfunction + + while 1 + try + let error = 0 + let v:errmsg = "" + + while 1 + try + Xpath 'b' + let var1 = {NAME(THROW("name", 1), 1)}(ARG(4711, 1), 1) + call assert_report('should not get here') + catch /^name$/ + Xpath 'c' + catch /.*/ + call assert_report('should not get here') + finally + call assert_equal("", v:errmsg) + let v:errmsg = "" + break + endtry + endwhile + call assert_true(!exists('var1')) + + while 1 + try + Xpath 'd' + let var2 = {NAME("F", 2)}(ARG(THROW("arg", 2), 2), 2) + call assert_report('should not get here') + catch /^arg$/ + Xpath 'e' + catch /.*/ + call assert_report('should not get here') + finally + call assert_equal("", v:errmsg) + let v:errmsg = "" + break + endtry + endwhile + call assert_true(!exists('var2')) + + while 1 + try + Xpath 'f' + let var3 = {NAME("THROW", 3)}(ARG("call", 3), 3) + call assert_report('should not get here') + catch /^call$/ + Xpath 'g' + catch /^0$/ " default return value + call assert_report('should not get here') + catch /.*/ + call assert_report('should not get here') + finally + call assert_equal("", v:errmsg) + let v:errmsg = "" + break + endtry + endwhile + call assert_true(!exists('var3')) + + while 1 + try + Xpath 'h' + let var4 = {NAME("F", 4)}(ARG(4711, 4), 4) + Xpath 'i' + catch /.*/ + call assert_report('should not get here') + finally + call assert_equal("", v:errmsg) + let v:errmsg = "" + break + endtry + endwhile + call assert_true(exists('var4') && var4 == 4711) + + catch /^0$/ " default return value + call assert_report('should not get here') + catch /.*/ + call assert_report('should not get here') + finally + call assert_equal("", v:errmsg) + break + endtry + endwhile + + call assert_equal('bAcdDBefEGCghFIai', g:Xpath) + delfunc F +endfunc + +"------------------------------------------------------------------------------- +" Test 76: Errors, interrupts, :throw during expression evaluation {{{1 +" +" When a function call made during expression evaluation is aborted +" due to an error inside a :try/:endtry region or due to an interrupt +" or a :throw, the expression evaluation is aborted as well. No +" message is displayed for the cancelled expression evaluation. On an +" error not inside :try/:endtry, the expression evaluation continues. +"------------------------------------------------------------------------------- + +func Test_expr_eval_error() + let test =<< trim [CODE] + let taken = "" + + func ERR(n) + let g:taken = g:taken .. "E" .. a:n + asdf + endfunc + + func ERRabort(n) abort + let g:taken = g:taken .. "A" .. a:n + asdf + endfunc " returns -1; may cause follow-up msg for illegal var/func name + + func WRAP(n, arg) + let g:taken = g:taken .. "W" .. a:n + let g:saved_errmsg = v:errmsg + return arg + endfunc + + func INT(n) + let g:taken = g:taken .. "I" .. a:n + call interrupt() + endfunc + + func THR(n) + let g:taken = g:taken .. "T" .. a:n + throw "should not be caught" + endfunc + + func CONT(n) + let g:taken = g:taken .. "C" .. a:n + endfunc + + func MSG(n) + let g:taken = g:taken .. "M" .. a:n + let errmsg = (a:n >= 37 && a:n <= 44) ? g:saved_errmsg : v:errmsg + let msgptn = (a:n >= 10 && a:n <= 27) ? "^$" : "asdf" + call assert_match(msgptn, errmsg) + let v:errmsg = "" + let g:saved_errmsg = "" + endfunc + + let v:errmsg = "" + + try + let t = 1 + while t <= 9 + Xloop 'a' + try + if t == 1 + let v{ERR(t) + CONT(t)} = 0 + elseif t == 2 + let v{ERR(t) + CONT(t)} + elseif t == 3 + let var = exists('v{ERR(t) + CONT(t)}') + elseif t == 4 + unlet v{ERR(t) + CONT(t)} + elseif t == 5 + function F{ERR(t) + CONT(t)}() + endfunction + elseif t == 6 + function F{ERR(t) + CONT(t)} + elseif t == 7 + let var = exists('*F{ERR(t) + CONT(t)}') + elseif t == 8 + delfunction F{ERR(t) + CONT(t)} + elseif t == 9 + let var = ERR(t) + CONT(t) + endif + catch /asdf/ + " v:errmsg is not set when the error message is converted to an + " exception. Set it to the original error message. + let v:errmsg = substitute(v:exception, '^Vim:', '', "") + catch /^Vim\((\a\+)\)\=:/ + " An error exception has been thrown after the original error. + let v:errmsg = "" + finally + call MSG(t) + let t = t + 1 + XloopNEXT + continue " discard an aborting error + endtry + endwhile + catch /.*/ + call assert_report('should not get here') + endtry + + try + let t = 10 + while t <= 18 + Xloop 'b' + try + if t == 10 + let v{INT(t) + CONT(t)} = 0 + elseif t == 11 + let v{INT(t) + CONT(t)} + elseif t == 12 + let var = exists('v{INT(t) + CONT(t)}') + elseif t == 13 + unlet v{INT(t) + CONT(t)} + elseif t == 14 + function F{INT(t) + CONT(t)}() + endfunction + elseif t == 15 + function F{INT(t) + CONT(t)} + elseif t == 16 + let var = exists('*F{INT(t) + CONT(t)}') + elseif t == 17 + delfunction F{INT(t) + CONT(t)} + elseif t == 18 + let var = INT(t) + CONT(t) + endif + catch /^Vim\((\a\+)\)\=:\(Interrupt\)\@!/ + " An error exception has been triggered after the interrupt. + let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "") + finally + call MSG(t) + let t = t + 1 + XloopNEXT + continue " discard interrupt + endtry + endwhile + catch /.*/ + call assert_report('should not get here') + endtry + + try + let t = 19 + while t <= 27 + Xloop 'c' + try + if t == 19 + let v{THR(t) + CONT(t)} = 0 + elseif t == 20 + let v{THR(t) + CONT(t)} + elseif t == 21 + let var = exists('v{THR(t) + CONT(t)}') + elseif t == 22 + unlet v{THR(t) + CONT(t)} + elseif t == 23 + function F{THR(t) + CONT(t)}() + endfunction + elseif t == 24 + function F{THR(t) + CONT(t)} + elseif t == 25 + let var = exists('*F{THR(t) + CONT(t)}') + elseif t == 26 + delfunction F{THR(t) + CONT(t)} + elseif t == 27 + let var = THR(t) + CONT(t) + endif + catch /^Vim\((\a\+)\)\=:/ + " An error exception has been triggered after the :throw. + let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "") + finally + call MSG(t) + let t = t + 1 + XloopNEXT + continue " discard exception + endtry + endwhile + catch /.*/ + call assert_report('should not get here') + endtry + + let v{ERR(28) + CONT(28)} = 0 + call MSG(28) + let v{ERR(29) + CONT(29)} + call MSG(29) + let var = exists('v{ERR(30) + CONT(30)}') + call MSG(30) + unlet v{ERR(31) + CONT(31)} + call MSG(31) + function F{ERR(32) + CONT(32)}() + endfunction + call MSG(32) + function F{ERR(33) + CONT(33)} + call MSG(33) + let var = exists('*F{ERR(34) + CONT(34)}') + call MSG(34) + delfunction F{ERR(35) + CONT(35)} + call MSG(35) + let var = ERR(36) + CONT(36) + call MSG(36) + + let saved_errmsg = "" + + let v{WRAP(37, ERRabort(37)) + CONT(37)} = 0 + call MSG(37) + let v{WRAP(38, ERRabort(38)) + CONT(38)} + call MSG(38) + let var = exists('v{WRAP(39, ERRabort(39)) + CONT(39)}') + call MSG(39) + unlet v{WRAP(40, ERRabort(40)) + CONT(40)} + call MSG(40) + function F{WRAP(41, ERRabort(41)) + CONT(41)}() + endfunction + call MSG(41) + function F{WRAP(42, ERRabort(42)) + CONT(42)} + call MSG(42) + let var = exists('*F{WRAP(43, ERRabort(43)) + CONT(43)}') + call MSG(43) + delfunction F{WRAP(44, ERRabort(44)) + CONT(44)} + call MSG(44) + let var = ERRabort(45) + CONT(45) + call MSG(45) + Xpath 'd' + + let expected = "" + \ .. "E1M1E2M2E3M3E4M4E5M5E6M6E7M7E8M8E9M9" + \ .. "I10M10I11M11I12M12I13M13I14M14I15M15I16M16I17M17I18M18" + \ .. "T19M19T20M20T21M21T22M22T23M23T24M24T25M25T26M26T27M27" + \ .. "E28C28M28E29C29M29E30C30M30E31C31M31E32C32M32E33C33M33" + \ .. "E34C34M34E35C35M35E36C36M36" + \ .. "A37W37C37M37A38W38C38M38A39W39C39M39A40W40C40M40A41W41C41M41" + \ .. "A42W42C42M42A43W43C43M43A44W44C44M44A45C45M45" + call assert_equal(expected, taken) + [CODE] + let verify =<< trim [CODE] + let expected = "a1a2a3a4a5a6a7a8a9" + \ .. "b10b11b12b13b14b15b16b17b18" + \ .. "c19c20c21c22c23c24c25c26c27d" + call assert_equal(expected, g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 77: Errors, interrupts, :throw in name{brace-expression} {{{1 +" +" When a function call made during evaluation of an expression in +" braces as part of a function name after ":function" is aborted due +" to an error inside a :try/:endtry region or due to an interrupt or +" a :throw, the expression evaluation is aborted as well, and the +" function definition is ignored, skipping all commands to the +" ":endfunction". On an error not inside :try/:endtry, the expression +" evaluation continues and the function gets defined, and can be +" called and deleted. +"------------------------------------------------------------------------------- +func Test_brace_expr_error() + let test =<< trim [CODE] + func ERR() abort + Xloop 'a' + asdf + endfunc " returns -1 + + func OK() + Xloop 'b' + let v:errmsg = "" + return 0 + endfunc + + let v:errmsg = "" + + Xpath 'c' + func F{1 + ERR() + OK()}(arg) + " F0 should be defined. + if exists("a:arg") && a:arg == "calling" + Xpath 'd' + else + call assert_report('should not get here') + endif + endfunction + call assert_equal("", v:errmsg) + XloopNEXT + + Xpath 'e' + call F{1 + ERR() + OK()}("calling") + call assert_equal("", v:errmsg) + XloopNEXT + + Xpath 'f' + delfunction F{1 + ERR() + OK()} + call assert_equal("", v:errmsg) + XloopNEXT + + try + while 1 + try + Xpath 'g' + func G{1 + ERR() + OK()}(arg) + " G0 should not be defined, and the function body should be + " skipped. + call assert_report('should not get here') + " Use an unmatched ":finally" to check whether the body is + " skipped when an error occurs in ERR(). This works whether or + " not the exception is converted to an exception. + finally + call assert_report('should not get here') + endtry + try + call assert_report('should not get here') + endfunction + + call assert_report('should not get here') + catch /asdf/ + " Jumped to when the function is not defined and the body is + " skipped. + Xpath 'h' + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'i' + break + endtry " jumped to when the body is not skipped + endwhile + catch /.*/ + call assert_report('should not get here') + endtry + [CODE] + let verify =<< trim [CODE] + call assert_equal('ca1b1ea2b2dfa3b3ga4hi', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 78: Messages on parsing errors in expression evaluation {{{1 +" +" When an expression evaluation detects a parsing error, an error +" message is given and converted to an exception, and the expression +" evaluation is aborted. +"------------------------------------------------------------------------------- +func Test_expr_eval_error_msg() + CheckEnglish + + let test =<< trim [CODE] + let taken = "" + + func F(n) + let g:taken = g:taken . "F" . a:n + endfunc + + func MSG(n, enr, emsg) + let g:taken = g:taken . "M" . a:n + call assert_match('^' .. a:enr .. ':', v:errmsg) + call assert_match(a:emsg, v:errmsg) + endfunc + + func CONT(n) + let g:taken = g:taken . "C" . a:n + endfunc + + let v:errmsg = "" + try + let t = 1 + while t <= 14 + let g:taken = g:taken . "T" . t + let v:errmsg = "" + try + if t == 1 + let v{novar + CONT(t)} = 0 + elseif t == 2 + let v{novar + CONT(t)} + elseif t == 3 + let var = exists('v{novar + CONT(t)}') + elseif t == 4 + unlet v{novar + CONT(t)} + elseif t == 5 + function F{novar + CONT(t)}() + endfunction + elseif t == 6 + function F{novar + CONT(t)} + elseif t == 7 + let var = exists('*F{novar + CONT(t)}') + elseif t == 8 + delfunction F{novar + CONT(t)} + elseif t == 9 + echo novar + CONT(t) + elseif t == 10 + echo v{novar + CONT(t)} + elseif t == 11 + echo F{novar + CONT(t)} + elseif t == 12 + let var = novar + CONT(t) + elseif t == 13 + let var = v{novar + CONT(t)} + elseif t == 14 + let var = F{novar + CONT(t)}() + endif + catch /^Vim\((\a\+)\)\=:/ + Xloop 'a' + " v:errmsg is not set when the error message is converted to an + " exception. Set it to the original error message. + let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "") + finally + Xloop 'b' + if t <= 8 && t != 3 && t != 7 + call MSG(t, 'E475', 'Invalid argument\>') + else + call MSG(t, 'E121', "Undefined variable") + endif + let t = t + 1 + XloopNEXT + continue " discard an aborting error + endtry + endwhile + catch /.*/ + call assert_report('should not get here') + endtry + + func T(n, expr, enr, emsg) + try + let g:taken = g:taken . "T" . a:n + let v:errmsg = "" + try + execute "let var = " . a:expr + catch /^Vim\((\a\+)\)\=:/ + Xloop 'c' + " v:errmsg is not set when the error message is converted to an + " exception. Set it to the original error message. + let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "") + finally + Xloop 'd' + call MSG(a:n, a:enr, a:emsg) + XloopNEXT + " Discard an aborting error: + return + endtry + catch /.*/ + call assert_report('should not get here') + endtry + endfunc + + call T(15, 'Nofunc() + CONT(15)', 'E117', "Unknown function") + call T(16, 'F(1 2 + CONT(16))', 'E116', "Invalid arguments") + call T(17, 'F(1, 2) + CONT(17)', 'E118', "Too many arguments") + call T(18, 'F() + CONT(18)', 'E119', "Not enough arguments") + call T(19, '{(1} + CONT(19)', 'E110', "Missing ')'") + call T(20, '("abc"[1) + CONT(20)', 'E111', "Missing ']'") + call T(21, '(1 +) + CONT(21)', 'E15', "Invalid expression") + call T(22, '1 2 + CONT(22)', 'E488', "Trailing characters: 2 +") + call T(23, '(1 ? 2) + CONT(23)', 'E109', "Missing ':' after '?'") + call T(24, '("abc) + CONT(24)', 'E114', "Missing double quote") + call T(25, "('abc) + CONT(25)", 'E115', "Missing single quote") + call T(26, '& + CONT(26)', 'E112', "Option name missing") + call T(27, '&asdf + CONT(27)', 'E113', "Unknown option") + + let expected = "" + \ .. "T1M1T2M2T3M3T4M4T5M5T6M6T7M7T8M8T9M9T10M10T11M11T12M12T13M13T14M14" + \ .. "T15M15T16M16T17M17T18M18T19M19T20M20T21M21T22M22T23M23T24M24T25M25" + \ .. "T26M26T27M27" + + call assert_equal(expected, taken) + [CODE] + let verify =<< trim [CODE] + let expected = "a1b1a2b2a3b3a4b4a5b5a6b6a7b7a8b8a9b9a10b10a11b11a12b12" + \ .. "a13b13a14b14c15d15c16d16c17d17c18d18c19d19c20d20" + \ .. "c21d21c22d22c23d23c24d24c25d25c26d26c27d27" + call assert_equal(expected, g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 79: Throwing one of several errors for the same command {{{1 +" +" When several errors appear in a row (for instance during expression +" evaluation), the first as the most specific one is used when +" throwing an error exception. If, however, a syntax error is +" detected afterwards, this one is used for the error exception. +" On a syntax error, the next command is not executed, on a normal +" error, however, it is (relevant only in a function without the +" "abort" flag). v:errmsg is not set. +" +" If throwing error exceptions is configured off, v:errmsg is always +" set to the latest error message, that is, to the more general +" message or the syntax error, respectively. +"------------------------------------------------------------------------------- +func Test_throw_multi_error() + CheckEnglish + + let test =<< trim [CODE] + func NEXT(cmd) + exec a:cmd . " | Xloop 'a'" + endfun + + call NEXT('echo novar') " (checks nextcmd) + XloopNEXT + call NEXT('let novar #') " (skips nextcmd) + XloopNEXT + call NEXT('unlet novar #') " (skips nextcmd) + XloopNEXT + call NEXT('let {novar}') " (skips nextcmd) + XloopNEXT + call NEXT('unlet{ novar}') " (skips nextcmd) + + call assert_equal('a1', g:Xpath) + XpathINIT + XloopINIT + + func EXEC(cmd) + exec a:cmd + endfunc + + try + while 1 " dummy loop + try + let v:errmsg = "" + call EXEC('echo novar') " normal error + catch /^Vim\((\a\+)\)\=:/ + Xpath 'b' + call assert_match('E121: Undefined variable: novar', v:exception) + finally + Xpath 'c' + call assert_equal("", v:errmsg) + break + endtry + endwhile + + Xpath 'd' + let cmd = "let" + while cmd != "" + try + let v:errmsg = "" + call EXEC(cmd . ' novar #') " normal plus syntax error + catch /^Vim\((\a\+)\)\=:/ + Xloop 'e' + if cmd =~ 'unlet' + " TODO: should get error for 'novar' + call assert_match('E488: Trailing characters', v:exception) + else + call assert_match('E121: Undefined variable: novar', v:exception) + endif + finally + Xloop 'f' + call assert_equal("", v:errmsg) + if cmd == "let" + let cmd = "unlet" + else + let cmd = "" + endif + XloopNEXT + continue + endtry + endwhile + + Xpath 'g' + let cmd = "let" + while cmd != "" + try + let v:errmsg = "" + call EXEC(cmd . ' {novar}') " normal plus syntax error + catch /^Vim\((\a\+)\)\=:/ + Xloop 'h' + call assert_match('E475: Invalid argument: {novar}', v:exception) + finally + Xloop 'i' + call assert_equal("", v:errmsg) + if cmd == "let" + let cmd = "unlet" + else + let cmd = "" + endif + XloopNEXT + continue + endtry + endwhile + catch /.*/ + call assert_report('should not get here') + endtry + Xpath 'j' + [CODE] + let verify =<< trim [CODE] + call assert_equal('bcde1f1e2f2gh3i3h4i4j', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 80: Syntax error in expression for illegal :elseif {{{1 +" +" If there is a syntax error in the expression after an illegal +" :elseif, an error message is given (or an error exception thrown) +" for the illegal :elseif rather than the expression error. +"------------------------------------------------------------------------------- +func Test_if_syntax_error() + CheckEnglish + + let test =<< trim [CODE] + let v:errmsg = "" + if 0 + else + elseif 1 ||| 2 + endif + Xpath 'a' + call assert_match('E584: :elseif after :else', v:errmsg) + + let v:errmsg = "" + if 1 + else + elseif 1 ||| 2 + endif + Xpath 'b' + call assert_match('E584: :elseif after :else', v:errmsg) + + let v:errmsg = "" + elseif 1 ||| 2 + Xpath 'c' + call assert_match('E582: :elseif without :if', v:errmsg) + + let v:errmsg = "" + while 1 + elseif 1 ||| 2 + endwhile + Xpath 'd' + call assert_match('E582: :elseif without :if', v:errmsg) + + while 1 + try + try + let v:errmsg = "" + if 0 + else + elseif 1 ||| 2 + endif + catch /^Vim\((\a\+)\)\=:/ + Xpath 'e' + call assert_match('E584: :elseif after :else', v:exception) + finally + Xpath 'f' + call assert_equal("", v:errmsg) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'g' + break + endtry + endwhile + + while 1 + try + try + let v:errmsg = "" + if 1 + else + elseif 1 ||| 2 + endif + catch /^Vim\((\a\+)\)\=:/ + Xpath 'h' + call assert_match('E584: :elseif after :else', v:exception) + finally + Xpath 'i' + call assert_equal("", v:errmsg) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'j' + break + endtry + endwhile + + while 1 + try + try + let v:errmsg = "" + elseif 1 ||| 2 + catch /^Vim\((\a\+)\)\=:/ + Xpath 'k' + call assert_match('E582: :elseif without :if', v:exception) + finally + Xpath 'l' + call assert_equal("", v:errmsg) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'm' + break + endtry + endwhile + + while 1 + try + try + let v:errmsg = "" + while 1 + elseif 1 ||| 2 + endwhile + catch /^Vim\((\a\+)\)\=:/ + Xpath 'n' + call assert_match('E582: :elseif without :if', v:exception) + finally + Xpath 'o' + call assert_equal("", v:errmsg) + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'p' + break + endtry + endwhile + Xpath 'q' + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcdefghijklmnopq', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 81: Discarding exceptions after an error or interrupt {{{1 +" +" When an exception is thrown from inside a :try conditional without +" :catch and :finally clauses and an error or interrupt occurs before +" the :endtry is reached, the exception is discarded. +"------------------------------------------------------------------------------- + +func Test_discard_exception_after_error_1() + let test =<< trim [CODE] + try + Xpath 'a' + try + Xpath 'b' + throw "arrgh" + call assert_report('should not get here') + if 1 + call assert_report('should not get here') + " error after :throw: missing :endif + endtry + call assert_report('should not get here') + catch /arrgh/ + call assert_report('should not get here') + endtry + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('ab', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +" interrupt the code before the endtry is invoked +func Test_discard_exception_after_error_2() + XpathINIT + let lines =<< trim [CODE] + try + Xpath 'a' + try + Xpath 'b' + throw "arrgh" + call assert_report('should not get here') + endtry " interrupt here + call assert_report('should not get here') + catch /arrgh/ + call assert_report('should not get here') + endtry + call assert_report('should not get here') + [CODE] + call writefile(lines, 'Xscript', 'D') + + breakadd file 7 Xscript + try + let caught_intr = 0 + debuggreedy + call feedkeys(":source Xscript\<CR>quit\<CR>", "xt") + catch /^Vim:Interrupt$/ + call assert_match('Xscript, line 7', v:throwpoint) + let caught_intr = 1 + endtry + 0debuggreedy + call assert_equal(1, caught_intr) + call assert_equal('ab', g:Xpath) + breakdel * +endfunc + +"------------------------------------------------------------------------------- +" Test 82: Ignoring :catch clauses after an error or interrupt {{{1 +" +" When an exception is thrown and an error or interrupt occurs before +" the matching :catch clause is reached, the exception is discarded +" and the :catch clause is ignored (also for the error or interrupt +" exception being thrown then). +"------------------------------------------------------------------------------- + +func Test_ignore_catch_after_error_1() + let test =<< trim [CODE] + try + try + Xpath 'a' + throw "arrgh" + call assert_report('should not get here') + if 1 + call assert_report('should not get here') + " error after :throw: missing :endif + catch /.*/ + call assert_report('should not get here') + catch /.*/ + call assert_report('should not get here') + endtry + call assert_report('should not get here') + catch /arrgh/ + call assert_report('should not get here') + endtry + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('a', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +func Test_ignore_catch_after_error_2() + let test =<< trim [CODE] + func E() + try + try + Xpath 'a' + throw "arrgh" + call assert_report('should not get here') + if 1 + call assert_report('should not get here') + " error after :throw: missing :endif + catch /.*/ + call assert_report('should not get here') + catch /.*/ + call assert_report('should not get here') + endtry + call assert_report('should not get here') + catch /arrgh/ + call assert_report('should not get here') + endtry + endfunc + + call E() + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('a', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +" interrupt right before a catch is invoked in a script +func Test_ignore_catch_after_intr_1() + " for unknown reasons this test sometimes fails on MS-Windows. + let g:test_is_flaky = 1 + + XpathINIT + let lines =<< trim [CODE] + try + try + Xpath 'a' + throw "arrgh" + call assert_report('should not get here') + catch /.*/ " interrupt here + call assert_report('should not get here') + catch /.*/ + call assert_report('should not get here') + endtry + call assert_report('should not get here') + catch /arrgh/ + call assert_report('should not get here') + endtry + call assert_report('should not get here') + [CODE] + call writefile(lines, 'Xscript', 'D') + + breakadd file 6 Xscript + try + let caught_intr = 0 + debuggreedy + call feedkeys(":source Xscript\<CR>quit\<CR>", "xt") + catch /^Vim:Interrupt$/ + call assert_match('Xscript, line 6', v:throwpoint) + let caught_intr = 1 + endtry + 0debuggreedy + call assert_equal(1, caught_intr) + call assert_equal('a', g:Xpath) + breakdel * +endfunc + +" interrupt right before a catch is invoked inside a function. +func Test_ignore_catch_after_intr_2() + " for unknown reasons this test sometimes fails on MS-Windows. + let g:test_is_flaky = 1 + + XpathINIT + func F() + try + try + Xpath 'a' + throw "arrgh" + call assert_report('should not get here') + catch /.*/ " interrupt here + call assert_report('should not get here') + catch /.*/ + call assert_report('should not get here') + endtry + call assert_report('should not get here') + catch /arrgh/ + call assert_report('should not get here') + endtry + call assert_report('should not get here') + endfunc + + breakadd func 6 F + try + let caught_intr = 0 + debuggreedy + call feedkeys(":call F()\<CR>quit\<CR>", "xt") + catch /^Vim:Interrupt$/ + call assert_match('\.F, line 6', v:throwpoint) + let caught_intr = 1 + endtry + 0debuggreedy + call assert_equal(1, caught_intr) + call assert_equal('a', g:Xpath) + breakdel * + delfunc F +endfunc + +"------------------------------------------------------------------------------- +" Test 83: Executing :finally clauses after an error or interrupt {{{1 +" +" When an exception is thrown and an error or interrupt occurs before +" the :finally of the innermost :try is reached, the exception is +" discarded and the :finally clause is executed. +"------------------------------------------------------------------------------- + +func Test_finally_after_error() + let test =<< trim [CODE] + try + Xpath 'a' + try + Xpath 'b' + throw "arrgh" + call assert_report('should not get here') + if 1 + call assert_report('should not get here') + " error after :throw: missing :endif + finally + Xpath 'c' + endtry + call assert_report('should not get here') + catch /arrgh/ + call assert_report('should not get here') + endtry + call assert_report('should not get here') + [CODE] + let verify =<< trim [CODE] + call assert_equal('abc', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +" interrupt the code right before the finally is invoked +func Test_finally_after_intr() + XpathINIT + let lines =<< trim [CODE] + try + Xpath 'a' + try + Xpath 'b' + throw "arrgh" + call assert_report('should not get here') + finally " interrupt here + Xpath 'c' + endtry + call assert_report('should not get here') + catch /arrgh/ + call assert_report('should not get here') + endtry + call assert_report('should not get here') + [CODE] + call writefile(lines, 'Xscript', 'D') + + breakadd file 7 Xscript + try + let caught_intr = 0 + debuggreedy + call feedkeys(":source Xscript\<CR>quit\<CR>", "xt") + catch /^Vim:Interrupt$/ + call assert_match('Xscript, line 7', v:throwpoint) + let caught_intr = 1 + endtry + 0debuggreedy + call assert_equal(1, caught_intr) + call assert_equal('abc', g:Xpath) + breakdel * +endfunc + +"------------------------------------------------------------------------------- +" Test 84: Exceptions in autocommand sequences. {{{1 +" +" When an exception occurs in a sequence of autocommands for +" a specific event, the rest of the sequence is not executed. The +" command that triggered the autocommand execution aborts, and the +" exception is propagated to the caller. +" +" For the FuncUndefined event under a function call expression or +" :call command, the function is not executed, even when it has +" been defined by the autocommands before the exception occurred. +"------------------------------------------------------------------------------- + +func Test_autocmd_exception() + let test =<< trim [CODE] + func INT() + call interrupt() + endfunc + + aug TMP + autocmd! + + autocmd User x1 Xpath 'a' + autocmd User x1 throw "x1" + autocmd User x1 call assert_report('should not get here') + + autocmd User x2 Xpath 'b' + autocmd User x2 asdf + autocmd User x2 call assert_report('should not get here') + + autocmd User x3 Xpath 'c' + autocmd User x3 call INT() + autocmd User x3 call assert_report('should not get here') + + autocmd FuncUndefined U1 func U1() + autocmd FuncUndefined U1 call assert_report('should not get here') + autocmd FuncUndefined U1 endfunc + autocmd FuncUndefined U1 Xpath 'd' + autocmd FuncUndefined U1 throw "U1" + autocmd FuncUndefined U1 call assert_report('should not get here') + + autocmd FuncUndefined U2 func U2() + autocmd FuncUndefined U2 call assert_report('should not get here') + autocmd FuncUndefined U2 endfunc + autocmd FuncUndefined U2 Xpath 'e' + autocmd FuncUndefined U2 ASDF + autocmd FuncUndefined U2 call assert_report('should not get here') + + autocmd FuncUndefined U3 func U3() + autocmd FuncUndefined U3 call assert_report('should not get here') + autocmd FuncUndefined U3 endfunc + autocmd FuncUndefined U3 Xpath 'f' + autocmd FuncUndefined U3 call INT() + autocmd FuncUndefined U3 call assert_report('should not get here') + aug END + + try + try + Xpath 'g' + doautocmd User x1 + catch /x1/ + Xpath 'h' + endtry + + while 1 + try + Xpath 'i' + doautocmd User x2 + catch /asdf/ + Xpath 'j' + finally + Xpath 'k' + break + endtry + endwhile + + while 1 + try + Xpath 'l' + doautocmd User x3 + catch /Vim:Interrupt/ + Xpath 'm' + finally + Xpath 'n' + " ... but break loop for caught interrupt exception, + " or discard interrupt and break loop if $VIMNOINTTHROW + break + endtry + endwhile + + if exists("*U1") | delfunction U1 | endif + if exists("*U2") | delfunction U2 | endif + if exists("*U3") | delfunction U3 | endif + + try + Xpath 'o' + call U1() + catch /U1/ + Xpath 'p' + endtry + + while 1 + try + Xpath 'q' + call U2() + catch /ASDF/ + Xpath 'r' + finally + Xpath 's' + " ... but break loop for caught error exception, + " or discard error and break loop if $VIMNOERRTHROW + break + endtry + endwhile + + while 1 + try + Xpath 't' + call U3() + catch /Vim:Interrupt/ + Xpath 'u' + finally + Xpath 'v' + " ... but break loop for caught interrupt exception, + " or discard interrupt and break loop if $VIMNOINTTHROW + break + endtry + endwhile + catch /.*/ + call assert_report('should not get here') + endtry + Xpath 'w' + [CODE] + let verify =<< trim [CODE] + call assert_equal('gahibjklcmnodpqerstfuvw', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 85: Error exceptions in autocommands for I/O command events {{{1 +" +" When an I/O command is inside :try/:endtry, autocommands to be +" executed after it should be skipped on an error (exception) in the +" command itself or in autocommands to be executed before the command. +" In the latter case, the I/O command should not be executed either. +" Example 1: BufWritePre, :write, BufWritePost +" Example 2: FileReadPre, :read, FileReadPost. +"------------------------------------------------------------------------------- + +func Test_autocmd_error_io_exception() + let test =<< trim [CODE] + " Remove the autocommands for the events specified as arguments in all used + " autogroups. + func Delete_autocommands(...) + let augfile = tempname() + while 1 + try + exec "redir >" . augfile + aug + redir END + exec "edit" augfile + g/^$/d + norm G$ + let wrap = "w" + while search('\%( \|^\)\@<=.\{-}\%( \)\@=', wrap) > 0 + let wrap = "W" + exec "norm y/ \n" + let argno = 1 + while argno <= a:0 + exec "au!" escape(@", " ") a:{argno} + let argno = argno + 1 + endwhile + endwhile + catch /.*/ + finally + bwipeout! + call delete(augfile) + break + endtry + endwhile + endfunc + + call Delete_autocommands("BufWritePre", "BufWritePost") + + while 1 + try + try + let post = 0 + aug TMP + au! BufWritePost * let post = 1 + aug END + write /n/o/n/e/x/i/s/t/e/n/t + catch /^Vim(write):/ + Xpath 'a' + call assert_match("E212: Can't open file for writing", v:exception) + finally + Xpath 'b' + call assert_equal(0, post) + au! TMP + aug! TMP + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'c' + break + endtry + endwhile + + while 1 + try + try + let post = 0 + aug TMP + au! BufWritePre * asdf + au! BufWritePost * let post = 1 + aug END + let tmpfile = tempname() + exec "write" tmpfile + catch /^Vim\((write)\)\=:/ + Xpath 'd' + call assert_match('E492: Not an editor command', v:exception) + finally + Xpath 'e' + if filereadable(tmpfile) + call assert_report('should not get here') + endif + call assert_equal(0, post) + au! TMP + aug! TMP + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'f' + break + endtry + endwhile + + call delete(tmpfile) + + call Delete_autocommands("BufWritePre", "BufWritePost", + \ "BufReadPre", "BufReadPost", "FileReadPre", "FileReadPost") + + while 1 + try + try + let post = 0 + aug TMP + au! FileReadPost * let post = 1 + aug END + let caught = 0 + read /n/o/n/e/x/i/s/t/e/n/t + catch /^Vim(read):/ + Xpath 'g' + call assert_match("E484: Can't open file", v:exception) + finally + Xpath 'h' + call assert_equal(0, post) + au! TMP + aug! TMP + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'i' + break + endtry + endwhile + + while 1 + try + let infile = tempname() + let tmpfile = tempname() + call writefile(["XYZ"], infile) + exec "edit" tmpfile + try + Xpath 'j' + try + let post = 0 + aug TMP + au! FileReadPre * asdf + au! FileReadPost * let post = 1 + aug END + exec "0read" infile + catch /^Vim\((read)\)\=:/ + Xpath 'k' + call assert_match('E492: Not an editor command', v:exception) + finally + Xpath 'l' + if getline("1") == "XYZ" + call assert_report('should not get here') + endif + call assert_equal(0, post) + au! TMP + aug! TMP + endtry + finally + Xpath 'm' + bwipeout! + endtry + catch /.*/ + call assert_report('should not get here') + finally + Xpath 'n' + break + endtry + endwhile + + call delete(infile) + call delete(tmpfile) + [CODE] + let verify =<< trim [CODE] + call assert_equal('abcdefghijklmn', g:Xpath) + [CODE] + call RunInNewVim(test, verify) +endfunc + +"------------------------------------------------------------------------------- +" Test 87 using (expr) ? funcref : funcref {{{1 +" +" Vim needs to correctly parse the funcref and even when it does +" not execute the funcref, it needs to consume the trailing () +"------------------------------------------------------------------------------- + +func Add2(x1, x2) + return a:x1 + a:x2 +endfu + +func GetStr() + return "abcdefghijklmnopqrstuvwxyp" +endfu + +func Test_funcref_with_condexpr() + call assert_equal(5, function('Add2')(2,3)) + + call assert_equal(3, 1 ? function('Add2')(1,2) : function('Add2')(2,3)) + call assert_equal(5, 0 ? function('Add2')(1,2) : function('Add2')(2,3)) + " Make sure, GetStr() still works. + call assert_equal('abcdefghijk', GetStr()[0:10]) +endfunc + +" Test 90: Recognizing {} in variable name. {{{1 +"------------------------------------------------------------------------------- + +func Test_curlies() + let s:var = 66 + let ns = 's' + call assert_equal(66, {ns}:var) + + let g:a = {} + let g:b = 't' + let g:a[g:b] = 77 + call assert_equal(77, g:a['t']) +endfunc + +"------------------------------------------------------------------------------- +" Test 91: using type(). {{{1 +"------------------------------------------------------------------------------- + +func Test_type() + call assert_equal(0, type(0)) + call assert_equal(1, type("")) + call assert_equal(2, type(function("tr"))) + call assert_equal(2, type(function("tr", [8]))) + call assert_equal(3, type([])) + call assert_equal(4, type({})) + call assert_equal(5, type(0.0)) + call assert_equal(6, type(v:false)) + call assert_equal(6, type(v:true)) + call assert_equal(7, type(v:none)) + call assert_equal(7, type(v:null)) + call assert_equal(8, v:t_job) + call assert_equal(9, v:t_channel) + call assert_equal(v:t_number, type(0)) + call assert_equal(v:t_string, type("")) + call assert_equal(v:t_func, type(function("tr"))) + call assert_equal(v:t_func, type(function("tr", [8]))) + call assert_equal(v:t_list, type([])) + call assert_equal(v:t_dict, type({})) + call assert_equal(v:t_float, type(0.0)) + call assert_equal(v:t_bool, type(v:false)) + call assert_equal(v:t_bool, type(v:true)) + call assert_equal(v:t_none, type(v:none)) + call assert_equal(v:t_none, type(v:null)) + call assert_equal(v:t_string, type(test_null_string())) + call assert_equal(v:t_func, type(test_null_function())) + call assert_equal(v:t_func, type(test_null_partial())) + call assert_equal(v:t_list, type(test_null_list())) + call assert_equal(v:t_dict, type(test_null_dict())) + if has('job') + call assert_equal(v:t_job, type(test_null_job())) + endif + if has('channel') + call assert_equal(v:t_channel, type(test_null_channel())) + endif + call assert_equal(v:t_blob, type(test_null_blob())) + + call assert_fails("call type(test_void())", ['E340:', 'E685:']) + call assert_fails("call type(test_unknown())", ['E340:', 'E685:']) + + call assert_equal(0, 0 + v:false) + call assert_equal(1, 0 + v:true) + call assert_equal(0, 0 + v:none) + call assert_equal(0, 0 + v:null) + + call assert_equal('v:false', '' . v:false) + call assert_equal('v:true', '' . v:true) + call assert_equal('v:none', '' . v:none) + call assert_equal('v:null', '' . v:null) + + call assert_true(v:false == 0) + call assert_false(v:false != 0) + call assert_true(v:true == 1) + call assert_false(v:true != 1) + call assert_false(v:true == v:false) + call assert_true(v:true != v:false) + + call assert_true(v:null == 0) + call assert_false(v:null == 1) + call assert_false(v:null != 0) + call assert_true(v:none == 0) + call assert_false(v:none == 1) + call assert_false(v:none != 0) + call assert_true(v:null == 0.0) + call assert_false(v:null == 0.1) + call assert_false(v:null != 0.0) + + call assert_true(v:false is v:false) + call assert_true(v:true is v:true) + call assert_true(v:none is v:none) + call assert_true(v:null is v:null) + + call assert_false(v:false isnot v:false) + call assert_false(v:true isnot v:true) + call assert_false(v:none isnot v:none) + call assert_false(v:null isnot v:null) + + call assert_false(v:false is 0) + call assert_false(v:true is 1) + call assert_false(v:true is v:false) + call assert_false(v:none is 0) + call assert_false(v:none is []) + call assert_false(v:none is {}) + call assert_false(v:none is 'text') + call assert_false(v:null is 0) + call assert_false(v:null is v:none) + + call assert_true(v:false isnot 0) + call assert_true(v:true isnot 1) + call assert_true(v:true isnot v:false) + call assert_true(v:none isnot 0) + call assert_true(v:null isnot 0) + call assert_true(v:null isnot v:none) + + call assert_equal(v:false, eval(string(v:false))) + call assert_equal(v:true, eval(string(v:true))) + call assert_equal(v:none, eval(string(v:none))) + call assert_equal(v:null, eval(string(v:null))) + + call assert_equal(v:false, copy(v:false)) + call assert_equal(v:true, copy(v:true)) + call assert_equal(v:none, copy(v:none)) + call assert_equal(v:null, copy(v:null)) + + call assert_equal([v:false], deepcopy([v:false])) + call assert_equal([v:true], deepcopy([v:true])) + call assert_equal([v:none], deepcopy([v:none])) + call assert_equal([v:null], deepcopy([v:null])) + + call assert_true(empty(v:false)) + call assert_false(empty(v:true)) + call assert_true(empty(v:null)) + call assert_true(empty(v:none)) + + func ChangeYourMind() + try + return v:true + finally + return 'something else' + endtry + endfunc + + call ChangeYourMind() +endfunc + +func Test_typename() + call assert_equal('number', typename(123)) + call assert_equal('string', typename('x')) + call assert_equal('list<number>', typename([123])) + call assert_equal('dict<number>', typename(#{key: 123})) + call assert_equal('list<dict<number>>', typename([#{key: 123}])) + + let l = [] + let d = #{a: 0} + let l = [d] + let l[0].e = #{b: l} + call assert_equal('list<dict<any>>', typename(l)) + call assert_equal('dict<any>', typename(d)) +endfunc + +"------------------------------------------------------------------------------- +" Test 92: skipping code {{{1 +"------------------------------------------------------------------------------- + +func Test_skip() + let Fn = function('Test_type') + call assert_false(0 && Fn[1]) + call assert_false(0 && string(Fn)) + call assert_false(0 && len(Fn)) + let l = [] + call assert_false(0 && l[1]) + call assert_false(0 && string(l)) + call assert_false(0 && len(l)) + let f = 1.0 + call assert_false(0 && f[1]) + call assert_false(0 && string(f)) + call assert_false(0 && len(f)) + let sp = v:null + call assert_false(0 && sp[1]) + call assert_false(0 && string(sp)) + call assert_false(0 && len(sp)) + +endfunc + +"------------------------------------------------------------------------------- +" Test 93: :echo and string() {{{1 +"------------------------------------------------------------------------------- + +func Test_echo_and_string() + " String + let a = 'foo bar' + redir => result + echo a + echo string(a) + redir END + let l = split(result, "\n") + call assert_equal(["foo bar", + \ "'foo bar'"], l) + + " Float + let a = -1.2e0 + redir => result + echo a + echo string(a) + redir END + let l = split(result, "\n") + call assert_equal(["-1.2", + \ "-1.2"], l) + + " Funcref + redir => result + echo function('string') + echo string(function('string')) + redir END + let l = split(result, "\n") + call assert_equal(["string", + \ "function('string')"], l) + + " Recursive dictionary + let a = {} + let a["a"] = a + redir => result + echo a + echo string(a) + redir END + let l = split(result, "\n") + call assert_equal(["{'a': {...}}", + \ "{'a': {...}}"], l) + + " Recursive list + let a = [0] + let a[0] = a + redir => result + echo a + echo string(a) + redir END + let l = split(result, "\n") + call assert_equal(["[[...]]", + \ "[[...]]"], l) + + " Empty dictionaries in a list + let a = {} + redir => result + echo [a, a, a] + echo string([a, a, a]) + redir END + let l = split(result, "\n") + call assert_equal(["[{}, {}, {}]", + \ "[{}, {}, {}]"], l) + + " Empty dictionaries in a dictionary + let a = {} + let b = {"a": a, "b": a} + redir => result + echo b + echo string(b) + redir END + let l = split(result, "\n") + call assert_equal(["{'a': {}, 'b': {}}", + \ "{'a': {}, 'b': {}}"], l) + + " Empty lists in a list + let a = [] + redir => result + echo [a, a, a] + echo string([a, a, a]) + redir END + let l = split(result, "\n") + call assert_equal(["[[], [], []]", + \ "[[], [], []]"], l) + + " Empty lists in a dictionary + let a = [] + let b = {"a": a, "b": a} + redir => result + echo b + echo string(b) + redir END + let l = split(result, "\n") + call assert_equal(["{'a': [], 'b': []}", + \ "{'a': [], 'b': []}"], l) + + " Dictionaries in a list + let a = {"one": "yes", "two": "yes", "three": "yes"} + redir => result + echo [a, a, a] + echo string([a, a, a]) + redir END + let l = split(result, "\n") + call assert_equal(["[{'one': 'yes', 'two': 'yes', 'three': 'yes'}, {...}, {...}]", + \ "[{'one': 'yes', 'two': 'yes', 'three': 'yes'}, {'one': 'yes', 'two': 'yes', 'three': 'yes'}, {'one': 'yes', 'two': 'yes', 'three': 'yes'}]"], l) + + " Dictionaries in a dictionary + let a = {"one": "yes", "two": "yes", "three": "yes"} + let b = {"a": a, "b": a} + redir => result + echo b + echo string(b) + redir END + let l = split(result, "\n") + call assert_equal(["{'a': {'one': 'yes', 'two': 'yes', 'three': 'yes'}, 'b': {...}}", + \ "{'a': {'one': 'yes', 'two': 'yes', 'three': 'yes'}, 'b': {'one': 'yes', 'two': 'yes', 'three': 'yes'}}"], l) + + " Lists in a list + let a = [1, 2, 3] + redir => result + echo [a, a, a] + echo string([a, a, a]) + redir END + let l = split(result, "\n") + call assert_equal(["[[1, 2, 3], [...], [...]]", + \ "[[1, 2, 3], [1, 2, 3], [1, 2, 3]]"], l) + + " Lists in a dictionary + let a = [1, 2, 3] + let b = {"a": a, "b": a} + redir => result + echo b + echo string(b) + redir END + let l = split(result, "\n") + call assert_equal(["{'a': [1, 2, 3], 'b': [...]}", + \ "{'a': [1, 2, 3], 'b': [1, 2, 3]}"], l) + + call assert_fails('echo &:', 'E112:') + call assert_fails('echo &g:', 'E112:') + call assert_fails('echo &l:', 'E112:') + +endfunc + +"------------------------------------------------------------------------------- +" Test 94: 64-bit Numbers {{{1 +"------------------------------------------------------------------------------- + +func Test_num64() + call assert_notequal( 4294967296, 0) + call assert_notequal(-4294967296, 0) + call assert_equal( 4294967296, 0xFFFFffff + 1) + call assert_equal(-4294967296, -0xFFFFffff - 1) + + call assert_equal( 9223372036854775807, 1 / 0) + call assert_equal(-9223372036854775807, -1 / 0) + call assert_equal(-9223372036854775807 - 1, 0 / 0) + + call assert_equal( 0x7FFFffffFFFFffff, float2nr( 1.0e150)) + call assert_equal(-0x7FFFffffFFFFffff, float2nr(-1.0e150)) + + let rng = range(0xFFFFffff, 0x100000001) + call assert_equal([0xFFFFffff, 0x100000000, 0x100000001], rng) + call assert_equal(0x100000001, max(rng)) + call assert_equal(0xFFFFffff, min(rng)) + call assert_equal(rng, sort(range(0x100000001, 0xFFFFffff, -1), 'N')) +endfunc + +"------------------------------------------------------------------------------- +" Test 95: lines of :append, :change, :insert {{{1 +"------------------------------------------------------------------------------- + +func DefineFunction(name, body) + let func = join(['function! ' . a:name . '()'] + a:body + ['endfunction'], "\n") + exec func +endfunc + +func Test_script_lines() + " :append + try + call DefineFunction('T_Append', [ + \ 'append', + \ 'py <<EOS', + \ '.', + \ ]) + catch + call assert_report("Can't define function") + endtry + try + call DefineFunction('T_Append', [ + \ 'append', + \ 'abc', + \ ]) + call assert_report("Shouldn't be able to define function") + catch + call assert_exception('Vim(function):E1145: Missing heredoc end marker: .') + endtry + + " :change + try + call DefineFunction('T_Change', [ + \ 'change', + \ 'py <<EOS', + \ '.', + \ ]) + catch + call assert_report("Can't define function") + endtry + try + call DefineFunction('T_Change', [ + \ 'change', + \ 'abc', + \ ]) + call assert_report("Shouldn't be able to define function") + catch + call assert_exception('Vim(function):E1145: Missing heredoc end marker: .') + endtry + + " :insert + try + call DefineFunction('T_Insert', [ + \ 'insert', + \ 'py <<EOS', + \ '.', + \ ]) + catch + call assert_report("Can't define function") + endtry + try + call DefineFunction('T_Insert', [ + \ 'insert', + \ 'abc', + \ ]) + call assert_report("Shouldn't be able to define function") + catch + call assert_exception('Vim(function):E1145: Missing heredoc end marker: .') + endtry +endfunc + +"------------------------------------------------------------------------------- +" Test 96: line continuation {{{1 +" +" Undefined behavior was detected by ubsan with line continuation +" after an empty line. +"------------------------------------------------------------------------------- +func Test_script_empty_line_continuation() + + \ +endfunc + +"------------------------------------------------------------------------------- +" Test 97: bitwise functions {{{1 +"------------------------------------------------------------------------------- +func Test_bitwise_functions() + " and + call assert_equal(127, and(127, 127)) + call assert_equal(16, and(127, 16)) + eval 127->and(16)->assert_equal(16) + call assert_equal(0, and(127, 128)) + call assert_fails("call and([], 1)", 'E745:') + call assert_fails("call and({}, 1)", 'E728:') + call assert_fails("call and(1.0, 1)", 'E805:') + call assert_fails("call and(1, 1.0)", 'E805:') + call assert_fails("call and(1, [])", 'E745:') + call assert_fails("call and(1, {})", 'E728:') + " or + call assert_equal(23, or(16, 7)) + call assert_equal(15, or(8, 7)) + eval 8->or(7)->assert_equal(15) + call assert_equal(123, or(0, 123)) + call assert_fails("call or([], 1)", 'E745:') + call assert_fails("call or({}, 1)", 'E728:') + call assert_fails("call or(1.0, 1)", 'E805:') + call assert_fails("call or(1, 1.0)", 'E805:') + call assert_fails("call or(1, [])", 'E745:') + call assert_fails("call or(1, {})", 'E728:') + " xor + call assert_equal(0, xor(127, 127)) + call assert_equal(111, xor(127, 16)) + eval 127->xor(16)->assert_equal(111) + call assert_equal(255, xor(127, 128)) + call assert_fails("call xor(1.0, 1)", 'E805:') + call assert_fails("call xor(1, 1.0)", 'E805:') + call assert_fails("call xor([], 1)", 'E745:') + call assert_fails("call xor({}, 1)", 'E728:') + call assert_fails("call xor(1, [])", 'E745:') + call assert_fails("call xor(1, {})", 'E728:') + " invert + call assert_equal(65408, and(invert(127), 65535)) + eval 127->invert()->and(65535)->assert_equal(65408) + call assert_equal(65519, and(invert(16), 65535)) + call assert_equal(65407, and(invert(128), 65535)) + call assert_fails("call invert(1.0)", 'E805:') + call assert_fails("call invert([])", 'E745:') + call assert_fails("call invert({})", 'E728:') +endfunc + +" Test using bang after user command {{{1 +func Test_user_command_with_bang() + command -bang Nieuw let nieuw = 1 + Ni! + call assert_equal(1, nieuw) + unlet nieuw + delcommand Nieuw +endfunc + +func Test_script_expand_sfile() + let lines =<< trim END + func s:snr() + return expand('<sfile>') + endfunc + let g:result = s:snr() + END + call writefile(lines, 'Xexpand', 'D') + source Xexpand + call assert_match('<SNR>\d\+_snr', g:result) + source Xexpand + call assert_match('<SNR>\d\+_snr', g:result) + + unlet g:result +endfunc + +func Test_compound_assignment_operators() + " Test for number + let x = 1 + let x += 10 + call assert_equal(11, x) + let x -= 5 + call assert_equal(6, x) + let x *= 4 + call assert_equal(24, x) + let x /= 3 + call assert_equal(8, x) + let x %= 3 + call assert_equal(2, x) + let x .= 'n' + call assert_equal('2n', x) + + " Test special cases: division or modulus with 0. + let x = 1 + let x /= 0 + call assert_equal(0x7FFFFFFFFFFFFFFF, x) + + let x = -1 + let x /= 0 + call assert_equal(-0x7FFFFFFFFFFFFFFF, x) + + let x = 0 + let x /= 0 + call assert_equal(-0x7FFFFFFFFFFFFFFF - 1, x) + + let x = 1 + let x %= 0 + call assert_equal(0, x) + + let x = -1 + let x %= 0 + call assert_equal(0, x) + + let x = 0 + let x %= 0 + call assert_equal(0, x) + + " Test for string + let x = 'str' + let x .= 'ing' + call assert_equal('string', x) + let x += 1 + call assert_equal(1, x) + + " Test for float + let x -= 1.5 + call assert_equal(-0.5, x) + let x = 0.5 + let x += 4.5 + call assert_equal(5.0, x) + let x -= 1.5 + call assert_equal(3.5, x) + let x *= 3.0 + call assert_equal(10.5, x) + let x /= 2.5 + call assert_equal(4.2, x) + call assert_fails('let x %= 0.5', 'E734:') + call assert_fails('let x .= "f"', 'E734:') + let x = !3.14 + call assert_equal(0.0, x) + + " integer and float operations + let x = 1 + let x *= 2.1 + call assert_equal(2.1, x) + let x = 1 + let x /= 0.25 + call assert_equal(4.0, x) + let x = 1 + call assert_fails('let x %= 0.25', 'E734:') + let x = 1 + call assert_fails('let x .= 0.25', 'E734:') + let x = 1.0 + call assert_fails('let x += [1.1]', 'E734:') + + " Test for environment variable + let $FOO = 1 + call assert_fails('let $FOO += 1', 'E734:') + call assert_fails('let $FOO -= 1', 'E734:') + call assert_fails('let $FOO *= 1', 'E734:') + call assert_fails('let $FOO /= 1', 'E734:') + call assert_fails('let $FOO %= 1', 'E734:') + let $FOO .= 's' + call assert_equal('1s', $FOO) + unlet $FOO + + " Test for option variable (type: number) + let &scrolljump = 1 + let &scrolljump += 5 + call assert_equal(6, &scrolljump) + let &scrolljump -= 2 + call assert_equal(4, &scrolljump) + let &scrolljump *= 3 + call assert_equal(12, &scrolljump) + let &scrolljump /= 2 + call assert_equal(6, &scrolljump) + let &scrolljump %= 5 + call assert_equal(1, &scrolljump) + call assert_fails('let &scrolljump .= "j"', ['E734:', 'E734:']) + set scrolljump&vim + + let &foldlevelstart = 2 + let &foldlevelstart -= 1 + call assert_equal(1, &foldlevelstart) + let &foldlevelstart -= 1 + call assert_equal(0, &foldlevelstart) + let &foldlevelstart = 2 + let &foldlevelstart -= 2 + call assert_equal(0, &foldlevelstart) + + " Test for register + let @/ = 1 + call assert_fails('let @/ += 1', 'E734:') + call assert_fails('let @/ -= 1', 'E734:') + call assert_fails('let @/ *= 1', 'E734:') + call assert_fails('let @/ /= 1', 'E734:') + call assert_fails('let @/ %= 1', 'E734:') + let @/ .= 's' + call assert_equal('1s', @/) + let @/ = '' +endfunc + +func Test_unlet_env() + let $TESTVAR = 'yes' + call assert_equal('yes', $TESTVAR) + call assert_fails('lockvar $TESTVAR', 'E940:') + call assert_fails('unlockvar $TESTVAR', 'E940:') + call assert_equal('yes', $TESTVAR) + if 0 + unlet $TESTVAR + endif + call assert_equal('yes', $TESTVAR) + unlet $TESTVAR + call assert_equal('', $TESTVAR) +endfunc + +func Test_refcount() + " Immediate values + call assert_equal(-1, test_refcount(1)) + call assert_equal(-1, test_refcount('s')) + call assert_equal(-1, test_refcount(v:true)) + call assert_equal(0, test_refcount([])) + call assert_equal(0, test_refcount({})) + call assert_equal(0, test_refcount(0zff)) + call assert_equal(0, test_refcount({-> line('.')})) + call assert_equal(-1, test_refcount(0.1)) + if has('job') + call assert_equal(0, test_refcount(job_start([&shell, &shellcmdflag, 'echo .']))) + endif + + " No refcount types + let x = 1 + call assert_equal(-1, test_refcount(x)) + let x = 's' + call assert_equal(-1, test_refcount(x)) + let x = v:true + call assert_equal(-1, test_refcount(x)) + let x = 0.1 + call assert_equal(-1, test_refcount(x)) + + " Check refcount + let x = [] + call assert_equal(1, test_refcount(x)) + + let x = {} + call assert_equal(1, x->test_refcount()) + + let x = 0zff + call assert_equal(1, test_refcount(x)) + + let X = {-> line('.')} + call assert_equal(1, test_refcount(X)) + let Y = X + call assert_equal(2, test_refcount(X)) + + if has('job') + let job = job_start([&shell, &shellcmdflag, 'echo .']) + call assert_equal(1, test_refcount(job)) + call assert_equal(1, test_refcount(job_getchannel(job))) + call assert_equal(1, test_refcount(job)) + endif + + " Function arguments, copying and unassigning + func ExprCheck(x, i) + let i = a:i + 1 + call assert_equal(i, test_refcount(a:x)) + let Y = a:x + call assert_equal(i + 1, test_refcount(a:x)) + call assert_equal(test_refcount(a:x), test_refcount(Y)) + let Y = 0 + call assert_equal(i, test_refcount(a:x)) + endfunc + call ExprCheck([], 0) + call ExprCheck({}, 0) + call ExprCheck(0zff, 0) + call ExprCheck({-> line('.')}, 0) + if has('job') + call ExprCheck(job, 1) + call ExprCheck(job_getchannel(job), 1) + call job_stop(job) + endif + delfunc ExprCheck + + " Regarding function + func Func(x) abort + call assert_equal(2, test_refcount(function('Func'))) + call assert_equal(0, test_refcount(funcref('Func'))) + endfunc + call assert_equal(1, test_refcount(function('Func'))) + call assert_equal(0, test_refcount(function('Func', [1]))) + call assert_equal(0, test_refcount(funcref('Func'))) + call assert_equal(0, test_refcount(funcref('Func', [1]))) + let X = function('Func') + let Y = X + call assert_equal(1, test_refcount(X)) + let X = function('Func', [1]) + let Y = X + call assert_equal(2, test_refcount(X)) + let X = funcref('Func') + let Y = X + call assert_equal(2, test_refcount(X)) + let X = funcref('Func', [1]) + let Y = X + call assert_equal(2, test_refcount(X)) + unlet X + unlet Y + call Func(1) + delfunc Func + + " Function with dict + func DictFunc() dict + call assert_equal(3, test_refcount(self)) + endfunc + let d = {'Func': function('DictFunc')} + call assert_equal(1, test_refcount(d)) + call assert_equal(0, test_refcount(d.Func)) + call d.Func() + unlet d + delfunc DictFunc + + if has('channel') + call assert_equal(-1, test_refcount(test_null_job())) + call assert_equal(-1, test_refcount(test_null_channel())) + endif + call assert_equal(-1, test_refcount(test_null_function())) + call assert_equal(-1, test_refcount(test_null_partial())) + call assert_equal(-1, test_refcount(test_null_blob())) + call assert_equal(-1, test_refcount(test_null_list())) + call assert_equal(-1, test_refcount(test_null_dict())) +endfunc + +" Test for missing :endif, :endfor, :endwhile and :endtry {{{1 +func Test_missing_end() + call writefile(['if 2 > 1', 'echo ">"'], 'Xscript', 'D') + call assert_fails('source Xscript', 'E171:') + call writefile(['for i in range(5)', 'echo i'], 'Xscript') + call assert_fails('source Xscript', 'E170:') + call writefile(['while v:true', 'echo "."'], 'Xscript') + call assert_fails('source Xscript', 'E170:') + call writefile(['try', 'echo "."'], 'Xscript') + call assert_fails('source Xscript', 'E600:') + + " Using endfor with :while + let caught_e732 = 0 + try + while v:true + endfor + catch /E732:/ + let caught_e732 = 1 + endtry + call assert_equal(1, caught_e732) + + " Using endwhile with :for + let caught_e733 = 0 + try + for i in range(1) + endwhile + catch /E733:/ + let caught_e733 = 1 + endtry + call assert_equal(1, caught_e733) + + " Using endfunc with :if + call assert_fails('exe "if 1 | endfunc | endif"', 'E193:') + + " Missing 'in' in a :for statement + call assert_fails('for i range(1) | endfor', 'E690:') + + " Incorrect number of variables in for + call assert_fails('for [i,] in range(3) | endfor', 'E475:') +endfunc + +" Test for deep nesting of if/for/while/try statements {{{1 +func Test_deep_nest() + CheckRunVimInTerminal + + let lines =<< trim [SCRIPT] + " Deep nesting of if ... endif + func Test1() + let @a = join(repeat(['if v:true'], 51), "\n") + let @a ..= "\n" + let @a ..= join(repeat(['endif'], 51), "\n") + @a + let @a = '' + endfunc + + " Deep nesting of for ... endfor + func Test2() + let @a = join(repeat(['for i in [1]'], 51), "\n") + let @a ..= "\n" + let @a ..= join(repeat(['endfor'], 51), "\n") + @a + let @a = '' + endfunc + + " Deep nesting of while ... endwhile + func Test3() + let @a = join(repeat(['while v:true'], 51), "\n") + let @a ..= "\n" + let @a ..= join(repeat(['endwhile'], 51), "\n") + @a + let @a = '' + endfunc + + " Deep nesting of try ... endtry + func Test4() + let @a = join(repeat(['try'], 51), "\n") + let @a ..= "\necho v:true\n" + let @a ..= join(repeat(['endtry'], 51), "\n") + @a + let @a = '' + endfunc + + " Deep nesting of function ... endfunction + func Test5() + let @a = join(repeat(['function X()'], 51), "\n") + let @a ..= "\necho v:true\n" + let @a ..= join(repeat(['endfunction'], 51), "\n") + @a + let @a = '' + endfunc + [SCRIPT] + call writefile(lines, 'Xscript', 'D') + + let buf = RunVimInTerminal('-S Xscript', {'rows': 6}) + + " Deep nesting of if ... endif + call term_sendkeys(buf, ":call Test1()\n") + call TermWait(buf) + call WaitForAssert({-> assert_match('^E579:', term_getline(buf, 5))}) + + " Deep nesting of for ... endfor + call term_sendkeys(buf, ":call Test2()\n") + call TermWait(buf) + call WaitForAssert({-> assert_match('^E585:', term_getline(buf, 5))}) + + " Deep nesting of while ... endwhile + call term_sendkeys(buf, ":call Test3()\n") + call TermWait(buf) + call WaitForAssert({-> assert_match('^E585:', term_getline(buf, 5))}) + + " Deep nesting of try ... endtry + call term_sendkeys(buf, ":call Test4()\n") + call TermWait(buf) + call WaitForAssert({-> assert_match('^E601:', term_getline(buf, 5))}) + + " Deep nesting of function ... endfunction + call term_sendkeys(buf, ":call Test5()\n") + call TermWait(buf) + call WaitForAssert({-> assert_match('^E1058:', term_getline(buf, 4))}) + call term_sendkeys(buf, "\<C-C>\n") + call TermWait(buf) + + "let l = '' + "for i in range(1, 6) + " let l ..= term_getline(buf, i) . "\n" + "endfor + "call assert_report(l) + + call StopVimInTerminal(buf) +endfunc + +" Test for errors in converting to float from various types {{{1 +func Test_float_conversion_errors() + call assert_fails('let x = 4.0 % 2.0', 'E804:') + call assert_fails('echo 1.1[0]', 'E806:') + call assert_fails('echo sort([function("min"), 1], "f")', 'E891:') + call assert_fails('echo 3.2 == "vim"', 'E892:') + call assert_fails('echo sort([[], 1], "f")', 'E893:') + call assert_fails('echo sort([{}, 1], "f")', 'E894:') + call assert_fails('echo 3.2 == v:true', 'E362:') + call assert_fails('echo 3.2 == v:none', 'E907:') +endfunc + +" invalid function names {{{1 +func Test_invalid_function_names() + " function name not starting with capital + let caught_e128 = 0 + try + func! g:test() + echo "test" + endfunc + catch /E128:/ + let caught_e128 = 1 + endtry + call assert_equal(1, caught_e128) + + " function name includes a colon + let caught_e884 = 0 + try + func! b:test() + echo "test" + endfunc + catch /E884:/ + let caught_e884 = 1 + endtry + call assert_equal(1, caught_e884) + + " function name followed by # + let caught_e128 = 0 + try + func! test2() "# + echo "test2" + endfunc + catch /E128:/ + let caught_e128 = 1 + endtry + call assert_equal(1, caught_e128) + + " function name starting with/without "g:", buffer-local funcref. + function! g:Foo(n) + return 'called Foo(' . a:n . ')' + endfunction + let b:my_func = function('Foo') + call assert_equal('called Foo(1)', b:my_func(1)) + call assert_equal('called Foo(2)', g:Foo(2)) + call assert_equal('called Foo(3)', Foo(3)) + delfunc g:Foo + + " script-local function used in Funcref must exist. + let lines =<< trim END + func s:Testje() + return "foo" + endfunc + let Bar = function('s:Testje') + call assert_equal(0, exists('s:Testje')) + call assert_equal(1, exists('*s:Testje')) + call assert_equal(1, exists('Bar')) + call assert_equal(1, exists('*Bar')) + END + call writefile(lines, 'Xscript', 'D') + source Xscript +endfunc + +" substring and variable name {{{1 +func Test_substring_var() + let str = 'abcdef' + let n = 3 + call assert_equal('def', str[n:]) + call assert_equal('abcd', str[:n]) + call assert_equal('d', str[n:n]) + unlet n + let nn = 3 + call assert_equal('def', str[nn:]) + call assert_equal('abcd', str[:nn]) + call assert_equal('d', str[nn:nn]) + unlet nn + let b:nn = 4 + call assert_equal('ef', str[b:nn:]) + call assert_equal('abcde', str[:b:nn]) + call assert_equal('e', str[b:nn:b:nn]) + unlet b:nn +endfunc + +" Test using s: with a typed command {{{1 +func Test_typed_script_var() + CheckRunVimInTerminal + + let buf = RunVimInTerminal('', {'rows': 6}) + + " Deep nesting of if ... endif + call term_sendkeys(buf, ":echo get(s:, 'foo', 'x')\n") + call TermWait(buf) + call WaitForAssert({-> assert_match('^E116:', term_getline(buf, 5))}) + + call StopVimInTerminal(buf) +endfunc + +" Test for issue6776 {{{1 +func Test_ternary_expression() + try + call eval('0 ? 0') + catch + endtry + " previous failure should not cause next expression to fail + call assert_equal(v:false, eval(string(v:false))) + + try + call eval('0 ? "burp') + catch + endtry + " previous failure should not cause next expression to fail + call assert_equal(v:false, eval(string(v:false))) + + try + call eval('1 ? 0 : "burp') + catch + endtry + " previous failure should not cause next expression to fail + call assert_equal(v:false, eval(string(v:false))) +endfunction + +func Test_for_over_string() + let res = '' + for c in 'aéc̀d' + let res ..= c .. '-' + endfor + call assert_equal('a-é-c̀-d-', res) + + let res = '' + for c in '' + let res ..= c .. '-' + endfor + call assert_equal('', res) + + let res = '' + for c in test_null_string() + let res ..= c .. '-' + endfor + call assert_equal('', res) +endfunc + +" Test for deeply nested :source command {{{1 +func Test_deeply_nested_source() + let lines =<< trim END + + so + sil 0scr + delete + so + 0 + END + call writefile(["vim9 silent! @0 \n/"] + lines, 'Xnested.vim', 'D') + + " this must not crash + let cmd = GetVimCommand() .. " -e -s -S Xnested.vim -c qa!" + call system(cmd) +endfunc + +"------------------------------------------------------------------------------- +" Modelines {{{1 +" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker +"-------------------------------------------------------------------------------