rubocop 1.85.1 → 1.86.1

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 (89) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +9 -12
  3. data/lib/rubocop/cache_config.rb +1 -1
  4. data/lib/rubocop/cli/command/auto_generate_config.rb +1 -1
  5. data/lib/rubocop/cli/command/show_cops.rb +2 -2
  6. data/lib/rubocop/cli/command/show_docs_url.rb +1 -1
  7. data/lib/rubocop/config.rb +14 -10
  8. data/lib/rubocop/config_finder.rb +1 -1
  9. data/lib/rubocop/config_loader_resolver.rb +2 -1
  10. data/lib/rubocop/config_store.rb +1 -1
  11. data/lib/rubocop/config_validator.rb +1 -1
  12. data/lib/rubocop/cop/documentation.rb +2 -3
  13. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
  14. data/lib/rubocop/cop/layout/dot_position.rb +1 -1
  15. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +9 -2
  16. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +1 -1
  17. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -0
  18. data/lib/rubocop/cop/layout/end_alignment.rb +6 -3
  19. data/lib/rubocop/cop/layout/line_length.rb +5 -3
  20. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +28 -3
  21. data/lib/rubocop/cop/layout/space_around_keyword.rb +3 -1
  22. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +1 -0
  23. data/lib/rubocop/cop/lint/constant_reassignment.rb +59 -9
  24. data/lib/rubocop/cop/lint/duplicate_methods.rb +55 -8
  25. data/lib/rubocop/cop/lint/empty_conditional_body.rb +6 -1
  26. data/lib/rubocop/cop/lint/empty_in_pattern.rb +8 -1
  27. data/lib/rubocop/cop/lint/empty_when.rb +8 -1
  28. data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
  29. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +16 -0
  30. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +17 -0
  31. data/lib/rubocop/cop/lint/syntax.rb +25 -1
  32. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -0
  33. data/lib/rubocop/cop/lint/unused_method_argument.rb +10 -0
  34. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +4 -4
  35. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +2 -0
  36. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +22 -7
  37. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +1 -1
  38. data/lib/rubocop/cop/registry.rb +20 -13
  39. data/lib/rubocop/cop/style/access_modifier_declarations.rb +14 -2
  40. data/lib/rubocop/cop/style/and_or.rb +1 -0
  41. data/lib/rubocop/cop/style/arguments_forwarding.rb +25 -7
  42. data/lib/rubocop/cop/style/ascii_comments.rb +1 -1
  43. data/lib/rubocop/cop/style/block_delimiters.rb +23 -31
  44. data/lib/rubocop/cop/style/collection_compact.rb +36 -16
  45. data/lib/rubocop/cop/style/concat_array_literals.rb +2 -0
  46. data/lib/rubocop/cop/style/conditional_assignment.rb +0 -4
  47. data/lib/rubocop/cop/style/empty_class_definition.rb +24 -2
  48. data/lib/rubocop/cop/style/file_open.rb +1 -1
  49. data/lib/rubocop/cop/style/global_vars.rb +1 -1
  50. data/lib/rubocop/cop/style/guard_clause.rb +9 -6
  51. data/lib/rubocop/cop/style/hash_lookup_method.rb +7 -0
  52. data/lib/rubocop/cop/style/if_inside_else.rb +1 -5
  53. data/lib/rubocop/cop/style/if_unless_modifier.rb +11 -0
  54. data/lib/rubocop/cop/style/if_with_semicolon.rb +7 -5
  55. data/lib/rubocop/cop/style/ip_addresses.rb +1 -2
  56. data/lib/rubocop/cop/style/magic_comment_format.rb +2 -2
  57. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +5 -3
  58. data/lib/rubocop/cop/style/module_member_existence_check.rb +1 -11
  59. data/lib/rubocop/cop/style/mutable_constant.rb +1 -1
  60. data/lib/rubocop/cop/style/non_nil_check.rb +5 -11
  61. data/lib/rubocop/cop/style/numeric_literals.rb +1 -1
  62. data/lib/rubocop/cop/style/one_class_per_file.rb +24 -4
  63. data/lib/rubocop/cop/style/raise_args.rb +1 -1
  64. data/lib/rubocop/cop/style/redundant_each.rb +3 -3
  65. data/lib/rubocop/cop/style/redundant_line_continuation.rb +16 -0
  66. data/lib/rubocop/cop/style/redundant_parentheses.rb +22 -20
  67. data/lib/rubocop/cop/style/redundant_percent_q.rb +4 -1
  68. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +0 -5
  69. data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +10 -0
  70. data/lib/rubocop/cop/style/safe_navigation.rb +7 -7
  71. data/lib/rubocop/cop/style/single_line_block_params.rb +1 -1
  72. data/lib/rubocop/cop/style/symbol_proc.rb +4 -3
  73. data/lib/rubocop/cop/style/trailing_method_end_statement.rb +1 -0
  74. data/lib/rubocop/cop/style/yoda_expression.rb +1 -1
  75. data/lib/rubocop/formatter/disabled_config_formatter.rb +1 -1
  76. data/lib/rubocop/formatter/junit_formatter.rb +1 -1
  77. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
  78. data/lib/rubocop/lsp/routes.rb +10 -3
  79. data/lib/rubocop/mcp/server.rb +27 -1
  80. data/lib/rubocop/path_util.rb +14 -2
  81. data/lib/rubocop/plugin/loader.rb +1 -1
  82. data/lib/rubocop/result_cache.rb +10 -1
  83. data/lib/rubocop/rspec/cop_helper.rb +8 -0
  84. data/lib/rubocop/rspec/shared_contexts.rb +11 -2
  85. data/lib/rubocop/runner.rb +8 -3
  86. data/lib/rubocop/server/core.rb +2 -0
  87. data/lib/rubocop/target_finder.rb +1 -1
  88. data/lib/rubocop/version.rb +2 -2
  89. metadata +5 -19
