rubocop 1.71.1 → 1.92.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 (705) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +23 -19
  4. data/config/default.yml +885 -168
  5. data/config/internal_affairs.yml +20 -0
  6. data/config/obsoletion.yml +47 -4
  7. data/exe/rubocop +2 -9
  8. data/lib/rubocop/cache_config.rb +29 -0
  9. data/lib/rubocop/cached_data.rb +50 -5
  10. data/lib/rubocop/changed_files.rb +134 -0
  11. data/lib/rubocop/cli/command/auto_generate_config.rb +36 -4
  12. data/lib/rubocop/cli/command/cop_names.rb +57 -0
  13. data/lib/rubocop/cli/command/execute_runner.rb +54 -3
  14. data/lib/rubocop/cli/command/explain.rb +34 -0
  15. data/lib/rubocop/cli/command/list_enabled_cops_for.rb +40 -0
  16. data/lib/rubocop/cli/command/lsp.rb +1 -1
  17. data/lib/rubocop/cli/command/mcp.rb +19 -0
  18. data/lib/rubocop/cli/command/show_cops.rb +8 -2
  19. data/lib/rubocop/cli/command/show_docs_url.rb +8 -10
  20. data/lib/rubocop/cli/command/suggest_extensions.rb +8 -2
  21. data/lib/rubocop/cli.rb +59 -17
  22. data/lib/rubocop/comment_config/directive_range.rb +34 -0
  23. data/lib/rubocop/comment_config/disable_next.rb +143 -0
  24. data/lib/rubocop/comment_config/push_pop.rb +55 -0
  25. data/lib/rubocop/comment_config.rb +98 -45
  26. data/lib/rubocop/config.rb +137 -19
  27. data/lib/rubocop/config_finder.rb +1 -1
  28. data/lib/rubocop/config_loader.rb +75 -49
  29. data/lib/rubocop/config_loader_resolver.rb +124 -33
  30. data/lib/rubocop/config_obsoletion/cop_rule.rb +2 -1
  31. data/lib/rubocop/config_obsoletion/extracted_cop.rb +6 -3
  32. data/lib/rubocop/config_obsoletion/removed_cop.rb +9 -1
  33. data/lib/rubocop/config_obsoletion/renamed_cop.rb +18 -3
  34. data/lib/rubocop/config_obsoletion.rb +47 -3
  35. data/lib/rubocop/config_store.rb +7 -2
  36. data/lib/rubocop/config_validator.rb +75 -12
  37. data/lib/rubocop/cop/autocorrect_logic.rb +43 -17
  38. data/lib/rubocop/cop/badge.rb +9 -1
  39. data/lib/rubocop/cop/base.rb +128 -35
  40. data/lib/rubocop/cop/bundler/gem_comment.rb +5 -4
  41. data/lib/rubocop/cop/bundler/gem_version.rb +28 -28
  42. data/lib/rubocop/cop/bundler/ordered_gems.rb +2 -11
  43. data/lib/rubocop/cop/bundler.rb +19 -0
  44. data/lib/rubocop/cop/commissioner.rb +13 -11
  45. data/lib/rubocop/cop/cop_documentation.rb +81 -0
  46. data/lib/rubocop/cop/cop_explanation.rb +140 -0
  47. data/lib/rubocop/cop/corrector.rb +4 -4
  48. data/lib/rubocop/cop/correctors/alignment_corrector.rb +92 -11
  49. data/lib/rubocop/cop/correctors/condition_corrector.rb +1 -1
  50. data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +22 -7
  51. data/lib/rubocop/cop/correctors/empty_line_corrector.rb +1 -1
  52. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +7 -2
  53. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +29 -2
  54. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +1 -5
  55. data/lib/rubocop/cop/correctors/ordered_gem_corrector.rb +8 -1
  56. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +73 -2
  57. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
  58. data/lib/rubocop/cop/correctors.rb +28 -0
  59. data/lib/rubocop/cop/documentation.rb +3 -4
  60. data/lib/rubocop/cop/exclude_limit.rb +31 -5
  61. data/lib/rubocop/cop/gemspec/attribute_assignment.rb +91 -0
  62. data/lib/rubocop/cop/gemspec/dependency_version.rb +0 -6
  63. data/lib/rubocop/cop/gemspec/development_dependencies.rb +1 -1
  64. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +49 -17
  65. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +2 -9
  66. data/lib/rubocop/cop/gemspec/require_mfa.rb +22 -5
  67. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +12 -7
  68. data/lib/rubocop/cop/gemspec.rb +22 -0
  69. data/lib/rubocop/cop/generator/registration_injector.rb +188 -0
  70. data/lib/rubocop/cop/generator.rb +5 -1
  71. data/lib/rubocop/cop/internal_affairs/example_description.rb +9 -5
  72. data/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb +8 -8
  73. data/lib/rubocop/cop/internal_affairs/itblock_handler.rb +69 -0
  74. data/lib/rubocop/cop/internal_affairs/location_exists.rb +142 -0
  75. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +1 -0
  76. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +16 -20
  77. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_processor.rb +1 -1
  78. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb +1 -2
  79. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +6 -2
  80. data/lib/rubocop/cop/internal_affairs/node_type_group.rb +85 -0
  81. data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +2 -2
  82. data/lib/rubocop/cop/internal_affairs/plugin.rb +33 -0
  83. data/lib/rubocop/cop/internal_affairs/redundant_context_config_parameter.rb +1 -7
  84. data/lib/rubocop/cop/internal_affairs/redundant_described_class_as_subject.rb +6 -5
  85. data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +5 -3
  86. data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +1 -7
  87. data/lib/rubocop/cop/internal_affairs/undefined_config.rb +13 -2
  88. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +4 -4
  89. data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +1 -1
  90. data/lib/rubocop/cop/internal_affairs.rb +3 -16
  91. data/lib/rubocop/cop/layout/argument_alignment.rb +4 -2
  92. data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
  93. data/lib/rubocop/cop/layout/begin_end_alignment.rb +1 -1
  94. data/lib/rubocop/cop/layout/block_alignment.rb +86 -4
  95. data/lib/rubocop/cop/layout/block_end_newline.rb +1 -0
  96. data/lib/rubocop/cop/layout/case_indentation.rb +28 -10
  97. data/lib/rubocop/cop/layout/class_structure.rb +172 -36
  98. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +5 -5
  99. data/lib/rubocop/cop/layout/comment_indentation.rb +17 -7
  100. data/lib/rubocop/cop/layout/condition_position.rb +13 -3
  101. data/lib/rubocop/cop/layout/def_end_alignment.rb +5 -1
  102. data/lib/rubocop/cop/layout/dot_position.rb +2 -2
  103. data/lib/rubocop/cop/layout/else_alignment.rb +3 -15
  104. data/lib/rubocop/cop/layout/empty_comment.rb +8 -10
  105. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +27 -9
  106. data/lib/rubocop/cop/layout/empty_line_after_magic_comment.rb +63 -5
  107. data/lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb +14 -5
  108. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +46 -21
  109. data/lib/rubocop/cop/layout/empty_lines.rb +5 -2
  110. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +101 -0
  111. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +34 -4
  112. data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +8 -29
  113. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +2 -0
  114. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +14 -3
  115. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +17 -3
  116. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +23 -1
  117. data/lib/rubocop/cop/layout/empty_lines_around_method_body.rb +22 -2
  118. data/lib/rubocop/cop/layout/empty_lines_around_module_body.rb +16 -2
  119. data/lib/rubocop/cop/layout/end_alignment.rb +20 -8
  120. data/lib/rubocop/cop/layout/extra_spacing.rb +109 -3
  121. data/lib/rubocop/cop/layout/first_argument_indentation.rb +57 -3
  122. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +18 -5
  123. data/lib/rubocop/cop/layout/first_array_element_line_break.rb +26 -0
  124. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +25 -18
  125. data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +25 -25
  126. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +6 -0
  127. data/lib/rubocop/cop/layout/first_method_parameter_line_break.rb +4 -0
  128. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +11 -0
  129. data/lib/rubocop/cop/layout/hash_alignment.rb +20 -18
  130. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +5 -3
  131. data/lib/rubocop/cop/layout/heredoc_indentation.rb +48 -3
  132. data/lib/rubocop/cop/layout/indentation_style.rb +5 -2
  133. data/lib/rubocop/cop/layout/indentation_width.rb +159 -12
  134. data/lib/rubocop/cop/layout/leading_comment_space.rb +40 -1
  135. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +10 -4
  136. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +25 -9
  137. data/lib/rubocop/cop/layout/line_length.rb +201 -30
  138. data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +69 -57
  139. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +8 -2
  140. data/lib/rubocop/cop/layout/multiline_block_layout.rb +11 -1
  141. data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +63 -56
  142. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +7 -1
  143. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +259 -40
  144. data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +4 -0
  145. data/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb +1 -0
  146. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +9 -5
  147. data/lib/rubocop/cop/layout/parameter_alignment.rb +1 -1
  148. data/lib/rubocop/cop/layout/redundant_line_break.rb +111 -7
  149. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +29 -9
  150. data/lib/rubocop/cop/layout/single_line_block_chain.rb +10 -2
  151. data/lib/rubocop/cop/layout/space_after_comma.rb +2 -10
  152. data/lib/rubocop/cop/layout/space_after_not.rb +5 -0
  153. data/lib/rubocop/cop/layout/space_after_semicolon.rb +11 -1
  154. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +10 -4
  155. data/lib/rubocop/cop/layout/space_around_keyword.rb +20 -3
  156. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +1 -1
  157. data/lib/rubocop/cop/layout/space_around_operators.rb +30 -4
  158. data/lib/rubocop/cop/layout/space_before_block_braces.rb +5 -1
  159. data/lib/rubocop/cop/layout/space_before_brackets.rb +6 -39
  160. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +9 -8
  161. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +55 -36
  162. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +32 -8
  163. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +3 -0
  164. data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
  165. data/lib/rubocop/cop/layout.rb +114 -0
  166. data/lib/rubocop/cop/lazy_loader.rb +41 -0
  167. data/lib/rubocop/cop/lint/ambiguous_assignment.rb +9 -17
  168. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +72 -1
  169. data/lib/rubocop/cop/lint/ambiguous_operator.rb +1 -1
  170. data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +3 -11
  171. data/lib/rubocop/cop/lint/ambiguous_range.rb +6 -2
  172. data/lib/rubocop/cop/lint/argument_mismatch.rb +99 -0
  173. data/lib/rubocop/cop/lint/array_literal_in_regexp.rb +2 -3
  174. data/lib/rubocop/cop/lint/assignment_in_condition.rb +15 -1
  175. data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -1
  176. data/lib/rubocop/cop/lint/circular_argument_reference.rb +47 -8
  177. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +4 -3
  178. data/lib/rubocop/cop/lint/constant_reassignment.rb +94 -15
  179. data/lib/rubocop/cop/lint/constant_resolution.rb +31 -6
  180. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +240 -0
  181. data/lib/rubocop/cop/lint/data_define_override.rb +63 -0
  182. data/lib/rubocop/cop/lint/debugger.rb +13 -4
  183. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +1 -1
  184. data/lib/rubocop/cop/lint/deprecated_constants.rb +2 -8
  185. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +5 -2
  186. data/lib/rubocop/cop/lint/deprecated_reference.rb +194 -0
  187. data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +4 -4
  188. data/lib/rubocop/cop/lint/duplicate_methods.rb +506 -41
  189. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +5 -42
  190. data/lib/rubocop/cop/lint/else_layout.rb +19 -0
  191. data/lib/rubocop/cop/lint/empty_block.rb +4 -4
  192. data/lib/rubocop/cop/lint/empty_conditional_body.rb +22 -65
  193. data/lib/rubocop/cop/lint/empty_expression.rb +0 -2
  194. data/lib/rubocop/cop/lint/empty_in_pattern.rb +8 -1
  195. data/lib/rubocop/cop/lint/empty_interpolation.rb +14 -1
  196. data/lib/rubocop/cop/lint/empty_when.rb +8 -1
  197. data/lib/rubocop/cop/lint/ensure_return.rb +19 -1
  198. data/lib/rubocop/cop/lint/erb_new_arguments.rb +4 -8
  199. data/lib/rubocop/cop/lint/float_comparison.rb +33 -10
  200. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +1 -1
  201. data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
  202. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +1 -2
  203. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +5 -1
  204. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +1 -2
  205. data/lib/rubocop/cop/lint/inherit_exception.rb +53 -7
  206. data/lib/rubocop/cop/lint/interpolation_check.rb +25 -6
  207. data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +1 -1
  208. data/lib/rubocop/cop/lint/literal_as_condition.rb +152 -11
  209. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +11 -1
  210. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +75 -37
  211. data/lib/rubocop/cop/lint/loop.rb +24 -2
  212. data/lib/rubocop/cop/lint/misplaced_magic_comment.rb +245 -0
  213. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +60 -23
  214. data/lib/rubocop/cop/lint/missing_super.rb +31 -1
  215. data/lib/rubocop/cop/lint/mixed_case_range.rb +2 -2
  216. data/lib/rubocop/cop/lint/multiple_comparison.rb +2 -2
  217. data/lib/rubocop/cop/lint/name_typo.rb +270 -0
  218. data/lib/rubocop/cop/lint/nested_method_definition.rb +2 -2
  219. data/lib/rubocop/cop/lint/next_without_accumulator.rb +2 -0
  220. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +20 -0
  221. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +4 -2
  222. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +4 -4
  223. data/lib/rubocop/cop/lint/number_conversion.rb +20 -11
  224. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -1
  225. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +11 -0
  226. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +1 -1
  227. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +7 -7
  228. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +15 -4
  229. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +20 -14
  230. data/lib/rubocop/cop/lint/raise_exception.rb +30 -11
  231. data/lib/rubocop/cop/lint/rand_one.rb +1 -1
  232. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +160 -39
  233. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +53 -19
  234. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +28 -6
  235. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
  236. data/lib/rubocop/cop/lint/redundant_require_statement.rb +5 -25
  237. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +134 -12
  238. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +13 -4
  239. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +267 -0
  240. data/lib/rubocop/cop/lint/redundant_with_index.rb +5 -3
  241. data/lib/rubocop/cop/lint/redundant_with_object.rb +8 -0
  242. data/lib/rubocop/cop/lint/refinement_import_methods.rb +8 -1
  243. data/lib/rubocop/cop/lint/regexp_as_condition.rb +9 -1
  244. data/lib/rubocop/cop/lint/require_parentheses.rb +13 -4
  245. data/lib/rubocop/cop/lint/require_range_parentheses.rb +3 -2
  246. data/lib/rubocop/cop/lint/require_relative_self_path.rb +7 -5
  247. data/lib/rubocop/cop/lint/rescue_exception.rb +1 -4
  248. data/lib/rubocop/cop/lint/rescue_type.rb +2 -2
  249. data/lib/rubocop/cop/lint/return_in_void_context.rb +9 -11
  250. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +28 -4
  251. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +9 -5
  252. data/lib/rubocop/cop/lint/safe_navigation_with_empty.rb +1 -1
  253. data/lib/rubocop/cop/lint/script_permission.rb +5 -1
  254. data/lib/rubocop/cop/lint/self_assignment.rb +63 -6
  255. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +1 -1
  256. data/lib/rubocop/cop/lint/shadowed_argument.rb +7 -7
  257. data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -1
  258. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +27 -1
  259. data/lib/rubocop/cop/lint/shared_mutable_default.rb +14 -1
  260. data/lib/rubocop/cop/lint/struct_new_override.rb +17 -1
  261. data/lib/rubocop/cop/lint/super_argument_mismatch.rb +179 -0
  262. data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
  263. data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +122 -0
  264. data/lib/rubocop/cop/lint/symbol_conversion.rb +21 -4
  265. data/lib/rubocop/cop/lint/syntax.rb +25 -1
  266. data/lib/rubocop/cop/lint/to_enum_arguments.rb +73 -5
  267. data/lib/rubocop/cop/lint/to_json.rb +20 -17
  268. data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +1 -1
  269. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +5 -1
  270. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +54 -11
  271. data/lib/rubocop/cop/lint/unexpected_block_arity.rb +2 -0
  272. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +2 -1
  273. data/lib/rubocop/cop/lint/unreachable_code.rb +16 -9
  274. data/lib/rubocop/cop/lint/unreachable_loop.rb +5 -5
  275. data/lib/rubocop/cop/lint/unreachable_pattern_branch.rb +113 -0
  276. data/lib/rubocop/cop/lint/unused_method_argument.rb +10 -0
  277. data/lib/rubocop/cop/lint/unused_private_method.rb +234 -0
  278. data/lib/rubocop/cop/lint/uri_escape_unescape.rb +2 -0
  279. data/lib/rubocop/cop/lint/useless_access_modifier.rb +31 -6
  280. data/lib/rubocop/cop/lint/useless_assignment.rb +63 -27
  281. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +107 -0
  282. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +92 -0
  283. data/lib/rubocop/cop/lint/useless_method_definition.rb +1 -1
  284. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +11 -10
  285. data/lib/rubocop/cop/lint/useless_or.rb +111 -0
  286. data/lib/rubocop/cop/lint/useless_rescue.rb +2 -3
  287. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +13 -7
  288. data/lib/rubocop/cop/lint/useless_setter_call.rb +4 -1
  289. data/lib/rubocop/cop/lint/useless_times.rb +22 -1
  290. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +201 -0
  291. data/lib/rubocop/cop/lint/void.rb +68 -22
  292. data/lib/rubocop/cop/lint.rb +179 -0
  293. data/lib/rubocop/cop/message_annotator.rb +7 -3
  294. data/lib/rubocop/cop/metrics/abc_size.rb +1 -1
  295. data/lib/rubocop/cop/metrics/block_length.rb +2 -1
  296. data/lib/rubocop/cop/metrics/block_nesting.rb +23 -0
  297. data/lib/rubocop/cop/metrics/class_length.rb +4 -2
  298. data/lib/rubocop/cop/metrics/collection_literal_length.rb +1 -1
  299. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +0 -4
  300. data/lib/rubocop/cop/metrics/method_length.rb +3 -2
  301. data/lib/rubocop/cop/metrics/module_length.rb +4 -2
  302. data/lib/rubocop/cop/metrics/perceived_complexity.rb +37 -7
  303. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +4 -3
  304. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +2 -2
  305. data/lib/rubocop/cop/metrics/utils/iterating_block.rb +1 -1
  306. data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +7 -7
  307. data/lib/rubocop/cop/metrics.rb +31 -0
  308. data/lib/rubocop/cop/migration/department_name.rb +11 -5
  309. data/lib/rubocop/cop/migration.rb +13 -0
  310. data/lib/rubocop/cop/mixin/alignment.rb +7 -3
  311. data/lib/rubocop/cop/mixin/allowed_methods.rb +5 -5
  312. data/lib/rubocop/cop/mixin/allowed_pattern.rb +9 -5
  313. data/lib/rubocop/cop/mixin/annotation_comment.rb +1 -1
  314. data/lib/rubocop/cop/mixin/check_line_breakable.rb +44 -23
  315. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +10 -8
  316. data/lib/rubocop/cop/mixin/code_length.rb +10 -2
  317. data/lib/rubocop/cop/mixin/comments_help.rb +2 -3
  318. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +1 -2
  319. data/lib/rubocop/cop/mixin/configurable_max.rb +6 -5
  320. data/lib/rubocop/cop/mixin/def_node.rb +1 -1
  321. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +13 -2
  322. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -7
  323. data/lib/rubocop/cop/mixin/endless_method_body_help.rb +69 -0
  324. data/lib/rubocop/cop/mixin/endless_method_rewriter.rb +13 -5
  325. data/lib/rubocop/cop/mixin/forbidden_identifiers.rb +20 -0
  326. data/lib/rubocop/cop/mixin/forbidden_pattern.rb +20 -0
  327. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -2
  328. data/lib/rubocop/cop/mixin/gemspec_help.rb +22 -0
  329. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +59 -17
  330. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +52 -25
  331. data/lib/rubocop/cop/mixin/hash_subset.rb +28 -6
  332. data/lib/rubocop/cop/mixin/hash_transform_method/autocorrection.rb +63 -0
  333. data/lib/rubocop/cop/mixin/hash_transform_method.rb +32 -78
  334. data/lib/rubocop/cop/mixin/indexed_method_arity.rb +94 -0
  335. data/lib/rubocop/cop/mixin/line_length_help.rb +52 -11
  336. data/lib/rubocop/cop/mixin/method_complexity.rb +2 -1
  337. data/lib/rubocop/cop/mixin/min_branches_count.rb +3 -1
  338. data/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb +11 -1
  339. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +3 -1
  340. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +10 -1
  341. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +2 -2
  342. data/lib/rubocop/cop/mixin/pattern_matching_help.rb +35 -0
  343. data/lib/rubocop/cop/mixin/percent_literal.rb +1 -1
  344. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +79 -8
  345. data/lib/rubocop/cop/mixin/project_index_help.rb +203 -0
  346. data/lib/rubocop/cop/mixin/range_help.rb +16 -5
  347. data/lib/rubocop/cop/mixin/reparsed_equivalence.rb +181 -0
  348. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +5 -4
  349. data/lib/rubocop/cop/mixin/statement_modifier.rb +66 -10
  350. data/lib/rubocop/cop/mixin/string_help.rb +1 -1
  351. data/lib/rubocop/cop/mixin/target_ruby_version.rb +1 -1
  352. data/lib/rubocop/cop/mixin/trailing_comma.rb +23 -6
  353. data/lib/rubocop/cop/mixin.rb +93 -0
  354. data/lib/rubocop/cop/naming/accessor_method_name.rb +8 -0
  355. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +17 -1
  356. data/lib/rubocop/cop/naming/block_forwarding.rb +7 -4
  357. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  358. data/lib/rubocop/cop/naming/file_name.rb +6 -5
  359. data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +9 -2
  360. data/lib/rubocop/cop/naming/heredoc_delimiter_naming.rb +5 -3
  361. data/lib/rubocop/cop/naming/inclusive_language.rb +8 -2
  362. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +13 -6
  363. data/lib/rubocop/cop/naming/method_name.rb +190 -15
  364. data/lib/rubocop/cop/naming/predicate_method.rb +330 -0
  365. data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +65 -8
  366. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +11 -6
  367. data/lib/rubocop/cop/naming/variable_name.rb +51 -6
  368. data/lib/rubocop/cop/naming/variable_number.rb +3 -1
  369. data/lib/rubocop/cop/naming.rb +31 -0
  370. data/lib/rubocop/cop/offense.rb +108 -9
  371. data/lib/rubocop/cop/registry.rb +234 -65
  372. data/lib/rubocop/cop/security/eval.rb +17 -3
  373. data/lib/rubocop/cop/security/io_methods.rb +2 -2
  374. data/lib/rubocop/cop/security/json_load.rb +45 -11
  375. data/lib/rubocop/cop/security/marshal_load.rb +1 -1
  376. data/lib/rubocop/cop/security/open.rb +1 -0
  377. data/lib/rubocop/cop/security.rb +19 -0
  378. data/lib/rubocop/cop/severity.rb +12 -0
  379. data/lib/rubocop/cop/style/access_modifier_declarations.rb +54 -13
  380. data/lib/rubocop/cop/style/accessor_grouping.rb +47 -10
  381. data/lib/rubocop/cop/style/alias.rb +33 -8
  382. data/lib/rubocop/cop/style/ambiguous_endless_method_definition.rb +4 -3
  383. data/lib/rubocop/cop/style/and_or.rb +6 -1
  384. data/lib/rubocop/cop/style/arguments_forwarding.rb +52 -37
  385. data/lib/rubocop/cop/style/array_first_last.rb +13 -3
  386. data/lib/rubocop/cop/style/array_intersect.rb +136 -38
  387. data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +50 -0
  388. data/lib/rubocop/cop/style/array_join.rb +4 -2
  389. data/lib/rubocop/cop/style/ascii_comments.rb +6 -3
  390. data/lib/rubocop/cop/style/attr.rb +8 -6
  391. data/lib/rubocop/cop/style/bare_percent_literals.rb +4 -3
  392. data/lib/rubocop/cop/style/begin_block.rb +3 -1
  393. data/lib/rubocop/cop/style/bitwise_predicate.rb +13 -5
  394. data/lib/rubocop/cop/style/block_delimiters.rb +71 -40
  395. data/lib/rubocop/cop/style/case_equality.rb +29 -15
  396. data/lib/rubocop/cop/style/case_like_if.rb +12 -11
  397. data/lib/rubocop/cop/style/character_literal.rb +2 -2
  398. data/lib/rubocop/cop/style/class_and_module_children.rb +179 -27
  399. data/lib/rubocop/cop/style/class_equality_comparison.rb +22 -14
  400. data/lib/rubocop/cop/style/class_methods_definitions.rb +19 -5
  401. data/lib/rubocop/cop/style/collection_compact.rb +37 -17
  402. data/lib/rubocop/cop/style/collection_methods.rb +1 -0
  403. data/lib/rubocop/cop/style/collection_querying.rb +167 -0
  404. data/lib/rubocop/cop/style/colon_method_call.rb +16 -7
  405. data/lib/rubocop/cop/style/combinable_loops.rb +88 -9
  406. data/lib/rubocop/cop/style/command_literal.rb +1 -1
  407. data/lib/rubocop/cop/style/commented_keyword.rb +12 -5
  408. data/lib/rubocop/cop/style/comparable_between.rb +78 -0
  409. data/lib/rubocop/cop/style/comparable_clamp.rb +12 -1
  410. data/lib/rubocop/cop/style/concat_array_literals.rb +53 -9
  411. data/lib/rubocop/cop/style/conditional_assignment.rb +58 -24
  412. data/lib/rubocop/cop/style/constant_visibility.rb +23 -12
  413. data/lib/rubocop/cop/style/copyright.rb +27 -12
  414. data/lib/rubocop/cop/style/data_inheritance.rb +25 -0
  415. data/lib/rubocop/cop/style/date_time.rb +4 -4
  416. data/lib/rubocop/cop/style/def_with_parentheses.rb +22 -5
  417. data/lib/rubocop/cop/style/dig_chain.rb +6 -1
  418. data/lib/rubocop/cop/style/dir_empty.rb +4 -0
  419. data/lib/rubocop/cop/style/directive_scope.rb +304 -0
  420. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +184 -20
  421. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +6 -1
  422. data/lib/rubocop/cop/style/documentation.rb +53 -6
  423. data/lib/rubocop/cop/style/documentation_method.rb +8 -12
  424. data/lib/rubocop/cop/style/double_negation.rb +14 -3
  425. data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
  426. data/lib/rubocop/cop/style/each_with_object.rb +4 -3
  427. data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
  428. data/lib/rubocop/cop/style/empty_case_condition.rb +13 -4
  429. data/lib/rubocop/cop/style/empty_class_definition.rb +126 -0
  430. data/lib/rubocop/cop/style/empty_heredoc.rb +4 -0
  431. data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
  432. data/lib/rubocop/cop/style/empty_literal.rb +12 -3
  433. data/lib/rubocop/cop/style/empty_method.rb +15 -9
  434. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +110 -0
  435. data/lib/rubocop/cop/style/encoding.rb +7 -1
  436. data/lib/rubocop/cop/style/end_block.rb +3 -1
  437. data/lib/rubocop/cop/style/endless_method.rb +252 -19
  438. data/lib/rubocop/cop/style/env_home.rb +4 -0
  439. data/lib/rubocop/cop/style/eval_with_location.rb +3 -3
  440. data/lib/rubocop/cop/style/even_odd.rb +11 -1
  441. data/lib/rubocop/cop/style/exact_regexp_match.rb +52 -8
  442. data/lib/rubocop/cop/style/expand_path_arguments.rb +2 -7
  443. data/lib/rubocop/cop/style/explicit_block_argument.rb +62 -9
  444. data/lib/rubocop/cop/style/exponential_notation.rb +5 -4
  445. data/lib/rubocop/cop/style/fetch_env_var.rb +33 -7
  446. data/lib/rubocop/cop/style/file_null.rb +6 -4
  447. data/lib/rubocop/cop/style/file_open.rb +84 -0
  448. data/lib/rubocop/cop/style/file_write.rb +21 -16
  449. data/lib/rubocop/cop/style/float_division.rb +43 -15
  450. data/lib/rubocop/cop/style/for.rb +48 -1
  451. data/lib/rubocop/cop/style/format_string.rb +44 -9
  452. data/lib/rubocop/cop/style/format_string_token.rb +86 -16
  453. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +5 -3
  454. data/lib/rubocop/cop/style/global_std_stream.rb +3 -0
  455. data/lib/rubocop/cop/style/global_vars.rb +5 -2
  456. data/lib/rubocop/cop/style/guard_clause.rb +41 -24
  457. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +27 -9
  458. data/lib/rubocop/cop/style/hash_conversion.rb +52 -16
  459. data/lib/rubocop/cop/style/hash_each_methods.rb +4 -4
  460. data/lib/rubocop/cop/style/hash_fetch_chain.rb +104 -0
  461. data/lib/rubocop/cop/style/hash_lookup_method.rb +128 -0
  462. data/lib/rubocop/cop/style/hash_slice.rb +16 -0
  463. data/lib/rubocop/cop/style/hash_syntax.rb +23 -6
  464. data/lib/rubocop/cop/style/hash_transform_keys.rb +19 -9
  465. data/lib/rubocop/cop/style/hash_transform_values.rb +19 -9
  466. data/lib/rubocop/cop/style/identical_conditional_branches.rb +39 -11
  467. data/lib/rubocop/cop/style/if_inside_else.rb +46 -21
  468. data/lib/rubocop/cop/style/if_unless_modifier.rb +90 -66
  469. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +19 -19
  470. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +5 -2
  471. data/lib/rubocop/cop/style/if_with_semicolon.rb +61 -8
  472. data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
  473. data/lib/rubocop/cop/style/inline_comment.rb +5 -2
  474. data/lib/rubocop/cop/style/inverse_methods.rb +34 -8
  475. data/lib/rubocop/cop/style/invertible_unless_condition.rb +27 -7
  476. data/lib/rubocop/cop/style/ip_addresses.rb +3 -4
  477. data/lib/rubocop/cop/style/it_assignment.rb +69 -12
  478. data/lib/rubocop/cop/style/it_block_parameter.rb +150 -0
  479. data/lib/rubocop/cop/style/keyword_arguments_merging.rb +4 -0
  480. data/lib/rubocop/cop/style/keyword_parameters_order.rb +23 -11
  481. data/lib/rubocop/cop/style/lambda.rb +30 -1
  482. data/lib/rubocop/cop/style/lambda_call.rb +31 -10
  483. data/lib/rubocop/cop/style/line_end_concatenation.rb +10 -4
  484. data/lib/rubocop/cop/style/magic_comment_format.rb +4 -4
  485. data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +11 -0
  486. data/lib/rubocop/cop/style/map_into_array.rb +6 -4
  487. data/lib/rubocop/cop/style/map_join.rb +123 -0
  488. data/lib/rubocop/cop/style/map_to_hash.rb +13 -5
  489. data/lib/rubocop/cop/style/map_to_set.rb +1 -3
  490. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +75 -24
  491. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +15 -2
  492. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +58 -6
  493. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +36 -8
  494. data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +2 -1
  495. data/lib/rubocop/cop/style/method_def_parentheses.rb +36 -8
  496. data/lib/rubocop/cop/style/min_max_comparison.rb +17 -6
  497. data/lib/rubocop/cop/style/missing_else.rb +7 -1
  498. data/lib/rubocop/cop/style/missing_respond_to_missing.rb +40 -7
  499. data/lib/rubocop/cop/style/mixin_usage.rb +1 -1
  500. data/lib/rubocop/cop/style/module_function.rb +4 -2
  501. data/lib/rubocop/cop/style/module_member_existence_check.rb +110 -0
  502. data/lib/rubocop/cop/style/multiline_block_chain.rb +2 -1
  503. data/lib/rubocop/cop/style/multiline_if_modifier.rb +47 -5
  504. data/lib/rubocop/cop/style/multiline_if_then.rb +10 -4
  505. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +7 -0
  506. data/lib/rubocop/cop/style/multiline_memoization.rb +7 -1
  507. data/lib/rubocop/cop/style/multiline_method_signature.rb +15 -22
  508. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +7 -3
  509. data/lib/rubocop/cop/style/multiline_when_then.rb +7 -0
  510. data/lib/rubocop/cop/style/multiple_comparison.rb +33 -22
  511. data/lib/rubocop/cop/style/mutable_constant.rb +139 -13
  512. data/lib/rubocop/cop/style/negated_if_else_condition.rb +3 -3
  513. data/lib/rubocop/cop/style/negative_array_index.rb +228 -0
  514. data/lib/rubocop/cop/style/nested_modifier.rb +36 -12
  515. data/lib/rubocop/cop/style/nested_ternary_operator.rb +26 -8
  516. data/lib/rubocop/cop/style/next.rb +68 -10
  517. data/lib/rubocop/cop/style/nil_comparison.rb +53 -13
  518. data/lib/rubocop/cop/style/nil_lambda.rb +9 -1
  519. data/lib/rubocop/cop/style/non_nil_check.rb +21 -11
  520. data/lib/rubocop/cop/style/not.rb +12 -3
  521. data/lib/rubocop/cop/style/numeric_literal_prefix.rb +11 -1
  522. data/lib/rubocop/cop/style/numeric_literals.rb +3 -2
  523. data/lib/rubocop/cop/style/numeric_predicate.rb +22 -6
  524. data/lib/rubocop/cop/style/object_then.rb +1 -0
  525. data/lib/rubocop/cop/style/one_class_per_file.rb +115 -0
  526. data/lib/rubocop/cop/style/one_line_conditional.rb +35 -15
  527. data/lib/rubocop/cop/style/open_struct_use.rb +1 -1
  528. data/lib/rubocop/cop/style/operator_method_call.rb +10 -2
  529. data/lib/rubocop/cop/style/option_hash.rb +1 -1
  530. data/lib/rubocop/cop/style/optional_arguments.rb +1 -0
  531. data/lib/rubocop/cop/style/parallel_assignment.rb +58 -27
  532. data/lib/rubocop/cop/style/parentheses_around_condition.rb +6 -1
  533. data/lib/rubocop/cop/style/partition_instead_of_double_select.rb +270 -0
  534. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +18 -3
  535. data/lib/rubocop/cop/style/percent_q_literals.rb +1 -1
  536. data/lib/rubocop/cop/style/perl_backrefs.rb +5 -3
  537. data/lib/rubocop/cop/style/predicate_with_kind.rb +84 -0
  538. data/lib/rubocop/cop/style/preferred_hash_methods.rb +12 -12
  539. data/lib/rubocop/cop/style/proc.rb +4 -2
  540. data/lib/rubocop/cop/style/raise_args.rb +9 -9
  541. data/lib/rubocop/cop/style/reduce_to_hash.rb +216 -0
  542. data/lib/rubocop/cop/style/redundant_argument.rb +2 -0
  543. data/lib/rubocop/cop/style/redundant_array_constructor.rb +2 -2
  544. data/lib/rubocop/cop/style/redundant_array_flatten.rb +50 -0
  545. data/lib/rubocop/cop/style/redundant_assignment.rb +12 -2
  546. data/lib/rubocop/cop/style/redundant_begin.rb +58 -6
  547. data/lib/rubocop/cop/style/redundant_condition.rb +93 -18
  548. data/lib/rubocop/cop/style/redundant_constant_base.rb +42 -6
  549. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +26 -4
  550. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +17 -3
  551. data/lib/rubocop/cop/style/redundant_each.rb +4 -5
  552. data/lib/rubocop/cop/style/redundant_exception.rb +22 -4
  553. data/lib/rubocop/cop/style/redundant_fetch_block.rb +11 -11
  554. data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +9 -0
  555. data/lib/rubocop/cop/style/redundant_filter_chain.rb +1 -1
  556. data/lib/rubocop/cop/style/redundant_format.rb +319 -0
  557. data/lib/rubocop/cop/style/redundant_freeze.rb +12 -3
  558. data/lib/rubocop/cop/style/redundant_heredoc_delimiter_quotes.rb +6 -1
  559. data/lib/rubocop/cop/style/redundant_interpolation.rb +12 -3
  560. data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +26 -10
  561. data/lib/rubocop/cop/style/redundant_line_continuation.rb +83 -119
  562. data/lib/rubocop/cop/style/redundant_min_max_by.rb +93 -0
  563. data/lib/rubocop/cop/style/redundant_parentheses.rb +176 -83
  564. data/lib/rubocop/cop/style/redundant_percent_q.rb +5 -3
  565. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +15 -0
  566. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +6 -5
  567. data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +27 -7
  568. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +16 -0
  569. data/lib/rubocop/cop/style/redundant_return.rb +4 -3
  570. data/lib/rubocop/cop/style/redundant_self.rb +20 -7
  571. data/lib/rubocop/cop/style/redundant_self_assignment.rb +2 -3
  572. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +0 -5
  573. data/lib/rubocop/cop/style/redundant_sort.rb +7 -7
  574. data/lib/rubocop/cop/style/redundant_sort_by.rb +17 -1
  575. data/lib/rubocop/cop/style/redundant_string_escape.rb +1 -2
  576. data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +133 -0
  577. data/lib/rubocop/cop/style/regexp_literal.rb +107 -29
  578. data/lib/rubocop/cop/style/require_order.rb +9 -0
  579. data/lib/rubocop/cop/style/rescue_modifier.rb +10 -12
  580. data/lib/rubocop/cop/style/return_nil.rb +19 -4
  581. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +11 -1
  582. data/lib/rubocop/cop/style/reverse_find.rb +51 -0
  583. data/lib/rubocop/cop/style/safe_navigation.rb +76 -20
  584. data/lib/rubocop/cop/style/sample.rb +19 -13
  585. data/lib/rubocop/cop/style/select_by_kind.rb +158 -0
  586. data/lib/rubocop/cop/style/select_by_range.rb +197 -0
  587. data/lib/rubocop/cop/style/select_by_regexp.rb +54 -22
  588. data/lib/rubocop/cop/style/self_assignment.rb +13 -16
  589. data/lib/rubocop/cop/style/semicolon.rb +56 -14
  590. data/lib/rubocop/cop/style/single_line_block_params.rb +2 -2
  591. data/lib/rubocop/cop/style/single_line_do_end_block.rb +22 -8
  592. data/lib/rubocop/cop/style/single_line_methods.rb +28 -11
  593. data/lib/rubocop/cop/style/sole_nested_conditional.rb +96 -104
  594. data/lib/rubocop/cop/style/special_global_vars.rb +7 -2
  595. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +32 -6
  596. data/lib/rubocop/cop/style/static_class.rb +21 -0
  597. data/lib/rubocop/cop/style/string_concatenation.rb +38 -16
  598. data/lib/rubocop/cop/style/string_hash_keys.rb +1 -0
  599. data/lib/rubocop/cop/style/struct_inheritance.rb +38 -2
  600. data/lib/rubocop/cop/style/super_arguments.rb +7 -6
  601. data/lib/rubocop/cop/style/symbol_array.rb +1 -1
  602. data/lib/rubocop/cop/style/symbol_proc.rb +34 -14
  603. data/lib/rubocop/cop/style/tally_method.rb +181 -0
  604. data/lib/rubocop/cop/style/ternary_parentheses.rb +34 -1
  605. data/lib/rubocop/cop/style/time_now.rb +41 -0
  606. data/lib/rubocop/cop/style/top_level_method_definition.rb +3 -2
  607. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +52 -1
  608. data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +47 -6
  609. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +2 -2
  610. data/lib/rubocop/cop/style/trailing_comma_in_hash_literal.rb +48 -6
  611. data/lib/rubocop/cop/style/trailing_method_end_statement.rb +1 -0
  612. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +35 -32
  613. data/lib/rubocop/cop/style/trivial_accessors.rb +2 -2
  614. data/lib/rubocop/cop/style/unless_else.rb +10 -9
  615. data/lib/rubocop/cop/style/unless_logical_operators.rb +3 -3
  616. data/lib/rubocop/cop/style/while_until_do.rb +7 -0
  617. data/lib/rubocop/cop/style/while_until_modifier.rb +16 -1
  618. data/lib/rubocop/cop/style/word_array.rb +1 -0
  619. data/lib/rubocop/cop/style/yoda_condition.rb +26 -12
  620. data/lib/rubocop/cop/style/yoda_expression.rb +11 -2
  621. data/lib/rubocop/cop/style/zero_length_predicate.rb +6 -3
  622. data/lib/rubocop/cop/style.rb +311 -0
  623. data/lib/rubocop/cop/team.rb +172 -49
  624. data/lib/rubocop/cop/util.rb +7 -10
  625. data/lib/rubocop/cop/utils/format_string.rb +20 -5
  626. data/lib/rubocop/cop/variable_force/assignment.rb +8 -4
  627. data/lib/rubocop/cop/variable_force/branch.rb +30 -6
  628. data/lib/rubocop/cop/variable_force/scope.rb +1 -1
  629. data/lib/rubocop/cop/variable_force/variable.rb +42 -11
  630. data/lib/rubocop/cop/variable_force.rb +26 -9
  631. data/lib/rubocop/cops_documentation_generator.rb +49 -15
  632. data/lib/rubocop/directive_comment.rb +147 -5
  633. data/lib/rubocop/ext/regexp_node.rb +0 -1
  634. data/lib/rubocop/feature_loader.rb +0 -6
  635. data/lib/rubocop/file_patterns.rb +9 -1
  636. data/lib/rubocop/formatter/base_formatter.rb +22 -0
  637. data/lib/rubocop/formatter/clang_style_formatter.rb +5 -2
  638. data/lib/rubocop/formatter/disabled_config_formatter.rb +57 -14
  639. data/lib/rubocop/formatter/emacs_style_formatter.rb +3 -1
  640. data/lib/rubocop/formatter/formatter_set.rb +7 -2
  641. data/lib/rubocop/formatter/fuubar_style_formatter.rb +1 -1
  642. data/lib/rubocop/formatter/github_actions_formatter.rb +6 -5
  643. data/lib/rubocop/formatter/html_formatter.rb +3 -5
  644. data/lib/rubocop/formatter/json_formatter.rb +82 -7
  645. data/lib/rubocop/formatter/junit_formatter.rb +27 -10
  646. data/lib/rubocop/formatter/markdown_formatter.rb +1 -0
  647. data/lib/rubocop/formatter/offense_count_formatter.rb +5 -4
  648. data/lib/rubocop/formatter/pacman_formatter.rb +5 -2
  649. data/lib/rubocop/formatter/sarif_formatter.rb +186 -0
  650. data/lib/rubocop/formatter/simple_text_formatter.rb +4 -5
  651. data/lib/rubocop/formatter/tap_formatter.rb +8 -3
  652. data/lib/rubocop/formatter/worst_offenders_formatter.rb +5 -4
  653. data/lib/rubocop/formatter.rb +23 -21
  654. data/lib/rubocop/lockfile.rb +18 -0
  655. data/lib/rubocop/lsp/diagnostic.rb +22 -41
  656. data/lib/rubocop/lsp/disable_comment_edits.rb +135 -0
  657. data/lib/rubocop/lsp/routes.rb +136 -33
  658. data/lib/rubocop/lsp/runtime.rb +66 -15
  659. data/lib/rubocop/lsp/server.rb +4 -2
  660. data/lib/rubocop/lsp/stdin_runner.rb +38 -19
  661. data/lib/rubocop/lsp.rb +5 -2
  662. data/lib/rubocop/magic_comment.rb +55 -0
  663. data/lib/rubocop/mcp/autocorrection_request.rb +52 -0
  664. data/lib/rubocop/mcp/explain_tool.rb +68 -0
  665. data/lib/rubocop/mcp/scope.rb +65 -0
  666. data/lib/rubocop/mcp/server.rb +307 -0
  667. data/lib/rubocop/mcp/source_files.rb +35 -0
  668. data/lib/rubocop/offense_limit.rb +30 -0
  669. data/lib/rubocop/options.rb +166 -23
  670. data/lib/rubocop/path_util.rb +20 -5
  671. data/lib/rubocop/pending_cops_reporter.rb +51 -0
  672. data/lib/rubocop/plugin/configuration_integrator.rb +153 -0
  673. data/lib/rubocop/plugin/load_error.rb +26 -0
  674. data/lib/rubocop/plugin/loader.rb +100 -0
  675. data/lib/rubocop/plugin/not_supported_error.rb +29 -0
  676. data/lib/rubocop/plugin.rb +46 -0
  677. data/lib/rubocop/project_index_loader.rb +82 -0
  678. data/lib/rubocop/rake_task.rb +6 -4
  679. data/lib/rubocop/remote_config.rb +18 -13
  680. data/lib/rubocop/result_cache.rb +110 -65
  681. data/lib/rubocop/rspec/cop_helper.rb +21 -1
  682. data/lib/rubocop/rspec/expect_offense.rb +11 -7
  683. data/lib/rubocop/rspec/shared_contexts.rb +85 -3
  684. data/lib/rubocop/rspec/support.rb +4 -0
  685. data/lib/rubocop/runner.rb +368 -106
  686. data/lib/rubocop/server/cache.rb +67 -48
  687. data/lib/rubocop/server/cli.rb +2 -4
  688. data/lib/rubocop/server/client_command/base.rb +10 -0
  689. data/lib/rubocop/server/client_command/exec.rb +2 -1
  690. data/lib/rubocop/server/client_command/start.rb +11 -1
  691. data/lib/rubocop/server/core.rb +8 -0
  692. data/lib/rubocop/server.rb +14 -0
  693. data/lib/rubocop/target_finder.rb +25 -11
  694. data/lib/rubocop/target_ruby.rb +31 -14
  695. data/lib/rubocop/todo_audit.rb +111 -0
  696. data/lib/rubocop/unified_diff.rb +257 -0
  697. data/lib/rubocop/util.rb +69 -0
  698. data/lib/rubocop/version.rb +48 -10
  699. data/lib/rubocop.rb +35 -682
  700. data/lib/ruby_lsp/rubocop/addon.rb +28 -17
  701. data/lib/ruby_lsp/rubocop/runtime_adapter.rb +99 -0
  702. metadata +129 -21
  703. data/lib/rubocop/cop/style/double_cop_disable_directive.rb +0 -46
  704. data/lib/rubocop/cop/utils/regexp_ranges.rb +0 -113
  705. data/lib/ruby_lsp/rubocop/wraps_built_in_lsp_runtime.rb +0 -50
