patch 9.1.0850: Vim9: cannot access nested object inside objects
Problem: Vim9: cannot access nested object inside objects
(lifepillar, 91khr, mawkish)
Solution: Add support for accessing an object member using a "any"
variable type (Yegappan Lakshmanan)
fixes: #11822
fixes: #12024
fixes: #12196
fixes: #12198
closes: #16029
Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
index 8791a52..4ce9fcd 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -784,7 +784,7 @@
vim9script
class Inner
- var value: number = 0
+ public var value: number = 0
endclass
class Outer
@@ -11213,4 +11213,339 @@
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 containg 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)
+enddef
+
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker