Commit: patch 9.1.2029: tests: the test_vim9_class.vim testfile is too long

2 views
Skip to first unread message

Christian Brabandt

unread,
Dec 28, 2025, 9:16:03 AM (yesterday) Dec 28
to vim...@googlegroups.com
patch 9.1.2029: tests: the test_vim9_class.vim testfile is too long

Commit: https://github.com/vim/vim/commit/1dd301a4b36bca40ef5044983382642f68f478bd
Author: Yegappan Lakshmanan <yega...@yahoo.com>
Date: Sun Dec 28 14:08:21 2025 +0000

patch 9.1.2029: tests: the test_vim9_class.vim testfile is too long

Problem: tests: the test_vim9_class.vim testfile is too long
Solution: Split out the interface related test cases into a new
test file test_vim9_interface.vim (Yegappan Lakshmanan)

closes: #19032

Signed-off-by: Yegappan Lakshmanan <yega...@yahoo.com>
Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak
index 9c0d11585..31f66d7ba 100644
--- a/src/testdir/Make_all.mak
+++ b/src/testdir/Make_all.mak
@@ -50,6 +50,7 @@ TEST_VIM9 = \
test_vim9_func \
test_vim9_generics \
test_vim9_import \
+ test_vim9_interface \
test_vim9_python3 \
test_vim9_script \
test_vim9_typealias
@@ -66,6 +67,7 @@ TEST_VIM9_RES = \
test_vim9_func.res \
test_vim9_generics.res \
test_vim9_import.res \
+ test_vim9_interface.res \
test_vim9_python3.res \
test_vim9_script.res \
test_vim9_typealias.res
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
index 69fad3381..7f0326e4a 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -568,24 +568,6 @@ def Test_using_null_class()
v9.CheckSourceSuccess(lines)
enddef

-def Test_class_interface_wrong_end()
- var lines =<< trim END
- vim9script
- abstract class SomeName
- var member = 'text'
- endinterface
- END
- v9.CheckSourceFailure(lines, 'E476: Invalid command: endinterface, expected endclass', 4)
-
- lines =<< trim END
- vim9script
- export interface AnotherName
- var member: string
- endclass
- END
- v9.CheckSourceFailure(lines, 'E476: Invalid command: endclass, expected endinterface', 4)
-enddef
-
def Test_object_not_set()
# Use an uninitialized object in script context
var lines =<< trim END
@@ -2103,51 +2085,6 @@ func Test_class_garbagecollect()
call v9.CheckSourceSuccess(lines)
endfunc

-" Test interface garbage collection
-func Test_interface_garbagecollect()
- let lines =<< trim END
- vim9script
-
- interface I
- var ro_obj_var: number
-
- def ObjFoo(): number
- endinterface
-
- class A implements I
- static var ro_class_var: number = 10
- public static var rw_class_var: number = 20
- static var _priv_class_var: number = 30
- var ro_obj_var: number = 40
- var _priv_obj_var: number = 60
-
- static def _ClassBar(): number
- return _priv_class_var
- enddef
-
- static def ClassFoo(): number
- return ro_class_var + rw_class_var + A._ClassBar()
- enddef
-
- def _ObjBar(): number
- return this._priv_obj_var
- enddef
-
- def ObjFoo(): number
- return this.ro_obj_var + this._ObjBar()
- enddef
- endclass
-
- assert_equal(60, A.ClassFoo())
- var o = A.new()
- assert_equal(100, o.ObjFoo())
- test_garbagecollect_now()
- assert_equal(60, A.ClassFoo())
- assert_equal(100, o.ObjFoo())
- END
- call v9.CheckSourceSuccess(lines)
-endfunc
-
def Test_class_method()
var lines =<< trim END
vim9script
@@ -2340,5327 +2277,3951 @@ def Test_class_object_to_string()
v9.CheckSourceSuccess(lines)
enddef

-def Test_interface_basics()
+def Test_class_used_as_type()
var lines =<< trim END
vim9script
- interface Something
- var ro_var: list<number>
- def GetCount(): number
- endinterface
- END
- v9.CheckSourceSuccess(lines)
-
- lines =<< trim END
- interface SomethingWrong
- static var count = 7
- endinterface
- END
- v9.CheckSourceFailure(lines, 'E1342: Interface can only be defined in Vim9 script', 1)

- lines =<< trim END
- vim9script
+ class Point
+ var x = 0
+ var y = 0
+ endclass

- interface Some
- var value: number
- def Method(value: number)
- endinterface
+ var p: Point
+ p = Point.new(2, 33)
+ assert_equal(2, p.x)
+ assert_equal(33, p.y)
END
- # The argument name and the object member name are the same, but this is not a
- # problem because object members are always accessed with the "this." prefix.
v9.CheckSourceSuccess(lines)

lines =<< trim END
vim9script
- interface somethingWrong
- static var count = 7
- endinterface
- END
- v9.CheckSourceFailure(lines, 'E1343: Interface name must start with an uppercase letter: somethingWrong', 2)
-
- lines =<< trim END
- vim9script
- interface SomethingWrong
- var value: string
- var count = 7
- def GetCount(): number
- endinterface
- END
- v9.CheckSourceFailure(lines, 'E1344: Cannot initialize a variable in an interface', 4)

- lines =<< trim END
- vim9script
- interface SomethingWrong
- var value: string
- var count: number
- def GetCount(): number
- return 5
- enddef
+ interface HasX
+ var x: number
endinterface
- END
- v9.CheckSourceFailure(lines, 'E1345: Not a valid command in an interface: return 5', 6)

- # Test for "interface" cannot be abbreviated
- lines =<< trim END
- vim9script
- inte Something
- endinterface
- END
- v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: inte Something', 2)
+ class Point implements HasX
+ var x = 0
+ var y = 0
+ endclass

- # Test for "endinterface" cannot be abbreviated
- lines =<< trim END
- vim9script
- interface Something
- endin
+ var p: Point
+ p = Point.new(2, 33)
+ var hx = p
+ assert_equal(2, hx.x)
END
- v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: endin', 3)
+ v9.CheckSourceSuccess(lines)

- # Additional commands after "interface name"
lines =<< trim END
vim9script
- interface Something | var x = 10 | var y = 20
- endinterface
- END
- v9.CheckSourceFailure(lines, "E488: Trailing characters: | var x = 10", 2)

- lines =<< trim END
- vim9script
- export interface EnterExit
- def Enter(): void
- def Exit(): void
- endinterface
- END
- writefile(lines, 'XdefIntf.vim', 'D')
+ class Point
+ var x = 0
+ var y = 0
+ endclass

- lines =<< trim END
- vim9script
- import './XdefIntf.vim' as defIntf
- export def With(ee: defIntf.EnterExit, F: func)
- ee.Enter()
- try
- F()
- finally
- ee.Exit()
- endtry
- enddef
+ var p: Point
+ p = 'text'
END
- v9.CheckScriptSuccess(lines)
+ v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected object<Point> but got string', 9)
+enddef

- var imported =<< trim END
+def Test_class_extends()
+ var lines =<< trim END
vim9script
- export abstract class EnterExit
- def Enter(): void
+ class Base
+ var one = 1
+ def GetOne(): number
+ return this.one
enddef
- def Exit(): void
+ endclass
+ class Child extends Base
+ var two = 2
+ def GetTotal(): number
+ return this.one + this.two
enddef
endclass
- END
- writefile(imported, 'XdefIntf2.vim', 'D')
-
- lines[1] = " import './XdefIntf2.vim' as defIntf"
- v9.CheckScriptSuccess(lines)
-enddef
-
-" Test for using string() with an interface
-def Test_interface_to_string()
- var lines =<< trim END
- vim9script
- interface Intf
- def Method(nr: number)
- endinterface
- assert_equal("interface Intf", string(Intf))
+ var o = Child.new()
+ assert_equal(1, o.one)
+ assert_equal(2, o.two)
+ assert_equal(1, o.GetOne())
+ assert_equal(3, o.GetTotal())
END
v9.CheckSourceSuccess(lines)
-enddef

-def Test_class_implements_interface()
- var lines =<< trim END
+ lines =<< trim END
vim9script
-
- interface Some
- var count: number
- def Method(nr: number)
- endinterface
-
- class SomeImpl implements Some
- var count: number
- def Method(nr: number)
- echo nr
- enddef
+ class Base
+ var one = 1
endclass
-
- interface Another
- var member: string
- endinterface
-
- class AnotherImpl implements Some, Another
- var member = 'abc'
- var count = 20
- def Method(nr: number)
- echo nr
- enddef
+ class Child extends Base
+ var two = 2
endclass
+ var o = Child.new(3, 44)
+ assert_equal(3, o.one)
+ assert_equal(44, o.two)
END
v9.CheckSourceSuccess(lines)

lines =<< trim END
vim9script
-
- interface Some
- var count: number
- endinterface
-
- class SomeImpl implements Some implements Some
- var count: number
+ class Base
+ var one = 1
+ endclass
+ class Child extends Base extends Base
+ var two = 2
endclass
END
- v9.CheckSourceFailure(lines, 'E1350: Duplicate "implements"', 7)
+ v9.CheckSourceFailure(lines, 'E1352: Duplicate "extends"', 5)

lines =<< trim END
vim9script
-
- interface Some
- var count: number
- endinterface
-
- class SomeImpl implements Some, Some
- var count: number
+ class Child extends BaseClass
+ var two = 2
endclass
END
- v9.CheckSourceFailure(lines, 'E1351: Duplicate interface after "implements": Some', 7)
+ v9.CheckSourceFailure(lines, 'E1353: Class name not found: BaseClass', 4)

lines =<< trim END
vim9script
-
- interface Some
- var counter: number
- def Method(nr: number)
- endinterface
-
- class SomeImpl implements Some
- var count: number
- def Method(nr: number)
- echo nr
- enddef
+ var SomeVar = 99
+ class Child extends SomeVar
+ var two = 2
endclass
END
- v9.CheckSourceFailure(lines, 'E1348: Variable "counter" of interface "Some" is not implemented', 13)
+ v9.CheckSourceFailure(lines, 'E1354: Cannot extend SomeVar', 5)

lines =<< trim END
vim9script
-
- interface Some
- var count: number
- def Methods(nr: number)
- endinterface
-
- class SomeImpl implements Some
- var count: number
- def Method(nr: number)
- echo nr
+ class Child
+ var age: number
+ def ToString(): number
+ return this.age
+ enddef
+ def ToString(): string
+ return this.age
enddef
endclass
END
- v9.CheckSourceFailure(lines, 'E1349: Method "Methods" of interface "Some" is not implemented', 13)
+ v9.CheckSourceFailure(lines, 'E1355: Duplicate function: ToString', 9)

- # Check different order of members in class and interface works.
lines =<< trim END
vim9script
+ class Base
+ var name: string
+ static def ToString(): string
+ return 'Base class'
+ enddef
+ endclass

- interface Result
- var label: string
- var errpos: number
- endinterface
-
- # order of members is opposite of interface
- class Failure implements Result
- public var lnum: number = 5
- var errpos: number = 42
- var label: string = 'label'
- endclass
-
- def Test()
- var result: Result = Failure.new()
-
- assert_equal('label', result.label)
- assert_equal(42, result.errpos)
+ class Child extends Base
+ var age: number
+ def ToString(): string
+ return Base.ToString() .. ': ' .. this.age
enddef
+ endclass

- Test()
+ var o = Child.new('John', 42)
+ assert_equal('Base class: 42', o.ToString())
END
v9.CheckSourceSuccess(lines)

- # Interface name after "extends" doesn't end in a space or NUL character
lines =<< trim END
vim9script
- interface A
- endinterface
- class B extends A"
+ class Base
+ var value = 1
+ def new(init: number)
+ this.value = number + 1
+ enddef
+ endclass
+ class Child extends Base
+ def new()
+ this.new(3)
+ enddef
endclass
+ var c = Child.new()
END
- v9.CheckSourceFailure(lines, 'E1315: White space required after name: A"', 4)
+ v9.CheckSourceFailure(lines, 'E1385: Class method "new" accessible only using class "Child"', 1)

- # Trailing characters after a class name
+ # base class with more than one object member
lines =<< trim END
vim9script
- class A bbb
+
+ class Result
+ var success: bool
+ var value: any = null
endclass
- END
- v9.CheckSourceFailure(lines, 'E488: Trailing characters: bbb', 2)

- # using "implements" with a non-existing class
- lines =<< trim END
- vim9script
- class A implements B
+ class Success extends Result
+ def new(this.value = v:none)
+ this.success = true
+ enddef
endclass
+
+ var v = Success.new('asdf')
+ assert_equal("object of Success {success: true, value: 'asdf'}", string(v))
END
- v9.CheckSourceFailure(lines, 'E1346: Interface name not found: B', 3)
+ v9.CheckSourceSuccess(lines)

- # using "implements" with a regular class
+ # class name after "extends" doesn't end in a space or NUL character
lines =<< trim END
vim9script
class A
endclass
- class B implements A
+ class B extends A"
endclass
END
- v9.CheckSourceFailure(lines, 'E1347: Not a valid interface: A', 5)
+ v9.CheckSourceFailure(lines, 'E1315: White space required after name: A"', 4)
+enddef

- # using "implements" with a variable
- lines =<< trim END
+def Test_using_base_class()
+ var lines =<< trim END
vim9script
- var T: number = 10
- class A implements T
- endclass
- END
- v9.CheckSourceFailure(lines, 'E1347: Not a valid interface: T', 4)

- # implements should be followed by a white space
- lines =<< trim END
- vim9script
- interface A
- endinterface
- class B implements A;
- endclass
- END
- v9.CheckSourceFailure(lines, 'E1315: White space required after name: A;', 4)
+ class BaseEE
+ def Enter(): any
+ return null
+ enddef
+ def Exit(resource: any): void
+ enddef
+ endclass

- lines =<< trim END
- vim9script
+ class ChildEE extends BaseEE
+ def Enter(): any
+ return 42
+ enddef

- interface One
- def IsEven(nr: number): bool
- endinterface
- class Two implements One
- def IsEven(nr: number): string
+ def Exit(resource: number): void
+ g:result ..= '/exit'
enddef
endclass
+
+ def With(ee: BaseEE)
+ var r = ee.Enter()
+ try
+ g:result ..= r
+ finally
+ g:result ..= '/finally'
+ ee.Exit(r)
+ endtry
+ enddef
+
+ g:result = ''
+ With(ChildEE.new())
+ assert_equal('42/finally/exit', g:result)
END
- v9.CheckSourceFailure(lines, 'E1383: Method "IsEven": type mismatch, expected func(number): bool but got func(number): string', 9)
+ v9.CheckSourceSuccess(lines)
+ unlet g:result
+enddef

- lines =<< trim END
+" Test for using a method from the super class
+def Test_super_dispatch()
+ # See #15448 and #15463
+ var lines =<< trim END
vim9script

- interface One
- def IsEven(nr: number): bool
- endinterface
- class Two implements One
- def IsEven(nr: bool): bool
+ class A
+ def String(): string
+ return 'A'
enddef
endclass
- END
- v9.CheckSourceFailure(lines, 'E1383: Method "IsEven": type mismatch, expected func(number): bool but got func(bool): bool', 9)

- lines =<< trim END
- vim9script
-
- interface One
- def IsEven(nr: number): bool
- endinterface
- class Two implements One
- def IsEven(nr: number, ...extra: list<number>): bool
+ class B extends A
+ def String(): string
+ return super.String()
enddef
endclass
+
+ class C extends B
+ endclass
+
+ assert_equal('A', C.new().String())
END
- v9.CheckSourceFailure(lines, 'E1383: Method "IsEven": type mismatch, expected func(number): bool but got func(number, ...list<number>): bool', 9)
+ v9.CheckSourceSuccess(lines)

- # access superclass interface members from subclass, mix variable order
lines =<< trim END
vim9script

- interface I1
- var mvar1: number
- var mvar2: number
- endinterface
-
- # NOTE: the order is swapped
- class A implements I1
- var mvar2: number
- var mvar1: number
- public static var svar2: number
- public static var svar1: number
- def new()
- svar1 = 11
- svar2 = 12
- this.mvar1 = 111
- this.mvar2 = 112
+ class A
+ def F(): string
+ return 'AA'
enddef
endclass

class B extends A
- def new()
- this.mvar1 = 121
- this.mvar2 = 122
+ def F(): string
+ return 'BB'
+ enddef
+ def S(): string
+ return super.F()
+ enddef
+ def S0(): string
+ return this.S()
enddef
endclass

class C extends B
- def new()
- this.mvar1 = 131
- this.mvar2 = 132
+ def F(): string
+ return 'CC'
+ enddef
+ def ToB(): string
+ return super.F()
enddef
endclass

- def F2(i: I1): list<number>
- return [ i.mvar1, i.mvar2 ]
- enddef
+ assert_equal('AA', B.new().S())
+ assert_equal('AA', C.new().S())
+ assert_equal('AA', B.new().S0())
+ assert_equal('AA', C.new().S0())

- var oa = A.new()
- var ob = B.new()
- var oc = C.new()
+ assert_equal('BB', C.new().ToB())

- assert_equal([111, 112], F2(oa))
- assert_equal([121, 122], F2(ob))
- assert_equal([131, 132], F2(oc))
+ assert_equal('CC', C.new().F())
+ assert_equal('BB', B.new().F())
+ assert_equal('AA', A.new().F())
END
v9.CheckSourceSuccess(lines)

- # Access superclass interface members from subclass, mix variable order.
- # Two interfaces, one on A, one on B; each has both kinds of variables
lines =<< trim END
vim9script

- interface I1
- var mvar1: number
- var mvar2: number
- endinterface
+ var call_chain: list<string>

- interface I2
- var mvar3: number
- var mvar4: number
- endinterface
+ abstract class A
+ abstract def _G(): string

- class A implements I1
- public static var svar1: number
- public static var svar2: number
- var mvar1: number
- var mvar2: number
- def new()
- svar1 = 11
- svar2 = 12
- this.mvar1 = 111
- this.mvar2 = 112
+ def F(): string
+ call_chain->add('A.F()')
+ return this._G()
+ enddef
+ def _H(): string
+ call_chain->add('A._H()')
+ return this.F()
enddef
endclass

- class B extends A implements I2
- static var svar3: number
- static var svar4: number
- var mvar3: number
- var mvar4: number
- def new()
- svar3 = 23
- svar4 = 24
- this.mvar1 = 121
- this.mvar2 = 122
- this.mvar3 = 123
- this.mvar4 = 124
+ class B extends A
+ def _G(): string
+ call_chain->add('B.G()')
+ return 'BBB'
+ enddef
+ def SF(): string
+ call_chain->add('B.SF()')
+ return super._H()
enddef
endclass

class C extends B
- public static var svar5: number
- def new()
- svar5 = 1001
- this.mvar1 = 131
- this.mvar2 = 132
- this.mvar3 = 133
- this.mvar4 = 134
+ endclass
+
+ class D extends C
+ def SF(): string
+ call_chain->add('D.SF()')
+ return super.SF()
enddef
endclass

- def F2(i: I1): list<number>
- return [ i.mvar1, i.mvar2 ]
- enddef
+ class E extends D
+ def SF(): string
+ call_chain->add('E.SF()')
+ return super.SF()
+ enddef
+ endclass

- def F4(i: I2): list<number>
- return [ i.mvar3, i.mvar4 ]
- enddef
+ class F extends E
+ def _G(): string
+ call_chain->add('F._G()')
+ return 'FFF'
+ enddef
+ endclass

- var oa = A.new()
- var ob = B.new()
- var oc = C.new()
+ # E.new() -> A.F() -> B._G()
+ call_chain = []
+ var o1 = E.new()
+ assert_equal('BBB', o1.F())
+ assert_equal(['A.F()', 'B.G()'], call_chain)

- assert_equal([[111, 112]], [F2(oa)])
- assert_equal([[121, 122], [123, 124]], [F2(ob), F4(ob)])
- assert_equal([[131, 132], [133, 134]], [F2(oc), F4(oc)])
+ # F.new() -> E.SF() -> D.SF() -> B.SF() -> A._H() -> A.F() -> F._G()
+ call_chain = []
+ var o2 = F.new()
+ assert_equal('FFF', o2.SF())
+ assert_equal(['E.SF()', 'D.SF()', 'B.SF()', 'A._H()', 'A.F()', 'F._G()'], call_chain)
END
v9.CheckSourceSuccess(lines)

- # Using two interface names without a space after the ","
+ # problems with method dispatch: super -> abstract
+ # https://github.com/vim/vim/issues/15514
lines =<< trim END
vim9script
- interface A
- endinterface
- interface B
- endinterface
- class C implements A,B
+ abstract class B
+ abstract def ToString(): string
endclass
- END
- v9.CheckSourceFailure(lines, 'E1315: White space required after name: A,B', 6)

- # No interface name after a comma
- lines =<< trim END
- vim9script
- interface A
- endinterface
- class B implements A,
+ class C extends B
+ def ToString(): string
+ return super.ToString()
+ enddef
endclass
+
+ try
+ defcompile C.ToString
+ call assert_false(1, 'command should have failed')
+ catch
+ call assert_exception('E1431: Abstract method "ToString" in class "B" cannot be accessed directly')
+ endtry
END
- v9.CheckSourceFailure(lines, 'E1389: Missing name after implements', 4)
+ v9.CheckSourceSuccess(lines)

- # No interface name after implements
+ # problems with method dispatch: super -> abstract -> concrete
lines =<< trim END
vim9script
- class A implements
+
+ class A
+ def ToString()
+ echo 'A'
+ enddef
endclass
- END
- v9.CheckSourceFailure(lines, 'E1389: Missing name after implements', 2)
-enddef

-def Test_call_interface_method()
- var lines =<< trim END
- vim9script
- interface Base
- def Enter(): void
- endinterface
+ abstract class B extends A
+ abstract def ToString()
+ endclass

- class Child implements Base
- def Enter(): void
- g:result ..= 'child'
+ class C extends B
+ def ToString()
+ super.ToString()
enddef
endclass

- def F(obj: Base)
- obj.Enter()
- enddef
-
- g:result = ''
- F(Child.new())
- assert_equal('child', g:result)
- unlet g:result
+ try
+ defcompile C.ToString
+ call assert_false(1, 'command should have failed')
+ catch
+ call assert_exception('E1431: Abstract method "ToString" in class "B" cannot be accessed directly')
+ endtry
END
v9.CheckSourceSuccess(lines)

+ # Invoking a super method and an interface method which have the same name.
lines =<< trim END
vim9script
- class Base
- def Enter(): void
- g:result ..= 'base'
+
+ interface I
+ def ToString(): string
+ endinterface
+
+ # Note that A does not implement I.
+ class A
+ def ToString(): string
+ return 'A'
enddef
endclass

- class Child extends Base
- def Enter(): void
- g:result ..= 'child'
+ class B extends A implements I
+ def ToString(): string
+ return super.ToString()
enddef
endclass

- def F(obj: Base)
- obj.Enter()
- enddef
+ def TestI(i: I): string
+ return i.ToString()
+ enddef

- g:result = ''
- F(Child.new())
- assert_equal('child', g:result)
- unlet g:result
+ assert_equal('A', B.new().ToString())
+ assert_equal('A', TestI(B.new()))
END
v9.CheckSourceSuccess(lines)

- # method of interface returns a value
+ # super and an abstract class with no abstract methods
lines =<< trim END
vim9script
- interface Base
- def Enter(): string
- endinterface

- class Child implements Base
- def Enter(): string
- g:result ..= 'child'
- return "/resource"
+ class A
+ def ToString(): string
+ return 'A'
enddef
endclass

- def F(obj: Base)
- var r = obj.Enter()
- g:result ..= r
- enddef
-
- g:result = ''
- F(Child.new())
- assert_equal('child/resource', g:result)
- unlet g:result
- END
- v9.CheckSourceSuccess(lines)
-
- lines =<< trim END
- vim9script
- class Base
- def Enter(): string
- return null_string
- enddef
+ # An abstract class with no abstract methods.
+ abstract class B extends A
endclass

- class Child extends Base
- def Enter(): string
- g:result ..= 'child'
- return "/resource"
+ class C extends B
+ def ToString(): string
+ return super.ToString()
enddef
endclass

- def F(obj: Base)
- var r = obj.Enter()
- g:result ..= r
+ def TestA(a: A): string
+ return a.ToString()
enddef

- g:result = ''
- F(Child.new())
- assert_equal('child/resource', g:result)
- unlet g:result
- END
- v9.CheckSourceSuccess(lines)
+ def TestB(b: B): string
+ return b.ToString()
+ enddef

- # No class that implements the interface.
- lines =<< trim END
+ assert_equal('A', C.new().ToString())
+ assert_equal('A', TestA(A.new()))
+ assert_equal('A', TestA(C.new()))
+ assert_equal('A', TestB(C.new()))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # super and an abstract class with no abstract methods and the initial
+ # implements clause
+ lines =<< trim END
vim9script

- interface IWithEE
- def Enter(): any
- def Exit(): void
+ interface I
+ def ToString(): string
endinterface

- def With1(ee: IWithEE, F: func)
- var r = ee.Enter()
+ # Note that A does not implement I.
+ class A
+ def ToString(): string
+ return 'A'
+ enddef
+ endclass
+
+ # An abstract class with no abstract methods.
+ abstract class B extends A implements I
+ endclass
+
+ class C extends B implements I
+ def ToString(): string
+ return super.ToString()
+ enddef
+ endclass
+
+ # Note that A.ToString() is different from I.ToString().
+ def TestA(a: A): string
+ return a.ToString()
enddef

- defcompile
+ assert_equal('A', C.new().ToString())
+ assert_equal('A', TestA(A.new()))
+ assert_equal('A', TestA(C.new()))
END
v9.CheckSourceSuccess(lines)
-enddef

-def Test_class_used_as_type()
- var lines =<< trim END
+ # Invoking a class method in the parent class using "super" should fail
+ lines =<< trim END
vim9script

- class Point
- var x = 0
- var y = 0
+ class A
+ static def Fn(): string
+ return 'A'
+ enddef
endclass

- var p: Point
- p = Point.new(2, 33)
- assert_equal(2, p.x)
- assert_equal(33, p.y)
+ class B extends A
+ static def Fn(): string
+ return super.Fn()
+ enddef
+ endclass
+ defcompile
END
- v9.CheckSourceSuccess(lines)
+ v9.CheckSourceFailure(lines, 'E1325: Method "Fn" not found in class "B"')

+ # Missing name after "super" keyword
lines =<< trim END
vim9script
-
- interface HasX
- var x: number
- endinterface
-
- class Point implements HasX
- var x = 0
- var y = 0
+ class A
endclass
+ class B extends A
+ def Fn()
+ var x = super.()
+ enddef
+ endclass
+ defcompile
+ END
+ v9.CheckSourceFailure(lines, 'E1127: Missing name after dot', 1)
+enddef

- var p: Point
- p = Point.new(2, 33)
- var hx = p
- assert_equal(2, hx.x)
+def Test_class_import()
+ var lines =<< trim END
+ vim9script
+ export class Animal
+ var kind: string
+ var name: string
+ endclass
END
- v9.CheckSourceSuccess(lines)
+ writefile(lines, 'Xanimal.vim', 'D')

