rubocop 1.84.2 → 1.86.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/config/default.yml +99 -16
- data/config/obsoletion.yml +5 -0
- data/lib/rubocop/cache_config.rb +1 -1
- data/lib/rubocop/cli/command/auto_generate_config.rb +28 -2
- data/lib/rubocop/cli/command/list_enabled_cops_for.rb +40 -0
- data/lib/rubocop/cli/command/mcp.rb +19 -0
- data/lib/rubocop/cli/command/show_cops.rb +2 -2
- data/lib/rubocop/cli/command/show_docs_url.rb +4 -8
- data/lib/rubocop/cli/command/suggest_extensions.rb +1 -1
- data/lib/rubocop/cli.rb +7 -7
- data/lib/rubocop/comment_config.rb +12 -15
- data/lib/rubocop/config.rb +14 -10
- data/lib/rubocop/config_finder.rb +1 -1
- data/lib/rubocop/config_loader_resolver.rb +2 -1
- data/lib/rubocop/config_obsoletion/extracted_cop.rb +4 -2
- data/lib/rubocop/config_store.rb +1 -1
- data/lib/rubocop/config_validator.rb +1 -1
- data/lib/rubocop/cop/autocorrect_logic.rb +2 -1
- data/lib/rubocop/cop/correctors/condition_corrector.rb +1 -1
- data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +1 -5
- data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
- data/lib/rubocop/cop/correctors.rb +28 -0
- data/lib/rubocop/cop/documentation.rb +2 -3
- data/lib/rubocop/cop/exclude_limit.rb +31 -5
- data/lib/rubocop/cop/gemspec/require_mfa.rb +4 -4
- data/lib/rubocop/cop/internal_affairs/itblock_handler.rb +69 -0
- data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +1 -0
- data/lib/rubocop/cop/internal_affairs.rb +1 -0
- data/lib/rubocop/cop/layout/argument_alignment.rb +2 -2
- data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/dot_position.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +9 -2
- data/lib/rubocop/cop/layout/empty_line_between_defs.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 +12 -2
- data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +16 -2
- data/lib/rubocop/cop/layout/empty_lines_around_module_body.rb +16 -2
- data/lib/rubocop/cop/layout/end_alignment.rb +6 -3
- data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +7 -1
- data/lib/rubocop/cop/layout/hash_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
- data/lib/rubocop/cop/layout/line_length.rb +5 -3
- data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +9 -2
- data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +53 -3
- data/lib/rubocop/cop/layout/parameter_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_block_parameters.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_keyword.rb +3 -1
- data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +1 -0
- data/lib/rubocop/cop/lint/constant_reassignment.rb +59 -9
- data/lib/rubocop/cop/lint/constant_resolution.rb +1 -1
- data/lib/rubocop/cop/lint/data_define_override.rb +63 -0
- data/lib/rubocop/cop/lint/duplicate_methods.rb +55 -8
- data/lib/rubocop/cop/lint/empty_block.rb +1 -1
- data/lib/rubocop/cop/lint/empty_conditional_body.rb +6 -1
- data/lib/rubocop/cop/lint/empty_in_pattern.rb +8 -1
- data/lib/rubocop/cop/lint/empty_when.rb +8 -1
- data/lib/rubocop/cop/lint/interpolation_check.rb +7 -2
- data/lib/rubocop/cop/lint/next_without_accumulator.rb +2 -0
- data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +3 -1
- data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +3 -13
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +0 -9
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +23 -6
- data/lib/rubocop/cop/lint/require_relative_self_path.rb +2 -0
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +17 -0
- data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +7 -1
- data/lib/rubocop/cop/lint/syntax.rb +25 -1
- data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -0
- data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -0
- data/lib/rubocop/cop/lint/unreachable_pattern_branch.rb +113 -0
- data/lib/rubocop/cop/lint/unused_method_argument.rb +10 -0
- data/lib/rubocop/cop/lint/useless_assignment.rb +4 -9
- data/lib/rubocop/cop/lint/useless_constant_scoping.rb +4 -4
- data/lib/rubocop/cop/lint/useless_default_value_argument.rb +2 -0
- data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +35 -9
- data/lib/rubocop/cop/lint/void.rb +32 -12
- data/lib/rubocop/cop/metrics/block_nesting.rb +23 -0
- data/lib/rubocop/cop/migration/department_name.rb +12 -1
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
- data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +2 -2
- data/lib/rubocop/cop/mixin/configurable_max.rb +6 -5
- data/lib/rubocop/cop/mixin/hash_transform_method/autocorrection.rb +63 -0
- data/lib/rubocop/cop/mixin/hash_transform_method.rb +10 -60
- data/lib/rubocop/cop/mixin.rb +85 -0
- data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
- data/lib/rubocop/cop/offense.rb +8 -0
- data/lib/rubocop/cop/registry.rb +39 -37
- data/lib/rubocop/cop/security/eval.rb +15 -2
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +14 -2
- data/lib/rubocop/cop/style/accessor_grouping.rb +4 -2
- data/lib/rubocop/cop/style/alias.rb +4 -1
- data/lib/rubocop/cop/style/and_or.rb +1 -0
- data/lib/rubocop/cop/style/arguments_forwarding.rb +25 -7
- data/lib/rubocop/cop/style/array_join.rb +4 -2
- data/lib/rubocop/cop/style/ascii_comments.rb +6 -3
- data/lib/rubocop/cop/style/attr.rb +5 -2
- data/lib/rubocop/cop/style/bare_percent_literals.rb +3 -1
- data/lib/rubocop/cop/style/begin_block.rb +3 -1
- data/lib/rubocop/cop/style/block_delimiters.rb +25 -33
- data/lib/rubocop/cop/style/case_equality.rb +4 -0
- data/lib/rubocop/cop/style/class_and_module_children.rb +10 -2
- data/lib/rubocop/cop/style/collection_compact.rb +36 -16
- data/lib/rubocop/cop/style/colon_method_call.rb +3 -1
- data/lib/rubocop/cop/style/concat_array_literals.rb +2 -0
- data/lib/rubocop/cop/style/conditional_assignment.rb +0 -4
- data/lib/rubocop/cop/style/copyright.rb +22 -11
- data/lib/rubocop/cop/style/date_time.rb +2 -2
- data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +6 -1
- data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
- data/lib/rubocop/cop/style/each_with_object.rb +2 -0
- data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
- data/lib/rubocop/cop/style/empty_class_definition.rb +43 -20
- data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
- data/lib/rubocop/cop/style/encoding.rb +7 -1
- data/lib/rubocop/cop/style/end_block.rb +3 -1
- data/lib/rubocop/cop/style/endless_method.rb +8 -3
- data/lib/rubocop/cop/style/file_open.rb +84 -0
- data/lib/rubocop/cop/style/for.rb +3 -0
- data/lib/rubocop/cop/style/format_string_token.rb +29 -2
- data/lib/rubocop/cop/style/global_vars.rb +5 -2
- data/lib/rubocop/cop/style/guard_clause.rb +9 -6
- data/lib/rubocop/cop/style/hash_as_last_array_item.rb +21 -5
- data/lib/rubocop/cop/style/hash_lookup_method.rb +19 -7
- data/lib/rubocop/cop/style/hash_transform_keys.rb +17 -7
- data/lib/rubocop/cop/style/hash_transform_values.rb +17 -7
- data/lib/rubocop/cop/style/if_inside_else.rb +16 -7
- data/lib/rubocop/cop/style/if_unless_modifier.rb +14 -3
- data/lib/rubocop/cop/style/if_with_semicolon.rb +7 -5
- data/lib/rubocop/cop/style/inline_comment.rb +4 -1
- data/lib/rubocop/cop/style/ip_addresses.rb +1 -2
- data/lib/rubocop/cop/style/magic_comment_format.rb +2 -2
- data/lib/rubocop/cop/style/map_join.rb +123 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +5 -3
- data/lib/rubocop/cop/style/module_member_existence_check.rb +7 -14
- data/lib/rubocop/cop/style/multiline_if_then.rb +3 -1
- data/lib/rubocop/cop/style/mutable_constant.rb +1 -1
- data/lib/rubocop/cop/style/nil_comparison.rb +2 -3
- data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
- data/lib/rubocop/cop/style/non_nil_check.rb +5 -11
- data/lib/rubocop/cop/style/not.rb +2 -0
- data/lib/rubocop/cop/style/numeric_literals.rb +3 -2
- data/lib/rubocop/cop/style/one_class_per_file.rb +115 -0
- data/lib/rubocop/cop/style/one_line_conditional.rb +4 -3
- data/lib/rubocop/cop/style/parallel_assignment.rb +4 -0
- data/lib/rubocop/cop/style/partition_instead_of_double_select.rb +270 -0
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -0
- data/lib/rubocop/cop/style/predicate_with_kind.rb +84 -0
- data/lib/rubocop/cop/style/proc.rb +3 -2
- data/lib/rubocop/cop/style/raise_args.rb +1 -1
- data/lib/rubocop/cop/style/reduce_to_hash.rb +200 -0
- data/lib/rubocop/cop/style/redundant_begin.rb +3 -3
- data/lib/rubocop/cop/style/redundant_each.rb +3 -3
- data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -1
- data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +26 -10
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +16 -0
- data/lib/rubocop/cop/style/redundant_min_max_by.rb +93 -0
- data/lib/rubocop/cop/style/redundant_parentheses.rb +25 -22
- data/lib/rubocop/cop/style/redundant_percent_q.rb +4 -1
- data/lib/rubocop/cop/style/redundant_return.rb +3 -1
- data/lib/rubocop/cop/style/redundant_self.rb +2 -2
- data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +0 -5
- data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +114 -0
- data/lib/rubocop/cop/style/regexp_literal.rb +29 -0
- data/lib/rubocop/cop/style/safe_navigation.rb +7 -7
- data/lib/rubocop/cop/style/select_by_kind.rb +158 -0
- data/lib/rubocop/cop/style/select_by_range.rb +197 -0
- data/lib/rubocop/cop/style/select_by_regexp.rb +51 -21
- data/lib/rubocop/cop/style/semicolon.rb +2 -0
- data/lib/rubocop/cop/style/single_line_block_params.rb +2 -2
- data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -1
- data/lib/rubocop/cop/style/single_line_methods.rb +3 -1
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +4 -2
- data/lib/rubocop/cop/style/special_global_vars.rb +6 -1
- data/lib/rubocop/cop/style/symbol_proc.rb +7 -6
- data/lib/rubocop/cop/style/tally_method.rb +181 -0
- data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
- data/lib/rubocop/cop/style/trailing_method_end_statement.rb +1 -0
- data/lib/rubocop/cop/style/while_until_modifier.rb +16 -0
- data/lib/rubocop/cop/style/yoda_expression.rb +1 -1
- data/lib/rubocop/cop/team.rb +86 -35
- data/lib/rubocop/cop/variable_force/branch.rb +2 -2
- data/lib/rubocop/directive_comment.rb +2 -1
- data/lib/rubocop/formatter/disabled_config_formatter.rb +5 -2
- data/lib/rubocop/formatter/formatter_set.rb +1 -1
- data/lib/rubocop/formatter/junit_formatter.rb +1 -1
- data/lib/rubocop/formatter/simple_text_formatter.rb +0 -2
- data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
- data/lib/rubocop/formatter.rb +22 -21
- data/lib/rubocop/lsp/diagnostic.rb +1 -0
- data/lib/rubocop/lsp/routes.rb +10 -3
- data/lib/rubocop/lsp/runtime.rb +1 -2
- data/lib/rubocop/mcp/server.rb +200 -0
- data/lib/rubocop/options.rb +17 -4
- data/lib/rubocop/path_util.rb +14 -2
- data/lib/rubocop/plugin/loader.rb +1 -1
- data/lib/rubocop/result_cache.rb +22 -10
- data/lib/rubocop/rspec/cop_helper.rb +8 -0
- data/lib/rubocop/rspec/shared_contexts.rb +32 -2
- data/lib/rubocop/runner.rb +78 -51
- data/lib/rubocop/server/cache.rb +5 -7
- data/lib/rubocop/server/core.rb +2 -0
- data/lib/rubocop/target_finder.rb +14 -7
- data/lib/rubocop/target_ruby.rb +18 -12
- data/lib/rubocop/version.rb +2 -2
- data/lib/rubocop.rb +21 -96
- metadata +25 -5
|
@@ -16,6 +16,11 @@ module RuboCop
|
|
|
16
16
|
# # good
|
|
17
17
|
# x += 1 while x < 10
|
|
18
18
|
#
|
|
19
|
+
# # good
|
|
20
|
+
# while x < 10
|
|
21
|
+
# y += 1 if x.odd?
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
19
24
|
# # bad
|
|
20
25
|
# until x > 10
|
|
21
26
|
# x += 1
|
|
@@ -24,6 +29,11 @@ module RuboCop
|
|
|
24
29
|
# # good
|
|
25
30
|
# x += 1 until x > 10
|
|
26
31
|
#
|
|
32
|
+
# # good
|
|
33
|
+
# until x > 10
|
|
34
|
+
# y += 1 unless x.even?
|
|
35
|
+
# end
|
|
36
|
+
#
|
|
27
37
|
# # bad
|
|
28
38
|
# x += 100 while x < 500 # a long comment that makes code too long if it were a single line
|
|
29
39
|
#
|
|
@@ -45,6 +55,12 @@ module RuboCop
|
|
|
45
55
|
end
|
|
46
56
|
end
|
|
47
57
|
alias on_until on_while
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def non_eligible_body?(body)
|
|
62
|
+
body&.conditional? || super
|
|
63
|
+
end
|
|
48
64
|
end
|
|
49
65
|
end
|
|
50
66
|
end
|
data/lib/rubocop/cop/team.rb
CHANGED
|
@@ -11,6 +11,9 @@ module RuboCop
|
|
|
11
11
|
# (unless autocorrections happened).
|
|
12
12
|
# rubocop:disable Metrics/ClassLength
|
|
13
13
|
class Team
|
|
14
|
+
InvestigationResult = Struct.new(:report, :corrector)
|
|
15
|
+
private_constant :InvestigationResult
|
|
16
|
+
|
|
14
17
|
# @return [Team]
|
|
15
18
|
def self.new(cop_or_classes, config, options = {})
|
|
16
19
|
# Support v0 api:
|
|
@@ -89,31 +92,25 @@ module RuboCop
|
|
|
89
92
|
|
|
90
93
|
# @return [Commissioner::InvestigationReport]
|
|
91
94
|
def investigate(processed_source, offset: 0, original: processed_source)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
# To speed things up, run autocorrecting cops by themselves, and only
|
|
97
|
-
# run the other cops when no corrections are left
|
|
98
|
-
on_duty = roundup_relevant_cops(processed_source)
|
|
95
|
+
result = investigate_with_corrector(processed_source, offset: offset, original: original)
|
|
96
|
+
autocorrect(processed_source, result.corrector)
|
|
97
|
+
result.report
|
|
98
|
+
end
|
|
99
99
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
# @return [Array<Offense>]
|
|
101
|
+
def investigate_fragments(fragments, original:)
|
|
102
|
+
@updated_source_file = false
|
|
103
103
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
report = report.merge(investigate_partial(other_cops, processed_source,
|
|
109
|
-
offset: offset, original: original))
|
|
110
|
-
end
|
|
104
|
+
offenses, errors, warnings, corrector =
|
|
105
|
+
fragments.each_with_object([[], [], [], nil]) do |fragment, data|
|
|
106
|
+
investigate_fragment(fragment, original, data)
|
|
107
|
+
end
|
|
111
108
|
|
|
112
|
-
|
|
109
|
+
autocorrect(original, corrector)
|
|
110
|
+
@errors = errors
|
|
111
|
+
@warnings = warnings
|
|
113
112
|
|
|
114
|
-
|
|
115
|
-
ensure
|
|
116
|
-
@ready = false
|
|
113
|
+
offenses
|
|
117
114
|
end
|
|
118
115
|
|
|
119
116
|
# @deprecated
|
|
@@ -136,14 +133,13 @@ module RuboCop
|
|
|
136
133
|
|
|
137
134
|
private
|
|
138
135
|
|
|
139
|
-
def autocorrect(processed_source,
|
|
136
|
+
def autocorrect(processed_source, corrector)
|
|
140
137
|
@updated_source_file = false
|
|
141
138
|
return unless autocorrect?
|
|
142
|
-
return
|
|
139
|
+
return unless corrector
|
|
140
|
+
return if corrector.empty?
|
|
143
141
|
|
|
144
|
-
new_source =
|
|
145
|
-
|
|
146
|
-
return unless new_source
|
|
142
|
+
new_source = corrector.rewrite
|
|
147
143
|
|
|
148
144
|
if @options[:stdin]
|
|
149
145
|
# holds source read in from stdin, when --stdin option is used
|
|
@@ -174,6 +170,54 @@ module RuboCop
|
|
|
174
170
|
commissioner.investigate(processed_source, offset: offset, original: original)
|
|
175
171
|
end
|
|
176
172
|
|
|
173
|
+
def investigate_with_corrector(processed_source, offset:, original:)
|
|
174
|
+
be_ready
|
|
175
|
+
|
|
176
|
+
# The autocorrection process may have to be repeated multiple times
|
|
177
|
+
# until there are no corrections left to perform
|
|
178
|
+
# To speed things up, run autocorrecting cops by themselves, and only
|
|
179
|
+
# run the other cops when no corrections are left
|
|
180
|
+
on_duty = roundup_relevant_cops(processed_source)
|
|
181
|
+
|
|
182
|
+
autocorrect_cops, other_cops = on_duty.partition(&:autocorrect?)
|
|
183
|
+
report = investigate_partial(autocorrect_cops, processed_source,
|
|
184
|
+
offset: offset, original: original)
|
|
185
|
+
|
|
186
|
+
corrector = collated_corrector(report, offset: offset, original: original)
|
|
187
|
+
|
|
188
|
+
unless corrector
|
|
189
|
+
# If we corrected some errors, another round of inspection will be
|
|
190
|
+
# done, and any other offenses will be caught then, so only need
|
|
191
|
+
# to check other_cops if no correction was done
|
|
192
|
+
report = report.merge(investigate_partial(other_cops, processed_source,
|
|
193
|
+
offset: offset, original: original))
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
process_errors(processed_source.path, report.errors)
|
|
197
|
+
|
|
198
|
+
InvestigationResult.new(report, corrector)
|
|
199
|
+
ensure
|
|
200
|
+
@ready = false
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def investigate_fragment(fragment, original, data)
|
|
204
|
+
offenses, errors, warnings, corrector = data
|
|
205
|
+
result = investigate_with_corrector(
|
|
206
|
+
fragment[:processed_source],
|
|
207
|
+
offset: fragment[:offset],
|
|
208
|
+
original: original
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
offenses.concat(result.report.offenses)
|
|
212
|
+
if result.corrector
|
|
213
|
+
corrector ||= Corrector.new(original)
|
|
214
|
+
merge_corrector!(corrector, result.corrector, offset: 0)
|
|
215
|
+
data[3] = corrector
|
|
216
|
+
end
|
|
217
|
+
errors.concat(@errors)
|
|
218
|
+
warnings.concat(@warnings)
|
|
219
|
+
end
|
|
220
|
+
|
|
177
221
|
# @return [Array<cop>]
|
|
178
222
|
def roundup_relevant_cops(processed_source)
|
|
179
223
|
cops.select do |cop|
|
|
@@ -200,28 +244,35 @@ module RuboCop
|
|
|
200
244
|
cop.class.support_target_rails_version?(cop.target_rails_version)
|
|
201
245
|
end
|
|
202
246
|
|
|
203
|
-
def
|
|
247
|
+
def collated_corrector(report, offset:, original:)
|
|
248
|
+
return unless autocorrect?
|
|
249
|
+
return if report.processed_source.parser_error
|
|
250
|
+
|
|
204
251
|
corrector = collate_corrections(report, offset: offset, original: original)
|
|
205
252
|
|
|
206
|
-
corrector
|
|
253
|
+
corrector unless corrector.empty?
|
|
207
254
|
end
|
|
208
255
|
|
|
209
256
|
def collate_corrections(report, offset:, original:)
|
|
210
257
|
corrector = Corrector.new(original)
|
|
211
258
|
|
|
212
259
|
each_corrector(report) do |to_merge|
|
|
213
|
-
|
|
214
|
-
if corrector.source_buffer == to_merge.source_buffer
|
|
215
|
-
corrector.merge!(to_merge)
|
|
216
|
-
else
|
|
217
|
-
corrector.import!(to_merge, offset: offset)
|
|
218
|
-
end
|
|
219
|
-
end
|
|
260
|
+
merge_corrector!(corrector, to_merge, offset: offset)
|
|
220
261
|
end
|
|
221
262
|
|
|
222
263
|
corrector
|
|
223
264
|
end
|
|
224
265
|
|
|
266
|
+
def merge_corrector!(corrector, to_merge, offset:)
|
|
267
|
+
suppress_clobbering do
|
|
268
|
+
if corrector.source_buffer == to_merge.source_buffer
|
|
269
|
+
corrector.merge!(to_merge)
|
|
270
|
+
else
|
|
271
|
+
corrector.import!(to_merge, offset: offset)
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
|
|
225
276
|
def each_corrector(report)
|
|
226
277
|
skips = Set.new
|
|
227
278
|
report.cop_reports.each do |cop_report|
|
|
@@ -52,7 +52,8 @@ module RuboCop
|
|
|
52
52
|
def initialize(comment, cop_registry = Cop::Registry.global)
|
|
53
53
|
@comment = comment
|
|
54
54
|
@cop_registry = cop_registry
|
|
55
|
-
|
|
55
|
+
match_data = comment.text.match(DIRECTIVE_COMMENT_REGEXP)
|
|
56
|
+
@match_data = match_data&.pre_match&.match?(/\A#\s*\z/) ? nil : match_data
|
|
56
57
|
@mode, @cops = match_captures
|
|
57
58
|
end
|
|
58
59
|
|
|
@@ -131,6 +131,9 @@ module RuboCop
|
|
|
131
131
|
end
|
|
132
132
|
|
|
133
133
|
def set_max(cfg, cop_name)
|
|
134
|
+
exclude_limits = RuboCop::ExcludeLimit.read_limits(cop_name)
|
|
135
|
+
cfg[:exclude_limit] = exclude_limits unless exclude_limits.empty?
|
|
136
|
+
|
|
134
137
|
return unless cfg[:exclude_limit]
|
|
135
138
|
|
|
136
139
|
cfg.merge!(cfg[:exclude_limit]) if should_set_max?(cop_name)
|
|
@@ -192,7 +195,7 @@ module RuboCop
|
|
|
192
195
|
next unless value.is_a?(Array)
|
|
193
196
|
next if value.empty?
|
|
194
197
|
|
|
195
|
-
value.map
|
|
198
|
+
value = value.map { |v| v.nil? ? '~' : v } # Change nil back to ~ as in the YAML file.
|
|
196
199
|
output_buffer.puts "# #{param}: #{value.uniq.join(', ')}"
|
|
197
200
|
end
|
|
198
201
|
end
|
|
@@ -233,7 +236,7 @@ module RuboCop
|
|
|
233
236
|
|
|
234
237
|
def output_exclude_list(output_buffer, offending_files, cop_name)
|
|
235
238
|
require 'pathname'
|
|
236
|
-
parent = Pathname.new(
|
|
239
|
+
parent = Pathname.new(PathUtil.pwd)
|
|
237
240
|
|
|
238
241
|
output_buffer.puts ' Exclude:'
|
|
239
242
|
excludes(offending_files, cop_name, parent).each do |exclude_path|
|
|
@@ -93,7 +93,7 @@ module RuboCop
|
|
|
93
93
|
|
|
94
94
|
def classname_attribute_value(file)
|
|
95
95
|
@classname_attribute_value_cache ||= Hash.new do |hash, key|
|
|
96
|
-
hash[key] = key.delete_suffix('.rb').gsub("#{
|
|
96
|
+
hash[key] = key.delete_suffix('.rb').gsub("#{PathUtil.pwd}/", '').tr('/', '.')
|
|
97
97
|
end
|
|
98
98
|
@classname_attribute_value_cache[file]
|
|
99
99
|
end
|
|
@@ -24,7 +24,7 @@ module RuboCop
|
|
|
24
24
|
def file_finished(file, offenses)
|
|
25
25
|
return if offenses.empty?
|
|
26
26
|
|
|
27
|
-
path = Pathname.new(file).relative_path_from(Pathname.new(
|
|
27
|
+
path = Pathname.new(file).relative_path_from(Pathname.new(PathUtil.pwd))
|
|
28
28
|
@offense_counts[path] = offenses.size
|
|
29
29
|
end
|
|
30
30
|
|
data/lib/rubocop/formatter.rb
CHANGED
|
@@ -3,32 +3,33 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
# The bootstrap module for formatter.
|
|
5
5
|
module Formatter
|
|
6
|
-
|
|
6
|
+
autoload :Colorizable, 'rubocop/formatter/colorizable'
|
|
7
|
+
autoload :TextUtil, 'rubocop/formatter/text_util'
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
autoload :BaseFormatter, 'rubocop/formatter/base_formatter'
|
|
10
|
+
autoload :SimpleTextFormatter, 'rubocop/formatter/simple_text_formatter'
|
|
10
11
|
|
|
11
12
|
# relies on simple text
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
13
|
+
autoload :ClangStyleFormatter, 'rubocop/formatter/clang_style_formatter'
|
|
14
|
+
autoload :DisabledConfigFormatter, 'rubocop/formatter/disabled_config_formatter'
|
|
15
|
+
autoload :EmacsStyleFormatter, 'rubocop/formatter/emacs_style_formatter'
|
|
16
|
+
autoload :FileListFormatter, 'rubocop/formatter/file_list_formatter'
|
|
17
|
+
autoload :FuubarStyleFormatter, 'rubocop/formatter/fuubar_style_formatter'
|
|
18
|
+
autoload :GitHubActionsFormatter, 'rubocop/formatter/github_actions_formatter'
|
|
19
|
+
autoload :HTMLFormatter, 'rubocop/formatter/html_formatter'
|
|
20
|
+
autoload :JSONFormatter, 'rubocop/formatter/json_formatter'
|
|
21
|
+
autoload :JUnitFormatter, 'rubocop/formatter/junit_formatter'
|
|
22
|
+
autoload :MarkdownFormatter, 'rubocop/formatter/markdown_formatter'
|
|
23
|
+
autoload :OffenseCountFormatter, 'rubocop/formatter/offense_count_formatter'
|
|
24
|
+
autoload :PacmanFormatter, 'rubocop/formatter/pacman_formatter'
|
|
25
|
+
autoload :ProgressFormatter, 'rubocop/formatter/progress_formatter'
|
|
26
|
+
autoload :QuietFormatter, 'rubocop/formatter/quiet_formatter'
|
|
27
|
+
autoload :TapFormatter, 'rubocop/formatter/tap_formatter'
|
|
28
|
+
autoload :WorstOffendersFormatter, 'rubocop/formatter/worst_offenders_formatter'
|
|
28
29
|
|
|
29
30
|
# relies on progress formatter
|
|
30
|
-
|
|
31
|
+
autoload :AutoGenConfigFormatter, 'rubocop/formatter/auto_gen_config_formatter'
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
autoload :FormatterSet, 'rubocop/formatter/formatter_set'
|
|
33
34
|
end
|
|
34
35
|
end
|
data/lib/rubocop/lsp/routes.rb
CHANGED
|
@@ -61,9 +61,16 @@ module RuboCop
|
|
|
61
61
|
|
|
62
62
|
handle 'initialized' do |_request|
|
|
63
63
|
version = RuboCop::Version::STRING
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
# Only one JIT can be enabled at the same time, since YJIT and ZJIT are mutually exclusive.
|
|
65
|
+
jit = if Object.const_defined?('RubyVM::YJIT') && RubyVM::YJIT.enabled?
|
|
66
|
+
'+YJIT'
|
|
67
|
+
elsif Object.const_defined?('RubyVM::ZJIT') && RubyVM::ZJIT.enabled?
|
|
68
|
+
'+ZJIT'
|
|
69
|
+
else
|
|
70
|
+
''
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
Logger.log("RuboCop #{version} language server#{jit} initialized, PID #{Process.pid}")
|
|
67
74
|
end
|
|
68
75
|
|
|
69
76
|
handle 'shutdown' do |request|
|
data/lib/rubocop/lsp/runtime.rb
CHANGED
|
@@ -23,7 +23,6 @@ module RuboCop
|
|
|
23
23
|
RuboCop::LSP.enable
|
|
24
24
|
|
|
25
25
|
@runner = RuboCop::Lsp::StdinRunner.new(config_store)
|
|
26
|
-
@cop_registry = RuboCop::Cop::Registry.global.to_h
|
|
27
26
|
|
|
28
27
|
@safe_autocorrect = true
|
|
29
28
|
@lint_mode = false
|
|
@@ -63,7 +62,7 @@ module RuboCop
|
|
|
63
62
|
document_encoding,
|
|
64
63
|
offense,
|
|
65
64
|
path,
|
|
66
|
-
|
|
65
|
+
RuboCop::Cop::Registry.global.find_by_cop_name(offense.cop_name),
|
|
67
66
|
processed_source
|
|
68
67
|
).to_lsp_diagnostic(config)
|
|
69
68
|
end
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require 'mcp'
|
|
5
|
+
|
|
6
|
+
required_mcp_version = '0.6.0'
|
|
7
|
+
|
|
8
|
+
if Gem::Version.new(required_mcp_version) > Gem::Version.new(MCP::VERSION)
|
|
9
|
+
# While `mcp` is not a runtime dependency, users may have an outdated version installed.
|
|
10
|
+
warn <<~MESSAGE
|
|
11
|
+
Error: `mcp` gem version #{MCP::VERSION} was loaded, but `rubocop --mcp` requires #{required_mcp_version}.
|
|
12
|
+
- If you're using Bundler and don't yet have `gem 'mcp'` as a dependency, add it now.
|
|
13
|
+
- If you're using Bundler and already have `gem 'mcp'` as a dependency, update it to the most recent version.
|
|
14
|
+
- If you don't use Bundler, run `gem update mcp`.
|
|
15
|
+
MESSAGE
|
|
16
|
+
exit!
|
|
17
|
+
end
|
|
18
|
+
rescue LoadError => e
|
|
19
|
+
raise unless e.path == 'mcp'
|
|
20
|
+
|
|
21
|
+
warn <<~MESSAGE
|
|
22
|
+
Error: Unable to load `mcp` gem. Add `gem 'mcp', '~> 0.6'` to your Gemfile, or run `gem install mcp`.
|
|
23
|
+
MESSAGE
|
|
24
|
+
|
|
25
|
+
exit!
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
require_relative '../lsp'
|
|
29
|
+
require_relative '../lsp/runtime'
|
|
30
|
+
|
|
31
|
+
module RuboCop
|
|
32
|
+
module MCP
|
|
33
|
+
# RuboCop MCP Server.
|
|
34
|
+
# @api private
|
|
35
|
+
class Server
|
|
36
|
+
def initialize(config_store)
|
|
37
|
+
@config_store = config_store
|
|
38
|
+
@runtime = RuboCop::LSP::Runtime.new(@config_store)
|
|
39
|
+
@options = {}
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def start
|
|
43
|
+
# No `protocol_version` is specified because draft feature by default can be used.
|
|
44
|
+
server = ::MCP::Server.new(
|
|
45
|
+
name: 'rubocop_mcp_server',
|
|
46
|
+
version: RuboCop::Version::STRING,
|
|
47
|
+
tools: [inspection_tool, autocorrection_tool]
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
::MCP::Server::Transports::StdioTransport.new(server).open
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def inspection_tool
|
|
56
|
+
build_tool(
|
|
57
|
+
name: 'rubocop_inspection',
|
|
58
|
+
description: 'Inspect Ruby code for offenses. ' \
|
|
59
|
+
'Provide `source_code` to check inline code or `path` to check files.',
|
|
60
|
+
title: "RuboCop's inspection",
|
|
61
|
+
destructive_hint: false,
|
|
62
|
+
idempotent_hint: true,
|
|
63
|
+
read_only_hint: true,
|
|
64
|
+
safety_required: false
|
|
65
|
+
) do |path, source_code|
|
|
66
|
+
run_inspection(path, source_code)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def autocorrection_tool
|
|
71
|
+
build_tool(
|
|
72
|
+
name: 'rubocop_autocorrection',
|
|
73
|
+
description: 'Autocorrect RuboCop offenses in Ruby code. ' \
|
|
74
|
+
'Provide `source_code` to correct inline code or `path` to correct files. ' \
|
|
75
|
+
'Set `safety` to false to include unsafe corrections.',
|
|
76
|
+
title: "RuboCop's autocorrection",
|
|
77
|
+
destructive_hint: true,
|
|
78
|
+
idempotent_hint: false,
|
|
79
|
+
read_only_hint: false,
|
|
80
|
+
safety_required: true
|
|
81
|
+
) do |path, source_code, safety|
|
|
82
|
+
run_autocorrection(path, source_code, safety)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def run_inspection(path, source_code)
|
|
87
|
+
if source_code
|
|
88
|
+
offenses = @runtime.offenses(path || 'example.rb', source_code, source_code.encoding)
|
|
89
|
+
offenses.to_json
|
|
90
|
+
else
|
|
91
|
+
process_files(path, filter_empty: true) do |file, source|
|
|
92
|
+
offenses = @runtime.offenses(file, source, source.encoding)
|
|
93
|
+
|
|
94
|
+
{ path: PathUtil.relative_path(file), offenses: offenses }
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def run_autocorrection(path, source_code, safety)
|
|
100
|
+
command = safety ? 'rubocop.formatAutocorrects' : 'rubocop.formatAutocorrectsAll'
|
|
101
|
+
|
|
102
|
+
if source_code
|
|
103
|
+
@runtime.format(path || 'example.rb', source_code, command: command).tap do |corrected|
|
|
104
|
+
write_file(path, corrected) if path
|
|
105
|
+
end
|
|
106
|
+
else
|
|
107
|
+
process_files(path) do |file, source|
|
|
108
|
+
@runtime.format(file, source, command: command).then do |corrected|
|
|
109
|
+
write_file(file, corrected)
|
|
110
|
+
|
|
111
|
+
{ path: PathUtil.relative_path(file), corrected: source != corrected }
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def process_files(path, filter_empty: false)
|
|
118
|
+
target_finder = RuboCop::TargetFinder.new(@config_store, @options)
|
|
119
|
+
target_files = target_finder.find(path ? [path] : [], :only_recognized_file_types)
|
|
120
|
+
all_files = target_files.map { |file| yield(file, read_file(file)) }
|
|
121
|
+
files = filter_empty ? all_files.reject { |f| f[:offenses]&.empty? } : all_files
|
|
122
|
+
|
|
123
|
+
{ files: files, summary: build_summary(target_files, all_files) }.to_json
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def read_file(file)
|
|
127
|
+
config = @config_store.for_file(file)
|
|
128
|
+
RuboCop::ProcessedSource.from_file(
|
|
129
|
+
file, config.target_ruby_version, parser_engine: config.parser_engine
|
|
130
|
+
).raw_source
|
|
131
|
+
rescue Errno::ENOENT
|
|
132
|
+
raise RuboCop::Error, "No such file or directory: #{file}"
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def write_file(file, content)
|
|
136
|
+
File.write(file, content)
|
|
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}"
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# NOTE: It is useful for RuboCop's result summary to be shown in the LLM's responses
|
|
146
|
+
# during interactions, so the summary is returned in a form that is easy for the LLM
|
|
147
|
+
# to reason about. Since LLM execution is non-deterministic, it is also sensible to
|
|
148
|
+
# compute the summary deterministically at this stage.
|
|
149
|
+
def build_summary(target_files, files)
|
|
150
|
+
summary = { target_file_count: target_files.count }
|
|
151
|
+
if files.first&.key?(:offenses)
|
|
152
|
+
summary[:offense_count] = files.sum { |f| f[:offenses].size }
|
|
153
|
+
else
|
|
154
|
+
summary[:corrected_file_count] = files.count { |f| f[:corrected] }
|
|
155
|
+
end
|
|
156
|
+
summary
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
|
|
160
|
+
def build_tool(
|
|
161
|
+
name:, description:,
|
|
162
|
+
title:, destructive_hint:, idempotent_hint:, read_only_hint:, safety_required:
|
|
163
|
+
)
|
|
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
|
+
::MCP::Tool.define(
|
|
173
|
+
name: name,
|
|
174
|
+
description: description,
|
|
175
|
+
input_schema: {
|
|
176
|
+
properties: {
|
|
177
|
+
path: { type: 'string' },
|
|
178
|
+
source_code: { type: 'string' }
|
|
179
|
+
}.merge(safety_property),
|
|
180
|
+
required: required
|
|
181
|
+
}.compact,
|
|
182
|
+
annotations: {
|
|
183
|
+
title: title,
|
|
184
|
+
destructive_hint: destructive_hint,
|
|
185
|
+
idempotent_hint: idempotent_hint,
|
|
186
|
+
open_world_hint: false,
|
|
187
|
+
read_only_hint: read_only_hint
|
|
188
|
+
}
|
|
189
|
+
) do |path: nil, source_code: nil, safety: true|
|
|
190
|
+
result = yield(path, source_code, safety)
|
|
191
|
+
|
|
192
|
+
::MCP::Tool::Response.new([{ type: 'text', text: result }])
|
|
193
|
+
rescue RuboCop::Error => e
|
|
194
|
+
::MCP::Tool::Response.new([{ type: 'text', text: e.message }], error: true)
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
# rubocop:enable Metrics/MethodLength, Metrics/ParameterLists
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|