diff src/testdir/test_vim9_script.vim @ 33678:7d9d2404a3d4 v9.0.2076

patch 9.0.2076: Vim9: No support for type aliases Commit: https://github.com/vim/vim/commit/ec3cebbd2b6b7583d2f683f5e66345163ec122aa Author: Yegappan Lakshmanan <yegappan@yahoo.com> Date: Fri Oct 27 19:35:26 2023 +0200 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<string> 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 <cb@256bit.org> Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
author Christian Brabandt <cb@256bit.org>
date Fri, 27 Oct 2023 19:45:05 +0200
parents 53416c49a7ab
children f126ffc85f7c
line wrap: on
line diff
--- 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<string>
+    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<list<string>>', 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<number> 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<number>
+  END
+  v9.CheckSourceFailure(lines, 'E1041: Redefining script item: "ListOfNums"', 3)
+
+  # duplicate type alias name
+  lines =<< trim END
+    vim9script
+    type MyList = list<number>
+    type MyList = list<string>
+  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<object<C>>', typename(AC))
+  END
+  new
+  setline(1, lines)
+  :source
+  :source
+  bw!
+
+  # Assigning to a type alias (script level)
+  lines =<< trim END
+    vim9script
+    type MyType = list<number>
+    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<string>
+    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<number>
+    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<number>
+    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<number>
+    def Foo()
+      MyType = list<string>
+    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<number>
+    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<string>
+    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<string>
+    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<string>
+    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<string>
+    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<number>
+  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<string>
+  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<number>
+  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<number>
+    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<number>
+    def Foo(l: list<number>)
+      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<number>
+    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<func>
+    assert_equal(0, empty(A))
+  END
+  v9.CheckScriptSuccess(lines)
+
+  # Using a type alias with len()
+  lines =<< trim END
+    vim9script
+    type A = list<func>
+    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<func>
+    assert_equal(1, test_refcount(A))
+  END
+  v9.CheckScriptSuccess(lines)
+
+  lines =<< trim END
+    vim9script
+    type B = list<number>
+    var x: B = []
+    assert_equal(1, test_refcount(B))
+  END
   v9.CheckScriptSuccess(lines)
 enddef