summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2023-05-02 16:25:47 +0100
committerBram Moolenaar <Bram@vim.org>2023-05-02 16:25:47 +0100
commita93d9cdc74f70ca2c85781496ffae4ca738fcd88 (patch)
tree25c7b84548c6fccafd119edb84807db998351f1f
parent17b695190d63b2de745499cb40a383c2672e275e (diff)
patch 9.0.1505: error when heredoc content looks like heredocv9.0.1505
Problem: Error when heredoc content looks like heredoc. Solution: Handle curly expressions. (closes #12325)
-rw-r--r--src/eval.c8
-rw-r--r--src/testdir/test_let.vim64
-rw-r--r--src/testdir/test_vim9_assign.vim71
-rw-r--r--src/userfunc.c31
-rw-r--r--src/version.c2
-rw-r--r--src/vim.h1
6 files changed, 145 insertions, 32 deletions
diff --git a/src/eval.c b/src/eval.c
index 0163ccc858..c31b0be072 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -6581,7 +6581,7 @@ find_name_end(
int br_nest = 0;
char_u *p;
int len;
- int vim9script = in_vim9script();
+ int allow_curly = (flags & FNE_ALLOW_CURLY) || !in_vim9script();
if (expr_start != NULL)
{
@@ -6591,12 +6591,12 @@ find_name_end(
// Quick check for valid starting character.
if ((flags & FNE_CHECK_START) && !eval_isnamec1(*arg)
- && (*arg != '{' || vim9script))
+ && (*arg != '{' || !allow_curly))
return arg;
for (p = arg; *p != NUL
&& (eval_isnamec(*p)
- || (*p == '{' && !vim9script)
+ || (*p == '{' && allow_curly)
|| ((flags & FNE_INCL_BR) && (*p == '['
|| (*p == '.' && eval_isdictc(p[1]))))
|| mb_nest != 0
@@ -6637,7 +6637,7 @@ find_name_end(
--br_nest;
}
- if (br_nest == 0 && !vim9script)
+ if (br_nest == 0 && allow_curly)
{
if (*p == '{')
{
diff --git a/src/testdir/test_let.vim b/src/testdir/test_let.vim
index 4e4a386df9..680572f76b 100644
--- a/src/testdir/test_let.vim
+++ b/src/testdir/test_let.vim
@@ -337,7 +337,43 @@ func Test_let_heredoc_fails()
call assert_report('No exception thrown')
catch /E488:/
catch
- call assert_report("Caught exception: " .. v:exception)
+ call assert_report('Caught exception: ' .. v:exception)
+ endtry
+
+ try
+ let &commentstring =<< trim TEXT
+ change
+ insert
+ append
+ TEXT
+ call assert_report('No exception thrown')
+ catch /E730:/
+ catch
+ call assert_report('Caught exception: ' .. v:exception)
+ endtry
+
+ try
+ let $SOME_ENV_VAR =<< trim TEXT
+ change
+ insert
+ append
+ TEXT
+ call assert_report('No exception thrown')
+ catch /E730:/
+ catch
+ call assert_report('Caught exception: ' .. v:exception)
+ endtry
+
+ try
+ let @r =<< trim TEXT
+ change
+ insert
+ append
+ TEXT
+ call assert_report('No exception thrown')
+ catch /E730:/
+ catch
+ call assert_report('Caught exception: ' .. v:exception)
endtry
let text =<< trim END
@@ -506,6 +542,32 @@ E
z
END
call assert_equal([' x', ' \y', ' z'], [a, b, c])
+
+ " unpack assignment without whitespace
+ let[a,b,c]=<<END
+change
+insert
+append
+END
+ call assert_equal(['change', 'insert', 'append'], [a, b, c])
+
+ " curly braces name and list slice assignment
+ let foo_3_bar = ['', '', '']
+ let foo_{1 + 2}_bar[ : ] =<< END
+change
+insert
+append
+END
+ call assert_equal(['change', 'insert', 'append'], foo_3_bar)
+
+ " dictionary key containing brackets and spaces
+ let d = {'abc] 123': 'baz'}
+ let d[d['abc] 123'] .. '{'] =<< END
+change
+insert
+append
+END
+ call assert_equal(['change', 'insert', 'append'], d['baz{'])
endfunc
" Test for evaluating Vim expressions in a heredoc using {expr}
diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim
index 4f4e58e069..6db2718daf 100644
--- a/src/testdir/test_vim9_assign.vim
+++ b/src/testdir/test_vim9_assign.vim
@@ -1848,30 +1848,81 @@ enddef
def Test_heredoc()
# simple heredoc
var lines =<< trim END
- var text =<< trim TEXT # comment
- abc
- TEXT
- assert_equal(['abc'], text)
+ var text =<< trim TEXT # comment
+ abc
+ TEXT
+ assert_equal(['abc'], text)
END
v9.CheckDefAndScriptSuccess(lines)
# empty heredoc
lines =<< trim END
- var text =<< trim TEXT
- TEXT
- assert_equal([], text)
+ var text =<< trim TEXT
+ TEXT
+ assert_equal([], text)
END
v9.CheckDefAndScriptSuccess(lines)
# heredoc with a single empty line
lines =<< trim END
- var text =<< trim TEXT
+ var text =<< trim TEXT
- TEXT
- assert_equal([''], text)
+ TEXT
+ assert_equal([''], text)
END
v9.CheckDefAndScriptSuccess(lines)
+ # assign heredoc to variable with type
+ lines =<< trim END
+ var text: list<string> =<< trim TEXT
+ var foo =<< trim FOO
+ TEXT
+ assert_equal(['var foo =<< trim FOO'], text)
+ END
+ v9.CheckDefAndScriptSuccess(lines)
+
+ # extra whitespace before type is allowed
+ lines =<< trim END
+ var text: list<string> =<< trim TEXT
+ var foo =<< trim FOO
+ TEXT
+ assert_equal(['var foo =<< trim FOO'], text)
+ END
+ v9.CheckDefAndScriptSuccess(lines)
+
+ # missing whitespace before type is an error
+ lines =<< trim END
+ var text:list<string> =<< trim TEXT
+ var foo =<< trim FOO
+ TEXT
+ assert_equal(['var foo =<< trim FOO'], text)
+ END
+ v9.CheckDefAndScriptFailure(lines, 'E1069:')
+
+ # assign heredoc to list slice
+ lines =<< trim END
+ var text = ['']
+ text[ : ] =<< trim TEXT
+ var foo =<< trim FOO
+ TEXT
+ assert_equal(['var foo =<< trim FOO'], text)
+ END
+ v9.CheckDefAndScriptSuccess(lines)
+
+ # assign heredoc to curly braces name in legacy function in Vim9 script
+ lines =<< trim END
+ vim9script
+ func Func()
+ let foo_3_bar = ['']
+ let foo_{1 + 2}_bar[ : ] =<< trim TEXT
+ var foo =<< trim FOO
+ TEXT
+ call assert_equal(['var foo =<< trim FOO'], foo_3_bar)
+ endfunc
+ Func()
+ END
+ v9.CheckScriptSuccess(lines)
+
v9.CheckDefFailure(['var lines =<< trim END X', 'END'], 'E488:')
v9.CheckDefFailure(['var lines =<< trim END " comment', 'END'], 'E488:')
diff --git a/src/userfunc.c b/src/userfunc.c
index fbde6edd0b..e63caf9fed 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -1143,7 +1143,7 @@ get_function_body(
skip_until = vim_strnsave(p, skiptowhite(p) - p);
getline_options = GETLINE_NONE;
is_heredoc = TRUE;
- if (eap->cmdidx == CMD_def && nesting == 0)
+ if (vim9_function && nesting == 0)
heredoc_concat_len = newlines->ga_len + 1;
}
@@ -1153,23 +1153,20 @@ get_function_body(
// 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 = p;
+ if (checkforcmd(&arg, "let", 2)
+ || checkforcmd(&arg, "var", 3)
+ || checkforcmd(&arg, "final", 5)
+ || checkforcmd(&arg, "const", 5)
+ || vim9_function)
{
- 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))))
+ while (vim_strchr((char_u *)"$@&", *arg) != NULL)
+ ++arg;
+ arg = skipwhite(find_name_end(arg, NULL, NULL,
+ FNE_INCL_BR | FNE_ALLOW_CURLY));
+ if (vim9_function && *arg == ':')
+ arg = skipwhite(skip_type(skipwhite(arg + 1), FALSE));
+ if (arg[0] == '=' && arg[1] == '<' && arg[2] =='<')
{
p = skipwhite(arg + 3);
while (TRUE)
diff --git a/src/version.c b/src/version.c
index 2a99c3b3ce..d01d7f26bd 100644
--- a/src/version.c
+++ b/src/version.c
@@ -696,6 +696,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1505,
+/**/
1504,
/**/
1503,
diff --git a/src/vim.h b/src/vim.h
index 656bcc9dab..4e0f2cd12e 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -2758,6 +2758,7 @@ typedef char *(*opt_did_set_cb_T)(optset_T *args);
// flags for find_name_end()
#define FNE_INCL_BR 1 // include [] in name
#define FNE_CHECK_START 2 // check name starts with valid character
+#define FNE_ALLOW_CURLY 4 // always allow curly braces name
// BSD is supposed to cover FreeBSD and similar systems.
#if (defined(SUN_SYSTEM) || defined(BSD) || defined(__FreeBSD_kernel__)) \