Mercurial > vim
view src/testdir/test_vim9_class.vim @ 33278:b5ed566262d3 v9.0.1906
patch 9.0.1906: Vim9: Interfaces should not support class methods and variables
Commit: https://github.com/vim/vim/commit/92d9ee5f4ca0d2de04c39afbafc7609da43fb2e9
Author: Yegappan Lakshmanan <yegappan@yahoo.com>
Date: Sun Sep 17 17:03:19 2023 +0200
patch 9.0.1906: Vim9: Interfaces should not support class methods and variables
Problem: Vim9: Interfaces should not support class methods and
variables
Solution: Make sure interface follow the interface specification
Vim9 interface changes to follow the new interface specification:
1) An interface can have only read-only and read-write instance
variables.
2) An interface can have only public instance methods.
3) An interface cannot have class variables and class methods.
4) An interface cannot have private instance variables and private
instance methods.
5) A interface can extend another interface using "extends". The
sub-interface gets all the variables and methods in the super
interface.
That means:
- Interfaces should not support class methods and variables.
- Adjust error numbers and add additional tests.
- Interface methods can be defined in one of the super classes.
- Interface variables can be defined in one of the super classes.
and instance variables can be repeated in sub interfaces.
- Check the class variable types with the type in interface.
closes: #13100
Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Sun, 17 Sep 2023 17:15:06 +0200 |
parents | aba1fa2b7d1e |
children | 0c3553cfe22e |
line wrap: on
line source
" Test Vim9 classes source check.vim import './vim9.vim' as v9 def Test_class_basic() var lines =<< trim END class NotWorking endclass END v9.CheckSourceFailure(lines, 'E1316:') lines =<< trim END vim9script class notWorking endclass END v9.CheckSourceFailure(lines, 'E1314:') lines =<< trim END vim9script class Not@working endclass END v9.CheckSourceFailure(lines, 'E1315:') lines =<< trim END vim9script abstract noclass Something endclass END v9.CheckSourceFailure(lines, 'E475:') lines =<< trim END vim9script abstract classy Something endclass END v9.CheckSourceFailure(lines, 'E475:') lines =<< trim END vim9script class Something endcl END v9.CheckSourceFailure(lines, 'E1065:') lines =<< trim END vim9script class Something endclass school's out END v9.CheckSourceFailure(lines, 'E488:') lines =<< trim END vim9script class Something endclass | echo 'done' END v9.CheckSourceFailure(lines, 'E488:') lines =<< trim END vim9script class Something this endclass END v9.CheckSourceFailure(lines, 'E1317:') lines =<< trim END vim9script class Something this. endclass END v9.CheckSourceFailure(lines, 'E1317:') lines =<< trim END vim9script class Something this .count endclass END v9.CheckSourceFailure(lines, 'E1317:') lines =<< trim END vim9script class Something this. count endclass END v9.CheckSourceFailure(lines, 'E1317:') lines =<< trim END vim9script class Something this.count: number that.count endclass END v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: that.count') lines =<< trim END vim9script class Something this.count endclass END v9.CheckSourceFailure(lines, 'E1022:') lines =<< trim END vim9script class Something def new() this.state = 0 enddef endclass var obj = Something.new() END v9.CheckSourceFailure(lines, 'E1089:') lines =<< trim END vim9script class Something this.count : number endclass END v9.CheckSourceFailure(lines, 'E1059:') lines =<< trim END vim9script class Something this.count:number endclass END v9.CheckSourceFailure(lines, 'E1069:') # Test for unsupported comment specifier lines =<< trim END vim9script class Something # comment #{ endclass END v9.CheckSourceFailure(lines, 'E1170:') # Test for using class as a bool lines =<< trim END vim9script class A endclass if A endif END v9.CheckSourceFailure(lines, 'E1319: Using a class as a Number') # Test for using object as a bool lines =<< trim END vim9script class A endclass var a = A.new() if a endif END v9.CheckSourceFailure(lines, 'E1320: Using an object as a Number') # Test for using class as a float lines =<< trim END vim9script class A endclass sort([1.1, A], 'f') END v9.CheckSourceFailure(lines, 'E1321: Using a class as a Float') # Test for using object as a float lines =<< trim END vim9script class A endclass var a = A.new() sort([1.1, a], 'f') END v9.CheckSourceFailure(lines, 'E1322: Using an object as a Float') # Test for using class as a string lines =<< trim END vim9script class A endclass :exe 'call ' .. A END v9.CheckSourceFailure(lines, 'E1323: Using a class as a String') # Test for using object as a string lines =<< trim END vim9script class A endclass var a = A.new() :exe 'call ' .. a END v9.CheckSourceFailure(lines, 'E1324: Using an object as a String') lines =<< trim END vim9script class TextPosition this.lnum: number this.col: number # make a nicely formatted string def ToString(): string return $'({this.lnum}, {this.col})' enddef endclass # use the automatically generated new() method var pos = TextPosition.new(2, 12) assert_equal(2, pos.lnum) assert_equal(12, pos.col) # call an object method assert_equal('(2, 12)', pos.ToString()) assert_equal(v:t_class, type(TextPosition)) assert_equal(v:t_object, type(pos)) assert_equal('class<TextPosition>', typename(TextPosition)) assert_equal('object<TextPosition>', typename(pos)) END v9.CheckSourceSuccess(lines) # When referencing object methods, space cannot be used after a "." lines =<< trim END vim9script class A def Foo(): number return 10 enddef endclass var a = A.new() var v = a. Foo() END v9.CheckSourceFailure(lines, 'E1202:') # Using an object without specifying a method or a member variable lines =<< trim END vim9script class A def Foo(): number return 10 enddef endclass var a = A.new() var v = a. END v9.CheckSourceFailure(lines, 'E15:') # Error when parsing the arguments of an object method. lines =<< trim END vim9script class A def Foo() enddef endclass var a = A.new() var v = a.Foo(,) END v9.CheckSourceFailure(lines, 'E15:') lines =<< trim END vim9script class A this.y = { X: 1 } endclass var a = A.new() END v9.CheckSourceSuccess(lines) enddef def Test_class_defined_twice() # class defined twice should fail var lines =<< trim END vim9script class There endclass class There endclass END v9.CheckSourceFailure(lines, 'E1041: Redefining script item: "There"') # one class, reload same script twice is OK lines =<< trim END vim9script class There endclass END writefile(lines, 'XclassTwice.vim', 'D') source XclassTwice.vim source XclassTwice.vim enddef def Test_returning_null_object() # this was causing an internal error var lines =<< trim END vim9script class BufferList def Current(): any return null_object enddef endclass var buffers = BufferList.new() echo buffers.Current() END v9.CheckSourceSuccess(lines) enddef def Test_using_null_class() var lines =<< trim END @_ = null_class.member END v9.CheckDefExecAndScriptFailure(lines, ['E715:', 'E1363:']) enddef def Test_class_interface_wrong_end() var lines =<< trim END vim9script abstract class SomeName this.member = 'text' endinterface END v9.CheckSourceFailure(lines, 'E476: Invalid command: endinterface, expected endclass') lines =<< trim END vim9script export interface AnotherName this.member: string endclass END v9.CheckSourceFailure(lines, 'E476: Invalid command: endclass, expected endinterface') enddef def Test_object_not_set() var lines =<< trim END vim9script class State this.value = 'xyz' endclass var state: State var db = {'xyz': 789} echo db[state.value] END v9.CheckSourceFailure(lines, 'E1360:') lines =<< trim END vim9script class Class this.id: string def Method1() echo 'Method1' .. this.id enddef endclass var obj: Class def Func() obj.Method1() enddef Func() END v9.CheckSourceFailure(lines, 'E1360:') lines =<< trim END vim9script class Background this.background = 'dark' endclass class Colorscheme this._bg: Background def GetBackground(): string return this._bg.background enddef endclass var bg: Background # UNINITIALIZED echo Colorscheme.new(bg).GetBackground() END v9.CheckSourceFailure(lines, 'E1360:') # TODO: this should not give an error but be handled at runtime lines =<< trim END vim9script class Class this.id: string def Method1() echo 'Method1' .. this.id enddef endclass var obj = null_object def Func() obj.Method1() enddef Func() END v9.CheckSourceFailure(lines, 'E1363:') enddef def Test_null_object_assign_compare() var lines =<< trim END vim9script var nullo = null_object def F(): any return nullo enddef assert_equal('object<Unknown>', typename(F())) var o0 = F() assert_true(o0 == null_object) assert_true(o0 == null) var o1: any = nullo assert_true(o1 == null_object) assert_true(o1 == null) def G() var x = null_object enddef class C endclass var o2: C assert_true(o2 == null_object) assert_true(o2 == null) o2 = null_object assert_true(o2 == null) o2 = C.new() assert_true(o2 != null) o2 = null_object assert_true(o2 == null) END v9.CheckSourceSuccess(lines) enddef def Test_class_member_initializer() var lines =<< trim END vim9script class TextPosition this.lnum: number = 1 this.col: number = 1 # constructor with only the line number def new(lnum: number) this.lnum = lnum enddef endclass var pos = TextPosition.new(3) assert_equal(3, pos.lnum) assert_equal(1, pos.col) var instr = execute('disassemble TextPosition.new') assert_match('new\_s*' .. '0 NEW TextPosition size \d\+\_s*' .. '\d PUSHNR 1\_s*' .. '\d STORE_THIS 0\_s*' .. '\d PUSHNR 1\_s*' .. '\d STORE_THIS 1\_s*' .. 'this.lnum = lnum\_s*' .. '\d LOAD arg\[-1]\_s*' .. '\d PUSHNR 0\_s*' .. '\d LOAD $0\_s*' .. '\d\+ STOREINDEX object\_s*' .. '\d\+ RETURN object.*', instr) END v9.CheckSourceSuccess(lines) enddef def Test_member_any_used_as_object() var lines =<< trim END vim9script class Inner this.value: number = 0 endclass class Outer this.inner: any endclass def F(outer: Outer) outer.inner.value = 1 enddef var inner_obj = Inner.new(0) var outer_obj = Outer.new(inner_obj) F(outer_obj) assert_equal(1, inner_obj.value) END v9.CheckSourceSuccess(lines) lines =<< trim END vim9script class Inner this.value: number = 0 endclass class Outer this.inner: Inner endclass def F(outer: Outer) outer.inner.value = 1 enddef def Test_assign_to_nested_typed_member() var inner = Inner.new(0) var outer = Outer.new(inner) F(outer) assert_equal(1, inner.value) enddef Test_assign_to_nested_typed_member() END v9.CheckSourceSuccess(lines) # Try modifying a private variable using an "any" object lines =<< trim END vim9script class Inner this._value: string = '' endclass class Outer this.inner: any endclass def F(outer: Outer) outer.inner._value = 'b' enddef var inner_obj = Inner.new('a') var outer_obj = Outer.new(inner_obj) F(outer_obj) END v9.CheckSourceFailure(lines, 'E1333: Cannot access private member: _value') # Try modifying a non-existing variable using an "any" object lines =<< trim END vim9script class Inner this.value: string = '' endclass class Outer this.inner: any endclass def F(outer: Outer) outer.inner.someval = 'b' enddef var inner_obj = Inner.new('a') var outer_obj = Outer.new(inner_obj) F(outer_obj) END v9.CheckSourceFailure(lines, 'E1326: Member not found on object "Inner": someval') enddef def Test_assignment_with_operator() var lines =<< trim END vim9script class Foo public this.x: number def Add(n: number) this.x += n enddef endclass var f = Foo.new(3) f.Add(17) assert_equal(20, f.x) def AddToFoo(obj: Foo) obj.x += 3 enddef AddToFoo(f) assert_equal(23, f.x) END v9.CheckSourceSuccess(lines) # do the same thing, but through an interface lines =<< trim END vim9script interface I public this.x: number endinterface class Foo implements I public this.x: number def Add(n: number) var i: I = this i.x += n enddef endclass var f = Foo.new(3) f.Add(17) assert_equal(20, f.x) def AddToFoo(i: I) i.x += 3 enddef AddToFoo(f) assert_equal(23, f.x) END v9.CheckSourceSuccess(lines) enddef def Test_list_of_objects() var lines =<< trim END vim9script class Foo def Add() enddef endclass def ProcessList(fooList: list<Foo>) for foo in fooList foo.Add() endfor enddef var l: list<Foo> = [Foo.new()] ProcessList(l) END v9.CheckSourceSuccess(lines) enddef def Test_expr_after_using_object() var lines =<< trim END vim9script class Something this.label: string = '' endclass def Foo(): Something var v = Something.new() echo 'in Foo(): ' .. typename(v) return v enddef Foo() END v9.CheckSourceSuccess(lines) enddef def Test_class_default_new() var lines =<< trim END vim9script class TextPosition this.lnum: number = 1 this.col: number = 1 endclass var pos = TextPosition.new() assert_equal(1, pos.lnum) assert_equal(1, pos.col) pos = TextPosition.new(v:none, v:none) assert_equal(1, pos.lnum) assert_equal(1, pos.col) pos = TextPosition.new(3, 22) assert_equal(3, pos.lnum) assert_equal(22, pos.col) pos = TextPosition.new(v:none, 33) assert_equal(1, pos.lnum) assert_equal(33, pos.col) END v9.CheckSourceSuccess(lines) lines =<< trim END vim9script class Person this.name: string this.age: number = 42 this.education: string = "unknown" def new(this.name, this.age = v:none, this.education = v:none) enddef endclass var piet = Person.new("Piet") assert_equal("Piet", piet.name) assert_equal(42, piet.age) assert_equal("unknown", piet.education) var chris = Person.new("Chris", 4, "none") assert_equal("Chris", chris.name) assert_equal(4, chris.age) assert_equal("none", chris.education) END v9.CheckSourceSuccess(lines) lines =<< trim END vim9script class Person this.name: string this.age: number = 42 this.education: string = "unknown" def new(this.name, this.age = v:none, this.education = v:none) enddef endclass var missing = Person.new() END v9.CheckSourceFailure(lines, 'E119:') # Using a specific value to initialize an instance variable in the new() # method. lines =<< trim END vim9script class A this.val: string def new(this.val = 'a') enddef endclass END v9.CheckSourceFailure(lines, "E1328: Constructor default value must be v:none: = 'a'") enddef def Test_class_new_with_object_member() var lines =<< trim END vim9script class C this.str: string this.num: number def new(this.str, this.num) enddef def newVals(this.str, this.num) enddef endclass def Check() try var c = C.new('cats', 2) assert_equal('cats', c.str) assert_equal(2, c.num) c = C.newVals('dogs', 4) assert_equal('dogs', c.str) assert_equal(4, c.num) catch assert_report($'Unexpected exception was caught: {v:exception}') endtry enddef Check() END v9.CheckSourceSuccess(lines) lines =<< trim END vim9script class C this.str: string this.num: number def new(this.str, this.num) enddef endclass def Check() try var c = C.new(1, 2) catch assert_report($'Unexpected exception was caught: {v:exception}') endtry enddef Check() END v9.CheckSourceFailure(lines, 'E1013:') lines =<< trim END vim9script class C this.str: string this.num: number def newVals(this.str, this.num) enddef endclass def Check() try var c = C.newVals('dogs', 'apes') catch assert_report($'Unexpected exception was caught: {v:exception}') endtry enddef Check() END v9.CheckSourceFailure(lines, 'E1013:') enddef def Test_class_object_member_inits() var lines =<< trim END vim9script class TextPosition this.lnum: number this.col = 1 this.addcol: number = 2 endclass var pos = TextPosition.new() assert_equal(0, pos.lnum) assert_equal(1, pos.col) assert_equal(2, pos.addcol) END v9.CheckSourceSuccess(lines) lines =<< trim END vim9script class TextPosition this.lnum this.col = 1 endclass END v9.CheckSourceFailure(lines, 'E1022:') # If the type is not specified for a member, then it should be set during # object creation and not when defining the class. lines =<< trim END vim9script var init_count = 0 def Init(): string init_count += 1 return 'foo' enddef class A this.str1 = Init() this.str2: string = Init() this.col = 1 endclass assert_equal(init_count, 0) var a = A.new() assert_equal(init_count, 2) END v9.CheckSourceSuccess(lines) # Test for initializing an object member with an unknown variable/type lines =<< trim END vim9script class A this.value = init_val endclass var a = A.new() END v9.CheckSourceFailure(lines, 'E1001:') # Test for initializing an object member with an special type lines =<< trim END vim9script class A this.value: void endclass END v9.CheckSourceFailure(lines, 'E1330: Invalid type for object member: void') enddef " Test for instance variable access def Test_instance_variable_access() var lines =<< trim END vim9script class Triple this._one = 1 this.two = 2 public this.three = 3 def GetOne(): number return this._one enddef endclass var trip = Triple.new() assert_equal(1, trip.GetOne()) assert_equal(2, trip.two) assert_equal(3, trip.three) assert_fails('echo trip._one', 'E1333') assert_fails('trip._one = 11', 'E1333') assert_fails('trip.two = 22', 'E1335') trip.three = 33 assert_equal(33, trip.three) assert_fails('trip.four = 4', 'E1326') END v9.CheckSourceSuccess(lines) # Test for a public member variable name beginning with an underscore lines =<< trim END vim9script class A public this._val = 10 endclass END v9.CheckSourceFailure(lines, 'E1332:') lines =<< trim END vim9script class MyCar this.make: string this.age = 5 def new(make_arg: string) this.make = make_arg enddef def GetMake(): string return $"make = {this.make}" enddef def GetAge(): number return this.age enddef endclass var c = MyCar.new("abc") assert_equal('make = abc', c.GetMake()) c = MyCar.new("def") assert_equal('make = def', c.GetMake()) var c2 = MyCar.new("123") assert_equal('make = 123', c2.GetMake()) def CheckCar() assert_equal("make = def", c.GetMake()) assert_equal(5, c.GetAge()) enddef CheckCar() END v9.CheckSourceSuccess(lines) lines =<< trim END vim9script class MyCar this.make: string def new(make_arg: string) this.make = make_arg enddef endclass var c = MyCar.new("abc") var c = MyCar.new("def") END v9.CheckSourceFailure(lines, 'E1041:') lines =<< trim END vim9script class Foo this.x: list<number> = [] def Add(n: number): any this.x->add(n) return this enddef endclass echo Foo.new().Add(1).Add(2).x echo Foo.new().Add(1).Add(2) .x echo Foo.new().Add(1) .Add(2).x echo Foo.new() .Add(1).Add(2).x echo Foo.new() .Add(1) .Add(2) .x END v9.CheckSourceSuccess(lines) # Test for "public" cannot be abbreviated lines =<< trim END vim9script class Something pub this.val = 1 endclass END v9.CheckSourceFailure(lines, 'E1065:') # Test for "public" keyword must be followed by "this" or "static". lines =<< trim END vim9script class Something public val = 1 endclass END v9.CheckSourceFailure(lines, 'E1331:') # Modify a instance variable using the class name in the script context lines =<< trim END vim9script class A public this.val = 1 endclass A.val = 1 END v9.CheckSourceFailure(lines, 'E1376: Object member "val" accessible only using class "A" object') # Read a instance variable using the class name in the script context lines =<< trim END vim9script class A public this.val = 1 endclass var i = A.val END v9.CheckSourceFailure(lines, 'E1376: Object member "val" accessible only using class "A" object') # Modify a instance variable using the class name in a def function lines =<< trim END vim9script class A public this.val = 1 endclass def T() A.val = 1 enddef T() END v9.CheckSourceFailure(lines, 'E1376: Object member "val" accessible only using class "A" object') # Read a instance variable using the class name in a def function lines =<< trim END vim9script class A public this.val = 1 endclass def T() var i = A.val enddef T() END v9.CheckSourceFailure(lines, 'E1376: Object member "val" accessible only using class "A" object') # Access from child class extending a class: lines =<< trim END vim9script class A this.ro_obj_var = 10 public this.rw_obj_var = 20 this._priv_obj_var = 30 endclass class B extends A def Foo() var x: number x = this.ro_obj_var this.ro_obj_var = 0 x = this.rw_obj_var this.rw_obj_var = 0 x = this._priv_obj_var this._priv_obj_var = 0 enddef endclass var b = B.new() b.Foo() END v9.CheckSourceSuccess(lines) enddef " Test for class variable access def Test_class_variable_access() # Test for "static" cannot be abbreviated var lines =<< trim END vim9script class Something stat this.val = 1 endclass END v9.CheckSourceFailure(lines, 'E1065:') # Test for "static" cannot be followed by "this". lines =<< trim END vim9script class Something static this.val = 1 endclass END v9.CheckSourceFailure(lines, 'E1368: Static cannot be followed by "this" in a member name') # Test for "static" cannot be followed by "public". lines =<< trim END vim9script class Something static public val = 1 endclass END v9.CheckSourceFailure(lines, 'E1022: Type or initialization required') # A readonly class variable cannot be modified from a child class lines =<< trim END vim9script class A static ro_class_var = 40 endclass class B extends A def Foo() A.ro_class_var = 50 enddef endclass var b = B.new() b.Foo() END v9.CheckSourceFailure(lines, 'E46: Cannot change read-only variable "ro_class_var"') # A private class variable cannot be accessed from a child class lines =<< trim END vim9script class A static _priv_class_var = 60 endclass class B extends A def Foo() var i = A._priv_class_var enddef endclass var b = B.new() b.Foo() END v9.CheckSourceFailure(lines, 'E1333: Cannot access private member: _priv_class_var') # A private class variable cannot be modified from a child class lines =<< trim END vim9script class A static _priv_class_var = 60 endclass class B extends A def Foo() A._priv_class_var = 0 enddef endclass var b = B.new() b.Foo() END v9.CheckSourceFailure(lines, 'E1333: Cannot access private member: _priv_class_var') # Access from child class extending a class and from script context lines =<< trim END vim9script class A static ro_class_var = 10 public static rw_class_var = 20 static _priv_class_var = 30 endclass class B extends A def Foo() var x: number x = A.ro_class_var assert_equal(10, x) x = A.rw_class_var assert_equal(25, x) A.rw_class_var = 20 assert_equal(20, A.rw_class_var) enddef endclass assert_equal(10, A.ro_class_var) assert_equal(20, A.rw_class_var) A.rw_class_var = 25 assert_equal(25, A.rw_class_var) var b = B.new() b.Foo() END v9.CheckSourceSuccess(lines) enddef def Test_class_object_compare() var class_lines =<< trim END vim9script class Item this.nr = 0 this.name = 'xx' endclass END # used at the script level and in a compiled function var test_lines =<< trim END var i1 = Item.new() assert_equal(i1, i1) assert_true(i1 is i1) var i2 = Item.new() assert_equal(i1, i2) assert_false(i1 is i2) var i3 = Item.new(0, 'xx') assert_equal(i1, i3) var io1 = Item.new(1, 'xx') assert_notequal(i1, io1) var io2 = Item.new(0, 'yy') assert_notequal(i1, io2) END v9.CheckSourceSuccess(class_lines + test_lines) v9.CheckSourceSuccess( class_lines + ['def Test()'] + test_lines + ['enddef', 'Test()']) for op in ['>', '>=', '<', '<=', '=~', '!~'] var op_lines = [ 'var i1 = Item.new()', 'var i2 = Item.new()', 'echo i1 ' .. op .. ' i2', ] v9.CheckSourceFailure(class_lines + op_lines, 'E1153: Invalid operation for object') v9.CheckSourceFailure(class_lines + ['def Test()'] + op_lines + ['enddef', 'Test()'], 'E1153: Invalid operation for object') endfor enddef def Test_object_type() var lines =<< trim END vim9script class One this.one = 1 endclass class Two this.two = 2 endclass class TwoMore extends Two this.more = 9 endclass var o: One = One.new() var t: Two = Two.new() var m: TwoMore = TwoMore.new() var tm: Two = TwoMore.new() t = m END v9.CheckSourceSuccess(lines) lines =<< trim END vim9script class One this.one = 1 endclass class Two this.two = 2 endclass var o: One = Two.new() END v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected object<One> but got object<Two>') lines =<< trim END vim9script interface One def GetMember(): number endinterface class Two implements One this.one = 1 def GetMember(): number return this.one enddef endclass var o: One = Two.new(5) assert_equal(5, o.GetMember()) END v9.CheckSourceSuccess(lines) lines =<< trim END vim9script class Num this.n: number = 0 endclass def Ref(name: string): func(Num): Num return (arg: Num): Num => { return eval(name)(arg) } enddef const Fn = Ref('Double') var Double = (m: Num): Num => Num.new(m.n * 2) echo Fn(Num.new(4)) END v9.CheckSourceSuccess(lines) enddef def Test_class_member() # check access rules var lines =<< trim END vim9script class TextPos this.lnum = 1 this.col = 1 static counter = 0 static _secret = 7 public static anybody = 42 static def AddToCounter(nr: number) counter += nr enddef endclass assert_equal(0, TextPos.counter) TextPos.AddToCounter(3) assert_equal(3, TextPos.counter) assert_fails('echo TextPos.noSuchMember', 'E1337:') def GetCounter(): number return TextPos.counter enddef assert_equal(3, GetCounter()) assert_fails('TextPos.noSuchMember = 2', 'E1337:') assert_fails('TextPos.counter = 5', 'E1335:') assert_fails('TextPos.counter += 5', 'E1335:') assert_fails('echo TextPos._secret', 'E1333:') assert_fails('TextPos._secret = 8', 'E1333:') assert_equal(42, TextPos.anybody) TextPos.anybody = 12 assert_equal(12, TextPos.anybody) TextPos.anybody += 5 assert_equal(17, TextPos.anybody) END v9.CheckSourceSuccess(lines) # example in the help lines =<< trim END vim9script class OtherThing this.size: number static totalSize: number def new(this.size) totalSize += this.size enddef endclass assert_equal(0, OtherThing.totalSize) var to3 = OtherThing.new(3) assert_equal(3, OtherThing.totalSize) var to7 = OtherThing.new(7) assert_equal(10, OtherThing.totalSize) END v9.CheckSourceSuccess(lines) # using static class member twice lines =<< trim END vim9script class HTML static author: string = 'John Doe' static def MacroSubstitute(s: string): string return substitute(s, '{{author}}', author, 'gi') enddef endclass assert_equal('some text', HTML.MacroSubstitute('some text')) assert_equal('some text', HTML.MacroSubstitute('some text')) END v9.CheckSourceSuccess(lines) # access private member in lambda lines =<< trim END vim9script class Foo this._x: number = 0 def Add(n: number): number const F = (): number => this._x + n return F() enddef endclass var foo = Foo.new() assert_equal(5, foo.Add(5)) END v9.CheckSourceSuccess(lines) # access private member in lambda body lines =<< trim END vim9script class Foo this._x: number = 6 def Add(n: number): number var Lam = () => { this._x = this._x + n } Lam() return this._x enddef endclass var foo = Foo.new() assert_equal(13, foo.Add(7)) END v9.CheckSourceSuccess(lines) # check shadowing lines =<< trim END vim9script class Some static count = 0 def Method(count: number) echo count enddef endclass var s = Some.new() s.Method(7) END v9.CheckSourceFailure(lines, 'E1340: Argument already declared in the class: count') lines =<< trim END vim9script class Some static count = 0 def Method(arg: number) var count = 3 echo arg count enddef endclass var s = Some.new() s.Method(7) END v9.CheckSourceFailure(lines, 'E1341: Variable already declared in the class: count') # Test for using an invalid type for a member variable lines =<< trim END vim9script class A this.val: xxx endclass END v9.CheckSourceFailure(lines, 'E1010:') # Test for setting a member on a null object lines =<< trim END vim9script class A public this.val: string endclass def F() var obj: A obj.val = "" enddef F() END v9.CheckSourceFailure(lines, 'E1360: Using a null object') # Test for accessing a member on a null object lines =<< trim END vim9script class A this.val: string endclass def F() var obj: A echo obj.val enddef F() END v9.CheckSourceFailure(lines, 'E1360: Using a null object') # Test for setting a member on a null object, at script level lines =<< trim END vim9script class A public this.val: string endclass var obj: A obj.val = "" END # FIXME(in source): this should give E1360 as well! v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected object<A> but got string') # Test for accessing a member on a null object, at script level lines =<< trim END vim9script class A this.val: string endclass var obj: A echo obj.val END v9.CheckSourceFailure(lines, 'E1360: Using a null object') # Test for no space before or after the '=' when initializing a member # variable lines =<< trim END vim9script class A this.val: number= 10 endclass END v9.CheckSourceFailure(lines, 'E1004:') lines =<< trim END vim9script class A this.val: number =10 endclass END v9.CheckSourceFailure(lines, 'E1004:') # Access a non-existing member lines =<< trim END vim9script class A endclass var a = A.new() var v = a.bar END v9.CheckSourceFailure(lines, 'E1326: Member not found on object "A": bar') enddef func Test_class_garbagecollect() let lines =<< trim END vim9script class Point this.p = [2, 3] static pl = ['a', 'b'] static pd = {a: 'a', b: 'b'} endclass echo Point.pl Point.pd call test_garbagecollect_now() echo Point.pl Point.pd END call v9.CheckSourceSuccess(lines) let lines =<< trim END vim9script interface View endinterface class Widget this.view: View endclass class MyView implements View this.widget: Widget def new() # this will result in a circular reference to this object this.widget = Widget.new(this) enddef endclass var view = MyView.new() # overwrite "view", will be garbage-collected next view = MyView.new() test_garbagecollect_now() END call v9.CheckSourceSuccess(lines) endfunc " Test interface garbage collection func Test_interface_garbagecollect() let lines =<< trim END vim9script interface I this.ro_obj_var: number public this.rw_obj_var: number def ObjFoo(): number endinterface class A implements I static ro_class_var: number = 10 public static rw_class_var: number = 20 static _priv_class_var: number = 30 this.ro_obj_var: number = 40 public this.rw_obj_var: number = 50 this._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.rw_obj_var + this._ObjBar() enddef endclass assert_equal(60, A.ClassFoo()) var o = A.new() assert_equal(150, o.ObjFoo()) test_garbagecollect_now() assert_equal(60, A.ClassFoo()) assert_equal(150, o.ObjFoo()) END call v9.CheckSourceSuccess(lines) endfunc def Test_class_function() var lines =<< trim END vim9script class Value this.value = 0 static objects = 0 def new(v: number) this.value = v ++objects enddef static def GetCount(): number return objects enddef endclass assert_equal(0, Value.GetCount()) var v1 = Value.new(2) assert_equal(1, Value.GetCount()) var v2 = Value.new(7) assert_equal(2, Value.GetCount()) END v9.CheckSourceSuccess(lines) # Test for cleaning up after a class definition failure when using class # functions. lines =<< trim END vim9script class A static def Foo() enddef aaa endclass END v9.CheckSourceFailure(lines, 'E1318:') enddef def Test_class_defcompile() var lines =<< trim END vim9script class C def Fo(i: number): string return i enddef endclass defcompile C.Fo END v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected string but got number') lines =<< trim END vim9script class C static def Fc(): number return 'x' enddef endclass defcompile C.Fc END v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected number but got string') lines =<< trim END vim9script class C static def new() enddef endclass defcompile C.new END v9.CheckSourceFailure(lines, 'E1370: Cannot define a "new" function as static') # Trying to compile a function using a non-existing class variable lines =<< trim END vim9script defcompile x.Foo() END v9.CheckSourceFailure(lines, 'E475:') # Trying to compile a function using a variable which is not a class lines =<< trim END vim9script var x: number defcompile x.Foo() END v9.CheckSourceFailure(lines, 'E475:') # Trying to compile a function without specifying the name lines =<< trim END vim9script class A endclass defcompile A. END v9.CheckSourceFailure(lines, 'E475:') # Trying to compile a non-existing class object member function lines =<< trim END vim9script class A endclass var a = A.new() defcompile a.Foo() END v9.CheckSourceFailureList(lines, ['E1326:', 'E475:']) enddef def Test_class_object_to_string() var lines =<< trim END vim9script class TextPosition this.lnum = 1 this.col = 22 endclass assert_equal("class TextPosition", string(TextPosition)) var pos = TextPosition.new() assert_equal("object of TextPosition {lnum: 1, col: 22}", string(pos)) END v9.CheckSourceSuccess(lines) enddef def Test_interface_basics() var lines =<< trim END vim9script interface Something this.ro_var: string public this.rw_var: list<number> def GetCount(): number endinterface END v9.CheckSourceSuccess(lines) lines =<< trim END interface SomethingWrong static count = 7 endinterface END v9.CheckSourceFailure(lines, 'E1342:') lines =<< trim END vim9script interface Some this.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 count = 7 endinterface END v9.CheckSourceFailure(lines, 'E1343: Interface name must start with an uppercase letter: somethingWrong') lines =<< trim END vim9script interface SomethingWrong this.value: string this.count = 7 def GetCount(): number endinterface END v9.CheckSourceFailure(lines, 'E1344:') lines =<< trim END vim9script interface SomethingWrong this.value: string this.count: number def GetCount(): number return 5 enddef endinterface END v9.CheckSourceFailure(lines, 'E1345: Not a valid command in an interface: return 5') 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_implements_interface() var lines =<< trim END vim9script interface Some this.count: number def Method(nr: number) endinterface class SomeImpl implements Some this.count: number def Method(nr: number) echo nr enddef endclass interface Another this.member: string endinterface class AnotherImpl implements Some, Another this.member = 'abc' this.count = 20 def Method(nr: number) echo nr enddef endclass END v9.CheckSourceSuccess(lines) lines =<< trim END vim9script interface Some this.count: number endinterface class SomeImpl implements Some implements Some this.count: number endclass END v9.CheckSourceFailure(lines, 'E1350:') lines =<< trim END vim9script interface Some this.count: number endinterface class SomeImpl implements Some, Some this.count: number endclass END v9.CheckSourceFailure(lines, 'E1351: Duplicate interface after "implements": Some') lines =<< trim END vim9script interface Some this.counter: number def Method(nr: number) endinterface class SomeImpl implements Some this.count: number def Method(nr: number) echo nr enddef endclass END v9.CheckSourceFailure(lines, 'E1348: Member "counter" of interface "Some" is not implemented') lines =<< trim END vim9script interface Some this.count: number def Methods(nr: number) endinterface class SomeImpl implements Some this.count: number def Method(nr: number) echo nr enddef endclass END v9.CheckSourceFailure(lines, 'E1349: Method "Methods" of interface "Some" is not implemented') # Check different order of members in class and interface works. lines =<< trim END vim9script interface Result public this.label: string this.errpos: number endinterface # order of members is opposite of interface class Failure implements Result this.errpos: number = 42 public this.label: string = 'label' endclass def Test() var result: Result = Failure.new() assert_equal('label', result.label) assert_equal(42, result.errpos) result.label = 'different' assert_equal('different', 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:') # Trailing characters after a class name lines =<< trim END vim9script class A bbb endclass END v9.CheckSourceFailure(lines, 'E488:') # using "implements" with a non-existing class lines =<< trim END vim9script class A implements B endclass END v9.CheckSourceFailure(lines, 'E1346:') # using "implements" with a regular class lines =<< trim END vim9script class A endclass class B implements A endclass END v9.CheckSourceFailure(lines, 'E1347:') # using "implements" with a variable lines =<< trim END vim9script var T: number = 10 class A implements T endclass END v9.CheckSourceFailure(lines, 'E1347:') # 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:') 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') 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') lines =<< trim END vim9script interface One def IsEven(nr: number): bool endinterface class Two implements One def IsEven(nr: number, ...extra: list<number>): bool enddef endclass END v9.CheckSourceFailure(lines, 'E1383: Method "IsEven": type mismatch, expected func(number): bool but got func(number, ...list<number>): bool') # access superclass interface members from subclass, mix variable order lines =<< trim END vim9script interface I1 public this.mvar1: number public this.mvar2: number endinterface # NOTE: the order is swapped class A implements I1 public this.mvar2: number public this.mvar1: number public static svar2: number public static svar1: number def new() svar1 = 11 svar2 = 12 this.mvar1 = 111 this.mvar2 = 112 enddef endclass class B extends A def new() 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<number> 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 public this.mvar1: number public this.mvar2: number endinterface interface I2 public this.mvar3: number public this.mvar4: number endinterface class A implements I1 public static svar1: number public static svar2: number public this.mvar1: number public this.mvar2: number def new() svar1 = 11 svar2 = 12 this.mvar1 = 111 this.mvar2 = 112 enddef endclass class B extends A implements I2 public static svar3: number public static svar4: number public this.mvar3: number public this.mvar4: number def new() svar3 = 23 svar4 = 24 this.mvar1 = 121 this.mvar2 = 122 this.mvar3 = 123 this.mvar4 = 124 enddef endclass class C extends B public static svar5: number def new() svar5 = 1001 this.mvar1 = 131 this.mvar2 = 132 this.mvar3 = 133 this.mvar4 = 134 enddef endclass def F2(i: I1): list<number> return [ i.mvar1, i.mvar2 ] enddef def F4(i: I2): list<number> 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) 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 def Test_class_used_as_type() var lines =<< trim END vim9script class Point this.x = 0 this.y = 0 endclass var p: Point p = Point.new(2, 33) assert_equal(2, p.x) assert_equal(33, p.y) END v9.CheckSourceSuccess(lines) lines =<< trim END vim9script interface HasX this.x: number endinterface class Point implements HasX this.x = 0 this.y = 0 endclass var p: Point p = Point.new(2, 33) var hx = p assert_equal(2, hx.x) END v9.CheckSourceSuccess(lines) lines =<< trim END vim9script class Point this.x = 0 this.y = 0 endclass var p: Point p = 'text' END v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected object<Point> but got string') enddef def Test_class_extends() var lines =<< trim END vim9script class Base this.one = 1 def GetOne(): number return this.one enddef endclass class Child extends Base this.two = 2 def GetTotal(): number return this.one + this.two 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) lines =<< trim END vim9script class Base this.one = 1 endclass class Child extends Base this.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 class Base this.one = 1 endclass class Child extends Base extends Base this.two = 2 endclass END v9.CheckSourceFailure(lines, 'E1352: Duplicate "extends"') lines =<< trim END vim9script class Child extends BaseClass this.two = 2 endclass END v9.CheckSourceFailure(lines, 'E1353: Class name not found: BaseClass') lines =<< trim END vim9script var SomeVar = 99 class Child extends SomeVar this.two = 2 endclass END v9.CheckSourceFailure(lines, 'E1354: Cannot extend SomeVar') lines =<< trim END vim9script class Base this.name: string def ToString(): string return this.name enddef endclass class Child extends Base this.age: number def ToString(): string return super.ToString() .. ': ' .. this.age enddef endclass var o = Child.new('John', 42) assert_equal('John: 42', o.ToString()) END v9.CheckSourceSuccess(lines) lines =<< trim END vim9script class Child this.age: number def ToString(): number return this.age enddef def ToString(): string return this.age enddef endclass END v9.CheckSourceFailure(lines, 'E1355: Duplicate function: ToString') lines =<< trim END vim9script class Child this.age: number def ToString(): string return super .ToString() .. ': ' .. this.age enddef endclass var o = Child.new(42) echo o.ToString() END v9.CheckSourceFailure(lines, 'E1356:') lines =<< trim END vim9script class Base this.name: string def ToString(): string return this.name enddef endclass var age = 42 def ToString(): string return super.ToString() .. ': ' .. age enddef echo ToString() END v9.CheckSourceFailure(lines, 'E1357:') lines =<< trim END vim9script class Child this.age: number def ToString(): string return super.ToString() .. ': ' .. this.age enddef endclass var o = Child.new(42) echo o.ToString() END v9.CheckSourceFailure(lines, 'E1358:') lines =<< trim END vim9script class Base this.name: string static def ToString(): string return 'Base class' enddef endclass class Child extends Base this.age: number def ToString(): string return Base.ToString() .. ': ' .. this.age enddef endclass var o = Child.new('John', 42) assert_equal('Base class: 42', o.ToString()) END v9.CheckSourceSuccess(lines) lines =<< trim END vim9script class Base this.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, 'E1375: Class member "new" accessible only using class "Child"') # base class with more than one object member lines =<< trim END vim9script class Result this.success: bool this.value: any = null endclass 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.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:') enddef def Test_using_base_class() 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 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.CheckSourceSuccess(lines) unlet g:result # Using super, Child invokes Base method which has optional arg. #12471 lines =<< trim END vim9script class Base this.success: bool = false def Method(arg = 0) this.success = true enddef endclass class Child extends Base def new() super.Method() enddef endclass var obj = Child.new() assert_equal(true, obj.success) END v9.CheckSourceSuccess(lines) enddef def Test_class_import() var lines =<< trim END vim9script export class Animal this.kind: string this.name: string endclass END writefile(lines, 'Xanimal.vim', 'D') lines =<< trim END vim9script import './Xanimal.vim' as animal var a: animal.Animal a = animal.Animal.new('fish', 'Eric') assert_equal('fish', a.kind) assert_equal('Eric', a.name) 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 Test_abstract_class() var lines =<< trim END vim9script abstract class Base this.name: string endclass class Person extends Base this.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 abstract class Base this.name: string endclass class Person extends Base this.age: number endclass var p = Base.new('Peter') END v9.CheckSourceFailure(lines, 'E1325: Method not found on class "Base": new') lines =<< trim END abstract class Base this.name: string endclass END v9.CheckSourceFailure(lines, 'E1316:') # Abstract class cannot have a "new" function lines =<< trim END vim9script abstract class Base def new() enddef endclass END v9.CheckSourceFailure(lines, 'E1359:') enddef def Test_closure_in_class() var lines =<< trim END vim9script class Foo this.y: list<string> = ['B'] def new() g:result = filter(['A', 'B'], (_, v) => index(this.y, v) == -1) enddef endclass Foo.new() assert_equal(['A'], g:result) END v9.CheckSourceSuccess(lines) enddef def Test_call_constructor_from_legacy() var lines =<< trim END vim9script var newCalled = 'false' class A def new() newCalled = 'true' enddef endclass export def F(options = {}): any return A enddef g:p = F() legacy call p.new() assert_equal('true', newCalled) END v9.CheckSourceSuccess(lines) enddef def Test_defer_with_object() var lines =<< trim END vim9script class CWithEE def Enter() g:result ..= "entered/" enddef def Exit() g:result ..= "exited" 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 lines =<< trim END vim9script class BaseWithEE def Enter() g:result ..= "entered-base/" enddef def Exit() g:result ..= "exited-base" enddef endclass class CWithEE extends BaseWithEE def Enter() g:result ..= "entered-child/" enddef def Exit() g:result ..= "exited-child" 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) 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 Property this.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 this.value2: bool endclass def Observe(obj: Property, who: Observer) obj.Register(who) enddef var p = Bool.new(false) var myObserver = Observer.new() Observe(p, myObserver) p.Set(true) END v9.CheckSourceSuccess(lines) enddef " Test for calling a method in a class that is extended def Test_call_method_in_extended_class() 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 enddef endclass class Bool extends Property endclass 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 def Test_instanceof() var lines =<< trim END vim9script class Base1 endclass class Base2 extends Base1 endclass interface Intf1 endinterface class Mix1 implements Intf1 endclass class Base3 extends Mix1 endclass 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_false(instanceof(b3, [])) assert_true(instanceof(b3, [Base1, Base2, Intf1])) 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_false(instanceof(a3, [])) assert_true(instanceof(a3, [Base1, Base2, Intf1])) enddef Foo() END v9.CheckSourceSuccess(lines) enddef " 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 class Widget this._lnum: number = 1 def SetY(lnum: number) this._lnum = lnum enddef def Text(): string return '' enddef endclass class Foo extends Widget def Text(): string return '<Foo>' enddef endclass def Stack(w1: Widget, w2: Widget): list<Widget> w1.SetY(1) w2.SetY(2) return [w1, w2] enddef var foo1 = Foo.new() var foo2 = Foo.new() var l = Stack(foo1, foo2) END v9.CheckSourceSuccess(lines) enddef " Test for calling methods from three levels of classes def Test_multi_level_method_call() 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 enddef def Func2() A_func2 += 1 enddef def Func3() A_func3 += 1 enddef endclass class B extends A def Func2() B_func2 += 1 enddef def Func3() B_func3 += 1 enddef endclass class C extends B def Func3() C_func3 += 1 enddef endclass def A_CallFuncs(a: A) a.Func1() a.Func2() a.Func3() enddef def B_CallFuncs(b: B) b.Func1() b.Func2() b.Func3() enddef def C_CallFuncs(c: C) c.Func1() c.Func2() c.Func3() enddef 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.CheckSourceSuccess(lines) enddef " Test for using members from three levels of classes def Test_multi_level_member_access() var lines =<< trim END vim9script class A public this.val1: number = 0 endclass class B extends A public this.val2: number = 0 endclass class C extends B public this.val3: number = 0 endclass def A_members(a: A) a.val1 += 1 enddef def B_members(b: B) b.val1 += 1 b.val2 += 1 enddef 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 " Test expansion of <stack> with class methods. def Test_stack_expansion_with_methods() var lines =<< trim END vim9script class C def M1() F0() enddef endclass def F0() assert_match('<SNR>\d\+_F\[1\]\.\.C\.M1\[1\]\.\.<SNR>\d\+_F0\[1\]$', expand('<stack>')) enddef def F() C.new().M1() enddef F() END v9.CheckSourceSuccess(lines) enddef " 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 class C this._bufnr: number def new(this._bufnr) if !bufexists(this._bufnr) this._bufnr = -1 endif enddef endclass var c = C.new(12345) assert_equal('object<C>', typename(c)) var v1: C v1 = C.new(12345) assert_equal('object<C>', typename(v1)) def F() var v2: C v2 = C.new(12345) assert_equal('object<C>', typename(v2)) enddef F() END v9.CheckSourceSuccess(lines) # new() uses the default return type and an empty 'return' statement lines =<< trim END vim9script class C this._bufnr: number def new(this._bufnr) if !bufexists(this._bufnr) this._bufnr = -1 return endif enddef endclass var c = C.new(12345) assert_equal('object<C>', typename(c)) var v1: C v1 = C.new(12345) assert_equal('object<C>', typename(v1)) def F() var v2: C v2 = C.new(12345) assert_equal('object<C>', typename(v2)) enddef F() END v9.CheckSourceSuccess(lines) # new() uses "any" return type and returns "this" lines =<< trim END vim9script class C this._bufnr: number def new(this._bufnr): any if !bufexists(this._bufnr) this._bufnr = -1 return this endif enddef endclass END v9.CheckSourceFailure(lines, 'E1365:') # new() uses 'Dict' return type and returns a Dict lines =<< trim END vim9script class C this._state: dict<any> def new(): dict<any> this._state = {} return this._state enddef endclass var c = C.new() assert_equal('object<C>', typename(c)) END v9.CheckSourceFailure(lines, 'E1365:') enddef " Test for checking a member initialization type at run time. def Test_runtime_type_check_for_member_init() 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 this._foo: bool = F() endclass var c1 = C.new() var c2 = C.new() END v9.CheckSourceFailure(lines, 'E1012:') enddef " Test for locking a variable referring to an object and reassigning to another " object. def Test_object_lockvar() var lines =<< trim END vim9script class C this.val: number def new(this.val) enddef endclass var some_dict: dict<C> = { 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'] enddef F() assert_equal(3, current.val) G() assert_equal(2, current.val) END v9.CheckSourceSuccess(lines) enddef " Test for a private object method def Test_private_object_method() # Try calling a private 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 private method: _Foo()') # Try calling a private 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 private method: _Foo()') # Use a private 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 private 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 private 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') # Try calling a private 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 private method: _Foo') # Try to use "public" keyword when defining a private method lines =<< trim END vim9script class A public def _Foo() enddef endclass var a = A.new() a._Foo() END v9.CheckSourceFailure(lines, 'E1331: Public must be followed by "this" or "static"') # Define two private 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') # Define a private 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') # Define an object method and a private 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') # Call a public method and a private method from a private 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 private 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 private method: _Foo()') # Call a private 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 private 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 private method: _Foo()') # 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') enddef " Test for an private class method def Test_private_class_method() # Try calling a class private 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 private method: _Foo()') # Try calling a class private 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 private method: _Foo()') # Try calling a class private 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 private method: _Foo') # Try calling a class private 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 private method: _Foo') # Use a class private method from an object method lines =<< trim END vim9script class A static def _Foo(): number return 1234 enddef def Bar() assert_equal(1234, A._Foo()) enddef endclass var a = A.new() a.Bar() END v9.CheckSourceSuccess(lines) # Use a class private method from another class private method lines =<< trim END vim9script class A static def _Foo1(): number return 1234 enddef static def _Foo2() assert_equal(1234, A._Foo1()) enddef def Bar() A._Foo2() enddef endclass var a = A.new() a.Bar() END v9.CheckSourceSuccess(lines) # Declare a class method and a class private method with the same name lines =<< trim END vim9script class A static def _Foo() enddef static def Foo() enddef endclass var a = A.new() END v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo') # Try calling a class private method from another class lines =<< trim END vim9script 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 private method: _Foo()') # Call a private 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()) END v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo()') # Call a private class method from a child class private class 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 static def Baz(): number return A._Foo() enddef endclass assert_equal(1234, C.Baz()) END v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo()') # Call a private 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() enddef endclass class C extends B def Baz(): number enddef endclass var c = C.new() assert_equal(1234, C._Foo()) END v9.CheckSourceFailure(lines, 'E1325: Method not found on class "C": _Foo') enddef " Test for using the return value of a class/object method as a function " argument. def Test_objmethod_funcarg() var lines =<< trim END vim9script class C def Foo(): string return 'foo' enddef endclass def Bar(a: number, s: string): string return s enddef def Baz(c: C) assert_equal('foo', Bar(10, c.Foo())) enddef var t = C.new() Baz(t) END v9.CheckSourceSuccess(lines) lines =<< trim END vim9script class C static def Foo(): string return 'foo' enddef endclass def Bar(a: number, s: string): string return s enddef def Baz() assert_equal('foo', Bar(10, C.Foo())) enddef Baz() END v9.CheckSourceSuccess(lines) enddef def Test_static_inheritence() # subclasses get their own static copy var lines =<< trim END vim9script class A static _svar: number this._mvar: number def new() _svar = 1 this._mvar = 101 enddef def AccessObject(): number return this._mvar enddef def AccessStaticThroughObject(): number return _svar enddef endclass class B extends A def new() this._mvar = 102 enddef endclass class C extends B def new() this._mvar = 103 enddef def AccessPrivateStaticThroughClassName(): number assert_equal(1, A._svar) return 444 enddef endclass var oa = A.new() var ob = B.new() var oc = C.new() assert_equal(101, oa.AccessObject()) assert_equal(102, ob.AccessObject()) assert_equal(103, oc.AccessObject()) assert_fails('echo oc.AccessPrivateStaticThroughClassName()', 'E1333: Cannot access private member: _svar') # 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 " Test for declaring duplicate object and class members def Test_dup_member_variable() # Duplicate member variable var lines =<< trim END vim9script class C this.val = 10 this.val = 20 endclass END v9.CheckSourceFailure(lines, 'E1369: Duplicate member: val') # Duplicate private member variable lines =<< trim END vim9script class C this._val = 10 this._val = 20 endclass END v9.CheckSourceFailure(lines, 'E1369: Duplicate member: _val') # Duplicate public member variable lines =<< trim END vim9script class C public this.val = 10 public this.val = 20 endclass END v9.CheckSourceFailure(lines, 'E1369: Duplicate member: val') # Duplicate private member variable lines =<< trim END vim9script class C this.val = 10 this._val = 20 endclass END v9.CheckSourceFailure(lines, 'E1369: Duplicate member: _val') # Duplicate public and private member variable lines =<< trim END vim9script class C this._val = 20 public this.val = 10 endclass END v9.CheckSourceFailure(lines, 'E1369: Duplicate member: val') # Duplicate class member variable lines =<< trim END vim9script class C static s: string = "abc" static _s: string = "def" endclass END v9.CheckSourceFailure(lines, 'E1369: Duplicate member: _s') # Duplicate public and private class member variable lines =<< trim END vim9script class C public static s: string = "abc" static _s: string = "def" endclass END v9.CheckSourceFailure(lines, 'E1369: Duplicate member: _s') # Duplicate class and object member variable lines =<< trim END vim9script class C static val = 10 this.val = 20 def new() enddef endclass var c = C.new() assert_equal(10, C.val) assert_equal(20, c.val) END v9.CheckSourceSuccess(lines) # Duplicate object member variable in a derived class lines =<< trim END vim9script class A this.val = 10 endclass class B extends A endclass class C extends B this.val = 20 endclass END v9.CheckSourceFailure(lines, 'E1369: Duplicate member: val') # Duplicate object private member variable in a derived class lines =<< trim END vim9script class A this._val = 10 endclass class B extends A endclass class C extends B this._val = 20 endclass END v9.CheckSourceFailure(lines, 'E1369: Duplicate member: _val') # Duplicate object private member variable in a derived class lines =<< trim END vim9script class A this.val = 10 endclass class B extends A endclass class C extends B this._val = 20 endclass END v9.CheckSourceFailure(lines, 'E1369: Duplicate member: _val') # Duplicate object member variable in a derived class lines =<< trim END vim9script class A this._val = 10 endclass class B extends A endclass class C extends B this.val = 20 endclass END v9.CheckSourceFailure(lines, 'E1369: Duplicate member: val') # Two member variables with a common prefix lines =<< trim END vim9script class A public static svar2: number public static svar: number endclass END v9.CheckSourceSuccess(lines) enddef " Test for accessing a private member outside a class in a def function def Test_private_member_access_outside_class() # private object member variable var lines =<< trim END vim9script class A this._val = 10 def GetVal(): number return this._val enddef endclass def T() var a = A.new() a._val = 20 enddef T() END v9.CheckSourceFailure(lines, 'E1333: Cannot access private member: _val') # access a non-existing private object member variable lines =<< trim END vim9script class A this._val = 10 endclass def T() var a = A.new() a._a = 1 enddef T() END v9.CheckSourceFailure(lines, 'E1089: Unknown variable: _a') # private static member variable lines =<< trim END vim9script class A static _val = 10 endclass def T() var a = A.new() var x = a._val enddef T() END v9.CheckSourceFailure(lines, 'E1375: Class member "_val" accessible only using class "A"') # private static member variable lines =<< trim END vim9script class A static _val = 10 endclass def T() var a = A.new() a._val = 3 enddef T() END v9.CheckSourceFailure(lines, 'E1374: Class member "_val" accessible only inside class "A"') # private static class variable lines =<< trim END vim9script class A static _val = 10 endclass def T() var x = A._val enddef T() END v9.CheckSourceFailure(lines, 'E1333: Cannot access private member: _val') # private static class variable lines =<< trim END vim9script class A static _val = 10 endclass def T() A._val = 3 enddef T() END v9.CheckSourceFailure(lines, 'E1333: Cannot access private member: _val') 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 public this.val: number endinterface class B implements A this.val = 10 endclass END v9.CheckSourceFailure(lines, 'E1367: Access level of member "val" of interface "A" is different') lines =<< trim END vim9script interface A this.val: number endinterface class B implements A public this.val = 10 endclass END v9.CheckSourceFailure(lines, 'E1367: Access level of member "val" of interface "A" is different') 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 this.val: number endclass def T() var a = A.new() a.val = 20 enddef T() END v9.CheckSourceFailure(lines, 'E46: Cannot change read-only variable "val"') enddef " 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 this.var1: number = 10 public static var2: list<number> = [1, 2] public static var3: dict<number> = {a: 1, b: 2} static _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 private member: _priv_var4') 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 svar1: list<number> = [1] public static svar2: list<number> = [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() END v9.CheckSourceSuccess(lines) # Cannot read from a class variable using an object in script context lines =<< trim END vim9script class A public this.var1: number public static svar2: list<number> = [1] endclass var a = A.new() echo a.svar2 END v9.CheckSourceFailure(lines, 'E1375: Class member "svar2" accessible only using class "A"') # Cannot write to a class variable using an object in script context lines =<< trim END vim9script class A public this.var1: number public static svar2: list<number> = [1] endclass var a = A.new() a.svar2 = [2] END v9.CheckSourceFailure(lines, 'E1375: Class member "svar2" accessible only using class "A"') # Cannot read from a class variable using an object in def method context lines =<< trim END vim9script class A public this.var1: number public static svar2: list<number> = [1] endclass def T() var a = A.new() echo a.svar2 enddef T() END v9.CheckSourceFailure(lines, 'E1375: Class member "svar2" accessible only using class "A"') # Cannot write to a class variable using an object in def method context lines =<< trim END vim9script class A public this.var1: number public static svar2: list<number> = [1] endclass def T() var a = A.new() a.svar2 = [2] enddef T() END v9.CheckSourceFailure(lines, 'E1374: Class member "svar2" accessible only inside class "A"') 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. " FIXME: This test fails. " 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 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()]) END v9.CheckSourceSuccess(lines) # Don't define an abstract method lines =<< trim END vim9script abstract class A abstract def Foo() endclass class B extends A endclass END v9.CheckSourceFailure(lines, 'E1373: Abstract method "Foo" is not implemented') # Use abstract method in a concrete class lines =<< trim END vim9script class A abstract def Foo() endclass class B extends A endclass END v9.CheckSourceFailure(lines, 'E1372: Abstract method "abstract def Foo()" cannot be defined in a concrete class') # Use abstract method in an interface lines =<< trim END vim9script interface A abstract def Foo() endinterface class B implements A def Foo() enddef endclass END v9.CheckSourceSuccess(lines) # Abbreviate the "abstract" keyword lines =<< trim END vim9script class A abs def Foo() endclass END v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: abs def Foo()') # Use "abstract" with a member variable lines =<< trim END vim9script abstract class A abstract this.val = 10 endclass END v9.CheckSourceFailure(lines, 'E1371: Abstract must be followed by "def" or "static"') # Use a static abstract method lines =<< trim END vim9script abstract class A abstract static def Foo(): number endclass class B extends A static def Foo(): number return 4 enddef endclass assert_equal(4, B.Foo()) END v9.CheckSourceSuccess(lines) # Type mismatch between abstract method and concrete method lines =<< trim END vim9script abstract class A abstract def Foo(a: string, b: number): list<number> endclass class B extends A def Foo(a: number, b: string): list<string> return [] enddef endclass END v9.CheckSourceFailure(lines, 'E1383: Method "Foo": type mismatch, expected func(string, number): list<number> but got func(number, string): list<string>') # Use an abstract class to invoke an abstract method # FIXME: This should fail lines =<< trim END vim9script abstract class A abstract static def Foo() endclass A.Foo() END v9.CheckSourceSuccess(lines) # Invoke an abstract method from a def function lines =<< trim END vim9script abstract class A abstract def Foo(): list<number> endclass class B extends A def Foo(): list<number> return [3, 5] enddef endclass def Bar(c: B) assert_equal([3, 5], c.Foo()) enddef var b = B.new() Bar(b) END 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 var lines =<< trim END vim9script class A static def Foo() echo "foo" enddef endclass class B extends A def Bar() Foo() enddef endclass var b = B.new() b.Bar() END v9.CheckSourceFailure(lines, 'E1374: Class member "Foo" accessible only inside class "A"') enddef " Test for calling a class method using an object in a def function context and " script context. def Test_class_method_call_using_object() # script context var lines =<< trim END vim9script class A static def Foo(): list<string> return ['a', 'b'] enddef def Bar() assert_equal(['a', 'b'], A.Foo()) assert_equal(['a', 'b'], Foo()) enddef endclass def T() assert_equal(['a', 'b'], A.Foo()) var t_a = A.new() t_a.Bar() enddef assert_equal(['a', 'b'], A.Foo()) var a = A.new() a.Bar() T() END v9.CheckSourceSuccess(lines) # script context lines =<< trim END vim9script class A static def Foo(): string return 'foo' enddef endclass var a = A.new() assert_equal('foo', a.Foo()) END v9.CheckSourceFailure(lines, 'E1375: Class member "Foo" accessible only using class "A"') # def function context lines =<< trim END vim9script class A static def Foo(): string return 'foo' enddef endclass def T() var a = A.new() assert_equal('foo', a.Foo()) enddef T() END v9.CheckSourceFailure(lines, 'E1375: Class member "Foo" accessible only using class "A"') enddef def Test_class_variable() var lines =<< trim END vim9script class A public static val: number = 10 static def ClassFunc() assert_equal(10, val) enddef def ObjFunc() assert_equal(10, val) enddef endclass class B extends A 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.CheckSourceSuccess(lines) # Modifying a parent class variable from a child class method lines =<< trim END vim9script class A static val: number = 10 endclass class B extends A static def ClassFunc() val = 20 enddef endclass B.ClassFunc() END v9.CheckSourceFailure(lines, 'E1374: Class member "val" accessible only inside class "A"') # Reading a parent class variable from a child class method lines =<< trim END vim9script class A static val: number = 10 endclass class B extends A static def ClassFunc() var i = val enddef endclass B.ClassFunc() END v9.CheckSourceFailure(lines, 'E1374: Class member "val" accessible only inside class "A"') # Modifying a parent class variable from a child object method lines =<< trim END vim9script class A static val: number = 10 endclass class B extends A def ObjFunc() val = 20 enddef endclass var b = B.new() b.ObjFunc() END v9.CheckSourceFailure(lines, 'E1374: Class member "val" accessible only inside class "A"') # Reading a parent class variable from a child object method lines =<< trim END vim9script class A static 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, 'E1374: Class member "val" accessible only inside class "A"') # Modifying a class variable using an object at script level lines =<< trim END vim9script class A static val: number = 10 endclass var a = A.new() a.val = 20 END v9.CheckSourceFailure(lines, 'E1375: Class member "val" accessible only using class "A"') # Reading a class variable using an object at script level lines =<< trim END vim9script class A static val: number = 10 endclass var a = A.new() var i = a.val END v9.CheckSourceFailure(lines, 'E1375: Class member "val" accessible only using class "A"') # Modifying a class variable using an object at function level lines =<< trim END vim9script class A static val: number = 10 endclass def T() var a = A.new() a.val = 20 enddef T() END v9.CheckSourceFailure(lines, 'E1374: Class member "val" accessible only inside class "A"') # Reading a class variable using an object at function level lines =<< trim END vim9script class A static val: number = 10 endclass def T() var a = A.new() var i = a.val enddef T() END v9.CheckSourceFailure(lines, 'E1375: Class member "val" accessible only using class "A"') enddef " 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 class A static sval = 100 static def Check() assert_equal(100, sval) enddef def GetVal(): number return sval enddef endclass class B extends A static sval = 200 static def Check() assert_equal(200, sval) enddef def GetVal(): number return sval enddef endclass 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(200, b.GetVal()) assert_equal(200, T1(b)) assert_equal(200, T2(b)) END v9.CheckSourceSuccess(lines) # duplicate class variable and class method lines =<< trim END vim9script class A static sval = 100 static def Check() assert_equal(100, sval) enddef def GetVal(): number return sval enddef endclass class B extends A static sval = 200 static def Check() assert_equal(200, sval) enddef endclass 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, 'E1376: Object member "Foo" accessible only using class "A" object') # Invoke an object method using a class in def function context lines =<< trim END vim9script class A def Foo() echo "foo" enddef endclass def T() A.Foo() enddef T() END v9.CheckSourceFailure(lines, 'E1376: Object member "Foo" accessible only using class "A" object') enddef " 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 Foo() enddef endclass END v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo') # Duplicate private instance method lines =<< trim END vim9script class A static def Foo() enddef def _Foo() enddef endclass END v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo') # Duplicate class method lines =<< trim END vim9script class A def Foo() enddef static def Foo() enddef endclass END v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo') # Duplicate private class method lines =<< trim END vim9script class A def Foo() enddef static def _Foo() enddef endclass END v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo') # Duplicate private class and object method lines =<< trim END vim9script class A def _Foo() enddef static def _Foo() enddef endclass END v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo') enddef " Test for an instance method access level comparison with parent instance " methods. def Test_instance_method_access_level() # Private method in subclass var lines =<< trim END vim9script class A def Foo() enddef endclass class B extends A endclass class C extends B def _Foo() enddef endclass END v9.CheckSourceFailure(lines, 'E1377: Access level of method "_Foo" is different in class "A"') # Public method in subclass lines =<< trim END vim9script class A def _Foo() enddef endclass class B extends A endclass class C extends B def Foo() enddef endclass END v9.CheckSourceFailure(lines, 'E1377: Access level of method "Foo" is different in class "A"') enddef def Test_extend_empty_class() var lines =<< trim END vim9script class A endclass class B extends A endclass class C extends B public static rw_class_var = 1 public this.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.CheckSourceSuccess(lines) enddef " A interface cannot have a static variable or a static method or a private " variable or a private method def Test_interface_with_unsupported_members() var lines =<< trim END vim9script interface A static num: number endinterface END v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface') lines =<< trim END vim9script interface A static _num: number endinterface END v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface') lines =<< trim END vim9script interface A public static num: number endinterface END v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface') lines =<< trim END vim9script interface A public static _num: number endinterface END v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface') lines =<< trim END vim9script interface A static def Foo(d: dict<any>): list<string> endinterface END v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface') lines =<< trim END vim9script interface A static def _Foo(d: dict<any>): list<string> endinterface END v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface') lines =<< trim END vim9script interface A this._Foo: list<string> endinterface END v9.CheckSourceFailure(lines, 'E1379: Private variable not supported in an interface') lines =<< trim END vim9script interface A def _Foo(d: dict<any>): list<string> endinterface END v9.CheckSourceFailure(lines, 'E1380: Private method not supported in an interface') enddef " Test for extending an interface def Test_extend_interface() var lines =<< trim END vim9script interface A this.var1: list<string> def Foo() endinterface interface B extends A public this.var2: dict<string> def Bar() endinterface class C implements A, B this.var1 = [1, 2] def Foo() enddef public this.var2 = {a: '1'} def Bar() enddef endclass END v9.CheckSourceSuccess(lines) lines =<< trim END vim9script interface A def Foo() endinterface interface B extends A public this.var2: dict<string> endinterface class C implements A, B public this.var2 = {a: '1'} endclass END v9.CheckSourceFailure(lines, 'E1349: Method "Foo" of interface "A" is not implemented') lines =<< trim END vim9script interface A def Foo() endinterface interface B extends A public this.var2: dict<string> endinterface class C implements A, B def Foo() enddef endclass END v9.CheckSourceFailure(lines, 'E1348: Member "var2" of interface "B" is not implemented') # 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') # 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') # 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"') # 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') # Variable type in an extended interface is of different type lines =<< trim END vim9script interface A this.val1: number endinterface interface B extends A this.val2: string endinterface interface C extends B this.val1: string this.val2: number endinterface END v9.CheckSourceFailure(lines, 'E1382: Member "val1": type mismatch, expected number but got string') 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<list<number>> def F2(): list<list<number>> def F3(): list<list<number>> this.var1: list<dict<number>> this.var2: list<dict<number>> this.var3: list<dict<number>> endinterface class A def A1() enddef def F3(): list<list<number>> return [[3]] enddef this.v1: list<list<number>> = [[0]] this.var3 = [{c: 30}] endclass class B extends A def B1() enddef def F2(): list<list<number>> return [[2]] enddef this.v2: list<list<number>> = [[0]] this.var2 = [{b: 20}] endclass class C extends B implements Intf def C1() enddef def F1(): list<list<number>> return [[1]] enddef this.v3: list<list<number>> = [[0]] this.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') # 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') # One of the interface variables is not present lines =<< trim END vim9script interface Intf this.var1: list<dict<number>> this.var2: list<dict<number>> this.var3: list<dict<number>> endinterface class A this.v1: list<list<number>> = [[0]] endclass class B extends A this.v2: list<list<number>> = [[0]] this.var2 = [{b: 20}] endclass class C extends B implements Intf this.v3: list<list<number>> = [[0]] this.var1 = [{a: 10}] endclass END v9.CheckSourceFailure(lines, 'E1348: Member "var3" of interface "Intf" is not implemented') # One of the interface variables is of different type lines =<< trim END vim9script interface Intf this.var1: list<dict<number>> this.var2: list<dict<number>> this.var3: list<dict<number>> endinterface class A this.v1: list<list<number>> = [[0]] this.var3: list<dict<string>> endclass class B extends A this.v2: list<list<number>> = [[0]] this.var2 = [{b: 20}] endclass class C extends B implements Intf this.v3: list<list<number>> = [[0]] this.var1 = [{a: 10}] endclass END v9.CheckSourceFailure(lines, 'E1382: Member "var3": type mismatch, expected list<dict<number>> but got list<dict<string>>') 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 this.n1: number def Foo1(): number endinterface interface B extends A this.n2: number this.n1: number def Foo2(): number def Foo1(): number endinterface class C implements B this.n1 = 10 this.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 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 this.val: list<dict<string>> endinterface class B implements A this.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 this.val: list<dict<string>> endinterface class B implements A this.val = {a: 1, b: 2} endclass var b = B.new() END v9.CheckSourceFailure(lines, 'E1382: Member "val": type mismatch, expected list<dict<string>> but got dict<number>') enddef " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker