Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check duplicates between shims and Sorbet's RBI payload #879

Merged
merged 7 commits into from Apr 7, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -257,6 +257,7 @@ check_shims:
gem_rbi_dir: sorbet/rbi/gems
dsl_rbi_dir: sorbet/rbi/dsl
shim_rbi_dir: sorbet/rbi/shims
payload: true
```
<!-- END_CONFIG_TEMPLATE -->

Expand Down
32 changes: 31 additions & 1 deletion lib/tapioca/cli.rb
Expand Up @@ -5,6 +5,7 @@ module Tapioca
class Cli < Thor
include CliHelper
include ConfigHelper
include SorbetHelper
include ShimsHelper

FILE_HEADER_OPTION_DESC = "Add a \"This file is generated\" header on top of each generated RBI file"
Expand Down Expand Up @@ -242,6 +243,7 @@ def gem(*gems)
option :gem_rbi_dir, type: :string, desc: "Path to gem RBIs", default: DEFAULT_GEM_DIR
option :dsl_rbi_dir, type: :string, desc: "Path to DSL RBIs", default: DEFAULT_DSL_DIR
option :shim_rbi_dir, type: :string, desc: "Path to shim RBIs", default: DEFAULT_SHIM_DIR
option :payload, type: :boolean, desc: "Check shims against Sorbet's payload", default: true
def check_shims
index = RBI::Index.new

Expand All @@ -251,6 +253,30 @@ def check_shims
exit(0)
end

payload_path = T.let(nil, T.nilable(String))

if options[:payload]
if sorbet_supports?(:print_payload_sources)
Dir.mktmpdir do |dir|
payload_path = dir
result = sorbet("--no-config --print=payload-sources:#{payload_path}")

unless result.status
say_error("Sorbet failed to dump payload")
say_error(result.err)
exit(1)
end

index_payload(index, payload_path)
end
else
say_error("The version of Sorbet used in your Gemfile.lock does not support `--print=payload-sources`")
say_error("Current: v#{SORBET_GEM_SPEC.version}")
say_error("Required: #{FEATURE_REQUIREMENTS[:print_payload_sources]}")
exit(1)
end
end

index_rbis(index, "shim", shim_rbi_dir)
index_rbis(index, "gem", options[:gem_rbi_dir])
index_rbis(index, "dsl", options[:dsl_rbi_dir])
Expand All @@ -260,7 +286,11 @@ def check_shims
duplicates.each do |key, nodes|
say_error("\nDuplicated RBI for #{key}:", :red)
nodes.each do |node|
say_error(" * #{node.loc}", :red)
node_loc = node.loc
next unless node_loc

loc_string = location_to_payload_url(node_loc, path_prefix: payload_path)
say_error(" * #{loc_string}", :red)
end
end
say_error("\nPlease remove the duplicated definitions from the #{shim_rbi_dir} directory.", :red)
Expand Down
43 changes: 35 additions & 8 deletions lib/tapioca/helpers/shims_helper.rb
Expand Up @@ -8,20 +8,25 @@ module ShimsHelper

requires_ancestor { Thor::Shell }

SORBET_PAYLOAD_URL = "https://github.com/sorbet/sorbet/tree/master/rbi"

sig { params(index: RBI::Index, dir: String).void }
def index_payload(index, dir)
return unless Dir.exist?(dir)

say("Loading Sorbet payload... ")
files = Dir.glob("#{dir}/**/*.rbi").sort
parse_and_index_files(index, files)
say(" Done", :green)
end

sig { params(index: RBI::Index, kind: String, dir: String).void }
def index_rbis(index, kind, dir)
return unless Dir.exist?(dir) && !Dir.empty?(dir)

say("Loading #{kind} RBIs from #{dir}... ")
files = Dir.glob("#{dir}/**/*.rbi").sort

trees = files.map do |file|
RBI::Parser.parse_file(file)
rescue RBI::ParseError => e
say_error("\nWarning: #{e} (#{e.location})", :yellow)
end.compact

index.visit_all(trees)
parse_and_index_files(index, files)
say(" Done", :green)
end

Expand All @@ -39,8 +44,30 @@ def duplicated_nodes_from_index(index, shim_rbi_dir)
duplicates
end

sig { params(loc: RBI::Loc, path_prefix: T.nilable(String)).returns(String) }
def location_to_payload_url(loc, path_prefix:)
return loc.to_s unless path_prefix

url = loc.file || ""
return loc.to_s unless url.start_with?(path_prefix)

url = url.sub(path_prefix, SORBET_PAYLOAD_URL)
url = "#{url}#L#{loc.begin_line}"
url
end

private

sig { params(index: RBI::Index, files: T::Array[String]).void }
def parse_and_index_files(index, files)
trees = files.map do |file|
RBI::Parser.parse_file(file)
rescue RBI::ParseError => e
say_error("\nWarning: #{e} (#{e.location})", :yellow)
end.compact
index.visit_all(trees)
end

sig { params(nodes: T::Array[RBI::Node], shim_rbi_dir: String).returns(T::Boolean) }
def shims_have_duplicates?(nodes, shim_rbi_dir)
return false if nodes.size == 1
Expand Down
4 changes: 2 additions & 2 deletions lib/tapioca/helpers/sorbet_helper.rb
Expand Up @@ -21,8 +21,8 @@ module SorbetHelper
SORBET_EXE_PATH_ENV_VAR = "TAPIOCA_SORBET_EXE"

FEATURE_REQUIREMENTS = T.let({
# First tag that includes https://github.com/sorbet/sorbet/pull/4706
to_ary_nil_support: ::Gem::Requirement.new(">= 0.5.9220"),
to_ary_nil_support: ::Gem::Requirement.new(">= 0.5.9220"), # https://github.com/sorbet/sorbet/pull/4706
print_payload_sources: ::Gem::Requirement.new(">= 0.5.9818"), # https://github.com/sorbet/sorbet/pull/5504
}.freeze, T::Hash[Symbol, ::Gem::Requirement])

sig { params(sorbet_args: String).returns(Spoom::ExecResult) }
Expand Down
6 changes: 0 additions & 6 deletions sorbet/rbi/shims/bundler.rbi

This file was deleted.