require "../../spec_helper"

describe "Semantic: module" do
  it "includes but not a module" do
    assert_error "class Foo; end; class Bar; include Foo; end",
      "Foo is not a module"
  end

  it "includes module in a class" do
    assert_type("module Foo; def foo; 1; end; end; class Bar; include Foo; end; Bar.new.foo") { int32 }
  end

  it "includes module in a module" do
    assert_type(<<-CRYSTAL) { int32 }
      module Moo
        def foo
          1
        end
      end

      module Boo
        include Moo
      end

      class Foo
        include Boo
      end

      Foo.new.foo
      CRYSTAL
  end

  it "finds in module when included" do
    assert_type(<<-CRYSTAL) { int32 }
      module Moo
        class B
          def foo; 1; end
        end
      end

      include Moo

      B.new.foo
      CRYSTAL
  end

  it "includes generic module with type" do
    assert_type(<<-CRYSTAL) { int32 }
      module Foo(T)
        def foo(x : T)
          x
        end
      end

      class Bar
        include Foo(Int)
      end

      Bar.new.foo(1)
      CRYSTAL
  end

  it "includes generic module and errors in call" do
    assert_error <<-CRYSTAL, "expected argument #1 to 'Bar#foo' to be Int, not Float64"
      module Foo(T)
        def foo(x : T)
          x
        end
      end

      class Bar
        include Foo(Int)
      end

      Bar.new.foo(1.5)
      CRYSTAL
  end

  it "includes module but not generic" do
    assert_error <<-CRYSTAL, "Foo is not a generic type"
      module Foo
      end

      class Bar
        include Foo(Int)
      end
      CRYSTAL
  end

  it "includes module but wrong number of arguments" do
    assert_error <<-CRYSTAL, "wrong number of type vars for Foo(T, U) (given 1, expected 2)"
      module Foo(T, U)
      end

      class Bar
        include Foo(Int)
      end
      CRYSTAL
  end

  it "errors if including generic module and not specifying type vars" do
    assert_error <<-CRYSTAL, "generic type arguments must be specified when including Foo(T)"
      module Foo(T)
      end

      class Bar
        include Foo
      end
      CRYSTAL
  end

  it "includes generic module explicitly" do
    assert_type(<<-CRYSTAL) { int32 }
      module Foo(T)
        def foo(x : T)
          x
        end
      end

      class Bar(U)
        include Foo(U)
      end

      Bar(Int32).new.foo(1)
      CRYSTAL
  end

  it "includes generic module explicitly and errors" do
    assert_error <<-CRYSTAL, "expected argument #1 to 'Bar(Int32)#foo' to be Int32, not Float64"
      module Foo(T)
        def foo(x : T)
          x
        end
      end

      class Bar(U)
        include Foo(U)
      end

      Bar(Int32).new.foo(1.5)
      CRYSTAL
  end

  it "reports can't use instance variables inside module" do
    assert_error "def foo; @a = 1; end; foo",
      "can't use instance variables at the top level"
  end

  it "works with int including enumerable" do
    assert_type(<<-CRYSTAL) { array_of(float64) }
      require "prelude"

      struct Int32
        include Enumerable(Int32)

        def each
          yield self
          yield self + 2
        end
      end

      1.map { |x| x * 0.5 }
      CRYSTAL
  end

  it "works with range and map" do
    assert_type(<<-CRYSTAL) { array_of(float64) }
      require "prelude"
      (1..3).map { |x| x * 0.5 }
      CRYSTAL
  end

  it "declares module automatically if not previously declared when declaring a class" do
    assert_type(<<-CRYSTAL
      class Foo::Bar
      end
      Foo
      CRYSTAL
    ) do
      foo = types["Foo"]
      foo.module?.should be_true
      foo.metaclass
    end
  end

  it "declares module automatically if not previously declared when declaring a module" do
    assert_type(<<-CRYSTAL
      module Foo::Bar
      end
      Foo
      CRYSTAL
    ) do
      foo = types["Foo"]
      foo.module?.should be_true
      foo.metaclass
    end
  end

  it "errors when a generic instance type alias is used as namespace (#8512)" do
    assert_error <<-CRYSTAL, "Foo(Int32) can't be used as a namespace"
      struct Foo(T)
      end

      alias Bar = Foo(Int32)

      module Bar::X
      end
      CRYSTAL
  end

  it "includes generic module with another generic type" do
    assert_type(<<-CRYSTAL) { generic_class("Baz", int32).metaclass }
      module Foo(T)
        def foo
          T
        end
      end

      class Baz(X)
      end

      class Bar(U)
        include Foo(Baz(U))
      end

      Bar(Int32).new.foo
      CRYSTAL
  end

  it "includes generic module with self" do
    assert_type(<<-CRYSTAL) { generic_class("Bar", int32).metaclass }
      module Foo(T)
        def foo
          T
        end
      end

      class Bar(U)
        include Foo(self)
      end

      Bar(Int32).new.foo
      CRYSTAL
  end

  it "includes generic module with self, and inherits it" do
    assert_type(<<-CRYSTAL) { generic_class("Bar", int32).metaclass }
      module Foo(T)
        def foo
          T
        end
      end

      class Bar(U)
        include Foo(self)
      end

      class Baz < Bar(Int32)
      end

      Baz.new.foo
      CRYSTAL
  end

  it "includes generic module with self (check argument type, success)" do
    assert_type(<<-CRYSTAL) { generic_class("Bar", int32) }
      module Foo(T)
        def foo(x : T)
          x
        end
      end

      class Bar(U)
        include Foo(self)
      end

      Bar(Int32).new.foo Bar(Int32).new
      CRYSTAL
  end

  it "includes generic module with self (check argument superclass type, success)" do
    assert_type(<<-CRYSTAL) { types["Baz"] }
      module Foo(T)
        def foo(x : T)
          x
        end
      end

      class Bar(U)
        include Foo(self)
      end

      class Baz < Bar(Int32)
      end

      Bar(Int32).new.foo Baz.new
      CRYSTAL
  end

  it "includes generic module with self (check argument type, error)" do
    assert_error <<-CRYSTAL, "expected argument #1 to 'Baz1#foo' to be Bar(Int32), not Baz2"
      module Foo(T)
        def foo(x : T)
          x
        end
      end

      class Bar(U)
        include Foo(self)
      end

      class Baz1 < Bar(Int32)
      end

      class Baz2 < Bar(Float64)
      end

      Baz1.new.foo Baz2.new
      CRYSTAL
  end

  it "includes generic module with self (check argument superclass type, success)" do
    assert_type(<<-CRYSTAL) { types["Baz2"] }
      module Foo(T)
        def foo(x : T)
          x
        end
      end

      class Bar(U)
        include Foo(self)
      end

      class Baz1 < Bar(Int32)
      end

      class Baz2 < Bar(Int32)
      end

      Baz1.new.foo Baz2.new
      CRYSTAL
  end

  it "includes generic module with self (check return type, success)" do
    assert_type(<<-CRYSTAL) { generic_class("Bar", int32) }
      module Foo(T)
        def foo : T
          Bar(Int32).new
        end
      end

      class Bar(U)
        include Foo(self)
      end

      Bar(Int32).new.foo
      CRYSTAL
  end

  it "includes generic module with self (check return subclass type, success)" do
    assert_type(<<-CRYSTAL) { types["Baz"] }
      module Foo(T)
        def foo : T
          Baz.new
        end
      end

      class Bar(U)
        include Foo(self)
      end

      class Baz < Bar(Int32)
      end

      Bar(Int32).new.foo
      CRYSTAL
  end

  it "includes generic module with self (check return type, error)" do
    assert_error <<-CRYSTAL, "method Baz#foo must return Bar(Float64) but it is returning Bar(Int32)"
      module Foo(T)
        def foo : T
          Bar(Int32).new
        end
      end

      class Bar(U)
        include Foo(self)
      end

      class Baz < Bar(Float64)
      end

      Baz.new.foo
      CRYSTAL
  end

  it "includes generic module with self (check return subclass type, error)" do
    assert_error <<-CRYSTAL, "method Baz1#foo must return Bar(Int32) but it is returning Baz2"
      module Foo(T)
        def foo : T
          Baz2.new
        end
      end

      class Bar(U)
        include Foo(self)
      end

      class Baz1 < Bar(Int32)
      end

      class Baz2 < Bar(Float64)
      end

      Baz1.new.foo
      CRYSTAL
  end

  it "includes module but can't access metaclass methods" do
    assert_error <<-CRYSTAL, "undefined method 'foo'"
      module Foo
        def self.foo
          1
        end
      end

      class Bar
        include Foo
      end

      Bar.foo
      CRYSTAL
  end

  it "extends a module" do
    assert_type(<<-CRYSTAL) { int32 }
      module Foo
        def foo
          1
        end
      end

      class Bar
        extend Foo
      end

      Bar.foo
      CRYSTAL
  end

  it "extends self" do
    assert_type(<<-CRYSTAL) { int32 }
      module Foo
        extend self

        def foo
          1
        end
      end

      Foo.foo
      CRYSTAL
  end

  it "gives error when including self" do
    assert_error <<-CRYSTAL, "cyclic include detected"
      module Foo
        include self
      end
      CRYSTAL
  end

  it "gives error with cyclic include" do
    assert_error <<-CRYSTAL, "cyclic include detected"
      module Foo
      end

      module Bar
        include Foo
      end

      module Foo
        include Bar
      end
      CRYSTAL
  end

  it "gives error when including self, generic module" do
    assert_error <<-CRYSTAL, "cyclic include detected"
      module Foo(T)
        include self
      end
      CRYSTAL
  end

  it "gives error when including instantiation of self, generic module" do
    assert_error <<-CRYSTAL, "cyclic include detected"
      module Foo(T)
        include Foo(Int32)
      end
      CRYSTAL
  end

  it "gives error with cyclic include, generic module" do
    assert_error <<-CRYSTAL, "cyclic include detected"
      module Foo(T)
      end

      module Bar(T)
        include Foo(T)
      end

      module Foo(T)
        include Bar(T)
      end
      CRYSTAL
  end

  it "gives error with cyclic include between non-generic and generic module" do
    assert_error <<-CRYSTAL, "cyclic include detected"
      module Foo
      end

      module Bar(T)
        include Foo
      end

      module Foo
        include Bar(Int32)
      end
      CRYSTAL
  end

  it "gives error with cyclic include between non-generic and generic module (2)" do
    assert_error <<-CRYSTAL, "cyclic include detected"
      module Bar(T)
      end

      module Foo
        include Bar(Int32)
      end

      module Bar(T)
        include Foo
      end
      CRYSTAL
  end

  it "finds types close to included module" do
    assert_type(<<-CRYSTAL) { types["Foo"].types["T"].metaclass }
      module Foo
        class T
        end

        def foo
          T
        end
      end

      class Bar
        class T
        end

        include Foo
      end

      Bar.new.foo
      CRYSTAL
  end

  it "finds nested type inside method in block inside module" do
    assert_type(<<-CRYSTAL) { types["Foo"].types["Bar"].metaclass }
      def foo
        yield
      end

      module Foo
        class Bar; end

        @@x : Bar.class
        @@x = foo { Bar }

        def self.x
          @@x
        end
      end

      Foo.x
      CRYSTAL
  end

  it "finds class method in block" do
    assert_type(<<-CRYSTAL) { int32 }
      def foo
        yield
      end

      module Foo
        def self.bar
          1
        end

        @@x : Int32
        @@x = foo { bar }

        def self.x
          @@x
        end
      end

      Foo.x
      CRYSTAL
  end

  it "types pointer of module" do
    assert_type(<<-CRYSTAL, inject_primitives: true) { types["Moo"] }
      module Moo
      end

      class Foo
        include Moo

        def foo
          1
        end
      end

      p = Pointer(Moo).malloc(1_u64)
      p.value = Foo.new
      p.value
      CRYSTAL
  end

  it "types pointer of module with method" do
    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }
      module Moo
      end

      class Foo
        include Moo

        def foo
          1
        end
      end

      p = Pointer(Moo).malloc(1_u64)
      p.value = Foo.new
      p.value.foo
      CRYSTAL
  end

  it "types pointer of module with method with two including types" do
    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(int32, char) }
      module Moo
      end

      class Foo
        include Moo

        def foo
          1
        end
      end

      class Bar
        include Moo

        def foo
          'a'
        end
      end

      p = Pointer(Moo).malloc(1_u64)
      p.value = Foo.new
      p.value = Bar.new
      p.value.foo
      CRYSTAL
  end

  it "types pointer of module with generic type" do
    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }
      module Moo
      end

      class Foo(T)
        include Moo

        def foo
          1
        end
      end

      p = Pointer(Moo).malloc(1_u64)
      p.value = Foo(Int32).new
      p.value.foo
      CRYSTAL
  end

  it "types pointer of module with generic type" do
    assert_type(<<-CRYSTAL, inject_primitives: true) { union_of(int32, char) }
      module Moo
      end

      class Bar
        def self.boo
          1
        end
      end

      class Baz
        def self.boo
          'a'
        end
      end

      class Foo(T)
        include Moo

        def foo
          T.boo
        end
      end

      p = Pointer(Moo).malloc(1_u64)
      p.value = Foo(Bar).new
      x = p.value.foo

      p.value = Foo(Baz).new

      x
      CRYSTAL
  end

  it "allows overloading with included generic module" do
    assert_type(<<-CRYSTAL) { union_of(int32, string) }
      module Foo(T)
        def foo(x : T)
          bar(x)
        end
      end

      class Bar
        include Foo(Int32)
        include Foo(String)

        def bar(x : Int32)
          1
        end

        def bar(x : String)
          "a"
        end
      end

      Bar.new.foo(1 || "hello")
      CRYSTAL
  end

  it "finds constant in generic module included in another module" do
    assert_type(<<-CRYSTAL) { int32.metaclass }
      module Foo(T)
        def foo
          T
        end
      end

      module Bar(T)
        include Foo(T)
      end

      class Baz
        include Bar(Int32)
      end

      Baz.new.foo
      CRYSTAL
  end

  it "calls super on included generic module" do
    assert_type(<<-CRYSTAL) { int32 }
      module Foo(T)
        def foo
          1
        end
      end

      class Bar
        include Foo(Int32)

        def foo
          super
        end
      end

      Bar.new.foo
      CRYSTAL
  end

  it "calls super on included generic module and finds type var" do
    assert_type(<<-CRYSTAL) { int32.metaclass }
      module Foo(T)
        def foo
          T
        end
      end

      class Bar(T)
        include Foo(T)

        def foo
          super
        end
      end

      Bar(Int32).new.foo
      CRYSTAL
  end

  it "calls super on included generic module and finds type var (2)" do
    assert_type(<<-CRYSTAL) { int32.metaclass }
      module Foo(T)
        def foo
          T
        end
      end

      module Bar(T)
        include Foo(T)

        def foo
          super
        end
      end

      class Baz(T)
        include Bar(T)
      end

      Baz(Int32).new.foo
      CRYSTAL
  end

  it "types union of module and class that includes it" do
    assert_type(<<-CRYSTAL) { union_of(types["Bar"].metaclass, types["Moo"].metaclass) }
      module Moo
        def self.foo
          1
        end
      end

      class Bar
        include Moo

        def self.foo
          2
        end
      end

      Bar || Moo
      CRYSTAL
  end

  it "works ok in a case where a typed-def type has an underlying type that has an included generic module (bug)" do
    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }
      lib LibC
        type X = Void*
        fun x : X
      end

      module Mod(T)
        def bar(other : T)
          1
        end
      end

      struct Pointer
        include Mod(self)

        def foo
          address
        end
      end

      LibC.x.foo

      p = Pointer(Void).malloc(1_u64)
      p.bar(p)
      CRYSTAL
  end

  it "finds inner class from inherited one (#476)" do
    assert_type(<<-CRYSTAL) { types["Foo"].types["Bar"].types["Baz"].metaclass }
      class Foo
        class Bar
          class Baz
          end
        end
      end

      class Quz < Foo
      end

      Quz::Bar::Baz
      CRYSTAL
  end

  it "type def does not reopen type from parent namespace (#11181)" do
    assert_type <<-CRYSTAL, inject_primitives: false { types["Baz"].types["Foo"].types["Bar"].metaclass }
      module Foo::Bar
      end

      module Baz
        module Foo::Bar
        end
      end

      Baz::Foo::Bar
      CRYSTAL
  end

  it "correctly types type var in included module, with a restriction with a free var (bug)" do
    assert_type(<<-CRYSTAL) { int32.metaclass }
      module Moo(T)
      end

      class Foo(T)
        include Moo(T)

        def foo(x : Moo(U)) forall U
          T
        end
      end

      Foo(Int32).new.foo(Foo(Char).new)
      CRYSTAL
  end

  it "types proc of module after type changes" do
    assert_type(<<-CRYSTAL, inject_primitives: true) { int32 }
      module Moo
      end

      class Foo(T)
        include Moo

        def foo
          3
        end
      end

      z = ->(x : Moo) { x.foo }
      z.call(Foo(Int32).new)
      CRYSTAL
  end

  it "types proc of module with generic class" do
    assert_type(<<-CRYSTAL, inject_primitives: true) { char }
      module Moo
      end

      class Foo(T)
        include Moo

        def foo
          'a'
        end
      end

      z = ->(x : Moo) { x.foo }
      z.call(Foo(Int32).new)
      CRYSTAL
  end

  it "errors if declares module inside if" do
    assert_error <<-CRYSTAL, "can't declare module dynamically"
      if 1 == 2
        module Foo; end
      end
      CRYSTAL
  end

  it "can't reopen as class" do
    assert_error <<-CRYSTAL, "Foo is not a class, it's a module"
      module Foo
      end

      class Foo
      end
      CRYSTAL
  end

  it "can't reopen as struct" do
    assert_error <<-CRYSTAL, "Foo is not a struct, it's a module"
      module Foo
      end

      struct Foo
      end
      CRYSTAL
  end

  it "errors if reopening non-generic module as generic" do
    assert_error <<-CRYSTAL, "Foo is not a generic module"
      module Foo
      end

      module Foo(T)
      end
      CRYSTAL
  end

  it "errors if reopening generic module with different type vars" do
    assert_error <<-CRYSTAL, "type var must be T, not U"
      module Foo(T)
      end

      module Foo(U)
      end
      CRYSTAL
  end

  it "errors if reopening generic module with different type vars (2)" do
    assert_error <<-CRYSTAL, "type vars must be A, B, not C"
      module Foo(A, B)
      end

      module Foo(C)
      end
      CRYSTAL
  end

  it "errors if reopening generic module with different splat index" do
    assert_error <<-CRYSTAL, "type var must be A, not *A"
      module Foo(A)
      end

      module Foo(*A)
      end
      CRYSTAL
  end

  it "errors if reopening generic module with different splat index (2)" do
    assert_error <<-CRYSTAL, "type var must be *A, not A"
      module Foo(*A)
      end

      module Foo(A)
      end
      CRYSTAL
  end

  it "errors if reopening generic module with different splat index (3)" do
    assert_error <<-CRYSTAL, "type vars must be *A, B, not A, *B"
      module Foo(*A, B)
      end

      module Foo(A, *B)
      end
      CRYSTAL
  end

  it "uses :Module name for modules in errors" do
    assert_error <<-CRYSTAL, "undefined method 'new' for Moo:Module"
      module Moo; end

      Moo.new
      CRYSTAL
  end

  it "gives error when trying to instantiate with new" do
    assert_error <<-CRYSTAL, "undefined local variable or method 'allocate' for Moo:Module (modules cannot be instantiated)"
      module Moo
        def initialize
        end
      end
      Moo.new
      CRYSTAL
  end

  it "gives error when trying to instantiate with allocate" do
    assert_error <<-CRYSTAL, "undefined method 'allocate' for Moo:Module (modules cannot be instantiated)"
      module Moo
        def initialize
        end
      end
      Moo.allocate
      CRYSTAL
  end

  it "uses type declaration inside module" do
    assert_type(<<-CRYSTAL) { int32 }
      module Moo
        @x : Int32

        def initialize
          @x = 1
        end

        def x
          @x
        end
      end

      class Foo
        include Moo
      end

      Foo.new.x
      CRYSTAL
  end

  it "uses type declaration inside module and gives error" do
    assert_error <<-CRYSTAL, "instance variable '@x' of Foo must be Int32, not Bool"
      module Moo
        @x : Int32

        def initialize(@x)
        end

        def moo
          @x = false
        end
      end

      class Foo
        include Moo

        def initialize
          super(1)
          @x = 1
        end
      end

      Foo.new.moo
      CRYSTAL
  end

  it "uses type declaration inside module, recursive, and gives error" do
    assert_error <<-CRYSTAL, "instance variable '@x' of Foo must be Int32"
      module Moo
        @x : Int32

        def initialize(@x)
        end

        def moo
          @x = false
        end
      end

      module Moo2
        include Moo
      end

      class Foo
        include Moo2

        def initialize
          super(1)
          @x = 1
        end
      end

      Foo.new.moo
      CRYSTAL
  end

  it "initializes variable in module" do
    assert_type(<<-CRYSTAL) { int32 }
      module Moo
        @x = 1

        def x
          @x
        end
      end

      class Foo
        include Moo
      end

      Foo.new.x
      CRYSTAL
  end

  it "initializes variable in module, recursive" do
    assert_type(<<-CRYSTAL) { int32 }
      module Moo
        @x = 1

        def x
          @x
        end
      end

      module Moo2
        include Moo
      end

      class Foo
        include Moo2
      end

      Foo.new.x
      CRYSTAL
  end

  it "inherits instance var type annotation from generic to concrete" do
    assert_type(<<-CRYSTAL) { nilable int32 }
      module Foo(T)
        @x : Int32?

        def x
          @x
        end
      end

      class Bar
        include Foo(Int32)
      end

      Bar.new.x
      CRYSTAL
  end

  it "inherits instance var type annotation from generic to concrete with T" do
    assert_type(<<-CRYSTAL) { nilable int32 }
      module Foo(T)
        @x : T?

        def x
          @x
        end
      end

      class Bar
        include Foo(Int32)
      end

      Bar.new.x
      CRYSTAL
  end

  it "inherits instance var type annotation from generic to generic to concrete" do
    assert_type(<<-CRYSTAL) { nilable int32 }
      module Foo(T)
        @x : Int32?

        def x
          @x
        end
      end

      module Bar(T)
        include Foo(T)
      end

      class Baz
        include Bar(Int32)
      end

      Baz.new.x
      CRYSTAL
  end

  it "instantiates generic variadic module, accesses T from instance method" do
    assert_type(<<-CRYSTAL) { tuple_of([int32, char]).metaclass }
      module Moo(*T)
        def t
          T
        end
      end

      class Foo
        include Moo(Int32, Char)
      end

      Foo.new.t
      CRYSTAL
  end

  it "instantiates generic variadic module, accesses T from class method" do
    assert_type(<<-CRYSTAL) { tuple_of([int32, char]).metaclass }
      module Moo(*T)
        def t
          T
        end
      end

      class Foo
        extend Moo(Int32, Char)
      end

      Foo.t
      CRYSTAL
  end

  it "instantiates generic variadic module, accesses T from instance method through generic include" do
    assert_type(<<-CRYSTAL) { tuple_of([int32, char]).metaclass }
      module Moo(*T)
        def t
          T
        end
      end

      class Foo(*T)
        include Moo(*T)
      end

      Foo(Int32, Char).new.t
      CRYSTAL
  end

  it "instantiates generic variadic module, accesses T from class method through generic extend" do
    assert_type(<<-CRYSTAL) { tuple_of([int32, char]).metaclass }
      module Moo(*T)
        def t
          T
        end
      end

      class Foo(*T)
        extend Moo(*T)
      end

      Foo(Int32, Char).t
      CRYSTAL
  end

  it "instantiates generic variadic module, accesses T from instance method, more args" do
    assert_type(<<-CRYSTAL) { tuple_of([int32.metaclass, tuple_of([float64, char]).metaclass, string.metaclass]) }
      module Moo(A, *T, B)
        def t
          {A, T, B}
        end
      end

      class Foo
        include Moo(Int32, Float64, Char, String)
      end

      Foo.new.t
      CRYSTAL
  end

  it "instantiates generic variadic module, accesses T from instance method through generic include, more args" do
    assert_type(<<-CRYSTAL) { tuple_of([int32.metaclass, tuple_of([float64, char]).metaclass, string.metaclass]) }
      module Moo(A, *T, B)
        def t
          {A, T, B}
        end
      end

      class Foo(*T)
        include Moo(Int32, *T, String)
      end

      Foo(Float64, Char).new.t
      CRYSTAL
  end

  it "includes module with Union(T*)" do
    assert_type(<<-CRYSTAL) { union_of(int32, char).metaclass }
      module Foo(U)
        def u
          U
        end
      end

      struct Tuple
        include Foo(Union(*T))
      end

      {1, 'a'}.u
      CRYSTAL
  end

  it "doesn't lookup type in ancestor when matches in current type (#2982)" do
    assert_error <<-CRYSTAL, "undefined constant Qux::Bar"
      module Foo
        module Qux
          class Bar
          end
        end
      end

      class Qux
      end

      include Foo

      Qux::Bar
      CRYSTAL
  end

  it "can restrict module with module (#3029)" do
    assert_type(<<-CRYSTAL) { int32 }
      module Foo
      end

      class Gen(T)
      end

      def foo(x : Gen(Foo))
        1
      end

      foo(Gen(Foo).new)
      CRYSTAL
  end

  it "can instantiate generic module" do
    assert_type(<<-CRYSTAL) { generic_module("Foo", int32).metaclass }
      module Foo(T)
      end

      Foo(Int32)
      CRYSTAL
  end

  it "can use generic module as instance variable type" do
    assert_type(<<-CRYSTAL) { union_of int32, char }
      module Moo(T)
        def foo
          1
        end
      end

      class Foo
        include Moo(Int32)
      end

      class Bar
        include Moo(Int32)

        def foo
          'a'
        end
      end

      class Mooer
        def initialize(@moo : Moo(Int32))
        end

        def moo
          @moo.foo
        end
      end

      mooer = Mooer.new(Foo.new)
      mooer.moo
      CRYSTAL
  end

  it "can use generic module as instance variable type (2)" do
    assert_type(<<-CRYSTAL) { union_of int32, char }
      module Moo(T)
        def foo
          1
        end
      end

      class Foo(T)
        include Moo(T)
      end

      class Bar(T)
        include Moo(T)

        def foo
          'a'
        end
      end

      class Mooer
        def initialize(@moo : Moo(Int32))
        end

        def moo
          @moo.foo
        end
      end

      mooer = Mooer.new(Foo(Int32).new)
      mooer = Mooer.new(Bar(Int32).new)
      mooer.moo
      CRYSTAL
  end

  it "errors when extending module that defines instance vars (#4065)" do
    assert_error <<-CRYSTAL, "can't declare instance variables in Foo because Bar extends it"
      module Foo
        @x = 0
      end

      module Bar
        extend Foo
      end
      CRYSTAL
  end

  it "errors when extending module that defines instance vars (2) (#4065)" do
    assert_error <<-CRYSTAL, "can't declare instance variables in Foo because Bar extends it"
      module Foo
        @x : Int32?
      end

      module Bar
        extend Foo
      end
      CRYSTAL
  end

  it "errors when extending generic module that defines instance vars" do
    assert_error <<-CRYSTAL, "can't declare instance variables in Foo(T) because Bar(T) extends it"
      module Foo(T)
        @x = 0
      end

      module Bar(T)
        extend Foo(T)
      end
      CRYSTAL
  end

  it "errors when extending generic module that defines instance vars (2)" do
    assert_error <<-CRYSTAL, "can't declare instance variables in Foo(T) because Bar(T) extends it"
      module Foo(T)
        @x : T?
      end

      module Bar(T)
        extend Foo(T)
      end
      CRYSTAL
  end

  it "errors when recursively extending module that defines instance vars" do
    assert_error <<-CRYSTAL, "can't declare instance variables in Foo because Baz extends it"
      module Foo
        @x = 0
      end

      module Bar
        include Foo
      end

      module Baz
        extend Bar
      end
      CRYSTAL
  end

  it "errors when recursively extending module that defines instance vars (2)" do
    assert_error <<-CRYSTAL, "can't declare instance variables in Foo because Baz extends it"
      module Foo
        @x : Int32?
      end

      module Bar
        include Foo
      end

      module Baz
        extend Bar
      end
      CRYSTAL
  end

  it "errors when extending self and self defines instance vars (#9568)" do
    assert_error <<-CRYSTAL, "can't declare instance variables in Foo because Foo extends it"
      module Foo
        extend self

        @x = 0
      end
      CRYSTAL
  end

  it "errors when extending self and self defines instance vars (2) (#9568)" do
    assert_error <<-CRYSTAL, "can't declare instance variables in Foo because Foo extends it"
      module Foo
        extend self

        @x : Int32?
      end
      CRYSTAL
  end

  it "errors when extending self and self defines instance vars (3) (#9568)" do
    assert_error <<-CRYSTAL, "can't declare instance variables in Foo because Foo extends it"
      module Foo
        extend self

        def initialize
          @x = 0
        end
      end
      CRYSTAL
  end

  it "can't pass module class to virtual metaclass (#6113)" do
    assert_error <<-CRYSTAL, "expected argument #1 to 'Gen(Foo.class).foo' to be Foo.class, not Moo:Module"
      module Moo
      end

      class Foo
      end

      class Bar < Foo
        include Moo
      end

      class Gen(T)
        def self.foo(x : T)
        end
      end

      Gen(Foo.class).foo(Moo)
      CRYSTAL
  end

  it "extends module from generic class and calls class method (#7167)" do
    assert_type(<<-CRYSTAL) { int32 }
      module Foo
        def foo
          1
        end
      end

      class Gen(T)
        extend Foo
      end

      Gen(Int32).foo
      CRYSTAL
  end

  it "extends generic module from generic class and calls class method (#7167)" do
    assert_type(<<-CRYSTAL) { int32.metaclass }
      module Foo(T)
        def foo
          T
        end
      end

      class Gen(U)
        extend Foo(U)
      end

      Gen(Int32).foo
      CRYSTAL
  end

  it "extends generic module from generic module and calls class method (#7167)" do
    assert_type(<<-CRYSTAL) { int32.metaclass }
      module Foo(T)
        def foo
          T
        end
      end

      module Gen(U)
        extend Foo(U)
      end

      Gen(Int32).foo
      CRYSTAL
  end

  it "doesn't look up initialize past module that defines initialize (#7007)" do
    assert_error <<-CRYSTAL, "wrong number of arguments"
      module Moo
        def initialize(x)
        end
      end

      class Foo
        include Moo
      end

      Foo.new
      CRYSTAL
  end

  it "gives helpful error message when generic type var is missing" do
    assert_error <<-CRYSTAL, "can't infer the type parameter T for the generic module Foo(T). Please provide it explicitly"
      module Foo(T)
        def self.foo
          T.bar
        end
      end

      Foo.foo
      CRYSTAL
  end

  it "gives helpful error message when generic type var is missing in block spec" do
    assert_error <<-CRYSTAL, "can't infer the type parameter T for the generic module Foo(T). Please provide it explicitly"
      module Foo(T)
        def self.foo(&block : T -> )
          block
        end
      end

      Foo.foo { |x| }
      CRYSTAL
  end
end
