rubocop 1.79.2 → 1.88.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 (421) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +2 -2
  4. data/config/default.yml +259 -90
  5. data/config/obsoletion.yml +30 -1
  6. data/exe/rubocop +1 -8
  7. data/lib/rubocop/cache_config.rb +29 -0
  8. data/lib/rubocop/cli/command/auto_generate_config.rb +36 -4
  9. data/lib/rubocop/cli/command/list_enabled_cops_for.rb +40 -0
  10. data/lib/rubocop/cli/command/lsp.rb +1 -1
  11. data/lib/rubocop/cli/command/mcp.rb +19 -0
  12. data/lib/rubocop/cli/command/show_cops.rb +2 -2
  13. data/lib/rubocop/cli/command/show_docs_url.rb +4 -8
  14. data/lib/rubocop/cli/command/suggest_extensions.rb +1 -1
  15. data/lib/rubocop/cli.rb +35 -9
  16. data/lib/rubocop/comment_config.rb +59 -17
  17. data/lib/rubocop/config.rb +14 -10
  18. data/lib/rubocop/config_finder.rb +1 -1
  19. data/lib/rubocop/config_loader.rb +37 -23
  20. data/lib/rubocop/config_loader_resolver.rb +20 -10
  21. data/lib/rubocop/config_obsoletion/extracted_cop.rb +4 -2
  22. data/lib/rubocop/config_store.rb +7 -2
  23. data/lib/rubocop/config_validator.rb +1 -1
  24. data/lib/rubocop/cop/autocorrect_logic.rb +10 -5
  25. data/lib/rubocop/cop/base.rb +25 -4
  26. data/lib/rubocop/cop/bundler/gem_comment.rb +2 -2
  27. data/lib/rubocop/cop/bundler/gem_version.rb +28 -28
  28. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -2
  29. data/lib/rubocop/cop/correctors/alignment_corrector.rb +26 -7
  30. data/lib/rubocop/cop/correctors/condition_corrector.rb +1 -1
  31. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +7 -2
  32. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +1 -5
  33. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +33 -2
  34. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
  35. data/lib/rubocop/cop/correctors.rb +28 -0
  36. data/lib/rubocop/cop/documentation.rb +2 -3
  37. data/lib/rubocop/cop/exclude_limit.rb +31 -5
  38. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +2 -2
  39. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -2
  40. data/lib/rubocop/cop/gemspec/require_mfa.rb +5 -5
  41. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +12 -7
  42. data/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb +8 -8
  43. data/lib/rubocop/cop/internal_affairs/itblock_handler.rb +69 -0
  44. data/lib/rubocop/cop/internal_affairs/location_exists.rb +28 -2
  45. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +1 -0
  46. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +9 -9
  47. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_processor.rb +1 -1
  48. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +3 -1
  49. data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
  50. data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +5 -3
  51. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +4 -4
  52. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  53. data/lib/rubocop/cop/layout/argument_alignment.rb +2 -2
  54. data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
  55. data/lib/rubocop/cop/layout/begin_end_alignment.rb +1 -1
  56. data/lib/rubocop/cop/layout/block_alignment.rb +41 -4
  57. data/lib/rubocop/cop/layout/case_indentation.rb +3 -1
  58. data/lib/rubocop/cop/layout/class_structure.rb +14 -7
  59. data/lib/rubocop/cop/layout/dot_position.rb +2 -2
  60. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +26 -7
  61. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +31 -13
  62. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +2 -2
  63. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -0
  64. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +12 -2
  65. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +16 -2
  66. data/lib/rubocop/cop/layout/empty_lines_around_module_body.rb +16 -2
  67. data/lib/rubocop/cop/layout/end_alignment.rb +10 -3
  68. data/lib/rubocop/cop/layout/first_argument_indentation.rb +34 -1
  69. data/lib/rubocop/cop/layout/first_array_element_line_break.rb +26 -0
  70. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +7 -1
  71. data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +25 -25
  72. data/lib/rubocop/cop/layout/hash_alignment.rb +3 -6
  73. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
  74. data/lib/rubocop/cop/layout/heredoc_indentation.rb +33 -3
  75. data/lib/rubocop/cop/layout/indentation_style.rb +1 -1
  76. data/lib/rubocop/cop/layout/indentation_width.rb +123 -7
  77. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
  78. data/lib/rubocop/cop/layout/line_length.rb +26 -9
  79. data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +57 -57
  80. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +9 -2
  81. data/lib/rubocop/cop/layout/multiline_block_layout.rb +2 -0
  82. data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +56 -56
  83. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +1 -1
  84. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +229 -39
  85. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +8 -4
  86. data/lib/rubocop/cop/layout/parameter_alignment.rb +1 -1
  87. data/lib/rubocop/cop/layout/redundant_line_break.rb +3 -1
  88. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +13 -3
  89. data/lib/rubocop/cop/layout/space_after_comma.rb +2 -10
  90. data/lib/rubocop/cop/layout/space_after_semicolon.rb +1 -1
  91. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +1 -1
  92. data/lib/rubocop/cop/layout/space_around_keyword.rb +4 -2
  93. data/lib/rubocop/cop/layout/space_before_brackets.rb +1 -1
  94. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +9 -8
  95. data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
  96. data/lib/rubocop/cop/lint/ambiguous_assignment.rb +1 -11
  97. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  98. data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +1 -10
  99. data/lib/rubocop/cop/lint/circular_argument_reference.rb +45 -3
  100. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +4 -3
  101. data/lib/rubocop/cop/lint/constant_reassignment.rb +93 -11
  102. data/lib/rubocop/cop/lint/constant_resolution.rb +6 -6
  103. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +14 -8
  104. data/lib/rubocop/cop/lint/data_define_override.rb +63 -0
  105. data/lib/rubocop/cop/lint/debugger.rb +0 -3
  106. data/lib/rubocop/cop/lint/deprecated_constants.rb +2 -8
  107. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +4 -1
  108. data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +4 -4
  109. data/lib/rubocop/cop/lint/duplicate_methods.rb +111 -12
  110. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +5 -42
  111. data/lib/rubocop/cop/lint/else_layout.rb +19 -0
  112. data/lib/rubocop/cop/lint/empty_block.rb +4 -4
  113. data/lib/rubocop/cop/lint/empty_conditional_body.rb +6 -1
  114. data/lib/rubocop/cop/lint/empty_in_pattern.rb +8 -1
  115. data/lib/rubocop/cop/lint/empty_interpolation.rb +11 -0
  116. data/lib/rubocop/cop/lint/empty_when.rb +8 -1
  117. data/lib/rubocop/cop/lint/ensure_return.rb +19 -1
  118. data/lib/rubocop/cop/lint/erb_new_arguments.rb +4 -2
  119. data/lib/rubocop/cop/lint/float_comparison.rb +2 -1
  120. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +5 -1
  121. data/lib/rubocop/cop/lint/interpolation_check.rb +25 -5
  122. data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +1 -1
  123. data/lib/rubocop/cop/lint/literal_as_condition.rb +5 -1
  124. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +11 -1
  125. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +9 -12
  126. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +19 -10
  127. data/lib/rubocop/cop/lint/multiple_comparison.rb +2 -2
  128. data/lib/rubocop/cop/lint/next_without_accumulator.rb +2 -0
  129. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +20 -0
  130. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +4 -2
  131. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +1 -1
  132. data/lib/rubocop/cop/lint/number_conversion.rb +19 -10
  133. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -1
  134. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +3 -0
  135. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +7 -7
  136. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +3 -13
  137. data/lib/rubocop/cop/lint/raise_exception.rb +1 -1
  138. data/lib/rubocop/cop/lint/rand_one.rb +1 -1
  139. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +27 -10
  140. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +6 -12
  141. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +15 -4
  142. data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -2
  143. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +36 -12
  144. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +12 -2
  145. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +10 -3
  146. data/lib/rubocop/cop/lint/redundant_with_index.rb +1 -1
  147. data/lib/rubocop/cop/lint/redundant_with_object.rb +5 -0
  148. data/lib/rubocop/cop/lint/refinement_import_methods.rb +8 -1
  149. data/lib/rubocop/cop/lint/regexp_as_condition.rb +9 -1
  150. data/lib/rubocop/cop/lint/require_parentheses.rb +13 -4
  151. data/lib/rubocop/cop/lint/require_range_parentheses.rb +2 -1
  152. data/lib/rubocop/cop/lint/require_relative_self_path.rb +7 -5
  153. data/lib/rubocop/cop/lint/rescue_exception.rb +1 -4
  154. data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
  155. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +18 -0
  156. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +7 -1
  157. data/lib/rubocop/cop/lint/safe_navigation_with_empty.rb +1 -1
  158. data/lib/rubocop/cop/lint/script_permission.rb +5 -1
  159. data/lib/rubocop/cop/lint/self_assignment.rb +39 -7
  160. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +1 -1
  161. data/lib/rubocop/cop/lint/shadowed_argument.rb +7 -7
  162. data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -1
  163. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +14 -0
  164. data/lib/rubocop/cop/lint/shared_mutable_default.rb +3 -1
  165. data/lib/rubocop/cop/lint/struct_new_override.rb +17 -1
  166. data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +12 -0
  167. data/lib/rubocop/cop/lint/symbol_conversion.rb +21 -4
  168. data/lib/rubocop/cop/lint/syntax.rb +25 -1
  169. data/lib/rubocop/cop/lint/to_enum_arguments.rb +28 -1
  170. data/lib/rubocop/cop/lint/to_json.rb +12 -16
  171. data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +1 -1
  172. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +5 -1
  173. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +4 -2
  174. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -0
  175. data/lib/rubocop/cop/lint/unreachable_code.rb +7 -5
  176. data/lib/rubocop/cop/lint/unreachable_pattern_branch.rb +113 -0
  177. data/lib/rubocop/cop/lint/unused_method_argument.rb +10 -0
  178. data/lib/rubocop/cop/lint/uri_escape_unescape.rb +2 -0
  179. data/lib/rubocop/cop/lint/useless_assignment.rb +53 -25
  180. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +4 -4
  181. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +2 -0
  182. data/lib/rubocop/cop/lint/useless_or.rb +15 -2
  183. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +8 -4
  184. data/lib/rubocop/cop/lint/useless_setter_call.rb +4 -1
  185. data/lib/rubocop/cop/lint/useless_times.rb +22 -1
  186. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +37 -11
  187. data/lib/rubocop/cop/lint/void.rb +39 -12
  188. data/lib/rubocop/cop/message_annotator.rb +1 -1
  189. data/lib/rubocop/cop/metrics/block_length.rb +1 -1
  190. data/lib/rubocop/cop/metrics/block_nesting.rb +23 -0
  191. data/lib/rubocop/cop/metrics/collection_literal_length.rb +1 -1
  192. data/lib/rubocop/cop/metrics/method_length.rb +1 -1
  193. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +4 -3
  194. data/lib/rubocop/cop/metrics/utils/iterating_block.rb +1 -1
  195. data/lib/rubocop/cop/migration/department_name.rb +12 -1
  196. data/lib/rubocop/cop/mixin/check_line_breakable.rb +2 -2
  197. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +4 -6
  198. data/lib/rubocop/cop/mixin/code_length.rb +1 -1
  199. data/lib/rubocop/cop/mixin/configurable_max.rb +6 -5
  200. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -7
  201. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +5 -5
  202. data/lib/rubocop/cop/mixin/hash_transform_method/autocorrection.rb +63 -0
  203. data/lib/rubocop/cop/mixin/hash_transform_method.rb +10 -60
  204. data/lib/rubocop/cop/mixin/line_length_help.rb +21 -2
  205. data/lib/rubocop/cop/mixin/method_complexity.rb +1 -1
  206. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
  207. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +1 -1
  208. data/lib/rubocop/cop/mixin/project_index_help.rb +48 -0
  209. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +5 -4
  210. data/lib/rubocop/cop/mixin/statement_modifier.rb +0 -6
  211. data/lib/rubocop/cop/mixin/trailing_comma.rb +8 -5
  212. data/lib/rubocop/cop/mixin.rb +86 -0
  213. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +1 -1
  214. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  215. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
  216. data/lib/rubocop/cop/naming/method_name.rb +5 -3
  217. data/lib/rubocop/cop/naming/predicate_method.rb +32 -8
  218. data/lib/rubocop/cop/naming/predicate_prefix.rb +12 -12
  219. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -1
  220. data/lib/rubocop/cop/offense.rb +17 -1
  221. data/lib/rubocop/cop/registry.rb +62 -38
  222. data/lib/rubocop/cop/security/eval.rb +15 -2
  223. data/lib/rubocop/cop/security/io_methods.rb +1 -1
  224. data/lib/rubocop/cop/security/json_load.rb +33 -11
  225. data/lib/rubocop/cop/style/access_modifier_declarations.rb +15 -4
  226. data/lib/rubocop/cop/style/accessor_grouping.rb +4 -2
  227. data/lib/rubocop/cop/style/alias.rb +15 -3
  228. data/lib/rubocop/cop/style/and_or.rb +2 -1
  229. data/lib/rubocop/cop/style/arguments_forwarding.rb +25 -7
  230. data/lib/rubocop/cop/style/array_first_last.rb +12 -1
  231. data/lib/rubocop/cop/style/array_intersect.rb +50 -12
  232. data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +50 -0
  233. data/lib/rubocop/cop/style/array_join.rb +4 -2
  234. data/lib/rubocop/cop/style/ascii_comments.rb +6 -3
  235. data/lib/rubocop/cop/style/attr.rb +5 -2
  236. data/lib/rubocop/cop/style/bare_percent_literals.rb +4 -3
  237. data/lib/rubocop/cop/style/begin_block.rb +3 -1
  238. data/lib/rubocop/cop/style/bitwise_predicate.rb +8 -1
  239. data/lib/rubocop/cop/style/block_delimiters.rb +39 -32
  240. data/lib/rubocop/cop/style/case_equality.rb +29 -15
  241. data/lib/rubocop/cop/style/character_literal.rb +2 -2
  242. data/lib/rubocop/cop/style/class_and_module_children.rb +19 -2
  243. data/lib/rubocop/cop/style/class_equality_comparison.rb +21 -13
  244. data/lib/rubocop/cop/style/class_methods_definitions.rb +11 -5
  245. data/lib/rubocop/cop/style/collection_compact.rb +36 -16
  246. data/lib/rubocop/cop/style/colon_method_call.rb +16 -7
  247. data/lib/rubocop/cop/style/combinable_loops.rb +5 -0
  248. data/lib/rubocop/cop/style/comparable_clamp.rb +12 -1
  249. data/lib/rubocop/cop/style/concat_array_literals.rb +7 -1
  250. data/lib/rubocop/cop/style/conditional_assignment.rb +14 -19
  251. data/lib/rubocop/cop/style/constant_visibility.rb +20 -12
  252. data/lib/rubocop/cop/style/copyright.rb +22 -11
  253. data/lib/rubocop/cop/style/date_time.rb +4 -4
  254. data/lib/rubocop/cop/style/dig_chain.rb +5 -0
  255. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +1 -1
  256. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +6 -1
  257. data/lib/rubocop/cop/style/documentation.rb +6 -6
  258. data/lib/rubocop/cop/style/documentation_method.rb +8 -8
  259. data/lib/rubocop/cop/style/double_negation.rb +1 -1
  260. data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
  261. data/lib/rubocop/cop/style/each_with_object.rb +2 -0
  262. data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
  263. data/lib/rubocop/cop/style/empty_class_definition.rb +119 -0
  264. data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
  265. data/lib/rubocop/cop/style/empty_method.rb +0 -6
  266. data/lib/rubocop/cop/style/encoding.rb +7 -1
  267. data/lib/rubocop/cop/style/end_block.rb +3 -1
  268. data/lib/rubocop/cop/style/endless_method.rb +23 -5
  269. data/lib/rubocop/cop/style/explicit_block_argument.rb +1 -1
  270. data/lib/rubocop/cop/style/fetch_env_var.rb +1 -1
  271. data/lib/rubocop/cop/style/file_open.rb +84 -0
  272. data/lib/rubocop/cop/style/file_write.rb +21 -16
  273. data/lib/rubocop/cop/style/float_division.rb +15 -1
  274. data/lib/rubocop/cop/style/for.rb +3 -0
  275. data/lib/rubocop/cop/style/format_string.rb +4 -3
  276. data/lib/rubocop/cop/style/format_string_token.rb +49 -5
  277. data/lib/rubocop/cop/style/global_vars.rb +5 -2
  278. data/lib/rubocop/cop/style/guard_clause.rb +27 -22
  279. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +27 -9
  280. data/lib/rubocop/cop/style/hash_conversion.rb +1 -1
  281. data/lib/rubocop/cop/style/hash_lookup_method.rb +106 -0
  282. data/lib/rubocop/cop/style/hash_slice.rb +16 -0
  283. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  284. data/lib/rubocop/cop/style/hash_transform_keys.rb +17 -7
  285. data/lib/rubocop/cop/style/hash_transform_values.rb +17 -7
  286. data/lib/rubocop/cop/style/if_inside_else.rb +16 -7
  287. data/lib/rubocop/cop/style/if_unless_modifier.rb +58 -18
  288. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +12 -12
  289. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +4 -1
  290. data/lib/rubocop/cop/style/if_with_semicolon.rb +7 -5
  291. data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
  292. data/lib/rubocop/cop/style/inline_comment.rb +4 -1
  293. data/lib/rubocop/cop/style/ip_addresses.rb +1 -2
  294. data/lib/rubocop/cop/style/lambda_call.rb +8 -8
  295. data/lib/rubocop/cop/style/magic_comment_format.rb +3 -3
  296. data/lib/rubocop/cop/style/map_join.rb +123 -0
  297. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +15 -2
  298. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +17 -4
  299. data/lib/rubocop/cop/style/method_def_parentheses.rb +2 -4
  300. data/lib/rubocop/cop/style/min_max_comparison.rb +1 -1
  301. data/lib/rubocop/cop/style/module_member_existence_check.rb +110 -0
  302. data/lib/rubocop/cop/style/multiline_if_then.rb +4 -4
  303. data/lib/rubocop/cop/style/multiline_method_signature.rb +2 -4
  304. data/lib/rubocop/cop/style/mutable_constant.rb +106 -12
  305. data/lib/rubocop/cop/style/negative_array_index.rb +220 -0
  306. data/lib/rubocop/cop/style/nil_comparison.rb +11 -10
  307. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  308. data/lib/rubocop/cop/style/non_nil_check.rb +5 -11
  309. data/lib/rubocop/cop/style/not.rb +2 -0
  310. data/lib/rubocop/cop/style/numeric_literals.rb +3 -2
  311. data/lib/rubocop/cop/style/one_class_per_file.rb +115 -0
  312. data/lib/rubocop/cop/style/one_line_conditional.rb +21 -12
  313. data/lib/rubocop/cop/style/operator_method_call.rb +11 -2
  314. data/lib/rubocop/cop/style/parallel_assignment.rb +14 -3
  315. data/lib/rubocop/cop/style/partition_instead_of_double_select.rb +270 -0
  316. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -0
  317. data/lib/rubocop/cop/style/predicate_with_kind.rb +84 -0
  318. data/lib/rubocop/cop/style/preferred_hash_methods.rb +12 -12
  319. data/lib/rubocop/cop/style/proc.rb +3 -2
  320. data/lib/rubocop/cop/style/raise_args.rb +1 -1
  321. data/lib/rubocop/cop/style/reduce_to_hash.rb +200 -0
  322. data/lib/rubocop/cop/style/redundant_argument.rb +2 -0
  323. data/lib/rubocop/cop/style/redundant_array_constructor.rb +2 -2
  324. data/lib/rubocop/cop/style/redundant_begin.rb +37 -3
  325. data/lib/rubocop/cop/style/redundant_condition.rb +6 -3
  326. data/lib/rubocop/cop/style/redundant_constant_base.rb +5 -5
  327. data/lib/rubocop/cop/style/redundant_each.rb +3 -3
  328. data/lib/rubocop/cop/style/redundant_exception.rb +1 -1
  329. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -1
  330. data/lib/rubocop/cop/style/redundant_format.rb +27 -5
  331. data/lib/rubocop/cop/style/redundant_interpolation.rb +11 -2
  332. data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +26 -10
  333. data/lib/rubocop/cop/style/redundant_line_continuation.rb +16 -0
  334. data/lib/rubocop/cop/style/redundant_min_max_by.rb +93 -0
  335. data/lib/rubocop/cop/style/redundant_parentheses.rb +36 -30
  336. data/lib/rubocop/cop/style/redundant_percent_q.rb +5 -3
  337. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +9 -0
  338. data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +2 -2
  339. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -0
  340. data/lib/rubocop/cop/style/redundant_return.rb +3 -1
  341. data/lib/rubocop/cop/style/redundant_self.rb +2 -2
  342. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +0 -5
  343. data/lib/rubocop/cop/style/redundant_sort.rb +7 -7
  344. data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +114 -0
  345. data/lib/rubocop/cop/style/regexp_literal.rb +31 -2
  346. data/lib/rubocop/cop/style/rescue_modifier.rb +3 -3
  347. data/lib/rubocop/cop/style/reverse_find.rb +51 -0
  348. data/lib/rubocop/cop/style/safe_navigation.rb +25 -8
  349. data/lib/rubocop/cop/style/select_by_kind.rb +158 -0
  350. data/lib/rubocop/cop/style/select_by_range.rb +197 -0
  351. data/lib/rubocop/cop/style/select_by_regexp.rb +51 -21
  352. data/lib/rubocop/cop/style/self_assignment.rb +1 -1
  353. data/lib/rubocop/cop/style/semicolon.rb +41 -8
  354. data/lib/rubocop/cop/style/single_line_block_params.rb +2 -2
  355. data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -1
  356. data/lib/rubocop/cop/style/single_line_methods.rb +3 -1
  357. data/lib/rubocop/cop/style/sole_nested_conditional.rb +12 -3
  358. data/lib/rubocop/cop/style/special_global_vars.rb +6 -1
  359. data/lib/rubocop/cop/style/string_concatenation.rb +17 -13
  360. data/lib/rubocop/cop/style/struct_inheritance.rb +13 -0
  361. data/lib/rubocop/cop/style/super_arguments.rb +2 -2
  362. data/lib/rubocop/cop/style/symbol_array.rb +1 -1
  363. data/lib/rubocop/cop/style/symbol_proc.rb +7 -6
  364. data/lib/rubocop/cop/style/tally_method.rb +181 -0
  365. data/lib/rubocop/cop/style/top_level_method_definition.rb +2 -2
  366. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +45 -0
  367. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  368. data/lib/rubocop/cop/style/trailing_method_end_statement.rb +1 -0
  369. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +11 -11
  370. data/lib/rubocop/cop/style/unless_else.rb +10 -9
  371. data/lib/rubocop/cop/style/unless_logical_operators.rb +3 -3
  372. data/lib/rubocop/cop/style/while_until_do.rb +7 -0
  373. data/lib/rubocop/cop/style/while_until_modifier.rb +16 -0
  374. data/lib/rubocop/cop/style/word_array.rb +1 -0
  375. data/lib/rubocop/cop/style/yoda_condition.rb +1 -1
  376. data/lib/rubocop/cop/style/yoda_expression.rb +1 -1
  377. data/lib/rubocop/cop/style/zero_length_predicate.rb +6 -3
  378. data/lib/rubocop/cop/team.rb +87 -36
  379. data/lib/rubocop/cop/util.rb +2 -3
  380. data/lib/rubocop/cop/utils/format_string.rb +10 -0
  381. data/lib/rubocop/cop/variable_force/branch.rb +30 -6
  382. data/lib/rubocop/cop/variable_force/variable.rb +1 -1
  383. data/lib/rubocop/cop/variable_force.rb +9 -7
  384. data/lib/rubocop/cops_documentation_generator.rb +4 -4
  385. data/lib/rubocop/directive_comment.rb +48 -4
  386. data/lib/rubocop/file_patterns.rb +9 -1
  387. data/lib/rubocop/formatter/clang_style_formatter.rb +5 -2
  388. data/lib/rubocop/formatter/disabled_config_formatter.rb +38 -14
  389. data/lib/rubocop/formatter/formatter_set.rb +2 -2
  390. data/lib/rubocop/formatter/junit_formatter.rb +1 -1
  391. data/lib/rubocop/formatter/simple_text_formatter.rb +0 -2
  392. data/lib/rubocop/formatter/tap_formatter.rb +5 -2
  393. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
  394. data/lib/rubocop/formatter.rb +22 -21
  395. data/lib/rubocop/lsp/diagnostic.rb +18 -33
  396. data/lib/rubocop/lsp/disable_comment_edits.rb +135 -0
  397. data/lib/rubocop/lsp/routes.rb +43 -7
  398. data/lib/rubocop/lsp/runtime.rb +13 -4
  399. data/lib/rubocop/lsp/stdin_runner.rb +8 -17
  400. data/lib/rubocop/magic_comment.rb +20 -0
  401. data/lib/rubocop/mcp/server.rb +200 -0
  402. data/lib/rubocop/options.rb +35 -4
  403. data/lib/rubocop/path_util.rb +14 -2
  404. data/lib/rubocop/plugin/loader.rb +1 -1
  405. data/lib/rubocop/project_index_loader.rb +66 -0
  406. data/lib/rubocop/rake_task.rb +1 -1
  407. data/lib/rubocop/remote_config.rb +10 -8
  408. data/lib/rubocop/result_cache.rb +61 -38
  409. data/lib/rubocop/rspec/cop_helper.rb +8 -0
  410. data/lib/rubocop/rspec/shared_contexts.rb +39 -5
  411. data/lib/rubocop/rspec/support.rb +2 -1
  412. data/lib/rubocop/runner.rb +134 -57
  413. data/lib/rubocop/server/cache.rb +6 -29
  414. data/lib/rubocop/server/core.rb +8 -0
  415. data/lib/rubocop/target_finder.rb +17 -10
  416. data/lib/rubocop/target_ruby.rb +31 -14
  417. data/lib/rubocop/version.rb +21 -3
  418. data/lib/rubocop.rb +28 -96
  419. data/lib/ruby_lsp/rubocop/addon.rb +23 -8
  420. data/lib/ruby_lsp/rubocop/runtime_adapter.rb +49 -15
  421. metadata +38 -9
