rubocop 1.56.3 → 1.57.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 +1 -1
- data/config/default.yml +12 -0
- data/lib/rubocop/cli.rb +1 -1
- data/lib/rubocop/cop/autocorrect_logic.rb +3 -1
- data/lib/rubocop/cop/internal_affairs/example_description.rb +42 -22
- 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 +8 -0
- data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +18 -3
- 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 +0 -1
- data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -0
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +20 -4
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +11 -4
- 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/utils/code_length_calculator.rb +2 -2
- data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +3 -2
- 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/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/multiline_block_chain.rb +1 -1
- data/lib/rubocop/cop/style/nested_ternary_operator.rb +3 -11
- 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 +86 -5
- data/lib/rubocop/cop/style/redundant_exception.rb +32 -12
- data/lib/rubocop/cop/style/redundant_filter_chain.rb +18 -2
- data/lib/rubocop/cop/style/redundant_parentheses.rb +21 -5
- 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/magic_comment.rb +12 -10
- data/lib/rubocop/server/cache.rb +1 -0
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +1 -0
- metadata +10 -9
@@ -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?)
|
@@ -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?
|
@@ -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")
|
@@ -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,68 @@ 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
30
|
return if node.pairs.empty? || node.pairs.any?(&:hash_rocket?)
|
23
31
|
return unless (parent = node.parent)
|
24
|
-
return
|
32
|
+
return if parent.call_type? && !merge_method?(parent)
|
33
|
+
return unless (kwsplat = node.each_ancestor(:kwsplat).first)
|
34
|
+
return if allowed_double_splat_receiver?(kwsplat)
|
25
35
|
|
26
|
-
add_offense(
|
27
|
-
corrector
|
28
|
-
corrector.remove(opening_brace(node))
|
29
|
-
corrector.remove(closing_brace(node))
|
36
|
+
add_offense(kwsplat) do |corrector|
|
37
|
+
autocorrect(corrector, node, kwsplat)
|
30
38
|
end
|
31
39
|
end
|
40
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
32
41
|
|
33
42
|
private
|
34
43
|
|
44
|
+
def allowed_double_splat_receiver?(kwsplat)
|
45
|
+
return false unless kwsplat.children.first.call_type?
|
46
|
+
|
47
|
+
root_receiver = root_receiver(kwsplat.children.first)
|
48
|
+
|
49
|
+
!root_receiver&.hash_type?
|
50
|
+
end
|
51
|
+
|
52
|
+
def autocorrect(corrector, node, kwsplat)
|
53
|
+
corrector.remove(kwsplat.loc.operator)
|
54
|
+
corrector.remove(opening_brace(node))
|
55
|
+
corrector.remove(closing_brace(node))
|
56
|
+
|
57
|
+
merge_methods = select_merge_method_nodes(kwsplat)
|
58
|
+
return if merge_methods.empty?
|
59
|
+
|
60
|
+
autocorrect_merge_methods(corrector, merge_methods, kwsplat)
|
61
|
+
end
|
62
|
+
|
63
|
+
def root_receiver(node)
|
64
|
+
receiver = node.receiver
|
65
|
+
if receiver&.receiver
|
66
|
+
root_receiver(receiver)
|
67
|
+
else
|
68
|
+
receiver
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def select_merge_method_nodes(kwsplat)
|
73
|
+
extract_send_methods(kwsplat).select do |node|
|
74
|
+
merge_method?(node)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
35
78
|
def opening_brace(node)
|
36
79
|
node.loc.begin.join(node.children.first.source_range.begin)
|
37
80
|
end
|
@@ -39,6 +82,44 @@ module RuboCop
|
|
39
82
|
def closing_brace(node)
|
40
83
|
node.children.last.source_range.end.join(node.loc.end)
|
41
84
|
end
|
85
|
+
|
86
|
+
def autocorrect_merge_methods(corrector, merge_methods, kwsplat)
|
87
|
+
range = range_of_merge_methods(merge_methods)
|
88
|
+
|
89
|
+
new_kwsplat_arguments = extract_send_methods(kwsplat).map do |descendant|
|
90
|
+
convert_to_new_arguments(descendant)
|
91
|
+
end
|
92
|
+
new_source = new_kwsplat_arguments.compact.reverse.unshift('').join(', ')
|
93
|
+
|
94
|
+
corrector.replace(range, new_source)
|
95
|
+
end
|
96
|
+
|
97
|
+
def range_of_merge_methods(merge_methods)
|
98
|
+
begin_merge_method = merge_methods.last
|
99
|
+
end_merge_method = merge_methods.first
|
100
|
+
|
101
|
+
begin_merge_method.loc.dot.begin.join(end_merge_method.source_range.end)
|
102
|
+
end
|
103
|
+
|
104
|
+
def extract_send_methods(kwsplat)
|
105
|
+
kwsplat.each_descendant(:send, :csend)
|
106
|
+
end
|
107
|
+
|
108
|
+
def convert_to_new_arguments(node)
|
109
|
+
return unless merge_method?(node)
|
110
|
+
|
111
|
+
node.arguments.map do |arg|
|
112
|
+
if arg.hash_type?
|
113
|
+
arg.source
|
114
|
+
else
|
115
|
+
"**#{arg.source}"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def merge_method?(node)
|
121
|
+
MERGE_METHODS.include?(node.method_name)
|
122
|
+
end
|
42
123
|
end
|
43
124
|
end
|
44
125
|
end
|
@@ -5,17 +5,21 @@ module RuboCop
|
|
5
5
|
module Style
|
6
6
|
# Checks for RuntimeError as the argument of raise/fail.
|
7
7
|
#
|
8
|
-
# It checks for code like this:
|
9
|
-
#
|
10
8
|
# @example
|
11
|
-
# #
|
9
|
+
# # bad
|
12
10
|
# raise RuntimeError, 'message'
|
13
|
-
#
|
14
|
-
# # Bad
|
15
11
|
# raise RuntimeError.new('message')
|
16
12
|
#
|
17
|
-
# #
|
13
|
+
# # good
|
18
14
|
# raise 'message'
|
15
|
+
#
|
16
|
+
# # bad - message is not a string
|
17
|
+
# raise RuntimeError, Object.new
|
18
|
+
# raise RuntimeError.new(Object.new)
|
19
|
+
#
|
20
|
+
# # good
|
21
|
+
# raise Object.new.to_s
|
22
|
+
#
|
19
23
|
class RedundantException < Base
|
20
24
|
extend AutoCorrector
|
21
25
|
|
@@ -30,26 +34,42 @@ module RuboCop
|
|
30
34
|
fix_exploded(node) || fix_compact(node)
|
31
35
|
end
|
32
36
|
|
37
|
+
private
|
38
|
+
|
33
39
|
def fix_exploded(node)
|
34
40
|
exploded?(node) do |command, message|
|
35
41
|
add_offense(node, message: MSG_1) do |corrector|
|
36
|
-
|
37
|
-
corrector.replace(node, "#{command}(#{message.source})")
|
38
|
-
else
|
39
|
-
corrector.replace(node, "#{command} #{message.source}")
|
40
|
-
end
|
42
|
+
corrector.replace(node, replaced_exploded(node, command, message))
|
41
43
|
end
|
42
44
|
end
|
43
45
|
end
|
44
46
|
|
47
|
+
def replaced_exploded(node, command, message)
|
48
|
+
arg = string_message?(message) ? message.source : "#{message.source}.to_s"
|
49
|
+
arg = node.parenthesized? ? "(#{arg})" : " #{arg}"
|
50
|
+
"#{command}#{arg}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def string_message?(message)
|
54
|
+
message.str_type? || message.dstr_type? || message.xstr_type?
|
55
|
+
end
|
56
|
+
|
45
57
|
def fix_compact(node)
|
46
58
|
compact?(node) do |new_call, message|
|
47
59
|
add_offense(node, message: MSG_2) do |corrector|
|
48
|
-
corrector.replace(new_call, message
|
60
|
+
corrector.replace(new_call, replaced_compact(message))
|
49
61
|
end
|
50
62
|
end
|
51
63
|
end
|
52
64
|
|
65
|
+
def replaced_compact(message)
|
66
|
+
if string_message?(message)
|
67
|
+
message.source
|
68
|
+
else
|
69
|
+
"#{message.source}.to_s"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
53
73
|
# @!method exploded?(node)
|
54
74
|
def_node_matcher :exploded?, <<~PATTERN
|
55
75
|
(send nil? ${:raise :fail} (const {nil? cbase} :RuntimeError) $_)
|
@@ -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
|
|
@@ -126,16 +126,32 @@ module RuboCop
|
|
126
126
|
|
127
127
|
def check(begin_node)
|
128
128
|
node = begin_node.children.first
|
129
|
-
return offense(begin_node, 'a keyword') if keyword_with_redundant_parentheses?(node)
|
130
|
-
return offense(begin_node, 'a literal') if disallowed_literal?(begin_node, node)
|
131
|
-
return offense(begin_node, 'a variable') if node.variable?
|
132
|
-
return offense(begin_node, 'a constant') if node.const_type?
|
133
129
|
|
134
|
-
|
130
|
+
if (message = find_offense_message(begin_node, node))
|
131
|
+
return offense(begin_node, message)
|
132
|
+
end
|
135
133
|
|
136
134
|
check_send(begin_node, node) if node.call_type?
|
137
135
|
end
|
138
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
|
+
|
139
155
|
# @!method interpolation?(node)
|
140
156
|
def_node_matcher :interpolation?, '[^begin ^^dstr]'
|
141
157
|
|
@@ -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
|
@@ -7,7 +7,7 @@ module RuboCop
|
|
7
7
|
class MagicComment
|
8
8
|
# IRB's pattern for matching magic comment tokens.
|
9
9
|
# @see https://github.com/ruby/ruby/blob/b4a55c1/lib/irb/magic-file.rb#L5
|
10
|
-
TOKEN =
|
10
|
+
TOKEN = '(?<token>[[:alnum:]\-_]+)'
|
11
11
|
KEYWORDS = {
|
12
12
|
encoding: '(?:en)?coding',
|
13
13
|
frozen_string_literal: 'frozen[_-]string[_-]literal',
|
@@ -129,7 +129,7 @@ module RuboCop
|
|
129
129
|
# @return [String] if pattern matched
|
130
130
|
# @return [nil] otherwise
|
131
131
|
def extract(pattern)
|
132
|
-
@comment[pattern,
|
132
|
+
@comment[pattern, :token]
|
133
133
|
end
|
134
134
|
|
135
135
|
# Parent to Vim and Emacs magic comment handling.
|
@@ -157,10 +157,10 @@ module RuboCop
|
|
157
157
|
# @return [String] extracted value if it is found
|
158
158
|
# @return [nil] otherwise
|
159
159
|
def match(keyword)
|
160
|
-
pattern = /\A#{keyword}\s*#{self.class::OPERATOR}\s
|
160
|
+
pattern = /\A#{keyword}\s*#{self.class::OPERATOR}\s*#{TOKEN}\z/
|
161
161
|
|
162
162
|
tokens.each do |token|
|
163
|
-
next unless (value = token[pattern,
|
163
|
+
next unless (value = token[pattern, :token])
|
164
164
|
|
165
165
|
return value.downcase
|
166
166
|
end
|
@@ -188,7 +188,7 @@ module RuboCop
|
|
188
188
|
# @see https://www.gnu.org/software/emacs/manual/html_node/emacs/Specify-Coding.html
|
189
189
|
# @see https://github.com/ruby/ruby/blob/3f306dc/parse.y#L6873-L6892 Emacs handling in parse.y
|
190
190
|
class EmacsComment < EditorComment
|
191
|
-
REGEXP = /-\*-(
|
191
|
+
REGEXP = /-\*-(?<token>.+)-\*-/.freeze
|
192
192
|
FORMAT = '# -*- %s -*-'
|
193
193
|
SEPARATOR = ';'
|
194
194
|
OPERATOR = ':'
|
@@ -216,7 +216,7 @@ module RuboCop
|
|
216
216
|
#
|
217
217
|
# comment.encoding # => 'ascii-8bit'
|
218
218
|
class VimComment < EditorComment
|
219
|
-
REGEXP = /#\s*vim:\s*(
|
219
|
+
REGEXP = /#\s*vim:\s*(?<token>.+)/.freeze
|
220
220
|
FORMAT = '# vim: %s'
|
221
221
|
SEPARATOR = ', '
|
222
222
|
OPERATOR = '='
|
@@ -259,9 +259,11 @@ module RuboCop
|
|
259
259
|
# comment2.frozen_string_literal # => nil
|
260
260
|
# comment2.encoding # => 'utf-8'
|
261
261
|
class SimpleComment < MagicComment
|
262
|
+
FSTRING_LITERAL_COMMENT = 'frozen_string_literal:\s*(true|false)'
|
263
|
+
|
262
264
|
# Match `encoding` or `coding`
|
263
265
|
def encoding
|
264
|
-
extract(/\A\s
|
266
|
+
extract(/\A\s*\#\s*(#{FSTRING_LITERAL_COMMENT})?\s*#{KEYWORDS[:encoding]}: (#{TOKEN})/io)
|
265
267
|
end
|
266
268
|
|
267
269
|
# Rewrite the comment without a given token type
|
@@ -283,15 +285,15 @@ module RuboCop
|
|
283
285
|
# Case-insensitive and dashes/underscores are acceptable.
|
284
286
|
# @see https://github.com/ruby/ruby/blob/78b95b4/parse.y#L7134-L7138
|
285
287
|
def extract_frozen_string_literal
|
286
|
-
extract(/\A\s*#\s*#{KEYWORDS[:frozen_string_literal]}:\s
|
288
|
+
extract(/\A\s*#\s*#{KEYWORDS[:frozen_string_literal]}:\s*#{TOKEN}\s*\z/io)
|
287
289
|
end
|
288
290
|
|
289
291
|
def extract_shareable_constant_value
|
290
|
-
extract(/\A\s*#\s*#{KEYWORDS[:shareable_constant_value]}:\s
|
292
|
+
extract(/\A\s*#\s*#{KEYWORDS[:shareable_constant_value]}:\s*#{TOKEN}\s*\z/io)
|
291
293
|
end
|
292
294
|
|
293
295
|
def extract_typed
|
294
|
-
extract(/\A\s*#\s*#{KEYWORDS[:typed]}:\s
|
296
|
+
extract(/\A\s*#\s*#{KEYWORDS[:typed]}:\s*#{TOKEN}\s*\z/io)
|
295
297
|
end
|
296
298
|
end
|
297
299
|
end
|
data/lib/rubocop/server/cache.rb
CHANGED
data/lib/rubocop/version.rb
CHANGED
data/lib/rubocop.rb
CHANGED
@@ -580,6 +580,7 @@ require_relative 'rubocop/cop/style/redundant_regexp_constructor'
|
|
580
580
|
require_relative 'rubocop/cop/style/redundant_self_assignment'
|
581
581
|
require_relative 'rubocop/cop/style/redundant_self_assignment_branch'
|
582
582
|
require_relative 'rubocop/cop/style/require_order'
|
583
|
+
require_relative 'rubocop/cop/style/single_line_do_end_block'
|
583
584
|
require_relative 'rubocop/cop/style/sole_nested_conditional'
|
584
585
|
require_relative 'rubocop/cop/style/static_class'
|
585
586
|
require_relative 'rubocop/cop/style/map_compact_with_conditional_block'
|