Skip to content
Open
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
74 changes: 73 additions & 1 deletion lib/tapioca/helpers/rbi_files_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,20 @@ def validate_rbi_files(command:, gem_dir:, dsl_dir:, auto_strictness:, gems: [],

errors = Spoom::Sorbet::Errors::Parser.parse_string(res.err || "")

if errors.empty?
payload_superclass_errors = T.let([], T::Array[Spoom::Sorbet::Errors::Error])
if auto_strictness
payload_superclass_res = sorbet(
"--no-config",
"--error-url-base=#{error_url_base}",
dsl_dir,
gem_dir,
)
payload_superclass_errors = Spoom::Sorbet::Errors::Parser
.parse_string(payload_superclass_res.err || "")
.select { |error| error.code == 5012 }
end

if errors.empty? && payload_superclass_errors.empty?
say(" No errors found\n\n", [:green, :bold])

return
Expand Down Expand Up @@ -132,6 +145,7 @@ def validate_rbi_files(command:, gem_dir:, dsl_dir:, auto_strictness:, gems: [],
if auto_strictness
redef_errors = errors.select { |error| error.code == 4010 }
update_gem_rbis_strictnesses(redef_errors, gem_dir)
update_sorbet_config_for_payload_superclass_redefinitions(payload_superclass_errors)
end

Kernel.raise Tapioca::Error, error_messages.join("\n") if parse_errors.any?
Expand Down Expand Up @@ -258,6 +272,64 @@ def extract_methods_and_attrs(nodes)
)
end

SUPPRESS_PAYLOAD_SUPERCLASS_REDEFINITION_FLAG =
"--suppress-payload-superclass-redefinition-for" #: String

#: (Array[Spoom::Sorbet::Errors::Error] errors) -> void
def update_sorbet_config_for_payload_superclass_redefinitions(errors)
errors
.filter_map { |error| payload_superclass_constant_from_error(error) }
.uniq
.each { |constant| add_payload_superclass_suppression_to_config(constant) }
end

#: (Spoom::Sorbet::Errors::Error error) -> String?
def payload_superclass_constant_from_error(error)
if error.message =~ /Parent of class `([^`]+)` redefined/
return T.must(Regexp.last_match(1))
end

error.more.each do |line|
if line =~ /--suppress-payload-superclass-redefinition-for=([^\s`]+)/
return T.must(Regexp.last_match(1))
end
end

nil
end

#: (String constant) -> void
def add_payload_superclass_suppression_to_config(constant)
flag = "#{SUPPRESS_PAYLOAD_SUPERCLASS_REDEFINITION_FLAG}=#{constant}"
config_path = Tapioca::SORBET_CONFIG_FILE
config = File.exist?(config_path) ? File.read(config_path) : ""
added = !config.lines(chomp: true).include?(flag)

if added
FileUtils.mkdir_p(File.dirname(config_path))
if config.empty?
File.write(config_path, "#{flag}\n")
else
suffix = config.end_with?("\n") ? "" : "\n"
File.write(config_path, "#{config}#{suffix}#{flag}\n")
end
end

if added
say(
"\n Added `#{flag}` to sorbet/config (payload superclass of `#{constant}` was redefined)",
[:yellow, :bold],
)
else
say(
"\n Payload superclass of `#{constant}` was redefined; `#{flag}` is already in sorbet/config",
[:yellow, :bold],
)
end

say("\n")
end

#: (Array[Spoom::Sorbet::Errors::Error] errors, String gem_dir) -> void
def update_gem_rbis_strictnesses(errors, gem_dir)
files = []
Expand Down
55 changes: 55 additions & 0 deletions spec/tapioca/cli/gem_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1875,6 +1875,61 @@ def foo; end

@project.remove!("sorbet/rbi/shims/foo.rbi")
end

it "must add a payload superclass redefinition suppression to sorbet/config" do
config = @project.read("sorbet/config")
@project.write!(
"sorbet/config",
"#{config.rstrip}\n--suppress-payload-superclass-redefinition-for=Net::IMAP::CommandData\n",
)

@project.write!("sorbet/rbi/gems/bar@0.3.0.rbi", <<~RBI)
# typed: true

module Bar
end

class Net::IMAP::Literal < ::String
end
RBI

result = @project.tapioca("gem foo")

assert_stdout_includes(result, <<~OUT)
Checking generated RBI files... Done


Added `--suppress-payload-superclass-redefinition-for=Net::IMAP::Literal` to sorbet/config (payload superclass of `Net::IMAP::Literal` was redefined)
OUT

assert_empty_stderr(result)
assert_success_status(result)

config = @project.read("sorbet/config")
assert_equal(
1,
config.lines(chomp: true).count("--suppress-payload-superclass-redefinition-for=Net::IMAP::Literal"),
)
assert_equal(
1,
config.lines(chomp: true).count("--suppress-payload-superclass-redefinition-for=Net::IMAP::CommandData"),
)

result = @project.tapioca("gem foo")

assert_stdout_includes(result, <<~OUT)
Payload superclass of `Net::IMAP::Literal` was redefined; `--suppress-payload-superclass-redefinition-for=Net::IMAP::Literal` is already in sorbet/config
OUT

config = @project.read("sorbet/config")
assert_equal(
1,
config.lines(chomp: true).count("--suppress-payload-superclass-redefinition-for=Net::IMAP::Literal"),
)

assert_empty_stderr(result)
assert_success_status(result)
end
end

describe "sanity" do
Expand Down
Loading