diff options
Diffstat (limited to 'src/testdir/test_vim9_class.vim')
-rw-r--r-- | src/testdir/test_vim9_class.vim | 1752 |
1 files changed, 1752 insertions, 0 deletions
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim new file mode 100644 index 0000000..bc8a8e1 --- /dev/null +++ b/src/testdir/test_vim9_class.vim @@ -0,0 +1,1752 @@ +" Test Vim9 classes + +source check.vim +import './vim9.vim' as v9 + +def Test_class_basic() + var lines =<< trim END + class NotWorking + endclass + END + v9.CheckScriptFailure(lines, 'E1316:') + + lines =<< trim END + vim9script + class notWorking + endclass + END + v9.CheckScriptFailure(lines, 'E1314:') + + lines =<< trim END + vim9script + class Not@working + endclass + END + v9.CheckScriptFailure(lines, 'E1315:') + + lines =<< trim END + vim9script + abstract noclass Something + endclass + END + v9.CheckScriptFailure(lines, 'E475:') + + lines =<< trim END + vim9script + abstract classy Something + endclass + END + v9.CheckScriptFailure(lines, 'E475:') + + lines =<< trim END + vim9script + class Something + endcl + END + v9.CheckScriptFailure(lines, 'E1065:') + + lines =<< trim END + vim9script + class Something + endclass school's out + END + v9.CheckScriptFailure(lines, 'E488:') + + lines =<< trim END + vim9script + class Something + endclass | echo 'done' + END + v9.CheckScriptFailure(lines, 'E488:') + + lines =<< trim END + vim9script + class Something + this + endclass + END + v9.CheckScriptFailure(lines, 'E1317:') + + lines =<< trim END + vim9script + class Something + this. + endclass + END + v9.CheckScriptFailure(lines, 'E1317:') + + lines =<< trim END + vim9script + class Something + this .count + endclass + END + v9.CheckScriptFailure(lines, 'E1317:') + + lines =<< trim END + vim9script + class Something + this. count + endclass + END + v9.CheckScriptFailure(lines, 'E1317:') + + lines =<< trim END + vim9script + class Something + this.count: number + that.count + endclass + END + v9.CheckScriptFailure(lines, 'E1318: Not a valid command in a class: that.count') + + lines =<< trim END + vim9script + class Something + this.count + endclass + END + v9.CheckScriptFailure(lines, 'E1022:') + + lines =<< trim END + vim9script + class Something + def new() + this.state = 0 + enddef + endclass + var obj = Something.new() + END + v9.CheckScriptFailure(lines, 'E1089:') + + lines =<< trim END + vim9script + class Something + this.count : number + endclass + END + v9.CheckScriptFailure(lines, 'E1059:') + + lines =<< trim END + vim9script + class Something + this.count:number + endclass + END + v9.CheckScriptFailure(lines, 'E1069:') + + 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.CheckScriptSuccess(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.CheckScriptFailure(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.CheckScriptSuccess(lines) +enddef + +def Test_class_interface_wrong_end() + var lines =<< trim END + vim9script + abstract class SomeName + this.member = 'text' + endinterface + END + v9.CheckScriptFailure(lines, 'E476: Invalid command: endinterface, expected endclass') + + lines =<< trim END + vim9script + export interface AnotherName + this.member: string + endclass + END + v9.CheckScriptFailure(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.CheckScriptFailure(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.CheckScriptFailure(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.CheckScriptFailure(lines, 'E1012: Type mismatch; expected object<Background> but got object<Unknown>') + + # 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.CheckScriptFailure(lines, 'E1363:') +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.CheckScriptSuccess(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.CheckScriptSuccess(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.CheckScriptSuccess(lines) +enddef + +def Test_assignment_with_operator() + var lines =<< trim END + vim9script + + class Foo + 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) + END + v9.CheckScriptSuccess(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.CheckScriptSuccess(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.CheckScriptSuccess(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.CheckScriptSuccess(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.CheckScriptSuccess(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.CheckScriptFailure(lines, 'E119:') +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.CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + class TextPosition + this.lnum + this.col = 1 + endclass + END + v9.CheckScriptFailure(lines, 'E1022:') + + lines =<< trim END + vim9script + class TextPosition + this.lnum = v:none + this.col = 1 + endclass + END + v9.CheckScriptFailure(lines, 'E1330:') +enddef + +def Test_class_object_member_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', 'E1334') + END + v9.CheckScriptSuccess(lines) + + 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.CheckScriptSuccess(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.CheckScriptFailure(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.CheckScriptSuccess(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.CheckScriptSuccess(class_lines + test_lines) + v9.CheckScriptSuccess( + 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.CheckScriptFailure(class_lines + op_lines, 'E1153: Invalid operation for object') + v9.CheckScriptFailure(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.CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + + class One + this.one = 1 + endclass + class Two + this.two = 2 + endclass + + var o: One = Two.new() + END + v9.CheckScriptFailure(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.CheckScriptSuccess(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.CheckScriptSuccess(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', 'E1338:') + + 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.CheckScriptSuccess(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.CheckScriptSuccess(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.CheckScriptSuccess(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.CheckScriptFailure(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.CheckScriptFailure(lines, 'E1341: Variable already declared in the class: count') +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.CheckScriptSuccess(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.CheckScriptSuccess(lines) +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.CheckScriptFailure(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.CheckScriptFailure(lines, 'E1012: Type mismatch; expected number but got string') +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.CheckScriptSuccess(lines) +enddef + +def Test_interface_basics() + var lines =<< trim END + vim9script + interface Something + this.value: string + static count: number + def GetCount(): number + endinterface + END + v9.CheckScriptSuccess(lines) + + lines =<< trim END + interface SomethingWrong + static count = 7 + endinterface + END + v9.CheckScriptFailure(lines, 'E1342:') + + lines =<< trim END + vim9script + + interface Some + static count: number + def Method(count: number) + endinterface + END + v9.CheckScriptFailure(lines, 'E1340: Argument already declared in the class: count') + + lines =<< trim END + vim9script + + interface Some + this.value: number + def Method(value: number) + endinterface + END + v9.CheckScriptFailure(lines, 'E1340: Argument already declared in the class: value') + + lines =<< trim END + vim9script + interface somethingWrong + static count = 7 + endinterface + END + v9.CheckScriptFailure(lines, 'E1343: Interface name must start with an uppercase letter: somethingWrong') + + lines =<< trim END + vim9script + interface SomethingWrong + this.value: string + static count = 7 + def GetCount(): number + endinterface + END + v9.CheckScriptFailure(lines, 'E1344:') + + lines =<< trim END + vim9script + interface SomethingWrong + this.value: string + static count: number + def GetCount(): number + return 5 + enddef + endinterface + END + v9.CheckScriptFailure(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 + static count: number + def Method(nr: number) + endinterface + + class SomeImpl implements Some + static count: number + def Method(nr: number) + echo nr + enddef + endclass + + interface Another + this.member: string + endinterface + + class AnotherImpl implements Some, Another + this.member = 'abc' + static count: number + def Method(nr: number) + echo nr + enddef + endclass + END + v9.CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + + interface Some + static counter: number + endinterface + + class SomeImpl implements Some implements Some + static count: number + endclass + END + v9.CheckScriptFailure(lines, 'E1350:') + + lines =<< trim END + vim9script + + interface Some + static counter: number + endinterface + + class SomeImpl implements Some, Some + static count: number + endclass + END + v9.CheckScriptFailure(lines, 'E1351: Duplicate interface after "implements": Some') + + lines =<< trim END + vim9script + + interface Some + static counter: number + def Method(nr: number) + endinterface + + class SomeImpl implements Some + static count: number + def Method(nr: number) + echo nr + enddef + endclass + END + v9.CheckScriptFailure(lines, 'E1348: Member "counter" of interface "Some" not implemented') + + lines =<< trim END + vim9script + + interface Some + static count: number + def Methods(nr: number) + endinterface + + class SomeImpl implements Some + static count: number + def Method(nr: number) + echo nr + enddef + endclass + END + v9.CheckScriptFailure(lines, 'E1349: Function "Methods" of interface "Some" 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.CheckScriptSuccess(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.CheckScriptSuccess(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.CheckScriptSuccess(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.CheckScriptSuccess(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.CheckScriptSuccess(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.CheckScriptSuccess(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.CheckScriptSuccess(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.CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + + class Point + this.x = 0 + this.y = 0 + endclass + + var p: Point + p = 'text' + END + v9.CheckScriptFailure(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.CheckScriptSuccess(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.CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + class Base + this.one = 1 + endclass + class Child extends Base extends Base + this.two = 2 + endclass + END + v9.CheckScriptFailure(lines, 'E1352: Duplicate "extends"') + + lines =<< trim END + vim9script + class Child extends BaseClass + this.two = 2 + endclass + END + v9.CheckScriptFailure(lines, 'E1353: Class name not found: BaseClass') + + lines =<< trim END + vim9script + var SomeVar = 99 + class Child extends SomeVar + this.two = 2 + endclass + END + v9.CheckScriptFailure(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.CheckScriptSuccess(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.CheckScriptFailure(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.CheckScriptFailure(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.CheckScriptFailure(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.CheckScriptFailure(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.CheckScriptSuccess(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.CheckScriptFailure(lines, 'E1325: Method not found on class "Child": new(') + + # 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.CheckScriptSuccess(lines) +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.CheckScriptSuccess(lines) + unlet g:result +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.CheckScriptSuccess(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.CheckScriptFailure(lines, 'E1325: Method not found on class "Base": new(') + + lines =<< trim END + abstract class Base + this.name: string + endclass + END + v9.CheckScriptFailure(lines, 'E1316:') +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.CheckScriptSuccess(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.CheckScriptSuccess(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.CheckScriptSuccess(lines) + unlet g:result +enddef + + +" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker |