rubocop 1.67.0 → 1.69.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 +1 -1
- data/config/default.yml +81 -6
- data/lib/rubocop/cached_data.rb +12 -4
- data/lib/rubocop/cli/command/execute_runner.rb +1 -1
- data/lib/rubocop/cli/command/version.rb +2 -2
- data/lib/rubocop/cop/autocorrect_logic.rb +22 -2
- data/lib/rubocop/cop/base.rb +1 -1
- data/lib/rubocop/cop/bundler/gem_filename.rb +0 -1
- data/lib/rubocop/cop/bundler/insecure_protocol_source.rb +0 -1
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +1 -12
- data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +1 -1
- data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +10 -0
- data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +1 -2
- data/lib/rubocop/cop/gemspec/required_ruby_version.rb +0 -2
- data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +2 -4
- data/lib/rubocop/cop/internal_affairs/numblock_handler.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/operator_keyword.rb +46 -0
- data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +0 -2
- data/lib/rubocop/cop/internal_affairs.rb +1 -0
- data/lib/rubocop/cop/layout/argument_alignment.rb +1 -2
- data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/begin_end_alignment.rb +0 -1
- data/lib/rubocop/cop/layout/block_alignment.rb +1 -2
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +2 -3
- data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +3 -4
- data/lib/rubocop/cop/layout/empty_lines_around_method_body.rb +3 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +7 -7
- data/lib/rubocop/cop/layout/leading_comment_space.rb +44 -1
- data/lib/rubocop/cop/layout/line_length.rb +118 -4
- data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -3
- data/lib/rubocop/cop/layout/parameter_alignment.rb +3 -4
- data/lib/rubocop/cop/layout/redundant_line_break.rb +3 -35
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +3 -2
- data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_operators.rb +16 -17
- data/lib/rubocop/cop/layout/space_before_brackets.rb +5 -5
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +6 -0
- data/lib/rubocop/cop/layout/space_inside_block_braces.rb +4 -0
- data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +4 -0
- data/lib/rubocop/cop/layout/space_inside_string_interpolation.rb +0 -1
- data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +11 -12
- data/lib/rubocop/cop/lint/circular_argument_reference.rb +2 -0
- data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +1 -1
- data/lib/rubocop/cop/lint/duplicate_branch.rb +39 -4
- data/lib/rubocop/cop/lint/empty_ensure.rb +1 -1
- data/lib/rubocop/cop/lint/empty_file.rb +0 -2
- data/lib/rubocop/cop/lint/ensure_return.rb +1 -1
- data/lib/rubocop/cop/lint/float_comparison.rb +14 -6
- data/lib/rubocop/cop/lint/float_out_of_range.rb +1 -3
- data/lib/rubocop/cop/lint/hash_new_with_keyword_arguments_as_default.rb +55 -0
- data/lib/rubocop/cop/lint/interpolation_check.rb +9 -0
- data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +3 -0
- data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +1 -1
- data/lib/rubocop/cop/lint/mixed_case_range.rb +2 -5
- data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
- data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +2 -2
- data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +8 -1
- data/lib/rubocop/cop/lint/number_conversion.rb +0 -1
- data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -2
- data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +106 -0
- data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +1 -2
- data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +12 -7
- data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +8 -7
- data/lib/rubocop/cop/lint/regexp_as_condition.rb +0 -1
- data/lib/rubocop/cop/lint/rescue_type.rb +3 -7
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +9 -0
- data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +5 -1
- data/lib/rubocop/cop/lint/self_assignment.rb +8 -10
- data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -1
- data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +88 -0
- data/lib/rubocop/cop/lint/unused_method_argument.rb +18 -2
- data/lib/rubocop/cop/lint/useless_defined.rb +55 -0
- data/lib/rubocop/cop/lint/useless_rescue.rb +1 -1
- data/lib/rubocop/cop/lint/useless_setter_call.rb +14 -25
- data/lib/rubocop/cop/lint/void.rb +3 -2
- data/lib/rubocop/cop/metrics/class_length.rb +7 -7
- data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +4 -1
- data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +1 -1
- data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -2
- data/lib/rubocop/cop/mixin/check_assignment.rb +4 -12
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +10 -0
- data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +49 -0
- data/lib/rubocop/cop/mixin/dig_help.rb +27 -0
- data/lib/rubocop/cop/mixin/endless_method_rewriter.rb +24 -0
- data/lib/rubocop/cop/mixin/frozen_string_literal.rb +3 -1
- data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +5 -9
- data/lib/rubocop/cop/mixin/range_help.rb +0 -1
- data/lib/rubocop/cop/mixin/target_ruby_version.rb +17 -1
- data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
- data/lib/rubocop/cop/naming/constant_name.rb +6 -7
- data/lib/rubocop/cop/naming/file_name.rb +0 -2
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +11 -12
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +3 -11
- data/lib/rubocop/cop/naming/variable_name.rb +3 -4
- data/lib/rubocop/cop/naming/variable_number.rb +2 -3
- data/lib/rubocop/cop/offense.rb +2 -3
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +53 -24
- data/lib/rubocop/cop/style/ambiguous_endless_method_definition.rb +79 -0
- data/lib/rubocop/cop/style/array_intersect.rb +5 -4
- data/lib/rubocop/cop/style/bitwise_predicate.rb +100 -0
- data/lib/rubocop/cop/style/block_delimiters.rb +18 -3
- data/lib/rubocop/cop/style/case_like_if.rb +8 -11
- data/lib/rubocop/cop/style/combinable_defined.rb +115 -0
- data/lib/rubocop/cop/style/commented_keyword.rb +11 -1
- data/lib/rubocop/cop/style/conditional_assignment.rb +19 -21
- data/lib/rubocop/cop/style/constant_visibility.rb +3 -12
- data/lib/rubocop/cop/style/dig_chain.rb +90 -0
- data/lib/rubocop/cop/style/endless_method.rb +1 -14
- data/lib/rubocop/cop/style/file_null.rb +73 -0
- data/lib/rubocop/cop/style/file_touch.rb +75 -0
- data/lib/rubocop/cop/style/for.rb +0 -1
- data/lib/rubocop/cop/style/global_vars.rb +1 -3
- data/lib/rubocop/cop/style/guard_clause.rb +15 -2
- data/lib/rubocop/cop/style/hash_conversion.rb +1 -2
- data/lib/rubocop/cop/style/if_inside_else.rb +0 -1
- data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +1 -2
- data/lib/rubocop/cop/style/if_with_semicolon.rb +14 -5
- data/lib/rubocop/cop/style/inverse_methods.rb +0 -1
- data/lib/rubocop/cop/style/keyword_arguments_merging.rb +67 -0
- data/lib/rubocop/cop/style/lambda_call.rb +0 -1
- data/lib/rubocop/cop/style/map_into_array.rb +6 -1
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +7 -11
- data/lib/rubocop/cop/style/missing_respond_to_missing.rb +33 -3
- data/lib/rubocop/cop/style/multiline_memoization.rb +1 -1
- data/lib/rubocop/cop/style/multiple_comparison.rb +28 -39
- data/lib/rubocop/cop/style/mutable_constant.rb +4 -5
- data/lib/rubocop/cop/style/negated_if_else_condition.rb +6 -4
- data/lib/rubocop/cop/style/nested_ternary_operator.rb +5 -4
- data/lib/rubocop/cop/style/not.rb +1 -1
- data/lib/rubocop/cop/style/one_line_conditional.rb +25 -4
- data/lib/rubocop/cop/style/operator_method_call.rb +5 -6
- data/lib/rubocop/cop/style/or_assignment.rb +3 -6
- data/lib/rubocop/cop/style/parallel_assignment.rb +8 -13
- data/lib/rubocop/cop/style/raise_args.rb +1 -1
- data/lib/rubocop/cop/style/redundant_assignment.rb +1 -1
- data/lib/rubocop/cop/style/redundant_condition.rb +36 -21
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +21 -2
- data/lib/rubocop/cop/style/redundant_parentheses.rb +9 -11
- data/lib/rubocop/cop/style/redundant_regexp_argument.rb +1 -0
- data/lib/rubocop/cop/style/redundant_return.rb +2 -2
- data/lib/rubocop/cop/style/redundant_self.rb +7 -14
- data/lib/rubocop/cop/style/redundant_self_assignment.rb +7 -5
- data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +4 -4
- data/lib/rubocop/cop/style/redundant_sort.rb +1 -1
- data/lib/rubocop/cop/style/rescue_modifier.rb +2 -3
- data/lib/rubocop/cop/style/safe_navigation.rb +13 -1
- data/lib/rubocop/cop/style/safe_navigation_chain_length.rb +52 -0
- data/lib/rubocop/cop/style/select_by_regexp.rb +1 -1
- data/lib/rubocop/cop/style/self_assignment.rb +11 -17
- data/lib/rubocop/cop/style/signal_exception.rb +2 -3
- data/lib/rubocop/cop/style/single_argument_dig.rb +9 -5
- data/lib/rubocop/cop/style/single_line_do_end_block.rb +13 -3
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +2 -3
- data/lib/rubocop/cop/style/special_global_vars.rb +1 -1
- data/lib/rubocop/cop/style/string_concatenation.rb +0 -1
- data/lib/rubocop/cop/style/swap_values.rb +4 -15
- data/lib/rubocop/cop/style/ternary_parentheses.rb +25 -4
- data/lib/rubocop/cop/style/trailing_underscore_variable.rb +4 -4
- data/lib/rubocop/cop/style/variable_interpolation.rb +1 -2
- data/lib/rubocop/cop/variable_force/assignment.rb +18 -3
- data/lib/rubocop/cop/variable_force/branch.rb +1 -1
- data/lib/rubocop/cop/variable_force/variable.rb +5 -1
- data/lib/rubocop/cop/variable_force/variable_table.rb +2 -2
- data/lib/rubocop/cop/variable_force.rb +4 -10
- data/lib/rubocop/cops_documentation_generator.rb +20 -10
- data/lib/rubocop/formatter/disabled_config_formatter.rb +1 -1
- data/lib/rubocop/runner.rb +16 -8
- data/lib/rubocop/target_ruby.rb +1 -1
- data/lib/rubocop/version.rb +27 -8
- data/lib/rubocop.rb +16 -0
- metadata +28 -12
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Prefer bitwise predicate methods over direct comparison operations.
|
7
|
+
#
|
8
|
+
# @safety
|
9
|
+
# This cop is unsafe, as it can produce false positives if the receiver
|
10
|
+
# is not an `Integer` object.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
#
|
14
|
+
# # bad - checks any set bits
|
15
|
+
# (variable & flags).positive?
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# variable.anybits?(flags)
|
19
|
+
#
|
20
|
+
# # bad - checks all set bits
|
21
|
+
# (variable & flags) == flags
|
22
|
+
#
|
23
|
+
# # good
|
24
|
+
# variable.allbits?(flags)
|
25
|
+
#
|
26
|
+
# # bad - checks no set bits
|
27
|
+
# (variable & flags).zero?
|
28
|
+
#
|
29
|
+
# # good
|
30
|
+
# variable.nobits?(flags)
|
31
|
+
#
|
32
|
+
class BitwisePredicate < Base
|
33
|
+
extend AutoCorrector
|
34
|
+
extend TargetRubyVersion
|
35
|
+
|
36
|
+
MSG = 'Replace with `%<preferred>s` for comparison with bit flags.'
|
37
|
+
RESTRICT_ON_SEND = %i[!= == > >= positive? zero?].freeze
|
38
|
+
|
39
|
+
minimum_target_ruby_version 2.5
|
40
|
+
|
41
|
+
# @!method anybits?(node)
|
42
|
+
def_node_matcher :anybits?, <<~PATTERN
|
43
|
+
{
|
44
|
+
(send #bit_operation? :positive?)
|
45
|
+
(send #bit_operation? :> (int 0))
|
46
|
+
(send #bit_operation? :>= (int 1))
|
47
|
+
(send #bit_operation? :!= (int 0))
|
48
|
+
}
|
49
|
+
PATTERN
|
50
|
+
|
51
|
+
# @!method allbits?(node)
|
52
|
+
def_node_matcher :allbits?, <<~PATTERN
|
53
|
+
{
|
54
|
+
(send (begin (send _ :& _flags)) :== _flags)
|
55
|
+
(send (begin (send _flags :& _)) :== _flags)
|
56
|
+
}
|
57
|
+
PATTERN
|
58
|
+
|
59
|
+
# @!method nobits?(node)
|
60
|
+
def_node_matcher :nobits?, <<~PATTERN
|
61
|
+
{
|
62
|
+
(send #bit_operation? :zero?)
|
63
|
+
(send #bit_operation? :== (int 0))
|
64
|
+
}
|
65
|
+
PATTERN
|
66
|
+
|
67
|
+
# @!method bit_operation?(node)
|
68
|
+
def_node_matcher :bit_operation?, <<~PATTERN
|
69
|
+
(begin
|
70
|
+
(send _ :& _))
|
71
|
+
PATTERN
|
72
|
+
|
73
|
+
def on_send(node)
|
74
|
+
return unless node.receiver&.begin_type?
|
75
|
+
return unless (preferred_method = preferred_method(node))
|
76
|
+
|
77
|
+
bit_operation = node.receiver.children.first
|
78
|
+
lhs, _operator, rhs = *bit_operation
|
79
|
+
preferred = "#{lhs.source}.#{preferred_method}(#{rhs.source})"
|
80
|
+
|
81
|
+
add_offense(node, message: format(MSG, preferred: preferred)) do |corrector|
|
82
|
+
corrector.replace(node, preferred)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def preferred_method(node)
|
89
|
+
if anybits?(node)
|
90
|
+
'anybits?'
|
91
|
+
elsif allbits?(node)
|
92
|
+
'allbits?'
|
93
|
+
elsif nobits?(node)
|
94
|
+
'nobits?'
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -303,13 +303,28 @@ module RuboCop
|
|
303
303
|
|
304
304
|
def move_comment_before_block(corrector, comment, block_node, closing_brace)
|
305
305
|
range = block_node.chained? ? end_of_chain(block_node.parent).source_range : closing_brace
|
306
|
+
|
307
|
+
# It is possible that there is code between the block and the comment
|
308
|
+
# which needs to be preserved and trimmed.
|
309
|
+
pre_comment_range = source_range_before_comment(range, comment)
|
310
|
+
|
306
311
|
corrector.remove(range_with_surrounding_space(comment.source_range, side: :right))
|
307
|
-
remove_trailing_whitespace(corrector,
|
308
|
-
corrector.insert_after(
|
312
|
+
remove_trailing_whitespace(corrector, pre_comment_range, comment)
|
313
|
+
corrector.insert_after(pre_comment_range, "\n")
|
309
314
|
|
310
315
|
corrector.insert_before(block_node, "#{comment.text}\n")
|
311
316
|
end
|
312
317
|
|
318
|
+
def source_range_before_comment(range, comment)
|
319
|
+
range = range.end.join(comment.source_range.begin)
|
320
|
+
|
321
|
+
# End the range before any whitespace that precedes the comment
|
322
|
+
trailing_whitespace_count = range.source[/\s+\z/]&.length
|
323
|
+
range = range.adjust(end_pos: -trailing_whitespace_count) if trailing_whitespace_count
|
324
|
+
|
325
|
+
range
|
326
|
+
end
|
327
|
+
|
313
328
|
def end_of_chain(node)
|
314
329
|
return end_of_chain(node.block_node) if with_block?(node)
|
315
330
|
return node unless node.chained?
|
@@ -466,7 +481,7 @@ module RuboCop
|
|
466
481
|
end
|
467
482
|
|
468
483
|
def conditional?(node)
|
469
|
-
node.if_type? || node.
|
484
|
+
node.if_type? || node.operator_keyword?
|
470
485
|
end
|
471
486
|
|
472
487
|
def array_or_range?(node)
|
@@ -106,7 +106,7 @@ module RuboCop
|
|
106
106
|
when :or
|
107
107
|
find_target(node.lhs)
|
108
108
|
when :match_with_lvasgn
|
109
|
-
lhs, rhs = *node
|
109
|
+
lhs, rhs = *node # rubocop:disable InternalAffairs/NodeDestructuring
|
110
110
|
if lhs.regexp_type?
|
111
111
|
rhs
|
112
112
|
elsif rhs.regexp_type?
|
@@ -172,7 +172,7 @@ module RuboCop
|
|
172
172
|
return collect_conditions(node.lhs, target, conditions) &&
|
173
173
|
collect_conditions(node.rhs, target, conditions)
|
174
174
|
when :match_with_lvasgn
|
175
|
-
lhs, rhs = *node
|
175
|
+
lhs, rhs = *node # rubocop:disable InternalAffairs/NodeDestructuring
|
176
176
|
condition_from_binary_op(lhs, rhs, target)
|
177
177
|
when :send
|
178
178
|
condition_from_send_node(node, target)
|
@@ -191,8 +191,7 @@ module RuboCop
|
|
191
191
|
when :=~, :match, :match?
|
192
192
|
condition_from_match_node(node, target)
|
193
193
|
when :===
|
194
|
-
|
195
|
-
lhs if rhs == target
|
194
|
+
node.receiver if node.first_argument == target
|
196
195
|
when :include?, :cover?
|
197
196
|
condition_from_include_or_cover_node(node, target)
|
198
197
|
end
|
@@ -200,14 +199,12 @@ module RuboCop
|
|
200
199
|
# rubocop:enable Metrics/CyclomaticComplexity
|
201
200
|
|
202
201
|
def condition_from_equality_node(node, target)
|
203
|
-
|
204
|
-
condition = condition_from_binary_op(lhs, rhs, target)
|
202
|
+
condition = condition_from_binary_op(node.receiver, node.first_argument, target)
|
205
203
|
condition if condition && !class_reference?(condition)
|
206
204
|
end
|
207
205
|
|
208
206
|
def condition_from_match_node(node, target)
|
209
|
-
|
210
|
-
condition_from_binary_op(lhs, rhs, target)
|
207
|
+
condition_from_binary_op(node.receiver, node.first_argument, target)
|
211
208
|
end
|
212
209
|
|
213
210
|
def condition_from_include_or_cover_node(node, target)
|
@@ -263,11 +260,11 @@ module RuboCop
|
|
263
260
|
def regexp_with_working_captures?(node)
|
264
261
|
case node.type
|
265
262
|
when :match_with_lvasgn
|
266
|
-
lhs, _rhs = *node
|
263
|
+
lhs, _rhs = *node # rubocop:disable InternalAffairs/NodeDestructuring
|
267
264
|
node.loc.selector.source == '=~' && regexp_with_named_captures?(lhs)
|
268
265
|
when :send
|
269
|
-
|
270
|
-
|
266
|
+
node.method?(:match) &&
|
267
|
+
[node.receiver, node.first_argument].any? { |n| regexp_with_named_captures?(n) }
|
271
268
|
end
|
272
269
|
end
|
273
270
|
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Checks for multiple `defined?` calls joined by `&&` that can be combined
|
7
|
+
# into a single `defined?`.
|
8
|
+
#
|
9
|
+
# When checking that a nested constant or chained method is defined, it is
|
10
|
+
# not necessary to check each ancestor or component of the chain.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# # bad
|
14
|
+
# defined?(Foo) && defined?(Foo::Bar) && defined?(Foo::Bar::Baz)
|
15
|
+
#
|
16
|
+
# # good
|
17
|
+
# defined?(Foo::Bar::Baz)
|
18
|
+
#
|
19
|
+
# # bad
|
20
|
+
# defined?(foo) && defined?(foo.bar) && defined?(foo.bar.baz)
|
21
|
+
#
|
22
|
+
# # good
|
23
|
+
# defined?(foo.bar.baz)
|
24
|
+
class CombinableDefined < Base
|
25
|
+
extend AutoCorrector
|
26
|
+
include RangeHelp
|
27
|
+
|
28
|
+
MSG = 'Combine nested `defined?` calls.'
|
29
|
+
OPERATORS = %w[&& and].freeze
|
30
|
+
|
31
|
+
def on_and(node)
|
32
|
+
# Only register an offense if all `&&` terms are `defined?` calls
|
33
|
+
return unless (terms = terms(node)).all?(&:defined_type?)
|
34
|
+
|
35
|
+
calls = defined_calls(terms)
|
36
|
+
namespaces = namespaces(calls)
|
37
|
+
|
38
|
+
calls.each do |call|
|
39
|
+
next unless namespaces.any?(call)
|
40
|
+
|
41
|
+
add_offense(node) do |corrector|
|
42
|
+
remove_term(corrector, call)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def terms(node)
|
50
|
+
node.each_descendant.select do |descendant|
|
51
|
+
descendant.parent.and_type? && !descendant.and_type?
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def defined_calls(nodes)
|
56
|
+
nodes.filter_map do |defined_node|
|
57
|
+
subject = defined_node.first_argument
|
58
|
+
subject if subject.const_type? || subject.call_type?
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def namespaces(nodes)
|
63
|
+
nodes.filter_map do |node|
|
64
|
+
if node.respond_to?(:namespace)
|
65
|
+
node.namespace
|
66
|
+
elsif node.respond_to?(:receiver)
|
67
|
+
node.receiver
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def remove_term(corrector, term)
|
73
|
+
term = term.parent until term.parent.and_type?
|
74
|
+
range = if term == term.parent.children.last
|
75
|
+
rhs_range_to_remove(term)
|
76
|
+
else
|
77
|
+
lhs_range_to_remove(term)
|
78
|
+
end
|
79
|
+
|
80
|
+
corrector.remove(range)
|
81
|
+
end
|
82
|
+
|
83
|
+
# If the redundant `defined?` node is the LHS of an `and` node,
|
84
|
+
# the term as well as the subsequent `&&`/`and` operator will be removed.
|
85
|
+
def lhs_range_to_remove(term)
|
86
|
+
source = @processed_source.buffer.source
|
87
|
+
|
88
|
+
pos = term.source_range.end_pos
|
89
|
+
pos += 1 until source[..pos].end_with?(*OPERATORS)
|
90
|
+
|
91
|
+
range_with_surrounding_space(
|
92
|
+
range: term.source_range.with(end_pos: pos + 1),
|
93
|
+
side: :right,
|
94
|
+
newlines: false
|
95
|
+
)
|
96
|
+
end
|
97
|
+
|
98
|
+
# If the redundant `defined?` node is the RHS of an `and` node,
|
99
|
+
# the term as well as the preceding `&&`/`and` operator will be removed.
|
100
|
+
def rhs_range_to_remove(term)
|
101
|
+
source = @processed_source.buffer.source
|
102
|
+
|
103
|
+
pos = term.source_range.begin_pos
|
104
|
+
pos -= 1 until source[pos, 3].start_with?(*OPERATORS)
|
105
|
+
|
106
|
+
range_with_surrounding_space(
|
107
|
+
range: term.source_range.with(begin_pos: pos - 1),
|
108
|
+
side: :right,
|
109
|
+
newlines: false
|
110
|
+
)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -57,6 +57,9 @@ module RuboCop
|
|
57
57
|
|
58
58
|
REGEXP = /(?<keyword>\S+).*#/.freeze
|
59
59
|
|
60
|
+
SUBCLASS_DEFINITION = /\A\s*class\s+\w+\s*<\s*\w+/.freeze
|
61
|
+
METHOD_DEFINITION = /\A\s*def\s/.freeze
|
62
|
+
|
60
63
|
def on_new_investigation
|
61
64
|
processed_source.comments.each do |comment|
|
62
65
|
next unless offensive?(comment) && (match = source_line(comment).match(REGEXP))
|
@@ -93,7 +96,14 @@ module RuboCop
|
|
93
96
|
end
|
94
97
|
|
95
98
|
def rbs_inline_annotation?(line, comment)
|
96
|
-
|
99
|
+
case line
|
100
|
+
when SUBCLASS_DEFINITION
|
101
|
+
comment.text.start_with?(/#\[.+\]/)
|
102
|
+
when METHOD_DEFINITION
|
103
|
+
comment.text.start_with?('#:')
|
104
|
+
else
|
105
|
+
false
|
106
|
+
end
|
97
107
|
end
|
98
108
|
end
|
99
109
|
end
|
@@ -33,24 +33,20 @@ module RuboCop
|
|
33
33
|
branch.begin_type? ? Array(branch).last : branch
|
34
34
|
end
|
35
35
|
|
36
|
-
# rubocop:disable Metrics/AbcSize
|
37
36
|
def lhs(node)
|
38
37
|
case node.type
|
39
38
|
when :send
|
40
39
|
lhs_for_send(node)
|
41
|
-
when :op_asgn
|
42
|
-
"#{node.
|
43
|
-
when :and_asgn, :or_asgn
|
44
|
-
"#{node.children[0].source} #{node.loc.operator.source} "
|
40
|
+
when :op_asgn, :and_asgn, :or_asgn
|
41
|
+
"#{node.assignment_node.source} #{node.operator}= "
|
45
42
|
when :casgn
|
46
43
|
lhs_for_casgn(node)
|
47
44
|
when *ConditionalAssignment::VARIABLE_ASSIGNMENT_TYPES
|
48
|
-
"#{node.
|
45
|
+
"#{node.name} = "
|
49
46
|
else
|
50
47
|
node.source
|
51
48
|
end
|
52
49
|
end
|
53
|
-
# rubocop:enable Metrics/AbcSize
|
54
50
|
|
55
51
|
def indent(cop, source)
|
56
52
|
conf = cop.config.for_cop(END_ALIGNMENT)
|
@@ -94,11 +90,12 @@ module RuboCop
|
|
94
90
|
end
|
95
91
|
|
96
92
|
def lhs_for_casgn(node)
|
97
|
-
|
98
|
-
|
99
|
-
|
93
|
+
if node.namespace.nil?
|
94
|
+
"#{node.name} = "
|
95
|
+
elsif node.namespace.cbase_type?
|
96
|
+
"::#{node.name} = "
|
100
97
|
else
|
101
|
-
"#{namespace.
|
98
|
+
"#{node.namespace.const_name}::#{node.name} = "
|
102
99
|
end
|
103
100
|
end
|
104
101
|
|
@@ -210,7 +207,6 @@ module RuboCop
|
|
210
207
|
class ConditionalAssignment < Base
|
211
208
|
include ConditionalAssignmentHelper
|
212
209
|
include ConfigurableEnforcedStyle
|
213
|
-
include IgnoredNode
|
214
210
|
extend AutoCorrector
|
215
211
|
|
216
212
|
MSG = 'Use the return of the conditional for variable assignment and comparison.'
|
@@ -317,12 +313,14 @@ module RuboCop
|
|
317
313
|
end
|
318
314
|
|
319
315
|
def assignment_node(node)
|
320
|
-
|
316
|
+
assignment = node.send_type? ? node.last_argument : node.expression
|
321
317
|
|
322
318
|
# ignore pseudo-assignments without rhs in for nodes
|
323
319
|
return if node.parent&.for_type?
|
324
320
|
|
325
|
-
|
321
|
+
if assignment.begin_type? && assignment.children.one?
|
322
|
+
assignment = assignment.children.first
|
323
|
+
end
|
326
324
|
|
327
325
|
assignment
|
328
326
|
end
|
@@ -338,7 +336,7 @@ module RuboCop
|
|
338
336
|
end
|
339
337
|
|
340
338
|
def move_assignment_inside_condition(corrector, node)
|
341
|
-
|
339
|
+
condition = node.send_type? ? node.last_argument : node.expression
|
342
340
|
|
343
341
|
if ternary_condition?(condition)
|
344
342
|
TernaryCorrector.move_assignment_inside_condition(corrector, node)
|
@@ -459,7 +457,7 @@ module RuboCop
|
|
459
457
|
end
|
460
458
|
|
461
459
|
def assignment(node)
|
462
|
-
|
460
|
+
condition = node.send_type? ? node.last_argument : node.expression
|
463
461
|
|
464
462
|
node.source_range.begin.join(condition.source_range.begin)
|
465
463
|
end
|
@@ -506,7 +504,7 @@ module RuboCop
|
|
506
504
|
end
|
507
505
|
|
508
506
|
def move_assignment_inside_condition(corrector, node)
|
509
|
-
|
507
|
+
rhs = node.send_type? ? node.last_argument : node.expression
|
510
508
|
if_branch, else_branch = extract_branches(node)
|
511
509
|
assignment = assignment(node)
|
512
510
|
|
@@ -537,8 +535,8 @@ module RuboCop
|
|
537
535
|
end
|
538
536
|
|
539
537
|
def extract_branches(node)
|
540
|
-
|
541
|
-
condition
|
538
|
+
rhs = node.send_type? ? node.last_argument : node.expression
|
539
|
+
condition = rhs.children.first if rhs.begin_type? && rhs.children.one?
|
542
540
|
_condition, if_branch, else_branch = *(condition || rhs)
|
543
541
|
|
544
542
|
[if_branch, else_branch]
|
@@ -567,7 +565,7 @@ module RuboCop
|
|
567
565
|
|
568
566
|
def move_assignment_inside_condition(corrector, node)
|
569
567
|
column = node.source_range.column
|
570
|
-
|
568
|
+
condition = node.send_type? ? node.last_argument : node.expression
|
571
569
|
assignment = assignment(node)
|
572
570
|
|
573
571
|
corrector.remove(assignment)
|
@@ -618,7 +616,7 @@ module RuboCop
|
|
618
616
|
|
619
617
|
def move_assignment_inside_condition(corrector, node)
|
620
618
|
column = node.source_range.column
|
621
|
-
|
619
|
+
condition = node.send_type? ? node.last_argument : node.expression
|
622
620
|
assignment = assignment(node)
|
623
621
|
|
624
622
|
corrector.remove(assignment)
|
@@ -53,8 +53,7 @@ module RuboCop
|
|
53
53
|
return if visibility_declaration?(node)
|
54
54
|
return if ignore_modules? && module?(node)
|
55
55
|
|
56
|
-
message
|
57
|
-
add_offense(node, message: message)
|
56
|
+
add_offense(node, message: format(MSG, constant_name: node.name))
|
58
57
|
end
|
59
58
|
|
60
59
|
private
|
@@ -64,13 +63,7 @@ module RuboCop
|
|
64
63
|
end
|
65
64
|
|
66
65
|
def module?(node)
|
67
|
-
node.
|
68
|
-
end
|
69
|
-
|
70
|
-
def message(node)
|
71
|
-
_namespace, constant_name, _value = *node
|
72
|
-
|
73
|
-
format(MSG, constant_name: constant_name)
|
66
|
+
node.expression.class_constructor?
|
74
67
|
end
|
75
68
|
|
76
69
|
def class_or_module_scope?(node)
|
@@ -85,10 +78,8 @@ module RuboCop
|
|
85
78
|
end
|
86
79
|
|
87
80
|
def visibility_declaration?(node)
|
88
|
-
_namespace, constant_name, _value = *node
|
89
|
-
|
90
81
|
node.parent.each_child_node(:send).any? do |child|
|
91
|
-
visibility_declaration_for?(child,
|
82
|
+
visibility_declaration_for?(child, node.name)
|
92
83
|
end
|
93
84
|
end
|
94
85
|
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Check for chained `dig` calls that can be collapsed into a single `dig`.
|
7
|
+
#
|
8
|
+
# @safety
|
9
|
+
# This cop is unsafe because it cannot be guaranteed that the receiver
|
10
|
+
# is an `Enumerable` or does not have a nonstandard implementation
|
11
|
+
# of `dig`.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# # bad
|
15
|
+
# x.dig(:foo).dig(:bar).dig(:baz)
|
16
|
+
# x.dig(:foo, :bar).dig(:baz)
|
17
|
+
# x.dig(:foo, :bar)&.dig(:baz)
|
18
|
+
#
|
19
|
+
# # good
|
20
|
+
# x.dig(:foo, :bar, :baz)
|
21
|
+
#
|
22
|
+
# # good - `dig`s cannot be combined
|
23
|
+
# x.dig(:foo).bar.dig(:baz)
|
24
|
+
#
|
25
|
+
class DigChain < Base
|
26
|
+
extend AutoCorrector
|
27
|
+
include RangeHelp
|
28
|
+
include CommentsHelp
|
29
|
+
include DigHelp
|
30
|
+
|
31
|
+
MSG = 'Use `%<replacement>s` instead of chaining.'
|
32
|
+
RESTRICT_ON_SEND = %i[dig].freeze
|
33
|
+
|
34
|
+
def on_send(node)
|
35
|
+
return if ignored_node?(node)
|
36
|
+
return unless node.loc.dot
|
37
|
+
return unless dig?(node)
|
38
|
+
|
39
|
+
range, arguments = inspect_chain(node)
|
40
|
+
return if invalid_arguments?(arguments)
|
41
|
+
return unless range
|
42
|
+
|
43
|
+
register_offense(node, range, arguments)
|
44
|
+
end
|
45
|
+
alias on_csend on_send
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# Walk up the method chain while the receiver is `dig` with arguments.
|
50
|
+
def inspect_chain(node)
|
51
|
+
arguments = node.arguments.dup
|
52
|
+
end_pos = node.source_range.end_pos
|
53
|
+
|
54
|
+
while dig?((node = node.receiver))
|
55
|
+
begin_pos = node.loc.dot ? node.loc.dot.begin_pos + 1 : 0
|
56
|
+
arguments.unshift(*node.arguments)
|
57
|
+
ignore_node(node)
|
58
|
+
end
|
59
|
+
|
60
|
+
return unless begin_pos
|
61
|
+
|
62
|
+
[range_between(begin_pos, end_pos), arguments]
|
63
|
+
end
|
64
|
+
|
65
|
+
def invalid_arguments?(arguments)
|
66
|
+
# If any of the arguments are arguments forwarding (`...`), it can only be the
|
67
|
+
# first argument, or else the resulting code will have a syntax error.
|
68
|
+
|
69
|
+
return false unless arguments&.any?
|
70
|
+
|
71
|
+
forwarded_args_index = arguments.index(&:forwarded_args_type?)
|
72
|
+
forwarded_args_index && forwarded_args_index < (arguments.size - 1)
|
73
|
+
end
|
74
|
+
|
75
|
+
def register_offense(node, range, arguments)
|
76
|
+
arguments = arguments.map(&:source).join(', ')
|
77
|
+
replacement = "dig(#{arguments})"
|
78
|
+
|
79
|
+
add_offense(range, message: format(MSG, replacement: replacement)) do |corrector|
|
80
|
+
corrector.replace(range, replacement)
|
81
|
+
|
82
|
+
comments_in_range(node).reverse_each do |comment|
|
83
|
+
corrector.insert_before(node, "#{comment.source}\n")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -48,6 +48,7 @@ module RuboCop
|
|
48
48
|
#
|
49
49
|
class EndlessMethod < Base
|
50
50
|
include ConfigurableEnforcedStyle
|
51
|
+
include EndlessMethodRewriter
|
51
52
|
extend TargetRubyVersion
|
52
53
|
extend AutoCorrector
|
53
54
|
|
@@ -81,20 +82,6 @@ module RuboCop
|
|
81
82
|
|
82
83
|
add_offense(node) { |corrector| correct_to_multiline(corrector, node) }
|
83
84
|
end
|
84
|
-
|
85
|
-
def correct_to_multiline(corrector, node)
|
86
|
-
replacement = <<~RUBY.strip
|
87
|
-
def #{node.method_name}#{arguments(node)}
|
88
|
-
#{node.body.source}
|
89
|
-
end
|
90
|
-
RUBY
|
91
|
-
|
92
|
-
corrector.replace(node, replacement)
|
93
|
-
end
|
94
|
-
|
95
|
-
def arguments(node, missing = '')
|
96
|
-
node.arguments.any? ? node.arguments.source : missing
|
97
|
-
end
|
98
85
|
end
|
99
86
|
end
|
100
87
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Use `File::NULL` instead of hardcoding the null device (`/dev/null` on Unix-like
|
7
|
+
# OSes, `NUL` or `NUL:` on Windows), so that code is platform independent.
|
8
|
+
# Only looks for full string matches, substrings within a longer string are not
|
9
|
+
# considered.
|
10
|
+
#
|
11
|
+
# NOTE: Uses inside arrays and hashes are ignored.
|
12
|
+
#
|
13
|
+
# @safety
|
14
|
+
# It is possible for a string value to be changed if code is being run
|
15
|
+
# on multiple platforms and was previously hardcoded to a specific null device.
|
16
|
+
#
|
17
|
+
# For example, the following string will change on Windows when changed to
|
18
|
+
# `File::NULL`:
|
19
|
+
#
|
20
|
+
# [source,ruby]
|
21
|
+
# ----
|
22
|
+
# path = "/dev/null"
|
23
|
+
# ----
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# # bad
|
27
|
+
# '/dev/null'
|
28
|
+
# 'NUL'
|
29
|
+
# 'NUL:'
|
30
|
+
#
|
31
|
+
# # good
|
32
|
+
# File::NULL
|
33
|
+
#
|
34
|
+
# # ok - inside an array
|
35
|
+
# null_devices = %w[/dev/null nul]
|
36
|
+
#
|
37
|
+
# # ok - inside a hash
|
38
|
+
# { unix: "/dev/null", windows: "nul" }
|
39
|
+
class FileNull < Base
|
40
|
+
extend AutoCorrector
|
41
|
+
|
42
|
+
REGEXP = %r{\A(/dev/null|NUL:?)\z}i.freeze
|
43
|
+
MSG = 'Use `File::NULL` instead of `%<source>s`.'
|
44
|
+
|
45
|
+
def on_str(node)
|
46
|
+
value = node.value
|
47
|
+
|
48
|
+
return if invalid_string?(value)
|
49
|
+
return if acceptable?(node)
|
50
|
+
return unless REGEXP.match?(value)
|
51
|
+
|
52
|
+
add_offense(node, message: format(MSG, source: value)) do |corrector|
|
53
|
+
corrector.replace(node, 'File::NULL')
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def invalid_string?(value)
|
60
|
+
value.empty? || !value.valid_encoding?
|
61
|
+
end
|
62
|
+
|
63
|
+
def acceptable?(node)
|
64
|
+
# Using a hardcoded null device is acceptable when inside an array or
|
65
|
+
# inside a hash to ensure behavior doesn't change.
|
66
|
+
return false unless node.parent
|
67
|
+
|
68
|
+
node.parent.type?(:array, :pair)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|