view src/testdir/test_vim9_class.vim @ 33278:b5ed566262d3 v9.0.1906

patch 9.0.1906: Vim9: Interfaces should not support class methods and variables Commit: https://github.com/vim/vim/commit/92d9ee5f4ca0d2de04c39afbafc7609da43fb2e9 Author: Yegappan Lakshmanan <yegappan@yahoo.com> Date: Sun Sep 17 17:03:19 2023 +0200 patch 9.0.1906: Vim9: Interfaces should not support class methods and variables Problem: Vim9: Interfaces should not support class methods and variables Solution: Make sure interface follow the interface specification Vim9 interface changes to follow the new interface specification: 1) An interface can have only read-only and read-write instance variables. 2) An interface can have only public instance methods. 3) An interface cannot have class variables and class methods. 4) An interface cannot have private instance variables and private instance methods. 5) A interface can extend another interface using "extends". The sub-interface gets all the variables and methods in the super interface. That means: - Interfaces should not support class methods and variables. - Adjust error numbers and add additional tests. - Interface methods can be defined in one of the super classes. - Interface variables can be defined in one of the super classes. and instance variables can be repeated in sub interfaces. - Check the class variable types with the type in interface. closes: #13100 Signed-off-by: Christian Brabandt <cb@256bit.org> Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
author Christian Brabandt <cb@256bit.org>
date Sun, 17 Sep 2023 17:15:06 +0200
parents aba1fa2b7d1e
children 0c3553cfe22e
line wrap: on
line source

" Test Vim9 classes

source check.vim
import './vim9.vim' as v9

def Test_class_basic()
  var lines =<< trim END
      class NotWorking
      endclass
  END
  v9.CheckSourceFailure(lines, 'E1316:')

  lines =<< trim END
      vim9script
      class notWorking
      endclass
  END
  v9.CheckSourceFailure(lines, 'E1314:')

  lines =<< trim END
      vim9script
      class Not@working
      endclass
  END
  v9.CheckSourceFailure(lines, 'E1315:')

  lines =<< trim END
      vim9script
      abstract noclass Something
      endclass
  END
  v9.CheckSourceFailure(lines, 'E475:')

  lines =<< trim END
      vim9script
      abstract classy Something
      endclass
  END
  v9.CheckSourceFailure(lines, 'E475:')

  lines =<< trim END
      vim9script
      class Something
      endcl
  END
  v9.CheckSourceFailure(lines, 'E1065:')

  lines =<< trim END
      vim9script
      class Something
      endclass school's out
  END
  v9.CheckSourceFailure(lines, 'E488:')

  lines =<< trim END
      vim9script
      class Something
      endclass | echo 'done'
  END
  v9.CheckSourceFailure(lines, 'E488:')

  lines =<< trim END
      vim9script
      class Something
        this
      endclass
  END
  v9.CheckSourceFailure(lines, 'E1317:')

  lines =<< trim END
      vim9script
      class Something
        this.
      endclass
  END
  v9.CheckSourceFailure(lines, 'E1317:')

  lines =<< trim END
      vim9script
      class Something
        this .count
      endclass
  END
  v9.CheckSourceFailure(lines, 'E1317:')

  lines =<< trim END
      vim9script
      class Something
        this. count
      endclass
  END
  v9.CheckSourceFailure(lines, 'E1317:')

  lines =<< trim END
      vim9script
      class Something
        this.count: number
        that.count
      endclass
  END
  v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: that.count')

  lines =<< trim END
      vim9script
      class Something
        this.count
      endclass
  END
  v9.CheckSourceFailure(lines, 'E1022:')

  lines =<< trim END
      vim9script
      class Something
        def new()
          this.state = 0
        enddef
      endclass
      var obj = Something.new()
  END
  v9.CheckSourceFailure(lines, 'E1089:')

  lines =<< trim END
      vim9script
      class Something
        this.count : number
      endclass
  END
  v9.CheckSourceFailure(lines, 'E1059:')

  lines =<< trim END
      vim9script
      class Something
        this.count:number
      endclass
  END
  v9.CheckSourceFailure(lines, 'E1069:')

  # Test for unsupported comment specifier
  lines =<< trim END
    vim9script
    class Something
      # comment
      #{
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1170:')

  # Test for using class as a bool
  lines =<< trim END
    vim9script
    class A
    endclass
    if A
    endif
  END
  v9.CheckSourceFailure(lines, 'E1319: Using a class as a Number')

  # 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')

  # Test for using class as a float
  lines =<< trim END
    vim9script
    class A
    endclass
    sort([1.1, A], 'f')
  END
  v9.CheckSourceFailure(lines, 'E1321: Using a class as a Float')

  # 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')

  # Test for using class as a string
  lines =<< trim END
    vim9script
    class A
    endclass
    :exe 'call ' .. A
  END
  v9.CheckSourceFailure(lines, 'E1323: Using a class as a String')

  # 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')

  lines =<< trim END
      vim9script

      class TextPosition
        this.lnum: number
        this.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:')

  # 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:')

  # 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:')

  lines =<< trim END
  vim9script
  class A
    this.y = {
      X: 1
    }
  endclass
  var a = A.new()
  END
  v9.CheckSourceSuccess(lines)
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"')

  # one class, reload same script twice is OK
  lines =<< trim END
      vim9script
      class There
      endclass
  END
  writefile(lines, 'XclassTwice.vim', 'D')
  source XclassTwice.vim
  source XclassTwice.vim
enddef

def Test_returning_null_object()
  # this was causing an internal error
  var lines =<< trim END
      vim9script

      class BufferList
          def Current(): any
              return null_object
          enddef
      endclass

      var buffers = BufferList.new()
      echo buffers.Current()
  END
  v9.CheckSourceSuccess(lines)
enddef

def Test_using_null_class()
  var lines =<< trim END
      @_ = null_class.member
  END
  v9.CheckDefExecAndScriptFailure(lines, ['E715:', 'E1363:'])
enddef

def Test_class_interface_wrong_end()
  var lines =<< trim END
      vim9script
      abstract class SomeName
        this.member = 'text'
      endinterface
  END
  v9.CheckSourceFailure(lines, 'E476: Invalid command: endinterface, expected endclass')

  lines =<< trim END
      vim9script
      export interface AnotherName
        this.member: string
      endclass
  END
  v9.CheckSourceFailure(lines, 'E476: Invalid command: endclass, expected endinterface')
enddef

def Test_object_not_set()
  var lines =<< trim END
      vim9script

      class State
        this.value = 'xyz'
      endclass

      var state: State
      var db = {'xyz': 789}
      echo db[state.value]
  END
  v9.CheckSourceFailure(lines, 'E1360:')

  lines =<< trim END
      vim9script

      class Class
          this.id: string
          def Method1()
              echo 'Method1' .. this.id
          enddef
      endclass

      var obj: Class
      def Func()
          obj.Method1()
      enddef
      Func()
  END
  v9.CheckSourceFailure(lines, 'E1360:')

  lines =<< trim END
      vim9script

      class Background
        this.background = 'dark'
      endclass

      class Colorscheme
        this._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:')

  # TODO: this should not give an error but be handled at runtime
  lines =<< trim END
      vim9script

      class Class
          this.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:')
enddef

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

def Test_class_member_initializer()
  var lines =<< trim END
      vim9script

      class TextPosition
        this.lnum: number = 1
        this.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
        this.value: number = 0
      endclass

      class Outer
        this.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)

  lines =<< trim END
      vim9script

      class Inner
        this.value: number = 0
      endclass

      class Outer
        this.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.CheckSourceSuccess(lines)

  # Try modifying a private variable using an "any" object
  lines =<< trim END
    vim9script

    class Inner
      this._value: string = ''
    endclass

    class Outer
      this.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 private member: _value')

  # Try modifying a non-existing variable using an "any" object
  lines =<< trim END
    vim9script

    class Inner
      this.value: string = ''
    endclass

    class Outer
      this.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: Member not found on object "Inner": someval')
enddef

def Test_assignment_with_operator()
  var lines =<< trim END
      vim9script

      class Foo
        public this.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)

  # do the same thing, but through an interface
  lines =<< trim END
      vim9script

      interface I
        public this.x: number
      endinterface

      class Foo implements I
        public this.x: number

        def Add(n: number)
          var i: I = this
          i.x += n
        enddef
      endclass

      var f =  Foo.new(3)
      f.Add(17)
      assert_equal(20, f.x)

      def AddToFoo(i: I)
        i.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
        this.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
        this.lnum: number = 1
        this.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
        this.name: string
        this.age: number = 42
        this.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
        this.name: string
        this.age: number = 42
        this.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:')

  # Using a specific value to initialize an instance variable in the new()
  # method.
  lines =<< trim END
      vim9script
      class A
        this.val: string
        def new(this.val = 'a')
        enddef
      endclass
  END
  v9.CheckSourceFailure(lines, "E1328: Constructor default value must be v:none:  = 'a'")
enddef


def Test_class_new_with_object_member()
  var lines =<< trim END
      vim9script

      class C
        this.str: string
        this.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
        this.str: string
        this.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:')

  lines =<< trim END
      vim9script

      class C
        this.str: string
        this.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:')
enddef

def Test_class_object_member_inits()
  var lines =<< trim END
      vim9script
      class TextPosition
        this.lnum: number
        this.col = 1
        this.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
        this.lnum
        this.col = 1
      endclass
  END
  v9.CheckSourceFailure(lines, 'E1022:')

  # 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
        this.str1 = Init()
        this.str2: string = Init()
        this.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
       this.value = init_val
    endclass
    var a = A.new()
  END
  v9.CheckSourceFailure(lines, 'E1001:')

  # Test for initializing an object member with an special type
  lines =<< trim END
    vim9script
    class A
       this.value: void
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1330: Invalid type for object member: void')
enddef