@@ -56,6 +56,27 @@ module RuboCop
56
56
  # 1
57
57
  # end
58
58
  #
59
+ # # bad
60
+ # class MyClass
61
+ # extend Forwardable
62
+ #
63
+ # # or with: `def_instance_delegator`, `def_delegators`, `def_instance_delegators`
64
+ # def_delegator :delegation_target, :delegated_method_name
65
+ #
66
+ # def delegated_method_name
67
+ # end
68
+ # end
69
+ #
70
+ # # good
71
+ # class MyClass
72
+ # extend Forwardable
73
+ #
74
+ # def_delegator :delegation_target, :delegated_method_name
75
+ #
76
+ # def non_duplicated_delegated_method_name
77
+ # end
78
+ # end
79
+ #
59
80
  # @example AllCops:ActiveSupportExtensionsEnabled: false (default)
60
81
  #
61
82
  # # good
@@ -97,10 +118,11 @@ module RuboCop
97
118
  # delegate :foo, to: :bar
98
119
  # end
99
120
  #
100
- class DuplicateMethods < Base
121
+ class DuplicateMethods < Base # rubocop:disable Metrics/ClassLength
101
122
  MSG = 'Method `%<method>s` is defined at both %<defined>s and %<current>s.'
102
123
  RESTRICT_ON_SEND = %i[alias_method attr_reader attr_writer attr_accessor attr
103
- delegate].freeze
124
+ delegate def_delegator def_instance_delegator def_delegators
125
+ def_instance_delegators].freeze
104
126
 
