rubocop 1.7.0 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +4 -3
  4. data/config/default.yml +137 -31
  5. data/config/obsoletion.yml +4 -0
  6. data/lib/rubocop.rb +14 -1
  7. data/lib/rubocop/cli/command/auto_genenerate_config.rb +5 -4
  8. data/lib/rubocop/comment_config.rb +6 -6
  9. data/lib/rubocop/config.rb +5 -2
  10. data/lib/rubocop/config_loader.rb +7 -14
  11. data/lib/rubocop/config_store.rb +12 -1
  12. data/lib/rubocop/cop/base.rb +2 -1
  13. data/lib/rubocop/cop/exclude_limit.rb +26 -0
  14. data/lib/rubocop/cop/gemspec/date_assignment.rb +56 -0
  15. data/lib/rubocop/cop/generator.rb +1 -3
  16. data/lib/rubocop/cop/internal_affairs.rb +5 -1
  17. data/lib/rubocop/cop/internal_affairs/empty_line_between_expect_offense_and_correction.rb +68 -0
  18. data/lib/rubocop/cop/internal_affairs/example_description.rb +89 -0
  19. data/lib/rubocop/cop/internal_affairs/redundant_described_class_as_subject.rb +61 -0
  20. data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +64 -0
  21. data/lib/rubocop/cop/layout/class_structure.rb +7 -2
  22. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +38 -18
  23. data/lib/rubocop/cop/layout/first_argument_indentation.rb +16 -2
  24. data/lib/rubocop/cop/layout/line_length.rb +2 -1
  25. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +26 -0
  26. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -2
  27. data/lib/rubocop/cop/layout/space_before_brackets.rb +19 -16
  28. data/lib/rubocop/cop/lint/debugger.rb +58 -14
  29. data/lib/rubocop/cop/lint/deprecated_constants.rb +80 -0
  30. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +13 -4
  31. data/lib/rubocop/cop/lint/duplicate_require.rb +2 -2
  32. data/lib/rubocop/cop/lint/else_layout.rb +1 -1
  33. data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +44 -0
  34. data/lib/rubocop/cop/lint/multiple_comparison.rb +4 -4
  35. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +10 -6
  36. data/lib/rubocop/cop/lint/number_conversion.rb +41 -6
  37. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +47 -0
  38. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +39 -0
  39. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +2 -1
  40. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +50 -0
  41. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +5 -3
  42. data/lib/rubocop/cop/lint/symbol_conversion.rb +103 -0
  43. data/lib/rubocop/cop/lint/triple_quotes.rb +71 -0
  44. data/lib/rubocop/cop/message_annotator.rb +4 -1
  45. data/lib/rubocop/cop/metrics/block_nesting.rb +2 -2
  46. data/lib/rubocop/cop/metrics/parameter_lists.rb +5 -2
  47. data/lib/rubocop/cop/mixin/allowed_identifiers.rb +18 -0
  48. data/lib/rubocop/cop/mixin/check_line_breakable.rb +5 -0
  49. data/lib/rubocop/cop/mixin/code_length.rb +3 -1
  50. data/lib/rubocop/cop/mixin/comments_help.rb +1 -11
  51. data/lib/rubocop/cop/mixin/configurable_max.rb +1 -0
  52. data/lib/rubocop/cop/mixin/first_element_line_break.rb +1 -1
  53. data/lib/rubocop/cop/mixin/method_complexity.rb +3 -1
  54. data/lib/rubocop/cop/mixin/preferred_delimiters.rb +2 -2
  55. data/lib/rubocop/cop/mixin/uncommunicative_name.rb +5 -1
  56. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +38 -5
  57. data/lib/rubocop/cop/naming/variable_name.rb +2 -0
  58. data/lib/rubocop/cop/naming/variable_number.rb +2 -9
  59. data/lib/rubocop/cop/registry.rb +1 -1
  60. data/lib/rubocop/cop/severity.rb +3 -3
  61. data/lib/rubocop/cop/style/ascii_comments.rb +1 -1
  62. data/lib/rubocop/cop/style/constant_visibility.rb +27 -0
  63. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +49 -9
  64. data/lib/rubocop/cop/style/double_negation.rb +2 -2
  65. data/lib/rubocop/cop/style/empty_literal.rb +6 -2
  66. data/lib/rubocop/cop/style/endless_method.rb +102 -0
  67. data/lib/rubocop/cop/style/eval_with_location.rb +138 -49
  68. data/lib/rubocop/cop/style/explicit_block_argument.rb +11 -1
  69. data/lib/rubocop/cop/style/exponential_notation.rb +6 -7
  70. data/lib/rubocop/cop/style/float_division.rb +3 -0
  71. data/lib/rubocop/cop/style/format_string_token.rb +18 -2
  72. data/lib/rubocop/cop/style/hash_conversion.rb +81 -0
  73. data/lib/rubocop/cop/style/hash_like_case.rb +2 -1
  74. data/lib/rubocop/cop/style/if_inside_else.rb +22 -10
  75. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +120 -0
  76. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +4 -0
  77. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +4 -0
  78. data/lib/rubocop/cop/style/nil_comparison.rb +3 -0
  79. data/lib/rubocop/cop/style/non_nil_check.rb +23 -13
  80. data/lib/rubocop/cop/style/numeric_literals.rb +6 -9
  81. data/lib/rubocop/cop/style/numeric_predicate.rb +1 -1
  82. data/lib/rubocop/cop/style/raise_args.rb +3 -2
  83. data/lib/rubocop/cop/style/redundant_return.rb +1 -1
  84. data/lib/rubocop/cop/style/single_line_methods.rb +32 -2
  85. data/lib/rubocop/cop/style/sole_nested_conditional.rb +29 -5
  86. data/lib/rubocop/cop/style/special_global_vars.rb +3 -3
  87. data/lib/rubocop/cop/style/string_concatenation.rb +1 -1
  88. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
  89. data/lib/rubocop/cop/style/while_until_modifier.rb +2 -4
  90. data/lib/rubocop/formatter/git_hub_actions_formatter.rb +1 -0
  91. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  92. data/lib/rubocop/formatter/simple_text_formatter.rb +2 -1
  93. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
  94. data/lib/rubocop/magic_comment.rb +30 -1
  95. data/lib/rubocop/options.rb +1 -1
  96. data/lib/rubocop/rspec/expect_offense.rb +5 -2
  97. data/lib/rubocop/runner.rb +1 -0
  98. data/lib/rubocop/target_ruby.rb +47 -11
  99. data/lib/rubocop/version.rb +2 -2
  100. metadata +24 -7
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InternalAffairs
6
+ # This cop checks for redundant `subject(:cop) { described_class.new }`.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # RSpec.describe RuboCop::Cop::Department::Foo do
11
+ # subject(:cop) { described_class.new(config) }
12
+ # end
13
+ #
14
+ # # good
15
+ # RSpec.describe RuboCop::Cop::Department::Foo, :config do
16
+ # end
17
+ #
18
+ class RedundantDescribedClassAsSubject < Base
19
+ include RangeHelp
20
+ extend AutoCorrector
21
+
22
+ MSG = 'Remove the redundant `subject`%<additional_message>s.'
23
+
24
+ def_node_matcher :described_class_subject?, <<~PATTERN
25
+ (block
26
+ (send nil? :subject
27
+ (sym :cop))
28
+ (args)
29
+ (send
30
+ (send nil? :described_class) :new
31
+ $...))
32
+ PATTERN
33
+
34
+ def on_block(node)
35
+ return unless (described_class_arguments = described_class_subject?(node))
36
+ return if described_class_arguments.count >= 2
37
+
38
+ describe = find_describe_method_node(node)
39
+
40
+ unless (exist_config = describe.last_argument.source == ':config')
41
+ additional_message = ' and specify `:config` in `describe`'
42
+ end
43
+
44
+ message = format(MSG, additional_message: additional_message)
45
+
46
+ add_offense(node, message: message) do |corrector|
47
+ corrector.remove(range_by_whole_lines(node.source_range, include_final_newline: true))
48
+
49
+ corrector.insert_after(describe.last_argument, ', :config') unless exist_config
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def find_describe_method_node(block_node)
56
+ block_node.ancestors.find { |node| node.block_type? && node.method?(:describe) }.send_node
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InternalAffairs
6
+ # This cop checks that `let` is `RuboCop::Config.new` with no arguments.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # RSpec.describe RuboCop::Cop::Department::Foo, :config do
11
+ # let(:config) { RuboCop::Config.new }
12
+ # end
13
+ #
14
+ # # good
15
+ # RSpec.describe RuboCop::Cop::Department::Foo, :config do
16
+ # end
17
+ #
18
+ # RSpec.describe RuboCop::Cop::Department::Foo, :config do
19
+ # let(:config) { RuboCop::Config.new(argument) }
20
+ # end
21
+ #
22
+ class RedundantLetRuboCopConfigNew < Base
23
+ include RangeHelp
24
+ extend AutoCorrector
25
+
26
+ MSG = 'Remove `let` that is `RuboCop::Config.new` with no arguments%<additional_message>s.'
27
+
28
+ def_node_matcher :let_rubocop_config_new?, <<~PATTERN
29
+ (block
30
+ (send nil? :let
31
+ (sym :config))
32
+ (args)
33
+ (send
34
+ (const
35
+ (const nil? :RuboCop) :Config) :new))
36
+ PATTERN
37
+
38
+ def on_block(node)
39
+ return unless let_rubocop_config_new?(node)
40
+
41
+ describe = find_describe_method_node(node)
42
+
43
+ unless (exist_config = describe.last_argument.source == ':config')
44
+ additional_message = ' and specify `:config` in `describe`'
45
+ end
46
+
47
+ message = format(MSG, additional_message: additional_message)
48
+
49
+ add_offense(node, message: message) do |corrector|
50
+ corrector.remove(range_by_whole_lines(node.source_range, include_final_newline: true))
51
+
52
+ corrector.insert_after(describe.last_argument, ', :config') unless exist_config
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def find_describe_method_node(block_node)
59
+ block_node.ancestors.find { |node| node.block_type? && node.method?(:describe) }.send_node
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -213,7 +213,12 @@ module RuboCop
213
213
  name = node.method_name.to_s
