rubocop 0.77.0 → 0.81.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/LICENSE.txt +1 -1
- data/README.md +3 -3
- data/config/default.yml +136 -60
- data/lib/rubocop.rb +20 -4
- data/lib/rubocop/ast/builder.rb +45 -42
- data/lib/rubocop/ast/node.rb +11 -18
- data/lib/rubocop/ast/node/block_node.rb +5 -1
- data/lib/rubocop/ast/node/case_match_node.rb +56 -0
- data/lib/rubocop/ast/node/def_node.rb +11 -0
- data/lib/rubocop/ast/node/forward_args_node.rb +18 -0
- data/lib/rubocop/ast/node/regexp_node.rb +2 -4
- data/lib/rubocop/ast/traversal.rb +29 -10
- data/lib/rubocop/cli/command/auto_genenerate_config.rb +7 -7
- data/lib/rubocop/cli/command/show_cops.rb +11 -4
- data/lib/rubocop/comment_config.rb +6 -1
- data/lib/rubocop/config.rb +28 -10
- data/lib/rubocop/config_loader.rb +19 -19
- data/lib/rubocop/config_obsoletion.rb +6 -4
- data/lib/rubocop/config_validator.rb +55 -95
- data/lib/rubocop/cop/autocorrect_logic.rb +7 -4
- data/lib/rubocop/cop/bundler/insecure_protocol_source.rb +2 -2
- data/lib/rubocop/cop/cop.rb +3 -1
- data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
- data/lib/rubocop/cop/generator.rb +3 -4
- data/lib/rubocop/cop/generator/configuration_injector.rb +1 -1
- data/lib/rubocop/cop/layout/array_alignment.rb +53 -10
- data/lib/rubocop/cop/layout/block_end_newline.rb +5 -3
- data/lib/rubocop/cop/layout/else_alignment.rb +8 -0
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +2 -1
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +2 -2
- data/lib/rubocop/cop/layout/hash_alignment.rb +8 -4
- data/lib/rubocop/cop/layout/heredoc_indentation.rb +4 -4
- data/lib/rubocop/cop/layout/leading_comment_space.rb +33 -2
- data/lib/rubocop/cop/{metrics → layout}/line_length.rb +35 -79
- data/lib/rubocop/cop/layout/multiline_block_layout.rb +14 -5
- data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +0 -4
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_operators.rb +49 -6
- data/lib/rubocop/cop/layout/space_before_block_braces.rb +17 -0
- data/lib/rubocop/cop/layout/space_before_first_arg.rb +8 -0
- data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +2 -9
- data/lib/rubocop/cop/lint/boolean_symbol.rb +12 -0
- data/lib/rubocop/cop/lint/debugger.rb +1 -1
- data/lib/rubocop/cop/lint/each_with_object_argument.rb +1 -1
- data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
- data/lib/rubocop/cop/lint/loop.rb +6 -4
- data/lib/rubocop/cop/lint/nested_method_definition.rb +2 -2
- data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +89 -0
- data/lib/rubocop/cop/lint/raise_exception.rb +39 -0
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +3 -3
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +13 -8
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +1 -1
- data/lib/rubocop/cop/lint/struct_new_override.rb +58 -0
- data/lib/rubocop/cop/lint/suppressed_exception.rb +12 -22
- data/lib/rubocop/cop/lint/unused_method_argument.rb +32 -6
- data/lib/rubocop/cop/lint/useless_setter_call.rb +4 -0
- data/lib/rubocop/cop/migration/department_name.rb +47 -6
- data/lib/rubocop/cop/mixin/alignment.rb +1 -1
- data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +4 -0
- data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +6 -1
- data/lib/rubocop/cop/mixin/frozen_string_literal.rb +7 -7
- data/lib/rubocop/cop/mixin/hash_transform_method.rb +171 -0
- data/lib/rubocop/cop/mixin/line_length_help.rb +88 -0
- data/lib/rubocop/cop/mixin/method_complexity.rb +5 -0
- data/lib/rubocop/cop/mixin/rational_literal.rb +18 -0
- data/lib/rubocop/cop/mixin/statement_modifier.rb +2 -2
- data/lib/rubocop/cop/mixin/trailing_comma.rb +8 -12
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
- data/lib/rubocop/cop/naming/method_name.rb +30 -0
- data/lib/rubocop/cop/naming/method_parameter_name.rb +1 -1
- data/lib/rubocop/cop/offense.rb +11 -0
- data/lib/rubocop/cop/registry.rb +7 -2
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +26 -6
- data/lib/rubocop/cop/style/attr.rb +8 -0
- data/lib/rubocop/cop/style/block_delimiters.rb +60 -1
- data/lib/rubocop/cop/style/collection_methods.rb +2 -0
- data/lib/rubocop/cop/style/conditional_assignment.rb +2 -2
- data/lib/rubocop/cop/style/documentation.rb +43 -5
- data/lib/rubocop/cop/style/end_block.rb +6 -0
- data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +89 -11
- data/lib/rubocop/cop/style/guard_clause.rb +3 -2
- data/lib/rubocop/cop/style/hash_each_methods.rb +89 -0
- data/lib/rubocop/cop/style/hash_transform_keys.rb +83 -0
- data/lib/rubocop/cop/style/hash_transform_values.rb +83 -0
- data/lib/rubocop/cop/style/if_unless_modifier.rb +38 -3
- data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
- data/lib/rubocop/cop/style/inverse_methods.rb +9 -5
- data/lib/rubocop/cop/style/lambda.rb +1 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +7 -205
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +169 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +54 -0
- data/lib/rubocop/cop/style/module_function.rb +56 -10
- data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -1
- data/lib/rubocop/cop/style/multiline_when_then.rb +5 -1
- data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +4 -4
- data/lib/rubocop/cop/style/numeric_predicate.rb +4 -3
- data/lib/rubocop/cop/style/one_line_conditional.rb +3 -2
- data/lib/rubocop/cop/style/or_assignment.rb +3 -2
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +7 -7
- data/lib/rubocop/cop/style/redundant_condition.rb +17 -4
- data/lib/rubocop/cop/style/redundant_sort.rb +2 -2
- data/lib/rubocop/cop/style/symbol_array.rb +2 -2
- data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +34 -22
- data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +41 -0
- data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +85 -0
- data/lib/rubocop/cop/style/trailing_comma_in_hash_literal.rb +44 -0
- data/lib/rubocop/cop/style/trailing_underscore_variable.rb +7 -1
- data/lib/rubocop/cop/style/while_until_modifier.rb +1 -1
- data/lib/rubocop/cop/style/yoda_condition.rb +16 -1
- data/lib/rubocop/cop/variable_force.rb +4 -1
- data/lib/rubocop/formatter/base_formatter.rb +2 -2
- data/lib/rubocop/formatter/clang_style_formatter.rb +1 -1
- data/lib/rubocop/formatter/formatter_set.rb +1 -0
- data/lib/rubocop/formatter/json_formatter.rb +6 -5
- data/lib/rubocop/formatter/junit_formatter.rb +74 -0
- data/lib/rubocop/formatter/tap_formatter.rb +1 -1
- data/lib/rubocop/node_pattern.rb +97 -11
- data/lib/rubocop/options.rb +8 -8
- data/lib/rubocop/processed_source.rb +1 -1
- data/lib/rubocop/result_cache.rb +2 -0
- data/lib/rubocop/rspec/shared_contexts.rb +5 -0
- data/lib/rubocop/runner.rb +5 -1
- data/lib/rubocop/target_ruby.rb +151 -0
- data/lib/rubocop/version.rb +1 -1
- metadata +38 -10
- data/lib/rubocop/cop/lint/end_in_method.rb +0 -40
- data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +0 -209
@@ -27,6 +27,7 @@ module RuboCop
|
|
27
27
|
# items.inject
|
28
28
|
# items.detect
|
29
29
|
# items.find_all
|
30
|
+
# items.member?
|
30
31
|
#
|
31
32
|
# # good
|
32
33
|
# items.map
|
@@ -34,6 +35,7 @@ module RuboCop
|
|
34
35
|
# items.reduce
|
35
36
|
# items.find
|
36
37
|
# items.select
|
38
|
+
# items.include?
|
37
39
|
#
|
38
40
|
class CollectionMethods < Cop
|
39
41
|
include MethodPreference
|
@@ -212,7 +212,7 @@ module RuboCop
|
|
212
212
|
%i[casgn cvasgn gvasgn ivasgn lvasgn].freeze
|
213
213
|
ASSIGNMENT_TYPES = VARIABLE_ASSIGNMENT_TYPES +
|
214
214
|
%i[and_asgn or_asgn op_asgn masgn].freeze
|
215
|
-
LINE_LENGTH = '
|
215
|
+
LINE_LENGTH = 'Layout/LineLength'
|
216
216
|
INDENTATION_WIDTH = 'Layout/IndentationWidth'
|
217
217
|
ENABLED = 'Enabled'
|
218
218
|
MAX = 'Max'
|
@@ -376,7 +376,7 @@ module RuboCop
|
|
376
376
|
assignment_types_match?(*statements)
|
377
377
|
end
|
378
378
|
|
379
|
-
# If `
|
379
|
+
# If `Layout/LineLength` is enabled, we do not want to introduce an
|
380
380
|
# offense by auto-correcting this cop. Find the max configured line
|
381
381
|
# length. Find the longest line of condition. Remove the assignment
|
382
382
|
# from lines that contain the offending assignment because after
|
@@ -3,10 +3,11 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Style
|
6
|
-
# This cop checks for missing top-level documentation of
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
6
|
+
# This cop checks for missing top-level documentation of classes and
|
7
|
+
# modules. Classes with no body are exempt from the check and so are
|
8
|
+
# namespace modules - modules that have nothing in their bodies except
|
9
|
+
# classes, other modules, constant definitions or constant visibility
|
10
|
+
# declarations.
|
10
11
|
#
|
11
12
|
# The documentation requirement is annulled if the class or module has
|
12
13
|
# a "#:nodoc:" comment next to it. Likewise, "#:nodoc: all" does the
|
@@ -18,12 +19,42 @@ module RuboCop
|
|
18
19
|
# # ...
|
19
20
|
# end
|
20
21
|
#
|
22
|
+
# module Math
|
23
|
+
# end
|
24
|
+
#
|
21
25
|
# # good
|
22
26
|
# # Description/Explanation of Person class
|
23
27
|
# class Person
|
24
28
|
# # ...
|
25
29
|
# end
|
26
30
|
#
|
31
|
+
# # allowed
|
32
|
+
# # Class without body
|
33
|
+
# class Person
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# # Namespace - A namespace can be a class or a module
|
37
|
+
# # Containing a class
|
38
|
+
# module Namespace
|
39
|
+
# # Description/Explanation of Person class
|
40
|
+
# class Person
|
41
|
+
# # ...
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# # Containing constant visibility declaration
|
46
|
+
# module Namespace
|
47
|
+
# class Private
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# private_constant :Private
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# # Containing constant definition
|
54
|
+
# module Namespace
|
55
|
+
# Public = Class.new
|
56
|
+
# end
|
57
|
+
#
|
27
58
|
class Documentation < Cop
|
28
59
|
include DocumentationComment
|
29
60
|
|
@@ -31,6 +62,9 @@ module RuboCop
|
|
31
62
|
|
32
63
|
def_node_matcher :constant_definition?, '{class module casgn}'
|
33
64
|
def_node_search :outer_module, '(const (const nil? _) _)'
|
65
|
+
def_node_matcher :constant_visibility_declaration?, <<~PATTERN
|
66
|
+
(send nil? {:public_constant :private_constant} ({sym str} _))
|
67
|
+
PATTERN
|
34
68
|
|
35
69
|
def on_class(node)
|
36
70
|
return unless node.body
|
@@ -59,12 +93,16 @@ module RuboCop
|
|
59
93
|
return false unless node
|
60
94
|
|
61
95
|
if node.begin_type?
|
62
|
-
node.children.all?
|
96
|
+
node.children.all?(&method(:constant_declaration?))
|
63
97
|
else
|
64
98
|
constant_definition?(node)
|
65
99
|
end
|
66
100
|
end
|
67
101
|
|
102
|
+
def constant_declaration?(node)
|
103
|
+
constant_definition?(node) || constant_visibility_declaration?(node)
|
104
|
+
end
|
105
|
+
|
68
106
|
def compact_namespace?(node)
|
69
107
|
node.loc.name.source =~ /::/
|
70
108
|
end
|
@@ -51,29 +51,65 @@ module RuboCop
|
|
51
51
|
# module Baz
|
52
52
|
# # ...
|
53
53
|
# end
|
54
|
+
#
|
55
|
+
# @example EnforcedStyle: always_true
|
56
|
+
# # The `always_true` style enforces that the frozen string literal
|
57
|
+
# # comment is set to `true`. This is a stricter option than `always`
|
58
|
+
# # and forces projects to use frozen string literals.
|
59
|
+
# # bad
|
60
|
+
# # frozen_string_literal: false
|
61
|
+
#
|
62
|
+
# module Baz
|
63
|
+
# # ...
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# # bad
|
67
|
+
# module Baz
|
68
|
+
# # ...
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# # good
|
72
|
+
# # frozen_string_literal: true
|
73
|
+
#
|
74
|
+
# module Bar
|
75
|
+
# # ...
|
76
|
+
# end
|
54
77
|
class FrozenStringLiteralComment < Cop
|
55
78
|
include ConfigurableEnforcedStyle
|
56
79
|
include FrozenStringLiteral
|
57
80
|
include RangeHelp
|
58
81
|
|
59
|
-
|
82
|
+
MSG_MISSING_TRUE = 'Missing magic comment `# frozen_string_literal: '\
|
83
|
+
'true`.'
|
84
|
+
MSG_MISSING = 'Missing frozen string literal comment.'
|
60
85
|
MSG_UNNECESSARY = 'Unnecessary frozen string literal comment.'
|
86
|
+
MSG_DISABLED = 'Frozen string literal comment must be set to `true`.'
|
61
87
|
SHEBANG = '#!'
|
62
88
|
|
63
89
|
def investigate(processed_source)
|
64
90
|
return if processed_source.tokens.empty?
|
65
91
|
|
66
|
-
|
67
|
-
|
92
|
+
case style
|
93
|
+
when :never
|
94
|
+
ensure_no_comment(processed_source)
|
95
|
+
when :always_true
|
96
|
+
ensure_enabled_comment(processed_source)
|
68
97
|
else
|
69
|
-
|
98
|
+
ensure_comment(processed_source)
|
70
99
|
end
|
71
100
|
end
|
72
101
|
|
73
102
|
def autocorrect(node)
|
74
103
|
lambda do |corrector|
|
75
|
-
|
104
|
+
case style
|
105
|
+
when :never
|
76
106
|
remove_comment(corrector, node)
|
107
|
+
when :always_true
|
108
|
+
if frozen_string_literal_specified?
|
109
|
+
enable_comment(corrector)
|
110
|
+
else
|
111
|
+
insert_comment(corrector)
|
112
|
+
end
|
77
113
|
else
|
78
114
|
insert_comment(corrector)
|
79
115
|
end
|
@@ -82,12 +118,27 @@ module RuboCop
|
|
82
118
|
|
83
119
|
private
|
84
120
|
|
85
|
-
def
|
86
|
-
|
121
|
+
def ensure_no_comment(processed_source)
|
122
|
+
return unless frozen_string_literal_comment_exists?
|
123
|
+
|
124
|
+
unnecessary_comment_offense(processed_source)
|
87
125
|
end
|
88
126
|
|
89
|
-
def
|
90
|
-
|
127
|
+
def ensure_comment(processed_source)
|
128
|
+
return if frozen_string_literal_comment_exists?
|
129
|
+
|
130
|
+
missing_offense(processed_source)
|
131
|
+
end
|
132
|
+
|
133
|
+
def ensure_enabled_comment(processed_source)
|
134
|
+
if frozen_string_literal_specified?
|
135
|
+
return if frozen_string_literals_enabled?
|
136
|
+
|
137
|
+
# The comment exists, but is not enabled.
|
138
|
+
disabled_offense(processed_source)
|
139
|
+
else # The comment doesn't exist at all.
|
140
|
+
missing_true_offense(processed_source)
|
141
|
+
end
|
91
142
|
end
|
92
143
|
|
93
144
|
def last_special_comment(processed_source)
|
@@ -111,11 +162,22 @@ module RuboCop
|
|
111
162
|
end
|
112
163
|
end
|
113
164
|
|
114
|
-
def
|
165
|
+
def missing_offense(processed_source)
|
166
|
+
last_special_comment = last_special_comment(processed_source)
|
167
|
+
range = source_range(processed_source.buffer, 0, 0)
|
168
|
+
|
169
|
+
add_offense(last_special_comment,
|
170
|
+
location: range,
|
171
|
+
message: MSG_MISSING)
|
172
|
+
end
|
173
|
+
|
174
|
+
def missing_true_offense(processed_source)
|
115
175
|
last_special_comment = last_special_comment(processed_source)
|
116
176
|
range = source_range(processed_source.buffer, 0, 0)
|
117
177
|
|
118
|
-
add_offense(last_special_comment,
|
178
|
+
add_offense(last_special_comment,
|
179
|
+
location: range,
|
180
|
+
message: MSG_MISSING_TRUE)
|
119
181
|
end
|
120
182
|
|
121
183
|
def unnecessary_comment_offense(processed_source)
|
@@ -127,11 +189,27 @@ module RuboCop
|
|
127
189
|
message: MSG_UNNECESSARY)
|
128
190
|
end
|
129
191
|
|
192
|
+
def disabled_offense(processed_source)
|
193
|
+
frozen_string_literal_comment =
|
194
|
+
frozen_string_literal_comment(processed_source)
|
195
|
+
|
196
|
+
add_offense(frozen_string_literal_comment,
|
197
|
+
location: frozen_string_literal_comment.pos,
|
198
|
+
message: MSG_DISABLED)
|
199
|
+
end
|
200
|
+
|
130
201
|
def remove_comment(corrector, node)
|
131
202
|
corrector.remove(range_with_surrounding_space(range: node.pos,
|
132
203
|
side: :right))
|
133
204
|
end
|
134
205
|
|
206
|
+
def enable_comment(corrector)
|
207
|
+
comment = frozen_string_literal_comment(processed_source)
|
208
|
+
|
209
|
+
corrector.replace(line_range(comment.line),
|
210
|
+
FROZEN_STRING_LITERAL_ENABLED)
|
211
|
+
end
|
212
|
+
|
135
213
|
def insert_comment(corrector)
|
136
214
|
comment = last_special_comment(processed_source)
|
137
215
|
|
@@ -49,8 +49,9 @@ module RuboCop
|
|
49
49
|
|
50
50
|
if body.if_type?
|
51
51
|
check_ending_if(body)
|
52
|
-
elsif body.begin_type?
|
53
|
-
|
52
|
+
elsif body.begin_type?
|
53
|
+
final_expression = body.children.last
|
54
|
+
check_ending_if(final_expression) if final_expression&.if_type?
|
54
55
|
end
|
55
56
|
end
|
56
57
|
alias on_defs on_def
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# This cop checks for uses of `each_key` and `each_value` Hash methods.
|
7
|
+
#
|
8
|
+
# Note: If you have an array of two-element arrays, you can put
|
9
|
+
# parentheses around the block arguments to indicate that you're not
|
10
|
+
# working with a hash, and suppress RuboCop offenses.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# # bad
|
14
|
+
# hash.keys.each { |k| p k }
|
15
|
+
# hash.values.each { |v| p v }
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# hash.each_key { |k| p k }
|
19
|
+
# hash.each_value { |v| p v }
|
20
|
+
class HashEachMethods < Cop
|
21
|
+
include Lint::UnusedArgument
|
22
|
+
|
23
|
+
MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
|
24
|
+
|
25
|
+
def_node_matcher :kv_each, <<~PATTERN
|
26
|
+
(block $(send (send _ ${:keys :values}) :each) ...)
|
27
|
+
PATTERN
|
28
|
+
|
29
|
+
def on_block(node)
|
30
|
+
register_kv_offense(node)
|
31
|
+
end
|
32
|
+
|
33
|
+
def autocorrect(node)
|
34
|
+
lambda do |corrector|
|
35
|
+
correct_key_value_each(node, corrector)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def register_kv_offense(node)
|
42
|
+
kv_each(node) do |target, method|
|
43
|
+
return unless target.receiver.receiver
|
44
|
+
|
45
|
+
msg = format(message, prefer: "each_#{method[0..-2]}",
|
46
|
+
current: "#{method}.each")
|
47
|
+
|
48
|
+
add_offense(target, location: kv_range(target), message: msg)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def check_argument(variable)
|
53
|
+
return unless variable.block_argument?
|
54
|
+
|
55
|
+
(@block_args ||= []).push(variable)
|
56
|
+
end
|
57
|
+
|
58
|
+
def used?(arg)
|
59
|
+
@block_args.find { |var| var.declaration_node.loc == arg.loc }.used?
|
60
|
+
end
|
61
|
+
|
62
|
+
def correct_implicit(node, corrector, method_name)
|
63
|
+
corrector.replace(node.loc.expression, method_name)
|
64
|
+
correct_args(node, corrector)
|
65
|
+
end
|
66
|
+
|
67
|
+
def correct_key_value_each(node, corrector)
|
68
|
+
receiver = node.receiver.receiver
|
69
|
+
name = "each_#{node.receiver.method_name.to_s.chop}"
|
70
|
+
return correct_implicit(node, corrector, name) unless receiver
|
71
|
+
|
72
|
+
new_source = receiver.source + ".#{name}"
|
73
|
+
corrector.replace(node.loc.expression, new_source)
|
74
|
+
end
|
75
|
+
|
76
|
+
def correct_args(node, corrector)
|
77
|
+
args = node.parent.arguments
|
78
|
+
name, = *args.children.find { |arg| used?(arg) }
|
79
|
+
|
80
|
+
corrector.replace(args.source_range, "|#{name}|")
|
81
|
+
end
|
82
|
+
|
83
|
+
def kv_range(outer_node)
|
84
|
+
outer_node.receiver.loc.selector.join(outer_node.loc.selector)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# This cop looks for uses of `_.each_with_object({}) {...}`,
|
7
|
+
# `_.map {...}.to_h`, and `Hash[_.map {...}]` that are actually just
|
8
|
+
# transforming the keys of a hash, and tries to use a simpler & faster
|
9
|
+
# call to `transform_keys` instead.
|
10
|
+
#
|
11
|
+
# This can produce false positives if we are transforming an enumerable
|
12
|
+
# of key-value-like pairs that isn't actually a hash, e.g.:
|
13
|
+
# `[[k1, v1], [k2, v2], ...]`
|
14
|
+
#
|
15
|
+
# This cop should only be enabled on Ruby version 2.5 or newer
|
16
|
+
# (`transform_keys` was added in Ruby 2.5.)
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
# # bad
|
20
|
+
# {a: 1, b: 2}.each_with_object({}) { |(k, v), h| h[foo(k)] = v }
|
21
|
+
# {a: 1, b: 2}.map { |k, v| [k.to_s, v] }
|
22
|
+
#
|
23
|
+
# # good
|
24
|
+
# {a: 1, b: 2}.transform_keys { |k| foo(k) }
|
25
|
+
# {a: 1, b: 2}.transform_keys { |k| k.to_s }
|
26
|
+
class HashTransformKeys < Cop
|
27
|
+
extend TargetRubyVersion
|
28
|
+
include HashTransformMethod
|
29
|
+
|
30
|
+
minimum_target_ruby_version 2.5
|
31
|
+
|
32
|
+
def_node_matcher :on_bad_each_with_object, <<~PATTERN
|
33
|
+
(block
|
34
|
+
({send csend}
|
35
|
+
!{(send _ :each_with_index) (array ...)}
|
36
|
+
:each_with_object (hash))
|
37
|
+
(args
|
38
|
+
(mlhs
|
39
|
+
(arg $_)
|
40
|
+
(arg _val))
|
41
|
+
(arg _memo))
|
42
|
+
({send csend} (lvar _memo) :[]= $_ $(lvar _val)))
|
43
|
+
PATTERN
|
44
|
+
|
45
|
+
def_node_matcher :on_bad_hash_brackets_map, <<~PATTERN
|
46
|
+
(send
|
47
|
+
(const _ :Hash)
|
48
|
+
:[]
|
49
|
+
(block
|
50
|
+
({send csend} !(send _ :each_with_index) {:map :collect})
|
51
|
+
(args
|
52
|
+
(arg $_)
|
53
|
+
(arg _val))
|
54
|
+
(array $_ $(lvar _val))))
|
55
|
+
PATTERN
|
56
|
+
|
57
|
+
def_node_matcher :on_bad_map_to_h, <<~PATTERN
|
58
|
+
({send csend}
|
59
|
+
(block
|
60
|
+
({send csend}
|
61
|
+
!{(send _ :each_with_index) (array ...)}
|
62
|
+
{:map :collect})
|
63
|
+
(args
|
64
|
+
(arg $_)
|
65
|
+
(arg _val))
|
66
|
+
(array $_ $(lvar _val)))
|
67
|
+
:to_h)
|
68
|
+
PATTERN
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def extract_captures(match)
|
73
|
+
key_argname, key_body_expr, val_body_expr = *match
|
74
|
+
Captures.new(key_argname, key_body_expr, val_body_expr)
|
75
|
+
end
|
76
|
+
|
77
|
+
def new_method_name
|
78
|
+
'transform_keys'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|