lines =<< trim END
vim9script
+ import './Xanimal.vim' as animal

- class Point
- var x = 0
- var y = 0
- endclass
+ var a: animal.Animal
+ a = animal.Animal.new('fish', 'Eric')
+ assert_equal('fish', a.kind)
+ assert_equal('Eric', a.name)

- var p: Point
- p = 'text'
+ var b: animal.Animal = animal.Animal.new('cat', 'Garfield')
+ assert_equal('cat', b.kind)
+ assert_equal('Garfield', b.name)
END
- v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected object<Point> but got string', 9)
+ v9.CheckScriptSuccess(lines)
enddef

-def Test_class_extends()
+" Test for importing a class into a legacy script and calling the class method
+def Test_class_method_from_legacy_script()
var lines =<< trim END
vim9script
- class Base
- var one = 1
- def GetOne(): number
- return this.one
+ export class A
+ static var name: string = 'a'
+ static def SetName(n: string)
+ name = n
enddef
endclass
- class Child extends Base
- var two = 2
- def GetTotal(): number
- return this.one + this.two
+ END
+ writefile(lines, 'Xvim9export.vim', 'D')
+
+ lines =<< trim END
+ import './Xvim9export.vim' as vim9
+
+ call s:vim9.A.SetName('b')
+ call assert_equal('b', s:vim9.A.name)
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
+" Test for extending an imported class
+def Test_extend_imported_class()
+ var lines =<< trim END
+ vim9script
+ export class Imp_C1
+ def Fn1(): number
+ return 5
enddef
endclass
- var o = Child.new()
- assert_equal(1, o.one)
- assert_equal(2, o.two)
- assert_equal(1, o.GetOne())
- assert_equal(3, o.GetTotal())
END
- v9.CheckSourceSuccess(lines)
+ writefile(lines, 'Xextendimportclass.vim', 'D')

lines =<< trim END
vim9script
- class Base
- var one = 1
+ import './Xextendimportclass.vim' as XClass
+
+ class A extends XClass.Imp_C1
endclass
- class Child extends Base
- var two = 2
+ var a = A.new()
+ assert_equal(5, a.Fn1())
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
+def Test_abstract_class()
+ var lines =<< trim END
+ vim9script
+ abstract class Base
+ var name: string
endclass
- var o = Child.new(3, 44)
- assert_equal(3, o.one)
- assert_equal(44, o.two)
+ class Person extends Base
+ var age: number
+ endclass
+ var p: Base = Person.new('Peter', 42)
+ assert_equal('Peter', p.name)
+ assert_equal(42, p.age)
END
v9.CheckSourceSuccess(lines)

lines =<< trim END
vim9script
- class Base
- var one = 1
+ abstract class Base
+ var name: string
endclass
- class Child extends Base extends Base
- var two = 2
+ class Person extends Base
+ var age: number
endclass
+ var p = Base.new('Peter')
END
- v9.CheckSourceFailure(lines, 'E1352: Duplicate "extends"', 5)
+ v9.CheckSourceFailure(lines, 'E1325: Method "new" not found in class "Base"', 8)

lines =<< trim END
- vim9script
- class Child extends BaseClass
- var two = 2
+ abstract class Base
+ var name: string
endclass
END
- v9.CheckSourceFailure(lines, 'E1353: Class name not found: BaseClass', 4)
+ v9.CheckSourceFailure(lines, 'E1316: Class can only be defined in Vim9 script', 1)

+ # Test for "abstract" cannot be abbreviated
lines =<< trim END
vim9script
- var SomeVar = 99
- class Child extends SomeVar
- var two = 2
+ abs class A
endclass
END
- v9.CheckSourceFailure(lines, 'E1354: Cannot extend SomeVar', 5)
+ v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: abs class A', 2)

+ # Additional commands after "abstract class"
lines =<< trim END
vim9script
- class Child
- var age: number
- def ToString(): number
- return this.age
- enddef
- def ToString(): string
- return this.age
- enddef
+ abstract class Something | var x = []
endclass
END
- v9.CheckSourceFailure(lines, 'E1355: Duplicate function: ToString', 9)
+ v9.CheckSourceFailure(lines, "E488: Trailing characters: | var x = []", 2)

+ # Abstract class cannot have a "new" function
lines =<< trim END
vim9script
- class Base
- var name: string
- static def ToString(): string
- return 'Base class'
- enddef
- endclass
-
- class Child extends Base
- var age: number
- def ToString(): string
- return Base.ToString() .. ': ' .. this.age
+ abstract class Base
+ def new()
enddef
endclass
-
- var o = Child.new('John', 42)
- assert_equal('Base class: 42', o.ToString())
END
- v9.CheckSourceSuccess(lines)
+ v9.CheckSourceFailure(lines, 'E1359: Cannot define a "new" method in an abstract class', 4)

+ # extending an abstract class with class methods and variables
lines =<< trim END
vim9script
- class Base
- var value = 1
- def new(init: number)
- this.value = number + 1
+ abstract class A
+ static var s: string = 'vim'
+ static def Fn(): list<number>
+ return [10]
enddef
endclass
- class Child extends Base
- def new()
- this.new(3)
- enddef
+ class B extends A
endclass
- var c = Child.new()
+ var b = B.new()
+ assert_equal('vim', A.s)
+ assert_equal([10], A.Fn())
END
- v9.CheckSourceFailure(lines, 'E1385: Class method "new" accessible only using class "Child"', 1)
+ v9.CheckScriptSuccess(lines)
+enddef

- # base class with more than one object member
- lines =<< trim END
+def Test_closure_in_class()
+ var lines =<< trim END
vim9script

- class Result
- var success: bool
- var value: any = null
- endclass
+ class Foo
+ var y: list<string> = ['B']

- class Success extends Result
- def new(this.value = v:none)
- this.success = true
+ def new()
+ g:result = filter(['A', 'B'], (_, v) => index(this.y, v) == -1)
enddef
endclass

- var v = Success.new('asdf')
- assert_equal("object of Success {success: true, value: 'asdf'}", string(v))
+ Foo.new()
+ assert_equal(['A'], g:result)
END
v9.CheckSourceSuccess(lines)
-
- # class name after "extends" doesn't end in a space or NUL character
- lines =<< trim END
- vim9script
- class A
- endclass
- class B extends A"
- endclass
- END
- v9.CheckSourceFailure(lines, 'E1315: White space required after name: A"', 4)
enddef

-def Test_using_base_class()
+def Test_construct_object_from_legacy()
+ # Cannot directly invoke constructor from legacy
var lines =<< trim END
vim9script

- class BaseEE
- def Enter(): any
- return null
- enddef
- def Exit(resource: any): void
- enddef
- endclass
-
- class ChildEE extends BaseEE
- def Enter(): any
- return 42
- enddef
+ var newCalled = false

- def Exit(resource: number): void
- g:result ..= '/exit'
+ class A
+ def new(arg: string)
+ newCalled = true
enddef
endclass

- def With(ee: BaseEE)
- var r = ee.Enter()
- try
- g:result ..= r
- finally
- g:result ..= '/finally'
- ee.Exit(r)
- endtry
+ export def CreateA(...args: list<any>): A
+ return call(A.new, args)
enddef

- g:result = ''
- With(ChildEE.new())
- assert_equal('42/finally/exit', g:result)
+ g:P = CreateA
+ legacy call g:P('some_arg')
+ assert_equal(true, newCalled)
+ unlet g:P
END
v9.CheckSourceSuccess(lines)
- unlet g:result
-enddef

-" Test for using a method from the super class
-def Test_super_dispatch()
- # See #15448 and #15463
- var lines =<< trim END
+ lines =<< trim END
vim9script

+ var newCalled = false
+
class A
- def String(): string
- return 'A'
+ static def CreateA(options = {}): any
+ return A.new()
enddef
- endclass
-
- class B extends A
- def String(): string
- return super.String()
+ def new()
+ newCalled = true
enddef
endclass

- class C extends B
- endclass
-
- assert_equal('A', C.new().String())
+ g:P = A.CreateA
+ legacy call g:P()
+ assert_equal(true, newCalled)
+ unlet g:P
END
v9.CheckSourceSuccess(lines)

+ # This also tests invoking "new()" with "call"
lines =<< trim END
vim9script

+ var createdObject: any
+
class A
- def F(): string
- return 'AA'
+ var val1: number
+ var val2: number
+ static def CreateA(...args: list<any>): any
+ createdObject = call(A.new, args)
+ return createdObject
enddef
endclass

- class B extends A
- def F(): string
- return 'BB'
- enddef
- def S(): string
- return super.F()
- enddef
- def S0(): string
- return this.S()
- enddef
- endclass
+ g:P = A.CreateA
+ legacy call g:P(3, 5)
+ assert_equal(3, createdObject.val1)
+ assert_equal(5, createdObject.val2)
+ legacy call g:P()
+ assert_equal(0, createdObject.val1)
+ assert_equal(0, createdObject.val2)
+ legacy call g:P(7)
+ assert_equal(7, createdObject.val1)
+ assert_equal(0, createdObject.val2)
+ unlet g:P
+ END
+ v9.CheckSourceSuccess(lines)
+enddef

- class C extends B
- def F(): string
- return 'CC'
+def Test_defer_with_object()
+ var lines =<< trim END
+ vim9script
+
+ class CWithEE
+ def Enter()
+ g:result ..= "entered/"
enddef
- def ToB(): string
- return super.F()
+ def Exit()
+ g:result ..= "exited"
enddef
endclass

- assert_equal('AA', B.new().S())
- assert_equal('AA', C.new().S())
- assert_equal('AA', B.new().S0())
- assert_equal('AA', C.new().S0())
-
- assert_equal('BB', C.new().ToB())
+ def With(ee: CWithEE, F: func)
+ ee.Enter()
+ defer ee.Exit()
+ F()
+ enddef

- assert_equal('CC', C.new().F())
- assert_equal('BB', B.new().F())
- assert_equal('AA', A.new().F())
+ g:result = ''
+ var obj = CWithEE.new()
+ obj->With(() => {
+ g:result ..= "called/"
+ })
+ assert_equal('entered/called/exited', g:result)
END
v9.CheckSourceSuccess(lines)
+ unlet g:result

lines =<< trim END
vim9script

- var call_chain: list<string>
-
- abstract class A
- abstract def _G(): string
-
- def F(): string
- call_chain->add('A.F()')
- return this._G()
+ class BaseWithEE
+ def Enter()
+ g:result ..= "entered-base/"
enddef
- def _H(): string
- call_chain->add('A._H()')
- return this.F()
+ def Exit()
+ g:result ..= "exited-base"
enddef
endclass

- class B extends A
- def _G(): string
- call_chain->add('B.G()')
- return 'BBB'
+ class CWithEE extends BaseWithEE
+ def Enter()
+ g:result ..= "entered-child/"
enddef
- def SF(): string
- call_chain->add('B.SF()')
- return super._H()
+ def Exit()
+ g:result ..= "exited-child"
enddef
endclass

- class C extends B
- endclass
+ def With(ee: BaseWithEE, F: func)
+ ee.Enter()
+ defer ee.Exit()
+ F()
+ enddef

- class D extends C
- def SF(): string
- call_chain->add('D.SF()')
- return super.SF()
- enddef
+ g:result = ''
+ var obj = CWithEE.new()
+ obj->With(() => {
+ g:result ..= "called/"
+ })
+ assert_equal('entered-child/called/exited-child', g:result)
+ END
+ v9.CheckSourceSuccess(lines)
+ unlet g:result
+enddef
+
+" The following test used to crash Vim (Github issue #12676)
+def Test_extends_method_crashes_vim()
+ var lines =<< trim END
+ vim9script
+
+ class Observer
endclass

- class E extends D
- def SF(): string
- call_chain->add('E.SF()')
- return super.SF()
+ class Property
+ var value: any
+
+ def Set(v: any)
+ if v != this.value
+ this.value = v
+ endif
enddef
- endclass

- class F extends E
- def _G(): string
- call_chain->add('F._G()')
- return 'FFF'
+ def Register(observer: Observer)
enddef
endclass

- # E.new() -> A.F() -> B._G()
- call_chain = []
- var o1 = E.new()
- assert_equal('BBB', o1.F())
- assert_equal(['A.F()', 'B.G()'], call_chain)
+ class Bool extends Property
+ var value2: bool
+ endclass

- # F.new() -> E.SF() -> D.SF() -> B.SF() -> A._H() -> A.F() -> F._G()
- call_chain = []
- var o2 = F.new()
- assert_equal('FFF', o2.SF())
- assert_equal(['E.SF()', 'D.SF()', 'B.SF()', 'A._H()', 'A.F()', 'F._G()'], call_chain)
- END
- v9.CheckSourceSuccess(lines)
+ def Observe(obj: Property, who: Observer)
+ obj.Register(who)
+ enddef

- # problems with method dispatch: super -> abstract
- # https://github.com/vim/vim/issues/15514
- lines =<< trim END
- vim9script
- abstract class B
- abstract def ToString(): string
- endclass
+ var p = Bool.new(false)
+ var myObserver = Observer.new()

- class C extends B
- def ToString(): string
- return super.ToString()
- enddef
- endclass
+ Observe(p, myObserver)

- try
- defcompile C.ToString
- call assert_false(1, 'command should have failed')
- catch
- call assert_exception('E1431: Abstract method "ToString" in class "B" cannot be accessed directly')
- endtry
+ p.Set(true)
END
v9.CheckSourceSuccess(lines)
+enddef

- # problems with method dispatch: super -> abstract -> concrete
- lines =<< trim END
+" Test for calling a method in a class that is extended
+def Test_call_method_in_extended_class()
+ var lines =<< trim END
vim9script

- class A
- def ToString()
- echo 'A'
+ var prop_init_called = false
+ var prop_register_called = false
+
+ class Property
+ def Init()
+ prop_init_called = true
enddef
- endclass

- abstract class B extends A
- abstract def ToString()
+ def Register()
+ prop_register_called = true
+ enddef
endclass

- class C extends B
- def ToString()
- super.ToString()
- enddef
+ class Bool extends Property
endclass

- try
- defcompile C.ToString
- call assert_false(1, 'command should have failed')
- catch
- call assert_exception('E1431: Abstract method "ToString" in class "B" cannot be accessed directly')
- endtry
+ def Observe(obj: Property)
+ obj.Register()
+ enddef
+
+ var p = Property.new()
+ Observe(p)
+
+ p.Init()
+ assert_true(prop_init_called)
+ assert_true(prop_register_called)
END
v9.CheckSourceSuccess(lines)
+enddef

- # Invoking a super method and an interface method which have the same name.
- lines =<< trim END
+def Test_instanceof()
+ var lines =<< trim END
vim9script

- interface I
- def ToString(): string
+ class Base1
+ endclass
+
+ class Base2 extends Base1
+ endclass
+
+ interface Intf1
endinterface

- # Note that A does not implement I.
- class A
- def ToString(): string
- return 'A'
- enddef
+ class Mix1 implements Intf1
endclass

- class B extends A implements I
- def ToString(): string
- return super.ToString()
- enddef
+ class Base3 extends Mix1
endclass

- def TestI(i: I): string
- return i.ToString()
- enddef
+ type AliasBase1 = Base1
+ type AliasBase2 = Base2
+ type AliasIntf1 = Intf1
+ type AliasMix1 = Mix1

- assert_equal('A', B.new().ToString())
- assert_equal('A', TestI(B.new()))
- END
- v9.CheckSourceSuccess(lines)
+ var b1 = Base1.new()
+ var b2 = Base2.new()
+ var b3 = Base3.new()

- # super and an abstract class with no abstract methods
- lines =<< trim END
- vim9script
+ assert_true(instanceof(b1, Base1))
+ assert_true(instanceof(b2, Base1))
+ assert_false(instanceof(b1, Base2))
+ assert_true(instanceof(b3, Mix1))
+ assert_true(instanceof(b3, Base1, Base2, Intf1))

- class A
- def ToString(): string
- return 'A'
- enddef
- endclass
+ assert_true(instanceof(b1, AliasBase1))
+ assert_true(instanceof(b2, AliasBase1))
+ assert_false(instanceof(b1, AliasBase2))
+ assert_true(instanceof(b3, AliasMix1))
+ assert_true(instanceof(b3, AliasBase1, AliasBase2, AliasIntf1))

- # An abstract class with no abstract methods.
- abstract class B extends A
- endclass
+ def Foo()
+ var a1 = Base1.new()
+ var a2 = Base2.new()
+ var a3 = Base3.new()

- class C extends B
- def ToString(): string
- return super.ToString()
- enddef
- endclass
+ assert_true(instanceof(a1, Base1))
+ assert_true(instanceof(a2, Base1))
+ assert_false(instanceof(a1, Base2))
+ assert_true(instanceof(a3, Mix1))
+ assert_true(instanceof(a3, Base1, Base2, Intf1))

- def TestA(a: A): string
- return a.ToString()
+ assert_true(instanceof(a1, AliasBase1))
+ assert_true(instanceof(a2, AliasBase1))
+ assert_false(instanceof(a1, AliasBase2))
+ assert_true(instanceof(a3, AliasMix1))
+ assert_true(instanceof(a3, AliasBase1, AliasBase2, AliasIntf1))
enddef
+ Foo()

- def TestB(b: B): string
- return b.ToString()
- enddef
+ var o_null: Base1
+ assert_false(instanceof(o_null, Base1))

- assert_equal('A', C.new().ToString())
- assert_equal('A', TestA(A.new()))
- assert_equal('A', TestA(C.new()))
- assert_equal('A', TestB(C.new()))
END
v9.CheckSourceSuccess(lines)

- # super and an abstract class with no abstract methods and the initial
- # implements clause
lines =<< trim END
vim9script

- interface I
- def ToString(): string
- endinterface
-
- # Note that A does not implement I.
- class A
- def ToString(): string
- return 'A'
- enddef
+ class Base1
endclass
+ instanceof(Base1.new())
+ END
+ v9.CheckSourceFailure(lines, 'E119: Not enough arguments for function: instanceof')

- # An abstract class with no abstract methods.
- abstract class B extends A implements I
- endclass
+ lines =<< trim END
+ vim9script

- class C extends B implements I
- def ToString(): string
- return super.ToString()
- enddef
+ class Base1
endclass
-
- # Note that A.ToString() is different from I.ToString().
- def TestA(a: A): string
- return a.ToString()
+ def F()
+ instanceof(Base1.new())
enddef
-
- assert_equal('A', C.new().ToString())
- assert_equal('A', TestA(A.new()))
- assert_equal('A', TestA(C.new()))
+ F()
END
- v9.CheckSourceSuccess(lines)
+ v9.CheckSourceFailure(lines, 'E119: Not enough arguments for function: instanceof')

- # Invoking a class method in the parent class using "super" should fail
lines =<< trim END
vim9script

- class A
- static def Fn(): string
- return 'A'
- enddef
+ class Base1
endclass

- class B extends A
- static def Fn(): string
- return super.Fn()
- enddef
+ class Base2
endclass
- defcompile
+
+ var o = Base2.new()
+ instanceof(o, Base1, Base2, 3)
END
- v9.CheckSourceFailure(lines, 'E1325: Method "Fn" not found in class "B"')
+ v9.CheckSourceFailure(lines, 'E693: Class or class typealias required for argument 4', 10)

- # Missing name after "super" keyword
lines =<< trim END
vim9script
- class A
+
+ class Base1
endclass
- class B extends A
- def Fn()
- var x = super.()
- enddef
+
+ class Base2
endclass
- defcompile
+
+ def F()
+ var o = Base2.new()
+ instanceof(o, Base1, Base2, 3)
+ enddef
+ F()
END
- v9.CheckSourceFailure(lines, 'E1127: Missing name after dot', 1)
+ v9.CheckSourceFailure(lines, 'E693: Class or class typealias required for argument 4')
enddef

-def Test_class_import()
+" Test for calling a method in the parent class that is extended partially.
+" This used to fail with the 'E118: Too many arguments for function: Text' error
+" message (Github issue #12524).
+def Test_call_method_in_parent_class()
var lines =<< trim END
vim9script
- export class Animal
- var kind: string
- var name: string
- endclass
- END
- writefile(lines, 'Xanimal.vim', 'D')

- lines =<< trim END
- vim9script
- import './Xanimal.vim' as animal
+ class Widget
+ var _lnum: number = 1

- var a: animal.Animal
- a = animal.Animal.new('fish', 'Eric')
- assert_equal('fish', a.kind)
- assert_equal('Eric', a.name)
+ def SetY(lnum: number)
+ this._lnum = lnum
+ enddef

- var b: animal.Animal = animal.Animal.new('cat', 'Garfield')
- assert_equal('cat', b.kind)
- assert_equal('Garfield', b.name)
- END
- v9.CheckScriptSuccess(lines)
-enddef
+ def Text(): string
+ return ''
+ enddef
+ endclass

-" Test for importing a class into a legacy script and calling the class method
-def Test_class_method_from_legacy_script()
- var lines =<< trim END
- vim9script
- export class A
- static var name: string = 'a'
- static def SetName(n: string)
- name = n
+ class Foo extends Widget
+ def Text(): string
+ return '<Foo>'
enddef
endclass
- END
- writefile(lines, 'Xvim9export.vim', 'D')

- lines =<< trim END
- import './Xvim9export.vim' as vim9
+ def Stack(w1: Widget, w2: Widget): list<Widget>
+ w1.SetY(1)
+ w2.SetY(2)
+ return [w1, w2]
+ enddef

- call s:vim9.A.SetName('b')
- call assert_equal('b', s:vim9.A.name)
+ var foo1 = Foo.new()
+ var foo2 = Foo.new()
+ var l = Stack(foo1, foo2)
END
- v9.CheckScriptSuccess(lines)
+ v9.CheckSourceSuccess(lines)
enddef

-" Test for implementing an imported interface
-def Test_implement_imported_interface()
+" Test for calling methods from three levels of classes
+def Test_multi_level_method_call()
var lines =<< trim END
vim9script
- export interface Imp_Intf1
- def Fn1(): number
- endinterface
- export interface Imp_Intf2
- def Fn2(): number
- endinterface
- END
- writefile(lines, 'Ximportinterface.vim', 'D')

- lines =<< trim END
- vim9script
- import './Ximportinterface.vim' as Xintf
+ var A_func1: number = 0
+ var A_func2: number = 0
+ var A_func3: number = 0
+ var B_func2: number = 0
+ var B_func3: number = 0
+ var C_func3: number = 0

- class A implements Xintf.Imp_Intf1, Xintf.Imp_Intf2
- def Fn1(): number
- return 10
+ class A
+ def Func1()
+ A_func1 += 1
enddef
- def Fn2(): number
- return 20
+
+ def Func2()
+ A_func2 += 1
enddef
- endclass
- var a = A.new()
- assert_equal(10, a.Fn1())
- assert_equal(20, a.Fn2())
- END
- v9.CheckScriptSuccess(lines)
-enddef

-" Test for extending an imported class
-def Test_extend_imported_class()
- var lines =<< trim END
- vim9script
- export class Imp_C1
- def Fn1(): number
- return 5
+ def Func3()
+ A_func3 += 1
enddef
endclass
- END
- writefile(lines, 'Xextendimportclass.vim', 'D')

- lines =<< trim END
- vim9script
- import './Xextendimportclass.vim' as XClass
+ class B extends A
+ def Func2()
+ B_func2 += 1
+ enddef

- class A extends XClass.Imp_C1
+ def Func3()
+ B_func3 += 1
+ enddef
endclass
- var a = A.new()
- assert_equal(5, a.Fn1())
- END
- v9.CheckScriptSuccess(lines)
-enddef

-def Test_abstract_class()
- var lines =<< trim END
- vim9script
- abstract class Base
- var name: string
- endclass
- class Person extends Base
- var age: number
+ class C extends B
+ def Func3()
+ C_func3 += 1
+ enddef
endclass
- var p: Base = Person.new('Peter', 42)
- assert_equal('Peter', p.name)
- assert_equal(42, p.age)
- END
- v9.CheckSourceSuccess(lines)

- lines =<< trim END
- vim9script
- abstract class Base
- var name: string
- endclass
- class Person extends Base
- var age: number
- endclass
- var p = Base.new('Peter')
- END
- v9.CheckSourceFailure(lines, 'E1325: Method "new" not found in class "Base"', 8)
+ def A_CallFuncs(a: A)
+ a.Func1()
+ a.Func2()
+ a.Func3()
+ enddef

- lines =<< trim END
- abstract class Base
- var name: string
- endclass
- END
- v9.CheckSourceFailure(lines, 'E1316: Class can only be defined in Vim9 script', 1)
+ def B_CallFuncs(b: B)
+ b.Func1()
+ b.Func2()
+ b.Func3()
+ enddef

- # Test for "abstract" cannot be abbreviated
- lines =<< trim END
- vim9script
- abs class A
- endclass
- END
- v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: abs class A', 2)
+ def C_CallFuncs(c: C)
+ c.Func1()
+ c.Func2()
+ c.Func3()
+ enddef

- # Additional commands after "abstract class"
- lines =<< trim END
- vim9script
- abstract class Something | var x = []
- endclass
+ var cobj = C.new()
+ A_CallFuncs(cobj)
+ B_CallFuncs(cobj)
+ C_CallFuncs(cobj)
+ assert_equal(3, A_func1)
+ assert_equal(0, A_func2)
+ assert_equal(0, A_func3)
+ assert_equal(3, B_func2)
+ assert_equal(0, B_func3)
+ assert_equal(3, C_func3)
END
- v9.CheckSourceFailure(lines, "E488: Trailing characters: | var x = []", 2)
+ v9.CheckSourceSuccess(lines)
+enddef

- # Abstract class cannot have a "new" function
- lines =<< trim END
+" Test for using members from three levels of classes
+def Test_multi_level_member_access()
+ var lines =<< trim END
vim9script
- abstract class Base
- def new()
- enddef
- endclass
- END
- v9.CheckSourceFailure(lines, 'E1359: Cannot define a "new" method in an abstract class', 4)

- # extending an abstract class with class methods and variables
- lines =<< trim END
- vim9script
- abstract class A
- static var s: string = 'vim'
- static def Fn(): list<number>
- return [10]
- enddef
+ class A
+ public var val1: number = 0
endclass
+
class B extends A
+ public var val2: number = 0
endclass
- var b = B.new()
- assert_equal('vim', A.s)
- assert_equal([10], A.Fn())
- END
- v9.CheckScriptSuccess(lines)
-enddef

-def Test_closure_in_class()
- var lines =<< trim END
- vim9script
+ class C extends B
+ public var val3: number = 0
+ endclass

- class Foo
- var y: list<string> = ['B']
+ def A_members(a: A)
+ a.val1 += 1
+ enddef

- def new()
- g:result = filter(['A', 'B'], (_, v) => index(this.y, v) == -1)
- enddef
- endclass
+ def B_members(b: B)
+ b.val1 += 1
+ b.val2 += 1
+ enddef

