# HG changeset patch # User Bram Moolenaar # Date 1607878804 -3600 # Node ID 055fa9db6f39ccc9bd75804d6cff8f4e5279355a # Parent 37634ffb6d1a17648025699384c314135f8405ca patch 8.2.2138: Vim9: "exit_cb" causes Vim to exit Commit: https://github.com/vim/vim/commit/b5b9480ee936ef4cd0e350c468ef8c5f42fa398b Author: Bram Moolenaar Date: Sun Dec 13 17:50:20 2020 +0100 patch 8.2.2138: Vim9: "exit_cb" causes Vim to exit Problem: Vim9: "exit_cb" causes Vim to exit. Solution: Require white space after a command in Vim9 script. (closes https://github.com/vim/vim/issues/7467) Also fix that Vim9 style heredoc was not always recognized. diff --git a/src/errors.h b/src/errors.h --- a/src/errors.h +++ b/src/errors.h @@ -316,4 +316,8 @@ EXTERN char e_indexable_type_required[] EXTERN char e_non_empty_string_required[] INIT(= N_("E1142: Non-empty string required")); EXTERN char e_empty_expression_str[] - INIT(= N_("E1143: empty expression: \"%s\"")); + INIT(= N_("E1143: Empty expression: \"%s\"")); +EXTERN char e_command_not_followed_by_white_space_str[] + INIT(= N_("E1144: Command is not followed by white space: %s")); +EXTERN char e_missing_heredoc_end_marker_str[] + INIT(= N_("E1145: Missing heredoc end marker: %s")); diff --git a/src/ex_cmds.h b/src/ex_cmds.h --- a/src/ex_cmds.h +++ b/src/ex_cmds.h @@ -55,6 +55,7 @@ #define EX_LOCK_OK 0x1000000 // command can be executed when textlock is // set; when missing disallows editing another // buffer when curbuf_lock is set +#define EX_NONWHITE_OK 0x2000000 // command can be followed by non-white #define EX_FILES (EX_XFILE | EX_EXTRA) // multiple extra files allowed #define EX_FILE1 (EX_FILES | EX_NOSPC) // 1 file, defaults to current file @@ -632,7 +633,7 @@ EXCMD(CMD_function, "function", ex_funct EX_EXTRA|EX_BANG|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK, ADDR_NONE), EXCMD(CMD_global, "global", ex_global, - EX_RANGE|EX_WHOLEFOLD|EX_BANG|EX_EXTRA|EX_DFLALL|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK, + EX_RANGE|EX_WHOLEFOLD|EX_BANG|EX_EXTRA|EX_DFLALL|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK|EX_NONWHITE_OK, ADDR_LINES), EXCMD(CMD_goto, "goto", ex_goto, EX_RANGE|EX_COUNT|EX_TRLBAR|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK, @@ -1277,7 +1278,7 @@ EXCMD(CMD_rviminfo, "rviminfo", ex_vimin EX_BANG|EX_FILE1|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, ADDR_NONE), EXCMD(CMD_substitute, "substitute", ex_substitute, - EX_RANGE|EX_WHOLEFOLD|EX_EXTRA|EX_CMDWIN|EX_LOCK_OK, + EX_RANGE|EX_WHOLEFOLD|EX_EXTRA|EX_CMDWIN|EX_LOCK_OK|EX_NONWHITE_OK, ADDR_LINES), EXCMD(CMD_sNext, "sNext", ex_previous, EX_EXTRA|EX_RANGE|EX_COUNT|EX_BANG|EX_CMDARG|EX_ARGOPT|EX_TRLBAR, @@ -1652,7 +1653,7 @@ EXCMD(CMD_update, "update", ex_update, EX_RANGE|EX_WHOLEFOLD|EX_BANG|EX_FILE1|EX_ARGOPT|EX_DFLALL|EX_TRLBAR, ADDR_LINES), EXCMD(CMD_vglobal, "vglobal", ex_global, - EX_RANGE|EX_WHOLEFOLD|EX_EXTRA|EX_DFLALL|EX_CMDWIN|EX_LOCK_OK, + EX_RANGE|EX_WHOLEFOLD|EX_EXTRA|EX_DFLALL|EX_CMDWIN|EX_LOCK_OK|EX_NONWHITE_OK, ADDR_LINES), EXCMD(CMD_var, "var", ex_var, EX_EXTRA|EX_NOTRLCOM|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK, @@ -1792,16 +1793,16 @@ EXCMD(CMD_z, "z", ex_z, // commands that don't start with a letter EXCMD(CMD_bang, "!", ex_bang, - EX_RANGE|EX_WHOLEFOLD|EX_BANG|EX_FILES|EX_CMDWIN|EX_LOCK_OK, + EX_RANGE|EX_WHOLEFOLD|EX_BANG|EX_FILES|EX_CMDWIN|EX_LOCK_OK|EX_NONWHITE_OK, ADDR_LINES), EXCMD(CMD_pound, "#", ex_print, EX_RANGE|EX_WHOLEFOLD|EX_COUNT|EX_FLAGS|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, ADDR_LINES), EXCMD(CMD_and, "&", ex_substitute, - EX_RANGE|EX_WHOLEFOLD|EX_EXTRA|EX_CMDWIN|EX_LOCK_OK|EX_MODIFY, + EX_RANGE|EX_WHOLEFOLD|EX_EXTRA|EX_CMDWIN|EX_LOCK_OK|EX_MODIFY|EX_NONWHITE_OK, ADDR_LINES), EXCMD(CMD_star, "*", ex_at, - EX_RANGE|EX_WHOLEFOLD|EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, + EX_RANGE|EX_WHOLEFOLD|EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK|EX_NONWHITE_OK, ADDR_LINES), EXCMD(CMD_lshift, "<", ex_operators, EX_RANGE|EX_WHOLEFOLD|EX_COUNT|EX_FLAGS|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK|EX_MODIFY, @@ -1813,7 +1814,7 @@ EXCMD(CMD_rshift, ">", ex_operators, EX_RANGE|EX_WHOLEFOLD|EX_COUNT|EX_FLAGS|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK|EX_MODIFY, ADDR_LINES), EXCMD(CMD_at, "@", ex_at, - EX_RANGE|EX_WHOLEFOLD|EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, + EX_RANGE|EX_WHOLEFOLD|EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK|EX_NONWHITE_OK, ADDR_LINES), EXCMD(CMD_block, "{{{{{{{{", ex_block, // not found normally 0, @@ -1822,7 +1823,7 @@ EXCMD(CMD_endblock, "}", ex_endblock, EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, ADDR_NONE), EXCMD(CMD_tilde, "~", ex_substitute, - EX_RANGE|EX_WHOLEFOLD|EX_EXTRA|EX_CMDWIN|EX_LOCK_OK|EX_MODIFY, + EX_RANGE|EX_WHOLEFOLD|EX_EXTRA|EX_CMDWIN|EX_LOCK_OK|EX_MODIFY|EX_NONWHITE_OK, ADDR_LINES), // commands that start with an uppercase letter diff --git a/src/ex_docmd.c b/src/ex_docmd.c --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -3528,6 +3528,14 @@ find_ex_command( if (eap->cmdidx == CMD_final && p - eap->cmd == 4) eap->cmdidx = CMD_finally; + if (eap->cmdidx != CMD_SIZE && in_vim9script() + && !IS_WHITE_OR_NUL(*p) && !ends_excmd(*p) && *p != '!' + && (cmdnames[eap->cmdidx].cmd_argt & EX_NONWHITE_OK) == 0) + { + semsg(_(e_command_not_followed_by_white_space_str), eap->cmd); + eap->cmdidx = CMD_SIZE; + } + return p; } @@ -5114,7 +5122,7 @@ ex_blast(exarg_T *eap) /* * Check if "c" ends an Ex command. - * In Vim9 script does not check for white space before # or #{. + * In Vim9 script does not check for white space before #. */ int ends_excmd(int c) diff --git a/src/testdir/test_let.vim b/src/testdir/test_let.vim --- a/src/testdir/test_let.vim +++ b/src/testdir/test_let.vim @@ -338,7 +338,7 @@ func Test_let_heredoc_fails() endfunc END call writefile(text, 'XheredocFail') - call assert_fails('source XheredocFail', 'E126:') + call assert_fails('source XheredocFail', 'E1145:') call delete('XheredocFail') let text =<< trim CodeEnd @@ -347,7 +347,7 @@ func Test_let_heredoc_fails() endfunc CodeEnd call writefile(text, 'XheredocWrong') - call assert_fails('source XheredocWrong', 'E126:') + call assert_fails('source XheredocWrong', 'E1145:') call delete('XheredocWrong') let text =<< trim TEXTend diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim --- a/src/testdir/test_vim9_assign.vim +++ b/src/testdir/test_vim9_assign.vim @@ -982,6 +982,17 @@ def Test_heredoc() var&lines =<< trim END x x + enddef + defcompile + [END] + CheckScriptFailure(lines, 'E1145: Missing heredoc end marker: END') + delfunc! g:Func + + lines =<< trim [END] + def Func() + var lines =<< trim END + x + x x x x @@ -991,7 +1002,7 @@ def Test_heredoc() enddef call Func() [END] - CheckScriptFailure(lines, 'E990:') + CheckScriptFailure(lines, 'E1145: Missing heredoc end marker: END') delfunc! g:Func enddef 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 @@ -3058,7 +3058,7 @@ def Test_put_with_linebreak() new var lines =<< trim END vim9script - pu=split('abc', '\zs') + pu =split('abc', '\zs') ->join() END CheckScriptSuccess(lines) @@ -3079,6 +3079,13 @@ def Test_invoke_normal_in_visual_mode() xunmap enddef +def Test_white_space_after_command() + var lines =<< trim END + exit_cb: Func}) + END + CheckDefAndScriptFailure(lines, 'E1144:', 1) +enddef + " Keep this last, it messes up highlighting. def Test_substitute_cmd() new diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -3185,7 +3185,9 @@ define_function(exarg_T *eap, char_u *na lines_left = Rows - 1; if (theline == NULL) { - if (eap->cmdidx == CMD_def) + if (skip_until != NULL) + semsg(_(e_missing_heredoc_end_marker_str), skip_until); + else if (eap->cmdidx == CMD_def) emsg(_(e_missing_enddef)); else emsg(_("E126: Missing :endfunction")); @@ -3352,18 +3354,24 @@ define_function(exarg_T *eap, char_u *na // Check for ":cmd v =<< [trim] EOF" // and ":cmd [a, b] =<< [trim] EOF" + // and "lines =<< [trim] EOF" for Vim9 // Where "cmd" can be "let", "var", "final" or "const". arg = skipwhite(skiptowhite(p)); if (*arg == '[') arg = vim_strchr(arg, ']'); if (arg != NULL) { - arg = skipwhite(skiptowhite(arg)); - if (arg[0] == '=' && arg[1] == '<' && arg[2] =='<' + int found = (eap->cmdidx == CMD_def && arg[0] == '=' + && arg[1] == '<' && arg[2] =='<'); + + if (!found) + // skip over the argument after "cmd" + arg = skipwhite(skiptowhite(arg)); + if (found || (arg[0] == '=' && arg[1] == '<' && arg[2] =='<' && (checkforcmd(&p, "let", 2) || checkforcmd(&p, "var", 3) || checkforcmd(&p, "final", 5) - || checkforcmd(&p, "const", 5))) + || checkforcmd(&p, "const", 5)))) { p = skipwhite(arg + 3); if (STRNCMP(p, "trim", 4) == 0) diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2138, +/**/ 2137, /**/ 2136,