From: Yegappan Lakshmanan Date: Sun, 28 Dec 2025 14:08:21 +0000 (+0000) Subject: patch 9.1.2029: tests: the test_vim9_class.vim testfile is too long X-Git-Tag: v9.1.2029^0 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1dd301a4b36bca40ef5044983382642f68f478bd;p=thirdparty%2Fvim.git patch 9.1.2029: tests: the test_vim9_class.vim testfile is too long Problem: tests: the test_vim9_class.vim testfile is too long Solution: Split out the interface related test cases into a new test file test_vim9_interface.vim (Yegappan Lakshmanan) closes: #19032 Signed-off-by: Yegappan Lakshmanan Signed-off-by: Christian Brabandt --- diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak index 9c0d11585a..31f66d7baf 100644 --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -50,6 +50,7 @@ TEST_VIM9 = \ test_vim9_func \ test_vim9_generics \ test_vim9_import \ + test_vim9_interface \ test_vim9_python3 \ test_vim9_script \ test_vim9_typealias @@ -66,6 +67,7 @@ TEST_VIM9_RES = \ test_vim9_func.res \ test_vim9_generics.res \ test_vim9_import.res \ + test_vim9_interface.res \ test_vim9_python3.res \ test_vim9_script.res \ test_vim9_typealias.res diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim index 69fad33813..7f0326e4a9 100644 --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -568,24 +568,6 @@ def Test_using_null_class() v9.CheckSourceSuccess(lines) enddef -def Test_class_interface_wrong_end() - var lines =<< trim END - vim9script - abstract class SomeName - var member = 'text' - endinterface - END - v9.CheckSourceFailure(lines, 'E476: Invalid command: endinterface, expected endclass', 4) - - lines =<< trim END - vim9script - export interface AnotherName - var member: string - endclass - END - v9.CheckSourceFailure(lines, 'E476: Invalid command: endclass, expected endinterface', 4) -enddef - def Test_object_not_set() # Use an uninitialized object in script context var lines =<< trim END @@ -2103,51 +2085,6 @@ func Test_class_garbagecollect() call v9.CheckSourceSuccess(lines) endfunc -" Test interface garbage collection -func Test_interface_garbagecollect() - let lines =<< trim END - vim9script - - interface I - var ro_obj_var: number - - def ObjFoo(): number - endinterface - - class A implements I - static var ro_class_var: number = 10 - public static var rw_class_var: number = 20 - static var _priv_class_var: number = 30 - var ro_obj_var: number = 40 - var _priv_obj_var: number = 60 - - static def _ClassBar(): number - return _priv_class_var - enddef - - static def ClassFoo(): number - return ro_class_var + rw_class_var + A._ClassBar() - enddef - - def _ObjBar(): number - return this._priv_obj_var - enddef - - def ObjFoo(): number - return this.ro_obj_var + this._ObjBar() - enddef - endclass - - assert_equal(60, A.ClassFoo()) - var o = A.new() - assert_equal(100, o.ObjFoo()) - test_garbagecollect_now() - assert_equal(60, A.ClassFoo()) - assert_equal(100, o.ObjFoo()) - END - call v9.CheckSourceSuccess(lines) -endfunc - def Test_class_method() var lines =<< trim END vim9script @@ -2340,5327 +2277,3951 @@ def Test_class_object_to_string() v9.CheckSourceSuccess(lines) enddef -def Test_interface_basics() +def Test_class_used_as_type() var lines =<< trim END vim9script - interface Something - var ro_var: list - def GetCount(): number - endinterface - END - v9.CheckSourceSuccess(lines) - - lines =<< trim END - interface SomethingWrong - static var count = 7 - endinterface - END - v9.CheckSourceFailure(lines, 'E1342: Interface can only be defined in Vim9 script', 1) - lines =<< trim END - vim9script + class Point + var x = 0 + var y = 0 + endclass - interface Some - var value: number - def Method(value: number) - endinterface + var p: Point + p = Point.new(2, 33) + assert_equal(2, p.x) + assert_equal(33, p.y) END - # The argument name and the object member name are the same, but this is not a - # problem because object members are always accessed with the "this." prefix. v9.CheckSourceSuccess(lines) lines =<< trim END vim9script - interface somethingWrong - static var count = 7 - endinterface - END - v9.CheckSourceFailure(lines, 'E1343: Interface name must start with an uppercase letter: somethingWrong', 2) - - lines =<< trim END - vim9script - interface SomethingWrong - var value: string - var count = 7 - def GetCount(): number - endinterface - END - v9.CheckSourceFailure(lines, 'E1344: Cannot initialize a variable in an interface', 4) - lines =<< trim END - vim9script - interface SomethingWrong - var value: string - var count: number - def GetCount(): number - return 5 - enddef + interface HasX + var x: number endinterface - END - v9.CheckSourceFailure(lines, 'E1345: Not a valid command in an interface: return 5', 6) - # Test for "interface" cannot be abbreviated - lines =<< trim END - vim9script - inte Something - endinterface - END - v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: inte Something', 2) + class Point implements HasX + var x = 0 + var y = 0 + endclass - # Test for "endinterface" cannot be abbreviated - lines =<< trim END - vim9script - interface Something - endin + var p: Point + p = Point.new(2, 33) + var hx = p + assert_equal(2, hx.x) END - v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: endin', 3) + v9.CheckSourceSuccess(lines) - # Additional commands after "interface name" lines =<< trim END vim9script - interface Something | var x = 10 | var y = 20 - endinterface - END - v9.CheckSourceFailure(lines, "E488: Trailing characters: | var x = 10", 2) - lines =<< trim END - vim9script - export interface EnterExit - def Enter(): void - def Exit(): void - endinterface - END - writefile(lines, 'XdefIntf.vim', 'D') + class Point + var x = 0 + var y = 0 + endclass - lines =<< trim END - vim9script - import './XdefIntf.vim' as defIntf - export def With(ee: defIntf.EnterExit, F: func) - ee.Enter() - try - F() - finally - ee.Exit() - endtry - enddef + var p: Point + p = 'text' END - v9.CheckScriptSuccess(lines) + v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected object but got string', 9) +enddef - var imported =<< trim END +def Test_class_extends() + var lines =<< trim END vim9script - export abstract class EnterExit - def Enter(): void + class Base + var one = 1 + def GetOne(): number + return this.one enddef - def Exit(): void + endclass + class Child extends Base + var two = 2 + def GetTotal(): number + return this.one + this.two enddef endclass - END - writefile(imported, 'XdefIntf2.vim', 'D') - - lines[1] = " import './XdefIntf2.vim' as defIntf" - v9.CheckScriptSuccess(lines) -enddef - -" Test for using string() with an interface -def Test_interface_to_string() - var lines =<< trim END - vim9script - interface Intf - def Method(nr: number) - endinterface - assert_equal("interface Intf", string(Intf)) + var o = Child.new() + assert_equal(1, o.one) + assert_equal(2, o.two) + assert_equal(1, o.GetOne()) + assert_equal(3, o.GetTotal()) END v9.CheckSourceSuccess(lines) -enddef -def Test_class_implements_interface() - var lines =<< trim END + lines =<< trim END vim9script - - interface Some - var count: number - def Method(nr: number) - endinterface - - class SomeImpl implements Some - var count: number - def Method(nr: number) - echo nr - enddef + class Base + var one = 1 endclass - - interface Another - var member: string - endinterface - - class AnotherImpl implements Some, Another - var member = 'abc' - var count = 20 - def Method(nr: number) - echo nr - enddef + class Child extends Base + var two = 2 endclass + var o = Child.new(3, 44) + assert_equal(3, o.one) + assert_equal(44, o.two) END v9.CheckSourceSuccess(lines) lines =<< trim END vim9script - - interface Some - var count: number - endinterface - - class SomeImpl implements Some implements Some - var count: number + class Base + var one = 1 + endclass + class Child extends Base extends Base + var two = 2 endclass END - v9.CheckSourceFailure(lines, 'E1350: Duplicate "implements"', 7) + v9.CheckSourceFailure(lines, 'E1352: Duplicate "extends"', 5) lines =<< trim END vim9script - - interface Some - var count: number - endinterface - - class SomeImpl implements Some, Some - var count: number + class Child extends BaseClass + var two = 2 endclass END - v9.CheckSourceFailure(lines, 'E1351: Duplicate interface after "implements": Some', 7) + v9.CheckSourceFailure(lines, 'E1353: Class name not found: BaseClass', 4) lines =<< trim END vim9script - - interface Some - var counter: number - def Method(nr: number) - endinterface - - class SomeImpl implements Some - var count: number - def Method(nr: number) - echo nr - enddef + var SomeVar = 99 + class Child extends SomeVar + var two = 2 endclass END - v9.CheckSourceFailure(lines, 'E1348: Variable "counter" of interface "Some" is not implemented', 13) + v9.CheckSourceFailure(lines, 'E1354: Cannot extend SomeVar', 5) lines =<< trim END vim9script - - interface Some - var count: number - def Methods(nr: number) - endinterface - - class SomeImpl implements Some - var count: number - def Method(nr: number) - echo nr + class Child + var age: number + def ToString(): number + return this.age + enddef + def ToString(): string + return this.age enddef endclass END - v9.CheckSourceFailure(lines, 'E1349: Method "Methods" of interface "Some" is not implemented', 13) + v9.CheckSourceFailure(lines, 'E1355: Duplicate function: ToString', 9) - # Check different order of members in class and interface works. lines =<< trim END vim9script + class Base + var name: string + static def ToString(): string + return 'Base class' + enddef + endclass - interface Result - var label: string - var errpos: number - endinterface - - # order of members is opposite of interface - class Failure implements Result - public var lnum: number = 5 - var errpos: number = 42 - var label: string = 'label' - endclass - - def Test() - var result: Result = Failure.new() - - assert_equal('label', result.label) - assert_equal(42, result.errpos) + class Child extends Base + var age: number + def ToString(): string + return Base.ToString() .. ': ' .. this.age enddef + endclass - Test() + var o = Child.new('John', 42) + assert_equal('Base class: 42', o.ToString()) END v9.CheckSourceSuccess(lines) - # Interface name after "extends" doesn't end in a space or NUL character lines =<< trim END vim9script - interface A - endinterface - class B extends A" + class Base + var value = 1 + def new(init: number) + this.value = number + 1 + enddef + endclass + class Child extends Base + def new() + this.new(3) + enddef endclass + var c = Child.new() END - v9.CheckSourceFailure(lines, 'E1315: White space required after name: A"', 4) + v9.CheckSourceFailure(lines, 'E1385: Class method "new" accessible only using class "Child"', 1) - # Trailing characters after a class name + # base class with more than one object member lines =<< trim END vim9script - class A bbb + + class Result + var success: bool + var value: any = null endclass - END - v9.CheckSourceFailure(lines, 'E488: Trailing characters: bbb', 2) - # using "implements" with a non-existing class - lines =<< trim END - vim9script - class A implements B + class Success extends Result + def new(this.value = v:none) + this.success = true + enddef endclass + + var v = Success.new('asdf') + assert_equal("object of Success {success: true, value: 'asdf'}", string(v)) END - v9.CheckSourceFailure(lines, 'E1346: Interface name not found: B', 3) + v9.CheckSourceSuccess(lines) - # using "implements" with a regular class + # class name after "extends" doesn't end in a space or NUL character lines =<< trim END vim9script class A endclass - class B implements A + class B extends A" endclass END - v9.CheckSourceFailure(lines, 'E1347: Not a valid interface: A', 5) + v9.CheckSourceFailure(lines, 'E1315: White space required after name: A"', 4) +enddef - # using "implements" with a variable - lines =<< trim END +def Test_using_base_class() + var lines =<< trim END vim9script - var T: number = 10 - class A implements T - endclass - END - v9.CheckSourceFailure(lines, 'E1347: Not a valid interface: T', 4) - # implements should be followed by a white space - lines =<< trim END - vim9script - interface A - endinterface - class B implements A; - endclass - END - v9.CheckSourceFailure(lines, 'E1315: White space required after name: A;', 4) + class BaseEE + def Enter(): any + return null + enddef + def Exit(resource: any): void + enddef + endclass - lines =<< trim END - vim9script + class ChildEE extends BaseEE + def Enter(): any + return 42 + enddef - interface One - def IsEven(nr: number): bool - endinterface - class Two implements One - def IsEven(nr: number): string + def Exit(resource: number): void + g:result ..= '/exit' enddef endclass + + def With(ee: BaseEE) + var r = ee.Enter() + try + g:result ..= r + finally + g:result ..= '/finally' + ee.Exit(r) + endtry + enddef + + g:result = '' + With(ChildEE.new()) + assert_equal('42/finally/exit', g:result) END - v9.CheckSourceFailure(lines, 'E1383: Method "IsEven": type mismatch, expected func(number): bool but got func(number): string', 9) + v9.CheckSourceSuccess(lines) + unlet g:result +enddef - lines =<< trim END +" Test for using a method from the super class +def Test_super_dispatch() + # See #15448 and #15463 + var lines =<< trim END vim9script - interface One - def IsEven(nr: number): bool - endinterface - class Two implements One - def IsEven(nr: bool): bool + class A + def String(): string + return 'A' enddef endclass - END - v9.CheckSourceFailure(lines, 'E1383: Method "IsEven": type mismatch, expected func(number): bool but got func(bool): bool', 9) - lines =<< trim END - vim9script - - interface One - def IsEven(nr: number): bool - endinterface - class Two implements One - def IsEven(nr: number, ...extra: list): bool + class B extends A + def String(): string + return super.String() enddef endclass + + class C extends B + endclass + + assert_equal('A', C.new().String()) END - v9.CheckSourceFailure(lines, 'E1383: Method "IsEven": type mismatch, expected func(number): bool but got func(number, ...list): bool', 9) + v9.CheckSourceSuccess(lines) - # access superclass interface members from subclass, mix variable order lines =<< trim END vim9script - interface I1 - var mvar1: number - var mvar2: number - endinterface - - # NOTE: the order is swapped - class A implements I1 - var mvar2: number - var mvar1: number - public static var svar2: number - public static var svar1: number - def new() - svar1 = 11 - svar2 = 12 - this.mvar1 = 111 - this.mvar2 = 112 + class A + def F(): string + return 'AA' enddef endclass class B extends A - def new() - this.mvar1 = 121 - this.mvar2 = 122 + def F(): string + return 'BB' + enddef + def S(): string + return super.F() + enddef + def S0(): string + return this.S() enddef endclass class C extends B - def new() - this.mvar1 = 131 - this.mvar2 = 132 + def F(): string + return 'CC' + enddef + def ToB(): string + return super.F() enddef endclass - def F2(i: I1): list - return [ i.mvar1, i.mvar2 ] - enddef + assert_equal('AA', B.new().S()) + assert_equal('AA', C.new().S()) + assert_equal('AA', B.new().S0()) + assert_equal('AA', C.new().S0()) - var oa = A.new() - var ob = B.new() - var oc = C.new() + assert_equal('BB', C.new().ToB()) - assert_equal([111, 112], F2(oa)) - assert_equal([121, 122], F2(ob)) - assert_equal([131, 132], F2(oc)) + assert_equal('CC', C.new().F()) + assert_equal('BB', B.new().F()) + assert_equal('AA', A.new().F()) END v9.CheckSourceSuccess(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 - var mvar1: number - var mvar2: number - endinterface + var call_chain: list - interface I2 - var mvar3: number - var mvar4: number - endinterface + abstract class A + abstract def _G(): string - class A implements I1 - public static var svar1: number - public static var svar2: number - var mvar1: number - var mvar2: number - def new() - svar1 = 11 - svar2 = 12 - this.mvar1 = 111 - this.mvar2 = 112 + def F(): string + call_chain->add('A.F()') + return this._G() + enddef + def _H(): string + call_chain->add('A._H()') + return this.F() enddef endclass - class B extends A implements I2 - static var svar3: number - static var svar4: number - var mvar3: number - var mvar4: number - def new() - svar3 = 23 - svar4 = 24 - this.mvar1 = 121 - this.mvar2 = 122 - this.mvar3 = 123 - this.mvar4 = 124 + class B extends A + def _G(): string + call_chain->add('B.G()') + return 'BBB' + enddef + def SF(): string + call_chain->add('B.SF()') + return super._H() enddef endclass class C extends B - public static var svar5: number - def new() - svar5 = 1001 - this.mvar1 = 131 - this.mvar2 = 132 - this.mvar3 = 133 - this.mvar4 = 134 + endclass + + class D extends C + def SF(): string + call_chain->add('D.SF()') + return super.SF() enddef endclass - def F2(i: I1): list - return [ i.mvar1, i.mvar2 ] - enddef + class E extends D + def SF(): string + call_chain->add('E.SF()') + return super.SF() + enddef + endclass - def F4(i: I2): list - return [ i.mvar3, i.mvar4 ] - enddef + class F extends E + def _G(): string + call_chain->add('F._G()') + return 'FFF' + enddef + endclass - var oa = A.new() - var ob = B.new() - var oc = C.new() + # E.new() -> A.F() -> B._G() + call_chain = [] + var o1 = E.new() + assert_equal('BBB', o1.F()) + assert_equal(['A.F()', 'B.G()'], call_chain) - 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)]) + # F.new() -> E.SF() -> D.SF() -> B.SF() -> A._H() -> A.F() -> F._G() + call_chain = [] + var o2 = F.new() + assert_equal('FFF', o2.SF()) + assert_equal(['E.SF()', 'D.SF()', 'B.SF()', 'A._H()', 'A.F()', 'F._G()'], call_chain) END v9.CheckSourceSuccess(lines) - # Using two interface names without a space after the "," + # problems with method dispatch: super -> abstract + # https://github.com/vim/vim/issues/15514 lines =<< trim END vim9script - interface A - endinterface - interface B - endinterface - class C implements A,B + abstract class B + abstract def ToString(): string endclass - END - v9.CheckSourceFailure(lines, 'E1315: White space required after name: A,B', 6) - # No interface name after a comma - lines =<< trim END - vim9script - interface A - endinterface - class B implements A, + class C extends B + def ToString(): string + return super.ToString() + enddef endclass + + try + defcompile C.ToString + call assert_false(1, 'command should have failed') + catch + call assert_exception('E1431: Abstract method "ToString" in class "B" cannot be accessed directly') + endtry END - v9.CheckSourceFailure(lines, 'E1389: Missing name after implements', 4) + v9.CheckSourceSuccess(lines) - # No interface name after implements + # problems with method dispatch: super -> abstract -> concrete lines =<< trim END vim9script - class A implements + + class A + def ToString() + echo 'A' + enddef endclass - END - v9.CheckSourceFailure(lines, 'E1389: Missing name after implements', 2) -enddef -def Test_call_interface_method() - var lines =<< trim END - vim9script - interface Base - def Enter(): void - endinterface + abstract class B extends A + abstract def ToString() + endclass - class Child implements Base - def Enter(): void - g:result ..= 'child' + class C extends B + def ToString() + super.ToString() enddef endclass - def F(obj: Base) - obj.Enter() - enddef - - g:result = '' - F(Child.new()) - assert_equal('child', g:result) - unlet g:result + try + defcompile C.ToString + call assert_false(1, 'command should have failed') + catch + call assert_exception('E1431: Abstract method "ToString" in class "B" cannot be accessed directly') + endtry END v9.CheckSourceSuccess(lines) + # Invoking a super method and an interface method which have the same name. lines =<< trim END vim9script - class Base - def Enter(): void - g:result ..= 'base' + + interface I + def ToString(): string + endinterface + + # Note that A does not implement I. + class A + def ToString(): string + return 'A' enddef endclass - class Child extends Base - def Enter(): void - g:result ..= 'child' + class B extends A implements I + def ToString(): string + return super.ToString() enddef endclass - def F(obj: Base) - obj.Enter() - enddef + def TestI(i: I): string + return i.ToString() + enddef - g:result = '' - F(Child.new()) - assert_equal('child', g:result) - unlet g:result + assert_equal('A', B.new().ToString()) + assert_equal('A', TestI(B.new())) END v9.CheckSourceSuccess(lines) - # method of interface returns a value + # super and an abstract class with no abstract methods lines =<< trim END vim9script - interface Base - def Enter(): string - endinterface - class Child implements Base - def Enter(): string - g:result ..= 'child' - return "/resource" + class A + def ToString(): string + return 'A' enddef endclass - def F(obj: Base) - var r = obj.Enter() - g:result ..= r - enddef - - g:result = '' - F(Child.new()) - assert_equal('child/resource', g:result) - unlet g:result - END - v9.CheckSourceSuccess(lines) - - lines =<< trim END - vim9script - class Base - def Enter(): string - return null_string - enddef + # An abstract class with no abstract methods. + abstract class B extends A endclass - class Child extends Base - def Enter(): string - g:result ..= 'child' - return "/resource" + class C extends B + def ToString(): string + return super.ToString() enddef endclass - def F(obj: Base) - var r = obj.Enter() - g:result ..= r + def TestA(a: A): string + return a.ToString() enddef - g:result = '' - F(Child.new()) - assert_equal('child/resource', g:result) - unlet g:result - END - v9.CheckSourceSuccess(lines) + def TestB(b: B): string + return b.ToString() + enddef - # No class that implements the interface. - lines =<< trim END + assert_equal('A', C.new().ToString()) + assert_equal('A', TestA(A.new())) + assert_equal('A', TestA(C.new())) + assert_equal('A', TestB(C.new())) + END + v9.CheckSourceSuccess(lines) + + # super and an abstract class with no abstract methods and the initial + # implements clause + lines =<< trim END vim9script - interface IWithEE - def Enter(): any - def Exit(): void + interface I + def ToString(): string endinterface - def With1(ee: IWithEE, F: func) - var r = ee.Enter() + # Note that A does not implement I. + class A + def ToString(): string + return 'A' + enddef + endclass + + # An abstract class with no abstract methods. + abstract class B extends A implements I + endclass + + class C extends B implements I + def ToString(): string + return super.ToString() + enddef + endclass + + # Note that A.ToString() is different from I.ToString(). + def TestA(a: A): string + return a.ToString() enddef - defcompile + assert_equal('A', C.new().ToString()) + assert_equal('A', TestA(A.new())) + assert_equal('A', TestA(C.new())) END v9.CheckSourceSuccess(lines) -enddef -def Test_class_used_as_type() - var lines =<< trim END + # Invoking a class method in the parent class using "super" should fail + lines =<< trim END vim9script - class Point - var x = 0 - var y = 0 + class A + static def Fn(): string + return 'A' + enddef endclass - var p: Point - p = Point.new(2, 33) - assert_equal(2, p.x) - assert_equal(33, p.y) + class B extends A + static def Fn(): string + return super.Fn() + enddef + endclass + defcompile END - v9.CheckSourceSuccess(lines) + v9.CheckSourceFailure(lines, 'E1325: Method "Fn" not found in class "B"') + # Missing name after "super" keyword lines =<< trim END vim9script - - interface HasX - var x: number - endinterface - - class Point implements HasX - var x = 0 - var y = 0 + class A endclass + class B extends A + def Fn() + var x = super.() + enddef + endclass + defcompile + END + v9.CheckSourceFailure(lines, 'E1127: Missing name after dot', 1) +enddef - var p: Point - p = Point.new(2, 33) - var hx = p - assert_equal(2, hx.x) +def Test_class_import() + var lines =<< trim END + vim9script + export class Animal + var kind: string + var name: string + endclass END - v9.CheckSourceSuccess(lines) + writefile(lines, 'Xanimal.vim', 'D') lines =<< trim END vim9script + import './Xanimal.vim' as animal - class Point - var x = 0 - var y = 0 - endclass + var a: animal.Animal + a = animal.Animal.new('fish', 'Eric') + assert_equal('fish', a.kind) + assert_equal('Eric', a.name) - var p: Point - p = 'text' + var b: animal.Animal = animal.Animal.new('cat', 'Garfield') + assert_equal('cat', b.kind) + assert_equal('Garfield', b.name) END - v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected object but got string', 9) + v9.CheckScriptSuccess(lines) enddef -def Test_class_extends() +" Test for importing a class into a legacy script and calling the class method +def Test_class_method_from_legacy_script() var lines =<< trim END vim9script - class Base - var one = 1 - def GetOne(): number - return this.one + export class A + static var name: string = 'a' + static def SetName(n: string) + name = n enddef endclass - class Child extends Base - var two = 2 - def GetTotal(): number - return this.one + this.two + END + writefile(lines, 'Xvim9export.vim', 'D') + + lines =<< trim END + import './Xvim9export.vim' as vim9 + + call s:vim9.A.SetName('b') + call assert_equal('b', s:vim9.A.name) + END + v9.CheckScriptSuccess(lines) +enddef + +" Test for extending an imported class +def Test_extend_imported_class() + var lines =<< trim END + vim9script + export class Imp_C1 + def Fn1(): number + return 5 enddef endclass - var o = Child.new() - assert_equal(1, o.one) - assert_equal(2, o.two) - assert_equal(1, o.GetOne()) - assert_equal(3, o.GetTotal()) END - v9.CheckSourceSuccess(lines) + writefile(lines, 'Xextendimportclass.vim', 'D') lines =<< trim END vim9script - class Base - var one = 1 + import './Xextendimportclass.vim' as XClass + + class A extends XClass.Imp_C1 endclass - class Child extends Base - var two = 2 + var a = A.new() + assert_equal(5, a.Fn1()) + END + v9.CheckScriptSuccess(lines) +enddef + +def Test_abstract_class() + var lines =<< trim END + vim9script + abstract class Base + var name: string endclass - var o = Child.new(3, 44) - assert_equal(3, o.one) - assert_equal(44, o.two) + class Person extends Base + var age: number + endclass + var p: Base = Person.new('Peter', 42) + assert_equal('Peter', p.name) + assert_equal(42, p.age) END v9.CheckSourceSuccess(lines) lines =<< trim END vim9script - class Base - var one = 1 + abstract class Base + var name: string endclass - class Child extends Base extends Base - var two = 2 + class Person extends Base + var age: number endclass + var p = Base.new('Peter') END - v9.CheckSourceFailure(lines, 'E1352: Duplicate "extends"', 5) + v9.CheckSourceFailure(lines, 'E1325: Method "new" not found in class "Base"', 8) lines =<< trim END - vim9script - class Child extends BaseClass - var two = 2 + abstract class Base + var name: string endclass END - v9.CheckSourceFailure(lines, 'E1353: Class name not found: BaseClass', 4) + v9.CheckSourceFailure(lines, 'E1316: Class can only be defined in Vim9 script', 1) + # Test for "abstract" cannot be abbreviated lines =<< trim END vim9script - var SomeVar = 99 - class Child extends SomeVar - var two = 2 + abs class A endclass END - v9.CheckSourceFailure(lines, 'E1354: Cannot extend SomeVar', 5) + v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: abs class A', 2) + # Additional commands after "abstract class" lines =<< trim END vim9script - class Child - var age: number - def ToString(): number - return this.age - enddef - def ToString(): string - return this.age - enddef + abstract class Something | var x = [] endclass END - v9.CheckSourceFailure(lines, 'E1355: Duplicate function: ToString', 9) + v9.CheckSourceFailure(lines, "E488: Trailing characters: | var x = []", 2) + # Abstract class cannot have a "new" function lines =<< trim END vim9script - class Base - var name: string - static def ToString(): string - return 'Base class' - enddef - endclass - - class Child extends Base - var age: number - def ToString(): string - return Base.ToString() .. ': ' .. this.age + abstract class Base + def new() enddef endclass - - var o = Child.new('John', 42) - assert_equal('Base class: 42', o.ToString()) END - v9.CheckSourceSuccess(lines) + v9.CheckSourceFailure(lines, 'E1359: Cannot define a "new" method in an abstract class', 4) + # extending an abstract class with class methods and variables lines =<< trim END vim9script - class Base - var value = 1 - def new(init: number) - this.value = number + 1 + abstract class A + static var s: string = 'vim' + static def Fn(): list + return [10] enddef endclass - class Child extends Base - def new() - this.new(3) - enddef + class B extends A endclass - var c = Child.new() + var b = B.new() + assert_equal('vim', A.s) + assert_equal([10], A.Fn()) END - v9.CheckSourceFailure(lines, 'E1385: Class method "new" accessible only using class "Child"', 1) + v9.CheckScriptSuccess(lines) +enddef - # base class with more than one object member - lines =<< trim END +def Test_closure_in_class() + var lines =<< trim END vim9script - class Result - var success: bool - var value: any = null - endclass + class Foo + var y: list = ['B'] - class Success extends Result - def new(this.value = v:none) - this.success = true + def new() + g:result = filter(['A', 'B'], (_, v) => index(this.y, v) == -1) enddef endclass - var v = Success.new('asdf') - assert_equal("object of Success {success: true, value: 'asdf'}", string(v)) + Foo.new() + assert_equal(['A'], g:result) END v9.CheckSourceSuccess(lines) - - # class name after "extends" doesn't end in a space or NUL character - lines =<< trim END - vim9script - class A - endclass - class B extends A" - endclass - END - v9.CheckSourceFailure(lines, 'E1315: White space required after name: A"', 4) enddef -def Test_using_base_class() +def Test_construct_object_from_legacy() + # Cannot directly invoke constructor from legacy var lines =<< trim END vim9script - class BaseEE - def Enter(): any - return null - enddef - def Exit(resource: any): void - enddef - endclass - - class ChildEE extends BaseEE - def Enter(): any - return 42 - enddef + var newCalled = false - def Exit(resource: number): void - g:result ..= '/exit' + class A + def new(arg: string) + newCalled = true enddef endclass - def With(ee: BaseEE) - var r = ee.Enter() - try - g:result ..= r - finally - g:result ..= '/finally' - ee.Exit(r) - endtry + export def CreateA(...args: list): A + return call(A.new, args) enddef - g:result = '' - With(ChildEE.new()) - assert_equal('42/finally/exit', g:result) + g:P = CreateA + legacy call g:P('some_arg') + assert_equal(true, newCalled) + unlet g:P END v9.CheckSourceSuccess(lines) - unlet g:result -enddef -" Test for using a method from the super class -def Test_super_dispatch() - # See #15448 and #15463 - var lines =<< trim END + lines =<< trim END vim9script + var newCalled = false + class A - def String(): string - return 'A' + static def CreateA(options = {}): any + return A.new() enddef - endclass - - class B extends A - def String(): string - return super.String() + def new() + newCalled = true enddef endclass - class C extends B - endclass - - assert_equal('A', C.new().String()) + g:P = A.CreateA + legacy call g:P() + assert_equal(true, newCalled) + unlet g:P END v9.CheckSourceSuccess(lines) + # This also tests invoking "new()" with "call" lines =<< trim END vim9script + var createdObject: any + class A - def F(): string - return 'AA' + var val1: number + var val2: number + static def CreateA(...args: list): any + createdObject = call(A.new, args) + return createdObject enddef endclass - class B extends A - def F(): string - return 'BB' - enddef - def S(): string - return super.F() - enddef - def S0(): string - return this.S() - enddef - endclass + g:P = A.CreateA + legacy call g:P(3, 5) + assert_equal(3, createdObject.val1) + assert_equal(5, createdObject.val2) + legacy call g:P() + assert_equal(0, createdObject.val1) + assert_equal(0, createdObject.val2) + legacy call g:P(7) + assert_equal(7, createdObject.val1) + assert_equal(0, createdObject.val2) + unlet g:P + END + v9.CheckSourceSuccess(lines) +enddef - class C extends B - def F(): string - return 'CC' +def Test_defer_with_object() + var lines =<< trim END + vim9script + + class CWithEE + def Enter() + g:result ..= "entered/" enddef - def ToB(): string - return super.F() + def Exit() + g:result ..= "exited" enddef endclass - assert_equal('AA', B.new().S()) - assert_equal('AA', C.new().S()) - assert_equal('AA', B.new().S0()) - assert_equal('AA', C.new().S0()) - - assert_equal('BB', C.new().ToB()) + def With(ee: CWithEE, F: func) + ee.Enter() + defer ee.Exit() + F() + enddef - assert_equal('CC', C.new().F()) - assert_equal('BB', B.new().F()) - assert_equal('AA', A.new().F()) + g:result = '' + var obj = CWithEE.new() + obj->With(() => { + g:result ..= "called/" + }) + assert_equal('entered/called/exited', g:result) END v9.CheckSourceSuccess(lines) + unlet g:result lines =<< trim END vim9script - var call_chain: list - - abstract class A - abstract def _G(): string - - def F(): string - call_chain->add('A.F()') - return this._G() + class BaseWithEE + def Enter() + g:result ..= "entered-base/" enddef - def _H(): string - call_chain->add('A._H()') - return this.F() + def Exit() + g:result ..= "exited-base" enddef endclass - class B extends A - def _G(): string - call_chain->add('B.G()') - return 'BBB' + class CWithEE extends BaseWithEE + def Enter() + g:result ..= "entered-child/" enddef - def SF(): string - call_chain->add('B.SF()') - return super._H() + def Exit() + g:result ..= "exited-child" enddef endclass - class C extends B - endclass + def With(ee: BaseWithEE, F: func) + ee.Enter() + defer ee.Exit() + F() + enddef - class D extends C - def SF(): string - call_chain->add('D.SF()') - return super.SF() - enddef + g:result = '' + var obj = CWithEE.new() + obj->With(() => { + g:result ..= "called/" + }) + assert_equal('entered-child/called/exited-child', g:result) + END + v9.CheckSourceSuccess(lines) + unlet g:result +enddef + +" The following test used to crash Vim (Github issue #12676) +def Test_extends_method_crashes_vim() + var lines =<< trim END + vim9script + + class Observer endclass - class E extends D - def SF(): string - call_chain->add('E.SF()') - return super.SF() + class Property + var value: any + + def Set(v: any) + if v != this.value + this.value = v + endif enddef - endclass - class F extends E - def _G(): string - call_chain->add('F._G()') - return 'FFF' + def Register(observer: Observer) enddef endclass - # E.new() -> A.F() -> B._G() - call_chain = [] - var o1 = E.new() - assert_equal('BBB', o1.F()) - assert_equal(['A.F()', 'B.G()'], call_chain) + class Bool extends Property + var value2: bool + endclass - # F.new() -> E.SF() -> D.SF() -> B.SF() -> A._H() -> A.F() -> F._G() - call_chain = [] - var o2 = F.new() - assert_equal('FFF', o2.SF()) - assert_equal(['E.SF()', 'D.SF()', 'B.SF()', 'A._H()', 'A.F()', 'F._G()'], call_chain) - END - v9.CheckSourceSuccess(lines) + def Observe(obj: Property, who: Observer) + obj.Register(who) + enddef - # problems with method dispatch: super -> abstract - # https://github.com/vim/vim/issues/15514 - lines =<< trim END - vim9script - abstract class B - abstract def ToString(): string - endclass + var p = Bool.new(false) + var myObserver = Observer.new() - class C extends B - def ToString(): string - return super.ToString() - enddef - endclass + Observe(p, myObserver) - try - defcompile C.ToString - call assert_false(1, 'command should have failed') - catch - call assert_exception('E1431: Abstract method "ToString" in class "B" cannot be accessed directly') - endtry + p.Set(true) END v9.CheckSourceSuccess(lines) +enddef - # problems with method dispatch: super -> abstract -> concrete - lines =<< trim END +" Test for calling a method in a class that is extended +def Test_call_method_in_extended_class() + var lines =<< trim END vim9script - class A - def ToString() - echo 'A' + var prop_init_called = false + var prop_register_called = false + + class Property + def Init() + prop_init_called = true enddef - endclass - abstract class B extends A - abstract def ToString() + def Register() + prop_register_called = true + enddef endclass - class C extends B - def ToString() - super.ToString() - enddef + class Bool extends Property endclass - try - defcompile C.ToString - call assert_false(1, 'command should have failed') - catch - call assert_exception('E1431: Abstract method "ToString" in class "B" cannot be accessed directly') - endtry + def Observe(obj: Property) + obj.Register() + enddef + + var p = Property.new() + Observe(p) + + p.Init() + assert_true(prop_init_called) + assert_true(prop_register_called) END v9.CheckSourceSuccess(lines) +enddef - # Invoking a super method and an interface method which have the same name. - lines =<< trim END +def Test_instanceof() + var lines =<< trim END vim9script - interface I - def ToString(): string + class Base1 + endclass + + class Base2 extends Base1 + endclass + + interface Intf1 endinterface - # Note that A does not implement I. - class A - def ToString(): string - return 'A' - enddef + class Mix1 implements Intf1 endclass - class B extends A implements I - def ToString(): string - return super.ToString() - enddef + class Base3 extends Mix1 endclass - def TestI(i: I): string - return i.ToString() - enddef + type AliasBase1 = Base1 + type AliasBase2 = Base2 + type AliasIntf1 = Intf1 + type AliasMix1 = Mix1 - assert_equal('A', B.new().ToString()) - assert_equal('A', TestI(B.new())) - END - v9.CheckSourceSuccess(lines) + var b1 = Base1.new() + var b2 = Base2.new() + var b3 = Base3.new() - # super and an abstract class with no abstract methods - lines =<< trim END - vim9script + assert_true(instanceof(b1, Base1)) + assert_true(instanceof(b2, Base1)) + assert_false(instanceof(b1, Base2)) + assert_true(instanceof(b3, Mix1)) + assert_true(instanceof(b3, Base1, Base2, Intf1)) - class A - def ToString(): string - return 'A' - enddef - endclass + assert_true(instanceof(b1, AliasBase1)) + assert_true(instanceof(b2, AliasBase1)) + assert_false(instanceof(b1, AliasBase2)) + assert_true(instanceof(b3, AliasMix1)) + assert_true(instanceof(b3, AliasBase1, AliasBase2, AliasIntf1)) - # An abstract class with no abstract methods. - abstract class B extends A - endclass + def Foo() + var a1 = Base1.new() + var a2 = Base2.new() + var a3 = Base3.new() - class C extends B - def ToString(): string - return super.ToString() - enddef - endclass + assert_true(instanceof(a1, Base1)) + assert_true(instanceof(a2, Base1)) + assert_false(instanceof(a1, Base2)) + assert_true(instanceof(a3, Mix1)) + assert_true(instanceof(a3, Base1, Base2, Intf1)) - def TestA(a: A): string - return a.ToString() + assert_true(instanceof(a1, AliasBase1)) + assert_true(instanceof(a2, AliasBase1)) + assert_false(instanceof(a1, AliasBase2)) + assert_true(instanceof(a3, AliasMix1)) + assert_true(instanceof(a3, AliasBase1, AliasBase2, AliasIntf1)) enddef + Foo() - def TestB(b: B): string - return b.ToString() - enddef + var o_null: Base1 + assert_false(instanceof(o_null, Base1)) - assert_equal('A', C.new().ToString()) - assert_equal('A', TestA(A.new())) - assert_equal('A', TestA(C.new())) - assert_equal('A', TestB(C.new())) END v9.CheckSourceSuccess(lines) - # super and an abstract class with no abstract methods and the initial - # implements clause lines =<< trim END vim9script - interface I - def ToString(): string - endinterface - - # Note that A does not implement I. - class A - def ToString(): string - return 'A' - enddef + class Base1 endclass + instanceof(Base1.new()) + END + v9.CheckSourceFailure(lines, 'E119: Not enough arguments for function: instanceof') - # An abstract class with no abstract methods. - abstract class B extends A implements I - endclass + lines =<< trim END + vim9script - class C extends B implements I - def ToString(): string - return super.ToString() - enddef + class Base1 endclass - - # Note that A.ToString() is different from I.ToString(). - def TestA(a: A): string - return a.ToString() + def F() + instanceof(Base1.new()) enddef - - assert_equal('A', C.new().ToString()) - assert_equal('A', TestA(A.new())) - assert_equal('A', TestA(C.new())) + F() END - v9.CheckSourceSuccess(lines) + v9.CheckSourceFailure(lines, 'E119: Not enough arguments for function: instanceof') - # Invoking a class method in the parent class using "super" should fail lines =<< trim END vim9script - class A - static def Fn(): string - return 'A' - enddef + class Base1 endclass - class B extends A - static def Fn(): string - return super.Fn() - enddef + class Base2 endclass - defcompile + + var o = Base2.new() + instanceof(o, Base1, Base2, 3) END - v9.CheckSourceFailure(lines, 'E1325: Method "Fn" not found in class "B"') + v9.CheckSourceFailure(lines, 'E693: Class or class typealias required for argument 4', 10) - # Missing name after "super" keyword lines =<< trim END vim9script - class A + + class Base1 endclass - class B extends A - def Fn() - var x = super.() - enddef + + class Base2 endclass - defcompile + + def F() + var o = Base2.new() + instanceof(o, Base1, Base2, 3) + enddef + F() END - v9.CheckSourceFailure(lines, 'E1127: Missing name after dot', 1) + v9.CheckSourceFailure(lines, 'E693: Class or class typealias required for argument 4') enddef -def Test_class_import() +" Test for calling a method in the parent class that is extended partially. +" This used to fail with the 'E118: Too many arguments for function: Text' error +" message (Github issue #12524). +def Test_call_method_in_parent_class() var lines =<< trim END vim9script - export class Animal - var kind: string - var name: string - endclass - END - writefile(lines, 'Xanimal.vim', 'D') - lines =<< trim END - vim9script - import './Xanimal.vim' as animal + class Widget + var _lnum: number = 1 - var a: animal.Animal - a = animal.Animal.new('fish', 'Eric') - assert_equal('fish', a.kind) - assert_equal('Eric', a.name) + def SetY(lnum: number) + this._lnum = lnum + enddef - var b: animal.Animal = animal.Animal.new('cat', 'Garfield') - assert_equal('cat', b.kind) - assert_equal('Garfield', b.name) - END - v9.CheckScriptSuccess(lines) -enddef + def Text(): string + return '' + enddef + endclass -" Test for importing a class into a legacy script and calling the class method -def Test_class_method_from_legacy_script() - var lines =<< trim END - vim9script - export class A - static var name: string = 'a' - static def SetName(n: string) - name = n + class Foo extends Widget + def Text(): string + return '' enddef endclass - END - writefile(lines, 'Xvim9export.vim', 'D') - lines =<< trim END - import './Xvim9export.vim' as vim9 + def Stack(w1: Widget, w2: Widget): list + w1.SetY(1) + w2.SetY(2) + return [w1, w2] + enddef - call s:vim9.A.SetName('b') - call assert_equal('b', s:vim9.A.name) + var foo1 = Foo.new() + var foo2 = Foo.new() + var l = Stack(foo1, foo2) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef -" Test for implementing an imported interface -def Test_implement_imported_interface() +" Test for calling methods from three levels of classes +def Test_multi_level_method_call() var lines =<< trim END vim9script - export interface Imp_Intf1 - def Fn1(): number - endinterface - export interface Imp_Intf2 - def Fn2(): number - endinterface - END - writefile(lines, 'Ximportinterface.vim', 'D') - lines =<< trim END - vim9script - import './Ximportinterface.vim' as Xintf + var A_func1: number = 0 + var A_func2: number = 0 + var A_func3: number = 0 + var B_func2: number = 0 + var B_func3: number = 0 + var C_func3: number = 0 - class A implements Xintf.Imp_Intf1, Xintf.Imp_Intf2 - def Fn1(): number - return 10 + class A + def Func1() + A_func1 += 1 enddef - def Fn2(): number - return 20 + + def Func2() + A_func2 += 1 enddef - endclass - var a = A.new() - assert_equal(10, a.Fn1()) - assert_equal(20, a.Fn2()) - END - v9.CheckScriptSuccess(lines) -enddef -" Test for extending an imported class -def Test_extend_imported_class() - var lines =<< trim END - vim9script - export class Imp_C1 - def Fn1(): number - return 5 + def Func3() + A_func3 += 1 enddef endclass - END - writefile(lines, 'Xextendimportclass.vim', 'D') - lines =<< trim END - vim9script - import './Xextendimportclass.vim' as XClass + class B extends A + def Func2() + B_func2 += 1 + enddef - class A extends XClass.Imp_C1 + def Func3() + B_func3 += 1 + enddef endclass - var a = A.new() - assert_equal(5, a.Fn1()) - END - v9.CheckScriptSuccess(lines) -enddef -def Test_abstract_class() - var lines =<< trim END - vim9script - abstract class Base - var name: string - endclass - class Person extends Base - var age: number + class C extends B + def Func3() + C_func3 += 1 + enddef endclass - var p: Base = Person.new('Peter', 42) - assert_equal('Peter', p.name) - assert_equal(42, p.age) - END - v9.CheckSourceSuccess(lines) - lines =<< trim END - vim9script - abstract class Base - var name: string - endclass - class Person extends Base - var age: number - endclass - var p = Base.new('Peter') - END - v9.CheckSourceFailure(lines, 'E1325: Method "new" not found in class "Base"', 8) + def A_CallFuncs(a: A) + a.Func1() + a.Func2() + a.Func3() + enddef - lines =<< trim END - abstract class Base - var name: string - endclass - END - v9.CheckSourceFailure(lines, 'E1316: Class can only be defined in Vim9 script', 1) + def B_CallFuncs(b: B) + b.Func1() + b.Func2() + b.Func3() + enddef - # Test for "abstract" cannot be abbreviated - lines =<< trim END - vim9script - abs class A - endclass - END - v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: abs class A', 2) + def C_CallFuncs(c: C) + c.Func1() + c.Func2() + c.Func3() + enddef - # Additional commands after "abstract class" - lines =<< trim END - vim9script - abstract class Something | var x = [] - endclass + var cobj = C.new() + A_CallFuncs(cobj) + B_CallFuncs(cobj) + C_CallFuncs(cobj) + assert_equal(3, A_func1) + assert_equal(0, A_func2) + assert_equal(0, A_func3) + assert_equal(3, B_func2) + assert_equal(0, B_func3) + assert_equal(3, C_func3) END - v9.CheckSourceFailure(lines, "E488: Trailing characters: | var x = []", 2) + v9.CheckSourceSuccess(lines) +enddef - # Abstract class cannot have a "new" function - lines =<< trim END +" Test for using members from three levels of classes +def Test_multi_level_member_access() + var lines =<< trim END vim9script - abstract class Base - def new() - enddef - endclass - END - v9.CheckSourceFailure(lines, 'E1359: Cannot define a "new" method in an abstract class', 4) - # extending an abstract class with class methods and variables - lines =<< trim END - vim9script - abstract class A - static var s: string = 'vim' - static def Fn(): list - return [10] - enddef + class A + public var val1: number = 0 endclass + class B extends A + public var val2: number = 0 endclass - var b = B.new() - assert_equal('vim', A.s) - assert_equal([10], A.Fn()) - END - v9.CheckScriptSuccess(lines) -enddef -def Test_closure_in_class() - var lines =<< trim END - vim9script + class C extends B + public var val3: number = 0 + endclass - class Foo - var y: list = ['B'] + def A_members(a: A) + a.val1 += 1 + enddef - def new() - g:result = filter(['A', 'B'], (_, v) => index(this.y, v) == -1) - enddef - endclass + def B_members(b: B) + b.val1 += 1 + b.val2 += 1 + enddef - Foo.new() - assert_equal(['A'], g:result) + def C_members(c: C) + c.val1 += 1 + c.val2 += 1 + c.val3 += 1 + enddef + + var cobj = C.new() + A_members(cobj) + B_members(cobj) + C_members(cobj) + assert_equal(3, cobj.val1) + assert_equal(2, cobj.val2) + assert_equal(1, cobj.val3) END v9.CheckSourceSuccess(lines) enddef -def Test_construct_object_from_legacy() - # Cannot directly invoke constructor from legacy +" Test expansion of with class methods. +def Test_stack_expansion_with_methods() var lines =<< trim END vim9script - var newCalled = false - - class A - def new(arg: string) - newCalled = true + class C + def M1() + F0() enddef endclass - export def CreateA(...args: list): A - return call(A.new, args) + def F0() + assert_match('\d\+_F\[1\]\.\.\d\+_C\.M1\[1\]\.\.\d\+_F0\[1\]$', expand('')) enddef - g:P = CreateA - legacy call g:P('some_arg') - assert_equal(true, newCalled) - unlet g:P + def F() + C.new().M1() + enddef + + F() END v9.CheckSourceSuccess(lines) +enddef - lines =<< trim END +" Test the return type of the new() constructor +def Test_new_return_type() + # new() uses the default return type and there is no return statement + var lines =<< trim END vim9script - var newCalled = false + class C + var _bufnr: number - class A - static def CreateA(options = {}): any - return A.new() - enddef - def new() - newCalled = true + def new(this._bufnr) + if !bufexists(this._bufnr) + this._bufnr = -1 + endif enddef endclass - g:P = A.CreateA - legacy call g:P() - assert_equal(true, newCalled) - unlet g:P + var c = C.new(12345) + assert_equal('object', typename(c)) + + var v1: C + v1 = C.new(12345) + assert_equal('object', typename(v1)) + + def F() + var v2: C + v2 = C.new(12345) + assert_equal('object', typename(v2)) + enddef + F() END v9.CheckSourceSuccess(lines) - # This also tests invoking "new()" with "call" + # new() uses the default return type and an empty 'return' statement lines =<< trim END vim9script - var createdObject: any + class C + var _bufnr: number - class A - var val1: number - var val2: number - static def CreateA(...args: list): any - createdObject = call(A.new, args) - return createdObject + def new(this._bufnr) + if !bufexists(this._bufnr) + this._bufnr = -1 + return + endif enddef endclass - g:P = A.CreateA - legacy call g:P(3, 5) - assert_equal(3, createdObject.val1) - assert_equal(5, createdObject.val2) - legacy call g:P() - assert_equal(0, createdObject.val1) - assert_equal(0, createdObject.val2) - legacy call g:P(7) - assert_equal(7, createdObject.val1) - assert_equal(0, createdObject.val2) - unlet g:P + var c = C.new(12345) + assert_equal('object', typename(c)) + + var v1: C + v1 = C.new(12345) + assert_equal('object', typename(v1)) + + def F() + var v2: C + v2 = C.new(12345) + assert_equal('object', typename(v2)) + enddef + F() END v9.CheckSourceSuccess(lines) -enddef -def Test_defer_with_object() - var lines =<< trim END + # new() uses "any" return type and returns "this" + lines =<< trim END vim9script - class CWithEE - def Enter() - g:result ..= "entered/" - enddef - def Exit() - g:result ..= "exited" + class C + var _bufnr: number + + def new(this._bufnr): any + if !bufexists(this._bufnr) + this._bufnr = -1 + return this + endif enddef endclass - - def With(ee: CWithEE, F: func) - ee.Enter() - defer ee.Exit() - F() - enddef - - g:result = '' - var obj = CWithEE.new() - obj->With(() => { - g:result ..= "called/" - }) - assert_equal('entered/called/exited', g:result) END - v9.CheckSourceSuccess(lines) - unlet g:result + v9.CheckSourceFailure(lines, 'E1365: Cannot use a return type with the "new" method', 11) + # new() uses 'Dict' return type and returns a Dict lines =<< trim END vim9script - class BaseWithEE - def Enter() - g:result ..= "entered-base/" - enddef - def Exit() - g:result ..= "exited-base" - enddef - endclass + class C + var _state: dict - class CWithEE extends BaseWithEE - def Enter() - g:result ..= "entered-child/" - enddef - def Exit() - g:result ..= "exited-child" + def new(): dict + this._state = {} + return this._state enddef endclass - def With(ee: BaseWithEE, F: func) - ee.Enter() - defer ee.Exit() - F() - enddef - - g:result = '' - var obj = CWithEE.new() - obj->With(() => { - g:result ..= "called/" - }) - assert_equal('entered-child/called/exited-child', g:result) + var c = C.new() + assert_equal('object', typename(c)) END - v9.CheckSourceSuccess(lines) - unlet g:result + v9.CheckSourceFailure(lines, 'E1365: Cannot use a return type with the "new" method', 9) enddef -" The following test used to crash Vim (Github issue #12676) -def Test_extends_method_crashes_vim() +" Test for checking a member initialization type at run time. +def Test_runtime_type_check_for_member_init() var lines =<< trim END vim9script - class Observer - endclass - - class Property - var value: any - - def Set(v: any) - if v != this.value - this.value = v - endif - enddef - - def Register(observer: Observer) - enddef - endclass - - class Bool extends Property - var value2: bool - endclass + var retnum: bool = false - def Observe(obj: Property, who: Observer) - obj.Register(who) + def F(): any + retnum = !retnum + if retnum + return 1 + else + return "hello" + endif enddef - var p = Bool.new(false) - var myObserver = Observer.new() - - Observe(p, myObserver) + class C + var _foo: bool = F() + endclass - p.Set(true) + var c1 = C.new() + var c2 = C.new() END - v9.CheckSourceSuccess(lines) + v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected bool but got string', 0) enddef -" Test for calling a method in a class that is extended -def Test_call_method_in_extended_class() +" Test for locking a variable referring to an object and reassigning to another +" object. +def Test_lockvar_object() var lines =<< trim END vim9script - var prop_init_called = false - var prop_register_called = false - - class Property - def Init() - prop_init_called = true - enddef - - def Register() - prop_register_called = true + class C + var val: number + def new(this.val) enddef endclass - class Bool extends Property - endclass + var some_dict: dict = { a: C.new(1), b: C.new(2), c: C.new(3), } + lockvar 2 some_dict - def Observe(obj: Property) - obj.Register() + var current: C + current = some_dict['c'] + assert_equal(3, current.val) + current = some_dict['b'] + assert_equal(2, current.val) + + def F() + current = some_dict['c'] enddef - var p = Property.new() - Observe(p) + def G() + current = some_dict['b'] + enddef - p.Init() - assert_true(prop_init_called) - assert_true(prop_register_called) + F() + assert_equal(3, current.val) + G() + assert_equal(2, current.val) END v9.CheckSourceSuccess(lines) enddef -def Test_instanceof() +" Test trying to lock an object variable from various places +def Test_lockvar_object_variable() + # An object variable lockvar has several cases: + # object method, scriptlevel, scriplevel from :def, :def arg + # method arg, static method arg. + # Also different depths + + # + # lockvar of read-only object variable + # + + # read-only lockvar from object method var lines =<< trim END vim9script - class Base1 + class C + var val1: number + def Lock() + lockvar this.val1 + enddef endclass + var o = C.new(3) + o.Lock() + END + v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "this.val1" in class "C"') - class Base2 extends Base1 - endclass - - interface Intf1 - endinterface - - class Mix1 implements Intf1 - endclass + # read-only lockvar from scriptlevel + lines =<< trim END + vim9script - class Base3 extends Mix1 + class C + var val2: number endclass + var o = C.new(3) + lockvar o.val2 + END + v9.CheckSourceFailure(lines, 'E1335: Variable "val2" in class "C" is not writable') - type AliasBase1 = Base1 - type AliasBase2 = Base2 - type AliasIntf1 = Intf1 - type AliasMix1 = Mix1 - - var b1 = Base1.new() - var b2 = Base2.new() - var b3 = Base3.new() - - assert_true(instanceof(b1, Base1)) - assert_true(instanceof(b2, Base1)) - assert_false(instanceof(b1, Base2)) - assert_true(instanceof(b3, Mix1)) - assert_true(instanceof(b3, Base1, Base2, Intf1)) - - assert_true(instanceof(b1, AliasBase1)) - assert_true(instanceof(b2, AliasBase1)) - assert_false(instanceof(b1, AliasBase2)) - assert_true(instanceof(b3, AliasMix1)) - assert_true(instanceof(b3, AliasBase1, AliasBase2, AliasIntf1)) - - def Foo() - var a1 = Base1.new() - var a2 = Base2.new() - var a3 = Base3.new() - - assert_true(instanceof(a1, Base1)) - assert_true(instanceof(a2, Base1)) - assert_false(instanceof(a1, Base2)) - assert_true(instanceof(a3, Mix1)) - assert_true(instanceof(a3, Base1, Base2, Intf1)) + # read-only lockvar of scriptlevel variable from def + lines =<< trim END + vim9script - assert_true(instanceof(a1, AliasBase1)) - assert_true(instanceof(a2, AliasBase1)) - assert_false(instanceof(a1, AliasBase2)) - assert_true(instanceof(a3, AliasMix1)) - assert_true(instanceof(a3, AliasBase1, AliasBase2, AliasIntf1)) + class C + var val3: number + endclass + var o = C.new(3) + def Lock() + lockvar o.val3 enddef - Foo() - - var o_null: Base1 - assert_false(instanceof(o_null, Base1)) - + Lock() END - v9.CheckSourceSuccess(lines) + v9.CheckSourceFailure(lines, 'E1335: Variable "val3" in class "C" is not writable') + # read-only lockvar of def argument variable lines =<< trim END vim9script - class Base1 + class C + var val4: number endclass - instanceof(Base1.new()) + def Lock(o: C) + lockvar o.val4 + enddef + Lock(C.new(3)) END - v9.CheckSourceFailure(lines, 'E119: Not enough arguments for function: instanceof') + v9.CheckSourceFailure(lines, 'E1335: Variable "val4" in class "C" is not writable') + # read-only lockvar from object method arg lines =<< trim END vim9script - class Base1 + class C + var val5: number + def Lock(c: C) + lockvar c.val5 + enddef endclass - def F() - instanceof(Base1.new()) - enddef - F() + var o = C.new(3) + o.Lock(C.new(5)) END - v9.CheckSourceFailure(lines, 'E119: Not enough arguments for function: instanceof') + v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val5" in class "C"') + # read-only lockvar from class method arg lines =<< trim END vim9script - class Base1 + class C + var val6: number + static def Lock(c: C) + lockvar c.val6 + enddef endclass + var o = C.new(3) + C.Lock(o) + END + v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val6" in class "C"') - class Base2 - endclass + # + # lockvar of public object variable + # - var o = Base2.new() - instanceof(o, Base1, Base2, 3) + # lockvar from object method + lines =<< trim END + vim9script + + class C + public var val1: number + def Lock() + lockvar this.val1 + enddef + endclass + var o = C.new(3) + o.Lock() END - v9.CheckSourceFailure(lines, 'E693: Class or class typealias required for argument 4', 10) + v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "this.val1" in class "C"', 1) + # lockvar from scriptlevel lines =<< trim END vim9script - class Base1 + class C + public var val2: number endclass + var o = C.new(3) + lockvar o.val2 + END + v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o.val2" in class "C"', 7) - class Base2 - endclass + # lockvar of scriptlevel variable from def + lines =<< trim END + vim9script - def F() - var o = Base2.new() - instanceof(o, Base1, Base2, 3) + class C + public var val3: number + endclass + var o = C.new(3) + def Lock() + lockvar o.val3 enddef - F() + Lock() END - v9.CheckSourceFailure(lines, 'E693: Class or class typealias required for argument 4') -enddef + v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o.val3" in class "C"', 1) -" Test for calling a method in the parent class that is extended partially. -" This used to fail with the 'E118: Too many arguments for function: Text' error -" message (Github issue #12524). -def Test_call_method_in_parent_class() - var lines =<< trim END + # lockvar of def argument variable + lines =<< trim END vim9script - class Widget - var _lnum: number = 1 + class C + public var val4: number + endclass + def Lock(o: C) + lockvar o.val4 + enddef + Lock(C.new(3)) + END + v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o.val4" in class "C"', 1) - def SetY(lnum: number) - this._lnum = lnum - enddef + # lockvar from object method arg + lines =<< trim END + vim9script - def Text(): string - return '' + class C + public var val5: number + def Lock(c: C) + lockvar c.val5 enddef endclass + var o = C.new(3) + o.Lock(C.new(5)) + END + v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val5" in class "C"', 1) - class Foo extends Widget - def Text(): string - return '' + # lockvar from class method arg + lines =<< trim END + vim9script + + class C + public var val6: number + static def Lock(c: C) + lockvar c.val6 enddef endclass - - def Stack(w1: Widget, w2: Widget): list - w1.SetY(1) - w2.SetY(2) - return [w1, w2] - enddef - - var foo1 = Foo.new() - var foo2 = Foo.new() - var l = Stack(foo1, foo2) + var o = C.new(3) + C.Lock(o) END - v9.CheckSourceSuccess(lines) + v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val6" in class "C"', 1) enddef -" Test for calling methods from three levels of classes -def Test_multi_level_method_call() +" Test trying to lock a class variable from various places +def Test_lockvar_class_variable() + + # lockvar bare static from object method var lines =<< trim END vim9script - var A_func1: number = 0 - var A_func2: number = 0 - var A_func3: number = 0 - var B_func2: number = 0 - var B_func3: number = 0 - var C_func3: number = 0 - - class A - def Func1() - A_func1 += 1 + class C + public static var sval1: number + def Lock() + lockvar sval1 enddef + endclass + var o = C.new() + o.Lock() + END + v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval1" in class "C"', 1) - def Func2() - A_func2 += 1 - enddef + # lockvar C.static from object method + lines =<< trim END + vim9script - def Func3() - A_func3 += 1 + class C + public static var sval2: number + def Lock() + lockvar C.sval2 enddef endclass + var o = C.new() + o.Lock() + END + v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval2" in class "C"', 1) - class B extends A - def Func2() - B_func2 += 1 - enddef + # lockvar bare static from class method + lines =<< trim END + vim9script - def Func3() - B_func3 += 1 + class C + public static var sval3: number + static def Lock() + lockvar sval3 enddef endclass + C.Lock() + END + v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval3" in class "C"', 1) - class C extends B - def Func3() - C_func3 += 1 + # lockvar C.static from class method + lines =<< trim END + vim9script + + class C + public static var sval4: number + static def Lock() + lockvar C.sval4 enddef endclass + C.Lock() + END + v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval4" in class "C"', 1) - def A_CallFuncs(a: A) - a.Func1() - a.Func2() - a.Func3() - enddef + # lockvar C.static from script level + lines =<< trim END + vim9script - def B_CallFuncs(b: B) - b.Func1() - b.Func2() - b.Func3() - enddef + class C + public static var sval5: number + endclass + lockvar C.sval5 + END + v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval5" in class "C"', 6) - def C_CallFuncs(c: C) - c.Func1() - c.Func2() - c.Func3() - enddef + # lockvar o.static from script level + lines =<< trim END + vim9script - var cobj = C.new() - A_CallFuncs(cobj) - B_CallFuncs(cobj) - C_CallFuncs(cobj) - assert_equal(3, A_func1) - assert_equal(0, A_func2) - assert_equal(0, A_func3) - assert_equal(3, B_func2) - assert_equal(0, B_func3) - assert_equal(3, C_func3) + class C + public static var sval6: number + endclass + var o = C.new() + lockvar o.sval6 END - v9.CheckSourceSuccess(lines) + v9.CheckSourceFailure(lines, 'E1375: Class variable "sval6" accessible only using class "C"', 7) enddef -" Test for using members from three levels of classes -def Test_multi_level_member_access() +" Test locking an argument to :def +def Test_lockvar_argument() + # Lockvar a function arg var lines =<< trim END vim9script - class A - public var val1: number = 0 - endclass + def Lock(val: any) + lockvar val + enddef - class B extends A - public var val2: number = 0 - endclass + var d = {a: 1, b: 2} + Lock(d) - class C extends B - public var val3: number = 0 - endclass + d->extend({c: 3}) + END + v9.CheckSourceFailure(lines, 'E741: Value is locked: extend() argument') - def A_members(a: A) - a.val1 += 1 - enddef + # Lockvar a function arg. Verify "sval" is interpreted as argument and not a + # class member in "C". This tests lval_root_is_arg. + lines =<< trim END + vim9script - def B_members(b: B) - b.val1 += 1 - b.val2 += 1 - enddef + class C + public static var sval: list + endclass - def C_members(c: C) - c.val1 += 1 - c.val2 += 1 - c.val3 += 1 + def Lock2(sval: any) + lockvar sval enddef - var cobj = C.new() - A_members(cobj) - B_members(cobj) - C_members(cobj) - assert_equal(3, cobj.val1) - assert_equal(2, cobj.val2) - assert_equal(1, cobj.val3) + var o = C.new() + Lock2(o) END v9.CheckSourceSuccess(lines) -enddef -" Test expansion of with class methods. -def Test_stack_expansion_with_methods() - var lines =<< trim END + # Lock a class. + lines =<< trim END vim9script class C - def M1() - F0() - enddef + public static var sval: list endclass - def F0() - assert_match('\d\+_F\[1\]\.\.\d\+_C\.M1\[1\]\.\.\d\+_F0\[1\]$', expand('')) - enddef - - def F() - C.new().M1() + def Lock2(sval: any) + lockvar sval enddef - F() + Lock2(C) END - v9.CheckSourceSuccess(lines) -enddef + v9.CheckSourceFailure(lines, 'E1405: Class "C" cannot be used as a value') -" Test the return type of the new() constructor -def Test_new_return_type() - # new() uses the default return type and there is no return statement - var lines =<< trim END + # Lock an object. + lines =<< trim END vim9script class C - var _bufnr: number - - def new(this._bufnr) - if !bufexists(this._bufnr) - this._bufnr = -1 - endif - enddef + public static var sval: list endclass - var c = C.new(12345) - assert_equal('object', typename(c)) - - var v1: C - v1 = C.new(12345) - assert_equal('object', typename(v1)) - - def F() - var v2: C - v2 = C.new(12345) - assert_equal('object', typename(v2)) + def Lock2(sval: any) + lockvar sval enddef - F() + + Lock2(C.new()) END v9.CheckSourceSuccess(lines) - # new() uses the default return type and an empty 'return' statement + # In this case (unlike previous) "lockvar sval" is a class member. lines =<< trim END vim9script class C - var _bufnr: number - - def new(this._bufnr) - if !bufexists(this._bufnr) - this._bufnr = -1 - return - endif + public static var sval: list + def Lock2() + lockvar sval enddef endclass - var c = C.new(12345) - assert_equal('object', typename(c)) - var v1: C - v1 = C.new(12345) - assert_equal('object', typename(v1)) + var o = C.new() + o.Lock2() + END + v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval" in class "C"', 1) +enddef - def F() - var v2: C - v2 = C.new(12345) - assert_equal('object', typename(v2)) - enddef - F() +" Test that this can be locked without error +def Test_lockvar_this() + # lockvar this + var lines =<< trim END + vim9script + class C + def TLock() + lockvar this + enddef + endclass + var o = C.new() + o.TLock() END v9.CheckSourceSuccess(lines) - # new() uses "any" return type and returns "this" + # lockvar four (four letter word, but not this) lines =<< trim END vim9script - class C - var _bufnr: number - - def new(this._bufnr): any - if !bufexists(this._bufnr) - this._bufnr = -1 - return this - endif + def TLock4() + var four: number + lockvar four enddef endclass + var o = C.new() + o.TLock4() END - v9.CheckSourceFailure(lines, 'E1365: Cannot use a return type with the "new" method', 11) + v9.CheckSourceFailure(lines, 'E1178: Cannot lock or unlock a local variable') - # new() uses 'Dict' return type and returns a Dict + # lockvar this5; "this" + one char, 5 letter word, starting with "this" lines =<< trim END vim9script - class C - var _state: dict - - def new(): dict - this._state = {} - return this._state + def TLock5() + var this5: number + lockvar this5 enddef endclass - - var c = C.new() - assert_equal('object', typename(c)) + var o = C.new() + o.TLock5() END - v9.CheckSourceFailure(lines, 'E1365: Cannot use a return type with the "new" method', 9) + v9.CheckSourceFailure(lines, 'E1178: Cannot lock or unlock a local variable') enddef -" Test for checking a member initialization type at run time. -def Test_runtime_type_check_for_member_init() +" Test some general lockvar cases +def Test_lockvar_general() + # lockvar an object and a class. It does nothing var lines =<< trim END vim9script - - var retnum: bool = false - - def F(): any - retnum = !retnum - if retnum - return 1 - else - return "hello" - endif - enddef - class C - var _foo: bool = F() endclass - - var c1 = C.new() - var c2 = C.new() + var o = C.new() + lockvar o + lockvar C END - v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected bool but got string', 0) -enddef + v9.CheckSourceSuccess(lines) -" Test for locking a variable referring to an object and reassigning to another -" object. -def Test_lockvar_object() - var lines =<< trim END + # Lock a list element that's nested in an object variable from a :def + lines =<< trim END vim9script class C - var val: number - def new(this.val) - enddef + public var val: list> = [ [1], [2], [3] ] endclass - - var some_dict: dict = { a: C.new(1), b: C.new(2), c: C.new(3), } - lockvar 2 some_dict - - var current: C - current = some_dict['c'] - assert_equal(3, current.val) - current = some_dict['b'] - assert_equal(2, current.val) - - def F() - current = some_dict['c'] - enddef - - def G() - current = some_dict['b'] + def Lock2(obj: any) + lockvar obj.val[1] enddef - F() - assert_equal(3, current.val) - G() - assert_equal(2, current.val) + var o = C.new() + Lock2(o) + o.val[0] = [9] + assert_equal([ [9], [2], [3] ], o.val) + try + o.val[1] = [999] + call assert_false(true, 'assign should have failed') + catch + assert_exception('E741:') + endtry + o.val[2] = [8] + assert_equal([ [9], [2], [8] ], o.val) END v9.CheckSourceSuccess(lines) -enddef - -" Test trying to lock an object variable from various places -def Test_lockvar_object_variable() - # An object variable lockvar has several cases: - # object method, scriptlevel, scriplevel from :def, :def arg - # method arg, static method arg. - # Also different depths - - # - # lockvar of read-only object variable - # - - # read-only lockvar from object method - var lines =<< trim END - vim9script - - class C - var val1: number - def Lock() - lockvar this.val1 - enddef - endclass - var o = C.new(3) - o.Lock() - END - v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "this.val1" in class "C"') - # read-only lockvar from scriptlevel + # Lock a list element that's nested in an object variable from scriptlevel lines =<< trim END vim9script class C - var val2: number + public var val: list> = [ [1], [2], [3] ] endclass - var o = C.new(3) - lockvar o.val2 - END - v9.CheckSourceFailure(lines, 'E1335: Variable "val2" in class "C" is not writable') - - # read-only lockvar of scriptlevel variable from def - lines =<< trim END - vim9script - class C - var val3: number - endclass - var o = C.new(3) - def Lock() - lockvar o.val3 - enddef - Lock() + var o = C.new() + lockvar o.val[1] + o.val[0] = [9] + assert_equal([ [9], [2], [3] ], o.val) + try + o.val[1] = [999] + call assert_false(true, 'assign should have failed') + catch + assert_exception('E741:') + endtry + o.val[2] = [8] + assert_equal([ [9], [2], [8] ], o.val) END - v9.CheckSourceFailure(lines, 'E1335: Variable "val3" in class "C" is not writable') + v9.CheckSourceSuccess(lines) - # read-only lockvar of def argument variable + # lock a script level variable from an object method lines =<< trim END vim9script class C - var val4: number + def Lock() + lockvar l + enddef endclass - def Lock(o: C) - lockvar o.val4 - enddef - Lock(C.new(3)) + + var l = [1] + C.new().Lock() + l[0] = 11 END - v9.CheckSourceFailure(lines, 'E1335: Variable "val4" in class "C" is not writable') + v9.CheckSourceFailure(lines, 'E741: Value is locked: l[0] = 11', 11) - # read-only lockvar from object method arg + # lock a list element referenced by a protected object variable + # in an object fetched via a script level list lines =<< trim END vim9script class C - var val5: number - def Lock(c: C) - lockvar c.val5 + var _v1: list> + def Lock() + lockvar lc[0]._v1[1] enddef endclass - var o = C.new(3) - o.Lock(C.new(5)) - END - v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val5" in class "C"') - # read-only lockvar from class method arg - lines =<< trim END - vim9script + var l = [[1], [2], [3]] + var o = C.new(l) + var lc: list = [ o ] - class C - var val6: number - static def Lock(c: C) - lockvar c.val6 - enddef - endclass - var o = C.new(3) - C.Lock(o) + o.Lock() + l[0] = [22] + l[1] = [33] END - v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val6" in class "C"') - - # - # lockvar of public object variable - # + v9.CheckSourceFailure(lines, 'E741: Value is locked: l[1] = [33]', 16) - # lockvar from object method + # similar to the previous test, except the locking code is executing + # in a class that does not own the protected variable. + # Note that the locking code is in a class has a protected variable of + # the same name. lines =<< trim END vim9script - class C - public var val1: number - def Lock() - lockvar this.val1 + class C2 + var _v1: list> + def Lock(obj: any) + lockvar lc[0]._v1[1] enddef endclass - var o = C.new(3) - o.Lock() - END - v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "this.val1" in class "C"', 1) - - # lockvar from scriptlevel - lines =<< trim END - vim9script class C - public var val2: number + var _v1: list> endclass - var o = C.new(3) - lockvar o.val2 - END - v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o.val2" in class "C"', 7) - # lockvar of scriptlevel variable from def - lines =<< trim END - vim9script + var l = [[1], [2], [3]] + var o = C.new(l) + var lc: list = [ o ] - class C - public var val3: number - endclass - var o = C.new(3) - def Lock() - lockvar o.val3 - enddef - Lock() + var o2 = C2.new() + o2.Lock(o) END - v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o.val3" in class "C"', 1) + v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_v1" in class "C"') +enddef - # lockvar of def argument variable - lines =<< trim END +" Test builtin islocked() +def Test_lockvar_islocked() + # Can't lock class/object variable + # Lock class/object variable's value + # Lock item of variable's value (a list item) + # variable is at index 1 within class/object + var lines =<< trim END vim9script class C - public var val4: number + var o0: list> = [ [0], [1], [2]] + var o1: list> = [[10], [11], [12]] + static var c0: list> = [[20], [21], [22]] + static var c1: list> = [[30], [31], [32]] endclass - def Lock(o: C) - lockvar o.val4 + + def LockIt(arg: any) + lockvar arg enddef - Lock(C.new(3)) - END - v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o.val4" in class "C"', 1) - # lockvar from object method arg - lines =<< trim END - vim9script + def UnlockIt(arg: any) + unlockvar arg + enddef - class C - public var val5: number - def Lock(c: C) - lockvar c.val5 - enddef - endclass - var o = C.new(3) - o.Lock(C.new(5)) - END - v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val5" in class "C"', 1) - - # lockvar from class method arg - lines =<< trim END - vim9script - - class C - public var val6: number - static def Lock(c: C) - lockvar c.val6 - enddef - endclass - var o = C.new(3) - C.Lock(o) - END - v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val6" in class "C"', 1) -enddef - -" Test trying to lock a class variable from various places -def Test_lockvar_class_variable() - - # lockvar bare static from object method - var lines =<< trim END - vim9script - - class C - public static var sval1: number - def Lock() - lockvar sval1 - enddef - endclass - var o = C.new() - o.Lock() - END - v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval1" in class "C"', 1) - - # lockvar C.static from object method - lines =<< trim END - vim9script - - class C - public static var sval2: number - def Lock() - lockvar C.sval2 - enddef - endclass - var o = C.new() - o.Lock() - END - v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval2" in class "C"', 1) - - # lockvar bare static from class method - lines =<< trim END - vim9script - - class C - public static var sval3: number - static def Lock() - lockvar sval3 - enddef - endclass - C.Lock() - END - v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval3" in class "C"', 1) - - # lockvar C.static from class method - lines =<< trim END - vim9script - - class C - public static var sval4: number - static def Lock() - lockvar C.sval4 - enddef - endclass - C.Lock() - END - v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval4" in class "C"', 1) - - # lockvar C.static from script level - lines =<< trim END - vim9script - - class C - public static var sval5: number - endclass - lockvar C.sval5 - END - v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval5" in class "C"', 6) - - # lockvar o.static from script level - lines =<< trim END - vim9script - - class C - public static var sval6: number - endclass - var o = C.new() - lockvar o.sval6 - END - v9.CheckSourceFailure(lines, 'E1375: Class variable "sval6" accessible only using class "C"', 7) -enddef - -" Test locking an argument to :def -def Test_lockvar_argument() - # Lockvar a function arg - var lines =<< trim END - vim9script - - def Lock(val: any) - lockvar val - enddef - - var d = {a: 1, b: 2} - Lock(d) - - d->extend({c: 3}) - END - v9.CheckSourceFailure(lines, 'E741: Value is locked: extend() argument') - - # Lockvar a function arg. Verify "sval" is interpreted as argument and not a - # class member in "C". This tests lval_root_is_arg. - lines =<< trim END - vim9script - - class C - public static var sval: list - endclass - - def Lock2(sval: any) - lockvar sval - enddef - - var o = C.new() - Lock2(o) - END - v9.CheckSourceSuccess(lines) - - # Lock a class. - lines =<< trim END - vim9script - - class C - public static var sval: list - endclass - - def Lock2(sval: any) - lockvar sval - enddef - - Lock2(C) - END - v9.CheckSourceFailure(lines, 'E1405: Class "C" cannot be used as a value') - - # Lock an object. - lines =<< trim END - vim9script - - class C - public static var sval: list - endclass - - def Lock2(sval: any) - lockvar sval - enddef - - Lock2(C.new()) - END - v9.CheckSourceSuccess(lines) - - # In this case (unlike previous) "lockvar sval" is a class member. - lines =<< trim END - vim9script - - class C - public static var sval: list - def Lock2() - lockvar sval - enddef - endclass - - - var o = C.new() - o.Lock2() - END - v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval" in class "C"', 1) -enddef - -" Test that this can be locked without error -def Test_lockvar_this() - # lockvar this - var lines =<< trim END - vim9script - class C - def TLock() - lockvar this - enddef - endclass - var o = C.new() - o.TLock() - END - v9.CheckSourceSuccess(lines) - - # lockvar four (four letter word, but not this) - lines =<< trim END - vim9script - class C - def TLock4() - var four: number - lockvar four - enddef - endclass - var o = C.new() - o.TLock4() - END - v9.CheckSourceFailure(lines, 'E1178: Cannot lock or unlock a local variable') - - # lockvar this5; "this" + one char, 5 letter word, starting with "this" - lines =<< trim END - vim9script - class C - def TLock5() - var this5: number - lockvar this5 - enddef - endclass - var o = C.new() - o.TLock5() - END - v9.CheckSourceFailure(lines, 'E1178: Cannot lock or unlock a local variable') -enddef - -" Test some general lockvar cases -def Test_lockvar_general() - # lockvar an object and a class. It does nothing - var lines =<< trim END - vim9script - class C - endclass - var o = C.new() - lockvar o - lockvar C - END - v9.CheckSourceSuccess(lines) - - # Lock a list element that's nested in an object variable from a :def - lines =<< trim END - vim9script - - class C - public var val: list> = [ [1], [2], [3] ] - endclass - def Lock2(obj: any) - lockvar obj.val[1] - enddef - - var o = C.new() - Lock2(o) - o.val[0] = [9] - assert_equal([ [9], [2], [3] ], o.val) - try - o.val[1] = [999] - call assert_false(true, 'assign should have failed') - catch - assert_exception('E741:') - endtry - o.val[2] = [8] - assert_equal([ [9], [2], [8] ], o.val) - END - v9.CheckSourceSuccess(lines) - - # Lock a list element that's nested in an object variable from scriptlevel - lines =<< trim END - vim9script - - class C - public var val: list> = [ [1], [2], [3] ] - endclass - - var o = C.new() - lockvar o.val[1] - o.val[0] = [9] - assert_equal([ [9], [2], [3] ], o.val) - try - o.val[1] = [999] - call assert_false(true, 'assign should have failed') - catch - assert_exception('E741:') - endtry - o.val[2] = [8] - assert_equal([ [9], [2], [8] ], o.val) - END - v9.CheckSourceSuccess(lines) - - # lock a script level variable from an object method - lines =<< trim END - vim9script - - class C - def Lock() - lockvar l - enddef - endclass - - var l = [1] - C.new().Lock() - l[0] = 11 - END - v9.CheckSourceFailure(lines, 'E741: Value is locked: l[0] = 11', 11) - - # lock a list element referenced by a protected object variable - # in an object fetched via a script level list - lines =<< trim END - vim9script - - class C - var _v1: list> - def Lock() - lockvar lc[0]._v1[1] - enddef - endclass - - var l = [[1], [2], [3]] - var o = C.new(l) - var lc: list = [ o ] - - o.Lock() - l[0] = [22] - l[1] = [33] - END - v9.CheckSourceFailure(lines, 'E741: Value is locked: l[1] = [33]', 16) - - # similar to the previous test, except the locking code is executing - # in a class that does not own the protected variable. - # Note that the locking code is in a class has a protected variable of - # the same name. - lines =<< trim END - vim9script - - class C2 - var _v1: list> - def Lock(obj: any) - lockvar lc[0]._v1[1] - enddef - endclass - - class C - var _v1: list> - endclass - - var l = [[1], [2], [3]] - var o = C.new(l) - var lc: list = [ o ] - - var o2 = C2.new() - o2.Lock(o) - END - v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_v1" in class "C"') -enddef - -" Test builtin islocked() -def Test_lockvar_islocked() - # Can't lock class/object variable - # Lock class/object variable's value - # Lock item of variable's value (a list item) - # variable is at index 1 within class/object - var lines =<< trim END - vim9script - - class C - var o0: list> = [ [0], [1], [2]] - var o1: list> = [[10], [11], [12]] - static var c0: list> = [[20], [21], [22]] - static var c1: list> = [[30], [31], [32]] - endclass - - def LockIt(arg: any) - lockvar arg - enddef - - def UnlockIt(arg: any) - unlockvar arg - enddef - - var obj = C.new() - #lockvar obj.o1 # can't lock something you can't write to - - try - lockvar obj.o1 # can't lock something you can't write to - call assert_false(1, '"lockvar obj.o1" should have failed') - catch - call assert_exception('E1335:') - endtry - - LockIt(obj.o1) # but can lock it's value - assert_equal(1, islocked("obj.o1")) - assert_equal(1, islocked("obj.o1[0]")) - assert_equal(1, islocked("obj.o1[1]")) - UnlockIt(obj.o1) - assert_equal(0, islocked("obj.o1")) - assert_equal(0, islocked("obj.o1[0]")) - - lockvar obj.o1[0] - assert_equal(0, islocked("obj.o1")) - assert_equal(1, islocked("obj.o1[0]")) - assert_equal(0, islocked("obj.o1[1]")) - unlockvar obj.o1[0] - assert_equal(0, islocked("obj.o1")) - assert_equal(0, islocked("obj.o1[0]")) - - # Same thing, but with a static - - try - lockvar C.c1 # can't lock something you can't write to - call assert_false(1, '"lockvar C.c1" should have failed') - catch - call assert_exception('E1335:') - endtry - - LockIt(C.c1) # but can lock it's value - assert_equal(1, islocked("C.c1")) - assert_equal(1, islocked("C.c1[0]")) - assert_equal(1, islocked("C.c1[1]")) - UnlockIt(C.c1) - assert_equal(0, islocked("C.c1")) - assert_equal(0, islocked("C.c1[0]")) - - lockvar C.c1[0] - assert_equal(0, islocked("C.c1")) - assert_equal(1, islocked("C.c1[0]")) - assert_equal(0, islocked("C.c1[1]")) - unlockvar C.c1[0] - assert_equal(0, islocked("C.c1")) - assert_equal(0, islocked("C.c1[0]")) - END - v9.CheckSourceSuccess(lines) - - # Do islocked() from an object method - # and then from a class method - lines =<< trim END - vim9script - - var l0o0 = [ [0], [1], [2]] - var l0o1 = [ [10], [11], [12]] - var l0c0 = [[120], [121], [122]] - var l0c1 = [[130], [131], [132]] - - class C0 - var o0: list> = l0o0 - var o1: list> = l0o1 - static var c0: list> = l0c0 - static var c1: list> = l0c1 - def Islocked(arg: string): number - return islocked(arg) - enddef - static def SIslocked(arg: string): number - return islocked(arg) - enddef - endclass - - var l2o0 = [[20000], [20001], [20002]] - var l2o1 = [[20010], [20011], [20012]] - var l2c0 = [[20120], [20121], [20122]] - var l2c1 = [[20130], [20131], [20132]] - - class C2 - var o0: list> = l2o0 - var o1: list> = l2o1 - static var c0: list> = l2c0 - static var c1: list> = l2c1 - def Islocked(arg: string): number - return islocked(arg) - enddef - static def SIslocked(arg: string): number - return islocked(arg) - enddef - endclass - - var obj0 = C0.new() - var obj2 = C2.new() - - var l = [ obj0, null_object, obj2 ] - - # lock list, object func access through script var expr - assert_equal(0, obj0.Islocked("l[0].o0")) - assert_equal(0, obj0.Islocked("l[0].o0[2]")) - lockvar l0o0 - assert_equal(1, obj0.Islocked("l[0].o0")) - assert_equal(1, obj0.Islocked("l[0].o0[2]")) - - #echo "check-b" obj2.Islocked("l[1].o1") # NULL OBJECT - - # lock list element, object func access through script var expr - lockvar l0o1[1] - assert_equal(0, obj0.Islocked("this.o1[0]")) - assert_equal(1, obj0.Islocked("this.o1[1]")) - - assert_equal(0, obj0.Islocked("this.o1")) - lockvar l0o1 - assert_equal(1, obj0.Islocked("this.o1")) - unlockvar l0o1 - - lockvar l0c1[1] - - # static by class name member expr from same class - assert_equal(0, obj0.Islocked("C0.c1[0]")) - assert_equal(1, obj0.Islocked("C0.c1[1]")) - # static by bare name member expr from same class - assert_equal(0, obj0.Islocked("c1[0]")) - assert_equal(1, obj0.Islocked("c1[1]")) - - # static by class name member expr from other class - assert_equal(0, obj2.Islocked("C0.c1[0]")) - assert_equal(1, obj2.Islocked("C0.c1[1]")) - # static by bare name member expr from other class - assert_equal(0, obj2.Islocked("c1[0]")) - assert_equal(0, obj2.Islocked("c1[1]")) - - - # static by bare name in same class - assert_equal(0, obj0.Islocked("c0")) - lockvar l0c0 - assert_equal(1, obj0.Islocked("c0")) - - # - # similar stuff, but use static method - # - - unlockvar l0o0 - - # lock list, object func access through script var expr - assert_equal(0, C0.SIslocked("l[0].o0")) - assert_equal(0, C0.SIslocked("l[0].o0[2]")) - lockvar l0o0 - assert_equal(1, C0.SIslocked("l[0].o0")) - assert_equal(1, C0.SIslocked("l[0].o0[2]")) - - unlockvar l0o1 - - # can't access "this" from class method - try - C0.SIslocked("this.o1[0]") - call assert_0(1, '"C0.SIslocked("this.o1[0]")" should have failed') - catch - call assert_exception('E121: Undefined variable: this') - endtry - - lockvar l0c1[1] - - # static by class name member expr from same class - assert_equal(0, C0.SIslocked("C0.c1[0]")) - assert_equal(1, C0.SIslocked("C0.c1[1]")) - # static by bare name member expr from same class - assert_equal(0, C0.SIslocked("c1[0]")) - assert_equal(1, C0.SIslocked("c1[1]")) - - # static by class name member expr from other class - assert_equal(0, C2.SIslocked("C0.c1[0]")) - assert_equal(1, C2.SIslocked("C0.c1[1]")) - # static by bare name member expr from other class - assert_equal(0, C2.SIslocked("c1[0]")) - assert_equal(0, C2.SIslocked("c1[1]")) - - - # static by bare name in same class - unlockvar l0c0 - assert_equal(0, C0.SIslocked("c0")) - lockvar l0c0 - assert_equal(1, C0.SIslocked("c0")) - END - v9.CheckSourceSuccess(lines) - - # Check islocked class/object from various places. - lines =<< trim END - vim9script - - class C - def Islocked(arg: string): number - return islocked(arg) - enddef - static def SIslocked(arg: string): number - return islocked(arg) - enddef - endclass - var obj = C.new() - - # object method - assert_equal(0, obj.Islocked("this")) - assert_equal(0, obj.Islocked("C")) - - # class method - ### assert_equal(0, C.SIslocked("this")) - assert_equal(0, C.SIslocked("C")) - - #script level - var v: number - v = islocked("C") - assert_equal(0, v) - v = islocked("obj") - assert_equal(0, v) - END - v9.CheckSourceSuccess(lines) -enddef - -def Test_lockvar_islocked_notfound() - # Try non-existent things - var lines =<< trim END - vim9script - - class C - def Islocked(arg: string): number - return islocked(arg) - enddef - static def SIslocked(arg: string): number - return islocked(arg) - enddef - endclass - var obj = C.new() - assert_equal(-1, obj.Islocked("anywhere")) - assert_equal(-1, C.SIslocked("notanywhere")) - END - v9.CheckSourceSuccess(lines) - - # Something not found of the form "name1.name2" is an error - lines =<< trim END - vim9script - - islocked("one.two") - END - v9.CheckSourceFailure(lines, 'E121: Undefined variable: one') - - lines =<< trim END - vim9script - - class C - var val = { key: "value" } - def Islocked(arg: string): number - return islocked(arg) - enddef - endclass - var obj = C.new() - obj.Islocked("this.val.not_there")) - END - v9.CheckSourceFailure(lines, 'E716: Key not present in Dictionary: "not_there"') - - lines =<< trim END - vim9script - - class C - def Islocked(arg: string): number - return islocked(arg) - enddef - endclass - var obj = C.new() - obj.Islocked("this.notobjmember") - END - v9.CheckSourceFailure(lines, 'E1326: Variable "notobjmember" not found in object "C"') - - # access a script variable through methods - lines =<< trim END - vim9script - - var l = [1] - class C - def Islocked(arg: string): number - return islocked(arg) - enddef - static def SIslocked(arg: string): number - return islocked(arg) - enddef - endclass - var obj = C.new() - assert_equal(0, obj.Islocked("l")) - assert_equal(0, C.SIslocked("l")) - lockvar l - assert_equal(1, obj.Islocked("l")) - assert_equal(1, C.SIslocked("l")) - END - v9.CheckSourceSuccess(lines) -enddef - -" Test for a protected object method -def Test_protected_object_method() - # Try calling a protected method using an object (at the script level) - var lines =<< trim END - vim9script - - class A - def _Foo(): number - return 1234 - enddef - endclass - var a = A.new() - a._Foo() - END - v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 9) - - # Try calling a protected method using an object (from a def function) - lines =<< trim END - vim9script - - class A - def _Foo(): number - return 1234 - enddef - endclass - def T() - var a = A.new() - a._Foo() - enddef - T() - END - v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 2) - - # Use a protected method from another object method (in script context) - lines =<< trim END - vim9script - - class A - def _Foo(): number - return 1234 - enddef - def Bar(): number - return this._Foo() - enddef - endclass - var a = A.new() - assert_equal(1234, a.Bar()) - END - v9.CheckSourceSuccess(lines) - - # Use a protected method from another object method (def function context) - lines =<< trim END - vim9script - - class A - def _Foo(): number - return 1234 - enddef - def Bar(): number - return this._Foo() - enddef - endclass - def T() - var a = A.new() - assert_equal(1234, a.Bar()) - enddef - T() - END - v9.CheckSourceSuccess(lines) - - # Try calling a protected method without the "this" prefix - lines =<< trim END - vim9script - - class A - def _Foo(): number - return 1234 - enddef - def Bar(): number - return _Foo() - enddef - endclass - var a = A.new() - a.Bar() - END - v9.CheckSourceFailure(lines, 'E117: Unknown function: _Foo', 1) - - # Try calling a protected method using the class name - lines =<< trim END - vim9script - - class A - def _Foo(): number - return 1234 - enddef - endclass - A._Foo() - END - v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 8) - - # Define two protected methods with the same name - lines =<< trim END - vim9script - - class A - def _Foo() - enddef - def _Foo() - enddef - endclass - var a = A.new() - END - v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 7) - - # Define a protected method and a object method with the same name - lines =<< trim END - vim9script - - class A - def _Foo() - enddef - def Foo() - enddef - endclass - var a = A.new() - END - v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 7) - - # Define an object method and a protected method with the same name - lines =<< trim END - vim9script - - class A - def Foo() - enddef - def _Foo() - enddef - endclass - var a = A.new() - END - v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 7) - - # Call a public method and a protected method from a protected method - lines =<< trim END - vim9script - - class A - def Foo(): number - return 100 - enddef - def _Bar(): number - return 200 - enddef - def _Baz() - assert_equal(100, this.Foo()) - assert_equal(200, this._Bar()) - enddef - def T() - this._Baz() - enddef - endclass - var a = A.new() - a.T() - END - v9.CheckSourceSuccess(lines) - - # Try calling a protected method from another class - lines =<< trim END - vim9script - - class A - def _Foo(): number - return 100 - enddef - endclass - class B - def Foo(): number - var a = A.new() - a._Foo() - enddef - endclass - var b = B.new() - b.Foo() - END - v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 2) - - # Call a protected object method from a child class object method - lines =<< trim END - vim9script - class A - def _Foo(): number - return 1234 - enddef - endclass - class B extends A - def Bar() - enddef - endclass - class C extends B - def Baz(): number - return this._Foo() - enddef - endclass - var c = C.new() - assert_equal(1234, c.Baz()) - END - v9.CheckSourceSuccess(lines) - - # Call a protected object method from a child class object - lines =<< trim END - vim9script - class A - def _Foo(): number - return 1234 - enddef - endclass - class B extends A - def Bar() - enddef - endclass - class C extends B - def Baz(): number - enddef - endclass - var c = C.new() - assert_equal(1234, c._Foo()) - END - v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 16) - - # Using "_" prefix in a method name should fail outside of a class - lines =<< trim END - vim9script - def _Foo(): number - return 1234 - enddef - var a = _Foo() - END - v9.CheckSourceFailure(lines, 'E1267: Function name must start with a capital: _Foo(): number', 2) - - # Test for initializing a protected funcref instance variable to a protected - # class method from another class - lines =<< trim END - vim9script - - class A - static def _Internal(): string - enddef - endclass - - class B - var Fn: func = A._Internal - endclass - var b = B.new() - END - v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Internal', 1) -enddef - -" Test for an protected class method -def Test_protected_class_method() - # Try calling a class protected method (at the script level) - var lines =<< trim END - vim9script - - class A - static def _Foo(): number - return 1234 - enddef - endclass - A._Foo() - END - v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 8) - - # Try calling a class protected method (from a def function) - lines =<< trim END - vim9script - - class A - static def _Foo(): number - return 1234 - enddef - endclass - def T() - A._Foo() - enddef - T() - END - v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1) - - # Try calling a class protected method using an object (at the script level) - lines =<< trim END - vim9script - - class A - static def _Foo(): number - return 1234 - enddef - endclass - var a = A.new() - a._Foo() - END - v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 9) - - # Try calling a class protected method using an object (from a def function) - lines =<< trim END - vim9script - - class A - static def _Foo(): number - return 1234 - enddef - endclass - def T() - var a = A.new() - a._Foo() - enddef - T() - END - v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 2) - - # Use a class protected method from an object method - lines =<< trim END - vim9script - - class A - static def _Foo(): number - return 1234 - enddef - def Bar() - assert_equal(1234, _Foo()) - enddef - endclass - var a = A.new() - a.Bar() - END - v9.CheckSourceSuccess(lines) - - # Use a class protected method from another class protected method without the - # class name prefix. - lines =<< trim END - vim9script + var obj = C.new() + #lockvar obj.o1 # can't lock something you can't write to - class A - static def _Foo1(): number - return 1234 - enddef - static def _Foo2() - assert_equal(1234, _Foo1()) - enddef - def Bar() - _Foo2() - enddef - endclass - var a = A.new() - a.Bar() - END - v9.CheckSourceSuccess(lines) + try + lockvar obj.o1 # can't lock something you can't write to + call assert_false(1, '"lockvar obj.o1" should have failed') + catch + call assert_exception('E1335:') + endtry - # Declare a class method and a class protected method with the same name - lines =<< trim END - vim9script + LockIt(obj.o1) # but can lock it's value + assert_equal(1, islocked("obj.o1")) + assert_equal(1, islocked("obj.o1[0]")) + assert_equal(1, islocked("obj.o1[1]")) + UnlockIt(obj.o1) + assert_equal(0, islocked("obj.o1")) + assert_equal(0, islocked("obj.o1[0]")) - class A - static def _Foo() - enddef - static def Foo() - enddef - endclass - var a = A.new() - END - v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 7) + lockvar obj.o1[0] + assert_equal(0, islocked("obj.o1")) + assert_equal(1, islocked("obj.o1[0]")) + assert_equal(0, islocked("obj.o1[1]")) + unlockvar obj.o1[0] + assert_equal(0, islocked("obj.o1")) + assert_equal(0, islocked("obj.o1[0]")) - # Try calling a class protected method from another class - lines =<< trim END - vim9script + # Same thing, but with a static - class A - static def _Foo(): number - return 1234 - enddef - endclass - class B - def Foo(): number - return A._Foo() - enddef - endclass - var b = B.new() - assert_equal(1234, b.Foo()) - END - v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1) + try + lockvar C.c1 # can't lock something you can't write to + call assert_false(1, '"lockvar C.c1" should have failed') + catch + call assert_exception('E1335:') + endtry - # Call a protected class method from a child class object method - lines =<< trim END - vim9script - class A - static def _Foo(): number - return 1234 - enddef - endclass - class B extends A - def Bar() - enddef - endclass - class C extends B - def Baz(): number - return A._Foo() - enddef - endclass - var c = C.new() - assert_equal(1234, c.Baz()) + LockIt(C.c1) # but can lock it's value + assert_equal(1, islocked("C.c1")) + assert_equal(1, islocked("C.c1[0]")) + assert_equal(1, islocked("C.c1[1]")) + UnlockIt(C.c1) + assert_equal(0, islocked("C.c1")) + assert_equal(0, islocked("C.c1[0]")) + + lockvar C.c1[0] + assert_equal(0, islocked("C.c1")) + assert_equal(1, islocked("C.c1[0]")) + assert_equal(0, islocked("C.c1[1]")) + unlockvar C.c1[0] + assert_equal(0, islocked("C.c1")) + assert_equal(0, islocked("C.c1[0]")) END - v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1) + v9.CheckSourceSuccess(lines) - # Call a protected class method from a child class protected class method + # Do islocked() from an object method + # and then from a class method lines =<< trim END vim9script - class A - static def _Foo(): number - return 1234 - enddef - endclass - class B extends A - def Bar() + + var l0o0 = [ [0], [1], [2]] + var l0o1 = [ [10], [11], [12]] + var l0c0 = [[120], [121], [122]] + var l0c1 = [[130], [131], [132]] + + class C0 + var o0: list> = l0o0 + var o1: list> = l0o1 + static var c0: list> = l0c0 + static var c1: list> = l0c1 + def Islocked(arg: string): number + return islocked(arg) enddef - endclass - class C extends B - static def Baz(): number - return A._Foo() + static def SIslocked(arg: string): number + return islocked(arg) enddef endclass - assert_equal(1234, C.Baz()) - END - v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1) - # Call a protected class method from a child class object - lines =<< trim END - vim9script - class A - static def _Foo(): number - return 1234 - enddef - endclass - class B extends A - def Bar() + var l2o0 = [[20000], [20001], [20002]] + var l2o1 = [[20010], [20011], [20012]] + var l2c0 = [[20120], [20121], [20122]] + var l2c1 = [[20130], [20131], [20132]] + + class C2 + var o0: list> = l2o0 + var o1: list> = l2o1 + static var c0: list> = l2c0 + static var c1: list> = l2c1 + def Islocked(arg: string): number + return islocked(arg) enddef - endclass - class C extends B - def Baz(): number + static def SIslocked(arg: string): number + return islocked(arg) enddef endclass - var c = C.new() - assert_equal(1234, C._Foo()) - END - v9.CheckSourceFailure(lines, 'E1325: Method "_Foo" not found in class "C"', 16) - # Test for initializing a protected funcref class variable to a protected - # class method from another class - lines =<< trim END - vim9script + var obj0 = C0.new() + var obj2 = C2.new() - class A - static def _Internal(): string - enddef - endclass + var l = [ obj0, null_object, obj2 ] - class B - static var Fn: func = A._Internal - endclass - END - v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Internal', 10) -enddef + # lock list, object func access through script var expr + assert_equal(0, obj0.Islocked("l[0].o0")) + assert_equal(0, obj0.Islocked("l[0].o0[2]")) + lockvar l0o0 + assert_equal(1, obj0.Islocked("l[0].o0")) + assert_equal(1, obj0.Islocked("l[0].o0[2]")) -" Test for using the return value of a class/object method as a function -" argument. -def Test_objmethod_funcarg() - var lines =<< trim END - vim9script + #echo "check-b" obj2.Islocked("l[1].o1") # NULL OBJECT - class C - def Foo(): string - return 'foo' - enddef - endclass + # lock list element, object func access through script var expr + lockvar l0o1[1] + assert_equal(0, obj0.Islocked("this.o1[0]")) + assert_equal(1, obj0.Islocked("this.o1[1]")) - def Bar(a: number, s: string): string - return s - enddef + assert_equal(0, obj0.Islocked("this.o1")) + lockvar l0o1 + assert_equal(1, obj0.Islocked("this.o1")) + unlockvar l0o1 - def Baz(c: C) - assert_equal('foo', Bar(10, c.Foo())) - enddef + lockvar l0c1[1] - var t = C.new() - Baz(t) + # static by class name member expr from same class + assert_equal(0, obj0.Islocked("C0.c1[0]")) + assert_equal(1, obj0.Islocked("C0.c1[1]")) + # static by bare name member expr from same class + assert_equal(0, obj0.Islocked("c1[0]")) + assert_equal(1, obj0.Islocked("c1[1]")) + + # static by class name member expr from other class + assert_equal(0, obj2.Islocked("C0.c1[0]")) + assert_equal(1, obj2.Islocked("C0.c1[1]")) + # static by bare name member expr from other class + assert_equal(0, obj2.Islocked("c1[0]")) + assert_equal(0, obj2.Islocked("c1[1]")) + + + # static by bare name in same class + assert_equal(0, obj0.Islocked("c0")) + lockvar l0c0 + assert_equal(1, obj0.Islocked("c0")) + + # + # similar stuff, but use static method + # + + unlockvar l0o0 + + # lock list, object func access through script var expr + assert_equal(0, C0.SIslocked("l[0].o0")) + assert_equal(0, C0.SIslocked("l[0].o0[2]")) + lockvar l0o0 + assert_equal(1, C0.SIslocked("l[0].o0")) + assert_equal(1, C0.SIslocked("l[0].o0[2]")) + + unlockvar l0o1 + + # can't access "this" from class method + try + C0.SIslocked("this.o1[0]") + call assert_0(1, '"C0.SIslocked("this.o1[0]")" should have failed') + catch + call assert_exception('E121: Undefined variable: this') + endtry + + lockvar l0c1[1] + + # static by class name member expr from same class + assert_equal(0, C0.SIslocked("C0.c1[0]")) + assert_equal(1, C0.SIslocked("C0.c1[1]")) + # static by bare name member expr from same class + assert_equal(0, C0.SIslocked("c1[0]")) + assert_equal(1, C0.SIslocked("c1[1]")) + + # static by class name member expr from other class + assert_equal(0, C2.SIslocked("C0.c1[0]")) + assert_equal(1, C2.SIslocked("C0.c1[1]")) + # static by bare name member expr from other class + assert_equal(0, C2.SIslocked("c1[0]")) + assert_equal(0, C2.SIslocked("c1[1]")) + + + # static by bare name in same class + unlockvar l0c0 + assert_equal(0, C0.SIslocked("c0")) + lockvar l0c0 + assert_equal(1, C0.SIslocked("c0")) END v9.CheckSourceSuccess(lines) + # Check islocked class/object from various places. lines =<< trim END vim9script class C - static def Foo(): string - return 'foo' + def Islocked(arg: string): number + return islocked(arg) + enddef + static def SIslocked(arg: string): number + return islocked(arg) enddef endclass + var obj = C.new() - def Bar(a: number, s: string): string - return s - enddef + # object method + assert_equal(0, obj.Islocked("this")) + assert_equal(0, obj.Islocked("C")) - def Baz() - assert_equal('foo', Bar(10, C.Foo())) - enddef + # class method + ### assert_equal(0, C.SIslocked("this")) + assert_equal(0, C.SIslocked("C")) - Baz() + #script level + var v: number + v = islocked("C") + assert_equal(0, v) + v = islocked("obj") + assert_equal(0, v) END v9.CheckSourceSuccess(lines) enddef -def Test_static_inheritence() - # subclasses get their own static copy +def Test_lockvar_islocked_notfound() + # Try non-existent things var lines =<< trim END vim9script - class A - static var _svar: number - var _mvar: number - def new() - _svar = 1 - this._mvar = 101 - enddef - def AccessObject(): number - return this._mvar + class C + def Islocked(arg: string): number + return islocked(arg) enddef - def AccessStaticThroughObject(): number - return _svar + static def SIslocked(arg: string): number + return islocked(arg) enddef endclass + var obj = C.new() + assert_equal(-1, obj.Islocked("anywhere")) + assert_equal(-1, C.SIslocked("notanywhere")) + END + v9.CheckSourceSuccess(lines) + + # Something not found of the form "name1.name2" is an error + lines =<< trim END + vim9script + + islocked("one.two") + END + v9.CheckSourceFailure(lines, 'E121: Undefined variable: one') - class B extends A - def new() - this._mvar = 102 + lines =<< trim END + vim9script + + class C + var val = { key: "value" } + def Islocked(arg: string): number + return islocked(arg) enddef endclass + var obj = C.new() + obj.Islocked("this.val.not_there")) + END + v9.CheckSourceFailure(lines, 'E716: Key not present in Dictionary: "not_there"') - class C extends B - def new() - this._mvar = 103 - enddef + lines =<< trim END + vim9script - def AccessPrivateStaticThroughClassName(): number - assert_equal(1, A._svar) - return 444 + class C + def Islocked(arg: string): number + return islocked(arg) enddef endclass + var obj = C.new() + obj.Islocked("this.notobjmember") + END + v9.CheckSourceFailure(lines, 'E1326: Variable "notobjmember" not found in object "C"') - 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_fails('echo oc.AccessPrivateStaticThroughClassName()', 'E1333: Cannot access protected variable "_svar" in class "A"') + # access a script variable through methods + lines =<< trim END + vim9script - # verify object properly resolves to correct static - assert_equal(1, oa.AccessStaticThroughObject()) - assert_equal(1, ob.AccessStaticThroughObject()) - assert_equal(1, oc.AccessStaticThroughObject()) + var l = [1] + class C + def Islocked(arg: string): number + return islocked(arg) + enddef + static def SIslocked(arg: string): number + return islocked(arg) + enddef + endclass + var obj = C.new() + assert_equal(0, obj.Islocked("l")) + assert_equal(0, C.SIslocked("l")) + lockvar l + assert_equal(1, obj.Islocked("l")) + assert_equal(1, C.SIslocked("l")) END v9.CheckSourceSuccess(lines) enddef -" Test for declaring duplicate object and class members -def Test_dup_member_variable() - # Duplicate member variable +" Test for a protected object method +def Test_protected_object_method() + # Try calling a protected method using an object (at the script level) var lines =<< trim END vim9script - class C - var val = 10 - var val = 20 + + class A + def _Foo(): number + return 1234 + enddef endclass + var a = A.new() + a._Foo() END - v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4) + v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 9) - # Duplicate protected member variable + # Try calling a protected method using an object (from a def function) lines =<< trim END vim9script - class C - var _val = 10 - var _val = 20 + + class A + def _Foo(): number + return 1234 + enddef endclass + def T() + var a = A.new() + a._Foo() + enddef + T() END - v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 4) + v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 2) - # Duplicate public member variable + # Use a protected method from another object method (in script context) lines =<< trim END vim9script - class C - public var val = 10 - public var val = 20 + + class A + def _Foo(): number + return 1234 + enddef + def Bar(): number + return this._Foo() + enddef endclass + var a = A.new() + assert_equal(1234, a.Bar()) END - v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4) + v9.CheckSourceSuccess(lines) - # Duplicate protected member variable + # Use a protected method from another object method (def function context) lines =<< trim END vim9script - class C - var val = 10 - var _val = 20 + + class A + def _Foo(): number + return 1234 + enddef + def Bar(): number + return this._Foo() + enddef endclass + def T() + var a = A.new() + assert_equal(1234, a.Bar()) + enddef + T() END - v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 4) + v9.CheckSourceSuccess(lines) - # Duplicate public and protected member variable + # Try calling a protected method without the "this" prefix lines =<< trim END vim9script - class C - var _val = 20 - public var val = 10 + + class A + def _Foo(): number + return 1234 + enddef + def Bar(): number + return _Foo() + enddef endclass + var a = A.new() + a.Bar() END - v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4) + v9.CheckSourceFailure(lines, 'E117: Unknown function: _Foo', 1) - # Duplicate class member variable + # Try calling a protected method using the class name lines =<< trim END vim9script - class C - static var s: string = "abc" - static var _s: string = "def" + + class A + def _Foo(): number + return 1234 + enddef endclass + A._Foo() END - v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _s', 4) + v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 8) - # Duplicate public and protected class member variable + # Define two protected methods with the same name lines =<< trim END vim9script - class C - public static var s: string = "abc" - static var _s: string = "def" + + class A + def _Foo() + enddef + def _Foo() + enddef endclass + var a = A.new() END - v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _s', 4) + v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 7) - # Duplicate class and object member variable + # Define a protected method and a object method with the same name lines =<< trim END vim9script - class C - static var val = 10 - var val = 20 - def new() + + class A + def _Foo() + enddef + def Foo() enddef endclass - var c = C.new() - assert_equal(10, C.val) - assert_equal(20, c.val) + var a = A.new() END - v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4) + v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 7) - # Duplicate object member variable in a derived class + # Define an object method and a protected method with the same name lines =<< trim END vim9script + class A - var val = 10 - endclass - class B extends A - endclass - class C extends B - var val = 20 + def Foo() + enddef + def _Foo() + enddef endclass + var a = A.new() END - v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 9) + v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 7) - # Duplicate object protected member variable in a derived class + # Call a public method and a protected method from a protected method lines =<< trim END vim9script + class A - var _val = 10 + def Foo(): number + return 100 + enddef + def _Bar(): number + return 200 + enddef + def _Baz() + assert_equal(100, this.Foo()) + assert_equal(200, this._Bar()) + enddef + def T() + this._Baz() + enddef endclass - class B extends A + var a = A.new() + a.T() + END + v9.CheckSourceSuccess(lines) + + # Try calling a protected method from another class + lines =<< trim END + vim9script + + class A + def _Foo(): number + return 100 + enddef endclass - class C extends B - var _val = 20 + class B + def Foo(): number + var a = A.new() + a._Foo() + enddef endclass + var b = B.new() + b.Foo() END - v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 9) + v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 2) - # Duplicate object protected member variable in a derived class + # Call a protected object method from a child class object method lines =<< trim END vim9script class A - var val = 10 + def _Foo(): number + return 1234 + enddef endclass class B extends A + def Bar() + enddef endclass class C extends B - var _val = 20 + def Baz(): number + return this._Foo() + enddef endclass + var c = C.new() + assert_equal(1234, c.Baz()) END - v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 9) + v9.CheckSourceSuccess(lines) - # Duplicate object member variable in a derived class + # Call a protected object method from a child class object lines =<< trim END vim9script class A - var _val = 10 + def _Foo(): number + return 1234 + enddef endclass class B extends A + def Bar() + enddef endclass class C extends B - var val = 20 + def Baz(): number + enddef endclass + var c = C.new() + assert_equal(1234, c._Foo()) END - v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 9) + v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 16) - # Two member variables with a common prefix + # Using "_" prefix in a method name should fail outside of a class lines =<< trim END vim9script - class A - public static var svar2: number - public static var svar: number - endclass - END - v9.CheckSourceSuccess(lines) -enddef - -" Test for accessing a protected member outside a class in a def function -def Test_protected_member_access_outside_class() - # try to modify a protected instance variable from a def function - var lines =<< trim END - vim9script - class A - var _val = 10 - endclass - def T() - var a = A.new() - a._val = 20 + def _Foo(): number + return 1234 enddef - T() + var a = _Foo() END - v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_val" in class "A"', 2) + v9.CheckSourceFailure(lines, 'E1267: Function name must start with a capital: _Foo(): number', 2) - # access a non-existing protected instance variable from a def function + # Test for initializing a protected funcref instance variable to a protected + # class method from another class lines =<< trim END vim9script + class A - var _val = 10 + static def _Internal(): string + enddef endclass - def T() - var a = A.new() - a._a = 1 - enddef - T() + + class B + var Fn: func = A._Internal + endclass + var b = B.new() END - v9.CheckSourceFailure(lines, 'E1326: Variable "_a" not found in object "A"', 2) + v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Internal', 1) +enddef - # try to read a protected class variable from a def function using an instance - lines =<< trim END +" Test for an protected class method +def Test_protected_class_method() + # Try calling a class protected method (at the script level) + var lines =<< trim END vim9script + class A - static var _val = 10 + static def _Foo(): number + return 1234 + enddef endclass - def T() - var a = A.new() - var x = a._val - enddef - T() + A._Foo() END - v9.CheckSourceFailure(lines, 'E1375: Class variable "_val" accessible only using class "A"', 2) + v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 8) - # try to modify a protected class variable from a def function using an - # instance + # Try calling a class protected method (from a def function) lines =<< trim END vim9script + class A - static var _val = 10 + static def _Foo(): number + return 1234 + enddef endclass def T() - var a = A.new() - a._val = 3 + A._Foo() enddef T() END - v9.CheckSourceFailure(lines, 'E1375: Class variable "_val" accessible only using class "A"', 2) + v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1) - # try to read a protected class variable from a def function using the class + # Try calling a class protected method using an object (at the script level) lines =<< trim END vim9script + class A - static var _val = 10 + static def _Foo(): number + return 1234 + enddef endclass - def T() - var x = A._val - enddef - T() + var a = A.new() + a._Foo() END - v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_val" in class "A"', 1) + v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 9) - # try to modify a protected class variable from a def function using the class + # Try calling a class protected method using an object (from a def function) lines =<< trim END vim9script + class A - static var _val = 10 + static def _Foo(): number + return 1234 + enddef endclass def T() - A._val = 3 + var a = A.new() + a._Foo() enddef T() END - v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_val" in class "A"', 1) + v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 2) - # initialize a protected class variable using a protected class variable - # from another class + # Use a class protected method from an object method lines =<< trim END vim9script + class A - static var _aval = 10 - endclass - class B - static var _bval = A._aval + static def _Foo(): number + return 1234 + enddef + def Bar() + assert_equal(1234, _Foo()) + enddef endclass + var a = A.new() + a.Bar() END - v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_aval" in class "A"', 7) + v9.CheckSourceSuccess(lines) - # initialize a protected instance variable using a protected class variable - # from another class + # Use a class protected method from another class protected method without the + # class name prefix. lines =<< trim END vim9script - class A - static var _aval = 10 - endclass - class B - var _bval = A._aval - endclass - var b = B.new() - END - v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_aval" in class "A"', 1) -enddef -" Test for changing the member access of an interface in a implementation class -def Test_change_interface_member_access() - var lines =<< trim END - vim9script - interface A - var val: number - endinterface - class B implements A - public var val = 10 + class A + static def _Foo1(): number + return 1234 + enddef + static def _Foo2() + assert_equal(1234, _Foo1()) + enddef + def Bar() + _Foo2() + enddef endclass + var a = A.new() + a.Bar() END - v9.CheckSourceFailure(lines, 'E1367: Access level of variable "val" of interface "A" is different', 7) + v9.CheckSourceSuccess(lines) + # Declare a class method and a class protected method with the same name lines =<< trim END vim9script - interface A - var val: number - endinterface - class B implements A - public var val = 10 - endclass - END - v9.CheckSourceFailure(lines, 'E1367: Access level of variable "val" of interface "A" is different', 7) -enddef -" Test for trying to change a readonly member from a def function -def Test_readonly_member_change_in_def_func() - var lines =<< trim END - vim9script class A - var val: number + static def _Foo() + enddef + static def Foo() + enddef endclass - def T() - var a = A.new() - a.val = 20 - enddef - T() + var a = A.new() END - v9.CheckSourceFailure(lines, 'E1335: Variable "val" in class "A" is not writable', 2) -enddef + v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 7) -" Test for reading and writing a class member from a def function -def Test_modify_class_member_from_def_function() - var lines =<< trim END + # Try calling a class protected method from another class + lines =<< trim END vim9script - class A - var var1: number = 10 - public static var var2: list = [1, 2] - public static var var3: dict = {a: 1, b: 2} - static var _priv_var4: number = 40 - endclass - def T() - assert_equal([1, 2], A.var2) - assert_equal({a: 1, b: 2}, A.var3) - A.var2 = [3, 4] - A.var3 = {c: 3, d: 4} - assert_equal([3, 4], A.var2) - assert_equal({c: 3, d: 4}, A.var3) - assert_fails('echo A._priv_var4', 'E1333: Cannot access protected variable "_priv_var4" in class "A"') - enddef - T() - END - v9.CheckSourceSuccess(lines) -enddef -" Test for accessing a class member variable using an object -def Test_class_variable_access_using_object() - var lines =<< trim END - vim9script class A - public static var svar1: list = [1] - public static var svar2: list = [2] - endclass - - A.svar1->add(3) - A.svar2->add(4) - assert_equal([1, 3], A.svar1) - assert_equal([2, 4], A.svar2) - - def Foo() - A.svar1->add(7) - A.svar2->add(8) - assert_equal([1, 3, 7], A.svar1) - assert_equal([2, 4, 8], A.svar2) - enddef - Foo() + static def _Foo(): number + return 1234 + enddef + endclass + class B + def Foo(): number + return A._Foo() + enddef + endclass + var b = B.new() + assert_equal(1234, b.Foo()) END - v9.CheckSourceSuccess(lines) + v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1) - # Cannot read from a class variable using an object in script context + # Call a protected class method from a child class object method lines =<< trim END vim9script class A - public var var1: number - public static var svar2: list = [1] + static def _Foo(): number + return 1234 + enddef endclass - - var a = A.new() - echo a.svar2 + class B extends A + def Bar() + enddef + endclass + class C extends B + def Baz(): number + return A._Foo() + enddef + endclass + var c = C.new() + assert_equal(1234, c.Baz()) END - v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 8) + v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1) - # Cannot write to a class variable using an object in script context + # Call a protected class method from a child class protected class method lines =<< trim END vim9script class A - public var var1: number - public static var svar2: list = [1] + static def _Foo(): number + return 1234 + enddef endclass - - var a = A.new() - a.svar2 = [2] + class B extends A + def Bar() + enddef + endclass + class C extends B + static def Baz(): number + return A._Foo() + enddef + endclass + assert_equal(1234, C.Baz()) END - v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 8) + v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1) - # Cannot read from a class variable using an object in def method context + # Call a protected class method from a child class object lines =<< trim END vim9script class A - public var var1: number - public static var svar2: list = [1] + static def _Foo(): number + return 1234 + enddef endclass - - def T() - var a = A.new() - echo a.svar2 - enddef - T() + class B extends A + def Bar() + enddef + endclass + class C extends B + def Baz(): number + enddef + endclass + var c = C.new() + assert_equal(1234, C._Foo()) END - v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 2) + v9.CheckSourceFailure(lines, 'E1325: Method "_Foo" not found in class "C"', 16) - # Cannot write to a class variable using an object in def method context + # Test for initializing a protected funcref class variable to a protected + # class method from another class lines =<< trim END vim9script + class A - public var var1: number - public static var svar2: list = [1] + static def _Internal(): string + enddef endclass - def T() - var a = A.new() - a.svar2 = [2] - enddef - T() + class B + static var Fn: func = A._Internal + endclass END - v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 2) + v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Internal', 10) enddef -" Test for using a interface method using a child object -def Test_interface_method_from_child() +" Test for using the return value of a class/object method as a function +" argument. +def Test_objmethod_funcarg() var lines =<< trim END vim9script - interface A - def Foo(): string - endinterface - - class B implements A + class C def Foo(): string return 'foo' enddef endclass - class C extends B - def Bar(): string - return 'bar' - enddef - endclass - - def T1(a: A) - assert_equal('foo', a.Foo()) + def Bar(a: number, s: string): string + return s enddef - def T2(b: B) - assert_equal('foo', b.Foo()) + def Baz(c: C) + assert_equal('foo', Bar(10, c.Foo())) enddef - var c = C.new() - T1(c) - T2(c) + var t = C.new() + Baz(t) END v9.CheckSourceSuccess(lines) -enddef -" Test for using an interface method using a child object when it is overridden -" by the child class. -def Test_interface_overridden_method_from_child() - var lines =<< trim END + lines =<< trim END vim9script - interface A - def Foo(): string - endinterface - - class B implements A - def Foo(): string - return 'b-foo' - enddef - endclass - - class C extends B - def Bar(): string - return 'bar' - enddef - def Foo(): string - return 'c-foo' + class C + static def Foo(): string + return 'foo' enddef endclass - def T1(a: A) - assert_equal('c-foo', a.Foo()) + def Bar(a: number, s: string): string + return s enddef - def T2(b: B) - assert_equal('c-foo', b.Foo()) + def Baz() + assert_equal('foo', Bar(10, C.Foo())) enddef - var c = C.new() - T1(c) - T2(c) + Baz() END v9.CheckSourceSuccess(lines) enddef -" Test for interface inheritance -def Test_interface_inheritance() +def Test_static_inheritence() + # subclasses get their own static copy var lines =<< trim END vim9script - interface A - def A_Fn(): string - endinterface - - interface B - def B_Fn(): string - endinterface - - interface C - def C_Fn(): string - endinterface - - class C1 implements A - def A_Fn(): string - return 'c1-a' + class A + static var _svar: number + var _mvar: number + def new() + _svar = 1 + this._mvar = 101 enddef - endclass - - class C2 extends C1 implements B - def B_Fn(): string - return 'c2-b' + def AccessObject(): number + return this._mvar enddef - def A_Fn(): string - return 'c2-a' + def AccessStaticThroughObject(): number + return _svar enddef endclass - class C3 extends C2 implements C - def C_Fn(): string - return 'c3-c' - enddef - def A_Fn(): string - return 'c3-a' - enddef - def B_Fn(): string - return 'c3-b' + class B extends A + def new() + this._mvar = 102 enddef endclass - def T1(a: A, s: string) - assert_equal(s, a.A_Fn()) - enddef - - def T2(b: B, s: string) - assert_equal(s, b.B_Fn()) - enddef - - def T3(c: C, s: string) - assert_equal(s, c.C_Fn()) - enddef - - def T4(c1: C1) - T1(c1, 'c3-a') - enddef - - def T5(c2: C2) - T1(c2, 'c3-a') - T2(c2, 'c3-b') - enddef - - def T6(c3: C3) - T1(c3, 'c3-a') - T2(c3, 'c3-b') - T3(c3, 'c3-c') - enddef - - var o3 = C3.new() - T4(o3) - T5(o3) - T6(o3) - END - v9.CheckSourceSuccess(lines) - - # Both the parent and child classes implement the same interface - lines =<< trim END - vim9script - - interface I - def Foo(): string - endinterface - - class A implements I - def Foo(): string - return 'A-foo' + class C extends B + def new() + this._mvar = 103 enddef - endclass - class B implements I - def Foo(): string - return 'B-foo' + def AccessPrivateStaticThroughClassName(): number + assert_equal(1, A._svar) + return 444 enddef endclass - def Bar(i1: I): string - return i1.Foo() - enddef + 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()) - var b = B.new() - assert_equal('B-foo', Bar(b)) - END - v9.CheckSourceSuccess(lines) -enddef + assert_fails('echo oc.AccessPrivateStaticThroughClassName()', 'E1333: Cannot access protected variable "_svar" in class "A"') -" Test for abstract methods -def Test_abstract_method() - # Use two abstract methods - var lines =<< trim END - vim9script - abstract class A - def M1(): number - return 10 - enddef - abstract def M2(): number - abstract def M3(): number - endclass - class B extends A - def M2(): number - return 20 - enddef - def M3(): number - return 30 - enddef - endclass - var b = B.new() - assert_equal([10, 20, 30], [b.M1(), b.M2(), b.M3()]) + # verify object properly resolves to correct static + assert_equal(1, oa.AccessStaticThroughObject()) + assert_equal(1, ob.AccessStaticThroughObject()) + assert_equal(1, oc.AccessStaticThroughObject()) END v9.CheckSourceSuccess(lines) +enddef - # Don't define an abstract method - lines =<< trim END - vim9script - abstract class A - abstract def Foo() - endclass - class B extends A +" Test for declaring duplicate object and class members +def Test_dup_member_variable() + # Duplicate member variable + var lines =<< trim END + vim9script + class C + var val = 10 + var val = 20 endclass END - v9.CheckSourceFailure(lines, 'E1373: Abstract method "Foo" is not implemented', 6) + v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4) - # Use abstract method in a concrete class + # Duplicate protected member variable lines =<< trim END vim9script - class A - abstract def Foo() - endclass - class B extends A + class C + var _val = 10 + var _val = 20 endclass END - v9.CheckSourceFailure(lines, 'E1372: Abstract method "abstract def Foo()" cannot be defined in a concrete class', 3) + v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 4) - # Use abstract method in an interface + # Duplicate public member variable lines =<< trim END vim9script - interface A - abstract def Foo() - endinterface - class B implements A - def Foo() - enddef + class C + public var val = 10 + public var val = 20 endclass END - v9.CheckSourceFailure(lines, 'E1404: Abstract cannot be used in an interface', 3) + v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4) - # Use abstract static method in an interface + # Duplicate protected member variable lines =<< trim END vim9script - interface A - abstract static def Foo() - enddef - endinterface + class C + var val = 10 + var _val = 20 + endclass END - v9.CheckSourceFailure(lines, 'E1404: Abstract cannot be used in an interface', 3) + v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 4) - # Use abstract static variable in an interface + # Duplicate public and protected member variable lines =<< trim END vim9script - interface A - abstract static foo: number = 10 - endinterface + class C + var _val = 20 + public var val = 10 + endclass END - v9.CheckSourceFailure(lines, 'E1404: Abstract cannot be used in an interface', 3) + v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4) - # Abbreviate the "abstract" keyword + # Duplicate class member variable lines =<< trim END vim9script - class A - abs def Foo() + class C + static var s: string = "abc" + static var _s: string = "def" endclass END - v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: abs def Foo()', 3) + v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _s', 4) - # Use "abstract" with a member variable + # Duplicate public and protected class member variable lines =<< trim END vim9script - abstract class A - abstract this.val = 10 + class C + public static var s: string = "abc" + static var _s: string = "def" endclass END - v9.CheckSourceFailure(lines, 'E1371: Abstract must be followed by "def"', 3) + v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _s', 4) - # Use a static abstract method + # Duplicate class and object member variable lines =<< trim END vim9script - abstract class A - abstract static def Foo(): number + class C + static var val = 10 + var val = 20 + def new() + enddef endclass + var c = C.new() + assert_equal(10, C.val) + assert_equal(20, c.val) END - v9.CheckSourceFailure(lines, 'E1371: Abstract must be followed by "def"', 3) + v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4) - # Type mismatch between abstract method and concrete method + # Duplicate object member variable in a derived class lines =<< trim END vim9script - abstract class A - abstract def Foo(a: string, b: number): list + class A + var val = 10 endclass class B extends A - def Foo(a: number, b: string): list - return [] - enddef + endclass + class C extends B + var val = 20 endclass END - v9.CheckSourceFailure(lines, 'E1383: Method "Foo": type mismatch, expected func(string, number): list but got func(number, string): list', 9) + v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 9) - # Invoke an abstract method from a def function + # Duplicate object protected member variable in a derived class lines =<< trim END vim9script - abstract class A - abstract def Foo(): list + class A + var _val = 10 endclass class B extends A - def Foo(): list - return [3, 5] - enddef endclass - def Bar(c: B) - assert_equal([3, 5], c.Foo()) - enddef - var b = B.new() - Bar(b) + class C extends B + var _val = 20 + endclass END - v9.CheckSourceSuccess(lines) + v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 9) - # Use a static method in an abstract class + # Duplicate object protected member variable in a derived class lines =<< trim END vim9script - abstract class A - static def Foo(): string - return 'foo' - enddef + class A + var val = 10 + endclass + class B extends A + endclass + class C extends B + var _val = 20 endclass - assert_equal('foo', A.Foo()) END - v9.CheckSourceSuccess(lines) + v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 9) - # Invoke method returning a value through the abstract class. See #15432. + # Duplicate object member variable in a derived class lines =<< trim END vim9script - - abstract class A - abstract def String(): string + class A + var _val = 10 endclass - class B extends A - def String(): string - return 'B' - enddef endclass - - def F(o: A) - assert_equal('B', o.String()) - enddef - F(B.new()) + class C extends B + var val = 20 + endclass END - v9.CheckSourceSuccess(lines) + v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 9) - # Invoke abstract method returning a value does not compile + # Two member variables with a common prefix lines =<< trim END vim9script - - abstract class A - abstract def String(): string - return 'X' - enddef + class A + public static var svar2: number + public static var svar: number endclass END - v9.CheckScriptFailure(lines, "E1318: Not a valid command in a class: return 'X'") + v9.CheckSourceSuccess(lines) enddef -" Test for calling a class method from a subclass -def Test_class_method_call_from_subclass() - # class method call from a subclass +" Test for accessing a protected member outside a class in a def function +def Test_protected_member_access_outside_class() + # try to modify a protected instance variable from a def function var lines =<< trim END vim9script + class A + var _val = 10 + endclass + def T() + var a = A.new() + a._val = 20 + enddef + T() + END + v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_val" in class "A"', 2) + # access a non-existing protected instance variable from a def function + lines =<< trim END + vim9script class A - static def Foo() - echo "foo" - enddef + var _val = 10 endclass + def T() + var a = A.new() + a._a = 1 + enddef + T() + END + v9.CheckSourceFailure(lines, 'E1326: Variable "_a" not found in object "A"', 2) - class B extends A - def Bar() - Foo() - enddef + # try to read a protected class variable from a def function using an instance + lines =<< trim END + vim9script + class A + static var _val = 10 endclass + def T() + var a = A.new() + var x = a._val + enddef + T() + END + v9.CheckSourceFailure(lines, 'E1375: Class variable "_val" accessible only using class "A"', 2) - var b = B.new() - b.Bar() + # try to modify a protected class variable from a def function using an + # instance + lines =<< trim END + vim9script + class A + static var _val = 10 + endclass + def T() + var a = A.new() + a._val = 3 + enddef + T() END - v9.CheckSourceFailure(lines, 'E1384: Class method "Foo" accessible only inside class "A"', 1) -enddef + v9.CheckSourceFailure(lines, 'E1375: Class variable "_val" accessible only using class "A"', 2) -" 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 + # try to read a protected class variable from a def function using the class + 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 + static var _val = 10 + endclass + def T() + var x = A._val + enddef + T() + END + v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_val" in class "A"', 1) + + # try to modify a protected class variable from a def function using the class + lines =<< trim END + vim9script + class A + static var _val = 10 endclass - def T() - assert_equal(['a', 'b'], A.Foo()) - var t_a = A.new() - t_a.Bar() + A._val = 3 enddef - - assert_equal(['a', 'b'], A.Foo()) - var a = A.new() - a.Bar() T() END - v9.CheckSourceSuccess(lines) + v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_val" in class "A"', 1) - # script context + # initialize a protected class variable using a protected class variable + # from another class lines =<< trim END vim9script class A - static def Foo(): string - return 'foo' - enddef + static var _aval = 10 + endclass + class B + static var _bval = A._aval endclass - - var a = A.new() - assert_equal('foo', a.Foo()) END - v9.CheckSourceFailure(lines, 'E1385: Class method "Foo" accessible only using class "A"', 9) + v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_aval" in class "A"', 7) - # def function context + # initialize a protected instance variable using a protected class variable + # from another class lines =<< trim END vim9script class A - static def Foo(): string - return 'foo' - enddef + static var _aval = 10 endclass + class B + var _bval = A._aval + endclass + var b = B.new() + END + v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_aval" in class "A"', 1) +enddef +" Test for trying to change a readonly member from a def function +def Test_readonly_member_change_in_def_func() + var lines =<< trim END + vim9script + class A + var val: number + endclass def T() var a = A.new() - assert_equal('foo', a.Foo()) + a.val = 20 enddef T() END - v9.CheckSourceFailure(lines, 'E1385: Class method "Foo" accessible only using class "A"', 2) + v9.CheckSourceFailure(lines, 'E1335: Variable "val" in class "A" is not writable', 2) enddef -def Test_class_variable() +" Test for reading and writing a class member from a def function +def Test_modify_class_member_from_def_function() var lines =<< trim END vim9script - class A - public static var val: number = 10 - static def ClassFunc() - assert_equal(10, val) - enddef - def ObjFunc() - assert_equal(10, val) - enddef + var var1: number = 10 + public static var var2: list = [1, 2] + public static var var3: dict = {a: 1, b: 2} + static var _priv_var4: number = 40 endclass + def T() + assert_equal([1, 2], A.var2) + assert_equal({a: 1, b: 2}, A.var3) + A.var2 = [3, 4] + A.var3 = {c: 3, d: 4} + assert_equal([3, 4], A.var2) + assert_equal({c: 3, d: 4}, A.var3) + assert_fails('echo A._priv_var4', 'E1333: Cannot access protected variable "_priv_var4" in class "A"') + enddef + T() + END + v9.CheckSourceSuccess(lines) +enddef - class B extends A +" Test for accessing a class member variable using an object +def Test_class_variable_access_using_object() + var lines =<< trim END + vim9script + class A + public static var svar1: list = [1] + public static var svar2: list = [2] endclass - assert_equal(10, A.val) - A.ClassFunc() - var a = A.new() - a.ObjFunc() - var b = B.new() - b.ObjFunc() + A.svar1->add(3) + A.svar2->add(4) + assert_equal([1, 3], A.svar1) + assert_equal([2, 4], A.svar2) - def T1(a1: A) - a1.ObjFunc() - A.ClassFunc() + def Foo() + A.svar1->add(7) + A.svar2->add(8) + assert_equal([1, 3, 7], A.svar1) + assert_equal([2, 4, 8], A.svar2) enddef - T1(b) - - A.val = 20 - assert_equal(20, A.val) + Foo() END v9.CheckSourceSuccess(lines) - # Modifying a parent class variable from a child class method + # Cannot read from a class variable using an object in script context lines =<< trim END vim9script - class A - static var val: number = 10 + public var var1: number + public static var svar2: list = [1] endclass - class B extends A - static def ClassFunc() - val = 20 - enddef - endclass - B.ClassFunc() + var a = A.new() + echo a.svar2 END - v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1) + v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 8) - # Reading a parent class variable from a child class method + # Cannot write to a class variable using an object in script context lines =<< trim END vim9script - class A - static var val: number = 10 + public var var1: number + public static var svar2: list = [1] endclass - class B extends A - static def ClassFunc() - var i = val - enddef - endclass - B.ClassFunc() + var a = A.new() + a.svar2 = [2] END - v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1) + v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 8) - # Modifying a parent class variable from a child object method + # Cannot read from a class variable using an object in def method context lines =<< trim END vim9script - class A - static var val: number = 10 + public var var1: number + public static var svar2: list = [1] endclass - class B extends A - def ObjFunc() - val = 20 - enddef - endclass - var b = B.new() - b.ObjFunc() + def T() + var a = A.new() + echo a.svar2 + enddef + T() END - v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1) + v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 2) - # Reading a parent class variable from a child object method + # Cannot write to a class variable using an object in def method context lines =<< trim END vim9script - class A - static var val: number = 10 + public var var1: number + public static var svar2: list = [1] endclass + def T() + var a = A.new() + a.svar2 = [2] + enddef + T() + END + v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 2) +enddef + +" Test for abstract methods +def Test_abstract_method() + # Use two abstract methods + var lines =<< trim END + vim9script + abstract class A + def M1(): number + return 10 + enddef + abstract def M2(): number + abstract def M3(): number + endclass class B extends A - def ObjFunc() - var i = val + def M2(): number + return 20 + enddef + def M3(): number + return 30 enddef endclass var b = B.new() - b.ObjFunc() + assert_equal([10, 20, 30], [b.M1(), b.M2(), b.M3()]) END - v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1) + v9.CheckSourceSuccess(lines) - # Modifying a class variable using an object at script level + # Don't define an abstract method lines =<< trim END vim9script - - class A - static var val: number = 10 + abstract class A + abstract def Foo() endclass - var a = A.new() - a.val = 20 - END - v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 7) - - # Reading a class variable using an object at script level - lines =<< trim END - vim9script - - class A - static var val: number = 10 + class B extends A endclass - var a = A.new() - var i = a.val END - v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 7) + v9.CheckSourceFailure(lines, 'E1373: Abstract method "Foo" is not implemented', 6) - # Modifying a class variable using an object at function level + # Use abstract method in a concrete class lines =<< trim END vim9script - class A - static var val: number = 10 + abstract def Foo() + endclass + class B extends A endclass - - def T() - var a = A.new() - a.val = 20 - enddef - T() END - v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 2) + v9.CheckSourceFailure(lines, 'E1372: Abstract method "abstract def Foo()" cannot be defined in a concrete class', 3) - # Reading a class variable using an object at function level + # Use abstract method in an interface lines =<< trim END vim9script - - class A - static var val: number = 10 - endclass - def T() - var a = A.new() - var i = a.val - enddef - T() + interface A + abstract def Foo() + endinterface + class B implements A + def Foo() + enddef + endclass END - v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 2) + v9.CheckSourceFailure(lines, 'E1404: Abstract cannot be used in an interface', 3) - # Use old implicit var declaration syntax (without initialization) + # Use abstract static method in an interface lines =<< trim END vim9script - - class A - static val: number - endclass + interface A + abstract static def Foo() + enddef + endinterface END - v9.CheckSourceFailure(lines, 'E1368: Static must be followed by "var" or "def"', 4) + v9.CheckSourceFailure(lines, 'E1404: Abstract cannot be used in an interface', 3) - # Use old implicit var declaration syntax (with initialization) + # Use abstract static variable in an interface lines =<< trim END vim9script - - class A - static val: number = 10 - endclass + interface A + abstract static foo: number = 10 + endinterface END - v9.CheckSourceFailure(lines, 'E1368: Static must be followed by "var" or "def"', 4) + v9.CheckSourceFailure(lines, 'E1404: Abstract cannot be used in an interface', 3) - # Use old implicit var declaration syntax (type inferred) + # Abbreviate the "abstract" keyword lines =<< trim END vim9script - class A - static val = 10 + abs def Foo() endclass END - v9.CheckSourceFailure(lines, 'E1368: Static must be followed by "var" or "def"', 4) + v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: abs def Foo()', 3) - # Missing ":var" in "var" class variable declaration (without initialization) + # Use "abstract" with a member variable lines =<< trim END vim9script - - class A - static var: number + abstract class A + abstract this.val = 10 endclass END - v9.CheckSourceFailure(lines, 'E1329: Invalid class variable declaration: static var: number', 4) + v9.CheckSourceFailure(lines, 'E1371: Abstract must be followed by "def"', 3) - # Missing ":var" in "var" class variable declaration (with initialization) + # Use a static abstract method lines =<< trim END vim9script - - class A - static var: number = 10 + abstract class A + abstract static def Foo(): number endclass END - v9.CheckSourceFailure(lines, 'E1329: Invalid class variable declaration: static var: number = 10', 4) + v9.CheckSourceFailure(lines, 'E1371: Abstract must be followed by "def"', 3) - # Missing ":var" in "var" class variable declaration (type inferred) + # Type mismatch between abstract method and concrete method lines =<< trim END vim9script - - class A - static var = 10 + abstract class A + abstract def Foo(a: string, b: number): list + endclass + class B extends A + def Foo(a: number, b: string): list + return [] + enddef endclass END - v9.CheckSourceFailure(lines, 'E1329: Invalid class variable declaration: static var = 10', 4) - -enddef + v9.CheckSourceFailure(lines, 'E1383: Method "Foo": type mismatch, expected func(string, number): list but got func(number, string): list', 9) -" Test for using a duplicate class method and class variable in a child class -def Test_dup_class_member() - # duplicate class variable, class method and overridden object method - var lines =<< trim END + # Invoke an abstract method from a def function + lines =<< trim END vim9script - class A - static var sval = 100 - static def Check() - assert_equal(100, sval) - enddef - def GetVal(): number - return sval - enddef + abstract class A + abstract def Foo(): list endclass - class B extends A - static var sval = 200 - static def Check() - assert_equal(200, sval) - enddef - def GetVal(): number - return sval + def Foo(): list + return [3, 5] enddef endclass - - def T1(aa: A): number - return aa.GetVal() - enddef - - def T2(bb: B): number - return bb.GetVal() + def Bar(c: B) + assert_equal([3, 5], c.Foo()) enddef - - assert_equal(100, A.sval) - assert_equal(200, B.sval) - var a = A.new() - assert_equal(100, a.GetVal()) var b = B.new() - assert_equal(200, b.GetVal()) - assert_equal(200, T1(b)) - assert_equal(200, T2(b)) + Bar(b) END v9.CheckSourceSuccess(lines) - # duplicate class variable and class method + # Use a static method in an abstract class lines =<< trim END vim9script - class A - static var sval = 100 - static def Check() - assert_equal(100, sval) - enddef - def GetVal(): number - return sval + abstract class A + static def Foo(): string + return 'foo' enddef endclass + assert_equal('foo', A.Foo()) + END + v9.CheckSourceSuccess(lines) - class B extends A - static var sval = 200 - static def Check() - assert_equal(200, sval) - enddef + # Invoke method returning a value through the abstract class. See #15432. + lines =<< trim END + vim9script + + abstract class A + abstract def String(): string endclass - def T1(aa: A): number - return aa.GetVal() - enddef + class B extends A + def String(): string + return 'B' + enddef + endclass - def T2(bb: B): number - return bb.GetVal() + def F(o: A) + assert_equal('B', o.String()) enddef - - assert_equal(100, A.sval) - assert_equal(200, B.sval) - var a = A.new() - assert_equal(100, a.GetVal()) - var b = B.new() - assert_equal(100, b.GetVal()) - assert_equal(100, T1(b)) - assert_equal(100, T2(b)) + F(B.new()) END v9.CheckSourceSuccess(lines) -enddef - -" Test for calling an instance method using the class -def Test_instance_method_call_using_class() - # Invoke an object method using a class in script context - var lines =<< trim END - vim9script - class A - def Foo() - echo "foo" - enddef - endclass - A.Foo() - END - v9.CheckSourceFailure(lines, 'E1386: Object method "Foo" accessible only using class "A" object', 7) - # Invoke an object method using a class in def function context + # Invoke abstract method returning a value does not compile lines =<< trim END vim9script - class A - def Foo() - echo "foo" + + abstract class A + abstract def String(): string + return 'X' enddef endclass - def T() - A.Foo() - enddef - T() END - v9.CheckSourceFailure(lines, 'E1386: Object method "Foo" accessible only using class "A" object', 1) + v9.CheckScriptFailure(lines, "E1318: Not a valid command in a class: return 'X'") enddef -" Test for duplicate class method and instance method -def Test_dup_classmethod_objmethod() - # Duplicate instance method +" Test for calling a class method from a subclass +def Test_class_method_call_from_subclass() + # class method call from a subclass var lines =<< trim END vim9script + class A static def Foo() - enddef - def Foo() + echo "foo" enddef endclass - END - v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 6) - # Duplicate protected instance method - lines =<< trim END - vim9script - class A - static def Foo() - enddef - def _Foo() + class B extends A + def Bar() + Foo() enddef endclass + + var b = B.new() + b.Bar() END - v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 6) + v9.CheckSourceFailure(lines, 'E1384: Class method "Foo" accessible only inside class "A"', 1) +enddef - # Duplicate class method - lines =<< trim END +" 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 - def Foo() + static def Foo(): list + return ['a', 'b'] enddef - static def Foo() + 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.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 6) + v9.CheckSourceSuccess(lines) - # Duplicate protected class method + # script context lines =<< trim END vim9script class A - def Foo() - enddef - static def _Foo() + static def Foo(): string + return 'foo' enddef endclass + + var a = A.new() + assert_equal('foo', a.Foo()) END - v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 6) + v9.CheckSourceFailure(lines, 'E1385: Class method "Foo" accessible only using class "A"', 9) - # Duplicate protected class and object method + # def function context lines =<< trim END vim9script class A - def _Foo() - enddef - static def _Foo() + static def Foo(): string + return 'foo' enddef endclass + + def T() + var a = A.new() + assert_equal('foo', a.Foo()) + enddef + T() END - v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 6) + v9.CheckSourceFailure(lines, 'E1385: Class method "Foo" accessible only using class "A"', 2) enddef -" Test for an instance method access level comparison with parent instance -" methods. -def Test_instance_method_access_level() - # protected method in subclass +def Test_class_variable() var lines =<< trim END vim9script + class A - def Foo() + public static var val: number = 10 + static def ClassFunc() + assert_equal(10, val) + enddef + def ObjFunc() + assert_equal(10, val) enddef endclass + class B extends A endclass - class C extends B - def _Foo() - enddef - endclass + + assert_equal(10, A.val) + A.ClassFunc() + var a = A.new() + a.ObjFunc() + var b = B.new() + b.ObjFunc() + + def T1(a1: A) + a1.ObjFunc() + A.ClassFunc() + enddef + T1(b) + + A.val = 20 + assert_equal(20, A.val) END - v9.CheckSourceFailure(lines, 'E1377: Access level of method "_Foo" is different in class "A"', 11) + v9.CheckSourceSuccess(lines) - # Public method in subclass + # Modifying a parent class variable from a child class method lines =<< trim END vim9script + class A - def _Foo() - enddef + static var val: number = 10 endclass + class B extends A - endclass - class C extends B - def Foo() + static def ClassFunc() + val = 20 enddef endclass + B.ClassFunc() END - v9.CheckSourceFailure(lines, 'E1377: Access level of method "Foo" is different in class "A"', 11) -enddef + v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1) -def Test_extend_empty_class() - var lines =<< trim END + # Reading a parent class variable from a child class method + lines =<< trim END vim9script + class A + static var val: number = 10 endclass + class B extends A - endclass - class C extends B - public static var rw_class_var = 1 - public var rw_obj_var = 2 - static def ClassMethod(): number - return 3 - enddef - def ObjMethod(): number - return 4 + static def ClassFunc() + var i = val enddef endclass - assert_equal(1, C.rw_class_var) - assert_equal(3, C.ClassMethod()) - var c = C.new() - assert_equal(2, c.rw_obj_var) - assert_equal(4, c.ObjMethod()) - END - v9.CheckSourceSuccess(lines) -enddef - -" A interface cannot have a static variable or a static method or a protected -" variable or a protected method or a public variable -def Test_interface_with_unsupported_members() - var lines =<< trim END - vim9script - interface A - static var num: number - endinterface + B.ClassFunc() END - v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3) + v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1) + # Modifying a parent class variable from a child object method lines =<< trim END vim9script - interface A - static var _num: number - endinterface - END - v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3) - lines =<< trim END - vim9script - interface A - public static var num: number - endinterface - END - v9.CheckSourceFailure(lines, 'E1387: public variable not supported in an interface', 3) + class A + static var val: number = 10 + endclass - lines =<< trim END - vim9script - interface A - public static var num: number - endinterface + class B extends A + def ObjFunc() + val = 20 + enddef + endclass + var b = B.new() + b.ObjFunc() END - v9.CheckSourceFailure(lines, 'E1387: public variable not supported in an interface', 3) + v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1) + # Reading a parent class variable from a child object method lines =<< trim END vim9script - interface A - static var _num: number - endinterface - END - v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3) - lines =<< trim END - vim9script - interface A - static def Foo(d: dict): list - endinterface + class A + static var val: number = 10 + endclass + + class B extends A + def ObjFunc() + var i = val + enddef + endclass + var b = B.new() + b.ObjFunc() END - v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3) + v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1) + # Modifying a class variable using an object at script level lines =<< trim END vim9script - interface A - static def _Foo(d: dict): list - endinterface + + class A + static var val: number = 10 + endclass + var a = A.new() + a.val = 20 END - v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3) + v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 7) + # Reading a class variable using an object at script level lines =<< trim END vim9script - interface A - var _Foo: list - endinterface + + class A + static var val: number = 10 + endclass + var a = A.new() + var i = a.val END - v9.CheckSourceFailure(lines, 'E1379: Protected variable not supported in an interface', 3) + v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 7) + # Modifying a class variable using an object at function level lines =<< trim END vim9script - interface A - def _Foo(d: dict): list - endinterface - END - v9.CheckSourceFailure(lines, 'E1380: Protected method not supported in an interface', 3) -enddef -" Test for extending an interface -def Test_extend_interface() - var lines =<< trim END - vim9script - interface A - var var1: list - def Foo() - endinterface - interface B extends A - var var2: dict - def Bar() - endinterface - class C implements A, B - var var1 = [1, 2] - def Foo() - enddef - var var2 = {a: '1'} - def Bar() - enddef + class A + static var val: number = 10 endclass + + def T() + var a = A.new() + a.val = 20 + enddef + T() END - v9.CheckSourceSuccess(lines) + v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 2) - # extending empty interface + # Reading a class variable using an object at function level lines =<< trim END vim9script - interface A - endinterface - interface B extends A - endinterface - class C implements B + + class A + static var val: number = 10 endclass + def T() + var a = A.new() + var i = a.val + enddef + T() END - v9.CheckSourceSuccess(lines) + v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 2) + # Use old implicit var declaration syntax (without initialization) lines =<< trim END vim9script - interface A - def Foo() - endinterface - interface B extends A - var var2: dict - endinterface - class C implements A, B - var var2 = {a: '1'} + + class A + static val: number endclass END - v9.CheckSourceFailure(lines, 'E1349: Method "Foo" of interface "A" is not implemented', 10) + v9.CheckSourceFailure(lines, 'E1368: Static must be followed by "var" or "def"', 4) + # Use old implicit var declaration syntax (with initialization) lines =<< trim END vim9script - interface A - def Foo() - endinterface - interface B extends A - var var2: dict - endinterface - class C implements A, B - def Foo() - enddef + + class A + static val: number = 10 endclass END - v9.CheckSourceFailure(lines, 'E1348: Variable "var2" of interface "B" is not implemented', 11) + v9.CheckSourceFailure(lines, 'E1368: Static must be followed by "var" or "def"', 4) - # interface cannot extend a class + # Use old implicit var declaration syntax (type inferred) lines =<< trim END vim9script + class A + static val = 10 endclass - interface B extends A - endinterface END - v9.CheckSourceFailure(lines, 'E1354: Cannot extend A', 5) + v9.CheckSourceFailure(lines, 'E1368: Static must be followed by "var" or "def"', 4) - # class cannot extend an interface + # Missing ":var" in "var" class variable declaration (without initialization) lines =<< trim END vim9script - interface A - endinterface - class B extends A + + class A + static var: number endclass END - v9.CheckSourceFailure(lines, 'E1354: Cannot extend A', 5) + v9.CheckSourceFailure(lines, 'E1329: Invalid class variable declaration: static var: number', 4) - # interface cannot implement another interface + # Missing ":var" in "var" class variable declaration (with initialization) lines =<< trim END vim9script - interface A - endinterface - interface B implements A - endinterface + + class A + static var: number = 10 + endclass END - v9.CheckSourceFailure(lines, 'E1381: Interface cannot use "implements"', 4) + v9.CheckSourceFailure(lines, 'E1329: Invalid class variable declaration: static var: number = 10', 4) - # interface cannot extend multiple interfaces + # Missing ":var" in "var" class variable declaration (type inferred) lines =<< trim END vim9script - interface A - endinterface - interface B - endinterface - interface C extends A, B - endinterface + + class A + static var = 10 + endclass END - v9.CheckSourceFailure(lines, 'E1315: White space required after name: A, B', 6) + v9.CheckSourceFailure(lines, 'E1329: Invalid class variable declaration: static var = 10', 4) - # Variable type in an extended interface is of different type - lines =<< trim END - vim9script - interface A - var val1: number - endinterface - interface B extends A - var val2: string - endinterface - interface C extends B - var val1: string - var val2: number - endinterface - END - v9.CheckSourceFailure(lines, 'E1382: Variable "val1": type mismatch, expected number but got string', 11) enddef -" Test for a child class implementing an interface when some of the methods are -" defined in the parent class. -def Test_child_class_implements_interface() +" Test for using a duplicate class method and class variable in a child class +def Test_dup_class_member() + # duplicate class variable, class method and overridden object method var lines =<< trim END vim9script - - interface Intf - def F1(): list> - def F2(): list> - def F3(): list> - var var1: list> - var var2: list> - var var3: list> - endinterface - class A - def A1() + static var sval = 100 + static def Check() + assert_equal(100, sval) enddef - def F3(): list> - return [[3]] + def GetVal(): number + return sval enddef - var v1: list> = [[0]] - var var3 = [{c: 30}] endclass class B extends A - def B1() + static var sval = 200 + static def Check() + assert_equal(200, sval) enddef - def F2(): list> - return [[2]] + def GetVal(): number + return sval enddef - var v2: list> = [[0]] - var var2 = [{b: 20}] endclass - class C extends B implements Intf - def C1() - enddef - def F1(): list> - return [[1]] - enddef - var v3: list> = [[0]] - var var1 = [{a: 10}] - endclass + def T1(aa: A): number + return aa.GetVal() + enddef - def T(if: Intf) - assert_equal([[1]], if.F1()) - assert_equal([[2]], if.F2()) - assert_equal([[3]], if.F3()) - assert_equal([{a: 10}], if.var1) - assert_equal([{b: 20}], if.var2) - assert_equal([{c: 30}], if.var3) + def T2(bb: B): number + return bb.GetVal() enddef - var c = C.new() - T(c) - assert_equal([[1]], c.F1()) - assert_equal([[2]], c.F2()) - assert_equal([[3]], c.F3()) - assert_equal([{a: 10}], c.var1) - assert_equal([{b: 20}], c.var2) - assert_equal([{c: 30}], c.var3) + assert_equal(100, A.sval) + assert_equal(200, B.sval) + var a = A.new() + assert_equal(100, a.GetVal()) + var b = B.new() + assert_equal(200, b.GetVal()) + assert_equal(200, T1(b)) + assert_equal(200, T2(b)) END v9.CheckSourceSuccess(lines) - # One of the interface methods is not found + # duplicate class variable and class method lines =<< trim END vim9script - - interface Intf - def F1() - def F2() - def F3() - endinterface - class A - def A1() + static var sval = 100 + static def Check() + assert_equal(100, sval) + enddef + def GetVal(): number + return sval enddef endclass class B extends A - def B1() - enddef - def F2() + static var sval = 200 + static def Check() + assert_equal(200, sval) enddef endclass - class C extends B implements Intf - def C1() - enddef - def F1() + def T1(aa: A): number + return aa.GetVal() + enddef + + def T2(bb: B): number + return bb.GetVal() + enddef + + assert_equal(100, A.sval) + assert_equal(200, B.sval) + var a = A.new() + assert_equal(100, a.GetVal()) + var b = B.new() + assert_equal(100, b.GetVal()) + assert_equal(100, T1(b)) + assert_equal(100, T2(b)) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for calling an instance method using the class +def Test_instance_method_call_using_class() + # Invoke an object method using a class in script context + var lines =<< trim END + vim9script + class A + def Foo() + echo "foo" enddef endclass + A.Foo() END - v9.CheckSourceFailure(lines, 'E1349: Method "F3" of interface "Intf" is not implemented', 26) + v9.CheckSourceFailure(lines, 'E1386: Object method "Foo" accessible only using class "A" object', 7) - # One of the interface methods is of different type + # Invoke an object method using a class in def function context lines =<< trim END vim9script - - interface Intf - def F1() - def F2() - def F3() - endinterface - class A - def F3(): number - return 0 - enddef - def A1() + def Foo() + echo "foo" enddef endclass + def T() + A.Foo() + enddef + T() + END + v9.CheckSourceFailure(lines, 'E1386: Object method "Foo" accessible only using class "A" object', 1) +enddef - class B extends A - def B1() +" Test for duplicate class method and instance method +def Test_dup_classmethod_objmethod() + # Duplicate instance method + var lines =<< trim END + vim9script + class A + static def Foo() enddef - def F2() + def Foo() enddef endclass + END + v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 6) - class C extends B implements Intf - def C1() + # Duplicate protected instance method + lines =<< trim END + vim9script + class A + static def Foo() enddef - def F1() + def _Foo() enddef endclass END - v9.CheckSourceFailure(lines, 'E1383: Method "F3": type mismatch, expected func() but got func(): number', 29) + v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 6) - # One of the interface variables is not present + # Duplicate class method lines =<< trim END vim9script - - interface Intf - var var1: list> - var var2: list> - var var3: list> - endinterface - class A - var v1: list> = [[0]] - endclass - - class B extends A - var v2: list> = [[0]] - var var2 = [{b: 20}] - endclass - - class C extends B implements Intf - var v3: list> = [[0]] - var var1 = [{a: 10}] + def Foo() + enddef + static def Foo() + enddef endclass END - v9.CheckSourceFailure(lines, 'E1348: Variable "var3" of interface "Intf" is not implemented', 21) + v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 6) - # One of the interface variables is of different type + # Duplicate protected class method lines =<< trim END vim9script - - interface Intf - var var1: list> - var var2: list> - var var3: list> - endinterface - class A - var v1: list> = [[0]] - var var3: list> - endclass - - class B extends A - var v2: list> = [[0]] - var var2 = [{b: 20}] - endclass - - class C extends B implements Intf - var v3: list> = [[0]] - var var1 = [{a: 10}] + def Foo() + enddef + static def _Foo() + enddef endclass END - v9.CheckSourceFailure(lines, 'E1382: Variable "var3": type mismatch, expected list> but got list>', 22) -enddef + v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 6) -" Test for extending an interface with duplicate variables and methods -def Test_interface_extends_with_dup_members() - var lines =<< trim END + # Duplicate protected class and object method + lines =<< trim END vim9script - interface A - var n1: number - def Foo1(): number - endinterface - interface B extends A - var n2: number - var n1: number - def Foo2(): number - def Foo1(): number - endinterface - class C implements B - var n1 = 10 - var n2 = 20 - def Foo1(): number - return 30 + class A + def _Foo() enddef - def Foo2(): number - return 40 + static def _Foo() enddef endclass - def T1(a: A) - assert_equal(10, a.n1) - assert_equal(30, a.Foo1()) - enddef - def T2(b: B) - assert_equal(10, b.n1) - assert_equal(20, b.n2) - assert_equal(30, b.Foo1()) - assert_equal(40, b.Foo2()) - enddef - var c = C.new() - T1(c) - T2(c) END - v9.CheckSourceSuccess(lines) + v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 6) enddef -" Test for implementing an interface with different ordering for the interface -" member variables. -def Test_implement_interface_with_different_variable_order() +" Test for an instance method access level comparison with parent instance +" methods. +def Test_instance_method_access_level() + # protected method in subclass var lines =<< trim END vim9script - - interface IX - var F: func(): string - endinterface - - class X implements IX - var x: number - var F: func(): string = () => 'ok' + class A + def Foo() + enddef endclass - - def Foo(ix: IX): string - return ix.F() - enddef - - var x0 = X.new(0) - assert_equal('ok', Foo(x0)) - END - v9.CheckSourceSuccess(lines) -enddef - -" Test for inheriting interfaces from an imported super class -def Test_interface_inheritance_with_imported_super() - var lines =<< trim END - vim9script - - export interface I - def F(): string - endinterface - - export class A implements I - def F(): string - return 'A' + class B extends A + endclass + class C extends B + def _Foo() enddef endclass END - writefile(lines, 'Xinheritintfimportclass.vim', 'D') + v9.CheckSourceFailure(lines, 'E1377: Access level of method "_Foo" is different in class "A"', 11) + # Public method in subclass lines =<< trim END vim9script - - import './Xinheritintfimportclass.vim' as i_imp - - # class C extends i_imp.A - class C extends i_imp.A implements i_imp.I - def F(): string - return 'C' + class A + def _Foo() + enddef + endclass + class B extends A + endclass + class C extends B + def Foo() enddef endclass - - def TestI(i: i_imp.I): string - return i.F() - enddef - - assert_equal('C', TestI(C.new())) END - v9.CheckSourceSuccess(lines) + v9.CheckSourceFailure(lines, 'E1377: Access level of method "Foo" is different in class "A"', 11) enddef -" Test for using "any" type for a variable in a sub-class while it has a -" concrete type in the interface -def Test_implements_using_var_type_any() +def Test_extend_empty_class() var lines =<< trim END vim9script - interface A - var val: list> - endinterface - class B implements A - var val = [{a: '1'}, {b: '2'}] + class A endclass - var b = B.new() - assert_equal([{a: '1'}, {b: '2'}], b.val) - END - v9.CheckSourceSuccess(lines) - - # initialize instance variable using a different type - lines =<< trim END - vim9script - interface A - var val: list> - endinterface - class B implements A - var val = {a: 1, b: 2} + class B extends A endclass - var b = B.new() + class C extends B + public static var rw_class_var = 1 + public var rw_obj_var = 2 + static def ClassMethod(): number + return 3 + enddef + def ObjMethod(): number + return 4 + enddef + endclass + assert_equal(1, C.rw_class_var) + assert_equal(3, C.ClassMethod()) + var c = C.new() + assert_equal(2, c.rw_obj_var) + assert_equal(4, c.ObjMethod()) END - v9.CheckSourceFailure(lines, 'E1382: Variable "val": type mismatch, expected list> but got dict', 1) + v9.CheckSourceSuccess(lines) enddef " Test for assigning to a member variable in a nested class @@ -13271,18 +11832,4 @@ func Test_class_selfref_gc() call v9.CheckSourceSuccess(lines) endfunc -" Test for defining an interface in a function -def Test_interface_defined_in_function() - var lines =<< trim END - vim9script - def Fn() - var x = 1 - interface Foo - endinterface - enddef - defcompile - END - v9.CheckScriptFailure(lines, 'E1436: Interface can only be used in a script', 2) -enddef - " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/testdir/test_vim9_interface.vim b/src/testdir/test_vim9_interface.vim new file mode 100644 index 0000000000..742b871f16 --- /dev/null +++ b/src/testdir/test_vim9_interface.vim @@ -0,0 +1,1459 @@ +" Tests for Vim9 interface + +import './util/vim9.vim' as v9 + +" Tests for basic interface declaration and errors +def Test_interface_basics() + var lines =<< trim END + vim9script + interface Something + var ro_var: list + def GetCount(): number + endinterface + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + interface SomethingWrong + static var count = 7 + endinterface + END + v9.CheckSourceFailure(lines, 'E1342: Interface can only be defined in Vim9 script', 1) + + lines =<< trim END + vim9script + + interface Some + var value: number + def Method(value: number) + endinterface + END + # The argument name and the object member name are the same, but this is not a + # problem because object members are always accessed with the "this." prefix. + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + interface somethingWrong + static var count = 7 + endinterface + END + v9.CheckSourceFailure(lines, 'E1343: Interface name must start with an uppercase letter: somethingWrong', 2) + + lines =<< trim END + vim9script + interface SomethingWrong + var value: string + var count = 7 + def GetCount(): number + endinterface + END + v9.CheckSourceFailure(lines, 'E1344: Cannot initialize a variable in an interface', 4) + + lines =<< trim END + vim9script + interface SomethingWrong + var value: string + var count: number + def GetCount(): number + return 5 + enddef + endinterface + END + v9.CheckSourceFailure(lines, 'E1345: Not a valid command in an interface: return 5', 6) + + # Test for "interface" cannot be abbreviated + lines =<< trim END + vim9script + inte Something + endinterface + END + v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: inte Something', 2) + + # Test for "endinterface" cannot be abbreviated + lines =<< trim END + vim9script + interface Something + endin + END + v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: endin', 3) + + # Additional commands after "interface name" + lines =<< trim END + vim9script + interface Something | var x = 10 | var y = 20 + endinterface + END + v9.CheckSourceFailure(lines, "E488: Trailing characters: | var x = 10", 2) + + lines =<< trim END + vim9script + export interface EnterExit + def Enter(): void + def Exit(): void + endinterface + END + writefile(lines, 'XdefIntf.vim', 'D') + + lines =<< trim END + vim9script + import './XdefIntf.vim' as defIntf + export def With(ee: defIntf.EnterExit, F: func) + ee.Enter() + try + F() + finally + ee.Exit() + endtry + enddef + END + v9.CheckScriptSuccess(lines) + + var imported =<< trim END + vim9script + export abstract class EnterExit + def Enter(): void + enddef + def Exit(): void + enddef + endclass + END + writefile(imported, 'XdefIntf2.vim', 'D') + + lines[1] = " import './XdefIntf2.vim' as defIntf" + v9.CheckScriptSuccess(lines) +enddef + +def Test_class_interface_wrong_end() + var lines =<< trim END + vim9script + abstract class SomeName + var member = 'text' + endinterface + END + v9.CheckSourceFailure(lines, 'E476: Invalid command: endinterface, expected endclass', 4) + + lines =<< trim END + vim9script + export interface AnotherName + var member: string + endclass + END + v9.CheckSourceFailure(lines, 'E476: Invalid command: endclass, expected endinterface', 4) +enddef + +" Test for using string() with an interface +def Test_interface_to_string() + var lines =<< trim END + vim9script + interface Intf + def Method(nr: number) + endinterface + assert_equal("interface Intf", string(Intf)) + END + v9.CheckSourceSuccess(lines) +enddef + +def Test_class_implements_interface() + var lines =<< trim END + vim9script + + interface Some + var count: number + def Method(nr: number) + endinterface + + class SomeImpl implements Some + var count: number + def Method(nr: number) + echo nr + enddef + endclass + + interface Another + var member: string + endinterface + + class AnotherImpl implements Some, Another + var member = 'abc' + var count = 20 + def Method(nr: number) + echo nr + enddef + endclass + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + + interface Some + var count: number + endinterface + + class SomeImpl implements Some implements Some + var count: number + endclass + END + v9.CheckSourceFailure(lines, 'E1350: Duplicate "implements"', 7) + + lines =<< trim END + vim9script + + interface Some + var count: number + endinterface + + class SomeImpl implements Some, Some + var count: number + endclass + END + v9.CheckSourceFailure(lines, 'E1351: Duplicate interface after "implements": Some', 7) + + lines =<< trim END + vim9script + + interface Some + var counter: number + def Method(nr: number) + endinterface + + class SomeImpl implements Some + var count: number + def Method(nr: number) + echo nr + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1348: Variable "counter" of interface "Some" is not implemented', 13) + + lines =<< trim END + vim9script + + interface Some + var count: number + def Methods(nr: number) + endinterface + + class SomeImpl implements Some + var count: number + def Method(nr: number) + echo nr + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1349: Method "Methods" of interface "Some" is not implemented', 13) + + # Check different order of members in class and interface works. + lines =<< trim END + vim9script + + interface Result + var label: string + var errpos: number + endinterface + + # order of members is opposite of interface + class Failure implements Result + public var lnum: number = 5 + var errpos: number = 42 + var label: string = 'label' + endclass + + def Test() + var result: Result = Failure.new() + + assert_equal('label', result.label) + assert_equal(42, result.errpos) + enddef + + Test() + END + v9.CheckSourceSuccess(lines) + + # Interface name after "extends" doesn't end in a space or NUL character + lines =<< trim END + vim9script + interface A + endinterface + class B extends A" + endclass + END + v9.CheckSourceFailure(lines, 'E1315: White space required after name: A"', 4) + + # Trailing characters after a class name + lines =<< trim END + vim9script + class A bbb + endclass + END + v9.CheckSourceFailure(lines, 'E488: Trailing characters: bbb', 2) + + # using "implements" with a non-existing class + lines =<< trim END + vim9script + class A implements B + endclass + END + v9.CheckSourceFailure(lines, 'E1346: Interface name not found: B', 3) + + # using "implements" with a regular class + lines =<< trim END + vim9script + class A + endclass + class B implements A + endclass + END + v9.CheckSourceFailure(lines, 'E1347: Not a valid interface: A', 5) + + # using "implements" with a variable + lines =<< trim END + vim9script + var T: number = 10 + class A implements T + endclass + END + v9.CheckSourceFailure(lines, 'E1347: Not a valid interface: T', 4) + + # implements should be followed by a white space + lines =<< trim END + vim9script + interface A + endinterface + class B implements A; + endclass + END + v9.CheckSourceFailure(lines, 'E1315: White space required after name: A;', 4) + + lines =<< trim END + vim9script + + interface One + def IsEven(nr: number): bool + endinterface + class Two implements One + def IsEven(nr: number): string + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1383: Method "IsEven": type mismatch, expected func(number): bool but got func(number): string', 9) + + lines =<< trim END + vim9script + + interface One + def IsEven(nr: number): bool + endinterface + class Two implements One + def IsEven(nr: bool): bool + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1383: Method "IsEven": type mismatch, expected func(number): bool but got func(bool): bool', 9) + + lines =<< trim END + vim9script + + interface One + def IsEven(nr: number): bool + endinterface + class Two implements One + def IsEven(nr: number, ...extra: list): bool + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1383: Method "IsEven": type mismatch, expected func(number): bool but got func(number, ...list): bool', 9) + + # access superclass interface members from subclass, mix variable order + lines =<< trim END + vim9script + + interface I1 + var mvar1: number + var mvar2: number + endinterface + + # NOTE: the order is swapped + class A implements I1 + var mvar2: number + var mvar1: number + public static var svar2: number + public static var svar1: number + def new() + svar1 = 11 + svar2 = 12 + this.mvar1 = 111 + this.mvar2 = 112 + enddef + endclass + + class B extends A + def new() + this.mvar1 = 121 + this.mvar2 = 122 + enddef + endclass + + class C extends B + def new() + this.mvar1 = 131 + this.mvar2 = 132 + enddef + endclass + + def F2(i: I1): list + return [ i.mvar1, i.mvar2 ] + enddef + + var oa = A.new() + var ob = B.new() + var oc = C.new() + + assert_equal([111, 112], F2(oa)) + assert_equal([121, 122], F2(ob)) + assert_equal([131, 132], F2(oc)) + END + v9.CheckSourceSuccess(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 + var mvar1: number + var mvar2: number + endinterface + + interface I2 + var mvar3: number + var mvar4: number + endinterface + + class A implements I1 + public static var svar1: number + public static var svar2: number + var mvar1: number + var mvar2: number + def new() + svar1 = 11 + svar2 = 12 + this.mvar1 = 111 + this.mvar2 = 112 + enddef + endclass + + class B extends A implements I2 + static var svar3: number + static var svar4: number + var mvar3: number + var mvar4: number + def new() + svar3 = 23 + svar4 = 24 + this.mvar1 = 121 + this.mvar2 = 122 + this.mvar3 = 123 + this.mvar4 = 124 + enddef + endclass + + class C extends B + public static var svar5: number + def new() + svar5 = 1001 + this.mvar1 = 131 + this.mvar2 = 132 + this.mvar3 = 133 + this.mvar4 = 134 + enddef + endclass + + def F2(i: I1): list + return [ i.mvar1, i.mvar2 ] + enddef + + def F4(i: I2): list + return [ i.mvar3, i.mvar4 ] + enddef + + var oa = A.new() + var ob = B.new() + var oc = C.new() + + 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)]) + END + v9.CheckSourceSuccess(lines) + + # Using two interface names without a space after the "," + lines =<< trim END + vim9script + interface A + endinterface + interface B + endinterface + class C implements A,B + endclass + END + v9.CheckSourceFailure(lines, 'E1315: White space required after name: A,B', 6) + + # No interface name after a comma + lines =<< trim END + vim9script + interface A + endinterface + class B implements A, + endclass + END + v9.CheckSourceFailure(lines, 'E1389: Missing name after implements', 4) + + # No interface name after implements + lines =<< trim END + vim9script + class A implements + endclass + END + v9.CheckSourceFailure(lines, 'E1389: Missing name after implements', 2) +enddef + +def Test_call_interface_method() + var lines =<< trim END + vim9script + interface Base + def Enter(): void + endinterface + + class Child implements Base + def Enter(): void + g:result ..= 'child' + enddef + endclass + + def F(obj: Base) + obj.Enter() + enddef + + g:result = '' + F(Child.new()) + assert_equal('child', g:result) + unlet g:result + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + class Base + def Enter(): void + g:result ..= 'base' + enddef + endclass + + class Child extends Base + def Enter(): void + g:result ..= 'child' + enddef + endclass + + def F(obj: Base) + obj.Enter() + enddef + + g:result = '' + F(Child.new()) + assert_equal('child', g:result) + unlet g:result + END + v9.CheckSourceSuccess(lines) + + # method of interface returns a value + lines =<< trim END + vim9script + interface Base + def Enter(): string + endinterface + + class Child implements Base + def Enter(): string + g:result ..= 'child' + return "/resource" + enddef + endclass + + def F(obj: Base) + var r = obj.Enter() + g:result ..= r + enddef + + g:result = '' + F(Child.new()) + assert_equal('child/resource', g:result) + unlet g:result + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + class Base + def Enter(): string + return null_string + enddef + endclass + + class Child extends Base + def Enter(): string + g:result ..= 'child' + return "/resource" + enddef + endclass + + def F(obj: Base) + var r = obj.Enter() + g:result ..= r + enddef + + g:result = '' + F(Child.new()) + assert_equal('child/resource', g:result) + unlet g:result + END + v9.CheckSourceSuccess(lines) + + # No class that implements the interface. + lines =<< trim END + vim9script + + interface IWithEE + def Enter(): any + def Exit(): void + endinterface + + def With1(ee: IWithEE, F: func) + var r = ee.Enter() + enddef + + defcompile + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for implementing an imported interface +def Test_implement_imported_interface() + var lines =<< trim END + vim9script + export interface Imp_Intf1 + def Fn1(): number + endinterface + export interface Imp_Intf2 + def Fn2(): number + endinterface + END + writefile(lines, 'Ximportinterface.vim', 'D') + + lines =<< trim END + vim9script + import './Ximportinterface.vim' as Xintf + + class A implements Xintf.Imp_Intf1, Xintf.Imp_Intf2 + def Fn1(): number + return 10 + enddef + def Fn2(): number + return 20 + enddef + endclass + var a = A.new() + assert_equal(10, a.Fn1()) + assert_equal(20, a.Fn2()) + END + v9.CheckScriptSuccess(lines) +enddef + +" Test for changing the member access of an interface in a implementation class +def Test_change_interface_member_access() + var lines =<< trim END + vim9script + interface A + var val: number + endinterface + class B implements A + public var val = 10 + endclass + END + v9.CheckSourceFailure(lines, 'E1367: Access level of variable "val" of interface "A" is different', 7) + + lines =<< trim END + vim9script + interface A + var val: number + endinterface + class B implements A + public var val = 10 + endclass + END + v9.CheckSourceFailure(lines, 'E1367: Access level of variable "val" of interface "A" is different', 7) +enddef + +" Test for using a interface method using a child object +def Test_interface_method_from_child() + var lines =<< trim END + vim9script + + interface A + def Foo(): string + endinterface + + class B implements A + def Foo(): string + return 'foo' + enddef + endclass + + class C extends B + def Bar(): string + return 'bar' + enddef + endclass + + def T1(a: A) + assert_equal('foo', a.Foo()) + enddef + + def T2(b: B) + assert_equal('foo', b.Foo()) + enddef + + var c = C.new() + T1(c) + T2(c) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for using an interface method using a child object when it is overridden +" by the child class. +def Test_interface_overridden_method_from_child() + var lines =<< trim END + vim9script + + interface A + def Foo(): string + endinterface + + class B implements A + def Foo(): string + return 'b-foo' + enddef + endclass + + class C extends B + def Bar(): string + return 'bar' + enddef + def Foo(): string + return 'c-foo' + enddef + endclass + + def T1(a: A) + assert_equal('c-foo', a.Foo()) + enddef + + def T2(b: B) + assert_equal('c-foo', b.Foo()) + enddef + + var c = C.new() + T1(c) + T2(c) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for interface inheritance +def Test_interface_inheritance() + var lines =<< trim END + vim9script + + interface A + def A_Fn(): string + endinterface + + interface B + def B_Fn(): string + endinterface + + interface C + def C_Fn(): string + endinterface + + class C1 implements A + def A_Fn(): string + return 'c1-a' + enddef + endclass + + class C2 extends C1 implements B + def B_Fn(): string + return 'c2-b' + enddef + def A_Fn(): string + return 'c2-a' + enddef + endclass + + class C3 extends C2 implements C + def C_Fn(): string + return 'c3-c' + enddef + def A_Fn(): string + return 'c3-a' + enddef + def B_Fn(): string + return 'c3-b' + enddef + endclass + + def T1(a: A, s: string) + assert_equal(s, a.A_Fn()) + enddef + + def T2(b: B, s: string) + assert_equal(s, b.B_Fn()) + enddef + + def T3(c: C, s: string) + assert_equal(s, c.C_Fn()) + enddef + + def T4(c1: C1) + T1(c1, 'c3-a') + enddef + + def T5(c2: C2) + T1(c2, 'c3-a') + T2(c2, 'c3-b') + enddef + + def T6(c3: C3) + T1(c3, 'c3-a') + T2(c3, 'c3-b') + T3(c3, 'c3-c') + enddef + + var o3 = C3.new() + T4(o3) + T5(o3) + T6(o3) + END + v9.CheckSourceSuccess(lines) + + # Both the parent and child classes implement the same interface + lines =<< trim END + vim9script + + interface I + def Foo(): string + endinterface + + class A implements I + def Foo(): string + return 'A-foo' + enddef + endclass + + class B implements I + def Foo(): string + return 'B-foo' + enddef + endclass + + def Bar(i1: I): string + return i1.Foo() + enddef + + var b = B.new() + assert_equal('B-foo', Bar(b)) + END + v9.CheckSourceSuccess(lines) +enddef + +" A interface cannot have a static variable or a static method or a protected +" variable or a protected method or a public variable +def Test_interface_with_unsupported_members() + var lines =<< trim END + vim9script + interface A + static var num: number + endinterface + END + v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3) + + lines =<< trim END + vim9script + interface A + static var _num: number + endinterface + END + v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3) + + lines =<< trim END + vim9script + interface A + public static var num: number + endinterface + END + v9.CheckSourceFailure(lines, 'E1387: public variable not supported in an interface', 3) + + lines =<< trim END + vim9script + interface A + public static var num: number + endinterface + END + v9.CheckSourceFailure(lines, 'E1387: public variable not supported in an interface', 3) + + lines =<< trim END + vim9script + interface A + static var _num: number + endinterface + END + v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3) + + lines =<< trim END + vim9script + interface A + static def Foo(d: dict): list + endinterface + END + v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3) + + lines =<< trim END + vim9script + interface A + static def _Foo(d: dict): list + endinterface + END + v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3) + + lines =<< trim END + vim9script + interface A + var _Foo: list + endinterface + END + v9.CheckSourceFailure(lines, 'E1379: Protected variable not supported in an interface', 3) + + lines =<< trim END + vim9script + interface A + def _Foo(d: dict): list + endinterface + END + v9.CheckSourceFailure(lines, 'E1380: Protected method not supported in an interface', 3) +enddef + +" Test for extending an interface +def Test_extend_interface() + var lines =<< trim END + vim9script + interface A + var var1: list + def Foo() + endinterface + interface B extends A + var var2: dict + def Bar() + endinterface + class C implements A, B + var var1 = [1, 2] + def Foo() + enddef + var var2 = {a: '1'} + def Bar() + enddef + endclass + END + v9.CheckSourceSuccess(lines) + + # extending empty interface + lines =<< trim END + vim9script + interface A + endinterface + interface B extends A + endinterface + class C implements B + endclass + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + interface A + def Foo() + endinterface + interface B extends A + var var2: dict + endinterface + class C implements A, B + var var2 = {a: '1'} + endclass + END + v9.CheckSourceFailure(lines, 'E1349: Method "Foo" of interface "A" is not implemented', 10) + + lines =<< trim END + vim9script + interface A + def Foo() + endinterface + interface B extends A + var var2: dict + endinterface + class C implements A, B + def Foo() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1348: Variable "var2" of interface "B" is not implemented', 11) + + # interface cannot extend a class + lines =<< trim END + vim9script + class A + endclass + interface B extends A + endinterface + END + v9.CheckSourceFailure(lines, 'E1354: Cannot extend A', 5) + + # class cannot extend an interface + lines =<< trim END + vim9script + interface A + endinterface + class B extends A + endclass + END + v9.CheckSourceFailure(lines, 'E1354: Cannot extend A', 5) + + # interface cannot implement another interface + lines =<< trim END + vim9script + interface A + endinterface + interface B implements A + endinterface + END + v9.CheckSourceFailure(lines, 'E1381: Interface cannot use "implements"', 4) + + # interface cannot extend multiple interfaces + lines =<< trim END + vim9script + interface A + endinterface + interface B + endinterface + interface C extends A, B + endinterface + END + v9.CheckSourceFailure(lines, 'E1315: White space required after name: A, B', 6) + + # Variable type in an extended interface is of different type + lines =<< trim END + vim9script + interface A + var val1: number + endinterface + interface B extends A + var val2: string + endinterface + interface C extends B + var val1: string + var val2: number + endinterface + END + v9.CheckSourceFailure(lines, 'E1382: Variable "val1": type mismatch, expected number but got string', 11) +enddef + +" Test for a child class implementing an interface when some of the methods are +" defined in the parent class. +def Test_child_class_implements_interface() + var lines =<< trim END + vim9script + + interface Intf + def F1(): list> + def F2(): list> + def F3(): list> + var var1: list> + var var2: list> + var var3: list> + endinterface + + class A + def A1() + enddef + def F3(): list> + return [[3]] + enddef + var v1: list> = [[0]] + var var3 = [{c: 30}] + endclass + + class B extends A + def B1() + enddef + def F2(): list> + return [[2]] + enddef + var v2: list> = [[0]] + var var2 = [{b: 20}] + endclass + + class C extends B implements Intf + def C1() + enddef + def F1(): list> + return [[1]] + enddef + var v3: list> = [[0]] + var var1 = [{a: 10}] + endclass + + def T(if: Intf) + assert_equal([[1]], if.F1()) + assert_equal([[2]], if.F2()) + assert_equal([[3]], if.F3()) + assert_equal([{a: 10}], if.var1) + assert_equal([{b: 20}], if.var2) + assert_equal([{c: 30}], if.var3) + enddef + + var c = C.new() + T(c) + assert_equal([[1]], c.F1()) + assert_equal([[2]], c.F2()) + assert_equal([[3]], c.F3()) + assert_equal([{a: 10}], c.var1) + assert_equal([{b: 20}], c.var2) + assert_equal([{c: 30}], c.var3) + END + v9.CheckSourceSuccess(lines) + + # One of the interface methods is not found + lines =<< trim END + vim9script + + interface Intf + def F1() + def F2() + def F3() + endinterface + + class A + def A1() + enddef + endclass + + class B extends A + def B1() + enddef + def F2() + enddef + endclass + + class C extends B implements Intf + def C1() + enddef + def F1() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1349: Method "F3" of interface "Intf" is not implemented', 26) + + # One of the interface methods is of different type + lines =<< trim END + vim9script + + interface Intf + def F1() + def F2() + def F3() + endinterface + + class A + def F3(): number + return 0 + enddef + def A1() + enddef + endclass + + class B extends A + def B1() + enddef + def F2() + enddef + endclass + + class C extends B implements Intf + def C1() + enddef + def F1() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1383: Method "F3": type mismatch, expected func() but got func(): number', 29) + + # One of the interface variables is not present + lines =<< trim END + vim9script + + interface Intf + var var1: list> + var var2: list> + var var3: list> + endinterface + + class A + var v1: list> = [[0]] + endclass + + class B extends A + var v2: list> = [[0]] + var var2 = [{b: 20}] + endclass + + class C extends B implements Intf + var v3: list> = [[0]] + var var1 = [{a: 10}] + endclass + END + v9.CheckSourceFailure(lines, 'E1348: Variable "var3" of interface "Intf" is not implemented', 21) + + # One of the interface variables is of different type + lines =<< trim END + vim9script + + interface Intf + var var1: list> + var var2: list> + var var3: list> + endinterface + + class A + var v1: list> = [[0]] + var var3: list> + endclass + + class B extends A + var v2: list> = [[0]] + var var2 = [{b: 20}] + endclass + + class C extends B implements Intf + var v3: list> = [[0]] + var var1 = [{a: 10}] + endclass + END + v9.CheckSourceFailure(lines, 'E1382: Variable "var3": type mismatch, expected list> but got list>', 22) +enddef + +" Test for extending an interface with duplicate variables and methods +def Test_interface_extends_with_dup_members() + var lines =<< trim END + vim9script + interface A + var n1: number + def Foo1(): number + endinterface + interface B extends A + var n2: number + var n1: number + def Foo2(): number + def Foo1(): number + endinterface + class C implements B + var n1 = 10 + var n2 = 20 + def Foo1(): number + return 30 + enddef + def Foo2(): number + return 40 + enddef + endclass + def T1(a: A) + assert_equal(10, a.n1) + assert_equal(30, a.Foo1()) + enddef + def T2(b: B) + assert_equal(10, b.n1) + assert_equal(20, b.n2) + assert_equal(30, b.Foo1()) + assert_equal(40, b.Foo2()) + enddef + var c = C.new() + T1(c) + T2(c) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for implementing an interface with different ordering for the interface +" member variables. +def Test_implement_interface_with_different_variable_order() + var lines =<< trim END + vim9script + + interface IX + var F: func(): string + endinterface + + class X implements IX + var x: number + var F: func(): string = () => 'ok' + endclass + + def Foo(ix: IX): string + return ix.F() + enddef + + var x0 = X.new(0) + assert_equal('ok', Foo(x0)) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for inheriting interfaces from an imported super class +def Test_interface_inheritance_with_imported_super() + var lines =<< trim END + vim9script + + export interface I + def F(): string + endinterface + + export class A implements I + def F(): string + return 'A' + enddef + endclass + END + writefile(lines, 'Xinheritintfimportclass.vim', 'D') + + lines =<< trim END + vim9script + + import './Xinheritintfimportclass.vim' as i_imp + + # class C extends i_imp.A + class C extends i_imp.A implements i_imp.I + def F(): string + return 'C' + enddef + endclass + + def TestI(i: i_imp.I): string + return i.F() + enddef + + assert_equal('C', TestI(C.new())) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for defining an interface in a function +def Test_interface_defined_in_function() + var lines =<< trim END + vim9script + def Fn() + var x = 1 + interface Foo + endinterface + enddef + defcompile + END + v9.CheckScriptFailure(lines, 'E1436: Interface can only be used in a script', 2) +enddef + +" Test for using "any" type for a variable in a sub-class while it has a +" concrete type in the interface +def Test_implements_using_var_type_any() + var lines =<< trim END + vim9script + interface A + var val: list> + endinterface + class B implements A + var val = [{a: '1'}, {b: '2'}] + endclass + var b = B.new() + assert_equal([{a: '1'}, {b: '2'}], b.val) + END + v9.CheckSourceSuccess(lines) + + # initialize instance variable using a different type + lines =<< trim END + vim9script + interface A + var val: list> + endinterface + class B implements A + var val = {a: 1, b: 2} + endclass + var b = B.new() + END + v9.CheckSourceFailure(lines, 'E1382: Variable "val": type mismatch, expected list> but got dict', 1) +enddef + +" Test interface garbage collection +func Test_interface_garbagecollect() + let lines =<< trim END + vim9script + + interface I + var ro_obj_var: number + + def ObjFoo(): number + endinterface + + class A implements I + static var ro_class_var: number = 10 + public static var rw_class_var: number = 20 + static var _priv_class_var: number = 30 + var ro_obj_var: number = 40 + var _priv_obj_var: number = 60 + + static def _ClassBar(): number + return _priv_class_var + enddef + + static def ClassFoo(): number + return ro_class_var + rw_class_var + A._ClassBar() + enddef + + def _ObjBar(): number + return this._priv_obj_var + enddef + + def ObjFoo(): number + return this.ro_obj_var + this._ObjBar() + enddef + endclass + + assert_equal(60, A.ClassFoo()) + var o = A.new() + assert_equal(100, o.ObjFoo()) + test_garbagecollect_now() + assert_equal(60, A.ClassFoo()) + assert_equal(100, o.ObjFoo()) + END + call v9.CheckSourceSuccess(lines) +endfunc + +" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index 0aa6ace8a3..b98a84e0d3 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2029, /**/ 2028, /**/