rubocop 1.91.0 → 1.92.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 (263) 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_loader.rb +6 -3
  14. data/lib/rubocop/config_loader_resolver.rb +37 -12
  15. data/lib/rubocop/config_obsoletion/removed_cop.rb +1 -1
  16. data/lib/rubocop/config_validator.rb +11 -0
  17. data/lib/rubocop/cop/autocorrect_logic.rb +10 -5
  18. data/lib/rubocop/cop/badge.rb +1 -1
  19. data/lib/rubocop/cop/base.rb +42 -21
  20. data/lib/rubocop/cop/bundler/gem_comment.rb +0 -1
  21. data/lib/rubocop/cop/bundler/ordered_gems.rb +0 -8
  22. data/lib/rubocop/cop/cop_documentation.rb +81 -0
  23. data/lib/rubocop/cop/cop_explanation.rb +140 -0
  24. data/lib/rubocop/cop/correctors/alignment_corrector.rb +13 -1
  25. data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +37 -7
  26. data/lib/rubocop/cop/correctors/empty_line_corrector.rb +1 -1
  27. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +25 -4
  28. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +34 -0
  29. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +45 -8
  30. data/lib/rubocop/cop/documentation.rb +1 -1
  31. data/lib/rubocop/cop/gemspec/dependency_version.rb +0 -6
  32. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +0 -6
  33. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +2 -5
  34. data/lib/rubocop/cop/internal_affairs/node_type_group.rb +0 -7
  35. data/lib/rubocop/cop/internal_affairs/redundant_context_config_parameter.rb +1 -7
  36. data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +1 -7
  37. data/lib/rubocop/cop/layout/argument_alignment.rb +5 -1
  38. data/lib/rubocop/cop/layout/block_alignment.rb +27 -0
  39. data/lib/rubocop/cop/layout/case_indentation.rb +25 -9
  40. data/lib/rubocop/cop/layout/class_structure.rb +29 -7
  41. data/lib/rubocop/cop/layout/def_end_alignment.rb +4 -0
  42. data/lib/rubocop/cop/layout/dot_position.rb +25 -15
  43. data/lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb +21 -8
  44. data/lib/rubocop/cop/layout/empty_lines.rb +5 -2
  45. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -0
  46. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +9 -3
  47. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +23 -1
  48. data/lib/rubocop/cop/layout/end_alignment.rb +19 -17
  49. data/lib/rubocop/cop/layout/extra_spacing.rb +4 -0
  50. data/lib/rubocop/cop/layout/first_argument_indentation.rb +27 -1
  51. data/lib/rubocop/cop/layout/first_array_element_line_break.rb +15 -0
  52. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +16 -8
  53. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +6 -0
  54. data/lib/rubocop/cop/layout/first_method_parameter_line_break.rb +4 -0
  55. data/lib/rubocop/cop/layout/hash_alignment.rb +16 -12
  56. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +7 -3
  57. data/lib/rubocop/cop/layout/heredoc_indentation.rb +15 -0
  58. data/lib/rubocop/cop/layout/indentation_consistency.rb +22 -5
  59. data/lib/rubocop/cop/layout/indentation_style.rb +4 -1
  60. data/lib/rubocop/cop/layout/indentation_width.rb +2 -2
  61. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +8 -1
  62. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +25 -9
  63. data/lib/rubocop/cop/layout/line_length.rb +153 -21
  64. data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +12 -0
  65. data/lib/rubocop/cop/layout/multiline_block_layout.rb +8 -1
  66. data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +9 -0
  67. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +6 -0
  68. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +30 -5
  69. data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +4 -0
  70. data/lib/rubocop/cop/layout/redundant_line_break.rb +22 -1
  71. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +15 -2
  72. data/lib/rubocop/cop/layout/single_line_block_chain.rb +10 -2
  73. data/lib/rubocop/cop/layout/space_after_not.rb +5 -0
  74. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +6 -11
  75. data/lib/rubocop/cop/layout/space_around_keyword.rb +10 -0
  76. data/lib/rubocop/cop/layout/space_before_block_braces.rb +4 -1
  77. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +15 -2
  78. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +31 -8
  79. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +2 -12
  80. data/lib/rubocop/cop/lint/ambiguous_operator.rb +1 -1
  81. data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +2 -1
  82. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +6 -1
  83. data/lib/rubocop/cop/lint/empty_conditional_body.rb +3 -1
  84. data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +4 -0
  85. data/lib/rubocop/cop/lint/literal_as_condition.rb +15 -0
  86. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +63 -19
  87. data/lib/rubocop/cop/lint/loop.rb +24 -2
  88. data/lib/rubocop/cop/lint/misplaced_magic_comment.rb +71 -11
  89. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +18 -3
  90. data/lib/rubocop/cop/lint/number_conversion.rb +3 -1
  91. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +12 -1
  92. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +13 -2
  93. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +47 -1
  94. data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -0
  95. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +18 -1
  96. data/lib/rubocop/cop/lint/useless_assignment.rb +14 -7
  97. data/lib/rubocop/cop/lint/useless_setter_call.rb +7 -4
  98. data/lib/rubocop/cop/lint/void.rb +8 -1
  99. data/lib/rubocop/cop/metrics/class_length.rb +4 -2
  100. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +0 -4
  101. data/lib/rubocop/cop/metrics/module_length.rb +4 -2
  102. data/lib/rubocop/cop/mixin/alignment.rb +4 -0
  103. data/lib/rubocop/cop/mixin/check_line_breakable.rb +23 -19
  104. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +8 -2
  105. data/lib/rubocop/cop/mixin/code_length.rb +10 -2
  106. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +12 -1
  107. data/lib/rubocop/cop/mixin/endless_method_body_help.rb +105 -0
  108. data/lib/rubocop/cop/mixin/forbidden_identifiers.rb +2 -2
  109. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +24 -5
  110. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +26 -2
  111. data/lib/rubocop/cop/mixin/min_branches_count.rb +3 -1
  112. data/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb +11 -1
  113. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +9 -0
  114. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  115. data/lib/rubocop/cop/mixin/pattern_matching_help.rb +35 -0
  116. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +17 -2
  117. data/lib/rubocop/cop/mixin/project_index_help.rb +15 -0
  118. data/lib/rubocop/cop/mixin/reparsed_equivalence.rb +13 -3
  119. data/lib/rubocop/cop/mixin/statement_modifier.rb +25 -4
  120. data/lib/rubocop/cop/mixin/visibility_help.rb +2 -1
  121. data/lib/rubocop/cop/mixin.rb +2 -0
  122. data/lib/rubocop/cop/naming/accessor_method_name.rb +0 -16
  123. data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
  124. data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +9 -2
  125. data/lib/rubocop/cop/naming/heredoc_delimiter_naming.rb +5 -3
  126. data/lib/rubocop/cop/naming/predicate_prefix.rb +7 -22
  127. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -1
  128. data/lib/rubocop/cop/offense.rb +59 -3
  129. data/lib/rubocop/cop/registry.rb +1 -1
  130. data/lib/rubocop/cop/security/json_load.rb +16 -4
  131. data/lib/rubocop/cop/style/access_modifier_declarations.rb +37 -2
  132. data/lib/rubocop/cop/style/alias.rb +18 -5
  133. data/lib/rubocop/cop/style/ambiguous_endless_method_definition.rb +4 -3
  134. data/lib/rubocop/cop/style/and_or.rb +6 -2
  135. data/lib/rubocop/cop/style/arguments_forwarding.rb +32 -3
  136. data/lib/rubocop/cop/style/attr.rb +3 -4
  137. data/lib/rubocop/cop/style/bitwise_predicate.rb +6 -4
  138. data/lib/rubocop/cop/style/block_delimiters.rb +35 -3
  139. data/lib/rubocop/cop/style/case_like_if.rb +6 -3
  140. data/lib/rubocop/cop/style/class_and_module_children.rb +56 -16
  141. data/lib/rubocop/cop/style/class_methods_definitions.rb +15 -0
  142. data/lib/rubocop/cop/style/collection_querying.rb +79 -46
  143. data/lib/rubocop/cop/style/combinable_loops.rb +89 -11
  144. data/lib/rubocop/cop/style/concat_array_literals.rb +58 -7
  145. data/lib/rubocop/cop/style/conditional_assignment.rb +4 -0
  146. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +65 -9
  147. data/lib/rubocop/cop/style/double_negation.rb +11 -0
  148. data/lib/rubocop/cop/style/empty_literal.rb +1 -1
  149. data/lib/rubocop/cop/style/empty_method.rb +3 -1
  150. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +53 -11
  151. data/lib/rubocop/cop/style/endless_method.rb +71 -6
  152. data/lib/rubocop/cop/style/exact_regexp_match.rb +45 -8
  153. data/lib/rubocop/cop/style/explicit_block_argument.rb +50 -10
  154. data/lib/rubocop/cop/style/float_division.rb +22 -2
  155. data/lib/rubocop/cop/style/for.rb +53 -2
  156. data/lib/rubocop/cop/style/format_string.rb +28 -6
  157. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +2 -1
  158. data/lib/rubocop/cop/style/guard_clause.rb +31 -22
  159. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +4 -0
  160. data/lib/rubocop/cop/style/hash_conversion.rb +28 -8
  161. data/lib/rubocop/cop/style/hash_lookup_method.rb +32 -9
  162. data/lib/rubocop/cop/style/hash_syntax.rb +16 -3
  163. data/lib/rubocop/cop/style/identical_conditional_branches.rb +22 -3
  164. data/lib/rubocop/cop/style/if_inside_else.rb +21 -1
  165. data/lib/rubocop/cop/style/if_unless_modifier.rb +32 -26
  166. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +4 -0
  167. data/lib/rubocop/cop/style/if_with_semicolon.rb +47 -4
  168. data/lib/rubocop/cop/style/inverse_methods.rb +24 -2
  169. data/lib/rubocop/cop/style/it_block_parameter.rb +94 -4
  170. data/lib/rubocop/cop/style/keyword_parameters_order.rb +3 -1
  171. data/lib/rubocop/cop/style/lambda.rb +34 -1
  172. data/lib/rubocop/cop/style/lambda_call.rb +7 -2
  173. data/lib/rubocop/cop/style/map_into_array.rb +1 -2
  174. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +80 -13
  175. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +25 -2
  176. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +26 -3
  177. data/lib/rubocop/cop/style/method_def_parentheses.rb +29 -3
  178. data/lib/rubocop/cop/style/missing_else.rb +7 -1
  179. data/lib/rubocop/cop/style/mixin_grouping.rb +12 -3
  180. data/lib/rubocop/cop/style/multiline_if_modifier.rb +45 -5
  181. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +7 -0
  182. data/lib/rubocop/cop/style/multiline_method_signature.rb +2 -5
  183. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +7 -3
  184. data/lib/rubocop/cop/style/multiline_when_then.rb +7 -0
  185. data/lib/rubocop/cop/style/mutable_constant.rb +52 -7
  186. data/lib/rubocop/cop/style/negated_if_else_condition.rb +2 -1
  187. data/lib/rubocop/cop/style/negative_array_index.rb +9 -1
  188. data/lib/rubocop/cop/style/nested_modifier.rb +27 -8
  189. data/lib/rubocop/cop/style/nested_ternary_operator.rb +26 -8
  190. data/lib/rubocop/cop/style/next.rb +24 -10
  191. data/lib/rubocop/cop/style/nil_comparison.rb +16 -4
  192. data/lib/rubocop/cop/style/non_nil_check.rb +1 -1
  193. data/lib/rubocop/cop/style/not.rb +10 -5
  194. data/lib/rubocop/cop/style/numeric_predicate.rb +21 -5
  195. data/lib/rubocop/cop/style/one_line_conditional.rb +13 -2
  196. data/lib/rubocop/cop/style/parentheses_around_condition.rb +1 -1
  197. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +14 -3
  198. data/lib/rubocop/cop/style/raise_args.rb +19 -9
  199. data/lib/rubocop/cop/style/reduce_to_hash.rb +17 -1
  200. data/lib/rubocop/cop/style/redundant_begin.rb +22 -4
  201. data/lib/rubocop/cop/style/redundant_condition.rb +30 -13
  202. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +6 -0
  203. data/lib/rubocop/cop/style/redundant_exception.rb +15 -3
  204. data/lib/rubocop/cop/style/redundant_fetch_block.rb +9 -1
  205. data/lib/rubocop/cop/style/redundant_freeze.rb +11 -3
  206. data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +24 -1
  207. data/lib/rubocop/cop/style/redundant_parentheses.rb +12 -1
  208. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +7 -0
  209. data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +20 -0
  210. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +4 -0
  211. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +28 -0
  212. data/lib/rubocop/cop/style/redundant_sort.rb +38 -4
  213. data/lib/rubocop/cop/style/regexp_literal.rb +75 -26
  214. data/lib/rubocop/cop/style/require_order.rb +25 -8
  215. data/lib/rubocop/cop/style/rescue_modifier.rb +11 -9
  216. data/lib/rubocop/cop/style/return_nil.rb +17 -2
  217. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +11 -1
  218. data/lib/rubocop/cop/style/sample.rb +9 -8
  219. data/lib/rubocop/cop/style/self_assignment.rb +12 -15
  220. data/lib/rubocop/cop/style/semicolon.rb +16 -5
  221. data/lib/rubocop/cop/style/send_with_literal_method_name.rb +4 -0
  222. data/lib/rubocop/cop/style/single_line_methods.rb +21 -4
  223. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +29 -5
  224. data/lib/rubocop/cop/style/string_concatenation.rb +9 -3
  225. data/lib/rubocop/cop/style/struct_inheritance.rb +6 -0
  226. data/lib/rubocop/cop/style/super_arguments.rb +3 -0
  227. data/lib/rubocop/cop/style/symbol_proc.rb +24 -6
  228. data/lib/rubocop/cop/style/ternary_parentheses.rb +23 -1
  229. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +23 -19
  230. data/lib/rubocop/cop/style/while_until_modifier.rb +1 -1
  231. data/lib/rubocop/cop/style/yoda_condition.rb +24 -9
  232. data/lib/rubocop/cop/style/yoda_expression.rb +10 -1
  233. data/lib/rubocop/cop/team.rb +10 -5
  234. data/lib/rubocop/cop/variable_force/assignment.rb +1 -1
  235. data/lib/rubocop/cop/variable_force/variable.rb +14 -1
  236. data/lib/rubocop/formatter/base_formatter.rb +22 -0
  237. data/lib/rubocop/formatter/formatter_set.rb +4 -0
  238. data/lib/rubocop/formatter/json_formatter.rb +71 -1
  239. data/lib/rubocop/formatter/offense_count_formatter.rb +3 -1
  240. data/lib/rubocop/formatter/pacman_formatter.rb +3 -1
  241. data/lib/rubocop/formatter/sarif_formatter.rb +12 -7
  242. data/lib/rubocop/formatter/worst_offenders_formatter.rb +3 -1
  243. data/lib/rubocop/lsp/routes.rb +23 -23
  244. data/lib/rubocop/lsp/runtime.rb +47 -11
  245. data/lib/rubocop/lsp/stdin_runner.rb +11 -5
  246. data/lib/rubocop/lsp.rb +5 -2
  247. data/lib/rubocop/mcp/autocorrection_request.rb +52 -0
  248. data/lib/rubocop/mcp/explain_tool.rb +68 -0
  249. data/lib/rubocop/mcp/scope.rb +65 -0
  250. data/lib/rubocop/mcp/server.rb +184 -76
  251. data/lib/rubocop/mcp/source_files.rb +35 -0
  252. data/lib/rubocop/offense_limit.rb +30 -0
  253. data/lib/rubocop/options.rb +45 -10
  254. data/lib/rubocop/plugin/configuration_integrator.rb +12 -5
  255. data/lib/rubocop/remote_config.rb +4 -1
  256. data/lib/rubocop/result_cache.rb +20 -13
  257. data/lib/rubocop/runner.rb +109 -41
  258. data/lib/rubocop/server/cache.rb +6 -6
  259. data/lib/rubocop/target_ruby.rb +21 -9
  260. data/lib/rubocop/util.rb +69 -0
  261. data/lib/rubocop/version.rb +1 -1
  262. data/lib/rubocop.rb +5 -0
  263. metadata +16 -5
@@ -56,6 +56,13 @@ module RuboCop
56
56
  ARG_MSG = 'Block argument expression is not on the same line as the block start.'
57
57
  PIPE_SIZE = '|'.length
58
58
 
59
+ # `Style/Lambda` moves the block arguments of `lambda` into `->(...)`, and
60
+ # `Style/ItBlockParameter` and `Style/HashEachMethods` remove some or all of them,
61
+ # in the same pass, while this cop moves them next to the block start.
62
+ def self.autocorrect_incompatible_with
63
+ [Style::HashEachMethods, Style::ItBlockParameter, Style::Lambda]
64
+ end
65
+
59
66
  def on_block(node)
60
67
  return if node.single_line?
61
68
 
@@ -155,7 +162,7 @@ module RuboCop
155
162
  end
156
163
 
157
164
  def include_trailing_comma?(args)
158
- arg_count = args.each_descendant(:arg).to_a.size
165
+ arg_count = args.each_descendant.count(&:argument_type?)
159
166
  arg_count == 1 && args.source.include?(',')
160
167
  end
161
168
  end
@@ -106,6 +106,15 @@ module RuboCop
106
106
  ALWAYS_SAME_LINE_MESSAGE = 'Closing hash brace must be on the same ' \
107
107
  'line as the last hash element.'
