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
@@ -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
@@ -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)
@@ -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)
@@ -6,10 +6,10 @@ module RuboCop
6
6
  module Lint
7
7
  # Checks that there is an `# rubocop:enable ...` statement
8
8
  # after a `# rubocop:disable ...` statement. This will prevent leaving
9
- # cop disables on wide ranges of code, that latter contributors to
9
+ # cop disables on wide ranges of code, that later contributors to
10
10
  # a file wouldn't be aware of.
11
11
  #
12
- # You can set `MaximumRangeSize` to define the maximum number of
12
+ # You can set `MaxRangeSize` to define the maximum number of
13
13
  # consecutive lines a cop can be disabled for.
14
14
  #
15
15
  # - `.inf` any size (default)
@@ -23,7 +23,7 @@ module RuboCop
23
23
  # # rubocop:enable SomeCop
24
24
  # ----
25
25
  #
26
- # @example MaximumRangeSize: .inf (default)
26
+ # @example MaxRangeSize: .inf (default)
27
27
  #
28
28
  # # good
29
29
  # # rubocop:disable Layout/SpaceAroundOperators
@@ -37,7 +37,7 @@ module RuboCop
37
37
  # x= 0
38
38
  # # EOF
39
39
  #
40
- # @example MaximumRangeSize: 2
40
+ # @example MaxRangeSize: 2
41
41
  #
42
42
  # # good
43
43
  # # rubocop:disable Layout/SpaceAroundOperators
@@ -94,7 +94,7 @@ module RuboCop
94
94
  end
95
95
 
96
96
  def max_range
97
- @max_range ||= cop_config['MaximumRangeSize']
97
+ @max_range ||= cop_config['MaxRangeSize']
98
98
  end
99
99
 
100
100
  def message(cop, comment, type = 'cop')
@@ -4,8 +4,8 @@ module RuboCop
4
4
  module Cop
5
5
  module Lint
6
6
  # In math and Python, we can use `x < y < z` style comparison to compare
7
- # multiple value. However, we can't use the comparison in Ruby. However,
8
- # the comparison is not syntax error. This cop checks the bad usage of
7
+ # multiple values. However, we can't use the comparison in Ruby. However,
8
+ # the comparison is not a syntax error. This cop checks the bad usage of
9
9
  # comparison operators.
10
10
  #
11
11
  # @example
@@ -31,6 +31,7 @@ module RuboCop
31
31
  end
32
32
  end
33
33
  alias on_numblock on_block
34
+ alias on_itblock on_block
34
35
 
35
36
  private
36
37
 
@@ -39,6 +40,7 @@ module RuboCop
39
40
  {
40
41
  (block (call _recv {:reduce :inject} !sym) _blockargs $(begin ...))
41
42
  (numblock (call _recv {:reduce :inject} !sym) _argscount $(begin ...))
43
+ (itblock (call _recv {:reduce :inject} !sym) _argscount $(begin ...))
42
44
  }
43
45
  PATTERN
44
46
 
@@ -41,6 +41,8 @@ module RuboCop
41
41
  def on_lvasgn(node)
42
42
  node.each_node(:kwbegin) do |kwbegin_node|
43
43
  kwbegin_node.each_node(:return) do |return_node|
44
+ next if return_from_inner_scope?(return_node, kwbegin_node)
45
+
44
46
  add_offense(return_node)
45
47
  end
46
48
  end
@@ -51,6 +53,20 @@ module RuboCop
51
53
  alias on_casgn on_lvasgn
52
54
  alias on_or_asgn on_lvasgn
53
55
  alias on_op_asgn on_lvasgn
56
+
57
+ private
58
+
59
+ # A `return` inside a nested method definition or lambda within the
60
+ # `begin..end` returns from that inner scope rather than the assignment
61
+ # context, so it is not an offense. A `return` inside a plain block (or
62
+ # `proc`) does propagate out, so it remains an offense.
63
+ def return_from_inner_scope?(return_node, kwbegin_node)
64
+ return_node.each_ancestor do |ancestor|
65
+ break if ancestor == kwbegin_node
66
+ return true if ancestor.any_def_type? || (ancestor.any_block_type? && ancestor.lambda?)
67
+ end
68
+ false
69
+ end
54
70
  end
55
71
  end
56
72
  end
@@ -12,7 +12,7 @@ module RuboCop
12
12
  # always sort the list.
13
13
  #
14
14
  # `Dir.glob` and `Dir[]` sort globbed results by default in Ruby 3.0.
15
- # So all bad cases are acceptable when Ruby 3.0 or higher are used.
15
+ # So all bad cases are acceptable when Ruby 3.0 or higher is used.
16
16
  #
