rubocop 1.91.0 → 1.92.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 (278) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +4 -1
  4. data/lib/rubocop/cached_data.rb +47 -7
  5. data/lib/rubocop/changed_files.rb +35 -13
  6. data/lib/rubocop/cli/command/cop_names.rb +57 -0
  7. data/lib/rubocop/cli/command/execute_runner.rb +2 -2
  8. data/lib/rubocop/cli/command/explain.rb +34 -0
  9. data/lib/rubocop/cli/command/show_cops.rb +6 -0
  10. data/lib/rubocop/cli/command/show_docs_url.rb +6 -4
  11. data/lib/rubocop/cli.rb +3 -2
  12. data/lib/rubocop/comment_config.rb +17 -8
  13. data/lib/rubocop/config.rb +0 -4
  14. data/lib/rubocop/config_loader.rb +6 -3
  15. data/lib/rubocop/config_loader_resolver.rb +37 -12
  16. data/lib/rubocop/config_obsoletion/removed_cop.rb +1 -1
  17. data/lib/rubocop/config_obsoletion.rb +9 -0
  18. data/lib/rubocop/config_validator.rb +11 -0
  19. data/lib/rubocop/cop/autocorrect_logic.rb +10 -5
  20. data/lib/rubocop/cop/badge.rb +1 -1
  21. data/lib/rubocop/cop/base.rb +42 -21
  22. data/lib/rubocop/cop/bundler/gem_comment.rb +0 -1
  23. data/lib/rubocop/cop/bundler/ordered_gems.rb +0 -8
  24. data/lib/rubocop/cop/cop_documentation.rb +81 -0
  25. data/lib/rubocop/cop/cop_explanation.rb +140 -0
  26. data/lib/rubocop/cop/correctors/alignment_corrector.rb +13 -1
  27. data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +37 -7
  28. data/lib/rubocop/cop/correctors/empty_line_corrector.rb +1 -1
  29. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +25 -4
  30. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +34 -0
  31. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +45 -8
  32. data/lib/rubocop/cop/documentation.rb +1 -1
  33. data/lib/rubocop/cop/gemspec/dependency_version.rb +0 -6
  34. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +0 -6
  35. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +2 -5
  36. data/lib/rubocop/cop/internal_affairs/node_type_group.rb +0 -7
  37. data/lib/rubocop/cop/internal_affairs/redundant_context_config_parameter.rb +1 -7
  38. data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +1 -7
  39. data/lib/rubocop/cop/layout/argument_alignment.rb +5 -1
  40. data/lib/rubocop/cop/layout/block_alignment.rb +27 -0
  41. data/lib/rubocop/cop/layout/case_indentation.rb +25 -9
  42. data/lib/rubocop/cop/layout/class_structure.rb +29 -7
  43. data/lib/rubocop/cop/layout/def_end_alignment.rb +4 -0
  44. data/lib/rubocop/cop/layout/dot_position.rb +25 -15
  45. data/lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb +21 -8
  46. data/lib/rubocop/cop/layout/empty_lines.rb +5 -2
  47. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +1 -1
  48. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -0
  49. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +9 -3
  50. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +23 -1
  51. data/lib/rubocop/cop/layout/end_alignment.rb +21 -25
  52. data/lib/rubocop/cop/layout/extra_spacing.rb +21 -6
  53. data/lib/rubocop/cop/layout/first_argument_indentation.rb +27 -1
  54. data/lib/rubocop/cop/layout/first_array_element_line_break.rb +15 -0
  55. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +16 -8
  56. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +6 -0
  57. data/lib/rubocop/cop/layout/first_method_parameter_line_break.rb +4 -0
  58. data/lib/rubocop/cop/layout/hash_alignment.rb +16 -12
  59. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +7 -3
  60. data/lib/rubocop/cop/layout/heredoc_indentation.rb +15 -0
  61. data/lib/rubocop/cop/layout/indentation_consistency.rb +22 -5
  62. data/lib/rubocop/cop/layout/indentation_style.rb +4 -1
  63. data/lib/rubocop/cop/layout/indentation_width.rb +2 -2
  64. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +8 -1
  65. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +25 -9
  66. data/lib/rubocop/cop/layout/line_length.rb +176 -26
  67. data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +12 -0
  68. data/lib/rubocop/cop/layout/multiline_block_layout.rb +8 -1
  69. data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +9 -0
  70. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +6 -0
  71. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +30 -5
  72. data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +4 -0
  73. data/lib/rubocop/cop/layout/redundant_line_break.rb +22 -1
  74. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +16 -3
  75. data/lib/rubocop/cop/layout/single_line_block_chain.rb +10 -2
  76. data/lib/rubocop/cop/layout/space_after_not.rb +5 -0
  77. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +6 -11
  78. data/lib/rubocop/cop/layout/space_around_keyword.rb +10 -0
  79. data/lib/rubocop/cop/layout/space_around_operators.rb +31 -4
  80. data/lib/rubocop/cop/layout/space_before_block_braces.rb +16 -1
  81. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +15 -2
  82. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +32 -9
  83. data/lib/rubocop/cop/layout/space_inside_range_literal.rb +29 -11
  84. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +2 -12
  85. data/lib/rubocop/cop/lint/ambiguous_operator.rb +1 -1
  86. data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +2 -1
  87. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +6 -1
  88. data/lib/rubocop/cop/lint/empty_conditional_body.rb +3 -1
  89. data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +4 -0
  90. data/lib/rubocop/cop/lint/interpolation_check.rb +20 -17
  91. data/lib/rubocop/cop/lint/literal_as_condition.rb +15 -0
  92. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +63 -19
  93. data/lib/rubocop/cop/lint/loop.rb +24 -2
  94. data/lib/rubocop/cop/lint/misplaced_magic_comment.rb +71 -11
  95. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +18 -3
  96. data/lib/rubocop/cop/lint/nested_percent_literal.rb +3 -0
  97. data/lib/rubocop/cop/lint/number_conversion.rb +3 -1
  98. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +22 -1
  99. data/lib/rubocop/cop/lint/percent_string_array.rb +75 -9
  100. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +39 -9
  101. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +13 -2
  102. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +11 -4
  103. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +47 -1
  104. data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -0
  105. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +18 -1
  106. data/lib/rubocop/cop/lint/useless_assignment.rb +14 -7
  107. data/lib/rubocop/cop/lint/useless_setter_call.rb +7 -4
  108. data/lib/rubocop/cop/lint/void.rb +8 -1
  109. data/lib/rubocop/cop/metrics/class_length.rb +4 -2
  110. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +0 -4
  111. data/lib/rubocop/cop/metrics/module_length.rb +4 -2
  112. data/lib/rubocop/cop/mixin/alignment.rb +4 -0
  113. data/lib/rubocop/cop/mixin/check_line_breakable.rb +23 -19
  114. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +8 -2
  115. data/lib/rubocop/cop/mixin/code_length.rb +10 -2
  116. data/lib/rubocop/cop/mixin/comments_help.rb +1 -1
  117. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +12 -1
  118. data/lib/rubocop/cop/mixin/endless_method_body_help.rb +105 -0
  119. data/lib/rubocop/cop/mixin/forbidden_identifiers.rb +2 -2
  120. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +41 -6
  121. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +26 -2
  122. data/lib/rubocop/cop/mixin/min_branches_count.rb +3 -1
  123. data/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb +11 -1
  124. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +9 -0
  125. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  126. data/lib/rubocop/cop/mixin/pattern_matching_help.rb +35 -0
  127. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +17 -2
  128. data/lib/rubocop/cop/mixin/project_index_help.rb +15 -0
  129. data/lib/rubocop/cop/mixin/reparsed_equivalence.rb +13 -3
  130. data/lib/rubocop/cop/mixin/statement_modifier.rb +39 -5
  131. data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
  132. data/lib/rubocop/cop/mixin/visibility_help.rb +2 -1
  133. data/lib/rubocop/cop/mixin.rb +2 -0
  134. data/lib/rubocop/cop/naming/accessor_method_name.rb +0 -16
  135. data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
  136. data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +9 -2
  137. data/lib/rubocop/cop/naming/heredoc_delimiter_naming.rb +5 -3
  138. data/lib/rubocop/cop/naming/predicate_prefix.rb +7 -22
  139. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -1
  140. data/lib/rubocop/cop/offense.rb +59 -3
  141. data/lib/rubocop/cop/registry.rb +1 -1
  142. data/lib/rubocop/cop/security/json_load.rb +16 -4
  143. data/lib/rubocop/cop/style/access_modifier_declarations.rb +37 -2
  144. data/lib/rubocop/cop/style/alias.rb +18 -5
  145. data/lib/rubocop/cop/style/ambiguous_endless_method_definition.rb +4 -3
  146. data/lib/rubocop/cop/style/and_or.rb +37 -10
  147. data/lib/rubocop/cop/style/arguments_forwarding.rb +32 -3
  148. data/lib/rubocop/cop/style/attr.rb +3 -4
  149. data/lib/rubocop/cop/style/bitwise_predicate.rb +6 -4
  150. data/lib/rubocop/cop/style/block_delimiters.rb +35 -3
  151. data/lib/rubocop/cop/style/case_equality.rb +14 -3
  152. data/lib/rubocop/cop/style/case_like_if.rb +6 -3
  153. data/lib/rubocop/cop/style/class_and_module_children.rb +57 -16
  154. data/lib/rubocop/cop/style/class_methods_definitions.rb +15 -0
  155. data/lib/rubocop/cop/style/collection_querying.rb +79 -46
  156. data/lib/rubocop/cop/style/combinable_loops.rb +89 -11
  157. data/lib/rubocop/cop/style/concat_array_literals.rb +58 -7
  158. data/lib/rubocop/cop/style/conditional_assignment.rb +4 -0
  159. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +65 -9
  160. data/lib/rubocop/cop/style/double_negation.rb +11 -0
  161. data/lib/rubocop/cop/style/empty_literal.rb +1 -1
  162. data/lib/rubocop/cop/style/empty_method.rb +3 -1
  163. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +53 -11
  164. data/lib/rubocop/cop/style/endless_method.rb +104 -11
  165. data/lib/rubocop/cop/style/exact_regexp_match.rb +45 -8
  166. data/lib/rubocop/cop/style/explicit_block_argument.rb +50 -10
  167. data/lib/rubocop/cop/style/float_division.rb +37 -2
  168. data/lib/rubocop/cop/style/for.rb +53 -2
  169. data/lib/rubocop/cop/style/format_string.rb +28 -6
  170. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +2 -1
  171. data/lib/rubocop/cop/style/guard_clause.rb +31 -22
  172. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +4 -0
  173. data/lib/rubocop/cop/style/hash_conversion.rb +28 -8
  174. data/lib/rubocop/cop/style/hash_lookup_method.rb +42 -9
  175. data/lib/rubocop/cop/style/hash_syntax.rb +16 -3
  176. data/lib/rubocop/cop/style/identical_conditional_branches.rb +22 -3
  177. data/lib/rubocop/cop/style/if_inside_else.rb +21 -1
  178. data/lib/rubocop/cop/style/if_unless_modifier.rb +32 -26
  179. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +4 -0
  180. data/lib/rubocop/cop/style/if_with_semicolon.rb +47 -4
  181. data/lib/rubocop/cop/style/inverse_methods.rb +24 -2
  182. data/lib/rubocop/cop/style/it_block_parameter.rb +94 -4
  183. data/lib/rubocop/cop/style/keyword_parameters_order.rb +3 -1
  184. data/lib/rubocop/cop/style/lambda.rb +34 -1
  185. data/lib/rubocop/cop/style/lambda_call.rb +7 -2
  186. data/lib/rubocop/cop/style/map_into_array.rb +1 -2
  187. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +80 -13
  188. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +26 -2
  189. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +26 -3
  190. data/lib/rubocop/cop/style/method_def_parentheses.rb +38 -4
  191. data/lib/rubocop/cop/style/missing_else.rb +7 -1
  192. data/lib/rubocop/cop/style/mixin_grouping.rb +12 -3
  193. data/lib/rubocop/cop/style/multiline_if_modifier.rb +45 -5
  194. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +7 -0
  195. data/lib/rubocop/cop/style/multiline_method_signature.rb +2 -5
  196. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +7 -3
  197. data/lib/rubocop/cop/style/multiline_when_then.rb +7 -0
  198. data/lib/rubocop/cop/style/mutable_constant.rb +52 -7
  199. data/lib/rubocop/cop/style/negated_if_else_condition.rb +2 -1
  200. data/lib/rubocop/cop/style/negative_array_index.rb +9 -1
  201. data/lib/rubocop/cop/style/nested_modifier.rb +27 -8
  202. data/lib/rubocop/cop/style/nested_ternary_operator.rb +26 -8
  203. data/lib/rubocop/cop/style/next.rb +24 -10
  204. data/lib/rubocop/cop/style/nil_comparison.rb +16 -4
  205. data/lib/rubocop/cop/style/non_nil_check.rb +1 -1
  206. data/lib/rubocop/cop/style/not.rb +10 -5
  207. data/lib/rubocop/cop/style/numeric_predicate.rb +28 -5
  208. data/lib/rubocop/cop/style/one_line_conditional.rb +13 -2
  209. data/lib/rubocop/cop/style/parentheses_around_condition.rb +19 -1
  210. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +14 -3
  211. data/lib/rubocop/cop/style/quoted_symbols.rb +5 -7
  212. data/lib/rubocop/cop/style/raise_args.rb +80 -11
  213. data/lib/rubocop/cop/style/reduce_to_hash.rb +47 -33
  214. data/lib/rubocop/cop/style/redundant_begin.rb +45 -5
  215. data/lib/rubocop/cop/style/redundant_condition.rb +30 -13
  216. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +6 -0
  217. data/lib/rubocop/cop/style/redundant_exception.rb +27 -11
  218. data/lib/rubocop/cop/style/redundant_fetch_block.rb +9 -1
  219. data/lib/rubocop/cop/style/redundant_freeze.rb +11 -3
  220. data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +24 -1
  221. data/lib/rubocop/cop/style/redundant_parentheses.rb +12 -1
  222. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +7 -0
  223. data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +20 -0
  224. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +4 -0
  225. data/lib/rubocop/cop/style/redundant_self.rb +9 -1
  226. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +28 -0
  227. data/lib/rubocop/cop/style/redundant_sort.rb +38 -4
  228. data/lib/rubocop/cop/style/regexp_literal.rb +75 -26
  229. data/lib/rubocop/cop/style/require_order.rb +25 -8
  230. data/lib/rubocop/cop/style/rescue_modifier.rb +11 -9
  231. data/lib/rubocop/cop/style/return_nil.rb +17 -2
  232. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +11 -1
  233. data/lib/rubocop/cop/style/sample.rb +9 -8
  234. data/lib/rubocop/cop/style/self_assignment.rb +12 -15
  235. data/lib/rubocop/cop/style/semicolon.rb +16 -5
  236. data/lib/rubocop/cop/style/send_with_literal_method_name.rb +4 -0
  237. data/lib/rubocop/cop/style/single_line_methods.rb +42 -4
  238. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +29 -5
  239. data/lib/rubocop/cop/style/string_concatenation.rb +21 -4
  240. data/lib/rubocop/cop/style/struct_inheritance.rb +6 -0
  241. data/lib/rubocop/cop/style/super_arguments.rb +3 -0
  242. data/lib/rubocop/cop/style/symbol_proc.rb +24 -6
  243. data/lib/rubocop/cop/style/ternary_parentheses.rb +23 -1
  244. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +23 -19
  245. data/lib/rubocop/cop/style/while_until_modifier.rb +1 -1
  246. data/lib/rubocop/cop/style/yoda_condition.rb +24 -9
  247. data/lib/rubocop/cop/style/yoda_expression.rb +10 -1
  248. data/lib/rubocop/cop/team.rb +10 -5
  249. data/lib/rubocop/cop/variable_force/assignment.rb +1 -1
  250. data/lib/rubocop/cop/variable_force/variable.rb +14 -1
  251. data/lib/rubocop/formatter/base_formatter.rb +22 -0
  252. data/lib/rubocop/formatter/formatter_set.rb +4 -0
  253. data/lib/rubocop/formatter/json_formatter.rb +71 -1
  254. data/lib/rubocop/formatter/offense_count_formatter.rb +3 -1
  255. data/lib/rubocop/formatter/pacman_formatter.rb +3 -1
  256. data/lib/rubocop/formatter/sarif_formatter.rb +12 -7
  257. data/lib/rubocop/formatter/worst_offenders_formatter.rb +3 -1
  258. data/lib/rubocop/lsp/routes.rb +23 -23
  259. data/lib/rubocop/lsp/runtime.rb +47 -11
  260. data/lib/rubocop/lsp/stdin_runner.rb +11 -5
  261. data/lib/rubocop/lsp.rb +5 -2
  262. data/lib/rubocop/mcp/autocorrection_request.rb +52 -0
  263. data/lib/rubocop/mcp/explain_tool.rb +68 -0
  264. data/lib/rubocop/mcp/scope.rb +65 -0
  265. data/lib/rubocop/mcp/server.rb +184 -76
  266. data/lib/rubocop/mcp/source_files.rb +35 -0
  267. data/lib/rubocop/offense_limit.rb +30 -0
  268. data/lib/rubocop/options.rb +45 -10
  269. data/lib/rubocop/plugin/configuration_integrator.rb +36 -8
  270. data/lib/rubocop/remote_config.rb +4 -1
  271. data/lib/rubocop/result_cache.rb +20 -13
  272. data/lib/rubocop/runner.rb +110 -42
  273. data/lib/rubocop/server/cache.rb +6 -6
  274. data/lib/rubocop/target_ruby.rb +21 -9
  275. data/lib/rubocop/util.rb +69 -0
  276. data/lib/rubocop/version.rb +1 -1
  277. data/lib/rubocop.rb +5 -0
  278. metadata +16 -5
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Common functionality for checking whether a node is used in a pattern of
6
+ # pattern matching, where only some expressions are allowed.
7
+ module PatternMatchingHelp
8
+ PATTERN_TYPES = %i[
9
+ array_pattern array_pattern_with_tail begin const_pattern find_pattern hash_pattern
10
+ match_alt match_as pair
11
+ ].freeze
12
+
13
+ private
14
+
15
+ def in_pattern?(node)
16
+ child = node
17
+
18
+ node.each_ancestor do |ancestor|
19
+ case ancestor.type
20
+ when :in_pattern
21
+ return ancestor.pattern.equal?(child)
22
+ when :match_pattern, :match_pattern_p
23
+ return ancestor.children[1].equal?(child)
24
+ when *PATTERN_TYPES
25
+ child = ancestor
26
+ else
27
+ return false
28
+ end
29
+ end
30
+
31
+ false
32
+ end
33
+ end
34
+ end
35
+ end
@@ -174,8 +174,13 @@ module RuboCop
174
174
  # ^