108
108
 
109
+ # Moving the closing brace after a method call value while
110
+ # `Layout/MultilineMethodCallBraceLayout` moves that call's closing parenthesis
111
+ # before a comment in the same pass places the brace after the comment.
112
+ # Moving it while `Style/HashAsLastArrayItem` or `Style/RedundantDoubleSplatHashBraces`
113
+ # removes the braces in the same pass leaves the brace behind.
114
+ def self.autocorrect_incompatible_with
115
+ [Layout::MultilineMethodCallBraceLayout, Style::HashAsLastArrayItem, Style::RedundantDoubleSplatHashBraces]
116
+ end
117
+
109
118
  def on_hash(node)
110
119
  check_brace_layout(node)
111
120
  end
@@ -106,6 +106,12 @@ module RuboCop
106
106
  ALWAYS_SAME_LINE_MESSAGE = 'Closing method call brace must be on ' \
107
107
  'the same line as the last argument.'
108
108
 
109
+ # Moving the closing parenthesis and removing it under `omit_parentheses` in the same pass
110
+ # leaves the moved parenthesis behind.
111
+ def self.autocorrect_incompatible_with
112
+ [Style::MethodCallWithArgsParentheses]
113
+ end
114
+
109
115
  def on_send(node)
110
116
  check_brace_layout(node)
