commit 3a70fc8cb8fedf78b5e0d7c606d99967faad70ab
parent 78bbe53f7615e8b38d5289d9ce0579996109579b
Author: zeertzjq <zeertzjq@outlook.com>
Date: Thu, 11 Dec 2025 12:46:45 +0800
Merge pull request #36906 from janlazo/vim-8.2.1618
vim-patch:8.2.{1618,2403,2404,2409,2411,2571,2834,4474}
Diffstat:
3 files changed, 293 insertions(+), 74 deletions(-)
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
@@ -735,7 +735,7 @@ static int qf_get_next_str_line(qfstate_T *state)
return QF_OK;
}
-/// Get the next string from state->p_Li.
+/// Get the next string from the List item state->p_li.
static int qf_get_next_list_line(qfstate_T *state)
{
listitem_T *p_li = state->p_li;
@@ -1249,7 +1249,8 @@ static void qf_store_title(qf_list_T *qfl, const char *title)
/// that created the quickfix list with the ":" prefix.
/// Create a quickfix list title string by prepending ":" to a user command.
/// Returns a pointer to a static buffer with the title.
-static char *qf_cmdtitle(char *cmd)
+static char *qf_cmdtitle(const char *cmd)
+ FUNC_ATTR_NONNULL_ALL
{
static char qftitle_str[IOSIZE];
@@ -6623,7 +6624,7 @@ static int qf_add_entry_from_dict(qf_list_T *qfl, dict_T *d, bool first_entry, b
// If the 'valid' field is present it overrules the detected value.
if (tv_dict_find(d, "valid", -1) != NULL) {
- valid = tv_dict_get_number(d, "valid");
+ valid = tv_dict_get_bool(d, "valid", false);
}
const int status = qf_add_entry(qfl,
@@ -7344,30 +7345,27 @@ static char *cexpr_get_auname(cmdidx_T cmdidx)
}
}
-/// ":cexpr {expr}", ":cgetexpr {expr}", ":caddexpr {expr}" command.
-/// ":lexpr {expr}", ":lgetexpr {expr}", ":laddexpr {expr}" command.
-void ex_cexpr(exarg_T *eap)
+static int trigger_cexpr_autocmd(int cmdidx)
{
- char *au_name = cexpr_get_auname(eap->cmdidx);
+ char *au_name = cexpr_get_auname(cmdidx);
if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name,
curbuf->b_fname, true, curbuf)) {
if (aborting()) {
- return;
+ return FAIL;
}
}
+ return OK;
+}
+int cexpr_core(const exarg_T *eap, typval_T *tv)
+ FUNC_ATTR_NONNULL_ALL
+{
win_T *wp = NULL;
qf_info_T *qi = qf_cmd_get_or_alloc_stack(eap, &wp);
- // Evaluate the expression. When the result is a string or a list we can
- // use it to fill the errorlist.
- typval_T *tv = eval_expr(eap->arg, eap);
- if (tv == NULL) {
- return;
- }
-
if ((tv->v_type == VAR_STRING && tv->vval.v_string != NULL)
|| tv->v_type == VAR_LIST) {
+ char *au_name = cexpr_get_auname(eap->cmdidx);
incr_quickfix_busy();
int res = qf_init_ext(qi, qi->qf_curlist, NULL, NULL, tv, p_efm,
(eap->cmdidx != CMD_caddexpr
@@ -7376,7 +7374,7 @@ void ex_cexpr(exarg_T *eap)
qf_cmdtitle(*eap->cmdlinep), NULL);
if (qf_stack_empty(qi)) {
decr_quickfix_busy();
- goto cleanup;
+ return FAIL;
}
if (res >= 0) {
qf_list_changed(qf_get_curlist(qi));
@@ -7396,10 +7394,30 @@ void ex_cexpr(exarg_T *eap)
qf_jump_first(qi, save_qfid, eap->forceit);
}
decr_quickfix_busy();
+ return OK;
} else {
emsg(_("E777: String or List expected"));
}
-cleanup:
+ return FAIL;
+}
+
+/// ":cexpr {expr}", ":cgetexpr {expr}", ":caddexpr {expr}" command.
+/// ":lexpr {expr}", ":lgetexpr {expr}", ":laddexpr {expr}" command.
+/// Also: ":caddexpr", ":cgetexpr", "laddexpr" and "laddexpr".
+void ex_cexpr(exarg_T *eap)
+{
+ if (trigger_cexpr_autocmd(eap->cmdidx) == FAIL) {
+ return;
+ }
+
+ // Evaluate the expression. When the result is a string or a list we can
+ // use it to fill the errorlist.
+ typval_T *tv = eval_expr(eap->arg, eap);
+ if (tv == NULL) {
+ return;
+ }
+
+ (void)cexpr_core(eap, tv);
tv_free(tv);
}
diff --git a/test/old/testdir/test_profile.vim b/test/old/testdir/test_profile.vim
@@ -5,24 +5,30 @@ CheckFeature profile
source shared.vim
source screendump.vim
+source vim9.vim
func Test_profile_func()
+ call RunProfileFunc('func', 'let', 'let')
+ " call RunProfileFunc('def', 'var', '')
+endfunc
+
+func RunProfileFunc(command, declare, assign)
let lines =<< trim [CODE]
profile start Xprofile_func.log
profile func Foo*
- func! Foo1()
- endfunc
- func! Foo2()
- let l:count = 100
- while l:count > 0
- let l:count = l:count - 1
+ XXX Foo1()
+ endXXX
+ XXX Foo2()
+ DDD counter = 100
+ while counter > 0
+ AAA counter = counter - 1
endwhile
sleep 1m
- endfunc
- func! Foo3()
- endfunc
- func! Bar()
- endfunc
+ endXXX
+ XXX Foo3()
+ endXXX
+ XXX Bar()
+ endXXX
call Foo1()
call Foo1()
profile pause
@@ -37,6 +43,10 @@ func Test_profile_func()
delfunc Foo3
[CODE]
+ call map(lines, {k, v -> substitute(v, 'XXX', a:command, '') })
+ call map(lines, {k, v -> substitute(v, 'DDD', a:declare, '') })
+ call map(lines, {k, v -> substitute(v, 'AAA', a:assign, '') })
+
call writefile(lines, 'Xprofile_func.vim')
call system(GetVimCommand()
\ . ' -es --clean'
@@ -70,10 +80,10 @@ func Test_profile_func()
call assert_match('^ Self time:\s\+\d\+\.\d\+$', lines[12])
call assert_equal('', lines[13])
call assert_equal('count total (s) self (s)', lines[14])
- call assert_match('^\s*1\s\+.*\slet l:count = 100$', lines[15])
- call assert_match('^\s*101\s\+.*\swhile l:count > 0$', lines[16])
- call assert_match('^\s*100\s\+.*\s let l:count = l:count - 1$', lines[17])
- call assert_match('^\s*101\s\+.*\sendwhile$', lines[18])
+ call assert_match('^\s*1\s\+.*\s\(let\|var\) counter = 100$', lines[15])
+ call assert_match('^\s*101\s\+.*\swhile counter > 0$', lines[16])
+ call assert_match('^\s*100\s\+.*\s \(let\)\= counter = counter - 1$', lines[17])
+ call assert_match('^\s*10[01]\s\+.*\sendwhile$', lines[18])
call assert_match('^\s*1\s\+.\+sleep 1m$', lines[19])
call assert_equal('', lines[20])
call assert_equal('FUNCTIONS SORTED ON TOTAL TIME', lines[21])
@@ -92,39 +102,47 @@ func Test_profile_func()
endfunc
func Test_profile_func_with_ifelse()
+ call Run_profile_func_with_ifelse('func', 'let')
+ " call Run_profile_func_with_ifelse('def', 'var')
+endfunc
+
+func Run_profile_func_with_ifelse(command, declare)
let lines =<< trim [CODE]
- func! Foo1()
+ XXX Foo1()
if 1
- let x = 0
+ DDD x = 0
elseif 1
- let x = 1
+ DDD x = 1
else
- let x = 2
+ DDD x = 2
endif
- endfunc
- func! Foo2()
+ endXXX
+ XXX Foo2()
if 0
- let x = 0
+ DDD x = 0
elseif 1
- let x = 1
+ DDD x = 1
else
- let x = 2
+ DDD x = 2
endif
- endfunc
- func! Foo3()
+ endXXX
+ XXX Foo3()
if 0
- let x = 0
+ DDD x = 0
elseif 0
- let x = 1
+ DDD x = 1
else
- let x = 2
+ DDD x = 2
endif
- endfunc
+ endXXX
call Foo1()
call Foo2()
call Foo3()
[CODE]
+ call map(lines, {k, v -> substitute(v, 'XXX', a:command, '') })
+ call map(lines, {k, v -> substitute(v, 'DDD', a:declare, '') })
+
call writefile(lines, 'Xprofile_func.vim')
call system(GetVimCommand()
\ . ' -es -i NONE --noplugin'
@@ -149,11 +167,11 @@ func Test_profile_func_with_ifelse()
call assert_equal('', lines[5])
call assert_equal('count total (s) self (s)', lines[6])
call assert_match('^\s*1\s\+.*\sif 1$', lines[7])
- call assert_match('^\s*1\s\+.*\s let x = 0$', lines[8])
+ call assert_match('^\s*1\s\+.*\s \(let\|var\) x = 0$', lines[8])
call assert_match( '^\s\+elseif 1$', lines[9])
- call assert_match( '^\s\+let x = 1$', lines[10])
+ call assert_match( '^\s\+\(let\|var\) x = 1$', lines[10])
call assert_match( '^\s\+else$', lines[11])
- call assert_match( '^\s\+let x = 2$', lines[12])
+ call assert_match( '^\s\+\(let\|var\) x = 2$', lines[12])
call assert_match('^\s*1\s\+.*\sendif$', lines[13])
call assert_equal('', lines[14])
call assert_equal('FUNCTION Foo2()', lines[15])
@@ -163,11 +181,11 @@ func Test_profile_func_with_ifelse()
call assert_equal('', lines[20])
call assert_equal('count total (s) self (s)', lines[21])
call assert_match('^\s*1\s\+.*\sif 0$', lines[22])
- call assert_match( '^\s\+let x = 0$', lines[23])
+ call assert_match( '^\s\+\(let\|var\) x = 0$', lines[23])
call assert_match('^\s*1\s\+.*\selseif 1$', lines[24])
- call assert_match('^\s*1\s\+.*\s let x = 1$', lines[25])
+ call assert_match('^\s*1\s\+.*\s \(let\|var\) x = 1$', lines[25])
call assert_match( '^\s\+else$', lines[26])
- call assert_match( '^\s\+let x = 2$', lines[27])
+ call assert_match( '^\s\+\(let\|var\) x = 2$', lines[27])
call assert_match('^\s*1\s\+.*\sendif$', lines[28])
call assert_equal('', lines[29])
call assert_equal('FUNCTION Foo3()', lines[30])
@@ -177,11 +195,11 @@ func Test_profile_func_with_ifelse()
call assert_equal('', lines[35])
call assert_equal('count total (s) self (s)', lines[36])
call assert_match('^\s*1\s\+.*\sif 0$', lines[37])
- call assert_match( '^\s\+let x = 0$', lines[38])
+ call assert_match( '^\s\+\(let\|var\) x = 0$', lines[38])
call assert_match('^\s*1\s\+.*\selseif 0$', lines[39])
- call assert_match( '^\s\+let x = 1$', lines[40])
+ call assert_match( '^\s\+\(let\|var\) x = 1$', lines[40])
call assert_match('^\s*1\s\+.*\selse$', lines[41])
- call assert_match('^\s*1\s\+.*\s let x = 2$', lines[42])
+ call assert_match('^\s*1\s\+.*\s \(let\|var\) x = 2$', lines[42])
call assert_match('^\s*1\s\+.*\sendif$', lines[43])
call assert_equal('', lines[44])
call assert_equal('FUNCTIONS SORTED ON TOTAL TIME', lines[45])
@@ -202,42 +220,56 @@ func Test_profile_func_with_ifelse()
endfunc
func Test_profile_func_with_trycatch()
+ call Run_profile_func_with_trycatch('func', 'let')
+ " call Run_profile_func_with_trycatch('def', 'var')
+endfunc
+
+func Run_profile_func_with_trycatch(command, declare)
let lines =<< trim [CODE]
- func! Foo1()
+ XXX Foo1()
try
- let x = 0
+ DDD x = 0
catch
- let x = 1
+ DDD x = 1
finally
- let x = 2
+ DDD x = 2
endtry
- endfunc
- func! Foo2()
+ endXXX
+ XXX Foo2()
try
throw 0
catch
- let x = 1
+ DDD x = 1
finally
- let x = 2
+ DDD x = 2
endtry
- endfunc
- func! Foo3()
+ endXXX
+ XXX Foo3()
try
throw 0
catch
throw 1
finally
- let x = 2
+ DDD x = 2
endtry
- endfunc
+ endXXX
call Foo1()
call Foo2()
+ let rethrown = 0
try
call Foo3()
catch
+ let rethrown = 1
endtry
+ if rethrown != 1
+ " call Foo1 again so that the test fails
+ call Foo1()
+ endif
[CODE]
+ call map(lines, {k, v -> substitute(v, 'XXX', a:command, '') })
+ call map(lines, {k, v -> substitute(v, 'DDD', a:declare, '') })
+
call writefile(lines, 'Xprofile_func.vim')
call system(GetVimCommand()
\ . ' -es -i NONE --noplugin'
@@ -262,11 +294,11 @@ func Test_profile_func_with_trycatch()
call assert_equal('', lines[5])
call assert_equal('count total (s) self (s)', lines[6])
call assert_match('^\s*1\s\+.*\stry$', lines[7])
- call assert_match('^\s*1\s\+.*\s let x = 0$', lines[8])
+ call assert_match('^\s*1\s\+.*\s \(let\|var\) x = 0$', lines[8])
call assert_match( '^\s\+catch$', lines[9])
- call assert_match( '^\s\+let x = 1$', lines[10])
+ call assert_match( '^\s\+\(let\|var\) x = 1$', lines[10])
call assert_match('^\s*1\s\+.*\sfinally$', lines[11])
- call assert_match('^\s*1\s\+.*\s let x = 2$', lines[12])
+ call assert_match('^\s*1\s\+.*\s \(let\|var\) x = 2$', lines[12])
call assert_match('^\s*1\s\+.*\sendtry$', lines[13])
call assert_equal('', lines[14])
call assert_equal('FUNCTION Foo2()', lines[15])
@@ -278,9 +310,9 @@ func Test_profile_func_with_trycatch()
call assert_match('^\s*1\s\+.*\stry$', lines[22])
call assert_match('^\s*1\s\+.*\s throw 0$', lines[23])
call assert_match('^\s*1\s\+.*\scatch$', lines[24])
- call assert_match('^\s*1\s\+.*\s let x = 1$', lines[25])
+ call assert_match('^\s*1\s\+.*\s \(let\|var\) x = 1$', lines[25])
call assert_match('^\s*1\s\+.*\sfinally$', lines[26])
- call assert_match('^\s*1\s\+.*\s let x = 2$', lines[27])
+ call assert_match('^\s*1\s\+.*\s \(let\|var\) x = 2$', lines[27])
call assert_match('^\s*1\s\+.*\sendtry$', lines[28])
call assert_equal('', lines[29])
call assert_equal('FUNCTION Foo3()', lines[30])
@@ -294,7 +326,7 @@ func Test_profile_func_with_trycatch()
call assert_match('^\s*1\s\+.*\scatch$', lines[39])
call assert_match('^\s*1\s\+.*\s throw 1$', lines[40])
call assert_match('^\s*1\s\+.*\sfinally$', lines[41])
- call assert_match('^\s*1\s\+.*\s let x = 2$', lines[42])
+ call assert_match('^\s*1\s\+.*\s \(let\|var\) x = 2$', lines[42])
call assert_match( '^\s\+endtry$', lines[43])
call assert_equal('', lines[44])
call assert_equal('FUNCTIONS SORTED ON TOTAL TIME', lines[45])
@@ -595,3 +627,28 @@ func Test_profile_typed_func()
call delete('XprofileTypedFunc')
call delete('XtestProfile')
endfunc
+
+func Test_vim9_profiling()
+ throw 'Skipped: Vim9 script is N/A'
+ " only tests that compiling and calling functions doesn't crash
+ let lines =<< trim END
+ vim9script
+ def Func()
+ Crash()
+ enddef
+ def Crash()
+ enddef
+ prof start Xprofile_crash.log
+ prof func Func
+ Func()
+ END
+ call writefile(lines, 'Xprofile_crash.vim')
+ call system(GetVimCommandClean() . ' -es -c "so Xprofile_crash.vim" -c q')
+ call assert_equal(0, v:shell_error)
+ call assert_true(readfile('Xprofile_crash.log')->len() > 10)
+ call delete('Xprofile_crash.vim')
+ call delete('Xprofile_crash.log')
+endfunc
+
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_quickfix.vim b/test/old/testdir/test_quickfix.vim
@@ -670,6 +670,150 @@ func Test_browse()
call Xtest_browse('l')
endfunc
+" Test for memory allocation failures
+func Xnomem_tests(cchar)
+ call s:setup_commands(a:cchar)
+
+ call test_alloc_fail(GetAllocId('qf_dirname_start'), 0, 0)
+ call assert_fails('Xvimgrep vim runtest.vim', 'E342:')
+
+ call test_alloc_fail(GetAllocId('qf_dirname_now'), 0, 0)
+ call assert_fails('Xvimgrep vim runtest.vim', 'E342:')
+
+ call test_alloc_fail(GetAllocId('qf_namebuf'), 0, 0)
+ call assert_fails('Xfile runtest.vim', 'E342:')
+
+ call test_alloc_fail(GetAllocId('qf_errmsg'), 0, 0)
+ call assert_fails('Xfile runtest.vim', 'E342:')
+
+ call test_alloc_fail(GetAllocId('qf_pattern'), 0, 0)
+ call assert_fails('Xfile runtest.vim', 'E342:')
+
+ call test_alloc_fail(GetAllocId('qf_efm_fmtstr'), 0, 0)
+ set efm=%f
+ call assert_fails('Xexpr ["Xfile1"]', 'E342:')
+ set efm&
+
+ call test_alloc_fail(GetAllocId('qf_efm_fmtpart'), 0, 0)
+ set efm=%f:%l:%m,%f-%l-%m
+ call assert_fails('Xaddexpr ["Xfile2", "Xfile3"]', 'E342:')
+ set efm&
+
+ call test_alloc_fail(GetAllocId('qf_title'), 0, 0)
+ call assert_fails('Xexpr ""', 'E342:')
+ call assert_equal('', g:Xgetlist({'all': 1}).title)
+
+ call test_alloc_fail(GetAllocId('qf_mef_name'), 0, 0)
+ set makeef=Xtmp##.err
+ call assert_fails('Xgrep needle haystack', 'E342:')
+ set makeef&
+
+ call test_alloc_fail(GetAllocId('qf_qfline'), 0, 0)
+ call assert_fails('Xexpr "Xfile1:10:Line10"', 'E342:')
+
+ if a:cchar == 'l'
+ for id in ['qf_qfline', 'qf_qfinfo']
+ lgetexpr ["Xfile1:10:L10", "Xfile2:20:L20"]
+ call test_alloc_fail(GetAllocId(id), 0, 0)
+ call assert_fails('new', 'E342:')
+ call assert_equal(2, winnr('$'))
+ call assert_equal([], getloclist(0))
+ %bw!
+ endfor
+ endif
+
+ call test_alloc_fail(GetAllocId('qf_qfline'), 0, 0)
+ try
+ call assert_fails('Xvimgrep vim runtest.vim', 'E342:')
+ catch /^Vim:Interrupt$/
+ endtry
+
+ call test_alloc_fail(GetAllocId('qf_qfline'), 0, 0)
+ try
+ call assert_fails('Xvimgrep /vim/f runtest.vim', 'E342:')
+ catch /^Vim:Interrupt$/
+ endtry
+
+ let l = getqflist({"lines": ["Xfile1:10:L10"]})
+ call test_alloc_fail(GetAllocId('qf_qfline'), 0, 0)
+ call assert_fails('call g:Xsetlist(l.items)', 'E342:')
+
+ call test_alloc_fail(GetAllocId('qf_qfline'), 0, 0)
+ try
+ call assert_fails('Xhelpgrep quickfix', 'E342:')
+ catch /^Vim:Interrupt$/
+ endtry
+
+ call test_alloc_fail(GetAllocId('qf_qfinfo'), 0, 0)
+ call assert_fails('let l = g:Xgetlist({"lines": ["Xfile1:10:L10"]})', 'E342:')
+ call assert_equal(#{items: []}, l)
+
+ if a:cchar == 'l'
+ call setqflist([], 'f')
+ call setloclist(0, [], 'f')
+ call test_alloc_fail(GetAllocId('qf_qfinfo'), 0, 0)
+ call assert_fails('lhelpgrep quickfix', 'E342:')
+ call assert_equal([], getloclist(0))
+
+ call test_alloc_fail(GetAllocId('qf_qfinfo'), 0, 0)
+ call assert_fails('lvimgrep vim runtest.vim', 'E342:')
+
+ let l = getqflist({"lines": ["Xfile1:10:L10"]})
+ call test_alloc_fail(GetAllocId('qf_qfinfo'), 0, 0)
+ call assert_fails('call setloclist(0, l.items)', 'E342:')
+
+ call test_alloc_fail(GetAllocId('qf_qfinfo'), 0, 0)
+ call assert_fails('lbuffer', 'E342:')
+
+ call test_alloc_fail(GetAllocId('qf_qfinfo'), 0, 0)
+ call assert_fails('lexpr ["Xfile1:10:L10", "Xfile2:20:L20"]', 'E342:')
+
+ call test_alloc_fail(GetAllocId('qf_qfinfo'), 0, 0)
+ call assert_fails('lfile runtest.vim', 'E342:')
+ endif
+
+ call test_alloc_fail(GetAllocId('qf_dirstack'), 0, 0)
+ set efm=%DEntering\ dir\ %f,%f:%l:%m
+ call assert_fails('Xexpr ["Entering dir abc", "abc.txt:1:Hello world"]', 'E342:')
+ set efm&
+
+ call test_alloc_fail(GetAllocId('qf_dirstack'), 0, 0)
+ set efm=%+P[%f],(%l)%m
+ call assert_fails('Xexpr ["[runtest.vim]", "(1)Hello"]', 'E342:')
+ set efm&
+
+ call test_alloc_fail(GetAllocId('qf_multiline_pfx'), 0, 0)
+ set efm=%EError,%Cline\ %l,%Z%m
+ call assert_fails('Xexpr ["Error", "line 1", "msg"]', 'E342:')
+ set efm&
+
+ call test_alloc_fail(GetAllocId('qf_makecmd'), 0, 0)
+ call assert_fails('Xgrep vim runtest.vim', 'E342:')
+
+ call test_alloc_fail(GetAllocId('qf_linebuf'), 0, 0)
+ call assert_fails('Xexpr repeat("a", 8192)', 'E342:')
+
+ call test_alloc_fail(GetAllocId('qf_linebuf'), 0, 0)
+ call assert_fails('Xexpr [repeat("a", 8192)]', 'E342:')
+
+ new
+ call setline(1, repeat('a', 8192))
+ call test_alloc_fail(GetAllocId('qf_linebuf'), 0, 0)
+ call assert_fails('Xbuffer', 'E342:')
+ %bw!
+
+ call writefile([repeat('a', 8192)], 'Xtest')
+ call test_alloc_fail(GetAllocId('qf_linebuf'), 0, 0)
+ call assert_fails('Xfile Xtest', 'E342:')
+ call delete('Xtest')
+endfunc
+
+func Test_nomem()
+ throw 'Skipped: Nvim does not support test_alloc_fail()'
+ call Xnomem_tests('c')
+ call Xnomem_tests('l')
+endfunc
+
func s:test_xhelpgrep(cchar)
call s:setup_commands(a:cchar)
Xhelpgrep quickfix