rubocop 1.56.0 → 1.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/config/default.yml +12 -6
- data/lib/rubocop/cli.rb +1 -1
- data/lib/rubocop/cop/autocorrect_logic.rb +3 -1
- data/lib/rubocop/cop/bundler/duplicated_group.rb +54 -8
- data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +7 -4
- data/lib/rubocop/cop/internal_affairs/example_description.rb +42 -22
- data/lib/rubocop/cop/internal_affairs/redundant_method_dispatch_node.rb +11 -2
- data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +2 -0
- data/lib/rubocop/cop/layout/dot_position.rb +1 -5
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +41 -8
- data/lib/rubocop/cop/layout/heredoc_indentation.rb +3 -0
- data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
- data/lib/rubocop/cop/layout/leading_comment_space.rb +1 -1
- data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +17 -9
- data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +13 -2
- data/lib/rubocop/cop/layout/redundant_line_break.rb +5 -3
- data/lib/rubocop/cop/layout/space_after_not.rb +1 -1
- data/lib/rubocop/cop/layout/space_inside_parens.rb +1 -1
- data/lib/rubocop/cop/lint/debugger.rb +10 -1
- data/lib/rubocop/cop/lint/empty_block.rb +1 -1
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
- data/lib/rubocop/cop/lint/mixed_case_range.rb +1 -1
- data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +10 -7
- data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -0
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +16 -4
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +11 -4
- data/lib/rubocop/cop/lint/to_enum_arguments.rb +5 -3
- data/lib/rubocop/cop/lint/useless_assignment.rb +38 -12
- data/lib/rubocop/cop/metrics/block_length.rb +1 -1
- data/lib/rubocop/cop/metrics/class_length.rb +2 -2
- data/lib/rubocop/cop/metrics/method_length.rb +1 -1
- data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +2 -2
- data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +3 -2
- data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +5 -7
- data/lib/rubocop/cop/naming/file_name.rb +1 -1
- data/lib/rubocop/cop/style/arguments_forwarding.rb +22 -11
- data/lib/rubocop/cop/style/array_intersect.rb +13 -5
- data/lib/rubocop/cop/style/class_equality_comparison.rb +5 -0
- data/lib/rubocop/cop/style/collection_methods.rb +2 -0
- data/lib/rubocop/cop/style/combinable_loops.rb +3 -2
- data/lib/rubocop/cop/style/concat_array_literals.rb +1 -1
- data/lib/rubocop/cop/style/empty_case_condition.rb +6 -1
- data/lib/rubocop/cop/style/for.rb +1 -1
- data/lib/rubocop/cop/style/format_string.rb +24 -3
- data/lib/rubocop/cop/style/guard_clause.rb +26 -0
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +17 -3
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +3 -1
- data/lib/rubocop/cop/style/multiline_block_chain.rb +1 -1
- data/lib/rubocop/cop/style/multiline_ternary_operator.rb +1 -1
- data/lib/rubocop/cop/style/nested_ternary_operator.rb +3 -11
- data/lib/rubocop/cop/style/open_struct_use.rb +1 -1
- data/lib/rubocop/cop/style/operator_method_call.rb +6 -0
- data/lib/rubocop/cop/style/redundant_begin.rb +9 -1
- data/lib/rubocop/cop/style/redundant_conditional.rb +1 -9
- data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +69 -6
- data/lib/rubocop/cop/style/redundant_filter_chain.rb +18 -2
- data/lib/rubocop/cop/style/redundant_parentheses.rb +24 -6
- data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +5 -0
- data/lib/rubocop/cop/style/return_nil.rb +6 -2
- data/lib/rubocop/cop/style/single_line_do_end_block.rb +65 -0
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +3 -1
- data/lib/rubocop/cop/style/symbol_array.rb +12 -11
- data/lib/rubocop/cop/style/yoda_expression.rb +8 -7
- data/lib/rubocop/cop/utils/regexp_ranges.rb +26 -13
- data/lib/rubocop/file_finder.rb +4 -7
- data/lib/rubocop/lsp/routes.rb +4 -1
- data/lib/rubocop/magic_comment.rb +12 -10
- data/lib/rubocop/rspec/shared_contexts.rb +2 -3
- data/lib/rubocop/server/cache.rb +1 -0
- data/lib/rubocop/target_ruby.rb +9 -5
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +1 -0
- metadata +13 -12
|
@@ -4,13 +4,25 @@ module RuboCop
|
|
|
4
4
|
module Cop
|
|
5
5
|
module Style
|
|
6
6
|
# Enforces the use of a single string formatting utility.
|
|
7
|
-
# Valid options include Kernel#format
|
|
7
|
+
# Valid options include `Kernel#format`, `Kernel#sprintf`, and `String#%`.
|
|
8
8
|
#
|
|
9
|
-
# The detection of String
|
|
9
|
+
# The detection of `String#%` cannot be implemented in a reliable
|
|
10
10
|
# manner for all cases, so only two scenarios are considered -
|
|
11
11
|
# if the first argument is a string literal and if the second
|
|
12
12
|
# argument is an array literal.
|
|
13
13
|
#
|
|
14
|
+
# Autocorrection will be applied when using argument is a literal or known built-in conversion
|
|
15
|
+
# methods such as `to_d`, `to_f`, `to_h`, `to_i`, `to_r`, `to_s`, and `to_sym` on variables,
|
|
16
|
+
# provided that their return value is not an array. For example, when using `to_s`,
|
|
17
|
+
# `'%s' % [1, 2, 3].to_s` can be autocorrected without any incompatibility:
|
|
18
|
+
#
|
|
19
|
+
# [source,ruby]
|
|
20
|
+
# ----
|
|
21
|
+
# '%s' % [1, 2, 3] #=> '1'
|
|
22
|
+
# format('%s', [1, 2, 3]) #=> '[1, 2, 3]'
|
|
23
|
+
# '%s' % [1, 2, 3].to_s #=> '[1, 2, 3]'
|
|
24
|
+
# ----
|
|
25
|
+
#
|
|
14
26
|
# @example EnforcedStyle: format (default)
|
|
15
27
|
# # bad
|
|
16
28
|
# puts sprintf('%10s', 'hoge')
|
|
@@ -42,6 +54,9 @@ module RuboCop
|
|
|
42
54
|
MSG = 'Favor `%<prefer>s` over `%<current>s`.'
|
|
43
55
|
RESTRICT_ON_SEND = %i[format sprintf %].freeze
|
|
44
56
|
|
|
57
|
+
# Known conversion methods whose return value is not an array.
|
|
58
|
+
AUTOCORRECTABLE_METHODS = %i[to_d to_f to_h to_i to_r to_s to_sym].freeze
|
|
59
|
+
|
|
45
60
|
# @!method formatter(node)
|
|
46
61
|
def_node_matcher :formatter, <<~PATTERN
|
|
47
62
|
{
|
|
@@ -53,7 +68,7 @@ module RuboCop
|
|
|
53
68
|
|
|
54
69
|
# @!method variable_argument?(node)
|
|
55
70
|
def_node_matcher :variable_argument?, <<~PATTERN
|
|
56
|
-
(send {str dstr} :%
|
|
71
|
+
(send {str dstr} :% #autocorrectable?)
|
|
57
72
|
PATTERN
|
|
58
73
|
|
|
59
74
|
def on_send(node)
|
|
@@ -70,6 +85,12 @@ module RuboCop
|
|
|
70
85
|
|
|
71
86
|
private
|
|
72
87
|
|
|
88
|
+
def autocorrectable?(node)
|
|
89
|
+
return true if node.lvar_type?
|
|
90
|
+
|
|
91
|
+
node.send_type? && !AUTOCORRECTABLE_METHODS.include?(node.method_name)
|
|
92
|
+
end
|
|
93
|
+
|
|
73
94
|
def message(detected_style)
|
|
74
95
|
format(MSG, prefer: method_name(style), current: method_name(detected_style))
|
|
75
96
|
end
|
|
@@ -55,6 +55,25 @@ module RuboCop
|
|
|
55
55
|
# foo || raise('exception') if something
|
|
56
56
|
# ok
|
|
57
57
|
#
|
|
58
|
+
# # bad
|
|
59
|
+
# define_method(:test) do
|
|
60
|
+
# if something
|
|
61
|
+
# work
|
|
62
|
+
# end
|
|
63
|
+
# end
|
|
64
|
+
#
|
|
65
|
+
# # good
|
|
66
|
+
# define_method(:test) do
|
|
67
|
+
# return unless something
|
|
68
|
+
#
|
|
69
|
+
# work
|
|
70
|
+
# end
|
|
71
|
+
#
|
|
72
|
+
# # also good
|
|
73
|
+
# define_method(:test) do
|
|
74
|
+
# work if something
|
|
75
|
+
# end
|
|
76
|
+
#
|
|
58
77
|
# @example AllowConsecutiveConditionals: false (default)
|
|
59
78
|
# # bad
|
|
60
79
|
# def test
|
|
@@ -110,6 +129,13 @@ module RuboCop
|
|
|
110
129
|
end
|
|
111
130
|
alias on_defs on_def
|
|
112
131
|
|
|
132
|
+
def on_block(node)
|
|
133
|
+
return unless node.method?(:define_method) || node.method?(:define_singleton_method)
|
|
134
|
+
|
|
135
|
+
on_def(node)
|
|
136
|
+
end
|
|
137
|
+
alias on_numblock on_block
|
|
138
|
+
|
|
113
139
|
def on_if(node)
|
|
114
140
|
return if accepted_form?(node)
|
|
115
141
|
|
|
@@ -136,7 +136,7 @@ module RuboCop
|
|
|
136
136
|
|
|
137
137
|
private
|
|
138
138
|
|
|
139
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
139
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
140
140
|
def check_branches(node, branches)
|
|
141
141
|
# return if any branch is empty. An empty branch can be an `if`
|
|
142
142
|
# without an `else` or a branch that contains only comments.
|
|
@@ -149,9 +149,15 @@ module RuboCop
|
|
|
149
149
|
branches.any? { |branch| single_child_branch?(branch) }
|
|
150
150
|
|
|
151
151
|
heads = branches.map { |branch| head(branch) }
|
|
152
|
-
|
|
152
|
+
|
|
153
|
+
return unless duplicated_expressions?(node, heads)
|
|
154
|
+
|
|
155
|
+
condition_variable = assignable_condition_value(node)
|
|
156
|
+
return if heads.first.assignment? && condition_variable == heads.first.name.to_s
|
|
157
|
+
|
|
158
|
+
check_expressions(node, heads, :before_condition)
|
|
153
159
|
end
|
|
154
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
160
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
155
161
|
|
|
156
162
|
def duplicated_expressions?(node, expressions)
|
|
157
163
|
unique_expressions = expressions.uniq
|
|
@@ -164,6 +170,14 @@ module RuboCop
|
|
|
164
170
|
node.condition.child_nodes.none? { |n| n.source == lhs.source if n.variable? }
|
|
165
171
|
end
|
|
166
172
|
|
|
173
|
+
def assignable_condition_value(node)
|
|
174
|
+
if node.condition.call_type?
|
|
175
|
+
(receiver = node.condition.receiver) ? receiver.source : node.condition.source
|
|
176
|
+
elsif node.condition.variable?
|
|
177
|
+
node.condition.source
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
167
181
|
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
168
182
|
def check_expressions(node, expressions, insert_position)
|
|
169
183
|
return if expressions.any?(&:nil?)
|
|
@@ -146,7 +146,9 @@ module RuboCop
|
|
|
146
146
|
end
|
|
147
147
|
|
|
148
148
|
def call_in_match_pattern?(node)
|
|
149
|
-
node.parent
|
|
149
|
+
return false unless (parent = node.parent)
|
|
150
|
+
|
|
151
|
+
parent.match_pattern_type? || parent.match_pattern_p_type?
|
|
150
152
|
end
|
|
151
153
|
|
|
152
154
|
def hash_literal_in_arguments?(node)
|
|
@@ -28,7 +28,7 @@ module RuboCop
|
|
|
28
28
|
MSG = 'Avoid multi-line chains of blocks.'
|
|
29
29
|
|
|
30
30
|
def on_block(node)
|
|
31
|
-
node.send_node.each_node(:send) do |send_node|
|
|
31
|
+
node.send_node.each_node(:send, :csend) do |send_node|
|
|
32
32
|
receiver = send_node.receiver
|
|
33
33
|
|
|
34
34
|
next unless (receiver&.block_type? || receiver&.numblock_type?) && receiver&.multiline?
|
|
@@ -39,7 +39,7 @@ module RuboCop
|
|
|
39
39
|
|
|
40
40
|
MSG_IF = 'Avoid multi-line ternary operators, use `if` or `unless` instead.'
|
|
41
41
|
MSG_SINGLE_LINE = 'Avoid multi-line ternary operators, use single-line instead.'
|
|
42
|
-
SINGLE_LINE_TYPES = %i[return break next send].freeze
|
|
42
|
+
SINGLE_LINE_TYPES = %i[return break next send csend].freeze
|
|
43
43
|
|
|
44
44
|
def on_if(node)
|
|
45
45
|
return unless offense?(node)
|
|
@@ -27,24 +27,16 @@ module RuboCop
|
|
|
27
27
|
|
|
28
28
|
node.each_descendant(:if).select(&:ternary?).each do |nested_ternary|
|
|
29
29
|
add_offense(nested_ternary) do |corrector|
|
|
30
|
-
|
|
31
|
-
next if part_of_ignored_node?(if_node)
|
|
30
|
+
next if part_of_ignored_node?(node)
|
|
32
31
|
|
|
33
|
-
autocorrect(corrector,
|
|
34
|
-
ignore_node(
|
|
32
|
+
autocorrect(corrector, node)
|
|
33
|
+
ignore_node(node)
|
|
35
34
|
end
|
|
36
35
|
end
|
|
37
36
|
end
|
|
38
37
|
|
|
39
38
|
private
|
|
40
39
|
|
|
41
|
-
def if_node(node)
|
|
42
|
-
node = node.parent
|
|
43
|
-
return node if node.if_type?
|
|
44
|
-
|
|
45
|
-
if_node(node)
|
|
46
|
-
end
|
|
47
|
-
|
|
48
40
|
def autocorrect(corrector, if_node)
|
|
49
41
|
replace_loc_and_whitespace(corrector, if_node.loc.question, "\n")
|
|
50
42
|
replace_loc_and_whitespace(corrector, if_node.loc.colon, "\nelse\n")
|
|
@@ -45,7 +45,7 @@ module RuboCop
|
|
|
45
45
|
MSG = 'Avoid using `OpenStruct`; use `Struct`, `Hash`, a class or test doubles instead.'
|
|
46
46
|
|
|
47
47
|
# @!method uses_open_struct?(node)
|
|
48
|
-
def_node_matcher :uses_open_struct?,
|
|
48
|
+
def_node_matcher :uses_open_struct?, <<~PATTERN
|
|
49
49
|
(const {nil? (cbase)} :OpenStruct)
|
|
50
50
|
PATTERN
|
|
51
51
|
|
|
@@ -23,6 +23,7 @@ module RuboCop
|
|
|
23
23
|
MSG = 'Redundant dot detected.'
|
|
24
24
|
RESTRICT_ON_SEND = %i[| ^ & <=> == === =~ > >= < <= << >> + - * / % ** ~ ! != !~].freeze
|
|
25
25
|
|
|
26
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
|
26
27
|
def on_send(node)
|
|
27
28
|
return unless (dot = node.loc.dot)
|
|
28
29
|
return if node.receiver.const_type? || !node.arguments.one?
|
|
@@ -33,8 +34,12 @@ module RuboCop
|
|
|
33
34
|
add_offense(dot) do |corrector|
|
|
34
35
|
wrap_in_parentheses_if_chained(corrector, node)
|
|
35
36
|
corrector.replace(dot, ' ')
|
|
37
|
+
|
|
38
|
+
selector = node.loc.selector
|
|
39
|
+
corrector.insert_after(selector, ' ') if selector.end_pos == rhs.source_range.begin_pos
|
|
36
40
|
end
|
|
37
41
|
end
|
|
42
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
|
38
43
|
|
|
39
44
|
private
|
|
40
45
|
|
|
@@ -54,6 +59,7 @@ module RuboCop
|
|
|
54
59
|
|
|
55
60
|
def wrap_in_parentheses_if_chained(corrector, node)
|
|
56
61
|
return unless node.parent&.call_type?
|
|
62
|
+
return if node.parent.first_argument == node
|
|
57
63
|
|
|
58
64
|
operator = node.loc.selector
|
|
59
65
|
|
|
@@ -114,7 +114,7 @@ module RuboCop
|
|
|
114
114
|
if node.parent&.assignment?
|
|
115
115
|
replace_begin_with_statement(corrector, offense_range, node)
|
|
116
116
|
else
|
|
117
|
-
corrector
|
|
117
|
+
remove_begin(corrector, offense_range, node)
|
|
118
118
|
end
|
|
119
119
|
|
|
120
120
|
if use_modifier_form_after_multiline_begin_block?(node)
|
|
@@ -136,6 +136,14 @@ module RuboCop
|
|
|
136
136
|
restore_removed_comments(corrector, offense_range, node, first_child)
|
|
137
137
|
end
|
|
138
138
|
|
|
139
|
+
def remove_begin(corrector, offense_range, node)
|
|
140
|
+
if node.parent.respond_to?(:endless?) && node.parent.endless?
|
|
141
|
+
offense_range = range_with_surrounding_space(offense_range, newlines: true)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
corrector.remove(offense_range)
|
|
145
|
+
end
|
|
146
|
+
|
|
139
147
|
# Restore comments that occur between "begin" and "first_child".
|
|
140
148
|
# These comments will be moved to above the assignment line.
|
|
141
149
|
def restore_removed_comments(corrector, offense_range, node, first_child)
|
|
@@ -70,19 +70,11 @@ module RuboCop
|
|
|
70
70
|
|
|
71
71
|
def replacement_condition(node)
|
|
72
72
|
condition = node.condition.source
|
|
73
|
-
expression =
|
|
73
|
+
expression = redundant_condition_inverted?(node) ? "!(#{condition})" : condition
|
|
74
74
|
|
|
75
75
|
node.elsif? ? indented_else_node(expression, node) : expression
|
|
76
76
|
end
|
|
77
77
|
|
|
78
|
-
def invert_expression?(node)
|
|
79
|
-
(
|
|
80
|
-
(node.if? || node.elsif? || node.ternary?) && redundant_condition_inverted?(node)
|
|
81
|
-
) || (
|
|
82
|
-
node.unless? && redundant_condition?(node)
|
|
83
|
-
)
|
|
84
|
-
end
|
|
85
|
-
|
|
86
78
|
def indented_else_node(expression, node)
|
|
87
79
|
"else\n#{indentation(node)}#{expression}"
|
|
88
80
|
end
|
|
@@ -13,25 +13,50 @@ module RuboCop
|
|
|
13
13
|
# # good
|
|
14
14
|
# do_something(foo: bar, baz: qux)
|
|
15
15
|
#
|
|
16
|
+
# # bad
|
|
17
|
+
# do_something(**{foo: bar, baz: qux}.merge(options))
|
|
18
|
+
#
|
|
19
|
+
# # good
|
|
20
|
+
# do_something(foo: bar, baz: qux, **options)
|
|
21
|
+
#
|
|
16
22
|
class RedundantDoubleSplatHashBraces < Base
|
|
17
23
|
extend AutoCorrector
|
|
18
24
|
|
|
19
25
|
MSG = 'Remove the redundant double splat and braces, use keyword arguments directly.'
|
|
26
|
+
MERGE_METHODS = %i[merge merge!].freeze
|
|
20
27
|
|
|
28
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
21
29
|
def on_hash(node)
|
|
22
|
-
return if node.pairs.empty? || node.pairs.any?(&:hash_rocket?)
|
|
30
|
+
return if !node.braces? || node.pairs.empty? || node.pairs.any?(&:hash_rocket?)
|
|
23
31
|
return unless (parent = node.parent)
|
|
24
|
-
return unless
|
|
32
|
+
return unless (kwsplat = node.each_ancestor(:kwsplat).first)
|
|
33
|
+
return if parent.call_type? && !merge_method?(parent)
|
|
25
34
|
|
|
26
|
-
add_offense(
|
|
27
|
-
corrector
|
|
28
|
-
corrector.remove(opening_brace(node))
|
|
29
|
-
corrector.remove(closing_brace(node))
|
|
35
|
+
add_offense(kwsplat) do |corrector|
|
|
36
|
+
autocorrect(corrector, node, kwsplat)
|
|
30
37
|
end
|
|
31
38
|
end
|
|
39
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
32
40
|
|
|
33
41
|
private
|
|
34
42
|
|
|
43
|
+
def autocorrect(corrector, node, kwsplat)
|
|
44
|
+
corrector.remove(kwsplat.loc.operator)
|
|
45
|
+
corrector.remove(opening_brace(node))
|
|
46
|
+
corrector.remove(closing_brace(node))
|
|
47
|
+
|
|
48
|
+
merge_methods = select_merge_method_nodes(kwsplat)
|
|
49
|
+
return if merge_methods.empty?
|
|
50
|
+
|
|
51
|
+
autocorrect_merge_methods(corrector, merge_methods, kwsplat)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def select_merge_method_nodes(kwsplat)
|
|
55
|
+
extract_send_methods(kwsplat).select do |node|
|
|
56
|
+
merge_method?(node)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
35
60
|
def opening_brace(node)
|
|
36
61
|
node.loc.begin.join(node.children.first.source_range.begin)
|
|
37
62
|
end
|
|
@@ -39,6 +64,44 @@ module RuboCop
|
|
|
39
64
|
def closing_brace(node)
|
|
40
65
|
node.children.last.source_range.end.join(node.loc.end)
|
|
41
66
|
end
|
|
67
|
+
|
|
68
|
+
def autocorrect_merge_methods(corrector, merge_methods, kwsplat)
|
|
69
|
+
range = range_of_merge_methods(merge_methods)
|
|
70
|
+
|
|
71
|
+
new_kwsplat_arguments = extract_send_methods(kwsplat).map do |descendant|
|
|
72
|
+
convert_to_new_arguments(descendant)
|
|
73
|
+
end
|
|
74
|
+
new_source = new_kwsplat_arguments.compact.reverse.unshift('').join(', ')
|
|
75
|
+
|
|
76
|
+
corrector.replace(range, new_source)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def range_of_merge_methods(merge_methods)
|
|
80
|
+
begin_merge_method = merge_methods.last
|
|
81
|
+
end_merge_method = merge_methods.first
|
|
82
|
+
|
|
83
|
+
begin_merge_method.loc.dot.begin.join(end_merge_method.source_range.end)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def extract_send_methods(kwsplat)
|
|
87
|
+
@extract_send_methods ||= kwsplat.each_descendant(:send, :csend)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def convert_to_new_arguments(node)
|
|
91
|
+
return unless merge_method?(node)
|
|
92
|
+
|
|
93
|
+
node.arguments.map do |arg|
|
|
94
|
+
if arg.hash_type?
|
|
95
|
+
arg.source
|
|
96
|
+
else
|
|
97
|
+
"**#{arg.source}"
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def merge_method?(node)
|
|
103
|
+
MERGE_METHODS.include?(node.method_name)
|
|
104
|
+
end
|
|
42
105
|
end
|
|
43
106
|
end
|
|
44
107
|
end
|
|
@@ -6,6 +6,12 @@ module RuboCop
|
|
|
6
6
|
# Identifies usages of `any?`, `empty?` or `none?` predicate methods
|
|
7
7
|
# chained to `select`/`filter`/`find_all` and change them to use predicate method instead.
|
|
8
8
|
#
|
|
9
|
+
# @safety
|
|
10
|
+
# This cop's autocorrection is unsafe because `array.select.any?` evaluates all elements
|
|
11
|
+
# through the `select` method, while `array.any?` uses short-circuit evaluation.
|
|
12
|
+
# In other words, `array.select.any?` guarantees the evaluation of every element,
|
|
13
|
+
# but `array.any?` does not necessarily evaluate all of them.
|
|
14
|
+
#
|
|
9
15
|
# @example
|
|
10
16
|
# # bad
|
|
11
17
|
# arr.select { |x| x > 1 }.any?
|
|
@@ -28,6 +34,9 @@ module RuboCop
|
|
|
28
34
|
# # good
|
|
29
35
|
# arr.select { |x| x > 1 }.many?
|
|
30
36
|
#
|
|
37
|
+
# # good
|
|
38
|
+
# arr.select { |x| x > 1 }.present?
|
|
39
|
+
#
|
|
31
40
|
# @example AllCops:ActiveSupportExtensionsEnabled: true
|
|
32
41
|
# # bad
|
|
33
42
|
# arr.select { |x| x > 1 }.many?
|
|
@@ -35,12 +44,18 @@ module RuboCop
|
|
|
35
44
|
# # good
|
|
36
45
|
# arr.many? { |x| x > 1 }
|
|
37
46
|
#
|
|
47
|
+
# # bad
|
|
48
|
+
# arr.select { |x| x > 1 }.present?
|
|
49
|
+
#
|
|
50
|
+
# # good
|
|
51
|
+
# arr.any? { |x| x > 1 }
|
|
52
|
+
#
|
|
38
53
|
class RedundantFilterChain < Base
|
|
39
54
|
extend AutoCorrector
|
|
40
55
|
|
|
41
56
|
MSG = 'Use `%<prefer>s` instead of `%<first_method>s.%<second_method>s`.'
|
|
42
57
|
|
|
43
|
-
RAILS_METHODS = %i[many?].freeze
|
|
58
|
+
RAILS_METHODS = %i[many? present?].freeze
|
|
44
59
|
RESTRICT_ON_SEND = (%i[any? empty? none? one?] + RAILS_METHODS).freeze
|
|
45
60
|
|
|
46
61
|
# @!method select_predicate?(node)
|
|
@@ -58,7 +73,8 @@ module RuboCop
|
|
|
58
73
|
empty?: :none?,
|
|
59
74
|
none?: :none?,
|
|
60
75
|
one?: :one?,
|
|
61
|
-
many?: :many
|
|
76
|
+
many?: :many?,
|
|
77
|
+
present?: :any?
|
|
62
78
|
}.freeze
|
|
63
79
|
private_constant :REPLACEMENT_METHODS
|
|
64
80
|
|
|
@@ -98,7 +98,9 @@ module RuboCop
|
|
|
98
98
|
end
|
|
99
99
|
|
|
100
100
|
def like_method_argument_parentheses?(node)
|
|
101
|
-
node.send_type? && node.
|
|
101
|
+
return false if !node.send_type? && !node.super_type? && !node.yield_type?
|
|
102
|
+
|
|
103
|
+
node.arguments.one? && !node.parenthesized? &&
|
|
102
104
|
!node.arithmetic_operation? && node.first_argument.begin_type?
|
|
103
105
|
end
|
|
104
106
|
|
|
@@ -124,16 +126,32 @@ module RuboCop
|
|
|
124
126
|
|
|
125
127
|
def check(begin_node)
|
|
126
128
|
node = begin_node.children.first
|
|
127
|
-
return offense(begin_node, 'a keyword') if keyword_with_redundant_parentheses?(node)
|
|
128
|
-
return offense(begin_node, 'a literal') if disallowed_literal?(begin_node, node)
|
|
129
|
-
return offense(begin_node, 'a variable') if node.variable?
|
|
130
|
-
return offense(begin_node, 'a constant') if node.const_type?
|
|
131
129
|
|
|
132
|
-
|
|
130
|
+
if (message = find_offense_message(begin_node, node))
|
|
131
|
+
return offense(begin_node, message)
|
|
132
|
+
end
|
|
133
133
|
|
|
134
134
|
check_send(begin_node, node) if node.call_type?
|
|
135
135
|
end
|
|
136
136
|
|
|
137
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
138
|
+
def find_offense_message(begin_node, node)
|
|
139
|
+
return 'a keyword' if keyword_with_redundant_parentheses?(node)
|
|
140
|
+
return 'a literal' if disallowed_literal?(begin_node, node)
|
|
141
|
+
return 'a variable' if node.variable?
|
|
142
|
+
return 'a constant' if node.const_type?
|
|
143
|
+
return 'an interpolated expression' if interpolation?(begin_node)
|
|
144
|
+
|
|
145
|
+
return if begin_node.chained? || !begin_node.parent.nil?
|
|
146
|
+
|
|
147
|
+
if node.and_type? || node.or_type?
|
|
148
|
+
'a logical expression'
|
|
149
|
+
elsif node.respond_to?(:comparison_method?) && node.comparison_method?
|
|
150
|
+
'a comparison expression'
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
154
|
+
|
|
137
155
|
# @!method interpolation?(node)
|
|
138
156
|
def_node_matcher :interpolation?, '[^begin ^^dstr]'
|
|
139
157
|
|
|
@@ -75,6 +75,11 @@ module RuboCop
|
|
|
75
75
|
add_offense(offense_branch) do |corrector|
|
|
76
76
|
assignment_value = opposite_branch ? opposite_branch.source : 'nil'
|
|
77
77
|
replacement = "#{assignment_value} #{keyword} #{if_node.condition.source}"
|
|
78
|
+
if opposite_branch.respond_to?(:heredoc?) && opposite_branch.heredoc?
|
|
79
|
+
replacement += opposite_branch.loc.heredoc_end.with(
|
|
80
|
+
begin_pos: opposite_branch.source_range.end_pos
|
|
81
|
+
).source
|
|
82
|
+
end
|
|
78
83
|
|
|
79
84
|
corrector.replace(if_node, replacement)
|
|
80
85
|
end
|
|
@@ -3,9 +3,13 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Style
|
|
6
|
-
# Enforces consistency between
|
|
6
|
+
# Enforces consistency between `return nil` and `return`.
|
|
7
7
|
#
|
|
8
|
-
#
|
|
8
|
+
# This cop is disabled by default. Because there seems to be a perceived semantic difference
|
|
9
|
+
# between `return` and `return nil`. The former can be seen as just halting evaluation,
|
|
10
|
+
# while the latter might be used when the return value is of specific concern.
|
|
11
|
+
#
|
|
12
|
+
# Supported styles are `return` and `return_nil`.
|
|
9
13
|
#
|
|
10
14
|
# @example EnforcedStyle: return (default)
|
|
11
15
|
# # bad
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Style
|
|
6
|
+
# Checks for single-line `do`...`end` block.
|
|
7
|
+
#
|
|
8
|
+
# In practice a single line `do`...`end` is autocorrected when `EnforcedStyle: semantic`
|
|
9
|
+
# in `Style/BlockDelimiters`. The autocorrection maintains the `do` ... `end` syntax to
|
|
10
|
+
# preserve semantics and does not change it to `{`...`}` block.
|
|
11
|
+
#
|
|
12
|
+
# @example
|
|
13
|
+
#
|
|
14
|
+
# # bad
|
|
15
|
+
# foo do |arg| bar(arg) end
|
|
16
|
+
#
|
|
17
|
+
# # good
|
|
18
|
+
# foo do |arg|
|
|
19
|
+
# bar(arg)
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
# # bad
|
|
23
|
+
# ->(arg) do bar(arg) end
|
|
24
|
+
#
|
|
25
|
+
# # good
|
|
26
|
+
# ->(arg) { bar(arg) }
|
|
27
|
+
#
|
|
28
|
+
class SingleLineDoEndBlock < Base
|
|
29
|
+
extend AutoCorrector
|
|
30
|
+
|
|
31
|
+
MSG = 'Prefer multiline `do`...`end` block.'
|
|
32
|
+
|
|
33
|
+
def on_block(node)
|
|
34
|
+
return if !node.single_line? || node.braces?
|
|
35
|
+
|
|
36
|
+
add_offense(node) do |corrector|
|
|
37
|
+
corrector.insert_after(do_line(node), "\n")
|
|
38
|
+
|
|
39
|
+
node_body = node.body
|
|
40
|
+
|
|
41
|
+
if node_body.respond_to?(:heredoc?) && node_body.heredoc?
|
|
42
|
+
corrector.remove(node.loc.end)
|
|
43
|
+
corrector.insert_after(node_body.loc.heredoc_end, "\nend")
|
|
44
|
+
else
|
|
45
|
+
corrector.insert_after(node_body, "\n")
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
alias on_numblock on_block
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def do_line(node)
|
|
54
|
+
if node.numblock_type? || node.arguments.children.empty? || node.send_node.lambda_literal?
|
|
55
|
+
node.loc.begin
|
|
56
|
+
else
|
|
57
|
+
node.arguments
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def x(corrector, node); end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -173,7 +173,9 @@ module RuboCop
|
|
|
173
173
|
end
|
|
174
174
|
|
|
175
175
|
def correct_for_comment(corrector, node, if_branch)
|
|
176
|
-
comments = processed_source.ast_with_comments[if_branch]
|
|
176
|
+
comments = processed_source.ast_with_comments[if_branch].select do |comment|
|
|
177
|
+
comment.loc.line < if_branch.condition.first_line
|
|
178
|
+
end
|
|
177
179
|
comment_text = comments.map(&:text).join("\n") << "\n"
|
|
178
180
|
|
|
179
181
|
corrector.insert_before(node.loc.keyword, comment_text) unless comments.empty?
|
|
@@ -50,6 +50,14 @@ module RuboCop
|
|
|
50
50
|
PERCENT_MSG = 'Use `%i` or `%I` for an array of symbols.'
|
|
51
51
|
ARRAY_MSG = 'Use %<prefer>s for an array of symbols.'
|
|
52
52
|
DELIMITERS = ['[', ']', '(', ')'].freeze
|
|
53
|
+
SPECIAL_GVARS = %w[
|
|
54
|
+
$! $" $$ $& $' $* $+ $, $/ $; $: $. $< $= $> $? $@ $\\ $_ $` $~ $0
|
|
55
|
+
$-0 $-F $-I $-K $-W $-a $-d $-i $-l $-p $-v $-w
|
|
56
|
+
].freeze
|
|
57
|
+
REDEFINABLE_OPERATORS = %w(
|
|
58
|
+
| ^ & <=> == === =~ > >= < <= << >>
|
|
59
|
+
+ - * / % ** ~ +@ -@ [] []= ` ! != !~
|
|
60
|
+
).freeze
|
|
53
61
|
|
|
54
62
|
class << self
|
|
55
63
|
attr_accessor :largest_brackets
|
|
@@ -69,6 +77,8 @@ module RuboCop
|
|
|
69
77
|
|
|
70
78
|
def complex_content?(node)
|
|
71
79
|
node.children.any? do |sym|
|
|
80
|
+
return false if DELIMITERS.include?(sym.source)
|
|
81
|
+
|
|
72
82
|
content = *sym
|
|
73
83
|
content = content.map { |c| c.is_a?(AST::Node) ? c.source : c }.join
|
|
74
84
|
content_without_delimiter_pairs = content.gsub(/(\[[^\s\[\]]*\])|(\([^\s\(\)]*\))/, '')
|
|
@@ -107,15 +117,6 @@ module RuboCop
|
|
|
107
117
|
end
|
|
108
118
|
|
|
109
119
|
def symbol_without_quote?(string)
|
|
110
|
-
special_gvars = %w[
|
|
111
|
-
$! $" $$ $& $' $* $+ $, $/ $; $: $. $< $= $> $? $@ $\\ $_ $` $~ $0
|
|
112
|
-
$-0 $-F $-I $-K $-W $-a $-d $-i $-l $-p $-v $-w
|
|
113
|
-
]
|
|
114
|
-
redefinable_operators = %w(
|
|
115
|
-
| ^ & <=> == === =~ > >= < <= << >>
|
|
116
|
-
+ - * / % ** ~ +@ -@ [] []= ` ! != !~
|
|
117
|
-
)
|
|
118
|
-
|
|
119
120
|
# method name
|
|
120
121
|
/\A[a-zA-Z_]\w*[!?]?\z/.match?(string) ||
|
|
121
122
|
# instance / class variable
|
|
@@ -123,8 +124,8 @@ module RuboCop
|
|
|
123
124
|
# global variable
|
|
124
125
|
/\A\$[1-9]\d*\z/.match?(string) ||
|
|
125
126
|
/\A\$[a-zA-Z_]\w*\z/.match?(string) ||
|
|
126
|
-
|
|
127
|
-
|
|
127
|
+
SPECIAL_GVARS.include?(string) ||
|
|
128
|
+
REDEFINABLE_OPERATORS.include?(string)
|
|
128
129
|
end
|
|
129
130
|
end
|
|
130
131
|
end
|