# HG changeset patch # User Christian Brabandt # Date 1694252708 -7200 # Node ID 3672d3d13524f0636e0c8975ddce346e2a5cea2e # Parent b57f476a1c7217380b6a9402964103a4e0a46da9 patch 9.0.1888: Vim9: Problem trying to invoke class method Commit: https://github.com/vim/vim/commit/342f4f626ed56921b1ef145ccaa05ba0a4b8ac01 Author: Yegappan Lakshmanan 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 Co-authored-by: Yegappan Lakshmanan diff --git a/src/proto/vim9class.pro b/src/proto/vim9class.pro --- 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 --- 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 + 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 --- 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 --- 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. */ diff --git a/src/vim9expr.c b/src/vim9expr.c --- 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.