@@ -50,10 +50,17 @@ module RuboCop
50
50
  unless when_node.conditions.first.first_line == when_node.conditions.last.last_line
51
51
  return true
52
52
  end
53
+ return true if ends_with_endless_range?(when_node.conditions.last)
53
54
  return false unless when_node.body
54
55
 
55
56
  same_line?(when_node, when_node.body)
56
57
  end
58
+
59
+ def ends_with_endless_range?(node)
60
+ node.each_node(:range).any? do |range|
61
+ range.end.nil? && range.source_range.end_pos == node.source_range.end_pos
62
+ end
63
+ end
57
64
  end
58
65
  end
59
66
  end
@@ -70,48 +70,59 @@ module RuboCop
70
70
  (send $_ :== ${lvar call})
71
71
  PATTERN
72
72
 
73
- # rubocop:disable Metrics/AbcSize
74
73
  def on_or(node)
75
- root_of_or_node = root_of_or_node(node)
76
- return unless node == root_of_or_node
74
+ return unless node == root_of_or_node(node)
77
75
  return unless nested_comparison?(node)
78
-
79
- return unless (variable, values = find_offending_var(node))
76
+ return unless (variable, values, skipped = find_offending_var(node))
80
77
  return if values.size < comparisons_threshold
81
78
 
