rubocop 1.88.2 → 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 (227) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +139 -16
  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 +81 -49
  10. data/lib/rubocop/config.rb +76 -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 +39 -3
  14. data/lib/rubocop/cop/autocorrect_logic.rb +8 -0
  15. data/lib/rubocop/cop/badge.rb +8 -0
  16. data/lib/rubocop/cop/base.rb +40 -6
  17. data/lib/rubocop/cop/bundler.rb +19 -0
  18. data/lib/rubocop/cop/corrector.rb +4 -4
  19. data/lib/rubocop/cop/correctors/alignment_corrector.rb +55 -5
  20. data/lib/rubocop/cop/gemspec.rb +22 -0
  21. data/lib/rubocop/cop/generator/registration_injector.rb +188 -0
  22. data/lib/rubocop/cop/generator.rb +4 -0
  23. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -2
  24. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb +1 -2
  25. data/lib/rubocop/cop/layout/block_alignment.rb +1 -2
  26. data/lib/rubocop/cop/layout/class_structure.rb +108 -26
  27. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -2
  28. data/lib/rubocop/cop/layout/empty_line_after_magic_comment.rb +63 -5
  29. data/lib/rubocop/cop/layout/end_alignment.rb +1 -2
  30. data/lib/rubocop/cop/layout/extra_spacing.rb +105 -3
  31. data/lib/rubocop/cop/layout/first_argument_indentation.rb +21 -1
  32. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +1 -1
  33. data/lib/rubocop/cop/layout/indentation_width.rb +3 -1
  34. data/lib/rubocop/cop/layout/leading_comment_space.rb +27 -0
  35. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -2
  36. data/lib/rubocop/cop/layout/line_length.rb +84 -12
  37. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +1 -2
  38. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +12 -2
  39. data/lib/rubocop/cop/layout/redundant_line_break.rb +77 -0
  40. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +1 -2
  41. data/lib/rubocop/cop/layout/space_around_operators.rb +7 -0
  42. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +31 -34
  43. data/lib/rubocop/cop/layout.rb +114 -0
  44. data/lib/rubocop/cop/lazy_loader.rb +41 -0
  45. data/lib/rubocop/cop/lint/ambiguous_assignment.rb +9 -7
  46. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +71 -0
  47. data/lib/rubocop/cop/lint/ambiguous_range.rb +1 -2
  48. data/lib/rubocop/cop/lint/argument_mismatch.rb +89 -0
  49. data/lib/rubocop/cop/lint/constant_reassignment.rb +18 -21
  50. data/lib/rubocop/cop/lint/constant_resolution.rb +25 -0
  51. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +84 -8
  52. data/lib/rubocop/cop/lint/deprecated_reference.rb +194 -0
  53. data/lib/rubocop/cop/lint/duplicate_methods.rb +300 -22
  54. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +1 -2
  55. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +1 -2
  56. data/lib/rubocop/cop/lint/inherit_exception.rb +53 -7
  57. data/lib/rubocop/cop/lint/interpolation_check.rb +1 -2
  58. data/lib/rubocop/cop/lint/literal_as_condition.rb +8 -6
  59. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +4 -7
  60. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +25 -12
  61. data/lib/rubocop/cop/lint/missing_super.rb +31 -1
  62. data/lib/rubocop/cop/lint/name_typo.rb +270 -0
  63. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +15 -4
  64. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +98 -19
  65. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +47 -7
  66. data/lib/rubocop/cop/lint/redundant_require_statement.rb +1 -2
  67. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +1 -2
  68. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +1 -2
  69. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +1 -2
  70. data/lib/rubocop/cop/lint/redundant_with_index.rb +1 -2
  71. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +8 -2
  72. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +2 -4
  73. data/lib/rubocop/cop/lint/super_argument_mismatch.rb +179 -0
  74. data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +1 -2
  75. data/lib/rubocop/cop/lint/to_enum_arguments.rb +38 -3
  76. data/lib/rubocop/cop/lint/unreachable_code.rb +1 -2
  77. data/lib/rubocop/cop/lint/unused_private_method.rb +234 -0
  78. data/lib/rubocop/cop/lint/useless_access_modifier.rb +1 -2
  79. data/lib/rubocop/cop/lint/useless_assignment.rb +1 -2
  80. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +36 -0
  81. data/lib/rubocop/cop/lint/useless_method_definition.rb +1 -1
  82. data/lib/rubocop/cop/lint/useless_rescue.rb +1 -2
  83. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +2 -0
  84. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +61 -7
  85. data/lib/rubocop/cop/lint/void.rb +18 -8
  86. data/lib/rubocop/cop/lint.rb +178 -0
  87. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -2
  88. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  89. data/lib/rubocop/cop/metrics.rb +31 -0
  90. data/lib/rubocop/cop/migration/department_name.rb +1 -6
  91. data/lib/rubocop/cop/migration.rb +13 -0
  92. data/lib/rubocop/cop/mixin/check_line_breakable.rb +23 -5
  93. data/lib/rubocop/cop/mixin/comments_help.rb +1 -2
  94. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +1 -2
  95. data/lib/rubocop/cop/mixin/endless_method_rewriter.rb +13 -5
  96. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +23 -6
  97. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +1 -2
  98. data/lib/rubocop/cop/mixin/hash_subset.rb +1 -2
  99. data/lib/rubocop/cop/mixin/indexed_method_arity.rb +94 -0
  100. data/lib/rubocop/cop/mixin/line_length_help.rb +7 -1
  101. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +51 -6
  102. data/lib/rubocop/cop/mixin/project_index_help.rb +140 -0
  103. data/lib/rubocop/cop/mixin/range_help.rb +1 -2
  104. data/lib/rubocop/cop/mixin/reparsed_equivalence.rb +171 -0
  105. data/lib/rubocop/cop/mixin/statement_modifier.rb +46 -4
  106. data/lib/rubocop/cop/mixin/trailing_comma.rb +2 -4
  107. data/lib/rubocop/cop/mixin.rb +82 -77
  108. data/lib/rubocop/cop/naming/accessor_method_name.rb +24 -0
  109. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +16 -0
  110. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +2 -4
  111. data/lib/rubocop/cop/naming/method_name.rb +5 -2
  112. data/lib/rubocop/cop/naming/predicate_prefix.rb +33 -5
  113. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -2
  114. data/lib/rubocop/cop/naming/variable_number.rb +3 -1
  115. data/lib/rubocop/cop/naming.rb +31 -0
  116. data/lib/rubocop/cop/offense.rb +37 -10
  117. data/lib/rubocop/cop/registry.rb +180 -46
  118. data/lib/rubocop/cop/security.rb +19 -0
  119. data/lib/rubocop/cop/style/accessor_grouping.rb +1 -2
  120. data/lib/rubocop/cop/style/arguments_forwarding.rb +3 -6
  121. data/lib/rubocop/cop/style/array_first_last.rb +1 -2
  122. data/lib/rubocop/cop/style/array_intersect.rb +24 -5
  123. data/lib/rubocop/cop/style/bitwise_predicate.rb +1 -2
  124. data/lib/rubocop/cop/style/block_delimiters.rb +3 -6
  125. data/lib/rubocop/cop/style/case_like_if.rb +2 -4
  126. data/lib/rubocop/cop/style/class_and_module_children.rb +79 -20
  127. data/lib/rubocop/cop/style/combinable_loops.rb +1 -2
  128. data/lib/rubocop/cop/style/concat_array_literals.rb +1 -2
  129. data/lib/rubocop/cop/style/conditional_assignment.rb +23 -2
  130. data/lib/rubocop/cop/style/constant_visibility.rb +1 -2
  131. data/lib/rubocop/cop/style/copyright.rb +5 -1
  132. data/lib/rubocop/cop/style/directive_scope.rb +171 -0
  133. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +76 -12
  134. data/lib/rubocop/cop/style/documentation.rb +47 -0
  135. data/lib/rubocop/cop/style/empty_case_condition.rb +1 -2
  136. data/lib/rubocop/cop/style/endless_method.rb +0 -10
  137. data/lib/rubocop/cop/style/format_string_token.rb +1 -2
  138. data/lib/rubocop/cop/style/guard_clause.rb +1 -2
  139. data/lib/rubocop/cop/style/hash_each_methods.rb +1 -2
  140. data/lib/rubocop/cop/style/hash_syntax.rb +1 -2
  141. data/lib/rubocop/cop/style/identical_conditional_branches.rb +2 -4
  142. data/lib/rubocop/cop/style/if_inside_else.rb +1 -2
  143. data/lib/rubocop/cop/style/if_unless_modifier.rb +8 -50
  144. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +1 -2
  145. data/lib/rubocop/cop/style/lambda.rb +4 -0
  146. data/lib/rubocop/cop/style/map_to_hash.rb +1 -2
  147. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +22 -7
  148. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +1 -2
  149. data/lib/rubocop/cop/style/missing_respond_to_missing.rb +30 -0
  150. data/lib/rubocop/cop/style/multiline_if_then.rb +7 -1
  151. data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -2
  152. data/lib/rubocop/cop/style/multiple_comparison.rb +33 -22
  153. data/lib/rubocop/cop/style/mutable_constant.rb +5 -0
  154. data/lib/rubocop/cop/style/negated_if_else_condition.rb +1 -2
  155. data/lib/rubocop/cop/style/negative_array_index.rb +3 -3
  156. data/lib/rubocop/cop/style/nil_comparison.rb +1 -2
  157. data/lib/rubocop/cop/style/operator_method_call.rb +1 -2
  158. data/lib/rubocop/cop/style/parallel_assignment.rb +1 -2
  159. data/lib/rubocop/cop/style/redundant_assignment.rb +12 -2
  160. data/lib/rubocop/cop/style/redundant_condition.rb +2 -4
  161. data/lib/rubocop/cop/style/redundant_constant_base.rb +37 -1
  162. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +1 -2
  163. data/lib/rubocop/cop/style/redundant_each.rb +1 -2
  164. data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +9 -0
  165. data/lib/rubocop/cop/style/redundant_format.rb +14 -7
  166. data/lib/rubocop/cop/style/redundant_line_continuation.rb +78 -135
  167. data/lib/rubocop/cop/style/redundant_parentheses.rb +65 -67
  168. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +1 -2
  169. data/lib/rubocop/cop/style/redundant_return.rb +1 -2
  170. data/lib/rubocop/cop/style/redundant_self_assignment.rb +1 -2
  171. data/lib/rubocop/cop/style/redundant_string_escape.rb +1 -2
  172. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -2
  173. data/lib/rubocop/cop/style/safe_navigation.rb +11 -2
  174. data/lib/rubocop/cop/style/sample.rb +11 -6
  175. data/lib/rubocop/cop/style/select_by_regexp.rb +1 -2
  176. data/lib/rubocop/cop/style/semicolon.rb +2 -4
  177. data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -2
  178. data/lib/rubocop/cop/style/sole_nested_conditional.rb +21 -5
  179. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +2 -0
  180. data/lib/rubocop/cop/style/static_class.rb +21 -0
  181. data/lib/rubocop/cop/style/string_concatenation.rb +18 -5
  182. data/lib/rubocop/cop/style/super_arguments.rb +1 -2
  183. data/lib/rubocop/cop/style/symbol_proc.rb +1 -2
  184. data/lib/rubocop/cop/style/time_now.rb +41 -0
  185. data/lib/rubocop/cop/style/yoda_condition.rb +1 -2
  186. data/lib/rubocop/cop/style.rb +312 -0
  187. data/lib/rubocop/cop/team.rb +59 -6
  188. data/lib/rubocop/cop/util.rb +2 -4
  189. data/lib/rubocop/cop/variable_force/variable.rb +19 -8
  190. data/lib/rubocop/cops_documentation_generator.rb +4 -7
  191. data/lib/rubocop/directive_comment.rb +39 -3
  192. data/lib/rubocop/feature_loader.rb +0 -6
  193. data/lib/rubocop/formatter/emacs_style_formatter.rb +3 -1
  194. data/lib/rubocop/formatter/html_formatter.rb +2 -4
  195. data/lib/rubocop/formatter/json_formatter.rb +11 -6
  196. data/lib/rubocop/formatter/junit_formatter.rb +26 -9
  197. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -2
  198. data/lib/rubocop/formatter/simple_text_formatter.rb +4 -3
  199. data/lib/rubocop/formatter/tap_formatter.rb +3 -1
  200. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -2
  201. data/lib/rubocop/lockfile.rb +18 -0
  202. data/lib/rubocop/lsp/diagnostic.rb +4 -8
  203. data/lib/rubocop/lsp/routes.rb +89 -22
  204. data/lib/rubocop/lsp/runtime.rb +4 -0
  205. data/lib/rubocop/lsp/server.rb +4 -0
  206. data/lib/rubocop/lsp/stdin_runner.rb +20 -0
  207. data/lib/rubocop/mcp/server.rb +1 -2
  208. data/lib/rubocop/options.rb +10 -6
  209. data/lib/rubocop/path_util.rb +1 -2
  210. data/lib/rubocop/pending_cops_reporter.rb +1 -6
  211. data/lib/rubocop/plugin/configuration_integrator.rb +6 -3
  212. data/lib/rubocop/project_index_loader.rb +16 -0
  213. data/lib/rubocop/rake_task.rb +1 -2
  214. data/lib/rubocop/result_cache.rb +6 -4
  215. data/lib/rubocop/rspec/expect_offense.rb +2 -4
  216. data/lib/rubocop/rspec/shared_contexts.rb +16 -2
  217. data/lib/rubocop/runner.rb +71 -43
  218. data/lib/rubocop/server/cache.rb +11 -7
  219. data/lib/rubocop/server/cli.rb +2 -4
  220. data/lib/rubocop/server.rb +14 -0
  221. data/lib/rubocop/target_finder.rb +4 -1
  222. data/lib/rubocop/target_ruby.rb +1 -1
  223. data/lib/rubocop/todo_audit.rb +111 -0
  224. data/lib/rubocop/version.rb +3 -5
  225. data/lib/rubocop.rb +20 -626
  226. data/lib/ruby_lsp/rubocop/addon.rb +1 -2
  227. metadata +28 -5
@@ -65,6 +65,7 @@ module RuboCop
65
65
  include AllowedPattern
66
66
  include RangeHelp
67
67
  include LineLengthHelp
68
+ include EndlessMethodRewriter
68
69
  extend AutoCorrector
69
70
 
70
71
  exclude_limit 'Max'
@@ -92,8 +93,15 @@ module RuboCop
92
93
  alias on_hash on_potential_breakable_node
93
94
  alias on_send on_potential_breakable_node
94
95
  alias on_csend on_potential_breakable_node
95
- alias on_def on_potential_breakable_node
96
- alias on_defs on_potential_breakable_node
96
+
97
+ def on_def(node)
98
+ if node.endless?
99
+ track_endless_method(node)
100
+ else
101
+ check_for_breakable_node(node)
102
+ end
103
+ end
104
+ alias on_defs on_def
97
105
 
98
106
  def on_new_investigation
99
107
  return unless processed_source.raw_source.include?(';')
@@ -111,6 +119,70 @@ module RuboCop
111
119
 
112
120
  attr_accessor :breakable_range
113
121
 
122
+ def track_endless_method(node)
123
+ line_index = node.first_line - processed_source.buffer.first_line
124
+ endless_methods_by_line[line_index] = node
125
+ end
126
+
127
+ def handle_endless_method_line(line, line_index)
128
+ if require_endless_methods?
129
+ return register_required_endless_method_offense(line, line_index)
130
+ end
131
+
132
+ register_endless_method_offense(line, line_index)
133
+ end
134
+
135
+ def require_endless_methods?
136
+ config.cop_enabled?('Style/EndlessMethod') &&
137
+ config.for_cop('Style/EndlessMethod')['EnforcedStyle'] == 'require_always'
138
+ end
139
+
140
+ def register_endless_method_offense(line, line_index)
141
+ message = format(MSG, length: line_length(line), max: max)
142
+ loc = excess_range(nil, line, line_index)
143
+
144
+ add_offense(loc, message: message) do |corrector|
145
+ self.max = line_length(line)
146
+
147
+ correct_to_multiline(corrector, endless_methods_by_line[line_index])
148
+ end
149
+ end
150
+
151
+ def register_required_endless_method_offense(line, line_index)
152
+ node = endless_methods_by_line[line_index]
153
+ loc = excess_range(nil, line, line_index)
154
+
155
+ add_offense(loc, message: format(MSG, length: line_length(line), max: max)) do |corrector|
156
+ self.max = line_length(line)
157
+
158
+ if correctable_endless_method_block?(node)
159
+ correct_endless_method_block_to_multiline(corrector, node)
160
+ end
161
+ end
162
+ end
163
+
164
+ def correctable_endless_method_block?(node)
165
+ block_node = node.body
166
+
167
+ block_node&.type?(:any_block) &&
168
+ block_node.braces? &&
169
+ block_node.single_line? &&
170
+ block_node.body &&
171
+ !receiver_contains_heredoc?(block_node)
172
+ end
173
+
174
+ def correct_endless_method_block_to_multiline(corrector, node)
175
+ block_node = node.body
176
+ block_arguments = block_node.arguments? ? " #{block_node.arguments.source}" : ''
177
+ replacement = [
178
+ "#{block_node.send_node.source} do#{block_arguments}",
179
+ "#{indent(node, offset: 2)}#{block_node.body.source}",
180
+ "#{indent(node)}end"
181
+ ].join("\n")
182
+
183
+ corrector.replace(block_node, replacement)
184
+ end
185
+
114
186
  def check_for_breakable_node(node)
