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.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/config/default.yml +241 -11
- data/config/obsoletion.yml +9 -0
- data/lib/rubocop/cached_data.rb +47 -7
- data/lib/rubocop/changed_files.rb +134 -0
- data/lib/rubocop/cli/command/cop_names.rb +57 -0
- data/lib/rubocop/cli/command/execute_runner.rb +23 -3
- data/lib/rubocop/cli/command/explain.rb +34 -0
- data/lib/rubocop/cli/command/show_cops.rb +6 -0
- data/lib/rubocop/cli/command/show_docs_url.rb +6 -4
- data/lib/rubocop/cli.rb +11 -4
- data/lib/rubocop/comment_config/directive_range.rb +3 -1
- data/lib/rubocop/comment_config/disable_next.rb +49 -3
- data/lib/rubocop/comment_config/push_pop.rb +55 -0
- data/lib/rubocop/comment_config.rb +32 -53
- data/lib/rubocop/config.rb +10 -0
- data/lib/rubocop/config_loader.rb +18 -7
- data/lib/rubocop/config_loader_resolver.rb +79 -12
- data/lib/rubocop/config_obsoletion/cop_rule.rb +2 -1
- data/lib/rubocop/config_obsoletion/removed_cop.rb +9 -1
- data/lib/rubocop/config_obsoletion.rb +1 -1
- data/lib/rubocop/config_validator.rb +29 -3
- data/lib/rubocop/cop/autocorrect_logic.rb +10 -5
- data/lib/rubocop/cop/badge.rb +1 -1
- data/lib/rubocop/cop/base.rb +57 -24
- data/lib/rubocop/cop/bundler/gem_comment.rb +0 -1
- data/lib/rubocop/cop/bundler/ordered_gems.rb +0 -8
- data/lib/rubocop/cop/cop_documentation.rb +81 -0
- data/lib/rubocop/cop/cop_explanation.rb +140 -0
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +13 -1
- data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +22 -7
- data/lib/rubocop/cop/correctors/empty_line_corrector.rb +1 -1
- data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +23 -2
- data/lib/rubocop/cop/correctors/parentheses_corrector.rb +45 -8
- data/lib/rubocop/cop/correctors.rb +2 -2
- data/lib/rubocop/cop/documentation.rb +1 -1
- data/lib/rubocop/cop/gemspec/dependency_version.rb +0 -6
- data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +0 -6
- data/lib/rubocop/cop/gemspec/require_mfa.rb +1 -1
- data/lib/rubocop/cop/generator.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +2 -5
- data/lib/rubocop/cop/internal_affairs/node_type_group.rb +0 -7
- data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/redundant_context_config_parameter.rb +1 -7
- data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +1 -7
- data/lib/rubocop/cop/layout/argument_alignment.rb +2 -0
- data/lib/rubocop/cop/layout/block_alignment.rb +27 -0
- data/lib/rubocop/cop/layout/case_indentation.rb +25 -9
- data/lib/rubocop/cop/layout/class_structure.rb +9 -1
- data/lib/rubocop/cop/layout/comment_indentation.rb +8 -5
- data/lib/rubocop/cop/layout/def_end_alignment.rb +4 -0
- data/lib/rubocop/cop/layout/else_alignment.rb +1 -0
- data/lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb +14 -5
- data/lib/rubocop/cop/layout/empty_lines.rb +5 -2
- data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -0
- data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +23 -1
- data/lib/rubocop/cop/layout/end_alignment.rb +16 -10
- data/lib/rubocop/cop/layout/extra_spacing.rb +4 -0
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -0
- data/lib/rubocop/cop/layout/first_array_element_indentation.rb +17 -4
- data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +5 -3
- data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +6 -0
- data/lib/rubocop/cop/layout/first_method_parameter_line_break.rb +4 -0
- data/lib/rubocop/cop/layout/first_parameter_indentation.rb +11 -0
- data/lib/rubocop/cop/layout/hash_alignment.rb +15 -10
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -0
- data/lib/rubocop/cop/layout/heredoc_indentation.rb +15 -0
- data/lib/rubocop/cop/layout/indentation_style.rb +4 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +4 -4
- data/lib/rubocop/cop/layout/line_continuation_spacing.rb +8 -1
- data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +25 -9
- data/lib/rubocop/cop/layout/line_length.rb +71 -19
- data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +12 -0
- data/lib/rubocop/cop/layout/multiline_block_layout.rb +8 -1
- data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +7 -0
- data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +6 -0
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +14 -0
- data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +4 -0
- data/lib/rubocop/cop/layout/redundant_line_break.rb +22 -1
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +15 -2
- data/lib/rubocop/cop/layout/single_line_block_chain.rb +10 -2
- data/lib/rubocop/cop/layout/space_after_not.rb +5 -0
- data/lib/rubocop/cop/layout/space_around_block_parameters.rb +10 -4
- data/lib/rubocop/cop/layout/space_around_keyword.rb +10 -0
- data/lib/rubocop/cop/layout/space_around_operators.rb +5 -1
- data/lib/rubocop/cop/layout/space_before_block_braces.rb +4 -1
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +15 -2
- data/lib/rubocop/cop/layout/space_inside_block_braces.rb +31 -8
- data/lib/rubocop/cop/lint/ambiguous_operator.rb +1 -1
- data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +2 -1
- data/lib/rubocop/cop/lint/argument_mismatch.rb +14 -4
- data/lib/rubocop/cop/lint/assignment_in_condition.rb +2 -0
- data/lib/rubocop/cop/lint/cop_directive_syntax.rb +88 -14
- data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -1
- data/lib/rubocop/cop/lint/empty_block.rb +1 -1
- data/lib/rubocop/cop/lint/empty_conditional_body.rb +3 -1
- data/lib/rubocop/cop/lint/literal_as_condition.rb +23 -2
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +63 -19
- data/lib/rubocop/cop/lint/loop.rb +24 -2
- data/lib/rubocop/cop/lint/misplaced_magic_comment.rb +245 -0
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +19 -4
- data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
- data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +1 -1
- data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +17 -1
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +42 -17
- data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +13 -2
- data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +18 -1
- data/lib/rubocop/cop/lint/useless_assignment.rb +14 -7
- data/lib/rubocop/cop/lint/void.rb +8 -1
- data/lib/rubocop/cop/lint.rb +1 -0
- data/lib/rubocop/cop/metrics/class_length.rb +4 -2
- data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +0 -4
- data/lib/rubocop/cop/metrics/module_length.rb +4 -2
- data/lib/rubocop/cop/migration/department_name.rb +1 -1
- data/lib/rubocop/cop/mixin/alignment.rb +5 -1
- data/lib/rubocop/cop/mixin/annotation_comment.rb +1 -1
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +17 -14
- data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +5 -1
- data/lib/rubocop/cop/mixin/code_length.rb +10 -2
- data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +12 -1
- data/lib/rubocop/cop/mixin/endless_method_body_help.rb +69 -0
- data/lib/rubocop/cop/mixin/forbidden_identifiers.rb +2 -2
- data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +26 -2
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +31 -3
- data/lib/rubocop/cop/mixin/hash_transform_method.rb +1 -1
- data/lib/rubocop/cop/mixin/min_branches_count.rb +3 -1
- data/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb +11 -1
- data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +9 -0
- data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
- data/lib/rubocop/cop/mixin/pattern_matching_help.rb +35 -0
- data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +29 -3
- data/lib/rubocop/cop/mixin/project_index_help.rb +15 -0
- data/lib/rubocop/cop/mixin/reparsed_equivalence.rb +13 -3
- data/lib/rubocop/cop/mixin/statement_modifier.rb +22 -2
- data/lib/rubocop/cop/mixin.rb +4 -2
- data/lib/rubocop/cop/naming/accessor_method_name.rb +0 -16
- data/lib/rubocop/cop/naming/block_forwarding.rb +4 -1
- data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
- data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +9 -2
- data/lib/rubocop/cop/naming/heredoc_delimiter_naming.rb +5 -3
- data/lib/rubocop/cop/naming/predicate_prefix.rb +7 -22
- data/lib/rubocop/cop/offense.rb +59 -3
- data/lib/rubocop/cop/registry.rb +10 -2
- data/lib/rubocop/cop/security/json_load.rb +16 -4
- data/lib/rubocop/cop/severity.rb +12 -0
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +10 -2
- data/lib/rubocop/cop/style/alias.rb +18 -5
- data/lib/rubocop/cop/style/ambiguous_endless_method_definition.rb +4 -3
- data/lib/rubocop/cop/style/and_or.rb +4 -0
- data/lib/rubocop/cop/style/arguments_forwarding.rb +6 -3
- data/lib/rubocop/cop/style/attr.rb +3 -4
- data/lib/rubocop/cop/style/bitwise_predicate.rb +6 -4
- data/lib/rubocop/cop/style/block_delimiters.rb +29 -3
- data/lib/rubocop/cop/style/case_like_if.rb +9 -6
- data/lib/rubocop/cop/style/class_and_module_children.rb +54 -16
- data/lib/rubocop/cop/style/class_methods_definitions.rb +8 -0
- data/lib/rubocop/cop/style/combinable_loops.rb +85 -11
- data/lib/rubocop/cop/style/concat_array_literals.rb +46 -7
- data/lib/rubocop/cop/style/conditional_assignment.rb +4 -0
- data/lib/rubocop/cop/style/constant_visibility.rb +6 -2
- data/lib/rubocop/cop/style/data_inheritance.rb +14 -0
- data/lib/rubocop/cop/style/directive_scope.rb +154 -21
- data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +118 -18
- data/lib/rubocop/cop/style/documentation_method.rb +1 -9
- data/lib/rubocop/cop/style/double_negation.rb +11 -0
- data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
- data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
- data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
- data/lib/rubocop/cop/style/empty_literal.rb +1 -1
- data/lib/rubocop/cop/style/empty_method.rb +15 -3
- data/lib/rubocop/cop/style/endless_method.rb +91 -11
- data/lib/rubocop/cop/style/exact_regexp_match.rb +45 -8
- data/lib/rubocop/cop/style/explicit_block_argument.rb +50 -10
- data/lib/rubocop/cop/style/file_null.rb +2 -2
- data/lib/rubocop/cop/style/float_division.rb +28 -14
- data/lib/rubocop/cop/style/for.rb +44 -1
- data/lib/rubocop/cop/style/format_string.rb +28 -6
- data/lib/rubocop/cop/style/format_string_token.rb +1 -1
- data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +2 -1
- data/lib/rubocop/cop/style/guard_clause.rb +13 -1
- data/lib/rubocop/cop/style/hash_conversion.rb +28 -8
- data/lib/rubocop/cop/style/hash_lookup_method.rb +35 -9
- data/lib/rubocop/cop/style/hash_syntax.rb +16 -3
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +22 -3
- data/lib/rubocop/cop/style/if_inside_else.rb +21 -1
- data/lib/rubocop/cop/style/if_unless_modifier.rb +19 -20
- data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +4 -0
- data/lib/rubocop/cop/style/if_with_semicolon.rb +47 -4
- data/lib/rubocop/cop/style/inverse_methods.rb +24 -2
- data/lib/rubocop/cop/style/it_block_parameter.rb +32 -3
- data/lib/rubocop/cop/style/keyword_parameters_order.rb +3 -1
- data/lib/rubocop/cop/style/lambda.rb +18 -0
- data/lib/rubocop/cop/style/lambda_call.rb +7 -2
- data/lib/rubocop/cop/style/magic_comment_format.rb +1 -1
- data/lib/rubocop/cop/style/map_into_array.rb +1 -2
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +45 -10
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +25 -2
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +26 -3
- data/lib/rubocop/cop/style/method_def_parentheses.rb +29 -3
- data/lib/rubocop/cop/style/missing_else.rb +7 -1
- data/lib/rubocop/cop/style/multiline_if_modifier.rb +45 -5
- data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +7 -0
- data/lib/rubocop/cop/style/multiline_method_signature.rb +2 -5
- data/lib/rubocop/cop/style/multiline_ternary_operator.rb +7 -3
- data/lib/rubocop/cop/style/multiline_when_then.rb +7 -0
- data/lib/rubocop/cop/style/mutable_constant.rb +28 -1
- data/lib/rubocop/cop/style/negated_if_else_condition.rb +2 -1
- data/lib/rubocop/cop/style/negative_array_index.rb +9 -1
- data/lib/rubocop/cop/style/nested_modifier.rb +36 -12
- data/lib/rubocop/cop/style/nested_ternary_operator.rb +26 -8
- data/lib/rubocop/cop/style/next.rb +24 -10
- data/lib/rubocop/cop/style/nil_comparison.rb +48 -8
- data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
- data/lib/rubocop/cop/style/non_nil_check.rb +18 -2
- data/lib/rubocop/cop/style/not.rb +10 -3
- data/lib/rubocop/cop/style/numeric_literal_prefix.rb +11 -1
- data/lib/rubocop/cop/style/numeric_predicate.rb +21 -5
- data/lib/rubocop/cop/style/one_line_conditional.rb +14 -3
- data/lib/rubocop/cop/style/parallel_assignment.rb +1 -1
- data/lib/rubocop/cop/style/parentheses_around_condition.rb +6 -1
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +14 -3
- data/lib/rubocop/cop/style/reduce_to_hash.rb +17 -1
- data/lib/rubocop/cop/style/redundant_begin.rb +20 -3
- data/lib/rubocop/cop/style/redundant_condition.rb +34 -17
- data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +16 -1
- data/lib/rubocop/cop/style/redundant_exception.rb +15 -3
- data/lib/rubocop/cop/style/redundant_fetch_block.rb +10 -2
- data/lib/rubocop/cop/style/redundant_freeze.rb +10 -1
- data/lib/rubocop/cop/style/redundant_parentheses.rb +23 -2
- data/lib/rubocop/cop/style/redundant_regexp_argument.rb +7 -0
- data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +6 -5
- data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +25 -5
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +4 -0
- data/lib/rubocop/cop/style/regexp_literal.rb +75 -26
- data/lib/rubocop/cop/style/require_order.rb +9 -0
- data/lib/rubocop/cop/style/rescue_modifier.rb +5 -9
- data/lib/rubocop/cop/style/return_nil.rb +17 -2
- data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +11 -1
- data/lib/rubocop/cop/style/sample.rb +9 -8
- data/lib/rubocop/cop/style/self_assignment.rb +12 -15
- data/lib/rubocop/cop/style/semicolon.rb +16 -5
- data/lib/rubocop/cop/style/single_line_block_params.rb +1 -1
- data/lib/rubocop/cop/style/single_line_methods.rb +16 -4
- data/lib/rubocop/cop/style/special_global_vars.rb +1 -1
- data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +29 -5
- data/lib/rubocop/cop/style/string_concatenation.rb +9 -3
- data/lib/rubocop/cop/style/struct_inheritance.rb +14 -0
- data/lib/rubocop/cop/style/super_arguments.rb +3 -0
- data/lib/rubocop/cop/style/symbol_proc.rb +23 -5
- data/lib/rubocop/cop/style/ternary_parentheses.rb +23 -1
- data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
- data/lib/rubocop/cop/style/trailing_underscore_variable.rb +23 -19
- data/lib/rubocop/cop/style/while_until_modifier.rb +1 -1
- data/lib/rubocop/cop/style/yoda_condition.rb +24 -9
- data/lib/rubocop/cop/style/yoda_expression.rb +10 -1
- data/lib/rubocop/cop/style.rb +0 -1
- data/lib/rubocop/cop/team.rb +10 -5
- data/lib/rubocop/cop/util.rb +1 -1
- data/lib/rubocop/cop/variable_force/assignment.rb +1 -1
- data/lib/rubocop/cop/variable_force/variable.rb +14 -1
- data/lib/rubocop/cops_documentation_generator.rb +23 -2
- data/lib/rubocop/directive_comment.rb +37 -8
- data/lib/rubocop/formatter/base_formatter.rb +22 -0
- data/lib/rubocop/formatter/disabled_config_formatter.rb +19 -1
- data/lib/rubocop/formatter/formatter_set.rb +6 -1
- data/lib/rubocop/formatter/github_actions_formatter.rb +6 -5
- data/lib/rubocop/formatter/html_formatter.rb +2 -2
- data/lib/rubocop/formatter/json_formatter.rb +71 -1
- data/lib/rubocop/formatter/junit_formatter.rb +1 -1
- data/lib/rubocop/formatter/offense_count_formatter.rb +3 -1
- data/lib/rubocop/formatter/pacman_formatter.rb +3 -1
- data/lib/rubocop/formatter/sarif_formatter.rb +186 -0
- data/lib/rubocop/formatter/worst_offenders_formatter.rb +3 -1
- data/lib/rubocop/formatter.rb +1 -0
- data/lib/rubocop/lsp/runtime.rb +47 -11
- data/lib/rubocop/lsp/stdin_runner.rb +11 -5
- data/lib/rubocop/lsp.rb +5 -2
- data/lib/rubocop/magic_comment.rb +27 -0
- data/lib/rubocop/mcp/autocorrection_request.rb +52 -0
- data/lib/rubocop/mcp/explain_tool.rb +68 -0
- data/lib/rubocop/mcp/scope.rb +65 -0
- data/lib/rubocop/mcp/server.rb +184 -76
- data/lib/rubocop/mcp/source_files.rb +35 -0
- data/lib/rubocop/offense_limit.rb +30 -0
- data/lib/rubocop/options.rb +97 -4
- data/lib/rubocop/path_util.rb +1 -1
- data/lib/rubocop/plugin/configuration_integrator.rb +12 -5
- data/lib/rubocop/plugin/loader.rb +1 -1
- data/lib/rubocop/rake_task.rb +1 -1
- data/lib/rubocop/remote_config.rb +8 -5
- data/lib/rubocop/result_cache.rb +36 -18
- data/lib/rubocop/runner.rb +160 -49
- data/lib/rubocop/server/cache.rb +2 -2
- data/lib/rubocop/unified_diff.rb +257 -0
- data/lib/rubocop/util.rb +69 -0
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +9 -1
- metadata +21 -6
- data/lib/rubocop/cop/style/double_cop_disable_directive.rb +0 -46
data/lib/rubocop/mcp/server.rb
CHANGED
|
@@ -19,7 +19,7 @@ rescue LoadError => e
|
|
|
19
19
|
raise unless e.path == 'mcp'
|
|
20
20
|
|
|
21
21
|
warn <<~MESSAGE
|
|
22
|
-
Error: Unable to load `mcp` gem. Add `gem 'mcp'
|
|
22
|
+
Error: Unable to load `mcp` gem. Add `gem 'mcp'` to your Gemfile, or run `gem install mcp`.
|
|
23
23
|
MESSAGE
|
|
24
24
|
|
|
25
25
|
exit!
|
|
@@ -27,16 +27,59 @@ end
|
|
|
27
27
|
|
|
28
28
|
require_relative '../lsp'
|
|
29
29
|
require_relative '../lsp/runtime'
|
|
30
|
+
require_relative 'autocorrection_request'
|
|
31
|
+
require_relative 'explain_tool'
|
|
32
|
+
require_relative 'scope'
|
|
33
|
+
require_relative 'source_files'
|
|
30
34
|
|
|
31
35
|
module RuboCop
|
|
32
36
|
module MCP
|
|
33
37
|
# RuboCop MCP Server.
|
|
34
38
|
# @api private
|
|
35
39
|
class Server
|
|
40
|
+
INSPECTION_DESCRIPTION =
|
|
41
|
+
'Inspect Ruby code for offenses. ' \
|
|
42
|
+
'Provide `source_code` to check inline code or `path` to check files. ' \
|
|
43
|
+
'Either way the result lists offenses per file, with a summary, and the ' \
|
|
44
|
+
'offenses use the `rubocop --format json` format, with 1-based lines. ' \
|
|
45
|
+
'`correctable` says whether `rubocop_autocorrection` fixes an offense, ' \
|
|
46
|
+
'and one whose `correction` is not `safe` needs `safety` set to false. ' \
|
|
47
|
+
'One with a `correction` that is not `correctable` is held back while code is ' \
|
|
48
|
+
'being written, and only fixed with `contextual` set to true. ' \
|
|
49
|
+
'`max_offenses_per_cop` caps what each cop reports, and the ' \
|
|
50
|
+
'summary\'s `unreported_offenses` counts what was left out. ' \
|
|
51
|
+
'A cop that crashes is listed in its file\'s `errors`; the rest still report.'
|
|
52
|
+
|
|
53
|
+
AUTOCORRECTION_DESCRIPTION =
|
|
54
|
+
'Autocorrect RuboCop offenses in Ruby code. ' \
|
|
55
|
+
'Provide `source_code` to correct inline code, which comes back corrected as ' \
|
|
56
|
+
'plain text, or `path` to correct files. ' \
|
|
57
|
+
'Set `safety` to false to include unsafe corrections, and `contextual` to true ' \
|
|
58
|
+
'to include the ones held back while code is being written, such as removing ' \
|
|
59
|
+
'an unused variable. ' \
|
|
60
|
+
'Set `dry_run` to true to preview: nothing is written, and each corrected file ' \
|
|
61
|
+
'comes with its `diff`. ' \
|
|
62
|
+
'For files, the result lists each file that was corrected or still has offenses, ' \
|
|
63
|
+
'with the offenses left in the `rubocop_inspection` format, located in the ' \
|
|
64
|
+
'corrected file. One whose `correction` is not `safe` needs `safety` set to false, ' \
|
|
65
|
+
'and one with a `correction` that is not `correctable` needs `contextual` set to ' \
|
|
66
|
+
'true. Each cop lists at most `max_offenses_per_cop` of them, 5 unless ' \
|
|
67
|
+
'set, and the summary\'s `unreported_offenses` counts the rest. ' \
|
|
68
|
+
'A cop that crashes is listed in its file\'s `errors`, and a file whose ' \
|
|
69
|
+
'corrections loop is left unchanged.'
|
|
70
|
+
|
|
36
71
|
def initialize(config_store)
|
|
37
72
|
@config_store = config_store
|
|
38
|
-
|
|
73
|
+
# An agent isn't an editor, so offenses are located and worded the way
|
|
74
|
+
# the command line does it. Only contextual corrections are held back.
|
|
75
|
+
@runtime = RuboCop::LSP::Runtime.new(@config_store, lsp_mode: false)
|
|
76
|
+
# One cop crashing on one file should not cost an agent everything else
|
|
77
|
+
# in the request, so cop errors are collected and reported per file.
|
|
78
|
+
@runtime.raise_cop_error = false
|
|
39
79
|
@options = {}
|
|
80
|
+
# Offenses are reported the way `--format json` reports them, so an agent
|
|
81
|
+
# sees the same 1-based locations and correction data either way.
|
|
82
|
+
@json_formatter = RuboCop::Formatter::JSONFormatter.new(nil)
|
|
40
83
|
end
|
|
41
84
|
|
|
42
85
|
def start
|
|
@@ -44,7 +87,7 @@ module RuboCop
|
|
|
44
87
|
server = ::MCP::Server.new(
|
|
45
88
|
name: 'rubocop_mcp_server',
|
|
46
89
|
version: RuboCop::Version::STRING,
|
|
47
|
-
tools: [inspection_tool, autocorrection_tool]
|
|
90
|
+
tools: [inspection_tool, autocorrection_tool, ExplainTool.new(@config_store).to_tool]
|
|
48
91
|
)
|
|
49
92
|
|
|
50
93
|
::MCP::Server::Transports::StdioTransport.new(server).open
|
|
@@ -55,91 +98,158 @@ module RuboCop
|
|
|
55
98
|
def inspection_tool
|
|
56
99
|
build_tool(
|
|
57
100
|
name: 'rubocop_inspection',
|
|
58
|
-
description:
|
|
59
|
-
'Provide `source_code` to check inline code or `path` to check files.',
|
|
101
|
+
description: INSPECTION_DESCRIPTION,
|
|
60
102
|
title: "RuboCop's inspection",
|
|
61
103
|
destructive_hint: false,
|
|
62
104
|
idempotent_hint: true,
|
|
63
105
|
read_only_hint: true,
|
|
64
|
-
|
|
65
|
-
) do |path, source_code|
|
|
66
|
-
run_inspection(path, source_code)
|
|
106
|
+
properties: { max_offenses_per_cop: { type: 'integer', minimum: 1 } }
|
|
107
|
+
) do |scope:, path: nil, source_code: nil, max_offenses_per_cop: nil|
|
|
108
|
+
run_inspection(path, source_code, max_offenses_per_cop, scope)
|
|
67
109
|
end
|
|
68
110
|
end
|
|
69
111
|
|
|
70
112
|
def autocorrection_tool
|
|
71
113
|
build_tool(
|
|
72
114
|
name: 'rubocop_autocorrection',
|
|
73
|
-
description:
|
|
74
|
-
'Provide `source_code` to correct inline code or `path` to correct files. ' \
|
|
75
|
-
'Set `safety` to false to include unsafe corrections.',
|
|
115
|
+
description: AUTOCORRECTION_DESCRIPTION,
|
|
76
116
|
title: "RuboCop's autocorrection",
|
|
77
117
|
destructive_hint: true,
|
|
78
118
|
idempotent_hint: false,
|
|
79
119
|
read_only_hint: false,
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
120
|
+
properties: AutocorrectionRequest::PROPERTIES,
|
|
121
|
+
required: ['safety']
|
|
122
|
+
) do |scope:, path: nil, source_code: nil, **arguments|
|
|
123
|
+
run_autocorrection(path, source_code, AutocorrectionRequest.new(scope, **arguments))
|
|
83
124
|
end
|
|
84
125
|
end
|
|
85
126
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
127
|
+
# A limit counts across the files of one request, so every request starts
|
|
128
|
+
# from zero.
|
|
129
|
+
def run_inspection(path, source_code, max_offenses_per_cop, scope)
|
|
130
|
+
limit = OffenseLimit.new(max_offenses_per_cop) if max_offenses_per_cop
|
|
131
|
+
result =
|
|
132
|
+
if source_code
|
|
133
|
+
file = path || 'example.rb'
|
|
134
|
+
build_result([file], [inspect_source(file, source_code, limit, scope)])
|
|
135
|
+
else
|
|
136
|
+
process_files(path, scope) { |file, src| inspect_source(file, src, limit, scope) }
|
|
95
137
|
end
|
|
138
|
+
result_json(result, limit)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def inspect_source(file, source, limit, scope)
|
|
142
|
+
options = { **scope.cop_options, editing: true }
|
|
143
|
+
offenses = @runtime.raw_offenses(file, source, options: options)
|
|
144
|
+
offense_entry(file, offenses, limit).merge(problems_of_last_run(file))
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# The offenses of a file the way `--format json` lists them, capped across
|
|
148
|
+
# the files of a request.
|
|
149
|
+
def offense_entry(file, offenses, limit)
|
|
150
|
+
offenses = limit.filter(offenses) if limit
|
|
151
|
+
@json_formatter.hash_for_file(file, offenses)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def run_autocorrection(path, source_code, request)
|
|
155
|
+
return correct_inline(path, source_code, request) if source_code
|
|
156
|
+
|
|
157
|
+
result = process_files(path, request.scope) do |file, source|
|
|
158
|
+
correct_file(file, source, request)
|
|
96
159
|
end
|
|
160
|
+
result[:summary][:corrected_file_count] = result[:files].count { |file| file[:corrected] }
|
|
161
|
+
result_json(result, request.limit)
|
|
97
162
|
end
|
|
98
163
|
|
|
99
|
-
|
|
100
|
-
|
|
164
|
+
# The agent never sees stderr, so the summary says what the limit left out.
|
|
165
|
+
def result_json(result, limit)
|
|
166
|
+
unreported = limit&.elided_per_cop
|
|
167
|
+
result[:summary][:unreported_offenses] = unreported if unreported&.any?
|
|
168
|
+
result.to_json
|
|
169
|
+
end
|
|
101
170
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
171
|
+
# Cops that keep undoing each other's corrections leave the file half
|
|
172
|
+
# corrected, so it is left alone and the loop is reported instead. A file
|
|
173
|
+
# with nothing to correct is left alone too, so its modification time
|
|
174
|
+
# doesn't tell editors and agents it changed. The offenses left over are
|
|
175
|
+
# the ones an agent has to fix itself, which saves it inspecting again.
|
|
176
|
+
def correct_file(file, source, request)
|
|
177
|
+
corrected = @runtime.format(file, source, **request.format_options)
|
|
178
|
+
problems = problems_of_last_run(file)
|
|
179
|
+
looped = @runtime.errors.any?(Runner::InfiniteCorrectionLoop)
|
|
180
|
+
changed = !looped && source != corrected
|
|
181
|
+
leftovers = leftover_offenses(file, source, corrected, changed, request)
|
|
182
|
+
entry = offense_entry(file, leftovers, request.limit)
|
|
183
|
+
diff = write_or_diff(file, entry[:path], source, corrected, request) if changed
|
|
184
|
+
|
|
185
|
+
{ path: entry[:path], corrected: changed, diff: diff, **entry, **problems }.compact
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# A dry run writes nothing and returns the diff a real run would have
|
|
189
|
+
# applied, line endings included.
|
|
190
|
+
def write_or_diff(file, path, source, corrected, request)
|
|
191
|
+
if request.dry_run?
|
|
192
|
+
UnifiedDiff.new(path, source, Util.emulate_write_read_cycle(corrected)).to_s
|
|
106
193
|
else
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
194
|
+
SourceFiles.write(file, corrected)
|
|
195
|
+
nil
|
|
196
|
+
end
|
|
197
|
+
end
|
|
110
198
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
199
|
+
# What's left is in the file as written, or as a dry run would have
|
|
200
|
+
# written it, or as it was when its corrections loop. The runner checks
|
|
201
|
+
# corrected code with LF line endings, which writing turns into CRLF on
|
|
202
|
+
# Windows, so there the file is checked again as written.
|
|
203
|
+
def leftover_offenses(file, source, corrected, changed, request)
|
|
204
|
+
on_disk = changed ? Util.emulate_write_read_cycle(corrected) : source
|
|
205
|
+
return @runtime.uncorrected_offenses if on_disk == corrected
|
|
206
|
+
|
|
207
|
+
@runtime.raw_offenses(file, on_disk, options: request.runtime_options)
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Corrected inline code comes back as plain text with nowhere to mention
|
|
211
|
+
# a cop that crashed, so a crash fails the call instead of going unseen.
|
|
212
|
+
def correct_inline(path, source_code, request)
|
|
213
|
+
file = path || 'example.rb'
|
|
214
|
+
corrected = @runtime.format(file, source_code, **request.format_options)
|
|
215
|
+
errors = problems_of_last_run(file)[:errors]
|
|
216
|
+
raise RuboCop::Error, errors.join("\n") if errors
|
|
217
|
+
|
|
218
|
+
SourceFiles.write(path, corrected) if path && !request.dry_run?
|
|
219
|
+
corrected
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Collapsed the way `--format json` collapses them, which also folds the
|
|
223
|
+
# repeats from each autocorrection pass a crashing cop fails on.
|
|
224
|
+
def problems_of_last_run(file)
|
|
225
|
+
errors, warnings = [@runtime.errors, @runtime.warnings].map do |problems|
|
|
226
|
+
problems.map { |problem| Rainbow::StringUtils.uncolor(problem.to_s) }
|
|
114
227
|
end
|
|
228
|
+
@json_formatter.hash_for_problems(file, errors, warnings)
|
|
115
229
|
end
|
|
116
230
|
|
|
117
|
-
def process_files(path,
|
|
231
|
+
def process_files(path, scope)
|
|
118
232
|
target_finder = RuboCop::TargetFinder.new(@config_store, @options)
|
|
119
|
-
target_files =
|
|
120
|
-
|
|
121
|
-
|
|
233
|
+
target_files = scope.select_files(
|
|
234
|
+
target_finder.find(path ? [path] : [], :only_recognized_file_types)
|
|
235
|
+
)
|
|
236
|
+
all_files = target_files.map do |file|
|
|
237
|
+
yield(file, SourceFiles.read(file, @config_store.for_file(file)))
|
|
238
|
+
end
|
|
122
239
|
|
|
123
|
-
|
|
240
|
+
build_result(target_files, all_files)
|
|
124
241
|
end
|
|
125
242
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
raise RuboCop::Error, "No such file or directory: #{file}"
|
|
243
|
+
# Inline code is reported as a single file, so an agent reads one shape
|
|
244
|
+
# whichever way it asked. Files with nothing to report are only counted.
|
|
245
|
+
def build_result(target_files, all_files)
|
|
246
|
+
files = all_files.reject { |f| nothing_to_report?(f) }
|
|
247
|
+
|
|
248
|
+
{ files: files, summary: build_summary(target_files, all_files) }
|
|
133
249
|
end
|
|
134
250
|
|
|
135
|
-
def
|
|
136
|
-
|
|
137
|
-
rescue Errno::EACCES
|
|
138
|
-
raise RuboCop::Error, "Permission denied: #{file}"
|
|
139
|
-
rescue Errno::ENOSPC
|
|
140
|
-
raise RuboCop::Error, "No space left on device: #{file}"
|
|
141
|
-
rescue Errno::EROFS
|
|
142
|
-
raise RuboCop::Error, "Read-only file system: #{file}"
|
|
251
|
+
def nothing_to_report?(file)
|
|
252
|
+
file[:offenses].empty? && !file[:corrected] && !file[:errors] && !file[:warnings]
|
|
143
253
|
end
|
|
144
254
|
|
|
145
255
|
# NOTE: It is useful for RuboCop's result summary to be shown in the LLM's responses
|
|
@@ -147,28 +257,16 @@ module RuboCop
|
|
|
147
257
|
# to reason about. Since LLM execution is non-deterministic, it is also sensible to
|
|
148
258
|
# compute the summary deterministically at this stage.
|
|
149
259
|
def build_summary(target_files, files)
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
else
|
|
154
|
-
summary[:corrected_file_count] = files.count { |f| f[:corrected] }
|
|
155
|
-
end
|
|
156
|
-
summary
|
|
260
|
+
offense_count = files.sum { |f| f[:offenses].size }
|
|
261
|
+
{ target_file_count: target_files.count, offense_count: offense_count }
|
|
262
|
+
.merge(@json_formatter.problem_counts(files))
|
|
157
263
|
end
|
|
158
264
|
|
|
159
265
|
# rubocop:disable-next Metrics/MethodLength, Metrics/ParameterLists
|
|
160
266
|
def build_tool(
|
|
161
267
|
name:, description:,
|
|
162
|
-
title:, destructive_hint:, idempotent_hint:, read_only_hint:,
|
|
268
|
+
title:, destructive_hint:, idempotent_hint:, read_only_hint:, properties:, required: nil
|
|
163
269
|
)
|
|
164
|
-
if safety_required
|
|
165
|
-
safety_property = { safety: { type: 'boolean' } }
|
|
166
|
-
required = ['safety']
|
|
167
|
-
else
|
|
168
|
-
safety_property = {}
|
|
169
|
-
required = nil
|
|
170
|
-
end
|
|
171
|
-
|
|
172
270
|
::MCP::Tool.define(
|
|
173
271
|
name: name,
|
|
174
272
|
description: description,
|
|
@@ -176,8 +274,11 @@ module RuboCop
|
|
|
176
274
|
properties: {
|
|
177
275
|
path: { type: 'string' },
|
|
178
276
|
source_code: { type: 'string' }
|
|
179
|
-
}.merge(
|
|
180
|
-
required: required
|
|
277
|
+
}.merge(properties, Scope::PROPERTIES),
|
|
278
|
+
required: required,
|
|
279
|
+
# The gem checks arguments against the schema, so one the tool
|
|
280
|
+
# doesn't take is refused by name rather than failing the call.
|
|
281
|
+
additionalProperties: false
|
|
181
282
|
}.compact,
|
|
182
283
|
annotations: {
|
|
183
284
|
title: title,
|
|
@@ -186,11 +287,18 @@ module RuboCop
|
|
|
186
287
|
open_world_hint: false,
|
|
187
288
|
read_only_hint: read_only_hint
|
|
188
289
|
}
|
|
189
|
-
) do |
|
|
190
|
-
|
|
290
|
+
) do |**arguments|
|
|
291
|
+
# Taking any keywords makes the gem pass a `server_context` as well.
|
|
292
|
+
# Each tool's own block names the arguments it accepts.
|
|
293
|
+
arguments.delete(:server_context)
|
|
294
|
+
selection = Scope::PROPERTIES.keys.to_h { |key| [key, arguments.delete(key)] }
|
|
295
|
+
scope = Scope.new(inline: !arguments[:source_code].nil?, **selection)
|
|
296
|
+
result = yield(**arguments, scope: scope)
|
|
191
297
|
|
|
192
298
|
::MCP::Tool::Response.new([{ type: 'text', text: result }])
|
|
193
|
-
rescue RuboCop::Error => e
|
|
299
|
+
rescue RuboCop::Error, IncorrectCopNameError => e
|
|
300
|
+
# The runner raises `IncorrectCopNameError` for a name in `only` or
|
|
301
|
+
# `except` that it does not know, with suggestions.
|
|
194
302
|
::MCP::Tool::Response.new([{ type: 'text', text: e.message }], error: true)
|
|
195
303
|
end
|
|
196
304
|
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module MCP
|
|
5
|
+
# Reads and writes the files the tools work on. A file that can't be read
|
|
6
|
+
# or written fails the request with an error the agent can act on.
|
|
7
|
+
# @api private
|
|
8
|
+
module SourceFiles
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
def read(file, config)
|
|
12
|
+
RuboCop::ProcessedSource.from_file(
|
|
13
|
+
file, config.target_ruby_version, parser_engine: config.parser_engine
|
|
14
|
+
).raw_source
|
|
15
|
+
rescue Errno::ENOENT
|
|
16
|
+
raise RuboCop::Error, "No such file or directory: #{file}"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Writing what a file already holds would only bump its modification time.
|
|
20
|
+
def write(file, content)
|
|
21
|
+
return if File.file?(file) && File.binread(file) == content.b
|
|
22
|
+
|
|
23
|
+
Util.replace_file_contents(file, content)
|
|
24
|
+
rescue Errno::EACCES
|
|
25
|
+
raise RuboCop::Error, "Permission denied: #{file}"
|
|
26
|
+
rescue Errno::EPERM
|
|
27
|
+
raise RuboCop::Error, "Operation not permitted: #{file}"
|
|
28
|
+
rescue Errno::ENOSPC
|
|
29
|
+
raise RuboCop::Error, "No space left on device: #{file}"
|
|
30
|
+
rescue Errno::EROFS
|
|
31
|
+
raise RuboCop::Error, "Read-only file system: #{file}"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
# Caps how many offenses each cop gets to report.
|
|
5
|
+
#
|
|
6
|
+
# Counted across a whole run rather than per file, since a cop that fires a
|
|
7
|
+
# handful of times in every file is exactly the one worth capping. Offenses
|
|
8
|
+
# have to be offered in a stable order (files in the order they are
|
|
9
|
+
# reported) for the same ones to survive every time.
|
|
10
|
+
# @api private
|
|
11
|
+
class OffenseLimit
|
|
12
|
+
attr_reader :max
|
|
13
|
+
|
|
14
|
+
def initialize(max)
|
|
15
|
+
@max = max
|
|
16
|
+
@offered = Hash.new(0)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# @return [Array<Cop::Offense>] the offenses still within their cop's limit
|
|
20
|
+
def filter(offenses)
|
|
21
|
+
offenses.select { |offense| (@offered[offense.cop_name] += 1) <= @max }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @return [Hash{String => Integer}] how many offenses each cop had left out,
|
|
25
|
+
# sorted by cop name
|
|
26
|
+
def elided_per_cop
|
|
27
|
+
@offered.filter_map { |cop, count| [cop, count - @max] if count > @max }.sort.to_h
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
data/lib/rubocop/options.rb
CHANGED
|
@@ -17,7 +17,7 @@ module RuboCop
|
|
|
17
17
|
'cops are enabled (via eg. Include/Exclude), and so that certain cops ' \
|
|
18
18
|
'like Naming/FileName can be checked.'
|
|
19
19
|
EXITING_OPTIONS = %i[
|
|
20
|
-
version verbose_version show_cops list_enabled_cops_for show_docs_url lsp mcp
|
|
20
|
+
version verbose_version show_cops list_enabled_cops_for show_docs_url explain lsp mcp
|
|
21
21
|
].freeze
|
|
22
22
|
DEFAULT_MAXIMUM_EXCLUSION_ITEMS = 15
|
|
23
23
|
|
|
@@ -33,6 +33,9 @@ module RuboCop
|
|
|
33
33
|
|
|
34
34
|
define_options.parse!(args)
|
|
35
35
|
|
|
36
|
+
return_path_given_to_changed(args)
|
|
37
|
+
imply_autocorrect_for_diff
|
|
38
|
+
|
|
36
39
|
@validator.validate_compatibility
|
|
37
40
|
|
|
38
41
|
if @options[:stdin]
|
|
@@ -51,6 +54,21 @@ module RuboCop
|
|
|
51
54
|
|
|
52
55
|
private
|
|
53
56
|
|
|
57
|
+
# `--changed lib` hands the path to `--changed` as its optional argument,
|
|
58
|
+
# so it goes back among the paths to inspect.
|
|
59
|
+
def return_path_given_to_changed(args)
|
|
60
|
+
args.unshift(@path_given_to_changed) if @path_given_to_changed
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# `--diff` is a dry run of autocorrection, so it turns autocorrection on
|
|
64
|
+
# unless the user already picked a mode with `-a`, `-A` or `-x`.
|
|
65
|
+
def imply_autocorrect_for_diff
|
|
66
|
+
return unless @options[:diff] && !@options[:autocorrect]
|
|
67
|
+
|
|
68
|
+
@options[:safe_autocorrect] = true
|
|
69
|
+
@options[:autocorrect] = true
|
|
70
|
+
end
|
|
71
|
+
|
|
54
72
|
# rubocop:disable-next Metrics/AbcSize
|
|
55
73
|
def define_options
|
|
56
74
|
OptionParser.new do |opts|
|
|
@@ -92,12 +110,20 @@ module RuboCop
|
|
|
92
110
|
option(opts, '--enable-pending-cops')
|
|
93
111
|
option(opts, '--disable-all-cops')
|
|
94
112
|
option(opts, '--enable-all-cops')
|
|
113
|
+
option(opts, '--[no-]preview')
|
|
95
114
|
option(opts, '--ignore-disable-comments')
|
|
96
115
|
option(opts, '--force-exclusion')
|
|
97
116
|
option(opts, '--only-recognized-file-types')
|
|
98
117
|
option(opts, '--ignore-parent-exclusion')
|
|
99
118
|
option(opts, '--ignore-unrecognized-cops')
|
|
100
119
|
option(opts, '--force-default-config')
|
|
120
|
+
option(opts, '--changed [REVISION]') do |revision|
|
|
121
|
+
if @validator.path_given_to_changed?(revision)
|
|
122
|
+
@path_given_to_changed = revision
|
|
123
|
+
revision = nil
|
|
124
|
+
end
|
|
125
|
+
@options[:changed] = revision || ChangedFiles::DEFAULT_REVISION
|
|
126
|
+
end
|
|
101
127
|
option(opts, '-s', '--stdin FILE')
|
|
102
128
|
option(opts, '--editor-mode')
|
|
103
129
|
option(opts, '-P', '--[no-]parallel')
|
|
@@ -132,10 +158,13 @@ module RuboCop
|
|
|
132
158
|
option(opts, '--display-only-correctable')
|
|
133
159
|
option(opts, '--display-only-safe-correctable')
|
|
134
160
|
option(opts, '--display-suppressed')
|
|
161
|
+
option(opts, '--max-offenses-per-cop COUNT') do
|
|
162
|
+
@validator.validate_max_offenses_per_cop_option
|
|
163
|
+
end
|
|
135
164
|
end
|
|
136
165
|
end
|
|
137
166
|
|
|
138
|
-
# rubocop:todo Naming/InclusiveLanguage
|
|
167
|
+
# rubocop:todo Naming/InclusiveLanguage -- `--auto-correct` is the deprecated flag name
|
|
139
168
|
# the autocorrect command-line arguments map to the autocorrect @options values like so:
|
|
140
169
|
# :fix_layout :autocorrect :safe_autocorrect :autocorrect_all
|
|
141
170
|
# -x, --fix-layout true true - -
|
|
@@ -161,6 +190,8 @@ module RuboCop
|
|
|
161
190
|
end
|
|
162
191
|
|
|
163
192
|
option(opts, '--disable-uncorrectable')
|
|
193
|
+
|
|
194
|
+
option(opts, '--diff')
|
|
164
195
|
end
|
|
165
196
|
end
|
|
166
197
|
# rubocop:enable Naming/InclusiveLanguage
|
|
@@ -249,6 +280,9 @@ module RuboCop
|
|
|
249
280
|
option(opts, '--show-docs-url [COP1,COP2,...]') do |list|
|
|
250
281
|
@options[:show_docs_url] = list.nil? ? [] : list.split(',')
|
|
251
282
|
end
|
|
283
|
+
option(opts, '--explain COP1,COP2,...') do |list|
|
|
284
|
+
@options[:explain] = list.split(',')
|
|
285
|
+
end
|
|
252
286
|
end
|
|
253
287
|
end
|
|
254
288
|
|
|
@@ -400,6 +434,8 @@ module RuboCop
|
|
|
400
434
|
validate_display_only_failed_and_display_only_correctable
|
|
401
435
|
validate_display_only_correctable_and_autocorrect
|
|
402
436
|
validate_lsp_and_editor_mode
|
|
437
|
+
validate_diff
|
|
438
|
+
validate_changed_and_stdin
|
|
403
439
|
validate_enable_all_cops_and_disable_all_cops
|
|
404
440
|
disable_parallel_when_invalid_option_combo
|
|
405
441
|
|
|
@@ -447,6 +483,20 @@ module RuboCop
|
|
|
447
483
|
'--display-only-failed cannot be used together with other display options.'
|
|
448
484
|
end
|
|
449
485
|
|
|
486
|
+
# `--diff` promises not to write anything, and `--auto-gen-config` exists to
|
|
487
|
+
# write a file.
|
|
488
|
+
def validate_diff
|
|
489
|
+
return unless @options.key?(:diff) && @options.key?(:auto_gen_config)
|
|
490
|
+
|
|
491
|
+
raise OptionArgumentError, '--diff cannot be used with --auto-gen-config.'
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
def validate_changed_and_stdin
|
|
495
|
+
return unless @options.key?(:changed) && @options.key?(:stdin)
|
|
496
|
+
|
|
497
|
+
raise OptionArgumentError, '--changed cannot be used with --stdin.'
|
|
498
|
+
end
|
|
499
|
+
|
|
450
500
|
def validate_lsp_and_editor_mode
|
|
451
501
|
return if !@options.key?(:lsp) || !@options.key?(:editor_mode)
|
|
452
502
|
|
|
@@ -475,7 +525,7 @@ module RuboCop
|
|
|
475
525
|
end
|
|
476
526
|
|
|
477
527
|
def disable_parallel_when_invalid_option_combo
|
|
478
|
-
return unless @options
|
|
528
|
+
return unless @options[:parallel]
|
|
479
529
|
|
|
480
530
|
invalid_flags = invalid_arguments_for_parallel
|
|
481
531
|
|
|
@@ -483,7 +533,9 @@ module RuboCop
|
|
|
483
533
|
|
|
484
534
|
@options.delete(:parallel)
|
|
485
535
|
|
|
486
|
-
|
|
536
|
+
# On stderr, so that it can't end up in the middle of a formatter's
|
|
537
|
+
# output, such as `--format json`.
|
|
538
|
+
warn '-P/--parallel is being ignored because ' \
|
|
487
539
|
"it is not compatible with #{invalid_flags.join(', ')}."
|
|
488
540
|
end
|
|
489
541
|
|
|
@@ -511,6 +563,25 @@ module RuboCop
|
|
|
511
563
|
@incompatible_options ||= @options.keys & Options::EXITING_OPTIONS
|
|
512
564
|
end
|
|
513
565
|
|
|
566
|
+
# OptionParser hands `--changed lib/` the path as the revision, which is a
|
|
567
|
+
# natural thing to type, so an argument that names an existing path is
|
|
568
|
+
# taken as one. When it also names a revision there is no telling which
|
|
569
|
+
# was meant, and git itself refuses such an argument as ambiguous.
|
|
570
|
+
def path_given_to_changed?(revision)
|
|
571
|
+
return false unless revision && File.exist?(revision)
|
|
572
|
+
return true unless ChangedFiles.revision?(revision)
|
|
573
|
+
|
|
574
|
+
raise OptionArgumentError,
|
|
575
|
+
"--changed got `#{revision}`, which is both a path and a git revision. Write " \
|
|
576
|
+
"`./#{revision}` for the path, or `--changed=#{revision}^0` for the revision."
|
|
577
|
+
end
|
|
578
|
+
|
|
579
|
+
def validate_max_offenses_per_cop_option
|
|
580
|
+
return if /^[1-9]\d*$/.match?(@options[:max_offenses_per_cop])
|
|
581
|
+
|
|
582
|
+
raise OptionParser::InvalidArgument, '--max-offenses-per-cop must be a positive integer'
|
|
583
|
+
end
|
|
584
|
+
|
|
514
585
|
def validate_exclude_limit_option
|
|
515
586
|
return if /^\d+$/.match?(@options[:exclude_limit])
|
|
516
587
|
|
|
@@ -566,12 +637,21 @@ module RuboCop
|
|
|
566
637
|
'when running --auto-gen-config, except if the',
|
|
567
638
|
'number of files with offenses is bigger than',
|
|
568
639
|
'exclude-limit. Default is false.'],
|
|
640
|
+
max_offenses_per_cop: ['Report at most COUNT offenses per cop across the',
|
|
641
|
+
'whole run, so a single noisy cop cannot bury the',
|
|
642
|
+
'rest. Offenses beyond the limit are not reported',
|
|
643
|
+
'and do not count towards the totals, the same way',
|
|
644
|
+
'the other display filters work.'],
|
|
569
645
|
exclude_limit: ['Set the limit for how many files to explicitly exclude.',
|
|
570
646
|
'If there are more files than the limit, the cop will',
|
|
571
647
|
"be disabled instead. Default is #{MAX_EXCL}."],
|
|
572
648
|
disable_uncorrectable: ['Used with --autocorrect to annotate any',
|
|
573
649
|
'offenses that do not support autocorrect',
|
|
574
650
|
'with `rubocop:todo` comments.'],
|
|
651
|
+
diff: ['Print a unified diff of what autocorrection',
|
|
652
|
+
'would change, without writing any files.',
|
|
653
|
+
'Turns on safe autocorrection unless a mode',
|
|
654
|
+
'was already given with -a, -A or -x.'],
|
|
575
655
|
no_exclude_limit: ['Do not set the limit for how many files to exclude.'],
|
|
576
656
|
force_exclusion: ['Any files excluded by `Exclude` in configuration',
|
|
577
657
|
'files will be excluded, even if given explicitly',
|
|
@@ -584,6 +664,10 @@ module RuboCop
|
|
|
584
664
|
ignore_parent_exclusion: ['Prevent from inheriting `AllCops/Exclude` from',
|
|
585
665
|
'parent folders.'],
|
|
586
666
|
ignore_unrecognized_cops: ['Ignore unrecognized cops or departments in the config.'],
|
|
667
|
+
changed: ['Inspect only the files that differ from a git',
|
|
668
|
+
'revision, defaulting to HEAD. Untracked files',
|
|
669
|
+
'count as changed. Pass a revision with',
|
|
670
|
+
'`--changed=REVISION`.'],
|
|
587
671
|
force_default_config: ['Use default configuration even if configuration',
|
|
588
672
|
'files are present in the directory tree.'],
|
|
589
673
|
format: ['Choose an output formatter. This option',
|
|
@@ -598,6 +682,7 @@ module RuboCop
|
|
|
598
682
|
'specified --format, or the default format',
|
|
599
683
|
'if no format is specified.'],
|
|
600
684
|
fail_level: ['Minimum severity for exit with error code.',
|
|
685
|
+
'Overrides `AllCops: FailLevel` in the configuration.',
|
|
601
686
|
' [A] autocorrect',
|
|
602
687
|
' [I] info',
|
|
603
688
|
' [R] refactor',
|
|
@@ -620,6 +705,10 @@ module RuboCop
|
|
|
620
705
|
'default, and their configurations for the',
|
|
621
706
|
'current directory.',
|
|
622
707
|
'You can use `*` as a wildcard.'],
|
|
708
|
+
explain: ['Explain what the given cops do: their',
|
|
709
|
+
'description, configuration, whether they',
|
|
710
|
+
'correct, and the examples from their own',
|
|
711
|
+
'documentation.'],
|
|
623
712
|
show_docs_url: ['Display url to documentation for the given',
|
|
624
713
|
'cops, or base url by default.'],
|
|
625
714
|
fail_fast: ['Inspect files in order of modification',
|
|
@@ -642,6 +731,10 @@ module RuboCop
|
|
|
642
731
|
'`AllCops/DisabledByDefault` in config files.'],
|
|
643
732
|
display_style_guide: 'Display style guide URLs in offense messages.',
|
|
644
733
|
enable_pending_cops: 'Run with pending cops.',
|
|
734
|
+
preview: ['Opt in to unstable behavior: cops that are',
|
|
735
|
+
'`Enabled: preview`, and changes to existing',
|
|
736
|
+
'cops that are not the default yet.',
|
|
737
|
+
'Overrides `AllCops: Preview`.'],
|
|
645
738
|
enable_all_cops: ['Run with all cops enabled, including those',
|
|
646
739
|
'disabled by default. Overrides',
|
|
647
740
|
'`AllCops/EnabledByDefault` and',
|
data/lib/rubocop/path_util.rb
CHANGED
|
@@ -44,7 +44,7 @@ module RuboCop
|
|
|
44
44
|
uri.start_with?('http://', 'https://')
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
-
SMART_PATH_CACHE = {} # rubocop:disable Style/MutableConstant
|
|
47
|
+
SMART_PATH_CACHE = {} # rubocop:disable Style/MutableConstant -- a cache, written to at runtime
|
|
48
48
|
private_constant :SMART_PATH_CACHE
|
|
49
49
|
|
|
50
50
|
def smart_path(path)
|