rubocop 1.75.5 → 1.75.8
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 +2 -0
- data/lib/rubocop/cop/autocorrect_logic.rb +18 -10
- data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +49 -5
- data/lib/rubocop/cop/layout/class_structure.rb +35 -0
- 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/space_before_brackets.rb +6 -32
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +1 -0
- 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/float_comparison.rb +27 -0
- data/lib/rubocop/cop/lint/useless_assignment.rb +2 -0
- data/lib/rubocop/cop/metrics/abc_size.rb +1 -1
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +32 -10
- data/lib/rubocop/cop/style/command_literal.rb +1 -1
- data/lib/rubocop/cop/style/comparable_between.rb +3 -0
- 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/if_unless_modifier.rb +20 -0
- data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +4 -7
- data/lib/rubocop/cop/style/map_to_hash.rb +11 -0
- 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_format.rb +6 -1
- data/lib/rubocop/cop/style/regexp_literal.rb +1 -1
- 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/team.rb +1 -1
- data/lib/rubocop/cop/variable_force/assignment.rb +7 -3
- data/lib/rubocop/formatter/disabled_config_formatter.rb +1 -0
- data/lib/rubocop/formatter/html_formatter.rb +1 -1
- data/lib/rubocop/version.rb +1 -1
- metadata +4 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 31a9b16f521cb4dd8d22db58d5473cd527162b65934d8e210993f7c435a85d26
|
4
|
+
data.tar.gz: 57af5692e8b85aca41224e621a0ba6b54001944a6625edef169a78278066a532
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ef0ab376b3993e9ad1961560911ed5b5e2abf069a2b9acd0187e3d31c03f853a520646f2c421813bc3c03aedd74004dcf057cbadecad0ea86acafbd6670c8e46
|
7
|
+
data.tar.gz: b76bd8b4d7492577429bab8abd02ebcf7f2d9d2f83066e81c48b060abcaad68a2660b064c1f5b0b9c42bc80cc78ce15cd6e10ace393cd636310055515af925db
|
data/config/default.yml
CHANGED
@@ -3702,7 +3702,9 @@ Style/CommentedKeyword:
|
|
3702
3702
|
Style/ComparableBetween:
|
3703
3703
|
Description: 'Enforces the use of `Comparable#between?` instead of logical comparison.'
|
3704
3704
|
Enabled: pending
|
3705
|
+
Safe: false
|
3705
3706
|
VersionAdded: '1.74'
|
3707
|
+
VersionChanged: '1.75'
|
3706
3708
|
StyleGuide: '#ranges-or-between'
|
3707
3709
|
|
3708
3710
|
Style/ComparableClamp:
|
@@ -50,7 +50,7 @@ module RuboCop
|
|
50
50
|
|
51
51
|
def disable_offense(offense_range)
|
52
52
|
unbreakable_range = multiline_ranges(offense_range)&.find do |range|
|
53
|
-
|
53
|
+
eol_comment_would_be_inside_literal?(offense_range, range)
|
54
54
|
end
|
55
55
|
|
56
56
|
if unbreakable_range
|
@@ -75,18 +75,22 @@ module RuboCop
|
|
75
75
|
end
|
76
76
|
|
77
77
|
def disable_offense_with_eol_or_surround_comment(range)
|
78
|
-
|
79
|
-
needed_line_length = (range.source_line + eol_comment).length
|
80
|
-
|
81
|
-
if needed_line_length <= max_line_length
|
82
|
-
disable_offense_at_end_of_line(range_of_first_line(range), eol_comment)
|
83
|
-
else
|
78
|
+
if line_with_eol_comment_too_long?(range)
|
84
79
|
disable_offense_before_and_after(range_by_lines(range))
|
80
|
+
else
|
81
|
+
disable_offense_at_end_of_line(range_of_first_line(range))
|
85
82
|
end
|
86
83
|
end
|
87
84
|
|
88
|
-
def
|
89
|
-
|
85
|
+
def eol_comment_would_be_inside_literal?(offense_range, literal_range)
|
86
|
+
return true if line_with_eol_comment_too_long?(offense_range)
|
87
|
+
|
88
|
+
offense_line = offense_range.line
|
89
|
+
offense_line >= literal_range.first_line && offense_line < literal_range.last_line
|
90
|
+
end
|
91
|
+
|
92
|
+
def line_with_eol_comment_too_long?(range)
|
93
|
+
(range.source_line + eol_comment).length > max_line_length
|
90
94
|
end
|
91
95
|
|
92
96
|
def surrounding_heredoc?(node)
|
@@ -132,10 +136,14 @@ module RuboCop
|
|
132
136
|
config.for_cop('Layout/LineLength')['Max'] || 120
|
133
137
|
end
|
134
138
|
|
135
|
-
def disable_offense_at_end_of_line(range
|
139
|
+
def disable_offense_at_end_of_line(range)
|
136
140
|
Corrector.new(range).insert_after(range, eol_comment)
|
137
141
|
end
|
138
142
|
|
143
|
+
def eol_comment
|
144
|
+
" # rubocop:todo #{cop_name}"
|
145
|
+
end
|
146
|
+
|
139
147
|
def disable_offense_before_and_after(range_by_lines)
|
140
148
|
range_with_newline = range_by_lines.resize(range_by_lines.size + 1)
|
141
149
|
leading_whitespace = range_by_lines.source_line[/^\s*/]
|
@@ -6,10 +6,11 @@ module RuboCop
|
|
6
6
|
# An attribute assignment method calls should be listed only once
|
7
7
|
# in a gemspec.
|
8
8
|
#
|
9
|
-
# Assigning to an attribute with the same name using `spec.foo =`
|
10
|
-
# an unintended usage. On the other hand,
|
11
|
-
# as `spec.requirements`,
|
12
|
-
# permitted because it is
|
9
|
+
# Assigning to an attribute with the same name using `spec.foo =` or
|
10
|
+
# `spec.attribute#[]=` will be an unintended usage. On the other hand,
|
11
|
+
# duplication of methods such # as `spec.requirements`,
|
12
|
+
# `spec.add_runtime_dependency`, and others are permitted because it is
|
13
|
+
# the intended use of appending values.
|
13
14
|
#
|
14
15
|
# @example
|
15
16
|
# # bad
|
@@ -34,6 +35,18 @@ module RuboCop
|
|
34
35
|
# spec.add_dependency('parallel', '~> 1.10')
|
35
36
|
# spec.add_dependency('parser', '>= 2.3.3.1', '< 3.0')
|
36
37
|
# end
|
38
|
+
#
|
39
|
+
# # bad
|
40
|
+
# Gem::Specification.new do |spec|
|
41
|
+
# spec.metadata["key"] = "value"
|
42
|
+
# spec.metadata["key"] = "value"
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# # good
|
46
|
+
# Gem::Specification.new do |spec|
|
47
|
+
# spec.metadata["key"] = "value"
|
48
|
+
# end
|
49
|
+
#
|
37
50
|
class DuplicatedAssignment < Base
|
38
51
|
include RangeHelp
|
39
52
|
include GemspecHelp
|
@@ -47,9 +60,26 @@ module RuboCop
|
|
47
60
|
(lvar #match_block_variable_name?) _ ...)
|
48
61
|
PATTERN
|
49
62
|
|
63
|
+
# @!method indexed_assignment_method_declarations(node)
|
64
|
+
def_node_search :indexed_assignment_method_declarations, <<~PATTERN
|
65
|
+
(send
|
66
|
+
(send (lvar #match_block_variable_name?) _)
|
67
|
+
:[]=
|
68
|
+
literal?
|
69
|
+
_
|
70
|
+
)
|
71
|
+
PATTERN
|
72
|
+
|
50
73
|
def on_new_investigation
|
51
74
|
return if processed_source.blank?
|
52
75
|
|
76
|
+
process_assignment_method_nodes
|
77
|
+
process_indexed_assignment_method_nodes
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def process_assignment_method_nodes
|
53
83
|
duplicated_assignment_method_nodes.each do |nodes|
|
54
84
|
nodes[1..].each do |node|
|
55
85
|
register_offense(node, node.method_name, nodes.first.first_line)
|
@@ -57,7 +87,14 @@ module RuboCop
|
|
57
87
|
end
|
58
88
|
end
|
59
89
|
|
60
|
-
|
90
|
+
def process_indexed_assignment_method_nodes
|
91
|
+
duplicated_indexed_assignment_method_nodes.each do |nodes|
|
92
|
+
nodes[1..].each do |node|
|
93
|
+
assignment = "#{node.children.first.method_name}[#{node.first_argument.source}]="
|
94
|
+
register_offense(node, assignment, nodes.first.first_line)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
61
98
|
|
62
99
|
def match_block_variable_name?(receiver_name)
|
63
100
|
gem_specification(processed_source.ast) do |block_variable_name|
|
@@ -73,6 +110,13 @@ module RuboCop
|
|
73
110
|
.select { |nodes| nodes.size > 1 }
|
74
111
|
end
|
75
112
|
|
113
|
+
def duplicated_indexed_assignment_method_nodes
|
114
|
+
indexed_assignment_method_declarations(processed_source.ast)
|
115
|
+
.group_by { |node| [node.children.first.method_name, node.first_argument] }
|
116
|
+
.values
|
117
|
+
.select { |nodes| nodes.size > 1 }
|
118
|
+
end
|
119
|
+
|
76
120
|
def register_offense(node, assignment, line_of_first_occurrence)
|
77
121
|
line_range = node.loc.column...node.loc.last_column
|
78
122
|
offense_location = source_range(processed_source.buffer, node.first_line, line_range)
|
@@ -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
|
@@ -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
|
@@ -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
|
|
@@ -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)
|
@@ -39,7 +39,7 @@ module RuboCop
|
|
39
39
|
class AbcSize < Base
|
40
40
|
include MethodComplexity
|
41
41
|
|
42
|
-
MSG = 'Assignment Branch Condition size for
|
42
|
+
MSG = 'Assignment Branch Condition size for `%<method>s` is too high. ' \
|
43
43
|
'[%<abc_vector>s %<complexity>.4g/%<max>.4g]'
|
44
44
|
|
45
45
|
private
|
@@ -195,15 +195,27 @@ module RuboCop
|
|
195
195
|
def autocorrect(corrector, node)
|
196
196
|
case style
|
197
197
|
when :group
|
198
|
-
|
199
|
-
return unless def_nodes.any?
|
200
|
-
|
201
|
-
replace_defs(corrector, node, def_nodes)
|
198
|
+
autocorrect_group_style(corrector, node)
|
202
199
|
when :inline
|
200
|
+
autocorrect_inline_style(corrector, node)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def autocorrect_group_style(corrector, node)
|
205
|
+
def_nodes = find_corresponding_def_nodes(node)
|
206
|
+
return unless def_nodes.any?
|
207
|
+
|
208
|
+
replace_defs(corrector, node, def_nodes)
|
209
|
+
end
|
210
|
+
|
211
|
+
def autocorrect_inline_style(corrector, node)
|
212
|
+
if node.parent&.begin_type?
|
213
|
+
remove_modifier_node_within_begin(corrector, node, node.parent)
|
214
|
+
else
|
203
215
|
remove_nodes(corrector, node)
|
204
|
-
|
205
|
-
|
206
|
-
|
216
|
+
end
|
217
|
+
select_grouped_def_nodes(node).each do |grouped_def_node|
|
218
|
+
insert_inline_modifier(corrector, grouped_def_node, node.method_name)
|
207
219
|
end
|
208
220
|
end
|
209
221
|
|
@@ -224,9 +236,13 @@ module RuboCop
|
|
224
236
|
end
|
225
237
|
|
226
238
|
def offense?(node)
|
227
|
-
|
228
|
-
|
229
|
-
|
239
|
+
if group_style?
|
240
|
+
return false if node.parent ? node.parent.if_type? : access_modifier_with_symbol?(node)
|
241
|
+
|
242
|
+
access_modifier_is_inlined?(node) && !right_siblings_same_inline_method?(node)
|
243
|
+
else
|
244
|
+
access_modifier_is_not_inlined?(node) && select_grouped_def_nodes(node).any?
|
245
|
+
end
|
230
246
|
end
|
231
247
|
|
232
248
|
def correctable_group_offense?(node)
|
@@ -331,6 +347,12 @@ module RuboCop
|
|
331
347
|
end
|
332
348
|
end
|
333
349
|
|
350
|
+
def remove_modifier_node_within_begin(corrector, modifier_node, begin_node)
|
351
|
+
def_node = begin_node.children[1]
|
352
|
+
range = modifier_node.source_range.begin.join(def_node.source_range.begin)
|
353
|
+
corrector.remove(range)
|
354
|
+
end
|
355
|
+
|
334
356
|
def def_source(node, def_nodes)
|
335
357
|
[
|
336
358
|
*processed_source.ast_with_comments[node].map(&:text),
|
@@ -9,6 +9,9 @@ module RuboCop
|
|
9
9
|
# although the difference generally isn't observable. If you require maximum
|
10
10
|
# performance, consider using logical comparison.
|
11
11
|
#
|
12
|
+
# @safety
|
13
|
+
# This cop is unsafe because the receiver may not respond to `between?`.
|
14
|
+
#
|
12
15
|
# @example
|
13
16
|
#
|
14
17
|
# # bad
|
@@ -4,6 +4,7 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module Style
|
6
6
|
# Checks for inheritance from `Data.define` to avoid creating the anonymous parent class.
|
7
|
+
# Inheriting from `Data.define` adds a superfluous level in inheritance tree.
|
7
8
|
#
|
8
9
|
# @safety
|
9
10
|
# Autocorrection is unsafe because it will change the inheritance
|
@@ -17,12 +18,18 @@ module RuboCop
|
|
17
18
|
# end
|
18
19
|
# end
|
19
20
|
#
|
21
|
+
# Person.ancestors
|
22
|
+
# # => [Person, #<Class:0x000000010b4e14a0>, Data, (...)]
|
23
|
+
#
|
20
24
|
# # good
|
21
25
|
# Person = Data.define(:first_name, :last_name) do
|
22
26
|
# def age
|
23
27
|
# 42
|
24
28
|
# end
|
25
29
|
# end
|
30
|
+
#
|
31
|
+
# Person.ancestors
|
32
|
+
# # => [Person, Data, (...)]
|
26
33
|
class DataInheritance < Base
|
27
34
|
include RangeHelp
|
28
35
|
extend AutoCorrector
|
@@ -25,8 +25,9 @@ module RuboCop
|
|
25
25
|
# # good
|
26
26
|
# def foo = do_something
|
27
27
|
#
|
28
|
-
# # good
|
28
|
+
# # good - without parentheses it's a syntax error
|
29
29
|
# def foo() do_something end
|
30
|
+
# def foo()=do_something
|
30
31
|
#
|
31
32
|
# # bad
|
32
33
|
# def Baz.foo()
|
@@ -38,19 +39,31 @@ module RuboCop
|
|
38
39
|
# do_something
|
39
40
|
# end
|
40
41
|
class DefWithParentheses < Base
|
42
|
+
include RangeHelp
|
41
43
|
extend AutoCorrector
|
42
44
|
|
43
45
|
MSG = "Omit the parentheses in defs when the method doesn't accept any arguments."
|
44
46
|
|
45
47
|
def on_def(node)
|
46
|
-
return
|
47
|
-
return
|
48
|
+
return unless !node.arguments? && (arguments_range = node.arguments.source_range)
|
49
|
+
return if parentheses_required?(node, arguments_range)
|
48
50
|
|
49
|
-
add_offense(
|
50
|
-
corrector.remove(
|
51
|
+
add_offense(arguments_range) do |corrector|
|
52
|
+
corrector.remove(arguments_range)
|
51
53
|
end
|
52
54
|
end
|
53
55
|
alias on_defs on_def
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def parentheses_required?(node, arguments_range)
|
60
|
+
return true if node.single_line? && !node.endless?
|
61
|
+
|
62
|
+
end_pos = arguments_range.end.end_pos
|
63
|
+
token_after_argument = range_between(end_pos, end_pos + 1).source
|
64
|
+
|
65
|
+
token_after_argument == '='
|
66
|
+
end
|
54
67
|
end
|
55
68
|
end
|
56
69
|
end
|
@@ -23,6 +23,17 @@ module RuboCop
|
|
23
23
|
# end
|
24
24
|
# ----
|
25
25
|
#
|
26
|
+
# The code `def method_name = body if condition` is considered a bad case by
|
27
|
+
# `Style/AmbiguousEndlessMethodDefinition` cop. So, to respect the user's intention to use
|
28
|
+
# an endless method definition in the `if` body, the following code is allowed:
|
29
|
+
#
|
30
|
+
# [source,ruby]
|
31
|
+
# ----
|
32
|
+
# if condition
|
33
|
+
# def method_name = body
|
34
|
+
# end
|
35
|
+
# ----
|
36
|
+
#
|
26
37
|
# NOTE: It is allowed when `defined?` argument has an undefined value,
|
27
38
|
# because using the modifier form causes the following incompatibility:
|
28
39
|
#
|
@@ -77,10 +88,14 @@ module RuboCop
|
|
77
88
|
[Style::SoleNestedConditional]
|
78
89
|
end
|
79
90
|
|
91
|
+
# rubocop:disable Metrics/AbcSize
|
80
92
|
def on_if(node)
|
93
|
+
return if endless_method?(node.body)
|
94
|
+
|
81
95
|
condition = node.condition
|
82
96
|
return if defined_nodes(condition).any? { |n| defined_argument_is_undefined?(node, n) } ||
|
83
97
|
pattern_matching_nodes(condition).any?
|
98
|
+
|
84
99
|
return unless (msg = message(node))
|
85
100
|
|
86
101
|
add_offense(node.loc.keyword, message: format(msg, keyword: node.keyword)) do |corrector|
|
@@ -90,9 +105,14 @@ module RuboCop
|
|
90
105
|
ignore_node(node)
|
91
106
|
end
|
92
107
|
end
|
108
|
+
# rubocop:enable Metrics/AbcSize
|
93
109
|
|
94
110
|
private
|
95
111
|
|
112
|
+
def endless_method?(body)
|
113
|
+
body&.any_def_type? && body.endless?
|
114
|
+
end
|
115
|
+
|
96
116
|
def defined_nodes(condition)
|
97
117
|
if condition.defined_type?
|
98
118
|
[condition]
|
@@ -28,19 +28,16 @@ module RuboCop
|
|
28
28
|
|
29
29
|
MSG = 'Avoid modifier `%<keyword>s` after another conditional.'
|
30
30
|
|
31
|
+
# rubocop:disable Metrics/AbcSize
|
31
32
|
def on_if(node)
|
32
33
|
return unless node.modifier_form? && node.body.if_type?
|
33
34
|
|
34
35
|
add_offense(node.loc.keyword, message: format(MSG, keyword: node.keyword)) do |corrector|
|
35
|
-
keyword
|
36
|
-
|
37
|
-
corrector.replace(node, <<~RUBY.chop)
|
38
|
-
#{keyword} #{node.condition.source}
|
39
|
-
#{node.if_branch.source}
|
40
|
-
end
|
41
|
-
RUBY
|
36
|
+
corrector.wrap(node.if_branch, "#{node.keyword} #{node.condition.source}\n", "\nend")
|
37
|
+
corrector.remove(node.if_branch.source_range.end.join(node.condition.source_range.end))
|
42
38
|
end
|
43
39
|
end
|
40
|
+
# rubocop:enable Metrics/AbcSize
|
44
41
|
end
|
45
42
|
end
|
46
43
|
end
|
@@ -45,6 +45,11 @@ module RuboCop
|
|
45
45
|
}
|
46
46
|
PATTERN
|
47
47
|
|
48
|
+
# @!method destructuring_argument(node)
|
49
|
+
def_node_matcher :destructuring_argument, <<~PATTERN
|
50
|
+
(args $(mlhs (arg _)+))
|
51
|
+
PATTERN
|
52
|
+
|
48
53
|
def self.autocorrect_incompatible_with
|
49
54
|
[Layout::SingleLineBlockChain]
|
50
55
|
end
|
@@ -73,6 +78,12 @@ module RuboCop
|
|
73
78
|
corrector.replace(map_dot, to_h.loc.dot.source)
|
74
79
|
end
|
75
80
|
corrector.replace(map.loc.selector, 'to_h')
|
81
|
+
|
82
|
+
return unless map.parent.block_type?
|
83
|
+
|
84
|
+
if (argument = destructuring_argument(map.parent.arguments))
|
85
|
+
corrector.replace(argument, argument.source[1..-2])
|
86
|
+
end
|
76
87
|
end
|
77
88
|
# rubocop:enable Metrics/AbcSize
|
78
89
|
end
|
@@ -23,11 +23,13 @@ module RuboCop
|
|
23
23
|
'clause in a multiline statement.'
|
24
24
|
|
25
25
|
def on_if(node)
|
26
|
+
return if part_of_ignored_node?(node)
|
26
27
|
return unless node.modifier_form? && node.body.multiline?
|
27
28
|
|
28
29
|
add_offense(node, message: format(MSG, keyword: node.keyword)) do |corrector|
|
29
30
|
corrector.replace(node, to_normal_if(node))
|
30
31
|
end
|
32
|
+
ignore_node(node)
|
31
33
|
end
|
32
34
|
|
33
35
|
private
|
@@ -45,7 +45,7 @@ module RuboCop
|
|
45
45
|
# Report offense only if changing case doesn't change semantics,
|
46
46
|
# i.e., if the string would become dynamic or has special characters.
|
47
47
|
ast = parse(corrected(node.source)).ast
|
48
|
-
return if node.children != ast
|
48
|
+
return if node.children != ast&.children
|
49
49
|
|
50
50
|
add_offense(node.loc.begin) do |corrector|
|
51
51
|
corrector.replace(node, corrected(node.source))
|
@@ -121,7 +121,12 @@ module RuboCop
|
|
121
121
|
def register_all_fields_literal(node, string, arguments)
|
122
122
|
return unless all_fields_literal?(string, arguments.dup)
|
123
123
|
|
124
|
-
|
124
|
+
format_arguments = argument_values(arguments)
|
125
|
+
begin
|
126
|
+
formatted_string = format(string, *format_arguments)
|
127
|
+
rescue ArgumentError
|
128
|
+
return
|
129
|
+
end
|
125
130
|
replacement = quote(formatted_string, node)
|
126
131
|
|
127
132
|
add_offense(node, message: message(node, replacement)) do |corrector|
|
@@ -155,7 +155,7 @@ module RuboCop
|
|
155
155
|
end
|
156
156
|
|
157
157
|
def preferred_delimiters
|
158
|
-
config.for_cop('Style/PercentLiteralDelimiters')
|
158
|
+
config.for_cop('Style/PercentLiteralDelimiters')['PreferredDelimiters']['%r'].chars
|
159
159
|
end
|
160
160
|
|
161
161
|
def allowed_omit_parentheses_with_percent_r_literal?(node)
|
@@ -185,8 +185,10 @@ module RuboCop
|
|
185
185
|
end
|
186
186
|
|
187
187
|
def add_parentheses?(node)
|
188
|
-
node.assignment? || (node.operator_keyword? && !node.and_type?)
|
189
|
-
|
188
|
+
return true if node.assignment? || (node.operator_keyword? && !node.and_type?)
|
189
|
+
return false unless node.call_type?
|
190
|
+
|
191
|
+
(node.arguments.any? && !node.parenthesized?) || node.prefix_not?
|
190
192
|
end
|
191
193
|
|
192
194
|
def parenthesized_method_arguments(node)
|
@@ -51,7 +51,6 @@ module RuboCop
|
|
51
51
|
# Pathname.new('/') + 'test'
|
52
52
|
#
|
53
53
|
class StringConcatenation < Base
|
54
|
-
include RangeHelp
|
55
54
|
extend AutoCorrector
|
56
55
|
|
57
56
|
MSG = 'Prefer string interpolation to string concatenation.'
|
@@ -147,7 +146,7 @@ module RuboCop
|
|
147
146
|
when :str
|
148
147
|
adjust_str(part)
|
149
148
|
when :dstr
|
150
|
-
part.children.all?(&:str_type?) ? adjust_str(part) :
|
149
|
+
part.children.all?(&:str_type?) ? adjust_str(part) : part.value
|
151
150
|
else
|
152
151
|
"\#{#{part.source}}"
|
153
152
|
end
|
@@ -3,7 +3,8 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Style
|
6
|
-
# Checks for inheritance from Struct.new.
|
6
|
+
# Checks for inheritance from `Struct.new`. Inheriting from `Struct.new`
|
7
|
+
# adds a superfluous level in inheritance tree.
|
7
8
|
#
|
8
9
|
# @safety
|
9
10
|
# Autocorrection is unsafe because it will change the inheritance
|
@@ -17,12 +18,18 @@ module RuboCop
|
|
17
18
|
# end
|
18
19
|
# end
|
19
20
|
#
|
21
|
+
# Person.ancestors
|
22
|
+
# # => [Person, #<Class:0x000000010b4e14a0>, Struct, (...)]
|
23
|
+
#
|
20
24
|
# # good
|
21
25
|
# Person = Struct.new(:first_name, :last_name) do
|
22
26
|
# def age
|
23
27
|
# 42
|
24
28
|
# end
|
25
29
|
# end
|
30
|
+
#
|
31
|
+
# Person.ancestors
|
32
|
+
# # => [Person, Struct, (...)]
|
26
33
|
class StructInheritance < Base
|
27
34
|
include RangeHelp
|
28
35
|
extend AutoCorrector
|
data/lib/rubocop/cop/team.rb
CHANGED
@@ -110,8 +110,13 @@ module RuboCop
|
|
110
110
|
end
|
111
111
|
|
112
112
|
def multiple_assignment_node
|
113
|
-
return nil unless node.parent
|
114
|
-
|
113
|
+
return nil unless (candidate_mlhs_node = node.parent)
|
114
|
+
|
115
|
+
# In `(foo, bar), *baz`, the splat node must be traversed as well.
|
116
|
+
candidate_mlhs_node = candidate_mlhs_node.parent if candidate_mlhs_node.splat_type?
|
117
|
+
|
118
|
+
return nil unless candidate_mlhs_node.mlhs_type?
|
119
|
+
return nil unless (grandparent_node = node.parent.parent)
|
115
120
|
if (node = find_multiple_assignment_node(grandparent_node))
|
116
121
|
return node
|
117
122
|
end
|
@@ -139,7 +144,6 @@ module RuboCop
|
|
139
144
|
|
140
145
|
def find_multiple_assignment_node(grandparent_node)
|
141
146
|
return unless grandparent_node.type == MULTIPLE_LEFT_HAND_SIDE_TYPE
|
142
|
-
return if grandparent_node.children.any?(&:splat_type?)
|
143
147
|
|
144
148
|
parent = grandparent_node.parent
|
145
149
|
return parent if parent.type == MULTIPLE_ASSIGNMENT_TYPE
|
data/lib/rubocop/version.rb
CHANGED
metadata
CHANGED
@@ -1,16 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubocop
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.75.
|
4
|
+
version: 1.75.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bozhidar Batsov
|
8
8
|
- Jonas Arvidsson
|
9
9
|
- Yuji Nakayama
|
10
|
-
autorequire:
|
11
10
|
bindir: exe
|
12
11
|
cert_chain: []
|
13
|
-
date: 2025-05-
|
12
|
+
date: 2025-05-28 00:00:00.000000000 Z
|
14
13
|
dependencies:
|
15
14
|
- !ruby/object:Gem::Dependency
|
16
15
|
name: json
|
@@ -1081,12 +1080,11 @@ licenses:
|
|
1081
1080
|
- MIT
|
1082
1081
|
metadata:
|
1083
1082
|
homepage_uri: https://rubocop.org/
|
1084
|
-
changelog_uri: https://github.com/rubocop/rubocop/releases/tag/v1.75.
|
1083
|
+
changelog_uri: https://github.com/rubocop/rubocop/releases/tag/v1.75.8
|
1085
1084
|
source_code_uri: https://github.com/rubocop/rubocop/
|
1086
1085
|
documentation_uri: https://docs.rubocop.org/rubocop/1.75/
|
1087
1086
|
bug_tracker_uri: https://github.com/rubocop/rubocop/issues
|
1088
1087
|
rubygems_mfa_required: 'true'
|
1089
|
-
post_install_message:
|
1090
1088
|
rdoc_options: []
|
1091
1089
|
require_paths:
|
1092
1090
|
- lib
|
@@ -1101,8 +1099,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
1101
1099
|
- !ruby/object:Gem::Version
|
1102
1100
|
version: '0'
|
1103
1101
|
requirements: []
|
1104
|
-
rubygems_version: 3.
|
1105
|
-
signing_key:
|
1102
|
+
rubygems_version: 3.6.2
|
1106
1103
|
specification_version: 4
|
1107
1104
|
summary: Automatic Ruby code style checking tool.
|
1108
1105
|
test_files: []
|