- Foo.new()
- assert_equal(['A'], g:result)
+ def C_members(c: C)
+ c.val1 += 1
+ c.val2 += 1
+ c.val3 += 1
+ enddef
+
+ var cobj = C.new()
+ A_members(cobj)
+ B_members(cobj)
+ C_members(cobj)
+ assert_equal(3, cobj.val1)
+ assert_equal(2, cobj.val2)
+ assert_equal(1, cobj.val3)
END
v9.CheckSourceSuccess(lines)
enddef

-def Test_construct_object_from_legacy()
- # Cannot directly invoke constructor from legacy
+" Test expansion of <stack> with class methods.
+def Test_stack_expansion_with_methods()
var lines =<< trim END
vim9script

- var newCalled = false
-
- class A
- def new(arg: string)
- newCalled = true
+ class C
+ def M1()
+ F0()
enddef
endclass

- export def CreateA(...args: list<any>): A
- return call(A.new, args)
+ def F0()
+ assert_match('<SNR>\d\+_F\[1\]\.\.<SNR>\d\+_C\.M1\[1\]\.\.<SNR>\d\+_F0\[1\]$', expand('<stack>'))
enddef

- g:P = CreateA
- legacy call g:P('some_arg')
- assert_equal(true, newCalled)
- unlet g:P
+ def F()
+ C.new().M1()
+ enddef
+
+ F()
END
v9.CheckSourceSuccess(lines)
+enddef

- lines =<< trim END
+" Test the return type of the new() constructor
+def Test_new_return_type()
+ # new() uses the default return type and there is no return statement
+ var lines =<< trim END
vim9script

- var newCalled = false
+ class C
+ var _bufnr: number

- class A
- static def CreateA(options = {}): any
- return A.new()
- enddef
- def new()
- newCalled = true
+ def new(this._bufnr)
+ if !bufexists(this._bufnr)
+ this._bufnr = -1
+ endif
enddef
endclass

- g:P = A.CreateA
- legacy call g:P()
- assert_equal(true, newCalled)
- unlet g:P
+ var c = C.new(12345)
+ assert_equal('object<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)

- # This also tests invoking "new()" with "call"
+ # new() uses the default return type and an empty 'return' statement
lines =<< trim END
vim9script

- var createdObject: any
+ class C
+ var _bufnr: number

- class A
- var val1: number
- var val2: number
- static def CreateA(...args: list<any>): any
- createdObject = call(A.new, args)
- return createdObject
+ def new(this._bufnr)
+ if !bufexists(this._bufnr)
+ this._bufnr = -1
+ return
+ endif
enddef
endclass

- g:P = A.CreateA
- legacy call g:P(3, 5)
- assert_equal(3, createdObject.val1)
- assert_equal(5, createdObject.val2)
- legacy call g:P()
- assert_equal(0, createdObject.val1)
- assert_equal(0, createdObject.val2)
- legacy call g:P(7)
- assert_equal(7, createdObject.val1)
- assert_equal(0, createdObject.val2)
- unlet g:P
+ var c = C.new(12345)
+ assert_equal('object<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)
-enddef

-def Test_defer_with_object()
- var lines =<< trim END
+ # new() uses "any" return type and returns "this"
+ lines =<< trim END
vim9script

- class CWithEE
- def Enter()
- g:result ..= "entered/"
- enddef
- def Exit()
- g:result ..= "exited"
+ class C
+ var _bufnr: number
+
+ def new(this._bufnr): any
+ if !bufexists(this._bufnr)
+ this._bufnr = -1
+ return this
+ endif
enddef
endclass
-
- def With(ee: CWithEE, F: func)
- ee.Enter()
- defer ee.Exit()
- F()
- enddef
-
- g:result = ''
- var obj = CWithEE.new()
- obj->With(() => {
- g:result ..= "called/"
- })
- assert_equal('entered/called/exited', g:result)
END
- v9.CheckSourceSuccess(lines)
- unlet g:result
+ v9.CheckSourceFailure(lines, 'E1365: Cannot use a return type with the "new" method', 11)

+ # new() uses 'Dict' return type and returns a Dict
lines =<< trim END
vim9script

- class BaseWithEE
- def Enter()
- g:result ..= "entered-base/"
- enddef
- def Exit()
- g:result ..= "exited-base"
- enddef
- endclass
+ class C
+ var _state: dict<any>

- class CWithEE extends BaseWithEE
- def Enter()
- g:result ..= "entered-child/"
- enddef
- def Exit()
- g:result ..= "exited-child"
+ def new(): dict<any>
+ this._state = {}
+ return this._state
enddef
endclass

- def With(ee: BaseWithEE, F: func)
- ee.Enter()
- defer ee.Exit()
- F()
- enddef
-
- g:result = ''
- var obj = CWithEE.new()
- obj->With(() => {
- g:result ..= "called/"
- })
- assert_equal('entered-child/called/exited-child', g:result)
+ var c = C.new()
+ assert_equal('object<C>', typename(c))
END
- v9.CheckSourceSuccess(lines)
- unlet g:result
+ v9.CheckSourceFailure(lines, 'E1365: Cannot use a return type with the "new" method', 9)
enddef

-" The following test used to crash Vim (Github issue #12676)
-def Test_extends_method_crashes_vim()
+" Test for checking a member initialization type at run time.
+def Test_runtime_type_check_for_member_init()
var lines =<< trim END
vim9script

- class Observer
- endclass
-
- class Property
- var value: any
-
- def Set(v: any)
- if v != this.value
- this.value = v
- endif
- enddef
-
- def Register(observer: Observer)
- enddef
- endclass
-
- class Bool extends Property
- var value2: bool
- endclass
+ var retnum: bool = false

- def Observe(obj: Property, who: Observer)
- obj.Register(who)
+ def F(): any
+ retnum = !retnum
+ if retnum
+ return 1
+ else
+ return "hello"
+ endif
enddef

- var p = Bool.new(false)
- var myObserver = Observer.new()
-
- Observe(p, myObserver)
+ class C
+ var _foo: bool = F()
+ endclass

- p.Set(true)
+ var c1 = C.new()
+ var c2 = C.new()
END
- v9.CheckSourceSuccess(lines)
+ v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected bool but got string', 0)
enddef

-" Test for calling a method in a class that is extended
-def Test_call_method_in_extended_class()
+" Test for locking a variable referring to an object and reassigning to another
+" object.
+def Test_lockvar_object()
var lines =<< trim END
vim9script

- var prop_init_called = false
- var prop_register_called = false
-
- class Property
- def Init()
- prop_init_called = true
- enddef
-
- def Register()
- prop_register_called = true
+ class C
+ var val: number
+ def new(this.val)
enddef
endclass

- class Bool extends Property
- endclass
+ var some_dict: dict<C> = { a: C.new(1), b: C.new(2), c: C.new(3), }
+ lockvar 2 some_dict

- def Observe(obj: Property)
- obj.Register()
+ var current: C
+ current = some_dict['c']
+ assert_equal(3, current.val)
+ current = some_dict['b']
+ assert_equal(2, current.val)
+
+ def F()
+ current = some_dict['c']
enddef

- var p = Property.new()
- Observe(p)
+ def G()
+ current = some_dict['b']
+ enddef

- p.Init()
- assert_true(prop_init_called)
- assert_true(prop_register_called)
+ F()
+ assert_equal(3, current.val)
+ G()
+ assert_equal(2, current.val)
END
v9.CheckSourceSuccess(lines)
enddef

-def Test_instanceof()
+" Test trying to lock an object variable from various places
+def Test_lockvar_object_variable()
+ # An object variable lockvar has several cases:
+ # object method, scriptlevel, scriplevel from :def, :def arg
+ # method arg, static method arg.
+ # Also different depths
+
+ #
+ # lockvar of read-only object variable
+ #
+
+ # read-only lockvar from object method
var lines =<< trim END
vim9script

- class Base1
+ class C
+ var val1: number
+ def Lock()
+ lockvar this.val1
+ enddef
endclass
+ var o = C.new(3)
+ o.Lock()
+ END
+ v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "this.val1" in class "C"')

- class Base2 extends Base1
- endclass
-
- interface Intf1
- endinterface
-
- class Mix1 implements Intf1
- endclass
+ # read-only lockvar from scriptlevel
+ lines =<< trim END
+ vim9script

- class Base3 extends Mix1
+ class C
+ var val2: number
endclass
+ var o = C.new(3)
+ lockvar o.val2
+ END
+ v9.CheckSourceFailure(lines, 'E1335: Variable "val2" in class "C" is not writable')

- type AliasBase1 = Base1
- type AliasBase2 = Base2
- type AliasIntf1 = Intf1
- type AliasMix1 = Mix1
-
- var b1 = Base1.new()
- var b2 = Base2.new()
- var b3 = Base3.new()
-
- assert_true(instanceof(b1, Base1))
- assert_true(instanceof(b2, Base1))
- assert_false(instanceof(b1, Base2))
- assert_true(instanceof(b3, Mix1))
- assert_true(instanceof(b3, Base1, Base2, Intf1))
-
- assert_true(instanceof(b1, AliasBase1))
- assert_true(instanceof(b2, AliasBase1))
- assert_false(instanceof(b1, AliasBase2))
- assert_true(instanceof(b3, AliasMix1))
- assert_true(instanceof(b3, AliasBase1, AliasBase2, AliasIntf1))
-
- def Foo()
- var a1 = Base1.new()
- var a2 = Base2.new()
- var a3 = Base3.new()
-
- assert_true(instanceof(a1, Base1))
- assert_true(instanceof(a2, Base1))
- assert_false(instanceof(a1, Base2))
- assert_true(instanceof(a3, Mix1))
- assert_true(instanceof(a3, Base1, Base2, Intf1))
+ # read-only lockvar of scriptlevel variable from def
+ lines =<< trim END
+ vim9script

- assert_true(instanceof(a1, AliasBase1))
- assert_true(instanceof(a2, AliasBase1))
- assert_false(instanceof(a1, AliasBase2))
- assert_true(instanceof(a3, AliasMix1))
- assert_true(instanceof(a3, AliasBase1, AliasBase2, AliasIntf1))
+ class C
+ var val3: number
+ endclass
+ var o = C.new(3)
+ def Lock()
+ lockvar o.val3
enddef
- Foo()
-
- var o_null: Base1
- assert_false(instanceof(o_null, Base1))
-
+ Lock()
END
- v9.CheckSourceSuccess(lines)
+ v9.CheckSourceFailure(lines, 'E1335: Variable "val3" in class "C" is not writable')

+ # read-only lockvar of def argument variable
lines =<< trim END
vim9script

- class Base1
+ class C
+ var val4: number
endclass
- instanceof(Base1.new())
+ def Lock(o: C)
+ lockvar o.val4
+ enddef
+ Lock(C.new(3))
END
- v9.CheckSourceFailure(lines, 'E119: Not enough arguments for function: instanceof')
+ v9.CheckSourceFailure(lines, 'E1335: Variable "val4" in class "C" is not writable')

+ # read-only lockvar from object method arg
lines =<< trim END
vim9script

- class Base1
+ class C
+ var val5: number
+ def Lock(c: C)
+ lockvar c.val5
+ enddef
endclass
- def F()
- instanceof(Base1.new())
- enddef
- F()
+ var o = C.new(3)
+ o.Lock(C.new(5))
END
- v9.CheckSourceFailure(lines, 'E119: Not enough arguments for function: instanceof')
+ v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val5" in class "C"')

+ # read-only lockvar from class method arg
lines =<< trim END
vim9script

- class Base1
+ class C
+ var val6: number
+ static def Lock(c: C)
+ lockvar c.val6
+ enddef
endclass
+ var o = C.new(3)
+ C.Lock(o)
+ END
+ v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val6" in class "C"')

- class Base2
- endclass
+ #
+ # lockvar of public object variable
+ #

- var o = Base2.new()
- instanceof(o, Base1, Base2, 3)
+ # lockvar from object method
+ lines =<< trim END
+ vim9script
+
+ class C
+ public var val1: number
+ def Lock()
+ lockvar this.val1
+ enddef
+ endclass
+ var o = C.new(3)
+ o.Lock()
END
- v9.CheckSourceFailure(lines, 'E693: Class or class typealias required for argument 4', 10)
+ v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "this.val1" in class "C"', 1)

+ # lockvar from scriptlevel
lines =<< trim END
vim9script

- class Base1
+ class C
+ public var val2: number
endclass
+ var o = C.new(3)
+ lockvar o.val2
+ END
+ v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o.val2" in class "C"', 7)

- class Base2
- endclass
+ # lockvar of scriptlevel variable from def
+ lines =<< trim END
+ vim9script

- def F()
- var o = Base2.new()
- instanceof(o, Base1, Base2, 3)
+ class C
+ public var val3: number
+ endclass
+ var o = C.new(3)
+ def Lock()
+ lockvar o.val3
enddef
- F()
+ Lock()
END
- v9.CheckSourceFailure(lines, 'E693: Class or class typealias required for argument 4')
-enddef
+ v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o.val3" in class "C"', 1)

-" Test for calling a method in the parent class that is extended partially.
-" This used to fail with the 'E118: Too many arguments for function: Text' error
-" message (Github issue #12524).
-def Test_call_method_in_parent_class()
- var lines =<< trim END
+ # lockvar of def argument variable
+ lines =<< trim END
vim9script

- class Widget
- var _lnum: number = 1
+ class C
+ public var val4: number
+ endclass
+ def Lock(o: C)
+ lockvar o.val4
+ enddef
+ Lock(C.new(3))
+ END
+ v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o.val4" in class "C"', 1)

- def SetY(lnum: number)
- this._lnum = lnum
- enddef
+ # lockvar from object method arg
+ lines =<< trim END
+ vim9script

- def Text(): string
- return ''
+ class C
+ public var val5: number
+ def Lock(c: C)
+ lockvar c.val5
enddef
endclass
+ var o = C.new(3)
+ o.Lock(C.new(5))
+ END
+ v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val5" in class "C"', 1)

- class Foo extends Widget
- def Text(): string
- return '<Foo>'
+ # lockvar from class method arg
+ lines =<< trim END
+ vim9script
+
+ class C
+ public var val6: number
+ static def Lock(c: C)
+ lockvar c.val6
enddef
endclass
-
- def Stack(w1: Widget, w2: Widget): list<Widget>
- w1.SetY(1)
- w2.SetY(2)
- return [w1, w2]
- enddef
-
- var foo1 = Foo.new()
- var foo2 = Foo.new()
- var l = Stack(foo1, foo2)
+ var o = C.new(3)
+ C.Lock(o)
END
- v9.CheckSourceSuccess(lines)
+ v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val6" in class "C"', 1)
enddef

-" Test for calling methods from three levels of classes
-def Test_multi_level_method_call()
+" Test trying to lock a class variable from various places
+def Test_lockvar_class_variable()
+
+ # lockvar bare static from object method
var lines =<< trim END
vim9script

- var A_func1: number = 0
- var A_func2: number = 0
- var A_func3: number = 0
- var B_func2: number = 0
- var B_func3: number = 0
- var C_func3: number = 0
-
- class A
- def Func1()
- A_func1 += 1
+ class C
+ public static var sval1: number
+ def Lock()
+ lockvar sval1
enddef
+ endclass
+ var o = C.new()
+ o.Lock()
+ END
+ v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval1" in class "C"', 1)

- def Func2()
- A_func2 += 1
- enddef
+ # lockvar C.static from object method
+ lines =<< trim END
+ vim9script

- def Func3()
- A_func3 += 1
+ class C
+ public static var sval2: number
+ def Lock()
+ lockvar C.sval2
enddef
endclass
+ var o = C.new()
+ o.Lock()
+ END
+ v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval2" in class "C"', 1)

- class B extends A
- def Func2()
- B_func2 += 1
- enddef
+ # lockvar bare static from class method
+ lines =<< trim END
+ vim9script

- def Func3()
- B_func3 += 1
+ class C
+ public static var sval3: number
+ static def Lock()
+ lockvar sval3
enddef
endclass
+ C.Lock()
+ END
+ v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval3" in class "C"', 1)

- class C extends B
- def Func3()
- C_func3 += 1
+ # lockvar C.static from class method
+ lines =<< trim END
+ vim9script
+
+ class C
+ public static var sval4: number
+ static def Lock()
+ lockvar C.sval4
enddef
endclass
+ C.Lock()
+ END
+ v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval4" in class "C"', 1)

- def A_CallFuncs(a: A)
- a.Func1()
- a.Func2()
- a.Func3()
- enddef
+ # lockvar C.static from script level
+ lines =<< trim END
+ vim9script

- def B_CallFuncs(b: B)
- b.Func1()
- b.Func2()
- b.Func3()
- enddef
+ class C
+ public static var sval5: number
+ endclass
+ lockvar C.sval5
+ END
+ v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval5" in class "C"', 6)

- def C_CallFuncs(c: C)
- c.Func1()
- c.Func2()
- c.Func3()
- enddef
+ # lockvar o.static from script level
+ lines =<< trim END
+ vim9script

- var cobj = C.new()
- A_CallFuncs(cobj)
- B_CallFuncs(cobj)
- C_CallFuncs(cobj)
- assert_equal(3, A_func1)
- assert_equal(0, A_func2)
- assert_equal(0, A_func3)
- assert_equal(3, B_func2)
- assert_equal(0, B_func3)
- assert_equal(3, C_func3)
+ class C
+ public static var sval6: number
+ endclass
+ var o = C.new()
+ lockvar o.sval6
END
- v9.CheckSourceSuccess(lines)
+ v9.CheckSourceFailure(lines, 'E1375: Class variable "sval6" accessible only using class "C"', 7)
enddef

-" Test for using members from three levels of classes
-def Test_multi_level_member_access()
+" Test locking an argument to :def
+def Test_lockvar_argument()
+ # Lockvar a function arg
var lines =<< trim END
vim9script

- class A
- public var val1: number = 0
- endclass
+ def Lock(val: any)
+ lockvar val
+ enddef

- class B extends A
- public var val2: number = 0
- endclass
+ var d = {a: 1, b: 2}
+ Lock(d)

- class C extends B
- public var val3: number = 0
- endclass
+ d->extend({c: 3})
+ END
+ v9.CheckSourceFailure(lines, 'E741: Value is locked: extend() argument')

- def A_members(a: A)
- a.val1 += 1
- enddef
+ # Lockvar a function arg. Verify "sval" is interpreted as argument and not a
+ # class member in "C". This tests lval_root_is_arg.
+ lines =<< trim END
+ vim9script

- def B_members(b: B)
- b.val1 += 1
- b.val2 += 1
- enddef
+ class C
+ public static var sval: list<number>
+ endclass

- def C_members(c: C)
- c.val1 += 1
- c.val2 += 1
- c.val3 += 1
+ def Lock2(sval: any)
+ lockvar sval
enddef

- var cobj = C.new()
- A_members(cobj)
- B_members(cobj)
- C_members(cobj)
- assert_equal(3, cobj.val1)
- assert_equal(2, cobj.val2)
- assert_equal(1, cobj.val3)
+ var o = C.new()
+ Lock2(o)
END
v9.CheckSourceSuccess(lines)
-enddef

-" Test expansion of <stack> with class methods.
-def Test_stack_expansion_with_methods()
- var lines =<< trim END
+ # Lock a class.
+ lines =<< trim END
vim9script

class C
- def M1()
- F0()
- enddef
+ public static var sval: list<number>
endclass

- def F0()
- assert_match('<SNR>\d\+_F\[1\]\.\.<SNR>\d\+_C\.M1\[1\]\.\.<SNR>\d\+_F0\[1\]$', expand('<stack>'))
- enddef
-
- def F()
- C.new().M1()
+ def Lock2(sval: any)
+ lockvar sval
enddef

- F()
+ Lock2(C)
END
- v9.CheckSourceSuccess(lines)
-enddef
+ v9.CheckSourceFailure(lines, 'E1405: Class "C" cannot be used as a value')

-" Test the return type of the new() constructor
-def Test_new_return_type()
- # new() uses the default return type and there is no return statement
- var lines =<< trim END
+ # Lock an object.
+ lines =<< trim END
vim9script

class C
- var _bufnr: number
-
- def new(this._bufnr)
- if !bufexists(this._bufnr)
- this._bufnr = -1
- endif
- enddef
+ public static var sval: list<number>
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))
+ def Lock2(sval: any)
+ lockvar sval
enddef
- F()
+
+ Lock2(C.new())
END
v9.CheckSourceSuccess(lines)

- # new() uses the default return type and an empty 'return' statement
+ # In this case (unlike previous) "lockvar sval" is a class member.
lines =<< trim END
vim9script

class C
- var _bufnr: number
-
- def new(this._bufnr)
- if !bufexists(this._bufnr)
- this._bufnr = -1
- return
- endif
+ public static var sval: list<number>
+ def Lock2()
+ lockvar sval
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))
+ var o = C.new()
+ o.Lock2()
+ END
+ v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval" in class "C"', 1)
+enddef

- def F()
- var v2: C
- v2 = C.new(12345)
- assert_equal('object<C>', typename(v2))
- enddef
- F()
+" Test that this can be locked without error
+def Test_lockvar_this()
+ # lockvar this
+ var lines =<< trim END
+ vim9script
+ class C
+ def TLock()
+ lockvar this
+ enddef
+ endclass
+ var o = C.new()
+ o.TLock()
END
v9.CheckSourceSuccess(lines)

- # new() uses "any" return type and returns "this"
+ # lockvar four (four letter word, but not this)
lines =<< trim END
vim9script
-
class C
- var _bufnr: number
-
- def new(this._bufnr): any
- if !bufexists(this._bufnr)
- this._bufnr = -1
- return this
- endif
+ def TLock4()
+ var four: number
+ lockvar four
enddef
endclass
+ var o = C.new()
+ o.TLock4()
END
- v9.CheckSourceFailure(lines, 'E1365: Cannot use a return type with the "new" method', 11)
+ v9.CheckSourceFailure(lines, 'E1178: Cannot lock or unlock a local variable')

- # new() uses 'Dict' return type and returns a Dict
+ # lockvar this5; "this" + one char, 5 letter word, starting with "this"
lines =<< trim END
vim9script
-
class C
- var _state: dict<any>
-
- def new(): dict<any>
- this._state = {}
- return this._state
+ def TLock5()
+ var this5: number
+ lockvar this5
enddef
endclass
-
- var c = C.new()
- assert_equal('object<C>', typename(c))
+ var o = C.new()
+ o.TLock5()
END
- v9.CheckSourceFailure(lines, 'E1365: Cannot use a return type with the "new" method', 9)
+ v9.CheckSourceFailure(lines, 'E1178: Cannot lock or unlock a local variable')
enddef

-" Test for checking a member initialization type at run time.
-def Test_runtime_type_check_for_member_init()
+" Test some general lockvar cases
+def Test_lockvar_general()
+ # lockvar an object and a class. It does nothing
var lines =<< trim END
vim9script
-
- var retnum: bool = false
-
- def F(): any
- retnum = !retnum
- if retnum
- return 1
- else
- return "hello"
- endif
- enddef
-
class C
- var _foo: bool = F()
endclass
-
- var c1 = C.new()
- var c2 = C.new()
+ var o = C.new()
+ lockvar o
+ lockvar C
END
- v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected bool but got string', 0)
-enddef
+ v9.CheckSourceSuccess(lines)

-" Test for locking a variable referring to an object and reassigning to another
-" object.
-def Test_lockvar_object()
- var lines =<< trim END
+ # Lock a list element that's nested in an object variable from a :def
+ lines =<< trim END
vim9script

class C
- var val: number
- def new(this.val)
- enddef
+ public var val: list<list<number>> = [ [1], [2], [3] ]
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']
+ def Lock2(obj: any)
+ lockvar obj.val[1]
enddef

- F()
- assert_equal(3, current.val)
- G()
- assert_equal(2, current.val)
+ var o = C.new()
+ Lock2(o)
+ o.val[0] = [9]
+ assert_equal([ [9], [2], [3] ], o.val)
+ try
+ o.val[1] = [999]
+ call assert_false(true, 'assign should have failed')
+ catch
+ assert_exception('E741:')
+ endtry
+ o.val[2] = [8]
+ assert_equal([ [9], [2], [8] ], o.val)
END
v9.CheckSourceSuccess(lines)
-enddef
-
-" Test trying to lock an object variable from various places
-def Test_lockvar_object_variable()
- # An object variable lockvar has several cases:
- # object method, scriptlevel, scriplevel from :def, :def arg
- # method arg, static method arg.
- # Also different depths
-
- #
- # lockvar of read-only object variable
- #
-
- # read-only lockvar from object method
- var lines =<< trim END
- vim9script
-
- class C
- var val1: number
- def Lock()
- lockvar this.val1
- enddef
- endclass
- var o = C.new(3)
- o.Lock()
- END
- v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "this.val1" in class "C"')

- # read-only lockvar from scriptlevel
+ # Lock a list element that's nested in an object variable from scriptlevel
lines =<< trim END
vim9script

class C
- var val2: number
+ public var val: list<list<number>> = [ [1], [2], [3] ]
endclass
- var o = C.new(3)
- lockvar o.val2
- END
- v9.CheckSourceFailure(lines, 'E1335: Variable "val2" in class "C" is not writable')
-
- # read-only lockvar of scriptlevel variable from def
- lines =<< trim END
- vim9script

- class C
- var val3: number
- endclass
- var o = C.new(3)
- def Lock()
- lockvar o.val3
- enddef
- Lock()
+ var o = C.new()
+ lockvar o.val[1]
+ o.val[0] = [9]
+ assert_equal([ [9], [2], [3] ], o.val)
+ try
+ o.val[1] = [999]
+ call assert_false(true, 'assign should have failed')
+ catch
+ assert_exception('E741:')
+ endtry
+ o.val[2] = [8]
+ assert_equal([ [9], [2], [8] ], o.val)
END
- v9.CheckSourceFailure(lines, 'E1335: Variable "val3" in class "C" is not writable')
+ v9.CheckSourceSuccess(lines)

- # read-only lockvar of def argument variable
+ # lock a script level variable from an object method
lines =<< trim END
vim9script

class C
- var val4: number
+ def Lock()
+ lockvar l
+ enddef
endclass
- def Lock(o: C)
- lockvar o.val4
- enddef
- Lock(C.new(3))
+
+ var l = [1]
+ C.new().Lock()
+ l[0] = 11
END
- v9.CheckSourceFailure(lines, 'E1335: Variable "val4" in class "C" is not writable')
+ v9.CheckSourceFailure(lines, 'E741: Value is locked: l[0] = 11', 11)

- # read-only lockvar from object method arg
+ # lock a list element referenced by a protected object variable
+ # in an object fetched via a script level list
lines =<< trim END
vim9script

class C
- var val5: number
- def Lock(c: C)
- lockvar c.val5
+ var _v1: list<list<number>>
+ def Lock()
+ lockvar lc[0]._v1[1]
enddef
endclass
- var o = C.new(3)
- o.Lock(C.new(5))
- END
- v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val5" in class "C"')

- # read-only lockvar from class method arg
- lines =<< trim END
- vim9script
+ var l = [[1], [2], [3]]
+ var o = C.new(l)
+ var lc: list<C> = [ o ]

