rubocop 1.91.0 → 1.92.2
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 +4 -1
- data/lib/rubocop/cached_data.rb +47 -7
- data/lib/rubocop/changed_files.rb +35 -13
- data/lib/rubocop/cli/command/cop_names.rb +57 -0
- data/lib/rubocop/cli/command/execute_runner.rb +2 -2
- 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 +3 -2
- data/lib/rubocop/comment_config.rb +17 -8
- data/lib/rubocop/config.rb +0 -4
- data/lib/rubocop/config_loader.rb +6 -3
- data/lib/rubocop/config_loader_resolver.rb +37 -12
- data/lib/rubocop/config_obsoletion/removed_cop.rb +1 -1
- data/lib/rubocop/config_obsoletion.rb +9 -0
- data/lib/rubocop/config_validator.rb +11 -0
- data/lib/rubocop/cop/autocorrect_logic.rb +10 -5
- data/lib/rubocop/cop/badge.rb +1 -1
- data/lib/rubocop/cop/base.rb +42 -21
- 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 +37 -7
- data/lib/rubocop/cop/correctors/empty_line_corrector.rb +1 -1
- data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +25 -4
- data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +34 -0
- data/lib/rubocop/cop/correctors/parentheses_corrector.rb +45 -8
- 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/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/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 +5 -1
- 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 +29 -7
- data/lib/rubocop/cop/layout/def_end_alignment.rb +4 -0
- data/lib/rubocop/cop/layout/dot_position.rb +25 -15
- data/lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb +21 -8
- 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 +9 -3
- data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +23 -1
- data/lib/rubocop/cop/layout/end_alignment.rb +21 -25
- data/lib/rubocop/cop/layout/extra_spacing.rb +21 -6
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +27 -1
- data/lib/rubocop/cop/layout/first_array_element_line_break.rb +15 -0
- data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +16 -8
- 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/hash_alignment.rb +16 -12
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +7 -3
- data/lib/rubocop/cop/layout/heredoc_indentation.rb +15 -0
- data/lib/rubocop/cop/layout/indentation_consistency.rb +22 -5
- data/lib/rubocop/cop/layout/indentation_style.rb +4 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +2 -2
- 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 +176 -26
- 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 +9 -0
- data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +6 -0
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +30 -5
- 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 +16 -3
- 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 +6 -11
- data/lib/rubocop/cop/layout/space_around_keyword.rb +10 -0
- data/lib/rubocop/cop/layout/space_around_operators.rb +31 -4
- data/lib/rubocop/cop/layout/space_before_block_braces.rb +16 -1
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +15 -2
- data/lib/rubocop/cop/layout/space_inside_block_braces.rb +32 -9
- data/lib/rubocop/cop/layout/space_inside_range_literal.rb +29 -11
- data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +2 -12
- 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/ambiguous_regexp_literal.rb +6 -1
- data/lib/rubocop/cop/lint/empty_conditional_body.rb +3 -1
- data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +4 -0
- data/lib/rubocop/cop/lint/interpolation_check.rb +20 -17
- data/lib/rubocop/cop/lint/literal_as_condition.rb +15 -0
- 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 +71 -11
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +18 -3
- data/lib/rubocop/cop/lint/nested_percent_literal.rb +3 -0
- data/lib/rubocop/cop/lint/number_conversion.rb +3 -1
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +22 -1
- data/lib/rubocop/cop/lint/percent_string_array.rb +75 -9
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +39 -9
- data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +13 -2
- data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +11 -4
- data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +47 -1
- data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -0
- 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/useless_setter_call.rb +7 -4
- data/lib/rubocop/cop/lint/void.rb +8 -1
- 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/mixin/alignment.rb +4 -0
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +23 -19
- data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +8 -2
- data/lib/rubocop/cop/mixin/code_length.rb +10 -2
- data/lib/rubocop/cop/mixin/comments_help.rb +1 -1
- data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +12 -1
- data/lib/rubocop/cop/mixin/endless_method_body_help.rb +105 -0
- data/lib/rubocop/cop/mixin/forbidden_identifiers.rb +2 -2
- data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +41 -6
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +26 -2
- 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 +17 -2
- 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 +39 -5
- data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
- data/lib/rubocop/cop/mixin/visibility_help.rb +2 -1
- data/lib/rubocop/cop/mixin.rb +2 -0
- data/lib/rubocop/cop/naming/accessor_method_name.rb +0 -16
- data/lib/rubocop/cop/naming/block_forwarding.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/naming/rescued_exceptions_variable_name.rb +1 -1
- data/lib/rubocop/cop/offense.rb +59 -3
- data/lib/rubocop/cop/registry.rb +1 -1
- data/lib/rubocop/cop/security/json_load.rb +16 -4
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +37 -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 +37 -10
- data/lib/rubocop/cop/style/arguments_forwarding.rb +32 -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 +35 -3
- data/lib/rubocop/cop/style/case_equality.rb +14 -3
- data/lib/rubocop/cop/style/case_like_if.rb +6 -3
- data/lib/rubocop/cop/style/class_and_module_children.rb +57 -16
- data/lib/rubocop/cop/style/class_methods_definitions.rb +15 -0
- data/lib/rubocop/cop/style/collection_querying.rb +79 -46
- data/lib/rubocop/cop/style/combinable_loops.rb +89 -11
- data/lib/rubocop/cop/style/concat_array_literals.rb +58 -7
- data/lib/rubocop/cop/style/conditional_assignment.rb +4 -0
- data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +65 -9
- data/lib/rubocop/cop/style/double_negation.rb +11 -0
- data/lib/rubocop/cop/style/empty_literal.rb +1 -1
- data/lib/rubocop/cop/style/empty_method.rb +3 -1
- data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +53 -11
- data/lib/rubocop/cop/style/endless_method.rb +104 -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/float_division.rb +37 -2
- data/lib/rubocop/cop/style/for.rb +53 -2
- data/lib/rubocop/cop/style/format_string.rb +28 -6
- data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +2 -1
- data/lib/rubocop/cop/style/guard_clause.rb +31 -22
- data/lib/rubocop/cop/style/hash_as_last_array_item.rb +4 -0
- data/lib/rubocop/cop/style/hash_conversion.rb +28 -8
- data/lib/rubocop/cop/style/hash_lookup_method.rb +42 -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 +32 -26
- 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 +94 -4
- data/lib/rubocop/cop/style/keyword_parameters_order.rb +3 -1
- data/lib/rubocop/cop/style/lambda.rb +34 -1
- data/lib/rubocop/cop/style/lambda_call.rb +7 -2
- data/lib/rubocop/cop/style/map_into_array.rb +1 -2
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +80 -13
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +26 -2
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +26 -3
- data/lib/rubocop/cop/style/method_def_parentheses.rb +38 -4
- data/lib/rubocop/cop/style/missing_else.rb +7 -1
- data/lib/rubocop/cop/style/mixin_grouping.rb +12 -3
- 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 +52 -7
- 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 +27 -8
- 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 +16 -4
- data/lib/rubocop/cop/style/non_nil_check.rb +1 -1
- data/lib/rubocop/cop/style/not.rb +10 -5
- data/lib/rubocop/cop/style/numeric_predicate.rb +28 -5
- data/lib/rubocop/cop/style/one_line_conditional.rb +13 -2
- data/lib/rubocop/cop/style/parentheses_around_condition.rb +19 -1
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +14 -3
- data/lib/rubocop/cop/style/quoted_symbols.rb +5 -7
- data/lib/rubocop/cop/style/raise_args.rb +80 -11
- data/lib/rubocop/cop/style/reduce_to_hash.rb +47 -33
- data/lib/rubocop/cop/style/redundant_begin.rb +45 -5
- data/lib/rubocop/cop/style/redundant_condition.rb +30 -13
- data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +6 -0
- data/lib/rubocop/cop/style/redundant_exception.rb +27 -11
- data/lib/rubocop/cop/style/redundant_fetch_block.rb +9 -1
- data/lib/rubocop/cop/style/redundant_freeze.rb +11 -3
- data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +24 -1
- data/lib/rubocop/cop/style/redundant_parentheses.rb +12 -1
- data/lib/rubocop/cop/style/redundant_regexp_argument.rb +7 -0
- data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +20 -0
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +4 -0
- data/lib/rubocop/cop/style/redundant_self.rb +9 -1
- data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +28 -0
- data/lib/rubocop/cop/style/redundant_sort.rb +38 -4
- data/lib/rubocop/cop/style/regexp_literal.rb +75 -26
- data/lib/rubocop/cop/style/require_order.rb +25 -8
- data/lib/rubocop/cop/style/rescue_modifier.rb +11 -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/send_with_literal_method_name.rb +4 -0
- data/lib/rubocop/cop/style/single_line_methods.rb +42 -4
- data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +29 -5
- data/lib/rubocop/cop/style/string_concatenation.rb +21 -4
- data/lib/rubocop/cop/style/struct_inheritance.rb +6 -0
- data/lib/rubocop/cop/style/super_arguments.rb +3 -0
- data/lib/rubocop/cop/style/symbol_proc.rb +24 -6
- data/lib/rubocop/cop/style/ternary_parentheses.rb +23 -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/team.rb +10 -5
- data/lib/rubocop/cop/variable_force/assignment.rb +1 -1
- data/lib/rubocop/cop/variable_force/variable.rb +14 -1
- data/lib/rubocop/formatter/base_formatter.rb +22 -0
- data/lib/rubocop/formatter/formatter_set.rb +4 -0
- data/lib/rubocop/formatter/json_formatter.rb +71 -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 +12 -7
- data/lib/rubocop/formatter/worst_offenders_formatter.rb +3 -1
- data/lib/rubocop/lsp/routes.rb +23 -23
- 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/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 +45 -10
- data/lib/rubocop/plugin/configuration_integrator.rb +36 -8
- data/lib/rubocop/remote_config.rb +4 -1
- data/lib/rubocop/result_cache.rb +20 -13
- data/lib/rubocop/runner.rb +110 -42
- data/lib/rubocop/server/cache.rb +6 -6
- data/lib/rubocop/target_ruby.rb +21 -9
- data/lib/rubocop/util.rb +69 -0
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +5 -0
- metadata +16 -5
|
@@ -67,7 +67,8 @@ module RuboCop
|
|
|
67
67
|
|
|
68
68
|
break if !assignment.branch || assignment.branch == reference.branch
|
|
69
69
|
|
|
70
|
-
unless assignment.branch.may_run_incompletely?
|
|
70
|
+
unless assignment.branch.may_run_incompletely? ||
|
|
71
|
+
exception_may_reach?(assignment.branch, node)
|
|
71
72
|
(consumed_branches ||= Set.new) << assignment.branch
|
|
72
73
|
end
|
|
73
74
|
end
|
|
@@ -83,6 +84,18 @@ module RuboCop
|
|
|
83
84
|
covers?(conditional, reference_node) && !covers?(conditional.condition, reference_node)
|
|
84
85
|
end
|
|
85
86
|
|
|
87
|
+
# A branch nested in the main body of `begin` can be cut short by an exception
|
|
88
|
+
# too. An earlier assignment in it is visible to a reference in the `rescue` or
|
|
89
|
+
# `ensure` clause, or after a `rescue` that handles the exception.
|
|
90
|
+
def exception_may_reach?(branch, reference_node)
|
|
91
|
+
branch.each_ancestor.any? do |ancestor|
|
|
92
|
+
next false unless ancestor.may_run_incompletely?
|
|
93
|
+
next false if covers?(ancestor.child_node, reference_node)
|
|
94
|
+
|
|
95
|
+
ancestor.control_node.rescue_type? || covers?(ancestor.control_node, reference_node)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
86
99
|
def modifier_conditional_of(node)
|
|
87
100
|
node.each_ancestor(:if, :while, :until).find do |conditional|
|
|
88
101
|
conditional.modifier_form? && covers?(conditional.condition, node)
|
|
@@ -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.
|
|
@@ -49,6 +49,10 @@ module RuboCop
|
|
|
49
49
|
each { |f| f.file_started(file, options) }
|
|
50
50
|
end
|
|
51
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
|
+
|
|
52
56
|
def file_finished(file, offenses)
|
|
53
57
|
each { |f| f.file_finished(file, offenses) }
|
|
54
58
|
offenses
|
|
@@ -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
|
-
|
|
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,
|
|
@@ -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}
|
|
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 =
|
|
55
|
+
_height, width = output.winsize if output.tty?
|
|
54
56
|
width.nil? || width.zero? ? FALLBACK_TERMINAL_WIDTH : width
|
|
55
57
|
end
|
|
56
58
|
end
|
|
@@ -38,8 +38,9 @@ module RuboCop
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def file_finished(file, offenses)
|
|
41
|
+
line_hash_counts = Hash.new(0)
|
|
41
42
|
offenses.each do |offense|
|
|
42
|
-
@results << result_for(file, offense)
|
|
43
|
+
@results << result_for(file, offense, line_hash_counts)
|
|
43
44
|
end
|
|
44
45
|
end
|
|
45
46
|
|
|
@@ -67,14 +68,14 @@ module RuboCop
|
|
|
67
68
|
}
|
|
68
69
|
end
|
|
69
70
|
|
|
70
|
-
def result_for(file, offense)
|
|
71
|
+
def result_for(file, offense, line_hash_counts)
|
|
71
72
|
result = {
|
|
72
73
|
ruleId: offense.cop_name,
|
|
73
74
|
ruleIndex: rule_index_for(file, offense),
|
|
74
75
|
level: SEVERITY_LEVELS.fetch(offense.severity.name, 'warning'),
|
|
75
76
|
message: { text: offense.message },
|
|
76
77
|
locations: [location_for(file, offense)],
|
|
77
|
-
partialFingerprints: fingerprints_for(offense)
|
|
78
|
+
partialFingerprints: fingerprints_for(offense, line_hash_counts)
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
# Suppressed offenses are only reported under `--display-suppressed`.
|
|
@@ -110,9 +111,13 @@ module RuboCop
|
|
|
110
111
|
|
|
111
112
|
# GitHub uses these to match an alert across runs. Without them it falls
|
|
112
113
|
# back to the file path, which makes alerts churn whenever a file moves.
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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}" }
|
|
116
121
|
end
|
|
117
122
|
|
|
118
123
|
def suppression_for(offense)
|
|
@@ -158,7 +163,7 @@ module RuboCop
|
|
|
158
163
|
|
|
159
164
|
def default_level_for(file, offense, cop_class)
|
|
160
165
|
severity = cop_config(file, offense)['Severity'] ||
|
|
161
|
-
(cop_class&.
|
|
166
|
+
Cop::Base::DEPARTMENT_SEVERITIES.fetch(cop_class&.department, :convention)
|
|
162
167
|
|
|
163
168
|
SEVERITY_LEVELS.fetch(severity.to_sym, 'warning')
|
|
164
169
|
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}
|
|
51
|
+
output.puts "#{total_count} Total in #{pluralize(file_count, 'file')}"
|
|
50
52
|
|
|
51
53
|
output.puts
|
|
52
54
|
end
|
data/lib/rubocop/lsp/routes.rb
CHANGED
|
@@ -25,12 +25,6 @@ module RuboCop
|
|
|
25
25
|
# via `executeCommandProvider` so it knows which commands it can invoke.
|
|
26
26
|
EXECUTE_COMMANDS = %w[rubocop.formatAutocorrects rubocop.formatAutocorrectsAll].freeze
|
|
27
27
|
|
|
28
|
-
def self.handle(name, &block)
|
|
29
|
-
define_method(:"handle_#{name}", &block)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
private_class_method :handle
|
|
33
|
-
|
|
34
28
|
def initialize(server)
|
|
35
29
|
@server = server
|
|
36
30
|
|
|
@@ -44,6 +38,29 @@ module RuboCop
|
|
|
44
38
|
method(name)
|
|
45
39
|
end
|
|
46
40
|
|
|
41
|
+
def handle_unsupported_method(request, method = request[:method])
|
|
42
|
+
@server.write(
|
|
43
|
+
id: request[:id],
|
|
44
|
+
error: LanguageServer::Protocol::Interface::ResponseError.new(
|
|
45
|
+
code: LanguageServer::Protocol::Constant::ErrorCodes::METHOD_NOT_FOUND,
|
|
46
|
+
message: "Unsupported Method: #{method}"
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
Logger.log("Unsupported Method: #{method}")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def handle_method_missing(request)
|
|
53
|
+
return unless request.key?(:id)
|
|
54
|
+
|
|
55
|
+
@server.write(id: request[:id], result: nil)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.handle(name, &block)
|
|
59
|
+
define_method(:"handle_#{name}", &block)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private_class_method :handle
|
|
63
|
+
|
|
47
64
|
handle 'initialize' do |request|
|
|
48
65
|
initialization_options = extract_initialization_options_from(request)
|
|
49
66
|
|
|
@@ -178,23 +195,6 @@ module RuboCop
|
|
|
178
195
|
# No-op, we log everything
|
|
179
196
|
end
|
|
180
197
|
|
|
181
|
-
def handle_unsupported_method(request, method = request[:method])
|
|
182
|
-
@server.write(
|
|
183
|
-
id: request[:id],
|
|
184
|
-
error: LanguageServer::Protocol::Interface::ResponseError.new(
|
|
185
|
-
code: LanguageServer::Protocol::Constant::ErrorCodes::METHOD_NOT_FOUND,
|
|
186
|
-
message: "Unsupported Method: #{method}"
|
|
187
|
-
)
|
|
188
|
-
)
|
|
189
|
-
Logger.log("Unsupported Method: #{method}")
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
def handle_method_missing(request)
|
|
193
|
-
return unless request.key?(:id)
|
|
194
|
-
|
|
195
|
-
@server.write(id: request[:id], result: nil)
|
|
196
|
-
end
|
|
197
|
-
|
|
198
198
|
private
|
|
199
199
|
|
|
200
200
|
def extract_initialization_options_from(request)
|
data/lib/rubocop/lsp/runtime.rb
CHANGED
|
@@ -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
|
-
|
|
23
|
-
|
|
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 = {
|
|
40
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
91
|
-
@processed_source =
|
|
92
|
-
[
|
|
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
|
@@ -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
|