From dd9de50f4262898384be6ea7694d05507c7cb260 Mon Sep 17 00:00:00 2001
From: Bram Moolenaar <Bram@vim.org>
Date: Sun, 15 Aug 2021 13:49:42 +0200
Subject: [PATCH] patch 8.2.3347: check for legacy script is incomplete

Problem:    Check for legacy script is incomplete. (Naohiro Ono)
Solution:   Also check the :legacy modifier.  Use for string concatenation
            with "." and others (issue #8756)
---
 src/errors.h                  |  2 ++
 src/eval.c                    |  7 ++---
 src/evalvars.c                |  4 +--
 src/ex_docmd.c                |  2 +-
 src/proto/vim9script.pro      |  1 +
 src/testdir/test_vim9_cmd.vim | 54 ++++++++++++++++++++++++++++++++++-
 src/typval.c                  |  2 +-
 src/version.c                 |  2 ++
 src/vim9script.c              | 12 ++++++++
 9 files changed, 77 insertions(+), 9 deletions(-)

diff --git a/src/errors.h b/src/errors.h
index 3b15d105fc..3468ab780c 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -650,3 +650,5 @@ EXTERN char e_argument_of_exists_compiled_must_be_literal_string[]
 	INIT(= N_("E1232: Argument of exists_compiled() must be a literal string"));
 EXTERN char e_exists_compiled_can_only_be_used_in_def_function[]
 	INIT(= N_("E1233: exists_compiled() can only be used in a :def function"));
+EXTERN char e_legacy_must_be_followed_by_command[]
+	INIT(= N_("E1234: legacy must be followed by a command"));
diff --git a/src/eval.c b/src/eval.c
index 020f55720c..12931aff51 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -2860,8 +2860,7 @@ eval5(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
 	// "++" and "--" on the next line are a separate command.
 	p = eval_next_non_blank(*arg, evalarg, &getnext);
 	op = *p;
-	concat = op == '.' && (*(p + 1) == '.'
-			      || (current_sctx.sc_version < 2 && !vim9script));
+	concat = op == '.' && (*(p + 1) == '.' || in_old_script(2));
 	if ((op != '+' && op != '-' && !concat) || p[1] == '='
 					       || (p[1] == '.' && p[2] == '='))
 	    break;
@@ -3402,7 +3401,7 @@ eval7(
 
     if (**arg == '.' && (!isdigit(*(*arg + 1))
 #ifdef FEAT_FLOAT
-	    || current_sctx.sc_version < 2
+	    || in_old_script(2)
 #endif
 	    ))
     {
@@ -5877,7 +5876,7 @@ handle_subscript(
 		|| (**arg == '.' && (rettv->v_type == VAR_DICT
 			|| (!evaluate
 			    && (*arg)[1] != '.'
-			    && current_sctx.sc_version >= 2))))
+			    && !in_old_script(2)))))
 	{
 	    dict_unref(selfdict);
 	    if (rettv->v_type == VAR_DICT)
diff --git a/src/evalvars.c b/src/evalvars.c
index 90dc7fbd1b..2de7999a0f 100644
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -774,7 +774,7 @@ ex_let(exarg_T *eap)
 	--argend;
     expr = skipwhite(argend);
     concat = expr[0] == '.'
-	&& ((expr[1] == '=' && current_sctx.sc_version < 2)
+	&& ((expr[1] == '=' && in_old_script(2))
 		|| (expr[1] == '.' && expr[2] == '='));
     has_assign =  *expr == '=' || (vim_strchr((char_u *)"+-*/%", *expr) != NULL
 							    && expr[1] == '=');
@@ -2932,7 +2932,7 @@ find_var_ht(char_u *name, char_u **varname)
 
 	// "version" is "v:version" in all scopes if scriptversion < 3.
 	// Same for a few other variables marked with VV_COMPAT.
-	if (current_sctx.sc_version < 3)
+	if (in_old_script(3))
 	{
 	    hi = hash_find(&compat_hashtab, name);
 	    if (!HASHITEM_EMPTY(hi))
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index 9640c500be..d29a932653 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -2951,7 +2951,7 @@ parse_command_modifiers(
 			    if (ends_excmd2(p, eap->cmd))
 			    {
 				*errormsg =
-				      _(e_vim9cmd_must_be_followed_by_command);
+				      _(e_legacy_must_be_followed_by_command);
 				return FAIL;
 			    }
 			    cmod->cmod_flags |= CMOD_LEGACY;
diff --git a/src/proto/vim9script.pro b/src/proto/vim9script.pro
index 1b73e87bba..97c1c92ad4 100644
--- a/src/proto/vim9script.pro
+++ b/src/proto/vim9script.pro
@@ -1,5 +1,6 @@
 /* vim9script.c */
 int in_vim9script(void);
+int in_old_script(int max_version);
 int current_script_is_vim9(void);
 void ex_vim9script(exarg_T *eap);
 int not_in_vim9(exarg_T *eap);
diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim
index 49bb8739f8..a9e95ff59a 100644
--- a/src/testdir/test_vim9_cmd.vim
+++ b/src/testdir/test_vim9_cmd.vim
@@ -13,9 +13,25 @@ def Test_vim9cmd()
     vim9cm assert_equal('yes', y)
   END
   CheckScriptSuccess(lines)
+
   assert_fails('vim9cmd', 'E1164:')
+  assert_fails('legacy', 'E1234:')
   assert_fails('vim9cmd echo "con" . "cat"', 'E15:')
 
+  lines =<< trim END
+      let str = 'con'
+      vim9cmd str .= 'cat'
+  END
+  CheckScriptFailure(lines, 'E492:')
+
+  lines =<< trim END
+      vim9script
+      legacy echo "con" . "cat"
+      legacy let str = 'con'
+      legacy let str .= 'cat'
+  END
+  CheckScriptSuccess(lines)
+
   lines =<< trim END
       vim9script
       def Foo()
@@ -24,11 +40,47 @@ def Test_vim9cmd()
       nmap ,; :vim9cmd <SID>Foo()<CR>
   END
   CheckScriptSuccess(lines)
+
   feedkeys(',;', 'xt')
   assert_equal("bar", g:found_bar)
-
   nunmap ,;
   unlet g:found_bar
+
+  lines =<< trim END
+      vim9script
+      legacy echo 1'000
+  END
+  CheckScriptFailure(lines, 'E115:')
+
+  if has('float')
+    lines =<< trim END
+        vim9script
+        echo .10
+    END
+    CheckScriptSuccess(lines)
+    lines =<< trim END
+        vim9cmd echo .10
+    END
+    CheckScriptSuccess(lines)
+    lines =<< trim END
+        vim9script
+        legacy echo .10
+    END
+    CheckScriptFailure(lines, 'E15:')
+  endif
+
+  echo v:version
+  assert_fails('vim9cmd echo version', 'E121:')
+  lines =<< trim END
+      vim9script
+      echo version
+  END
+  CheckScriptFailure(lines, 'E121:')
+  lines =<< trim END
+      vim9script
+      legacy echo version
+  END
+  CheckScriptSuccess(lines)
 enddef
 
 def Test_edit_wildcards()
diff --git a/src/typval.c b/src/typval.c
index cdb617554e..5fb98eb673 100644
--- a/src/typval.c
+++ b/src/typval.c
@@ -1704,7 +1704,7 @@ eval_number(
 	int	    want_string UNUSED)
 {
     int		len;
-    int		skip_quotes = current_sctx.sc_version >= 4 || in_vim9script();
+    int		skip_quotes = !in_old_script(4);
 #ifdef FEAT_FLOAT
     char_u	*p;
     int		get_float = FALSE;
diff --git a/src/version.c b/src/version.c
index a2a229bf50..34793f11ef 100644
--- a/src/version.c
+++ b/src/version.c
@@ -755,6 +755,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    3347,
 /**/
     3346,
 /**/
diff --git a/src/vim9script.c b/src/vim9script.c
index 66305a304e..19991097a0 100644
--- a/src/vim9script.c
+++ b/src/vim9script.c
@@ -33,6 +33,18 @@ in_vim9script(void)
 }
 
 #if defined(FEAT_EVAL) || defined(PROTO)
+/*
+ * Return TRUE when currently in a script with script version smaller than
+ * "max_version" or command modifiers forced it.
+ */
+    int
+in_old_script(int max_version)
+{
+    return (current_sctx.sc_version <= max_version
+					 && !(cmdmod.cmod_flags & CMOD_VIM9CMD))
+		|| (cmdmod.cmod_flags & CMOD_LEGACY);
+}
+
 /*
  * Return TRUE if the current script is Vim9 script.
  * This also returns TRUE in a legacy function in a Vim9 script.
-- 
GitLab