# HG changeset patch # User Bram Moolenaar # Date 1683041405 -7200 # Node ID edea3992cb01e0a17df310e0b99d62896143db7e # Parent 0e2ba4c8b1c14220b20d831b17c5c85c9cb4d90d patch 9.0.1505: error when heredoc content looks like heredoc Commit: https://github.com/vim/vim/commit/a93d9cdc74f70ca2c85781496ffae4ca738fcd88 Author: zeertzjq Date: Tue May 2 16:25:47 2023 +0100 patch 9.0.1505: error when heredoc content looks like heredoc Problem: Error when heredoc content looks like heredoc. Solution: Handle curly expressions. (closes https://github.com/vim/vim/issues/12325) diff --git a/src/eval.c b/src/eval.c --- 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 --- 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]=< =<< 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 =<< 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 =<< 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 --- 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 --- 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 --- a/src/vim.h +++ b/src/vim.h @@ -2758,6 +2758,7 @@ typedef char *(*opt_did_set_cb_T)(optset // 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__)) \