From e721ad1b116a0e0dbd80233066591e014be6f074 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 23 Aug 2026 12:53:18 +0000 Subject: [PATCH] Drop runtime support for Ruby 3.2 Ruby 3.2 reached EOL on 2026-04-01, and CI has been running 3.3, 3.4, 4.0 and head for a while, so 3.2 is declared as supported without being tested. Raise `required_ruby_version` to `>= 3.3` and delete the code that only existed for 3.2: - `Prototype::Helpers#parse_comments` had a Ripper implementation for 3.2, where Prism cannot parse the source. Only the Prism one is left. - The `RUBY_VERSION >= '3.2'` guards around `Data` in the runtime prototype are always true now. `.rubocop.yml` pinned `TargetRubyVersion` to 3.4 while the gem accepted 3.2. Drop the setting rather than correct it: with nothing pinned, RuboCop reads `required_ruby_version` from the gemspec, so the two cannot drift apart again. The tree is clean at the inferred 3.3. The `if false` block that set `required_ruby_version` to `>= 3.4` is dead code from an earlier experiment and goes with it. Closes #2830 Co-Authored-By: Claude --- .rubocop.yml | 1 - lib/rbs/prototype/helpers.rb | 71 +++++-------------- lib/rbs/prototype/runtime.rb | 2 +- .../runtime/value_object_generator.rb | 1 - rbs.gemspec | 6 +- 5 files changed, 21 insertions(+), 60 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index b43b2f141c..1eed1a8355 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -3,7 +3,6 @@ plugins: - rubocop-on-rbs AllCops: - TargetRubyVersion: 3.4 DisabledByDefault: true Exclude: - 'vendor/bundle/**/*' diff --git a/lib/rbs/prototype/helpers.rb b/lib/rbs/prototype/helpers.rb index df28a8eed9..f508f45c5b 100644 --- a/lib/rbs/prototype/helpers.rb +++ b/lib/rbs/prototype/helpers.rb @@ -5,58 +5,25 @@ module Prototype module Helpers private - # Prism can't parse Ruby 3.2 code - if RUBY_VERSION >= "3.3" - def parse_comments(string, include_trailing:) - Prism.parse_comments(string, version: "current").yield_self do |prism_comments| # steep:ignore UnexpectedKeywordArgument - prism_comments.each_with_object({}) do |comment, hash| #$ Hash[Integer, AST::Comment] - # Skip EmbDoc comments - next unless comment.is_a?(Prism::InlineComment) - # skip like `module Foo # :nodoc:` - next if comment.trailing? && !include_trailing - - line = comment.location.start_line - body = "#{comment.location.slice}\n" - body = body[2..-1] or raise - body = "\n" if body.empty? - - comment = AST::Comment.new(string: body, location: nil) - if prev_comment = hash.delete(line - 1) - hash[line] = AST::Comment.new(string: prev_comment.string + comment.string, - location: nil) - else - hash[line] = comment - end - end - end - end - else - require "ripper" - def parse_comments(string, include_trailing:) - Ripper.lex(string).yield_self do |tokens| - code_lines = {} #: Hash[Integer, bool] - tokens.each.with_object({}) do |token, hash| #$ Hash[Integer, AST::Comment] - case token[1] - when :on_sp, :on_ignored_nl - # skip - when :on_comment - line = token[0][0] - # skip like `module Foo # :nodoc:` - next if code_lines[line] && !include_trailing - body = token[2][2..-1] or raise - - body = "\n" if body.empty? - - comment = AST::Comment.new(string: body, location: nil) - if prev_comment = hash.delete(line - 1) - hash[line] = AST::Comment.new(string: prev_comment.string + comment.string, - location: nil) - else - hash[line] = comment - end - else - code_lines[token[0][0]] = true - end + def parse_comments(string, include_trailing:) + Prism.parse_comments(string, version: "current").yield_self do |prism_comments| # steep:ignore UnexpectedKeywordArgument + prism_comments.each_with_object({}) do |comment, hash| #$ Hash[Integer, AST::Comment] + # Skip EmbDoc comments + next unless comment.is_a?(Prism::InlineComment) + # skip like `module Foo # :nodoc:` + next if comment.trailing? && !include_trailing + + line = comment.location.start_line + body = "#{comment.location.slice}\n" + body = body[2..-1] or raise + body = "\n" if body.empty? + + comment = AST::Comment.new(string: body, location: nil) + if prev_comment = hash.delete(line - 1) + hash[line] = AST::Comment.new(string: prev_comment.string + comment.string, + location: nil) + else + hash[line] = comment end end end diff --git a/lib/rbs/prototype/runtime.rb b/lib/rbs/prototype/runtime.rb index c209f9ad01..6cf73b879b 100644 --- a/lib/rbs/prototype/runtime.rb +++ b/lib/rbs/prototype/runtime.rb @@ -527,7 +527,7 @@ def generate_class(mod) generate_mixin(mod, decl, type_name, type_name_absolute) - unless mod < Struct || (RUBY_VERSION >= '3.2' && mod < Data) + unless mod < Struct || mod < Data generate_methods(mod, type_name, decl.members) unless outline end diff --git a/lib/rbs/prototype/runtime/value_object_generator.rb b/lib/rbs/prototype/runtime/value_object_generator.rb index ab984e1311..876d793dd5 100644 --- a/lib/rbs/prototype/runtime/value_object_generator.rb +++ b/lib/rbs/prototype/runtime/value_object_generator.rb @@ -212,7 +212,6 @@ def build_s_keyword_init_p class DataGenerator < ValueObjectBase def self.generatable?(target) - return false unless RUBY_VERSION >= '3.2' return false unless target < Data # Avoid direct inherited class like `class Option < Data` return false unless target.respond_to?(:members) diff --git a/rbs.gemspec b/rbs.gemspec index 65782803bc..f699e7411d 100644 --- a/rbs.gemspec +++ b/rbs.gemspec @@ -67,14 +67,10 @@ Gem::Specification.new do |spec| spec.extensions = %w{ext/rbs_extension/extconf.rb} end - if false - spec.required_ruby_version = ">= 3.4" - end - spec.bindir = "exe" spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] - spec.required_ruby_version = ">= 3.2" + spec.required_ruby_version = ">= 3.3" spec.add_dependency "logger" spec.add_dependency "prism", ">= 1.6.0" spec.add_dependency "tsort"