Skip to content

Commit 2059d28

Browse files
committed
Support Prism as a Ruby parser
Follow up rubocop/rubocop-ast#277 In Prism (`Prism::Translation::Parser`), `match-with-lvasgn` can be distinctly differentiated. It is unclear whether to conform to the current behavior of the Parser gem, but initially, `def_node_matcher` has been updated to accept the following incompatibilities for `Performance/EndWith`, `Performance/StringInclude`, and `Performance/StartWith` cops to ensure it works with Prism 0.24.0 as well. ## Parser gem Returns an `match_with_lvasgn` node: ```console $ bundle exec ruby -rparser/ruby33 -ve 'p Parser::Ruby33.parse("/foo/ =~ bar")' ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [x86_64-darwin22] s(:match_with_lvasgn, s(:regexp, s(:str, "foo"), s(:regopt)), s(:send, nil, :bar)) ``` Returns an `match_with_lvasgn` node: ```console $ bundle exec ruby -rparser/ruby33 -ve 'p Parser::Ruby33.parse("/(?<foo>)foo/ =~ bar")' ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [x86_64-darwin22] s(:match_with_lvasgn, s(:regexp, s(:str, "(?<foo>)foo"), s(:regopt)), s(:send, nil, :bar)) ``` This lvar-injecting feature appears to have not been supported by Parser gem for a long time: whitequark/parser#69 (comment) ## Prism Returns an `send` node: ```console $ bundle exec ruby -Ilib -rprism -rprism/translation/parser33 -ve 'p Prism::Translation::Parser33.parse("/foo/ =~ bar")' ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [x86_64-darwin22] s(:send, s(:regexp, s(:str, "foo"), s(:regopt)), :=~, s(:send, nil, :bar)) ``` Returns an `match_with_lvasgn` node: ```console $ bundle exec ruby -Ilib -rprism -rprism/translation/parser33 -ve 'p Prism::Translation::Parser33.parse("/(?<foo>)foo/ =~ bar")' ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [x86_64-darwin22] s(:match_with_lvasgn, s(:regexp, s(:str, "(?<foo>)foo"), s(:regopt)), s(:send, nil, :bar)) ```
1 parent 13aa3d1 commit 2059d28

21 files changed

+63
-21
lines changed

.github/workflows/test.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,22 @@ jobs:
5353
- name: internal_investigation
5454
run: bundle exec rake internal_investigation
5555

56+
prism:
57+
runs-on: ubuntu-latest
58+
name: Prism
59+
steps:
60+
- uses: actions/checkout@v4
61+
- name: set up Ruby
62+
uses: ruby/setup-ruby@v1
63+
with:
64+
# Specify the minimum Ruby version 2.7 required for Prism to run.
65+
ruby-version: 2.7
66+
bundler-cache: true
67+
- name: spec
68+
env:
69+
PARSER_ENGINE: parser_prism
70+
run: bundle exec rake prism_spec
71+
5672
documentation_checks:
5773
runs-on: ubuntu-latest
5874
name: Check documentation syntax

Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
77
gemspec
88

99
gem 'bump', require: false
10+
gem 'prism'
1011
gem 'rake'
1112
gem 'rspec'
1213
gem 'rubocop', github: 'rubocop/rubocop'

Rakefile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ task :spec do
2727
end
2828
end
2929

30+
desc 'Run RSpec with Prism'
31+
task :prism_spec do
32+
sh('PARSER_ENGINE=parser_prism bundle exec rake spec')
33+
end
34+
3035
desc 'Run RSpec with code coverage'
3136
task :coverage do
3237
ENV['COVERAGE'] = 'true'
@@ -36,7 +41,7 @@ end
3641
desc 'Run RuboCop over itself'
3742
RuboCop::RakeTask.new(:internal_investigation)
3843

39-
task default: %i[documentation_syntax_check spec internal_investigation]
44+
task default: %i[documentation_syntax_check spec prism_spec internal_investigation]
4045

