| " 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) |
| |
| # Additional command after "class name" |
| lines =<< trim END |
| vim9script |
| class Something | var x = 10 |
| endclass |
| END |
| v9.CheckSourceFailure(lines, "E488: Trailing characters: | var x = 10", 2) |
| |
| # Additional command after "object variable" |
| lines =<< trim END |
| vim9script |
| class Something |
| var l: list<number> = [] | var y = 10 |
| endclass |
| END |
| v9.CheckSourceFailure(lines, "E488: Trailing characters: | var y = 10", 3) |
| |
| # Additional command after "class variable" |
| lines =<< trim END |
| vim9script |
| class Something |
| static var d = {a: 10} | var y = 10 |
| endclass |
| END |
| v9.CheckSourceFailure(lines, "E488: Trailing characters: | var y = 10", 3) |
| |
| # Additional command after "object method" |
| lines =<< trim END |
| vim9script |
| class Something |
| def Foo() | var y = 10 |
| enddef |
| endclass |
| END |
| v9.CheckSourceFailure(lines, "E488: Trailing characters: | var y = 10", 3) |
| |
| # Comments are allowed after an inline block |
| lines =<< trim END |
| vim9script |
| class Foo |
| static const bar = { # {{{ |
| baz: 'qux' |
| } # }}} |
| endclass |
| assert_equal({baz: 'qux'}, Foo.bar) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # Try to define a class with the same name as an existing variable |
| lines =<< trim END |
| vim9script |
| var Something: list<number> = [1] |
| class Thing |
| endclass |
| interface Api |
| endinterface |
| class Something extends Thing implements Api |
| var v1: string = '' |
| def Foo() |
| enddef |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E1041: Redefining script item: "Something"', 7) |
| |
| # Use old "this." prefixed member variable declaration syntax (without initialization) |
| 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 initialization) |
| 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 ".<variable>" |
| 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<TextPosition>', typename(TextPosition)) |
| assert_equal('object<TextPosition>', typename(pos)) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # When referencing object methods, space cannot be used after a "." |
| lines =<< trim END |
| vim9script |
| class A |
| def Foo(): number |
| return 10 |
| enddef |
| endclass |
| var a = A.new() |
| var v = a. Foo() |
| END |
| v9.CheckSourceFailure(lines, "E1202: 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, 'E1388: public keyword not supported for a method', 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, 'E1388: public keyword not supported for a method', 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, ['E1395: Using a null class', 'E1363: Incomplete type']) |
| |
| # Test for using a null class as a value |
| lines =<< trim END |
| vim9script |
| echo empty(null_class) |
| END |
| v9.CheckSourceFailure(lines, 'E1405: Class "" cannot be used as a value', 2) |
| |
| # Test for using a null class with string() |
| lines =<< trim END |
| vim9script |
| assert_equal('class [unknown]', string(null_class)) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # Test for using a null class with type() and typename() |
| lines =<< trim END |
| vim9script |
| assert_equal(12, type(null_class)) |
| assert_equal('class<Unknown>', typename(null_class)) |
| END |
| v9.CheckSourceSuccess(lines) |
| enddef |
| |
| def Test_class_interface_wrong_end() |
| var lines =<< trim END |
| vim9script |
| abstract class SomeName |
| var member = 'text' |
| endinterface |
| END |
| v9.CheckSourceFailure(lines, 'E476: Invalid command: endinterface, expected endclass', 4) |
| |
| lines =<< trim END |
| vim9script |
| export interface AnotherName |
| var member: string |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E476: Invalid command: endclass, expected endinterface', 4) |
| enddef |
| |
| def Test_object_not_set() |
| # Use an uninitialized object in script context |
| var lines =<< trim END |
| 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) |
| |
| # Reference a object variable through a null class object which is stored in a |
| # variable of type "any". |
| lines =<< trim END |
| vim9script |
| |
| def Z() |
| var o: any = null_object |
| o.v = 4 |
| enddef |
| Z() |
| END |
| v9.CheckSourceFailure(lines, 'E1360: Using a null object', 2) |
| |
| # Do "echom" of a null object variable. |
| lines =<< trim END |
| vim9script |
| |
| def X() |
| var x = null_object |
| echom x |
| enddef |
| X() |
| END |
| v9.CheckSourceFailure(lines, 'E1324: Using an Object as a String', 2) |
| |
| # Use a null object variable that vim wants to force to number. |
| lines =<< trim END |
| vim9script |
| |
| def X() |
| var o = null_object |
| var l = [ 1, o] |
| sort(l, 'N') |
| enddef |
| X() |
| END |
| v9.CheckSourceFailure(lines, 'E1324: Using an Object as a String', 3) |
| 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<Unknown>', typename(F())) |
| |
| var o0 = F() |
| assert_true(o0 == null_object) |
| assert_true(o0 == null) |
| |
| var o1: any = nullo |
| assert_true(o1 == null_object) |
| assert_true(o1 == null) |
| |
| def G() |
| var x = null_object |
| enddef |
| |
| class C |
| endclass |
| var o2: C |
| assert_true(o2 == null_object) |
| assert_true(o2 == null) |
| |
| o2 = null_object |
| assert_true(o2 == null) |
| |
| o2 = C.new() |
| assert_true(o2 != null) |
| |
| o2 = null_object |
| assert_true(o2 == null) |
| END |
| v9.CheckSourceSuccess(lines) |
| enddef |
| |
| " 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 |
| public 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<Foo>) |
| for foo in fooList |
| foo.Add() |
| endfor |
| enddef |
| |
| var l: list<Foo> = [Foo.new()] |
| ProcessList(l) |
| END |
| v9.CheckSourceSuccess(lines) |
| enddef |
| |
| def Test_expr_after_using_object() |
| var lines =<< trim END |
| vim9script |
| |
| class Something |
| 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<number> = [] |
| |
| def Add(n: number): any |
| this.x->add(n) |
| return this |
| enddef |
| endclass |
| |
| echo Foo.new().Add(1).Add(2).x |
| echo Foo.new().Add(1).Add(2) |
| .x |
| echo Foo.new().Add(1) |
| .Add(2).x |
| echo Foo.new() |
| .Add(1).Add(2).x |
| echo Foo.new() |
| .Add(1) |
| .Add(2) |
| .x |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # Test for "public" cannot be abbreviated |
| lines =<< trim END |
| vim9script |
| class Something |
| pub 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<One> but got object<Two>', 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<list<number>> |
| 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<list<number>> |
| 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<list<number>> |
| 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<list<number>> |
| 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<list<number>> |
| 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<list<number>> |
| 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<number> = [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) |
| |
| # check string() with object nesting |
| lines =<< trim END |
| vim9script |
| class C |
| var nest1: C |
| var nest2: C |
| def Init(n1: C, n2: C) |
| this.nest1 = n1 |
| this.nest2 = n2 |
| enddef |
| endclass |
| |
| var o1 = C.new() |
| var o2 = C.new() |
| o1.Init(o1, o2) |
| o2.Init(o2, o1) |
| |
| # The following previously put's vim into an infinite loop. |
| |
| var expect = "object of C {nest1: object of C {...}, nest2: object of C {nest1: object of C {...}, nest2: object of C {...}}}" |
| assert_equal(expect, string(o1)) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| |
| class B |
| endclass |
| |
| class C |
| var b: B |
| var c: C |
| endclass |
| |
| var o1 = C.new(B.new(), C.new(B.new())) |
| var expect = "object of C {b: object of B {}, c: object of C {b: object of B {}, c: object of [unknown]}}" |
| assert_equal(expect, string(o1)) |
| END |
| v9.CheckSourceSuccess(lines) |
| enddef |
| |
| def Test_interface_basics() |
| var lines =<< trim END |
| vim9script |
| interface Something |
| var ro_var: list<number> |
| def GetCount(): number |
| endinterface |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| lines =<< trim END |
| interface SomethingWrong |
| static var count = 7 |
| endinterface |
| END |
| v9.CheckSourceFailure(lines, 'E1342: Interface can only be defined in Vim9 script', 1) |
| |
| lines =<< trim END |
| vim9script |
| |
| interface Some |
| var value: number |
| def Method(value: number) |
| endinterface |
| END |
| # The argument name and the object member name are the same, but this is not a |
| # problem because object members are always accessed with the "this." prefix. |
| v9.CheckSourceSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| interface somethingWrong |
| static var count = 7 |
| endinterface |
| END |
| v9.CheckSourceFailure(lines, 'E1343: Interface name must start with an uppercase letter: somethingWrong', 2) |
| |
| lines =<< trim END |
| vim9script |
| interface SomethingWrong |
| var value: string |
| var count = 7 |
| def GetCount(): number |
| endinterface |
| END |
| v9.CheckSourceFailure(lines, 'E1344: Cannot initialize a variable in an interface', 4) |
| |
| lines =<< trim END |
| vim9script |
| interface SomethingWrong |
| var value: string |
| var count: number |
| def GetCount(): number |
| return 5 |
| enddef |
| endinterface |
| END |
| v9.CheckSourceFailure(lines, 'E1345: Not a valid command in an interface: return 5', 6) |
| |
| # Test for "interface" cannot be abbreviated |
| lines =<< trim END |
| vim9script |
| inte Something |
| endinterface |
| END |
| v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: inte Something', 2) |
| |
| # Test for "endinterface" cannot be abbreviated |
| lines =<< trim END |
| vim9script |
| interface Something |
| endin |
| END |
| v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: endin', 3) |
| |
| # Additional commands after "interface name" |
| lines =<< trim END |
| vim9script |
| interface Something | var x = 10 | var y = 20 |
| endinterface |
| END |
| v9.CheckSourceFailure(lines, "E488: Trailing characters: | var x = 10", 2) |
| |
| lines =<< trim END |
| vim9script |
| export interface EnterExit |
| def Enter(): void |
| def Exit(): void |
| endinterface |
| END |
| writefile(lines, 'XdefIntf.vim', 'D') |
| |
| lines =<< trim END |
| vim9script |
| import './XdefIntf.vim' as defIntf |
| export def With(ee: defIntf.EnterExit, F: func) |
| ee.Enter() |
| try |
| F() |
| finally |
| ee.Exit() |
| endtry |
| enddef |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| var imported =<< trim END |
| vim9script |
| export abstract class EnterExit |
| def Enter(): void |
| enddef |
| def Exit(): void |
| enddef |
| endclass |
| END |
| writefile(imported, 'XdefIntf2.vim', 'D') |
| |
| lines[1] = " import './XdefIntf2.vim' as defIntf" |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| " Test for using string() with an interface |
| def Test_interface_to_string() |
| var lines =<< trim END |
| vim9script |
| interface Intf |
| def Method(nr: number) |
| endinterface |
| assert_equal("interface Intf", string(Intf)) |
| END |
| v9.CheckSourceSuccess(lines) |
| enddef |
| |
| def Test_class_implements_interface() |
| var lines =<< trim END |
| vim9script |
| |
| interface Some |
| var count: number |
| def Method(nr: number) |
| endinterface |
| |
| class SomeImpl implements Some |
| var count: number |
| def Method(nr: number) |
| echo nr |
| enddef |
| endclass |
| |
| interface Another |
| var member: string |
| endinterface |
| |
| class AnotherImpl implements Some, Another |
| var member = 'abc' |
| var count = 20 |
| def Method(nr: number) |
| echo nr |
| enddef |
| endclass |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| |
| interface Some |
| var count: number |
| endinterface |
| |
| class SomeImpl implements Some implements Some |
| var count: number |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E1350: Duplicate "implements"', 7) |
| |
| lines =<< trim END |
| vim9script |
| |
| interface Some |
| var count: number |
| endinterface |
| |
| class SomeImpl implements Some, Some |
| var count: number |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E1351: Duplicate interface after "implements": Some', 7) |
| |
| lines =<< trim END |
| vim9script |
| |
| interface Some |
| var counter: number |
| def Method(nr: number) |
| endinterface |
| |
| class SomeImpl implements Some |
| var count: number |
| def Method(nr: number) |
| echo nr |
| enddef |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E1348: Variable "counter" of interface "Some" is not implemented', 13) |
| |
| lines =<< trim END |
| vim9script |
| |
| interface Some |
| var count: number |
| def Methods(nr: number) |
| endinterface |
| |
| class SomeImpl implements Some |
| var count: number |
| def Method(nr: number) |
| echo nr |
| enddef |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E1349: Method "Methods" of interface "Some" is not implemented', 13) |
| |
| # Check different order of members in class and interface works. |
| lines =<< trim END |
| vim9script |
| |
| interface Result |
| var label: string |
| var errpos: number |
| endinterface |
| |
| # order of members is opposite of interface |
| class Failure implements Result |
| public var lnum: number = 5 |
| var errpos: number = 42 |
| var label: string = 'label' |
| endclass |
| |
| def Test() |
| var result: Result = Failure.new() |
| |
| assert_equal('label', result.label) |
| assert_equal(42, result.errpos) |
| enddef |
| |
| Test() |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # Interface name after "extends" doesn't end in a space or NUL character |
| lines =<< trim END |
| vim9script |
| interface A |
| endinterface |
| class B extends A" |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E1315: White space required after name: A"', 4) |
| |
| # Trailing characters after a class name |
| lines =<< trim END |
| vim9script |
| class A bbb |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E488: Trailing characters: bbb', 2) |
| |
| # using "implements" with a non-existing class |
| lines =<< trim END |
| vim9script |
| class A implements B |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E1346: Interface name not found: B', 3) |
| |
| # using "implements" with a regular class |
| lines =<< trim END |
| vim9script |
| class A |
| endclass |
| class B implements A |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E1347: Not a valid interface: A', 5) |
| |
| # using "implements" with a variable |
| lines =<< trim END |
| vim9script |
| var T: number = 10 |
| class A implements T |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E1347: Not a valid interface: T', 4) |
| |
| # implements should be followed by a white space |
| lines =<< trim END |
| vim9script |
| interface A |
| endinterface |
| class B implements A; |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E1315: White space required after name: A;', 4) |
| |
| lines =<< trim END |
| vim9script |
| |
| interface One |
| def IsEven(nr: number): bool |
| endinterface |
| class Two implements One |
| def IsEven(nr: number): string |
| enddef |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E1383: Method "IsEven": type mismatch, expected func(number): bool but got func(number): string', 9) |
| |
| lines =<< trim END |
| vim9script |
| |
| interface One |
| def IsEven(nr: number): bool |
| endinterface |
| class Two implements One |
| def IsEven(nr: bool): bool |
| enddef |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E1383: Method "IsEven": type mismatch, expected func(number): bool but got func(bool): bool', 9) |
| |
| lines =<< trim END |
| vim9script |
| |
| interface One |
| def IsEven(nr: number): bool |
| endinterface |
| class Two implements One |
| def IsEven(nr: number, ...extra: list<number>): bool |
| enddef |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E1383: Method "IsEven": type mismatch, expected func(number): bool but got func(number, ...list<number>): bool', 9) |
| |
| # access superclass interface members from subclass, mix variable order |
| lines =<< trim END |
| vim9script |
| |
| interface I1 |
| var mvar1: number |
| var mvar2: number |
| endinterface |
| |
| # NOTE: the order is swapped |
| class A implements I1 |
| var mvar2: number |
| var mvar1: number |
| public static var svar2: number |
| public static var svar1: number |
| def new() |
| svar1 = 11 |
| svar2 = 12 |
| this.mvar1 = 111 |
| this.mvar2 = 112 |
| enddef |
| endclass |
| |
| class B extends A |
| def new() |
| this.mvar1 = 121 |
| this.mvar2 = 122 |
| enddef |
| endclass |
| |
| class C extends B |
| def new() |
| this.mvar1 = 131 |
| this.mvar2 = 132 |
| enddef |
| endclass |
| |
| def F2(i: I1): list<number> |
| return [ i.mvar1, i.mvar2 ] |
| enddef |
| |
| var oa = A.new() |
| var ob = B.new() |
| var oc = C.new() |
| |
| assert_equal([111, 112], F2(oa)) |
| assert_equal([121, 122], F2(ob)) |
| assert_equal([131, 132], F2(oc)) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # Access superclass interface members from subclass, mix variable order. |
| # Two interfaces, one on A, one on B; each has both kinds of variables |
| lines =<< trim END |
| vim9script |
| |
| interface I1 |
| var mvar1: number |
| var mvar2: number |
| endinterface |
| |
| interface I2 |
| var mvar3: number |
| var mvar4: number |
| endinterface |
| |
| class A implements I1 |
| public static var svar1: number |
| public static var svar2: number |
| var mvar1: number |
| var mvar2: number |
| def new() |
| svar1 = 11 |
| svar2 = 12 |
| this.mvar1 = 111 |
| this.mvar2 = 112 |
| enddef |
| endclass |
| |
| class B extends A implements I2 |
| static var svar3: number |
| static var svar4: number |
| var mvar3: number |
| var mvar4: number |
| def new() |
| svar3 = 23 |
| svar4 = 24 |
| this.mvar1 = 121 |
| this.mvar2 = 122 |
| this.mvar3 = 123 |
| this.mvar4 = 124 |
| enddef |
| endclass |
| |
| class C extends B |
| public static var svar5: number |
| def new() |
| svar5 = 1001 |
| this.mvar1 = 131 |
| this.mvar2 = 132 |
| this.mvar3 = 133 |
| this.mvar4 = 134 |
| enddef |
| endclass |
| |
| def F2(i: I1): list<number> |
| return [ i.mvar1, i.mvar2 ] |
| enddef |
| |
| def F4(i: I2): list<number> |
| return [ i.mvar3, i.mvar4 ] |
| enddef |
| |
| var oa = A.new() |
| var ob = B.new() |
| var oc = C.new() |
| |
| assert_equal([[111, 112]], [F2(oa)]) |
| assert_equal([[121, 122], [123, 124]], [F2(ob), F4(ob)]) |
| assert_equal([[131, 132], [133, 134]], [F2(oc), F4(oc)]) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # Using two interface names without a space after the "," |
| lines =<< trim END |
| vim9script |
| interface A |
| endinterface |
| interface B |
| endinterface |
| class C implements A,B |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E1315: White space required after name: A,B', 6) |
| |
| # No interface name after a comma |
| lines =<< trim END |
| vim9script |
| interface A |
| endinterface |
| class B implements A, |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E1389: Missing name after implements', 4) |
| |
| # No interface name after implements |
| lines =<< trim END |
| vim9script |
| class A implements |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E1389: Missing name after implements', 2) |
| enddef |
| |
| def Test_call_interface_method() |
| var lines =<< trim END |
| vim9script |
| interface Base |
| def Enter(): void |
| endinterface |
| |
| class Child implements Base |
| def Enter(): void |
| g:result ..= 'child' |
| enddef |
| endclass |
| |
| def F(obj: Base) |
| obj.Enter() |
| enddef |
| |
| g:result = '' |
| F(Child.new()) |
| assert_equal('child', g:result) |
| unlet g:result |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| class Base |
| def Enter(): void |
| g:result ..= 'base' |
| enddef |
| endclass |
| |
| class Child extends Base |
| def Enter(): void |
| g:result ..= 'child' |
| enddef |
| endclass |
| |
| def F(obj: Base) |
| obj.Enter() |
| enddef |
| |
| g:result = '' |
| F(Child.new()) |
| assert_equal('child', g:result) |
| unlet g:result |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # method of interface returns a value |
| lines =<< trim END |
| vim9script |
| interface Base |
| def Enter(): string |
| endinterface |
| |
| class Child implements Base |
| def Enter(): string |
| g:result ..= 'child' |
| return "/resource" |
| enddef |
| endclass |
| |
| def F(obj: Base) |
| var r = obj.Enter() |
| g:result ..= r |
| enddef |
| |
| g:result = '' |
| F(Child.new()) |
| assert_equal('child/resource', g:result) |
| unlet g:result |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| class Base |
| def Enter(): string |
| return null_string |
| enddef |
| endclass |
| |
| class Child extends Base |
| def Enter(): string |
| g:result ..= 'child' |
| return "/resource" |
| enddef |
| endclass |
| |
| def F(obj: Base) |
| var r = obj.Enter() |
| g:result ..= r |
| enddef |
| |
| g:result = '' |
| F(Child.new()) |
| assert_equal('child/resource', g:result) |
| unlet g:result |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # No class that implements the interface. |
| lines =<< trim END |
| vim9script |
| |
| interface IWithEE |
| def Enter(): any |
| def Exit(): void |
| endinterface |
| |
| def With1(ee: IWithEE, F: func) |
| var r = ee.Enter() |
| enddef |
| |
| defcompile |
| END |
| v9.CheckSourceSuccess(lines) |
| enddef |
| |
| 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<Point> 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 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 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 |
| enddef |
| |
| " Test for using a method from the super class |
| def Test_super_dispatch() |
| # See #15448 and #15463 |
| var lines =<< trim END |
| vim9script |
| |
| class A |
| def String(): string |
| return 'A' |
| enddef |
| endclass |
| |
| class B extends A |
| def String(): string |
| return super.String() |
| enddef |
| endclass |
| |
| class C extends B |
| endclass |
| |
| assert_equal('A', C.new().String()) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| |
| class A |
| def F(): string |
| return 'AA' |
| enddef |
| endclass |
| |
| class B extends A |
| def F(): string |
| return 'BB' |
| enddef |
| def S(): string |
| return super.F() |
| enddef |
| def S0(): string |
| return this.S() |
| enddef |
| endclass |
| |
| class C extends B |
| def F(): string |
| return 'CC' |
| enddef |
| def ToB(): string |
| return super.F() |
| enddef |
| endclass |
| |
| assert_equal('AA', B.new().S()) |
| assert_equal('AA', C.new().S()) |
| assert_equal('AA', B.new().S0()) |
| assert_equal('AA', C.new().S0()) |
| |
| assert_equal('BB', C.new().ToB()) |
| |
| assert_equal('CC', C.new().F()) |
| assert_equal('BB', B.new().F()) |
| assert_equal('AA', A.new().F()) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| |
| var call_chain: list<string> |
| |
| abstract class A |
| abstract def _G(): string |
| |
| def F(): string |
| call_chain->add('A.F()') |
| return this._G() |
| enddef |
| def _H(): string |
| call_chain->add('A._H()') |
| return this.F() |
| enddef |
| endclass |
| |
| class B extends A |
| def _G(): string |
| call_chain->add('B.G()') |
| return 'BBB' |
| enddef |
| def SF(): string |
| call_chain->add('B.SF()') |
| return super._H() |
| enddef |
| endclass |
| |
| class C extends B |
| endclass |
| |
| class D extends C |
| def SF(): string |
| call_chain->add('D.SF()') |
| return super.SF() |
| enddef |
| endclass |
| |
| class E extends D |
| def SF(): string |
| call_chain->add('E.SF()') |
| return super.SF() |
| enddef |
| endclass |
| |
| class F extends E |
| def _G(): string |
| call_chain->add('F._G()') |
| return 'FFF' |
| enddef |
| endclass |
| |
| # E.new() -> A.F() -> B._G() |
| call_chain = [] |
| var o1 = E.new() |
| assert_equal('BBB', o1.F()) |
| assert_equal(['A.F()', 'B.G()'], call_chain) |
| |
| # F.new() -> E.SF() -> D.SF() -> B.SF() -> A._H() -> A.F() -> F._G() |
| call_chain = [] |
| var o2 = F.new() |
| assert_equal('FFF', o2.SF()) |
| assert_equal(['E.SF()', 'D.SF()', 'B.SF()', 'A._H()', 'A.F()', 'F._G()'], call_chain) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # problems with method dispatch: super -> abstract |
| # https://github.com/vim/vim/issues/15514 |
| lines =<< trim END |
| vim9script |
| abstract class B |
| abstract def ToString(): string |
| endclass |
| |
| class C extends B |
| def ToString(): string |
| return super.ToString() |
| enddef |
| endclass |
| |
| try |
| defcompile C.ToString |
| call assert_false(1, 'command should have failed') |
| catch |
| call assert_exception('E1431: Abstract method "ToString" in class "B" cannot be accessed directly') |
| endtry |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # problems with method dispatch: super -> abstract -> concrete |
| lines =<< trim END |
| vim9script |
| |
| class A |
| def ToString() |
| echo 'A' |
| enddef |
| endclass |
| |
| abstract class B extends A |
| abstract def ToString() |
| endclass |
| |
| class C extends B |
| def ToString() |
| super.ToString() |
| enddef |
| endclass |
| |
| try |
| defcompile C.ToString |
| call assert_false(1, 'command should have failed') |
| catch |
| call assert_exception('E1431: Abstract method "ToString" in class "B" cannot be accessed directly') |
| endtry |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # Invoking a super method and an interface method which have the same name. |
| lines =<< trim END |
| vim9script |
| |
| interface I |
| def ToString(): string |
| endinterface |
| |
| # Note that A does not implement I. |
| class A |
| def ToString(): string |
| return 'A' |
| enddef |
| endclass |
| |
| class B extends A implements I |
| def ToString(): string |
| return super.ToString() |
| enddef |
| endclass |
| |
| def TestI(i: I): string |
| return i.ToString() |
| enddef |
| |
| assert_equal('A', B.new().ToString()) |
| assert_equal('A', TestI(B.new())) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # super and an abstract class with no abstract methods |
| lines =<< trim END |
| vim9script |
| |
| class A |
| def ToString(): string |
| return 'A' |
| enddef |
| endclass |
| |
| # An abstract class with no abstract methods. |
| abstract class B extends A |
| endclass |
| |
| class C extends B |
| def ToString(): string |
| return super.ToString() |
| enddef |
| endclass |
| |
| def TestA(a: A): string |
| return a.ToString() |
| enddef |
| |
| def TestB(b: B): string |
| return b.ToString() |
| enddef |
| |
| assert_equal('A', C.new().ToString()) |
| assert_equal('A', TestA(A.new())) |
| assert_equal('A', TestA(C.new())) |
| assert_equal('A', TestB(C.new())) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # super and an abstract class with no abstract methods and the initial |
| # implements clause |
| lines =<< trim END |
| vim9script |
| |
| interface I |
| def ToString(): string |
| endinterface |
| |
| # Note that A does not implement I. |
| class A |
| def ToString(): string |
| return 'A' |
| enddef |
| endclass |
| |
| # An abstract class with no abstract methods. |
| abstract class B extends A implements I |
| endclass |
| |
| class C extends B implements I |
| def ToString(): string |
| return super.ToString() |
| enddef |
| endclass |
| |
| # Note that A.ToString() is different from I.ToString(). |
| def TestA(a: A): string |
| return a.ToString() |
| enddef |
| |
| assert_equal('A', C.new().ToString()) |
| assert_equal('A', TestA(A.new())) |
| assert_equal('A', TestA(C.new())) |
| END |
| v9.CheckSourceSuccess(lines) |
| enddef |
| |
| def Test_class_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 importing a class into a legacy script and calling the class method |
| def Test_class_method_from_legacy_script() |
| var lines =<< trim END |
| vim9script |
| export class A |
| static var name: string = 'a' |
| static def SetName(n: string) |
| name = n |
| enddef |
| endclass |
| END |
| writefile(lines, 'Xvim9export.vim', 'D') |
| |
| lines =<< trim END |
| import './Xvim9export.vim' as vim9 |
| |
| call s:vim9.A.SetName('b') |
| call assert_equal('b', s:vim9.A.name) |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| " Test for 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) |
| |
| # Test for "abstract" cannot be abbreviated |
| lines =<< trim END |
| vim9script |
| abs class A |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: abs class A', 2) |
| |
| # Additional commands after "abstract class" |
| lines =<< trim END |
| vim9script |
| abstract class Something | var x = [] |
| endclass |
| END |
| v9.CheckSourceFailure(lines, "E488: Trailing characters: | var x = []", 2) |
| |
| # 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) |
| |
| # extending an abstract class with class methods and variables |
| lines =<< trim END |
| vim9script |
| abstract class A |
| static var s: string = 'vim' |
| static def Fn(): list<number> |
| return [10] |
| enddef |
| endclass |
| class B extends A |
| endclass |
| var b = B.new() |
| assert_equal('vim', A.s) |
| assert_equal([10], A.Fn()) |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| def Test_closure_in_class() |
| var lines =<< trim END |
| vim9script |
| |
| class Foo |
| var y: list<string> = ['B'] |
| |
| def new() |
| g:result = filter(['A', 'B'], (_, v) => index(this.y, v) == -1) |
| enddef |
| endclass |
| |
| Foo.new() |
| assert_equal(['A'], g:result) |
| END |
| v9.CheckSourceSuccess(lines) |
| enddef |
| |
| def Test_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<any>): 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>): 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 '<Foo>' |
| enddef |
| endclass |
| |
| def Stack(w1: Widget, w2: Widget): list<Widget> |
| w1.SetY(1) |
| w2.SetY(2) |
| return [w1, w2] |
| enddef |
| |
| var foo1 = Foo.new() |
| var foo2 = Foo.new() |
| var l = Stack(foo1, foo2) |
| END |
| v9.CheckSourceSuccess(lines) |
| enddef |
| |
| " Test for calling methods from three levels of classes |
| def Test_multi_level_method_call() |
| var lines =<< trim END |
| vim9script |
| |
| var A_func1: number = 0 |
| var A_func2: number = 0 |
| var A_func3: number = 0 |
| var B_func2: number = 0 |
| var B_func3: number = 0 |
| var C_func3: number = 0 |
| |
| class A |
| def Func1() |
| A_func1 += 1 |
| enddef |
| |
| def Func2() |
| A_func2 += 1 |
| enddef |
| |
| def Func3() |
| A_func3 += 1 |
| enddef |
| endclass |
| |
| class B extends A |
| def Func2() |
| B_func2 += 1 |
| enddef |
| |
| def Func3() |
| B_func3 += 1 |
| enddef |
| endclass |
| |
| class C extends B |
| def Func3() |
| C_func3 += 1 |
| enddef |
| endclass |
| |
| def A_CallFuncs(a: A) |
| a.Func1() |
| a.Func2() |
| a.Func3() |
| enddef |
| |
| def B_CallFuncs(b: B) |
| b.Func1() |
| b.Func2() |
| b.Func3() |
| enddef |
| |
| def C_CallFuncs(c: C) |
| c.Func1() |
| c.Func2() |
| c.Func3() |
| enddef |
| |
| var cobj = C.new() |
| A_CallFuncs(cobj) |
| B_CallFuncs(cobj) |
| C_CallFuncs(cobj) |
| assert_equal(3, A_func1) |
| assert_equal(0, A_func2) |
| assert_equal(0, A_func3) |
| assert_equal(3, B_func2) |
| assert_equal(0, B_func3) |
| assert_equal(3, C_func3) |
| END |
| v9.CheckSourceSuccess(lines) |
| enddef |
| |
| " Test for using members from three levels of classes |
| def Test_multi_level_member_access() |
| var lines =<< trim END |
| vim9script |
| |
| class A |
| public 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 <stack> with class methods. |
| def Test_stack_expansion_with_methods() |
| var lines =<< trim END |
| vim9script |
| |
| class C |
| def M1() |
| F0() |
| enddef |
| endclass |
| |
| def F0() |
| assert_match('<SNR>\d\+_F\[1\]\.\.<SNR>\d\+_C\.M1\[1\]\.\.<SNR>\d\+_F0\[1\]$', expand('<stack>')) |
| enddef |
| |
| def F() |
| C.new().M1() |
| enddef |
| |
| F() |
| END |
| v9.CheckSourceSuccess(lines) |
| enddef |
| |
| " Test the return type of the new() constructor |
| def Test_new_return_type() |
| # new() uses the default return type and there is no return statement |
| var lines =<< trim END |
| vim9script |
| |
| class C |
| 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<C>', typename(c)) |
| |
| var v1: C |
| v1 = C.new(12345) |
| assert_equal('object<C>', typename(v1)) |
| |
| def F() |
| var v2: C |
| v2 = C.new(12345) |
| assert_equal('object<C>', typename(v2)) |
| enddef |
| F() |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # new() uses the default return type and an empty 'return' statement |
| lines =<< trim END |
| vim9script |
| |
| class C |
| 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<C>', typename(c)) |
| |
| var v1: C |
| v1 = C.new(12345) |
| assert_equal('object<C>', typename(v1)) |
| |
| def F() |
| var v2: C |
| v2 = C.new(12345) |
| assert_equal('object<C>', typename(v2)) |
| enddef |
| F() |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # new() uses "any" return type and returns "this" |
| lines =<< trim END |
| vim9script |
| |
| class C |
| 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<any> |
| |
| def new(): dict<any> |
| this._state = {} |
| return this._state |
| enddef |
| endclass |
| |
| var c = C.new() |
| assert_equal('object<C>', typename(c)) |
| END |
| v9.CheckSourceFailure(lines, 'E1365: 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<C> = { a: C.new(1), b: C.new(2), c: C.new(3), } |
| lockvar 2 some_dict |
| |
| var current: C |
| current = some_dict['c'] |
| assert_equal(3, current.val) |
| current = some_dict['b'] |
| assert_equal(2, current.val) |
| |
| def F() |
| current = some_dict['c'] |
| enddef |
| |
| def G() |
| current = some_dict['b'] |
| enddef |
| |
| F() |
| assert_equal(3, current.val) |
| G() |
| assert_equal(2, current.val) |
| END |
| v9.CheckSourceSuccess(lines) |
| enddef |
| |
| " Test 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') |
| |
| # read-only lockvar from object method arg |
| lines =<< trim END |
| vim9script |
| |
| class C |
| var val5: number |
| def Lock(c: C) |
| lockvar c.val5 |
| enddef |
| endclass |
| var o = C.new(3) |
| o.Lock(C.new(5)) |
| END |
| v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val5" in class "C"') |
| |
| # read-only lockvar from class method arg |
| lines =<< trim END |
| vim9script |
| |
| class C |
| var val6: number |
| static def Lock(c: C) |
| lockvar c.val6 |
| enddef |
| endclass |
| var o = C.new(3) |
| C.Lock(o) |
| END |
| v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val6" in class "C"') |
| |
| # |
| # 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(c: C) |
| lockvar c.val5 |
| enddef |
| endclass |
| var o = C.new(3) |
| o.Lock(C.new(5)) |
| END |
| v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val5" in class "C"', 1) |
| |
| # lockvar from class method arg |
| lines =<< trim END |
| vim9script |
| |
| class C |
| public var val6: number |
| static def Lock(c: C) |
| lockvar c.val6 |
| enddef |
| endclass |
| var o = C.new(3) |
| C.Lock(o) |
| END |
| v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "c.val6" in class "C"', 1) |
| enddef |
| |
| " Test trying to lock a class variable from various places |
| def Test_lockvar_class_variable() |
| |
| # lockvar bare static from object method |
| var lines =<< trim END |
| vim9script |
| |
| class C |
| public static var sval1: number |
| def Lock() |
| lockvar sval1 |
| enddef |
| endclass |
| var o = C.new() |
| o.Lock() |
| END |
| v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval1" in class "C"', 1) |
| |
| # lockvar C.static from object method |
| lines =<< trim END |
| vim9script |
| |
| class C |
| public static var sval2: number |
| def Lock() |
| lockvar C.sval2 |
| enddef |
| endclass |
| var o = C.new() |
| o.Lock() |
| END |
| v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval2" in class "C"', 1) |
| |
| # lockvar bare static from class method |
| lines =<< trim END |
| vim9script |
| |
| class C |
| public static var sval3: number |
| static def Lock() |
| lockvar sval3 |
| enddef |
| endclass |
| C.Lock() |
| END |
| v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval3" in class "C"', 1) |
| |
| # lockvar C.static from class method |
| lines =<< trim END |
| vim9script |
| |
| class C |
| public static var sval4: number |
| static def Lock() |
| lockvar C.sval4 |
| enddef |
| endclass |
| C.Lock() |
| END |
| v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval4" in class "C"', 1) |
| |
| # lockvar C.static from script level |
| lines =<< trim END |
| vim9script |
| |
| class C |
| public static var sval5: number |
| endclass |
| lockvar C.sval5 |
| END |
| v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "C.sval5" in class "C"', 6) |
| |
| # lockvar o.static from script level |
| lines =<< trim END |
| vim9script |
| |
| class C |
| public static var sval6: number |
| endclass |
| var o = C.new() |
| lockvar o.sval6 |
| END |
| v9.CheckSourceFailure(lines, 'E1375: Class variable "sval6" accessible only using class "C"', 7) |
| enddef |
| |
| " Test locking an argument to :def |
| def Test_lockvar_argument() |
| # Lockvar a function arg |
| var lines =<< trim END |
| vim9script |
| |
| def Lock(val: any) |
| lockvar val |
| enddef |
| |
| var d = {a: 1, b: 2} |
| Lock(d) |
| |
| d->extend({c: 3}) |
| END |
| v9.CheckSourceFailure(lines, 'E741: Value is locked: extend() argument') |
| |
| # Lockvar a function arg. Verify "sval" is interpreted as argument and not a |
| # class member in "C". This tests lval_root_is_arg. |
| lines =<< trim END |
| vim9script |
| |
| class C |
| public static var sval: list<number> |
| endclass |
| |
| def Lock2(sval: any) |
| lockvar sval |
| enddef |
| |
| var o = C.new() |
| Lock2(o) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # Lock a class. |
| lines =<< trim END |
| vim9script |
| |
| class C |
| public static var sval: list<number> |
| endclass |
| |
| def Lock2(sval: any) |
| lockvar sval |
| enddef |
| |
| Lock2(C) |
| END |
| v9.CheckSourceFailure(lines, 'E1405: Class "C" cannot be used as a value') |
| |
| # Lock an object. |
| lines =<< trim END |
| vim9script |
| |
| class C |
| public static var sval: list<number> |
| endclass |
| |
| def Lock2(sval: any) |
| lockvar sval |
| enddef |
| |
| Lock2(C.new()) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # In this case (unlike previous) "lockvar sval" is a class member. |
| lines =<< trim END |
| vim9script |
| |
| class C |
| public static var sval: list<number> |
| def Lock2() |
| lockvar sval |
| enddef |
| endclass |
| |
| |
| var o = C.new() |
| o.Lock2() |
| END |
| v9.CheckSourceFailure(lines, 'E1392: Cannot (un)lock class variable "sval" in class "C"', 1) |
| enddef |
| |
| " Test that this can be locked without error |
| def Test_lockvar_this() |
| # lockvar this |
| var lines =<< trim END |
| vim9script |
| class C |
| def TLock() |
| lockvar this |
| enddef |
| endclass |
| var o = C.new() |
| o.TLock() |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # lockvar four (four letter word, but not this) |
| lines =<< trim END |
| vim9script |
| class C |
| def TLock4() |
| var four: number |
| lockvar four |
| enddef |
| endclass |
| var o = C.new() |
| o.TLock4() |
| END |
| v9.CheckSourceFailure(lines, 'E1178: Cannot lock or unlock a local variable') |
| |
| # lockvar this5; "this" + one char, 5 letter word, starting with "this" |
| lines =<< trim END |
| vim9script |
| class C |
| def TLock5() |
| var this5: number |
| lockvar this5 |
| enddef |
| endclass |
| var o = C.new() |
| o.TLock5() |
| END |
| v9.CheckSourceFailure(lines, 'E1178: Cannot lock or unlock a local variable') |
| enddef |
| |
| " Test some general lockvar cases |
| def Test_lockvar_general() |
| # lockvar an object and a class. It does nothing |
| var lines =<< trim END |
| vim9script |
| class C |
| endclass |
| var o = C.new() |
| lockvar o |
| lockvar C |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # Lock a list element that's nested in an object variable from a :def |
| lines =<< trim END |
| vim9script |
| |
| class C |
| public var val: list<list<number>> = [ [1], [2], [3] ] |
| endclass |
| def Lock2(obj: any) |
| lockvar obj.val[1] |
| enddef |
| |
| var o = C.new() |
| Lock2(o) |
| o.val[0] = [9] |
| assert_equal([ [9], [2], [3] ], o.val) |
| try |
| o.val[1] = [999] |
| call assert_false(true, 'assign should have failed') |
| catch |
| assert_exception('E741:') |
| endtry |
| o.val[2] = [8] |
| assert_equal([ [9], [2], [8] ], o.val) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # Lock a list element that's nested in an object variable from scriptlevel |
| lines =<< trim END |
| vim9script |
| |
| class C |
| public var val: list<list<number>> = [ [1], [2], [3] ] |
| endclass |
| |
| var o = C.new() |
| lockvar o.val[1] |
| o.val[0] = [9] |
| assert_equal([ [9], [2], [3] ], o.val) |
| try |
| o.val[1] = [999] |
| call assert_false(true, 'assign should have failed') |
| catch |
| assert_exception('E741:') |
| endtry |
| o.val[2] = [8] |
| assert_equal([ [9], [2], [8] ], o.val) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # lock a script level variable from an object method |
| lines =<< trim END |
| vim9script |
| |
| class C |
| def Lock() |
| lockvar l |
| enddef |
| endclass |
| |
| var l = [1] |
| C.new().Lock() |
| l[0] = 11 |
| END |
| v9.CheckSourceFailure(lines, 'E741: Value is locked: l[0] = 11', 11) |
| |
| # lock a list element referenced by a protected object variable |
| # in an object fetched via a script level list |
| lines =<< trim END |
| vim9script |
| |
| class C |
| var _v1: list<list<number>> |
| def Lock() |
| lockvar lc[0]._v1[1] |
| enddef |
| endclass |
| |
| var l = [[1], [2], [3]] |
| var o = C.new(l) |
| var lc: list<C> = [ o ] |
| |
| o.Lock() |
| l[0] = [22] |
| l[1] = [33] |
| END |
| v9.CheckSourceFailure(lines, 'E741: Value is locked: l[1] = [33]', 16) |
| |
| # similar to the previous test, except the locking code is executing |
| # in a class that does not own the protected variable. |
| # Note that the locking code is in a class has a protected variable of |
| # the same name. |
| lines =<< trim END |
| vim9script |
| |
| class C2 |
| var _v1: list<list<number>> |
| def Lock(obj: any) |
| lockvar lc[0]._v1[1] |
| enddef |
| endclass |
| |
| class C |
| var _v1: list<list<number>> |
| endclass |
| |
| var l = [[1], [2], [3]] |
| var o = C.new(l) |
| var lc: list<C> = [ o ] |
| |
| var o2 = C2.new() |
| o2.Lock(o) |
| END |
| v9.CheckSourceFailure(lines, 'E1333: Cannot access protected variable "_v1" in class "C"') |
| enddef |
| |
| " Test builtin islocked() |
| def Test_lockvar_islocked() |
| # Can't lock class/object variable |
| # Lock class/object variable's value |
| # Lock item of variable's value (a list item) |
| # variable is at index 1 within class/object |
| var lines =<< trim END |
| vim9script |
| |
| class C |
| var o0: list<list<number>> = [ [0], [1], [2]] |
| var o1: list<list<number>> = [[10], [11], [12]] |
| static var c0: list<list<number>> = [[20], [21], [22]] |
| static var c1: list<list<number>> = [[30], [31], [32]] |
| endclass |
| |
| def LockIt(arg: any) |
| lockvar arg |
| enddef |
| |
| def UnlockIt(arg: any) |
| unlockvar arg |
| enddef |
| |
| var obj = C.new() |
| #lockvar obj.o1 # can't lock something you can't write to |
| |
| try |
| lockvar obj.o1 # can't lock something you can't write to |
| call assert_false(1, '"lockvar obj.o1" should have failed') |
| catch |
| call assert_exception('E1335:') |
| endtry |
| |
| LockIt(obj.o1) # but can lock it's value |
| assert_equal(1, islocked("obj.o1")) |
| assert_equal(1, islocked("obj.o1[0]")) |
| assert_equal(1, islocked("obj.o1[1]")) |
| UnlockIt(obj.o1) |
| assert_equal(0, islocked("obj.o1")) |
| assert_equal(0, islocked("obj.o1[0]")) |
| |
| lockvar obj.o1[0] |
| assert_equal(0, islocked("obj.o1")) |
| assert_equal(1, islocked("obj.o1[0]")) |
| assert_equal(0, islocked("obj.o1[1]")) |
| unlockvar obj.o1[0] |
| assert_equal(0, islocked("obj.o1")) |
| assert_equal(0, islocked("obj.o1[0]")) |
| |
| # Same thing, but with a static |
| |
| try |
| lockvar C.c1 # can't lock something you can't write to |
| call assert_false(1, '"lockvar C.c1" should have failed') |
| catch |
| call assert_exception('E1335:') |
| endtry |
| |
| LockIt(C.c1) # but can lock it's value |
| assert_equal(1, islocked("C.c1")) |
| assert_equal(1, islocked("C.c1[0]")) |
| assert_equal(1, islocked("C.c1[1]")) |
| UnlockIt(C.c1) |
| assert_equal(0, islocked("C.c1")) |
| assert_equal(0, islocked("C.c1[0]")) |
| |
| lockvar C.c1[0] |
| assert_equal(0, islocked("C.c1")) |
| assert_equal(1, islocked("C.c1[0]")) |
| assert_equal(0, islocked("C.c1[1]")) |
| unlockvar C.c1[0] |
| assert_equal(0, islocked("C.c1")) |
| assert_equal(0, islocked("C.c1[0]")) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # Do islocked() from an object method |
| # and then from a class method |
| lines =<< trim END |
| vim9script |
| |
| var l0o0 = [ [0], [1], [2]] |
| var l0o1 = [ [10], [11], [12]] |
| var l0c0 = [[120], [121], [122]] |
| var l0c1 = [[130], [131], [132]] |
| |
| class C0 |
| var o0: list<list<number>> = l0o0 |
| var o1: list<list<number>> = l0o1 |
| static var c0: list<list<number>> = l0c0 |
| static var c1: list<list<number>> = l0c1 |
| def Islocked(arg: string): number |
| return islocked(arg) |
| enddef |
| static def SIslocked(arg: string): number |
| return islocked(arg) |
| enddef |
| endclass |
| |
| var l2o0 = [[20000], [20001], [20002]] |
| var l2o1 = [[20010], [20011], [20012]] |
| var l2c0 = [[20120], [20121], [20122]] |
| var l2c1 = [[20130], [20131], [20132]] |
| |
| class C2 |
| var o0: list<list<number>> = l2o0 |
| var o1: list<list<number>> = l2o1 |
| static var c0: list<list<number>> = l2c0 |
| static var c1: list<list<number>> = l2c1 |
| def Islocked(arg: string): number |
| return islocked(arg) |
| enddef |
| static def SIslocked(arg: string): number |
| return islocked(arg) |
| enddef |
| endclass |
| |
| var obj0 = C0.new() |
| var obj2 = C2.new() |
| |
| var l = [ obj0, null_object, obj2 ] |
| |
| # lock list, object func access through script var expr |
| assert_equal(0, obj0.Islocked("l[0].o0")) |
| assert_equal(0, obj0.Islocked("l[0].o0[2]")) |
| lockvar l0o0 |
| assert_equal(1, obj0.Islocked("l[0].o0")) |
| assert_equal(1, obj0.Islocked("l[0].o0[2]")) |
| |
| #echo "check-b" obj2.Islocked("l[1].o1") # NULL OBJECT |
| |
| # lock list element, object func access through script var expr |
| lockvar l0o1[1] |
| assert_equal(0, obj0.Islocked("this.o1[0]")) |
| assert_equal(1, obj0.Islocked("this.o1[1]")) |
| |
| assert_equal(0, obj0.Islocked("this.o1")) |
| lockvar l0o1 |
| assert_equal(1, obj0.Islocked("this.o1")) |
| unlockvar l0o1 |
| |
| lockvar l0c1[1] |
| |
| # static by class name member expr from same class |
| assert_equal(0, obj0.Islocked("C0.c1[0]")) |
| assert_equal(1, obj0.Islocked("C0.c1[1]")) |
| # static by bare name member expr from same class |
| assert_equal(0, obj0.Islocked("c1[0]")) |
| assert_equal(1, obj0.Islocked("c1[1]")) |
| |
| # static by class name member expr from other class |
| assert_equal(0, obj2.Islocked("C0.c1[0]")) |
| assert_equal(1, obj2.Islocked("C0.c1[1]")) |
| # static by bare name member expr from other class |
| assert_equal(0, obj2.Islocked("c1[0]")) |
| assert_equal(0, obj2.Islocked("c1[1]")) |
| |
| |
| # static by bare name in same class |
| assert_equal(0, obj0.Islocked("c0")) |
| lockvar l0c0 |
| assert_equal(1, obj0.Islocked("c0")) |
| |
| # |
| # similar stuff, but use static method |
| # |
| |
| unlockvar l0o0 |
| |
| # lock list, object func access through script var expr |
| assert_equal(0, C0.SIslocked("l[0].o0")) |
| assert_equal(0, C0.SIslocked("l[0].o0[2]")) |
| lockvar l0o0 |
| assert_equal(1, C0.SIslocked("l[0].o0")) |
| assert_equal(1, C0.SIslocked("l[0].o0[2]")) |
| |
| unlockvar l0o1 |
| |
| # can't access "this" from class method |
| try |
| C0.SIslocked("this.o1[0]") |
| call assert_0(1, '"C0.SIslocked("this.o1[0]")" should have failed') |
| catch |
| call assert_exception('E121: Undefined variable: this') |
| endtry |
| |
| lockvar l0c1[1] |
| |
| # static by class name member expr from same class |
| assert_equal(0, C0.SIslocked("C0.c1[0]")) |
| assert_equal(1, C0.SIslocked("C0.c1[1]")) |
| # static by bare name member expr from same class |
| assert_equal(0, C0.SIslocked("c1[0]")) |
| assert_equal(1, C0.SIslocked("c1[1]")) |
| |
| # static by class name member expr from other class |
| assert_equal(0, C2.SIslocked("C0.c1[0]")) |
| assert_equal(1, C2.SIslocked("C0.c1[1]")) |
| # static by bare name member expr from other class |
| assert_equal(0, C2.SIslocked("c1[0]")) |
| assert_equal(0, C2.SIslocked("c1[1]")) |
| |
| |
| # static by bare name in same class |
| unlockvar l0c0 |
| assert_equal(0, C0.SIslocked("c0")) |
| lockvar l0c0 |
| assert_equal(1, C0.SIslocked("c0")) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # Check islocked class/object from various places. |
| lines =<< trim END |
| vim9script |
| |
| class C |
| def Islocked(arg: string): number |
| return islocked(arg) |
| enddef |
| static def SIslocked(arg: string): number |
| return islocked(arg) |
| enddef |
| endclass |
| var obj = C.new() |
| |
| # object method |
| assert_equal(0, obj.Islocked("this")) |
| assert_equal(0, obj.Islocked("C")) |
| |
| # class method |
| ### assert_equal(0, C.SIslocked("this")) |
| assert_equal(0, C.SIslocked("C")) |
| |
| #script level |
| var v: number |
| v = islocked("C") |
| assert_equal(0, v) |
| v = islocked("obj") |
| assert_equal(0, v) |
| END |
| v9.CheckSourceSuccess(lines) |
| enddef |
| |
| def Test_lockvar_islocked_notfound() |
| # Try non-existent things |
| var lines =<< trim END |
| vim9script |
| |
| class C |
| def Islocked(arg: string): number |
| return islocked(arg) |
| enddef |
| static def SIslocked(arg: string): number |
| return islocked(arg) |
| enddef |
| endclass |
| var obj = C.new() |
| assert_equal(-1, obj.Islocked("anywhere")) |
| assert_equal(-1, C.SIslocked("notanywhere")) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # Something not found of the form "name1.name2" is an error |
| lines =<< trim END |
| vim9script |
| |
| islocked("one.two") |
| END |
| v9.CheckSourceFailure(lines, 'E121: Undefined variable: one') |
| |
| lines =<< trim END |
| vim9script |
| |
| class C |
| var val = { key: "value" } |
| def Islocked(arg: string): number |
| return islocked(arg) |
| enddef |
| endclass |
| var obj = C.new() |
| obj.Islocked("this.val.not_there")) |
| END |
| v9.CheckSourceFailure(lines, 'E716: Key not present in Dictionary: "not_there"') |
| |
| lines =<< trim END |
| vim9script |
| |
| class C |
| def Islocked(arg: string): number |
| return islocked(arg) |
| enddef |
| endclass |
| var obj = C.new() |
| obj.Islocked("this.notobjmember") |
| END |
| v9.CheckSourceFailure(lines, 'E1326: Variable "notobjmember" not found in object "C"') |
| |
| # access a script variable through methods |
| lines =<< trim END |
| vim9script |
| |
| var l = [1] |
| class C |
| def Islocked(arg: string): number |
| return islocked(arg) |
| enddef |
| static def SIslocked(arg: string): number |
| return islocked(arg) |
| enddef |
| endclass |
| var obj = C.new() |
| assert_equal(0, obj.Islocked("l")) |
| assert_equal(0, C.SIslocked("l")) |
| lockvar l |
| assert_equal(1, obj.Islocked("l")) |
| assert_equal(1, C.SIslocked("l")) |
| END |
| v9.CheckSourceSuccess(lines) |
| enddef |
| |
| " Test for a protected object method |
| def Test_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<number> = [1, 2] |
| public static var var3: dict<number> = {a: 1, b: 2} |
| static var _priv_var4: number = 40 |
| endclass |
| def T() |
| assert_equal([1, 2], A.var2) |
| assert_equal({a: 1, b: 2}, A.var3) |
| A.var2 = [3, 4] |
| A.var3 = {c: 3, d: 4} |
| assert_equal([3, 4], A.var2) |
| assert_equal({c: 3, d: 4}, A.var3) |
| assert_fails('echo A._priv_var4', 'E1333: Cannot access protected variable "_priv_var4" in class "A"') |
| enddef |
| T() |
| END |
| v9.CheckSourceSuccess(lines) |
| enddef |
| |
| " Test for accessing a class member variable using an object |
| def Test_class_variable_access_using_object() |
| var lines =<< trim END |
| vim9script |
| class A |
| public static var svar1: list<number> = [1] |
| public static var svar2: list<number> = [2] |
| endclass |
| |
| A.svar1->add(3) |
| A.svar2->add(4) |
| assert_equal([1, 3], A.svar1) |
| assert_equal([2, 4], A.svar2) |
| |
| def Foo() |
| A.svar1->add(7) |
| A.svar2->add(8) |
| assert_equal([1, 3, 7], A.svar1) |
| assert_equal([2, 4, 8], A.svar2) |
| enddef |
| Foo() |
| 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<number> = [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<number> = [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<number> = [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<number> = [1] |
| endclass |
| |
| def T() |
| var a = A.new() |
| a.svar2 = [2] |
| enddef |
| T() |
| END |
| v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 2) |
| enddef |
| |
| " Test for using a interface method using a child object |
| def Test_interface_method_from_child() |
| var lines =<< trim END |
| vim9script |
| |
| interface A |
| def Foo(): string |
| endinterface |
| |
| class B implements A |
| def Foo(): string |
| return 'foo' |
| enddef |
| endclass |
| |
| class C extends B |
| def Bar(): string |
| return 'bar' |
| enddef |
| endclass |
| |
| def T1(a: A) |
| assert_equal('foo', a.Foo()) |
| enddef |
| |
| def T2(b: B) |
| assert_equal('foo', b.Foo()) |
| enddef |
| |
| var c = C.new() |
| T1(c) |
| T2(c) |
| END |
| v9.CheckSourceSuccess(lines) |
| enddef |
| |
| " Test for using an interface method using a child object when it is overridden |
| " by the child class. |
| def Test_interface_overridden_method_from_child() |
| var lines =<< trim END |
| vim9script |
| |
| interface A |
| def Foo(): string |
| endinterface |
| |
| class B implements A |
| def Foo(): string |
| return 'b-foo' |
| enddef |
| endclass |
| |
| class C extends B |
| def Bar(): string |
| return 'bar' |
| enddef |
| def Foo(): string |
| return 'c-foo' |
| enddef |
| endclass |
| |
| def T1(a: A) |
| assert_equal('c-foo', a.Foo()) |
| enddef |
| |
| def T2(b: B) |
| assert_equal('c-foo', b.Foo()) |
| enddef |
| |
| var c = C.new() |
| T1(c) |
| T2(c) |
| END |
| v9.CheckSourceSuccess(lines) |
| enddef |
| |
| " Test for interface inheritance |
| def Test_interface_inheritance() |
| var lines =<< trim END |
| vim9script |
| |
| interface A |
| def A_Fn(): string |
| endinterface |
| |
| interface B |
| def B_Fn(): string |
| endinterface |
| |
| interface C |
| def C_Fn(): string |
| endinterface |
| |
| class C1 implements A |
| def A_Fn(): string |
| return 'c1-a' |
| enddef |
| endclass |
| |
| class C2 extends C1 implements B |
| def B_Fn(): string |
| return 'c2-b' |
| enddef |
| def A_Fn(): string |
| return 'c2-a' |
| enddef |
| endclass |
| |
| class C3 extends C2 implements C |
| def C_Fn(): string |
| return 'c3-c' |
| enddef |
| def A_Fn(): string |
| return 'c3-a' |
| enddef |
| def B_Fn(): string |
| return 'c3-b' |
| enddef |
| endclass |
| |
| def T1(a: A, s: string) |
| assert_equal(s, a.A_Fn()) |
| enddef |
| |
| def T2(b: B, s: string) |
| assert_equal(s, b.B_Fn()) |
| enddef |
| |
| def T3(c: C, s: string) |
| assert_equal(s, c.C_Fn()) |
| enddef |
| |
| def T4(c1: C1) |
| T1(c1, 'c3-a') |
| enddef |
| |
| def T5(c2: C2) |
| T1(c2, 'c3-a') |
| T2(c2, 'c3-b') |
| enddef |
| |
| def T6(c3: C3) |
| T1(c3, 'c3-a') |
| T2(c3, 'c3-b') |
| T3(c3, 'c3-c') |
| enddef |
| |
| var o3 = C3.new() |
| T4(o3) |
| T5(o3) |
| T6(o3) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # Both the parent and child classes implement the same interface |
| lines =<< trim END |
| vim9script |
| |
| interface I |
| def Foo(): string |
| endinterface |
| |
| class A implements I |
| def Foo(): string |
| return 'A-foo' |
| enddef |
| endclass |
| |
| class B implements I |
| def Foo(): string |
| return 'B-foo' |
| enddef |
| endclass |
| |
| def Bar(i1: I): string |
| return i1.Foo() |
| enddef |
| |
| var b = B.new() |
| assert_equal('B-foo', Bar(b)) |
| END |
| v9.CheckSourceSuccess(lines) |
| enddef |
| |
| " 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<number> |
| endclass |
| class B extends A |
| def Foo(a: number, b: string): list<string> |
| return [] |
| enddef |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E1383: Method "Foo": type mismatch, expected func(string, number): list<number> but got func(number, string): list<string>', 9) |
| |
| # Invoke an abstract method from a def function |
| lines =<< trim END |
| vim9script |
| abstract class A |
| abstract def Foo(): list<number> |
| endclass |
| class B extends A |
| def Foo(): list<number> |
| return [3, 5] |
| enddef |
| endclass |
| def Bar(c: B) |
| assert_equal([3, 5], c.Foo()) |
| enddef |
| var b = B.new() |
| Bar(b) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # 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) |
| |
| # Invoke method returning a value through the abstract class. See #15432. |
| lines =<< trim END |
| vim9script |
| |
| abstract class A |
| abstract def String(): string |
| endclass |
| |
| class B extends A |
| def String(): string |
| return 'B' |
| enddef |
| endclass |
| |
| def F(o: A) |
| assert_equal('B', o.String()) |
| enddef |
| F(B.new()) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # Invoke abstract method returning a value does not compile |
| lines =<< trim END |
| vim9script |
| |
| abstract class A |
| abstract def String(): string |
| return 'X' |
| enddef |
| endclass |
| END |
| v9.CheckScriptFailure(lines, "E1318: Not a valid command in a class: return 'X'") |
| 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<string> |
| return ['a', 'b'] |
| enddef |
| def Bar() |
| assert_equal(['a', 'b'], A.Foo()) |
| assert_equal(['a', 'b'], Foo()) |
| enddef |
| endclass |
| |
| def T() |
| assert_equal(['a', 'b'], A.Foo()) |
| var t_a = A.new() |
| t_a.Bar() |
| enddef |
| |
| assert_equal(['a', 'b'], A.Foo()) |
| var a = A.new() |
| a.Bar() |
| T() |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # script context |
| lines =<< trim END |
| vim9script |
| class A |
| static def Foo(): string |
| return 'foo' |
| enddef |
| endclass |
| |
| var a = A.new() |
| assert_equal('foo', a.Foo()) |
| END |
| v9.CheckSourceFailure(lines, '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<any>): list<string> |
| endinterface |
| END |
| v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3) |
| |
| lines =<< trim END |
| vim9script |
| interface A |
| static def _Foo(d: dict<any>): list<string> |
| endinterface |
| END |
| v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface', 3) |
| |
| lines =<< trim END |
| vim9script |
| interface A |
| var _Foo: list<string> |
| endinterface |
| END |
| v9.CheckSourceFailure(lines, 'E1379: Protected variable not supported in an interface', 3) |
| |
| lines =<< trim END |
| vim9script |
| interface A |
| def _Foo(d: dict<any>): list<string> |
| endinterface |
| END |
| v9.CheckSourceFailure(lines, 'E1380: Protected method not supported in an interface', 3) |
| enddef |
| |
| " Test for extending an interface |
| def Test_extend_interface() |
| var lines =<< trim END |
| vim9script |
| interface A |
| var var1: list<string> |
| def Foo() |
| endinterface |
| interface B extends A |
| var var2: dict<string> |
| def Bar() |
| endinterface |
| class C implements A, B |
| var var1 = [1, 2] |
| def Foo() |
| enddef |
| var var2 = {a: '1'} |
| def Bar() |
| enddef |
| endclass |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # extending empty interface |
| lines =<< trim END |
| vim9script |
| interface A |
| endinterface |
| interface B extends A |
| endinterface |
| class C implements B |
| endclass |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| interface A |
| def Foo() |
| endinterface |
| interface B extends A |
| var var2: dict<string> |
| endinterface |
| class C implements A, B |
| var var2 = {a: '1'} |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E1349: Method "Foo" of interface "A" is not implemented', 10) |
| |
| lines =<< trim END |
| vim9script |
| interface A |
| def Foo() |
| endinterface |
| interface B extends A |
| var var2: dict<string> |
| endinterface |
| class C implements A, B |
| def Foo() |
| enddef |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E1348: Variable "var2" of interface "B" is not implemented', 11) |
| |
| # interface cannot extend a class |
| lines =<< trim END |
| vim9script |
| class A |
| endclass |
| interface B extends A |
| endinterface |
| END |
| v9.CheckSourceFailure(lines, 'E1354: Cannot extend A', 5) |
| |
| # class cannot extend an interface |
| lines =<< trim END |
| vim9script |
| interface A |
| endinterface |
| class B extends A |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E1354: Cannot extend A', 5) |
| |
| # interface cannot implement another interface |
| lines =<< trim END |
| vim9script |
| interface A |
| endinterface |
| interface B implements A |
| endinterface |
| END |
| v9.CheckSourceFailure(lines, 'E1381: Interface cannot use "implements"', 4) |
| |
| # interface cannot extend multiple interfaces |
| lines =<< trim END |
| vim9script |
| interface A |
| endinterface |
| interface B |
| endinterface |
| interface C extends A, B |
| endinterface |
| END |
| v9.CheckSourceFailure(lines, 'E1315: White space required after name: A, B', 6) |
| |
| # Variable type in an extended interface is of different type |
| lines =<< trim END |
| vim9script |
| interface A |
| var val1: number |
| endinterface |
| interface B extends A |
| var val2: string |
| endinterface |
| interface C extends B |
| var val1: string |
| var val2: number |
| endinterface |
| END |
| v9.CheckSourceFailure(lines, 'E1382: Variable "val1": type mismatch, expected number but got string', 11) |
| enddef |
| |
| " Test for a child class implementing an interface when some of the methods are |
| " defined in the parent class. |
| def Test_child_class_implements_interface() |
| var lines =<< trim END |
| vim9script |
| |
| interface Intf |
| def F1(): list<list<number>> |
| def F2(): list<list<number>> |
| def F3(): list<list<number>> |
| var var1: list<dict<number>> |
| var var2: list<dict<number>> |
| var var3: list<dict<number>> |
| endinterface |
| |
| class A |
| def A1() |
| enddef |
| def F3(): list<list<number>> |
| return [[3]] |
| enddef |
| var v1: list<list<number>> = [[0]] |
| var var3 = [{c: 30}] |
| endclass |
| |
| class B extends A |
| def B1() |
| enddef |
| def F2(): list<list<number>> |
| return [[2]] |
| enddef |
| var v2: list<list<number>> = [[0]] |
| var var2 = [{b: 20}] |
| endclass |
| |
| class C extends B implements Intf |
| def C1() |
| enddef |
| def F1(): list<list<number>> |
| return [[1]] |
| enddef |
| var v3: list<list<number>> = [[0]] |
| var var1 = [{a: 10}] |
| endclass |
| |
| def T(if: Intf) |
| assert_equal([[1]], if.F1()) |
| assert_equal([[2]], if.F2()) |
| assert_equal([[3]], if.F3()) |
| assert_equal([{a: 10}], if.var1) |
| assert_equal([{b: 20}], if.var2) |
| assert_equal([{c: 30}], if.var3) |
| enddef |
| |
| var c = C.new() |
| T(c) |
| assert_equal([[1]], c.F1()) |
| assert_equal([[2]], c.F2()) |
| assert_equal([[3]], c.F3()) |
| assert_equal([{a: 10}], c.var1) |
| assert_equal([{b: 20}], c.var2) |
| assert_equal([{c: 30}], c.var3) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # One of the interface methods is not found |
| lines =<< trim END |
| vim9script |
| |
| interface Intf |
| def F1() |
| def F2() |
| def F3() |
| endinterface |
| |
| class A |
| def A1() |
| enddef |
| endclass |
| |
| class B extends A |
| def B1() |
| enddef |
| def F2() |
| enddef |
| endclass |
| |
| class C extends B implements Intf |
| def C1() |
| enddef |
| def F1() |
| enddef |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E1349: Method "F3" of interface "Intf" is not implemented', 26) |
| |
| # One of the interface methods is of different type |
| lines =<< trim END |
| vim9script |
| |
| interface Intf |
| def F1() |
| def F2() |
| def F3() |
| endinterface |
| |
| class A |
| def F3(): number |
| return 0 |
| enddef |
| def A1() |
| enddef |
| endclass |
| |
| class B extends A |
| def B1() |
| enddef |
| def F2() |
| enddef |
| endclass |
| |
| class C extends B implements Intf |
| def C1() |
| enddef |
| def F1() |
| enddef |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E1383: Method "F3": type mismatch, expected func() but got func(): number', 29) |
| |
| # One of the interface variables is not present |
| lines =<< trim END |
| vim9script |
| |
| interface Intf |
| var var1: list<dict<number>> |
| var var2: list<dict<number>> |
| var var3: list<dict<number>> |
| endinterface |
| |
| class A |
| var v1: list<list<number>> = [[0]] |
| endclass |
| |
| class B extends A |
| var v2: list<list<number>> = [[0]] |
| var var2 = [{b: 20}] |
| endclass |
| |
| class C extends B implements Intf |
| var v3: list<list<number>> = [[0]] |
| var var1 = [{a: 10}] |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E1348: Variable "var3" of interface "Intf" is not implemented', 21) |
| |
| # One of the interface variables is of different type |
| lines =<< trim END |
| vim9script |
| |
| interface Intf |
| var var1: list<dict<number>> |
| var var2: list<dict<number>> |
| var var3: list<dict<number>> |
| endinterface |
| |
| class A |
| var v1: list<list<number>> = [[0]] |
| var var3: list<dict<string>> |
| endclass |
| |
| class B extends A |
| var v2: list<list<number>> = [[0]] |
| var var2 = [{b: 20}] |
| endclass |
| |
| class C extends B implements Intf |
| var v3: list<list<number>> = [[0]] |
| var var1 = [{a: 10}] |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E1382: Variable "var3": type mismatch, expected list<dict<number>> but got list<dict<string>>', 22) |
| enddef |
| |
| " Test for extending an interface with duplicate variables and methods |
| def Test_interface_extends_with_dup_members() |
| var lines =<< trim END |
| vim9script |
| interface A |
| var n1: number |
| def Foo1(): number |
| endinterface |
| interface B extends A |
| var n2: number |
| var n1: number |
| def Foo2(): number |
| def Foo1(): number |
| endinterface |
| class C implements B |
| var n1 = 10 |
| var n2 = 20 |
| def Foo1(): number |
| return 30 |
| enddef |
| def Foo2(): number |
| return 40 |
| enddef |
| endclass |
| def T1(a: A) |
| assert_equal(10, a.n1) |
| assert_equal(30, a.Foo1()) |
| enddef |
| def T2(b: B) |
| assert_equal(10, b.n1) |
| assert_equal(20, b.n2) |
| assert_equal(30, b.Foo1()) |
| assert_equal(40, b.Foo2()) |
| enddef |
| var c = C.new() |
| T1(c) |
| T2(c) |
| END |
| v9.CheckSourceSuccess(lines) |
| enddef |
| |
| " Test for implementing an interface with different ordering for the interface |
| " member variables. |
| def Test_implement_interface_with_different_variable_order() |
| var lines =<< trim END |
| vim9script |
| |
| interface IX |
| var F: func(): string |
| endinterface |
| |
| class X implements IX |
| var x: number |
| var F: func(): string = () => 'ok' |
| endclass |
| |
| def Foo(ix: IX): string |
| return ix.F() |
| enddef |
| |
| var x0 = X.new(0) |
| assert_equal('ok', Foo(x0)) |
| END |
| v9.CheckSourceSuccess(lines) |
| enddef |
| |
| " Test for inheriting interfaces from an imported super class |
| def Test_interface_inheritance_with_imported_super() |
| var lines =<< trim END |
| vim9script |
| |
| export interface I |
| def F(): string |
| endinterface |
| |
| export class A implements I |
| def F(): string |
| return 'A' |
| enddef |
| endclass |
| END |
| writefile(lines, 'Xinheritintfimportclass.vim', 'D') |
| |
| lines =<< trim END |
| vim9script |
| |
| import './Xinheritintfimportclass.vim' as i_imp |
| |
| # class C extends i_imp.A |
| class C extends i_imp.A implements i_imp.I |
| def F(): string |
| return 'C' |
| enddef |
| endclass |
| |
| def TestI(i: i_imp.I): string |
| return i.F() |
| enddef |
| |
| assert_equal('C', TestI(C.new())) |
| END |
| v9.CheckSourceSuccess(lines) |
| enddef |
| |
| " Test for using "any" type for a variable in a sub-class while it has a |
| " concrete type in the interface |
| def Test_implements_using_var_type_any() |
| var lines =<< trim END |
| vim9script |
| interface A |
| var val: list<dict<string>> |
| endinterface |
| class B implements A |
| var val = [{a: '1'}, {b: '2'}] |
| endclass |
| var b = B.new() |
| assert_equal([{a: '1'}, {b: '2'}], b.val) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # initialize instance variable using a different type |
| lines =<< trim END |
| vim9script |
| interface A |
| var val: list<dict<string>> |
| endinterface |
| class B implements A |
| var val = {a: 1, b: 2} |
| endclass |
| var b = B.new() |
| END |
| v9.CheckSourceFailure(lines, 'E1382: Variable "val": type mismatch, expected list<dict<string>> but got dict<number>', 1) |
| enddef |
| |
| " Test 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) |
| |
| # Calling an object method defined in a class that is extended. This differs |
| # from the previous by invoking ISN_METHODCALL instead of ISN_DCALL. |
| lines =<< trim END |
| vim9script |
| |
| class C0 |
| def F() |
| enddef |
| endclass |
| |
| class C extends C0 |
| endclass |
| |
| def X() |
| var o: C0 = null_object |
| o.F() |
| enddef |
| X() |
| END |
| v9.CheckSourceFailure(lines, 'E1360: Using a null object', 2) |
| |
| # Getting a function ref an object method. |
| lines =<< trim END |
| vim9script |
| |
| class C0 |
| def F() |
| enddef |
| endclass |
| |
| class C extends C0 |
| endclass |
| |
| def X() |
| var o: C0 = null_object |
| var XXX = o.F |
| enddef |
| X() |
| 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<number> = {} |
| def GetState(): dict<number> |
| 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<number> = [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<number> = [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<number> = [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<number> = [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<B>): object<B> but got func(object<C>): object<B>', 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<B>): object<B> but got func(object<A>): object<B>', 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<B>): object<B> but got func(object<B>): object<A>', 20) |
| |
| # check varargs type mismatch |
| lines =<< trim END |
| vim9script |
| |
| class B |
| def F(...xxx: list<any>) |
| enddef |
| endclass |
| class C extends B |
| def F(xxx: list<any>) |
| enddef |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E1383: Method "F": type mismatch, expected func(...list<any>) but got func(list<any>)', 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<blob>>): dict<list<blob>> |
| return {} |
| enddef |
| class A |
| public static var Fn: func(list<dict<blob>>): dict<list<blob>> = Foo |
| endclass |
| test_garbagecollect_now() |
| A.Fn = "abc" |
| END |
| call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list<dict<blob>>): dict<list<blob>> 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<blob>>): dict<list<blob>> |
| return {} |
| enddef |
| class A |
| public static var Fn: func(list<dict<blob>>): dict<list<blob>> = 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<blob>>): dict<list<blob>> 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<blob>>): dict<list<blob>> |
| return {} |
| enddef |
| class A |
| public static var Fn: func(list<dict<blob>>): dict<list<blob>> = Foo |
| endclass |
| def Bar() |
| A.Fn = "abc" |
| enddef |
| test_garbagecollect_now() |
| Bar() |
| END |
| call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list<dict<blob>>): dict<list<blob>> 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<blob>>): dict<list<blob>> |
| 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<blob>>): dict<list<blob>> 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<blob>>): dict<list<blob>> |
| 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<blob>>): dict<list<blob>> 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<blob>>): dict<list<blob>> |
| 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<blob>>): dict<list<blob>> but got string', 1) |
| |
| " class variable with 'any" type. Can be assigned different types. |
| let lines =<< trim END |
| vim9script |
| def Foo(l: list<dict<blob>>): dict<list<blob>> |
| return {} |
| enddef |
| class A |
| public static var Fn: any = Foo |
| public static var Fn2: any |
| endclass |
| test_garbagecollect_now() |
| assert_equal('func(list<dict<blob>>): dict<list<blob>>', 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<blob>>): dict<list<blob>>', 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<blob>>): dict<list<blob>> |
| return {} |
| enddef |
| class A |
| public static var Fn: any = Foo |
| public static var Fn2: any |
| |
| def Bar() |
| assert_equal('func(list<dict<blob>>): dict<list<blob>>', typename(Fn)) |
| Fn = "abc" |
| assert_equal('string', typename(Fn)) |
| Fn2 = Foo |
| assert_equal('func(list<dict<blob>>): dict<list<blob>>', 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<blob>>): dict<list<blob>> |
| return {} |
| enddef |
| class A |
| public static var Fn: any = Foo |
| public static var Fn2: any |
| endclass |
| |
| def Bar() |
| assert_equal('func(list<dict<blob>>): dict<list<blob>>', typename(A.Fn)) |
| A.Fn = "abc" |
| assert_equal('string', typename(A.Fn)) |
| A.Fn2 = Foo |
| assert_equal('func(list<dict<blob>>): dict<list<blob>>', 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<blob>>): dict<list<blob>> |
| return {} |
| enddef |
| class A |
| public var Fn: func(list<dict<blob>>): dict<list<blob>> = Foo |
| endclass |
| var a = A.new() |
| test_garbagecollect_now() |
| a.Fn = "abc" |
| END |
| call v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(list<dict<blob>>): dict<list<blob>> 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<blob>>): dict<list<blob>> |
| return {} |
| enddef |
| class A |
| public var Fn: func(list<dict<blob>>): dict<list<blob>> = 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<blob>>): dict<list<blob>> 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<blob>>): dict<list<blob>> |
| return {} |
| enddef |
| class A |
| public var Fn: func(list<dict<blob>>): dict<list<blob>> = 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<blob>>): dict<list<blob>> 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<blob>>): dict<list<blob>> |
| 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<blob>>): dict<list<blob>> 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<blob>>): dict<list<blob>> |
| 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<blob>>): dict<list<blob>> 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<blob>>): dict<list<blob>> |
| 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<blob>>): dict<list<blob>> but got string', 2) |
| |
| " object variable with 'any" type. Can be assigned different types. |
| let lines =<< trim END |
| vim9script |
| def Foo(l: list<dict<blob>>): dict<list<blob>> |
| 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<blob>>): dict<list<blob>>', 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<blob>>): dict<list<blob>>', 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<blob>>): dict<list<blob>> |
| return {} |
| enddef |
| class A |
| public var Fn: any = Foo |
| public var Fn2: any |
| |
| def Bar() |
| assert_equal('func(list<dict<blob>>): dict<list<blob>>', typename(this.Fn)) |
| this.Fn = "abc" |
| assert_equal('string', typename(this.Fn)) |
| this.Fn2 = Foo |
| assert_equal('func(list<dict<blob>>): dict<list<blob>>', 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<blob>>): dict<list<blob>> |
| 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<blob>>): dict<list<blob>>', typename(a.Fn)) |
| a.Fn = "abc" |
| assert_equal('string', typename(a.Fn)) |
| a.Fn2 = Foo |
| assert_equal('func(list<dict<blob>>): dict<list<blob>>', 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<A>): object<A> but got func(object<B>): object<B>', 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<A>): object<A> but got func(object<B>): object<B>', 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<B>): number but got func(object<A>): 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<B>): number but got func(object<C>): 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<number> = [] |
| static def Foo(): list<number> |
| val += [1] |
| return val |
| enddef |
| endclass |
| def Bar(): list<number> |
| 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<number> = [] |
| def Foo(): list<number> |
| this.val += [1] |
| return this.val |
| enddef |
| endclass |
| def Bar(bar_a: A): list<number> |
| 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<number> |
| 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<number> |
| 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<number> |
| 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<any>): list<any> |
| 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<number> |
| 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<number> |
| 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<number> |
| 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<any>): list<any> |
| 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<number> |
| 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<number> |
| 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<string> = [] |
| |
| 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<string> = [] |
| |
| 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<string> = [] |
| |
| 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<string> = [] |
| |
| 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<any> = [] |
| 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<any> |
| 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<string> = {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<string> = {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<any>', 2) |
| |
| lines =<< trim END |
| vim9script |
| |
| class A |
| var numbers: dict<number> = {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<any>', 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<number> |
| 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<number> = [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<number> = [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<number> = [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<number> = [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<number> = [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<number> = [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<number> = [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<number> |
| 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<number> |
| 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<number> = [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<number> = [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<number> = [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<number> = [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<number> = [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<number> = [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<number> = [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<number> = [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<number> = [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<number> = [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 compiling class/object methods using :defcompile |
| def Test_defcompile_class() |
| # defcompile all the classes in the current script |
| var lines =<< trim END |
| vim9script |
| class A |
| def Foo() |
| var i = 10 |
| enddef |
| endclass |
| class B |
| def Bar() |
| var i = 20 |
| xxx |
| enddef |
| endclass |
| defcompile |
| END |
| v9.CheckSourceFailure(lines, 'E476: Invalid command: xxx', 2) |
| |
| # defcompile a specific class |
| lines =<< trim END |
| vim9script |
| class A |
| def Foo() |
| xxx |
| enddef |
| endclass |
| class B |
| def Bar() |
| yyy |
| enddef |
| endclass |
| defcompile B |
| END |
| v9.CheckSourceFailure(lines, 'E476: Invalid command: yyy', 1) |
| |
| # defcompile a non-class |
| lines =<< trim END |
| vim9script |
| class A |
| def Foo() |
| enddef |
| endclass |
| var X: list<number> = [] |
| defcompile X |
| END |
| v9.CheckSourceFailure(lines, 'E1061: Cannot find function X', 7) |
| |
| # defcompile a class twice |
| lines =<< trim END |
| vim9script |
| class A |
| def new() |
| enddef |
| endclass |
| defcompile A |
| defcompile A |
| assert_equal('Function A.new does not need compiling', v:statusmsg) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # defcompile should not compile an imported class |
| lines =<< trim END |
| vim9script |
| export class A |
| def Foo() |
| xxx |
| enddef |
| endclass |
| END |
| writefile(lines, 'Xdefcompileimport.vim', 'D') |
| lines =<< trim END |
| vim9script |
| |
| import './Xdefcompileimport.vim' |
| class B |
| endclass |
| defcompile |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| " Test for cases common to all the object builtin methods |
| def Test_object_builtin_method() |
| var lines =<< trim END |
| vim9script |
| class A |
| def abc() |
| enddef |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E1267: Function name must start with a capital: abc()', 3) |
| |
| for funcname in ["len", "string", "empty"] |
| lines =<< trim eval END |
| vim9script |
| class A |
| static def {funcname}(): number |
| enddef |
| endclass |
| END |
| v9.CheckSourceFailure(lines, 'E1413: Builtin class method not supported', 3) |
| endfor |
| enddef |
| |
| " Test for using the empty() builtin method with an object |
| " This is a legacy function to use the test_garbagecollect_now() function. |
| func Test_object_empty() |
| let lines =<< trim END |
| vim9script |
| class A |
| def empty(): bool |
| return true |
| enddef |
| endclass |
| |
| def Foo() |
| var afoo = A.new() |
| assert_equal(true, empty(afoo)) |
| assert_equal(true, afoo->empty()) |
| enddef |
| |
| var a = A.new() |
| assert_equal(1, empty(a)) |
| assert_equal(1, a->empty()) |
| test_garbagecollect_now() |
| assert_equal(1, empty(a)) |
| Foo() |
| test_garbagecollect_now() |
| Foo() |
| END |
| call v9.CheckSourceSuccess(lines) |
| |
| " empty() should return 1 without a builtin method |
| let lines =<< trim END |
| vim9script |
| class A |
| endclass |
| |
| def Foo() |
| var afoo = A.new() |
| assert_equal(1, empty(afoo)) |
| enddef |
| |
| var a = A.new() |
| assert_equal(1, empty(a)) |
| Foo() |
| END |
| call v9.CheckSourceSuccess(lines) |
| |
| " Unsupported signature for the empty() method |
| let lines =<< trim END |
| vim9script |
| class A |
| def empty() |
| enddef |
| endclass |
| END |
| call v9.CheckSourceFailure(lines, 'E1383: Method "empty": type mismatch, expected func(): bool but got func()', 4) |
| |
| " Error when calling the empty() method |
| let lines =<< trim END |
| vim9script |
| class A |
| def empty(): bool |
| throw "Failed to check emptiness" |
| enddef |
| endclass |
| |
| def Foo() |
| var afoo = A.new() |
| var i = empty(afoo) |
| enddef |
| |
| var a = A.new() |
| assert_fails('empty(a)', 'Failed to check emptiness') |
| assert_fails('Foo()', 'Failed to check emptiness') |
| END |
| call v9.CheckSourceSuccess(lines) |
| |
| " call empty() using an object from a script |
| let lines =<< trim END |
| vim9script |
| class A |
| def empty(): bool |
| return true |
| enddef |
| endclass |
| var afoo = A.new() |
| assert_equal(true, afoo.empty()) |
| END |
| call v9.CheckSourceSuccess(lines) |
| |
| " call empty() using an object from a method |
| let lines =<< trim END |
| vim9script |
| class A |
| def empty(): bool |
| return true |
| enddef |
| endclass |
| def Foo() |
| var afoo = A.new() |
| assert_equal(true, afoo.empty()) |
| enddef |
| Foo() |
| END |
| call v9.CheckSourceSuccess(lines) |
| |
| " call empty() using "this" from an object method |
| let lines =<< trim END |
| vim9script |
| class A |
| def empty(): bool |
| return true |
| enddef |
| def Foo(): bool |
| return this.empty() |
| enddef |
| endclass |
| def Bar() |
| var abar = A.new() |
| assert_equal(true, abar.Foo()) |
| enddef |
| Bar() |
| END |
| call v9.CheckSourceSuccess(lines) |
| |
| " Call empty() from a derived object |
| let lines =<< trim END |
| vim9script |
| class A |
| def empty(): bool |
| return false |
| enddef |
| endclass |
| class B extends A |
| def empty(): bool |
| return true |
| enddef |
| endclass |
| def Foo(afoo: A) |
| assert_equal(true, empty(afoo)) |
| var bfoo = B.new() |
| assert_equal(true, empty(bfoo)) |
| enddef |
| var b = B.new() |
| assert_equal(1, empty(b)) |
| Foo(b) |
| END |
| call v9.CheckSourceSuccess(lines) |
| |
| " Invoking empty method using an interface |
| let lines =<< trim END |
| vim9script |
| interface A |
| def empty(): bool |
| endinterface |
| class B implements A |
| def empty(): bool |
| return false |
| enddef |
| endclass |
| def Foo(a: A) |
| assert_equal(false, empty(a)) |
| enddef |
| var b = B.new() |
| Foo(b) |
| END |
| call v9.CheckSourceSuccess(lines) |
| endfunc |
| |
| " Test for using the len() builtin method with an object |
| " This is a legacy function to use the test_garbagecollect_now() function. |
| func Test_object_length() |
| let lines =<< trim END |
| vim9script |
| class A |
| var mylen: number = 0 |
| def new(n: number) |
| this.mylen = n |
| enddef |
| def len(): number |
| return this.mylen |
| enddef |
| endclass |
| |
| def Foo() |
| var afoo = A.new(12) |
| assert_equal(12, len(afoo)) |
| assert_equal(12, afoo->len()) |
| enddef |
| |
| var a = A.new(22) |
| assert_equal(22, len(a)) |
| assert_equal(22, a->len()) |
| test_garbagecollect_now() |
| assert_equal(22, len(a)) |
| Foo() |
| test_garbagecollect_now() |
| Foo() |
| END |
| call v9.CheckSourceSuccess(lines) |
| |
| " len() should return 0 without a builtin method |
| let lines =<< trim END |
| vim9script |
| class A |
| endclass |
| |
| def Foo() |
| var afoo = A.new() |
| assert_equal(0, len(afoo)) |
| enddef |
| |
| var a = A.new() |
| assert_equal(0, len(a)) |
| Foo() |
| END |
| call v9.CheckSourceSuccess(lines) |
| |
| " Unsupported signature for the len() method |
| let lines =<< trim END |
| vim9script |
| class A |
| def len() |
| enddef |
| endclass |
| END |
| call v9.CheckSourceFailure(lines, 'E1383: Method "len": type mismatch, expected func(): number but got func()', 4) |
| |
| " Error when calling the len() method |
| let lines =<< trim END |
| vim9script |
| class A |
| def len(): number |
| throw "Failed to compute length" |
| enddef |
| endclass |
| |
| def Foo() |
| var afoo = A.new() |
| var i = len(afoo) |
| enddef |
| |
| var a = A.new() |
| assert_fails('len(a)', 'Failed to compute length') |
| assert_fails('Foo()', 'Failed to compute length') |
| END |
| call v9.CheckSourceSuccess(lines) |
| |
| " call len() using an object from a script |
| let lines =<< trim END |
| vim9script |
| class A |
| def len(): number |
| return 5 |
| enddef |
| endclass |
| var afoo = A.new() |
| assert_equal(5, afoo.len()) |
| END |
| call v9.CheckSourceSuccess(lines) |
| |
| " call len() using an object from a method |
| let lines =<< trim END |
| vim9script |
| class A |
| def len(): number |
| return 5 |
| enddef |
| endclass |
| def Foo() |
| var afoo = A.new() |
| assert_equal(5, afoo.len()) |
| enddef |
| Foo() |
| END |
| call v9.CheckSourceSuccess(lines) |
| |
| " call len() using "this" from an object method |
| let lines =<< trim END |
| vim9script |
| class A |
| def len(): number |
| return 8 |
| enddef |
| def Foo(): number |
| return this.len() |
| enddef |
| endclass |
| def Bar() |
| var abar = A.new() |
| assert_equal(8, abar.Foo()) |
| enddef |
| Bar() |
| END |
| call v9.CheckSourceSuccess(lines) |
| |
| " Call len() from a derived object |
| let lines =<< trim END |
| vim9script |
| class A |
| def len(): number |
| return 10 |
| enddef |
| endclass |
| class B extends A |
| def len(): number |
| return 20 |
| enddef |
| endclass |
| def Foo(afoo: A) |
| assert_equal(20, len(afoo)) |
| var bfoo = B.new() |
| assert_equal(20, len(bfoo)) |
| enddef |
| var b = B.new() |
| assert_equal(20, len(b)) |
| Foo(b) |
| END |
| call v9.CheckSourceSuccess(lines) |
| |
| " Invoking len method using an interface |
| let lines =<< trim END |
| vim9script |
| interface A |
| def len(): number |
| endinterface |
| class B implements A |
| def len(): number |
| return 123 |
| enddef |
| endclass |
| def Foo(a: A) |
| assert_equal(123, len(a)) |
| enddef |
| var b = B.new() |
| Foo(b) |
| END |
| call v9.CheckSourceSuccess(lines) |
| endfunc |
| |
| " Test for using the string() builtin method with an object |
| " This is a legacy function to use the test_garbagecollect_now() function. |
| func Test_object_string() |
| let lines =<< trim END |
| vim9script |
| class A |
| var name: string |
| def string(): string |
| return this.name |
| enddef |
| endclass |
| |
| def Foo() |
| var afoo = A.new("foo-A") |
| assert_equal('foo-A', string(afoo)) |
| assert_equal('foo-A', afoo->string()) |
| enddef |
| |
| var a = A.new("script-A") |
| assert_equal('script-A', string(a)) |
| assert_equal('script-A', a->string()) |
| assert_equal(['script-A'], execute('echo a')->split("\n")) |
| test_garbagecollect_now() |
| assert_equal('script-A', string(a)) |
| Foo() |
| test_garbagecollect_now() |
| Foo() |
| END |
| call v9.CheckSourceSuccess(lines) |
| |
| " string() should return "object of A {}" without a builtin method |
| let lines =<< trim END |
| vim9script |
| class A |
| endclass |
| |
| def Foo() |
| var afoo = A.new() |
| assert_equal('object of A {}', string(afoo)) |
| enddef |
| |
| var a = A.new() |
| assert_equal('object of A {}', string(a)) |
| Foo() |
| END |
| call v9.CheckSourceSuccess(lines) |
| |
| " Unsupported signature for the string() method |
| let lines =<< trim END |
| vim9script |
| class A |
| def string() |
| enddef |
| endclass |
| END |
| call v9.CheckSourceFailure(lines, 'E1383: Method "string": type mismatch, expected func(): string but got func()', 4) |
| |
| " Error when calling the string() method |
| let lines =<< trim END |
| vim9script |
| class A |
| def string(): string |
| throw "Failed to get text" |
| enddef |
| endclass |
| |
| def Foo() |
| var afoo = A.new() |
| var i = string(afoo) |
| enddef |
| |
| var a = A.new() |
| assert_fails('string(a)', 'Failed to get text') |
| assert_fails('Foo()', 'Failed to get text') |
| END |
| call v9.CheckSourceSuccess(lines) |
| |
| " call string() using an object from a script |
| let lines =<< trim END |
| vim9script |
| class A |
| def string(): string |
| return 'A' |
| enddef |
| endclass |
| var afoo = A.new() |
| assert_equal('A', afoo.string()) |
| END |
| call v9.CheckSourceSuccess(lines) |
| |
| " call string() using an object from a method |
| let lines =<< trim END |
| vim9script |
| class A |
| def string(): string |
| return 'A' |
| enddef |
| endclass |
| def Foo() |
| var afoo = A.new() |
| assert_equal('A', afoo.string()) |
| enddef |
| Foo() |
| END |
| call v9.CheckSourceSuccess(lines) |
| |
| " call string() using "this" from an object method |
| let lines =<< trim END |
| vim9script |
| class A |
| def string(): string |
| return 'A' |
| enddef |
| def Foo(): string |
| return this.string() |
| enddef |
| endclass |
| def Bar() |
| var abar = A.new() |
| assert_equal('A', abar.string()) |
| enddef |
| Bar() |
| END |
| call v9.CheckSourceSuccess(lines) |
| |
| " Call string() from a derived object |
| let lines =<< trim END |
| vim9script |
| class A |
| def string(): string |
| return 'A' |
| enddef |
| endclass |
| class B extends A |
| def string(): string |
| return 'B' |
| enddef |
| endclass |
| def Foo(afoo: A) |
| assert_equal('B', string(afoo)) |
| var bfoo = B.new() |
| assert_equal('B', string(bfoo)) |
| enddef |
| var b = B.new() |
| assert_equal('B', string(b)) |
| Foo(b) |
| END |
| call v9.CheckSourceSuccess(lines) |
| |
| " Invoking string method using an interface |
| let lines =<< trim END |
| vim9script |
| interface A |
| def string(): string |
| endinterface |
| class B implements A |
| def string(): string |
| return 'B' |
| enddef |
| endclass |
| def Foo(a: A) |
| assert_equal('B', string(a)) |
| enddef |
| var b = B.new() |
| Foo(b) |
| END |
| call v9.CheckSourceSuccess(lines) |
| endfunc |
| |
| " Test for using the string() builtin method with an object's method |
| def Test_method_string() |
| var lines =<< trim END |
| vim9script |
| class A |
| def F() |
| enddef |
| endclass |
| assert_match('function(''<SNR>\d\+_A\.F'')', string(A.new().F)) |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| |
| " Test for using a class in the class definition |
| def Test_Ref_Class_Within_Same_Class() |
| var lines =<< trim END |
| vim9script |
| class A |
| var n: number = 0 |
| def Equals(other: A): bool |
| return this.n == other.n |
| enddef |
| endclass |
| |
| var a1 = A.new(10) |
| var a2 = A.new(10) |
| var a3 = A.new(20) |
| assert_equal(true, a1.Equals(a2)) |
| assert_equal(false, a2.Equals(a3)) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| |
| class Foo |
| var num: number |
| def Clone(): Foo |
| return Foo.new(this.num) |
| enddef |
| endclass |
| |
| var f1 = Foo.new(1) |
| |
| def F() |
| var f2: Foo = f1.Clone() |
| assert_equal(false, f2 is f1) |
| assert_equal(true, f2.num == f1.num) |
| enddef |
| F() |
| |
| var f3: Foo = f1.Clone() |
| assert_equal(false, f3 is f1) |
| assert_equal(true, f3.num == f1.num) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| # Test for trying to use a class to extend when defining the same class |
| lines =<< trim END |
| vim9script |
| class A extends A |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1354: Cannot extend A', 3) |
| |
| # Test for trying to use a class to implement when defining the same class |
| lines =<< trim END |
| vim9script |
| class A implements A |
| endclass |
| END |
| v9.CheckScriptFailure(lines, 'E1347: Not a valid interface: A', 3) |
| enddef |
| |
| " Test for comparing a class referencing itself |
| def Test_Object_Compare_With_Recursive_Class_Ref() |
| var lines =<< trim END |
| vim9script |
| |
| class C |
| public var nest: C |
| endclass |
| |
| var o1 = C.new() |
| o1.nest = o1 |
| |
| var result = o1 == o1 |
| assert_equal(true, result) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| |
| class C |
| public var nest: C |
| endclass |
| var o1 = C.new() |
| var o2 = C.new(C.new()) |
| |
| var result = o1 == o2 |
| assert_equal(false, result) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| class C |
| var nest1: C |
| var nest2: C |
| def Init(n1: C, n2: C) |
| this.nest1 = n1 |
| this.nest2 = n2 |
| enddef |
| endclass |
| |
| var o1 = C.new() |
| var o2 = C.new() |
| o1.Init(o1, o2) |
| o2.Init(o2, o1) |
| |
| var result = o1 == o2 |
| assert_equal(true, result) |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| " Test for comparing a class with nesting objects |
| def Test_Object_Compare_With_Nesting_Objects() |
| # On a compare, after vim equal recurses 1000 times, not finding an unequal, |
| # return the compare is equal. |
| # Test that limit |
| |
| var lines =<< trim END |
| vim9script |
| class C |
| public var n: number |
| public var nest: C |
| |
| # Create a "C" that chains/nests to indicated depth. |
| # return {head: firstC, tail: lastC} |
| static def CreateNested(depth: number): dict<C> |
| var first = C.new(1, null_object) |
| var last = first |
| for i in range(2, depth) |
| last.nest = C.new(i, null_object) |
| last = last.nest |
| endfor |
| return {head: first, tail: last} |
| enddef |
| |
| # Return pointer to nth item in chain. |
| def GetLink(depth: number): C |
| var count = 1 |
| var p: C = this |
| while count < depth |
| p = p.nest |
| if p == null |
| throw "too deep" |
| endif |
| count += 1 |
| endwhile |
| return p |
| enddef |
| |
| # Return the length of the chain |
| def len(): number |
| var count = 1 |
| var p: C = this |
| while p.nest != null |
| p = p.nest |
| count += 1 |
| endwhile |
| return count |
| enddef |
| endclass |
| |
| var chain = C.CreateNested(3) |
| var s = "object of C {n: 1, nest: object of C {n: 2, nest: object of C {n: 3, nest: object of [unknown]}}}" |
| assert_equal(s, string(chain.head)) |
| assert_equal(3, chain.head->len()) |
| |
| var chain1 = C.CreateNested(100) |
| var chain2 = C.CreateNested(100) |
| assert_true(chain1.head == chain2.head) |
| |
| # modify the tail of chain2, compare not equal |
| chain2.tail.n = 123456 |
| assert_true(chain1.head != chain2.head) |
| |
| # a tail of a different length compares not equal |
| chain2 = C.CreateNested(101) |
| assert_true(chain1.head != chain2.head) |
| |
| chain1 = C.CreateNested(1000) |
| chain2 = C.CreateNested(1000) |
| assert_true(chain1.head == chain2.head) |
| |
| # modify the tail of chain2, compare not equal |
| chain2.tail.n = 123456 |
| assert_true(chain1.head != chain2.head) |
| |
| # try a chain longer that the limit |
| chain1 = C.CreateNested(1001) |
| chain2 = C.CreateNested(1001) |
| assert_true(chain1.head == chain2.head) |
| |
| # modify the tail, but still equal |
| chain2.tail.n = 123456 |
| assert_true(chain1.head == chain2.head) |
| |
| # remove 2 items from front, shorten the chain by two. |
| chain1.head = chain1.head.GetLink(3) |
| chain2.head = chain2.head.GetLink(3) |
| assert_equal(3, chain1.head.n) |
| assert_equal(3, chain2.head.n) |
| assert_equal(999, chain1.head->len()) |
| assert_equal(999, chain2.head->len()) |
| # Now less than the limit, compare not equal |
| assert_true(chain1.head != chain2.head) |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| " Test for using a compound operator from a lambda function in an object method |
| def Test_compound_op_in_objmethod_lambda() |
| # Test using the "+=" operator |
| var lines =<< trim END |
| vim9script |
| class A |
| var n: number = 10 |
| def Foo() |
| var Fn = () => { |
| this.n += 1 |
| } |
| Fn() |
| enddef |
| endclass |
| |
| var a = A.new() |
| a.Foo() |
| assert_equal(11, a.n) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| # Test using the "..=" operator |
| lines =<< trim END |
| vim9script |
| class A |
| var s: string = "a" |
| def Foo() |
| var Fn = () => { |
| this.s ..= "a" |
| } |
| Fn() |
| enddef |
| endclass |
| |
| var a = A.new() |
| a.Foo() |
| a.Foo() |
| assert_equal("aaa", a.s) |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| " Test for using test_refcount() with a class and an object |
| def Test_class_object_refcount() |
| var lines =<< trim END |
| vim9script |
| class A |
| endclass |
| var a: A = A.new() |
| assert_equal(2, test_refcount(A)) |
| assert_equal(1, test_refcount(a)) |
| var b = a |
| assert_equal(2, test_refcount(A)) |
| assert_equal(2, test_refcount(a)) |
| assert_equal(2, test_refcount(b)) |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| " call a lambda function in one object from another object |
| def Test_lambda_invocation_across_classes() |
| var lines =<< trim END |
| vim9script |
| class A |
| var s: string = "foo" |
| def GetFn(): func |
| var Fn = (): string => { |
| return this.s |
| } |
| return Fn |
| enddef |
| endclass |
| |
| class B |
| var s: string = "bar" |
| def GetFn(): func |
| var a = A.new() |
| return a.GetFn() |
| enddef |
| endclass |
| |
| var b = B.new() |
| var Fn = b.GetFn() |
| assert_equal("foo", Fn()) |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| " Test for using a class member which is an object of the current class |
| def Test_current_class_object_class_member() |
| var lines =<< trim END |
| vim9script |
| class A |
| public static var obj1: A = A.new(10) |
| var n: number |
| endclass |
| defcompile |
| assert_equal(10, A.obj1.n) |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| " Test for updating a base class variable from a base class method without the |
| " class name. This used to crash Vim (Github issue #14352). |
| def Test_use_base_class_variable_from_base_class_method() |
| var lines =<< trim END |
| vim9script |
| |
| class DictKeyClass |
| static var _obj_id_count = 1 |
| def _GenerateKey() |
| _obj_id_count += 1 |
| enddef |
| static def GetIdCount(): number |
| return _obj_id_count |
| enddef |
| endclass |
| |
| class C extends DictKeyClass |
| def F() |
| this._GenerateKey() |
| enddef |
| endclass |
| |
| C.new().F() |
| assert_equal(2, DictKeyClass.GetIdCount()) |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| " Test for accessing protected funcref object and class variables |
| def Test_protected_funcref() |
| # protected funcref object variable |
| var lines =<< trim END |
| vim9script |
| class Test1 |
| const _Id: func(any): any = (v) => v |
| endclass |
| var n = Test1.new()._Id(1) |
| END |
| v9.CheckScriptFailure(lines, 'E1333: Cannot access protected variable "_Id" in class "Test1"', 5) |
| |
| # protected funcref class variable |
| lines =<< trim END |
| vim9script |
| class Test2 |
| static const _Id: func(any): any = (v) => v |
| endclass |
| var n = Test2._Id(2) |
| END |
| v9.CheckScriptFailure(lines, 'E1333: Cannot access protected variable "_Id" in class "Test2"', 5) |
| enddef |
| |
| " Test for using lambda block in classes |
| def Test_lambda_block_in_class() |
| # This used to crash Vim |
| var lines =<< trim END |
| vim9script |
| class IdClass1 |
| const Id: func(number): number = (num: number): number => { |
| # Return a ID |
| return num * 10 |
| } |
| endclass |
| var id = IdClass1.new() |
| assert_equal(20, id.Id(2)) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| # This used to crash Vim |
| lines =<< trim END |
| vim9script |
| class IdClass2 |
| static const Id: func(number): number = (num: number): number => { |
| # Return a ID |
| return num * 2 |
| } |
| endclass |
| assert_equal(16, IdClass2.Id(8)) |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| " Test for defcompiling an abstract method |
| def Test_abstract_method_defcompile() |
| # Compile an abstract class with abstract object methods |
| var lines =<< trim END |
| vim9script |
| abstract class A |
| abstract def Foo(): string |
| abstract def Bar(): list<string> |
| endclass |
| defcompile |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| # Compile a concrete object method in an abstract class |
| lines =<< trim END |
| vim9script |
| abstract class A |
| abstract def Foo(): string |
| abstract def Bar(): list<string> |
| def Baz(): string |
| pass |
| enddef |
| endclass |
| defcompile |
| END |
| v9.CheckScriptFailure(lines, 'E476: Invalid command: pass', 1) |
| |
| # Compile a concrete class method in an abstract class |
| lines =<< trim END |
| vim9script |
| abstract class A |
| abstract def Foo(): string |
| abstract def Bar(): list<string> |
| static def Baz(): string |
| pass |
| enddef |
| endclass |
| defcompile |
| END |
| v9.CheckScriptFailure(lines, 'E476: Invalid command: pass', 1) |
| enddef |
| |
| " Test for defining a class in a function |
| def Test_class_definition_in_a_function() |
| var lines =<< trim END |
| vim9script |
| def Foo() |
| class A |
| endclass |
| enddef |
| defcompile |
| END |
| v9.CheckScriptFailure(lines, 'E1429: Class can only be used in a script', 1) |
| enddef |
| |
| " Test for using [] with a class and an object |
| def Test_class_object_index() |
| var lines =<< trim END |
| vim9script |
| class A |
| endclass |
| A[10] = 1 |
| END |
| v9.CheckScriptFailure(lines, 'E689: Index not allowed after a class: A[10] = 1', 4) |
| |
| lines =<< trim END |
| vim9script |
| class A |
| endclass |
| var a = A.new() |
| a[10] = 1 |
| END |
| v9.CheckScriptFailure(lines, 'E689: Index not allowed after a object: a[10] = 1', 5) |
| enddef |
| |
| def Test_class_member_init_typecheck() |
| # Ensure the class member is assigned its declared type. |
| var lines =<< trim END |
| vim9script |
| class S |
| static var l: list<string> = [] |
| endclass |
| S.l->add(123) |
| END |
| v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected string but got number', 5) |
| |
| # Ensure the initializer value and the declared type match. |
| lines =<< trim END |
| vim9script |
| class S |
| var l: list<string> = [1, 2, 3] |
| endclass |
| var o = S.new() |
| END |
| v9.CheckScriptFailure(lines, 'E1382: Variable "l": type mismatch, expected list<string> but got list<number>') |
| |
| # Ensure the class member is assigned its declared type. |
| lines =<< trim END |
| vim9script |
| class S |
| var l: list<string> = [] |
| endclass |
| var o = S.new() |
| o.l->add(123) |
| END |
| v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected string but got number', 6) |
| enddef |
| |
| def Test_class_cast() |
| var lines =<< trim END |
| vim9script |
| class A |
| endclass |
| class B extends A |
| var mylen: number |
| endclass |
| def F(o: A): number |
| return (<B>o).mylen |
| enddef |
| |
| defcompile F |
| END |
| v9.CheckScriptSuccess(lines) |
| enddef |
| |
| " Test for using a variable of type "any" with an object |
| def Test_any_obj_var_type() |
| var lines =<< trim END |
| vim9script |
| class A |
| var name: string = "foobar" |
| def Foo(): string |
| return "func foo" |
| enddef |
| endclass |
| |
| def CheckVals(x: any) |
| assert_equal("foobar", x.name) |
| assert_equal("func foo", x.Foo()) |
| enddef |
| |
| var a = A.new() |
| CheckVals(a) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| # Try to set a non-existing variable |
| lines =<< trim END |
| vim9script |
| class A |
| var name: string = "foobar" |
| endclass |
| |
| def SetNonExistingVar(x: any) |
| x.bar = [1, 2, 3] |
| enddef |
| |
| var a = A.new() |
| SetNonExistingVar(a) |
| END |
| v9.CheckScriptFailure(lines, 'E1326: Variable "bar" not found in object "A"', 1) |
| |
| # Try to read a non-existing variable |
| lines =<< trim END |
| vim9script |
| class A |
| var name: string = "foobar" |
| endclass |
| |
| def GetNonExistingVar(x: any) |
| var i: dict<any> = x.bar |
| enddef |
| |
| var a = A.new() |
| GetNonExistingVar(a) |
| END |
| v9.CheckScriptFailure(lines, 'E1326: Variable "bar" not found in object "A"', 1) |
| |
| # Try to invoke a non-existing method |
| lines =<< trim END |
| vim9script |
| class A |
| def Foo(): number |
| return 10 |
| enddef |
| endclass |
| |
| def CallNonExistingMethod(x: any) |
| var i: number = x.Bar() |
| enddef |
| |
| var a = A.new() |
| CallNonExistingMethod(a) |
| END |
| v9.CheckScriptFailure(lines, 'E1326: Variable "Bar" not found in object "A"', 1) |
| |
| # Use an object which is a Dict value |
| lines =<< trim END |
| vim9script |
| class Foo |
| def Bar(): number |
| return 369 |
| enddef |
| endclass |
| |
| def GetValue(FooDict: dict<any>): number |
| var n: number = 0 |
| for foo in values(FooDict) |
| n += foo.Bar() |
| endfor |
| return n |
| enddef |
| |
| var d = {'x': Foo.new()} |
| assert_equal(369, GetValue(d)) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| # Nested data. Object containing a Dict containing another Object. |
| lines =<< trim END |
| vim9script |
| class Context |
| public var state: dict<any> = {} |
| endclass |
| |
| class Metadata |
| public var value = 0 |
| endclass |
| |
| var ctx = Context.new() |
| ctx.state["meta"] = Metadata.new(2468) |
| |
| const foo = ctx.state.meta.value |
| |
| def F(): number |
| const bar = ctx.state.meta.value |
| return bar |
| enddef |
| |
| assert_equal(2468, F()) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| # Accessing an object from a method inside the class using any type |
| lines =<< trim END |
| vim9script |
| class C |
| def _G(): string |
| return '_G' |
| enddef |
| static def S(o_any: any): string |
| return o_any._G() |
| enddef |
| endclass |
| |
| var o1 = C.new() |
| assert_equal('_G', C.S(o1)) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| # Modifying an object private variable from a method in another class using |
| # any type |
| lines =<< trim END |
| vim9script |
| |
| class A |
| var num = 10 |
| endclass |
| |
| class B |
| def SetVal(x: any) |
| x.num = 20 |
| enddef |
| endclass |
| |
| var a = A.new() |
| var b = B.new() |
| b.SetVal(a) |
| END |
| v9.CheckScriptFailure(lines, 'E1335: Variable "num" in class "A" is not writable', 1) |
| |
| # Accessing a object protected variable from a method in another class using |
| # any type |
| lines =<< trim END |
| vim9script |
| |
| class A |
| var _num = 10 |
| endclass |
| |
| class B |
| def GetVal(x: any): number |
| return x._num |
| enddef |
| endclass |
| |
| var a = A.new() |
| var b = B.new() |
| var i = b.GetVal(a) |
| END |
| v9.CheckScriptFailure(lines, 'E1333: Cannot access protected variable "_num" in class "A"', 1) |
| |
| # Accessing an object returned from an imported function and class |
| lines =<< trim END |
| vim9script |
| export class Foo |
| public var name: string |
| endclass |
| |
| export def ReturnFooObject(): Foo |
| var r = Foo.new('star') |
| return r |
| enddef |
| END |
| writefile(lines, 'Xanyvar1.vim', 'D') |
| |
| lines =<< trim END |
| vim9script |
| |
| import './Xanyvar1.vim' |
| |
| def GetName(): string |
| var whatever = Xanyvar1.ReturnFooObject() |
| return whatever.name |
| enddef |
| |
| assert_equal('star', GetName()) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| # Try to modify a private object variable using a variable of type "any" |
| lines =<< trim END |
| vim9script |
| |
| class Foo |
| var n: number = 10 |
| endclass |
| def Fn(x: any) |
| x.n = 20 |
| enddef |
| var a = Foo.new() |
| Fn(a) |
| END |
| v9.CheckScriptFailure(lines, 'E1335: Variable "n" in class "Foo" is not writable', 1) |
| |
| # Try to read a protected object variable using a variable of type "any" |
| lines =<< trim END |
| vim9script |
| |
| class Foo |
| var _n: number = 10 |
| endclass |
| def Fn(x: any): number |
| return x._n |
| enddef |
| |
| var a = Foo.new() |
| Fn(a) |
| END |
| v9.CheckScriptFailure(lines, 'E1333: Cannot access protected variable "_n" in class "Foo"', 1) |
| |
| # Read a protected object variable using a variable of type "any" in an object |
| # method |
| lines =<< trim END |
| vim9script |
| |
| class Foo |
| var _n: number = 10 |
| def Fn(x: any): number |
| return x._n |
| enddef |
| endclass |
| |
| var a = Foo.new() |
| assert_equal(10, a.Fn(a)) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| # Try to call a protected object method using a "any" type variable |
| lines =<< trim END |
| vim9script |
| |
| class Foo |
| def _GetVal(): number |
| return 234 |
| enddef |
| endclass |
| def Fn(x: any): number |
| return x._GetVal() |
| enddef |
| |
| var a = Foo.new() |
| Fn(a) |
| END |
| v9.CheckScriptFailure(lines, 'E1366: Cannot access protected method: _GetVal', 1) |
| |
| # Call a protected object method using a "any" type variable from another |
| # object method |
| lines =<< trim END |
| vim9script |
| |
| class Foo |
| def _GetVal(): number |
| return 234 |
| enddef |
| def FooVal(x: any): number |
| return x._GetVal() |
| enddef |
| endclass |
| |
| var a = Foo.new() |
| assert_equal(234, a.FooVal(a)) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| # Method chaining |
| lines =<< trim END |
| vim9script |
| |
| export class T |
| var id: number = 268 |
| def F(): any |
| return this |
| enddef |
| endclass |
| |
| def H() |
| var a = T.new().F().F() |
| assert_equal(268, a.id) |
| enddef |
| H() |
| |
| var b: T = T.new().F().F() |
| assert_equal(268, b.id) |
| END |
| v9.CheckScriptSuccess(lines) |
| |
| # Using a null object to access a member variable |
| lines =<< trim END |
| vim9script |
| def Fn(x: any): number |
| return x.num |
| enddef |
| |
| Fn(null_object) |
| END |
| v9.CheckScriptFailure(lines, 'E1360: Using a null object', 1) |
| |
| # Using a null object to invoke a method |
| lines =<< trim END |
| vim9script |
| def Fn(x: any) |
| x.Foo() |
| enddef |
| |
| Fn(null_object) |
| END |
| v9.CheckScriptFailure(lines, 'E1360: Using a null object', 1) |
| |
| # Try to change a const object variable using a "any" variable |
| lines =<< trim END |
| vim9script |
| class A |
| public const v1: number = 123 |
| endclass |
| |
| def Fn(o: any) |
| o.v1 = 321 |
| enddef |
| |
| var a = A.new() |
| Fn(a) |
| END |
| v9.CheckScriptFailure(lines, 'E1409: Cannot change read-only variable "v1" in class "A"', 1) |
| |
| # Try to change a final object variable using a "any" variable |
| lines =<< trim END |
| vim9script |
| class A |
| public final v1: number = 123 |
| endclass |
| |
| def Fn(o: any) |
| o.v1 = 321 |
| enddef |
| |
| var a = A.new() |
| Fn(a) |
| END |
| v9.CheckScriptFailure(lines, 'E1409: Cannot change read-only variable "v1" in class "A"', 1) |
| |
| # Assign a different type of value to an "any" type object variable |
| lines =<< trim END |
| vim9script |
| class A |
| public var v1: list<any> = [1, 2] |
| endclass |
| |
| def Fn(o: A) |
| o.v1 = 'abc' |
| enddef |
| |
| var a = A.new() |
| Fn(a) |
| END |
| v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected list<any> but got string', 1) |
| enddef |
| |
| " Test for using an object method with mapnew() |
| def Test_mapnew_with_instance_method() |
| var lines =<< trim END |
| vim9script |
| |
| class Foo |
| var str: string |
| var nums: list<number> = [1, 2, 3] |
| |
| def InstanceMethod(n: number): string |
| return this.str .. n |
| enddef |
| |
| def MapperMethod(idx: number, elem: number): string |
| return elem->this.InstanceMethod() |
| enddef |
| |
| def MapTest() |
| this.str = "foo" |
| var l = ['foo1', 'foo2', 'foo3'] |
| assert_equal(l, this.nums->mapnew(this.MapperMethod)) |
| enddef |
| endclass |
| |
| Foo.new().MapTest() |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # Error in the mapnew() function |
| lines =<< trim END |
| vim9script |
| |
| class Foo |
| var str: string |
| var nums: list<number> = [1, 2, 3] |
| |
| def InstanceMethod(n: number): string |
| throw "InstanceMethod failed" |
| enddef |
| |
| def MapperMethod(idx: number, elem: number): string |
| return elem->this.InstanceMethod() |
| enddef |
| |
| def MapTest() |
| this.str = "foo" |
| var caught_exception: bool = false |
| try |
| this.nums->mapnew(this.MapperMethod) |
| catch /InstanceMethod failed/ |
| caught_exception = true |
| endtry |
| assert_true(caught_exception) |
| enddef |
| endclass |
| |
| Foo.new().MapTest() |
| END |
| v9.CheckSourceSuccess(lines) |
| enddef |
| |
| " Test for using an object method in a method call. |
| def Test_use_object_method_in_a_method_call() |
| var lines =<< trim END |
| vim9script |
| |
| class Foo |
| def Cost(nums: list<number>): number |
| return nums[0] * nums[1] |
| enddef |
| |
| def ShowCost(): string |
| var g = [4, 5] |
| return $"Cost is: {g->this.Cost()}" |
| enddef |
| endclass |
| |
| var d = Foo.new() |
| assert_equal('Cost is: 20', d.ShowCost()) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # Test for using a non-existing object method in string interpolation |
| lines =<< trim END |
| vim9script |
| |
| class Foo |
| def Cost(nums: list<number>): number |
| return nums[0] * nums[1] |
| enddef |
| |
| def ShowCost(): string |
| var g = [4, 5] |
| echo $"Cost is: {g->this.NewCost()}" |
| enddef |
| endclass |
| |
| var d = Foo.new() |
| d.ShowCost() |
| END |
| v9.CheckSourceFailure(lines, 'E1326: Variable "NewCost" not found in object "Foo"') |
| enddef |
| |
| " Test for referencing an object variable which is not yet initialized |
| def Test_uninitialized_object_var() |
| var lines =<< trim END |
| vim9script |
| class Foo |
| const two: number = Foo.Two(this) |
| const one: number = 1 |
| |
| static def Two(that: Foo): number |
| return that.one + 2 |
| enddef |
| endclass |
| |
| echo Foo.Two(Foo.new()) |
| END |
| v9.CheckSourceFailure(lines, "E1430: Uninitialized object variable 'one' referenced") |
| |
| lines =<< trim END |
| vim9script |
| class Foo |
| const one: number = Foo.One(this) |
| |
| static def One(that: Foo): number |
| return 1 |
| enddef |
| endclass |
| |
| assert_equal(1, Foo.One(Foo.new())) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| class Foo |
| const one: number = 1 |
| const two: number = Foo.Two(this) |
| |
| static def Two(that: Foo): number |
| return that.one + 1 |
| enddef |
| endclass |
| |
| assert_equal(2, Foo.Two(Foo.new())) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| class Foo |
| const Id: func(any): any = ((_) => (v) => v)(this) |
| |
| static def Id(that: Foo): func(any): any |
| return that.Id |
| enddef |
| endclass |
| |
| assert_equal(5, Foo.Id(Foo.new())(5)) |
| assert_equal(7, Foo.new().Id(7)) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| class Foo |
| const Id: func(any): any = ((that) => (_) => that)(this) |
| |
| static def Id(that: Foo): func(any): any |
| return that.Id |
| enddef |
| endclass |
| |
| const Id0: func(any): any = Foo.Id(Foo.new()) |
| const Id1: func(any): any = Foo.new().Id |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| class Foo |
| const Id: any = Foo.Id(this) |
| |
| static def Id(that: Foo): any |
| return that.Id |
| enddef |
| endclass |
| |
| const Id2: any = Foo.Id(Foo.new()) |
| const Id3: any = Foo.new().Id |
| END |
| v9.CheckSourceFailure(lines, "E1430: Uninitialized object variable 'Id' referenced") |
| |
| lines =<< trim END |
| vim9script |
| |
| class Foo |
| var x: string = '' |
| var Y: func(): string = () => this.x |
| endclass |
| |
| var foo = Foo.new('ok') |
| assert_equal('ok', foo.Y()) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| |
| class Foo |
| var x: string = this.x |
| endclass |
| |
| var foo = Foo.new('ok') |
| END |
| v9.CheckSourceFailure(lines, "E1430: Uninitialized object variable 'x' referenced") |
| enddef |
| |
| " Test for initializing member variables of compound type in the constructor |
| def Test_constructor_init_compound_member_var() |
| var lines =<< trim END |
| vim9script |
| |
| class Foo |
| var v1: string = "aaa" |
| var v2: list<number> = [1, 2] |
| var v3: dict<string> = {a: 'a', b: 'b'} |
| endclass |
| |
| class Bar |
| var v4: string = "bbb" |
| var v5: Foo = Foo.new() |
| var v6: list<number> = [1, 2] |
| endclass |
| |
| var b: Bar = Bar.new() |
| assert_equal("aaa", b.v5.v1) |
| assert_equal([1, 2], b.v5.v2) |
| assert_equal({a: 'a', b: 'b'}, b.v5.v3) |
| assert_equal("bbb", b.v4) |
| assert_equal([1, 2], b.v6) |
| END |
| v9.CheckSourceSuccess(lines) |
| enddef |
| |
| " Test for using a concrete method in an abstract extended class which is |
| " further extended |
| def Test_abstract_method_across_hierarchy() |
| var lines =<< trim END |
| vim9script |
| |
| abstract class A |
| abstract def Foo(): string |
| endclass |
| |
| abstract class B extends A |
| abstract def Bar(): string |
| endclass |
| |
| class C extends B |
| def Foo(): string |
| return 'foo' |
| enddef |
| |
| def Bar(): string |
| return 'bar' |
| enddef |
| endclass |
| |
| def Fn1(a: A): string |
| return a.Foo() |
| enddef |
| |
| def Fn2(b: B): string |
| return b.Bar() |
| enddef |
| |
| var c = C.new() |
| assert_equal('foo', Fn1(c)) |
| assert_equal('bar', Fn2(c)) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| |
| abstract class A |
| abstract def Foo(): string |
| endclass |
| |
| abstract class B extends A |
| abstract def Bar(): string |
| endclass |
| |
| class C extends B |
| def Bar(): string |
| return 'bar' |
| enddef |
| endclass |
| |
| defcompile |
| END |
| v9.CheckSourceFailure(lines, 'E1373: Abstract method "Foo" is not implemented') |
| |
| lines =<< trim END |
| vim9script |
| |
| abstract class A |
| abstract def M1(): string |
| abstract def M2(): string |
| endclass |
| |
| abstract class B extends A |
| def M1(): string |
| return 'B: M1' |
| enddef |
| |
| def M2(): string |
| return 'B: M2' |
| enddef |
| endclass |
| |
| class C1 extends B |
| def M1(): string |
| return 'C1: M1' |
| enddef |
| endclass |
| |
| class C2 extends B |
| def M2(): string |
| return 'C2: M2' |
| enddef |
| endclass |
| |
| class D1 extends C1 |
| endclass |
| |
| class D2 extends C2 |
| endclass |
| |
| var l: list<string> = [] |
| for Type in ['C1', 'C2', 'D1', 'D2'] |
| l->add(eval($'{Type}.new().M1()')) |
| l->add(eval($'{Type}.new().M2()')) |
| endfor |
| assert_equal(['C1: M1', 'B: M2', 'B: M1', 'C2: M2', 'C1: M1', 'B: M2', 'B: M1', 'C2: M2'], l) |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| lines =<< trim END |
| vim9script |
| |
| abstract class A |
| abstract def M1(): string |
| abstract def M2(): string |
| endclass |
| |
| class B extends A |
| def M1(): string |
| return 'B: M1' |
| enddef |
| |
| def M2(): string |
| return 'B: M2' |
| enddef |
| endclass |
| |
| abstract class C extends B |
| endclass |
| |
| class D1 extends C |
| def M1(): string |
| return 'D1: M1' |
| enddef |
| endclass |
| |
| class D2 extends C |
| def M2(): string |
| return 'D2: M2' |
| enddef |
| endclass |
| |
| class E1 extends D1 |
| endclass |
| |
| class E2 extends D2 |
| endclass |
| |
| var l: list<string> = [] |
| for Type in ['B', 'D1', 'D2', 'E1', 'E2'] |
| l->add(eval($'{Type}.new().M1()')) |
| l->add( eval($'{Type}.new().M2()')) |
| endfor |
| assert_equal(['B: M1', 'B: M2', 'D1: M1', 'B: M2', 'B: M1', 'D2: M2', 'D1: M1', 'B: M2', 'B: M1', 'D2: M2'], l) |
| END |
| v9.CheckSourceSuccess(lines) |
| enddef |
| |
| " Test for using a protected new() method (singleton design pattern) |
| def Test_protected_new_method() |
| var lines =<< trim END |
| vim9script |
| class A |
| def _new() |
| enddef |
| endclass |
| var a = A.new() |
| END |
| v9.CheckSourceFailure(lines, 'E1325: Method "new" not found in class "A"', 6) |
| |
| lines =<< trim END |
| vim9script |
| class A |
| static var _instance: A |
| var str: string |
| def _new(str: string) |
| this.str = str |
| enddef |
| static def GetInstance(str: string): A |
| if _instance == null |
| _instance = A._new(str) |
| endif |
| return _instance |
| enddef |
| endclass |
| var a: A = A.GetInstance('foo') |
| var b: A = A.GetInstance('bar') |
| assert_equal('foo', a.str) |
| assert_equal('foo', b.str) |
| END |
| v9.CheckSourceSuccess(lines) |
| enddef |
| |
| " Test for using 'super' in a closure function inside an object method |
| def Test_super_in_closure() |
| var lines =<< trim END |
| vim9script |
| |
| class A |
| const _value: number |
| |
| def Fn(): func(any): number |
| return (_: any) => this._value |
| enddef |
| endclass |
| |
| class B extends A |
| def Fn(): func(any): number |
| return (_: any) => super._value |
| enddef |
| endclass |
| |
| assert_equal(100, A.new(100).Fn()(null)) |
| assert_equal(200, B.new(200).Fn()(null)) |
| END |
| v9.CheckSourceSuccess(lines) |
| enddef |
| |
| " Test for using 'super' to access methods and variables |
| def Test_super_keyword() |
| var 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(): 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) |
| |
| # 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) |
| |
| # Using 'super' to access an object variable in the parent |
| lines =<< trim END |
| vim9script |
| |
| class A |
| var foo: string = 'xxx' |
| endclass |
| |
| class B extends A |
| def GetString(): string |
| return super.foo |
| enddef |
| endclass |
| |
| var b: B = B.new() |
| echo b.GetString() |
| END |
| v9.CheckSourceSuccess(lines) |
| |
| # Using super to access an overriden method in the parent class |
| lines =<< trim END |
| vim9script |
| |
| class A |
| def Foo(): string |
| return 'A.Foo' |
| enddef |
| endclass |
| |
| class B extends A |
| def Foo(): string |
| return 'B.Foo' |
| enddef |
| |
| def Bar(): string |
| return $'{super.Foo()} {this.Foo()}' |
| enddef |
| endclass |
| |
| var b = B.new() |
| assert_equal('A.Foo B.Foo', b.Bar()) |
| END |
| v9.CheckSourceSuccess(lines) |
| enddef |
| |
| " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker |