rubocop 1.89.0 → 1.90.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 (176) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +74 -7
  3. data/exe/rubocop +1 -1
  4. data/lib/rubocop/cached_data.rb +7 -2
  5. data/lib/rubocop/cli/command/execute_runner.rb +31 -0
  6. data/lib/rubocop/cli.rb +2 -4
  7. data/lib/rubocop/comment_config/directive_range.rb +32 -0
  8. data/lib/rubocop/comment_config/disable_next.rb +97 -0
  9. data/lib/rubocop/comment_config.rb +46 -30
  10. data/lib/rubocop/config.rb +23 -5
  11. data/lib/rubocop/config_loader.rb +1 -2
  12. data/lib/rubocop/config_loader_resolver.rb +1 -2
  13. data/lib/rubocop/config_validator.rb +1 -2
  14. data/lib/rubocop/cop/autocorrect_logic.rb +8 -0
  15. data/lib/rubocop/cop/base.rb +34 -2
  16. data/lib/rubocop/cop/corrector.rb +3 -3
  17. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -2
  18. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb +1 -2
  19. data/lib/rubocop/cop/layout/block_alignment.rb +1 -2
  20. data/lib/rubocop/cop/layout/class_structure.rb +102 -26
  21. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -2
  22. data/lib/rubocop/cop/layout/empty_line_after_magic_comment.rb +63 -5
  23. data/lib/rubocop/cop/layout/end_alignment.rb +1 -2
  24. data/lib/rubocop/cop/layout/extra_spacing.rb +105 -3
  25. data/lib/rubocop/cop/layout/first_argument_indentation.rb +21 -1
  26. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -2
  27. data/lib/rubocop/cop/layout/line_length.rb +1 -2
  28. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +1 -2
  29. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +5 -5
  30. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +1 -2
  31. data/lib/rubocop/cop/layout/space_around_operators.rb +7 -0
  32. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +2 -4
  33. data/lib/rubocop/cop/lint/ambiguous_range.rb +1 -2
  34. data/lib/rubocop/cop/lint/argument_mismatch.rb +89 -0
  35. data/lib/rubocop/cop/lint/constant_reassignment.rb +17 -11
  36. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +84 -8
  37. data/lib/rubocop/cop/lint/deprecated_reference.rb +11 -2
  38. data/lib/rubocop/cop/lint/duplicate_methods.rb +112 -14
  39. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +1 -2
  40. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +1 -2
  41. data/lib/rubocop/cop/lint/interpolation_check.rb +1 -2
  42. data/lib/rubocop/cop/lint/literal_as_condition.rb +3 -6
  43. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +3 -6
  44. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +25 -12
  45. data/lib/rubocop/cop/lint/name_typo.rb +53 -6
  46. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +15 -0
  47. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +98 -19
  48. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +47 -7
  49. data/lib/rubocop/cop/lint/redundant_require_statement.rb +1 -2
  50. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +1 -2
  51. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +1 -2
  52. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +1 -2
  53. data/lib/rubocop/cop/lint/redundant_with_index.rb +1 -2
  54. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +1 -2
  55. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +2 -4
  56. data/lib/rubocop/cop/lint/super_argument_mismatch.rb +179 -0
  57. data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +1 -2
  58. data/lib/rubocop/cop/lint/to_enum_arguments.rb +1 -2
  59. data/lib/rubocop/cop/lint/unreachable_code.rb +1 -2
  60. data/lib/rubocop/cop/lint/unused_private_method.rb +94 -17
  61. data/lib/rubocop/cop/lint/useless_access_modifier.rb +1 -2
  62. data/lib/rubocop/cop/lint/useless_assignment.rb +1 -2
  63. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +36 -0
  64. data/lib/rubocop/cop/lint/useless_method_definition.rb +1 -1
  65. data/lib/rubocop/cop/lint/useless_rescue.rb +1 -2
  66. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +61 -7
  67. data/lib/rubocop/cop/lint/void.rb +1 -2
  68. data/lib/rubocop/cop/lint.rb +2 -0
  69. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -2
  70. data/lib/rubocop/cop/migration/department_name.rb +1 -2
  71. data/lib/rubocop/cop/mixin/comments_help.rb +1 -2
  72. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +1 -2
  73. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +1 -2
  74. data/lib/rubocop/cop/mixin/hash_subset.rb +1 -2
  75. data/lib/rubocop/cop/mixin/indexed_method_arity.rb +94 -0
  76. data/lib/rubocop/cop/mixin/line_length_help.rb +7 -1
  77. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +51 -6
  78. data/lib/rubocop/cop/mixin/range_help.rb +1 -2
  79. data/lib/rubocop/cop/mixin/statement_modifier.rb +13 -7
  80. data/lib/rubocop/cop/mixin/trailing_comma.rb +2 -4
  81. data/lib/rubocop/cop/mixin.rb +4 -0
  82. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +16 -0
  83. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +2 -4
  84. data/lib/rubocop/cop/naming/method_name.rb +5 -2
  85. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -2
  86. data/lib/rubocop/cop/offense.rb +27 -4
  87. data/lib/rubocop/cop/registry.rb +34 -10
  88. data/lib/rubocop/cop/style/accessor_grouping.rb +1 -2
  89. data/lib/rubocop/cop/style/arguments_forwarding.rb +3 -6
  90. data/lib/rubocop/cop/style/array_first_last.rb +1 -2
  91. data/lib/rubocop/cop/style/bitwise_predicate.rb +1 -2
  92. data/lib/rubocop/cop/style/block_delimiters.rb +3 -6
  93. data/lib/rubocop/cop/style/case_like_if.rb +2 -4
  94. data/lib/rubocop/cop/style/class_and_module_children.rb +1 -2
  95. data/lib/rubocop/cop/style/combinable_loops.rb +1 -2
  96. data/lib/rubocop/cop/style/concat_array_literals.rb +1 -2
  97. data/lib/rubocop/cop/style/conditional_assignment.rb +1 -2
  98. data/lib/rubocop/cop/style/constant_visibility.rb +1 -2
  99. data/lib/rubocop/cop/style/copyright.rb +1 -2
  100. data/lib/rubocop/cop/style/directive_scope.rb +171 -0
  101. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +36 -10
  102. data/lib/rubocop/cop/style/empty_case_condition.rb +1 -2
  103. data/lib/rubocop/cop/style/format_string_token.rb +1 -2
  104. data/lib/rubocop/cop/style/guard_clause.rb +1 -2
  105. data/lib/rubocop/cop/style/hash_each_methods.rb +1 -2
  106. data/lib/rubocop/cop/style/hash_syntax.rb +1 -2
  107. data/lib/rubocop/cop/style/identical_conditional_branches.rb +2 -4
  108. data/lib/rubocop/cop/style/if_inside_else.rb +1 -2
  109. data/lib/rubocop/cop/style/if_unless_modifier.rb +1 -2
  110. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +1 -2
  111. data/lib/rubocop/cop/style/map_to_hash.rb +1 -2
  112. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -4
  113. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +1 -2
  114. data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -2
  115. data/lib/rubocop/cop/style/negated_if_else_condition.rb +1 -2
  116. data/lib/rubocop/cop/style/negative_array_index.rb +2 -4
  117. data/lib/rubocop/cop/style/nil_comparison.rb +1 -2
  118. data/lib/rubocop/cop/style/operator_method_call.rb +1 -2
  119. data/lib/rubocop/cop/style/parallel_assignment.rb +1 -2
  120. data/lib/rubocop/cop/style/redundant_assignment.rb +1 -2
  121. data/lib/rubocop/cop/style/redundant_condition.rb +2 -4
  122. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +1 -2
  123. data/lib/rubocop/cop/style/redundant_each.rb +1 -2
  124. data/lib/rubocop/cop/style/redundant_format.rb +2 -4
  125. data/lib/rubocop/cop/style/redundant_parentheses.rb +2 -4
  126. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +1 -2
  127. data/lib/rubocop/cop/style/redundant_return.rb +1 -2
  128. data/lib/rubocop/cop/style/redundant_self_assignment.rb +1 -2
  129. data/lib/rubocop/cop/style/redundant_string_escape.rb +1 -2
  130. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -2
  131. data/lib/rubocop/cop/style/safe_navigation.rb +1 -2
  132. data/lib/rubocop/cop/style/sample.rb +11 -6
  133. data/lib/rubocop/cop/style/select_by_regexp.rb +1 -2
  134. data/lib/rubocop/cop/style/semicolon.rb +2 -4
  135. data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -2
  136. data/lib/rubocop/cop/style/sole_nested_conditional.rb +1 -2
  137. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +2 -0
  138. data/lib/rubocop/cop/style/string_concatenation.rb +18 -5
  139. data/lib/rubocop/cop/style/super_arguments.rb +1 -2
  140. data/lib/rubocop/cop/style/symbol_proc.rb +1 -2
  141. data/lib/rubocop/cop/style/time_now.rb +41 -0
  142. data/lib/rubocop/cop/style/yoda_condition.rb +1 -2
  143. data/lib/rubocop/cop/style.rb +2 -0
  144. data/lib/rubocop/cop/team.rb +11 -3
  145. data/lib/rubocop/cop/util.rb +2 -4
  146. data/lib/rubocop/cop/variable_force/variable.rb +1 -2
  147. data/lib/rubocop/cops_documentation_generator.rb +3 -6
  148. data/lib/rubocop/directive_comment.rb +37 -3
  149. data/lib/rubocop/formatter/emacs_style_formatter.rb +3 -1
  150. data/lib/rubocop/formatter/html_formatter.rb +2 -4
  151. data/lib/rubocop/formatter/json_formatter.rb +11 -6
  152. data/lib/rubocop/formatter/junit_formatter.rb +26 -9
  153. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -2
  154. data/lib/rubocop/formatter/simple_text_formatter.rb +4 -3
  155. data/lib/rubocop/formatter/tap_formatter.rb +3 -1
  156. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -2
  157. data/lib/rubocop/lockfile.rb +18 -0
  158. data/lib/rubocop/lsp/diagnostic.rb +4 -8
  159. data/lib/rubocop/lsp/routes.rb +48 -19
  160. data/lib/rubocop/mcp/server.rb +1 -2
  161. data/lib/rubocop/options.rb +10 -6
  162. data/lib/rubocop/path_util.rb +1 -2
  163. data/lib/rubocop/plugin/configuration_integrator.rb +6 -3
  164. data/lib/rubocop/rake_task.rb +1 -2
  165. data/lib/rubocop/rspec/expect_offense.rb +2 -4
  166. data/lib/rubocop/rspec/shared_contexts.rb +4 -2
  167. data/lib/rubocop/runner.rb +26 -23
  168. data/lib/rubocop/server/cache.rb +1 -2
  169. data/lib/rubocop/server/cli.rb +2 -4
  170. data/lib/rubocop/server.rb +14 -0
  171. data/lib/rubocop/target_finder.rb +4 -1
  172. data/lib/rubocop/todo_audit.rb +111 -0
  173. data/lib/rubocop/version.rb +3 -5
  174. data/lib/rubocop.rb +3 -0
  175. data/lib/ruby_lsp/rubocop/addon.rb +1 -2
  176. metadata +13 -5
@@ -100,7 +100,7 @@ module RuboCop
100
100
  comment_line?(processed_source[node.first_line - 2])
101
101
  end
102
102
 
103
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
103
+ # rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
104
104
  def groupable_accessor?(node)
105
105
  return true unless (previous_expression = node.left_siblings.last)
106
106
 
@@ -122,7 +122,6 @@ module RuboCop
122
122
  previous_expression.access_modifier? ||
123
123
  node.first_line - previous_expression.last_line > 1 # there is a space between nodes
124
124
  end
125
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
126
125
 
127
126
  def class_send_elements(class_node)
128
127
  class_def = class_node.body
@@ -197,7 +197,7 @@ module RuboCop
197
197
  send_classifications.all? { |_, c, _, _| all_classifications.include?(c) }
198
198
  end
199
199
 
200
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
200
+ # rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
201
201
  def add_forward_all_offenses(node, send_classifications, forwardable_args)
202
202
  rest_arg, kwrest_arg, block_arg = *forwardable_args
203
203
  registered_block_arg_offense = false
@@ -220,13 +220,12 @@ module RuboCop
220
220
 
221
221
  register_forward_all_offense(node, node.arguments, rest_arg || kwrest_arg)
222
222
  end
223
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
224
223
 
225
224
  def forward_all_first_argument(node)