175
175
  tokens = remove_equals_in_def(tokens, processed_source)
176
176
 
177
- # Only attempt to align the first = on each line
178
- Set.new(tokens.uniq(&:line))
177
+ # Only attempt to align the first = on each line. An `=` that comes after a comparison
178
+ # on its line is left out as well: whether a line is aligned is judged by its first
179
+ # assignment or comparison operator, so such an `=` could never be found aligned
180
+ # and `Layout/SpaceAroundOperators` would keep undoing the padding added for it.
181
+ Set.new(tokens.uniq(&:line).select do |token|
182
+ first_operator_on_line(token.line).equal?(token)
183
+ end)
179
184
  end
180
185
  end
181
186
 
@@ -270,6 +275,16 @@ module RuboCop
270
275
 
271
276
  asgn_tokens.reject { |t| eqls_to_ignore.include?(t.begin_pos) }
272
277
  end
278
+
279
+ def first_operator_on_line(line)
280
+ @first_operator_by_line ||= processed_source.tokens.each_with_object({}) do |token, by_line|
281
+ next unless ASSIGNMENT_OR_COMPARISON_TOKENS.include?(token.type)
282
+
283
+ by_line[token.line] ||= token
284
+ end
285
+
286
+ @first_operator_by_line[line]
287
+ end
273
288
  end
