From ec3cebbd2b6b7583d2f683f5e66345163ec122aa Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Fri, 27 Oct 2023 19:35:26 +0200 Subject: patch 9.0.2076: Vim9: No support for type aliases Problem: Vim9: No support for type aliases Solution: Implement :type command A type definition is giving a name to a type specification. This also known type alias. :type ListOfStrings = list The type alias can be used wherever a built-in type can be used. The type alias name must start with an upper case character. closes: #13407 Signed-off-by: Christian Brabandt Signed-off-by: Yegappan Lakshmanan --- src/testdir/test_vim9_class.vim | 12 +- src/testdir/test_vim9_script.vim | 380 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 385 insertions(+), 7 deletions(-) (limited to 'src/testdir') diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim index 1c309e4f0c..a8d0fd6697 100644 --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -170,7 +170,7 @@ def Test_class_basic() if A endif END - v9.CheckSourceFailure(lines, 'E1319: Using a class as a Number', 4) + v9.CheckSourceFailure(lines, 'E1319: Using a Class as a Number', 4) # Test for using object as a bool lines =<< trim END @@ -181,7 +181,7 @@ def Test_class_basic() if a endif END - v9.CheckSourceFailure(lines, 'E1320: Using an object as a Number', 5) + v9.CheckSourceFailure(lines, 'E1320: Using an Object as a Number', 5) # Test for using class as a float lines =<< trim END @@ -190,7 +190,7 @@ def Test_class_basic() endclass sort([1.1, A], 'f') END - v9.CheckSourceFailure(lines, 'E1321: Using a class as a Float', 4) + v9.CheckSourceFailure(lines, 'E1321: Using a Class as a Float', 4) # Test for using object as a float lines =<< trim END @@ -200,7 +200,7 @@ def Test_class_basic() var a = A.new() sort([1.1, a], 'f') END - v9.CheckSourceFailure(lines, 'E1322: Using an object as a Float', 5) + v9.CheckSourceFailure(lines, 'E1322: Using an Object as a Float', 5) # Test for using class as a string lines =<< trim END @@ -209,7 +209,7 @@ def Test_class_basic() endclass :exe 'call ' .. A END - v9.CheckSourceFailure(lines, 'E1323: Using a class as a String', 4) + v9.CheckSourceFailure(lines, 'E1323: Using a Class as a String', 4) # Test for using object as a string lines =<< trim END @@ -219,7 +219,7 @@ def Test_class_basic() var a = A.new() :exe 'call ' .. a END - v9.CheckSourceFailure(lines, 'E1324: Using an object as a String', 5) + 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. diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index cac8484977..2bc9b14aa3 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -4722,7 +4722,7 @@ def Test_defer_after_exception() assert_equal([2, 3, 1, 4, 5, 6, 7], callTrace) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef " Test for multiple deferred function which throw exceptions. @@ -4780,6 +4780,384 @@ def Test_multidefer_with_exception() assert_equal('E605: Exception not caught: InnerException', v:errmsg) assert_equal([11, 9, 10, 7, 8, 5, 1, 3, 4, 12, 15, 16], callTrace) END + v9.CheckSourceSuccess(lines) +enddef + +" Test for :type command to create type aliases +def Test_typealias() + var lines =<< trim END + vim9script + type ListOfStrings = list + var a: ListOfStrings = ['a', 'b'] + assert_equal(['a', 'b'], a) + def Foo(b: ListOfStrings): ListOfStrings + var c: ListOfStrings = ['c', 'd'] + assert_equal(['c', 'd'], c) + return b + enddef + assert_equal(['e', 'f'], Foo(['e', 'f'])) + assert_equal('typealias>', typename(ListOfStrings)) + assert_equal(v:t_typealias, type(ListOfStrings)) + assert_equal('ListOfStrings', string(ListOfStrings)) + assert_equal(false, null == ListOfStrings) + END + v9.CheckSourceSuccess(lines) + + # Use :type outside a Vim9 script + lines =<< trim END + type Index = number + END + v9.CheckSourceFailure(lines, 'E1393: Type can only be defined in Vim9 script', 1) + + # Use :type without any arguments + lines =<< trim END + vim9script + type + END + v9.CheckSourceFailure(lines, 'E1397: Missing type alias name', 2) + + # Use :type with a name but no type + lines =<< trim END + vim9script + type MyType + END + v9.CheckSourceFailure(lines, "E398: Missing '=': ", 2) + + # Use :type with a name but no type following "=" + lines =<< trim END + vim9script + type MyType = + END + v9.CheckSourceFailure(lines, 'E1398: Missing type alias type', 2) + + # No space before or after "=" + lines =<< trim END + vim9script + type MyType=number + END + v9.CheckSourceFailure(lines, 'E1315: White space required after name: MyType=number', 2) + + # No space after "=" + lines =<< trim END + vim9script + type MyType =number + END + v9.CheckSourceFailure(lines, "E1069: White space required after '=': =number", 2) + + # type alias without "=" + lines =<< trim END + vim9script + type Index number + END + v9.CheckSourceFailure(lines, "E398: Missing '=': number", 2) + + # type alias for a non-existing type + lines =<< trim END + vim9script + type Index = integer + END + v9.CheckSourceFailure(lines, 'E1010: Type not recognized: integer', 2) + + # type alias starting with lower-case letter + lines =<< trim END + vim9script + type index number + END + v9.CheckSourceFailure(lines, 'E1394: Type name must start with an uppercase letter: index number', 2) + + # No white space following the alias name + lines =<< trim END + vim9script + type Index:number + END + v9.CheckSourceFailure(lines, 'E1315: White space required after name: Index:number', 2) + + # something following the type alias + lines =<< trim END + vim9script + type ListOfNums = list string + END + v9.CheckSourceFailure(lines, 'E488: Trailing characters: string', 2) + + # type alias name collides with a variable name + lines =<< trim END + vim9script + var ListOfNums: number = 10 + type ListOfNums = list + END + v9.CheckSourceFailure(lines, 'E1041: Redefining script item: "ListOfNums"', 3) + + # duplicate type alias name + lines =<< trim END + vim9script + type MyList = list + type MyList = list + END + v9.CheckSourceFailure(lines, 'E1396: Type alias "MyList" already exists', 3) + + # Sourcing a script twice (which will free script local variables) + lines =<< trim END + vim9script + class C + endclass + type AC = C + assert_equal('typealias>', typename(AC)) + END + new + setline(1, lines) + :source + :source + bw! + + # Assigning to a type alias (script level) + lines =<< trim END + vim9script + type MyType = list + MyType = [1, 2, 3] + END + v9.CheckSourceFailure(lines, 'E1395: Type alias "MyType" cannot be used as a variable', 3) + + # Assigning a type alias (def function level) + lines =<< trim END + vim9script + type A = list + def Foo() + var x = A + enddef + Foo() + END + v9.CheckSourceFailure(lines, 'E1395: Type alias "A" cannot be used as a variable', 1) + + # Using type alias in an expression (script level) + lines =<< trim END + vim9script + type MyType = list + assert_fails('var m = MyType', 'E1395: Type alias "MyType" cannot be used as a variable') + assert_fails('var i = MyType + 1', 'E1395: Type alias "MyType" cannot be used as a variable') + assert_fails('var f = 1.0 + MyType', 'E1395: Type alias "MyType" cannot be used as a variable') + assert_fails('MyType += 10', 'E1395: Type alias "MyType" cannot be used as a variable') + END + v9.CheckSourceSuccess(lines) + + # Using type alias in an expression (def function level) + lines =<< trim END + vim9script + type MyType = list + def Foo() + var x = MyType + 1 + enddef + Foo() + END + v9.CheckSourceFailure(lines, 'E1395: Type alias "MyType" cannot be used as a variable', 1) + + # Using type alias in an expression (def function level) + lines =<< trim END + vim9script + type MyType = list + def Foo() + MyType = list + enddef + Foo() + END + v9.CheckSourceFailure(lines, 'E46: Cannot change read-only variable "MyType"', 1) + + # Using type alias in an expression (def function level) + lines =<< trim END + vim9script + type MyType = list + def Foo() + MyType += 10 + enddef + Foo() + END + v9.CheckSourceFailure(lines, 'E46: Cannot change read-only variable "MyType"', 1) + + # Creating a typealias in a def function + lines =<< trim END + vim9script + def Foo() + var n: number = 10 + type A = list + enddef + defcompile + END + v9.CheckSourceFailure(lines, 'E1399: Type can only be used in a script', 2) + + # json_encode should fail with a type alias + lines =<< trim END + vim9script + type A = list + var x = json_encode(A) + END + v9.CheckSourceFailure(lines, 'E1161: Cannot json encode a typealias', 3) + + # Comparing type alias with a number (script level) + lines =<< trim END + vim9script + type A = list + var n: number + var x = A == n + END + v9.CheckSourceFailure(lines, 'E1072: Cannot compare typealias with number', 4) + + # Comparing type alias with a number (def function level) + lines =<< trim END + vim9script + type A = list + def Foo() + var n: number + var x = A == n + enddef + Foo() + END + v9.CheckSourceFailure(lines, 'E1395: Type alias "A" cannot be used as a variable', 2) +enddef + +" Test for exporting and importing type aliases +def Test_import_typealias() + var lines =<< trim END + vim9script + export type MyType = list + END + writefile(lines, 'Xtypeexport.vim', 'D') + + lines =<< trim END + vim9script + import './Xtypeexport.vim' as A + + var myList: A.MyType = [1, 2, 3] + def Foo(l: A.MyType) + assert_equal([1, 2, 3], l) + enddef + Foo(myList) + END + v9.CheckScriptSuccess(lines) + + # Use a non existing type alias + lines =<< trim END + vim9script + import './Xtypeexport.vim' as A + + var myNum: A.SomeType = 10 + END + v9.CheckScriptFailure(lines, 'E1010: Type not recognized: A.SomeType = 10', 4) + + # Use a type alias that is not exported + lines =<< trim END + vim9script + type NewType = dict + END + writefile(lines, 'Xtypeexport2.vim', 'D') + lines =<< trim END + vim9script + import './Xtypeexport2.vim' as A + + var myDict: A.NewType = {} + END + v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: NewType', 4) + + # Using the same name as an imported type alias + lines =<< trim END + vim9script + export type MyType2 = list + END + writefile(lines, 'Xtypeexport3.vim', 'D') + lines =<< trim END + vim9script + import './Xtypeexport3.vim' as A + + type MyType2 = A.MyType2 + var myList1: A.MyType2 = [1, 2, 3] + var myList2: MyType2 = [4, 5, 6] + assert_equal([1, 2, 3], myList1) + assert_equal([4, 5, 6], myList2) + END + v9.CheckScriptSuccess(lines) +enddef + +" Test for using typealias as a def function argument and return type +def Test_typealias_func_argument() + var lines =<< trim END + vim9script + type A = list + def Foo(l: A): A + assert_equal([1, 2], l) + return l + enddef + var x: A = [1, 2] + assert_equal([1, 2], Foo(x)) + END + v9.CheckScriptSuccess(lines) + + # passing a type alias variable to a function expecting a specific type + lines =<< trim END + vim9script + type A = list + def Foo(l: list) + assert_equal([1, 2], l) + enddef + var x: A = [1, 2] + Foo(x) + END + v9.CheckScriptSuccess(lines) + + # passing a type alias variable to a function expecting any + lines =<< trim END + vim9script + type A = list + def Foo(l: any) + assert_equal([1, 2], l) + enddef + var x: A = [1, 2] + Foo(x) + END + v9.CheckScriptSuccess(lines) +enddef + +" Using a type alias with a builtin function +def Test_typealias_with_builtin_functions() + var lines =<< trim END + vim9script + type A = list + assert_equal(0, empty(A)) + END + v9.CheckScriptSuccess(lines) + + # Using a type alias with len() + lines =<< trim END + vim9script + type A = list + var x = len(A) + END + v9.CheckScriptFailure(lines, 'E701: Invalid type for len()', 3) + + # Using a type alias with eval() + lines =<< trim END + vim9script + type A = number + def Foo() + var x = eval("A") + enddef + Foo() + END + v9.CheckScriptFailure(lines, 'E1395: Type alias "A" cannot be used as a variable', 1) +enddef + +" Test for type alias refcount +def Test_typealias_refcount() + var lines =<< trim END + vim9script + type A = list + assert_equal(1, test_refcount(A)) + END + v9.CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + type B = list + var x: B = [] + assert_equal(1, test_refcount(B)) + END v9.CheckScriptSuccess(lines) enddef -- cgit v1.2.3