" Test for instance variable access
def Test_instance_variable_access()
  var lines =<< trim END
      vim9script
      class Triple
         this._one = 1
         this.two = 2
         public this.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')

      assert_fails('trip._one = 11', 'E1333')
      assert_fails('trip.two = 22', 'E1335')
      trip.three = 33
      assert_equal(33, trip.three)

      assert_fails('trip.four = 4', 'E1326')
  END
  v9.CheckSourceSuccess(lines)

  # Test for a public member variable name beginning with an underscore
  lines =<< trim END
    vim9script
    class A
      public this._val = 10
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1332:')

  lines =<< trim END
      vim9script

      class MyCar
        this.make: string
        this.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
        this.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:')

  lines =<< trim END
      vim9script

      class Foo
        this.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 this.val = 1
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1065:')

  # Test for "public" keyword must be followed by "this" or "static".
  lines =<< trim END
    vim9script
    class Something
      public val = 1
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1331:')

  # Modify a instance variable using the class name in the script context
  lines =<< trim END
    vim9script
    class A
      public this.val = 1
    endclass
    A.val = 1
  END
  v9.CheckSourceFailure(lines, 'E1376: Object member "val" accessible only using class "A" object')

  # Read a instance variable using the class name in the script context
  lines =<< trim END
    vim9script
    class A
      public this.val = 1
    endclass
    var i = A.val
  END
  v9.CheckSourceFailure(lines, 'E1376: Object member "val" accessible only using class "A" object')

  # Modify a instance variable using the class name in a def function
  lines =<< trim END
    vim9script
    class A
      public this.val = 1
    endclass
    def T()
      A.val = 1
    enddef
    T()
  END
  v9.CheckSourceFailure(lines, 'E1376: Object member "val" accessible only using class "A" object')

  # Read a instance variable using the class name in a def function
  lines =<< trim END
    vim9script
    class A
      public this.val = 1
    endclass
    def T()
      var i = A.val
    enddef
    T()
  END
  v9.CheckSourceFailure(lines, 'E1376: Object member "val" accessible only using class "A" object')

  # Access from child class extending a class:
  lines =<< trim END
      vim9script
      class A
        this.ro_obj_var = 10
        public this.rw_obj_var = 20
        this._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 this.val = 1
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1065:')

  # Test for "static" cannot be followed by "this".
  lines =<< trim END
    vim9script
    class Something
      static this.val = 1
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1368: Static cannot be followed by "this" in a member name')

  # Test for "static" cannot be followed by "public".
  lines =<< trim END
    vim9script
    class Something
      static public val = 1
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1022: Type or initialization required')

  # A readonly class variable cannot be modified from a child class
  lines =<< trim END
      vim9script
      class A
        static 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, 'E46: Cannot change read-only variable "ro_class_var"')

  # A private class variable cannot be accessed from a child class
  lines =<< trim END
      vim9script
      class A
        static _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 private member: _priv_class_var')

  # A private class variable cannot be modified from a child class
  lines =<< trim END
      vim9script
      class A
        static _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 private member: _priv_class_var')

  # Access from child class extending a class and from script context
  lines =<< trim END
      vim9script
      class A
        static ro_class_var = 10
        public static rw_class_var = 20
        static _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
        this.nr = 0
        this.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')
    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
        this.one = 1
      endclass
      class Two
        this.two = 2
      endclass
      class TwoMore extends Two
        this.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
        this.one = 1
      endclass
      class Two
        this.two = 2
      endclass

      var o: One = Two.new()
  END
  v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected object<One> but got object<Two>')

  lines =<< trim END
      vim9script

      interface One
        def GetMember(): number
      endinterface
      class Two implements One
        this.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
        this.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
         this.lnum = 1
         this.col = 1
         static counter = 0
         static _secret = 7
         public static  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:')

      def GetCounter(): number
        return TextPos.counter
      enddef
      assert_equal(3, GetCounter())

      assert_fails('TextPos.noSuchMember = 2', 'E1337:')
      assert_fails('TextPos.counter = 5', 'E1335:')
      assert_fails('TextPos.counter += 5', 'E1335:')

      assert_fails('echo TextPos._secret', 'E1333:')
      assert_fails('TextPos._secret = 8', 'E1333:')

      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
	   this.size: number
	   static 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 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 private member in lambda
  lines =<< trim END
      vim9script

      class Foo
        this._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 private member in lambda body
  lines =<< trim END
      vim9script

      class Foo
        this._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 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')

  lines =<< trim END
      vim9script

      class Some
        static 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')

  # Test for using an invalid type for a member variable
  lines =<< trim END
    vim9script
    class A
      this.val: xxx
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1010:')

  # Test for setting a member on a null object
  lines =<< trim END
    vim9script
    class A
        public this.val: string
    endclass

    def F()
        var obj: A
        obj.val = ""
    enddef
    F()
  END
  v9.CheckSourceFailure(lines, 'E1360: Using a null object')

  # Test for accessing a member on a null object
  lines =<< trim END
    vim9script
    class A
        this.val: string
    endclass

    def F()
        var obj: A
        echo obj.val
    enddef
    F()
  END
  v9.CheckSourceFailure(lines, 'E1360: Using a null object')

  # Test for setting a member on a null object, at script level
  lines =<< trim END
    vim9script
    class A
        public this.val: string
    endclass

    var obj: A
    obj.val = ""
  END
  # FIXME(in source): this should give E1360 as well!
  v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected object<A> but got string')

  # Test for accessing a member on a null object, at script level
  lines =<< trim END
    vim9script
    class A
        this.val: string
    endclass

    var obj: A
    echo obj.val
  END
  v9.CheckSourceFailure(lines, 'E1360: Using a null object')

  # Test for no space before or after the '=' when initializing a member
  # variable
  lines =<< trim END
    vim9script
    class A
      this.val: number= 10
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1004:')
  lines =<< trim END
    vim9script
    class A
      this.val: number =10
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1004:')

  # 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: Member not found on object "A": bar')
enddef

func Test_class_garbagecollect()
  let lines =<< trim END
      vim9script

      class Point
        this.p = [2, 3]
        static pl = ['a', 'b']
        static 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
        this.view: View
      endclass

      class MyView implements View
        this.widget: Widget

        def new()
          # this will result in a circular reference to this object
          this.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
      this.ro_obj_var: number
      public this.rw_obj_var: number

      def ObjFoo(): number
    endinterface

    class A implements I
      static ro_class_var: number = 10
      public static rw_class_var: number = 20
      static _priv_class_var: number = 30
      this.ro_obj_var: number = 40
      public this.rw_obj_var: number = 50
      this._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.rw_obj_var + this._ObjBar()
      enddef
    endclass

    assert_equal(60, A.ClassFoo())
    var o = A.new()
    assert_equal(150, o.ObjFoo())
    test_garbagecollect_now()
    assert_equal(60, A.ClassFoo())
    assert_equal(150, o.ObjFoo())
  END
  call v9.CheckSourceSuccess(lines)
endfunc

def Test_class_function()
  var lines =<< trim END
      vim9script
      class Value
        this.value = 0
        static 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:')
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')

  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')

  lines =<< trim END
      vim9script

      class C
          static def new()
          enddef
      endclass

      defcompile C.new
  END
  v9.CheckSourceFailure(lines, 'E1370: Cannot define a "new" function as static')

  # Trying to compile a function using a non-existing class variable
  lines =<< trim END
    vim9script
    defcompile x.Foo()
  END
  v9.CheckSourceFailure(lines, 'E475:')

  # 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:')

  # Trying to compile a function without specifying the name
  lines =<< trim END
    vim9script
    class A
    endclass
    defcompile A.
  END
  v9.CheckSourceFailure(lines, 'E475:')

  # 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:', 'E475:'])
enddef

def Test_class_object_to_string()
  var lines =<< trim END
      vim9script
      class TextPosition
        this.lnum = 1
        this.col = 22
      endclass

      assert_equal("class TextPosition", string(TextPosition))

      var pos = TextPosition.new()
      assert_equal("object of TextPosition {lnum: 1, col: 22}", string(pos))
  END
  v9.CheckSourceSuccess(lines)
enddef

