rubocop 1.72.1 → 1.73.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +3 -3
- data/config/default.yml +26 -11
- data/config/internal_affairs.yml +16 -0
- data/lib/rubocop/config_loader_resolver.rb +2 -2
- data/lib/rubocop/config_validator.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/example_description.rb +4 -2
- data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +4 -4
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +26 -1
- data/lib/rubocop/cop/layout/line_length.rb +3 -3
- data/lib/rubocop/cop/lint/duplicate_methods.rb +0 -14
- data/lib/rubocop/cop/lint/empty_conditional_body.rb +10 -5
- data/lib/rubocop/cop/lint/float_comparison.rb +1 -6
- data/lib/rubocop/cop/lint/literal_as_condition.rb +99 -9
- data/lib/rubocop/cop/lint/mixed_case_range.rb +2 -2
- data/lib/rubocop/cop/lint/redundant_require_statement.rb +0 -21
- data/lib/rubocop/cop/lint/redundant_type_conversion.rb +23 -2
- data/lib/rubocop/cop/lint/useless_constant_scoping.rb +7 -1
- data/lib/rubocop/cop/lint/void.rb +6 -0
- data/lib/rubocop/cop/mixin/allowed_pattern.rb +4 -4
- data/lib/rubocop/cop/mixin/hash_subset.rb +19 -4
- data/lib/rubocop/cop/mixin/trailing_comma.rb +12 -0
- data/lib/rubocop/cop/naming/block_forwarding.rb +3 -3
- data/lib/rubocop/cop/naming/variable_name.rb +64 -6
- data/lib/rubocop/cop/style/accessor_grouping.rb +19 -5
- data/lib/rubocop/cop/style/arguments_forwarding.rb +3 -3
- data/lib/rubocop/cop/style/commented_keyword.rb +1 -1
- data/lib/rubocop/cop/style/endless_method.rb +163 -18
- data/lib/rubocop/cop/style/line_end_concatenation.rb +10 -4
- data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +1 -1
- data/lib/rubocop/cop/style/redundant_condition.rb +46 -0
- data/lib/rubocop/cop/style/redundant_format.rb +39 -11
- data/lib/rubocop/cop/style/redundant_parentheses.rb +1 -3
- data/lib/rubocop/cop/style/redundant_self_assignment.rb +1 -1
- data/lib/rubocop/cop/style/single_line_methods.rb +3 -3
- data/lib/rubocop/cop/style/string_concatenation.rb +1 -1
- data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +47 -6
- data/lib/rubocop/cop/style/trailing_comma_in_hash_literal.rb +48 -6
- data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
- data/lib/rubocop/cops_documentation_generator.rb +12 -1
- data/lib/rubocop/plugin/configuration_integrator.rb +2 -0
- data/lib/rubocop/plugin/load_error.rb +1 -1
- data/lib/rubocop/plugin.rb +9 -2
- data/lib/rubocop/rspec/cop_helper.rb +2 -0
- data/lib/rubocop/rspec/shared_contexts.rb +15 -0
- data/lib/rubocop/rspec/support.rb +1 -0
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +0 -1
- metadata +4 -5
- data/lib/rubocop/cop/utils/regexp_ranges.rb +0 -113
@@ -39,7 +39,7 @@ module RuboCop
|
|
39
39
|
# 1i.to_c
|
40
40
|
# [].to_a
|
41
41
|
# {}.to_h
|
42
|
-
# Set.new.
|
42
|
+
# Set.new.to_set
|
43
43
|
#
|
44
44
|
# # good
|
45
45
|
# "text"
|
@@ -52,6 +52,16 @@ module RuboCop
|
|
52
52
|
# {}
|
53
53
|
# Set.new
|
54
54
|
#
|
55
|
+
# # bad
|
56
|
+
# Integer(var).to_i
|
57
|
+
#
|
58
|
+
# # good
|
59
|
+
# Integer(var)
|
60
|
+
#
|
61
|
+
# # good - chaining to a type constructor with exceptions suppressed
|
62
|
+
# # in this case, `Integer()` could return `nil`
|
63
|
+
# Integer(var, exception: false).to_i
|
64
|
+
#
|
55
65
|
# # bad - chaining the same conversion
|
56
66
|
# foo.to_s.to_s
|
57
67
|
#
|
@@ -161,6 +171,11 @@ module RuboCop
|
|
161
171
|
}
|
162
172
|
PATTERN
|
163
173
|
|
174
|
+
# @!method exception_false_keyword_argument?(node)
|
175
|
+
def_node_matcher :exception_false_keyword_argument?, <<~PATTERN
|
176
|
+
(hash (pair (sym :exception) false))
|
177
|
+
PATTERN
|
178
|
+
|
164
179
|
# rubocop:disable Metrics/AbcSize
|
165
180
|
def on_send(node)
|
166
181
|
return if hash_or_set_with_block?(node)
|
@@ -211,7 +226,13 @@ module RuboCop
|
|
211
226
|
matcher = CONSTRUCTOR_MAPPING[node.method_name]
|
212
227
|
return false unless matcher
|
213
228
|
|
214
|
-
public_send(matcher, receiver)
|
229
|
+
public_send(matcher, receiver) && !constructor_suppresses_exceptions?(receiver)
|
230
|
+
end
|
231
|
+
|
232
|
+
def constructor_suppresses_exceptions?(receiver)
|
233
|
+
# If the constructor suppresses exceptions with `exception: false`, it is possible
|
234
|
+
# it could return `nil`, and therefore a chained conversion is not redundant.
|
235
|
+
receiver.arguments.any? { |arg| exception_false_keyword_argument?(arg) }
|
215
236
|
end
|
216
237
|
|
217
238
|
def chained_conversion?(node, receiver)
|
@@ -56,7 +56,13 @@ module RuboCop
|
|
56
56
|
private
|
57
57
|
|
58
58
|
def after_private_modifier?(left_siblings)
|
59
|
-
left_siblings.compact.select
|
59
|
+
access_modifier_candidates = left_siblings.compact.select do |left_sibling|
|
60
|
+
left_sibling.respond_to?(:send_type?) && left_sibling.send_type?
|
61
|
+
end
|
62
|
+
|
63
|
+
access_modifier_candidates.any? do |candidate|
|
64
|
+
candidate.command?(:private) && candidate.arguments.none?
|
65
|
+
end
|
60
66
|
end
|
61
67
|
|
62
68
|
def private_constantize?(right_siblings, const_value)
|
@@ -125,9 +125,14 @@ module RuboCop
|
|
125
125
|
check_nonmutating(expr)
|
126
126
|
end
|
127
127
|
|
128
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
128
129
|
def check_void_op(node, &block)
|
129
130
|
node = node.children.first while node.begin_type?
|
130
131
|
return unless node.call_type? && OPERATORS.include?(node.method_name)
|
132
|
+
if !UNARY_OPERATORS.include?(node.method_name) && node.loc.dot && node.arguments.none?
|
133
|
+
return
|
134
|
+
end
|
135
|
+
|
131
136
|
return if block && yield(node)
|
132
137
|
|
133
138
|
add_offense(node.loc.selector,
|
@@ -135,6 +140,7 @@ module RuboCop
|
|
135
140
|
autocorrect_void_op(corrector, node)
|
136
141
|
end
|
137
142
|
end
|
143
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
138
144
|
|
139
145
|
def check_var(node)
|
140
146
|
return unless node.variable? || node.const_type?
|
@@ -18,12 +18,12 @@ module RuboCop
|
|
18
18
|
end
|
19
19
|
|
20
20
|
# @deprecated Use allowed_line? instead
|
21
|
-
def ignored_line?
|
21
|
+
def ignored_line?(line)
|
22
22
|
warn Rainbow(<<~WARNING).yellow, uplevel: 1
|
23
23
|
`ignored_line?` is deprecated. Use `allowed_line?` instead.
|
24
24
|
WARNING
|
25
25
|
|
26
|
-
allowed_line?
|
26
|
+
allowed_line?(line)
|
27
27
|
end
|
28
28
|
|
29
29
|
def matches_allowed_pattern?(line)
|
@@ -31,12 +31,12 @@ module RuboCop
|
|
31
31
|
end
|
32
32
|
|
33
33
|
# @deprecated Use matches_allowed_pattern? instead
|
34
|
-
def matches_ignored_pattern?
|
34
|
+
def matches_ignored_pattern?(line)
|
35
35
|
warn Rainbow(<<~WARNING).yellow, uplevel: 1
|
36
36
|
`matches_ignored_pattern?` is deprecated. Use `matches_allowed_pattern?` instead.
|
37
37
|
WARNING
|
38
38
|
|
39
|
-
matches_allowed_pattern?
|
39
|
+
matches_allowed_pattern?(line)
|
40
40
|
end
|
41
41
|
|
42
42
|
def allowed_patterns
|
@@ -23,7 +23,7 @@ module RuboCop
|
|
23
23
|
(call _ _)
|
24
24
|
(args
|
25
25
|
$(arg _key)
|
26
|
-
(arg _))
|
26
|
+
$(arg _))
|
27
27
|
{
|
28
28
|
$(send
|
29
29
|
{(lvar _key) $_ _ | _ $_ (lvar _key)})
|
@@ -67,7 +67,7 @@ module RuboCop
|
|
67
67
|
end
|
68
68
|
|
69
69
|
def extracts_hash_subset?(block)
|
70
|
-
block_with_first_arg_check?(block) do |key_arg, send_node, method|
|
70
|
+
block_with_first_arg_check?(block) do |key_arg, value_arg, send_node, method|
|
71
71
|
# Only consider methods that have one argument
|
72
72
|
return false unless send_node.arguments.one?
|
73
73
|
|
@@ -76,15 +76,22 @@ module RuboCop
|
|
76
76
|
|
77
77
|
case method
|
78
78
|
when :include?, :exclude?
|
79
|
-
send_node
|
79
|
+
slices_key?(send_node, :first_argument, key_arg, value_arg)
|
80
80
|
when :in?
|
81
|
-
send_node
|
81
|
+
slices_key?(send_node, :receiver, key_arg, value_arg)
|
82
82
|
else
|
83
83
|
true
|
84
84
|
end
|
85
85
|
end
|
86
86
|
end
|
87
87
|
|
88
|
+
def slices_key?(send_node, method, key_arg, value_arg)
|
89
|
+
return false if using_value_variable?(send_node, value_arg)
|
90
|
+
|
91
|
+
node = method == :receiver ? send_node.receiver : send_node.first_argument
|
92
|
+
node.source == key_arg.source
|
93
|
+
end
|
94
|
+
|
88
95
|
def range_include?(send_node)
|
89
96
|
# When checking `include?`, `exclude?` and `in?` for offenses, if the receiver
|
90
97
|
# or first argument is a range, an offense should not be registered.
|
@@ -97,6 +104,14 @@ module RuboCop
|
|
97
104
|
receiver.range_type?
|
98
105
|
end
|
99
106
|
|
107
|
+
def using_value_variable?(send_node, value_arg)
|
108
|
+
# If the receiver of `include?` or `exclude?`, or the first argument of `in?` is the
|
109
|
+
# hash value block argument, an offense should not be registered.
|
110
|
+
# ie. `v.include?(k)` or `k.in?(v)`
|
111
|
+
(send_node.receiver.lvar_type? && send_node.receiver.name == value_arg.name) ||
|
112
|
+
(send_node.first_argument.lvar_type? && send_node.first_argument.name == value_arg.name)
|
113
|
+
end
|
114
|
+
|
100
115
|
def supported_subset_method?(method)
|
101
116
|
if active_support_extensions_enabled?
|
102
117
|
ACTIVE_SUPPORT_SUBSET_METHODS.include?(method)
|
@@ -4,6 +4,7 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
# Common methods shared by Style/TrailingCommaInArguments,
|
6
6
|
# Style/TrailingCommaInArrayLiteral and Style/TrailingCommaInHashLiteral
|
7
|
+
# rubocop:disable Metrics/ModuleLength
|
7
8
|
module TrailingComma
|
8
9
|
include ConfigurableEnforcedStyle
|
9
10
|
include RangeHelp
|
@@ -57,6 +58,8 @@ module RuboCop
|
|
57
58
|
', unless each item is on its own line'
|
58
59
|
when :consistent_comma
|
59
60
|
', unless items are split onto multiple lines'
|
61
|
+
when :diff_comma
|
62
|
+
', unless that item immediately precedes a newline'
|
60
63
|
else
|
61
64
|
''
|
62
65
|
end
|
@@ -68,6 +71,8 @@ module RuboCop
|
|
68
71
|
multiline?(node) && no_elements_on_same_line?(node)
|
69
72
|
when :consistent_comma
|
70
73
|
multiline?(node) && !method_name_and_arguments_on_same_line?(node)
|
74
|
+
when :diff_comma
|
75
|
+
multiline?(node) && last_item_precedes_newline?(node)
|
71
76
|
else
|
72
77
|
false
|
73
78
|
end
|
@@ -130,6 +135,12 @@ module RuboCop
|
|
130
135
|
range1.last_line == range2.line
|
131
136
|
end
|
132
137
|
|
138
|
+
def last_item_precedes_newline?(node)
|
139
|
+
after_last_item =
|
140
|
+
range_between(node.children.last.source_range.end_pos, node.loc.end.begin_pos)
|
141
|
+
after_last_item.source =~ /\A,?\s*\n/
|
142
|
+
end
|
143
|
+
|
133
144
|
def avoid_comma(kind, comma_begin_pos, extra_info)
|
134
145
|
range = range_between(comma_begin_pos, comma_begin_pos + 1)
|
135
146
|
article = kind.include?('array') ? 'an' : 'a'
|
@@ -205,5 +216,6 @@ module RuboCop
|
|
205
216
|
false
|
206
217
|
end
|
207
218
|
end
|
219
|
+
# rubocop:enable Metrics/ModuleLength
|
208
220
|
end
|
209
221
|
end
|
@@ -14,10 +14,10 @@ module RuboCop
|
|
14
14
|
# autocorrected.
|
15
15
|
#
|
16
16
|
# [NOTE]
|
17
|
-
#
|
17
|
+
# ====
|
18
18
|
# Because of a bug in Ruby 3.3.0, when a block is referenced inside of another block,
|
19
19
|
# no offense will be registered until Ruby 3.4:
|
20
|
-
|
20
|
+
#
|
21
21
|
# [source,ruby]
|
22
22
|
# ----
|
23
23
|
# def foo(&block)
|
@@ -25,7 +25,7 @@ module RuboCop
|
|
25
25
|
# block_method { bar(&block) }
|
26
26
|
# end
|
27
27
|
# ----
|
28
|
-
#
|
28
|
+
# ====
|
29
29
|
#
|
30
30
|
# @example EnforcedStyle: anonymous (default)
|
31
31
|
#
|
@@ -3,8 +3,15 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Naming
|
6
|
-
#
|
7
|
-
#
|
6
|
+
# Checks that the configured style (snake_case or camelCase) is used for all variable names.
|
7
|
+
# This includes local variables, instance variables, class variables, method arguments
|
8
|
+
# (positional, keyword, rest or block), and block arguments.
|
9
|
+
#
|
10
|
+
# The cop can also be configured to forbid using specific names for variables, using
|
11
|
+
# `ForbiddenIdentifiers` or `ForbiddenPatterns`. In addition to the above, this applies
|
12
|
+
# to global variables as well.
|
13
|
+
#
|
14
|
+
# Method definitions and method calls are not affected by this cop.
|
8
15
|
#
|
9
16
|
# @example EnforcedStyle: snake_case (default)
|
10
17
|
# # bad
|
@@ -26,7 +33,21 @@ module RuboCop
|
|
26
33
|
#
|
27
34
|
# @example AllowedPatterns: ['_v\d+\z']
|
28
35
|
# # good (with EnforcedStyle: camelCase)
|
29
|
-
#
|
36
|
+
# release_v1 = true
|
37
|
+
#
|
38
|
+
# @example ForbiddenIdentifiers: ['fooBar']
|
39
|
+
# # bad (in all cases)
|
40
|
+
# fooBar = 1
|
41
|
+
# @fooBar = 1
|
42
|
+
# @@fooBar = 1
|
43
|
+
# $fooBar = 1
|
44
|
+
#
|
45
|
+
# @example ForbiddenPatterns: ['_v\d+\z']
|
46
|
+
# # bad (in all cases)
|
47
|
+
# release_v1 = true
|
48
|
+
# @release_v1 = true
|
49
|
+
# @@release_v1 = true
|
50
|
+
# $release_v1 = true
|
30
51
|
#
|
31
52
|
class VariableName < Base
|
32
53
|
include AllowedIdentifiers
|
@@ -34,16 +55,21 @@ module RuboCop
|
|
34
55
|
include AllowedPattern
|
35
56
|
|
36
57
|
MSG = 'Use %<style>s for variable names.'
|
58
|
+
MSG_FORBIDDEN = '`%<identifier>s` is forbidden, use another name instead.'
|
37
59
|
|
38
60
|
def valid_name?(node, name, given_style = style)
|
39
61
|
super || matches_allowed_pattern?(name)
|
40
62
|
end
|
41
63
|
|
42
64
|
def on_lvasgn(node)
|
43
|
-
return unless node.name
|
44
|
-
return if allowed_identifier?(
|
65
|
+
return unless (name = node.name)
|
66
|
+
return if allowed_identifier?(name)
|
45
67
|
|
46
|
-
|
68
|
+
if forbidden_name?(name)
|
69
|
+
register_forbidden_name(node)
|
70
|
+
else
|
71
|
+
check_name(node, name, node.loc.name)
|
72
|
+
end
|
47
73
|
end
|
48
74
|
alias on_ivasgn on_lvasgn
|
49
75
|
alias on_cvasgn on_lvasgn
|
@@ -56,11 +82,43 @@ module RuboCop
|
|
56
82
|
alias on_blockarg on_lvasgn
|
57
83
|
alias on_lvar on_lvasgn
|
58
84
|
|
85
|
+
# Only forbidden names are checked for global variable assignment
|
86
|
+
def on_gvasgn(node)
|
87
|
+
return unless (name = node.name)
|
88
|
+
return unless forbidden_name?(name)
|
89
|
+
|
90
|
+
register_forbidden_name(node)
|
91
|
+
end
|
92
|
+
|
59
93
|
private
|
60
94
|
|
61
95
|
def message(style)
|
62
96
|
format(MSG, style: style)
|
63
97
|
end
|
98
|
+
|
99
|
+
def forbidden_identifiers
|
100
|
+
cop_config.fetch('ForbiddenIdentifiers', [])
|
101
|
+
end
|
102
|
+
|
103
|
+
def forbidden_patterns
|
104
|
+
cop_config.fetch('ForbiddenPatterns', [])
|
105
|
+
end
|
106
|
+
|
107
|
+
def matches_forbidden_pattern?(name)
|
108
|
+
forbidden_patterns.any? { |pattern| Regexp.new(pattern).match?(name) }
|
109
|
+
end
|
110
|
+
|
111
|
+
def forbidden_name?(name)
|
112
|
+
name = name.to_s.delete(SIGILS)
|
113
|
+
|
114
|
+
(forbidden_identifiers.any? && forbidden_identifiers.include?(name)) ||
|
115
|
+
(forbidden_patterns.any? && matches_forbidden_pattern?(name))
|
116
|
+
end
|
117
|
+
|
118
|
+
def register_forbidden_name(node)
|
119
|
+
message = format(MSG_FORBIDDEN, identifier: node.name)
|
120
|
+
add_offense(node.loc.name, message: message)
|
121
|
+
end
|
64
122
|
end
|
65
123
|
end
|
66
124
|
end
|
@@ -139,12 +139,16 @@ module RuboCop
|
|
139
139
|
style == :separated
|
140
140
|
end
|
141
141
|
|
142
|
+
def groupable_sibling_accessor?(node, sibling)
|
143
|
+
sibling.attribute_accessor? &&
|
144
|
+
sibling.method?(node.method_name) &&
|
145
|
+
node_visibility(sibling) == node_visibility(node) &&
|
146
|
+
groupable_accessor?(sibling) && !previous_line_comment?(sibling)
|
147
|
+
end
|
148
|
+
|
142
149
|
def groupable_sibling_accessors(send_node)
|
143
150
|
send_node.parent.each_child_node(:send).select do |sibling|
|
144
|
-
|
145
|
-
sibling.method?(send_node.method_name) &&
|
146
|
-
node_visibility(sibling) == node_visibility(send_node) &&
|
147
|
-
groupable_accessor?(sibling) && !previous_line_comment?(sibling)
|
151
|
+
groupable_sibling_accessor?(send_node, sibling)
|
148
152
|
end
|
149
153
|
end
|
150
154
|
|
@@ -155,13 +159,23 @@ module RuboCop
|
|
155
159
|
|
156
160
|
def preferred_accessors(node)
|
157
161
|
if grouped_style?
|
162
|
+
return if skip_for_grouping?(node)
|
163
|
+
|
158
164
|
accessors = groupable_sibling_accessors(node)
|
159
|
-
|
165
|
+
if node.loc == accessors.first.loc || skip_for_grouping?(accessors.first)
|
166
|
+
group_accessors(node, accessors)
|
167
|
+
end
|
160
168
|
else
|
161
169
|
separate_accessors(node)
|
162
170
|
end
|
163
171
|
end
|
164
172
|
|
173
|
+
# Group after constants
|
174
|
+
def skip_for_grouping?(node)
|
175
|
+
node.right_siblings.any?(&:casgn_type?) &&
|
176
|
+
node.right_siblings.any? { |n| n.send_type? && groupable_sibling_accessor?(node, n) }
|
177
|
+
end
|
178
|
+
|
165
179
|
def group_accessors(node, accessors)
|
166
180
|
accessor_names = accessors.flat_map { |accessor| accessor.arguments.map(&:source) }.uniq
|
167
181
|
|
@@ -32,10 +32,10 @@ module RuboCop
|
|
32
32
|
# This cop handles not only method forwarding but also forwarding to `super`.
|
33
33
|
#
|
34
34
|
# [NOTE]
|
35
|
-
#
|
35
|
+
# ====
|
36
36
|
# Because of a bug in Ruby 3.3.0, when a block is referenced inside of another block,
|
37
37
|
# no offense will be registered until Ruby 3.4:
|
38
|
-
|
38
|
+
#
|
39
39
|
# [source,ruby]
|
40
40
|
# ----
|
41
41
|
# def foo(&block)
|
@@ -43,7 +43,7 @@ module RuboCop
|
|
43
43
|
# block_method { bar(&block) }
|
44
44
|
# end
|
45
45
|
# ----
|
46
|
-
#
|
46
|
+
# ====
|
47
47
|
#
|
48
48
|
# @example
|
49
49
|
# # bad
|
@@ -57,7 +57,7 @@ module RuboCop
|
|
57
57
|
|
58
58
|
REGEXP = /(?<keyword>\S+).*#/.freeze
|
59
59
|
|
60
|
-
SUBCLASS_DEFINITION = /\A\s*class\s
|
60
|
+
SUBCLASS_DEFINITION = /\A\s*class\s+(\w|::)+\s*<\s*(\w|::)+/.freeze
|
61
61
|
METHOD_DEFINITION = /\A\s*def\s/.freeze
|
62
62
|
|
63
63
|
def on_new_investigation
|
@@ -5,8 +5,11 @@ module RuboCop
|
|
5
5
|
module Style
|
6
6
|
# Checks for endless methods.
|
7
7
|
#
|
8
|
-
# It can enforce
|
9
|
-
#
|
8
|
+
# It can enforce endless method definitions whenever possible or with single line methods.
|
9
|
+
# It can also disallow multiline endless method definitions or all endless definitions.
|
10
|
+
#
|
11
|
+
# `require_single_line` style enforces endless method definitions for single line methods.
|
12
|
+
# `require_always` style enforces endless method definitions for single statement methods.
|
10
13
|
#
|
11
14
|
# Other method definition types are not considered by this cop.
|
12
15
|
#
|
@@ -15,36 +18,116 @@ module RuboCop
|
|
15
18
|
# * allow_single_line (default) - only single line endless method definitions are allowed.
|
16
19
|
# * allow_always - all endless method definitions are allowed.
|
17
20
|
# * disallow - all endless method definitions are disallowed.
|
21
|
+
# * require_single_line - endless method definitions are required for single line methods.
|
22
|
+
# * require_always - all endless method definitions are required.
|
18
23
|
#
|
19
24
|
# NOTE: Incorrect endless method definitions will always be
|
20
25
|
# corrected to a multi-line definition.
|
21
26
|
#
|
22
27
|
# @example EnforcedStyle: allow_single_line (default)
|
28
|
+
# # bad, multi-line endless method
|
29
|
+
# def my_method = x.foo
|
30
|
+
# .bar
|
31
|
+
# .baz
|
32
|
+
#
|
23
33
|
# # good
|
24
|
-
# def my_method
|
34
|
+
# def my_method
|
35
|
+
# x
|
36
|
+
# end
|
25
37
|
#
|
26
|
-
# #
|
27
|
-
# def my_method
|
28
|
-
#
|
29
|
-
#
|
38
|
+
# # good
|
39
|
+
# def my_method = x
|
40
|
+
#
|
41
|
+
# # good
|
42
|
+
# def my_method
|
43
|
+
# x.foo
|
44
|
+
# .bar
|
45
|
+
# .baz
|
46
|
+
# end
|
30
47
|
#
|
31
48
|
# @example EnforcedStyle: allow_always
|
32
49
|
# # good
|
33
|
-
# def my_method
|
50
|
+
# def my_method
|
51
|
+
# x
|
52
|
+
# end
|
34
53
|
#
|
35
54
|
# # good
|
36
|
-
# def my_method
|
37
|
-
#
|
38
|
-
#
|
55
|
+
# def my_method = x
|
56
|
+
#
|
57
|
+
# # good
|
58
|
+
# def my_method = x.foo
|
59
|
+
# .bar
|
60
|
+
# .baz
|
61
|
+
#
|
62
|
+
# # good
|
63
|
+
# def my_method
|
64
|
+
# x.foo
|
65
|
+
# .bar
|
66
|
+
# .baz
|
67
|
+
# end
|
39
68
|
#
|
40
69
|
# @example EnforcedStyle: disallow
|
41
70
|
# # bad
|
42
|
-
# def my_method
|
71
|
+
# def my_method = x
|
72
|
+
#
|
73
|
+
# # bad
|
74
|
+
# def my_method = x.foo
|
75
|
+
# .bar
|
76
|
+
# .baz
|
77
|
+
#
|
78
|
+
# # good
|
79
|
+
# def my_method
|
80
|
+
# x
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# # good
|
84
|
+
# def my_method
|
85
|
+
# x.foo
|
86
|
+
# .bar
|
87
|
+
# .baz
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
# @example EnforcedStyle: require_single_line
|
91
|
+
# # bad
|
92
|
+
# def my_method
|
93
|
+
# x
|
94
|
+
# end
|
95
|
+
#
|
96
|
+
# # bad
|
97
|
+
# def my_method = x.foo
|
98
|
+
# .bar
|
99
|
+
# .baz
|
100
|
+
#
|
101
|
+
# # good
|
102
|
+
# def my_method = x
|
103
|
+
#
|
104
|
+
# # good
|
105
|
+
# def my_method
|
106
|
+
# x.foo
|
107
|
+
# .bar
|
108
|
+
# .baz
|
109
|
+
# end
|
110
|
+
#
|
111
|
+
# @example EnforcedStyle: require_always
|
112
|
+
# # bad
|
113
|
+
# def my_method
|
114
|
+
# x
|
115
|
+
# end
|
43
116
|
#
|
44
117
|
# # bad
|
45
|
-
# def my_method
|
46
|
-
#
|
47
|
-
#
|
118
|
+
# def my_method
|
119
|
+
# x.foo
|
120
|
+
# .bar
|
121
|
+
# .baz
|
122
|
+
# end
|
123
|
+
#
|
124
|
+
# # good
|
125
|
+
# def my_method = x
|
126
|
+
#
|
127
|
+
# # good
|
128
|
+
# def my_method = x.foo
|
129
|
+
# .bar
|
130
|
+
# .baz
|
48
131
|
#
|
49
132
|
class EndlessMethod < Base
|
50
133
|
include ConfigurableEnforcedStyle
|
@@ -57,12 +140,21 @@ module RuboCop
|
|
57
140
|
CORRECTION_STYLES = %w[multiline single_line].freeze
|
58
141
|
MSG = 'Avoid endless method definitions.'
|
59
142
|
MSG_MULTI_LINE = 'Avoid endless method definitions with multiple lines.'
|
143
|
+
MSG_REQUIRE_SINGLE = 'Use endless method definitions for single line methods.'
|
144
|
+
MSG_REQUIRE_ALWAYS = 'Use endless method definitions.'
|
60
145
|
|
61
146
|
def on_def(node)
|
62
|
-
if
|
63
|
-
|
64
|
-
|
147
|
+
return if node.assignment_method?
|
148
|
+
|
149
|
+
case style
|
150
|
+
when :allow_single_line, :allow_always
|
65
151
|
handle_allow_style(node)
|
152
|
+
when :disallow
|
153
|
+
handle_disallow_style(node)
|
154
|
+
when :require_single_line
|
155
|
+
handle_require_single_line_style(node)
|
156
|
+
when :require_always
|
157
|
+
handle_require_always_style(node)
|
66
158
|
end
|
67
159
|
end
|
68
160
|
|
@@ -77,11 +169,64 @@ module RuboCop
|
|
77
169
|
end
|
78
170
|
end
|
79
171
|
|
172
|
+
def handle_require_single_line_style(node)
|
173
|
+
if node.endless? && !node.single_line?
|
174
|
+
add_offense(node, message: MSG_MULTI_LINE) do |corrector|
|
175
|
+
correct_to_multiline(corrector, node)
|
176
|
+
end
|
177
|
+
elsif !node.endless? && can_be_made_endless?(node) && node.body.single_line?
|
178
|
+
return if too_long_when_made_endless?(node)
|
179
|
+
|
180
|
+
add_offense(node, message: MSG_REQUIRE_SINGLE) do |corrector|
|
181
|
+
corrector.replace(node, endless_replacement(node))
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def handle_require_always_style(node)
|
187
|
+
return if node.endless? || !can_be_made_endless?(node)
|
188
|
+
return if too_long_when_made_endless?(node)
|
189
|
+
|
190
|
+
add_offense(node, message: MSG_REQUIRE_ALWAYS) do |corrector|
|
191
|
+
corrector.replace(node, endless_replacement(node))
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
80
195
|
def handle_disallow_style(node)
|
81
196
|
return unless node.endless?
|
82
197
|
|
83
198
|
add_offense(node) { |corrector| correct_to_multiline(corrector, node) }
|
84
199
|
end
|
200
|
+
|
201
|
+
def correct_to_multiline(corrector, node)
|
202
|
+
replacement = <<~RUBY.strip
|
203
|
+
def #{node.method_name}#{arguments(node)}
|
204
|
+
#{node.body.source}
|
205
|
+
end
|
206
|
+
RUBY
|
207
|
+
|
208
|
+
corrector.replace(node, replacement)
|
209
|
+
end
|
210
|
+
|
211
|
+
def endless_replacement(node)
|
212
|
+
<<~RUBY.strip
|
213
|
+
def #{node.method_name}#{arguments(node)} = #{node.body.source}
|
214
|
+
RUBY
|
215
|
+
end
|
216
|
+
|
217
|
+
def arguments(node, missing = '')
|
218
|
+
node.arguments.any? ? node.arguments.source : missing
|
219
|
+
end
|
220
|
+
|
221
|
+
def can_be_made_endless?(node)
|
222
|
+
node.body && !node.body.begin_type? && !node.body.kwbegin_type?
|
223
|
+
end
|
224
|
+
|
225
|
+
def too_long_when_made_endless?(node)
|
226
|
+
return false unless config.cop_enabled?('Layout/LineLength')
|
227
|
+
|
228
|
+
endless_replacement(node).length > config.for_cop('Layout/LineLength')['Max']
|
229
|
+
end
|
85
230
|
end
|
86
231
|
end
|
87
232
|
end
|