summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2022-01-24 13:54:45 +0000
committerBram Moolenaar <Bram@vim.org>2022-01-24 13:54:45 +0000
commitacc4b5648b49ec13c4f35ee0bf552eda71b0c372 (patch)
tree1d39323dfdfb6cec204d7a342dc6a70bf010fb91
parent8e4af851fd3eff4b22fca962e5be783742e8f1bb (diff)
downloadvim-git-acc4b5648b49ec13c4f35ee0bf552eda71b0c372.tar.gz
patch 8.2.4202: Vim9: cannot export function that exists globallyv8.2.4202
Problem: Vim9: cannot export function that exists globally. Solution: When checking if a function already exists only check for script-local functions. (closes #9615)
-rw-r--r--src/proto/userfunc.pro2
-rw-r--r--src/testdir/test_vim9_import.vim31
-rw-r--r--src/userfunc.c27
-rw-r--r--src/version.c2
-rw-r--r--src/vim.h4
-rw-r--r--src/vim9compile.c2
-rw-r--r--src/vim9instr.c2
7 files changed, 58 insertions, 12 deletions
diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro
index 475b70af4..464d7f239 100644
--- a/src/proto/userfunc.pro
+++ b/src/proto/userfunc.pro
@@ -8,7 +8,7 @@ char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, type_T **
void emsg_funcname(char *ermsg, char_u *name);
int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, evalarg_T *evalarg, funcexe_T *funcexe);
char_u *fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error);
-ufunc_T *find_func_even_dead(char_u *name, int is_global);
+ufunc_T *find_func_even_dead(char_u *name, int flags);
ufunc_T *find_func(char_u *name, int is_global);
int func_is_global(ufunc_T *ufunc);
int func_name_refcount(char_u *name);
diff --git a/src/testdir/test_vim9_import.vim b/src/testdir/test_vim9_import.vim
index 2ad505756..4e0b5332e 100644
--- a/src/testdir/test_vim9_import.vim
+++ b/src/testdir/test_vim9_import.vim
@@ -965,6 +965,37 @@ def Run_Test_import_in_spellsuggest_expr()
set nospell spellsuggest& verbose=0
enddef
+def Test_export_shadows_global_function()
+ mkdir('Xdir/autoload', 'p')
+ var save_rtp = &rtp
+ exe 'set rtp^=' .. getcwd() .. '/Xdir'
+
+ var lines =<< trim END
+ vim9script
+ export def Shadow(): string
+ return 'Shadow()'
+ enddef
+ END
+ writefile(lines, 'Xdir/autoload/shadow.vim')
+
+ lines =<< trim END
+ vim9script
+
+ def g:Shadow(): string
+ return 'global'
+ enddef
+
+ import autoload 'shadow.vim'
+ assert_equal('Shadow()', shadow.Shadow())
+ END
+ CheckScriptSuccess(lines)
+
+ delfunc g:Shadow
+ bwipe!
+ delete('Xdir', 'rf')
+ &rtp = save_rtp
+enddef
+
def Test_export_fails()
CheckScriptFailure(['export var some = 123'], 'E1042:')
CheckScriptFailure(['vim9script', 'export var g:some'], 'E1022:')
diff --git a/src/userfunc.c b/src/userfunc.c
index 650dfc9d1..8c204398c 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -1941,16 +1941,18 @@ find_func_with_prefix(char_u *name, int sid)
/*
* Find a function by name, return pointer to it in ufuncs.
- * When "is_global" is true don't find script-local or imported functions.
+ * When "flags" has FFED_IS_GLOBAL don't find script-local or imported
+ * functions.
+ * When "flags" has "FFED_NO_GLOBAL" don't find global functions.
* Return NULL for unknown function.
*/
ufunc_T *
-find_func_even_dead(char_u *name, int is_global)
+find_func_even_dead(char_u *name, int flags)
{
hashitem_T *hi;
ufunc_T *func;
- if (!is_global)
+ if ((flags & FFED_IS_GLOBAL) == 0)
{
int find_script_local = in_vim9script() && eval_isnamec1(*name)
&& (name[1] != ':' || *name == 's');
@@ -1965,10 +1967,13 @@ find_func_even_dead(char_u *name, int is_global)
}
}
- hi = hash_find(&func_hashtab,
+ if ((flags & FFED_NO_GLOBAL) == 0)
+ {
+ hi = hash_find(&func_hashtab,
STRNCMP(name, "g:", 2) == 0 ? name + 2 : name);
- if (!HASHITEM_EMPTY(hi))
- return HI2UF(hi);
+ if (!HASHITEM_EMPTY(hi))
+ return HI2UF(hi);
+ }
// Find autoload function if this is an autoload script.
return find_func_with_prefix(name[0] == 's' && name[1] == ':'
@@ -1983,7 +1988,7 @@ find_func_even_dead(char_u *name, int is_global)
ufunc_T *
find_func(char_u *name, int is_global)
{
- ufunc_T *fp = find_func_even_dead(name, is_global);
+ ufunc_T *fp = find_func_even_dead(name, is_global ? FFED_IS_GLOBAL : 0);
if (fp != NULL && (fp->uf_flags & FC_DEAD) == 0)
return fp;
@@ -2354,7 +2359,7 @@ func_clear_free(ufunc_T *fp, int force)
int
copy_func(char_u *lambda, char_u *global, ectx_T *ectx)
{
- ufunc_T *ufunc = find_func_even_dead(lambda, TRUE);
+ ufunc_T *ufunc = find_func_even_dead(lambda, FFED_IS_GLOBAL);
ufunc_T *fp = NULL;
if (ufunc == NULL)
@@ -4464,6 +4469,7 @@ define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free)
hashtab_T *ht;
char_u *find_name = name;
int var_conflict = FALSE;
+ int ffed_flags = is_global ? FFED_IS_GLOBAL : 0;
v = find_var(name, &ht, TRUE);
if (v != NULL && (in_vim9script() || v->di_tv.v_type == VAR_FUNC))
@@ -4481,6 +4487,9 @@ define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free)
v = find_var(find_name, &ht, TRUE);
if (v != NULL)
var_conflict = TRUE;
+ // Only check if the function already exists in the script,
+ // global functions can be shadowed.
+ ffed_flags |= FFED_NO_GLOBAL;
}
else
{
@@ -4508,7 +4517,7 @@ define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free)
goto erret;
}
- fp = find_func_even_dead(find_name, is_global);
+ fp = find_func_even_dead(find_name, ffed_flags);
if (vim9script)
{
char_u *uname = untrans_function_name(name);
diff --git a/src/version.c b/src/version.c
index 1ba9e1d02..99a7555b6 100644
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 4202,
+/**/
4201,
/**/
4200,
diff --git a/src/vim.h b/src/vim.h
index f418e04c8..9aac6d30d 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -2798,4 +2798,8 @@ long elapsed(DWORD start_tick);
#define VSE_SHELL 1 // escape for a shell command
#define VSE_BUFFER 2 // escape for a ":buffer" command
+// Flags used by find_func_even_dead()
+#define FFED_IS_GLOBAL 1 // "g:" was used
+#define FFED_NO_GLOBAL 2 // only check for script-local functions
+
#endif // VIM__H
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 2b706ad18..9ad1e7ddc 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -332,7 +332,7 @@ check_defined(char_u *p, size_t len, cctx_T *cctx, int is_arg)
&& (lookup_local(p, len, NULL, cctx) == OK
|| arg_exists(p, len, NULL, NULL, NULL, cctx) == OK))
|| find_imported(p, len, FALSE, cctx) != NULL
- || (ufunc = find_func_even_dead(p, FALSE)) != NULL)
+ || (ufunc = find_func_even_dead(p, 0)) != NULL)
{
// A local or script-local function can shadow a global function.
if (ufunc == NULL || ((ufunc->uf_flags & FC_DEAD) == 0
diff --git a/src/vim9instr.c b/src/vim9instr.c
index 493683acb..b6e28b0b9 100644
--- a/src/vim9instr.c
+++ b/src/vim9instr.c
@@ -2050,7 +2050,7 @@ delete_instr(isn_T *isn)
case ISN_NEWFUNC:
{
char_u *lambda = isn->isn_arg.newfunc.nf_lambda;
- ufunc_T *ufunc = find_func_even_dead(lambda, TRUE);
+ ufunc_T *ufunc = find_func_even_dead(lambda, FFED_IS_GLOBAL);
if (ufunc != NULL)
{