def Test_interface_basics()
  var lines =<< trim END
      vim9script
      interface Something
        this.ro_var: string
        public this.rw_var: list<number>
        def GetCount(): number
      endinterface
  END
  v9.CheckSourceSuccess(lines)

  lines =<< trim END
      interface SomethingWrong
        static count = 7
      endinterface
  END
  v9.CheckSourceFailure(lines, 'E1342:')

  lines =<< trim END
      vim9script

      interface Some
        this.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 count = 7
      endinterface
  END
  v9.CheckSourceFailure(lines, 'E1343: Interface name must start with an uppercase letter: somethingWrong')

  lines =<< trim END
      vim9script
      interface SomethingWrong
        this.value: string
        this.count = 7
        def GetCount(): number
      endinterface
  END
  v9.CheckSourceFailure(lines, 'E1344:')

  lines =<< trim END
      vim9script
      interface SomethingWrong
        this.value: string
        this.count: number
        def GetCount(): number
          return 5
        enddef
      endinterface
  END
  v9.CheckSourceFailure(lines, 'E1345: Not a valid command in an interface: return 5')

  lines =<< trim END
      vim9script
      export interface EnterExit
          def Enter(): void
          def Exit(): void
      endinterface
  END
  writefile(lines, 'XdefIntf.vim', 'D')

  lines =<< trim END
      vim9script
      import './XdefIntf.vim' as defIntf
      export def With(ee: defIntf.EnterExit, F: func)
          ee.Enter()
          try
              F()
          finally
              ee.Exit()
          endtry
      enddef
  END
  v9.CheckScriptSuccess(lines)

  var imported =<< trim END
      vim9script
      export abstract class EnterExit
          def Enter(): void
          enddef
          def Exit(): void
          enddef
      endclass
  END
  writefile(imported, 'XdefIntf2.vim', 'D')

  lines[1] = " import './XdefIntf2.vim' as defIntf"
  v9.CheckScriptSuccess(lines)
enddef

def Test_class_implements_interface()
  var lines =<< trim END
      vim9script

      interface Some
        this.count: number
        def Method(nr: number)
      endinterface

      class SomeImpl implements Some
        this.count: number
        def Method(nr: number)
          echo nr
        enddef
      endclass

      interface Another
        this.member: string
      endinterface

      class AnotherImpl implements Some, Another
        this.member = 'abc'
        this.count = 20
        def Method(nr: number)
          echo nr
        enddef
      endclass
  END
  v9.CheckSourceSuccess(lines)

  lines =<< trim END
      vim9script

      interface Some
        this.count: number
      endinterface

      class SomeImpl implements Some implements Some
        this.count: number
      endclass
  END
  v9.CheckSourceFailure(lines, 'E1350:')

  lines =<< trim END
      vim9script

      interface Some
        this.count: number
      endinterface

      class SomeImpl implements Some, Some
        this.count: number
      endclass
  END
  v9.CheckSourceFailure(lines, 'E1351: Duplicate interface after "implements": Some')

  lines =<< trim END
      vim9script

      interface Some
        this.counter: number
        def Method(nr: number)
      endinterface

      class SomeImpl implements Some
        this.count: number
        def Method(nr: number)
          echo nr
        enddef
      endclass
  END
  v9.CheckSourceFailure(lines, 'E1348: Member "counter" of interface "Some" is not implemented')

  lines =<< trim END
      vim9script

      interface Some
        this.count: number
        def Methods(nr: number)
      endinterface

      class SomeImpl implements Some
        this.count: number
        def Method(nr: number)
          echo nr
        enddef
      endclass
  END
  v9.CheckSourceFailure(lines, 'E1349: Method "Methods" of interface "Some" is not implemented')

  # Check different order of members in class and interface works.
  lines =<< trim END
      vim9script

      interface Result
        public this.label: string
        this.errpos: number
      endinterface

      # order of members is opposite of interface
      class Failure implements Result
        this.errpos: number = 42
        public this.label: string = 'label'
      endclass

      def Test()
        var result: Result = Failure.new()

        assert_equal('label', result.label)
        assert_equal(42, result.errpos)

        result.label = 'different'
        assert_equal('different', 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:')

  # Trailing characters after a class name
  lines =<< trim END
    vim9script
    class A bbb
    endclass
  END
  v9.CheckSourceFailure(lines, 'E488:')

  # using "implements" with a non-existing class
  lines =<< trim END
    vim9script
    class A implements B
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1346:')

  # using "implements" with a regular class
  lines =<< trim END
    vim9script
    class A
    endclass
    class B implements A
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1347:')

  # using "implements" with a variable
  lines =<< trim END
    vim9script
    var T: number = 10
    class A implements T
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1347:')

  # 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:')

  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')

  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')

  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')

  # access superclass interface members from subclass, mix variable order
  lines =<< trim END
    vim9script

    interface I1
        public this.mvar1: number
        public this.mvar2: number
    endinterface

    # NOTE: the order is swapped
    class A implements I1
        public this.mvar2: number
        public this.mvar1: number
        public static svar2: number
        public static 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
        public this.mvar1: number
        public this.mvar2: number
    endinterface

    interface I2
        public this.mvar3: number
        public this.mvar4: number
    endinterface

    class A implements I1
        public static svar1: number
        public static svar2: number
        public this.mvar1: number
        public this.mvar2: number
        def new()
            svar1 = 11
            svar2 = 12
            this.mvar1 = 111
            this.mvar2 = 112
        enddef
    endclass

    class B extends A implements I2
        public static svar3: number
        public static svar4: number
        public this.mvar3: number
        public this.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 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)
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
        this.x = 0
        this.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
        this.x: number
      endinterface

      class Point implements HasX
        this.x = 0
        this.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
        this.x = 0
        this.y = 0
      endclass

      var p: Point
      p = 'text'
  END
  v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected object<Point> but got string')
enddef

def Test_class_extends()
  var lines =<< trim END
      vim9script
      class Base
        this.one = 1
        def GetOne(): number
          return this.one
        enddef
      endclass
      class Child extends Base
        this.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
        this.one = 1
      endclass
      class Child extends Base
        this.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
        this.one = 1
      endclass
      class Child extends Base extends Base
        this.two = 2
      endclass
  END
  v9.CheckSourceFailure(lines, 'E1352: Duplicate "extends"')

  lines =<< trim END
      vim9script
      class Child extends BaseClass
        this.two = 2
      endclass
  END
  v9.CheckSourceFailure(lines, 'E1353: Class name not found: BaseClass')

  lines =<< trim END
      vim9script
      var SomeVar = 99
      class Child extends SomeVar
        this.two = 2
      endclass
  END
  v9.CheckSourceFailure(lines, 'E1354: Cannot extend SomeVar')

  lines =<< trim END
      vim9script
      class Base
        this.name: string
        def ToString(): string
          return this.name
        enddef
      endclass

      class Child extends Base
        this.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
        this.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')

  lines =<< trim END
      vim9script
      class Child
        this.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:')

  lines =<< trim END
      vim9script
      class Base
        this.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:')

  lines =<< trim END
      vim9script
      class Child
        this.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:')

  lines =<< trim END
      vim9script
      class Base
        this.name: string
        static def ToString(): string
          return 'Base class'
        enddef
      endclass

      class Child extends Base
        this.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
        this.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, 'E1375: Class member "new" accessible only using class "Child"')

  # base class with more than one object member
  lines =<< trim END
      vim9script

      class Result
        this.success: bool
        this.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:')
enddef

def Test_using_base_class()
  var lines =<< trim END
    vim9script

    class BaseEE
        def Enter(): any
            return null
        enddef
        def Exit(resource: any): void
        enddef
    endclass

    class ChildEE extends BaseEE
        def Enter(): any
            return 42
        enddef

        def Exit(resource: number): void
            g:result ..= '/exit'
        enddef
    endclass

    def With(ee: BaseEE)
        var r = ee.Enter()
        try
            g:result ..= r
        finally
            g:result ..= '/finally'
            ee.Exit(r)
        endtry
    enddef

    g:result = ''
    With(ChildEE.new())
    assert_equal('42/finally/exit', g:result)
  END
  v9.CheckSourceSuccess(lines)
  unlet g:result

  # Using super, Child invokes Base method which has optional arg. #12471
  lines =<< trim END
    vim9script

    class Base
        this.success: bool = false
        def Method(arg = 0)
            this.success = true
        enddef
    endclass

    class Child extends Base
        def new()
            super.Method()
        enddef
    endclass

    var obj = Child.new()
    assert_equal(true, obj.success)
  END
  v9.CheckSourceSuccess(lines)
enddef


def Test_class_import()
  var lines =<< trim END
      vim9script
      export class Animal
        this.kind: string
        this.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

def Test_abstract_class()
  var lines =<< trim END
      vim9script
      abstract class Base
        this.name: string
      endclass
      class Person extends Base
        this.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
        this.name: string
      endclass
      class Person extends Base
        this.age: number
      endclass
      var p = Base.new('Peter')
  END
  v9.CheckSourceFailure(lines, 'E1325: Method not found on class "Base": new')

  lines =<< trim END
      abstract class Base
        this.name: string
      endclass
  END
  v9.CheckSourceFailure(lines, 'E1316:')

  # Abstract class cannot have a "new" function
  lines =<< trim END
    vim9script
    abstract class Base
      def new()
      enddef
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1359:')
enddef

def Test_closure_in_class()
  var lines =<< trim END
      vim9script

      class Foo
        this.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_call_constructor_from_legacy()
  var lines =<< trim END
      vim9script

      var newCalled = 'false'

      class A
        def new()
          newCalled = 'true'
        enddef
      endclass

      export def F(options = {}): any
        return A
      enddef

      g:p = F()
      legacy call p.new()
      assert_equal('true', newCalled)
  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
      this.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
      this.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

    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_false(instanceof(b3, []))
    assert_true(instanceof(b3, [Base1, Base2, Intf1]))

    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_false(instanceof(a3, []))
      assert_true(instanceof(a3, [Base1, Base2, Intf1]))
    enddef
    Foo()
  END
  v9.CheckSourceSuccess(lines)
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
      this._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 this.val1: number = 0
    endclass

    class B extends A
      public this.val2: number = 0
    endclass

    class C extends B
      public this.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\]\.\.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
      this._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
      this._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
      this._bufnr: number

      def new(this._bufnr): any
        if !bufexists(this._bufnr)
          this._bufnr = -1
          return this
        endif
      enddef
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1365:')

  # new() uses 'Dict' return type and returns a Dict
  lines =<< trim END
    vim9script

    class C
      this._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:')
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
        this._foo: bool = F()
    endclass

    var c1 = C.new()
    var c2 = C.new()
  END
  v9.CheckSourceFailure(lines, 'E1012:')
enddef

" Test for locking a variable referring to an object and reassigning to another
" object.
def Test_object_lockvar()
  var lines =<< trim END
    vim9script

    class C
      this.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 for a private object method
def Test_private_object_method()
  # Try calling a private 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 private method: _Foo()')

  # Try calling a private 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 private method: _Foo()')

  # Use a private 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 private 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 private 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')

  # Try calling a private 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 private method: _Foo')

  # Try to use "public" keyword when defining a private method
  lines =<< trim END
    vim9script

    class A
      public def _Foo()
      enddef
    endclass
    var a = A.new()
    a._Foo()
  END
  v9.CheckSourceFailure(lines, 'E1331: Public must be followed by "this" or "static"')

  # Define two private 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')

  # Define a private 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')

  # Define an object method and a private 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')

  # Call a public method and a private method from a private 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 private 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 private method: _Foo()')

  # Call a private 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 private 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 private method: _Foo()')

  # 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')
enddef

