Mercurial > vim
changeset 33204:36aebbf8389f v9.0.1880
patch 9.0.1880: Vim9: Need more tests for inheritance
Commit: https://github.com/vim/vim/commit/cf138d4ea5b7e4495abbc867f9bb8a6be6482762
Author: Ernie Rael <errael@raelity.com>
Date: Wed Sep 6 20:45:03 2023 +0200
patch 9.0.1880: Vim9: Need more tests for inheritance
Problem: Vim9: Need more tests for inheritance
Solution: Add access tests and fixes.
`inside_class` fix from yegappan. `object_index_from_itf_index` fix
access of member on class extending class implementing interface.
Based on tests from Vim9: Class/Object member variable access control #12979
closes: #13032
related: #12979
Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Ernie Rael <errael@raelity.com>
Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Wed, 06 Sep 2023 21:00:04 +0200 |
parents | c3c347a71e05 |
children | 537c616434a8 |
files | src/testdir/test_vim9_class.vim src/version.c src/vim9class.c |
diffstat | 3 files changed, 281 insertions(+), 5 deletions(-) [+] |
line wrap: on
line diff
--- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -933,6 +933,50 @@ def Test_class_object_member_access() endclass END v9.CheckScriptFailure(lines, 'E1368: Static cannot be followed by "this" in a member name') + + # Access from child class extending a class: + lines =<< trim END + vim9script + class A + this.ro_obj_var = 10 + public this.rw_obj_var = 20 + this._priv_obj_var = 30 + + static ro_class_var = 40 + public static rw_class_var = 50 + static _priv_class_var = 60 + endclass + + class B extends A + def Foo() + var x: number + x = this.ro_obj_var + this.ro_obj_var = 0 + x = this.rw_obj_var + this.rw_obj_var = 0 + x = this._priv_obj_var + this._priv_obj_var = 0 + + x = ro_class_var + ro_class_var = 0 + x = rw_class_var + rw_class_var = 0 + x = _priv_class_var + _priv_class_var = 0 + + x = A.ro_class_var + A.ro_class_var = 0 + x = A.rw_class_var + A.rw_class_var = 0 + x = A._priv_class_var + A._priv_class_var = 0 + enddef + endclass + + var b = B.new() + b.Foo() + END + v9.CheckScriptSuccess(lines) enddef def Test_class_object_compare() @@ -1867,6 +1911,171 @@ def Test_class_implements_interface() endclass END v9.CheckScriptFailure(lines, 'E1407: Member "IsEven": type mismatch, expected func(number): bool but got func(number, ...list<number>): bool') + + # access superclass interface members from subclass, mix variable order + lines =<< trim END + vim9script + + interface I1 + public static svar1: number + public static svar2: number + public this.mvar1: number + public this.mvar2: number + endinterface + + # NOTE: the order is swapped + class A implements I1 + public this.mvar2: number + public this.mvar1: number + public static svar2: number + public static svar1: number + def new() + svar1 = 11 + svar2 = 12 + this.mvar1 = 111 + this.mvar2 = 112 + enddef + endclass + + class B extends A + def new() + svar1 = 21 + svar2 = 22 + this.mvar1 = 121 + this.mvar2 = 122 + enddef + endclass + + class C extends B + def new() + svar1 = 31 + svar2 = 32 + this.mvar1 = 131 + this.mvar2 = 132 + enddef + endclass + + def F1(i: I1): list<number> + return [ i.svar1, i.svar2 ] + enddef + + def F2(i: I1): list<number> + return [ i.mvar1, i.mvar2 ] + enddef + + var oa = A.new() + var ob = B.new() + var oc = C.new() + + assert_equal([11, 12], F1(oa)) + assert_equal([21, 22], F1(ob)) + assert_equal([31, 32], F1(oc)) + + assert_equal([111, 112], F2(oa)) + assert_equal([121, 122], F2(ob)) + assert_equal([131, 132], F2(oc)) + END + v9.CheckScriptSuccess(lines) + + # Access superclass interface members from subclass, mix variable order. + # Two interfaces, one on A, one on B; each has both kinds of variables + lines =<< trim END + vim9script + + interface I1 + public static svar1: number + public static svar2: number + public this.mvar1: number + public this.mvar2: number + endinterface + + interface I2 + public static svar3: number + public static svar4: number + public this.mvar3: number + public this.mvar4: number + endinterface + + class A implements I1 + public static svar1: number + public static svar2: number + public this.mvar1: number + public this.mvar2: number + def new() + svar1 = 11 + svar2 = 12 + this.mvar1 = 111 + this.mvar2 = 112 + enddef + endclass + + class B extends A implements I2 + public static svar3: number + public static svar4: number + public this.mvar3: number + public this.mvar4: number + def new() + svar1 = 21 + svar2 = 22 + svar3 = 23 + svar4 = 24 + this.mvar1 = 121 + this.mvar2 = 122 + this.mvar3 = 123 + this.mvar4 = 124 + enddef + endclass + + class C extends B + public static svar5: number + def new() + svar1 = 31 + svar2 = 32 + svar3 = 33 + svar4 = 34 + svar5 = 1001 + this.mvar1 = 131 + this.mvar2 = 132 + this.mvar3 = 133 + this.mvar4 = 134 + enddef + endclass + + def F1(i: I1): list<number> + return [ i.svar1, i.svar2 ] + enddef + + def F2(i: I1): list<number> + return [ i.mvar1, i.mvar2 ] + enddef + + def F3(i: I2): list<number> + return [ i.svar3, i.svar4 ] + enddef + + def F4(i: I2): list<number> + return [ i.mvar3, i.mvar4 ] + enddef + + def F5(o: C): number + return o.svar5 + enddef + + var oa = A.new() + var ob = B.new() + var oc = C.new() + + assert_equal([[11, 12]], [F1(oa)]) + assert_equal([[21, 22], [23, 24]], [F1(ob), F3(ob)]) + assert_equal([[31, 32], [33, 34]], [F1(oc), F3(oc)]) + + assert_equal([[111, 112]], [F2(oa)]) + assert_equal([[121, 122], [123, 124]], [F2(ob), F4(ob)]) + assert_equal([[131, 132], [133, 134]], [F2(oc), F4(oc)]) + + assert_equal(1001, F5(oc)) + END + v9.CheckScriptSuccess(lines) enddef def Test_call_interface_method() @@ -3592,6 +3801,64 @@ def Test_objmethod_funcarg() v9.CheckScriptSuccess(lines) enddef +def Test_static_inheritence() + # subclasses get their own static copy + var lines =<< trim END + vim9script + + class A + static _svar: number + this._mvar: number + def new() + _svar = 1 + this._mvar = 101 + enddef + def AccessObject(): number + return this._mvar + enddef + def AccessStaticThroughObject(): number + return _svar + enddef + endclass + + class B extends A + def new() + _svar = 2 + this._mvar = 102 + enddef + endclass + + class C extends B + def new() + _svar = 3 + this._mvar = 103 + enddef + + def AccessPrivateStaticThroughClassName(): number + assert_equal(1, A._svar) + assert_equal(2, B._svar) + assert_equal(3, C._svar) + return 444 + enddef + endclass + + var oa = A.new() + var ob = B.new() + var oc = C.new() + assert_equal(101, oa.AccessObject()) + assert_equal(102, ob.AccessObject()) + assert_equal(103, oc.AccessObject()) + + assert_equal(444, oc.AccessPrivateStaticThroughClassName()) + + # verify object properly resolves to correct static + assert_equal(1, oa.AccessStaticThroughObject()) + assert_equal(2, ob.AccessStaticThroughObject()) + assert_equal(3, oc.AccessStaticThroughObject()) + END + v9.CheckScriptSuccess(lines) +enddef + " Test for declaring duplicate object and class members def Test_dup_member_variable() # Duplicate member variable
--- 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 */ /**/ + 1880, +/**/ 1879, /**/ 1878,
--- a/src/vim9class.c +++ b/src/vim9class.c @@ -237,10 +237,16 @@ object_index_from_itf_index(class_T *itf if (cl == itf) return idx; - itf2class_T *i2c; - for (i2c = itf->class_itf2class; i2c != NULL; i2c = i2c->i2c_next) - if (i2c->i2c_class == cl && i2c->i2c_is_method == is_method) - break; + itf2class_T *i2c = NULL; + int searching = TRUE; + for (class_T *super = cl; super != NULL && searching; + super = super->class_extends) + for (i2c = itf->class_itf2class; i2c != NULL; i2c = i2c->i2c_next) + if (i2c->i2c_class == super && i2c->i2c_is_method == is_method) + { + searching = FALSE; + break; + } if (i2c == NULL) { siemsg("class %s not found on interface %s", @@ -1978,7 +1984,8 @@ class_member_index(char_u *name, size_t inside_class(cctx_T *cctx_arg, class_T *cl) { for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer) - if (cctx->ctx_ufunc != NULL && cctx->ctx_ufunc->uf_class == cl) + if (cctx->ctx_ufunc != NULL + && class_instance_of(cctx->ctx_ufunc->uf_class, cl)) return TRUE; return FALSE; }