From f10397223319e600c1d0190e3642cc93d2faace7 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Sat, 4 Jan 2025 15:27:07 +0900 Subject: [PATCH 01/18] Switch back docs version to master --- docs/antora.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/antora.yml b/docs/antora.yml index 0ee5485e3f..21b1815a34 100644 --- a/docs/antora.yml +++ b/docs/antora.yml @@ -2,6 +2,6 @@ name: rubocop-performance title: RuboCop Performance # We always provide version without patch here (e.g. 1.1), # as patch versions should not appear in the docs. -version: '1.23' +version: ~ nav: - modules/ROOT/nav.adoc From 4e147b0c0e9bfecee1431823ebda5a98580d163f Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Wed, 8 Jan 2025 16:22:55 +0900 Subject: [PATCH 02/18] Remove a useless constant It seems that unnecessary constant was left behind when introducing the Inject mechanism, inspired by RuboCop RSpec, during the initial implementation of this gem. --- lib/rubocop/performance.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/rubocop/performance.rb b/lib/rubocop/performance.rb index 4ed61a2fa0..e040f480cd 100644 --- a/lib/rubocop/performance.rb +++ b/lib/rubocop/performance.rb @@ -5,7 +5,6 @@ module RuboCop module Performance PROJECT_ROOT = Pathname.new(__dir__).parent.parent.expand_path.freeze CONFIG_DEFAULT = PROJECT_ROOT.join('config', 'default.yml').freeze - CONFIG = YAML.safe_load(CONFIG_DEFAULT.read).freeze private_constant(:CONFIG_DEFAULT, :PROJECT_ROOT) From a1a7c353d088a0383940f8b6dba2f98efda6d5ae Mon Sep 17 00:00:00 2001 From: viralpraxis Date: Wed, 15 Jan 2025 09:15:20 +0300 Subject: [PATCH 03/18] Fix `Performance/CaseWhenSplat` cop error on `when` node without body Previously if `when` node has an empty body this cop raises: ```ruby case 1 when 1 # no body # 1 == 1 else nil end ``` ``` undefined method `loc' for nil # ./lib/rubocop/cop/performance/case_when_splat.rb:154:in `indent_for' # ./lib/rubocop/cop/performance/case_when_splat.rb:144:in `new_branch_without_then' # ./lib/rubocop/cop/performance/case_when_splat.rb:123:in `reordering_correction' # ./lib/rubocop/cop/performance/case_when_splat.rb:114:in `reorder_condition' # ./lib/rubocop/cop/performance/case_when_splat.rb:86:in `autocorrect' ``` It also did not preserve comments. --- ...en_splat_cop_error_on_when_without_body.md | 1 + .../cop/performance/case_when_splat.rb | 26 ++++++++++++++---- rubocop-performance-1.23.0.gem | Bin 0 -> 44544 bytes .../cop/performance/case_when_splat_spec.rb | 25 +++++++++++++++++ 4 files changed, 46 insertions(+), 6 deletions(-) create mode 100644 changelog/fix_performance_case_when_splat_cop_error_on_when_without_body.md create mode 100644 rubocop-performance-1.23.0.gem diff --git a/changelog/fix_performance_case_when_splat_cop_error_on_when_without_body.md b/changelog/fix_performance_case_when_splat_cop_error_on_when_without_body.md new file mode 100644 index 0000000000..7cd6e80aa1 --- /dev/null +++ b/changelog/fix_performance_case_when_splat_cop_error_on_when_without_body.md @@ -0,0 +1 @@ +* [#484](https://github.com/rubocop/rubocop-performance/pull/484): Fix `Performance/CaseWhenSplat` cop error on `when` node without body. ([@viralpraxis][]) diff --git a/lib/rubocop/cop/performance/case_when_splat.rb b/lib/rubocop/cop/performance/case_when_splat.rb index 0fc30bd660..40c9a565dd 100644 --- a/lib/rubocop/cop/performance/case_when_splat.rb +++ b/lib/rubocop/cop/performance/case_when_splat.rb @@ -58,6 +58,7 @@ module Performance class CaseWhenSplat < Base include Alignment include RangeHelp + include CommentsHelp extend AutoCorrector MSG = 'Reordering `when` conditions with a splat to the end of the `when` branches can improve performance.' @@ -116,11 +117,18 @@ def reorder_condition(corrector, when_node) def reordering_correction(when_node) new_condition = replacement(when_node.conditions) - if same_line?(when_node, when_node.body) - new_condition_with_then(when_node, new_condition) - else - new_branch_without_then(when_node, new_condition) - end + condition = + if same_line?(when_node, when_node.body) + new_condition_with_then(when_node, new_condition) + else + new_branch_without_then(when_node, new_condition) + end + + condition_comments = comments_in_range(when_node).map do |comment_node| + "#{indent_for(comment_node)}#{comment_node.source}" + end.join("\n") + + "#{condition}#{condition_comments}" end def when_branch_range(when_node) @@ -134,7 +142,13 @@ def new_condition_with_then(node, new_condition) end def new_branch_without_then(node, new_condition) - "\n#{indent_for(node)}when #{new_condition}\n#{indent_for(node.body)}#{node.body.source}" + new_branch = "\n#{indent_for(node)}when #{new_condition}\n" + + if node.body + "#{new_branch}#{indent_for(node.body)}#{node.body.source}" + else + new_branch + end end def indent_for(node) diff --git a/rubocop-performance-1.23.0.gem b/rubocop-performance-1.23.0.gem new file mode 100644 index 0000000000000000000000000000000000000000..54605d67187ba014e9a81c28292a2e663d19dcf9 GIT binary patch literal 44544 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 C-H_n` literal 0 HcmV?d00001 diff --git a/spec/rubocop/cop/performance/case_when_splat_spec.rb b/spec/rubocop/cop/performance/case_when_splat_spec.rb index 4b25112474..99961b053f 100644 --- a/spec/rubocop/cop/performance/case_when_splat_spec.rb +++ b/spec/rubocop/cop/performance/case_when_splat_spec.rb @@ -54,6 +54,31 @@ RUBY end + it 'registers an offense for case when with a splat in the first condition with branch without body' do + expect_offense(<<~RUBY) + case foo + when *cond + ^^^^^^^^^^ Reordering `when` conditions with a splat to the end of the `when` branches can improve performance. + # bar + # bar + # bar + when 3 + baz + end + RUBY + + expect_correction(<<~RUBY) + case foo + when 3 + baz + when *cond + # bar + # bar + # bar + end + RUBY + end + it 'registers an offense for case when with a splat without an else' do expect_offense(<<~RUBY) case foo From c4308ae0f1ecf475f15217d537e1ae679e8ad619 Mon Sep 17 00:00:00 2001 From: Dave Corson-Knowles Date: Mon, 16 Sep 2024 16:43:47 -0700 Subject: [PATCH 04/18] Add new `ZipWithoutBlock` cop Add new Performance Cop to check for patterns like `.map { |id| [id] }` or `.map { [_1] }` and replace them with `.zip` without a block. This is a Performance Cop for the more efficient way to generate an Array of Arrays. * Performs 40-90% faster than `.map` to iteratively wrap array contents. * Performs 5 - 55% faster on ranges, depending on size. --- changelog/new_merge_pull_request_462_from.md | 1 + config/default.yml | 6 + .../cop/performance/zip_without_block.rb | 56 +++ lib/rubocop/cop/performance_cops.rb | 1 + .../cop/performance/zip_without_block_spec.rb | 389 ++++++++++++++++++ 5 files changed, 453 insertions(+) create mode 100644 changelog/new_merge_pull_request_462_from.md create mode 100644 lib/rubocop/cop/performance/zip_without_block.rb create mode 100644 spec/rubocop/cop/performance/zip_without_block_spec.rb diff --git a/changelog/new_merge_pull_request_462_from.md b/changelog/new_merge_pull_request_462_from.md new file mode 100644 index 0000000000..4f026ed531 --- /dev/null +++ b/changelog/new_merge_pull_request_462_from.md @@ -0,0 +1 @@ +* [#462](https://github.com/rubocop/rubocop-performance/pull/462): Add new `Performance/ZipWithoutBlock` cop that checks patterns like `.map { |id| [id] }` or `.map { [_1] }` and can replace them with `.zip`. ([@corsonknowles][]) diff --git a/config/default.yml b/config/default.yml index 437f1ed440..0ebb259928 100644 --- a/config/default.yml +++ b/config/default.yml @@ -381,3 +381,9 @@ Performance/UriDefaultParser: Description: 'Use `URI::DEFAULT_PARSER` instead of `URI::Parser.new`.' Enabled: true VersionAdded: '0.50' + +Performance/ZipWithoutBlock: + Description: 'Checks for `map { |id| [id] }` and suggests replacing it with `zip`.' + Enabled: pending + Safe: false + VersionAdded: <> diff --git a/lib/rubocop/cop/performance/zip_without_block.rb b/lib/rubocop/cop/performance/zip_without_block.rb new file mode 100644 index 0000000000..f2815b9912 --- /dev/null +++ b/lib/rubocop/cop/performance/zip_without_block.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Performance + # Checks for `map { |id| [id] }` and suggests replacing it with `zip`. + # + # @safety + # This cop is unsafe for novel definitions of `map` and `collect` + # on non-Enumerable objects that do not respond to `zip`. + # To make your object enumerable, define an `each` method + # as described in https://ruby-doc.org/core/Enumerable.html + # + # @example + # # bad + # [1, 2, 3].map { |id| [id] } + # + # # good + # [1, 2, 3].zip + class ZipWithoutBlock < Base + extend AutoCorrector + + MSG = 'Use `zip` without a block argument instead.' + RESTRICT_ON_SEND = Set.new(%i[map collect]).freeze + + # @!method map_with_array?(node) + def_node_matcher :map_with_array?, <<~PATTERN + { + (block (call !nil? RESTRICT_ON_SEND) (args (arg _)) (array (lvar _))) + (numblock (call !nil? RESTRICT_ON_SEND) 1 (array (lvar _))) + } + PATTERN + + def on_send(node) + return unless map_with_array?(node.parent) + + register_offense(node) + end + alias on_csend on_send + + private + + def register_offense(node) + offense_range = offense_range(node) + add_offense(offense_range) do |corrector| + corrector.replace(offense_range, 'zip') + end + end + + def offense_range(node) + node.loc.selector.join(node.parent.loc.end) + end + end + end + end +end diff --git a/lib/rubocop/cop/performance_cops.rb b/lib/rubocop/cop/performance_cops.rb index e71d4066c7..8a56be5db1 100644 --- a/lib/rubocop/cop/performance_cops.rb +++ b/lib/rubocop/cop/performance_cops.rb @@ -54,3 +54,4 @@ require_relative 'performance/unfreeze_string' require_relative 'performance/uri_default_parser' require_relative 'performance/chain_array_allocation' +require_relative 'performance/zip_without_block' diff --git a/spec/rubocop/cop/performance/zip_without_block_spec.rb b/spec/rubocop/cop/performance/zip_without_block_spec.rb new file mode 100644 index 0000000000..f750b4ff06 --- /dev/null +++ b/spec/rubocop/cop/performance/zip_without_block_spec.rb @@ -0,0 +1,389 @@ +# frozen_string_literal: true + +RSpec.describe RuboCop::Cop::Performance::ZipWithoutBlock, :config do + context 'when using map with array literal' do + it 'registers an offense and corrects to use zip with no arguments' do + expect_offense(<<~RUBY) + [1, 2, 3].map { |id| [id] } + ^^^^^^^^^^^^^^^^^ Use `zip` without a block argument instead. + RUBY + + expect_correction(<<~RUBY) + [1, 2, 3].zip + RUBY + end + end + + context 'when using map with a short iterator name' do + it 'registers an offense and corrects to use zip with no arguments' do + expect_offense(<<~RUBY) + [1, 2, 3].map { |e| [e] } + ^^^^^^^^^^^^^^^ Use `zip` without a block argument instead. + RUBY + + expect_correction(<<~RUBY) + [1, 2, 3].zip + RUBY + end + end + + context 'when using map on a range with another iterator name' do + it 'registers an offense and corrects' do + expect_offense(<<~RUBY) + (1..3).map { |x| [x] } + ^^^^^^^^^^^^^^^ Use `zip` without a block argument instead. + RUBY + + expect_correction(<<~RUBY) + (1..3).zip + RUBY + end + end + + context 'when using map in a do end block' do + it 'registers an offense and corrects' do + expect_offense(<<~RUBY) + (a..b).map do + ^^^^^^ Use `zip` without a block argument instead. + |m| [m] + end + RUBY + + expect_correction(<<~RUBY) + (a..b).zip + RUBY + end + end + + context 'with a safe operator' do + context 'when using map with array literal' do + it 'registers an offense and corrects to use zip with no arguments' do + expect_offense(<<~RUBY) + [1, 2, 3]&.map { |id| [id] } + ^^^^^^^^^^^^^^^^^ Use `zip` without a block argument instead. + RUBY + + expect_correction(<<~RUBY) + [1, 2, 3]&.zip + RUBY + end + end + + context 'when using map with a short iterator name' do + it 'registers an offense and corrects to use zip with no arguments' do + expect_offense(<<~RUBY) + [1, 2, 3]&.map { |e| [e] } + ^^^^^^^^^^^^^^^ Use `zip` without a block argument instead. + RUBY + + expect_correction(<<~RUBY) + [1, 2, 3]&.zip + RUBY + end + end + + context 'when using map on a range with another iterator name' do + it 'registers an offense and corrects' do + expect_offense(<<~RUBY) + (1..3)&.map { |x| [x] } + ^^^^^^^^^^^^^^^ Use `zip` without a block argument instead. + RUBY + + expect_correction(<<~RUBY) + (1..3)&.zip + RUBY + end + end + + context 'when using map in a do end block' do + it 'registers an offense and corrects' do + expect_offense(<<~RUBY) + (a..b)&.map do + ^^^^^^ Use `zip` without a block argument instead. + |m| [m] + end + RUBY + + expect_correction(<<~RUBY) + (a..b)&.zip + RUBY + end + end + end + + context 'when using map in a chain' do + it 'registers an offense and corrects' do + expect_offense(<<~RUBY) + [nil, tuple].flatten.map { |e| [e] }.call + ^^^^^^^^^^^^^^^ Use `zip` without a block argument instead. + RUBY + + expect_correction(<<~RUBY) + [nil, tuple].flatten.zip.call + RUBY + end + end + + context 'when the map block does not contain an array literal' do + it 'does not register an offense' do + expect_no_offenses(<<~RUBY) + [1, 2, 3].map { |id| id } + RUBY + end + end + + context 'when using map with an array literal containing multiple elements' do + it 'does not register an offense' do + expect_no_offenses(<<~RUBY) + [1, 2, 3].map { |id| [id, id] } + RUBY + end + end + + context 'when using other iterators such as' do + context 'when using collect' do + it 'registers an offense as collect is an alias of map' do + expect_offense(<<~RUBY) + [1, 2, 3].collect { |id| [id] } + ^^^^^^^^^^^^^^^^^^^^^ Use `zip` without a block argument instead. + RUBY + + expect_correction(<<~RUBY) + [1, 2, 3].zip + RUBY + end + + it 'registers an offense for collect with a numblock', :ruby27 do + expect_offense(<<~RUBY) + [1, 2, 3].collect { [_1] } + ^^^^^^^^^^^^^^^^ Use `zip` without a block argument instead. + RUBY + + expect_correction(<<~RUBY) + [1, 2, 3].zip + RUBY + end + end + + context 'when using select with an array literal' do + it 'does not register an offense' do + expect_no_offenses(<<~RUBY) + [1, 2, 3].select { |id| [id] } + RUBY + end + end + + context 'when calling filter_map' do + it 'does not register an offense' do + expect_no_offenses(<<~RUBY) + [1, 2, 3].filter_map {|id| [id]} + RUBY + end + end + + context 'when calling flat_map' do + it 'does not register an offense' do + expect_no_offenses(<<~RUBY) + [1, 2, 3].flat_map {|id| [id]} + RUBY + end + end + end + + context 'when using map with doubly wrapped arrays' do + it 'does not register an offense' do + expect_no_offenses(<<~RUBY) + [1, 2, 3].map { |id| [[id]] } + RUBY + end + end + + context 'when using map with addition' do + it 'does not register an offense' do + expect_no_offenses(<<~RUBY) + [1, 2, 3].map { |id| id + 1 } + RUBY + end + end + + context 'when using map with array addition' do + it 'does not register an offense' do + expect_no_offenses(<<~RUBY) + [1, 2, 3].map { |id| [id] + [id] } + RUBY + end + end + + context 'when using map with indexing into an array' do + it 'does not register an offense' do + expect_no_offenses(<<~RUBY) + [1, 2, 3].map { |id| [id][id] } + RUBY + end + end + + context 'when calling map with no arguments i.e. no parent' do + it 'does not register an offense' do + expect_no_offenses(<<~RUBY) + [1, 2, 3].map + RUBY + end + end + + context 'when calling map with an empty block' do + it 'does not register an offense' do + expect_no_offenses(<<~RUBY) + [1, 2, 3].map {} + RUBY + end + end + + context 'with related array of array patterns' do + context 'when using [*foo] to dynamically wrap only non-arrays in the list' do + it 'does not register an offense since the map is doing useful work' do + expect_no_offenses(<<~RUBY) + [1, [2], 3].map { |id| [*id] } + RUBY + end + end + + context 'when using Array.wrap the Rails extension of the [*foo] pattern that handles Hashes' do + it 'does not register an offense since the map is doing useful work' do + expect_no_offenses(<<~RUBY) + [1, 2, 3].map { |id| Array.wrap(id) } + RUBY + end + end + + context 'when making an array of arrays using each_with_object' do + it 'does not register an offense since we have not included this pattern yet' do + expect_no_offenses(<<~RUBY) + [1,2,3].each_with_object([]) {|id, object| object << [id]} + RUBY + end + end + end + + context 'with a numblock', :ruby27 do + context 'when using map with a numerical argument' do + it 'registers an offense and corrects' do + expect_offense(<<~RUBY) + [1, 2, 3].map { [_1] } + ^^^^^^^^^^^^ Use `zip` without a block argument instead. + RUBY + + expect_correction(<<~RUBY) + [1, 2, 3].zip + RUBY + end + end + + context 'when using map with a numblock in a chain' do + it 'registers an offense and corrects' do + expect_offense(<<~RUBY) + [1, 2].sum.map { [_1] }.flatten + ^^^^^^^^^^^^ Use `zip` without a block argument instead. + RUBY + + expect_correction(<<~RUBY) + [1, 2].sum.zip.flatten + RUBY + end + end + + context 'when using map on a range with a numblock' do + it 'registers an offense and corrects' do + expect_offense(<<~RUBY) + (1..3).map { [_1] } + ^^^^^^^^^^^^ Use `zip` without a block argument instead. + RUBY + + expect_correction(<<~RUBY) + (1..3).zip + RUBY + end + end + + context 'when using map in a do end block with a numblock' do + it 'registers an offense and corrects' do + expect_offense(<<~RUBY) + (a..b).map do [_1] end + ^^^^^^^^^^^^^^^ Use `zip` without a block argument instead. + RUBY + + expect_correction(<<~RUBY) + (a..b).zip + RUBY + end + end + + context 'when calling filter_map with a numblock' do + it 'does not register an offense' do + expect_no_offenses(<<~RUBY) + [1, 2, 3].filter_map { [_1] } + RUBY + end + end + + context 'when calling map, adding, and wrapping, with a numblock' do + it 'does not register an offense' do + expect_no_offenses(<<~RUBY) + [1, 2, 3].map { [_1 + 1] } + RUBY + end + end + + context 'when calling double wrapping with a numblock' do + it 'does not register an offense' do + expect_no_offenses(<<~RUBY) + [1, 2, 3].map { [[_1]] } + RUBY + end + end + end + + context 'when calling map with an unused iterator' do + it 'does not register an offense' do + expect_no_offenses(<<~RUBY) + [1, 2, 3].map { |e| [1] } + RUBY + end + end + + context 'when calling map with a static block that always returns the same value' do + it 'does not register an offense' do + expect_no_offenses(<<~RUBY) + [1, 2, 3].map { [id] } + RUBY + end + end + + context 'when calling map with a static array' do + it 'does not register an offense' do + expect_no_offenses(<<~RUBY) + [1, 2, 3].map { [] } + RUBY + end + end + + context 'when map has no receiver' do + it 'does not register an offense' do + expect_no_offenses(<<~RUBY) + map { |id| [id] } + RUBY + end + end + + context 'when map has an indeterminate object as a receiver' do + it 'still registers an offense' do + expect_offense(<<~RUBY) + foo.map { |id| [id] } + ^^^^^^^^^^^^^^^^^ Use `zip` without a block argument instead. + RUBY + + expect_correction(<<~RUBY) + foo.zip + RUBY + end + end +end From d00788cc3fdfbcd76fe58a2b7d584b1b561ea452 Mon Sep 17 00:00:00 2001 From: viralpraxis Date: Wed, 22 Jan 2025 13:40:51 +0300 Subject: [PATCH 05/18] Remove accidentally commited built gem --- rubocop-performance-1.23.0.gem | Bin 44544 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 rubocop-performance-1.23.0.gem diff --git a/rubocop-performance-1.23.0.gem b/rubocop-performance-1.23.0.gem deleted file mode 100644 index 54605d67187ba014e9a81c28292a2e663d19dcf9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44544 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 C-H_n` From b83ff1e3092cf4dda85798b50cc5eafb4e4a6dcb Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Thu, 23 Jan 2025 00:33:38 +0900 Subject: [PATCH 06/18] Apply `bundle exec rubocop --regenerate-todo` --- .rubocop_todo.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 88cad19a6c..719e5d053f 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2024-12-13 20:34:48 UTC using RuboCop version 1.69.2. +# on 2025-01-22 15:33:31 UTC using RuboCop version 1.70.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -28,12 +28,16 @@ InternalAffairs/NumblockHandler: - 'lib/rubocop/cop/performance/redundant_equality_comparison_block.rb' - 'lib/rubocop/cop/performance/sum.rb' +# Offense count: 18 +InternalAffairs/OnSendWithoutOnCSend: + Enabled: false + # Offense count: 4 # Configuration parameters: CountComments, CountAsOne. Metrics/ClassLength: Max: 159 -# Offense count: 13 +# Offense count: 14 # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns. Metrics/MethodLength: Max: 14 @@ -53,11 +57,11 @@ RSpec/ContextWording: - 'spec/rubocop/cop/performance/string_replacement_spec.rb' - 'spec/rubocop/cop/performance/times_map_spec.rb' -# Offense count: 388 +# Offense count: 390 # Configuration parameters: CountAsOne. RSpec/ExampleLength: Max: 24 -# Offense count: 312 +# Offense count: 314 RSpec/MultipleExpectations: Max: 3 From 664b6b00fac6f7b0b627292132dd97e344876468 Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Sat, 25 Jan 2025 14:28:43 +0100 Subject: [PATCH 07/18] Add missing `RESTRICT_ON_SEND` for two cops For these, it is known on what methods they will operate --- lib/rubocop/cop/performance/chain_array_allocation.rb | 2 ++ lib/rubocop/cop/performance/collection_literal_in_loop.rb | 2 ++ 2 files changed, 4 insertions(+) diff --git a/lib/rubocop/cop/performance/chain_array_allocation.rb b/lib/rubocop/cop/performance/chain_array_allocation.rb index d422013c59..bf4364d62e 100644 --- a/lib/rubocop/cop/performance/chain_array_allocation.rb +++ b/lib/rubocop/cop/performance/chain_array_allocation.rb @@ -45,6 +45,8 @@ class ChainArrayAllocation < Base RETURNS_NEW_ARRAY = (ALWAYS_RETURNS_NEW_ARRAY + RETURNS_NEW_ARRAY_WHEN_NO_BLOCK).freeze + RESTRICT_ON_SEND = RETURNS_NEW_ARRAY + MSG = 'Use unchained `%s` and `%s!` ' \ '(followed by `return array` if required) instead of chaining ' \ '`%s...%s`.' diff --git a/lib/rubocop/cop/performance/collection_literal_in_loop.rb b/lib/rubocop/cop/performance/collection_literal_in_loop.rb index 937c5e9ea9..f75847a6b1 100644 --- a/lib/rubocop/cop/performance/collection_literal_in_loop.rb +++ b/lib/rubocop/cop/performance/collection_literal_in_loop.rb @@ -65,6 +65,8 @@ class CollectionLiteralInLoop < Base HASH_METHODS = (ENUMERABLE_METHOD_NAMES | NONMUTATING_HASH_METHODS).to_set.freeze + RESTRICT_ON_SEND = ARRAY_METHODS + HASH_METHODS + def_node_matcher :kernel_loop?, <<~PATTERN (block (send {nil? (const nil? :Kernel)} :loop) From 959cc5fb967dc3affa7a891c9693734ac1c84cb1 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Tue, 28 Jan 2025 01:48:19 +0900 Subject: [PATCH 08/18] Remove a redundant config in spec_helper.rb Follow up https://github.com/rubocop/rubocop/pull/13748. --- spec/spec_helper.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 3e67f78a51..cd49bf7ca8 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -9,8 +9,6 @@ end RSpec.configure do |config| - config.include RuboCop::RSpec::ExpectOffense - config.shared_context_metadata_behavior = :apply_to_host_groups config.filter_run_when_matching :focus config.filter_run_excluding broken_on: :prism if ENV['PARSER_ENGINE'] == 'parser_prism' From 79be0f2a3c457f279c616c3f9c3308054a10b785 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Tue, 28 Jan 2025 01:58:42 +0900 Subject: [PATCH 09/18] Fix a build error This commit fixes the following build error: https://github.com/rubocop/rubocop-performance/actions/runs/12994012963/job/36237512771 --- spec/spec_helper.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index cd49bf7ca8..7c71d930e1 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -9,6 +9,10 @@ end RSpec.configure do |config| + # TODO: It can be removed when the oldest supported RuboCop version is greater than 1.71.0. + # https://github.com/rubocop/rubocop/pull/13748 + config.include RuboCop::RSpec::ExpectOffense + config.shared_context_metadata_behavior = :apply_to_host_groups config.filter_run_when_matching :focus config.filter_run_excluding broken_on: :prism if ENV['PARSER_ENGINE'] == 'parser_prism' From 61bd21a7df8ca57dc021e1cb42335fd71e42f8c8 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Tue, 28 Jan 2025 03:10:25 +0900 Subject: [PATCH 10/18] Use `Node#any_block_type?` Follow up https://github.com/rubocop/rubocop-ast/pull/356. --- .../cop/performance/chain_array_allocation.rb | 4 ++-- lib/rubocop/cop/performance/times_map.rb | 14 +++++++------- rubocop-performance.gemspec | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/rubocop/cop/performance/chain_array_allocation.rb b/lib/rubocop/cop/performance/chain_array_allocation.rb index bf4364d62e..cda0153729 100644 --- a/lib/rubocop/cop/performance/chain_array_allocation.rb +++ b/lib/rubocop/cop/performance/chain_array_allocation.rb @@ -54,7 +54,7 @@ class ChainArrayAllocation < Base def_node_matcher :chain_array_allocation?, <<~PATTERN (send { (send _ $%RETURN_NEW_ARRAY_WHEN_ARGS {int lvar ivar cvar gvar send}) - ({block numblock} (send _ $%ALWAYS_RETURNS_NEW_ARRAY) ...) + (any_block (send _ $%ALWAYS_RETURNS_NEW_ARRAY) ...) (send _ $%RETURNS_NEW_ARRAY ...) } $%HAS_MUTATION_ALTERNATIVE ...) PATTERN @@ -75,7 +75,7 @@ def on_send(node) def enumerable_select_method?(node) # NOTE: `QueryMethods#select` in Rails accepts positional arguments, whereas `Enumerable#select` does not. # This difference can be utilized to reduce the knowledge requirements related to `select`. - (node.block_type? || node.numblock_type?) && node.send_node.arguments.empty? + node.any_block_type? && node.send_node.arguments.empty? end end end diff --git a/lib/rubocop/cop/performance/times_map.rb b/lib/rubocop/cop/performance/times_map.rb index 7b37959479..afca1cfd1f 100644 --- a/lib/rubocop/cop/performance/times_map.rb +++ b/lib/rubocop/cop/performance/times_map.rb @@ -36,6 +36,13 @@ class TimesMap < Base MESSAGE_ONLY_IF = 'only if `%s` is always 0 or more' RESTRICT_ON_SEND = %i[map collect].freeze + def_node_matcher :times_map_call, <<~PATTERN + { + (any_block $(call (call $!nil? :times) {:map :collect}) ...) + $(call (call $!nil? :times) {:map :collect} (block_pass ...)) + } + PATTERN + def on_send(node) check(node) end @@ -75,13 +82,6 @@ def message(map_or_collect, count) end format(template, count: count.source, map_or_collect: map_or_collect.method_name) end - - def_node_matcher :times_map_call, <<~PATTERN - { - ({block numblock} $(call (call $!nil? :times) {:map :collect}) ...) - $(call (call $!nil? :times) {:map :collect} (block_pass ...)) - } - PATTERN end end end diff --git a/rubocop-performance.gemspec b/rubocop-performance.gemspec index 4c6e2025f4..e164d63953 100644 --- a/rubocop-performance.gemspec +++ b/rubocop-performance.gemspec @@ -31,5 +31,5 @@ Gem::Specification.new do |s| } s.add_dependency('rubocop', '>= 1.48.1', '< 2.0') - s.add_dependency('rubocop-ast', '>= 1.31.1', '< 2.0') + s.add_dependency('rubocop-ast', '>= 1.38.0', '< 2.0') end From fbc7529cd4f2aa4e37197a6932ec64a3b1907467 Mon Sep 17 00:00:00 2001 From: Daniel Vandersluis Date: Mon, 27 Jan 2025 15:26:06 -0500 Subject: [PATCH 11/18] Use node groups in node patterns to replace unions of types `rubocop-ast` defines some node groups (https://github.com/rubocop/rubocop-ast/blob/master/lib/rubocop/ast/node.rb#L89-L116) that can be used in place of a union of node types. --- .../cop/performance/array_semi_infinite_range_slice.rb | 4 ++-- lib/rubocop/cop/performance/range_include.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/rubocop/cop/performance/array_semi_infinite_range_slice.rb b/lib/rubocop/cop/performance/array_semi_infinite_range_slice.rb index 3658ab3e79..78daaada56 100644 --- a/lib/rubocop/cop/performance/array_semi_infinite_range_slice.rb +++ b/lib/rubocop/cop/performance/array_semi_infinite_range_slice.rb @@ -44,8 +44,8 @@ class ArraySemiInfiniteRangeSlice < Base def_node_matcher :endless_range?, <<~PATTERN { - ({irange erange} nil? (int positive?)) - ({irange erange} (int positive?) nil?) + (range nil? (int positive?)) + (range (int positive?) nil?) } PATTERN diff --git a/lib/rubocop/cop/performance/range_include.rb b/lib/rubocop/cop/performance/range_include.rb index d56afc2262..90062bf6b7 100644 --- a/lib/rubocop/cop/performance/range_include.rb +++ b/lib/rubocop/cop/performance/range_include.rb @@ -38,7 +38,7 @@ class RangeInclude < Base # (We don't even catch it if the Range is in double parens) def_node_matcher :range_include, <<~PATTERN - (call {irange erange (begin {irange erange})} ${:include? :member?} ...) + (call {range (begin range)} ${:include? :member?} ...) PATTERN def on_send(node) From 349ae04ccd695300d256e18bc208025184d52851 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Fri, 31 Jan 2025 23:59:15 +0900 Subject: [PATCH 12/18] Suppress RuboCop's offenses Follow up https://github.com/rubocop/rubocop/pull/13772 --- lib/rubocop/cop/performance/fixed_size.rb | 2 +- lib/rubocop/cop/performance/redundant_match.rb | 2 +- lib/rubocop/cop/performance/regexp_match.rb | 2 +- lib/rubocop/cop/performance/times_map.rb | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/rubocop/cop/performance/fixed_size.rb b/lib/rubocop/cop/performance/fixed_size.rb index cf7ef63c9d..4e2e1d826f 100644 --- a/lib/rubocop/cop/performance/fixed_size.rb +++ b/lib/rubocop/cop/performance/fixed_size.rb @@ -75,7 +75,7 @@ def allowed_argument?(arg) end def allowed_parent?(node) - node && (node.casgn_type? || node.block_type?) + node&.type?(:casgn, :block) end def contains_splat?(node) diff --git a/lib/rubocop/cop/performance/redundant_match.rb b/lib/rubocop/cop/performance/redundant_match.rb index d5d2c4cb16..b36d6a2e66 100644 --- a/lib/rubocop/cop/performance/redundant_match.rb +++ b/lib/rubocop/cop/performance/redundant_match.rb @@ -84,7 +84,7 @@ def requires_parentheses_for_call_like?(arg) end def call_like?(arg) - arg.call_type? || arg.yield_type? || arg.super_type? + arg.type?(:call, :yield, :super) end end end diff --git a/lib/rubocop/cop/performance/regexp_match.rb b/lib/rubocop/cop/performance/regexp_match.rb index 51d11d4246..0077728b1d 100644 --- a/lib/rubocop/cop/performance/regexp_match.rb +++ b/lib/rubocop/cop/performance/regexp_match.rb @@ -239,7 +239,7 @@ def scope_body(node) def scope_root(node) node.each_ancestor.find do |ancestor| - ancestor.def_type? || ancestor.defs_type? || ancestor.class_type? || ancestor.module_type? + ancestor.type?(:def, :defs, :class, :module) end end diff --git a/lib/rubocop/cop/performance/times_map.rb b/lib/rubocop/cop/performance/times_map.rb index afca1cfd1f..07623634da 100644 --- a/lib/rubocop/cop/performance/times_map.rb +++ b/lib/rubocop/cop/performance/times_map.rb @@ -69,7 +69,7 @@ def check(node) def handleable_receiver?(node) receiver = node.receiver.receiver - return true if receiver.literal? && (receiver.int_type? || receiver.float_type?) + return true if receiver.literal? && receiver.type?(:int, :float) node.receiver.dot? end From 074bd2aef174dd5067ba427e82dc5aa3cb2f8f31 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Mon, 3 Feb 2025 03:44:29 +0900 Subject: [PATCH 13/18] Explicit default `Performance/Sum` cop config for the spec If not explicitly specified, it depends on `config/default.yml`, which is the result of configuration file loading as a side effect of the Inject mechanism. --- spec/rubocop/cop/performance/sum_spec.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/rubocop/cop/performance/sum_spec.rb b/spec/rubocop/cop/performance/sum_spec.rb index fdbf2b76a8..987d69f3bd 100644 --- a/spec/rubocop/cop/performance/sum_spec.rb +++ b/spec/rubocop/cop/performance/sum_spec.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true RSpec.describe RuboCop::Cop::Performance::Sum, :config do + let(:cop_config) { { 'SafeAutoCorrect' => false, 'OnlySumOrWithInitialValue' => false } } + %i[inject reduce].each do |method| it "registers an offense and corrects when using `array.#{method}(10, :+)`" do expect_offense(<<~RUBY, method: method) From 2c00726662fad516ae559c0bb086de4c45330fdc Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Tue, 4 Feb 2025 10:44:59 +0900 Subject: [PATCH 14/18] Avoid unnecessary `send` --- lib/rubocop/performance/inject.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rubocop/performance/inject.rb b/lib/rubocop/performance/inject.rb index e255a881a4..b89fd14c3c 100644 --- a/lib/rubocop/performance/inject.rb +++ b/lib/rubocop/performance/inject.rb @@ -7,7 +7,7 @@ module Performance module Inject def self.defaults! path = CONFIG_DEFAULT.to_s - hash = ConfigLoader.send(:load_yaml_configuration, path) + hash = ConfigLoader.load_yaml_configuration(path) config = Config.new(hash, path).tap(&:make_excludes_absolute) puts "configuration from #{path}" if ConfigLoader.debug? config = ConfigLoader.merge_with_default(config, path) From ab7ef1631e74197bb5932d7a0de447981664b39c Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Tue, 4 Feb 2025 20:54:04 +0900 Subject: [PATCH 15/18] Suppress redundant configuration logging for rubocop-performace `rubocop-performance-1.23.1/config/default.yml` is being output twice: ```console $ bundle exec rubocop -d path/to/example.rb (snip) For /Users/koic/src/github.com/rubocop/rubocop-rails: configuration from /Users/koic/src/github.com/rubocop/rubocop-rails/.rubocop.yml configuration from /Users/koic/src/github.com/rubocop/rubocop-rails/config/default.yml configuration from /Users/koic/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/rubocop-da31e5acaa98/config/internal_affairs.yml configuration from /Users/koic/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/rubocop-da31e5acaa98/config/internal_affairs.yml Default configuration from /Users/koic/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/bundler/gems/rubocop-da31e5acaa98/config/default.yml configuration from /Users/koic/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/rubocop-performance-1.23.1/config/default.yml configuration from /Users/koic/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/rubocop-performance-1.23.1/config/default.yml ``` Since it is already logged by `ConfigLoader.load_yaml_configuration`: https://github.com/rubocop/rubocop/blob/v1.71.2/lib/rubocop/config_loader.rb#L71-L82 The unnecessary code on the caller side will be removed. --- lib/rubocop/performance/inject.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/rubocop/performance/inject.rb b/lib/rubocop/performance/inject.rb index b89fd14c3c..bb9a712c88 100644 --- a/lib/rubocop/performance/inject.rb +++ b/lib/rubocop/performance/inject.rb @@ -9,9 +9,8 @@ def self.defaults! path = CONFIG_DEFAULT.to_s hash = ConfigLoader.load_yaml_configuration(path) config = Config.new(hash, path).tap(&:make_excludes_absolute) - puts "configuration from #{path}" if ConfigLoader.debug? - config = ConfigLoader.merge_with_default(config, path) - ConfigLoader.instance_variable_set(:@default_configuration, config) + new_config = ConfigLoader.merge_with_default(config, path) + ConfigLoader.instance_variable_set(:@default_configuration, new_config) end end end From b83339d4bad0161d827e867921e3ffe4673a12b1 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Sat, 4 Jan 2025 16:24:10 +0900 Subject: [PATCH 16/18] Pluginfy RuboCop Performance This PR adds support for RuboCop's Plugin feature: https://github.com/rubocop/rubocop/pull/13792 It replaces the ad-hoc `Inject` with RuboCop plugins introduced in RuboCop 1.72. NOTE: Version 1.72.1 or later is specified, as it includes https://github.com/rubocop/rubocop/pull/13837, which removes the `Inject` from spec_helper.rb. While 1.72.0 is sufficient for runtime, but specifying the patch version 1.72.1 keeps things simple. --- .github/workflows/test.yml | 2 +- .rubocop.yml | 7 +++-- README.md | 11 +++++--- changelog/new_pluginfy_with_lint_roller.md | 1 + docs/modules/ROOT/pages/usage.adoc | 8 +++--- lib/rubocop-performance.rb | 5 +--- lib/rubocop/performance.rb | 8 +----- lib/rubocop/performance/inject.rb | 17 ------------ lib/rubocop/performance/plugin.rb | 31 ++++++++++++++++++++++ rubocop-performance.gemspec | 6 +++-- spec/spec_helper.rb | 4 --- tasks/cops_documentation.rake | 2 +- 12 files changed, 57 insertions(+), 45 deletions(-) create mode 100644 changelog/new_pluginfy_with_lint_roller.md delete mode 100644 lib/rubocop/performance/inject.rb create mode 100644 lib/rubocop/performance/plugin.rb diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 17d3c7f671..d0a1e8b93e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -92,7 +92,7 @@ jobs: sed -e "/gem 'rubocop', github: 'rubocop\/rubocop'/d" \ -e "/gem 'rubocop-rspec',/d" -i Gemfile cat << EOF > Gemfile.local - gem 'rubocop', '1.48.1' # Specify the oldest supported RuboCop version + gem 'rubocop', '1.72.1' # Specify the oldest supported RuboCop version EOF - name: set up Ruby uses: ruby/setup-ruby@v1 diff --git a/.rubocop.yml b/.rubocop.yml index ab330ce48d..e111ec6c5f 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,9 +1,12 @@ # This is the configuration used to check the rubocop source code. inherit_from: .rubocop_todo.yml -require: - - rubocop/cop/internal_affairs + +plugins: + - rubocop-internal_affairs - rubocop-performance + +require: - rubocop-rspec AllCops: diff --git a/README.md b/README.md index 8058c780ef..5d0f13a7fb 100644 --- a/README.md +++ b/README.md @@ -30,13 +30,13 @@ ways to do this: Put this into your `.rubocop.yml`. ```yaml -require: rubocop-performance +plugins: rubocop-performance ``` Alternatively, use the following array notation when specifying multiple extensions. ```yaml -require: +plugins: - rubocop-other-extension - rubocop-performance ``` @@ -44,10 +44,13 @@ require: Now you can run `rubocop` and it will automatically load the RuboCop Performance cops together with the standard cops. +> [!NOTE] +> The plugin system is supported in RuboCop 1.72+. In earlier versions, use `require` instead of `plugins`. + ### Command line ```sh -$ rubocop --require rubocop-performance +$ rubocop --plugin rubocop-performance ``` ### Rake task @@ -56,7 +59,7 @@ $ rubocop --require rubocop-performance require 'rubocop/rake_task' RuboCop::RakeTask.new do |task| - task.requires << 'rubocop-performance' + task.plugins << 'rubocop-performance' end ``` diff --git a/changelog/new_pluginfy_with_lint_roller.md b/changelog/new_pluginfy_with_lint_roller.md new file mode 100644 index 0000000000..45540759fa --- /dev/null +++ b/changelog/new_pluginfy_with_lint_roller.md @@ -0,0 +1 @@ +* [#490](https://github.com/rubocop/rubocop-performance/pull/490): Pluginfy RuboCop Performance. ([@koic][]) diff --git a/docs/modules/ROOT/pages/usage.adoc b/docs/modules/ROOT/pages/usage.adoc index 3632335c8c..3711cae813 100644 --- a/docs/modules/ROOT/pages/usage.adoc +++ b/docs/modules/ROOT/pages/usage.adoc @@ -9,17 +9,19 @@ Put this into your `.rubocop.yml`. [source,yaml] ---- -require: rubocop-performance +plugins: rubocop-performance ---- Now you can run `rubocop` and it will automatically load the RuboCop Performance cops together with the standard cops. +NOTE: The plugin system is supported in RuboCop 1.72+. In earlier versions, use `require` instead of `plugins`. + == Command line [source,sh] ---- -$ rubocop --require rubocop-performance +$ rubocop --plugin rubocop-performance ---- == Rake task @@ -27,6 +29,6 @@ $ rubocop --require rubocop-performance [source,ruby] ---- RuboCop::RakeTask.new do |task| - task.requires << 'rubocop-performance' + task.plugins << 'rubocop-performance' end ---- diff --git a/lib/rubocop-performance.rb b/lib/rubocop-performance.rb index 37a7ceb49c..9a32f00c97 100644 --- a/lib/rubocop-performance.rb +++ b/lib/rubocop-performance.rb @@ -4,10 +4,7 @@ require_relative 'rubocop/performance' require_relative 'rubocop/performance/version' -require_relative 'rubocop/performance/inject' - -RuboCop::Performance::Inject.defaults! - +require_relative 'rubocop/performance/plugin' require_relative 'rubocop/cop/performance_cops' RuboCop::Cop::Lint::UnusedMethodArgument.singleton_class.prepend( diff --git a/lib/rubocop/performance.rb b/lib/rubocop/performance.rb index e040f480cd..b1bdf70f56 100644 --- a/lib/rubocop/performance.rb +++ b/lib/rubocop/performance.rb @@ -1,13 +1,7 @@ # frozen_string_literal: true module RuboCop - # RuboCop Performance project namespace + # RuboCop Performance project namespace. module Performance - PROJECT_ROOT = Pathname.new(__dir__).parent.parent.expand_path.freeze - CONFIG_DEFAULT = PROJECT_ROOT.join('config', 'default.yml').freeze - - private_constant(:CONFIG_DEFAULT, :PROJECT_ROOT) - - ::RuboCop::ConfigObsoletion.files << PROJECT_ROOT.join('config', 'obsoletion.yml') end end diff --git a/lib/rubocop/performance/inject.rb b/lib/rubocop/performance/inject.rb deleted file mode 100644 index bb9a712c88..0000000000 --- a/lib/rubocop/performance/inject.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -module RuboCop - module Performance - # Because RuboCop doesn't yet support plugins, we have to monkey patch in a - # bit of our configuration. - module Inject - def self.defaults! - path = CONFIG_DEFAULT.to_s - hash = ConfigLoader.load_yaml_configuration(path) - config = Config.new(hash, path).tap(&:make_excludes_absolute) - new_config = ConfigLoader.merge_with_default(config, path) - ConfigLoader.instance_variable_set(:@default_configuration, new_config) - end - end - end -end diff --git a/lib/rubocop/performance/plugin.rb b/lib/rubocop/performance/plugin.rb new file mode 100644 index 0000000000..2c082d3376 --- /dev/null +++ b/lib/rubocop/performance/plugin.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require 'lint_roller' + +module RuboCop + module Performance + # A plugin that integrates RuboCop Performance with RuboCop's plugin system. + class Plugin < LintRoller::Plugin + def about + LintRoller::About.new( + name: 'rubocop-performance', + version: Version::STRING, + homepage: 'https://github.com/rubocop/rubocop-performance', + description: 'A collection of RuboCop cops to check for performance optimizations in Ruby code.' + ) + end + + def supported?(context) + context.engine == :rubocop + end + + def rules(_context) + project_root = Pathname.new(__dir__).join('../../..') + + ConfigObsoletion.files << project_root.join('config', 'obsoletion.yml') + + LintRoller::Rules.new(type: :path, config_format: :rubocop, value: project_root.join('config', 'default.yml')) + end + end + end +end diff --git a/rubocop-performance.gemspec b/rubocop-performance.gemspec index e164d63953..e7c2ebd7e3 100644 --- a/rubocop-performance.gemspec +++ b/rubocop-performance.gemspec @@ -27,9 +27,11 @@ Gem::Specification.new do |s| 'source_code_uri' => 'https://github.com/rubocop/rubocop-performance/', 'documentation_uri' => "https://docs.rubocop.org/rubocop-performance/#{RuboCop::Performance::Version.document_version}/", 'bug_tracker_uri' => 'https://github.com/rubocop/rubocop-performance/issues', - 'rubygems_mfa_required' => 'true' + 'rubygems_mfa_required' => 'true', + 'default_lint_roller_plugin' => 'RuboCop::Performance::Plugin' } - s.add_dependency('rubocop', '>= 1.48.1', '< 2.0') + s.add_dependency('lint_roller', '~> 1.1') + s.add_dependency('rubocop', '>= 1.72.1', '< 2.0') s.add_dependency('rubocop-ast', '>= 1.38.0', '< 2.0') end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 7c71d930e1..cd49bf7ca8 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -9,10 +9,6 @@ end RSpec.configure do |config| - # TODO: It can be removed when the oldest supported RuboCop version is greater than 1.71.0. - # https://github.com/rubocop/rubocop/pull/13748 - config.include RuboCop::RSpec::ExpectOffense - config.shared_context_metadata_behavior = :apply_to_host_groups config.filter_run_when_matching :focus config.filter_run_excluding broken_on: :prism if ENV['PARSER_ENGINE'] == 'parser_prism' diff --git a/tasks/cops_documentation.rake b/tasks/cops_documentation.rake index 677bbfb940..52e29f1be7 100644 --- a/tasks/cops_documentation.rake +++ b/tasks/cops_documentation.rake @@ -15,7 +15,7 @@ task update_cops_documentation: :yard_for_generate_documentation do # NOTE: Update `<>` version for docs/modules/ROOT/pages/cops_performance.adoc # when running release tasks. - RuboCop::Performance::Inject.defaults! + RuboCop::ConfigLoader.inject_defaults!("#{__dir__}/../config/default.yml") CopsDocumentationGenerator.new(departments: deps).call end From 20c07a276bc086597cab3391aa7574f8bf6f6d30 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Sun, 16 Feb 2025 01:06:23 +0900 Subject: [PATCH 17/18] Update Changelog --- CHANGELOG.md | 10 ++++++++++ ...e_case_when_splat_cop_error_on_when_without_body.md | 1 - changelog/new_merge_pull_request_462_from.md | 1 - changelog/new_pluginfy_with_lint_roller.md | 1 - 4 files changed, 10 insertions(+), 3 deletions(-) delete mode 100644 changelog/fix_performance_case_when_splat_cop_error_on_when_without_body.md delete mode 100644 changelog/new_merge_pull_request_462_from.md delete mode 100644 changelog/new_pluginfy_with_lint_roller.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 886b3e795c..f801d14a46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,15 @@ ## master (unreleased) +### New features + +* [#490](https://github.com/rubocop/rubocop-performance/pull/490): Pluginfy RuboCop Performance. ([@koic][]) +* [#462](https://github.com/rubocop/rubocop-performance/pull/462): Add new `Performance/ZipWithoutBlock` cop that checks patterns like `.map { |id| [id] }` or `.map { [_1] }` and can replace them with `.zip`. ([@corsonknowles][]) + +### Bug fixes + +* [#484](https://github.com/rubocop/rubocop-performance/pull/484): Fix `Performance/CaseWhenSplat` cop error on `when` node without body. ([@viralpraxis][]) + ## 1.23.1 (2025-01-04) ### Bug fixes @@ -569,3 +578,4 @@ [@earlopain]: https://github.com/earlopain [@parkerfinch]: https://github.com/parkerfinch [@viralpraxis]: https://github.com/viralpraxis +[@corsonknowles]: https://github.com/corsonknowles diff --git a/changelog/fix_performance_case_when_splat_cop_error_on_when_without_body.md b/changelog/fix_performance_case_when_splat_cop_error_on_when_without_body.md deleted file mode 100644 index 7cd6e80aa1..0000000000 --- a/changelog/fix_performance_case_when_splat_cop_error_on_when_without_body.md +++ /dev/null @@ -1 +0,0 @@ -* [#484](https://github.com/rubocop/rubocop-performance/pull/484): Fix `Performance/CaseWhenSplat` cop error on `when` node without body. ([@viralpraxis][]) diff --git a/changelog/new_merge_pull_request_462_from.md b/changelog/new_merge_pull_request_462_from.md deleted file mode 100644 index 4f026ed531..0000000000 --- a/changelog/new_merge_pull_request_462_from.md +++ /dev/null @@ -1 +0,0 @@ -* [#462](https://github.com/rubocop/rubocop-performance/pull/462): Add new `Performance/ZipWithoutBlock` cop that checks patterns like `.map { |id| [id] }` or `.map { [_1] }` and can replace them with `.zip`. ([@corsonknowles][]) diff --git a/changelog/new_pluginfy_with_lint_roller.md b/changelog/new_pluginfy_with_lint_roller.md deleted file mode 100644 index 45540759fa..0000000000 --- a/changelog/new_pluginfy_with_lint_roller.md +++ /dev/null @@ -1 +0,0 @@ -* [#490](https://github.com/rubocop/rubocop-performance/pull/490): Pluginfy RuboCop Performance. ([@koic][]) From 24a3a4e2af013f1378dca53d541dafed9487e5e8 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Sun, 16 Feb 2025 01:06:43 +0900 Subject: [PATCH 18/18] Cut 1.24.0 --- CHANGELOG.md | 2 ++ config/default.yml | 2 +- docs/antora.yml | 2 +- docs/modules/ROOT/pages/cops.adoc | 1 + docs/modules/ROOT/pages/cops_performance.adoc | 35 +++++++++++++++++++ lib/rubocop/performance/version.rb | 2 +- relnotes/v1.24.0.md | 12 +++++++ 7 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 relnotes/v1.24.0.md diff --git a/CHANGELOG.md b/CHANGELOG.md index f801d14a46..1d8bbc8dc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ ## master (unreleased) +## 1.24.0 (2025-02-16) + ### New features * [#490](https://github.com/rubocop/rubocop-performance/pull/490): Pluginfy RuboCop Performance. ([@koic][]) diff --git a/config/default.yml b/config/default.yml index 0ebb259928..606acb83df 100644 --- a/config/default.yml +++ b/config/default.yml @@ -386,4 +386,4 @@ Performance/ZipWithoutBlock: Description: 'Checks for `map { |id| [id] }` and suggests replacing it with `zip`.' Enabled: pending Safe: false - VersionAdded: <> + VersionAdded: '1.24' diff --git a/docs/antora.yml b/docs/antora.yml index 21b1815a34..3ee5f7e808 100644 --- a/docs/antora.yml +++ b/docs/antora.yml @@ -2,6 +2,6 @@ name: rubocop-performance title: RuboCop Performance # We always provide version without patch here (e.g. 1.1), # as patch versions should not appear in the docs. -version: ~ +version: '1.24' nav: - modules/ROOT/nav.adoc diff --git a/docs/modules/ROOT/pages/cops.adoc b/docs/modules/ROOT/pages/cops.adoc index 7e91ba8416..d319e7fd96 100644 --- a/docs/modules/ROOT/pages/cops.adoc +++ b/docs/modules/ROOT/pages/cops.adoc @@ -63,5 +63,6 @@ Performance cops optimization analysis for your projects. * xref:cops_performance.adoc#performancetimesmap[Performance/TimesMap] * xref:cops_performance.adoc#performanceunfreezestring[Performance/UnfreezeString] * xref:cops_performance.adoc#performanceuridefaultparser[Performance/UriDefaultParser] +* xref:cops_performance.adoc#performancezipwithoutblock[Performance/ZipWithoutBlock] // END_COP_LIST diff --git a/docs/modules/ROOT/pages/cops_performance.adoc b/docs/modules/ROOT/pages/cops_performance.adoc index 5c78a3f0e1..bae90e109e 100644 --- a/docs/modules/ROOT/pages/cops_performance.adoc +++ b/docs/modules/ROOT/pages/cops_performance.adoc @@ -2598,3 +2598,38 @@ URI::Parser.new # good URI::DEFAULT_PARSER ---- + +[#performancezipwithoutblock] +== Performance/ZipWithoutBlock + +|=== +| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed + +| Pending +| No +| Always (Unsafe) +| 1.24 +| - +|=== + +Checks for `map { |id| [id] }` and suggests replacing it with `zip`. + +[#safety-performancezipwithoutblock] +=== Safety + +This cop is unsafe for novel definitions of `map` and `collect` +on non-Enumerable objects that do not respond to `zip`. +To make your object enumerable, define an `each` method +as described in https://ruby-doc.org/core/Enumerable.html + +[#examples-performancezipwithoutblock] +=== Examples + +[source,ruby] +---- +# bad +[1, 2, 3].map { |id| [id] } + +# good +[1, 2, 3].zip +---- diff --git a/lib/rubocop/performance/version.rb b/lib/rubocop/performance/version.rb index 103e69b485..24695abafb 100644 --- a/lib/rubocop/performance/version.rb +++ b/lib/rubocop/performance/version.rb @@ -4,7 +4,7 @@ module RuboCop module Performance # This module holds the RuboCop Performance version information. module Version - STRING = '1.23.1' + STRING = '1.24.0' def self.document_version STRING.match('\d+\.\d+').to_s diff --git a/relnotes/v1.24.0.md b/relnotes/v1.24.0.md new file mode 100644 index 0000000000..2c9a4586c3 --- /dev/null +++ b/relnotes/v1.24.0.md @@ -0,0 +1,12 @@ +### New features + +* [#490](https://github.com/rubocop/rubocop-performance/pull/490): Pluginfy RuboCop Performance. ([@koic][]) +* [#462](https://github.com/rubocop/rubocop-performance/pull/462): Add new `Performance/ZipWithoutBlock` cop that checks patterns like `.map { |id| [id] }` or `.map { [_1] }` and can replace them with `.zip`. ([@corsonknowles][]) + +### Bug fixes + +* [#484](https://github.com/rubocop/rubocop-performance/pull/484): Fix `Performance/CaseWhenSplat` cop error on `when` node without body. ([@viralpraxis][]) + +[@koic]: https://github.com/koic +[@corsonknowles]: https://github.com/corsonknowles +[@viralpraxis]: https://github.com/viralpraxis pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy