rubocop 1.89.0 → 1.91.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 (248) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +309 -14
  3. data/config/obsoletion.yml +9 -0
  4. data/exe/rubocop +1 -1
  5. data/lib/rubocop/cached_data.rb +7 -2
  6. data/lib/rubocop/changed_files.rb +112 -0
  7. data/lib/rubocop/cli/command/execute_runner.rb +52 -1
  8. data/lib/rubocop/cli.rb +11 -7
  9. data/lib/rubocop/comment_config/directive_range.rb +34 -0
  10. data/lib/rubocop/comment_config/disable_next.rb +143 -0
  11. data/lib/rubocop/comment_config/push_pop.rb +55 -0
  12. data/lib/rubocop/comment_config.rb +50 -64
  13. data/lib/rubocop/config.rb +33 -5
  14. data/lib/rubocop/config_loader.rb +13 -6
  15. data/lib/rubocop/config_loader_resolver.rb +50 -9
  16. data/lib/rubocop/config_obsoletion/cop_rule.rb +2 -1
  17. data/lib/rubocop/config_obsoletion/removed_cop.rb +8 -0
  18. data/lib/rubocop/config_obsoletion.rb +1 -1
  19. data/lib/rubocop/config_validator.rb +19 -5
  20. data/lib/rubocop/cop/autocorrect_logic.rb +8 -0
  21. data/lib/rubocop/cop/badge.rb +1 -1
  22. data/lib/rubocop/cop/base.rb +49 -5
  23. data/lib/rubocop/cop/corrector.rb +3 -3
  24. data/lib/rubocop/cop/correctors.rb +2 -2
  25. data/lib/rubocop/cop/gemspec/require_mfa.rb +1 -1
  26. data/lib/rubocop/cop/generator.rb +1 -1
  27. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -2
  28. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb +1 -2
  29. data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
  30. data/lib/rubocop/cop/layout/argument_alignment.rb +2 -0
  31. data/lib/rubocop/cop/layout/block_alignment.rb +1 -2
  32. data/lib/rubocop/cop/layout/class_structure.rb +102 -26
  33. data/lib/rubocop/cop/layout/comment_indentation.rb +8 -5
  34. data/lib/rubocop/cop/layout/else_alignment.rb +1 -0
  35. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -2
  36. data/lib/rubocop/cop/layout/empty_line_after_magic_comment.rb +63 -5
  37. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +1 -1
  38. data/lib/rubocop/cop/layout/end_alignment.rb +1 -2
  39. data/lib/rubocop/cop/layout/extra_spacing.rb +105 -3
  40. data/lib/rubocop/cop/layout/first_argument_indentation.rb +21 -1
  41. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +17 -4
  42. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +11 -0
  43. data/lib/rubocop/cop/layout/hash_alignment.rb +6 -5
  44. data/lib/rubocop/cop/layout/indentation_width.rb +2 -2
  45. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -2
  46. data/lib/rubocop/cop/layout/line_length.rb +20 -3
  47. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +1 -2
  48. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +5 -5
  49. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +1 -2
  50. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +14 -3
  51. data/lib/rubocop/cop/layout/space_around_operators.rb +12 -1
  52. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +2 -4
  53. data/lib/rubocop/cop/lint/ambiguous_range.rb +1 -2
  54. data/lib/rubocop/cop/lint/argument_mismatch.rb +99 -0
  55. data/lib/rubocop/cop/lint/assignment_in_condition.rb +2 -0
  56. data/lib/rubocop/cop/lint/constant_reassignment.rb +17 -11
  57. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +165 -15
  58. data/lib/rubocop/cop/lint/deprecated_reference.rb +11 -2
  59. data/lib/rubocop/cop/lint/duplicate_methods.rb +113 -15
  60. data/lib/rubocop/cop/lint/empty_block.rb +1 -1
  61. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +1 -2
  62. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +1 -2
  63. data/lib/rubocop/cop/lint/interpolation_check.rb +1 -2
  64. data/lib/rubocop/cop/lint/literal_as_condition.rb +11 -8
  65. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +3 -6
  66. data/lib/rubocop/cop/lint/misplaced_magic_comment.rb +185 -0
  67. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +25 -12
  68. data/lib/rubocop/cop/lint/name_typo.rb +53 -6
  69. data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
  70. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +1 -1
  71. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +15 -0
  72. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +17 -1
  73. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +122 -29
  74. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +47 -7
  75. data/lib/rubocop/cop/lint/redundant_require_statement.rb +1 -2
  76. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +1 -2
  77. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +1 -2
  78. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +1 -2
  79. data/lib/rubocop/cop/lint/redundant_with_index.rb +1 -2
  80. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +1 -2
  81. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +2 -4
  82. data/lib/rubocop/cop/lint/super_argument_mismatch.rb +179 -0
  83. data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +1 -2
  84. data/lib/rubocop/cop/lint/to_enum_arguments.rb +1 -2
  85. data/lib/rubocop/cop/lint/unreachable_code.rb +1 -2
  86. data/lib/rubocop/cop/lint/unused_private_method.rb +94 -17
  87. data/lib/rubocop/cop/lint/useless_access_modifier.rb +1 -2
  88. data/lib/rubocop/cop/lint/useless_assignment.rb +1 -2
  89. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +36 -0
  90. data/lib/rubocop/cop/lint/useless_method_definition.rb +1 -1
  91. data/lib/rubocop/cop/lint/useless_rescue.rb +1 -2
  92. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +61 -7
  93. data/lib/rubocop/cop/lint/void.rb +1 -2
  94. data/lib/rubocop/cop/lint.rb +3 -0
  95. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -2
  96. data/lib/rubocop/cop/migration/department_name.rb +1 -2
  97. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  98. data/lib/rubocop/cop/mixin/annotation_comment.rb +1 -1
  99. data/lib/rubocop/cop/mixin/comments_help.rb +1 -2
  100. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +1 -2
  101. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +11 -1
  102. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +7 -4
  103. data/lib/rubocop/cop/mixin/hash_subset.rb +1 -2
  104. data/lib/rubocop/cop/mixin/hash_transform_method.rb +1 -1
  105. data/lib/rubocop/cop/mixin/indexed_method_arity.rb +94 -0
  106. data/lib/rubocop/cop/mixin/line_length_help.rb +7 -1
  107. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +62 -6
  108. data/lib/rubocop/cop/mixin/range_help.rb +1 -2
  109. data/lib/rubocop/cop/mixin/statement_modifier.rb +13 -7
  110. data/lib/rubocop/cop/mixin/trailing_comma.rb +2 -4
  111. data/lib/rubocop/cop/mixin.rb +6 -2
  112. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +16 -0
  113. data/lib/rubocop/cop/naming/block_forwarding.rb +4 -1
  114. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  115. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +2 -4
  116. data/lib/rubocop/cop/naming/method_name.rb +5 -2
  117. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -2
  118. data/lib/rubocop/cop/offense.rb +27 -4
  119. data/lib/rubocop/cop/registry.rb +43 -11
  120. data/lib/rubocop/cop/severity.rb +12 -0
  121. data/lib/rubocop/cop/style/access_modifier_declarations.rb +3 -2
  122. data/lib/rubocop/cop/style/accessor_grouping.rb +1 -2
  123. data/lib/rubocop/cop/style/arguments_forwarding.rb +5 -8
  124. data/lib/rubocop/cop/style/array_first_last.rb +1 -2
  125. data/lib/rubocop/cop/style/bitwise_predicate.rb +1 -2
  126. data/lib/rubocop/cop/style/block_delimiters.rb +3 -6
  127. data/lib/rubocop/cop/style/case_like_if.rb +5 -7
  128. data/lib/rubocop/cop/style/class_and_module_children.rb +1 -2
  129. data/lib/rubocop/cop/style/combinable_loops.rb +1 -2
  130. data/lib/rubocop/cop/style/concat_array_literals.rb +1 -2
  131. data/lib/rubocop/cop/style/conditional_assignment.rb +1 -2
  132. data/lib/rubocop/cop/style/constant_visibility.rb +6 -3
  133. data/lib/rubocop/cop/style/copyright.rb +1 -2
  134. data/lib/rubocop/cop/style/data_inheritance.rb +14 -0
  135. data/lib/rubocop/cop/style/directive_scope.rb +304 -0
  136. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +83 -13
  137. data/lib/rubocop/cop/style/documentation_method.rb +1 -9
  138. data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
  139. data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
  140. data/lib/rubocop/cop/style/empty_case_condition.rb +1 -2
  141. data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
  142. data/lib/rubocop/cop/style/empty_method.rb +12 -2
  143. data/lib/rubocop/cop/style/endless_method.rb +31 -12
  144. data/lib/rubocop/cop/style/file_null.rb +2 -2
  145. data/lib/rubocop/cop/style/float_division.rb +10 -16
  146. data/lib/rubocop/cop/style/for.rb +5 -0
  147. data/lib/rubocop/cop/style/format_string_token.rb +1 -2
  148. data/lib/rubocop/cop/style/guard_clause.rb +5 -2
  149. data/lib/rubocop/cop/style/hash_each_methods.rb +1 -2
  150. data/lib/rubocop/cop/style/hash_lookup_method.rb +3 -0
  151. data/lib/rubocop/cop/style/hash_syntax.rb +1 -2
  152. data/lib/rubocop/cop/style/identical_conditional_branches.rb +2 -4
  153. data/lib/rubocop/cop/style/if_inside_else.rb +1 -2
  154. data/lib/rubocop/cop/style/if_unless_modifier.rb +1 -2
  155. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +1 -2
  156. data/lib/rubocop/cop/style/magic_comment_format.rb +1 -1
  157. data/lib/rubocop/cop/style/map_to_hash.rb +1 -2
  158. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -4
  159. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +1 -1
  160. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +1 -2
  161. data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -2
  162. data/lib/rubocop/cop/style/negated_if_else_condition.rb +1 -2
  163. data/lib/rubocop/cop/style/negative_array_index.rb +2 -4
  164. data/lib/rubocop/cop/style/nested_modifier.rb +9 -4
  165. data/lib/rubocop/cop/style/nil_comparison.rb +36 -9
  166. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  167. data/lib/rubocop/cop/style/non_nil_check.rb +18 -2
  168. data/lib/rubocop/cop/style/not.rb +3 -1
  169. data/lib/rubocop/cop/style/numeric_literal_prefix.rb +11 -1
  170. data/lib/rubocop/cop/style/one_line_conditional.rb +1 -1
  171. data/lib/rubocop/cop/style/operator_method_call.rb +1 -2
  172. data/lib/rubocop/cop/style/parallel_assignment.rb +1 -2
  173. data/lib/rubocop/cop/style/parentheses_around_condition.rb +6 -1
  174. data/lib/rubocop/cop/style/redundant_assignment.rb +1 -2
  175. data/lib/rubocop/cop/style/redundant_condition.rb +6 -8
  176. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +11 -3
  177. data/lib/rubocop/cop/style/redundant_each.rb +1 -2
  178. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -1
  179. data/lib/rubocop/cop/style/redundant_format.rb +2 -4
  180. data/lib/rubocop/cop/style/redundant_parentheses.rb +13 -5
  181. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +1 -2
  182. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +6 -5
  183. data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +6 -6
  184. data/lib/rubocop/cop/style/redundant_return.rb +1 -2
  185. data/lib/rubocop/cop/style/redundant_self_assignment.rb +1 -2
  186. data/lib/rubocop/cop/style/redundant_string_escape.rb +1 -2
  187. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -2
  188. data/lib/rubocop/cop/style/safe_navigation.rb +1 -2
  189. data/lib/rubocop/cop/style/sample.rb +11 -6
  190. data/lib/rubocop/cop/style/select_by_regexp.rb +1 -2
  191. data/lib/rubocop/cop/style/semicolon.rb +2 -4
  192. data/lib/rubocop/cop/style/single_line_block_params.rb +1 -1
  193. data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -2
  194. data/lib/rubocop/cop/style/sole_nested_conditional.rb +1 -2
  195. data/lib/rubocop/cop/style/special_global_vars.rb +1 -1
  196. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +2 -0
  197. data/lib/rubocop/cop/style/string_concatenation.rb +18 -5
  198. data/lib/rubocop/cop/style/struct_inheritance.rb +14 -0
  199. data/lib/rubocop/cop/style/super_arguments.rb +1 -2
  200. data/lib/rubocop/cop/style/symbol_proc.rb +1 -2
  201. data/lib/rubocop/cop/style/time_now.rb +41 -0
  202. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  203. data/lib/rubocop/cop/style/yoda_condition.rb +1 -2
  204. data/lib/rubocop/cop/style.rb +2 -1
  205. data/lib/rubocop/cop/team.rb +11 -3
  206. data/lib/rubocop/cop/util.rb +3 -5
  207. data/lib/rubocop/cop/variable_force/variable.rb +1 -2
  208. data/lib/rubocop/cops_documentation_generator.rb +26 -8
  209. data/lib/rubocop/directive_comment.rb +73 -10
  210. data/lib/rubocop/formatter/disabled_config_formatter.rb +19 -1
  211. data/lib/rubocop/formatter/emacs_style_formatter.rb +3 -1
  212. data/lib/rubocop/formatter/formatter_set.rb +2 -1
  213. data/lib/rubocop/formatter/github_actions_formatter.rb +6 -5
  214. data/lib/rubocop/formatter/html_formatter.rb +2 -4
  215. data/lib/rubocop/formatter/json_formatter.rb +11 -6
  216. data/lib/rubocop/formatter/junit_formatter.rb +26 -9
  217. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -2
  218. data/lib/rubocop/formatter/sarif_formatter.rb +181 -0
  219. data/lib/rubocop/formatter/simple_text_formatter.rb +4 -3
  220. data/lib/rubocop/formatter/tap_formatter.rb +3 -1
  221. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -2
  222. data/lib/rubocop/formatter.rb +1 -0
  223. data/lib/rubocop/lockfile.rb +18 -0
  224. data/lib/rubocop/lsp/diagnostic.rb +4 -8
  225. data/lib/rubocop/lsp/routes.rb +48 -19
  226. data/lib/rubocop/magic_comment.rb +27 -0
  227. data/lib/rubocop/mcp/server.rb +1 -2
  228. data/lib/rubocop/options.rb +69 -7
  229. data/lib/rubocop/path_util.rb +2 -3
  230. data/lib/rubocop/plugin/configuration_integrator.rb +6 -3
  231. data/lib/rubocop/plugin/loader.rb +1 -1
  232. data/lib/rubocop/rake_task.rb +1 -2
  233. data/lib/rubocop/remote_config.rb +4 -4
  234. data/lib/rubocop/result_cache.rb +16 -5
  235. data/lib/rubocop/rspec/expect_offense.rb +2 -4
  236. data/lib/rubocop/rspec/shared_contexts.rb +4 -2
  237. data/lib/rubocop/runner.rb +90 -44
  238. data/lib/rubocop/server/cache.rb +1 -2
  239. data/lib/rubocop/server/cli.rb +2 -4
  240. data/lib/rubocop/server.rb +14 -0
  241. data/lib/rubocop/target_finder.rb +4 -1
  242. data/lib/rubocop/todo_audit.rb +111 -0
  243. data/lib/rubocop/unified_diff.rb +257 -0
  244. data/lib/rubocop/version.rb +3 -5
  245. data/lib/rubocop.rb +7 -1
  246. data/lib/ruby_lsp/rubocop/addon.rb +1 -2
  247. metadata +18 -6
  248. data/lib/rubocop/cop/style/double_cop_disable_directive.rb +0 -46
@@ -175,10 +175,19 @@ module RuboCop
175
175
  end
176
176
 
177
177
  def ancestor_namespaces(node)
178
- node
179
- .each_ancestor(:class, :module)
180
- .map { |ancestor| ancestor.identifier.short_name }
181
- .reverse
178
+ ancestors = node.each_ancestor(:class, :module).reverse_each
179
+
180
+ ancestors.with_object([]) do |ancestor, namespaces|
181
+ append_namespaces(namespaces, ancestor.identifier)
182
+ end
183
+ end
184
+
185
+ # Compact definitions (e.g. `module A::B`) contribute every path segment,
186
+ # and an absolute one (e.g. `module ::A::B`) discards the enclosing namespaces.
187
+ def append_namespaces(namespaces, identifier)
188
+ namespaces.clear if identifier.absolute?
189
+ namespaces.concat(identifier_namespaces(identifier))
190
+ namespaces << identifier.short_name
182
191
  end
183
192
 
184
193
  def unconditional_definition?(node)
@@ -188,14 +197,11 @@ module RuboCop
188
197
  end
189
198
 
190
199
  def definition_name(node)
191
- identifier = node.identifier
200
+ namespaces = ancestor_namespaces(node)
201
+ append_namespaces(namespaces, node.identifier)
202
+ constant = namespaces.pop
192
203
 
