rubocop 1.71.2 → 1.73.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 +3 -3
- data/config/default.yml +51 -11
- data/config/internal_affairs.yml +16 -0
- data/lib/rubocop/cli/command/suggest_extensions.rb +7 -1
- data/lib/rubocop/comment_config.rb +1 -1
- data/lib/rubocop/config.rb +4 -0
- data/lib/rubocop/config_loader.rb +44 -8
- data/lib/rubocop/config_loader_resolver.rb +23 -9
- data/lib/rubocop/config_validator.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/example_description.rb +4 -2
- data/lib/rubocop/cop/internal_affairs/location_exists.rb +116 -0
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/plugin.rb +33 -0
- data/lib/rubocop/cop/internal_affairs/undefined_config.rb +7 -1
- data/lib/rubocop/cop/internal_affairs.rb +1 -16
- data/lib/rubocop/cop/layout/block_alignment.rb +2 -0
- data/lib/rubocop/cop/layout/else_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +26 -1
- data/lib/rubocop/cop/layout/empty_lines_around_method_body.rb +22 -2
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -1
- data/lib/rubocop/cop/layout/line_length.rb +3 -3
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +2 -2
- data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +1 -1
- data/lib/rubocop/cop/lint/cop_directive_syntax.rb +84 -0
- data/lib/rubocop/cop/lint/duplicate_methods.rb +0 -14
- data/lib/rubocop/cop/lint/float_comparison.rb +1 -6
- data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +1 -1
- data/lib/rubocop/cop/lint/literal_as_condition.rb +104 -7
- data/lib/rubocop/cop/lint/mixed_case_range.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_require_statement.rb +0 -21
- data/lib/rubocop/cop/lint/redundant_type_conversion.rb +252 -0
- data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +111 -0
- data/lib/rubocop/cop/lint/useless_constant_scoping.rb +80 -0
- data/lib/rubocop/cop/lint/void.rb +6 -0
- data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +7 -7
- data/lib/rubocop/cop/mixin/alignment.rb +2 -2
- data/lib/rubocop/cop/mixin/allowed_pattern.rb +4 -4
- data/lib/rubocop/cop/mixin/comments_help.rb +1 -1
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +18 -18
- data/lib/rubocop/cop/mixin/hash_subset.rb +19 -4
- data/lib/rubocop/cop/mixin/hash_transform_method.rb +74 -74
- data/lib/rubocop/cop/mixin/percent_literal.rb +1 -1
- data/lib/rubocop/cop/mixin/range_help.rb +3 -3
- data/lib/rubocop/cop/mixin/string_help.rb +1 -1
- 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/predicate_name.rb +44 -0
- 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/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 +34 -0
- data/lib/rubocop/cop/style/redundant_format.rb +250 -0
- data/lib/rubocop/cop/style/redundant_parentheses.rb +18 -4
- 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/cop/util.rb +1 -1
- data/lib/rubocop/cop/utils/format_string.rb +7 -5
- data/lib/rubocop/cops_documentation_generator.rb +12 -1
- data/lib/rubocop/directive_comment.rb +35 -2
- data/lib/rubocop/lsp/runtime.rb +2 -0
- data/lib/rubocop/lsp/server.rb +0 -2
- data/lib/rubocop/options.rb +26 -11
- data/lib/rubocop/path_util.rb +4 -0
- data/lib/rubocop/plugin/configuration_integrator.rb +143 -0
- data/lib/rubocop/plugin/load_error.rb +26 -0
- data/lib/rubocop/plugin/loader.rb +100 -0
- data/lib/rubocop/plugin/not_supported_error.rb +29 -0
- data/lib/rubocop/plugin.rb +46 -0
- data/lib/rubocop/rake_task.rb +4 -1
- data/lib/rubocop/rspec/cop_helper.rb +9 -0
- data/lib/rubocop/rspec/shared_contexts.rb +15 -0
- data/lib/rubocop/rspec/support.rb +1 -0
- data/lib/rubocop/server/cache.rb +35 -2
- data/lib/rubocop/server/cli.rb +2 -2
- data/lib/rubocop/version.rb +17 -2
- data/lib/rubocop.rb +5 -1
- data/lib/ruby_lsp/rubocop/addon.rb +7 -10
- data/lib/ruby_lsp/rubocop/{wraps_built_in_lsp_runtime.rb → runtime_adapter.rb} +5 -8
- metadata +35 -10
- data/lib/rubocop/cop/utils/regexp_ranges.rb +0 -113
@@ -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
|
@@ -36,7 +36,7 @@ module RuboCop
|
|
36
36
|
include RangeHelp
|
37
37
|
extend AutoCorrector
|
38
38
|
|
39
|
-
MSG = 'Use `\\` instead of
|
39
|
+
MSG = 'Use `\\` instead of `%<operator>s` to concatenate multiline strings.'
|
40
40
|
CONCAT_TOKEN_TYPES = %i[tPLUS tLSHFT].freeze
|
41
41
|
SIMPLE_STRING_TOKEN_TYPE = :tSTRING
|
42
42
|
COMPLEX_STRING_BEGIN_TOKEN = :tSTRING_BEG
|
@@ -61,14 +61,20 @@ module RuboCop
|
|
61
61
|
successor = tokens[index + 2]
|
62
62
|
|
63
63
|
return unless eligible_token_set?(predecessor, operator, successor)
|
64
|
-
|
65
64
|
return if same_line?(operator, successor)
|
66
65
|
|
67
66
|
next_successor = token_after_last_string(successor, index)
|
68
|
-
|
69
67
|
return unless eligible_next_successor?(next_successor)
|
70
68
|
|
71
|
-
|
69
|
+
register_offense(operator)
|
70
|
+
end
|
71
|
+
|
72
|
+
def register_offense(operator)
|
73
|
+
message = format(MSG, operator: operator.text)
|
74
|
+
|
75
|
+
add_offense(operator.pos, message: message) do |corrector|
|
76
|
+
autocorrect(corrector, operator.pos)
|
77
|
+
end
|
72
78
|
end
|
73
79
|
|
74
80
|
def autocorrect(corrector, operator_range)
|
@@ -41,7 +41,7 @@ module RuboCop
|
|
41
41
|
return if ignored_node?(node)
|
42
42
|
|
43
43
|
return unless (receiver = node.receiver)
|
44
|
-
return unless receiver.any_block_type? && receiver.
|
44
|
+
return unless receiver.any_block_type? && receiver.keywords?
|
45
45
|
|
46
46
|
range = range_between(receiver.loc.end.begin_pos, node.source_range.end_pos)
|
47
47
|
|
@@ -42,6 +42,22 @@ module RuboCop
|
|
42
42
|
# c
|
43
43
|
# end
|
44
44
|
#
|
45
|
+
# # bad
|
46
|
+
# a.nil? ? true : a
|
47
|
+
#
|
48
|
+
# # good
|
49
|
+
# a.nil? || a
|
50
|
+
#
|
51
|
+
# # bad
|
52
|
+
# if a.nil?
|
53
|
+
# true
|
54
|
+
# else
|
55
|
+
# a
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# # good
|
59
|
+
# a.nil? || a
|
60
|
+
#
|
45
61
|
class RedundantCondition < Base
|
46
62
|
include CommentsHelp
|
47
63
|
include RangeHelp
|
@@ -128,6 +144,16 @@ module RuboCop
|
|
128
144
|
# end
|
129
145
|
return true if condition == if_branch
|
130
146
|
|
147
|
+
# e.g.
|
148
|
+
# a.nil? ? true : a
|
149
|
+
# or
|
150
|
+
# if a.nil?
|
151
|
+
# true
|
152
|
+
# else
|
153
|
+
# a
|
154
|
+
# end
|
155
|
+
return true if if_branch_is_true_type_and_else_is_not?(node)
|
156
|
+
|
131
157
|
# e.g.
|
132
158
|
# if foo
|
133
159
|
# @value = foo
|
@@ -146,6 +172,12 @@ module RuboCop
|
|
146
172
|
!use_hash_key_access?(if_branch)
|
147
173
|
end
|
148
174
|
|
175
|
+
def if_branch_is_true_type_and_else_is_not?(node)
|
176
|
+
return false unless node.ternary? || node.if?
|
177
|
+
|
178
|
+
node.if_branch&.true_type? && node.else_branch && !node.else_branch.true_type?
|
179
|
+
end
|
180
|
+
|
149
181
|
def branches_have_assignment?(node)
|
150
182
|
_condition, if_branch, else_branch = *node # rubocop:disable InternalAffairs/NodeDestructuring
|
151
183
|
|
@@ -194,6 +226,8 @@ module RuboCop
|
|
194
226
|
argument_source = if_branch.first_argument.source
|
195
227
|
|
196
228
|
"#{if_branch.receiver.source} #{if_branch.method_name} (#{argument_source}"
|
229
|
+
elsif if_branch.true_type?
|
230
|
+
if_branch.parent.condition.source
|
197
231
|
else
|
198
232
|
if_branch.source
|
199
233
|
end
|
@@ -0,0 +1,250 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Checks for calls to `Kernel#format` or `Kernel#sprintf` that are redundant.
|
7
|
+
#
|
8
|
+
# Calling `format` with only a single string argument is redundant, as it can be
|
9
|
+
# replaced by the string itself.
|
10
|
+
#
|
11
|
+
# Also looks for `format` calls where the arguments are literals that can be
|
12
|
+
# inlined into a string easily. This applies to the `%s`, `%d`, `%i`, `%u`, and
|
13
|
+
# `%f` format specifiers.
|
14
|
+
#
|
15
|
+
# @safety
|
16
|
+
# This cop's autocorrection is unsafe because string object returned by
|
17
|
+
# `format` and `sprintf` are never frozen. If `format('string')` is autocorrected to
|
18
|
+
# `'string'`, `FrozenError` may occur when calling a destructive method like `String#<<`.
|
19
|
+
# Consider using `'string'.dup` instead of `format('string')`.
|
20
|
+
# Additionally, since the necessity of `dup` cannot be determined automatically,
|
21
|
+
# this autocorrection is inherently unsafe.
|
22
|
+
#
|
23
|
+
# [source,ruby]
|
24
|
+
# ----
|
25
|
+
# # frozen_string_literal: true
|
26
|
+
#
|
27
|
+
# format('template').frozen? # => false
|
28
|
+
# 'template'.frozen? # => true
|
29
|
+
# ----
|
30
|
+
#
|
31
|
+
# @example
|
32
|
+
#
|
33
|
+
# # bad
|
34
|
+
# format('the quick brown fox jumps over the lazy dog.')
|
35
|
+
# sprintf('the quick brown fox jumps over the lazy dog.')
|
36
|
+
#
|
37
|
+
# # good
|
38
|
+
# 'the quick brown fox jumps over the lazy dog.'
|
39
|
+
#
|
40
|
+
# # bad
|
41
|
+
# format('%s %s', 'foo', 'bar')
|
42
|
+
# sprintf('%s %s', 'foo', 'bar')
|
43
|
+
#
|
44
|
+
# # good
|
45
|
+
# 'foo bar'
|
46
|
+
#
|
47
|
+
class RedundantFormat < Base
|
48
|
+
extend AutoCorrector
|
49
|
+
|
50
|
+
MSG = 'Use `%<prefer>s` directly instead of `%<method_name>s`.'
|
51
|
+
|
52
|
+
RESTRICT_ON_SEND = %i[format sprintf].to_set.freeze
|
53
|
+
ACCEPTABLE_LITERAL_TYPES = %i[str dstr sym dsym numeric boolean nil].freeze
|
54
|
+
|
55
|
+
# @!method format_without_additional_args?(node)
|
56
|
+
def_node_matcher :format_without_additional_args?, <<~PATTERN
|
57
|
+
(send {(const {nil? cbase} :Kernel) nil?} %RESTRICT_ON_SEND ${str dstr})
|
58
|
+
PATTERN
|
59
|
+
|
60
|
+
# @!method rational_number?(node)
|
61
|
+
def_node_matcher :rational_number?, <<~PATTERN
|
62
|
+
{rational (send int :/ rational) (begin rational) (begin (send int :/ rational))}
|
63
|
+
PATTERN
|
64
|
+
|
65
|
+
# @!method complex_number?(node)
|
66
|
+
def_node_matcher :complex_number?, <<~PATTERN
|
67
|
+
{complex (send int :+ complex) (begin complex) (begin (send int :+ complex))}
|
68
|
+
PATTERN
|
69
|
+
|
70
|
+
# @!method find_hash_value_node(node, name)
|
71
|
+
def_node_search :find_hash_value_node, <<~PATTERN
|
72
|
+
(pair (sym %1) $_)
|
73
|
+
PATTERN
|
74
|
+
|
75
|
+
# @!method splatted_arguments?(node)
|
76
|
+
def_node_matcher :splatted_arguments?, <<~PATTERN
|
77
|
+
(send _ %RESTRICT_ON_SEND <{
|
78
|
+
splat
|
79
|
+
(hash <kwsplat ...>)
|
80
|
+
} ...>)
|
81
|
+
PATTERN
|
82
|
+
|
83
|
+
def on_send(node)
|
84
|
+
format_without_additional_args?(node) do |value|
|
85
|
+
replacement = value.source
|
86
|
+
|
87
|
+
add_offense(node, message: message(node, replacement)) do |corrector|
|
88
|
+
corrector.replace(node, replacement)
|
89
|
+
end
|
90
|
+
return
|
91
|
+
end
|
92
|
+
|
93
|
+
detect_unnecessary_fields(node)
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def message(node, prefer)
|
99
|
+
format(MSG, prefer: prefer, method_name: node.method_name)
|
100
|
+
end
|
101
|
+
|
102
|
+
def detect_unnecessary_fields(node)
|
103
|
+
return unless node.first_argument&.str_type?
|
104
|
+
|
105
|
+
string = node.first_argument.value
|
106
|
+
arguments = node.arguments[1..]
|
107
|
+
|
108
|
+
return unless string && arguments.any?
|
109
|
+
return if splatted_arguments?(node)
|
110
|
+
|
111
|
+
register_all_fields_literal(node, string, arguments)
|
112
|
+
end
|
113
|
+
|
114
|
+
def register_all_fields_literal(node, string, arguments)
|
115
|
+
return unless all_fields_literal?(string, arguments.dup)
|
116
|
+
|
117
|
+
formatted_string = format(string, *argument_values(arguments))
|
118
|
+
replacement = quote(formatted_string, node)
|
119
|
+
|
120
|
+
add_offense(node, message: message(node, replacement)) do |corrector|
|
121
|
+
corrector.replace(node, replacement)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def all_fields_literal?(string, arguments)
|
126
|
+
count = 0
|
127
|
+
sequences = RuboCop::Cop::Utils::FormatString.new(string).format_sequences
|
128
|
+
return false unless sequences.any?
|
129
|
+
|
130
|
+
sequences.each do |sequence|
|
131
|
+
next if sequence.percent?
|
132
|
+
|
133
|
+
hash = arguments.detect(&:hash_type?)
|
134
|
+
next unless (argument = find_argument(sequence, arguments, hash))
|
135
|
+
next unless matching_argument?(sequence, argument)
|
136
|
+
|
137
|
+
count += 1
|
138
|
+
end
|
139
|
+
|
140
|
+
sequences.size == count
|
141
|
+
end
|
142
|
+
|
143
|
+
def find_argument(sequence, arguments, hash)
|
144
|
+
if hash && (sequence.annotated? || sequence.template?)
|
145
|
+
find_hash_value_node(hash, sequence.name.to_sym).first
|
146
|
+
elsif sequence.arg_number
|
147
|
+
arguments[sequence.arg_number.to_i - 1]
|
148
|
+
else
|
149
|
+
# If the specifier contains `*`, the following arguments will be used
|
150
|
+
# to specify the width and can be ignored.
|
151
|
+
(sequence.arity - 1).times { arguments.shift }
|
152
|
+
arguments.shift
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def matching_argument?(sequence, argument)
|
157
|
+
# Template specifiers don't give a type, any acceptable literal type is ok.
|
158
|
+
return argument.type?(*ACCEPTABLE_LITERAL_TYPES) if sequence.template?
|
159
|
+
|
160
|
+
# An argument matches a specifier if it can be easily converted
|
161
|
+
# to that type.
|
162
|
+
case sequence.type
|
163
|
+
when 's'
|
164
|
+
argument.type?(*ACCEPTABLE_LITERAL_TYPES)
|
165
|
+
when 'd', 'i', 'u'
|
166
|
+
integer?(argument)
|
167
|
+
when 'f'
|
168
|
+
float?(argument)
|
169
|
+
else
|
170
|
+
false
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def numeric?(argument)
|
175
|
+
argument.type?(:numeric, :str) ||
|
176
|
+
rational_number?(argument) ||
|
177
|
+
complex_number?(argument)
|
178
|
+
end
|
179
|
+
|
180
|
+
def integer?(argument)
|
181
|
+
numeric?(argument) && Integer(argument_value(argument), exception: false)
|
182
|
+
end
|
183
|
+
|
184
|
+
def float?(argument)
|
185
|
+
numeric?(argument) && Float(argument_value(argument), exception: false)
|
186
|
+
end
|
187
|
+
|
188
|
+
# Add correct quotes to the formatted string, preferring retaining the existing
|
189
|
+
# quotes if possible.
|
190
|
+
def quote(string, node)
|
191
|
+
str_node = node.first_argument
|
192
|
+
start_delimiter = str_node.loc.begin.source
|
193
|
+
end_delimiter = str_node.loc.end.source
|
194
|
+
|
195
|
+
# If there is any interpolation, the delimiters need to be changed potentially
|
196
|
+
if node.each_descendant(:dstr, :dsym).any?
|
197
|
+
case start_delimiter
|
198
|
+
when "'"
|
199
|
+
start_delimiter = end_delimiter = '"'
|
200
|
+
when /\A%q(.)/
|
201
|
+
start_delimiter = "%Q#{Regexp.last_match[1]}"
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
"#{start_delimiter}#{string}#{end_delimiter}"
|
206
|
+
end
|
207
|
+
|
208
|
+
def argument_values(arguments)
|
209
|
+
arguments.map { |argument| argument_value(argument) }
|
210
|
+
end
|
211
|
+
|
212
|
+
def argument_value(argument)
|
213
|
+
argument = argument.children.first if argument.begin_type?
|
214
|
+
|
215
|
+
if argument.dsym_type?
|
216
|
+
dsym_value(argument)
|
217
|
+
elsif argument.hash_type?
|
218
|
+
hash_value(argument)
|
219
|
+
elsif rational_number?(argument)
|
220
|
+
rational_value(argument)
|
221
|
+
elsif complex_number?(argument)
|
222
|
+
complex_value(argument)
|
223
|
+
elsif argument.respond_to?(:value)
|
224
|
+
argument.value
|
225
|
+
else
|
226
|
+
argument.source
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def dsym_value(dsym_node)
|
231
|
+
dsym_node.children.first.source
|
232
|
+
end
|
233
|
+
|
234
|
+
def hash_value(hash_node)
|
235
|
+
hash_node.each_pair.with_object({}) do |pair, hash|
|
236
|
+
hash[pair.key.value] = argument_value(pair.value)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def rational_value(rational_node)
|
241
|
+
rational_node.source.to_r
|
242
|
+
end
|
243
|
+
|
244
|
+
def complex_value(complex_node)
|
245
|
+
Complex(complex_node.source)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
@@ -13,14 +13,16 @@ module RuboCop
|
|
13
13
|
# # good
|
14
14
|
# x if y.z.nil?
|
15
15
|
#
|
16
|
-
class RedundantParentheses < Base
|
16
|
+
class RedundantParentheses < Base # rubocop:disable Metrics/ClassLength
|
17
17
|
include Parentheses
|
18
18
|
extend AutoCorrector
|
19
19
|
|
20
20
|
ALLOWED_NODE_TYPES = %i[and or send splat kwsplat].freeze
|
21
21
|
|
22
22
|
# @!method square_brackets?(node)
|
23
|
-
def_node_matcher :square_brackets?,
|
23
|
+
def_node_matcher :square_brackets?, <<~PATTERN
|
24
|
+
(send `{(send _recv _msg) str array hash const #variable?} :[] ...)
|
25
|
+
PATTERN
|
24
26
|
|
25
27
|
# @!method method_node_and_args(node)
|
26
28
|
def_node_matcher :method_node_and_args, '$(call _recv _msg $...)'
|
@@ -39,6 +41,10 @@ module RuboCop
|
|
39
41
|
|
40
42
|
private
|
41
43
|
|
44
|
+
def variable?(node)
|
45
|
+
node.respond_to?(:variable?) && node.variable?
|
46
|
+
end
|
47
|
+
|
42
48
|
def parens_allowed?(node)
|
43
49
|
empty_parentheses?(node) ||
|
44
50
|
first_arg_begins_with_hash_literal?(node) ||
|
@@ -128,6 +134,8 @@ module RuboCop
|
|
128
134
|
node = begin_node.children.first
|
129
135
|
|
130
136
|
if (message = find_offense_message(begin_node, node))
|
137
|
+
begin_node = begin_node.parent if node.range_type?
|
138
|
+
|
131
139
|
return offense(begin_node, message)
|
132
140
|
end
|
133
141
|
|
@@ -137,7 +145,7 @@ module RuboCop
|
|
137
145
|
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
138
146
|
def find_offense_message(begin_node, node)
|
139
147
|
return 'a keyword' if keyword_with_redundant_parentheses?(node)
|
140
|
-
return 'a literal' if disallowed_literal?(begin_node, node)
|
148
|
+
return 'a literal' if node.literal? && disallowed_literal?(begin_node, node)
|
141
149
|
return 'a variable' if node.variable?
|
142
150
|
return 'a constant' if node.const_type?
|
143
151
|
if node.assignment? && (begin_node.parent.nil? || begin_node.parent.begin_type?)
|
@@ -207,7 +215,13 @@ module RuboCop
|
|
207
215
|
end
|
208
216
|
|
209
217
|
def disallowed_literal?(begin_node, node)
|
210
|
-
|
218
|
+
if node.range_type?
|
219
|
+
return false unless (parent = begin_node.parent)
|
220
|
+
|
221
|
+
parent.begin_type? && parent.children.one?
|
222
|
+
else
|
223
|
+
!raised_to_power_negative_numeric?(begin_node, node)
|
224
|
+
end
|
211
225
|
end
|
212
226
|
|
213
227
|
def raised_to_power_negative_numeric?(begin_node, node)
|
@@ -58,7 +58,7 @@ module RuboCop
|
|
58
58
|
# rubocop:disable Metrics/AbcSize
|
59
59
|
def on_lvasgn(node)
|
60
60
|
return unless (rhs = node.rhs)
|
61
|
-
return unless rhs.
|
61
|
+
return unless rhs.type?(:any_block, :call) && method_returning_self?(rhs.method_name)
|
62
62
|
return unless (receiver = rhs.receiver)
|
63
63
|
|
64
64
|
receiver_type = ASSIGNMENT_TYPE_TO_RECEIVER_TYPE[node.type]
|
@@ -8,9 +8,9 @@ module RuboCop
|
|
8
8
|
#
|
9
9
|
# Endless methods added in Ruby 3.0 are also accepted by this cop.
|
10
10
|
#
|
11
|
-
# If `Style/EndlessMethod` is enabled with `EnforcedStyle: allow_single_line`
|
12
|
-
# `
|
13
|
-
# methods if there is only one statement in the body.
|
11
|
+
# If `Style/EndlessMethod` is enabled with `EnforcedStyle: allow_single_line`, `allow_always`,
|
12
|
+
# `require_single_line`, or `require_always`, single-line methods will be autocorrected
|
13
|
+
# to endless methods if there is only one statement in the body.
|
14
14
|
#
|
15
15
|
# @example
|
16
16
|
# # bad
|