rubocop 1.90.0 → 1.91.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 +238 -10
- data/config/obsoletion.yml +9 -0
- data/lib/rubocop/changed_files.rb +112 -0
- data/lib/rubocop/cli/command/execute_runner.rb +21 -1
- data/lib/rubocop/cli.rb +9 -3
- data/lib/rubocop/comment_config/directive_range.rb +3 -1
- data/lib/rubocop/comment_config/disable_next.rb +49 -3
- data/lib/rubocop/comment_config/push_pop.rb +55 -0
- data/lib/rubocop/comment_config.rb +21 -51
- data/lib/rubocop/config.rb +10 -0
- data/lib/rubocop/config_loader.rb +12 -4
- data/lib/rubocop/config_loader_resolver.rb +49 -7
- data/lib/rubocop/config_obsoletion/cop_rule.rb +2 -1
- data/lib/rubocop/config_obsoletion/removed_cop.rb +8 -0
- data/lib/rubocop/config_obsoletion.rb +1 -1
- data/lib/rubocop/config_validator.rb +18 -3
- data/lib/rubocop/cop/badge.rb +1 -1
- data/lib/rubocop/cop/base.rb +15 -3
- data/lib/rubocop/cop/correctors.rb +2 -2
- data/lib/rubocop/cop/gemspec/require_mfa.rb +1 -1
- data/lib/rubocop/cop/generator.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
- data/lib/rubocop/cop/layout/argument_alignment.rb +2 -0
- data/lib/rubocop/cop/layout/comment_indentation.rb +8 -5
- data/lib/rubocop/cop/layout/else_alignment.rb +1 -0
- data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +1 -1
- data/lib/rubocop/cop/layout/first_array_element_indentation.rb +17 -4
- data/lib/rubocop/cop/layout/first_parameter_indentation.rb +11 -0
- data/lib/rubocop/cop/layout/hash_alignment.rb +6 -5
- data/lib/rubocop/cop/layout/indentation_width.rb +2 -2
- data/lib/rubocop/cop/layout/line_length.rb +19 -1
- data/lib/rubocop/cop/layout/space_around_block_parameters.rb +14 -3
- data/lib/rubocop/cop/layout/space_around_operators.rb +5 -1
- data/lib/rubocop/cop/lint/argument_mismatch.rb +14 -4
- data/lib/rubocop/cop/lint/assignment_in_condition.rb +2 -0
- data/lib/rubocop/cop/lint/cop_directive_syntax.rb +88 -14
- data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -1
- data/lib/rubocop/cop/lint/empty_block.rb +1 -1
- data/lib/rubocop/cop/lint/literal_as_condition.rb +8 -2
- data/lib/rubocop/cop/lint/misplaced_magic_comment.rb +185 -0
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -1
- data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
- data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +1 -1
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +17 -1
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +30 -16
- data/lib/rubocop/cop/lint.rb +1 -0
- data/lib/rubocop/cop/migration/department_name.rb +1 -1
- data/lib/rubocop/cop/mixin/alignment.rb +1 -1
- data/lib/rubocop/cop/mixin/annotation_comment.rb +1 -1
- data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +11 -1
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +6 -2
- data/lib/rubocop/cop/mixin/hash_transform_method.rb +1 -1
- data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +12 -1
- data/lib/rubocop/cop/mixin.rb +2 -2
- data/lib/rubocop/cop/naming/block_forwarding.rb +4 -1
- data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
- data/lib/rubocop/cop/registry.rb +9 -1
- data/lib/rubocop/cop/severity.rb +12 -0
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +3 -2
- data/lib/rubocop/cop/style/arguments_forwarding.rb +2 -2
- data/lib/rubocop/cop/style/case_like_if.rb +3 -3
- data/lib/rubocop/cop/style/constant_visibility.rb +6 -2
- data/lib/rubocop/cop/style/data_inheritance.rb +14 -0
- data/lib/rubocop/cop/style/directive_scope.rb +154 -21
- data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +53 -9
- data/lib/rubocop/cop/style/documentation_method.rb +1 -9
- data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
- data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
- data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
- data/lib/rubocop/cop/style/empty_method.rb +12 -2
- data/lib/rubocop/cop/style/endless_method.rb +31 -12
- data/lib/rubocop/cop/style/file_null.rb +2 -2
- data/lib/rubocop/cop/style/float_division.rb +10 -16
- data/lib/rubocop/cop/style/for.rb +5 -0
- data/lib/rubocop/cop/style/format_string_token.rb +1 -1
- data/lib/rubocop/cop/style/guard_clause.rb +4 -0
- data/lib/rubocop/cop/style/hash_lookup_method.rb +3 -0
- data/lib/rubocop/cop/style/magic_comment_format.rb +1 -1
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/nested_modifier.rb +9 -4
- data/lib/rubocop/cop/style/nil_comparison.rb +36 -8
- data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
- data/lib/rubocop/cop/style/non_nil_check.rb +18 -2
- data/lib/rubocop/cop/style/not.rb +3 -1
- data/lib/rubocop/cop/style/numeric_literal_prefix.rb +11 -1
- data/lib/rubocop/cop/style/one_line_conditional.rb +1 -1
- data/lib/rubocop/cop/style/parallel_assignment.rb +1 -1
- data/lib/rubocop/cop/style/parentheses_around_condition.rb +6 -1
- data/lib/rubocop/cop/style/redundant_condition.rb +4 -4
- data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +10 -1
- data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -1
- data/lib/rubocop/cop/style/redundant_parentheses.rb +11 -1
- data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +6 -5
- data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +6 -6
- data/lib/rubocop/cop/style/single_line_block_params.rb +1 -1
- data/lib/rubocop/cop/style/special_global_vars.rb +1 -1
- data/lib/rubocop/cop/style/struct_inheritance.rb +14 -0
- data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
- data/lib/rubocop/cop/style.rb +0 -1
- data/lib/rubocop/cop/util.rb +1 -1
- data/lib/rubocop/cops_documentation_generator.rb +23 -2
- data/lib/rubocop/directive_comment.rb +37 -8
- data/lib/rubocop/formatter/disabled_config_formatter.rb +19 -1
- data/lib/rubocop/formatter/formatter_set.rb +2 -1
- data/lib/rubocop/formatter/github_actions_formatter.rb +6 -5
- data/lib/rubocop/formatter/html_formatter.rb +2 -2
- data/lib/rubocop/formatter/junit_formatter.rb +1 -1
- data/lib/rubocop/formatter/sarif_formatter.rb +181 -0
- data/lib/rubocop/formatter.rb +1 -0
- data/lib/rubocop/magic_comment.rb +27 -0
- data/lib/rubocop/options.rb +59 -1
- data/lib/rubocop/path_util.rb +1 -1
- data/lib/rubocop/plugin/loader.rb +1 -1
- data/lib/rubocop/rake_task.rb +1 -1
- data/lib/rubocop/remote_config.rb +4 -4
- data/lib/rubocop/result_cache.rb +16 -5
- data/lib/rubocop/runner.rb +64 -21
- data/lib/rubocop/unified_diff.rb +257 -0
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +4 -1
- metadata +8 -4
- data/lib/rubocop/cop/style/double_cop_disable_directive.rb +0 -46
|
@@ -106,7 +106,7 @@ module RuboCop
|
|
|
106
106
|
when :or
|
|
107
107
|
find_target(node.lhs)
|
|
108
108
|
when :match_with_lvasgn
|
|
109
|
-
lhs, rhs = *node # rubocop:disable InternalAffairs/NodeDestructuring
|
|
109
|
+
lhs, rhs = *node # rubocop:disable InternalAffairs/NodeDestructuring -- `match_with_lvasgn` has no named operand accessors
|
|
110
110
|
if lhs.regexp_type?
|
|
111
111
|
rhs
|
|
112
112
|
elsif rhs.regexp_type?
|
|
@@ -171,7 +171,7 @@ module RuboCop
|
|
|
171
171
|
return collect_conditions(node.lhs, target, conditions) &&
|
|
172
172
|
collect_conditions(node.rhs, target, conditions)
|
|
173
173
|
when :match_with_lvasgn
|
|
174
|
-
lhs, rhs = *node # rubocop:disable InternalAffairs/NodeDestructuring
|
|
174
|
+
lhs, rhs = *node # rubocop:disable InternalAffairs/NodeDestructuring -- `match_with_lvasgn` has no named operand accessors
|
|
175
175
|
condition_from_binary_op(lhs, rhs, target)
|
|
176
176
|
when :send
|
|
177
177
|
condition_from_send_node(node, target)
|
|
@@ -258,7 +258,7 @@ module RuboCop
|
|
|
258
258
|
def regexp_with_working_captures?(node)
|
|
259
259
|
case node.type
|
|
260
260
|
when :match_with_lvasgn
|
|
261
|
-
lhs, _rhs = *node # rubocop:disable InternalAffairs/NodeDestructuring
|
|
261
|
+
lhs, _rhs = *node # rubocop:disable InternalAffairs/NodeDestructuring -- `match_with_lvasgn` has no named operand accessors
|
|
262
262
|
node.loc.selector.source == '=~' && regexp_with_named_captures?(lhs)
|
|
263
263
|
when :send
|
|
264
264
|
node.method?(:match) &&
|
|
@@ -82,12 +82,16 @@ module RuboCop
|
|
|
82
82
|
end
|
|
83
83
|
end
|
|
84
84
|
|
|
85
|
-
# rubocop:disable-next Metrics/AbcSize
|
|
85
|
+
# rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity
|
|
86
86
|
def visibility_declaration?(node)
|
|
87
87
|
node.parent.each_child_node(:send).any? do |child|
|
|
88
88
|
next false unless (arguments = visibility_declaration_for(child))
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
if (splat = arguments.first)&.splat_type?
|
|
91
|
+
next true unless splat.children.first.array_type?
|
|
92
|
+
|
|
93
|
+
arguments = splat.children.first.children
|
|
94
|
+
end
|
|
91
95
|
constant_values = arguments.map do |argument|
|
|
92
96
|
# `respond_to?(:value)` is too broad: `int`/`float` nodes respond to it
|
|
93
97
|
# but their value is a `Numeric`, which has no `to_sym` (e.g.
|
|
@@ -10,6 +10,10 @@ module RuboCop
|
|
|
10
10
|
# Autocorrection is unsafe because it will change the inheritance
|
|
11
11
|
# tree (e.g. return value of `Module#ancestors`) of the constant.
|
|
12
12
|
#
|
|
13
|
+
# It is also unsafe because constants that the class body resolves through its
|
|
14
|
+
# ancestors (e.g. one provided by an included module) fall out of scope inside
|
|
15
|
+
# the block.
|
|
16
|
+
#
|
|
13
17
|
# @example
|
|
14
18
|
# # bad
|
|
15
19
|
# class Person < Data.define(:first_name, :last_name)
|
|
@@ -42,6 +46,7 @@ module RuboCop
|
|
|
42
46
|
|
|
43
47
|
def on_class(node)
|
|
44
48
|
return unless data_define?(node.parent_class)
|
|
49
|
+
return if defines_constants?(node.body)
|
|
45
50
|
|
|
46
51
|
add_offense(node.parent_class) do |corrector|
|
|
47
52
|
corrector.remove(range_with_surrounding_space(node.loc.keyword, newlines: false))
|
|
@@ -59,6 +64,15 @@ module RuboCop
|
|
|
59
64
|
|
|
60
65
|
private
|
|
61
66
|
|
|
67
|
+
# Constants, including nested classes and modules, are scoped to the class when they
|
|
68
|
+
# are defined in a class body, but leak into the enclosing namespace once moved into a
|
|
69
|
+
# `Data.define` block, so such a class cannot be rewritten as an assignment.
|
|
70
|
+
def defines_constants?(class_body)
|
|
71
|
+
return false unless class_body
|
|
72
|
+
|
|
73
|
+
class_body.each_node(:casgn, :class, :module).any?
|
|
74
|
+
end
|
|
75
|
+
|
|
62
76
|
def correct_parent(parent, corrector)
|
|
63
77
|
if parent.block_type?
|
|
64
78
|
# Convert a brace block to `do`, so the class's own `end` closes it
|
|
@@ -4,10 +4,10 @@ module RuboCop
|
|
|
4
4
|
module Cop
|
|
5
5
|
module Style
|
|
6
6
|
# Checks for directive scopes that can be expressed with the tighter
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
# one statement. A statement-scoped directive cannot
|
|
10
|
-
# surrounding code changes and needs no closing boundary.
|
|
7
|
+
# next-statement forms: a `disable`/`enable` pair, an
|
|
8
|
+
# `enable`/`disable` pair, or a `push`/`pop` with signed arguments
|
|
9
|
+
# wrapping exactly one statement. A statement-scoped directive cannot
|
|
10
|
+
# drift as the surrounding code changes and needs no closing boundary.
|
|
11
11
|
#
|
|
12
12
|
# @safety
|
|
13
13
|
# The autocorrection is unsafe because the suppression scope shrinks
|
|
@@ -37,6 +37,21 @@ module RuboCop
|
|
|
37
37
|
# def foo
|
|
38
38
|
# end
|
|
39
39
|
#
|
|
40
|
+
# # bad
|
|
41
|
+
# # rubocop:disable Metrics/AbcSize
|
|
42
|
+
# # rubocop:push +Metrics/AbcSize
|
|
43
|
+
# def foo
|
|
44
|
+
# end
|
|
45
|
+
# # rubocop:pop
|
|
46
|
+
# # rubocop:enable Metrics/AbcSize
|
|
47
|
+
#
|
|
48
|
+
# # good
|
|
49
|
+
# # rubocop:disable Metrics/AbcSize
|
|
50
|
+
# # rubocop:enable-next Metrics/AbcSize
|
|
51
|
+
# def foo
|
|
52
|
+
# end
|
|
53
|
+
# # rubocop:enable Metrics/AbcSize
|
|
54
|
+
#
|
|
40
55
|
# # good - the region spans more than one statement
|
|
41
56
|
# # rubocop:disable Metrics/AbcSize
|
|
42
57
|
# def foo
|
|
@@ -52,7 +67,9 @@ module RuboCop
|
|
|
52
67
|
|
|
53
68
|
MSG_PAIR = 'Use `%<mode>s-next` instead of a `%<mode>s`/`enable` pair ' \
|
|
54
69
|
'around a single statement.'
|
|
55
|
-
|
|
70
|
+
MSG_ENABLE_PAIR = 'Use `enable-next` instead of an `enable`/`disable` pair ' \
|
|
71
|
+
'around a single statement.'
|
|
72
|
+
MSG_PUSH_POP = 'Use `%<replacement>s` instead of `push`/`pop` around a single statement.'
|
|
56
73
|
|
|
57
74
|
def on_new_investigation
|
|
58
75
|
processed_source.comments.each do |comment|
|
|
@@ -61,8 +78,10 @@ module RuboCop
|
|
|
61
78
|
|
|
62
79
|
if plain_disable?(directive)
|
|
63
80
|
check_pair(directive)
|
|
64
|
-
elsif
|
|
81
|
+
elsif signed_push?(directive)
|
|
65
82
|
check_push_pop(directive)
|
|
83
|
+
elsif plain_enable?(directive)
|
|
84
|
+
check_enable_pair(directive)
|
|
66
85
|
end
|
|
67
86
|
end
|
|
68
87
|
end
|
|
@@ -77,8 +96,12 @@ module RuboCop
|
|
|
77
96
|
directive.disabled? && !directive.disable_next?
|
|
78
97
|
end
|
|
79
98
|
|
|
80
|
-
def
|
|
81
|
-
directive.
|
|
99
|
+
def plain_enable?(directive)
|
|
100
|
+
directive.enabled? && !directive.enable_next? && !directive.all_cops?
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def signed_push?(directive)
|
|
104
|
+
directive.push? && !directive.signed_args.empty?
|
|
82
105
|
end
|
|
83
106
|
|
|
84
107
|
def check_pair(directive)
|
|
@@ -100,15 +123,132 @@ module RuboCop
|
|
|
100
123
|
end
|
|
101
124
|
|
|
102
125
|
def check_push_pop(directive)
|
|
103
|
-
|
|
104
|
-
return unless
|
|
126
|
+
pop_line = balancing_pop_line(directive)
|
|
127
|
+
return unless pop_line && comment_config.comment_only_line?(pop_line)
|
|
128
|
+
return unless wraps_single_statement?(directive, pop_line)
|
|
105
129
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
130
|
+
pop_comment = processed_source.comment_at_line(pop_line)
|
|
131
|
+
replacement = push_replacement(directive)
|
|
132
|
+
message = format(MSG_PUSH_POP, replacement: replacement_mode(directive))
|
|
133
|
+
add_offense(directive.comment, message: message) do |corrector|
|
|
134
|
+
corrector.replace(directive.comment, replacement)
|
|
135
|
+
remove_line(corrector, pop_comment)
|
|
109
136
|
end
|
|
110
137
|
end
|
|
111
138
|
|
|
139
|
+
# The line of the `pop` balancing this `push`, taking nesting into
|
|
140
|
+
# account, or `nil` when the push is never popped.
|
|
141
|
+
def balancing_pop_line(push_directive)
|
|
142
|
+
depth = 0
|
|
143
|
+
each_directive_after(push_directive) do |directive|
|
|
144
|
+
if directive.push?
|
|
145
|
+
depth += 1
|
|
146
|
+
elsif directive.pop?
|
|
147
|
+
return directive.line_number if depth.zero?
|
|
148
|
+
|
|
149
|
+
depth -= 1
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
nil
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def each_directive_after(reference)
|
|
156
|
+
processed_source.comments.each do |comment|
|
|
157
|
+
directive = DirectiveComment.new(comment)
|
|
158
|
+
next unless directive.cop_names || directive.push? || directive.pop?
|
|
159
|
+
|
|
160
|
+
yield directive if directive.line_number > reference.line_number
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def replacement_mode(directive)
|
|
165
|
+
ops = directive.signed_args.keys.sort
|
|
166
|
+
if ops == ['-']
|
|
167
|
+
'disable-next'
|
|
168
|
+
elsif ops == ['+']
|
|
169
|
+
'enable-next'
|
|
170
|
+
else
|
|
171
|
+
'next'
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def push_replacement(directive)
|
|
176
|
+
text = case replacement_mode(directive)
|
|
177
|
+
when 'disable-next'
|
|
178
|
+
"# rubocop:disable-next #{directive.signed_args['-'].join(', ')}"
|
|
179
|
+
when 'enable-next'
|
|
180
|
+
"# rubocop:enable-next #{directive.signed_args['+'].join(', ')}"
|
|
181
|
+
else
|
|
182
|
+
"# rubocop:next #{directive.cops}"
|
|
183
|
+
end
|
|
184
|
+
reason = directive.reason
|
|
185
|
+
reason ? "#{text} -- #{reason}" : text
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# An `enable`/`disable` pair around one statement inside a disabled
|
|
189
|
+
# region re-enables the cops for just that statement - `enable-next`
|
|
190
|
+
# says the same without the closing boundary. The pair only counts
|
|
191
|
+
# when the `enable` really closed open disables (otherwise the
|
|
192
|
+
# trailing `disable` opens a new region and the conversion would
|
|
193
|
+
# change what is covered).
|
|
194
|
+
def check_enable_pair(directive)
|
|
195
|
+
closing = enable_pair_closing(directive)
|
|
196
|
+
return unless closing
|
|
197
|
+
|
|
198
|
+
add_offense(directive.comment, message: MSG_ENABLE_PAIR) do |corrector|
|
|
199
|
+
corrector.replace(directive.comment,
|
|
200
|
+
directive.comment.text.sub(/\benable\b/, 'enable-next'))
|
|
201
|
+
remove_line(corrector, closing.comment)
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def enable_pair_closing(directive)
|
|
206
|
+
scope = comment_config.statement_scope_after(directive.line_number)
|
|
207
|
+
return nil unless scope && scope.begin == directive.line_number + 1
|
|
208
|
+
|
|
209
|
+
closing = re_disable_below(directive, scope.end + 1)
|
|
210
|
+
closing if closing && closed_open_disables?(directive, closing)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def re_disable_below(directive, line)
|
|
214
|
+
return nil unless comment_config.comment_only_line?(line)
|
|
215
|
+
|
|
216
|
+
comment = processed_source.comment_at_line(line)
|
|
217
|
+
return nil unless comment
|
|
218
|
+
|
|
219
|
+
closing = DirectiveComment.new(comment)
|
|
220
|
+
return nil unless closing.disabled? && !closing.disable_next?
|
|
221
|
+
return nil unless closing.raw_cop_names.sort == directive.raw_cop_names.sort
|
|
222
|
+
|
|
223
|
+
closing
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# Every cop of the pair must have a range the `enable` closed and a
|
|
227
|
+
# range the trailing `disable` reopened - otherwise the `enable` was
|
|
228
|
+
# not inside a disabled region and the conversion would change what
|
|
229
|
+
# is covered.
|
|
230
|
+
def closed_open_disables?(directive, closing)
|
|
231
|
+
directive.cop_names.all? do |cop|
|
|
232
|
+
ranges = comment_config.cop_disabled_line_ranges[qualified_name(cop)]
|
|
233
|
+
ranges && closed_at?(ranges, directive.line_number) && reopened_by?(ranges, closing)
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def closed_at?(ranges, line)
|
|
238
|
+
ranges.any? { |range| range.end == line }
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def reopened_by?(ranges, closing)
|
|
242
|
+
ranges.any? do |range|
|
|
243
|
+
range.respond_to?(:directive) && range.directive.comment.equal?(closing.comment)
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def qualified_name(cop_name)
|
|
248
|
+
Registry.qualified_cop_name(cop_name.strip, processed_source.file_path,
|
|
249
|
+
correct_namespace: false)
|
|
250
|
+
end
|
|
251
|
+
|
|
112
252
|
# The directive closing this one's scope, when that scope wraps
|
|
113
253
|
# exactly one statement - `nil` otherwise.
|
|
114
254
|
def single_statement_closing_directive(directive)
|
|
@@ -133,7 +273,7 @@ module RuboCop
|
|
|
133
273
|
ends = ranges.map(&:end).uniq
|
|
134
274
|
return nil unless ends.size == 1 && ends.first.to_f.finite?
|
|
135
275
|
|
|
136
|
-
|
|
276
|
+
ends.first
|
|
137
277
|
end
|
|
138
278
|
|
|
139
279
|
def ranges_opened_by(directive)
|
|
@@ -155,13 +295,6 @@ module RuboCop
|
|
|
155
295
|
!scope.nil? && scope.begin == directive.line_number + 1 && scope.end + 1 == closing_line
|
|
156
296
|
end
|
|
157
297
|
|
|
158
|
-
def disable_next_replacement(directive)
|
|
159
|
-
cops = directive.push_args['-'].join(', ')
|
|
160
|
-
reason = directive.reason
|
|
161
|
-
text = "# rubocop:disable-next #{cops}"
|
|
162
|
-
reason ? "#{text} -- #{reason}" : text
|
|
163
|
-
end
|
|
164
|
-
|
|
165
298
|
def remove_line(corrector, comment)
|
|
166
299
|
corrector.remove(range_by_whole_lines(comment.source_range, include_final_newline: true))
|
|
167
300
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# rubocop:disable Lint/RedundantCopDisableDirective
|
|
3
|
+
# rubocop:disable Lint/RedundantCopDisableDirective -- the examples below read as real directives
|
|
4
4
|
|
|
5
5
|
module RuboCop
|
|
6
6
|
module Cop
|
|
@@ -12,6 +12,10 @@ module RuboCop
|
|
|
12
12
|
# Specific cops can be allowed with the `AllowedCops` configuration. Note that
|
|
13
13
|
# if this configuration is set, `rubocop:disable all` is still disallowed.
|
|
14
14
|
#
|
|
15
|
+
# `AllowedCops` and `DisallowedCops` accept cop names and department names
|
|
16
|
+
# alike. Listing a cop does not cover its whole department: a directive
|
|
17
|
+
# disabling `Metrics` is broader than one disabling `Metrics/AbcSize`.
|
|
18
|
+
#
|
|
15
19
|
# Alternatively, specific cops can be disallowed with the `DisallowedCops`
|
|
16
20
|
# configuration. When `DisallowedCops` is set, only directives for the listed
|
|
17
21
|
# cops (and `all`) will be flagged. This is useful when you want
|
|
@@ -19,7 +23,13 @@ module RuboCop
|
|
|
19
23
|
# allowlisting all other cops. `AllowedCops` and `DisallowedCops` should not
|
|
20
24
|
# both be set at the same time; if `DisallowedCops` is set, it takes precedence.
|
|
21
25
|
#
|
|
22
|
-
#
|
|
26
|
+
# `AllowedDirectives` names directive forms - `disable`, `todo`,
|
|
27
|
+
# `disable-next`, `todo-next`, `push`, `next` - and exempts them from the
|
|
28
|
+
# cop entirely. `rubocop:todo` is the usual candidate, since
|
|
29
|
+
# `--disable-uncorrectable` generates those and they record debt rather
|
|
30
|
+
# than a decision someone should be asked to justify.
|
|
31
|
+
#
|
|
32
|
+
# With `AllowWithReason` set to `true`, a disable directive carrying
|
|
23
33
|
# a `--` trailing justification comment is allowed, so a team can require
|
|
24
34
|
# every disable to be documented instead of banning them outright. Enable
|
|
25
35
|
# directives are not checked in this mode, since they end a suppression
|
|
@@ -58,7 +68,21 @@ module RuboCop
|
|
|
58
68
|
# foo
|
|
59
69
|
# # rubocop:enable Metrics/AbcSize
|
|
60
70
|
#
|
|
61
|
-
# @example
|
|
71
|
+
# @example AllowedDirectives: [todo]
|
|
72
|
+
# # good - the cop does not look at `todo` directives at all
|
|
73
|
+
# x = 0 # rubocop:todo Layout/SpaceAroundOperators
|
|
74
|
+
#
|
|
75
|
+
# # bad
|
|
76
|
+
# x = 0 # rubocop:disable Layout/SpaceAroundOperators
|
|
77
|
+
#
|
|
78
|
+
# @example AllowedCops: [Metrics]
|
|
79
|
+
# # good - every cop the directive disables is in an exempt department
|
|
80
|
+
# # rubocop:disable Metrics/AbcSize
|
|
81
|
+
# def foo
|
|
82
|
+
# end
|
|
83
|
+
# # rubocop:enable Metrics/AbcSize
|
|
84
|
+
#
|
|
85
|
+
# @example AllowWithReason: true
|
|
62
86
|
# # bad
|
|
63
87
|
# x = 0 # rubocop:disable Layout/SpaceAroundOperators
|
|
64
88
|
#
|
|
@@ -77,7 +101,7 @@ module RuboCop
|
|
|
77
101
|
def on_new_investigation
|
|
78
102
|
processed_source.comments.each do |comment|
|
|
79
103
|
directive = DirectiveComment.new(comment)
|
|
80
|
-
next if
|
|
104
|
+
next if ignored?(directive)
|
|
81
105
|
|
|
82
106
|
directive_cops = directive_cops(directive)
|
|
83
107
|
disallowed_cops = compute_disallowed_cops(directive_cops)
|
|
@@ -95,10 +119,10 @@ module RuboCop
|
|
|
95
119
|
if directive_cops.include?('all')
|
|
96
120
|
directive_cops
|
|
97
121
|
else
|
|
98
|
-
directive_cops.uniq.select { |cop|
|
|
122
|
+
directive_cops.uniq.select { |cop| listed?(disallowed_cops_config, cop) }
|
|
99
123
|
end
|
|
100
124
|
else
|
|
101
|
-
directive_cops.reject { |cop|
|
|
125
|
+
directive_cops.reject { |cop| cop != 'all' && listed?(allowed_cops, cop) }
|
|
102
126
|
end
|
|
103
127
|
end
|
|
104
128
|
|
|
@@ -116,7 +140,7 @@ module RuboCop
|
|
|
116
140
|
end
|
|
117
141
|
|
|
118
142
|
def offense_message(disallowed_cops)
|
|
119
|
-
if
|
|
143
|
+
if allow_with_reason?
|
|
120
144
|
MSG_MISSING_REASON
|
|
121
145
|
elsif any_cops_allowed? || disallowed_cops_config.any?
|
|
122
146
|
format(MSG_FOR_COPS, cops: "`#{disallowed_cops.join('`, `')}`")
|
|
@@ -130,14 +154,34 @@ module RuboCop
|
|
|
130
154
|
match_captures && match_captures[1] ? match_captures[1].split(',').map(&:strip) : []
|
|
131
155
|
end
|
|
132
156
|
|
|
133
|
-
def
|
|
134
|
-
|
|
157
|
+
def ignored?(directive)
|
|
158
|
+
return true if exempt_mode?(directive)
|
|
159
|
+
|
|
160
|
+
allow_with_reason? && (directive.reason || directive.enabled?)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def exempt_mode?(directive)
|
|
164
|
+
allowed_directives.include?(directive.mode)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def allowed_directives
|
|
168
|
+
@allowed_directives ||= Array(cop_config['AllowedDirectives'])
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def allow_with_reason?
|
|
172
|
+
cop_config['AllowWithReason']
|
|
135
173
|
end
|
|
136
174
|
|
|
137
175
|
def allowed_cops
|
|
138
176
|
@allowed_cops ||= Array(cop_config['AllowedCops']).to_set
|
|
139
177
|
end
|
|
140
178
|
|
|
179
|
+
# A name matches either outright or through its department, so
|
|
180
|
+
# `Metrics` covers `Metrics/AbcSize` but not the other way round.
|
|
181
|
+
def listed?(names, cop)
|
|
182
|
+
names.include?(cop) || names.include?(cop.split('/').first)
|
|
183
|
+
end
|
|
184
|
+
|
|
141
185
|
def any_cops_allowed?
|
|
142
186
|
allowed_cops.any?
|
|
143
187
|
end
|
|
@@ -119,6 +119,7 @@ module RuboCop
|
|
|
119
119
|
|
|
120
120
|
def on_def(node)
|
|
121
121
|
return if node.method?(:initialize)
|
|
122
|
+
return if allowed_methods.include?(node.method_name)
|
|
122
123
|
|
|
123
124
|
parent = node.parent
|
|
124
125
|
modifier_node?(parent) ? check(parent) : check(node)
|
|
@@ -130,7 +131,6 @@ module RuboCop
|
|
|
130
131
|
def check(node)
|
|
131
132
|
return if non_public?(node) && !require_for_non_public_methods?
|
|
132
133
|
return if documentation_comment?(node)
|
|
133
|
-
return if method_allowed?(node)
|
|
134
134
|
|
|
135
135
|
add_offense(node)
|
|
136
136
|
end
|
|
@@ -139,14 +139,6 @@ module RuboCop
|
|
|
139
139
|
cop_config['RequireForNonPublicMethods']
|
|
140
140
|
end
|
|
141
141
|
|
|
142
|
-
def method_allowed?(node)
|
|
143
|
-
# For a modifier form like `module_function def foo; end`, `node` is the
|
|
144
|
-
# `module_function`/`ruby2_keywords` send; the real method name is on its
|
|
145
|
-
# `def`/`defs` argument, not the modifier itself.
|
|
146
|
-
method_name = node.send_type? ? node.first_argument.method_name : node.method_name
|
|
147
|
-
allowed_methods.include?(method_name)
|
|
148
|
-
end
|
|
149
|
-
|
|
150
142
|
def allowed_methods
|
|
151
143
|
@allowed_methods ||= cop_config.fetch('AllowedMethods', []).map(&:to_sym)
|
|
152
144
|
end
|
|
@@ -26,7 +26,7 @@ module RuboCop
|
|
|
26
26
|
|
|
27
27
|
MSG = 'Use `Integer#times` for a simple loop which iterates a fixed number of times.'
|
|
28
28
|
|
|
29
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
29
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler -- only matches blocks that take no parameters
|
|
30
30
|
return unless offending?(node)
|
|
31
31
|
|
|
32
32
|
send_node = node.send_node
|
|
@@ -28,7 +28,7 @@ module RuboCop
|
|
|
28
28
|
|
|
29
29
|
MSG = 'Omit pipes for the empty block parameters.'
|
|
30
30
|
|
|
31
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
31
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler -- the offense is an empty `||`
|
|
32
32
|
send_node = node.send_node
|
|
33
33
|
check(node) unless send_node.send_type? && send_node.lambda_literal?
|
|
34
34
|
end
|
|
@@ -23,7 +23,7 @@ module RuboCop
|
|
|
23
23
|
|
|
24
24
|
MSG = 'Omit parentheses for the empty lambda parameters.'
|
|
25
25
|
|
|
26
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
26
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler -- the offense is an empty `()`
|
|
27
27
|
send_node = node.send_node
|
|
28
28
|
return unless send_node.send_type?
|
|
29
29
|
|
|
@@ -53,11 +53,11 @@ module RuboCop
|
|
|
53
53
|
|
|
54
54
|
def on_def(node)
|
|
55
55
|
return if node.body || processed_source.contains_comment?(node.source_range)
|
|
56
|
-
return if correct_style?(node)
|
|
56
|
+
return if correct_style?(node) || compact_style_disallowed?
|
|
57
57
|
|
|
58
58
|
add_offense(node) do |corrector|
|
|
59
59
|
correction = corrected(node)
|
|
60
|
-
next if
|
|
60
|
+
next if correction_exceeds_line_length?(correction)
|
|
61
61
|
|
|
62
62
|
corrector.replace(node, correction)
|
|
63
63
|
end
|
|
@@ -74,6 +74,12 @@ module RuboCop
|
|
|
74
74
|
(compact_style? && compact?(node)) || (expanded_style? && expanded?(node))
|
|
75
75
|
end
|
|
76
76
|
|
|
77
|
+
def compact_style_disallowed?
|
|
78
|
+
return false unless compact_style?
|
|
79
|
+
|
|
80
|
+
config.for_enabled_cop('Style/SingleLineMethods')['AllowIfMethodIsEmpty'] == false
|
|
81
|
+
end
|
|
82
|
+
|
|
77
83
|
def corrected(node)
|
|
78
84
|
scope = node.receiver ? "#{node.receiver.source}." : ''
|
|
79
85
|
arguments = if node.arguments?
|
|
@@ -86,6 +92,10 @@ module RuboCop
|
|
|
86
92
|
["def #{signature}", 'end'].join(joint(node))
|
|
87
93
|
end
|
|
88
94
|
|
|
95
|
+
def correction_exceeds_line_length?(correction)
|
|
96
|
+
compact_style? && max_line_length && correction.size > max_line_length
|
|
97
|
+
end
|
|
98
|
+
|
|
89
99
|
def joint(node)
|
|
90
100
|
indent = ' ' * node.loc.column
|
|
91
101
|
|
|
@@ -166,6 +166,8 @@ module RuboCop
|
|
|
166
166
|
return if node.single_line? || style == :allow_always
|
|
167
167
|
|
|
168
168
|
add_offense(node, message: MSG_MULTI_LINE) do |corrector|
|
|
169
|
+
next if endless_parent?(node)
|
|
170
|
+
|
|
169
171
|
correct_to_multiline(corrector, node)
|
|
170
172
|
end
|
|
171
173
|
end
|
|
@@ -175,11 +177,11 @@ module RuboCop
|
|
|
175
177
|
add_offense(node, message: MSG_MULTI_LINE) do |corrector|
|
|
176
178
|
correct_to_multiline(corrector, node)
|
|
177
179
|
end
|
|
178
|
-
elsif !node.endless? && can_be_made_endless?(node) && node
|
|
180
|
+
elsif !node.endless? && can_be_made_endless?(node) && single_line_when_made_endless?(node)
|
|
179
181
|
return if too_long_when_made_endless?(node)
|
|
180
182
|
|
|
181
183
|
add_offense(node, message: MSG_REQUIRE_SINGLE) do |corrector|
|
|
182
|
-
corrector
|
|
184
|
+
correct_to_endless(corrector, node)
|
|
183
185
|
end
|
|
184
186
|
end
|
|
185
187
|
end
|
|
@@ -189,14 +191,22 @@ module RuboCop
|
|
|
189
191
|
return if too_long_when_made_endless?(node)
|
|
190
192
|
|
|
191
193
|
add_offense(node, message: MSG_REQUIRE_ALWAYS) do |corrector|
|
|
192
|
-
corrector
|
|
194
|
+
correct_to_endless(corrector, node)
|
|
193
195
|
end
|
|
194
196
|
end
|
|
195
197
|
|
|
196
198
|
def handle_disallow_style(node)
|
|
197
199
|
return unless node.endless?
|
|
198
200
|
|
|
199
|
-
add_offense(node)
|
|
201
|
+
add_offense(node) do |corrector|
|
|
202
|
+
next if endless_parent?(node)
|
|
203
|
+
|
|
204
|
+
correct_to_multiline(corrector, node)
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def endless_parent?(node)
|
|
209
|
+
node.parent&.any_def_type? && node.parent.endless?
|
|
200
210
|
end
|
|
201
211
|
|
|
202
212
|
def use_heredoc?(node)
|
|
@@ -206,6 +216,17 @@ module RuboCop
|
|
|
206
216
|
body.each_descendant(:str).any?(&:heredoc?)
|
|
207
217
|
end
|
|
208
218
|
|
|
219
|
+
def correct_to_endless(corrector, node)
|
|
220
|
+
corrector.replace(signature_to_body_range(node), ' = ')
|
|
221
|
+
corrector.remove(node.body.source_range.end.join(node.loc.end.end))
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def signature_to_body_range(node)
|
|
225
|
+
signature_end = node.arguments.any? ? node.arguments.source_range.end : node.loc.name.end
|
|
226
|
+
|
|
227
|
+
signature_end.join(node.body.source_range.begin)
|
|
228
|
+
end
|
|
229
|
+
|
|
209
230
|
def endless_replacement(node)
|
|
210
231
|
<<~RUBY.strip
|
|
211
232
|
def #{receiver(node)}#{node.method_name}#{arguments(node)} = #{node.body.source}
|
|
@@ -221,19 +242,17 @@ module RuboCop
|
|
|
221
242
|
end
|
|
222
243
|
|
|
223
244
|
def can_be_made_endless?(node)
|
|
224
|
-
node.body && !node.body.
|
|
245
|
+
node.body && !node.body.type?(:begin, :kwbegin, :rescue, :ensure)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def single_line_when_made_endless?(node)
|
|
249
|
+
!endless_replacement(node).include?("\n")
|
|
225
250
|
end
|
|
226
251
|
|
|
227
252
|
def too_long_when_made_endless?(node)
|
|
228
253
|
return false unless config.cop_enabled?('Layout/LineLength')
|
|
229
254
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
endless_replacement(node).length + offset > max_line_length
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
def modifier_offset(node)
|
|
236
|
-
same_line?(node.parent, node) ? node.loc.column - node.parent.loc.column : 0
|
|
255
|
+
endless_replacement(node).length + node.loc.column > max_line_length
|
|
237
256
|
end
|
|
238
257
|
end
|
|
239
258
|
end
|
|
@@ -54,7 +54,7 @@ module RuboCop
|
|
|
54
54
|
@contain_dev_null_string_in_file = ast.each_descendant(:str).any? do |str|
|
|
55
55
|
content = str.str_content
|
|
56
56
|
|
|
57
|
-
valid_string?(content) && content.downcase == '/dev/null' # rubocop:disable Style/FileNull
|
|
57
|
+
valid_string?(content) && content.downcase == '/dev/null' # rubocop:disable Style/FileNull -- the cop needs the literal it is matching
|
|
58
58
|
end
|
|
59
59
|
end
|
|
60
60
|
|
|
@@ -62,7 +62,7 @@ module RuboCop
|
|
|
62
62
|
value = node.value
|
|
63
63
|
return unless valid_string?(value)
|
|
64
64
|
return if acceptable?(node)
|
|
65
|
-
return if value.downcase == 'nul' && !@contain_dev_null_string_in_file # rubocop:disable Style/FileNull
|
|
65
|
+
return if value.downcase == 'nul' && !@contain_dev_null_string_in_file # rubocop:disable Style/FileNull -- the cop needs the literal it is matching
|
|
66
66
|
return unless REGEXP.match?(value)
|
|
67
67
|
|
|
68
68
|
add_offense(node, message: format(MSG, source: value)) do |corrector|
|