rubocop 1.84.2 → 1.86.2

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 (210) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +99 -16
  3. data/config/obsoletion.yml +5 -0
  4. data/lib/rubocop/cache_config.rb +1 -1
  5. data/lib/rubocop/cli/command/auto_generate_config.rb +28 -2
  6. data/lib/rubocop/cli/command/list_enabled_cops_for.rb +40 -0
  7. data/lib/rubocop/cli/command/mcp.rb +19 -0
  8. data/lib/rubocop/cli/command/show_cops.rb +2 -2
  9. data/lib/rubocop/cli/command/show_docs_url.rb +4 -8
  10. data/lib/rubocop/cli/command/suggest_extensions.rb +1 -1
  11. data/lib/rubocop/cli.rb +7 -7
  12. data/lib/rubocop/comment_config.rb +12 -15
  13. data/lib/rubocop/config.rb +14 -10
  14. data/lib/rubocop/config_finder.rb +1 -1
  15. data/lib/rubocop/config_loader_resolver.rb +2 -1
  16. data/lib/rubocop/config_obsoletion/extracted_cop.rb +4 -2
  17. data/lib/rubocop/config_store.rb +1 -1
  18. data/lib/rubocop/config_validator.rb +1 -1
  19. data/lib/rubocop/cop/autocorrect_logic.rb +2 -1
  20. data/lib/rubocop/cop/correctors/condition_corrector.rb +1 -1
  21. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +1 -5
  22. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
  23. data/lib/rubocop/cop/correctors.rb +28 -0
  24. data/lib/rubocop/cop/documentation.rb +2 -3
  25. data/lib/rubocop/cop/exclude_limit.rb +31 -5
  26. data/lib/rubocop/cop/gemspec/require_mfa.rb +4 -4
  27. data/lib/rubocop/cop/internal_affairs/itblock_handler.rb +69 -0
  28. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +1 -0
  29. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  30. data/lib/rubocop/cop/layout/argument_alignment.rb +2 -2
  31. data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
  32. data/lib/rubocop/cop/layout/dot_position.rb +1 -1
  33. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +9 -2
  34. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +1 -1
  35. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -0
  36. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +12 -2
  37. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +16 -2
  38. data/lib/rubocop/cop/layout/empty_lines_around_module_body.rb +16 -2
  39. data/lib/rubocop/cop/layout/end_alignment.rb +6 -3
  40. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +7 -1
  41. data/lib/rubocop/cop/layout/hash_alignment.rb +1 -1
  42. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  43. data/lib/rubocop/cop/layout/line_length.rb +5 -3
  44. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +9 -2
  45. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +1 -1
  46. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +53 -3
  47. data/lib/rubocop/cop/layout/parameter_alignment.rb +1 -1
  48. data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -1
  49. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +1 -1
  50. data/lib/rubocop/cop/layout/space_around_keyword.rb +3 -1
  51. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +1 -0
  52. data/lib/rubocop/cop/lint/constant_reassignment.rb +59 -9
  53. data/lib/rubocop/cop/lint/constant_resolution.rb +1 -1
  54. data/lib/rubocop/cop/lint/data_define_override.rb +63 -0
  55. data/lib/rubocop/cop/lint/duplicate_methods.rb +55 -8
  56. data/lib/rubocop/cop/lint/empty_block.rb +1 -1
  57. data/lib/rubocop/cop/lint/empty_conditional_body.rb +6 -1
  58. data/lib/rubocop/cop/lint/empty_in_pattern.rb +8 -1
  59. data/lib/rubocop/cop/lint/empty_when.rb +8 -1
  60. data/lib/rubocop/cop/lint/interpolation_check.rb +7 -2
  61. data/lib/rubocop/cop/lint/next_without_accumulator.rb +2 -0
  62. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +3 -1
  63. data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
  64. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +3 -13
  65. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +0 -9
  66. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +23 -6
  67. data/lib/rubocop/cop/lint/require_relative_self_path.rb +2 -0
  68. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +17 -0
  69. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +7 -1
  70. data/lib/rubocop/cop/lint/syntax.rb +25 -1
  71. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -0
  72. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -0
  73. data/lib/rubocop/cop/lint/unreachable_pattern_branch.rb +113 -0
  74. data/lib/rubocop/cop/lint/unused_method_argument.rb +10 -0
  75. data/lib/rubocop/cop/lint/useless_assignment.rb +4 -9
  76. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +4 -4
  77. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +2 -0
  78. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +35 -9
  79. data/lib/rubocop/cop/lint/void.rb +32 -12
  80. data/lib/rubocop/cop/metrics/block_nesting.rb +23 -0
  81. data/lib/rubocop/cop/migration/department_name.rb +12 -1
  82. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  83. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +2 -2
  84. data/lib/rubocop/cop/mixin/configurable_max.rb +6 -5
  85. data/lib/rubocop/cop/mixin/hash_transform_method/autocorrection.rb +63 -0
  86. data/lib/rubocop/cop/mixin/hash_transform_method.rb +10 -60
  87. data/lib/rubocop/cop/mixin.rb +85 -0
  88. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  89. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
  90. data/lib/rubocop/cop/offense.rb +8 -0
  91. data/lib/rubocop/cop/registry.rb +39 -37
  92. data/lib/rubocop/cop/security/eval.rb +15 -2
  93. data/lib/rubocop/cop/style/access_modifier_declarations.rb +14 -2
  94. data/lib/rubocop/cop/style/accessor_grouping.rb +4 -2
  95. data/lib/rubocop/cop/style/alias.rb +4 -1
  96. data/lib/rubocop/cop/style/and_or.rb +1 -0
  97. data/lib/rubocop/cop/style/arguments_forwarding.rb +25 -7
  98. data/lib/rubocop/cop/style/array_join.rb +4 -2
  99. data/lib/rubocop/cop/style/ascii_comments.rb +6 -3
  100. data/lib/rubocop/cop/style/attr.rb +5 -2
  101. data/lib/rubocop/cop/style/bare_percent_literals.rb +3 -1
  102. data/lib/rubocop/cop/style/begin_block.rb +3 -1
  103. data/lib/rubocop/cop/style/block_delimiters.rb +25 -33
  104. data/lib/rubocop/cop/style/case_equality.rb +4 -0
  105. data/lib/rubocop/cop/style/class_and_module_children.rb +10 -2
  106. data/lib/rubocop/cop/style/collection_compact.rb +36 -16
  107. data/lib/rubocop/cop/style/colon_method_call.rb +3 -1
  108. data/lib/rubocop/cop/style/concat_array_literals.rb +2 -0
  109. data/lib/rubocop/cop/style/conditional_assignment.rb +0 -4
  110. data/lib/rubocop/cop/style/copyright.rb +22 -11
  111. data/lib/rubocop/cop/style/date_time.rb +2 -2
  112. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +6 -1
  113. data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
  114. data/lib/rubocop/cop/style/each_with_object.rb +2 -0
  115. data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
  116. data/lib/rubocop/cop/style/empty_class_definition.rb +43 -20
  117. data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
  118. data/lib/rubocop/cop/style/encoding.rb +7 -1
  119. data/lib/rubocop/cop/style/end_block.rb +3 -1
  120. data/lib/rubocop/cop/style/endless_method.rb +8 -3
  121. data/lib/rubocop/cop/style/file_open.rb +84 -0
  122. data/lib/rubocop/cop/style/for.rb +3 -0
  123. data/lib/rubocop/cop/style/format_string_token.rb +29 -2
  124. data/lib/rubocop/cop/style/global_vars.rb +5 -2
  125. data/lib/rubocop/cop/style/guard_clause.rb +9 -6
  126. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +21 -5
  127. data/lib/rubocop/cop/style/hash_lookup_method.rb +19 -7
  128. data/lib/rubocop/cop/style/hash_transform_keys.rb +17 -7
  129. data/lib/rubocop/cop/style/hash_transform_values.rb +17 -7
  130. data/lib/rubocop/cop/style/if_inside_else.rb +16 -7
  131. data/lib/rubocop/cop/style/if_unless_modifier.rb +14 -3
  132. data/lib/rubocop/cop/style/if_with_semicolon.rb +7 -5
  133. data/lib/rubocop/cop/style/inline_comment.rb +4 -1
  134. data/lib/rubocop/cop/style/ip_addresses.rb +1 -2
  135. data/lib/rubocop/cop/style/magic_comment_format.rb +2 -2
  136. data/lib/rubocop/cop/style/map_join.rb +123 -0
  137. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +5 -3
  138. data/lib/rubocop/cop/style/module_member_existence_check.rb +7 -14
  139. data/lib/rubocop/cop/style/multiline_if_then.rb +3 -1
  140. data/lib/rubocop/cop/style/mutable_constant.rb +1 -1
  141. data/lib/rubocop/cop/style/nil_comparison.rb +2 -3
  142. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  143. data/lib/rubocop/cop/style/non_nil_check.rb +5 -11
  144. data/lib/rubocop/cop/style/not.rb +2 -0
  145. data/lib/rubocop/cop/style/numeric_literals.rb +3 -2
  146. data/lib/rubocop/cop/style/one_class_per_file.rb +115 -0
  147. data/lib/rubocop/cop/style/one_line_conditional.rb +4 -3
  148. data/lib/rubocop/cop/style/parallel_assignment.rb +4 -0
  149. data/lib/rubocop/cop/style/partition_instead_of_double_select.rb +270 -0
  150. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -0
  151. data/lib/rubocop/cop/style/predicate_with_kind.rb +84 -0
  152. data/lib/rubocop/cop/style/proc.rb +3 -2
  153. data/lib/rubocop/cop/style/raise_args.rb +1 -1
  154. data/lib/rubocop/cop/style/reduce_to_hash.rb +200 -0
  155. data/lib/rubocop/cop/style/redundant_begin.rb +3 -3
  156. data/lib/rubocop/cop/style/redundant_each.rb +3 -3
  157. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -1
  158. data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +26 -10
  159. data/lib/rubocop/cop/style/redundant_line_continuation.rb +16 -0
  160. data/lib/rubocop/cop/style/redundant_min_max_by.rb +93 -0
  161. data/lib/rubocop/cop/style/redundant_parentheses.rb +25 -22
  162. data/lib/rubocop/cop/style/redundant_percent_q.rb +4 -1
  163. data/lib/rubocop/cop/style/redundant_return.rb +3 -1
  164. data/lib/rubocop/cop/style/redundant_self.rb +2 -2
  165. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +0 -5
  166. data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +114 -0
  167. data/lib/rubocop/cop/style/regexp_literal.rb +29 -0
  168. data/lib/rubocop/cop/style/safe_navigation.rb +7 -7
  169. data/lib/rubocop/cop/style/select_by_kind.rb +158 -0
  170. data/lib/rubocop/cop/style/select_by_range.rb +197 -0
  171. data/lib/rubocop/cop/style/select_by_regexp.rb +51 -21
  172. data/lib/rubocop/cop/style/semicolon.rb +2 -0
  173. data/lib/rubocop/cop/style/single_line_block_params.rb +2 -2
  174. data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -1
  175. data/lib/rubocop/cop/style/single_line_methods.rb +3 -1
  176. data/lib/rubocop/cop/style/sole_nested_conditional.rb +4 -2
  177. data/lib/rubocop/cop/style/special_global_vars.rb +6 -1
  178. data/lib/rubocop/cop/style/symbol_proc.rb +7 -6
  179. data/lib/rubocop/cop/style/tally_method.rb +181 -0
  180. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  181. data/lib/rubocop/cop/style/trailing_method_end_statement.rb +1 -0
  182. data/lib/rubocop/cop/style/while_until_modifier.rb +16 -0
  183. data/lib/rubocop/cop/style/yoda_expression.rb +1 -1
  184. data/lib/rubocop/cop/team.rb +86 -35
  185. data/lib/rubocop/cop/variable_force/branch.rb +2 -2
  186. data/lib/rubocop/directive_comment.rb +2 -1
  187. data/lib/rubocop/formatter/disabled_config_formatter.rb +5 -2
  188. data/lib/rubocop/formatter/formatter_set.rb +1 -1
  189. data/lib/rubocop/formatter/junit_formatter.rb +1 -1
  190. data/lib/rubocop/formatter/simple_text_formatter.rb +0 -2
  191. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
  192. data/lib/rubocop/formatter.rb +22 -21
  193. data/lib/rubocop/lsp/diagnostic.rb +1 -0
  194. data/lib/rubocop/lsp/routes.rb +10 -3
  195. data/lib/rubocop/lsp/runtime.rb +1 -2
  196. data/lib/rubocop/mcp/server.rb +200 -0
  197. data/lib/rubocop/options.rb +17 -4
  198. data/lib/rubocop/path_util.rb +14 -2
  199. data/lib/rubocop/plugin/loader.rb +1 -1
  200. data/lib/rubocop/result_cache.rb +22 -10
  201. data/lib/rubocop/rspec/cop_helper.rb +8 -0
  202. data/lib/rubocop/rspec/shared_contexts.rb +32 -2
  203. data/lib/rubocop/runner.rb +78 -51
  204. data/lib/rubocop/server/cache.rb +5 -7
  205. data/lib/rubocop/server/core.rb +2 -0
  206. data/lib/rubocop/target_finder.rb +14 -7
  207. data/lib/rubocop/target_ruby.rb +18 -12
  208. data/lib/rubocop/version.rb +2 -2
  209. data/lib/rubocop.rb +21 -96
  210. metadata +25 -5
@@ -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
@@ -421,24 +411,18 @@ module RuboCop
421
411
 
422
412
  if node.braces?
423
413
  functional_method?(method_name) || functional_block?(node) ||
424
- (procedural_oneliners_may_have_braces? && !node.multiline?)
414
+ (procedural_oneliners_may_have_braces? && node.single_line?)
425
415
  else
426
416
  procedural_method?(method_name) || !return_value_used?(node)
427
417
  end
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)
@@ -489,7 +481,7 @@ module RuboCop
489
481
  def begin_required?(block_node)
490
482
  # If the block contains `rescue` or `ensure`, it needs to be wrapped in
491
483
  # `begin`...`end` when changing `do-end` to `{}`.
492
- block_node.each_child_node(:rescue, :ensure).any? && !block_node.single_line?
484
+ block_node.each_child_node(:rescue, :ensure).any? && block_node.multiline?
493
485
  end
494
486
 
495
487
  def single_argument_operator_method?(node)
@@ -4,6 +4,10 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # Checks for uses of the case equality operator (`===`).
7
+ # The `===` operator has different behavior depending on the
8
+ # receiver and its use outside of `case`/`when` is confusing.
9
+ # Prefer more explicit alternatives like `is_a?`, `include?`,
10
+ # or `match?`.
7
11
  #