82
79
  range = offense_range(values)
80
+ # Bail when an allowed method comparison sits between the collected
81
+ # values: collapsing them into a single `include?` would drop it.
82
+ return if skipped_within_range?(skipped, range)
83
83
 
84
84
  add_offense(range) do |corrector|
85
- elements = values.map(&:source).join(', ')
86
- argument = variable.lvar_type? ? variable_name(variable) : variable.source
87
- prefer_method = "[#{elements}].include?(#{argument})"
88
-
89
- corrector.replace(range, prefer_method)
85
+ corrector.replace(range, preferred_method(variable, values))
90
86
  end
91
87
  end
92
- # rubocop:enable Metrics/AbcSize
93
88
 
94
89
  private
95
90
 
96
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
97
- def find_offending_var(node, variables = Set.new, values = [])
91
+ def skipped_within_range?(skipped, range)
92
+ skipped.any? { |node_part| range.contains?(node_part.source_range) }
93
+ end
94
+
95
+ def preferred_method(variable, values)
96
+ elements = values.map(&:source).join(', ')
97
+ argument = variable.lvar_type? ? variable_name(variable) : variable.source
98
+ "[#{elements}].include?(#{argument})"
99
+ end
100
+
101
+ def find_offending_var(node, variables = Set.new, values = [], skipped = [])
98
102
  if node.or_type?
