require_relative '../spec_helper'

ruby_version_is "3.4" do
  eval <<-RUBY, binding, __FILE__, __LINE__ + 1 # use eval to avoid warnings on Ruby 3.3
  describe "The `it` parameter" do
    it "provides it in a block" do
      -> { it }.call("a").should == "a"
      proc { it }.call("a").should == "a"
      lambda { it }.call("a").should == "a"
      ["a"].map { it }.should == ["a"]
    end

    it "assigns nil to not passed parameters" do
      proc { it }.call().should == nil
    end

    it "can be used in both outer and nested blocks at the same time" do
      -> { it + -> { it * it }.call(2) }.call(3).should == 7
    end

    it "can be reassigned to act as a local variable" do
      proc { tmp = it; it = tmp * 2; it }.call(21).should == 42
    end

    it "is a regular local variable if there is already a 'it' local variable" do
      it = 0
      proc { it }.call("a").should == 0
    end

    it "is a regular local variable if there is a method `it` defined" do
      o = Object.new
      def o.it
        21
      end

      o.instance_eval("proc { it * 2 }").call(1).should == 2
    end

    it "is not shadowed by an reassignment in a block" do
      a = nil
      proc { a = it; it = 42 }.call(0)
      a.should == 0 # if `it` were shadowed its value would be nil
    end

    it "raises SyntaxError when block parameters are specified explicitly" do
      -> { eval("-> () { it }")         }.should.raise(SyntaxError, /ordinary parameter is defined/)
      -> { eval("-> (x) { it }")        }.should.raise(SyntaxError, /ordinary parameter is defined/)

      -> { eval("proc { || it }")       }.should.raise(SyntaxError, /ordinary parameter is defined/)
      -> { eval("proc { |x| it }")      }.should.raise(SyntaxError, /ordinary parameter is defined/)

      -> { eval("lambda { || it }")     }.should.raise(SyntaxError, /ordinary parameter is defined/)
      -> { eval("lambda { |x| it }")    }.should.raise(SyntaxError, /ordinary parameter is defined/)

      -> { eval("['a'].map { || it }")  }.should.raise(SyntaxError, /ordinary parameter is defined/)
      -> { eval("['a'].map { |x| it }") }.should.raise(SyntaxError, /ordinary parameter is defined/)
    end

    it "cannot be mixed with numbered parameters" do
      -> {
        eval("proc { it + _1 }")
      }.should.raise(SyntaxError, /numbered parameters are not allowed when 'it' is already used|'it' is already used in/)

      -> {
        eval("proc { _1 + it }")
      }.should.raise(SyntaxError, /numbered parameter is already used in|'it' is not allowed when a numbered parameter is already used/)
    end

    it "affects block arity" do
      -> {}.arity.should == 0
      -> { it }.arity.should == 1
    end

    it "affects block parameters" do
      -> { it }.parameters.should == [[:req]]

      ruby_version_is ""..."4.0" do
        proc { it }.parameters.should == [[:opt, nil]]
      end
      ruby_version_is "4.0" do
        proc { it }.parameters.should == [[:opt]]
      end
    end

    it "does not affect binding local variables" do
      -> { it; binding.local_variables }.call("a").should == []
    end

    it "does not affect binding local variables getting" do
      proc {
        a = it; binding.local_variable_get(:it)
      }.should.raise(NameError, /local variable 'it' is not defined for/)
    end

    it "does not affect binding local variables setting" do
      -> {
       a = it
       binding.local_variable_set(:it, :b)
       [a, it]
      }.call(:a).should == [:a, :a]
    end

    it "does not affect binding local variables definition check" do
      a = it
      binding.local_variable_defined?(:it).should == false
    end

    it "does not work in methods" do
      obj = Object.new
      def obj.foo; it; end

      -> { obj.foo("a") }.should.raise(ArgumentError, /wrong number of arguments/)
    end

    context "given multiple arguments" do
      it "provides it in a block and assigns the first argument for a block" do
        proc { it }.call("a", "b").should == "a"
      end

      it "raises ArgumentError for a proc" do
        -> { -> { it }.call("a", "b") }.should.raise(ArgumentError, "wrong number of arguments (given 2, expected 1)")
        -> { lambda { it }.call("a", "b") }.should.raise(ArgumentError, "wrong number of arguments (given 2, expected 1)")
      end
    end
  end
  RUBY
end