8
12
  # If `AllowOnConstant` option is enabled, the cop will ignore violations when the receiver of
9
13
  # the case equality operator is a constant.
@@ -28,16 +28,24 @@ module RuboCop
28
28
  # manual oversight.
29
29
  #
30
30
  # @example EnforcedStyle: nested (default)
31
+ # # bad
32
+ # class Foo::Bar
33
+ # end
34
+ #
31
35
  # # good
32
- # # have each child on its own line
33
36
  # class Foo
34
37
  # class Bar
35
38
  # end
36
39
  # end
37
40
  #
38
41
  # @example EnforcedStyle: compact
42
+ # # bad
43
+ # class Foo
44
+ # class Bar
45
+ # end
46
+ # end
47
+ #
39
48
  # # good
40
- # # combine definitions as much as possible
41
49
  # class Foo::Bar
42
50
  # end
43
51
  #
@@ -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
@@ -4,7 +4,9 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # Checks for methods invoked via the `::` operator instead
7
- # of the `.` operator (like `FileUtils::rmdir` instead of `FileUtils.rmdir`).
7
+ # of the `.` operator (like `FileUtils::rmdir` instead of
8
+ # `FileUtils.rmdir`). The `::` operator is conventionally used to
9
+ # reference constants, so using it for method calls can be misleading.
8
10
  #
9
11
  # @example
10
12
  # # bad
@@ -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
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Check that a copyright notice was given in each source file.
6
+ # Checks that a copyright notice was given in each source file.
7
7
  #
8
8
  # The default regexp for an acceptable copyright notice can be found in
9
9
  # config/default.yml. The default can be changed as follows:
@@ -46,15 +46,16 @@ module RuboCop
46
46
  token = insert_notice_before(processed_source)
47
47
  range = token.nil? ? range_between(0, 0) : token.pos
48
48
 
49
- corrector.insert_before(range, "#{autocorrect_notice}\n")
49
+ corrector.insert_before(range, "#{normalized_autocorrect_notice}\n")
50
50
  end
51
51
 
52
- def notice
53
- cop_config['Notice']
54
- end
52
+ def normalized_autocorrect_notice
53
+ autocorrect_notice.lines.map do |line|
54
+ next line if line.start_with?('#')
55
+ next "#\n" if line.chomp.empty?
55
56
 
56
- def autocorrect_notice
57
- cop_config['AutocorrectNotice']
57
+ "# #{line}"
58
+ end.join
58
59
  end
59
60
 
60
61
  def verify_autocorrect_notice!
@@ -62,8 +63,7 @@ module RuboCop
62
63
  raise Warning, "#{cop_name}: #{AUTOCORRECT_EMPTY_WARNING}"
63
64
  end
64
65
 
65
- regex = Regexp.new(notice)
66
- return if autocorrect_notice.gsub(/^# */, '').match?(regex)
66
+ return if normalized_autocorrect_notice.gsub(/^# */, '').match?(notice_regexp)
67
67
 
68
68
  message = "AutocorrectNotice '#{autocorrect_notice}' must match Notice /#{notice}/"
69
69
  raise Warning, "#{cop_name}: #{message}"
@@ -91,18 +91,29 @@ module RuboCop
91
91
  end
92
92
 
93
93
  def notice_found?(processed_source)
94
- notice_regexp = Regexp.new(notice.lines.map(&:strip).join)
95
94
  multiline_notice = +''
96
95
  processed_source.tokens.each do |token|
97
96
  break unless token.comment?
98
97
 
99
- multiline_notice << token.text.sub(/\A# */, '')
98
+ multiline_notice << token.text.sub(/\A# */, '') << "\n"
100
99
 
101
100
  break if notice_regexp.match?(token.text)
102
101
  end
103
102
 
104
103
  multiline_notice.match?(notice_regexp)
105
104
  end
105
+
106
+ def notice_regexp
107
+ @notice_regexp ||= Regexp.new(notice.sub(/\A(?:\\A|\^)?#(?:\\s[*+?]?|\s)*/, ''))
108
+ end
109
+
110
+ def notice
111
+ cop_config['Notice']
112
+ end
113
+
114
+ def autocorrect_notice
115
+ cop_config['AutocorrectNotice']
116
+ end
106
117
  end
107
118
  end
108
119
  end
@@ -3,8 +3,8 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks for consistent usage of the `DateTime` class over the
7
- # `Time` class. This cop is disabled by default since these classes,
6
+ # Checks for consistent usage of the `Time` class over the
7
+ # `DateTime` class. This cop is disabled by default since these classes,
8
8
  # although highly overlapping, have particularities that make them not
9
9
  # replaceable in certain situations when dealing with multiple timezones
10
10
  # and/or DST.
@@ -161,7 +161,12 @@ module RuboCop
161
161
  source = source.gsub(COMMENT_REGEXP, '')
162
162
  return if source.blank?
163
163
 
164
- /\s*#{Regexp.escape(source.strip)}/
164
+ # Treat `\#` (an escaped interpolation marker in the heredoc) as matching
165
+ # either `\#` or `#` in the comment, since the comment may show either
166
+ # the literal source form or the runtime appearance.
167
+ segments = source.strip.split('\\#', -1).map { |segment| Regexp.escape(segment) }
168
+
169
+ /\s*#{segments.join('\\\\?#')}/
165
170
  end
166
171
  end
167
172
  end
@@ -26,7 +26,7 @@ module RuboCop
26
26
 
27
27
  MSG = 'Use `Integer#times` for a simple loop which iterates a fixed number of times.'
28
28
 
29
- def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
29
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
30
30
  return unless offending?(node)
31
31
 
32
32
  send_node = node.send_node
@@ -40,6 +40,8 @@ module RuboCop
40
40
  end
41
41
  end
42
42
 
43
+ alias on_itblock on_block
44
+
43
45
  def on_numblock(node)
44
46
  each_with_object_numblock_candidate?(node) do |method, body|
45
47
  _, method_name, method_arg = *method
@@ -28,7 +28,7 @@ module RuboCop
28
28
 
29
29
  MSG = 'Omit pipes for the empty block parameters.'
30
30
 
31
- def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
31
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
32
32
  send_node = node.send_node
33
33
  check(node) unless send_node.send_type? && send_node.lambda_literal?
34
34
  end
@@ -5,15 +5,26 @@ module RuboCop
5
5
  module Style
6
6
  # Enforces consistent style for empty class definitions.
7
7
  #
8
- # This cop can enforce either a two-line class definition or `Class.new`
8
+ # This cop can enforce either a standard class definition or `Class.new`
9
9
  # for classes with no body.
10
10
  #
11
11
  # The supported styles are:
12
12
  #
13
- # * class_definition (default) - prefer two-line 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
- # @example EnforcedStyle: class_definition (default)
16
+ # One difference between the two styles is that the `Class.new` form does not make
17
+ # the subclass name available to the base class's `inherited` callback.
18
+ # For this reason, `EnforcedStyle: class_keyword` is set as the default style.
19
+ # Class definitions without a superclass, which are not involved in inheritance,
20
+ # are not detected. This ensures safe detection regardless of the applied style.
21
+ # This avoids overlapping responsibilities with the `Lint/EmptyClass` cop.
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
+ #
27
+ # @example EnforcedStyle: class_keyword (default)
17
28
  # # bad
18
29
  # FooError = Class.new(StandardError)
19
30
  #
@@ -35,33 +46,43 @@ module RuboCop
35
46
  # # good
36
47
  # FooError = Class.new(StandardError)
37
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
+ #
38
57
  class EmptyClassDefinition < Base
39
58
  include ConfigurableEnforcedStyle
40
- include RangeHelp
41
59
  extend AutoCorrector
42
60
 
43
- MSG_CLASS_DEFINITION =
44
- 'Prefer a two-line class definition over `Class.new` for classes with no body.'
45
- MSG_CLASS_NEW = 'Prefer `Class.new` over class definition for classes with no body.'
61
+ MSG_CLASS_KEYWORD =
62
+ 'Use the `class` keyword instead of `Class.new` to define an empty class.'
63
+ MSG_CLASS_NEW = 'Use `Class.new` instead of the `class` keyword to define an empty class.'
46
64
 
47
65
  # @!method class_new_assignment(node)
48
66
  def_node_matcher :class_new_assignment, <<~PATTERN
49
- (casgn _ _ $(send (const _ :Class) :new ...))
67
+ (casgn _ _ $(send (const _ :Class) :new _))
50
68
  PATTERN
51
69
 
52
70
  def on_casgn(node)
53
- return unless style == :class_definition
71
+ return unless %i[class_keyword class_definition].include?(style)
54
72
  return unless (class_new_node = class_new_assignment(node))
55
73
  return if (arg = class_new_node.first_argument) && !arg.const_type?
74
+ return if allowed_parent_class?(class_new_node.first_argument.source)
56
75
 
57
- add_offense(node, message: MSG_CLASS_DEFINITION) do |corrector|
76
+ add_offense(node, message: MSG_CLASS_KEYWORD) do |corrector|
58
77
  autocorrect_class_new(corrector, node, class_new_node)
59
78
  end
60
79
  end
61
80
 
62
81
  def on_class(node)
63
82
  return unless style == :class_new
83
+ return unless node.parent_class
64
84
  return if (body = node.body) && !body.children.empty?
85
+ return if allowed_parent_class?(node.parent_class.source)
65
86
 
66
87
  add_offense(node, message: MSG_CLASS_NEW) do |corrector|
67
88
  autocorrect_class_definition(corrector, node)
@@ -70,25 +91,27 @@ module RuboCop
70
91
 
71
92
  private
72
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
+
73
102
  def autocorrect_class_new(corrector, node, class_new_node)
74
103
  indent = ' ' * node.loc.column
75
104
  class_name = node.name
76
- if (parent_class = class_new_node.first_argument)
77
- parent_class_name = " < #{parent_class.source}"
78
- end
105
+ parent_class_name = class_new_node.first_argument.source
79
106
 
80
- corrector.replace(node, "class #{class_name}#{parent_class_name}\n#{indent}end")
107
+ corrector.replace(node, "class #{class_name} < #{parent_class_name}\n#{indent}end")
81
108
  end
82
109
 
83
110
  def autocorrect_class_definition(corrector, node)
84
- indent = ' ' * node.loc.column
85
111
  class_name = node.identifier.source
86
- if (parent_class = node.parent_class)
87
- parent_class_name = "(#{parent_class.source})"
88
- end
89
- range = range_by_whole_lines(node.source_range, include_final_newline: true)
112
+ parent_class_name = node.parent_class.source
90
113
 
91
- corrector.replace(range, "#{indent}#{class_name} = Class.new#{parent_class_name}\n")
114
+ corrector.replace(node, "#{class_name} = Class.new(#{parent_class_name})")
92
115
  end
93
116
  end
94
117
  end
@@ -23,7 +23,7 @@ module RuboCop
23
23
 
24
24
  MSG = 'Omit parentheses for the empty lambda parameters.'
25
25
 
26
- def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
26
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
27
27
  send_node = node.send_node
28
28
  return unless send_node.send_type?
29
29
 
@@ -3,12 +3,18 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks ensures source files have no utf-8 encoding comments.
6
+ # Checks that source files have no utf-8 encoding comments.
7
+ # Since Ruby 2.0, UTF-8 is the default source encoding, so
8
+ # these comments are no longer necessary and just add noise.
9
+ #
7
10
  # @example
8
11
  # # bad
9
12
  # # encoding: UTF-8
10
13
  # # coding: UTF-8
11
14
  # # -*- coding: UTF-8 -*-
15
+ #
16
+ # # good
17
+ # # No encoding comment needed
12
18
  class Encoding < Base
13
19
  include RangeHelp
14
20
  extend AutoCorrector
@@ -3,7 +3,9 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks for END blocks.
6
+ # Checks for `END` blocks. `END` blocks are Perl-style constructs
7
+ # and `Kernel#at_exit` is the idiomatic Ruby alternative, as it's
8
+ # explicit and can be used anywhere.
7
9
  #
8
10
  # @example
9
11
  # # bad
@@ -157,6 +157,7 @@ module RuboCop
157
157
  handle_require_always_style(node)
158
158
  end
159
159
  end
160
+ alias on_defs on_def
160
161
 
161
162
  private
162
163
 
@@ -170,7 +171,7 @@ module RuboCop
170
171
  end
171
172
 
172
173
  def handle_require_single_line_style(node)
173
- if node.endless? && !node.single_line?
174
+ if node.endless? && node.multiline?
174
175
  add_offense(node, message: MSG_MULTI_LINE) do |corrector|
175
176
  correct_to_multiline(corrector, node)
176
177
  end
@@ -207,7 +208,7 @@ module RuboCop
207
208
 
208
209
  def correct_to_multiline(corrector, node)
209
210
  replacement = <<~RUBY.strip
210
- def #{node.method_name}#{arguments(node)}
211
+ def #{receiver(node)}#{node.method_name}#{arguments(node)}
211
212
  #{node.body.source}
212
213
  end
213
214
  RUBY
@@ -217,10 +218,14 @@ module RuboCop
217
218
 
218
219
  def endless_replacement(node)
219
220
  <<~RUBY.strip
220
- def #{node.method_name}#{arguments(node)} = #{node.body.source}
221
+ def #{receiver(node)}#{node.method_name}#{arguments(node)} = #{node.body.source}
221
222
  RUBY
222
223
  end
223
224
 
225
+ def receiver(node)
226
+ node.receiver ? "#{node.receiver.source}#{node.loc.operator.source}" : ''
227
+ end
228
+
224
229
  def arguments(node, missing = '')
225
230
  node.arguments.any? ? node.arguments.source : missing
226
231
  end