rubocop 1.89.0 → 1.90.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/config/default.yml +74 -7
- data/exe/rubocop +1 -1
- data/lib/rubocop/cached_data.rb +7 -2
- data/lib/rubocop/cli/command/execute_runner.rb +31 -0
- data/lib/rubocop/cli.rb +2 -4
- data/lib/rubocop/comment_config/directive_range.rb +32 -0
- data/lib/rubocop/comment_config/disable_next.rb +97 -0
- data/lib/rubocop/comment_config.rb +46 -30
- data/lib/rubocop/config.rb +23 -5
- data/lib/rubocop/config_loader.rb +1 -2
- data/lib/rubocop/config_loader_resolver.rb +1 -2
- data/lib/rubocop/config_validator.rb +1 -2
- data/lib/rubocop/cop/autocorrect_logic.rb +8 -0
- data/lib/rubocop/cop/base.rb +34 -2
- data/lib/rubocop/cop/corrector.rb +3 -3
- data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -2
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb +1 -2
- data/lib/rubocop/cop/layout/block_alignment.rb +1 -2
- data/lib/rubocop/cop/layout/class_structure.rb +102 -26
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -2
- data/lib/rubocop/cop/layout/empty_line_after_magic_comment.rb +63 -5
- data/lib/rubocop/cop/layout/end_alignment.rb +1 -2
- data/lib/rubocop/cop/layout/extra_spacing.rb +105 -3
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +21 -1
- data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -2
- data/lib/rubocop/cop/layout/line_length.rb +1 -2
- data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +1 -2
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +5 -5
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +1 -2
- data/lib/rubocop/cop/layout/space_around_operators.rb +7 -0
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +2 -4
- data/lib/rubocop/cop/lint/ambiguous_range.rb +1 -2
- data/lib/rubocop/cop/lint/argument_mismatch.rb +89 -0
- data/lib/rubocop/cop/lint/constant_reassignment.rb +17 -11
- data/lib/rubocop/cop/lint/cop_directive_syntax.rb +84 -8
- data/lib/rubocop/cop/lint/deprecated_reference.rb +11 -2
- data/lib/rubocop/cop/lint/duplicate_methods.rb +112 -14
- data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +1 -2
- data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +1 -2
- data/lib/rubocop/cop/lint/interpolation_check.rb +1 -2
- data/lib/rubocop/cop/lint/literal_as_condition.rb +3 -6
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +3 -6
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +25 -12
- data/lib/rubocop/cop/lint/name_typo.rb +53 -6
- data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +15 -0
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +98 -19
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +47 -7
- data/lib/rubocop/cop/lint/redundant_require_statement.rb +1 -2
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +1 -2
- data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +1 -2
- data/lib/rubocop/cop/lint/redundant_type_conversion.rb +1 -2
- data/lib/rubocop/cop/lint/redundant_with_index.rb +1 -2
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +1 -2
- data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +2 -4
- data/lib/rubocop/cop/lint/super_argument_mismatch.rb +179 -0
- data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +1 -2
- data/lib/rubocop/cop/lint/to_enum_arguments.rb +1 -2
- data/lib/rubocop/cop/lint/unreachable_code.rb +1 -2
- data/lib/rubocop/cop/lint/unused_private_method.rb +94 -17
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +1 -2
- data/lib/rubocop/cop/lint/useless_assignment.rb +1 -2
- data/lib/rubocop/cop/lint/useless_constant_scoping.rb +36 -0
- data/lib/rubocop/cop/lint/useless_method_definition.rb +1 -1
- data/lib/rubocop/cop/lint/useless_rescue.rb +1 -2
- data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +61 -7
- data/lib/rubocop/cop/lint/void.rb +1 -2
- data/lib/rubocop/cop/lint.rb +2 -0
- data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -2
- data/lib/rubocop/cop/migration/department_name.rb +1 -2
- data/lib/rubocop/cop/mixin/comments_help.rb +1 -2
- data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +1 -2
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +1 -2
- data/lib/rubocop/cop/mixin/hash_subset.rb +1 -2
- data/lib/rubocop/cop/mixin/indexed_method_arity.rb +94 -0
- data/lib/rubocop/cop/mixin/line_length_help.rb +7 -1
- data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +51 -6
- data/lib/rubocop/cop/mixin/range_help.rb +1 -2
- data/lib/rubocop/cop/mixin/statement_modifier.rb +13 -7
- data/lib/rubocop/cop/mixin/trailing_comma.rb +2 -4
- data/lib/rubocop/cop/mixin.rb +4 -0
- data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +16 -0
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +2 -4
- data/lib/rubocop/cop/naming/method_name.rb +5 -2
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -2
- data/lib/rubocop/cop/offense.rb +27 -4
- data/lib/rubocop/cop/registry.rb +34 -10
- data/lib/rubocop/cop/style/accessor_grouping.rb +1 -2
- data/lib/rubocop/cop/style/arguments_forwarding.rb +3 -6
- data/lib/rubocop/cop/style/array_first_last.rb +1 -2
- data/lib/rubocop/cop/style/bitwise_predicate.rb +1 -2
- data/lib/rubocop/cop/style/block_delimiters.rb +3 -6
- data/lib/rubocop/cop/style/case_like_if.rb +2 -4
- data/lib/rubocop/cop/style/class_and_module_children.rb +1 -2
- data/lib/rubocop/cop/style/combinable_loops.rb +1 -2
- data/lib/rubocop/cop/style/concat_array_literals.rb +1 -2
- data/lib/rubocop/cop/style/conditional_assignment.rb +1 -2
- data/lib/rubocop/cop/style/constant_visibility.rb +1 -2
- data/lib/rubocop/cop/style/copyright.rb +1 -2
- data/lib/rubocop/cop/style/directive_scope.rb +171 -0
- data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +36 -10
- data/lib/rubocop/cop/style/empty_case_condition.rb +1 -2
- data/lib/rubocop/cop/style/format_string_token.rb +1 -2
- data/lib/rubocop/cop/style/guard_clause.rb +1 -2
- data/lib/rubocop/cop/style/hash_each_methods.rb +1 -2
- data/lib/rubocop/cop/style/hash_syntax.rb +1 -2
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +2 -4
- data/lib/rubocop/cop/style/if_inside_else.rb +1 -2
- data/lib/rubocop/cop/style/if_unless_modifier.rb +1 -2
- data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +1 -2
- data/lib/rubocop/cop/style/map_to_hash.rb +1 -2
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -4
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +1 -2
- data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -2
- data/lib/rubocop/cop/style/negated_if_else_condition.rb +1 -2
- data/lib/rubocop/cop/style/negative_array_index.rb +2 -4
- data/lib/rubocop/cop/style/nil_comparison.rb +1 -2
- data/lib/rubocop/cop/style/operator_method_call.rb +1 -2
- data/lib/rubocop/cop/style/parallel_assignment.rb +1 -2
- data/lib/rubocop/cop/style/redundant_assignment.rb +1 -2
- data/lib/rubocop/cop/style/redundant_condition.rb +2 -4
- data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +1 -2
- data/lib/rubocop/cop/style/redundant_each.rb +1 -2
- data/lib/rubocop/cop/style/redundant_format.rb +2 -4
- data/lib/rubocop/cop/style/redundant_parentheses.rb +2 -4
- data/lib/rubocop/cop/style/redundant_regexp_argument.rb +1 -2
- data/lib/rubocop/cop/style/redundant_return.rb +1 -2
- data/lib/rubocop/cop/style/redundant_self_assignment.rb +1 -2
- data/lib/rubocop/cop/style/redundant_string_escape.rb +1 -2
- data/lib/rubocop/cop/style/rescue_modifier.rb +1 -2
- data/lib/rubocop/cop/style/safe_navigation.rb +1 -2
- data/lib/rubocop/cop/style/sample.rb +11 -6
- data/lib/rubocop/cop/style/select_by_regexp.rb +1 -2
- data/lib/rubocop/cop/style/semicolon.rb +2 -4
- data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -2
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +1 -2
- data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +2 -0
- data/lib/rubocop/cop/style/string_concatenation.rb +18 -5
- data/lib/rubocop/cop/style/super_arguments.rb +1 -2
- data/lib/rubocop/cop/style/symbol_proc.rb +1 -2
- data/lib/rubocop/cop/style/time_now.rb +41 -0
- data/lib/rubocop/cop/style/yoda_condition.rb +1 -2
- data/lib/rubocop/cop/style.rb +2 -0
- data/lib/rubocop/cop/team.rb +11 -3
- data/lib/rubocop/cop/util.rb +2 -4
- data/lib/rubocop/cop/variable_force/variable.rb +1 -2
- data/lib/rubocop/cops_documentation_generator.rb +3 -6
- data/lib/rubocop/directive_comment.rb +37 -3
- data/lib/rubocop/formatter/emacs_style_formatter.rb +3 -1
- data/lib/rubocop/formatter/html_formatter.rb +2 -4
- data/lib/rubocop/formatter/json_formatter.rb +11 -6
- data/lib/rubocop/formatter/junit_formatter.rb +26 -9
- data/lib/rubocop/formatter/offense_count_formatter.rb +1 -2
- data/lib/rubocop/formatter/simple_text_formatter.rb +4 -3
- data/lib/rubocop/formatter/tap_formatter.rb +3 -1
- data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -2
- data/lib/rubocop/lockfile.rb +18 -0
- data/lib/rubocop/lsp/diagnostic.rb +4 -8
- data/lib/rubocop/lsp/routes.rb +48 -19
- data/lib/rubocop/mcp/server.rb +1 -2
- data/lib/rubocop/options.rb +10 -6
- data/lib/rubocop/path_util.rb +1 -2
- data/lib/rubocop/plugin/configuration_integrator.rb +6 -3
- data/lib/rubocop/rake_task.rb +1 -2
- data/lib/rubocop/rspec/expect_offense.rb +2 -4
- data/lib/rubocop/rspec/shared_contexts.rb +4 -2
- data/lib/rubocop/runner.rb +26 -23
- data/lib/rubocop/server/cache.rb +1 -2
- data/lib/rubocop/server/cli.rb +2 -4
- data/lib/rubocop/server.rb +14 -0
- data/lib/rubocop/target_finder.rb +4 -1
- data/lib/rubocop/todo_audit.rb +111 -0
- data/lib/rubocop/version.rb +3 -5
- data/lib/rubocop.rb +3 -0
- data/lib/ruby_lsp/rubocop/addon.rb +1 -2
- metadata +13 -5
|
@@ -35,7 +35,7 @@ module RuboCop
|
|
|
35
35
|
code_actions
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
38
|
+
# rubocop:disable-next Metrics/AbcSize, Metrics/MethodLength
|
|
39
39
|
def to_lsp_diagnostic(config)
|
|
40
40
|
highlighted = @offense.highlighted_area
|
|
41
41
|
|
|
@@ -61,7 +61,6 @@ module RuboCop
|
|
|
61
61
|
}
|
|
62
62
|
)
|
|
63
63
|
end
|
|
64
|
-
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
|
65
64
|
|
|
66
65
|
private
|
|
67
66
|
|
|
@@ -82,7 +81,7 @@ module RuboCop
|
|
|
82
81
|
LanguageServer::Protocol::Interface::CodeDescription.new(href: doc_url)
|
|
83
82
|
end
|
|
84
83
|
|
|
85
|
-
# rubocop:disable Metrics/MethodLength
|
|
84
|
+
# rubocop:disable-next Metrics/MethodLength
|
|
86
85
|
def autocorrect_action
|
|
87
86
|
LanguageServer::Protocol::Interface::CodeAction.new(
|
|
88
87
|
title: "Autocorrect #{@offense.cop_name}",
|
|
@@ -101,9 +100,8 @@ module RuboCop
|
|
|
101
100
|
is_preferred: true
|
|
102
101
|
)
|
|
103
102
|
end
|
|
104
|
-
# rubocop:enable Metrics/MethodLength
|
|
105
103
|
|
|
106
|
-
# rubocop:disable Metrics/MethodLength
|
|
104
|
+
# rubocop:disable-next Metrics/MethodLength
|
|
107
105
|
def offense_replacements
|
|
108
106
|
@offense.corrector.as_replacements.map do |range, replacement|
|
|
109
107
|
LanguageServer::Protocol::Interface::TextEdit.new(
|
|
@@ -121,9 +119,8 @@ module RuboCop
|
|
|
121
119
|
)
|
|
122
120
|
end
|
|
123
121
|
end
|
|
124
|
-
# rubocop:enable Metrics/MethodLength
|
|
125
122
|
|
|
126
|
-
# rubocop:disable Metrics/MethodLength
|
|
123
|
+
# rubocop:disable-next Metrics/MethodLength
|
|
127
124
|
def disable_line_action
|
|
128
125
|
LanguageServer::Protocol::Interface::CodeAction.new(
|
|
129
126
|
title: "Disable #{@offense.cop_name} for this line",
|
|
@@ -141,7 +138,6 @@ module RuboCop
|
|
|
141
138
|
)
|
|
142
139
|
)
|
|
143
140
|
end
|
|
144
|
-
# rubocop:enable Metrics/MethodLength
|
|
145
141
|
|
|
146
142
|
def line_disable_comment
|
|
147
143
|
DisableCommentEdits.new(
|
data/lib/rubocop/lsp/routes.rb
CHANGED
|
@@ -151,25 +151,13 @@ module RuboCop
|
|
|
151
151
|
return
|
|
152
152
|
end
|
|
153
153
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
@server.write(
|
|
162
|
-
id: request[:id],
|
|
163
|
-
method: 'workspace/applyEdit',
|
|
164
|
-
params: {
|
|
165
|
-
label: label,
|
|
166
|
-
edit: {
|
|
167
|
-
changes: {
|
|
168
|
-
uri => formatted
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
)
|
|
154
|
+
# Clients such as Eglot can invoke a command without arguments, so respond with
|
|
155
|
+
# an error instead of letting the server crash on a missing URI.
|
|
156
|
+
if (uri = document_uri_from(request))
|
|
157
|
+
write_formatting_edits(request, uri: uri, command: command, label: label)
|
|
158
|
+
else
|
|
159
|
+
write_invalid_arguments_error(request, command)
|
|
160
|
+
end
|
|
173
161
|
end
|
|
174
162
|
|
|
175
163
|
handle 'textDocument/willSave' do |_request|
|
|
@@ -219,6 +207,47 @@ module RuboCop
|
|
|
219
207
|
}
|
|
220
208
|
end
|
|
221
209
|
|
|
210
|
+
def document_uri_from(request)
|
|
211
|
+
argument = request.dig(:params, :arguments, 0)
|
|
212
|
+
|
|
213
|
+
argument[:uri] if argument.is_a?(Hash)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def write_formatting_edits(request, uri:, command:, label:)
|
|
217
|
+
formatted = nil
|
|
218
|
+
|
|
219
|
+
# The `workspace/executeCommand` is an LSP method triggered by intentional user actions,
|
|
220
|
+
# so the user's intention for autocorrection is respected.
|
|
221
|
+
LSP.disable { formatted = format_file(uri, command: command) }
|
|
222
|
+
|
|
223
|
+
@server.write(
|
|
224
|
+
id: request[:id],
|
|
225
|
+
method: 'workspace/applyEdit',
|
|
226
|
+
params: {
|
|
227
|
+
label: label,
|
|
228
|
+
edit: {
|
|
229
|
+
changes: {
|
|
230
|
+
uri => formatted
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def write_invalid_arguments_error(request, command)
|
|
238
|
+
message = "Missing document URI in arguments for #{command}"
|
|
239
|
+
|
|
240
|
+
@server.write(
|
|
241
|
+
id: request[:id],
|
|
242
|
+
error: LanguageServer::Protocol::Interface::ResponseError.new(
|
|
243
|
+
code: LanguageServer::Protocol::Constant::ErrorCodes::INVALID_PARAMS,
|
|
244
|
+
message: message
|
|
245
|
+
)
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
Logger.log(message)
|
|
249
|
+
end
|
|
250
|
+
|
|
222
251
|
def format_file(file_uri, command: nil)
|
|
223
252
|
unless (text = @text_cache[file_uri])
|
|
224
253
|
Logger.log("Format request arrived before text synchronized; skipping: `#{file_uri}'")
|
data/lib/rubocop/mcp/server.rb
CHANGED
|
@@ -156,7 +156,7 @@ module RuboCop
|
|
|
156
156
|
summary
|
|
157
157
|
end
|
|
158
158
|
|
|
159
|
-
# rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
|
|
159
|
+
# rubocop:disable-next Metrics/MethodLength, Metrics/ParameterLists
|
|
160
160
|
def build_tool(
|
|
161
161
|
name:, description:,
|
|
162
162
|
title:, destructive_hint:, idempotent_hint:, read_only_hint:, safety_required:
|
|
@@ -194,7 +194,6 @@ module RuboCop
|
|
|
194
194
|
::MCP::Tool::Response.new([{ type: 'text', text: e.message }], error: true)
|
|
195
195
|
end
|
|
196
196
|
end
|
|
197
|
-
# rubocop:enable Metrics/MethodLength, Metrics/ParameterLists
|
|
198
197
|
end
|
|
199
198
|
end
|
|
200
199
|
end
|
data/lib/rubocop/options.rb
CHANGED
|
@@ -51,7 +51,7 @@ module RuboCop
|
|
|
51
51
|
|
|
52
52
|
private
|
|
53
53
|
|
|
54
|
-
# rubocop:disable Metrics/AbcSize
|
|
54
|
+
# rubocop:disable-next Metrics/AbcSize
|
|
55
55
|
def define_options
|
|
56
56
|
OptionParser.new do |opts|
|
|
57
57
|
opts.banner = rainbow.wrap('Usage: rubocop [options] [file1, file2, ...]').bright
|
|
@@ -71,7 +71,6 @@ module RuboCop
|
|
|
71
71
|
add_profile_options(opts) if RUBY_ENGINE == 'ruby' && !Platform.windows?
|
|
72
72
|
end
|
|
73
73
|
end
|
|
74
|
-
# rubocop:enable Metrics/AbcSize
|
|
75
74
|
|
|
76
75
|
def add_check_options(opts) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
77
76
|
section(opts, 'Basic Options') do # rubocop:disable Metrics/BlockLength
|
|
@@ -132,6 +131,7 @@ module RuboCop
|
|
|
132
131
|
option(opts, '--display-only-fail-level-offenses')
|
|
133
132
|
option(opts, '--display-only-correctable')
|
|
134
133
|
option(opts, '--display-only-safe-correctable')
|
|
134
|
+
option(opts, '--display-suppressed')
|
|
135
135
|
end
|
|
136
136
|
end
|
|
137
137
|
|
|
@@ -173,6 +173,8 @@ module RuboCop
|
|
|
173
173
|
@options.replace(ConfigRegeneration.new.options.merge(@options))
|
|
174
174
|
end
|
|
175
175
|
|
|
176
|
+
option(opts, '--report-unused-todo-entries')
|
|
177
|
+
|
|
176
178
|
option(opts, '--exclude-limit COUNT') { @validator.validate_exclude_limit_option }
|
|
177
179
|
option(opts, '--no-exclude-limit')
|
|
178
180
|
|
|
@@ -382,7 +384,7 @@ module RuboCop
|
|
|
382
384
|
%i[only except].each { |opt| OptionsValidator.validate_cop_list(@options[opt]) }
|
|
383
385
|
end
|
|
384
386
|
|
|
385
|
-
# rubocop:disable Metrics/AbcSize
|
|
387
|
+
# rubocop:disable-next Metrics/AbcSize
|
|
386
388
|
def validate_compatibility # rubocop:disable Metrics/MethodLength
|
|
387
389
|
if only_includes_redundant_disable?
|
|
388
390
|
raise OptionArgumentError, 'Lint/RedundantCopDisableDirective cannot be used with --only.'
|
|
@@ -405,7 +407,6 @@ module RuboCop
|
|
|
405
407
|
|
|
406
408
|
raise OptionArgumentError, "Incompatible cli options: #{incompatible_options.inspect}"
|
|
407
409
|
end
|
|
408
|
-
# rubocop:enable Metrics/AbcSize
|
|
409
410
|
|
|
410
411
|
def validate_auto_gen_config
|
|
411
412
|
return if @options.key?(:auto_gen_config)
|
|
@@ -527,7 +528,7 @@ module RuboCop
|
|
|
527
528
|
|
|
528
529
|
# This module contains help texts for command line options.
|
|
529
530
|
# @api private
|
|
530
|
-
# rubocop:disable Metrics/ModuleLength
|
|
531
|
+
# rubocop:disable-next Metrics/ModuleLength
|
|
531
532
|
module OptionsHelp
|
|
532
533
|
MAX_EXCL = RuboCop::Options::DEFAULT_MAXIMUM_EXCLUSION_ITEMS.to_s
|
|
533
534
|
FORMATTER_OPTION_LIST = RuboCop::Formatter::FormatterSet::BUILTIN_FORMATTERS_FOR_KEYS.keys
|
|
@@ -545,6 +546,8 @@ module RuboCop
|
|
|
545
546
|
regenerate_todo: ['Regenerate the TODO configuration file using',
|
|
546
547
|
'the last configuration. If there is no existing',
|
|
547
548
|
'TODO file, acts like --auto-gen-config.'],
|
|
549
|
+
report_unused_todo_entries: ['Also report TODO configuration file entries that',
|
|
550
|
+
'are no longer needed, and fail if any are found.'],
|
|
548
551
|
offense_counts: ['Include offense counts in configuration',
|
|
549
552
|
'file generated by --auto-gen-config.',
|
|
550
553
|
'Default is true.'],
|
|
@@ -611,6 +614,8 @@ module RuboCop
|
|
|
611
614
|
display_only_correctable: ['Only output correctable offense messages.'],
|
|
612
615
|
display_only_safe_correctable: ['Only output safe-correctable offense messages',
|
|
613
616
|
'when combined with --display-only-correctable.'],
|
|
617
|
+
display_suppressed: ['Also output offenses suppressed by directive',
|
|
618
|
+
'comments. They do not affect the exit code.'],
|
|
614
619
|
show_cops: ['Show the given cops, or all cops by',
|
|
615
620
|
'default, and their configurations for the',
|
|
616
621
|
'current directory.',
|
|
@@ -689,5 +694,4 @@ module RuboCop
|
|
|
689
694
|
memory: 'Profile rubocop memory usage.'
|
|
690
695
|
}.freeze
|
|
691
696
|
end
|
|
692
|
-
# rubocop:enable Metrics/ModuleLength
|
|
693
697
|
end
|
data/lib/rubocop/path_util.rb
CHANGED
|
@@ -63,7 +63,7 @@ module RuboCop
|
|
|
63
63
|
end
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
-
# rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
66
|
+
# rubocop:disable-next Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
67
67
|
def match_path?(pattern, path)
|
|
68
68
|
case pattern
|
|
69
69
|
when String
|
|
@@ -89,7 +89,6 @@ module RuboCop
|
|
|
89
89
|
end
|
|
90
90
|
end
|
|
91
91
|
end
|
|
92
|
-
# rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
93
92
|
|
|
94
93
|
# Returns true for an absolute Unix or Windows path.
|
|
95
94
|
def absolute?(path)
|
|
@@ -59,7 +59,11 @@ module RuboCop
|
|
|
59
59
|
|
|
60
60
|
plugin_config.make_excludes_absolute
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
# `unset_nil: false` keeps `AllCops` keys declared with a nil value by a plugin
|
|
63
|
+
# (e.g. `TargetRailsVersion: ~` of rubocop-rails); `merge_all_cop_settings` above
|
|
64
|
+
# carries such keys into each subsequent plugin's config, where `unset_nil: true`
|
|
65
|
+
# would delete them from the combined configuration.
|
|
66
|
+
ConfigLoader.merge_with_default(plugin_config, plugin_config_path, unset_nil: false)
|
|
63
67
|
end
|
|
64
68
|
end
|
|
65
69
|
end
|
|
@@ -88,7 +92,7 @@ module RuboCop
|
|
|
88
92
|
result
|
|
89
93
|
end
|
|
90
94
|
|
|
91
|
-
# rubocop:disable Metrics/AbcSize
|
|
95
|
+
# rubocop:disable-next Metrics/AbcSize
|
|
92
96
|
def load_plugin_rubocop_config(plugin, runner_context)
|
|
93
97
|
rules = plugin.rules(runner_context)
|
|
94
98
|
|
|
@@ -105,7 +109,6 @@ module RuboCop
|
|
|
105
109
|
raise "Plugin `#{plugin_name}' failed to load with error: #{error_message}"
|
|
106
110
|
end
|
|
107
111
|
end
|
|
108
|
-
# rubocop:enable Metrics/AbcSize
|
|
109
112
|
|
|
110
113
|
# This is how we ensure "first-in wins": plugins can override AllCops settings that are
|
|
111
114
|
# set by RuboCop's default configuration, but once a plugin sets an AllCop setting, they
|
data/lib/rubocop/rake_task.rb
CHANGED
|
@@ -74,7 +74,7 @@ module RuboCop
|
|
|
74
74
|
|
|
75
75
|
def setup_subtasks(name, *args, &task_block) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
76
76
|
namespace(name) do
|
|
77
|
-
# rubocop:todo Naming/InclusiveLanguage
|
|
77
|
+
# rubocop:todo-next Naming/InclusiveLanguage
|
|
78
78
|
task(:auto_correct, *args) do |_, task_args|
|
|
79
79
|
require 'rainbow'
|
|
80
80
|
warn Rainbow(
|
|
@@ -86,7 +86,6 @@ module RuboCop
|
|
|
86
86
|
perform('--autocorrect')
|
|
87
87
|
end
|
|
88
88
|
end
|
|
89
|
-
# rubocop:enable Naming/InclusiveLanguage
|
|
90
89
|
|
|
91
90
|
desc "Autocorrect RuboCop offenses (only when it's safe)."
|
|
92
91
|
task(:autocorrect, *args) do |_, task_args|
|
|
@@ -117,7 +117,7 @@ module RuboCop
|
|
|
117
117
|
source
|
|
118
118
|
end
|
|
119
119
|
|
|
120
|
-
# rubocop:disable Metrics/AbcSize
|
|
120
|
+
# rubocop:disable-next Metrics/AbcSize
|
|
121
121
|
def expect_offense(source, file = nil, severity: nil, chomp: false, **replacements)
|
|
122
122
|
expected_annotations = parse_annotations(source, **replacements)
|
|
123
123
|
source = expected_annotations.plain_source
|
|
@@ -138,9 +138,8 @@ module RuboCop
|
|
|
138
138
|
|
|
139
139
|
@offenses
|
|
140
140
|
end
|
|
141
|
-
# rubocop:enable Metrics/AbcSize
|
|
142
141
|
|
|
143
|
-
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
|
|
142
|
+
# rubocop:disable-next Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
|
|
144
143
|
def expect_correction(correction, loop: true, source: nil)
|
|
145
144
|
if source
|
|
146
145
|
expected_annotations = parse_annotations(source, raise_error: false)
|
|
@@ -177,7 +176,6 @@ module RuboCop
|
|
|
177
176
|
expect(new_source).to eq(correction)
|
|
178
177
|
expect(@processed_source).to be_valid_syntax, 'Expected correction to be valid syntax'
|
|
179
178
|
end
|
|
180
|
-
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
|
|
181
179
|
|
|
182
180
|
def expect_no_corrections
|
|
183
181
|
raise '`expect_no_corrections` must follow `expect_offense`' unless @processed_source
|
|
@@ -6,7 +6,7 @@ require 'tmpdir'
|
|
|
6
6
|
# or stubbing Dir.pwd get a fresh value.
|
|
7
7
|
RSpec.configure { |c| c.before { RuboCop::PathUtil.reset_pwd } }
|
|
8
8
|
|
|
9
|
-
# rubocop:disable Metrics/BlockLength
|
|
9
|
+
# rubocop:disable-next Metrics/BlockLength
|
|
10
10
|
RSpec.shared_context 'isolated environment' do
|
|
11
11
|
around do |example|
|
|
12
12
|
Dir.mktmpdir do |tmpdir|
|
|
@@ -69,7 +69,6 @@ RSpec.shared_context 'isolated environment' do
|
|
|
69
69
|
end
|
|
70
70
|
end
|
|
71
71
|
end
|
|
72
|
-
# rubocop:enable Metrics/BlockLength
|
|
73
72
|
|
|
74
73
|
# Workaround for https://github.com/rubocop/rubocop/issues/12978,
|
|
75
74
|
# there should already be no gemfile in the temp directory
|
|
@@ -136,6 +135,8 @@ RSpec.shared_context 'config' do # rubocop:disable Metrics/BlockLength
|
|
|
136
135
|
|
|
137
136
|
let(:gem_versions) { {} }
|
|
138
137
|
|
|
138
|
+
let(:path_sourced_gems) { [] }
|
|
139
|
+
|
|
139
140
|
### Utilities
|
|
140
141
|
|
|
141
142
|
def source_range(range, buffer: source_buffer)
|
|
@@ -180,6 +181,7 @@ RSpec.shared_context 'config' do # rubocop:disable Metrics/BlockLength
|
|
|
180
181
|
**gem_versions.transform_values { |value| Gem::Version.new(value) }
|
|
181
182
|
}
|
|
182
183
|
)
|
|
184
|
+
allow(config).to receive(:path_sourced_gems_in_target).and_return(path_sourced_gems)
|
|
183
185
|
|
|
184
186
|
config
|
|
185
187
|
end
|
data/lib/rubocop/runner.rb
CHANGED
|
@@ -67,6 +67,8 @@ module RuboCop
|
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
def run(paths)
|
|
70
|
+
@inspection_team_by_config = {}.compare_by_identity
|
|
71
|
+
|
|
70
72
|
# Compute the cache source checksum once to avoid potential
|
|
71
73
|
# inconsistencies between workers.
|
|
72
74
|
ResultCache.source_checksum
|
|
@@ -264,7 +266,8 @@ module RuboCop
|
|
|
264
266
|
file_offense_cache(file) do
|
|
265
267
|
source, offenses = do_inspection_loop(file)
|
|
266
268
|
offenses = add_redundant_disables(file, offenses.compact.sort, source)
|
|
267
|
-
offenses.
|
|
269
|
+
offenses = offenses.reject(&:disabled?) unless @options[:display_suppressed]
|
|
270
|
+
offenses.sort.freeze
|
|
268
271
|
end
|
|
269
272
|
end
|
|
270
273
|
|
|
@@ -321,7 +324,13 @@ module RuboCop
|
|
|
321
324
|
end
|
|
322
325
|
|
|
323
326
|
def check_for_redundant_disables?(source)
|
|
324
|
-
return false if
|
|
327
|
+
return false if except_redundant_cop_disable_directive?
|
|
328
|
+
# Detached `disable-next` directives produce no disabled ranges but must
|
|
329
|
+
# still be reported as redundant.
|
|
330
|
+
if source.disabled_line_ranges.empty? &&
|
|
331
|
+
source.comment_config.detached_next_directives.empty?
|
|
332
|
+
return false
|
|
333
|
+
end
|
|
325
334
|
|
|
326
335
|
!@options[:only]
|
|
327
336
|
end
|
|
@@ -496,15 +505,8 @@ module RuboCop
|
|
|
496
505
|
|
|
497
506
|
def mobilize_team(processed_source)
|
|
498
507
|
config = @config_store.for_file(processed_source.path)
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
if @project_index
|
|
502
|
-
team.cops.each do |cop|
|
|
503
|
-
cop.project_index = @project_index
|
|
504
|
-
end
|
|
505
|
-
end
|
|
506
|
-
|
|
507
|
-
team
|
|
508
|
+
@inspection_team_by_config ||= {}.compare_by_identity
|
|
509
|
+
@inspection_team_by_config[config] ||= assemble_team(config)
|
|
508
510
|
end
|
|
509
511
|
|
|
510
512
|
def mobilized_cop_classes(config)
|
|
@@ -609,7 +611,7 @@ module RuboCop
|
|
|
609
611
|
end
|
|
610
612
|
end
|
|
611
613
|
|
|
612
|
-
# rubocop:disable Metrics/MethodLength
|
|
614
|
+
# rubocop:disable-next Metrics/MethodLength
|
|
613
615
|
def get_processed_source(file, prism_result, source: nil)
|
|
614
616
|
config = @config_store.for_file(file)
|
|
615
617
|
ruby_version = config.target_ruby_version
|
|
@@ -643,25 +645,26 @@ module RuboCop
|
|
|
643
645
|
processed_source.registry = mobilized_cop_classes(config)
|
|
644
646
|
processed_source
|
|
645
647
|
end
|
|
646
|
-
# rubocop:enable Metrics/MethodLength
|
|
647
648
|
|
|
648
649
|
# A Cop::Team instance is stateful and may change when inspecting.
|
|
649
650
|
# The "standby" team for a given config is an initialized but
|
|
650
651
|
# otherwise dormant team that can be used for config- and option-
|
|
651
652
|
# level caching in ResultCache.
|
|
652
653
|
def standby_team(config)
|
|
653
|
-
@
|
|
654
|
-
@
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
if @project_index
|
|
658
|
-
team.cops.each do |cop|
|
|
659
|
-
cop.project_index = @project_index
|
|
660
|
-
end
|
|
661
|
-
end
|
|
654
|
+
@standby_team_by_config ||= {}.compare_by_identity
|
|
655
|
+
@standby_team_by_config[config] ||= assemble_team(config)
|
|
656
|
+
end
|
|
662
657
|
|
|
663
|
-
|
|
658
|
+
def assemble_team(config)
|
|
659
|
+
team = Cop::Team.mobilize(mobilized_cop_classes(config), config, @options)
|
|
660
|
+
|
|
661
|
+
if @project_index
|
|
662
|
+
team.cops.each do |cop|
|
|
663
|
+
cop.project_index = @project_index
|
|
664
|
+
end
|
|
664
665
|
end
|
|
666
|
+
|
|
667
|
+
team
|
|
665
668
|
end
|
|
666
669
|
end
|
|
667
670
|
end
|
data/lib/rubocop/server/cache.rb
CHANGED
|
@@ -45,7 +45,7 @@ module RuboCop
|
|
|
45
45
|
@project_dir_cache_key ||= project_dir[1..].tr('/', '+')
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
-
# rubocop:disable Metrics/AbcSize
|
|
48
|
+
# rubocop:disable-next Metrics/AbcSize
|
|
49
49
|
def restart_key(args_config_file_path: nil)
|
|
50
50
|
lockfile_path = LOCKFILE_NAMES.map do |lockfile_name|
|
|
51
51
|
Pathname(project_dir).join(lockfile_name)
|
|
@@ -61,7 +61,6 @@ module RuboCop
|
|
|
61
61
|
|
|
62
62
|
Digest::SHA1.hexdigest(version_data + config_data + inherit_from_data + require_data)
|
|
63
63
|
end
|
|
64
|
-
# rubocop:enable Metrics/AbcSize
|
|
65
64
|
|
|
66
65
|
def dir
|
|
67
66
|
dir_path.tap do |d|
|
data/lib/rubocop/server/cli.rb
CHANGED
|
@@ -55,7 +55,7 @@ module RuboCop
|
|
|
55
55
|
|
|
56
56
|
private
|
|
57
57
|
|
|
58
|
-
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
|
58
|
+
# rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
|
59
59
|
def process_arguments(argv)
|
|
60
60
|
server_arguments = delete_server_argument_from(argv)
|
|
61
61
|
|
|
@@ -84,9 +84,8 @@ module RuboCop
|
|
|
84
84
|
|
|
85
85
|
STATUS_SUCCESS
|
|
86
86
|
end
|
|
87
|
-
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
|
88
87
|
|
|
89
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
|
88
|
+
# rubocop:disable-next Metrics/CyclomaticComplexity, Metrics/MethodLength
|
|
90
89
|
def run_command(server_command, detach:)
|
|
91
90
|
case server_command
|
|
92
91
|
when '--server'
|
|
@@ -107,7 +106,6 @@ module RuboCop
|
|
|
107
106
|
Server::ClientCommand::Status.new.run
|
|
108
107
|
end
|
|
109
108
|
end
|
|
110
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
|
111
109
|
|
|
112
110
|
def fetch_cache_root_path_from(arguments)
|
|
113
111
|
cache_root = arguments.detect { |argument| argument.start_with?('--cache-root') }
|
data/lib/rubocop/server.rb
CHANGED
|
@@ -17,6 +17,11 @@ module RuboCop
|
|
|
17
17
|
module Server
|
|
18
18
|
TIMEOUT = 20
|
|
19
19
|
|
|
20
|
+
# Options that start a long-lived protocol server on stdio in the current process.
|
|
21
|
+
# They must never be forwarded to the server process, which would run them as
|
|
22
|
+
# a lint request and silently produce no protocol response.
|
|
23
|
+
IN_PROCESS_PROTOCOL_OPTIONS = %w[--lsp --mcp].freeze
|
|
24
|
+
|
|
20
25
|
autoload :CLI, 'rubocop/server/cli'
|
|
21
26
|
autoload :Cache, 'rubocop/server/cache'
|
|
22
27
|
autoload :ClientCommand, 'rubocop/server/client_command'
|
|
@@ -36,6 +41,15 @@ module RuboCop
|
|
|
36
41
|
Cache.pid_running?
|
|
37
42
|
end
|
|
38
43
|
|
|
44
|
+
# Whether the invocation should be forwarded to the running server process.
|
|
45
|
+
# `--lsp` and `--mcp` always run in the current process even when the server is running:
|
|
46
|
+
# they serve a protocol on stdio, which the server's `exec` command cannot do.
|
|
47
|
+
def forward_to_server?(argv = ARGV)
|
|
48
|
+
return false unless running?
|
|
49
|
+
|
|
50
|
+
(IN_PROCESS_PROTOCOL_OPTIONS & argv).empty?
|
|
51
|
+
end
|
|
52
|
+
|
|
39
53
|
def wait_for_running_status!(expected)
|
|
40
54
|
start_time = Time.now
|
|
41
55
|
while Server.running? != expected
|
|
@@ -21,7 +21,10 @@ module RuboCop
|
|
|
21
21
|
|
|
22
22
|
args.uniq.each do |arg|
|
|
23
23
|
files += if File.directory?(arg)
|
|
24
|
-
|
|
24
|
+
# Expand the directory so that the traversal can prune directories the
|
|
25
|
+
# config excludes - the exclude patterns are absolute paths, and glued
|
|
26
|
+
# onto a relative base (`rubocop .`) they never match anything.
|
|
27
|
+
target_files_in_dir(File.expand_path(arg.chomp(File::SEPARATOR)))
|
|
25
28
|
else
|
|
26
29
|
process_explicit_path(arg, mode)
|
|
27
30
|
end
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
# Audits `.rubocop_todo.yml` for `Exclude` entries that are no longer
|
|
5
|
+
# needed: files a cop would not flag anymore, or files that no longer
|
|
6
|
+
# exist. Used by the `--report-unused-todo-entries` option.
|
|
7
|
+
# @api private
|
|
8
|
+
class TodoAudit
|
|
9
|
+
Entry = Struct.new(:cop_name, :path)
|
|
10
|
+
|
|
11
|
+
def initialize(config_store, options)
|
|
12
|
+
@config_store = config_store
|
|
13
|
+
@options = options
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @return [Array<Entry>, nil] the unused entries, or `nil` when there is
|
|
17
|
+
# no todo file to audit
|
|
18
|
+
def unused_entries
|
|
19
|
+
return nil unless todo_path
|
|
20
|
+
|
|
21
|
+
entries = todo_exclude_entries
|
|
22
|
+
return [] if entries.empty?
|
|
23
|
+
|
|
24
|
+
offending_cops = offending_cops_without_todo(entries)
|
|
25
|
+
entries.reject { |entry| offending_cops[absolute(entry.path)]&.include?(entry.cop_name) }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def todo_file
|
|
29
|
+
CLI::Command::AutoGenerateConfig::AUTO_GENERATED_FILE
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def todo_path
|
|
35
|
+
return @todo_path if defined?(@todo_path)
|
|
36
|
+
|
|
37
|
+
path = File.expand_path(todo_file)
|
|
38
|
+
@todo_path = File.exist?(path) ? path : nil
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def todo_exclude_entries
|
|
42
|
+
yaml = YAML.safe_load_file(todo_path, permitted_classes: [Regexp, Symbol], aliases: true)
|
|
43
|
+
return [] unless yaml.is_a?(Hash)
|
|
44
|
+
|
|
45
|
+
yaml.flat_map do |cop_name, cop_config|
|
|
46
|
+
next [] unless auditable_cop?(cop_name, cop_config)
|
|
47
|
+
|
|
48
|
+
cop_config['Exclude'].grep(String).map { |path| Entry.new(cop_name, path) }
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Only registered cops with an `Exclude` list can be audited - for an
|
|
53
|
+
# unknown cop (e.g. from an extension that is not loaded in this run)
|
|
54
|
+
# the absence of offenses proves nothing.
|
|
55
|
+
def auditable_cop?(cop_name, cop_config)
|
|
56
|
+
cop_name.include?('/') &&
|
|
57
|
+
cop_config.is_a?(Hash) &&
|
|
58
|
+
cop_config['Exclude'].is_a?(Array) &&
|
|
59
|
+
Cop::Registry.global.contains_cop_matching?([cop_name])
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Inspects every file the todo entries mention, with the todo exclusions
|
|
63
|
+
# subtracted from the configuration, and returns the names of the cops
|
|
64
|
+
# that still report offenses, keyed by absolute file path.
|
|
65
|
+
def offending_cops_without_todo(entries)
|
|
66
|
+
files = entries.map { |entry| absolute(entry.path) }.uniq.select { |file| File.file?(file) }
|
|
67
|
+
|
|
68
|
+
files.to_h do |file|
|
|
69
|
+
offenses = inspect_file(file, entries)
|
|
70
|
+
[file, offenses.to_set(&:cop_name)]
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def inspect_file(file, entries)
|
|
75
|
+
config = audit_config(@config_store.for_file(file), entries)
|
|
76
|
+
team = Cop::Team.mobilize(Cop::Registry.global, config, @options.merge(autocorrect: false))
|
|
77
|
+
|
|
78
|
+
processed_source = ProcessedSource.from_file(
|
|
79
|
+
file, config.target_ruby_version, parser_engine: config.parser_engine
|
|
80
|
+
)
|
|
81
|
+
processed_source.config = config
|
|
82
|
+
processed_source.registry = Cop::Registry.global
|
|
83
|
+
|
|
84
|
+
team.investigate(processed_source).offenses.reject(&:disabled?)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# A copy of the configuration with the todo exclusions removed for the
|
|
88
|
+
# audited cops, so their offenses in the listed files become visible.
|
|
89
|
+
def audit_config(config, entries)
|
|
90
|
+
hash = config.to_hash.dup
|
|
91
|
+
|
|
92
|
+
entries.group_by(&:cop_name).each do |cop_name, cop_entries|
|
|
93
|
+
cop_config = hash[cop_name]
|
|
94
|
+
next unless cop_config.is_a?(Hash) && cop_config['Exclude'].is_a?(Array)
|
|
95
|
+
|
|
96
|
+
hash[cop_name] = subtract_excludes(cop_config, cop_entries)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
Config.create(hash, config.loaded_path, check: false)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def subtract_excludes(cop_config, cop_entries)
|
|
103
|
+
removals = cop_entries.map { |entry| absolute(entry.path) }
|
|
104
|
+
cop_config.merge('Exclude' => cop_config['Exclude'] - removals)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def absolute(path)
|
|
108
|
+
File.expand_path(path, File.dirname(todo_path))
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|