diff --git a/changelog/change_update_metricsblocklength_and.md b/changelog/change_update_metricsblocklength_and.md index bc446729cfb..f20825753bd 100644 --- a/changelog/change_update_metricsblocklength_and.md +++ b/changelog/change_update_metricsblocklength_and.md @@ -1 +1,2 @@ * [#9098](https://github.com/rubocop-hq/rubocop/pull/9098): Update `Metrics/BlockLength` and `Metrics/MethodLength` to use `IgnoredMethods` instead of `ExcludedMethods` in configuration. The previous key is retained for backwards compatibility. ([@dvandersluis][]) +* [#9098](https://github.com/rubocop-hq/rubocop/pull/9098): Update `IgnoredMethods` so that every cop that uses it will accept both strings and regexes in the configuration. ([@dvandersluis][]) diff --git a/lib/rubocop/cop/metrics/block_length.rb b/lib/rubocop/cop/metrics/block_length.rb index 84057672a7a..8d80547a482 100644 --- a/lib/rubocop/cop/metrics/block_length.rb +++ b/lib/rubocop/cop/metrics/block_length.rb @@ -44,7 +44,8 @@ class BlockLength < Base LABEL = 'Block' def on_block(node) - return if excluded_method?(node) + return if ignored_method?(node.method_name) + return if method_receiver_excluded?(node) return if node.class_constructor? || node.struct_constructor? check_code_length(node) @@ -52,11 +53,13 @@ def on_block(node) private - def excluded_method?(node) + def method_receiver_excluded?(node) node_receiver = node.receiver&.source&.gsub(/\s+/, '') node_method = String(node.method_name) ignored_methods.any? do |config| + next unless config.is_a?(String) + receiver, method = config.split('.') unless method diff --git a/lib/rubocop/cop/metrics/method_length.rb b/lib/rubocop/cop/metrics/method_length.rb index aafb7ba2325..024d26302de 100644 --- a/lib/rubocop/cop/metrics/method_length.rb +++ b/lib/rubocop/cop/metrics/method_length.rb @@ -41,7 +41,7 @@ class MethodLength < Base LABEL = 'Method' def on_def(node) - return if ignored_methods.any? { |m| m.match? String(node.method_name) } + return if ignored_method?(node.method_name) check_code_length(node) end diff --git a/lib/rubocop/cop/mixin/ignored_methods.rb b/lib/rubocop/cop/mixin/ignored_methods.rb index d6957dff035..e6b13a989a0 100644 --- a/lib/rubocop/cop/mixin/ignored_methods.rb +++ b/lib/rubocop/cop/mixin/ignored_methods.rb @@ -4,6 +4,8 @@ module RuboCop module Cop # This module encapsulates the ability to ignore certain methods when # parsing. + # Cops that use `IgnoredMethods` can accept either strings or regexes to match + # against. module IgnoredMethods # Configuration for IgnoredMethods. It is added to classes that include # the module so that configuration can be set using the `ignored_methods` @@ -21,7 +23,14 @@ def self.included(base) end def ignored_method?(name) - ignored_methods.include?(name.to_s) + ignored_methods.any? do |value| + case value + when Regexp + value.match? String(name) + else + value == String(name) + end + end end def ignored_methods diff --git a/lib/rubocop/cop/mixin/method_complexity.rb b/lib/rubocop/cop/mixin/method_complexity.rb index c02b4d299e3..9a680c4e187 100644 --- a/lib/rubocop/cop/mixin/method_complexity.rb +++ b/lib/rubocop/cop/mixin/method_complexity.rb @@ -11,6 +11,12 @@ module MethodComplexity include Metrics::Utils::RepeatedCsendDiscount extend NodePattern::Macros + # Ensure cops that include `MethodComplexity` have the config + # `attr_accessor`s that `ignored_method?` needs. + def self.included(base) + base.extend(IgnoredMethods::Config) + end + def on_def(node) return if ignored_method?(node.method_name) diff --git a/spec/rubocop/cop/lint/number_conversion_spec.rb b/spec/rubocop/cop/lint/number_conversion_spec.rb index 949d596a314..96405b2d27b 100644 --- a/spec/rubocop/cop/lint/number_conversion_spec.rb +++ b/spec/rubocop/cop/lint/number_conversion_spec.rb @@ -158,19 +158,38 @@ end context 'IgnoredMethods' do - let(:cop_config) { { 'IgnoredMethods' => %w[minutes] } } - - it 'does not register an offense for an ignored method' do - expect_no_offenses(<<~RUBY) - 10.minutes.to_i - RUBY - end - - it 'registers an offense for other methods' do - expect_offense(<<~RUBY) - 10.hours.to_i - ^^^^^^^^^^^^^ Replace unsafe number conversion with number class parsing, instead of using 10.hours.to_i, use stricter Integer(10.hours, 10). - RUBY + context 'with a string' do + let(:cop_config) { { 'IgnoredMethods' => %w[minutes] } } + + it 'does not register an offense for an ignored method' do + expect_no_offenses(<<~RUBY) + 10.minutes.to_i + RUBY + end + + it 'registers an offense for other methods' do + expect_offense(<<~RUBY) + 10.hours.to_i + ^^^^^^^^^^^^^ Replace unsafe number conversion with number class parsing, instead of using 10.hours.to_i, use stricter Integer(10.hours, 10). + RUBY + end + end + + context 'with a regex' do + let(:cop_config) { { 'IgnoredMethods' => [/minutes/] } } + + it 'does not register an offense for an ignored method' do + expect_no_offenses(<<~RUBY) + 10.minutes.to_i + RUBY + end + + it 'registers an offense for other methods' do + expect_offense(<<~RUBY) + 10.hours.to_i + ^^^^^^^^^^^^^ Replace unsafe number conversion with number class parsing, instead of using 10.hours.to_i, use stricter Integer(10.hours, 10). + RUBY + end end end end diff --git a/spec/rubocop/cop/metrics/abc_size_spec.rb b/spec/rubocop/cop/metrics/abc_size_spec.rb index 605e4f6c3fb..b1b82cdd1b3 100644 --- a/spec/rubocop/cop/metrics/abc_size_spec.rb +++ b/spec/rubocop/cop/metrics/abc_size_spec.rb @@ -78,30 +78,60 @@ def method_name end context 'when method is in list of ignored methods' do - let(:cop_config) { { 'Max' => 0, 'IgnoredMethods' => ['foo'] } } + context 'when given a string' do + let(:cop_config) { { 'Max' => 0, 'IgnoredMethods' => ['foo'] } } + + it 'does not register an offense when defining an instance method' do + expect_no_offenses(<<~RUBY) + def foo + bar.baz(:qux) + end + RUBY + end - it 'does not register an offense when defining an instance method' do - expect_no_offenses(<<~RUBY) - def foo - bar.baz(:qux) - end - RUBY - end + it 'does not register an offense when defining a class method' do + expect_no_offenses(<<~RUBY) + def self.foo + bar.baz(:qux) + end + RUBY + end - it 'does not register an offense when defining a class method' do - expect_no_offenses(<<~RUBY) - def self.foo - bar.baz(:qux) - end - RUBY + it 'does not register an offense when using `define_method`' do + expect_no_offenses(<<~RUBY) + define_method :foo do + bar.baz(:qux) + end + RUBY + end end - it 'does not register an offense when using `define_method`' do - expect_no_offenses(<<~RUBY) - define_method :foo do - bar.baz(:qux) - end - RUBY + context 'when given a regex' do + let(:cop_config) { { 'Max' => 0, 'IgnoredMethods' => [/foo/] } } + + it 'does not register an offense when defining an instance method' do + expect_no_offenses(<<~RUBY) + def foo + bar.baz(:qux) + end + RUBY + end + + it 'does not register an offense when defining a class method' do + expect_no_offenses(<<~RUBY) + def self.foo + bar.baz(:qux) + end + RUBY + end + + it 'does not register an offense when using `define_method`' do + expect_no_offenses(<<~RUBY) + define_method :foo do + bar.baz(:qux) + end + RUBY + end end end end diff --git a/spec/rubocop/cop/metrics/block_length_spec.rb b/spec/rubocop/cop/metrics/block_length_spec.rb index bc68e6f4af0..63b9e537b69 100644 --- a/spec/rubocop/cop/metrics/block_length_spec.rb +++ b/spec/rubocop/cop/metrics/block_length_spec.rb @@ -249,5 +249,32 @@ def foo end end end + + context 'when given a regex' do + before { cop_config['IgnoredMethods'] = [/baz/] } + + it 'does not report an offense' do + expect_no_offenses(<<~RUBY) + Foo::Bar.baz do + a = 1 + a = 2 + a = 3 + end + RUBY + end + + context 'that does not match' do + it 'reports an offense' do + expect_offense(<<~RUBY) + Foo::Bar.bar do + ^^^^^^^^^^^^^^^ Block has too many lines. [3/2] + a = 1 + a = 2 + a = 3 + end + RUBY + end + end + end end end diff --git a/spec/rubocop/cop/metrics/cyclomatic_complexity_spec.rb b/spec/rubocop/cop/metrics/cyclomatic_complexity_spec.rb index 57ed75c65cf..4f9a18a6445 100644 --- a/spec/rubocop/cop/metrics/cyclomatic_complexity_spec.rb +++ b/spec/rubocop/cop/metrics/cyclomatic_complexity_spec.rb @@ -292,31 +292,61 @@ def method_name_2 end end - context 'when method is in list of ignored methods' do - let(:cop_config) { { 'Max' => 0, 'IgnoredMethods' => ['foo'] } } + context 'when IgnoredMethods is set' do + context 'with a string' do + let(:cop_config) { { 'Max' => 0, 'IgnoredMethods' => ['foo'] } } + + it 'does not register an offense when defining an instance method' do + expect_no_offenses(<<~RUBY) + def foo + bar.baz(:qux) + end + RUBY + end - it 'does not register an offense when defining an instance method' do - expect_no_offenses(<<~RUBY) - def foo - bar.baz(:qux) - end - RUBY - end + it 'does not register an offense when defining a class method' do + expect_no_offenses(<<~RUBY) + def self.foo + bar.baz(:qux) + end + RUBY + end - it 'does not register an offense when defining a class method' do - expect_no_offenses(<<~RUBY) - def self.foo - bar.baz(:qux) - end - RUBY + it 'does not register an offense when using `define_method`' do + expect_no_offenses(<<~RUBY) + define_method :foo do + bar.baz(:qux) + end + RUBY + end end - it 'does not register an offense when using `define_method`' do - expect_no_offenses(<<~RUBY) - define_method :foo do - bar.baz(:qux) - end - RUBY + context 'with a regex' do + let(:cop_config) { { 'Max' => 0, 'IgnoredMethods' => [/foo/] } } + + it 'does not register an offense when defining an instance method' do + expect_no_offenses(<<~RUBY) + def foo + bar.baz(:qux) + end + RUBY + end + + it 'does not register an offense when defining a class method' do + expect_no_offenses(<<~RUBY) + def self.foo + bar.baz(:qux) + end + RUBY + end + + it 'does not register an offense when using `define_method`' do + expect_no_offenses(<<~RUBY) + define_method :foo do + bar.baz(:qux) + end + RUBY + end end end diff --git a/spec/rubocop/cop/style/block_delimiters_spec.rb b/spec/rubocop/cop/style/block_delimiters_spec.rb index 30e25cf1952..77ac3768fb6 100644 --- a/spec/rubocop/cop/style/block_delimiters_spec.rb +++ b/spec/rubocop/cop/style/block_delimiters_spec.rb @@ -34,7 +34,7 @@ 'EnforcedStyle' => 'semantic', 'ProceduralMethods' => %w[tap], 'FunctionalMethods' => %w[let], - 'IgnoredMethods' => %w[lambda] + 'IgnoredMethods' => ['lambda', /test/] } let(:cop_config) { cop_config } @@ -168,8 +168,7 @@ RUBY end - it 'accepts a multi-line functional block with do-end if it is ' \ - 'an ignored method' do + it 'accepts a multi-line functional block with do-end if it is an ignored method' do expect_no_offenses(<<~RUBY) foo = lambda do puts 42 @@ -177,6 +176,14 @@ RUBY end + it 'accepts a multi-line functional block with do-end if it is an ignored method by regex' do + expect_no_offenses(<<~RUBY) + foo = test_method do + puts 42 + end + RUBY + end + context 'with a procedural one-line block' do context 'with AllowBracesOnProceduralOneLiners false or unset' do it 'registers an offense for a single line procedural block' do @@ -303,7 +310,7 @@ context 'line count-based style' do cop_config = { 'EnforcedStyle' => 'line_count_based', - 'IgnoredMethods' => %w[proc] + 'IgnoredMethods' => ['proc', /test/] } let(:cop_config) { cop_config } @@ -383,6 +390,14 @@ RUBY end + it 'accepts a multi-line functional block with {} if it is an ignored method by regex' do + expect_no_offenses(<<~RUBY) + foo = test_method { + puts 42 + } + RUBY + end + it 'registers an offense for braces if do-end would not change ' \ 'the meaning' do expect_offense(<<~RUBY) @@ -455,7 +470,7 @@ context 'braces for chaining style' do cop_config = { 'EnforcedStyle' => 'braces_for_chaining', - 'IgnoredMethods' => %w[proc] + 'IgnoredMethods' => ['proc', /test/] } let(:cop_config) { cop_config } @@ -484,6 +499,14 @@ RUBY end + it 'accepts a multi-line functional block with {} if it is an ignored method by regex' do + expect_no_offenses(<<~RUBY) + foo = test_method { + puts 42 + } + RUBY + end + it 'allows when :[] is chained' do expect_no_offenses(<<~RUBY) foo = [{foo: :bar}].find { |h| @@ -574,7 +597,7 @@ context 'always braces' do cop_config = { 'EnforcedStyle' => 'always_braces', - 'IgnoredMethods' => %w[proc] + 'IgnoredMethods' => ['proc', /test/] } let(:cop_config) { cop_config } @@ -649,6 +672,15 @@ RUBY end + it 'accepts a multi-line functional block with do-end if it is ' \ + 'an ignored method by regex' do + expect_no_offenses(<<~RUBY) + foo = test_method do + puts 42 + end + RUBY + end + context 'when there are braces around a multi-line block' do it 'allows in the simple case' do expect_no_offenses(<<~RUBY) diff --git a/spec/rubocop/cop/style/class_equality_comparison_spec.rb b/spec/rubocop/cop/style/class_equality_comparison_spec.rb index d402e0df67c..632de769909 100644 --- a/spec/rubocop/cop/style/class_equality_comparison_spec.rb +++ b/spec/rubocop/cop/style/class_equality_comparison_spec.rb @@ -67,17 +67,34 @@ end context 'when IgnoredMethods is specified' do - let(:cop_config) do - { 'IgnoredMethods' => ['=='] } + context 'with a string' do + let(:cop_config) do + { 'IgnoredMethods' => ['=='] } + end + + it 'does not register an offense when comparing class for equality' do + expect_no_offenses(<<~RUBY) + def ==(other) + self.class == other.class && + name == other.name + end + RUBY + end end - it 'does not register an offense when comparing class for equality' do - expect_no_offenses(<<~RUBY) - def ==(other) - self.class == other.class && - name == other.name - end - RUBY + context 'with a regex' do + let(:cop_config) do + { 'IgnoredMethods' => [/equal/] } + end + + it 'does not register an offense when comparing class for equality' do + expect_no_offenses(<<~RUBY) + def equal?(other) + self.class == other.class && + name == other.name + end + RUBY + end end end end 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 d3c4953639b..e0f9a3578b1 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 @@ -2,10 +2,6 @@ RSpec.describe RuboCop::Cop::Style::MethodCallWithArgsParentheses, :config do context 'when EnforcedStyle is require_parentheses (default)' do - let(:cop_config) do - { 'IgnoredMethods' => %w[puts] } - end - it 'accepts no parens in method call without args' do expect_no_offenses('top.test') end @@ -256,8 +252,22 @@ def my_method RUBY end - it 'ignores method listed in IgnoredMethods' do - expect_no_offenses('puts :test') + context 'with IgnoredMethods' do + context 'with a string' do + let(:cop_config) { { 'IgnoredMethods' => %w[puts] } } + + it 'ignores method listed in IgnoredMethods' do + expect_no_offenses('puts :test') + end + end + + context 'with a regex' do + let(:cop_config) { { 'IgnoredMethods' => [/puts/] } } + + it 'ignores method listed in IgnoredMethods' do + expect_no_offenses('puts :test') + end + end end context 'when inspecting macro methods' do diff --git a/spec/rubocop/cop/style/method_call_without_args_parentheses_spec.rb b/spec/rubocop/cop/style/method_call_without_args_parentheses_spec.rb index b34194b6f2e..345a4e1e9ab 100644 --- a/spec/rubocop/cop/style/method_call_without_args_parentheses_spec.rb +++ b/spec/rubocop/cop/style/method_call_without_args_parentheses_spec.rb @@ -1,10 +1,6 @@ # frozen_string_literal: true RSpec.describe RuboCop::Cop::Style::MethodCallWithoutArgsParentheses, :config do - let(:cop_config) do - { 'IgnoredMethods' => %w[s] } - end - it 'registers an offense for parens in method call without args' do expect_offense(<<~RUBY) top.test() @@ -33,8 +29,26 @@ expect_no_offenses('not(something)') end - it 'ignores method listed in IgnoredMethods' do - expect_no_offenses('s()') + context 'with IgnoredMethods' do + context 'with a string' do + let(:cop_config) do + { 'IgnoredMethods' => %w[s] } + end + + it 'ignores method listed in IgnoredMethods' do + expect_no_offenses('s()') + end + end + + context 'with a regex' do + let(:cop_config) do + { 'IgnoredMethods' => [/test/] } + end + + it 'ignores method listed in IgnoredMethods' do + expect_no_offenses('my_test()') + end + end end context 'assignment to a variable with the same name' do diff --git a/spec/rubocop/cop/style/numeric_predicate_spec.rb b/spec/rubocop/cop/style/numeric_predicate_spec.rb index cb141694571..2881ce882ef 100644 --- a/spec/rubocop/cop/style/numeric_predicate_spec.rb +++ b/spec/rubocop/cop/style/numeric_predicate_spec.rb @@ -220,7 +220,7 @@ def m(foo) { 'EnforcedStyle' => 'predicate', 'AutoCorrect' => true, - 'IgnoredMethods' => %w[where] + 'IgnoredMethods' => ['where', /order/] } end @@ -262,12 +262,24 @@ def m(foo) context 'in argument' do context 'ignored method' do - it 'allows checking if a number is positive' do - expect_no_offenses('where(Sequel[:number] > 0)') + context 'with a string' do + it 'allows checking if a number is positive' do + expect_no_offenses('where(Sequel[:number] > 0)') + end + + it 'allows checking if a number is negative' do + expect_no_offenses('where(Sequel[:number] < 0)') + end end - it 'allows checking if a number is negative' do - expect_no_offenses('where(Sequel[:number] < 0)') + context 'with a regex' do + it 'allows checking if a number is positive' do + expect_no_offenses('order(Sequel[:number] > 0)') + end + + it 'allows checking if a number is negative' do + expect_no_offenses('order(Sequel[:number] < 0)') + end end end @@ -296,12 +308,24 @@ def m(foo) context 'in block' do context 'ignored method' do - it 'allows checking if a number is positive' do - expect_no_offenses('where { table[number] > 0 }') + context 'with a string' do + it 'allows checking if a number is positive' do + expect_no_offenses('where { table[number] > 0 }') + end + + it 'allows checking if a number is negative' do + expect_no_offenses('where { table[number] < 0 }') + end end - it 'allows checking if a number is negative' do - expect_no_offenses('where { table[number] < 0 }') + context 'with a regex' do + it 'allows checking if a number is positive' do + expect_no_offenses('order { table[number] > 0 }') + end + + it 'allows checking if a number is negative' do + expect_no_offenses('order { table[number] < 0 }') + end end end diff --git a/spec/rubocop/cop/style/symbol_proc_spec.rb b/spec/rubocop/cop/style/symbol_proc_spec.rb index 8e424803295..1deb81e6c39 100644 --- a/spec/rubocop/cop/style/symbol_proc_spec.rb +++ b/spec/rubocop/cop/style/symbol_proc_spec.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true RSpec.describe RuboCop::Cop::Style::SymbolProc, :config do - let(:cop_config) { { 'IgnoredMethods' => %w[respond_to] } } - it 'registers an offense for a block with parameterless method call on ' \ 'param' do expect_offense(<<~RUBY) @@ -46,8 +44,22 @@ expect_no_offenses('::Proc.new { |x| x.method }') end - it 'accepts ignored method' do - expect_no_offenses('respond_to { |format| format.xml }') + context 'with IgnoredMethods' do + context 'when given a string' do + let(:cop_config) { { 'IgnoredMethods' => %w[respond_to] } } + + it 'accepts ignored method' do + expect_no_offenses('respond_to { |format| format.xml }') + end + end + + context 'when given a regex' do + let(:cop_config) { { 'IgnoredMethods' => [/respond_/] } } + + it 'accepts ignored method' do + expect_no_offenses('respond_to { |format| format.xml }') + end + end end it 'accepts block with no arguments' do