require "../../spec_helper"

describe "Code gen: new" do
  it "codegens instance method with allocate" do
    run(<<-CRYSTAL).to_i.should eq(1)
      class Foo
        def coco
          1
        end
      end

      Foo.allocate.coco
      CRYSTAL
  end

  it "codegens instance method with new and instance var" do
    run(<<-CRYSTAL).to_i.should eq(1)
      class Foo
        def initialize
          @coco = 2
        end

        def coco
          @coco = 1
          @coco
        end
      end

      f = Foo.new
      f.coco
      CRYSTAL
  end

  it "codegens instance method with new" do
    run(<<-CRYSTAL).to_i.should eq(1)
      class Foo
        def coco
          1
        end
      end

      Foo.new.coco
      CRYSTAL
  end

  it "can create Reference" do
    run(<<-CRYSTAL).to_b.should be_false
      Reference.new.object_id == 0
      CRYSTAL
  end

  it "inherits initialize" do
    run(<<-CRYSTAL).to_i.should eq(42)
      class Foo
        def initialize(@x : Int32)
        end

        def x
          @x
        end
      end

      class Bar < Foo
      end

      Bar.new(42).x
      CRYSTAL
  end

  it "inherits initialize for generic type" do
    run(<<-CRYSTAL).to_i.should eq(42)
      class Foo(T)
        def initialize(@x : Int32)
        end
      end

      class Bar(T) < Foo(T)
        def x
          @x
        end
      end

      Bar(Int32).new(42).x
      CRYSTAL
  end

  it "overloads new and initialize, 1 (#2489)" do
    run(<<-CRYSTAL).to_i.should eq(10)
      class String
        def size
          10
        end
      end

      class Foo
        def initialize(@foo : Int32)
        end

        def self.new(bar) : self
          new bar.size
        end

        def self.new : self
          new "foo"
        end

        def foo
          @foo
        end
      end

      Foo.new.foo
      CRYSTAL
  end

  it "overloads new and initialize, 2 (#2489)" do
    run(<<-CRYSTAL).to_i.should eq(6)
      class Global
        @@x = 0

        def self.x=(@@x)
        end

        def self.x
          @@x
        end
      end

      class Foo
        def initialize(@foo : Int32)
        end
      end

      class Bar < Foo
        def self.new(foo : Int32) : self
          Global.x = foo &+ 1
          super
        end
      end

      Bar.new(5)

      Global.x
      CRYSTAL
  end

  it "overloads new and initialize, 3 (#2489)" do
    run(<<-CRYSTAL).to_i.should eq(6)
      class Global
        @@x = 0

        def self.x=(@@x)
        end

        def self.x
          @@x
        end
      end

      class Foo
        def initialize(@foo : Int32)
        end

        def self.new(foo : Int32) : self
          Global.x = foo &+ 1
          previous_def
        end
      end

      Foo.new(5)

      Global.x
      CRYSTAL
  end

  it "defines new for module" do
    run(<<-CRYSTAL).to_i.should eq(42)
      module Moo
        @x : Int32

        def initialize(x : Int32)
          @x = x &+ 1
        end

        def x
          @x
        end
      end

      class Foo
        include Moo
      end

      Foo.new(41).x
      CRYSTAL
  end

  it "finds super in deep hierarchy" do
    run(<<-CRYSTAL).to_i.should eq(42)
      class Foo
        def initialize(@x : Int32)
        end
      end

      class Bar < Foo
      end

      class Baz < Bar
      end

      class Qux < Baz
        def initialize
          super(42)
        end

        def x
          @x
        end
      end

      Qux.new.x
      CRYSTAL
  end

  it "finds new in superclass if no initialize is defined (1)" do
    run(<<-CRYSTAL).to_i.should eq(42)
      class Foo
        def self.new
          42
        end
      end

      class Bar < Foo
      end

      Bar.new
      CRYSTAL
  end

  it "finds new in superclass if no initialize is defined (2)" do
    run(<<-CRYSTAL).to_i.should eq(42)
      class Foo
        def self.new
          42
        end
      end

      class Bar < Foo
        def self.new(x)
          x
        end
      end

      Bar.new
      CRYSTAL
  end

  it "finds new in superclass for Enum" do
    run(<<-CRYSTAL).to_i.should eq(1)
      struct Enum
        def self.new(x : String)
          new(1)
        end
      end

      enum Color
        Red
        Green
        Blue
      end

      color = Color.new("foo")
      color.value
      CRYSTAL
  end

  it "can create Tuple with Tuple.new" do
    run(<<-CRYSTAL).to_i.should eq(0)
      require "prelude"

      Tuple.new.size
      CRYSTAL
  end

  it "evaluates initialize default value at the instance scope (1) (#731)" do
    run(<<-CRYSTAL).to_i.should eq(42)
      class Foo
        @x : Int32

        def initialize(@x = bar)
        end

        def x
          @x
        end

        def bar
          42
        end
      end

      Foo.new.x
      CRYSTAL
  end

  it "evaluates initialize default value at the instance scope (2) (#731)" do
    run(<<-CRYSTAL).to_i.should eq(42)
      class Foo
        @x : Int32

        def initialize(@x = bar, @y = 2)
        end

        def x
          @x
        end

        def y
          @y
        end

        def bar
          20
        end
      end

      foo = Foo.new(y: 22)
      foo.x &+ foo.y
      CRYSTAL
  end

  it "evaluates initialize default value at the instance scope (3) (#731)" do
    run(<<-CRYSTAL).to_i.should eq(42)
      class Foo
        @x : Int32

        def initialize(@x = bar)
          yield 10, 12
        end

        def x
          @x
        end

        def bar
          20
        end
      end

      total = 0
      foo = Foo.new do |a, b|
        total &+= a
        total &+= b
      end
      total &+= foo.x
      total
      CRYSTAL
  end

  it "evaluates initialize default value at the instance scope (4) (#731)" do
    run(<<-CRYSTAL).to_i.should eq(42)
      class Foo
        @x : Int32

        def initialize(@x = bar, &@block : -> Int32)
        end

        def x
          @x
        end

        def bar
          22
        end

        def block
          @block
        end
      end

      foo = Foo.new do
        20
      end
      foo.x &+ foo.block.call
      CRYSTAL
  end
end
