require "../../spec_helper"

describe "Codegen: responds_to?" do
  it "codegens responds_to? true for simple type" do
    run("1.responds_to?(:\"+\")").to_b.should be_true
  end

  it "codegens responds_to? false for simple type" do
    run("1.responds_to?(:foo)").to_b.should be_false
  end

  it "codegens responds_to? with union gives true" do
    run("(1 == 1 ? 1 : 'a').responds_to?(:\"+\")").to_b.should be_true
  end

  it "codegens responds_to? with union gives false" do
    run("(1 == 1 ? 1 : 'a').responds_to?(:\"foo\")").to_b.should be_false
  end

  it "codegens responds_to? with nilable gives true" do
    run("struct Nil; def foo; end; end; (1 == 1 ? nil : Reference.new).responds_to?(:foo)").to_b.should be_true
  end

  it "codegens responds_to? with nilable gives false because other type 1" do
    run("(1 == 1 ? nil : Reference.new).responds_to?(:foo)").to_b.should be_false
  end

  it "codegens responds_to? with nilable gives false because other type 2" do
    run("class Reference; def foo; end; end; (1 == 2 ? nil : Reference.new).responds_to?(:foo)").to_b.should be_true
  end

  it "codegens responds_to? with generic class (1)" do
    run(<<-CRYSTAL).to_b.should be_true
      class Foo(T)
        def foo
        end
      end

      Foo(Int32).new.responds_to?(:foo)
      CRYSTAL
  end

  it "codegens responds_to? with generic class (2)" do
    run(<<-CRYSTAL).to_b.should be_false
      class Foo(T)
        def foo
        end
      end

      Foo(Int32).new.responds_to?(:bar)
      CRYSTAL
  end

  it "doesn't error if result is discarded (#14113)" do
    run(<<-CRYSTAL).to_i.should eq(1)
      class Foo
        def foo
        end
      end

      (Foo.new || "").responds_to?(:foo)
      1
      CRYSTAL
  end

  it "works with virtual type" do
    run(<<-CRYSTAL).to_b.should be_true
      class Foo
      end

      class Bar < Foo
        def foo
          1
        end
      end

      foo = Bar.new || Foo.new
      foo.responds_to?(:foo)
      CRYSTAL
  end

  it "works with two virtual types" do
    run(<<-CRYSTAL).to_b.should be_true
      class Foo
      end

      class Bar < Foo
        def foo
          1
        end
      end

      class Bar2 < Bar
      end

      class Other
      end

      class Sub < Other
        def foo
          3
        end
      end

      class Sub2 < Sub
        def foo
          4
        end
      end

      foo = Sub2.new || Bar.new || Bar2.new || Sub.new || Sub2.new
      foo.responds_to?(:foo)
      CRYSTAL
  end

  it "works with virtual class type (1) (#1926)" do
    run(<<-CRYSTAL).to_b.should be_true
      class Foo
      end

      class Bar < Foo
        def self.foo
          1
        end
      end

      foo = Bar || Foo
      foo.responds_to?(:foo)
      CRYSTAL
  end

  it "works with virtual class type (2) (#1926)" do
    run(<<-CRYSTAL).to_b.should be_false
      class Foo
      end

      class Bar < Foo
        def self.foo
          1
        end
      end

      foo = Foo || Bar
      foo.responds_to?(:foo)
      CRYSTAL
  end

  it "works with generic virtual superclass (1)" do
    run(<<-CRYSTAL).to_b.should be_true
      class Foo(T)
      end

      class Bar < Foo(Int32)

        def foo
          1
        end
      end

      foo = Bar.new.as(Foo(Int32))
      foo.responds_to?(:foo)
      CRYSTAL
  end

  it "works with generic virtual superclass (2)" do
    run(<<-CRYSTAL).to_b.should be_true
      class Foo(T)
      end

      class Bar(T) < Foo(T)

        def foo
          1
        end
      end

      foo = Bar(Int32).new.as(Foo(Int32))
      foo.responds_to?(:foo)
      CRYSTAL
  end

  it "works with module" do
    run(<<-CRYSTAL).to_b.should be_true
      module Moo
      end

      class Foo
        include Moo

        def foo
          1
        end
      end

      class Bar
        include Moo

        def foo
          1
        end
      end

      ptr = Pointer(Moo).malloc(1_u64)
      ptr.value = Bar.new
      ptr.value = Foo.new

      moo = ptr.value
      moo.responds_to?(:foo)
      CRYSTAL
  end

  it "works with generic virtual module (1)" do
    run(<<-CRYSTAL).to_b.should be_true
      module Foo(T)
      end

      class Bar
        include Foo(Int32)

        def foo
          1
        end
      end

      foo = Bar.new.as(Foo(Int32))
      foo.responds_to?(:foo)
      CRYSTAL
  end

  it "works with generic virtual module (2) (#8334)" do
    run(<<-CRYSTAL).to_b.should be_true
      module Foo(T)
      end

      class Bar(T)
        include Foo(T)

        def foo
          1
        end
      end

      foo = Bar(Int32).new.as(Foo(Int32))
      foo.responds_to?(:foo)
      CRYSTAL
  end

  it "does for generic instance type metaclass (#4353)" do
    run(<<-CRYSTAL).to_b.should be_true
      class MyGeneric(T)
        def self.hallo
          1
        end
      end

      MyGeneric(String).responds_to? :hallo
      CRYSTAL
  end
end
