From 0ec759ce9bae02d4b3efb2f454c68a5a311e8154 Mon Sep 17 00:00:00 2001 From: Daniel Vandersluis Date: Thu, 27 Feb 2025 10:02:08 -0500 Subject: [PATCH 01/28] [Fix #12851] Add `EnforcedStyleForClasses` and `EnforcedStyleForModules` configuration options to `Style/ClassAndModuleChildren` These new options allow the cop to be configured differently for classes and modules. If they are not set, or set to nil, the `EnforceStyle` value will be used instead. --- ...ed_style_for_classes_and_20250227100212.md | 1 + config/default.yml | 17 +++- .../cop/style/class_and_module_children.rb | 36 ++++++-- .../style/class_and_module_children_spec.rb | 92 +++++++++++++++++++ 4 files changed, 138 insertions(+), 8 deletions(-) create mode 100644 changelog/change_add_enforced_style_for_classes_and_20250227100212.md diff --git a/changelog/change_add_enforced_style_for_classes_and_20250227100212.md b/changelog/change_add_enforced_style_for_classes_and_20250227100212.md new file mode 100644 index 000000000000..79426dbfee40 --- /dev/null +++ b/changelog/change_add_enforced_style_for_classes_and_20250227100212.md @@ -0,0 +1 @@ +* [#12851](https://github.com/rubocop/rubocop/issues/12851): Add `EnforcedStyleForClasses` and `EnforcedStyleForModules` configuration options to `Style/ClassAndModuleChildren`. ([@dvandersluis][]) diff --git a/config/default.yml b/config/default.yml index e4dbfb976468..6345c0c1a1d8 100644 --- a/config/default.yml +++ b/config/default.yml @@ -3499,6 +3499,7 @@ Style/ClassAndModuleChildren: SafeAutoCorrect: false Enabled: true VersionAdded: '0.19' + VersionChanged: '<>' # # Basically there are two different styles: # @@ -3514,7 +3515,21 @@ Style/ClassAndModuleChildren: # # The compact style is only forced, for classes or modules with one child. EnforcedStyle: nested - SupportedStyles: + SupportedStyles: &supported_styles + - nested + - compact + # Configure classes separately, if desired. If not set, or set to `nil`, + # the `EnforcedStyle` value will be used. + EnforcedStyleForClasses: ~ + SupportedStylesForClasses: + - ~ + - nested + - compact + # Configure modules separately, if desired. If not set, or set to `nil`, + # the `EnforcedStyle` value will be used. + EnforcedStyleForModules: ~ + SupportedStylesForModules: + - ~ - nested - compact diff --git a/lib/rubocop/cop/style/class_and_module_children.rb b/lib/rubocop/cop/style/class_and_module_children.rb index 8654ca3f0b71..757bae92238f 100644 --- a/lib/rubocop/cop/style/class_and_module_children.rb +++ b/lib/rubocop/cop/style/class_and_module_children.rb @@ -3,14 +3,26 @@ module RuboCop module Cop module Style - # Checks the style of children definitions at classes and - # modules. Basically there are two different styles: + # Checks that namespaced classes and modules are defined with a consistent style. + # + # With `nested` style, classes and modules should be defined separately (one constant + # on each line, without `::`). With `compact` style, classes and modules should be + # defined with fully qualified names (using `::` for namespaces). + # + # NOTE: The style chosen will affect `Module.nesting` for the class or module. Using + # `nested` style will result in each level being added, whereas `compact` style will + # only include the fully qualified class or module name. + # + # By default, `EnforcedStyle` applies to both classes and modules. If desired, separate + # styles can be defined for classes and modules by using `EnforcedStyleForClasses` and + # `EnforcedStyleForModules` respectively. If not set, or set to nil, the `EnforcedStyle` + # value will be used. # # @safety # Autocorrection is unsafe. # - # Moving from compact to nested children requires knowledge of whether the - # outer parent is a module or a class. Moving from nested to compact requires + # Moving from `compact` to `nested` children requires knowledge of whether the + # outer parent is a module or a class. Moving from `nested` to `compact` requires # verification that the outer parent is defined elsewhere. RuboCop does not # have the knowledge to perform either operation safely and thus requires # manual oversight. @@ -42,16 +54,18 @@ class ClassAndModuleChildren < Base def on_class(node) return if node.parent_class && style != :nested - check_style(node, node.body) + check_style(node, node.body, style_for_classes) end def on_module(node) - check_style(node, node.body) + check_style(node, node.body, style_for_modules) end private def nest_or_compact(corrector, node) + style = node.class_type? ? style_for_classes : style_for_modules + if style == :nested nest_definition(corrector, node) else @@ -141,7 +155,7 @@ def leading_spaces(node) node.source_range.source_line[/\A\s*/] end - def check_style(node, body) + def check_style(node, body, style) return if node.identifier.namespace&.cbase_type? if style == :nested @@ -183,6 +197,14 @@ def needs_compacting?(body) def compact_node_name?(node) node.identifier.source.include?('::') end + + def style_for_classes + cop_config['EnforcedStyleForClasses'] || style + end + + def style_for_modules + cop_config['EnforcedStyleForModules'] || style + end end end end diff --git a/spec/rubocop/cop/style/class_and_module_children_spec.rb b/spec/rubocop/cop/style/class_and_module_children_spec.rb index 0b726ff6deb4..f8f0d77141e9 100644 --- a/spec/rubocop/cop/style/class_and_module_children_spec.rb +++ b/spec/rubocop/cop/style/class_and_module_children_spec.rb @@ -450,4 +450,96 @@ def foo; 1; end end end end + + context 'when EnforcedStyleForClasses is set' do + let(:cop_config) do + { 'EnforcedStyle' => 'nested', 'EnforcedStyleForClasses' => enforced_style_for_classes } + end + + context 'EnforcedStyleForClasses: nil' do + let(:enforced_style_for_classes) { nil } + + it 'uses the set `EnforcedStyle` for classes' do + expect_offense(<<~RUBY) + class FooClass::BarClass + ^^^^^^^^^^^^^^^^^^ Use nested module/class definitions instead of compact style. + end + RUBY + + expect_correction(<<~RUBY) + module FooClass + class BarClass + end + end + RUBY + end + end + + context 'EnforcedStyleForClasses: compact' do + let(:enforced_style_for_classes) { 'compact' } + + it 'registers an offense for classes with nested children' do + expect_offense(<<~RUBY) + class FooClass + ^^^^^^^^ Use compact module/class definition instead of nested style. + class BarClass + end + end + RUBY + + expect_correction(<<~RUBY) + class FooClass::BarClass + end + RUBY + end + end + end + + context 'when EnforcedStyleForModules is set' do + let(:cop_config) do + { 'EnforcedStyle' => 'nested', 'EnforcedStyleForModules' => enforced_style_for_modules } + end + + context 'EnforcedStyleForModules: nil' do + let(:enforced_style_for_modules) { nil } + + it 'uses the set `EnforcedStyle` for modules' do + expect_offense(<<~RUBY) + module FooModule::BarModule + ^^^^^^^^^^^^^^^^^^^^ Use nested module/class definitions instead of compact style. + end + RUBY + + expect_correction(<<~RUBY) + module FooModule + module BarModule + end + end + RUBY + end + end + + context 'EnforcedStyleForModules: compact' do + let(:enforced_style_for_modules) { 'compact' } + + it 'registers an offense for modules with nested children' do + expect_offense(<<~RUBY) + module FooModule + ^^^^^^^^^ Use compact module/class definition instead of nested style. + module BarModule + def method_example + end + end + end + RUBY + + expect_correction(<<~RUBY) + module FooModule::BarModule + def method_example + end + end + RUBY + end + end + end end From 4b741589da7f5a8c4c6069a2118fefe74f855e49 Mon Sep 17 00:00:00 2001 From: Bozhidar Batsov Date: Tue, 4 Mar 2025 09:39:15 +0200 Subject: [PATCH 02/28] 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 23154969646433d1d670143fb36a54d87569a30c Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Tue, 4 Mar 2025 12:57:08 +0100 Subject: [PATCH 03/28] Fix a false positive for `Style/DoubleNegation` when calling `define_method` with a numblock Same as https://github.com/rubocop/rubocop/pull/10587 --- changelog/fix_double_negation_numblock.md | 1 + lib/rubocop/cop/style/double_negation.rb | 2 +- spec/rubocop/cop/style/double_negation_spec.rb | 9 +++++++++ 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 changelog/fix_double_negation_numblock.md diff --git a/changelog/fix_double_negation_numblock.md b/changelog/fix_double_negation_numblock.md new file mode 100644 index 000000000000..d0d50e8e87cc --- /dev/null +++ b/changelog/fix_double_negation_numblock.md @@ -0,0 +1 @@ +* [#13945](https://github.com/rubocop/rubocop/pull/13945): Fix a false positive for `Style/DoubleNegation` when calling `define_method`/`define_singleton_method` with a numblock. ([@earlopain][]) diff --git a/lib/rubocop/cop/style/double_negation.rb b/lib/rubocop/cop/style/double_negation.rb index 20ce65198a45..f261088d8c41 100644 --- a/lib/rubocop/cop/style/double_negation.rb +++ b/lib/rubocop/cop/style/double_negation.rb @@ -109,7 +109,7 @@ def find_def_node_from_ascendant(node) end def define_method?(node) - return false unless node.block_type? + return false unless node.any_block_type? child = node.child_nodes.first return false unless child.send_type? diff --git a/spec/rubocop/cop/style/double_negation_spec.rb b/spec/rubocop/cop/style/double_negation_spec.rb index 96920eb3c9bd..f01aa34fd5a7 100644 --- a/spec/rubocop/cop/style/double_negation_spec.rb +++ b/spec/rubocop/cop/style/double_negation_spec.rb @@ -502,6 +502,15 @@ def foo? RUBY end + it 'does not register an offense for `!!` when return location by `define_method` with numblock' do + expect_no_offenses(<<~RUBY) + define_method :foo? do + do_something(_1) + !!qux + end + RUBY + end + it 'does not register an offense for `!!` when return location by `define_singleton_method`' do expect_no_offenses(<<~RUBY) define_singleton_method :foo? do From b9aebe0ea93ab7d092c1ce99edaf83c8327151cd Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Tue, 4 Mar 2025 14:06:02 +0100 Subject: [PATCH 04/28] Fix some false positives for `Style/MethodCallWithArgsParentheses` with `EnforcedStyle: omit_parentheses` style and numblocks Some of this syntax is actually currently allowed on Ruby 3.4 with prism. See https://bugs.ruby-lang.org/issues/21168 --- changelog/fix_method_call_args_numblock.md | 1 + .../omit_parentheses.rb | 6 ++-- .../method_call_with_args_parentheses_spec.rb | 28 +++++++++++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 changelog/fix_method_call_args_numblock.md diff --git a/changelog/fix_method_call_args_numblock.md b/changelog/fix_method_call_args_numblock.md new file mode 100644 index 000000000000..b65fd41e3753 --- /dev/null +++ b/changelog/fix_method_call_args_numblock.md @@ -0,0 +1 @@ +* [#13946](https://github.com/rubocop/rubocop/pull/13946): Fix some false positives for `Style/MethodCallWithArgsParentheses` with `EnforcedStyle: omit_parentheses` style and numblocks. ([@earlopain][]) diff --git a/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb b/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb index 47ce9df4d39b..62fe95ac7f9e 100644 --- a/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +++ b/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb @@ -108,7 +108,7 @@ def legitimate_call_with_parentheses?(node) # rubocop:disable Metrics/PerceivedC end def call_in_literals?(node) - parent = node.parent&.block_type? ? node.parent.parent : node.parent + parent = node.parent&.any_block_type? ? node.parent.parent : node.parent return false unless parent parent.type?(:pair, :array, :range) || @@ -117,7 +117,7 @@ def call_in_literals?(node) end def call_in_logical_operators?(node) - parent = node.parent&.block_type? ? node.parent.parent : node.parent + parent = node.parent&.any_block_type? ? node.parent.parent : node.parent return false unless parent logical_operator?(parent) || @@ -153,7 +153,7 @@ def call_with_braced_block?(node) end def call_in_argument_with_block?(node) - parent = node.parent&.block_type? && node.parent.parent + parent = node.parent&.any_block_type? && node.parent.parent return false unless parent parent.type?(:call, :super, :yield) diff --git a/spec/rubocop/cop/style/method_call_with_args_parentheses_spec.rb b/spec/rubocop/cop/style/method_call_with_args_parentheses_spec.rb index af8aa734713f..bfd0fe0ee4e3 100644 --- a/spec/rubocop/cop/style/method_call_with_args_parentheses_spec.rb +++ b/spec/rubocop/cop/style/method_call_with_args_parentheses_spec.rb @@ -839,6 +839,16 @@ def seatle_style arg: default(42) RUBY end + it 'accepts parens in array literal calls with numblocks' do + expect_no_offenses(<<~RUBY) + [ + foo.bar.quux(:args) do + pass _1 + end, + ] + RUBY + end + it 'accepts parens in calls with logical operators' do expect_no_offenses('foo(a) && bar(b)') expect_no_offenses('foo(a) || bar(b)') @@ -849,6 +859,14 @@ def seatle_style arg: default(42) RUBY end + it 'accepts parens in calls with logical operator and numblock' do + expect_no_offenses(<<~RUBY) + foo(a) || bar(b) do + pass _1 + end + RUBY + end + it 'accepts parens in calls with args with logical operators' do expect_no_offenses('foo(a, b || c)') expect_no_offenses('foo a, b || c') @@ -1157,6 +1175,16 @@ def seatle_style arg: default(42) RUBY end + it 'accepts parens in argument calls with numblocks' do + expect_no_offenses(<<~RUBY) + foo( + bar.new(quux) do + pass _1 + end + ) + RUBY + end + it 'accepts parens in argument csend with blocks' do expect_no_offenses(<<~RUBY) foo( From d83d67b7847d12e6c31c06cf2847b28997f669b2 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Wed, 5 Mar 2025 11:35:21 +0900 Subject: [PATCH 05/28] [Doc] Remove incorrect example for `Lint/UselessConstantScoping` Removing an incorrect `good` example for `Lint/UselessConstantScoping` that does not become an expected private constant. --- lib/rubocop/cop/lint/useless_constant_scoping.rb | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/lib/rubocop/cop/lint/useless_constant_scoping.rb b/lib/rubocop/cop/lint/useless_constant_scoping.rb index 5a214c00f782..f91ed792f59e 100644 --- a/lib/rubocop/cop/lint/useless_constant_scoping.rb +++ b/lib/rubocop/cop/lint/useless_constant_scoping.rb @@ -4,8 +4,8 @@ module RuboCop module Cop module Lint # Checks for useless constant scoping. Private constants must be defined using - # `private_constant` or `class << self`. Even if `private` access modifier is used, - # it is public scope despite its appearance. + # `private_constant`. Even if `private` access modifier is used, it is public scope despite + # its appearance. # # It does not support autocorrection due to behavior change and multiple ways to fix it. # Or a public constant may be intended. @@ -26,14 +26,6 @@ module Lint # # # good # class Foo - # class << self - # private - # PRIVATE_CONST = 42 - # end - # end - # - # # good - # class Foo # PUBLIC_CONST = 42 # If private scope is not intended. # end # From f53d5809ce34ecef42c8337d0bef387e1e42ffa6 Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Wed, 5 Mar 2025 09:03:35 +0100 Subject: [PATCH 06/28] Fix a false negative for `Lint/UselessConstantScoping` for constants defined in `class << self` Followup to https://github.com/rubocop/rubocop/commit/d83d67b7847d12e6c31c06cf2847b28997f669b2 Also see https://github.com/rubocop/rubocop/pull/13831#issuecomment-2697843782 --- ...se_negative_useless_constant_scoping_sclass.md | 1 + lib/rubocop/cop/lint/useless_constant_scoping.rb | 1 - .../cop/lint/useless_constant_scoping_spec.rb | 15 ++++++++++++++- 3 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 changelog/fix_false_negative_useless_constant_scoping_sclass.md diff --git a/changelog/fix_false_negative_useless_constant_scoping_sclass.md b/changelog/fix_false_negative_useless_constant_scoping_sclass.md new file mode 100644 index 000000000000..4dedc9060a3f --- /dev/null +++ b/changelog/fix_false_negative_useless_constant_scoping_sclass.md @@ -0,0 +1 @@ +* [#13947](https://github.com/rubocop/rubocop/pull/13947): Fix a false negative for `Lint/UselessConstantScoping` for constants defined in `class << self`. ([@earlopain][]) diff --git a/lib/rubocop/cop/lint/useless_constant_scoping.rb b/lib/rubocop/cop/lint/useless_constant_scoping.rb index f91ed792f59e..078b8b5e7a21 100644 --- a/lib/rubocop/cop/lint/useless_constant_scoping.rb +++ b/lib/rubocop/cop/lint/useless_constant_scoping.rb @@ -38,7 +38,6 @@ class UselessConstantScoping < Base PATTERN def on_casgn(node) - return if node.each_ancestor(:sclass).any? return unless after_private_modifier?(node.left_siblings) return if private_constantize?(node.right_siblings, node.name) diff --git a/spec/rubocop/cop/lint/useless_constant_scoping_spec.rb b/spec/rubocop/cop/lint/useless_constant_scoping_spec.rb index 1d481bb2129b..a75d88abdfd8 100644 --- a/spec/rubocop/cop/lint/useless_constant_scoping_spec.rb +++ b/spec/rubocop/cop/lint/useless_constant_scoping_spec.rb @@ -54,12 +54,25 @@ class Foo RUBY end - it 'does not register an offense when using constant after `private` access modifier in `class << self`' do + it 'registers an offense when using constant after `private` access modifier in `class << self`' do + expect_offense(<<~RUBY) + class Foo + class << self + private + CONST = 42 + ^^^^^^^^^^ Useless `private` access modifier for constant scope. + end + end + RUBY + end + + it 'does not register an offense when using constant after `private` access modifier in `class << self` with `private_constant`' do expect_no_offenses(<<~RUBY) class Foo class << self private CONST = 42 + private_constant :CONST end end RUBY From 2a59d9322f371f2e086e38a4411ad929b16f48c8 Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Wed, 5 Mar 2025 11:01:01 +0100 Subject: [PATCH 07/28] Fix wrong autocorrect for `Style/RescueModifier` when mllhs and the right-hand-side is not a bracketed array It previously put the statement as is (`1, 2`), but that is not valid syntax. --- ...fix_wrong_autocorrect_rescue_modifier_mlhs.md | 1 + lib/rubocop/cop/style/rescue_modifier.rb | 3 +++ spec/rubocop/cop/style/rescue_modifier_spec.rb | 16 ++++++++++++++++ 3 files changed, 20 insertions(+) create mode 100644 changelog/fix_wrong_autocorrect_rescue_modifier_mlhs.md diff --git a/changelog/fix_wrong_autocorrect_rescue_modifier_mlhs.md b/changelog/fix_wrong_autocorrect_rescue_modifier_mlhs.md new file mode 100644 index 000000000000..70213deb37f6 --- /dev/null +++ b/changelog/fix_wrong_autocorrect_rescue_modifier_mlhs.md @@ -0,0 +1 @@ +* [#13948](https://github.com/rubocop/rubocop/pull/13948): Fix wrong autocorrect for `Style/RescueModifier` when using parallel assignment and the right-hand-side is not a bracketed array. ([@earlopain][]) diff --git a/lib/rubocop/cop/style/rescue_modifier.rb b/lib/rubocop/cop/style/rescue_modifier.rb index c64c28c97beb..2a586d990c4b 100644 --- a/lib/rubocop/cop/style/rescue_modifier.rb +++ b/lib/rubocop/cop/style/rescue_modifier.rb @@ -67,11 +67,13 @@ def parenthesized?(node) node.parent && parentheses?(node.parent) end + # rubocop:disable Metrics/AbcSize def correct_rescue_block(corrector, node, parenthesized) operation = node.body node_indentation, node_offset = indentation_and_offset(node, parenthesized) + corrector.wrap(operation, '[', ']') if operation.array_type? && !operation.bracketed? corrector.remove(range_between(operation.source_range.end_pos, node.source_range.end_pos)) corrector.insert_before(operation, "begin\n#{node_indentation}") corrector.insert_after(heredoc_end(operation) || operation, <<~RESCUE_CLAUSE.chop) @@ -81,6 +83,7 @@ def correct_rescue_block(corrector, node, parenthesized) #{node_offset}end RESCUE_CLAUSE end + # rubocop:enable Metrics/AbcSize def indentation_and_offset(node, parenthesized) node_indentation = indentation(node) diff --git a/spec/rubocop/cop/style/rescue_modifier_spec.rb b/spec/rubocop/cop/style/rescue_modifier_spec.rb index 965413846e55..d5cd652292fb 100644 --- a/spec/rubocop/cop/style/rescue_modifier_spec.rb +++ b/spec/rubocop/cop/style/rescue_modifier_spec.rb @@ -103,6 +103,14 @@ a, b = 1, 2 rescue nil ^^^^^^^^^^^^^^^^^^^^^^ Avoid using `rescue` in its modifier form. RUBY + + expect_correction(<<~RUBY) + begin + a, b = 1, 2 + rescue + nil + end + RUBY end it 'registers an offense for modifier rescue around parallel assignment', :ruby27 do @@ -110,6 +118,14 @@ a, b = 1, 2 rescue nil ^^^^^^^^^^^^^^^ Avoid using `rescue` in its modifier form. RUBY + + expect_correction(<<~RUBY) + a, b = begin + [1, 2] + rescue + nil + end + RUBY end it 'handles more complex expression with modifier rescue' do From 392622ec3cb7227d6d61bbc0fd896628b1f17922 Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Wed, 5 Mar 2025 14:32:17 +0100 Subject: [PATCH 08/28] Fix a false negatives for `Lint/NonLocalExitFromIterator` with numblocks `numblock.arguments?` always returns false. `argument_list` is aware of other block types and includes implicit args like `_1` --- .../fix_false_negatives_nonlocal_exit_numblocks.md | 1 + lib/rubocop/cop/lint/non_local_exit_from_iterator.rb | 4 ++-- .../cop/lint/non_local_exit_from_iterator_spec.rb | 10 ++++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 changelog/fix_false_negatives_nonlocal_exit_numblocks.md diff --git a/changelog/fix_false_negatives_nonlocal_exit_numblocks.md b/changelog/fix_false_negatives_nonlocal_exit_numblocks.md new file mode 100644 index 000000000000..c0bfb035260c --- /dev/null +++ b/changelog/fix_false_negatives_nonlocal_exit_numblocks.md @@ -0,0 +1 @@ +* [#13949](https://github.com/rubocop/rubocop/pull/13949): Fix a false negative for `Lint/NonLocalExitFromIterator` with numblocks. ([@earlopain][]) diff --git a/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb b/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb index 07c3ce041c79..08121ed8aaba 100644 --- a/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +++ b/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb @@ -46,7 +46,7 @@ class NonLocalExitFromIterator < Base def on_return(return_node) return if return_value?(return_node) - return_node.each_ancestor(:block, :def, :defs) do |node| + return_node.each_ancestor(:any_block, :def, :defs) do |node| break if scoped_node?(node) # if a proc is passed to `Module#define_method` or @@ -54,7 +54,7 @@ def on_return(return_node) # non-local exit error break if define_method?(node.send_node) - next unless node.arguments? + next if node.argument_list.empty? if chained_send?(node.send_node) add_offense(return_node.loc.keyword) diff --git a/spec/rubocop/cop/lint/non_local_exit_from_iterator_spec.rb b/spec/rubocop/cop/lint/non_local_exit_from_iterator_spec.rb index d0d5699da1a4..d9567a5c6240 100644 --- a/spec/rubocop/cop/lint/non_local_exit_from_iterator_spec.rb +++ b/spec/rubocop/cop/lint/non_local_exit_from_iterator_spec.rb @@ -12,6 +12,16 @@ end RUBY end + + it 'registers an offense for numblocks' do + expect_offense(<<~RUBY) + items.each do + return if baz?(_1) + ^^^^^^ Non-local exit from iterator, [...] + _1.update!(foobar: true) + end + RUBY + end end context 'and has multiple arguments' do From f24b7873569dda34c97924ca9d8ca48b8fdee377 Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Wed, 5 Mar 2025 18:30:31 +0100 Subject: [PATCH 09/28] [Fix #13950] Obsoletion cache can be broken The `LOAD_RULES_CACHE` uses a _shared_ array, which can cause the index to become damaged. So, although the array has been modified, stale data is returned, causing the extension to ignore that is was loaded from a gem. rehashing seems to be occasionally be performed by ruby, this demonstrates the problem: ```rb array = [] hash = {} hash[array] = "ok" puts hash[array] # => ok array << "added" # Ruby does this sometimes hash.rehash puts hash[array] # => ok ``` Also see https://docs.ruby-lang.org/en/master/Hash.html#class-Hash-label-Modifying+an+Active+Hash+Key --- changelog/fix_obsoletion_cache_broken.md | 1 + lib/rubocop/config_obsoletion.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelog/fix_obsoletion_cache_broken.md diff --git a/changelog/fix_obsoletion_cache_broken.md b/changelog/fix_obsoletion_cache_broken.md new file mode 100644 index 000000000000..754f892bcbce --- /dev/null +++ b/changelog/fix_obsoletion_cache_broken.md @@ -0,0 +1 @@ +* [#13950](https://github.com/rubocop/rubocop/pull/13950): Fix sporadic errors about `rubocop-rails` or `rubocop-performance` extraction, even if they are already part of the Gemfile. ([@earlopain][]) diff --git a/lib/rubocop/config_obsoletion.rb b/lib/rubocop/config_obsoletion.rb index 5cc6f3d0c808..f81abafb9d80 100644 --- a/lib/rubocop/config_obsoletion.rb +++ b/lib/rubocop/config_obsoletion.rb @@ -50,7 +50,7 @@ def reject_obsolete! # Default rules for obsoletions are in config/obsoletion.yml # Additional rules files can be added with `RuboCop::ConfigObsoletion.files << filename` def load_rules # rubocop:disable Metrics/AbcSize - rules = LOAD_RULES_CACHE[self.class.files] ||= + rules = LOAD_RULES_CACHE[self.class.files.hash] ||= self.class.files.each_with_object({}) do |filename, hash| hash.merge!(YAML.safe_load(File.read(filename)) || {}) do |_key, first, second| case first From cca1b98de3a621cdb794371bc65c67485a9bba34 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Thu, 6 Mar 2025 14:13:45 +0900 Subject: [PATCH 10/28] [Doc] Remove redundant periods This commit removes duplicate periods appearing after `lint_roller` in the "Extension" document: https://docs.rubocop.org/rubocop/1.73/extensions.html --- docs/modules/ROOT/pages/extensions.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/extensions.adoc b/docs/modules/ROOT/pages/extensions.adoc index 11256a85b9e1..7e57194f516b 100644 --- a/docs/modules/ROOT/pages/extensions.adoc +++ b/docs/modules/ROOT/pages/extensions.adoc @@ -1,6 +1,6 @@ = Extensions -:lint_roller: https://github.com/standardrb/lint_roller[lint_roller]. +:lint_roller: https://github.com/standardrb/lint_roller[lint_roller] It's possible to extend RuboCop with additional cops and formatters. There are already many official extensions, maintained by RuboCop's team, From 49fda29a814b6afbccdb3c30df8cba5aec81fa25 Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Thu, 6 Mar 2025 10:12:36 +0100 Subject: [PATCH 11/28] Remove no longer needed workaround for `regexp_parser` bug This code was added in https://github.com/rubocop/rubocop/commit/075d4d5d7597c2b179a90f76a2a9fc886465ecb9 but actually works around a bug in `regexp_parser` that was fixed in 2.9.3 with https://github.com/ammar/regexp_parser/pull/94 It's now the minimum supported version so the workaround can just be removed --- lib/rubocop/ext/regexp_node.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/rubocop/ext/regexp_node.rb b/lib/rubocop/ext/regexp_node.rb index 548f2a99abd2..84fb47c6f71e 100644 --- a/lib/rubocop/ext/regexp_node.rb +++ b/lib/rubocop/ext/regexp_node.rb @@ -43,7 +43,6 @@ def each_capture(named: ANY) def named_capturing?(exp, event, named) event == :enter && named == exp.respond_to?(:name) && - !exp.text.start_with?('(?<=') && exp.respond_to?(:capturing?) && exp.capturing? end From d065dd3c789a129fe00cc239b9198ee36f8d5e9a Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Thu, 6 Mar 2025 10:24:05 +0100 Subject: [PATCH 12/28] Use latest jruby again 9.4.12.0 has been released which should contain a fix for this --- .github/workflows/rubocop.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/rubocop.yml b/.github/workflows/rubocop.yml index 126ce604f8ef..ca700d5628b2 100644 --- a/.github/workflows/rubocop.yml +++ b/.github/workflows/rubocop.yml @@ -52,10 +52,7 @@ jobs: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: - # FIXME: When the JRuby issue https://github.com/jruby/jruby/issues/8606 is resolved, - # please replace it with the following: - # ruby-version: 'jruby' # Latest stable JRuby version - ruby-version: 'jruby-9.4.10.0' # The latest JRuby version that passes CI + ruby-version: 'jruby' # Latest stable JRuby version bundler-cache: true - name: spec run: bundle exec rake spec From 0fd4829923a42de9bc35206f43bda75f5455f6b7 Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Thu, 6 Mar 2025 16:58:29 +0100 Subject: [PATCH 13/28] Fix a false negative for `Lint/ReturnInVoidContext` when returning out of a block Should be block happen to be called, the return will break out of the block and attribute the return value to the method. This has been that way since the cop was added and there were no tests for blocks. The only expection to the rule is when returning from inside a lambda. Additionally, I simplified the implementation. The cop looked for `sdef` but then immediatly after required that the node be simply `def`. --- ...x_false_negative_return_in_void_context.md | 1 + .../cop/lint/return_in_void_context.rb | 15 ++--- .../cop/lint/return_in_void_context_spec.rb | 64 +++++++++++++++++++ 3 files changed, 69 insertions(+), 11 deletions(-) create mode 100644 changelog/fix_false_negative_return_in_void_context.md diff --git a/changelog/fix_false_negative_return_in_void_context.md b/changelog/fix_false_negative_return_in_void_context.md new file mode 100644 index 000000000000..7ec6e98dfaac --- /dev/null +++ b/changelog/fix_false_negative_return_in_void_context.md @@ -0,0 +1 @@ +* [#13960](https://github.com/rubocop/rubocop/pull/13960): Fix a false negative for `Lint/ReturnInVoidContext` when returning out of a block. ([@earlopain][]) diff --git a/lib/rubocop/cop/lint/return_in_void_context.rb b/lib/rubocop/cop/lint/return_in_void_context.rb index 1c930fbe81a0..ff1f6acdac60 100644 --- a/lib/rubocop/cop/lint/return_in_void_context.rb +++ b/lib/rubocop/cop/lint/return_in_void_context.rb @@ -35,22 +35,15 @@ class ReturnInVoidContext < Base def on_return(return_node) return unless return_node.descendants.any? - context_node = non_void_context(return_node) - - return unless context_node&.def_type? - return unless context_node&.void_context? + def_node = return_node.each_ancestor(:def).first + return unless def_node&.void_context? + return if return_node.each_ancestor(:any_block).any?(&:lambda?) add_offense( return_node.loc.keyword, - message: format(message, method: context_node.method_name) + message: format(message, method: def_node.method_name) ) end - - private - - def non_void_context(return_node) - return_node.each_ancestor(:block, :def, :defs).first - end end end end diff --git a/spec/rubocop/cop/lint/return_in_void_context_spec.rb b/spec/rubocop/cop/lint/return_in_void_context_spec.rb index 7e655aed86b9..f4999895dc1b 100644 --- a/spec/rubocop/cop/lint/return_in_void_context_spec.rb +++ b/spec/rubocop/cop/lint/return_in_void_context_spec.rb @@ -12,6 +12,58 @@ def initialize end RUBY end + + it 'registers an offense when the value is returned in a block' do + expect_offense(<<~RUBY) + class A + def initialize + foo do + return :qux + ^^^^^^ Do not return a value in `initialize`. + end + end + end + RUBY + end + + it 'registers an offense when the value is returned in a numblock' do + expect_offense(<<~RUBY) + class A + def initialize + foo do + _1 + return :qux + ^^^^^^ Do not return a value in `initialize`. + end + end + end + RUBY + end + + it 'registers an offense when the value is returned from inside a proc' do + expect_offense(<<~RUBY) + class A + def initialize + proc do + return :qux + ^^^^^^ Do not return a value in `initialize`. + end + end + end + RUBY + end + + it 'registers no offense when the value is returned from inside a lamdba' do + expect_no_offenses(<<~RUBY) + class A + def initialize + lambda do + return :qux + end + end + end + RUBY + end end context 'with an initialize method containing a return without a value' do @@ -24,6 +76,18 @@ def initialize end RUBY end + + it 'accepts when the return is in a block' do + expect_no_offenses(<<~RUBY) + class A + def initialize + foo do + return if bar? + end + end + end + RUBY + end end context 'with a setter method containing a return with a value' do From 0d893143e3e21a1cd25a2b8bda91c03e6db92479 Mon Sep 17 00:00:00 2001 From: Daniel Vandersluis Date: Wed, 5 Mar 2025 12:30:49 -0500 Subject: [PATCH 14/28] Tweak the offense message for `Style/ExponentialNotation` The current offense message uses the potentially obscure `[1, 10[` form for an exclusive range. Using comparisons is clearer. --- lib/rubocop/cop/style/exponential_notation.rb | 4 ++-- .../cop/style/exponential_notation_spec.rb | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/rubocop/cop/style/exponential_notation.rb b/lib/rubocop/cop/style/exponential_notation.rb index 9a395a8ccc3f..41ad62f7e3ab 100644 --- a/lib/rubocop/cop/style/exponential_notation.rb +++ b/lib/rubocop/cop/style/exponential_notation.rb @@ -60,8 +60,8 @@ module Style class ExponentialNotation < Base include ConfigurableEnforcedStyle MESSAGES = { - scientific: 'Use a mantissa in [1, 10[.', - engineering: 'Use an exponent divisible by 3 and a mantissa in [0.1, 1000[.', + scientific: 'Use a mantissa >= 1 and < 10.', + engineering: 'Use an exponent divisible by 3 and a mantissa >= 0.1 and < 1000.', integral: 'Use an integer as mantissa, without trailing zero.' }.freeze diff --git a/spec/rubocop/cop/style/exponential_notation_spec.rb b/spec/rubocop/cop/style/exponential_notation_spec.rb index 3787f87a551e..a3ddb231c346 100644 --- a/spec/rubocop/cop/style/exponential_notation_spec.rb +++ b/spec/rubocop/cop/style/exponential_notation_spec.rb @@ -7,21 +7,21 @@ it 'registers an offense for mantissa equal to 10' do expect_offense(<<~RUBY) 10e6 - ^^^^ Use a mantissa in [1, 10[. + ^^^^ Use a mantissa >= 1 and < 10. RUBY end it 'registers an offense for mantissa greater than 10' do expect_offense(<<~RUBY) 12.34e3 - ^^^^^^^ Use a mantissa in [1, 10[. + ^^^^^^^ Use a mantissa >= 1 and < 10. RUBY end it 'registers an offense for mantissa smaller than 1' do expect_offense(<<~RUBY) 0.314e1 - ^^^^^^^ Use a mantissa in [1, 10[. + ^^^^^^^ Use a mantissa >= 1 and < 10. RUBY end @@ -56,35 +56,35 @@ it 'registers an offense for exponent equal to 4' do expect_offense(<<~RUBY) 10e4 - ^^^^ Use an exponent divisible by 3 and a mantissa in [0.1, 1000[. + ^^^^ Use an exponent divisible by 3 and a mantissa >= 0.1 and < 1000. RUBY end it 'registers an offense for exponent equal to -2' do expect_offense(<<~RUBY) 12.3e-2 - ^^^^^^^ Use an exponent divisible by 3 and a mantissa in [0.1, 1000[. + ^^^^^^^ Use an exponent divisible by 3 and a mantissa >= 0.1 and < 1000. RUBY end it 'registers an offense for mantissa smaller than 0.1' do expect_offense(<<~RUBY) 0.09e9 - ^^^^^^ Use an exponent divisible by 3 and a mantissa in [0.1, 1000[. + ^^^^^^ Use an exponent divisible by 3 and a mantissa >= 0.1 and < 1000. RUBY end it 'registers an offense for a mantissa greater than -0.1' do expect_offense(<<~RUBY) -0.09e3 - ^^^^^^^ Use an exponent divisible by 3 and a mantissa in [0.1, 1000[. + ^^^^^^^ Use an exponent divisible by 3 and a mantissa >= 0.1 and < 1000. RUBY end it 'registers an offense for mantissa smaller than -1000' do expect_offense(<<~RUBY) -1012.34e6 - ^^^^^^^^^^ Use an exponent divisible by 3 and a mantissa in [0.1, 1000[. + ^^^^^^^^^^ Use an exponent divisible by 3 and a mantissa >= 0.1 and < 1000. RUBY end From 8b83cec4f4f3351f5546b48cf54c6b8e450f78c4 Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Fri, 7 Mar 2025 13:45:54 +0100 Subject: [PATCH 15/28] [Fix #13963] Fix wrong autocorrect for `Lint/LiteralAsCondition` when the literal is followed by `return`, `break`, or `next` It still makes sense to register an offense in this case since the code is likely faulty and not what the user intended. There's just not much to reasonably autocorrect to. For example, OP of the issue noticed that parens where missing. --- changelog/fix_literal_condition_void_expr.md | 1 + lib/rubocop/cop/lint/literal_as_condition.rb | 4 ++ .../cop/lint/literal_as_condition_spec.rb | 50 +++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 changelog/fix_literal_condition_void_expr.md diff --git a/changelog/fix_literal_condition_void_expr.md b/changelog/fix_literal_condition_void_expr.md new file mode 100644 index 000000000000..3b7fa598ad0c --- /dev/null +++ b/changelog/fix_literal_condition_void_expr.md @@ -0,0 +1 @@ +* [#13963](https://github.com/rubocop/rubocop/issues/13963): Fix wrong autocorrect for `Lint/LiteralAsCondition` when the literal is followed by `return`, `break`, or `next`. ([@earlopain][]) diff --git a/lib/rubocop/cop/lint/literal_as_condition.rb b/lib/rubocop/cop/lint/literal_as_condition.rb index 74f5ce7ebd73..c1219740fb0d 100644 --- a/lib/rubocop/cop/lint/literal_as_condition.rb +++ b/lib/rubocop/cop/lint/literal_as_condition.rb @@ -46,6 +46,10 @@ def on_and(node) return unless node.lhs.truthy_literal? add_offense(node.lhs) do |corrector| + # Don't autocorrect `'foo' && return` because having `return` as + # the leftmost node can lead to a void value expression syntax error. + next if node.rhs.type?(:return, :break, :next) + corrector.replace(node, node.rhs.source) end end diff --git a/spec/rubocop/cop/lint/literal_as_condition_spec.rb b/spec/rubocop/cop/lint/literal_as_condition_spec.rb index e30da217241c..459487391c7b 100644 --- a/spec/rubocop/cop/lint/literal_as_condition_spec.rb +++ b/spec/rubocop/cop/lint/literal_as_condition_spec.rb @@ -549,4 +549,54 @@ end until false RUBY end + + context 'void value expressions after autocorrect' do + it 'registers an offense but does not autocorrect when `return` is used after `&&`' do + expect_offense(<<~RUBY) + def foo + puts 123 && return if bar? + ^^^ Literal `123` appeared as a condition. + end + RUBY + + expect_no_corrections + end + + it 'registers an offense but does not autocorrect when inside `if` and `return` is used after `&&`' do + expect_offense(<<~RUBY) + def foo + baz? if 123 && return + ^^^ Literal `123` appeared as a condition. + end + RUBY + + expect_no_corrections + end + + it 'registers an offense but does not autocorrect when `break` is used after `&&`' do + expect_offense(<<~RUBY) + def foo + bar do + puts 123 && break if baz? + ^^^ Literal `123` appeared as a condition. + end + end + RUBY + + expect_no_corrections + end + + it 'registers an offense but does not autocorrect when `next` is used after `&&`' do + expect_offense(<<~RUBY) + def foo + bar do + puts 123 && next if baz? + ^^^ Literal `123` appeared as a condition. + end + end + RUBY + + expect_no_corrections + end + end end From c8759b23eb6d2ac385955ab8e64edc8288506902 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Sun, 9 Mar 2025 16:10:54 +0900 Subject: [PATCH 16/28] Make `Lint/RedundantTypeConversion` aware of redundant `to_d` This PR makes `Lint/RedundantTypeConversion` aware of redundant `to_d`. ```ruby require 'bigdecimal/util' BigDecimal(42).to_d == BigDecimal(42) # => true ``` --- ...undant_type_conversion_aware_of_redundant_to_d.md | 1 + lib/rubocop/cop/lint/redundant_type_conversion.rb | 12 +++++++++--- .../cop/lint/redundant_type_conversion_spec.rb | 12 ++++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 changelog/change_make_lint_redundant_type_conversion_aware_of_redundant_to_d.md diff --git a/changelog/change_make_lint_redundant_type_conversion_aware_of_redundant_to_d.md b/changelog/change_make_lint_redundant_type_conversion_aware_of_redundant_to_d.md new file mode 100644 index 000000000000..1508e08b58a1 --- /dev/null +++ b/changelog/change_make_lint_redundant_type_conversion_aware_of_redundant_to_d.md @@ -0,0 +1 @@ +* [#13967](https://github.com/rubocop/rubocop/pull/13967): Make `Lint/RedundantTypeConversion` aware of redundant `to_d`. ([@koic][]) diff --git a/lib/rubocop/cop/lint/redundant_type_conversion.rb b/lib/rubocop/cop/lint/redundant_type_conversion.rb index be1e9b388d9c..f65a7595011c 100644 --- a/lib/rubocop/cop/lint/redundant_type_conversion.rb +++ b/lib/rubocop/cop/lint/redundant_type_conversion.rb @@ -3,13 +3,13 @@ module RuboCop module Cop module Lint - # Checks for redundant uses of `to_s`, `to_sym`, `to_i`, `to_f`, `to_r`, `to_c`, + # Checks for redundant uses of `to_s`, `to_sym`, `to_i`, `to_f`, `to_d`, `to_r`, `to_c`, # `to_a`, `to_h`, and `to_set`. # # When one of these methods is called on an object of the same type, that object # is returned, making the call unnecessary. The cop detects conversion methods called # on object literals, class constructors, class `[]` methods, and the `Kernel` methods - # `String()`, `Integer()`, `Float()`, `Rational()`, `Complex()` and `Array()`. + # `String()`, `Integer()`, `Float()`, BigDecimal(), `Rational()`, `Complex()`, and `Array()`. # # Specifically, these cases are detected for each conversion method: # @@ -98,6 +98,7 @@ class RedundantTypeConversion < Base to_s: 'string_constructor?', to_i: 'integer_constructor?', to_f: 'float_constructor?', + to_d: 'bigdecimal_constructor?', to_r: 'rational_constructor?', to_c: 'complex_constructor?', to_a: 'array_constructor?', @@ -110,7 +111,7 @@ class RedundantTypeConversion < Base TYPED_METHODS = { to_s: %i[inspect] }.freeze CONVERSION_METHODS = Set[*LITERAL_NODE_TYPES.keys].freeze - RESTRICT_ON_SEND = CONVERSION_METHODS + RESTRICT_ON_SEND = CONVERSION_METHODS + [:to_d] private_constant :LITERAL_NODE_TYPES, :CONSTRUCTOR_MAPPING @@ -137,6 +138,11 @@ class RedundantTypeConversion < Base #type_constructor?(:Float) PATTERN + # @!method bigdecimal_constructor?(node) + def_node_matcher :bigdecimal_constructor?, <<~PATTERN + #type_constructor?(:BigDecimal) + PATTERN + # @!method rational_constructor?(node) def_node_matcher :rational_constructor?, <<~PATTERN #type_constructor?(:Rational) diff --git a/spec/rubocop/cop/lint/redundant_type_conversion_spec.rb b/spec/rubocop/cop/lint/redundant_type_conversion_spec.rb index 5c1a948a0dc1..17d854c583b9 100644 --- a/spec/rubocop/cop/lint/redundant_type_conversion_spec.rb +++ b/spec/rubocop/cop/lint/redundant_type_conversion_spec.rb @@ -248,6 +248,18 @@ it_behaves_like 'accepted', 'Float("number", exception: false).to_f' end + describe '`to_d`' do + it_behaves_like 'conversion', :to_d + + it_behaves_like 'offense', :to_d, 'BigDecimal(42)' + it_behaves_like 'offense', :to_d, 'Kernel::BigDecimal(42)' + it_behaves_like 'offense', :to_d, '::Kernel::BigDecimal(42)' + it_behaves_like 'offense', :to_d, 'BigDecimal("number", exception: true)' + + it_behaves_like 'accepted', 'BigDecimal("number", exception: false).to_d' + it_behaves_like 'accepted', 'BigDecimal(obj, n, exception: false).to_d' + end + describe '`to_r`' do it_behaves_like 'conversion', :to_r From 08aabb73f9cdd650671dd421619020460354d33f Mon Sep 17 00:00:00 2001 From: dak2 Date: Tue, 4 Mar 2025 16:55:55 +0900 Subject: [PATCH 17/28] Allow writing Steep annotation on the same line as `def`, `end`, `begin`, `class` and `module` in `Style/CommentedKeyword` [Steep](https://github.com/soutaro/steep) is a type checker for Ruby. Under certain circumstances, if the programmer writes `steep:ignore`, Steep will not do any type checking. In particular, `steep:ignore` can be written on the same line as `def`, `end`, `begin`, `class` and `module`, but it is rejected by the `Style/CommentedKeyword` rules. Another way to do this is to add options, but once an option is added, it is difficult to remove it, so the default rule written. Also, it is unlikely that you would write `steep:ignore` if you were not using steep, so I defined it as a default rule. refs: https://github.com/soutaro/steep/wiki/Release-Note-1.7#ignore-diagnostics-by-steepignore-comment --- ...ng_steep_annotaion_to_method_definition.md | 1 + lib/rubocop/cop/style/commented_keyword.rb | 11 +- .../cop/style/commented_keyword_spec.rb | 199 ++++++++++++++++++ 3 files changed, 209 insertions(+), 2 deletions(-) create mode 100644 changelog/new_allow_writing_steep_annotaion_to_method_definition.md diff --git a/changelog/new_allow_writing_steep_annotaion_to_method_definition.md b/changelog/new_allow_writing_steep_annotaion_to_method_definition.md new file mode 100644 index 000000000000..c331d2f11718 --- /dev/null +++ b/changelog/new_allow_writing_steep_annotaion_to_method_definition.md @@ -0,0 +1 @@ +* [#13943](https://github.com/rubocop/rubocop/pull/13943): Allow writing steep annotation to method definition for `Style/CommentedKeyword`. ([@dak2][]) diff --git a/lib/rubocop/cop/style/commented_keyword.rb b/lib/rubocop/cop/style/commented_keyword.rb index d41702d10ce3..9666ce4a06b0 100644 --- a/lib/rubocop/cop/style/commented_keyword.rb +++ b/lib/rubocop/cop/style/commented_keyword.rb @@ -9,8 +9,8 @@ module Style # These keywords are: `class`, `module`, `def`, `begin`, `end`. # # Note that some comments - # (`:nodoc:`, `:yields:`, `rubocop:disable` and `rubocop:todo`) - # and RBS::Inline annotation comments are allowed. + # (`:nodoc:`, `:yields:`, `rubocop:disable` and `rubocop:todo`), + # RBS::Inline annotation, and Steep annotation (`steep:ignore`) are allowed. # # Autocorrection removes comments from `end` keyword and keeps comments # for `class`, `module`, `def` and `begin` above the keyword. @@ -60,6 +60,8 @@ class CommentedKeyword < Base SUBCLASS_DEFINITION = /\A\s*class\s+(\w|::)+\s*<\s*(\w|::)+/.freeze METHOD_DEFINITION = /\A\s*def\s/.freeze + STEEP_REGEXP = /#\ssteep:ignore(\s|\z)/.freeze + def on_new_investigation processed_source.comments.each do |comment| next unless offensive?(comment) && (match = source_line(comment).match(REGEXP)) @@ -86,6 +88,7 @@ def register_offense(comment, matched_keyword) def offensive?(comment) line = source_line(comment) return false if rbs_inline_annotation?(line, comment) + return false if steep_annotation?(comment) KEYWORD_REGEXES.any? { |r| r.match?(line) } && ALLOWED_COMMENT_REGEXES.none? { |r| r.match?(line) } @@ -105,6 +108,10 @@ def rbs_inline_annotation?(line, comment) false end end + + def steep_annotation?(comment) + comment.text.match?(STEEP_REGEXP) + end end end end diff --git a/spec/rubocop/cop/style/commented_keyword_spec.rb b/spec/rubocop/cop/style/commented_keyword_spec.rb index 5c02fcb5201b..9f61cc09b586 100644 --- a/spec/rubocop/cop/style/commented_keyword_spec.rb +++ b/spec/rubocop/cop/style/commented_keyword_spec.rb @@ -352,4 +352,203 @@ module Y end RUBY end + + context 'when Steep annotation is used' do + it 'does not register an offense for `steep:ignore` annotation on the same line as `def`' do + expect_no_offenses(<<~RUBY) + def x # steep:ignore + end + + def x # steep:ignore MethodBodyTypeMismatch + end + RUBY + end + + it 'does not register an offense for `steep:ignore` annotation on the same line as `end`' do + expect_no_offenses(<<~RUBY) + def x + end # steep:ignore + + def x + end # steep:ignore MethodBodyTypeMismatch + RUBY + end + + it 'does not register an offense for `steep:ignore` annotation on the same line as `begin`' do + expect_no_offenses(<<~RUBY) + begin # steep:ignore + end + + begin # steep:ignore NoMethod + end + RUBY + end + + it 'does not register an offense for `steep:ignore` annotation on the same line as `class`' do + expect_no_offenses(<<~RUBY) + class X # steep:ignore + end + + class X # steep:ignore UnknownConstant + end + RUBY + end + + it 'does not register an offense for `steep:ignore` annotation on the same line as `module`' do + expect_no_offenses(<<~RUBY) + module X # steep:ignore + end + + module X # steep:ignore UnknownConstant + end + RUBY + end + + it 'registers an offense and corrects for non `steep:ignore` annotation on the same line as `def' do + expect_offense(<<~RUBY) + def x # steep + ^^^^^^^ Do not place comments on the same line as the `def` keyword. + end + + def x #steep:ignore + ^^^^^^^^^^^^^ Do not place comments on the same line as the `def` keyword. + end + + def x # steep:ignoreMethodBodyTypeMismatch + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not place comments on the same line as the `def` keyword. + end + RUBY + + expect_correction(<<~RUBY) + # steep + def x + end + + #steep:ignore + def x + end + + # steep:ignoreMethodBodyTypeMismatch + def x + end + RUBY + end + + it 'registers an offense and corrects for non `steep:ignore` annotation on the same line as `end' do + expect_offense(<<~RUBY) + def x + end # steep + ^^^^^^^ Do not place comments on the same line as the `end` keyword. + + def x + end #steep:ignore + ^^^^^^^^^^^^^ Do not place comments on the same line as the `end` keyword. + + def x + end # steep:ignoreMethodBodyTypeMismatch + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not place comments on the same line as the `end` keyword. + RUBY + + expect_correction(<<~RUBY) + def x + end + + def x + end + + def x + end + RUBY + end + + it 'registers an offense and corrects for non `steep:ignore` annotation on the same line as `begin' do + expect_offense(<<~RUBY) + begin # steep + ^^^^^^^ Do not place comments on the same line as the `begin` keyword. + end + + begin #steep:ignore + ^^^^^^^^^^^^^ Do not place comments on the same line as the `begin` keyword. + end + + begin # steep:ignoreUnknownConstant + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not place comments on the same line as the `begin` keyword. + end + RUBY + + expect_correction(<<~RUBY) + # steep + begin + end + + #steep:ignore + begin + end + + # steep:ignoreUnknownConstant + begin + end + RUBY + end + + it 'registers an offense and corrects for non `steep:ignore` annotation on the same line as `class' do + expect_offense(<<~RUBY) + class X # steep + ^^^^^^^ Do not place comments on the same line as the `class` keyword. + end + + class X #steep:ignore + ^^^^^^^^^^^^^ Do not place comments on the same line as the `class` keyword. + end + + class X # steep:ignoreUnknownConstant + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not place comments on the same line as the `class` keyword. + end + RUBY + + expect_correction(<<~RUBY) + # steep + class X + end + + #steep:ignore + class X + end + + # steep:ignoreUnknownConstant + class X + end + RUBY + end + + it 'registers an offense and corrects for non `steep:ignore` annotation on the same line as `module' do + expect_offense(<<~RUBY) + module X # steep + ^^^^^^^ Do not place comments on the same line as the `module` keyword. + end + + module X #steep:ignore + ^^^^^^^^^^^^^ Do not place comments on the same line as the `module` keyword. + end + + module X # steep:ignoreUnknownConstant + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not place comments on the same line as the `module` keyword. + end + RUBY + + expect_correction(<<~RUBY) + # steep + module X + end + + #steep:ignore + module X + end + + # steep:ignoreUnknownConstant + module X + end + RUBY + end + end end From e6469fda46b5164c04ef989396e4c2692db619ef Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Mon, 10 Mar 2025 13:09:14 +0900 Subject: [PATCH 18/28] Fix false alarm for config obsoletion Follow-up to https://github.com/rubocop/rubocop/issues/13950#issuecomment-2709112256 This prevents an unexpected warning that occurred because the config obsoletion was not considered in the plugin system introduced in #13792. --- .../fix_false_alarm_for_extracted_cop.md | 1 + .../config_obsoletion/extracted_cop.rb | 7 +++-- spec/rubocop/config_obsoletion_spec.rb | 29 ++++++++++++++++++- 3 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 changelog/fix_false_alarm_for_extracted_cop.md diff --git a/changelog/fix_false_alarm_for_extracted_cop.md b/changelog/fix_false_alarm_for_extracted_cop.md new file mode 100644 index 000000000000..5ae935c7ff94 --- /dev/null +++ b/changelog/fix_false_alarm_for_extracted_cop.md @@ -0,0 +1 @@ +* [#13971](https://github.com/rubocop/rubocop/pull/13971): Fix false alarm for config obsoletion. ([@koic][]) diff --git a/lib/rubocop/config_obsoletion/extracted_cop.rb b/lib/rubocop/config_obsoletion/extracted_cop.rb index b744ef604fae..650eaf13238f 100644 --- a/lib/rubocop/config_obsoletion/extracted_cop.rb +++ b/lib/rubocop/config_obsoletion/extracted_cop.rb @@ -15,7 +15,7 @@ def initialize(config, old_name, gem) end def violated? - return false if feature_loaded? + return false if plugin_loaded? affected_cops.any? end @@ -38,8 +38,9 @@ def affected_cops end end - def feature_loaded? - config.loaded_features.include?(gem) + def plugin_loaded? + # Plugins loaded via `require` are included in `loaded_features`. + config.loaded_plugins.include?(gem) || config.loaded_features.include?(gem) end end end diff --git a/spec/rubocop/config_obsoletion_spec.rb b/spec/rubocop/config_obsoletion_spec.rb index 5f99a7d8814d..35e003a8fabe 100644 --- a/spec/rubocop/config_obsoletion_spec.rb +++ b/spec/rubocop/config_obsoletion_spec.rb @@ -7,10 +7,11 @@ let(:configuration) { RuboCop::Config.new(hash, loaded_path) } let(:loaded_path) { 'example/.rubocop.yml' } + let(:plugins) { [] } let(:requires) { [] } before do - allow(configuration).to receive(:loaded_features).and_return(requires) + allow(configuration).to receive_messages(loaded_plugins: plugins, loaded_features: requires) described_class.files = [described_class::DEFAULT_RULES_FILE] end @@ -231,6 +232,32 @@ } end + context 'when the plugin extensions are loaded' do + let(:plugins) { %w[rubocop-rails rubocop-performance] } + + it 'does not print a warning message' do + expect { config_obsoletion.reject_obsolete! }.not_to raise_error + end + end + + context 'when only one plugin extension is loaded' do + let(:plugins) { %w[rubocop-performance] } + + let(:expected_message) do + <<~OUTPUT.chomp + `Rails` cops have been extracted to the `rubocop-rails` gem. + (obsolete configuration found in example/.rubocop.yml, please update it) + OUTPUT + end + + it 'prints a warning message' do + config_obsoletion.reject_obsolete! + raise 'Expected a RuboCop::ValidationError' + rescue RuboCop::ValidationError => e + expect(e.message).to eq(expected_message) + end + end + context 'when the extensions are loaded' do let(:requires) { %w[rubocop-rails rubocop-performance] } From 0ee5eb3d878eaf0d37ed971c3d02a965b89cf886 Mon Sep 17 00:00:00 2001 From: Lovro Bikic Date: Tue, 4 Mar 2025 09:29:25 +0100 Subject: [PATCH 19/28] Add cop Style/ComparableBetween --- .../new_add_cop_style_comparable_between.md | 1 + config/default.yml | 6 ++ lib/rubocop.rb | 1 + .../mixin/check_single_line_suitability.rb | 2 +- lib/rubocop/cop/mixin/target_ruby_version.rb | 2 +- lib/rubocop/cop/style/comparable_between.rb | 75 +++++++++++++++++++ lib/rubocop/cop/style/if_unless_modifier.rb | 4 +- .../cop/style/comparable_between_spec.rb | 60 +++++++++++++++ 8 files changed, 147 insertions(+), 4 deletions(-) create mode 100644 changelog/new_add_cop_style_comparable_between.md create mode 100644 lib/rubocop/cop/style/comparable_between.rb create mode 100644 spec/rubocop/cop/style/comparable_between_spec.rb diff --git a/changelog/new_add_cop_style_comparable_between.md b/changelog/new_add_cop_style_comparable_between.md new file mode 100644 index 000000000000..400969eae042 --- /dev/null +++ b/changelog/new_add_cop_style_comparable_between.md @@ -0,0 +1 @@ +* [#13936](https://github.com/rubocop/rubocop/pull/13936): Adds new cop `Style/ComparableBetween`. ([@lovro-bikic][]) diff --git a/config/default.yml b/config/default.yml index 669b331b6a84..6da4cc332d49 100644 --- a/config/default.yml +++ b/config/default.yml @@ -3682,6 +3682,12 @@ Style/CommentedKeyword: VersionAdded: '0.51' VersionChanged: '1.19' +Style/ComparableBetween: + Description: 'Enforces the use of `Comparable#between?` instead of logical comparison.' + Enabled: pending + VersionAdded: '<>' + StyleGuide: '#ranges-or-between' + Style/ComparableClamp: Description: 'Enforces the use of `Comparable#clamp` instead of comparison by minimum and maximum.' Enabled: pending diff --git a/lib/rubocop.rb b/lib/rubocop.rb index 01eb817eefdb..0ba1cada53aa 100644 --- a/lib/rubocop.rb +++ b/lib/rubocop.rb @@ -509,6 +509,7 @@ require_relative 'rubocop/cop/style/command_literal' require_relative 'rubocop/cop/style/comment_annotation' require_relative 'rubocop/cop/style/commented_keyword' +require_relative 'rubocop/cop/style/comparable_between' require_relative 'rubocop/cop/style/comparable_clamp' require_relative 'rubocop/cop/style/concat_array_literals' require_relative 'rubocop/cop/style/conditional_assignment' diff --git a/lib/rubocop/cop/mixin/check_single_line_suitability.rb b/lib/rubocop/cop/mixin/check_single_line_suitability.rb index 0c89d443e5c1..3d1dcb256ed1 100644 --- a/lib/rubocop/cop/mixin/check_single_line_suitability.rb +++ b/lib/rubocop/cop/mixin/check_single_line_suitability.rb @@ -35,7 +35,7 @@ def comment_within?(node) comment_line_numbers = processed_source.comments.map { |comment| comment.loc.line } comment_line_numbers.any? do |comment_line_number| - comment_line_number >= node.first_line && comment_line_number <= node.last_line + comment_line_number.between?(node.first_line, node.last_line) end end diff --git a/lib/rubocop/cop/mixin/target_ruby_version.rb b/lib/rubocop/cop/mixin/target_ruby_version.rb index 7c332f9671f7..e986a8687cab 100644 --- a/lib/rubocop/cop/mixin/target_ruby_version.rb +++ b/lib/rubocop/cop/mixin/target_ruby_version.rb @@ -29,7 +29,7 @@ def support_target_ruby_version?(version) min = required_minimum_ruby_version || 0 max = required_maximum_ruby_version || Float::INFINITY - min <= version && max >= version + version.between?(min, max) end end end diff --git a/lib/rubocop/cop/style/comparable_between.rb b/lib/rubocop/cop/style/comparable_between.rb new file mode 100644 index 000000000000..846f0976cfdc --- /dev/null +++ b/lib/rubocop/cop/style/comparable_between.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Style + # Checks for logical comparison which can be replaced with `Comparable#between?`. + # + # NOTE: `Comparable#between?` is on average slightly slower than logical comparison, + # although the difference generally isn't observable. If you require maximum + # performance, consider using logical comparison. + # + # @example + # + # # bad + # x >= min && x <= max + # + # # bad + # x <= max && x >= min + # + # # good + # x.between?(min, max) + # + class ComparableBetween < Base + extend AutoCorrector + + MSG = 'Prefer `%s` over logical comparison.' + + # @!method logical_comparison_between_by_min_first?(node) + def_node_matcher :logical_comparison_between_by_min_first?, <<~PATTERN + (and + (send + {$_value :>= $_min | $_min :<= $_value}) + (send + {$_value :<= $_max | $_max :>= $_value})) + PATTERN + + # @!method logical_comparison_between_by_max_first?(node) + def_node_matcher :logical_comparison_between_by_max_first?, <<~PATTERN + (and + (send + {$_value :<= $_max | $_max :>= $_value}) + (send + {$_value :>= $_min | $_min :<= $_value})) + PATTERN + + def on_and(node) + logical_comparison_between_by_min_first?(node) do |*args| + min_and_value, max_and_value = args.each_slice(2).to_a + + register_offense(node, min_and_value, max_and_value) + end + + logical_comparison_between_by_max_first?(node) do |*args| + max_and_value, min_and_value = args.each_slice(2).to_a + + register_offense(node, min_and_value, max_and_value) + end + end + + private + + def register_offense(node, min_and_value, max_and_value) + value = (min_and_value & max_and_value).first + min = min_and_value.find { _1 != value } + max = max_and_value.find { _1 != value } + + prefer = "#{value.source}.between?(#{min.source}, #{max.source})" + add_offense(node, message: format(MSG, prefer: prefer)) do |corrector| + corrector.replace(node, prefer) + end + end + end + end + end +end diff --git a/lib/rubocop/cop/style/if_unless_modifier.rb b/lib/rubocop/cop/style/if_unless_modifier.rb index 1545b78e0e86..448039eba494 100644 --- a/lib/rubocop/cop/style/if_unless_modifier.rb +++ b/lib/rubocop/cop/style/if_unless_modifier.rb @@ -164,8 +164,8 @@ def too_long_due_to_modifier?(node) def too_long_due_to_comment_after_modifier?(node, comment) source_length = processed_source.lines[node.first_line - 1].length - source_length >= max_line_length && - source_length - comment.source_range.length <= max_line_length + + max_line_length.between?(source_length - comment.source_range.length, source_length) end def allowed_patterns diff --git a/spec/rubocop/cop/style/comparable_between_spec.rb b/spec/rubocop/cop/style/comparable_between_spec.rb new file mode 100644 index 000000000000..72277aef23aa --- /dev/null +++ b/spec/rubocop/cop/style/comparable_between_spec.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +RSpec.describe RuboCop::Cop::Style::ComparableBetween, :config do + [ + 'x >= min && x <= max', + 'x >= min && max >= x', + 'min <= x && x <= max', + 'min <= x && max >= x', + 'x <= max && x >= min', + 'x <= max && min <= x', + 'max >= x && x >= min', + 'max >= x && min <= x' + ].each do |logical_comparison| + it "registers an offense with logical comparison #{logical_comparison}" do + expect_offense(<<~RUBY) + #{logical_comparison} + ^^^^^^^^^^^^^^^^^^^^ Prefer `x.between?(min, max)` over logical comparison. + RUBY + + expect_correction(<<~RUBY) + x.between?(min, max) + RUBY + end + end + + it 'registers an offense when using logical comparison with `and`' do + expect_offense(<<~RUBY) + x >= min and x <= max + ^^^^^^^^^^^^^^^^^^^^^ Prefer `x.between?(min, max)` over logical comparison. + RUBY + + expect_correction(<<~RUBY) + x.between?(min, max) + RUBY + end + + it 'does not register an offense when logical comparison excludes max value' do + expect_no_offenses(<<~RUBY) + x >= min && x < max + RUBY + end + + it 'does not register an offense when logical comparison excludes min value' do + expect_no_offenses(<<~RUBY) + x > min && x <= max + RUBY + end + + it 'does not register an offense when logical comparison excludes min and max value' do + expect_no_offenses(<<~RUBY) + x > min && x < max + RUBY + end + + it 'does not register an offense when logical comparison has different subjects for min and max' do + expect_no_offenses(<<~RUBY) + x >= min && y <= max + RUBY + end +end From 17767493d7b078c9cb5e9b893d53bd14f340b1bb Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Mon, 10 Mar 2025 11:51:22 +0900 Subject: [PATCH 20/28] [Fix #13969] Fix a false positive for `Lint/SharedMutableDefault` This PR fixes a false positive for `Lint/SharedMutableDefault` when `capacity` keyword argument is used. NOTE: The `minimum_target_ruby_version: 3.4` is intentionally not specified. A false negative may occur in Ruby 3.3 or earlier, but the intent behind upgrading to Ruby 3.4 is prioritized. Fixes #13969. --- ..._positive_for_lint_shared_mutable_default.md | 1 + lib/rubocop/cop/lint/shared_mutable_default.rb | 13 ++++++++++++- .../cop/lint/shared_mutable_default_spec.rb | 17 +++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 changelog/fix_a_false_positive_for_lint_shared_mutable_default.md diff --git a/changelog/fix_a_false_positive_for_lint_shared_mutable_default.md b/changelog/fix_a_false_positive_for_lint_shared_mutable_default.md new file mode 100644 index 000000000000..20ea90ebed2a --- /dev/null +++ b/changelog/fix_a_false_positive_for_lint_shared_mutable_default.md @@ -0,0 +1 @@ +* [#13969](https://github.com/rubocop/rubocop/issues/13969): Fix a false positive for `Lint/SharedMutableDefault` when `capacity` keyword argument is used. ([@koic][]) diff --git a/lib/rubocop/cop/lint/shared_mutable_default.rb b/lib/rubocop/cop/lint/shared_mutable_default.rb index 9a6b4378bad4..be9bd4b24ebb 100644 --- a/lib/rubocop/cop/lint/shared_mutable_default.rb +++ b/lib/rubocop/cop/lint/shared_mutable_default.rb @@ -51,7 +51,18 @@ class SharedMutableDefault < Base # @!method hash_initialized_with_mutable_shared_object?(node) def_node_matcher :hash_initialized_with_mutable_shared_object?, <<~PATTERN - (send (const {nil? cbase} :Hash) :new {array hash (send (const {nil? cbase} {:Array :Hash}) :new)}) + { + (send (const {nil? cbase} :Hash) :new [ + {array hash (send (const {nil? cbase} {:Array :Hash}) :new)} + !#capacity_keyword_argument? + ]) + (send (const {nil? cbase} :Hash) :new hash #capacity_keyword_argument?) + } + PATTERN + + # @!method capacity_keyword_argument?(node) + def_node_matcher :capacity_keyword_argument?, <<~PATTERN + (hash (pair (sym :capacity) _)) PATTERN def on_send(node) diff --git a/spec/rubocop/cop/lint/shared_mutable_default_spec.rb b/spec/rubocop/cop/lint/shared_mutable_default_spec.rb index ea7d80636a7e..b3b94a0b2ada 100644 --- a/spec/rubocop/cop/lint/shared_mutable_default_spec.rb +++ b/spec/rubocop/cop/lint/shared_mutable_default_spec.rb @@ -38,6 +38,14 @@ end end + context 'when `capacity` keyword argument is used' do + it 'does not register an offense' do + expect_no_offenses(<<~RUBY) + Hash.new(capacity: 42) + RUBY + end + end + context 'when Hash is initialized with an array' do it 'registers an offense' do expect_offense(<<~RUBY) @@ -59,4 +67,13 @@ RUBY end end + + context 'when Hash is initialized with a Hash and `capacity` keyword argument' do + it 'registers an offense' do + expect_offense(<<~RUBY) + Hash.new({}, capacity: 42) + ^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not create a Hash with a mutable default value [...] + RUBY + end + end end From 6bd6f895456c130c2b7c2bfc4478de4c68fc98c4 Mon Sep 17 00:00:00 2001 From: Daniel Vandersluis Date: Mon, 10 Mar 2025 14:58:32 -0400 Subject: [PATCH 21/28] [Fix #13965] Update `Lint/RedundantCopDisableDirective` to register an offense when cop names are given with improper casing This change allows the cop to report offenses for disabled cops with incorrect letter casing. The disable is redundant because cop names are case-sensitive, but the offense will point out the proper cop name. --- ...cop_disable_directive_to_20250310151635.md | 1 + lib/rubocop/directive_comment.rb | 2 +- .../redundant_cop_disable_directive_spec.rb | 22 +++++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 changelog/fix_update_lint_redundant_cop_disable_directive_to_20250310151635.md diff --git a/changelog/fix_update_lint_redundant_cop_disable_directive_to_20250310151635.md b/changelog/fix_update_lint_redundant_cop_disable_directive_to_20250310151635.md new file mode 100644 index 000000000000..b66f03e56e9e --- /dev/null +++ b/changelog/fix_update_lint_redundant_cop_disable_directive_to_20250310151635.md @@ -0,0 +1 @@ +* [#13965](https://github.com/rubocop/rubocop/issues/13965): Update `Lint/RedundantCopDisableDirective` to register an offense when cop names are given with improper casing. ([@dvandersluis][]) diff --git a/lib/rubocop/directive_comment.rb b/lib/rubocop/directive_comment.rb index 96cc1fabae67..b35ed4a4292a 100644 --- a/lib/rubocop/directive_comment.rb +++ b/lib/rubocop/directive_comment.rb @@ -12,7 +12,7 @@ class DirectiveComment # @api private LINT_SYNTAX_COP = "#{LINT_DEPARTMENT}/Syntax" # @api private - COP_NAME_PATTERN = '([A-Z]\w+/)*(?:[A-Z]\w+)' + COP_NAME_PATTERN = '([A-Za-z]\w+/)*(?:[A-Za-z]\w+)' # @api private COP_NAMES_PATTERN = "(?:#{COP_NAME_PATTERN} , )*#{COP_NAME_PATTERN}" # @api private diff --git a/spec/rubocop/cop/lint/redundant_cop_disable_directive_spec.rb b/spec/rubocop/cop/lint/redundant_cop_disable_directive_spec.rb index 378eca96242e..09ac763819ab 100644 --- a/spec/rubocop/cop/lint/redundant_cop_disable_directive_spec.rb +++ b/spec/rubocop/cop/lint/redundant_cop_disable_directive_spec.rb @@ -266,6 +266,28 @@ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{message} RUBY end + + context 'when the department starts with a lowercase letter' do + it 'registers an offense' do + expect_offense(<<~RUBY) + # rubocop:disable lint/SelfAssignment + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unnecessary disabling of `lint/SelfAssignment` (did you mean `Lint/SelfAssignment`?). + RUBY + + expect_correction('') + end + end + + context 'when the cop starts with a lowercase letter' do + it 'registers an offense' do + expect_offense(<<~RUBY) + # rubocop:disable Lint/selfAssignment + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unnecessary disabling of `Lint/selfAssignment` (did you mean `Lint/SelfAssignment`?). + RUBY + + expect_correction('') + end + end end context 'all cops' do From 5a405d554f3dfb4adf99fa7d8590f2a5dfd88244 Mon Sep 17 00:00:00 2001 From: Daniel Vandersluis Date: Tue, 11 Mar 2025 10:58:23 -0400 Subject: [PATCH 22/28] Update documentation for `Style/FormatStringToken` for clarity --- lib/rubocop/cop/style/format_string_token.rb | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/rubocop/cop/style/format_string_token.rb b/lib/rubocop/cop/style/format_string_token.rb index 6e2b11d2beda..eade46e9b5f8 100644 --- a/lib/rubocop/cop/style/format_string_token.rb +++ b/lib/rubocop/cop/style/format_string_token.rb @@ -3,17 +3,21 @@ module RuboCop module Cop module Style - # Use a consistent style for named format string tokens. + # Use a consistent style for tokens within a format string. # - # NOTE: `unannotated` style cop only works for strings - # which are passed as arguments to those methods: - # `printf`, `sprintf`, `format`, `%`. - # The reason is that _unannotated_ format is very similar - # to encoded URLs or Date/Time formatting strings. + # All strings are evaluated. In some cases, this may be undesirable, + # as they could be used as arguments to a method that does not consider + # them to be tokens, but rather other identifiers or just part of the string. + # In these cases, `AllowedMethods` or `AllowedPatterns` can be used to mark the + # method as allowed, thereby avoiding an offense from the cop. # - # This cop's allowed methods can be customized with `AllowedMethods`. # By default, there are no allowed methods. # + # NOTE: Tokens in the `unannotated` style (eg. `%s`) are only considered if used + # in the format string argument to the methods `printf`, `sprintf`, `format` and + # `%`. This is done in order to prevent false positives, because this format is + # very similar to encoded URLs or Date/Time formatting strings. + # # @example EnforcedStyle: annotated (default) # # # bad From f20e4c5f8d4f79d2d68c6319a20485db70293ef1 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Wed, 12 Mar 2025 13:20:50 +0900 Subject: [PATCH 23/28] Allow `TLS1_1` and `TLS1_2` by default in `Naming/VariableNumber` This PR allows `TLS1_1` and `TLS1_2` by default in `Naming/VariableNumber`. to accommodate OpenSSL version parameter names. --- ...nge_allow_tls_1_1_and_tls_1_2_in_naming_variable_number.md | 1 + config/default.yml | 2 ++ spec/rubocop/config_loader_spec.rb | 4 +++- 3 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 changelog/change_allow_tls_1_1_and_tls_1_2_in_naming_variable_number.md diff --git a/changelog/change_allow_tls_1_1_and_tls_1_2_in_naming_variable_number.md b/changelog/change_allow_tls_1_1_and_tls_1_2_in_naming_variable_number.md new file mode 100644 index 000000000000..e61258b9fd93 --- /dev/null +++ b/changelog/change_allow_tls_1_1_and_tls_1_2_in_naming_variable_number.md @@ -0,0 +1 @@ +* [#13977](https://github.com/rubocop/rubocop/issues/13977): Allow `TLS1_1` and `TLS1_2` by default in `Naming/VariableNumber` to accommodate OpenSSL version parameter names. ([@koic][]) diff --git a/config/default.yml b/config/default.yml index 6da4cc332d49..4144c8a8cc6a 100644 --- a/config/default.yml +++ b/config/default.yml @@ -3104,6 +3104,8 @@ Naming/VariableNumber: CheckMethodNames: true CheckSymbols: true AllowedIdentifiers: + - TLS1_1 # OpenSSL::SSL::TLS1_1_VERSION + - TLS1_2 # OpenSSL::SSL::TLS1_2_VERSION - capture3 # Open3.capture3 - iso8601 # Time#iso8601 - rfc1123_date # CGI.rfc1123_date diff --git a/spec/rubocop/config_loader_spec.rb b/spec/rubocop/config_loader_spec.rb index f299858c46ec..309739f7aa69 100644 --- a/spec/rubocop/config_loader_spec.rb +++ b/spec/rubocop/config_loader_spec.rb @@ -746,7 +746,9 @@ .to contain_exactly('foo.rb', 'test.rb') expect(examples_configuration['Include']).to contain_exactly('bar.rb', 'another_test.rb') expect(examples_configuration['AllowedIdentifiers']) - .to match_array(%w[capture3 iso8601 rfc1123_date rfc2822 rfc3339 rfc822 iso2 x86_64]) + .to match_array( + %w[TLS1_1 TLS1_2 capture3 iso8601 rfc1123_date rfc2822 rfc3339 rfc822 iso2 x86_64] + ) expect(examples_configuration['InheritedArraySpecifiedString']).to contain_exactly( 'bare string', 'string in array' From b6678159b618a9274d56fd4a95310fa48f36666c Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Wed, 12 Mar 2025 11:26:34 +0900 Subject: [PATCH 24/28] [Fix #13975] Fix false positives for `Style/RedundantCurrentDirectoryInPath` This PR fixes false positives for `Style/RedundantCurrentDirectoryInPath` when using a complex current directory path in `require_relative`. Fixes #13975. --- ...tyle_redundant_current_directory_in_path.md | 1 + .../redundant_current_directory_in_path.rb | 18 ++++++++++++++---- ...redundant_current_directory_in_path_spec.rb | 17 +++++++++++++++++ 3 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 changelog/fix_false_positives_for_style_redundant_current_directory_in_path.md diff --git a/changelog/fix_false_positives_for_style_redundant_current_directory_in_path.md b/changelog/fix_false_positives_for_style_redundant_current_directory_in_path.md new file mode 100644 index 000000000000..8ae8fca950de --- /dev/null +++ b/changelog/fix_false_positives_for_style_redundant_current_directory_in_path.md @@ -0,0 +1 @@ +* [#13975](https://github.com/rubocop/rubocop/issues/13975): Fix false positives for `Style/RedundantCurrentDirectoryInPath` when using a complex current directory path in `require_relative`. ([@koic][]) diff --git a/lib/rubocop/cop/style/redundant_current_directory_in_path.rb b/lib/rubocop/cop/style/redundant_current_directory_in_path.rb index 7ee364bfae9c..38f36ce1ff74 100644 --- a/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +++ b/lib/rubocop/cop/style/redundant_current_directory_in_path.rb @@ -20,20 +20,30 @@ class RedundantCurrentDirectoryInPath < Base MSG = 'Remove the redundant current directory path.' RESTRICT_ON_SEND = %i[require_relative].freeze - CURRENT_DIRECTORY_PATH = './' + CURRENT_DIRECTORY_PREFIX = %r{./+}.freeze + REDUNDANT_CURRENT_DIRECTORY_PREFIX = /\A#{CURRENT_DIRECTORY_PREFIX}/.freeze def on_send(node) return unless (first_argument = node.first_argument) - return unless first_argument.str_content&.start_with?(CURRENT_DIRECTORY_PATH) - return unless (index = first_argument.source.index(CURRENT_DIRECTORY_PATH)) + return unless (index = first_argument.source.index(CURRENT_DIRECTORY_PREFIX)) + return unless (redundant_length = redundant_path_length(first_argument.str_content)) begin_pos = first_argument.source_range.begin.begin_pos + index - range = range_between(begin_pos, begin_pos + 2) + end_pos = begin_pos + redundant_length + range = range_between(begin_pos, end_pos) add_offense(range) do |corrector| corrector.remove(range) end end + + private + + def redundant_path_length(path) + return unless (match = path&.match(REDUNDANT_CURRENT_DIRECTORY_PREFIX)) + + match[0].length + end end end end diff --git a/spec/rubocop/cop/style/redundant_current_directory_in_path_spec.rb b/spec/rubocop/cop/style/redundant_current_directory_in_path_spec.rb index a3c5dd21e16d..82c0a7bd0db9 100644 --- a/spec/rubocop/cop/style/redundant_current_directory_in_path_spec.rb +++ b/spec/rubocop/cop/style/redundant_current_directory_in_path_spec.rb @@ -23,6 +23,17 @@ RUBY end + it "registers an offense when using a complex current directory path in `require_relative '...'`" do + expect_offense(<<~RUBY) + require_relative './//./../path/to/feature' + ^^^^ Remove the redundant current directory path. + RUBY + + expect_correction(<<~RUBY) + require_relative '../path/to/feature' + RUBY + end + it 'registers an offense when using a current directory path in `require_relative %q(...)`' do expect_offense(<<~RUBY) require_relative %q(./path/to/feature) @@ -40,6 +51,12 @@ RUBY end + it 'does not register an offense when using a path that starts with a dot in `require_relative`' do + expect_no_offenses(<<~RUBY) + require_relative '.path' + RUBY + end + it 'does not register an offense when not using a current directory path in `require_relative`' do expect_no_offenses(<<~RUBY) require_relative 'path/to/feature' From a11862402ce9a9f247984cf98479c40538737426 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Thu, 13 Mar 2025 12:18:52 +0900 Subject: [PATCH 25/28] Prevent redundant plugin loading This PR prevents redundant plugin loading when a duplicate plugin is specified in an inherited config. --- .../fix_prevent_redundant_plugin_loading.md | 1 + lib/rubocop/config_loader_resolver.rb | 3 +- spec/rubocop/version_spec.rb | 42 +++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 changelog/fix_prevent_redundant_plugin_loading.md diff --git a/changelog/fix_prevent_redundant_plugin_loading.md b/changelog/fix_prevent_redundant_plugin_loading.md new file mode 100644 index 000000000000..9d5361fb8820 --- /dev/null +++ b/changelog/fix_prevent_redundant_plugin_loading.md @@ -0,0 +1 @@ +* [#13981](https://github.com/rubocop/rubocop/pull/13981): Prevent redundant plugin loading when a duplicate plugin is specified in an inherited config. ([@koic][]) diff --git a/lib/rubocop/config_loader_resolver.rb b/lib/rubocop/config_loader_resolver.rb index ee27a12f0699..5d727f06cdfa 100644 --- a/lib/rubocop/config_loader_resolver.rb +++ b/lib/rubocop/config_loader_resolver.rb @@ -9,7 +9,8 @@ module RuboCop # @api private class ConfigLoaderResolver # rubocop:disable Metrics/ClassLength def resolve_plugins(rubocop_config, plugins) - return if (plugins = Array(plugins)).empty? + plugins = Array(plugins) - ConfigLoader.loaded_plugins.map { |plugin| plugin.about.name } + return if plugins.empty? Plugin.integrate_plugins(rubocop_config, plugins) end diff --git a/spec/rubocop/version_spec.rb b/spec/rubocop/version_spec.rb index cf49cc007797..bfae007239e0 100644 --- a/spec/rubocop/version_spec.rb +++ b/spec/rubocop/version_spec.rb @@ -104,6 +104,48 @@ module FooBarBaz end end + context 'when plugins are specified' do + before do + create_file('.rubocop.yml', <<~YAML) + plugins: + - rubocop-performance + - rubocop-rspec + YAML + end + + it 'returns the extensions' do + expect(extension_versions).to contain_exactly( + /- rubocop-performance \d+\.\d+\.\d+/, + /- rubocop-rspec \d+\.\d+\.\d+/ + ) + end + end + + context 'when a duplicate plugin is specified in an inherited config' do + before do + create_file('base.yml', <<~YAML) + plugins: + - rubocop-performance + YAML + + create_file('.rubocop.yml', <<~YAML) + inherit_from: + - base.yml + + plugins: + - rubocop-performance + - rubocop-rspec + YAML + end + + it 'returns each extension exactly once' do + expect(extension_versions).to contain_exactly( + /- rubocop-performance \d+\.\d+\.\d+/, + /- rubocop-rspec \d+\.\d+\.\d+/ + ) + end + end + context 'with an invalid cop in config' do before do create_file('.rubocop.yml', <<~YAML) From e2fc35aee78695d18ca02c33af5257812a877b85 Mon Sep 17 00:00:00 2001 From: Daniel Vandersluis Date: Tue, 11 Mar 2025 16:51:31 -0400 Subject: [PATCH 26/28] Add `Mode: conservative` configuration to `Style/FormatStringToken` to make the cop only register offenses for strings given to `printf`, `sprintf`, `format`, and `%` --- ...rvative_configuration_to_20250311170524.md | 1 + config/default.yml | 8 ++- lib/rubocop/cop/style/format_string_token.rb | 45 ++++++++++---- .../cop/style/format_string_token_spec.rb | 60 ++++++++++++++++++- 4 files changed, 101 insertions(+), 13 deletions(-) create mode 100644 changelog/change_add_mode_conservative_configuration_to_20250311170524.md diff --git a/changelog/change_add_mode_conservative_configuration_to_20250311170524.md b/changelog/change_add_mode_conservative_configuration_to_20250311170524.md new file mode 100644 index 000000000000..191bc71c1998 --- /dev/null +++ b/changelog/change_add_mode_conservative_configuration_to_20250311170524.md @@ -0,0 +1 @@ +* [#13979](https://github.com/rubocop/rubocop/pull/13979): Add `Mode: conservative` configuration to `Style/FormatStringToken` to make the cop only register offenses for strings given to `printf`, `sprintf`, `format`, and `%`. ([@dvandersluis][]) diff --git a/config/default.yml b/config/default.yml index 4144c8a8cc6a..a4d03839ecc2 100644 --- a/config/default.yml +++ b/config/default.yml @@ -4092,8 +4092,14 @@ Style/FormatStringToken: # style token in a format string to be allowed when enforced style is not # `unannotated`. MaxUnannotatedPlaceholdersAllowed: 1 + # The mode the cop operates in. Two values are allowed: + # * aggressive (default): all strings are considered + # * conservative: + # only register offenses for strings given to `printf`, `sprintf`, + # format` and `%` methods. Other strings are not considered. + Mode: aggressive VersionAdded: '0.49' - VersionChanged: '1.0' + VersionChanged: '<>' AllowedMethods: [] AllowedPatterns: [] diff --git a/lib/rubocop/cop/style/format_string_token.rb b/lib/rubocop/cop/style/format_string_token.rb index eade46e9b5f8..63f9ba01ee06 100644 --- a/lib/rubocop/cop/style/format_string_token.rb +++ b/lib/rubocop/cop/style/format_string_token.rb @@ -5,18 +5,22 @@ module Cop module Style # Use a consistent style for tokens within a format string. # - # All strings are evaluated. In some cases, this may be undesirable, + # By default, all strings are evaluated. In some cases, this may be undesirable, # as they could be used as arguments to a method that does not consider # them to be tokens, but rather other identifiers or just part of the string. - # In these cases, `AllowedMethods` or `AllowedPatterns` can be used to mark the - # method as allowed, thereby avoiding an offense from the cop. # - # By default, there are no allowed methods. + # `AllowedMethods` or `AllowedPatterns` can be configured with in order to mark specific + # methods as always allowed, thereby avoiding an offense from the cop. By default, there + # are no allowed methods. # - # NOTE: Tokens in the `unannotated` style (eg. `%s`) are only considered if used - # in the format string argument to the methods `printf`, `sprintf`, `format` and - # `%`. This is done in order to prevent false positives, because this format is - # very similar to encoded URLs or Date/Time formatting strings. + # Additionally, the cop can be made conservative by configuring it with + # `Mode: conservative` (default `aggressive`). In this mode, tokens (regardless + # of `EnforcedStyle`) are only considered if used in the format string argument to the + # methods `printf`, `sprintf`, `format` and `%`. + # + # NOTE: Tokens in the `unannotated` style (eg. `%s`) are always treated as if + # configured with `Conservative: true`. This is done in order to prevent false positives, + # because this format is very similar to encoded URLs or Date/Time formatting strings. # # @example EnforcedStyle: annotated (default) # @@ -86,6 +90,20 @@ module Style # # good # redirect('foo/%{bar_id}') # + # @example Mode: conservative, EnforcedStyle: annotated + # # In `conservative` mode, offenses are only registered for strings + # # given to a known formatting method. + # + # # good + # "%{greeting}" + # foo("%{greeting}") + # + # # bad + # format("%{greeting}", greeting: 'Hello') + # printf("%{greeting}", greeting: 'Hello') + # sprintf("%{greeting}", greeting: 'Hello') + # "%{greeting}" % { greeting: 'Hello' } + # class FormatStringToken < Base include ConfigurableEnforcedStyle include AllowedMethods @@ -157,8 +175,9 @@ def autocorrect_sequence(corrector, detected_sequence, token_range) corrector.replace(token_range, correction) end - def unannotated_format?(node, detected_style) - detected_style == :unannotated && !format_string_in_typical_context?(node) + def allowed_string?(node, detected_style) + (detected_style == :unannotated || conservative?) && + !format_string_in_typical_context?(node) end def message(detected_style) @@ -207,7 +226,7 @@ def token_ranges(contents) def collect_detections(node) detections = [] tokens(node) do |detected_sequence, token_range| - unless unannotated_format?(node, detected_sequence.style) + unless allowed_string?(node, detected_sequence.style) detections << [detected_sequence, token_range] end end @@ -226,6 +245,10 @@ def allowed_unannotated?(detections) def max_unannotated_placeholders_allowed cop_config['MaxUnannotatedPlaceholdersAllowed'] end + + def conservative? + cop_config.fetch('Mode', :aggressive).to_sym == :conservative + end end end end diff --git a/spec/rubocop/cop/style/format_string_token_spec.rb b/spec/rubocop/cop/style/format_string_token_spec.rb index e10fd6700c7a..b45d0cadaa7f 100644 --- a/spec/rubocop/cop/style/format_string_token_spec.rb +++ b/spec/rubocop/cop/style/format_string_token_spec.rb @@ -4,6 +4,7 @@ let(:enforced_style) { :annotated } let(:allowed_methods) { [] } let(:allowed_patterns) { [] } + let(:mode) { :aggressive } let(:cop_config) do { @@ -11,7 +12,8 @@ 'SupportedStyles' => %i[annotated template unannotated], 'MaxUnannotatedPlaceholdersAllowed' => 0, 'AllowedMethods' => allowed_methods, - 'AllowedPatterns' => allowed_patterns + 'AllowedPatterns' => allowed_patterns, + 'Mode' => mode } end @@ -416,4 +418,60 @@ expect_no_corrections end end + + context 'with `Mode: conservative`' do + let(:mode) { :conservative } + + %i[annotated template unannotated].each do |style| + context "when enforced style is #{style}" do + let(:enforced_style) { style } + + shared_examples 'conservative' do |given_style, string| + context "with `#{string}`" do + context 'with a bare string' do + it 'does not register an offense' do + expect_no_offenses(<<~RUBY) + '#{string}' + RUBY + end + end + + if style != given_style + %i[printf sprintf format].each do |method| + context "as an argument to `#{method}`" do + it 'registers an offense' do + expect_offense(<<~RUBY, string: string, method: method) + %{method}('%{string}', *vars) + _{method} ^{string} Prefer [...] + RUBY + end + end + end + + context 'as an argument to `%`' do + it 'registers an offense' do + expect_offense(<<~RUBY, string: string) + '#{string}' % vars + ^{string} Prefer [...] + RUBY + end + end + end + + context 'as an argument to another method' do + it 'does not register an offense' do + expect_no_offenses(<<~RUBY) + foo('#{string}') + RUBY + end + end + end + end + + it_behaves_like 'conservative', :annotated, '%s' + it_behaves_like 'conservative', :template, '%{greetings}' + it_behaves_like 'conservative', :unannotated, '%s' + end + end + end end From 448dc7ffe520cc888ef5e4d2bf188a3d7ff018af Mon Sep 17 00:00:00 2001 From: Bozhidar Batsov Date: Thu, 13 Mar 2025 10:22:09 +0200 Subject: [PATCH 27/28] Update Changelog --- CHANGELOG.md | 28 +++++++++++++++++++ ...ed_style_for_classes_and_20250227100212.md | 1 - ...rvative_configuration_to_20250311170524.md | 1 - ...1_and_tls_1_2_in_naming_variable_number.md | 1 - ...type_conversion_aware_of_redundant_to_d.md | 1 - ...ositive_for_lint_shared_mutable_default.md | 1 - changelog/fix_double_negation_numblock.md | 1 - .../fix_false_alarm_for_extracted_cop.md | 1 - ...x_false_negative_return_in_void_context.md | 1 - ...egative_useless_constant_scoping_sclass.md | 1 - ...false_negatives_nonlocal_exit_numblocks.md | 1 - ...yle_redundant_current_directory_in_path.md | 1 - changelog/fix_literal_condition_void_expr.md | 1 - changelog/fix_method_call_args_numblock.md | 1 - changelog/fix_obsoletion_cache_broken.md | 1 - .../fix_prevent_redundant_plugin_loading.md | 1 - ...cop_disable_directive_to_20250310151635.md | 1 - ..._wrong_autocorrect_rescue_modifier_mlhs.md | 1 - .../new_add_cop_style_comparable_between.md | 1 - ...ng_steep_annotaion_to_method_definition.md | 1 - 20 files changed, 28 insertions(+), 19 deletions(-) delete mode 100644 changelog/change_add_enforced_style_for_classes_and_20250227100212.md delete mode 100644 changelog/change_add_mode_conservative_configuration_to_20250311170524.md delete mode 100644 changelog/change_allow_tls_1_1_and_tls_1_2_in_naming_variable_number.md delete mode 100644 changelog/change_make_lint_redundant_type_conversion_aware_of_redundant_to_d.md delete mode 100644 changelog/fix_a_false_positive_for_lint_shared_mutable_default.md delete mode 100644 changelog/fix_double_negation_numblock.md delete mode 100644 changelog/fix_false_alarm_for_extracted_cop.md delete mode 100644 changelog/fix_false_negative_return_in_void_context.md delete mode 100644 changelog/fix_false_negative_useless_constant_scoping_sclass.md delete mode 100644 changelog/fix_false_negatives_nonlocal_exit_numblocks.md delete mode 100644 changelog/fix_false_positives_for_style_redundant_current_directory_in_path.md delete mode 100644 changelog/fix_literal_condition_void_expr.md delete mode 100644 changelog/fix_method_call_args_numblock.md delete mode 100644 changelog/fix_obsoletion_cache_broken.md delete mode 100644 changelog/fix_prevent_redundant_plugin_loading.md delete mode 100644 changelog/fix_update_lint_redundant_cop_disable_directive_to_20250310151635.md delete mode 100644 changelog/fix_wrong_autocorrect_rescue_modifier_mlhs.md delete mode 100644 changelog/new_add_cop_style_comparable_between.md delete mode 100644 changelog/new_allow_writing_steep_annotaion_to_method_definition.md diff --git a/CHANGELOG.md b/CHANGELOG.md index da0490f2b859..3a2cce457d56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,34 @@ ## master (unreleased) +### New features + +* [#13936](https://github.com/rubocop/rubocop/pull/13936): Adds new cop `Style/ComparableBetween`. ([@lovro-bikic][]) +* [#13943](https://github.com/rubocop/rubocop/pull/13943): Allow writing steep annotation to method definition for `Style/CommentedKeyword`. ([@dak2][]) + +### Bug fixes + +* [#13969](https://github.com/rubocop/rubocop/issues/13969): Fix a false positive for `Lint/SharedMutableDefault` when `capacity` keyword argument is used. ([@koic][]) +* [#13945](https://github.com/rubocop/rubocop/pull/13945): Fix a false positive for `Style/DoubleNegation` when calling `define_method`/`define_singleton_method` with a numblock. ([@earlopain][]) +* [#13971](https://github.com/rubocop/rubocop/pull/13971): Fix false alarm for config obsoletion. ([@koic][]) +* [#13960](https://github.com/rubocop/rubocop/pull/13960): Fix a false negative for `Lint/ReturnInVoidContext` when returning out of a block. ([@earlopain][]) +* [#13947](https://github.com/rubocop/rubocop/pull/13947): Fix a false negative for `Lint/UselessConstantScoping` for constants defined in `class << self`. ([@earlopain][]) +* [#13949](https://github.com/rubocop/rubocop/pull/13949): Fix a false negative for `Lint/NonLocalExitFromIterator` with numblocks. ([@earlopain][]) +* [#13975](https://github.com/rubocop/rubocop/issues/13975): Fix false positives for `Style/RedundantCurrentDirectoryInPath` when using a complex current directory path in `require_relative`. ([@koic][]) +* [#13963](https://github.com/rubocop/rubocop/issues/13963): Fix wrong autocorrect for `Lint/LiteralAsCondition` when the literal is followed by `return`, `break`, or `next`. ([@earlopain][]) +* [#13946](https://github.com/rubocop/rubocop/pull/13946): Fix some false positives for `Style/MethodCallWithArgsParentheses` with `EnforcedStyle: omit_parentheses` style and numblocks. ([@earlopain][]) +* [#13950](https://github.com/rubocop/rubocop/pull/13950): Fix sporadic errors about `rubocop-rails` or `rubocop-performance` extraction, even if they are already part of the Gemfile. ([@earlopain][]) +* [#13981](https://github.com/rubocop/rubocop/pull/13981): Prevent redundant plugin loading when a duplicate plugin is specified in an inherited config. ([@koic][]) +* [#13965](https://github.com/rubocop/rubocop/issues/13965): Update `Lint/RedundantCopDisableDirective` to register an offense when cop names are given with improper casing. ([@dvandersluis][]) +* [#13948](https://github.com/rubocop/rubocop/pull/13948): Fix wrong autocorrect for `Style/RescueModifier` when using parallel assignment and the right-hand-side is not a bracketed array. ([@earlopain][]) + +### Changes + +* [#12851](https://github.com/rubocop/rubocop/issues/12851): Add `EnforcedStyleForClasses` and `EnforcedStyleForModules` configuration options to `Style/ClassAndModuleChildren`. ([@dvandersluis][]) +* [#13979](https://github.com/rubocop/rubocop/pull/13979): Add `Mode: conservative` configuration to `Style/FormatStringToken` to make the cop only register offenses for strings given to `printf`, `sprintf`, `format`, and `%`. ([@dvandersluis][]) +* [#13977](https://github.com/rubocop/rubocop/issues/13977): Allow `TLS1_1` and `TLS1_2` by default in `Naming/VariableNumber` to accommodate OpenSSL version parameter names. ([@koic][]) +* [#13967](https://github.com/rubocop/rubocop/pull/13967): Make `Lint/RedundantTypeConversion` aware of redundant `to_d`. ([@koic][]) + ## 1.73.2 (2025-03-04) ### Bug fixes diff --git a/changelog/change_add_enforced_style_for_classes_and_20250227100212.md b/changelog/change_add_enforced_style_for_classes_and_20250227100212.md deleted file mode 100644 index 79426dbfee40..000000000000 --- a/changelog/change_add_enforced_style_for_classes_and_20250227100212.md +++ /dev/null @@ -1 +0,0 @@ -* [#12851](https://github.com/rubocop/rubocop/issues/12851): Add `EnforcedStyleForClasses` and `EnforcedStyleForModules` configuration options to `Style/ClassAndModuleChildren`. ([@dvandersluis][]) diff --git a/changelog/change_add_mode_conservative_configuration_to_20250311170524.md b/changelog/change_add_mode_conservative_configuration_to_20250311170524.md deleted file mode 100644 index 191bc71c1998..000000000000 --- a/changelog/change_add_mode_conservative_configuration_to_20250311170524.md +++ /dev/null @@ -1 +0,0 @@ -* [#13979](https://github.com/rubocop/rubocop/pull/13979): Add `Mode: conservative` configuration to `Style/FormatStringToken` to make the cop only register offenses for strings given to `printf`, `sprintf`, `format`, and `%`. ([@dvandersluis][]) diff --git a/changelog/change_allow_tls_1_1_and_tls_1_2_in_naming_variable_number.md b/changelog/change_allow_tls_1_1_and_tls_1_2_in_naming_variable_number.md deleted file mode 100644 index e61258b9fd93..000000000000 --- a/changelog/change_allow_tls_1_1_and_tls_1_2_in_naming_variable_number.md +++ /dev/null @@ -1 +0,0 @@ -* [#13977](https://github.com/rubocop/rubocop/issues/13977): Allow `TLS1_1` and `TLS1_2` by default in `Naming/VariableNumber` to accommodate OpenSSL version parameter names. ([@koic][]) diff --git a/changelog/change_make_lint_redundant_type_conversion_aware_of_redundant_to_d.md b/changelog/change_make_lint_redundant_type_conversion_aware_of_redundant_to_d.md deleted file mode 100644 index 1508e08b58a1..000000000000 --- a/changelog/change_make_lint_redundant_type_conversion_aware_of_redundant_to_d.md +++ /dev/null @@ -1 +0,0 @@ -* [#13967](https://github.com/rubocop/rubocop/pull/13967): Make `Lint/RedundantTypeConversion` aware of redundant `to_d`. ([@koic][]) diff --git a/changelog/fix_a_false_positive_for_lint_shared_mutable_default.md b/changelog/fix_a_false_positive_for_lint_shared_mutable_default.md deleted file mode 100644 index 20ea90ebed2a..000000000000 --- a/changelog/fix_a_false_positive_for_lint_shared_mutable_default.md +++ /dev/null @@ -1 +0,0 @@ -* [#13969](https://github.com/rubocop/rubocop/issues/13969): Fix a false positive for `Lint/SharedMutableDefault` when `capacity` keyword argument is used. ([@koic][]) diff --git a/changelog/fix_double_negation_numblock.md b/changelog/fix_double_negation_numblock.md deleted file mode 100644 index d0d50e8e87cc..000000000000 --- a/changelog/fix_double_negation_numblock.md +++ /dev/null @@ -1 +0,0 @@ -* [#13945](https://github.com/rubocop/rubocop/pull/13945): Fix a false positive for `Style/DoubleNegation` when calling `define_method`/`define_singleton_method` with a numblock. ([@earlopain][]) diff --git a/changelog/fix_false_alarm_for_extracted_cop.md b/changelog/fix_false_alarm_for_extracted_cop.md deleted file mode 100644 index 5ae935c7ff94..000000000000 --- a/changelog/fix_false_alarm_for_extracted_cop.md +++ /dev/null @@ -1 +0,0 @@ -* [#13971](https://github.com/rubocop/rubocop/pull/13971): Fix false alarm for config obsoletion. ([@koic][]) diff --git a/changelog/fix_false_negative_return_in_void_context.md b/changelog/fix_false_negative_return_in_void_context.md deleted file mode 100644 index 7ec6e98dfaac..000000000000 --- a/changelog/fix_false_negative_return_in_void_context.md +++ /dev/null @@ -1 +0,0 @@ -* [#13960](https://github.com/rubocop/rubocop/pull/13960): Fix a false negative for `Lint/ReturnInVoidContext` when returning out of a block. ([@earlopain][]) diff --git a/changelog/fix_false_negative_useless_constant_scoping_sclass.md b/changelog/fix_false_negative_useless_constant_scoping_sclass.md deleted file mode 100644 index 4dedc9060a3f..000000000000 --- a/changelog/fix_false_negative_useless_constant_scoping_sclass.md +++ /dev/null @@ -1 +0,0 @@ -* [#13947](https://github.com/rubocop/rubocop/pull/13947): Fix a false negative for `Lint/UselessConstantScoping` for constants defined in `class << self`. ([@earlopain][]) diff --git a/changelog/fix_false_negatives_nonlocal_exit_numblocks.md b/changelog/fix_false_negatives_nonlocal_exit_numblocks.md deleted file mode 100644 index c0bfb035260c..000000000000 --- a/changelog/fix_false_negatives_nonlocal_exit_numblocks.md +++ /dev/null @@ -1 +0,0 @@ -* [#13949](https://github.com/rubocop/rubocop/pull/13949): Fix a false negative for `Lint/NonLocalExitFromIterator` with numblocks. ([@earlopain][]) diff --git a/changelog/fix_false_positives_for_style_redundant_current_directory_in_path.md b/changelog/fix_false_positives_for_style_redundant_current_directory_in_path.md deleted file mode 100644 index 8ae8fca950de..000000000000 --- a/changelog/fix_false_positives_for_style_redundant_current_directory_in_path.md +++ /dev/null @@ -1 +0,0 @@ -* [#13975](https://github.com/rubocop/rubocop/issues/13975): Fix false positives for `Style/RedundantCurrentDirectoryInPath` when using a complex current directory path in `require_relative`. ([@koic][]) diff --git a/changelog/fix_literal_condition_void_expr.md b/changelog/fix_literal_condition_void_expr.md deleted file mode 100644 index 3b7fa598ad0c..000000000000 --- a/changelog/fix_literal_condition_void_expr.md +++ /dev/null @@ -1 +0,0 @@ -* [#13963](https://github.com/rubocop/rubocop/issues/13963): Fix wrong autocorrect for `Lint/LiteralAsCondition` when the literal is followed by `return`, `break`, or `next`. ([@earlopain][]) diff --git a/changelog/fix_method_call_args_numblock.md b/changelog/fix_method_call_args_numblock.md deleted file mode 100644 index b65fd41e3753..000000000000 --- a/changelog/fix_method_call_args_numblock.md +++ /dev/null @@ -1 +0,0 @@ -* [#13946](https://github.com/rubocop/rubocop/pull/13946): Fix some false positives for `Style/MethodCallWithArgsParentheses` with `EnforcedStyle: omit_parentheses` style and numblocks. ([@earlopain][]) diff --git a/changelog/fix_obsoletion_cache_broken.md b/changelog/fix_obsoletion_cache_broken.md deleted file mode 100644 index 754f892bcbce..000000000000 --- a/changelog/fix_obsoletion_cache_broken.md +++ /dev/null @@ -1 +0,0 @@ -* [#13950](https://github.com/rubocop/rubocop/pull/13950): Fix sporadic errors about `rubocop-rails` or `rubocop-performance` extraction, even if they are already part of the Gemfile. ([@earlopain][]) diff --git a/changelog/fix_prevent_redundant_plugin_loading.md b/changelog/fix_prevent_redundant_plugin_loading.md deleted file mode 100644 index 9d5361fb8820..000000000000 --- a/changelog/fix_prevent_redundant_plugin_loading.md +++ /dev/null @@ -1 +0,0 @@ -* [#13981](https://github.com/rubocop/rubocop/pull/13981): Prevent redundant plugin loading when a duplicate plugin is specified in an inherited config. ([@koic][]) diff --git a/changelog/fix_update_lint_redundant_cop_disable_directive_to_20250310151635.md b/changelog/fix_update_lint_redundant_cop_disable_directive_to_20250310151635.md deleted file mode 100644 index b66f03e56e9e..000000000000 --- a/changelog/fix_update_lint_redundant_cop_disable_directive_to_20250310151635.md +++ /dev/null @@ -1 +0,0 @@ -* [#13965](https://github.com/rubocop/rubocop/issues/13965): Update `Lint/RedundantCopDisableDirective` to register an offense when cop names are given with improper casing. ([@dvandersluis][]) diff --git a/changelog/fix_wrong_autocorrect_rescue_modifier_mlhs.md b/changelog/fix_wrong_autocorrect_rescue_modifier_mlhs.md deleted file mode 100644 index 70213deb37f6..000000000000 --- a/changelog/fix_wrong_autocorrect_rescue_modifier_mlhs.md +++ /dev/null @@ -1 +0,0 @@ -* [#13948](https://github.com/rubocop/rubocop/pull/13948): Fix wrong autocorrect for `Style/RescueModifier` when using parallel assignment and the right-hand-side is not a bracketed array. ([@earlopain][]) diff --git a/changelog/new_add_cop_style_comparable_between.md b/changelog/new_add_cop_style_comparable_between.md deleted file mode 100644 index 400969eae042..000000000000 --- a/changelog/new_add_cop_style_comparable_between.md +++ /dev/null @@ -1 +0,0 @@ -* [#13936](https://github.com/rubocop/rubocop/pull/13936): Adds new cop `Style/ComparableBetween`. ([@lovro-bikic][]) diff --git a/changelog/new_allow_writing_steep_annotaion_to_method_definition.md b/changelog/new_allow_writing_steep_annotaion_to_method_definition.md deleted file mode 100644 index c331d2f11718..000000000000 --- a/changelog/new_allow_writing_steep_annotaion_to_method_definition.md +++ /dev/null @@ -1 +0,0 @@ -* [#13943](https://github.com/rubocop/rubocop/pull/13943): Allow writing steep annotation to method definition for `Style/CommentedKeyword`. ([@dak2][]) From ba27193a24638a20b5ad4df0463170f0c949f34e Mon Sep 17 00:00:00 2001 From: Bozhidar Batsov Date: Thu, 13 Mar 2025 10:23:50 +0200 Subject: [PATCH 28/28] Cut 1.74 --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- CHANGELOG.md | 2 + CONTRIBUTING.md | 2 +- README.md | 2 +- config/default.yml | 6 +- docs/antora.yml | 2 +- docs/modules/ROOT/pages/cops.adoc | 1 + docs/modules/ROOT/pages/cops_lint.adoc | 16 +-- docs/modules/ROOT/pages/cops_naming.adoc | 2 +- docs/modules/ROOT/pages/cops_style.adoc | 122 +++++++++++++++--- docs/modules/ROOT/pages/installation.adoc | 2 +- .../pages/integration_with_other_tools.adoc | 4 +- lib/rubocop/version.rb | 2 +- relnotes/v1.74.0.md | 33 +++++ 14 files changed, 158 insertions(+), 40 deletions(-) create mode 100644 relnotes/v1.74.0.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 4994e6ccb565..2a401681b39c 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.2 (using Parser 3.3.5.0, rubocop-ast 1.32.3, analyzing as Ruby 3.3, running on ruby 3.3.5) [x86_64-linux] +1.74.0 (using Parser 3.3.5.0, rubocop-ast 1.32.3, analyzing as Ruby 3.3, running on ruby 3.3.5) [x86_64-linux] - rubocop-performance 1.22.1 - rubocop-rspec 3.1.0 ``` diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a2cce457d56..a91a53dd029e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ ## master (unreleased) +## 1.74.0 (2025-03-13) + ### New features * [#13936](https://github.com/rubocop/rubocop/pull/13936): Adds new cop `Style/ComparableBetween`. ([@lovro-bikic][]) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f503b2ed5239..9b3d41f61b8a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,7 +17,7 @@ do so. ```console $ rubocop -V -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] +1.74.0 (using Parser 3.3.5.0, rubocop-ast 1.32.3, analyzing as Ruby 3.3, running on ruby 3.3.5) [x86_64-linux] - rubocop-performance 1.22.1 - rubocop-rspec 3.1.0 ``` diff --git a/README.md b/README.md index b44affe73981..94b5c39f6b7a 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ To prevent an unwanted RuboCop update you might want to use a conservative versi in your `Gemfile`: ```rb -gem 'rubocop', '~> 1.73', require: false +gem 'rubocop', '~> 1.74', require: false ``` See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) for further details. diff --git a/config/default.yml b/config/default.yml index a4d03839ecc2..cf1f10b45b0f 100644 --- a/config/default.yml +++ b/config/default.yml @@ -3501,7 +3501,7 @@ Style/ClassAndModuleChildren: SafeAutoCorrect: false Enabled: true VersionAdded: '0.19' - VersionChanged: '<>' + VersionChanged: '1.74' # # Basically there are two different styles: # @@ -3687,7 +3687,7 @@ Style/CommentedKeyword: Style/ComparableBetween: Description: 'Enforces the use of `Comparable#between?` instead of logical comparison.' Enabled: pending - VersionAdded: '<>' + VersionAdded: '1.74' StyleGuide: '#ranges-or-between' Style/ComparableClamp: @@ -4099,7 +4099,7 @@ Style/FormatStringToken: # format` and `%` methods. Other strings are not considered. Mode: aggressive VersionAdded: '0.49' - VersionChanged: '<>' + VersionChanged: '1.74' AllowedMethods: [] AllowedPatterns: [] diff --git a/docs/antora.yml b/docs/antora.yml index 9c897ea03d1d..69f60481ad3a 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.74' nav: - modules/ROOT/nav.adoc diff --git a/docs/modules/ROOT/pages/cops.adoc b/docs/modules/ROOT/pages/cops.adoc index 0e63bd0bce2a..8ad42e71fd58 100644 --- a/docs/modules/ROOT/pages/cops.adoc +++ b/docs/modules/ROOT/pages/cops.adoc @@ -438,6 +438,7 @@ In the following section you find all available cops: * xref:cops_style.adoc#stylecommandliteral[Style/CommandLiteral] * xref:cops_style.adoc#stylecommentannotation[Style/CommentAnnotation] * xref:cops_style.adoc#stylecommentedkeyword[Style/CommentedKeyword] +* xref:cops_style.adoc#stylecomparablebetween[Style/ComparableBetween] * xref:cops_style.adoc#stylecomparableclamp[Style/ComparableClamp] * xref:cops_style.adoc#styleconcatarrayliterals[Style/ConcatArrayLiterals] * xref:cops_style.adoc#styleconditionalassignment[Style/ConditionalAssignment] diff --git a/docs/modules/ROOT/pages/cops_lint.adoc b/docs/modules/ROOT/pages/cops_lint.adoc index f3a1aa8e9661..a2af5268eb07 100644 --- a/docs/modules/ROOT/pages/cops_lint.adoc +++ b/docs/modules/ROOT/pages/cops_lint.adoc @@ -5456,13 +5456,13 @@ warn something | - |=== -Checks for redundant uses of `to_s`, `to_sym`, `to_i`, `to_f`, `to_r`, `to_c`, +Checks for redundant uses of `to_s`, `to_sym`, `to_i`, `to_f`, `to_d`, `to_r`, `to_c`, `to_a`, `to_h`, and `to_set`. When one of these methods is called on an object of the same type, that object is returned, making the call unnecessary. The cop detects conversion methods called on object literals, class constructors, class `[]` methods, and the `Kernel` methods -`String()`, `Integer()`, `Float()`, `Rational()`, `Complex()` and `Array()`. +`String()`, `Integer()`, `Float()`, BigDecimal(), `Rational()`, `Complex()`, and `Array()`. Specifically, these cases are detected for each conversion method: @@ -8108,8 +8108,8 @@ end |=== Checks for useless constant scoping. Private constants must be defined using -`private_constant` or `class << self`. Even if `private` access modifier is used, -it is public scope despite its appearance. +`private_constant`. Even if `private` access modifier is used, it is public scope despite +its appearance. It does not support autocorrection due to behavior change and multiple ways to fix it. Or a public constant may be intended. @@ -8131,14 +8131,6 @@ class Foo private_constant :PRIVATE_CONST end -# good -class Foo - class << self - private - PRIVATE_CONST = 42 - end -end - # good class Foo PUBLIC_CONST = 42 # If private scope is not intended. diff --git a/docs/modules/ROOT/pages/cops_naming.adoc b/docs/modules/ROOT/pages/cops_naming.adoc index 136acb8cdeb7..8a4dc8e2cc65 100644 --- a/docs/modules/ROOT/pages/cops_naming.adoc +++ b/docs/modules/ROOT/pages/cops_naming.adoc @@ -1733,7 +1733,7 @@ expect(Open3).to receive(:capture3) | Boolean | AllowedIdentifiers -| `capture3`, `iso8601`, `rfc1123_date`, `rfc822`, `rfc2822`, `rfc3339`, `x86_64` +| `TLS1_1`, `TLS1_2`, `capture3`, `iso8601`, `rfc1123_date`, `rfc822`, `rfc2822`, `rfc3339`, `x86_64` | Array | AllowedPatterns diff --git a/docs/modules/ROOT/pages/cops_style.adoc b/docs/modules/ROOT/pages/cops_style.adoc index 6abb5be0741d..891a2f28e5ec 100644 --- a/docs/modules/ROOT/pages/cops_style.adoc +++ b/docs/modules/ROOT/pages/cops_style.adoc @@ -1752,11 +1752,23 @@ That's a good use case of ? literal so it doesn't count it as an offense. | Yes | Always (Unsafe) | 0.19 -| - +| 1.74 |=== -Checks the style of children definitions at classes and -modules. Basically there are two different styles: +Checks that namespaced classes and modules are defined with a consistent style. + +With `nested` style, classes and modules should be defined separately (one constant +on each line, without `::`). With `compact` style, classes and modules should be +defined with fully qualified names (using `::` for namespaces). + +NOTE: The style chosen will affect `Module.nesting` for the class or module. Using +`nested` style will result in each level being added, whereas `compact` style will +only include the fully qualified class or module name. + +By default, `EnforcedStyle` applies to both classes and modules. If desired, separate +styles can be defined for classes and modules by using `EnforcedStyleForClasses` and +`EnforcedStyleForModules` respectively. If not set, or set to nil, the `EnforcedStyle` +value will be used. The compact style is only forced for classes/modules with one child. @@ -1765,8 +1777,8 @@ The compact style is only forced for classes/modules with one child. Autocorrection is unsafe. -Moving from compact to nested children requires knowledge of whether the -outer parent is a module or a class. Moving from nested to compact requires +Moving from `compact` to `nested` children requires knowledge of whether the +outer parent is a module or a class. Moving from `nested` to `compact` requires verification that the outer parent is defined elsewhere. RuboCop does not have the knowledge to perform either operation safely and thus requires manual oversight. @@ -1807,6 +1819,14 @@ end | EnforcedStyle | `nested` | `nested`, `compact` + +| EnforcedStyleForClasses +| `` +| ``, `nested`, `compact` + +| EnforcedStyleForModules +| `` +| ``, `nested`, `compact` |=== [#references-styleclassandmodulechildren] @@ -2775,8 +2795,8 @@ Checks for comments put on the same line as some keywords. These keywords are: `class`, `module`, `def`, `begin`, `end`. Note that some comments -(`:nodoc:`, `:yields:`, `rubocop:disable` and `rubocop:todo`) -and RBS::Inline annotation comments are allowed. +(`:nodoc:`, `:yields:`, `rubocop:disable` and `rubocop:todo`), +RBS::Inline annotation, and Steep annotation (`steep:ignore`) are allowed. Autocorrection removes comments from `end` keyword and keeps comments for `class`, `module`, `def` and `begin` above the keyword. @@ -2816,6 +2836,45 @@ class X # :nodoc: end ---- +[#stylecomparablebetween] +== Style/ComparableBetween + +|=== +| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed + +| Pending +| Yes +| Always +| 1.74 +| - +|=== + +Checks for logical comparison which can be replaced with `Comparable#between?`. + +NOTE: `Comparable#between?` is on average slightly slower than logical comparison, +although the difference generally isn't observable. If you require maximum +performance, consider using logical comparison. + +[#examples-stylecomparablebetween] +=== Examples + +[source,ruby] +---- +# bad +x >= min && x <= max + +# bad +x <= max && x >= min + +# good +x.between?(min, max) +---- + +[#references-stylecomparablebetween] +=== References + +* https://rubystyle.guide#ranges-or-between + [#stylecomparableclamp] == Style/ComparableClamp @@ -5787,19 +5846,27 @@ puts '%10s' % 'foo' | Yes | Always | 0.49 -| 1.0 +| 1.74 |=== -Use a consistent style for named format string tokens. +Use a consistent style for tokens within a format string. -NOTE: `unannotated` style cop only works for strings -which are passed as arguments to those methods: -`printf`, `sprintf`, `format`, `%`. -The reason is that _unannotated_ format is very similar -to encoded URLs or Date/Time formatting strings. +By default, all strings are evaluated. In some cases, this may be undesirable, +as they could be used as arguments to a method that does not consider +them to be tokens, but rather other identifiers or just part of the string. -This cop's allowed methods can be customized with `AllowedMethods`. -By default, there are no allowed methods. +`AllowedMethods` or `AllowedPatterns` can be configured with in order to mark specific +methods as always allowed, thereby avoiding an offense from the cop. By default, there +are no allowed methods. + +Additionally, the cop can be made conservative by configuring it with +`Mode: conservative` (default `aggressive`). In this mode, tokens (regardless +of `EnforcedStyle`) are only considered if used in the format string argument to the +methods `printf`, `sprintf`, `format` and `%`. + +NOTE: Tokens in the `unannotated` style (eg. `%s`) are always treated as if +configured with `Conservative: true`. This is done in order to prevent false positives, +because this format is very similar to encoded URLs or Date/Time formatting strings. It is allowed to contain unannotated token if the number of them is less than or equals to @@ -5908,6 +5975,25 @@ redirect('foo/%{bar_id}') redirect('foo/%{bar_id}') ---- +[#mode_-conservative_-enforcedstyle_-annotated-styleformatstringtoken] +==== Mode: conservative, EnforcedStyle: annotated + +[source,ruby] +---- +# In `conservative` mode, offenses are only registered for strings +# given to a known formatting method. + +# good +"%{greeting}" +foo("%{greeting}") + +# bad +format("%{greeting}", greeting: 'Hello') +printf("%{greeting}", greeting: 'Hello') +sprintf("%{greeting}", greeting: 'Hello') +"%{greeting}" % { greeting: 'Hello' } +---- + [#configurable-attributes-styleformatstringtoken] === Configurable attributes @@ -5922,6 +6008,10 @@ redirect('foo/%{bar_id}') | `1` | Integer +| Mode +| `aggressive` +| String + | AllowedMethods | `[]` | Array diff --git a/docs/modules/ROOT/pages/installation.adoc b/docs/modules/ROOT/pages/installation.adoc index 7397758c172e..2905859cbe7e 100644 --- a/docs/modules/ROOT/pages/installation.adoc +++ b/docs/modules/ROOT/pages/installation.adoc @@ -22,7 +22,7 @@ in your `Gemfile`: [source,rb] ---- -gem 'rubocop', '~> 1.73', require: false +gem 'rubocop', '~> 1.74', require: false ---- .A Modular RuboCop diff --git a/docs/modules/ROOT/pages/integration_with_other_tools.adoc b/docs/modules/ROOT/pages/integration_with_other_tools.adoc index 0270a958373a..8ec8304e1d1f 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.2 + rev: v1.74.0 hooks: - id: rubocop ---- @@ -136,7 +136,7 @@ entries in `additional_dependencies`: [source,yaml] ---- - repo: https://github.com/rubocop/rubocop - rev: v1.73.2 + rev: v1.74.0 hooks: - id: rubocop additional_dependencies: diff --git a/lib/rubocop/version.rb b/lib/rubocop/version.rb index fa04e384ff0b..98e89aaeb980 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.2' + STRING = '1.74.0' MSG = '%s (using %s, ' \ 'rubocop-ast %s, ' \ diff --git a/relnotes/v1.74.0.md b/relnotes/v1.74.0.md new file mode 100644 index 000000000000..304ad27bea38 --- /dev/null +++ b/relnotes/v1.74.0.md @@ -0,0 +1,33 @@ +### New features + +* [#13936](https://github.com/rubocop/rubocop/pull/13936): Adds new cop `Style/ComparableBetween`. ([@lovro-bikic][]) +* [#13943](https://github.com/rubocop/rubocop/pull/13943): Allow writing steep annotation to method definition for `Style/CommentedKeyword`. ([@dak2][]) + +### Bug fixes + +* [#13969](https://github.com/rubocop/rubocop/issues/13969): Fix a false positive for `Lint/SharedMutableDefault` when `capacity` keyword argument is used. ([@koic][]) +* [#13945](https://github.com/rubocop/rubocop/pull/13945): Fix a false positive for `Style/DoubleNegation` when calling `define_method`/`define_singleton_method` with a numblock. ([@earlopain][]) +* [#13971](https://github.com/rubocop/rubocop/pull/13971): Fix false alarm for config obsoletion. ([@koic][]) +* [#13960](https://github.com/rubocop/rubocop/pull/13960): Fix a false negative for `Lint/ReturnInVoidContext` when returning out of a block. ([@earlopain][]) +* [#13947](https://github.com/rubocop/rubocop/pull/13947): Fix a false negative for `Lint/UselessConstantScoping` for constants defined in `class << self`. ([@earlopain][]) +* [#13949](https://github.com/rubocop/rubocop/pull/13949): Fix a false negative for `Lint/NonLocalExitFromIterator` with numblocks. ([@earlopain][]) +* [#13975](https://github.com/rubocop/rubocop/issues/13975): Fix false positives for `Style/RedundantCurrentDirectoryInPath` when using a complex current directory path in `require_relative`. ([@koic][]) +* [#13963](https://github.com/rubocop/rubocop/issues/13963): Fix wrong autocorrect for `Lint/LiteralAsCondition` when the literal is followed by `return`, `break`, or `next`. ([@earlopain][]) +* [#13946](https://github.com/rubocop/rubocop/pull/13946): Fix some false positives for `Style/MethodCallWithArgsParentheses` with `EnforcedStyle: omit_parentheses` style and numblocks. ([@earlopain][]) +* [#13950](https://github.com/rubocop/rubocop/pull/13950): Fix sporadic errors about `rubocop-rails` or `rubocop-performance` extraction, even if they are already part of the Gemfile. ([@earlopain][]) +* [#13981](https://github.com/rubocop/rubocop/pull/13981): Prevent redundant plugin loading when a duplicate plugin is specified in an inherited config. ([@koic][]) +* [#13965](https://github.com/rubocop/rubocop/issues/13965): Update `Lint/RedundantCopDisableDirective` to register an offense when cop names are given with improper casing. ([@dvandersluis][]) +* [#13948](https://github.com/rubocop/rubocop/pull/13948): Fix wrong autocorrect for `Style/RescueModifier` when using parallel assignment and the right-hand-side is not a bracketed array. ([@earlopain][]) + +### Changes + +* [#12851](https://github.com/rubocop/rubocop/issues/12851): Add `EnforcedStyleForClasses` and `EnforcedStyleForModules` configuration options to `Style/ClassAndModuleChildren`. ([@dvandersluis][]) +* [#13979](https://github.com/rubocop/rubocop/pull/13979): Add `Mode: conservative` configuration to `Style/FormatStringToken` to make the cop only register offenses for strings given to `printf`, `sprintf`, `format`, and `%`. ([@dvandersluis][]) +* [#13977](https://github.com/rubocop/rubocop/issues/13977): Allow `TLS1_1` and `TLS1_2` by default in `Naming/VariableNumber` to accommodate OpenSSL version parameter names. ([@koic][]) +* [#13967](https://github.com/rubocop/rubocop/pull/13967): Make `Lint/RedundantTypeConversion` aware of redundant `to_d`. ([@koic][]) + +[@lovro-bikic]: https://github.com/lovro-bikic +[@dak2]: https://github.com/dak2 +[@koic]: https://github.com/koic +[@earlopain]: https://github.com/earlopain +[@dvandersluis]: https://github.com/dvandersluis