4146
desc 'Generate a new cop template'
4247
task :new_cop, [:cop] do |_task, args|

changelog/new_support_prism.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* [#446](https://github.com/rubocop/rubocop-performance/pull/446): Support Prism as a Ruby parser (experimental). ([@koic][])

lib/rubocop/cop/performance/end_with.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ class EndWith < Base
5656
def_node_matcher :redundant_regex?, <<~PATTERN
5757
{(call $!nil? {:match :=~ :match?} (regexp (str $#literal_at_end?) (regopt)))
5858
(send (regexp (str $#literal_at_end?) (regopt)) {:match :match?} $_)
59-
(match-with-lvasgn (regexp (str $#literal_at_end?) (regopt)) $_)}
59+
({send match-with-lvasgn} (regexp (str $#literal_at_end?) (regopt)) $_)
60+
(send (regexp (str $#literal_at_end?) (regopt)) :=~ $_)}
6061
PATTERN
6162

6263
def on_send(node)

lib/rubocop/cop/performance/start_with.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ class StartWith < Base
5656
def_node_matcher :redundant_regex?, <<~PATTERN
5757
{(call $!nil? {:match :=~ :match?} (regexp (str $#literal_at_start?) (regopt)))
5858
(send (regexp (str $#literal_at_start?) (regopt)) {:match :match?} $_)
59-
(match-with-lvasgn (regexp (str $#literal_at_start?) (regopt)) $_)}
59+
(match-with-lvasgn (regexp (str $#literal_at_start?) (regopt)) $_)
60+
(send (regexp (str $#literal_at_start?) (regopt)) :=~ $_)}
6061
PATTERN
6162

6263
def on_send(node)

lib/rubocop/cop/performance/string_include.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ class StringInclude < Base
2929
def_node_matcher :redundant_regex?, <<~PATTERN
3030
{(call $!nil? {:match :=~ :!~ :match?} (regexp (str $#literal?) (regopt)))
3131
(send (regexp (str $#literal?) (regopt)) {:match :match? :===} $_)
32-
(match-with-lvasgn (regexp (str $#literal?) (regopt)) $_)}
32+
(match-with-lvasgn (regexp (str $#literal?) (regopt)) $_)
33+
(send (regexp (str $#literal?) (regopt)) :=~ $_)}
3334
PATTERN
3435

3536
# rubocop:disable Metrics/AbcSize

rubocop-performance.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,5 @@ Gem::Specification.new do |s|
3131
}
3232

3333
s.add_runtime_dependency('rubocop', '>= 1.48.1', '< 2.0')
34-
s.add_runtime_dependency('rubocop-ast', '>= 1.30.0', '< 2.0')
34+
s.add_runtime_dependency('rubocop-ast', '>= 1.31.1', '< 2.0')
3535
end

spec/rubocop/cop/performance/bind_call_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# frozen_string_literal: true
22

33
RSpec.describe RuboCop::Cop::Performance::BindCall, :config do
4-
context 'when TargetRubyVersion <= 2.6', :ruby26 do
4+
context 'when TargetRubyVersion <= 2.6', :ruby26, unsupported_on: :prism do
55
it 'does not register an offense when using `bind(obj).call(args, ...)`' do
66
expect_no_offenses(<<~RUBY)
77
umethod.bind(obj).call(foo, bar)

spec/rubocop/cop/performance/collection_literal_in_loop_spec.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,9 @@
144144
RUBY
145145
end
146146

147-
it 'registers an offense when the method is called with no receiver' do
147+
# FIXME: `undefined method `[]' for nil` occurs Prism 0.24.0. It has been resolved in
148+
# the development line. This will be resolved in Prism > 0.24.0 and higher releases.
149+
it 'registers an offense when the method is called with no receiver', broken_on: :prism do
148150
expect_offense(<<~RUBY)
149151
all? do |e|
150152
[1, 2, 3].include?(e)

spec/rubocop/cop/performance/delete_prefix_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
let(:cop_config) { { 'SafeMultiline' => safe_multiline } }
55
let(:safe_multiline) { true }
66

7-
context 'when TargetRubyVersion <= 2.4', :ruby24 do
7+
context 'when TargetRubyVersion <= 2.4', :ruby24, unsupported_on: :prism do
88
it "does not register an offense when using `gsub(/\Aprefix/, '')`" do
99
expect_no_offenses(<<~RUBY)
1010
str.gsub(/\\Aprefix/, '')

spec/rubocop/cop/performance/delete_suffix_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
let(:cop_config) { { 'SafeMultiline' => safe_multiline } }
55
let(:safe_multiline) { true }
66

7-
context 'when TargetRubyVersion <= 2.4', :ruby24 do
7+
context 'when TargetRubyVersion <= 2.4', :ruby24, unsupported_on: :prism do
88
it "does not register an offense when using `gsub(/suffix\z/, '')`" do
99
expect_no_offenses(<<~RUBY)
1010
str.gsub(/suffix\\z/, '')

spec/rubocop/cop/performance/map_compact_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@
361361
end
362362
end
363363

364-
context 'when TargetRubyVersion <= 2.6', :ruby26 do
364+
context 'when TargetRubyVersion <= 2.6', :ruby26, unsupported_on: :prism do
365365
it 'does not register an offense when using `collection.map(&:do_something).compact`' do
366366
expect_no_offenses(<<~RUBY)
367367
collection.map(&:do_something).compact

spec/rubocop/cop/performance/redundant_equality_comparison_block_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@
149149
end
150150
end
151151

152-
context 'when TargetRubyVersion <= 2.4', :ruby24 do
152+
context 'when TargetRubyVersion <= 2.4', :ruby24, unsupported_on: :prism do
153153
# Ruby 2.4 does not support `items.all?(Klass)`.
154154
it 'does not register an offense when using `all?` with `is_a?` comparison block' do
155155
expect_no_offenses(<<~RUBY)

spec/rubocop/cop/performance/redundant_merge_spec.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@
3636
end
3737
end
3838

39-
context 'when receiver is implicit' do
39+
# FIXME: `undefined method `[]' for nil` occurs Prism 0.24.0. It has been resolved in
40+
# the development line. This will be resolved in Prism > 0.24.0 and higher releases.
41+
context 'when receiver is implicit', broken_on: :prism do
4042
it "doesn't autocorrect" do
4143
new_source = autocorrect_source('merge!(foo: 1, bar: 2)')
4244
expect(new_source).to eq('merge!(foo: 1, bar: 2)')

spec/rubocop/cop/performance/regexp_match_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ def foo
396396
RUBY
397397
end
398398

399-
context 'when Ruby <= 2.3', :ruby23 do
399+
context 'when Ruby <= 2.3', :ruby23, unsupported_on: :prism do
400400
it 'does not register an offense when using `String#match` in condition' do
401401
expect_no_offenses(<<~RUBY)
402402
if 'foo'.match(re)

spec/rubocop/cop/performance/select_map_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@
130130
end
131131
end
132132

133-
context 'when TargetRubyVersion <= 2.6', :ruby26 do
133+
context 'when TargetRubyVersion <= 2.6', :ruby26, unsupported_on: :prism do
134134
it 'does not register an offense when using `select.map`' do
135135
expect_no_offenses(<<~RUBY)
136136
ary.select(&:present?).map(&:to_i)

spec/rubocop/cop/performance/string_identifier_argument_spec.rb

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727
RUBY
2828
end
2929
else
30-
it "registers an offense when using string argument for `#{method}` method" do
30+
# FIXME: `undefined method `[]' for nil` occurs Prism 0.24.0. It has been resolved in
31+
# the development line. This will be resolved in Prism > 0.24.0 and higher releases.
32+
it "registers an offense when using string argument for `#{method}` method", broken_on: :prism do
3133
expect_offense(<<~RUBY, method: method)
3234
#{method}('do_something')
3335
_{method} ^^^^^^^^^^^^^^ Use `:do_something` instead of `'do_something'`.
@@ -38,14 +40,18 @@
3840
RUBY
3941
end
4042

41-
it "does not register an offense when using symbol argument for `#{method}` method" do
43+
# FIXME: `undefined method `[]' for nil` occurs Prism 0.24.0. It has been resolved in
44+
# the development line. This will be resolved in Prism > 0.24.0 and higher releases.
45+
it "does not register an offense when using symbol argument for `#{method}` method", broken_on: :prism do
4246
expect_no_offenses(<<~RUBY)
4347
#{method}(:do_something)
4448
RUBY
4549
end
4650

4751
if described_class::INTERPOLATION_IGNORE_METHODS.include?(method)
48-
it 'does not register an offense when using string interpolation for `#{method}` method' do
52+
# FIXME: `undefined method `[]' for nil` occurs Prism 0.24.0. It has been resolved in
53+
# the development line. This will be resolved in Prism > 0.24.0 and higher releases.
54+
it 'does not register an offense when using string interpolation for `#{method}` method', broken_on: :prism do
4955
# NOTE: These methods don't support `::` when passing a symbol. const_get('A::B') is valid
5056
# but const_get(:'A::B') isn't. Since interpolated arguments may contain any content these
5157
# cases are not detected as an offense to prevent false positives.
@@ -54,7 +60,9 @@
5460
RUBY
5561
end
5662
else
57-
it 'registers an offense when using interpolated string argument' do
63+
# FIXME: `undefined method `[]' for nil` occurs Prism 0.24.0. It has been resolved in
64+
# the development line. This will be resolved in Prism > 0.24.0 and higher releases.
65+
it 'registers an offense when using interpolated string argument', broken_on: :prism do
5866
expect_offense(<<~RUBY, method: method)
5967
#{method}("do_something_\#{var}")
6068
_{method} ^^^^^^^^^^^^^^^^^^^^^ Use `:"do_something_\#{var}"` instead of `"do_something_\#{var}"`.

spec/rubocop/cop/performance/sum_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@
343343
RUBY
344344
end
345345

346-
context 'when Ruby 2.3 or lower', :ruby23 do
346+
context 'when Ruby 2.3 or lower', :ruby23, unsupported_on: :prism do
347347
it "does not register an offense when using `array.#{method}(10, :+)`" do
348348
expect_no_offenses(<<~RUBY)
349349
array.#{method}(10, :+)

spec/rubocop/cop/performance/unfreeze_string_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
end
1010
end
1111

12-
context 'when Ruby <= 3.2', :ruby32 do
12+
context 'when Ruby <= 3.2', :ruby32, unsupported_on: :prism do
1313
it 'registers an offense and corrects for an empty string with `.dup`' do
1414
expect_offense(<<~RUBY)
1515
"".dup
@@ -123,7 +123,7 @@
123123
RUBY
124124
end
125125

126-
context 'when Ruby <= 2.2', :ruby22 do
126+
context 'when Ruby <= 2.2', :ruby22, unsupported_on: :prism do
127127
it 'does not register an offense for an empty string with `.dup`' do
128128
expect_no_offenses(<<~RUBY)
129129
"".dup

spec/spec_helper.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313

1414
config.shared_context_metadata_behavior = :apply_to_host_groups
1515
config.filter_run_when_matching :focus
16+
config.filter_run_excluding broken_on: :prism if ENV['PARSER_ENGINE'] == 'parser_prism'
17+
18+
# Prism supports Ruby 3.3+ parsing.
19+
config.filter_run_excluding unsupported_on: :prism if ENV['PARSER_ENGINE'] == 'parser_prism'
1620
config.example_status_persistence_file_path = 'spec/examples.txt'
1721
config.disable_monkey_patching!
1822
config.warnings = true

0 commit comments

Comments
 (0)