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
@@ -17,27 +17,31 @@ module RuboCop
17
17
  # Runtime for Language Server Protocol of RuboCop.
18
18
  # @api private
19
19
  class Runtime
20
- attr_writer :safe_autocorrect, :lint_mode, :layout_mode
20
+ attr_writer :safe_autocorrect, :lint_mode, :layout_mode, :raise_cop_error
21
21
 
22
- def initialize(config_store)
23
- RuboCop::LSP.enable
22
+ # Without `lsp_mode`, offenses keep the locations and messages the
23
+ # command line gives them, rather than the ones meant for an editor.
24
+ def initialize(config_store, lsp_mode: true)
25
+ RuboCop::LSP.enable if lsp_mode
24
26
 
25
27
  @runner = RuboCop::Lsp::StdinRunner.new(config_store)
26
28
 
27
29
  @safe_autocorrect = true
28
30
  @lint_mode = false
29
31
  @layout_mode = false
32
+ @raise_cop_error = true
30
33
  end
31
34
 
32
- def format(path, text, command:, prism_result: nil)
35
+ def format(path, text, command:, prism_result: nil, options: {})
33
36
  safe_autocorrect = if command
34
37
  command == 'rubocop.formatAutocorrects'
35
38
  else
36
39
  @safe_autocorrect
37
40
  end
38
41
 
39
- formatting_options = { autocorrect: true, safe_autocorrect: safe_autocorrect }
40
- formatting_options[:only] = config_only_options if @lint_mode || @layout_mode
42
+ formatting_options = {
43
+ autocorrect: true, safe_autocorrect: safe_autocorrect, raise_cop_error: @raise_cop_error
44
+ }.merge(call_options(options))
41
45
 
42
46
  @runner.run(path, text, formatting_options, prism_result: prism_result)
43
47
  @runner.formatted_source
@@ -48,17 +52,39 @@ module RuboCop
48
52
  end
49
53
 
50
54
  def offenses(path, text, document_encoding = nil, prism_result: nil)
51
- diagnostic_options = {}
52
- diagnostic_options[:only] = config_only_options if @lint_mode || @layout_mode
53
-
54
- @runner.run(path, text, diagnostic_options, prism_result: prism_result)
55
+ found = raw_offenses(path, text, prism_result: prism_result)
55
56
  processed_source = @runner.processed_source
56
57
  config = @runner.config_for_working_directory
57
- @runner.offenses.map do |offense|
58
+ found.map do |offense|
58
59
  build_diagnostic(offense, path, document_encoding, processed_source, config)
59
60
  end
60
61
  end
61
62
 
63
+ # The offenses before they are converted to LSP diagnostics, for callers
64
+ # that do not speak LSP.
65
+ def raw_offenses(path, text, prism_result: nil, options: {})
66
+ diagnostic_options = { raise_cop_error: @raise_cop_error }.merge(call_options(options))
67
+
68
+ @runner.run(path, text, diagnostic_options, prism_result: prism_result)
69
+ @runner.offenses
70
+ end
71
+
72
+ # The offenses the last `format` found but didn't correct, located in the
73
+ # source it returned.
74
+ def uncorrected_offenses
75
+ @runner.offenses.reject(&:corrected?)
76
+ end
77
+
78
+ # What went wrong in the last run. Cop errors only end up here when
79
+ # `raise_cop_error` is off; otherwise they are raised.
80
+ def errors
81
+ @runner.errors
82
+ end
83
+
84
+ def warnings
85
+ @runner.warnings
86
+ end
87
+
62
88
  private
63
89
 
64
90
  def build_diagnostic(offense, path, document_encoding, processed_source, config)
@@ -71,6 +97,16 @@ module RuboCop
71
97
  ).to_lsp_diagnostic(config)
72
98
  end
73
99
 
100
+ # What a caller adds for one call, such as `only` and `except` to select
101
+ # the cops, or `editing` to hold back the corrections of cops with
102
+ # `AutoCorrect: contextual`, as LSP mode does. Without `only`, the cops
103
+ # are the departments a lint or layout mode limits the server to.
104
+ def call_options(options)
105
+ return options if options[:only] || !(@lint_mode || @layout_mode)
106
+
107
+ options.merge(only: config_only_options)
108
+ end
109
+
74
110
  def config_only_options