@@ -232,9 +232,7 @@ module RuboCop
232
232
  end
233
233
 
234
234
  def semantic_message(node)
235
- block_begin = node.loc.begin.source
236
-
237
- if block_begin == '{'
235
+ if node.braces?
238
236
  'Prefer `do...end` over `{...}` for procedural blocks.'
239
237
  else
240
238
  'Prefer `{...}` over `do...end` for functional blocks.'
@@ -372,17 +370,22 @@ module RuboCop
372
370
  end
373
371
  # rubocop:enable Metrics/CyclomaticComplexity
374
372
 
373
+ # rubocop:disable Metrics/CyclomaticComplexity -- inlined special_method checks to avoid double evaluation
375
374
  def proper_block_style?(node)
376
375
  return true if require_do_end?(node)
377
- return special_method_proper_block_style?(node) if special_method?(node.method_name)
376
+
377
+ method_name = node.method_name
378
+ return true if allowed_method?(method_name) || matches_allowed_pattern?(method_name)
379
+ return node.braces? if braces_required_method?(method_name)
378
380
 
379
381
  case style
380
382
  when :line_count_based then line_count_based_block_style?(node)
381
383
  when :semantic then semantic_block_style?(node)
382
384
  when :braces_for_chaining then braces_for_chaining_style?(node)
383
- when :always_braces then braces_style?(node)
385
+ when :always_braces then node.braces?
384
386
  end
385
387
  end
388
+ # rubocop:enable Metrics/CyclomaticComplexity
386
389
 
387
390
  def require_do_end?(node)
388
391
  return false if node.braces? || node.multiline?
@@ -391,19 +394,6 @@ module RuboCop
391
394
  resbody.children.first&.array_type?
392
395
  end
393
396
 
394
- def special_method?(method_name)
395
- allowed_method?(method_name) ||
396
- matches_allowed_pattern?(method_name) ||
397
- braces_required_method?(method_name)
398
- end
399
-
400
- def special_method_proper_block_style?(node)
401
- method_name = node.method_name
402
- return true if allowed_method?(method_name) || matches_allowed_pattern?(method_name)
403
-
404
- node.braces? if braces_required_method?(method_name)
405
- end
406
-
407
397
  def braces_required_method?(method_name)
408
398
  braces_required_methods.include?(method_name.to_s)
409
399
  end
@@ -428,17 +418,11 @@ module RuboCop
428
418
  end
429
419
 
