# HG changeset patch # User Bram Moolenaar # Date 1585854904 -7200 # Node ID 5feb426d2ea1a552894d20fc34a76416cfed3a67 # Parent e1168788aa04789e8fad8b8284b10be86b21f7bf patch 8.2.0502: Vim9: some code is not tested Commit: https://github.com/vim/vim/commit/e8c4abbbd711af8fd3ed85ea69e9ac3d63a0d879 Author: Bram Moolenaar Date: Thu Apr 2 21:13:25 2020 +0200 patch 8.2.0502: Vim9: some code is not tested Problem: Vim9: some code is not tested. Solution: Add more tests. Fix uncovered problems. diff --git a/src/cmdexpand.c b/src/cmdexpand.c --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -1389,7 +1389,7 @@ set_one_cmd_context( if (*arg != NUL) { xp->xp_context = EXPAND_NOTHING; - arg = skip_regexp(arg + 1, *arg, p_magic, NULL); + arg = skip_regexp(arg + 1, *arg, p_magic); } } return find_nextcmd(arg); @@ -1427,7 +1427,7 @@ set_one_cmd_context( { // skip "from" part ++arg; - arg = skip_regexp(arg, delim, p_magic, NULL); + arg = skip_regexp(arg, delim, p_magic); } // skip "to" part while (arg[0] != NUL && arg[0] != delim) diff --git a/src/ex_cmds.c b/src/ex_cmds.c --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -451,7 +451,7 @@ ex_sort(exarg_T *eap) } else if (!ASCII_ISALPHA(*p) && regmatch.regprog == NULL) { - s = skip_regexp(p + 1, *p, TRUE, NULL); + s = skip_regexp(p + 1, *p, TRUE); if (*s != *p) { emsg(_(e_invalpat)); @@ -3626,7 +3626,7 @@ do_sub(exarg_T *eap) which_pat = RE_LAST; // use last used regexp delimiter = *cmd++; // remember delimiter character pat = cmd; // remember start of search pat - cmd = skip_regexp(cmd, delimiter, p_magic, &eap->arg); + cmd = skip_regexp_ex(cmd, delimiter, p_magic, &eap->arg, NULL); if (cmd[0] == delimiter) // end delimiter found *cmd++ = NUL; // replace it with a NUL } @@ -4801,7 +4801,7 @@ ex_global(exarg_T *eap) if (delim) ++cmd; // skip delimiter if there is one pat = cmd; // remember start of pattern - cmd = skip_regexp(cmd, delim, p_magic, &eap->arg); + cmd = skip_regexp_ex(cmd, delim, p_magic, &eap->arg, NULL); if (cmd[0] == delim) // end delimiter found *cmd++ = NUL; // replace it with a NUL } @@ -6441,7 +6441,7 @@ skip_vimgrep_pat(char_u *p, char_u **s, if (s != NULL) *s = p + 1; c = *p; - p = skip_regexp(p + 1, c, TRUE, NULL); + p = skip_regexp(p + 1, c, TRUE); if (*p != c) return NULL; diff --git a/src/ex_docmd.c b/src/ex_docmd.c --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -3663,7 +3663,7 @@ get_address( } if (skip) // skip "/pat/" { - cmd = skip_regexp(cmd, c, (int)p_magic, NULL); + cmd = skip_regexp(cmd, c, (int)p_magic); if (*cmd == c) ++cmd; } @@ -6123,7 +6123,7 @@ ex_open(exarg_T *eap) { // ":open /pattern/": put cursor in column found with pattern ++eap->arg; - p = skip_regexp(eap->arg, '/', p_magic, NULL); + p = skip_regexp(eap->arg, '/', p_magic); *p = NUL; regmatch.regprog = vim_regcomp(eap->arg, p_magic ? RE_MAGIC : 0); if (regmatch.regprog != NULL) @@ -7857,7 +7857,7 @@ ex_findpat(exarg_T *eap) { whole = FALSE; ++eap->arg; - p = skip_regexp(eap->arg, '/', p_magic, NULL); + p = skip_regexp(eap->arg, '/', p_magic); if (*p) { *p++ = NUL; diff --git a/src/ex_eval.c b/src/ex_eval.c --- a/src/ex_eval.c +++ b/src/ex_eval.c @@ -1527,7 +1527,7 @@ ex_catch(exarg_T *eap) else { pat = eap->arg + 1; - end = skip_regexp(pat, *eap->arg, TRUE, NULL); + end = skip_regexp(pat, *eap->arg, TRUE); } if (!give_up) diff --git a/src/ex_getln.c b/src/ex_getln.c --- a/src/ex_getln.c +++ b/src/ex_getln.c @@ -277,7 +277,7 @@ do_incsearch_highlighting(int firstc, in p = skipwhite(p); delim = (delim_optional && vim_isIDc(*p)) ? ' ' : *p++; *search_delim = delim; - end = skip_regexp(p, delim, p_magic, NULL); + end = skip_regexp(p, delim, p_magic); use_last_pat = end == p && *end == delim; diff --git a/src/highlight.c b/src/highlight.c --- a/src/highlight.c +++ b/src/highlight.c @@ -4964,7 +4964,7 @@ ex_match(exarg_T *eap) semsg(_(e_invarg2), eap->arg); return; } - end = skip_regexp(p + 1, *p, TRUE, NULL); + end = skip_regexp(p + 1, *p, TRUE); if (!eap->skip) { if (*end != NUL && !ends_excmd(*skipwhite(end + 1))) diff --git a/src/proto/regexp.pro b/src/proto/regexp.pro --- a/src/proto/regexp.pro +++ b/src/proto/regexp.pro @@ -1,6 +1,7 @@ /* regexp.c */ int re_multiline(regprog_T *prog); -char_u *skip_regexp(char_u *startp, int dirc, int magic, char_u **newp); +char_u *skip_regexp(char_u *startp, int dirc, int magic); +char_u *skip_regexp_ex(char_u *startp, int dirc, int magic, char_u **newp, int *dropped); reg_extmatch_T *ref_extmatch(reg_extmatch_T *em); void unref_extmatch(reg_extmatch_T *em); char_u *regtilde(char_u *source, int magic); diff --git a/src/regexp.c b/src/regexp.c --- a/src/regexp.c +++ b/src/regexp.c @@ -537,16 +537,30 @@ skip_anyof(char_u *p) * Stop at end of "startp" or where "dirc" is found ('/', '?', etc). * Take care of characters with a backslash in front of it. * Skip strings inside [ and ]. - * When "newp" is not NULL and "dirc" is '?', make an allocated copy of the - * expression and change "\?" to "?". If "*newp" is not NULL the expression - * is changed in-place. */ char_u * skip_regexp( char_u *startp, int dirc, + int magic) +{ + return skip_regexp_ex(startp, dirc, magic, NULL, NULL); +} + +/* + * skip_regexp() with extra arguments: + * When "newp" is not NULL and "dirc" is '?', make an allocated copy of the + * expression and change "\?" to "?". If "*newp" is not NULL the expression + * is changed in-place. + * If a "\?" is changed to "?" then "dropped" is incremented, unless NULL. + */ + char_u * +skip_regexp_ex( + char_u *startp, + int dirc, int magic, - char_u **newp) + char_u **newp, + int *dropped) { int mymagic; char_u *p = startp; @@ -579,6 +593,8 @@ skip_regexp( if (*newp != NULL) p = *newp + (p - startp); } + if (dropped != NULL) + ++*dropped; if (*newp != NULL) STRMOVE(p, p + 1); else diff --git a/src/search.c b/src/search.c --- a/src/search.c +++ b/src/search.c @@ -1312,7 +1312,7 @@ do_search( * If there is a matching '/' or '?', toss it. */ ps = strcopy; - p = skip_regexp(pat, search_delim, (int)p_magic, &strcopy); + p = skip_regexp_ex(pat, search_delim, (int)p_magic, &strcopy, NULL); if (strcopy != ps) { // made a copy of "pat" to change "\?" to "?" diff --git a/src/syntax.c b/src/syntax.c --- a/src/syntax.c +++ b/src/syntax.c @@ -5598,7 +5598,7 @@ get_syn_pattern(char_u *arg, synpat_T *c if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL) return NULL; - end = skip_regexp(arg + 1, *arg, TRUE, NULL); + end = skip_regexp(arg + 1, *arg, TRUE); if (*end != *arg) // end delimiter not found { semsg(_("E401: Pattern delimiter not found: %s"), arg); @@ -5775,7 +5775,7 @@ syn_cmd_sync(exarg_T *eap, int syncing U finished = TRUE; break; } - arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL); + arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE); if (*arg_end != *next_arg) // end delimiter not found { illegal = TRUE; diff --git a/src/tag.c b/src/tag.c --- a/src/tag.c +++ b/src/tag.c @@ -3530,7 +3530,7 @@ jumpto_tag( */ str = pbuf; if (pbuf[0] == '/' || pbuf[0] == '?') - str = skip_regexp(pbuf + 1, pbuf[0], FALSE, NULL) + 1; + str = skip_regexp(pbuf + 1, pbuf[0], FALSE) + 1; if (str > pbuf_end - 1) // search command with nothing following { save_p_ws = p_ws; @@ -3820,7 +3820,7 @@ find_extra(char_u **pp) str = skipdigits(str); else if (*str == '/' || *str == '?') { - str = skip_regexp(str + 1, *str, FALSE, NULL); + str = skip_regexp(str + 1, *str, FALSE); if (*str != first_char) str = NULL; else diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -809,6 +809,8 @@ def Test_disassemble_compare_const() let cases = [ \ ['"xx" == "yy"', false], \ ['"aa" == "aa"', true], + \ ['has("eval") ? true : false', true], + \ ['has("asdf") ? true : false', false], \ ] let nr = 1 diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -468,12 +468,20 @@ def Test_try_catch_match() seq ..= 'b' catch /asdf/ seq ..= 'x' + catch ?a\?sdf? + seq ..= 'y' finally seq ..= 'c' endtry assert_equal('abc', seq) enddef +def Test_try_catch_fails() + call CheckDefFailure(['catch'], 'E603:') + call CheckDefFailure(['try', 'echo 0', 'catch','catch'], 'E1033:') + call CheckDefFailure(['try', 'echo 0', 'catch /pat'], 'E1067:') +enddef + let s:export_script_lines =<< trim END vim9script let name: string = 'bob' @@ -926,6 +934,13 @@ def Test_if_elseif_else() assert_equal('three', IfElse(3)) enddef +def Test_if_elseif_else_fails() + call CheckDefFailure(['elseif true'], 'E582:') + call CheckDefFailure(['else'], 'E581:') + call CheckDefFailure(['endif'], 'E580:') + call CheckDefFailure(['if true', 'elseif xxx'], 'E1001:') +enddef + let g:bool_true = v:true let g:bool_false = v:false @@ -973,6 +988,12 @@ def Test_if_const_expr() assert_equal(false, res) res = false + if has('xyz') ? true : false + res = true + endif + assert_equal(false, res) + + res = false if true && true res = true endif @@ -1030,6 +1051,8 @@ enddef def Test_if_const_expr_fails() call CheckDefFailure(['if "aaa" == "bbb'], 'E114:') call CheckDefFailure(["if 'aaa' == 'bbb"], 'E115:') + call CheckDefFailure(["if has('aaa'"], 'E110:') + call CheckDefFailure(["if has('aaa') ? true false"], 'E109:') enddef def Test_delfunc() @@ -1096,6 +1119,30 @@ def Test_for_outside_of_function() delete('Xvim9for.vim') enddef +def Test_for_loop() + let result = '' + for cnt in range(7) + if cnt == 4 + break + endif + if cnt == 2 + continue + endif + result ..= cnt .. '_' + endfor + assert_equal('0_1_3_', result) +enddef + +def Test_for_loop_fails() + call CheckDefFailure(['for # in range(5)'], 'E690:') + call CheckDefFailure(['for i In range(5)'], 'E690:') + call CheckDefFailure(['let x = 5', 'for x in range(5)'], 'E1023:') + call CheckScriptFailure(['def Func(arg)', 'for arg in range(5)', 'enddef'], 'E1006:') + call CheckDefFailure(['for i in "text"'], 'E1024:') + call CheckDefFailure(['for i in xxx'], 'E1001:') + call CheckDefFailure(['endfor'], 'E588:') +enddef + def Test_while_loop() let result = '' let cnt = 0 @@ -1112,12 +1159,13 @@ def Test_while_loop() assert_equal('1_3_', result) enddef -def Test_for_loop_fails() - call CheckDefFailure(['for # in range(5)'], 'E690:') - call CheckDefFailure(['for i In range(5)'], 'E690:') - call CheckDefFailure(['let x = 5', 'for x in range(5)'], 'E1023:') - call CheckScriptFailure(['def Func(arg)', 'for arg in range(5)', 'enddef'], 'E1006:') - call CheckDefFailure(['for i in "text"'], 'E1024:') +def Test_while_loop_fails() + call CheckDefFailure(['while xxx'], 'E1001:') + call CheckDefFailure(['endwhile'], 'E588:') + call CheckDefFailure(['continue'], 'E586:') + call CheckDefFailure(['if true', 'continue'], 'E586:') + call CheckDefFailure(['break'], 'E587:') + call CheckDefFailure(['if true', 'break'], 'E587:') enddef def Test_interrupt_loop() diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -2330,7 +2330,7 @@ ex_function(exarg_T *eap) */ if (*eap->arg == '/') { - p = skip_regexp(eap->arg + 1, '/', TRUE, NULL); + p = skip_regexp(eap->arg + 1, '/', TRUE); if (!eap->skip) { regmatch_T regmatch; diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -739,6 +739,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 502, +/**/ 501, /**/ 500, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -4095,7 +4095,7 @@ evaluate_const_expr7(char_u **arg, cctx_ *arg = skipwhite(*arg); if (**arg != ')') return FAIL; - *arg = skipwhite(*arg + 1); + *arg = *arg + 1; argvars[0] = *tv; argvars[1].v_type = VAR_UNKNOWN; @@ -4269,6 +4269,7 @@ evaluate_const_expr1(char_u **arg, cctx_ int val = tv2bool(tv); typval_T tv2; + // require space before and after the ? if (!VIM_ISWHITE(**arg) || !VIM_ISWHITE(p[1])) return FAIL; @@ -4553,6 +4554,7 @@ compile_for(char_u *arg, cctx_T *cctx) loop_idx = reserve_local(cctx, (char_u *)"", 0, FALSE, &t_number); if (loop_idx < 0) { + // only happens when out of memory drop_scope(cctx); return NULL; } @@ -4899,12 +4901,13 @@ compile_catch(char_u *arg, cctx_T *cctx char_u *end; char_u *pat; char_u *tofree = NULL; + int dropped = 0; int len; // Push v:exception, push {expr} and MATCH generate_instr_type(cctx, ISN_PUSHEXC, &t_string); - end = skip_regexp(p + 1, *p, TRUE, &tofree); + end = skip_regexp_ex(p + 1, *p, TRUE, &tofree, &dropped); if (*end != *p) { semsg(_("E1067: Separator mismatch: %s"), p); @@ -4914,10 +4917,10 @@ compile_catch(char_u *arg, cctx_T *cctx if (tofree == NULL) len = (int)(end - (p + 1)); else - len = (int)(end - (tofree + 1)); - pat = vim_strnsave(p + 1, len); + len = (int)(end - tofree); + pat = vim_strnsave(tofree == NULL ? p + 1 : tofree, len); vim_free(tofree); - p += len + 2; + p += len + 2 + dropped; if (pat == NULL) return FAIL; if (generate_PUSHS(cctx, pat) == FAIL)