rubocop 1.84.2 → 1.88.1

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 (357) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +183 -87
  3. data/config/obsoletion.yml +26 -1
  4. data/lib/rubocop/cache_config.rb +1 -1
  5. data/lib/rubocop/cli/command/auto_generate_config.rb +34 -2
  6. data/lib/rubocop/cli/command/list_enabled_cops_for.rb +40 -0
  7. data/lib/rubocop/cli/command/mcp.rb +19 -0
  8. data/lib/rubocop/cli/command/show_cops.rb +2 -2
  9. data/lib/rubocop/cli/command/show_docs_url.rb +4 -8
  10. data/lib/rubocop/cli/command/suggest_extensions.rb +1 -1
  11. data/lib/rubocop/cli.rb +9 -7
  12. data/lib/rubocop/comment_config.rb +12 -15
  13. data/lib/rubocop/config.rb +14 -10
  14. data/lib/rubocop/config_finder.rb +1 -1
  15. data/lib/rubocop/config_loader.rb +17 -2
  16. data/lib/rubocop/config_loader_resolver.rb +13 -4
  17. data/lib/rubocop/config_obsoletion/extracted_cop.rb +4 -2
  18. data/lib/rubocop/config_store.rb +2 -2
  19. data/lib/rubocop/config_validator.rb +1 -1
  20. data/lib/rubocop/cop/autocorrect_logic.rb +2 -1
  21. data/lib/rubocop/cop/base.rb +25 -4
  22. data/lib/rubocop/cop/bundler/gem_comment.rb +5 -3
  23. data/lib/rubocop/cop/correctors/condition_corrector.rb +1 -1
  24. data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +1 -1
  25. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +7 -1
  26. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +1 -5
  27. data/lib/rubocop/cop/correctors/ordered_gem_corrector.rb +8 -1
  28. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +33 -2
  29. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
  30. data/lib/rubocop/cop/correctors.rb +28 -0
  31. data/lib/rubocop/cop/documentation.rb +2 -3
  32. data/lib/rubocop/cop/exclude_limit.rb +31 -5
  33. data/lib/rubocop/cop/gemspec/development_dependencies.rb +1 -1
  34. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +2 -2
  35. data/lib/rubocop/cop/gemspec/require_mfa.rb +9 -6
  36. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +3 -3
  37. data/lib/rubocop/cop/internal_affairs/itblock_handler.rb +69 -0
  38. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +1 -0
  39. data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +5 -3
  40. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  41. data/lib/rubocop/cop/layout/argument_alignment.rb +2 -2
  42. data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
  43. data/lib/rubocop/cop/layout/begin_end_alignment.rb +1 -1
  44. data/lib/rubocop/cop/layout/block_alignment.rb +58 -4
  45. data/lib/rubocop/cop/layout/class_structure.rb +8 -4
  46. data/lib/rubocop/cop/layout/condition_position.rb +13 -3
  47. data/lib/rubocop/cop/layout/dot_position.rb +1 -1
  48. data/lib/rubocop/cop/layout/empty_comment.rb +8 -10
  49. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +23 -7
  50. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +15 -2
  51. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -0
  52. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +12 -2
  53. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +16 -2
  54. data/lib/rubocop/cop/layout/empty_lines_around_module_body.rb +16 -2
  55. data/lib/rubocop/cop/layout/end_alignment.rb +8 -5
  56. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +20 -15
  57. data/lib/rubocop/cop/layout/hash_alignment.rb +1 -1
  58. data/lib/rubocop/cop/layout/indentation_width.rb +40 -0
  59. data/lib/rubocop/cop/layout/line_length.rb +5 -3
  60. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +9 -2
  61. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +1 -1
  62. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +53 -3
  63. data/lib/rubocop/cop/layout/parameter_alignment.rb +1 -1
  64. data/lib/rubocop/cop/layout/redundant_line_break.rb +3 -1
  65. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +1 -1
  66. data/lib/rubocop/cop/layout/space_around_keyword.rb +3 -1
  67. data/lib/rubocop/cop/layout/space_around_operators.rb +6 -2
  68. data/lib/rubocop/cop/layout/space_before_brackets.rb +1 -1
  69. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +1 -0
  70. data/lib/rubocop/cop/lint/ambiguous_assignment.rb +1 -11
  71. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  72. data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +1 -10
  73. data/lib/rubocop/cop/lint/assignment_in_condition.rb +13 -1
  74. data/lib/rubocop/cop/lint/circular_argument_reference.rb +1 -3
  75. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +1 -1
  76. data/lib/rubocop/cop/lint/constant_reassignment.rb +93 -11
  77. data/lib/rubocop/cop/lint/constant_resolution.rb +6 -6
  78. data/lib/rubocop/cop/lint/data_define_override.rb +63 -0
  79. data/lib/rubocop/cop/lint/debugger.rb +0 -1
  80. data/lib/rubocop/cop/lint/deprecated_constants.rb +2 -8
  81. data/lib/rubocop/cop/lint/duplicate_methods.rb +55 -8
  82. data/lib/rubocop/cop/lint/empty_block.rb +4 -4
  83. data/lib/rubocop/cop/lint/empty_conditional_body.rb +6 -1
  84. data/lib/rubocop/cop/lint/empty_in_pattern.rb +8 -1
  85. data/lib/rubocop/cop/lint/empty_when.rb +8 -1
  86. data/lib/rubocop/cop/lint/ensure_return.rb +19 -1
  87. data/lib/rubocop/cop/lint/erb_new_arguments.rb +4 -2
  88. data/lib/rubocop/cop/lint/float_comparison.rb +1 -0
  89. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +5 -1
  90. data/lib/rubocop/cop/lint/interpolation_check.rb +25 -5
  91. data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +1 -1
  92. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +11 -1
  93. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +8 -11
  94. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +5 -5
  95. data/lib/rubocop/cop/lint/multiple_comparison.rb +2 -2
  96. data/lib/rubocop/cop/lint/next_without_accumulator.rb +2 -0
  97. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +16 -0
  98. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +4 -2
  99. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +1 -1
  100. data/lib/rubocop/cop/lint/number_conversion.rb +19 -10
  101. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -1
  102. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +3 -0
  103. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +7 -7
  104. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +3 -13
  105. data/lib/rubocop/cop/lint/raise_exception.rb +1 -1
  106. data/lib/rubocop/cop/lint/rand_one.rb +1 -1
  107. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +4 -1
  108. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +6 -12
  109. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +15 -4
  110. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +36 -12
  111. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +4 -0
  112. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +10 -3
  113. data/lib/rubocop/cop/lint/redundant_with_index.rb +1 -1
  114. data/lib/rubocop/cop/lint/redundant_with_object.rb +5 -0
  115. data/lib/rubocop/cop/lint/refinement_import_methods.rb +8 -1
  116. data/lib/rubocop/cop/lint/regexp_as_condition.rb +9 -1
  117. data/lib/rubocop/cop/lint/require_parentheses.rb +13 -4
  118. data/lib/rubocop/cop/lint/require_range_parentheses.rb +2 -1
  119. data/lib/rubocop/cop/lint/require_relative_self_path.rb +7 -5
  120. data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
  121. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +18 -0
  122. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +7 -1
  123. data/lib/rubocop/cop/lint/safe_navigation_with_empty.rb +1 -1
  124. data/lib/rubocop/cop/lint/script_permission.rb +5 -1
  125. data/lib/rubocop/cop/lint/self_assignment.rb +24 -1
  126. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +1 -1
  127. data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -1
  128. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +14 -0
  129. data/lib/rubocop/cop/lint/shared_mutable_default.rb +3 -1
  130. data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +12 -0
  131. data/lib/rubocop/cop/lint/symbol_conversion.rb +21 -4
  132. data/lib/rubocop/cop/lint/syntax.rb +25 -1
  133. data/lib/rubocop/cop/lint/to_enum_arguments.rb +35 -2
  134. data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +1 -1
  135. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +5 -1
  136. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +36 -10
  137. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -0
  138. data/lib/rubocop/cop/lint/unreachable_code.rb +2 -2
  139. data/lib/rubocop/cop/lint/unreachable_pattern_branch.rb +113 -0
  140. data/lib/rubocop/cop/lint/unused_method_argument.rb +10 -0
  141. data/lib/rubocop/cop/lint/useless_assignment.rb +14 -14
  142. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +4 -4
  143. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +2 -0
  144. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +8 -4
  145. data/lib/rubocop/cop/lint/useless_setter_call.rb +4 -1
  146. data/lib/rubocop/cop/lint/useless_times.rb +22 -1
  147. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +35 -9
  148. data/lib/rubocop/cop/lint/void.rb +32 -12
  149. data/lib/rubocop/cop/metrics/block_length.rb +1 -1
  150. data/lib/rubocop/cop/metrics/block_nesting.rb +23 -0
  151. data/lib/rubocop/cop/metrics/collection_literal_length.rb +1 -1
  152. data/lib/rubocop/cop/metrics/method_length.rb +2 -2
  153. data/lib/rubocop/cop/metrics/perceived_complexity.rb +38 -7
  154. data/lib/rubocop/cop/metrics/utils/iterating_block.rb +1 -1
  155. data/lib/rubocop/cop/migration/department_name.rb +12 -1
  156. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  157. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +2 -2
  158. data/lib/rubocop/cop/mixin/configurable_max.rb +6 -5
  159. data/lib/rubocop/cop/mixin/hash_subset.rb +8 -0
  160. data/lib/rubocop/cop/mixin/hash_transform_method/autocorrection.rb +63 -0
  161. data/lib/rubocop/cop/mixin/hash_transform_method.rb +14 -60
  162. data/lib/rubocop/cop/mixin/project_index_help.rb +48 -0
  163. data/lib/rubocop/cop/mixin.rb +86 -0
  164. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +1 -1
  165. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  166. data/lib/rubocop/cop/naming/file_name.rb +4 -3
  167. data/lib/rubocop/cop/naming/inclusive_language.rb +8 -2
  168. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +10 -1
  169. data/lib/rubocop/cop/naming/predicate_method.rb +2 -2
  170. data/lib/rubocop/cop/naming/predicate_prefix.rb +1 -1
  171. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +10 -4
  172. data/lib/rubocop/cop/offense.rb +8 -0
  173. data/lib/rubocop/cop/registry.rb +62 -38
  174. data/lib/rubocop/cop/security/eval.rb +15 -2
  175. data/lib/rubocop/cop/security/io_methods.rb +2 -2
  176. data/lib/rubocop/cop/security/marshal_load.rb +1 -1
  177. data/lib/rubocop/cop/style/access_modifier_declarations.rb +14 -2
  178. data/lib/rubocop/cop/style/accessor_grouping.rb +15 -3
  179. data/lib/rubocop/cop/style/alias.rb +15 -3
  180. data/lib/rubocop/cop/style/and_or.rb +2 -1
  181. data/lib/rubocop/cop/style/arguments_forwarding.rb +25 -7
  182. data/lib/rubocop/cop/style/array_first_last.rb +12 -1
  183. data/lib/rubocop/cop/style/array_intersect.rb +4 -0
  184. data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +3 -0
  185. data/lib/rubocop/cop/style/array_join.rb +4 -2
  186. data/lib/rubocop/cop/style/ascii_comments.rb +6 -3
  187. data/lib/rubocop/cop/style/attr.rb +5 -2
  188. data/lib/rubocop/cop/style/bare_percent_literals.rb +3 -1
  189. data/lib/rubocop/cop/style/begin_block.rb +3 -1
  190. data/lib/rubocop/cop/style/block_delimiters.rb +37 -31
  191. data/lib/rubocop/cop/style/case_equality.rb +18 -2
  192. data/lib/rubocop/cop/style/character_literal.rb +2 -2
  193. data/lib/rubocop/cop/style/class_and_module_children.rb +18 -2
  194. data/lib/rubocop/cop/style/class_equality_comparison.rb +21 -13
  195. data/lib/rubocop/cop/style/class_methods_definitions.rb +11 -5
  196. data/lib/rubocop/cop/style/collection_compact.rb +36 -16
  197. data/lib/rubocop/cop/style/colon_method_call.rb +16 -7
  198. data/lib/rubocop/cop/style/combinable_loops.rb +5 -0
  199. data/lib/rubocop/cop/style/comparable_clamp.rb +12 -1
  200. data/lib/rubocop/cop/style/concat_array_literals.rb +7 -1
  201. data/lib/rubocop/cop/style/conditional_assignment.rb +6 -5
  202. data/lib/rubocop/cop/style/constant_visibility.rb +4 -1
  203. data/lib/rubocop/cop/style/copyright.rb +22 -11
  204. data/lib/rubocop/cop/style/data_inheritance.rb +4 -0
  205. data/lib/rubocop/cop/style/date_time.rb +4 -4
  206. data/lib/rubocop/cop/style/dig_chain.rb +5 -0
  207. data/lib/rubocop/cop/style/dir_empty.rb +4 -0
  208. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +1 -1
  209. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +6 -1
  210. data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
  211. data/lib/rubocop/cop/style/each_with_object.rb +2 -0
  212. data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
  213. data/lib/rubocop/cop/style/empty_case_condition.rb +12 -2
  214. data/lib/rubocop/cop/style/empty_class_definition.rb +51 -21
  215. data/lib/rubocop/cop/style/empty_heredoc.rb +4 -0
  216. data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
  217. data/lib/rubocop/cop/style/empty_literal.rb +7 -2
  218. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +30 -20
  219. data/lib/rubocop/cop/style/encoding.rb +7 -1
  220. data/lib/rubocop/cop/style/end_block.rb +3 -1
  221. data/lib/rubocop/cop/style/endless_method.rb +8 -3
  222. data/lib/rubocop/cop/style/env_home.rb +4 -0
  223. data/lib/rubocop/cop/style/even_odd.rb +11 -1
  224. data/lib/rubocop/cop/style/exact_regexp_match.rb +8 -1
  225. data/lib/rubocop/cop/style/fetch_env_var.rb +1 -1
  226. data/lib/rubocop/cop/style/file_null.rb +4 -2
  227. data/lib/rubocop/cop/style/file_open.rb +84 -0
  228. data/lib/rubocop/cop/style/file_write.rb +21 -16
  229. data/lib/rubocop/cop/style/for.rb +3 -0
  230. data/lib/rubocop/cop/style/format_string.rb +17 -4
  231. data/lib/rubocop/cop/style/format_string_token.rb +29 -2
  232. data/lib/rubocop/cop/style/global_vars.rb +5 -2
  233. data/lib/rubocop/cop/style/guard_clause.rb +9 -6
  234. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +21 -5
  235. data/lib/rubocop/cop/style/hash_conversion.rb +1 -1
  236. data/lib/rubocop/cop/style/hash_lookup_method.rb +19 -7
  237. data/lib/rubocop/cop/style/hash_slice.rb +16 -0
  238. data/lib/rubocop/cop/style/hash_syntax.rb +2 -0
  239. data/lib/rubocop/cop/style/hash_transform_keys.rb +17 -7
  240. data/lib/rubocop/cop/style/hash_transform_values.rb +17 -7
  241. data/lib/rubocop/cop/style/if_inside_else.rb +16 -7
  242. data/lib/rubocop/cop/style/if_unless_modifier.rb +15 -4
  243. data/lib/rubocop/cop/style/if_with_semicolon.rb +16 -6
  244. data/lib/rubocop/cop/style/inline_comment.rb +5 -2
  245. data/lib/rubocop/cop/style/ip_addresses.rb +1 -2
  246. data/lib/rubocop/cop/style/keyword_arguments_merging.rb +4 -0
  247. data/lib/rubocop/cop/style/keyword_parameters_order.rb +7 -3
  248. data/lib/rubocop/cop/style/lambda.rb +7 -1
  249. data/lib/rubocop/cop/style/magic_comment_format.rb +3 -3
  250. data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +11 -0
  251. data/lib/rubocop/cop/style/map_into_array.rb +1 -1
  252. data/lib/rubocop/cop/style/map_join.rb +123 -0
  253. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +5 -3
  254. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +6 -2
  255. data/lib/rubocop/cop/style/method_def_parentheses.rb +1 -1
  256. data/lib/rubocop/cop/style/min_max_comparison.rb +4 -1
  257. data/lib/rubocop/cop/style/module_member_existence_check.rb +7 -14
  258. data/lib/rubocop/cop/style/multiline_if_then.rb +4 -2
  259. data/lib/rubocop/cop/style/multiline_memoization.rb +7 -1
  260. data/lib/rubocop/cop/style/multiline_method_signature.rb +11 -4
  261. data/lib/rubocop/cop/style/mutable_constant.rb +106 -12
  262. data/lib/rubocop/cop/style/nil_comparison.rb +2 -3
  263. data/lib/rubocop/cop/style/nil_lambda.rb +9 -1
  264. data/lib/rubocop/cop/style/non_nil_check.rb +5 -11
  265. data/lib/rubocop/cop/style/not.rb +2 -0
  266. data/lib/rubocop/cop/style/numeric_literals.rb +3 -2
  267. data/lib/rubocop/cop/style/numeric_predicate.rb +1 -1
  268. data/lib/rubocop/cop/style/one_class_per_file.rb +115 -0
  269. data/lib/rubocop/cop/style/one_line_conditional.rb +4 -3
  270. data/lib/rubocop/cop/style/open_struct_use.rb +1 -1
  271. data/lib/rubocop/cop/style/option_hash.rb +1 -1
  272. data/lib/rubocop/cop/style/optional_arguments.rb +1 -0
  273. data/lib/rubocop/cop/style/parallel_assignment.rb +23 -3
  274. data/lib/rubocop/cop/style/partition_instead_of_double_select.rb +270 -0
  275. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +4 -0
  276. data/lib/rubocop/cop/style/perl_backrefs.rb +5 -3
  277. data/lib/rubocop/cop/style/predicate_with_kind.rb +84 -0
  278. data/lib/rubocop/cop/style/proc.rb +3 -2
  279. data/lib/rubocop/cop/style/raise_args.rb +1 -1
  280. data/lib/rubocop/cop/style/reduce_to_hash.rb +200 -0
  281. data/lib/rubocop/cop/style/redundant_array_constructor.rb +2 -2
  282. data/lib/rubocop/cop/style/redundant_begin.rb +3 -3
  283. data/lib/rubocop/cop/style/redundant_constant_base.rb +5 -5
  284. data/lib/rubocop/cop/style/redundant_each.rb +3 -3
  285. data/lib/rubocop/cop/style/redundant_exception.rb +6 -0
  286. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -1
  287. data/lib/rubocop/cop/style/redundant_filter_chain.rb +1 -1
  288. data/lib/rubocop/cop/style/redundant_format.rb +29 -0
  289. data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +26 -10
  290. data/lib/rubocop/cop/style/redundant_line_continuation.rb +27 -3
  291. data/lib/rubocop/cop/style/redundant_min_max_by.rb +93 -0
  292. data/lib/rubocop/cop/style/redundant_parentheses.rb +25 -22
  293. data/lib/rubocop/cop/style/redundant_percent_q.rb +4 -1
  294. data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +2 -2
  295. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -4
  296. data/lib/rubocop/cop/style/redundant_return.rb +3 -1
  297. data/lib/rubocop/cop/style/redundant_self.rb +11 -2
  298. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +0 -5
  299. data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +133 -0
  300. data/lib/rubocop/cop/style/regexp_literal.rb +31 -2
  301. data/lib/rubocop/cop/style/rescue_modifier.rb +3 -3
  302. data/lib/rubocop/cop/style/safe_navigation.rb +7 -7
  303. data/lib/rubocop/cop/style/select_by_kind.rb +158 -0
  304. data/lib/rubocop/cop/style/select_by_range.rb +197 -0
  305. data/lib/rubocop/cop/style/select_by_regexp.rb +51 -21
  306. data/lib/rubocop/cop/style/self_assignment.rb +1 -1
  307. data/lib/rubocop/cop/style/semicolon.rb +22 -5
  308. data/lib/rubocop/cop/style/single_line_block_params.rb +2 -2
  309. data/lib/rubocop/cop/style/single_line_do_end_block.rb +18 -5
  310. data/lib/rubocop/cop/style/single_line_methods.rb +3 -1
  311. data/lib/rubocop/cop/style/sole_nested_conditional.rb +4 -2
  312. data/lib/rubocop/cop/style/special_global_vars.rb +6 -1
  313. data/lib/rubocop/cop/style/string_hash_keys.rb +1 -0
  314. data/lib/rubocop/cop/style/struct_inheritance.rb +13 -0
  315. data/lib/rubocop/cop/style/symbol_proc.rb +7 -6
  316. data/lib/rubocop/cop/style/tally_method.rb +181 -0
  317. data/lib/rubocop/cop/style/ternary_parentheses.rb +11 -0
  318. data/lib/rubocop/cop/style/top_level_method_definition.rb +2 -2
  319. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  320. data/lib/rubocop/cop/style/trailing_method_end_statement.rb +1 -0
  321. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +7 -8
  322. data/lib/rubocop/cop/style/unless_logical_operators.rb +3 -3
  323. data/lib/rubocop/cop/style/while_until_do.rb +7 -0
  324. data/lib/rubocop/cop/style/while_until_modifier.rb +16 -0
  325. data/lib/rubocop/cop/style/word_array.rb +1 -0
  326. data/lib/rubocop/cop/style/yoda_condition.rb +1 -1
  327. data/lib/rubocop/cop/style/yoda_expression.rb +1 -1
  328. data/lib/rubocop/cop/style/zero_length_predicate.rb +6 -3
  329. data/lib/rubocop/cop/team.rb +86 -35
  330. data/lib/rubocop/cop/variable_force/branch.rb +2 -2
  331. data/lib/rubocop/directive_comment.rb +2 -1
  332. data/lib/rubocop/file_patterns.rb +9 -1
  333. data/lib/rubocop/formatter/disabled_config_formatter.rb +19 -9
  334. data/lib/rubocop/formatter/formatter_set.rb +1 -1
  335. data/lib/rubocop/formatter/junit_formatter.rb +1 -1
  336. data/lib/rubocop/formatter/simple_text_formatter.rb +0 -2
  337. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
  338. data/lib/rubocop/formatter.rb +22 -21
  339. data/lib/rubocop/lsp/diagnostic.rb +1 -0
  340. data/lib/rubocop/lsp/routes.rb +10 -3
  341. data/lib/rubocop/lsp/runtime.rb +1 -2
  342. data/lib/rubocop/mcp/server.rb +200 -0
  343. data/lib/rubocop/options.rb +35 -4
  344. data/lib/rubocop/path_util.rb +14 -2
  345. data/lib/rubocop/plugin/loader.rb +1 -1
  346. data/lib/rubocop/project_index_loader.rb +66 -0
  347. data/lib/rubocop/result_cache.rb +22 -10
  348. data/lib/rubocop/rspec/cop_helper.rb +8 -0
  349. data/lib/rubocop/rspec/shared_contexts.rb +32 -2
  350. data/lib/rubocop/runner.rb +126 -53
  351. data/lib/rubocop/server/cache.rb +5 -7
  352. data/lib/rubocop/server/core.rb +8 -0
  353. data/lib/rubocop/target_finder.rb +14 -7
  354. data/lib/rubocop/target_ruby.rb +18 -12
  355. data/lib/rubocop/version.rb +21 -3
  356. data/lib/rubocop.rb +22 -96
  357. metadata +27 -5
@@ -4,8 +4,8 @@ module RuboCop
4
4
  module Cop
5
5
  module Lint
6
6
  # Checks for unreachable code.
7
- # The check are based on the presence of flow of control
8
- # statement in non-final position in `begin` (implicit) blocks.
7
+ # The check is based on the presence of flow-of-control
8
+ # statements in non-final position in `begin` (implicit) blocks.
9
9
  #
10
10
  # @example
11
11
  #
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Checks for unreachable `in` pattern branches in `case...in` statements.
7
+ #
8
+ # An `in` branch is unreachable when a previous branch uses an unguarded
9
+ # catch-all pattern that matches any value unconditionally. Any `in` branches
10
+ # (and `else`) that follow such a catch-all are dead code.
11
+ #
12
+ # A catch-all pattern is one of:
13
+ #
14
+ # * A bare variable capture (`in x`)
15
+ # * An underscore (`in _`)
16
+ # * A pattern alias where the left side is a catch-all (`in _ => y`)
17
+ # * An alternation pattern where at least one alternative is a catch-all
18
+ # (`in _ | Integer`)
19
+ #
20
+ # NOTE: A catch-all pattern with a guard clause (e.g., `in _ if condition`)
21
+ # does NOT make subsequent branches unreachable because the guard might
22
+ # not be satisfied.
23
+ #
24
+ # @example
25
+ #
26
+ # # bad
27
+ # case value
28
+ # in Integer
29
+ # handle_integer
30
+ # in x
31
+ # handle_other
32
+ # in String
33
+ # handle_string
34
+ # else
35
+ # handle_else
36
+ # end
37
+ #
38
+ # # good
39
+ # case value
40
+ # in Integer
41
+ # handle_integer
42
+ # in String
43
+ # handle_string
44
+ # in x
45
+ # handle_other
46
+ # end
47
+ #
48
+ # # bad - else is unreachable after catch-all
49
+ # case value
50
+ # in Integer
51
+ # handle_integer
52
+ # in _
53
+ # handle_other
54
+ # else
55
+ # handle_else
56
+ # end
57
+ #
58
+ # # good - guard clause means catch-all might not match
59
+ # case value
60
+ # in x if x.positive?
61
+ # handle_positive
62
+ # in Integer
63
+ # handle_integer
64
+ # else
65
+ # handle_other
66
+ # end
67
+ #
68
+ class UnreachablePatternBranch < Base
69
+ extend TargetRubyVersion
70
+
71
+ MSG = 'Unreachable `in` pattern branch detected.'
72
+ MSG_ELSE = 'Unreachable `else` branch detected.'
73
+
74
+ minimum_target_ruby_version 2.7
75
+
76
+ def on_case_match(case_node)
77
+ catch_all_found = false
78
+
79
+ case_node.in_pattern_branches.each do |in_pattern_node|
80
+ if catch_all_found
81
+ add_offense(in_pattern_node)
82
+ next
83
+ end
84
+
85
+ pattern = in_pattern_node.pattern
86
+ guard = in_pattern_node.children[1]
87
+
88
+ catch_all_found = true if catch_all_pattern?(pattern) && guard.nil?
89
+ end
90
+
91
+ return unless catch_all_found && case_node.else?
92
+
93
+ add_offense(case_node.loc.else, message: MSG_ELSE)
94
+ end
95
+
96
+ private
97
+
98
+ def catch_all_pattern?(pattern)
99
+ case pattern.type
100
+ when :match_var
101
+ true
102
+ when :match_as, :begin
103
+ catch_all_pattern?(pattern.children[0])
104
+ when :match_alt
105
+ pattern.children.any? { |child| catch_all_pattern?(child) }
106
+ else
107
+ false
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -95,10 +95,20 @@ module RuboCop
95
95
  return unless variable.method_argument?