430
420
  def braces_for_chaining_style?(node)
431
- block_begin = node.loc.begin.source
432
-
433
- block_begin == if node.multiline?
434
- (node.chained? ? '{' : 'do')
435
- else
436
- '{'
437
- end
438
- end
439
-
440
- def braces_style?(node)
441
- node.loc.begin.source == '{'
421
+ if node.multiline?
422
+ node.chained? ? node.braces? : !node.braces?
423
+ else
424
+ node.braces?
425
+ end
442
426
  end
443
427
 
444
428
  def correction_would_break_code?(node)
@@ -448,7 +432,11 @@ module RuboCop
448
432
  end
449
433
 
450
434
  def functional_method?(method_name)
451
- cop_config['FunctionalMethods'].map(&:to_sym).include?(method_name)
435
+ functional_methods.include?(method_name)
436
+ end
437
+
438
+ def functional_methods
439
+ @functional_methods ||= cop_config['FunctionalMethods'].to_set(&:to_sym).freeze
452
440
  end
453
441
 
454
442
  def functional_block?(node)
@@ -460,7 +448,11 @@ module RuboCop
460
448
  end
461
449
 
462
450
  def procedural_method?(method_name)
463
- cop_config['ProceduralMethods'].map(&:to_sym).include?(method_name)
451
+ procedural_methods.include?(method_name)
452
+ end
453
+
454
+ def procedural_methods
455
+ @procedural_methods ||= cop_config['ProceduralMethods'].to_set(&:to_sym).freeze
464
456
  end
465
457
 
466
458
  def return_value_used?(node)
@@ -64,22 +64,34 @@ module RuboCop
64
64
  # @!method reject_method?(node)
65
65
  def_node_matcher :reject_method?, <<~PATTERN
66
66
  (block
67
- (call
68
- !nil? {:reject :reject!})
67
+ (call !nil? {:reject :reject!})
69
68
  $(args ...)
70
- (call
71
- $(lvar _) :nil?))
69
+ (call $(lvar _) :nil?))
70
+ PATTERN
71
+
72
+ # @!method reject_method_for_numblock_or_itblock?(node)
73
+ def_node_matcher :reject_method_for_numblock_or_itblock?, <<~PATTERN
74
+ {
75
+ (numblock (call !nil? {:reject :reject!}) _ (call (lvar :_1) :nil?))
76
+ (itblock (call !nil? {:reject :reject!}) _ (call (lvar :it) :nil?))
77
+ }
72
78
  PATTERN
73
79
 
74
80
  # @!method select_method?(node)
75
81
  def_node_matcher :select_method?, <<~PATTERN
76
82
  (block
77
- (call
78
- !nil? {:select :select! :filter :filter!})
83
+ (call !nil? {:select :select! :filter :filter!})
79
84
  $(args ...)
80
85
  (call
81
- (call
82
- $(lvar _) :nil?) :!))
86
+ (call $(lvar _) :nil?) :!))
87
+ PATTERN
88
+
89
+ # @!method select_method_for_numblock_or_itblock?(node)
90
+ def_node_matcher :select_method_for_numblock_or_itblock?, <<~PATTERN
91
+ {
92
+ (numblock (call !nil? {:select :select! :filter :filter!}) _ (call (call (lvar :_1) :nil?) :!))
93
+ (itblock (call !nil? {:select :select! :filter :filter!}) _ (call (call (lvar :it) :nil?) :!))
94
+ }
83
95
  PATTERN
84
96
 
85
97
  # @!method grep_v_with_nil?(node)
@@ -102,26 +114,34 @@ module RuboCop
102
114
 
103
115
  private
104
116
 
105
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
106
117
  def offense_range(node)
107
118
  if reject_method_with_block_pass?(node) || grep_v_with_nil?(node)
108
119
  range(node, node)
109
120
  else
110
121
  block_node = node.parent
111
122
 
112
- return unless block_node&.block_type?
113
- unless (args, receiver = reject_method?(block_node) || select_method?(block_node))
114
- return
115
- end
116
- return unless args.last.source == receiver.source
123
+ return unless block_node&.any_block_type?
124
+ return unless match_block_method?(block_node)
117
125
 
118
126
  range(node, block_node)