105
127
  def initialize(config = nil, options = nil)
106
128
  super
@@ -154,23 +176,56 @@ module RuboCop
154
176
  )
155
177
  PATTERN
156
178
 
179
+ # @!method delegator?(node)
180
+ def_node_matcher :delegator?, <<~PATTERN
181
+ (send nil? {:def_delegator :def_instance_delegator}
182
+ {
183
+ {sym str} ({sym str} $_) |
184
+ {sym str} {sym str} ({sym str} $_)
185
+ }
186
+ )
187
+ PATTERN
188
+
189
+ # @!method delegators?(node)
190
+ def_node_matcher :delegators?, <<~PATTERN
191
+ (send nil? {:def_delegators :def_instance_delegators}
192
+ {sym str}
193
+ ({sym str} $_)+
194
+ )
195
+ PATTERN
196
+
157
197
  # @!method sym_name(node)
158
198
  def_node_matcher :sym_name, '(sym $_name)'
159
199
 
160
- def on_send(node) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
200
+ # @!method class_or_module_new_block?(node)
201
+ def_node_matcher :class_or_module_new_block?, <<~PATTERN
202
+ (block
203
+ (send (const _ {:Class :Module}) :new ...)
204
+ ...)
205
+ PATTERN
206
+
207
+ def on_send(node) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
161
208
  name, original_name = alias_method?(node)
162
209
 
163
210
  if name && original_name
164
211
  return if name == original_name
165
- return if node.ancestors.any?(&:if_type?)
212
+ return if inside_condition?(node)
166
213
 
167
214
  found_instance_method(node, name)
168
215
  elsif (attr = node.attribute_accessor?)
169
216
  on_attr(node, *attr)
170
217
  elsif active_support_extensions_enabled? && (names = delegate_method?(node))
171
- return if node.ancestors.any?(&:if_type?)
218
+ return if inside_condition?(node)
172
219
 
173
220
  on_delegate(node, names)
221
+ elsif (name = delegator?(node))
222
+ return if inside_condition?(node)
223
+
224
+ found_instance_method(node, name)
225
+ elsif (names = delegators?(node))
226
+ return if inside_condition?(node)
227
+
228
+ names.each { |name| found_instance_method(node, name) }
174
229
  end
175
230
  end
176
231
 
@@ -185,9 +240,16 @@ module RuboCop
185
240
 
186
241
  def check_self_receiver(node, name)
187
242
  enclosing = node.parent_module_name
188
- return unless enclosing
243
+ if enclosing
244
+ found_method(node, "#{enclosing}.#{name}")
245
+ elsif (anon_block = anonymous_class_block(node))
246
+ scope = qualified_name(anon_block.parent_module_name, nil, 'Object')
247
+ found_method(node, "#{scope}.#{name}", scope_id: anon_block_scope_id(anon_block))
248
+ end
249
+ end
189
250
 
190
- found_method(node, "#{enclosing}.#{name}")
251
+ def inside_condition?(node)
252
+ node.ancestors.any?(&:if_type?)
191
253
  end
192
254
 
193
255
  def message_for_dup(node, method_name, key)
@@ -222,16 +284,50 @@ module RuboCop
222
284
  end
223
285
 
224
286
  def found_instance_method(node, name)
225
- return found_sclass_method(node, name) unless (scope = node.parent_module_name)
287
+ if (scope = node.parent_module_name)
288
+ found_method(node, "#{humanize_scope(scope)}#{name}")
289
+ elsif (anon_block = anonymous_class_block(node))
290
+ base = qualified_name(anon_block.parent_module_name, nil, 'Object')
291
+ scope = node.each_ancestor(:sclass).any? ? "#<Class:#{base}>" : base
292
+ found_method(
293
+ node, "#{humanize_scope(scope)}#{name}", scope_id: anon_block_scope_id(anon_block)
294
+ )
295
+ else
296
+ found_sclass_method(node, name)
297
+ end
298
+ end
226
299
 
227
- # Humanize the scope
300
+ def humanize_scope(scope)
228
301
  scope = scope.sub(
229
302
  /(?:(?<name>.*)::)#<Class:\k<name>>|#<Class:(?<name>.*)>(?:::)?/,
230
303
  '\k<name>.'
231
304
  )