96
96
  return if variable.keyword_argument? && cop_config['AllowUnusedKeywordArguments']
97
97
  return if ignored_method?(variable.scope.node.body)
98
+ return if block_argument_with_yield?(variable)
98
99
 
99
100
  super
100
101
  end
101
102
 
103
+ def block_argument_with_yield?(variable)
104
+ return false unless variable.declaration_node.blockarg_type?
105
+
106
+ method_body = variable.scope.node.body
107
+ return false unless method_body
108
+
109
+ method_body.yield_type? || method_body.each_descendant(:yield).any?
110
+ end
111
+
102
112
  def ignored_method?(body)
103
113
  (cop_config['IgnoreEmptyMethods'] && body.nil?) ||
104
114
  (cop_config['IgnoreNotImplementedMethods'] && not_implemented?(body))
@@ -40,8 +40,6 @@ module RuboCop
40
40
  class UselessAssignment < Base
41
41
  extend AutoCorrector
42
42
 
43
- include RangeHelp
44
-
45
43
  MSG = 'Useless assignment to variable - `%<variable>s`.'
46
44
 
47
45
  def self.joining_forces
@@ -69,11 +67,7 @@ module RuboCop
69
67
  range = offense_range(assignment)
70
68
 
71
69
  add_offense(range, message: message) do |corrector|