111
117
  end
@@ -73,14 +73,28 @@ module RuboCop
73
73
  end
74
74
 
75
75
  def find_pair_ancestor(node)
76
+ child = node
76
77
  node.each_ancestor do |ancestor|
77
78
  return ancestor if ancestor.pair_type?
78
79
  break if grouped_expression?(ancestor) || inside_arg_list_parentheses?(node, ancestor)
80
+ break if inside_body?(ancestor, child)
81
+
82
+ child = ancestor
79
83
  end
80
84
 
81
85
  nil
82
86
  end
83
87
 
88
+ # A block, `begin`, `if` or loop body is a separate set of statements, so
89
+ # a method chain in it is not the value of a hash pair the body is nested in.
90
+ # The send node of a block is still part of the value.
91
+ def inside_body?(ancestor, child)
92
+ return !child.equal?(ancestor.send_node) if ancestor.any_block_type?
93
+ return !ancestor.ternary? if ancestor.if_type?
94
+
95
+ ancestor.type?(:kwbegin, :while, :until, :for)
96
+ end
97
+
84
98
  def unwrap_block_node(node)
85
99
  node&.any_block_type? ? node.send_node : node
86
100
  end
@@ -88,7 +102,7 @@ module RuboCop
88
102
  def autocorrect(corrector, node)
89
103
  if @send_node.block_node
90
104
  correct_selector_only(corrector, node)
91
- correct_block(corrector, @send_node.block_node)
105
+ correct_block(corrector, @send_node.block_node, node.first_line)
92
106
  else
93
107
  AlignmentCorrector.correct(corrector, processed_source, node, @column_delta)
94
108
  end
@@ -100,10 +114,21 @@ module RuboCop
100
114
  AlignmentCorrector.correct(corrector, processed_source, selector_range, @column_delta)
101
115
  end
102
116
 
103
- def correct_block(corrector, block_node)
104
- AlignmentCorrector.correct(corrector, processed_source, block_node.body, @column_delta)
105
- end_range = range_by_whole_lines(block_node.loc.end, include_final_newline: false)
106
- AlignmentCorrector.correct(corrector, processed_source, end_range, @column_delta)
117
+ def correct_block(corrector, block_node, selector_line)
118
+ lines_to_shift = block_lines(block_node) - [selector_line]
119
+
120
+ lines_to_shift.each do |line|
121
+ line_range = processed_source.buffer.line_range(line)
122
+
123
+ AlignmentCorrector.correct(corrector, processed_source, line_range, @column_delta)
124
+ end
125
+ end
126
+
127
+ def block_lines(block_node)
128
+ body = block_node.body
129
+ lines = body ? (body.first_line..body.last_line).to_a : []
130
+
131
+ (lines << block_node.loc.end.line).uniq
107
132
  end
108
133
 
109
134
  def relevant_node?(send_node)
