vim-patch:8.2.3838: cannot use script-local function for setting *func options

Problem:    Cannot use script-local function for setting *func options.
Solution:   Use the script context. (Yegappan Lakshmanan, closes vim/vim#9362)

db1a410b61

Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
This commit is contained in:
zeertzjq
2022-12-02 20:47:52 +08:00
parent 1aad5af637
commit 7d10194426
5 changed files with 274 additions and 26 deletions

View File

@@ -5157,10 +5157,6 @@ int option_set_callback_func(char *optval, Callback *optcb)
return OK;
}
if (strncmp(optval, "s:", 2) == 0 && !SCRIPT_ID_VALID(current_sctx.sc_sid)) {
return FAIL;
}
typval_T *tv;
if (*optval == '{'
|| (strncmp(optval, "function(", 9) == 0)
@@ -5174,8 +5170,27 @@ int option_set_callback_func(char *optval, Callback *optcb)
// treat everything else as a function name string
tv = xcalloc(1, sizeof(*tv));
tv->v_type = VAR_STRING;
// Function name starting with "s:" are supported only in a vimscript
// context.
if (strncmp(optval, "s:", 2) == 0) {
char sid_buf[25];
if (!SCRIPT_ID_VALID(current_sctx.sc_sid)) {
emsg(_(e_usingsid));
return FAIL;
}
// Expand s: prefix into <SNR>nr_<name>
snprintf(sid_buf, sizeof(sid_buf), "<SNR>%" PRId64 "_",
(int64_t)current_sctx.sc_sid);
char *funcname = xmalloc(strlen(sid_buf) + strlen(optval + 2) + 1);
STRCPY(funcname, sid_buf);
STRCAT(funcname, optval + 2);
tv->vval.v_string = funcname;
} else {
tv->vval.v_string = xstrdup(optval);
}
}
Callback cb;
if (!callback_from_typval(&cb, tv) || cb.type == kCallbackNone) {

View File

@@ -1300,13 +1300,13 @@ func Test_completefunc_callback()
endfunc
let lines =<< trim END
#" Test for using a function name
#" Test for using a global function name
LET &completefunc = 'g:CompleteFunc2'
new
call setline(1, 'zero')
call setline(1, 'global')
LET g:CompleteFunc2Args = []
call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'zero']], g:CompleteFunc2Args)
call assert_equal([[1, ''], [0, 'global']], g:CompleteFunc2Args)
bw!
#" Test for using a function()
@@ -1442,6 +1442,29 @@ func Test_completefunc_callback()
END
call CheckLegacyAndVim9Success(lines)
" Test for using a script-local function name
func s:CompleteFunc3(findstart, base)
call add(g:CompleteFunc3Args, [a:findstart, a:base])
return a:findstart ? 0 : []
endfunc
set completefunc=s:CompleteFunc3
new
call setline(1, 'script1')
let g:CompleteFunc3Args = []
call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'script1']], g:CompleteFunc3Args)
bw!
let &completefunc = 's:CompleteFunc3'
new
call setline(1, 'script2')
let g:CompleteFunc3Args = []
call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'script2']], g:CompleteFunc3Args)
bw!
delfunc s:CompleteFunc3
" invalid return value
let &completefunc = {a -> 'abc'}
call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
@@ -1476,11 +1499,12 @@ func Test_completefunc_callback()
let lines =<< trim END
vim9script
# Test for using a def function with completefunc
def Vim9CompleteFunc(val: number, findstart: number, base: string): any
add(g:Vim9completeFuncArgs, [val, findstart, base])
def Vim9CompleteFunc(callnr: number, findstart: number, base: string): any
add(g:Vim9completeFuncArgs, [callnr, findstart, base])
return findstart ? 0 : []
enddef
# Test for using a def function with completefunc
set completefunc=function('Vim9CompleteFunc',\ [60])
new | only
setline(1, 'one')
@@ -1488,6 +1512,28 @@ func Test_completefunc_callback()
feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
assert_equal([[60, 1, ''], [60, 0, 'one']], g:Vim9completeFuncArgs)
bw!
# Test for using a global function name
&completefunc = g:CompleteFunc2
new | only
setline(1, 'two')
g:CompleteFunc2Args = []
feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
assert_equal([[1, ''], [0, 'two']], g:CompleteFunc2Args)
bw!
# Test for using a script-local function name
def s:LocalCompleteFunc(findstart: number, base: string): any
add(g:LocalCompleteFuncArgs, [findstart, base])
return findstart ? 0 : []
enddef
&completefunc = s:LocalCompleteFunc
new | only
setline(1, 'three')
g:LocalCompleteFuncArgs = []
feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
assert_equal([[1, ''], [0, 'three']], g:LocalCompleteFuncArgs)
bw!
END
" call CheckScriptSuccess(lines)
@@ -1653,6 +1699,29 @@ func Test_omnifunc_callback()
END
call CheckLegacyAndVim9Success(lines)
" Test for using a script-local function name
func s:OmniFunc3(findstart, base)
call add(g:OmniFunc3Args, [a:findstart, a:base])
return a:findstart ? 0 : []
endfunc
set omnifunc=s:OmniFunc3
new
call setline(1, 'script1')
let g:OmniFunc3Args = []
call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'script1']], g:OmniFunc3Args)
bw!
let &omnifunc = 's:OmniFunc3'
new
call setline(1, 'script2')
let g:OmniFunc3Args = []
call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'script2']], g:OmniFunc3Args)
bw!
delfunc s:OmniFunc3
" invalid return value
let &omnifunc = {a -> 'abc'}
call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
@@ -1687,11 +1756,12 @@ func Test_omnifunc_callback()
let lines =<< trim END
vim9script
# Test for using a def function with omnifunc
def Vim9omniFunc(val: number, findstart: number, base: string): any
add(g:Vim9omniFunc_Args, [val, findstart, base])
def Vim9omniFunc(callnr: number, findstart: number, base: string): any
add(g:Vim9omniFunc_Args, [callnr, findstart, base])
return findstart ? 0 : []
enddef
# Test for using a def function with omnifunc
set omnifunc=function('Vim9omniFunc',\ [60])
new | only
setline(1, 'one')
@@ -1699,6 +1769,28 @@ func Test_omnifunc_callback()
feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
assert_equal([[60, 1, ''], [60, 0, 'one']], g:Vim9omniFunc_Args)
bw!
# Test for using a global function name
&omnifunc = g:OmniFunc2
new | only
setline(1, 'two')
g:OmniFunc2Args = []
feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
assert_equal([[1, ''], [0, 'two']], g:OmniFunc2Args)
bw!
# Test for using a script-local function name
def s:LocalOmniFunc(findstart: number, base: string): any
add(g:LocalOmniFuncArgs, [findstart, base])
return findstart ? 0 : []
enddef
&omnifunc = s:LocalOmniFunc
new | only
setline(1, 'three')
g:LocalOmniFuncArgs = []
feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
assert_equal([[1, ''], [0, 'three']], g:LocalOmniFuncArgs)
bw!
END
" call CheckScriptSuccess(lines)
@@ -1887,6 +1979,29 @@ func Test_thesaurusfunc_callback()
END
call CheckLegacyAndVim9Success(lines)
" Test for using a script-local function name
func s:TsrFunc3(findstart, base)
call add(g:TsrFunc3Args, [a:findstart, a:base])
return a:findstart ? 0 : []
endfunc
set tsrfu=s:TsrFunc3
new
call setline(1, 'script1')
let g:TsrFunc3Args = []
call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'script1']], g:TsrFunc3Args)
bw!
let &tsrfu = 's:TsrFunc3'
new
call setline(1, 'script2')
let g:TsrFunc3Args = []
call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'script2']], g:TsrFunc3Args)
bw!
delfunc s:TsrFunc3
" invalid return value
let &thesaurusfunc = {a -> 'abc'}
call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
@@ -1934,11 +2049,12 @@ func Test_thesaurusfunc_callback()
let lines =<< trim END
vim9script
# Test for using a def function with thesaurusfunc
def Vim9tsrFunc(val: number, findstart: number, base: string): any
add(g:Vim9tsrFunc_Args, [val, findstart, base])
def Vim9tsrFunc(callnr: number, findstart: number, base: string): any
add(g:Vim9tsrFunc_Args, [callnr, findstart, base])
return findstart ? 0 : []
enddef
# Test for using a def function with thesaurusfunc
set thesaurusfunc=function('Vim9tsrFunc',\ [60])
new | only
setline(1, 'one')
@@ -1946,6 +2062,28 @@ func Test_thesaurusfunc_callback()
feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
assert_equal([[60, 1, ''], [60, 0, 'one']], g:Vim9tsrFunc_Args)
bw!
# Test for using a global function name
&thesaurusfunc = g:TsrFunc2
new | only
setline(1, 'two')
g:TsrFunc2Args = []
feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
assert_equal([[1, ''], [0, 'two']], g:TsrFunc2Args)
bw!
# Test for using a script-local function name
def s:LocalTsrFunc(findstart: number, base: string): any
add(g:LocalTsrFuncArgs, [findstart, base])
return findstart ? 0 : []
enddef
&thesaurusfunc = s:LocalTsrFunc
new | only
setline(1, 'three')
g:LocalTsrFuncArgs = []
feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
assert_equal([[1, ''], [0, 'three']], g:LocalTsrFuncArgs)
bw!
END
" call CheckScriptSuccess(lines)