" Test for an private class method
def Test_private_class_method()
  # Try calling a class private 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 private method: _Foo()')

  # Try calling a class private 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 private method: _Foo()')

  # Try calling a class private 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 private method: _Foo')

  # Try calling a class private 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 private method: _Foo')

  # Use a class private method from an object method
  lines =<< trim END
    vim9script

    class A
      static def _Foo(): number
        return 1234
      enddef
      def Bar()
        assert_equal(1234, A._Foo())
      enddef
    endclass
    var a = A.new()
    a.Bar()
  END
  v9.CheckSourceSuccess(lines)

  # Use a class private method from another class private method
  lines =<< trim END
    vim9script

    class A
      static def _Foo1(): number
        return 1234
      enddef
      static def _Foo2()
        assert_equal(1234, A._Foo1())
      enddef
      def Bar()
        A._Foo2()
      enddef
    endclass
    var a = A.new()
    a.Bar()
  END
  v9.CheckSourceSuccess(lines)

  # Declare a class method and a class private 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')

  # Try calling a class private 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 private method: _Foo()')

  # Call a private 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 private method: _Foo()')

  # Call a private class method from a child class private 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 private method: _Foo()')

  # Call a private 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 not found on class "C": _Foo')
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 _svar: number
        this._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 private member: _svar')

    # 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
      this.val = 10
      this.val = 20
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1369: Duplicate member: val')

  # Duplicate private member variable
  lines =<< trim END
    vim9script
    class C
      this._val = 10
      this._val = 20
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1369: Duplicate member: _val')

  # Duplicate public member variable
  lines =<< trim END
    vim9script
    class C
      public this.val = 10
      public this.val = 20
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1369: Duplicate member: val')

  # Duplicate private member variable
  lines =<< trim END
    vim9script
    class C
      this.val = 10
      this._val = 20
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1369: Duplicate member: _val')

  # Duplicate public and private member variable
  lines =<< trim END
    vim9script
    class C
      this._val = 20
      public this.val = 10
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1369: Duplicate member: val')

  # Duplicate class member variable
  lines =<< trim END
    vim9script
    class C
      static s: string = "abc"
      static _s: string = "def"
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1369: Duplicate member: _s')

  # Duplicate public and private class member variable
  lines =<< trim END
    vim9script
    class C
      public static s: string = "abc"
      static _s: string = "def"
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1369: Duplicate member: _s')

  # Duplicate class and object member variable
  lines =<< trim END
    vim9script
    class C
      static val = 10
      this.val = 20
      def new()
      enddef
    endclass
    var c = C.new()
    assert_equal(10, C.val)
    assert_equal(20, c.val)
  END
  v9.CheckSourceSuccess(lines)

  # Duplicate object member variable in a derived class
  lines =<< trim END
    vim9script
    class A
      this.val = 10
    endclass
    class B extends A
    endclass
    class C extends B
      this.val = 20
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1369: Duplicate member: val')

  # Duplicate object private member variable in a derived class
  lines =<< trim END
    vim9script
    class A
      this._val = 10
    endclass
    class B extends A
    endclass
    class C extends B
      this._val = 20
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1369: Duplicate member: _val')

  # Duplicate object private member variable in a derived class
  lines =<< trim END
    vim9script
    class A
      this.val = 10
    endclass
    class B extends A
    endclass
    class C extends B
      this._val = 20
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1369: Duplicate member: _val')

  # Duplicate object member variable in a derived class
  lines =<< trim END
    vim9script
    class A
      this._val = 10
    endclass
    class B extends A
    endclass
    class C extends B
      this.val = 20
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1369: Duplicate member: val')

  # Two member variables with a common prefix
  lines =<< trim END
    vim9script
    class A
      public static svar2: number
      public static svar: number
    endclass
  END
  v9.CheckSourceSuccess(lines)
enddef

" Test for accessing a private member outside a class in a def function
def Test_private_member_access_outside_class()
  # private object member variable
  var lines =<< trim END
    vim9script
    class A
      this._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 private member: _val')

  # access a non-existing private object member variable
  lines =<< trim END
    vim9script
    class A
      this._val = 10
    endclass
    def T()
      var a = A.new()
      a._a = 1
    enddef
    T()
  END
  v9.CheckSourceFailure(lines, 'E1089: Unknown variable: _a')

  # private static member variable
  lines =<< trim END
    vim9script
    class A
      static _val = 10
    endclass
    def T()
      var a = A.new()
      var x = a._val
    enddef
    T()
  END
  v9.CheckSourceFailure(lines, 'E1375: Class member "_val" accessible only using class "A"')

  # private static member variable
  lines =<< trim END
    vim9script
    class A
      static _val = 10
    endclass
    def T()
      var a = A.new()
      a._val = 3
    enddef
    T()
  END
  v9.CheckSourceFailure(lines, 'E1374: Class member "_val" accessible only inside class "A"')

  # private static class variable
  lines =<< trim END
    vim9script
    class A
      static _val = 10
    endclass
    def T()
      var x = A._val
    enddef
    T()
  END
  v9.CheckSourceFailure(lines, 'E1333: Cannot access private member: _val')

  # private static class variable
  lines =<< trim END
    vim9script
    class A
      static _val = 10
    endclass
    def T()
      A._val = 3
    enddef
    T()
  END
  v9.CheckSourceFailure(lines, 'E1333: Cannot access private member: _val')
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
      public this.val: number
    endinterface
    class B implements A
      this.val = 10
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1367: Access level of member "val" of interface "A" is different')

  lines =<< trim END
    vim9script
    interface A
      this.val: number
    endinterface
    class B implements A
      public this.val = 10
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1367: Access level of member "val" of interface "A" is different')
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
      this.val: number
    endclass
    def T()
      var a = A.new()
      a.val = 20
    enddef
    T()
  END
  v9.CheckSourceFailure(lines, 'E46: Cannot change read-only variable "val"')
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
      this.var1: number = 10
      public static var2: list<number> = [1, 2]
      public static var3: dict<number> = {a: 1, b: 2}
      static _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 private member: _priv_var4')
    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 svar1: list<number> = [1]
      public static 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 this.var1: number
      public static svar2: list<number> = [1]
    endclass

    var a = A.new()
    echo a.svar2
  END
  v9.CheckSourceFailure(lines, 'E1375: Class member "svar2" accessible only using class "A"')

  # Cannot write to a class variable using an object in script context
  lines =<< trim END
    vim9script
    class A
      public this.var1: number
      public static svar2: list<number> = [1]
    endclass

    var a = A.new()
    a.svar2 = [2]
  END
  v9.CheckSourceFailure(lines, 'E1375: Class member "svar2" accessible only using class "A"')

  # Cannot read from a class variable using an object in def method context
  lines =<< trim END
    vim9script
    class A
      public this.var1: number
      public static svar2: list<number> = [1]
    endclass

    def T()
      var a = A.new()
      echo a.svar2
    enddef
    T()
  END
  v9.CheckSourceFailure(lines, 'E1375: Class member "svar2" accessible only using class "A"')

  # Cannot write to a class variable using an object in def method context
  lines =<< trim END
    vim9script
    class A
      public this.var1: number
      public static svar2: list<number> = [1]
    endclass

    def T()
      var a = A.new()
      a.svar2 = [2]
    enddef
    T()
  END
  v9.CheckSourceFailure(lines, 'E1374: Class member "svar2" accessible only inside class "A"')