- class C
- var val6: number
- static def Lock(c: C)
- lockvar c.val6
- enddef
- endclass
- var o = C.new(3)
- C.Lock(o)
+ o.Lock()
+ l[0] = [22]
+ l[1] = [33]
END
- v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val6" in class "C"')
-
- #
- # lockvar of public object variable
- #
+ v9.CheckSourceFailure(lines, 'E741: Value is locked: l[1] = [33]', 16)

- # lockvar from object method
+ # similar to the previous test, except the locking code is executing
+ # in a class that does not own the protected variable.
+ # Note that the locking code is in a class has a protected variable of
+ # the same name.
lines =<< trim END
vim9script

- class C
- public var val1: number
- def Lock()
- lockvar this.val1
+ class C2
+ var _v1: list<list<number>>
+ def Lock(obj: any)
+ lockvar lc[0]._v1[1]
enddef
endclass
- var o = C.new(3)
- o.Lock()
- END
- v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "this.val1" in class "C"', 1)
-
- # lockvar from scriptlevel
- lines =<< trim END
- vim9script

class C
- public var val2: number
+ var _v1: list<list<number>>
endclass
- var o = C.new(3)
- lockvar o.val2
- END
- v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o.val2" in class "C"', 7)

- # lockvar of scriptlevel variable from def
- lines =<< trim END
- vim9script
+ var l = [[1], [2], [3]]
+ var o = C.new(l)
+ var lc: list<C> = [ o ]

- class C
- public var val3: number
- endclass
- var o = C.new(3)
- def Lock()
- lockvar o.val3
- enddef
- Lock()
+ var o2 = C2.new()
+ o2.Lock(o)
END
- v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o.val3" in class "C"', 1)
+ v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_v1" in class "C"')
+enddef

- # lockvar of def argument variable
- lines =<< trim END
+" Test builtin islocked()
+def Test_lockvar_islocked()
+ # Can't lock class/object variable
+ # Lock class/object variable's value
+ # Lock item of variable's value (a list item)
+ # variable is at index 1 within class/object
+ var lines =<< trim END
vim9script

class C
- public var val4: number
+ var o0: list<list<number>> = [ [0], [1], [2]]
+ var o1: list<list<number>> = [[10], [11], [12]]
+ static var c0: list<list<number>> = [[20], [21], [22]]
+ static var c1: list<list<number>> = [[30], [31], [32]]
endclass
- def Lock(o: C)
- lockvar o.val4
+
+ def LockIt(arg: any)
+ lockvar arg
enddef
- Lock(C.new(3))
- END
- v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o.val4" in class "C"', 1)

- # lockvar from object method arg
- lines =<< trim END
- vim9script
+ def UnlockIt(arg: any)
+ unlockvar arg
+ enddef

- class C
- public var val5: number
- def Lock(c: C)
- lockvar c.val5
- enddef
- endclass
- var o = C.new(3)
- o.Lock(C.new(5))
- END
- v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val5" in class "C"', 1)
-
- # lockvar from class method arg
- lines =<< trim END
- vim9script
-
- class C
- public var val6: number
- static def Lock(c: C)
- lockvar c.val6
- enddef
- endclass
- var o = C.new(3)
- C.Lock(o)
- END
- v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val6" in class "C"', 1)
-enddef
-
-" Test trying to lock a class variable from various places
-def Test_lockvar_class_variable()
-
- # lockvar bare static from object method
- var lines =<< trim END
- vim9script
-
- class C
- public static var sval1: number
- def Lock()
- lockvar sval1
- enddef
- endclass
- var o = C.new()
- o.Lock()
- END
- v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval1" in class "C"', 1)
-
- # lockvar C.static from object method
- lines =<< trim END
- vim9script
-
- class C
- public static var sval2: number
- def Lock()
- lockvar C.sval2
- enddef
- endclass
- var o = C.new()
- o.Lock()
- END
- v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval2" in class "C"', 1)
-
- # lockvar bare static from class method
- lines =<< trim END
- vim9script
-
- class C
- public static var sval3: number
- static def Lock()
- lockvar sval3
- enddef
- endclass
- C.Lock()
- END
- v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval3" in class "C"', 1)
-
- # lockvar C.static from class method
- lines =<< trim END
- vim9script
-
- class C
- public static var sval4: number
- static def Lock()
- lockvar C.sval4
- enddef
- endclass
- C.Lock()
- END
- v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval4" in class "C"', 1)
-
- # lockvar C.static from script level
- lines =<< trim END
- vim9script
-
- class C
- public static var sval5: number
- endclass
- lockvar C.sval5
- END
- v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval5" in class "C"', 6)
-
- # lockvar o.static from script level
- lines =<< trim END
- vim9script
-
- class C
- public static var sval6: number
- endclass
- var o = C.new()
- lockvar o.sval6
- END
- v9.CheckSourceFailure(lines, 'E1375: Class variable "sval6" accessible only using class "C"', 7)
-enddef
-
-" Test locking an argument to :def
-def Test_lockvar_argument()
- # Lockvar a function arg
- var lines =<< trim END
- vim9script
-
- def Lock(val: any)
- lockvar val
- enddef
-
- var d = {a: 1, b: 2}
- Lock(d)
-
- d->extend({c: 3})
- END
- v9.CheckSourceFailure(lines, 'E741: Value is locked: extend() argument')
-
- # Lockvar a function arg. Verify "sval" is interpreted as argument and not a
- # class member in "C". This tests lval_root_is_arg.
- lines =<< trim END
- vim9script
-
- class C
- public static var sval: list<number>
- endclass
-
- def Lock2(sval: any)
- lockvar sval
- enddef
-
- var o = C.new()
- Lock2(o)
- END
- v9.CheckSourceSuccess(lines)
-
- # Lock a class.
- lines =<< trim END
- vim9script
-
- class C
- public static var sval: list<number>
- endclass
-
- def Lock2(sval: any)
- lockvar sval
- enddef
-
- Lock2(C)
- END
- v9.CheckSourceFailure(lines, 'E1405: Class "C" cannot be used as a value')
-
- # Lock an object.
- lines =<< trim END
- vim9script
-
- class C
- public static var sval: list<number>
- endclass
-
- def Lock2(sval: any)
- lockvar sval
- enddef
-
- Lock2(C.new())
- END
- v9.CheckSourceSuccess(lines)
-
- # In this case (unlike previous) "lockvar sval" is a class member.
- lines =<< trim END
- vim9script
-
- class C
- public static var sval: list<number>
- def Lock2()
- lockvar sval
- enddef
- endclass
-
-
- var o = C.new()
- o.Lock2()
- END
- v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval" in class "C"', 1)
-enddef
-
-" Test that this can be locked without error
-def Test_lockvar_this()
- # lockvar this
- var lines =<< trim END
- vim9script
- class C
- def TLock()
- lockvar this
- enddef
- endclass
- var o = C.new()
- o.TLock()
- END
- v9.CheckSourceSuccess(lines)
-
- # lockvar four (four letter word, but not this)
- lines =<< trim END
- vim9script
- class C
- def TLock4()
- var four: number
- lockvar four
- enddef
- endclass
- var o = C.new()
- o.TLock4()
- END
- v9.CheckSourceFailure(lines, 'E1178: Cannot lock or unlock a local variable')
-
- # lockvar this5; "this" + one char, 5 letter word, starting with "this"
- lines =<< trim END
- vim9script
- class C
- def TLock5()
- var this5: number
- lockvar this5
- enddef
- endclass
- var o = C.new()
- o.TLock5()
- END
- v9.CheckSourceFailure(lines, 'E1178: Cannot lock or unlock a local variable')
-enddef
-
-" Test some general lockvar cases
-def Test_lockvar_general()
- # lockvar an object and a class. It does nothing
- var lines =<< trim END
- vim9script
- class C
- endclass
- var o = C.new()
- lockvar o
- lockvar C
- END
- v9.CheckSourceSuccess(lines)
-
- # Lock a list element that's nested in an object variable from a :def
- lines =<< trim END
- vim9script
-
- class C
- public var val: list<list<number>> = [ [1], [2], [3] ]
- endclass
- def Lock2(obj: any)
- lockvar obj.val[1]
- enddef
-
- var o = C.new()
- Lock2(o)
- o.val[0] = [9]
- assert_equal([ [9], [2], [3] ], o.val)
- try
- o.val[1] = [999]
- call assert_false(true, 'assign should have failed')
- catch
- assert_exception('E741:')
- endtry
- o.val[2] = [8]
- assert_equal([ [9], [2], [8] ], o.val)
- END
- v9.CheckSourceSuccess(lines)
-
- # Lock a list element that's nested in an object variable from scriptlevel
- lines =<< trim END
- vim9script
-
- class C
- public var val: list<list<number>> = [ [1], [2], [3] ]
- endclass
-
- var o = C.new()
- lockvar o.val[1]
- o.val[0] = [9]
- assert_equal([ [9], [2], [3] ], o.val)
- try
- o.val[1] = [999]
- call assert_false(true, 'assign should have failed')
- catch
- assert_exception('E741:')
- endtry
- o.val[2] = [8]
- assert_equal([ [9], [2], [8] ], o.val)
- END
- v9.CheckSourceSuccess(lines)
-
- # lock a script level variable from an object method
- lines =<< trim END
- vim9script
-
- class C
- def Lock()
- lockvar l
- enddef
- endclass
-
- var l = [1]
- C.new().Lock()
- l[0] = 11
- END
- v9.CheckSourceFailure(lines, 'E741: Value is locked: l[0] = 11', 11)
-
- # lock a list element referenced by a protected object variable
- # in an object fetched via a script level list
- lines =<< trim END
- vim9script
-
- class C
- var _v1: list<list<number>>
- def Lock()
- lockvar lc[0]._v1[1]
- enddef
- endclass
-
- var l = [[1], [2], [3]]
- var o = C.new(l)
- var lc: list<C> = [ o ]
-
- o.Lock()
- l[0] = [22]
- l[1] = [33]
- END
- v9.CheckSourceFailure(lines, 'E741: Value is locked: l[1] = [33]', 16)
-
- # similar to the previous test, except the locking code is executing
- # in a class that does not own the protected variable.
- # Note that the locking code is in a class has a protected variable of
- # the same name.
- lines =<< trim END
- vim9script
-
- class C2
- var _v1: list<list<number>>
- def Lock(obj: any)
- lockvar lc[0]._v1[1]
- enddef
- endclass
-
- class C
- var _v1: list<list<number>>
- endclass
-
- var l = [[1], [2], [3]]
- var o = C.new(l)
- var lc: list<C> = [ o ]
-
- var o2 = C2.new()
- o2.Lock(o)
- END
- v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_v1" in class "C"')
-enddef
-
-" Test builtin islocked()
-def Test_lockvar_islocked()
- # Can't lock class/object variable
- # Lock class/object variable's value
- # Lock item of variable's value (a list item)
- # variable is at index 1 within class/object
- var lines =<< trim END
- vim9script
-
- class C
- var o0: list<list<number>> = [ [0], [1], [2]]
- var o1: list<list<number>> = [[10], [11], [12]]
- static var c0: list<list<number>> = [[20], [21], [22]]
- static var c1: list<list<number>> = [[30], [31], [32]]
- endclass
-
- def LockIt(arg: any)
- lockvar arg
- enddef
-
- def UnlockIt(arg: any)
- unlockvar arg
- enddef
-
- var obj = C.new()
- #lockvar obj.o1 # can't lock something you can't write to
-
- try
- lockvar obj.o1 # can't lock something you can't write to
- call assert_false(1, '"lockvar obj.o1" should have failed')
- catch
- call assert_exception('E1335:')
- endtry
-
- LockIt(obj.o1) # but can lock it's value
- assert_equal(1, islocked("obj.o1"))
- assert_equal(1, islocked("obj.o1[0]"))
- assert_equal(1, islocked("obj.o1[1]"))
- UnlockIt(obj.o1)
- assert_equal(0, islocked("obj.o1"))
- assert_equal(0, islocked("obj.o1[0]"))
-
- lockvar obj.o1[0]
- assert_equal(0, islocked("obj.o1"))
- assert_equal(1, islocked("obj.o1[0]"))
- assert_equal(0, islocked("obj.o1[1]"))
- unlockvar obj.o1[0]
- assert_equal(0, islocked("obj.o1"))
- assert_equal(0, islocked("obj.o1[0]"))
-
- # Same thing, but with a static
-
- try
- lockvar C.c1 # can't lock something you can't write to
- call assert_false(1, '"lockvar C.c1" should have failed')
- catch
- call assert_exception('E1335:')
- endtry
-
- LockIt(C.c1) # but can lock it's value
- assert_equal(1, islocked("C.c1"))
- assert_equal(1, islocked("C.c1[0]"))
- assert_equal(1, islocked("C.c1[1]"))
- UnlockIt(C.c1)
- assert_equal(0, islocked("C.c1"))
- assert_equal(0, islocked("C.c1[0]"))
-
- lockvar C.c1[0]
- assert_equal(0, islocked("C.c1"))
- assert_equal(1, islocked("C.c1[0]"))
- assert_equal(0, islocked("C.c1[1]"))
- unlockvar C.c1[0]
- assert_equal(0, islocked("C.c1"))
- assert_equal(0, islocked("C.c1[0]"))
- END
- v9.CheckSourceSuccess(lines)
-
- # Do islocked() from an object method
- # and then from a class method
- lines =<< trim END
- vim9script
-
- var l0o0 = [ [0], [1], [2]]
- var l0o1 = [ [10], [11], [12]]
- var l0c0 = [[120], [121], [122]]
- var l0c1 = [[130], [131], [132]]
-
- class C0
- var o0: list<list<number>> = l0o0
- var o1: list<list<number>> = l0o1
- static var c0: list<list<number>> = l0c0
- static var c1: list<list<number>> = l0c1
- def Islocked(arg: string): number
- return islocked(arg)
- enddef
- static def SIslocked(arg: string): number
- return islocked(arg)
- enddef
- endclass
-
- var l2o0 = [[20000], [20001], [20002]]
- var l2o1 = [[20010], [20011], [20012]]
- var l2c0 = [[20120], [20121], [20122]]
- var l2c1 = [[20130], [20131], [20132]]
-
- class C2
- var o0: list<list<number>> = l2o0
- var o1: list<list<number>> = l2o1
- static var c0: list<list<number>> = l2c0
- static var c1: list<list<number>> = l2c1
- def Islocked(arg: string): number
- return islocked(arg)
- enddef
- static def SIslocked(arg: string): number
- return islocked(arg)
- enddef
- endclass
-
- var obj0 = C0.new()
- var obj2 = C2.new()
-
- var l = [ obj0, null_object, obj2 ]
-
- # lock list, object func access through script var expr
- assert_equal(0, obj0.Islocked("l[0].o0"))
- assert_equal(0, obj0.Islocked("l[0].o0[2]"))
- lockvar l0o0
- assert_equal(1, obj0.Islocked("l[0].o0"))
- assert_equal(1, obj0.Islocked("l[0].o0[2]"))
-
- #echo "check-b" obj2.Islocked("l[1].o1") # NULL OBJECT
-
- # lock list element, object func access through script var expr
- lockvar l0o1[1]
- assert_equal(0, obj0.Islocked("this.o1[0]"))
- assert_equal(1, obj0.Islocked("this.o1[1]"))
-
- assert_equal(0, obj0.Islocked("this.o1"))
- lockvar l0o1
- assert_equal(1, obj0.Islocked("this.o1"))
- unlockvar l0o1
-
- lockvar l0c1[1]
-
- # static by class name member expr from same class
- assert_equal(0, obj0.Islocked("C0.c1[0]"))
- assert_equal(1, obj0.Islocked("C0.c1[1]"))
- # static by bare name member expr from same class
- assert_equal(0, obj0.Islocked("c1[0]"))
- assert_equal(1, obj0.Islocked("c1[1]"))
-
- # static by class name member expr from other class
- assert_equal(0, obj2.Islocked("C0.c1[0]"))
- assert_equal(1, obj2.Islocked("C0.c1[1]"))
- # static by bare name member expr from other class
- assert_equal(0, obj2.Islocked("c1[0]"))
- assert_equal(0, obj2.Islocked("c1[1]"))
-
-
- # static by bare name in same class
- assert_equal(0, obj0.Islocked("c0"))
- lockvar l0c0
- assert_equal(1, obj0.Islocked("c0"))
-
- #
- # similar stuff, but use static method
- #
-
- unlockvar l0o0
-
- # lock list, object func access through script var expr
- assert_equal(0, C0.SIslocked("l[0].o0"))
- assert_equal(0, C0.SIslocked("l[0].o0[2]"))
- lockvar l0o0
- assert_equal(1, C0.SIslocked("l[0].o0"))
- assert_equal(1, C0.SIslocked("l[0].o0[2]"))
-
- unlockvar l0o1
-
- # can't access "this" from class method
- try
- C0.SIslocked("this.o1[0]")
- call assert_0(1, '"C0.SIslocked("this.o1[0]")" should have failed')
- catch
- call assert_exception('E121: Undefined variable: this')
- endtry
-
- lockvar l0c1[1]
-
- # static by class name member expr from same class
- assert_equal(0, C0.SIslocked("C0.c1[0]"))
- assert_equal(1, C0.SIslocked("C0.c1[1]"))
- # static by bare name member expr from same class
- assert_equal(0, C0.SIslocked("c1[0]"))
- assert_equal(1, C0.SIslocked("c1[1]"))
-
- # static by class name member expr from other class
- assert_equal(0, C2.SIslocked("C0.c1[0]"))
- assert_equal(1, C2.SIslocked("C0.c1[1]"))
- # static by bare name member expr from other class
- assert_equal(0, C2.SIslocked("c1[0]"))
- assert_equal(0, C2.SIslocked("c1[1]"))
-
-
- # static by bare name in same class
- unlockvar l0c0
- assert_equal(0, C0.SIslocked("c0"))
- lockvar l0c0
- assert_equal(1, C0.SIslocked("c0"))
- END
- v9.CheckSourceSuccess(lines)
-
- # Check islocked class/object from various places.
- lines =<< trim END
- vim9script
-
- class C
- def Islocked(arg: string): number
- return islocked(arg)
- enddef
- static def SIslocked(arg: string): number
- return islocked(arg)
- enddef
- endclass
- var obj = C.new()
-
- # object method
- assert_equal(0, obj.Islocked("this"))
- assert_equal(0, obj.Islocked("C"))
-
- # class method
- ### assert_equal(0, C.SIslocked("this"))
- assert_equal(0, C.SIslocked("C"))
-
- #script level
- var v: number
- v = islocked("C")
- assert_equal(0, v)
- v = islocked("obj")
- assert_equal(0, v)
- END
- v9.CheckSourceSuccess(lines)
-enddef
-
-def Test_lockvar_islocked_notfound()
- # Try non-existent things
- var lines =<< trim END
- vim9script
-
- class C
- def Islocked(arg: string): number
- return islocked(arg)
- enddef
- static def SIslocked(arg: string): number
- return islocked(arg)
- enddef
- endclass
- var obj = C.new()
- assert_equal(-1, obj.Islocked("anywhere"))
- assert_equal(-1, C.SIslocked("notanywhere"))
- END
- v9.CheckSourceSuccess(lines)
-
- # Something not found of the form "name1.name2" is an error
- lines =<< trim END
- vim9script
-
- islocked("one.two")
- END
- v9.CheckSourceFailure(lines, 'E121: Undefined variable: one')
-
- lines =<< trim END
- vim9script
-
- class C
- var val = { key: "value" }
- def Islocked(arg: string): number
- return islocked(arg)
- enddef
- endclass
- var obj = C.new()
- obj.Islocked("this.val.not_there"))
- END
- v9.CheckSourceFailure(lines, 'E716: Key not present in Dictionary: "not_there"')
-
- lines =<< trim END
- vim9script
-
- class C
- def Islocked(arg: string): number
- return islocked(arg)
- enddef
- endclass
- var obj = C.new()
- obj.Islocked("this.notobjmember")
- END
- v9.CheckSourceFailure(lines, 'E1326: Variable "notobjmember" not found in object "C"')
-
- # access a script variable through methods
- lines =<< trim END
- vim9script
-
- var l = [1]
- class C
- def Islocked(arg: string): number
- return islocked(arg)
- enddef
- static def SIslocked(arg: string): number
- return islocked(arg)
- enddef
- endclass
- var obj = C.new()
- assert_equal(0, obj.Islocked("l"))
- assert_equal(0, C.SIslocked("l"))
- lockvar l
- assert_equal(1, obj.Islocked("l"))
- assert_equal(1, C.SIslocked("l"))
- END
- v9.CheckSourceSuccess(lines)
-enddef
-
-" Test for a protected object method
-def Test_protected_object_method()
- # Try calling a protected method using an object (at the script level)
- var lines =<< trim END
- vim9script
-
- class A
- def _Foo(): number
- return 1234
- enddef
- endclass
- var a = A.new()
- a._Foo()
- END
- v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 9)
-
- # Try calling a protected method using an object (from a def function)
- lines =<< trim END
- vim9script
-
- class A
- def _Foo(): number
- return 1234
- enddef
- endclass
- def T()
- var a = A.new()
- a._Foo()
- enddef
- T()
- END
- v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 2)
-
- # Use a protected method from another object method (in script context)
- lines =<< trim END
- vim9script
-
- class A
- def _Foo(): number
- return 1234
- enddef
- def Bar(): number
- return this._Foo()
- enddef
- endclass
- var a = A.new()
- assert_equal(1234, a.Bar())
- END
- v9.CheckSourceSuccess(lines)
-
- # Use a protected method from another object method (def function context)
- lines =<< trim END
- vim9script
-
- class A
- def _Foo(): number
- return 1234
- enddef
- def Bar(): number
- return this._Foo()
- enddef
- endclass
- def T()
- var a = A.new()
- assert_equal(1234, a.Bar())
- enddef
- T()
- END
- v9.CheckSourceSuccess(lines)
-
- # Try calling a protected method without the "this" prefix
- lines =<< trim END
- vim9script
-
- class A
- def _Foo(): number
- return 1234
- enddef
- def Bar(): number
- return _Foo()
- enddef
- endclass
- var a = A.new()
- a.Bar()
- END
- v9.CheckSourceFailure(lines, 'E117: Unknown function: _Foo', 1)
-
- # Try calling a protected method using the class name
- lines =<< trim END
- vim9script
-
- class A
- def _Foo(): number
- return 1234
- enddef
- endclass
- A._Foo()
- END
- v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 8)
-
- # Define two protected methods with the same name
- lines =<< trim END
- vim9script
-
- class A
- def _Foo()
- enddef
- def _Foo()
- enddef
- endclass
- var a = A.new()
- END
- v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 7)
-
- # Define a protected method and a object method with the same name
- lines =<< trim END
- vim9script
-
- class A
- def _Foo()
- enddef
- def Foo()
- enddef
- endclass
- var a = A.new()
- END
- v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 7)
-
- # Define an object method and a protected method with the same name
- lines =<< trim END
- vim9script
-
- class A
- def Foo()
- enddef
- def _Foo()
- enddef
- endclass
- var a = A.new()
- END
- v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 7)
-
- # Call a public method and a protected method from a protected method
- lines =<< trim END
- vim9script
-
- class A
- def Foo(): number
- return 100
- enddef
- def _Bar(): number
- return 200
- enddef
- def _Baz()
- assert_equal(100, this.Foo())
- assert_equal(200, this._Bar())
- enddef
- def T()
- this._Baz()
- enddef
- endclass
- var a = A.new()
- a.T()
- END
- v9.CheckSourceSuccess(lines)
-
- # Try calling a protected method from another class
- lines =<< trim END
- vim9script
-
- class A
- def _Foo(): number
- return 100
- enddef
- endclass
- class B
- def Foo(): number
- var a = A.new()
- a._Foo()
- enddef
- endclass
- var b = B.new()
- b.Foo()
- END
- v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 2)
-
- # Call a protected object method from a child class object method
- lines =<< trim END
- vim9script
- class A
- def _Foo(): number
- return 1234
- enddef
- endclass
- class B extends A
- def Bar()
- enddef
- endclass
- class C extends B
- def Baz(): number
- return this._Foo()
- enddef
- endclass
- var c = C.new()
- assert_equal(1234, c.Baz())
- END
- v9.CheckSourceSuccess(lines)
-
- # Call a protected object method from a child class object
- lines =<< trim END
- vim9script
- class A
- def _Foo(): number
- return 1234
- enddef
- endclass
- class B extends A
- def Bar()
- enddef
- endclass
- class C extends B
- def Baz(): number
- enddef
- endclass
- var c = C.new()
- assert_equal(1234, c._Foo())
- END
- v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 16)
-
- # Using "_" prefix in a method name should fail outside of a class
- lines =<< trim END
- vim9script
- def _Foo(): number
- return 1234
- enddef
- var a = _Foo()
- END
- v9.CheckSourceFailure(lines, 'E1267: Function name must start with a capital: _Foo(): number', 2)
-
- # Test for initializing a protected funcref instance variable to a protected
- # class method from another class
- lines =<< trim END
- vim9script
-
- class A
- static def _Internal(): string
- enddef
- endclass
-
- class B
- var Fn: func = A._Internal
- endclass
- var b = B.new()
- END
- v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Internal', 1)
-enddef
-
-" Test for an protected class method
-def Test_protected_class_method()
- # Try calling a class protected method (at the script level)
- var lines =<< trim END
- vim9script
-
- class A
- static def _Foo(): number
- return 1234
- enddef
- endclass
- A._Foo()
- END
- v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 8)
-
- # Try calling a class protected method (from a def function)
- lines =<< trim END
- vim9script
-
- class A
- static def _Foo(): number
- return 1234
- enddef
- endclass
- def T()
- A._Foo()
- enddef
- T()
- END
- v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1)
-
- # Try calling a class protected method using an object (at the script level)
- lines =<< trim END
- vim9script
-
- class A
- static def _Foo(): number
- return 1234
- enddef
- endclass
- var a = A.new()
- a._Foo()
- END
- v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 9)
-
- # Try calling a class protected method using an object (from a def function)
- lines =<< trim END
- vim9script
-
- class A
- static def _Foo(): number
- return 1234
- enddef
- endclass
- def T()
- var a = A.new()
- a._Foo()
- enddef
- T()
- END
- v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 2)
-
- # Use a class protected method from an object method
- lines =<< trim END
- vim9script
-
- class A
- static def _Foo(): number
- return 1234
- enddef
- def Bar()
- assert_equal(1234, _Foo())
- enddef
- endclass
- var a = A.new()
- a.Bar()
- END
- v9.CheckSourceSuccess(lines)
-
- # Use a class protected method from another class protected method without the
- # class name prefix.
- lines =<< trim END
- vim9script
+ var obj = C.new()
+ #lockvar obj.o1 # can't lock something you can't write to

- class A
- static def _Foo1(): number
- return 1234
- enddef
- static def _Foo2()
- assert_equal(1234, _Foo1())
- enddef
- def Bar()
- _Foo2()
- enddef
- endclass
- var a = A.new()
- a.Bar()
- END
- v9.CheckSourceSuccess(lines)
+ try
+ lockvar obj.o1 # can't lock something you can't write to
+ call assert_false(1, '"lockvar obj.o1" should have failed')
+ catch
+ call assert_exception('E1335:')
+ endtry

