rubocop 1.90.0 → 1.92.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (303) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +241 -11
  4. data/config/obsoletion.yml +9 -0
  5. data/lib/rubocop/cached_data.rb +47 -7
  6. data/lib/rubocop/changed_files.rb +134 -0
  7. data/lib/rubocop/cli/command/cop_names.rb +57 -0
  8. data/lib/rubocop/cli/command/execute_runner.rb +23 -3
  9. data/lib/rubocop/cli/command/explain.rb +34 -0
  10. data/lib/rubocop/cli/command/show_cops.rb +6 -0
  11. data/lib/rubocop/cli/command/show_docs_url.rb +6 -4
  12. data/lib/rubocop/cli.rb +11 -4
  13. data/lib/rubocop/comment_config/directive_range.rb +3 -1
  14. data/lib/rubocop/comment_config/disable_next.rb +49 -3
  15. data/lib/rubocop/comment_config/push_pop.rb +55 -0
  16. data/lib/rubocop/comment_config.rb +32 -53
  17. data/lib/rubocop/config.rb +10 -0
  18. data/lib/rubocop/config_loader.rb +18 -7
  19. data/lib/rubocop/config_loader_resolver.rb +79 -12
  20. data/lib/rubocop/config_obsoletion/cop_rule.rb +2 -1
  21. data/lib/rubocop/config_obsoletion/removed_cop.rb +9 -1
  22. data/lib/rubocop/config_obsoletion.rb +1 -1
  23. data/lib/rubocop/config_validator.rb +29 -3
  24. data/lib/rubocop/cop/autocorrect_logic.rb +10 -5
  25. data/lib/rubocop/cop/badge.rb +1 -1
  26. data/lib/rubocop/cop/base.rb +57 -24
  27. data/lib/rubocop/cop/bundler/gem_comment.rb +0 -1
  28. data/lib/rubocop/cop/bundler/ordered_gems.rb +0 -8
  29. data/lib/rubocop/cop/cop_documentation.rb +81 -0
  30. data/lib/rubocop/cop/cop_explanation.rb +140 -0
  31. data/lib/rubocop/cop/correctors/alignment_corrector.rb +13 -1
  32. data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +22 -7
  33. data/lib/rubocop/cop/correctors/empty_line_corrector.rb +1 -1
  34. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +23 -2
  35. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +45 -8
  36. data/lib/rubocop/cop/correctors.rb +2 -2
  37. data/lib/rubocop/cop/documentation.rb +1 -1
  38. data/lib/rubocop/cop/gemspec/dependency_version.rb +0 -6
  39. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +0 -6
  40. data/lib/rubocop/cop/gemspec/require_mfa.rb +1 -1
  41. data/lib/rubocop/cop/generator.rb +1 -1
  42. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +2 -5
  43. data/lib/rubocop/cop/internal_affairs/node_type_group.rb +0 -7
  44. data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
  45. data/lib/rubocop/cop/internal_affairs/redundant_context_config_parameter.rb +1 -7
  46. data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +1 -7
  47. data/lib/rubocop/cop/layout/argument_alignment.rb +2 -0
  48. data/lib/rubocop/cop/layout/block_alignment.rb +27 -0
  49. data/lib/rubocop/cop/layout/case_indentation.rb +25 -9
  50. data/lib/rubocop/cop/layout/class_structure.rb +9 -1
  51. data/lib/rubocop/cop/layout/comment_indentation.rb +8 -5
  52. data/lib/rubocop/cop/layout/def_end_alignment.rb +4 -0
  53. data/lib/rubocop/cop/layout/else_alignment.rb +1 -0
  54. data/lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb +14 -5
  55. data/lib/rubocop/cop/layout/empty_lines.rb +5 -2
  56. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +1 -1
  57. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -0
  58. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +1 -1
  59. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +23 -1
  60. data/lib/rubocop/cop/layout/end_alignment.rb +16 -10
  61. data/lib/rubocop/cop/layout/extra_spacing.rb +4 -0
  62. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -0
  63. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +17 -4
  64. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +5 -3
  65. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +6 -0
  66. data/lib/rubocop/cop/layout/first_method_parameter_line_break.rb +4 -0
  67. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +11 -0
  68. data/lib/rubocop/cop/layout/hash_alignment.rb +15 -10
  69. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -0
  70. data/lib/rubocop/cop/layout/heredoc_indentation.rb +15 -0
  71. data/lib/rubocop/cop/layout/indentation_style.rb +4 -1
  72. data/lib/rubocop/cop/layout/indentation_width.rb +4 -4
  73. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +8 -1
  74. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +25 -9
  75. data/lib/rubocop/cop/layout/line_length.rb +71 -19
  76. data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +12 -0
  77. data/lib/rubocop/cop/layout/multiline_block_layout.rb +8 -1
  78. data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +7 -0
  79. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +6 -0
  80. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +14 -0
  81. data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +4 -0
  82. data/lib/rubocop/cop/layout/redundant_line_break.rb +22 -1
  83. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +15 -2
  84. data/lib/rubocop/cop/layout/single_line_block_chain.rb +10 -2
  85. data/lib/rubocop/cop/layout/space_after_not.rb +5 -0
  86. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +10 -4
  87. data/lib/rubocop/cop/layout/space_around_keyword.rb +10 -0
  88. data/lib/rubocop/cop/layout/space_around_operators.rb +5 -1
  89. data/lib/rubocop/cop/layout/space_before_block_braces.rb +4 -1
  90. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +15 -2
  91. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +31 -8
  92. data/lib/rubocop/cop/lint/ambiguous_operator.rb +1 -1
  93. data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +2 -1
  94. data/lib/rubocop/cop/lint/argument_mismatch.rb +14 -4
  95. data/lib/rubocop/cop/lint/assignment_in_condition.rb +2 -0
  96. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +88 -14
  97. data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -1
  98. data/lib/rubocop/cop/lint/empty_block.rb +1 -1
  99. data/lib/rubocop/cop/lint/empty_conditional_body.rb +3 -1
  100. data/lib/rubocop/cop/lint/literal_as_condition.rb +23 -2
  101. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +63 -19
  102. data/lib/rubocop/cop/lint/loop.rb +24 -2
  103. data/lib/rubocop/cop/lint/misplaced_magic_comment.rb +245 -0
  104. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +19 -4
  105. data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
  106. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +1 -1
  107. data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
  108. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +17 -1
  109. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +42 -17
  110. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +13 -2
  111. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +18 -1
  112. data/lib/rubocop/cop/lint/useless_assignment.rb +14 -7
  113. data/lib/rubocop/cop/lint/void.rb +8 -1
  114. data/lib/rubocop/cop/lint.rb +1 -0
  115. data/lib/rubocop/cop/metrics/class_length.rb +4 -2
  116. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +0 -4
  117. data/lib/rubocop/cop/metrics/module_length.rb +4 -2
  118. data/lib/rubocop/cop/migration/department_name.rb +1 -1
  119. data/lib/rubocop/cop/mixin/alignment.rb +5 -1
  120. data/lib/rubocop/cop/mixin/annotation_comment.rb +1 -1
  121. data/lib/rubocop/cop/mixin/check_line_breakable.rb +17 -14
  122. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +5 -1
  123. data/lib/rubocop/cop/mixin/code_length.rb +10 -2
  124. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +12 -1
  125. data/lib/rubocop/cop/mixin/endless_method_body_help.rb +69 -0
  126. data/lib/rubocop/cop/mixin/forbidden_identifiers.rb +2 -2
  127. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +26 -2
  128. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +31 -3
  129. data/lib/rubocop/cop/mixin/hash_transform_method.rb +1 -1
  130. data/lib/rubocop/cop/mixin/min_branches_count.rb +3 -1
  131. data/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb +11 -1
  132. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +9 -0
  133. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  134. data/lib/rubocop/cop/mixin/pattern_matching_help.rb +35 -0
  135. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +29 -3
  136. data/lib/rubocop/cop/mixin/project_index_help.rb +15 -0
  137. data/lib/rubocop/cop/mixin/reparsed_equivalence.rb +13 -3
  138. data/lib/rubocop/cop/mixin/statement_modifier.rb +22 -2
  139. data/lib/rubocop/cop/mixin.rb +4 -2
  140. data/lib/rubocop/cop/naming/accessor_method_name.rb +0 -16
  141. data/lib/rubocop/cop/naming/block_forwarding.rb +4 -1
  142. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  143. data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +9 -2
  144. data/lib/rubocop/cop/naming/heredoc_delimiter_naming.rb +5 -3
  145. data/lib/rubocop/cop/naming/predicate_prefix.rb +7 -22
  146. data/lib/rubocop/cop/offense.rb +59 -3
  147. data/lib/rubocop/cop/registry.rb +10 -2
  148. data/lib/rubocop/cop/security/json_load.rb +16 -4
  149. data/lib/rubocop/cop/severity.rb +12 -0
  150. data/lib/rubocop/cop/style/access_modifier_declarations.rb +10 -2
  151. data/lib/rubocop/cop/style/alias.rb +18 -5
  152. data/lib/rubocop/cop/style/ambiguous_endless_method_definition.rb +4 -3
  153. data/lib/rubocop/cop/style/and_or.rb +4 -0
  154. data/lib/rubocop/cop/style/arguments_forwarding.rb +6 -3
  155. data/lib/rubocop/cop/style/attr.rb +3 -4
  156. data/lib/rubocop/cop/style/bitwise_predicate.rb +6 -4
  157. data/lib/rubocop/cop/style/block_delimiters.rb +29 -3
  158. data/lib/rubocop/cop/style/case_like_if.rb +9 -6
  159. data/lib/rubocop/cop/style/class_and_module_children.rb +54 -16
  160. data/lib/rubocop/cop/style/class_methods_definitions.rb +8 -0
  161. data/lib/rubocop/cop/style/combinable_loops.rb +85 -11
  162. data/lib/rubocop/cop/style/concat_array_literals.rb +46 -7
  163. data/lib/rubocop/cop/style/conditional_assignment.rb +4 -0
  164. data/lib/rubocop/cop/style/constant_visibility.rb +6 -2
  165. data/lib/rubocop/cop/style/data_inheritance.rb +14 -0
  166. data/lib/rubocop/cop/style/directive_scope.rb +154 -21
  167. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +118 -18
  168. data/lib/rubocop/cop/style/documentation_method.rb +1 -9
  169. data/lib/rubocop/cop/style/double_negation.rb +11 -0
  170. data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
  171. data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
  172. data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
  173. data/lib/rubocop/cop/style/empty_literal.rb +1 -1
  174. data/lib/rubocop/cop/style/empty_method.rb +15 -3
  175. data/lib/rubocop/cop/style/endless_method.rb +91 -11
  176. data/lib/rubocop/cop/style/exact_regexp_match.rb +45 -8
  177. data/lib/rubocop/cop/style/explicit_block_argument.rb +50 -10
  178. data/lib/rubocop/cop/style/file_null.rb +2 -2
  179. data/lib/rubocop/cop/style/float_division.rb +28 -14
  180. data/lib/rubocop/cop/style/for.rb +44 -1
  181. data/lib/rubocop/cop/style/format_string.rb +28 -6
  182. data/lib/rubocop/cop/style/format_string_token.rb +1 -1
  183. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +2 -1
  184. data/lib/rubocop/cop/style/guard_clause.rb +13 -1
  185. data/lib/rubocop/cop/style/hash_conversion.rb +28 -8
  186. data/lib/rubocop/cop/style/hash_lookup_method.rb +35 -9
  187. data/lib/rubocop/cop/style/hash_syntax.rb +16 -3
  188. data/lib/rubocop/cop/style/identical_conditional_branches.rb +22 -3
  189. data/lib/rubocop/cop/style/if_inside_else.rb +21 -1
  190. data/lib/rubocop/cop/style/if_unless_modifier.rb +19 -20
  191. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +4 -0
  192. data/lib/rubocop/cop/style/if_with_semicolon.rb +47 -4
  193. data/lib/rubocop/cop/style/inverse_methods.rb +24 -2
  194. data/lib/rubocop/cop/style/it_block_parameter.rb +32 -3
  195. data/lib/rubocop/cop/style/keyword_parameters_order.rb +3 -1
  196. data/lib/rubocop/cop/style/lambda.rb +18 -0
  197. data/lib/rubocop/cop/style/lambda_call.rb +7 -2
  198. data/lib/rubocop/cop/style/magic_comment_format.rb +1 -1
  199. data/lib/rubocop/cop/style/map_into_array.rb +1 -2
  200. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +45 -10
  201. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +25 -2
  202. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +26 -3
  203. data/lib/rubocop/cop/style/method_def_parentheses.rb +29 -3
  204. data/lib/rubocop/cop/style/missing_else.rb +7 -1
  205. data/lib/rubocop/cop/style/multiline_if_modifier.rb +45 -5
  206. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +7 -0
  207. data/lib/rubocop/cop/style/multiline_method_signature.rb +2 -5
  208. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +7 -3
  209. data/lib/rubocop/cop/style/multiline_when_then.rb +7 -0
  210. data/lib/rubocop/cop/style/mutable_constant.rb +28 -1
  211. data/lib/rubocop/cop/style/negated_if_else_condition.rb +2 -1
  212. data/lib/rubocop/cop/style/negative_array_index.rb +9 -1
  213. data/lib/rubocop/cop/style/nested_modifier.rb +36 -12
  214. data/lib/rubocop/cop/style/nested_ternary_operator.rb +26 -8
  215. data/lib/rubocop/cop/style/next.rb +24 -10
  216. data/lib/rubocop/cop/style/nil_comparison.rb +48 -8
  217. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  218. data/lib/rubocop/cop/style/non_nil_check.rb +18 -2
  219. data/lib/rubocop/cop/style/not.rb +10 -3
  220. data/lib/rubocop/cop/style/numeric_literal_prefix.rb +11 -1
  221. data/lib/rubocop/cop/style/numeric_predicate.rb +21 -5
  222. data/lib/rubocop/cop/style/one_line_conditional.rb +14 -3
  223. data/lib/rubocop/cop/style/parallel_assignment.rb +1 -1
  224. data/lib/rubocop/cop/style/parentheses_around_condition.rb +6 -1
  225. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +14 -3
  226. data/lib/rubocop/cop/style/reduce_to_hash.rb +17 -1
  227. data/lib/rubocop/cop/style/redundant_begin.rb +20 -3
  228. data/lib/rubocop/cop/style/redundant_condition.rb +34 -17
  229. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +16 -1
  230. data/lib/rubocop/cop/style/redundant_exception.rb +15 -3
  231. data/lib/rubocop/cop/style/redundant_fetch_block.rb +10 -2
  232. data/lib/rubocop/cop/style/redundant_freeze.rb +10 -1
  233. data/lib/rubocop/cop/style/redundant_parentheses.rb +23 -2
  234. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +7 -0
  235. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +6 -5
  236. data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +25 -5
  237. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +4 -0
  238. data/lib/rubocop/cop/style/regexp_literal.rb +75 -26
  239. data/lib/rubocop/cop/style/require_order.rb +9 -0
  240. data/lib/rubocop/cop/style/rescue_modifier.rb +5 -9
  241. data/lib/rubocop/cop/style/return_nil.rb +17 -2
  242. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +11 -1
  243. data/lib/rubocop/cop/style/sample.rb +9 -8
  244. data/lib/rubocop/cop/style/self_assignment.rb +12 -15
  245. data/lib/rubocop/cop/style/semicolon.rb +16 -5
  246. data/lib/rubocop/cop/style/single_line_block_params.rb +1 -1
  247. data/lib/rubocop/cop/style/single_line_methods.rb +16 -4
  248. data/lib/rubocop/cop/style/special_global_vars.rb +1 -1
  249. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +29 -5
  250. data/lib/rubocop/cop/style/string_concatenation.rb +9 -3
  251. data/lib/rubocop/cop/style/struct_inheritance.rb +14 -0
  252. data/lib/rubocop/cop/style/super_arguments.rb +3 -0
  253. data/lib/rubocop/cop/style/symbol_proc.rb +23 -5
  254. data/lib/rubocop/cop/style/ternary_parentheses.rb +23 -1
  255. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  256. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +23 -19
  257. data/lib/rubocop/cop/style/while_until_modifier.rb +1 -1
  258. data/lib/rubocop/cop/style/yoda_condition.rb +24 -9
  259. data/lib/rubocop/cop/style/yoda_expression.rb +10 -1
  260. data/lib/rubocop/cop/style.rb +0 -1
  261. data/lib/rubocop/cop/team.rb +10 -5
  262. data/lib/rubocop/cop/util.rb +1 -1
  263. data/lib/rubocop/cop/variable_force/assignment.rb +1 -1
  264. data/lib/rubocop/cop/variable_force/variable.rb +14 -1
  265. data/lib/rubocop/cops_documentation_generator.rb +23 -2
  266. data/lib/rubocop/directive_comment.rb +37 -8
  267. data/lib/rubocop/formatter/base_formatter.rb +22 -0
  268. data/lib/rubocop/formatter/disabled_config_formatter.rb +19 -1
  269. data/lib/rubocop/formatter/formatter_set.rb +6 -1
  270. data/lib/rubocop/formatter/github_actions_formatter.rb +6 -5
  271. data/lib/rubocop/formatter/html_formatter.rb +2 -2
  272. data/lib/rubocop/formatter/json_formatter.rb +71 -1
  273. data/lib/rubocop/formatter/junit_formatter.rb +1 -1
  274. data/lib/rubocop/formatter/offense_count_formatter.rb +3 -1
  275. data/lib/rubocop/formatter/pacman_formatter.rb +3 -1
  276. data/lib/rubocop/formatter/sarif_formatter.rb +186 -0
  277. data/lib/rubocop/formatter/worst_offenders_formatter.rb +3 -1
  278. data/lib/rubocop/formatter.rb +1 -0
  279. data/lib/rubocop/lsp/runtime.rb +47 -11
  280. data/lib/rubocop/lsp/stdin_runner.rb +11 -5
  281. data/lib/rubocop/lsp.rb +5 -2
  282. data/lib/rubocop/magic_comment.rb +27 -0
  283. data/lib/rubocop/mcp/autocorrection_request.rb +52 -0
  284. data/lib/rubocop/mcp/explain_tool.rb +68 -0
  285. data/lib/rubocop/mcp/scope.rb +65 -0
  286. data/lib/rubocop/mcp/server.rb +184 -76
  287. data/lib/rubocop/mcp/source_files.rb +35 -0
  288. data/lib/rubocop/offense_limit.rb +30 -0
  289. data/lib/rubocop/options.rb +97 -4
  290. data/lib/rubocop/path_util.rb +1 -1
  291. data/lib/rubocop/plugin/configuration_integrator.rb +12 -5
  292. data/lib/rubocop/plugin/loader.rb +1 -1
  293. data/lib/rubocop/rake_task.rb +1 -1
  294. data/lib/rubocop/remote_config.rb +8 -5
  295. data/lib/rubocop/result_cache.rb +36 -18
  296. data/lib/rubocop/runner.rb +160 -49
  297. data/lib/rubocop/server/cache.rb +2 -2
  298. data/lib/rubocop/unified_diff.rb +257 -0
  299. data/lib/rubocop/util.rb +69 -0
  300. data/lib/rubocop/version.rb +1 -1
  301. data/lib/rubocop.rb +9 -1
  302. metadata +21 -6
  303. data/lib/rubocop/cop/style/double_cop_disable_directive.rb +0 -46
@@ -51,14 +51,6 @@ module RuboCop
51
51
  end
52
52
  end
53
53
 
54
- private
55
-
56
- def previous_declaration(node)
57
- declarations = gem_declarations(processed_source.ast)
58
- node_index = declarations.map(&:location).find_index(node.location)
59
- declarations.to_a[node_index - 1]
60
- end
61
-
62
54
  # @!method gem_declarations(node)
63
55
  def_node_search :gem_declarations, <<~PATTERN
64
56
  (:send nil? :gem str ...)
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Reads the documentation comment a cop carries above its class definition
6
+ # and splits it into the sections worth showing on their own.
7
+ #
8
+ # YARD is a development dependency, so the comment is read straight from the
9
+ # source rather than through a doclet.
10
+ # @api private
11
+ class CopDocumentation
12
+ TAG = /^@(\w+)\s*(.*)$/.freeze
13
+
14
+ def initialize(cop_class)
15
+ @cop_class = cop_class
16
+ end
17
+
18
+ # @return [Array<Array(String, Array<String>)>] title and body per section
19
+ def sections
20
+ comment = comment_lines
21
+ return [] if comment.empty?
22
+
23
+ prose, tagged = split_on_first_tag(comment)
24
+
25
+ [['Details', strip_blank(prose)], *tagged_sections(tagged)].reject { |_, b| b.empty? }
26
+ end
27
+
28
+ private
29
+
30
+ def comment_lines
31
+ path, line = source_location
32
+ return [] unless path && File.file?(path)
33
+
34
+ lines = File.readlines(path, chomp: true)
35
+ collect_comment_above(lines, line - 2)
36
+ end
37
+
38
+ def source_location
39
+ name = @cop_class.name
40
+ return [] unless name
41
+
42
+ Module.const_source_location(name) || []
43
+ rescue NameError
44
+ []
45
+ end
46
+
47
+ # Walks up from the class definition for as long as the lines are comments.
48
+ def collect_comment_above(lines, index)
49
+ block = []
50
+ while index >= 0 && lines[index].match?(/^\s*#/)
51
+ block.unshift(lines[index].sub(/^\s*# ?/, ''))
52
+ index -= 1
53
+ end
54
+ block
55
+ end
56
+
57
+ def split_on_first_tag(comment)
58
+ first_tag = comment.index { |line| line.match?(TAG) }
59
+ return [comment, []] unless first_tag
60
+
61
+ [comment[0...first_tag], comment[first_tag..]]
62
+ end
63
+
64
+ def tagged_sections(lines)
65
+ lines.slice_before { |line| line.match?(TAG) }.map do |(head, *body)|
66
+ tag, argument = head.match(TAG).captures
67
+ [section_title(tag, argument), strip_blank(body)]
68
+ end
69
+ end
70
+
71
+ def section_title(tag, argument)
72
+ title = tag.capitalize
73
+ argument.empty? ? title : "#{title}: #{argument}"
74
+ end
75
+
76
+ def strip_blank(lines)
77
+ lines.drop_while(&:empty?).reverse.drop_while(&:empty?).reverse
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Explains what a single cop does: its description, how it is configured,
6
+ # whether it corrects, and the examples from its own documentation.
7
+ #
8
+ # Rendered as plain text, which is what `--explain` prints and what the MCP
9
+ # server hands to an agent.
10
+ # @api private
11
+ class CopExplanation
12
+ # Keys that describe the cop to RuboCop itself rather than something a
13
+ # user would set, so they are reported in their own section or not at all.
14
+ METADATA_KEYS = %w[
15
+ Description StyleGuide Reference References Enabled Safe SafeAutoCorrect
16
+ AutoCorrect VersionAdded VersionChanged Include Exclude Details Preview
17
+ ].freeze
18
+
19
+ def initialize(cop_class, config)
20
+ @cop_class = cop_class
21
+ @config = config
22
+ @cop_config = config.for_cop(cop_class.cop_name)
23
+ end
24
+
25
+ def to_s
26
+ lines.join("\n")
27
+ end
28
+
29
+ private
30
+
31
+ def lines
32
+ description = description_lines
33
+
34
+ [
35
+ @cop_class.cop_name,
36
+ *section(nil, description),
37
+ *section('Properties', property_lines),
38
+ *documentation_sections(description).flat_map { |title, body| section(title, body) },
39
+ *section('Configuration', configuration_lines),
40
+ *section('References', reference_lines)
41
+ ]
42
+ end
43
+
44
+ def section(title, body)
45
+ return [] if body.empty?
46
+
47
+ heading = title ? [title, ''] : []
48
+ ['', *heading, *body.map { |line| line.empty? ? '' : " #{line}" }]
49
+ end
50
+
51
+ def description_lines
52
+ Array(@cop_config['Description']).flat_map { |line| line.split("\n") }
53
+ end
54
+
55
+ def property_lines
56
+ [
57
+ "Enabled: #{@cop_config.fetch('Enabled', true)}",
58
+ "Safe: #{@cop_config.fetch('Safe', true)}",
59
+ "Autocorrect: #{autocorrect_description}",
60
+ *version_lines,
61
+ *scope_lines
62
+ ]
63
+ end
64
+
65
+ # Which files the cop looks at. A cop that only runs on Gemfiles
66
+ # explains an absent offense better than anything else here does.
67
+ def scope_lines
68
+ [
69
+ *patterns_line('Applies to: ', @cop_config['Include']),
70
+ *patterns_line('Excludes: ', @cop_config['Exclude'])
71
+ ]
72
+ end
73
+
74
+ def patterns_line(label, patterns)
75
+ patterns = Array(patterns).compact
76
+ return [] if patterns.empty?
77
+
78
+ ["#{label}#{patterns.map { |pattern| readable_pattern(pattern) }.join(', ')}"]
79
+ end
80
+
81
+ # `Exclude` patterns are absolutized against the config that set them,
82
+ # which makes for a wall of identical prefixes. A user config can also
83
+ # hold a regexp rather than a glob.
84
+ def readable_pattern(pattern)
85
+ pattern.is_a?(String) ? PathUtil.smart_path(pattern) : pattern.inspect
86
+ end
87
+
88
+ def autocorrect_description
89
+ return 'not supported' unless @cop_class.support_autocorrect?
90
+
91
+ # Asks the cop, so `AutoCorrect` is read the way the cop reads it.
92
+ cop = @cop_class.new(@config)
93
+ return 'disabled' unless cop.always_autocorrect? || cop.contextual_autocorrect?
94
+
95
+ description = cop.safe_autocorrect? ? 'safe, applied by -a' : 'unsafe, applied by -A only'
96
+ # Contextual corrections wait until the code is finished, which an
97
+ # editor or an agent mid-edit cannot promise, so they have to ask.
98
+ description += ', but through LSP or MCP only on request' if cop.contextual_autocorrect?
99
+ description
100
+ end
101
+
102
+ def version_lines
103
+ lines = []
104
+ lines << "Added: #{@cop_config['VersionAdded']}" if @cop_config['VersionAdded']
105
+ lines << "Changed: #{@cop_config['VersionChanged']}" if @cop_config['VersionChanged']
106
+ lines
107
+ end
108
+
109
+ def configuration_lines
110
+ (@cop_config.keys - METADATA_KEYS).sort.map do |key|
111
+ "#{key}: #{format_value(@cop_config[key])}"
112
+ end
113
+ end
114
+
115
+ def format_value(value)
116
+ value.is_a?(Array) ? value.join(', ') : value.to_s
117
+ end
118
+
119
+ def reference_lines
120
+ annotator = MessageAnnotator.new(@config, @cop_class.cop_name, @cop_config, {})
121
+
122
+ [*annotator.urls, Documentation.url_for(@cop_class, @config)].compact
123
+ end
124
+
125
+ # The comment above the class opens by restating the description, which
126
+ # is already printed above, so that opening is dropped.
127
+ def documentation_sections(description)
128
+ CopDocumentation.new(@cop_class).sections.filter_map do |title, body|
129
+ body -= description if title == 'Details'
130
+ body = strip_blank(body)
131
+ [title, body] unless body.empty?
132
+ end
133
+ end
134
+
135
+ def strip_blank(lines)
136
+ lines.drop_while(&:empty?).reverse.drop_while(&:empty?).reverse
137
+ end
138
+ end
139
+ end
140
+ end
@@ -109,8 +109,20 @@ module RuboCop
109
109
  end
110
110
 
111
111
  def inside_string_ranges(node)
112
- return [] unless node.is_a?(Parser::AST::Node)
112
+ return source_string_ranges unless node.is_a?(Parser::AST::Node)
113
113
 
114
+ string_ranges(node)
115
+ end
116
+
117
+ def source_string_ranges
118
+ return [] unless processed_source.ast
119
+ return @source_string_ranges if @source_string_ranges_source.equal?(processed_source)
120
+
121
+ @source_string_ranges_source = processed_source
122
+ @source_string_ranges = string_ranges(processed_source.ast)
123
+ end
124
+
125
+ def string_ranges(node)
114
126
  node.each_node(:any_str).filter_map { |n| inside_string_range(n) }
115
127
  end
116
128
 
@@ -17,6 +17,7 @@ module RuboCop
17
17
 
18
18
  def call(corrector)
19
19
  corrector.replace(offending_range, correction)
20
+ corrector.replace(block_node.loc.end, closing_keyword) if block_node.braces?
20
21
  end
21
22
 
22
23
  private
@@ -24,13 +25,27 @@ module RuboCop
24
25
  attr_reader :block_node, :collection_node, :argument_node
25
26
 
26
27
  def correction
27
- if block_node.arguments?
28
- format(CORRECTION_WITH_ARGUMENTS,
29
- collection: collection_node.source,
30
- variables: argument_node.children.map(&:source).join(', '))
31
- else
32
- format(CORRECTION_WITHOUT_ARGUMENTS, enumerable: collection_node.source)
33
- end
28
+ keyword = if block_node.arguments?
29
+ format(
30
+ CORRECTION_WITH_ARGUMENTS,
31
+ collection: collection_node.source,
32
+ variables: argument_node.children.map(&:source).join(', ')
33
+ )
34
+ else
35
+ format(CORRECTION_WITHOUT_ARGUMENTS, enumerable: collection_node.source)
36
+ end
37
+
38
+ padding_needed?(offending_range.end_pos) ? "#{keyword} " : keyword
39
+ end
40
+
41
+ def closing_keyword
42
+ padding_needed?(block_node.loc.end.begin_pos - 1) ? ' end' : 'end'
43
+ end
44
+
45
+ def padding_needed?(position)
46
+ character = block_node.source_range.source_buffer.source[position]
47
+
48
+ !character.nil? && !character.match?(/\s/)
34
49
  end
35
50
 
36
51
  def offending_range
@@ -12,7 +12,7 @@ module RuboCop
12
12
  when :no_empty_lines
13
13
  corrector.remove(range)
14
14
  when :empty_lines
15
- corrector.insert_before(range, "\n")
15
+ corrector.insert_before(range.begin, "\n")
16
16
  end
17
17
  end
18
18
 
@@ -29,6 +29,10 @@ module RuboCop
29
29
  insert_arguments(corrector)
30
30
  end
31
31
 
32
+ def replace_delimiters?
33
+ !block_node.braces? && arg_to_unparenthesized_call?
34
+ end
35
+
32
36
  private
33
37
 
34
38
  attr_reader :block_node, :method, :arguments
@@ -77,7 +81,7 @@ module RuboCop
77
81
  end
78
82
 
79
83
  def replace_delimiters(corrector)
80
- return if block_node.braces? || !arg_to_unparenthesized_call?
84
+ return unless replace_delimiters?
81
85
 
82
86
  corrector.insert_after(block_begin, ' ') unless separating_space?
83
87
 
@@ -90,7 +94,7 @@ module RuboCop
90
94
  # `;`; joining everything with `,` would turn them into extra parameters
91
95
  # and change the lambda's arity.
92
96
  regular, shadow = arguments.children.partition { |arg| !arg.shadowarg_type? }
93
- arg_string = regular.map(&:source).join(', ')
97
+ arg_string = regular.map { |arg| argument_source(arg) }.join(', ')
94
98
  arg_string += "; #{shadow.map(&:source).join(', ')}" unless shadow.empty?
95
99
  arg_string
96
100
  end
@@ -140,6 +144,23 @@ module RuboCop
140
144
  def separating_space?
141
145
  block_begin.source_buffer.source[block_begin.begin_pos + 2].match?(/\s/)
142
146
  end
147
+
148
+ def argument_source(argument)
149
+ return argument.source unless argument.type?(:optarg, :kwoptarg)
150
+ return argument.source unless operator_expression?(argument.default_value)
151
+
152
+ default_value = argument.default_value
153
+ name = argument.source_range.begin.join(default_value.source_range.begin).source
154
+
155
+ "#{name}(#{default_value.source})"
156
+ end
157
+
158
+ def operator_expression?(node)
159
+ return true if node.type?(:range, :if, :and, :or)
160
+ return false unless node.call_type?
161
+
162
+ node.operator_method? && !node.method?(:[])
163
+ end
143
164
  end
144
165
  end
145
166
  end
@@ -21,14 +21,55 @@ module RuboCop
21
21
  corrector.insert_after(node.loc.end, ' ')
22
22
  end
23
23
 
24
+ def heredoc_strands_code_after_close_paren?(node)
25
+ return false unless heredoc_body_before_close_paren?(node)
26
+
27
+ after_paren = text_after_close_paren(node)
28
+
29
+ !after_paren.empty? && !after_paren.start_with?('.', '&.', ',')
30
+ end
31
+
24
32
  private
25
33
 
26
34
  # When the line above `)` ends with a comment and a chained call follows `)`,
27
35
  # crossing the newline would pull the chain into the comment. Preserve the newline.
36
+ # When a heredoc body comes before `)`, a chained call has to start on the line
37
+ # after the terminator instead, since the terminator must stay alone on its line.
28
38
  def remove_close_paren(corrector, node, buffer)
29
- newlines = !comment_above_close_paren_swallows_chain?(node, buffer)
30
- corrector.remove(range_with_surrounding_space(range: node.loc.end, buffer: buffer,
31
- side: :left, newlines: newlines))
39
+ if heredoc_above_close_paren_swallows_chain?(node)
40
+ range = range_with_surrounding_space(range: node.loc.end, buffer: buffer, side: :left)
41
+
42
+ corrector.replace(range, "\n")
43
+ else
44
+ newlines = !comment_above_close_paren_swallows_chain?(node, buffer)
45
+ range = range_with_surrounding_space(
46
+ range: node.loc.end, buffer: buffer, side: :left, newlines: newlines
47
+ )
48
+
49
+ corrector.remove(range)
50
+ end
51
+ end
52
+
53
+ def heredoc_above_close_paren_swallows_chain?(node)
54
+ return false unless heredoc_body_before_close_paren?(node)
55
+
56
+ text_after_close_paren(node).start_with?('.', '&.')
57
+ end
58
+
59
+ def heredoc_body_before_close_paren?(node)
60
+ return false unless (last_child = node.children.last)
61
+
62
+ close_paren_pos = node.loc.end.begin_pos
63
+
64
+ last_child.each_node(:any_str).any? do |str_node|
65
+ str_node.heredoc? && str_node.loc.heredoc_end.end_pos <= close_paren_pos
66
+ end
67
+ end
68
+
69
+ def text_after_close_paren(node)
70
+ close_paren = node.loc.end
71
+
72
+ close_paren.source_line[(close_paren.column + 1)..].to_s.lstrip
32
73
  end
33
74
 
34
75
  def comment_above_close_paren_swallows_chain?(node, buffer)
@@ -46,12 +87,8 @@ module RuboCop
46
87
  end
47
88
 
48
89
  def chained_after_close_paren?(node)
49
- close_paren = node.loc.end
50
- line_text = close_paren.source_line
51
- after_paren = line_text[(close_paren.column + 1)..]
52
- return false if after_paren.nil?
90
+ trimmed = text_after_close_paren(node)
53
91
 
54
- trimmed = after_paren.lstrip
55
92
  !trimmed.empty? && !trimmed.start_with?('#')
56
93
  end
57
94
 
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RuboCop
4
- module Cop # rubocop:disable Style/Documentation
4
+ module Cop # rubocop:disable Style/Documentation -- a namespace that only autoloads the correctors below
5
5
  # Autoloads corrector classes used by cops. Classes are autoloaded to reduce the number of
6
6
  # required classes because they're referenced only when autocorrection is performed.
7
7
 
8
- # rubocop:disable Layout/LineLength
8
+ # rubocop:disable Layout/LineLength -- the autoload paths do not wrap
9
9
  autoload :AlignmentCorrector, 'rubocop/cop/correctors/alignment_corrector'
10
10
  autoload :ConditionCorrector, 'rubocop/cop/correctors/condition_corrector'
11
11
  autoload :EachToForCorrector, 'rubocop/cop/correctors/each_to_for_corrector'
@@ -14,7 +14,7 @@ module RuboCop
14
14
  # @api private
15
15
  def url_for(cop_class, config = nil)
16
16
  base = department_to_basename(cop_class.department)
17
- fragment = cop_class.cop_name.downcase.gsub(/[^a-z]/, '')
17
+ fragment = cop_class.cop_name.downcase.delete('/')
18
18
  base_url = base_url_for(cop_class, config)
19
19
  extension = extension_for(cop_class, config)
20
20
 
@@ -109,12 +109,6 @@ module RuboCop
109
109
  end
110
110
  end
111
111
 
112
- def match_block_variable_name?(receiver_name)
113
- gem_specification(processed_source.ast) do |block_variable_name|
114
- return block_variable_name == receiver_name
115
- end
116
- end
117
-
118
112
  def add_dependency_method?(method_name)
119
113
  ADD_DEPENDENCY_METHODS.include?(method_name)
120
114
  end
@@ -80,12 +80,6 @@ module RuboCop
80
80
 
81
81
  private
82
82
 
83
- def previous_declaration(node)
84
- declarations = dependency_declarations(processed_source.ast)
85
- node_index = declarations.find_index(node)
86
- declarations.to_a[node_index - 1]
87
- end
88
-
89
83
  def get_dependency_name(node)
90
84
  node.method_name
91
85
  end
@@ -92,7 +92,7 @@ module RuboCop
92
92
  (str "true")
93
93
  PATTERN
94
94
 
95
- def on_block(node) # rubocop:disable Metrics/MethodLength, InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
95
+ def on_block(node) # rubocop:disable Metrics/MethodLength, InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler -- the gemspec block is matched by its named block variable
96
96
  gem_specification(node) do |block_var|
97
97
  metadata_value = metadata(node)
98
98
  mfa_value = mfa_value(metadata_value)
@@ -147,7 +147,7 @@ module RuboCop
147
147
  badge: badge,
148
148
  version_added: version_added)
149
149
 
150
- injector.inject do # rubocop:disable Lint/UnexpectedBlockArity
150
+ injector.inject do # rubocop:disable Lint/UnexpectedBlockArity -- this `inject` is the injector API, not `Enumerable#inject`
151
151
  output.puts(format(CONFIGURATION_ADDED_MESSAGE,
152
152
  configuration_file_path: config_file_path))
153
153
  end
@@ -201,11 +201,8 @@ module RuboCop
201
201
  def add_newline?(node)
202
202
  # Determine if a blank line should be inserted before the new directive
203
203
  # in order to spread out pattern matchers
204
- return false if node.sibling_index&.zero?
205
- return false unless node.parent
206
-
207
- prev_sibling = node.parent.child_nodes[node.sibling_index - 1]
208
- return false unless prev_sibling && pattern_matcher?(prev_sibling)
204
+ prev_sibling = node.left_sibling
205
+ return false unless prev_sibling.is_a?(AST::Node) && pattern_matcher?(prev_sibling)
209
206
 
210
207
  node.loc.line == last_line(prev_sibling) + 1
211
208
  end
@@ -46,13 +46,6 @@ module RuboCop
46
46
 
47
47
  private
48
48
 
49
- def arguments_range(node)
50
- range_between(
51
- node.first_argument.source_range.begin_pos,
52
- node.last_argument.source_range.end_pos
53
- )
54
- end
55
-
56
49
  def group_satisfied?(group_types, symbol_args)
57
50
  group_types.all? { |type| symbol_args.any? { |arg| arg.value == type } }
58
51
  end
@@ -75,7 +75,7 @@ module RuboCop
75
75
  @on_csend_definition = node if node.new_identifier.value == :on_csend
76
76
  end
77
77
 
78
- def on_send(node) # rubocop:disable InternalAffairs/OnSendWithoutOnCSend
78
+ def on_send(node) # rubocop:disable InternalAffairs/OnSendWithoutOnCSend -- this cop is what enforces the rule, and does not apply to itself
79
79
  return unless (new_identifier = node.first_argument)
80
80
  return unless new_identifier.basic_literal?
81
81
 
@@ -31,15 +31,9 @@ module RuboCop
31
31
  dup_arguments = arguments.dup
32
32
  dup_arguments.delete(config_node)
33
33
 
34
- corrector.replace(offense_range(arguments), dup_arguments.map(&:source).join(', '))
34
+ corrector.replace(arguments_range(node), dup_arguments.map(&:source).join(', '))
35
35
  end
36
36
  end
37
-
38
- private
39
-
40
- def offense_range(arguments)
41
- range_between(arguments.first.source_range.begin_pos, arguments.last.source_range.end_pos)
42
- end
43
37
  end
44
38
  end
45
39
  end
@@ -109,7 +109,7 @@ module RuboCop
109
109
  end
110
110
 
111
111
  def on_if(node)
112
- traverse_condition(node.condition) do |cond|
112
+ node.condition.each_node(:send) do |cond|
113
113
  add_offense(cond, message: MSG_FOR_CONDITIONAL_USE) if style_detected_api_used?(cond)
114
114
  end
115
115
  end
@@ -134,12 +134,6 @@ module RuboCop
134
134
  no_acceptable_style_check(node) ||
135
135
  style_detected_check(node)
136
136
  end
137
-
138
- def traverse_condition(condition, &block)
139
- yield condition if condition.send_type?
140
-
141
- condition.each_child_node { |child| traverse_condition(child, &block) }
142
- end
143
137
  end
144
138
  end
145
139
  end
@@ -81,6 +81,8 @@ module RuboCop
81
81
  last_arg = node.last_argument
82
82
 
83
83
  if last_arg.hash_type? && !last_arg.braces?
84
+ return items if enforce_hash_argument_with_separator?
85
+
84
86
  items += last_arg.pairs
85
87
  else
86
88
  items << last_arg
@@ -68,6 +68,7 @@ module RuboCop
68
68
  # end
69
69
  #
70
70
  class BlockAlignment < Base
71
+ include Alignment
71
72
  include ConfigurableEnforcedStyle
72
73
  include RangeHelp
73
74
  extend AutoCorrector
@@ -154,6 +155,32 @@ module RuboCop
154
155
  else
155
156
  start_for_line_node(node)
156
157
  end
158
+
159
+ if using_tabs? && (indentation = anchor_indentation(ancestor_node, node))
160
+ replace_end_indentation(corrector, node, indentation)
161
+ else
162
+ shift_end_to_start_col(corrector, ancestor_node, node)
163
+ end
164
+ end
165
+
166
+ def anchor_indentation(ancestor_node, node)
167
+ if style == :start_of_block
168
+ do_line_anchor_loc(node, node.loc.begin).source_line[/\A\s*/]
169
+ else
170
+ anchor_loc = (ancestor_node || node).source_range
171
+ anchor_loc.source_line[/\A\s*/] if begins_its_line?(anchor_loc)
172
+ end
173
+ end
174
+
175
+ def replace_end_indentation(corrector, node, indentation)
176
+ loc_end = node.loc.end
177
+ whitespace = range_between(loc_end.begin_pos - loc_end.column, loc_end.begin_pos)
178
+ return if whitespace.source == indentation
179
+
180
+ corrector.replace(whitespace, indentation)
181
+ end
182
+
183
+ def shift_end_to_start_col(corrector, ancestor_node, node)
157
184
  start_col = compute_start_col(ancestor_node, node)
158
185
  loc_end = node.loc.end
159
186
  delta = start_col - loc_end.column