274
289
  end
275
290
  end
@@ -119,6 +119,21 @@ module RuboCop
119
119
  end
120
120
  end
121
121
 
122
+ # Whether the `def`/`defs` node overrides a method defined by an ancestor
123
+ # of its enclosing class or module elsewhere in the project.
124
+ def overrides_inherited_method?(def_node)
125
+ return false unless project_index
126
+ return false unless (namespace_node = def_node.each_ancestor(:class, :module).first)
127
+
128
+ declaration = resolve_constant_in_index(namespace_node.identifier)
129
+ return false unless declaration.is_a?(Rubydex::Namespace)
130
+
131
+ scope = def_node.defs_type? ? indexed_singleton_of(declaration) : declaration
132
+ !scope.nil? && inherited_index_member?(scope, "#{def_node.method_name}()")
133
+ rescue StandardError
134
+ false
135
+ end
136
+
122
137
  # Whether every link of the declaration's ancestry is resolved in the
123
138
  # index: no definition of any ancestor has an unresolved superclass or
124
139
  # mixin reference. Unresolved links silently vanish from `ancestors`,
@@ -51,15 +51,25 @@ module RuboCop
51
51
  # regardless of size (for cops where verification is the offense logic).
52
52
  def verified_by_reparse(items, oversized: :report)