- # Declare a class method and a class protected method with the same name
- lines =<< trim END
- vim9script
+ LockIt(obj.o1) # but can lock it's value
+ assert_equal(1, islocked("obj.o1"))
+ assert_equal(1, islocked("obj.o1[0]"))
+ assert_equal(1, islocked("obj.o1[1]"))
+ UnlockIt(obj.o1)
+ assert_equal(0, islocked("obj.o1"))
+ assert_equal(0, islocked("obj.o1[0]"))

- class A
- static def _Foo()
- enddef
- static def Foo()
- enddef
- endclass
- var a = A.new()
- END
- v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 7)
+ lockvar obj.o1[0]
+ assert_equal(0, islocked("obj.o1"))
+ assert_equal(1, islocked("obj.o1[0]"))
+ assert_equal(0, islocked("obj.o1[1]"))
+ unlockvar obj.o1[0]
+ assert_equal(0, islocked("obj.o1"))
+ assert_equal(0, islocked("obj.o1[0]"))

- # Try calling a class protected method from another class
- lines =<< trim END
- vim9script
+ # Same thing, but with a static

- class A
- static def _Foo(): number
- return 1234
- enddef
- endclass
- class B
- def Foo(): number
- return A._Foo()
- enddef
- endclass
- var b = B.new()
- assert_equal(1234, b.Foo())
- END
- v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1)
+ try
+ lockvar C.c1 # can't lock something you can't write to
+ call assert_false(1, '"lockvar C.c1" should have failed')
+ catch
+ call assert_exception('E1335:')
+ endtry

- # Call a protected class method from a child class object method
- lines =<< trim END
- vim9script
- class A
- static def _Foo(): number
- return 1234
- enddef
- endclass
- class B extends A
- def Bar()
- enddef
- endclass
- class C extends B
- def Baz(): number
- return A._Foo()
- enddef
- endclass
- var c = C.new()
- assert_equal(1234, c.Baz())
+ LockIt(C.c1) # but can lock it's value
+ assert_equal(1, islocked("C.c1"))
+ assert_equal(1, islocked("C.c1[0]"))
+ assert_equal(1, islocked("C.c1[1]"))
+ UnlockIt(C.c1)
+ assert_equal(0, islocked("C.c1"))
+ assert_equal(0, islocked("C.c1[0]"))
+
+ lockvar C.c1[0]
+ assert_equal(0, islocked("C.c1"))
+ assert_equal(1, islocked("C.c1[0]"))
+ assert_equal(0, islocked("C.c1[1]"))
+ unlockvar C.c1[0]
+ assert_equal(0, islocked("C.c1"))
+ assert_equal(0, islocked("C.c1[0]"))
END
- v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1)
+ v9.CheckSourceSuccess(lines)

- # Call a protected class method from a child class protected class method
+ # Do islocked() from an object method
+ # and then from a class method
lines =<< trim END
vim9script
- class A
- static def _Foo(): number
- return 1234
- enddef
- endclass
- class B extends A
- def Bar()
+
+ var l0o0 = [ [0], [1], [2]]
+ var l0o1 = [ [10], [11], [12]]
+ var l0c0 = [[120], [121], [122]]
+ var l0c1 = [[130], [131], [132]]
+
+ class C0
+ var o0: list<list<number>> = l0o0
+ var o1: list<list<number>> = l0o1
+ static var c0: list<list<number>> = l0c0
+ static var c1: list<list<number>> = l0c1
+ def Islocked(arg: string): number
+ return islocked(arg)
enddef
- endclass
- class C extends B
- static def Baz(): number
- return A._Foo()
+ static def SIslocked(arg: string): number
+ return islocked(arg)
enddef
endclass
- assert_equal(1234, C.Baz())
- END
- v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1)

- # Call a protected class method from a child class object
- lines =<< trim END
- vim9script
- class A
- static def _Foo(): number
- return 1234
- enddef
- endclass
- class B extends A
- def Bar()
+ var l2o0 = [[20000], [20001], [20002]]
+ var l2o1 = [[20010], [20011], [20012]]
+ var l2c0 = [[20120], [20121], [20122]]
+ var l2c1 = [[20130], [20131], [20132]]
+
+ class C2
+ var o0: list<list<number>> = l2o0
+ var o1: list<list<number>> = l2o1
+ static var c0: list<list<number>> = l2c0
+ static var c1: list<list<number>> = l2c1
+ def Islocked(arg: string): number
+ return islocked(arg)
enddef
- endclass
- class C extends B
- def Baz(): number
+ static def SIslocked(arg: string): number
+ return islocked(arg)
enddef
endclass
- var c = C.new()
- assert_equal(1234, C._Foo())
- END
- v9.CheckSourceFailure(lines, 'E1325: Method "_Foo" not found in class "C"', 16)

- # Test for initializing a protected funcref class variable to a protected
- # class method from another class
- lines =<< trim END
- vim9script
+ var obj0 = C0.new()
+ var obj2 = C2.new()

- class A
- static def _Internal(): string
- enddef
- endclass
+ var l = [ obj0, null_object, obj2 ]

- class B
- static var Fn: func = A._Internal
- endclass
- END
- v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Internal', 10)
-enddef
+ # lock list, object func access through script var expr
+ assert_equal(0, obj0.Islocked("l[0].o0"))
+ assert_equal(0, obj0.Islocked("l[0].o0[2]"))
+ lockvar l0o0
+ assert_equal(1, obj0.Islocked("l[0].o0"))
+ assert_equal(1, obj0.Islocked("l[0].o0[2]"))

-" Test for using the return value of a class/object method as a function
-" argument.
-def Test_objmethod_funcarg()
- var lines =<< trim END
- vim9script
+ #echo "check-b" obj2.Islocked("l[1].o1") # NULL OBJECT

- class C
- def Foo(): string
- return 'foo'
- enddef
- endclass
+ # lock list element, object func access through script var expr
+ lockvar l0o1[1]
+ assert_equal(0, obj0.Islocked("this.o1[0]"))
+ assert_equal(1, obj0.Islocked("this.o1[1]"))

- def Bar(a: number, s: string): string
- return s
- enddef
+ assert_equal(0, obj0.Islocked("this.o1"))
+ lockvar l0o1
+ assert_equal(1, obj0.Islocked("this.o1"))
+ unlockvar l0o1

- def Baz(c: C)
- assert_equal('foo', Bar(10, c.Foo()))
- enddef
+ lockvar l0c1[1]

- var t = C.new()
- Baz(t)
+ # static by class name member expr from same class
+ assert_equal(0, obj0.Islocked("C0.c1[0]"))
+ assert_equal(1, obj0.Islocked("C0.c1[1]"))
+ # static by bare name member expr from same class
+ assert_equal(0, obj0.Islocked("c1[0]"))
+ assert_equal(1, obj0.Islocked("c1[1]"))
+
+ # static by class name member expr from other class
+ assert_equal(0, obj2.Islocked("C0.c1[0]"))
+ assert_equal(1, obj2.Islocked("C0.c1[1]"))
+ # static by bare name member expr from other class
+ assert_equal(0, obj2.Islocked("c1[0]"))
+ assert_equal(0, obj2.Islocked("c1[1]"))
+
+
+ # static by bare name in same class
+ assert_equal(0, obj0.Islocked("c0"))
+ lockvar l0c0
+ assert_equal(1, obj0.Islocked("c0"))
+
+ #
+ # similar stuff, but use static method
+ #
+
+ unlockvar l0o0
+
+ # lock list, object func access through script var expr
+ assert_equal(0, C0.SIslocked("l[0].o0"))
+ assert_equal(0, C0.SIslocked("l[0].o0[2]"))
+ lockvar l0o0
+ assert_equal(1, C0.SIslocked("l[0].o0"))
+ assert_equal(1, C0.SIslocked("l[0].o0[2]"))
+
+ unlockvar l0o1
+
+ # can't access "this" from class method
+ try
+ C0.SIslocked("this.o1[0]")
+ call assert_0(1, '"C0.SIslocked("this.o1[0]")" should have failed')
+ catch
+ call assert_exception('E121: Undefined variable: this')
+ endtry
+
+ lockvar l0c1[1]
+
+ # static by class name member expr from same class
+ assert_equal(0, C0.SIslocked("C0.c1[0]"))
+ assert_equal(1, C0.SIslocked("C0.c1[1]"))
+ # static by bare name member expr from same class
+ assert_equal(0, C0.SIslocked("c1[0]"))
+ assert_equal(1, C0.SIslocked("c1[1]"))
+
+ # static by class name member expr from other class
+ assert_equal(0, C2.SIslocked("C0.c1[0]"))
+ assert_equal(1, C2.SIslocked("C0.c1[1]"))
+ # static by bare name member expr from other class
+ assert_equal(0, C2.SIslocked("c1[0]"))
+ assert_equal(0, C2.SIslocked("c1[1]"))
+
+
+ # static by bare name in same class
+ unlockvar l0c0
+ assert_equal(0, C0.SIslocked("c0"))
+ lockvar l0c0
+ assert_equal(1, C0.SIslocked("c0"))
END
v9.CheckSourceSuccess(lines)

+ # Check islocked class/object from various places.
lines =<< trim END
vim9script

class C
- static def Foo(): string
- return 'foo'
+ def Islocked(arg: string): number
+ return islocked(arg)
+ enddef
+ static def SIslocked(arg: string): number
+ return islocked(arg)
enddef
endclass
+ var obj = C.new()

- def Bar(a: number, s: string): string
- return s
- enddef
+ # object method
+ assert_equal(0, obj.Islocked("this"))
+ assert_equal(0, obj.Islocked("C"))

- def Baz()
- assert_equal('foo', Bar(10, C.Foo()))
- enddef
+ # class method
+ ### assert_equal(0, C.SIslocked("this"))
+ assert_equal(0, C.SIslocked("C"))

- Baz()
+ #script level
+ var v: number
+ v = islocked("C")
+ assert_equal(0, v)
+ v = islocked("obj")
+ assert_equal(0, v)
END
v9.CheckSourceSuccess(lines)
enddef

-def Test_static_inheritence()
- # subclasses get their own static copy
+def Test_lockvar_islocked_notfound()
+ # Try non-existent things
var lines =<< trim END
vim9script

- class A
- static var _svar: number
- var _mvar: number
- def new()
- _svar = 1
- this._mvar = 101
- enddef
- def AccessObject(): number
- return this._mvar
+ class C
+ def Islocked(arg: string): number
+ return islocked(arg)
enddef
- def AccessStaticThroughObject(): number
- return _svar
+ static def SIslocked(arg: string): number
+ return islocked(arg)
enddef
endclass
+ var obj = C.new()
+ assert_equal(-1, obj.Islocked("anywhere"))
+ assert_equal(-1, C.SIslocked("notanywhere"))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Something not found of the form "name1.name2" is an error
+ lines =<< trim END
+ vim9script
+
+ islocked("one.two")
+ END
+ v9.CheckSourceFailure(lines, 'E121: Undefined variable: one')

- class B extends A
- def new()
- this._mvar = 102
+ lines =<< trim END
+ vim9script
+
+ class C
+ var val = { key: "value" }
+ def Islocked(arg: string): number
+ return islocked(arg)
enddef
endclass
+ var obj = C.new()
+ obj.Islocked("this.val.not_there"))
+ END
+ v9.CheckSourceFailure(lines, 'E716: Key not present in Dictionary: "not_there"')

- class C extends B
- def new()
- this._mvar = 103
- enddef
+ lines =<< trim END
+ vim9script

- def AccessPrivateStaticThroughClassName(): number
- assert_equal(1, A._svar)
- return 444
+ class C
+ def Islocked(arg: string): number
+ return islocked(arg)
enddef
endclass
+ var obj = C.new()
+ obj.Islocked("this.notobjmember")
+ END
+ v9.CheckSourceFailure(lines, 'E1326: Variable "notobjmember" not found in object "C"')

- var oa = A.new()
- var ob = B.new()
- var oc = C.new()
- assert_equal(101, oa.AccessObject())
- assert_equal(102, ob.AccessObject())
- assert_equal(103, oc.AccessObject())
-
- assert_fails('echo oc.AccessPrivateStaticThroughClassName()', 'E1333: Cannot access protected variable "_svar" in class "A"')
+ # access a script variable through methods
+ lines =<< trim END
+ vim9script

- # verify object properly resolves to correct static
- assert_equal(1, oa.AccessStaticThroughObject())
- assert_equal(1, ob.AccessStaticThroughObject())
- assert_equal(1, oc.AccessStaticThroughObject())
+ var l = [1]
+ class C
+ def Islocked(arg: string): number
+ return islocked(arg)
+ enddef
+ static def SIslocked(arg: string): number
+ return islocked(arg)
+ enddef
+ endclass
+ var obj = C.new()
+ assert_equal(0, obj.Islocked("l"))
+ assert_equal(0, C.SIslocked("l"))
+ lockvar l
+ assert_equal(1, obj.Islocked("l"))
+ assert_equal(1, C.SIslocked("l"))
END
v9.CheckSourceSuccess(lines)
enddef

-" Test for declaring duplicate object and class members
-def Test_dup_member_variable()
- # Duplicate member variable
+" Test for a protected object method
+def Test_protected_object_method()
+ # Try calling a protected method using an object (at the script level)
var lines =<< trim END
vim9script
- class C
- var val = 10
- var val = 20
+
+ class A
+ def _Foo(): number
+ return 1234
+ enddef
endclass
+ var a = A.new()
+ a._Foo()
END
- v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4)
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 9)

- # Duplicate protected member variable
+ # Try calling a protected method using an object (from a def function)
lines =<< trim END
vim9script
- class C
- var _val = 10
- var _val = 20
+
+ class A
+ def _Foo(): number
+ return 1234
+ enddef
endclass
+ def T()
+ var a = A.new()
+ a._Foo()
+ enddef
+ T()
END
- v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 4)
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 2)

- # Duplicate public member variable
+ # Use a protected method from another object method (in script context)
lines =<< trim END
vim9script
- class C
- public var val = 10
- public var val = 20
+
+ class A
+ def _Foo(): number
+ return 1234
+ enddef
+ def Bar(): number
+ return this._Foo()
+ enddef
endclass
+ var a = A.new()
+ assert_equal(1234, a.Bar())
END
- v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4)
+ v9.CheckSourceSuccess(lines)

- # Duplicate protected member variable
+ # Use a protected method from another object method (def function context)
lines =<< trim END
vim9script
- class C
- var val = 10
- var _val = 20
+
+ class A
+ def _Foo(): number
+ return 1234
+ enddef
+ def Bar(): number
+ return this._Foo()
+ enddef
endclass
+ def T()
+ var a = A.new()
+ assert_equal(1234, a.Bar())
+ enddef
+ T()
END
- v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 4)
+ v9.CheckSourceSuccess(lines)

- # Duplicate public and protected member variable
+ # Try calling a protected method without the "this" prefix
lines =<< trim END
vim9script
- class C
- var _val = 20
- public var val = 10
+
+ class A
+ def _Foo(): number
+ return 1234
+ enddef
+ def Bar(): number
+ return _Foo()
+ enddef
endclass
+ var a = A.new()
+ a.Bar()
END
- v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4)
+ v9.CheckSourceFailure(lines, 'E117: Unknown function: _Foo', 1)

- # Duplicate class member variable
+ # Try calling a protected method using the class name
lines =<< trim END
vim9script
- class C
- static var s: string = "abc"
- static var _s: string = "def"
+
+ class A
+ def _Foo(): number
+ return 1234
+ enddef
endclass
+ A._Foo()
END
- v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _s', 4)
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 8)

- # Duplicate public and protected class member variable
+ # Define two protected methods with the same name
lines =<< trim END
vim9script
- class C
- public static var s: string = "abc"
- static var _s: string = "def"
+
+ class A
+ def _Foo()
+ enddef
+ def _Foo()
+ enddef
endclass
+ var a = A.new()
END
- v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _s', 4)
+ v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 7)

- # Duplicate class and object member variable
+ # Define a protected method and a object method with the same name
lines =<< trim END
vim9script
- class C
- static var val = 10
- var val = 20
- def new()
+
+ class A
+ def _Foo()
+ enddef
+ def Foo()
enddef
endclass
- var c = C.new()
- assert_equal(10, C.val)
- assert_equal(20, c.val)
+ var a = A.new()
END
- v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4)
+ v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 7)

- # Duplicate object member variable in a derived class
+ # Define an object method and a protected method with the same name
lines =<< trim END
vim9script
+
class A
- var val = 10
- endclass
- class B extends A
- endclass
- class C extends B
- var val = 20
+ def Foo()
+ enddef
+ def _Foo()
+ enddef
endclass
+ var a = A.new()
END
- v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 9)
+ v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 7)

- # Duplicate object protected member variable in a derived class
+ # Call a public method and a protected method from a protected method
lines =<< trim END
vim9script
+
class A
- var _val = 10
+ def Foo(): number
+ return 100
+ enddef
+ def _Bar(): number
+ return 200
+ enddef
+ def _Baz()
+ assert_equal(100, this.Foo())
+ assert_equal(200, this._Bar())
+ enddef
+ def T()
+ this._Baz()
+ enddef
endclass
- class B extends A
+ var a = A.new()
+ a.T()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Try calling a protected method from another class
+ lines =<< trim END
+ vim9script
+
+ class A
+ def _Foo(): number
+ return 100
+ enddef
endclass
- class C extends B
- var _val = 20
+ class B
+ def Foo(): number
+ var a = A.new()
+ a._Foo()
+ enddef
endclass
+ var b = B.new()
+ b.Foo()
END
- v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 9)
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 2)

- # Duplicate object protected member variable in a derived class
+ # Call a protected object method from a child class object method
lines =<< trim END
vim9script
class A
- var val = 10
+ def _Foo(): number
+ return 1234
+ enddef
endclass
class B extends A
+ def Bar()
+ enddef
endclass
class C extends B
- var _val = 20
+ def Baz(): number
+ return this._Foo()
+ enddef
endclass
+ var c = C.new()
+ assert_equal(1234, c.Baz())
END
- v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 9)
+ v9.CheckSourceSuccess(lines)

- # Duplicate object member variable in a derived class
+ # Call a protected object method from a child class object
lines =<< trim END
vim9script
class A
- var _val = 10
+ def _Foo(): number
+ return 1234
+ enddef
endclass
class B extends A
+ def Bar()
+ enddef
endclass
class C extends B
- var val = 20
+ def Baz(): number
+ enddef
endclass
+ var c = C.new()
+ assert_equal(1234, c._Foo())
END
- v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 9)
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 16)

- # Two member variables with a common prefix
+ # Using "_" prefix in a method name should fail outside of a class
lines =<< trim END
vim9script
- class A
- public static var svar2: number
- public static var svar: number
- endclass
- END
- v9.CheckSourceSuccess(lines)
-enddef
-
-" Test for accessing a protected member outside a class in a def function
-def Test_protected_member_access_outside_class()
- # try to modify a protected instance variable from a def function
- var lines =<< trim END
- vim9script
- class A
- var _val = 10
- endclass
- def T()
- var a = A.new()
- a._val = 20
+ def _Foo(): number
+ return 1234
enddef
- T()
+ var a = _Foo()
END
- v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_val" in class "A"', 2)
+ v9.CheckSourceFailure(lines, 'E1267: Function name must start with a capital: _Foo(): number', 2)

- # access a non-existing protected instance variable from a def function
+ # Test for initializing a protected funcref instance variable to a protected
+ # class method from another class
lines =<< trim END
vim9script
+
class A
- var _val = 10
+ static def _Internal(): string
+ enddef
endclass
- def T()
- var a = A.new()
- a._a = 1
- enddef
- T()
+
+ class B
+ var Fn: func = A._Internal
+ endclass
+ var b = B.new()
END
- v9.CheckSourceFailure(lines, 'E1326: Variable "_a" not found in object "A"', 2)
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Internal', 1)
+enddef

- # try to read a protected class variable from a def function using an instance
- lines =<< trim END
+" Test for an protected class method
+def Test_protected_class_method()
+ # Try calling a class protected method (at the script level)
+ var lines =<< trim END
vim9script
+
class A
- static var _val = 10
+ static def _Foo(): number
+ return 1234
+ enddef
endclass
- def T()
- var a = A.new()
- var x = a._val
- enddef
- T()
+ A._Foo()
END
- v9.CheckSourceFailure(lines, 'E1375: Class variable "_val" accessible only using class "A"', 2)
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 8)

- # try to modify a protected class variable from a def function using an
- # instance
+ # Try calling a class protected method (from a def function)
lines =<< trim END
vim9script
+
class A
- static var _val = 10
+ static def _Foo(): number
+ return 1234
+ enddef
endclass
def T()
- var a = A.new()
- a._val = 3
+ A._Foo()
enddef
T()
END
- v9.CheckSourceFailure(lines, 'E1375: Class variable "_val" accessible only using class "A"', 2)
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1)

- # try to read a protected class variable from a def function using the class
+ # Try calling a class protected method using an object (at the script level)
lines =<< trim END
vim9script
+
class A
- static var _val = 10
+ static def _Foo(): number
+ return 1234
+ enddef
endclass
- def T()
- var x = A._val
- enddef
- T()
+ var a = A.new()
+ a._Foo()
END
- v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_val" in class "A"', 1)
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 9)

- # try to modify a protected class variable from a def function using the class
+ # Try calling a class protected method using an object (from a def function)
lines =<< trim END
vim9script
+
class A
- static var _val = 10
+ static def _Foo(): number
+ return 1234
+ enddef
endclass
def T()
- A._val = 3
+ var a = A.new()
+ a._Foo()
enddef
T()
END
- v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_val" in class "A"', 1)
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 2)

- # initialize a protected class variable using a protected class variable
- # from another class
+ # Use a class protected method from an object method
lines =<< trim END
vim9script
+
class A
- static var _aval = 10
- endclass
- class B
- static var _bval = A._aval
+ static def _Foo(): number
+ return 1234
+ enddef
+ def Bar()
+ assert_equal(1234, _Foo())
+ enddef
endclass
+ var a = A.new()
+ a.Bar()
END
- v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_aval" in class "A"', 7)
+ v9.CheckSourceSuccess(lines)

- # initialize a protected instance variable using a protected class variable
- # from another class
+ # Use a class protected method from another class protected method without the
+ # class name prefix.
lines =<< trim END
vim9script
- class A
- static var _aval = 10
- endclass
- class B
- var _bval = A._aval
- endclass
- var b = B.new()
- END
- v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_aval" in class "A"', 1)
-enddef

-" Test for changing the member access of an interface in a implementation class
-def Test_change_interface_member_access()
- var lines =<< trim END
- vim9script
- interface A
- var val: number
- endinterface
- class B implements A
- public var val = 10
+ class A
+ static def _Foo1(): number
+ return 1234
+ enddef
+ static def _Foo2()
+ assert_equal(1234, _Foo1())
+ enddef
+ def Bar()
+ _Foo2()
+ enddef
endclass
+ var a = A.new()
+ a.Bar()
END
- v9.CheckSourceFailure(lines, 'E1367: Access level of variable "val" of interface "A" is different', 7)
+ v9.CheckSourceSuccess(lines)

+ # Declare a class method and a class protected method with the same name
lines =<< trim END
vim9script
- interface A
- var val: number
- endinterface
- class B implements A
- public var val = 10
- endclass
- END
- v9.CheckSourceFailure(lines, 'E1367: Access level of variable "val" of interface "A" is different', 7)
-enddef

-" Test for trying to change a readonly member from a def function
-def Test_readonly_member_change_in_def_func()
- var lines =<< trim END
- vim9script
class A
- var val: number
+ static def _Foo()
+ enddef
+ static def Foo()
+ enddef
endclass
- def T()
- var a = A.new()
- a.val = 20
- enddef
- T()
+ var a = A.new()
END
- v9.CheckSourceFailure(lines, 'E1335: Variable "val" in class "A" is not writable', 2)
-enddef
+ v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 7)

-" Test for reading and writing a class member from a def function
-def Test_modify_class_member_from_def_function()
- var lines =<< trim END
+ # Try calling a class protected method from another class
+ lines =<< trim END
vim9script
- class A
- var var1: number = 10
- public static var var2: list<number> = [1, 2]
- public static var var3: dict<number> = {a: 1, b: 2}
- static var _priv_var4: number = 40
- endclass
- def T()
- assert_equal([1, 2], A.var2)
- assert_equal({a: 1, b: 2}, A.var3)
- A.var2 = [3, 4]
- A.var3 = {c: 3, d: 4}
- assert_equal([3, 4], A.var2)
- assert_equal({c: 3, d: 4}, A.var3)
- assert_fails('echo A._priv_var4', 'E1333: Cannot access protected variable "_priv_var4" in class "A"')
- enddef
- T()
- END
- v9.CheckSourceSuccess(lines)
-enddef

-" Test for accessing a class member variable using an object
-def Test_class_variable_access_using_object()
- var lines =<< trim END
- vim9script
class A
- public static var svar1: list<number> = [1]
- public static var 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()
+ static def _Foo(): number
+ return 1234
+ enddef
+ endclass
+ class B
+ def Foo(): number
+ return A._Foo()
+ enddef
+ endclass
+ var b = B.new()
+ assert_equal(1234, b.Foo())
END
- v9.CheckSourceSuccess(lines)
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1)

- # Cannot read from a class variable using an object in script context
+ # Call a protected class method from a child class object method
lines =<< trim END
vim9script
class A
- public var var1: number
- public static var svar2: list<number> = [1]
+ static def _Foo(): number
+ return 1234
+ enddef
endclass
-
- var a = A.new()
- echo a.svar2
+ class B extends A
+ def Bar()
+ enddef
+ endclass
+ class C extends B
+ def Baz(): number
+ return A._Foo()
+ enddef
+ endclass
+ var c = C.new()
+ assert_equal(1234, c.Baz())
END
- v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 8)
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1)

- # Cannot write to a class variable using an object in script context
+ # Call a protected class method from a child class protected class method
lines =<< trim END
vim9script
class A
- public var var1: number
- public static var svar2: list<number> = [1]
+ static def _Foo(): number
+ return 1234
+ enddef
endclass
-
- var a = A.new()
- a.svar2 = [2]
+ class B extends A
+ def Bar()
+ enddef
+ endclass
+ class C extends B
+ static def Baz(): number
+ return A._Foo()
+ enddef
+ endclass
+ assert_equal(1234, C.Baz())
END
- v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 8)
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1)

- # Cannot read from a class variable using an object in def method context
+ # Call a protected class method from a child class object
lines =<< trim END
vim9script
class A
- public var var1: number
- public static var svar2: list<number> = [1]
+ static def _Foo(): number
+ return 1234
+ enddef
endclass
-
- def T()
- var a = A.new()
- echo a.svar2
- enddef
- T()
+ class B extends A
+ def Bar()
+ enddef
+ endclass
+ class C extends B
+ def Baz(): number
+ enddef
+ endclass
+ var c = C.new()
+ assert_equal(1234, C._Foo())
END
- v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 2)
+ v9.CheckSourceFailure(lines, 'E1325: Method "_Foo" not found in class "C"', 16)

