rubocop 1.62.1 → 1.67.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 (276) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +67 -68
  3. data/config/default.yml +82 -12
  4. data/config/internal_affairs.yml +11 -0
  5. data/exe/rubocop +4 -3
  6. data/lib/rubocop/cached_data.rb +11 -3
  7. data/lib/rubocop/cli/command/auto_generate_config.rb +6 -7
  8. data/lib/rubocop/cli/command/lsp.rb +2 -2
  9. data/lib/rubocop/cli/command/show_docs_url.rb +2 -2
  10. data/lib/rubocop/cli.rb +4 -0
  11. data/lib/rubocop/comment_config.rb +1 -1
  12. data/lib/rubocop/config.rb +37 -11
  13. data/lib/rubocop/config_loader.rb +15 -9
  14. data/lib/rubocop/config_loader_resolver.rb +13 -8
  15. data/lib/rubocop/config_obsoletion.rb +1 -1
  16. data/lib/rubocop/config_validator.rb +3 -2
  17. data/lib/rubocop/cop/base.rb +56 -16
  18. data/lib/rubocop/cop/bundler/gem_version.rb +4 -5
  19. data/lib/rubocop/cop/cop.rb +30 -4
  20. data/lib/rubocop/cop/correctors/line_break_corrector.rb +2 -0
  21. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +1 -1
  22. data/lib/rubocop/cop/documentation.rb +32 -5
  23. data/lib/rubocop/cop/force.rb +12 -0
  24. data/lib/rubocop/cop/gemspec/add_runtime_dependency.rb +38 -0
  25. data/lib/rubocop/cop/gemspec/dependency_version.rb +3 -5
  26. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +2 -2
  27. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +3 -3
  28. data/lib/rubocop/cop/internal_affairs/cop_description.rb +0 -4
  29. data/lib/rubocop/cop/internal_affairs/empty_line_between_expect_offense_and_correction.rb +2 -1
  30. data/lib/rubocop/cop/internal_affairs/example_description.rb +2 -1
  31. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -1
  32. data/lib/rubocop/cop/internal_affairs/redundant_message_argument.rb +6 -21
  33. data/lib/rubocop/cop/internal_affairs/redundant_source_range.rb +8 -1
  34. data/lib/rubocop/cop/internal_affairs/undefined_config.rb +11 -1
  35. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +0 -5
  36. data/lib/rubocop/cop/internal_affairs.rb +16 -0
  37. data/lib/rubocop/cop/layout/access_modifier_indentation.rb +5 -1
  38. data/lib/rubocop/cop/layout/assignment_indentation.rb +3 -2
  39. data/lib/rubocop/cop/layout/block_alignment.rb +30 -12
  40. data/lib/rubocop/cop/layout/case_indentation.rb +1 -1
  41. data/lib/rubocop/cop/layout/comment_indentation.rb +1 -1
  42. data/lib/rubocop/cop/layout/condition_position.rb +0 -4
  43. data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
  44. data/lib/rubocop/cop/layout/empty_comment.rb +3 -1
  45. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
  46. data/lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb +1 -1
  47. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +2 -1
  48. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +8 -3
  49. data/lib/rubocop/cop/layout/first_argument_indentation.rb +2 -2
  50. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +2 -3
  51. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +8 -0
  52. data/lib/rubocop/cop/layout/heredoc_indentation.rb +1 -1
  53. data/lib/rubocop/cop/layout/indentation_width.rb +5 -6
  54. data/lib/rubocop/cop/layout/leading_comment_space.rb +28 -1
  55. data/lib/rubocop/cop/layout/line_length.rb +20 -20
  56. data/lib/rubocop/cop/layout/space_around_operators.rb +3 -0
  57. data/lib/rubocop/cop/layout/space_inside_string_interpolation.rb +3 -4
  58. data/lib/rubocop/cop/legacy/corrector.rb +12 -2
  59. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +0 -2
  60. data/lib/rubocop/cop/lint/ambiguous_operator.rb +0 -2
  61. data/lib/rubocop/cop/lint/ambiguous_range.rb +4 -1
  62. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +0 -2
  63. data/lib/rubocop/cop/lint/assignment_in_condition.rb +2 -2
  64. data/lib/rubocop/cop/lint/big_decimal_new.rb +4 -7
  65. data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -3
  66. data/lib/rubocop/cop/lint/circular_argument_reference.rb +0 -13
  67. data/lib/rubocop/cop/lint/debugger.rb +27 -6
  68. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +1 -1
  69. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +0 -10
  70. data/lib/rubocop/cop/lint/duplicate_case_condition.rb +1 -5
  71. data/lib/rubocop/cop/lint/duplicate_hash_key.rb +0 -4
  72. data/lib/rubocop/cop/lint/duplicate_methods.rb +0 -10
  73. data/lib/rubocop/cop/lint/duplicate_set_element.rb +74 -0
  74. data/lib/rubocop/cop/lint/each_with_object_argument.rb +0 -4
  75. data/lib/rubocop/cop/lint/else_layout.rb +0 -2
  76. data/lib/rubocop/cop/lint/empty_conditional_body.rb +28 -7
  77. data/lib/rubocop/cop/lint/empty_ensure.rb +1 -11
  78. data/lib/rubocop/cop/lint/empty_interpolation.rb +0 -4
  79. data/lib/rubocop/cop/lint/empty_when.rb +1 -3
  80. data/lib/rubocop/cop/lint/ensure_return.rb +1 -9
  81. data/lib/rubocop/cop/lint/erb_new_arguments.rb +21 -14
  82. data/lib/rubocop/cop/lint/float_comparison.rb +1 -1
  83. data/lib/rubocop/cop/lint/float_out_of_range.rb +0 -4
  84. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +0 -10
  85. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +23 -12
  86. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +0 -7
  87. data/lib/rubocop/cop/lint/interpolation_check.rb +0 -4
  88. data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +5 -14
  89. data/lib/rubocop/cop/lint/literal_as_condition.rb +1 -1
  90. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +1 -1
  91. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +25 -6
  92. data/lib/rubocop/cop/lint/loop.rb +6 -12
  93. data/lib/rubocop/cop/lint/mixed_case_range.rb +9 -4
  94. data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -7
  95. data/lib/rubocop/cop/lint/next_without_accumulator.rb +0 -4
  96. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +0 -5
  97. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +1 -1
  98. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +5 -6
  99. data/lib/rubocop/cop/lint/percent_string_array.rb +0 -4
  100. data/lib/rubocop/cop/lint/percent_symbol_array.rb +0 -4
  101. data/lib/rubocop/cop/lint/rand_one.rb +0 -4
  102. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +3 -1
  103. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +1 -1
  104. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +1 -1
  105. data/lib/rubocop/cop/lint/redundant_string_coercion.rb +0 -4
  106. data/lib/rubocop/cop/lint/redundant_with_index.rb +3 -0
  107. data/lib/rubocop/cop/lint/require_parentheses.rb +0 -4
  108. data/lib/rubocop/cop/lint/rescue_exception.rb +0 -4
  109. data/lib/rubocop/cop/lint/return_in_void_context.rb +0 -2
  110. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +0 -4
  111. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +105 -41
  112. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +6 -10
  113. data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
  114. data/lib/rubocop/cop/lint/to_enum_arguments.rb +2 -9
  115. data/lib/rubocop/cop/lint/unified_integer.rb +0 -4
  116. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -0
  117. data/lib/rubocop/cop/lint/unreachable_code.rb +4 -7
  118. data/lib/rubocop/cop/lint/unreachable_loop.rb +8 -2
  119. data/lib/rubocop/cop/lint/uri_regexp.rb +25 -7
  120. data/lib/rubocop/cop/lint/useless_assignment.rb +19 -16
  121. data/lib/rubocop/cop/lint/useless_else_without_rescue.rb +0 -4
  122. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +77 -0
  123. data/lib/rubocop/cop/lint/useless_setter_call.rb +0 -4
  124. data/lib/rubocop/cop/lint/void.rb +35 -8
  125. data/lib/rubocop/cop/metrics/block_length.rb +6 -5
  126. data/lib/rubocop/cop/metrics/block_nesting.rb +19 -7
  127. data/lib/rubocop/cop/metrics/class_length.rb +6 -5
  128. data/lib/rubocop/cop/metrics/method_length.rb +6 -5
  129. data/lib/rubocop/cop/metrics/module_length.rb +6 -5
  130. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +5 -5
  131. data/lib/rubocop/cop/mixin/alignment.rb +5 -1
  132. data/lib/rubocop/cop/mixin/allowed_methods.rb +7 -1
  133. data/lib/rubocop/cop/mixin/allowed_pattern.rb +15 -3
  134. data/lib/rubocop/cop/mixin/annotation_comment.rb +0 -2
  135. data/lib/rubocop/cop/mixin/code_length.rb +12 -1
  136. data/lib/rubocop/cop/mixin/configurable_max.rb +5 -1
  137. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +19 -9
  138. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +9 -2
  139. data/lib/rubocop/cop/mixin/line_length_help.rb +7 -2
  140. data/lib/rubocop/cop/mixin/method_complexity.rb +15 -6
  141. data/lib/rubocop/cop/mixin/percent_array.rb +1 -1
  142. data/lib/rubocop/cop/mixin/rescue_node.rb +4 -0
  143. data/lib/rubocop/cop/mixin/safe_assignment.rb +1 -1
  144. data/lib/rubocop/cop/mixin/statement_modifier.rb +3 -2
  145. data/lib/rubocop/cop/mixin/string_literals_help.rb +12 -0
  146. data/lib/rubocop/cop/naming/accessor_method_name.rb +5 -0
  147. data/lib/rubocop/cop/naming/block_forwarding.rb +31 -12
  148. data/lib/rubocop/cop/naming/file_name.rb +2 -2
  149. data/lib/rubocop/cop/naming/inclusive_language.rb +13 -5
  150. data/lib/rubocop/cop/naming/predicate_name.rb +53 -27
  151. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +10 -1
  152. data/lib/rubocop/cop/offense.rb +2 -2
  153. data/lib/rubocop/cop/security/compound_hash.rb +2 -2
  154. data/lib/rubocop/cop/style/access_modifier_declarations.rb +62 -2
  155. data/lib/rubocop/cop/style/accessor_grouping.rb +10 -2
  156. data/lib/rubocop/cop/style/alias.rb +2 -1
  157. data/lib/rubocop/cop/style/arguments_forwarding.rb +59 -11
  158. data/lib/rubocop/cop/style/block_delimiters.rb +14 -1
  159. data/lib/rubocop/cop/style/collection_compact.rb +9 -9
  160. data/lib/rubocop/cop/style/combinable_loops.rb +7 -0
  161. data/lib/rubocop/cop/style/commented_keyword.rb +7 -1
  162. data/lib/rubocop/cop/style/conditional_assignment.rb +2 -2
  163. data/lib/rubocop/cop/style/copyright.rb +31 -21
  164. data/lib/rubocop/cop/style/data_inheritance.rb +1 -1
  165. data/lib/rubocop/cop/style/def_with_parentheses.rb +0 -2
  166. data/lib/rubocop/cop/style/documentation.rb +24 -24
  167. data/lib/rubocop/cop/style/documentation_method.rb +20 -0
  168. data/lib/rubocop/cop/style/each_for_simple_loop.rb +0 -1
  169. data/lib/rubocop/cop/style/empty_else.rb +6 -5
  170. data/lib/rubocop/cop/style/empty_heredoc.rb +1 -14
  171. data/lib/rubocop/cop/style/empty_literal.rb +31 -22
  172. data/lib/rubocop/cop/style/eval_with_location.rb +16 -13
  173. data/lib/rubocop/cop/style/exact_regexp_match.rb +2 -1
  174. data/lib/rubocop/cop/style/file_read.rb +2 -5
  175. data/lib/rubocop/cop/style/file_write.rb +2 -5
  176. data/lib/rubocop/cop/style/format_string.rb +9 -9
  177. data/lib/rubocop/cop/style/format_string_token.rb +2 -2
  178. data/lib/rubocop/cop/style/global_std_stream.rb +7 -1
  179. data/lib/rubocop/cop/style/guard_clause.rb +3 -1
  180. data/lib/rubocop/cop/style/hash_each_methods.rb +6 -0
  181. data/lib/rubocop/cop/style/hash_except.rb +8 -5
  182. data/lib/rubocop/cop/style/hash_syntax.rb +20 -2
  183. data/lib/rubocop/cop/style/identical_conditional_branches.rb +1 -1
  184. data/lib/rubocop/cop/style/if_inside_else.rb +1 -1
  185. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +5 -4
  186. data/lib/rubocop/cop/style/if_with_semicolon.rb +49 -6
  187. data/lib/rubocop/cop/style/in_pattern_then.rb +6 -2
  188. data/lib/rubocop/cop/style/invertible_unless_condition.rb +2 -2
  189. data/lib/rubocop/cop/style/lambda.rb +1 -1
  190. data/lib/rubocop/cop/style/magic_comment_format.rb +1 -1
  191. data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +77 -43
  192. data/lib/rubocop/cop/style/map_into_array.rb +228 -0
  193. data/lib/rubocop/cop/style/map_to_hash.rb +1 -1
  194. data/lib/rubocop/cop/style/map_to_set.rb +1 -1
  195. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +17 -7
  196. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +1 -1
  197. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +2 -2
  198. data/lib/rubocop/cop/style/missing_else.rb +0 -4
  199. data/lib/rubocop/cop/style/multiline_memoization.rb +1 -1
  200. data/lib/rubocop/cop/style/multiline_when_then.rb +0 -4
  201. data/lib/rubocop/cop/style/multiple_comparison.rb +3 -11
  202. data/lib/rubocop/cop/style/nested_modifier.rb +1 -1
  203. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +1 -1
  204. data/lib/rubocop/cop/style/numeric_predicate.rb +12 -4
  205. data/lib/rubocop/cop/style/one_line_conditional.rb +6 -2
  206. data/lib/rubocop/cop/style/operator_method_call.rb +25 -6
  207. data/lib/rubocop/cop/style/parallel_assignment.rb +5 -4
  208. data/lib/rubocop/cop/style/quoted_symbols.rb +1 -3
  209. data/lib/rubocop/cop/style/redundant_argument.rb +24 -1
  210. data/lib/rubocop/cop/style/redundant_begin.rb +5 -1
  211. data/lib/rubocop/cop/style/redundant_condition.rb +4 -4
  212. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +1 -1
  213. data/lib/rubocop/cop/style/redundant_each.rb +1 -1
  214. data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +1 -1
  215. data/lib/rubocop/cop/style/redundant_filter_chain.rb +1 -1
  216. data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +46 -0
  217. data/lib/rubocop/cop/style/redundant_line_continuation.rb +15 -18
  218. data/lib/rubocop/cop/style/redundant_parentheses.rb +1 -1
  219. data/lib/rubocop/cop/style/redundant_percent_q.rb +1 -1
  220. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +4 -1
  221. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -24
  222. data/lib/rubocop/cop/style/require_order.rb +2 -2
  223. data/lib/rubocop/cop/style/rescue_modifier.rb +13 -1
  224. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +54 -12
  225. data/lib/rubocop/cop/style/safe_navigation.rb +94 -52
  226. data/lib/rubocop/cop/style/select_by_regexp.rb +9 -6
  227. data/lib/rubocop/cop/style/semicolon.rb +1 -1
  228. data/lib/rubocop/cop/style/send.rb +4 -4
  229. data/lib/rubocop/cop/style/send_with_literal_method_name.rb +104 -0
  230. data/lib/rubocop/cop/style/sole_nested_conditional.rb +21 -2
  231. data/lib/rubocop/cop/style/special_global_vars.rb +1 -2
  232. data/lib/rubocop/cop/style/struct_inheritance.rb +1 -1
  233. data/lib/rubocop/cop/style/super_arguments.rb +174 -0
  234. data/lib/rubocop/cop/style/symbol_proc.rb +39 -5
  235. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
  236. data/lib/rubocop/cop/style/top_level_method_definition.rb +1 -1
  237. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  238. data/lib/rubocop/cop/style/while_until_do.rb +0 -2
  239. data/lib/rubocop/cop/style/while_until_modifier.rb +0 -1
  240. data/lib/rubocop/cop/style/zero_length_predicate.rb +32 -24
  241. data/lib/rubocop/cop/team.rb +27 -3
  242. data/lib/rubocop/cop/util.rb +8 -2
  243. data/lib/rubocop/cop/variable_force.rb +13 -1
  244. data/lib/rubocop/cops_documentation_generator.rb +73 -34
  245. data/lib/rubocop/core_ext/string.rb +2 -6
  246. data/lib/rubocop/ext/regexp_node.rb +9 -31
  247. data/lib/rubocop/ext/regexp_parser.rb +4 -21
  248. data/lib/rubocop/file_finder.rb +9 -4
  249. data/lib/rubocop/formatter/clang_style_formatter.rb +3 -7
  250. data/lib/rubocop/formatter/disabled_config_formatter.rb +13 -9
  251. data/lib/rubocop/formatter/formatter_set.rb +7 -1
  252. data/lib/rubocop/formatter/html_formatter.rb +3 -1
  253. data/lib/rubocop/formatter/junit_formatter.rb +70 -23
  254. data/lib/rubocop/formatter/tap_formatter.rb +3 -7
  255. data/lib/rubocop/lockfile.rb +58 -7
  256. data/lib/rubocop/lsp/routes.rb +10 -13
  257. data/lib/rubocop/lsp/runtime.rb +2 -0
  258. data/lib/rubocop/lsp/server.rb +2 -1
  259. data/lib/rubocop/lsp.rb +9 -2
  260. data/lib/rubocop/options.rb +6 -4
  261. data/lib/rubocop/rake_task.rb +1 -1
  262. data/lib/rubocop/remote_config.rb +5 -1
  263. data/lib/rubocop/result_cache.rb +2 -8
  264. data/lib/rubocop/rspec/expect_offense.rb +9 -0
  265. data/lib/rubocop/rspec/shared_contexts.rb +35 -3
  266. data/lib/rubocop/rspec/support.rb +1 -0
  267. data/lib/rubocop/runner.rb +8 -1
  268. data/lib/rubocop/server/cache.rb +16 -1
  269. data/lib/rubocop/server/client_command/exec.rb +2 -2
  270. data/lib/rubocop/server/client_command/start.rb +1 -1
  271. data/lib/rubocop/server/core.rb +5 -0
  272. data/lib/rubocop/target_ruby.rb +19 -15
  273. data/lib/rubocop/version.rb +7 -5
  274. data/lib/rubocop/yaml_duplication_checker.rb +20 -26
  275. data/lib/rubocop.rb +13 -1
  276. metadata +20 -32
@@ -12,7 +12,7 @@ module RuboCop
12
12
  class Config
13
13
  include PathUtil
14
14
  include FileFinder
15
- extend Forwardable
15
+ extend SimpleForwardable
16
16
 
17
17
  CopConfig = Struct.new(:name, :metadata)
18
18
 
@@ -171,6 +171,10 @@ module RuboCop
171
171
  for_all_cops['ActiveSupportExtensionsEnabled']
172
172
  end
173
173
 
174
+ def string_literals_frozen_by_default?
175
+ for_all_cops['StringLiteralsFrozenByDefault']
176
+ end
177
+
174
178
  def file_to_include?(file)
175
179
  relative_file_path = path_relative_to_config(file)
176
180
 
@@ -263,6 +267,7 @@ module RuboCop
263
267
  PathUtil.smart_path(@loaded_path)
264
268
  end
265
269
 
270
+ # @return [String, nil]
266
271
  def bundler_lock_file_path
267
272
  return nil unless loaded_path
268
273
 
@@ -286,27 +291,48 @@ module RuboCop
286
291
  end
287
292
  end
288
293
 
294
+ # Returns target's locked gem versions (i.e. from Gemfile.lock or gems.locked)
295
+ # @returns [Hash{String => Gem::Version}] The locked gem versions, keyed by the gems' names.
296
+ def gem_versions_in_target
297
+ @gem_versions_in_target ||= read_gem_versions_from_target_lockfile
298
+ end
299
+
289
300
  def inspect # :nodoc:
290
301
  "#<#{self.class.name}:#{object_id} @loaded_path=#{loaded_path}>"
291
302
  end
292
303
 
293
304
  private
294
305
 
306
+ # @return [Float, nil] The Rails version as a `major.minor` Float.
295
307
  def target_rails_version_from_bundler_lock_file
296
308
  @target_rails_version_from_bundler_lock_file ||= read_rails_version_from_bundler_lock_file
297
309
  end
298
310
 
311
+ # @return [Float, nil] The Rails version as a `major.minor` Float.
299
312
  def read_rails_version_from_bundler_lock_file
300
- lock_file_path = bundler_lock_file_path
301
- return nil unless lock_file_path
302
-
303
- File.foreach(lock_file_path) do |line|
304
- # If Rails (or one of its frameworks) is in Gemfile.lock or gems.lock, there should be
305
- # a line like:
306
- # railties (X.X.X)
307
- result = line.match(/^\s+railties\s+\((\d+\.\d+)/)
308
- return result.captures.first.to_f if result
309
- end
313
+ return nil unless gem_versions_in_target
314
+
315
+ # Look for `railties` instead of `rails`, to support apps that only use a subset of `rails`
316
+ # See https://github.com/rubocop/rubocop/pull/11289
317
+ rails_version_in_target = gem_versions_in_target['railties']
318
+ return nil unless rails_version_in_target
319
+
320
+ gem_version_to_major_minor_float(rails_version_in_target)
321
+ end
322
+
323
+ # @param [Gem::Version] gem_version an object like `Gem::Version.new("7.1.2.3")`
324
+ # @return [Float] The major and minor version, like `7.1`
325
+ def gem_version_to_major_minor_float(gem_version)
326
+ segments = gem_version.segments
327
+ Float("#{segments[0]}.#{segments[1]}")
328
+ end
329
+
330
+ # @returns [Hash{String => Gem::Version}] The locked gem versions, keyed by the gems' names.
331
+ def read_gem_versions_from_target_lockfile
332
+ lockfile_path = bundler_lock_file_path
333
+ return nil unless lockfile_path
334
+
335
+ Lockfile.new(lockfile_path).gem_versions
310
336
  end
311
337
 
312
338
  def enable_cop?(qualified_cop_name, cop_options)
@@ -67,8 +67,8 @@ module RuboCop
67
67
  def load_yaml_configuration(absolute_path)
68
68
  file_contents = read_file(absolute_path)
69
69
  yaml_code = Dir.chdir(File.dirname(absolute_path)) { ERB.new(file_contents).result }
70
- check_duplication(yaml_code, absolute_path)
71
- hash = yaml_safe_load(yaml_code, absolute_path) || {}
70
+ yaml_tree = check_duplication(yaml_code, absolute_path)
71
+ hash = yaml_tree_to_hash(yaml_tree) || {}
72
72
 
73
73
  puts "configuration from #{absolute_path}" if debug?
74
74
 
@@ -164,7 +164,7 @@ module RuboCop
164
164
  # searches will go past this directory.
165
165
  # @deprecated Use `RuboCop::ConfigFinder.project_root` instead.
166
166
  def project_root
167
- warn Rainbow(<<~WARNING).yellow
167
+ warn Rainbow(<<~WARNING).yellow, uplevel: 1
168
168
  `RuboCop::ConfigLoader.project_root` is deprecated and will be removed in RuboCop 2.0. \
169
169
  Use `RuboCop::ConfigFinder.project_root` instead.
170
170
  WARNING
@@ -235,8 +235,8 @@ module RuboCop
235
235
  raise ConfigNotFoundError, "Configuration file not found: #{absolute_path}"
236
236
  end
237
237
 
238
- def yaml_safe_load(yaml_code, filename)
239
- yaml_safe_load!(yaml_code, filename)
238
+ def yaml_tree_to_hash(yaml_tree)
239
+ yaml_tree_to_hash!(yaml_tree)
240
240
  rescue ::StandardError
241
241
  if defined?(::SafeYAML)
242
242
  raise 'SafeYAML is unmaintained, no longer needed and should be removed'
@@ -245,10 +245,16 @@ module RuboCop
245
245
  raise
246
246
  end
247
247
 
248
- def yaml_safe_load!(yaml_code, filename)
249
- YAML.safe_load(
250
- yaml_code, permitted_classes: [Regexp, Symbol], aliases: true, filename: filename
251
- )
248
+ def yaml_tree_to_hash!(yaml_tree)
249
+ return nil unless yaml_tree
250
+
251
+ # Optimization: Because we checked for duplicate keys, we already have the
252
+ # yaml tree and don't need to parse it again.
253
+ # Also see https://github.com/ruby/psych/blob/v5.1.2/lib/psych.rb#L322-L336
254
+ class_loader = YAML::ClassLoader::Restricted.new(%w[Regexp Symbol], [])
255
+ scanner = YAML::ScalarScanner.new(class_loader)
256
+ visitor = YAML::Visitors::ToRuby.new(scanner, class_loader)
257
+ visitor.accept(yaml_tree)
252
258
  end
253
259
  end
254
260
 
@@ -6,7 +6,7 @@ require 'yaml'
6
6
  module RuboCop
7
7
  # A help class for ConfigLoader that handles configuration resolution.
8
8
  # @api private
9
- class ConfigLoaderResolver
9
+ class ConfigLoaderResolver # rubocop:disable Metrics/ClassLength
10
10
  def resolve_requires(path, hash)
11
11
  config_dir = File.dirname(path)
12
12
  hash.delete('require').tap do |loaded_features|
@@ -166,7 +166,7 @@ module RuboCop
166
166
  return unless duplicate_setting?(base_hash, derived_hash, key, opts[:inherited_file])
167
167
 
168
168
  inherit_mode = opts[:inherit_mode]['merge'] || opts[:inherit_mode]['override']
169
- return if base_hash[key].is_a?(Array) && inherit_mode && inherit_mode.include?(key)
169
+ return if base_hash[key].is_a?(Array) && inherit_mode&.include?(key)
170
170
 
171
171
  puts "#{PathUtil.smart_path(opts[:file])}: " \
172
172
  "#{opts[:cop_name]}:#{key} overrides " \
@@ -194,11 +194,11 @@ module RuboCop
194
194
  end
195
195
 
196
196
  def should_merge?(mode, key)
197
- mode && mode['merge'] && mode['merge'].include?(key)
197
+ mode && mode['merge']&.include?(key)
198
198
  end
199
199
 
200
200
  def should_override?(mode, key)
201
- mode && mode['override'] && mode['override'].include?(key)
201
+ mode && mode['override']&.include?(key)
202
202
  end
203
203
 
204
204
  def merge_hashes?(base_hash, derived_hash, key)
@@ -239,8 +239,7 @@ module RuboCop
239
239
  end
240
240
 
241
241
  def remote_file?(uri)
242
- regex = URI::DEFAULT_PARSER.make_regexp(%w[http https])
243
- /\A#{regex}\z/.match?(uri)
242
+ uri.start_with?('http://', 'https://')
244
243
  end
245
244
 
246
245
  def handle_disabled_by_default(config, new_default_configuration)
@@ -267,8 +266,14 @@ module RuboCop
267
266
 
268
267
  def gem_config_path(gem_name, relative_config_path)
269
268
  if defined?(Bundler)
270
- gem = Bundler.load.specs[gem_name].first
271
- gem_path = gem.full_gem_path if gem
269
+ begin
270
+ gem = Bundler.load.specs[gem_name].first
271
+ gem_path = gem.full_gem_path if gem
272
+ rescue Bundler::GemfileNotFound
273
+ # No Gemfile found. Bundler may be loaded manually
274
+ rescue Bundler::GitError
275
+ # The Gemfile exists but contains an uninstalled git source
276
+ end
272
277
  end
273
278
 
274
279
  gem_path ||= Gem::Specification.find_by_name(gem_name).gem_dir
@@ -52,7 +52,7 @@ module RuboCop
52
52
  def load_rules # rubocop:disable Metrics/AbcSize
53
53
  rules = LOAD_RULES_CACHE[self.class.files] ||=
54
54
  self.class.files.each_with_object({}) do |filename, hash|
55
- hash.merge!(YAML.safe_load(File.read(filename))) do |_key, first, second|
55
+ hash.merge!(YAML.safe_load(File.read(filename)) || {}) do |_key, first, second|
56
56
  case first
57
57
  when Hash
58
58
  first.merge(second)
@@ -4,10 +4,11 @@ module RuboCop
4
4
  # Handles validation of configuration, for example cop names, parameter
5
5
  # names, and Ruby versions.
6
6
  class ConfigValidator
7
- extend Forwardable
7
+ extend SimpleForwardable
8
8
 
9
9
  # @api private
10
- COMMON_PARAMS = %w[Exclude Include Severity inherit_mode AutoCorrect StyleGuide Details].freeze
10
+ COMMON_PARAMS = %w[Exclude Include Severity inherit_mode AutoCorrect StyleGuide Details
11
+ Enabled].freeze
11
12
  # @api private
12
13
  INTERNAL_PARAMS = %w[Description StyleGuide
13
14
  VersionAdded VersionChanged VersionRemoved
@@ -60,16 +60,20 @@ module RuboCop
60
60
  []
61
61
  end
62
62
 
63
- # Cops (other than builtin) are encouraged to implement this
63
+ # Returns an url to view this cops documentation online.
64
+ # Requires 'DocumentationBaseURL' to be set for your department.
65
+ # Will follow the convention of RuboCops own documentation structure,
66
+ # overwrite this method to accommodate your custom layout.
64
67
  # @return [String, nil]
65
68
  #
66
69
  # @api public
67
- def self.documentation_url
68
- Documentation.url_for(self) if builtin?
70
+ def self.documentation_url(config = nil)
71
+ Documentation.url_for(self, config)
69
72
  end
70
73
 
71
74
  def self.inherited(subclass)
72
75
  super
76
+ subclass.instance_variable_set(:@gem_requirements, gem_requirements.dup)
73
77
  Registry.global.enlist(subclass)
74
78
  end
75
79
 
@@ -126,6 +130,29 @@ module RuboCop
126
130
  false
127
131
  end
128
132
 
133
+ ## Gem requirements
134
+
135
+ @gem_requirements = {}
136
+
137
+ class << self
138
+ attr_reader :gem_requirements
139
+
140
+ # Register a version requirement for the given gem name.
141
+ # This cop will be skipped unless the target satisfies *all* requirements.
142
+ # @param [String] gem_name
143
+ # @param [Array<String>] version_requirements The version requirements,
144
+ # using the same syntax as a Gemfile, e.g. ">= 1.2.3"
145
+ #
146
+ # If omitted, any version of the gem will be accepted.
147
+ #
148
+ # https://guides.rubygems.org/patterns/#declaring-dependencies
149
+ #
150
+ # @api public
151
+ def requires_gem(gem_name, *version_requirements)
152
+ @gem_requirements[gem_name] = Gem::Requirement.new(version_requirements)
153
+ end
154
+ end
155
+
129
156
  def initialize(config = nil, options = nil)
130
157
  @config = config || Config.new
131
158
  @options = options || { debug: false }
@@ -162,7 +189,9 @@ module RuboCop
162
189
  def add_global_offense(message = nil, severity: nil)
163
190
  severity = find_severity(nil, severity)
164
191
  message = find_message(nil, message)
165
- current_offenses << Offense.new(severity, Offense::NO_LOCATION, message, name, :unsupported)
192
+ range = Offense::NO_LOCATION
193
+ status = enabled_line?(range.line) ? :unsupported : :disabled
194
+ current_offenses << Offense.new(severity, range, message, name, status)
166
195
  end
167
196
 
168
197
  # Adds an offense on the specified range (or node with an expression)
@@ -244,7 +273,12 @@ module RuboCop
244
273
  @config.active_support_extensions_enabled?
245
274
  end
246
275
 
276
+ def string_literals_frozen_by_default?
277
+ @config.string_literals_frozen_by_default?
278
+ end
279
+
247
280
  def relevant_file?(file)
281
+ return false unless target_satisfies_all_gem_version_requirements?
248
282
  return true unless @config.clusivity_config_for_badge?(self.class.badge)
249
283
 
250
284
  file == RuboCop::AST::ProcessedSource::STRING_SOURCE_NAME ||
@@ -288,8 +322,12 @@ module RuboCop
288
322
  # @api private
289
323
  def self.callbacks_needed
290
324
  @callbacks_needed ||= public_instance_methods.select do |m|
291
- m.start_with?(/on_|after_/) &&
292
- !Base.method_defined?(m) # exclude standard "callbacks" like 'on_begin_investigation'
325
+ # OPTIMIZE: Check method existence first to make fewer `start_with?` calls.
326
+ # At the time of writing this comment, this excludes 98 of ~104 methods.
327
+ # `start_with?` with two string arguments instead of a regex is faster
328
+ # in this specific case as well.
329
+ !Base.method_defined?(m) && # exclude standard "callbacks" like 'on_begin_investigation'
330
+ m.start_with?('on_', 'after_')
293
331
  end
294
332
  end
295
333
  # rubocop:enable Layout/ClassStructure
@@ -371,16 +409,6 @@ module RuboCop
371
409
 
372
410
  ### Actually private methods
373
411
 
374
- # rubocop:disable Layout/ClassStructure
375
- def self.builtin?
376
- return false unless (m = instance_methods(false).first) # any custom method will do
377
-
378
- path, _line = instance_method(m).source_location
379
- path.start_with?(__dir__)
380
- end
381
- private_class_method :builtin?
382
- # rubocop:enable Layout/ClassStructure
383
-
384
412
  def reset_investigation
385
413
  @currently_disabled_lines = @current_offenses = @processed_source = @current_corrector = nil
386
414
  end
@@ -496,6 +524,18 @@ module RuboCop
496
524
  range.end_pos + @current_offset
497
525
  )
498
526
  end
527
+
528
+ def target_satisfies_all_gem_version_requirements?
529
+ self.class.gem_requirements.all? do |gem_name, version_req|
530
+ all_gem_versions_in_target = @config.gem_versions_in_target
531
+ next false unless all_gem_versions_in_target
532
+
533
+ gem_version_in_target = all_gem_versions_in_target[gem_name]
534
+ next false unless gem_version_in_target
535
+
536
+ version_req.satisfied_by?(gem_version_in_target)
537
+ end
538
+ end
499
539
  end
500
540
  end
501
541
  end
@@ -56,6 +56,7 @@ module RuboCop
56
56
 
57
57
  REQUIRED_MSG = 'Gem version specification is required.'
58
58
  FORBIDDEN_MSG = 'Gem version specification is forbidden.'
59
+ RESTRICT_ON_SEND = %i[gem].freeze
59
60
  VERSION_SPECIFICATION_REGEX = /^\s*[~<>=]*\s*[0-9.]+/.freeze
60
61
 
61
62
  # @!method includes_version_specification?(node)
@@ -90,13 +91,11 @@ module RuboCop
90
91
  Array(cop_config['AllowedGems'])
91
92
  end
92
93
 
93
- def message(range)
94
- gem_specification = range.source
95
-
94
+ def message(_range)
96
95
  if required_style?
97
- format(REQUIRED_MSG, gem_specification: gem_specification)
96
+ REQUIRED_MSG
98
97
  elsif forbidden_style?
99
- format(FORBIDDEN_MSG, gem_specification: gem_specification)
98
+ FORBIDDEN_MSG
100
99
  end
101
100
  end
102
101
 
@@ -22,6 +22,14 @@ module RuboCop
22
22
  end
23
23
  end
24
24
 
25
+ def self.inherited(_subclass)
26
+ super
27
+ warn Rainbow(<<~WARNING).yellow, uplevel: 1
28
+ Inheriting from `RuboCop::Cop::Cop` is deprecated. Use `RuboCop::Cop::Base` instead.
29
+ For more information, see https://docs.rubocop.org/rubocop/v1_upgrade_notes.html.
30
+ WARNING
31
+ end
32
+
25
33
  def self.support_autocorrect?
26
34
  method_defined?(:autocorrect)
27
35
  end
@@ -37,16 +45,28 @@ module RuboCop
37
45
 
38
46
  # @deprecated Use Registry.global
39
47
  def self.registry
48
+ warn Rainbow(<<~WARNING).yellow, uplevel: 1
49
+ `Cop.registry` is deprecated. Use `Registry.global` instead.
50
+ WARNING
51
+
40
52
  Registry.global
41
53
  end
42
54
 
43
55
  # @deprecated Use Registry.all
44
56
  def self.all
57
+ warn Rainbow(<<~WARNING).yellow, uplevel: 1
58
+ `Cop.all` is deprecated. Use `Registry.all` instead.
59
+ WARNING
60
+
45
61
  Registry.all
46
62
  end
47
63
 
48
64
  # @deprecated Use Registry.qualified_cop_name
49
65
  def self.qualified_cop_name(name, origin)
66
+ warn Rainbow(<<~WARNING).yellow, uplevel: 1
67
+ `Cop.qualified_cop_name` is deprecated. Use `Registry.qualified_cop_name` instead.
68
+ WARNING
69
+
50
70
  Registry.qualified_cop_name(name, origin)
51
71
  end
52
72
 
@@ -58,7 +78,7 @@ module RuboCop
58
78
  # template file, we convert it to location info in the original file.
59
79
  range = range_for_original(range)
60
80
 
61
- if block.nil? && !support_autocorrect?
81
+ if block.nil? && !self.class.support_autocorrect?
62
82
  super(range, message: message, severity: severity)
63
83
  else
64
84
  super(range, message: message, severity: severity) do |corrector|
@@ -74,13 +94,19 @@ module RuboCop
74
94
 
75
95
  # @deprecated Use class method
76
96
  def support_autocorrect?
77
- # warn 'deprecated, use cop.class.support_autocorrect?' TODO
97
+ warn Rainbow(<<~WARNING).yellow, uplevel: 1
98
+ `support_autocorrect?` is deprecated. Use `cop.class.support_autocorrect?`.
99
+ WARNING
100
+
78
101
  self.class.support_autocorrect?
79
102
  end
80
103
 
81
104
  # @deprecated
82
105
  def corrections
83
- # warn 'Cop#corrections is deprecated' TODO
106
+ warn Rainbow(<<~WARNING).yellow, uplevel: 1
107
+ `Cop#corrections` is deprecated.
108
+ WARNING
109
+
84
110
  return [] unless @last_corrector
85
111
 
86
112
  Legacy::CorrectionsProxy.new(@last_corrector)
@@ -136,7 +162,7 @@ module RuboCop
136
162
  end
137
163
 
138
164
  def correction_lambda
139
- return unless support_autocorrect?
165
+ return unless self.class.support_autocorrect?
140
166
 
141
167
  dedupe_on_node(@v0_argument) { autocorrect(@v0_argument) }
142
168
  end
@@ -51,6 +51,8 @@ module RuboCop
51
51
  def semicolon(node)
52
52
  @semicolon ||= {}.compare_by_identity
53
53
  @semicolon[node] ||= processed_source.sorted_tokens.select(&:semicolon?).find do |token|
54
+ next if token.pos.end_pos <= node.source_range.begin_pos
55
+
54
56
  same_line?(token, node.body) && trailing_class_definition?(token, node.body)
55
57
  end
56
58
  end
@@ -22,7 +22,7 @@ module RuboCop
22
22
  private
23
23
 
24
24
  def ternary_condition?(node)
25
- node.parent&.if_type? && node.parent&.ternary?
25
+ node.parent&.if_type? && node.parent.ternary?
26
26
  end
27
27
 
28
28
  def next_char_is_question_mark?(node)
@@ -16,24 +16,51 @@ module RuboCop
16
16
  base = department_to_basename(cop_class.department)
17
17
  fragment = cop_class.cop_name.downcase.gsub(/[^a-z]/, '')
18
18
  base_url = base_url_for(cop_class, config)
19
+ extension = extension_for(cop_class, config)
19
20
 
20
- "#{base_url}/#{base}.html##{fragment}"
21
+ "#{base_url}/#{base}#{extension}##{fragment}" if base_url
21
22
  end
22
23
 
23
24
  # @api private
24
25
  def base_url_for(cop_class, config)
25
- return default_base_url unless config
26
+ if config
27
+ department_name = cop_class.department.to_s
28
+ url = config.for_department(department_name)['DocumentationBaseURL']
29
+ return url if url
30
+ end
26
31
 
27
- department_name = cop_class.department.to_s
32
+ default_base_url if builtin?(cop_class)
33
+ end
34
+
35
+ # @api private
36
+ def extension_for(cop_class, config)
37
+ if config
38
+ department_name = cop_class.department
39
+ extension = config.for_department(department_name)['DocumentationExtension']
40
+ return extension if extension
41
+ end
28
42
 
29
- config.for_department(department_name)['DocumentationBaseURL'] ||
30
- config.for_all_cops['DocumentationBaseURL']
43
+ default_extension
31
44
  end
32
45
 
33
46
  # @api private
34
47
  def default_base_url
35
48
  'https://docs.rubocop.org/rubocop'
36
49
  end
50
+
51
+ # @api private
52
+ def default_extension
53
+ '.html'
54
+ end
55
+
56
+ # @api private
57
+ def builtin?(cop_class)
58
+ # any custom method will do
59
+ return false unless (m = cop_class.instance_methods(false).first)
60
+
61
+ path, _line = cop_class.instance_method(m).source_location
62
+ path.start_with?(__dir__)
63
+ end
37
64
  end
38
65
  end
39
66
  end
@@ -4,6 +4,16 @@ module RuboCop
4
4
  module Cop
5
5
  # A scaffold for concrete forces.
6
6
  class Force
7
+ # @api private
8
+ class HookError < StandardError
9
+ attr_reader :joining_cop
10
+
11
+ def initialize(joining_cop)
12
+ super
13
+ @joining_cop = joining_cop
14
+ end
15
+ end
16
+
7
17
  attr_reader :cops
8
18
 
9
19
  def self.all
@@ -32,6 +42,8 @@ module RuboCop
32
42
  next unless cop.respond_to?(method_name)
33
43
 
34
44
  cop.public_send(method_name, *args)
45
+ rescue StandardError
46
+ raise HookError, cop
35
47
  end
36
48
  end
37
49
 
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Gemspec
6
+ # Prefer `add_dependency` over `add_runtime_dependency` as the latter is
7
+ # considered soft-deprecated.
8
+ #
9
+ # @example
10
+ #
11
+ # # bad
12
+ # Gem::Specification.new do |spec|
13
+ # spec.add_runtime_dependency('rubocop')
14
+ # end
15
+ #
16
+ # # good
17
+ # Gem::Specification.new do |spec|
18
+ # spec.add_dependency('rubocop')
19
+ # end
20
+ #
21
+ class AddRuntimeDependency < Base
22
+ extend AutoCorrector
23
+
24
+ MSG = 'Use `add_dependency` instead of `add_runtime_dependency`.'
25
+
26
+ RESTRICT_ON_SEND = %i[add_runtime_dependency].freeze
27
+
28
+ def on_send(node)
29
+ return if !node.receiver || node.arguments.empty?
30
+
31
+ add_offense(node.loc.selector) do |corrector|
32
+ corrector.replace(node.loc.selector, 'add_dependency')
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -101,13 +101,11 @@ module RuboCop
101
101
  Array(cop_config['AllowedGems'])
102
102
  end
103
103
 
104
- def message(range)
105
- gem_specification = range.source
106
-
104
+ def message(_range)
107
105
  if required_style?
108
- format(REQUIRED_MSG, gem_specification: gem_specification)
106
+ REQUIRED_MSG
109
107
  elsif forbidden_style?
110
- format(FORBIDDEN_MSG, gem_specification: gem_specification)
108
+ FORBIDDEN_MSG
111
109
  end
112
110
  end
113
111
 
@@ -31,8 +31,8 @@ module RuboCop
31
31
  #
32
32
  # # good
33
33
  # Gem::Specification.new do |spec|
34
- # spec.add_runtime_dependency('parallel', '~> 1.10')
35
- # spec.add_runtime_dependency('parser', '>= 2.3.3.1', '< 3.0')
34
+ # spec.add_dependency('parallel', '~> 1.10')
35
+ # spec.add_dependency('parser', '>= 2.3.3.1', '< 3.0')
36
36
  # end
37
37
  class DuplicatedAssignment < Base
38
38
  include RangeHelp
@@ -14,15 +14,15 @@ module RuboCop
14
14
  # # bad
15
15
  # Gem::Specification.new do |spec|
16
16
  # if RUBY_VERSION >= '3.0'
17
- # spec.add_runtime_dependency 'gem_a'
17
+ # spec.add_dependency 'gem_a'
18
18
  # else
19
- # spec.add_runtime_dependency 'gem_b'
19
+ # spec.add_dependency 'gem_b'
20
20
  # end
21
21
  # end
22
22
  #
23
23
  # # good
24
24
  # Gem::Specification.new do |spec|
25
- # spec.add_runtime_dependency 'gem_a'
25
+ # spec.add_dependency 'gem_a'
26
26
  # end
27
27
  #
28
28
  class RubyVersionGlobalsUsage < Base
@@ -112,10 +112,6 @@ module RuboCop
112
112
  body = comment_body(comment_line)
113
113
  node.source.index(body)
114
114
  end
115
-
116
- def relevant_file?(file)
117
- file.match?(%r{/cop/.*\.rb\z})
118
- end
119
115
  end
120
116
  end
121
117
  end
@@ -38,7 +38,8 @@ module RuboCop
38
38
  CORRECTION_EXPECTATION_METHODS = %i[expect_correction expect_no_corrections].freeze
39
39
 
40
40
  def on_send(node)
41
- return unless (next_sibling = node.right_sibling) && next_sibling.send_type?
41
+ return unless (next_sibling = node.right_sibling)
42
+ return unless next_sibling.respond_to?(:send_type?) && next_sibling.send_type?
42
43
 
43
44
  method_name = next_sibling.method_name
44
45
  return unless CORRECTION_EXPECTATION_METHODS.include?(method_name)