rubocop 1.28.2 → 1.29.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/README.md +3 -3
- data/config/default.yml +36 -21
- data/lib/rubocop/cop/badge.rb +1 -1
- data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -1
- data/lib/rubocop/cop/bundler/gem_comment.rb +1 -1
- data/lib/rubocop/cop/gemspec/dependency_version.rb +156 -0
- data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +3 -6
- data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/method_name_end_with.rb +80 -0
- data/lib/rubocop/cop/internal_affairs.rb +1 -0
- data/lib/rubocop/cop/layout/comment_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_block_parameters.rb +1 -1
- data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +1 -1
- data/lib/rubocop/cop/layout/trailing_empty_lines.rb +1 -1
- data/lib/rubocop/cop/lint/ambiguous_range.rb +2 -2
- data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
- data/lib/rubocop/cop/lint/loop.rb +1 -1
- data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +5 -5
- data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +1 -1
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -1
- data/lib/rubocop/cop/lint/raise_exception.rb +1 -1
- data/lib/rubocop/cop/lint/return_in_void_context.rb +5 -17
- data/lib/rubocop/cop/lint/useless_times.rb +1 -1
- data/lib/rubocop/cop/mixin/duplication.rb +1 -1
- data/lib/rubocop/cop/mixin/preferred_delimiters.rb +2 -2
- data/lib/rubocop/cop/mixin/statement_modifier.rb +1 -1
- data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
- data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
- data/lib/rubocop/cop/naming/file_name.rb +1 -1
- data/lib/rubocop/cop/naming/inclusive_language.rb +3 -2
- data/lib/rubocop/cop/naming/predicate_name.rb +2 -2
- data/lib/rubocop/cop/naming/variable_name.rb +9 -0
- data/lib/rubocop/cop/naming/variable_number.rb +10 -0
- data/lib/rubocop/cop/security/yaml_load.rb +1 -1
- data/lib/rubocop/cop/style/alias.rb +3 -3
- data/lib/rubocop/cop/style/and_or.rb +1 -1
- data/lib/rubocop/cop/style/bisected_attr_accessor/macro.rb +1 -1
- data/lib/rubocop/cop/style/case_like_if.rb +1 -1
- data/lib/rubocop/cop/style/character_literal.rb +1 -1
- data/lib/rubocop/cop/style/collection_compact.rb +3 -3
- data/lib/rubocop/cop/style/date_time.rb +1 -1
- data/lib/rubocop/cop/style/double_negation.rb +28 -2
- data/lib/rubocop/cop/style/empty_case_condition.rb +1 -1
- data/lib/rubocop/cop/style/empty_literal.rb +1 -1
- data/lib/rubocop/cop/style/env_home.rb +56 -0
- data/lib/rubocop/cop/style/fetch_env_var.rb +240 -11
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +2 -2
- data/lib/rubocop/cop/style/mixin_grouping.rb +1 -1
- data/lib/rubocop/cop/style/multiline_ternary_operator.rb +5 -1
- data/lib/rubocop/cop/style/next.rb +1 -1
- data/lib/rubocop/cop/style/optional_arguments.rb +1 -1
- data/lib/rubocop/cop/style/optional_boolean_parameter.rb +1 -1
- data/lib/rubocop/cop/style/quoted_symbols.rb +1 -1
- data/lib/rubocop/cop/style/raise_args.rb +4 -1
- data/lib/rubocop/cop/style/redundant_condition.rb +108 -11
- data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +1 -1
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +1 -1
- data/lib/rubocop/cop/style/redundant_self_assignment.rb +1 -2
- data/lib/rubocop/cop/style/safe_navigation.rb +1 -1
- data/lib/rubocop/cop/style/string_chars.rb +1 -1
- data/lib/rubocop/cop/style/trivial_accessors.rb +7 -8
- data/lib/rubocop/cops_documentation_generator.rb +1 -1
- data/lib/rubocop/formatter/formatter_set.rb +1 -0
- data/lib/rubocop/formatter/html_formatter.rb +2 -9
- data/lib/rubocop/formatter/markdown_formatter.rb +76 -0
- data/lib/rubocop/magic_comment.rb +4 -3
- data/lib/rubocop/options.rb +4 -3
- data/lib/rubocop/result_cache.rb +1 -1
- data/lib/rubocop/rspec/cop_helper.rb +1 -1
- data/lib/rubocop/rspec/parallel_formatter.rb +1 -1
- data/lib/rubocop/rspec/shared_contexts.rb +1 -1
- data/lib/rubocop/runner.rb +1 -1
- data/lib/rubocop/string_interpreter.rb +4 -4
- data/lib/rubocop/target_ruby.rb +7 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +3 -1
- metadata +16 -7
- data/lib/rubocop/cop/lint/useless_else_without_rescue.rb +0 -45
@@ -9,17 +9,32 @@ module RuboCop
|
|
9
9
|
# On the other hand, `ENV.fetch` raises KeyError or returns the explicitly
|
10
10
|
# specified default value.
|
11
11
|
#
|
12
|
+
# When an `ENV[]` is the LHS of `||`, the autocorrect makes the RHS
|
13
|
+
# the default value of `ENV.fetch`.
|
14
|
+
#
|
12
15
|
# @example
|
13
16
|
# # bad
|
14
17
|
# ENV['X']
|
15
|
-
# ENV['X'] ||
|
18
|
+
# ENV['X'] || 'string literal'
|
19
|
+
# ENV['X'] || some_method
|
16
20
|
# x = ENV['X']
|
17
21
|
#
|
22
|
+
# ENV['X'] || y.map do |a|
|
23
|
+
# puts a * 2
|
24
|
+
# end
|
25
|
+
#
|
18
26
|
# # good
|
19
27
|
# ENV.fetch('X')
|
20
|
-
# ENV.fetch('X',
|
28
|
+
# ENV.fetch('X', 'string literal')
|
29
|
+
# ENV.fetch('X') { some_method }
|
21
30
|
# x = ENV.fetch('X')
|
22
31
|
#
|
32
|
+
# ENV.fetch('X') do
|
33
|
+
# y.map do |a|
|
34
|
+
# puts a * 2
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
#
|
23
38
|
# # also good
|
24
39
|
# !ENV['X']
|
25
40
|
# ENV['X'].some_method # (e.g. `.nil?`)
|
@@ -27,41 +42,84 @@ module RuboCop
|
|
27
42
|
class FetchEnvVar < Base
|
28
43
|
extend AutoCorrector
|
29
44
|
|
30
|
-
|
45
|
+
# rubocop:disable Layout/LineLength
|
46
|
+
MSG_DEFAULT_NIL = 'Use `ENV.fetch(%<key>s)` or `ENV.fetch(%<key>s, nil)` instead of `ENV[%<key>s]`.'
|
47
|
+
MSG_DEFAULT_RHS_SECOND_ARG_OF_FETCH = 'Use `ENV.fetch(%<key>s, %<default>s)` instead of `ENV[%<key>s] || %<default>s`.'
|
48
|
+
MSG_DEFAULT_RHS_SINGLE_LINE_BLOCK = 'Use `ENV.fetch(%<key>s) { %<default>s }` instead of `ENV[%<key>s] || %<default>s`.'
|
49
|
+
MSG_DEFAULT_RHS_MULTILINE_BLOCK = 'Use `ENV.fetch(%<key>s)` with a block containing `%<default>s ...`'
|
50
|
+
# rubocop:enable Layout/LineLength
|
31
51
|
|
32
52
|
# @!method env_with_bracket?(node)
|
33
53
|
def_node_matcher :env_with_bracket?, <<~PATTERN
|
34
54
|
(send (const nil? :ENV) :[] $_)
|
35
55
|
PATTERN
|
36
56
|
|
57
|
+
# @!method operand_of_or?(node)
|
58
|
+
def_node_matcher :operand_of_or?, <<~PATTERN
|
59
|
+
(^or ...)
|
60
|
+
PATTERN
|
61
|
+
|
62
|
+
# @!method block_control?(node)
|
63
|
+
def_node_matcher :block_control?, <<~PATTERN
|
64
|
+
({next | break | retry | redo})
|
65
|
+
PATTERN
|
66
|
+
|
67
|
+
# @!method env_with_bracket_in_descendants?(node)
|
68
|
+
def_node_matcher :env_with_bracket_in_descendants?, <<~PATTERN
|
69
|
+
`(send (const nil? :ENV) :[] $_)
|
70
|
+
PATTERN
|
71
|
+
|
37
72
|
def on_send(node)
|
38
73
|
env_with_bracket?(node) do |expression|
|
39
|
-
break
|
40
|
-
|
74
|
+
break unless offensive?(node)
|
75
|
+
|
76
|
+
if operand_of_or?(node)
|
77
|
+
target_node = lookahead_target_node(node)
|
78
|
+
target_expr = env_with_bracket?(target_node)
|
41
79
|
|
42
|
-
|
43
|
-
|
80
|
+
if default_to_rhs?(target_node)
|
81
|
+
default_rhs(target_node, target_expr)
|
82
|
+
else
|
83
|
+
default_nil(target_node, target_expr)
|
84
|
+
end
|
85
|
+
else
|
86
|
+
default_nil(node, expression)
|
44
87
|
end
|
45
88
|
end
|
46
89
|
end
|
47
90
|
|
48
91
|
private
|
49
92
|
|
50
|
-
def allowed_var?(
|
51
|
-
|
93
|
+
def allowed_var?(node)
|
94
|
+
env_key_node = node.children.last
|
95
|
+
env_key_node.str_type? && cop_config['AllowedVars'].include?(env_key_node.value)
|
52
96
|
end
|
53
97
|
|
54
98
|
def used_as_flag?(node)
|
55
99
|
return false if node.root?
|
56
100
|
|
57
|
-
|
101
|
+
if_node = node.ancestors.find(&:if_type?)
|
102
|
+
return true if if_node&.condition == node
|
103
|
+
|
104
|
+
node.parent.send_type? && (node.parent.prefix_bang? || node.parent.comparison_method?)
|
105
|
+
end
|
106
|
+
|
107
|
+
def offensive?(node)
|
108
|
+
!(allowed_var?(node) || allowable_use?(node))
|
109
|
+
end
|
110
|
+
|
111
|
+
def default_to_rhs?(node)
|
112
|
+
operand_of_or?(node) && !right_end_of_or_chains?(node) && rhs_can_be_default_value?(node)
|
58
113
|
end
|
59
114
|
|
60
115
|
# Check if the node is a receiver and receives a message with dot syntax.
|
61
116
|
def message_chained_with_dot?(node)
|
62
117
|
return false if node.root?
|
63
118
|
|
64
|
-
|
119
|
+
parent = node.parent
|
120
|
+
return false if !parent.call_type? || parent.children.first != node
|
121
|
+
|
122
|
+
parent.dot? || parent.safe_navigation?
|
65
123
|
end
|
66
124
|
|
67
125
|
# The following are allowed cases:
|
@@ -84,6 +142,177 @@ module RuboCop
|
|
84
142
|
lhs, _method, _rhs = *parent
|
85
143
|
node == lhs
|
86
144
|
end
|
145
|
+
|
146
|
+
def left_end_of_or_chains?(node)
|
147
|
+
return false unless operand_of_or?(node)
|
148
|
+
|
149
|
+
node.parent.lhs == node
|
150
|
+
end
|
151
|
+
|
152
|
+
def right_end_of_or_chains?(node)
|
153
|
+
!(left_end_of_or_chains?(node) || node.parent&.parent&.or_type?)
|
154
|
+
end
|
155
|
+
|
156
|
+
# Returns the node and expression of the rightmost `ENV[]` in `||` chains.
|
157
|
+
# e.g.,
|
158
|
+
# `ENV['X'] || y || z || ENV['A'] || b`
|
159
|
+
# ^^^^^^^^ Matches this one
|
160
|
+
def rightmost_offense_in_or_chains(base_node)
|
161
|
+
or_nodes = [base_node.parent]
|
162
|
+
|
163
|
+
while (grand_parent = or_nodes.last&.parent)&.or_type?
|
164
|
+
or_nodes << grand_parent
|
165
|
+
end
|
166
|
+
|
167
|
+
# Finds the rightmost `ENV[]` in `||` chains.
|
168
|
+
or_node = or_nodes.reverse.find do |n|
|
169
|
+
env_with_bracket?(n.rhs)
|
170
|
+
end
|
171
|
+
|
172
|
+
or_node ? or_node.rhs : base_node
|
173
|
+
end
|
174
|
+
|
175
|
+
def no_env_with_bracket_in_descendants?(node)
|
176
|
+
!env_with_bracket_in_descendants?(node)
|
177
|
+
end
|
178
|
+
|
179
|
+
def conterpart_rhs_of(node)
|
180
|
+
left_end_of_or_chains?(node) ? node.parent.rhs : node.parent.parent.rhs
|
181
|
+
end
|
182
|
+
|
183
|
+
# Looks ahead to the `ENV[]` that must be corrected first, avoiding a cross correction.
|
184
|
+
# ```
|
185
|
+
# ENV['X'] || y.map do |a|
|
186
|
+
# a.map do |b|
|
187
|
+
# ENV['Z'] + b
|
188
|
+
# ^^^^^^^^ This must be corrected first.
|
189
|
+
# end
|
190
|
+
# end
|
191
|
+
# ```
|
192
|
+
def lookahead_target_node(base_node)
|
193
|
+
return base_node unless operand_of_or?(base_node)
|
194
|
+
|
195
|
+
candidate_node = rightmost_offense_in_or_chains(base_node)
|
196
|
+
return candidate_node if right_end_of_or_chains?(candidate_node)
|
197
|
+
|
198
|
+
counterpart_rhs = conterpart_rhs_of(candidate_node)
|
199
|
+
return candidate_node if no_env_with_bracket_in_descendants?(counterpart_rhs)
|
200
|
+
|
201
|
+
new_base_node = counterpart_rhs.each_descendant.find do |d|
|
202
|
+
env_with_bracket?(d) && offensive?(d)
|
203
|
+
end
|
204
|
+
return candidate_node unless new_base_node
|
205
|
+
|
206
|
+
lookahead_target_node(new_base_node)
|
207
|
+
end
|
208
|
+
|
209
|
+
def rhs_can_be_default_value?(node)
|
210
|
+
!rhs_is_block_control?(node)
|
211
|
+
end
|
212
|
+
|
213
|
+
def rhs_is_block_control?(node)
|
214
|
+
block_control?(conterpart_rhs_of(node))
|
215
|
+
end
|
216
|
+
|
217
|
+
def new_code_default_nil(expression)
|
218
|
+
"ENV.fetch(#{expression.source}, nil)"
|
219
|
+
end
|
220
|
+
|
221
|
+
def new_code_default_rhs_single_line(node, expression)
|
222
|
+
parent = node.parent
|
223
|
+
if parent.rhs.basic_literal?
|
224
|
+
"ENV.fetch(#{expression.source}, #{parent.rhs.source})"
|
225
|
+
else
|
226
|
+
"ENV.fetch(#{expression.source}) { #{parent.rhs.source} }"
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def new_code_default_rhs_multiline(node, expression)
|
231
|
+
env_indent = indent(node.parent)
|
232
|
+
default = node.parent.rhs.source.split("\n").map do |line|
|
233
|
+
"#{env_indent}#{line}"
|
234
|
+
end.join("\n")
|
235
|
+
<<~NEW_CODE.chomp
|
236
|
+
ENV.fetch(#{expression.source}) do
|
237
|
+
#{configured_indentation}#{default}
|
238
|
+
#{env_indent}end
|
239
|
+
NEW_CODE
|
240
|
+
end
|
241
|
+
|
242
|
+
def new_code_default_rhs(node, expression)
|
243
|
+
if node.parent.rhs.single_line?
|
244
|
+
new_code_default_rhs_single_line(node, expression)
|
245
|
+
else
|
246
|
+
new_code_default_rhs_multiline(node, expression)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def default_rhs(node, expression)
|
251
|
+
if left_end_of_or_chains?(node)
|
252
|
+
default_rhs_in_same_or(node, expression)
|
253
|
+
else
|
254
|
+
default_rhs_in_outer_or(node, expression)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
# Adds an offense and sets `nil` to the default value of `ENV.fetch`.
|
259
|
+
# `ENV['X']` --> `ENV.fetch('X', nil)`
|
260
|
+
def default_nil(node, expression)
|
261
|
+
message = format(MSG_DEFAULT_NIL, key: expression.source)
|
262
|
+
|
263
|
+
add_offense(node, message: message) do |corrector|
|
264
|
+
corrector.replace(node, new_code_default_nil(expression))
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
# Adds an offense and makes the RHS the default value of `ENV.fetch`.
|
269
|
+
# `ENV['X'] || y` --> `ENV.fetch('X') { y }`
|
270
|
+
def default_rhs_in_same_or(node, expression)
|
271
|
+
template = message_template_for(node.parent.rhs)
|
272
|
+
message = format(template,
|
273
|
+
key: expression.source,
|
274
|
+
default: first_line_of(node.parent.rhs.source))
|
275
|
+
|
276
|
+
add_offense(node, message: message) do |corrector|
|
277
|
+
corrector.replace(node.parent, new_code_default_rhs(node, expression))
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
# Adds an offense and makes the RHS the default value of `ENV.fetch`.
|
282
|
+
# `z || ENV['X'] || y` --> `z || ENV.fetch('X') { y }`
|
283
|
+
def default_rhs_in_outer_or(node, expression)
|
284
|
+
parent = node.parent
|
285
|
+
grand_parent = parent.parent
|
286
|
+
|
287
|
+
template = message_template_for(grand_parent.rhs)
|
288
|
+
message = format(template,
|
289
|
+
key: expression.source,
|
290
|
+
default: first_line_of(grand_parent.rhs.source))
|
291
|
+
|
292
|
+
add_offense(node, message: message) do |corrector|
|
293
|
+
lhs_code = parent.lhs.source
|
294
|
+
rhs_code = new_code_default_rhs(parent, expression)
|
295
|
+
corrector.replace(grand_parent, "#{lhs_code} || #{rhs_code}")
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
def message_template_for(rhs)
|
300
|
+
if rhs.multiline?
|
301
|
+
MSG_DEFAULT_RHS_MULTILINE_BLOCK
|
302
|
+
elsif rhs.basic_literal?
|
303
|
+
MSG_DEFAULT_RHS_SECOND_ARG_OF_FETCH
|
304
|
+
else
|
305
|
+
MSG_DEFAULT_RHS_SINGLE_LINE_BLOCK
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
def configured_indentation
|
310
|
+
' ' * (config.for_cop('Layout/IndentationWidth')['Width'] || 2)
|
311
|
+
end
|
312
|
+
|
313
|
+
def first_line_of(source)
|
314
|
+
source.split("\n").first
|
315
|
+
end
|
87
316
|
end
|
88
317
|
end
|
89
318
|
end
|
@@ -13,7 +13,7 @@ module RuboCop
|
|
13
13
|
#
|
14
14
|
# @safety
|
15
15
|
# Auto-correction is unsafe because changing the order of method invocations
|
16
|
-
# may change the
|
16
|
+
# may change the behavior of the code. For example:
|
17
17
|
#
|
18
18
|
# [source,ruby]
|
19
19
|
# ----
|
@@ -27,7 +27,7 @@ module RuboCop
|
|
27
27
|
# ----
|
28
28
|
#
|
29
29
|
# In this example, `method_that_relies_on_global_state` will be moved before
|
30
|
-
# `method_that_modifies_global_state`, which changes the
|
30
|
+
# `method_that_modifies_global_state`, which changes the behavior of the program.
|
31
31
|
#
|
32
32
|
# @example
|
33
33
|
# # bad
|
@@ -119,7 +119,7 @@ module RuboCop
|
|
119
119
|
arguments = node.arguments.reverse
|
120
120
|
mixins = ["#{node.method_name} #{arguments.first.source}"]
|
121
121
|
|
122
|
-
arguments[1
|
122
|
+
arguments[1..].inject(mixins) do |replacement, arg|
|
123
123
|
replacement << "#{indent(node)}#{node.method_name} #{arg.source}"
|
124
124
|
end.join("\n")
|
125
125
|
end
|
@@ -73,7 +73,11 @@ module RuboCop
|
|
73
73
|
end
|
74
74
|
|
75
75
|
def enforce_single_line_ternary_operator?(node)
|
76
|
-
SINGLE_LINE_TYPES.include?(node.parent.type)
|
76
|
+
SINGLE_LINE_TYPES.include?(node.parent.type) && !use_assignment_method?(node.parent)
|
77
|
+
end
|
78
|
+
|
79
|
+
def use_assignment_method?(node)
|
80
|
+
node.send_type? && node.assignment_method?
|
77
81
|
end
|
78
82
|
end
|
79
83
|
end
|
@@ -80,7 +80,7 @@ module RuboCop
|
|
80
80
|
return node.source if message_nodes.size > 1
|
81
81
|
|
82
82
|
argument = message_nodes.first.source
|
83
|
-
exception_class = exception_node.
|
83
|
+
exception_class = exception_node.receiver&.source || exception_node.source
|
84
84
|
|
85
85
|
if node.parent && requires_parens?(node.parent)
|
86
86
|
"#{node.method_name}(#{exception_class}.new(#{argument}))"
|
@@ -91,6 +91,9 @@ module RuboCop
|
|
91
91
|
|
92
92
|
def check_compact(node)
|
93
93
|
if node.arguments.size > 1
|
94
|
+
exception = node.first_argument
|
95
|
+
return if exception.send_type? && exception.first_argument&.hash_type?
|
96
|
+
|
94
97
|
add_offense(node, message: format(COMPACT_MSG, method: node.method_name)) do |corrector|
|
95
98
|
replacement = correction_exploded_to_compact(node)
|
96
99
|
|
@@ -44,9 +44,9 @@ module RuboCop
|
|
44
44
|
message = message(node)
|
45
45
|
|
46
46
|
add_offense(range_of_offense(node), message: message) do |corrector|
|
47
|
-
if node.ternary?
|
47
|
+
if node.ternary? && !branches_have_method?(node)
|
48
48
|
correct_ternary(corrector, node)
|
49
|
-
elsif
|
49
|
+
elsif redudant_condition?(node)
|
50
50
|
corrector.replace(node, node.if_branch.source)
|
51
51
|
else
|
52
52
|
corrected = make_ternary_form(node)
|
@@ -59,7 +59,7 @@ module RuboCop
|
|
59
59
|
private
|
60
60
|
|
61
61
|
def message(node)
|
62
|
-
if
|
62
|
+
if redudant_condition?(node)
|
63
63
|
REDUNDANT_CONDITION
|
64
64
|
else
|
65
65
|
MSG
|
@@ -68,18 +68,22 @@ module RuboCop
|
|
68
68
|
|
69
69
|
def range_of_offense(node)
|
70
70
|
return node.loc.expression unless node.ternary?
|
71
|
+
return node.loc.expression if node.ternary? && branches_have_method?(node)
|
71
72
|
|
72
73
|
range_between(node.loc.question.begin_pos, node.loc.colon.end_pos)
|
73
74
|
end
|
74
75
|
|
75
76
|
def offense?(node)
|
76
|
-
|
77
|
+
_condition, _if_branch, else_branch = *node
|
77
78
|
|
78
79
|
return false if use_if_branch?(else_branch) || use_hash_key_assignment?(else_branch)
|
79
80
|
|
80
|
-
|
81
|
-
node.ternary? || !else_branch.instance_of?(AST::Node) || else_branch.single_line?
|
82
|
-
|
81
|
+
synonymous_condition_and_branch?(node) && !node.elsif? &&
|
82
|
+
(node.ternary? || !else_branch.instance_of?(AST::Node) || else_branch.single_line?)
|
83
|
+
end
|
84
|
+
|
85
|
+
def redudant_condition?(node)
|
86
|
+
node.modifier_form? || !node.else_branch
|
83
87
|
end
|
84
88
|
|
85
89
|
def use_if_branch?(else_branch)
|
@@ -90,19 +94,108 @@ module RuboCop
|
|
90
94
|
else_branch&.send_type? && else_branch&.method?(:[]=)
|
91
95
|
end
|
92
96
|
|
97
|
+
def use_hash_key_access?(node)
|
98
|
+
node.send_type? && node.method?(:[])
|
99
|
+
end
|
100
|
+
|
101
|
+
def synonymous_condition_and_branch?(node)
|
102
|
+
condition, if_branch, _else_branch = *node
|
103
|
+
# e.g.
|
104
|
+
# if var
|
105
|
+
# var
|
106
|
+
# else
|
107
|
+
# 'foo'
|
108
|
+
# end
|
109
|
+
return true if condition == if_branch
|
110
|
+
|
111
|
+
# e.g.
|
112
|
+
# if foo
|
113
|
+
# @value = foo
|
114
|
+
# else
|
115
|
+
# @value = another_value?
|
116
|
+
# end
|
117
|
+
return true if branches_have_assignment?(node) && condition == if_branch.expression
|
118
|
+
|
119
|
+
# e.g.
|
120
|
+
# if foo
|
121
|
+
# test.value = foo
|
122
|
+
# else
|
123
|
+
# test.value = another_value?
|
124
|
+
# end
|
125
|
+
branches_have_method?(node) && condition == if_branch.first_argument &&
|
126
|
+
!use_hash_key_access?(if_branch)
|
127
|
+
end
|
128
|
+
|
129
|
+
def branches_have_assignment?(node)
|
130
|
+
_condition, if_branch, else_branch = *node
|
131
|
+
|
132
|
+
return false unless if_branch && else_branch
|
133
|
+
|
134
|
+
asgn_type?(if_branch) && (if_branch_variable_name = if_branch.name) &&
|
135
|
+
asgn_type?(else_branch) && (else_branch_variable_name = else_branch.name) &&
|
136
|
+
if_branch_variable_name == else_branch_variable_name
|
137
|
+
end
|
138
|
+
|
139
|
+
def asgn_type?(node)
|
140
|
+
node.lvasgn_type? || node.ivasgn_type? || node.cvasgn_type? || node.gvasgn_type?
|
141
|
+
end
|
142
|
+
|
143
|
+
def branches_have_method?(node)
|
144
|
+
_condition, if_branch, else_branch = *node
|
145
|
+
|
146
|
+
return false unless if_branch && else_branch
|
147
|
+
|
148
|
+
if_branch.send_type? && if_branch.arguments.count == 1 &&
|
149
|
+
else_branch.send_type? && else_branch.arguments.count == 1 &&
|
150
|
+
if_branch.method?(else_branch.method_name)
|
151
|
+
end
|
152
|
+
|
153
|
+
def if_source(if_branch)
|
154
|
+
if branches_have_method?(if_branch.parent) && if_branch.parenthesized?
|
155
|
+
if_branch.source.delete_suffix(')')
|
156
|
+
else
|
157
|
+
if_branch.source
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
93
161
|
def else_source(else_branch)
|
94
|
-
if
|
162
|
+
if branches_have_method?(else_branch.parent)
|
163
|
+
else_source_if_has_method(else_branch)
|
164
|
+
elsif require_parentheses?(else_branch)
|
95
165
|
"(#{else_branch.source})"
|
96
166
|
elsif without_argument_parentheses_method?(else_branch)
|
97
167
|
"#{else_branch.method_name}(#{else_branch.arguments.map(&:source).join(', ')})"
|
168
|
+
elsif branches_have_assignment?(else_branch.parent)
|
169
|
+
else_source_if_has_assignment(else_branch)
|
98
170
|
else
|
99
171
|
else_branch.source
|
100
172
|
end
|
101
173
|
end
|
102
174
|
|
175
|
+
def else_source_if_has_method(else_branch)
|
176
|
+
if require_parentheses?(else_branch.first_argument)
|
177
|
+
"(#{else_branch.first_argument.source})"
|
178
|
+
elsif require_braces?(else_branch.first_argument)
|
179
|
+
"{ #{else_branch.first_argument.source} }"
|
180
|
+
else
|
181
|
+
else_branch.first_argument.source
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def else_source_if_has_assignment(else_branch)
|
186
|
+
if require_parentheses?(else_branch.expression)
|
187
|
+
"(#{else_branch.expression.source})"
|
188
|
+
elsif require_braces?(else_branch.expression)
|
189
|
+
"{ #{else_branch.expression.source} }"
|
190
|
+
else
|
191
|
+
else_branch.expression.source
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
103
195
|
def make_ternary_form(node)
|
104
196
|
_condition, if_branch, else_branch = *node
|
105
|
-
ternary_form = [if_branch
|
197
|
+
ternary_form = [if_source(if_branch), else_source(else_branch)].join(' || ')
|
198
|
+
ternary_form += ')' if branches_have_method?(node) && if_branch.parenthesized?
|
106
199
|
|
107
200
|
if node.parent&.send_type?
|
108
201
|
"(#{ternary_form})"
|
@@ -126,9 +219,13 @@ module RuboCop
|
|
126
219
|
(node.respond_to?(:semantic_operator?) && node.semantic_operator?)
|
127
220
|
end
|
128
221
|
|
222
|
+
def require_braces?(node)
|
223
|
+
node.hash_type? && !node.braces?
|
224
|
+
end
|
225
|
+
|
129
226
|
def without_argument_parentheses_method?(node)
|
130
|
-
node.send_type? &&
|
131
|
-
!node.
|
227
|
+
node.send_type? && !node.arguments.empty? &&
|
228
|
+
!node.parenthesized? && !node.operator_method? && !node.assignment_method?
|
132
229
|
end
|
133
230
|
end
|
134
231
|
end
|
@@ -99,7 +99,7 @@ module RuboCop
|
|
99
99
|
end
|
100
100
|
|
101
101
|
def backslash_b?(elem)
|
102
|
-
# \b's
|
102
|
+
# \b's behavior is different inside and outside of a character class, matching word
|
103
103
|
# boundaries outside but backspace (0x08) when inside.
|
104
104
|
elem == '\b'
|
105
105
|
end
|
@@ -59,7 +59,7 @@ module RuboCop
|
|
59
59
|
def allowed_escape?(node, char, within_character_class)
|
60
60
|
# Strictly speaking a few single-letter metachars are currently
|
61
61
|
# unnecessary to "escape", e.g. i, E, F, but enumerating them is
|
62
|
-
# rather difficult, and their
|
62
|
+
# rather difficult, and their behavior could change over time with
|
63
63
|
# different versions of Ruby so that e.g. /\i/ != /i/
|
64
64
|
return true if /[[:alnum:]]/.match?(char)
|
65
65
|
return true if ALLOWED_ALWAYS_ESCAPES.include?(char) || delimiter?(node, char)
|
@@ -67,8 +67,7 @@ module RuboCop
|
|
67
67
|
alias on_gvasgn on_lvasgn
|
68
68
|
|
69
69
|
def on_send(node)
|
70
|
-
|
71
|
-
return unless node.method_name.to_s.end_with?('=')
|
70
|
+
return unless node.assignment_method?
|
72
71
|
return unless redundant_assignment?(node)
|
73
72
|
|
74
73
|
message = format(MSG, method_name: node.first_argument.method_name)
|
@@ -8,7 +8,7 @@ module RuboCop
|
|
8
8
|
# @safety
|
9
9
|
# This cop is unsafe because it cannot be guaranteed that the receiver
|
10
10
|
# is actually a string. If another class has a `split` method with
|
11
|
-
# different
|
11
|
+
# different behavior, it would be registered as a false positive.
|
12
12
|
#
|
13
13
|
# @example
|
14
14
|
# # bad
|
@@ -167,8 +167,8 @@ module RuboCop
|
|
167
167
|
allowed_methods.map(&:to_sym) + [:initialize]
|
168
168
|
end
|
169
169
|
|
170
|
-
def dsl_writer?(
|
171
|
-
!
|
170
|
+
def dsl_writer?(node)
|
171
|
+
!node.assignment_method?
|
172
172
|
end
|
173
173
|
|
174
174
|
def trivial_reader?(node)
|
@@ -180,8 +180,7 @@ module RuboCop
|
|
180
180
|
end
|
181
181
|
|
182
182
|
def trivial_writer?(node)
|
183
|
-
looks_like_trivial_writer?(node) &&
|
184
|
-
!allowed_method_name?(node) && !allowed_writer?(node.method_name)
|
183
|
+
looks_like_trivial_writer?(node) && !allowed_method_name?(node) && !allowed_writer?(node)
|
185
184
|
end
|
186
185
|
|
187
186
|
# @!method looks_like_trivial_writer?(node)
|
@@ -195,8 +194,8 @@ module RuboCop
|
|
195
194
|
(exact_name_match? && !names_match?(node))
|
196
195
|
end
|
197
196
|
|
198
|
-
def allowed_writer?(
|
199
|
-
allow_dsl_writers? && dsl_writer?(
|
197
|
+
def allowed_writer?(node)
|
198
|
+
allow_dsl_writers? && dsl_writer?(node)
|
200
199
|
end
|
201
200
|
|
202
201
|
def allowed_reader?(node)
|
@@ -206,11 +205,11 @@ module RuboCop
|
|
206
205
|
def names_match?(node)
|
207
206
|
ivar_name, = *node.body
|
208
207
|
|
209
|
-
node.method_name.to_s.sub(/[=?]$/, '') == ivar_name[1
|
208
|
+
node.method_name.to_s.sub(/[=?]$/, '') == ivar_name[1..]
|
210
209
|
end
|
211
210
|
|
212
211
|
def trivial_accessor_kind(node)
|
213
|
-
if trivial_writer?(node) && !dsl_writer?(node
|
212
|
+
if trivial_writer?(node) && !dsl_writer?(node)
|
214
213
|
'writer'
|
215
214
|
elsif trivial_reader?(node)
|
216
215
|
'reader'
|