- # Cannot write to a class variable using an object in def method context
+ # Test for initializing a protected funcref class variable to a protected
+ # class method from another class
lines =<< trim END
vim9script
+
class A
- public var var1: number
- public static var svar2: list<number> = [1]
+ static def _Internal(): string
+ enddef
endclass

- def T()
- var a = A.new()
- a.svar2 = [2]
- enddef
- T()
+ class B
+ static var Fn: func = A._Internal
+ endclass
END
- v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 2)
+ v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Internal', 10)
enddef

-" Test for using a interface method using a child object
-def Test_interface_method_from_child()
+" Test for using the return value of a class/object method as a function
+" argument.
+def Test_objmethod_funcarg()
var lines =<< trim END
vim9script

- interface A
- def Foo(): string
- endinterface
-
- class B implements A
+ class C
def Foo(): string
return 'foo'
enddef
endclass

- class C extends B
- def Bar(): string
- return 'bar'
- enddef
- endclass
-
- def T1(a: A)
- assert_equal('foo', a.Foo())
+ def Bar(a: number, s: string): string
+ return s
enddef

- def T2(b: B)
- assert_equal('foo', b.Foo())
+ def Baz(c: C)
+ assert_equal('foo', Bar(10, c.Foo()))
enddef

- var c = C.new()
- T1(c)
- T2(c)
+ var t = C.new()
+ Baz(t)
END
v9.CheckSourceSuccess(lines)
-enddef

-" Test for using an interface method using a child object when it is overridden
-" by the child class.
-def Test_interface_overridden_method_from_child()
- var lines =<< trim END
+ lines =<< trim END
vim9script

- interface A
- def Foo(): string
- endinterface
-
- class B implements A
- def Foo(): string
- return 'b-foo'
- enddef
- endclass
-
- class C extends B
- def Bar(): string
- return 'bar'
- enddef
- def Foo(): string
- return 'c-foo'
+ class C
+ static def Foo(): string
+ return 'foo'
enddef
endclass

- def T1(a: A)
- assert_equal('c-foo', a.Foo())
+ def Bar(a: number, s: string): string
+ return s
enddef

- def T2(b: B)
- assert_equal('c-foo', b.Foo())
+ def Baz()
+ assert_equal('foo', Bar(10, C.Foo()))
enddef

- var c = C.new()
- T1(c)
- T2(c)
+ Baz()
END
v9.CheckSourceSuccess(lines)
enddef

-" Test for interface inheritance
-def Test_interface_inheritance()
+def Test_static_inheritence()
+ # subclasses get their own static copy
var lines =<< trim END
vim9script

- interface A
- def A_Fn(): string
- endinterface
-
- interface B
- def B_Fn(): string
- endinterface
-
- interface C
- def C_Fn(): string
- endinterface
-
- class C1 implements A
- def A_Fn(): string
- return 'c1-a'
+ class A
+ static var _svar: number
+ var _mvar: number
+ def new()
+ _svar = 1
+ this._mvar = 101
enddef
- endclass
-
- class C2 extends C1 implements B
- def B_Fn(): string
- return 'c2-b'
+ def AccessObject(): number
+ return this._mvar
enddef
- def A_Fn(): string
- return 'c2-a'
+ def AccessStaticThroughObject(): number
+ return _svar
enddef
endclass

- class C3 extends C2 implements C
- def C_Fn(): string
- return 'c3-c'
- enddef
- def A_Fn(): string
- return 'c3-a'
- enddef
- def B_Fn(): string
- return 'c3-b'
+ class B extends A
+ def new()
+ this._mvar = 102
enddef
endclass

- def T1(a: A, s: string)
- assert_equal(s, a.A_Fn())
- enddef
-
- def T2(b: B, s: string)
- assert_equal(s, b.B_Fn())
- enddef
-
- def T3(c: C, s: string)
- assert_equal(s, c.C_Fn())
- enddef
-
- def T4(c1: C1)
- T1(c1, 'c3-a')
- enddef
-
- def T5(c2: C2)
- T1(c2, 'c3-a')
- T2(c2, 'c3-b')
- enddef
-
- def T6(c3: C3)
- T1(c3, 'c3-a')
- T2(c3, 'c3-b')
- T3(c3, 'c3-c')
- enddef
-
- var o3 = C3.new()
- T4(o3)
- T5(o3)
- T6(o3)
- END
- v9.CheckSourceSuccess(lines)
-
- # Both the parent and child classes implement the same interface
- lines =<< trim END
- vim9script
-
- interface I
- def Foo(): string
- endinterface
-
- class A implements I
- def Foo(): string
- return 'A-foo'
+ class C extends B
+ def new()
+ this._mvar = 103
enddef
- endclass

- class B implements I
- def Foo(): string
- return 'B-foo'
+ def AccessPrivateStaticThroughClassName(): number
+ assert_equal(1, A._svar)
+ return 444
enddef
endclass

- def Bar(i1: I): string
- return i1.Foo()
- enddef
+ var oa = A.new()
+ var ob = B.new()
+ var oc = C.new()
+ assert_equal(101, oa.AccessObject())
+ assert_equal(102, ob.AccessObject())
+ assert_equal(103, oc.AccessObject())

- var b = B.new()
- assert_equal('B-foo', Bar(b))
- END
- v9.CheckSourceSuccess(lines)
-enddef
+ assert_fails('echo oc.AccessPrivateStaticThroughClassName()', 'E1333: Cannot access protected variable "_svar" in class "A"')

-" Test for abstract methods
-def Test_abstract_method()
- # Use two abstract methods
- var lines =<< trim END
- vim9script
- abstract class A
- def M1(): number
- return 10
- enddef
- abstract def M2(): number
- abstract def M3(): number
- endclass
- class B extends A
- def M2(): number
- return 20
- enddef
- def M3(): number
- return 30
- enddef
- endclass
- var b = B.new()
- assert_equal([10, 20, 30], [b.M1(), b.M2(), b.M3()])
+ # verify object properly resolves to correct static
+ assert_equal(1, oa.AccessStaticThroughObject())
+ assert_equal(1, ob.AccessStaticThroughObject())
+ assert_equal(1, oc.AccessStaticThroughObject())
END
v9.CheckSourceSuccess(lines)
+enddef

- # Don't define an abstract method
- lines =<< trim END
- vim9script
- abstract class A
- abstract def Foo()
- endclass
- class B extends A
+" Test for declaring duplicate object and class members
+def Test_dup_member_variable()
+ # Duplicate member variable
+ var lines =<< trim END
+ vim9script
+ class C
+ var val = 10
+ var val = 20
endclass
END
- v9.CheckSourceFailure(lines, 'E1373: Abstract method "Foo" is not implemented', 6)
+ v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4)

- # Use abstract method in a concrete class
+ # Duplicate protected member variable
lines =<< trim END
vim9script
- class A
- abstract def Foo()
- endclass
- class B extends A
+ class C
+ var _val = 10
+ var _val = 20
endclass
END
- v9.CheckSourceFailure(lines, 'E1372: Abstract method "abstract def Foo()" cannot be defined in a concrete class', 3)
+ v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 4)

- # Use abstract method in an interface
+ # Duplicate public member variable
lines =<< trim END
vim9script
- interface A
- abstract def Foo()
- endinterface
- class B implements A
- def Foo()
- enddef
+ class C
+ public var val = 10
+ public var val = 20
endclass
END
- v9.CheckSourceFailure(lines, 'E1404: Abstract cannot be used in an interface', 3)
+ v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4)

- # Use abstract static method in an interface
+ # Duplicate protected member variable
lines =<< trim END
vim9script
- interface A
- abstract static def Foo()
- enddef
- endinterface
+ class C
+ var val = 10
+ var _val = 20
+ endclass
END
- v9.CheckSourceFailure(lines, 'E1404: Abstract cannot be used in an interface', 3)
+ v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 4)

- # Use abstract static variable in an interface
+ # Duplicate public and protected member variable
lines =<< trim END
vim9script
- interface A
- abstract static foo: number = 10
- endinterface
+ class C
+ var _val = 20
+ public var val = 10
+ endclass
END
- v9.CheckSourceFailure(lines, 'E1404: Abstract cannot be used in an interface', 3)
+ v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4)

- # Abbreviate the "abstract" keyword
+ # Duplicate class member variable
lines =<< trim END
vim9script
- class A
- abs def Foo()
+ class C
+ static var s: string = "abc"
+ static var _s: string = "def"
endclass
END
- v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: abs def Foo()', 3)
+ v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _s', 4)

- # Use "abstract" with a member variable
+ # Duplicate public and protected class member variable
lines =<< trim END
vim9script
- abstract class A
- abstract this.val = 10
+ class C
+ public static var s: string = "abc"
+ static var _s: string = "def"
endclass
END
- v9.CheckSourceFailure(lines, 'E1371: Abstract must be followed by "def"', 3)
+ v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _s', 4)

- # Use a static abstract method
+ # Duplicate class and object member variable
lines =<< trim END
vim9script
- abstract class A
- abstract static def Foo(): number
+ class C
+ static var val = 10
+ var val = 20
+ def new()
+ enddef
endclass
+ var c = C.new()
+ assert_equal(10, C.val)
+ assert_equal(20, c.val)
END
- v9.CheckSourceFailure(lines, 'E1371: Abstract must be followed by "def"', 3)
+ v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4)

- # Type mismatch between abstract method and concrete method
+ # Duplicate object member variable in a derived class
lines =<< trim END
vim9script
- abstract class A
- abstract def Foo(a: string, b: number): list<number>
+ class A
+ var val = 10
endclass
class B extends A
- def Foo(a: number, b: string): list<string>
- return []
- enddef
+ endclass
+ class C extends B
+ var val = 20
endclass
END
- v9.CheckSourceFailure(lines, 'E1383: Method "Foo": type mismatch, expected func(string, number): list<number> but got func(number, string): list<string>', 9)
+ v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 9)

- # Invoke an abstract method from a def function
+ # Duplicate object protected member variable in a derived class
lines =<< trim END
vim9script
- abstract class A
- abstract def Foo(): list<number>
+ class A
+ var _val = 10
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)
+ class C extends B
+ var _val = 20
+ endclass
END
- v9.CheckSourceSuccess(lines)
+ v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 9)

- # Use a static method in an abstract class
+ # Duplicate object protected member variable in a derived class
lines =<< trim END
vim9script
- abstract class A
- static def Foo(): string
- return 'foo'
- enddef
+ class A
+ var val = 10
+ endclass
+ class B extends A
+ endclass
+ class C extends B
+ var _val = 20
endclass
- assert_equal('foo', A.Foo())
END
- v9.CheckSourceSuccess(lines)
+ v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 9)

- # Invoke method returning a value through the abstract class. See #15432.
+ # Duplicate object member variable in a derived class
lines =<< trim END
vim9script
-
- abstract class A
- abstract def String(): string
+ class A
+ var _val = 10
endclass
-
class B extends A
- def String(): string
- return 'B'
- enddef
endclass
-
- def F(o: A)
- assert_equal('B', o.String())
- enddef
- F(B.new())
+ class C extends B
+ var val = 20
+ endclass
END
- v9.CheckSourceSuccess(lines)
+ v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 9)

- # Invoke abstract method returning a value does not compile
+ # Two member variables with a common prefix
lines =<< trim END
vim9script
-
- abstract class A
- abstract def String(): string
- return 'X'
- enddef
+ class A
+ public static var svar2: number
+ public static var svar: number
endclass
END
- v9.CheckScriptFailure(lines, "E1318: Not a valid command in a class: return 'X'")
+ v9.CheckSourceSuccess(lines)
enddef

-" Test for calling a class method from a subclass
-def Test_class_method_call_from_subclass()
- # class method call from a subclass
+" Test for accessing a protected member outside a class in a def function
+def Test_protected_member_access_outside_class()
+ # try to modify a protected instance variable from a def function
var lines =<< trim END
vim9script
+ class A
+ var _val = 10
+ endclass
+ def T()
+ var a = A.new()
+ a._val = 20
+ enddef
+ T()
+ END
+ v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_val" in class "A"', 2)

+ # access a non-existing protected instance variable from a def function
+ lines =<< trim END
+ vim9script
class A
- static def Foo()
- echo "foo"
- enddef
+ var _val = 10
endclass
+ def T()
+ var a = A.new()
+ a._a = 1
+ enddef
+ T()
+ END
+ v9.CheckSourceFailure(lines, 'E1326: Variable "_a" not found in object "A"', 2)

- class B extends A
- def Bar()
- Foo()
- enddef
+ # try to read a protected class variable from a def function using an instance
+ lines =<< trim END
+ vim9script
+ class A
+ static var _val = 10
endclass
+ def T()
+ var a = A.new()
+ var x = a._val
+ enddef
+ T()
+ END
+ v9.CheckSourceFailure(lines, 'E1375: Class variable "_val" accessible only using class "A"', 2)

- var b = B.new()
- b.Bar()
+ # try to modify a protected class variable from a def function using an
+ # instance
+ lines =<< trim END
+ vim9script
+ class A
+ static var _val = 10
+ endclass
+ def T()
+ var a = A.new()
+ a._val = 3
+ enddef
+ T()
END
- v9.CheckSourceFailure(lines, 'E1384: Class method "Foo" accessible only inside class "A"', 1)
-enddef
+ v9.CheckSourceFailure(lines, 'E1375: Class variable "_val" accessible only using class "A"', 2)

-" Test for calling a class method using an object in a def function context and
-" script context.
-def Test_class_method_call_using_object()
- # script context
- var lines =<< trim END
+ # try to read a protected class variable from a def function using the class
+ lines =<< trim END
vim9script
class A
- static def Foo(): list<string>
- return ['a', 'b']
- enddef
- def Bar()
- assert_equal(['a', 'b'], A.Foo())
- assert_equal(['a', 'b'], Foo())
- enddef
+ static var _val = 10
+ endclass
+ def T()
+ var x = A._val
+ enddef
+ T()
+ END
+ v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_val" in class "A"', 1)
+
+ # try to modify a protected class variable from a def function using the class
+ lines =<< trim END
+ vim9script
+ class A
+ static var _val = 10
endclass
-
def T()
- assert_equal(['a', 'b'], A.Foo())
- var t_a = A.new()
- t_a.Bar()
+ A._val = 3
enddef
-
- assert_equal(['a', 'b'], A.Foo())
- var a = A.new()
- a.Bar()
T()
END
- v9.CheckSourceSuccess(lines)
+ v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_val" in class "A"', 1)

- # script context
+ # initialize a protected class variable using a protected class variable
+ # from another class
lines =<< trim END
vim9script
class A
- static def Foo(): string
- return 'foo'
- enddef
+ static var _aval = 10
+ endclass
+ class B
+ static var _bval = A._aval
endclass
-
- var a = A.new()
- assert_equal('foo', a.Foo())
END
- v9.CheckSourceFailure(lines, 'E1385: Class method "Foo" accessible only using class "A"', 9)
+ v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_aval" in class "A"', 7)

- # def function context
+ # initialize a protected instance variable using a protected class variable
+ # from another class
lines =<< trim END
vim9script
class A
- static def Foo(): string
- return 'foo'
- enddef
+ static var _aval = 10
endclass
+ class B
+ var _bval = A._aval
+ endclass
+ var b = B.new()
+ END
+ v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_aval" in class "A"', 1)
+enddef

+" Test for trying to change a readonly member from a def function
+def Test_readonly_member_change_in_def_func()
+ var lines =<< trim END
+ vim9script
+ class A
+ var val: number
+ endclass
def T()
var a = A.new()
- assert_equal('foo', a.Foo())
+ a.val = 20
enddef
T()
END
- v9.CheckSourceFailure(lines, 'E1385: Class method "Foo" accessible only using class "A"', 2)
+ v9.CheckSourceFailure(lines, 'E1335: Variable "val" in class "A" is not writable', 2)
enddef

-def Test_class_variable()
+" Test for reading and writing a class member from a def function
+def Test_modify_class_member_from_def_function()
var lines =<< trim END
vim9script
-
class A
- public static var val: number = 10
- static def ClassFunc()
- assert_equal(10, val)
- enddef
- def ObjFunc()
- assert_equal(10, val)
- enddef
+ var var1: number = 10
+ public static var var2: list<number> = [1, 2]
+ public static var var3: dict<number> = {a: 1, b: 2}
+ static var _priv_var4: number = 40
endclass
+ def T()
+ assert_equal([1, 2], A.var2)
+ assert_equal({a: 1, b: 2}, A.var3)
+ A.var2 = [3, 4]
+ A.var3 = {c: 3, d: 4}
+ assert_equal([3, 4], A.var2)
+ assert_equal({c: 3, d: 4}, A.var3)
+ assert_fails('echo A._priv_var4', 'E1333: Cannot access protected variable "_priv_var4" in class "A"')
+ enddef
+ T()
+ END
+ v9.CheckSourceSuccess(lines)
+enddef

- class B extends A
+" Test for accessing a class member variable using an object
+def Test_class_variable_access_using_object()
+ var lines =<< trim END
+ vim9script
+ class A
+ public static var svar1: list<number> = [1]
+ public static var svar2: list<number> = [2]
endclass

- assert_equal(10, A.val)
- A.ClassFunc()
- var a = A.new()
- a.ObjFunc()
- var b = B.new()
- b.ObjFunc()
+ A.svar1->add(3)
+ A.svar2->add(4)
+ assert_equal([1, 3], A.svar1)
+ assert_equal([2, 4], A.svar2)

- def T1(a1: A)
- a1.ObjFunc()
- A.ClassFunc()
+ def Foo()
+ A.svar1->add(7)
+ A.svar2->add(8)
+ assert_equal([1, 3, 7], A.svar1)
+ assert_equal([2, 4, 8], A.svar2)
enddef
- T1(b)
-
- A.val = 20
- assert_equal(20, A.val)
+ Foo()
END
v9.CheckSourceSuccess(lines)

- # Modifying a parent class variable from a child class method
+ # Cannot read from a class variable using an object in script context
lines =<< trim END
vim9script
-
class A
- static var val: number = 10
+ public var var1: number
+ public static var svar2: list<number> = [1]
endclass

- class B extends A
- static def ClassFunc()
- val = 20
- enddef
- endclass
- B.ClassFunc()
+ var a = A.new()
+ echo a.svar2
END
- v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1)
+ v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 8)

- # Reading a parent class variable from a child class method
+ # Cannot write to a class variable using an object in script context
lines =<< trim END
vim9script
-
class A
- static var val: number = 10
+ public var var1: number
+ public static var svar2: list<number> = [1]
endclass

- class B extends A
- static def ClassFunc()
- var i = val
- enddef
- endclass
- B.ClassFunc()
+ var a = A.new()
+ a.svar2 = [2]
END
- v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1)
+ v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 8)

- # Modifying a parent class variable from a child object method
+ # Cannot read from a class variable using an object in def method context
lines =<< trim END
vim9script
-
class A
- static var val: number = 10
+ public var var1: number
+ public static var svar2: list<number> = [1]
endclass

- class B extends A
- def ObjFunc()
- val = 20
- enddef
- endclass
- var b = B.new()
- b.ObjFunc()
+ def T()
+ var a = A.new()
+ echo a.svar2
+ enddef
+ T()
END
- v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1)
+ v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 2)

- # Reading a parent class variable from a child object method
+ # Cannot write to a class variable using an object in def method context
lines =<< trim END
vim9script
-
class A
- static var val: number = 10
+ public var var1: number
+ public static var svar2: list<number> = [1]
endclass

+ def T()
+ var a = A.new()
+ a.svar2 = [2]
+ enddef
+ T()
+ END
+ v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 2)
+enddef
+
+" Test for abstract methods
+def Test_abstract_method()
+ # Use two abstract methods
+ var lines =<< trim END
+ vim9script
+ abstract class A
+ def M1(): number
+ return 10
+ enddef
+ abstract def M2(): number
+ abstract def M3(): number
+ endclass
class B extends A
- def ObjFunc()
- var i = val
+ def M2(): number
+ return 20
+ enddef
+ def M3(): number
+ return 30
enddef
endclass
var b = B.new()
- b.ObjFunc()
+ assert_equal([10, 20, 30], [b.M1(), b.M2(), b.M3()])
END
- v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1)
+ v9.CheckSourceSuccess(lines)

- # Modifying a class variable using an object at script level
+ # Don't define an abstract method
lines =<< trim END
vim9script
-
- class A
- static var val: number = 10
+ abstract class A
+ abstract def Foo()
endclass
- var a = A.new()
- a.val = 20
- END
- v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 7)
-
- # Reading a class variable using an object at script level
- lines =<< trim END
- vim9script
-
- class A
- static var val: number = 10
+ class B extends A
endclass
- var a = A.new()
- var i = a.val
END
- v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 7)
+ v9.CheckSourceFailure(lines, 'E1373: Abstract method "Foo" is not implemented', 6)

- # Modifying a class variable using an object at function level
+ # Use abstract method in a concrete class
lines =<< trim END
vim9script
-
class A
- static var val: number = 10
+ abstract def Foo()
+ endclass
+ class B extends A
endclass
-
- def T()
- var a = A.new()
- a.val = 20
- enddef
- T()
END
- v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 2)
+ v9.CheckSourceFailure(lines, 'E1372: Abstract method "abstract def Foo()" cannot be defined in a concrete class', 3)

- # Reading a class variable using an object at function level
+ # Use abstract method in an interface
lines =<< trim END
vim9script
-
- class A
- static var val: number = 10
- endclass
- def T()
- var a = A.new()
- var i = a.val
- enddef
- T()
+ interface A
+ abstract def Foo()
+ endinterface
+ class B implements A
+ def Foo()
+ enddef
+ endclass
END
- v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 2)
+ v9.CheckSourceFailure(lines, 'E1404: Abstract cannot be used in an interface', 3)

- # Use old implicit var declaration syntax (without initialization)
+ # Use abstract static method in an interface
lines =<< trim END
vim9script
-
- class A
- static val: number
- endclass
+ interface A
+ abstract static def Foo()
+ enddef
+ endinterface
END
- v9.CheckSourceFailure(lines, 'E1368: Static must be followed by "var" or "def"', 4)
+ v9.CheckSourceFailure(lines, 'E1404: Abstract cannot be used in an interface', 3)

- # Use old implicit var declaration syntax (with initialization)
+ # Use abstract static variable in an interface
lines =<< trim END
vim9script
-
- class A
- static val: number = 10
- endclass
+ interface A
+ abstract static foo: number = 10
+ endinterface
END
- v9.CheckSourceFailure(lines, 'E1368: Static must be followed by "var" or "def"', 4)
+ v9.CheckSourceFailure(lines, 'E1404: Abstract cannot be used in an interface', 3)

- # Use old implicit var declaration syntax (type inferred)
+ # Abbreviate the "abstract" keyword
lines =<< trim END
vim9script
-
class A
- static val = 10
+ abs def Foo()
endclass
END
- v9.CheckSourceFailure(lines, 'E1368: Static must be followed by "var" or "def"', 4)
+ v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: abs def Foo()', 3)

- # Missing ":var" in "var" class variable declaration (without initialization)
+ # Use "abstract" with a member variable
lines =<< trim END
vim9script
-
- class A
- static var: number
+ abstract class A
+ abstract this.val = 10
endclass
END
- v9.CheckSourceFailure(lines, 'E1329: Invalid class variable declaration: static var: number', 4)
+ v9.CheckSourceFailure(lines, 'E1371: Abstract must be followed by "def"', 3)

- # Missing ":var" in "var" class variable declaration (with initialization)
+ # Use a static abstract method
lines =<< trim END
vim9script
-
- class A
- static var: number = 10
+ abstract class A
+ abstract static def Foo(): number
endclass
END
- v9.CheckSourceFailure(lines, 'E1329: Invalid class variable declaration: static var: number = 10', 4)
+ v9.CheckSourceFailure(lines, 'E1371: Abstract must be followed by "def"', 3)

- # Missing ":var" in "var" class variable declaration (type inferred)
+ # Type mismatch between abstract method and concrete method
lines =<< trim END
vim9script
-
- class A
- static var = 10
+ abstract class A
+ abstract def Foo(a: string, b: number): list<number>
+ endclass
+ class B extends A
+ def Foo(a: number, b: string): list<string>
+ return []
+ enddef
endclass
END
- v9.CheckSourceFailure(lines, 'E1329: Invalid class variable declaration: static var = 10', 4)
-
-enddef
+ v9.CheckSourceFailure(lines, 'E1383: Method "Foo": type mismatch, expected func(string, number): list<number> but got func(number, string): list<string>', 9)

-" Test for using a duplicate class method and class variable in a child class
-def Test_dup_class_member()
- # duplicate class variable, class method and overridden object method
- var lines =<< trim END
+ # Invoke an abstract method from a def function
+ lines =<< trim END
vim9script
- class A
- static var sval = 100
- static def Check()
- assert_equal(100, sval)
- enddef
- def GetVal(): number
- return sval
- enddef
+ abstract class A
+ abstract def Foo(): list<number>
endclass
-
class B extends A
- static var sval = 200
- static def Check()
- assert_equal(200, sval)
- enddef
- def GetVal(): number
- return sval
+ def Foo(): list<number>
+ return [3, 5]
enddef
endclass
-
- def T1(aa: A): number
- return aa.GetVal()
- enddef
-
- def T2(bb: B): number
- return bb.GetVal()
+ def Bar(c: B)
+ assert_equal([3, 5], c.Foo())
enddef
-
- assert_equal(100, A.sval)
- assert_equal(200, B.sval)
- var a = A.new()
- assert_equal(100, a.GetVal())
var b = B.new()
- assert_equal(200, b.GetVal())
- assert_equal(200, T1(b))
- assert_equal(200, T2(b))
+ Bar(b)
END
v9.CheckSourceSuccess(lines)

- # duplicate class variable and class method
+ # Use a static method in an abstract class
lines =<< trim END
vim9script
- class A
- static var sval = 100
- static def Check()
- assert_equal(100, sval)
- enddef
- def GetVal(): number
- return sval
+ abstract class A
+ static def Foo(): string
+ return 'foo'
enddef
endclass
+ assert_equal('foo', A.Foo())
+ END
+ v9.CheckSourceSuccess(lines)

- class B extends A
- static var sval = 200
- static def Check()
- assert_equal(200, sval)
- enddef
+ # Invoke method returning a value through the abstract class. See #15432.
+ lines =<< trim END
+ vim9script
+
+ abstract class A
+ abstract def String(): string
endclass

- def T1(aa: A): number
- return aa.GetVal()
- enddef
+ class B extends A
+ def String(): string
+ return 'B'
+ enddef
+ endclass

- def T2(bb: B): number
- return bb.GetVal()
+ def F(o: A)
+ assert_equal('B', o.String())
enddef
-
- assert_equal(100, A.sval)
- assert_equal(200, B.sval)
- var a = A.new()
- assert_equal(100, a.GetVal())
- var b = B.new()
- assert_equal(100, b.GetVal())
- assert_equal(100, T1(b))
- assert_equal(100, T2(b))
+ F(B.new())
END
v9.CheckSourceSuccess(lines)
-enddef
-
-" Test for calling an instance method using the class
-def Test_instance_method_call_using_class()
- # Invoke an object method using a class in script context
- var lines =<< trim END
- vim9script
- class A
- def Foo()
- echo "foo"
- enddef
- endclass
- A.Foo()
- END
- v9.CheckSourceFailure(lines, 'E1386: Object method "Foo" accessible only using class "A" object', 7)

