# HG changeset patch # User Bram Moolenaar # Date 1642362302 -3600 # Node ID 04af0c68dba8542d6325fb7dea3220f596b01615 # Parent e3eab17e6c7464b3ddb5ed047aa921cc3ef95dca patch 8.2.4115: cannot use a method with a complex expression Commit: https://github.com/vim/vim/commit/c665dabdf4c49a0fbf1dc566253c75c2abe2effa Author: Bram Moolenaar Date: Sun Jan 16 19:38:07 2022 +0000 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. diff --git a/src/errors.h b/src/errors.h --- a/src/errors.h +++ b/src/errors.h @@ -3212,4 +3212,6 @@ EXTERN char e_using_autoload_in_script_n 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 --- 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; - - // value->import.func() - if (import != NULL) + typval_T ref; + + *arg = name; + *paren = NUL; + ref.v_type = VAR_UNKNOWN; + if (eval7(arg, &ref, evalarg, FALSE) == FAIL) + { + *arg = name + len; + ret = FAIL; + } + else if (*skipwhite(*arg) != NUL) { - name = NULL; - ++*arg; - fname = *arg; - len2 = get_name_len(arg, &alias, evaluate, TRUE); - if (len2 <= 0) + 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 --- 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 --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 4115, +/**/ 4114, /**/ 4113,