75
111
  only_options = []
76
112
  only_options << 'Lint' if @lint_mode
@@ -23,7 +23,6 @@ module RuboCop
23
23
  stderr: true,
24
24
  force_exclusion: true,
25
25
  formatters: ['RuboCop::Formatter::BaseFormatter'],
26
- raise_cop_error: true,
27
26
  todo_file: nil,
28
27
  todo_ignore_files: []
29
28
  }.freeze
@@ -41,6 +40,13 @@ module RuboCop
41
40
  end
42
41
 
43
42
  def run(path, contents, options, prism_result: nil)
43
+ # The cops mobilized for a configuration are cached, which only holds while
44
+ # every run asks for the same ones.
45
+ selection = options.values_at(:only, :except)
46
+ unless selection == @cop_selection
47
+ forget_mobilized_cops
48
+ @cop_selection = selection
49
+ end
44
50
  @options = options.merge(DEFAULT_RUBOCOP_OPTIONS)
45
51
  @options[:stdin] = contents
46
52
 
@@ -86,10 +92,10 @@ module RuboCop
86
92
  @offenses = offenses
87
93
  end
88
94
 
89
- def do_inspection_loop(file)
90
- source, offenses = super
91
- @processed_source = source
92
- [source, offenses]
95
+ def do_inspection_loop(file, source = nil)
96
+ processed_source, offenses = super
97
+ @processed_source = processed_source
98
+ [processed_source, offenses]
93
99
  end
94
100
  end
95
101
  end
data/lib/rubocop/lsp.rb CHANGED
@@ -26,8 +26,11 @@ module RuboCop
26
26
  if block
27
27
  original = @enabled
28
28
  @enabled = false
29
- yield
30
- @enabled = original
29
+ begin
30
+ yield
31
+ ensure
32
+ @enabled = original
33
+ end
31
34
  else
32
35
  @enabled = false
33
36
  end
@@ -12,6 +12,7 @@ module RuboCop
12
12
  encoding: '(?:en)?coding',
13
13
  frozen_string_literal: 'frozen[_-]string[_-]literal',
14
14
  rbs_inline: 'rbs_inline',
15
+ warn_indent: 'warn[_-]indent',
15
16
  shareable_constant_value: 'shareable[_-]constant[_-]value',
16
17
  typed: 'typed'
17
18
  }.freeze
@@ -38,6 +39,7 @@ module RuboCop
38
39
  frozen_string_literal_specified? ||
39
40
  encoding_specified? ||
40
41
  rbs_inline_specified? ||
42
+ warn_indent_specified? ||
41
43
  shareable_constant_value_specified? ||
42
44
  typed_specified?
43
45
  end
@@ -77,6 +79,13 @@ module RuboCop
77
79
  specified?(frozen_string_literal)
78
80
  end
79
81
 
82
+ # Was a warn_indent specified?
83
+ #
84
+ # @return [Boolean]
85
+ def warn_indent_specified?
86
+ specified?(warn_indent)
87
+ end
88
+
80
89
  # Was a shareable_constant_value specified?
81
90
  #
82
91
  # @return [Boolean]
@@ -100,6 +109,13 @@ module RuboCop
100
109
  end
101
110
  end
102
111
 
112
+ # Expose the `warn_indent` value.
113
+ #
114
+ # @return [String] for warn_indent config
115
+ def warn_indent
116
+ extract_warn_indent
117
+ end
118
+
103
119
  # Expose the `shareable_constant_value` value coerced to a boolean if possible.
104
120
  #
105
121
  # @return [String] for shareable_constant_value config
@@ -216,6 +232,10 @@ module RuboCop
216
232
  # Emacs comments cannot specify RBS::inline behavior.
217
233
  def extract_rbs_inline_value; end
218
234
 
235
+ def extract_warn_indent
236
+ match(KEYWORDS[:warn_indent])
237
+ end
238
+
219
239
  def extract_shareable_constant_value
220
240
  match(KEYWORDS[:shareable_constant_value])
221
241
  end
@@ -258,6 +278,9 @@ module RuboCop
258
278
  # Vim comments cannot specify RBS::inline behavior.
259
279
  def extract_rbs_inline_value; end
260
280
 
281
+ # Vim comments cannot specify indentation warning behavior.
282
+ def warn_indent; end
283
+
261
284
  # Vim comments cannot specify shareable constant values behavior.
262
285
  def shareable_constant_value; end
263
286
 
@@ -316,6 +339,10 @@ module RuboCop
316
339
  extract(/\A\s*#\s*#{KEYWORDS[:rbs_inline]}:\s*#{TOKEN}\s*\z/io)
317
340
  end
318
341
 
342
+ def extract_warn_indent
343
+ extract(/\A\s*#\s*#{KEYWORDS[:warn_indent]}:\s*#{TOKEN}\s*\z/io)
344
+ end
345
+
319
346
  def extract_shareable_constant_value
320
347
  extract(/\A\s*#\s*#{KEYWORDS[:shareable_constant_value]}:\s*#{TOKEN}\s*\z/io)
321
348
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module MCP
5
+ # What an autocorrection request asks for, shared by every file it corrects.
6
+ # The keywords are the tool's arguments, so an unknown one is refused.
7
+ # @api private
8
+ class AutocorrectionRequest
9
+ PROPERTIES = {
10
+ safety: { type: 'boolean' },
11
+ contextual: { type: 'boolean' },
12
+ dry_run: { type: 'boolean' },
13
+ max_offenses_per_cop: { type: 'integer', minimum: 1 }
14
+ }.freeze
15
+
16
+ # The offenses left over come back after the files are written, so they're
17
+ # capped unless the agent asks otherwise: a result too big for the client
18
+ # would hide what was done.
19
+ DEFAULT_MAX_OFFENSES_PER_COP = 5
20
+
21
+ attr_reader :scope, :limit
22
+
23
+ def initialize(scope, safety: true, contextual: false, dry_run: false,
24
+ max_offenses_per_cop: DEFAULT_MAX_OFFENSES_PER_COP)
25
+ @scope = scope
26
+ @safety = safety
27
+ @contextual = contextual
28
+ @dry_run = dry_run
29
+ @limit = OffenseLimit.new(max_offenses_per_cop)
30
+ end
31
+
32
+ def dry_run?
33
+ @dry_run
34
+ end
35
+
36
+ # The options `LSP::Runtime#format` takes to apply the corrections
37
+ # `safety` and `contextual` allow, from the cops the scope selects.
38
+ def format_options
39
+ command = @safety ? 'rubocop.formatAutocorrects' : 'rubocop.formatAutocorrectsAll'
40
+ { command: command, options: runtime_options }
41
+ end
42
+
43
+ # The options the LSP runtime takes for each call the request makes.
44
+ # Cops with `AutoCorrect: contextual` hold back corrections that get in
45
+ # the way of code still being written, such as removing a variable that
46
+ # is about to be used, so an agent that is done editing has to ask for them.
47
+ def runtime_options
48
+ { **@scope.cop_options, editing: !@contextual }
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module MCP
5
+ # The `rubocop_explain` tool: what cops do, as `--explain` prints it.
6
+ # @api private
7
+ class ExplainTool
8
+ include RuboCop::CLI::Command::CopNames
9
+
10
+ DESCRIPTION =
11
+ 'Explain what cops do: the problem each one targets, bad and good examples, ' \
12
+ 'its configuration as this project resolves it, and whether it autocorrects. ' \
13
+ 'Takes cop names as offenses report them, such as `Style/StringLiterals`. ' \
14
+ 'Pass the `path` of a file to see the configuration that applies to it.'
15
+ INPUT_SCHEMA = {
16
+ properties: {
17
+ cop_names: { type: 'array', items: { type: 'string' }, minItems: 1 },
18
+ path: { type: 'string' }
19
+ },
20
+ required: ['cop_names'],
21
+ additionalProperties: false
22
+ }.freeze
23
+ ANNOTATIONS = {
24
+ title: "RuboCop's cop explanation", destructive_hint: false, idempotent_hint: true,
25
+ open_world_hint: false, read_only_hint: true
26
+ }.freeze
27
+
28
+ def initialize(config_store)
29
+ @config_store = config_store
30
+ end
31
+
32
+ # The tool's block does not run in this instance, so it reaches
33
+ # `explain` through a method object it closes over.
34
+ def to_tool
35
+ explain = method(:explain)
36
+
37
+ ::MCP::Tool.define(
38
+ name: 'rubocop_explain',
39
+ description: DESCRIPTION,
40
+ input_schema: INPUT_SCHEMA,
41
+ annotations: ANNOTATIONS
42
+ ) do |cop_names:, path: nil|
43
+ ::MCP::Tool::Response.new([{ type: 'text', text: explain.call(cop_names, path) }])
44
+ rescue RuboCop::Error => e
45
+ ::MCP::Tool::Response.new([{ type: 'text', text: e.message }], error: true)
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ # Explains the cops it recognizes and then names the ones it does not, as
52
+ # `--explain` does, so a typo does not cost the explanations asked for
53
+ # alongside it. The configuration is loaded first, since that is what
54
+ # registers the cops of any plugins it names.
55
+ def explain(cop_names, path)
56
+ config = path ? @config_store.for_file(path) : @config_store.for_pwd
57
+ explanations = known_cop_classes(cop_names).map do |cop_class|
58
+ Cop::CopExplanation.new(cop_class, config).to_s
59
+ end
60
+ validate_cop_names!(cop_names)
61
+
62
+ explanations.join("\n\n")
63
+ rescue IncorrectCopNameError => e
64
+ raise RuboCop::Error, [*explanations, e.message].join("\n\n")
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module MCP
5
+ # What a request asks RuboCop to look at: which cops run, as `--only` and
6
+ # `--except` choose them, and, when checking files, whether to stick to the
7
+ # ones git says changed, as `--changed` does.
8
+ # @api private
9
+ class Scope
10
+ PROPERTIES = {
11
+ only: {
12
+ type: 'array', items: { type: 'string' },
13
+ description: 'Run only these cops or departments, ' \
14
+ 'such as `Style/StringLiterals` or `Lint`.'
15
+ },
16
+ except: {
17
+ type: 'array', items: { type: 'string' },
18
+ description: 'Run every cop except these cops or departments.'
19
+ },
20
+ changed: {
21
+ type: %w[boolean string],
22
+ description: 'Check only the files git says changed: `true` for changes since the ' \
23
+ 'last commit, or a revision to compare against, such as `main`.'
24
+ }
25
+ }.freeze
26
+
27
+ # An empty list would select no cops at all rather than leave the choice
28
+ # alone, so it counts as no list.
29
+ #
30
+ # @raise [RuboCop::Error] for a scope the command line would refuse
31
+ def initialize(inline:, only: nil, except: nil, changed: nil)
32
+ @only = only if only&.any?
33
+ @except = except if except&.any?
34
+ @changed = changed == true ? ChangedFiles::DEFAULT_REVISION : changed
35
+ validate(inline)
36
+ end
37
+
38
+ # @return [Hash] the cop selection, among the options the LSP runtime takes
39
+ def cop_options
40
+ { only: @only, except: @except }.compact
41
+ end
42
+
43
+ def select_files(files)
44
+ @changed ? ChangedFiles.new(@changed).filter(files) : files
45
+ end
46
+
47
+ private
48
+
49
+ # Refuses what the command line refuses for the same options. Cop names
50
+ # are left to the runner, which checks them the way `--only` does: short
51
+ # names allowed, and only once the configuration naming a plugin's cops
52
+ # has been loaded.
53
+ def validate(inline)
54
+ raise Error, '`changed` only applies when checking files.' if inline && @changed
55
+ raise Error, '`changed` takes `true` or a git revision.' if @changed == ''
56
+
57
+ validator = OptionsValidator.new(cop_options)
58
+ if validator.only_includes_redundant_disable?
59
+ raise Error, 'Lint/RedundantCopDisableDirective cannot be used with `only`.'
60
+ end
61
+ raise Error, 'Syntax checking cannot be turned off.' if validator.except_syntax?
62
+ end
63
+ end
64
+ end
65
+ end