226
225
  node.arguments.reverse_each.find(&:forwarded_restarg_type?)
227
226
  end
228
227
 
229
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
228
+ # rubocop:disable-next Metrics/AbcSize, Metrics/MethodLength
230
229
  def add_post_ruby_32_offenses(def_node, send_classifications, forwardable_args)
231
230
  return unless use_anonymous_forwarding?
232
231
  return unless all_forwarding_offenses_correctable?(send_classifications)
@@ -250,7 +249,6 @@ module RuboCop
250
249
  end
251
250
  end
252
251
  end
253
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
254
252
 
255
253
  def non_splat_or_block_pass_lvar_references(body)
256
254
  body.each_descendant(:lvar, :lvasgn).filter_map do |lvar|
@@ -473,7 +471,7 @@ module RuboCop
473
471
 
474
472
  private
475
473
 
476
- # rubocop:disable Metrics/CyclomaticComplexity
474
+ # rubocop:disable-next Metrics/CyclomaticComplexity
477
475
  def can_forward_all?
478
476
  return false if any_arg_referenced?
479
477
  return false if ruby_30_or_lower_optarg?
@@ -483,7 +481,6 @@ module RuboCop
483
481
 
484
482
  no_additional_args? || (target_ruby_version >= 3.0 && no_post_splat_args?)
485
483
  end
486
- # rubocop:enable Metrics/CyclomaticComplexity
487
484
 
488
485
  # def foo(a = 41, ...) is a syntax error in 3.0.
489
486
  def ruby_30_or_lower_optarg?
@@ -31,7 +31,7 @@ module RuboCop
31
31
  MSG = 'Use `%<preferred>s`.'
32
32
  RESTRICT_ON_SEND = %i[[]].freeze
33
33
 
34
- # rubocop:disable Metrics/AbcSize
34
+ # rubocop:disable-next Metrics/AbcSize
35
35
  def on_send(node)
36
36
  return unless node.arguments.size == 1 && node.first_argument.int_type?
37
37
 
@@ -49,7 +49,6 @@ module RuboCop
49
49
  corrector.replace(offense_range, preferred_value(node, preferred))
50
50
  end
51
51
  end
52
- # rubocop:enable Metrics/AbcSize
53
52
  alias on_csend on_send
54
53
 
55
54
  private
@@ -70,7 +70,7 @@ module RuboCop
70
70
  (send _ :& _))
71
71
  PATTERN
72
72
 
73
- # rubocop:disable Metrics/AbcSize
73
+ # rubocop:disable-next Metrics/AbcSize
74
74
  def on_send(node)
75
75
  return unless node.receiver&.begin_type?
76
76
  return unless (preferred_method = preferred_method(node))
@@ -88,7 +88,6 @@ module RuboCop
88
88
  corrector.replace(node, preferred)
89
89
  end
90
90
  end
91
- # rubocop:enable Metrics/AbcSize
92
91
 
93
92
  private
94
93
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # rubocop:disable Metrics/ClassLength
3
+ # rubocop:disable-next Metrics/ClassLength
4
4
  module RuboCop
5
5
  module Cop
6
6
  module Style
@@ -344,7 +344,7 @@ module RuboCop
344
344
  node.respond_to?(:block_node) && node.block_node
345
345
  end
346
346
 
347
- # rubocop:disable Metrics/CyclomaticComplexity
347
+ # rubocop:disable-next Metrics/CyclomaticComplexity
348
348
  def get_blocks(node, &block)
349
349
  case node.type
350
350
  when :block, :numblock, :itblock
@@ -368,9 +368,8 @@ module RuboCop
368
368
  node.each_child_node { |child| get_blocks(child, &block) }
369
369
  end
370
370
  end
371
- # rubocop:enable Metrics/CyclomaticComplexity
372
371
 
373
- # rubocop:disable Metrics/CyclomaticComplexity -- inlined special_method checks to avoid double evaluation
372
+ # rubocop:disable-next Metrics/CyclomaticComplexity -- inlined special_method checks to avoid double evaluation
374
373
  def proper_block_style?(node)
375
374
  return true if require_do_end?(node)
376
375
 
@@ -385,7 +384,6 @@ module RuboCop
385
384
  when :always_braces then node.braces?
386
385
  end
387
386
  end
388
- # rubocop:enable Metrics/CyclomaticComplexity
389
387
 
390
388
  def require_do_end?(node)
391
389
  return false if node.braces? || node.multiline?
@@ -507,4 +505,3 @@ module RuboCop
507
505
  end
508
506
  end
509
507
  end
510
- # rubocop:enable Metrics/ClassLength
@@ -98,7 +98,7 @@ module RuboCop
98
98
  node.elsif_conditional? && min_branches_count?(node)
99
99
  end
100
100
 
101
- # rubocop:disable Metrics/MethodLength
101
+ # rubocop:disable-next Metrics/MethodLength
102
102
  def find_target(node)
103
103
  case node.type
104
104
  when :begin
@@ -116,7 +116,6 @@ module RuboCop
116
116
  find_target_in_send_node(node)
117
117
  end
118
118
  end
119
- # rubocop:enable Metrics/MethodLength
120
119
 
121
120
  def find_target_in_send_node(node)
122
121
  case node.method_name
@@ -181,7 +180,7 @@ module RuboCop
181
180
  conditions << condition if condition
182
181
  end
183
182
 
184
- # rubocop:disable Metrics/CyclomaticComplexity
183
+ # rubocop:disable-next Metrics/CyclomaticComplexity
185
184
  def condition_from_send_node(node, target)
186
185
  case node.method_name
187
186
  when :is_a?
@@ -196,7 +195,6 @@ module RuboCop
196
195
  condition_from_include_or_cover_node(node, target)
197
196
  end
198
197
  end
199
- # rubocop:enable Metrics/CyclomaticComplexity
200
198
 
201
199
  def condition_from_equality_node(node, target)
202
200
  condition = condition_from_binary_op(node.receiver, node.first_argument, target)
@@ -194,7 +194,7 @@ module RuboCop
194
194
  "#{node.body.children.first.const_name}"
195
195
  end
196
196
 
197
- # rubocop:disable Metrics/AbcSize
197
+ # rubocop:disable-next Metrics/AbcSize
198
198
  def remove_end(corrector, body)
199
199
  remove_begin_pos = if same_line?(body.loc.name, body.loc.end)
200
200
  body.loc.name.end_pos
@@ -206,7 +206,6 @@ module RuboCop
206
206
 
207
207
  corrector.remove(range)
208
208
  end
209
- # rubocop:enable Metrics/AbcSize
210
209
 
211
210
  def unindent(corrector, node)
212
211
  return unless node.body.children.last
@@ -64,7 +64,7 @@ module RuboCop
64
64
 
65
65
  MSG = 'Combine this loop with the previous loop.'
66
66
 
67
- # rubocop:disable Metrics/CyclomaticComplexity
67
+ # rubocop:disable-next Metrics/CyclomaticComplexity
68
68
  def on_block(node)
69
69
  return unless node.parent&.begin_type?
70
70
  return unless collection_looping_method?(node)
@@ -77,7 +77,6 @@ module RuboCop
77
77
  combine_with_left_sibling(corrector, node)
78
78
  end
79
79
  end
80
- # rubocop:enable Metrics/CyclomaticComplexity
81
80
 
82
81
  alias on_numblock on_block
83
82
  alias on_itblock on_block
@@ -30,7 +30,7 @@ module RuboCop
30
30
  'Use `push` with elements as arguments without array brackets instead of `%<current>s`.'
31
31
  RESTRICT_ON_SEND = %i[concat].freeze
32
32
 
33
- # rubocop:disable Metrics
33
+ # rubocop:disable-next Metrics
34
34
  def on_send(node)
35
35
  return if node.arguments.empty?
36
36
  return unless node.arguments.all?(&:array_type?)
@@ -68,7 +68,6 @@ module RuboCop
68
68
  end
69
69
  end
70
70
  end
71
- # rubocop:enable Metrics
72
71
  alias on_csend on_send
73
72
 
74
73
  private
@@ -448,7 +448,7 @@ module RuboCop
448
448
  # Helper module to provide common methods to ConditionalAssignment
449
449
  # correctors
450
450
  module ConditionalCorrectorHelper
451
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
451
+ # rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity
452
452
  def remove_whitespace_in_branches(corrector, branch, condition, column)
453
453
  branch.each_node do |child|
454
454
  next if child.source_range.nil?
@@ -468,7 +468,6 @@ module RuboCop
468
468
 
469
469
  corrector.remove_preceding(condition.loc.end, condition.loc.end.column - column)
470
470
  end
471
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
472
471
 
473
472
  def same_line?(node1, node2)
474
473
  RuboCop::Cop::Util.same_line?(node1, node2)
@@ -82,7 +82,7 @@ module RuboCop
82
82
  end
83
83
  end
84
84
 
85
- # rubocop:disable Metrics/AbcSize
85
+ # rubocop:disable-next Metrics/AbcSize
86
86
  def visibility_declaration?(node)
87
87
  node.parent.each_child_node(:send).any? do |child|
88
88
  next false unless (arguments = visibility_declaration_for(child))
@@ -98,7 +98,6 @@ module RuboCop
98
98
  constant_values.include?(node.name)
99
99
  end
100
100
  end
101
- # rubocop:enable Metrics/AbcSize
102
101
  end
103
102
  end
104
103
  end
@@ -25,7 +25,7 @@ module RuboCop
25
25
  MSG = 'Include a copyright notice matching /%<notice>s/ before any code.'
26
26
  AUTOCORRECT_EMPTY_WARNING = 'An AutocorrectNotice must be defined in your RuboCop config'
27
27
 
28
- # rubocop:disable Metrics/AbcSize
28
+ # rubocop:disable-next Metrics/AbcSize
29
29
  def on_new_investigation
30
30
  return if notice.empty? || notice_found?(processed_source)
31
31
 
@@ -43,7 +43,6 @@ module RuboCop
43
43
  end
44
44
  end
45
45
  end
46
- # rubocop:enable Metrics/AbcSize
47
46
 
48
47
  private
49
48
 
@@ -0,0 +1,171 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for directive scopes that can be expressed with the tighter
7
+ # `disable-next` form: a `disable`/`enable` pair wrapping exactly one
8
+ # statement, or a `push`/`pop` that only disables cops around exactly
9
+ # one statement. A statement-scoped directive cannot drift as the
10
+ # surrounding code changes and needs no closing boundary.
11
+ #
12
+ # @safety
13
+ # The autocorrection is unsafe because the suppression scope shrinks
14
+ # from the whole region to the statement: offenses of the suppressed
15
+ # cops located on the directive lines themselves resurface.
16
+ #
17
+ # @example
18
+ # # bad
19
+ # # rubocop:disable Metrics/AbcSize
20
+ # def foo
21
+ # end
22
+ # # rubocop:enable Metrics/AbcSize
23
+ #
24
+ # # good
25
+ # # rubocop:disable-next Metrics/AbcSize
26
+ # def foo
27
+ # end
28
+ #
29
+ # # bad
30
+ # # rubocop:push -Metrics/AbcSize
31
+ # def foo
32
+ # end
33
+ # # rubocop:pop
34
+ #
35
+ # # good
36
+ # # rubocop:disable-next Metrics/AbcSize
37
+ # def foo
38
+ # end
39
+ #
40
+ # # good - the region spans more than one statement
41
+ # # rubocop:disable Metrics/AbcSize
42
+ # def foo
43
+ # end
44
+ #
45
+ # def bar
46
+ # end
47
+ # # rubocop:enable Metrics/AbcSize
48
+ #
49
+ class DirectiveScope < Base
50
+ include RangeHelp
51
+ extend AutoCorrector
52
+
53
+ MSG_PAIR = 'Use `%<mode>s-next` instead of a `%<mode>s`/`enable` pair ' \
54
+ 'around a single statement.'
55
+ MSG_PUSH_POP = 'Use `disable-next` instead of `push`/`pop` around a single statement.'
56
+
57
+ def on_new_investigation
58
+ processed_source.comments.each do |comment|
59
+ directive = DirectiveComment.new(comment)
60
+ next unless comment_config.comment_only_line?(directive.line_number)
61
+
62
+ if plain_disable?(directive)
63
+ check_pair(directive)
64
+ elsif disable_only_push?(directive)
65
+ check_push_pop(directive)
66
+ end
67
+ end
68
+ end
69
+
70
+ private
71
+
72
+ def comment_config
73
+ processed_source.comment_config
74
+ end
75
+
76
+ def plain_disable?(directive)
77
+ directive.disabled? && !directive.disable_next?
78
+ end
79
+
80
+ def disable_only_push?(directive)
81
+ directive.push? && !directive.push_args.empty? && directive.push_args.keys == ['-']
82
+ end
83
+
84
+ def check_pair(directive)
85
+ enable = single_statement_closing_directive(directive)
86
+ return unless enable&.enabled?
87
+ return unless enable.raw_cop_names.sort == directive.raw_cop_names.sort
88
+
89
+ message = format(MSG_PAIR, mode: directive.mode)
90
+ add_offense(directive.comment, message: message) do |corrector|
91
+ convert_pair(corrector, directive, enable)
92
+ end
93
+ end
94
+
95
+ def convert_pair(corrector, directive, enable)
96
+ replacement = directive.comment.text.sub(/\b#{directive.mode}\b/,
97
+ "#{directive.mode}-next")
98
+ corrector.replace(directive.comment, replacement)
99
+ remove_line(corrector, enable.comment)
100
+ end
101
+
102
+ def check_push_pop(directive)
103
+ pop = single_statement_closing_directive(directive)
104
+ return unless pop&.pop?
105
+
106
+ add_offense(directive.comment, message: MSG_PUSH_POP) do |corrector|
107
+ corrector.replace(directive.comment, disable_next_replacement(directive))
108
+ remove_line(corrector, pop.comment)
109
+ end
110
+ end
111
+
112
+ # The directive closing this one's scope, when that scope wraps
113
+ # exactly one statement - `nil` otherwise.
114
+ def single_statement_closing_directive(directive)
115
+ closing_line = single_closing_line(directive)
116
+ return nil unless closing_line
117
+
118
+ closing_comment = processed_source.comment_at_line(closing_line)
119
+ return nil unless closing_comment
120
+ return nil unless wraps_single_statement?(directive, closing_line)
121
+
122
+ DirectiveComment.new(closing_comment)
123
+ end
124
+
125
+ # The single line on which every disabled range opened by this
126
+ # directive ends, or `nil` when the ranges disagree or never close.
127
+ # For a `disable` that is the `enable` line; for a `push` it is the
128
+ # line before the `pop`.
129
+ def single_closing_line(directive)
130
+ ranges = ranges_opened_by(directive)
131
+ return nil if ranges.empty?
132
+
133
+ ends = ranges.map(&:end).uniq
134
+ return nil unless ends.size == 1 && ends.first.to_f.finite?
135
+
136
+ directive.push? ? ends.first + 1 : ends.first
137
+ end
138
+
139
+ def ranges_opened_by(directive)
140
+ comment_config.cop_disabled_line_ranges.each_value.flat_map do |ranges|
141
+ ranges.select do |range|
142
+ range.respond_to?(:directive) && range.directive.comment.equal?(directive.comment)
143
+ end
144
+ end
145
+ end
146
+
147
+ # The region wraps a single statement only when the directive sits
148
+ # immediately above it and the closing directive immediately below -
149
+ # any other line inside the region (a comment, a blank line) may
150
+ # carry offenses of the suppressed cops that the tighter scope
151
+ # would no longer cover.
152
+ def wraps_single_statement?(directive, closing_line)
153
+ scope = comment_config.statement_scope_after(directive.line_number)
154
+
155
+ !scope.nil? && scope.begin == directive.line_number + 1 && scope.end + 1 == closing_line
156
+ end
157
+
158
+ def disable_next_replacement(directive)
159
+ cops = directive.push_args['-'].join(', ')
160
+ reason = directive.reason
161
+ text = "# rubocop:disable-next #{cops}"
162
+ reason ? "#{text} -- #{reason}" : text
163
+ end
164
+
165
+ def remove_line(corrector, comment)
166
+ corrector.remove(range_by_whole_lines(comment.source_range, include_final_newline: true))
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
@@ -19,6 +19,12 @@ module RuboCop
19
19
  # allowlisting all other cops. `AllowedCops` and `DisallowedCops` should not
20
20
  # both be set at the same time; if `DisallowedCops` is set, it takes precedence.
21
21
  #
22
+ # With `AllowTrailingComment` set to `true`, a disable directive carrying
23
+ # a `--` trailing justification comment is allowed, so a team can require
24
+ # every disable to be documented instead of banning them outright. Enable
25
+ # directives are not checked in this mode, since they end a suppression
26
+ # rather than start one.
27
+ #
22
28
  # This cop cannot be disabled via directive comments when it is explicitly
23
29
  # enabled with `Enabled: true`. This prevents users from bypassing the cop
24
30
  # with `# rubocop:disable Style/DisableCopsWithinSourceCodeDirective`.
@@ -52,16 +58,28 @@ module RuboCop
52
58
  # foo
53
59
  # # rubocop:enable Metrics/AbcSize
54
60
  #
61
+ # @example AllowTrailingComment: true
62
+ # # bad
63
+ # x = 0 # rubocop:disable Layout/SpaceAroundOperators
64
+ #
65
+ # # good
66
+ # x = 0 # rubocop:disable Layout/SpaceAroundOperators -- would misalign the table
67
+ #
55
68
  class DisableCopsWithinSourceCodeDirective < Base
56
69
  extend AutoCorrector
57
70
 
58
71
  # rubocop:enable Lint/RedundantCopDisableDirective
59
72
  MSG = 'RuboCop disable/enable directives are not permitted.'
60
73
  MSG_FOR_COPS = 'RuboCop disable/enable directives for %<cops>s are not permitted.'
74
+ MSG_MISSING_REASON = 'RuboCop disable directives without a `--` justification ' \
75
+ 'comment are not permitted.'
61
76
 
62
77
  def on_new_investigation
63
78
  processed_source.comments.each do |comment|
64
- directive_cops = directive_cops(comment)
79
+ directive = DirectiveComment.new(comment)
80
+ next if allow_trailing_comment? && (directive.reason || directive.enabled?)
81
+
82
+ directive_cops = directive_cops(directive)
65
83
  disallowed_cops = compute_disallowed_cops(directive_cops)
66
84
 
67
85
  next unless disallowed_cops.any?
@@ -85,13 +103,7 @@ module RuboCop
85
103
  end
86
104
 
87
105
  def register_offense(comment, directive_cops, disallowed_cops)
88
- message = if any_cops_allowed? || disallowed_cops_config.any?
89
- format(MSG_FOR_COPS, cops: "`#{disallowed_cops.join('`, `')}`")
90
- else
91
- MSG
92
- end
93
-
94
- add_offense(comment, message: message) do |corrector|
106
+ add_offense(comment, message: offense_message(disallowed_cops)) do |corrector|
95
107
  replacement = ''
96
108
 
97
109
  if directive_cops.length != disallowed_cops.length
@@ -103,11 +115,25 @@ module RuboCop
103
115
  end
104
116
  end
105
117
 
106
- def directive_cops(comment)
107
- match_captures = DirectiveComment.new(comment).match_captures
118
+ def offense_message(disallowed_cops)
119
+ if allow_trailing_comment?
120
+ MSG_MISSING_REASON
121
+ elsif any_cops_allowed? || disallowed_cops_config.any?
122
+ format(MSG_FOR_COPS, cops: "`#{disallowed_cops.join('`, `')}`")
123
+ else
124
+ MSG
125
+ end
126
+ end
127
+
128
+ def directive_cops(directive)
129
+ match_captures = directive.match_captures
108
130
  match_captures && match_captures[1] ? match_captures[1].split(',').map(&:strip) : []
109
131
  end
110
132
 
133
+ def allow_trailing_comment?
134
+ cop_config['AllowTrailingComment']
135
+ end
136
+
111
137
  def allowed_cops
112
138
  @allowed_cops ||= Array(cop_config['AllowedCops']).to_set
113
139
  end
@@ -42,7 +42,7 @@ module RuboCop
42
42
  MSG = 'Do not use empty `case` condition, instead use an `if` expression.'
43
43
  NOT_SUPPORTED_PARENT_TYPES = %i[return break next send csend yield super].freeze
44
44
 
45
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
45
+ # rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
46
46
  def on_case(case_node)
47
47
  if case_node.condition || NOT_SUPPORTED_PARENT_TYPES.include?(case_node.parent&.type)
48
48
  return
@@ -56,7 +56,6 @@ module RuboCop
56
56
 
