rubocop 1.57.1 → 1.58.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/README.md +2 -2
- data/config/default.yml +37 -2
- data/lib/rubocop/cli/command/auto_generate_config.rb +10 -5
- data/lib/rubocop/config_obsoletion.rb +11 -8
- data/lib/rubocop/cop/bundler/gem_comment.rb +2 -2
- data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +2 -2
- data/lib/rubocop/cop/internal_affairs/method_name_equal.rb +19 -20
- data/lib/rubocop/cop/internal_affairs/node_first_or_last_argument.rb +53 -0
- data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +2 -2
- data/lib/rubocop/cop/internal_affairs.rb +1 -0
- data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/end_alignment.rb +7 -1
- data/lib/rubocop/cop/layout/extra_spacing.rb +4 -10
- data/lib/rubocop/cop/layout/first_array_element_indentation.rb +6 -6
- data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +4 -4
- data/lib/rubocop/cop/layout/single_line_block_chain.rb +4 -0
- data/lib/rubocop/cop/layout/space_around_operators.rb +50 -20
- data/lib/rubocop/cop/lint/assignment_in_condition.rb +4 -4
- data/lib/rubocop/cop/lint/debugger.rb +2 -1
- data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -1
- data/lib/rubocop/cop/lint/erb_new_arguments.rb +3 -3
- data/lib/rubocop/cop/lint/float_comparison.rb +10 -0
- data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +64 -0
- data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +3 -5
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +43 -0
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +3 -4
- data/lib/rubocop/cop/lint/self_assignment.rb +37 -0
- data/lib/rubocop/cop/lint/symbol_conversion.rb +7 -2
- data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -1
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
- data/lib/rubocop/cop/lint/void.rb +43 -12
- data/lib/rubocop/cop/metrics/class_length.rb +6 -1
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
- data/lib/rubocop/cop/mixin/comments_help.rb +16 -12
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +14 -11
- data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +1 -1
- data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
- data/lib/rubocop/cop/naming/block_forwarding.rb +2 -2
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +2 -2
- data/lib/rubocop/cop/style/accessor_grouping.rb +1 -1
- data/lib/rubocop/cop/style/arguments_forwarding.rb +68 -6
- data/lib/rubocop/cop/style/array_first_last.rb +64 -0
- data/lib/rubocop/cop/style/auto_resource_cleanup.rb +21 -14
- data/lib/rubocop/cop/style/bisected_attr_accessor.rb +2 -2
- data/lib/rubocop/cop/style/case_like_if.rb +4 -4
- data/lib/rubocop/cop/style/combinable_loops.rb +2 -7
- data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
- data/lib/rubocop/cop/style/empty_literal.rb +1 -1
- data/lib/rubocop/cop/style/eval_with_location.rb +3 -3
- data/lib/rubocop/cop/style/explicit_block_argument.rb +2 -2
- data/lib/rubocop/cop/style/hash_each_methods.rb +58 -10
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +9 -1
- data/lib/rubocop/cop/style/inverse_methods.rb +6 -5
- data/lib/rubocop/cop/style/map_to_hash.rb +9 -4
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/method_def_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/missing_respond_to_missing.rb +2 -2
- data/lib/rubocop/cop/style/redundant_argument.rb +2 -2
- data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +17 -10
- data/lib/rubocop/cop/style/redundant_filter_chain.rb +4 -3
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +2 -0
- data/lib/rubocop/cop/style/redundant_parentheses.rb +28 -12
- data/lib/rubocop/cop/style/redundant_return.rb +1 -1
- data/lib/rubocop/cop/style/redundant_sort.rb +1 -1
- data/lib/rubocop/cop/style/redundant_string_escape.rb +1 -1
- data/lib/rubocop/cop/style/select_by_regexp.rb +1 -1
- data/lib/rubocop/cop/style/self_assignment.rb +1 -1
- data/lib/rubocop/cop/style/semicolon.rb +8 -0
- data/lib/rubocop/cop/style/single_argument_dig.rb +2 -1
- data/lib/rubocop/cop/style/single_line_do_end_block.rb +3 -1
- data/lib/rubocop/cop/style/slicing_with_range.rb +1 -1
- data/lib/rubocop/cop/style/super_with_args_parentheses.rb +35 -0
- data/lib/rubocop/formatter/html_formatter.rb +5 -4
- data/lib/rubocop/result_cache.rb +0 -1
- data/lib/rubocop/runner.rb +1 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +3 -0
- metadata +13 -23
@@ -6,6 +6,16 @@ module RuboCop
|
|
6
6
|
# Checks for operators, variables, literals, lambda, proc and nonmutating
|
7
7
|
# methods used in void context.
|
8
8
|
#
|
9
|
+
# `each` blocks are allowed to prevent false positives.
|
10
|
+
# For example, the expression inside the `each` block below.
|
11
|
+
# It's not void, especially when the receiver is an `Enumerator`:
|
12
|
+
#
|
13
|
+
# [source,ruby]
|
14
|
+
# ----
|
15
|
+
# enumerator = [1, 2, 3].filter
|
16
|
+
# enumerator.each { |item| item >= 2 } #=> [2, 3]
|
17
|
+
# ----
|
18
|
+
#
|
9
19
|
# @example CheckForMethodsWithNoSideEffects: false (default)
|
10
20
|
# # bad
|
11
21
|
# def some_method
|
@@ -47,6 +57,7 @@ module RuboCop
|
|
47
57
|
|
48
58
|
OP_MSG = 'Operator `%<op>s` used in void context.'
|
49
59
|
VAR_MSG = 'Variable `%<var>s` used in void context.'
|
60
|
+
CONST_MSG = 'Constant `%<var>s` used in void context.'
|
50
61
|
LIT_MSG = 'Literal `%<lit>s` used in void context.'
|
51
62
|
SELF_MSG = '`self` used in void context.'
|
52
63
|
EXPRESSION_MSG = '`%<expression>s` used in void context.'
|
@@ -72,6 +83,7 @@ module RuboCop
|
|
72
83
|
return unless node.body && !node.body.begin_type?
|
73
84
|
return unless in_void_context?(node.body)
|
74
85
|
|
86
|
+
check_void_op(node.body) { node.method?(:each) }
|
75
87
|
check_expression(node.body)
|
76
88
|
end
|
77
89
|
|
@@ -87,11 +99,13 @@ module RuboCop
|
|
87
99
|
def check_begin(node)
|
88
100
|
expressions = *node
|
89
101
|
expressions.pop unless in_void_context?(node)
|
90
|
-
expressions.each
|
102
|
+
expressions.each do |expr|
|
103
|
+
check_void_op(expr)
|
104
|
+
check_expression(expr)
|
105
|
+
end
|
91
106
|
end
|
92
107
|
|
93
108
|
def check_expression(expr)
|
94
|
-
check_void_op(expr)
|
95
109
|
check_literal(expr)
|
96
110
|
check_var(expr)
|
97
111
|
check_self(expr)
|
@@ -101,8 +115,9 @@ module RuboCop
|
|
101
115
|
check_nonmutating(expr)
|
102
116
|
end
|
103
117
|
|
104
|
-
def check_void_op(node)
|
118
|
+
def check_void_op(node, &block)
|
105
119
|
return unless node.send_type? && OPERATORS.include?(node.method_name)
|
120
|
+
return if block && yield(node)
|
106
121
|
|
107
122
|
add_offense(node.loc.selector,
|
108
123
|
message: format(OP_MSG, op: node.method_name)) do |corrector|
|
@@ -113,20 +128,23 @@ module RuboCop
|
|
113
128
|
def check_var(node)
|
114
129
|
return unless node.variable? || node.const_type?
|
115
130
|
|
116
|
-
if node.const_type?
|
117
|
-
|
118
|
-
|
119
|
-
|
131
|
+
if node.const_type?
|
132
|
+
template = node.special_keyword? ? VAR_MSG : CONST_MSG
|
133
|
+
|
134
|
+
offense_range = node
|
135
|
+
message = format(template, var: node.source)
|
120
136
|
else
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
137
|
+
offense_range = node.loc.name
|
138
|
+
message = format(VAR_MSG, var: node.loc.name.source)
|
139
|
+
end
|
140
|
+
|
141
|
+
add_offense(offense_range, message: message) do |corrector|
|
142
|
+
autocorrect_void_expression(corrector, node)
|
125
143
|
end
|
126
144
|
end
|
127
145
|
|
128
146
|
def check_literal(node)
|
129
|
-
return if !node
|
147
|
+
return if !entirely_literal?(node) || node.xstr_type? || node.range_type?
|
130
148
|
|
131
149
|
add_offense(node, message: format(LIT_MSG, lit: node.source)) do |corrector|
|
132
150
|
autocorrect_void_expression(corrector, node)
|
@@ -199,6 +217,19 @@ module RuboCop
|
|
199
217
|
end
|
200
218
|
corrector.replace(send_node.loc.selector, suggestion)
|
201
219
|
end
|
220
|
+
|
221
|
+
def entirely_literal?(node)
|
222
|
+
case node.type
|
223
|
+
when :array
|
224
|
+
node.each_value.all? { |value| entirely_literal?(value) }
|
225
|
+
when :hash
|
226
|
+
return false unless node.each_key.all? { |key| entirely_literal?(key) }
|
227
|
+
|
228
|
+
node.each_value.all? { |value| entirely_literal?(value) }
|
229
|
+
else
|
230
|
+
node.literal?
|
231
|
+
end
|
232
|
+
end
|
202
233
|
end
|
203
234
|
end
|
204
235
|
end
|
@@ -218,7 +218,7 @@ module RuboCop
|
|
218
218
|
|
219
219
|
# @api private
|
220
220
|
def already_on_multiple_lines?(node)
|
221
|
-
return node.first_line != node.
|
221
|
+
return node.first_line != node.last_argument.last_line if node.def_type?
|
222
222
|
|
223
223
|
!node.single_line?
|
224
224
|
end
|
@@ -62,25 +62,29 @@ module RuboCop
|
|
62
62
|
# Returns the end line of a node, which might be a comment and not part of the AST
|
63
63
|
# End line is considered either the line at which another node starts, or
|
64
64
|
# the line at which the parent node ends.
|
65
|
-
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
65
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
66
66
|
def find_end_line(node)
|
67
|
-
if node.if_type?
|
68
|
-
node.
|
69
|
-
|
70
|
-
node.
|
71
|
-
|
72
|
-
node.
|
67
|
+
if node.if_type?
|
68
|
+
if node.else?
|
69
|
+
node.loc.else.line
|
70
|
+
elsif node.ternary?
|
71
|
+
node.else_branch.loc.line
|
72
|
+
elsif node.elsif?
|
73
|
+
node.each_ancestor(:if).find(&:if?).loc.end.line
|
74
|
+
end
|
73
75
|
elsif node.block_type? || node.numblock_type?
|
74
76
|
node.loc.end.line
|
75
77
|
elsif (next_sibling = node.right_sibling) && next_sibling.is_a?(AST::Node)
|
76
78
|
next_sibling.loc.line
|
77
79
|
elsif (parent = node.parent)
|
78
|
-
parent.loc.respond_to?(:end) && parent.loc.end
|
79
|
-
|
80
|
-
|
81
|
-
|
80
|
+
if parent.loc.respond_to?(:end) && parent.loc.end
|
81
|
+
parent.loc.end.line
|
82
|
+
else
|
83
|
+
parent.loc.line
|
84
|
+
end
|
85
|
+
end || node.loc.end.line
|
82
86
|
end
|
83
|
-
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
87
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
84
88
|
end
|
85
89
|
end
|
86
90
|
end
|
@@ -48,18 +48,21 @@ module RuboCop
|
|
48
48
|
|
49
49
|
def register_offense(node, message, replacement) # rubocop:disable Metrics/AbcSize
|
50
50
|
add_offense(node.value, message: message) do |corrector|
|
51
|
-
if (def_node = def_node_that_require_parentheses(node))
|
52
|
-
last_argument = def_node.last_argument
|
53
|
-
if last_argument.nil? || !last_argument.hash_type?
|
54
|
-
next corrector.replace(node, replacement)
|
55
|
-
end
|
56
|
-
|
57
|
-
white_spaces = range_between(def_node.selector.end_pos,
|
58
|
-
def_node.first_argument.source_range.begin_pos)
|
59
|
-
corrector.replace(white_spaces, '(')
|
60
|
-
corrector.insert_after(last_argument, ')') if node == last_argument.pairs.last
|
61
|
-
end
|
62
51
|
corrector.replace(node, replacement)
|
52
|
+
|
53
|
+
next unless (def_node = def_node_that_require_parentheses(node))
|
54
|
+
|
55
|
+
last_argument = def_node.last_argument
|
56
|
+
if last_argument.nil? || !last_argument.hash_type?
|
57
|
+
next corrector.replace(node, replacement)
|
58
|
+
end
|
59
|
+
|
60
|
+
white_spaces = range_between(def_node.selector.end_pos,
|
61
|
+
def_node.first_argument.source_range.begin_pos)
|
62
|
+
next if node.parent.braces?
|
63
|
+
|
64
|
+
corrector.replace(white_spaces, '(')
|
65
|
+
corrector.insert_after(last_argument, ')') if node == last_argument.pairs.last
|
63
66
|
end
|
64
67
|
end
|
65
68
|
|
@@ -48,13 +48,13 @@ module RuboCop
|
|
48
48
|
MSG = 'Use %<style>s block forwarding.'
|
49
49
|
|
50
50
|
def self.autocorrect_incompatible_with
|
51
|
-
[Lint::AmbiguousOperator]
|
51
|
+
[Lint::AmbiguousOperator, Style::ArgumentsForwarding]
|
52
52
|
end
|
53
53
|
|
54
54
|
def on_def(node)
|
55
55
|
return if node.arguments.empty?
|
56
56
|
|
57
|
-
last_argument = node.
|
57
|
+
last_argument = node.last_argument
|
58
58
|
return if expected_block_forwarding_style?(node, last_argument)
|
59
59
|
|
60
60
|
register_offense(last_argument, node)
|
@@ -203,7 +203,7 @@ module RuboCop
|
|
203
203
|
|
204
204
|
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
205
205
|
def on_defined?(node)
|
206
|
-
arg = node.
|
206
|
+
arg = node.first_argument
|
207
207
|
return false unless arg.ivar_type?
|
208
208
|
|
209
209
|
method_node, method_name = find_definition(node)
|
@@ -168,12 +168,12 @@ module RuboCop
|
|
168
168
|
|
169
169
|
def find_corresponding_def_node(node)
|
170
170
|
if access_modifier_with_symbol?(node)
|
171
|
-
method_name = node.
|
171
|
+
method_name = node.first_argument.value
|
172
172
|
node.parent.each_child_node(:def).find do |child|
|
173
173
|
child.method?(method_name)
|
174
174
|
end
|
175
175
|
else
|
176
|
-
node.
|
176
|
+
node.first_argument
|
177
177
|
end
|
178
178
|
end
|
179
179
|
|
@@ -12,7 +12,16 @@ module RuboCop
|
|
12
12
|
#
|
13
13
|
# This cop also identifies places where `use_args(*args)`/`use_kwargs(**kwargs)` can be
|
14
14
|
# replaced by `use_args(*)`/`use_kwargs(**)`; if desired, this functionality can be disabled
|
15
|
-
# by setting UseAnonymousForwarding: false
|
15
|
+
# by setting `UseAnonymousForwarding: false`.
|
16
|
+
#
|
17
|
+
# And this cop has `RedundantRestArgumentNames`, `RedundantKeywordRestArgumentNames`,
|
18
|
+
# and `RedundantBlockArgumentNames` options. This configuration is a list of redundant names
|
19
|
+
# that are sufficient for anonymizing meaningless naming.
|
20
|
+
#
|
21
|
+
# Meaningless names that are commonly used can be anonymized by default:
|
22
|
+
# e.g., `*args`, `**options`, `&block`, and so on.
|
23
|
+
#
|
24
|
+
# Names not on this list are likely to be meaningful and are allowed by default.
|
16
25
|
#
|
17
26
|
# @example
|
18
27
|
# # bad
|
@@ -72,6 +81,38 @@ module RuboCop
|
|
72
81
|
# bar(**kwargs)
|
73
82
|
# end
|
74
83
|
#
|
84
|
+
# @example RedundantRestArgumentNames: ['args', 'arguments'] (default)
|
85
|
+
# # bad
|
86
|
+
# def foo(*args)
|
87
|
+
# bar(*args)
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
# # good
|
91
|
+
# def foo(*)
|
92
|
+
# bar(*)
|
93
|
+
# end
|
94
|
+
#
|
95
|
+
# @example RedundantKeywordRestArgumentNames: ['kwargs', 'options', 'opts'] (default)
|
96
|
+
# # bad
|
97
|
+
# def foo(**kwargs)
|
98
|
+
# bar(**kwargs)
|
99
|
+
# end
|
100
|
+
#
|
101
|
+
# # good
|
102
|
+
# def foo(**)
|
103
|
+
# bar(**)
|
104
|
+
# end
|
105
|
+
#
|
106
|
+
# @example RedundantBlockArgumentNames: ['blk', 'block', 'proc'] (default)
|
107
|
+
# # bad
|
108
|
+
# def foo(&block)
|
109
|
+
# bar(&block)
|
110
|
+
# end
|
111
|
+
#
|
112
|
+
# # good
|
113
|
+
# def foo(&)
|
114
|
+
# bar(&)
|
115
|
+
# end
|
75
116
|
class ArgumentsForwarding < Base
|
76
117
|
include RangeHelp
|
77
118
|
extend AutoCorrector
|
@@ -86,16 +127,19 @@ module RuboCop
|
|
86
127
|
ARGS_MSG = 'Use anonymous positional arguments forwarding (`*`).'
|
87
128
|
KWARGS_MSG = 'Use anonymous keyword arguments forwarding (`**`).'
|
88
129
|
|
130
|
+
def self.autocorrect_incompatible_with
|
131
|
+
[Naming::BlockForwarding]
|
132
|
+
end
|
133
|
+
|
89
134
|
def on_def(node)
|
90
135
|
return unless node.body
|
91
136
|
|
92
|
-
|
137
|
+
restarg, kwrestarg, blockarg = extract_forwardable_args(node.arguments)
|
138
|
+
forwardable_args = redundant_forwardable_named_args(restarg, kwrestarg, blockarg)
|
139
|
+
send_nodes = node.each_descendant(:send).to_a
|
93
140
|
|
94
141
|
send_classifications = classify_send_nodes(
|
95
|
-
node,
|
96
|
-
node.each_descendant(:send).to_a,
|
97
|
-
non_splat_or_block_pass_lvar_references(node.body),
|
98
|
-
forwardable_args
|
142
|
+
node, send_nodes, non_splat_or_block_pass_lvar_references(node.body), forwardable_args
|
99
143
|
)
|
100
144
|
|
101
145
|
return if send_classifications.empty?
|
@@ -115,6 +159,14 @@ module RuboCop
|
|
115
159
|
[args.find(&:restarg_type?), args.find(&:kwrestarg_type?), args.find(&:blockarg_type?)]
|
116
160
|
end
|
117
161
|
|
162
|
+
def redundant_forwardable_named_args(restarg, kwrestarg, blockarg)
|
163
|
+
restarg_node = redundant_named_arg(restarg, 'RedundantRestArgumentNames', '*')
|
164
|
+
kwrestarg_node = redundant_named_arg(kwrestarg, 'RedundantKeywordRestArgumentNames', '**')
|
165
|
+
blockarg_node = redundant_named_arg(blockarg, 'RedundantBlockArgumentNames', '&')
|
166
|
+
|
167
|
+
[restarg_node, kwrestarg_node, blockarg_node]
|
168
|
+
end
|
169
|
+
|
118
170
|
def only_forwards_all?(send_classifications)
|
119
171
|
send_classifications.all? { |_, c, _, _| c == :all }
|
120
172
|
end
|
@@ -188,6 +240,16 @@ module RuboCop
|
|
188
240
|
[classification, classifier.forwarded_rest_arg, classifier.forwarded_kwrest_arg]
|
189
241
|
end
|
190
242
|
|
243
|
+
def redundant_named_arg(arg, config_name, keyword)
|
244
|
+
return nil unless arg
|
245
|
+
|
246
|
+
redundant_arg_names = cop_config.fetch(config_name, []).map do |redundant_arg_name|
|
247
|
+
"#{keyword}#{redundant_arg_name}"
|
248
|
+
end << keyword
|
249
|
+
|
250
|
+
redundant_arg_names.include?(arg.source) ? arg : nil
|
251
|
+
end
|
252
|
+
|
191
253
|
def register_forward_args_offense(def_arguments_or_send, rest_arg_or_splat)
|
192
254
|
add_offense(rest_arg_or_splat, message: ARGS_MSG) do |corrector|
|
193
255
|
add_parens_if_missing(def_arguments_or_send, corrector)
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Identifies usages of `arr[0]` and `arr[-1]` and suggests to change
|
7
|
+
# them to use `arr.first` and `arr.instead`.
|
8
|
+
#
|
9
|
+
# The cop is disabled by default due to safety concerns.
|
10
|
+
#
|
11
|
+
# @safety
|
12
|
+
# This cop is unsafe because `[0]` or `[-1]` can be called on a Hash,
|
13
|
+
# which returns a value for `0` or `-1` key, but changing these to use
|
14
|
+
# `.first` or `.last` will return first/last tuple instead. Also, String
|
15
|
+
# does not implement `first`/`last` methods.
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
# # bad
|
19
|
+
# arr[0]
|
20
|
+
# arr[-1]
|
21
|
+
#
|
22
|
+
# # good
|
23
|
+
# arr.first
|
24
|
+
# arr.last
|
25
|
+
# arr[0] = 2
|
26
|
+
# arr[0][-2]
|
27
|
+
#
|
28
|
+
class ArrayFirstLast < Base
|
29
|
+
extend AutoCorrector
|
30
|
+
|
31
|
+
MSG = 'Use `%<preferred>s`.'
|
32
|
+
RESTRICT_ON_SEND = %i[[]].freeze
|
33
|
+
|
34
|
+
# rubocop:disable Metrics/AbcSize
|
35
|
+
def on_send(node)
|
36
|
+
return unless node.arguments.size == 1 && node.first_argument.int_type?
|
37
|
+
|
38
|
+
value = node.first_argument.value
|
39
|
+
return unless [0, -1].include?(value)
|
40
|
+
|
41
|
+
node = innermost_braces_node(node)
|
42
|
+
return if node.parent && brace_method?(node.parent)
|
43
|
+
|
44
|
+
preferred = (value.zero? ? 'first' : 'last')
|
45
|
+
add_offense(node.loc.selector, message: format(MSG, preferred: preferred)) do |corrector|
|
46
|
+
corrector.replace(node.loc.selector, ".#{preferred}")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
# rubocop:enable Metrics/AbcSize
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def innermost_braces_node(node)
|
54
|
+
node = node.receiver while node.receiver.send_type? && node.receiver.method?(:[])
|
55
|
+
node
|
56
|
+
end
|
57
|
+
|
58
|
+
def brace_method?(node)
|
59
|
+
node.send_type? && (node.method?(:[]) || node.method?(:[]=))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -16,31 +16,38 @@ module RuboCop
|
|
16
16
|
# File.open('file') do |f|
|
17
17
|
# # ...
|
18
18
|
# end
|
19
|
+
#
|
20
|
+
# # bad
|
21
|
+
# f = Tempfile.open('temp')
|
22
|
+
#
|
23
|
+
# # good
|
24
|
+
# Tempfile.open('temp') do |f|
|
25
|
+
# # ...
|
26
|
+
# end
|
19
27
|
class AutoResourceCleanup < Base
|
20
|
-
MSG = 'Use the block version of `%<
|
21
|
-
|
22
|
-
TARGET_METHODS = { File: :open }.freeze
|
28
|
+
MSG = 'Use the block version of `%<current>s`.'
|
29
|
+
RESTRICT_ON_SEND = %i[open].freeze
|
23
30
|
|
24
|
-
|
31
|
+
# @!method file_open_method?(node)
|
32
|
+
def_node_matcher :file_open_method?, <<~PATTERN
|
33
|
+
(send (const {nil? cbase} {:File :Tempfile}) :open ...)
|
34
|
+
PATTERN
|
25
35
|
|
26
36
|
def on_send(node)
|
27
|
-
|
28
|
-
next if node.method_name != target_method
|
37
|
+
return if !file_open_method?(node) || cleanup?(node)
|
29
38
|
|
30
|
-
|
31
|
-
next if node.receiver != target_receiver
|
39
|
+
current = node.receiver.source_range.begin.join(node.selector.end).source
|
32
40
|
|
33
|
-
|
34
|
-
|
35
|
-
add_offense(node, message: format(MSG, class: target_class, method: target_method))
|
36
|
-
end
|
41
|
+
add_offense(node, message: format(MSG, current: current))
|
37
42
|
end
|
38
43
|
|
39
44
|
private
|
40
45
|
|
41
46
|
def cleanup?(node)
|
42
|
-
|
43
|
-
|
47
|
+
return true if node.block_argument?
|
48
|
+
return false unless (parent = node.parent)
|
49
|
+
|
50
|
+
parent.block_type? || !parent.lvasgn_type?
|
44
51
|
end
|
45
52
|
end
|
46
53
|
end
|
@@ -33,7 +33,7 @@ module RuboCop
|
|
33
33
|
def on_class(class_node)
|
34
34
|
@macros_to_rewrite[class_node] = Set.new
|
35
35
|
|
36
|
-
find_macros(class_node.body).
|
36
|
+
find_macros(class_node.body).each_value do |macros|
|
37
37
|
bisected = find_bisection(macros)
|
38
38
|
next unless bisected.any?
|
39
39
|
|
@@ -74,7 +74,7 @@ module RuboCop
|
|
74
74
|
def find_macros(class_def)
|
75
75
|
# Find all the macros (`attr_reader`, `attr_writer`, etc.) in the class body
|
76
76
|
# and turn them into `Macro` objects so that they can be processed.
|
77
|
-
return
|
77
|
+
return {} if !class_def || class_def.def_type?
|
78
78
|
|
79
79
|
send_nodes =
|
80
80
|
if class_def.send_type?
|
@@ -125,7 +125,7 @@ module RuboCop
|
|
125
125
|
when :==, :eql?, :equal?
|
126
126
|
find_target_in_equality_node(node)
|
127
127
|
when :===
|
128
|
-
node.
|
128
|
+
node.first_argument
|
129
129
|
when :include?, :cover?
|
130
130
|
find_target_in_include_or_cover_node(node)
|
131
131
|
when :match, :match?, :=~
|
@@ -134,7 +134,7 @@ module RuboCop
|
|
134
134
|
end
|
135
135
|
|
136
136
|
def find_target_in_equality_node(node)
|
137
|
-
argument = node.
|
137
|
+
argument = node.first_argument
|
138
138
|
receiver = node.receiver
|
139
139
|
return unless argument && receiver
|
140
140
|
|
@@ -152,7 +152,7 @@ module RuboCop
|
|
152
152
|
end
|
153
153
|
|
154
154
|
def find_target_in_match_node(node)
|
155
|
-
argument = node.
|
155
|
+
argument = node.first_argument
|
156
156
|
receiver = node.receiver
|
157
157
|
return unless receiver
|
158
158
|
|
@@ -185,7 +185,7 @@ module RuboCop
|
|
185
185
|
def condition_from_send_node(node, target)
|
186
186
|
case node.method_name
|
187
187
|
when :is_a?
|
188
|
-
node.
|
188
|
+
node.first_argument if node.receiver == target
|
189
189
|
when :==, :eql?, :equal?
|
190
190
|
condition_from_equality_node(node, target)
|
191
191
|
when :=~, :match, :match?
|
@@ -59,8 +59,6 @@ module RuboCop
|
|
59
59
|
class CombinableLoops < Base
|
60
60
|
extend AutoCorrector
|
61
61
|
|
62
|
-
include RangeHelp
|
63
|
-
|
64
62
|
MSG = 'Combine this loop with the previous loop.'
|
65
63
|
|
66
64
|
def on_block(node)
|
@@ -105,11 +103,8 @@ module RuboCop
|
|
105
103
|
end
|
106
104
|
|
107
105
|
def combine_with_left_sibling(corrector, node)
|
108
|
-
corrector.
|
109
|
-
|
110
|
-
"#{node.left_sibling.body.source}\n#{node.body.source}"
|
111
|
-
)
|
112
|
-
corrector.remove(range_with_surrounding_space(range: node.source_range, side: :left))
|
106
|
+
corrector.remove(node.left_sibling.body.source_range.end.join(node.left_sibling.loc.end))
|
107
|
+
corrector.remove(node.source_range.begin.join(node.body.source_range.begin))
|
113
108
|
end
|
114
109
|
end
|
115
110
|
end
|
@@ -83,7 +83,7 @@ module RuboCop
|
|
83
83
|
parent = node.parent
|
84
84
|
return false unless parent && %i[send super zsuper].include?(parent.type)
|
85
85
|
|
86
|
-
node.equal?(parent.
|
86
|
+
node.equal?(parent.first_argument) && !parentheses?(node.parent)
|
87
87
|
end
|
88
88
|
|
89
89
|
def replacement_range(node)
|
@@ -84,7 +84,7 @@ module RuboCop
|
|
84
84
|
# are considered.
|
85
85
|
return if node.method?(:eval) && !valid_eval_receiver?(node.receiver)
|
86
86
|
|
87
|
-
code = node.
|
87
|
+
code = node.first_argument
|
88
88
|
return unless code && (code.str_type? || code.dstr_type?)
|
89
89
|
|
90
90
|
check_location(node, code)
|
@@ -165,7 +165,7 @@ module RuboCop
|
|
165
165
|
end
|
166
166
|
|
167
167
|
def check_line(node, code)
|
168
|
-
line_node = node.
|
168
|
+
line_node = node.last_argument
|
169
169
|
line_diff = line_difference(line_node, code)
|
170
170
|
if line_diff.zero?
|
171
171
|
add_offense_for_same_line(node, line_node)
|
@@ -227,7 +227,7 @@ module RuboCop
|
|
227
227
|
end
|
228
228
|
|
229
229
|
def missing_line(node, code)
|
230
|
-
line_diff = line_difference(node.
|
230
|
+
line_diff = line_difference(node.last_argument, code)
|
231
231
|
sign = line_diff.positive? ? :+ : :-
|
232
232
|
expected_line(sign, line_diff)
|
233
233
|
end
|
@@ -86,7 +86,7 @@ module RuboCop
|
|
86
86
|
|
87
87
|
def extract_block_name(def_node)
|
88
88
|
if def_node.block_argument?
|
89
|
-
def_node.
|
89
|
+
def_node.last_argument.name
|
90
90
|
else
|
91
91
|
'block'
|
92
92
|
end
|
@@ -127,7 +127,7 @@ module RuboCop
|
|
127
127
|
end
|
128
128
|
|
129
129
|
def insert_argument(node, corrector, block_name)
|
130
|
-
last_arg = node.
|
130
|
+
last_arg = node.last_argument
|
131
131
|
arg_range = range_with_surrounding_comma(last_arg.source_range, :right)
|
132
132
|
replacement = " &#{block_name}"
|
133
133
|
replacement = ",#{replacement}" unless arg_range.source.end_with?(',')
|