rubocop 1.87.0 → 1.88.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/config/default.yml +75 -71
- data/config/obsoletion.yml +21 -1
- data/lib/rubocop/cli/command/auto_generate_config.rb +6 -0
- data/lib/rubocop/cop/base.rb +17 -2
- data/lib/rubocop/cop/bundler/gem_comment.rb +2 -2
- data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +5 -3
- data/lib/rubocop/cop/layout/block_alignment.rb +41 -4
- data/lib/rubocop/cop/lint/ambiguous_assignment.rb +1 -11
- data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +1 -10
- data/lib/rubocop/cop/lint/circular_argument_reference.rb +1 -3
- data/lib/rubocop/cop/lint/debugger.rb +0 -1
- data/lib/rubocop/cop/lint/deprecated_constants.rb +1 -7
- data/lib/rubocop/cop/lint/empty_block.rb +3 -3
- data/lib/rubocop/cop/lint/ensure_return.rb +19 -1
- data/lib/rubocop/cop/lint/erb_new_arguments.rb +3 -1
- data/lib/rubocop/cop/lint/float_comparison.rb +1 -0
- data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +5 -1
- data/lib/rubocop/cop/lint/interpolation_check.rb +18 -3
- data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +1 -1
- data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +11 -1
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +8 -11
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +4 -4
- data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +16 -0
- data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +1 -1
- data/lib/rubocop/cop/lint/number_conversion.rb +13 -4
- data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +3 -0
- data/lib/rubocop/cop/lint/ordered_magic_comments.rb +7 -7
- data/lib/rubocop/cop/lint/raise_exception.rb +1 -1
- data/lib/rubocop/cop/lint/rand_one.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +4 -1
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +4 -1
- data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +15 -4
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +14 -7
- data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +4 -0
- data/lib/rubocop/cop/lint/redundant_type_conversion.rb +7 -0
- data/lib/rubocop/cop/lint/redundant_with_index.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_with_object.rb +5 -0
- data/lib/rubocop/cop/lint/refinement_import_methods.rb +8 -1
- data/lib/rubocop/cop/lint/regexp_as_condition.rb +9 -1
- data/lib/rubocop/cop/lint/require_parentheses.rb +13 -4
- data/lib/rubocop/cop/lint/require_range_parentheses.rb +2 -1
- data/lib/rubocop/cop/lint/require_relative_self_path.rb +5 -5
- data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +1 -0
- data/lib/rubocop/cop/lint/safe_navigation_with_empty.rb +1 -1
- data/lib/rubocop/cop/lint/script_permission.rb +5 -1
- data/lib/rubocop/cop/lint/self_assignment.rb +24 -1
- data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +1 -1
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +14 -0
- data/lib/rubocop/cop/lint/shared_mutable_default.rb +3 -1
- data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +12 -0
- data/lib/rubocop/cop/lint/symbol_conversion.rb +21 -4
- data/lib/rubocop/cop/lint/to_enum_arguments.rb +28 -1
- data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +1 -1
- data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +4 -1
- data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +3 -1
- data/lib/rubocop/cop/lint/useless_assignment.rb +10 -5
- data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +7 -3
- data/lib/rubocop/cop/lint/useless_setter_call.rb +4 -1
- data/lib/rubocop/cop/lint/useless_times.rb +22 -1
- data/lib/rubocop/cop/metrics/collection_literal_length.rb +1 -1
- data/lib/rubocop/cop/style/alias.rb +1 -1
- data/lib/rubocop/cop/style/and_or.rb +1 -1
- data/lib/rubocop/cop/style/array_first_last.rb +12 -1
- data/lib/rubocop/cop/style/array_intersect.rb +4 -0
- data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +3 -0
- data/lib/rubocop/cop/style/block_delimiters.rb +16 -2
- data/lib/rubocop/cop/style/case_equality.rb +14 -2
- data/lib/rubocop/cop/style/class_equality_comparison.rb +21 -13
- data/lib/rubocop/cop/style/class_methods_definitions.rb +11 -5
- data/lib/rubocop/cop/style/colon_method_call.rb +13 -6
- data/lib/rubocop/cop/style/combinable_loops.rb +5 -0
- data/lib/rubocop/cop/style/comparable_clamp.rb +12 -1
- data/lib/rubocop/cop/style/concat_array_literals.rb +5 -1
- data/lib/rubocop/cop/style/conditional_assignment.rb +6 -1
- data/lib/rubocop/cop/style/constant_visibility.rb +4 -1
- data/lib/rubocop/cop/style/date_time.rb +2 -2
- data/lib/rubocop/cop/style/dig_chain.rb +5 -0
- data/lib/rubocop/cop/style/fetch_env_var.rb +1 -1
- data/lib/rubocop/cop/style/file_write.rb +17 -14
- data/lib/rubocop/cop/style/hash_slice.rb +16 -0
- data/lib/rubocop/cop/style/if_unless_modifier.rb +1 -1
- data/lib/rubocop/cop/style/mutable_constant.rb +105 -11
- data/lib/rubocop/cop/style/parallel_assignment.rb +8 -1
- data/lib/rubocop/cop/style/redundant_format.rb +1 -0
- data/lib/rubocop/cop/style/semicolon.rb +16 -1
- data/lib/rubocop/cop/style/while_until_do.rb +7 -0
- data/lib/rubocop/cop/style/word_array.rb +1 -0
- data/lib/rubocop/cop/style/zero_length_predicate.rb +6 -3
- data/lib/rubocop/formatter/disabled_config_formatter.rb +14 -7
- data/lib/rubocop/server/core.rb +6 -0
- data/lib/rubocop/version.rb +1 -1
- metadata +3 -3
|
@@ -46,8 +46,7 @@ module RuboCop
|
|
|
46
46
|
|
|
47
47
|
def on_block(node)
|
|
48
48
|
return unless let_rubocop_config_new?(node)
|
|
49
|
-
|
|
50
|
-
describe = find_describe_method_node(node)
|
|
49
|
+
return unless (describe = find_describe_method_node(node))
|
|
51
50
|
|
|
52
51
|
unless (exist_config = describe.last_argument.source == ':config')
|
|
53
52
|
additional_message = ' and specify `:config` in `describe`'
|
|
@@ -65,7 +64,10 @@ module RuboCop
|
|
|
65
64
|
private
|
|
66
65
|
|
|
67
66
|
def find_describe_method_node(block_node)
|
|
68
|
-
block_node.ancestors.find
|
|
67
|
+
describe = block_node.ancestors.find do |ancestor|
|
|
68
|
+
ancestor.block_type? && ancestor.method?(:describe)
|
|
69
|
+
end
|
|
70
|
+
describe&.send_node
|
|
69
71
|
end
|
|
70
72
|
end
|
|
71
73
|
end
|
|
@@ -18,6 +18,10 @@ module RuboCop
|
|
|
18
18
|
# `either` (which is the default) : the `end` is allowed to be in either
|
|
19
19
|
# location. The autocorrect will default to `start_of_line`.
|
|
20
20
|
#
|
|
21
|
+
# When the `do` or `{` appears on a continuation line of multiline
|
|
22
|
+
# method arguments, the start of the line where the method is called
|
|
23
|
+
# is used as the alignment target instead of that continuation line.
|
|
24
|
+
#
|
|
21
25
|
# @example EnforcedStyleAlignWith: either (default)
|
|
22
26
|
# # bad
|
|
23
27
|
#
|
|
@@ -116,6 +120,7 @@ module RuboCop
|
|
|
116
120
|
end_loc = block_node.loc.end
|
|
117
121
|
return unless begins_its_line?(end_loc)
|
|
118
122
|
|
|
123
|
+
start_node = start_for_line_node(block_node) if style == :start_of_line
|
|
119
124
|
start_loc = start_node.source_range
|
|
120
125
|
return unless start_loc.column != end_loc.column || style == :start_of_block
|
|
121
126
|
|
|
@@ -196,23 +201,34 @@ module RuboCop
|
|
|
196
201
|
|
|
197
202
|
def compute_do_source_line_column(node, end_loc)
|
|
198
203
|
do_loc = node.loc.begin # Actually it's either do or {.
|
|
204
|
+
anchor_loc = do_line_anchor_loc(node, do_loc)
|
|
199
205
|
|
|
200
206
|
# We've found that "end" is not aligned with the start node (which
|
|
201
207
|
# can be a block, a variable assignment, etc). But we also allow
|
|
202
208
|
# the "end" to be aligned with the start of the line where the "do"
|
|
203
209
|
# is, which is a style some people use in multi-line chains of
|
|
204
210
|
# blocks.
|
|
205
|
-
match = /\S.*/.match(
|
|
211
|
+
match = /\S.*/.match(anchor_loc.source_line)
|
|
206
212
|
indentation_of_do_line = match.begin(0)
|
|
207
|
-
|
|
213
|
+
permitted_columns = permitted_do_line_columns(do_loc, indentation_of_do_line)
|
|
214
|
+
return if permitted_columns.include?(end_loc.column) && style != :start_of_line
|
|
208
215
|
|
|
209
216
|
{
|
|
210
217
|
source: match[0],
|
|
211
|
-
line:
|
|
218
|
+
line: anchor_loc.line,
|
|
212
219
|
column: indentation_of_do_line
|
|
213
220
|
}
|
|
214
221
|
end
|
|
215
222
|
|
|
223
|
+
# `end` aligned with an argument continuation line that holds the `do`
|
|
224
|
+
# was accepted before the anchor moved to the method dispatch line;
|
|
225
|
+
# keep accepting it so that such code does not become an offense.
|
|
226
|
+
def permitted_do_line_columns(do_loc, indentation_of_do_line)
|
|
227
|
+
columns = [indentation_of_do_line]
|
|
228
|
+
columns << (do_loc.source_line =~ /\S/) if style == :either
|
|
229
|
+
columns
|
|
230
|
+
end
|
|
231
|
+
|
|
216
232
|
def loc_to_source_line_column(loc)
|
|
217
233
|
{
|
|
218
234
|
source: loc.source.lines.to_a.first.chomp,
|
|
@@ -239,7 +255,7 @@ module RuboCop
|
|
|
239
255
|
def compute_start_col(ancestor_node, node)
|
|
240
256
|
if style == :start_of_block
|
|
241
257
|
do_loc = node.loc.begin
|
|
242
|
-
return do_loc.source_line =~ /\S/
|
|
258
|
+
return do_line_anchor_loc(node, do_loc).source_line =~ /\S/
|
|
243
259
|
end
|
|
244
260
|
(ancestor_node || node).source_range.column
|
|
245
261
|
end
|
|
@@ -253,6 +269,27 @@ module RuboCop
|
|
|
253
269
|
|
|
254
270
|
corrector.remove(range)
|
|
255
271
|
end
|
|
272
|
+
|
|
273
|
+
# When the `do` or `{` is on a continuation line of multiline method
|
|
274
|
+
# arguments, the indentation of that line is not a meaningful
|
|
275
|
+
# alignment target; anchor on the method dispatch position instead.
|
|
276
|
+
def do_line_anchor_loc(node, do_loc)
|
|
277
|
+
if do_line_begins_inside_argument?(node, do_loc)
|
|
278
|
+
node.send_node.selector || node.send_node.source_range
|
|
279
|
+
else
|
|
280
|
+
do_loc
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def do_line_begins_inside_argument?(node, do_loc)
|
|
285
|
+
line_begin_pos = do_loc.begin_pos - do_loc.column
|
|
286
|
+
first_char_pos = line_begin_pos + (do_loc.source_line =~ /\S/)
|
|
287
|
+
|
|
288
|
+
(node.send_node.arguments + node.arguments).any? do |argument|
|
|
289
|
+
argument.source_range.begin_pos <= first_char_pos &&
|
|
290
|
+
first_char_pos < argument.source_range.end_pos
|
|
291
|
+
end
|
|
292
|
+
end
|
|
256
293
|
end
|
|
257
294
|
end
|
|
258
295
|
end
|
|
@@ -28,7 +28,7 @@ module RuboCop
|
|
|
28
28
|
MISTAKES = { '=-' => '-=', '=+' => '+=', '=*' => '*=', '=!' => '!=' }.freeze
|
|
29
29
|
|
|
30
30
|
def on_asgn(node)
|
|
31
|
-
return unless (rhs =
|
|
31
|
+
return unless (rhs = node.expression)
|
|
32
32
|
|
|
33
33
|
range = range_between(node.loc.operator.end_pos - 1, rhs.source_range.begin_pos + 1)
|
|
34
34
|
source = range.source
|
|
@@ -38,16 +38,6 @@ module RuboCop
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
SIMPLE_ASSIGNMENT_TYPES.each { |asgn_type| alias_method :"on_#{asgn_type}", :on_asgn }
|
|
41
|
-
|
|
42
|
-
private
|
|
43
|
-
|
|
44
|
-
def rhs(node)
|
|
45
|
-
if node.casgn_type?
|
|
46
|
-
node.children[2]
|
|
47
|
-
else
|
|
48
|
-
node.children[1]
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
41
|
end
|
|
52
42
|
end
|
|
53
43
|
end
|
|
@@ -44,13 +44,6 @@ module RuboCop
|
|
|
44
44
|
RESTRICT_ON_SEND = PRECEDENCE.flatten.freeze
|
|
45
45
|
MSG = 'Wrap expressions with varying precedence with parentheses to avoid ambiguity.'
|
|
46
46
|
|
|
47
|
-
def on_new_investigation
|
|
48
|
-
# Cache the precedence of each node being investigated
|
|
49
|
-
# so that we only need to calculate it once
|
|
50
|
-
@node_precedences = {}
|
|
51
|
-
super
|
|
52
|
-
end
|
|
53
|
-
|
|
54
47
|
def on_and(node)
|
|
55
48
|
return unless (parent = node.parent)
|
|
56
49
|
|
|
@@ -77,9 +70,7 @@ module RuboCop
|
|
|
77
70
|
private
|
|
78
71
|
|
|
79
72
|
def precedence(node)
|
|
80
|
-
|
|
81
|
-
PRECEDENCE.index { |operators| operators.include?(operator_name(node)) }
|
|
82
|
-
end
|
|
73
|
+
PRECEDENCE.index { |operators| operators.include?(operator_name(node)) }
|
|
83
74
|
end
|
|
84
75
|
|
|
85
76
|
def operator?(node)
|
|
@@ -80,7 +80,6 @@ module RuboCop
|
|
|
80
80
|
check_assignment_chain(arg_name, arg_value)
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
-
# rubocop:disable Metrics/AbcSize
|
|
84
83
|
def check_assignment_chain(arg_name, node)
|
|
85
84
|
return unless node.lvasgn_type?
|
|
86
85
|
|
|
@@ -88,7 +87,7 @@ module RuboCop
|
|
|
88
87
|
current_node = node
|
|
89
88
|
|
|
90
89
|
while current_node.lvasgn_type?
|
|
91
|
-
seen_variables << current_node.children.first
|
|
90
|
+
seen_variables << current_node.children.first
|
|
92
91
|
current_node = current_node.children.last
|
|
93
92
|
end
|
|
94
93
|
|
|
@@ -99,7 +98,6 @@ module RuboCop
|
|
|
99
98
|
|
|
100
99
|
add_offense(current_node, message: format(MSG, arg_name: arg_name))
|
|
101
100
|
end
|
|
102
|
-
# rubocop:enable Metrics/AbcSize
|
|
103
101
|
end
|
|
104
102
|
end
|
|
105
103
|
end
|
|
@@ -49,7 +49,7 @@ module RuboCop
|
|
|
49
49
|
# Maybe further investigation of RuboCop AST will lead to an essential solution.
|
|
50
50
|
return unless node.loc
|
|
51
51
|
|
|
52
|
-
constant = node.
|
|
52
|
+
constant = node.source.delete_prefix('::')
|
|
53
53
|
return unless (deprecated_constant = deprecated_constants[constant])
|
|
54
54
|
|
|
55
55
|
alternative = deprecated_constant['Alternative']
|
|
@@ -63,12 +63,6 @@ module RuboCop
|
|
|
63
63
|
|
|
64
64
|
private
|
|
65
65
|
|
|
66
|
-
def constant_name(node, nested_constant_name)
|
|
67
|
-
return nested_constant_name.to_s unless node.namespace.const_type?
|
|
68
|
-
|
|
69
|
-
constant_name(node.namespace, "#{node.namespace.short_name}::#{nested_constant_name}")
|
|
70
|
-
end
|
|
71
|
-
|
|
72
66
|
def message(good, bad, deprecated_version)
|
|
73
67
|
deprecated_message = ", deprecated since Ruby #{deprecated_version}" if deprecated_version
|
|
74
68
|
|
|
@@ -77,7 +77,7 @@ module RuboCop
|
|
|
77
77
|
return false unless processed_source.contains_comment?(node.source_range)
|
|
78
78
|
|
|
79
79
|
line_comment = processed_source.comment_at_line(node.source_range.line)
|
|
80
|
-
!line_comment || !comment_disables_cop?(line_comment
|
|
80
|
+
!line_comment || !comment_disables_cop?(line_comment)
|
|
81
81
|
end
|
|
82
82
|
|
|
83
83
|
def allow_empty_lambdas?
|
|
@@ -85,8 +85,8 @@ module RuboCop
|
|
|
85
85
|
end
|
|
86
86
|
|
|
87
87
|
def comment_disables_cop?(comment)
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
directive = DirectiveComment.new(comment)
|
|
89
|
+
directive.disabled? && directive.cop_names.include?(cop_name)
|
|
90
90
|
end
|
|
91
91
|
end
|
|
92
92
|
end
|
|
@@ -43,7 +43,25 @@ module RuboCop
|
|
|
43
43
|
MSG = 'Do not return from an `ensure` block.'
|
|
44
44
|
|
|
45
45
|
def on_ensure(node)
|
|
46
|
-
node.branch&.each_node(:return)
|
|
46
|
+
node.branch&.each_node(:return) do |return_node|
|
|
47
|
+
next if return_from_inner_scope?(return_node, node)
|
|
48
|
+
|
|
49
|
+
add_offense(return_node)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
# A `return` inside a nested method definition or lambda within the
|
|
56
|
+
# `ensure` returns from that inner scope, not from the method whose
|
|
57
|
+
# `ensure` this is, so it is not an offense. A `return` inside a plain
|
|
58
|
+
# block (or `proc`) does propagate out, so it remains an offense.
|
|
59
|
+
def return_from_inner_scope?(return_node, ensure_node)
|
|
60
|
+
return_node.each_ancestor do |ancestor|
|
|
61
|
+
break if ancestor == ensure_node
|
|
62
|
+
return true if ancestor.any_def_type? || (ancestor.any_block_type? && ancestor.lambda?)
|
|
63
|
+
end
|
|
64
|
+
false
|
|
47
65
|
end
|
|
48
66
|
end
|
|
49
67
|
end
|
|
@@ -148,7 +148,9 @@ module RuboCop
|
|
|
148
148
|
arguments = node.arguments
|
|
149
149
|
overridden_kwargs = kwargs.dup
|
|
150
150
|
|
|
151
|
-
|
|
151
|
+
if arguments[2] && !arguments[2].hash_type?
|
|
152
|
+
overridden_kwargs[0] = "trim_mode: #{arguments[2].source}"
|
|
153
|
+
end
|
|
152
154
|
|
|
153
155
|
if arguments[3] && !arguments[3].hash_type?
|
|
154
156
|
overridden_kwargs[1] = "eoutvar: #{arguments[3].source}"
|
|
@@ -61,11 +61,15 @@ module RuboCop
|
|
|
61
61
|
private
|
|
62
62
|
|
|
63
63
|
def scheduler_compatible?(io1, io2)
|
|
64
|
-
return false unless
|
|
64
|
+
return false unless single_io_array?(io1)
|
|
65
65
|
|
|
66
66
|
io2&.array_type? ? io2.values.empty? : (io2.nil? || io2.nil_type?)
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
+
def single_io_array?(node)
|
|
70
|
+
node&.array_type? && node.values.size == 1 && !node.values.first.splat_type?
|
|
71
|
+
end
|
|
72
|
+
|
|
69
73
|
def preferred_method(read, write, timeout)
|
|
70
74
|
timeout_argument = timeout.nil? ? '' : "(#{timeout.source})"
|
|
71
75
|
|
|
@@ -24,8 +24,25 @@ module RuboCop
|
|
|
24
24
|
MSG = 'Interpolation in single quoted string detected. ' \
|
|
25
25
|
'Use double quoted strings if you need interpolation.'
|
|
26
26
|
|
|
27
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
28
27
|
def on_str(node)
|
|
28
|
+
check(node)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# A multiline single-quoted string is parsed as a `dstr` of `str` segments, so it
|
|
32
|
+
# is not covered by `on_str`. Inspect single-quoted `dstr`s here; double-quoted
|
|
33
|
+
# interpolation is also a `dstr`, hence the delimiter check.
|
|
34
|
+
def on_dstr(node)
|
|
35
|
+
# A heredoc is also a `dstr`, but its `loc` is a `Parser::Source::Map::Heredoc`
|
|
36
|
+
# with no `begin`, so bail before touching it.
|
|
37
|
+
return if heredoc?(node)
|
|
38
|
+
|
|
39
|
+
check(node) if node.loc.begin&.source == "'"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
|
45
|
+
def check(node)
|
|
29
46
|
return if node.parent&.regexp_type?
|
|
30
47
|
return unless /(?<!\\)#\{.*\}/.match?(node.source)
|
|
31
48
|
return if heredoc?(node)
|
|
@@ -36,8 +53,6 @@ module RuboCop
|
|
|
36
53
|
end
|
|
37
54
|
# rubocop:enable Metrics/CyclomaticComplexity
|
|
38
55
|
|
|
39
|
-
private
|
|
40
|
-
|
|
41
56
|
def autocorrect(corrector, node)
|
|
42
57
|
starting_token, ending_token = if node.source.include?('"')
|
|
43
58
|
['%{', '}']
|
|
@@ -56,7 +56,17 @@ module RuboCop
|
|
|
56
56
|
def traverse_node(node, &block)
|
|
57
57
|
yield node if node.equals_asgn?
|
|
58
58
|
|
|
59
|
-
node.each_child_node
|
|
59
|
+
node.each_child_node do |child|
|
|
60
|
+
next if scope_body?(node, child)
|
|
61
|
+
|
|
62
|
+
traverse_node(child, &block)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# An assignment inside a block or method body within the condition belongs to
|
|
67
|
+
# that inner scope rather than the condition itself, so it is not inspected.
|
|
68
|
+
def scope_body?(node, child)
|
|
69
|
+
node.type?(:any_block, :any_def) && child == node.body
|
|
60
70
|
end
|
|
61
71
|
|
|
62
72
|
def all_literals?(node)
|
|
@@ -119,11 +119,13 @@ module RuboCop
|
|
|
119
119
|
end
|
|
120
120
|
|
|
121
121
|
def autocorrected_value_for_string(node)
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
122
|
+
return node.source.delete_prefix('"').delete_suffix('"') unless node.value.valid_encoding?
|
|
123
|
+
|
|
124
|
+
escape_string_content(node.children.last)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def escape_string_content(string)
|
|
128
|
+
string.gsub(/[\\"]|#(?=[@{$])/, '\\\\\&')
|
|
127
129
|
end
|
|
128
130
|
|
|
129
131
|
def autocorrected_value_for_symbol(node)
|
|
@@ -134,12 +136,7 @@ module RuboCop
|
|
|
134
136
|
end
|
|
135
137
|
|
|
136
138
|
def autocorrected_value_in_hash_for_symbol(node)
|
|
137
|
-
|
|
138
|
-
if / |"|'/.match?(node.value.to_s)
|
|
139
|
-
":\\\"#{node.value.to_s.gsub('"') { '\\\\\"' }}\\\""
|
|
140
|
-
else
|
|
141
|
-
":#{node.value}"
|
|
142
|
-
end
|
|
139
|
+
escape_string_content(node.value.inspect)
|
|
143
140
|
end
|
|
144
141
|
|
|
145
142
|
def autocorrected_value_for_array(node)
|
|
@@ -9,7 +9,7 @@ module RuboCop
|
|
|
9
9
|
# cop disables on wide ranges of code, that later contributors to
|
|
10
10
|
# a file wouldn't be aware of.
|
|
11
11
|
#
|
|
12
|
-
# You can set `
|
|
12
|
+
# You can set `MaxRangeSize` to define the maximum number of
|
|
13
13
|
# consecutive lines a cop can be disabled for.
|
|
14
14
|
#
|
|
15
15
|
# - `.inf` any size (default)
|
|
@@ -23,7 +23,7 @@ module RuboCop
|
|
|
23
23
|
# # rubocop:enable SomeCop
|
|
24
24
|
# ----
|
|
25
25
|
#
|
|
26
|
-
# @example
|
|
26
|
+
# @example MaxRangeSize: .inf (default)
|
|
27
27
|
#
|
|
28
28
|
# # good
|
|
29
29
|
# # rubocop:disable Layout/SpaceAroundOperators
|
|
@@ -37,7 +37,7 @@ module RuboCop
|
|
|
37
37
|
# x= 0
|
|
38
38
|
# # EOF
|
|
39
39
|
#
|
|
40
|
-
# @example
|
|
40
|
+
# @example MaxRangeSize: 2
|
|
41
41
|
#
|
|
42
42
|
# # good
|
|
43
43
|
# # rubocop:disable Layout/SpaceAroundOperators
|
|
@@ -94,7 +94,7 @@ module RuboCop
|
|
|
94
94
|
end
|
|
95
95
|
|
|
96
96
|
def max_range
|
|
97
|
-
@max_range ||= cop_config['
|
|
97
|
+
@max_range ||= cop_config['MaxRangeSize']
|
|
98
98
|
end
|
|
99
99
|
|
|
100
100
|
def message(cop, comment, type = 'cop')
|
|
@@ -41,6 +41,8 @@ module RuboCop
|
|
|
41
41
|
def on_lvasgn(node)
|
|
42
42
|
node.each_node(:kwbegin) do |kwbegin_node|
|
|
43
43
|
kwbegin_node.each_node(:return) do |return_node|
|
|
44
|
+
next if return_from_inner_scope?(return_node, kwbegin_node)
|
|
45
|
+
|
|
44
46
|
add_offense(return_node)
|
|
45
47
|
end
|
|
46
48
|
end
|
|
@@ -51,6 +53,20 @@ module RuboCop
|
|
|
51
53
|
alias on_casgn on_lvasgn
|
|
52
54
|
alias on_or_asgn on_lvasgn
|
|
53
55
|
alias on_op_asgn on_lvasgn
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
# A `return` inside a nested method definition or lambda within the
|
|
60
|
+
# `begin..end` returns from that inner scope rather than the assignment
|
|
61
|
+
# context, so it is not an offense. A `return` inside a plain block (or
|
|
62
|
+
# `proc`) does propagate out, so it remains an offense.
|
|
63
|
+
def return_from_inner_scope?(return_node, kwbegin_node)
|
|
64
|
+
return_node.each_ancestor do |ancestor|
|
|
65
|
+
break if ancestor == kwbegin_node
|
|
66
|
+
return true if ancestor.any_def_type? || (ancestor.any_block_type? && ancestor.lambda?)
|
|
67
|
+
end
|
|
68
|
+
false
|
|
69
|
+
end
|
|
54
70
|
end
|
|
55
71
|
end
|
|
56
72
|
end
|
|
@@ -66,7 +66,7 @@ module RuboCop
|
|
|
66
66
|
# # good
|
|
67
67
|
# 10.minutes.to_i
|
|
68
68
|
#
|
|
69
|
-
# @example
|
|
69
|
+
# @example AllowedClasses: [Time, DateTime] (default)
|
|
70
70
|
#
|
|
71
71
|
# # good
|
|
72
72
|
# Time.now.to_datetime.to_i
|
|
@@ -117,11 +117,11 @@ module RuboCop
|
|
|
117
117
|
|
|
118
118
|
message = format(
|
|
119
119
|
MSG,
|
|
120
|
-
current:
|
|
120
|
+
current: current_method(node, receiver, to_method),
|
|
121
121
|
corrected_method: correct_method(node, receiver)
|
|
122
122
|
)
|
|
123
123
|
add_offense(node, message: message) do |corrector|
|
|
124
|
-
next if part_of_ignored_node?(node)
|
|
124
|
+
next if safe_navigation?(node) || part_of_ignored_node?(node)
|
|
125
125
|
|
|
126
126
|
corrector.replace(node, correct_method(node, node.receiver))
|
|
127
127
|
|
|
@@ -156,11 +156,20 @@ module RuboCop
|
|
|
156
156
|
"{ |i| #{body} }"
|
|
157
157
|
end
|
|
158
158
|
|
|
159
|
+
def current_method(node, receiver, to_method)
|
|
160
|
+
operator = node.csend_type? ? '&.' : '.'
|
|
161
|
+
"#{receiver.source}#{operator}#{to_method}"
|
|
162
|
+
end
|
|
163
|
+
|
|
159
164
|
def remove_parentheses(corrector, node)
|
|
160
165
|
corrector.replace(node.loc.begin, ' ')
|
|
161
166
|
corrector.remove(node.loc.end)
|
|
162
167
|
end
|
|
163
168
|
|
|
169
|
+
def safe_navigation?(node)
|
|
170
|
+
node.csend_type? || node.each_descendant(:csend).any?
|
|
171
|
+
end
|
|
172
|
+
|
|
164
173
|
def allow_receiver?(receiver)
|
|
165
174
|
if receiver.numeric_type? || (receiver.call_type? &&
|
|
166
175
|
(conversion_method?(receiver.method_name) ||
|
|
@@ -188,7 +197,7 @@ module RuboCop
|
|
|
188
197
|
end
|
|
189
198
|
|
|
190
199
|
def ignored_classes
|
|
191
|
-
cop_config.fetch('
|
|
200
|
+
cop_config.fetch('AllowedClasses', [])
|
|
192
201
|
end
|
|
193
202
|
|
|
194
203
|
def ignored_class?(name)
|
|
@@ -58,6 +58,9 @@ module RuboCop
|
|
|
58
58
|
'(op-asgn (lvasgn $_lhs) $_operation ({int lvar} $_rhs))'
|
|
59
59
|
|
|
60
60
|
def on_send(node)
|
|
61
|
+
# Safe navigation short-circuits to `nil` when the receiver is `nil`, so the
|
|
62
|
+
# result is not constant and replacing it with `0`/`1` would change behavior.
|
|
63
|
+
return if node.csend_type?
|
|
61
64
|
return unless (lhs, operation, rhs = operation_with_constant_result?(node))
|
|
62
65
|
return unless (result = constant_result?(lhs, operation, rhs))
|
|
63
66
|
|
|
@@ -38,23 +38,23 @@ module RuboCop
|
|
|
38
38
|
def on_new_investigation
|
|
39
39
|
return if processed_source.buffer.source.empty?
|
|
40
40
|
|
|
41
|
-
encoding_line,
|
|
41
|
+
encoding_line, other_magic_comment_line = magic_comment_lines
|
|
42
42
|
|
|
43
|
-
return unless encoding_line &&
|
|
44
|
-
return if encoding_line <
|
|
43
|
+
return unless encoding_line && other_magic_comment_line
|
|
44
|
+
return if encoding_line < other_magic_comment_line
|
|
45
45
|
|
|
46
46
|
range = processed_source.buffer.line_range(encoding_line + 1)
|
|
47
47
|
|
|
48
48
|
add_offense(range) do |corrector|
|
|
49
|
-
autocorrect(corrector, encoding_line,
|
|
49
|
+
autocorrect(corrector, encoding_line, other_magic_comment_line)
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
private
|
|
54
54
|
|
|
55
|
-
def autocorrect(corrector, encoding_line,
|
|
55
|
+
def autocorrect(corrector, encoding_line, other_magic_comment_line)
|
|
56
56
|
range1 = processed_source.buffer.line_range(encoding_line + 1)
|
|
57
|
-
range2 = processed_source.buffer.line_range(
|
|
57
|
+
range2 = processed_source.buffer.line_range(other_magic_comment_line + 1)
|
|
58
58
|
|
|
59
59
|
corrector.replace(range1, range2.source)
|
|
60
60
|
corrector.replace(range2, range1.source)
|
|
@@ -66,7 +66,7 @@ module RuboCop
|
|
|
66
66
|
leading_magic_comments.each.with_index do |comment, index|
|
|
67
67
|
if comment.encoding_specified?
|
|
68
68
|
lines[0] = index
|
|
69
|
-
elsif comment.
|
|
69
|
+
elsif comment.valid?
|
|
70
70
|
lines[1] = index
|
|
71
71
|
end
|
|
72
72
|
|
|
@@ -283,7 +283,10 @@ module RuboCop
|
|
|
283
283
|
end
|
|
284
284
|
|
|
285
285
|
def matching_range(haystack, needle)
|
|
286
|
-
|
|
286
|
+
# Match the cop name as a whole token so a shorter name is not found inside a
|
|
287
|
+
# longer one that shares its prefix (e.g. `Lint/AmbiguousOperator` in
|
|
288
|
+
# `Lint/AmbiguousOperatorPrecedence`).
|
|
289
|
+
offset = haystack.source.index(/#{Regexp.escape(needle)}(?!\w)/)
|
|
287
290
|
return unless offset
|
|
288
291
|
|
|
289
292
|
offset += haystack.begin_pos
|
|
@@ -74,7 +74,10 @@ module RuboCop
|
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
def cop_name_indention(comment, name)
|
|
77
|
-
|
|
77
|
+
# Match the cop name as a whole token so a shorter name is not found inside a
|
|
78
|
+
# longer one that shares its prefix (e.g. `Layout/EmptyLines` in
|
|
79
|
+
# `Layout/EmptyLinesAfterModuleInclusion`).
|
|
80
|
+
comment.text.index(/#{Regexp.escape(name)}(?!\w)/)
|
|
78
81
|
end
|
|
79
82
|
|
|
80
83
|
def range_with_comma(comment, name)
|