mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
vim-patch:7.4.1582
Problem: Get E923 when using function(dict.func, [], dict). (Kent Sibilev)
Storing a function with a dict in a variable drops the dict if the
function is script-local.
Solution: Translate the function name. Use dict arg if present.
6f2e4b36c9
This commit is contained in:
parent
5cf0c99755
commit
e2258598ca
150
src/nvim/eval.c
150
src/nvim/eval.c
@ -170,8 +170,6 @@ static char *e_letwrong = N_("E734: Wrong variable type for %s=");
|
|||||||
static char *e_nofunc = N_("E130: Unknown function: %s");
|
static char *e_nofunc = N_("E130: Unknown function: %s");
|
||||||
static char *e_illvar = N_("E461: Illegal variable name: %s");
|
static char *e_illvar = N_("E461: Illegal variable name: %s");
|
||||||
static char *e_float_as_string = N_("E806: using Float as a String");
|
static char *e_float_as_string = N_("E806: using Float as a String");
|
||||||
static char *e_dict_both = N_("E924: can't have both a \"self\" dict and "
|
|
||||||
"a partial: %s");
|
|
||||||
|
|
||||||
static char_u * const empty_string = (char_u *)"";
|
static char_u * const empty_string = (char_u *)"";
|
||||||
static char_u * const namespace_char = (char_u *)"abglstvw";
|
static char_u * const namespace_char = (char_u *)"abglstvw";
|
||||||
@ -6933,12 +6931,65 @@ get_func_tv (
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define ERROR_UNKNOWN 0
|
||||||
|
#define ERROR_TOOMANY 1
|
||||||
|
#define ERROR_TOOFEW 2
|
||||||
|
#define ERROR_SCRIPT 3
|
||||||
|
#define ERROR_DICT 4
|
||||||
|
#define ERROR_NONE 5
|
||||||
|
#define ERROR_OTHER 6
|
||||||
|
#define ERROR_BOTH 7
|
||||||
|
#define FLEN_FIXED 40
|
||||||
|
|
||||||
/*
|
/// In a script change <SID>name() and s:name() to K_SNR 123_name().
|
||||||
* Call a function with its resolved parameters
|
/// Change <SNR>123_name() to K_SNR 123_name().
|
||||||
* Return FAIL when the function can't be called, OK otherwise.
|
/// Use "fname_buf[FLEN_FIXED + 1]" when it fits, otherwise allocate memory
|
||||||
* Also returns OK when an error was encountered while executing the function.
|
/// (slow).
|
||||||
*/
|
static char_u *
|
||||||
|
fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error) {
|
||||||
|
int llen;
|
||||||
|
char_u *fname;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
llen = eval_fname_script(name);
|
||||||
|
if (llen > 0) {
|
||||||
|
fname_buf[0] = K_SPECIAL;
|
||||||
|
fname_buf[1] = KS_EXTRA;
|
||||||
|
fname_buf[2] = (int)KE_SNR;
|
||||||
|
i = 3;
|
||||||
|
if (eval_fname_sid(name)) { // "<SID>" or "s:"
|
||||||
|
if (current_SID <= 0) {
|
||||||
|
*error = ERROR_SCRIPT;
|
||||||
|
} else {
|
||||||
|
vim_snprintf((char *)fname_buf + 3, ARRAY_SIZE(fname_buf) - 3,
|
||||||
|
"%" PRId64 "_", (int64_t)current_SID);
|
||||||
|
i = (int)STRLEN(fname_buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i + STRLEN(name + llen) < FLEN_FIXED) {
|
||||||
|
STRCPY(fname_buf + i, name + llen);
|
||||||
|
fname = fname_buf;
|
||||||
|
} else {
|
||||||
|
fname = xmalloc((unsigned)(i + STRLEN(name + llen) + 1));
|
||||||
|
if (fname == NULL) {
|
||||||
|
*error = ERROR_OTHER;
|
||||||
|
} else {
|
||||||
|
*tofree = fname;
|
||||||
|
memmove(fname, fname_buf, (size_t)i);
|
||||||
|
STRCPY(fname + i, name + llen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fname = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fname;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call a function with its resolved parameters
|
||||||
|
/// Return FAIL when the function can't be called, OK otherwise.
|
||||||
|
/// Also returns OK when an error was encountered while executing the function.
|
||||||
int
|
int
|
||||||
call_func(
|
call_func(
|
||||||
char_u *funcname, // name of the function
|
char_u *funcname, // name of the function
|
||||||
@ -6956,19 +7007,10 @@ call_func(
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
int ret = FAIL;
|
int ret = FAIL;
|
||||||
#define ERROR_UNKNOWN 0
|
|
||||||
#define ERROR_TOOMANY 1
|
|
||||||
#define ERROR_TOOFEW 2
|
|
||||||
#define ERROR_SCRIPT 3
|
|
||||||
#define ERROR_DICT 4
|
|
||||||
#define ERROR_NONE 5
|
|
||||||
#define ERROR_OTHER 6
|
|
||||||
#define ERROR_BOTH 7
|
|
||||||
int error = ERROR_NONE;
|
int error = ERROR_NONE;
|
||||||
int llen;
|
|
||||||
ufunc_T *fp;
|
ufunc_T *fp;
|
||||||
#define FLEN_FIXED 40
|
|
||||||
char_u fname_buf[FLEN_FIXED + 1];
|
char_u fname_buf[FLEN_FIXED + 1];
|
||||||
|
char_u *tofree = NULL;
|
||||||
char_u *fname;
|
char_u *fname;
|
||||||
char_u *name;
|
char_u *name;
|
||||||
int argcount = argcount_in;
|
int argcount = argcount_in;
|
||||||
@ -6980,44 +7022,20 @@ call_func(
|
|||||||
// Make a copy of the name, if it comes from a funcref variable it could
|
// Make a copy of the name, if it comes from a funcref variable it could
|
||||||
// be changed or deleted in the called function.
|
// be changed or deleted in the called function.
|
||||||
name = vim_strnsave(funcname, len);
|
name = vim_strnsave(funcname, len);
|
||||||
|
if (name == NULL) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
fname = fname_trans_sid(name, fname_buf, &tofree, &error);
|
||||||
* In a script change <SID>name() and s:name() to K_SNR 123_name().
|
|
||||||
* Change <SNR>123_name() to K_SNR 123_name().
|
|
||||||
* Use fname_buf[] when it fits, otherwise allocate memory (slow).
|
|
||||||
*/
|
|
||||||
llen = eval_fname_script(name);
|
|
||||||
if (llen > 0) {
|
|
||||||
fname_buf[0] = K_SPECIAL;
|
|
||||||
fname_buf[1] = KS_EXTRA;
|
|
||||||
fname_buf[2] = (int)KE_SNR;
|
|
||||||
int i = 3;
|
|
||||||
if (eval_fname_sid(name)) { // "<SID>" or "s:"
|
|
||||||
if (current_SID <= 0) {
|
|
||||||
error = ERROR_SCRIPT;
|
|
||||||
} else {
|
|
||||||
vim_snprintf((char *)fname_buf + 3, ARRAY_SIZE(fname_buf) - 3,
|
|
||||||
"%" PRId64 "_", (int64_t)current_SID);
|
|
||||||
i = (int)STRLEN(fname_buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i + STRLEN(name + llen) < FLEN_FIXED) {
|
|
||||||
STRCPY(fname_buf + i, name + llen);
|
|
||||||
fname = fname_buf;
|
|
||||||
} else {
|
|
||||||
fname = xmalloc(i + STRLEN(name + llen) + 1);
|
|
||||||
memmove(fname, fname_buf, (size_t)i);
|
|
||||||
STRCPY(fname + i, name + llen);
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
fname = name;
|
|
||||||
|
|
||||||
*doesrange = FALSE;
|
*doesrange = FALSE;
|
||||||
|
|
||||||
if (partial != NULL) {
|
if (partial != NULL) {
|
||||||
if (partial->pt_dict != NULL) {
|
if (partial->pt_dict != NULL) {
|
||||||
if (selfdict_in != NULL) {
|
// When the function has a partial with a dict and there is a dict
|
||||||
error = ERROR_BOTH;
|
// argument, use the dict argument. That is backwards compatible.
|
||||||
|
if (selfdict_in == NULL) {
|
||||||
|
selfdict = partial->pt_dict;
|
||||||
}
|
}
|
||||||
selfdict = partial->pt_dict;
|
selfdict = partial->pt_dict;
|
||||||
}
|
}
|
||||||
@ -7138,18 +7156,13 @@ call_func(
|
|||||||
emsg_funcname(N_("E725: Calling dict function without Dictionary: %s"),
|
emsg_funcname(N_("E725: Calling dict function without Dictionary: %s"),
|
||||||
name);
|
name);
|
||||||
break;
|
break;
|
||||||
case ERROR_BOTH:
|
|
||||||
emsg_funcname(N_(e_dict_both), name);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (argv_clear > 0) {
|
while (argv_clear > 0) {
|
||||||
clear_tv(&argv[--argv_clear]);
|
clear_tv(&argv[--argv_clear]);
|
||||||
}
|
}
|
||||||
if (fname != name && fname != fname_buf) {
|
xfree(tofree);
|
||||||
xfree(fname);
|
|
||||||
}
|
|
||||||
xfree(name);
|
xfree(name);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -9505,11 +9518,6 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
xfree(name);
|
xfree(name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (argvars[0].v_type == VAR_PARTIAL) {
|
|
||||||
EMSG2(_(e_dict_both), name);
|
|
||||||
xfree(name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (argvars[dict_idx].vval.v_dict == NULL) {
|
if (argvars[dict_idx].vval.v_dict == NULL) {
|
||||||
dict_idx = 0;
|
dict_idx = 0;
|
||||||
}
|
}
|
||||||
@ -9548,12 +9556,14 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argvars[0].v_type == VAR_PARTIAL) {
|
// For "function(dict.func, [], dict)" and "func" is a partial
|
||||||
pt->pt_dict = argvars[0].vval.v_partial->pt_dict;
|
// use "dict". That is backwards compatible.
|
||||||
(pt->pt_dict->dv_refcount)++;
|
if (dict_idx > 0) {
|
||||||
} else if (dict_idx > 0) {
|
|
||||||
pt->pt_dict = argvars[dict_idx].vval.v_dict;
|
pt->pt_dict = argvars[dict_idx].vval.v_dict;
|
||||||
(pt->pt_dict->dv_refcount)++;
|
(pt->pt_dict->dv_refcount)++;
|
||||||
|
} else if (argvars[0].v_type == VAR_PARTIAL) {
|
||||||
|
pt->pt_dict = argvars[0].vval.v_partial->pt_dict;
|
||||||
|
(pt->pt_dict->dv_refcount)++;
|
||||||
}
|
}
|
||||||
|
|
||||||
pt->pt_refcount = 1;
|
pt->pt_refcount = 1;
|
||||||
@ -18350,7 +18360,17 @@ handle_subscript (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (rettv->v_type == VAR_FUNC && selfdict != NULL) {
|
if (rettv->v_type == VAR_FUNC && selfdict != NULL) {
|
||||||
ufunc_T *fp = find_func(rettv->vval.v_string);
|
char_u *fname;
|
||||||
|
char_u *tofree = NULL;
|
||||||
|
ufunc_T *fp;
|
||||||
|
char_u fname_buf[FLEN_FIXED + 1];
|
||||||
|
int error;
|
||||||
|
|
||||||
|
// Translate "s:func" to the stored function name.
|
||||||
|
fname = fname_trans_sid(rettv->vval.v_string, fname_buf, &tofree, &error);
|
||||||
|
|
||||||
|
fp = find_func(fname);
|
||||||
|
xfree(tofree);
|
||||||
|
|
||||||
// Turn "dict.Func" into a partial for "Func" with "dict".
|
// Turn "dict.Func" into a partial for "Func" with "dict".
|
||||||
if (fp != NULL && (fp->uf_flags & FC_DICT)) {
|
if (fp != NULL && (fp->uf_flags & FC_DICT)) {
|
||||||
|
@ -69,8 +69,6 @@ func Test_partial_implicit()
|
|||||||
|
|
||||||
let Func = function(dict.MyFunc, ['bbb'])
|
let Func = function(dict.MyFunc, ['bbb'])
|
||||||
call assert_equal('foo/bbb', Func())
|
call assert_equal('foo/bbb', Func())
|
||||||
|
|
||||||
call assert_fails('call function(dict.MyFunc, ["bbb"], dict)', 'E924:')
|
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
fun InnerCall(funcref)
|
fun InnerCall(funcref)
|
||||||
@ -86,3 +84,24 @@ func Test_function_in_dict()
|
|||||||
call OuterCall()
|
call OuterCall()
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
function! s:cache_clear() dict
|
||||||
|
return self.name
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
func Test_script_function_in_dict()
|
||||||
|
let s:obj = {'name': 'foo'}
|
||||||
|
let s:obj2 = {'name': 'bar'}
|
||||||
|
|
||||||
|
let s:obj['clear'] = function('s:cache_clear')
|
||||||
|
|
||||||
|
call assert_equal('foo', s:obj.clear())
|
||||||
|
let F = s:obj.clear
|
||||||
|
call assert_equal('foo', F())
|
||||||
|
call assert_equal('foo', call(s:obj.clear, [], s:obj))
|
||||||
|
call assert_equal('bar', call(s:obj.clear, [], s:obj2))
|
||||||
|
|
||||||
|
let s:obj2['clear'] = function('s:cache_clear')
|
||||||
|
call assert_equal('bar', s:obj2.clear())
|
||||||
|
let B = s:obj2.clear
|
||||||
|
call assert_equal('bar', B())
|
||||||
|
endfunc
|
||||||
|
@ -860,7 +860,7 @@ static int included_patches[] = {
|
|||||||
// 1585,
|
// 1585,
|
||||||
// 1584 NA
|
// 1584 NA
|
||||||
// 1583 NA
|
// 1583 NA
|
||||||
// 1582,
|
1582,
|
||||||
1581,
|
1581,
|
||||||
1580,
|
1580,
|
||||||
// 1579 NA
|
// 1579 NA
|
||||||
|
Loading…
Reference in New Issue
Block a user