rubocop 0.87.1 → 0.88.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|