require "../../../spec_helper"

module Ameba::Rule::Style
  subject = RedundantReturn.new

  describe RedundantReturn do
    it "does not report if there is no return" do
      expect_no_issues subject, <<-CRYSTAL
        def inc(a)
          a + 1
        end
        CRYSTAL
    end

    it "reports if there is redundant return in method body" do
      source = expect_issue subject, <<-CRYSTAL
        def inc(a)
          return a + 1
        # ^^^^^^^^^^^^ error: Redundant `return` detected
        end
        CRYSTAL

      expect_correction source, <<-CRYSTAL
        def inc(a)
          a + 1
        end
        CRYSTAL
    end

    it "doesn't report if it returns tuple literal" do
      expect_no_issues subject, <<-CRYSTAL
        def foo(a)
          return a, a + 2
        end
        CRYSTAL
    end

    it "doesn't report if there are other expressions after control flow" do
      expect_no_issues subject, <<-CRYSTAL
        def method(a)
          case a
          when true then return true
          when .nil? then return :nil
          end
          false
        rescue
          nil
        end
        CRYSTAL
    end

    context "if" do
      it "doesn't report if there is return in if branch" do
        expect_no_issues subject, <<-CRYSTAL
          def inc(a)
            return a + 1 if a > 0
          end
          CRYSTAL
      end

      it "reports if there are returns in if/else branch" do
        source = expect_issue subject, <<-CRYSTAL
          def inc(a)
            do_something(a)
            if a > 0
              return :positive
            # ^^^^^^^^^^^^^^^^ error: Redundant `return` detected
            else
              return :negative
            # ^^^^^^^^^^^^^^^^ error: Redundant `return` detected
            end
          end
          CRYSTAL

        expect_correction source, <<-CRYSTAL
          def inc(a)
            do_something(a)
            if a > 0
              :positive
            else
              :negative
            end
          end
          CRYSTAL
      end
    end

    context "unless" do
      it "doesn't report if there is return in unless branch" do
        expect_no_issues subject, <<-CRYSTAL
          def inc(a)
            return a + 1 unless a > 0
          end
          CRYSTAL
      end

      it "reports if there are returns in unless/else branch" do
        source = expect_issue subject, <<-CRYSTAL
          def inc(a)
            do_something(a)
            unless a < 0
              return :positive
            # ^^^^^^^^^^^^^^^^ error: Redundant `return` detected
            else
              return :negative
            # ^^^^^^^^^^^^^^^^ error: Redundant `return` detected
            end
          end
          CRYSTAL

        expect_correction source, <<-CRYSTAL
          def inc(a)
            do_something(a)
            unless a < 0
              :positive
            else
              :negative
            end
          end
          CRYSTAL
      end
    end

    context "binary op" do
      it "doesn't report if there is no return in the right binary op node" do
        expect_no_issues subject, <<-CRYSTAL
          def can_create?(a)
            valid? && a > 0
          end
          CRYSTAL
      end

      it "reports if there is return in the right binary op node" do
        source = expect_issue subject, <<-CRYSTAL
          def can_create?(a)
            valid? && return a > 0
                    # ^^^^^^^^^^^^ error: Redundant `return` detected
          end
          CRYSTAL

        expect_correction source, <<-CRYSTAL
          def can_create?(a)
            valid? && a > 0
          end
          CRYSTAL
      end
    end

    context "case" do
      it "reports if there are returns in whens" do
        source = expect_issue subject, <<-CRYSTAL
          def foo(a)
            case a
            when .nil?
              puts "blah"
              return nil
            # ^^^^^^^^^^ error: Redundant `return` detected
            when .blank?
              return ""
            # ^^^^^^^^^ error: Redundant `return` detected
            when true
              true
            end
          end
          CRYSTAL

        expect_correction source, <<-CRYSTAL
          def foo(a)
            case a
            when .nil?
              puts "blah"
              nil
            when .blank?
              ""
            when true
              true
            end
          end
          CRYSTAL
      end

      it "reports if there is return in else" do
        source = expect_issue subject, <<-CRYSTAL
          def foo(a)
            do_something_with(a)

            case a
            when true
              true
            else
              return false
            # ^^^^^^^^^^^^ error: Redundant `return` detected
            end
          end
          CRYSTAL

        expect_correction source, <<-CRYSTAL
          def foo(a)
            do_something_with(a)

            case a
            when true
              true
            else
              false
            end
          end
          CRYSTAL
      end
    end

    context "exception handler" do
      it "reports if there are returns in body" do
        source = expect_issue subject, <<-CRYSTAL
          def foo(a)
            return true
          # ^^^^^^^^^^^ error: Redundant `return` detected
          rescue
            false
          end
          CRYSTAL

        expect_correction source, <<-CRYSTAL
          def foo(a)
            true
          rescue
            false
          end
          CRYSTAL
      end

      it "reports if there are returns in rescues" do
        source = expect_issue subject, <<-CRYSTAL
          def foo(a)
            true
          rescue ArgumentError
            return false
          # ^^^^^^^^^^^^ error: Redundant `return` detected
          rescue RuntimeError
            ""
          rescue Exception
            return nil
          # ^^^^^^^^^^ error: Redundant `return` detected
          end
          CRYSTAL

        expect_correction source, <<-CRYSTAL
          def foo(a)
            true
          rescue ArgumentError
            false
          rescue RuntimeError
            ""
          rescue Exception
            nil
          end
          CRYSTAL
      end

      it "reports if there are returns in else" do
        source = expect_issue subject, <<-CRYSTAL
          def foo(a)
            true
          rescue Exception
            nil
          else
            puts "else branch"
            return :bar
          # ^^^^^^^^^^^ error: Redundant `return` detected
          end
          CRYSTAL

        expect_correction source, <<-CRYSTAL
          def foo(a)
            true
          rescue Exception
            nil
          else
            puts "else branch"
            :bar
          end
          CRYSTAL
      end
    end

    context "properties" do
      context "#allow_multi_return=" do
        it "allows multi returns by default" do
          expect_no_issues subject, <<-CRYSTAL
            def method(a, b)
              return a, b
            end
            CRYSTAL
        end

        it "allows to configure multi returns" do
          rule = Rule::Style::RedundantReturn.new
          rule.allow_multi_return = false
          source = expect_issue rule, <<-CRYSTAL
            def method(a, b)
              return a, b
            # ^^^^^^^^^^^ error: Redundant `return` detected
            end
            CRYSTAL

          expect_correction source, <<-CRYSTAL
            def method(a, b)
              {a, b}
            end
            CRYSTAL
        end
      end

      context "#allow_empty_return" do
        it "allows empty returns by default" do
          expect_no_issues subject, <<-CRYSTAL
            def method
              return
            end
            CRYSTAL
        end

        it "allows to configure empty returns" do
          rule = Rule::Style::RedundantReturn.new
          rule.allow_empty_return = false
          source = expect_issue rule, <<-CRYSTAL
            def method
              return
            # ^^^^^^ error: Redundant `return` detected
            end
            CRYSTAL

          expect_no_corrections source
        end
      end
    end
  end
end
