summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYegappan Lakshmanan <yegappan@yahoo.com>2023-09-09 11:37:23 +0200
committerChristian Brabandt <cb@256bit.org>2023-09-09 11:37:23 +0200
commit342f4f626ed56921b1ef145ccaa05ba0a4b8ac01 (patch)
tree5441a7bb6c17a6ea0298f1dfde9f6069527d9ecb
parent23c92d93c1b877edf18881b715ad51ec26386c2e (diff)
patch 9.0.1888: Vim9: Problem trying to invoke class methodv9.0.1888
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>
-rw-r--r--src/proto/vim9class.pro1
-rw-r--r--src/testdir/test_vim9_class.vim63
-rw-r--r--src/version.c2
-rw-r--r--src/vim9class.c27
-rw-r--r--src/vim9expr.c15
5 files changed, 106 insertions, 2 deletions
diff --git a/src/proto/vim9class.pro b/src/proto/vim9class.pro
index 0f8aa7f1fe..8413a81dd3 100644
--- 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);
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
index cf6122d858..be4eb2eb8a 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -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
diff --git a/src/version.c b/src/version.c
index b8e7879c6e..d3be87cb02 100644
--- 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,
diff --git a/src/vim9class.c b/src/vim9class.c
index 14d29c7dac..90cdb8ec12 100644
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -2091,6 +2091,33 @@ class_member_index(char_u *name, size_t len, class_T **cl_ret, cctx_T *cctx)
}
/*
+ * 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.
*/
diff --git a/src/vim9expr.c b/src/vim9expr.c
index 3817e38c56..883219ef58 100644
--- 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.