99
- find_offending_var(node.lhs, variables, values)
100
- find_offending_var(node.rhs, variables, values)
103
+ find_offending_var(node.lhs, variables, values, skipped)
104
+ find_offending_var(node.rhs, variables, values, skipped)
101
105
  elsif simple_double_comparison?(node)
102
106
  return
103
- elsif (var, obj = simple_comparison(node))
104
- return if allow_method_comparison? && obj.call_type?
107
+ elsif (comparison = simple_comparison(node))
108
+ collect_comparison(node, comparison, variables, values, skipped)
109
+ end
105
110
 
106
- variables << var
107
- return if variables.size > 1
111
+ [variables.first, values, skipped] if variables.any?
112
+ end
108
113
 
109
- values << obj
114
+ def collect_comparison(node, comparison, variables, values, skipped)
115
+ var, obj = comparison
116
+ if allow_method_comparison? && obj.call_type?
117
+ skipped << node
118
+ return
110
119
  end
111
120
 
112
- [variables.first, values] if variables.any?
121
+ variables << var
122
+ return if variables.size > 1
123
+
124
+ values << obj
113
125
  end
114
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
115
126
 
116
127
  def offense_range(values)
117
128
  values.first.parent.source_range.begin.join(values.last.parent.source_range.end)
@@ -6,6 +6,14 @@ module RuboCop
6
6
  # Checks whether some constant value isn't a
7
7
  # mutable literal (e.g. array or hash).
8
8
  #
9
+ # When the `Recursive` option is enabled, mutable literals nested inside
10
+ # arrays and hashes are also frozen, so an offense on the outermost
11
+ # unfrozen literal will autocorrect every nested mutable literal as well.
12
+ # When the outer literal already has `.freeze` appended, the cop descends
13
+ # into it and reports each outermost unfrozen literal underneath. The
14
+ # option is disabled by default to preserve existing behavior; opt in to
15
+ # get strict nested freezing.
16
+ #
9
17
  # Strict mode can be used to freeze all constants, rather than
10
18
  # just literals.
11
19
  # Strict mode is considered an experimental feature. It has not been
@@ -49,6 +57,17 @@ module RuboCop
49
57
  # CONST = Something.new
50
58
  #
51
59
  #
60
+ # @example Recursive: false (default)
61
+ # # good - only the outer container needs to be frozen
62
+ # CONST = [{ a: [], b: 'foo' }].freeze
63
+ #
64
+ # @example Recursive: true
65
+ # # bad - nested mutable literals must be frozen too
66
+ # CONST = [{ a: [], b: 'foo' }].freeze
67
+ #
68
+ # # good
69
+ # CONST = [{ a: [].freeze, b: 'foo'.freeze }.freeze].freeze
70
+ #
52
71
  # @example EnforcedStyle: strict