119
127
  end
120
128
  end
121
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
129
+
130
+ def match_block_method?(block_node)
131
+ if block_node.block_type?
132
+ result = reject_method?(block_node) || select_method?(block_node)
133
+ return false unless result
134
+
135
+ args, receiver = result
136
+ args.last.source == receiver.source
137
+ else
138
+ reject_method_for_numblock_or_itblock?(block_node) ||
139
+ select_method_for_numblock_or_itblock?(block_node)
140
+ end
141
+ end
122
142
 
123
143
  def to_enum_method?(node)
124
- return false unless node.receiver.send_type?
144
+ return false unless node.receiver.call_type?
125
145
 
126
146
  TO_ENUM_METHODS.include?(node.receiver.method_name)
127
147
  end
@@ -52,6 +52,8 @@ module RuboCop
52
52
 
53
53
  add_offense(offense, message: message) do |corrector|
54
54
  if use_percent_literal
55
+ next unless prefer
56
+
55
57
  corrector.replace(offense, prefer)
56
58
  else
57
59
  corrector.replace(node.loc.selector, 'push')
@@ -99,10 +99,6 @@ module RuboCop
99
99
  end
100
100
  end
101
101
 
102
- def setter_method?(method_name)
103
- method_name.to_s.end_with?(EQUAL) && !%i[!= == === >= <=].include?(method_name)
104
- end
105
-
106
102
  def assignment_rhs_exist?(node)
107
103
  parent = node.parent
108
104
  return true unless parent
@@ -10,16 +10,20 @@ module RuboCop
10
10
  #
11
11
  # The supported styles are:
12
12
  #
13
- # * class_definition (default) - prefer standard class definition over `Class.new`
13
+ # * class_keyword (default) - prefer standard class definition over `Class.new`
14
14
  # * class_new - prefer `Class.new` over class definition
15
15
  #
16
16
  # One difference between the two styles is that the `Class.new` form does not make
17
17
  # the subclass name available to the base class's `inherited` callback.
18
- # For this reason, `EnforcedStyle: class_definition` is set as the default style.
18
+ # For this reason, `EnforcedStyle: class_keyword` is set as the default style.
19
19
  # Class definitions without a superclass, which are not involved in inheritance,
20
20
  # are not detected. This ensures safe detection regardless of the applied style.
21
21
  # This avoids overlapping responsibilities with the `Lint/EmptyClass` cop.
22
22
  #
23
+ # Use `AllowedParentClasses` to permit both styles for specific parent classes.
24
+ # For example, adding `StandardError` allows both `Error = Class.new(StandardError)`
25
+ # and `class Error < StandardError; end` regardless of the enforced style.
26
+ #
23
27
  # @example EnforcedStyle: class_keyword (default)
24
28
  # # bad
25
29
  # FooError = Class.new(StandardError)
@@ -42,6 +46,14 @@ module RuboCop
42
46
  # # good
43
47
  # FooError = Class.new(StandardError)
44
48
  #
49
+ # @example AllowedParentClasses: ['StandardError']
50
+ # # good - allowed regardless of EnforcedStyle
51
+ # FooError = Class.new(StandardError)
52
+ #
53
+ # # good - allowed regardless of EnforcedStyle
54
+ # class FooError < StandardError
55
+ # end
56
+ #
45
57
  class EmptyClassDefinition < Base
46
58
  include ConfigurableEnforcedStyle
47
59
  extend AutoCorrector
@@ -59,6 +71,7 @@ module RuboCop
59
71
  return unless %i[class_keyword class_definition].include?(style)
60
72
  return unless (class_new_node = class_new_assignment(node))
61
73
  return if (arg = class_new_node.first_argument) && !arg.const_type?
74
+ return if allowed_parent_class?(class_new_node.first_argument.source)
62
75
 
63
76
  add_offense(node, message: MSG_CLASS_KEYWORD) do |corrector|
64
77
  autocorrect_class_new(corrector, node, class_new_node)
@@ -69,6 +82,7 @@ module RuboCop
69
82
  return unless style == :class_new
70
83
  return unless node.parent_class
71
84
  return if (body = node.body) && !body.children.empty?
85
+ return if allowed_parent_class?(node.parent_class.source)
72
86
 