53
53
  scope_groups(items).flat_map do |scope, group|
54
- if (oversized == :report && verification_too_large?(scope)) ||
55
- (group.size > 1 && corrections_verify?(scope, group))
54
+ if oversized == :report && verification_too_large?(scope)
56
55
  group
57
56
  else
58
- group.select { |item| corrections_verify?(scope, [item]) }
57
+ verified_subset(scope, group)
59
58
  end
60
59
  end
61
60
  end
62
61
 
62
+ # Verifies a group with a single reparse and bisects it when that fails,
63
+ # so a few significant items among many cost a logarithmic number of
64
+ # reparses rather than one each.
65
+ def verified_subset(scope, group)
66
+ return group if corrections_verify?(scope, group)
67
+ return [] if group.one?
68
+
69
+ half = group.size / 2
70
+ verified_subset(scope, group.take(half)) + verified_subset(scope, group.drop(half))
71
+ end
72
+
63
73
  # Whether the exact correction for `item` produces source that still
64
74
  # parses, without requiring an equivalent AST. For corrections that
65
75
  # intentionally change the tree.
@@ -7,6 +7,8 @@ module RuboCop
7
7
  include LineLengthHelp
8
8
  include AllowedPattern
9
9
 
10
+ SPACE_INSIDE_PARENS_STYLES = %w[space compact].freeze
11
+
10
12
  private
