rubocop 1.66.1 → 1.72.2

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 (434) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +2 -2
  4. data/config/default.yml +162 -14
  5. data/config/internal_affairs.yml +11 -0
  6. data/lib/rubocop/cached_data.rb +12 -4
  7. data/lib/rubocop/cli/command/auto_generate_config.rb +6 -7
  8. data/lib/rubocop/cli/command/execute_runner.rb +4 -4
  9. data/lib/rubocop/cli/command/lsp.rb +2 -2
  10. data/lib/rubocop/cli/command/show_cops.rb +24 -2
  11. data/lib/rubocop/cli/command/suggest_extensions.rb +7 -1
  12. data/lib/rubocop/cli/command/version.rb +2 -2
  13. data/lib/rubocop/comment_config.rb +2 -2
  14. data/lib/rubocop/config.rb +17 -4
  15. data/lib/rubocop/config_loader.rb +48 -8
  16. data/lib/rubocop/config_loader_resolver.rb +36 -11
  17. data/lib/rubocop/config_validator.rb +20 -9
  18. data/lib/rubocop/cop/autocorrect_logic.rb +36 -19
  19. data/lib/rubocop/cop/base.rb +13 -3
  20. data/lib/rubocop/cop/bundler/duplicated_gem.rb +2 -2
  21. data/lib/rubocop/cop/bundler/gem_comment.rb +1 -1
  22. data/lib/rubocop/cop/bundler/gem_filename.rb +0 -1
  23. data/lib/rubocop/cop/bundler/gem_version.rb +1 -0
  24. data/lib/rubocop/cop/bundler/insecure_protocol_source.rb +0 -1
  25. data/lib/rubocop/cop/cop.rb +8 -0
  26. data/lib/rubocop/cop/correctors/alignment_corrector.rb +1 -12
  27. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +1 -1
  28. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +1 -1
  29. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +10 -0
  30. data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +1 -2
  31. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +0 -2
  32. data/lib/rubocop/cop/generator.rb +6 -0
  33. data/lib/rubocop/cop/internal_affairs/cop_description.rb +0 -4
  34. data/lib/rubocop/cop/internal_affairs/cop_enabled.rb +85 -0
  35. data/lib/rubocop/cop/internal_affairs/example_description.rb +4 -2
  36. data/lib/rubocop/cop/internal_affairs/location_exists.rb +116 -0
  37. data/lib/rubocop/cop/internal_affairs/location_expression.rb +2 -1
  38. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +3 -4
  39. data/lib/rubocop/cop/internal_affairs/node_first_or_last_argument.rb +3 -2
  40. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -1
  41. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_processor.rb +63 -0
  42. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb +131 -0
  43. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +229 -0
  44. data/lib/rubocop/cop/internal_affairs/node_type_multiple_predicates.rb +126 -0
  45. data/lib/rubocop/cop/internal_affairs/node_type_predicate.rb +4 -3
  46. data/lib/rubocop/cop/internal_affairs/numblock_handler.rb +1 -1
  47. data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +90 -0
  48. data/lib/rubocop/cop/internal_affairs/operator_keyword.rb +48 -0
  49. data/lib/rubocop/cop/internal_affairs/plugin.rb +33 -0
  50. data/lib/rubocop/cop/internal_affairs/redundant_message_argument.rb +6 -21
  51. data/lib/rubocop/cop/internal_affairs/redundant_source_range.rb +11 -2
  52. data/lib/rubocop/cop/internal_affairs/single_line_comparison.rb +5 -4
  53. data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +0 -2
  54. data/lib/rubocop/cop/internal_affairs/undefined_config.rb +7 -1
  55. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +0 -5
  56. data/lib/rubocop/cop/internal_affairs.rb +6 -0
  57. data/lib/rubocop/cop/layout/access_modifier_indentation.rb +6 -2
  58. data/lib/rubocop/cop/layout/argument_alignment.rb +2 -9
  59. data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
  60. data/lib/rubocop/cop/layout/begin_end_alignment.rb +0 -1
  61. data/lib/rubocop/cop/layout/block_alignment.rb +3 -2
  62. data/lib/rubocop/cop/layout/class_structure.rb +9 -9
  63. data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
  64. data/lib/rubocop/cop/layout/dot_position.rb +1 -1
  65. data/lib/rubocop/cop/layout/else_alignment.rb +2 -2
  66. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +3 -3
  67. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +7 -11
  68. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +3 -3
  69. data/lib/rubocop/cop/layout/empty_lines_around_begin_body.rb +5 -6
  70. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +4 -5
  71. data/lib/rubocop/cop/layout/empty_lines_around_method_body.rb +23 -1
  72. data/lib/rubocop/cop/layout/end_alignment.rb +1 -1
  73. data/lib/rubocop/cop/layout/extra_spacing.rb +1 -1
  74. data/lib/rubocop/cop/layout/first_argument_indentation.rb +3 -8
  75. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +2 -7
  76. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +2 -7
  77. data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +1 -1
  78. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +8 -0
  79. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +2 -2
  80. data/lib/rubocop/cop/layout/hash_alignment.rb +6 -4
  81. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -1
  82. data/lib/rubocop/cop/layout/indentation_width.rb +11 -12
  83. data/lib/rubocop/cop/layout/leading_comment_space.rb +71 -1
  84. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +11 -2
  85. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +7 -1
  86. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +2 -2
  87. data/lib/rubocop/cop/layout/line_length.rb +119 -4
  88. data/lib/rubocop/cop/layout/multiline_hash_key_line_breaks.rb +1 -1
  89. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +25 -0
  90. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +2 -1
  91. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +4 -4
  92. data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +1 -1
  93. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -3
  94. data/lib/rubocop/cop/layout/parameter_alignment.rb +3 -4
  95. data/lib/rubocop/cop/layout/redundant_line_break.rb +10 -41
  96. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +4 -3
  97. data/lib/rubocop/cop/layout/single_line_block_chain.rb +1 -1
  98. data/lib/rubocop/cop/layout/space_after_colon.rb +2 -2
  99. data/lib/rubocop/cop/layout/space_after_comma.rb +1 -1
  100. data/lib/rubocop/cop/layout/space_after_method_name.rb +1 -1
  101. data/lib/rubocop/cop/layout/space_after_semicolon.rb +1 -1
  102. data/lib/rubocop/cop/layout/space_around_keyword.rb +2 -1
  103. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +1 -1
  104. data/lib/rubocop/cop/layout/space_around_operators.rb +19 -20
  105. data/lib/rubocop/cop/layout/space_before_brackets.rb +5 -5
  106. data/lib/rubocop/cop/layout/space_before_comma.rb +1 -1
  107. data/lib/rubocop/cop/layout/space_before_semicolon.rb +1 -1
  108. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +6 -0
  109. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +4 -0
  110. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +4 -0
  111. data/lib/rubocop/cop/layout/space_inside_string_interpolation.rb +0 -1
  112. data/lib/rubocop/cop/layout/trailing_whitespace.rb +5 -3
  113. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  114. data/lib/rubocop/cop/lint/ambiguous_range.rb +4 -1
  115. data/lib/rubocop/cop/lint/array_literal_in_regexp.rb +119 -0
  116. data/lib/rubocop/cop/lint/assignment_in_condition.rb +1 -3
  117. data/lib/rubocop/cop/lint/big_decimal_new.rb +4 -7
  118. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +10 -12
  119. data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -1
  120. data/lib/rubocop/cop/lint/circular_argument_reference.rb +6 -0
  121. data/lib/rubocop/cop/lint/constant_definition_in_block.rb +3 -3
  122. data/lib/rubocop/cop/lint/constant_reassignment.rb +148 -0
  123. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +84 -0
  124. data/lib/rubocop/cop/lint/debugger.rb +1 -1
  125. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +2 -1
  126. data/lib/rubocop/cop/lint/duplicate_branch.rb +39 -4
  127. data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +1 -1
  128. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +1 -1
  129. data/lib/rubocop/cop/lint/duplicate_set_element.rb +87 -0
  130. data/lib/rubocop/cop/lint/empty_ensure.rb +1 -1
  131. data/lib/rubocop/cop/lint/empty_expression.rb +0 -2
  132. data/lib/rubocop/cop/lint/empty_file.rb +0 -2
  133. data/lib/rubocop/cop/lint/ensure_return.rb +1 -4
  134. data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
  135. data/lib/rubocop/cop/lint/float_comparison.rb +20 -9
  136. data/lib/rubocop/cop/lint/float_out_of_range.rb +2 -4
  137. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +2 -2
  138. data/lib/rubocop/cop/lint/hash_new_with_keyword_arguments_as_default.rb +55 -0
  139. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +11 -5
  140. data/lib/rubocop/cop/lint/interpolation_check.rb +9 -0
  141. data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +8 -14
  142. data/lib/rubocop/cop/lint/literal_as_condition.rb +1 -0
  143. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +1 -1
  144. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +49 -8
  145. data/lib/rubocop/cop/lint/missing_super.rb +2 -2
  146. data/lib/rubocop/cop/lint/mixed_case_range.rb +3 -6
  147. data/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb +1 -1
  148. data/lib/rubocop/cop/lint/nested_method_definition.rb +9 -5
  149. data/lib/rubocop/cop/lint/next_without_accumulator.rb +1 -1
  150. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +2 -2
  151. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +12 -3
  152. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +3 -3
  153. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +1 -1
  154. data/lib/rubocop/cop/lint/number_conversion.rb +0 -1
  155. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -2
  156. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +93 -0
  157. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +1 -2
  158. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +3 -2
  159. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +6 -11
  160. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +1 -1
  161. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
  162. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +13 -8
  163. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +8 -7
  164. data/lib/rubocop/cop/lint/redundant_string_coercion.rb +2 -2
  165. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +231 -0
  166. data/lib/rubocop/cop/lint/refinement_import_methods.rb +1 -1
  167. data/lib/rubocop/cop/lint/regexp_as_condition.rb +0 -1
  168. data/lib/rubocop/cop/lint/rescue_exception.rb +1 -1
  169. data/lib/rubocop/cop/lint/rescue_type.rb +3 -7
  170. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +17 -1
  171. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +109 -41
  172. data/lib/rubocop/cop/lint/self_assignment.rb +8 -10
  173. data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -1
  174. data/lib/rubocop/cop/lint/shared_mutable_default.rb +65 -0
  175. data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
  176. data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +111 -0
  177. data/lib/rubocop/cop/lint/symbol_conversion.rb +2 -2
  178. data/lib/rubocop/cop/lint/syntax.rb +4 -1
  179. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +88 -0
  180. data/lib/rubocop/cop/lint/unexpected_block_arity.rb +1 -1
  181. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -1
  182. data/lib/rubocop/cop/lint/unreachable_code.rb +51 -2
  183. data/lib/rubocop/cop/lint/unreachable_loop.rb +1 -1
  184. data/lib/rubocop/cop/lint/unused_method_argument.rb +18 -2
  185. data/lib/rubocop/cop/lint/uri_regexp.rb +25 -7
  186. data/lib/rubocop/cop/lint/useless_access_modifier.rb +4 -4
  187. data/lib/rubocop/cop/lint/useless_assignment.rb +1 -1
  188. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +80 -0
  189. data/lib/rubocop/cop/lint/useless_defined.rb +55 -0
  190. data/lib/rubocop/cop/lint/useless_else_without_rescue.rb +4 -0
  191. data/lib/rubocop/cop/lint/useless_method_definition.rb +1 -1
  192. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +2 -1
  193. data/lib/rubocop/cop/lint/useless_rescue.rb +1 -1
  194. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +2 -2
  195. data/lib/rubocop/cop/lint/useless_setter_call.rb +14 -25
  196. data/lib/rubocop/cop/lint/void.rb +8 -11
  197. data/lib/rubocop/cop/metrics/block_nesting.rb +1 -1
  198. data/lib/rubocop/cop/metrics/class_length.rb +9 -9
  199. data/lib/rubocop/cop/metrics/collection_literal_length.rb +7 -0
  200. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +5 -2
  201. data/lib/rubocop/cop/metrics/method_length.rb +8 -1
  202. data/lib/rubocop/cop/metrics/module_length.rb +1 -1
  203. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
  204. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +1 -1
  205. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +2 -3
  206. data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +7 -7
  207. data/lib/rubocop/cop/mixin/alignment.rb +2 -2
  208. data/lib/rubocop/cop/mixin/allowed_pattern.rb +4 -4
  209. data/lib/rubocop/cop/mixin/check_assignment.rb +4 -12
  210. data/lib/rubocop/cop/mixin/check_line_breakable.rb +20 -10
  211. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +49 -0
  212. data/lib/rubocop/cop/mixin/comments_help.rb +8 -3
  213. data/lib/rubocop/cop/mixin/dig_help.rb +27 -0
  214. data/lib/rubocop/cop/mixin/endless_method_rewriter.rb +24 -0
  215. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +4 -2
  216. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +22 -22
  217. data/lib/rubocop/cop/mixin/hash_subset.rb +188 -0
  218. data/lib/rubocop/cop/mixin/hash_transform_method.rb +74 -74
  219. data/lib/rubocop/cop/mixin/line_length_help.rb +5 -4
  220. data/lib/rubocop/cop/mixin/method_complexity.rb +1 -1
  221. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +5 -9
  222. data/lib/rubocop/cop/mixin/percent_array.rb +1 -1
  223. data/lib/rubocop/cop/mixin/percent_literal.rb +1 -1
  224. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +68 -30
  225. data/lib/rubocop/cop/mixin/range_help.rb +3 -4
  226. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  227. data/lib/rubocop/cop/mixin/statement_modifier.rb +11 -5
  228. data/lib/rubocop/cop/mixin/string_help.rb +2 -2
  229. data/lib/rubocop/cop/mixin/string_literals_help.rb +1 -1
  230. data/lib/rubocop/cop/mixin/target_ruby_version.rb +17 -1
  231. data/lib/rubocop/cop/mixin/trailing_comma.rb +3 -3
  232. data/lib/rubocop/cop/naming/accessor_method_name.rb +6 -6
  233. data/lib/rubocop/cop/naming/block_forwarding.rb +20 -16
  234. data/lib/rubocop/cop/naming/constant_name.rb +6 -7
  235. data/lib/rubocop/cop/naming/file_name.rb +0 -2
  236. data/lib/rubocop/cop/naming/inclusive_language.rb +12 -3
  237. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +11 -12
  238. data/lib/rubocop/cop/naming/predicate_name.rb +45 -1
  239. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +6 -14
  240. data/lib/rubocop/cop/naming/variable_name.rb +3 -4
  241. data/lib/rubocop/cop/naming/variable_number.rb +2 -3
  242. data/lib/rubocop/cop/offense.rb +4 -5
  243. data/lib/rubocop/cop/security/compound_hash.rb +2 -0
  244. data/lib/rubocop/cop/security/yaml_load.rb +3 -2
  245. data/lib/rubocop/cop/style/access_modifier_declarations.rb +96 -28
  246. data/lib/rubocop/cop/style/accessor_grouping.rb +10 -2
  247. data/lib/rubocop/cop/style/ambiguous_endless_method_definition.rb +79 -0
  248. data/lib/rubocop/cop/style/and_or.rb +1 -1
  249. data/lib/rubocop/cop/style/arguments_forwarding.rb +78 -22
  250. data/lib/rubocop/cop/style/array_first_last.rb +18 -2
  251. data/lib/rubocop/cop/style/array_intersect.rb +5 -4
  252. data/lib/rubocop/cop/style/bitwise_predicate.rb +100 -0
  253. data/lib/rubocop/cop/style/block_delimiters.rb +49 -19
  254. data/lib/rubocop/cop/style/case_like_if.rb +8 -11
  255. data/lib/rubocop/cop/style/class_and_module_children.rb +6 -3
  256. data/lib/rubocop/cop/style/collection_compact.rb +10 -10
  257. data/lib/rubocop/cop/style/collection_methods.rb +1 -1
  258. data/lib/rubocop/cop/style/combinable_defined.rb +115 -0
  259. data/lib/rubocop/cop/style/combinable_loops.rb +9 -2
  260. data/lib/rubocop/cop/style/commented_keyword.rb +17 -1
  261. data/lib/rubocop/cop/style/concat_array_literals.rb +1 -1
  262. data/lib/rubocop/cop/style/conditional_assignment.rb +26 -26
  263. data/lib/rubocop/cop/style/constant_visibility.rb +3 -12
  264. data/lib/rubocop/cop/style/data_inheritance.rb +1 -1
  265. data/lib/rubocop/cop/style/dig_chain.rb +89 -0
  266. data/lib/rubocop/cop/style/documentation.rb +1 -1
  267. data/lib/rubocop/cop/style/double_negation.rb +3 -3
  268. data/lib/rubocop/cop/style/each_for_simple_loop.rb +4 -7
  269. data/lib/rubocop/cop/style/each_with_object.rb +2 -3
  270. data/lib/rubocop/cop/style/empty_else.rb +4 -2
  271. data/lib/rubocop/cop/style/empty_literal.rb +1 -1
  272. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  273. data/lib/rubocop/cop/style/endless_method.rb +1 -14
  274. data/lib/rubocop/cop/style/eval_with_location.rb +2 -2
  275. data/lib/rubocop/cop/style/exact_regexp_match.rb +2 -3
  276. data/lib/rubocop/cop/style/explicit_block_argument.rb +15 -2
  277. data/lib/rubocop/cop/style/exponential_notation.rb +1 -1
  278. data/lib/rubocop/cop/style/fetch_env_var.rb +2 -1
  279. data/lib/rubocop/cop/style/file_null.rb +89 -0
  280. data/lib/rubocop/cop/style/file_touch.rb +75 -0
  281. data/lib/rubocop/cop/style/float_division.rb +8 -4
  282. data/lib/rubocop/cop/style/for.rb +0 -1
  283. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +1 -1
  284. data/lib/rubocop/cop/style/global_vars.rb +1 -3
  285. data/lib/rubocop/cop/style/guard_clause.rb +16 -3
  286. data/lib/rubocop/cop/style/hash_conversion.rb +1 -2
  287. data/lib/rubocop/cop/style/hash_each_methods.rb +9 -6
  288. data/lib/rubocop/cop/style/hash_except.rb +35 -147
  289. data/lib/rubocop/cop/style/hash_slice.rb +80 -0
  290. data/lib/rubocop/cop/style/hash_syntax.rb +8 -5
  291. data/lib/rubocop/cop/style/identical_conditional_branches.rb +22 -3
  292. data/lib/rubocop/cop/style/if_inside_else.rb +1 -2
  293. data/lib/rubocop/cop/style/if_unless_modifier.rb +3 -3
  294. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +2 -3
  295. data/lib/rubocop/cop/style/if_with_semicolon.rb +26 -11
  296. data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
  297. data/lib/rubocop/cop/style/inverse_methods.rb +6 -7
  298. data/lib/rubocop/cop/style/it_assignment.rb +36 -0
  299. data/lib/rubocop/cop/style/keyword_arguments_merging.rb +67 -0
  300. data/lib/rubocop/cop/style/keyword_parameters_order.rb +1 -1
  301. data/lib/rubocop/cop/style/lambda.rb +1 -1
  302. data/lib/rubocop/cop/style/lambda_call.rb +3 -2
  303. data/lib/rubocop/cop/style/map_into_array.rb +60 -9
  304. data/lib/rubocop/cop/style/map_to_hash.rb +1 -1
  305. data/lib/rubocop/cop/style/map_to_set.rb +3 -2
  306. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +32 -20
  307. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +2 -0
  308. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +8 -11
  309. data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +2 -4
  310. data/lib/rubocop/cop/style/method_def_parentheses.rb +1 -1
  311. data/lib/rubocop/cop/style/missing_else.rb +2 -0
  312. data/lib/rubocop/cop/style/missing_respond_to_missing.rb +33 -3
  313. data/lib/rubocop/cop/style/multiline_block_chain.rb +1 -1
  314. data/lib/rubocop/cop/style/multiline_memoization.rb +2 -2
  315. data/lib/rubocop/cop/style/multiple_comparison.rb +52 -51
  316. data/lib/rubocop/cop/style/mutable_constant.rb +7 -8
  317. data/lib/rubocop/cop/style/negated_if_else_condition.rb +7 -5
  318. data/lib/rubocop/cop/style/nested_modifier.rb +1 -1
  319. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +2 -2
  320. data/lib/rubocop/cop/style/nested_ternary_operator.rb +5 -4
  321. data/lib/rubocop/cop/style/not.rb +1 -1
  322. data/lib/rubocop/cop/style/object_then.rb +14 -15
  323. data/lib/rubocop/cop/style/one_line_conditional.rb +29 -4
  324. data/lib/rubocop/cop/style/open_struct_use.rb +5 -5
  325. data/lib/rubocop/cop/style/operator_method_call.rb +25 -7
  326. data/lib/rubocop/cop/style/or_assignment.rb +3 -6
  327. data/lib/rubocop/cop/style/parallel_assignment.rb +9 -18
  328. data/lib/rubocop/cop/style/parentheses_around_condition.rb +2 -2
  329. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +1 -1
  330. data/lib/rubocop/cop/style/proc.rb +1 -2
  331. data/lib/rubocop/cop/style/quoted_symbols.rb +1 -1
  332. data/lib/rubocop/cop/style/raise_args.rb +7 -5
  333. data/lib/rubocop/cop/style/random_with_offset.rb +3 -3
  334. data/lib/rubocop/cop/style/redundant_argument.rb +3 -1
  335. data/lib/rubocop/cop/style/redundant_assignment.rb +1 -1
  336. data/lib/rubocop/cop/style/redundant_begin.rb +5 -1
  337. data/lib/rubocop/cop/style/redundant_condition.rb +39 -24
  338. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +2 -1
  339. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +6 -10
  340. data/lib/rubocop/cop/style/redundant_each.rb +1 -1
  341. data/lib/rubocop/cop/style/redundant_exception.rb +2 -2
  342. data/lib/rubocop/cop/style/redundant_format.rb +238 -0
  343. data/lib/rubocop/cop/style/redundant_freeze.rb +2 -2
  344. data/lib/rubocop/cop/style/redundant_initialize.rb +12 -3
  345. data/lib/rubocop/cop/style/redundant_line_continuation.rb +56 -17
  346. data/lib/rubocop/cop/style/redundant_parentheses.rb +37 -25
  347. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +4 -0
  348. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +1 -1
  349. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +1 -1
  350. data/lib/rubocop/cop/style/redundant_return.rb +2 -2
  351. data/lib/rubocop/cop/style/redundant_self.rb +8 -15
  352. data/lib/rubocop/cop/style/redundant_self_assignment.rb +20 -32
  353. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +4 -4
  354. data/lib/rubocop/cop/style/redundant_sort.rb +3 -3
  355. data/lib/rubocop/cop/style/redundant_string_escape.rb +2 -2
  356. data/lib/rubocop/cop/style/require_order.rb +1 -1
  357. data/lib/rubocop/cop/style/rescue_modifier.rb +15 -4
  358. data/lib/rubocop/cop/style/return_nil.rb +1 -1
  359. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +54 -12
  360. data/lib/rubocop/cop/style/safe_navigation.rb +105 -51
  361. data/lib/rubocop/cop/style/safe_navigation_chain_length.rb +52 -0
  362. data/lib/rubocop/cop/style/select_by_regexp.rb +10 -7
  363. data/lib/rubocop/cop/style/self_assignment.rb +11 -17
  364. data/lib/rubocop/cop/style/semicolon.rb +2 -2
  365. data/lib/rubocop/cop/style/send_with_literal_method_name.rb +2 -1
  366. data/lib/rubocop/cop/style/signal_exception.rb +2 -3
  367. data/lib/rubocop/cop/style/single_argument_dig.rb +9 -5
  368. data/lib/rubocop/cop/style/single_line_block_params.rb +1 -1
  369. data/lib/rubocop/cop/style/single_line_do_end_block.rb +12 -3
  370. data/lib/rubocop/cop/style/single_line_methods.rb +3 -4
  371. data/lib/rubocop/cop/style/slicing_with_range.rb +40 -11
  372. data/lib/rubocop/cop/style/sole_nested_conditional.rb +4 -5
  373. data/lib/rubocop/cop/style/special_global_vars.rb +1 -1
  374. data/lib/rubocop/cop/style/string_concatenation.rb +14 -13
  375. data/lib/rubocop/cop/style/string_literals.rb +1 -1
  376. data/lib/rubocop/cop/style/string_methods.rb +1 -1
  377. data/lib/rubocop/cop/style/struct_inheritance.rb +1 -1
  378. data/lib/rubocop/cop/style/super_arguments.rb +65 -17
  379. data/lib/rubocop/cop/style/swap_values.rb +4 -15
  380. data/lib/rubocop/cop/style/ternary_parentheses.rb +26 -5
  381. data/lib/rubocop/cop/style/top_level_method_definition.rb +1 -1
  382. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +4 -1
  383. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +4 -4
  384. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  385. data/lib/rubocop/cop/style/variable_interpolation.rb +1 -2
  386. data/lib/rubocop/cop/style/while_until_modifier.rb +0 -1
  387. data/lib/rubocop/cop/style/yoda_condition.rb +8 -4
  388. data/lib/rubocop/cop/style/yoda_expression.rb +2 -1
  389. data/lib/rubocop/cop/team.rb +8 -1
  390. data/lib/rubocop/cop/util.rb +12 -5
  391. data/lib/rubocop/cop/utils/format_string.rb +7 -5
  392. data/lib/rubocop/cop/variable_force/assignment.rb +18 -3
  393. data/lib/rubocop/cop/variable_force/branch.rb +1 -1
  394. data/lib/rubocop/cop/variable_force/variable.rb +18 -2
  395. data/lib/rubocop/cop/variable_force/variable_table.rb +5 -5
  396. data/lib/rubocop/cop/variable_force.rb +4 -10
  397. data/lib/rubocop/cops_documentation_generator.rb +100 -51
  398. data/lib/rubocop/directive_comment.rb +44 -10
  399. data/lib/rubocop/file_finder.rb +9 -4
  400. data/lib/rubocop/formatter/disabled_config_formatter.rb +1 -1
  401. data/lib/rubocop/formatter/formatter_set.rb +1 -1
  402. data/lib/rubocop/lsp/diagnostic.rb +189 -0
  403. data/lib/rubocop/lsp/logger.rb +2 -2
  404. data/lib/rubocop/lsp/routes.rb +7 -23
  405. data/lib/rubocop/lsp/runtime.rb +18 -48
  406. data/lib/rubocop/lsp/server.rb +0 -3
  407. data/lib/rubocop/lsp/stdin_runner.rb +83 -0
  408. data/lib/rubocop/magic_comment.rb +3 -3
  409. data/lib/rubocop/options.rb +28 -12
  410. data/lib/rubocop/path_util.rb +15 -8
  411. data/lib/rubocop/plugin/configuration_integrator.rb +143 -0
  412. data/lib/rubocop/plugin/load_error.rb +26 -0
  413. data/lib/rubocop/plugin/loader.rb +100 -0
  414. data/lib/rubocop/plugin/not_supported_error.rb +29 -0
  415. data/lib/rubocop/plugin.rb +39 -0
  416. data/lib/rubocop/rake_task.rb +4 -1
  417. data/lib/rubocop/result_cache.rb +13 -13
  418. data/lib/rubocop/rspec/cop_helper.rb +9 -0
  419. data/lib/rubocop/rspec/expect_offense.rb +7 -2
  420. data/lib/rubocop/rspec/shared_contexts.rb +4 -1
  421. data/lib/rubocop/rspec/support.rb +1 -2
  422. data/lib/rubocop/runner.rb +22 -12
  423. data/lib/rubocop/server/cache.rb +39 -1
  424. data/lib/rubocop/server/cli.rb +2 -2
  425. data/lib/rubocop/server/core.rb +1 -0
  426. data/lib/rubocop/target_finder.rb +1 -0
  427. data/lib/rubocop/target_ruby.rb +28 -13
  428. data/lib/rubocop/version.rb +42 -6
  429. data/lib/rubocop/yaml_duplication_checker.rb +20 -27
  430. data/lib/rubocop.rb +29 -0
  431. data/lib/ruby_lsp/rubocop/addon.rb +75 -0
  432. data/lib/ruby_lsp/rubocop/runtime_adapter.rb +47 -0
  433. metadata +75 -19
  434. data/lib/rubocop/rspec/host_environment_simulation_helper.rb +0 -28
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InternalAffairs
6
+ # Use `config.cop_enabled?('Department/CopName')` instead of
7
+ # traversing the config hash.
8
+ #
9
+ # @example
10
+ # # `for_cop(...)['Enabled']
11
+ #
12
+ # # bad
13
+ # config.for_cop('Department/CopName')['Enabled']
14
+ #
15
+ # # good
16
+ # config.cop_enabled?('Department/CopName')
17
+ #
18
+ # @example
19
+ # # when keeping a cop's config in a local and then checking the `Enabled` key
20
+ #
21
+ # # bad
22
+ # cop_config = config.for_cop('Department/CopName')
23
+ # cop_config['Enabled'] && cop_config['Foo']
24
+ #
25
+ # # good
26
+ # config.for_enabled_cop('Department/CopName')['Foo']
27
+ #
28
+ class CopEnabled < Base
29
+ extend AutoCorrector
30
+
31
+ MSG = 'Use `%<replacement>s` instead of `%<source>s`.'
32
+ MSG_HASH = 'Consider replacing uses of `%<hash_name>s` with `config.for_enabled_cop`.'
33
+
34
+ RESTRICT_ON_SEND = [:[]].freeze
35
+
36
+ # @!method for_cop_enabled?(node)
37
+ def_node_matcher :for_cop_enabled?, <<~PATTERN
38
+ (send
39
+ (send
40
+ ${(send nil? :config) (ivar :@config)} :for_cop
41
+ $(str _)) :[]
42
+ (str "Enabled"))
43
+ PATTERN
44
+
45
+ # @!method config_enabled_lookup?(node)
46
+ def_node_matcher :config_enabled_lookup?, <<~PATTERN
47
+ (send
48
+ {(lvar $_) (ivar $_) (send nil? $_)} :[]
49
+ (str "Enabled"))
50
+ PATTERN
51
+
52
+ def on_send(node)
53
+ if (config_var, cop_name = for_cop_enabled?(node))
54
+ handle_for_cop(node, config_var, cop_name)
55
+ elsif (config_var = config_enabled_lookup?(node))
56
+ return unless config_var.end_with?('_config')
57
+
58
+ handle_hash(node, config_var)
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def handle_for_cop(node, config_var, cop_name)
65
+ source = node.source
66
+ quote = cop_name.loc.begin.source
67
+ cop_name = cop_name.value
68
+
69
+ replacement = "#{config_var.source}.cop_enabled?(#{quote}#{cop_name}#{quote})"
70
+ message = format(MSG, source: source, replacement: replacement)
71
+
72
+ add_offense(node, message: message) do |corrector|
73
+ corrector.replace(node, replacement)
74
+ end
75
+ end
76
+
77
+ def handle_hash(node, config_var)
78
+ message = format(MSG_HASH, hash_name: config_var)
79
+
80
+ add_offense(node, message: message)
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -90,8 +90,10 @@ module RuboCop
90
90
  description_text = string_contents(current_description)
91
91
  return unless (new_description = correct_description(description_text, description_map))
92
92
 
93
+ quote = current_description.dstr_type? ? '"' : "'"
94
+
93
95
  add_offense(current_description, message: message) do |corrector|
94
- corrector.replace(current_description, "'#{new_description}'")
96
+ corrector.replace(current_description, "#{quote}#{new_description}#{quote}")
95
97
  end
96
98
  end
97
99
 
@@ -106,7 +108,7 @@ module RuboCop
106
108
  end
107
109
 
108
110
  def string_contents(node)
109
- node.str_type? ? node.value : node.source
111
+ node.type?(:str, :dstr) ? node.value : node.source
110
112
  end
111
113
  end
112
114
  end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InternalAffairs
6
+ # When a node location may not exist, `Node#loc?` or `Node#loc_is?`
7
+ # can be used instead of calling `Node#respond_to?` before using
8
+ # the value.
9
+ #
10
+ # @example
11
+ # # bad
12
+ # node.loc.respond_to?(:begin) && node.loc.begin
13
+ #
14
+ # # good
15
+ # node.loc?(:begin)
16
+ #
17
+ # # bad
18
+ # node.loc.respond_to?(:begin) && node.loc.begin.is?('(')
19
+ #
20
+ # # good
21
+ # node.loc_is?(:begin, '(')
22
+ #
23
+ # # bad
24
+ # node.loc.respond_to?(:begin) && node.loc.begin.source == '('
25
+ #
26
+ # # good
27
+ # node.loc_is?(:begin, '(')
28
+ #
29
+ class LocationExists < Base
30
+ extend AutoCorrector
31
+
32
+ MSG = 'Use `%<replacement>s` instead of `%<source>s`.'
33
+
34
+ # @!method replaceable_with_loc_is(node)
35
+ def_node_matcher :replaceable_with_loc_is, <<~PATTERN
36
+ (and
37
+ (call
38
+ (call $_receiver :loc) :respond_to?
39
+ $(sym _location))
40
+ {
41
+ (call
42
+ (call
43
+ (call _receiver :loc) _location) :is?
44
+ $(str _))
45
+ (call
46
+ (call
47
+ (call
48
+ (call _receiver :loc) _location) :source) :==
49
+ $(str _))
50
+ })
51
+ PATTERN
52
+
53
+ # @!method replaceable_with_loc(node)
54
+ def_node_matcher :replaceable_with_loc, <<~PATTERN
55
+ (and
56
+ (call
57
+ (call $_receiver :loc) :respond_to?
58
+ $(sym _location))
59
+ (call
60
+ (call _receiver :loc) _location))
61
+ PATTERN
62
+
63
+ def on_and(node)
64
+ replace_with_loc(node) || replace_with_loc_is(node)
65
+ end
66
+
67
+ private
68
+
69
+ def replace_with_loc(node)
70
+ replaceable_with_loc(node) do |receiver, location|
71
+ if node.parent&.assignment?
72
+ register_offense(node, replace_assignment(receiver, location))
73
+ else
74
+ register_offense(node, replacement(receiver, "loc?(#{location.source})"))
75
+ end
76
+ end
77
+ end
78
+
79
+ def replace_with_loc_is(node)
80
+ replaceable_with_loc_is(node) do |receiver, location, value|
81
+ replacement = replacement(receiver, "loc_is?(#{location.source}, #{value.source})")
82
+ register_offense(node, replacement)
83
+ end
84
+ end
85
+
86
+ def register_offense(node, replacement)
87
+ message = format(MSG, replacement: replacement, source: node.source)
88
+
89
+ add_offense(node, message: message) do |corrector|
90
+ corrector.replace(node, replacement)
91
+ end
92
+ end
93
+
94
+ def replacement(receiver, rest)
95
+ "#{replace_receiver(receiver)}#{rest}"
96
+ end
97
+
98
+ def replace_assignment(receiver, location)
99
+ prefix = replace_receiver(receiver)
100
+
101
+ "#{prefix}loc#{dot(receiver)}#{location.value} if #{prefix}loc?(#{location.source})"
102
+ end
103
+
104
+ def replace_receiver(receiver)
105
+ return '' unless receiver
106
+
107
+ "#{receiver.source}#{dot(receiver)}"
108
+ end
109
+
110
+ def dot(node)
111
+ node.parent.loc.dot.source
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -22,7 +22,7 @@ module RuboCop
22
22
 