17
17
  # NOTE: This cop will be deprecated and removed when supporting only Ruby 3.0 and higher.
18
18
  #
@@ -65,7 +65,9 @@ module RuboCop
65
65
 
66
66
  maximum_target_ruby_version 2.7
67
67
 
68
- def on_block(node)
68
+ # NOTE: itblock is not handled because this cop is limited to Ruby <= 2.7
69
+ # via `maximum_target_ruby_version`, so itblock nodes (Ruby 3.4+) are never encountered.
70
+ def on_block(node) # rubocop:disable InternalAffairs/ItblockHandler
69
71
  return unless node.body
70
72
  return unless unsorted_dir_loop?(node.send_node)
71
73
 
@@ -74,7 +74,7 @@ module RuboCop
74
74
  end
75
75
 
76
76
  # @!method chained_send?(node)
77
- def_node_matcher :chained_send?, '(send !nil? ...)'
77
+ def_node_matcher :chained_send?, '(call !nil? ...)'
78
78
 
79
79
  # @!method define_method?(node)
80
80
  def_node_matcher :define_method?, <<~PATTERN
@@ -3,9 +3,9 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # Warns the usage of unsafe number conversions. Unsafe
7
- # number conversion can cause unexpected error if auto type conversion
8
- # fails. Cop prefer parsing with number class instead.
6
+ # Warns against the usage of unsafe number conversions. Unsafe
7
+ # number conversion can cause an unexpected error if auto type conversion
8
+ # fails. The cop prefers parsing with a number class instead.
9
9
  #
10
10
  # Conversion with `Integer`, `Float`, etc. will raise an `ArgumentError`
11
11
  # if given input that is not numeric (eg. an empty string), whereas
@@ -14,10 +14,10 @@ module RuboCop
14
14
  # always correct to raise if a value is not numeric.
15
15
  #
16
16
  # NOTE: Some values cannot be converted properly using one of the `Kernel`
17
- # method (for instance, `Time` and `DateTime` values are allowed by this
17
+ # methods (for instance, `Time` and `DateTime` values are allowed by this
18
18
  # cop by default). Similarly, Rails' duration methods do not work well
19
19
  # with `Integer()` and can be allowed with `AllowedMethods`. By default,
20
- # there are no methods to allowed.
20
+ # there are no allowed methods.
21
21
  #
22
22
  # @safety
23
23
  # Autocorrection is unsafe because it is not guaranteed that the
@@ -66,7 +66,7 @@ module RuboCop
66
66
  # # good
67
67
  # 10.minutes.to_i
68
68
  #
69
- # @example IgnoredClasses: [Time, DateTime] (default)
69
+ # @example AllowedClasses: [Time, DateTime] (default)
70
70
  #
71
71
  # # good
72
72
  # Time.now.to_datetime.to_i
@@ -117,11 +117,11 @@ module RuboCop
117
117
 
118
118
  message = format(
119
119
  MSG,
120
- current: "#{receiver.source}.#{to_method}",
120
+ current: current_method(node, receiver, to_method),
121
121
  corrected_method: correct_method(node, receiver)
122
122
  )
123
123
  add_offense(node, message: message) do |corrector|
124
- next if part_of_ignored_node?(node)
124
+ next if safe_navigation?(node) || part_of_ignored_node?(node)
125
125
 
126
126
  corrector.replace(node, correct_method(node, node.receiver))
127
127
 
@@ -156,13 +156,22 @@ module RuboCop
156
156
  "{ |i| #{body} }"
157
157
  end
158
158
 
159
+ def current_method(node, receiver, to_method)
160
+ operator = node.csend_type? ? '&.' : '.'
161
+ "#{receiver.source}#{operator}#{to_method}"
162
+ end
163
+
159
164
  def remove_parentheses(corrector, node)
160
165
  corrector.replace(node.loc.begin, ' ')
161
166
  corrector.remove(node.loc.end)
162
167
  end
163
168
 
169
+ def safe_navigation?(node)
170
+ node.csend_type? || node.each_descendant(:csend).any?
171
+ end
172
+
164
173
  def allow_receiver?(receiver)
165
- if receiver.numeric_type? || (receiver.send_type? &&
174
+ if receiver.numeric_type? || (receiver.call_type? &&
166
175
  (conversion_method?(receiver.method_name) ||
167
176
  allowed_method_name?(receiver.method_name)))
168
177
  true
@@ -188,7 +197,7 @@ module RuboCop
188
197
  end
189
198
 
190
199
  def ignored_classes
191
- cop_config.fetch('IgnoredClasses', [])
200
+ cop_config.fetch('AllowedClasses', [])
192
201
  end
193
202
 
194
203
  def ignored_class?(name)
@@ -16,7 +16,7 @@ module RuboCop
16
16
  # ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin19]
17
17
  # -e:1: _1 is reserved for numbered parameter
18
18
  #
19
- # NOTE: The parametered parameters are from `_1` to `_9`. This cop checks `_0`, and over `_10`
19
+ # NOTE: The numbered parameters are from `_1` to `_9`. This cop checks `_0`, and over `_10`
20
20
  # as well to prevent confusion.
21
21
  #
22
22
  # @example
@@ -58,6 +58,9 @@ module RuboCop
58
58
  '(op-asgn (lvasgn $_lhs) $_operation ({int lvar} $_rhs))'
59
59
 
60
60
  def on_send(node)
61
+ # Safe navigation short-circuits to `nil` when the receiver is `nil`, so the
62
+ # result is not constant and replacing it with `0`/`1` would change behavior.
63
+ return if node.csend_type?
61
64
  return unless (lhs, operation, rhs = operation_with_constant_result?(node))
62
65
  return unless (result = constant_result?(lhs, operation, rhs))
63
66
 
@@ -38,23 +38,23 @@ module RuboCop
38
38
  def on_new_investigation
39
39
  return if processed_source.buffer.source.empty?
40
40
 
41
- encoding_line, frozen_string_literal_line = magic_comment_lines
41
+ encoding_line, other_magic_comment_line = magic_comment_lines
42
42
 
43
- return unless encoding_line && frozen_string_literal_line
44
- return if encoding_line < frozen_string_literal_line
43
+ return unless encoding_line && other_magic_comment_line
44
+ return if encoding_line < other_magic_comment_line
45
45
 
46
46
  range = processed_source.buffer.line_range(encoding_line + 1)
47
47
 
48
48
  add_offense(range) do |corrector|
49
- autocorrect(corrector, encoding_line, frozen_string_literal_line)
49
+ autocorrect(corrector, encoding_line, other_magic_comment_line)
50
50
  end
51
51
  end
52
52
 
53
53
  private
54
54
 
55
- def autocorrect(corrector, encoding_line, frozen_string_literal_line)
55
+ def autocorrect(corrector, encoding_line, other_magic_comment_line)
56
56
  range1 = processed_source.buffer.line_range(encoding_line + 1)
57
- range2 = processed_source.buffer.line_range(frozen_string_literal_line + 1)
57
+ range2 = processed_source.buffer.line_range(other_magic_comment_line + 1)
58
58
 
59
59
  corrector.replace(range1, range2.source)
60
60
  corrector.replace(range2, range1.source)
@@ -66,7 +66,7 @@ module RuboCop
66
66
  leading_magic_comments.each.with_index do |comment, index|
67
67
  if comment.encoding_specified?
68
68
  lines[0] = index
69
- elsif comment.frozen_string_literal_specified?
69
+ elsif comment.valid?
70
70
  lines[1] = index
71
71
  end
72
72
 
@@ -63,19 +63,9 @@ module RuboCop
63
63
  end
64
64
 
65
65
  def spaces_before_left_parenthesis(node)
66
- receiver = node.receiver
67
- receiver_length = if receiver
68
- receiver.source.length
69
- else
70
- 0
71
- end
72
- without_receiver = node.source[receiver_length..]
73
-
74
- # Escape question mark if any.
75
- method_regexp = Regexp.escape(node.method_name)
76
-
77
- match = without_receiver.match(/^\s*&?\.?\s*#{method_regexp}(\s+)\(/)
78
- match ? match.captures[0].length : 0
66
+ return 0 if node.parenthesized? || !node.first_argument.source.start_with?('(')
67
+
68
+ node.first_argument.source_range.begin_pos - node.loc.selector.end_pos
79
69
  end
80
70
 
81
71
  def space_range(expr, space_length)
@@ -95,7 +95,7 @@ module RuboCop
95
95
  if parent.module_type?
96
96
  namespace = parent.identifier.source
97
97
 
98
- return allow_implicit_namespaces.include?(namespace)
98
+ return true if allow_implicit_namespaces.include?(namespace)
99
99
  end
100
100
 
101
101
  implicit_namespace?(parent)
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Cop
5
5
  module Lint
6
6
  # Checks for `rand(1)` calls.
7
- # Such calls always return `0`.
7
+ # Such calls always return `0`, and so do `rand(-1)`, `rand(1.0)`, and `rand(-1.0)`.
8
8
  #
9
9
  # @example
10
10
  #
@@ -283,7 +283,10 @@ module RuboCop
283
283
  end
284
284
 
285
285
  def matching_range(haystack, needle)
286
- offset = haystack.source.index(needle)
286
+ # Match the cop name as a whole token so a shorter name is not found inside a
287
+ # longer one that shares its prefix (e.g. `Lint/AmbiguousOperator` in
288
+ # `Lint/AmbiguousOperatorPrecedence`).
289
+ offset = haystack.source.index(/#{Regexp.escape(needle)}(?!\w)/)
287
290
  return unless offset
288
291
 
289
292
  offset += haystack.begin_pos
@@ -1,19 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # The Lint/RedundantCopEnableDirective and Lint/RedundantCopDisableDirective
4
- # cops need to be disabled so as to be able to provide a (bad) example of an
5
- # unneeded enable.
6
-
7
- # rubocop:disable Lint/RedundantCopEnableDirective
8
- # rubocop:disable Lint/RedundantCopDisableDirective
9
3
  module RuboCop
10
4
  module Cop
11
5
  module Lint
12
6
  # Detects instances of rubocop:enable comments that can be
13
7
  # removed.
14
8
  #
15
- # When comment enables all cops at once `rubocop:enable all`
16
- # that cop checks whether any cop was actually enabled.
9
+ # When a comment enables all cops at once `rubocop:enable all`
10
+ # the cop checks whether any cop was actually enabled.
17
11
  #
18
12
  # @example
19
13
  #
@@ -80,7 +74,10 @@ module RuboCop
80
74
  end
81
75
 
82
76
  def cop_name_indention(comment, name)
83
- comment.text.index(name)
77
+ # Match the cop name as a whole token so a shorter name is not found inside a
78
+ # longer one that shares its prefix (e.g. `Layout/EmptyLines` in
79
+ # `Layout/EmptyLinesAfterModuleInclusion`).
80
+ comment.text.index(/#{Regexp.escape(name)}(?!\w)/)
84
81
  end
85
82
 
86
83
  def range_with_comma(comment, name)
@@ -130,6 +127,3 @@ module RuboCop
130
127
  end
131
128
  end
132
129
  end
133
-
134
- # rubocop:enable Lint/RedundantCopDisableDirective
135
- # rubocop:enable Lint/RedundantCopEnableDirective
@@ -38,10 +38,10 @@ module RuboCop
38
38
  GLOB_METHODS = %i[glob []].freeze
39
39
 
40
40
  def on_send(node)
41
- return unless (receiver = node.receiver)
42
- return unless receiver.receiver&.const_type? && receiver.receiver.short_name == :Dir
43
- return unless GLOB_METHODS.include?(receiver.method_name)
44
- return if multiple_argument?(receiver)
41
+ return unless dir_glob?(node.receiver)
42
+ # `sort` with a comparator block or block-pass changes the order, so it is
43
+ # not redundant with the default sorting performed by `Dir.glob`/`Dir[]`.
44
+ return if sort_with_comparator?(node) || multiple_argument?(node.receiver)
45
45
 
46
46
  selector = node.loc.selector
47
47
 
@@ -53,9 +53,20 @@ module RuboCop
53
53
 
54
54
  private
55
55
 
56
+ def dir_glob?(receiver)
57
+ return false unless receiver&.receiver&.const_type?
58
+ return false unless receiver.receiver.short_name == :Dir
59
+
60
+ GLOB_METHODS.include?(receiver.method_name)
61
+ end
62
+
56
63
  def multiple_argument?(glob_method)
57
64
  glob_method.arguments.count >= 2 || glob_method.first_argument&.splat_type?
58
65
  end
66
+
67
+ def sort_with_comparator?(node)
68
+ node.parent&.any_block_type? || node.last_argument&.block_pass_type?
69
+ end
59
70
  end
60
71
  end
61
72
  end
@@ -62,6 +62,22 @@ module RuboCop
62
62
  # do_something if attrs.respond_to?(:[])
63
63
  #
64
64
  # # bad
65
+ # foo&.bar ? foo&.bar.baz : qux
66
+ #
67
+ # # good
68
+ # foo&.bar ? foo.bar.baz : qux
69
+ #
70
+ # # bad
71
+ # if foo&.bar
72
+ # foo&.bar.baz
73
+ # end
74
+ #
75
+ # # good
76
+ # if foo&.bar
77
+ # foo.bar.baz
78
+ # end
79
+ #
80
+ # # bad
65
81
  # while node&.is_a?(BeginNode)
66
82
  # node = node.parent
67
83
  # end
@@ -71,8 +87,9 @@ module RuboCop
71
87
  # node = node.parent
72
88
  # end
73
89
  #
74
- # # good - without `&.` this will always return `true`
90
+ # # good - without `&.` this changes the return value for `nil`
75
91
  # foo&.respond_to?(:to_a)
92
+ # foo&.respond_to?(:class)
76
93
  #
77
94
  # # bad - for `nil`s conversion methods return default values for the type
78
95
  # foo&.to_h || {}
@@ -151,22 +168,22 @@ module RuboCop
151
168
  MSG_NON_NIL = 'Redundant safe navigation on non-nil receiver (detected by analyzing ' \
152
169
  'previous code/method invocations).'
153
170
 
154
- NIL_SPECIFIC_METHODS = (nil.methods - Object.new.methods).to_set.freeze
171
+ NIL_METHODS = nil.methods.to_set.freeze
155
172
 
156
173
  SNAKE_CASE = /\A[[:digit:][:upper:]_]+\z/.freeze
157
174
 
158
175
  GUARANTEED_INSTANCE_METHODS = %i[to_s to_i to_f to_a to_h].freeze
159
176
 
160
- # @!method respond_to_nil_specific_method?(node)
161
- def_node_matcher :respond_to_nil_specific_method?, <<~PATTERN
162
- (csend _ :respond_to? (sym %NIL_SPECIFIC_METHODS))
177
+ # @!method respond_to_nil_method?(node)
178
+ def_node_matcher :respond_to_nil_method?, <<~PATTERN
179
+ (csend _ :respond_to? (sym %NIL_METHODS))
163
180
  PATTERN
164
181
 
165
182
  # @!method conversion_with_default?(node)
166
183
  def_node_matcher :conversion_with_default?, <<~PATTERN
167
184
  {
168
185
  (or $(csend _ :to_h) (hash))
169
- (or (block $(csend _ :to_h) ...) (hash))
186
+ (or (any_block $(csend _ :to_h) ...) (hash))
170
187
  (or $(csend _ :to_a) (array))
171
188
  (or $(csend _ :to_i) (int 0))
172
189
  (or $(csend _ :to_f) (float 0.0))
@@ -174,7 +191,6 @@ module RuboCop
174
191
  }
175
192
  PATTERN
176
193
 
177
- # rubocop:disable Metrics/AbcSize
178
194
  def on_csend(node)
179
195
  range = node.loc.dot
180
196
 
@@ -187,14 +203,10 @@ module RuboCop
187
203
  end
188
204
  end
189
205
 
190
- unless assume_receiver_instance_exists?(node.receiver)
191
- return if !guaranteed_instance?(node.receiver) && !check?(node)
192
- return if respond_to_nil_specific_method?(node)
193
- end
206
+ return if guarded_by_nil_receiver?(node)
194
207
 
195
208
  add_offense(range) { |corrector| corrector.replace(range, '.') }
196
209
  end
197
- # rubocop:enable Metrics/AbcSize
198
210
 
199
211
  # rubocop:disable Metrics/AbcSize
200
212
  def on_or(node)
@@ -213,6 +225,18 @@ module RuboCop
213
225
 
214
226
  private
215
227
 
228
+ # Returns true when the `&.` is meaningful because the receiver may actually be nil.
229
+ def guarded_by_nil_receiver?(node)
230
+ return false if assume_receiver_instance_exists?(node.receiver)
231
+
232
+ guaranteed_instance = guaranteed_instance?(node.receiver)
233
+ return true if !guaranteed_instance && !check?(node)
234
+
235
+ # `nil.respond_to?(<nil method>)` is `true`, so `&.` is meaningful when the receiver
236
+ # may be nil. A guaranteed instance can never be nil, so `&.` is still redundant there.
237
+ respond_to_nil_method?(node) && !guaranteed_instance
238
+ end
239
+
216
240
  def assume_receiver_instance_exists?(receiver)
217
241
  return true if receiver.const_type? && !receiver.short_name.match?(SNAKE_CASE)
218
242
 
@@ -122,6 +122,10 @@ module RuboCop
122
122
 
123
123
  grandparent = node.parent.parent
124
124
  return if grandparent && !ASSIGNMENT_TYPES.include?(grandparent.type)
125
+ # An empty array/percent literal (`*[]`, `*%w()`, ...) expands to nothing, so
126
+ # removing the splat would produce invalid or semantically different code.
127
+ elsif expanded_item.array_type? && expanded_item.children.empty?
128
+ return
125
129
  end
126
130
 
127
131
  yield