193
- if identifier.namespace&.cbase_type?
194
- fully_qualified_name_for([], identifier.short_name)
195
- else
196
- namespaces = ancestor_namespaces(node) + identifier_namespaces(identifier)
197
- fully_qualified_name_for(namespaces, identifier.short_name)
198
- end
204
+ fully_qualified_name_for(namespaces, constant)
199
205
  end
200
206
 
201
207
  def identifier_namespaces(identifier)
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # rubocop:disable Lint/RedundantCopDisableDirective
4
- # rubocop:disable Style/DoubleCopDisableDirective
5
3
  module RuboCop
6
4
  module Cop
7
5
  module Lint
@@ -23,12 +21,11 @@ module RuboCop
23
21
  # # good
24
22
  # # rubocop:disable all
25
23
  #
26
- # # bad
24
+ # # bad - only the first directive takes effect
27
25
  # # rubocop:disable Layout/LineLength # rubocop:disable Style/Encoding
28
26
  #
29
27
  # # good
30
- # # rubocop:disable Layout/LineLength
31
- # # rubocop:disable Style/Encoding
28
+ # # rubocop:disable Layout/LineLength, Style/Encoding
32
29
  #
33
30
  # # bad
34
31
  # # rubocop:wrongmode Layout/LineLength
@@ -42,29 +39,139 @@ module RuboCop
42
39
  # # good
