changeset 23185:055fa9db6f39 v8.2.2138

patch 8.2.2138: Vim9: "exit_cb" causes Vim to exit Commit: https://github.com/vim/vim/commit/b5b9480ee936ef4cd0e350c468ef8c5f42fa398b Author: Bram Moolenaar <Bram@vim.org> 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.
author Bram Moolenaar <Bram@vim.org>
date Sun, 13 Dec 2020 18:00:04 +0100
parents 37634ffb6d1a
children 00583b0bedd5
files src/errors.h src/ex_cmds.h src/ex_docmd.c src/testdir/test_let.vim src/testdir/test_vim9_assign.vim src/testdir/test_vim9_script.vim src/userfunc.c src/version.c
diffstat 8 files changed, 59 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- 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"));
--- 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
--- 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)
--- 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
--- 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
 
--- 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 <F3>
 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
--- 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)
--- 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,