53
72
  # # bad
54
73
  # CONST = Something.new
@@ -70,6 +89,9 @@ module RuboCop
70
89
  # end
71
90
  # end.freeze
72
91
  #
92
+ # # good - `Data.define` declares an immutable value type
93
+ # CONST = Data.define(:foo, :bar)
94
+ #
73
95
  # @example
74
96
  # # Magic comment - shareable_constant_value: literal
75
97
  #
@@ -124,8 +146,15 @@ module RuboCop
124
146
 
125
147
  MSG = 'Freeze mutable objects assigned to constants.'
126
148
 
149
+ # With `Recursive: true`, `.freeze` is appended to a nested literal. When the literal
150
+ # is the last element and spans multiple lines, the trailing comma lands between the literal
151
+ # and the `.freeze` appended in the same pass.
152
+ def self.autocorrect_incompatible_with
153
+ [Style::TrailingCommaInArrayLiteral, Style::TrailingCommaInHashLiteral]
154
+ end
155
+
127
156
  def on_casgn(node)
128
- if node.expression.nil? # This is only the case for `CONST += ...` or similarg66
157
+ if node.expression.nil? # This is only the case for `CONST += ...` or similar
129
158
  parent = node.parent
130
159
  return unless parent.or_asgn_type? # We only care about `CONST ||= ...`
131
160
 
@@ -138,31 +167,55 @@ module RuboCop
138
167
  private
139
168
 
140
169
  def on_assignment(value)
141
- if style == :strict
142
- strict_check(value)
170
+ nodes = mutable_nodes(value) do |node|
171
+ if style == :strict
172
+ strict_check(node)
173
+ else
174
+ literal_check(node)
175
+ end
176
+ end
177
+
178
+ nodes.each do |node|
179
+ add_offense(node) { |corrector| autocorrect(corrector, node) }
180
+ end
181
+ end
182
+
183
+ def mutable_nodes(value, &block)
184
+ if recursive? && explicitly_frozen_literal?(value)
185
+ literal_children(value.receiver).flat_map { |c| mutable_nodes(c, &block) }
143
186
  else
144
- check(value)
187
+ node_offending = yield(value)
188
+
189
+ if node_offending
190
+ [value]
191
+ else
192
+ []
193
+ end
145
194
  end
146
195
  end
147
196
 
148
197
  def strict_check(value)
198
+ return if value.assignment?
149
199
  return if immutable_literal?(value)