@@ -118,6 +118,10 @@ module RuboCop
118
118
  ALWAYS_SAME_LINE_MESSAGE = 'Closing method definition brace must be ' \
119
119
  'on the same line as the last parameter.'
120
120
 
121
+ def self.autocorrect_incompatible_with
122
+ [Style::MultilineMethodSignature]
123
+ end
124
+
121
125
  def on_def(node)
122
126
  check_brace_layout(node.arguments)
123
127
  end
@@ -54,6 +54,11 @@ module RuboCop
54
54
 
55
55
  MSG = 'Redundant line break detected.'
56
56
 
57
+ def on_new_investigation
58
+ super
59
+ @offense_line_ranges = []
60
+ end
61
+
57
62
  def on_lvasgn(node)
58
63
  super unless end_with_percent_blank_string?(processed_source)
59
64
  end
@@ -77,12 +82,15 @@ module RuboCop
77
82
  end
78
83
 
79
84
  def check_assignment(node, _rhs)
80
- return unless offense?(node)
85
+ return unless offense?(node) && !part_of_ignored_node?(node)
81
86
 
82
87
  register_offense(node)
83
88
  end
84
89
 
85
90
  def register_offense(node)
91
+ line_range = joined_line_range(node)
92
+ return if too_long_as_single_line?(line_range)
93
+
86
94
  # The exact single-line correction is verified to parse equivalently
87
95
  # before the offense is registered, so a join that would change how
88
96
  # the code parses is never reported or offered.
@@ -92,6 +100,19 @@ module RuboCop
92
100
  corrector.replace(node, to_single_line(node.source).strip)
93
101
  end
94
102
  ignore_node(node)
103
+ @offense_line_ranges.reject! { |range| line_range.cover?(range) }
104
+ @offense_line_ranges << line_range
105
+ end
106
+
107
+ # Offenses registered in the same pass that share a line with `node`
108
+ # get joined onto that line too, so the combined line has to fit.
109
+ def joined_line_range(node)
110
+ sharing_ranges = @offense_line_ranges.select do |range|
111
+ range.begin <= node.last_line && node.first_line <= range.end
112
+ end
113
+ lines = [node.first_line, node.last_line, *sharing_ranges.flat_map(&:minmax)]
114
+
115
+ lines.min..lines.max
95
116
  end
96
117
 
97
118
  def apply_reparse_correction(corrector, node)
@@ -22,6 +22,7 @@ module RuboCop
22
22
  # puts 'error'
23
23
  # end
24
24
  class RescueEnsureAlignment < Base
25
+ include Alignment
25
26
  include RangeHelp
26
27
  include EndKeywordAlignment
27
28
  extend AutoCorrector
@@ -74,9 +75,10 @@ module RuboCop
74
75
  # Some inline node is sitting before current node.
75
76
  return nil unless whitespace.source.strip.empty?
76
77
 
77
- new_column = alignment_location.column
78
+ indentation = alignment_indentation(alignment_location)
79
+ return nil if indentation.nil?
78
80
 
79
- corrector.replace(whitespace, ' ' * new_column)
81
+ corrector.replace(whitespace, indentation)
80
82
  end
81
83
 
82
84
  def format_message(alignment_node, alignment_loc, kw_loc)
@@ -91,6 +93,17 @@ module RuboCop
91
93
  )
92
94
  end
93
95
 
96
+ # A run of spaces sized to the target column would be turned back into tabs by
97
+ # `Layout/IndentationStyle`, generally landing on a different column, so under
98
+ # tabs the target line's own indentation is reused. There is nothing to reuse
99
+ # when the target does not start its line.
100
+ def alignment_indentation(location)
101
+ return ' ' * location.column unless using_tabs?
102
+ return unless begins_its_line?(location)
103
+
104
+ location.source_line[/\A\s*/]
105
+ end
106
+
94
107
  # rubocop:disable-next Metrics/AbcSize, Metrics/MethodLength
95
108
  def alignment_source(node, starting_loc)
96
109
  ending_loc =
@@ -25,13 +25,17 @@ module RuboCop
25
25
 
26
26
  MSG = 'Put method call on a separate line if chained to a single line block.'
27
27
 
28
+ # These cops rewrite a method chained to the block in the same pass,
29
+ # which must not start a line of its own.
28
30
  def self.autocorrect_incompatible_with
29
- [Style::MapToHash]
31
+ [Style::FloatDivision, Style::MapToHash, Style::NilComparison, Style::RedundantSort]
30
32
  end
31
33
 
32
34
  def on_send(node)
33
35
  range = offending_range(node)
34
- add_offense(range) { |corrector| corrector.insert_before(range, "\n") } if range
36
+ return if !range || heredoc_opened_before?(node.receiver, range)
37
+
38
+ add_offense(range) { |corrector| corrector.insert_before(range, "\n") }
35
39
  end
36
40
  alias on_csend on_send
37
41
 
@@ -63,6 +67,10 @@ module RuboCop
63
67
  # l.(1) has no selector, so we use the opening parenthesis instead
64
68
  node.loc.selector || node.loc.begin
65
69
  end
70
+
71
+ def heredoc_opened_before?(receiver, range)
72
+ receiver.each_node(:any_str).any? { |str| str.heredoc? && str.first_line == range.line }
73
+ end
66
74
  end
67
75
  end
68
76
  end
@@ -20,6 +20,7 @@ module RuboCop
20
20
 
21
21
  def on_send(node)
22
22
  return unless node.prefix_bang? && whitespace_after_operator?(node)