72
- # In cases like `x = 1, y = 2`, where removing a variable would cause a syntax error,
73
- # and where changing `x ||= 1` to `x = 1` would cause `NameError`,
74
- # the autocorrect will be skipped, even if the variable is unused.
75
- next if sequential_assignment?(assignment_node) ||
76
- assignment_node.parent&.or_asgn_type?
70
+ next if uncorrectable_assignment?(assignment_node)
77
71
 
78
72
  autocorrect(corrector, assignment)
79
73
  end
@@ -81,6 +75,15 @@ module RuboCop
81
75
  ignore_node(assignment_node) if chained_assignment?(assignment_node)
82
76
  end
83
77
 
78
+ # Autocorrect is skipped when removing a variable would cause a syntax error
79
+ # (`x = 1, y = 2`), or where rewriting `x ||= 1`/`x &&= 1` to `x = 1` would raise
80
+ # `NameError` because the variable is not declared before the operator assignment.
81
+ def uncorrectable_assignment?(assignment_node)
82
+ sequential_assignment?(assignment_node) ||
83
+ assignment_node.parent&.or_asgn_type? ||
84
+ assignment_node.parent&.and_asgn_type?
85
+ end
86
+
84
87
  def ignored_assignment?(variable, assignment_node, assignment)
85
88
  assignment.used? || part_of_ignored_node?(assignment_node) ||