11
13
 
12
14
  def single_line_as_modifier?(node)
@@ -42,7 +44,10 @@ module RuboCop
42
44
  def modifier_fits_on_single_line?(node)
43
45
  return true unless max_line_length
44
46
 
45
- line_length(line_in_modifier_form(node)) <= max_line_length
47
+ length = line_length(line_in_modifier_form(node))
48
+ length += 2 if parenthesize?(node) && space_inside_parens?
49
+
50
+ length <= max_line_length
46
51
  end
47
52
 
48
53
  def line_in_modifier_form(node)
@@ -52,6 +57,14 @@ module RuboCop
52
57
  "#{code_before}#{to_modifier_form(node)}#{code_after(node)}"
53
58
  end
54
59
 
60
+ def space_inside_parens?
61
+ return false unless config.cop_enabled?('Layout/SpaceInsideParens')
62
+
63
+ style = config.for_cop('Layout/SpaceInsideParens')['EnforcedStyle']
64
+
65
+ SPACE_INSIDE_PARENS_STYLES.include?(style)
66
+ end
67
+
55
68
  # Whether an existing line is acceptable to `Layout/LineLength`,
56
69
  # exemptions (the cop disabled at that line, an allowed pattern, an
57
70
  # allowed cop directive, an allowed URI) included. Only for judging
@@ -96,15 +109,22 @@ module RuboCop
96
109
  [parenthesized, first_line_comment(node)].compact.join(' ')
97
110
  end
98
111
 
112
+ # rubocop:disable-next Metrics/AbcSize
99
113
  def if_body_source(if_body)
100
- if if_body.call_type? && !if_body.method?(:[]=) && omitted_value_in_last_hash_arg?(if_body)
114
+ if unparenthesized_call_with_omitted_value?(if_body)
101
115
  "#{method_source(if_body)}(#{if_body.arguments.map(&:source).join(', ')})"
116
+ elsif (value = wrapped_value(if_body))
117
+ prefix = if_body.source_range.begin.join(value.source_range.begin).source
118
+ suffix = value.source_range.end.join(if_body.source_range.end).source
119
+
120
+ "#{prefix}#{if_body_source(value)}#{suffix}"
102
121
  else
103
122
  if_body.source
104
123
  end
105
124
  end
106
125
 
107
- def omitted_value_in_last_hash_arg?(if_body)
126
+ def unparenthesized_call_with_omitted_value?(if_body)
127
+ return false if !if_body.call_type? || if_body.method?(:[]=) || if_body.parenthesized?
108
128
  return false unless (last_argument = if_body.last_argument)
109
129
 
110
130
  last_argument.hash_type? && last_argument.pairs.last&.value_omission?
@@ -116,6 +136,14 @@ module RuboCop
116
136
  if_body.source_range.begin.join(end_range).source
117
137
  end
118
138
 
139
+ def wrapped_value(if_body)
140
+ if if_body.type?(:return, :next, :break)
141
+ if_body.first_argument if if_body.arguments.one?
142
+ elsif if_body.type?(:lvasgn, :ivasgn, :cvasgn, :gvasgn, :casgn)
143
+ if_body.expression
144
+ end
145
+ end
146
+
119
147
  def first_line_comment(node)
120
148
  comment = processed_source.comment_at_line(node.first_line)
121
149
  return unless comment
@@ -137,9 +165,15 @@ module RuboCop
137
165
  parent = node.parent
138
166
  return false if parent.nil?
139
167
  return true if parent.assignment? || parent.operator_keyword?
140
- return true if %i[array pair].include?(parent.type)
168
+ return true if parent.type?(:array, :pair, :call, :yield, :super)
169
+
170
+ ternary_branch_or_endless_method_body?(parent)
171
+ end
172
+
173
+ def ternary_branch_or_endless_method_body?(parent)
174
+ return parent.ternary? if parent.if_type?
141
175
 
142
- node.parent.send_type?
176
+ parent.any_def_type? && parent.endless?
143
177
  end
144
178
 
145
179
  def comment_disables_cop?(comment)
@@ -163,7 +163,7 @@ module RuboCop
163
163
 
164
164
  def put_comma(items, kind)
165
165
  last_item = items.last
166
- return if last_item.block_pass_type?
166
+ return if last_item.type?(:block_pass, :forwarded_args)
167
167
 
168
168
  range = autocorrect_range(last_item)
169
169
  msg = format(MSG, command: 'Put a', unit: format(kind, article: 'a multiline'))
@@ -63,9 +63,10 @@ module RuboCop
63
63
  (send nil? VISIBILITY_SCOPES def)
64
64
  PATTERN
65
65
 
66
+ # A modifier applies to every symbol it is given, not only to a sole argument.
66
67
  # @!method visibility_inline_on_method_name?(node, method_name:)
67
68
  def_node_matcher :visibility_inline_on_method_name?, <<~PATTERN
68
- (send nil? VISIBILITY_SCOPES (sym %method_name))
69
+ (send nil? VISIBILITY_SCOPES <(sym %method_name) ...>)
69
70
  PATTERN
70
71
  end
71
72
  end
