1
0
forked from aniani/vim

patch 9.0.1338: :defcompile and :disassemble can't find class method

Problem:    :defcompile and :disassemble can't find class method. (Ernie Rael)
Solution:   Make a class name and class.method name work. (closes #11984)
This commit is contained in:
Bram Moolenaar 2023-02-21 19:55:14 +00:00
parent cfce5cf542
commit 99a7c0d89c
7 changed files with 149 additions and 56 deletions

View File

@ -1529,45 +1529,81 @@ get_lval(
if (cl != NULL)
{
lp->ll_valtype = NULL;
int count = v_type == VAR_OBJECT ? cl->class_obj_member_count
: cl->class_class_member_count;
ocmember_T *members = v_type == VAR_OBJECT
? cl->class_obj_members
: cl->class_class_members;
for (int i = 0; i < count; ++i)
if (flags & GLV_PREFER_FUNC)
{
ocmember_T *om = members + i;
if (STRNCMP(om->ocm_name, key, p - key) == 0
&& om->ocm_name[p - key] == NUL)
// First look for a function with this name.
// round 1: class functions (skipped for an object)
// round 2: object methods
for (int round = v_type == VAR_OBJECT ? 2 : 1;
round <= 2; ++round)
{
switch (om->ocm_access)
int count = round == 1
? cl->class_class_function_count
: cl->class_obj_method_count;
ufunc_T **funcs = round == 1
? cl->class_class_functions
: cl->class_obj_methods;
for (int i = 0; i < count; ++i)
{
case ACCESS_PRIVATE:
semsg(_(e_cannot_access_private_member_str),
om->ocm_name);
return NULL;
case ACCESS_READ:
if (!(flags & GLV_READ_ONLY))
{
semsg(_(e_member_is_not_writable_str),
om->ocm_name);
return NULL;
}
break;
case ACCESS_ALL:
break;
ufunc_T *fp = funcs[i];
char_u *ufname = (char_u *)fp->uf_name;
if (STRNCMP(ufname, key, p - key) == 0
&& ufname[p - key] == NUL)
{
lp->ll_ufunc = fp;
lp->ll_valtype = fp->uf_func_type;
round = 3;
break;
}
}
lp->ll_valtype = om->ocm_type;
if (v_type == VAR_OBJECT)
lp->ll_tv = ((typval_T *)(
lp->ll_tv->vval.v_object + 1)) + i;
else
lp->ll_tv = &cl->class_members_tv[i];
break;
}
}
if (lp->ll_valtype == NULL)
{
int count = v_type == VAR_OBJECT
? cl->class_obj_member_count
: cl->class_class_member_count;
ocmember_T *members = v_type == VAR_OBJECT
? cl->class_obj_members
: cl->class_class_members;
for (int i = 0; i < count; ++i)
{
ocmember_T *om = members + i;
if (STRNCMP(om->ocm_name, key, p - key) == 0
&& om->ocm_name[p - key] == NUL)
{
switch (om->ocm_access)
{
case ACCESS_PRIVATE:
semsg(_(e_cannot_access_private_member_str),
om->ocm_name);
return NULL;
case ACCESS_READ:
if ((flags & GLV_READ_ONLY) == 0)
{
semsg(_(e_member_is_not_writable_str),
om->ocm_name);
return NULL;
}
break;
case ACCESS_ALL:
break;
}
lp->ll_valtype = om->ocm_type;
if (v_type == VAR_OBJECT)
lp->ll_tv = ((typval_T *)(
lp->ll_tv->vval.v_object + 1)) + i;
else
lp->ll_tv = &cl->class_members_tv[i];
break;
}
}
}
if (lp->ll_valtype == NULL)
{
if (v_type == VAR_OBJECT)

View File

@ -41,7 +41,8 @@ void user_func_error(funcerror_T error, char_u *name, int found_var);
int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typval_T *argvars_in, funcexe_T *funcexe);
int call_simple_func(char_u *funcname, int len, typval_T *rettv);
char_u *printable_func_name(ufunc_T *fp);
char_u *trans_function_name(char_u **pp, int *is_global, int skip, int flags, funcdict_T *fdp, partial_T **partial, type_T **type);
char_u *trans_function_name(char_u **pp, int *is_global, int skip, int flags);
char_u *trans_function_name_ext(char_u **pp, int *is_global, int skip, int flags, funcdict_T *fdp, partial_T **partial, type_T **type, ufunc_T **ufunc);
char_u *get_scriptlocal_funcname(char_u *funcname);
char_u *alloc_printable_func_name(char_u *fname);
char_u *save_function_name(char_u **name, int *is_global, int skip, int flags, funcdict_T *fudi);

View File

@ -4521,6 +4521,7 @@ typedef struct lval_S
char_u *ll_newkey; // New key for Dict in alloc. mem or NULL.
type_T *ll_valtype; // type expected for the value or NULL
blob_T *ll_blob; // The Blob or NULL
ufunc_T *ll_ufunc; // The function or NULL
} lval_T;
// Structure used to save the current state. Used when executing Normal mode

View File

@ -842,6 +842,34 @@ def Test_class_function()
v9.CheckScriptSuccess(lines)
enddef
def Test_class_defcompile()
var lines =<< trim END
vim9script
class C
def Fo(i: number): string
return i
enddef
endclass
defcompile C.Fo
END
v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected string but got number')
lines =<< trim END
vim9script
class C
static def Fc(): number
return 'x'
enddef
endclass
defcompile C.Fc
END
v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected number but got string')
enddef
def Test_class_object_to_string()
var lines =<< trim END
vim9script

View File

@ -1037,8 +1037,7 @@ get_function_body(
if (*p == '!')
p = skipwhite(p + 1);
p += eval_fname_script(p);
vim_free(trans_function_name(&p, NULL, TRUE, 0, NULL,
NULL, NULL));
vim_free(trans_function_name(&p, NULL, TRUE, 0));
if (*skipwhite(p) == '(')
{
if (nesting == MAX_FUNC_NESTING - 1)
@ -4038,13 +4037,29 @@ list_func_head(ufunc_T *fp, int indent)
*/
char_u *
trans_function_name(
char_u **pp,
int *is_global,
int skip, // only find the end, don't evaluate
int flags)
{
return trans_function_name_ext(pp, is_global, skip, flags,
NULL, NULL, NULL, NULL);
}
/*
* trans_function_name() with extra arguments.
* "fdp", "partial", "type" and "ufunc" can be NULL.
*/
char_u *
trans_function_name_ext(
char_u **pp,
int *is_global,
int skip, // only find the end, don't evaluate
int flags,
funcdict_T *fdp, // return: info about dictionary used
partial_T **partial, // return: partial of a FuncRef
type_T **type) // return: type of funcref if not NULL
type_T **type, // return: type of funcref
ufunc_T **ufunc) // return: function
{
char_u *name = NULL;
char_u *start;
@ -4079,7 +4094,8 @@ trans_function_name(
start += lead;
// Note that TFN_ flags use the same values as GLV_ flags.
end = get_lval(start, NULL, &lv, FALSE, skip, flags | GLV_READ_ONLY,
end = get_lval(start, NULL, &lv, FALSE, skip,
flags | GLV_READ_ONLY | GLV_PREFER_FUNC,
lead > 2 ? 0 : FNE_CHECK_START);
if (end == start || (vim9script && end != NULL
&& end[-1] == AUTOLOAD_CHAR && *end == '('))
@ -4105,6 +4121,13 @@ trans_function_name(
goto theend;
}
if (lv.ll_ufunc != NULL)
{
*ufunc = lv.ll_ufunc;
name = vim_strsave(lv.ll_ufunc->uf_name);
goto theend;
}
if (lv.ll_tv != NULL)
{
if (fdp != NULL)
@ -4455,8 +4478,8 @@ save_function_name(
CLEAR_POINTER(fudi);
}
else
saved = trans_function_name(&p, is_global, skip,
flags, fudi, NULL, NULL);
saved = trans_function_name_ext(&p, is_global, skip,
flags, fudi, NULL, NULL, NULL);
*name = p;
return saved;
}
@ -5330,15 +5353,20 @@ find_func_by_name(char_u *name, compiletype_T *compile_type)
}
else
{
// First try finding a method in a class, find_func_by_name() will give
// an error if the function is not found.
// First try finding a method in a class, trans_function_name() will
// give an error if the function is not found.
ufunc = find_class_func(&arg);
if (ufunc != NULL)
return ufunc;
fname = trans_function_name(&arg, &is_global, FALSE,
fname = trans_function_name_ext(&arg, &is_global, FALSE,
TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DECL,
NULL, NULL, NULL);
NULL, NULL, NULL, &ufunc);
if (ufunc != NULL)
{
vim_free(fname);
return ufunc;
}
}
if (fname == NULL)
{
@ -5375,13 +5403,10 @@ find_func_by_name(char_u *name, compiletype_T *compile_type)
void
ex_defcompile(exarg_T *eap)
{
ufunc_T *ufunc;
if (*eap->arg != NUL)
{
compiletype_T compile_type = CT_NONE;
ufunc = find_func_by_name(eap->arg, &compile_type);
ufunc_T *ufunc = find_func_by_name(eap->arg, &compile_type);
if (ufunc != NULL)
{
if (func_needs_compiling(ufunc, compile_type))
@ -5401,7 +5426,7 @@ ex_defcompile(exarg_T *eap)
if (!HASHITEM_EMPTY(hi))
{
--todo;
ufunc = HI2UF(hi);
ufunc_T *ufunc = HI2UF(hi);
if (ufunc->uf_script_ctx.sc_sid == current_sctx.sc_sid
&& ufunc->uf_def_status == UF_TO_BE_COMPILED
&& (ufunc->uf_flags & FC_DEAD) == 0)
@ -5475,7 +5500,7 @@ function_exists(char_u *name, int no_deref)
flag = TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD;
if (no_deref)
flag |= TFN_NO_DEREF;
p = trans_function_name(&nm, &is_global, FALSE, flag, NULL, NULL, NULL);
p = trans_function_name(&nm, &is_global, FALSE, flag);
nm = skipwhite(nm);
// Only accept "funcname", "funcname ", "funcname (..." and
@ -5494,8 +5519,7 @@ get_expanded_name(char_u *name, int check)
char_u *p;
int is_global = FALSE;
p = trans_function_name(&nm, &is_global, FALSE,
TFN_INT|TFN_QUIET, NULL, NULL, NULL);
p = trans_function_name(&nm, &is_global, FALSE, TFN_INT|TFN_QUIET);
if (p != NULL && *nm == NUL
&& (!check || translated_function_exists(p, is_global)))
@ -5631,8 +5655,8 @@ ex_delfunction(exarg_T *eap)
int is_global = FALSE;
p = eap->arg;
name = trans_function_name(&p, &is_global, eap->skip, 0, &fudi,
NULL, NULL);
name = trans_function_name_ext(&p, &is_global, eap->skip, 0, &fudi,
NULL, NULL, NULL);
vim_free(fudi.fd_newkey);
if (name == NULL)
{
@ -6166,8 +6190,8 @@ ex_call(exarg_T *eap)
return;
}
tofree = trans_function_name(&arg, NULL, eap->skip, TFN_INT,
&fudi, &partial, vim9script ? &type : NULL);
tofree = trans_function_name_ext(&arg, NULL, eap->skip, TFN_INT,
&fudi, &partial, vim9script ? &type : NULL, NULL);
if (fudi.fd_newkey != NULL)
{
// Still need to give an error message for missing key.

View File

@ -695,6 +695,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1338,
/**/
1337,
/**/

View File

@ -2722,6 +2722,7 @@ typedef char *(*opt_did_set_cb_T)(optset_T *args);
#define GLV_NO_DECL TFN_NO_DECL // assignment without :var or :let
#define GLV_COMPILING TFN_COMPILING // variable may be defined later
#define GLV_ASSIGN_WITH_OP TFN_ASSIGN_WITH_OP // assignment with operator
#define GLV_PREFER_FUNC 0x10000 // prefer function above variable
#define DO_NOT_FREE_CNT 99999 // refcount for dict or list that should not
// be freed.