86
89
  variable_in_loop_condition?(assignment_node, variable)
@@ -189,12 +192,9 @@ module RuboCop
189
192
  # rubocop:enable Metrics/AbcSize
190
193
 
191
194
  def remove_exception_assignment_part(corrector, node)
192
- corrector.remove(
193
- range_between(
194
- (node.parent.children.first&.source_range || node.parent.location.keyword).end_pos,
195
- node.source_range.end_pos
196
- )
197
- )
195
+ range = node.parent.children.first&.source_range || node.parent.location.keyword
196
+
197
+ corrector.remove(range.end.join(node.source_range.end))
198
198
  end
199
199
 
200
200
  def rename_variable_with_underscore(corrector, node)
@@ -213,7 +213,7 @@ module RuboCop
213
213
  end
214
214
 
215
215
  def remove_local_variable_assignment_part(corrector, node)
216
- corrector.replace(node, node.expression.source)
216
+ corrector.remove(node.loc.name.begin.join(node.expression.source_range.begin))
217
217
  end
218
218
 
219
219
  def variable_in_loop_condition?(assignment_node, variable)
@@ -48,12 +48,12 @@ module RuboCop
48
48
 
49
49
  def after_private_modifier?(left_siblings)
50
50
  access_modifier_candidates = left_siblings.compact.select do |left_sibling|
51
- left_sibling.respond_to?(:send_type?) && left_sibling.send_type?
51
+ left_sibling.respond_to?(:bare_access_modifier?) && left_sibling.bare_access_modifier?
52
52
  end
53
53
 
54
- access_modifier_candidates.any? do |candidate|
55
- candidate.command?(:private) && candidate.arguments.none?
56
- end
54
+ return false if access_modifier_candidates.empty?
55
+
56
+ access_modifier_candidates.last.command?(:private)
57
57
  end
58
58
 
59
59
  def private_constantize?(right_siblings, const_value)
@@ -67,6 +67,8 @@ module RuboCop
67
67
  PATTERN
68
68
 
69
69
  def on_send(node)
70
+ return unless node.receiver
71
+
70
72
  unless (prev_arg_node, default_value_node = default_value_argument_and_block(node.parent))
71
73
  return
72
74
  end
@@ -6,7 +6,7 @@ module RuboCop
6
6
  # Looks for `ruby2_keywords` calls for methods that do not need it.
7
7
  #
8
8
  # `ruby2_keywords` should only be called on methods that accept an argument splat
9
- # (`\*args`) but do not explicit keyword arguments (`k:` or `k: true`) or
9
+ # (`\*args`) but do not have explicit keyword arguments (`k:` or `k: true`) or
10
10
  # a keyword splat (`**kwargs`).
11
11
  #
12
12
  # @example
@@ -107,11 +107,15 @@ module RuboCop
107
107
  end
108
108
 
109
109
  def find_method_definition(node, method_name)
110
- node.each_ancestor.lazy.map do |ancestor|
111
- ancestor.each_child_node(:def, :any_block).find do |child|
110
+ node.each_ancestor do |ancestor|
111
+ found = ancestor.each_child_node(:def, :any_block).find do |child|
112
112
  method_definition(child, method_name)
113
113
  end
114
- end.find(&:itself)
114
+ return found if found
115
+ # A method defined in an outer lexical scope does not define this scope's method,
116
+ # so stop searching once a class/module boundary is crossed without a match.
117
+ return nil if ancestor.type?(:class, :module, :sclass)
118
+ end
115
119
  end
116
120
 
117
121
  # `ruby2_keywords` is only allowed if there's a `restarg` and no keyword arguments
@@ -107,7 +107,10 @@ module RuboCop
107
107
  end
108
108
 
109
109
  def process_multiple_assignment(masgn_node)
110
- masgn_node.assignments.each_with_index do |lhs_node, index|
110
+ # Iterate the top-level destructuring slots so each maps to the right-hand side
111
+ # element at the same position. Using the flattened `assignments` would misalign
112
+ # the index when a slot is itself a nested destructuring (e.g. `(a, b), c = x, y`).
113
+ masgn_node.lhs.children.each_with_index do |lhs_node, index|
111
114
  next unless ASSIGNMENT_TYPES.include?(lhs_node.type)
112
115
 
113
116
  if masgn_node.rhs.array_type? && (rhs_node = masgn_node.rhs.children[index])
@@ -83,7 +83,7 @@ module RuboCop
83
83
 
84
84
  def autocorrect_block(corrector, node)
85
85
  block_arg = block_arg(node)
86
- return if block_reassigns_arg?(node, block_arg)
86
+ return unless reducible_to_body?(node, block_arg)
87
87
 
88
88
  source = node.body.source
89
89
  source.gsub!(/\b#{block_arg}\b/, '0') if block_arg
@@ -91,6 +91,27 @@ module RuboCop
91
91
  corrector.replace(node, fix_indentation(source, node.loc.column...node.body.loc.column))
92
92
  end
93
93
 
94
+ def reducible_to_body?(node, block_arg)
95
+ # A block with multiple arguments can't be reduced to its body (the extra arguments
96
+ # would become undefined references), and `next`/`break`/`redo` bound to the block
97
+ # become orphaned (a syntax error) once the block is removed.
98
+ return false if node.arguments.size > 1 || orphans_loop_control_keyword?(node)
99
+
100
+ # A lone non-simple argument (destructuring `|(a, b)|` or a splat `|*a|`) can't be
101
+ # substituted either, so reducing to the body would leave it referencing an
102
+ # undefined variable.
103
+ return false if node.arguments.one? && block_arg.nil?
104
+
105
+ !block_reassigns_arg?(node, block_arg)
106
+ end
107
+
108
+ def orphans_loop_control_keyword?(node)
109
+ node.body&.each_node(:next, :break, :redo)&.any? do |control|
110
+ inner = control.each_ancestor.take_while { |ancestor| !ancestor.equal?(node) }
111
+ inner.none? { |ancestor| ancestor.type?(:any_block, :while, :until, :for) }
112
+ end
113
+ end
114
+
94
115
  def fix_indentation(source, range)
95
116
  # Cleanup indentation in a multiline block
96
117
  source_lines = source.split("\n")
@@ -60,9 +60,7 @@ module RuboCop
60
60
  return true if _cant_be_nil?(node.expression, receiver)
61
61
  end
62
62
 
63
- # Due to how `if/else` are implemented (`elsif` is a child of `if` or another `elsif`),
64
- # using left_siblings will not work correctly for them.
65
- if !else_branch?(node) || (node.if_type? && !node.elsif?)
63
+ if sequentially_reached?(node)
66
64
  node.left_siblings.reverse_each do |sibling|
67
65
  next unless sibling.is_a?(AST::Node)
68
66
 
@@ -82,28 +80,48 @@ module RuboCop
82
80
  !NIL_METHODS.include?(method_name) && !@additional_nil_methods.include?(method_name)
83
81
  end
84
82
 
85
- # rubocop:disable Metrics/PerceivedComplexity
83
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
86
84
  def sole_condition_of_parent_if?(node)
85
+ child = node
87
86
  parent = node.parent
88
87
 
89
88
  while parent
90
89
  if parent.if_type?
91
- if parent.condition == node
92
- return true
93
- elsif parent.elsif?
94
- parent = find_top_if(parent)
90
+ unless parent.unless?
91
+ condition = parent.condition
92
+ return true if !child.equal?(condition) && non_nil_condition?(condition, node)
95
93
  end
94
+
95
+ parent = find_top_if(parent) if parent.elsif?
96
96
  elsif else_branch?(parent)
97
97
  # Find the top `if` for `else`.
98
98
  parent = parent.parent
99
99
  end
100
100
 
101
+ child = parent
101
102
  parent = parent&.parent
102
103
  end
103
104
 
104
105
  false
105
106
  end
106
- # rubocop:enable Metrics/PerceivedComplexity
107
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
108
+
109
+ def non_nil_condition?(condition, node)
110
+ return true if condition == node
111
+
112
+ condition.csend_type? && csend_root_receiver(condition) == node
113
+ end
114
+
115
+ # Whether control reaches `node` by falling through its left siblings rather than by
116
+ # a non-sequential entry. A `resbody` is entered via an exception, the `ensure` branch
117
+ # runs even after a partway raise, and the `else` arm of an `if/elsif` chain is reached by
118
+ # branching (its `if/case` siblings are walked via parent recursion instead).
119
+ def sequentially_reached?(node)
120
+ return false if node.resbody_type?
121
+ return false if node.parent&.ensure_type? && node.parent.branch.equal?(node)
122
+
123
+ !else_branch?(node) || (node.if_type? && !node.elsif?)
124
+ end
107
125
 
108
126
  def else_branch?(node)
109
127
  node.parent&.if_type? && node.parent.else_branch == node
@@ -114,6 +132,14 @@ module RuboCop
114
132
 
115
133
  node
116
134
  end
135
+
136
+ def csend_root_receiver(node)
137
+ return unless (receiver = node.receiver)
138
+
139
+ receiver = receiver.receiver while receiver.call_type? && receiver.receiver
140
+
141
+ receiver
142
+ end
117
143
  end
118
144
  end
119
145
  end
@@ -87,8 +87,9 @@ module RuboCop
87
87
  def on_block(node)
88
88
  return unless node.body && !node.body.begin_type?
89
89
  return unless in_void_context?(node.body)
90
+ return if node.method?(:each)
90
91
 
91
- check_void_op(node.body) { node.method?(:each) }
92
+ check_void_op(node.body)
92
93
  check_expression(node.body)
93
94
  end
94
95
  alias on_numblock on_block
@@ -107,22 +108,23 @@ module RuboCop
107
108
 
108
109
  def check_begin(node)
109
110
  expressions = *node
110
- expressions.pop unless in_void_context?(node)
111
+ inside_each_block = node.each_ancestor(:any_block).first&.method?(:each)
112
+ expressions.pop if !in_void_context?(node) || inside_each_block
111
113
  expressions.each do |expr|
112
- check_void_op(expr) do
113
- block_node = node.each_ancestor(:any_block).first
114
-
115
- block_node&.method?(:each)
116
- end
117
-
114
+ check_void_op(expr) { inside_each_block }
118
115
  check_expression(expr)
119
116
  end
120
117
  end
121
118
 
122
119
  def check_expression(expr)
123
- expr = expr.body if expr.if_type?
124
- return unless expr
120
+ return check_if_expression(expr) if expr.if_type?
121
+ return check_case_expression(expr) if expr.case_type?
122
+ return check_case_match_expression(expr) if expr.case_match_type?
123
+
124
+ check_void_expression_nodes(expr)
125
+ end
125
126
 
127
+ def check_void_expression_nodes(expr)
126
128
  check_literal(expr)
127
129
  check_var(expr)
128
130
  check_self(expr)
@@ -132,6 +134,22 @@ module RuboCop
132
134
  check_nonmutating(expr)
133
135
  end
134
136
 
137
+ def check_if_expression(if_node)
138
+ check_void_expression_nodes(if_node.body) if if_node.body
139
+ end
140
+
141
+ def check_case_expression(case_node)
142
+ case_node.each_when { |when_node| check_expression(when_node.body) if when_node.body }
143
+ check_expression(case_node.else_branch) if case_node.else_branch
144
+ end
145
+
146
+ def check_case_match_expression(case_node)
147
+ case_node.each_in_pattern do |in_pattern_node|
148
+ check_expression(in_pattern_node.body) if in_pattern_node.body
149
+ end
150
+ check_expression(case_node.else_branch) if case_node.else_branch
151
+ end
152
+
135
153
  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
136
154
  def check_void_op(node, &block)
137
155
  node = node.children.first while node&.begin_type?
@@ -168,7 +186,9 @@ module RuboCop
168
186
  end
169
187
 
170
188
  def check_literal(node)
171
- return if !entirely_literal?(node) || node.xstr_type? || node.range_type?
189
+ if !entirely_literal?(node) || node.xstr_type? || node.range_type? || node.nil_type?
190
+ return
191
+ end
172
192
 
173
193
  add_offense(node, message: format(LIT_MSG, lit: node.source)) do |corrector|
174
194
  autocorrect_void_expression(corrector, node)
@@ -238,7 +258,7 @@ module RuboCop
238
258
  end
239
259
 
240
260
  def autocorrect_void_expression(corrector, node)
241
- return if node.parent.if_type?
261
+ return if node.parent.type?(:if, :case, :when, :case_match, :in_pattern)
242
262
  return if (def_node = node.each_ancestor(:any_def).first) && def_node.assignment_method?
243
263
 
244
264
  corrector.remove(range_with_surrounding_space(range: node.source_range, side: :left))
@@ -17,7 +17,7 @@ module RuboCop
17
17
  #
18
18
  # NOTE: The `ExcludedMethods` configuration is deprecated and only kept
19
19
  # for backwards compatibility. Please use `AllowedMethods` and `AllowedPatterns`
20
- # instead. By default, there are no methods to allowed.
20
+ # instead. By default, there are no allowed methods.
21
21
  #
22
22
  # @example CountAsOne: ['array', 'hash', 'heredoc', 'method_call']
23
23
  #
@@ -4,6 +4,8 @@ module RuboCop
4
4
  module Cop
5
5
  module Metrics
6
6
  # Checks for excessive nesting of conditional and looping constructs.
7
+ # Deeply nested code is harder to read, understand, and maintain.
8
+ # Extracting nested logic into methods improves clarity.
7
9
  #
8
10
  # You can configure if blocks are considered using the `CountBlocks` and `CountModifierForms`
9
11
  # options. When both are set to `false` (the default) blocks and modifier forms are not
@@ -11,6 +13,27 @@ module RuboCop
11
13
  # calculation as well.
12
14
  #
13
15
  # The maximum level of nesting allowed is configurable.
16
+ #
17
+ # @example Max: 3 (default)
18
+ # # bad
19
+ # if condition1
20
+ # if condition2
21
+ # if condition3
22
+ # if condition4
23
+ # do_something
24
+ # end
25
+ # end
26
+ # end
27
+ # end
28
+ #
29
+ # # good
30
+ # if condition1
31
+ # if condition2
32
+ # if condition3
33
+ # do_something
34
+ # end
35
+ # end
36
+ # end
14
37
  class BlockNesting < Base
15
38
  NESTING_BLOCKS = %i[case case_match if while while_post until until_post for resbody].freeze
16
39
 
@@ -75,7 +75,7 @@ module RuboCop
75
75
  private
76
76
 
77
77
  def collection_threshold
78
- cop_config.fetch('LengthThreshold', Float::INFINITY)
78
+ cop_config.fetch('Max', Float::INFINITY)
79
79
  end
80
80
  end
81
81
  end
@@ -15,7 +15,7 @@ module RuboCop
15
15
  # NOTE: The `ExcludedMethods` and `IgnoredMethods` configuration is
16
16
  # deprecated and only kept for backwards compatibility.
17
17
  # Please use `AllowedMethods` and `AllowedPatterns` instead.
18
- # By default, there are no methods to allowed.
18
+ # By default, there are no allowed methods.
19
19
  #
20
20
  # @example CountAsOne: ['array', 'hash', 'heredoc', 'method_call']
21
21
  #
@@ -58,7 +58,7 @@ module RuboCop
58
58
  return unless node.method?(:define_method)
59
59
 
60
60
  method_name = node.send_node.first_argument
61
- return if method_name.basic_literal? && allowed?(method_name.value)
61
+ return if method_name&.basic_literal? && allowed?(method_name.value)
62
62
 
63
63
  check_code_length(node)
64
64
  end
@@ -12,9 +12,15 @@ module RuboCop
12
12
  # nodes count. In contrast to the CyclomaticComplexity cop, this cop
13
13
  # considers `else` nodes as adding complexity.
14
14
  #
15
+ # A `case`/`in` branch whose pattern is a simple literal (e.g. `in 1`, `in "red"`, `in 1..10`)
16
+ # or a constant/type (e.g. `in Integer`) and has no guard is just as easy to read as a `when`
17
+ # branch, so it is discounted the same way. Branches with structural patterns (e.g. array,
18
+ # hash, or find patterns), bindings, alternatives, or a guard add the full complexity of
19
+ # a decision point.
20
+ #
15
21
  # @example
16
22
  #
17
- # def my_method # 1
23
+ # def example_1 # 1
18
24
  # if cond # 1
19
25
  # case var # 2 (0.8 + 4 * 0.2, rounded)
20
26
  # when 1 then func_one
@@ -26,33 +32,58 @@ module RuboCop
26
32
  # do_something until a && b # 2
27
33
  # end # ===
28
34
  # end # 7 complexity points
35
+ #
36
+ # def example_2 # 1
37
+ # case color # 1 (3 * 0.2, rounded)
38
+ # in "red" then func_red
39
+ # in "blue" then func_blue
40
+ # in "green" then func_green
41
+ # end # ===
42
+ # end # 2 complexity points
29
43
  class PerceivedComplexity < CyclomaticComplexity
30
44
  MSG = 'Perceived complexity for `%<method>s` is too high. [%<complexity>d/%<max>d]'
31
45
 
32
- COUNTED_NODES = (CyclomaticComplexity::COUNTED_NODES - [:when] + [:case]).freeze
46
+ COUNTED_NODES = (
47
+ CyclomaticComplexity::COUNTED_NODES - %i[when in_pattern] + %i[case case_match]
48
+ ).freeze
33
49
 
34
50
  private
35
51
 
52
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
36
53
  def complexity_score_for(node)
37
54
  case node.type
38
55
  when :case
39
- # If cond is nil, that means each when has an expression that
40
- # evaluates to true or false. It's just an alternative to
41
- # if/elsif/elsif... so the when nodes count.
56
+ # If cond is nil, that means each when has an expression that evaluates to true or
57
+ # false. It's just an alternative to if/elsif/elsif... so the when nodes count.
42
58
  nb_branches = node.when_branches.length + (node.else_branch ? 1 : 0)
43
59
  if node.condition.nil?
44
60
  nb_branches
45
61
  else
46
- # Otherwise, the case node gets 0.8 complexity points and each
47
- # when gets 0.2.
62
+ # Otherwise, the case node gets 0.8 complexity points and each when gets 0.2.
48
63
  ((nb_branches * 0.2) + 0.8).round
49
64
  end
65
+ when :case_match
66
+ # Simple `in` branches are discounted like `when`, while structural patterns keep
67
+ # the full complexity of a decision point.
68
+ score = node.in_pattern_branches.sum { |branch| simple_in_pattern?(branch) ? 0.2 : 1 }
69
+ score += 0.2 if node.else_branch
70
+ score.round
50
71
  when :if
51
72
  node.else? && !node.elsif? ? 2 : 1
52
73
  else
53
74
  super
54
75
  end
55
76
  end
77
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
78
+
79
+ def simple_in_pattern?(in_pattern_node)
80
+ # `in_pattern_node.children[1]` is the guard (`if`/`unless`), or `nil`.
81
+ return false unless in_pattern_node.children[1].nil?
82
+
83
+ # A scalar literal, a literal range, or a constant/type is as easy to read as a `when`.
84
+ pattern = in_pattern_node.pattern
85
+ pattern.literal? || pattern.const_type?
86
+ end
56
87
  end
57
88
  end
58
89
  end
@@ -43,7 +43,7 @@ module RuboCop
43
43
  end
44
44
  end
45
45
 
46
- # Returns true iff name is a known iterating type (e.g. :each, :transform_values)
46
+ # Returns true only when name is a known iterating type (e.g. :each, :transform_values).
47
47
  def iterating_method?(name)
48
48
  KNOWN_ITERATING_METHODS.include? name
49
49
  end