changeset 27173:04af0c68dba8 v8.2.4115

patch 8.2.4115: cannot use a method with a complex expression Commit: https://github.com/vim/vim/commit/c665dabdf4c49a0fbf1dc566253c75c2abe2effa Author: Bram Moolenaar <Bram@vim.org> 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.
author Bram Moolenaar <Bram@vim.org>
date Sun, 16 Jan 2022 20:45:02 +0100
parents e3eab17e6c74
children 10acb2602253
files src/errors.h src/eval.c src/testdir/test_vim9_expr.vim src/version.c
diffstat 4 files changed, 80 insertions(+), 52 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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;
 }
--- 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()
--- 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,