43
40
  # # rubocop:disable Layout/LineLength -- comment
44
41
  #
42
+ # # bad
43
+ # # rucocop:disable Layout/LineLength
44
+ #
45
+ # # good
46
+ # # rubocop:disable Layout/LineLength
47
+ #
48
+ # # bad
49
+ # # rubocop:disable Layout/LineLenght
50
+ #
51
+ # # good
52
+ # # rubocop:disable Layout/LineLength
53
+ #
45
54
  class CopDirectiveSyntax < Base
55
+ extend AutoCorrector
56
+
46
57
  COMMON_MSG = 'Malformed directive comment detected.'
47
58
 
48
59
  MISSING_MODE_NAME_MSG = 'The mode name is missing.'
49
- INVALID_MODE_NAME_MSG = 'The mode name must be one of `enable`, `disable`, `todo`, `push`, or `pop`.' # rubocop:disable Layout/LineLength
60
+ INVALID_MODE_NAME_MSG = 'The mode name must be one of `enable`, `disable`, `disable-next`, `enable-next`, `todo`, `todo-next`, `next`, `push`, or `pop`.' # rubocop:disable Layout/LineLength -- the message lists every mode and does not wrap
50
61
  MISSING_COP_NAME_MSG = 'The cop name is missing.'
62
+ MULTIPLE_DIRECTIVES_MSG = 'Only the first directive on a line takes effect. ' \
63
+ 'List the cop names in a single directive instead.'
51
64
  MALFORMED_COP_NAMES_MSG = 'Cop names must be separated by commas. ' \
52
65
  'Comment in the directive must start with `--`.'
66
+ INVALID_SIGNED_ARGS_MSG = '`push` and `next` arguments must be `+`- or `-`-prefixed ' \
67
+ 'cop names, and `pop` takes no arguments.'
68
+ NEXT_DIRECTIVE_AT_EOL_MSG = 'A `-next` directive must be on its own line, above the ' \
69
+ 'statement it applies to.'
70
+ INVALID_KEYWORD_MSG = 'The directive keyword must be `rubocop`, not `%<keyword>s`.'
71
+ UNKNOWN_COP_MSG = 'Unknown cop name `%<name>s`%<suggestion>s.'
72
+
73
+ # A comment that looks like an attempted directive: any keyword
74
+ # followed by a colon and a valid mode name.
75
+ NEAR_MISS_KEYWORD_REGEXP = /
76
+ \A\#+\s*(?<keyword>[A-Za-z][\w-]*)\s*:\s*
77
+ (?:#{DirectiveComment::MODES_PATTERN})\b
78
+ /x.freeze
53
79
 
54
80
  def on_new_investigation
55
81
  processed_source.comments.each do |comment|
56
82
  directive_comment = DirectiveComment.new(comment)
57
- next unless directive_comment.start_with_marker?
58
- next unless directive_comment.malformed?
59
-
60
- message = offense_message(directive_comment)
61
- add_offense(comment, message: message)
83
+ if directive_comment.start_with_marker?
84
+ check_directive(comment, directive_comment)
85
+ elsif (keyword = near_miss_keyword(comment))
86
+ message = format("#{COMMON_MSG} #{INVALID_KEYWORD_MSG}", keyword: keyword)
87
+ add_offense(comment, message: message)
88
+ end
62
89
  end
63
90
  end
64
91
 
65
92
  private
66
93
 
67
- # rubocop:disable Metrics/MethodLength
94
+ def check_directive(comment, directive_comment)
95
+ if (directives = multiple_directives(comment, directive_comment))
96
+ add_offense(comment, message: "#{COMMON_MSG} #{MULTIPLE_DIRECTIVES_MSG}") do |corrector|
97
+ merge_directives(corrector, comment, directives)
98
+ end
99
+ elsif directive_comment.malformed?
100
+ add_offense(comment, message: offense_message(directive_comment))
101
+ elsif misplaced_next_directive?(directive_comment)
102
+ add_offense(comment, message: NEXT_DIRECTIVE_AT_EOL_MSG)
103
+ elsif (name = unknown_cop_name(directive_comment))
104
+ add_offense(comment, message: unknown_cop_message(directive_comment, name))
105
+ end
106
+ end
107
+
108
+ # Everything after the first directive in a comment is trailing text as
109
+ # far as the parser is concerned, so a second directive written on the
110
+ # same line disables nothing at all. A valid `--` reason may mention a
111
+ # directive as plain text, which should not be treated as another one.
112
+ def multiple_directives(comment, directive_comment)
113
+ matches = comment.text.to_enum(:scan, DirectiveComment::DIRECTIVE_COMMENT_REGEXP)
114
+ .map { Regexp.last_match }
115
+ matches = filter_reason_matches(matches, directive_comment)
116
+
117
+ matches if matches.size > 1
118
+ end
119
+
120
+ def filter_reason_matches(matches, directive_comment)
121
+ return matches unless directive_comment.reason
122
+
123
+ [matches.first, *matches.drop(1).reject { |match| match.post_match.match?(/\S/) }]
124
+ end
125
+
126
+ def merge_directives(corrector, comment, directives)
127
+ return unless directives_adjacent?(comment, directives)
128
+ return unless (mode = merged_mode(directives))
129
+ return unless (cops = merged_cops(directives))
130
+
131
+ corrector.replace(comment, "# rubocop:#{mode} #{cops.join(', ')}")
132
+ end
133
+
134
+ # `all` covers every cop already, so folding it into a list of names
135
+ # would be a rewrite rather than a merge.
136
+ def merged_cops(directives)
137
+ cops = directives.flat_map { |match| match[2].to_s.split(',').map(&:strip) }
138
+ cops = cops.reject(&:empty?).uniq
139
+ cops unless cops.empty? || cops.include?('all')
140
+ end
141
+
142
+ # Anything written between the directives is a `--` reason or prose,
143
+ # and merging around it would either move it or drop it.
144
+ def directives_adjacent?(comment, directives)
145
+ text = comment.text
146
+ bounds = directives.map { |match| match.end(0) }
147
+ .zip(directives.map { |match| match.begin(0) }[1..] + [text.length])
148
+ bounds.all? { |from, to| text[from...to].strip.empty? }
149
+ end
150
+
151
+ # `todo` is an alias of `disable`, so those two combine. Anything else
152
+ # (an `enable` beside a `disable`, or directives of different scopes)
153
+ # means different things per directive and can't be folded into one.
154
+ def merged_mode(directives)
155
+ modes = directives.map { |match| match[1] }.uniq
156
+ return modes.first if modes.size == 1
157
+ return modes.first if (modes - %w[disable todo]).empty?
158
+
159
+ nil
160
+ end
161
+
162
+ # An EOL next-statement directive is not honored - it must fail
163
+ # loudly instead of silently doing nothing.
164
+ def misplaced_next_directive?(directive_comment)
165
+ next_statement_directive?(directive_comment) &&
166
+ !processed_source.comment_config.comment_only_line?(directive_comment.line_number)
167
+ end
168
+
169
+ def next_statement_directive?(directive_comment)
170
+ directive_comment.disable_next? || directive_comment.enable_next? ||
171
+ directive_comment.next?
172
+ end
173
+
174
+ # rubocop:disable-next Metrics/MethodLength
68
175
  def offense_message(directive_comment)
69
176
  comment = directive_comment.comment
70
177
  after_marker = comment.text.sub(DirectiveComment::DIRECTIVE_MARKER_REGEXP, '')
@@ -75,16 +182,59 @@ module RuboCop
75
182
  INVALID_MODE_NAME_MSG
76
183
  elsif directive_comment.missing_cop_name?
77
184
  MISSING_COP_NAME_MSG
185
+ elsif directive_comment.invalid_signed_args?
186
+ INVALID_SIGNED_ARGS_MSG
78
187
  else
79
188
  MALFORMED_COP_NAMES_MSG
80
189
  end
81
190
 
82
191
  "#{COMMON_MSG} #{additional_msg}"
83
192
  end
84
- # rubocop:enable Metrics/MethodLength
193
+
194
+ def near_miss_keyword(comment)
195
+ match = comment.text.match(NEAR_MISS_KEYWORD_REGEXP)
196
+ return unless match
197
+
198
+ keyword = match[:keyword]
199
+ return if keyword == 'rubocop'
200
+
201
+ keyword if similar_to_rubocop?(keyword)
202
+ end
203
+
204
+ def similar_to_rubocop?(keyword)
205
+ return true if keyword.casecmp?('rubocop')
206
+ # `DidYouMean` is not always available - see `NameSimilarity`.
207
+ return false unless defined?(DidYouMean::Levenshtein)
208
+
209
+ DidYouMean::Levenshtein.distance(keyword.downcase, 'rubocop') <= 2
210
+ end
211
+
212
+ def unknown_cop_name(directive_comment)
213
+ return if directive_comment.pop? || directive_comment.all_cops?
214
+
215
+ registry = directive_comment.cop_registry
216
+ directive_names(directive_comment).find do |name|
217
+ next false if registry.department?(name)
218
+
219
+ qualified = registry.qualified_cop_name(name, nil, warn: false)
220
+ !registry.contains_cop_matching?([qualified])
221
+ end
222
+ end
223
+
224
+ def directive_names(directive_comment)
225
+ if directive_comment.push? || directive_comment.next?
226
+ directive_comment.signed_args.values.flatten
227
+ else
228
+ directive_comment.raw_cop_names
229
+ end
230
+ end
231
+
232
+ def unknown_cop_message(directive_comment, name)
233
+ similar = NameSimilarity.find_similar_name(name, directive_comment.cop_registry.names)
234
+ suggestion = similar ? " (did you mean `#{similar}`?)" : ''
235
+ format(UNKNOWN_COP_MSG, name: name, suggestion: suggestion)
236
+ end
85
237
  end
86
238
  end
87
239
  end
88
240
  end
89
- # rubocop:enable Lint/RedundantCopDisableDirective
90
- # rubocop:enable Style/DoubleCopDisableDirective
@@ -150,10 +150,19 @@ module RuboCop
150
150
  false
151
151
  end
152
152
 
153
+ # Enumerated in a single pass that stops at the first non-deprecated
154
+ # definition, since materializing every definition of a declaration
155
+ # that is almost never deprecated dominates the cop's cost.
153
156
  def deprecated?(declaration)
154
- definitions = declaration.definitions.to_a
157
+ any = false
155
158
 
156
- definitions.any? && definitions.all?(&:deprecated?)
159
+ declaration.definitions.each do |definition|
160
+ return false unless definition.deprecated?
161
+
162
+ any = true
163
+ end
164
+
165
+ any
157
166
  end
158
167
 
159
168
  # References from a definition that is itself documented as deprecated
@@ -19,7 +19,22 @@ module RuboCop
19
19
  # file is wrapped in a conditional, so a platform-specific redefinition in
20
20
  # another file may still be reported. Aliasing the method to itself (see above)
21
21
  # before redefining marks the redefinition as intentional and is respected
22
- # across files.
22
+ # across files. With `AllCops/ActiveSupportExtensionsEnabled: true`, Active
23
+ # Support's redefinition markers (`silence_redefinition_of_method` and
24
+ # `redefine_method`) are honored the same way.
25
+ #
26
+ # NOTE: Methods defined with `define_method` are not recorded in the project
27
+ # index, so a duplicate whose other definition uses `define_method` cannot
28
+ # be detected across files.
29
+ #
30
+ # Cross-file duplicates whose other definition lives in a file matching one
31
+ # of the `AllowedCrossFilePaths` patterns are not reported. This suits files
32
+ # that redefine application methods but are never loaded together with them,
33
+ # such as standalone scripts. Patterns are matched with the same glob (or
34
+ # regexp) semantics as `Exclude`, against the other file's path relative to
35
+ # the directory of the `.rubocop.yml` that configures the cop; absolute
36
+ # patterns are matched against the absolute path. Offenses inside such files
37
+ # themselves are best silenced with an ordinary per-cop `Exclude`.
23
38
  #
24
39
  # @example
25
40
  #
@@ -129,6 +144,24 @@ module RuboCop
129
144
  # delegate :foo, to: :bar
130
145
  # end
131
146
  #
147
+ # # good - Active Support's redefinition markers signal an intentional
148
+ # # redefinition of a method defined in another file
149
+ # silence_redefinition_of_method :foo
150
+ # def foo
151
+ # 1
152
+ # end
153
+ #
154
+ # @example AllowedCrossFilePaths: ['script/**/*'] (default: [])
155
+ # # Cross-file duplicates whose other definition lives in a file
156
+ # # matching one of the patterns are not reported.
157
+ #
158
+ # # good - assuming `AppHelper#format` is also defined in
159
+ # # `script/backfill.rb`, which is never loaded with this file
160
+ # class AppHelper
161
+ # def format
162
+ # end
163
+ # end
164
+ #
132
165
  # @example DelegatingMethods: ['delegate', 'expose'] (default: ['delegate'])
133
166
  # # A project's own `delegate`-shaped macros can be registered so the
134
167
  # # methods they define are recognized (with
@@ -160,13 +193,14 @@ module RuboCop
160
193
  super
161
194
  @definitions = {}
162
195
  @scopes = Hash.new { |hash, key| hash[key] = [] }
163
- @self_aliased = Set.new
196
+ @intentionally_redefined = Set.new
164
197
  end
165
198
 
166
199
  def on_new_investigation
167
- # The self-alias trick declares an intentional redefinition only within
168
- # the file that uses it, so the tracked names do not carry over.
169
- @self_aliased = Set.new
200
+ # The self-alias trick and Active Support's redefinition markers declare
201
+ # an intentional redefinition only within the file that uses them, so the
202
+ # tracked names do not carry over.
203
+ @intentionally_redefined = Set.new
170
204
  super
171
205
  end
172
206
 
@@ -199,7 +233,7 @@ module RuboCop
199
233
  return unless name && original_name
200
234
 
201
235
  if name == original_name
202
- track_self_alias(node, name)
236
+ track_intentional_redefinition(node, name)
203
237
  return
204
238
  end
205
239
  return if node.ancestors.any?(&:if_type?)
@@ -241,6 +275,15 @@ module RuboCop
241
275
  )
242
276
  PATTERN
243
277
 
278
+ # Matches Active Support's markers of an intentional method redefinition:
279
+ # `silence_redefinition_of_method :name` suppresses Ruby's redefinition
280
+ # warning for a subsequent definition of `name`, and
281
+ # `redefine_method(:name) { ... }` both silences the warning and redefines.
282
+ # @!method active_support_redefinition_marker(node)
283
+ def_node_matcher :active_support_redefinition_marker, <<~PATTERN
284
+ (send nil? {:silence_redefinition_of_method :redefine_method} ({sym str} $_) ...)
285
+ PATTERN
286
+
244
287
  # @!method sym_name(node)
245
288
  def_node_matcher :sym_name, '(sym $_name)'
246
289
 
@@ -263,7 +306,7 @@ module RuboCop
263
306
 
264
307
  if name && original_name
265
308
  if name == original_name
266
- track_self_alias(node, name)
309
+ track_intentional_redefinition(node, name)
267
310
  return
268
311
  end
269
312
  return if inside_condition?(node)
@@ -283,6 +326,12 @@ module RuboCop
283
326
  return if inside_condition?(node)
284
327
 
285
328
  names.each { |name| found_instance_method(node, name) }
329
+ elsif (name = active_support_redefinition_marker(node))
330
+ # `redefine_method` takes a block; scope resolution must start from the
331
+ # block node, since `parent_module_name` cannot see through a block
332
+ # ancestor that is not a module constructor.
333
+ track_intentional_redefinition(node.block_node || node, name) if
334
+ active_support_extensions_enabled?
286
335
  end
287
336
  end
288
337
 
@@ -386,7 +435,7 @@ module RuboCop
386
435
 
387
436
  def anon_block_scope_id(anon_block)
388
437
  parent = anon_block.parent
389
- return unless parent&.type?(:any_block, :begin, :call, :casgn, :any_def)
438
+ return unless parent&.type?(:any_block, :begin, :call, :casgn, :ivasgn, :any_def)
390
439
 
391
440
  if (receiver = scope_receiver(parent, anon_block))
392
441
  "#{receiver.source}.#{parent.method_name}"
@@ -441,27 +490,39 @@ module RuboCop
441
490
  if scope && !@scopes[scope].include?(key)
442
491
  @definitions[key] = node
443
492
  @scopes[scope] << key
444
- elsif intentional_cross_file_redefinition?(node, method_name, key)
493
+ elsif intentional_cross_file_redefinition?(node, method_name, key) ||
494
+ allowed_cross_file_redefinition?(node, key)
445
495
  @definitions[key] = node
446
496
  else
447
497
  add_offense(location(node), message: message_for_dup(node, method_name, key))
448
498
  end
449
499
  end
450
500
 
501
+ # `AllowedCrossFilePaths` also covers duplicates detected without the index,
502
+ # when the cop instance carries definitions over from an earlier file of the
503
+ # same run.
504
+ def allowed_cross_file_redefinition?(node, key)
505
+ other_path = @definitions[key].source_range.source_buffer.name
506
+ return false if other_path == node.source_range.source_buffer.name
507
+
508
+ allowed_cross_file_path?(other_path)
509
+ end
510
+
451
511
  # The self-alias trick (`alias foo foo` or `alias_method :foo, :foo` right before
452
512
  # a `def`) suppresses Ruby's method redefinition warning, signaling an intentional
453
- # redefinition of a method defined in another file.
513
+ # redefinition of a method defined in another file. Active Support's redefinition
514
+ # markers (tracked when `ActiveSupportExtensionsEnabled` is on) signal the same.
454
515
  def intentional_cross_file_redefinition?(node, method_name, key)
455
- @self_aliased.include?(method_name) &&
516
+ @intentionally_redefined.include?(method_name) &&
456
517
  @definitions[key].source_range.source_buffer.name !=
457
518
  node.source_range.source_buffer.name
458
519
  end
459
520
 
460
- def track_self_alias(node, name)
521
+ def track_intentional_redefinition(node, name)
461
522
  scope = node.parent_module_name
462
523
  return unless scope
463
524
 
464
- @self_aliased << "#{humanize_scope(scope)}#{name}"
525
+ @intentionally_redefined << "#{humanize_scope(scope)}#{name}"
465
526
  end
466
527
 
467
528
  def method_key(node, method_name)
@@ -544,7 +605,7 @@ module RuboCop
544
605
 
545
606
  def check_cross_file_duplicate(node, method_name)
546
607
  return unless project_index
547
- return if @self_aliased.include?(method_name)
608
+ return if @intentionally_redefined.include?(method_name)
548
609
  return if node.each_ancestor(:any_def).any?
549
610
  return unless (prior = cross_file_prior_definition(method_name))
550
611
 
@@ -559,12 +620,27 @@ module RuboCop
559
620
 
560
621
  definitions = definitions_in_other_files(
561
622
  indexed_definitions(match[:owner], match[:separator], match[:name])
562
- )
623
+ ).reject { |definition| allowed_cross_file_path?(definition.location.to_file_path) }
563
624
  return if definitions.empty? || cross_file_self_alias_trick?(definitions)
625
+ return if cross_file_redefinition_marker?(definitions, match[:name])
564
626
 
565
627
  first_indexed_definition(definitions)
566
628
  end
567
629
 
630
+ # Cross-file duplicates whose other definition site matches one of the
631
+ # `AllowedCrossFilePaths` patterns are skipped (e.g. standalone scripts that
632
+ # are never loaded together with the code they redefine). Patterns are
633
+ # matched like `Exclude` patterns, against the path relative to the
634
+ # directory of the config file that configures the cop; absolute patterns
635
+ # are matched against the absolute path.
636
+ def allowed_cross_file_path?(path)
637
+ patterns = cop_config.fetch('AllowedCrossFilePaths', [])
638
+ return false if patterns.empty?
639
+
640
+ relative = config.path_relative_to_config(path)
641
+ patterns.any? { |pattern| match_path?(pattern, relative) || match_path?(pattern, path) }
642
+ end
643
+
568
644
  def indexed_definitions(owner, separator, name)
569
645
  namespace = separator == '.' ? "#{owner}::<#{owner.split('::').last}>" : owner
570
646
 
@@ -601,6 +677,28 @@ module RuboCop
601
677
  others.any? { |definition| alias_paths.include?(definition.location.to_file_path) }
602
678
  end
603
679
 
680
+ # An Active Support redefinition marker alongside one of the definitions in
681
+ # another file marks the redefinition there as intentional, like the
682
+ # self-alias trick. The index does not record the marker's method-name
683
+ # argument, so the other file's source is searched for a marker naming
684
+ # the method.
685
+ def cross_file_redefinition_marker?(definitions, name)
686
+ return false unless active_support_extensions_enabled?
687
+
688
+ definitions.map { |definition| definition.location.to_file_path }.uniq.any? do |path|
689
+ redefinition_marker_in_file?(path, name)
690
+ end
691
+ end
692
+
693
+ def redefinition_marker_in_file?(path, name)
694
+ return false unless File.readable?(path)
695
+
696
+ escaped = Regexp.escape(name)
697
+ marker = /\b(?:silence_redefinition_of_method|redefine_method)\s*\(?\s*
698
+ (?::#{escaped}|(["'])#{escaped}\1)(?![\w=!?])/x
699
+ File.read(path).scrub.match?(marker)
700
+ end
701
+
604
702
  def first_indexed_definition(definitions)
605
703
  definitions.find { |definition| !definition.is_a?(Rubydex::MethodAliasDefinition) }
606
704
  end
@@ -63,7 +63,7 @@ module RuboCop
63
63
  class EmptyBlock < Base
64
64
  MSG = 'Empty block detected.'
65
65
 
66
- def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
66
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler -- an empty block has no parameters to number
67
67
  return if node.body
68
68
  return if allow_empty_lambdas? && node.lambda_or_proc?
69
69
  return if cop_config['AllowComments'] && allow_comment?(node)
@@ -28,7 +28,7 @@ module RuboCop
28
28
  FOR_METHOD = ' Or, if they were intended to be separate method ' \
29
29
  'arguments, separate them with a comma.'
30
30
 
31
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
31
+ # rubocop:disable-next Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
32
32
  def on_dstr(node)
33
33
  each_bad_cons(node) do |lhs_node, rhs_node|
34
34
  range = lhs_node.source_range.join(rhs_node.source_range)
@@ -52,7 +52,6 @@ module RuboCop
52
52
  end
53
53
  end
54
54
  end
55
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
56
55
 
57
56
  private
58
57
 
@@ -80,7 +80,7 @@ module RuboCop
80
80
  alternative: alternative)
81
81
  end
82
82
 
83
- # rubocop:disable Metrics/CyclomaticComplexity
83
+ # rubocop:disable-next Metrics/CyclomaticComplexity
84
84
  def ineffective_modifier(node, ignored_methods = nil, modifier = nil, &block)
85
85
  node.each_child_node do |child|
86
86
  case child.type
@@ -97,7 +97,6 @@ module RuboCop
97
97
  end
98
98
  end
99
99
  end
100
- # rubocop:enable Metrics/CyclomaticComplexity
101
100
 
102
101
  def access_modifier?(node)
103
102
  node.bare_access_modifier? && !node.method?(:module_function)
@@ -41,7 +41,7 @@ module RuboCop
41
41
 
42
42
  private
43
43
 
44
- # rubocop:disable Metrics/CyclomaticComplexity
44
+ # rubocop:disable-next Metrics/CyclomaticComplexity
45
45
  def check(node)
46
46
  return if node.parent&.regexp_type?
47
47
  return unless /(?<!\\)#\{.*\}/.match?(node.source)
@@ -51,7 +51,6 @@ module RuboCop
51
51
 
52
52
  add_offense(node) { |corrector| autocorrect(corrector, node) }
53
53
  end
54
- # rubocop:enable Metrics/CyclomaticComplexity
55
54
 
56
55
  def autocorrect(corrector, node)
57
56
  starting_token, ending_token = if node.source.include?('"')
@@ -48,7 +48,7 @@ module RuboCop
48
48
  add_offense(node.lhs) do |corrector|
49
49
  # Don't autocorrect `'foo' && return` because having `return` as
50
50
  # the leftmost node can lead to a void value expression syntax error.
51
- next if node.rhs.type?(:return, :break, :next)
51
+ next if void_value_expression?(node.rhs)
52
52
 
53
53
  corrector.replace(node, node.rhs.source)
54
54
  end
@@ -60,7 +60,7 @@ module RuboCop
60
60
  add_offense(node.lhs) do |corrector|
61
61
  # Don't autocorrect `'foo' && return` because having `return` as
62
62
  # the leftmost node can lead to a void value expression syntax error.
63
- next if node.rhs.type?(:return, :break, :next)
63
+ next if void_value_expression?(node.rhs)
64
64
 
65
65
  corrector.replace(node, node.rhs.source)
66
66
  end
@@ -88,7 +88,7 @@ module RuboCop
88
88
  end
89
89
  end
90
90
 
91
- # rubocop:disable Metrics/AbcSize
91
+ # rubocop:disable-next Metrics/AbcSize
92
92
  def on_while_post(node)
93
93
  return if node.condition.source == 'true'
94
94
 
@@ -102,7 +102,6 @@ module RuboCop
102
102
  end
103
103
  end
104
104
  end
105
- # rubocop:enable Metrics/AbcSize
106
105
 
107
106
  def on_until(node)
108
107
  return if node.condition.source == 'false'
@@ -118,7 +117,7 @@ module RuboCop
118
117
  end
119
118
  end
120
119
 
121
- # rubocop:disable Metrics/AbcSize
120
+ # rubocop:disable-next Metrics/AbcSize
122
121
  def on_until_post(node)
123
122
  return if node.condition.source == 'false'
124
123
 
@@ -132,7 +131,6 @@ module RuboCop
132
131
  end
133
132
  end
134
133
  end
135
- # rubocop:enable Metrics/AbcSize
136
134
 
137
135
  def on_case(case_node)
138
136
  if (cond = case_node.condition)
@@ -177,6 +175,12 @@ module RuboCop
177
175
 
178
176
  private
179
177
 
178
+ def void_value_expression?(node)
179
+ node = node.children.last while node&.begin_type?
180
+
181
+ node&.type?(:return, :break, :next)
182
+ end
183
+
180
184
  def check_for_literal(node)
181
185
  cond = condition(node)
182
186
  if cond.literal?
@@ -250,7 +254,7 @@ module RuboCop
250
254
  end
251
255
  end
252
256
 
253
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
257
+ # rubocop:disable-next Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
254
258
  def correct_if_node(node, cond)
255
259
  result = condition_evaluation?(node, cond)
256
260
 
@@ -281,7 +285,6 @@ module RuboCop
281
285
  ignore_node(node)
282
286
  end
283
287
  end
284
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
285
288
  end
286
289
  end
287
290
  end