From 69ad338e3391f132c8d3d82db45e28819bb79034 Mon Sep 17 00:00:00 2001 From: Bozhidar Batsov Date: Wed, 26 Feb 2025 09:38:46 +0200 Subject: [PATCH 01/26] 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 0ec0cdbc5d57..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.73' +version: ~ nav: - modules/ROOT/nav.adoc From c432974f960011a6a06eeecf6fe63b827c6fa796 Mon Sep 17 00:00:00 2001 From: Bozhidar Batsov Date: Wed, 26 Feb 2025 09:40:54 +0200 Subject: [PATCH 02/26] [GHA] Fix a typo in the GH release workflow --- .github/workflows/github_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github_release.yml b/.github/workflows/github_release.yml index 74230aa9ca77..fb09b40d804e 100644 --- a/.github/workflows/github_release.yml +++ b/.github/workflows/github_release.yml @@ -21,5 +21,5 @@ jobs: with: tag: ${{ github.ref_name }} name: RuboCop ${{ github.ref_name }} - bodyFile: releases${{ github.ref_name }}.md + bodyFile: releases/${{ github.ref_name }}.md token: ${{ secrets.GITHUB_TOKEN }} From e583e3252bbebe034b17786023a7f33b5ea8697a Mon Sep 17 00:00:00 2001 From: Bozhidar Batsov Date: Wed, 26 Feb 2025 09:42:11 +0200 Subject: [PATCH 03/26] [GHA] Use the right directory name Seems that yesterday I've messed up everything that I could have. --- .github/workflows/github_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github_release.yml b/.github/workflows/github_release.yml index fb09b40d804e..2607babd6251 100644 --- a/.github/workflows/github_release.yml +++ b/.github/workflows/github_release.yml @@ -21,5 +21,5 @@ jobs: with: tag: ${{ github.ref_name }} name: RuboCop ${{ github.ref_name }} - bodyFile: releases/${{ github.ref_name }}.md + bodyFile: relnotes/${{ github.ref_name }}.md token: ${{ secrets.GITHUB_TOKEN }} From 5b287c48fc8e2889e963c17e143d879602952131 Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Wed, 26 Feb 2025 12:05:57 +0100 Subject: [PATCH 04/26] Fix wrong autocorrect for `Lint/EmptyConditionalBody` when assigning to a variable with only a single branch This currently produces wrong syntax: ``` var = if; end # => var = ``` I chose not to offer autocorrect in such cases. But you could also: * Remove the variable as well * Assign nil to it Not doing anything seems safest --- ...ix_autocorrect_empty_cond_single_branch.md | 1 + .../cop/lint/empty_conditional_body.rb | 15 ++++++++----- .../cop/lint/empty_conditional_body_spec.rb | 22 +++++++++++++++++++ 3 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 changelog/fix_autocorrect_empty_cond_single_branch.md diff --git a/changelog/fix_autocorrect_empty_cond_single_branch.md b/changelog/fix_autocorrect_empty_cond_single_branch.md new file mode 100644 index 000000000000..563cc25a7949 --- /dev/null +++ b/changelog/fix_autocorrect_empty_cond_single_branch.md @@ -0,0 +1 @@ +* [#13912](https://github.com/rubocop/rubocop/pull/13912): Fix wrong autocorrect for `Lint/EmptyConditionalBody` when assigning to a variable with only a single branch. ([@earlopain][]) diff --git a/lib/rubocop/cop/lint/empty_conditional_body.rb b/lib/rubocop/cop/lint/empty_conditional_body.rb index 84b937455b33..5bcafceb13d9 100644 --- a/lib/rubocop/cop/lint/empty_conditional_body.rb +++ b/lib/rubocop/cop/lint/empty_conditional_body.rb @@ -67,7 +67,6 @@ class EmptyConditionalBody < Base MSG = 'Avoid `%s` branches without a body.' - # rubocop:disable Metrics/AbcSize def on_if(node) return if node.body || same_line?(node.loc.begin, node.loc.end) return if cop_config['AllowComments'] && contains_comments?(node) @@ -75,15 +74,21 @@ def on_if(node) range = offense_range(node) add_offense(range, message: format(MSG, keyword: node.keyword)) do |corrector| - next if node.parent&.call_type? - - autocorrect(corrector, node) + autocorrect(corrector, node) if do_autocorrect?(node) end end - # rubocop:enable Metrics/AbcSize private + def do_autocorrect?(node) + # if condition; end.do_something + return false if (parent = node.parent)&.call_type? + # x = if condition; end + return false if (parent&.assignment? || parent&.operator_keyword?) && node.children.one? + + true + end + def offense_range(node) if node.loc.else node.source_range.begin.join(node.loc.else.begin) diff --git a/spec/rubocop/cop/lint/empty_conditional_body_spec.rb b/spec/rubocop/cop/lint/empty_conditional_body_spec.rb index acfaa3c8ab4d..33fb286ec4cd 100644 --- a/spec/rubocop/cop/lint/empty_conditional_body_spec.rb +++ b/spec/rubocop/cop/lint/empty_conditional_body_spec.rb @@ -416,6 +416,26 @@ class Foo RUBY end + it 'does not autocorrect when there is only one branch with assignment' do + expect_offense(<<~RUBY) + x = if foo + ^^^^^^ Avoid `if` branches without a body. + end + RUBY + + expect_no_corrections + end + + it 'does not autocorrect when there is only one branch after `||`' do + expect_offense(<<~RUBY) + x || if foo + ^^^^^^ Avoid `if` branches without a body. + end + RUBY + + expect_no_corrections + end + context '>= Ruby 3.1', :ruby31 do it 'registers an offense for multi-line value omission in `unless`' do expect_offense(<<~RUBY) @@ -426,6 +446,8 @@ class Foo condition || other_condition end RUBY + + expect_no_corrections end end From 6fa9c371b86a41d66fadeffd11cb3cd6480c02ca Mon Sep 17 00:00:00 2001 From: Tejas Bubane Date: Wed, 26 Feb 2025 17:18:03 +0400 Subject: [PATCH 05/26] [Fix #13909] Fix false positive with `Layout/ClosingParenthesisIndentation` when first parameter is a hash Closes https://github.com/rubocop/rubocop/issues/13909 --- changelog/fix_fix_false_positive_with_20250226171903.md | 1 + .../cop/layout/closing_parenthesis_indentation.rb | 8 ++++---- .../cop/layout/closing_parenthesis_indentation_spec.rb | 9 +++++++++ 3 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 changelog/fix_fix_false_positive_with_20250226171903.md diff --git a/changelog/fix_fix_false_positive_with_20250226171903.md b/changelog/fix_fix_false_positive_with_20250226171903.md new file mode 100644 index 000000000000..ddc3a186e05d --- /dev/null +++ b/changelog/fix_fix_false_positive_with_20250226171903.md @@ -0,0 +1 @@ +* [#13909](https://github.com/rubocop/rubocop/issues/13909): Fix false positive with `Layout/ClosingParenthesisIndentation` when first parameter is a hash. ([@tejasbubane][]) diff --git a/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb b/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb index 99d4bf043b21..8e7e9e61edfd 100644 --- a/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +++ b/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb @@ -155,10 +155,10 @@ def expected_column(left_paren, elements) end def all_elements_aligned?(elements) - elements.flat_map do |e| - if e.hash_type? - e.each_child_node.map { |child| child.loc.column } - else + if elements.first.hash_type? + elements.first.each_child_node.map { |child| child.loc.column } + else + elements.flat_map do |e| e.loc.column end end.uniq.count == 1 diff --git a/spec/rubocop/cop/layout/closing_parenthesis_indentation_spec.rb b/spec/rubocop/cop/layout/closing_parenthesis_indentation_spec.rb index 3409ae7033aa..111491f53566 100644 --- a/spec/rubocop/cop/layout/closing_parenthesis_indentation_spec.rb +++ b/spec/rubocop/cop/layout/closing_parenthesis_indentation_spec.rb @@ -114,6 +114,15 @@ ) RUBY end + + it 'does not register offense for aligned parens when first parameter is a hash' do + expect_no_offenses(<<~RUBY) + some_method({ foo: 1, bar: 2 }, + x: 1, + y: 2 + ) + RUBY + end end context 'without arguments' do From 0e334241d791ee3696745a3b3cb6e82c75433bda Mon Sep 17 00:00:00 2001 From: dak2 Date: Wed, 26 Feb 2025 23:25:48 +0900 Subject: [PATCH 06/26] Fix writing generics type of rbs-inline annotation for nested class in `Style/CommentedKeyword` [RBS::Inline](https://github.com/soutaro/rbs-inline) is a utility to embed RBS type declarations into Ruby code as comments. I made rules for generics type below pull request, but nested classes were not supported. Follow https://github.com/rubocop/rubocop/pull/13476 --- ...of_rbs_inline_annotation_20250226233214.md | 1 + lib/rubocop/cop/style/commented_keyword.rb | 2 +- .../cop/style/commented_keyword_spec.rb | 32 +++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 changelog/fix_fix_writing_generics_type_of_rbs_inline_annotation_20250226233214.md diff --git a/changelog/fix_fix_writing_generics_type_of_rbs_inline_annotation_20250226233214.md b/changelog/fix_fix_writing_generics_type_of_rbs_inline_annotation_20250226233214.md new file mode 100644 index 000000000000..f18c6517c6db --- /dev/null +++ b/changelog/fix_fix_writing_generics_type_of_rbs_inline_annotation_20250226233214.md @@ -0,0 +1 @@ +* [#13915](https://github.com/rubocop/rubocop/pull/13915): Fix writing generics type of rbs-inline annotation for nested class in `Style/CommentedKeyword`. ([@dak2][]) diff --git a/lib/rubocop/cop/style/commented_keyword.rb b/lib/rubocop/cop/style/commented_keyword.rb index 17ec063d7156..d41702d10ce3 100644 --- a/lib/rubocop/cop/style/commented_keyword.rb +++ b/lib/rubocop/cop/style/commented_keyword.rb @@ -57,7 +57,7 @@ class CommentedKeyword < Base REGEXP = /(?\S+).*#/.freeze - SUBCLASS_DEFINITION = /\A\s*class\s+\w+\s*<\s*\w+/.freeze + SUBCLASS_DEFINITION = /\A\s*class\s+(\w|::)+\s*<\s*(\w|::)+/.freeze METHOD_DEFINITION = /\A\s*def\s/.freeze def on_new_investigation diff --git a/spec/rubocop/cop/style/commented_keyword_spec.rb b/spec/rubocop/cop/style/commented_keyword_spec.rb index c1b95f780330..5c02fcb5201b 100644 --- a/spec/rubocop/cop/style/commented_keyword_spec.rb +++ b/spec/rubocop/cop/style/commented_keyword_spec.rb @@ -241,6 +241,14 @@ def x(y: "#value") expect_no_offenses(<<~RUBY) class X < Y #[String] end + class A < B::C #[String] + end + class A < B::C::D #[String] + end + class A::B < C #[String] + end + class A::B::C < D #[String] + end RUBY end @@ -261,6 +269,18 @@ class X < Y # String] class X < Y #String ] ^^^^^^^^^ Do not place comments on the same line as the `class` keyword. end + class A < B::C #String] + ^^^^^^^^ Do not place comments on the same line as the `class` keyword. + end + class A < B::C::D #String] + ^^^^^^^^ Do not place comments on the same line as the `class` keyword. + end + class A::B < C #String] + ^^^^^^^^ Do not place comments on the same line as the `class` keyword. + end + class A::B::C < D #String] + ^^^^^^^^ Do not place comments on the same line as the `class` keyword. + end RUBY expect_correction(<<~RUBY) @@ -279,6 +299,18 @@ class X < Y #String ] class X < Y end + #String] + class A < B::C + end + #String] + class A < B::C::D + end + #String] + class A::B < C + end + #String] + class A::B::C < D + end RUBY end From 53e5d198fa69abd08f9221067634755d17c8769c Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Thu, 27 Feb 2025 12:18:10 +0900 Subject: [PATCH 07/26] [Fix #13913] Fix false positives for `Style/RedundantCondition` This PR fixes false positives for `Style/RedundantCondition` when using when true is used as the true branch and condition is not predicate method. Fixes #13913. --- ...positives_for_style_redundant_condition.md | 1 + config/default.yml | 3 ++ lib/rubocop/cop/style/redundant_condition.rb | 12 +++++ .../cop/style/redundant_condition_spec.rb | 49 +++++++++++++++++++ 4 files changed, 65 insertions(+) create mode 100644 changelog/fix_false_positives_for_style_redundant_condition.md diff --git a/changelog/fix_false_positives_for_style_redundant_condition.md b/changelog/fix_false_positives_for_style_redundant_condition.md new file mode 100644 index 000000000000..db142df2f86f --- /dev/null +++ b/changelog/fix_false_positives_for_style_redundant_condition.md @@ -0,0 +1 @@ +* [#13913](https://github.com/rubocop/rubocop/issues/13913): Fix false positives for `Style/RedundantCondition` when using when true is used as the true branch and the condition is not a predicate method. ([@koic][]) diff --git a/config/default.yml b/config/default.yml index f95515ff9441..05eaee698301 100644 --- a/config/default.yml +++ b/config/default.yml @@ -5108,6 +5108,9 @@ Style/RedundantCondition: Description: 'Checks for unnecessary conditional expressions.' Enabled: true VersionAdded: '0.76' + VersionChanged: '<>' + AllowedMethods: + - nonzero? Style/RedundantConditional: Description: "Don't return true/false from a conditional." diff --git a/lib/rubocop/cop/style/redundant_condition.rb b/lib/rubocop/cop/style/redundant_condition.rb index 6bb7463c074b..c6f7264910ac 100644 --- a/lib/rubocop/cop/style/redundant_condition.rb +++ b/lib/rubocop/cop/style/redundant_condition.rb @@ -58,7 +58,12 @@ module Style # # good # a.nil? || a # + # @example AllowedMethods: ['nonzero?'] (default) + # # good + # num.nonzero? ? true : false + # class RedundantCondition < Base + include AllowedMethods include CommentsHelp include RangeHelp extend AutoCorrector @@ -172,11 +177,18 @@ def synonymous_condition_and_branch?(node) !use_hash_key_access?(if_branch) end + # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity def if_branch_is_true_type_and_else_is_not?(node) return false unless node.ternary? || node.if? + cond = node.condition + if cond.call_type? && (!cond.predicate_method? || allowed_method?(cond.method_name)) + return false + end + node.if_branch&.true_type? && node.else_branch && !node.else_branch.true_type? end + # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity def branches_have_assignment?(node) _condition, if_branch, else_branch = *node # rubocop:disable InternalAffairs/NodeDestructuring diff --git a/spec/rubocop/cop/style/redundant_condition_spec.rb b/spec/rubocop/cop/style/redundant_condition_spec.rb index b5d3df85328f..ec4fa21482fc 100644 --- a/spec/rubocop/cop/style/redundant_condition_spec.rb +++ b/spec/rubocop/cop/style/redundant_condition_spec.rb @@ -571,6 +571,16 @@ def do_something(foo, ...) end context 'when `true` as the true branch' do + it 'does not register an offense when true is used as the true branch and the condition is not a predicate method' do + expect_no_offenses(<<~RUBY) + if a[:key] + true + else + a + end + RUBY + end + it 'registers an offense and autocorrects when true is used as the true branch' do expect_offense(<<~RUBY) if a.zero? @@ -586,6 +596,31 @@ def do_something(foo, ...) RUBY end + it 'registers an offense and autocorrects when true is used as the true branch and the condition uses safe navigation' do + expect_offense(<<~RUBY) + if a&.zero? + ^^^^^^^^^^^ Use double pipes `||` instead. + true + else + a + end + RUBY + + expect_correction(<<~RUBY) + a&.zero? || a + RUBY + end + + it 'does not register an offense when false is used as the else branch and the condition is not a predicate method' do + expect_no_offenses(<<~RUBY) + if !a[:key] + a + else + false + end + RUBY + end + it 'registers an offense and autocorrects when true is used as the true branch and the false branch is a string' do expect_offense(<<~RUBY) if b.nil? @@ -1042,4 +1077,18 @@ def do_something(foo, ...) end end end + + context 'when `AllowedMethods: nonzero?`' do + let(:cop_config) { { 'AllowedMethods' => ['nonzero?'] } } + + it 'does not register an offense when using `nonzero?`' do + expect_no_offenses(<<~RUBY) + if a.nonzero? + true + else + false + end + RUBY + end + end end From a15904c16cb6bdd221a29d57e4e54e56b6805cf6 Mon Sep 17 00:00:00 2001 From: Bozhidar Batsov Date: Thu, 27 Feb 2025 10:58:42 +0200 Subject: [PATCH 08/26] Add default labels to our issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 1 + .github/ISSUE_TEMPLATE/feature_request.md | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index b803099a391a..334a244d9010 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,6 +1,7 @@ --- name: Bug Report about: Report an issue with RuboCop you've discovered. +labels: [bug] --- *Be clear, concise and precise in your description of the problem. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 961828ae28e5..221ed5f73da8 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,6 +1,7 @@ --- name: Feature Request about: Suggest new RuboCop features or improvements to existing features. +labels: [feature request] --- ## Is your feature request related to a problem? Please describe. From 1ec4ca566e3c32a383e0c237d9387a6c7821569a Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Thu, 27 Feb 2025 19:01:20 +0900 Subject: [PATCH 09/26] [Fix #13920] Fix an error for `Lint/MixedCaseRange` This PR fixes an error for `Lint/MixedCaseRange` when `/[[ ]]/` is used. Fixes #13920. --- changelog/fix_an_error_for_lint_mixed_case_range_cop.md | 1 + lib/rubocop/cop/lint/mixed_case_range.rb | 2 +- spec/rubocop/cop/lint/mixed_case_range_spec.rb | 6 ++++++ 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 changelog/fix_an_error_for_lint_mixed_case_range_cop.md diff --git a/changelog/fix_an_error_for_lint_mixed_case_range_cop.md b/changelog/fix_an_error_for_lint_mixed_case_range_cop.md new file mode 100644 index 000000000000..e1c35086c784 --- /dev/null +++ b/changelog/fix_an_error_for_lint_mixed_case_range_cop.md @@ -0,0 +1 @@ +* [#13920](https://github.com/rubocop/rubocop/issues/13920): Fix an error for `Lint/MixedCaseRange` when `/[[ ]]/` is used. ([@koic][]) diff --git a/lib/rubocop/cop/lint/mixed_case_range.rb b/lib/rubocop/cop/lint/mixed_case_range.rb index 3a54e2fd5de0..38e121ab1650 100644 --- a/lib/rubocop/cop/lint/mixed_case_range.rb +++ b/lib/rubocop/cop/lint/mixed_case_range.rb @@ -94,7 +94,7 @@ def skip_expression?(expr) def skip_range?(range_start, range_end) [range_start, range_end].any? do |bound| - bound.type != :literal + bound&.type != :literal end end diff --git a/spec/rubocop/cop/lint/mixed_case_range_spec.rb b/spec/rubocop/cop/lint/mixed_case_range_spec.rb index 08cff437709c..e05394a7ef58 100644 --- a/spec/rubocop/cop/lint/mixed_case_range_spec.rb +++ b/spec/rubocop/cop/lint/mixed_case_range_spec.rb @@ -227,6 +227,12 @@ RUBY end + it 'does not register an offense with `/[[ ]]/' do + expect_no_offenses(<<~RUBY) + /[[ ]]/ + RUBY + end + it 'does not register an offense when a character between `Z` and `a` is at the start of range.' do expect_no_offenses(<<~RUBY) foo = /[_-a]/ From ec2508892eb732ec1dba13f6d4d10c45763abf3d Mon Sep 17 00:00:00 2001 From: Zopolis4 Date: Thu, 27 Feb 2025 08:26:06 +1100 Subject: [PATCH 10/26] [Fix #13916] Stop Lint/LiteralAsCondition from acting on the rhs of && statements --- ..._condition_autocorrecting_rhs_and_nodes.md | 1 + lib/rubocop/cop/lint/literal_as_condition.rb | 25 ++++------- .../cop/lint/literal_as_condition_spec.rb | 44 +++++++++++-------- 3 files changed, 35 insertions(+), 35 deletions(-) create mode 100644 changelog/fix_lint_literal_as_condition_autocorrecting_rhs_and_nodes.md diff --git a/changelog/fix_lint_literal_as_condition_autocorrecting_rhs_and_nodes.md b/changelog/fix_lint_literal_as_condition_autocorrecting_rhs_and_nodes.md new file mode 100644 index 000000000000..e2147d8fdaee --- /dev/null +++ b/changelog/fix_lint_literal_as_condition_autocorrecting_rhs_and_nodes.md @@ -0,0 +1 @@ +* [#13916](https://github.com/rubocop/rubocop/issues/13916): Fix `Lint/LiteralAsCondition` acting on the right hand side of && nodes. ([@zopolis4][]) diff --git a/lib/rubocop/cop/lint/literal_as_condition.rb b/lib/rubocop/cop/lint/literal_as_condition.rb index 1344798053d8..74f5ce7ebd73 100644 --- a/lib/rubocop/cop/lint/literal_as_condition.rb +++ b/lib/rubocop/cop/lint/literal_as_condition.rb @@ -18,12 +18,15 @@ module Lint # end # # # bad - # if some_var && true + # # We're only interested in the left hand side being a truthy literal, + # # because it affects the evaluation of the &&, whereas the right hand + # # side will be conditionally executed/called and can be a literal. + # if true && some_var # do_something # end # # # good - # if some_var && some_condition + # if some_var # do_something # end # @@ -39,23 +42,13 @@ class LiteralAsCondition < Base MSG = 'Literal `%s` appeared as a condition.' RESTRICT_ON_SEND = [:!].freeze - # rubocop:disable Metrics/AbcSize def on_and(node) - if node.lhs.truthy_literal? && node.rhs.truthy_literal? - add_offense(node) do |corrector| - corrector.replace(node, 'true') - end - elsif node.lhs.truthy_literal? - add_offense(node.lhs) do |corrector| - corrector.replace(node, node.rhs.source) - end - elsif node.rhs.truthy_literal? - add_offense(node.rhs) do |corrector| - corrector.replace(node, node.lhs.source) - end + return unless node.lhs.truthy_literal? + + add_offense(node.lhs) do |corrector| + corrector.replace(node, node.rhs.source) end end - # rubocop:enable Metrics/AbcSize def on_if(node) cond = condition(node) diff --git a/spec/rubocop/cop/lint/literal_as_condition_spec.rb b/spec/rubocop/cop/lint/literal_as_condition_spec.rb index 1869221d37d8..e30da217241c 100644 --- a/spec/rubocop/cop/lint/literal_as_condition_spec.rb +++ b/spec/rubocop/cop/lint/literal_as_condition_spec.rb @@ -177,10 +177,10 @@ end end - it "registers an offense for truthy literal #{lit} in &&" do + it "registers an offense for truthy literal #{lit} on the lhs of &&" do expect_offense(<<~RUBY, lit: lit) - if x && %{lit} - ^{lit} Literal `#{lit}` appeared as a condition. + if %{lit} && x + ^{lit} Literal `#{lit}` appeared as a condition. top end RUBY @@ -192,47 +192,53 @@ RUBY end - it "registers an offense for truthy literal #{lit} in complex cond" do + it "registers an offense for truthy literal #{lit} on the lhs of && with a truthy literal rhs" do expect_offense(<<~RUBY, lit: lit) - if x && !(a && %{lit}) && y && z - ^{lit} Literal `#{lit}` appeared as a condition. + if %{lit} && true + ^{lit} Literal `#{lit}` appeared as a condition. top end RUBY expect_correction(<<~RUBY) - if x && !(a) && y && z - top - end + top RUBY end - it "registers an offense for literal #{lit} in !" do - expect_offense(<<~RUBY, lit: lit) - if !%{lit} - ^{lit} Literal `#{lit}` appeared as a condition. + it "does not register an offense for truthy literal #{lit} on the rhs of &&" do + expect_no_offenses(<<~RUBY) + if x && %{lit} top end RUBY - - expect_no_corrections end - it "registers an offense for truthy literal #{lit} in complex !" do + it "registers an offense for truthy literal #{lit} in complex cond" do expect_offense(<<~RUBY, lit: lit) - if !(x && (y && %{lit})) - ^{lit} Literal `#{lit}` appeared as a condition. + if x && !(%{lit} && a) && y && z + ^{lit} Literal `#{lit}` appeared as a condition. top end RUBY expect_correction(<<~RUBY) - if !(x && (y)) + if x && !(a) && y && z top end RUBY end + it "registers an offense for literal #{lit} in !" do + expect_offense(<<~RUBY, lit: lit) + if !%{lit} + ^{lit} Literal `#{lit}` appeared as a condition. + top + end + RUBY + + expect_no_corrections + end + it "accepts literal #{lit} if it's not an and/or operand" do expect_no_offenses(<<~RUBY) if test(#{lit}) From 9909e5ed9f9d5d4a5131abcbaeafdb4efbb65eb9 Mon Sep 17 00:00:00 2001 From: Bozhidar Batsov Date: Thu, 27 Feb 2025 12:20:53 +0200 Subject: [PATCH 11/26] Update Changelog --- CHANGELOG.md | 9 +++++++++ changelog/fix_an_error_for_lint_mixed_case_range_cop.md | 1 - changelog/fix_autocorrect_empty_cond_single_branch.md | 1 - .../fix_false_positives_for_style_redundant_condition.md | 1 - changelog/fix_fix_false_positive_with_20250226171903.md | 1 - ...erics_type_of_rbs_inline_annotation_20250226233214.md | 1 - ..._literal_as_condition_autocorrecting_rhs_and_nodes.md | 1 - 7 files changed, 9 insertions(+), 6 deletions(-) delete mode 100644 changelog/fix_an_error_for_lint_mixed_case_range_cop.md delete mode 100644 changelog/fix_autocorrect_empty_cond_single_branch.md delete mode 100644 changelog/fix_false_positives_for_style_redundant_condition.md delete mode 100644 changelog/fix_fix_false_positive_with_20250226171903.md delete mode 100644 changelog/fix_fix_writing_generics_type_of_rbs_inline_annotation_20250226233214.md delete mode 100644 changelog/fix_lint_literal_as_condition_autocorrecting_rhs_and_nodes.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cab487aaac9..d3873751c92e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,15 @@ ## master (unreleased) +### Bug fixes + +* [#13920](https://github.com/rubocop/rubocop/issues/13920): Fix an error for `Lint/MixedCaseRange` when `/[[ ]]/` is used. ([@koic][]) +* [#13912](https://github.com/rubocop/rubocop/pull/13912): Fix wrong autocorrect for `Lint/EmptyConditionalBody` when assigning to a variable with only a single branch. ([@earlopain][]) +* [#13913](https://github.com/rubocop/rubocop/issues/13913): Fix false positives for `Style/RedundantCondition` when using when true is used as the true branch and the condition is not a predicate method. ([@koic][]) +* [#13909](https://github.com/rubocop/rubocop/issues/13909): Fix false positive with `Layout/ClosingParenthesisIndentation` when first parameter is a hash. ([@tejasbubane][]) +* [#13915](https://github.com/rubocop/rubocop/pull/13915): Fix writing generics type of rbs-inline annotation for nested class in `Style/CommentedKeyword`. ([@dak2][]) +* [#13916](https://github.com/rubocop/rubocop/issues/13916): Fix `Lint/LiteralAsCondition` acting on the right hand side of && nodes. ([@zopolis4][]) + ## 1.73.0 (2025-02-26) ### New features diff --git a/changelog/fix_an_error_for_lint_mixed_case_range_cop.md b/changelog/fix_an_error_for_lint_mixed_case_range_cop.md deleted file mode 100644 index e1c35086c784..000000000000 --- a/changelog/fix_an_error_for_lint_mixed_case_range_cop.md +++ /dev/null @@ -1 +0,0 @@ -* [#13920](https://github.com/rubocop/rubocop/issues/13920): Fix an error for `Lint/MixedCaseRange` when `/[[ ]]/` is used. ([@koic][]) diff --git a/changelog/fix_autocorrect_empty_cond_single_branch.md b/changelog/fix_autocorrect_empty_cond_single_branch.md deleted file mode 100644 index 563cc25a7949..000000000000 --- a/changelog/fix_autocorrect_empty_cond_single_branch.md +++ /dev/null @@ -1 +0,0 @@ -* [#13912](https://github.com/rubocop/rubocop/pull/13912): Fix wrong autocorrect for `Lint/EmptyConditionalBody` when assigning to a variable with only a single branch. ([@earlopain][]) diff --git a/changelog/fix_false_positives_for_style_redundant_condition.md b/changelog/fix_false_positives_for_style_redundant_condition.md deleted file mode 100644 index db142df2f86f..000000000000 --- a/changelog/fix_false_positives_for_style_redundant_condition.md +++ /dev/null @@ -1 +0,0 @@ -* [#13913](https://github.com/rubocop/rubocop/issues/13913): Fix false positives for `Style/RedundantCondition` when using when true is used as the true branch and the condition is not a predicate method. ([@koic][]) diff --git a/changelog/fix_fix_false_positive_with_20250226171903.md b/changelog/fix_fix_false_positive_with_20250226171903.md deleted file mode 100644 index ddc3a186e05d..000000000000 --- a/changelog/fix_fix_false_positive_with_20250226171903.md +++ /dev/null @@ -1 +0,0 @@ -* [#13909](https://github.com/rubocop/rubocop/issues/13909): Fix false positive with `Layout/ClosingParenthesisIndentation` when first parameter is a hash. ([@tejasbubane][]) diff --git a/changelog/fix_fix_writing_generics_type_of_rbs_inline_annotation_20250226233214.md b/changelog/fix_fix_writing_generics_type_of_rbs_inline_annotation_20250226233214.md deleted file mode 100644 index f18c6517c6db..000000000000 --- a/changelog/fix_fix_writing_generics_type_of_rbs_inline_annotation_20250226233214.md +++ /dev/null @@ -1 +0,0 @@ -* [#13915](https://github.com/rubocop/rubocop/pull/13915): Fix writing generics type of rbs-inline annotation for nested class in `Style/CommentedKeyword`. ([@dak2][]) diff --git a/changelog/fix_lint_literal_as_condition_autocorrecting_rhs_and_nodes.md b/changelog/fix_lint_literal_as_condition_autocorrecting_rhs_and_nodes.md deleted file mode 100644 index e2147d8fdaee..000000000000 --- a/changelog/fix_lint_literal_as_condition_autocorrecting_rhs_and_nodes.md +++ /dev/null @@ -1 +0,0 @@ -* [#13916](https://github.com/rubocop/rubocop/issues/13916): Fix `Lint/LiteralAsCondition` acting on the right hand side of && nodes. ([@zopolis4][]) From 75737eea72a70383317ec522c13034b9de8b2842 Mon Sep 17 00:00:00 2001 From: Bozhidar Batsov Date: Thu, 27 Feb 2025 12:21:36 +0200 Subject: [PATCH 12/26] Cut 1.73.1 --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- CHANGELOG.md | 2 ++ CONTRIBUTING.md | 2 +- config/default.yml | 2 +- docs/antora.yml | 2 +- docs/modules/ROOT/pages/cops_lint.adoc | 7 ++++-- docs/modules/ROOT/pages/cops_style.adoc | 22 ++++++++++++++++++- .../pages/integration_with_other_tools.adoc | 4 ++-- lib/rubocop/version.rb | 2 +- relnotes/v1.73.1.md | 14 ++++++++++++ 10 files changed, 49 insertions(+), 10 deletions(-) create mode 100644 relnotes/v1.73.1.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 334a244d9010..d7ae265f2385 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.73.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] +1.73.1 (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 d3873751c92e..b7500b09432f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ ## master (unreleased) +## 1.73.1 (2025-02-27) + ### Bug fixes * [#13920](https://github.com/rubocop/rubocop/issues/13920): Fix an error for `Lint/MixedCaseRange` when `/[[ ]]/` is used. ([@koic][]) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 88dea0850803..e25b83841d72 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,7 +17,7 @@ do so. ```console $ rubocop -V -1.73.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] +1.73.1 (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/config/default.yml b/config/default.yml index 05eaee698301..51da11eb5d1e 100644 --- a/config/default.yml +++ b/config/default.yml @@ -5108,7 +5108,7 @@ Style/RedundantCondition: Description: 'Checks for unnecessary conditional expressions.' Enabled: true VersionAdded: '0.76' - VersionChanged: '<>' + VersionChanged: '1.73' AllowedMethods: - nonzero? diff --git a/docs/antora.yml b/docs/antora.yml index 9c897ea03d1d..0ec0cdbc5d57 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.73' nav: - modules/ROOT/nav.adoc diff --git a/docs/modules/ROOT/pages/cops_lint.adoc b/docs/modules/ROOT/pages/cops_lint.adoc index e2c6281202ed..2350bec8c29a 100644 --- a/docs/modules/ROOT/pages/cops_lint.adoc +++ b/docs/modules/ROOT/pages/cops_lint.adoc @@ -3494,12 +3494,15 @@ if 20 end # bad -if some_var && true +# We're only interested in the left hand side being a truthy literal, +# because it affects the evaluation of the &&, whereas the right hand +# side will be conditionally executed/called and can be a literal. +if true && some_var do_something end # good -if some_var && some_condition +if some_var do_something end diff --git a/docs/modules/ROOT/pages/cops_style.adoc b/docs/modules/ROOT/pages/cops_style.adoc index 7f4364e23e34..6abb5be0741d 100644 --- a/docs/modules/ROOT/pages/cops_style.adoc +++ b/docs/modules/ROOT/pages/cops_style.adoc @@ -12838,7 +12838,7 @@ Checks for usage of the %W() syntax when %w() would do. | Yes | Always | 0.76 -| - +| 1.73 |=== Checks for unnecessary conditional expressions. @@ -12901,6 +12901,26 @@ end a.nil? || a ---- +[#allowedmethods_-__nonzero___-_default_-styleredundantcondition] +==== AllowedMethods: ['nonzero?'] (default) + +[source,ruby] +---- +# good +num.nonzero? ? true : false +---- + +[#configurable-attributes-styleredundantcondition] +=== Configurable attributes + +|=== +| Name | Default value | Configurable values + +| AllowedMethods +| `nonzero?` +| Array +|=== + [#styleredundantconditional] == Style/RedundantConditional diff --git a/docs/modules/ROOT/pages/integration_with_other_tools.adoc b/docs/modules/ROOT/pages/integration_with_other_tools.adoc index 8b360d13cf43..3edd20112dc2 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.73.0 + rev: v1.73.1 hooks: - id: rubocop ---- @@ -136,7 +136,7 @@ entries in `additional_dependencies`: [source,yaml] ---- - repo: https://github.com/rubocop/rubocop - rev: v1.73.0 + rev: v1.73.1 hooks: - id: rubocop additional_dependencies: diff --git a/lib/rubocop/version.rb b/lib/rubocop/version.rb index d8ea91610df8..e4da2ba844cc 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.73.0' + STRING = '1.73.1' MSG = '%s (using %s, ' \ 'rubocop-ast %s, ' \ diff --git a/relnotes/v1.73.1.md b/relnotes/v1.73.1.md new file mode 100644 index 000000000000..63933e355b16 --- /dev/null +++ b/relnotes/v1.73.1.md @@ -0,0 +1,14 @@ +### Bug fixes + +* [#13920](https://github.com/rubocop/rubocop/issues/13920): Fix an error for `Lint/MixedCaseRange` when `/[[ ]]/` is used. ([@koic][]) +* [#13912](https://github.com/rubocop/rubocop/pull/13912): Fix wrong autocorrect for `Lint/EmptyConditionalBody` when assigning to a variable with only a single branch. ([@earlopain][]) +* [#13913](https://github.com/rubocop/rubocop/issues/13913): Fix false positives for `Style/RedundantCondition` when using when true is used as the true branch and the condition is not a predicate method. ([@koic][]) +* [#13909](https://github.com/rubocop/rubocop/issues/13909): Fix false positive with `Layout/ClosingParenthesisIndentation` when first parameter is a hash. ([@tejasbubane][]) +* [#13915](https://github.com/rubocop/rubocop/pull/13915): Fix writing generics type of rbs-inline annotation for nested class in `Style/CommentedKeyword`. ([@dak2][]) +* [#13916](https://github.com/rubocop/rubocop/issues/13916): Fix `Lint/LiteralAsCondition` acting on the right hand side of && nodes. ([@zopolis4][]) + +[@koic]: https://github.com/koic +[@earlopain]: https://github.com/earlopain +[@tejasbubane]: https://github.com/tejasbubane +[@dak2]: https://github.com/dak2 +[@zopolis4]: https://github.com/zopolis4 From 8c2766ec0542f34567ef67859935586e945a82ec Mon Sep 17 00:00:00 2001 From: Bozhidar Batsov Date: Thu, 27 Feb 2025 12:23:23 +0200 Subject: [PATCH 13/26] 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 0ec0cdbc5d57..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.73' +version: ~ nav: - modules/ROOT/nav.adoc From 548a3410a476d62b962fc4685f8181bcf7d11dfb Mon Sep 17 00:00:00 2001 From: Daniel Vandersluis Date: Thu, 27 Feb 2025 11:49:29 -0500 Subject: [PATCH 14/26] [Fix #12669] Remove unsafe autocorrection for `Lint/EmptyConditionalBody` In #10862, autocorrection was added to remove branches that had no body. This was made unsafe in #10867, because deleting a branch introduces a behaviour change if any of the branch conditions had side effects. As a `Lint` cop, it'd be better to just not correct when unsafe. The user can decide whether the branch can be removed, or an explicit `nil` can be added. --- ...pdate_autocorrection_for_20250227114940.md | 1 + config/default.yml | 3 +- .../cop/lint/empty_conditional_body.rb | 85 ++-------- spec/rubocop/cli_spec.rb | 3 +- .../cop/lint/empty_conditional_body_spec.rb | 157 +++++++++--------- 5 files changed, 101 insertions(+), 148 deletions(-) create mode 100644 changelog/change_update_autocorrection_for_20250227114940.md diff --git a/changelog/change_update_autocorrection_for_20250227114940.md b/changelog/change_update_autocorrection_for_20250227114940.md new file mode 100644 index 000000000000..98118cba5ce4 --- /dev/null +++ b/changelog/change_update_autocorrection_for_20250227114940.md @@ -0,0 +1 @@ +* [#12669](https://github.com/rubocop/rubocop/issues/12669): Update autocorrection for `Lint/EmptyConditionalBody` to be safe. ([@dvandersluis][]) diff --git a/config/default.yml b/config/default.yml index 51da11eb5d1e..e4dbfb976468 100644 --- a/config/default.yml +++ b/config/default.yml @@ -1887,10 +1887,9 @@ Lint/EmptyConditionalBody: Description: 'Checks for the presence of `if`, `elsif` and `unless` branches without a body.' Enabled: true AutoCorrect: contextual - SafeAutoCorrect: false AllowComments: true VersionAdded: '0.89' - VersionChanged: '1.61' + VersionChanged: '<>' Lint/EmptyEnsure: Description: 'Checks for empty ensure block.' diff --git a/lib/rubocop/cop/lint/empty_conditional_body.rb b/lib/rubocop/cop/lint/empty_conditional_body.rb index 5bcafceb13d9..deca3e118f4d 100644 --- a/lib/rubocop/cop/lint/empty_conditional_body.rb +++ b/lib/rubocop/cop/lint/empty_conditional_body.rb @@ -7,11 +7,6 @@ module Lint # # NOTE: empty `else` branches are handled by `Style/EmptyElse`. # - # @safety - # Autocorrection for this cop is not safe. The conditions for empty branches that - # the autocorrection removes may have side effects, or the logic in subsequent - # branches may change due to the removal of a previous condition. - # # @example # # bad # if condition @@ -41,6 +36,13 @@ module Lint # if condition # do_something # elsif other_condition + # nil + # end + # + # # good + # if condition + # do_something + # elsif other_condition # do_something_else # end # @@ -63,7 +65,6 @@ module Lint class EmptyConditionalBody < Base extend AutoCorrector include CommentsHelp - include RangeHelp MSG = 'Avoid `%s` branches without a body.' @@ -74,21 +75,14 @@ def on_if(node) range = offense_range(node) add_offense(range, message: format(MSG, keyword: node.keyword)) do |corrector| - autocorrect(corrector, node) if do_autocorrect?(node) + next unless can_simplify_conditional?(node) + + flip_orphaned_else(corrector, node) end end private - def do_autocorrect?(node) - # if condition; end.do_something - return false if (parent = node.parent)&.call_type? - # x = if condition; end - return false if (parent&.assignment? || parent&.operator_keyword?) && node.children.one? - - true - end - def offense_range(node) if node.loc.else node.source_range.begin.join(node.loc.else.begin) @@ -97,53 +91,23 @@ def offense_range(node) end end - def autocorrect(corrector, node) - remove_comments(corrector, node) - remove_empty_branch(corrector, node) - correct_other_branches(corrector, node) - end - - def remove_comments(corrector, node) - comments_in_range(node).each do |comment| - range = range_by_whole_lines(comment.source_range, include_final_newline: true) - corrector.remove(range) - end + def can_simplify_conditional?(node) + node.else_branch && node.loc.else.source == 'else' end - # rubocop:disable Metrics/AbcSize def remove_empty_branch(corrector, node) range = if empty_if_branch?(node) && else_branch?(node) branch_range(node) - elsif same_line?(node, else_kw_loc = node.loc.else) - node.source_range.begin.join(else_kw_loc.begin) - elsif node.parent&.loc.respond_to?(:end) && - same_line?(node, end_loc = node.parent.loc.end) - node.source_range.begin.join(end_loc.begin) else deletion_range(branch_range(node)) end corrector.remove(range) end - # rubocop:enable Metrics/AbcSize - def correct_other_branches(corrector, node) - return unless require_other_branches_correction?(node) - - if node.else_branch&.if_type? && !node.else_branch.modifier_form? - # Replace an orphaned `elsif` with `if` - corrector.replace(node.else_branch.loc.keyword, 'if') - else - # Flip orphaned `else` - corrector.replace(node.loc.else, "#{node.inverse_keyword} #{node.condition.source}") - end - end - - def require_other_branches_correction?(node) - return false unless node.if_type? && node.else? - return false if !empty_if_branch?(node) && node.elsif? - - !empty_elsif_branch?(node) + def flip_orphaned_else(corrector, node) + corrector.replace(node.loc.else, "#{node.inverse_keyword} #{node.condition.source}") + remove_empty_branch(corrector, node) end def empty_if_branch?(node) @@ -154,36 +118,17 @@ def empty_if_branch?(node) if_branch.if_type? && !if_branch.body end - def empty_elsif_branch?(node) - return false unless (else_branch = node.else_branch) - - else_branch.if_type? && !else_branch.body - end - def else_branch?(node) node.else_branch && !node.else_branch.if_type? end - # rubocop:disable Metrics/AbcSize def branch_range(node) if empty_if_branch?(node) && else_branch?(node) node.source_range.with(end_pos: node.loc.else.begin_pos) elsif node.loc.else node.source_range.with(end_pos: node.condition.source_range.end_pos) - elsif all_branches_body_missing?(node) - if_node = node.ancestors.detect(&:if?) - node.source_range.join(if_node.loc.end.end) - else - node.source_range end end - # rubocop:enable Metrics/AbcSize - - def all_branches_body_missing?(node) - return false unless node.parent&.if_type? - - node.parent.branches.compact.empty? - end def deletion_range(range) # Collect a range between the start of the `if` node and the next relevant node, diff --git a/spec/rubocop/cli_spec.rb b/spec/rubocop/cli_spec.rb index 9e571a01aa51..665eb3b74219 100644 --- a/spec/rubocop/cli_spec.rb +++ b/spec/rubocop/cli_spec.rb @@ -2285,8 +2285,7 @@ def method(foo, bar, qux, fred, arg5, f) end #{'#' * 85} create_file('example.rb', <<~RUBY) # frozen_string_literal: true - if foo and bar - end + a.foo..b.bar # Lint/AmbiguousRange offense is not safe correctable RUBY expect(cli.run(['--display-only-safe-correctable', 'example.rb'])).to eq(0) expect($stderr.string).to eq('') diff --git a/spec/rubocop/cop/lint/empty_conditional_body_spec.rb b/spec/rubocop/cop/lint/empty_conditional_body_spec.rb index 33fb286ec4cd..38f5da374747 100644 --- a/spec/rubocop/cop/lint/empty_conditional_body_spec.rb +++ b/spec/rubocop/cop/lint/empty_conditional_body_spec.rb @@ -10,7 +10,7 @@ end RUBY - expect_correction('') + expect_no_corrections end it 'registers an offense for missing `if` and `else` body' do @@ -21,7 +21,7 @@ end RUBY - expect_correction('') + expect_no_corrections end it 'registers an offense for missing `if` and `else` body with some indentation' do @@ -34,13 +34,10 @@ def foo end RUBY - expect_correction(<<~RUBY) - def foo - end - RUBY + expect_no_corrections end - it 'registers an offense for missing `if` body with present `else` body' do + it 'registers an offense and corrects for missing `if` body with present `else` body' do expect_offense(<<~RUBY) class Foo if condition @@ -123,13 +120,7 @@ class Foo end RUBY - expect_correction(<<~RUBY) - if condition - do_something1 - elsif other_condition2 - do_something2 - end - RUBY + expect_no_corrections end it 'registers an offense for missing `if` and `elsif` body' do @@ -143,11 +134,7 @@ class Foo end RUBY - expect_correction(<<~RUBY) - if other_condition2 - do_something - end - RUBY + expect_no_corrections end it 'registers an offense for missing all branches of `if` and `elsif` body' do @@ -159,7 +146,7 @@ class Foo end RUBY - expect_correction('') + expect_no_corrections end it 'registers an offense for missing all branches of `if` and multiple `elsif` body' do @@ -173,10 +160,10 @@ class Foo end RUBY - expect_correction('') + expect_no_corrections end - it 'registers an offense for missing `if` body with `else`' do + it 'registers an offense and corrects for missing `if` body with `else`' do expect_offense(<<~RUBY) if condition ^^^^^^^^^^^^ Avoid `if` branches without a body. @@ -192,7 +179,7 @@ class Foo RUBY end - it 'registers an offense for missing `unless` body with `else`' do + it 'registers an offense and corrects for missing `unless` body with `else`' do expect_offense(<<~RUBY) unless condition ^^^^^^^^^^^^^^^^ Avoid `unless` branches without a body. @@ -208,7 +195,7 @@ class Foo RUBY end - it 'registers an offense for missing `if` body with conditional `else` body' do + it 'registers an offense and corrects for missing `if` body with conditional `else` body' do expect_offense(<<~RUBY) if condition ^^^^^^^^^^^^ Avoid `if` branches without a body. @@ -224,7 +211,7 @@ class Foo RUBY end - it 'registers an offense for missing `unless` body with conditional `else` body' do + it 'registers an offense and corrects for missing `unless` body with conditional `else` body' do expect_offense(<<~RUBY) unless condition ^^^^^^^^^^^^^^^^ Avoid `unless` branches without a body. @@ -248,7 +235,7 @@ class Foo end RUBY - expect_correction('') + expect_no_corrections end it 'registers an offense for missing `if` body with `elsif`' do @@ -262,13 +249,7 @@ class Foo end RUBY - expect_correction(<<~RUBY) - if other_condition - do_something - elsif another_condition - do_something_else - end - RUBY + expect_no_corrections end it 'registers an offense for missing `elsif` body with `end` on the same line' do @@ -279,11 +260,7 @@ class Foo ^^^^^^^^^^^^^ Avoid `elsif` branches without a body. RUBY - expect_correction(<<~RUBY) - if cond_a - do_a - end - RUBY + expect_no_corrections end it 'registers an offense for missing `elsif` and `else` bodies with `end` on the same line' do @@ -294,11 +271,7 @@ class Foo ^^^^^^^^^^^^^ Avoid `elsif` branches without a body. RUBY - expect_correction(<<~RUBY) - if cond_a - do_a - else;end - RUBY + expect_no_corrections end it 'does not register an offense for missing `elsif` body with a comment' do @@ -322,13 +295,7 @@ class Foo end RUBY - expect_correction(<<~RUBY) - if condition - do_something - else - # noop - end - RUBY + expect_no_corrections end it 'does not register an offense for missing `elsif` body with an inline comment' do @@ -353,13 +320,7 @@ class Foo end RUBY - expect_correction(<<~RUBY) - if foo - do_foo - elsif bar - do_bar - end - RUBY + expect_no_corrections end it 'registers an offense for missing `unless` body' do @@ -369,7 +330,7 @@ class Foo end RUBY - expect_correction('') + expect_no_corrections end it 'registers an offense when missing `if` body and using method call for return value' do @@ -400,7 +361,7 @@ class Foo RUBY end - it 'autocorrects properly when the if is assigned to a variable' do + it 'registers an offense when the if is assigned to a variable' do expect_offense(<<~RUBY) x = if foo ^^^^^^ Avoid `if` branches without a body. @@ -409,14 +370,10 @@ class Foo end RUBY - expect_correction(<<~RUBY) - x = if bar - 5 - end - RUBY + expect_no_corrections end - it 'does not autocorrect when there is only one branch with assignment' do + it 'registers an offense when there is only one branch with assignment' do expect_offense(<<~RUBY) x = if foo ^^^^^^ Avoid `if` branches without a body. @@ -426,7 +383,7 @@ class Foo expect_no_corrections end - it 'does not autocorrect when there is only one branch after `||`' do + it 'registers an offense when there is only one branch after `||`' do expect_offense(<<~RUBY) x || if foo ^^^^^^ Avoid `if` branches without a body. @@ -436,6 +393,32 @@ class Foo expect_no_corrections end + it 'does not register an offense for an `if` with `nil` body' do + expect_no_offenses(<<~RUBY) + if condition + nil + end + RUBY + end + + it 'does not register an offense for an `unless` with `nil` body' do + expect_no_offenses(<<~RUBY) + unless condition + nil + end + RUBY + end + + it 'does not register an offense for an `elsif` with `nil` body' do + expect_no_offenses(<<~RUBY) + if condition + do_something + elsif other_condition + nil + end + RUBY + end + context '>= Ruby 3.1', :ruby31 do it 'registers an offense for multi-line value omission in `unless`' do expect_offense(<<~RUBY) @@ -451,7 +434,37 @@ class Foo end end - context 'when AllowComments is false' do + context 'when `AllowComments` is `true`' do + let(:cop_config) { { 'AllowComments' => true } } + + it 'does not register an offense for missing `if` body with a comment' do + expect_no_offenses(<<~RUBY) + if condition + # noop + end + RUBY + end + + it 'does not register an offense for missing `elsif` body with a comment' do + expect_no_offenses(<<~RUBY) + if condition + do_something + elsif other_condition + # noop + end + RUBY + end + + it 'does not register an offense for missing `unless` body with a comment' do + expect_no_offenses(<<~RUBY) + unless condition + # noop + end + RUBY + end + end + + context 'when `AllowComments` is `false`' do let(:cop_config) { { 'AllowComments' => false } } it 'registers an offense for missing `if` body with a comment' do @@ -462,7 +475,7 @@ class Foo end RUBY - expect_correction('') + expect_no_corrections end it 'registers an offense for missing `elsif` body with a comment' do @@ -475,11 +488,7 @@ class Foo end RUBY - expect_correction(<<~RUBY) - if condition - do_something - end - RUBY + expect_no_corrections end it 'registers an offense for missing `unless` body with a comment' do @@ -490,7 +499,7 @@ class Foo end RUBY - expect_correction('') + expect_no_corrections end end end From 8ee486774302bf9a49e834fefa780b893371b11c Mon Sep 17 00:00:00 2001 From: Daniel Vandersluis Date: Thu, 27 Feb 2025 12:51:29 -0500 Subject: [PATCH 15/26] Update `InternalAffairs/ExampleDescription` to handle `expect_no_corrections` with "registers an offense and corrects" description Also added `expect_correction`s to the tests where it was missing. --- .../internal_affairs/example_description.rb | 4 +- .../example_description_spec.rb | 63 +++++++++++++++++++ .../cop/lint/deprecated_constants_spec.rb | 2 +- spec/rubocop/cop/lint/ensure_return_spec.rb | 4 +- spec/rubocop/cop/lint/unified_integer_spec.rb | 2 +- spec/rubocop/cop/lint/useless_times_spec.rb | 2 +- .../access_modifier_declarations_spec.rb | 2 +- spec/rubocop/cop/style/case_equality_spec.rb | 4 +- .../cop/style/comparable_clamp_spec.rb | 4 +- .../identical_conditional_branches_spec.rb | 2 +- 10 files changed, 77 insertions(+), 12 deletions(-) diff --git a/lib/rubocop/cop/internal_affairs/example_description.rb b/lib/rubocop/cop/internal_affairs/example_description.rb index ca894f72b2f9..ad6e8b21d93b 100644 --- a/lib/rubocop/cop/internal_affairs/example_description.rb +++ b/lib/rubocop/cop/internal_affairs/example_description.rb @@ -50,10 +50,12 @@ class ExampleDescription < Base }.freeze EXPECT_NO_CORRECTIONS_DESCRIPTION_MAPPING = { - /\A(auto[- ]?)?correct/ => 'does not correct' + /\A(auto[- ]?)?corrects?/ => 'does not correct', + /\band (auto[- ]?)?corrects/ => 'but does not correct' }.freeze EXPECT_CORRECTION_DESCRIPTION_MAPPING = { + /\bbut (does not|doesn't) (auto[- ]?)?correct/ => 'and autocorrects', /\b(does not|doesn't) (auto[- ]?)?correct/ => 'autocorrects' }.freeze diff --git a/spec/rubocop/cop/internal_affairs/example_description_spec.rb b/spec/rubocop/cop/internal_affairs/example_description_spec.rb index 22a95a9d9f60..318d89839248 100644 --- a/spec/rubocop/cop/internal_affairs/example_description_spec.rb +++ b/spec/rubocop/cop/internal_affairs/example_description_spec.rb @@ -203,6 +203,12 @@ expect_correction('code', source: 'new code') end RUBY + + expect_correction(<<~RUBY) + it 'autocorrects' do + expect_correction('code', source: 'new code') + end + RUBY end context 'in conjunction with expect_offense' do @@ -214,6 +220,13 @@ expect_correction('code') end RUBY + + expect_correction(<<~RUBY) + it 'registers an offense and autocorrects' do + expect_offense('code') + expect_correction('code') + end + RUBY end context 'when the description is invalid for both methods' do @@ -225,6 +238,13 @@ expect_correction('code') end RUBY + + expect_correction(<<~RUBY) + it 'registers an offense and autocorrects' do + expect_offense('code') + expect_correction('code') + end + RUBY end end end @@ -238,6 +258,12 @@ expect_no_corrections end RUBY + + expect_correction(<<~RUBY) + it 'does not correct' do + expect_no_corrections + end + RUBY end context 'in conjunction with expect_offense' do @@ -249,8 +275,45 @@ expect_no_corrections end RUBY + + expect_correction(<<~RUBY) + it 'does not correct' do + expect_offense('code') + expect_no_corrections + end + RUBY end end + + it 'registers an offense with a "registers an offense and corrects" description' do + expect_offense(<<~RUBY) + it 'registers an offense and corrects' do + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Description does not match use of `expect_no_corrections`. + expect_no_corrections + end + RUBY + + expect_correction(<<~RUBY) + it 'registers an offense but does not correct' do + expect_no_corrections + end + RUBY + end + + it 'registers an offense with a "registers an offense and autocorrects" description' do + expect_offense(<<~RUBY) + it 'registers an offense and autocorrects' do + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Description does not match use of `expect_no_corrections`. + expect_no_corrections + end + RUBY + + expect_correction(<<~RUBY) + it 'registers an offense but does not correct' do + expect_no_corrections + end + RUBY + end end context 'when not making an expectation on offenses' do diff --git a/spec/rubocop/cop/lint/deprecated_constants_spec.rb b/spec/rubocop/cop/lint/deprecated_constants_spec.rb index 578463db7c9d..92ec7424903a 100644 --- a/spec/rubocop/cop/lint/deprecated_constants_spec.rb +++ b/spec/rubocop/cop/lint/deprecated_constants_spec.rb @@ -184,7 +184,7 @@ RUBY end - it 'registers and corrects an offense when using deprecated methods that have no alternative' do + it 'registers an offense when using deprecated methods that have no alternative' do expect_offense(<<~RUBY) Have::No::Alternative ^^^^^^^^^^^^^^^^^^^^^ Do not use `Have::No::Alternative`, deprecated since Ruby 2.4. diff --git a/spec/rubocop/cop/lint/ensure_return_spec.rb b/spec/rubocop/cop/lint/ensure_return_spec.rb index 58476d95ce35..e691aa363e71 100644 --- a/spec/rubocop/cop/lint/ensure_return_spec.rb +++ b/spec/rubocop/cop/lint/ensure_return_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true RSpec.describe RuboCop::Cop::Lint::EnsureReturn, :config do - it 'registers an offense and corrects for return in ensure' do + it 'registers an offense but does not correct for return in ensure' do expect_offense(<<~RUBY) begin something @@ -15,7 +15,7 @@ expect_no_corrections end - it 'registers an offense and corrects for return with argument in ensure' do + it 'registers an offense but does not correct for return with argument in ensure' do expect_offense(<<~RUBY) begin foo diff --git a/spec/rubocop/cop/lint/unified_integer_spec.rb b/spec/rubocop/cop/lint/unified_integer_spec.rb index b126a0eb940e..5c2376b5a081 100644 --- a/spec/rubocop/cop/lint/unified_integer_spec.rb +++ b/spec/rubocop/cop/lint/unified_integer_spec.rb @@ -5,7 +5,7 @@ context 'target ruby version < 2.4', :ruby23, unsupported_on: :prism do context "when #{klass}" do context 'without any decorations' do - it 'registers an offense and autocorrects' do + it 'registers an offense but does not correct' do expect_offense(<<~RUBY, klass: klass) 1.is_a?(%{klass}) ^{klass} Use `Integer` instead of `#{klass}`. diff --git a/spec/rubocop/cop/lint/useless_times_spec.rb b/spec/rubocop/cop/lint/useless_times_spec.rb index 0ae4118d38b1..f48d0e2ed5a9 100644 --- a/spec/rubocop/cop/lint/useless_times_spec.rb +++ b/spec/rubocop/cop/lint/useless_times_spec.rb @@ -59,7 +59,7 @@ RUBY end - it 'registers an offense and corrects with 1.times with method chain' do + it 'registers an offense but does not correct with 1.times with method chain' do expect_offense(<<~RUBY) 1.times.reverse_each do ^^^^^^^ Useless call to `1.times` detected. diff --git a/spec/rubocop/cop/style/access_modifier_declarations_spec.rb b/spec/rubocop/cop/style/access_modifier_declarations_spec.rb index 45b4db33d209..6340a4dc312e 100644 --- a/spec/rubocop/cop/style/access_modifier_declarations_spec.rb +++ b/spec/rubocop/cop/style/access_modifier_declarations_spec.rb @@ -161,7 +161,7 @@ def quux; end RUBY end - it 'registers an offense and autocorrects when not all methods are defined in class' do + it 'registers an offense but does not correct when not all methods are defined in class' do expect_offense(<<~RUBY, access_modifier: access_modifier) module Foo def bar; end diff --git a/spec/rubocop/cop/style/case_equality_spec.rb b/spec/rubocop/cop/style/case_equality_spec.rb index 39960f809b74..9cedce6a42fe 100644 --- a/spec/rubocop/cop/style/case_equality_spec.rb +++ b/spec/rubocop/cop/style/case_equality_spec.rb @@ -14,7 +14,7 @@ RUBY end - it 'registers an offense and corrects for === when the receiver is a regexp' do + it 'registers an offense but does not correct for === when the receiver is a regexp' do expect_offense(<<~RUBY) /OMG/ === var ^^^ Avoid the use of the case equality operator `===`. @@ -100,7 +100,7 @@ RUBY end - it 'registers an offense and corrects for === when the receiver is self.klass' do + it 'registers an offense but does not correct for === when the receiver is self.klass' do expect_offense(<<~RUBY) self.klass === var ^^^ Avoid the use of the case equality operator `===`. diff --git a/spec/rubocop/cop/style/comparable_clamp_spec.rb b/spec/rubocop/cop/style/comparable_clamp_spec.rb index 607627d29fbf..a9b048e4a892 100644 --- a/spec/rubocop/cop/style/comparable_clamp_spec.rb +++ b/spec/rubocop/cop/style/comparable_clamp_spec.rb @@ -227,7 +227,7 @@ expect_no_corrections end - it 'registers and corrects an offense when using `[[low, high].min].max`' do + it 'registers but does not correct an offense when using `[[low, high].min].max`' do expect_offense(<<~RUBY) [low, [x, high].min].max ^^^^^^^^^^^^^^^^^^^^^^^^ Use `Comparable#clamp` instead. @@ -236,7 +236,7 @@ expect_no_corrections end - it 'registers and corrects an offense when using `[low, [high, x].min].max`' do + it 'registers but does not correct an offense when using `[low, [high, x].min].max`' do expect_offense(<<~RUBY) [low, [high, x].min].max ^^^^^^^^^^^^^^^^^^^^^^^^ Use `Comparable#clamp` instead. diff --git a/spec/rubocop/cop/style/identical_conditional_branches_spec.rb b/spec/rubocop/cop/style/identical_conditional_branches_spec.rb index 4dfc2d9f8ef0..e56fa6afdcf7 100644 --- a/spec/rubocop/cop/style/identical_conditional_branches_spec.rb +++ b/spec/rubocop/cop/style/identical_conditional_branches_spec.rb @@ -772,7 +772,7 @@ def bar end context 'with a ternary' do - it 'registers an offense and corrects' do + it 'registers an offense but does not correct' do expect_offense(<<~RUBY) x = foo ? bar : bar ^^^ Move `bar` out of the conditional. From 59e9f3869ead573e8ea78ca905c117ae8d8e2f21 Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Fri, 28 Feb 2025 13:35:37 +0100 Subject: [PATCH 16/26] Introduce `InternalAffairs/NodeTypeGroup` I previously went through the code manually and made most changes. Now it's checked automatically (I missed one) --- config/internal_affairs.yml | 4 + lib/rubocop/cop/internal_affairs.rb | 1 + .../cop/internal_affairs/node_type_group.rb | 91 +++++++++++++ .../cop/style/multiline_block_chain.rb | 2 +- .../internal_affairs/node_type_group_spec.rb | 126 ++++++++++++++++++ 5 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 lib/rubocop/cop/internal_affairs/node_type_group.rb create mode 100644 spec/rubocop/cop/internal_affairs/node_type_group_spec.rb diff --git a/config/internal_affairs.yml b/config/internal_affairs.yml index e3d256539a10..cfff30cf0966 100644 --- a/config/internal_affairs.yml +++ b/config/internal_affairs.yml @@ -14,6 +14,10 @@ InternalAffairs/ExampleDescription: Include: - 'spec/rubocop/cop/**/*.rb' +InternalAffairs/NodeTypeGroup: + Include: + - 'lib/rubocop/cop/**/*.rb' + InternalAffairs/OnSendWithoutOnCSend: Include: - 'lib/rubocop/cop/**/*.rb' diff --git a/lib/rubocop/cop/internal_affairs.rb b/lib/rubocop/cop/internal_affairs.rb index d9d2ef54ddc4..39bf3b4eb0e9 100644 --- a/lib/rubocop/cop/internal_affairs.rb +++ b/lib/rubocop/cop/internal_affairs.rb @@ -17,6 +17,7 @@ require_relative 'internal_affairs/node_first_or_last_argument' require_relative 'internal_affairs/node_matcher_directive' require_relative 'internal_affairs/node_pattern_groups' +require_relative 'internal_affairs/node_type_group' require_relative 'internal_affairs/node_type_multiple_predicates' require_relative 'internal_affairs/node_type_predicate' require_relative 'internal_affairs/numblock_handler' diff --git a/lib/rubocop/cop/internal_affairs/node_type_group.rb b/lib/rubocop/cop/internal_affairs/node_type_group.rb new file mode 100644 index 000000000000..6fb3a3365bd2 --- /dev/null +++ b/lib/rubocop/cop/internal_affairs/node_type_group.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module InternalAffairs + # Checks that node types are checked against their group when all types of a + # group are checked. + # + # @example + # # bad + # node.type?(:irange, :erange) + # + # # good + # node.range_type? + # + # # bad + # node.type?(:irange, :erange, :send, :csend) + # + # # good + # node.type?(:range, :call) + # + class NodeTypeGroup < Base + extend AutoCorrector + include RangeHelp + + MSG = 'Use `:%s` instead of individually listing group types.' + + RESTRICT_ON_SEND = %i[type? each_ancestor each_child_node each_descendant each_node].freeze + + def on_send(node) + return unless node.receiver + + symbol_args = node.arguments.select(&:sym_type?) + return if symbol_args.none? + + NodePatternGroups::NODE_GROUPS.each do |group_name, group_types| + next unless group_satisfied?(group_types, symbol_args) + + offense_range = arguments_range(node) + add_offense(offense_range, message: format(MSG, group: group_name)) do |corrector| + autocorrect(corrector, node, symbol_args, group_name, group_types) + end + end + end + alias on_csend on_send + + private + + def arguments_range(node) + range_between( + node.first_argument.source_range.begin_pos, + node.last_argument.source_range.end_pos + ) + end + + def group_satisfied?(group_types, symbol_args) + group_types.all? { |type| symbol_args.any? { |arg| arg.value == type } } + end + + def autocorrect(corrector, node, symbol_args, group_name, group_types) + if node.method?(:type?) && node.arguments.count == group_types.count + autocorrect_to_explicit_predicate(corrector, node, group_name) + else + autocorrect_keep_method(corrector, symbol_args, group_name, group_types) + end + end + + def autocorrect_to_explicit_predicate(corrector, node, group_name) + corrector.replace(node.selector, "#{group_name}_type?") + corrector.remove(arguments_range(node)) + end + + def autocorrect_keep_method(corrector, symbol_args, group_name, group_types) + first_replaced = false + symbol_args.each do |arg| + next unless group_types.include?(arg.value) + + if first_replaced + range = range_with_surrounding_space(arg.source_range) + range = range_with_surrounding_comma(range, :left) + corrector.remove(range) + else + first_replaced = true + corrector.replace(arg, ":#{group_name}") + end + end + end + end + end + end +end diff --git a/lib/rubocop/cop/style/multiline_block_chain.rb b/lib/rubocop/cop/style/multiline_block_chain.rb index 393a8673b72a..5fb7ccccc692 100644 --- a/lib/rubocop/cop/style/multiline_block_chain.rb +++ b/lib/rubocop/cop/style/multiline_block_chain.rb @@ -28,7 +28,7 @@ class MultilineBlockChain < Base MSG = 'Avoid multi-line chains of blocks.' def on_block(node) - node.send_node.each_node(:send, :csend) do |send_node| + node.send_node.each_node(:call) do |send_node| receiver = send_node.receiver next unless receiver&.any_block_type? && receiver.multiline? diff --git a/spec/rubocop/cop/internal_affairs/node_type_group_spec.rb b/spec/rubocop/cop/internal_affairs/node_type_group_spec.rb new file mode 100644 index 000000000000..1bd61c88c6a3 --- /dev/null +++ b/spec/rubocop/cop/internal_affairs/node_type_group_spec.rb @@ -0,0 +1,126 @@ +# frozen_string_literal: true + +RSpec.describe RuboCop::Cop::InternalAffairs::NodeTypeGroup, :config do + it 'registers an offense when using `type?` with entire `numeric` group' do + expect_offense(<<~RUBY) + node.type?(:int, :float, :rational, :complex) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `:numeric` instead of individually listing group types. + RUBY + + expect_correction(<<~RUBY) + node.numeric_type?() + RUBY + end + + it 'registers an offense when using `type?` with entire `numeric` group in any order' do + expect_offense(<<~RUBY) + node.type?(:rational, :complex, :int, :float) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `:numeric` instead of individually listing group types. + RUBY + + expect_correction(<<~RUBY) + node.numeric_type?() + RUBY + end + + it 'registers an offense when using `type?` with entire `numeric` when other types are present' do + expect_offense(<<~RUBY) + node.type?(:rational, :complex, :send, :int, :float, :def) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `:numeric` instead of individually listing group types. + RUBY + + expect_correction(<<~RUBY) + node.type?(:numeric, :send, :def) + RUBY + end + + it 'registers an offense when using `type?` with multiple groups' do + expect_offense(<<~RUBY) + node.type?(:rational, :complex, :irange, :int, :float, :erange) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `:numeric` instead of individually listing group types. + RUBY + + expect_correction(<<~RUBY) + node.type?(:numeric, :range) + RUBY + end + + it 'registers an offense when using `type?` and other node types as argument' do + expect_offense(<<~RUBY) + node.type?(bar, :irange, foo, :erange) + ^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `:range` instead of individually listing group types. + RUBY + + expect_correction(<<~RUBY) + node.type?(bar, :range, foo) + RUBY + end + + it 'registers an offense for save navigation' do + expect_offense(<<~RUBY) + node&.type?(:irange, :erange) + ^^^^^^^^^^^^^^^^ Use `:range` instead of individually listing group types. + RUBY + + expect_correction(<<~RUBY) + node&.range_type?() + RUBY + end + + it 'registers an offense when chained to a method call' do + expect_offense(<<~RUBY) + node.each_child_node(:irange, :erange).any? + ^^^^^^^^^^^^^^^^ Use `:range` instead of individually listing group types. + RUBY + + expect_correction(<<~RUBY) + node.each_child_node(:range).any? + RUBY + end + + it 'registers an offense when chained to a method call with block' do + expect_offense(<<~RUBY) + node.each_child_node(:irange, :erange).any? do |node| + ^^^^^^^^^^^^^^^^ Use `:range` instead of individually listing group types. + foo?(node) + end + RUBY + + expect_correction(<<~RUBY) + node.each_child_node(:range).any? do |node| + foo?(node) + end + RUBY + end + + it 'registers no offense when the group is incomplete' do + expect_no_offenses(<<~RUBY) + node.type?(:int, :float, :complex) + RUBY + end + + it 'registers no offense with no arguments' do + expect_no_offenses(<<~RUBY) + node.type? + RUBY + end + + it 'registers no offense when there is no receiver' do + expect_no_offenses(<<~RUBY) + type?(:irange, :erange) + RUBY + end + + %i[each_ancestor each_child_node each_descendant each_node].each do |method| + it "registers an offense for #{method}" do + expect_offense(<<~RUBY, method: method) + node.#{method}(:irange, :erange) + _{method} ^^^^^^^^^^^^^^^^ Use `:range` instead of individually listing group types. + RUBY + + expect_correction(<<~RUBY) + node.#{method}(:range) + RUBY + end + end +end From 73d4abb63e841c4594f7633bcc9e52cb01555c8b Mon Sep 17 00:00:00 2001 From: Daniel Vandersluis Date: Fri, 28 Feb 2025 10:25:54 -0500 Subject: [PATCH 17/26] [Fix #13928] Fix `end pattern with unmatched parenthesis: / (RegexpError)` on Ruby 3.2.0 Ref: https://bugs.ruby-lang.org/issues/19379 --- ...d_pattern_with_unmatched_parenthesis__20250228103004.md | 1 + lib/rubocop/cop/utils/format_string.rb | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 changelog/fix_fix_end_pattern_with_unmatched_parenthesis__20250228103004.md diff --git a/changelog/fix_fix_end_pattern_with_unmatched_parenthesis__20250228103004.md b/changelog/fix_fix_end_pattern_with_unmatched_parenthesis__20250228103004.md new file mode 100644 index 000000000000..d57ed01613de --- /dev/null +++ b/changelog/fix_fix_end_pattern_with_unmatched_parenthesis__20250228103004.md @@ -0,0 +1 @@ +* [#13928](https://github.com/rubocop/rubocop/issues/13928): Fix `end pattern with unmatched parenthesis: / (RegexpError)` on Ruby 3.2.0. ([@dvandersluis][]) diff --git a/lib/rubocop/cop/utils/format_string.rb b/lib/rubocop/cop/utils/format_string.rb index decdfec60ab7..aa46e70ac002 100644 --- a/lib/rubocop/cop/utils/format_string.rb +++ b/lib/rubocop/cop/utils/format_string.rb @@ -5,8 +5,11 @@ module Cop module Utils # Parses {Kernel#sprintf} format strings. class FormatString + # Escaping the `#` in `INTERPOLATION` and `TEMPLATE_NAME` is necessary to + # avoid a bug in Ruby 3.2.0 + # See: https://bugs.ruby-lang.org/issues/19379 DIGIT_DOLLAR = /(?\d+)\$/.freeze - INTERPOLATION = /#\{.*?\}/.freeze + INTERPOLATION = /\#\{.*?\}/.freeze FLAG = /[ #0+-]|#{DIGIT_DOLLAR}/.freeze NUMBER_ARG = /\*#{DIGIT_DOLLAR}?/.freeze NUMBER = /\d+|#{NUMBER_ARG}|#{INTERPOLATION}/.freeze @@ -14,7 +17,7 @@ class FormatString PRECISION = /\.(?#{NUMBER}?)/.freeze TYPE = /(?[bBdiouxXeEfgGaAcps])/.freeze NAME = /<(?\w+)>/.freeze - TEMPLATE_NAME = /(?\w+)\}/.freeze + TEMPLATE_NAME = /(?\w+)\}/.freeze SEQUENCE = / % (?%) From 829b41239e75a22948f4875cc3ec2b454ed7206c Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Sun, 2 Mar 2025 12:31:39 +0100 Subject: [PATCH 18/26] Fix a false negative for `Style/RedundantFreeze` when calling methods that produce frozen objects with numblocks --- changelog/fix_false_positive_redundant_freeze_numblock.md | 1 + lib/rubocop/cop/style/redundant_freeze.rb | 2 +- spec/rubocop/cop/style/redundant_freeze_spec.rb | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 changelog/fix_false_positive_redundant_freeze_numblock.md diff --git a/changelog/fix_false_positive_redundant_freeze_numblock.md b/changelog/fix_false_positive_redundant_freeze_numblock.md new file mode 100644 index 000000000000..5ae378bebf05 --- /dev/null +++ b/changelog/fix_false_positive_redundant_freeze_numblock.md @@ -0,0 +1 @@ +* [#13935](https://github.com/rubocop/rubocop/pull/13935): Fix a false negative for `Style/RedundantFreeze` when calling methods that produce frozen objects with numblocks. ([@dvandersluis][]) diff --git a/lib/rubocop/cop/style/redundant_freeze.rb b/lib/rubocop/cop/style/redundant_freeze.rb index 8d5beb29c187..0503270bacbb 100644 --- a/lib/rubocop/cop/style/redundant_freeze.rb +++ b/lib/rubocop/cop/style/redundant_freeze.rb @@ -60,7 +60,7 @@ def strip_parenthesis(node) (begin (send !{(str _) array} {:+ :- :* :** :/ :%} {float int})) (begin (send _ {:== :=== :!= :<= :>= :< :>} _)) (send _ {:count :length :size} ...) - (block (send _ {:count :length :size} ...) ...) + (any_block (send _ {:count :length :size} ...) ...) } PATTERN end diff --git a/spec/rubocop/cop/style/redundant_freeze_spec.rb b/spec/rubocop/cop/style/redundant_freeze_spec.rb index e4751e7ea701..d8ef639d7bff 100644 --- a/spec/rubocop/cop/style/redundant_freeze_spec.rb +++ b/spec/rubocop/cop/style/redundant_freeze_spec.rb @@ -26,6 +26,8 @@ it_behaves_like 'immutable objects', "('a' > 'b')" it_behaves_like 'immutable objects', '(a > b)' it_behaves_like 'immutable objects', '[1, 2, 3].size' + it_behaves_like 'immutable objects', '[1, 2, 3].count { |x| bar?(x) }' + it_behaves_like 'immutable objects', '[1, 2, 3].count { bar?(_1) }' shared_examples 'mutable objects' do |o| it "allows #{o} with freeze" do From 1cdb3b77b04f54845c8ad47ff9d090a578a69028 Mon Sep 17 00:00:00 2001 From: Daniel Vandersluis Date: Mon, 3 Mar 2025 00:18:22 -0500 Subject: [PATCH 19/26] Fix changelog for #13935 --- changelog/fix_false_positive_redundant_freeze_numblock.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/fix_false_positive_redundant_freeze_numblock.md b/changelog/fix_false_positive_redundant_freeze_numblock.md index 5ae378bebf05..fb4449e863c9 100644 --- a/changelog/fix_false_positive_redundant_freeze_numblock.md +++ b/changelog/fix_false_positive_redundant_freeze_numblock.md @@ -1 +1 @@ -* [#13935](https://github.com/rubocop/rubocop/pull/13935): Fix a false negative for `Style/RedundantFreeze` when calling methods that produce frozen objects with numblocks. ([@dvandersluis][]) +* [#13935](https://github.com/rubocop/rubocop/pull/13935): Fix a false negative for `Style/RedundantFreeze` when calling methods that produce frozen objects with numblocks. ([@earlopain][]) From 9884defa7514a2b616e65fbd8f5b9718f5b49ef0 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Mon, 3 Mar 2025 13:42:54 +0900 Subject: [PATCH 20/26] Fix false positives for `Style/RedundantCondition` This PR fixes false positives for `Style/RedundantCondition` when a variable or a constant is used. This fixes false positives that were not addressed in #13919. For example, logic that transforms `variable = 42` into `variable ? true : false` should not be detected. Since it is fundamentally impossible to determine what values a variable or constant might be assigned, they are always allowed. --- ..._positive_for_style_redundant_condition.md | 1 + lib/rubocop/cop/style/redundant_condition.rb | 5 +- .../cop/style/redundant_condition_spec.rb | 52 +++++++++++++++++++ 3 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 changelog/fix_false_positive_for_style_redundant_condition.md diff --git a/changelog/fix_false_positive_for_style_redundant_condition.md b/changelog/fix_false_positive_for_style_redundant_condition.md new file mode 100644 index 000000000000..f6cb080a5bbd --- /dev/null +++ b/changelog/fix_false_positive_for_style_redundant_condition.md @@ -0,0 +1 @@ +* [#13938](https://github.com/rubocop/rubocop/pull/13938): Fix false positives for `Style/RedundantCondition` when a variable or a constant is used. ([@koic][]) diff --git a/lib/rubocop/cop/style/redundant_condition.rb b/lib/rubocop/cop/style/redundant_condition.rb index c6f7264910ac..ec82d87007c6 100644 --- a/lib/rubocop/cop/style/redundant_condition.rb +++ b/lib/rubocop/cop/style/redundant_condition.rb @@ -182,9 +182,8 @@ def if_branch_is_true_type_and_else_is_not?(node) return false unless node.ternary? || node.if? cond = node.condition - if cond.call_type? && (!cond.predicate_method? || allowed_method?(cond.method_name)) - return false - end + return false unless cond.call_type? + return false if !cond.predicate_method? || allowed_method?(cond.method_name) node.if_branch&.true_type? && node.else_branch && !node.else_branch.true_type? end diff --git a/spec/rubocop/cop/style/redundant_condition_spec.rb b/spec/rubocop/cop/style/redundant_condition_spec.rb index ec4fa21482fc..7f75dcb3bb71 100644 --- a/spec/rubocop/cop/style/redundant_condition_spec.rb +++ b/spec/rubocop/cop/style/redundant_condition_spec.rb @@ -571,6 +571,58 @@ def do_something(foo, ...) end context 'when `true` as the true branch' do + it 'does not register an offense when true is used as the true branch and the condition is a local variable' do + expect_no_offenses(<<~RUBY) + variable = do_something + + if variable + true + else + a + end + RUBY + end + + it 'does not register an offense when true is used as the true branch and the condition is an instance variable' do + expect_no_offenses(<<~RUBY) + if @variable + true + else + a + end + RUBY + end + + it 'does not register an offense when true is used as the true branch and the condition is a class variable' do + expect_no_offenses(<<~RUBY) + if @@variable + true + else + a + end + RUBY + end + + it 'does not register an offense when true is used as the true branch and the condition is a global variable' do + expect_no_offenses(<<~RUBY) + if $variable + true + else + a + end + RUBY + end + + it 'does not register an offense when true is used as the true branch and the condition is a constant' do + expect_no_offenses(<<~RUBY) + if CONST + true + else + a + end + RUBY + end + it 'does not register an offense when true is used as the true branch and the condition is not a predicate method' do expect_no_offenses(<<~RUBY) if a[:key] From f6ddcdd3d836c9d6f4a9e12815ac4c29809b6840 Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Mon, 3 Mar 2025 13:51:06 +0100 Subject: [PATCH 21/26] Move common `arguments_range` to `RangeHelp` It's used in a few places and I'm about to add a new one. Also simplify the implementation a bit (thanks dvandersluis) --- lib/rubocop/cop/lint/erb_new_arguments.rb | 6 ------ lib/rubocop/cop/mixin/range_help.rb | 12 ++++++++++++ lib/rubocop/cop/style/expand_path_arguments.rb | 9 ++------- lib/rubocop/cop/style/multiline_method_signature.rb | 10 +--------- lib/rubocop/cop/style/sole_nested_conditional.rb | 6 ------ 5 files changed, 15 insertions(+), 28 deletions(-) diff --git a/lib/rubocop/cop/lint/erb_new_arguments.rb b/lib/rubocop/cop/lint/erb_new_arguments.rb index 466aea7727b0..1130dfd8b9e8 100644 --- a/lib/rubocop/cop/lint/erb_new_arguments.rb +++ b/lib/rubocop/cop/lint/erb_new_arguments.rb @@ -156,12 +156,6 @@ def override_by_legacy_args(kwargs, node) overridden_kwargs end - - def arguments_range(node) - arguments = node.arguments - - range_between(arguments.first.source_range.begin_pos, arguments.last.source_range.end_pos) - end end end end diff --git a/lib/rubocop/cop/mixin/range_help.rb b/lib/rubocop/cop/mixin/range_help.rb index 0fd76674301c..1e447eaa46d8 100644 --- a/lib/rubocop/cop/mixin/range_help.rb +++ b/lib/rubocop/cop/mixin/range_help.rb @@ -34,6 +34,18 @@ def contents_range(node) range_between(node.loc.begin.end_pos, node.loc.end.begin_pos) end + # A range containing the first to the last argument + # of a method call or method definition. + # def foo(a, b:) + # ^^^^^ + # bar(1, 2, 3, &blk) + # ^^^^^^^^^^^^^ + # baz { |x, y:, z:| } + # ^^^^^^^^^ + def arguments_range(node) + node.first_argument.source_range.join(node.last_argument.source_range) + end + def range_between(start_pos, end_pos) Parser::Source::Range.new(processed_source.buffer, start_pos, end_pos) end diff --git a/lib/rubocop/cop/style/expand_path_arguments.rb b/lib/rubocop/cop/style/expand_path_arguments.rb index 2f36cd7bd85a..f7617f415c4e 100644 --- a/lib/rubocop/cop/style/expand_path_arguments.rb +++ b/lib/rubocop/cop/style/expand_path_arguments.rb @@ -137,11 +137,11 @@ def autocorrect_expand_path(corrector, current_path, default_dir) case depth(stripped_current_path) when 0 - range = arguments_range(current_path) + range = arguments_range(current_path.parent) corrector.replace(range, '__FILE__') when 1 - range = arguments_range(current_path) + range = arguments_range(current_path.parent) corrector.replace(range, '__dir__') else @@ -185,11 +185,6 @@ def remove_parent_method(corrector, default_dir) corrector.remove(node.loc.dot) corrector.remove(node.loc.selector) end - - def arguments_range(node) - range_between(node.parent.first_argument.source_range.begin_pos, - node.parent.last_argument.source_range.end_pos) - end end end end diff --git a/lib/rubocop/cop/style/multiline_method_signature.rb b/lib/rubocop/cop/style/multiline_method_signature.rb index b345944b33ce..523544acdc00 100644 --- a/lib/rubocop/cop/style/multiline_method_signature.rb +++ b/lib/rubocop/cop/style/multiline_method_signature.rb @@ -50,7 +50,7 @@ def autocorrect(corrector, node, begin_of_arguments) corrector.remove(range_by_whole_lines(arguments.loc.end, include_final_newline: true)) end - arguments_range = arguments_range(node) + arguments_range = range_with_surrounding_space(arguments_range(node), side: :left) # If the method name isn't on the same line as def, move it directly after def if arguments_range.first_line != opening_line(node) corrector.remove(node.loc.name) @@ -66,14 +66,6 @@ def last_line_source_of_arguments(arguments) processed_source[arguments.last_line - 1].strip end - def arguments_range(node) - range = range_between( - node.first_argument.source_range.begin_pos, node.last_argument.source_range.end_pos - ) - - range_with_surrounding_space(range, side: :left) - end - def opening_line(node) node.first_line end diff --git a/lib/rubocop/cop/style/sole_nested_conditional.rb b/lib/rubocop/cop/style/sole_nested_conditional.rb index 8018d29495aa..c7f5bacd1809 100644 --- a/lib/rubocop/cop/style/sole_nested_conditional.rb +++ b/lib/rubocop/cop/style/sole_nested_conditional.rb @@ -230,12 +230,6 @@ def require_parentheses?(condition) !condition.comparison_method? end - def arguments_range(node) - range_between( - node.first_argument.source_range.begin_pos, node.last_argument.source_range.end_pos - ) - end - def wrap_condition?(node) node.operator_keyword? || (node.call_type? && node.arguments.any? && !node.parenthesized?) end From 7e91fd91b9a34d814c853639ce4f2e6e67779f6f Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Mon, 3 Mar 2025 14:12:27 +0100 Subject: [PATCH 22/26] [Fix #13933] Fix wrong autocorrect for `Style/KeywordParametersOrder` when the arguments are on multiple lines and contain comments There is nothing sensible to do here, except to not offer autocorrect at all. I think. --- ...utocorrect_keyword_param_order_comments.md | 1 + .../cop/style/keyword_parameters_order.rb | 20 ++++++++++++------- .../style/keyword_parameters_order_spec.rb | 12 +++++++++++ 3 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 changelog/fix_wrong_autocorrect_keyword_param_order_comments.md diff --git a/changelog/fix_wrong_autocorrect_keyword_param_order_comments.md b/changelog/fix_wrong_autocorrect_keyword_param_order_comments.md new file mode 100644 index 000000000000..c5ff7305cea9 --- /dev/null +++ b/changelog/fix_wrong_autocorrect_keyword_param_order_comments.md @@ -0,0 +1 @@ +* [#13933](https://github.com/rubocop/rubocop/issues/13933): Fix wrong autocorrect for `Style/KeywordParametersOrder` when the arguments are on multiple lines and contain comments. ([@earlopain][]) diff --git a/lib/rubocop/cop/style/keyword_parameters_order.rb b/lib/rubocop/cop/style/keyword_parameters_order.rb index dfca953132fc..7f3a7cdd3ea8 100644 --- a/lib/rubocop/cop/style/keyword_parameters_order.rb +++ b/lib/rubocop/cop/style/keyword_parameters_order.rb @@ -42,19 +42,25 @@ def on_kwoptarg(node) return if kwarg_nodes.empty? add_offense(node) do |corrector| - if node.parent.find(&:kwoptarg_type?) == node - corrector.insert_before(node, "#{kwarg_nodes.map(&:source).join(', ')}, ") + defining_node = node.each_ancestor(:def, :defs, :block).first + next if processed_source.contains_comment?(arguments_range(defining_node)) + next unless node.parent.find(&:kwoptarg_type?) == node - arguments = node.each_ancestor(:def, :defs, :block).first.arguments - append_newline_to_last_kwoptarg(arguments, corrector) unless parentheses?(arguments) - - remove_kwargs(kwarg_nodes, corrector) - end + autocorrect(corrector, node, defining_node, kwarg_nodes) end end private + def autocorrect(corrector, node, defining_node, kwarg_nodes) + corrector.insert_before(node, "#{kwarg_nodes.map(&:source).join(', ')}, ") + + arguments = defining_node.arguments + append_newline_to_last_kwoptarg(arguments, corrector) unless parentheses?(arguments) + + remove_kwargs(kwarg_nodes, corrector) + end + def append_newline_to_last_kwoptarg(arguments, corrector) last_argument = arguments.last return if last_argument.type?(:kwrestarg, :blockarg) diff --git a/spec/rubocop/cop/style/keyword_parameters_order_spec.rb b/spec/rubocop/cop/style/keyword_parameters_order_spec.rb index 02e749b9bff2..7c5a1233eb88 100644 --- a/spec/rubocop/cop/style/keyword_parameters_order_spec.rb +++ b/spec/rubocop/cop/style/keyword_parameters_order_spec.rb @@ -93,6 +93,18 @@ def m arg, required1:, required2:, optional1: 1, optional2: 2, **rest, &block RUBY end + it 'registers an offense but does not autocorrect when the argument range contains comments' do + expect_offense(<<~RUBY) + def foo(optional: 123, + ^^^^^^^^^^^^^ Place optional keyword parameters at the end of the parameters list. + # Some explanation + required:) + end + RUBY + + expect_no_corrections + end + it 'does not register an offense when there are no `kwoptarg`s before `kwarg`s' do expect_no_offenses(<<~RUBY) def m(arg, required:, optional: 1) From ba9d953c13a6cd577ff41ead497c45798f4eb567 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Fri, 28 Feb 2025 14:08:44 +0900 Subject: [PATCH 23/26] [Fix #13766] Fix false positives for `Style/InverseMethods` This PR fixes false positives for `Style/InverseMethods` when using `any?` or `none?` with safe navigation operator. Additionally, some specs for the existing incorrect behavior has been updated. Fixes #13766. --- ...alse_positive_for_style_inverse_methods.md | 1 + lib/rubocop/cop/style/inverse_methods.rb | 13 +++-- .../rubocop/cop/style/inverse_methods_spec.rb | 57 +++++++++---------- 3 files changed, 36 insertions(+), 35 deletions(-) create mode 100644 changelog/fix_false_positive_for_style_inverse_methods.md diff --git a/changelog/fix_false_positive_for_style_inverse_methods.md b/changelog/fix_false_positive_for_style_inverse_methods.md new file mode 100644 index 000000000000..c7990f54c7c7 --- /dev/null +++ b/changelog/fix_false_positive_for_style_inverse_methods.md @@ -0,0 +1 @@ +* [#13766](https://github.com/rubocop/rubocop/issues/13766): Fix false positives for `Style/InverseMethods` when using `any?` or `none?` with safe navigation operator. ([@koic][]) diff --git a/lib/rubocop/cop/style/inverse_methods.rb b/lib/rubocop/cop/style/inverse_methods.rb index 3a9b8ec60138..01efa22b3be6 100644 --- a/lib/rubocop/cop/style/inverse_methods.rb +++ b/lib/rubocop/cop/style/inverse_methods.rb @@ -46,6 +46,7 @@ class InverseMethods < Base MSG = 'Use `%s` instead of inverting `%s`.' CLASS_COMPARISON_METHODS = %i[<= >= < >].freeze + SAFE_NAVIGATION_INCOMPATIBLE_METHODS = (CLASS_COMPARISON_METHODS + %i[any? none?]).freeze EQUALITY_METHODS = %i[== != =~ !~ <= >= < >].freeze NEGATED_EQUALITY_METHODS = %i[!= !~].freeze CAMEL_CASE = /[A-Z]+[a-z]+/.freeze @@ -77,7 +78,7 @@ def self.autocorrect_incompatible_with def on_send(node) inverse_candidate?(node) do |method_call, lhs, method, rhs| return unless inverse_methods.key?(method) - return if negated?(node) || relational_comparison_with_safe_navigation?(method_call) + return if negated?(node) || safe_navigation_incompatible?(method_call) return if part_of_ignored_node?(node) return if possible_class_hierarchy_check?(lhs, rhs, method) @@ -154,10 +155,6 @@ def negated?(node) node.parent.respond_to?(:method?) && node.parent.method?(:!) end - def relational_comparison_with_safe_navigation?(node) - node.csend_type? && CLASS_COMPARISON_METHODS.include?(node.method_name) - end - def not_to_receiver(node, method_call) node.loc.selector.begin.join(method_call.source_range.begin) end @@ -166,6 +163,12 @@ def end_parentheses(node, method_call) method_call.source_range.end.join(node.source_range.end) end + def safe_navigation_incompatible?(node) + return false unless node.csend_type? + + SAFE_NAVIGATION_INCOMPATIBLE_METHODS.include?(node.method_name) + end + # When comparing classes, `!(Integer < Numeric)` is not the same as # `Integer > Numeric`. def possible_class_hierarchy_check?(lhs, rhs, method) diff --git a/spec/rubocop/cop/style/inverse_methods_spec.rb b/spec/rubocop/cop/style/inverse_methods_spec.rb index d9712adfc44e..77f5b16e3180 100644 --- a/spec/rubocop/cop/style/inverse_methods_spec.rb +++ b/spec/rubocop/cop/style/inverse_methods_spec.rb @@ -33,14 +33,9 @@ RUBY end - it 'registers an offense for safe navigation calling !.none? with a symbol proc' do - expect_offense(<<~RUBY) + it 'does not register an offense for safe navigation calling !.none? with a symbol proc' do + expect_no_offenses(<<~RUBY) !foo&.none?(&:even?) - ^^^^^^^^^^^^^^^^^^^^ Use `any?` instead of inverting `none?`. - RUBY - - expect_correction(<<~RUBY) - foo&.any?(&:even?) RUBY end @@ -55,14 +50,9 @@ RUBY end - it 'registers an offense for safe navigation calling !.none? with a block' do - expect_offense(<<~RUBY) + it 'does not register an offense for safe navigation calling !.none? with a block' do + expect_no_offenses(<<~RUBY) !foo&.none? { |f| f.even? } - ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `any?` instead of inverting `none?`. - RUBY - - expect_correction(<<~RUBY) - foo&.any? { |f| f.even? } RUBY end @@ -90,14 +80,9 @@ RUBY end - it 'registers an offense for safe navigation calling !.any? inside parens' do - expect_offense(<<~RUBY) + it 'does not register an offense for safe navigation calling !.any? inside parens' do + expect_no_offenses(<<~RUBY) !(foo&.any? &:working?) - ^^^^^^^^^^^^^^^^^^^^^^^ Use `none?` instead of inverting `any?`. - RUBY - - expect_correction(<<~RUBY) - foo&.none? &:working? RUBY end @@ -168,17 +153,17 @@ def test_method odd?: :even?, blank?: :present?, exclude?: :include? }.each do |method, inverse| - it "registers an offense for !foo.#{method}" do - expect_offense(<<~RUBY, method: method) - !foo.%{method} - ^^^^^^{method} Use `#{inverse}` instead of inverting `#{method}`. - RUBY + it "registers an offense for !foo.#{method}" do + expect_offense(<<~RUBY, method: method) + !foo.%{method} + ^^^^^^{method} Use `#{inverse}` instead of inverting `#{method}`. + RUBY - expect_correction(<<~RUBY) - foo.#{inverse} - RUBY - end + expect_correction(<<~RUBY) + foo.#{inverse} + RUBY end + end { :== => :!=, :!= => :==, @@ -209,6 +194,18 @@ def test_method end end + it 'allows using `any?` method with safe navigation operator' do + expect_no_offenses(<<~RUBY) + !nullable&.any?(&:odd) + RUBY + end + + it 'allows using `none?` method with safe navigation operator' do + expect_no_offenses(<<~RUBY) + !nullable&.none?(&:odd) + RUBY + end + it 'allows comparing for relational comparison operator (`<`) with safe navigation operator' do expect_no_offenses(<<~RUBY) !nullable&.<(0) From 4ae459d28c940abc337fa01598aa386cbdd430a3 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Tue, 4 Mar 2025 14:24:24 +0900 Subject: [PATCH 24/26] Fix incorrect disabling of departments when inheriting configuration This fixes an issue where departments that were disabled in `rubocop-i18n` were unexpectedly enabled, even when they were also disabled in the user configuration. Since `override_department_setting_for_cops` is processed in `resolve_inheritance`, unnecessary calls were being made. This caused `'override_department'` to be unexpectedly set for cop's `Enabled`. It seems that part of the configuration processing changes introduced with plugin support was missed. Fixes https://github.com/rubocop/rubocop-i18n/issues/66. --- ...partments_when_inheriting_configuration.md | 1 + lib/rubocop/config_loader.rb | 1 - spec/rubocop/config_loader_spec.rb | 21 +++++++++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 changelog/fix_disabling_of_departments_when_inheriting_configuration.md diff --git a/changelog/fix_disabling_of_departments_when_inheriting_configuration.md b/changelog/fix_disabling_of_departments_when_inheriting_configuration.md new file mode 100644 index 000000000000..8f73e41568f4 --- /dev/null +++ b/changelog/fix_disabling_of_departments_when_inheriting_configuration.md @@ -0,0 +1 @@ +* [#13942](https://github.com/rubocop/rubocop/pull/13942): Fix incorrect disabling of departments when inheriting configuration. ([@koic][]) diff --git a/lib/rubocop/config_loader.rb b/lib/rubocop/config_loader.rb index 4e0ba50457ec..1aee37210318 100644 --- a/lib/rubocop/config_loader.rb +++ b/lib/rubocop/config_loader.rb @@ -63,7 +63,6 @@ def load_file(file, check: true) loaded_features = resolver.resolve_requires(path, hash) add_loaded_features(loaded_features) - resolver.override_department_setting_for_cops({}, hash) resolver.resolve_inheritance_from_gems(hash) resolver.resolve_inheritance(path, hash, file, debug?) hash.delete('inherit_from') diff --git a/spec/rubocop/config_loader_spec.rb b/spec/rubocop/config_loader_spec.rb index b90e192d06aa..f299858c46ec 100644 --- a/spec/rubocop/config_loader_spec.rb +++ b/spec/rubocop/config_loader_spec.rb @@ -804,6 +804,12 @@ it "handles EnabledByDefault: #{enabled_by_default}, " \ "DisabledByDefault: #{disabled_by_default} with disabled #{custom_dept_to_disable}" do create_file('grandparent_rubocop.yml', <<~YAML) + Layout: + Enabled: false + + Layout/EndOfLine: + Enabled: true + Naming/FileName: Enabled: pending @@ -841,6 +847,12 @@ EnabledByDefault: #{enabled_by_default} DisabledByDefault: #{disabled_by_default} + Layout: + Enabled: false + + Layout/LineLength: + Enabled: true + Style: Enabled: false @@ -870,6 +882,15 @@ def enabled?(cop) next end + # Department disabled in grandparent config. + expect(enabled?('Layout/DotPosition')).to be(false) + + # Enabled in grandparent config, disabled in user config. + expect(enabled?('Layout/EndOfLine')).to be(false) + + # Department disabled in grandparent config, cop enabled in user config. + expect(enabled?('Layout/LineLength')).to be(true) + # Department disabled in parent config, cop enabled in child. expect(enabled?('Metrics/MethodLength')).to be(true) From 398aaabde8da74ec9c2aa22b575f83738f7158c2 Mon Sep 17 00:00:00 2001 From: Bozhidar Batsov Date: Tue, 4 Mar 2025 09:32:24 +0200 Subject: [PATCH 25/26] Update Changelog --- CHANGELOG.md | 13 +++++++++++++ ...ange_update_autocorrection_for_20250227114940.md | 1 - ..._of_departments_when_inheriting_configuration.md | 1 - .../fix_false_positive_for_style_inverse_methods.md | 1 - ..._false_positive_for_style_redundant_condition.md | 1 - .../fix_false_positive_redundant_freeze_numblock.md | 1 - ...rn_with_unmatched_parenthesis__20250228103004.md | 1 - ...rong_autocorrect_keyword_param_order_comments.md | 1 - 8 files changed, 13 insertions(+), 7 deletions(-) delete mode 100644 changelog/change_update_autocorrection_for_20250227114940.md delete mode 100644 changelog/fix_disabling_of_departments_when_inheriting_configuration.md delete mode 100644 changelog/fix_false_positive_for_style_inverse_methods.md delete mode 100644 changelog/fix_false_positive_for_style_redundant_condition.md delete mode 100644 changelog/fix_false_positive_redundant_freeze_numblock.md delete mode 100644 changelog/fix_fix_end_pattern_with_unmatched_parenthesis__20250228103004.md delete mode 100644 changelog/fix_wrong_autocorrect_keyword_param_order_comments.md diff --git a/CHANGELOG.md b/CHANGELOG.md index b7500b09432f..de6061fe40b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,19 @@ ## master (unreleased) +### Bug fixes + +* [#13942](https://github.com/rubocop/rubocop/pull/13942): Fix incorrect disabling of departments when inheriting configuration. ([@koic][]) +* [#13766](https://github.com/rubocop/rubocop/issues/13766): Fix false positives for `Style/InverseMethods` when using `any?` or `none?` with safe navigation operator. ([@koic][]) +* [#13938](https://github.com/rubocop/rubocop/pull/13938): Fix false positives for `Style/RedundantCondition` when a variable or a constant is used. ([@koic][]) +* [#13935](https://github.com/rubocop/rubocop/pull/13935): Fix a false negative for `Style/RedundantFreeze` when calling methods that produce frozen objects with numblocks. ([@earlopain][]) +* [#13928](https://github.com/rubocop/rubocop/issues/13928): Fix `end pattern with unmatched parenthesis: / (RegexpError)` on Ruby 3.2.0. ([@dvandersluis][]) +* [#13933](https://github.com/rubocop/rubocop/issues/13933): Fix wrong autocorrect for `Style/KeywordParametersOrder` when the arguments are on multiple lines and contain comments. ([@earlopain][]) + +### Changes + +* [#12669](https://github.com/rubocop/rubocop/issues/12669): Update autocorrection for `Lint/EmptyConditionalBody` to be safe. ([@dvandersluis][]) + ## 1.73.1 (2025-02-27) ### Bug fixes diff --git a/changelog/change_update_autocorrection_for_20250227114940.md b/changelog/change_update_autocorrection_for_20250227114940.md deleted file mode 100644 index 98118cba5ce4..000000000000 --- a/changelog/change_update_autocorrection_for_20250227114940.md +++ /dev/null @@ -1 +0,0 @@ -* [#12669](https://github.com/rubocop/rubocop/issues/12669): Update autocorrection for `Lint/EmptyConditionalBody` to be safe. ([@dvandersluis][]) diff --git a/changelog/fix_disabling_of_departments_when_inheriting_configuration.md b/changelog/fix_disabling_of_departments_when_inheriting_configuration.md deleted file mode 100644 index 8f73e41568f4..000000000000 --- a/changelog/fix_disabling_of_departments_when_inheriting_configuration.md +++ /dev/null @@ -1 +0,0 @@ -* [#13942](https://github.com/rubocop/rubocop/pull/13942): Fix incorrect disabling of departments when inheriting configuration. ([@koic][]) diff --git a/changelog/fix_false_positive_for_style_inverse_methods.md b/changelog/fix_false_positive_for_style_inverse_methods.md deleted file mode 100644 index c7990f54c7c7..000000000000 --- a/changelog/fix_false_positive_for_style_inverse_methods.md +++ /dev/null @@ -1 +0,0 @@ -* [#13766](https://github.com/rubocop/rubocop/issues/13766): Fix false positives for `Style/InverseMethods` when using `any?` or `none?` with safe navigation operator. ([@koic][]) diff --git a/changelog/fix_false_positive_for_style_redundant_condition.md b/changelog/fix_false_positive_for_style_redundant_condition.md deleted file mode 100644 index f6cb080a5bbd..000000000000 --- a/changelog/fix_false_positive_for_style_redundant_condition.md +++ /dev/null @@ -1 +0,0 @@ -* [#13938](https://github.com/rubocop/rubocop/pull/13938): Fix false positives for `Style/RedundantCondition` when a variable or a constant is used. ([@koic][]) diff --git a/changelog/fix_false_positive_redundant_freeze_numblock.md b/changelog/fix_false_positive_redundant_freeze_numblock.md deleted file mode 100644 index fb4449e863c9..000000000000 --- a/changelog/fix_false_positive_redundant_freeze_numblock.md +++ /dev/null @@ -1 +0,0 @@ -* [#13935](https://github.com/rubocop/rubocop/pull/13935): Fix a false negative for `Style/RedundantFreeze` when calling methods that produce frozen objects with numblocks. ([@earlopain][]) diff --git a/changelog/fix_fix_end_pattern_with_unmatched_parenthesis__20250228103004.md b/changelog/fix_fix_end_pattern_with_unmatched_parenthesis__20250228103004.md deleted file mode 100644 index d57ed01613de..000000000000 --- a/changelog/fix_fix_end_pattern_with_unmatched_parenthesis__20250228103004.md +++ /dev/null @@ -1 +0,0 @@ -* [#13928](https://github.com/rubocop/rubocop/issues/13928): Fix `end pattern with unmatched parenthesis: / (RegexpError)` on Ruby 3.2.0. ([@dvandersluis][]) diff --git a/changelog/fix_wrong_autocorrect_keyword_param_order_comments.md b/changelog/fix_wrong_autocorrect_keyword_param_order_comments.md deleted file mode 100644 index c5ff7305cea9..000000000000 --- a/changelog/fix_wrong_autocorrect_keyword_param_order_comments.md +++ /dev/null @@ -1 +0,0 @@ -* [#13933](https://github.com/rubocop/rubocop/issues/13933): Fix wrong autocorrect for `Style/KeywordParametersOrder` when the arguments are on multiple lines and contain comments. ([@earlopain][]) From 815dccadbbf373075cf3d42f3fb7365502259b15 Mon Sep 17 00:00:00 2001 From: Bozhidar Batsov Date: Tue, 4 Mar 2025 09:36:39 +0200 Subject: [PATCH 26/26] Cut 1.73.2 --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- CHANGELOG.md | 2 ++ CONTRIBUTING.md | 2 +- config/default.yml | 2 +- docs/antora.yml | 2 +- docs/modules/ROOT/pages/cops_lint.adoc | 18 +++++++++--------- .../pages/integration_with_other_tools.adoc | 4 ++-- lib/rubocop/version.rb | 2 +- relnotes/v1.73.2.md | 16 ++++++++++++++++ 9 files changed, 34 insertions(+), 16 deletions(-) create mode 100644 relnotes/v1.73.2.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index d7ae265f2385..4994e6ccb565 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.73.1 (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.73.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] - rubocop-performance 1.22.1 - rubocop-rspec 3.1.0 ``` diff --git a/CHANGELOG.md b/CHANGELOG.md index de6061fe40b1..da0490f2b859 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ ## master (unreleased) +## 1.73.2 (2025-03-04) + ### Bug fixes * [#13942](https://github.com/rubocop/rubocop/pull/13942): Fix incorrect disabling of departments when inheriting configuration. ([@koic][]) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e25b83841d72..f503b2ed5239 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,7 +17,7 @@ do so. ```console $ rubocop -V -1.73.1 (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.73.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] - rubocop-performance 1.22.1 - rubocop-rspec 3.1.0 ``` diff --git a/config/default.yml b/config/default.yml index e4dbfb976468..37f4be2cb400 100644 --- a/config/default.yml +++ b/config/default.yml @@ -1889,7 +1889,7 @@ Lint/EmptyConditionalBody: AutoCorrect: contextual AllowComments: true VersionAdded: '0.89' - VersionChanged: '<>' + VersionChanged: '1.73' Lint/EmptyEnsure: Description: 'Checks for empty ensure block.' diff --git a/docs/antora.yml b/docs/antora.yml index 9c897ea03d1d..0ec0cdbc5d57 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.73' nav: - modules/ROOT/nav.adoc diff --git a/docs/modules/ROOT/pages/cops_lint.adoc b/docs/modules/ROOT/pages/cops_lint.adoc index 2350bec8c29a..f3a1aa8e9661 100644 --- a/docs/modules/ROOT/pages/cops_lint.adoc +++ b/docs/modules/ROOT/pages/cops_lint.adoc @@ -2257,22 +2257,15 @@ end | Enabled | Yes -| Command-line only (Unsafe) +| Command-line only | 0.89 -| 1.61 +| 1.73 |=== Checks for the presence of `if`, `elsif` and `unless` branches without a body. NOTE: empty `else` branches are handled by `Style/EmptyElse`. -[#safety-lintemptyconditionalbody] -=== Safety - -Autocorrection for this cop is not safe. The conditions for empty branches that -the autocorrection removes may have side effects, or the logic in subsequent -branches may change due to the removal of a previous condition. - [#examples-lintemptyconditionalbody] === Examples @@ -2302,6 +2295,13 @@ unless condition do_something end +# good +if condition + do_something +elsif other_condition + nil +end + # good if condition do_something diff --git a/docs/modules/ROOT/pages/integration_with_other_tools.adoc b/docs/modules/ROOT/pages/integration_with_other_tools.adoc index 3edd20112dc2..0270a958373a 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.73.1 + rev: v1.73.2 hooks: - id: rubocop ---- @@ -136,7 +136,7 @@ entries in `additional_dependencies`: [source,yaml] ---- - repo: https://github.com/rubocop/rubocop - rev: v1.73.1 + rev: v1.73.2 hooks: - id: rubocop additional_dependencies: diff --git a/lib/rubocop/version.rb b/lib/rubocop/version.rb index e4da2ba844cc..fa04e384ff0b 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.73.1' + STRING = '1.73.2' MSG = '%s (using %s, ' \ 'rubocop-ast %s, ' \ diff --git a/relnotes/v1.73.2.md b/relnotes/v1.73.2.md new file mode 100644 index 000000000000..e8804065d11b --- /dev/null +++ b/relnotes/v1.73.2.md @@ -0,0 +1,16 @@ +### Bug fixes + +* [#13942](https://github.com/rubocop/rubocop/pull/13942): Fix incorrect disabling of departments when inheriting configuration. ([@koic][]) +* [#13766](https://github.com/rubocop/rubocop/issues/13766): Fix false positives for `Style/InverseMethods` when using `any?` or `none?` with safe navigation operator. ([@koic][]) +* [#13938](https://github.com/rubocop/rubocop/pull/13938): Fix false positives for `Style/RedundantCondition` when a variable or a constant is used. ([@koic][]) +* [#13935](https://github.com/rubocop/rubocop/pull/13935): Fix a false negative for `Style/RedundantFreeze` when calling methods that produce frozen objects with numblocks. ([@earlopain][]) +* [#13928](https://github.com/rubocop/rubocop/issues/13928): Fix `end pattern with unmatched parenthesis: / (RegexpError)` on Ruby 3.2.0. ([@dvandersluis][]) +* [#13933](https://github.com/rubocop/rubocop/issues/13933): Fix wrong autocorrect for `Style/KeywordParametersOrder` when the arguments are on multiple lines and contain comments. ([@earlopain][]) + +### Changes + +* [#12669](https://github.com/rubocop/rubocop/issues/12669): Update autocorrection for `Lint/EmptyConditionalBody` to be safe. ([@dvandersluis][]) + +[@koic]: https://github.com/koic +[@earlopain]: https://github.com/earlopain +[@dvandersluis]: https://github.com/dvandersluis pFad - Phonifier reborn

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

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


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy