# frozen_string_literal: true

return if RUBY_VERSION < "3.1"

require_relative "test_helper"
require "stringio"

module Prism
  class WarningsTest < TestCase
    def test_ambiguous_uminus
      assert_warning("a -b", "ambiguous first argument")
    end

    def test_ambiguous_uplus
      assert_warning("a +b", "ambiguous first argument")
    end

    def test_ambiguous_ustar
      assert_warning("a *b", "argument prefix")
    end

    def test_ambiguous_regexp
      assert_warning("a /b/", "wrap regexp in parentheses")
    end

    def test_equal_in_conditional
      assert_warning("if a = 1; end", "should be ==")
    end

    def test_dot_dot_dot_eol
      assert_warning("foo...", "... at EOL")
      assert_warning("def foo(...) = bar ...", "... at EOL")

      assert_warning("foo... #", "... at EOL")
      assert_warning("foo... \t\v\f\n", "... at EOL")

      refute_warning("p foo...bar")
      refute_warning("p foo...      bar")
    end

    def test_END_in_method
      assert_warning("def foo; END {}; end", "END in method")
    end

    def test_duplicated_hash_key
      assert_warning("{ a: 1, a: 2 }", "duplicated and overwritten")
    end

    def test_duplicated_when_clause
      assert_warning("case 1; when 1, 1; end", "clause with line")
    end

    def test_float_out_of_range
      assert_warning("1.0e100000", "out of range")
    end

    def test_integer_in_flip_flop
      assert_warning("1 if 2..foo", "integer")
    end

    def test_keyword_eol
      assert_warning("if\ntrue; end", "end of line")
      assert_warning("if true\nelsif\nfalse; end", "end of line")
    end

    def test_string_in_predicate
      assert_warning("if 'foo'; end", "string")
      assert_warning("if \"\#{foo}\"; end", "string")
      assert_warning("if __FILE__; end", "string")
    end

    def test_symbol_in_predicate
      assert_warning("if :foo; end", "symbol")
      assert_warning("if :\"\#{foo}\"; end", "symbol")
    end

    def test_literal_in_predicate
      assert_warning("if __LINE__; end", "literal")
      assert_warning("if __ENCODING__; end", "literal")
      assert_warning("if 1; end", "literal")
      assert_warning("if 1.0; end", "literal")
      assert_warning("if 1r; end", "literal")
      assert_warning("if 1i; end", "literal")
    end

    def test_regexp_in_predicate
      assert_warning("if /foo/; end", "regex")
      assert_warning("if /foo\#{bar}/; end", "regex")
    end

    private

    def assert_warning(source, message)
      warnings = Prism.parse(source).warnings

      assert_equal 1, warnings.length
      assert_include warnings.first.message, message

      if defined?(RubyVM::AbstractSyntaxTree)
        assert_include capture_warning { RubyVM::AbstractSyntaxTree.parse(source) }, message
      end
    end

    def refute_warning(source)
      assert_empty Prism.parse(source).warnings

      if defined?(RubyVM::AbstractSyntaxTree)
        assert_empty capture_warning { RubyVM::AbstractSyntaxTree.parse(source) }
      end
    end

    def capture_warning
      stderr, $stderr, verbose, $VERBOSE = $stderr, StringIO.new, $VERBOSE, true

      begin
        yield
        $stderr.string
      ensure
        $stderr, $VERBOSE = stderr, verbose
      end
    end
  end
end
