# HG changeset patch # User Bram Moolenaar # Date 1677009605 -3600 # Node ID ec05de98b0f7b0d02f6d7dda829dc18864ac696d # Parent 2c4d6d25953cb35e4c144d708c137d1f1ed50647 patch 9.0.1338: :defcompile and :disassemble can't find class method Commit: https://github.com/vim/vim/commit/99a7c0d89cf77c0a908b60191e63f41f04f9e793 Author: Bram Moolenaar Date: Tue Feb 21 19:55:14 2023 +0000 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 https://github.com/vim/vim/issues/11984) diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -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), + 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; + } + } + } + } + + 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; - } - break; - case ACCESS_ALL: - break; + 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; } - - 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) diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro --- a/src/proto/userfunc.pro +++ b/src/proto/userfunc.pro @@ -41,7 +41,8 @@ void user_func_error(funcerror_T error, 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); diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -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 diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -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 diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -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) @@ -4041,10 +4040,26 @@ 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, compilet } 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, compilet 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_der 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 chec 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. diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -696,6 +696,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1338, +/**/ 1337, /**/ 1336, diff --git a/src/vim.h b/src/vim.h --- a/src/vim.h +++ b/src/vim.h @@ -2722,6 +2722,7 @@ typedef char *(*opt_did_set_cb_T)(optset #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.