changeset 33227:3672d3d13524 v9.0.1888

patch 9.0.1888: Vim9: Problem trying to invoke class method Commit: https://github.com/vim/vim/commit/342f4f626ed56921b1ef145ccaa05ba0a4b8ac01 Author: Yegappan Lakshmanan <yegappan@yahoo.com> Date: Sat Sep 9 11:37:23 2023 +0200 patch 9.0.1888: Vim9: Problem trying to invoke class method Problem: Vim9: Problem trying to invoke class method Solution: Lookup the class method insider other classes closes: #13055 Signed-off-by: Christian Brabandt <cb@256bit.org> Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
author Christian Brabandt <cb@256bit.org>
date Sat, 09 Sep 2023 11:45:08 +0200
parents b57f476a1c72
children a4d08ab9add4
files src/proto/vim9class.pro src/testdir/test_vim9_class.vim src/version.c src/vim9class.c src/vim9expr.c
diffstat 5 files changed, 106 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/src/proto/vim9class.pro
+++ b/src/proto/vim9class.pro
@@ -7,6 +7,7 @@ void ex_type(exarg_T *eap);
 int class_object_index(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose);
 ufunc_T *find_class_func(char_u **arg);
 int class_member_index(char_u *name, size_t len, class_T **cl_ret, cctx_T *cctx);
+int class_method_index(char_u *name, size_t len, class_T **cl_ret, cctx_T *cctx);
 int inside_class(cctx_T *cctx_arg, class_T *cl);
 void copy_object(typval_T *from, typval_T *to);
 void object_unref(object_T *obj);
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -4248,8 +4248,6 @@ def Test_private_member_access_outside_c
     T()
   END
   v9.CheckScriptFailure(lines, 'E1333: Cannot access private member: _val')
-
-
 enddef
 
 " Test for changing the member access of an interface in a implementation class
@@ -4613,4 +4611,65 @@ def Test_abstract_method()
   v9.CheckScriptSuccess(lines)
 enddef
 
+" Test for calling a class method using an object in a def function context and
+" script context.
+def Test_class_method_call_using_object()
+  # script context
+  var lines =<< trim END
+    vim9script
+    class A
+      static def Foo(): list<string>
+        return ['a', 'b']
+      enddef
+      def Bar()
+        assert_equal(['a', 'b'], A.Foo())
+        assert_equal(['a', 'b'], Foo())
+      enddef
+    endclass
+
+    def T()
+      assert_equal(['a', 'b'], A.Foo())
+      var t_a = A.new()
+      t_a.Bar()
+    enddef
+
+    assert_equal(['a', 'b'], A.Foo())
+    var a = A.new()
+    a.Bar()
+    T()
+  END
+  v9.CheckScriptSuccess(lines)
+
+  # script context
+  lines =<< trim END
+    vim9script
+    class A
+      static def Foo(): string
+        return 'foo'
+      enddef
+    endclass
+
+    var a = A.new()
+    assert_equal('foo', a.Foo())
+  END
+  v9.CheckScriptFailure(lines, 'E1325: Method not found on class "A": Foo()')
+
+  # def function context
+  lines =<< trim END
+    vim9script
+    class A
+      static def Foo(): string
+        return 'foo'
+      enddef
+    endclass
+
+    def T()
+      var a = A.new()
+      assert_equal('foo', a.Foo())
+    enddef
+    T()
+  END
+  v9.CheckScriptFailure(lines, 'E1325: Method not found on class "A": Foo()')
+enddef
+
 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
--- a/src/version.c
+++ b/src/version.c
@@ -700,6 +700,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1888,
+/**/
     1887,
 /**/
     1886,
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -2091,6 +2091,33 @@ class_member_index(char_u *name, size_t 
 }
 
 /*
+ * If "name[len]" is a class method in cctx->ctx_ufunc->uf_class return the
+ * index in class.class_class_functions[].
+ * If "cl_ret" is not NULL set it to the class.
+ * Otherwise return -1.
+ */
+    int
+class_method_index(char_u *name, size_t len, class_T **cl_ret, cctx_T *cctx)
+{
+    if (cctx == NULL || cctx->ctx_ufunc == NULL
+					  || cctx->ctx_ufunc->uf_class == NULL)
+	return -1;
+    class_T *cl = cctx->ctx_ufunc->uf_class;
+
+    for (int i = 0; i < cl->class_class_function_count; ++i)
+    {
+	ufunc_T *fp = cl->class_class_functions[i];
+	if (STRNCMP(name, fp->uf_name, len) == 0 && fp->uf_name[len] == NUL)
+	{
+	    if (cl_ret != NULL)
+		*cl_ret = cl;
+	    return i;
+	}
+    }
+    return -1;
+}
+
+/*
  * Return TRUE if current context "cctx_arg" is inside class "cl".
  * Return FALSE if not.
  */
--- a/src/vim9expr.c
+++ b/src/vim9expr.c
@@ -775,6 +775,8 @@ compile_load(
 	    }
 	    else if ((idx = class_member_index(*arg, len, &cl, cctx)) >= 0)
 	    {
+		// Referencing a class member without the class name.  Infer
+		// the class from the def function context.
 		res = generate_CLASSMEMBER(cctx, TRUE, cl, idx);
 	    }
 	    else
@@ -1118,6 +1120,9 @@ compile_call(
     if (lookup_local(namebuf, varlen, NULL, cctx) == FAIL
 	    && arg_exists(namebuf, varlen, NULL, NULL, NULL, cctx) != OK)
     {
+	class_T		*cl = NULL;
+	int		mi = 0;
+
 	// If we can find the function by name generate the right call.
 	// Skip global functions here, a local funcref takes precedence.
 	ufunc = find_func(name, FALSE);
@@ -1136,6 +1141,16 @@ compile_call(
 		goto theend;
 	    }
 	}
+	else if ((mi = class_method_index(name, varlen, &cl, cctx)) >= 0)
+	{
+	    // Class method invocation without the class name.  The
+	    // generate_CALL() function expects the class type at the top of
+	    // the stack.  So push the class type to the stack.
+	    push_type_stack(cctx, &t_class);
+	    res = generate_CALL(cctx, cl->class_class_functions[mi], NULL, 0,
+							type, argcount);
+	    goto theend;
+	}
     }
 
     // If the name is a variable, load it and use PCALL.