From c665dabdf4c49a0fbf1dc566253c75c2abe2effa Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 16 Jan 2022 19:38:07 +0000 Subject: patch 8.2.4115: cannot use a method with a complex expression Problem: Cannot use a method with a complex expression. Solution: Evaluate the expression after "->" and use the result. --- src/errors.h | 2 + src/eval.c | 101 +++++++++++++++++++++-------------------- src/testdir/test_vim9_expr.vim | 23 +++++++++- src/version.c | 2 + 4 files changed, 78 insertions(+), 50 deletions(-) diff --git a/src/errors.h b/src/errors.h index 5e9b6af5d..e576f810c 100644 --- a/src/errors.h +++ b/src/errors.h @@ -3212,4 +3212,6 @@ EXTERN char e_using_autoload_in_script_not_under_autoload_directory[] INIT(= N_("E1263: Using autoload in a script not under an autoload directory")); EXTERN char e_autoload_import_cannot_use_absolute_or_relative_path[] INIT(= N_("E1264: Autoload import cannot use absolute or relative path: %s")); +EXTERN char e_cannot_use_partial_here[] + INIT(= N_("E1265: Cannot use a partial here")); #endif diff --git a/src/eval.c b/src/eval.c index 935f19aa7..c8907defd 100644 --- a/src/eval.c +++ b/src/eval.c @@ -3948,6 +3948,7 @@ eval_method( char_u *name; long len; char_u *alias; + char_u *tofree = NULL; typval_T base = *rettv; int ret = OK; int evaluate = evalarg != NULL @@ -3968,67 +3969,68 @@ eval_method( } else { - if (**arg == '.') + char_u *paren; + + // If there is no "(" immediately following, but there is further on, + // it can be "import.Func()", "dict.Func()", "list[nr]", etc. + // Does not handle anything where "(" is part of the expression. + *arg = skipwhite(*arg); + + if (**arg != '(' && alias == NULL + && (paren = vim_strchr(*arg, '(')) != NULL) { - int len2; - char_u *fname; - int idx; - imported_T *import = find_imported(name, len, TRUE, - evalarg == NULL ? NULL : evalarg->eval_cctx); - type_T *type; + typval_T ref; - // value->import.func() - if (import != NULL) + *arg = name; + *paren = NUL; + ref.v_type = VAR_UNKNOWN; + if (eval7(arg, &ref, evalarg, FALSE) == FAIL) { - name = NULL; - ++*arg; - fname = *arg; - len2 = get_name_len(arg, &alias, evaluate, TRUE); - if (len2 <= 0) + *arg = name + len; + ret = FAIL; + } + else if (*skipwhite(*arg) != NUL) + { + if (verbose) + semsg(_(e_trailing_characters_str), *arg); + ret = FAIL; + } + else if (ref.v_type == VAR_FUNC && ref.vval.v_string != NULL) + { + name = ref.vval.v_string; + ref.vval.v_string = NULL; + tofree = name; + len = STRLEN(name); + } + else if (ref.v_type == VAR_PARTIAL && ref.vval.v_partial != NULL) + { + if (ref.vval.v_partial->pt_argc > 0 + || ref.vval.v_partial->pt_dict != NULL) { - if (verbose) - emsg(_(e_missing_name_after_dot)); + emsg(_(e_cannot_use_partial_here)); ret = FAIL; } - else if (evaluate) + else { - int cc = fname[len2]; - ufunc_T *ufunc; - - fname[len2] = NUL; - idx = find_exported(import->imp_sid, fname, &ufunc, &type, - evalarg->eval_cctx, verbose); - fname[len2] = cc; - - if (idx >= 0) + name = vim_strsave(partial_name(ref.vval.v_partial)); + tofree = name; + if (name == NULL) { - scriptitem_T *si = SCRIPT_ITEM(import->imp_sid); - svar_T *sv = - ((svar_T *)si->sn_var_vals.ga_data) + idx; - - if (sv->sv_tv->v_type == VAR_FUNC - && sv->sv_tv->vval.v_string != NULL) - { - name = sv->sv_tv->vval.v_string; - len = STRLEN(name); - } - else - { - // TODO: how about a partial? - if (verbose) - semsg(_(e_not_callable_type_str), fname); - ret = FAIL; - } - } - else if (ufunc != NULL) - { - name = ufunc->uf_name; - len = STRLEN(name); + ret = FAIL; + name = *arg; } else - ret = FAIL; + len = STRLEN(name); } } + else + { + if (verbose) + semsg(_(e_not_callable_type_str), name); + ret = FAIL; + } + clear_tv(&ref); + *paren = '('; } if (ret == OK) @@ -4057,6 +4059,7 @@ eval_method( // evaluating the arguments is possible (see test55). if (evaluate) clear_tv(&base); + vim_free(tofree); return ret; } diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index e1b48d7db..263b6d310 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -3136,16 +3136,37 @@ def Test_expr7_method_call() var sorted = [3, 1, 2] -> sort() assert_equal([1, 2, 3], sorted) + END + CheckDefAndScriptSuccess(lines) + lines =<< trim END + vim9script def SetNumber(n: number) g:number = n enddef const Setit = SetNumber len('text')->Setit() assert_equal(4, g:number) + + const SetFuncref = funcref(SetNumber) + len('longer')->SetFuncref() + assert_equal(6, g:number) + + const SetList = [SetNumber, SetFuncref] + len('xx')->SetList[0]() + assert_equal(2, g:number) + len('xxx')->SetList[1]() + assert_equal(3, g:number) + + const SetDict = {key: SetNumber} + len('xxxx')->SetDict['key']() + assert_equal(4, g:number) + len('xxxxx')->SetDict.key() + assert_equal(5, g:number) + unlet g:number END - CheckDefAndScriptSuccess(lines) + CheckScriptSuccess(lines) # TODO: CheckDefAndScriptSuccess() lines =<< trim END def RetVoid() diff --git a/src/version.c b/src/version.c index d75555958..83b979f6c 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4115, /**/ 4114, /**/ -- cgit v1.2.1