From acc4b5648b49ec13c4f35ee0bf552eda71b0c372 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 24 Jan 2022 13:54:45 +0000 Subject: patch 8.2.4202: Vim9: cannot export function that exists globally Problem: Vim9: cannot export function that exists globally. Solution: When checking if a function already exists only check for script-local functions. (closes #9615) --- src/proto/userfunc.pro | 2 +- src/testdir/test_vim9_import.vim | 31 +++++++++++++++++++++++++++++++ src/userfunc.c | 27 ++++++++++++++++++--------- src/version.c | 2 ++ src/vim.h | 4 ++++ src/vim9compile.c | 2 +- src/vim9instr.c | 2 +- 7 files changed, 58 insertions(+), 12 deletions(-) diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro index 475b70af4c..464d7f239a 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 2ad5057565..4e0b5332e6 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 650dfc9d12..8c204398ca 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 1ba9e1d026..99a7555b61 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 */ +/**/ + 4202, /**/ 4201, /**/ diff --git a/src/vim.h b/src/vim.h index f418e04c8f..9aac6d30d9 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 2b706ad188..9ad1e7ddc9 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 493683acb9..b6e28b0b94 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) { -- cgit v1.2.3