214
214
  category, = categories.find { |_, names| names.include?(name) }
215
215
  key = category || name
216
- visibility_key = "#{node_visibility(node)}_#{key}"
216
+ visibility_key =
217
+ if node.def_modifier?
218
+ "#{name}_methods"
219
+ else
220
+ "#{node_visibility(node)}_#{key}"
221
+ end
217
222
  expected_order.include?(visibility_key) ? visibility_key : key
218
223
  end
219
224
 
@@ -264,7 +269,7 @@ module RuboCop
264
269
 
265
270
  def source_range_with_comment(node)
266
271
  begin_pos, end_pos =
267
- if node.def_type?
272
+ if node.def_type? && !node.method?(:initialize) || node.send_type? && node.def_modifier?
268
273
  start_node = find_visibility_start(node) || node
269
274
  end_node = find_visibility_end(node) || node
270
275
  [begin_pos_with_comment(start_node),
@@ -88,7 +88,7 @@ module RuboCop
88
88
  include RangeHelp
89
89
  extend AutoCorrector
90
90
 
91
- MSG = 'Use empty lines between %<type>s definitions.'
91
+ MSG = 'Expected %<expected>s between %<type>s definitions; found %<actual>d.'
92
92
 
93
93
  def self.autocorrect_incompatible_with
94
94
  [Layout::EmptyLines]
@@ -107,19 +107,21 @@ module RuboCop
107
107
  end
108
108
 
109
109
  def check_defs(nodes)
110
- return if blank_lines_between?(*nodes)
110
+ count = blank_lines_count_between(*nodes)
111
+
112
+ return if line_count_allowed?(count)
111
113
  return if multiple_blank_lines_groups?(*nodes)
112
114
  return if nodes.all?(&:single_line?) &&
113
115
  cop_config['AllowAdjacentOneLineDefs']
114
116
 
115
117
  correction_node = nodes.last
116
118
  location = correction_node.loc.keyword.join(correction_node.loc.name)
117
- add_offense(location, message: message(correction_node)) do |corrector|
118
- autocorrect(corrector, *nodes)
119
+ add_offense(location, message: message(correction_node, count: count)) do |corrector|
120
+ autocorrect(corrector, *nodes, count)
119
121
  end
120
122
  end
121
123
 
122
- def autocorrect(corrector, prev_def, node)
124
+ def autocorrect(corrector, prev_def, node, count)
123
125
  # finds position of first newline
124
126
  end_pos = end_loc(prev_def).end_pos
125
127
  source_buffer = end_loc(prev_def).source_buffer
@@ -128,8 +130,6 @@ module RuboCop
128
130
  # Handle the case when multiple one-liners are on the same line.
129
131
  newline_pos = end_pos + 1 if newline_pos > node.source_range.begin_pos
130
132
 
131
- count = blank_lines_count_between(prev_def, node)
132
-
133
133
  if count > maximum_empty_lines
134
134
  autocorrect_remove_lines(corrector, newline_pos, count)
135
135
  else
@@ -157,14 +157,22 @@ module RuboCop
157
157
  cop_config['EmptyLineBetweenModuleDefs'] && node.module_type?
158
158
  end
159
159
 
160
- def message(node)
161
- type = case node.type
162
- when :def, :defs
163
- :method
164
- else
165
- node.type
166
- end
167
- format(MSG, type: type)
160
+ def message(node, count: nil)
161
+ type = node_type(node)
162
+
163
+ format(MSG,
164
+ type: type,
165
+ expected: expected_lines,
166
+ actual: count)
167
+ end
168
+
169
+ def expected_lines
170
+ if allowance_range?
171
+ "#{minimum_empty_lines..maximum_empty_lines} empty lines"
172
+ else
173
+ lines = maximum_empty_lines == 1 ? 'line' : 'lines'
174
+ "#{maximum_empty_lines} empty #{lines}"
175
+ end
168
176
  end
169
177
 
170
178
  def multiple_blank_lines_groups?(first_def_node, second_def_node)
@@ -176,8 +184,7 @@ module RuboCop
176
184
  blank_start > non_blank_end
177
185
  end
178
186
 
179
- def blank_lines_between?(first_def_node, second_def_node)
180
- count = blank_lines_count_between(first_def_node, second_def_node)
187
+ def line_count_allowed?(count)
181
188
  (minimum_empty_lines..maximum_empty_lines).cover?(count)
182
189
  end
183
190
 
@@ -210,7 +217,7 @@ module RuboCop
210
217
  end
211
218
 
212
219
  def end_loc(node)
213
- if node.def_type? && node.endless?
220
+ if (node.def_type? || node.defs_type?) && node.endless?
214
221
  node.loc.expression.end
215
222
  else
216
223
  node.loc.end
@@ -230,6 +237,19 @@ module RuboCop
230
237
 
231
238
  corrector.insert_after(where_to_insert, "\n" * difference)
232
239
  end
240
+
241
+ def node_type(node)
242
+ case node.type
243
+ when :def, :defs
244
+ :method
245
+ else
246
+ node.type
247
+ end
248
+ end
249
+
250
+ def allowance_range?
251
+ minimum_empty_lines != maximum_empty_lines
252
+ end
233
253
  end
234
254
  end
235
255
  end
@@ -4,11 +4,14 @@ module RuboCop
4
4
  module Cop
5
5
  module Layout
6
6
  # This cop checks the indentation of the first argument in a method call.
7
- # Arguments after the first one are checked by Layout/ArgumentAlignment,
7
+ # Arguments after the first one are checked by `Layout/ArgumentAlignment`,
8
8
  # not by this cop.
9
9
  #
10
10
  # For indenting the first parameter of method _definitions_, check out
11
- # Layout/FirstParameterIndentation.
11
+ # `Layout/FirstParameterIndentation`.
12
+ #
13
+ # This cop will respect `Layout/ArgumentAlignment` and will not work when
14
+ # `EnforcedStyle: with_fixed_indentation` is specified for `Layout/ArgumentAlignment`.
12
15
  #
13
16
  # @example
14
17
  #
@@ -149,6 +152,7 @@ module RuboCop
149
152
  MSG = 'Indent the first argument one step more than %<base>s.'
150
153
 
151
154
  def on_send(node)
155
+ return if style != :consistent && enforce_first_argument_with_fixed_indentation?
152
156
  return if !node.arguments? || node.operator_method?
153
157
 
154
158
  indent = base_indentation(node) + configured_indentation_width
@@ -250,6 +254,16 @@ module RuboCop
250
254
  def on_new_investigation
251
255
  @comment_lines = nil
252
256
  end
257
+
258
+ def enforce_first_argument_with_fixed_indentation?
259
+ return false unless argument_alignment_config['Enabled']
260
+
261
+ argument_alignment_config['EnforcedStyle'] == 'with_fixed_indentation'
262
+ end
263
+
264
+ def argument_alignment_config
265
+ config.for_cop('Layout/ArgumentAlignment')
266
+ end
253
267
  end
254
268
  end
255
269
  end
@@ -60,12 +60,13 @@ module RuboCop
60
60
  # }
61
61
  class LineLength < Base
62
62
  include CheckLineBreakable
63
- include ConfigurableMax
64
63
  include IgnoredPattern
65
64
  include RangeHelp
66
65
  include LineLengthHelp
67
66
  extend AutoCorrector
68
67
 
68
+ exclude_limit 'Max'
69
+
69
70
  MSG = 'Line is too long. [%<length>d/%<max>d]'
70
71
 
71
72
  def on_block(node)
@@ -31,6 +31,32 @@ module RuboCop
31
31
  # foo = if expression
32
32
  # 'bar'
33
33
  # end
34
+ #
35
+ # @example SupportedTypes: ['block', 'case', 'class', 'if', 'kwbegin', 'module'] (default)
36
+ # # good
37
+ # foo =
38
+ # if expression
39
+ # 'bar'
40
+ # end
41
+ #
42
+ # # good
43
+ # foo =
44
+ # [1].map do |i|
45
+ # i + 1
46
+ # end
47
+ #
48
+ # @example SupportedTypes: ['block']
49
+ # # good
50
+ # foo = if expression
51
+ # 'bar'
52
+ # end
53
+ #
54
+ # # good
55
+ # foo =
56
+ # [1].map do |i|
57
+ # 'bar' * i
58
+ # end
59
+ #
34
60
  class MultilineAssignmentLayout < Base
35
61
  include CheckAssignment
36
62
  include ConfigurableEnforcedStyle
@@ -16,14 +16,14 @@ module RuboCop
16
16
  # if a +
17
17
  # b
18
18
  # something &&
19
- # something_else
19
+ # something_else
20
20
  # end
21
21
  #
22
22
  # # good
23
23
  # if a +
24
24
  # b
25
25
  # something &&
26
- # something_else
26
+ # something_else
27
27
  # end
28
28
  #
29
29
  # @example EnforcedStyle: indented
@@ -6,10 +6,6 @@ module RuboCop
6
6
  # Checks for space between the name of a receiver and a left
7
7
  # brackets.
8
8
  #
9
- # This cop is marked as unsafe because it can occur false positives
10
- # for `do_something [this_is_an_array_literal_argument]` that take
11
- # an array without parentheses as an argument.
12
- #
13
9
  # @example
14
10
  #
15
11
  # # bad
@@ -25,28 +21,35 @@ module RuboCop
25
21
  MSG = 'Remove the space before the opening brackets.'
26
22
 
27
23
  def on_send(node)
28
- return if node.parenthesized? || node.parent&.send_type?
29
24
  return unless (first_argument = node.first_argument)
30
25
 
31
26
  begin_pos = first_argument.source_range.begin_pos
32
-
33
- return unless (range = offense_range(node, first_argument, begin_pos))
27
+ return unless (range = offense_range(node, begin_pos))
34
28
 
35
29
  register_offense(range)
36
30
  end
37
31
 
38
32
  private
39
33
 
40
- def offense_range(node, first_argument, begin_pos)
41
- if space_before_brackets?(node, first_argument)
42
- range_between(node.loc.selector.end_pos, begin_pos)
34
+ def offense_range(node, begin_pos)
35
+ if reference_variable_with_brackets?(node)
36
+ receiver_end_pos = node.receiver.source_range.end_pos
37
+ selector_begin_pos = node.loc.selector.begin_pos
38
+ return if receiver_end_pos >= selector_begin_pos
39
+
40
+ range_between(receiver_end_pos, selector_begin_pos)
43
41
  elsif node.method?(:[]=)
44
- end_pos = node.receiver.source_range.end_pos
42
+ offense_range_for_assignment(node, begin_pos)
43
+ end
44
+ end
45
45
 
46
- return if begin_pos - end_pos == 1
46
+ def offense_range_for_assignment(node, begin_pos)
47
+ end_pos = node.receiver.source_range.end_pos
47
48
 
48
- range_between(end_pos, begin_pos - 1)
49
- end
49
+ return if begin_pos - end_pos == 1 ||
50
+ (range = range_between(end_pos, begin_pos - 1)).source.start_with?('[')
51
+
52
+ range
50
53
  end
51
54
 
52
55
  def register_offense(range)
@@ -55,8 +58,8 @@ module RuboCop
55
58
  end
56
59
  end
57
60
 
58
- def space_before_brackets?(node, first_argument)
59
- node.receiver.nil? && first_argument.array_type? && node.arguments.size == 1
61
+ def reference_variable_with_brackets?(node)
62
+ node.receiver&.variable? && node.method?(:[]) && node.arguments.size == 1
60
63
  end
61
64
  end
62
65
  end
@@ -3,8 +3,21 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # This cop checks for calls to debugger or pry.
7
- # The cop can be configured to define which methods and receivers must be fixed.
6
+ # This cop checks for debug calls (such as `debugger` or `binding.pry`) that should
7
+ # not be kept for production code.
8
+ #
9
+ # The cop can be configured using `DebuggerMethods`. By default, a number of gems
10
+ # debug entrypoints are configured (`Kernel`, `Byebug`, `Capybara`, `Pry`, `Rails`,
11
+ # and `WebConsole`). Additional methods can be added.
12
+ #
13
+ # Specific default groups can be disabled if necessary:
14
+ #
15
+ # [source,yaml]
16
+ # ----
17
+ # Lint/Debugger:
18
+ # WebConsole: ~
19
+ # ---
20
+ #
8
21
  #
9
22
  # @example
10
23
  #
@@ -33,14 +46,32 @@ module RuboCop
33
46
  # def some_method
34
47
  # do_something
35
48
  # end
49
+ #
50
+ # @example DebuggerMethods: [my_debugger]
51
+ #
52
+ # # bad (ok during development)
53
+ #
54
+ # def some_method
55
+ # my_debugger
56
+ # end
36
57
  class Debugger < Base
37
58
  MSG = 'Remove debugger entry point `%<source>s`.'
38
59
 
39
60
  RESTRICT_ON_SEND = [].freeze
40
61
 
62
+ def_node_matcher :kernel?, <<~PATTERN
63
+ (const {nil? cbase} :Kernel)
64
+ PATTERN
65
+
66
+ def_node_matcher :valid_receiver?, <<~PATTERN
67
+ {
68
+ (const {nil? cbase} %1)
69
+ (send {nil? #kernel?} %1)
70
+ }
71
+ PATTERN
72
+
41
73
  def on_send(node)
42
- return unless debugger_method?(node.method_name)
43
- return if !node.receiver.nil? && !debugger_receiver?(node)
74
+ return unless debugger_method?(node)
44
75
 
45
76
  add_offense(node)
46
77
  end
@@ -51,19 +82,32 @@ module RuboCop
51
82
  format(MSG, source: node.source)
52
83
  end
53
84
 
54
- def debugger_method?(name)
55
- cop_config.fetch('DebuggerMethods', []).include?(name.to_s)
85
+ def debugger_methods
86
+ @debugger_methods ||= begin
87
+ config = cop_config.fetch('DebuggerMethods', [])
88
+ values = config.is_a?(Array) ? config : config.values.flatten
89
+ values.map do |v|
90
+ next unless v
91
+
92
+ *receiver, method_name = v.split('.')
93
+ {
94
+ receiver: receiver.empty? ? nil : receiver.join.to_sym,
95
+ method_name: method_name.to_sym
96
+ }
97
+ end.compact
98
+ end
56
99
  end
57
100
 
58
- def debugger_receiver?(node)
59
- receiver = case node.receiver
60
- when RuboCop::AST::SendNode
61
- node.receiver.method_name
62
- when RuboCop::AST::ConstNode
63
- node.receiver.const_name
64
- end
101
+ def debugger_method?(send_node)
102
+ debugger_methods.any? do |method|
103
+ next unless method[:method_name] == send_node.method_name
65
104
 
66
- cop_config.fetch('DebuggerReceivers', []).include?(receiver.to_s)
105
+ if method[:receiver].nil?
106
+ send_node.receiver.nil?
107
+ else
108
+ valid_receiver?(send_node.receiver, method[:receiver])
109
+ end
110
+ end
67
111
  end
68
112
  end
69
113
  end