]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.0.1888: Vim9: Problem trying to invoke class method v9.0.1888
authorYegappan Lakshmanan <yegappan@yahoo.com>
Sat, 9 Sep 2023 09:37:23 +0000 (11:37 +0200)
committerChristian Brabandt <cb@256bit.org>
Sat, 9 Sep 2023 09:37:23 +0000 (11:37 +0200)
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>
src/proto/vim9class.pro
src/testdir/test_vim9_class.vim
src/version.c
src/vim9class.c
src/vim9expr.c

index 0f8aa7f1fe6f2f5242e8ba6187200a26a07c8857..8413a81dd3eb6eb5dde26c0e1403e5a81bfaf57a 100644 (file)
@@ -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);
index cf6122d8583b9e241a727e43e71b35244a78c79d..be4eb2eb8a0f9cb5ba7d26d84928e20ef7a7b2cd 100644 (file)
@@ -4248,8 +4248,6 @@ def Test_private_member_access_outside_class()
     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
index b8e7879c6ee0ea6423da613f985a9bd4b0b07844..d3be87cb02a3661c1a60f023cdadc8256004da92 100644 (file)
@@ -699,6 +699,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1888,
 /**/
     1887,
 /**/
index 14d29c7dac505e0d6d1cb03f86138482fe1ace64..90cdb8ec12a0fc2c3f7fe14749c61191dc52a99b 100644 (file)
@@ -2090,6 +2090,33 @@ class_member_index(char_u *name, size_t len, class_T **cl_ret, cctx_T *cctx)
     return -1;
 }
 
+/*
+ * 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.
index 3817e38c569959167655640d2e8d8f1caa311bcd..883219ef587fae906b162cc2ccdcec23bf998ec5 100644 (file)
@@ -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.