73
87
  add_offense(node, message: MSG_CLASS_NEW) do |corrector|
74
88
  autocorrect_class_definition(corrector, node)
@@ -77,6 +91,14 @@ module RuboCop
77
91
 
78
92
  private
79
93
 
94
+ def allowed_parent_class?(parent_class_name)
95
+ allowed_parent_classes.include?(parent_class_name)
96
+ end
97
+
98
+ def allowed_parent_classes
99
+ cop_config.fetch('AllowedParentClasses', [])
100
+ end
101
+
80
102
  def autocorrect_class_new(corrector, node, class_new_node)
81
103
  indent = ' ' * node.loc.column
82
104
  class_name = node.name
@@ -72,7 +72,7 @@ module RuboCop
72
72
  def offensive_usage?(node)
73
73
  return true unless node.value_used?
74
74
 
75
- node.parent.assignment? || receiver_of_chained_call?(node)
75
+ node.parent.lvasgn_type? || receiver_of_chained_call?(node)
76
76
  end
77
77
 
78
78
  def receiver_of_chained_call?(node)
@@ -57,7 +57,7 @@ module RuboCop
57
57
  ].map(&:to_sym)
58
58
 
59
59
  def user_vars
60
- cop_config['AllowedVariables'].map(&:to_sym)
60
+ @user_vars ||= cop_config['AllowedVariables'].map(&:to_sym).freeze
61
61
  end
62
62
 
63
63
  def allowed_var?(global_var)
@@ -224,15 +224,18 @@ module RuboCop
224
224
  end
225
225
 
226
226
  def find_heredoc_argument(node)
227
- return unless node&.call_type?
227
+ return unless node
228
228
 
229
- last_arg = node.last_argument
229
+ node = node.children.first while node.begin_type?
230
+ return node if heredoc?(node)
231
+ return unless node.call_type?
230
232
 
231
- if heredoc?(last_arg)
232
- last_arg
233
- elsif last_arg&.call_type?
234
- find_heredoc_argument(last_arg)
233
+ node.arguments.reverse_each do |argument|
234
+ heredoc_argument = find_heredoc_argument(argument)
235
+ return heredoc_argument if heredoc_argument
235
236
  end
237
+
238
+ find_heredoc_argument(node.receiver)
236
239
  end
237
240
 
238
241
  def autocorrect_heredoc_argument(corrector, node, heredoc_node, leave_branch, guard)
@@ -41,8 +41,13 @@ module RuboCop
41
41
  # # good
42
42
  # hash.fetch(key)
43
43
  #
44
+ # @example AllowedReceivers: ['Rails.cache']
45
+ # # good
46
+ # Rails.cache.fetch(name, options) { block }
47
+ #
44
48
  class HashLookupMethod < Base
45
49
  include ConfigurableEnforcedStyle
50
+ include AllowedReceivers
46
51
  extend AutoCorrector
47
52
 
48
53
  BRACKET_MSG = 'Use `Hash#[]` instead of `Hash#fetch`.'
@@ -51,6 +56,8 @@ module RuboCop
51
56
  RESTRICT_ON_SEND = %i[[] fetch].freeze
52
57
 
53
58
  def on_send(node)
59
+ return if (receiver = node.receiver) && allowed_receiver?(receiver)
60
+
54
61
  if offense_for_brackets?(node)
55
62
  add_offense(node.loc.selector, message: BRACKET_MSG) do |corrector|
56
63
  correct_fetch_to_brackets(corrector, node)
@@ -85,7 +85,7 @@ module RuboCop
85
85
  private
86
86
 
87
87
  def autocorrect(corrector, node)
88
- if then?(node)
88
+ if node.then?
89
89
  # If the nested `if` is a then node, correct it first,
90
90
  # then the next pass will use `correct_to_elsif_from_if_inside_else_form`
91
91
  IfThenCorrector.new(node, indentation: 0).call(corrector)
@@ -124,10 +124,6 @@ module RuboCop
124
124
  corrector.remove(range_by_whole_lines(find_end_range(node), include_final_newline: true))
125
125
  end
126
126
 