- # Invoke an object method using a class in def function context
+ # Invoke abstract method returning a value does not compile
lines =<< trim END
vim9script
- class A
- def Foo()
- echo "foo"
+
+ abstract class A
+ abstract def String(): string
+ return 'X'
enddef
endclass
- def T()
- A.Foo()
- enddef
- T()
END
- v9.CheckSourceFailure(lines, 'E1386: Object method "Foo" accessible only using class "A" object', 1)
+ v9.CheckScriptFailure(lines, "E1318: Not a valid command in a class: return 'X'")
enddef

-" Test for duplicate class method and instance method
-def Test_dup_classmethod_objmethod()
- # Duplicate instance method
+" Test for calling a class method from a subclass
+def Test_class_method_call_from_subclass()
+ # class method call from a subclass
var lines =<< trim END
vim9script
+
class A
static def Foo()
- enddef
- def Foo()
+ echo "foo"
enddef
endclass
- END
- v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 6)

- # Duplicate protected instance method
- lines =<< trim END
- vim9script
- class A
- static def Foo()
- enddef
- def _Foo()
+ class B extends A
+ def Bar()
+ Foo()
enddef
endclass
+
+ var b = B.new()
+ b.Bar()
END
- v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 6)
+ v9.CheckSourceFailure(lines, 'E1384: Class method "Foo" accessible only inside class "A"', 1)
+enddef

- # Duplicate class method
- lines =<< trim END
+" Test for calling a class method using an object in a def function context and
+" script context.
+def Test_class_method_call_using_object()
+ # script context
+ var lines =<< trim END
vim9script
class A
- def Foo()
+ static def Foo(): list<string>
+ return ['a', 'b']
enddef
- static def Foo()
+ def Bar()
+ assert_equal(['a', 'b'], A.Foo())
+ assert_equal(['a', 'b'], Foo())
enddef
endclass
+
+ def T()
+ assert_equal(['a', 'b'], A.Foo())
+ var t_a = A.new()
+ t_a.Bar()
+ enddef
+
+ assert_equal(['a', 'b'], A.Foo())
+ var a = A.new()
+ a.Bar()
+ T()
END
- v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 6)
+ v9.CheckSourceSuccess(lines)

- # Duplicate protected class method
+ # script context
lines =<< trim END
vim9script
class A
- def Foo()
- enddef
- static def _Foo()
+ static def Foo(): string
+ return 'foo'
enddef
endclass
+
+ var a = A.new()
+ assert_equal('foo', a.Foo())
END
- v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 6)
+ v9.CheckSourceFailure(lines, 'E1385: Class method "Foo" accessible only using class "A"', 9)

- # Duplicate protected class and object method
+ # def function context
lines =<< trim END
vim9script
class A
- def _Foo()
- enddef
- static def _Foo()
+ static def Foo(): string
+ return 'foo'
enddef
endclass
+
+ def T()
+ var a = A.new()
+ assert_equal('foo', a.Foo())
+ enddef
+ T()
END
- v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 6)
+ v9.CheckSourceFailure(lines, 'E1385: Class method "Foo" accessible only using class "A"', 2)
enddef

-" Test for an instance method access level comparison with parent instance
-" methods.
-def Test_instance_method_access_level()
- # protected method in subclass
+def Test_class_variable()
var lines =<< trim END
vim9script
+
class A
- def Foo()
+ public static var val: number = 10
+ static def ClassFunc()
+ assert_equal(10, val)
+ enddef
+ def ObjFunc()
+ assert_equal(10, val)
enddef
endclass
+
class B extends A
endclass
- class C extends B
- def _Foo()
- enddef
- endclass
+
+ assert_equal(10, A.val)
+ A.ClassFunc()
+ var a = A.new()
+ a.ObjFunc()
+ var b = B.new()
+ b.ObjFunc()
+
+ def T1(a1: A)
+ a1.ObjFunc()
+ A.ClassFunc()
+ enddef
+ T1(b)
+
+ A.val = 20
+ assert_equal(20, A.val)
END
- v9.CheckSourceFailure(lines, 'E1377: Access level of method "_Foo" is different in class "A"', 11)
+ v9.CheckSourceSuccess(lines)

- # Public method in subclass
+ # Modifying a parent class variable from a child class method
lines =<< trim END
vim9script
+
class A
- def _Foo()
- enddef
+ static var val: number = 10
endclass
+
class B extends A
- endclass
- class C extends B
- def Foo()
+ static def ClassFunc()
+ val = 20
enddef
endclass
+ B.ClassFunc()
END
- v9.CheckSourceFailure(lines, 'E1377: Access level of method "Foo" is different in class "A"', 11)
-enddef
+ v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1)

-def Test_extend_empty_class()
- var lines =<< trim END
+ # Reading a parent class variable from a child class method
+ lines =<< trim END
vim9script
+
class A
+ static var val: number = 10
endclass
+
class B extends A
- endclass
- class C extends B
- public static var rw_class_var = 1
- public var rw_obj_var = 2
- static def ClassMethod(): number
- return 3
- enddef
- def ObjMethod(): number
- return 4
+ static def ClassFunc()
+ var i = val
enddef
endclass
- assert_equal(1, C.rw_class_var)
- assert_equal(3, C.ClassMethod())
- var c = C.new()
- assert_equal(2, c.rw_obj_var)
- assert_equal(4, c.ObjMethod())
- END
- v9.CheckSourceSuccess(lines)
-enddef
-
-" A interface cannot have a static variable or a static method or a protected
-" variable or a protected method or a public variable
-def Test_interface_with_unsupported_members()
- var lines =<< trim END
- vim9script
- interface A
- static var num: number
- endinterface
+ B.ClassFunc()
END
- v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3)
+ v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1)

+ # Modifying a parent class variable from a child object method
lines =<< trim END
vim9script
- interface A
- static var _num: number
- endinterface
- END
- v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3)

- lines =<< trim END
- vim9script
- interface A
- public static var num: number
- endinterface
- END
- v9.CheckSourceFailure(lines, 'E1387: public variable not supported in an interface', 3)
+ class A
+ static var val: number = 10
+ endclass

- lines =<< trim END
- vim9script
- interface A
- public static var num: number
- endinterface
+ class B extends A
+ def ObjFunc()
+ val = 20
+ enddef
+ endclass
+ var b = B.new()
+ b.ObjFunc()
END
- v9.CheckSourceFailure(lines, 'E1387: public variable not supported in an interface', 3)
+ v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1)

+ # Reading a parent class variable from a child object method
lines =<< trim END
vim9script
- interface A
- static var _num: number
- endinterface
- END
- v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3)

- lines =<< trim END
- vim9script
- interface A
- static def Foo(d: dict<any>): list<string>
- endinterface
+ class A
+ static var val: number = 10
+ endclass
+
+ class B extends A
+ def ObjFunc()
+ var i = val
+ enddef
+ endclass
+ var b = B.new()
+ b.ObjFunc()
END
- v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3)
+ v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1)

+ # Modifying a class variable using an object at script level
lines =<< trim END
vim9script
- interface A
- static def _Foo(d: dict<any>): list<string>
- endinterface
+
+ class A
+ static var val: number = 10
+ endclass
+ var a = A.new()
+ a.val = 20
END
- v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3)
+ v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 7)

+ # Reading a class variable using an object at script level
lines =<< trim END
vim9script
- interface A
- var _Foo: list<string>
- endinterface
+
+ class A
+ static var val: number = 10
+ endclass
+ var a = A.new()
+ var i = a.val
END
- v9.CheckSourceFailure(lines, 'E1379: Protected variable not supported in an interface', 3)
+ v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 7)

+ # Modifying a class variable using an object at function level
lines =<< trim END
vim9script
- interface A
- def _Foo(d: dict<any>): list<string>
- endinterface
- END
- v9.CheckSourceFailure(lines, 'E1380: Protected method not supported in an interface', 3)
-enddef

-" Test for extending an interface
-def Test_extend_interface()
- var lines =<< trim END
- vim9script
- interface A
- var var1: list<string>
- def Foo()
- endinterface
- interface B extends A
- var var2: dict<string>
- def Bar()
- endinterface
- class C implements A, B
- var var1 = [1, 2]
- def Foo()
- enddef
- var var2 = {a: '1'}
- def Bar()
- enddef
+ class A
+ static var val: number = 10
endclass
+
+ def T()
+ var a = A.new()
+ a.val = 20
+ enddef
+ T()
END
- v9.CheckSourceSuccess(lines)
+ v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 2)

- # extending empty interface
+ # Reading a class variable using an object at function level
lines =<< trim END
vim9script
- interface A
- endinterface
- interface B extends A
- endinterface
- class C implements B
+
+ class A
+ static var val: number = 10
endclass
+ def T()
+ var a = A.new()
+ var i = a.val
+ enddef
+ T()
END
- v9.CheckSourceSuccess(lines)
+ v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 2)

+ # Use old implicit var declaration syntax (without initialization)
lines =<< trim END
vim9script
- interface A
- def Foo()
- endinterface
- interface B extends A
- var var2: dict<string>
- endinterface
- class C implements A, B
- var var2 = {a: '1'}
+
+ class A
+ static val: number
endclass
END
- v9.CheckSourceFailure(lines, 'E1349: Method "Foo" of interface "A" is not implemented', 10)
+ v9.CheckSourceFailure(lines, 'E1368: Static must be followed by "var" or "def"', 4)

+ # Use old implicit var declaration syntax (with initialization)
lines =<< trim END
vim9script
- interface A
- def Foo()
- endinterface
- interface B extends A
- var var2: dict<string>
- endinterface
- class C implements A, B
- def Foo()
- enddef
+
+ class A
+ static val: number = 10
endclass
END
- v9.CheckSourceFailure(lines, 'E1348: Variable "var2" of interface "B" is not implemented', 11)
+ v9.CheckSourceFailure(lines, 'E1368: Static must be followed by "var" or "def"', 4)

- # interface cannot extend a class
+ # Use old implicit var declaration syntax (type inferred)
lines =<< trim END
vim9script
+
class A
+ static val = 10
endclass
- interface B extends A
- endinterface
END
- v9.CheckSourceFailure(lines, 'E1354: Cannot extend A', 5)
+ v9.CheckSourceFailure(lines, 'E1368: Static must be followed by "var" or "def"', 4)

- # class cannot extend an interface
+ # Missing ":var" in "var" class variable declaration (without initialization)
lines =<< trim END
vim9script
- interface A
- endinterface
- class B extends A
+
+ class A
+ static var: number
endclass
END
- v9.CheckSourceFailure(lines, 'E1354: Cannot extend A', 5)
+ v9.CheckSourceFailure(lines, 'E1329: Invalid class variable declaration: static var: number', 4)

- # interface cannot implement another interface
+ # Missing ":var" in "var" class variable declaration (with initialization)
lines =<< trim END
vim9script
- interface A
- endinterface
- interface B implements A
- endinterface
+
+ class A
+ static var: number = 10
+ endclass
END
- v9.CheckSourceFailure(lines, 'E1381: Interface cannot use "implements"', 4)
+ v9.CheckSourceFailure(lines, 'E1329: Invalid class variable declaration: static var: number = 10', 4)

- # interface cannot extend multiple interfaces
+ # Missing ":var" in "var" class variable declaration (type inferred)
lines =<< trim END
vim9script
- interface A
- endinterface
- interface B
- endinterface
- interface C extends A, B
- endinterface
+
+ class A
+ static var = 10
+ endclass
END
- v9.CheckSourceFailure(lines, 'E1315: White space required after name: A, B', 6)
+ v9.CheckSourceFailure(lines, 'E1329: Invalid class variable declaration: static var = 10', 4)

- # Variable type in an extended interface is of different type
- lines =<< trim END
- vim9script
- interface A
- var val1: number
- endinterface
- interface B extends A
- var val2: string
- endinterface
- interface C extends B
- var val1: string
- var val2: number
- endinterface
- END
- v9.CheckSourceFailure(lines, 'E1382: Variable "val1": type mismatch, expected number but got string', 11)
enddef

-" Test for a child class implementing an interface when some of the methods are
-" defined in the parent class.
-def Test_child_class_implements_interface()
+" Test for using a duplicate class method and class variable in a child class
+def Test_dup_class_member()
+ # duplicate class variable, class method and overridden object method
var lines =<< trim END
vim9script
-
- interface Intf
- def F1(): list<list<number>>
- def F2(): list<list<number>>
- def F3(): list<list<number>>
- var var1: list<dict<number>>
- var var2: list<dict<number>>
- var var3: list<dict<number>>
- endinterface
-
class A
- def A1()
+ static var sval = 100
+ static def Check()
+ assert_equal(100, sval)
enddef
- def F3(): list<list<number>>
- return [[3]]
+ def GetVal(): number
+ return sval
enddef
- var v1: list<list<number>> = [[0]]
- var var3 = [{c: 30}]
endclass

class B extends A
- def B1()
+ static var sval = 200
+ static def Check()
+ assert_equal(200, sval)
enddef
- def F2(): list<list<number>>
- return [[2]]
+ def GetVal(): number
+ return sval
enddef
- var v2: list<list<number>> = [[0]]
- var var2 = [{b: 20}]
endclass

- class C extends B implements Intf
- def C1()
- enddef
- def F1(): list<list<number>>
- return [[1]]
- enddef
- var v3: list<list<number>> = [[0]]
- var var1 = [{a: 10}]
- endclass
+ def T1(aa: A): number
+ return aa.GetVal()
+ enddef

- def T(if: Intf)
- assert_equal([[1]], if.F1())
- assert_equal([[2]], if.F2())
- assert_equal([[3]], if.F3())
- assert_equal([{a: 10}], if.var1)
- assert_equal([{b: 20}], if.var2)
- assert_equal([{c: 30}], if.var3)
+ def T2(bb: B): number
+ return bb.GetVal()
enddef

- var c = C.new()
- T(c)
- assert_equal([[1]], c.F1())
- assert_equal([[2]], c.F2())
- assert_equal([[3]], c.F3())
- assert_equal([{a: 10}], c.var1)
- assert_equal([{b: 20}], c.var2)
- assert_equal([{c: 30}], c.var3)
+ assert_equal(100, A.sval)
+ assert_equal(200, B.sval)
+ var a = A.new()
+ assert_equal(100, a.GetVal())
+ var b = B.new()
+ assert_equal(200, b.GetVal())
+ assert_equal(200, T1(b))
+ assert_equal(200, T2(b))
END
v9.CheckSourceSuccess(lines)

- # One of the interface methods is not found
+ # duplicate class variable and class method
lines =<< trim END
vim9script
-
- interface Intf
- def F1()
- def F2()
- def F3()
- endinterface
-
class A
- def A1()
+ static var sval = 100
+ static def Check()
+ assert_equal(100, sval)
+ enddef
+ def GetVal(): number
+ return sval
enddef
endclass

class B extends A
- def B1()
- enddef
- def F2()
+ static var sval = 200
+ static def Check()
+ assert_equal(200, sval)
enddef
endclass

- class C extends B implements Intf
- def C1()
- enddef
- def F1()
+ def T1(aa: A): number
+ return aa.GetVal()
+ enddef
+
+ def T2(bb: B): number
+ return bb.GetVal()
+ enddef
+
+ assert_equal(100, A.sval)
+ assert_equal(200, B.sval)
+ var a = A.new()
+ assert_equal(100, a.GetVal())
+ var b = B.new()
+ assert_equal(100, b.GetVal())
+ assert_equal(100, T1(b))
+ assert_equal(100, T2(b))
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for calling an instance method using the class
+def Test_instance_method_call_using_class()
+ # Invoke an object method using a class in script context
+ var lines =<< trim END
+ vim9script
+ class A
+ def Foo()
+ echo "foo"
enddef
endclass
+ A.Foo()
END
- v9.CheckSourceFailure(lines, 'E1349: Method "F3" of interface "Intf" is not implemented', 26)
+ v9.CheckSourceFailure(lines, 'E1386: Object method "Foo" accessible only using class "A" object', 7)

- # One of the interface methods is of different type
+ # Invoke an object method using a class in def function context
lines =<< trim END
vim9script
-
- interface Intf
- def F1()
- def F2()
- def F3()
- endinterface
-
class A
- def F3(): number
- return 0
- enddef
- def A1()
+ def Foo()
+ echo "foo"
enddef
endclass
+ def T()
+ A.Foo()
+ enddef
+ T()
+ END
+ v9.CheckSourceFailure(lines, 'E1386: Object method "Foo" accessible only using class "A" object', 1)
+enddef

- class B extends A
- def B1()
+" Test for duplicate class method and instance method
+def Test_dup_classmethod_objmethod()
+ # Duplicate instance method
+ var lines =<< trim END
+ vim9script
+ class A
+ static def Foo()
enddef
- def F2()
+ def Foo()
enddef
endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 6)

- class C extends B implements Intf
- def C1()
+ # Duplicate protected instance method
+ lines =<< trim END
+ vim9script
+ class A
+ static def Foo()
enddef
- def F1()
+ def _Foo()
enddef
endclass
END
- v9.CheckSourceFailure(lines, 'E1383: Method "F3": type mismatch, expected func() but got func(): number', 29)
+ v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 6)

- # One of the interface variables is not present
+ # Duplicate class method
lines =<< trim END
vim9script
-
- interface Intf
- var var1: list<dict<number>>
- var var2: list<dict<number>>
- var var3: list<dict<number>>
- endinterface
-
class A
- var v1: list<list<number>> = [[0]]
- endclass
-
- class B extends A
- var v2: list<list<number>> = [[0]]
- var var2 = [{b: 20}]
- endclass
-
- class C extends B implements Intf
- var v3: list<list<number>> = [[0]]
- var var1 = [{a: 10}]
+ def Foo()
+ enddef
+ static def Foo()
+ enddef
endclass
END
- v9.CheckSourceFailure(lines, 'E1348: Variable "var3" of interface "Intf" is not implemented', 21)
+ v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 6)

- # One of the interface variables is of different type
+ # Duplicate protected class method
lines =<< trim END
vim9script
-
- interface Intf
- var var1: list<dict<number>>
- var var2: list<dict<number>>
- var var3: list<dict<number>>
- endinterface
-
class A
- var v1: list<list<number>> = [[0]]
- var var3: list<dict<string>>
- endclass
-
- class B extends A
- var v2: list<list<number>> = [[0]]
- var var2 = [{b: 20}]
- endclass
-
- class C extends B implements Intf
- var v3: list<list<number>> = [[0]]
- var var1 = [{a: 10}]
+ def Foo()
+ enddef
+ static def _Foo()
+ enddef
endclass
END
- v9.CheckSourceFailure(lines, 'E1382: Variable "var3": type mismatch, expected list<dict<number>> but got list<dict<string>>', 22)
-enddef
+ v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 6)

-" Test for extending an interface with duplicate variables and methods
-def Test_interface_extends_with_dup_members()
- var lines =<< trim END
+ # Duplicate protected class and object method
+ lines =<< trim END
vim9script
- interface A
- var n1: number
- def Foo1(): number
- endinterface
- interface B extends A
- var n2: number
- var n1: number
- def Foo2(): number
- def Foo1(): number
- endinterface
- class C implements B
- var n1 = 10
- var n2 = 20
- def Foo1(): number
- return 30
+ class A
+ def _Foo()
enddef
- def Foo2(): number
- return 40
+ static def _Foo()
enddef
endclass
- def T1(a: A)
- assert_equal(10, a.n1)
- assert_equal(30, a.Foo1())
- enddef
- def T2(b: B)
- assert_equal(10, b.n1)
- assert_equal(20, b.n2)
- assert_equal(30, b.Foo1())
- assert_equal(40, b.Foo2())
- enddef
- var c = C.new()
- T1(c)
- T2(c)
END
- v9.CheckSourceSuccess(lines)
+ v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 6)
enddef

-" Test for implementing an interface with different ordering for the interface
-" member variables.
-def Test_implement_interface_with_different_variable_order()
+" Test for an instance method access level comparison with parent instance
+" methods.
+def Test_instance_method_access_level()
+ # protected method in subclass
var lines =<< trim END
vim9script
-
- interface IX
- var F: func(): string
- endinterface
-
- class X implements IX
- var x: number
- var F: func(): string = () => 'ok'
+ class A
+ def Foo()
+ enddef
endclass
-
- def Foo(ix: IX): string
- return ix.F()
- enddef
-
- var x0 = X.new(0)
- assert_equal('ok', Foo(x0))
- END
- v9.CheckSourceSuccess(lines)
-enddef
-
-" Test for inheriting interfaces from an imported super class
-def Test_interface_inheritance_with_imported_super()
- var lines =<< trim END
- vim9script
-
- export interface I
- def F(): string
- endinterface
-
- export class A implements I
- def F(): string
- return 'A'
+ class B extends A
+ endclass
+ class C extends B
+ def _Foo()
enddef
endclass
END
- writefile(lines, 'Xinheritintfimportclass.vim', 'D')
+ v9.CheckSourceFailure(lines, 'E1377: Access level of method "_Foo" is different in class "A"', 11)

+ # Public method in subclass
lines =<< trim END
vim9script
-
- import './Xinheritintfimportclass.vim' as i_imp
-
- # class C extends i_imp.A
- class C extends i_imp.A implements i_imp.I
- def F(): string
- return 'C'
+ class A
+ def _Foo()
+ enddef
+ endclass
+ class B extends A
+ endclass
+ class C extends B
+ def Foo()
enddef
endclass
-
- def TestI(i: i_imp.I): string
- return i.F()
- enddef
-
- assert_equal('C', TestI(C.new()))
END
- v9.CheckSourceSuccess(lines)
+ v9.CheckSourceFailure(lines, 'E1377: Access level of method "Foo" is different in class "A"', 11)
enddef

-" Test for using "any" type for a variable in a sub-class while it has a
-" concrete type in the interface
-def Test_implements_using_var_type_any()
+def Test_extend_empty_class()
var lines =<< trim END
vim9script
- interface A
- var val: list<dict<string>>
- endinterface
- class B implements A
- var val = [{a: '1'}, {b: '2'}]
+ class A
endclass
- var b = B.new()
- assert_equal([{a: '1'}, {b: '2'}], b.val)
- END
- v9.CheckSourceSuccess(lines)
-
- # initialize instance variable using a different type
- lines =<< trim END
- vim9script
- interface A
- var val: list<dict<string>>
- endinterface
- class B implements A
- var val = {a: 1, b: 2}
+ class B extends A
endclass
- var b = B.new()
+ class C extends B
+ public static var rw_class_var = 1
+ public var rw_obj_var = 2
+ static def ClassMethod(): number
+ return 3
+ enddef
+ def ObjMethod(): number
+ return 4
+ enddef
+ endclass
+ assert_equal(1, C.rw_class_var)
+ assert_equal(3, C.ClassMethod())
+ var c = C.new()
+ assert_equal(2, c.rw_obj_var)
+ assert_equal(4, c.ObjMethod())
END
- v9.CheckSourceFailure(lines, 'E1382: Variable "val": type mismatch, expected list<dict<string>> but got dict<number>', 1)
+ v9.CheckSourceSuccess(lines)
enddef

" Test for assigning to a member variable in a nested class
@@ -13271,18 +11832,4 @@ func Test_class_selfref_gc()
call v9.CheckSourceSuccess(lines)
endfunc