150
200
  return if operation_produces_immutable_object?(value)
151
201
  return if frozen_string_literal?(value)
152
202
  return if shareable_constant_value?(value)
203
+ return if parenthesized_immutable_value?(value)
153
204
 
154
- add_offense(value) { |corrector| autocorrect(corrector, value) }
205
+ true
155
206
  end
156
207
 
157
- def check(value)
158
- range_enclosed_in_parentheses = range_enclosed_in_parentheses?(value)
159
- return unless mutable_literal?(value) ||
160
- (target_ruby_version <= 2.7 && range_enclosed_in_parentheses)
161
-
208
+ def literal_check(value)
209
+ return unless mutable_or_unfrozen_range?(value)
162
210
  return if frozen_string_literal?(value)
163
211
  return if shareable_constant_value?(value)
164
212
 
165
- add_offense(value) { |corrector| autocorrect(corrector, value) }
213
+ true
214
+ end
215
+
216
+ def mutable_or_unfrozen_range?(value)
217
+ mutable_literal?(value) ||
218
+ (target_ruby_version <= 2.7 && range_enclosed_in_parentheses?(value))
166
219
  end
167
220
 
168
221
  def autocorrect(corrector, node)
@@ -171,13 +224,66 @@ module RuboCop
171
224
  splat_value = splat_value(node)
172
225
  if splat_value
173
226
  correct_splat_expansion(corrector, expr, splat_value)
174
- elsif node.array_type? && !node.bracketed?
227
+ corrector.insert_after(expr, '.freeze')
228
+ return
229
+ end
230
+
231
+ if node.array_type? && !node.bracketed?
175
232
  corrector.wrap(expr, '[', ']')
176
233
  elsif requires_parentheses?(node)
177
234
  corrector.wrap(expr, '(', ')')
178
235
  end
179
236
 
180
237
  corrector.insert_after(expr, '.freeze')
238
+
239
+ freeze_nested_literals(corrector, node) if recursive?
240
+ end
241
+
242
+ # Recursively freezes every nested mutable literal inside an array or
243
+ # hash literal. Already-frozen subtrees are not re-frozen, but their
244
+ # children are still inspected for unfrozen literals deeper down.
245
+ def freeze_nested_literals(corrector, node)
246
+ literal_children(node).each do |child|
247
+ if explicitly_frozen_literal?(child)
248
+ freeze_nested_literals(corrector, child.receiver)
249
+ elsif freezable_nested_literal?(child)
250
+ autocorrect(corrector, child)
251
+ end
252
+ end
253
+ end
254
+
255
+ def freezable_nested_literal?(node)
256
+ return false if frozen_string_literal?(node)
257
+ return false if shareable_constant_value?(node)
258
+
259
+ mutable_literal?(node)
260
+ end
261
+
262
+ # Returns the child literals of an array or hash node that may
263
+ # themselves need freezing. For hashes, both keys and values are
264
+ # included. Percent-literal arrays (e.g. `%w(a b)`) are skipped because
265
+ # `.freeze` cannot be appended to their contents.
266
+ def literal_children(node)
267
+ case node.type
268
+ when :array
269
+ return [] if node.percent_literal?
270
+
271
+ node.children
272
+ when :hash
273
+ node.children.flat_map { |child| child.pair_type? ? child.children : [] }
274
+ else
275
+ []
276
+ end
277
+ end
278
+
279
+ def explicitly_frozen_literal?(node)
280
+ return false unless node.send_type? && node.method?(:freeze)
281
+
282
+ node.receiver && mutable_literal?(node.receiver)
283
+ end
284
+
285
+ def recursive?
286
+ cop_config.fetch('Recursive', false)
181
287
  end
182
288
 
183
289
  def mutable_literal?(value)
@@ -196,12 +302,30 @@ module RuboCop
196
302
  recent_shareable_value? node
197
303
  end
198
304
 
305
+ def parenthesized_immutable_value?(value)
306
+ return false if !value.begin_type? || !value.children.one?
307
+
308
+ inner = value.children.first
309
+
310
+ !inner.assignment? && !strict_check(inner)
311
+ end
312
+
199
313
  def frozen_regexp_or_range_literals?(node)
200
314
  target_ruby_version >= 3.0 && node.type?(:regexp, :range)
201
315
  end
202
316
 
203
317
  def requires_parentheses?(node)
204
- node.range_type? || (node.send_type? && node.loc.dot.nil?)
318
+ if node.range_type? || node.operator_keyword? || unparenthesized_method_call?(node)
319
+ return true
320
+ end
321
+
322
+ node.if_type? && node.ternary?
323
+ end
324
+
325
+ def unparenthesized_method_call?(node)
326
+ return false unless node.call_type?
327
+
328
+ node.loc.dot.nil? || (node.arguments.any? && !node.parenthesized?)
205
329
  end
206
330
 
207
331
  def correct_splat_expansion(corrector, expr, splat_value)
@@ -225,6 +349,8 @@ module RuboCop
225
349
  (const _ _)
226
350
  (send (const {nil? cbase} :Struct) :new ...)
227
351
  (block (send (const {nil? cbase} :Struct) :new ...) ...)
352
+ (send (const {nil? cbase} :Data) :define ...)
353
+ (block (send (const {nil? cbase} :Data) :define ...) ...)
228
354
  (send _ :freeze)
229
355
  (send {float int} {:+ :- :* :** :/ :% :<<} _)
230
356
  (send _ {:+ :- :* :** :/ :%} {float int})
@@ -46,7 +46,7 @@ module RuboCop
46
46
  @corrected_nodes = nil
47
47
  end
48
48
 
49
- # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity
49
+ # rubocop:disable-next Metrics/AbcSize,Metrics/CyclomaticComplexity
50
50
  def on_if(node)
51
51
  return unless if_else?(node)
52
52
  return unless (condition = unwrap_begin_nodes(node.condition))
@@ -64,7 +64,6 @@ module RuboCop
64
64
  end
65
65
  end
66
66
  end
67
- # rubocop:enable Metrics/AbcSize,Metrics/CyclomaticComplexity
68
67
 
69
68
  private
70
69
 
@@ -119,7 +118,8 @@ module RuboCop
119
118
  if node.ternary?
120
119
  node.if_branch
121
120
  else
122
- range_between(node.condition.source_range.end_pos, node.loc.else.begin_pos)
121
+ if_start = node.loc.begin || node.condition.source_range
122
+ range_between(if_start.end_pos, node.loc.else.begin_pos)
123
123
  end
124
124
  end
125
125
 