127
- def then?(node)
128
- node.loc.begin&.source == 'then'
129
- end
130
-
131
127
  def find_end_range(node)
132
128
  end_range = node.loc.end
133
129
  return end_range if end_range
@@ -101,6 +101,7 @@ module RuboCop
101
101
 
102
102
  add_offense(node.loc.keyword, message: format(msg, keyword: node.keyword)) do |corrector|
103
103
  next if part_of_ignored_node?(node)
104
+ next if another_modifier_if_on_same_line?(node)
104
105
 
105
106
  autocorrect(corrector, node)
106
107
  ignore_node(node)
@@ -280,6 +281,16 @@ module RuboCop
280
281
  node.parent if node&.type?(:pair)
281
282
  end
282
283
 
284
+ def another_modifier_if_on_same_line?(node)
285
+ collection = find_containing_collection(node)
286
+ return false unless collection
287
+
288
+ line = node.source_range.line
289
+ collection.each_descendant(:if).any? do |sibling|
290
+ sibling != node && sibling.modifier_form? && sibling.source_range.line == line
291
+ end
292
+ end
293
+
283
294
  def non_simple_if_unless?(node)
284
295
  node.ternary? || node.elsif? || node.else?
285
296
  end
@@ -17,9 +17,9 @@ module RuboCop
17
17
  include OnNormalIfUnless
18
18
  extend AutoCorrector
19
19
 
20
- MSG_IF_ELSE = 'Do not use `if %<expr>s;` - use `if/else` instead.'
21
- MSG_NEWLINE = 'Do not use `if %<expr>s;` - use a newline instead.'
22
- MSG_TERNARY = 'Do not use `if %<expr>s;` - use a ternary operator instead.'
20
+ MSG_IF_ELSE = 'Do not use `%<keyword>s %<expr>s;` - use `if/else` instead.'
21
+ MSG_NEWLINE = 'Do not use `%<keyword>s %<expr>s;` - use a newline instead.'
22
+ MSG_TERNARY = 'Do not use `%<keyword>s %<expr>s;` - use a ternary operator instead.'
23
23
 
24
24
  def on_normal_if_unless(node)
25
25
  return if node.parent&.if_type?
@@ -49,7 +49,7 @@ module RuboCop
49
49
  MSG_TERNARY
50
50
  end
51
51
 
52
- format(template, expr: node.condition.source)
52
+ format(template, keyword: node.keyword, expr: node.condition.source)
53
53
  end
54
54
 
55
55
  def autocorrect(corrector, node)
@@ -71,7 +71,7 @@ module RuboCop
71
71
  end
72
72
 
73
73
  def use_return_with_argument?(node)
74
- node.if_branch&.return_type? && node.if_branch&.arguments&.any?
74
+ node.branches.compact.any? { |branch| branch.return_type? && branch.arguments.any? }
75
75
  end
76
76
 
77
77
  def replacement(node)
@@ -80,6 +80,8 @@ module RuboCop
80
80
  then_code = node.if_branch ? build_expression(node.if_branch) : 'nil'
81
81
  else_code = node.else_branch ? build_expression(node.else_branch) : 'nil'
82
82
 
83
+ then_code, else_code = else_code, then_code if node.unless?
84
+
83
85
  "#{node.condition.source} ? #{then_code} : #{else_code}"
84
86
  end
85
87
 
@@ -48,8 +48,7 @@ module RuboCop
48
48
  private
49
49
 
50
50
  def allowed_addresses
51
- allowed_addresses = cop_config['AllowedAddresses']
52
- Array(allowed_addresses).map(&:downcase)
51
+ @allowed_addresses ||= Array(cop_config['AllowedAddresses']).map(&:downcase).freeze
53
52
  end
54
53
 
55
54
  def potential_ip?(str)
@@ -176,7 +176,7 @@ module RuboCop
176
176
  if first_non_comment_token
177
177
  0...first_non_comment_token.line
178
178
  else
179
- (0..)
179
+ 0..
180
180
  end
181
181
  end
182
182
 
@@ -299,7 +299,7 @@ module RuboCop
299
299
  end
300
300
 
301
301
  def supported_capitalizations
