rubocop 0.87.1 → 0.88.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/bin/rubocop-profile +31 -0
- data/config/default.yml +57 -6
- data/lib/rubocop.rb +6 -0
- data/lib/rubocop/cli.rb +2 -2
- data/lib/rubocop/cli/command/auto_genenerate_config.rb +2 -2
- data/lib/rubocop/config_loader.rb +20 -7
- data/lib/rubocop/config_store.rb +4 -0
- data/lib/rubocop/cop/autocorrect_logic.rb +1 -1
- data/lib/rubocop/cop/badge.rb +1 -1
- data/lib/rubocop/cop/base.rb +12 -4
- data/lib/rubocop/cop/cop.rb +1 -1
- data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +26 -0
- data/lib/rubocop/cop/gemspec/required_ruby_version.rb +6 -1
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -0
- data/lib/rubocop/cop/layout/end_alignment.rb +3 -2
- data/lib/rubocop/cop/layout/multiline_block_layout.rb +16 -5
- data/lib/rubocop/cop/layout/space_around_block_parameters.rb +3 -2
- data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +27 -68
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +3 -2
- data/lib/rubocop/cop/lint/disjunctive_assignment_in_constructor.rb +8 -2
- data/lib/rubocop/cop/lint/duplicate_elsif_condition.rb +39 -0
- data/lib/rubocop/cop/lint/duplicate_methods.rb +2 -2
- data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +3 -2
- data/lib/rubocop/cop/lint/literal_as_condition.rb +11 -1
- data/lib/rubocop/cop/lint/nested_method_definition.rb +13 -19
- data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +67 -0
- data/lib/rubocop/cop/mixin/statement_modifier.rb +3 -3
- data/lib/rubocop/cop/style/accessor_grouping.rb +8 -1
- data/lib/rubocop/cop/style/array_coercion.rb +63 -0
- data/lib/rubocop/cop/style/auto_resource_cleanup.rb +3 -2
- data/lib/rubocop/cop/style/bisected_attr_accessor.rb +5 -4
- data/lib/rubocop/cop/style/case_like_if.rb +217 -0
- data/lib/rubocop/cop/style/commented_keyword.rb +5 -2
- data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
- data/lib/rubocop/cop/style/exponential_notation.rb +6 -8
- data/lib/rubocop/cop/style/float_division.rb +7 -10
- data/lib/rubocop/cop/style/format_string_token.rb +5 -5
- data/lib/rubocop/cop/style/hash_as_last_array_item.rb +62 -0
- data/lib/rubocop/cop/style/hash_like_case.rb +76 -0
- data/lib/rubocop/cop/style/if_unless_modifier.rb +11 -11
- data/lib/rubocop/cop/style/missing_else.rb +1 -11
- data/lib/rubocop/cop/style/numeric_predicate.rb +3 -4
- data/lib/rubocop/cop/style/parallel_assignment.rb +3 -3
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +1 -1
- data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +50 -0
- data/lib/rubocop/cop/style/redundant_sort.rb +3 -2
- data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +3 -2
- data/lib/rubocop/cop/style/trailing_method_end_statement.rb +9 -32
- data/lib/rubocop/cop/variable_force/variable.rb +5 -3
- data/lib/rubocop/file_finder.rb +12 -12
- data/lib/rubocop/path_util.rb +2 -17
- data/lib/rubocop/result_cache.rb +12 -8
- data/lib/rubocop/rspec/expect_offense.rb +31 -5
- data/lib/rubocop/rspec/shared_contexts.rb +12 -9
- data/lib/rubocop/runner.rb +5 -6
- data/lib/rubocop/target_finder.rb +2 -2
- data/lib/rubocop/version.rb +1 -1
- metadata +9 -2
@@ -17,9 +17,9 @@ module RuboCop
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def non_eligible_node?(node)
|
20
|
-
node.
|
21
|
-
|
22
|
-
|
20
|
+
node.modifier_form? ||
|
21
|
+
node.nonempty_line_count > 3 ||
|
22
|
+
processed_source.commented?(node.loc.end)
|
23
23
|
end
|
24
24
|
|
25
25
|
def non_eligible_body?(body)
|
@@ -58,6 +58,10 @@ module RuboCop
|
|
58
58
|
|
59
59
|
private
|
60
60
|
|
61
|
+
def previous_line_comment?(node)
|
62
|
+
comment_line?(processed_source[node.first_line - 2])
|
63
|
+
end
|
64
|
+
|
61
65
|
def class_send_elements(class_node)
|
62
66
|
class_def = class_node.body
|
63
67
|
|
@@ -75,6 +79,8 @@ module RuboCop
|
|
75
79
|
end
|
76
80
|
|
77
81
|
def check(send_node)
|
82
|
+
return if previous_line_comment?(send_node)
|
83
|
+
|
78
84
|
if grouped_style? && sibling_accessors(send_node).size > 1
|
79
85
|
add_offense(send_node)
|
80
86
|
elsif separated_style? && send_node.arguments.size > 1
|
@@ -94,7 +100,8 @@ module RuboCop
|
|
94
100
|
send_node.parent.each_child_node(:send).select do |sibling|
|
95
101
|
accessor?(sibling) &&
|
96
102
|
sibling.method?(send_node.method_name) &&
|
97
|
-
node_visibility(sibling) == node_visibility(send_node)
|
103
|
+
node_visibility(sibling) == node_visibility(send_node) &&
|
104
|
+
!previous_line_comment?(sibling)
|
98
105
|
end
|
99
106
|
end
|
100
107
|
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# This cop enforces the use of `Array()` instead of explicit `Array` check or `[*var]`.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# paths = [paths] unless paths.is_a?(Array)
|
11
|
+
# paths.each { |path| do_something(path) }
|
12
|
+
#
|
13
|
+
# # bad (always creates a new Array instance)
|
14
|
+
# [*paths].each { |path| do_something(path) }
|
15
|
+
#
|
16
|
+
# # good (and a bit more readable)
|
17
|
+
# Array(paths).each { |path| do_something(path) }
|
18
|
+
#
|
19
|
+
class ArrayCoercion < Base
|
20
|
+
extend AutoCorrector
|
21
|
+
|
22
|
+
SPLAT_MSG = 'Use `Array(%<arg>s)` instead of `[*%<arg>s]`.'
|
23
|
+
CHECK_MSG = 'Use `Array(%<arg>s)` instead of explicit `Array` check.'
|
24
|
+
|
25
|
+
def_node_matcher :array_splat?, <<~PATTERN
|
26
|
+
(array (splat $_))
|
27
|
+
PATTERN
|
28
|
+
|
29
|
+
def_node_matcher :unless_array?, <<~PATTERN
|
30
|
+
(if
|
31
|
+
(send
|
32
|
+
(lvar $_) :is_a?
|
33
|
+
(const nil? :Array)) nil?
|
34
|
+
(lvasgn $_
|
35
|
+
(array
|
36
|
+
(lvar $_))))
|
37
|
+
PATTERN
|
38
|
+
|
39
|
+
def on_array(node)
|
40
|
+
return unless node.square_brackets?
|
41
|
+
|
42
|
+
array_splat?(node) do |arg_node|
|
43
|
+
message = format(SPLAT_MSG, arg: arg_node.source)
|
44
|
+
add_offense(node, message: message) do |corrector|
|
45
|
+
corrector.replace(node, "Array(#{arg_node.source})")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def on_if(node)
|
51
|
+
unless_array?(node) do |var_a, var_b, var_c|
|
52
|
+
if var_a == var_b && var_c == var_b
|
53
|
+
message = format(CHECK_MSG, arg: var_a)
|
54
|
+
add_offense(node, message: message) do |corrector|
|
55
|
+
corrector.replace(node, "#{var_a} = Array(#{var_a})")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -25,10 +25,11 @@ module RuboCop
|
|
25
25
|
|
26
26
|
def on_send(node)
|
27
27
|
TARGET_METHODS.each do |target_class, target_method|
|
28
|
-
|
28
|
+
next if node.method_name != target_method
|
29
29
|
|
30
|
+
target_receiver = s(:const, nil, target_class)
|
30
31
|
next if node.receiver != target_receiver
|
31
|
-
|
32
|
+
|
32
33
|
next if cleanup?(node)
|
33
34
|
|
34
35
|
add_offense(node,
|
@@ -28,6 +28,7 @@ module RuboCop
|
|
28
28
|
def on_class(class_node)
|
29
29
|
VISIBILITY_SCOPES.each do |visibility|
|
30
30
|
reader_names, writer_names = accessor_names(class_node, visibility)
|
31
|
+
next unless reader_names && writer_names
|
31
32
|
|
32
33
|
accessor_macroses(class_node, visibility).each do |macro|
|
33
34
|
check(macro, reader_names, writer_names)
|
@@ -48,17 +49,17 @@ module RuboCop
|
|
48
49
|
private
|
49
50
|
|
50
51
|
def accessor_names(class_node, visibility)
|
51
|
-
reader_names =
|
52
|
-
writer_names =
|
52
|
+
reader_names = nil
|
53
|
+
writer_names = nil
|
53
54
|
|
54
55
|
accessor_macroses(class_node, visibility).each do |macro|
|
55
56
|
names = macro.arguments.map(&:source)
|
56
57
|
|
57
58
|
names.each do |name|
|
58
59
|
if attr_reader?(macro)
|
59
|
-
reader_names.add(name)
|
60
|
+
(reader_names ||= Set.new).add(name)
|
60
61
|
else
|
61
|
-
writer_names.add(name)
|
62
|
+
(writer_names ||= Set.new).add(name)
|
62
63
|
end
|
63
64
|
end
|
64
65
|
end
|
@@ -0,0 +1,217 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# This cop identifies places where `if-elsif` constructions
|
7
|
+
# can be replaced with `case-when`.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# if status == :active
|
12
|
+
# perform_action
|
13
|
+
# elsif status == :inactive || status == :hibernating
|
14
|
+
# check_timeout
|
15
|
+
# else
|
16
|
+
# final_action
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# # good
|
20
|
+
# case status
|
21
|
+
# when :active
|
22
|
+
# perform_action
|
23
|
+
# when :inactive, :hibernating
|
24
|
+
# check_timeout
|
25
|
+
# else
|
26
|
+
# final_action
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
class CaseLikeIf < Cop
|
30
|
+
include RangeHelp
|
31
|
+
|
32
|
+
MSG = 'Convert `if-elsif` to `case-when`.'
|
33
|
+
|
34
|
+
def on_if(node)
|
35
|
+
return unless should_check?(node)
|
36
|
+
|
37
|
+
target = find_target(node.condition)
|
38
|
+
return unless target
|
39
|
+
|
40
|
+
conditions = []
|
41
|
+
convertible = true
|
42
|
+
|
43
|
+
branch_conditions(node).each do |branch_condition|
|
44
|
+
conditions << []
|
45
|
+
convertible = collect_conditions(branch_condition, target, conditions.last)
|
46
|
+
break unless convertible
|
47
|
+
end
|
48
|
+
|
49
|
+
add_offense(node) if convertible
|
50
|
+
end
|
51
|
+
|
52
|
+
def autocorrect(node)
|
53
|
+
target = find_target(node.condition)
|
54
|
+
|
55
|
+
lambda do |corrector|
|
56
|
+
corrector.insert_before(node, "case #{target.source}\n#{indent(node)}")
|
57
|
+
|
58
|
+
branch_conditions(node).each do |branch_condition|
|
59
|
+
conditions = []
|
60
|
+
collect_conditions(branch_condition, target, conditions)
|
61
|
+
|
62
|
+
range = correction_range(branch_condition)
|
63
|
+
branch_replacement = "when #{conditions.map(&:source).join(', ')}"
|
64
|
+
corrector.replace(range, branch_replacement)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def should_check?(node)
|
72
|
+
!node.unless? && !node.elsif? && !node.modifier_form? && !node.ternary? &&
|
73
|
+
node.elsif_conditional?
|
74
|
+
end
|
75
|
+
|
76
|
+
# rubocop:disable Metrics/MethodLength
|
77
|
+
def find_target(node)
|
78
|
+
case node.type
|
79
|
+
when :begin
|
80
|
+
find_target(node.children.first)
|
81
|
+
when :or
|
82
|
+
find_target(node.lhs)
|
83
|
+
when :match_with_lvasgn
|
84
|
+
lhs, rhs = *node
|
85
|
+
if lhs.regexp_type?
|
86
|
+
rhs
|
87
|
+
elsif rhs.regexp_type?
|
88
|
+
lhs
|
89
|
+
end
|
90
|
+
when :send
|
91
|
+
find_target_in_send_node(node)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
# rubocop:enable Metrics/MethodLength
|
95
|
+
|
96
|
+
def find_target_in_send_node(node)
|
97
|
+
case node.method_name
|
98
|
+
when :is_a?
|
99
|
+
node.receiver
|
100
|
+
when :==, :eql?, :equal?
|
101
|
+
find_target_in_equality_node(node)
|
102
|
+
when :===
|
103
|
+
node.arguments.first
|
104
|
+
when :include?, :cover?
|
105
|
+
receiver = deparenthesize(node.receiver)
|
106
|
+
node.arguments.first if receiver.range_type?
|
107
|
+
when :match, :match?
|
108
|
+
find_target_in_match_node(node)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def find_target_in_equality_node(node)
|
113
|
+
argument = node.arguments.first
|
114
|
+
receiver = node.receiver
|
115
|
+
|
116
|
+
if argument.literal? || const_reference?(argument)
|
117
|
+
receiver
|
118
|
+
elsif receiver.literal? || const_reference?(receiver)
|
119
|
+
argument
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def find_target_in_match_node(node)
|
124
|
+
argument = node.arguments.first
|
125
|
+
receiver = node.receiver
|
126
|
+
|
127
|
+
if receiver.regexp_type?
|
128
|
+
argument
|
129
|
+
elsif argument.regexp_type?
|
130
|
+
receiver
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def collect_conditions(node, target, conditions)
|
135
|
+
condition =
|
136
|
+
case node.type
|
137
|
+
when :begin
|
138
|
+
return collect_conditions(node.children.first, target, conditions)
|
139
|
+
when :or
|
140
|
+
return collect_conditions(node.lhs, target, conditions) &&
|
141
|
+
collect_conditions(node.rhs, target, conditions)
|
142
|
+
when :match_with_lvasgn
|
143
|
+
lhs, rhs = *node
|
144
|
+
condition_from_binary_op(lhs, rhs, target)
|
145
|
+
when :send
|
146
|
+
condition_from_send_node(node, target)
|
147
|
+
end
|
148
|
+
|
149
|
+
conditions << condition if condition
|
150
|
+
end
|
151
|
+
|
152
|
+
# rubocop:disable Metrics/AbcSize
|
153
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
154
|
+
def condition_from_send_node(node, target)
|
155
|
+
case node.method_name
|
156
|
+
when :is_a?
|
157
|
+
node.arguments.first if node.receiver == target
|
158
|
+
when :==, :eql?, :equal?, :=~, :match, :match?
|
159
|
+
lhs, _method, rhs = *node
|
160
|
+
condition_from_binary_op(lhs, rhs, target)
|
161
|
+
when :===
|
162
|
+
lhs, _method, rhs = *node
|
163
|
+
lhs if rhs == target
|
164
|
+
when :include?, :cover?
|
165
|
+
receiver = deparenthesize(node.receiver)
|
166
|
+
receiver if receiver.range_type? && node.arguments.first == target
|
167
|
+
end
|
168
|
+
end
|
169
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
170
|
+
# rubocop:enable Metrics/AbcSize
|
171
|
+
|
172
|
+
def condition_from_binary_op(lhs, rhs, target)
|
173
|
+
lhs = deparenthesize(lhs)
|
174
|
+
rhs = deparenthesize(rhs)
|
175
|
+
|
176
|
+
if lhs == target
|
177
|
+
rhs
|
178
|
+
elsif rhs == target
|
179
|
+
lhs
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def branch_conditions(node)
|
184
|
+
conditions = []
|
185
|
+
while node&.if_type?
|
186
|
+
conditions << node.condition
|
187
|
+
node = node.else_branch
|
188
|
+
end
|
189
|
+
conditions
|
190
|
+
end
|
191
|
+
|
192
|
+
def const_reference?(node)
|
193
|
+
return false unless node.const_type?
|
194
|
+
|
195
|
+
name = node.children[1].to_s
|
196
|
+
|
197
|
+
# We can no be sure if, e.g. `C`, represents a constant or a class reference
|
198
|
+
name.length > 1 &&
|
199
|
+
name == name.upcase
|
200
|
+
end
|
201
|
+
|
202
|
+
def deparenthesize(node)
|
203
|
+
node = node.children.last while node.begin_type?
|
204
|
+
node
|
205
|
+
end
|
206
|
+
|
207
|
+
def correction_range(node)
|
208
|
+
range_between(node.parent.loc.keyword.begin_pos, node.loc.expression.end_pos)
|
209
|
+
end
|
210
|
+
|
211
|
+
def indent(node)
|
212
|
+
' ' * node.loc.column
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
@@ -46,17 +46,20 @@ module RuboCop
|
|
46
46
|
private
|
47
47
|
|
48
48
|
KEYWORDS = %w[begin class def end module].freeze
|
49
|
+
KEYWORD_REGEXES = KEYWORDS.map { |w| /^\s*#{w}\s/ }.freeze
|
50
|
+
|
49
51
|
ALLOWED_COMMENTS = %w[
|
50
52
|
:nodoc:
|
51
53
|
:yields:
|
52
54
|
rubocop:disable
|
53
55
|
rubocop:todo
|
54
56
|
].freeze
|
57
|
+
ALLOWED_COMMENT_REGEXES = ALLOWED_COMMENTS.map { |c| /#\s*#{c}/ }.freeze
|
55
58
|
|
56
59
|
def offensive?(comment)
|
57
60
|
line = line(comment)
|
58
|
-
|
59
|
-
|
61
|
+
KEYWORD_REGEXES.any? { |r| r.match?(line) } &&
|
62
|
+
ALLOWED_COMMENT_REGEXES.none? { |r| r.match?(line) }
|
60
63
|
end
|
61
64
|
|
62
65
|
def message(comment)
|
@@ -60,6 +60,11 @@ module RuboCop
|
|
60
60
|
#
|
61
61
|
class ExponentialNotation < Cop
|
62
62
|
include ConfigurableEnforcedStyle
|
63
|
+
MESSAGES = {
|
64
|
+
scientific: 'Use a mantissa in [1, 10[.',
|
65
|
+
engineering: 'Use an exponent divisible by 3 and a mantissa in [0.1, 1000[.',
|
66
|
+
integral: 'Use an integer as mantissa, without trailing zero.'
|
67
|
+
}.freeze
|
63
68
|
|
64
69
|
def on_float(node)
|
65
70
|
add_offense(node) if offense?(node)
|
@@ -104,14 +109,7 @@ module RuboCop
|
|
104
109
|
end
|
105
110
|
|
106
111
|
def message(_node)
|
107
|
-
|
108
|
-
when :scientific
|
109
|
-
'Use a mantissa in [1, 10[.'
|
110
|
-
when :engineering
|
111
|
-
'Use an exponent divisible by 3 and a mantissa in [0.1, 1000[.'
|
112
|
-
when :integral
|
113
|
-
'Use an integer as mantissa, without trailing zero.'
|
114
|
-
end
|
112
|
+
MESSAGES[style]
|
115
113
|
end
|
116
114
|
end
|
117
115
|
end
|
@@ -41,6 +41,12 @@ module RuboCop
|
|
41
41
|
# a.fdiv(b)
|
42
42
|
class FloatDivision < Cop
|
43
43
|
include ConfigurableEnforcedStyle
|
44
|
+
MESSAGES = {
|
45
|
+
left_coerce: 'Prefer using `.to_f` on the left side.',
|
46
|
+
right_coerce: 'Prefer using `.to_f` on the right side.',
|
47
|
+
single_coerce: 'Prefer using `.to_f` on one side only.',
|
48
|
+
fdiv: 'Prefer using `fdiv` for float divisions.'
|
49
|
+
}.freeze
|
44
50
|
|
45
51
|
def_node_matcher :right_coerce?, <<~PATTERN
|
46
52
|
(send _ :/ (send _ :to_f))
|
@@ -77,16 +83,7 @@ module RuboCop
|
|
77
83
|
end
|
78
84
|
|
79
85
|
def message(_node)
|
80
|
-
|
81
|
-
when :left_coerce
|
82
|
-
'Prefer using `.to_f` on the left side.'
|
83
|
-
when :right_coerce
|
84
|
-
'Prefer using `.to_f` on the right side.'
|
85
|
-
when :single_coerce
|
86
|
-
'Prefer using `.to_f` on one side only.'
|
87
|
-
when :fdiv
|
88
|
-
'Prefer using `fdiv` for float divisions.'
|
89
|
-
end
|
86
|
+
MESSAGES[style]
|
90
87
|
end
|
91
88
|
end
|
92
89
|
end
|