@@ -32,6 +32,7 @@ module RuboCop
32
32
  autoload :AnnotationComment, "#{__dir__}/mixin/annotation_comment"
33
33
  autoload :EmptyParameter, "#{__dir__}/mixin/empty_parameter"
34
34
  autoload :EndKeywordAlignment, "#{__dir__}/mixin/end_keyword_alignment"
35
+ autoload :EndlessMethodBodyHelp, "#{__dir__}/mixin/endless_method_body_help"
35
36
  autoload :EndlessMethodRewriter, "#{__dir__}/mixin/endless_method_rewriter"
36
37
  autoload :EnforceSuperclass, "#{__dir__}/mixin/enforce_superclass"
37
38
  autoload :FirstElementLineBreak, "#{__dir__}/mixin/first_element_line_break"
@@ -64,6 +65,7 @@ module RuboCop
64
65
  autoload :OnNormalIfUnless, "#{__dir__}/mixin/on_normal_if_unless"
65
66
  autoload :OrderedGemNode, "#{__dir__}/mixin/ordered_gem_node"
66
67
  autoload :Parentheses, "#{__dir__}/mixin/parentheses"
68
+ autoload :PatternMatchingHelp, "#{__dir__}/mixin/pattern_matching_help"
67
69
  autoload :PercentArray, "#{__dir__}/mixin/percent_array"
68
70
  autoload :PercentLiteral, "#{__dir__}/mixin/percent_literal"
69
71
  autoload :PrecedingFollowingAlignment, "#{__dir__}/mixin/preceding_following_alignment"
@@ -80,22 +80,6 @@ module RuboCop
80
80
  node.arguments.one? &&
81
81
  node.first_argument.arg_type?
82
82
  end
83
-
84
- # When `AllCops/UseProjectIndex` is enabled, methods that override a
85
- # method defined by an ancestor elsewhere in the project are not
86
- # reported: renaming an override breaks the inherited contract.
87
- def overrides_inherited_method?(node)
88
- return false unless project_index
89
- return false unless (namespace_node = node.each_ancestor(:class, :module).first)
90
-
91
- declaration = resolve_constant_in_index(namespace_node.identifier)
92
- return false unless declaration.is_a?(Rubydex::Namespace)
93
-
94
- scope = node.defs_type? ? indexed_singleton_of(declaration) : declaration
95
- !scope.nil? && inherited_index_member?(scope, "#{node.method_name}()")
96
- rescue StandardError
97
- false
98
- end
99
83
  end
100
84
  end
101
85
  end
@@ -63,7 +63,7 @@ module RuboCop
63
63
 
64
64
  def self.autocorrect_incompatible_with
65
65
  [
66
- Lint::AmbiguousOperator, Style::ArgumentsForwarding,
66
+ Lint::AmbiguousOperator, Style::ArgumentsForwarding, Style::EndlessMethod,
67
67
  Style::ExplicitBlockArgument, Style::MethodDefParentheses
68
68
  ]
69
69
  end
@@ -35,7 +35,7 @@ module RuboCop
35
35
  MSG = 'Use %<style>s heredoc delimiters.'
36
36
 
37
37
  def on_heredoc(node)
38
- return if correct_case_delimiters?(node)
38
+ return if correct_case_delimiters?(node) || corrected_delimiter_in_body?(node)
39
39
 
40
40
  add_offense(node.loc.heredoc_end) do |corrector|
41
41
  expr = node.source_range
@@ -52,7 +52,14 @@ module RuboCop
52
52
  end
53
53
 
54
54
  def correct_case_delimiters?(node)
55
- delimiter_string(node) == correct_delimiters(delimiter_string(node))
55
+ delimiters = delimiter_string(node)
56
+ delimiters == correct_delimiters(delimiters)
57
+ end
58
+
59
+ def corrected_delimiter_in_body?(node)
60
+ corrected = correct_delimiters(delimiter_string(node))
61
+
62
+ node.loc.heredoc_body.source.each_line.any? { |line| line.strip == corrected }
56
63
  end
57
64
 
58
65
  def correct_delimiters(source)
@@ -43,9 +43,11 @@ module RuboCop
43
43
 
44
44
  return false unless /\w/.match?(delimiters)
45
45
 
46
- forbidden_delimiters.none? do |forbidden_delimiter|
47
- Regexp.new(forbidden_delimiter).match?(delimiters)
48
- end
46
+ forbidden_delimiter_regexps.none? { |regexp| regexp.match?(delimiters) }
47
+ end
48
+
49
+ def forbidden_delimiter_regexps
50
+ @forbidden_delimiter_regexps ||= forbidden_delimiters.map { |d| Regexp.new(d) }
49
51
  end
50
52
 
51
53
  def forbidden_delimiters
@@ -142,12 +142,6 @@ module RuboCop
142
142
  end
143
143
  end
144
144
 
145
- def offending_prefixes_for(node, method_name)
146
- predicate_prefixes.reject do |prefix|
147
- allowed_method_name?(method_name, prefix) ||
148
- (use_sorbet_sigs? && !sorbet_sig?(node, return_type: 'T::Boolean'))
149
- end
150
- end
151
145
  alias on_defs on_def
152
146
 
153
147
  def validate_config
@@ -162,6 +156,13 @@ module RuboCop
162
156
 
163
157
  private
164
158
 
159
+ def offending_prefixes_for(node, method_name)
160
+ prefixes = predicate_prefixes.reject { |p| allowed_method_name?(method_name, p) }
161
+ return prefixes if prefixes.empty? || !use_sorbet_sigs?
162
+
163
+ sorbet_sig?(node, return_type: 'T::Boolean') ? prefixes : []
164
+ end
165
+
165
166
  # @!method sorbet_return_type(node)
166
167
  def_node_matcher :sorbet_return_type, <<~PATTERN
167
168
  (block (send nil? :sig) args (send _ :returns $_type))
@@ -210,22 +211,6 @@ module RuboCop
210
211
  def method_definition_macro?(macro_name)
211
212
  cop_config['MethodDefinitionMacros'].include?(macro_name.to_s)
212
213
  end
213
-
214
- # When `AllCops/UseProjectIndex` is enabled, methods that override a
215
- # method defined by an ancestor elsewhere in the project are not
216
- # reported: renaming an override breaks the inherited contract.
217
- def overrides_inherited_method?(node)
218
- return false unless project_index
219
- return false unless (namespace_node = node.each_ancestor(:class, :module).first)
220
-
221
- declaration = resolve_constant_in_index(namespace_node.identifier)
222
- return false unless declaration.is_a?(Rubydex::Namespace)
223
-
224
- scope = node.defs_type? ? indexed_singleton_of(declaration) : declaration
225
- !scope.nil? && inherited_index_member?(scope, "#{node.method_name}()")
226
- rescue StandardError
227
- false
228
- end
229
214
  end
230
215
  end
231
216
  end
@@ -128,7 +128,7 @@ module RuboCop
128
128
  if child_node.lvar_type?
129
129
  parent_node = child_node.parent
130
130
  if parent_node.respond_to?(:value_omission?) && parent_node.value_omission?
131
- corrector.insert_after(parent_node.loc.operator, " #{preferred_name}")
131
+ corrector.replace(parent_node.loc.operator, ": #{preferred_name}")
132
132
  else
133
133
  corrector.replace(child_node, preferred_name)
134
134
  end
@@ -86,6 +86,9 @@ module RuboCop
86
86
 
87
87
  NO_LOCATION = PseudoSourceRange.new(1, 0, '', 0, 0).freeze
88
88
 
89
+ EMPTY_CORRECTIONS = [].freeze
90
+ private_constant :EMPTY_CORRECTIONS
91
+
89
92
  # @api public
90
93
  #
91
94
  # @!attribute [r] justification
@@ -96,9 +99,43 @@ module RuboCop
96
99
  # or the directive carries no reason
97
100
  attr_reader :justification
98
101
 
102
+ # @api public
103
+ #
104
+ # @!attribute [r] corrections
105
+ #
106
+ # @return [Array<Correction>]
107
+ # the individual edits that autocorrecting this offense would make, in
108
+ # source order. Empty when the offense has no correction. Unlike
109
+ # {#corrector}, this survives serialization, so it is available on a
110
+ # cache hit and in parallel runs.
111
+ attr_reader :corrections
112
+
113
+ # @api public
114
+ #
115
+ # @!attribute [r] correction_safe
116
+ #
117
+ # @return [Boolean]
118
+ # whether autocorrection would apply {#corrections} in a normal `-a`
119
+ # run. A cop marked `Safe: false` or `SafeAutoCorrect: false` still
120
+ # builds its correction, but only `-A` applies it, so a consumer that
121
+ # applies edits itself has to make the same distinction.
122
+ attr_reader :correction_safe
123
+
124
+ # A single edit of an autocorrection: the source range it replaces and
125
+ # the text it replaces it with. An insertion has an empty range, a
126
+ # removal an empty replacement.
127
+ #
128
+ # @api public
129
+ Correction = Struct.new(:begin_pos, :end_pos, :replacement) do
130
+ def length
131
+ end_pos - begin_pos
132
+ end
133
+ end
134
+
99
135
  # @api private
100
136
  def initialize(severity, location, message, cop_name, # rubocop:disable Metrics/ParameterLists
101
- status = :uncorrected, corrector = nil, justification: nil)
137
+ status = :uncorrected, corrector = nil, justification: nil,
138
+ corrections: nil, correction_safe: true)
102
139
  @severity = RuboCop::Cop::Severity.new(severity)
103
140
  @location = location
104
141
 
@@ -112,15 +149,21 @@ module RuboCop
112
149
  @status = status
113
150
  @corrector = corrector
114
151
  @justification = justification.freeze
152
+ @corrections = (corrections || corrections_from(corrector)).freeze
153
+ @correction_safe = correction_safe
115
154
  freeze
116
155
  end
117
156
 
118
157
  def marshal_dump
119
- [@severity, @location, @message, @cop_name, @status, @justification]
158
+ [@severity, @location, @message, @cop_name, @status, @justification, @corrections,
159
+ @correction_safe]
120
160
  end
121
161
 
122
162
  def marshal_load(array)
123
- @severity, @location, @message, @cop_name, @status, @justification = array
163
+ @severity, @location, @message, @cop_name, @status, @justification, @corrections,
164
+ @correction_safe = array
165
+ @corrections ||= EMPTY_CORRECTIONS
166
+ @correction_safe = true if @correction_safe.nil?
124
167
  @line = @location.line
125
168
  @column = @location.column
126
169
  end
@@ -276,6 +319,19 @@ module RuboCop
276
319
  end
277
320
  0
278
321
  end
322
+
323
+ private
324
+
325
+ # Extracted eagerly rather than on demand because the offense is frozen,
326
+ # and because a corrector cannot be serialized - the cache and parallel
327
+ # workers would otherwise hand back offenses that have lost their edits.
328
+ def corrections_from(corrector)
329
+ return EMPTY_CORRECTIONS unless corrector
330
+
331
+ corrector.as_replacements.map do |range, replacement|
332
+ Correction.new(range.begin_pos, range.end_pos, replacement.freeze).freeze
333
+ end
334
+ end
279
335
  end
280
336
  end
281
337
  end
@@ -43,7 +43,7 @@ module RuboCop
43
43
 
44
44
  def self.qualified_cop?(name)
45
45
  badge = Badge.parse(name)
46
- global.qualify_badge(badge).first == badge
46
+ global.qualify_badge(badge).include?(badge)
47
47
  end
48
48
 
49
49
  attr_reader :options, :warnings
@@ -22,6 +22,13 @@ module RuboCop
22
22
  # passing the `create_additions` keyword argument emits a deprecation warning, with the
23
23
  # goal of being secure by default in the next major version 3.0.0.
24
24
  #
25
+ # NOTE: Before `json` gem version 2.17.0, `JSON.load` only takes options as its third
26
+ # argument. A hash passed as the second argument is taken as the `proc` argument, so
27
+ # its `create_additions` option is ignored. Which `json` version runs can't be told
28
+ # statically (the lockfile may resolve a newer one than the default gem loaded in
29
+ # production), so options passed as the second argument are always reported. Pass
30
+ # `nil` as the second argument instead.
31
+ #
25
32
  # @safety
26
33
  # This cop's autocorrection is unsafe because it's potentially dangerous.
27
34
  # If using a stream, like `JSON.load(open('file'))`, you will need to call
@@ -33,13 +40,16 @@ module RuboCop
33
40
  # JSON.load('{}')
34
41
  # JSON.restore('{}')
35
42
  #
43
+ # # bad - ignored by `json` older than 2.17.0
44
+ # JSON.load('{}', create_additions: false)
45
+ #
36
46
  # # good
37
47
  # JSON.parse('{}')
38
48
  # JSON.unsafe_load('{}')
39
49
  #
40
50
  # # good - explicit use of `create_additions` option
41
- # JSON.load('{}', create_additions: true)
42
- # JSON.load('{}', create_additions: false)
51
+ # JSON.load('{}', nil, create_additions: true)
52
+ # JSON.load('{}', nil, create_additions: false)
43
53
  #
44
54
  class JSONLoad < Base
45
55
  extend AutoCorrector
@@ -51,8 +61,10 @@ module RuboCop
51
61
  def_node_matcher :insecure_json_load, <<~PATTERN
52
62
  (
53
63
  send (const {nil? cbase} :JSON) ${:load :restore}
54
- ...
55
- !`(pair (sym :create_additions) _)
64
+ {
65
+ _ hash
66
+ | ... !`(pair (sym :create_additions) _)
67
+ }
56
68
  )
57
69
  PATTERN
58
70
 
@@ -132,10 +132,11 @@ module RuboCop
132
132
  # private alias_method :qux, :foo
133
133
  #
134
134
  # end
135
- class AccessModifierDeclarations < Base
135
+ class AccessModifierDeclarations < Base # rubocop:disable Metrics/ClassLength
136
136
  extend AutoCorrector
137
137
 
138
138
  include ConfigurableEnforcedStyle
139
+ include EndlessMethodBodyHelp
139
140
  include RangeHelp
140
141
 
141
142
  GROUP_STYLE_MESSAGE = [
@@ -169,6 +170,10 @@ module RuboCop
169
170
  (send nil? :alias_method _ _))
170
171
  PATTERN
171
172
 
173
+ def self.autocorrect_incompatible_with
174
+ [Style::EndlessMethod, Style::SingleLineMethods]
175
+ end
176
+
172
177
  def on_send(node)
173
178
  return if allowed?(node)
174
179
 
@@ -187,11 +192,18 @@ module RuboCop
187
192
  def allowed?(node)
188
193
  !node.access_modifier? ||
189
194
  node.parent&.type?(:pair, :any_block) ||
195
+ dynamic_method_list?(node) ||
190
196
  allow_modifiers_on_symbols?(node) ||
191
197
  allow_modifiers_on_attrs?(node) ||
192
198
  allow_modifiers_on_alias_method?(node)
193
199
  end
194
200
 
201
+ # `private(*names)` with a list computed at runtime names no definition
202
+ # the modifier could be grouped with.
203
+ def dynamic_method_list?(node)
204
+ node.arguments.any?(&:splat_type?) && !access_modifier_with_symbol?(node)
205
+ end
206
+
195
207
  def autocorrect(corrector, node)
196
208
  case style
197
209
  when :group
@@ -241,10 +253,33 @@ module RuboCop
241
253
 
242
254
  access_modifier_is_inlined?(node) && !right_siblings_same_inline_method?(node)
243
255
  else
244
- access_modifier_is_not_inlined?(node) && select_grouped_def_nodes(node).any?
256
+ access_modifier_is_not_inlined?(node) && inlinable?(select_grouped_def_nodes(node))
245
257
  end
246
258
  end
247
259
 
260
+ def inlinable?(def_nodes)
261
+ return false if def_nodes.empty?
262
+
263
+ def_nodes.none? { |def_node| def_node.endless? && body_unsafe_to_inline?(def_node.body) }
264
+ end
265
+
266
+ def body_unsafe_to_inline?(body)
267
+ return true if body.each_node(:any_block).any? { |block| do_end_block?(block) }
268
+
269
+ target_ruby_version < 4.0 && command_body?(body)
270
+ end
271
+
272
+ def do_end_block?(block)
273
+ block.keywords? && !block.send_node.lambda_literal?
274
+ end
275
+
276
+ def command_body?(body)
277
+ body = body.body while body.rescue_type?
278
+ return true if body.send_type? && body.prefix_not? && !body.parenthesized?
279
+
280
+ command_call?(body)
281
+ end
282
+
248
283
  def correctable_group_offense?(node)
249
284
  return false unless group_style?
250
285
  return false if allowed?(node)