Skip to content

Commit 6f2e4b3

Browse files
committed
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.
1 parent 6563903 commit 6f2e4b3

3 files changed

Lines changed: 105 additions & 69 deletions

File tree

src/eval.c

Lines changed: 82 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ static char *e_illvar = N_("E461: Illegal variable name: %s");
110110
#ifdef FEAT_FLOAT
111111
static char *e_float_as_string = N_("E806: using Float as a String");
112112
#endif
113-
static char *e_dict_both = N_("E924: can't have both a \"self\" dict and a partial: %s");
114113

115114
#define NAMESPACE_CHAR (char_u *)"abglstvw"
116115

@@ -8678,61 +8677,28 @@ get_func_tv(
86788677
return ret;
86798678
}
86808679

8681-
8682-
/*
8683-
* Call a function with its resolved parameters
8684-
* Return FAIL when the function can't be called, OK otherwise.
8685-
* Also returns OK when an error was encountered while executing the function.
8686-
*/
8687-
int
8688-
call_func(
8689-
char_u *funcname, /* name of the function */
8690-
int len, /* length of "name" */
8691-
typval_T *rettv, /* return value goes here */
8692-
int argcount_in, /* number of "argvars" */
8693-
typval_T *argvars_in, /* vars for arguments, must have "argcount"
8694-
PLUS ONE elements! */
8695-
linenr_T firstline, /* first line of range */
8696-
linenr_T lastline, /* last line of range */
8697-
int *doesrange, /* return: function handled range */
8698-
int evaluate,
8699-
partial_T *partial, /* optional, can be NULL */
8700-
dict_T *selfdict_in) /* Dictionary for "self" */
8701-
{
8702-
int ret = FAIL;
87038680
#define ERROR_UNKNOWN 0
87048681
#define ERROR_TOOMANY 1
87058682
#define ERROR_TOOFEW 2
87068683
#define ERROR_SCRIPT 3
87078684
#define ERROR_DICT 4
87088685
#define ERROR_NONE 5
87098686
#define ERROR_OTHER 6
8710-
#define ERROR_BOTH 7
8711-
int error = ERROR_NONE;
8712-
int i;
8713-
int llen;
8714-
ufunc_T *fp;
87158687
#define FLEN_FIXED 40
8716-
char_u fname_buf[FLEN_FIXED + 1];
8717-
char_u *fname;
8718-
char_u *name;
8719-
int argcount = argcount_in;
8720-
typval_T *argvars = argvars_in;
8721-
dict_T *selfdict = selfdict_in;
8722-
typval_T argv[MAX_FUNC_ARGS + 1]; /* used when "partial" is not NULL */
8723-
int argv_clear = 0;
87248688

8725-
/* Make a copy of the name, if it comes from a funcref variable it could
8726-
* be changed or deleted in the called function. */
8727-
name = vim_strnsave(funcname, len);
8728-
if (name == NULL)
8729-
return ret;
8689+
/*
8690+
* In a script change <SID>name() and s:name() to K_SNR 123_name().
8691+
* Change <SNR>123_name() to K_SNR 123_name().
8692+
* Use "fname_buf[FLEN_FIXED + 1]" when it fits, otherwise allocate memory
8693+
* (slow).
8694+
*/
8695+
static char_u *
8696+
fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error)
8697+
{
8698+
int llen;
8699+
char_u *fname;
8700+
int i;
87308701

8731-
/*
8732-
* In a script change <SID>name() and s:name() to K_SNR 123_name().
8733-
* Change <SNR>123_name() to K_SNR 123_name().
8734-
* Use fname_buf[] when it fits, otherwise allocate memory (slow).
8735-
*/
87368702
llen = eval_fname_script(name);
87378703
if (llen > 0)
87388704
{
@@ -8743,7 +8709,7 @@ call_func(
87438709
if (eval_fname_sid(name)) /* "<SID>" or "s:" */
87448710
{
87458711
if (current_SID <= 0)
8746-
error = ERROR_SCRIPT;
8712+
*error = ERROR_SCRIPT;
87478713
else
87488714
{
87498715
sprintf((char *)fname_buf + 3, "%ld_", (long)current_SID);
@@ -8759,26 +8725,73 @@ call_func(
87598725
{
87608726
fname = alloc((unsigned)(i + STRLEN(name + llen) + 1));
87618727
if (fname == NULL)
8762-
error = ERROR_OTHER;
8728+
*error = ERROR_OTHER;
87638729
else
87648730
{
8731+
*tofree = fname;
87658732
mch_memmove(fname, fname_buf, (size_t)i);
87668733
STRCPY(fname + i, name + llen);
87678734
}
87688735
}
87698736
}
87708737
else
87718738
fname = name;
8739+
return fname;
8740+
}
8741+
8742+
/*
8743+
* Call a function with its resolved parameters
8744+
* Return FAIL when the function can't be called, OK otherwise.
8745+
* Also returns OK when an error was encountered while executing the function.
8746+
*/
8747+
int
8748+
call_func(
8749+
char_u *funcname, /* name of the function */
8750+
int len, /* length of "name" */
8751+
typval_T *rettv, /* return value goes here */
8752+
int argcount_in, /* number of "argvars" */
8753+
typval_T *argvars_in, /* vars for arguments, must have "argcount"
8754+
PLUS ONE elements! */
8755+
linenr_T firstline, /* first line of range */
8756+
linenr_T lastline, /* last line of range */
8757+
int *doesrange, /* return: function handled range */
8758+
int evaluate,
8759+
partial_T *partial, /* optional, can be NULL */
8760+
dict_T *selfdict_in) /* Dictionary for "self" */
8761+
{
8762+
int ret = FAIL;
8763+
int error = ERROR_NONE;
8764+
int i;
8765+
ufunc_T *fp;
8766+
char_u fname_buf[FLEN_FIXED + 1];
8767+
char_u *tofree = NULL;
8768+
char_u *fname;
8769+
char_u *name;
8770+
int argcount = argcount_in;
8771+
typval_T *argvars = argvars_in;
8772+
dict_T *selfdict = selfdict_in;
8773+
typval_T argv[MAX_FUNC_ARGS + 1]; /* used when "partial" is not NULL */
8774+
int argv_clear = 0;
8775+
8776+
/* Make a copy of the name, if it comes from a funcref variable it could
8777+
* be changed or deleted in the called function. */
8778+
name = vim_strnsave(funcname, len);
8779+
if (name == NULL)
8780+
return ret;
8781+
8782+
fname = fname_trans_sid(name, fname_buf, &tofree, &error);
87728783

87738784
*doesrange = FALSE;
87748785

87758786
if (partial != NULL)
87768787
{
87778788
if (partial->pt_dict != NULL)
87788789
{
8779-
if (selfdict_in != NULL)
8780-
error = ERROR_BOTH;
8781-
selfdict = partial->pt_dict;
8790+
/* When the function has a partial with a dict and there is a dict
8791+
* argument, use the dict argument. That is backwards compatible.
8792+
*/
8793+
if (selfdict_in == NULL)
8794+
selfdict = partial->pt_dict;
87828795
}
87838796
if (error == ERROR_NONE && partial->pt_argc > 0)
87848797
{
@@ -8934,16 +8947,12 @@ call_func(
89348947
emsg_funcname(N_("E725: Calling dict function without Dictionary: %s"),
89358948
name);
89368949
break;
8937-
case ERROR_BOTH:
8938-
emsg_funcname(e_dict_both, name);
8939-
break;
89408950
}
89418951
}
89428952

89438953
while (argv_clear > 0)
89448954
clear_tv(&argv[--argv_clear]);
8945-
if (fname != name && fname != fname_buf)
8946-
vim_free(fname);
8955+
vim_free(tofree);
89478956
vim_free(name);
89488957

89498958
return ret;
@@ -11876,12 +11885,6 @@ f_function(typval_T *argvars, typval_T *rettv)
1187611885
vim_free(name);
1187711886
return;
1187811887
}
11879-
if (argvars[0].v_type == VAR_PARTIAL)
11880-
{
11881-
EMSG2(_(e_dict_both), name);
11882-
vim_free(name);
11883-
return;
11884-
}
1188511888
if (argvars[dict_idx].vval.v_dict == NULL)
1188611889
dict_idx = 0;
1188711890
}
@@ -11925,14 +11928,16 @@ f_function(typval_T *argvars, typval_T *rettv)
1192511928
}
1192611929
}
1192711930

11928-
if (argvars[0].v_type == VAR_PARTIAL)
11931+
/* For "function(dict.func, [], dict)" and "func" is a partial
11932+
* use "dict". That is backwards compatible. */
11933+
if (dict_idx > 0)
1192911934
{
11930-
pt->pt_dict = argvars[0].vval.v_partial->pt_dict;
11935+
pt->pt_dict = argvars[dict_idx].vval.v_dict;
1193111936
++pt->pt_dict->dv_refcount;
1193211937
}
11933-
else if (dict_idx > 0)
11938+
else if (argvars[0].v_type == VAR_PARTIAL)
1193411939
{
11935-
pt->pt_dict = argvars[dict_idx].vval.v_dict;
11940+
pt->pt_dict = argvars[0].vval.v_partial->pt_dict;
1193611941
++pt->pt_dict->dv_refcount;
1193711942
}
1193811943

@@ -21714,7 +21719,17 @@ handle_subscript(
2171421719

2171521720
if (rettv->v_type == VAR_FUNC && selfdict != NULL)
2171621721
{
21717-
ufunc_T *fp = find_func(rettv->vval.v_string);
21722+
char_u *fname;
21723+
char_u *tofree = NULL;
21724+
ufunc_T *fp;
21725+
char_u fname_buf[FLEN_FIXED + 1];
21726+
int error;
21727+
21728+
/* Translate "s:func" to the stored function name. */
21729+
fname = fname_trans_sid(rettv->vval.v_string, fname_buf,
21730+
&tofree, &error);
21731+
fp = find_func(fname);
21732+
vim_free(tofree);
2171821733

2171921734
/* Turn "dict.Func" into a partial for "Func" with "dict". */
2172021735
if (fp != NULL && (fp->uf_flags & FC_DICT))

src/testdir/test_partial.vim

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,6 @@ func Test_partial_implicit()
7070

7171
let Func = function(dict.MyFunc, ['bbb'])
7272
call assert_equal('foo/bbb', Func())
73-
74-
call assert_fails('call function(dict.MyFunc, ["bbb"], dict)', 'E924:')
7573
endfunc
7674

7775
fun InnerCall(funcref)
@@ -87,3 +85,24 @@ func Test_function_in_dict()
8785
call OuterCall()
8886
endfunc
8987

88+
function! s:cache_clear() dict
89+
return self.name
90+
endfunction
91+
92+
func Test_script_function_in_dict()
93+
let s:obj = {'name': 'foo'}
94+
let s:obj2 = {'name': 'bar'}
95+
96+
let s:obj['clear'] = function('s:cache_clear')
97+
98+
call assert_equal('foo', s:obj.clear())
99+
let F = s:obj.clear
100+
call assert_equal('foo', F())
101+
call assert_equal('foo', call(s:obj.clear, [], s:obj))
102+
call assert_equal('bar', call(s:obj.clear, [], s:obj2))
103+
104+
let s:obj2['clear'] = function('s:cache_clear')
105+
call assert_equal('bar', s:obj2.clear())
106+
let B = s:obj2.clear
107+
call assert_equal('bar', B())
108+
endfunc

src/version.c

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

749749
static int included_patches[] =
750750
{ /* Add new patch number below this line */
751+
/**/
752+
1582,
751753
/**/
752754
1581,
753755
/**/

0 commit comments

Comments
 (0)