Skip to content

Commit acc4b56

Browse files
committed
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)
1 parent 8e4af85 commit acc4b56

File tree

7 files changed

+58
-12
lines changed

7 files changed

+58
-12
lines changed

src/proto/userfunc.pro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, type_T **
88
void emsg_funcname(char *ermsg, char_u *name);
99
int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, evalarg_T *evalarg, funcexe_T *funcexe);
1010
char_u *fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error);
11-
ufunc_T *find_func_even_dead(char_u *name, int is_global);
11+
ufunc_T *find_func_even_dead(char_u *name, int flags);
1212
ufunc_T *find_func(char_u *name, int is_global);
1313
int func_is_global(ufunc_T *ufunc);
1414
int func_name_refcount(char_u *name);

src/testdir/test_vim9_import.vim

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -965,6 +965,37 @@ def Run_Test_import_in_spellsuggest_expr()
965965
set nospell spellsuggest& verbose=0
966966
enddef
967967

968+
def Test_export_shadows_global_function()
969+
mkdir('Xdir/autoload', 'p')
970+
var save_rtp = &rtp
971+
exe 'set rtp^=' .. getcwd() .. '/Xdir'
972+
973+
var lines =<< trim END
974+
vim9script
975+
export def Shadow(): string
976+
return 'Shadow()'
977+
enddef
978+
END
979+
writefile(lines, 'Xdir/autoload/shadow.vim')
980+
981+
lines =<< trim END
982+
vim9script
983+
984+
def g:Shadow(): string
985+
return 'global'
986+
enddef
987+
988+
import autoload 'shadow.vim'
989+
assert_equal('Shadow()', shadow.Shadow())
990+
END
991+
CheckScriptSuccess(lines)
992+
993+
delfunc g:Shadow
994+
bwipe!
995+
delete('Xdir', 'rf')
996+
&rtp = save_rtp
997+
enddef
998+
968999
def Test_export_fails()
9691000
CheckScriptFailure(['export var some = 123'], 'E1042:')
9701001
CheckScriptFailure(['vim9script', 'export var g:some'], 'E1022:')

src/userfunc.c

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1941,16 +1941,18 @@ find_func_with_prefix(char_u *name, int sid)
19411941

19421942
/*
19431943
* Find a function by name, return pointer to it in ufuncs.
1944-
* When "is_global" is true don't find script-local or imported functions.
1944+
* When "flags" has FFED_IS_GLOBAL don't find script-local or imported
1945+
* functions.
1946+
* When "flags" has "FFED_NO_GLOBAL" don't find global functions.
19451947
* Return NULL for unknown function.
19461948
*/
19471949
ufunc_T *
1948-
find_func_even_dead(char_u *name, int is_global)
1950+
find_func_even_dead(char_u *name, int flags)
19491951
{
19501952
hashitem_T *hi;
19511953
ufunc_T *func;
19521954

1953-
if (!is_global)
1955+
if ((flags & FFED_IS_GLOBAL) == 0)
19541956
{
19551957
int find_script_local = in_vim9script() && eval_isnamec1(*name)
19561958
&& (name[1] != ':' || *name == 's');
@@ -1965,10 +1967,13 @@ find_func_even_dead(char_u *name, int is_global)
19651967
}
19661968
}
19671969