enddef

" Test for using a interface method using a child object
def Test_interface_method_from_child()
  var lines =<< trim END
    vim9script

    interface A
      def Foo(): string
    endinterface

    class B implements A
      def Foo(): string
        return 'foo'
      enddef
    endclass

    class C extends B
      def Bar(): string
        return 'bar'
      enddef
    endclass

    def T1(a: A)
      assert_equal('foo', a.Foo())
    enddef

    def T2(b: B)
      assert_equal('foo', b.Foo())
    enddef

    var c = C.new()
    T1(c)
    T2(c)
  END
  v9.CheckSourceSuccess(lines)
enddef

" Test for using an interface method using a child object when it is overridden
" by the child class.
" FIXME: This test fails.
" def Test_interface_overridden_method_from_child()
"   var lines =<< trim END
"     vim9script
"
"     interface A
"       def Foo(): string
"     endinterface
"
"     class B implements A
"       def Foo(): string
"         return 'b-foo'
"       enddef
"     endclass
"
"     class C extends B
"       def Bar(): string
"         return 'bar'
"       enddef
"       def Foo(): string
"         return 'c-foo'
"       enddef
"     endclass
"
"     def T1(a: A)
"       assert_equal('c-foo', a.Foo())
"     enddef
"
"     def T2(b: B)
"       assert_equal('c-foo', b.Foo())
"     enddef
"
"     var c = C.new()
"     T1(c)
"     T2(c)
"   END
"   v9.CheckSourceSuccess(lines)
" enddef

" Test for abstract methods
def Test_abstract_method()
  # Use two abstract methods
  var lines =<< trim END
    vim9script
    abstract class A
      def M1(): number
        return 10
      enddef
      abstract def M2(): number
      abstract def M3(): number
    endclass
    class B extends A
      def M2(): number
        return 20
      enddef
      def M3(): number
        return 30
      enddef
    endclass
    var b = B.new()
    assert_equal([10, 20, 30], [b.M1(), b.M2(), b.M3()])
  END
  v9.CheckSourceSuccess(lines)

  # Don't define an abstract method
  lines =<< trim END
    vim9script
    abstract class A
      abstract def Foo()
    endclass
    class B extends A
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1373: Abstract method "Foo" is not implemented')

  # 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')

  # 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.CheckSourceSuccess(lines)

  # 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()')

  # 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" or "static"')

  # Use a static abstract method
  lines =<< trim END
    vim9script
    abstract class A
      abstract static def Foo(): number
    endclass
    class B extends A
      static def Foo(): number
        return 4
      enddef
    endclass
    assert_equal(4, B.Foo())
  END
  v9.CheckSourceSuccess(lines)

  # 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>')

  # Use an abstract class to invoke an abstract method
  # FIXME: This should fail
  lines =<< trim END
    vim9script
    abstract class A
      abstract static def Foo()
    endclass
    A.Foo()
  END
  v9.CheckSourceSuccess(lines)

  # 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)
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, 'E1374: Class member "Foo" accessible only inside class "A"')
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, 'E1375: Class member "Foo" accessible only using class "A"')

  # 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, 'E1375: Class member "Foo" accessible only using class "A"')
enddef

def Test_class_variable()
  var lines =<< trim END
    vim9script

    class A
      public static 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 val: number = 10
    endclass

    class B extends A
      static def ClassFunc()
        val = 20
      enddef
    endclass
    B.ClassFunc()
  END
  v9.CheckSourceFailure(lines, 'E1374: Class member "val" accessible only inside class "A"')

  # Reading a parent class variable from a child class method
  lines =<< trim END
    vim9script

    class A
      static val: number = 10
    endclass

    class B extends A
      static def ClassFunc()
        var i = val
      enddef
    endclass
    B.ClassFunc()
  END
  v9.CheckSourceFailure(lines, 'E1374: Class member "val" accessible only inside class "A"')

  # Modifying a parent class variable from a child object method
  lines =<< trim END
    vim9script

    class A
      static 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 member "val" accessible only inside class "A"')

  # Reading a parent class variable from a child object method
  lines =<< trim END
    vim9script

    class A
      static 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 member "val" accessible only inside class "A"')

  # Modifying a class variable using an object at script level
  lines =<< trim END
    vim9script

    class A
      static val: number = 10
    endclass
    var a = A.new()
    a.val = 20
  END
  v9.CheckSourceFailure(lines, 'E1375: Class member "val" accessible only using class "A"')

  # Reading a class variable using an object at script level
  lines =<< trim END
    vim9script

    class A
      static val: number = 10
    endclass
    var a = A.new()
    var i = a.val
  END
  v9.CheckSourceFailure(lines, 'E1375: Class member "val" accessible only using class "A"')

  # Modifying a class variable using an object at function level
  lines =<< trim END
    vim9script

    class A
      static val: number = 10
    endclass

    def T()
      var a = A.new()
      a.val = 20
    enddef
    T()
  END
  v9.CheckSourceFailure(lines, 'E1374: Class member "val" accessible only inside class "A"')

  # Reading a class variable using an object at function level
  lines =<< trim END
    vim9script

    class A
      static val: number = 10
    endclass
    def T()
      var a = A.new()
      var i = a.val
    enddef
    T()
  END
  v9.CheckSourceFailure(lines, 'E1375: Class member "val" accessible only using class "A"')
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 sval = 100
      static def Check()
        assert_equal(100, sval)
      enddef
      def GetVal(): number
        return sval
      enddef
    endclass

    class B extends A
      static 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 sval = 100
      static def Check()
        assert_equal(100, sval)
      enddef
      def GetVal(): number
        return sval
      enddef
    endclass

    class B extends A
      static 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, 'E1376: Object member "Foo" accessible only using class "A" object')

  # 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, 'E1376: Object member "Foo" accessible only using class "A" object')
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')

  # Duplicate private instance method
  lines =<< trim END
    vim9script
    class A
      static def Foo()
      enddef
      def _Foo()
      enddef
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo')

  # 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')

  # Duplicate private class method
  lines =<< trim END
    vim9script
    class A
      def Foo()
      enddef
      static def _Foo()
      enddef
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo')

  # Duplicate private 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')
enddef