232
- scope << '#' unless scope.end_with?('.')
305
+ scope.end_with?('.') ? scope : "#{scope}#"
306
+ end
307
+
308
+ def anonymous_class_block(node)
309
+ first_block = node.each_ancestor(:block).first
310
+ return unless class_or_module_new_block?(first_block)
311
+ return if first_block.parent&.type?(:lvasgn)
312
+ return if node.each_ancestor(:sclass).any? { |s| !s.children.first.self_type? }
313
+
314
+ first_block
315
+ end
316
+
317
+ def anon_block_scope_id(anon_block)
318
+ parent = anon_block.parent
319
+ return unless parent&.type?(:any_block, :begin, :call, :casgn, :any_def)
320
+
321
+ if (receiver = named_receiver(parent))
322
+ "#{receiver.source}.#{parent.method_name}"
323
+ elsif !parent.begin_type? || parent.parent&.any_block_type?
324
+ source_location(anon_block)
325
+ end
326
+ end
233
327
 
234
- found_method(node, "#{scope}#{name}")
328
+ def named_receiver(node)
329
+ receiver = node.receiver
330
+ receiver unless class_or_module_new_block?(receiver)
235
331
  end
236
332
 
237
333
  def found_sclass_method(node, name)
@@ -244,8 +340,10 @@ module RuboCop
244
340
  found_method(node, "#{singleton_receiver_node.method_name}.#{name}")
245
341
  end
246
342
 
247
- def found_method(node, method_name)
343
+ # rubocop:disable Metrics/AbcSize
344
+ def found_method(node, method_name, scope_id: nil)
248
345
  key = method_key(node, method_name)
346
+ key = "#{key}@#{scope_id}" if scope_id
249
347
  scope = node.each_ancestor(:rescue, :ensure).first&.type
250
348
 
251
349
  if @definitions.key?(key)
@@ -262,6 +360,7 @@ module RuboCop
262
360
  @definitions[key] = node
263
361
  end
264
362
  end
363
+ # rubocop:enable Metrics/AbcSize
265
364
 
266
365
  def method_key(node, method_name)
267
366
  if (ancestor_def = node.each_ancestor(:any_def).first)
@@ -24,8 +24,6 @@ module RuboCop
24
24
 
25
25
  MSG_REPEATED_ELEMENT = 'Duplicate element inside regexp character class'
26
26
 
27
- OCTAL_DIGITS_AFTER_ESCAPE = 2
28
-
29
27
  def on_regexp(node)
30
28
  each_repeated_character_class_element_loc(node) do |loc|
31
29
  add_offense(loc, message: MSG_REPEATED_ELEMENT) do |corrector|
@@ -40,9 +38,9 @@ module RuboCop
40
38
 
41
39
  seen = Set.new
42
40
  group_expressions(node, expr.expressions) do |group|
43
- group_source = group.map(&:to_s).join
41
+ group_source = group.to_s
44
42
 
45
- yield source_range(group) if seen.include?(group_source)
43
+ yield group.expression if seen.include?(group_source)
46
44
 
47
45
  seen << group_source
48
46
  end
@@ -52,40 +50,13 @@ module RuboCop
52
50
  private
53
51
 
54
52
  def group_expressions(node, expressions)
55
- # Create a mutable list to simplify state tracking while we iterate.
56
- expressions = expressions.to_a
57
-
58
- until expressions.empty?
59
- # With we may need to compose a group of multiple expressions.
60
- group = [expressions.shift]
61
- next if within_interpolation?(node, group.first)
62
-
63
- # With regexp_parser < 2.7 escaped octal sequences may be up to 3
64
- # separate expressions ("\\0", "0", "1").
65
- pop_octal_digits(group, expressions) if escaped_octal?(group.first.to_s)
66
-
67
- yield(group)
68
- end
69
- end
70
-
71
- def pop_octal_digits(current_child, expressions)
72
- OCTAL_DIGITS_AFTER_ESCAPE.times do
73
- next_child = expressions.first
74
- break unless octal?(next_child.to_s)
53
+ expressions.each do |expression|
54
+ next if within_interpolation?(node, expression)
75
55
 
76
- current_child << expressions.shift
56
+ yield(expression)
77
57
  end
78
58
  end
79
59
 
80
- def source_range(children)
81
- return children.first.expression if children.size == 1
82
-
83
- range_between(
84
- children.first.expression.begin_pos,
85
- children.last.expression.begin_pos + children.last.to_s.length
86
- )
87
- end
88
-
89
60
  def skip_expression?(expr)
90
61
  expr.type != :set || expr.token == :intersection
91
62
  end
@@ -99,14 +70,6 @@ module RuboCop
99
70
  interpolation_locs(node).any? { |il| il.overlaps?(parse_tree_child_loc) }
100
71
  end
101
72
 
102
- def escaped_octal?(string)
103
- string.length == 2 && string[0] == '\\' && octal?(string[1])
104
- end
105
-
106
- def octal?(char)
107
- ('0'..'7').cover?(char)
108
- end
109
-
110
73
  def interpolation_locs(node)
111
74
  @interpolation_locs ||= {}
112
75
 
@@ -38,6 +38,24 @@ module RuboCop
38
38
  # elsif do_this
39
39
  # do_that
40
40
  # end
41
+ #
42
+ # # bad
43
+ #
44
+ # # For single-line conditionals using `then` the layout is disallowed
45
+ # # when the `else` body is multiline because it is treated as a lint offense.
46
+ # if something then on_the_same_line_as_then
47
+ # else first_line
48
+ # second_line
49
+ # end
50
+ #
51
+ # # good
52
+ #
53
+ # # For single-line conditional using `then` the layout is allowed
54
+ # # when `else` body is a single-line because it is treated as intentional.
55
+ #
56
+ # if something then on_the_same_line_as_then
57
+ # else single_line
58
+ # end
41
59
  class ElseLayout < Base
42
60
  include Alignment
43
61
  include RangeHelp
@@ -47,6 +65,7 @@ module RuboCop
47
65
 
48
66
  def on_if(node)
49
67
  return if node.ternary?
68
+ return if node.then? && !node.else_branch&.begin_type?
50
69
 
51
70
  # If the if is on a single line, it'll be handled by `Style/OneLineConditional`
52
71
  return if node.single_line?
@@ -63,7 +63,7 @@ module RuboCop
63
63
  class EmptyBlock < Base
64
64
  MSG = 'Empty block detected.'
65
65
 
66
- def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
66
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
67
67
  return if node.body
68
68
  return if allow_empty_lambdas? && node.lambda_or_proc?
69
69
  return if cop_config['AllowComments'] && allow_comment?(node)
@@ -77,7 +77,7 @@ module RuboCop
77
77
  return false unless processed_source.contains_comment?(node.source_range)
78
78
 
79
79
  line_comment = processed_source.comment_at_line(node.source_range.line)
80
- !line_comment || !comment_disables_cop?(line_comment.source)
80
+ !line_comment || !comment_disables_cop?(line_comment)
81
81
  end
82
82
 
83
83
  def allow_empty_lambdas?
@@ -85,8 +85,8 @@ module RuboCop
85
85
  end
86
86
 
87
87
  def comment_disables_cop?(comment)
88
- regexp_pattern = "# rubocop : (disable|todo) ([^,],)* (all|#{cop_name})"
89
- Regexp.new(regexp_pattern.gsub(' ', '\s*')).match?(comment)
88
+ directive = DirectiveComment.new(comment)
89
+ directive.disabled? && directive.cop_names.include?(cop_name)
90
90
  end
91
91
  end
92
92
  end
@@ -70,7 +70,7 @@ module RuboCop
70
70
 
71
71
  def on_if(node)
72
72
  return if node.body || same_line?(node.loc.begin, node.loc.end)
73
- return if cop_config['AllowComments'] && contains_comments?(node)
73
+ return if allow_comments?(node)
74
74
 
75
75
  range = offense_range(node)
76
76
 
@@ -83,6 +83,11 @@ module RuboCop
83
83
 
84
84
  private
85
85
 
86
+ def allow_comments?(node)
87
+ cop_config['AllowComments'] && contains_comments?(node) &&
88
+ !comments_contain_disables?(node, name)
89
+ end
90
+
86
91
  def offense_range(node)
87
92
  if node.loc.else
88
93
  node.source_range.begin.join(node.loc.else.begin)
@@ -53,11 +53,18 @@ module RuboCop
53
53
  def on_case_match(node)
54
54
  node.in_pattern_branches.each do |branch|
55
55
  next if branch.body
56
- next if cop_config['AllowComments'] && contains_comments?(branch)
56
+ next if allow_comments?(branch)
57
57
 
58
58
  add_offense(branch)
59
59
  end
60
60
  end
61
+
62
+ private
63
+
64
+ def allow_comments?(node)
65
+ cop_config['AllowComments'] && contains_comments?(node) &&
66
+ !comments_contain_disables?(node, name)
67
+ end
61
68
  end
62
69
  end
63
70
  end
@@ -19,12 +19,23 @@ module RuboCop
19
19
  MSG = 'Empty interpolation detected.'
20
20
 
21
21
  def on_interpolation(begin_node)
22
+ return if in_percent_literal_array?(begin_node)
23
+
22
24
  node_children = begin_node.children.dup
23
25
  node_children.delete_if { |e| e.nil_type? || (e.basic_literal? && e.str_content&.empty?) }
24
26
  return unless node_children.empty?
25
27
 
26
28
  add_offense(begin_node) { |corrector| corrector.remove(begin_node) }
27
29
  end
30
+
31
+ private
32
+
33
+ def in_percent_literal_array?(begin_node)
34
+ array_node = begin_node.each_ancestor(:array).first
35
+ return false unless array_node
36
+
37
+ array_node.percent_literal?
38
+ end
28
39
  end
29
40
  end
30
41
  end
@@ -50,11 +50,18 @@ module RuboCop
50
50
  def on_case(node)
51
51
  node.when_branches.each do |when_node|
52
52
  next if when_node.body
53
- next if cop_config['AllowComments'] && contains_comments?(when_node)
53
+ next if allow_comments?(when_node)
54
54
 
55
55
  add_offense(when_node)
56
56
  end
57
57
  end
58
+
59
+ private
60
+
61
+ def allow_comments?(node)
62
+ cop_config['AllowComments'] && contains_comments?(node) &&
63
+ !comments_contain_disables?(node, name)
64
+ end
58
65
  end
59
66
  end
60
67
  end
@@ -43,7 +43,25 @@ module RuboCop
43
43
  MSG = 'Do not return from an `ensure` block.'
44
44
 
45
45
  def on_ensure(node)
46
- node.branch&.each_node(:return) { |return_node| add_offense(return_node) }
46
+ node.branch&.each_node(:return) do |return_node|
47
+ next if return_from_inner_scope?(return_node, node)
48
+
49
+ add_offense(return_node)
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ # A `return` inside a nested method definition or lambda within the
56
+ # `ensure` returns from that inner scope, not from the method whose
57
+ # `ensure` this is, so it is not an offense. A `return` inside a plain
58
+ # block (or `proc`) does propagate out, so it remains an offense.
59
+ def return_from_inner_scope?(return_node, ensure_node)
60
+ return_node.each_ancestor do |ancestor|
61
+ break if ancestor == ensure_node
62
+ return true if ancestor.any_def_type? || (ancestor.any_block_type? && ancestor.lambda?)
63
+ end
64
+ false
47
65
  end
48
66
  end
49
67
  end
@@ -23,7 +23,7 @@ module RuboCop
23
23
  # `ERB.new` with non-keyword arguments is deprecated since ERB 2.2.0.
24
24
  # Use `:trim_mode` and `:eoutvar` keyword arguments to `ERB.new`.
25
25
  # This cop identifies places where `ERB.new(str, trim_mode, eoutvar)` can
26
- # be replaced by `ERB.new(str, :trim_mode: trim_mode, eoutvar: eoutvar)`.
26
+ # be replaced by `ERB.new(str, trim_mode: trim_mode, eoutvar: eoutvar)`.
27
27
  #
28
28
  # @example
29
29
  # # Target codes supports Ruby 2.6 and higher only
@@ -148,7 +148,9 @@ module RuboCop
148
148
  arguments = node.arguments
149
149
  overridden_kwargs = kwargs.dup
150
150
 
151
- overridden_kwargs[0] = "trim_mode: #{arguments[2].source}" if arguments[2]
151
+ if arguments[2] && !arguments[2].hash_type?
152
+ overridden_kwargs[0] = "trim_mode: #{arguments[2].source}"
153
+ end
152
154
 
153
155
  if arguments[3] && !arguments[3].hash_type?
154
156
  overridden_kwargs[1] = "eoutvar: #{arguments[3].source}"
@@ -77,7 +77,7 @@ module RuboCop
77
77
 
78
78
  def on_case(node)
79
79
  node.when_branches.each do |when_branch|
80
- when_branch.each_condition do |condition|
80
+ when_branch.conditions.each do |condition|
81
81
  next if !float?(condition) || literal_safe?(condition)
82
82
 
83
83
  add_offense(condition, message: MSG_CASE)
@@ -104,6 +104,7 @@ module RuboCop
104
104
 
105
105
  def literal_safe?(node)
106
106
  return false unless node
107
+ return literal_safe?(node.children.first) if node.begin_type?
107
108
 
108
109
  (node.numeric_type? && node.value.zero?) || node.nil_type?
109
110
  end
@@ -61,11 +61,15 @@ module RuboCop
61
61
  private
62
62
 
63
63
  def scheduler_compatible?(io1, io2)
64
- return false unless io1&.array_type? && io1.values.size == 1
64
+ return false unless single_io_array?(io1)
65
65
 
66
66
  io2&.array_type? ? io2.values.empty? : (io2.nil? || io2.nil_type?)
67
67
  end
68
68
 
69
+ def single_io_array?(node)
70
+ node&.array_type? && node.values.size == 1 && !node.values.first.splat_type?
71
+ end
72
+
69
73
  def preferred_method(read, write, timeout)
70
74
  timeout_argument = timeout.nil? ? '' : "(#{timeout.source})"
71
75
 
@@ -24,8 +24,25 @@ module RuboCop
24
24
  MSG = 'Interpolation in single quoted string detected. ' \
25
25
  'Use double quoted strings if you need interpolation.'
26
26
 
27
- # rubocop:disable Metrics/CyclomaticComplexity
28
27
  def on_str(node)
28
+ check(node)
29
+ end
30
+
31
+ # A multiline single-quoted string is parsed as a `dstr` of `str` segments, so it
32
+ # is not covered by `on_str`. Inspect single-quoted `dstr`s here; double-quoted
33
+ # interpolation is also a `dstr`, hence the delimiter check.
34
+ def on_dstr(node)
35
+ # A heredoc is also a `dstr`, but its `loc` is a `Parser::Source::Map::Heredoc`
36
+ # with no `begin`, so bail before touching it.
37
+ return if heredoc?(node)
38
+
39
+ check(node) if node.loc.begin&.source == "'"
40
+ end
41
+
42
+ private
43
+
44
+ # rubocop:disable Metrics/CyclomaticComplexity
45
+ def check(node)
29
46
  return if node.parent&.regexp_type?
30
47
  return unless /(?<!\\)#\{.*\}/.match?(node.source)
31
48
  return if heredoc?(node)
@@ -36,8 +53,6 @@ module RuboCop
36
53
  end
37
54
  # rubocop:enable Metrics/CyclomaticComplexity
38
55
 
39
- private
40
-
41
56
  def autocorrect(corrector, node)
42
57
  starting_token, ending_token = if node.source.include?('"')
43
58
  ['%{', '}']
@@ -54,9 +69,14 @@ module RuboCop
54
69
  end
55
70
 
56
71
  def valid_syntax?(node)
57
- double_quoted_string = node.source.gsub(/\A'|'\z/, '"')
72
+ double_quoted_string = if node.source.include?('"')
73
+ node.source.sub(/\A'/, '%{').sub(/'\z/, '}')
74
+ else
75
+ node.source.gsub(/\A'|'\z/, '"')
76
+ end
58
77
 
59
- parse(double_quoted_string).valid_syntax?
78
+ processed_source = parse(double_quoted_string)
79
+ processed_source.valid_syntax? && processed_source.ast.dstr_type?
60
80
  end
61
81
  end
62
82
  end
@@ -42,7 +42,7 @@ module RuboCop
42
42
  end
43
43
 
44
44
  add_offense(node) do |corrector|
45
- corrector.replace(node, node.first_argument.source.delete('&'))
45
+ corrector.replace(node, node.first_argument.source.delete_prefix('&'))
46
46
  end
47
47
  end
48
48
  end
@@ -157,7 +157,7 @@ module RuboCop
157
157
 
158
158
  check_case(case_match_node)
159
159
  else
160
- case_match_node.each_in_pattern do |in_pattern_node|
160
+ case_match_node.in_pattern_branches.each do |in_pattern_node|
161
161
  next unless in_pattern_node.condition.literal?
162
162
 
163
163
  add_offense(in_pattern_node)
@@ -269,7 +269,11 @@ module RuboCop
269
269
  end
270
270
 
271
271
  add_offense(cond) do |corrector|
272
+ next if part_of_ignored_node?(node)
273
+
272
274
  corrector.replace(node, new_node)
275
+
276
+ ignore_node(node)
273
277
  end
274
278
  end
275
279
  # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
@@ -56,7 +56,17 @@ module RuboCop
56
56
  def traverse_node(node, &block)
57
57
  yield node if node.equals_asgn?
58
58
 
59
- node.each_child_node { |child| traverse_node(child, &block) }
59
+ node.each_child_node do |child|
60
+ next if scope_body?(node, child)
61
+
62
+ traverse_node(child, &block)
63
+ end
64
+ end
65
+
66
+ # An assignment inside a block or method body within the condition belongs to
67
+ # that inner scope rather than the condition itself, so it is not inspected.
68
+ def scope_body?(node, child)
69
+ node.type?(:any_block, :any_def) && child == node.body
60
70
  end
61
71
 
62
72
  def all_literals?(node)
@@ -66,7 +66,7 @@ module RuboCop
66
66
 
67
67
  def special_keyword?(node)
68
68
  # handle strings like __FILE__
69
- (node.str_type? && !node.loc.respond_to?(:begin)) || node.source_range.is?('__LINE__')
69
+ (node.str_type? && !node.loc?(:begin)) || node.source_range.is?('__LINE__')
70
70
  end
71
71
 
72
72
  def array_in_regexp?(node)
@@ -119,11 +119,13 @@ module RuboCop
119
119
  end
120
120
 
121
121
  def autocorrected_value_for_string(node)
122
- if node.source.start_with?("'", '%q')
123
- node.children.last.inspect[1..-2]
124
- else
125
- node.children.last
126
- end
122
+ return node.source.delete_prefix('"').delete_suffix('"') unless node.value.valid_encoding?
123
+
124
+ escape_string_content(node.children.last)
125
+ end
126
+
127
+ def escape_string_content(string)
128
+ string.gsub(/[\\"]|#(?=[@{$])/, '\\\\\&')
127
129
  end
128
130
 
129
131
  def autocorrected_value_for_symbol(node)
@@ -134,12 +136,7 @@ module RuboCop
134
136
  end
135
137
 
136
138
  def autocorrected_value_in_hash_for_symbol(node)
137
- # TODO: We need to detect symbol unacceptable names more correctly
138
- if / |"|'/.match?(node.value.to_s)
139
- ":\\\"#{node.value.to_s.gsub('"') { '\\\\\"' }}\\\""
140
- else
141
- ":#{node.value}"
142
- end
139
+ escape_string_content(node.value.inspect)
143
140
  end
144
141
 
145
142
  def autocorrected_value_for_array(node)