summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYegappan Lakshmanan <yegappan@yahoo.com>2021-12-24 20:47:38 +0000
committerBram Moolenaar <Bram@vim.org>2021-12-24 20:47:38 +0000
commite7f4abd38b6e05100c699900c8f87281e363beb2 (patch)
treebadec536bcfe024c7214b18fcce1fa58e5315f80
parent73a024209cbfbd5b39a2e974084d807c6131e2ed (diff)
downloadvim-git-e7f4abd38b6e05100c699900c8f87281e363beb2.tar.gz
patch 8.2.3889: duplicate code for translating script-local function namev8.2.3889
Problem: Duplicate code for translating script-local function name. Solution: Move the code to get_scriptlocal_funcname(). (Yegappan Lakshmanan, closes #9393)
-rw-r--r--src/evalfunc.c13
-rw-r--r--src/evalvars.c13
-rw-r--r--src/option.c27
-rw-r--r--src/proto/userfunc.pro1
-rw-r--r--src/testdir/test_expr.vim15
-rw-r--r--src/testdir/test_normal.vim12
-rw-r--r--src/userfunc.c40
-rw-r--r--src/version.c2
8 files changed, 84 insertions, 39 deletions
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 778d16d77..8f5205393 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -4050,22 +4050,11 @@ common_function(typval_T *argvars, typval_T *rettv, int is_funcref)
list_T *list = NULL;
if (STRNCMP(s, "s:", 2) == 0 || STRNCMP(s, "<SID>", 5) == 0)
- {
- char sid_buf[25];
- int off = *s == 's' ? 2 : 5;
-
// Expand s: and <SID> into <SNR>nr_, so that the function can
// also be called from another script. Using trans_function_name()
// would also work, but some plugins depend on the name being
// printable text.
- sprintf(sid_buf, "<SNR>%ld_", (long)current_sctx.sc_sid);
- name = alloc(STRLEN(sid_buf) + STRLEN(s + off) + 1);
- if (name != NULL)
- {
- STRCPY(name, sid_buf);
- STRCAT(name, s + off);
- }
- }
+ name = get_scriptlocal_funcname(s);
else
name = vim_strsave(s);
diff --git a/src/evalvars.c b/src/evalvars.c
index d002e57b4..34cc014ad 100644
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -4450,7 +4450,18 @@ get_callback(typval_T *arg)
r = FAIL;
else if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING)
{
- // Note that we don't make a copy of the string.
+ if (arg->v_type == VAR_STRING)
+ {
+ char_u *name;
+
+ name = get_scriptlocal_funcname(arg->vval.v_string);
+ if (name != NULL)
+ {
+ vim_free(arg->vval.v_string);
+ arg->vval.v_string = name;
+ }
+ }
+
res.cb_name = arg->vval.v_string;
func_ref(res.cb_name);
}
diff --git a/src/option.c b/src/option.c
index b55789dba..739b29adf 100644
--- a/src/option.c
+++ b/src/option.c
@@ -7205,33 +7205,8 @@ option_set_callback_func(char_u *optval UNUSED, callback_T *optcb UNUSED)
// Lambda expression or a funcref
tv = eval_expr(optval, NULL);
else
- {
// treat everything else as a function name string
-
- // Function name starting with "s:" are supported only in a vimscript
- // context.
- if (STRNCMP(optval, "s:", 2) == 0)
- {
- char sid_buf[25];
- char_u *funcname;
-
- if (!SCRIPT_ID_VALID(current_sctx.sc_sid))
- {
- emsg(_(e_using_sid_not_in_script_context));
- return FAIL;
- }
- // Expand s: prefix into <SNR>nr_<name>
- sprintf(sid_buf, "<SNR>%ld_", (long)current_sctx.sc_sid);
- funcname = alloc(STRLEN(sid_buf) + STRLEN(optval + 2) + 1);
- if (funcname == NULL)
- return FAIL;
- STRCPY(funcname, sid_buf);
- STRCAT(funcname, optval + 2);
- tv = alloc_string_tv(funcname);
- }
- else
- tv = alloc_string_tv(vim_strsave(optval));
- }
+ tv = alloc_string_tv(vim_strsave(optval));
if (tv == NULL)
return FAIL;
diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro
index bb3814352..0320f09d3 100644
--- a/src/proto/userfunc.pro
+++ b/src/proto/userfunc.pro
@@ -35,6 +35,7 @@ int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typva
char_u *printable_func_name(ufunc_T *fp);
char_u *trans_function_name(char_u **pp, int *is_global, int skip, int flags, funcdict_T *fdp, partial_T **partial, type_T **type);
char_u *untrans_function_name(char_u *name);
+char_u *get_scriptlocal_funcname(char_u *funcname);
char_u *save_function_name(char_u **name, int *is_global, int skip, int flags, funcdict_T *fudi);
void list_functions(regmatch_T *regmatch);
ufunc_T *define_function(exarg_T *eap, char_u *name_arg);
diff --git a/src/testdir/test_expr.vim b/src/testdir/test_expr.vim
index 99cb9a137..ba2e2597a 100644
--- a/src/testdir/test_expr.vim
+++ b/src/testdir/test_expr.vim
@@ -639,6 +639,21 @@ func Test_funcref()
call assert_fails('echo test_null_function()->funcref()', 'E475: Invalid argument: NULL')
endfunc
+" Test for calling function() and funcref() outside of a Vim script context.
+func Test_function_outside_script()
+ let cleanup =<< trim END
+ call writefile([execute('messages')], 'Xtest.out')
+ qall
+ END
+ call writefile(cleanup, 'Xverify.vim')
+ call RunVim([], [], "-c \"echo function('s:abc')\" -S Xverify.vim")
+ call assert_match('E81: Using <SID> not in a', readfile('Xtest.out')[0])
+ call RunVim([], [], "-c \"echo funcref('s:abc')\" -S Xverify.vim")
+ call assert_match('E81: Using <SID> not in a', readfile('Xtest.out')[0])
+ call delete('Xtest.out')
+ call delete('Xverify.vim')
+endfunc
+
func Test_setmatches()
let lines =<< trim END
hi def link 1 Comment
diff --git a/src/testdir/test_normal.vim b/src/testdir/test_normal.vim
index 08539deb0..57ea6d3ec 100644
--- a/src/testdir/test_normal.vim
+++ b/src/testdir/test_normal.vim
@@ -642,6 +642,18 @@ func Test_opfunc_callback()
END
call CheckScriptSuccess(lines)
+ " setting 'opfunc' to a script local function outside of a script context
+ " should fail
+ let cleanup =<< trim END
+ call writefile([execute('messages')], 'Xtest.out')
+ qall
+ END
+ call writefile(cleanup, 'Xverify.vim')
+ call RunVim([], [], "-c \"set opfunc=s:abc\" -S Xverify.vim")
+ call assert_match('E81: Using <SID> not in a', readfile('Xtest.out')[0])
+ call delete('Xtest.out')
+ call delete('Xverify.vim')
+
" cleanup
set opfunc&
delfunc OpFunc1
diff --git a/src/userfunc.c b/src/userfunc.c
index 7f6754444..897174092 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -3876,6 +3876,46 @@ untrans_function_name(char_u *name)
}
/*
+ * If the 'funcname' starts with "s:" or "<SID>", then expands it to the
+ * current script ID and returns the expanded function name. The caller should
+ * free the returned name. If not called from a script context or the function
+ * name doesn't start with these prefixes, then returns NULL.
+ * This doesn't check whether the script-local function exists or not.
+ */
+ char_u *
+get_scriptlocal_funcname(char_u *funcname)
+{
+ char sid_buf[25];
+ int off;
+ char_u *newname;
+
+ if (funcname == NULL)
+ return NULL;
+
+ if (STRNCMP(funcname, "s:", 2) != 0
+ && STRNCMP(funcname, "<SID>", 5) != 0)
+ // The function name is not a script-local function name
+ return NULL;
+
+ if (!SCRIPT_ID_VALID(current_sctx.sc_sid))
+ {
+ emsg(_(e_using_sid_not_in_script_context));
+ return NULL;
+ }
+ // Expand s: prefix into <SNR>nr_<name>
+ vim_snprintf(sid_buf, sizeof(sid_buf), "<SNR>%ld_",
+ (long)current_sctx.sc_sid);
+ off = *funcname == 's' ? 2 : 5;
+ newname = alloc(STRLEN(sid_buf) + STRLEN(funcname + off) + 1);
+ if (newname == NULL)
+ return NULL;
+ STRCPY(newname, sid_buf);
+ STRCAT(newname, funcname + off);
+
+ return newname;
+}
+
+/*
* Call trans_function_name(), except that a lambda is returned as-is.
* Returns the name in allocated memory.
*/
diff --git a/src/version.c b/src/version.c
index 78baffdec..cd4d15775 100644
--- a/src/version.c
+++ b/src/version.c
@@ -750,6 +750,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 3889,
+/**/
3888,
/**/
3887,