rubocop 1.39.0 → 1.44.1
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/LICENSE.txt +1 -1
- data/README.md +2 -2
- data/config/default.yml +149 -11
- data/exe/rubocop +1 -1
- data/lib/rubocop/cli.rb +1 -1
- data/lib/rubocop/comment_config.rb +5 -0
- data/lib/rubocop/config.rb +39 -15
- data/lib/rubocop/config_loader.rb +26 -20
- data/lib/rubocop/config_loader_resolver.rb +6 -2
- data/lib/rubocop/config_validator.rb +1 -1
- data/lib/rubocop/cop/badge.rb +9 -4
- data/lib/rubocop/cop/base.rb +84 -74
- data/lib/rubocop/cop/commissioner.rb +8 -3
- data/lib/rubocop/cop/cop.rb +29 -29
- data/lib/rubocop/cop/corrector.rb +30 -10
- data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +22 -6
- data/lib/rubocop/cop/correctors/ordered_gem_corrector.rb +1 -6
- data/lib/rubocop/cop/gemspec/dependency_version.rb +16 -18
- data/lib/rubocop/cop/gemspec/development_dependencies.rb +107 -0
- data/lib/rubocop/cop/internal_affairs/cop_description.rb +3 -1
- data/lib/rubocop/cop/internal_affairs/lambda_or_proc.rb +46 -0
- data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +11 -3
- data/lib/rubocop/cop/internal_affairs.rb +1 -0
- data/lib/rubocop/cop/layout/block_end_newline.rb +7 -1
- data/lib/rubocop/cop/layout/class_structure.rb +32 -11
- data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +2 -6
- data/lib/rubocop/cop/layout/comment_indentation.rb +3 -1
- data/lib/rubocop/cop/layout/empty_lines.rb +2 -0
- data/lib/rubocop/cop/layout/extra_spacing.rb +10 -6
- data/lib/rubocop/cop/layout/first_array_element_line_break.rb +38 -2
- data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +49 -2
- data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +61 -2
- data/lib/rubocop/cop/layout/first_method_parameter_line_break.rb +52 -2
- data/lib/rubocop/cop/layout/heredoc_indentation.rb +6 -9
- data/lib/rubocop/cop/layout/indentation_style.rb +7 -2
- data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +5 -0
- data/lib/rubocop/cop/layout/line_continuation_spacing.rb +11 -5
- data/lib/rubocop/cop/layout/line_length.rb +2 -0
- data/lib/rubocop/cop/layout/multiline_array_line_breaks.rb +51 -2
- data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_hash_key_line_breaks.rb +49 -2
- data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +53 -2
- data/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb +58 -2
- data/lib/rubocop/cop/layout/redundant_line_break.rb +2 -2
- data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +0 -2
- data/lib/rubocop/cop/layout/trailing_empty_lines.rb +1 -1
- data/lib/rubocop/cop/layout/trailing_whitespace.rb +11 -4
- data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
- data/lib/rubocop/cop/lint/ambiguous_operator.rb +4 -0
- data/lib/rubocop/cop/lint/assignment_in_condition.rb +11 -1
- data/lib/rubocop/cop/lint/constant_resolution.rb +4 -0
- data/lib/rubocop/cop/lint/debugger.rb +3 -1
- data/lib/rubocop/cop/lint/deprecated_class_methods.rb +62 -112
- data/lib/rubocop/cop/lint/deprecated_constants.rb +8 -1
- data/lib/rubocop/cop/lint/duplicate_branch.rb +0 -2
- data/lib/rubocop/cop/lint/duplicate_methods.rb +19 -8
- data/lib/rubocop/cop/lint/else_layout.rb +2 -6
- data/lib/rubocop/cop/lint/empty_block.rb +1 -5
- data/lib/rubocop/cop/lint/empty_conditional_body.rb +1 -1
- data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +11 -7
- data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +15 -17
- data/lib/rubocop/cop/lint/interpolation_check.rb +4 -3
- data/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb +1 -0
- data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +10 -5
- data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +19 -0
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +5 -0
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +15 -3
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_require_statement.rb +11 -1
- data/lib/rubocop/cop/lint/regexp_as_condition.rb +6 -0
- data/lib/rubocop/cop/lint/require_parentheses.rb +3 -1
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +10 -12
- data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +5 -4
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +4 -3
- data/lib/rubocop/cop/lint/unused_method_argument.rb +2 -1
- data/lib/rubocop/cop/lint/useless_method_definition.rb +3 -3
- data/lib/rubocop/cop/lint/useless_rescue.rb +85 -0
- data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +14 -4
- data/lib/rubocop/cop/lint/void.rb +25 -16
- data/lib/rubocop/cop/metrics/block_length.rb +9 -4
- data/lib/rubocop/cop/metrics/block_nesting.rb +1 -1
- data/lib/rubocop/cop/metrics/class_length.rb +10 -5
- data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +1 -1
- data/lib/rubocop/cop/metrics/method_length.rb +9 -4
- data/lib/rubocop/cop/metrics/module_length.rb +10 -5
- data/lib/rubocop/cop/metrics/parameter_lists.rb +27 -0
- data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
- data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +3 -6
- data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +6 -3
- data/lib/rubocop/cop/mixin/alignment.rb +2 -2
- data/lib/rubocop/cop/mixin/allowed_identifiers.rb +2 -2
- data/lib/rubocop/cop/mixin/annotation_comment.rb +13 -6
- data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +21 -9
- data/lib/rubocop/cop/mixin/first_element_line_break.rb +11 -7
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +59 -6
- data/lib/rubocop/cop/mixin/line_length_help.rb +11 -2
- data/lib/rubocop/cop/mixin/method_complexity.rb +5 -3
- data/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb +5 -3
- data/lib/rubocop/cop/mixin/percent_array.rb +3 -5
- data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +1 -1
- data/lib/rubocop/cop/mixin/require_library.rb +2 -0
- data/lib/rubocop/cop/mixin/rescue_node.rb +3 -3
- data/lib/rubocop/cop/mixin/statement_modifier.rb +16 -1
- data/lib/rubocop/cop/naming/block_forwarding.rb +5 -1
- data/lib/rubocop/cop/naming/class_and_module_camel_case.rb +2 -0
- data/lib/rubocop/cop/naming/inclusive_language.rb +4 -1
- data/lib/rubocop/cop/registry.rb +63 -43
- data/lib/rubocop/cop/security/compound_hash.rb +2 -1
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +18 -10
- data/lib/rubocop/cop/style/alias.rb +9 -1
- data/lib/rubocop/cop/style/array_intersect.rb +111 -0
- data/lib/rubocop/cop/style/block_comments.rb +1 -1
- data/lib/rubocop/cop/style/block_delimiters.rb +8 -2
- data/lib/rubocop/cop/style/class_and_module_children.rb +2 -9
- data/lib/rubocop/cop/style/comparable_clamp.rb +125 -0
- data/lib/rubocop/cop/style/concat_array_literals.rb +86 -0
- data/lib/rubocop/cop/style/conditional_assignment.rb +0 -6
- data/lib/rubocop/cop/style/documentation.rb +11 -5
- data/lib/rubocop/cop/style/guard_clause.rb +44 -9
- data/lib/rubocop/cop/style/hash_each_methods.rb +13 -1
- data/lib/rubocop/cop/style/hash_syntax.rb +11 -7
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +15 -0
- data/lib/rubocop/cop/style/if_with_semicolon.rb +4 -4
- data/lib/rubocop/cop/style/infinite_loop.rb +2 -5
- data/lib/rubocop/cop/style/inverse_methods.rb +2 -0
- data/lib/rubocop/cop/style/invertible_unless_condition.rb +114 -0
- data/lib/rubocop/cop/style/line_end_concatenation.rb +4 -1
- data/lib/rubocop/cop/style/map_to_set.rb +61 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +23 -14
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +2 -0
- data/lib/rubocop/cop/style/method_def_parentheses.rb +11 -4
- data/lib/rubocop/cop/style/min_max_comparison.rb +83 -0
- data/lib/rubocop/cop/style/missing_else.rb +13 -1
- data/lib/rubocop/cop/style/multiline_if_modifier.rb +0 -4
- data/lib/rubocop/cop/style/multiline_memoization.rb +2 -2
- data/lib/rubocop/cop/style/negated_if_else_condition.rb +1 -5
- data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
- data/lib/rubocop/cop/style/one_line_conditional.rb +3 -6
- data/lib/rubocop/cop/style/operator_method_call.rb +15 -1
- data/lib/rubocop/cop/style/parallel_assignment.rb +3 -1
- data/lib/rubocop/cop/style/redundant_argument.rb +3 -0
- data/lib/rubocop/cop/style/redundant_conditional.rb +0 -4
- data/lib/rubocop/cop/style/redundant_constant_base.rb +85 -0
- data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +45 -0
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +2 -1
- data/lib/rubocop/cop/style/redundant_return.rb +7 -0
- data/lib/rubocop/cop/style/redundant_sort.rb +1 -1
- data/lib/rubocop/cop/style/redundant_string_escape.rb +6 -3
- data/lib/rubocop/cop/style/require_order.rb +135 -0
- data/lib/rubocop/cop/style/safe_navigation.rb +35 -6
- data/lib/rubocop/cop/style/select_by_regexp.rb +13 -5
- data/lib/rubocop/cop/style/self_assignment.rb +2 -2
- data/lib/rubocop/cop/style/semicolon.rb +26 -3
- data/lib/rubocop/cop/style/signal_exception.rb +8 -6
- data/lib/rubocop/cop/style/string_hash_keys.rb +4 -1
- data/lib/rubocop/cop/style/string_literals.rb +1 -5
- data/lib/rubocop/cop/style/symbol_proc.rb +2 -4
- data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +4 -4
- data/lib/rubocop/cop/style/word_array.rb +41 -0
- data/lib/rubocop/cop/style/yoda_expression.rb +81 -0
- data/lib/rubocop/cop/style/zero_length_predicate.rb +31 -14
- data/lib/rubocop/cop/team.rb +30 -30
- data/lib/rubocop/cop/util.rb +32 -5
- data/lib/rubocop/cop/variable_force/assignment.rb +1 -1
- data/lib/rubocop/cop/variable_force/variable_table.rb +3 -1
- data/lib/rubocop/cop/variable_force.rb +18 -30
- data/lib/rubocop/cops_documentation_generator.rb +33 -11
- data/lib/rubocop/directive_comment.rb +1 -1
- data/lib/rubocop/file_patterns.rb +43 -0
- data/lib/rubocop/formatter/disabled_config_formatter.rb +17 -6
- data/lib/rubocop/formatter/html_formatter.rb +1 -1
- data/lib/rubocop/formatter.rb +4 -1
- data/lib/rubocop/options.rb +8 -0
- data/lib/rubocop/path_util.rb +50 -22
- data/lib/rubocop/result_cache.rb +2 -2
- data/lib/rubocop/rspec/cop_helper.rb +4 -1
- data/lib/rubocop/rspec/expect_offense.rb +6 -4
- data/lib/rubocop/rspec/support.rb +2 -2
- data/lib/rubocop/runner.rb +10 -3
- data/lib/rubocop/server/cache.rb +3 -1
- data/lib/rubocop/server/core.rb +1 -1
- data/lib/rubocop/target_finder.rb +1 -1
- data/lib/rubocop/target_ruby.rb +1 -2
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +23 -6
- metadata +23 -9
data/lib/rubocop/cop/registry.rb
CHANGED
@@ -19,6 +19,33 @@ module RuboCop
|
|
19
19
|
class Registry
|
20
20
|
include Enumerable
|
21
21
|
|
22
|
+
def self.all
|
23
|
+
global.without_department(:Test).cops
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.qualified_cop_name(name, origin)
|
27
|
+
global.qualified_cop_name(name, origin)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Changes momentarily the global registry
|
31
|
+
# Intended for testing purposes
|
32
|
+
def self.with_temporary_global(temp_global = global.dup)
|
33
|
+
previous = @global
|
34
|
+
@global = temp_global
|
35
|
+
yield
|
36
|
+
ensure
|
37
|
+
@global = previous
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.reset!
|
41
|
+
@global = new
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.qualified_cop?(name)
|
45
|
+
badge = Badge.parse(name)
|
46
|
+
global.qualify_badge(badge).first == badge
|
47
|
+
end
|
48
|
+
|
22
49
|
attr_reader :options
|
23
50
|
|
24
51
|
def initialize(cops = [], options = {})
|
@@ -28,6 +55,9 @@ module RuboCop
|
|
28
55
|
|
29
56
|
@enrollment_queue = cops
|
30
57
|
@options = options
|
58
|
+
|
59
|
+
@enabled_cache = {}.compare_by_identity
|
60
|
+
@disabled_cache = {}.compare_by_identity
|
31
61
|
end
|
32
62
|
|
33
63
|
def enlist(cop)
|
@@ -61,7 +91,7 @@ module RuboCop
|
|
61
91
|
|
62
92
|
# @return [Boolean] Checks if given name is department
|
63
93
|
def department?(name)
|
64
|
-
departments.include?
|
94
|
+
departments.include?(name.to_sym)
|
65
95
|
end
|
66
96
|
|
67
97
|
def contains_cop_matching?(names)
|
@@ -72,27 +102,27 @@ module RuboCop
|
|
72
102
|
#
|
73
103
|
# @example gives back a correctly qualified cop name
|
74
104
|
#
|
75
|
-
#
|
76
|
-
#
|
77
|
-
# qualified_cop_name('Layout/EndOfLine') # => 'Layout/EndOfLine'
|
105
|
+
# registry = RuboCop::Cop::Registry
|
106
|
+
# registry.qualified_cop_name('Layout/EndOfLine', '') # => 'Layout/EndOfLine'
|
78
107
|
#
|
79
108
|
# @example fixes incorrect namespaces
|
80
109
|
#
|
81
|
-
#
|
82
|
-
#
|
110
|
+
# registry = RuboCop::Cop::Registry
|
111
|
+
# registry.qualified_cop_name('Lint/EndOfLine', '') # => 'Layout/EndOfLine'
|
83
112
|
#
|
84
113
|
# @example namespaces bare cop identifiers
|
85
114
|
#
|
86
|
-
#
|
87
|
-
#
|
115
|
+
# registry = RuboCop::Cop::Registry
|
116
|
+
# registry.qualified_cop_name('EndOfLine', '') # => 'Layout/EndOfLine'
|
88
117
|
#
|
89
118
|
# @example passes back unrecognized cop names
|
90
119
|
#
|
91
|
-
#
|
92
|
-
#
|
120
|
+
# registry = RuboCop::Cop::Registry
|
121
|
+
# registry.qualified_cop_name('NotACop', '') # => 'NotACop'
|
93
122
|
#
|
94
123
|
# @param name [String] Cop name extracted from config
|
95
124
|
# @param path [String, nil] Path of file that `name` was extracted from
|
125
|
+
# @param warn [Boolean] Print a warning if no department given for `name`
|
96
126
|
#
|
97
127
|
# @raise [AmbiguousCopName]
|
98
128
|
# if a bare identifier with two possible namespaces is provided
|
@@ -133,6 +163,13 @@ module RuboCop
|
|
133
163
|
'RedundantCopDisableDirective'
|
134
164
|
end
|
135
165
|
|
166
|
+
def qualify_badge(badge)
|
167
|
+
clear_enrollment_queue
|
168
|
+
@departments
|
169
|
+
.map { |department, _| badge.with_department(department) }
|
170
|
+
.select { |potential_badge| registered?(potential_badge) }
|
171
|
+
end
|
172
|
+
|
136
173
|
# @return [Hash{String => Array<Class>}]
|
137
174
|
def to_h
|
138
175
|
clear_enrollment_queue
|
@@ -150,15 +187,15 @@ module RuboCop
|
|
150
187
|
end
|
151
188
|
|
152
189
|
def enabled(config)
|
153
|
-
select { |cop| enabled?(cop, config) }
|
190
|
+
@enabled_cache[config] ||= select { |cop| enabled?(cop, config) }
|
154
191
|
end
|
155
192
|
|
156
193
|
def disabled(config)
|
157
|
-
reject { |cop| enabled?(cop, config) }
|
194
|
+
@disabled_cache[config] ||= reject { |cop| enabled?(cop, config) }
|
158
195
|
end
|
159
196
|
|
160
197
|
def enabled?(cop, config)
|
161
|
-
return true if options
|
198
|
+
return true if options[:only]&.include?(cop.cop_name)
|
162
199
|
|
163
200
|
cfg = config.for_cop(cop)
|
164
201
|
|
@@ -182,8 +219,12 @@ module RuboCop
|
|
182
219
|
cops.map(&:cop_name)
|
183
220
|
end
|
184
221
|
|
222
|
+
def cops_for_department(department)
|
223
|
+
cops.select { |cop| cop.department == department.to_sym }
|
224
|
+
end
|
225
|
+
|
185
226
|
def names_for_department(department)
|
186
|
-
|
227
|
+
cops_for_department(department).map(&:cop_name)
|
187
228
|
end
|
188
229
|
|
189
230
|
def ==(other)
|
@@ -211,6 +252,14 @@ module RuboCop
|
|
211
252
|
to_h[cop_name].first
|
212
253
|
end
|
213
254
|
|
255
|
+
# When a cop name is given returns a single-element array with the cop class.
|
256
|
+
# When a department name is given returns an array with all the cop classes
|
257
|
+
# for that department.
|
258
|
+
def find_cops_by_directive(directive)
|
259
|
+
cop = find_by_cop_name(directive)
|
260
|
+
cop ? [cop] : cops_for_department(directive)
|
261
|
+
end
|
262
|
+
|
214
263
|
def freeze
|
215
264
|
clear_enrollment_queue
|
216
265
|
unqualified_cop_names # build cache
|
@@ -223,28 +272,6 @@ module RuboCop
|
|
223
272
|
attr_reader :global
|
224
273
|
end
|
225
274
|
|
226
|
-
def self.all
|
227
|
-
global.without_department(:Test).cops
|
228
|
-
end
|
229
|
-
|
230
|
-
def self.qualified_cop_name(name, origin)
|
231
|
-
global.qualified_cop_name(name, origin)
|
232
|
-
end
|
233
|
-
|
234
|
-
# Changes momentarily the global registry
|
235
|
-
# Intended for testing purposes
|
236
|
-
def self.with_temporary_global(temp_global = global.dup)
|
237
|
-
previous = @global
|
238
|
-
@global = temp_global
|
239
|
-
yield
|
240
|
-
ensure
|
241
|
-
@global = previous
|
242
|
-
end
|
243
|
-
|
244
|
-
def self.reset!
|
245
|
-
@global = new
|
246
|
-
end
|
247
|
-
|
248
275
|
private
|
249
276
|
|
250
277
|
def initialize_copy(reg)
|
@@ -267,13 +294,6 @@ module RuboCop
|
|
267
294
|
self.class.new(cops)
|
268
295
|
end
|
269
296
|
|
270
|
-
def qualify_badge(badge)
|
271
|
-
clear_enrollment_queue
|
272
|
-
@departments
|
273
|
-
.map { |department, _| badge.with_department(department) }
|
274
|
-
.select { |potential_badge| registered?(potential_badge) }
|
275
|
-
end
|
276
|
-
|
277
297
|
def resolve_badge(given_badge, real_badge, source_path)
|
278
298
|
unless given_badge.match?(real_badge)
|
279
299
|
path = PathUtil.smart_path(source_path)
|
@@ -9,7 +9,8 @@ module RuboCop
|
|
9
9
|
# Manually combining hashes is error prone and hard to follow, especially
|
10
10
|
# when there are many values. Poor implementations may also introduce
|
11
11
|
# performance or security concerns if they are prone to collisions.
|
12
|
-
# Delegating to `Array#hash` is clearer
|
12
|
+
# Delegating to `Array#hash` is clearer and safer, although it might be slower
|
13
|
+
# depending on the use case.
|
13
14
|
#
|
14
15
|
# @safety
|
15
16
|
# This cop may be unsafe if the application logic depends on the hash
|
@@ -115,9 +115,7 @@ module RuboCop
|
|
115
115
|
def_node = find_corresponding_def_node(node)
|
116
116
|
return unless def_node
|
117
117
|
|
118
|
-
|
119
|
-
remove_node(corrector, node)
|
120
|
-
insert_def(corrector, node, def_node.source)
|
118
|
+
replace_def(corrector, node, def_node)
|
121
119
|
when :inline
|
122
120
|
remove_node(corrector, node)
|
123
121
|
select_grouped_def_nodes(node).each do |grouped_def_node|
|
@@ -173,7 +171,9 @@ module RuboCop
|
|
173
171
|
end
|
174
172
|
|
175
173
|
def find_argument_less_modifier_node(node)
|
176
|
-
node.parent
|
174
|
+
return unless (parent = node.parent)
|
175
|
+
|
176
|
+
parent.each_child_node(:send).find do |child|
|
177
177
|
child.method?(node.method_name) && child.arguments.empty?
|
178
178
|
end
|
179
179
|
end
|
@@ -184,17 +184,21 @@ module RuboCop
|
|
184
184
|
end.select(&:def_type?)
|
185
185
|
end
|
186
186
|
|
187
|
-
def
|
188
|
-
source =
|
187
|
+
def replace_def(corrector, node, def_node)
|
188
|
+
source = def_source(node, def_node)
|
189
189
|
argument_less_modifier_node = find_argument_less_modifier_node(node)
|
190
190
|
if argument_less_modifier_node
|
191
191
|
corrector.insert_after(argument_less_modifier_node, "\n\n#{source}")
|
192
|
+
elsif (ancestor = node.each_ancestor(:block, :class, :module).first)
|
193
|
+
|
194
|
+
corrector.insert_before(ancestor.loc.end, "#{node.method_name}\n\n#{source}\n")
|
192
195
|
else
|
193
|
-
corrector.
|
194
|
-
|
195
|
-
"#{node.method_name}\n\n#{source}\n"
|
196
|
-
)
|
196
|
+
corrector.replace(node, "#{node.method_name}\n\n#{source}")
|
197
|
+
return
|
197
198
|
end
|
199
|
+
|
200
|
+
remove_node(corrector, def_node)
|
201
|
+
remove_node(corrector, node)
|
198
202
|
end
|
199
203
|
|
200
204
|
def insert_inline_modifier(corrector, node, modifier_name)
|
@@ -204,6 +208,10 @@ module RuboCop
|
|
204
208
|
def remove_node(corrector, node)
|
205
209
|
corrector.remove(range_with_comments_and_lines(node))
|
206
210
|
end
|
211
|
+
|
212
|
+
def def_source(node, def_node)
|
213
|
+
[*processed_source.ast_with_comments[node].map(&:text), def_node.source].join("\n")
|
214
|
+
end
|
207
215
|
end
|
208
216
|
end
|
209
217
|
end
|
@@ -7,6 +7,11 @@ module RuboCop
|
|
7
7
|
# depending on configuration.
|
8
8
|
# It also flags uses of `alias :symbol` rather than `alias bareword`.
|
9
9
|
#
|
10
|
+
# However, it will always enforce `method_alias` when used `alias`
|
11
|
+
# in an instance method definition and in a singleton method definition.
|
12
|
+
# If used in a block, always enforce `alias_method`
|
13
|
+
# unless it is an `instance_eval` block.
|
14
|
+
#
|
10
15
|
# @example EnforcedStyle: prefer_alias (default)
|
11
16
|
# # bad
|
12
17
|
# alias_method :bar, :foo
|
@@ -22,6 +27,7 @@ module RuboCop
|
|
22
27
|
#
|
23
28
|
# # good
|
24
29
|
# alias_method :bar, :foo
|
30
|
+
#
|
25
31
|
class Alias < Base
|
26
32
|
include ConfigurableEnforcedStyle
|
27
33
|
extend AutoCorrector
|
@@ -71,7 +77,9 @@ module RuboCop
|
|
71
77
|
end
|
72
78
|
|
73
79
|
def alias_method_possible?(node)
|
74
|
-
scope_type(node) != :instance_eval &&
|
80
|
+
scope_type(node) != :instance_eval &&
|
81
|
+
node.children.none?(&:gvar_type?) &&
|
82
|
+
node&.parent&.type != :def
|
75
83
|
end
|
76
84
|
|
77
85
|
def add_offense_for_args(node, &block)
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# In Ruby 3.1, `Array#intersect?` has been added.
|
7
|
+
#
|
8
|
+
# This cop identifies places where `(array1 & array2).any?`
|
9
|
+
# can be replaced by `array1.intersect?(array2)`.
|
10
|
+
#
|
11
|
+
# The `array1.intersect?(array2)` method is faster than
|
12
|
+
# `(array1 & array2).any?` and is more readable.
|
13
|
+
#
|
14
|
+
# @safety
|
15
|
+
# This cop cannot guarantee that array1 and array2 are
|
16
|
+
# actually arrays while method `intersect?` is for arrays only.
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
# # bad
|
20
|
+
# (array1 & array2).any?
|
21
|
+
# (array1 & array2).empty?
|
22
|
+
#
|
23
|
+
# # good
|
24
|
+
# array1.intersect?(array2)
|
25
|
+
# !array1.intersect?(array2)
|
26
|
+
#
|
27
|
+
# @example AllCops:ActiveSupportExtensionsEnabled: false (default)
|
28
|
+
# # good
|
29
|
+
# (array1 & array2).present?
|
30
|
+
# (array1 & array2).blank?
|
31
|
+
#
|
32
|
+
# @example AllCops:ActiveSupportExtensionsEnabled: true
|
33
|
+
# # bad
|
34
|
+
# (array1 & array2).present?
|
35
|
+
# (array1 & array2).blank?
|
36
|
+
#
|
37
|
+
# # good
|
38
|
+
# array1.intersect?(array2)
|
39
|
+
# !array1.intersect?(array2)
|
40
|
+
class ArrayIntersect < Base
|
41
|
+
extend AutoCorrector
|
42
|
+
extend TargetRubyVersion
|
43
|
+
|
44
|
+
minimum_target_ruby_version 3.1
|
45
|
+
|
46
|
+
# @!method regular_bad_intersection_check?(node)
|
47
|
+
def_node_matcher :regular_bad_intersection_check?, <<~PATTERN
|
48
|
+
(send
|
49
|
+
(begin
|
50
|
+
(send $(...) :& $(...))
|
51
|
+
) ${:any? :empty?}
|
52
|
+
)
|
53
|
+
PATTERN
|
54
|
+
|
55
|
+
# @!method active_support_bad_intersection_check?(node)
|
56
|
+
def_node_matcher :active_support_bad_intersection_check?, <<~PATTERN
|
57
|
+
(send
|
58
|
+
(begin
|
59
|
+
(send $(...) :& $(...))
|
60
|
+
) ${:present? :any? :blank? :empty?}
|
61
|
+
)
|
62
|
+
PATTERN
|
63
|
+
|
64
|
+
MSG = 'Use `%<negated>s%<receiver>s.intersect?(%<argument>s)` ' \
|
65
|
+
'instead of `(%<receiver>s & %<argument>s).%<method_name>s`.'
|
66
|
+
STRAIGHT_METHODS = %i[present? any?].freeze
|
67
|
+
NEGATED_METHODS = %i[blank? empty?].freeze
|
68
|
+
RESTRICT_ON_SEND = (STRAIGHT_METHODS + NEGATED_METHODS).freeze
|
69
|
+
|
70
|
+
def on_send(node)
|
71
|
+
return unless (receiver, argument, method_name = bad_intersection_check?(node))
|
72
|
+
|
73
|
+
message = message(receiver.source, argument.source, method_name)
|
74
|
+
|
75
|
+
add_offense(node, message: message) do |corrector|
|
76
|
+
if straight?(method_name)
|
77
|
+
corrector.replace(node, "#{receiver.source}.intersect?(#{argument.source})")
|
78
|
+
else
|
79
|
+
corrector.replace(node, "!#{receiver.source}.intersect?(#{argument.source})")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def bad_intersection_check?(node)
|
87
|
+
if active_support_extensions_enabled?
|
88
|
+
active_support_bad_intersection_check?(node)
|
89
|
+
else
|
90
|
+
regular_bad_intersection_check?(node)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def straight?(method_name)
|
95
|
+
STRAIGHT_METHODS.include?(method_name.to_sym)
|
96
|
+
end
|
97
|
+
|
98
|
+
def message(receiver, argument, method_name)
|
99
|
+
negated = straight?(method_name) ? '' : '!'
|
100
|
+
format(
|
101
|
+
MSG,
|
102
|
+
negated: negated,
|
103
|
+
receiver: receiver,
|
104
|
+
argument: argument,
|
105
|
+
method_name: method_name
|
106
|
+
)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -32,7 +32,7 @@ module RuboCop
|
|
32
32
|
eq_begin, eq_end, contents = parts(comment)
|
33
33
|
|
34
34
|
corrector.remove(eq_begin)
|
35
|
-
unless contents.
|
35
|
+
unless contents.empty?
|
36
36
|
corrector.replace(
|
37
37
|
contents,
|
38
38
|
contents.source.gsub(/\A/, '# ').gsub(/\n\n/, "\n#\n").gsub(/\n(?=[^#])/, "\n# ")
|
@@ -299,8 +299,8 @@ module RuboCop
|
|
299
299
|
|
300
300
|
def move_comment_before_block(corrector, comment, block_node, closing_brace)
|
301
301
|
range = block_node.chained? ? end_of_chain(block_node.parent).source_range : closing_brace
|
302
|
-
|
303
|
-
corrector
|
302
|
+
corrector.remove(range_with_surrounding_space(comment.loc.expression, side: :right))
|
303
|
+
remove_trailing_whitespace(corrector, range, comment)
|
304
304
|
corrector.insert_after(range, "\n")
|
305
305
|
|
306
306
|
corrector.insert_before(block_node, "#{comment.text}\n")
|
@@ -313,6 +313,12 @@ module RuboCop
|
|
313
313
|
end_of_chain(node.parent)
|
314
314
|
end
|
315
315
|
|
316
|
+
def remove_trailing_whitespace(corrector, range, comment)
|
317
|
+
range_of_trailing = range.end.join(comment.loc.expression.begin)
|
318
|
+
|
319
|
+
corrector.remove(range_of_trailing) if range_of_trailing.source.match?(/\A\s+\z/)
|
320
|
+
end
|
321
|
+
|
316
322
|
def with_block?(node)
|
317
323
|
node.respond_to?(:block_node) && node.block_node
|
318
324
|
end
|
@@ -31,6 +31,7 @@ module RuboCop
|
|
31
31
|
#
|
32
32
|
# The compact style is only forced for classes/modules with one child.
|
33
33
|
class ClassAndModuleChildren < Base
|
34
|
+
include Alignment
|
34
35
|
include ConfigurableEnforcedStyle
|
35
36
|
include RangeHelp
|
36
37
|
extend AutoCorrector
|
@@ -59,7 +60,7 @@ module RuboCop
|
|
59
60
|
end
|
60
61
|
|
61
62
|
def nest_definition(corrector, node)
|
62
|
-
padding = (
|
63
|
+
padding = indentation(node) + leading_spaces(node)
|
63
64
|
padding_for_trailing_end = padding.sub(' ' * node.loc.end.column, '')
|
64
65
|
|
65
66
|
replace_namespace_keyword(corrector, node)
|
@@ -124,10 +125,6 @@ module RuboCop
|
|
124
125
|
corrector.remove(range)
|
125
126
|
end
|
126
127
|
|
127
|
-
def configured_indentation_width
|
128
|
-
config.for_badge(Layout::IndentationWidth.badge).fetch('Width', 2)
|
129
|
-
end
|
130
|
-
|
131
128
|
def unindent(corrector, node)
|
132
129
|
return if node.body.children.last.nil?
|
133
130
|
|
@@ -141,10 +138,6 @@ module RuboCop
|
|
141
138
|
node.source_range.source_line[/\A\s*/]
|
142
139
|
end
|
143
140
|
|
144
|
-
def indent_width
|
145
|
-
@config.for_cop('Layout/IndentationWidth')['Width'] || 2
|
146
|
-
end
|
147
|
-
|
148
141
|
def check_style(node, body)
|
149
142
|
return if node.identifier.children[0]&.cbase_type?
|
150
143
|
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Enforces the use of `Comparable#clamp` instead of comparison by minimum and maximum.
|
7
|
+
#
|
8
|
+
# This cop supports autocorrection for `if/elsif/else` bad style only.
|
9
|
+
# Because `ArgumentError` occurs if the minimum and maximum of `clamp` arguments are reversed.
|
10
|
+
# When these are variables, it is not possible to determine which is the minimum and maximum:
|
11
|
+
#
|
12
|
+
# [source,ruby]
|
13
|
+
# ----
|
14
|
+
# [1, [2, 3].max].min # => 1
|
15
|
+
# 1.clamp(3, 1) # => min argument must be smaller than max argument (ArgumentError)
|
16
|
+
# ----
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
#
|
20
|
+
# # bad
|
21
|
+
# [[x, low].max, high].min
|
22
|
+
#
|
23
|
+
# # bad
|
24
|
+
# if x < low
|
25
|
+
# low
|
26
|
+
# elsif high < x
|
27
|
+
# high
|
28
|
+
# else
|
29
|
+
# x
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# # good
|
33
|
+
# x.clamp(low, high)
|
34
|
+
#
|
35
|
+
class ComparableClamp < Base
|
36
|
+
include Alignment
|
37
|
+
extend AutoCorrector
|
38
|
+
extend TargetRubyVersion
|
39
|
+
|
40
|
+
minimum_target_ruby_version 2.4
|
41
|
+
|
42
|
+
MSG = 'Use `%<prefer>s` instead of `if/elsif/else`.'
|
43
|
+
MSG_MIN_MAX = 'Use `Comparable#clamp` instead.'
|
44
|
+
RESTRICT_ON_SEND = %i[min max].freeze
|
45
|
+
|
46
|
+
# @!method if_elsif_else_condition?(node)
|
47
|
+
def_node_matcher :if_elsif_else_condition?, <<~PATTERN
|
48
|
+
{
|
49
|
+
(if (send _x :< _min) _min (if (send _max :< _x) _max _x))
|
50
|
+
(if (send _min :> _x) _min (if (send _max :< _x) _max _x))
|
51
|
+
(if (send _x :< _min) _min (if (send _x :> _max) _max _x))
|
52
|
+
(if (send _min :> _x) _min (if (send _x :> _max) _max _x))
|
53
|
+
(if (send _max :< _x) _max (if (send _x :< _min) _min _x))
|
54
|
+
(if (send _x :> _max) _max (if (send _x :< _min) _min _x))
|
55
|
+
(if (send _max :< _x) _max (if (send _min :> _x) _min _x))
|
56
|
+
(if (send _x :> _max) _max (if (send _min :> _x) _min _x))
|
57
|
+
}
|
58
|
+
PATTERN
|
59
|
+
|
60
|
+
# @!method array_min_max?(node)
|
61
|
+
def_node_matcher :array_min_max?, <<~PATTERN
|
62
|
+
{
|
63
|
+
(send
|
64
|
+
(array
|
65
|
+
(send (array _ _) :max) _) :min)
|
66
|
+
(send
|
67
|
+
(array
|
68
|
+
_ (send (array _ _) :max)) :min)
|
69
|
+
(send
|
70
|
+
(array
|
71
|
+
(send (array _ _) :min) _) :max)
|
72
|
+
(send
|
73
|
+
(array
|
74
|
+
_ (send (array _ _) :min)) :max)
|
75
|
+
}
|
76
|
+
PATTERN
|
77
|
+
|
78
|
+
def on_if(node)
|
79
|
+
return unless if_elsif_else_condition?(node)
|
80
|
+
|
81
|
+
if_body, elsif_body, else_body = *node.branches
|
82
|
+
|
83
|
+
else_body_source = else_body.source
|
84
|
+
|
85
|
+
if min_condition?(node.condition, else_body_source)
|
86
|
+
min = if_body.source
|
87
|
+
max = elsif_body.source
|
88
|
+
else
|
89
|
+
min = elsif_body.source
|
90
|
+
max = if_body.source
|
91
|
+
end
|
92
|
+
|
93
|
+
prefer = "#{else_body_source}.clamp(#{min}, #{max})"
|
94
|
+
|
95
|
+
add_offense(node, message: format(MSG, prefer: prefer)) do |corrector|
|
96
|
+
autocorrect(corrector, node, prefer)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def on_send(node)
|
101
|
+
return unless array_min_max?(node)
|
102
|
+
|
103
|
+
add_offense(node, message: MSG_MIN_MAX)
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def autocorrect(corrector, node, prefer)
|
109
|
+
if node.elsif?
|
110
|
+
corrector.insert_before(node, "else\n")
|
111
|
+
corrector.replace(node, "#{indentation(node)}#{prefer}")
|
112
|
+
else
|
113
|
+
corrector.replace(node, prefer)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def min_condition?(if_condition, else_body)
|
118
|
+
lhs, op, rhs = *if_condition
|
119
|
+
|
120
|
+
(lhs.source == else_body && op == :<) || (rhs.source == else_body && op == :>)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Enforces the use of `Array#push(item)` instead of `Array#concat([item])`
|
7
|
+
# to avoid redundant array literals.
|
8
|
+
#
|
9
|
+
# @safety
|
10
|
+
# This cop is unsafe, as it can produce false positives if the receiver
|
11
|
+
# is not an `Array` object.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
#
|
15
|
+
# # bad
|
16
|
+
# list.concat([foo])
|
17
|
+
# list.concat([bar, baz])
|
18
|
+
# list.concat([qux, quux], [corge])
|
19
|
+
#
|
20
|
+
# # good
|
21
|
+
# list.push(foo)
|
22
|
+
# list.push(bar, baz)
|
23
|
+
# list.push(qux, quux, corge)
|
24
|
+
#
|
25
|
+
class ConcatArrayLiterals < Base
|
26
|
+
extend AutoCorrector
|
27
|
+
|
28
|
+
MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
|
29
|
+
MSG_FOR_PERCENT_LITERALS =
|
30
|
+
'Use `push` with elements as arguments without array brackets instead of `%<current>s`.'
|
31
|
+
RESTRICT_ON_SEND = %i[concat].freeze
|
32
|
+
|
33
|
+
# rubocop:disable Metrics
|
34
|
+
def on_send(node)
|
35
|
+
return if node.arguments.empty?
|
36
|
+
return unless node.arguments.all?(&:array_type?)
|
37
|
+
|
38
|
+
offense = offense_range(node)
|
39
|
+
current = offense.source
|
40
|
+
|
41
|
+
if node.arguments.any?(&:percent_literal?)
|
42
|
+
if percent_literals_includes_only_basic_literals?(node)
|
43
|
+
prefer = preferred_method(node)
|
44
|
+
message = format(MSG, prefer: prefer, current: current)
|
45
|
+
else
|
46
|
+
message = format(MSG_FOR_PERCENT_LITERALS, current: current)
|
47
|
+
end
|
48
|
+
else
|
49
|
+
prefer = preferred_method(node)
|
50
|
+
message = format(MSG, prefer: prefer, current: current)
|
51
|
+
end
|
52
|
+
|
53
|
+
add_offense(offense, message: message) do |corrector|
|
54
|
+
corrector.replace(offense, prefer)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
# rubocop:enable Metrics
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def offense_range(node)
|
62
|
+
node.loc.selector.join(node.source_range.end)
|
63
|
+
end
|
64
|
+
|
65
|
+
def preferred_method(node)
|
66
|
+
new_arguments =
|
67
|
+
node.arguments.map do |arg|
|
68
|
+
if arg.percent_literal?
|
69
|
+
arg.children.map(&:value).map(&:inspect)
|
70
|
+
else
|
71
|
+
arg.children.map(&:source)
|
72
|
+
end
|
73
|
+
end.join(', ')
|
74
|
+
|
75
|
+
"push(#{new_arguments})"
|
76
|
+
end
|
77
|
+
|
78
|
+
def percent_literals_includes_only_basic_literals?(node)
|
79
|
+
node.arguments.select(&:percent_literal?).all? do |arg|
|
80
|
+
arg.children.all? { |child| child.str_type? || child.sym_type? }
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|