View File

@@ -591,6 +591,21 @@ func Test_opfunc_callback()
END
call CheckTransLegacySuccess(lines)
" Test for using a script-local function name
func s:OpFunc3(type)
let g:OpFunc3Args = [a:type]
endfunc
set opfunc=s:OpFunc3
let g:OpFunc3Args = []
normal! g@l
call assert_equal(['char'], g:OpFunc3Args)
let &opfunc = 's:OpFunc3'
let g:OpFunc3Args = []
normal! g@l
call assert_equal(['char'], g:OpFunc3Args)
delfunc s:OpFunc3
" Using Vim9 lambda expression in legacy context should fail
" set opfunc=(a)\ =>\ OpFunc1(24,\ a)
let g:OpFunc1Args = []
@@ -614,14 +629,32 @@ func Test_opfunc_callback()
let lines =<< trim END
vim9script
# Test for using a def function with opfunc
def g:Vim9opFunc(val: number, type: string): void
g:OpFunc1Args = [val, type]
enddef
# Test for using a def function with opfunc
set opfunc=function('g:Vim9opFunc',\ [60])
g:OpFunc1Args = []
normal! g@l
assert_equal([60, 'char'], g:OpFunc1Args)
# Test for using a global function name
&opfunc = g:OpFunc2
g:OpFunc2Args = []
normal! g@l
assert_equal(['char'], g:OpFunc2Args)
bw!
# Test for using a script-local function name
def s:LocalOpFunc(type: string): void
g:LocalOpFuncArgs = [type]
enddef
&opfunc = s:LocalOpFunc
g:LocalOpFuncArgs = []
normal! g@l
assert_equal(['char'], g:LocalOpFuncArgs)
bw!
END
" call CheckScriptSuccess(lines)

