Mercurial > vim
view src/testdir/test_vimscript.vim @ 27199:d4f254d02d7c v8.2.4128
patch 8.2.4128: crash when method cannot be found
Commit: https://github.com/vim/vim/commit/64283d5e1f5487e2dbaa17d478e6eae040daa064
Author: Bram Moolenaar <Bram@vim.org>
Date: Tue Jan 18 10:37:29 2022 +0000
patch 8.2.4128: crash when method cannot be found
Problem: Crash when method cannot be found. (Christian J. Robinson)
Solution: Don't mix up pointer names.
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Tue, 18 Jan 2022 11:45:04 +0100 |
parents | c40554dfe03e |
children | dd7d94b911fa |
line wrap: on
line source
" 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') call writefile(a:test, 'Xtest.vim', 'a') call writefile(a:verify, 'Xverify.vim') call writefile(cleanup, 'Xverify.vim', 'a') call RunVim([], [], "-S Xtest.vim -S Xverify.vim") call assert_equal([], readfile('Xtest.out')) call delete('Xtest.out') call delete('Xtest.vim') call delete('Xverify.vim') 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 "------------------------------------------------------------------------------- " 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 function! NULL() Xpath 'a' return 0 endfunction function! ZERO() Xpath 'b' return 0 endfunction function! F0() Xpath 'c' endfunction function! F1(arg) Xpath 'e' endfunction 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') 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 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 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 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 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 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') call delete('Xtest') 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') 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') call delete('Xtest') 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') 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') call delete('Xtest') 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') 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') call delete('Xtest') 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_execption_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') 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 * call delete('Xscript') 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() 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') 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 * call delete('Xscript') endfunc " interrupt right before a catch is invoked inside a function. func Test_ignore_catch_after_intr_2() 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') 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 * call delete('Xscript') 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({})) if has('float') call assert_equal(5, type(0.0)) endif 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({})) if has('float') call assert_equal(v:t_float, type(0.0)) endif 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())", 'E685:') call assert_fails("call type(test_unknown())", '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 != 0) call assert_true(v:none == 0) call assert_false(v:none != 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: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 if has('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) endif " 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) if has('float') call assert_equal( 0x7FFFffffFFFFffff, float2nr( 1.0e150)) call assert_equal(-0x7FFFffffFFFFffff, float2nr(-1.0e150)) endif 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 "------------------------------------------------------------------------------- function! DefineFunction(name, body) let func = join(['function! ' . a:name . '()'] + a:body + ['endfunction'], "\n") exec func endfunction 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_emty_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:') if has('float') call assert_fails("call and(1.0, 1)", 'E805:') call assert_fails("call and(1, 1.0)", 'E805:') endif 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:') if has('float') call assert_fails("call or(1.0, 1)", 'E805:') call assert_fails("call or(1, 1.0)", 'E805:') endif 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)) if has('float') call assert_fails("call xor(1.0, 1)", 'E805:') call assert_fails("call xor(1, 1.0)", 'E805:') endif 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)) if has('float') call assert_fails("call invert(1.0)", 'E805:') endif 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') source Xexpand call assert_match('<SNR>\d\+_snr', g:result) source Xexpand call assert_match('<SNR>\d\+_snr', g:result) call delete('Xexpand') 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) if has('float') " 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:') endif " 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:') 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('.')})) if has('float') call assert_equal(-1, test_refcount(0.1)) endif 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)) if has('float') let x = 0.1 call assert_equal(-1, test_refcount(x)) endif " 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 endfunc " Test for missing :endif, :endfor, :endwhile and :endtry {{{1 func Test_missing_end() call writefile(['if 2 > 1', 'echo ">"'], 'Xscript') 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:') call delete('Xscript') " 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') 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) call delete('Xscript') endfunc " Test for errors in converting to float from various types {{{1 func Test_float_conversion_errors() if has('float') 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:') endif 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') source Xscript call delete('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_trinary_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 "------------------------------------------------------------------------------- " Modelines {{{1 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker "-------------------------------------------------------------------------------