302
- cop_config['SupportedCapitalizations'].map(&:to_sym)
302
+ @supported_capitalizations ||= cop_config['SupportedCapitalizations'].map(&:to_sym).freeze
303
303
  end
304
304
  end
305
305
  end
@@ -33,16 +33,18 @@ module RuboCop
33
33
  end
34
34
 
35
35
  def included_macros_list
36
- cop_config.fetch('IncludedMacros', []).map(&:to_sym)
36
+ @included_macros_list ||= cop_config.fetch('IncludedMacros', []).map(&:to_sym).freeze
37
37
  end
38
38
 
39
39
  def included_macro_patterns
40
- cop_config.fetch('IncludedMacroPatterns', [])
40
+ @included_macro_patterns ||=
41
+ cop_config.fetch('IncludedMacroPatterns', [])
42
+ .map { |pattern| Regexp.new(pattern) }.freeze
41
43
  end
42
44
 
43
45
  def matches_included_macro_pattern?(method_name)
44
46
  included_macro_patterns.any? do |pattern|
45
- Regexp.new(pattern).match?(method_name.to_s)
47
+ pattern.match?(method_name.to_s)
46
48
  end
47
49
  end
48
50
 
@@ -32,7 +32,6 @@ module RuboCop
32
32
  # Array.private_instance_methods.include?(:foo)
33
33
  # Array.protected_instance_methods.include?(:foo)
34
34
  # Array.public_instance_methods.include?(:foo)
35
- # Array.included_modules.include?(:foo)
36
35
  #
37
36
  # # good
38
37
  # Array.class_variable_defined?(:foo)
@@ -40,15 +39,8 @@ module RuboCop
40
39
  # Array.private_method_defined?(:foo)
41
40
  # Array.protected_method_defined?(:foo)
42
41
  # Array.public_method_defined?(:foo)
43
- # Array.include?(:foo)
44
- #
45
- # @example AllowedMethods: [included_modules]
46
- #
47
- # # good
48
- # Array.included_modules.include?(:foo)
49
42
  #
50
43
  class ModuleMemberExistenceCheck < Base
51
- include AllowedMethods
52
44
  extend AutoCorrector
53
45
 
54
46
  MSG = 'Use `%<replacement>s` instead.'
@@ -64,14 +56,13 @@ module RuboCop
64
56
  METHOD_REPLACEMENTS = {
65
57
  class_variables: :class_variable_defined?,
66
58
  constants: :const_defined?,
67
- included_modules: :include?,
68
59
  instance_methods: :method_defined?,
69
60
  private_instance_methods: :private_method_defined?,
70
61
  protected_instance_methods: :protected_method_defined?,
71
62
  public_instance_methods: :public_method_defined?
72
63
  }.freeze
73
64
 
74
- METHODS_WITHOUT_INHERIT_PARAM = Set[:class_variables, :included_modules].freeze
65
+ METHODS_WITHOUT_INHERIT_PARAM = Set[:class_variables].freeze
75
66
  METHODS_WITH_INHERIT_PARAM =
76
67
  (METHOD_REPLACEMENTS.keys.to_set - METHODS_WITHOUT_INHERIT_PARAM).freeze
77
68
 
@@ -81,7 +72,6 @@ module RuboCop
81
72
  return unless (parent = node.parent)
82
73
  return unless module_member_inclusion?(parent)
83
74
  return unless simple_method_argument?(node) && simple_method_argument?(parent)
84
- return if allowed_method?(node.method_name)
85
75
 
86
76
  offense_range = node.location.selector.join(parent.source_range.end)
87
77
  replacement = replacement_for(node, parent)
@@ -125,7 +125,7 @@ module RuboCop
125
125
  MSG = 'Freeze mutable objects assigned to constants.'
126
126
 
127
127
  def on_casgn(node)
128
- if node.expression.nil? # This is only the case for `CONST += ...` or similarg66
128
+ if node.expression.nil? # This is only the case for `CONST += ...` or similar
129
129
  parent = node.parent
130
130
  return unless parent.or_asgn_type? # We only care about `CONST ||= ...`
131
131
 
@@ -121,17 +121,11 @@ module RuboCop
121
121
  end
122
122
 
123
123
  def autocorrect_comparison(corrector, node)