57
57
  add_offense(case_node.loc.keyword) { |corrector| autocorrect(corrector, case_node) }
58
58
  end
59
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
60
59
 
61
60
  private
62
61
 
@@ -228,7 +228,7 @@ module RuboCop
228
228
  "Prefer #{message_text(style)} over #{message_text(detected_style)}."
229
229
  end
230
230
 
231
- # rubocop:disable Style/FormatStringToken
231
+ # rubocop:disable-next Style/FormatStringToken
232
232
  def message_text(style)
233
233
  {
234
234
  annotated: 'annotated tokens (like `%<foo>s`)',
@@ -236,7 +236,6 @@ module RuboCop
236
236
  unannotated: 'unannotated tokens (like `%s`)'
237
237
  }[style]
238
238
  end
239
- # rubocop:enable Style/FormatStringToken
240
239
 
241
240
  def tokens(str_node, &block)
242
241
  return if str_node.source == '__FILE__'
@@ -196,7 +196,7 @@ module RuboCop
196
196
  end
197
197
  end
198
198
 
199
- # rubocop:disable Metrics/AbcSize
199
+ # rubocop:disable-next Metrics/AbcSize
200
200
  def autocorrect(corrector, node, condition, replacement, guard)
201
201
  corrector.replace(node.loc.keyword.join(condition.source_range), replacement)
202
202
 
@@ -217,7 +217,6 @@ module RuboCop
217
217
  corrector.remove(range_of_branch_to_remove(node, guard))
218
218
  end
219
219
  end
220
- # rubocop:enable Metrics/AbcSize
221
220
 
222
221
  def heredoc?(argument)
223
222
  argument.respond_to?(:heredoc?) && argument.heredoc?
@@ -76,7 +76,7 @@ module RuboCop
76
76
  alias on_numblock on_block
77
77
  alias on_itblock on_block
78
78
 
79
- # rubocop:disable Metrics/AbcSize
79
+ # rubocop:disable-next Metrics/AbcSize
80
80
  def check_unused_block_args(node, key, value)
81
81
  return if node.body.nil?
82
82
 
@@ -96,7 +96,6 @@ module RuboCop
96
96
  register_each_args_offense(node, message, 'each_value', unused_range)
97
97
  end
98
98
  end
99
- # rubocop:enable Metrics/AbcSize
100
99
 
101
100
  def on_block_pass(node)
102
101
  kv_each_with_block_pass(node.parent) do |target, method|
@@ -217,7 +217,7 @@ module RuboCop
217
217
  acceptable_19_syntax_symbol?(pair.key.source)
218
218
  end
219
219
 
220
- # rubocop:disable Metrics/CyclomaticComplexity
220
+ # rubocop:disable-next Metrics/CyclomaticComplexity
221
221
  def acceptable_19_syntax_symbol?(sym_name)
222
222
  sym_name.delete_prefix!(':')
223
223
 
@@ -237,7 +237,6 @@ module RuboCop
237
237
  (sym_name.start_with?("'") && sym_name.end_with?("'")) ||
238
238
  (sym_name.start_with?('"') && sym_name.end_with?('"'))
239
239
  end
240
- # rubocop:enable Metrics/CyclomaticComplexity
241
240
 
242
241
  def check(pairs, delim, msg)
243
242
  pairs.each do |pair|
@@ -136,7 +136,7 @@ module RuboCop
136
136
 
137
137
  private
138
138
 
139
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
139
+ # rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
140
140
  def check_branches(node, branches)
141
141
  # return if any branch is empty. An empty branch can be an `if`
142
142
  # without an `else` or a branch that contains only comments.
@@ -168,7 +168,6 @@ module RuboCop
168
168
 
169
169
  check_expressions(node, heads, :before_condition)
170
170
  end
171
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
172
171
 
173
172
  def duplicated_expressions?(node, expressions)
174
173
  unique_expressions = expressions.uniq
@@ -189,7 +188,7 @@ module RuboCop
189
188
  end
190
189
  end
191
190
 
192
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
191
+ # rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
193
192
  def check_expressions(node, expressions, insert_position)
194
193
  return if expressions.any?(&:nil?)
195
194
 
@@ -213,7 +212,6 @@ module RuboCop
213
212
  end
214
213
  end
215
214
  end
216
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
217
215
 
218
216
  def correct_assignment(corrector, node, expression, insert_position)
219
217
  indentation = indentation_of(node.parent)
@@ -64,7 +64,7 @@ module RuboCop
64
64
 
65
65
  MSG = 'Convert `if` nested inside `else` to `elsif`.'
66
66
 
67
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
67
+ # rubocop:disable-next Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
68
68
  def on_if(node)
69
69
  return if node.ternary? || node.unless?
70
70
 
@@ -81,7 +81,6 @@ module RuboCop
81
81
  ignore_node(node)
82
82
  end
83
83
  end
84
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
85
84
 
86
85
  private
87
86