From 029f72b1a93430b24b88eb3a72c6114d9f149737 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 10 Apr 2024 22:09:20 +0200 Subject: Adding upstream version 2:9.1.0016. Signed-off-by: Daniel Baumann --- src/testdir/test_vim9_class.vim | 9689 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 9689 insertions(+) create mode 100644 src/testdir/test_vim9_class.vim (limited to 'src/testdir/test_vim9_class.vim') diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim new file mode 100644 index 0000000..b34d2ad --- /dev/null +++ b/src/testdir/test_vim9_class.vim @@ -0,0 +1,9689 @@ +" Test Vim9 classes + +source check.vim +import './vim9.vim' as v9 + +def Test_class_basic() + # Class supported only in "vim9script" + var lines =<< trim END + class NotWorking + endclass + END + v9.CheckSourceFailure(lines, 'E1316: Class can only be defined in Vim9 script', 1) + + # First character in a class name should be capitalized. + lines =<< trim END + vim9script + class notWorking + endclass + END + v9.CheckSourceFailure(lines, 'E1314: Class name must start with an uppercase letter: notWorking', 2) + + # Only alphanumeric characters are supported in a class name + lines =<< trim END + vim9script + class Not@working + endclass + END + v9.CheckSourceFailure(lines, 'E1315: White space required after name: Not@working', 2) + + # Unsupported keyword (instead of class) + lines =<< trim END + vim9script + abstract noclass Something + endclass + END + v9.CheckSourceFailure(lines, 'E475: Invalid argument: noclass Something', 2) + + # Only the complete word "class" should be recognized + lines =<< trim END + vim9script + abstract classy Something + endclass + END + v9.CheckSourceFailure(lines, 'E475: Invalid argument: classy Something', 2) + + # The complete "endclass" should be specified. + lines =<< trim END + vim9script + class Something + endcl + END + v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: endcl', 3) + + # Additional words after "endclass" + lines =<< trim END + vim9script + class Something + endclass school's out + END + v9.CheckSourceFailure(lines, "E488: Trailing characters: school's out", 3) + + # Additional commands after "endclass" + lines =<< trim END + vim9script + class Something + endclass | echo 'done' + END + v9.CheckSourceFailure(lines, "E488: Trailing characters: | echo 'done'", 3) + + # Use old "this." prefixed member variable declaration syntax (without intialization) + lines =<< trim END + vim9script + class Something + this.count: number + endclass + END + v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: this.count: number', 3) + + # Use old "this." prefixed member variable declaration syntax (with intialization) + lines =<< trim END + vim9script + class Something + this.count: number = 42 + endclass + END + v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: this.count: number = 42', 3) + + # Use old "this." prefixed member variable declaration syntax (type inferred) + lines =<< trim END + vim9script + class Something + this.count = 42 + endclass + END + v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: this.count = 42', 3) + + # Use "this" without any member variable name + lines =<< trim END + vim9script + class Something + this + endclass + END + v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: this', 3) + + # Use "this." without any member variable name + lines =<< trim END + vim9script + class Something + this. + endclass + END + v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: this.', 3) + + # Space between "this" and "." + lines =<< trim END + vim9script + class Something + this .count + endclass + END + v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: this .count', 3) + + # Space between "this." and the member variable name + lines =<< trim END + vim9script + class Something + this. count + endclass + END + v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: this. count', 3) + + # Use "that" instead of "this" + lines =<< trim END + vim9script + class Something + var count: number + that.count + endclass + END + v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: that.count', 4) + + # Use "variable" instead of "var" for member variable declaration (without initialization) + lines =<< trim END + vim9script + class Something + variable count: number + endclass + END + v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: variable count: number', 3) + + # Use "variable" instead of "var" for member variable declaration (with initialization) + lines =<< trim END + vim9script + class Something + variable count: number = 42 + endclass + END + v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: variable count: number = 42', 3) + + # Use "variable" instead of "var" for member variable declaration (type inferred) + lines =<< trim END + vim9script + class Something + variable count = 42 + endclass + END + v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: variable count = 42', 3) + + # Use a non-existing member variable in new() + lines =<< trim END + vim9script + class Something + def new() + this.state = 0 + enddef + endclass + var obj = Something.new() + END + v9.CheckSourceFailure(lines, 'E1326: Variable "state" not found in object "Something"', 1) + + # Space before ":" in a member variable declaration + lines =<< trim END + vim9script + class Something + var count : number + endclass + END + v9.CheckSourceFailure(lines, 'E1059: No white space allowed before colon: count : number', 3) + + # No space after ":" in a member variable declaration + lines =<< trim END + vim9script + class Something + var count:number + endclass + END + v9.CheckSourceFailure(lines, "E1069: White space required after ':'", 3) + + # Missing ":var" in a "var" member variable declaration (without initialization) + lines =<< trim END + vim9script + class Something + var: number + endclass + END + v9.CheckSourceFailure(lines, 'E1317: Invalid object variable declaration: var: number', 3) + + # Missing ":var" in a "var" member variable declaration (with initialization) + lines =<< trim END + vim9script + class Something + var: number = 42 + endclass + END + v9.CheckSourceFailure(lines, 'E1317: Invalid object variable declaration: var: number = 42', 3) + + # Missing ":var" in a "var" member variable declaration (type inferred) + lines =<< trim END + vim9script + class Something + var = 42 + endclass + END + v9.CheckSourceFailure(lines, 'E1317: Invalid object variable declaration: var = 42', 3) + + # Test for unsupported comment specifier + lines =<< trim END + vim9script + class Something + # comment + #{ + endclass + END + v9.CheckSourceFailure(lines, 'E1170: Cannot use #{ to start a comment', 3) + + # Test for using class as a bool + lines =<< trim END + vim9script + class A + endclass + if A + endif + END + v9.CheckSourceFailure(lines, 'E1405: Class "A" cannot be used as a value', 4) + + # Test for using object as a bool + lines =<< trim END + vim9script + class A + endclass + var a = A.new() + if a + endif + END + v9.CheckSourceFailure(lines, 'E1320: Using an Object as a Number', 5) + + # Test for using class as a float + lines =<< trim END + vim9script + class A + endclass + sort([1.1, A], 'f') + END + v9.CheckSourceFailure(lines, 'E1405: Class "A" cannot be used as a value', 4) + + # Test for using object as a float + lines =<< trim END + vim9script + class A + endclass + var a = A.new() + sort([1.1, a], 'f') + END + v9.CheckSourceFailure(lines, 'E1322: Using an Object as a Float', 5) + + # Test for using class as a string + lines =<< trim END + vim9script + class A + endclass + :exe 'call ' .. A + END + v9.CheckSourceFailure(lines, 'E1405: Class "A" cannot be used as a value', 4) + + # Test for using object as a string + lines =<< trim END + vim9script + class A + endclass + var a = A.new() + :exe 'call ' .. a + END + v9.CheckSourceFailure(lines, 'E1324: Using an Object as a String', 5) + + # Test creating a class with member variables and methods, calling a object + # method. Check for using type() and typename() with a class and an object. + lines =<< trim END + vim9script + + class TextPosition + var lnum: number + var col: number + + # make a nicely formatted string + def ToString(): string + return $'({this.lnum}, {this.col})' + enddef + endclass + + # use the automatically generated new() method + var pos = TextPosition.new(2, 12) + assert_equal(2, pos.lnum) + assert_equal(12, pos.col) + + # call an object method + assert_equal('(2, 12)', pos.ToString()) + + assert_equal(v:t_class, type(TextPosition)) + assert_equal(v:t_object, type(pos)) + assert_equal('class', typename(TextPosition)) + assert_equal('object', typename(pos)) + END + v9.CheckSourceSuccess(lines) + + # When referencing object methods, space cannot be used after a "." + lines =<< trim END + vim9script + class A + def Foo(): number + return 10 + enddef + endclass + var a = A.new() + var v = a. Foo() + END + v9.CheckSourceFailure(lines, "E1202: No white space allowed after '.'", 8) + + # Using an object without specifying a method or a member variable + lines =<< trim END + vim9script + class A + def Foo(): number + return 10 + enddef + endclass + var a = A.new() + var v = a. + END + v9.CheckSourceFailure(lines, 'E15: Invalid expression: "a."', 8) + + # Error when parsing the arguments of an object method. + lines =<< trim END + vim9script + class A + def Foo() + enddef + endclass + var a = A.new() + var v = a.Foo(,) + END + v9.CheckSourceFailure(lines, 'E15: Invalid expression: "a.Foo(,)"', 7) + + # Use a multi-line initialization for a member variable + lines =<< trim END + vim9script + class A + var y = { + X: 1 + } + endclass + var a = A.new() + END + v9.CheckSourceSuccess(lines) +enddef + +" Tests for object/class methods in a class +def Test_class_def_method() + # Using the "public" keyword when defining an object method + var lines =<< trim END + vim9script + class A + public def Foo() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1331: Public must be followed by "var" or "static"', 3) + + # Using the "public" keyword when defining a class method + lines =<< trim END + vim9script + class A + public static def Foo() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1388: Public keyword not supported for a method', 3) + + # Using the "public" keyword when defining an object protected method + lines =<< trim END + vim9script + class A + public def _Foo() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1331: Public must be followed by "var" or "static"', 3) + + # Using the "public" keyword when defining a class protected method + lines =<< trim END + vim9script + class A + public static def _Foo() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1388: Public keyword not supported for a method', 3) + + # Using a "def" keyword without an object method name + lines =<< trim END + vim9script + class A + def + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: def', 3) + + # Using a "def" keyword without a class method name + lines =<< trim END + vim9script + class A + static def + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: static def', 3) +enddef + +def Test_class_defined_twice() + # class defined twice should fail + var lines =<< trim END + vim9script + class There + endclass + class There + endclass + END + v9.CheckSourceFailure(lines, 'E1041: Redefining script item: "There"', 4) + + # one class, reload same script twice is OK + lines =<< trim END + vim9script + class There + endclass + END + writefile(lines, 'XclassTwice.vim', 'D') + source XclassTwice.vim + source XclassTwice.vim +enddef + +def Test_returning_null_object() + # this was causing an internal error + var lines =<< trim END + vim9script + + class BufferList + def Current(): any + return null_object + enddef + endclass + + var buffers = BufferList.new() + echo buffers.Current() + END + v9.CheckSourceSuccess(lines) +enddef + +def Test_using_null_class() + var lines =<< trim END + @_ = null_class.member + END + v9.CheckDefExecAndScriptFailure(lines, ['E715: Dictionary required', 'E1363: Incomplete type']) +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 + vim9script + + class State + var value = 'xyz' + endclass + + var state: State + var db = {'xyz': 789} + echo db[state.value] + END + v9.CheckSourceFailure(lines, 'E1360: Using a null object', 9) + + # Use an uninitialized object from a def function + lines =<< trim END + vim9script + + class Class + var id: string + def Method1() + echo 'Method1' .. this.id + enddef + endclass + + var obj: Class + def Func() + obj.Method1() + enddef + Func() + END + v9.CheckSourceFailure(lines, 'E1360: Using a null object', 1) + + # Pass an uninitialized object variable to a "new" function and try to call an + # object method. + lines =<< trim END + vim9script + + class Background + var background = 'dark' + endclass + + class Colorscheme + var _bg: Background + + def GetBackground(): string + return this._bg.background + enddef + endclass + + var bg: Background # UNINITIALIZED + echo Colorscheme.new(bg).GetBackground() + END + v9.CheckSourceFailure(lines, 'E1360: Using a null object', 1) + + # TODO: this should not give an error but be handled at runtime + lines =<< trim END + vim9script + + class Class + var id: string + def Method1() + echo 'Method1' .. this.id + enddef + endclass + + var obj = null_object + def Func() + obj.Method1() + enddef + Func() + END + v9.CheckSourceFailure(lines, 'E1363: Incomplete type', 1) +enddef + +" Null object assignment and comparison +def Test_null_object_assign_compare() + var lines =<< trim END + vim9script + + var nullo = null_object + def F(): any + return nullo + enddef + assert_equal('object', typename(F())) + + var o0 = F() + assert_true(o0 == null_object) + assert_true(o0 == null) + + var o1: any = nullo + assert_true(o1 == null_object) + assert_true(o1 == null) + + def G() + var x = null_object + enddef + + class C + endclass + var o2: C + assert_true(o2 == null_object) + assert_true(o2 == null) + + o2 = null_object + assert_true(o2 == null) + + o2 = C.new() + assert_true(o2 != null) + + o2 = null_object + assert_true(o2 == null) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for object member initialization and disassembly +def Test_class_member_initializer() + var lines =<< trim END + vim9script + + class TextPosition + var lnum: number = 1 + var col: number = 1 + + # constructor with only the line number + def new(lnum: number) + this.lnum = lnum + enddef + endclass + + var pos = TextPosition.new(3) + assert_equal(3, pos.lnum) + assert_equal(1, pos.col) + + var instr = execute('disassemble TextPosition.new') + assert_match('new\_s*' .. + '0 NEW TextPosition size \d\+\_s*' .. + '\d PUSHNR 1\_s*' .. + '\d STORE_THIS 0\_s*' .. + '\d PUSHNR 1\_s*' .. + '\d STORE_THIS 1\_s*' .. + 'this.lnum = lnum\_s*' .. + '\d LOAD arg\[-1]\_s*' .. + '\d PUSHNR 0\_s*' .. + '\d LOAD $0\_s*' .. + '\d\+ STOREINDEX object\_s*' .. + '\d\+ RETURN object.*', + instr) + END + v9.CheckSourceSuccess(lines) +enddef + +def Test_member_any_used_as_object() + var lines =<< trim END + vim9script + + class Inner + var value: number = 0 + endclass + + class Outer + var inner: any + endclass + + def F(outer: Outer) + outer.inner.value = 1 + enddef + + var inner_obj = Inner.new(0) + var outer_obj = Outer.new(inner_obj) + F(outer_obj) + assert_equal(1, inner_obj.value) + END + v9.CheckSourceSuccess(lines) + + # Try modifying a protected variable using an "any" object + lines =<< trim END + vim9script + + class Inner + var _value: string = '' + endclass + + class Outer + var inner: any + endclass + + def F(outer: Outer) + outer.inner._value = 'b' + enddef + + var inner_obj = Inner.new('a') + var outer_obj = Outer.new(inner_obj) + F(outer_obj) + END + v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_value" in class "Inner"', 1) + + # Try modifying a non-existing variable using an "any" object + lines =<< trim END + vim9script + + class Inner + var value: string = '' + endclass + + class Outer + var inner: any + endclass + + def F(outer: Outer) + outer.inner.someval = 'b' + enddef + + var inner_obj = Inner.new('a') + var outer_obj = Outer.new(inner_obj) + F(outer_obj) + END + v9.CheckSourceFailure(lines, 'E1326: Variable "someval" not found in object "Inner"', 1) +enddef + +" Nested assignment to a object variable which is of another class type +def Test_assignment_nested_type() + var lines =<< trim END + vim9script + + class Inner + public var value: number = 0 + endclass + + class Outer + var inner: Inner + endclass + + def F(outer: Outer) + outer.inner.value = 1 + enddef + + def Test_assign_to_nested_typed_member() + var inner = Inner.new(0) + var outer = Outer.new(inner) + F(outer) + assert_equal(1, inner.value) + enddef + + Test_assign_to_nested_typed_member() + + var script_inner = Inner.new(0) + var script_outer = Outer.new(script_inner) + script_outer.inner.value = 1 + assert_equal(1, script_inner.value) + END + v9.CheckSourceSuccess(lines) + + # Assignment where target item is read only in :def + lines =<< trim END + vim9script + + class Inner + var value: number = 0 + endclass + + class Outer + var inner: Inner + endclass + + def F(outer: Outer) + outer.inner.value = 1 + enddef + + def Test_assign_to_nested_typed_member() + var inner = Inner.new(0) + var outer = Outer.new(inner) + F(outer) + assert_equal(1, inner.value) + enddef + + Test_assign_to_nested_typed_member() + END + v9.CheckSourceFailure(lines, 'E1335: Variable "value" in class "Inner" is not writable', 1) + + # Assignment where target item is read only script level + lines =<< trim END + vim9script + + class Inner + var value: number = 0 + endclass + + class Outer + var inner: Inner + endclass + + def F(outer: Outer) + outer.inner.value = 1 + enddef + + var script_inner = Inner.new(0) + var script_outer = Outer.new(script_inner) + script_outer.inner.value = 1 + assert_equal(1, script_inner.value) + END + v9.CheckSourceFailure(lines, 'E1335: Variable "value" in class "Inner" is not writable', 17) +enddef + +def Test_assignment_with_operator() + # Use "+=" to assign to a object variable + var lines =<< trim END + vim9script + + class Foo + public var x: number + + def Add(n: number) + this.x += n + enddef + endclass + + var f = Foo.new(3) + f.Add(17) + assert_equal(20, f.x) + + def AddToFoo(obj: Foo) + obj.x += 3 + enddef + + AddToFoo(f) + assert_equal(23, f.x) + END + v9.CheckSourceSuccess(lines) +enddef + +def Test_list_of_objects() + var lines =<< trim END + vim9script + + class Foo + def Add() + enddef + endclass + + def ProcessList(fooList: list) + for foo in fooList + foo.Add() + endfor + enddef + + var l: list = [Foo.new()] + ProcessList(l) + END + v9.CheckSourceSuccess(lines) +enddef + +def Test_expr_after_using_object() + var lines =<< trim END + vim9script + + class Something + var label: string = '' + endclass + + def Foo(): Something + var v = Something.new() + echo 'in Foo(): ' .. typename(v) + return v + enddef + + Foo() + END + v9.CheckSourceSuccess(lines) +enddef + +def Test_class_default_new() + var lines =<< trim END + vim9script + + class TextPosition + var lnum: number = 1 + var col: number = 1 + endclass + + var pos = TextPosition.new() + assert_equal(1, pos.lnum) + assert_equal(1, pos.col) + + pos = TextPosition.new(v:none, v:none) + assert_equal(1, pos.lnum) + assert_equal(1, pos.col) + + pos = TextPosition.new(3, 22) + assert_equal(3, pos.lnum) + assert_equal(22, pos.col) + + pos = TextPosition.new(v:none, 33) + assert_equal(1, pos.lnum) + assert_equal(33, pos.col) + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + class Person + var name: string + var age: number = 42 + var education: string = "unknown" + + def new(this.name, this.age = v:none, this.education = v:none) + enddef + endclass + + var piet = Person.new("Piet") + assert_equal("Piet", piet.name) + assert_equal(42, piet.age) + assert_equal("unknown", piet.education) + + var chris = Person.new("Chris", 4, "none") + assert_equal("Chris", chris.name) + assert_equal(4, chris.age) + assert_equal("none", chris.education) + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + class Person + var name: string + var age: number = 42 + var education: string = "unknown" + + def new(this.name, this.age = v:none, this.education = v:none) + enddef + endclass + + var missing = Person.new() + END + v9.CheckSourceFailure(lines, 'E119: Not enough arguments for function: new', 11) + + # Using a specific value to initialize an instance variable in the new() + # method. + lines =<< trim END + vim9script + class A + var val: string + def new(this.val = 'a') + enddef + endclass + END + v9.CheckSourceFailure(lines, "E1328: Constructor default value must be v:none: = 'a'", 4) +enddef + +def Test_class_new_with_object_member() + var lines =<< trim END + vim9script + + class C + var str: string + var num: number + def new(this.str, this.num) + enddef + def newVals(this.str, this.num) + enddef + endclass + + def Check() + try + var c = C.new('cats', 2) + assert_equal('cats', c.str) + assert_equal(2, c.num) + + c = C.newVals('dogs', 4) + assert_equal('dogs', c.str) + assert_equal(4, c.num) + catch + assert_report($'Unexpected exception was caught: {v:exception}') + endtry + enddef + + Check() + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + + class C + var str: string + var num: number + def new(this.str, this.num) + enddef + endclass + + def Check() + try + var c = C.new(1, 2) + catch + assert_report($'Unexpected exception was caught: {v:exception}') + endtry + enddef + + Check() + END + v9.CheckSourceFailure(lines, 'E1013: Argument 1: type mismatch, expected string but got number', 2) + + lines =<< trim END + vim9script + + class C + var str: string + var num: number + def newVals(this.str, this.num) + enddef + endclass + + def Check() + try + var c = C.newVals('dogs', 'apes') + catch + assert_report($'Unexpected exception was caught: {v:exception}') + endtry + enddef + + Check() + END + v9.CheckSourceFailure(lines, 'E1013: Argument 2: type mismatch, expected number but got string', 2) + + lines =<< trim END + vim9script + + class C + var str: string + def new(str: any) + enddef + endclass + + def Check() + try + var c = C.new(1) + catch + assert_report($'Unexpected exception was caught: {v:exception}') + endtry + enddef + + Check() + END + v9.CheckSourceSuccess(lines) + + # Try using "this." argument in a class method + lines =<< trim END + vim9script + class A + var val = 10 + static def Foo(this.val: number) + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1390: Cannot use an object variable "this.val" except with the "new" method', 4) + + # Try using "this." argument in an object method + lines =<< trim END + vim9script + class A + var val = 10 + def Foo(this.val: number) + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1390: Cannot use an object variable "this.val" except with the "new" method', 4) +enddef + +def Test_class_object_member_inits() + var lines =<< trim END + vim9script + class TextPosition + var lnum: number + var col = 1 + var addcol: number = 2 + endclass + + var pos = TextPosition.new() + assert_equal(0, pos.lnum) + assert_equal(1, pos.col) + assert_equal(2, pos.addcol) + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + class TextPosition + var lnum + var col = 1 + endclass + END + v9.CheckSourceFailure(lines, 'E1022: Type or initialization required', 3) + + # If the type is not specified for a member, then it should be set during + # object creation and not when defining the class. + lines =<< trim END + vim9script + + var init_count = 0 + def Init(): string + init_count += 1 + return 'foo' + enddef + + class A + var str1 = Init() + var str2: string = Init() + var col = 1 + endclass + + assert_equal(init_count, 0) + var a = A.new() + assert_equal(init_count, 2) + END + v9.CheckSourceSuccess(lines) + + # Test for initializing an object member with an unknown variable/type + lines =<< trim END + vim9script + class A + var value = init_val + endclass + var a = A.new() + END + v9.CheckSourceFailure(lines, 'E1001: Variable not found: init_val', 1) + + # Test for initializing an object member with an special type + lines =<< trim END + vim9script + class A + var value: void + endclass + END + v9.CheckSourceFailure(lines, 'E1330: Invalid type for object variable: void', 3) +enddef + +" Test for instance variable access +def Test_instance_variable_access() + var lines =<< trim END + vim9script + class Triple + var _one = 1 + var two = 2 + public var three = 3 + + def GetOne(): number + return this._one + enddef + endclass + + var trip = Triple.new() + assert_equal(1, trip.GetOne()) + assert_equal(2, trip.two) + assert_equal(3, trip.three) + assert_fails('echo trip._one', 'E1333: Cannot access protected variable "_one" in class "Triple"') + + assert_fails('trip._one = 11', 'E1333: Cannot access protected variable "_one" in class "Triple"') + assert_fails('trip.two = 22', 'E1335: Variable "two" in class "Triple" is not writable') + trip.three = 33 + assert_equal(33, trip.three) + + assert_fails('trip.four = 4', 'E1326: Variable "four" not found in object "Triple"') + END + v9.CheckSourceSuccess(lines) + + # Test for a public member variable name beginning with an underscore + lines =<< trim END + vim9script + class A + public var _val = 10 + endclass + END + v9.CheckSourceFailure(lines, 'E1332: Public variable name cannot start with underscore: public var _val = 10', 3) + + lines =<< trim END + vim9script + + class MyCar + var make: string + var age = 5 + + def new(make_arg: string) + this.make = make_arg + enddef + + def GetMake(): string + return $"make = {this.make}" + enddef + def GetAge(): number + return this.age + enddef + endclass + + var c = MyCar.new("abc") + assert_equal('make = abc', c.GetMake()) + + c = MyCar.new("def") + assert_equal('make = def', c.GetMake()) + + var c2 = MyCar.new("123") + assert_equal('make = 123', c2.GetMake()) + + def CheckCar() + assert_equal("make = def", c.GetMake()) + assert_equal(5, c.GetAge()) + enddef + CheckCar() + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + + class MyCar + var make: string + + def new(make_arg: string) + this.make = make_arg + enddef + endclass + + var c = MyCar.new("abc") + var c = MyCar.new("def") + END + v9.CheckSourceFailure(lines, 'E1041: Redefining script item: "c"', 12) + + lines =<< trim END + vim9script + + class Foo + var x: list = [] + + def Add(n: number): any + this.x->add(n) + return this + enddef + endclass + + echo Foo.new().Add(1).Add(2).x + echo Foo.new().Add(1).Add(2) + .x + echo Foo.new().Add(1) + .Add(2).x + echo Foo.new() + .Add(1).Add(2).x + echo Foo.new() + .Add(1) + .Add(2) + .x + END + v9.CheckSourceSuccess(lines) + + # Test for "public" cannot be abbreviated + lines =<< trim END + vim9script + class Something + pub var val = 1 + endclass + END + v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: pub var val = 1', 3) + + # Test for "public" keyword must be followed by "var" or "static". + lines =<< trim END + vim9script + class Something + public val = 1 + endclass + END + v9.CheckSourceFailure(lines, 'E1331: Public must be followed by "var" or "static"', 3) + + # Modify a instance variable using the class name in the script context + lines =<< trim END + vim9script + class A + public var val = 1 + endclass + A.val = 1 + END + v9.CheckSourceFailure(lines, 'E1376: Object variable "val" accessible only using class "A" object', 5) + + # Read a instance variable using the class name in the script context + lines =<< trim END + vim9script + class A + public var val = 1 + endclass + var i = A.val + END + v9.CheckSourceFailure(lines, 'E1376: Object variable "val" accessible only using class "A" object', 5) + + # Modify a instance variable using the class name in a def function + lines =<< trim END + vim9script + class A + public var val = 1 + endclass + def T() + A.val = 1 + enddef + T() + END + v9.CheckSourceFailure(lines, 'E1376: Object variable "val" accessible only using class "A" object', 1) + + # Read a instance variable using the class name in a def function + lines =<< trim END + vim9script + class A + public var val = 1 + endclass + def T() + var i = A.val + enddef + T() + END + v9.CheckSourceFailure(lines, 'E1376: Object variable "val" accessible only using class "A" object', 1) + + # Access from child class extending a class: + lines =<< trim END + vim9script + class A + var ro_obj_var = 10 + public var rw_obj_var = 20 + var _priv_obj_var = 30 + endclass + + class B extends A + def Foo() + var x: number + x = this.ro_obj_var + this.ro_obj_var = 0 + x = this.rw_obj_var + this.rw_obj_var = 0 + x = this._priv_obj_var + this._priv_obj_var = 0 + enddef + endclass + + var b = B.new() + b.Foo() + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for class variable access +def Test_class_variable_access() + # Test for "static" cannot be abbreviated + var lines =<< trim END + vim9script + class Something + stat var val = 1 + endclass + END + v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: stat var val = 1', 3) + + # Test for "static" cannot be followed by "public". + lines =<< trim END + vim9script + class Something + static public var val = 1 + endclass + END + v9.CheckSourceFailure(lines, 'E1368: Static must be followed by "var" or "def"', 3) + + # A readonly class variable cannot be modified from a child class + lines =<< trim END + vim9script + class A + static var ro_class_var = 40 + endclass + + class B extends A + def Foo() + A.ro_class_var = 50 + enddef + endclass + + var b = B.new() + b.Foo() + END + v9.CheckSourceFailure(lines, 'E1335: Variable "ro_class_var" in class "A" is not writable', 1) + + # A protected class variable cannot be accessed from a child class + lines =<< trim END + vim9script + class A + static var _priv_class_var = 60 + endclass + + class B extends A + def Foo() + var i = A._priv_class_var + enddef + endclass + + var b = B.new() + b.Foo() + END + v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_priv_class_var" in class "A"', 1) + + # A protected class variable cannot be modified from a child class + lines =<< trim END + vim9script + class A + static var _priv_class_var = 60 + endclass + + class B extends A + def Foo() + A._priv_class_var = 0 + enddef + endclass + + var b = B.new() + b.Foo() + END + v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_priv_class_var" in class "A"', 1) + + # Access from child class extending a class and from script context + lines =<< trim END + vim9script + class A + static var ro_class_var = 10 + public static var rw_class_var = 20 + static var _priv_class_var = 30 + endclass + + class B extends A + def Foo() + var x: number + x = A.ro_class_var + assert_equal(10, x) + x = A.rw_class_var + assert_equal(25, x) + A.rw_class_var = 20 + assert_equal(20, A.rw_class_var) + enddef + endclass + + assert_equal(10, A.ro_class_var) + assert_equal(20, A.rw_class_var) + A.rw_class_var = 25 + assert_equal(25, A.rw_class_var) + var b = B.new() + b.Foo() + END + v9.CheckSourceSuccess(lines) +enddef + +def Test_class_object_compare() + var class_lines =<< trim END + vim9script + class Item + var nr = 0 + var name = 'xx' + endclass + END + + # used at the script level and in a compiled function + var test_lines =<< trim END + var i1 = Item.new() + assert_equal(i1, i1) + assert_true(i1 is i1) + var i2 = Item.new() + assert_equal(i1, i2) + assert_false(i1 is i2) + var i3 = Item.new(0, 'xx') + assert_equal(i1, i3) + + var io1 = Item.new(1, 'xx') + assert_notequal(i1, io1) + var io2 = Item.new(0, 'yy') + assert_notequal(i1, io2) + END + + v9.CheckSourceSuccess(class_lines + test_lines) + v9.CheckSourceSuccess( + class_lines + ['def Test()'] + test_lines + ['enddef', 'Test()']) + + for op in ['>', '>=', '<', '<=', '=~', '!~'] + var op_lines = [ + 'var i1 = Item.new()', + 'var i2 = Item.new()', + 'echo i1 ' .. op .. ' i2', + ] + v9.CheckSourceFailure(class_lines + op_lines, 'E1153: Invalid operation for object', 8) + v9.CheckSourceFailure(class_lines + + ['def Test()'] + op_lines + ['enddef', 'Test()'], 'E1153: Invalid operation for object') + endfor +enddef + +def Test_object_type() + var lines =<< trim END + vim9script + + class One + var one = 1 + endclass + class Two + var two = 2 + endclass + class TwoMore extends Two + var more = 9 + endclass + + var o: One = One.new() + var t: Two = Two.new() + var m: TwoMore = TwoMore.new() + var tm: Two = TwoMore.new() + + t = m + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + + class One + var one = 1 + endclass + class Two + var two = 2 + endclass + + var o: One = Two.new() + END + v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected object but got object', 10) + + lines =<< trim END + vim9script + + interface One + def GetMember(): number + endinterface + class Two implements One + var one = 1 + def GetMember(): number + return this.one + enddef + endclass + + var o: One = Two.new(5) + assert_equal(5, o.GetMember()) + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + + class Num + var n: number = 0 + endclass + + def Ref(name: string): func(Num): Num + return (arg: Num): Num => { + return eval(name)(arg) + } + enddef + + const Fn = Ref('Double') + var Double = (m: Num): Num => Num.new(m.n * 2) + + echo Fn(Num.new(4)) + END + v9.CheckSourceSuccess(lines) +enddef + +def Test_class_member() + # check access rules + var lines =<< trim END + vim9script + class TextPos + var lnum = 1 + var col = 1 + static var counter = 0 + static var _secret = 7 + public static var anybody = 42 + + static def AddToCounter(nr: number) + counter += nr + enddef + endclass + + assert_equal(0, TextPos.counter) + TextPos.AddToCounter(3) + assert_equal(3, TextPos.counter) + assert_fails('echo TextPos.noSuchMember', 'E1337: Class variable "noSuchMember" not found in class "TextPos"') + + def GetCounter(): number + return TextPos.counter + enddef + assert_equal(3, GetCounter()) + + assert_fails('TextPos.noSuchMember = 2', 'E1337: Class variable "noSuchMember" not found in class "TextPos"') + assert_fails('TextPos.counter = 5', 'E1335: Variable "counter" in class "TextPos" is not writable') + assert_fails('TextPos.counter += 5', 'E1335: Variable "counter" in class "TextPos" is not writable') + + assert_fails('echo TextPos._secret', 'E1333: Cannot access protected variable "_secret" in class "TextPos"') + assert_fails('TextPos._secret = 8', 'E1333: Cannot access protected variable "_secret" in class "TextPos"') + + assert_equal(42, TextPos.anybody) + TextPos.anybody = 12 + assert_equal(12, TextPos.anybody) + TextPos.anybody += 5 + assert_equal(17, TextPos.anybody) + END + v9.CheckSourceSuccess(lines) + + # example in the help + lines =<< trim END + vim9script + class OtherThing + var size: number + static var totalSize: number + + def new(this.size) + totalSize += this.size + enddef + endclass + assert_equal(0, OtherThing.totalSize) + var to3 = OtherThing.new(3) + assert_equal(3, OtherThing.totalSize) + var to7 = OtherThing.new(7) + assert_equal(10, OtherThing.totalSize) + END + v9.CheckSourceSuccess(lines) + + # using static class member twice + lines =<< trim END + vim9script + + class HTML + static var author: string = 'John Doe' + + static def MacroSubstitute(s: string): string + return substitute(s, '{{author}}', author, 'gi') + enddef + endclass + + assert_equal('some text', HTML.MacroSubstitute('some text')) + assert_equal('some text', HTML.MacroSubstitute('some text')) + END + v9.CheckSourceSuccess(lines) + + # access protected member in lambda + lines =<< trim END + vim9script + + class Foo + var _x: number = 0 + + def Add(n: number): number + const F = (): number => this._x + n + return F() + enddef + endclass + + var foo = Foo.new() + assert_equal(5, foo.Add(5)) + END + v9.CheckSourceSuccess(lines) + + # access protected member in lambda body + lines =<< trim END + vim9script + + class Foo + var _x: number = 6 + + def Add(n: number): number + var Lam = () => { + this._x = this._x + n + } + Lam() + return this._x + enddef + endclass + + var foo = Foo.new() + assert_equal(13, foo.Add(7)) + END + v9.CheckSourceSuccess(lines) + + # check shadowing + lines =<< trim END + vim9script + + class Some + static var count = 0 + def Method(count: number) + echo count + enddef + endclass + + var s = Some.new() + s.Method(7) + END + v9.CheckSourceFailure(lines, 'E1340: Argument already declared in the class: count', 5) + + # Use a local variable in a method with the same name as a class variable + lines =<< trim END + vim9script + + class Some + static var count = 0 + def Method(arg: number) + var count = 3 + echo arg count + enddef + endclass + + var s = Some.new() + s.Method(7) + END + v9.CheckSourceFailure(lines, 'E1341: Variable already declared in the class: count', 1) + + # Test for using an invalid type for a member variable + lines =<< trim END + vim9script + class A + var val: xxx + endclass + END + v9.CheckSourceFailure(lines, 'E1010: Type not recognized: xxx', 3) + + # Test for setting a member on a null object + lines =<< trim END + vim9script + class A + public var val: string + endclass + + def F() + var obj: A + obj.val = "" + enddef + F() + END + v9.CheckSourceFailure(lines, 'E1360: Using a null object', 2) + + # Test for accessing a member on a null object + lines =<< trim END + vim9script + class A + var val: string + endclass + + def F() + var obj: A + echo obj.val + enddef + F() + END + v9.CheckSourceFailure(lines, 'E1360: Using a null object', 2) + + # Test for setting a member on a null object, at script level + lines =<< trim END + vim9script + class A + public var val: string + endclass + + var obj: A + obj.val = "" + END + v9.CheckSourceFailure(lines, 'E1360: Using a null object', 7) + + # Test for accessing a member on a null object, at script level + lines =<< trim END + vim9script + class A + var val: string + endclass + + var obj: A + echo obj.val + END + v9.CheckSourceFailure(lines, 'E1360: Using a null object', 7) + + # Test for no space before or after the '=' when initializing a member + # variable + lines =<< trim END + vim9script + class A + var val: number= 10 + endclass + END + v9.CheckSourceFailure(lines, "E1004: White space required before and after '='", 3) + lines =<< trim END + vim9script + class A + var val: number =10 + endclass + END + v9.CheckSourceFailure(lines, "E1004: White space required before and after '='", 3) + + # Access a non-existing member + lines =<< trim END + vim9script + class A + endclass + var a = A.new() + var v = a.bar + END + v9.CheckSourceFailure(lines, 'E1326: Variable "bar" not found in object "A"', 5) +enddef + +" These messages should show the defining class of the variable (base class), +" not the class that did the reference (super class) +def Test_defining_class_message() + var lines =<< trim END + vim9script + + class Base + var _v1: list> + endclass + + class Child extends Base + endclass + + var o = Child.new() + var x = o._v1 + END + v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_v1" in class "Base"', 11) + lines =<< trim END + vim9script + + class Base + var _v1: list> + endclass + + class Child extends Base + endclass + + def F() + var o = Child.new() + var x = o._v1 + enddef + F() + END + v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_v1" in class "Base"', 2) + lines =<< trim END + vim9script + + class Base + var v1: list> + endclass + + class Child extends Base + endclass + + var o = Child.new() + o.v1 = [] + END + v9.CheckSourceFailure(lines, 'E1335: Variable "v1" in class "Base" is not writable', 11) + lines =<< trim END + vim9script + + class Base + var v1: list> + endclass + + class Child extends Base + endclass + + def F() + var o = Child.new() + o.v1 = [] + enddef + F() + END + + # Attempt to read a protected variable that is in the middle + # of the class hierarchy. + v9.CheckSourceFailure(lines, 'E1335: Variable "v1" in class "Base" is not writable', 2) + lines =<< trim END + vim9script + + class Base0 + endclass + + class Base extends Base0 + var _v1: list> + endclass + + class Child extends Base + endclass + + def F() + var o = Child.new() + var x = o._v1 + enddef + F() + END + v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_v1" in class "Base"', 2) + + # Attempt to read a protected variable that is at the start + # of the class hierarchy. + lines =<< trim END + vim9script + + class Base0 + endclass + + class Base extends Base0 + endclass + + class Child extends Base + var _v1: list> + endclass + + def F() + var o = Child.new() + var x = o._v1 + enddef + F() + END + v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_v1" in class "Child"', 2) +enddef + +func Test_class_garbagecollect() + let lines =<< trim END + vim9script + + class Point + var p = [2, 3] + static var pl = ['a', 'b'] + static var pd = {a: 'a', b: 'b'} + endclass + + echo Point.pl Point.pd + call test_garbagecollect_now() + echo Point.pl Point.pd + END + call v9.CheckSourceSuccess(lines) + + let lines =<< trim END + vim9script + + interface View + endinterface + + class Widget + var view: View + endclass + + class MyView implements View + var widget: Widget + + def new() + # this will result in a circular reference to this object + var widget = Widget.new(this) + enddef + endclass + + var view = MyView.new() + + # overwrite "view", will be garbage-collected next + view = MyView.new() + test_garbagecollect_now() + END + call v9.CheckSourceSuccess(lines) +endfunc + +" Test interface garbage collection +func Test_interface_garbagecollect() + let lines =<< trim END + vim9script + + interface I + 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 + class Value + var value = 0 + static var objects = 0 + + def new(v: number) + this.value = v + ++objects + enddef + + static def GetCount(): number + return objects + enddef + endclass + + assert_equal(0, Value.GetCount()) + var v1 = Value.new(2) + assert_equal(1, Value.GetCount()) + var v2 = Value.new(7) + assert_equal(2, Value.GetCount()) + END + v9.CheckSourceSuccess(lines) + + # Test for cleaning up after a class definition failure when using class + # functions. + lines =<< trim END + vim9script + class A + static def Foo() + enddef + aaa + endclass + END + v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: aaa', 5) + + # Test for calling a class method from another class method without the class + # name prefix. + lines =<< trim END + vim9script + class A + static var myList: list = [1] + static def Foo(n: number) + myList->add(n) + enddef + static def Bar() + Foo(2) + enddef + def Baz() + Foo(3) + enddef + endclass + A.Bar() + var a = A.new() + a.Baz() + assert_equal([1, 2, 3], A.myList) + END + v9.CheckSourceSuccess(lines) +enddef + +def Test_class_defcompile() + var lines =<< trim END + vim9script + + class C + def Fo(i: number): string + return i + enddef + endclass + + defcompile C.Fo + END + v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected string but got number', 1) + + lines =<< trim END + vim9script + + class C + static def Fc(): number + return 'x' + enddef + endclass + + defcompile C.Fc + END + v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected number but got string', 1) + + lines =<< trim END + vim9script + + class C + static def new() + enddef + endclass + + defcompile C.new + END + v9.CheckSourceFailure(lines, 'E1370: Cannot define a "new" method as static', 5) + + # Trying to compile a function using a non-existing class variable + lines =<< trim END + vim9script + defcompile x.Foo() + END + v9.CheckSourceFailure(lines, 'E475: Invalid argument: x.Foo()', 2) + + # Trying to compile a function using a variable which is not a class + lines =<< trim END + vim9script + var x: number + defcompile x.Foo() + END + v9.CheckSourceFailure(lines, 'E475: Invalid argument: x.Foo()', 3) + + # Trying to compile a function without specifying the name + lines =<< trim END + vim9script + class A + endclass + defcompile A. + END + v9.CheckSourceFailure(lines, 'E475: Invalid argument: A.', 4) + + # Trying to compile a non-existing class object member function + lines =<< trim END + vim9script + class A + endclass + var a = A.new() + defcompile a.Foo() + END + v9.CheckSourceFailureList(lines, ['E1326: Variable "Foo" not found in object "A"', 'E475: Invalid argument: a.Foo()']) +enddef + +def Test_class_object_to_string() + var lines =<< trim END + vim9script + class TextPosition + var lnum = 1 + var col = 22 + endclass + + assert_equal("class TextPosition", string(TextPosition)) + + var pos = TextPosition.new() + assert_equal("object of TextPosition {lnum: 1, col: 22}", string(pos)) + END + v9.CheckSourceSuccess(lines) +enddef + +def Test_interface_basics() + var lines =<< trim END + vim9script + interface Something + var ro_var: list + def GetCount(): number + endinterface + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + interface SomethingWrong + static var count = 7 + endinterface + END + v9.CheckSourceFailure(lines, 'E1342: Interface can only be defined in Vim9 script', 1) + + lines =<< trim END + vim9script + + interface Some + var value: number + def Method(value: number) + endinterface + END + # The argument name and the object member name are the same, but this is not a + # problem because object members are always accessed with the "this." prefix. + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + interface somethingWrong + static var count = 7 + endinterface + END + v9.CheckSourceFailure(lines, 'E1343: Interface name must start with an uppercase letter: somethingWrong', 2) + + lines =<< trim END + vim9script + interface SomethingWrong + var value: string + var count = 7 + def GetCount(): number + endinterface + END + v9.CheckSourceFailure(lines, 'E1344: Cannot initialize a variable in an interface', 4) + + lines =<< trim END + vim9script + interface SomethingWrong + var value: string + var count: number + def GetCount(): number + return 5 + enddef + endinterface + END + v9.CheckSourceFailure(lines, 'E1345: Not a valid command in an interface: return 5', 6) + + lines =<< trim END + vim9script + export interface EnterExit + def Enter(): void + def Exit(): void + endinterface + END + writefile(lines, 'XdefIntf.vim', 'D') + + lines =<< trim END + vim9script + import './XdefIntf.vim' as defIntf + export def With(ee: defIntf.EnterExit, F: func) + ee.Enter() + try + F() + finally + ee.Exit() + endtry + enddef + END + v9.CheckScriptSuccess(lines) + + var imported =<< trim END + vim9script + export abstract class EnterExit + def Enter(): void + enddef + def Exit(): void + enddef + endclass + END + writefile(imported, 'XdefIntf2.vim', 'D') + + lines[1] = " import './XdefIntf2.vim' as defIntf" + v9.CheckScriptSuccess(lines) +enddef + +def Test_class_implements_interface() + var lines =<< trim END + vim9script + + interface Some + var count: number + def Method(nr: number) + endinterface + + class SomeImpl implements Some + var count: number + def Method(nr: number) + echo nr + enddef + endclass + + interface Another + var member: string + endinterface + + class AnotherImpl implements Some, Another + var member = 'abc' + var count = 20 + def Method(nr: number) + echo nr + enddef + endclass + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + + interface Some + var count: number + endinterface + + class SomeImpl implements Some implements Some + var count: number + endclass + END + v9.CheckSourceFailure(lines, 'E1350: Duplicate "implements"', 7) + + lines =<< trim END + vim9script + + interface Some + var count: number + endinterface + + class SomeImpl implements Some, Some + var count: number + endclass + END + v9.CheckSourceFailure(lines, 'E1351: Duplicate interface after "implements": Some', 7) + + lines =<< trim END + vim9script + + interface Some + var counter: number + def Method(nr: number) + endinterface + + class SomeImpl implements Some + var count: number + def Method(nr: number) + echo nr + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1348: Variable "counter" of interface "Some" is not implemented', 13) + + lines =<< trim END + vim9script + + interface Some + var count: number + def Methods(nr: number) + endinterface + + class SomeImpl implements Some + var count: number + def Method(nr: number) + echo nr + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1349: Method "Methods" of interface "Some" is not implemented', 13) + + # Check different order of members in class and interface works. + lines =<< trim END + vim9script + + interface Result + var label: string + var errpos: number + endinterface + + # order of members is opposite of interface + class Failure implements Result + public var lnum: number = 5 + var errpos: number = 42 + var label: string = 'label' + endclass + + def Test() + var result: Result = Failure.new() + + assert_equal('label', result.label) + assert_equal(42, result.errpos) + enddef + + Test() + END + v9.CheckSourceSuccess(lines) + + # Interface name after "extends" doesn't end in a space or NUL character + lines =<< trim END + vim9script + interface A + endinterface + class B extends A" + endclass + END + v9.CheckSourceFailure(lines, 'E1315: White space required after name: A"', 4) + + # Trailing characters after a class name + lines =<< trim END + vim9script + class A bbb + endclass + END + v9.CheckSourceFailure(lines, 'E488: Trailing characters: bbb', 2) + + # using "implements" with a non-existing class + lines =<< trim END + vim9script + class A implements B + endclass + END + v9.CheckSourceFailure(lines, 'E1346: Interface name not found: B', 3) + + # using "implements" with a regular class + lines =<< trim END + vim9script + class A + endclass + class B implements A + endclass + END + v9.CheckSourceFailure(lines, 'E1347: Not a valid interface: A', 5) + + # using "implements" with a variable + lines =<< trim END + vim9script + var T: number = 10 + class A implements T + endclass + END + v9.CheckSourceFailure(lines, 'E1347: Not a valid interface: T', 4) + + # implements should be followed by a white space + lines =<< trim END + vim9script + interface A + endinterface + class B implements A; + endclass + END + v9.CheckSourceFailure(lines, 'E1315: White space required after name: A;', 4) + + lines =<< trim END + vim9script + + interface One + def IsEven(nr: number): bool + endinterface + class Two implements One + def IsEven(nr: number): string + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1383: Method "IsEven": type mismatch, expected func(number): bool but got func(number): string', 9) + + lines =<< trim END + vim9script + + interface One + def IsEven(nr: number): bool + endinterface + class Two implements One + def IsEven(nr: bool): bool + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1383: Method "IsEven": type mismatch, expected func(number): bool but got func(bool): bool', 9) + + lines =<< trim END + vim9script + + interface One + def IsEven(nr: number): bool + endinterface + class Two implements One + def IsEven(nr: number, ...extra: list): bool + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1383: Method "IsEven": type mismatch, expected func(number): bool but got func(number, ...list): bool', 9) + + # access superclass interface members from subclass, mix variable order + lines =<< trim END + vim9script + + interface I1 + var mvar1: number + var mvar2: number + endinterface + + # NOTE: the order is swapped + class A implements I1 + var mvar2: number + var mvar1: number + public static var svar2: number + public static var svar1: number + def new() + svar1 = 11 + svar2 = 12 + this.mvar1 = 111 + this.mvar2 = 112 + enddef + endclass + + class B extends A + def new() + this.mvar1 = 121 + this.mvar2 = 122 + enddef + endclass + + class C extends B + def new() + this.mvar1 = 131 + this.mvar2 = 132 + enddef + endclass + + def F2(i: I1): list + return [ i.mvar1, i.mvar2 ] + enddef + + var oa = A.new() + var ob = B.new() + var oc = C.new() + + assert_equal([111, 112], F2(oa)) + assert_equal([121, 122], F2(ob)) + assert_equal([131, 132], F2(oc)) + END + v9.CheckSourceSuccess(lines) + + # Access superclass interface members from subclass, mix variable order. + # Two interfaces, one on A, one on B; each has both kinds of variables + lines =<< trim END + vim9script + + interface I1 + var mvar1: number + var mvar2: number + endinterface + + interface I2 + var mvar3: number + var mvar4: number + endinterface + + class A implements I1 + public static var svar1: number + public static var svar2: number + var mvar1: number + var mvar2: number + def new() + svar1 = 11 + svar2 = 12 + this.mvar1 = 111 + this.mvar2 = 112 + enddef + endclass + + class B extends A implements I2 + static var svar3: number + static var svar4: number + var mvar3: number + var mvar4: number + def new() + svar3 = 23 + svar4 = 24 + this.mvar1 = 121 + this.mvar2 = 122 + this.mvar3 = 123 + this.mvar4 = 124 + enddef + endclass + + class C extends B + public static var svar5: number + def new() + svar5 = 1001 + this.mvar1 = 131 + this.mvar2 = 132 + this.mvar3 = 133 + this.mvar4 = 134 + enddef + endclass + + def F2(i: I1): list + return [ i.mvar1, i.mvar2 ] + enddef + + def F4(i: I2): list + return [ i.mvar3, i.mvar4 ] + enddef + + var oa = A.new() + var ob = B.new() + var oc = C.new() + + assert_equal([[111, 112]], [F2(oa)]) + assert_equal([[121, 122], [123, 124]], [F2(ob), F4(ob)]) + assert_equal([[131, 132], [133, 134]], [F2(oc), F4(oc)]) + END + v9.CheckSourceSuccess(lines) + + # Using two interface names without a space after the "," + lines =<< trim END + vim9script + interface A + endinterface + interface B + endinterface + class C implements A,B + endclass + END + v9.CheckSourceFailure(lines, 'E1315: White space required after name: A,B', 6) + + # No interface name after a comma + lines =<< trim END + vim9script + interface A + endinterface + class B implements A, + endclass + END + v9.CheckSourceFailure(lines, 'E1389: Missing name after implements', 4) + + # No interface name after implements + lines =<< trim END + vim9script + class A implements + endclass + END + v9.CheckSourceFailure(lines, 'E1389: Missing name after implements', 2) +enddef + +def Test_call_interface_method() + var lines =<< trim END + vim9script + interface Base + def Enter(): void + endinterface + + class Child implements Base + def Enter(): void + g:result ..= 'child' + enddef + endclass + + def F(obj: Base) + obj.Enter() + enddef + + g:result = '' + F(Child.new()) + assert_equal('child', g:result) + unlet g:result + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + class Base + def Enter(): void + g:result ..= 'base' + enddef + endclass + + class Child extends Base + def Enter(): void + g:result ..= 'child' + enddef + endclass + + def F(obj: Base) + obj.Enter() + enddef + + g:result = '' + F(Child.new()) + assert_equal('child', g:result) + unlet g:result + END + v9.CheckSourceSuccess(lines) + + # method of interface returns a value + lines =<< trim END + vim9script + interface Base + def Enter(): string + endinterface + + class Child implements Base + def Enter(): string + g:result ..= 'child' + return "/resource" + enddef + endclass + + def F(obj: Base) + var r = obj.Enter() + g:result ..= r + enddef + + g:result = '' + F(Child.new()) + assert_equal('child/resource', g:result) + unlet g:result + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + class Base + def Enter(): string + return null_string + enddef + endclass + + class Child extends Base + def Enter(): string + g:result ..= 'child' + return "/resource" + enddef + endclass + + def F(obj: Base) + var r = obj.Enter() + g:result ..= r + enddef + + g:result = '' + F(Child.new()) + assert_equal('child/resource', g:result) + unlet g:result + END + v9.CheckSourceSuccess(lines) + + # No class that implements the interface. + lines =<< trim END + vim9script + + interface IWithEE + def Enter(): any + def Exit(): void + endinterface + + def With1(ee: IWithEE, F: func) + var r = ee.Enter() + enddef + + defcompile + END + v9.CheckSourceSuccess(lines) +enddef + +def Test_class_used_as_type() + var lines =<< trim END + vim9script + + class Point + var x = 0 + var y = 0 + endclass + + var p: Point + p = Point.new(2, 33) + assert_equal(2, p.x) + assert_equal(33, p.y) + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + + interface HasX + var x: number + endinterface + + class Point implements HasX + var x = 0 + var y = 0 + endclass + + var p: Point + p = Point.new(2, 33) + var hx = p + assert_equal(2, hx.x) + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + + class Point + var x = 0 + var y = 0 + endclass + + var p: Point + p = 'text' + END + v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected object but got string', 9) +enddef + +def Test_class_extends() + var lines =<< trim END + vim9script + class Base + var one = 1 + def GetOne(): number + return this.one + enddef + endclass + class Child extends Base + var two = 2 + def GetTotal(): number + return this.one + this.two + enddef + endclass + var o = Child.new() + assert_equal(1, o.one) + assert_equal(2, o.two) + assert_equal(1, o.GetOne()) + assert_equal(3, o.GetTotal()) + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + class Base + var one = 1 + endclass + 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 + class Base + var one = 1 + endclass + class Child extends Base extends Base + var two = 2 + endclass + END + v9.CheckSourceFailure(lines, 'E1352: Duplicate "extends"', 5) + + lines =<< trim END + vim9script + class Child extends BaseClass + var two = 2 + endclass + END + v9.CheckSourceFailure(lines, 'E1353: Class name not found: BaseClass', 4) + + lines =<< trim END + vim9script + var SomeVar = 99 + class Child extends SomeVar + var two = 2 + endclass + END + v9.CheckSourceFailure(lines, 'E1354: Cannot extend SomeVar', 5) + + lines =<< trim END + vim9script + class Base + var name: string + def ToString(): string + return this.name + enddef + endclass + + class Child extends Base + var age: number + def ToString(): string + return super.ToString() .. ': ' .. this.age + enddef + endclass + + var o = Child.new('John', 42) + assert_equal('John: 42', o.ToString()) + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + class Child + var age: number + def ToString(): number + return this.age + enddef + def ToString(): string + return this.age + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1355: Duplicate function: ToString', 9) + + lines =<< trim END + vim9script + class Child + var age: number + def ToString(): string + return super .ToString() .. ': ' .. this.age + enddef + endclass + var o = Child.new(42) + echo o.ToString() + END + v9.CheckSourceFailure(lines, 'E1356: "super" must be followed by a dot', 1) + + lines =<< trim END + vim9script + class Base + var name: string + def ToString(): string + return this.name + enddef + endclass + + var age = 42 + def ToString(): string + return super.ToString() .. ': ' .. age + enddef + echo ToString() + END + v9.CheckSourceFailure(lines, 'E1357: Using "super" not in a class method', 1) + + lines =<< trim END + vim9script + class Child + var age: number + def ToString(): string + return super.ToString() .. ': ' .. this.age + enddef + endclass + var o = Child.new(42) + echo o.ToString() + END + v9.CheckSourceFailure(lines, 'E1358: Using "super" not in a child class', 1) + + 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 + enddef + endclass + + var o = Child.new('John', 42) + assert_equal('Base class: 42', o.ToString()) + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + class Base + 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, 'E1385: Class method "new" accessible only using class "Child"', 1) + + # base class with more than one object member + lines =<< trim END + vim9script + + class Result + var success: bool + var value: any = null + endclass + + class Success extends Result + def new(this.value = v:none) + this.success = true + enddef + endclass + + var v = Success.new('asdf') + assert_equal("object of Success {success: true, value: 'asdf'}", string(v)) + END + v9.CheckSourceSuccess(lines) + + # class name after "extends" doesn't end in a space or NUL character + lines =<< trim END + vim9script + class A + endclass + class B extends A" + endclass + END + v9.CheckSourceFailure(lines, 'E1315: White space required after name: A"', 4) +enddef + +def Test_using_base_class() + var lines =<< trim END + vim9script + + class BaseEE + def Enter(): any + return null + enddef + def Exit(resource: any): void + enddef + endclass + + class ChildEE extends BaseEE + def Enter(): any + return 42 + enddef + + def Exit(resource: number): void + g:result ..= '/exit' + enddef + endclass + + def With(ee: BaseEE) + var r = ee.Enter() + try + g:result ..= r + finally + g:result ..= '/finally' + ee.Exit(r) + endtry + enddef + + g:result = '' + With(ChildEE.new()) + assert_equal('42/finally/exit', g:result) + END + v9.CheckSourceSuccess(lines) + unlet g:result + + # Using super, Child invokes Base method which has optional arg. #12471 + lines =<< trim END + vim9script + + class Base + var success: bool = false + def Method(arg = 0) + this.success = true + enddef + endclass + + class Child extends Base + def new() + super.Method() + enddef + endclass + + var obj = Child.new() + assert_equal(true, obj.success) + END + v9.CheckSourceSuccess(lines) +enddef + +def Test_class_import() + var lines =<< trim END + vim9script + export class Animal + var kind: string + var name: string + endclass + END + writefile(lines, 'Xanimal.vim', 'D') + + lines =<< trim END + vim9script + import './Xanimal.vim' as animal + + var a: animal.Animal + a = animal.Animal.new('fish', 'Eric') + assert_equal('fish', a.kind) + assert_equal('Eric', a.name) + + var b: animal.Animal = animal.Animal.new('cat', 'Garfield') + assert_equal('cat', b.kind) + assert_equal('Garfield', b.name) + END + v9.CheckScriptSuccess(lines) +enddef + +" 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 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 + END + writefile(lines, 'Xextendimportclass.vim', 'D') + + lines =<< trim END + vim9script + import './Xextendimportclass.vim' as XClass + + class A extends XClass.Imp_C1 + 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 + 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) + + lines =<< trim END + abstract class Base + var name: string + endclass + END + v9.CheckSourceFailure(lines, 'E1316: Class can only be defined in Vim9 script', 1) + + # Abstract class cannot have a "new" function + 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) +enddef + +def Test_closure_in_class() + var lines =<< trim END + vim9script + + class Foo + var y: list = ['B'] + + def new() + g:result = filter(['A', 'B'], (_, v) => index(this.y, v) == -1) + enddef + endclass + + Foo.new() + assert_equal(['A'], g:result) + END + v9.CheckSourceSuccess(lines) +enddef + +def Test_construct_object_from_legacy() + # Cannot directly invoke constructor from legacy + var lines =<< trim END + vim9script + + var newCalled = false + + class A + def new(arg: string) + newCalled = true + enddef + endclass + + export def CreateA(...args: list): A + return call(A.new, args) + enddef + + g:P = CreateA + legacy call g:P('some_arg') + assert_equal(true, newCalled) + unlet g:P + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + + var newCalled = false + + class A + static def CreateA(options = {}): any + return A.new() + enddef + def new() + newCalled = true + enddef + endclass + + 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 + var val1: number + var val2: number + static def CreateA(...args: list): any + createdObject = call(A.new, args) + return createdObject + 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 + +def Test_defer_with_object() + var lines =<< trim END + vim9script + + class CWithEE + def Enter() + g:result ..= "entered/" + enddef + def Exit() + g:result ..= "exited" + enddef + endclass + + def With(ee: CWithEE, F: func) + ee.Enter() + defer ee.Exit() + F() + enddef + + g:result = '' + var obj = CWithEE.new() + obj->With(() => { + g:result ..= "called/" + }) + assert_equal('entered/called/exited', g:result) + END + v9.CheckSourceSuccess(lines) + unlet g:result + + lines =<< trim END + vim9script + + class BaseWithEE + def Enter() + g:result ..= "entered-base/" + enddef + def Exit() + g:result ..= "exited-base" + enddef + endclass + + class CWithEE extends BaseWithEE + def Enter() + g:result ..= "entered-child/" + enddef + def Exit() + g:result ..= "exited-child" + enddef + endclass + + def With(ee: BaseWithEE, F: func) + ee.Enter() + defer ee.Exit() + F() + enddef + + g:result = '' + var obj = CWithEE.new() + obj->With(() => { + g:result ..= "called/" + }) + assert_equal('entered-child/called/exited-child', g:result) + END + v9.CheckSourceSuccess(lines) + unlet g:result +enddef + +" The following test used to crash Vim (Github issue #12676) +def Test_extends_method_crashes_vim() + var lines =<< trim END + vim9script + + class Observer + endclass + + class Property + 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 + + def Observe(obj: Property, who: Observer) + obj.Register(who) + enddef + + var p = Bool.new(false) + var myObserver = Observer.new() + + Observe(p, myObserver) + + p.Set(true) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for calling a method in a class that is extended +def Test_call_method_in_extended_class() + var lines =<< trim END + vim9script + + var prop_init_called = false + var prop_register_called = false + + class Property + def Init() + prop_init_called = true + enddef + + def Register() + prop_register_called = true + enddef + endclass + + class Bool extends Property + endclass + + def Observe(obj: Property) + obj.Register() + enddef + + var p = Property.new() + Observe(p) + + p.Init() + assert_true(prop_init_called) + assert_true(prop_register_called) + END + v9.CheckSourceSuccess(lines) +enddef + +def Test_instanceof() + var lines =<< trim END + vim9script + + class Base1 + endclass + + class Base2 extends Base1 + endclass + + interface Intf1 + endinterface + + class Mix1 implements Intf1 + endclass + + class Base3 extends Mix1 + endclass + + 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)) + + 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() + + var o_null: Base1 + assert_false(instanceof(o_null, Base1)) + + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + + class Base1 + endclass + instanceof(Base1.new()) + END + v9.CheckSourceFailure(lines, 'E119: Not enough arguments for function: instanceof') + + lines =<< trim END + vim9script + + class Base1 + endclass + def F() + instanceof(Base1.new()) + enddef + F() + END + v9.CheckSourceFailure(lines, 'E119: Not enough arguments for function: instanceof') + + lines =<< trim END + vim9script + + class Base1 + endclass + + class Base2 + endclass + + var o = Base2.new() + instanceof(o, Base1, Base2, 3) + END + v9.CheckSourceFailure(lines, 'E693: Class or class typealias required for argument 4', 10) + + lines =<< trim END + vim9script + + class Base1 + endclass + + class Base2 + endclass + + def F() + var o = Base2.new() + instanceof(o, Base1, Base2, 3) + enddef + F() + END + v9.CheckSourceFailure(lines, 'E693: Class or class typealias required for argument 4') +enddef + +" Test for calling a method in the parent class that is extended partially. +" This used to fail with the 'E118: Too many arguments for function: Text' error +" message (Github issue #12524). +def Test_call_method_in_parent_class() + var lines =<< trim END + vim9script + + class Widget + var _lnum: number = 1 + + def SetY(lnum: number) + this._lnum = lnum + enddef + + def Text(): string + return '' + enddef + endclass + + class Foo extends Widget + def Text(): string + return '' + enddef + endclass + + def Stack(w1: Widget, w2: Widget): list + w1.SetY(1) + w2.SetY(2) + return [w1, w2] + enddef + + var foo1 = Foo.new() + var foo2 = Foo.new() + var l = Stack(foo1, foo2) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for calling methods from three levels of classes +def Test_multi_level_method_call() + var lines =<< trim END + vim9script + + var A_func1: number = 0 + var A_func2: number = 0 + var A_func3: number = 0 + var B_func2: number = 0 + var B_func3: number = 0 + var C_func3: number = 0 + + class A + def Func1() + A_func1 += 1 + enddef + + def Func2() + A_func2 += 1 + enddef + + def Func3() + A_func3 += 1 + enddef + endclass + + class B extends A + def Func2() + B_func2 += 1 + enddef + + def Func3() + B_func3 += 1 + enddef + endclass + + class C extends B + def Func3() + C_func3 += 1 + enddef + endclass + + def A_CallFuncs(a: A) + a.Func1() + a.Func2() + a.Func3() + enddef + + def B_CallFuncs(b: B) + b.Func1() + b.Func2() + b.Func3() + enddef + + def C_CallFuncs(c: C) + c.Func1() + c.Func2() + c.Func3() + enddef + + var cobj = C.new() + A_CallFuncs(cobj) + B_CallFuncs(cobj) + C_CallFuncs(cobj) + assert_equal(3, A_func1) + assert_equal(0, A_func2) + assert_equal(0, A_func3) + assert_equal(3, B_func2) + assert_equal(0, B_func3) + assert_equal(3, C_func3) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for using members from three levels of classes +def Test_multi_level_member_access() + var lines =<< trim END + vim9script + + class A + public var val1: number = 0 + endclass + + class B extends A + public var val2: number = 0 + endclass + + class C extends B + public var val3: number = 0 + endclass + + def A_members(a: A) + a.val1 += 1 + enddef + + def B_members(b: B) + b.val1 += 1 + b.val2 += 1 + enddef + + def C_members(c: C) + c.val1 += 1 + c.val2 += 1 + c.val3 += 1 + enddef + + var cobj = C.new() + A_members(cobj) + B_members(cobj) + C_members(cobj) + assert_equal(3, cobj.val1) + assert_equal(2, cobj.val2) + assert_equal(1, cobj.val3) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test expansion of with class methods. +def Test_stack_expansion_with_methods() + var lines =<< trim END + vim9script + + class C + def M1() + F0() + enddef + endclass + + def F0() + assert_match('\d\+_F\[1\]\.\.C\.M1\[1\]\.\.\d\+_F0\[1\]$', expand('')) + enddef + + def F() + C.new().M1() + enddef + + F() + END + v9.CheckSourceSuccess(lines) +enddef + +" Test the return type of the new() constructor +def Test_new_return_type() + # new() uses the default return type and there is no return statement + var lines =<< trim END + vim9script + + class C + var _bufnr: number + + def new(this._bufnr) + if !bufexists(this._bufnr) + this._bufnr = -1 + endif + enddef + endclass + + var c = C.new(12345) + assert_equal('object', typename(c)) + + var v1: C + v1 = C.new(12345) + assert_equal('object', typename(v1)) + + def F() + var v2: C + v2 = C.new(12345) + assert_equal('object', typename(v2)) + enddef + F() + END + v9.CheckSourceSuccess(lines) + + # new() uses the default return type and an empty 'return' statement + lines =<< trim END + vim9script + + class C + var _bufnr: number + + def new(this._bufnr) + if !bufexists(this._bufnr) + this._bufnr = -1 + return + endif + enddef + endclass + + var c = C.new(12345) + assert_equal('object', typename(c)) + + var v1: C + v1 = C.new(12345) + assert_equal('object', typename(v1)) + + def F() + var v2: C + v2 = C.new(12345) + assert_equal('object', typename(v2)) + enddef + F() + END + v9.CheckSourceSuccess(lines) + + # new() uses "any" return type and returns "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 + enddef + endclass + END + 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 C + var _state: dict + + def new(): dict + this._state = {} + return this._state + enddef + endclass + + var c = C.new() + assert_equal('object', typename(c)) + END + v9.CheckSourceFailure(lines, 'E1365: Cannot use a return type with the "new" method', 9) +enddef + +" Test for checking a member initialization type at run time. +def Test_runtime_type_check_for_member_init() + var lines =<< trim END + vim9script + + var retnum: bool = false + + def F(): any + retnum = !retnum + if retnum + return 1 + else + return "hello" + endif + enddef + + class C + var _foo: bool = F() + endclass + + var c1 = C.new() + var c2 = C.new() + END + v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected bool but got string', 0) +enddef + +" Test for locking a variable referring to an object and reassigning to another +" object. +def Test_lockvar_object() + var lines =<< trim END + vim9script + + class C + var val: number + def new(this.val) + enddef + endclass + + var some_dict: dict = { a: C.new(1), b: C.new(2), c: C.new(3), } + lockvar 2 some_dict + + var current: C + current = some_dict['c'] + assert_equal(3, current.val) + current = some_dict['b'] + assert_equal(2, current.val) + + def F() + current = some_dict['c'] + enddef + + def G() + current = some_dict['b'] + enddef + + F() + assert_equal(3, current.val) + G() + assert_equal(2, current.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 + lines =<< trim END + vim9script + + 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') + + # 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() + END + v9.CheckSourceFailure(lines, 'E1335: Variable "val3" in class "C" is not writable') + + # read-only lockvar of def argument variable + lines =<< trim END + vim9script + + class C + var val4: number + endclass + def Lock(o: C) + lockvar o.val4 + enddef + Lock(C.new(3)) + END + v9.CheckSourceFailure(lines, 'E1335: Variable "val4" in class "C" is not writable') + + # TODO: the following tests use type "any" for argument. Need a run time + # check for access. Probably OK as is for now. + + # read-only lockvar from object method arg + lines =<< trim END + vim9script + + class C + var val5: number + def Lock(o_any: any) + lockvar o_any.val5 + enddef + endclass + var o = C.new(3) + o.Lock(C.new(5)) + END + v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o_any.val5" in class "C"') + + # read-only lockvar from class method arg + lines =<< trim END + vim9script + + class C + var val6: number + static def Lock(o_any: any) + lockvar o_any.val6 + enddef + endclass + var o = C.new(3) + C.Lock(o) + END + v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o_any.val6" in class "C"') + + # + # lockvar of public object variable + # + + # 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, 'E1391: Cannot (un)lock variable "this.val1" in class "C"', 1) + + # lockvar from scriptlevel + lines =<< trim END + vim9script + + 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) + + # lockvar of scriptlevel variable from def + lines =<< trim END + vim9script + + class C + public var val3: number + endclass + var o = C.new(3) + def Lock() + lockvar o.val3 + enddef + Lock() + END + v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o.val3" in class "C"', 1) + + # lockvar of def argument variable + lines =<< trim END + vim9script + + 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) + + # lockvar from object method arg + lines =<< trim END + vim9script + + class C + public var val5: number + def Lock(o_any: any) + lockvar o_any.val5 + enddef + endclass + var o = C.new(3) + o.Lock(C.new(5)) + END + v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o_any.val5" in class "C"', 1) + + # lockvar from class method arg + lines =<< trim END + vim9script + + class C + public var val6: number + static def Lock(o_any: any) + lockvar o_any.val6 + enddef + endclass + var o = C.new(3) + C.Lock(o) + END + v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o_any.val6" in class "C"', 1) +enddef + +" Test trying to lock a class variable from various places +def Test_lockvar_class_variable() + + # lockvar bare static from object method + var lines =<< trim END + vim9script + + class C + public static var sval1: number + def Lock() + lockvar sval1 + enddef + endclass + var o = C.new() + o.Lock() + END + v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval1" in class "C"', 1) + + # lockvar C.static from object method + lines =<< trim END + vim9script + + class C + public static var sval2: number + def Lock() + lockvar C.sval2 + enddef + endclass + var o = C.new() + o.Lock() + END + v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval2" in class "C"', 1) + + # lockvar bare static from class method + lines =<< trim END + vim9script + + class C + public static var sval3: number + static def Lock() + lockvar sval3 + enddef + endclass + C.Lock() + END + v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval3" in class "C"', 1) + + # lockvar C.static from class method + lines =<< trim END + vim9script + + class C + public static var sval4: number + static def Lock() + lockvar C.sval4 + enddef + endclass + C.Lock() + END + v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval4" in class "C"', 1) + + # lockvar C.static from script level + lines =<< trim END + vim9script + + class C + public static var sval5: number + endclass + lockvar C.sval5 + END + v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval5" in class "C"', 6) + + # lockvar o.static from script level + lines =<< trim END + vim9script + + class C + public static var sval6: number + endclass + var o = C.new() + lockvar o.sval6 + END + v9.CheckSourceFailure(lines, 'E1375: Class variable "sval6" accessible only using class "C"', 7) +enddef + +" Test locking an argument to :def +def Test_lockvar_argument() + # Lockvar a function arg + var lines =<< trim END + vim9script + + def Lock(val: any) + lockvar val + enddef + + var d = {a: 1, b: 2} + Lock(d) + + d->extend({c: 3}) + END + v9.CheckSourceFailure(lines, 'E741: Value is locked: extend() argument') + + # Lockvar a function arg. Verify "sval" is interpreted as argument and not a + # class member in "C". This tests lval_root_is_arg. + lines =<< trim END + vim9script + + class C + public static var sval: list + endclass + + def Lock2(sval: any) + lockvar sval + enddef + + var o = C.new() + Lock2(o) + END + v9.CheckSourceSuccess(lines) + + # Lock a class. + lines =<< trim END + vim9script + + class C + public static var sval: list + endclass + + def Lock2(sval: any) + lockvar sval + enddef + + Lock2(C) + END + v9.CheckSourceFailure(lines, 'E1405: Class "C" cannot be used as a value') + + # Lock an object. + lines =<< trim END + vim9script + + class C + public static var sval: list + endclass + + def Lock2(sval: any) + lockvar sval + enddef + + Lock2(C.new()) + END + v9.CheckSourceSuccess(lines) + + # In this case (unlike previous) "lockvar sval" is a class member. + lines =<< trim END + vim9script + + class C + public static var sval: list + def Lock2() + lockvar sval + enddef + endclass + + + var o = C.new() + o.Lock2() + END + v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval" in class "C"', 1) +enddef + +" Test that this can be locked without error +def Test_lockvar_this() + # lockvar this + var lines =<< trim END + vim9script + class C + def TLock() + lockvar this + enddef + endclass + var o = C.new() + o.TLock() + END + v9.CheckSourceSuccess(lines) + + # lockvar four (four letter word, but not this) + lines =<< trim END + vim9script + class C + def TLock4() + var four: number + lockvar four + enddef + endclass + var o = C.new() + o.TLock4() + END + v9.CheckSourceFailure(lines, 'E1178: Cannot lock or unlock a local variable') + + # lockvar this5; "this" + one char, 5 letter word, starting with "this" + lines =<< trim END + vim9script + class C + def TLock5() + var this5: number + lockvar this5 + enddef + endclass + var o = C.new() + o.TLock5() + END + v9.CheckSourceFailure(lines, 'E1178: Cannot lock or unlock a local variable') +enddef + +" Test some general lockvar cases +def Test_lockvar_general() + # lockvar an object and a class. It does nothing + var lines =<< trim END + vim9script + class C + endclass + var o = C.new() + lockvar o + lockvar C + END + v9.CheckSourceSuccess(lines) + + # Lock a list element that's nested in an object variable from a :def + lines =<< trim END + vim9script + + class C + public var val: list> = [ [1], [2], [3] ] + endclass + def Lock2(obj: any) + lockvar obj.val[1] + enddef + + var o = C.new() + Lock2(o) + o.val[0] = [9] + assert_equal([ [9], [2], [3] ], o.val) + try + o.val[1] = [999] + call assert_false(true, 'assign should have failed') + catch + assert_exception('E741:') + endtry + o.val[2] = [8] + assert_equal([ [9], [2], [8] ], o.val) + END + v9.CheckSourceSuccess(lines) + + # Lock a list element that's nested in an object variable from scriptlevel + lines =<< trim END + vim9script + + class C + public var val: list> = [ [1], [2], [3] ] + endclass + + var o = C.new() + lockvar o.val[1] + o.val[0] = [9] + assert_equal([ [9], [2], [3] ], o.val) + try + o.val[1] = [999] + call assert_false(true, 'assign should have failed') + catch + assert_exception('E741:') + endtry + o.val[2] = [8] + assert_equal([ [9], [2], [8] ], o.val) + END + v9.CheckSourceSuccess(lines) + + # lock a script level variable from an object method + lines =<< trim END + vim9script + + class C + def Lock() + lockvar l + enddef + endclass + + var l = [1] + C.new().Lock() + l[0] = 11 + END + v9.CheckSourceFailure(lines, 'E741: Value is locked: l[0] = 11', 11) + + # lock a list element referenced by a protected object variable + # in an object fetched via a script level list + lines =<< trim END + vim9script + + class C + var _v1: list> + def Lock() + lockvar lc[0]._v1[1] + enddef + endclass + + var l = [[1], [2], [3]] + var o = C.new(l) + var lc: list = [ o ] + + o.Lock() + l[0] = [22] + l[1] = [33] + END + v9.CheckSourceFailure(lines, 'E741: Value is locked: l[1] = [33]', 16) + + # similar to the previous test, except the locking code is executing + # in a class that does not own the protected variable. + # Note that the locking code is in a class has a protected variable of + # the same name. + lines =<< trim END + vim9script + + class C2 + var _v1: list> + def Lock(obj: any) + lockvar lc[0]._v1[1] + enddef + endclass + + class C + var _v1: list> + endclass + + var l = [[1], [2], [3]] + var o = C.new(l) + var lc: list = [ o ] + + var o2 = C2.new() + o2.Lock(o) + END + v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_v1" in class "C"') +enddef + +" Test builtin islocked() +def Test_lockvar_islocked() + # Can't lock class/object variable + # Lock class/object variable's value + # Lock item of variable's value (a list item) + # variable is at index 1 within class/object + var lines =<< trim END + vim9script + + class C + var o0: list> = [ [0], [1], [2]] + var o1: list> = [[10], [11], [12]] + static var c0: list> = [[20], [21], [22]] + static var c1: list> = [[30], [31], [32]] + endclass + + def LockIt(arg: any) + lockvar arg + enddef + + def UnlockIt(arg: any) + unlockvar arg + enddef + + var obj = C.new() + #lockvar obj.o1 # can't lock something you can't write to + + try + lockvar obj.o1 # can't lock something you can't write to + call assert_false(1, '"lockvar obj.o1" should have failed') + catch + call assert_exception('E1335:') + endtry + + LockIt(obj.o1) # but can lock it's value + assert_equal(1, islocked("obj.o1")) + assert_equal(1, islocked("obj.o1[0]")) + assert_equal(1, islocked("obj.o1[1]")) + UnlockIt(obj.o1) + assert_equal(0, islocked("obj.o1")) + assert_equal(0, islocked("obj.o1[0]")) + + lockvar obj.o1[0] + assert_equal(0, islocked("obj.o1")) + assert_equal(1, islocked("obj.o1[0]")) + assert_equal(0, islocked("obj.o1[1]")) + unlockvar obj.o1[0] + assert_equal(0, islocked("obj.o1")) + assert_equal(0, islocked("obj.o1[0]")) + + # Same thing, but with a static + + try + lockvar C.c1 # can't lock something you can't write to + call assert_false(1, '"lockvar C.c1" should have failed') + catch + call assert_exception('E1335:') + endtry + + LockIt(C.c1) # but can lock it's value + assert_equal(1, islocked("C.c1")) + assert_equal(1, islocked("C.c1[0]")) + assert_equal(1, islocked("C.c1[1]")) + UnlockIt(C.c1) + assert_equal(0, islocked("C.c1")) + assert_equal(0, islocked("C.c1[0]")) + + lockvar C.c1[0] + assert_equal(0, islocked("C.c1")) + assert_equal(1, islocked("C.c1[0]")) + assert_equal(0, islocked("C.c1[1]")) + unlockvar C.c1[0] + assert_equal(0, islocked("C.c1")) + assert_equal(0, islocked("C.c1[0]")) + END + v9.CheckSourceSuccess(lines) + + # Do islocked() from an object method + # and then from a class method + lines =<< trim END + vim9script + + var l0o0 = [ [0], [1], [2]] + var l0o1 = [ [10], [11], [12]] + var l0c0 = [[120], [121], [122]] + var l0c1 = [[130], [131], [132]] + + class C0 + var o0: list> = l0o0 + var o1: list> = l0o1 + static var c0: list> = l0c0 + static var c1: list> = l0c1 + def Islocked(arg: string): number + return islocked(arg) + enddef + static def SIslocked(arg: string): number + return islocked(arg) + enddef + endclass + + var l2o0 = [[20000], [20001], [20002]] + var l2o1 = [[20010], [20011], [20012]] + var l2c0 = [[20120], [20121], [20122]] + var l2c1 = [[20130], [20131], [20132]] + + class C2 + var o0: list> = l2o0 + var o1: list> = l2o1 + static var c0: list> = l2c0 + static var c1: list> = l2c1 + def Islocked(arg: string): number + return islocked(arg) + enddef + static def SIslocked(arg: string): number + return islocked(arg) + enddef + endclass + + var obj0 = C0.new() + var obj2 = C2.new() + + var l = [ obj0, null_object, obj2 ] + + # lock list, object func access through script var expr + assert_equal(0, obj0.Islocked("l[0].o0")) + assert_equal(0, obj0.Islocked("l[0].o0[2]")) + lockvar l0o0 + assert_equal(1, obj0.Islocked("l[0].o0")) + assert_equal(1, obj0.Islocked("l[0].o0[2]")) + + #echo "check-b" obj2.Islocked("l[1].o1") # NULL OBJECT + + # lock list element, object func access through script var expr + lockvar l0o1[1] + assert_equal(0, obj0.Islocked("this.o1[0]")) + assert_equal(1, obj0.Islocked("this.o1[1]")) + + assert_equal(0, obj0.Islocked("this.o1")) + lockvar l0o1 + assert_equal(1, obj0.Islocked("this.o1")) + unlockvar l0o1 + + lockvar l0c1[1] + + # static by class name member expr from same class + assert_equal(0, obj0.Islocked("C0.c1[0]")) + assert_equal(1, obj0.Islocked("C0.c1[1]")) + # static by bare name member expr from same class + assert_equal(0, obj0.Islocked("c1[0]")) + assert_equal(1, obj0.Islocked("c1[1]")) + + # static by class name member expr from other class + assert_equal(0, obj2.Islocked("C0.c1[0]")) + assert_equal(1, obj2.Islocked("C0.c1[1]")) + # static by bare name member expr from other class + assert_equal(0, obj2.Islocked("c1[0]")) + assert_equal(0, obj2.Islocked("c1[1]")) + + + # static by bare name in same class + assert_equal(0, obj0.Islocked("c0")) + lockvar l0c0 + assert_equal(1, obj0.Islocked("c0")) + + # + # similar stuff, but use static method + # + + unlockvar l0o0 + + # lock list, object func access through script var expr + assert_equal(0, C0.SIslocked("l[0].o0")) + assert_equal(0, C0.SIslocked("l[0].o0[2]")) + lockvar l0o0 + assert_equal(1, C0.SIslocked("l[0].o0")) + assert_equal(1, C0.SIslocked("l[0].o0[2]")) + + unlockvar l0o1 + + # can't access "this" from class method + try + C0.SIslocked("this.o1[0]") + call assert_0(1, '"C0.SIslocked("this.o1[0]")" should have failed') + catch + call assert_exception('E121: Undefined variable: this') + endtry + + lockvar l0c1[1] + + # static by class name member expr from same class + assert_equal(0, C0.SIslocked("C0.c1[0]")) + assert_equal(1, C0.SIslocked("C0.c1[1]")) + # static by bare name member expr from same class + assert_equal(0, C0.SIslocked("c1[0]")) + assert_equal(1, C0.SIslocked("c1[1]")) + + # static by class name member expr from other class + assert_equal(0, C2.SIslocked("C0.c1[0]")) + assert_equal(1, C2.SIslocked("C0.c1[1]")) + # static by bare name member expr from other class + assert_equal(0, C2.SIslocked("c1[0]")) + assert_equal(0, C2.SIslocked("c1[1]")) + + + # static by bare name in same class + unlockvar l0c0 + assert_equal(0, C0.SIslocked("c0")) + lockvar l0c0 + assert_equal(1, C0.SIslocked("c0")) + END + v9.CheckSourceSuccess(lines) + + # Check islocked class/object from various places. + lines =<< trim END + vim9script + + class C + def Islocked(arg: string): number + return islocked(arg) + enddef + static def SIslocked(arg: string): number + return islocked(arg) + enddef + endclass + var obj = C.new() + + # object method + assert_equal(0, obj.Islocked("this")) + assert_equal(0, obj.Islocked("C")) + + # class method + ### assert_equal(0, C.SIslocked("this")) + assert_equal(0, C.SIslocked("C")) + + #script level + var v: number + v = islocked("C") + assert_equal(0, v) + v = islocked("obj") + assert_equal(0, v) + END + v9.CheckSourceSuccess(lines) +enddef + +def Test_lockvar_islocked_notfound() + # Try non-existent things + var lines =<< trim END + vim9script + + class C + def Islocked(arg: string): number + return islocked(arg) + enddef + static def SIslocked(arg: string): number + return islocked(arg) + enddef + endclass + var obj = C.new() + assert_equal(-1, obj.Islocked("anywhere")) + assert_equal(-1, C.SIslocked("notanywhere")) + END + v9.CheckSourceSuccess(lines) + + # Something not found of the form "name1.name2" is an error + lines =<< trim END + vim9script + + islocked("one.two") + END + v9.CheckSourceFailure(lines, 'E121: Undefined variable: one') + + lines =<< trim END + vim9script + + class C + var val = { key: "value" } + def Islocked(arg: string): number + return islocked(arg) + enddef + endclass + var obj = C.new() + obj.Islocked("this.val.not_there")) + END + v9.CheckSourceFailure(lines, 'E716: Key not present in Dictionary: "not_there"') + + lines =<< trim END + vim9script + + class C + def Islocked(arg: string): number + return islocked(arg) + enddef + endclass + var obj = C.new() + obj.Islocked("this.notobjmember") + END + v9.CheckSourceFailure(lines, 'E1326: Variable "notobjmember" not found in object "C"') + + # access a script variable through methods + lines =<< trim END + vim9script + + var l = [1] + class C + def Islocked(arg: string): number + return islocked(arg) + enddef + static def SIslocked(arg: string): number + return islocked(arg) + enddef + endclass + var obj = C.new() + assert_equal(0, obj.Islocked("l")) + assert_equal(0, C.SIslocked("l")) + lockvar l + assert_equal(1, obj.Islocked("l")) + assert_equal(1, C.SIslocked("l")) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for a protected object method +def Test_private_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) +enddef + +" Test for an protected class method +def Test_private_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 + + 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) + + # Declare a class method and a class protected method with the same name + lines =<< trim END + vim9script + + class A + static def _Foo() + enddef + static def Foo() + enddef + endclass + var a = A.new() + END + v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 7) + + # Try calling a class protected method from another class + lines =<< trim END + vim9script + + class A + static def _Foo(): number + return 1234 + enddef + endclass + class B + def Foo(): number + return A._Foo() + enddef + endclass + var b = B.new() + assert_equal(1234, b.Foo()) + END + v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1) + + # 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()) + END + v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo()', 1) + + # Call a protected class method from a child class protected class method + lines =<< trim END + vim9script + class A + static def _Foo(): number + return 1234 + enddef + endclass + class B extends A + def Bar() + enddef + endclass + class C extends B + static def Baz(): number + return A._Foo() + enddef + endclass + assert_equal(1234, C.Baz()) + END + v9.CheckSourceFailure(lines, 'E1366: Cannot access 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() + enddef + endclass + class C extends B + def Baz(): number + enddef + endclass + var c = C.new() + assert_equal(1234, C._Foo()) + END + v9.CheckSourceFailure(lines, 'E1325: Method "_Foo" not found in class "C"', 16) +enddef + +" Test for using the return value of a class/object method as a function +" argument. +def Test_objmethod_funcarg() + var lines =<< trim END + vim9script + + class C + def Foo(): string + return 'foo' + enddef + endclass + + def Bar(a: number, s: string): string + return s + enddef + + def Baz(c: C) + assert_equal('foo', Bar(10, c.Foo())) + enddef + + var t = C.new() + Baz(t) + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + + class C + static def Foo(): string + return 'foo' + enddef + endclass + + def Bar(a: number, s: string): string + return s + enddef + + def Baz() + assert_equal('foo', Bar(10, C.Foo())) + enddef + + Baz() + END + v9.CheckSourceSuccess(lines) +enddef + +def Test_static_inheritence() + # subclasses get their own static copy + var lines =<< trim END + vim9script + + class A + static var _svar: number + var _mvar: number + def new() + _svar = 1 + this._mvar = 101 + enddef + def AccessObject(): number + return this._mvar + enddef + def AccessStaticThroughObject(): number + return _svar + enddef + endclass + + class B extends A + def new() + this._mvar = 102 + enddef + endclass + + class C extends B + def new() + this._mvar = 103 + enddef + + def AccessPrivateStaticThroughClassName(): number + assert_equal(1, A._svar) + return 444 + enddef + endclass + + var oa = A.new() + var ob = B.new() + var oc = C.new() + assert_equal(101, oa.AccessObject()) + assert_equal(102, ob.AccessObject()) + assert_equal(103, oc.AccessObject()) + + assert_fails('echo oc.AccessPrivateStaticThroughClassName()', 'E1333: Cannot access protected variable "_svar" in class "A"') + + # verify object properly resolves to correct static + assert_equal(1, oa.AccessStaticThroughObject()) + assert_equal(1, ob.AccessStaticThroughObject()) + assert_equal(1, oc.AccessStaticThroughObject()) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for declaring duplicate object and class members +def Test_dup_member_variable() + # Duplicate member variable + var lines =<< trim END + vim9script + class C + var val = 10 + var val = 20 + endclass + END + v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4) + + # Duplicate protected member variable + lines =<< trim END + vim9script + class C + var _val = 10 + var _val = 20 + endclass + END + v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 4) + + # Duplicate public member variable + lines =<< trim END + vim9script + class C + public var val = 10 + public var val = 20 + endclass + END + v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4) + + # Duplicate protected member variable + lines =<< trim END + vim9script + class C + var val = 10 + var _val = 20 + endclass + END + v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 4) + + # Duplicate public and protected member variable + lines =<< trim END + vim9script + class C + var _val = 20 + public var val = 10 + endclass + END + v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4) + + # Duplicate class member variable + lines =<< trim END + vim9script + class C + static var s: string = "abc" + static var _s: string = "def" + endclass + END + v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _s', 4) + + # Duplicate public and protected class member variable + lines =<< trim END + vim9script + class C + public static var s: string = "abc" + static var _s: string = "def" + endclass + END + v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _s', 4) + + # Duplicate class and object member variable + lines =<< trim END + vim9script + 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, 'E1369: Duplicate variable: val', 4) + + # Duplicate object member variable in a derived class + lines =<< trim END + vim9script + class A + var val = 10 + endclass + class B extends A + endclass + class C extends B + var val = 20 + endclass + END + v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 9) + + # Duplicate object protected member variable in a derived class + lines =<< trim END + vim9script + class A + var _val = 10 + endclass + class B extends A + endclass + class C extends B + var _val = 20 + endclass + END + v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 9) + + # Duplicate object protected member variable in a derived class + lines =<< trim END + vim9script + class A + var val = 10 + endclass + class B extends A + endclass + class C extends B + var _val = 20 + endclass + END + v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 9) + + # Duplicate object member variable in a derived class + lines =<< trim END + vim9script + class A + var _val = 10 + endclass + class B extends A + endclass + class C extends B + var val = 20 + endclass + END + v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 9) + + # Two member variables with a common prefix + 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_private_member_access_outside_class() + # protected object member variable + var lines =<< trim END + vim9script + class A + var _val = 10 + def GetVal(): number + return this._val + enddef + endclass + def T() + var a = A.new() + a._val = 20 + enddef + T() + END + v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_val" in class "A"', 2) + + # access a non-existing protected object member variable + lines =<< trim END + vim9script + class A + 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) + + # protected static member variable + 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) + + # protected static member variable + 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, 'E1375: Class variable "_val" accessible only using class "A"', 2) + + # protected static class variable + lines =<< trim END + vim9script + class A + 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) + + # protected static class variable + lines =<< trim END + vim9script + class A + static var _val = 10 + endclass + def T() + A._val = 3 + enddef + T() + END + v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_val" 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 + 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 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() + a.val = 20 + enddef + T() + END + v9.CheckSourceFailure(lines, 'E1335: Variable "val" in class "A" is not writable', 2) +enddef + +" Test for reading and writing a class member from a def function +def Test_modify_class_member_from_def_function() + var lines =<< trim END + vim9script + class A + var var1: number = 10 + public static var var2: list = [1, 2] + public static var var3: dict = {a: 1, b: 2} + static var _priv_var4: number = 40 + endclass + def T() + assert_equal([1, 2], A.var2) + assert_equal({a: 1, b: 2}, A.var3) + A.var2 = [3, 4] + A.var3 = {c: 3, d: 4} + assert_equal([3, 4], A.var2) + assert_equal({c: 3, d: 4}, A.var3) + assert_fails('echo A._priv_var4', 'E1333: Cannot access protected variable "_priv_var4" in class "A"') + enddef + T() + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for accessing a class member variable using an object +def Test_class_variable_access_using_object() + var lines =<< trim END + vim9script + class A + public static var svar1: list = [1] + public static var svar2: list = [2] + endclass + + A.svar1->add(3) + A.svar2->add(4) + assert_equal([1, 3], A.svar1) + assert_equal([2, 4], A.svar2) + + def Foo() + A.svar1->add(7) + A.svar2->add(8) + assert_equal([1, 3, 7], A.svar1) + assert_equal([2, 4, 8], A.svar2) + enddef + Foo() + END + v9.CheckSourceSuccess(lines) + + # Cannot read from a class variable using an object in script context + lines =<< trim END + vim9script + class A + public var var1: number + public static var svar2: list = [1] + endclass + + var a = A.new() + echo a.svar2 + END + v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 8) + + # Cannot write to a class variable using an object in script context + lines =<< trim END + vim9script + class A + public var var1: number + public static var svar2: list = [1] + endclass + + var a = A.new() + a.svar2 = [2] + END + v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 8) + + # Cannot read from a class variable using an object in def method context + lines =<< trim END + vim9script + class A + public var var1: number + public static var svar2: list = [1] + endclass + + def T() + var a = A.new() + echo a.svar2 + enddef + T() + END + v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 2) + + # Cannot write to a class variable using an object in def method context + lines =<< trim END + vim9script + class A + public var var1: number + public static var svar2: list = [1] + endclass + + def T() + var a = A.new() + a.svar2 = [2] + enddef + T() + END + v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 2) +enddef + +" Test for using a interface method using a child object +def Test_interface_method_from_child() + var lines =<< trim END + vim9script + + interface A + def Foo(): string + endinterface + + class B implements A + def Foo(): string + return 'foo' + enddef + endclass + + class C extends B + def Bar(): string + return 'bar' + enddef + endclass + + def T1(a: A) + assert_equal('foo', a.Foo()) + enddef + + def T2(b: B) + assert_equal('foo', b.Foo()) + enddef + + var c = C.new() + T1(c) + T2(c) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for using an interface method using a child object when it is overridden +" by the child class. +" FIXME: This test fails. +" def Test_interface_overridden_method_from_child() +" var lines =<< trim END +" vim9script +" +" interface A +" def Foo(): string +" endinterface +" +" class B implements A +" def Foo(): string +" return 'b-foo' +" enddef +" endclass +" +" class C extends B +" def Bar(): string +" return 'bar' +" enddef +" def Foo(): string +" return 'c-foo' +" enddef +" endclass +" +" def T1(a: A) +" assert_equal('c-foo', a.Foo()) +" enddef +" +" def T2(b: B) +" assert_equal('c-foo', b.Foo()) +" enddef +" +" var c = C.new() +" T1(c) +" T2(c) +" END +" v9.CheckSourceSuccess(lines) +" enddef + +" Test for abstract methods +def Test_abstract_method() + # Use two abstract methods + var lines =<< trim END + vim9script + abstract class A + def M1(): number + return 10 + enddef + abstract def M2(): number + abstract def M3(): number + endclass + class B extends A + def M2(): number + return 20 + enddef + def M3(): number + return 30 + enddef + endclass + var b = B.new() + assert_equal([10, 20, 30], [b.M1(), b.M2(), b.M3()]) + END + v9.CheckSourceSuccess(lines) + + # Don't define an abstract method + lines =<< trim END + vim9script + abstract class A + abstract def Foo() + endclass + class B extends A + endclass + END + v9.CheckSourceFailure(lines, 'E1373: Abstract method "Foo" is not implemented', 6) + + # Use abstract method in a concrete class + lines =<< trim END + vim9script + class A + abstract def Foo() + endclass + class B extends A + endclass + END + v9.CheckSourceFailure(lines, 'E1372: Abstract method "abstract def Foo()" cannot be defined in a concrete class', 3) + + # Use abstract method in an interface + lines =<< trim END + vim9script + interface A + abstract def Foo() + endinterface + class B implements A + def Foo() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1404: Abstract cannot be used in an interface', 3) + + # Use abstract static method in an interface + lines =<< trim END + vim9script + interface A + abstract static def Foo() + enddef + endinterface + END + v9.CheckSourceFailure(lines, 'E1404: Abstract cannot be used in an interface', 3) + + # Use abstract static variable in an interface + lines =<< trim END + vim9script + interface A + abstract static foo: number = 10 + endinterface + END + v9.CheckSourceFailure(lines, 'E1404: Abstract cannot be used in an interface', 3) + + # Abbreviate the "abstract" keyword + lines =<< trim END + vim9script + class A + abs def Foo() + endclass + END + v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: abs def Foo()', 3) + + # Use "abstract" with a member variable + lines =<< trim END + vim9script + abstract class A + abstract this.val = 10 + endclass + END + v9.CheckSourceFailure(lines, 'E1371: Abstract must be followed by "def"', 3) + + # Use a static abstract method + lines =<< trim END + vim9script + abstract class A + abstract static def Foo(): number + endclass + END + v9.CheckSourceFailure(lines, 'E1371: Abstract must be followed by "def"', 3) + + # Type mismatch between abstract method and concrete method + lines =<< trim END + vim9script + abstract class A + abstract def Foo(a: string, b: number): list + endclass + class B extends A + def Foo(a: number, b: string): list + return [] + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1383: Method "Foo": type mismatch, expected func(string, number): list but got func(number, string): list', 9) + + # Invoke an abstract method from a def function + lines =<< trim END + vim9script + abstract class A + abstract def Foo(): list + endclass + class B extends A + def Foo(): list + return [3, 5] + enddef + endclass + def Bar(c: B) + assert_equal([3, 5], c.Foo()) + enddef + var b = B.new() + Bar(b) + END + v9.CheckSourceSuccess(lines) + + # Use a static method in an abstract class + lines =<< trim END + vim9script + abstract class A + static def Foo(): string + return 'foo' + enddef + endclass + assert_equal('foo', A.Foo()) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for calling a class method from a subclass +def Test_class_method_call_from_subclass() + # class method call from a subclass + var lines =<< trim END + vim9script + + class A + static def Foo() + echo "foo" + enddef + endclass + + class B extends A + def Bar() + Foo() + enddef + endclass + + var b = B.new() + b.Bar() + END + v9.CheckSourceFailure(lines, 'E1384: Class method "Foo" accessible only inside class "A"', 1) +enddef + +" Test for calling a class method using an object in a def function context and +" script context. +def Test_class_method_call_using_object() + # script context + var lines =<< trim END + vim9script + class A + static def Foo(): list + return ['a', 'b'] + enddef + def Bar() + assert_equal(['a', 'b'], A.Foo()) + assert_equal(['a', 'b'], Foo()) + enddef + endclass + + def T() + assert_equal(['a', 'b'], A.Foo()) + var t_a = A.new() + t_a.Bar() + enddef + + assert_equal(['a', 'b'], A.Foo()) + var a = A.new() + a.Bar() + T() + END + v9.CheckSourceSuccess(lines) + + # script context + lines =<< trim END + vim9script + class A + static def Foo(): string + return 'foo' + enddef + endclass + + var a = A.new() + assert_equal('foo', a.Foo()) + END + v9.CheckSourceFailure(lines, 'E1385: Class method "Foo" accessible only using class "A"', 9) + + # def function context + lines =<< trim END + vim9script + class A + static def Foo(): string + return 'foo' + enddef + endclass + + def T() + var a = A.new() + assert_equal('foo', a.Foo()) + enddef + T() + END + v9.CheckSourceFailure(lines, 'E1385: Class method "Foo" accessible only using class "A"', 2) +enddef + +def Test_class_variable() + 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 + endclass + + class B extends A + endclass + + assert_equal(10, A.val) + A.ClassFunc() + var a = A.new() + a.ObjFunc() + var b = B.new() + b.ObjFunc() + + def T1(a1: A) + a1.ObjFunc() + A.ClassFunc() + enddef + T1(b) + + A.val = 20 + assert_equal(20, A.val) + END + v9.CheckSourceSuccess(lines) + + # Modifying a parent class variable from a child class method + lines =<< trim END + vim9script + + class A + static var val: number = 10 + endclass + + class B extends A + static def ClassFunc() + val = 20 + enddef + endclass + B.ClassFunc() + END + v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1) + + # 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 + static def ClassFunc() + var i = val + enddef + endclass + B.ClassFunc() + END + 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 + + class A + static var val: number = 10 + endclass + + class B extends A + def ObjFunc() + val = 20 + enddef + endclass + var b = B.new() + b.ObjFunc() + END + v9.CheckSourceFailure(lines, 'E1374: Class variable "val" accessible only inside class "A"', 1) + + # Reading a parent class variable from a child object method + lines =<< trim END + vim9script + + 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, 'E1374: Class variable "val" accessible only inside class "A"', 1) + + # Modifying a class variable using an object at script level + lines =<< trim END + vim9script + + class A + static var val: number = 10 + 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 + endclass + var a = A.new() + var i = a.val + END + 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 + + class A + static var val: number = 10 + 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) + + # Reading a class variable using an object at function level + lines =<< trim END + vim9script + + class A + static var val: number = 10 + endclass + def T() + var a = A.new() + var i = a.val + enddef + T() + END + 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 + + class A + static val: number + endclass + END + 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 + + class A + static val: number = 10 + endclass + END + v9.CheckSourceFailure(lines, 'E1368: Static must be followed by "var" or "def"', 4) + + # Use old implicit var declaration syntax (type inferred) + lines =<< trim END + vim9script + + class A + static val = 10 + endclass + END + v9.CheckSourceFailure(lines, 'E1368: Static must be followed by "var" or "def"', 4) + + # Missing ":var" in "var" class variable declaration (without initialization) + lines =<< trim END + vim9script + + class A + static var: number + endclass + END + v9.CheckSourceFailure(lines, 'E1329: Invalid class variable declaration: static var: number', 4) + + # Missing ":var" in "var" class variable declaration (with initialization) + lines =<< trim END + vim9script + + class A + static var: number = 10 + endclass + END + v9.CheckSourceFailure(lines, 'E1329: Invalid class variable declaration: static var: number = 10', 4) + + # Missing ":var" in "var" class variable declaration (type inferred) + lines =<< trim END + vim9script + + class A + static var = 10 + endclass + END + v9.CheckSourceFailure(lines, 'E1329: Invalid class variable declaration: static var = 10', 4) + +enddef + +" Test for using a duplicate class method and class variable in a child class +def Test_dup_class_member() + # duplicate class variable, class method and overridden object method + var lines =<< trim END + vim9script + class A + static var sval = 100 + static def Check() + assert_equal(100, sval) + enddef + def GetVal(): number + return sval + enddef + endclass + + class B extends A + static var sval = 200 + static def Check() + assert_equal(200, sval) + enddef + def GetVal(): number + return sval + enddef + endclass + + def T1(aa: A): number + return aa.GetVal() + enddef + + def T2(bb: B): number + return bb.GetVal() + enddef + + assert_equal(100, A.sval) + assert_equal(200, B.sval) + var a = A.new() + assert_equal(100, a.GetVal()) + var b = B.new() + assert_equal(200, b.GetVal()) + assert_equal(200, T1(b)) + assert_equal(200, T2(b)) + END + v9.CheckSourceSuccess(lines) + + # duplicate class variable and class method + lines =<< trim END + vim9script + class A + static var sval = 100 + static def Check() + assert_equal(100, sval) + enddef + def GetVal(): number + return sval + enddef + endclass + + class B extends A + static var sval = 200 + static def Check() + assert_equal(200, sval) + enddef + endclass + + def T1(aa: A): number + return aa.GetVal() + enddef + + def T2(bb: B): number + return bb.GetVal() + enddef + + assert_equal(100, A.sval) + assert_equal(200, B.sval) + var a = A.new() + assert_equal(100, a.GetVal()) + var b = B.new() + assert_equal(100, b.GetVal()) + assert_equal(100, T1(b)) + assert_equal(100, T2(b)) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for calling an instance method using the class +def Test_instance_method_call_using_class() + # Invoke an object method using a class in script context + var lines =<< trim END + vim9script + class A + def Foo() + echo "foo" + enddef + endclass + A.Foo() + END + v9.CheckSourceFailure(lines, 'E1386: Object method "Foo" accessible only using class "A" object', 7) + + # Invoke an object method using a class in def function context + lines =<< trim END + vim9script + class A + def Foo() + echo "foo" + enddef + endclass + def T() + A.Foo() + enddef + T() + END + v9.CheckSourceFailure(lines, 'E1386: Object method "Foo" accessible only using class "A" object', 1) +enddef + +" Test for duplicate class method and instance method +def Test_dup_classmethod_objmethod() + # Duplicate instance method + var lines =<< trim END + vim9script + class A + static def Foo() + enddef + def Foo() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 6) + + # Duplicate protected instance method + lines =<< trim END + vim9script + class A + static def Foo() + enddef + def _Foo() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 6) + + # Duplicate class method + lines =<< trim END + vim9script + class A + def Foo() + enddef + static def Foo() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo', 6) + + # Duplicate protected class method + lines =<< trim END + vim9script + class A + def Foo() + enddef + static def _Foo() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 6) + + # Duplicate protected class and object method + lines =<< trim END + vim9script + class A + def _Foo() + enddef + static def _Foo() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo', 6) +enddef + +" 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 + class A + def Foo() + enddef + endclass + class B extends A + endclass + class C extends B + def _Foo() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1377: Access level of method "_Foo" is different in class "A"', 11) + + # Public method in subclass + lines =<< trim END + vim9script + class A + def _Foo() + enddef + endclass + class B extends A + endclass + class C extends B + def Foo() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1377: Access level of method "Foo" is different in class "A"', 11) +enddef + +def Test_extend_empty_class() + var lines =<< trim END + vim9script + class A + endclass + class B extends A + endclass + class C extends B + public static 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.CheckSourceSuccess(lines) +enddef + +" A interface cannot have a static variable or a static method or a private +" variable or a protected method or a public variable +def Test_interface_with_unsupported_members() + var lines =<< trim END + vim9script + interface A + static var num: number + endinterface + END + v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3) + + lines =<< trim END + vim9script + interface A + static var _num: number + endinterface + END + v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3) + + lines =<< trim END + vim9script + interface A + public static var num: number + endinterface + END + v9.CheckSourceFailure(lines, 'E1387: Public variable not supported in an interface', 3) + + lines =<< trim END + vim9script + interface A + public static var num: number + endinterface + END + v9.CheckSourceFailure(lines, 'E1387: Public variable not supported in an interface', 3) + + lines =<< trim END + vim9script + interface A + static var _num: number + endinterface + END + v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3) + + lines =<< trim END + vim9script + interface A + static def Foo(d: dict): list + endinterface + END + v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3) + + lines =<< trim END + vim9script + interface A + static def _Foo(d: dict): list + endinterface + END + v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3) + + lines =<< trim END + vim9script + interface A + var _Foo: list + endinterface + END + v9.CheckSourceFailure(lines, 'E1379: Protected variable not supported in an interface', 3) + + lines =<< trim END + vim9script + interface A + def _Foo(d: dict): list + endinterface + END + v9.CheckSourceFailure(lines, 'E1380: Protected method not supported in an interface', 3) +enddef + +" Test for extending an interface +def Test_extend_interface() + var lines =<< trim END + vim9script + interface A + var var1: list + def Foo() + endinterface + interface B extends A + var var2: dict + def Bar() + endinterface + class C implements A, B + var var1 = [1, 2] + def Foo() + enddef + var var2 = {a: '1'} + def Bar() + enddef + endclass + END + v9.CheckSourceSuccess(lines) + + # extending empty interface + lines =<< trim END + vim9script + interface A + endinterface + interface B extends A + endinterface + class C implements B + endclass + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + interface A + def Foo() + endinterface + interface B extends A + var var2: dict + endinterface + class C implements A, B + var var2 = {a: '1'} + endclass + END + v9.CheckSourceFailure(lines, 'E1349: Method "Foo" of interface "A" is not implemented', 10) + + lines =<< trim END + vim9script + interface A + def Foo() + endinterface + interface B extends A + var var2: dict + endinterface + class C implements A, B + def Foo() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1348: Variable "var2" of interface "B" is not implemented', 11) + + # interface cannot extend a class + lines =<< trim END + vim9script + class A + endclass + interface B extends A + endinterface + END + v9.CheckSourceFailure(lines, 'E1354: Cannot extend A', 5) + + # class cannot extend an interface + lines =<< trim END + vim9script + interface A + endinterface + class B extends A + endclass + END + v9.CheckSourceFailure(lines, 'E1354: Cannot extend A', 5) + + # interface cannot implement another interface + lines =<< trim END + vim9script + interface A + endinterface + interface B implements A + endinterface + END + v9.CheckSourceFailure(lines, 'E1381: Interface cannot use "implements"', 4) + + # interface cannot extend multiple interfaces + lines =<< trim END + vim9script + interface A + endinterface + interface B + endinterface + interface C extends A, B + endinterface + END + v9.CheckSourceFailure(lines, 'E1315: White space required after name: A, B', 6) + + # Variable type in an extended interface is of different type + lines =<< trim END + vim9script + interface A + var val1: number + endinterface + interface B extends A + var val2: string + endinterface + interface C extends B + var val1: string + var val2: number + endinterface + END + v9.CheckSourceFailure(lines, 'E1382: Variable "val1": type mismatch, expected number but got string', 11) +enddef + +" Test for a child class implementing an interface when some of the methods are +" defined in the parent class. +def Test_child_class_implements_interface() + var lines =<< trim END + vim9script + + interface Intf + def F1(): list> + def F2(): list> + def F3(): list> + var var1: list> + var var2: list> + var var3: list> + endinterface + + class A + def A1() + enddef + def F3(): list> + return [[3]] + enddef + var v1: list> = [[0]] + var var3 = [{c: 30}] + endclass + + class B extends A + def B1() + enddef + def F2(): list> + return [[2]] + enddef + var v2: list> = [[0]] + var var2 = [{b: 20}] + endclass + + class C extends B implements Intf + def C1() + enddef + def F1(): list> + return [[1]] + enddef + var v3: list> = [[0]] + var var1 = [{a: 10}] + endclass + + def T(if: Intf) + assert_equal([[1]], if.F1()) + assert_equal([[2]], if.F2()) + assert_equal([[3]], if.F3()) + assert_equal([{a: 10}], if.var1) + assert_equal([{b: 20}], if.var2) + assert_equal([{c: 30}], if.var3) + enddef + + var c = C.new() + T(c) + assert_equal([[1]], c.F1()) + assert_equal([[2]], c.F2()) + assert_equal([[3]], c.F3()) + assert_equal([{a: 10}], c.var1) + assert_equal([{b: 20}], c.var2) + assert_equal([{c: 30}], c.var3) + END + v9.CheckSourceSuccess(lines) + + # One of the interface methods is not found + lines =<< trim END + vim9script + + interface Intf + def F1() + def F2() + def F3() + endinterface + + class A + def A1() + enddef + endclass + + class B extends A + def B1() + enddef + def F2() + enddef + endclass + + class C extends B implements Intf + def C1() + enddef + def F1() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1349: Method "F3" of interface "Intf" is not implemented', 26) + + # One of the interface methods is of different type + lines =<< trim END + vim9script + + interface Intf + def F1() + def F2() + def F3() + endinterface + + class A + def F3(): number + return 0 + enddef + def A1() + enddef + endclass + + class B extends A + def B1() + enddef + def F2() + enddef + endclass + + class C extends B implements Intf + def C1() + enddef + def F1() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1383: Method "F3": type mismatch, expected func() but got func(): number', 29) + + # One of the interface variables is not present + lines =<< trim END + vim9script + + interface Intf + var var1: list> + var var2: list> + var var3: list> + endinterface + + class A + var v1: list> = [[0]] + endclass + + class B extends A + var v2: list> = [[0]] + var var2 = [{b: 20}] + endclass + + class C extends B implements Intf + var v3: list> = [[0]] + var var1 = [{a: 10}] + endclass + END + v9.CheckSourceFailure(lines, 'E1348: Variable "var3" of interface "Intf" is not implemented', 21) + + # One of the interface variables is of different type + lines =<< trim END + vim9script + + interface Intf + var var1: list> + var var2: list> + var var3: list> + endinterface + + class A + var v1: list> = [[0]] + var var3: list> + endclass + + class B extends A + var v2: list> = [[0]] + var var2 = [{b: 20}] + endclass + + class C extends B implements Intf + var v3: list> = [[0]] + var var1 = [{a: 10}] + endclass + END + v9.CheckSourceFailure(lines, 'E1382: Variable "var3": type mismatch, expected list> but got list>', 22) +enddef + +" Test for extending an interface with duplicate variables and methods +def Test_interface_extends_with_dup_members() + var lines =<< trim END + vim9script + interface A + var n1: number + def Foo1(): number + endinterface + interface B extends A + var n2: number + var n1: number + def Foo2(): number + def Foo1(): number + endinterface + class C implements B + var n1 = 10 + var n2 = 20 + def Foo1(): number + return 30 + enddef + def Foo2(): number + return 40 + enddef + endclass + def T1(a: A) + assert_equal(10, a.n1) + assert_equal(30, a.Foo1()) + enddef + def T2(b: B) + assert_equal(10, b.n1) + assert_equal(20, b.n2) + assert_equal(30, b.Foo1()) + assert_equal(40, b.Foo2()) + enddef + var c = C.new() + T1(c) + T2(c) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for using "any" type for a variable in a sub-class while it has a +" concrete type in the interface +def Test_implements_using_var_type_any() + var lines =<< trim END + vim9script + interface A + var val: list> + endinterface + class B implements A + var val = [{a: '1'}, {b: '2'}] + endclass + var b = B.new() + assert_equal([{a: '1'}, {b: '2'}], b.val) + END + v9.CheckSourceSuccess(lines) + + # initialize instance variable using a different type + lines =<< trim END + vim9script + interface A + var val: list> + endinterface + class B implements A + var val = {a: 1, b: 2} + endclass + var b = B.new() + END + v9.CheckSourceFailure(lines, 'E1382: Variable "val": type mismatch, expected list> but got dict', 1) +enddef + +" Test for assigning to a member variable in a nested class +def Test_nested_object_assignment() + var lines =<< trim END + vim9script + + class A + var value: number + endclass + + class B + var a: A = A.new() + endclass + + class C + var b: B = B.new() + endclass + + class D + var c: C = C.new() + endclass + + def T(da: D) + da.c.b.a.value = 10 + enddef + + var d = D.new() + T(d) + END + v9.CheckSourceFailure(lines, 'E1335: Variable "value" in class "A" is not writable', 1) +enddef + +" Test for calling methods using a null object +def Test_null_object_method_call() + # Calling a object method using a null object in script context + var lines =<< trim END + vim9script + + class C + def Foo() + assert_report('This method should not be executed') + enddef + endclass + + var o: C + o.Foo() + END + v9.CheckSourceFailure(lines, 'E1360: Using a null object', 10) + + # Calling a object method using a null object in def function context + lines =<< trim END + vim9script + + class C + def Foo() + assert_report('This method should not be executed') + enddef + endclass + + def T() + var o: C + o.Foo() + enddef + T() + END + v9.CheckSourceFailure(lines, 'E1360: Using a null object', 2) + + # Calling a object method through another class method using a null object in + # script context + lines =<< trim END + vim9script + + class C + def Foo() + assert_report('This method should not be executed') + enddef + + static def Bar(o_any: any) + var o_typed: C = o_any + o_typed.Foo() + enddef + endclass + + var o: C + C.Bar(o) + END + v9.CheckSourceFailure(lines, 'E1360: Using a null object', 2) + + # Calling a object method through another class method using a null object in + # def function context + lines =<< trim END + vim9script + + class C + def Foo() + assert_report('This method should not be executed') + enddef + + static def Bar(o_any: any) + var o_typed: C = o_any + o_typed.Foo() + enddef + endclass + + def T() + var o: C + C.Bar(o) + enddef + T() + END + v9.CheckSourceFailure(lines, 'E1360: Using a null object', 2) +enddef + +" Test for using a dict as an object member +def Test_dict_object_member() + var lines =<< trim END + vim9script + + class Context + public var state: dict = {} + def GetState(): dict + return this.state + enddef + endclass + + var ctx = Context.new() + ctx.state->extend({a: 1}) + ctx.state['b'] = 2 + assert_equal({a: 1, b: 2}, ctx.GetState()) + + def F() + ctx.state['c'] = 3 + assert_equal({a: 1, b: 2, c: 3}, ctx.GetState()) + enddef + F() + assert_equal(3, ctx.state.c) + ctx.state.c = 4 + assert_equal(4, ctx.state.c) + END + v9.CheckSourceSuccess(lines) +enddef + +" The following test was failing after 9.0.1914. This was caused by using a +" freed object from a previous method call. +def Test_freed_object_from_previous_method_call() + var lines =<< trim END + vim9script + + class Context + endclass + + class Result + endclass + + def Failure(): Result + return Result.new() + enddef + + def GetResult(ctx: Context): Result + return Failure() + enddef + + def Test_GetResult() + var ctx = Context.new() + var result = GetResult(ctx) + enddef + + Test_GetResult() + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for duplicate object and class variable +def Test_duplicate_variable() + # Object variable name is same as the class variable name + var lines =<< trim END + vim9script + class A + public static var sval: number + public var sval: number + endclass + var a = A.new() + END + v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: sval', 4) + + # Duplicate variable name and calling a class method + lines =<< trim END + vim9script + class A + public static var sval: number + public var sval: number + def F1() + echo this.sval + enddef + static def F2() + echo sval + enddef + endclass + A.F2() + END + v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: sval', 4) + + # Duplicate variable with an empty constructor + lines =<< trim END + vim9script + class A + public static var sval: number + public var sval: number + def new() + enddef + endclass + var a = A.new() + END + v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: sval', 4) +enddef + +" Test for using a reserved keyword as a variable name +def Test_reserved_varname() + for kword in ['true', 'false', 'null', 'null_blob', 'null_dict', + 'null_function', 'null_list', 'null_partial', 'null_string', + 'null_channel', 'null_job', 'super', 'this'] + + var lines =<< trim eval END + vim9script + class C + public var {kword}: list = [1, 2, 3] + endclass + var o = C.new() + END + v9.CheckSourceFailure(lines, $'E1034: Cannot use reserved name {kword}', 3) + + lines =<< trim eval END + vim9script + class C + public var {kword}: list = [1, 2, 3] + def new() + enddef + endclass + var o = C.new() + END + v9.CheckSourceFailure(lines, $'E1034: Cannot use reserved name {kword}', 3) + + lines =<< trim eval END + vim9script + class C + public var {kword}: list = [1, 2, 3] + def new() + enddef + def F() + echo this.{kword} + enddef + endclass + var o = C.new() + o.F() + END + v9.CheckSourceFailure(lines, $'E1034: Cannot use reserved name {kword}', 3) + + # class variable name + if kword != 'this' + lines =<< trim eval END + vim9script + class C + public static var {kword}: list = [1, 2, 3] + endclass + END + v9.CheckSourceFailure(lines, $'E1034: Cannot use reserved name {kword}', 3) + endif + endfor +enddef + +" Test for checking the type of the arguments and the return value of a object +" method in an extended class. +def Test_extended_obj_method_type_check() + var lines =<< trim END + vim9script + + class A + endclass + class B extends A + endclass + class C extends B + endclass + + class Foo + def Doit(p: B): B + return B.new() + enddef + endclass + + class Bar extends Foo + def Doit(p: C): B + return B.new() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1383: Method "Doit": type mismatch, expected func(object): object but got func(object): object', 20) + + lines =<< trim END + vim9script + + class A + endclass + class B extends A + endclass + class C extends B + endclass + + class Foo + def Doit(p: B): B + return B.new() + enddef + endclass + + class Bar extends Foo + def Doit(p: B): C + return C.new() + enddef + endclass + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + + class A + endclass + class B extends A + endclass + class C extends B + endclass + + class Foo + def Doit(p: B): B + return B.new() + enddef + endclass + + class Bar extends Foo + def Doit(p: A): B + return B.new() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1383: Method "Doit": type mismatch, expected func(object): object but got func(object): object', 20) + + lines =<< trim END + vim9script + + class A + endclass + class B extends A + endclass + class C extends B + endclass + + class Foo + def Doit(p: B): B + return B.new() + enddef + endclass + + class Bar extends Foo + def Doit(p: B): A + return A.new() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1383: Method "Doit": type mismatch, expected func(object): object but got func(object): object', 20) + + # check varargs type mismatch + lines =<< trim END + vim9script + + class B + def F(...xxx: list) + enddef + endclass + class C extends B + def F(xxx: list) + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1383: Method "F": type mismatch, expected func(...list) but got func(list)', 10) +enddef + +" Test type checking for class variable in assignments +func Test_class_variable_complex_type_check() + " class variable with a specific type. Try assigning a different type at + " script level. + let lines =<< trim END + vim9script + def Foo(l: list>): dict> + return {} + enddef + class A + public static var Fn: func(list>): dict> = Foo + endclass + test_garbagecollect_now() + A.Fn = "abc" + END + call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list>): dict> but got string', 9) + + " class variable with a specific type. Try assigning a different type at + " class def method level. + let lines =<< trim END + vim9script + def Foo(l: list>): dict> + return {} + enddef + class A + public static var Fn: func(list>): dict> = Foo + def Bar() + Fn = "abc" + enddef + endclass + var a = A.new() + test_garbagecollect_now() + a.Bar() + END + call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list>): dict> but got string', 1) + + " class variable with a specific type. Try assigning a different type at + " script def method level. + let lines =<< trim END + vim9script + def Foo(l: list>): dict> + return {} + enddef + class A + public static var Fn: func(list>): dict> = Foo + endclass + def Bar() + A.Fn = "abc" + enddef + test_garbagecollect_now() + Bar() + END + call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list>): dict> but got string', 1) + + " class variable without any type. Should be set to the initialization + " expression type. Try assigning a different type from script level. + let lines =<< trim END + vim9script + def Foo(l: list>): dict> + return {} + enddef + class A + public static var Fn = Foo + endclass + test_garbagecollect_now() + A.Fn = "abc" + END + call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list>): dict> but got string', 9) + + " class variable without any type. Should be set to the initialization + " expression type. Try assigning a different type at class def level. + let lines =<< trim END + vim9script + def Foo(l: list>): dict> + return {} + enddef + class A + public static var Fn = Foo + def Bar() + Fn = "abc" + enddef + endclass + var a = A.new() + test_garbagecollect_now() + a.Bar() + END + call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list>): dict> but got string', 1) + + " class variable without any type. Should be set to the initialization + " expression type. Try assigning a different type at script def level. + let lines =<< trim END + vim9script + def Foo(l: list>): dict> + return {} + enddef + class A + public static var Fn = Foo + endclass + def Bar() + A.Fn = "abc" + enddef + test_garbagecollect_now() + Bar() + END + call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list>): dict> but got string', 1) + + " class variable with 'any" type. Can be assigned different types. + let lines =<< trim END + vim9script + def Foo(l: list>): dict> + return {} + enddef + class A + public static var Fn: any = Foo + public static var Fn2: any + endclass + test_garbagecollect_now() + assert_equal('func(list>): dict>', typename(A.Fn)) + A.Fn = "abc" + test_garbagecollect_now() + assert_equal('string', typename(A.Fn)) + A.Fn2 = Foo + test_garbagecollect_now() + assert_equal('func(list>): dict>', typename(A.Fn2)) + A.Fn2 = "xyz" + test_garbagecollect_now() + assert_equal('string', typename(A.Fn2)) + END + call v9.CheckSourceSuccess(lines) + + " class variable with 'any" type. Can be assigned different types. + let lines =<< trim END + vim9script + def Foo(l: list>): dict> + return {} + enddef + class A + public static var Fn: any = Foo + public static var Fn2: any + + def Bar() + assert_equal('func(list>): dict>', typename(Fn)) + Fn = "abc" + assert_equal('string', typename(Fn)) + Fn2 = Foo + assert_equal('func(list>): dict>', typename(Fn2)) + Fn2 = "xyz" + assert_equal('string', typename(Fn2)) + enddef + endclass + var a = A.new() + test_garbagecollect_now() + a.Bar() + test_garbagecollect_now() + A.Fn = Foo + a.Bar() + END + call v9.CheckSourceSuccess(lines) + + " class variable with 'any" type. Can be assigned different types. + let lines =<< trim END + vim9script + def Foo(l: list>): dict> + return {} + enddef + class A + public static var Fn: any = Foo + public static var Fn2: any + endclass + + def Bar() + assert_equal('func(list>): dict>', typename(A.Fn)) + A.Fn = "abc" + assert_equal('string', typename(A.Fn)) + A.Fn2 = Foo + assert_equal('func(list>): dict>', typename(A.Fn2)) + A.Fn2 = "xyz" + assert_equal('string', typename(A.Fn2)) + enddef + Bar() + test_garbagecollect_now() + A.Fn = Foo + Bar() + END + call v9.CheckSourceSuccess(lines) + + let lines =<< trim END + vim9script + class A + public static var foo = [0z10, 0z20] + endclass + assert_equal([0z10, 0z20], A.foo) + A.foo = [0z30] + assert_equal([0z30], A.foo) + var a = A.foo + assert_equal([0z30], a) + END + call v9.CheckSourceSuccess(lines) +endfunc + +" Test type checking for object variable in assignments +func Test_object_variable_complex_type_check() + " object variable with a specific type. Try assigning a different type at + " script level. + let lines =<< trim END + vim9script + def Foo(l: list>): dict> + return {} + enddef + class A + public var Fn: func(list>): dict> = Foo + endclass + var a = A.new() + test_garbagecollect_now() + a.Fn = "abc" + END + call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list>): dict> but got string', 10) + + " object variable with a specific type. Try assigning a different type at + " object def method level. + let lines =<< trim END + vim9script + def Foo(l: list>): dict> + return {} + enddef + class A + public var Fn: func(list>): dict> = Foo + def Bar() + this.Fn = "abc" + this.Fn = Foo + enddef + endclass + var a = A.new() + test_garbagecollect_now() + a.Bar() + END + call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list>): dict> but got string', 1) + + " object variable with a specific type. Try assigning a different type at + " script def method level. + let lines =<< trim END + vim9script + def Foo(l: list>): dict> + return {} + enddef + class A + public var Fn: func(list>): dict> = Foo + endclass + def Bar() + var a = A.new() + a.Fn = "abc" + a.Fn = Foo + enddef + test_garbagecollect_now() + Bar() + END + call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list>): dict> but got string', 2) + + " object variable without any type. Should be set to the initialization + " expression type. Try assigning a different type from script level. + let lines =<< trim END + vim9script + def Foo(l: list>): dict> + return {} + enddef + class A + public var Fn = Foo + endclass + var a = A.new() + test_garbagecollect_now() + a.Fn = "abc" + END + call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list>): dict> but got string', 10) + + " object variable without any type. Should be set to the initialization + " expression type. Try assigning a different type at object def level. + let lines =<< trim END + vim9script + def Foo(l: list>): dict> + return {} + enddef + class A + public var Fn = Foo + def Bar() + this.Fn = "abc" + this.Fn = Foo + enddef + endclass + var a = A.new() + test_garbagecollect_now() + a.Bar() + END + call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list>): dict> but got string', 1) + + " object variable without any type. Should be set to the initialization + " expression type. Try assigning a different type at script def level. + let lines =<< trim END + vim9script + def Foo(l: list>): dict> + return {} + enddef + class A + public var Fn = Foo + endclass + def Bar() + var a = A.new() + a.Fn = "abc" + a.Fn = Foo + enddef + test_garbagecollect_now() + Bar() + END + call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list>): dict> but got string', 2) + + " object variable with 'any" type. Can be assigned different types. + let lines =<< trim END + vim9script + def Foo(l: list>): dict> + return {} + enddef + class A + public var Fn: any = Foo + public var Fn2: any + endclass + + var a = A.new() + test_garbagecollect_now() + assert_equal('func(list>): dict>', typename(a.Fn)) + a.Fn = "abc" + test_garbagecollect_now() + assert_equal('string', typename(a.Fn)) + a.Fn2 = Foo + test_garbagecollect_now() + assert_equal('func(list>): dict>', typename(a.Fn2)) + a.Fn2 = "xyz" + test_garbagecollect_now() + assert_equal('string', typename(a.Fn2)) + END + call v9.CheckSourceSuccess(lines) + + " object variable with 'any" type. Can be assigned different types. + let lines =<< trim END + vim9script + def Foo(l: list>): dict> + return {} + enddef + class A + public var Fn: any = Foo + public var Fn2: any + + def Bar() + assert_equal('func(list>): dict>', typename(this.Fn)) + this.Fn = "abc" + assert_equal('string', typename(this.Fn)) + this.Fn2 = Foo + assert_equal('func(list>): dict>', typename(this.Fn2)) + this.Fn2 = "xyz" + assert_equal('string', typename(this.Fn2)) + enddef + endclass + + var a = A.new() + test_garbagecollect_now() + a.Bar() + test_garbagecollect_now() + a.Fn = Foo + a.Bar() + END + call v9.CheckSourceSuccess(lines) + + " object variable with 'any" type. Can be assigned different types. + let lines =<< trim END + vim9script + def Foo(l: list>): dict> + return {} + enddef + class A + public var Fn: any = Foo + public var Fn2: any + endclass + + def Bar() + var a = A.new() + assert_equal('func(list>): dict>', typename(a.Fn)) + a.Fn = "abc" + assert_equal('string', typename(a.Fn)) + a.Fn2 = Foo + assert_equal('func(list>): dict>', typename(a.Fn2)) + a.Fn2 = "xyz" + assert_equal('string', typename(a.Fn2)) + enddef + test_garbagecollect_now() + Bar() + test_garbagecollect_now() + Bar() + END + call v9.CheckSourceSuccess(lines) +endfunc + +" Test for recursively calling an object method. This used to cause an +" use-after-free error. +def Test_recursive_object_method_call() + var lines =<< trim END + vim9script + class A + var val: number = 0 + def Foo(): number + if this.val >= 90 + return this.val + endif + this.val += 1 + return this.Foo() + enddef + endclass + var a = A.new() + assert_equal(90, a.Foo()) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for recursively calling a class method. +def Test_recursive_class_method_call() + var lines =<< trim END + vim9script + class A + static var val: number = 0 + static def Foo(): number + if val >= 90 + return val + endif + val += 1 + return Foo() + enddef + endclass + assert_equal(90, A.Foo()) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for checking the argument types and the return type when assigning a +" funcref to make sure the invariant class type is used. +def Test_funcref_argtype_returntype_check() + var lines =<< trim END + vim9script + class A + endclass + class B extends A + endclass + + def Foo(p: B): B + return B.new() + enddef + + var Bar: func(A): A = Foo + END + v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(object): object but got func(object): object', 11) + + lines =<< trim END + vim9script + class A + endclass + class B extends A + endclass + + def Foo(p: B): B + return B.new() + enddef + + def Baz() + var Bar: func(A): A = Foo + enddef + Baz() + END + v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(object): object but got func(object): object', 1) +enddef + +def Test_funcref_argtype_invariance_check() + var lines =<< trim END + vim9script + + class A + endclass + class B extends A + endclass + class C extends B + endclass + + var Func: func(B): number + Func = (o: B): number => 3 + assert_equal(3, Func(B.new())) + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + + class A + endclass + class B extends A + endclass + class C extends B + endclass + + var Func: func(B): number + Func = (o: A): number => 3 + END + v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(object): number but got func(object): number', 11) + + lines =<< trim END + vim9script + + class A + endclass + class B extends A + endclass + class C extends B + endclass + + var Func: func(B): number + Func = (o: C): number => 3 + END + v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(object): number but got func(object): number', 11) +enddef + +" Test for using an operator (e.g. +) with an assignment +def Test_op_and_assignment() + # Using += with a class variable + var lines =<< trim END + vim9script + class A + public static var val: list = [] + static def Foo(): list + val += [1] + return val + enddef + endclass + def Bar(): list + A.val += [2] + return A.val + enddef + assert_equal([1], A.Foo()) + assert_equal([1, 2], Bar()) + A.val += [3] + assert_equal([1, 2, 3], A.val) + END + v9.CheckSourceSuccess(lines) + + # Using += with an object variable + lines =<< trim END + vim9script + class A + public var val: list = [] + def Foo(): list + this.val += [1] + return this.val + enddef + endclass + def Bar(bar_a: A): list + bar_a.val += [2] + return bar_a.val + enddef + var a = A.new() + assert_equal([1], a.Foo()) + assert_equal([1, 2], Bar(a)) + a.val += [3] + assert_equal([1, 2, 3], a.val) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for using an object method as a funcref +def Test_object_funcref() + # Using object method funcref from a def function + var lines =<< trim END + vim9script + class A + def Foo(): list + return [3, 2, 1] + enddef + endclass + def Bar() + var a = A.new() + var Fn = a.Foo + assert_equal([3, 2, 1], Fn()) + enddef + Bar() + END + v9.CheckSourceSuccess(lines) + + # Using object method funcref at the script level + lines =<< trim END + vim9script + class A + def Foo(): dict + return {a: 1, b: 2} + enddef + endclass + var a = A.new() + var Fn = a.Foo + assert_equal({a: 1, b: 2}, Fn()) + END + v9.CheckSourceSuccess(lines) + + # Using object method funcref at the script level + lines =<< trim END + vim9script + class A + var val: number + def Foo(): number + return this.val + enddef + endclass + var a = A.new(345) + var Fn = a.Foo + assert_equal(345, Fn()) + END + v9.CheckSourceSuccess(lines) + + # Using object method funcref from another object method + lines =<< trim END + vim9script + class A + def Foo(): list + return [3, 2, 1] + enddef + def Bar() + var Fn = this.Foo + assert_equal([3, 2, 1], Fn()) + enddef + endclass + var a = A.new() + a.Bar() + END + v9.CheckSourceSuccess(lines) + + # Using function() to get a object method funcref + lines =<< trim END + vim9script + class A + def Foo(l: list): list + return l + enddef + endclass + var a = A.new() + var Fn = function(a.Foo, [[{a: 1, b: 2}, [3, 4]]]) + assert_equal([{a: 1, b: 2}, [3, 4]], Fn()) + END + v9.CheckSourceSuccess(lines) + + # Use an object method with a function returning a funcref and then call the + # funcref. + lines =<< trim END + vim9script + + def Map(F: func(number): number): func(number): number + return (n: number) => F(n) + enddef + + class Math + def Double(n: number): number + return 2 * n + enddef + endclass + + const math = Math.new() + assert_equal(48, Map(math.Double)(24)) + END + v9.CheckSourceSuccess(lines) + + # Try using a protected object method funcref from a def function + lines =<< trim END + vim9script + class A + def _Foo() + enddef + endclass + def Bar() + var a = A.new() + var Fn = a._Foo + enddef + Bar() + END + v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 2) + + # Try using a protected object method funcref at the script level + lines =<< trim END + vim9script + class A + def _Foo() + enddef + endclass + var a = A.new() + var Fn = a._Foo + END + v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 7) + + # Using a protected object method funcref from another object method + lines =<< trim END + vim9script + class A + def _Foo(): list + return [3, 2, 1] + enddef + def Bar() + var Fn = this._Foo + assert_equal([3, 2, 1], Fn()) + enddef + endclass + var a = A.new() + a.Bar() + END + v9.CheckSourceSuccess(lines) + + # Using object method funcref using call() + lines =<< trim END + vim9script + class A + var val: number + def Foo(): number + return this.val + enddef + endclass + + def Bar(obj: A) + assert_equal(123, call(obj.Foo, [])) + enddef + + var a = A.new(123) + Bar(a) + assert_equal(123, call(a.Foo, [])) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for using a class method as a funcref +def Test_class_funcref() + # Using class method funcref in a def function + var lines =<< trim END + vim9script + class A + static def Foo(): list + return [3, 2, 1] + enddef + endclass + def Bar() + var Fn = A.Foo + assert_equal([3, 2, 1], Fn()) + enddef + Bar() + END + v9.CheckSourceSuccess(lines) + + # Using class method funcref at script level + lines =<< trim END + vim9script + class A + static def Foo(): dict + return {a: 1, b: 2} + enddef + endclass + var Fn = A.Foo + assert_equal({a: 1, b: 2}, Fn()) + END + v9.CheckSourceSuccess(lines) + + # Using class method funcref at the script level + lines =<< trim END + vim9script + class A + public static var val: number + static def Foo(): number + return val + enddef + endclass + A.val = 567 + var Fn = A.Foo + assert_equal(567, Fn()) + END + v9.CheckSourceSuccess(lines) + + # Using function() to get a class method funcref + lines =<< trim END + vim9script + class A + static def Foo(l: list): list + return l + enddef + endclass + var Fn = function(A.Foo, [[{a: 1, b: 2}, [3, 4]]]) + assert_equal([{a: 1, b: 2}, [3, 4]], Fn()) + END + v9.CheckSourceSuccess(lines) + + # Using a class method funcref from another class method + lines =<< trim END + vim9script + class A + static def Foo(): list + return [3, 2, 1] + enddef + static def Bar() + var Fn = Foo + assert_equal([3, 2, 1], Fn()) + enddef + endclass + A.Bar() + END + v9.CheckSourceSuccess(lines) + + # Use a class method with a function returning a funcref and then call the + # funcref. + lines =<< trim END + vim9script + + def Map(F: func(number): number): func(number): number + return (n: number) => F(n) + enddef + + class Math + static def StaticDouble(n: number): number + return 2 * n + enddef + endclass + + assert_equal(48, Map(Math.StaticDouble)(24)) + END + v9.CheckSourceSuccess(lines) + + # Try using a protected class method funcref in a def function + lines =<< trim END + vim9script + class A + static def _Foo() + enddef + endclass + def Bar() + var Fn = A._Foo + enddef + Bar() + END + v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 1) + + # Try using a protected class method funcref at script level + lines =<< trim END + vim9script + class A + static def _Foo() + enddef + endclass + var Fn = A._Foo + END + v9.CheckSourceFailure(lines, 'E1366: Cannot access protected method: _Foo', 6) + + # Using a protected class method funcref from another class method + lines =<< trim END + vim9script + class A + static def _Foo(): list + return [3, 2, 1] + enddef + static def Bar() + var Fn = _Foo + assert_equal([3, 2, 1], Fn()) + enddef + endclass + A.Bar() + END + v9.CheckSourceSuccess(lines) + + # Using class method funcref using call() + lines =<< trim END + vim9script + class A + public static var val: number + static def Foo(): number + return val + enddef + endclass + + def Bar() + A.val = 468 + assert_equal(468, call(A.Foo, [])) + enddef + Bar() + assert_equal(468, call(A.Foo, [])) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for using an object member as a funcref +def Test_object_member_funcref() + # Using a funcref object variable in an object method + var lines =<< trim END + vim9script + def Foo(n: number): number + return n * 10 + enddef + + class A + var Cb: func(number): number = Foo + def Bar() + assert_equal(200, this.Cb(20)) + enddef + endclass + + var a = A.new() + a.Bar() + END + v9.CheckSourceSuccess(lines) + + # Using a funcref object variable in a def method + lines =<< trim END + vim9script + def Foo(n: number): number + return n * 10 + enddef + + class A + var Cb: func(number): number = Foo + endclass + + def Bar() + var a = A.new() + assert_equal(200, a.Cb(20)) + enddef + Bar() + END + v9.CheckSourceSuccess(lines) + + # Using a funcref object variable at script level + lines =<< trim END + vim9script + def Foo(n: number): number + return n * 10 + enddef + + class A + var Cb: func(number): number = Foo + endclass + + var a = A.new() + assert_equal(200, a.Cb(20)) + END + v9.CheckSourceSuccess(lines) + + # Using a funcref object variable pointing to an object method in an object + # method. + lines =<< trim END + vim9script + class A + var Cb: func(number): number = this.Foo + def Foo(n: number): number + return n * 10 + enddef + def Bar() + assert_equal(200, this.Cb(20)) + enddef + endclass + + var a = A.new() + a.Bar() + END + v9.CheckSourceSuccess(lines) + + # Using a funcref object variable pointing to an object method in a def + # method. + lines =<< trim END + vim9script + class A + var Cb: func(number): number = this.Foo + def Foo(n: number): number + return n * 10 + enddef + endclass + + def Bar() + var a = A.new() + assert_equal(200, a.Cb(20)) + enddef + Bar() + END + v9.CheckSourceSuccess(lines) + + # Using a funcref object variable pointing to an object method at script + # level. + lines =<< trim END + vim9script + class A + var Cb = this.Foo + def Foo(n: number): number + return n * 10 + enddef + endclass + + var a = A.new() + assert_equal(200, a.Cb(20)) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for using a class member as a funcref +def Test_class_member_funcref() + # Using a funcref class variable in a class method + var lines =<< trim END + vim9script + def Foo(n: number): number + return n * 10 + enddef + + class A + static var Cb = Foo + static def Bar() + assert_equal(200, Cb(20)) + enddef + endclass + + A.Bar() + END + v9.CheckSourceSuccess(lines) + + # Using a funcref class variable in a def method + lines =<< trim END + vim9script + def Foo(n: number): number + return n * 10 + enddef + + class A + public static var Cb = Foo + endclass + + def Bar() + assert_equal(200, A.Cb(20)) + enddef + Bar() + END + v9.CheckSourceSuccess(lines) + + # Using a funcref class variable at script level + lines =<< trim END + vim9script + def Foo(n: number): number + return n * 10 + enddef + + class A + public static var Cb = Foo + endclass + + assert_equal(200, A.Cb(20)) + END + v9.CheckSourceSuccess(lines) + + # Using a funcref class variable pointing to a class method in a class + # method. + lines =<< trim END + vim9script + class A + static var Cb: func(number): number + static def Foo(n: number): number + return n * 10 + enddef + static def Init() + Cb = Foo + enddef + static def Bar() + assert_equal(200, Cb(20)) + enddef + endclass + + A.Init() + A.Bar() + END + v9.CheckSourceSuccess(lines) + + # Using a funcref class variable pointing to a class method in a def method. + lines =<< trim END + vim9script + class A + static var Cb: func(number): number + static def Foo(n: number): number + return n * 10 + enddef + static def Init() + Cb = Foo + enddef + endclass + + def Bar() + A.Init() + assert_equal(200, A.Cb(20)) + enddef + Bar() + END + v9.CheckSourceSuccess(lines) + + # Using a funcref class variable pointing to a class method at script level. + lines =<< trim END + vim9script + class A + static var Cb: func(number): number + static def Foo(n: number): number + return n * 10 + enddef + static def Init() + Cb = Foo + enddef + endclass + + A.Init() + assert_equal(200, A.Cb(20)) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for using object methods as popup callback functions +def Test_objmethod_popup_callback() + # Use the popup from the script level + var lines =<< trim END + vim9script + + class A + var selection: number = -1 + var filterkeys: list = [] + + def PopupFilter(id: number, key: string): bool + add(this.filterkeys, key) + return popup_filter_yesno(id, key) + enddef + + def PopupCb(id: number, result: number) + this.selection = result ? 100 : 200 + enddef + endclass + + var a = A.new() + feedkeys('', 'xt') + var winid = popup_create('Y/N?', + {filter: a.PopupFilter, callback: a.PopupCb}) + feedkeys('y', 'xt') + popup_close(winid) + assert_equal(100, a.selection) + assert_equal(['y'], a.filterkeys) + feedkeys('', 'xt') + winid = popup_create('Y/N?', + {filter: a.PopupFilter, callback: a.PopupCb}) + feedkeys('n', 'xt') + popup_close(winid) + assert_equal(200, a.selection) + assert_equal(['y', 'n'], a.filterkeys) + END + v9.CheckSourceSuccess(lines) + + # Use the popup from a def function + lines =<< trim END + vim9script + + class A + var selection: number = -1 + var filterkeys: list = [] + + def PopupFilter(id: number, key: string): bool + add(this.filterkeys, key) + return popup_filter_yesno(id, key) + enddef + + def PopupCb(id: number, result: number) + this.selection = result ? 100 : 200 + enddef + endclass + + def Foo() + var a = A.new() + feedkeys('', 'xt') + var winid = popup_create('Y/N?', + {filter: a.PopupFilter, callback: a.PopupCb}) + feedkeys('y', 'xt') + popup_close(winid) + assert_equal(100, a.selection) + assert_equal(['y'], a.filterkeys) + feedkeys('', 'xt') + winid = popup_create('Y/N?', + {filter: a.PopupFilter, callback: a.PopupCb}) + feedkeys('n', 'xt') + popup_close(winid) + assert_equal(200, a.selection) + assert_equal(['y', 'n'], a.filterkeys) + enddef + Foo() + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for using class methods as popup callback functions +def Test_classmethod_popup_callback() + # Use the popup from the script level + var lines =<< trim END + vim9script + + class A + static var selection: number = -1 + static var filterkeys: list = [] + + static def PopupFilter(id: number, key: string): bool + add(filterkeys, key) + return popup_filter_yesno(id, key) + enddef + + static def PopupCb(id: number, result: number) + selection = result ? 100 : 200 + enddef + endclass + + feedkeys('', 'xt') + var winid = popup_create('Y/N?', + {filter: A.PopupFilter, callback: A.PopupCb}) + feedkeys('y', 'xt') + popup_close(winid) + assert_equal(100, A.selection) + assert_equal(['y'], A.filterkeys) + feedkeys('', 'xt') + winid = popup_create('Y/N?', + {filter: A.PopupFilter, callback: A.PopupCb}) + feedkeys('n', 'xt') + popup_close(winid) + assert_equal(200, A.selection) + assert_equal(['y', 'n'], A.filterkeys) + END + v9.CheckSourceSuccess(lines) + + # Use the popup from a def function + lines =<< trim END + vim9script + + class A + static var selection: number = -1 + static var filterkeys: list = [] + + static def PopupFilter(id: number, key: string): bool + add(filterkeys, key) + return popup_filter_yesno(id, key) + enddef + + static def PopupCb(id: number, result: number) + selection = result ? 100 : 200 + enddef + endclass + + def Foo() + feedkeys('', 'xt') + var winid = popup_create('Y/N?', + {filter: A.PopupFilter, callback: A.PopupCb}) + feedkeys('y', 'xt') + popup_close(winid) + assert_equal(100, A.selection) + assert_equal(['y'], A.filterkeys) + feedkeys('', 'xt') + winid = popup_create('Y/N?', + {filter: A.PopupFilter, callback: A.PopupCb}) + feedkeys('n', 'xt') + popup_close(winid) + assert_equal(200, A.selection) + assert_equal(['y', 'n'], A.filterkeys) + enddef + Foo() + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for using an object method as a timer callback function +def Test_objmethod_timer_callback() + # Use the timer callback from script level + var lines =<< trim END + vim9script + + class A + var timerTick: number = -1 + def TimerCb(timerID: number) + this.timerTick = 6 + enddef + endclass + + var a = A.new() + timer_start(0, a.TimerCb) + var maxWait = 5 + while maxWait > 0 && a.timerTick == -1 + :sleep 10m + maxWait -= 1 + endwhile + assert_equal(6, a.timerTick) + END + v9.CheckSourceSuccess(lines) + + # Use the timer callback from a def function + lines =<< trim END + vim9script + + class A + var timerTick: number = -1 + def TimerCb(timerID: number) + this.timerTick = 6 + enddef + endclass + + def Foo() + var a = A.new() + timer_start(0, a.TimerCb) + var maxWait = 5 + while maxWait > 0 && a.timerTick == -1 + :sleep 10m + maxWait -= 1 + endwhile + assert_equal(6, a.timerTick) + enddef + Foo() + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for using a class method as a timer callback function +def Test_classmethod_timer_callback() + # Use the timer callback from script level + var lines =<< trim END + vim9script + + class A + static var timerTick: number = -1 + static def TimerCb(timerID: number) + timerTick = 6 + enddef + endclass + + timer_start(0, A.TimerCb) + var maxWait = 5 + while maxWait > 0 && A.timerTick == -1 + :sleep 10m + maxWait -= 1 + endwhile + assert_equal(6, A.timerTick) + END + v9.CheckSourceSuccess(lines) + + # Use the timer callback from a def function + lines =<< trim END + vim9script + + class A + static var timerTick: number = -1 + static def TimerCb(timerID: number) + timerTick = 6 + enddef + endclass + + def Foo() + timer_start(0, A.TimerCb) + var maxWait = 5 + while maxWait > 0 && A.timerTick == -1 + :sleep 10m + maxWait -= 1 + endwhile + assert_equal(6, A.timerTick) + enddef + Foo() + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for using a class variable as the first and/or second operand of a binary +" operator. +def Test_class_variable_as_operands() + var lines =<< trim END + vim9script + class Tests + static var truthy: bool = true + public static var TruthyFn: func + static var list: list = [] + static var four: number = 4 + static var str: string = 'hello' + + static def Str(): string + return str + enddef + + static def Four(): number + return four + enddef + + static def List(): list + return list + enddef + + static def Truthy(): bool + return truthy + enddef + + def TestOps() + assert_true(Tests.truthy == truthy) + assert_true(truthy == Tests.truthy) + assert_true(Tests.list isnot []) + assert_true([] isnot Tests.list) + assert_equal(2, Tests.four >> 1) + assert_equal(16, 1 << Tests.four) + assert_equal(8, Tests.four + four) + assert_equal(8, four + Tests.four) + assert_equal('hellohello', Tests.str .. str) + assert_equal('hellohello', str .. Tests.str) + + # Using class variable for list indexing + var l = range(10) + assert_equal(4, l[Tests.four]) + assert_equal([4, 5, 6], l[Tests.four : Tests.four + 2]) + + # Using class variable for Dict key + var d = {hello: 'abc'} + assert_equal('abc', d[Tests.str]) + enddef + endclass + + def TestOps2() + assert_true(Tests.truthy == Tests.Truthy()) + assert_true(Tests.Truthy() == Tests.truthy) + assert_true(Tests.truthy == Tests.TruthyFn()) + assert_true(Tests.TruthyFn() == Tests.truthy) + assert_true(Tests.list is Tests.List()) + assert_true(Tests.List() is Tests.list) + assert_equal(2, Tests.four >> 1) + assert_equal(16, 1 << Tests.four) + assert_equal(8, Tests.four + Tests.Four()) + assert_equal(8, Tests.Four() + Tests.four) + assert_equal('hellohello', Tests.str .. Tests.Str()) + assert_equal('hellohello', Tests.Str() .. Tests.str) + + # Using class variable for list indexing + var l = range(10) + assert_equal(4, l[Tests.four]) + assert_equal([4, 5, 6], l[Tests.four : Tests.four + 2]) + + # Using class variable for Dict key + var d = {hello: 'abc'} + assert_equal('abc', d[Tests.str]) + enddef + + Tests.TruthyFn = Tests.Truthy + var t = Tests.new() + t.TestOps() + TestOps2() + + assert_true(Tests.truthy == Tests.Truthy()) + assert_true(Tests.Truthy() == Tests.truthy) + assert_true(Tests.truthy == Tests.TruthyFn()) + assert_true(Tests.TruthyFn() == Tests.truthy) + assert_true(Tests.list is Tests.List()) + assert_true(Tests.List() is Tests.list) + assert_equal(2, Tests.four >> 1) + assert_equal(16, 1 << Tests.four) + assert_equal(8, Tests.four + Tests.Four()) + assert_equal(8, Tests.Four() + Tests.four) + assert_equal('hellohello', Tests.str .. Tests.Str()) + assert_equal('hellohello', Tests.Str() .. Tests.str) + + # Using class variable for list indexing + var l = range(10) + assert_equal(4, l[Tests.four]) + assert_equal([4, 5, 6], l[Tests.four : Tests.four + 2]) + + # Using class variable for Dict key + var d = {hello: 'abc'} + assert_equal('abc', d[Tests.str]) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for checking the type of the key used to access an object dict member. +def Test_dict_member_key_type_check() + var lines =<< trim END + vim9script + + abstract class State + var numbers: dict = {0: 'nil', 1: 'unity'} + endclass + + class Test extends State + def ObjMethodTests() + var cursor: number = 0 + var z: number = 0 + [this.numbers[cursor]] = ['zero.1'] + assert_equal({0: 'zero.1', 1: 'unity'}, this.numbers) + [this.numbers[string(cursor)], z] = ['zero.2', 1] + assert_equal({0: 'zero.2', 1: 'unity'}, this.numbers) + [z, this.numbers[string(cursor)]] = [1, 'zero.3'] + assert_equal({0: 'zero.3', 1: 'unity'}, this.numbers) + [this.numbers[cursor], z] = ['zero.4', 1] + assert_equal({0: 'zero.4', 1: 'unity'}, this.numbers) + [z, this.numbers[cursor]] = [1, 'zero.5'] + assert_equal({0: 'zero.5', 1: 'unity'}, this.numbers) + enddef + + static def ClassMethodTests(that: State) + var cursor: number = 0 + var z: number = 0 + [that.numbers[cursor]] = ['zero.1'] + assert_equal({0: 'zero.1', 1: 'unity'}, that.numbers) + [that.numbers[string(cursor)], z] = ['zero.2', 1] + assert_equal({0: 'zero.2', 1: 'unity'}, that.numbers) + [z, that.numbers[string(cursor)]] = [1, 'zero.3'] + assert_equal({0: 'zero.3', 1: 'unity'}, that.numbers) + [that.numbers[cursor], z] = ['zero.4', 1] + assert_equal({0: 'zero.4', 1: 'unity'}, that.numbers) + [z, that.numbers[cursor]] = [1, 'zero.5'] + assert_equal({0: 'zero.5', 1: 'unity'}, that.numbers) + enddef + + def new() + enddef + + def newMethodTests() + var cursor: number = 0 + var z: number + [this.numbers[cursor]] = ['zero.1'] + assert_equal({0: 'zero.1', 1: 'unity'}, this.numbers) + [this.numbers[string(cursor)], z] = ['zero.2', 1] + assert_equal({0: 'zero.2', 1: 'unity'}, this.numbers) + [z, this.numbers[string(cursor)]] = [1, 'zero.3'] + assert_equal({0: 'zero.3', 1: 'unity'}, this.numbers) + [this.numbers[cursor], z] = ['zero.4', 1] + assert_equal({0: 'zero.4', 1: 'unity'}, this.numbers) + [z, this.numbers[cursor]] = [1, 'zero.5'] + assert_equal({0: 'zero.5', 1: 'unity'}, this.numbers) + enddef + endclass + + def DefFuncTests(that: Test) + var cursor: number = 0 + var z: number + [that.numbers[cursor]] = ['zero.1'] + assert_equal({0: 'zero.1', 1: 'unity'}, that.numbers) + [that.numbers[string(cursor)], z] = ['zero.2', 1] + assert_equal({0: 'zero.2', 1: 'unity'}, that.numbers) + [z, that.numbers[string(cursor)]] = [1, 'zero.3'] + assert_equal({0: 'zero.3', 1: 'unity'}, that.numbers) + [that.numbers[cursor], z] = ['zero.4', 1] + assert_equal({0: 'zero.4', 1: 'unity'}, that.numbers) + [z, that.numbers[cursor]] = [1, 'zero.5'] + assert_equal({0: 'zero.5', 1: 'unity'}, that.numbers) + enddef + + Test.newMethodTests() + Test.new().ObjMethodTests() + Test.ClassMethodTests(Test.new()) + DefFuncTests(Test.new()) + + const test: Test = Test.new() + var cursor: number = 0 + [test.numbers[cursor], cursor] = ['zero', 1] + [cursor, test.numbers[cursor]] = [1, 'one'] + assert_equal({0: 'zero', 1: 'one'}, test.numbers) + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + + class A + var numbers: dict = {a: '1', b: '2'} + + def new() + enddef + + def Foo() + var z: number + [this.numbers.a, z] = [{}, 10] + enddef + endclass + + var a = A.new() + a.Foo() + END + v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected string but got dict', 2) + + lines =<< trim END + vim9script + + class A + var numbers: dict = {a: 1, b: 2} + + def new() + enddef + + def Foo() + var x: string = 'a' + var y: number + [this.numbers[x], y] = [{}, 10] + enddef + endclass + + var a = A.new() + a.Foo() + END + v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected number but got dict', 3) +enddef + +def Test_compile_many_def_functions_in_funcref_instr() + # This used to crash Vim. This is reproducible only when run on new instance + # of Vim. + var lines =<< trim END + vim9script + + class A + def new() + this.TakeFunc(this.F00) + enddef + + def TakeFunc(F: func) + enddef + + def F00() + this.F01() + this.F02() + this.F03() + this.F04() + this.F05() + this.F06() + this.F07() + this.F08() + this.F09() + this.F10() + this.F11() + this.F12() + this.F13() + this.F14() + this.F15() + this.F16() + this.F17() + this.F18() + this.F19() + this.F20() + this.F21() + this.F22() + this.F23() + this.F24() + this.F25() + this.F26() + this.F27() + this.F28() + this.F29() + this.F30() + this.F31() + this.F32() + this.F33() + this.F34() + this.F35() + this.F36() + this.F37() + this.F38() + this.F39() + this.F40() + this.F41() + this.F42() + this.F43() + this.F44() + this.F45() + this.F46() + this.F47() + enddef + + def F01() + enddef + def F02() + enddef + def F03() + enddef + def F04() + enddef + def F05() + enddef + def F06() + enddef + def F07() + enddef + def F08() + enddef + def F09() + enddef + def F10() + enddef + def F11() + enddef + def F12() + enddef + def F13() + enddef + def F14() + enddef + def F15() + enddef + def F16() + enddef + def F17() + enddef + def F18() + enddef + def F19() + enddef + def F20() + enddef + def F21() + enddef + def F22() + enddef + def F23() + enddef + def F24() + enddef + def F25() + enddef + def F26() + enddef + def F27() + enddef + def F28() + enddef + def F29() + enddef + def F30() + enddef + def F31() + enddef + def F32() + enddef + def F33() + enddef + def F34() + enddef + def F35() + enddef + def F36() + enddef + def F37() + enddef + def F38() + enddef + def F39() + enddef + def F40() + enddef + def F41() + enddef + def F42() + enddef + def F43() + enddef + def F44() + enddef + def F45() + enddef + def F46() + enddef + def F47() + enddef + endclass + + A.new() + END + writefile(lines, 'Xscript', 'D') + g:RunVim([], [], '-u NONE -S Xscript -c qa') + assert_equal(0, v:shell_error) +enddef + +" Test for 'final' class and object variables +def Test_final_class_object_variable() + # Test for changing a final object variable from an object function + var lines =<< trim END + vim9script + class A + final foo: string = "abc" + def Foo() + this.foo = "def" + enddef + endclass + defcompile A.Foo + END + v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "foo" in class "A"', 1) + + # Test for changing a final object variable from the 'new' function + lines =<< trim END + vim9script + class A + final s1: string + final s2: string + def new(this.s1) + this.s2 = 'def' + enddef + endclass + var a = A.new('abc') + assert_equal('abc', a.s1) + assert_equal('def', a.s2) + END + v9.CheckSourceSuccess(lines) + + # Test for a final class variable + lines =<< trim END + vim9script + class A + static final s1: string = "abc" + endclass + assert_equal('abc', A.s1) + END + v9.CheckSourceSuccess(lines) + + # Test for changing a final class variable from a class function + lines =<< trim END + vim9script + class A + static final s1: string = "abc" + static def Foo() + s1 = "def" + enddef + endclass + A.Foo() + END + v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "s1" in class "A"', 1) + + # Test for changing a public final class variable at script level + lines =<< trim END + vim9script + class A + public static final s1: string = "abc" + endclass + assert_equal('abc', A.s1) + A.s1 = 'def' + END + v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "s1" in class "A"', 6) + + # Test for changing a public final class variable from a class function + lines =<< trim END + vim9script + class A + public static final s1: string = "abc" + static def Foo() + s1 = "def" + enddef + endclass + A.Foo() + END + v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "s1" in class "A"', 1) + + # Test for changing a public final class variable from a function + lines =<< trim END + vim9script + class A + public static final s1: string = "abc" + endclass + def Foo() + A.s1 = 'def' + enddef + defcompile + END + v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "s1" in class "A"', 1) + + # Test for using a final variable of composite type + lines =<< trim END + vim9script + class A + public final l: list + def new() + this.l = [1, 2] + enddef + def Foo() + this.l[0] = 3 + this.l->add(4) + enddef + endclass + var a = A.new() + assert_equal([1, 2], a.l) + a.Foo() + assert_equal([3, 2, 4], a.l) + END + v9.CheckSourceSuccess(lines) + + # Test for changing a final variable of composite type from another object + # function + lines =<< trim END + vim9script + class A + public final l: list = [1, 2] + def Foo() + this.l = [3, 4] + enddef + endclass + var a = A.new() + a.Foo() + END + v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "l" in class "A"', 1) + + # Test for modifying a final variable of composite type at script level + lines =<< trim END + vim9script + class A + public final l: list = [1, 2] + endclass + var a = A.new() + a.l[0] = 3 + a.l->add(4) + assert_equal([3, 2, 4], a.l) + END + v9.CheckSourceSuccess(lines) + + # Test for modifying a final variable of composite type from a function + lines =<< trim END + vim9script + class A + public final l: list = [1, 2] + endclass + def Foo() + var a = A.new() + a.l[0] = 3 + a.l->add(4) + assert_equal([3, 2, 4], a.l) + enddef + Foo() + END + v9.CheckSourceSuccess(lines) + + # Test for modifying a final variable of composite type from another object + # function + lines =<< trim END + vim9script + class A + public final l: list = [1, 2] + def Foo() + this.l[0] = 3 + this.l->add(4) + enddef + endclass + var a = A.new() + a.Foo() + assert_equal([3, 2, 4], a.l) + END + v9.CheckSourceSuccess(lines) + + # Test for assigning a new value to a final variable of composite type at + # script level + lines =<< trim END + vim9script + class A + public final l: list = [1, 2] + endclass + var a = A.new() + a.l = [3, 4] + END + v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "l" in class "A"', 6) + + # Test for assigning a new value to a final variable of composite type from + # another object function + lines =<< trim END + vim9script + class A + public final l: list = [1, 2] + def Foo() + this.l = [3, 4] + enddef + endclass + var a = A.new() + a.Foo() + END + v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "l" in class "A"', 1) + + # Test for assigning a new value to a final variable of composite type from + # another function + lines =<< trim END + vim9script + class A + public final l: list = [1, 2] + endclass + def Foo() + var a = A.new() + a.l = [3, 4] + enddef + Foo() + END + v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "l" in class "A"', 2) + + # Error case: Use 'final' with just a variable name + lines =<< trim END + vim9script + class A + final foo + endclass + var a = A.new() + END + v9.CheckSourceFailure(lines, 'E1022: Type or initialization required', 3) + + # Error case: Use 'final' followed by 'public' + lines =<< trim END + vim9script + class A + final public foo: number + endclass + var a = A.new() + END + v9.CheckSourceFailure(lines, 'E1022: Type or initialization required', 3) + + # Error case: Use 'final' followed by 'static' + lines =<< trim END + vim9script + class A + final static foo: number + endclass + var a = A.new() + END + v9.CheckSourceFailure(lines, 'E1022: Type or initialization required', 3) + + # Error case: 'final' cannot be used in an interface + lines =<< trim END + vim9script + interface A + final foo: number = 10 + endinterface + END + v9.CheckSourceFailure(lines, 'E1408: Final variable not supported in an interface', 3) + + # Error case: 'final' not supported for an object method + lines =<< trim END + vim9script + class A + final def Foo() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1022: Type or initialization required', 3) + + # Error case: 'final' not supported for a class method + lines =<< trim END + vim9script + class A + static final def Foo() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1022: Type or initialization required', 3) +enddef + +" Test for 'const' class and object variables +def Test_const_class_object_variable() + # Test for changing a const object variable from an object function + var lines =<< trim END + vim9script + class A + const foo: string = "abc" + def Foo() + this.foo = "def" + enddef + endclass + defcompile A.Foo + END + v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "foo" in class "A"', 1) + + # Test for changing a const object variable from the 'new' function + lines =<< trim END + vim9script + class A + const s1: string + const s2: string + def new(this.s1) + this.s2 = 'def' + enddef + endclass + var a = A.new('abc') + assert_equal('abc', a.s1) + assert_equal('def', a.s2) + END + v9.CheckSourceSuccess(lines) + + # Test for changing a const object variable from an object method called from + # the 'new' function + lines =<< trim END + vim9script + class A + const s1: string = 'abc' + def new() + this.ChangeStr() + enddef + def ChangeStr() + this.s1 = 'def' + enddef + endclass + var a = A.new() + END + v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "s1" in class "A"', 1) + + # Test for a const class variable + lines =<< trim END + vim9script + class A + static const s1: string = "abc" + endclass + assert_equal('abc', A.s1) + END + v9.CheckSourceSuccess(lines) + + # Test for changing a const class variable from a class function + lines =<< trim END + vim9script + class A + static const s1: string = "abc" + static def Foo() + s1 = "def" + enddef + endclass + A.Foo() + END + v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "s1" in class "A"', 1) + + # Test for changing a public const class variable at script level + lines =<< trim END + vim9script + class A + public static const s1: string = "abc" + endclass + assert_equal('abc', A.s1) + A.s1 = 'def' + END + v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "s1" in class "A"', 6) + + # Test for changing a public const class variable from a class function + lines =<< trim END + vim9script + class A + public static const s1: string = "abc" + static def Foo() + s1 = "def" + enddef + endclass + A.Foo() + END + v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "s1" in class "A"', 1) + + # Test for changing a public const class variable from a function + lines =<< trim END + vim9script + class A + public static const s1: string = "abc" + endclass + def Foo() + A.s1 = 'def' + enddef + defcompile + END + v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "s1" in class "A"', 1) + + # Test for changing a const List item from an object function + lines =<< trim END + vim9script + class A + public const l: list + def new() + this.l = [1, 2] + enddef + def Foo() + this.l[0] = 3 + enddef + endclass + var a = A.new() + assert_equal([1, 2], a.l) + a.Foo() + END + v9.CheckSourceFailure(lines, 'E1119: Cannot change locked list item', 1) + + # Test for adding a value to a const List from an object function + lines =<< trim END + vim9script + class A + public const l: list + def new() + this.l = [1, 2] + enddef + def Foo() + this.l->add(3) + enddef + endclass + var a = A.new() + a.Foo() + END + v9.CheckSourceFailure(lines, 'E741: Value is locked: add() argument', 1) + + # Test for reassigning a const List from an object function + lines =<< trim END + vim9script + class A + public const l: list = [1, 2] + def Foo() + this.l = [3, 4] + enddef + endclass + var a = A.new() + a.Foo() + END + v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "l" in class "A"', 1) + + # Test for changing a const List item at script level + lines =<< trim END + vim9script + class A + public const l: list = [1, 2] + endclass + var a = A.new() + a.l[0] = 3 + END + v9.CheckSourceFailure(lines, 'E741: Value is locked:', 6) + + # Test for adding a value to a const List item at script level + lines =<< trim END + vim9script + class A + public const l: list = [1, 2] + endclass + var a = A.new() + a.l->add(4) + END + v9.CheckSourceFailure(lines, 'E741: Value is locked:', 6) + + # Test for changing a const List item from a function + lines =<< trim END + vim9script + class A + public const l: list = [1, 2] + endclass + def Foo() + var a = A.new() + a.l[0] = 3 + enddef + Foo() + END + v9.CheckSourceFailure(lines, 'E1119: Cannot change locked list item', 2) + + # Test for adding a value to a const List item from a function + lines =<< trim END + vim9script + class A + public const l: list = [1, 2] + endclass + def Foo() + var a = A.new() + a.l->add(4) + enddef + Foo() + END + v9.CheckSourceFailure(lines, 'E741: Value is locked: add() argument', 2) + + # Test for changing a const List item from an object method + lines =<< trim END + vim9script + class A + public const l: list = [1, 2] + def Foo() + this.l[0] = 3 + enddef + endclass + var a = A.new() + a.Foo() + END + v9.CheckSourceFailure(lines, 'E1119: Cannot change locked list item', 1) + + # Test for adding a value to a const List item from an object method + lines =<< trim END + vim9script + class A + public const l: list = [1, 2] + def Foo() + this.l->add(4) + enddef + endclass + var a = A.new() + a.Foo() + END + v9.CheckSourceFailure(lines, 'E741: Value is locked: add() argument', 1) + + # Test for reassigning a const List object variable at script level + lines =<< trim END + vim9script + class A + public const l: list = [1, 2] + endclass + var a = A.new() + a.l = [3, 4] + END + v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "l" in class "A"', 6) + + # Test for reassigning a const List object variable from an object method + lines =<< trim END + vim9script + class A + public const l: list = [1, 2] + def Foo() + this.l = [3, 4] + enddef + endclass + var a = A.new() + a.Foo() + END + v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "l" in class "A"', 1) + + # Test for reassigning a const List object variable from another function + lines =<< trim END + vim9script + class A + public const l: list = [1, 2] + endclass + def Foo() + var a = A.new() + a.l = [3, 4] + enddef + Foo() + END + v9.CheckSourceFailure(lines, 'E1409: Cannot change read-only variable "l" in class "A"', 2) + + # Error case: Use 'const' with just a variable name + lines =<< trim END + vim9script + class A + const foo + endclass + var a = A.new() + END + v9.CheckSourceFailure(lines, 'E1022: Type or initialization required', 3) + + # Error case: Use 'const' followed by 'public' + lines =<< trim END + vim9script + class A + const public foo: number + endclass + var a = A.new() + END + v9.CheckSourceFailure(lines, 'E1022: Type or initialization required', 3) + + # Error case: Use 'const' followed by 'static' + lines =<< trim END + vim9script + class A + const static foo: number + endclass + var a = A.new() + END + v9.CheckSourceFailure(lines, 'E1022: Type or initialization required', 3) + + # Error case: 'const' cannot be used in an interface + lines =<< trim END + vim9script + interface A + const foo: number = 10 + endinterface + END + v9.CheckSourceFailure(lines, 'E1410: Const variable not supported in an interface', 3) + + # Error case: 'const' not supported for an object method + lines =<< trim END + vim9script + class A + const def Foo() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1022: Type or initialization required', 3) + + # Error case: 'const' not supported for a class method + lines =<< trim END + vim9script + class A + static const def Foo() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1022: Type or initialization required', 3) +enddef + +" Test for using double underscore prefix in a class/object method name. +def Test_method_double_underscore_prefix() + # class method + var lines =<< trim END + vim9script + class A + static def __foo() + echo "foo" + enddef + endclass + defcompile + END + v9.CheckSourceFailure(lines, 'E1034: Cannot use reserved name __foo()', 3) + + # object method + lines =<< trim END + vim9script + class A + def __foo() + echo "foo" + enddef + endclass + defcompile + END + v9.CheckSourceFailure(lines, 'E1034: Cannot use reserved name __foo()', 3) +enddef + +" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker -- cgit v1.2.3