0
0
mirror of https://github.com/vim/vim.git synced 2025-09-24 03:44:06 -04:00

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) if (cl != NULL)
{ {
lp->ll_valtype = NULL; lp->ll_valtype = NULL;
int count = v_type == VAR_OBJECT ? cl->class_obj_member_count
: cl->class_class_member_count; if (flags & GLV_PREFER_FUNC)
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; // First look for a function with this name.
if (STRNCMP(om->ocm_name, key, p - key) == 0 // round 1: class functions (skipped for an object)
&& om->ocm_name[p - key] == NUL) // 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: ufunc_T *fp = funcs[i];
semsg(_(e_cannot_access_private_member_str), char_u *ufname = (char_u *)fp->uf_name;
om->ocm_name); if (STRNCMP(ufname, key, p - key) == 0
return NULL; && ufname[p - key] == NUL)
case ACCESS_READ: {
if (!(flags & GLV_READ_ONLY)) lp->ll_ufunc = fp;
{ lp->ll_valtype = fp->uf_func_type;
semsg(_(e_member_is_not_writable_str), round = 3;
om->ocm_name); break;
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)
{
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 (lp->ll_valtype == NULL)
{ {
if (v_type == VAR_OBJECT) 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_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); int call_simple_func(char_u *funcname, int len, typval_T *rettv);
char_u *printable_func_name(ufunc_T *fp); 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 *get_scriptlocal_funcname(char_u *funcname);
char_u *alloc_printable_func_name(char_u *fname); 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); 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. char_u *ll_newkey; // New key for Dict in alloc. mem or NULL.
type_T *ll_valtype; // type expected for the value or NULL type_T *ll_valtype; // type expected for the value or NULL
blob_T *ll_blob; // The Blob or NULL blob_T *ll_blob; // The Blob or NULL
ufunc_T *ll_ufunc; // The function or NULL
} lval_T; } lval_T;
// Structure used to save the current state. Used when executing Normal mode // 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) v9.CheckScriptSuccess(lines)
enddef 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() def Test_class_object_to_string()
var lines =<< trim END var lines =<< trim END
vim9script vim9script

View File

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

View File

@@ -695,6 +695,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 */
/**/
1338,
/**/ /**/
1337, 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_NO_DECL TFN_NO_DECL // assignment without :var or :let
#define GLV_COMPILING TFN_COMPILING // variable may be defined later #define GLV_COMPILING TFN_COMPILING // variable may be defined later
#define GLV_ASSIGN_WITH_OP TFN_ASSIGN_WITH_OP // assignment with operator #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 #define DO_NOT_FREE_CNT 99999 // refcount for dict or list that should not
// be freed. // be freed.