23
+ return if unary_tilde?(node.receiver)
23
24
 
24
25
  add_offense(node) do |corrector|
25
26
  corrector.remove(
@@ -33,6 +34,10 @@ module RuboCop
33
34
  def whitespace_after_operator?(node)
34
35
  node.receiver.source_range.begin_pos - node.source_range.begin_pos > 1
35
36
  end
37
+
38
+ def unary_tilde?(receiver)
39
+ receiver.send_type? && receiver.unary_operation? && receiver.method?(:~)
40
+ end
36
41
  end
37
42
  end
38
43
  end
@@ -6,7 +6,8 @@ module RuboCop
6
6
  # Checks the spacing inside and after block parameters pipes. Line breaks
7
7
  # inside parameter pipes are checked by `Layout/MultilineBlockLayout` and
8
8
  # not by this cop. Spaces inside a lambda's parameter parentheses are left
9
- # to `Layout/SpaceInsideParens` when that cop enforces a conflicting style.
9
+ # to `Layout/SpaceInsideParens` when that cop is enabled, so the examples
10
+ # below apply to a lambda only when it is not.
10
11
  #
11
12
  # @example EnforcedStyleInsidePipes: no_space (default)
12
13
  # # bad
@@ -35,7 +36,7 @@ module RuboCop
35
36
 
36
37
  return unless node.arguments? && pipes?(arguments)
37
38
 
38
- check_inside_pipes(arguments) unless conflicting_space_inside_parens_style?(arguments)
39
+ check_inside_pipes(arguments) unless checked_by_space_inside_parens?(arguments)
39
40
  check_after_closing_pipe(arguments) if node.body
40
41
  check_each_arg(arguments)
41
42
  end
@@ -54,14 +55,8 @@ module RuboCop
54
55
  'EnforcedStyleInsidePipes'
55
56
  end
56
57
 
57
- def conflicting_space_inside_parens_style?(arguments)
58
- return false unless arguments.loc.begin.source == '('
59
-
60
- case config.for_enabled_cop('Layout/SpaceInsideParens')['EnforcedStyle']
61
- when 'no_space' then style == :space
62
- when 'space', 'compact' then style == :no_space
63
- else false
64
- end
58
+ def checked_by_space_inside_parens?(arguments)
59
+ arguments.loc.begin.source == '(' && config.cop_enabled?('Layout/SpaceInsideParens')
65
60
  end
66
61
 
67
62
  def check_inside_pipes(arguments)
@@ -153,7 +148,7 @@ module RuboCop
153
148
  if node
154
149
  corrector.insert_before(node, ' ')
155
150
  else
156
- corrector.insert_after(target, ' ')
151
+ corrector.insert_after(range_between(space_begin_pos, space_begin_pos), ' ')
157
152
  end
158
153
  end
159
154
  end
@@ -209,6 +209,7 @@ module RuboCop
209
209
  end
210
210
 
211
211
  return unless space_after_missing?(range)
212
+ return if block_brace_with_no_space_style?(node, range)
212
213
 
213
214
  add_offense(range, message: format(MSG_AFTER, range: range.source)) do |corrector|
214
215
  corrector.insert_after(range, ' ')
@@ -233,6 +234,15 @@ module RuboCop
233
234
  !/[\s;,#\\)}\].]/.match?(char)
234
235
  end
235
236
 
237
+ def block_brace_with_no_space_style?(node, range)
238
+ return false unless (parent = node.parent)
239
+ return false unless parent.any_block_type? && parent.loc.begin&.begin_pos == range.end_pos
240
+
241
+ space_before_block_braces_config = config.for_cop('Layout/SpaceBeforeBlockBraces')
242
+ space_before_block_braces_config.fetch('Enabled', true) &&
243
+ space_before_block_braces_config['EnforcedStyle'] == 'no_space'
244
+ end
245
+
236
246
  def accepted_opening_delimiter?(range, char)
237
247
  return true unless char
238
248
 
@@ -49,8 +49,11 @@ module RuboCop
49
49
  MISSING_MSG = 'Space missing to the left of {.'
50
50
  DETECTED_MSG = 'Space detected to the left of {.'
51
51
 
52
+ # `Style/BlockDelimiters` decides whether to put a space before the `do`
53
+ # it writes by looking at the source before this cop removed the space,
54
+ # so the two corrections cannot be applied in the same pass.
52
55
  def self.autocorrect_incompatible_with
53
- [Style::SymbolProc]
56
+ [Style::BlockDelimiters, Style::SymbolProc]
54
57
  end
55
58
 
56
59
  def on_block(node)
@@ -117,7 +117,7 @@ module RuboCop
117
117
  elsif style == :space
118
118
  SpaceCorrector.add_space(processed_source, corrector, left, right)
119
119
  else
120
- compact_corrections(corrector, tokens, left, right)
120
+ compact_corrections(corrector, node, tokens, left, right)
121
121
  end
122
122
  end
123
123
 
@@ -178,6 +178,8 @@ module RuboCop
178
178
  space_offenses(node, left, nil, MSG, start_ok: start_ok, end_ok: true)
179
179
  end
180
180
 
181
+ return if closing_bracket_kept_apart?(node, tokens, left, right)
182
+
181
183
  if qualifies_for_compact?(tokens, right)
182
184
  compact_offense(node, right)
183
185
  elsif !multi_dimensional_array?(tokens, right)
@@ -212,13 +214,24 @@ module RuboCop
212
214
  end
213
215
  end
214
216
 
215
- def compact_corrections(corrector, tokens, left, right)
217
+ # A closing bracket on its own line is only collapsed when the opening
218
+ # bracket ends up on the first element's line, so that the brackets
219
+ # stay symmetrical (see `Layout/MultilineArrayBraceLayout`).
220
+ def closing_bracket_kept_apart?(node, tokens, left, right)
221
+ end_has_own_line?(right) &&
222
+ !multi_dimensional_array?(tokens, left, side: :left) &&
223
+ !same_line?(left, node.children.first)
224
+ end
225
+
226
+ def compact_corrections(corrector, node, tokens, left, right)
216
227
  if multi_dimensional_array?(tokens, left, side: :left)
217
228
  compact(corrector, left, :right)
218
229
  elsif !left.space_after?
219
230
  corrector.insert_after(left.pos, ' ')
220
231
  end
221
232
 
233
+ return if closing_bracket_kept_apart?(node, tokens, left, right)
234
+
222
235
  if multi_dimensional_array?(tokens, right)
223
236
  compact(corrector, right, :left)
224
237
  elsif !right.space_before?
@@ -153,7 +153,7 @@ module RuboCop
153
153
  else
154
154
  column = node.source_range.column
155
155
  return if multiline_block?(left_brace, right_brace) &&
156
- aligned_braces?(inner, right_brace, column)
156
+ aligned_braces?(inner, right_brace, column, left_brace)
157
157
 
158
158
  space_inside_right_brace(inner, right_brace, column)
159
159
  end
@@ -163,14 +163,21 @@ module RuboCop
163
163
  left_brace.first_line != right_brace.first_line
164
164
  end
165
165
 
166
- def aligned_braces?(inner, right_brace, column)
167
- column == right_brace.column || column == inner_last_space_count(inner)
166
+ def aligned_braces?(inner, right_brace, column, left_brace)
167
+ column == right_brace.column ||
168
+ column == inner_last_space_count(inner) ||
169
+ aligned_with_opening_brace_line?(left_brace, right_brace)
168
170
  end
169
171
 
170
172
  def inner_last_space_count(inner)
171
173
  inner.split("\n").last.count(' ')
172
174
  end
173
175
 
176
+ def aligned_with_opening_brace_line?(left_brace, right_brace)
177
+ begins_its_line?(right_brace) &&
178
+ right_brace.column == left_brace.source_line[/\A\s*/].length
179
+ end
180
+
174
181
  def no_space_inside_left_brace(left_brace, args_delimiter)
175
182
  if pipe?(args_delimiter)
176
183
  if left_brace.end_pos == args_delimiter.begin_pos &&
@@ -197,9 +204,7 @@ module RuboCop
197
204
  'Space between { and | detected.')
198
205
  end
199
206
  else
200
- brace_with_space = range_with_surrounding_space(left_brace, side: :right)
201
- space(brace_with_space.begin_pos + 1, brace_with_space.end_pos,
202
- 'Space inside { detected.')
207
+ space_after_left_brace(left_brace)
203
208
  end
204
209
  end
205
210
 
@@ -207,6 +212,20 @@ module RuboCop
207
212
  args_delimiter&.is?('|')
208
213
  end
209
214
 
215
+ def space_after_left_brace(left_brace)
216
+ brace_with_space = range_with_surrounding_space(left_brace, side: :right)
217
+ return if brace_with_space.source.match?(/\R/)
218
+ return if comment_at?(brace_with_space.end_pos)
219
+
220
+ space(
221
+ brace_with_space.begin_pos + 1, brace_with_space.end_pos, 'Space inside { detected.'
222
+ )
223
+ end
224
+
225
+ def comment_at?(pos)
226
+ processed_source.comments.any? { |comment| comment.source_range.begin_pos == pos }
227
+ end
228
+
210
229
  def space_inside_right_brace(inner, right_brace, column)
211
230
  brace_with_space = range_with_surrounding_space(right_brace, side: :left)
212
231
  begin_pos = brace_with_space.begin_pos
@@ -216,7 +235,7 @@ module RuboCop
216
235
  begin_pos = end_pos - (right_brace.column - column)
217
236
  end
218
237
 
219
- if inner.end_with?(']')
238
+ if closing_bracket_on_own_line?(inner)
220
239
  end_pos -= 1
221
240
  begin_pos = end_pos - (inner_last_space_count(inner) - column)
222
241
  end
@@ -224,6 +243,10 @@ module RuboCop
224
243
  space(begin_pos, end_pos, 'Space inside } detected.')
225
244
  end
226
245
 
246
+ def closing_bracket_on_own_line?(inner)
247
+ inner[/[^\n]*\z/].match?(/\A\s*\]\z/)
248
+ end
249
+
227
250
  def no_space(begin_pos, end_pos, msg)
228
251
  if style == :space
229
252
  offense(begin_pos, end_pos, msg)
@@ -241,7 +264,7 @@ module RuboCop
241
264
  end
242
265
 
243
266
  def offense(begin_pos, end_pos, msg, style_param = 'EnforcedStyle')
244
- return if begin_pos > end_pos
267
+ return if begin_pos >= end_pos
245
268
 
246
269
  range = range_between(begin_pos, end_pos)
247
270
  add_offense(range, message: msg) do |corrector|
@@ -108,14 +108,9 @@ module RuboCop
108
108
  end
109
109
 
110
110
  def left_ref_bracket(node, tokens)
111
- current_token = tokens.reverse.find(&:left_ref_bracket?)
112
- previous_token = previous_token(current_token)
111
+ bracket_pos = node.loc.selector.begin_pos
113
112
 
114
- if node.method?(:[]=) || (previous_token && !previous_token.right_bracket?)
115
- tokens.find(&:left_ref_bracket?)
116
- else
117
- current_token
118
- end
113
+ tokens.find { |token| token.left_ref_bracket? && token.begin_pos == bracket_pos }
119
114
  end
120
115
 
121
116
  def closing_bracket(tokens, opening_bracket)
@@ -129,11 +124,6 @@ module RuboCop
129
124
  end
130
125
  end
131
126
 
132
- def previous_token(current_token)
133
- index = processed_source.tokens.index(current_token)
134
- index.nil? || index.zero? ? nil : processed_source.tokens[index - 1]
135
- end
136
-
137
127
  def empty_config
138
128
  cop_config['EnforcedStyleForEmptyBrackets']
139
129
  end
@@ -37,7 +37,7 @@ module RuboCop
37
37
  'should be %<possible>s.'
38
38
 
39
39
  def self.autocorrect_incompatible_with
40
- [Naming::BlockForwarding]
40
+ [Naming::BlockForwarding, Style::ArgumentsForwarding]
41
41
  end
42
42
 
43
43
  def on_new_investigation
@@ -56,7 +56,8 @@ module RuboCop
56
56
  end
57
57
 
58
58
  def on_send(node)
59
- return if node.parenthesized?
59
+ # `foo.*` is a regular method call, not a binary operator.
60
+ return if node.parenthesized? || node.loc.dot
60
61
 
61
62
  return unless (parent = node.parent)
62
63
  return unless operator?(parent)
@@ -55,7 +55,8 @@ module RuboCop
55
55
  return node if first_argument_is_regexp?(node) || !node.parent
56
56
 
57
57
  if (node.parent.send_type? && node.receiver) ||
58
- method_chain_to_regexp_receiver?(node, regexp_receiver)
58
+ method_chain_to_regexp_receiver?(node, regexp_receiver) ||
59
+ part_of_hash_argument?(node)
59
60
  node = find_offense_node(node.parent, regexp_receiver)
60
61
  end
61
62
 
@@ -72,6 +73,10 @@ module RuboCop
72
73
 
73
74
  parent.parent && parent_receiver.receiver == regexp_receiver
74
75
  end
76
+
77
+ def part_of_hash_argument?(node)
78
+ node.pair_type? || node.parent.pair_type? || (node.hash_type? && !node.braces?)
79
+ end
75
80
  end
76
81
  end
77
82
  end
@@ -97,12 +97,14 @@ module RuboCop
97
97
  end
98
98
 
99
99
  def can_simplify_conditional?(node)
100
- node.else_branch && node.loc.else.source == 'else'
100
+ !node.elsif? && node.else_branch && node.loc.else.source == 'else'
101
101
  end
102
102
 
103
103
  def remove_empty_branch(corrector, node)
104
104
  range = if empty_if_branch?(node) && else_branch?(node)
105
105
  branch_range(node)
106
+ elsif node.condition.last_line == node.loc.else.line
107
+ node.source_range.with(end_pos: node.loc.else.begin_pos)
106
108
  else
107
109
  deletion_range(branch_range(node))
108
110
  end
@@ -34,6 +34,10 @@ module RuboCop
34
34
 
35
35
  MSG = 'Put a method call with a HEREDOC receiver on the same line as the HEREDOC opening.'
36
36
 
37
+ def self.autocorrect_incompatible_with
38
+ [Style::MethodCallWithArgsParentheses]
39
+ end
40
+
37
41
  def on_send(node)
38
42
  heredoc = heredoc_node_descendent_receiver(node)
39
43
  return unless heredoc
@@ -98,6 +98,8 @@ module RuboCop
98
98
  end
99
99
  elsif node.condition.falsey_literal?
100
100
  add_offense(node.condition) do |corrector|
101
+ next if orphans_loop_control_keyword?(node)
102
+
101
103
  corrector.replace(node, node.body.child_nodes.map(&:source).join("\n"))
102
104
  end
103
105
  end
@@ -127,6 +129,8 @@ module RuboCop
127
129
  end
128
130
  elsif node.condition.truthy_literal?
129
131
  add_offense(node.condition) do |corrector|
132
+ next if orphans_loop_control_keyword?(node)
133
+
130
134
  corrector.replace(node, node.body.child_nodes.map(&:source).join("\n"))
131
135
  end
132
136
  end
@@ -175,6 +179,15 @@ module RuboCop
175
179
 
176
180
  private
177
181
 
182
+ # Unwrapping the body of a loop that runs once would leave a `break`,
183
+ # `next` or `redo` that belongs to it outside of any loop.
184
+ def orphans_loop_control_keyword?(node)
185
+ node.body.each_node(:next, :break, :redo).any? do |control|
186
+ inner = control.each_ancestor.take_while { |ancestor| !ancestor.equal?(node) }
187
+ inner.none? { |ancestor| ancestor.any_block_type? || ancestor.loop_keyword? }
188
+ end
189
+ end
190
+
178
191
  def void_value_expression?(node)
179
192
  node = node.children.last while node&.begin_type?
180
193
 
@@ -273,6 +286,8 @@ module RuboCop
273
286
  "#{node.else_branch.source.sub('elsif', 'if')}\nend"
274
287
  elsif node.else? || node.ternary?
275
288
  node.else_branch.source
289
+ elsif node.value_used?
290
+ 'nil'
276
291
  else
277
292
  '' # Equivalent to removing the node
278
293
  end