patch 8.2.3889: duplicate code for translating script-local function name
Problem: Duplicate code for translating script-local function name.
Solution: Move the code to get_scriptlocal_funcname(). (Yegappan Lakshmanan,
closes #9393)
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 778d16d..8f52053 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -4050,22 +4050,11 @@
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 d002e57..34cc014 100644
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -4450,7 +4450,18 @@
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 b55789d..739b29a 100644
--- a/src/option.c
+++ b/src/option.c
@@ -7205,33 +7205,8 @@
// 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 bb38143..0320f09 100644
--- a/src/proto/userfunc.pro
+++ b/src/proto/userfunc.pro
@@ -35,6 +35,7 @@
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 99cb9a1..ba2e259 100644
--- a/src/testdir/test_expr.vim
+++ b/src/testdir/test_expr.vim
@@ -639,6 +639,21 @@
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 08539de..57ea6d3 100644
--- a/src/testdir/test_normal.vim
+++ b/src/testdir/test_normal.vim
@@ -642,6 +642,18 @@
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 7f67544..8971740 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -3876,6 +3876,46 @@
}
/*
+ * 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 78baffd..cd4d157 100644
--- a/src/version.c
+++ b/src/version.c
@@ -750,6 +750,8 @@
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 3889,
+/**/
3888,
/**/
3887,