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
|
@@ -27,6 +27,13 @@ module RuboCop
|
|
|
27
27
|
# alone is not reported, since the index does not see gems or the
|
|
28
28
|
# standard library.
|
|
29
29
|
#
|
|
30
|
+
# For the same reason, a namespace whose root segment names a gem in the
|
|
31
|
+
# bundle (`Flipper` for the `flipper` gem) is left alone: a project that
|
|
32
|
+
# reopens it (`module Flipper; module Adapters; ...`) makes it resolve in
|
|
33
|
+
# the index, while the members the gem itself defines stay invisible and
|
|
34
|
+
# would look like typos. Enabling `AllCops/ProjectIndexIncludesGems`
|
|
35
|
+
# indexes those sources and restores the check.
|
|
36
|
+
#
|
|
30
37
|
# Names that appear as symbols or inside string literals in the same
|
|
31
38
|
# file are never reported, since they usually belong to runtime
|
|
32
39
|
# definitions the index cannot see (`stub_const`, `const_set`,
|
|
@@ -129,26 +136,61 @@ module RuboCop
|
|
|
129
136
|
end
|
|
130
137
|
|
|
131
138
|
def constant_typo_suggestion(node)
|
|
132
|
-
return nil if literal_names.include?(node.short_name.to_s)
|
|
133
|
-
|
|
134
139
|
namespace = resolve_constant_in_index(node.namespace)
|
|
135
140
|
return nil unless namespace.is_a?(Rubydex::Namespace)
|
|
136
141
|
return nil if namespace.find_member(node.short_name.to_s)
|
|
142
|
+
return nil unless complete_index_members?(namespace)
|
|
143
|
+
return nil if literal_names.include?(node.short_name.to_s)
|
|
137
144
|
|
|
138
145
|
spell_check(node.short_name, constant_member_names(namespace))
|
|
139
146
|
rescue StandardError
|
|
140
147
|
nil
|
|
141
148
|
end
|
|
142
149
|
|
|
150
|
+
# Whether the index can be trusted to list everything the namespace
|
|
151
|
+
# defines. A name missing from an incomplete member list is not
|
|
152
|
+
# evidence of a typo, so both gaps have to be ruled out first:
|
|
153
|
+
# an ancestor that does not resolve may contribute the name, and so
|
|
154
|
+
# may a gem that reopens the namespace.
|
|
155
|
+
def complete_index_members?(declaration)
|
|
156
|
+
fully_resolved_index_ancestry?(declaration) && !gem_owned_namespace?(declaration)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Whether the namespace's root segment names a gem in the bundle,
|
|
160
|
+
# which is then free to define members the index never sees: gem
|
|
161
|
+
# sources are only indexed when `AllCops/ProjectIndexIncludesGems`
|
|
162
|
+
# is enabled. A project that reopens such a namespace to add its own
|
|
163
|
+
# members (`module Flipper; module Adapters; ...`) makes it resolve
|
|
164
|
+
# in the index while most of what it holds stays invisible.
|
|
165
|
+
def gem_owned_namespace?(declaration)
|
|
166
|
+
return false if config.for_all_cops['ProjectIndexIncludesGems']
|
|
167
|
+
|
|
168
|
+
root = declaration.name.to_s.split('::').first.to_s
|
|
169
|
+
bundled_gem_namespaces.include?(root.downcase)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Gem names normalized towards the constant they conventionally
|
|
173
|
+
# provide: `flipper` for `Flipper`, `activerecord` for `ActiveRecord`.
|
|
174
|
+
def bundled_gem_namespaces
|
|
175
|
+
@bundled_gem_namespaces ||= bundled_gem_names.to_set { |name| name.delete('-_').downcase }
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# The bundle's gems except those sourced from a local path, whose code
|
|
179
|
+
# lives in the project and is indexed like the rest of it — among them
|
|
180
|
+
# the project's own gem, which its lockfile lists in the `PATH` section.
|
|
181
|
+
def bundled_gem_names
|
|
182
|
+
(config.gem_versions_in_target || {}).keys - (config.path_sourced_gems_in_target || [])
|
|
183
|
+
end
|
|
184
|
+
|
|
143
185
|
# Writers are indexed under the reader's name, so setter calls are
|
|
144
186
|
# verified and spell-checked through the base name.
|
|
145
187
|
def method_typo_suggestion(node)
|
|
146
188
|
setter = setter_call?(node)
|
|
147
189
|
base = setter ? node.method_name.to_s.delete_suffix('=') : node.method_name.to_s
|
|
148
|
-
return nil if literal_names.include?(base)
|
|
149
190
|
|
|
150
191
|
declaration = unknown_method_owner(node, base)
|
|
151
192
|
return nil unless declaration
|
|
193
|
+
return nil if literal_names.include?(base)
|
|
152
194
|
|
|
153
195
|
suggestion = spell_check(base, method_member_names(declaration))
|
|
154
196
|
suggestion && setter ? "#{suggestion}=" : suggestion
|
|
@@ -156,13 +198,15 @@ module RuboCop
|
|
|
156
198
|
nil
|
|
157
199
|
end
|
|
158
200
|
|
|
159
|
-
# The receiver's declaration when it resolves in the index,
|
|
160
|
-
#
|
|
201
|
+
# The receiver's declaration when it resolves in the index, everything
|
|
202
|
+
# it can inherit or be reopened with is visible there, and the method
|
|
203
|
+
# is not found — nil otherwise.
|
|
161
204
|
def unknown_method_owner(node, base)
|
|
162
205
|
declaration = resolve_constant_in_index(node.receiver)
|
|
163
206
|
return nil unless declaration.is_a?(Rubydex::Namespace)
|
|
164
207
|
return nil if responds_in_index?(declaration, node.method_name.to_s, base)
|
|
165
208
|
return nil unless fully_resolved_index_ancestry?(declaration)
|
|
209
|
+
return nil if gem_owned_namespace?(declaration)
|
|
166
210
|
|
|
167
211
|
declaration
|
|
168
212
|
end
|
|
@@ -191,7 +235,10 @@ module RuboCop
|
|
|
191
235
|
if literal.sym_type?
|
|
192
236
|
names << literal.value.to_s
|
|
193
237
|
else
|
|
194
|
-
|
|
238
|
+
# `scan` raises on binary string literals with invalid byte sequences.
|
|
239
|
+
value = literal.value
|
|
240
|
+
value = value.scrub unless value.valid_encoding?
|
|
241
|
+
value.scan(LITERAL_IDENTIFIER_PATTERN) { |token| names << token }
|
|
195
242
|
end
|
|
196
243
|
end
|
|
197
244
|
end
|
|
@@ -53,6 +53,8 @@ module RuboCop
|
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
def after_send(node)
|
|
56
|
+
return if backrefs_unaffected?(node)
|
|
57
|
+
|
|
56
58
|
@valid_ref = nil
|
|
57
59
|
|
|
58
60
|
if regexp_first_argument?(node)
|
|
@@ -111,6 +113,19 @@ module RuboCop
|
|
|
111
113
|
end
|
|
112
114
|
end
|
|
113
115
|
|
|
116
|
+
# A call such as `hash[:key]` or `array[0]` cannot involve a regexp match:
|
|
117
|
+
# a non-string literal cannot act as a pattern (whereas `sub`, `gsub`,
|
|
118
|
+
# and `match` treat a string one as a pattern and do set backreferences),
|
|
119
|
+
# and `$~` is frame-local, so a user-defined method cannot change
|
|
120
|
+
# the caller's backreferences either.
|
|
121
|
+
def backrefs_unaffected?(send_node)
|
|
122
|
+
first_argument = send_node.first_argument
|
|
123
|
+
return false unless first_argument&.basic_literal?
|
|
124
|
+
return false if first_argument.str_type?
|
|
125
|
+
|
|
126
|
+
!regexp_receiver?(send_node)
|
|
127
|
+
end
|
|
128
|
+
|
|
114
129
|
def regexp_first_argument?(send_node)
|
|
115
130
|
send_node.first_argument&.regexp_type? \
|
|
116
131
|
&& REGEXP_ARGUMENT_METHODS.include?(send_node.method_name)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# The Lint/RedundantCopDisableDirective cop needs to be disabled so as
|
|
4
4
|
# to be able to provide a (bad) example of a redundant disable.
|
|
5
|
-
# rubocop:disable Lint/RedundantCopDisableDirective
|
|
5
|
+
# rubocop:disable-next Lint/RedundantCopDisableDirective
|
|
6
6
|
module RuboCop
|
|
7
7
|
module Cop
|
|
8
8
|
module Lint
|
|
@@ -104,17 +104,35 @@ module RuboCop
|
|
|
104
104
|
end
|
|
105
105
|
|
|
106
106
|
def each_redundant_disable(&block)
|
|
107
|
+
each_detached_next_directive(&block)
|
|
108
|
+
|
|
107
109
|
cop_disabled_line_ranges.each do |cop, line_ranges|
|
|
110
|
+
# A pending cop that is not enabled in this run produces no
|
|
111
|
+
# offenses, so its directives cannot be judged - they typically
|
|
112
|
+
# prepare the code for the moment the cop gets enabled.
|
|
113
|
+
next if pending_cop_not_run?(cop)
|
|
114
|
+
|
|
108
115
|
each_already_disabled(cop, line_ranges, &block)
|
|
109
116
|
each_line_range(cop, line_ranges, &block)
|
|
110
117
|
end
|
|
111
118
|
end
|
|
112
119
|
|
|
120
|
+
def pending_cop_not_run?(cop)
|
|
121
|
+
# `Config#for_cop` corrects wrongly-namespaced names, but a name that
|
|
122
|
+
# is not registered as written never suppresses anything, so it is
|
|
123
|
+
# reported as unknown rather than exempted via the corrected cop.
|
|
124
|
+
return false unless all_cop_names.include?(cop)
|
|
125
|
+
|
|
126
|
+
cop_cfg = config.for_cop(cop)
|
|
127
|
+
cop_cfg['Enabled'] == 'pending' &&
|
|
128
|
+
!processed_source.registry.enabled_pending_cop?(cop_cfg, config, cop)
|
|
129
|
+
end
|
|
130
|
+
|
|
113
131
|
def each_line_range(cop, line_ranges)
|
|
114
132
|
line_ranges.each_with_index do |line_range, line_range_index|
|
|
115
133
|
next if should_skip_line_range?(cop, line_range)
|
|
116
134
|
|
|
117
|
-
comment =
|
|
135
|
+
comment = directive_comment(line_range)
|
|
118
136
|
next if skip_directive?(comment)
|
|
119
137
|
|
|
120
138
|
next_range = line_ranges[line_range_index + 1]
|
|
@@ -127,6 +145,24 @@ module RuboCop
|
|
|
127
145
|
ignore_offense?(line_range) || expected_final_disable?(cop, line_range)
|
|
128
146
|
end
|
|
129
147
|
|
|
148
|
+
# A range opened by a `disable-next` directive starts at the statement,
|
|
149
|
+
# not at the directive comment above it, so ask the range itself.
|
|
150
|
+
def directive_comment(line_range)
|
|
151
|
+
if line_range.respond_to?(:directive)
|
|
152
|
+
line_range.directive.comment
|
|
153
|
+
else
|
|
154
|
+
processed_source.comment_at_line(line_range.begin)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# A `disable-next` with no statement attached (or misplaced at the end
|
|
159
|
+
# of a code line) suppresses nothing, so it is redundant by definition.
|
|
160
|
+
def each_detached_next_directive
|
|
161
|
+
processed_source.comment_config.detached_next_directives.each do |directive|
|
|
162
|
+
directive.raw_cop_names.each { |cop| yield directive.comment, cop }
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
130
166
|
def skip_directive?(comment)
|
|
131
167
|
directive = DirectiveComment.new(comment)
|
|
132
168
|
directive.push? || directive.pop?
|
|
@@ -142,7 +178,7 @@ module RuboCop
|
|
|
142
178
|
end
|
|
143
179
|
end
|
|
144
180
|
|
|
145
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
181
|
+
# rubocop:disable-next Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
146
182
|
def each_already_disabled(cop, line_ranges)
|
|
147
183
|
line_ranges.each_cons(2) do |previous_range, range|
|
|
148
184
|
next if ignore_offense?(range)
|
|
@@ -152,7 +188,7 @@ module RuboCop
|
|
|
152
188
|
# whether there are offenses or not.
|
|
153
189
|
next unless followed_ranges?(previous_range, range)
|
|
154
190
|
|
|
155
|
-
comment =
|
|
191
|
+
comment = directive_comment(range)
|
|
156
192
|
|
|
157
193
|
next unless comment
|
|
158
194
|
# Comments disabling all cops don't count since it's reasonable
|
|
@@ -170,7 +206,6 @@ module RuboCop
|
|
|
170
206
|
yield comment, redundant if redundant
|
|
171
207
|
end
|
|
172
208
|
end
|
|
173
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
174
209
|
|
|
175
210
|
def find_redundant_cop(cop, range)
|
|
176
211
|
cop_offenses = offenses_to_check.select { |offense| offense.cop_name == cop }
|
|
@@ -198,7 +233,11 @@ module RuboCop
|
|
|
198
233
|
end
|
|
199
234
|
|
|
200
235
|
def range_with_offense?(range, offenses = offenses_to_check)
|
|
201
|
-
|
|
236
|
+
# An offense is covered when the disabled range overlaps any line of
|
|
237
|
+
# its span, mirroring how offenses are suppressed.
|
|
238
|
+
offenses.none? do |offense|
|
|
239
|
+
offense.line <= range.end && offense.location.last_line >= range.begin
|
|
240
|
+
end
|
|
202
241
|
end
|
|
203
242
|
|
|
204
243
|
def all_disabled?(comment)
|
|
@@ -244,14 +283,28 @@ module RuboCop
|
|
|
244
283
|
location = DirectiveComment.new(comment).range
|
|
245
284
|
cop_names = cops.sort.map { |c| describe(c) }.join(', ')
|
|
246
285
|
|
|
286
|
+
# An unknown cop may just not be loaded in this run (e.g. a custom
|
|
287
|
+
# cop whose configuration failed to load) - removing its directive
|
|
288
|
+
# would destroy something that cannot be restored, so only report.
|
|
289
|
+
# A misplaced EOL `disable-next` should be moved, not deleted.
|
|
290
|
+
if any_unknown_cop?(cops) || misplaced_next_directive?(comment)
|
|
291
|
+
return add_offense(location, message: message(cop_names))
|
|
292
|
+
end
|
|
293
|
+
|
|
247
294
|
add_offense(location, message: message(cop_names)) do |corrector|
|
|
248
|
-
|
|
295
|
+
remove_entire_comment(corrector, comment)
|
|
296
|
+
end
|
|
297
|
+
end
|
|
249
298
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
299
|
+
def remove_entire_comment(corrector, comment)
|
|
300
|
+
# Take the `--` reason with the directive; `leave_free_comment?` keeps anything else.
|
|
301
|
+
directive_range = DirectiveComment.new(comment).range_with_reason
|
|
302
|
+
range = comment_range_with_surrounding_space(directive_range, comment.source_range)
|
|
303
|
+
|
|
304
|
+
if leave_free_comment?(comment, range)
|
|
305
|
+
corrector.replace(range, ' # ')
|
|
306
|
+
else
|
|
307
|
+
corrector.remove(range)
|
|
255
308
|
end
|
|
256
309
|
end
|
|
257
310
|
|
|
@@ -261,14 +314,35 @@ module RuboCop
|
|
|
261
314
|
ranges = cop_ranges.map { |_, r| r }
|
|
262
315
|
|
|
263
316
|
cop_ranges.each do |cop, range|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
317
|
+
add_offense_for_cop_in_list(cop, range, ranges)
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def add_offense_for_cop_in_list(cop, range, ranges)
|
|
322
|
+
cop_name = describe(cop)
|
|
323
|
+
return add_offense(range, message: message(cop_name)) if unknown_cop?(cop)
|
|
324
|
+
|
|
325
|
+
add_offense(range, message: message(cop_name)) do |corrector|
|
|
326
|
+
corrector.remove(directive_range_in_list(range, ranges))
|
|
269
327
|
end
|
|
270
328
|
end
|
|
271
329
|
|
|
330
|
+
def any_unknown_cop?(cops)
|
|
331
|
+
cops.any? { |cop| unknown_cop?(cop) }
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def misplaced_next_directive?(comment)
|
|
335
|
+
directive = DirectiveComment.new(comment)
|
|
336
|
+
directive.disable_next? &&
|
|
337
|
+
!processed_source.comment_config.comment_only_line?(directive.line_number)
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def unknown_cop?(cop)
|
|
341
|
+
return false if cop == 'all' || department_marker?(cop)
|
|
342
|
+
|
|
343
|
+
!all_cop_names.include?(cop)
|
|
344
|
+
end
|
|
345
|
+
|
|
272
346
|
def leave_free_comment?(comment, range)
|
|
273
347
|
free_comment = comment.text.gsub(range.source.strip, '')
|
|
274
348
|
|
|
@@ -303,7 +377,13 @@ module RuboCop
|
|
|
303
377
|
|
|
304
378
|
SIMILAR_COP_NAMES_CACHE = Hash.new do |hash, cop_name|
|
|
305
379
|
hash[:all_cop_names] = Registry.global.names unless hash.key?(:all_cop_names)
|
|
306
|
-
|
|
380
|
+
# A registered cop with the same base name is a better suggestion
|
|
381
|
+
# than anything spelling similarity can find - the most common
|
|
382
|
+
# mistake is qualifying a cop with the wrong department.
|
|
383
|
+
basename = "/#{cop_name.split('/').last}"
|
|
384
|
+
same_basename = hash[:all_cop_names].find { |name| name.end_with?(basename) }
|
|
385
|
+
hash[cop_name] =
|
|
386
|
+
same_basename || NameSimilarity.find_similar_name(cop_name, hash[:all_cop_names])
|
|
307
387
|
end
|
|
308
388
|
private_constant :SIMILAR_COP_NAMES_CACHE
|
|
309
389
|
|
|
@@ -344,4 +424,3 @@ module RuboCop
|
|
|
344
424
|
end
|
|
345
425
|
end
|
|
346
426
|
end
|
|
347
|
-
# rubocop:enable Lint/RedundantCopDisableDirective
|
|
@@ -30,21 +30,55 @@ module RuboCop
|
|
|
30
30
|
# foo = "1"
|
|
31
31
|
# # rubocop:enable all
|
|
32
32
|
# baz
|
|
33
|
+
#
|
|
34
|
+
# # bad
|
|
35
|
+
# foo = 1
|
|
36
|
+
# # rubocop:pop
|
|
37
|
+
#
|
|
38
|
+
# # good
|
|
39
|
+
# # rubocop:push -Style/StringLiterals
|
|
40
|
+
# foo = "1"
|
|
41
|
+
# # rubocop:pop
|
|
33
42
|
class RedundantCopEnableDirective < Base
|
|
34
43
|
include RangeHelp
|
|
35
44
|
include SurroundingSpace
|
|
36
45
|
extend AutoCorrector
|
|
37
46
|
|
|
38
47
|
MSG = 'Unnecessary enabling of %<cop>s.'
|
|
48
|
+
MSG_ORPHAN_POP = 'Unnecessary `rubocop:pop` without a matching `rubocop:push`.'
|
|
39
49
|
|
|
40
50
|
def on_new_investigation
|
|
41
|
-
return if processed_source.blank?
|
|
51
|
+
return if processed_source.blank?
|
|
42
52
|
|
|
53
|
+
source = processed_source.raw_source
|
|
54
|
+
check_extra_enables if source.include?('enable')
|
|
55
|
+
check_orphan_pops if source.include?('pop')
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def check_extra_enables
|
|
43
61
|
offenses = processed_source.comment_config.extra_enabled_comments
|
|
44
62
|
offenses.each { |comment, cop_names| register_offense(comment, cop_names) }
|
|
45
63
|
end
|
|
46
64
|
|
|
47
|
-
|
|
65
|
+
def check_orphan_pops
|
|
66
|
+
push_depth = 0
|
|
67
|
+
processed_source.comments.each do |comment|
|
|
68
|
+
directive = DirectiveComment.new(comment)
|
|
69
|
+
if directive.push?
|
|
70
|
+
push_depth += 1
|
|
71
|
+
elsif directive.pop?
|
|
72
|
+
push_depth.zero? ? register_orphan_pop(directive) : push_depth -= 1
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def register_orphan_pop(directive)
|
|
78
|
+
add_offense(directive.range, message: MSG_ORPHAN_POP) do |corrector|
|
|
79
|
+
corrector.remove(range_with_surrounding_space(directive.range, side: :right))
|
|
80
|
+
end
|
|
81
|
+
end
|
|
48
82
|
|
|
49
83
|
def register_offense(comment, cop_names)
|
|
50
84
|
directive = DirectiveComment.new(comment)
|
|
@@ -55,15 +89,21 @@ module RuboCop
|
|
|
55
89
|
range_of_offense(comment, name),
|
|
56
90
|
message: format(MSG, cop: all_or_name(name))
|
|
57
91
|
) do |corrector|
|
|
58
|
-
|
|
59
|
-
corrector.remove(range_with_surrounding_space(directive.range, side: :right))
|
|
60
|
-
else
|
|
61
|
-
corrector.remove(range_with_comma(comment, name))
|
|
62
|
-
end
|
|
92
|
+
corrector.remove(removal_range(directive, comment, cop_names, name))
|
|
63
93
|
end
|
|
64
94
|
end
|
|
65
95
|
end
|
|
66
96
|
|
|
97
|
+
# When every cop on the directive is redundant the whole comment goes, `--` reason
|
|
98
|
+
# included; otherwise just the one cop is spliced out of the list.
|
|
99
|
+
def removal_range(directive, comment, cop_names, name)
|
|
100
|
+
if directive.match?(cop_names)
|
|
101
|
+
range_with_surrounding_space(directive.range_with_reason, side: :right)
|
|
102
|
+
else
|
|
103
|
+
range_with_comma(comment, name)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
67
107
|
def range_of_offense(comment, name)
|
|
68
108
|
start_pos = comment_start(comment) + cop_name_indention(comment, name)
|
|
69
109
|
range_between(start_pos, start_pos + name.size)
|
|
@@ -63,7 +63,7 @@ module RuboCop
|
|
|
63
63
|
|
|
64
64
|
private
|
|
65
65
|
|
|
66
|
-
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
66
|
+
# rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
67
67
|
def redundant_feature?(feature_name)
|
|
68
68
|
feature_name == 'enumerator' ||
|
|
69
69
|
(target_ruby_version >= 2.1 && feature_name == 'thread') ||
|
|
@@ -73,7 +73,6 @@ module RuboCop
|
|
|
73
73
|
(target_ruby_version >= 3.2 && feature_name == 'set') ||
|
|
74
74
|
(target_ruby_version >= 4.0 && feature_name == 'pathname')
|
|
75
75
|
end
|
|
76
|
-
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
77
76
|
end
|
|
78
77
|
end
|
|
79
78
|
end
|
|
@@ -208,7 +208,7 @@ module RuboCop
|
|
|
208
208
|
add_offense(range) { |corrector| corrector.replace(range, '.') }
|
|
209
209
|
end
|
|
210
210
|
|
|
211
|
-
# rubocop:disable Metrics/AbcSize
|
|
211
|
+
# rubocop:disable-next Metrics/AbcSize
|
|
212
212
|
def on_or(node)
|
|
213
213
|
conversion_with_default?(node) do |send_node|
|
|
214
214
|
range = send_node.loc.dot.begin.join(node.source_range.end)
|
|
@@ -221,7 +221,6 @@ module RuboCop
|
|
|
221
221
|
end
|
|
222
222
|
end
|
|
223
223
|
end
|
|
224
|
-
# rubocop:enable Metrics/AbcSize
|
|
225
224
|
|
|
226
225
|
private
|
|
227
226
|
|
|
@@ -139,7 +139,7 @@ module RuboCop
|
|
|
139
139
|
grandparent.array_type? && grandparent.children.size > 1
|
|
140
140
|
end
|
|
141
141
|
|
|
142
|
-
# rubocop:disable Metrics/AbcSize
|
|
142
|
+
# rubocop:disable-next Metrics/AbcSize
|
|
143
143
|
def replacement_range_and_content(node)
|
|
144
144
|
variable = node.children.first
|
|
145
145
|
expression = node.source_range
|
|
@@ -157,7 +157,6 @@ module RuboCop
|
|
|
157
157
|
[node.loc.operator, '']
|
|
158
158
|
end
|
|
159
159
|
end
|
|
160
|
-
# rubocop:enable Metrics/AbcSize
|
|
161
160
|
|
|
162
161
|
def array_splat?(node)
|
|
163
162
|
node.children.first.array_type?
|
|
@@ -192,7 +192,7 @@ module RuboCop
|
|
|
192
192
|
(hash (pair (sym :exception) false))
|
|
193
193
|
PATTERN
|
|
194
194
|
|
|
195
|
-
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
|
195
|
+
# rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity
|
|
196
196
|
def on_send(node)
|
|
197
197
|
return if node.arguments.any? || hash_or_set_with_block?(node)
|
|
198
198
|
|
|
@@ -208,7 +208,6 @@ module RuboCop
|
|
|
208
208
|
corrector.remove(node.loc.dot.join(node.loc.end || node.loc.selector))
|
|
209
209
|
end
|
|
210
210
|
end
|
|
211
|
-
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
|
212
211
|
alias on_csend on_send
|
|
213
212
|
|
|
214
213
|
private
|
|
@@ -33,7 +33,7 @@ module RuboCop
|
|
|
33
33
|
MSG_EACH_WITH_INDEX = 'Use `each` instead of `each_with_index`.'
|
|
34
34
|
MSG_WITH_INDEX = 'Remove redundant `with_index`.'
|
|
35
35
|
|
|
36
|
-
# rubocop:disable Metrics/AbcSize
|
|
36
|
+
# rubocop:disable-next Metrics/AbcSize
|
|
37
37
|
def on_block(node)
|
|
38
38
|
return unless node.receiver
|
|
39
39
|
return if node.method?(:with_index) && !node.receiver.receiver
|
|
@@ -50,7 +50,6 @@ module RuboCop
|
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
|
-
# rubocop:enable Metrics/AbcSize
|
|
54
53
|
|
|
55
54
|
alias on_numblock on_block
|
|
56
55
|
alias on_itblock on_block
|
|
@@ -45,7 +45,7 @@ module RuboCop
|
|
|
45
45
|
}
|
|
46
46
|
PATTERN
|
|
47
47
|
|
|
48
|
-
# rubocop:disable Metrics/AbcSize
|
|
48
|
+
# rubocop:disable-next Metrics/AbcSize
|
|
49
49
|
def on_send(node)
|
|
50
50
|
return unless require_safe_navigation?(node)
|
|
51
51
|
|
|
@@ -63,7 +63,6 @@ module RuboCop
|
|
|
63
63
|
end
|
|
64
64
|
end
|
|
65
65
|
end
|
|
66
|
-
# rubocop:enable Metrics/AbcSize
|
|
67
66
|
|
|
68
67
|
private
|
|
69
68
|
|
|
@@ -85,7 +85,7 @@ module RuboCop
|
|
|
85
85
|
end
|
|
86
86
|
end
|
|
87
87
|
|
|
88
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
88
|
+
# rubocop:disable-next Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
89
89
|
def find_consistent_parts(grouped_operands)
|
|
90
90
|
csend_in_and, csend_in_or, send_in_and, send_in_or = most_left_indices(grouped_operands)
|
|
91
91
|
|
|
@@ -99,7 +99,6 @@ module RuboCop
|
|
|
99
99
|
['.', csend_in_or]
|
|
100
100
|
end
|
|
101
101
|
end
|
|
102
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
103
102
|
|
|
104
103
|
def already_appropriate_call?(operand, dot_op)
|
|
105
104
|
return true if operand.safe_navigation? && dot_op == '&.'
|
|
@@ -126,7 +125,7 @@ module RuboCop
|
|
|
126
125
|
end
|
|
127
126
|
end
|
|
128
127
|
|
|
129
|
-
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
128
|
+
# rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
130
129
|
def most_left_indices(grouped_operands)
|
|
131
130
|
indices = { csend_in_and: nil, csend_in_or: nil, send_in_and: nil, send_in_or: nil }
|
|
132
131
|
|
|
@@ -139,7 +138,6 @@ module RuboCop
|
|
|
139
138
|
|
|
140
139
|
indices.values
|
|
141
140
|
end
|
|
142
|
-
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
143
141
|
|
|
144
142
|
def operand_in_and?(node)
|
|
145
143
|
return true if node.parent.and_type?
|