mirror of
https://github.com/vim/vim.git
synced 2025-09-25 03:54:15 -04:00
patch 8.2.1667: local function name cannot shadow a global function name
Problem: Local function name cannot shadow a global function name. Solution: Ignore global functions when checking a script-local or scoped function name. (closes #6926)
This commit is contained in:
@@ -11,6 +11,7 @@ int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, evalarg_T
|
|||||||
char_u *fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error);
|
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, cctx_T *cctx);
|
ufunc_T *find_func_even_dead(char_u *name, int is_global, cctx_T *cctx);
|
||||||
ufunc_T *find_func(char_u *name, int is_global, cctx_T *cctx);
|
ufunc_T *find_func(char_u *name, int is_global, cctx_T *cctx);
|
||||||
|
int func_is_global(ufunc_T *ufunc);
|
||||||
void copy_func(char_u *lambda, char_u *global);
|
void copy_func(char_u *lambda, char_u *global);
|
||||||
int call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv, funcexe_T *funcexe, dict_T *selfdict);
|
int call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv, funcexe_T *funcexe, dict_T *selfdict);
|
||||||
void save_funccal(funccal_entry_T *entry);
|
void save_funccal(funccal_entry_T *entry);
|
||||||
|
@@ -232,6 +232,36 @@ def Test_global_local_function()
|
|||||||
CheckScriptFailure(lines, 'E117:')
|
CheckScriptFailure(lines, 'E117:')
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
def Test_local_function_shadows_global()
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
def g:Gfunc(): string
|
||||||
|
return 'global'
|
||||||
|
enddef
|
||||||
|
def AnotherFunc(): number
|
||||||
|
let Gfunc = function('len')
|
||||||
|
return Gfunc('testing')
|
||||||
|
enddef
|
||||||
|
g:Gfunc()->assert_equal('global')
|
||||||
|
AnotherFunc()->assert_equal(7)
|
||||||
|
delfunc g:Gfunc
|
||||||
|
END
|
||||||
|
CheckScriptSuccess(lines)
|
||||||
|
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
def g:Func(): string
|
||||||
|
return 'global'
|
||||||
|
enddef
|
||||||
|
def AnotherFunc()
|
||||||
|
g:Func = function('len')
|
||||||
|
enddef
|
||||||
|
AnotherFunc()
|
||||||
|
END
|
||||||
|
CheckScriptFailure(lines, 'E705:')
|
||||||
|
delfunc g:Func
|
||||||
|
enddef
|
||||||
|
|
||||||
func TakesOneArg(arg)
|
func TakesOneArg(arg)
|
||||||
echo a:arg
|
echo a:arg
|
||||||
endfunc
|
endfunc
|
||||||
|
@@ -874,6 +874,15 @@ find_func(char_u *name, int is_global, cctx_T *cctx)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return TRUE if "ufunc" is a global function.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
func_is_global(ufunc_T *ufunc)
|
||||||
|
{
|
||||||
|
return ufunc->uf_name[0] != K_SPECIAL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copy the function name of "fp" to buffer "buf".
|
* Copy the function name of "fp" to buffer "buf".
|
||||||
* "buf" must be able to hold the function name plus three bytes.
|
* "buf" must be able to hold the function name plus three bytes.
|
||||||
@@ -882,7 +891,7 @@ find_func(char_u *name, int is_global, cctx_T *cctx)
|
|||||||
static void
|
static void
|
||||||
cat_func_name(char_u *buf, ufunc_T *fp)
|
cat_func_name(char_u *buf, ufunc_T *fp)
|
||||||
{
|
{
|
||||||
if (fp->uf_name[0] == K_SPECIAL)
|
if (!func_is_global(fp))
|
||||||
{
|
{
|
||||||
STRCPY(buf, "<SNR>");
|
STRCPY(buf, "<SNR>");
|
||||||
STRCAT(buf, fp->uf_name + 3);
|
STRCAT(buf, fp->uf_name + 3);
|
||||||
|
@@ -750,6 +750,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
1667,
|
||||||
/**/
|
/**/
|
||||||
1666,
|
1666,
|
||||||
/**/
|
/**/
|
||||||
|
@@ -292,12 +292,14 @@ lookup_script(char_u *name, size_t len, int vim9script)
|
|||||||
/*
|
/*
|
||||||
* Check if "p[len]" is already defined, either in script "import_sid" or in
|
* Check if "p[len]" is already defined, either in script "import_sid" or in
|
||||||
* compilation context "cctx".
|
* compilation context "cctx".
|
||||||
|
* Does not check the global namespace.
|
||||||
* Return FAIL and give an error if it defined.
|
* Return FAIL and give an error if it defined.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
check_defined(char_u *p, size_t len, cctx_T *cctx)
|
check_defined(char_u *p, size_t len, cctx_T *cctx)
|
||||||
{
|
{
|
||||||
int c = p[len];
|
int c = p[len];
|
||||||
|
ufunc_T *ufunc = NULL;
|
||||||
|
|
||||||
p[len] = NUL;
|
p[len] = NUL;
|
||||||
if (lookup_script(p, len, FALSE) == OK
|
if (lookup_script(p, len, FALSE) == OK
|
||||||
@@ -305,11 +307,16 @@ check_defined(char_u *p, size_t len, cctx_T *cctx)
|
|||||||
&& (lookup_local(p, len, cctx) != NULL
|
&& (lookup_local(p, len, cctx) != NULL
|
||||||
|| lookup_arg(p, len, NULL, NULL, NULL, cctx) == OK))
|
|| lookup_arg(p, len, NULL, NULL, NULL, cctx) == OK))
|
||||||
|| find_imported(p, len, cctx) != NULL
|
|| find_imported(p, len, cctx) != NULL
|
||||||
|| find_func_even_dead(p, FALSE, cctx) != NULL)
|
|| (ufunc = find_func_even_dead(p, FALSE, cctx)) != NULL)
|
||||||
{
|
{
|
||||||
p[len] = c;
|
// A local or script-local function can shadow a global function.
|
||||||
semsg(_(e_name_already_defined_str), p);
|
if (ufunc == NULL || !func_is_global(ufunc)
|
||||||
return FAIL;
|
|| (p[0] == 'g' && p[1] == ':'))
|
||||||
|
{
|
||||||
|
p[len] = c;
|
||||||
|
semsg(_(e_name_already_defined_str), p);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
p[len] = c;
|
p[len] = c;
|
||||||
return OK;
|
return OK;
|
||||||
@@ -2114,10 +2121,16 @@ generate_funcref(cctx_T *cctx, char_u *name)
|
|||||||
/*
|
/*
|
||||||
* Compile a variable name into a load instruction.
|
* Compile a variable name into a load instruction.
|
||||||
* "end" points to just after the name.
|
* "end" points to just after the name.
|
||||||
|
* "is_expr" is TRUE when evaluating an expression, might be a funcref.
|
||||||
* When "error" is FALSE do not give an error when not found.
|
* When "error" is FALSE do not give an error when not found.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
compile_load(char_u **arg, char_u *end_arg, cctx_T *cctx, int error)
|
compile_load(
|
||||||
|
char_u **arg,
|
||||||
|
char_u *end_arg,
|
||||||
|
cctx_T *cctx,
|
||||||
|
int is_expr,
|
||||||
|
int error)
|
||||||
{
|
{
|
||||||
type_T *type;
|
type_T *type;
|
||||||
char_u *name = NULL;
|
char_u *name = NULL;
|
||||||
@@ -2214,10 +2227,11 @@ compile_load(char_u **arg, char_u *end_arg, cctx_T *cctx, int error)
|
|||||||
|| find_imported(name, 0, cctx) != NULL)
|
|| find_imported(name, 0, cctx) != NULL)
|
||||||
res = compile_load_scriptvar(cctx, name, *arg, &end, FALSE);
|
res = compile_load_scriptvar(cctx, name, *arg, &end, FALSE);
|
||||||
|
|
||||||
// When the name starts with an uppercase letter or "x:" it
|
// When evaluating an expression and the name starts with an
|
||||||
// can be a user defined function.
|
// uppercase letter or "x:" it can be a user defined function.
|
||||||
// TODO: this is just guessing
|
// TODO: this is just guessing
|
||||||
if (res == FAIL && (ASCII_ISUPPER(*name) || name[1] == ':'))
|
if (res == FAIL && is_expr
|
||||||
|
&& (ASCII_ISUPPER(*name) || name[1] == ':'))
|
||||||
res = generate_funcref(cctx, name);
|
res = generate_funcref(cctx, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2368,8 +2382,9 @@ compile_call(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If we can find the function by name generate the right call.
|
// If we can find the function by name generate the right call.
|
||||||
|
// Skip global functions here, a local funcref takes precedence.
|
||||||
ufunc = find_func(name, FALSE, cctx);
|
ufunc = find_func(name, FALSE, cctx);
|
||||||
if (ufunc != NULL)
|
if (ufunc != NULL && !func_is_global(ufunc))
|
||||||
{
|
{
|
||||||
res = generate_CALL(cctx, ufunc, argcount);
|
res = generate_CALL(cctx, ufunc, argcount);
|
||||||
goto theend;
|
goto theend;
|
||||||
@@ -2380,7 +2395,7 @@ compile_call(
|
|||||||
// Not for eome#Func(), it will be loaded later.
|
// Not for eome#Func(), it will be loaded later.
|
||||||
p = namebuf;
|
p = namebuf;
|
||||||
if (STRNCMP(namebuf, "g:", 2) != 0 && !is_autoload
|
if (STRNCMP(namebuf, "g:", 2) != 0 && !is_autoload
|
||||||
&& compile_load(&p, namebuf + varlen, cctx, FALSE) == OK)
|
&& compile_load(&p, namebuf + varlen, cctx, FALSE, FALSE) == OK)
|
||||||
{
|
{
|
||||||
garray_T *stack = &cctx->ctx_type_stack;
|
garray_T *stack = &cctx->ctx_type_stack;
|
||||||
type_T *type;
|
type_T *type;
|
||||||
@@ -2390,6 +2405,13 @@ compile_call(
|
|||||||
goto theend;
|
goto theend;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we can find a global function by name generate the right call.
|
||||||
|
if (ufunc != NULL)
|
||||||
|
{
|
||||||
|
res = generate_CALL(cctx, ufunc, argcount);
|
||||||
|
goto theend;
|
||||||
|
}
|
||||||
|
|
||||||
// A global function may be defined only later. Need to figure out at
|
// A global function may be defined only later. Need to figure out at
|
||||||
// runtime. Also handles a FuncRef at runtime.
|
// runtime. Also handles a FuncRef at runtime.
|
||||||
if (STRNCMP(namebuf, "g:", 2) == 0 || is_autoload)
|
if (STRNCMP(namebuf, "g:", 2) == 0 || is_autoload)
|
||||||
@@ -3548,7 +3570,7 @@ compile_expr7(
|
|||||||
{
|
{
|
||||||
if (generate_ppconst(cctx, ppconst) == FAIL)
|
if (generate_ppconst(cctx, ppconst) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
r = compile_load(arg, p, cctx, TRUE);
|
r = compile_load(arg, p, cctx, TRUE, TRUE);
|
||||||
}
|
}
|
||||||
if (r == FAIL)
|
if (r == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
@@ -5002,6 +5024,11 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
|
|||||||
: ((type_T **)stack->ga_data)[stack->ga_len - 1];
|
: ((type_T **)stack->ga_data)[stack->ga_len - 1];
|
||||||
if (lvar != NULL && (is_decl || !has_type))
|
if (lvar != NULL && (is_decl || !has_type))
|
||||||
{
|
{
|
||||||
|
if ((stacktype->tt_type == VAR_FUNC
|
||||||
|
|| stacktype->tt_type == VAR_PARTIAL)
|
||||||
|
&& var_wrong_func_name(name, TRUE))
|
||||||
|
goto theend;
|
||||||
|
|
||||||
if (new_local && !has_type)
|
if (new_local && !has_type)
|
||||||
{
|
{
|
||||||
if (stacktype->tt_type == VAR_VOID)
|
if (stacktype->tt_type == VAR_VOID)
|
||||||
@@ -5009,12 +5036,6 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
|
|||||||
emsg(_(e_cannot_use_void_value));
|
emsg(_(e_cannot_use_void_value));
|
||||||
goto theend;
|
goto theend;
|
||||||
}
|
}
|
||||||
else if ((stacktype->tt_type == VAR_FUNC
|
|
||||||
|| stacktype->tt_type == VAR_PARTIAL)
|
|
||||||
&& var_wrong_func_name(name, TRUE))
|
|
||||||
{
|
|
||||||
goto theend;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// An empty list or dict has a &t_void member,
|
// An empty list or dict has a &t_void member,
|
||||||
|
Reference in New Issue
Block a user