124
- expr = node.source
125
-
126
- new_code = if include_semantic_changes?
127
- expr.sub(/\s*!=\s*nil/, '')
128
- else
129
- expr.sub(/^(\S*)\s*!=\s*nil/, '!\1.nil?')
130
- end
131
-
132
- return if expr == new_code
133
-
134
- corrector.replace(node, new_code)
124
+ if include_semantic_changes?
125
+ corrector.replace(node, node.receiver.source)
126
+ else
127
+ corrector.replace(node, "!#{node.receiver.source}.nil?")
128
+ end
135
129
  end
136
130
 
137
131
  def autocorrect_non_nil(corrector, node, inner_node)
@@ -117,7 +117,7 @@ module RuboCop
117
117
  end
118
118
 
119
119
  def allowed_numbers
120
- cop_config.fetch('AllowedNumbers', []).map(&:to_s)
120
+ @allowed_numbers ||= cop_config.fetch('AllowedNumbers', []).map(&:to_s).freeze
121
121
  end
122
122
 
123
123
  def allowed_patterns
@@ -13,30 +13,50 @@ module RuboCop
13
13
  # classes that logically belong with the main class.
14
14
  #
15
15
  # @example
16
- # # bad
16
+ # # bad - Multiple top-level classes
17
17
  # class Foo
18
18
  # end
19
19
  #
20
20
  # class Bar
21
21
  # end
22
22
  #
23
- # # bad
23
+ # # bad - Multiple top-level modules
24
+ # module Foo
25
+ # end
26
+ #
27
+ # module Bar
28
+ # end
29
+ #
30
+ # # bad - A top-level class and a top-level module
24
31
  # class Foo
25
32
  # end
26
33
  #
27
34
  # module Bar
28
35
  # end
29
36
  #
30
- # # good
37
+ # # good - A single top-level class
31
38
  # class Foo
32
39
  # end
33
40
  #
34
- # # good
41
+ # # good - A single top-level module
42
+ # module Foo
43
+ # end
44
+ #
45
+ # # good - Nested classes within a single top-level class
35
46
  # class Foo
36
47
  # class Bar
37
48
  # end
38
49
  # end
39
50
  #
51
+ # # good - Multiple classes within a single top-level module
52
+ # module Foo
53
+ # class Bar
54
+ # end
55
+ #
56
+ # class Baz
57
+ # end
58
+ # end
59
+ #
40
60
  # @example AllowedClasses: ['AllowedClass']
41
61
  # # good
42
62
  # class Foo
@@ -51,7 +51,7 @@ module RuboCop
51
51
  EXPLODED_MSG = 'Provide an exception class and message as arguments to `%<method>s`.'
52
52
  COMPACT_MSG = 'Provide an exception object as an argument to `%<method>s`.'
53
53
  ACCEPTABLE_ARG_TYPES = %i[
54
- hash forwarded_restarg splat forwarded_restarg forwarded_args
54
+ hash forwarded_restarg splat forwarded_kwrestarg forwarded_args
55
55
  ].freeze
56
56
 
57
57
  RESTRICT_ON_SEND = %i[raise fail].freeze
@@ -64,7 +64,7 @@ module RuboCop
64
64
  def redundant_each_method(node)
65
65
  return if node.last_argument&.block_pass_type?
66
66
 
67
- if node.method?(:each) && !node.parent&.block_type?
67
+ if node.method?(:each) && !node.parent&.any_block_type?
68
68
  ancestor_node = node.each_ancestor(:call).detect do |ancestor|
69
69
  ancestor.receiver == node &&
70
70
  (RESTRICT_ON_SEND.include?(ancestor.method_name) || ancestor.method?(:reverse_each))
@@ -74,8 +74,8 @@ module RuboCop
74
74
  end
75
75
 
76
76
  return unless (prev_method = node.children.first)
77
- return if !prev_method.send_type? ||
78
- prev_method.parent.block_type? || prev_method.last_argument&.block_pass_type?
77
+ return if !prev_method.call_type? || prev_method.parent.any_block_type? ||
78
+ prev_method.last_argument&.block_pass_type?
79
79
 
80
80
  detected = prev_method.method_name.to_s.start_with?('each_') unless node.method?(:each)
81
81