diff --git a/lib/rbs/collection/config/lockfile_generator.rb b/lib/rbs/collection/config/lockfile_generator.rb index ebf4ab71c3..3426c31e32 100644 --- a/lib/rbs/collection/config/lockfile_generator.rb +++ b/lib/rbs/collection/config/lockfile_generator.rb @@ -4,18 +4,22 @@ module RBS module Collection class Config class LockfileGenerator + # Stdlibs that used to be bundled in rbs-gem but are now maintained in their own gems. + # + # The value is the version requirement of the gem that ships the type definitions in its `sig/` directory, + # or `nil` if no released version of the gem ships them yet. ALUMNI_STDLIBS = { "mutex_m" => ">= 0.3.0", "abbrev" => nil, - "base64" => nil, + "base64" => ">= 0.3.0", "benchmark" => nil, - "bigdecimal" => nil, + "bigdecimal" => ">= 4.1.0", "csv" => nil, "kconv" => nil, "logger" => nil, "minitest" => nil, "net-smtp" => nil, - "nkf" => nil, + "nkf" => ">= 0.3.0", "observer" => nil, "cgi" => nil, "pstore" => nil, @@ -190,11 +194,14 @@ def generate case name when 'bigdecimal-math', 'kconv' - # These gems are never released as a gem. - # Therefore, `assign_gem` should not be called. + # These libraries are never released as a gem, so `assign_gem` should not be called. + # Their type definitions are shipped in the `sig/` directory of the parent gem + # (`bigdecimal` for `bigdecimal-math`, `nkf` for `kconv`) instead. + parent_gem = name == 'kconv' ? 'nkf' : 'bigdecimal' + parent_version = ALUMNI_STDLIBS.fetch(parent_gem) or raise RBS.logger.info { from = from_gem || "rbs_collection.yaml" - "`#{name}` is included in the RBS dependencies of `#{from}`, but the type definition as a stdlib in rbs-gem is deprecated. Delete `#{name}` from the RBS dependencies of `#{from}`." + "`#{name}` is included in the RBS dependencies of `#{from}`, but the type definition as a stdlib in rbs-gem is deprecated. Delete `#{name}` from the RBS dependencies of `#{from}` and add `#{parent_gem}` (#{parent_version}) to the dependency of your Ruby program to use the gem-bundled type definition." } source = find_source(name: name) if source&.is_a?(Sources::Stdlib) diff --git a/sig/collection/config/lockfile_generator.rbs b/sig/collection/config/lockfile_generator.rbs index 066f9f4fc3..ead55b19e8 100644 --- a/sig/collection/config/lockfile_generator.rbs +++ b/sig/collection/config/lockfile_generator.rbs @@ -4,6 +4,8 @@ module RBS class LockfileGenerator # Name of stdlibs that was rbs-bundled stdlib but is now a gem. # + # The value is the version requirement of the gem that ships the type definitions, or `nil` if not shipped yet. + # ALUMNI_STDLIBS: Hash[String, String?] NONGEM_STDLIBS: Set[String] diff --git a/test/rbs/collection/config_test.rb b/test/rbs/collection/config_test.rb index bc524dfa1b..9df0b4fced 100644 --- a/test/rbs/collection/config_test.rb +++ b/test/rbs/collection/config_test.rb @@ -401,6 +401,61 @@ def test_generate_lock_from_rubygems end end + EMPTY_GEMFILE_LOCK = <<~GEMFILE_LOCK + GEM + remote: https://rubygems.org/ + specs: + + PLATFORMS + x86_64-linux + + DEPENDENCIES + + BUNDLED WITH + 2.2.0 + GEMFILE_LOCK + + def test_generate_lock_from_alumni_stdlib__kconv + # `kconv` is never released as a gem; its types are shipped with the `nkf` gem. + mktmpdir do |tmpdir| + config_path = tmpdir / 'rbs_collection.yaml' + config_path.write <<~YAML + sources: + - name: ruby/gem_rbs_collection + remote: https://github.com/ruby/gem_rbs_collection.git + revision: cde6057e7546843ace6420c5783dd945c6ccda54 + repo_dir: gems + + path: /path/to/somewhere + + gems: + - name: kconv + YAML + gemfile_path = tmpdir / 'Gemfile' + gemfile_path.write 'source "https://rubygems.org"' + gemfile_lock_path = tmpdir / 'Gemfile.lock' + gemfile_lock_path.write EMPTY_GEMFILE_LOCK + + definition = Bundler::Definition.build(gemfile_path, gemfile_lock_path, false) + lockfile, log = with_logger_output(level: Logger::INFO) do + _config, lockfile = RBS::Collection::Config.generate_lockfile(config_path: config_path, definition: definition) + lockfile + end + + assert_config <<~YAML, YAML.dump(lockfile.to_lockfile) + path: "/path/to/somewhere" + gemfile_lock_path: 'Gemfile.lock' + gems: + - name: kconv + version: "0" + source: + type: stdlib + YAML + assert_include log, '`kconv` is included in the RBS dependencies of `rbs_collection.yaml`, but the type definition as a stdlib in rbs-gem is deprecated.' + assert_include log, 'add `nkf` (>= 0.3.0) to the dependency of your Ruby program' + end + end + def test_generate_lock_with_empty_gemfile_lock mktmpdir do |tmpdir| config_path = tmpdir / 'rbs_collection.yaml' @@ -870,4 +925,18 @@ def test_generate_lockfile_from_mismatched_lockfiles yield Pathname(path) end end + + # Runs the block with `RBS.logger` writing to a string, and returns `[block_result, logged_string]`. + private def with_logger_output(level:) + orig_output = RBS.logger_output + orig_level = RBS.logger_level + io = StringIO.new + RBS.logger_output = io + RBS.logger_level = level + result = yield + [result, io.string] + ensure + RBS.logger_output = orig_output + RBS.logger_level = orig_level + end end