From cc1bb39b5bbe2d2b0d456570d768c355b960d4fc Mon Sep 17 00:00:00 2001 From: Bozhidar Batsov Date: Tue, 5 Aug 2025 15:15:23 +0300 Subject: [PATCH 01/37] Reset the docs version --- docs/antora.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/antora.yml b/docs/antora.yml index 84f3553fdcf7..9c897ea03d1d 100644 --- a/docs/antora.yml +++ b/docs/antora.yml @@ -2,6 +2,6 @@ name: rubocop title: RuboCop # We always provide version without patch here (e.g. 1.1), # as patch versions should not appear in the docs. -version: '1.79' +version: ~ nav: - modules/ROOT/nav.adoc From 6ecbee75e2a6710cb9b04ca2c962d733977e685f Mon Sep 17 00:00:00 2001 From: Martin Emde Date: Tue, 5 Aug 2025 07:26:57 -0700 Subject: [PATCH 02/37] Fix SafeNavigation cop to preserve safe navigation in check variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes issue where 'variable&.foo ? variable.foo.bar : nil' was incorrectly corrected to 'variable.foo&.bar' instead of the correct 'variable&.foo&.bar' - Fixes: foo&.bar && foo.bar.baz → foo&.bar&.baz - Fixes: if foo&.bar; foo.bar.baz; end → foo&.bar&.baz - Fixes: foo&.bar ? foo.bar.baz : nil → foo&.bar&.baz Co-authored-by: Koichi ITO --- ...ion_cop_to_preserve_safe_20250805124624.md | 1 + lib/rubocop/cop/style/safe_navigation.rb | 11 +++++- .../rubocop/cop/style/safe_navigation_spec.rb | 35 +++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 changelog/fix_fix_safe_navigation_cop_to_preserve_safe_20250805124624.md diff --git a/changelog/fix_fix_safe_navigation_cop_to_preserve_safe_20250805124624.md b/changelog/fix_fix_safe_navigation_cop_to_preserve_safe_20250805124624.md new file mode 100644 index 000000000000..936b125ed709 --- /dev/null +++ b/changelog/fix_fix_safe_navigation_cop_to_preserve_safe_20250805124624.md @@ -0,0 +1 @@ +* [#14424](https://github.com/rubocop/rubocop/pull/14424): Fix SafeNavigation cop to preserve existing safe navigation in fixed code. ([@martinemde][]) diff --git a/lib/rubocop/cop/style/safe_navigation.rb b/lib/rubocop/cop/style/safe_navigation.rb index ffe39e0f3fbc..91ddf9e50585 100644 --- a/lib/rubocop/cop/style/safe_navigation.rb +++ b/lib/rubocop/cop/style/safe_navigation.rb @@ -142,6 +142,7 @@ class SafeNavigation < Base # rubocop:disable Metrics/ClassLength # @!method strip_begin(node) def_node_matcher :strip_begin, '{ (begin $!begin) $!(begin) }' + # rubocop:disable Metrics/AbcSize def on_if(node) return if allowed_if_condition?(node) @@ -155,9 +156,11 @@ def on_if(node) removal_ranges = [begin_range(node, body), end_range(node, body)] report_offense(node, method_chain, method_call, *removal_ranges) do |corrector| + corrector.replace(receiver, checked_variable.source) if checked_variable.csend_type? corrector.insert_before(method_call.loc.dot, '&') unless method_call.safe_navigation? end end + # rubocop:enable Metrics/AbcSize def on_and(node) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength collect_and_clauses(node).each do |(lhs, lhs_operator_range), (rhs, _rhs_operator_range)| @@ -335,8 +338,14 @@ def matching_nodes?(left, right) def matching_call_nodes?(left, right) return false unless left && right.respond_to?(:call_type?) + return false unless left.call_type? && right.call_type? - left.call_type? && right.call_type? && left.children == right.children + # Compare receiver and method name, but ignore the difference between + # safe navigation method call (`&.`) and dot method call (`.`). + left_receiver, left_method = left.children.take(2) + right_receiver, right_method = right.children.take(2) + + left_method == right_method && matching_nodes?(left_receiver, right_receiver) end def chain_length(method_chain, method) diff --git a/spec/rubocop/cop/style/safe_navigation_spec.rb b/spec/rubocop/cop/style/safe_navigation_spec.rb index 062345e97649..a48d358f1e5b 100644 --- a/spec/rubocop/cop/style/safe_navigation_spec.rb +++ b/spec/rubocop/cop/style/safe_navigation_spec.rb @@ -1437,6 +1437,41 @@ def foobar #{variable}&.one&.two(baz) { |e| e.qux } RUBY end + + it 'corrects a ternary expression with safe navigation object check followed by a chained method call' do + expect_offense(<<~RUBY, variable: variable) + %{variable}&.bar ? %{variable}.bar.baz : nil + ^{variable}^^^^^^^^^{variable}^^^^^^^^^^^^^^ Use safe navigation (`&.`) instead [...] + RUBY + + expect_correction(<<~RUBY) + #{variable}&.bar&.baz + RUBY + end + + it 'corrects an object check with safe navigation followed by a chained method call' do + expect_offense(<<~RUBY, variable: variable) + %{variable}&.bar && %{variable}.bar.baz + ^{variable}^^^^^^^^^^{variable}^^^^^^^^ Use safe navigation (`&.`) instead [...] + RUBY + + expect_correction(<<~RUBY) + #{variable}&.bar&.baz + RUBY + end + + it 'corrects a check with safe navigation followed by a chained method call' do + expect_offense(<<~RUBY, variable: variable) + if %{variable}&.bar + ^^^^{variable}^^^^^ Use safe navigation (`&.`) instead [...] + %{variable}.bar.baz + end + RUBY + + expect_correction(<<~RUBY) + #{variable}&.bar&.baz + RUBY + end end end From 76f2ce1346bf0d7768b1f66a4f970d4696784c57 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Wed, 6 Aug 2025 01:06:26 +0900 Subject: [PATCH 03/37] [Fix #14419] Fix false positives for `Lint/UselessAssignment` This PR fixes false positives for `Lint/UselessAssignment` when duplicate assignments appear in nested `if` branches inside a loop and the variable is used outside `while` loop. Fixes #14419. --- ...e_positives_for_lint_useless_assignment.md | 1 + lib/rubocop/cop/variable_force.rb | 9 +++---- .../cop/lint/useless_assignment_spec.rb | 27 +++++++++++++++++++ 3 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 changelog/fix_false_positives_for_lint_useless_assignment.md diff --git a/changelog/fix_false_positives_for_lint_useless_assignment.md b/changelog/fix_false_positives_for_lint_useless_assignment.md new file mode 100644 index 000000000000..0f81fa3a5c16 --- /dev/null +++ b/changelog/fix_false_positives_for_lint_useless_assignment.md @@ -0,0 +1 @@ +* [#14419](https://github.com/rubocop/rubocop/issues/14419): Fix false positives for `Lint/UselessAssignment` when duplicate assignments appear in nested `if` branches inside a loop and the variable is used outside `while` loop. ([@koic][]) diff --git a/lib/rubocop/cop/variable_force.rb b/lib/rubocop/cop/variable_force.rb index 8af505effa71..3a110792c4fe 100644 --- a/lib/rubocop/cop/variable_force.rb +++ b/lib/rubocop/cop/variable_force.rb @@ -356,17 +356,14 @@ def descendant_reference(node) end def reference_assignments(loop_assignments, loop_node) - node = loop_assignments.first.node - # If inside a branching statement, mark all as referenced. # Otherwise, mark only the last assignment as referenced. # Note that `rescue` must be considered as branching because of # the `retry` keyword. - if node.each_ancestor(*BRANCH_NODES).any? || node.parent.each_descendant(*BRANCH_NODES).any? - loop_assignments.each { |assignment| assignment.reference!(loop_node) } - else - loop_assignments.last&.reference!(loop_node) + loop_assignments.each do |assignment| + assignment.reference!(loop_node) if assignment.node.each_ancestor(*BRANCH_NODES).any? end + loop_assignments.last&.reference!(loop_node) end def scanned_node?(node) diff --git a/spec/rubocop/cop/lint/useless_assignment_spec.rb b/spec/rubocop/cop/lint/useless_assignment_spec.rb index df3a04328a23..0c76bf02a543 100644 --- a/spec/rubocop/cop/lint/useless_assignment_spec.rb +++ b/spec/rubocop/cop/lint/useless_assignment_spec.rb @@ -2415,6 +2415,33 @@ def some_method(environment) end end + context 'when duplicate assignments appear in nested `if` branches inside a loop and the variable is used outside `while` loop' do + context 'while loop' do + it 'does not register an offense' do + expect_no_offenses(<<~RUBY) + def parse_options + index = -1 + while loop_cond + index += 1 + + if first_cond + index += 1 + else + if second_cond + index += 1 + else + if third_cond + index += 1 + end + end + end + end + end + RUBY + end + end + end + context 'when duplicate assignments in `rescue` branch with `retry`' do it 'does not register an offense' do expect_no_offenses(<<~RUBY) From 28fc1d6cc033eac40fc2ffba72c2f6e19e28d433 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Thu, 7 Aug 2025 01:22:10 +0900 Subject: [PATCH 04/37] Fix false positives for `Style/RedundantParentheses` Follow-up to https://github.com/rubocop/rubocop/pull/14407#issuecomment-3157714513. This PR fixes false positives for `Style/RedundantParentheses` when `do`...`end` block is wrapped in parentheses as a method argument. --- ...tives_for_style_redundant_parentheses_cop.md | 1 + lib/rubocop/cop/style/redundant_parentheses.rb | 2 +- .../cop/style/redundant_parentheses_spec.rb | 17 +++++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 changelog/fix_false_positives_for_style_redundant_parentheses_cop.md diff --git a/changelog/fix_false_positives_for_style_redundant_parentheses_cop.md b/changelog/fix_false_positives_for_style_redundant_parentheses_cop.md new file mode 100644 index 000000000000..248db4e3f25f --- /dev/null +++ b/changelog/fix_false_positives_for_style_redundant_parentheses_cop.md @@ -0,0 +1 @@ +* [#14427](https://github.com/rubocop/rubocop/pull/14427): Fix false positives for `Style/RedundantParentheses` when `do`...`end` block is wrapped in parentheses as a method argument. ([@koic][]) diff --git a/lib/rubocop/cop/style/redundant_parentheses.rb b/lib/rubocop/cop/style/redundant_parentheses.rb index c70cab503b0c..552fd1885caf 100644 --- a/lib/rubocop/cop/style/redundant_parentheses.rb +++ b/lib/rubocop/cop/style/redundant_parentheses.rb @@ -220,7 +220,7 @@ def allow_in_multiline_conditions? end def call_node?(node) - node.call_type? || (node.any_block_type? && !node.lambda_or_proc?) + node.call_type? || (node.any_block_type? && node.braces? && !node.lambda_or_proc?) end def check_send(begin_node, node) diff --git a/spec/rubocop/cop/style/redundant_parentheses_spec.rb b/spec/rubocop/cop/style/redundant_parentheses_spec.rb index f44067f8f630..8f1863f5ae0a 100644 --- a/spec/rubocop/cop/style/redundant_parentheses_spec.rb +++ b/spec/rubocop/cop/style/redundant_parentheses_spec.rb @@ -683,6 +683,23 @@ RUBY end + it 'registers an offense when braces block is wrapped in parentheses as a method argument' do + expect_offense(<<~RUBY) + foo (x.select { |item| item }).y + ^^^^^^^^^^^^^^^^^^^^^^^^^^ Don't use parentheses around a method call. + RUBY + + expect_correction(<<~RUBY) + foo x.select { |item| item }.y + RUBY + end + + it 'does not register an offense when `do`...`end` block is wrapped in parentheses as a method argument' do + expect_no_offenses(<<~RUBY) + foo (x.select do |item| item end).y + RUBY + end + it 'registers a multiline expression around block wrapped in parens with a chained method' do expect_offense(<<~RUBY) ( From 1a81f2885de00edaa7a52aec54af0fde5514755e Mon Sep 17 00:00:00 2001 From: viralpraxis Date: Wed, 6 Aug 2025 22:43:48 +0400 Subject: [PATCH 05/37] Enhance `Lint/SelfAssignment` to detect offenses within indexed assignment with non-standard arity --- ...ment_with_many_arguments_20250806224151.md | 1 + lib/rubocop/cop/lint/self_assignment.rb | 9 ++++--- spec/rubocop/cop/lint/self_assignment_spec.rb | 26 +++++++++++++++++++ 3 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 changelog/change_enhance_lint_self_assignment_to_detect_indexed_assignment_with_many_arguments_20250806224151.md diff --git a/changelog/change_enhance_lint_self_assignment_to_detect_indexed_assignment_with_many_arguments_20250806224151.md b/changelog/change_enhance_lint_self_assignment_to_detect_indexed_assignment_with_many_arguments_20250806224151.md new file mode 100644 index 000000000000..485e13f0ba1b --- /dev/null +++ b/changelog/change_enhance_lint_self_assignment_to_detect_indexed_assignment_with_many_arguments_20250806224151.md @@ -0,0 +1 @@ +* [#14428](https://github.com/rubocop/rubocop/pull/14428): Enhance `Lint/SelfAssignment` to handle indexed assignment with multiple arguments. ([@viralpraxis][]) diff --git a/lib/rubocop/cop/lint/self_assignment.rb b/lib/rubocop/cop/lint/self_assignment.rb index 51ee1d7dc01f..445162093b26 100644 --- a/lib/rubocop/cop/lint/self_assignment.rb +++ b/lib/rubocop/cop/lint/self_assignment.rb @@ -45,7 +45,7 @@ def on_send(node) return if allow_rbs_inline_annotation? && rbs_inline_annotation?(node.receiver) if node.method?(:[]=) - handle_key_assignment(node) if node.arguments.size == 2 + handle_key_assignment(node) elsif node.assignment_method? handle_attribute_assignment(node) if node.arguments.size == 1 end @@ -105,12 +105,13 @@ def rhs_matches_lhs?(rhs, lhs) end def handle_key_assignment(node) - value_node = node.arguments[1] + value_node = node.last_argument + node_arguments = node.arguments[0...-1] if value_node.send_type? && value_node.method?(:[]) && node.receiver == value_node.receiver && - !node.first_argument.call_type? && - node.first_argument == value_node.first_argument + node_arguments.none?(&:call_type?) && + node_arguments == value_node.arguments add_offense(node) end end diff --git a/spec/rubocop/cop/lint/self_assignment_spec.rb b/spec/rubocop/cop/lint/self_assignment_spec.rb index bfe3dedb3eb6..61fe60da4445 100644 --- a/spec/rubocop/cop/lint/self_assignment_spec.rb +++ b/spec/rubocop/cop/lint/self_assignment_spec.rb @@ -319,6 +319,32 @@ RUBY end + it 'registers an offense when ussing `[]=` self-assignment with multiple key arguments' do + expect_offense(<<~RUBY) + matrix[1, 2] = matrix[1, 2] + ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Self-assignment detected. + RUBY + end + + it 'does not register an offense when ussing `[]=` self-assignment with multiple key arguments with arguments mismatch' do + expect_no_offenses(<<~RUBY) + matrix[1, 2] = matrix[1, 3] + RUBY + end + + it 'does not register an offense when ussing `[]=` self-assignment with multiple key arguments with method call argument' do + expect_no_offenses(<<~RUBY) + matrix[1, foo] = matrix[1, foo] + RUBY + end + + it 'registers an offense when ussing `[]=` self-assignment with using zero key arguments' do + expect_offense(<<~RUBY) + singleton[] = singleton[] + ^^^^^^^^^^^^^^^^^^^^^^^^^ Self-assignment detected. + RUBY + end + describe 'RBS::Inline annotation' do context 'when config option is enabled' do let(:cop_config) { { 'AllowRBSInlineAnnotation' => true } } From 877687a5f01e5d90d573999aea9f0f8455cc3a06 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Sat, 9 Aug 2025 10:53:24 +0800 Subject: [PATCH 06/37] Tweak a changelog entry Follow-up to https://github.com/rubocop/rubocop/pull/14424 --- ...x_fix_safe_navigation_cop_to_preserve_safe_20250805124624.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/fix_fix_safe_navigation_cop_to_preserve_safe_20250805124624.md b/changelog/fix_fix_safe_navigation_cop_to_preserve_safe_20250805124624.md index 936b125ed709..a6538a984a00 100644 --- a/changelog/fix_fix_safe_navigation_cop_to_preserve_safe_20250805124624.md +++ b/changelog/fix_fix_safe_navigation_cop_to_preserve_safe_20250805124624.md @@ -1 +1 @@ -* [#14424](https://github.com/rubocop/rubocop/pull/14424): Fix SafeNavigation cop to preserve existing safe navigation in fixed code. ([@martinemde][]) +* [#14424](https://github.com/rubocop/rubocop/pull/14424): Fix `Style/SafeNavigation` cop to preserve existing safe navigation in fixed code. ([@martinemde][]) From c595cf6e2d5cfa336ac82ad1a77200288b7ed7b6 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Mon, 11 Aug 2025 02:54:40 +0800 Subject: [PATCH 07/37] Use `RuboCop::AST::Node#post_condition_loop?` --- lib/rubocop/cop/style/infinite_loop.rb | 2 +- lib/rubocop/cop/variable_force.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rubocop/cop/style/infinite_loop.rb b/lib/rubocop/cop/style/infinite_loop.rb index b215dcfaf069..7174ac3b3dfd 100644 --- a/lib/rubocop/cop/style/infinite_loop.rb +++ b/lib/rubocop/cop/style/infinite_loop.rb @@ -68,7 +68,7 @@ def while_or_until(node) end def autocorrect(corrector, node) - if node.type?(:while_post, :until_post) + if node.post_condition_loop? replace_begin_end_with_modifier(corrector, node) elsif node.modifier_form? replace_source(corrector, node.source_range, modifier_replacement(node)) diff --git a/lib/rubocop/cop/variable_force.rb b/lib/rubocop/cop/variable_force.rb index 3a110792c4fe..83d0c47b80f4 100644 --- a/lib/rubocop/cop/variable_force.rb +++ b/lib/rubocop/cop/variable_force.rb @@ -238,7 +238,7 @@ def process_variable_referencing(node) end def process_loop(node) - if POST_CONDITION_LOOP_TYPES.include?(node.type) + if node.post_condition_loop? # See the comment at the end of file for this behavior. condition_node, body_node = *node process_node(body_node) From 620921b946baa427686f4723b409d115e21c3965 Mon Sep 17 00:00:00 2001 From: TOMITA Masahiro Date: Mon, 11 Aug 2025 12:33:03 +0900 Subject: [PATCH 08/37] support LSP TextDocumentSyncKind.Incremental --- ...nt_sync_kind_incremental_20250811135600.md | 1 + lib/rubocop/lsp/routes.rb | 29 +++++++++++++++---- spec/rubocop/lsp/server_spec.rb | 2 +- 3 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 changelog/change_support_lsp_text_document_sync_kind_incremental_20250811135600.md diff --git a/changelog/change_support_lsp_text_document_sync_kind_incremental_20250811135600.md b/changelog/change_support_lsp_text_document_sync_kind_incremental_20250811135600.md new file mode 100644 index 000000000000..0bf596575be0 --- /dev/null +++ b/changelog/change_support_lsp_text_document_sync_kind_incremental_20250811135600.md @@ -0,0 +1 @@ +* [#14431](https://github.com/rubocop/rubocop/pull/14431): Support LSP TextDocumentSyncKind.Incremental. ([@tmtm][]) diff --git a/lib/rubocop/lsp/routes.rb b/lib/rubocop/lsp/routes.rb index be96dcaa0858..0f2f59a0d0f1 100644 --- a/lib/rubocop/lsp/routes.rb +++ b/lib/rubocop/lsp/routes.rb @@ -51,7 +51,7 @@ def for(name) capabilities: LanguageServer::Protocol::Interface::ServerCapabilities.new( document_formatting_provider: true, text_document_sync: LanguageServer::Protocol::Interface::TextDocumentSyncOptions.new( - change: LanguageServer::Protocol::Constant::TextDocumentSyncKind::FULL, + change: LanguageServer::Protocol::Constant::TextDocumentSyncKind::INCREMENTAL, open_close: true ) ) @@ -76,7 +76,8 @@ def for(name) handle 'textDocument/didChange' do |request| params = request[:params] - result = diagnostic(params[:textDocument][:uri], params[:contentChanges][0][:text]) + content = params[:contentChanges][0] + result = diagnostic(params[:textDocument][:uri], content[:text], content[:range]) @server.write(result) end @@ -207,18 +208,36 @@ def format_file(file_uri, command: nil) ] end - def diagnostic(file_uri, text) - @text_cache[file_uri] = text + def diagnostic(file_uri, text, range = nil) + if range + start_pos = text_pos(@text_cache[file_uri], range[:start]) + end_pos = text_pos(@text_cache[file_uri], range[:end]) + @text_cache[file_uri].bytesplice(start_pos...end_pos, text) + else + @text_cache[file_uri] = text + end { method: 'textDocument/publishDiagnostics', params: { uri: file_uri, - diagnostics: @server.offenses(convert_file_uri_to_path(file_uri), text) + diagnostics: @server.offenses(convert_file_uri_to_path(file_uri), @text_cache[file_uri]) } } end + def text_pos(text, range) + line = range[:line] + char = range[:character] + pos = 0 + text.each_line.with_index do |l, i| + return pos + char if i == line + + pos += l.bytesize + end + pos + end + def convert_file_uri_to_path(uri) URI.decode_www_form_component(uri.delete_prefix('file://')) end diff --git a/spec/rubocop/lsp/server_spec.rb b/spec/rubocop/lsp/server_spec.rb index e1d2e7a8b6e2..103579db91cc 100644 --- a/spec/rubocop/lsp/server_spec.rb +++ b/spec/rubocop/lsp/server_spec.rb @@ -40,7 +40,7 @@ id: 2, result: { capabilities: { - textDocumentSync: { openClose: true, change: 1 }, + textDocumentSync: { openClose: true, change: 2 }, documentFormattingProvider: true } } From dbc8ac584f52a37e6da17a9373df16af0a26fe38 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Aug 2025 21:29:26 +0000 Subject: [PATCH 09/37] Bump actions/checkout from 4 to 5 Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/github_release.yml | 2 +- .github/workflows/linting.yml | 6 +++--- .github/workflows/rubocop.yml | 14 +++++++------- .github/workflows/spell_checking.yml | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/github_release.yml b/.github/workflows/github_release.yml index 2607babd6251..379dcebf60c4 100644 --- a/.github/workflows/github_release.yml +++ b/.github/workflows/github_release.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Checkout Code - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Create GitHub Release uses: ncipollo/release-action@v1 diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index e9e651c899e8..819944f72663 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -15,7 +15,7 @@ jobs: name: Ruby runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ruby/setup-ruby@v1 with: ruby-version: ruby # Latest stable CRuby version @@ -31,7 +31,7 @@ jobs: - uses: ruby/setup-ruby@v1 with: ruby-version: ruby # Latest stable CRuby version - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Package and install RuboCop locally run: | gem build @@ -53,7 +53,7 @@ jobs: name: Yaml runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Yamllint uses: karancode/yamllint-github-action@v3.0.0 with: diff --git a/.github/workflows/rubocop.yml b/.github/workflows/rubocop.yml index 8ab9ee1d39d0..168ad9147ce2 100644 --- a/.github/workflows/rubocop.yml +++ b/.github/workflows/rubocop.yml @@ -24,7 +24,7 @@ jobs: ruby: ['2.7', '3.0', '3.1', '3.2', '3.3', '3.4', 'head'] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} @@ -42,7 +42,7 @@ jobs: name: Spec - JRuby runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ruby/setup-ruby@v1 with: ruby-version: 'jruby' # Latest stable JRuby version @@ -62,7 +62,7 @@ jobs: - 'ruby' # Latest stable CRuby version steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} @@ -88,7 +88,7 @@ jobs: - 'ruby' # Latest stable CRuby version steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} @@ -102,7 +102,7 @@ jobs: name: Documentation Check runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ruby/setup-ruby@v1 with: ruby-version: ruby # Latest stable CRuby version @@ -114,7 +114,7 @@ jobs: runs-on: ubuntu-latest name: Prism steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ruby/setup-ruby@v1 with: # Specify the minimum Ruby version 2.7 required for Prism to run. @@ -129,7 +129,7 @@ jobs: runs-on: ubuntu-latest name: RSpec 4 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Use latest RSpec 4 from `4-0-dev` branch run: | sed -e "/'rspec', '~> 3/d" -i Gemfile diff --git a/.github/workflows/spell_checking.yml b/.github/workflows/spell_checking.yml index ef255974413f..114c0d875b7a 100644 --- a/.github/workflows/spell_checking.yml +++ b/.github/workflows/spell_checking.yml @@ -14,14 +14,14 @@ jobs: name: Check spelling of all files with codespell runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: codespell-project/actions-codespell@v2 misspell: name: Check spelling of all files in commit with misspell runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install run: wget -O - -q https://raw.githubusercontent.com/client9/misspell/master/install-misspell.sh | sh -s -- -b . - name: Misspell From b7d2b494e7f763f0363aa9ced577e10b3a6fd14a Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Tue, 12 Aug 2025 06:56:10 +0800 Subject: [PATCH 10/37] Update test for `LoadError` message change in JRuby 10.0.2.0 This updates a test to reflect the change in the `LoadError` message in JRuby 10.0.2.0: ## JRuby 10.0.1.0 > `LoadError: no such file to load -- unknownlibrary` ```console $ ruby -ve "load('unknownlibrary')" jruby 10.0.1.0 (3.4.2) 2025-07-17 0f10d1dfdf Java HotSpot(TM) 64-Bit Server VM 24.0.1+9-30 on 24.0.1+9-30 +indy +jit [arm64-darwin] LoadError: no such file to load -- unknownlibrary load at org/jruby/RubyKernel.java:1212
at -e:1 ``` ## JRuby 10.0.2.0 > `LoadError: cannot load such file -- unknownlibrary` ```console $ ruby -ve "load('unknownlibrary')" jruby 10.0.2.0 (3.4.2) 2025-08-07 cba6031bd0 Java HotSpot(TM) 64-Bit Server VM 24.0.1+9-30 on 24.0.1+9-30 +indy +jit [arm64-darwin] LoadError: cannot load such file -- unknownlibrary load at org/jruby/RubyKernel.java:1212
at -e:1 ``` This will fix the following test: ```console $ bundle exec rspec spec/rubocop/cli_spec.rb:2063 (snip) Failures: 1) RuboCop::CLI configuration of `require` unknown library is specified exits with 2 Failure/Error: expect($stderr.string).to match(regexp) expected "cannot load such file -- unknownlibrary\n/Users/koic/src/github.com/rubocop/rubocop/lib/rubocop/feat...yKernel.java:1212:in 'load'\n/Users/koic/.rbenv/versions/jruby-10.0.2.0/bin/bundle:25:in '
'\n" to match /no such file to load -- unknownlibrary/ Diff: @@ -1 +1,99 @@ -/no such file to load -- unknownlibrary/ +cannot load such file -- unknownlibrary +/Users/koic/src/github.com/rubocop/rubocop/lib/rubocop/feature_loader.rb:46:in 'load' ``` https://github.com/rubocop/rubocop/actions/runs/16893928053/job/47859836538#step:4:628 --- spec/rubocop/cli_spec.rb | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/spec/rubocop/cli_spec.rb b/spec/rubocop/cli_spec.rb index 4546a7e8776d..63f0af1eff2d 100644 --- a/spec/rubocop/cli_spec.rb +++ b/spec/rubocop/cli_spec.rb @@ -2066,14 +2066,8 @@ def method(foo, bar, qux, fred, arg5, f) end #{'#' * 85} require: unknownlibrary YAML - regexp = - if RUBY_ENGINE == 'jruby' - /no such file to load -- unknownlibrary/ - else - /cannot load such file -- unknownlibrary/ - end expect(cli.run([])).to eq(2) - expect($stderr.string).to match(regexp) + expect($stderr.string).to match(/cannot load such file -- unknownlibrary/) end end From c10b748b144511f434e901f9f622320bb47d312e Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Tue, 12 Aug 2025 12:25:40 +0200 Subject: [PATCH 11/37] Remove a jruby test skip I fixed this in https://github.com/jruby/jruby/pull/8769 --- spec/rubocop/cop/style/redundant_format_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rubocop/cop/style/redundant_format_spec.rb b/spec/rubocop/cop/style/redundant_format_spec.rb index 2ef40cbc28ca..35f16939a4fb 100644 --- a/spec/rubocop/cop/style/redundant_format_spec.rb +++ b/spec/rubocop/cop/style/redundant_format_spec.rb @@ -189,7 +189,7 @@ it_behaves_like 'offending format specifier', '% d', '5', "' 5'" it_behaves_like 'offending format specifier', '%+d', '5', "'+5'" it_behaves_like 'offending format specifier', '%.3d', '10', "'010'" - it_behaves_like 'offending format specifier', '%.d', '0', "''", broken_on: :jruby + it_behaves_like 'offending format specifier', '%.d', '0', "''" it_behaves_like 'offending format specifier', '%05d', '5', "'00005'" it_behaves_like 'offending format specifier', '%.2f', '5', "'5.00'" it_behaves_like 'offending format specifier', '%10.2f', '5', "' 5.00'" From b776d32b4fa253a9b212489c3c4dea9a8845acba Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Tue, 12 Aug 2025 10:45:22 +0200 Subject: [PATCH 12/37] [Fix #14435] Fix false negatives for regexp cops when `Lint/DuplicateRegexpCharacterClassElement` is enabled It does `expressions = expressions.to_a`, which does not create a copy. So other cops that run after would not see all the expressions. `.dup` would solve this, but all this does is work around a bug in `regexp_parser` < 2.7.0. RuboCop now requires >= 2.9.3 so all that code can simply be removed. I didn't add a new test because the code that would be tested is gone now. Also see https://github.com/rubocop/rubocop/pull/13875 for something quite similar. --- changelog/fix_false_negatives_regexp_cops.md | 1 + ...uplicate_regexp_character_class_element.rb | 47 ++----------------- lib/rubocop/cop/style/symbol_array.rb | 2 +- lib/rubocop/target_finder.rb | 2 +- spec/rubocop/cli/autocorrect_spec.rb | 30 ++++++++++++ 5 files changed, 38 insertions(+), 44 deletions(-) create mode 100644 changelog/fix_false_negatives_regexp_cops.md diff --git a/changelog/fix_false_negatives_regexp_cops.md b/changelog/fix_false_negatives_regexp_cops.md new file mode 100644 index 000000000000..20aca4916545 --- /dev/null +++ b/changelog/fix_false_negatives_regexp_cops.md @@ -0,0 +1 @@ +* [#14435](https://github.com/rubocop/rubocop/issues/14435): Fix false negatives for regexp cops when `Lint/DuplicateRegexpCharacterClassElement` is enabled. ([@earlopain][]) diff --git a/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb b/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb index 48c05c4e9314..780d9401c599 100644 --- a/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +++ b/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb @@ -24,8 +24,6 @@ class DuplicateRegexpCharacterClassElement < Base MSG_REPEATED_ELEMENT = 'Duplicate element inside regexp character class' - OCTAL_DIGITS_AFTER_ESCAPE = 2 - def on_regexp(node) each_repeated_character_class_element_loc(node) do |loc| add_offense(loc, message: MSG_REPEATED_ELEMENT) do |corrector| @@ -40,9 +38,9 @@ def each_repeated_character_class_element_loc(node) seen = Set.new group_expressions(node, expr.expressions) do |group| - group_source = group.map(&:to_s).join + group_source = group.to_s - yield source_range(group) if seen.include?(group_source) + yield group.expression if seen.include?(group_source) seen << group_source end @@ -52,40 +50,13 @@ def each_repeated_character_class_element_loc(node) private def group_expressions(node, expressions) - # Create a mutable list to simplify state tracking while we iterate. - expressions = expressions.to_a - - until expressions.empty? - # With we may need to compose a group of multiple expressions. - group = [expressions.shift] - next if within_interpolation?(node, group.first) - - # With regexp_parser < 2.7 escaped octal sequences may be up to 3 - # separate expressions ("\\0", "0", "1"). - pop_octal_digits(group, expressions) if escaped_octal?(group.first.to_s) - - yield(group) - end - end - - def pop_octal_digits(current_child, expressions) - OCTAL_DIGITS_AFTER_ESCAPE.times do - next_child = expressions.first - break unless octal?(next_child.to_s) + expressions.each do |expression| + next if within_interpolation?(node, expression) - current_child << expressions.shift + yield(expression) end end - def source_range(children) - return children.first.expression if children.size == 1 - - range_between( - children.first.expression.begin_pos, - children.last.expression.begin_pos + children.last.to_s.length - ) - end - def skip_expression?(expr) expr.type != :set || expr.token == :intersection end @@ -99,14 +70,6 @@ def within_interpolation?(node, child) interpolation_locs(node).any? { |il| il.overlaps?(parse_tree_child_loc) } end - def escaped_octal?(string) - string.length == 2 && string[0] == '\\' && octal?(string[1]) - end - - def octal?(char) - ('0'..'7').cover?(char) - end - def interpolation_locs(node) @interpolation_locs ||= {} diff --git a/lib/rubocop/cop/style/symbol_array.rb b/lib/rubocop/cop/style/symbol_array.rb index 66d5fbd5f717..8728a7804fce 100644 --- a/lib/rubocop/cop/style/symbol_array.rb +++ b/lib/rubocop/cop/style/symbol_array.rb @@ -81,7 +81,7 @@ def complex_content?(node) content = *sym content = content.map { |c| c.is_a?(AST::Node) ? c.source : c }.join - content_without_delimiter_pairs = content.gsub(/(\[[^\s\[\]]*\])|(\([^\s\(\)]*\))/, '') + content_without_delimiter_pairs = content.gsub(/(\[[^\s\[\]]*\])|(\([^\s()]*\))/, '') content.include?(' ') || DELIMITERS.any? do |delimiter| content_without_delimiter_pairs.include?(delimiter) diff --git a/lib/rubocop/target_finder.rb b/lib/rubocop/target_finder.rb index 223cb35479f7..deb7e2aee783 100644 --- a/lib/rubocop/target_finder.rb +++ b/lib/rubocop/target_finder.rb @@ -85,7 +85,7 @@ def to_inspect?(file, hidden_files, base_dir_config) def wanted_dir_patterns(base_dir, exclude_pattern, flags) # Escape glob characters in base_dir to avoid unwanted behavior. - base_dir = base_dir.gsub(/[\\\{\}\[\]\*\?]/) do |reserved_glob_character| + base_dir = base_dir.gsub(/[\\{}\[\]*?]/) do |reserved_glob_character| "\\#{reserved_glob_character}" end diff --git a/spec/rubocop/cli/autocorrect_spec.rb b/spec/rubocop/cli/autocorrect_spec.rb index 3658a2d9c924..fadea629da39 100644 --- a/spec/rubocop/cli/autocorrect_spec.rb +++ b/spec/rubocop/cli/autocorrect_spec.rb @@ -3974,4 +3974,34 @@ def foo RESULT expect($stderr.string).to eq('') end + + it 'registers an offense and corrects for `Style/RedundantRegexpEscape` when `NewCops: enable`' do + create_file('.rubocop.yml', <<~YAML) + AllCops: + NewCops: enable + Style/FrozenStringLiteralComment: + Enabled: false + YAML + source = <<~'RUBY' + /[\.-]/ + RUBY + create_file('example.rb', source) + expect(cli.run(['--autocorrect-all'])).to eq(0) + expect($stdout.string).to eq(<<~'RESULT') + Inspecting 1 file + C + + Offenses: + + example.rb:1:3: C: [Corrected] Style/RedundantRegexpEscape: Redundant escape inside regexp literal + /[\.-]/ + ^^ + + 1 file inspected, 1 offense detected, 1 offense corrected + RESULT + expect($stderr.string).to eq('') + expect(File.read('example.rb')).to eq(<<~RUBY) + /[.-]/ + RUBY + end end From c34098ee0512bbee0d56b8d04ad9f3cd7f0b326b Mon Sep 17 00:00:00 2001 From: Bozhidar Batsov Date: Tue, 12 Aug 2025 13:30:54 +0300 Subject: [PATCH 13/37] Tweak a changelog entry --- ...rt_lsp_text_document_sync_kind_incremental_20250811135600.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/change_support_lsp_text_document_sync_kind_incremental_20250811135600.md b/changelog/change_support_lsp_text_document_sync_kind_incremental_20250811135600.md index 0bf596575be0..844c1bbcefa7 100644 --- a/changelog/change_support_lsp_text_document_sync_kind_incremental_20250811135600.md +++ b/changelog/change_support_lsp_text_document_sync_kind_incremental_20250811135600.md @@ -1 +1 @@ -* [#14431](https://github.com/rubocop/rubocop/pull/14431): Support LSP TextDocumentSyncKind.Incremental. ([@tmtm][]) +* [#14431](https://github.com/rubocop/rubocop/pull/14431): Support LSP `TextDocumentSyncKind.Incremental`. ([@tmtm][]) From ca3bbcbaf118e2495b8d3b06c58add24e1037e04 Mon Sep 17 00:00:00 2001 From: Aleksandr Borisov Date: Thu, 14 Aug 2025 11:45:43 +0300 Subject: [PATCH 14/37] Mention Rubocop::Cop::Base#external_dependency_checksum in docs Co-authored-by: Koichi ITO --- docs/modules/ROOT/pages/usage/caching.adoc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/modules/ROOT/pages/usage/caching.adoc b/docs/modules/ROOT/pages/usage/caching.adoc index 6269ca8de442..da2ca35e2077 100644 --- a/docs/modules/ROOT/pages/usage/caching.adoc +++ b/docs/modules/ROOT/pages/usage/caching.adoc @@ -20,6 +20,11 @@ bearing on which offenses are reported * version of the `rubocop` program (or to be precise, anything in the source code of the invoked `rubocop` program) +In rare cases, you may need to invalidate the cache when changing +external dependencies. This happens when your cop depends on external +configuration. In this case, override the method +`RuboCop::Cop::Base#external_dependency_checksum`. + == Enabling and Disabling the Cache The caching functionality is enabled if the configuration parameter From eeac0de77405a4831ff842fec44e856d4d932778 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Thu, 14 Aug 2025 01:59:29 +0900 Subject: [PATCH 15/37] Fix an unexpected diagnostic with `TextDocumentSyncKind.Incremental` on the LSP Follow-up to https://github.com/rubocop/rubocop/pull/14431#issuecomment-3184481229 This PR fixes an unexpect diagnostic with `TextDocumentSyncKind.Incremental` on the LSP when `contentChanges` contains multiple entries. Since #14431 changed `TextDocumentSyncKind` from `Full` to `Incremental`, it is necessary to handle the possibility that `params[:contentChanges]` may contain multiple entries. Ref: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_synchronization --- lib/rubocop/lsp/routes.rb | 7 +++-- spec/rubocop/lsp/server_spec.rb | 51 +++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/lib/rubocop/lsp/routes.rb b/lib/rubocop/lsp/routes.rb index 0f2f59a0d0f1..6f9797a9cb6b 100644 --- a/lib/rubocop/lsp/routes.rb +++ b/lib/rubocop/lsp/routes.rb @@ -76,9 +76,10 @@ def for(name) handle 'textDocument/didChange' do |request| params = request[:params] - content = params[:contentChanges][0] - result = diagnostic(params[:textDocument][:uri], content[:text], content[:range]) - @server.write(result) + params[:contentChanges].each do |content| + result = diagnostic(params[:textDocument][:uri], content[:text], content[:range]) + @server.write(result) + end end handle 'textDocument/didOpen' do |request| diff --git a/spec/rubocop/lsp/server_spec.rb b/spec/rubocop/lsp/server_spec.rb index 103579db91cc..417c0085564f 100644 --- a/spec/rubocop/lsp/server_spec.rb +++ b/spec/rubocop/lsp/server_spec.rb @@ -1319,6 +1319,57 @@ end end + describe 'formatting via multiple entries of `contentChanges`' do + let(:requests) do + [ + { + jsonrpc: '2.0', + method: 'textDocument/didOpen', + params: { + textDocument: { + languageId: 'ruby', + text: "puts 'hi'", + uri: 'file:///path/to/file.rb', + version: 0 + } + } + }, { + jsonrpc: '2.0', + method: 'textDocument/didChange', + params: { + contentChanges: [{ text: "puts 'first'" }, { text: "puts 'last'" }], + textDocument: { + uri: 'file:///path/to/file.rb', + version: 10 + } + } + }, { + jsonrpc: '2.0', + id: 20, + method: 'textDocument/formatting', + params: { + options: { insertSpaces: true, tabSize: 2 }, + textDocument: { uri: 'file:///path/to/file.rb' } + } + } + ] + end + + it 'handles requests' do + expect(messages.count).to eq(4) + expect(messages.last).to eq( + jsonrpc: '2.0', id: 20, result: [ + { + newText: "puts 'last'\n", + range: { + end: { character: 0, line: 1 }, start: { character: 0, line: 0 } + } + } + ] + ) + end + end + describe 'formatting via formatting path on ignored path' do let(:requests) do [ From 3bb85c910d71cbd5313b7ee6e9a20e406cbe239a Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Thu, 14 Aug 2025 12:55:21 +0200 Subject: [PATCH 16/37] Fix wrong autocorrect for `Style/RedundantCondition` with parenthesised method call in condition When there are already parenthesis, don't do it again. Previously the autocorrect produced the following invalid syntax: `foo?(arg)) || bar` --- ...wrong_autocorrect_style_redundant_condition.md | 1 + lib/rubocop/cop/style/redundant_condition.rb | 2 +- .../rubocop/cop/style/redundant_condition_spec.rb | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 changelog/fix_wrong_autocorrect_style_redundant_condition.md diff --git a/changelog/fix_wrong_autocorrect_style_redundant_condition.md b/changelog/fix_wrong_autocorrect_style_redundant_condition.md new file mode 100644 index 000000000000..28772cb38c85 --- /dev/null +++ b/changelog/fix_wrong_autocorrect_style_redundant_condition.md @@ -0,0 +1 @@ +* [#14447](https://github.com/rubocop/rubocop/pull/14447): Fix wrong autocorrect for `Style/RedundantCondition` with a parenthesised method call in the condition. ([@earlopain][]) diff --git a/lib/rubocop/cop/style/redundant_condition.rb b/lib/rubocop/cop/style/redundant_condition.rb index e578b85ae18b..cd34ffb27a54 100644 --- a/lib/rubocop/cop/style/redundant_condition.rb +++ b/lib/rubocop/cop/style/redundant_condition.rb @@ -247,7 +247,7 @@ def if_source(if_branch, arithmetic_operation) "#{if_branch.receiver.source} #{if_branch.method_name} (#{argument_source}" elsif if_branch.true_type? condition = if_branch.parent.condition - return condition.source if condition.arguments.empty? + return condition.source if condition.arguments.empty? || condition.parenthesized? wrap_arguments_with_parens(condition) else diff --git a/spec/rubocop/cop/style/redundant_condition_spec.rb b/spec/rubocop/cop/style/redundant_condition_spec.rb index 222d6e310d00..f80545360486 100644 --- a/spec/rubocop/cop/style/redundant_condition_spec.rb +++ b/spec/rubocop/cop/style/redundant_condition_spec.rb @@ -678,6 +678,21 @@ def do_something(foo, ...) RUBY end + it 'registers an offense and autocorrects when true is used the the true branch and the condition is a parenthesized predicate call with arguments' do + expect_offense(<<~RUBY) + if foo?(arg) + ^^^^^^^^^^^^ Use double pipes `||` instead. + true + else + bar + end + RUBY + + expect_correction(<<~RUBY) + foo?(arg) || bar + RUBY + end + it 'registers an offense and autocorrects when true is used as the true branch and the condition takes arguments with safe navigation' do expect_offense(<<~RUBY) if obj&.foo? arg From 49c140b387fa801662f7bcb7e7723417d3454dbb Mon Sep 17 00:00:00 2001 From: Issy Long Date: Wed, 13 Aug 2025 23:51:54 +0100 Subject: [PATCH 17/37] [Fix #14443] Fix false positive in `Layout/EmptyLinesAfterModuleInclusion` - Don't register an offense (and hence don't infinite loop when autocorrecting `Layout/EmptyLinesAfterModuleInclusion` and `Layout/EmptyLinesAroundBlockBody`) when the `include` send node is not followed by a constant argument. Example: ```ruby includes = [include, sdk_include].compact ``` vs. ```ruby include SdkIncludeModule ``` --- changelog/fix_fix_false_positive_in_20250814000019.md | 1 + .../cop/layout/empty_lines_after_module_inclusion.rb | 2 +- .../layout/empty_lines_after_module_inclusion_spec.rb | 10 ++++++++++ 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 changelog/fix_fix_false_positive_in_20250814000019.md diff --git a/changelog/fix_fix_false_positive_in_20250814000019.md b/changelog/fix_fix_false_positive_in_20250814000019.md new file mode 100644 index 000000000000..ed6db89c262b --- /dev/null +++ b/changelog/fix_fix_false_positive_in_20250814000019.md @@ -0,0 +1 @@ +* [#14443](https://github.com/rubocop/rubocop/issues/14443): Fix false positive in `Layout/EmptyLinesAfterModuleInclusion` when `include` does not have exactly one argument. ([@issyl0][]) diff --git a/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb b/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb index 0c09609550f5..16f184c2b09d 100644 --- a/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +++ b/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb @@ -38,7 +38,7 @@ class EmptyLinesAfterModuleInclusion < Base RESTRICT_ON_SEND = MODULE_INCLUSION_METHODS def on_send(node) - return if node.receiver + return if node.receiver || !node.arguments.one? return if node.parent&.type?(:send, :any_block) return if next_line_empty_or_enable_directive_comment?(node.last_line) diff --git a/spec/rubocop/cop/layout/empty_lines_after_module_inclusion_spec.rb b/spec/rubocop/cop/layout/empty_lines_after_module_inclusion_spec.rb index 85d15ab85137..aa7ec58a8248 100644 --- a/spec/rubocop/cop/layout/empty_lines_after_module_inclusion_spec.rb +++ b/spec/rubocop/cop/layout/empty_lines_after_module_inclusion_spec.rb @@ -242,6 +242,16 @@ module Bar RUBY end + it 'does not register an offense when `include` does not have exactly one argument' do + expect_no_offenses(<<~RUBY) + class Foo + includes = [include, sdk_include].compact.map do |inc| + inc + "blah" + end.join(' ') + end + RUBY + end + it 'does not register an offense when module inclusion is called with modifier' do expect_no_offenses(<<~RUBY) class Foo From 230649aa2bd1cb2c3335e7faa05092bedf58eed5 Mon Sep 17 00:00:00 2001 From: Lovro Bikic Date: Sat, 16 Aug 2025 16:45:20 +0200 Subject: [PATCH 18/37] Change offense range for Lint/MissingCopEnableDirective to whole comment for better visibility --- lib/rubocop/cop/lint/missing_cop_enable_directive.rb | 3 +-- .../cop/lint/missing_cop_enable_directive_spec.rb | 12 ++++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/rubocop/cop/lint/missing_cop_enable_directive.rb b/lib/rubocop/cop/lint/missing_cop_enable_directive.rb index d286548bbaeb..6dce1ff72972 100644 --- a/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +++ b/lib/rubocop/cop/lint/missing_cop_enable_directive.rb @@ -52,10 +52,9 @@ def on_new_investigation each_missing_enable do |cop, line_range| next if acceptable_range?(cop, line_range) - range = source_range(processed_source.buffer, line_range.min, 0..0) comment = processed_source.comment_at_line(line_range.begin) - add_offense(range, message: message(cop, comment)) + add_offense(comment, message: message(cop, comment)) end end diff --git a/spec/rubocop/cop/lint/missing_cop_enable_directive_spec.rb b/spec/rubocop/cop/lint/missing_cop_enable_directive_spec.rb index 484deb96f7fa..2e9a2acacf0c 100644 --- a/spec/rubocop/cop/lint/missing_cop_enable_directive_spec.rb +++ b/spec/rubocop/cop/lint/missing_cop_enable_directive_spec.rb @@ -8,7 +8,7 @@ it 'registers an offense when a cop is disabled and never re-enabled' do expect_offense(<<~RUBY) # rubocop:disable Layout/SpaceAroundOperators - ^ Re-enable Layout/SpaceAroundOperators cop with `# rubocop:enable` after disabling it. + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Re-enable Layout/SpaceAroundOperators cop with `# rubocop:enable` after disabling it. x = 0 # Some other code RUBY @@ -26,7 +26,7 @@ it 'registers an offense when a department is disabled and never re-enabled' do expect_offense(<<~RUBY) # rubocop:disable Layout - ^ Re-enable Layout department with `# rubocop:enable` after disabling it. + ^^^^^^^^^^^^^^^^^^^^^^^^ Re-enable Layout department with `# rubocop:enable` after disabling it. x = 0 # Some other code RUBY @@ -48,7 +48,7 @@ it 'registers an offense when a cop is disabled for too many lines' do expect_offense(<<~RUBY) # rubocop:disable Layout/SpaceAroundOperators - ^ Re-enable Layout/SpaceAroundOperators cop within 2 lines after disabling it. + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Re-enable Layout/SpaceAroundOperators cop within 2 lines after disabling it. x = 0 y = 1 # Some other code @@ -59,7 +59,7 @@ it 'registers an offense when a cop is disabled and never re-enabled' do expect_offense(<<~RUBY) # rubocop:disable Layout/SpaceAroundOperators - ^ Re-enable Layout/SpaceAroundOperators cop within 2 lines after disabling it. + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Re-enable Layout/SpaceAroundOperators cop within 2 lines after disabling it. x = 0 # Some other code RUBY @@ -78,7 +78,7 @@ it 'registers an offense when a department is disabled for too many lines' do expect_offense(<<~RUBY) # rubocop:disable Layout - ^ Re-enable Layout department within 2 lines after disabling it. + ^^^^^^^^^^^^^^^^^^^^^^^^ Re-enable Layout department within 2 lines after disabling it. x = 0 y = 1 # Some other code @@ -89,7 +89,7 @@ it 'registers an offense when a department is disabled and never re-enabled' do expect_offense(<<~RUBY) # rubocop:disable Layout - ^ Re-enable Layout department within 2 lines after disabling it. + ^^^^^^^^^^^^^^^^^^^^^^^^ Re-enable Layout department within 2 lines after disabling it. x = 0 # Some other code RUBY From 7a08e3db6a5948347347864d1c36dc6162485ab8 Mon Sep 17 00:00:00 2001 From: Issy Long Date: Fri, 15 Aug 2025 00:55:45 +0100 Subject: [PATCH 19/37] [Fix #14441] Better hash access handling in `Style/SafeNavigation` - Make the `matching_call_nodes?` method account for differences in arguments. - Code like `foo[:bar] && foo[:baz].blank?` does not need safe navigation because the hash keys are different. --- ...14441_better_hash_access_handling_in_20250815010135.md | 1 + lib/rubocop/cop/style/safe_navigation.rb | 8 +++++--- spec/rubocop/cop/style/safe_navigation_spec.rb | 4 ++++ 3 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 changelog/fix_fix_14441_better_hash_access_handling_in_20250815010135.md diff --git a/changelog/fix_fix_14441_better_hash_access_handling_in_20250815010135.md b/changelog/fix_fix_14441_better_hash_access_handling_in_20250815010135.md new file mode 100644 index 000000000000..a95c2c305b6c --- /dev/null +++ b/changelog/fix_fix_14441_better_hash_access_handling_in_20250815010135.md @@ -0,0 +1 @@ +* [#14441](https://github.com/rubocop/rubocop/issues/14441): Better hash access handling in `Style/SafeNavigation`. ([@issyl0][]) diff --git a/lib/rubocop/cop/style/safe_navigation.rb b/lib/rubocop/cop/style/safe_navigation.rb index 91ddf9e50585..7e7779cf46c6 100644 --- a/lib/rubocop/cop/style/safe_navigation.rb +++ b/lib/rubocop/cop/style/safe_navigation.rb @@ -342,10 +342,12 @@ def matching_call_nodes?(left, right) # Compare receiver and method name, but ignore the difference between # safe navigation method call (`&.`) and dot method call (`.`). - left_receiver, left_method = left.children.take(2) - right_receiver, right_method = right.children.take(2) + left_receiver, left_method, *left_args = left.children + right_receiver, right_method, *right_args = right.children - left_method == right_method && matching_nodes?(left_receiver, right_receiver) + left_method == right_method && + matching_nodes?(left_receiver, right_receiver) && + left_args == right_args end def chain_length(method_chain, method) diff --git a/spec/rubocop/cop/style/safe_navigation_spec.rb b/spec/rubocop/cop/style/safe_navigation_spec.rb index a48d358f1e5b..9fa4398402c5 100644 --- a/spec/rubocop/cop/style/safe_navigation_spec.rb +++ b/spec/rubocop/cop/style/safe_navigation_spec.rb @@ -28,6 +28,10 @@ expect_no_offenses('foo && foo[:bar]') end + it 'allows hash key access and then a different hash key access' do + expect_no_offenses('return if foo[:bar] && foo[:baz].blank?') + end + it 'allows an object check before a negated predicate' do expect_no_offenses('foo && !foo.bar?') end From 1231fea7ad8b43f1970f124d90945cd1f3df737e Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Mon, 18 Aug 2025 12:04:17 +0200 Subject: [PATCH 20/37] [Fix #14445] Fix false positives for `Lint/UselessAssignment` with `for` loops when the variable is referenced in the collection In a for-style loop, the children don't appear in the order that they actually execute. --- changelog/fix_useless_assignment_for_loop.md | 1 + lib/rubocop/cop/variable_force.rb | 5 ++++ .../cop/lint/useless_assignment_spec.rb | 28 +++++++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 changelog/fix_useless_assignment_for_loop.md diff --git a/changelog/fix_useless_assignment_for_loop.md b/changelog/fix_useless_assignment_for_loop.md new file mode 100644 index 000000000000..ef45a979ff52 --- /dev/null +++ b/changelog/fix_useless_assignment_for_loop.md @@ -0,0 +1 @@ +* [#14445](https://github.com/rubocop/rubocop/issues/14445): Fix false positives for `Lint/UselessAssignment` with `for` loops when the variable is referenced in the collection. ([@earlopain][]) diff --git a/lib/rubocop/cop/variable_force.rb b/lib/rubocop/cop/variable_force.rb index 83d0c47b80f4..64b926873f0d 100644 --- a/lib/rubocop/cop/variable_force.rb +++ b/lib/rubocop/cop/variable_force.rb @@ -243,6 +243,11 @@ def process_loop(node) condition_node, body_node = *node process_node(body_node) process_node(condition_node) + elsif node.for_type? + # In `for item in items` the rightmost expression is evaluated first. + process_node(node.collection) + process_node(node.variable) + process_node(node.body) if node.body else process_children(node) end diff --git a/spec/rubocop/cop/lint/useless_assignment_spec.rb b/spec/rubocop/cop/lint/useless_assignment_spec.rb index 0c76bf02a543..6175b2e3aaa5 100644 --- a/spec/rubocop/cop/lint/useless_assignment_spec.rb +++ b/spec/rubocop/cop/lint/useless_assignment_spec.rb @@ -270,6 +270,34 @@ module x::Foo end end + context 'when a variable is assigned before `for`' do + it 'registers an offense when it is not referenced' do + expect_offense(<<~RUBY) + node = foo + ^^^^ Useless assignment to variable - `node`. + for node in bar + return node if baz? + end + RUBY + + expect_correction(<<~RUBY) + foo + for node in bar + return node if baz? + end + RUBY + end + + it 'registers no offense when the variable is referenced in the collection' do + expect_no_offenses(<<~RUBY) + node = foo + for node in node.children + return node if bar? + end + RUBY + end + end + context 'when a variable is assigned and unreferenced in `for` with multiple variables' do it 'registers an offense' do expect_offense(<<~RUBY) From 790cc6300fa3f5201aae82ae0a821821ab5b8480 Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Mon, 18 Aug 2025 12:53:27 +0200 Subject: [PATCH 21/37] Remove misspell from CI The repo was archived https://github.com/client9/misspell and we also use codespell. One spell checker seems more than enough. --- .github/workflows/spell_checking.yml | 10 ---------- CONTRIBUTING.md | 16 +--------------- 2 files changed, 1 insertion(+), 25 deletions(-) diff --git a/.github/workflows/spell_checking.yml b/.github/workflows/spell_checking.yml index 114c0d875b7a..8cafc6a653f1 100644 --- a/.github/workflows/spell_checking.yml +++ b/.github/workflows/spell_checking.yml @@ -16,13 +16,3 @@ jobs: steps: - uses: actions/checkout@v5 - uses: codespell-project/actions-codespell@v2 - - misspell: - name: Check spelling of all files in commit with misspell - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v5 - - name: Install - run: wget -O - -q https://raw.githubusercontent.com/client9/misspell/master/install-misspell.sh | sh -s -- -b . - - name: Misspell - run: git ls-files --empty-directory | xargs ./misspell -i 'enviromnent' -error diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c650390f94e2..af3fceb6a8f1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -49,21 +49,7 @@ $ rubocop -V ### Spell Checking -We are running [misspell](https://github.com/client9/misspell) which is mainly written in -[Golang](https://golang.org/) to check spelling with [GitHub Actions](https://github.com/rubocop/rubocop/blob/master/.github/workflows/spell_checking.yml). -Correct commonly misspelled English words quickly with `misspell`. `misspell` is different from most other spell checkers -because it doesn't use a custom dictionary. You can run `misspell` locally against all files with: - -```console -$ find . -type f | xargs ./misspell -i 'enviromnent' -error -``` - -Notable `misspell` help options or flags are: - -* `-i` string: ignore the following corrections, comma separated -* `-w`: Overwrite file with corrections (default is just to display) - -We also run [codespell](https://github.com/codespell-project/codespell) with GitHub Actions to check spelling and +We are running[codespell](https://github.com/codespell-project/codespell) with GitHub Actions to check spelling and [codespell](https://pypi.org/project/codespell/) runs against a [small custom dictionary](https://github.com/rubocop/rubocop/blob/master/.codespellrc). If you have `codespell` locally available in your `$PATH`, `bundle exec rake` will run it for you. From 90ae8675f3db195cbf9657e51accaa633299403a Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Mon, 18 Aug 2025 13:28:21 +0200 Subject: [PATCH 22/37] Fix wrong autocorrect for `Style/For` with save navigation in the collection In such a case calling `each` is not safe without also using safe navigation. `Lint/Lint/SafeNavigationChain` would pick this up so it's not a big deal but it's also not difficult to do the correct thing directly. --- .codespellrc | 1 + .../fix_autocorrect_style_for_save_navigation.md | 1 + lib/rubocop/cop/correctors/for_to_each_corrector.rb | 9 +++++++-- spec/rubocop/cop/style/for_spec.rb | 13 +++++++++++++ 4 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 changelog/fix_autocorrect_style_for_save_navigation.md diff --git a/.codespellrc b/.codespellrc index cf385a135cf1..be1d59487530 100644 --- a/.codespellrc +++ b/.codespellrc @@ -10,5 +10,6 @@ ignore-words-list = ba, irregardless, mange, ofo, + seach, thi, upto, diff --git a/changelog/fix_autocorrect_style_for_save_navigation.md b/changelog/fix_autocorrect_style_for_save_navigation.md new file mode 100644 index 000000000000..60fb6d282e30 --- /dev/null +++ b/changelog/fix_autocorrect_style_for_save_navigation.md @@ -0,0 +1 @@ +* [#14459](https://github.com/rubocop/rubocop/pull/14459): Fix wrong autocorrect for `Style/For` with save navigation in the collection. ([@earlopain][]) diff --git a/lib/rubocop/cop/correctors/for_to_each_corrector.rb b/lib/rubocop/cop/correctors/for_to_each_corrector.rb index cd33923fc0ae..4e44f438b23f 100644 --- a/lib/rubocop/cop/correctors/for_to_each_corrector.rb +++ b/lib/rubocop/cop/correctors/for_to_each_corrector.rb @@ -6,7 +6,7 @@ module Cop class ForToEachCorrector extend NodePattern::Macros - CORRECTION = '%s.each do |%s|' + CORRECTION = '%s%seach do |%s|' def initialize(for_node) @for_node = for_node @@ -25,7 +25,12 @@ def call(corrector) attr_reader :for_node, :variable_node, :collection_node def correction - format(CORRECTION, collection: collection_source, argument: variable_node.source) + format( + CORRECTION, + collection: collection_source, + dot: collection_node.csend_type? ? '&.' : '.', + argument: variable_node.source + ) end def collection_source diff --git a/spec/rubocop/cop/style/for_spec.rb b/spec/rubocop/cop/style/for_spec.rb index 70fe955de883..41012de3afc3 100644 --- a/spec/rubocop/cop/style/for_spec.rb +++ b/spec/rubocop/cop/style/for_spec.rb @@ -370,6 +370,19 @@ def func end RUBY end + + it 'corrects to `each` with safe navigation if collection ends with safe navigation' do + expect_offense(<<~RUBY) + for item in foo&.items + ^^^^^^^^^^^^^^^^^^^^^^ Prefer `each` over `for`. + end + RUBY + + expect_correction(<<~RUBY) + foo&.items&.each do |item| + end + RUBY + end end it 'accepts multiline each' do From 6a65be61b2156058ef996459b93eb265297392cc Mon Sep 17 00:00:00 2001 From: Lovro Bikic Date: Mon, 18 Aug 2025 16:19:40 +0200 Subject: [PATCH 23/37] Clarify operator alignment for next-line assignments --- lib/rubocop/cop/layout/multiline_operation_indentation.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/rubocop/cop/layout/multiline_operation_indentation.rb b/lib/rubocop/cop/layout/multiline_operation_indentation.rb index b36d97507dc7..30a4871a4554 100644 --- a/lib/rubocop/cop/layout/multiline_operation_indentation.rb +++ b/lib/rubocop/cop/layout/multiline_operation_indentation.rb @@ -10,6 +10,8 @@ module Layout # condition, an explicit `return` statement, etc. In other contexts, the second operand should # be indented regardless of enforced style. # + # In both styles, operators should be aligned when an assignment begins on the next line. + # # @example EnforcedStyle: aligned (default) # # bad # if a + From 6f30cf56452e43d7312ea25d1efed0f3d4f7b59a Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Mon, 18 Aug 2025 18:52:16 +0200 Subject: [PATCH 24/37] Add a test for `Lint/UselessAssignment` I was looking at `VariableForce` and saw `scanned_nodes` which I though could be removed: https://github.com/Earlopain/rubocop/blob/cb83c6f61158e8d13b3bb0f8cdd49b88617fce99/lib/rubocop/cop/variable_force.rb#L373 No tests fail after but I found missing offenses after making the change. Add some test coverage for that. --- spec/rubocop/cop/lint/useless_assignment_spec.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/spec/rubocop/cop/lint/useless_assignment_spec.rb b/spec/rubocop/cop/lint/useless_assignment_spec.rb index 0c76bf02a543..ba90e77c9b0e 100644 --- a/spec/rubocop/cop/lint/useless_assignment_spec.rb +++ b/spec/rubocop/cop/lint/useless_assignment_spec.rb @@ -1187,6 +1187,19 @@ def some_method end RUBY end + + it 'registers an offense when the reassignment is the last statement' do + expect_offense(<<~RUBY) + foo = [1, 2] + foo = foo.map { |i| i + 1 } + ^^^ Useless assignment to variable - `foo`. + RUBY + + expect_correction(<<~RUBY) + foo = [1, 2] + foo.map { |i| i + 1 } + RUBY + end end context 'when a variable is reassigned with binary operator assignment and referenced' do From c6618480c850fec505786559e8c61eb6c0214ae0 Mon Sep 17 00:00:00 2001 From: Issy Long Date: Sun, 17 Aug 2025 23:26:10 +0100 Subject: [PATCH 25/37] Follow module inclusion with nonzero args with an empty line - The fix for `Layout/EmptyLinesAfterModuleInclusion` cop in 49c140b387fa801662f7bcb7e7723417d3454dbb produced false negatives[1] when `include` and friends were, validly, called with multiple arguments. [1]: https://github.com/rubocop/rubocop/pull/14444#discussion_r2279911551 --- ...ith_nonzero_args_with_an_20250817233441.md | 1 + .../empty_lines_after_module_inclusion.rb | 2 +- ...empty_lines_after_module_inclusion_spec.rb | 22 ++++++++++++++++++- 3 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 changelog/fix_follow_module_inclusion_with_nonzero_args_with_an_20250817233441.md diff --git a/changelog/fix_follow_module_inclusion_with_nonzero_args_with_an_20250817233441.md b/changelog/fix_follow_module_inclusion_with_nonzero_args_with_an_20250817233441.md new file mode 100644 index 000000000000..4d0fcd3c59e8 --- /dev/null +++ b/changelog/fix_follow_module_inclusion_with_nonzero_args_with_an_20250817233441.md @@ -0,0 +1 @@ +* [#14455](https://github.com/rubocop/rubocop/pull/14455): Follow module inclusion with nonzero args with an empty line. ([@issyl0][]) diff --git a/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb b/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb index 16f184c2b09d..eb232c523a97 100644 --- a/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +++ b/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb @@ -38,7 +38,7 @@ class EmptyLinesAfterModuleInclusion < Base RESTRICT_ON_SEND = MODULE_INCLUSION_METHODS def on_send(node) - return if node.receiver || !node.arguments.one? + return if node.receiver || node.arguments.empty? return if node.parent&.type?(:send, :any_block) return if next_line_empty_or_enable_directive_comment?(node.last_line) diff --git a/spec/rubocop/cop/layout/empty_lines_after_module_inclusion_spec.rb b/spec/rubocop/cop/layout/empty_lines_after_module_inclusion_spec.rb index aa7ec58a8248..4d857d2fb455 100644 --- a/spec/rubocop/cop/layout/empty_lines_after_module_inclusion_spec.rb +++ b/spec/rubocop/cop/layout/empty_lines_after_module_inclusion_spec.rb @@ -94,6 +94,26 @@ def do_something RUBY end + it "registers an offense and corrects when #{method} has multiple arguments" do + expect_offense(<<~RUBY, method: method) + class Foo + #{method} Bar, Baz + ^{method}^^^^^^^^^ Add an empty line after module inclusion. + def do_something + end + end + RUBY + + expect_correction(<<~RUBY) + class Foo + #{method} Bar, Baz + + def do_something + end + end + RUBY + end + it "registers an offense and corrects for code that immediately follows #{method} inside a class" do expect_offense(<<~RUBY, method: method) class Bar @@ -242,7 +262,7 @@ module Bar RUBY end - it 'does not register an offense when `include` does not have exactly one argument' do + it 'does not register an offense when `include` has zero arguments' do expect_no_offenses(<<~RUBY) class Foo includes = [include, sdk_include].compact.map do |inc| From 309f13f288033255745065a2ffaec4408604d9ca Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Tue, 19 Aug 2025 11:39:09 +0200 Subject: [PATCH 26/37] Fix two tests printing to stderr Because they use backtick-style command execution, their output doesn't get captured like usually. To fix this, I've moved `--display-time` handling into the CLI class, slightly tweaking some existing specs to be less specific about the output --- exe/rubocop | 9 +-------- lib/rubocop/cli.rb | 5 +++++ spec/rubocop/cli/options_spec.rb | 18 ++++++++++-------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/exe/rubocop b/exe/rubocop index f9bf90424c25..14efcc57f63c 100755 --- a/exe/rubocop +++ b/exe/rubocop @@ -12,13 +12,6 @@ if RuboCop::Server.running? exit_status = RuboCop::Server::ClientCommand::Exec.new.run else require 'rubocop' - - cli = RuboCop::CLI.new - - time_start = Process.clock_gettime(Process::CLOCK_MONOTONIC) - exit_status = cli.run - elapsed_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) - time_start - - puts "Finished in #{elapsed_time} seconds" if cli.options[:debug] || cli.options[:display_time] + exit_status = RuboCop::CLI.new.run end exit exit_status diff --git a/lib/rubocop/cli.rb b/lib/rubocop/cli.rb index f4a34c1928da..0ac351d602d3 100644 --- a/lib/rubocop/cli.rb +++ b/lib/rubocop/cli.rb @@ -37,6 +37,8 @@ def initialize # # rubocop:disable Metrics/MethodLength, Metrics/AbcSize def run(args = ARGV) + time_start = Process.clock_gettime(Process::CLOCK_MONOTONIC) + @options, paths = Options.new.parse(args) @env = Environment.new(@options, @config_store, paths) @@ -72,6 +74,9 @@ def run(args = ARGV) warn e.message warn e.backtrace STATUS_ERROR + ensure + elapsed_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) - time_start + puts "Finished in #{elapsed_time} seconds" if @options[:debug] || @options[:display_time] end # rubocop:enable Metrics/MethodLength, Metrics/AbcSize diff --git a/spec/rubocop/cli/options_spec.rb b/spec/rubocop/cli/options_spec.rb index 7dc4a21dd8aa..c56b155e133e 100644 --- a/spec/rubocop/cli/options_spec.rb +++ b/spec/rubocop/cli/options_spec.rb @@ -1091,9 +1091,9 @@ def on_send(node) create_file('example1.rb', "\tputs 0") expect(cli.run(['--debug', 'example1.rb'])).to eq(1) home = File.dirname(File.dirname(File.dirname(File.dirname(__FILE__)))) - expect($stdout.string.lines.grep(/configuration/).map(&:chomp)) - .to eq(["For #{abs('')}: " \ - "Default configuration from #{home}/config/default.yml"]) + expect($stdout.string) + .to include("For #{abs('')}: " \ + "Default configuration from #{home}/config/default.yml") end it 'shows cop names' do @@ -1101,9 +1101,9 @@ def on_send(node) file = abs('example1.rb') expect(cli.run(['--format', 'emacs', '--debug', 'example1.rb'])).to eq(1) - expect($stdout.string.lines.to_a[-1]) - .to eq("#{file}:1:7: C: [Correctable] Layout/TrailingWhitespace: Trailing " \ - "whitespace detected.\n") + expect($stdout.string) + .to include("#{file}:1:7: C: [Correctable] Layout/TrailingWhitespace: Trailing " \ + "whitespace detected.\n") end end @@ -1114,13 +1114,15 @@ def on_send(node) context 'without --display-time' do it 'does not display elapsed time in seconds' do - expect(`rubocop example1.rb`).not_to match(regex) + expect(cli.run(['example1.rb'])).to eq(0) + expect($stdout.string).not_to match(regex) end end context 'with --display-time' do it 'displays elapsed time in seconds' do - expect(`rubocop --display-time example1.rb`).to match(regex) + expect(cli.run(['--display-time', 'example1.rb'])).to eq(0) + expect($stdout.string).to match(regex) end end end From f7384fdb4d39e56b3796461d3a362a6fd839678b Mon Sep 17 00:00:00 2001 From: TOMITA Masahiro Date: Mon, 18 Aug 2025 01:30:24 +0900 Subject: [PATCH 27/37] Fixed didChange position calculation it was using UTF-8, but only positionEncoding UTF-16 is supported. --- lib/rubocop/lsp/routes.rb | 35 ++++++++++++------- spec/rubocop/lsp/server_spec.rb | 61 ++++++++++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 14 deletions(-) diff --git a/lib/rubocop/lsp/routes.rb b/lib/rubocop/lsp/routes.rb index 6f9797a9cb6b..8916a0db38f8 100644 --- a/lib/rubocop/lsp/routes.rb +++ b/lib/rubocop/lsp/routes.rb @@ -76,10 +76,13 @@ def for(name) handle 'textDocument/didChange' do |request| params = request[:params] + file_uri = params[:textDocument][:uri] + text = @text_cache[file_uri] params[:contentChanges].each do |content| - result = diagnostic(params[:textDocument][:uri], content[:text], content[:range]) - @server.write(result) + text = change_text(text, content[:text], content[:range]) end + result = diagnostic(file_uri, text) + @server.write(result) end handle 'textDocument/didOpen' do |request| @@ -209,31 +212,37 @@ def format_file(file_uri, command: nil) ] end - def diagnostic(file_uri, text, range = nil) - if range - start_pos = text_pos(@text_cache[file_uri], range[:start]) - end_pos = text_pos(@text_cache[file_uri], range[:end]) - @text_cache[file_uri].bytesplice(start_pos...end_pos, text) - else - @text_cache[file_uri] = text - end + def diagnostic(file_uri, text) + @text_cache[file_uri] = text { method: 'textDocument/publishDiagnostics', params: { uri: file_uri, - diagnostics: @server.offenses(convert_file_uri_to_path(file_uri), @text_cache[file_uri]) + diagnostics: @server.offenses(convert_file_uri_to_path(file_uri), text) } } end + def change_text(orig_text, text, range) + return text unless range + + start_pos = text_pos(orig_text, range[:start]) + end_pos = text_pos(orig_text, range[:end]) + text_bin = orig_text.b + text_bin[start_pos...end_pos] = text.b + text_bin.force_encoding(orig_text.encoding) + end + def text_pos(text, range) line = range[:line] char = range[:character] pos = 0 text.each_line.with_index do |l, i| - return pos + char if i == line - + if i == line + pos += l.encode('utf-16be').b[0, char * 2].encode('utf-8', 'utf-16be').bytesize + return pos + end pos += l.bytesize end pos diff --git a/spec/rubocop/lsp/server_spec.rb b/spec/rubocop/lsp/server_spec.rb index 417c0085564f..9ccff385302f 100644 --- a/spec/rubocop/lsp/server_spec.rb +++ b/spec/rubocop/lsp/server_spec.rb @@ -1356,7 +1356,7 @@ end it 'handles requests' do - expect(messages.count).to eq(4) + expect(messages.count).to eq(3) expect(messages.last).to eq( jsonrpc: '2.0', id: 20, result: [ { @@ -1413,6 +1413,65 @@ end end + describe 'did change with multibyte character' do + let(:requests) do + [ + { + jsonrpc: '2.0', + method: 'textDocument/didOpen', + params: { + textDocument: { + languageId: 'ruby', + text: "puts '🍣🍺'", + uri: 'file:///path/to/file.rb', + version: 0 + } + } + }, { + jsonrpc: '2.0', + method: 'textDocument/didChange', + params: { + contentChanges: [ + { + text: '💎', + range: { + start: { line: 0, character: 6 }, + end: { line: 0, character: 10 } + } + } + ], + textDocument: { + uri: 'file:///path/to/file.rb', + version: 10 + } + } + }, { + jsonrpc: '2.0', + id: 20, + method: 'textDocument/formatting', + params: { + options: { insertSpaces: true, tabSize: 2 }, + textDocument: { uri: 'file:///path/to/file.rb' } + } + } + ] + end + + it 'handles requests' do + expect(messages.count).to eq(3) + expect(messages.last).to eq( + jsonrpc: '2.0', id: 20, result: [ + { + newText: "puts '💎'\n", + range: { + end: { character: 0, line: 1 }, start: { character: 0, line: 0 } + } + } + ] + ) + end + end + context 'when an internal error occurs' do before do allow_any_instance_of(RuboCop::LSP::Routes).to receive(:for).with('initialize').and_raise # rubocop:disable RSpec/AnyInstance From a08b607aa376e679562c577fbb43f2690eea4d89 Mon Sep 17 00:00:00 2001 From: Daniel Vandersluis Date: Mon, 18 Aug 2025 13:52:43 -0400 Subject: [PATCH 28/37] [Fix #14453] Update `Style/RedundantBegin` to register `begin` blocks inside `if`, `unless`, `case`, `while` and `until` as redundant --- ..._begin_to_register_begin_20250818135329.md | 1 + lib/rubocop/cop/style/redundant_begin.rb | 31 ++ .../rubocop/cop/style/redundant_begin_spec.rb | 290 ++++++++++++++++++ 3 files changed, 322 insertions(+) create mode 100644 changelog/change_update_style_redundant_begin_to_register_begin_20250818135329.md diff --git a/changelog/change_update_style_redundant_begin_to_register_begin_20250818135329.md b/changelog/change_update_style_redundant_begin_to_register_begin_20250818135329.md new file mode 100644 index 000000000000..8ddfb8777000 --- /dev/null +++ b/changelog/change_update_style_redundant_begin_to_register_begin_20250818135329.md @@ -0,0 +1 @@ +* [#14453](https://github.com/rubocop/rubocop/issues/14453): Update `Style/RedundantBegin` to register `begin` blocks inside `if`, `unless`, `case`, `while` and `until` as redundant. ([@dvandersluis][]) diff --git a/lib/rubocop/cop/style/redundant_begin.rb b/lib/rubocop/cop/style/redundant_begin.rb index b921ff653f76..16476dfdc641 100644 --- a/lib/rubocop/cop/style/redundant_begin.rb +++ b/lib/rubocop/cop/style/redundant_begin.rb @@ -85,6 +85,28 @@ def on_def(node) end alias on_defs on_def + def on_if(node) + return if node.modifier_form? + + inspect_branches(node) + end + + def on_case(node) + inspect_branches(node) + end + + def on_while(node) + return if node.modifier_form? + + body = node.body + + return unless body&.kwbegin_type? + return if body.rescue_node || body.ensure_node + + register_offense(body) + end + alias on_until on_while + def on_block(node) return if target_ruby_version < 2.5 return if node.send_node.lambda_literal? @@ -199,6 +221,15 @@ def valid_context_using_only_begin?(node) def valid_begin_assignment?(node) node.parent&.assignment? && !node.children.one? end + + def inspect_branches(node) + node.branches.each do |branch| + next unless branch&.kwbegin_type? + next if branch.rescue_node || branch.ensure_node + + register_offense(branch) + end + end end end end diff --git a/spec/rubocop/cop/style/redundant_begin_spec.rb b/spec/rubocop/cop/style/redundant_begin_spec.rb index 9da8dbb81edc..ea63190f4bd0 100644 --- a/spec/rubocop/cop/style/redundant_begin_spec.rb +++ b/spec/rubocop/cop/style/redundant_begin_spec.rb @@ -265,6 +265,296 @@ def method RUBY end + it 'registers and corrects an offense when a multiline `begin` block is inside `if`' do + expect_offense(<<~RUBY) + if condition + begin + ^^^^^ Redundant `begin` block detected. + foo + bar + end + end + RUBY + + expect_correction(<<~RUBY) + if condition + #{trailing_whitespace} + foo + bar + #{trailing_whitespace} + end + RUBY + end + + it 'registers and corrects an offense when a multiline `begin` block is inside `unless`' do + expect_offense(<<~RUBY) + unless condition + begin + ^^^^^ Redundant `begin` block detected. + foo + bar + end + end + RUBY + + expect_correction(<<~RUBY) + unless condition + #{trailing_whitespace} + foo + bar + #{trailing_whitespace} + end + RUBY + end + + it 'registers and corrects an offense when a multiline `begin` block is inside `elsif`' do + expect_offense(<<~RUBY) + if condition + foo + elsif condition2 + begin + ^^^^^ Redundant `begin` block detected. + bar + baz + end + end + RUBY + + expect_correction(<<~RUBY) + if condition + foo + elsif condition2 + #{trailing_whitespace} + bar + baz + #{trailing_whitespace} + end + RUBY + end + + it 'registers and corrects an offense when a multiline `begin` block is inside `else`' do + expect_offense(<<~RUBY) + if condition + foo + else + begin + ^^^^^ Redundant `begin` block detected. + bar + baz + end + end + RUBY + + expect_correction(<<~RUBY) + if condition + foo + else + #{trailing_whitespace} + bar + baz + #{trailing_whitespace} + end + RUBY + end + + it 'registers and corrects an offense when a multiline `begin` block is inside `case-when`' do + expect_offense(<<~RUBY) + case condition + when foo + begin + ^^^^^ Redundant `begin` block detected. + bar + baz + end + end + RUBY + + expect_correction(<<~RUBY) + case condition + when foo + #{trailing_whitespace} + bar + baz + #{trailing_whitespace} + end + RUBY + end + + it 'registers and corrects an offense when a multiline `begin` block is inside `case-else`' do + expect_offense(<<~RUBY) + case condition + when foo + bar + else + begin + ^^^^^ Redundant `begin` block detected. + baz + quux + end + end + RUBY + + expect_correction(<<~RUBY) + case condition + when foo + bar + else + #{trailing_whitespace} + baz + quux + #{trailing_whitespace} + end + RUBY + end + + it 'registers and corrects an offense when a multiline `begin` block is inside `while`' do + expect_offense(<<~RUBY) + while condition + begin + ^^^^^ Redundant `begin` block detected. + foo + bar + end + end + RUBY + + expect_correction(<<~RUBY) + while condition + #{trailing_whitespace} + foo + bar + #{trailing_whitespace} + end + RUBY + end + + it 'registers and corrects an offense when a multiline `begin` block is inside `until`' do + expect_offense(<<~RUBY) + until condition + begin + ^^^^^ Redundant `begin` block detected. + foo + bar + end + end + RUBY + + expect_correction(<<~RUBY) + until condition + #{trailing_whitespace} + foo + bar + #{trailing_whitespace} + end + RUBY + end + + it 'does not register an offense when using `begin` with `rescue` inside an `if` statement' do + expect_no_offenses(<<~RUBY) + if condition + begin + foo + bar + rescue StandardError + baz + end + end + RUBY + end + + it 'does not register an offense when using `begin` with `ensure` inside an `if` statement' do + expect_no_offenses(<<~RUBY) + if condition + begin + foo + bar + ensure + baz + end + end + RUBY + end + + it 'does not register an offense when using `begin` with `rescue` inside an `case` statement' do + expect_no_offenses(<<~RUBY) + case condition + when foo + begin + bar + baz + rescue StandardError + quux + end + end + RUBY + end + + it 'does not register an offense when using `begin` with `ensure` inside an `case` statement' do + expect_no_offenses(<<~RUBY) + case condition + when foo + begin + bar + baz + ensure + quux + end + end + RUBY + end + + it 'does not register an offense when using `begin` with `rescue` inside an `while` statement' do + expect_no_offenses(<<~RUBY) + while condition + begin + foo + bar + rescue StandardError + baz + end + end + RUBY + end + + it 'does not register an offense when using `begin` with `ensure` inside an `while` statement' do + expect_no_offenses(<<~RUBY) + while condition + begin + foo + bar + ensure + baz + end + end + RUBY + end + + it 'does not register an offense when using `begin` with `rescue` inside an `until` statement' do + expect_no_offenses(<<~RUBY) + until condition + begin + foo + bar + rescue StandardError + baz + end + end + RUBY + end + + it 'does not register an offense when using `begin` with `ensure` inside an `until` statement' do + expect_no_offenses(<<~RUBY) + until condition + begin + foo + bar + ensure + baz + end + end + RUBY + end + it 'does not register an offense when using `begin` with multiple statement for or assignment' do expect_no_offenses(<<~RUBY) var ||= begin From 61483991641f6079d08beefa4057f91111e11cf2 Mon Sep 17 00:00:00 2001 From: r7kamura Date: Tue, 19 Aug 2025 08:48:30 +0900 Subject: [PATCH 29/37] Exclude `AutoCorrect` and `Include` from configuration parameters --- ...correct_and_include_from_20250819090646.md | 1 + .../formatter/disabled_config_formatter.rb | 23 +++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 changelog/change_exclude_auto_correct_and_include_from_20250819090646.md diff --git a/changelog/change_exclude_auto_correct_and_include_from_20250819090646.md b/changelog/change_exclude_auto_correct_and_include_from_20250819090646.md new file mode 100644 index 000000000000..c9752d13faf7 --- /dev/null +++ b/changelog/change_exclude_auto_correct_and_include_from_20250819090646.md @@ -0,0 +1 @@ +* [#14464](https://github.com/rubocop/rubocop/pull/14464): Exclude `AutoCorrect` and `Include` from configuration parameters. ([@r7kamura][]) diff --git a/lib/rubocop/formatter/disabled_config_formatter.rb b/lib/rubocop/formatter/disabled_config_formatter.rb index 6b74eb45138e..b105a1dc97fb 100644 --- a/lib/rubocop/formatter/disabled_config_formatter.rb +++ b/lib/rubocop/formatter/disabled_config_formatter.rb @@ -4,7 +4,7 @@ module RuboCop module Formatter # This formatter displays a YAML configuration file where all cops that # detected any offenses are configured to not detect the offense. - class DisabledConfigFormatter < BaseFormatter + class DisabledConfigFormatter < BaseFormatter # rubocop:disable Metrics/ClassLength include PathUtil HEADING = <<~COMMENTS @@ -17,6 +17,22 @@ class DisabledConfigFormatter < BaseFormatter # versions of RuboCop, may require this file to be generated again. COMMENTS + EXCLUDED_CONFIG_KEYS = %w[ + AutoCorrect + Description + Enabled + Exclude + Include + Reference + References + Safe + SafeAutoCorrect + StyleGuide + VersionAdded + VersionChanged + VersionRemoved + ].freeze + @config_to_allow_offenses = {} @detected_styles = {} @@ -163,10 +179,7 @@ def supports_unsafe_autocorrect?(cop_class, default_cfg) end def cop_config_params(default_cfg, cfg) - default_cfg.keys - - %w[Description StyleGuide Reference References Enabled Exclude Safe - SafeAutoCorrect VersionAdded VersionChanged VersionRemoved] - - cfg.keys + default_cfg.keys - EXCLUDED_CONFIG_KEYS - cfg.keys end def output_cop_param_comments(output_buffer, params, default_cfg) From 0bd61aae17e2c380d1950b6496e9e19825dc00b1 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Wed, 20 Aug 2025 14:10:47 +0900 Subject: [PATCH 30/37] Use `RuboCop::AST::Node#basic_conditional?` --- lib/rubocop/cop/variable_force/variable.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rubocop/cop/variable_force/variable.rb b/lib/rubocop/cop/variable_force/variable.rb index 95bc36aa91eb..cc2d38194dcc 100644 --- a/lib/rubocop/cop/variable_force/variable.rb +++ b/lib/rubocop/cop/variable_force/variable.rb @@ -79,7 +79,7 @@ def in_modifier_conditional?(assignment) parent = parent.parent if parent&.begin_type? return false if parent.nil? - parent.type?(:if, :while, :until) && parent.modifier_form? + parent.basic_conditional? && parent.modifier_form? end def capture_with_block! From 7cb7438b1a1c420f314c1b4ed35fb007c75870d5 Mon Sep 17 00:00:00 2001 From: Lovro Bikic Date: Wed, 20 Aug 2025 07:45:18 +0200 Subject: [PATCH 31/37] Register array intersection size checks as offenses under Style/ArrayIntersect --- ...ay_intersect_array_size_false_negatives.md | 1 + lib/rubocop/cop/style/array_intersect.rb | 56 +++++-- .../rubocop/cop/style/array_intersect_spec.rb | 149 ++++++++++++++++++ 3 files changed, 195 insertions(+), 11 deletions(-) create mode 100644 changelog/change_style_array_intersect_array_size_false_negatives.md diff --git a/changelog/change_style_array_intersect_array_size_false_negatives.md b/changelog/change_style_array_intersect_array_size_false_negatives.md new file mode 100644 index 000000000000..edbbadd48919 --- /dev/null +++ b/changelog/change_style_array_intersect_array_size_false_negatives.md @@ -0,0 +1 @@ +* [#14448](https://github.com/rubocop/rubocop/pull/14448): Register array intersection size checks as offenses under `Style/ArrayIntersect`. ([@lovro-bikic][]) diff --git a/lib/rubocop/cop/style/array_intersect.rb b/lib/rubocop/cop/style/array_intersect.rb index 80c5379daddb..c59d89e62b7e 100644 --- a/lib/rubocop/cop/style/array_intersect.rb +++ b/lib/rubocop/cop/style/array_intersect.rb @@ -10,6 +10,8 @@ module Style # * `(array1 & array2).any?` # * `(array1.intersection(array2)).any?` # * `array1.any? { |elem| array2.member?(elem) }` + # * `(array1 & array2).count > 0` + # * `(array1 & array2).size > 0` # # can be replaced with `array1.intersect?(array2)`. # @@ -51,6 +53,19 @@ module Style # array1.intersect?(array2) # !array1.intersect?(array2) # + # # bad + # (array1 & array2).count > 0 + # (array1 & array2).count.positive? + # (array1 & array2).count != 0 + # + # (array1 & array2).count == 0 + # (array1 & array2).count.zero? + # + # # good + # array1.intersect?(array2) + # + # !array1.intersect?(array2) + # # @example AllCops:ActiveSupportExtensionsEnabled: false (default) # # good # (array1 & array2).present? @@ -73,9 +88,11 @@ class ArrayIntersect < Base PREDICATES = %i[any? empty? none?].to_set.freeze ACTIVE_SUPPORT_PREDICATES = (PREDICATES + %i[present? blank?]).freeze + ARRAY_SIZE_METHODS = %i[count length size].to_set.freeze + # @!method bad_intersection_check?(node, predicates) def_node_matcher :bad_intersection_check?, <<~PATTERN - (call + $(call { (begin (send $_ :& $_)) (call $_ :intersection $_) @@ -84,6 +101,20 @@ class ArrayIntersect < Base ) PATTERN + # @!method intersection_size_check?(node, predicates) + def_node_matcher :intersection_size_check?, <<~PATTERN + (call + $(call + { + (begin (send $_ :& $_)) + (call $_ :intersection $_) + } + %ARRAY_SIZE_METHODS + ) + {$:> (int 0) | $:positive? | $:!= (int 0) | $:== (int 0) | $:zero?} + ) + PATTERN + # @!method any_none_block_intersection(node) def_node_matcher :any_none_block_intersection, <<~PATTERN { @@ -104,15 +135,15 @@ class ArrayIntersect < Base PATTERN MSG = 'Use `%s` instead of `%s`.' - STRAIGHT_METHODS = %i[present? any?].freeze - NEGATED_METHODS = %i[blank? empty? none?].freeze + STRAIGHT_METHODS = %i[present? any? > positive? !=].freeze + NEGATED_METHODS = %i[blank? empty? none? == zero?].freeze RESTRICT_ON_SEND = (STRAIGHT_METHODS + NEGATED_METHODS).freeze def on_send(node) return if node.block_literal? - return unless (receiver, argument, method_name = bad_intersection?(node)) + return unless (dot_node, receiver, argument, method_name = bad_intersection?(node)) - dot = node.loc.dot.source + dot = dot_node.loc.dot.source bang = straight?(method_name) ? '' : '!' replacement = "#{bang}#{receiver.source}#{dot}intersect?(#{argument.source})" @@ -135,13 +166,16 @@ def on_block(node) private def bad_intersection?(node) - predicates = if active_support_extensions_enabled? - ACTIVE_SUPPORT_PREDICATES - else - PREDICATES - end + bad_intersection_check?(node, bad_intersection_predicates) || + intersection_size_check?(node) + end - bad_intersection_check?(node, predicates) + def bad_intersection_predicates + if active_support_extensions_enabled? + ACTIVE_SUPPORT_PREDICATES + else + PREDICATES + end end def straight?(method_name) diff --git a/spec/rubocop/cop/style/array_intersect_spec.rb b/spec/rubocop/cop/style/array_intersect_spec.rb index 24d7d93de3e3..9a25fa0d6733 100644 --- a/spec/rubocop/cop/style/array_intersect_spec.rb +++ b/spec/rubocop/cop/style/array_intersect_spec.rb @@ -85,6 +85,75 @@ ([1, 2, 3] & [4, 5, 6]).blank? RUBY end + + described_class::ARRAY_SIZE_METHODS.each do |method| + it "registers an offense when using `.#{method} > 0`" do + expect_offense(<<~RUBY, method: method) + (a & b).#{method} > 0 + ^^^^^^^^^{method}^^^^ Use `a.intersect?(b)` instead of `(a & b).#{method} > 0`. + RUBY + + expect_correction(<<~RUBY) + a.intersect?(b) + RUBY + end + + it "registers an offense when using `.#{method} == 0`" do + expect_offense(<<~RUBY, method: method) + (a & b).#{method} == 0 + ^^^^^^^^^{method}^^^^^ Use `!a.intersect?(b)` instead of `(a & b).#{method} == 0`. + RUBY + + expect_correction(<<~RUBY) + !a.intersect?(b) + RUBY + end + + it "registers an offense when using `.#{method} != 0`" do + expect_offense(<<~RUBY, method: method) + (a & b).#{method} != 0 + ^^^^^^^^^{method}^^^^^ Use `a.intersect?(b)` instead of `(a & b).#{method} != 0`. + RUBY + + expect_correction(<<~RUBY) + a.intersect?(b) + RUBY + end + + it "registers an offense when using `.#{method}.zero?`" do + expect_offense(<<~RUBY, method: method) + (a & b).#{method}.zero? + ^^^^^^^^^{method}^^^^^^ Use `!a.intersect?(b)` instead of `(a & b).#{method}.zero?`. + RUBY + + expect_correction(<<~RUBY) + !a.intersect?(b) + RUBY + end + + it "registers an offense when using `.#{method}.positive?`" do + expect_offense(<<~RUBY, method: method) + (a & b).#{method}.positive? + ^^^^^^^^^{method}^^^^^^^^^^ Use `a.intersect?(b)` instead of `(a & b).#{method}.positive?`. + RUBY + + expect_correction(<<~RUBY) + a.intersect?(b) + RUBY + end + + it "does not register an offense when using `.#{method} > 1`" do + expect_no_offenses(<<~RUBY) + (a & b).#{method} > 1 + RUBY + end + + it "does not register an offense when using `.#{method} == 1`" do + expect_no_offenses(<<~RUBY) + (a & b).#{method} == 1 + RUBY + end + end end context 'with Array#intersection' do @@ -143,6 +212,86 @@ array1.intersection(array2, array3).any? RUBY end + + described_class::ARRAY_SIZE_METHODS.each do |method| + it "registers an offense when using `.#{method} > 0`" do + expect_offense(<<~RUBY, method: method) + a.intersection(b).#{method} > 0 + ^^^^^^^^^^^^^^^^^^^{method}^^^^ Use `a.intersect?(b)` instead of `a.intersection(b).#{method} > 0`. + RUBY + + expect_correction(<<~RUBY) + a.intersect?(b) + RUBY + end + + it "registers an offense when using `.#{method} == 0`" do + expect_offense(<<~RUBY, method: method) + a.intersection(b).#{method} == 0 + ^^^^^^^^^^^^^^^^^^^{method}^^^^^ Use `!a.intersect?(b)` instead of `a.intersection(b).#{method} == 0`. + RUBY + + expect_correction(<<~RUBY) + !a.intersect?(b) + RUBY + end + + it "registers an offense when using `.#{method} != 0`" do + expect_offense(<<~RUBY, method: method) + a.intersection(b).#{method} != 0 + ^^^^^^^^^^^^^^^^^^^{method}^^^^^ Use `a.intersect?(b)` instead of `a.intersection(b).#{method} != 0`. + RUBY + + expect_correction(<<~RUBY) + a.intersect?(b) + RUBY + end + + it "registers an offense when using `.#{method}.zero?`" do + expect_offense(<<~RUBY, method: method) + a.intersection(b).#{method}.zero? + ^^^^^^^^^^^^^^^^^^^{method}^^^^^^ Use `!a.intersect?(b)` instead of `a.intersection(b).#{method}.zero?`. + RUBY + + expect_correction(<<~RUBY) + !a.intersect?(b) + RUBY + end + + it "registers an offense when using `.#{method}.positive?`" do + expect_offense(<<~RUBY, method: method) + a.intersection(b).#{method}.positive? + ^^^^^^^^^^^^^^^^^^^{method}^^^^^^^^^^ Use `a.intersect?(b)` instead of `a.intersection(b).#{method}.positive?`. + RUBY + + expect_correction(<<~RUBY) + a.intersect?(b) + RUBY + end + + it "registers an offense when using `&.#{method}&.positive?`" do + expect_offense(<<~RUBY, method: method) + a&.intersection(b)&.#{method}&.positive? + ^^^^^^^^^^^^^^^^^^^^^{method}^^^^^^^^^^^ Use `a&.intersect?(b)` instead of `a&.intersection(b)&.#{method}&.positive?`. + RUBY + + expect_correction(<<~RUBY) + a&.intersect?(b) + RUBY + end + + it "does not register an offense when using `.#{method} > 1`" do + expect_no_offenses(<<~RUBY) + a.intersection(b).#{method} > 1 + RUBY + end + + it "does not register an offense when using `.#{method} == 1`" do + expect_no_offenses(<<~RUBY) + a.intersection(b).#{method} == 1 + RUBY + end + end end context 'with Array#any?' do From ea47b4afa143f6b35cdb7023b6a9e278d448ee2a Mon Sep 17 00:00:00 2001 From: Lovro Bikic Date: Wed, 20 Aug 2025 16:27:26 +0200 Subject: [PATCH 32/37] Optimize hidden files logic in TargetFinder --- lib/rubocop/target_finder.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/rubocop/target_finder.rb b/lib/rubocop/target_finder.rb index deb7e2aee783..690017f2d998 100644 --- a/lib/rubocop/target_finder.rb +++ b/lib/rubocop/target_finder.rb @@ -42,14 +42,12 @@ def target_files_in_dir(base_dir = Dir.pwd) # Support Windows: Backslashes from command-line -> forward slashes base_dir = base_dir.gsub(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR all_files = find_files(base_dir, File::FNM_DOTMATCH) - # use file.include? for performance optimization - hidden_files = all_files.select { |file| file.include?(HIDDEN_PATH_SUBSTRING) }.sort base_dir_config = @config_store.for(base_dir) - target_files = if base_dir.include?(HIDDEN_PATH_SUBSTRING) + target_files = if hidden_path?(base_dir) all_files.select { |file| ruby_file?(file) } else - all_files.select { |file| to_inspect?(file, hidden_files, base_dir_config) } + all_files.select { |file| to_inspect?(file, base_dir_config) } end target_files.sort_by!(&order) @@ -74,15 +72,17 @@ def find_files(base_dir, flags) private - def to_inspect?(file, hidden_files, base_dir_config) + def to_inspect?(file, base_dir_config) return false if base_dir_config.file_to_exclude?(file) - return true if !hidden_files.bsearch do |hidden_file| - file <=> hidden_file - end && ruby_file?(file) + return true if !hidden_path?(file) && ruby_file?(file) base_dir_config.file_to_include?(file) end + def hidden_path?(path) + path.include?(HIDDEN_PATH_SUBSTRING) + end + def wanted_dir_patterns(base_dir, exclude_pattern, flags) # Escape glob characters in base_dir to avoid unwanted behavior. base_dir = base_dir.gsub(/[\\{}\[\]*?]/) do |reserved_glob_character| From a39b602e30ae485e65db47361cf543a893a94a87 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Thu, 21 Aug 2025 17:15:01 +0900 Subject: [PATCH 33/37] [Fix #14468] Fix false positives for `Naming/MethodName` This PR fixes false positives for `Naming/MethodName` when an operator method is defined using a string. Fixes #14468. --- changelog/fix_false_positives_for_naming_method_name.md | 1 + lib/rubocop/cop/naming/method_name.rb | 2 +- spec/rubocop/cop/naming/method_name_spec.rb | 7 +++++++ 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 changelog/fix_false_positives_for_naming_method_name.md diff --git a/changelog/fix_false_positives_for_naming_method_name.md b/changelog/fix_false_positives_for_naming_method_name.md new file mode 100644 index 000000000000..c5afd600188a --- /dev/null +++ b/changelog/fix_false_positives_for_naming_method_name.md @@ -0,0 +1 @@ +* [#14468](https://github.com/rubocop/rubocop/issues/14468): Fix false positives for `Naming/MethodName` when an operator method is defined using a string. ([@koic][]) diff --git a/lib/rubocop/cop/naming/method_name.rb b/lib/rubocop/cop/naming/method_name.rb index 5b1789223a0e..c03a66ecd254 100644 --- a/lib/rubocop/cop/naming/method_name.rb +++ b/lib/rubocop/cop/naming/method_name.rb @@ -198,7 +198,7 @@ def handle_method_name(node, name) if forbidden_name?(name.to_s) register_forbidden_name(node) - elsif !OPERATOR_METHODS.include?(name) + elsif !OPERATOR_METHODS.include?(name.to_sym) check_name(node, name, range_position(node)) end end diff --git a/spec/rubocop/cop/naming/method_name_spec.rb b/spec/rubocop/cop/naming/method_name_spec.rb index 464678f18659..de1209e7f8a2 100644 --- a/spec/rubocop/cop/naming/method_name_spec.rb +++ b/spec/rubocop/cop/naming/method_name_spec.rb @@ -496,6 +496,13 @@ def %{identifier}; true; end end RUBY end + + it 'does not register an offense when an operator method is defined using a string' do + expect_no_offenses(<<~RUBY) + #{name} '`' do + end + RUBY + end end end From 570889af86b0385afe96678312dbac0fa4b56d93 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Thu, 21 Aug 2025 16:55:08 +0900 Subject: [PATCH 34/37] [Fix #14469] Fix an incorrect autocorrect for `Style/BitwisePredicate` This PR fixes an incorrect autocorrect for `Style/BitwisePredicate` when using `&` with RHS flags in conjunction with `==` for comparisons. Fixes #14469. --- ..._incorrect_autocorrect_for_style_bitwise_predicate.md | 1 + lib/rubocop/cop/style/bitwise_predicate.rb | 9 ++++++++- spec/rubocop/cop/style/bitwise_predicate_spec.rb | 4 ++-- 3 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 changelog/fix_an_incorrect_autocorrect_for_style_bitwise_predicate.md diff --git a/changelog/fix_an_incorrect_autocorrect_for_style_bitwise_predicate.md b/changelog/fix_an_incorrect_autocorrect_for_style_bitwise_predicate.md new file mode 100644 index 000000000000..eca8549766de --- /dev/null +++ b/changelog/fix_an_incorrect_autocorrect_for_style_bitwise_predicate.md @@ -0,0 +1 @@ +* [#14469](https://github.com/rubocop/rubocop/issues/14469): Fix an incorrect autocorrect for `Style/BitwisePredicate` when using `&` with LHS flags in conjunction with `==` for comparisons. ([@koic][]) diff --git a/lib/rubocop/cop/style/bitwise_predicate.rb b/lib/rubocop/cop/style/bitwise_predicate.rb index 9eee7c7d9778..41910e55de28 100644 --- a/lib/rubocop/cop/style/bitwise_predicate.rb +++ b/lib/rubocop/cop/style/bitwise_predicate.rb @@ -70,18 +70,25 @@ class BitwisePredicate < Base (send _ :& _)) PATTERN + # rubocop:disable Metrics/AbcSize def on_send(node) return unless node.receiver&.begin_type? return unless (preferred_method = preferred_method(node)) bit_operation = node.receiver.children.first lhs, _operator, rhs = *bit_operation - preferred = "#{lhs.source}.#{preferred_method}(#{rhs.source})" + + preferred = if preferred_method == 'allbits?' && lhs.source == node.first_argument.source + "#{rhs.source}.allbits?(#{lhs.source})" + else + "#{lhs.source}.#{preferred_method}(#{rhs.source})" + end add_offense(node, message: format(MSG, preferred: preferred)) do |corrector| corrector.replace(node, preferred) end end + # rubocop:enable Metrics/AbcSize private diff --git a/spec/rubocop/cop/style/bitwise_predicate_spec.rb b/spec/rubocop/cop/style/bitwise_predicate_spec.rb index 13ad9b00120d..2c66ae5101f5 100644 --- a/spec/rubocop/cop/style/bitwise_predicate_spec.rb +++ b/spec/rubocop/cop/style/bitwise_predicate_spec.rb @@ -91,11 +91,11 @@ it 'registers an offense when using `&` with LHS flags in conjunction with `==` for comparisons' do expect_offense(<<~RUBY) (flags & variable) == flags - ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Replace with `flags.allbits?(variable)` for comparison with bit flags. + ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Replace with `variable.allbits?(flags)` for comparison with bit flags. RUBY expect_correction(<<~RUBY) - flags.allbits?(variable) + variable.allbits?(flags) RUBY end From 2b7e6add50e341a43f2e082d7ac3edcbb785759a Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Fri, 22 Aug 2025 03:21:12 +0900 Subject: [PATCH 35/37] Make `Style/RedundantBegin` aware of `case` pattern matching Follow-up to https://github.com/rubocop/rubocop/pull/14463. This PR makes `Style/RedundantBegin` aware of `case` pattern matching. --- ...ake_redundant_begin_aware_of_case_match.md | 1 + lib/rubocop/cop/style/redundant_begin.rb | 1 + .../rubocop/cop/style/redundant_begin_spec.rb | 54 ++++++++++++++++++- 3 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 changelog/change_make_redundant_begin_aware_of_case_match.md diff --git a/changelog/change_make_redundant_begin_aware_of_case_match.md b/changelog/change_make_redundant_begin_aware_of_case_match.md new file mode 100644 index 000000000000..739ca9226e61 --- /dev/null +++ b/changelog/change_make_redundant_begin_aware_of_case_match.md @@ -0,0 +1 @@ +* [#14472](https://github.com/rubocop/rubocop/pull/14472): Make `Style/RedundantBegin` aware of `case` pattern matching. ([@koic][]) diff --git a/lib/rubocop/cop/style/redundant_begin.rb b/lib/rubocop/cop/style/redundant_begin.rb index 16476dfdc641..351a54af8719 100644 --- a/lib/rubocop/cop/style/redundant_begin.rb +++ b/lib/rubocop/cop/style/redundant_begin.rb @@ -94,6 +94,7 @@ def on_if(node) def on_case(node) inspect_branches(node) end + alias on_case_match on_case def on_while(node) return if node.modifier_form? diff --git a/spec/rubocop/cop/style/redundant_begin_spec.rb b/spec/rubocop/cop/style/redundant_begin_spec.rb index ea63190f4bd0..50bd6aa5f3d0 100644 --- a/spec/rubocop/cop/style/redundant_begin_spec.rb +++ b/spec/rubocop/cop/style/redundant_begin_spec.rb @@ -357,7 +357,7 @@ def method RUBY end - it 'registers and corrects an offense when a multiline `begin` block is inside `case-when`' do + it 'registers and corrects an offense when a multiline `begin` block is inside `case`/`when`' do expect_offense(<<~RUBY) case condition when foo @@ -380,7 +380,7 @@ def method RUBY end - it 'registers and corrects an offense when a multiline `begin` block is inside `case-else`' do + it 'registers and corrects an offense when a multiline `begin` block is inside `case`/`when`/`else`' do expect_offense(<<~RUBY) case condition when foo @@ -407,6 +407,56 @@ def method RUBY end + it 'registers and corrects an offense when a multiline `begin` block is inside `case`/`in`' do + expect_offense(<<~RUBY) + case condition + in foo + begin + ^^^^^ Redundant `begin` block detected. + bar + baz + end + end + RUBY + + expect_correction(<<~RUBY) + case condition + in foo + #{trailing_whitespace} + bar + baz + #{trailing_whitespace} + end + RUBY + end + + it 'registers and corrects an offense when a multiline `begin` block is inside `case`/`in`/`else`' do + expect_offense(<<~RUBY) + case condition + in foo + bar + else + begin + ^^^^^ Redundant `begin` block detected. + baz + quux + end + end + RUBY + + expect_correction(<<~RUBY) + case condition + in foo + bar + else + #{trailing_whitespace} + baz + quux + #{trailing_whitespace} + end + RUBY + end + it 'registers and corrects an offense when a multiline `begin` block is inside `while`' do expect_offense(<<~RUBY) while condition From 39a158f9e2e445719d878a394a9267ea3f1e6d98 Mon Sep 17 00:00:00 2001 From: Bozhidar Batsov Date: Fri, 22 Aug 2025 11:32:11 +0300 Subject: [PATCH 36/37] Update Changelog --- CHANGELOG.md | 26 +++++++++++++++++++ ...ment_with_many_arguments_20250806224151.md | 1 - ...correct_and_include_from_20250819090646.md | 1 - ...ake_redundant_begin_aware_of_case_match.md | 1 - ...ay_intersect_array_size_false_negatives.md | 1 - ...nt_sync_kind_incremental_20250811135600.md | 1 - ..._begin_to_register_begin_20250818135329.md | 1 - ...autocorrect_for_style_bitwise_predicate.md | 1 - ...x_autocorrect_style_for_save_navigation.md | 1 - changelog/fix_false_negatives_regexp_cops.md | 1 - ...e_positives_for_lint_useless_assignment.md | 1 - ..._false_positives_for_naming_method_name.md | 1 - ...ves_for_style_redundant_parentheses_cop.md | 1 - ..._hash_access_handling_in_20250815010135.md | 1 - ...ix_fix_false_positive_in_20250814000019.md | 1 - ...ion_cop_to_preserve_safe_20250805124624.md | 1 - ...ith_nonzero_args_with_an_20250817233441.md | 1 - changelog/fix_useless_assignment_for_loop.md | 1 - ...g_autocorrect_style_redundant_condition.md | 1 - 19 files changed, 26 insertions(+), 18 deletions(-) delete mode 100644 changelog/change_enhance_lint_self_assignment_to_detect_indexed_assignment_with_many_arguments_20250806224151.md delete mode 100644 changelog/change_exclude_auto_correct_and_include_from_20250819090646.md delete mode 100644 changelog/change_make_redundant_begin_aware_of_case_match.md delete mode 100644 changelog/change_style_array_intersect_array_size_false_negatives.md delete mode 100644 changelog/change_support_lsp_text_document_sync_kind_incremental_20250811135600.md delete mode 100644 changelog/change_update_style_redundant_begin_to_register_begin_20250818135329.md delete mode 100644 changelog/fix_an_incorrect_autocorrect_for_style_bitwise_predicate.md delete mode 100644 changelog/fix_autocorrect_style_for_save_navigation.md delete mode 100644 changelog/fix_false_negatives_regexp_cops.md delete mode 100644 changelog/fix_false_positives_for_lint_useless_assignment.md delete mode 100644 changelog/fix_false_positives_for_naming_method_name.md delete mode 100644 changelog/fix_false_positives_for_style_redundant_parentheses_cop.md delete mode 100644 changelog/fix_fix_14441_better_hash_access_handling_in_20250815010135.md delete mode 100644 changelog/fix_fix_false_positive_in_20250814000019.md delete mode 100644 changelog/fix_fix_safe_navigation_cop_to_preserve_safe_20250805124624.md delete mode 100644 changelog/fix_follow_module_inclusion_with_nonzero_args_with_an_20250817233441.md delete mode 100644 changelog/fix_useless_assignment_for_loop.md delete mode 100644 changelog/fix_wrong_autocorrect_style_redundant_condition.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 86f674d13233..d0173af357ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,30 @@ ## master (unreleased) +### Bug fixes + +* [#14469](https://github.com/rubocop/rubocop/issues/14469): Fix an incorrect autocorrect for `Style/BitwisePredicate` when using `&` with LHS flags in conjunction with `==` for comparisons. ([@koic][]) +* [#14459](https://github.com/rubocop/rubocop/pull/14459): Fix wrong autocorrect for `Style/For` with save navigation in the collection. ([@earlopain][]) +* [#14435](https://github.com/rubocop/rubocop/issues/14435): Fix false negatives for regexp cops when `Lint/DuplicateRegexpCharacterClassElement` is enabled. ([@earlopain][]) +* [#14419](https://github.com/rubocop/rubocop/issues/14419): Fix false positives for `Lint/UselessAssignment` when duplicate assignments appear in nested `if` branches inside a loop and the variable is used outside `while` loop. ([@koic][]) +* [#14468](https://github.com/rubocop/rubocop/issues/14468): Fix false positives for `Naming/MethodName` when an operator method is defined using a string. ([@koic][]) +* [#14427](https://github.com/rubocop/rubocop/pull/14427): Fix false positives for `Style/RedundantParentheses` when `do`...`end` block is wrapped in parentheses as a method argument. ([@koic][]) +* [#14441](https://github.com/rubocop/rubocop/issues/14441): Better hash access handling in `Style/SafeNavigation`. ([@issyl0][]) +* [#14443](https://github.com/rubocop/rubocop/issues/14443): Fix false positive in `Layout/EmptyLinesAfterModuleInclusion` when `include` does not have exactly one argument. ([@issyl0][]) +* [#14424](https://github.com/rubocop/rubocop/pull/14424): Fix `Style/SafeNavigation` cop to preserve existing safe navigation in fixed code. ([@martinemde][]) +* [#14455](https://github.com/rubocop/rubocop/pull/14455): Follow module inclusion with nonzero args with an empty line. ([@issyl0][]) +* [#14445](https://github.com/rubocop/rubocop/issues/14445): Fix false positives for `Lint/UselessAssignment` with `for` loops when the variable is referenced in the collection. ([@earlopain][]) +* [#14447](https://github.com/rubocop/rubocop/pull/14447): Fix wrong autocorrect for `Style/RedundantCondition` with a parenthesised method call in the condition. ([@earlopain][]) + +### Changes + +* [#14428](https://github.com/rubocop/rubocop/pull/14428): Enhance `Lint/SelfAssignment` to handle indexed assignment with multiple arguments. ([@viralpraxis][]) +* [#14464](https://github.com/rubocop/rubocop/pull/14464): Exclude `AutoCorrect` and `Include` from configuration parameters. ([@r7kamura][]) +* [#14472](https://github.com/rubocop/rubocop/pull/14472): Make `Style/RedundantBegin` aware of `case` pattern matching. ([@koic][]) +* [#14448](https://github.com/rubocop/rubocop/pull/14448): Register array intersection size checks as offenses under `Style/ArrayIntersect`. ([@lovro-bikic][]) +* [#14431](https://github.com/rubocop/rubocop/pull/14431): Support LSP `TextDocumentSyncKind.Incremental`. ([@tmtm][]) +* [#14453](https://github.com/rubocop/rubocop/issues/14453): Update `Style/RedundantBegin` to register `begin` blocks inside `if`, `unless`, `case`, `while` and `until` as redundant. ([@dvandersluis][]) + ## 1.79.2 (2025-08-05) ### Bug fixes @@ -4290,3 +4314,5 @@ [@girasquid]: https://github.com/girasquid [@hakanensari]: https://github.com/hakanensari [@jvlara]: https://github.com/jvlara +[@tmtm]: https://github.com/tmtm +[@martinemde]: https://github.com/martinemde diff --git a/changelog/change_enhance_lint_self_assignment_to_detect_indexed_assignment_with_many_arguments_20250806224151.md b/changelog/change_enhance_lint_self_assignment_to_detect_indexed_assignment_with_many_arguments_20250806224151.md deleted file mode 100644 index 485e13f0ba1b..000000000000 --- a/changelog/change_enhance_lint_self_assignment_to_detect_indexed_assignment_with_many_arguments_20250806224151.md +++ /dev/null @@ -1 +0,0 @@ -* [#14428](https://github.com/rubocop/rubocop/pull/14428): Enhance `Lint/SelfAssignment` to handle indexed assignment with multiple arguments. ([@viralpraxis][]) diff --git a/changelog/change_exclude_auto_correct_and_include_from_20250819090646.md b/changelog/change_exclude_auto_correct_and_include_from_20250819090646.md deleted file mode 100644 index c9752d13faf7..000000000000 --- a/changelog/change_exclude_auto_correct_and_include_from_20250819090646.md +++ /dev/null @@ -1 +0,0 @@ -* [#14464](https://github.com/rubocop/rubocop/pull/14464): Exclude `AutoCorrect` and `Include` from configuration parameters. ([@r7kamura][]) diff --git a/changelog/change_make_redundant_begin_aware_of_case_match.md b/changelog/change_make_redundant_begin_aware_of_case_match.md deleted file mode 100644 index 739ca9226e61..000000000000 --- a/changelog/change_make_redundant_begin_aware_of_case_match.md +++ /dev/null @@ -1 +0,0 @@ -* [#14472](https://github.com/rubocop/rubocop/pull/14472): Make `Style/RedundantBegin` aware of `case` pattern matching. ([@koic][]) diff --git a/changelog/change_style_array_intersect_array_size_false_negatives.md b/changelog/change_style_array_intersect_array_size_false_negatives.md deleted file mode 100644 index edbbadd48919..000000000000 --- a/changelog/change_style_array_intersect_array_size_false_negatives.md +++ /dev/null @@ -1 +0,0 @@ -* [#14448](https://github.com/rubocop/rubocop/pull/14448): Register array intersection size checks as offenses under `Style/ArrayIntersect`. ([@lovro-bikic][]) diff --git a/changelog/change_support_lsp_text_document_sync_kind_incremental_20250811135600.md b/changelog/change_support_lsp_text_document_sync_kind_incremental_20250811135600.md deleted file mode 100644 index 844c1bbcefa7..000000000000 --- a/changelog/change_support_lsp_text_document_sync_kind_incremental_20250811135600.md +++ /dev/null @@ -1 +0,0 @@ -* [#14431](https://github.com/rubocop/rubocop/pull/14431): Support LSP `TextDocumentSyncKind.Incremental`. ([@tmtm][]) diff --git a/changelog/change_update_style_redundant_begin_to_register_begin_20250818135329.md b/changelog/change_update_style_redundant_begin_to_register_begin_20250818135329.md deleted file mode 100644 index 8ddfb8777000..000000000000 --- a/changelog/change_update_style_redundant_begin_to_register_begin_20250818135329.md +++ /dev/null @@ -1 +0,0 @@ -* [#14453](https://github.com/rubocop/rubocop/issues/14453): Update `Style/RedundantBegin` to register `begin` blocks inside `if`, `unless`, `case`, `while` and `until` as redundant. ([@dvandersluis][]) diff --git a/changelog/fix_an_incorrect_autocorrect_for_style_bitwise_predicate.md b/changelog/fix_an_incorrect_autocorrect_for_style_bitwise_predicate.md deleted file mode 100644 index eca8549766de..000000000000 --- a/changelog/fix_an_incorrect_autocorrect_for_style_bitwise_predicate.md +++ /dev/null @@ -1 +0,0 @@ -* [#14469](https://github.com/rubocop/rubocop/issues/14469): Fix an incorrect autocorrect for `Style/BitwisePredicate` when using `&` with LHS flags in conjunction with `==` for comparisons. ([@koic][]) diff --git a/changelog/fix_autocorrect_style_for_save_navigation.md b/changelog/fix_autocorrect_style_for_save_navigation.md deleted file mode 100644 index 60fb6d282e30..000000000000 --- a/changelog/fix_autocorrect_style_for_save_navigation.md +++ /dev/null @@ -1 +0,0 @@ -* [#14459](https://github.com/rubocop/rubocop/pull/14459): Fix wrong autocorrect for `Style/For` with save navigation in the collection. ([@earlopain][]) diff --git a/changelog/fix_false_negatives_regexp_cops.md b/changelog/fix_false_negatives_regexp_cops.md deleted file mode 100644 index 20aca4916545..000000000000 --- a/changelog/fix_false_negatives_regexp_cops.md +++ /dev/null @@ -1 +0,0 @@ -* [#14435](https://github.com/rubocop/rubocop/issues/14435): Fix false negatives for regexp cops when `Lint/DuplicateRegexpCharacterClassElement` is enabled. ([@earlopain][]) diff --git a/changelog/fix_false_positives_for_lint_useless_assignment.md b/changelog/fix_false_positives_for_lint_useless_assignment.md deleted file mode 100644 index 0f81fa3a5c16..000000000000 --- a/changelog/fix_false_positives_for_lint_useless_assignment.md +++ /dev/null @@ -1 +0,0 @@ -* [#14419](https://github.com/rubocop/rubocop/issues/14419): Fix false positives for `Lint/UselessAssignment` when duplicate assignments appear in nested `if` branches inside a loop and the variable is used outside `while` loop. ([@koic][]) diff --git a/changelog/fix_false_positives_for_naming_method_name.md b/changelog/fix_false_positives_for_naming_method_name.md deleted file mode 100644 index c5afd600188a..000000000000 --- a/changelog/fix_false_positives_for_naming_method_name.md +++ /dev/null @@ -1 +0,0 @@ -* [#14468](https://github.com/rubocop/rubocop/issues/14468): Fix false positives for `Naming/MethodName` when an operator method is defined using a string. ([@koic][]) diff --git a/changelog/fix_false_positives_for_style_redundant_parentheses_cop.md b/changelog/fix_false_positives_for_style_redundant_parentheses_cop.md deleted file mode 100644 index 248db4e3f25f..000000000000 --- a/changelog/fix_false_positives_for_style_redundant_parentheses_cop.md +++ /dev/null @@ -1 +0,0 @@ -* [#14427](https://github.com/rubocop/rubocop/pull/14427): Fix false positives for `Style/RedundantParentheses` when `do`...`end` block is wrapped in parentheses as a method argument. ([@koic][]) diff --git a/changelog/fix_fix_14441_better_hash_access_handling_in_20250815010135.md b/changelog/fix_fix_14441_better_hash_access_handling_in_20250815010135.md deleted file mode 100644 index a95c2c305b6c..000000000000 --- a/changelog/fix_fix_14441_better_hash_access_handling_in_20250815010135.md +++ /dev/null @@ -1 +0,0 @@ -* [#14441](https://github.com/rubocop/rubocop/issues/14441): Better hash access handling in `Style/SafeNavigation`. ([@issyl0][]) diff --git a/changelog/fix_fix_false_positive_in_20250814000019.md b/changelog/fix_fix_false_positive_in_20250814000019.md deleted file mode 100644 index ed6db89c262b..000000000000 --- a/changelog/fix_fix_false_positive_in_20250814000019.md +++ /dev/null @@ -1 +0,0 @@ -* [#14443](https://github.com/rubocop/rubocop/issues/14443): Fix false positive in `Layout/EmptyLinesAfterModuleInclusion` when `include` does not have exactly one argument. ([@issyl0][]) diff --git a/changelog/fix_fix_safe_navigation_cop_to_preserve_safe_20250805124624.md b/changelog/fix_fix_safe_navigation_cop_to_preserve_safe_20250805124624.md deleted file mode 100644 index a6538a984a00..000000000000 --- a/changelog/fix_fix_safe_navigation_cop_to_preserve_safe_20250805124624.md +++ /dev/null @@ -1 +0,0 @@ -* [#14424](https://github.com/rubocop/rubocop/pull/14424): Fix `Style/SafeNavigation` cop to preserve existing safe navigation in fixed code. ([@martinemde][]) diff --git a/changelog/fix_follow_module_inclusion_with_nonzero_args_with_an_20250817233441.md b/changelog/fix_follow_module_inclusion_with_nonzero_args_with_an_20250817233441.md deleted file mode 100644 index 4d0fcd3c59e8..000000000000 --- a/changelog/fix_follow_module_inclusion_with_nonzero_args_with_an_20250817233441.md +++ /dev/null @@ -1 +0,0 @@ -* [#14455](https://github.com/rubocop/rubocop/pull/14455): Follow module inclusion with nonzero args with an empty line. ([@issyl0][]) diff --git a/changelog/fix_useless_assignment_for_loop.md b/changelog/fix_useless_assignment_for_loop.md deleted file mode 100644 index ef45a979ff52..000000000000 --- a/changelog/fix_useless_assignment_for_loop.md +++ /dev/null @@ -1 +0,0 @@ -* [#14445](https://github.com/rubocop/rubocop/issues/14445): Fix false positives for `Lint/UselessAssignment` with `for` loops when the variable is referenced in the collection. ([@earlopain][]) diff --git a/changelog/fix_wrong_autocorrect_style_redundant_condition.md b/changelog/fix_wrong_autocorrect_style_redundant_condition.md deleted file mode 100644 index 28772cb38c85..000000000000 --- a/changelog/fix_wrong_autocorrect_style_redundant_condition.md +++ /dev/null @@ -1 +0,0 @@ -* [#14447](https://github.com/rubocop/rubocop/pull/14447): Fix wrong autocorrect for `Style/RedundantCondition` with a parenthesised method call in the condition. ([@earlopain][]) From 732ee2975e440e5753e56c4bbc832a4b7087e596 Mon Sep 17 00:00:00 2001 From: Bozhidar Batsov Date: Fri, 22 Aug 2025 11:33:02 +0300 Subject: [PATCH 37/37] Cut 1.80 --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- CHANGELOG.md | 2 ++ CONTRIBUTING.md | 2 +- README.md | 2 +- docs/antora.yml | 2 +- docs/modules/ROOT/pages/cops_layout.adoc | 2 ++ docs/modules/ROOT/pages/cops_style.adoc | 15 +++++++++ docs/modules/ROOT/pages/installation.adoc | 2 +- .../pages/integration_with_other_tools.adoc | 4 +-- lib/rubocop/version.rb | 2 +- relnotes/v1.80.0.md | 33 +++++++++++++++++++ 11 files changed, 60 insertions(+), 8 deletions(-) create mode 100644 relnotes/v1.80.0.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index b21e463ef324..916060f5a238 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -39,7 +39,7 @@ output by `rubocop -V`, include them as well. Here's an example: ``` $ [bundle exec] rubocop -V -1.79.2 (using Parser 3.3.5.0, rubocop-ast 1.32.3, analyzing as Ruby 3.3, running on ruby 3.3.5) [x86_64-linux] +1.80.0 (using Parser 3.3.5.0, rubocop-ast 1.32.3, analyzing as Ruby 3.3, running on ruby 3.3.5) [x86_64-linux] - rubocop-performance 1.22.1 - rubocop-rspec 3.1.0 ``` diff --git a/CHANGELOG.md b/CHANGELOG.md index d0173af357ec..a2058da5337f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ ## master (unreleased) +## 1.80.0 (2025-08-22) + ### Bug fixes * [#14469](https://github.com/rubocop/rubocop/issues/14469): Fix an incorrect autocorrect for `Style/BitwisePredicate` when using `&` with LHS flags in conjunction with `==` for comparisons. ([@koic][]) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index af3fceb6a8f1..0a6b8ea3d1f1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,7 +17,7 @@ do so. ```console $ rubocop -V -1.79.2 (using Parser 3.3.5.0, rubocop-ast 1.32.3, analyzing as Ruby 3.3, running on ruby 3.3.5) [x86_64-linux] +1.80.0 (using Parser 3.3.5.0, rubocop-ast 1.32.3, analyzing as Ruby 3.3, running on ruby 3.3.5) [x86_64-linux] - rubocop-performance 1.22.1 - rubocop-rspec 3.1.0 ``` diff --git a/README.md b/README.md index 52c30097803b..73f18d381ae3 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ To prevent an unwanted RuboCop update you might want to use a conservative versi in your `Gemfile`: ```rb -gem 'rubocop', '~> 1.79', require: false +gem 'rubocop', '~> 1.80', require: false ``` See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) for further details. diff --git a/docs/antora.yml b/docs/antora.yml index 9c897ea03d1d..d313e5883ed1 100644 --- a/docs/antora.yml +++ b/docs/antora.yml @@ -2,6 +2,6 @@ name: rubocop title: RuboCop # We always provide version without patch here (e.g. 1.1), # as patch versions should not appear in the docs. -version: ~ +version: '1.80' nav: - modules/ROOT/nav.adoc diff --git a/docs/modules/ROOT/pages/cops_layout.adoc b/docs/modules/ROOT/pages/cops_layout.adoc index 2ec69019094a..a75e35ea6a3f 100644 --- a/docs/modules/ROOT/pages/cops_layout.adoc +++ b/docs/modules/ROOT/pages/cops_layout.adoc @@ -6149,6 +6149,8 @@ The `aligned` style checks that operators are aligned if they are part of an `if condition, an explicit `return` statement, etc. In other contexts, the second operand should be indented regardless of enforced style. +In both styles, operators should be aligned when an assignment begins on the next line. + [#examples-layoutmultilineoperationindentation] === Examples diff --git a/docs/modules/ROOT/pages/cops_style.adoc b/docs/modules/ROOT/pages/cops_style.adoc index 8c89fcf7e8ec..a9ad8a024dd8 100644 --- a/docs/modules/ROOT/pages/cops_style.adoc +++ b/docs/modules/ROOT/pages/cops_style.adoc @@ -851,6 +851,8 @@ This cop identifies places where: * `(array1 & array2).any?` * `(array1.intersection(array2)).any?` * `array1.any? { |elem| array2.member?(elem) }` +* `(array1 & array2).count > 0` +* `(array1 & array2).size > 0` can be replaced with `array1.intersect?(array2)`. @@ -896,6 +898,19 @@ array1.none? { |elem| array2.member?(elem) } # good array1.intersect?(array2) +!array1.intersect?(array2) + +# bad +(array1 & array2).count > 0 +(array1 & array2).count.positive? +(array1 & array2).count != 0 + +(array1 & array2).count == 0 +(array1 & array2).count.zero? + +# good +array1.intersect?(array2) + !array1.intersect?(array2) ---- diff --git a/docs/modules/ROOT/pages/installation.adoc b/docs/modules/ROOT/pages/installation.adoc index 38885d572301..1be97a557f6a 100644 --- a/docs/modules/ROOT/pages/installation.adoc +++ b/docs/modules/ROOT/pages/installation.adoc @@ -22,7 +22,7 @@ in your `Gemfile`: [source,rb] ---- -gem 'rubocop', '~> 1.79', require: false +gem 'rubocop', '~> 1.80', require: false ---- .A Modular RuboCop diff --git a/docs/modules/ROOT/pages/integration_with_other_tools.adoc b/docs/modules/ROOT/pages/integration_with_other_tools.adoc index e792183f44d9..ee61bccdbc12 100644 --- a/docs/modules/ROOT/pages/integration_with_other_tools.adoc +++ b/docs/modules/ROOT/pages/integration_with_other_tools.adoc @@ -125,7 +125,7 @@ following to your `.pre-commit-config.yaml` file: [source,yaml] ---- - repo: https://github.com/rubocop/rubocop - rev: v1.79.2 + rev: v1.80.0 hooks: - id: rubocop ---- @@ -136,7 +136,7 @@ entries in `additional_dependencies`: [source,yaml] ---- - repo: https://github.com/rubocop/rubocop - rev: v1.79.2 + rev: v1.80.0 hooks: - id: rubocop additional_dependencies: diff --git a/lib/rubocop/version.rb b/lib/rubocop/version.rb index ebcd211474b1..3c14466a06e9 100644 --- a/lib/rubocop/version.rb +++ b/lib/rubocop/version.rb @@ -3,7 +3,7 @@ module RuboCop # This module holds the RuboCop version information. module Version - STRING = '1.79.2' + STRING = '1.80.0' MSG = '%s (using %s, ' \ 'rubocop-ast %s, ' \ diff --git a/relnotes/v1.80.0.md b/relnotes/v1.80.0.md new file mode 100644 index 000000000000..1cc61c023cb0 --- /dev/null +++ b/relnotes/v1.80.0.md @@ -0,0 +1,33 @@ +### Bug fixes + +* [#14469](https://github.com/rubocop/rubocop/issues/14469): Fix an incorrect autocorrect for `Style/BitwisePredicate` when using `&` with LHS flags in conjunction with `==` for comparisons. ([@koic][]) +* [#14459](https://github.com/rubocop/rubocop/pull/14459): Fix wrong autocorrect for `Style/For` with save navigation in the collection. ([@earlopain][]) +* [#14435](https://github.com/rubocop/rubocop/issues/14435): Fix false negatives for regexp cops when `Lint/DuplicateRegexpCharacterClassElement` is enabled. ([@earlopain][]) +* [#14419](https://github.com/rubocop/rubocop/issues/14419): Fix false positives for `Lint/UselessAssignment` when duplicate assignments appear in nested `if` branches inside a loop and the variable is used outside `while` loop. ([@koic][]) +* [#14468](https://github.com/rubocop/rubocop/issues/14468): Fix false positives for `Naming/MethodName` when an operator method is defined using a string. ([@koic][]) +* [#14427](https://github.com/rubocop/rubocop/pull/14427): Fix false positives for `Style/RedundantParentheses` when `do`...`end` block is wrapped in parentheses as a method argument. ([@koic][]) +* [#14441](https://github.com/rubocop/rubocop/issues/14441): Better hash access handling in `Style/SafeNavigation`. ([@issyl0][]) +* [#14443](https://github.com/rubocop/rubocop/issues/14443): Fix false positive in `Layout/EmptyLinesAfterModuleInclusion` when `include` does not have exactly one argument. ([@issyl0][]) +* [#14424](https://github.com/rubocop/rubocop/pull/14424): Fix `Style/SafeNavigation` cop to preserve existing safe navigation in fixed code. ([@martinemde][]) +* [#14455](https://github.com/rubocop/rubocop/pull/14455): Follow module inclusion with nonzero args with an empty line. ([@issyl0][]) +* [#14445](https://github.com/rubocop/rubocop/issues/14445): Fix false positives for `Lint/UselessAssignment` with `for` loops when the variable is referenced in the collection. ([@earlopain][]) +* [#14447](https://github.com/rubocop/rubocop/pull/14447): Fix wrong autocorrect for `Style/RedundantCondition` with a parenthesised method call in the condition. ([@earlopain][]) + +### Changes + +* [#14428](https://github.com/rubocop/rubocop/pull/14428): Enhance `Lint/SelfAssignment` to handle indexed assignment with multiple arguments. ([@viralpraxis][]) +* [#14464](https://github.com/rubocop/rubocop/pull/14464): Exclude `AutoCorrect` and `Include` from configuration parameters. ([@r7kamura][]) +* [#14472](https://github.com/rubocop/rubocop/pull/14472): Make `Style/RedundantBegin` aware of `case` pattern matching. ([@koic][]) +* [#14448](https://github.com/rubocop/rubocop/pull/14448): Register array intersection size checks as offenses under `Style/ArrayIntersect`. ([@lovro-bikic][]) +* [#14431](https://github.com/rubocop/rubocop/pull/14431): Support LSP `TextDocumentSyncKind.Incremental`. ([@tmtm][]) +* [#14453](https://github.com/rubocop/rubocop/issues/14453): Update `Style/RedundantBegin` to register `begin` blocks inside `if`, `unless`, `case`, `while` and `until` as redundant. ([@dvandersluis][]) + +[@koic]: https://github.com/koic +[@earlopain]: https://github.com/earlopain +[@issyl0]: https://github.com/issyl0 +[@martinemde]: https://github.com/martinemde +[@viralpraxis]: https://github.com/viralpraxis +[@r7kamura]: https://github.com/r7kamura +[@lovro-bikic]: https://github.com/lovro-bikic +[@tmtm]: https://github.com/tmtm +[@dvandersluis]: https://github.com/dvandersluis