Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion lib/spoom/cli/srb/bump.rb
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ def bump(directory = ".")
end

if result.status
parse_result = Sorbet::Errors::Parser.parse_result(result.err, error_url_base: error_url_base)
parse_result.warnings.each do |warning|
say_error(warning, status: nil, nl: false)
end
Sorbet::Sigils.change_sigil_in_files(files_to_bump, to) unless dry
print_changes(files_to_bump, command: cmd, from: from, to: to, dry: dry, path: exec_path)
exit(files_to_bump.empty?)
Expand All @@ -147,7 +151,11 @@ def bump(directory = ".")
exit(1)
end

errors = Sorbet::Errors::Parser.parse_string(result.err, error_url_base: error_url_base)
parse_result = Sorbet::Errors::Parser.parse_result(result.err, error_url_base: error_url_base)
parse_result.warnings.each do |warning|
say_error(warning, status: nil, nl: false)
end
errors = parse_result.errors

all_files = errors.flat_map do |err|
[err.file, *err.files_from_error_sections]
Expand Down
6 changes: 5 additions & 1 deletion lib/spoom/cli/srb/tc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,11 @@ def tc(*paths_to_select)
exit(1)
end

errors = Spoom::Sorbet::Errors::Parser.parse_string(result.err, error_url_base: error_url_base)
parse_result = Spoom::Sorbet::Errors::Parser.parse_result(result.err, error_url_base: error_url_base)
parse_result.warnings.each do |warning|
say_error(warning, status: nil, nl: false)
end
errors = parse_result.errors
errors_count = errors.size

errors = errors.select { |e| e.code == code } if code
Expand Down
42 changes: 39 additions & 3 deletions lib/spoom/sorbet/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,22 +56,35 @@ class ParseError < Spoom::Error; end
] #: Array[String]

class << self
# Used when only Sorbet errors are needed and leading stderr warnings can be ignored.
#: (String output, ?error_url_base: String) -> Array[Error]
def parse_string(output, error_url_base: DEFAULT_ERROR_URL_BASE)
parse_result(output, error_url_base: error_url_base).errors
end
Comment on lines +59 to +63

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we just change this one to return a ParseResult?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what I had initially but Tapioca uses parse_string in here. Which I had to execute during https://github.com/Shopify/spoom/blob/main/bin/export-rbi. So I went with a new function instead to be backwards compatible and ship this easier.


# Used when callers need both parsed Sorbet errors and leading stderr warnings.
#: (String output, ?error_url_base: String) -> ParseResult
def parse_result(output, error_url_base: DEFAULT_ERROR_URL_BASE)
parser = Spoom::Sorbet::Errors::Parser.new(error_url_base: error_url_base)
parser.parse(output)
parser.parse_result(output)
end
end

#: (?error_url_base: String) -> void
def initialize(error_url_base: DEFAULT_ERROR_URL_BASE)
@errors = [] #: Array[Error]
@warnings = [] #: Array[String]
@error_line_match_regex = error_line_match_regexp(error_url_base) #: Regexp
@current_error = nil #: Error?
end

#: (String output) -> Array[Error]
def parse(output)
parse_result(output).errors
end

Comment on lines 81 to +85

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, let's just return a ParseResult directly?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could although it's public API and would break callers if there are any

#: (String output) -> ParseResult
def parse_result(output)
output.each_line do |line|
break if /^No errors! Great job\./.match?(line)
break if /^Errors: /.match?(line)
Expand All @@ -85,10 +98,14 @@ def parse(output)
next
end

append_error(line) if @current_error
if @current_error
append_error(line)
else
append_warning(line)
end
end
close_error if @current_error
@errors
ParseResult.new(@errors, @warnings)
end

private
Expand Down Expand Up @@ -144,6 +161,11 @@ def append_error(line)
end
@current_error.more << line
end

#: (String warning) -> void
def append_warning(warning)
@warnings << warning
end
end

class Error
Expand Down Expand Up @@ -214,6 +236,20 @@ def to_junit_xml_element
testcase_element
end
end

class ParseResult
#: Array[Error]
attr_reader :errors

#: Array[String]
attr_reader :warnings

#: (Array[Error] errors, Array[String] warnings) -> void
def initialize(errors, warnings)
@errors = errors
@warnings = warnings
end
end
end
end
end
20 changes: 20 additions & 0 deletions rbi/spoom.rbi
Original file line number Diff line number Diff line change
Expand Up @@ -2753,18 +2753,35 @@ class Spoom::Sorbet::Errors::Error
def to_s; end
end

class Spoom::Sorbet::Errors::ParseResult
sig { params(errors: T::Array[::Spoom::Sorbet::Errors::Error], warnings: T::Array[::String]).void }
def initialize(errors, warnings); end

sig { returns(T::Array[::Spoom::Sorbet::Errors::Error]) }
def errors; end

sig { returns(T::Array[::String]) }
def warnings; end
end

class Spoom::Sorbet::Errors::Parser
sig { params(error_url_base: ::String).void }
def initialize(error_url_base: T.unsafe(nil)); end

sig { params(output: ::String).returns(T::Array[::Spoom::Sorbet::Errors::Error]) }
def parse(output); end

sig { params(output: ::String).returns(::Spoom::Sorbet::Errors::ParseResult) }
def parse_result(output); end

private

sig { params(line: ::String).void }
def append_error(line); end

sig { params(warning: ::String).void }
def append_warning(warning); end

sig { void }
def close_error; end

Expand All @@ -2778,6 +2795,9 @@ class Spoom::Sorbet::Errors::Parser
def open_error(error); end

class << self
sig { params(output: ::String, error_url_base: ::String).returns(::Spoom::Sorbet::Errors::ParseResult) }
def parse_result(output, error_url_base: T.unsafe(nil)); end

sig { params(output: ::String, error_url_base: ::String).returns(T::Array[::Spoom::Sorbet::Errors::Error]) }
def parse_string(output, error_url_base: T.unsafe(nil)); end
end
Expand Down
53 changes: 53 additions & 0 deletions test/spoom/cli/srb/bump_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,59 @@ def foo
assert_equal("false", @project.read_file_strictness("file1.rb"))
end

def test_bump_displays_sorbet_warnings
@project.write!("mock_sorbet", <<~RB)
#!/usr/bin/env ruby
$stderr.puts "Option `--enable-experimental-rbs-signatures` has been combined into another option."
$stderr.puts "file.rb:1: Method `foo` does not exist on `T.class_of(<root>)` https://srb.help/7003"
exit(100)
RB
@project.exec("chmod +x mock_sorbet")
@project.write!("file.rb", <<~RB)
# typed: false
foo
RB

result = @project.spoom("srb bump --no-color --sorbet #{@project.absolute_path}/mock_sorbet")
assert_equal(<<~ERR, result.err)
Option `--enable-experimental-rbs-signatures` has been combined into another option.
ERR
assert_equal(<<~OUT, result.out)
Checking files...

No files to bump from `false` to `true`
OUT
assert(result.status)
assert_equal("false", @project.read_file_strictness("file.rb"))
end

def test_bump_displays_sorbet_warnings_without_errors
@project.write!("mock_sorbet", <<~RB)
#!/usr/bin/env ruby
$stderr.puts "Option `--enable-experimental-rbs-signatures` has been combined into another option."
$stderr.puts "No errors! Great job."
exit(0)
RB
@project.exec("chmod +x mock_sorbet")
@project.write!("file.rb", <<~RB)
# typed: false
class Foo; end
RB

result = @project.spoom("srb bump --no-color --sorbet #{@project.absolute_path}/mock_sorbet")
assert_equal(<<~ERR, result.err)
Option `--enable-experimental-rbs-signatures` has been combined into another option.
ERR
assert_equal(<<~OUT, result.out)
Checking files...

Bumped `1` file from `false` to `true`:
+ file.rb
OUT
refute(result.status)
assert_equal("true", @project.read_file_strictness("file.rb"))
end

def test_bump_with_sorbet_segfault
# Create a fake Sorbet that will segfault
@project.write!("mock_sorbet", <<~RB)
Expand Down
18 changes: 18 additions & 0 deletions test/spoom/cli/srb/tc_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,24 @@ def test_display_sorbet_error
refute(result.status)
end

def test_display_sorbet_warnings_with_formatted_errors
@project.write!("mock_sorbet", <<~RB)
#!/usr/bin/env ruby
$stderr.puts "Option `--enable-experimental-rbs-signatures` has been combined into another option."
$stderr.puts "foo.rb:1: Method `foo` does not exist on `T.class_of(<root>)` https://srb.help/7003"
exit(100)
RB
@project.exec("chmod +x mock_sorbet")

result = @project.spoom("srb tc --no-color --sorbet #{@project.absolute_path}/mock_sorbet")
assert_equal(<<~MSG, result.err)
Option `--enable-experimental-rbs-signatures` has been combined into another option.
7003 - foo.rb:1: Method `foo` does not exist on `T.class_of(<root>)`
Errors: 1
MSG
refute(result.status)
end

def test_deprecated_command_spoom_tc
@project.remove!("errors")
result = @project.spoom("tc --no-color")
Expand Down
27 changes: 27 additions & 0 deletions test/spoom/sorbet/errors_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,33 @@ def test_parses_errors_with_debug_string
assert_equal([[], []], errors.map(&:files_from_error_sections).map(&:to_a))
end

def test_parse_result_preserves_warnings
result = Spoom::Sorbet::Errors::Parser.parse_result(<<~ERR)
Option `--enable-experimental-rbs-signatures` has been combined into another option.
Please add `--parser=prism` to your Sorbet config.

a.rb:80: unexpected token "end" https://srb.help/2001
80 |end
^^^
Errors: 1
ERR

assert_equal(
[
"Option `--enable-experimental-rbs-signatures` has been combined into another option.\n",
"Please add `--parser=prism` to your Sorbet config.\n",
],
result.warnings,
)
assert_equal(1, result.errors.size)

error = T.must(result.errors.first)
assert_equal("a.rb", error.file)
assert_equal(80, error.line)
assert_equal("unexpected token \"end\"", error.message)
assert_equal(2001, error.code)
end

def test_parses_errors_with_multiple_blank_lines
errors = Spoom::Sorbet::Errors::Parser.parse_string(<<~ERR)
lib/a.rb:54: Method `foo` does not exist on `String` https://srb.help/7003
Expand Down
Loading