23
23
  def on_send(node)
24
24
  return unless (parent = node.parent)
25
- return unless parent.send_type? && parent.method?(:expression)
25
+ return unless parent.call_type? && parent.method?(:expression)
26
26
  return unless parent.receiver.receiver
27
27
 
28
28
  offense = node.loc.selector.join(parent.source_range.end)
@@ -31,6 +31,7 @@ module RuboCop
31
31
  corrector.replace(offense, 'source_range')
32
32
  end
33
33
  end
34
+ alias on_csend on_send
34
35
  end
35
36
  end
36
37
  end
@@ -19,6 +19,7 @@ module RuboCop
19
19
  extend AutoCorrector
20
20
 
21
21
  MSG = 'Use `%<preferred>s`.'
22
+ RESTRICT_ON_SEND = [:==].freeze
22
23
 
23
24
  # @!method line_send(node)
24
25
  def_node_matcher :line_send, <<~PATTERN
@@ -36,10 +37,8 @@ module RuboCop
36
37
  def on_send(node)
37
38
  return unless location_line_equality_comparison?(node)
38
39
 
39
- lhs, _op, rhs = *node
40
-
41
- lhs_receiver = extract_receiver(lhs)
42
- rhs_receiver = extract_receiver(rhs)
40
+ lhs_receiver = extract_receiver(node.receiver)
41
+ rhs_receiver = extract_receiver(node.first_argument)
43
42
  preferred = "same_line?(#{lhs_receiver}, #{rhs_receiver})"
44
43
 
45
44
  add_offense(node, message: format(MSG, preferred: preferred)) do |corrector|
@@ -27,8 +27,8 @@ module RuboCop
27
27
  # @!method arguments_first_or_last?(node)
28
28
  def_node_matcher :arguments_first_or_last?, <<~PATTERN
29
29
  {
30
- (send (send !nil? :arguments) ${:first :last})
31
- (send (send !nil? :arguments) :[] (int ${0 -1}))
30
+ (call (call !nil? :arguments) ${:first :last})
31
+ (call (call !nil? :arguments) :[] (int ${0 -1}))
32
32
  }
33
33
  PATTERN
34
34
 
@@ -47,6 +47,7 @@ module RuboCop
47
47
  end
48
48
  end
49
49
  end
50
+ alias on_csend on_send
50
51
  end
51
52
  end
52
53
  end
@@ -62,7 +62,7 @@ module RuboCop
62
62
  private
63
63
 
64
64
  def valid_method_name?(node)
65
- node.first_argument.str_type? || node.first_argument.sym_type?
65
+ node.first_argument.type?(:str, :sym)
66
66
  end
67
67
 
68
68
  def method_directives(node)
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InternalAffairs
6
+ class NodePatternGroups
7
+ # AST Processor for NodePattern ASTs, for use with `InternalAffairs/NodePatternGroups`.
8
+ #
9
+ # Looks for sequences and subsequences where the first item is a `node_type` node,
10
+ # and converts them to `node_sequence` nodes (not a true `Rubocop::AST::NodePattern`
11
+ # node type).
12
+ #
13
+ # The resulting AST will be walked by `InternalAffairs::NodePatternGroups::ASTWalker`
14
+ # in order to find node types in a `union` node that can be rewritten as a node group.
15
+ #
16
+ # NOTE: The `on_*` methods in this class relate not to the normal node types but
17
+ # rather to the Node Pattern node types. Not every node type is handled.
18
+ #
19
+ class ASTProcessor
20
+ include ::AST::Processor::Mixin
21
+
22
+ def handler_missing(node)
23
+ node.updated(nil, process_children(node))
24
+ end
25
+
26
+ # Look for `sequence` and `subsequence` nodes that contain a `node_type` node as
27
+ # their first child. These are rewritten as `node_sequence` nodes so that it is
28
+ # possible to compare nodes while looking for replacement candidates for node groups.
29
+ # This is necessary so that extended patterns can be matched and replaced.
30
+ # ie. `{(send _ :foo ...) (csend _ :foo ...)}` can become `(call _ :foo ...)`
31
+ def on_sequence(node)
32
+ first_child = node.child
33
+
34
+ if first_child.type == :node_type
35
+ children = [first_child.child, *process_children(node, 1..)]
36
+
37
+ # The `node_sequence` node contains the `node_type` symbol as its first child,
38
+ # followed by all the other nodes contained in the `sequence` node.
39
+ # The location is copied from the sequence, so that the entire sequence can
40
+ # eventually be corrected in the cop.
41
+ n(:node_sequence, children, location: node.location)
42
+ else
43
+ node.updated(nil, process_children(node))
44
+ end
45
+ end
46
+ alias on_subsequence on_sequence
47
+
48
+ private
49
+
50
+ def n(type, children = [], properties = {})
51
+ NodePattern::Node.new(type, children, properties)
52
+ end
53
+
54
+ def process_children(node, range = 0..-1)
55
+ node.children[range].map do |child|
56
+ child.is_a?(::AST::Node) ? process(child) : child
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InternalAffairs
6
+ # rubocop:disable InternalAffairs/RedundantSourceRange -- node here is a `NodePattern::Node`
7
+ class NodePatternGroups
8
+ # Walks an AST that has been processed by `InternalAffairs::NodePatternGroups::Processor`
9
+ # in order to find `node_type` and `node_sequence` nodes that can be replaced with a node
10
+ # group in `InternalAffairs/NodePatternGroups`.
11
+ #
12
+ # Calling `ASTWalker#walk` sets `node_groups` with an array of `NodeGroup` structs
13
+ # that contain metadata about nodes that can be replaced, including location data. That
14
+ # metadata is used by the cop to register offenses and perform corrections.
15
+ class ASTWalker
16
+ # Struct to contain data about parts of a node pattern that can be replaced
17
+ NodeGroup = Struct.new(
18
+ :name, # The name of the node group that will be inserted
19
+ :union, # The entire `union` node
20
+ :node_types, # An array of `node_type` nodes that will be removed
21
+ :sequence?, # The pattern matches a node type with given attributes
22
+ :start_index, # The index in the union of the first node type to remove
23
+ :offense_range, # The range to mark an offense on
24
+ :ranges, # Range of each element to remove, since they may not be adjacent
25
+ :pipe, # Is the union delimited by pipes?
26
+ :other_elements?, # Does the union have other elements other than those to remove?
27
+ keyword_init: true
28
+ )
29
+
30
+ def initialize
31
+ reset!
32
+ end
33
+
34
+ def reset!
35
+ @node_groups = []
36
+ end
37
+
38
+ attr_reader :node_groups
39
+
40
+ # Recursively walk the AST in a depth-first manner.
41
+ # Only `union` nodes are handled further.
42
+ def walk(node)
43
+ return if node.nil?
44
+
45
+ on_union(node) if node.type == :union
46
+
47
+ node.child_nodes.each do |child|
48
+ walk(child)
49
+ end
50
+ end
51
+
52
+ # Search `union` nodes for `node_type` and `node_sequence` nodes that can be
53
+ # collapsed into a node group.
54
+ # * `node_type` nodes are nodes with no further configuration (ie. `send`)
55
+ # * `node_sequence` nodes are nodes with further configuration (ie. `(send ...)`)
56
+ #
57
+ # Each group of types that can be collapsed will have a `NodeGroup` record added
58
+ # to `node_groups`, which is then used by the cop.
59
+ def on_union(node)
60
+ all_node_types = each_child_node(node, :node_type, :node_sequence).to_a
61
+
62
+ each_node_group(all_node_types) do |group_name, node_types|
63
+ next unless sequences_match?(node_types)
64
+
65
+ node_groups << node_group_data(
66
+ group_name, node, node_types,
67
+ all_node_types.index(node_types.first),
68
+ (node.children - node_types).any?
69
+ )
70
+ end
71
+ end
72
+
73
+ private
74
+
75
+ def each_child_node(node, *types)
76
+ return to_enum(__method__, node, *types) unless block_given?
77
+
78
+ node.children.each do |child|
79
+ yield child if types.empty? || types.include?(child.type)
80
+ end
81
+
82
+ self
83
+ end
84
+
85
+ def each_node_group(types_to_check)
86
+ # Find all node groups where all of the members are present in the union
87
+ type_names = types_to_check.map(&:child)
88
+
89
+ NODE_GROUPS.select { |_, group| group & type_names == group }.each_key do |name|
90
+ nodes = get_relevant_nodes(types_to_check, name)
91
+
92
+ yield name, nodes
93
+ end
94
+ end
95
+
96
+ def get_relevant_nodes(node_types, group_name)
97
+ node_types.each_with_object([]) do |node_type, arr|
98
+ next unless NODE_GROUPS[group_name].include?(node_type.child)
99
+
100
+ arr << node_type
101
+ end
102
+ end
103
+
104
+ def node_group_data(name, union, node_types, start_index, other)
105
+ NodeGroup.new(
106
+ name: name,
107
+ union: union,
108
+ node_types: node_types,
109
+ sequence?: node_types.first.type == :node_sequence,
110
+ start_index: start_index,
111
+ pipe: union.source_range.source['|'],
112
+ other_elements?: other
113
+ )
114
+ end
115
+
116
+ def sequences_match?(types)
117
+ # Ensure all given types have the same type and the same sequence
118
+ # ie. `(send ...)` and `(csend ...) is a match
119
+ # `(send)` and `(csend ...)` is not a match
120
+ # `send` and `(csend ...)` is not a match
121
+
122
+ types.each_cons(2).all? do |left, right|
123
+ left.type == right.type && left.children[1..] == right.children[1..]
124
+ end
125
+ end
126
+ end
127
+ end
128
+ # rubocop:enable InternalAffairs/RedundantSourceRange
129
+ end
130
+ end
131
+ end