-" Test for defining an interface in a function
-def Test_interface_defined_in_function()
- var lines =<< trim END
- vim9script
- def Fn()
- var x = 1
- interface Foo
- endinterface
- enddef
- defcompile
- END
- v9.CheckScriptFailure(lines, 'E1436: Interface can only be used in a script', 2)
-enddef
-
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/testdir/test_vim9_interface.vim b/src/testdir/test_vim9_interface.vim
new file mode 100644
index 000000000..742b871f1
--- /dev/null
+++ b/src/testdir/test_vim9_interface.vim
@@ -0,0 +1,1459 @@
+" Tests for Vim9 interface
+
+import './util/vim9.vim' as v9
+
+" Tests for basic interface declaration and errors
+def Test_interface_basics()
+ var lines =<< trim END
+ vim9script
+ interface Something
+ var ro_var: list<number>
+ def GetCount(): number
+ endinterface
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ interface SomethingWrong
+ static var count = 7
+ endinterface
+ END
+ v9.CheckSourceFailure(lines, 'E1342: Interface can only be defined in Vim9 script', 1)
+
+ lines =<< trim END
+ vim9script
+
+ interface Some
+ var value: number
+ def Method(value: number)
+ endinterface
+ END
+ # The argument name and the object member name are the same, but this is not a
+ # problem because object members are always accessed with the "this." prefix.
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ vim9script
+ interface somethingWrong
+ static var count = 7
+ endinterface
+ END
+ v9.CheckSourceFailure(lines, 'E1343: Interface name must start with an uppercase letter: somethingWrong', 2)
+
+ lines =<< trim END
+ vim9script
+ interface SomethingWrong
+ var value: string
+ var count = 7
+ def GetCount(): number
+ endinterface
+ END
+ v9.CheckSourceFailure(lines, 'E1344: Cannot initialize a variable in an interface', 4)
+
+ lines =<< trim END
+ vim9script
+ interface SomethingWrong
+ var value: string
+ var count: number
+ def GetCount(): number
+ return 5
+ enddef
+ endinterface
+ END
+ v9.CheckSourceFailure(lines, 'E1345: Not a valid command in an interface: return 5', 6)
+
+ # Test for "interface" cannot be abbreviated
+ lines =<< trim END
+ vim9script
+ inte Something
+ endinterface
+ END
+ v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: inte Something', 2)
+
+ # Test for "endinterface" cannot be abbreviated
+ lines =<< trim END
+ vim9script
+ interface Something
+ endin
+ END
+ v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: endin', 3)
+
+ # Additional commands after "interface name"
+ lines =<< trim END
+ vim9script
+ interface Something | var x = 10 | var y = 20
+ endinterface
+ END
+ v9.CheckSourceFailure(lines, "E488: Trailing characters: | var x = 10", 2)
+
+ lines =<< trim END
+ vim9script
+ export interface EnterExit
+ def Enter(): void
+ def Exit(): void
+ endinterface
+ END
+ writefile(lines, 'XdefIntf.vim', 'D')
+
+ lines =<< trim END
+ vim9script
+ import './XdefIntf.vim' as defIntf
+ export def With(ee: defIntf.EnterExit, F: func)
+ ee.Enter()
+ try
+ F()
+ finally
+ ee.Exit()
+ endtry
+ enddef
+ END
+ v9.CheckScriptSuccess(lines)
+
+ var imported =<< trim END
+ vim9script
+ export abstract class EnterExit
+ def Enter(): void
+ enddef
+ def Exit(): void
+ enddef
+ endclass
+ END
+ writefile(imported, 'XdefIntf2.vim', 'D')
+
+ lines[1] = " import './XdefIntf2.vim' as defIntf"
+ v9.CheckScriptSuccess(lines)
+enddef
+
+def Test_class_interface_wrong_end()
+ var lines =<< trim END
+ vim9script
+ abstract class SomeName
+ var member = 'text'
+ endinterface
+ END
+ v9.CheckSourceFailure(lines, 'E476: Invalid command: endinterface, expected endclass', 4)
+
+ lines =<< trim END
+ vim9script
+ export interface AnotherName
+ var member: string
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E476: Invalid command: endclass, expected endinterface', 4)
+enddef
+
+" Test for using string() with an interface
+def Test_interface_to_string()
+ var lines =<< trim END
+ vim9script
+ interface Intf
+ def Method(nr: number)
+ endinterface
+ assert_equal("interface Intf", string(Intf))
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+def Test_class_implements_interface()
+ var lines =<< trim END
+ vim9script
+
+ interface Some
+ var count: number
+ def Method(nr: number)
+ endinterface
+
+ class SomeImpl implements Some
+ var count: number
+ def Method(nr: number)
+ echo nr
+ enddef
+ endclass
+
+ interface Another
+ var member: string
+ endinterface
+
+ class AnotherImpl implements Some, Another
+ var member = 'abc'
+ var count = 20
+ def Method(nr: number)
+ echo nr
+ enddef
+ endclass
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ vim9script
+
+ interface Some
+ var count: number
+ endinterface
+
+ class SomeImpl implements Some implements Some
+ var count: number
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1350: Duplicate "implements"', 7)
+
+ lines =<< trim END
+ vim9script
+
+ interface Some
+ var count: number
+ endinterface
+
+ class SomeImpl implements Some, Some
+ var count: number
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1351: Duplicate interface after "implements": Some', 7)
+
+ lines =<< trim END
+ vim9script
+
+ interface Some
+ var counter: number
+ def Method(nr: number)
+ endinterface
+
+ class SomeImpl implements Some
+ var count: number
+ def Method(nr: number)
+ echo nr
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1348: Variable "counter" of interface "Some" is not implemented', 13)
+
+ lines =<< trim END
+ vim9script
+
+ interface Some
+ var count: number
+ def Methods(nr: number)
+ endinterface
+
+ class SomeImpl implements Some
+ var count: number
+ def Method(nr: number)
+ echo nr
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1349: Method "Methods" of interface "Some" is not implemented', 13)
+
+ # Check different order of members in class and interface works.
+ lines =<< trim END
+ vim9script
+
+ interface Result
+ var label: string
+ var errpos: number
+ endinterface
+
+ # order of members is opposite of interface
+ class Failure implements Result
+ public var lnum: number = 5
+ var errpos: number = 42
+ var label: string = 'label'
+ endclass
+
+ def Test()
+ var result: Result = Failure.new()
+
+ assert_equal('label', result.label)
+ assert_equal(42, result.errpos)
+ enddef
+
+ Test()
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Interface name after "extends" doesn't end in a space or NUL character
+ lines =<< trim END
+ vim9script
+ interface A
+ endinterface
+ class B extends A"
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1315: White space required after name: A"', 4)
+
+ # Trailing characters after a class name
+ lines =<< trim END
+ vim9script
+ class A bbb
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E488: Trailing characters: bbb', 2)
+
+ # using "implements" with a non-existing class
+ lines =<< trim END
+ vim9script
+ class A implements B
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1346: Interface name not found: B', 3)
+
+ # using "implements" with a regular class
+ lines =<< trim END
+ vim9script
+ class A
+ endclass
+ class B implements A
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1347: Not a valid interface: A', 5)
+
+ # using "implements" with a variable
+ lines =<< trim END
+ vim9script
+ var T: number = 10
+ class A implements T
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1347: Not a valid interface: T', 4)
+
+ # implements should be followed by a white space
+ lines =<< trim END
+ vim9script
+ interface A
+ endinterface
+ class B implements A;
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1315: White space required after name: A;', 4)
+
+ lines =<< trim END
+ vim9script
+
+ interface One
+ def IsEven(nr: number): bool
+ endinterface
+ class Two implements One
+ def IsEven(nr: number): string
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1383: Method "IsEven": type mismatch, expected func(number): bool but got func(number): string', 9)
+
+ lines =<< trim END
+ vim9script
+
+ interface One
+ def IsEven(nr: number): bool
+ endinterface
+ class Two implements One
+ def IsEven(nr: bool): bool
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1383: Method "IsEven": type mismatch, expected func(number): bool but got func(bool): bool', 9)
+
+ lines =<< trim END
+ vim9script
+
+ interface One
+ def IsEven(nr: number): bool
+ endinterface
+ class Two implements One
+ def IsEven(nr: number, ...extra: list<number>): bool
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1383: Method "IsEven": type mismatch, expected func(number): bool but got func(number, ...list<number>): bool', 9)
+
+ # access superclass interface members from subclass, mix variable order
+ lines =<< trim END
+ vim9script
+
+ interface I1
+ var mvar1: number
+ var mvar2: number
+ endinterface
+
+ # NOTE: the order is swapped
+ class A implements I1
+ var mvar2: number
+ var mvar1: number
+ public static var svar2: number
+ public static var svar1: number
+ def new()
+ svar1 = 11
+ svar2 = 12
+ this.mvar1 = 111
+ this.mvar2 = 112
+ enddef
+ endclass
+
+ class B extends A
+ def new()
+ this.mvar1 = 121
+ this.mvar2 = 122
+ enddef
+ endclass
+
+ class C extends B
+ def new()
+ this.mvar1 = 131
+ this.mvar2 = 132
+ enddef
+ endclass
+
+ def F2(i: I1): list<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
+ var mvar1: number
+ var mvar2: number
+ endinterface
+
+ interface I2
+ var mvar3: number
+ var mvar4: number
+ endinterface
+
+ class A implements I1
+ public static var svar1: number
+ public static var svar2: number
+ var mvar1: number
+ var mvar2: number
+ def new()
+ svar1 = 11
+ svar2 = 12
+ this.mvar1 = 111
+ this.mvar2 = 112
+ enddef
+ endclass
+
+ class B extends A implements I2
+ static var svar3: number
+ static var svar4: number
+ var mvar3: number
+ var mvar4: number
+ def new()
+ svar3 = 23
+ svar4 = 24
+ this.mvar1 = 121
+ this.mvar2 = 122
+ this.mvar3 = 123
+ this.mvar4 = 124
+ enddef
+ endclass
+
+ class C extends B
+ public static var svar5: number
+ def new()
+ svar5 = 1001
+ this.mvar1 = 131
+ this.mvar2 = 132
+ this.mvar3 = 133
+ this.mvar4 = 134
+ enddef
+ endclass
+
+ def F2(i: I1): list<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)
+
+ # Using two interface names without a space after the ","
+ lines =<< trim END
+ vim9script
+ interface A
+ endinterface
+ interface B
+ endinterface
+ class C implements A,B
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1315: White space required after name: A,B', 6)
+
+ # No interface name after a comma
+ lines =<< trim END
+ vim9script
+ interface A
+ endinterface
+ class B implements A,
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1389: Missing name after implements', 4)
+
+ # No interface name after implements
+ lines =<< trim END
+ vim9script
+ class A implements
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1389: Missing name after implements', 2)
+enddef
+
+def Test_call_interface_method()
+ var lines =<< trim END
+ vim9script
+ interface Base
+ def Enter(): void
+ endinterface
+
+ class Child implements Base
+ def Enter(): void
+ g:result ..= 'child'
+ enddef
+ endclass
+
+ def F(obj: Base)
+ obj.Enter()
+ enddef
+
+ g:result = ''
+ F(Child.new())
+ assert_equal('child', g:result)
+ unlet g:result
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ vim9script
+ class Base
+ def Enter(): void
+ g:result ..= 'base'
+ enddef
+ endclass
+
+ class Child extends Base
+ def Enter(): void
+ g:result ..= 'child'
+ enddef
+ endclass
+
+ def F(obj: Base)
+ obj.Enter()
+ enddef
+
+ g:result = ''
+ F(Child.new())
+ assert_equal('child', g:result)
+ unlet g:result
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # method of interface returns a value
+ lines =<< trim END
+ vim9script
+ interface Base
+ def Enter(): string
+ endinterface
+
+ class Child implements Base
+ def Enter(): string
+ g:result ..= 'child'
+ return "/resource"
+ enddef
+ endclass
+
+ def F(obj: Base)
+ var r = obj.Enter()
+ g:result ..= r
+ enddef
+
+ g:result = ''
+ F(Child.new())
+ assert_equal('child/resource', g:result)
+ unlet g:result
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ vim9script
+ class Base
+ def Enter(): string
+ return null_string
+ enddef
+ endclass
+
+ class Child extends Base
+ def Enter(): string
+ g:result ..= 'child'
+ return "/resource"
+ enddef
+ endclass
+
+ def F(obj: Base)
+ var r = obj.Enter()
+ g:result ..= r
+ enddef
+
+ g:result = ''
+ F(Child.new())
+ assert_equal('child/resource', g:result)
+ unlet g:result
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # No class that implements the interface.
+ lines =<< trim END
+ vim9script
+
+ interface IWithEE
+ def Enter(): any
+ def Exit(): void
+ endinterface
+
+ def With1(ee: IWithEE, F: func)
+ var r = ee.Enter()
+ enddef
+
+ defcompile
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for implementing an imported interface
+def Test_implement_imported_interface()
+ var lines =<< trim END
+ vim9script
+ export interface Imp_Intf1
+ def Fn1(): number
+ endinterface
+ export interface Imp_Intf2
+ def Fn2(): number
+ endinterface
+ END
+ writefile(lines, 'Ximportinterface.vim', 'D')
+
+ lines =<< trim END
+ vim9script
+ import './Ximportinterface.vim' as Xintf
+
+ class A implements Xintf.Imp_Intf1, Xintf.Imp_Intf2
+ def Fn1(): number
+ return 10
+ enddef
+ def Fn2(): number
+ return 20
+ enddef
+ endclass
+ var a = A.new()
+ assert_equal(10, a.Fn1())
+ assert_equal(20, a.Fn2())
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
+" Test for changing the member access of an interface in a implementation class
+def Test_change_interface_member_access()
+ var lines =<< trim END
+ vim9script
+ interface A
+ var val: number
+ endinterface
+ class B implements A
+ public var val = 10
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1367: Access level of variable "val" of interface "A" is different', 7)
+
+ lines =<< trim END
+ vim9script
+ interface A
+ var val: number
+ endinterface
+ class B implements A
+ public var val = 10
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1367: Access level of variable "val" of interface "A" is different', 7)
+enddef
+
+" Test for using a interface method using a child object
+def Test_interface_method_from_child()
+ var lines =<< trim END
+ vim9script
+
+ interface A
+ def Foo(): string
+ endinterface
+
+ class B implements A
+ def Foo(): string
+ return 'foo'
+ enddef
+ endclass
+
+ class C extends B
+ def Bar(): string
+ return 'bar'
+ enddef
+ endclass
+
+ def T1(a: A)
+ assert_equal('foo', a.Foo())
+ enddef
+
+ def T2(b: B)
+ assert_equal('foo', b.Foo())
+ enddef
+
+ var c = C.new()
+ T1(c)
+ T2(c)
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using an interface method using a child object when it is overridden
+" by the child class.
+def Test_interface_overridden_method_from_child()
+ var lines =<< trim END
+ vim9script
+
+ interface A
+ def Foo(): string
+ endinterface
+
+ class B implements A
+ def Foo(): string
+ return 'b-foo'
+ enddef
+ endclass
+
+ class C extends B
+ def Bar(): string
+ return 'bar'
+ enddef
+ def Foo(): string
+ return 'c-foo'
+ enddef
+ endclass
+
+ def T1(a: A)
+ assert_equal('c-foo', a.Foo())
+ enddef
+
+ def T2(b: B)
+ assert_equal('c-foo', b.Foo())
+ enddef
+
+ var c = C.new()
+ T1(c)
+ T2(c)
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for interface inheritance
+def Test_interface_inheritance()
+ var lines =<< trim END
+ vim9script
+
+ interface A
+ def A_Fn(): string
+ endinterface
+
+ interface B
+ def B_Fn(): string
+ endinterface
+
+ interface C
+ def C_Fn(): string
+ endinterface
+
+ class C1 implements A
+ def A_Fn(): string
+ return 'c1-a'
+ enddef
+ endclass
+
+ class C2 extends C1 implements B
+ def B_Fn(): string
+ return 'c2-b'
+ enddef
+ def A_Fn(): string
+ return 'c2-a'
+ enddef
+ endclass
+
+ class C3 extends C2 implements C
+ def C_Fn(): string
+ return 'c3-c'
+ enddef
+ def A_Fn(): string
+ return 'c3-a'
+ enddef
+ def B_Fn(): string
+ return 'c3-b'
+ enddef
+ endclass
+
+ def T1(a: A, s: string)
+ assert_equal(s, a.A_Fn())
+ enddef
+
+ def T2(b: B, s: string)
+ assert_equal(s, b.B_Fn())
+ enddef
+
+ def T3(c: C, s: string)
+ assert_equal(s, c.C_Fn())
+ enddef
+
+ def T4(c1: C1)
+ T1(c1, 'c3-a')
+ enddef
+
+ def T5(c2: C2)
+ T1(c2, 'c3-a')
+ T2(c2, 'c3-b')
+ enddef
+
+ def T6(c3: C3)
+ T1(c3, 'c3-a')
+ T2(c3, 'c3-b')
+ T3(c3, 'c3-c')
+ enddef
+
+ var o3 = C3.new()
+ T4(o3)
+ T5(o3)
+ T6(o3)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Both the parent and child classes implement the same interface
+ lines =<< trim END
+ vim9script
+
+ interface I
+ def Foo(): string
+ endinterface
+
+ class A implements I
+ def Foo(): string
+ return 'A-foo'
+ enddef
+ endclass
+
+ class B implements I
+ def Foo(): string
+ return 'B-foo'
+ enddef
+ endclass
+
+ def Bar(i1: I): string
+ return i1.Foo()
+ enddef
+
+ var b = B.new()
+ assert_equal('B-foo', Bar(b))
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" A interface cannot have a static variable or a static method or a protected
+" variable or a protected method or a public variable
+def Test_interface_with_unsupported_members()
+ var lines =<< trim END
+ vim9script
+ interface A
+ static var num: number
+ endinterface
+ END
+ v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3)
+
+ lines =<< trim END
+ vim9script
+ interface A
+ static var _num: number
+ endinterface
+ END
+ v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3)
+
+ lines =<< trim END
+ vim9script
+ interface A
+ public static var num: number
+ endinterface
+ END
+ v9.CheckSourceFailure(lines, 'E1387: public variable not supported in an interface', 3)
+
+ lines =<< trim END
+ vim9script
+ interface A
+ public static var num: number
+ endinterface
+ END
+ v9.CheckSourceFailure(lines, 'E1387: public variable not supported in an interface', 3)
+
+ lines =<< trim END
+ vim9script
+ interface A
+ static var _num: number
+ endinterface
+ END
+ v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3)
+
+ lines =<< trim END
+ vim9script
+ interface A
+ static def Foo(d: dict<any>): list<string>
+ endinterface
+ END
+ v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3)
+
+ lines =<< trim END
+ vim9script
+ interface A
+ static def _Foo(d: dict<any>): list<string>
+ endinterface
+ END
+ v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3)
+
+ lines =<< trim END
+ vim9script
+ interface A
+ var _Foo: list<string>
+ endinterface
+ END
+ v9.CheckSourceFailure(lines, 'E1379: Protected variable not supported in an interface', 3)
+
+ lines =<< trim END
+ vim9script
+ interface A
+ def _Foo(d: dict<any>): list<string>
+ endinterface
+ END
+ v9.CheckSourceFailure(lines, 'E1380: Protected method not supported in an interface', 3)
+enddef
+
+" Test for extending an interface
+def Test_extend_interface()
+ var lines =<< trim END
+ vim9script
+ interface A
+ var var1: list<string>
+ def Foo()
+ endinterface
+ interface B extends A
+ var var2: dict<string>
+ def Bar()
+ endinterface
+ class C implements A, B
+ var var1 = [1, 2]
+ def Foo()
+ enddef
+ var var2 = {a: '1'}
+ def Bar()
+ enddef
+ endclass
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # extending empty interface
+ lines =<< trim END
+ vim9script
+ interface A
+ endinterface
+ interface B extends A
+ endinterface
+ class C implements B
+ endclass
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ vim9script
+ interface A
+ def Foo()
+ endinterface
+ interface B extends A
+ var var2: dict<string>
+ endinterface
+ class C implements A, B
+ var var2 = {a: '1'}
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1349: Method "Foo" of interface "A" is not implemented', 10)
+
+ lines =<< trim END
+ vim9script
+ interface A
+ def Foo()
+ endinterface
+ interface B extends A
+ var var2: dict<string>
+ endinterface
+ class C implements A, B
+ def Foo()
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1348: Variable "var2" of interface "B" is not implemented', 11)
+
+ # interface cannot extend a class
+ lines =<< trim END
+ vim9script
+ class A
+ endclass
+ interface B extends A
+ endinterface
+ END
+ v9.CheckSourceFailure(lines, 'E1354: Cannot extend A', 5)
+
+ # class cannot extend an interface
+ lines =<< trim END
+ vim9script
+ interface A
+ endinterface
+ class B extends A
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1354: Cannot extend A', 5)
+
+ # interface cannot implement another interface
+ lines =<< trim END
+ vim9script
+ interface A
+ endinterface
+ interface B implements A
+ endinterface
+ END
+ v9.CheckSourceFailure(lines, 'E1381: Interface cannot use "implements"', 4)
+
+ # interface cannot extend multiple interfaces
+ lines =<< trim END
+ vim9script
+ interface A
+ endinterface
+ interface B
+ endinterface
+ interface C extends A, B
+ endinterface
+ END
+ v9.CheckSourceFailure(lines, 'E1315: White space required after name: A, B', 6)
+
+ # Variable type in an extended interface is of different type
+ lines =<< trim END
+ vim9script
+ interface A
+ var val1: number
+ endinterface
+ interface B extends A
+ var val2: string
+ endinterface
+ interface C extends B
+ var val1: string
+ var val2: number
+ endinterface
+ END
+ v9.CheckSourceFailure(lines, 'E1382: Variable "val1": type mismatch, expected number but got string', 11)
+enddef
+
+" Test for a child class implementing an interface when some of the methods are
+" defined in the parent class.
+def Test_child_class_implements_interface()
+ var lines =<< trim END
+ vim9script
+
+ interface Intf
+ def F1(): list<list<number>>
+ def F2(): list<list<number>>
+ def F3(): list<list<number>>
+ var var1: list<dict<number>>
+ var var2: list<dict<number>>
+ var var3: list<dict<number>>
+ endinterface
+
+ class A
+ def A1()
+ enddef
+ def F3(): list<list<number>>
+ return [[3]]
+ enddef
+ var v1: list<list<number>> = [[0]]
+ var var3 = [{c: 30}]
+ endclass
+
+ class B extends A
+ def B1()
+ enddef
+ def F2(): list<list<number>>
+ return [[2]]
+ enddef
+ var v2: list<list<number>> = [[0]]
+ var var2 = [{b: 20}]
+ endclass
+
+ class C extends B implements Intf
+ def C1()
+ enddef
+ def F1(): list<list<number>>
+ return [[1]]
+ enddef
+ var v3: list<list<number>> = [[0]]
+ var var1 = [{a: 10}]
+ endclass
+
+ def T(if: Intf)
+ assert_equal([[1]], if.F1())
+ assert_equal([[2]], if.F2())
+ assert_equal([[3]], if.F3())
+ assert_equal([{a: 10}], if.var1)
+ assert_equal([{b: 20}], if.var2)
+ assert_equal([{c: 30}], if.var3)
+ enddef
+
+ var c = C.new()
+ T(c)
+ assert_equal([[1]], c.F1())
+ assert_equal([[2]], c.F2())
+ assert_equal([[3]], c.F3())
+ assert_equal([{a: 10}], c.var1)
+ assert_equal([{b: 20}], c.var2)
+ assert_equal([{c: 30}], c.var3)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # One of the interface methods is not found
+ lines =<< trim END
+ vim9script
+
+ interface Intf
+ def F1()
+ def F2()
+ def F3()
+ endinterface
+
+ class A
+ def A1()
+ enddef
+ endclass
+
+ class B extends A
+ def B1()
+ enddef
+ def F2()
+ enddef
+ endclass
+
+ class C extends B implements Intf
+ def C1()
+ enddef
+ def F1()
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1349: Method "F3" of interface "Intf" is not implemented', 26)
+
+ # One of the interface methods is of different type
+ lines =<< trim END
+ vim9script
+
+ interface Intf
+ def F1()
+ def F2()
+ def F3()
+ endinterface
+
+ class A
+ def F3(): number
+ return 0
+ enddef
+ def A1()
+ enddef
+ endclass
+
+ class B extends A
+ def B1()
+ enddef
+ def F2()
+ enddef
+ endclass
+
+ class C extends B implements Intf
+ def C1()
+ enddef
+ def F1()
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1383: Method "F3": type mismatch, expected func() but got func(): number', 29)
+
+ # One of the interface variables is not present
+ lines =<< trim END
+ vim9script
+
+ interface Intf
+ var var1: list<dict<number>>
+ var var2: list<dict<number>>
+ var var3: list<dict<number>>
+ endinterface
+
+ class A
+ var v1: list<list<number>> = [[0]]
+ endclass
+
+ class B extends A
+ var v2: list<list<number>> = [[0]]
+ var var2 = [{b: 20}]
+ endclass
+
+ class C extends B implements Intf
+ var v3: list<list<number>> = [[0]]
+ var var1 = [{a: 10}]
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1348: Variable "var3" of interface "Intf" is not implemented', 21)
+
+ # One of the interface variables is of different type
+ lines =<< trim END
+ vim9script
+
+ interface Intf
+ var var1: list<dict<number>>
+ var var2: list<dict<number>>
+ var var3: list<dict<number>>
+ endinterface
+
+ class A
+ var v1: list<list<number>> = [[0]]
+ var var3: list<dict<string>>
+ endclass
+
+ class B extends A
+ var v2: list<list<number>> = [[0]]
+ var var2 = [{b: 20}]
+ endclass
+
+ class C extends B implements Intf
+ var v3: list<list<number>> = [[0]]
+ var var1 = [{a: 10}]
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1382: Variable "var3": type mismatch, expected list<dict<number>> but got list<dict<string>>', 22)
+enddef
+
+" Test for extending an interface with duplicate variables and methods
+def Test_interface_extends_with_dup_members()
+ var lines =<< trim END
+ vim9script
+ interface A
+ var n1: number
+ def Foo1(): number
+ endinterface
+ interface B extends A
+ var n2: number
+ var n1: number
+ def Foo2(): number
+ def Foo1(): number
+ endinterface
+ class C implements B
+ var n1 = 10
+ var n2 = 20
+ def Foo1(): number
+ return 30
+ enddef
+ def Foo2(): number
+ return 40
+ enddef
+ endclass
+ def T1(a: A)
+ assert_equal(10, a.n1)
+ assert_equal(30, a.Foo1())
+ enddef
+ def T2(b: B)
+ assert_equal(10, b.n1)
+ assert_equal(20, b.n2)
+ assert_equal(30, b.Foo1())
+ assert_equal(40, b.Foo2())
+ enddef
+ var c = C.new()
+ T1(c)
+ T2(c)
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for implementing an interface with different ordering for the interface
+" member variables.
+def Test_implement_interface_with_different_variable_order()
+ var lines =<< trim END
+ vim9script
+
+ interface IX
+ var F: func(): string
+ endinterface
+
+ class X implements IX
+ var x: number
+ var F: func(): string = () => 'ok'
+ endclass
+
+ def Foo(ix: IX): string
+ return ix.F()
+ enddef
+
+ var x0 = X.new(0)
+ assert_equal('ok', Foo(x0))
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for inheriting interfaces from an imported super class
+def Test_interface_inheritance_with_imported_super()
+ var lines =<< trim END
+ vim9script
+
+ export interface I
+ def F(): string
+ endinterface
+
+ export class A implements I
+ def F(): string
+ return 'A'
+ enddef
+ endclass
+ END
+ writefile(lines, 'Xinheritintfimportclass.vim', 'D')
+
+ lines =<< trim END
+ vim9script
+
+ import './Xinheritintfimportclass.vim' as i_imp
+
+ # class C extends i_imp.A
+ class C extends i_imp.A implements i_imp.I
+ def F(): string
+ return 'C'
+ enddef
+ endclass
+
+ def TestI(i: i_imp.I): string
+ return i.F()
+ enddef
+
+ assert_equal('C', TestI(C.new()))
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for defining an interface in a function
+def Test_interface_defined_in_function()
+ var lines =<< trim END
+ vim9script
+ def Fn()
+ var x = 1
+ interface Foo
+ endinterface
+ enddef
+ defcompile
+ END
+ v9.CheckScriptFailure(lines, 'E1436: Interface can only be used in a script', 2)
+enddef
+
+" Test for using "any" type for a variable in a sub-class while it has a
+" concrete type in the interface
+def Test_implements_using_var_type_any()
+ var lines =<< trim END
+ vim9script
+ interface A
+ var val: list<dict<string>>
+ endinterface
+ class B implements A
+ var val = [{a: '1'}, {b: '2'}]
+ endclass
+ var b = B.new()
+ assert_equal([{a: '1'}, {b: '2'}], b.val)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # initialize instance variable using a different type
+ lines =<< trim END
+ vim9script
+ interface A
+ var val: list<dict<string>>
+ endinterface
+ class B implements A
+ var val = {a: 1, b: 2}
+ endclass
+ var b = B.new()
+ END
+ v9.CheckSourceFailure(lines, 'E1382: Variable "val": type mismatch, expected list<dict<string>> but got dict<number>', 1)
+enddef
+
+" Test interface garbage collection
+func Test_interface_garbagecollect()
+ let lines =<< trim END
+ vim9script
+
+ interface I
+ var ro_obj_var: number
+
+ def ObjFoo(): number
+ endinterface
+
+ class A implements I
+ static var ro_class_var: number = 10
+ public static var rw_class_var: number = 20
+ static var _priv_class_var: number = 30
+ var ro_obj_var: number = 40
+ var _priv_obj_var: number = 60
+
+ static def _ClassBar(): number
+ return _priv_class_var
+ enddef
+
+ static def ClassFoo(): number
+ return ro_class_var + rw_class_var + A._ClassBar()
+ enddef
+
+ def _ObjBar(): number
+ return this._priv_obj_var
+ enddef
+
+ def ObjFoo(): number
+ return this.ro_obj_var + this._ObjBar()
+ enddef
+ endclass
+
+ assert_equal(60, A.ClassFoo())
+ var o = A.new()
+ assert_equal(100, o.ObjFoo())
+ test_garbagecollect_now()
+ assert_equal(60, A.ClassFoo())
+ assert_equal(100, o.ObjFoo())
+ END
+ call v9.CheckSourceSuccess(lines)
+endfunc
+
+" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/version.c b/src/version.c
index 0aa6ace8a..b98a84e0d 100644
--- a/src/version.c
+++ b/src/version.c
@@ -734,6 +734,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 2029,
/**/
2028,
/**/
Reply all
Reply to author
Forward
0 new messages