rubocop 1.75.2 → 1.76.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 +19 -13
- data/config/default.yml +79 -23
- data/config/obsoletion.yml +6 -3
- data/lib/rubocop/config_validator.rb +6 -6
- data/lib/rubocop/cop/autocorrect_logic.rb +18 -10
- data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -1
- data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +50 -6
- data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +1 -0
- data/lib/rubocop/cop/internal_affairs/undefined_config.rb +6 -1
- data/lib/rubocop/cop/layout/class_structure.rb +35 -0
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +6 -2
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/hash_alignment.rb +2 -2
- data/lib/rubocop/cop/layout/leading_comment_space.rb +13 -1
- data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/space_after_semicolon.rb +10 -0
- data/lib/rubocop/cop/layout/space_before_brackets.rb +6 -32
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +5 -1
- data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +3 -0
- data/lib/rubocop/cop/lint/array_literal_in_regexp.rb +2 -3
- data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -1
- data/lib/rubocop/cop/lint/circular_argument_reference.rb +2 -5
- data/lib/rubocop/cop/lint/deprecated_class_methods.rb +1 -1
- data/lib/rubocop/cop/lint/duplicate_methods.rb +84 -2
- data/lib/rubocop/cop/lint/empty_interpolation.rb +3 -1
- data/lib/rubocop/cop/lint/float_comparison.rb +27 -0
- data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
- data/lib/rubocop/cop/lint/literal_as_condition.rb +31 -25
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -1
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +5 -0
- data/lib/rubocop/cop/lint/useless_assignment.rb +2 -0
- data/lib/rubocop/cop/lint/useless_default_value_argument.rb +87 -0
- data/lib/rubocop/cop/lint/useless_or.rb +98 -0
- data/lib/rubocop/cop/lint/useless_rescue.rb +1 -1
- data/lib/rubocop/cop/lint/void.rb +2 -2
- data/lib/rubocop/cop/message_annotator.rb +7 -3
- data/lib/rubocop/cop/metrics/abc_size.rb +1 -1
- data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +15 -14
- data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +2 -0
- data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
- data/lib/rubocop/cop/mixin/trailing_comma.rb +9 -5
- data/lib/rubocop/cop/naming/predicate_method.rb +216 -0
- data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +2 -2
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +32 -10
- data/lib/rubocop/cop/style/arguments_forwarding.rb +8 -5
- data/lib/rubocop/cop/style/class_and_module_children.rb +19 -3
- data/lib/rubocop/cop/style/command_literal.rb +1 -1
- data/lib/rubocop/cop/style/commented_keyword.rb +2 -2
- data/lib/rubocop/cop/style/comparable_between.rb +5 -2
- data/lib/rubocop/cop/style/conditional_assignment.rb +15 -6
- data/lib/rubocop/cop/style/data_inheritance.rb +7 -0
- data/lib/rubocop/cop/style/def_with_parentheses.rb +18 -5
- data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +100 -0
- data/lib/rubocop/cop/style/eval_with_location.rb +3 -3
- data/lib/rubocop/cop/style/hash_fetch_chain.rb +0 -1
- data/lib/rubocop/cop/style/hash_transform_keys.rb +2 -2
- data/lib/rubocop/cop/style/hash_transform_values.rb +2 -2
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +3 -3
- data/lib/rubocop/cop/style/if_unless_modifier.rb +22 -4
- data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +4 -7
- data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +1 -1
- data/lib/rubocop/cop/style/it_block_parameter.rb +33 -14
- data/lib/rubocop/cop/style/lambda_call.rb +7 -2
- data/lib/rubocop/cop/style/map_into_array.rb +3 -1
- data/lib/rubocop/cop/style/map_to_hash.rb +11 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +4 -1
- data/lib/rubocop/cop/style/multiline_if_modifier.rb +2 -0
- data/lib/rubocop/cop/style/percent_q_literals.rb +1 -1
- data/lib/rubocop/cop/style/redundant_array_flatten.rb +48 -0
- data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +1 -1
- data/lib/rubocop/cop/style/redundant_format.rb +6 -1
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +0 -3
- data/lib/rubocop/cop/style/redundant_parentheses.rb +31 -3
- data/lib/rubocop/cop/style/regexp_literal.rb +1 -1
- data/lib/rubocop/cop/style/safe_navigation.rb +28 -10
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +4 -2
- data/lib/rubocop/cop/style/string_concatenation.rb +1 -2
- data/lib/rubocop/cop/style/struct_inheritance.rb +8 -1
- data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +7 -1
- data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +1 -1
- data/lib/rubocop/cop/style/trailing_comma_in_hash_literal.rb +1 -1
- data/lib/rubocop/cop/team.rb +1 -1
- data/lib/rubocop/cop/variable_force/assignment.rb +7 -3
- data/lib/rubocop/cops_documentation_generator.rb +6 -2
- data/lib/rubocop/formatter/disabled_config_formatter.rb +2 -1
- data/lib/rubocop/formatter/html_formatter.rb +1 -1
- data/lib/rubocop/formatter/pacman_formatter.rb +1 -1
- data/lib/rubocop/rspec/cop_helper.rb +2 -2
- data/lib/rubocop/rspec/shared_contexts.rb +1 -2
- data/lib/rubocop/target_finder.rb +5 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +6 -1
- metadata +12 -7
|
@@ -22,6 +22,11 @@ module RuboCop
|
|
|
22
22
|
# * Private attribute macros (`attr_accessor`, `attr_writer`, `attr_reader`)
|
|
23
23
|
# * Private instance methods
|
|
24
24
|
#
|
|
25
|
+
# NOTE: Simply enabling the cop with `Enabled: true` will not use
|
|
26
|
+
# the example order shown below.
|
|
27
|
+
# To enforce the order of macros like `attr_reader`,
|
|
28
|
+
# you must define both `ExpectedOrder` *and* `Categories`.
|
|
29
|
+
#
|
|
25
30
|
# You can configure the following order:
|
|
26
31
|
#
|
|
27
32
|
# [source,yaml]
|
|
@@ -68,6 +73,36 @@ module RuboCop
|
|
|
68
73
|
# - extend
|
|
69
74
|
# ----
|
|
70
75
|
#
|
|
76
|
+
# If you only set `ExpectedOrder`
|
|
77
|
+
# without defining `Categories`,
|
|
78
|
+
# macros such as `attr_reader` or `has_many`
|
|
79
|
+
# will not be recognized as part of a category, and their order will not be validated.
|
|
80
|
+
# For example, the following will NOT raise any offenses, even if the order is incorrect:
|
|
81
|
+
#
|
|
82
|
+
# [source,yaml]
|
|
83
|
+
# ----
|
|
84
|
+
# Layout/ClassStructure:
|
|
85
|
+
# Enabled: true
|
|
86
|
+
# ExpectedOrder:
|
|
87
|
+
# - public_attribute_macros
|
|
88
|
+
# - initializer
|
|
89
|
+
# ----
|
|
90
|
+
#
|
|
91
|
+
# To make it work as expected, you must also specify `Categories` like this:
|
|
92
|
+
#
|
|
93
|
+
# [source,yaml]
|
|
94
|
+
# ----
|
|
95
|
+
# Layout/ClassStructure:
|
|
96
|
+
# ExpectedOrder:
|
|
97
|
+
# - public_attribute_macros
|
|
98
|
+
# - initializer
|
|
99
|
+
# Categories:
|
|
100
|
+
# attribute_macros:
|
|
101
|
+
# - attr_reader
|
|
102
|
+
# - attr_writer
|
|
103
|
+
# - attr_accessor
|
|
104
|
+
# ----
|
|
105
|
+
#
|
|
71
106
|
# @safety
|
|
72
107
|
# Autocorrection is unsafe because class methods and module inclusion
|
|
73
108
|
# can behave differently, based on which methods or constants have
|
|
@@ -116,7 +116,7 @@ module RuboCop
|
|
|
116
116
|
def allowed_only_before_style?(node)
|
|
117
117
|
if node.special_modifier?
|
|
118
118
|
return true if processed_source[node.last_line] == 'end'
|
|
119
|
-
return false if
|
|
119
|
+
return false if next_line_empty_and_exists?(node.last_line)
|
|
120
120
|
end
|
|
121
121
|
|
|
122
122
|
previous_line_empty?(node.first_line)
|
|
@@ -129,7 +129,7 @@ module RuboCop
|
|
|
129
129
|
when :around
|
|
130
130
|
corrector.insert_after(line, "\n") unless next_line_empty?(node.last_line)
|
|
131
131
|
when :only_before
|
|
132
|
-
if
|
|
132
|
+
if next_line_empty_and_exists?(node.last_line)
|
|
133
133
|
range = next_empty_line_range(node)
|
|
134
134
|
|
|
135
135
|
corrector.remove(range)
|
|
@@ -154,6 +154,10 @@ module RuboCop
|
|
|
154
154
|
body_end?(last_send_line) || next_line.blank?
|
|
155
155
|
end
|
|
156
156
|
|
|
157
|
+
def next_line_empty_and_exists?(last_send_line)
|
|
158
|
+
next_line_empty?(last_send_line) && last_send_line.next != processed_source.lines.size
|
|
159
|
+
end
|
|
160
|
+
|
|
157
161
|
def empty_lines_around?(node)
|
|
158
162
|
previous_line_empty?(node.first_line) && next_line_empty?(node.last_line)
|
|
159
163
|
end
|
|
@@ -155,7 +155,7 @@ module RuboCop
|
|
|
155
155
|
def on_send(node)
|
|
156
156
|
return unless should_check?(node)
|
|
157
157
|
return if same_line?(node, node.first_argument)
|
|
158
|
-
return if
|
|
158
|
+
return if enforce_first_argument_with_fixed_indentation? &&
|
|
159
159
|
!enable_layout_first_method_argument_line_break?
|
|
160
160
|
|
|
161
161
|
indent = base_indentation(node) + configured_indentation_width
|
|
@@ -19,7 +19,7 @@ module RuboCop
|
|
|
19
19
|
# * ignore_implicit (without curly braces)
|
|
20
20
|
#
|
|
21
21
|
# Alternatively you can specify multiple allowed styles. That's done by
|
|
22
|
-
# passing a list of styles to
|
|
22
|
+
# passing a list of styles to EnforcedHashRocketStyle and EnforcedColonStyle.
|
|
23
23
|
#
|
|
24
24
|
# @example EnforcedHashRocketStyle: key (default)
|
|
25
25
|
# # bad
|
|
@@ -250,7 +250,7 @@ module RuboCop
|
|
|
250
250
|
reset!
|
|
251
251
|
|
|
252
252
|
alignment_for(first_pair).each do |alignment|
|
|
253
|
-
delta = alignment.deltas_for_first_pair(first_pair
|
|
253
|
+
delta = alignment.deltas_for_first_pair(first_pair)
|
|
254
254
|
check_delta delta, node: first_pair, alignment: alignment
|
|
255
255
|
end
|
|
256
256
|
|
|
@@ -58,6 +58,12 @@ module RuboCop
|
|
|
58
58
|
# attr_reader :name #: String
|
|
59
59
|
# attr_reader :age #: Integer?
|
|
60
60
|
#
|
|
61
|
+
# #: (
|
|
62
|
+
# #| Integer,
|
|
63
|
+
# #| String
|
|
64
|
+
# #| ) -> void
|
|
65
|
+
# def foo; end
|
|
66
|
+
#
|
|
61
67
|
# @example AllowRBSInlineAnnotation: true
|
|
62
68
|
#
|
|
63
69
|
# # good
|
|
@@ -67,6 +73,12 @@ module RuboCop
|
|
|
67
73
|
# attr_reader :name #: String
|
|
68
74
|
# attr_reader :age #: Integer?
|
|
69
75
|
#
|
|
76
|
+
# #: (
|
|
77
|
+
# #| Integer,
|
|
78
|
+
# #| String
|
|
79
|
+
# #| ) -> void
|
|
80
|
+
# def foo; end
|
|
81
|
+
#
|
|
70
82
|
# @example AllowSteepAnnotation: false (default)
|
|
71
83
|
#
|
|
72
84
|
# # bad
|
|
@@ -175,7 +187,7 @@ module RuboCop
|
|
|
175
187
|
end
|
|
176
188
|
|
|
177
189
|
def rbs_inline_annotation?(comment)
|
|
178
|
-
allow_rbs_inline_annotation? && comment.text.start_with?(/#:|#\[.+\]
|
|
190
|
+
allow_rbs_inline_annotation? && comment.text.start_with?(/#:|#\[.+\]|#\|/)
|
|
179
191
|
end
|
|
180
192
|
|
|
181
193
|
def allow_steep_annotation?
|
|
@@ -23,6 +23,16 @@ module RuboCop
|
|
|
23
23
|
def kind(token)
|
|
24
24
|
'semicolon' if token.semicolon?
|
|
25
25
|
end
|
|
26
|
+
|
|
27
|
+
def space_missing?(token1, token2)
|
|
28
|
+
super && !semicolon_sequence?(token1, token2)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def semicolon_sequence?(token, next_token)
|
|
34
|
+
token.semicolon? && next_token.semicolon?
|
|
35
|
+
end
|
|
26
36
|
end
|
|
27
37
|
end
|
|
28
38
|
end
|
|
@@ -22,51 +22,25 @@ module RuboCop
|
|
|
22
22
|
RESTRICT_ON_SEND = %i[[] []=].freeze
|
|
23
23
|
|
|
24
24
|
def on_send(node)
|
|
25
|
-
return unless (first_argument = node.first_argument)
|
|
26
|
-
|
|
27
|
-
begin_pos = first_argument.source_range.begin_pos
|
|
28
|
-
return unless (range = offense_range(node, begin_pos))
|
|
29
|
-
|
|
30
|
-
register_offense(range)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
private
|
|
34
|
-
|
|
35
|
-
def offense_range(node, begin_pos)
|
|
36
25
|
receiver_end_pos = node.receiver.source_range.end_pos
|
|
37
26
|
selector_begin_pos = node.loc.selector.begin_pos
|
|
38
27
|
return if receiver_end_pos >= selector_begin_pos
|
|
39
28
|
return if dot_before_brackets?(node, receiver_end_pos, selector_begin_pos)
|
|
40
29
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
30
|
+
range = range_between(receiver_end_pos, selector_begin_pos)
|
|
31
|
+
|
|
32
|
+
add_offense(range) do |corrector|
|
|
33
|
+
corrector.remove(range)
|
|
45
34
|
end
|
|
46
35
|
end
|
|
47
36
|
|
|
37
|
+
private
|
|
38
|
+
|
|
48
39
|
def dot_before_brackets?(node, receiver_end_pos, selector_begin_pos)
|
|
49
40
|
return false unless node.loc.respond_to?(:dot) && (dot = node.loc.dot)
|
|
50
41
|
|
|
51
42
|
dot.begin_pos == receiver_end_pos && dot.end_pos == selector_begin_pos
|
|
52
43
|
end
|
|
53
|
-
|
|
54
|
-
def offense_range_for_assignment(node, begin_pos)
|
|
55
|
-
end_pos = node.receiver.source_range.end_pos
|
|
56
|
-
|
|
57
|
-
return if begin_pos - end_pos == 1 ||
|
|
58
|
-
(range = range_between(end_pos, begin_pos - 1)).source.start_with?('[')
|
|
59
|
-
|
|
60
|
-
range
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def register_offense(range)
|
|
64
|
-
add_offense(range) { |corrector| corrector.remove(range) }
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def reference_variable_with_brackets?(node)
|
|
68
|
-
node.receiver&.variable? && node.method?(:[]) && node.arguments.size == 1
|
|
69
|
-
end
|
|
70
44
|
end
|
|
71
45
|
end
|
|
72
46
|
end
|
|
@@ -6,6 +6,8 @@ module RuboCop
|
|
|
6
6
|
# Checks that brackets used for array literals have or don't have
|
|
7
7
|
# surrounding space depending on configuration.
|
|
8
8
|
#
|
|
9
|
+
# Array pattern matching is handled in the same way.
|
|
10
|
+
#
|
|
9
11
|
# @example EnforcedStyle: no_space (default)
|
|
10
12
|
# # The `no_space` style enforces that array literals have
|
|
11
13
|
# # no surrounding space.
|
|
@@ -82,9 +84,10 @@ module RuboCop
|
|
|
82
84
|
EMPTY_MSG = '%<command>s space inside empty array brackets.'
|
|
83
85
|
|
|
84
86
|
def on_array(node)
|
|
85
|
-
return
|
|
87
|
+
return if node.array_type? && !node.square_brackets?
|
|
86
88
|
|
|
87
89
|
tokens, left, right = array_brackets(node)
|
|
90
|
+
return unless left && right
|
|
88
91
|
|
|
89
92
|
if empty_brackets?(left, right, tokens: tokens)
|
|
90
93
|
return empty_offenses(node, left, right, EMPTY_MSG)
|
|
@@ -95,6 +98,7 @@ module RuboCop
|
|
|
95
98
|
|
|
96
99
|
issue_offenses(node, left, right, start_ok, end_ok)
|
|
97
100
|
end
|
|
101
|
+
alias on_array_pattern on_array
|
|
98
102
|
|
|
99
103
|
private
|
|
100
104
|
|
|
@@ -6,6 +6,8 @@ module RuboCop
|
|
|
6
6
|
# Checks that braces used for hash literals have or don't have
|
|
7
7
|
# surrounding space depending on configuration.
|
|
8
8
|
#
|
|
9
|
+
# Hash pattern matching is handled in the same way.
|
|
10
|
+
#
|
|
9
11
|
# @example EnforcedStyle: space (default)
|
|
10
12
|
# # The `space` style enforces that hash literals have
|
|
11
13
|
# # surrounding space.
|
|
@@ -87,6 +89,7 @@ module RuboCop
|
|
|
87
89
|
check(tokens[-2], tokens[-1]) if tokens.size > 2
|
|
88
90
|
check_whitespace_only_hash(node) if enforce_no_space_style_for_empty_braces?
|
|
89
91
|
end
|
|
92
|
+
alias on_hash_pattern on_hash
|
|
90
93
|
|
|
91
94
|
private
|
|
92
95
|
|
|
@@ -51,10 +51,9 @@ module RuboCop
|
|
|
51
51
|
'in a regexp.'
|
|
52
52
|
|
|
53
53
|
def on_interpolation(begin_node)
|
|
54
|
-
final_node = begin_node.children.last
|
|
55
|
-
|
|
56
|
-
return unless begin_node.parent.regexp_type?
|
|
54
|
+
return unless (final_node = begin_node.children.last)
|
|
57
55
|
return unless final_node.array_type?
|
|
56
|
+
return unless begin_node.parent.regexp_type?
|
|
58
57
|
|
|
59
58
|
if array_of_literal_values?(final_node)
|
|
60
59
|
register_array_of_literal_values(begin_node, final_node)
|
|
@@ -48,7 +48,7 @@ module RuboCop
|
|
|
48
48
|
def autocorrect(corrector, node)
|
|
49
49
|
boolean_literal = node.source.delete(':')
|
|
50
50
|
parent = node.parent
|
|
51
|
-
if parent&.pair_type? && node.equal?(parent.children[0])
|
|
51
|
+
if parent&.pair_type? && parent.colon? && node.equal?(parent.children[0])
|
|
52
52
|
corrector.remove(parent.loc.operator)
|
|
53
53
|
boolean_literal = "#{node.source} =>"
|
|
54
54
|
end
|
|
@@ -6,9 +6,8 @@ module RuboCop
|
|
|
6
6
|
# Checks for circular argument references in optional keyword
|
|
7
7
|
# arguments and optional ordinal arguments.
|
|
8
8
|
#
|
|
9
|
-
# This
|
|
10
|
-
#
|
|
11
|
-
# NOTE: This syntax is no longer valid on Ruby 2.7 or higher.
|
|
9
|
+
# NOTE: This syntax was made invalid on Ruby 2.7 - Ruby 3.3 but is allowed
|
|
10
|
+
# again since Ruby 3.4.
|
|
12
11
|
#
|
|
13
12
|
# @example
|
|
14
13
|
#
|
|
@@ -41,8 +40,6 @@ module RuboCop
|
|
|
41
40
|
|
|
42
41
|
MSG = 'Circular argument reference - `%<arg_name>s`.'
|
|
43
42
|
|
|
44
|
-
maximum_target_ruby_version 2.6
|
|
45
|
-
|
|
46
43
|
def on_kwoptarg(node)
|
|
47
44
|
check_for_circular_argument_references(*node)
|
|
48
45
|
end
|
|
@@ -39,9 +39,52 @@ module RuboCop
|
|
|
39
39
|
# end
|
|
40
40
|
#
|
|
41
41
|
# alias bar foo
|
|
42
|
+
#
|
|
43
|
+
# @example AllCops:ActiveSupportExtensionsEnabled: false (default)
|
|
44
|
+
#
|
|
45
|
+
# # good
|
|
46
|
+
# def foo
|
|
47
|
+
# 1
|
|
48
|
+
# end
|
|
49
|
+
#
|
|
50
|
+
# delegate :foo, to: :bar
|
|
51
|
+
#
|
|
52
|
+
# @example AllCops:ActiveSupportExtensionsEnabled: true
|
|
53
|
+
#
|
|
54
|
+
# # bad
|
|
55
|
+
# def foo
|
|
56
|
+
# 1
|
|
57
|
+
# end
|
|
58
|
+
#
|
|
59
|
+
# delegate :foo, to: :bar
|
|
60
|
+
#
|
|
61
|
+
# # good
|
|
62
|
+
# def foo
|
|
63
|
+
# 1
|
|
64
|
+
# end
|
|
65
|
+
#
|
|
66
|
+
# delegate :baz, to: :bar
|
|
67
|
+
#
|
|
68
|
+
# # good - delegate with splat arguments is ignored
|
|
69
|
+
# def foo
|
|
70
|
+
# 1
|
|
71
|
+
# end
|
|
72
|
+
#
|
|
73
|
+
# delegate :foo, **options
|
|
74
|
+
#
|
|
75
|
+
# # good - delegate inside a condition is ignored
|
|
76
|
+
# def foo
|
|
77
|
+
# 1
|
|
78
|
+
# end
|
|
79
|
+
#
|
|
80
|
+
# if cond
|
|
81
|
+
# delegate :foo, to: :bar
|
|
82
|
+
# end
|
|
83
|
+
#
|
|
42
84
|
class DuplicateMethods < Base
|
|
43
85
|
MSG = 'Method `%<method>s` is defined at both %<defined>s and %<current>s.'
|
|
44
|
-
RESTRICT_ON_SEND = %i[alias_method attr_reader attr_writer attr_accessor attr
|
|
86
|
+
RESTRICT_ON_SEND = %i[alias_method attr_reader attr_writer attr_accessor attr
|
|
87
|
+
delegate].freeze
|
|
45
88
|
|
|
46
89
|
def initialize(config = nil, options = nil)
|
|
47
90
|
super
|
|
@@ -85,15 +128,28 @@ module RuboCop
|
|
|
85
128
|
(send nil? :alias_method (sym $_name) _)
|
|
86
129
|
PATTERN
|
|
87
130
|
|
|
131
|
+
# @!method delegate_method?(node)
|
|
132
|
+
def_node_matcher :delegate_method?, <<~PATTERN
|
|
133
|
+
(send nil? :delegate
|
|
134
|
+
({sym str} $_)+
|
|
135
|
+
(hash <(pair (sym :to) {sym str}) ...>)
|
|
136
|
+
)
|
|
137
|
+
PATTERN
|
|
138
|
+
|
|
88
139
|
# @!method sym_name(node)
|
|
89
140
|
def_node_matcher :sym_name, '(sym $_name)'
|
|
90
|
-
|
|
141
|
+
|
|
142
|
+
def on_send(node) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
91
143
|
if (name = alias_method?(node))
|
|
92
144
|
return if node.ancestors.any?(&:if_type?)
|
|
93
145
|
|
|
94
146
|
found_instance_method(node, name)
|
|
95
147
|
elsif (attr = node.attribute_accessor?)
|
|
96
148
|
on_attr(node, *attr)
|
|
149
|
+
elsif active_support_extensions_enabled? && (names = delegate_method?(node))
|
|
150
|
+
return if node.ancestors.any?(&:if_type?)
|
|
151
|
+
|
|
152
|
+
on_delegate(node, names)
|
|
97
153
|
end
|
|
98
154
|
end
|
|
99
155
|
|
|
@@ -118,6 +174,32 @@ module RuboCop
|
|
|
118
174
|
current: source_location(node))
|
|
119
175
|
end
|
|
120
176
|
|
|
177
|
+
def on_delegate(node, method_names)
|
|
178
|
+
name_prefix = delegate_prefix(node)
|
|
179
|
+
|
|
180
|
+
method_names.each do |name|
|
|
181
|
+
name = "#{name_prefix}_#{name}" if name_prefix
|
|
182
|
+
|
|
183
|
+
found_instance_method(node, name)
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def delegate_prefix(node)
|
|
188
|
+
kwargs_node = node.last_argument
|
|
189
|
+
|
|
190
|
+
return unless (prefix = hash_value(kwargs_node, :prefix))
|
|
191
|
+
|
|
192
|
+
if prefix.true_type?
|
|
193
|
+
hash_value(kwargs_node, :to).value
|
|
194
|
+
elsif prefix.type?(:sym, :str)
|
|
195
|
+
prefix.value
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def hash_value(node, key)
|
|
200
|
+
node.pairs.find { |pair| pair.key.value == key }&.value
|
|
201
|
+
end
|
|
202
|
+
|
|
121
203
|
def found_instance_method(node, name)
|
|
122
204
|
return found_sclass_method(node, name) unless (scope = node.parent_module_name)
|
|
123
205
|
|
|
@@ -19,7 +19,9 @@ module RuboCop
|
|
|
19
19
|
MSG = 'Empty interpolation detected.'
|
|
20
20
|
|
|
21
21
|
def on_interpolation(begin_node)
|
|
22
|
-
|
|
22
|
+
node_children = begin_node.children.dup
|
|
23
|
+
node_children.delete_if { |e| e&.nil_type? || (e&.basic_literal? && e&.value&.empty?) }
|
|
24
|
+
return unless node_children.empty?
|
|
23
25
|
|
|
24
26
|
add_offense(begin_node) { |corrector| corrector.remove(begin_node) }
|
|
25
27
|
end
|
|
@@ -15,6 +15,14 @@ module RuboCop
|
|
|
15
15
|
# x == 0.1
|
|
16
16
|
# x != 0.1
|
|
17
17
|
#
|
|
18
|
+
# # bad
|
|
19
|
+
# case value
|
|
20
|
+
# when 1.0
|
|
21
|
+
# foo
|
|
22
|
+
# when 2.0
|
|
23
|
+
# bar
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
18
26
|
# # good - using BigDecimal
|
|
19
27
|
# x.to_d == 0.1.to_d
|
|
20
28
|
#
|
|
@@ -32,12 +40,21 @@ module RuboCop
|
|
|
32
40
|
# # good - comparing against nil
|
|
33
41
|
# Float(x, exception: false) == nil
|
|
34
42
|
#
|
|
43
|
+
# # good - using epsilon comparison in case expression
|
|
44
|
+
# case
|
|
45
|
+
# when (value - 1.0).abs < Float::EPSILON
|
|
46
|
+
# foo
|
|
47
|
+
# when (value - 2.0).abs < Float::EPSILON
|
|
48
|
+
# bar
|
|
49
|
+
# end
|
|
50
|
+
#
|
|
35
51
|
# # Or some other epsilon based type of comparison:
|
|
36
52
|
# # https://www.embeddeduse.com/2019/08/26/qt-compare-two-floats/
|
|
37
53
|
#
|
|
38
54
|
class FloatComparison < Base
|
|
39
55
|
MSG_EQUALITY = 'Avoid equality comparisons of floats as they are unreliable.'
|
|
40
56
|
MSG_INEQUALITY = 'Avoid inequality comparisons of floats as they are unreliable.'
|
|
57
|
+
MSG_CASE = 'Avoid float literal comparisons in case statements as they are unreliable.'
|
|
41
58
|
|
|
42
59
|
EQUALITY_METHODS = %i[== != eql? equal?].freeze
|
|
43
60
|
FLOAT_RETURNING_METHODS = %i[to_f Float fdiv].freeze
|
|
@@ -58,6 +75,16 @@ module RuboCop
|
|
|
58
75
|
end
|
|
59
76
|
alias on_csend on_send
|
|
60
77
|
|
|
78
|
+
def on_case(node)
|
|
79
|
+
node.when_branches.each do |when_branch|
|
|
80
|
+
when_branch.each_condition do |condition|
|
|
81
|
+
next if !float?(condition) || literal_safe?(condition)
|
|
82
|
+
|
|
83
|
+
add_offense(condition, message: MSG_CASE)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
61
88
|
private
|
|
62
89
|
|
|
63
90
|
def float?(node)
|
|
@@ -11,39 +11,43 @@ module RuboCop
|
|
|
11
11
|
# @example
|
|
12
12
|
# # bad
|
|
13
13
|
# foo.object_id == bar.object_id
|
|
14
|
+
# foo.object_id != baz.object_id
|
|
14
15
|
#
|
|
15
16
|
# # good
|
|
16
17
|
# foo.equal?(bar)
|
|
18
|
+
# !foo.equal?(baz)
|
|
17
19
|
#
|
|
18
20
|
class IdentityComparison < Base
|
|
19
21
|
extend AutoCorrector
|
|
20
22
|
|
|
21
|
-
MSG = 'Use
|
|
22
|
-
RESTRICT_ON_SEND = %i[==].freeze
|
|
23
|
+
MSG = 'Use `%<bang>sequal?` instead of `%<comparison_method>s` when comparing `object_id`.'
|
|
24
|
+
RESTRICT_ON_SEND = %i[== !=].freeze
|
|
25
|
+
|
|
26
|
+
# @!method object_id_comparison(node)
|
|
27
|
+
def_node_matcher :object_id_comparison, <<~PATTERN
|
|
28
|
+
(send
|
|
29
|
+
(send
|
|
30
|
+
_lhs_receiver :object_id) ${:== :!=}
|
|
31
|
+
(send
|
|
32
|
+
_rhs_receiver :object_id))
|
|
33
|
+
PATTERN
|
|
23
34
|
|
|
24
35
|
def on_send(node)
|
|
25
|
-
return unless
|
|
36
|
+
return unless (comparison_method = object_id_comparison(node))
|
|
26
37
|
|
|
27
|
-
|
|
38
|
+
bang = comparison_method == :== ? '' : '!'
|
|
39
|
+
add_offense(node,
|
|
40
|
+
message: format(MSG, comparison_method: comparison_method,
|
|
41
|
+
bang: bang)) do |corrector|
|
|
28
42
|
receiver = node.receiver.receiver
|
|
29
43
|
argument = node.first_argument.receiver
|
|
30
44
|
return unless receiver && argument
|
|
31
45
|
|
|
32
|
-
replacement = "#{receiver.source}.equal?(#{argument.source})"
|
|
46
|
+
replacement = "#{bang}#{receiver.source}.equal?(#{argument.source})"
|
|
33
47
|
|
|
34
48
|
corrector.replace(node, replacement)
|
|
35
49
|
end
|
|
36
50
|
end
|
|
37
|
-
|
|
38
|
-
private
|
|
39
|
-
|
|
40
|
-
def compare_between_object_id_by_double_equal?(node)
|
|
41
|
-
object_id_method?(node.receiver) && object_id_method?(node.first_argument)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def object_id_method?(node)
|
|
45
|
-
node.send_type? && node.method?(:object_id)
|
|
46
|
-
end
|
|
47
51
|
end
|
|
48
52
|
end
|
|
49
53
|
end
|
|
@@ -57,13 +57,9 @@ module RuboCop
|
|
|
57
57
|
def on_if(node)
|
|
58
58
|
cond = condition(node)
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
else
|
|
64
|
-
correct_if_node(node, cond, true) if cond.truthy_literal?
|
|
65
|
-
correct_if_node(node, cond, false) if cond.falsey_literal?
|
|
66
|
-
end
|
|
60
|
+
return unless cond.falsey_literal? || cond.truthy_literal?
|
|
61
|
+
|
|
62
|
+
correct_if_node(node, cond)
|
|
67
63
|
end
|
|
68
64
|
|
|
69
65
|
def on_while(node)
|
|
@@ -232,27 +228,37 @@ module RuboCop
|
|
|
232
228
|
)
|
|
233
229
|
end
|
|
234
230
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
add_offense(cond) do |corrector|
|
|
239
|
-
corrector.replace(node, node.if_branch.source)
|
|
240
|
-
end
|
|
241
|
-
elsif node.elsif_conditional?
|
|
242
|
-
add_offense(cond) do |corrector|
|
|
243
|
-
corrector.replace(node, "#{node.else_branch.source.sub('elsif', 'if')}\nend")
|
|
244
|
-
end
|
|
245
|
-
elsif node.else? || node.ternary?
|
|
246
|
-
add_offense(cond) do |corrector|
|
|
247
|
-
corrector.replace(node, node.else_branch.source)
|
|
248
|
-
end
|
|
231
|
+
def condition_evaluation(node, cond)
|
|
232
|
+
if node.unless?
|
|
233
|
+
cond.falsey_literal?
|
|
249
234
|
else
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
235
|
+
cond.truthy_literal?
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
240
|
+
def correct_if_node(node, cond)
|
|
241
|
+
result = condition_evaluation(node, cond)
|
|
242
|
+
|
|
243
|
+
new_node = if node.elsif? && result
|
|
244
|
+
"else\n #{range_with_comments(node.if_branch).source}"
|
|
245
|
+
elsif node.elsif? && !result
|
|
246
|
+
"else\n #{node.else_branch.source}"
|
|
247
|
+
elsif node.if_branch && result
|
|
248
|
+
node.if_branch.source
|
|
249
|
+
elsif node.elsif_conditional?
|
|
250
|
+
"#{node.else_branch.source.sub('elsif', 'if')}\nend"
|
|
251
|
+
elsif node.else? || node.ternary?
|
|
252
|
+
node.else_branch.source
|
|
253
|
+
else
|
|
254
|
+
'' # Equivalent to removing the node
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
add_offense(cond) do |corrector|
|
|
258
|
+
corrector.replace(node, new_node)
|
|
253
259
|
end
|
|
254
260
|
end
|
|
255
|
-
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
|
261
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
256
262
|
end
|
|
257
263
|
end
|
|
258
264
|
end
|
|
@@ -52,7 +52,7 @@ module RuboCop
|
|
|
52
52
|
each_missing_enable do |cop, line_range|
|
|
53
53
|
next if acceptable_range?(cop, line_range)
|
|
54
54
|
|
|
55
|
-
range = source_range(processed_source.buffer, line_range.min,
|
|
55
|
+
range = source_range(processed_source.buffer, line_range.min, 0..0)
|
|
56
56
|
comment = processed_source.comment_at_line(line_range.begin)
|
|
57
57
|
|
|
58
58
|
add_offense(range, message: message(cop, comment))
|
|
@@ -8,6 +8,11 @@ module RuboCop
|
|
|
8
8
|
# given by `ruby -cw` prior to Ruby 2.6:
|
|
9
9
|
# "shadowing outer local variable - foo".
|
|
10
10
|
#
|
|
11
|
+
# The cop is now disabled by default to match the upstream Ruby behavior.
|
|
12
|
+
# It's useful, however, if you'd like to avoid shadowing variables from outer
|
|
13
|
+
# scopes, which some people consider an anti-pattern that makes it harder
|
|
14
|
+
# to keep track of what's going on in a program.
|
|
15
|
+
#
|
|
11
16
|
# NOTE: Shadowing of variables in block passed to `Ractor.new` is allowed
|
|
12
17
|
# because `Ractor` should not access outer variables.
|
|
13
18
|
# eg. following style is encouraged:
|