View File

@@ -5789,6 +5789,23 @@ func Test_qftextfunc_callback()
END
call CheckLegacyAndVim9Success(lines)
" Test for using a script-local function name
func s:TqfFunc2(info)
let g:TqfFunc2Args = [a:info.start_idx, a:info.end_idx]
return ''
endfunc
let g:TqfFunc2Args = []
set quickfixtextfunc=s:TqfFunc2
cexpr "F10:10:10:L10"
cclose
call assert_equal([1, 1], g:TqfFunc2Args)
let &quickfixtextfunc = 's:TqfFunc2'
cexpr "F11:11:11:L11"
cclose
call assert_equal([1, 1], g:TqfFunc2Args)
delfunc s:TqfFunc2
" set 'quickfixtextfunc' to a partial with dict. This used to cause a crash.
func SetQftfFunc()
let params = {'qftf': function('g:DictQftfFunc')}

View File

@@ -267,38 +267,62 @@ func Test_tagfunc_callback()
END
call CheckLegacyAndVim9Success(lines)
" Test for using a script-local function name
func s:TagFunc3(pat, flags, info)
let g:TagFunc3Args = [a:pat, a:flags, a:info]
return v:null
endfunc
set tagfunc=s:TagFunc3
new
let g:TagFunc3Args = []
call assert_fails('tag a21', 'E433:')
call assert_equal(['a21', '', {}], g:TagFunc3Args)
bw!
let &tagfunc = 's:TagFunc3'
new
let g:TagFunc3Args = []
call assert_fails('tag a22', 'E433:')
call assert_equal(['a22', '', {}], g:TagFunc3Args)
bw!
delfunc s:TagFunc3
" invalid return value
let &tagfunc = "{a -> 'abc'}"
call assert_fails("echo taglist('a')", "E987:")
" Using Vim9 lambda expression in legacy context should fail
" set tagfunc=(a,\ b,\ c)\ =>\ g:TagFunc1(21,\ a,\ b,\ c)
new | only
new
let g:TagFunc1Args = []
" call assert_fails("tag a17", "E117:")
call assert_equal([], g:TagFunc1Args)
bw!
" Test for using a script local function
set tagfunc=<SID>ScriptLocalTagFunc
new | only
new
let g:ScriptLocalFuncArgs = []
call assert_fails('tag a15', 'E433:')
call assert_equal(['a15', '', {}], g:ScriptLocalFuncArgs)
bw!
" Test for using a script local funcref variable
let Fn = function("s:ScriptLocalTagFunc")
let &tagfunc= Fn
new | only
new
let g:ScriptLocalFuncArgs = []
call assert_fails('tag a16', 'E433:')
call assert_equal(['a16', '', {}], g:ScriptLocalFuncArgs)
bw!
" Test for using a string(script local funcref variable)
let Fn = function("s:ScriptLocalTagFunc")
let &tagfunc= string(Fn)
new | only
new
let g:ScriptLocalFuncArgs = []
call assert_fails('tag a16', 'E433:')
call assert_equal(['a16', '', {}], g:ScriptLocalFuncArgs)
bw!
" set 'tagfunc' to a partial with dict. This used to cause a crash.
func SetTagFunc()
@@ -324,16 +348,37 @@ func Test_tagfunc_callback()
let lines =<< trim END
vim9script
# Test for using function()
def Vim9tagFunc(val: number, pat: string, flags: string, info: dict<any>): any
g:Vim9tagFuncArgs = [val, pat, flags, info]
def Vim9tagFunc(callnr: number, pat: string, flags: string, info: dict<any>): any
g:Vim9tagFuncArgs = [callnr, pat, flags, info]
return null
enddef
# Test for using a def function with completefunc
set tagfunc=function('Vim9tagFunc',\ [60])
new | only
new
g:Vim9tagFuncArgs = []
assert_fails('tag a10', 'E433:')
assert_equal([60, 'a10', '', {}], g:Vim9tagFuncArgs)
# Test for using a global function name
&tagfunc = g:TagFunc2
new
g:TagFunc2Args = []
assert_fails('tag a11', 'E433:')
assert_equal(['a11', '', {}], g:TagFunc2Args)
bw!
# Test for using a script-local function name
def s:LocalTagFunc(pat: string, flags: string, info: dict<any> ): any
g:LocalTagFuncArgs = [pat, flags, info]
return null
enddef
&tagfunc = s:LocalTagFunc
new
g:LocalTagFuncArgs = []
assert_fails('tag a12', 'E433:')
assert_equal(['a12', '', {}], g:LocalTagFuncArgs)
bw!
END
" call CheckScriptSuccess(lines)