115
187
  breakable_node = extract_breakable_node(node, max)
116
188
  return if breakable_node.nil?
@@ -237,6 +309,10 @@ module RuboCop
237
309
  @breakable_range_by_line_index ||= {}
238
310
  end
239
311
 
312
+ def endless_methods_by_line
313
+ @endless_methods_by_line ||= {}
314
+ end
315
+
240
316
  def breakable_string_delimiters
241
317
  @breakable_string_delimiters ||= {}
242
318
  end
@@ -252,10 +328,15 @@ module RuboCop
252
328
  [max - indentation_difference(line), 0].max
253
329
  end
254
330
 
255
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
331
+ # rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
256
332
  def check_line(line, line_index)
257
333
  return if line_length(line) <= max
258
334
  return if allowed_line?(line, line_index)
335
+
336
+ if endless_methods_by_line.key?(line_index)
337
+ return handle_endless_method_line(line, line_index)
338
+ end
339
+
259
340
  if allow_rbs_inline_annotation? && rbs_inline_annotation_on_source_line?(line_index)
260
341
  return
261
342
  end
@@ -267,7 +348,6 @@ module RuboCop
267
348
 
268
349
  register_offense(excess_range(nil, line, line_index), line, line_index)
269
350
  end
270
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
271
351
 
272
352
  def allowed_line?(line, line_index)
273
353
  matches_allowed_pattern?(line) ||
@@ -313,10 +393,6 @@ module RuboCop
313
393
  end
314
394
  alias max_line_length max
315
395
 
316
- def allow_heredoc?
317
- allowed_heredoc
318
- end
319
-
320
396
  def allowed_heredoc
321
397
  cop_config['AllowHeredoc']
322
398
  end
@@ -344,10 +420,6 @@ module RuboCop
344
420
  end
345
421
  end
346
422
 
347
- def line_in_heredoc?(line_number)
348
- heredocs.any? { |range, _delimiter| range.cover?(line_number) }
349
- end
350
-
351
423
  def receiver_contains_heredoc?(node)
352
424
  return false unless (receiver = node.receiver)
353
425
  return true if receiver.any_str_type? && receiver.heredoc?
@@ -71,7 +71,7 @@ module RuboCop
71
71
 
72
72
  BLOCK_TYPES = %i[block numblock itblock].freeze
73
73
 
74
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
74
+ # rubocop:disable-next Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
75
75
  def check_assignment(node, rhs)
76
76
  return if node.send_type? && node.loc.operator&.source != '='
77
77
  return unless rhs
@@ -80,7 +80,6 @@ module RuboCop
80
80
 
81
81
  check_by_enforced_style(node, rhs)
82
82
  end
83
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
84
83
 
85
84
  def check_by_enforced_style(node, rhs)
86
85
  case style
@@ -410,7 +410,7 @@ module RuboCop
410
410
 
411
411
  def find_continuation_node(node)
412
412
  receiver = node.receiver
413
- return receiver.send_node if single_line_block_receiver?(receiver)
413
+ return leftmost_call_on_same_line(receiver) if single_line_block_receiver?(receiver)
414
414
  return unless receiver.call_type? && receiver.loc.dot
415
415
  return receiver if receiver.receiver.begin_type? && node.block_node.single_line?
416
416
  return unless receiver.loc.dot.line > receiver.receiver.last_line
@@ -422,9 +422,19 @@ module RuboCop
422
422
  receiver.single_line? && receiver.any_block_type?
423
423
  end
424
424
 
425
+ def leftmost_call_on_same_line(node)
426
+ current = unwrap_block_node(node)
427
+ while (receiver = unwrap_block_node(current.receiver)) &&
428
+ receiver.call_type? && receiver.loc?(:dot) &&
429
+ receiver.loc.dot.line == current.loc.dot.line
430
+ current = receiver
431
+ end
432
+ current
433
+ end
434
+
425
435
  def handle_descendant_block(node)
426
436
  receiver = node.receiver
427
- return receiver.send_node if single_line_block_receiver?(receiver)
437
+ return leftmost_call_on_same_line(receiver) if single_line_block_receiver?(receiver)
428
438
 
429
439
  block_node = node.each_descendant(:any_block).first
430
440
  return unless block_node&.multiline?
@@ -49,6 +49,7 @@ module RuboCop
49
49
  class RedundantLineBreak < Base
50
50
  include CheckAssignment
51
51
  include CheckSingleLineSuitability
52
+ include ReparsedEquivalence
52
53
  extend AutoCorrector
53
54
 
54
55
  MSG = 'Redundant line break detected.'
@@ -82,12 +83,88 @@ module RuboCop
82
83
  end
83
84
 
84
85
  def register_offense(node)
86
+ # The exact single-line correction is verified to parse equivalently
87
+ # before the offense is registered, so a join that would change how
88
+ # the code parses is never reported or offered.
89
+ return if verified_by_reparse([node], oversized: :verify).empty?
90
+
85
91
  add_offense(node) do |corrector|
86
92
  corrector.replace(node, to_single_line(node.source).strip)
87
93
  end
88
94
  ignore_node(node)
89
95
  end
90
96
 
97
+ def apply_reparse_correction(corrector, node)
98
+ corrector.replace(node, to_single_line(node.source).strip)
99
+ end
100
+
101
+ # Joining lines shifts the value of any later `__LINE__`, exactly as
102
+ # any other line-removing correction does; neutralize it on both sides
103
+ # with an identifier, which stays valid in every position the keyword
104
+ # can appear in.
105
+ def preprocess_reparsed_source(source)
106
+ source.gsub(/\b__LINE__\b/, '__LINE0__')
107
+ end
108
+
109
+ def normalize_reparsed_ast(node)
110
+ fold_string_concatenation(node)
111
+ end
112
+
113
+ # Joining lines merges split string literals (`"a" \<newline> "b"` into
114
+ # `"ab"`) and turns mixed-quote pairs into `+` concatenation, which
115
+ # changes the tree but not the resulting string. Normalize all string
116
+ # concatenation to a canonical form before comparing: nested
117
+ # concatenations are flattened and adjacent literal parts merged.
118
+ def fold_string_concatenation(node)
119
+ return node unless node.is_a?(::Parser::AST::Node)
120
+
121
+ children = node.children.map { |child| fold_string_concatenation(child) }
122
+ node = node.updated(nil, children)
123
+
124
+ parts = string_concatenation_parts(node)
125
+ return node unless parts
126
+
127
+ merged = merge_string_parts(parts)
128
+ if merged.one? && merged.first.str_type?
129
+ node.updated(:str, merged.first.children)
130
+ else
131
+ node.updated(:dstr, merged)
132
+ end
133
+ end
134
+
135
+ def string_concatenation_parts(node)
136
+ case node.type
137
+ when :dstr
138
+ node.children
139
+ when :send
140
+ parts = [node.receiver, *node.arguments]
141
+ parts if node.method?(:+) && parts.all? { |part| stringish?(part) }
142
+ end
143
+ end
144
+
145
+ def merge_string_parts(parts)
146
+ flattened = parts.flat_map do |part|
147
+ part.is_a?(::Parser::AST::Node) && part.dstr_type? ? part.children : [part]
148
+ end
149
+
150
+ flattened.chunk_while { |left, right| plain_string?(left) && plain_string?(right) }
151
+ .map { |chunk| merge_plain_strings(chunk) }
152
+ end
153
+
154
+ def merge_plain_strings(chunk)
155
+ return chunk.first if chunk.one?
156
+
157
+ chunk.first.updated(:str, [chunk.map { |part| part.children.first }.join])
158
+ end
159
+
160
+ def stringish?(node)
161
+ node.is_a?(::Parser::AST::Node) && %i[str dstr].include?(node.type)
162
+ end
163
+
164
+ def plain_string?(node)
165
+ node.is_a?(::Parser::AST::Node) && node.str_type? && node.children.one?
166
+ end
167
+
91
168
  def offense?(node)
92
169
  return false unless node.multiline? && suitable_as_single_line?(node)
93
170
  return require_backslash?(node) if node.operator_keyword?
@@ -91,7 +91,7 @@ module RuboCop
91
91
  )
92
92
  end
93
93
 
94
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
94
+ # rubocop:disable-next Metrics/AbcSize, Metrics/MethodLength
95
95
  def alignment_source(node, starting_loc)
96
96
  ending_loc =
97
97
  case node.type
@@ -111,7 +111,6 @@ module RuboCop
111
111
 
112
112
  range_between(starting_loc.begin_pos, ending_loc.end_pos).source
113
113
  end
114
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
115
114
 
116
115
  # We will use ancestor or wrapper with access modifier.
117
116
 
@@ -129,6 +129,12 @@ module RuboCop
129
129
  check_operator(:class, node.loc.operator, rhs)
130
130
  end
131
131
 
132
+ def on_def(node)
133
+ return unless node.endless?
134
+
135
+ check_operator(:assignment, node.loc.assignment, node.body)
136
+ end
137
+
132
138
  def on_binary(node)
133
139
  rhs = node.rhs
134
140
 
@@ -170,6 +176,7 @@ module RuboCop
170
176
  alias on_or_asgn on_assignment
171
177
  alias on_and_asgn on_assignment
172
178
  alias on_op_asgn on_assignment
179
+ alias on_defs on_def
173
180
 
174
181
  private
175
182
 
@@ -94,10 +94,10 @@ module RuboCop
94
94
  return empty_offenses(node, left, right, EMPTY_MSG)
95
95
  end
96
96
 
97
- start_ok = next_to_newline?(node, left)
97
+ start_ok = next_to_newline?(tokens, left)
98
98
  end_ok = node.single_line? ? false : end_has_own_line?(right)
99
99
 
100
- issue_offenses(node, left, right, start_ok, end_ok)
100
+ issue_offenses(node, tokens, left, right, start_ok, end_ok)
101
101
  end
102
102
  alias on_array_pattern on_array
103
103
 
@@ -117,7 +117,7 @@ module RuboCop
117
117
  elsif style == :space
118
118
  SpaceCorrector.add_space(processed_source, corrector, left, right)
119
119
  else
120
- compact_corrections(corrector, node, left, right)
120
+ compact_corrections(corrector, tokens, left, right)
121
121
  end
122
122
  end
123
123
 
@@ -134,8 +134,8 @@ module RuboCop
134
134
  cop_config['EnforcedStyleForEmptyBrackets']
135
135
  end
136
136
 
137
- def next_to_newline?(node, token)
138
- processed_source.tokens_within(node)[index_for(node, token) + 1].line != token.line
137
+ def next_to_newline?(tokens, token)
138
+ tokens[index_for(tokens, token) + 1].line != token.line
139
139
  end
140
140
 
141
141
  def end_has_own_line?(token)
@@ -145,68 +145,65 @@ module RuboCop
145
145
  !/\S/.match?(processed_source.lines[line][0..col])
146
146
  end
147
147
 
148
- def index_for(node, token)
149
- processed_source.tokens_within(node).index(token)
148
+ def index_for(tokens, token)
149
+ tokens.index(token)
150
150
  end
151
151
 
152
152
  def line_and_column_for(token)
153
153
  [token.line - 1, token.column - 1]
154
154
  end
155
155
 
156
- def issue_offenses(node, left, right, start_ok, end_ok)
156
+ # rubocop:disable-next Metrics/ParameterLists
157
+ def issue_offenses(node, tokens, left, right, start_ok, end_ok)
157
158
  case style
158
159
  when :no_space
159
- start_ok = next_to_comment?(node, left)
160
+ start_ok = next_to_comment?(tokens, left)
160
161
  no_space_offenses(node, left, right, MSG, start_ok: start_ok, end_ok: end_ok)
161
162
  when :space
162
163
  space_offenses(node, left, right, MSG, start_ok: start_ok, end_ok: end_ok)
163
164
  else
164
- compact_offenses(node, left, right, start_ok, end_ok)
165
+ compact_offenses(node, tokens, left, right, start_ok, end_ok)
165
166
  end
166
167
  end
167
168
 
168
- def next_to_comment?(node, token)
169
- processed_source.tokens_within(node)[index_for(node, token) + 1].comment?
169
+ def next_to_comment?(tokens, token)
170
+ tokens[index_for(tokens, token) + 1].comment?
170
171
  end
171
172
 
172
- def compact_offenses(node, left, right, start_ok, end_ok)
173
- if qualifies_for_compact?(node, left, side: :left)
173
+ # rubocop:disable-next Metrics/ParameterLists
174
+ def compact_offenses(node, tokens, left, right, start_ok, end_ok)
175
+ if qualifies_for_compact?(tokens, left, side: :left)
174
176
  compact_offense(node, left, side: :left)
175
- elsif !multi_dimensional_array?(node, left, side: :left)
177
+ elsif !multi_dimensional_array?(tokens, left, side: :left)
176
178
  space_offenses(node, left, nil, MSG, start_ok: start_ok, end_ok: true)
177
179
  end
178
- if qualifies_for_compact?(node, right)
180
+
181
+ if qualifies_for_compact?(tokens, right)
179
182
  compact_offense(node, right)
180
- elsif !multi_dimensional_array?(node, right)
183
+ elsif !multi_dimensional_array?(tokens, right)
181
184
  space_offenses(node, nil, right, MSG, start_ok: true, end_ok: end_ok)
182
185
  end
183
186
  end
184
187
 
185
- def qualifies_for_compact?(node, token, side: :right)
188
+ def qualifies_for_compact?(tokens, token, side: :right)
186
189
  if side == :right
187
- multi_dimensional_array?(node, token) && token.space_before?
190
+ multi_dimensional_array?(tokens, token) && token.space_before?
188
191
  else
189
- multi_dimensional_array?(node, token, side: :left) && token.space_after?
192
+ multi_dimensional_array?(tokens, token, side: :left) && token.space_after?
190
193
  end
191
194
  end
192
195
 
193
- def multi_dimensional_array?(node, token, side: :right)
196
+ def multi_dimensional_array?(tokens, token, side: :right)
194
197
  offset = side == :right ? -1 : +1
195
- i = index_for(node, token) + offset
196
- i += offset while processed_source.tokens_within(node)[i].new_line?
198
+ i = index_for(tokens, token) + offset
199
+ i += offset while tokens[i].new_line?
197
200
  if side == :right
198
- processed_source.tokens_within(node)[i].right_bracket?
201
+ tokens[i].right_bracket?
199
202
  else
200
- processed_source.tokens_within(node)[i].left_bracket?
203
+ tokens[i].left_bracket?
201
204
  end
202
205
  end
203
206
 
204
- def next_to_bracket?(token, side: :right)
205
- line_index, col = line_and_column_for(token)
206
- line = processed_source.lines[line_index]
207
- side == :right ? line[col] == ']' : line[col + 2] == '['
208
- end
209
-
210
207
  def compact_offense(node, token, side: :right)
211
208
  if side == :right
212
209
  space_offense(node, token, :left, MSG, NO_SPACE_COMMAND)
@@ -215,14 +212,14 @@ module RuboCop
215
212
  end
216
213
  end
217
214
 
218
- def compact_corrections(corrector, node, left, right)
219
- if multi_dimensional_array?(node, left, side: :left)
215
+ def compact_corrections(corrector, tokens, left, right)
216
+ if multi_dimensional_array?(tokens, left, side: :left)
220
217
  compact(corrector, left, :right)
221
218
  elsif !left.space_after?
222
219
  corrector.insert_after(left.pos, ' ')
223
220
  end
224
221
 
225
- if multi_dimensional_array?(node, right)
222
+ if multi_dimensional_array?(tokens, right)
226
223
  compact(corrector, right, :left)
227
224
  elsif !right.space_before?
228
225
  corrector.insert_before(right.pos, ' ')
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Cops for the `Layout` department. The department's cops are registered for lazy loading
6
+ # and their files are loaded on demand.
7
+ module Layout
8
+ extend LazyLoader
9
+
10
+ register_cop :AccessModifierIndentation, "#{__dir__}/layout/access_modifier_indentation"
11
+ register_cop :ArgumentAlignment, "#{__dir__}/layout/argument_alignment"
12
+ register_cop :ArrayAlignment, "#{__dir__}/layout/array_alignment"
13
+ register_cop :AssignmentIndentation, "#{__dir__}/layout/assignment_indentation"
14
+ register_cop :BeginEndAlignment, "#{__dir__}/layout/begin_end_alignment"
15
+ register_cop :BlockAlignment, "#{__dir__}/layout/block_alignment"
16
+ register_cop :BlockEndNewline, "#{__dir__}/layout/block_end_newline"
17
+ register_cop :CaseIndentation, "#{__dir__}/layout/case_indentation"
18
+ register_cop :ClassStructure, "#{__dir__}/layout/class_structure"
19
+ register_cop :ClosingHeredocIndentation, "#{__dir__}/layout/closing_heredoc_indentation"
20
+ register_cop :ClosingParenthesisIndentation, "#{__dir__}/layout/closing_parenthesis_indentation"
21
+ register_cop :CommentIndentation, "#{__dir__}/layout/comment_indentation"
22
+ register_cop :ConditionPosition, "#{__dir__}/layout/condition_position"
23
+ register_cop :DefEndAlignment, "#{__dir__}/layout/def_end_alignment"
24
+ register_cop :DotPosition, "#{__dir__}/layout/dot_position"
25
+ register_cop :ElseAlignment, "#{__dir__}/layout/else_alignment"
26
+ register_cop :EmptyComment, "#{__dir__}/layout/empty_comment"
27
+ register_cop :EmptyLineAfterGuardClause, "#{__dir__}/layout/empty_line_after_guard_clause"
28
+ register_cop :EmptyLineAfterMagicComment, "#{__dir__}/layout/empty_line_after_magic_comment"
29
+ register_cop :EmptyLineAfterMultilineCondition, "#{__dir__}/layout/empty_line_after_multiline_condition"
30
+ register_cop :EmptyLineBetweenDefs, "#{__dir__}/layout/empty_line_between_defs"
31
+ register_cop :EmptyLinesAfterModuleInclusion, "#{__dir__}/layout/empty_lines_after_module_inclusion"
32
+ register_cop :EmptyLinesAroundAccessModifier, "#{__dir__}/layout/empty_lines_around_access_modifier"
33
+ register_cop :EmptyLinesAroundArguments, "#{__dir__}/layout/empty_lines_around_arguments"
34
+ register_cop :EmptyLinesAroundAttributeAccessor, "#{__dir__}/layout/empty_lines_around_attribute_accessor"
35
+ register_cop :EmptyLinesAroundBeginBody, "#{__dir__}/layout/empty_lines_around_begin_body"
36
+ register_cop :EmptyLinesAroundBlockBody, "#{__dir__}/layout/empty_lines_around_block_body"
37
+ register_cop :EmptyLinesAroundClassBody, "#{__dir__}/layout/empty_lines_around_class_body"
38
+ register_cop :EmptyLinesAroundExceptionHandlingKeywords, "#{__dir__}/layout/empty_lines_around_exception_handling_keywords"
39
+ register_cop :EmptyLinesAroundMethodBody, "#{__dir__}/layout/empty_lines_around_method_body"
40
+ register_cop :EmptyLinesAroundModuleBody, "#{__dir__}/layout/empty_lines_around_module_body"
41
+ register_cop :EmptyLines, "#{__dir__}/layout/empty_lines"
42
+ register_cop :EndAlignment, "#{__dir__}/layout/end_alignment"
43
+ register_cop :EndOfLine, "#{__dir__}/layout/end_of_line"
44
+ register_cop :ExtraSpacing, "#{__dir__}/layout/extra_spacing"
45
+ register_cop :FirstArgumentIndentation, "#{__dir__}/layout/first_argument_indentation"
46
+ register_cop :FirstArrayElementIndentation, "#{__dir__}/layout/first_array_element_indentation"
47
+ register_cop :FirstArrayElementLineBreak, "#{__dir__}/layout/first_array_element_line_break"
48
+ register_cop :FirstHashElementIndentation, "#{__dir__}/layout/first_hash_element_indentation"
49
+ register_cop :FirstHashElementLineBreak, "#{__dir__}/layout/first_hash_element_line_break"
50
+ register_cop :FirstMethodArgumentLineBreak, "#{__dir__}/layout/first_method_argument_line_break"
51
+ register_cop :FirstMethodParameterLineBreak, "#{__dir__}/layout/first_method_parameter_line_break"
52
+ register_cop :FirstParameterIndentation, "#{__dir__}/layout/first_parameter_indentation"
53
+ register_cop :HashAlignment, "#{__dir__}/layout/hash_alignment"
54
+ register_cop :HeredocArgumentClosingParenthesis, "#{__dir__}/layout/heredoc_argument_closing_parenthesis"
55
+ register_cop :HeredocIndentation, "#{__dir__}/layout/heredoc_indentation"
56
+ register_cop :IndentationConsistency, "#{__dir__}/layout/indentation_consistency"
57
+ register_cop :IndentationStyle, "#{__dir__}/layout/indentation_style"
58
+ register_cop :IndentationWidth, "#{__dir__}/layout/indentation_width"
59
+ register_cop :InitialIndentation, "#{__dir__}/layout/initial_indentation"
60
+ register_cop :LeadingCommentSpace, "#{__dir__}/layout/leading_comment_space"
61
+ register_cop :LeadingEmptyLines, "#{__dir__}/layout/leading_empty_lines"
62
+ register_cop :LineContinuationLeadingSpace, "#{__dir__}/layout/line_continuation_leading_space"
63
+ register_cop :LineContinuationSpacing, "#{__dir__}/layout/line_continuation_spacing"
64
+ register_cop :LineEndStringConcatenationIndentation, "#{__dir__}/layout/line_end_string_concatenation_indentation"
65
+ register_cop :LineLength, "#{__dir__}/layout/line_length"
66
+ register_cop :MultilineArrayBraceLayout, "#{__dir__}/layout/multiline_array_brace_layout"
67
+ register_cop :MultilineArrayLineBreaks, "#{__dir__}/layout/multiline_array_line_breaks"
68
+ register_cop :MultilineAssignmentLayout, "#{__dir__}/layout/multiline_assignment_layout"
69
+ register_cop :MultilineBlockLayout, "#{__dir__}/layout/multiline_block_layout"
70
+ register_cop :MultilineHashBraceLayout, "#{__dir__}/layout/multiline_hash_brace_layout"
71
+ register_cop :MultilineHashKeyLineBreaks, "#{__dir__}/layout/multiline_hash_key_line_breaks"
72
+ register_cop :MultilineMethodArgumentLineBreaks, "#{__dir__}/layout/multiline_method_argument_line_breaks"
73
+ register_cop :MultilineMethodCallBraceLayout, "#{__dir__}/layout/multiline_method_call_brace_layout"
74
+ register_cop :MultilineMethodCallIndentation, "#{__dir__}/layout/multiline_method_call_indentation"
75
+ register_cop :MultilineMethodDefinitionBraceLayout, "#{__dir__}/layout/multiline_method_definition_brace_layout"
76
+ register_cop :MultilineMethodParameterLineBreaks, "#{__dir__}/layout/multiline_method_parameter_line_breaks"
77
+ register_cop :MultilineOperationIndentation, "#{__dir__}/layout/multiline_operation_indentation"
78
+ register_cop :ParameterAlignment, "#{__dir__}/layout/parameter_alignment"
79
+ register_cop :RedundantLineBreak, "#{__dir__}/layout/redundant_line_break"
80
+ register_cop :RescueEnsureAlignment, "#{__dir__}/layout/rescue_ensure_alignment"
81
+ register_cop :SingleLineBlockChain, "#{__dir__}/layout/single_line_block_chain"
82
+ register_cop :SpaceAfterColon, "#{__dir__}/layout/space_after_colon"
83
+ register_cop :SpaceAfterComma, "#{__dir__}/layout/space_after_comma"
84
+ register_cop :SpaceAfterMethodName, "#{__dir__}/layout/space_after_method_name"
85
+ register_cop :SpaceAfterNot, "#{__dir__}/layout/space_after_not"
86
+ register_cop :SpaceAfterSemicolon, "#{__dir__}/layout/space_after_semicolon"
87
+ register_cop :SpaceAroundBlockParameters, "#{__dir__}/layout/space_around_block_parameters"
88
+ register_cop :SpaceAroundEqualsInParameterDefault, "#{__dir__}/layout/space_around_equals_in_parameter_default"
89
+ register_cop :SpaceAroundKeyword, "#{__dir__}/layout/space_around_keyword"
90
+ register_cop :SpaceAroundMethodCallOperator, "#{__dir__}/layout/space_around_method_call_operator"
91
+ register_cop :SpaceAroundOperators, "#{__dir__}/layout/space_around_operators"
92
+ register_cop :SpaceBeforeBlockBraces, "#{__dir__}/layout/space_before_block_braces"
93
+ register_cop :SpaceBeforeBrackets, "#{__dir__}/layout/space_before_brackets"
94
+ register_cop :SpaceBeforeComma, "#{__dir__}/layout/space_before_comma"
95
+ register_cop :SpaceBeforeComment, "#{__dir__}/layout/space_before_comment"
96
+ register_cop :SpaceBeforeFirstArg, "#{__dir__}/layout/space_before_first_arg"
97
+ register_cop :SpaceBeforeSemicolon, "#{__dir__}/layout/space_before_semicolon"
98
+ register_cop :SpaceInLambdaLiteral, "#{__dir__}/layout/space_in_lambda_literal"
99
+ register_cop :SpaceInsideArrayPercentLiteral, "#{__dir__}/layout/space_inside_array_percent_literal"
100
+ register_cop :SpaceInsideArrayLiteralBrackets, "#{__dir__}/layout/space_inside_array_literal_brackets"
101
+ register_cop :SpaceInsideBlockBraces, "#{__dir__}/layout/space_inside_block_braces"
102
+ register_cop :SpaceInsideHashLiteralBraces, "#{__dir__}/layout/space_inside_hash_literal_braces"
103
+ register_cop :SpaceInsideParens, "#{__dir__}/layout/space_inside_parens"
104
+ register_cop :SpaceInsidePercentLiteralDelimiters, "#{__dir__}/layout/space_inside_percent_literal_delimiters"
105
+ register_cop :SpaceInsideRangeLiteral, "#{__dir__}/layout/space_inside_range_literal"
106
+ register_cop :SpaceInsideReferenceBrackets, "#{__dir__}/layout/space_inside_reference_brackets"
107
+ register_cop :SpaceInsideStringInterpolation, "#{__dir__}/layout/space_inside_string_interpolation"
108
+ register_cop :TrailingEmptyLines, "#{__dir__}/layout/trailing_empty_lines"
109
+ register_cop :TrailingWhitespace, "#{__dir__}/layout/trailing_whitespace"
110
+
111
+ autoload :EmptyLinesAroundBody, "#{__dir__}/mixin/empty_lines_around_body"
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Extend a department module with this module and call `register_cop` to register
6
+ # the department's cops with the global registry without loading their files until
7
+ # a cop class itself is needed:
8
+ #
9
+ # module RuboCop
10
+ # module Cop
11
+ # # The Foo department.
12
+ # module Foo
13
+ # extend LazyLoader
14
+ #
15
+ # register_cop :BarBaz, "#{__dir__}/foo/bar_baz"
16
+ # end
17
+ # end
18
+ # end
19
+ module LazyLoader
20
+ # Registers a cop for lazy loading. The cop appears in the registry by name right away,
21
+ # but its file is loaded only when the cop class is needed, e.g. when the cop is enabled
22
+ # for an inspection run.
23
+ #
24
+ # The path must be absolute so that loading does not depend on `$LOAD_PATH` and cannot resolve
25
+ # to another RuboCop installation.
26
+ #
27
+ # @param constant_name [Symbol] name of the cop class, e.g. `:BarBaz`
28
+ # @param path [String] absolute path to the file defining the cop class,
29
+ # with or without the `.rb` extension
30
+ def register_cop(constant_name, path)
31
+ raise ArgumentError, "cop path must be absolute: #{path}" unless File.absolute_path?(path)
32
+
33
+ autoload constant_name, path
34
+
35
+ qualified_name = "#{name}::#{constant_name}"
36
+
37
+ Registry.global.lazy_load(Badge.for(qualified_name), qualified_name)
38
+ end
39
+ end
40
+ end
41
+ end