@@ -0,0 +1,228 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Identifies usages of `arr[arr.length - n]`, `arr[arr.size - n]`, or
7
+ # `arr[arr.count - n]` and suggests to change them to use `arr[-n]` instead.
8
+ # Also handles range patterns like `arr[0..(arr.length - n)]`.
9
+ #
10
+ # The cop recognizes preserving methods (`sort`, `reverse`, `shuffle`, `rotate`)
11
+ # and their combinations, allowing safe replacement when the receiver matches.
12
+ # It works with variables, instance variables, class variables, and constants.
13
+ #
14
+ # @safety
15
+ # This cop is unsafe because the receiver may not be an Array. For a Hash,
16
+ # `hash[hash.size - 1]` looks up the key `hash.size - 1`, which `hash[-1]`
17
+ # does not. Even for an Array, `arr[arr.length - n]` wraps around to an
18
+ # element from the end when `n` exceeds the length, while `arr[-n]`
19
+ # returns `nil`.
20
+ #
21
+ # @example
22
+ # # bad
23
+ # arr[arr.count - 2]
24
+ # arr[0..(arr.length - 2)]
25
+ # arr[0...(arr.length - 4)]
26
+ # arr.sort[arr.reverse.length - 2]
27
+ # arr.sort.reverse[arr.sort.size - 2]
28
+ #
29
+ # # good
30
+ # arr[-2]
31
+ # arr[0..-2]
32
+ # arr[0...-4]
33
+ # arr.sort[-2]
34
+ # arr.sort.reverse[-2]
35
+ #
36
+ class NegativeArrayIndex < Base
37
+ extend AutoCorrector
38
+ include RangeHelp
39
+
40
+ MSG = 'Use `%<receiver>s[-%<index>s]` instead of `%<current>s`.'
41
+ MSG_RANGE = 'Use `%<receiver>s[%<start>s%<range_op>s-%<index>s]` instead of `%<current>s`.'
42
+ RESTRICT_ON_SEND = %i[[]].freeze
43
+
44
+ LENGTH_METHODS = %i[length size count].freeze
45
+
46
+ PRESERVING_METHODS = %i[sort reverse shuffle rotate].freeze
47
+
48
+ # @!method length_subtraction?(node)
49
+ def_node_matcher :length_subtraction?, <<~PATTERN
50
+ (send
51
+ (send $_ {:length :size :count}) :-
52
+ (int $_))
53
+ PATTERN
54
+
55
+ def on_send(node)
56
+ return if node.arguments.empty?
57
+
58
+ index_arg = node.first_argument
59
+ range_node = extract_range_from_begin(index_arg)
60
+ if range_with_length_subtraction?(range_node, node.receiver)
61
+ receiver = node.receiver.source
62
+ return handle_range_pattern(receiver, range_node, index_arg)
63
+ end
64
+
65
+ handle_simple_index_pattern(node, index_arg)
66
+ end
67
+
68
+ alias on_csend on_send
69
+
70
+ private
71
+
72
+ def handle_simple_index_pattern(node, index_arg)
73
+ length_receiver, negative_index = length_subtraction?(index_arg)
74
+
75
+ return unless negative_index&.positive?
76
+ return unless receivers_match?(length_receiver, node.receiver)
77
+
78
+ add_offense_for_subtraction(node, index_arg, negative_index)
79
+ end
80
+
81
+ def extract_range_from_begin(node)
82
+ node.begin_type? ? node.children.first : node
83
+ end
84
+
85
+ def extract_inner_end(node)
86
+ node.children.size == 1 ? node.children.first : node
87
+ end
88
+
89
+ def add_offense_for_subtraction(node, index_arg, negative_index)
90
+ receiver = node.receiver.source
91
+ offense_range = index_arg.source_range
92
+ current = "#{receiver}[#{index_arg.source}]"
93
+
94
+ message = format(MSG, receiver: receiver, index: negative_index, current: current)
95
+
96
+ add_offense(offense_range, message: message) do |corrector|
97
+ corrector.replace(offense_range, "-#{negative_index}")
98
+ end
99
+ end
100
+
101
+ # rubocop:disable-next Metrics/CyclomaticComplexity
102
+ def range_with_length_subtraction?(range_node, array_receiver)
103
+ return false unless range_node&.range_type?
104
+
105
+ range_end = range_node.end
106
+ range_start = range_node.begin
107
+ return false unless range_end && range_start
108
+
109
+ return false unless preserving_method?(range_start)
110
+
111
+ inner_end = extract_inner_end(range_end)
112
+ length_receiver, negative_index = length_subtraction?(inner_end)
113
+
114
+ return false unless negative_index&.positive?
115
+
116
+ receivers_match_strict?(length_receiver, array_receiver)
117
+ end
118
+
119
+ def handle_range_pattern(receiver, range_node, index_arg)
120
+ range_end = range_node.end
121
+ inner_end = extract_inner_end(range_end)
122
+ _length_receiver, negative_index = length_subtraction?(inner_end)
123
+
124
+ message, replacement = build_range_offense_data(
125
+ receiver, range_node, range_end, inner_end, negative_index, index_arg
126
+ )
127
+
128
+ add_offense(range_end, message: message) do |corrector|
129
+ corrector.replace(index_arg, replacement)
130
+ end
131
+ end
132
+
133
+ # rubocop:disable-next Metrics/ParameterLists
134
+ def build_range_offense_data(receiver, range_node, range_end, inner_end, negative_index,
135
+ index_arg)
136
+ range_op = range_node.erange_type? ? '...' : '..'
137
+ range_start = range_node.begin.source
138
+
139
+ range_without_parens =
140
+ build_range_without_parens(range_start, range_op, range_end, inner_end)
141
+ current_source = build_current_source(receiver, range_without_parens, index_arg)
142
+ start, index = format_range_message_parts(range_start, negative_index, index_arg)
143
+
144
+ message = build_message_for_range(receiver, start, range_op, index, current_source)
145
+ replacement = build_replacement_string(range_start, range_op, negative_index, index_arg)
146
+
147
+ [message, replacement]
148
+ end
149
+
150
+ def format_range_message_parts(range_start, negative_index, index_arg)
151
+ has_parentheses = index_arg.begin_type?
152
+ start = has_parentheses ? "(#{range_start}" : range_start
153
+ index = has_parentheses ? "#{negative_index})" : negative_index
154
+
155
+ [start, index]
156
+ end
157
+
158
+ def build_message_for_range(receiver, start, range_op, index, current)
159
+ format(
160
+ MSG_RANGE,
161
+ receiver: receiver, start: start, range_op: range_op, index: index, current: current
162
+ )
163
+ end
164
+
165
+ def build_replacement_string(range_start, range_op, negative_index, index_arg)
166
+ has_parentheses = index_arg.begin_type?
167
+
168
+ if has_parentheses
169
+ "(#{range_start}#{range_op}-#{negative_index})"
170
+ else
171
+ "#{range_start}#{range_op}-#{negative_index}"
172
+ end
173
+ end
174
+
175
+ def build_current_source(receiver, range_without_parens, index_arg)
176
+ has_parentheses = index_arg.begin_type?
177
+
178
+ if has_parentheses
179
+ "#{receiver}[(#{range_without_parens})]"
180
+ else
181
+ "#{receiver}[#{range_without_parens}]"
182
+ end
183
+ end
184
+
185
+ def build_range_without_parens(range_start, range_op, range_end, inner_end)
186
+ end_expression = range_end.begin_type? ? range_end.source : inner_end.source
187
+
188
+ "#{range_start}#{range_op}#{end_expression}"
189
+ end
190
+
191
+ def receivers_match?(length_receiver, array_receiver)
192
+ return array_receiver.self_type? unless length_receiver
193
+
194
+ unless preserving_method?(array_receiver) && preserving_method?(length_receiver)
195
+ return false
196
+ end
197
+ return true if length_receiver.source == array_receiver.source
198
+ return false unless (base_receiver = extract_base_receiver(array_receiver))
199
+
200
+ base_receiver.source == (extract_base_receiver(length_receiver) || length_receiver).source
201
+ end
202
+
203
+ def receivers_match_strict?(length_receiver, array_receiver)
204
+ preserving_method?(array_receiver) &&
205
+ length_receiver.source == array_receiver.source
206
+ end
207
+
208
+ def extract_base_receiver(node)
209
+ receiver = node.receiver
210
+
211
+ return nil unless receiver
212
+ return receiver unless receiver.receiver
213
+
214
+ extract_base_receiver(receiver)
215
+ end
216
+
217
+ def preserving_method?(node)
218
+ return true if node.receiver.nil?
219
+
220
+ method_name = node.method_name
221
+ return false unless PRESERVING_METHODS.include?(method_name)
222
+
223
+ preserving_method?(node.receiver)
224
+ end
225
+ end
226
+ end
227
+ end
228
+ end
@@ -19,6 +19,19 @@ module RuboCop
19
19
 
20
20
  MSG = 'Avoid using nested modifiers.'
21
21
 
22
+ # Operands that would otherwise bind differently next to `&&`/`||`, or
23
+ # not parse at all.
24
+ # @!method low_precedence?(node)
25
+ def_node_matcher :low_precedence?, <<~PATTERN
26
+ {
27
+ [{and or} semantic_operator?]
28
+ [send prefix_not?]
29
+ [if ternary?]
30
+ assignment?
31
+ [{csend yield super} arguments? !parenthesized? !operator_method?]
32
+ }
33
+ PATTERN
34
+
22
35
  def on_while(node)
23
36
  check(node)
24
37
  end
@@ -63,25 +76,31 @@ module RuboCop
63
76
  end
64
77
 
65
78
  def left_hand_operand(node, operator)
66
- expr = node.condition.source
67
- expr = "(#{expr})" if node.condition.or_type? && operator == '&&'
79
+ expr = operand_source(node.condition)
80
+ if (node.condition.or_type? && operator == '&&') || low_precedence?(node.condition)
81
+ expr = "(#{expr})"
82
+ end
68
83
  expr
69
84
  end
70
85
 
71
86
  def right_hand_operand(node, left_hand_keyword)
72
87
  condition = node.condition
88
+ negated = left_hand_keyword != node.keyword
73
89
 
74
- expr = if condition.send_type? && !condition.arguments.empty? &&
75
- !condition.operator_method?
76
- add_parentheses_to_method_arguments(condition)
77
- else
78
- condition.source
79
- end
80
- expr = "(#{expr})" if requires_parens?(condition)
81
- expr = "!#{expr}" unless left_hand_keyword == node.keyword
90
+ expr = operand_source(condition)
91
+ expr = "(#{expr})" if requires_parens?(condition, negated)
92
+ expr = "!#{expr}" if negated
82
93
  expr
83
94
  end
84
95
 
96
+ def operand_source(condition)
97
+ if condition.send_type? && !condition.arguments.empty? && !condition.operator_method?
98
+ add_parentheses_to_method_arguments(condition)
99
+ else
100
+ condition.source
101
+ end
102
+ end
103
+
85
104
  def add_parentheses_to_method_arguments(send_node)
86
105
  expr = +''
87
106
  expr << "#{send_node.receiver.source}." if send_node.receiver
@@ -91,8 +110,13 @@ module RuboCop
91
110
  expr
92
111
  end
93
112
 
94
- def requires_parens?(node)
95
- node.or_type? || !(RuboCop::AST::Node::COMPARISON_OPERATORS & node.children).empty?
113
+ def requires_parens?(node, negated)
114
+ # A negated `&&`/`||` operand must be wrapped so the `!` applies to the
115
+ # whole expression (e.g. `!(a && b)`, not `!a && b`).
116
+ (negated && node.operator_keyword?) ||
117
+ node.or_type? ||
118
+ low_precedence?(node) ||
119
+ !(RuboCop::AST::Node::COMPARISON_OPERATORS & node.children).empty?
96
120
  end
97
121
  end
98
122
  end