1968-
hi = hash_find(&func_hashtab,
1970+
if ((flags & FFED_NO_GLOBAL) == 0)
1971+
{
1972+
hi = hash_find(&func_hashtab,
19691973
STRNCMP(name, "g:", 2) == 0 ? name + 2 : name);
1970-
if (!HASHITEM_EMPTY(hi))
1971-
return HI2UF(hi);
1974+
if (!HASHITEM_EMPTY(hi))
1975+
return HI2UF(hi);
1976+
}
19721977

19731978
// Find autoload function if this is an autoload script.
19741979
return find_func_with_prefix(name[0] == 's' && name[1] == ':'
@@ -1983,7 +1988,7 @@ find_func_even_dead(char_u *name, int is_global)
19831988
ufunc_T *
19841989
find_func(char_u *name, int is_global)
19851990
{
1986-
ufunc_T *fp = find_func_even_dead(name, is_global);
1991+
ufunc_T *fp = find_func_even_dead(name, is_global ? FFED_IS_GLOBAL : 0);
19871992

19881993
if (fp != NULL && (fp->uf_flags & FC_DEAD) == 0)
19891994
return fp;
@@ -2354,7 +2359,7 @@ func_clear_free(ufunc_T *fp, int force)
23542359
int
23552360
copy_func(char_u *lambda, char_u *global, ectx_T *ectx)
23562361
{
2357-
ufunc_T *ufunc = find_func_even_dead(lambda, TRUE);
2362+
ufunc_T *ufunc = find_func_even_dead(lambda, FFED_IS_GLOBAL);
23582363
ufunc_T *fp = NULL;
23592364

23602365
if (ufunc == NULL)
@@ -4464,6 +4469,7 @@ define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free)
44644469
hashtab_T *ht;
44654470
char_u *find_name = name;
44664471
int var_conflict = FALSE;
4472+
int ffed_flags = is_global ? FFED_IS_GLOBAL : 0;
44674473

44684474
v = find_var(name, &ht, TRUE);
44694475
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)
44814487
v = find_var(find_name, &ht, TRUE);
44824488
if (v != NULL)
44834489
var_conflict = TRUE;
4490+
// Only check if the function already exists in the script,
4491+
// global functions can be shadowed.
4492+
ffed_flags |= FFED_NO_GLOBAL;
44844493
}
44854494
else
44864495
{
@@ -4508,7 +4517,7 @@ define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free)
45084517
goto erret;
45094518
}
45104519

4511-
fp = find_func_even_dead(find_name, is_global);
4520+
fp = find_func_even_dead(find_name, ffed_flags);
45124521
if (vim9script)
45134522
{
45144523
char_u *uname = untrans_function_name(name);

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,8 @@ static char *(features[]) =
750750

751751
static int included_patches[] =
752752
{ /* Add new patch number below this line */
753+
/**/
754+
4202,
753755
/**/
754756
4201,
755757
/**/

src/vim.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2798,4 +2798,8 @@ long elapsed(DWORD start_tick);
27982798
#define VSE_SHELL 1 // escape for a shell command
27992799
#define VSE_BUFFER 2 // escape for a ":buffer" command
28002800

2801+
// Flags used by find_func_even_dead()
2802+
#define FFED_IS_GLOBAL 1 // "g:" was used
2803+
#define FFED_NO_GLOBAL 2 // only check for script-local functions
2804+
28012805
#endif // VIM__H

src/vim9compile.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ check_defined(char_u *p, size_t len, cctx_T *cctx, int is_arg)
332332
&& (lookup_local(p, len, NULL, cctx) == OK
333333
|| arg_exists(p, len, NULL, NULL, NULL, cctx) == OK))
334334
|| find_imported(p, len, FALSE, cctx) != NULL
335-
|| (ufunc = find_func_even_dead(p, FALSE)) != NULL)
335+
|| (ufunc = find_func_even_dead(p, 0)) != NULL)
336336
{
337337
// A local or script-local function can shadow a global function.
338338
if (ufunc == NULL || ((ufunc->uf_flags & FC_DEAD) == 0

src/vim9instr.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2050,7 +2050,7 @@ delete_instr(isn_T *isn)
20502050
case ISN_NEWFUNC:
20512051
{
20522052
char_u *lambda = isn->isn_arg.newfunc.nf_lambda;
2053-
ufunc_T *ufunc = find_func_even_dead(lambda, TRUE);
2053+
ufunc_T *ufunc = find_func_even_dead(lambda, FFED_IS_GLOBAL);
20542054

20552055
if (ufunc != NULL)
20562056
{

0 commit comments

Comments
 (0)