" Test for an instance method access level comparison with parent instance
" methods.
def Test_instance_method_access_level()
  # Private 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"')

  # 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"')
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 rw_class_var = 1
      public this.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 private method
def Test_interface_with_unsupported_members()
  var lines =<< trim END
    vim9script
    interface A
      static num: number
    endinterface
  END
  v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface')

  lines =<< trim END
    vim9script
    interface A
      static _num: number
    endinterface
  END
  v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface')

  lines =<< trim END
    vim9script
    interface A
      public static num: number
    endinterface
  END
  v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface')

  lines =<< trim END
    vim9script
    interface A
      public static _num: number
    endinterface
  END
  v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface')

  lines =<< trim END
    vim9script
    interface A
      static def Foo(d: dict<any>): list<string>
    endinterface
  END
  v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface')

  lines =<< trim END
    vim9script
    interface A
      static def _Foo(d: dict<any>): list<string>
    endinterface
  END
  v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface')

  lines =<< trim END
    vim9script
    interface A
      this._Foo: list<string>
    endinterface
  END
  v9.CheckSourceFailure(lines, 'E1379: Private variable not supported in an interface')

  lines =<< trim END
    vim9script
    interface A
      def _Foo(d: dict<any>): list<string>
    endinterface
  END
  v9.CheckSourceFailure(lines, 'E1380: Private method not supported in an interface')
enddef

" Test for extending an interface
def Test_extend_interface()
  var lines =<< trim END
    vim9script
    interface A
      this.var1: list<string>
      def Foo()
    endinterface
    interface B extends A
      public this.var2: dict<string>
      def Bar()
    endinterface
    class C implements A, B
      this.var1 = [1, 2]
      def Foo()
      enddef
      public this.var2 = {a: '1'}
      def Bar()
      enddef
    endclass
  END
  v9.CheckSourceSuccess(lines)

  lines =<< trim END
    vim9script
    interface A
      def Foo()
    endinterface
    interface B extends A
      public this.var2: dict<string>
    endinterface
    class C implements A, B
      public this.var2 = {a: '1'}
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1349: Method "Foo" of interface "A" is not implemented')

  lines =<< trim END
    vim9script
    interface A
      def Foo()
    endinterface
    interface B extends A
      public this.var2: dict<string>
    endinterface
    class C implements A, B
      def Foo()
      enddef
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1348: Member "var2" of interface "B" is not implemented')

  # 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')

  # 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')

  # 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"')

  # 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')

  # Variable type in an extended interface is of different type
  lines =<< trim END
    vim9script
    interface A
      this.val1: number
    endinterface
    interface B extends A
      this.val2: string
    endinterface
    interface C extends B
      this.val1: string
      this.val2: number
    endinterface
  END
  v9.CheckSourceFailure(lines, 'E1382: Member "val1": type mismatch, expected number but got string')
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>>
      this.var1: list<dict<number>>
      this.var2: list<dict<number>>
      this.var3: list<dict<number>>
    endinterface

    class A
      def A1()
      enddef
      def F3(): list<list<number>>
        return [[3]]
      enddef
      this.v1: list<list<number>> = [[0]]
      this.var3 = [{c: 30}]
    endclass

    class B extends A
      def B1()
      enddef
      def F2(): list<list<number>>
        return [[2]]
      enddef
      this.v2: list<list<number>> = [[0]]
      this.var2 = [{b: 20}]
    endclass

    class C extends B implements Intf
      def C1()
      enddef
      def F1(): list<list<number>>
        return [[1]]
      enddef
      this.v3: list<list<number>> = [[0]]
      this.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')

  # 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')

  # One of the interface variables is not present
  lines =<< trim END
    vim9script

    interface Intf
      this.var1: list<dict<number>>
      this.var2: list<dict<number>>
      this.var3: list<dict<number>>
    endinterface

    class A
      this.v1: list<list<number>> = [[0]]
    endclass

    class B extends A
      this.v2: list<list<number>> = [[0]]
      this.var2 = [{b: 20}]
    endclass

    class C extends B implements Intf
      this.v3: list<list<number>> = [[0]]
      this.var1 = [{a: 10}]
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1348: Member "var3" of interface "Intf" is not implemented')

  # One of the interface variables is of different type
  lines =<< trim END
    vim9script

    interface Intf
      this.var1: list<dict<number>>
      this.var2: list<dict<number>>
      this.var3: list<dict<number>>
    endinterface

    class A
      this.v1: list<list<number>> = [[0]]
      this.var3: list<dict<string>>
    endclass

    class B extends A
      this.v2: list<list<number>> = [[0]]
      this.var2 = [{b: 20}]
    endclass

    class C extends B implements Intf
      this.v3: list<list<number>> = [[0]]
      this.var1 = [{a: 10}]
    endclass
  END
  v9.CheckSourceFailure(lines, 'E1382: Member "var3": type mismatch, expected list<dict<number>> but got list<dict<string>>')
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
      this.n1: number
      def Foo1(): number
    endinterface
    interface B extends A
      this.n2: number
      this.n1: number
      def Foo2(): number
      def Foo1(): number
    endinterface
    class C implements B
      this.n1 = 10
      this.n2 = 20
      def Foo1(): number
        return 30
      enddef
      def Foo2(): number
        return 40
      enddef
    endclass
    def T1(a: A)
      assert_equal(10, a.n1)
      assert_equal(30, a.Foo1())
    enddef
    def T2(b: B)
      assert_equal(10, b.n1)
      assert_equal(20, b.n2)
      assert_equal(30, b.Foo1())
      assert_equal(40, b.Foo2())
    enddef
    var c = C.new()
    T1(c)
    T2(c)
  END
  v9.CheckSourceSuccess(lines)
enddef

" Test for using "any" type for a variable in a sub-class while it has a
" concrete type in the interface
def Test_implements_using_var_type_any()
  var lines =<< trim END
    vim9script
    interface A
      this.val: list<dict<string>>
    endinterface
    class B implements A
      this.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
      this.val: list<dict<string>>
    endinterface
    class B implements A
      this.val = {a: 1, b: 2}
    endclass
    var b = B.new()
  END
  v9.CheckSourceFailure(lines, 'E1382: Member "val": type mismatch, expected list<dict<string>> but got dict<number>')
enddef

" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker