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
@@ -26,7 +26,8 @@ module RuboCop
26
26
  # @api private
27
27
  PUSH_POP_ARGS_PATTERN = "([+\\-]#{COP_NAME_PATTERN_NC}(?:\\s+[+\\-]#{COP_NAME_PATTERN_NC})*)"
28
28
  # @api private
29
- AVAILABLE_MODES = %w[disable enable todo push pop disable-next todo-next].freeze
29
+ AVAILABLE_MODES = %w[disable enable todo push pop disable-next todo-next enable-next
30
+ next].freeze
30
31
  # @api private
31
32
  # Longest first, so a `-next` mode is not matched as its prefix
32
33
  # (`-` is a word boundary).
@@ -43,6 +44,8 @@ module RuboCop
43
44
  .gsub(' ', '\s*')
44
45
  )
45
46
  # @api private
47
+ SIGNED_OPERATIONS = %w[+ -].freeze
48
+ # @api private
46
49
  TRAILING_COMMENT_MARKER = '--'
47
50
  # @api private
48
51
  MALFORMED_DIRECTIVE_WITHOUT_COP_NAME_REGEXP = Regexp.new(
@@ -71,7 +74,7 @@ module RuboCop
71
74
  # Checks if the comment is malformed as a `# rubocop:` directive
72
75
  def malformed?
73
76
  return true if !start_with_marker? || @match_data.nil?
74
- return true if missing_cop_name?
77
+ return true if missing_cop_name? || invalid_signed_args?
75
78
 
76
79
  tail = @match_data.post_match.lstrip
77
80
  !(tail.empty? || tail.start_with?(TRAILING_COMMENT_MARKER))
@@ -84,6 +87,16 @@ module RuboCop
84
87
  MALFORMED_DIRECTIVE_WITHOUT_COP_NAME_REGEXP.match?(comment.text)
85
88
  end
86
89
 
90
+ # `push` and `next` arguments must be `+`/`-` prefixed cop names, and
91
+ # `pop` takes no arguments at all.
92
+ def invalid_signed_args?
93
+ return cops ? !cops.empty? : false if pop?
94
+ return false unless push? || next?
95
+ return false unless cops
96
+
97
+ cops.split.any? { |cop_spec| !cop_spec.start_with?('+', '-') }
98
+ end
99
+
87
100
  # The text of the directive's optional `--` trailing comment, or `nil`
88
101
  # when there is none.
89
102
  def reason
@@ -150,7 +163,12 @@ module RuboCop
150
163
 
151
164
  # Checks if this directive enables cops
152
165
  def enabled?
153
- mode == 'enable'
166
+ mode == 'enable' || enable_next?
167
+ end
168
+
169
+ # Checks if this directive enables cops for the next statement only
170
+ def enable_next?
171
+ mode == 'enable-next'
154
172
  end
155
173
 
156
174
  # Checks if this directive is a push
@@ -163,10 +181,17 @@ module RuboCop
163
181
  mode == 'pop'
164
182
  end
165
183
 
166
- # Returns the push arguments as a hash of cop names with their operations
167
- def push_args
168
- @push_args ||= parse_push_args
184
+ # Checks if this directive toggles cops for the next statement only
185
+ def next?
186
+ mode == 'next'
187
+ end
188
+
189
+ # Returns the `+`/`-` arguments of a `push` or `next` directive as a hash
190
+ # of operations to cop names
191
+ def signed_args
192
+ @signed_args ||= parse_signed_args
169
193
  end
194
+ alias push_args signed_args
170
195
 
171
196
  # Checks if this directive enables all cops
172
197
  def enabled_all?
@@ -210,6 +235,8 @@ module RuboCop
210
235
  end
211
236
 
212
237
  def directive_count
238
+ return signed_args.values.sum(&:count) if push? || next?
239
+
213
240
  raw_cop_names.count
214
241
  end
215
242
 
@@ -244,13 +271,15 @@ module RuboCop
244
271
  cops - [LINT_REDUNDANT_DIRECTIVE_COP, LINT_SYNTAX_COP]
245
272
  end
246
273
 
247
- def parse_push_args
248
- return {} unless push? && cops
274
+ def parse_signed_args
275
+ return {} unless (push? || next?) && cops
249
276
 
250
277
  args = {}
251
278
  cops.split.each do |cop_spec|
252
279
  op = cop_spec[0]
253
280
  cop_name = cop_spec[1..]
281
+ next unless SIGNED_OPERATIONS.include?(op)
282
+
254
283
  args[op] ||= []
255
284
  args[op] << cop_name
256
285
  end
@@ -35,6 +35,7 @@ module RuboCop
35
35
  # * `#file_started`
36
36
  # * `#file_finished`
37
37
  # * `#file_started`
38
+ # * `#file_problems` (only when a cop crashed or warned on the file)
38
39
  # * `#file_finished`
39
40
  # * `#finished`
40
41
  #
@@ -88,6 +89,27 @@ module RuboCop
88
89
  # @return [void]
89
90
  def file_started(file, options); end
90
91
 
92
+ # @api public
93
+ #
94
+ # Invoked before `#file_finished` for a file that a cop crashed on or
95
+ # raised a warning for, or whose corrections went into an infinite loop.
96
+ # Optional: a formatter implementing the API by duck typing is skipped
97
+ # when it doesn't define it.
98
+ #
99
+ # @param file [String]
100
+ # the file path
101
+ #
102
+ # @param errors [Array(String)]
103
+ # the error messages as they're printed, without colors. A cop that
104
+ # crashes on every node of a kind has a message for each, naming the
105
+ # line and column.
106
+ #
107
+ # @param warnings [Array(String)]
108
+ # the warning messages, in the same form
109
+ #
110
+ # @return [void]
111
+ def file_problems(file, errors, warnings); end
112
+
91
113
  # @api public
92
114
  #
93
115
  # Invoked at the end of inspecting each files.
@@ -23,6 +23,7 @@ module RuboCop
23
23
  Enabled
24
24
  Exclude
25
25
  Include
26
+ Preview
26
27
  Reference
27
28
  References
28
29
  Safe
@@ -93,6 +94,7 @@ module RuboCop
93
94
  command += ' --auto-gen-only-exclude' if @options[:auto_gen_only_exclude]
94
95
  command += ' --disable-pending-cops' if @options[:disable_pending_cops]
95
96
  command += ' --enable-pending-cops' if @options[:enable_pending_cops]
97
+ command += preview_option
96
98
  command += exclude_limit_option
97
99
  command += ' --no-offense-counts' unless show_offense_counts?
98
100
 
@@ -103,6 +105,14 @@ module RuboCop
103
105
  command
104
106
  end
105
107
 
108
+ def preview_option
109
+ case @options[:preview]
110
+ when true then ' --preview'
111
+ when false then ' --no-preview'
112
+ else ''
113
+ end
114
+ end
115
+
106
116
  def exclude_limit_option
107
117
  if no_exclude_limit?
108
118
  ' --no-exclude-limit'
@@ -259,7 +269,7 @@ module RuboCop
259
269
  config = ConfigStore.new.for(parent)
260
270
  cfg = config[cop_name] || {}
261
271
 
262
- if merge_mode_for_exclude?(config) || merge_mode_for_exclude?(cfg)
272
+ if exclude_merges?(config, cfg)
263
273
  offending_files
264
274
  else
265
275
  cop_exclude = cfg['Exclude']
@@ -272,6 +282,14 @@ module RuboCop
272
282
  end
273
283
  end
274
284
 
285
+ # `Preview` merges `Exclude` with the default configuration, so the excludes
286
+ # already in the configuration do not have to be copied into the todo file.
287
+ def exclude_merges?(config, cop_config)
288
+ merge_mode_for_exclude?(config) ||
289
+ merge_mode_for_exclude?(cop_config) ||
290
+ config.preview?(@options)
291
+ end
292
+
275
293
  def merge_mode_for_exclude?(cfg)
276
294
  Array(cfg.to_h.dig('inherit_mode', 'merge')).include?('Exclude')
277
295
  end
@@ -23,6 +23,7 @@ module RuboCop
23
23
  '[pa]cman' => 'PacmanFormatter',
24
24
  '[p]rogress' => 'ProgressFormatter',
25
25
  '[q]uiet' => 'QuietFormatter',
26
+ '[sa]rif' => 'SARIFFormatter',
26
27
  '[s]imple' => 'SimpleTextFormatter',
27
28
  '[t]ap' => 'TapFormatter',
28
29
  '[w]orst' => 'WorstOffendersFormatter'
@@ -48,6 +49,10 @@ module RuboCop
48
49
  each { |f| f.file_started(file, options) }
49
50
  end
50
51
 
52
+ def file_problems(file, errors, warnings)
53
+ each { |f| f.file_problems(file, errors, warnings) if f.respond_to?(:file_problems) }
54
+ end
55
+
51
56
  def file_finished(file, offenses)
52
57
  each { |f| f.file_finished(file, offenses) }
53
58
  offenses
@@ -57,7 +62,7 @@ module RuboCop
57
62
  if output_path
58
63
  dir_path = File.dirname(output_path)
59
64
  FileUtils.mkdir_p(dir_path)
60
- output = File.open(output_path, 'w') # rubocop:disable Style/FileOpen
65
+ output = File.open(output_path, 'w') # rubocop:disable Style/FileOpen -- the file is closed by `close_output_files`, not by a block
61
66
  else
62
67
  output = $stdout
63
68
  end
@@ -15,6 +15,10 @@ module RuboCop
15
15
  @offenses_for_files[file] = offenses unless offenses.empty?
16
16
  end
17
17
 
18
+ def file_started(_file, options)
19
+ @config_store = options[:config_store]
20
+ end
21
+
18
22
  def finished(_inspected_files)
19
23
  @offenses_for_files.each do |file, offenses|
20
24
  offenses.each do |offense|
@@ -31,11 +35,8 @@ module RuboCop
31
35
  end
32
36
 
33
37
  def minimum_severity_to_fail
34
- @minimum_severity_to_fail ||= begin
35
- # Unless given explicitly as `fail_level`, `:info` severity offenses do not fail
36
- name = options.fetch(:fail_level, :refactor)
37
- RuboCop::Cop::Severity.new(name)
38
- end
38
+ @minimum_severity_to_fail ||=
39
+ RuboCop::Cop::Severity.minimum_to_fail(options, @config_store&.for_pwd)
39
40
  end
40
41
 
41
42
  def github_severity(offense)
@@ -74,7 +74,7 @@ module RuboCop
74
74
  end
75
75
 
76
76
  # Make Kernel#binding public.
77
- # rubocop:disable-next Lint/UselessMethodDefinition
77
+ # rubocop:disable-next Lint/UselessMethodDefinition -- redefined only to make `Kernel#binding` public
78
78
  def binding
79
79
  super
80
80
  end
@@ -143,7 +143,7 @@ module RuboCop
143
143
  }.freeze
144
144
 
145
145
  # Make Kernel#binding public.
146
- # rubocop:disable-next Lint/UselessMethodDefinition
146
+ # rubocop:disable-next Lint/UselessMethodDefinition -- redefined only to make `Kernel#binding` public
147
147
  def binding
148
148
  super
149
149
  end
@@ -13,19 +13,29 @@ module RuboCop
13
13
  def initialize(output, options = {})
14
14
  super
15
15
  @output_hash = { metadata: metadata_hash, files: [], summary: { offense_count: 0 } }
16
+ @problems = {}
16
17
  end
17
18
 
18
19
  def started(target_files)
19
20
  output_hash[:summary][:target_file_count] = target_files.count
20
21
  end
21
22
 
23
+ def file_problems(file, errors, warnings)
24
+ @problems[file] = hash_for_problems(file, errors, warnings)
25
+ end
26
+
22
27
  def file_finished(file, offenses)
23
- output_hash[:files] << hash_for_file(file, offenses)
28
+ entry = hash_for_file(file, offenses)
29
+ problems = @problems.delete(file)
30
+ entry.merge!(problems) if problems
31
+
32
+ output_hash[:files] << entry
24
33
  output_hash[:summary][:offense_count] += offenses.count
25
34
  end
26
35
 
27
36
  def finished(inspected_files)
28
37
  output_hash[:summary][:inspected_file_count] = inspected_files.count
38
+ output_hash[:summary].merge!(problem_counts(output_hash[:files]))
29
39
  output.write output_hash.to_json
30
40
  end
31
41
 
@@ -46,6 +56,32 @@ module RuboCop
46
56
  }
47
57
  end
48
58
 
59
+ # A cop that crashes fails once per node it visits, so messages that
60
+ # differ only in their line and column are listed once, at the first place
61
+ # they happened. They name the file the way its entry does. The keys are
62
+ # absent when there's nothing to report, which keeps them additive for
63
+ # existing consumers.
64
+ def hash_for_problems(file, errors, warnings)
65
+ return {} if errors.empty? && warnings.empty?
66
+
67
+ path = smart_path(file)
68
+ location = /#{Regexp.escape(File.expand_path(file))}((?::\d+)*)/
69
+
70
+ { errors: errors, warnings: warnings }.filter_map do |key, messages|
71
+ next if messages.empty?
72
+
73
+ messages = messages.uniq { |message| message.gsub(location, '') }
74
+ [key, messages.map { |message| message.gsub(location) { path + Regexp.last_match(1) } }]
75
+ end.to_h
76
+ end
77
+
78
+ def problem_counts(files)
79
+ {
80
+ error_count: files.sum { |f| f[:errors]&.size.to_i },
81
+ warning_count: files.sum { |f| f[:warnings]&.size.to_i }
82
+ }.reject { |_, count| count.zero? }
83
+ end
84
+
49
85
  def hash_for_offense(offense)
50
86
  hash = {
51
87
  severity: offense.severity.name,
@@ -63,9 +99,43 @@ module RuboCop
63
99
  hash[:justification] = offense.justification
64
100
  end
65
101
 
102
+ # The edits autocorrection would make, so a consumer can apply or review
103
+ # them without running autocorrection itself. Additive, and absent when
104
+ # the offense has no correction.
105
+ hash[:correction] = correction_for(offense) unless offense.corrections.empty?
106
+
66
107
  hash
67
108
  end
68
109
 
110
+ def correction_for(offense)
111
+ { safe: offense.correction_safe, edits: edits_for(offense) }
112
+ end
113
+
114
+ def edits_for(offense)
115
+ buffer = offense.location.source_buffer
116
+
117
+ offense.corrections.map do |correction|
118
+ # Built as a range so the columns follow the same 1-based convention
119
+ # as `hash_for_location`, end-exclusive column included.
120
+ range = ::Parser::Source::Range.new(buffer, correction.begin_pos, correction.end_pos)
121
+
122
+ hash_for_edit_range(range).merge!(
123
+ begin_pos: correction.begin_pos,
124
+ end_pos: correction.end_pos,
125
+ replacement: correction.replacement
126
+ )
127
+ end
128
+ end
129
+
130
+ def hash_for_edit_range(range)
131
+ {
132
+ start_line: range.line,
133
+ start_column: range.column + 1,
134
+ last_line: range.last_line,
135
+ last_column: range.last_column.zero? ? 1 : range.last_column
136
+ }
137
+ end
138
+
69
139
  def hash_for_location(offense)
70
140
  {
71
141
  start_line: offense.line,
@@ -47,7 +47,7 @@ module RuboCop
47
47
  end
48
48
  end
49
49
 
50
- # rubocop:disable-next Layout/LineLength,Metrics/AbcSize,Metrics/MethodLength
50
+ # rubocop:disable-next Layout/LineLength,Metrics/AbcSize,Metrics/MethodLength -- the XML is emitted line by line
51
51
  def finished(_inspected_files)
52
52
  output.puts %(<?xml version='1.0'?>)
53
53
  output.puts %(<testsuites>)
@@ -14,6 +14,8 @@ module RuboCop
14
14
  # --
15
15
  # 29 Total in 5 files
16
16
  class OffenseCountFormatter < BaseFormatter
17
+ include TextUtil
18
+
17
19
  attr_reader :offense_counts
18
20
 
19
21
  def started(target_files)
@@ -64,7 +66,7 @@ module RuboCop
64
66
  output.puts "#{count.to_s.ljust(column_width)}#{cop_information(cop_name)}"
65
67
  end
66
68
  output.puts '--'
67
- output.puts "#{total_count} Total in #{offending_files_count} files"
69
+ output.puts "#{total_count} Total in #{pluralize(offending_files_count, 'file')}"
68
70
 
69
71
  output.puts
70
72
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'io/console'
4
+
3
5
  module RuboCop
4
6
  module Formatter
5
7
  # This formatter prints a PACDOT per every file to be analyzed.
@@ -50,7 +52,7 @@ module RuboCop
50
52
 
51
53
  def cols
52
54
  @cols ||= begin
53
- _height, width = $stdout.winsize
55
+ _height, width = output.winsize if output.tty?
54
56
  width.nil? || width.zero? ? FALLBACK_TERMINAL_WIDTH : width
55
57
  end
56
58
  end
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest'
4
+
5
+ module RuboCop
6
+ module Formatter
7
+ # This formatter formats the report data in SARIF 2.1.0, the OASIS standard
8
+ # for static analysis results. It's understood by GitHub code scanning,
9
+ # Azure DevOps, and other tools that aggregate analyzer output.
10
+ #
11
+ # SARIF only knows three severity levels, so RuboCop's `info`, `refactor`
12
+ # and `convention` severities are all reported as `note`.
13
+ class SARIFFormatter < BaseFormatter
14
+ SCHEMA_URI = 'https://json.schemastore.org/sarif-2.1.0.json'
15
+ SARIF_VERSION = '2.1.0'
16
+ INFORMATION_URI = 'https://rubocop.org'
17
+
18
+ SEVERITY_LEVELS = {
19
+ info: 'note',
20
+ refactor: 'note',
21
+ convention: 'note',
22
+ warning: 'warning',
23
+ error: 'error',
24
+ fatal: 'error'
25
+ }.freeze
26
+
27
+ def initialize(output, options = {})
28
+ super
29
+
30
+ @rules = []
31
+ @rule_indexes = {}
32
+ @results = []
33
+ @config_store = nil
34
+ end
35
+
36
+ def file_started(_file, options)
37
+ @config_store = options[:config_store]
38
+ end
39
+
40
+ def file_finished(file, offenses)
41
+ line_hash_counts = Hash.new(0)
42
+ offenses.each do |offense|
43
+ @results << result_for(file, offense, line_hash_counts)
44
+ end
45
+ end
46
+
47
+ def finished(_inspected_files)
48
+ output.write(report.to_json)
49
+ end
50
+
51
+ private
52
+
53
+ def report
54
+ {
55
+ '$schema': SCHEMA_URI,
56
+ version: SARIF_VERSION,
57
+ runs: [{ tool: { driver: driver }, results: @results }]
58
+ }
59
+ end
60
+
61
+ def driver
62
+ {
63
+ name: 'RuboCop',
64
+ version: RuboCop::Version::STRING,
65
+ semanticVersion: RuboCop::Version::STRING,
66
+ informationUri: INFORMATION_URI,
67
+ rules: @rules
68
+ }
69
+ end
70
+
71
+ def result_for(file, offense, line_hash_counts)
72
+ result = {
73
+ ruleId: offense.cop_name,
74
+ ruleIndex: rule_index_for(file, offense),
75
+ level: SEVERITY_LEVELS.fetch(offense.severity.name, 'warning'),
76
+ message: { text: offense.message },
77
+ locations: [location_for(file, offense)],
78
+ partialFingerprints: fingerprints_for(offense, line_hash_counts)
79
+ }
80
+
81
+ # Suppressed offenses are only reported under `--display-suppressed`.
82
+ # SARIF has a first-class representation for them, so consumers can
83
+ # tell them apart from offenses that were never triggered.
84
+ result[:suppressions] = [suppression_for(offense)] if offense.disabled?
85
+ result
86
+ end
87
+
88
+ # The URI is relative to the working directory, which is what consumers
89
+ # expect to resolve against their own checkout. No `uriBaseId`: it would
90
+ # have to be declared under `originalUriBaseIds` to mean anything, and
91
+ # every consumer that matters resolves relative paths without it.
92
+ def location_for(file, offense)
93
+ {
94
+ physicalLocation: {
95
+ artifactLocation: { uri: uri_for(file) },
96
+ region: region_for(offense)
97
+ }
98
+ }
99
+ end
100
+
101
+ def region_for(offense)
102
+ # SARIF columns are 1-based and `endColumn` points at the character
103
+ # *after* the region, while `real_last_column` is the last character.
104
+ {
105
+ startLine: offense.first_line,
106
+ startColumn: offense.real_column,
107
+ endLine: offense.last_line,
108
+ endColumn: offense.real_last_column + 1
109
+ }
110
+ end
111
+
112
+ # GitHub uses these to match an alert across runs. Without them it falls
113
+ # back to the file path, which makes alerts churn whenever a file moves.
114
+ # Repeats of the same hash within a file get a `:N` suffix, like GitHub's
115
+ # own fingerprints, so two offenses on one line don't merge into one alert.
116
+ # The first occurrence keeps the bare hash to match earlier reports.
117
+ def fingerprints_for(offense, line_hash_counts)
118
+ line_hash = Digest::SHA256.hexdigest("#{offense.cop_name}#{offense.location.source_line}")
119
+ count = line_hash_counts[line_hash] += 1
120
+ { primaryLocationLineHash: count == 1 ? line_hash : "#{line_hash}:#{count}" }
121
+ end
122
+
123
+ def suppression_for(offense)
124
+ suppression = { kind: 'inSource' }
125
+ suppression[:justification] = offense.justification if offense.justification
126
+ suppression
127
+ end
128
+
129
+ def rule_index_for(file, offense)
130
+ @rule_indexes[offense.cop_name] ||= begin
131
+ @rules << rule_for(file, offense)
132
+ @rules.size - 1
133
+ end
134
+ end
135
+
136
+ def rule_for(file, offense)
137
+ cop_class = Cop::Registry.global.find_by_cop_name(offense.cop_name)
138
+ description = description_for(file, offense)
139
+ help_uri = cop_class && Cop::Documentation.url_for(cop_class, config_for(file))
140
+
141
+ rule = {
142
+ id: offense.cop_name,
143
+ name: offense.cop_name,
144
+ shortDescription: { text: description },
145
+ fullDescription: { text: description },
146
+ help: help_for(description, help_uri),
147
+ defaultConfiguration: { level: default_level_for(file, offense, cop_class) },
148
+ properties: { tags: [offense.cop_name.split('/').first] }
149
+ }
150
+ rule[:helpUri] = help_uri if help_uri
151
+ rule
152
+ end
153
+
154
+ def help_for(description, help_uri)
155
+ help = { text: description }
156
+ help[:markdown] = "#{description}\n\n[Documentation](#{help_uri})" if help_uri
157
+ help
158
+ end
159
+
160
+ def description_for(file, offense)
161
+ cop_config(file, offense)['Description'] || offense.cop_name
162
+ end
163
+
164
+ def default_level_for(file, offense, cop_class)
165
+ severity = cop_config(file, offense)['Severity'] ||
166
+ Cop::Base::DEPARTMENT_SEVERITIES.fetch(cop_class&.department, :convention)
167
+
168
+ SEVERITY_LEVELS.fetch(severity.to_sym, 'warning')
169
+ end
170
+
171
+ def cop_config(file, offense)
172
+ config_for(file)&.for_cop(offense.cop_name) || {}
173
+ end
174
+
175
+ # `file_started` is not called when a formatter is used directly through
176
+ # the API, so there may be no config to consult.
177
+ def config_for(file)
178
+ @config_store&.for_file(file)
179
+ end
180
+
181
+ def uri_for(file)
182
+ PathUtil.smart_path(file).tr('\\', '/')
183
+ end
184
+ end
185
+ end
186
+ end
@@ -14,6 +14,8 @@ module RuboCop
14
14
  # --
15
15
  # 29 Total in 2 files
16
16
  class WorstOffendersFormatter < BaseFormatter
17
+ include TextUtil
18
+
17
19
  attr_reader :offense_counts
18
20
 
19
21
  def started(target_files)
@@ -46,7 +48,7 @@ module RuboCop
46
48
  end
47
49
 
48
50
  output.puts '--'
49
- output.puts "#{total_count} Total in #{file_count} files"
51
+ output.puts "#{total_count} Total in #{pluralize(file_count, 'file')}"
50
52
 
51
53
  output.puts
52
54
  end
@@ -24,6 +24,7 @@ module RuboCop
24
24
  autoload :PacmanFormatter, 'rubocop/formatter/pacman_formatter'
25
25
  autoload :ProgressFormatter, 'rubocop/formatter/progress_formatter'
26
26
  autoload :QuietFormatter, 'rubocop/formatter/quiet_formatter'
27
+ autoload :SARIFFormatter, 'rubocop/formatter/sarif_formatter'
27
28
  autoload :TapFormatter, 'rubocop/formatter/tap_formatter'
28
29
  autoload :WorstOffendersFormatter, 'rubocop/formatter/worst_offenders_formatter'
29
30